diff --git a/.gitattributes b/.gitattributes index ac0a71232efc9..51a670b5fbefd 100644 --- a/.gitattributes +++ b/.gitattributes @@ -7,10 +7,12 @@ *.fixed linguist-language=Rust *.mir linguist-language=Rust src/etc/installer/gfx/* binary -*.woff binary src/vendor/** -text Cargo.lock linguist-generated=false +config.toml.example linguist-language=TOML -# Older git versions try to fix line endings on images, this prevents it. +# Older git versions try to fix line endings on images and fonts, this prevents it. *.png binary *.ico binary +*.woff binary +*.woff2 binary diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 99a0c0b4b85de..ffaa2b03df9e7 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -43,7 +43,7 @@ jobs: - name: mingw-check os: ubuntu-latest-xl env: {} - - name: x86_64-gnu-llvm-9 + - name: x86_64-gnu-llvm-10 os: ubuntu-latest-xl env: {} - name: x86_64-gnu-tools @@ -265,7 +265,7 @@ jobs: - name: x86_64-gnu-distcheck os: ubuntu-latest-xl env: {} - - name: x86_64-gnu-llvm-9 + - name: x86_64-gnu-llvm-10 env: RUST_BACKTRACE: 1 os: ubuntu-latest-xl @@ -622,7 +622,7 @@ jobs: if: "github.event_name == 'push' && github.ref == 'refs/heads/master' && github.repository == 'rust-lang-ci/rust'" steps: - name: checkout the source code - uses: actions/checkout@v1 + uses: actions/checkout@v2 with: fetch-depth: 2 - name: publish toolstate diff --git a/.gitignore b/.gitignore index 3b2e8bd86a11c..0cd6b9f648d0f 100644 --- a/.gitignore +++ b/.gitignore @@ -5,51 +5,71 @@ # created during manual debugging and many people like to clean up instead of # having git ignore such leftovers. You can use `.git/info/exclude` to # configure your local ignore list. -# FIXME: This needs cleanup. -*~ -.#* + +## File system .DS_Store +desktop.ini + +## Editor +*.swp +*.swo +Session.vim .cproject -.hg/ -.hgignore .idea *.iml -__pycache__/ -*.py[cod] -*$py.class +.vscode .project +.favorites.json .settings/ + +## Tool .valgrindrc -.vscode -.favorites.json -/Makefile -/build/ +.cargo +# Included because it is part of the test case +!/src/test/run-make/thumb-none-qemu/example/.cargo + +## Configuration /config.toml -/dist/ +/Makefile +config.mk +config.stamp +no_llvm_build + +## Build /dl/ /doc/ /inst/ /llvm/ /mingw-build/ -/src/tools/x/target -# Created by default with `src/ci/docker/run.sh`: -/obj/ +/build/ +/dist/ /unicode-downloads /target -# Generated by compiletest for incremental: +/src/tools/x/target +# Generated by compiletest for incremental /tmp/ +# Created by default with `src/ci/docker/run.sh` +/obj/ + +## Temporary files +*~ +\#* +\#*\# +.#* + +## Tags tags tags.* TAGS TAGS.* -\#* -\#*\# -config.mk -config.stamp -Session.vim -.cargo -!/src/test/run-make/thumb-none-qemu/example/.cargo -no_llvm_build + +## Python +__pycache__/ +*.py[cod] +*$py.class + +## Node **node_modules **package-lock.json + # Before adding new lines, see the comment at the top. diff --git a/.gitmodules b/.gitmodules index 8618f61f04acd..d4a80efc277e3 100644 --- a/.gitmodules +++ b/.gitmodules @@ -37,7 +37,7 @@ [submodule "src/llvm-project"] path = src/llvm-project url = https://github.com/rust-lang/llvm-project.git - branch = rustc/12.0-2021-02-03 + branch = rustc/12.0-2021-04-15 [submodule "src/doc/embedded-book"] path = src/doc/embedded-book url = https://github.com/rust-embedded/book.git diff --git a/.mailmap b/.mailmap index 94f2938f11c26..1019710dc9793 100644 --- a/.mailmap +++ b/.mailmap @@ -235,6 +235,7 @@ Philipp Matthias Schäfer Przemysław Wesołek Przemek Wesołek Rafael Ávila de Espíndola Rafael Avila de Espindola Ralph Giles Ralph Giles +Ramkumar Ramachandra Renato Riccieri Santos Zannon Richard Diamond Rob Arnold @@ -285,7 +286,7 @@ Xuefeng Wu Xuefeng Wu Xuefeng Wu XuefengWu York Xiang Youngsoo Son -Yuki Okushi +Yuki Okushi Zach Pomerantz Zack Corr Zack Slayton diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index b600074c19770..2827a46ae6f73 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,8 +1,31 @@ # Contributing to Rust -Thank you for your interest in contributing to Rust! +Thank you for your interest in contributing to Rust! There are many ways to contribute +and we appreciate all of them. -To get started, read the [Contributing to Rust] chapter of the [rustc-dev-guide]. +Documentation for contributing to Rust is located in the [Guide to Rustc Development](https://rustc-dev-guide.rust-lang.org/), +commonly known as the [rustc-dev-guide]. Despite the name, this guide documents +not just how to develop rustc (the Rust compiler), but also how to contribute to any part +of the Rust project. + +To get started with contributing, please read the [Contributing to Rust] chapter of the guide. +That chapter explains how to get your development environment set up and how to get help. + +## About the [rustc-dev-guide] + +The [rustc-dev-guide] is meant to help document how rustc –the Rust compiler– works, +as well as to help new contributors get involved in rustc development. It is recommend +to read and understand the [rustc-dev-guide] before making a contribution. This guide +talks about the different bots in the Rust ecosystem, the Rust development tools, +bootstrapping, the compiler architecture, source code representation, and more. + +## [Getting help](https://rustc-dev-guide.rust-lang.org/getting-started.html#asking-questions) + +There are many ways you can get help when you're stuck. Rust has many platforms for this: +[internals], [rust-zulip], and [rust-discord]. It is recommended to ask for help on +the [rust-zulip], but any of these platforms are a great way to seek help and even +find a mentor! You can learn more about asking questions and getting help in the +[Asking Questions](https://rustc-dev-guide.rust-lang.org/getting-started.html#asking-questions) chapter of the [rustc-dev-guide]. ## Bug reports @@ -13,3 +36,6 @@ refer to [this section][contributing-bug-reports] and [open an issue][issue temp [rustc-dev-guide]: https://rustc-dev-guide.rust-lang.org/ [contributing-bug-reports]: https://rustc-dev-guide.rust-lang.org/contributing.html#bug-reports [issue template]: https://github.com/rust-lang/rust/issues/new/choose +[internals]: https://internals.rust-lang.org +[rust-discord]: http://discord.gg/rust-lang +[rust-zulip]: https://rust-lang.zulipchat.com diff --git a/Cargo.lock b/Cargo.lock index 9e773c572cc41..c51843440a302 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,5 +1,7 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. +version = 3 + [[package]] name = "addr2line" version = "0.14.0" @@ -37,7 +39,7 @@ version = "0.0.0" dependencies = [ "compiler_builtins", "core", - "rand", + "rand 0.7.3", "rand_xorshift", ] @@ -95,12 +97,6 @@ version = "1.0.34" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bf8dcb5b4bbaa28653b647d8c77bd4ed40183b48882e130c1f1ffb73de069fd7" -[[package]] -name = "arc-swap" -version = "0.4.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d25d88fd6b8041580a654f9d0c581a047baee2b3efee13275f2fc392fc75034" - [[package]] name = "array_tool" version = "1.0.3" @@ -115,9 +111,15 @@ checksum = "a4c527152e37cf757a3f78aae5a06fbeefdb07ccc535c980a3208ee3060dd544" [[package]] name = "arrayvec" -version = "0.5.1" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cff77d8686867eceff3105329d4698d96c2391c176d5d03adc90c7389162b5b8" +checksum = "23b62fc65de8e4e7f52534fb52b0f3ed04746ae267519eef2a83941e8085068b" + +[[package]] +name = "arrayvec" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a2f58b0bb10c380af2b26e57212856b8c9a59e0925b4c20f4a174a49734eaf7" [[package]] name = "atty" @@ -164,7 +166,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d8fb2d74254a3a0b5cac33ac9f8ed0e44aa50378d9dbb2e5d83bd21ed1dc2c8a" dependencies = [ "arrayref", - "arrayvec", + "arrayvec 0.5.2", "constant_time_eq", ] @@ -177,7 +179,7 @@ dependencies = [ "block-padding", "byte-tools", "byteorder", - "generic-array 0.12.3", + "generic-array 0.12.4", ] [[package]] @@ -258,11 +260,11 @@ checksum = "e3b5ca7a04898ad4bcd41c90c5285445ff5b791899bb1b0abdd2a2aa791211d7" [[package]] name = "bytecount" -version = "0.6.0" +version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0017894339f586ccb943b01b9555de56770c11cda818e7e3d8bd93f4ed7f46e" +checksum = "72feb31ffc86498dacdbd0fcebb56138e7177a8cc5cea4516031d15ae85a742e" dependencies = [ - "packed_simd", + "packed_simd_2", ] [[package]] @@ -285,7 +287,7 @@ checksum = "81a18687293a1546b67c246452202bbbf143d239cb43494cc163da14979082da" [[package]] name = "cargo" -version = "0.53.0" +version = "0.54.0" dependencies = [ "anyhow", "atty", @@ -293,11 +295,10 @@ dependencies = [ "cargo-platform", "cargo-test-macro", "cargo-test-support", + "cargo-util", "clap", - "core-foundation", "crates-io", "crossbeam-utils 0.8.0", - "crypto-hash", "curl", "curl-sys", "env_logger 0.8.1", @@ -319,15 +320,14 @@ dependencies = [ "libgit2-sys", "log", "memchr", - "miow 0.3.6", "num_cpus", "opener", "openssl", "percent-encoding 2.1.0", "pretty_env_logger", + "rand 0.8.3", "rustc-workspace-hack", "rustfix", - "same-file", "semver 0.10.0", "serde", "serde_ignored", @@ -401,8 +401,9 @@ version = "0.1.0" name = "cargo-test-support" version = "0.1.0" dependencies = [ - "cargo", + "anyhow", "cargo-test-macro", + "cargo-util", "filetime", "flate2", "git2", @@ -415,6 +416,26 @@ dependencies = [ "url 2.1.1", ] +[[package]] +name = "cargo-util" +version = "0.1.0" +dependencies = [ + "anyhow", + "core-foundation", + "crypto-hash", + "filetime", + "hex 0.4.2", + "jobserver", + "libc", + "log", + "miow 0.3.6", + "same-file", + "shell-escape", + "tempfile", + "walkdir", + "winapi 0.3.9", +] + [[package]] name = "cargo_metadata" version = "0.8.2" @@ -562,19 +583,22 @@ dependencies = [ [[package]] name = "clippy" -version = "0.1.52" +version = "0.1.53" dependencies = [ "cargo_metadata 0.12.0", "clippy-mini-macro-test", "clippy_lints", - "compiletest_rs 0.6.0", + "compiletest_rs", "derive-new", + "quote", + "regex", "rustc-workspace-hack", "rustc_tools_util 0.2.0", "semver 0.11.0", "serde", + "syn", "tempfile", - "tester 0.9.0", + "tester", ] [[package]] @@ -583,7 +607,7 @@ version = "0.2.0" [[package]] name = "clippy_lints" -version = "0.1.52" +version = "0.1.53" dependencies = [ "cargo_metadata 0.12.0", "clippy_utils", @@ -591,13 +615,10 @@ dependencies = [ "itertools 0.9.0", "pulldown-cmark 0.8.0", "quine-mc_cluskey", - "quote", "regex-syntax", "rustc-semver", "semver 0.11.0", "serde", - "smallvec 1.6.1", - "syn", "toml", "unicode-normalization", "url 2.1.1", @@ -605,27 +626,16 @@ dependencies = [ [[package]] name = "clippy_utils" -version = "0.1.52" +version = "0.1.53" dependencies = [ "if_chain", "itertools 0.9.0", "regex-syntax", "rustc-semver", "serde", - "smallvec 1.6.1", - "toml", "unicode-normalization", ] -[[package]] -name = "cloudabi" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4344512281c643ae7638bbabc3af17a11307803ec8f0fcad9fae512a8bf36467" -dependencies = [ - "bitflags", -] - [[package]] name = "cmake" version = "0.1.44" @@ -696,28 +706,6 @@ dependencies = [ "winapi 0.3.9", ] -[[package]] -name = "compiletest_rs" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f737835bfbbe29ed1ff82d5137520338d7ed5bf1a1d4b9c1c7c58bb45b8fa29" -dependencies = [ - "diff", - "filetime", - "getopts", - "libc", - "log", - "miow 0.3.6", - "regex", - "rustfix", - "serde", - "serde_derive", - "serde_json", - "tempfile", - "tester 0.7.0", - "winapi 0.3.9", -] - [[package]] name = "compiletest_rs" version = "0.6.0" @@ -737,7 +725,7 @@ dependencies = [ "serde_derive", "serde_json", "tempfile", - "tester 0.9.0", + "tester", "winapi 0.3.9", ] @@ -757,7 +745,7 @@ checksum = "245097e9a4535ee1e3e3931fcfcd55a796a44c643e8596ff6566d68f09b87bbc" name = "core" version = "0.0.0" dependencies = [ - "rand", + "rand 0.7.3", ] [[package]] @@ -843,15 +831,6 @@ dependencies = [ "scopeguard", ] -[[package]] -name = "crossbeam-queue" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c979cd6cfe72335896575c6b5688da489e420d36a27a0b9eb0c73db574b4a4b" -dependencies = [ - "crossbeam-utils 0.6.6", -] - [[package]] name = "crossbeam-queue" version = "0.2.3" @@ -863,16 +842,6 @@ dependencies = [ "maybe-uninit", ] -[[package]] -name = "crossbeam-utils" -version = "0.6.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04973fa96e96579258a5091af6003abde64af786b860f18622b82e026cca60e6" -dependencies = [ - "cfg-if 0.1.10", - "lazy_static", -] - [[package]] name = "crossbeam-utils" version = "0.7.2" @@ -1005,7 +974,7 @@ version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f3d0c8c8752312f9713efd397ff63acb9f85585afbf179282e720e7704954dd5" dependencies = [ - "generic-array 0.12.3", + "generic-array 0.12.4", ] [[package]] @@ -1109,6 +1078,26 @@ dependencies = [ "log", ] +[[package]] +name = "enum-iterator" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c79a6321a1197d7730510c7e3f6cb80432dfefecb32426de8cea0aa19b4bb8d7" +dependencies = [ + "enum-iterator-derive", +] + +[[package]] +name = "enum-iterator-derive" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e94aa31f7c0dc764f57896dc615ddd76fc13b0d5dca7eb6cc5e018a5a09ec06" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "env_logger" version = "0.6.2" @@ -1242,6 +1231,12 @@ dependencies = [ "rustc-std-workspace-core", ] +[[package]] +name = "fs-err" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bcd1163ae48bda72a20ae26d66a04d3094135cadab911cff418ae5e33f253431" + [[package]] name = "fs_extra" version = "1.1.0" @@ -1398,9 +1393,9 @@ dependencies = [ [[package]] name = "generic-array" -version = "0.12.3" +version = "0.12.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c68f0274ae0e023facc3c97b2e00f076be70e254bc851d972503b328db79b2ec" +checksum = "ffdf9f34f1447443d37393cc6c2b8313aebddcd96906caf34e54c68d8e57d7bd" dependencies = [ "typenum", ] @@ -1448,6 +1443,18 @@ dependencies = [ "wasi", ] +[[package]] +name = "getset" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24b328c01a4d71d2d8173daa93562a73ab0fe85616876f02500f53d82948c504" +dependencies = [ + "proc-macro-error", + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "gimli" version = "0.23.0" @@ -1530,9 +1537,15 @@ dependencies = [ [[package]] name = "hashbrown" -version = "0.9.0" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7afe4a420e3fe79967a00898cc1f4db7c8a49a9333a29f8a4bd76a253d5cd04" + +[[package]] +name = "hashbrown" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00d63df3d41950fb462ed38308eea019113ad1508da725bbedcd0fa5a85ef5f7" +checksum = "362385356d610bd1e5a408ddf8d022041774b683f345a1d2cfcb4f60f8ae2db5" dependencies = [ "compiler_builtins", "rustc-std-workspace-alloc", @@ -1662,7 +1675,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3ca8957e71f04a205cb162508f9326aea04676c8dfd0711220190d6b83664f3f" dependencies = [ "bitmaps", - "rand_core", + "rand_core 0.5.1", "rand_xoshiro", "sized-chunks", "typenum", @@ -1676,7 +1689,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "55e2e4c765aa53a0424761bf9f41aa7a6ac1efa87238f59560640e27fca028f2" dependencies = [ "autocfg", - "hashbrown", + "hashbrown 0.9.1", ] [[package]] @@ -1735,22 +1748,11 @@ version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dc6f3ad7b9d11a0c00842ff8de1b60ee58661048eb8049ed33c73594f359d7e6" -[[package]] -name = "jemalloc-sys" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d3b9f3f5c9b31aa0f5ed3260385ac205db665baa41d49bb8338008ae94ede45" -dependencies = [ - "cc", - "fs_extra", - "libc", -] - [[package]] name = "jobserver" -version = "0.1.21" +version = "0.1.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c71313ebb9439f74b00d9d2dcec36440beaf57a6aa0623068441dd7cd81a7f2" +checksum = "972f5ae5d1cb9c6ae417789196c803205313edde988685da5e3aae0827b9e7fd" dependencies = [ "libc", ] @@ -1765,6 +1767,7 @@ checksum = "92c245af8786f6ac35f95ca14feca9119e71339aaab41e878e7cdd655c97e9e5" name = "jsondocck" version = "0.1.0" dependencies = [ + "fs-err", "getopts", "jsonpath_lib", "lazy_static", @@ -1867,7 +1870,7 @@ dependencies = [ "lazy_static", "log", "parking_lot", - "rand", + "rand 0.7.3", "serde", ] @@ -1912,9 +1915,9 @@ checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" [[package]] name = "libc" -version = "0.2.85" +version = "0.2.93" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ccac4b00700875e6a07c6cde370d44d32fa01c5a65cdd2fca6858c479d28bb3" +checksum = "9385f66bf6105b241aa65a61cb923ef20efc665cb9f9bb50ac2f0c4b7f378d41" dependencies = [ "rustc-std-workspace-core", ] @@ -1933,6 +1936,12 @@ dependencies = [ "pkg-config", ] +[[package]] +name = "libm" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fc7aa29613bd6a620df431842069224d8bc9011086b1db4c0e0cd47fa03ec9a" + [[package]] name = "libnghttp2-sys" version = "0.1.4+1.41.0" @@ -2162,7 +2171,7 @@ dependencies = [ "parking_lot", "perf-event-open-sys", "rustc-hash", - "smallvec 1.6.1", + "smallvec", ] [[package]] @@ -2181,6 +2190,15 @@ dependencies = [ "winapi 0.3.9", ] +[[package]] +name = "memmap2" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04e3e85b970d650e2ae6d70592474087051c11c54da7f7b4949725c5735fbcc6" +dependencies = [ + "libc", +] + [[package]] name = "memoffset" version = "0.5.5" @@ -2214,9 +2232,9 @@ dependencies = [ [[package]] name = "minifier" -version = "0.0.33" +version = "0.0.39" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70bf0db2475f5e627787da77ca52fe33c294063f49f4134b8bc662eedb5e7332" +checksum = "6cdf618de5c9c98d4a7b2e0d1f1e44f82a19196cfd94040bb203621c25d28d98" dependencies = [ "macro-utils", ] @@ -2302,17 +2320,17 @@ name = "miri" version = "0.1.0" dependencies = [ "colored", - "compiletest_rs 0.5.0", - "env_logger 0.7.1", + "compiletest_rs", + "env_logger 0.8.1", "getrandom 0.2.0", "hex 0.4.2", "libc", "log", - "rand", + "rand 0.8.3", "rustc-workspace-hack", "rustc_version", "shell-escape", - "smallvec 1.6.1", + "smallvec", ] [[package]] @@ -2374,9 +2392,9 @@ dependencies = [ [[package]] name = "once_cell" -version = "1.4.1" +version = "1.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "260e51e7efe62b592207e9e13a68e43692a7a279171d6ba57abd208bf23645ad" +checksum = "af8b08b04175473088b46763e51ee54da5f9a164bc162f615b91bc179dbf15a3" [[package]] name = "opaque-debug" @@ -2467,18 +2485,20 @@ dependencies = [ ] [[package]] -name = "packed_simd" -version = "0.3.3" +name = "packed_simd_2" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a85ea9fc0d4ac0deb6fe7911d38786b32fc11119afd9e9d38b84ff691ce64220" +checksum = "3278e0492f961fd4ae70909f56b2723a7e8d01a228427294e19cdfdebda89a17" dependencies = [ "cfg-if 0.1.10", + "libm", ] [[package]] name = "panic_abort" version = "0.0.0" dependencies = [ + "alloc", "cfg-if 0.1.10", "compiler_builtins", "core", @@ -2508,16 +2528,16 @@ dependencies = [ "log", "mio-named-pipes", "miow 0.3.6", - "rand", + "rand 0.7.3", "tokio", "winapi 0.3.9", ] [[package]] name = "parking_lot" -version = "0.11.0" +version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4893845fa2ca272e647da5d0e46660a314ead9c2fdd9a883aabc32e481a8733" +checksum = "6d7744ac029df22dca6284efe4e898991d28e3085c706c972bcd7da4a27a15eb" dependencies = [ "instant", "lock_api", @@ -2526,16 +2546,15 @@ dependencies = [ [[package]] name = "parking_lot_core" -version = "0.8.0" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c361aa727dd08437f2f1447be8b59a33b0edd15e0fcee698f935613d9efbca9b" +checksum = "fa7a782938e745763fe6907fc6ba86946d72f49fe7e21de074e08128a99fb018" dependencies = [ - "cfg-if 0.1.10", - "cloudabi", + "cfg-if 1.0.0", "instant", "libc", - "redox_syscall 0.1.57", - "smallvec 1.6.1", + "redox_syscall 0.2.5", + "smallvec", "winapi 0.3.9", ] @@ -2645,7 +2664,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "17367f0cc86f2d25802b2c26ee58a7b23faeccf78a396094c13dced0d0182526" dependencies = [ "phf_shared", - "rand", + "rand 0.7.3", ] [[package]] @@ -2773,9 +2792,9 @@ checksum = "bc881b2c22681370c6a780e47af9840ef841837bc98118431d4e1868bd0c1086" [[package]] name = "proc-macro2" -version = "1.0.19" +version = "1.0.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04f5f085b5d71e2188cb8271e5da0161ad52c3f227a661a3c135fdf28e258b12" +checksum = "1e0704ee1a7e00d7bb417d0770ea303c1bccbabf0ef1667dae92b5967f5f8a71" dependencies = [ "unicode-xid", ] @@ -2863,9 +2882,9 @@ dependencies = [ [[package]] name = "racer" -version = "2.1.44" +version = "2.1.45" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7972a124e2b24dce35eb19f81eced829faec0e8227a7d744bbb1089934d05399" +checksum = "15408926f6207643150e0fc2c54a75a689b192df03ac6c59d42ea99c6782c7f7" dependencies = [ "bitflags", "clap", @@ -2892,12 +2911,24 @@ checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" dependencies = [ "getrandom 0.1.14", "libc", - "rand_chacha", - "rand_core", - "rand_hc", + "rand_chacha 0.2.2", + "rand_core 0.5.1", + "rand_hc 0.2.0", "rand_pcg", ] +[[package]] +name = "rand" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ef9e7e66b4468674bfcb0c81af8b7fa0bb154fa9f28eb840da5c447baeb8d7e" +dependencies = [ + "libc", + "rand_chacha 0.3.0", + "rand_core 0.6.2", + "rand_hc 0.3.0", +] + [[package]] name = "rand_chacha" version = "0.2.2" @@ -2905,7 +2936,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402" dependencies = [ "ppv-lite86", - "rand_core", + "rand_core 0.5.1", +] + +[[package]] +name = "rand_chacha" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e12735cf05c9e10bf21534da50a147b924d555dc7a547c42e6bb2d5b6017ae0d" +dependencies = [ + "ppv-lite86", + "rand_core 0.6.2", ] [[package]] @@ -2917,13 +2958,31 @@ dependencies = [ "getrandom 0.1.14", ] +[[package]] +name = "rand_core" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34cf66eb183df1c5876e2dcf6b13d57340741e8dc255b48e40a26de954d06ae7" +dependencies = [ + "getrandom 0.2.0", +] + [[package]] name = "rand_hc" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" dependencies = [ - "rand_core", + "rand_core 0.5.1", +] + +[[package]] +name = "rand_hc" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3190ef7066a446f2e7f42e239d161e905420ccab01eb967c9eb27d21b2322a73" +dependencies = [ + "rand_core 0.6.2", ] [[package]] @@ -2932,7 +2991,7 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "16abd0c1b639e9eb4d7c50c0b8100b0d0f849be2349829c740fe8e6eb4816429" dependencies = [ - "rand_core", + "rand_core 0.5.1", ] [[package]] @@ -2941,7 +3000,7 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "77d416b86801d23dde1aa643023b775c3a462efc0ed96443add11546cdf1dca8" dependencies = [ - "rand_core", + "rand_core 0.5.1", ] [[package]] @@ -2950,7 +3009,7 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a9fcdd2e881d02f1d9390ae47ad8e5696a9e4be7b547a1da2afbc61973217004" dependencies = [ - "rand_core", + "rand_core 0.5.1", ] [[package]] @@ -2972,7 +3031,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e92e15d89083484e11353891f1af602cc661426deb9564c298b270c726973280" dependencies = [ "crossbeam-deque", - "crossbeam-queue 0.2.3", + "crossbeam-queue", "crossbeam-utils 0.7.2", "lazy_static", "num_cpus", @@ -3065,6 +3124,7 @@ version = "1.41.0" dependencies = [ "anyhow", "cargo", + "cargo-util", "cargo_metadata 0.8.2", "clippy_lints", "crossbeam-channel", @@ -3082,7 +3142,7 @@ dependencies = [ "num_cpus", "ordslice", "racer", - "rand", + "rand 0.7.3", "rayon", "regex", "rls-analysis", @@ -3153,7 +3213,7 @@ dependencies = [ "env_logger 0.7.1", "futures 0.3.12", "log", - "rand", + "rand 0.7.3", "rls-data", "rls-ipc", "serde", @@ -3210,18 +3270,19 @@ dependencies = [ [[package]] name = "rustc-ap-rustc_arena" -version = "705.0.0" +version = "712.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93575affa286089b92c8208aea4e60fe9fdd251a619a09b566d6e4e2cc123212" +checksum = "259cca0e975ecb05fd289ace45280c30ff792efc04e856a7f18b7fc86a3cb610" dependencies = [ - "smallvec 1.6.1", + "rustc-ap-rustc_data_structures", + "smallvec", ] [[package]] name = "rustc-ap-rustc_ast" -version = "705.0.0" +version = "712.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c700f2d3b25aa8d6446dd2936048737b08b2d547bd86e2a70afa9fee4e9c522" +checksum = "bb9be435d50c88e94bbad6ea468c8680b52c5043bb298ab8058d05251717f8f8" dependencies = [ "bitflags", "rustc-ap-rustc_data_structures", @@ -3230,15 +3291,15 @@ dependencies = [ "rustc-ap-rustc_macros", "rustc-ap-rustc_serialize", "rustc-ap-rustc_span", - "smallvec 1.6.1", + "smallvec", "tracing", ] [[package]] name = "rustc-ap-rustc_ast_passes" -version = "705.0.0" +version = "712.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e01f63e5259ee397bbe2e395d34a2e6b6b24f10c184d30fbbee1dcd7117f4f3" +checksum = "75246dd1a95a57f7767e53bde3971baa2d948078e180564709f5ea46cf863ddd" dependencies = [ "itertools 0.9.0", "rustc-ap-rustc_ast", @@ -3255,9 +3316,9 @@ dependencies = [ [[package]] name = "rustc-ap-rustc_ast_pretty" -version = "705.0.0" +version = "712.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "99d644c69c55deb24257cb0cb5261265fe5134f6f545e9062e1c18b07e422c68" +checksum = "79bede0b44bed453fd0034b7ba492840391f6486bf3e17a1af12922f0b98d4cc" dependencies = [ "rustc-ap-rustc_ast", "rustc-ap-rustc_span", @@ -3266,9 +3327,9 @@ dependencies = [ [[package]] name = "rustc-ap-rustc_attr" -version = "705.0.0" +version = "712.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "797fc68816d5396870f04e03d35164f5275d2502403239d4caec7ce063683f41" +checksum = "84a92a4a34b996694ca2dab70361c60d2d48c07adce57e8155b7ec75e069e3ea" dependencies = [ "rustc-ap-rustc_ast", "rustc-ap-rustc_ast_pretty", @@ -3284,11 +3345,11 @@ dependencies = [ [[package]] name = "rustc-ap-rustc_data_structures" -version = "705.0.0" +version = "712.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d840c4e6198b57982a54543ae604d634c7ceb7107f0c75970b88ebaff077ac5" +checksum = "9cbfa7f82517a1b2efe7106c864c3f930b1da8aff07a27fd317af2f36522fd2e" dependencies = [ - "arrayvec", + "arrayvec 0.5.2", "bitflags", "cfg-if 0.1.10", "crossbeam-utils 0.7.2", @@ -3305,7 +3366,7 @@ dependencies = [ "rustc-hash", "rustc-rayon", "rustc-rayon-core", - "smallvec 1.6.1", + "smallvec", "stable_deref_trait", "stacker", "tempfile", @@ -3315,9 +3376,9 @@ dependencies = [ [[package]] name = "rustc-ap-rustc_errors" -version = "705.0.0" +version = "712.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f2f99bdc828ad417636d9016611dc9047b641fadcb7f533b8b0e9616d81f90b" +checksum = "58a272a5101843bcb40900cc9ccf80ecfec62830bb1f4a242986da4a34c0da89" dependencies = [ "annotate-snippets 0.8.0", "atty", @@ -3335,9 +3396,9 @@ dependencies = [ [[package]] name = "rustc-ap-rustc_expand" -version = "705.0.0" +version = "712.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "27008b4c7ded287bf5cb20b84d6d5a6566329140f2e2bc8f6e68b37a34898595" +checksum = "3bc7988f3facf2402fe057405ef0f7fbacc7e7a483da25e35a35ac09491fbbfb" dependencies = [ "rustc-ap-rustc_ast", "rustc-ap-rustc_ast_passes", @@ -3353,15 +3414,15 @@ dependencies = [ "rustc-ap-rustc_serialize", "rustc-ap-rustc_session", "rustc-ap-rustc_span", - "smallvec 1.6.1", + "smallvec", "tracing", ] [[package]] name = "rustc-ap-rustc_feature" -version = "705.0.0" +version = "712.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6bb47b53670f1263ed1389dda932d5b5a6daf98579c1f076c2ee7d7f22709b7c" +checksum = "5e931cd1580ae60c5737d3fa57633034935e885414e794d83b3e52a81021985c" dependencies = [ "rustc-ap-rustc_data_structures", "rustc-ap-rustc_span", @@ -3369,41 +3430,41 @@ dependencies = [ [[package]] name = "rustc-ap-rustc_fs_util" -version = "705.0.0" +version = "712.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cdaddc4bae5ffab17037553e172f5014686db600050429aaa60aec14fe780e84" +checksum = "8fe9422e10d5b441d2a78202667bc85d7cf713a087b9ae6cdea0dfc825d79f07" [[package]] name = "rustc-ap-rustc_graphviz" -version = "705.0.0" +version = "712.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d73c72543311e88786f7380a3bfd946395579c1a0c0441a879a97fcdea79130" +checksum = "ffffffdef9fd51db69c1d4c045ced8aaab999be5627f2d3a0ce020d74c1f1e50" [[package]] name = "rustc-ap-rustc_index" -version = "705.0.0" +version = "712.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bba8d74ed4bad44a5b4264cf2a51ad0bd458ed56caa5bb090e989b8002ec6327" +checksum = "7f6f53afc4f7111c82295cb7ea3878f520bbac6a2c5a12e125b4ca9156498cff" dependencies = [ - "arrayvec", + "arrayvec 0.5.2", "rustc-ap-rustc_macros", "rustc-ap-rustc_serialize", ] [[package]] name = "rustc-ap-rustc_lexer" -version = "705.0.0" +version = "712.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a030d00510966cd31e13dca5e6c1bd40d303a932c54eca40e854188bca8c49e" +checksum = "8056b05346dff7e39164d0434c6ec443a14ab5fbf6221bd1a56e5abbeae5f60c" dependencies = [ "unicode-xid", ] [[package]] name = "rustc-ap-rustc_lint_defs" -version = "705.0.0" +version = "712.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bdff95da1b5d979183ef5c285817ba6cc67a1ac11296ef1e87b1b5bbaf57213c" +checksum = "364c3fb7b3cbdfe3fbb21d4078ff2cb3c58df63cda27995f8b064d21ee6dede5" dependencies = [ "rustc-ap-rustc_ast", "rustc-ap-rustc_data_structures", @@ -3416,9 +3477,9 @@ dependencies = [ [[package]] name = "rustc-ap-rustc_macros" -version = "705.0.0" +version = "712.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe3ed7401bf6f5a256d58cd0e1c1e2e77eec25e60a0d7ad75313962edcb4e396" +checksum = "4607d6879cae3bae4d0369ca4b3a7510fd6295ac32eec088ac975208ba96ca45" dependencies = [ "proc-macro2", "quote", @@ -3428,9 +3489,9 @@ dependencies = [ [[package]] name = "rustc-ap-rustc_parse" -version = "705.0.0" +version = "712.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "609a624baffa3f99847d57d30c96ee6732ce0912f8df4be239b6fd91533910d6" +checksum = "78d22889bff7ca2346037c9df7ea55c66ffb714f5b50fb62b41975f8ac7a2d70" dependencies = [ "bitflags", "rustc-ap-rustc_ast", @@ -3441,26 +3502,26 @@ dependencies = [ "rustc-ap-rustc_lexer", "rustc-ap-rustc_session", "rustc-ap-rustc_span", - "smallvec 1.6.1", + "smallvec", "tracing", "unicode-normalization", ] [[package]] name = "rustc-ap-rustc_serialize" -version = "705.0.0" +version = "712.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc232e2a351d8131c8f1386ce372ee22ef7b1b0b897bbf817a8ce4792029a564" +checksum = "d33c710120953c0214f47a6caf42064d7e241003b4af36c98a6d6156e70335f1" dependencies = [ "indexmap", - "smallvec 1.6.1", + "smallvec", ] [[package]] name = "rustc-ap-rustc_session" -version = "705.0.0" +version = "712.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "18acf94c820cd0c64ee1cbd811fd1f4d5ba18987c457c88771359b90cb1a12f5" +checksum = "6d35919041429a90713c8f704fa5209ba159cb554ce74d95722cbc18ac4b4c6f" dependencies = [ "bitflags", "getopts", @@ -3480,9 +3541,9 @@ dependencies = [ [[package]] name = "rustc-ap-rustc_span" -version = "705.0.0" +version = "712.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3479f453a38b6a5572938d035fc2b3cb6ec379c57f598b8682b512eb90c7858" +checksum = "73b12170c69603c0bf4b50e5c25fd348aae13b8c6465aa0ef4389c9eaa568e51" dependencies = [ "cfg-if 0.1.10", "md-5", @@ -3500,9 +3561,9 @@ dependencies = [ [[package]] name = "rustc-ap-rustc_target" -version = "705.0.0" +version = "712.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78cacaf829778cf07bb97a9f4604896789de12392175f3743e74a30ed370f1c1" +checksum = "0a8329d92e7dc24b974f759e6c6e97e2bbc47b18d0573343028f8135ca367200" dependencies = [ "bitflags", "rustc-ap-rustc_data_structures", @@ -3533,16 +3594,17 @@ checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" name = "rustc-main" version = "0.0.0" dependencies = [ - "jemalloc-sys", "rustc_codegen_ssa", "rustc_driver", + "tikv-jemalloc-sys", + "tikv-jemallocator", ] [[package]] name = "rustc-rayon" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f32767f90d938f1b7199a174ef249ae1924f6e5bbdb9d112fea141e016f25b3a" +checksum = "ed7d6a39f8bfd4421ce720918234d1e672b83824c91345b47c93746839cf1629" dependencies = [ "crossbeam-deque", "either", @@ -3551,13 +3613,13 @@ dependencies = [ [[package]] name = "rustc-rayon-core" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea2427831f0053ea3ea73559c8eabd893133a51b251d142bacee53c62a288cb3" +checksum = "e94187d9ea3e8c38fafdbc38acb94eafa7ce155867f6ccb13830466a0d0db8c6" dependencies = [ "crossbeam-deque", - "crossbeam-queue 0.1.2", - "crossbeam-utils 0.6.6", + "crossbeam-queue", + "crossbeam-utils 0.7.2", "lazy_static", "num_cpus", ] @@ -3601,8 +3663,7 @@ dependencies = [ "quote", "serde", "serde_json", - "smallvec 0.6.14", - "smallvec 1.6.1", + "smallvec", "syn", "url 2.1.1", "winapi 0.3.9", @@ -3613,14 +3674,15 @@ name = "rustc_apfloat" version = "0.0.0" dependencies = [ "bitflags", - "smallvec 1.6.1", + "smallvec", ] [[package]] name = "rustc_arena" version = "0.0.0" dependencies = [ - "smallvec 1.6.1", + "rustc_data_structures", + "smallvec", ] [[package]] @@ -3634,7 +3696,7 @@ dependencies = [ "rustc_macros", "rustc_serialize", "rustc_span", - "smallvec 1.6.1", + "smallvec", "tracing", ] @@ -3652,7 +3714,7 @@ dependencies = [ "rustc_session", "rustc_span", "rustc_target", - "smallvec 1.6.1", + "smallvec", "tracing", ] @@ -3715,7 +3777,7 @@ dependencies = [ "rustc_session", "rustc_span", "rustc_target", - "smallvec 1.6.1", + "smallvec", "tracing", ] @@ -3744,7 +3806,7 @@ dependencies = [ "rustc_session", "rustc_span", "rustc_target", - "smallvec 1.6.1", + "smallvec", "snap", "tracing", ] @@ -3758,7 +3820,6 @@ dependencies = [ "itertools 0.9.0", "jobserver", "libc", - "memmap", "pathdiff", "rustc_apfloat", "rustc_ast", @@ -3784,7 +3845,7 @@ dependencies = [ name = "rustc_data_structures" version = "0.0.0" dependencies = [ - "arrayvec", + "arrayvec 0.7.0", "bitflags", "cfg-if 0.1.10", "crossbeam-utils 0.7.2", @@ -3793,6 +3854,7 @@ dependencies = [ "jobserver", "libc", "measureme", + "memmap2", "parking_lot", "rustc-hash", "rustc-rayon", @@ -3801,7 +3863,7 @@ dependencies = [ "rustc_index", "rustc_macros", "rustc_serialize", - "smallvec 1.6.1", + "smallvec", "stable_deref_trait", "stacker", "tempfile", @@ -3829,6 +3891,7 @@ dependencies = [ "rustc_metadata", "rustc_middle", "rustc_mir", + "rustc_mir_build", "rustc_parse", "rustc_plugin_impl", "rustc_save_analysis", @@ -3836,6 +3899,7 @@ dependencies = [ "rustc_session", "rustc_span", "rustc_target", + "rustc_typeck", "tracing", "tracing-subscriber", "tracing-tree", @@ -3882,7 +3946,7 @@ dependencies = [ "rustc_serialize", "rustc_session", "rustc_span", - "smallvec 1.6.1", + "smallvec", "tracing", ] @@ -3914,7 +3978,7 @@ dependencies = [ "rustc_serialize", "rustc_span", "rustc_target", - "smallvec 1.6.1", + "smallvec", "tracing", ] @@ -3933,7 +3997,7 @@ dependencies = [ name = "rustc_incremental" version = "0.0.0" dependencies = [ - "rand", + "rand 0.7.3", "rustc_ast", "rustc_data_structures", "rustc_fs_util", @@ -3951,7 +4015,7 @@ dependencies = [ name = "rustc_index" version = "0.0.0" dependencies = [ - "arrayvec", + "arrayvec 0.7.0", "rustc_macros", "rustc_serialize", ] @@ -3972,7 +4036,7 @@ dependencies = [ "rustc_session", "rustc_span", "rustc_target", - "smallvec 1.6.1", + "smallvec", "tracing", ] @@ -4016,7 +4080,7 @@ dependencies = [ "rustc_traits", "rustc_ty_utils", "rustc_typeck", - "smallvec 1.6.1", + "smallvec", "tempfile", "tracing", "winapi 0.3.9", @@ -4090,7 +4154,6 @@ name = "rustc_metadata" version = "0.0.0" dependencies = [ "libc", - "memmap", "rustc_ast", "rustc_attr", "rustc_data_structures", @@ -4106,7 +4169,7 @@ dependencies = [ "rustc_session", "rustc_span", "rustc_target", - "smallvec 1.6.1", + "smallvec", "snap", "stable_deref_trait", "tracing", @@ -4138,7 +4201,7 @@ dependencies = [ "rustc_span", "rustc_target", "rustc_type_ir", - "smallvec 1.6.1", + "smallvec", "tracing", ] @@ -4169,7 +4232,7 @@ dependencies = [ "rustc_span", "rustc_target", "rustc_trait_selection", - "smallvec 1.6.1", + "smallvec", "tracing", ] @@ -4192,7 +4255,7 @@ dependencies = [ "rustc_span", "rustc_target", "rustc_trait_selection", - "smallvec 1.6.1", + "smallvec", "tracing", ] @@ -4209,7 +4272,7 @@ dependencies = [ "rustc_lexer", "rustc_session", "rustc_span", - "smallvec 1.6.1", + "smallvec", "tracing", "unicode-normalization", ] @@ -4227,6 +4290,7 @@ name = "rustc_passes" version = "0.0.0" dependencies = [ "rustc_ast", + "rustc_ast_pretty", "rustc_attr", "rustc_data_structures", "rustc_errors", @@ -4309,7 +4373,7 @@ dependencies = [ "rustc_serialize", "rustc_session", "rustc_span", - "smallvec 1.6.1", + "smallvec", "tracing", ] @@ -4333,7 +4397,7 @@ dependencies = [ "rustc_middle", "rustc_session", "rustc_span", - "smallvec 1.6.1", + "smallvec", "tracing", ] @@ -4362,7 +4426,7 @@ version = "0.0.0" dependencies = [ "indexmap", "rustc_macros", - "smallvec 1.6.1", + "smallvec", ] [[package]] @@ -4459,7 +4523,7 @@ dependencies = [ "rustc_session", "rustc_span", "rustc_target", - "smallvec 1.6.1", + "smallvec", "tracing", ] @@ -4479,7 +4543,7 @@ dependencies = [ "rustc_middle", "rustc_span", "rustc_trait_selection", - "smallvec 1.6.1", + "smallvec", "tracing", ] @@ -4529,24 +4593,24 @@ dependencies = [ "rustc_span", "rustc_target", "rustc_trait_selection", - "smallvec 1.6.1", + "smallvec", "tracing", ] [[package]] name = "rustc_version" -version = "0.2.3" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" +checksum = "f0dfe2087c51c460008730de8b57e6a320782fbfb312e1f4d520e6c6fae155ee" dependencies = [ - "semver 0.9.0", + "semver 0.11.0", ] [[package]] name = "rustdoc" version = "0.0.0" dependencies = [ - "arrayvec", + "arrayvec 0.7.0", "expect-test", "itertools 0.9.0", "minifier", @@ -4556,7 +4620,7 @@ dependencies = [ "rustdoc-json-types", "serde", "serde_json", - "smallvec 1.6.1", + "smallvec", "tempfile", "tracing", "tracing-subscriber", @@ -4568,6 +4632,7 @@ name = "rustdoc-json-types" version = "0.1.0" dependencies = [ "serde", + "serde_json", ] [[package]] @@ -4605,7 +4670,7 @@ dependencies = [ [[package]] name = "rustfmt-nightly" -version = "1.4.36" +version = "1.4.37" dependencies = [ "annotate-snippets 0.6.1", "anyhow", @@ -4623,7 +4688,6 @@ dependencies = [ "regex", "rustc-ap-rustc_ast", "rustc-ap-rustc_ast_pretty", - "rustc-ap-rustc_attr", "rustc-ap-rustc_data_structures", "rustc-ap-rustc_errors", "rustc-ap-rustc_expand", @@ -4735,7 +4799,7 @@ version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f301af10236f6df4160f7c3f04eec6dbc70ace82d23326abad5edee88801c6b6" dependencies = [ - "semver-parser 0.10.1", + "semver-parser 0.10.2", "serde", ] @@ -4747,27 +4811,27 @@ checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" [[package]] name = "semver-parser" -version = "0.10.1" +version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42ef146c2ad5e5f4b037cd6ce2ebb775401729b19a82040c1beac9d36c7d1428" +checksum = "00b0bef5b7f9e0df16536d3961cfb6e84331c065b4066afb39768d0e319411f7" dependencies = [ "pest", ] [[package]] name = "serde" -version = "1.0.118" +version = "1.0.125" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06c64263859d87aa2eb554587e2d23183398d617427327cf2b3d0ed8c69e4800" +checksum = "558dc50e1a5a5fa7112ca2ce4effcb321b0300c0d4ccf0776a9f60cd89031171" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.118" +version = "1.0.125" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c84d3526699cd55261af4b941e4e725444df67aa4f9e6a3564f18030d12672df" +checksum = "b093b7a2bb58203b5da3056c05b4ec1fed827dcfdb37347a8841695263b3d06d" dependencies = [ "proc-macro2", "quote", @@ -4873,11 +4937,10 @@ checksum = "42a568c8f2cd051a4d283bd6eb0343ac214c1b0f1ac19f93e1175b2dee38c73d" [[package]] name = "signal-hook-registry" -version = "1.2.1" +version = "1.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3e12110bc539e657a646068aaf5eb5b63af9d0c1f7b29c97113fad80e15f035" +checksum = "ce32ea0c6c56d5eacaeb814fbed9960547021d3edd010ded1425f180536b20ab" dependencies = [ - "arc-swap", "libc", ] @@ -4889,9 +4952,9 @@ checksum = "fa8f3741c7372e75519bd9346068370c9cdaabcc1f9599cbcf2a2719352286b7" [[package]] name = "sized-chunks" -version = "0.6.2" +version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ec31ceca5644fa6d444cc77548b88b67f46db6f7c71683b0f9336e671830d2f" +checksum = "65e65d6a9f13cd78f361ea5a2cf53a45d67cdda421ba0316b9be101560f3d207" dependencies = [ "bitmaps", "typenum", @@ -4903,15 +4966,6 @@ version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c111b5bd5695e56cffe5129854aa230b39c93a305372fdbb2668ca2394eea9f8" -[[package]] -name = "smallvec" -version = "0.6.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b97fcaeba89edba30f044a10c6a3cc39df9c3f17d7cd829dd1446cab35f890e0" -dependencies = [ - "maybe-uninit", -] - [[package]] name = "smallvec" version = "1.6.1" @@ -4966,7 +5020,7 @@ dependencies = [ "core", "dlmalloc", "fortanix-sgx-abi", - "hashbrown", + "hashbrown 0.11.0", "hermit-abi", "libc", "miniz_oxide", @@ -4974,12 +5028,24 @@ dependencies = [ "panic_abort", "panic_unwind", "profiler_builtins", - "rand", + "rand 0.7.3", "rustc-demangle", + "std_detect", "unwind", "wasi", ] +[[package]] +name = "std_detect" +version = "0.1.5" +dependencies = [ + "cfg-if 0.1.10", + "compiler_builtins", + "libc", + "rustc-std-workspace-alloc", + "rustc-std-workspace-core", +] + [[package]] name = "string_cache" version = "0.8.0" @@ -5064,9 +5130,9 @@ dependencies = [ [[package]] name = "syn" -version = "1.0.38" +version = "1.0.65" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e69abc24912995b3038597a7a593be5053eb0fb44f3cc5beec0deb421790c1f4" +checksum = "f3a1d708c221c5a612956ef9f75b37e454e88d1f7b899fbd3a18d4252012d663" dependencies = [ "proc-macro2", "quote", @@ -5105,7 +5171,7 @@ checksum = "7a6e24d9338a0a5be79593e2fa15a648add6138caa803e2d5bc782c371732ca9" dependencies = [ "cfg-if 0.1.10", "libc", - "rand", + "rand 0.7.3", "redox_syscall 0.1.57", "remove_dir_all", "winapi 0.3.9", @@ -5185,17 +5251,6 @@ dependencies = [ "term 0.0.0", ] -[[package]] -name = "tester" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee72ec31009a42b53de9a6b7d8f462b493ab3b1e4767bda1fcdbb52127f13b6c" -dependencies = [ - "getopts", - "libc", - "term 0.6.1", -] - [[package]] name = "tester" version = "0.9.0" @@ -5252,6 +5307,7 @@ name = "tidy" version = "0.1.0" dependencies = [ "cargo_metadata 0.11.1", + "crossbeam-utils 0.8.0", "lazy_static", "regex", "walkdir", @@ -5261,6 +5317,27 @@ dependencies = [ name = "tier-check" version = "0.1.0" +[[package]] +name = "tikv-jemalloc-sys" +version = "0.4.1+5.2.1-patched" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a26331b05179d4cb505c8d6814a7e18d298972f0a551b0e3cefccff927f86d3" +dependencies = [ + "cc", + "fs_extra", + "libc", +] + +[[package]] +name = "tikv-jemallocator" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c14a5a604eb8715bc5785018a37d00739b180bcf609916ddf4393d33d49ccdf" +dependencies = [ + "libc", + "tikv-jemalloc-sys", +] + [[package]] name = "time" version = "0.1.43" @@ -5408,7 +5485,7 @@ dependencies = [ "serde", "serde_json", "sharded-slab", - "smallvec 1.6.1", + "smallvec", "thread_local", "tracing", "tracing-core", @@ -5418,9 +5495,9 @@ dependencies = [ [[package]] name = "tracing-tree" -version = "0.1.8" +version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a60657cfbf397c603257a8230b3f427e6a2a4e5911a59331b9bb4dffff5b608" +checksum = "1712b40907f8d9bc2bc66763ab61dec914b7123d7149e59feb0d4e2a95fc4967" dependencies = [ "ansi_term 0.12.1", "atty", @@ -5608,12 +5685,17 @@ checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191" [[package]] name = "vergen" -version = "3.1.0" +version = "5.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ce50d8996df1f85af15f2cd8d33daae6e479575123ef4314a51a70a230739cb" +checksum = "dfbc87f9a7a9d61b15d51d1d3547284f67b6b4f1494ce3fc5814c101f35a5183" dependencies = [ - "bitflags", + "anyhow", "chrono", + "enum-iterator", + "getset", + "git2", + "rustversion", + "thiserror", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index f961d3e9b97be..02011357eac9b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -45,6 +45,8 @@ exclude = [ # not all `Cargo.toml` files are available, so we exclude the `x` binary, # so it can be invoked before the current checkout is set up. "src/tools/x", + # stdarch has its own Cargo workspace + "library/stdarch", ] [profile.release.package.compiler_builtins] @@ -88,6 +90,7 @@ object.debug = 0 # vendored copy. [patch."https://github.com/rust-lang/cargo"] cargo = { path = "src/tools/cargo" } +cargo-util = { path = "src/tools/cargo/crates/cargo-util" } [patch."https://github.com/rust-lang/rustfmt"] # Similar to Cargo above we want the RLS to use a vendored version of `rustfmt` diff --git a/README.md b/README.md index 6ab11e7e2be6d..5ec94e189f835 100644 --- a/README.md +++ b/README.md @@ -31,7 +31,7 @@ by running `./x.py --help` or reading the [rustc dev guide][rustcguidebuild]. * `g++` 5.1 or later or `clang++` 3.5 or later * `python` 3 or 2.7 * GNU `make` 3.81 or later - * `cmake` 3.4.3 or later + * `cmake` 3.13.4 or later * `ninja` * `curl` * `git` @@ -90,7 +90,7 @@ build. [MSYS2][msys2] can be used to easily build Rust on Windows: -[msys2]: https://msys2.github.io/ +[msys2]: https://www.msys2.org/ 1. Grab the latest [MSYS2 installer][msys2] and go through the installer. diff --git a/RELEASES.md b/RELEASES.md index 5dca7abcb2629..024610bc7a417 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -1,3 +1,181 @@ +Version 1.51.0 (2021-03-25) +============================ + +Language +-------- +- [You can now parameterize items such as functions, traits, and `struct`s by constant + values in addition to by types and lifetimes.][79135] Also known as "const generics" + E.g. you can now write the following. Note: Only values of primitive integers, + `bool`, or `char` types are currently permitted. + ```rust + struct GenericArray { + inner: [T; LENGTH] + } + + impl GenericArray { + const fn last(&self) -> Option<&T> { + if LENGTH == 0 { + None + } else { + Some(&self.inner[LENGTH - 1]) + } + } + } + ``` + + +Compiler +-------- + +- [Added the `-Csplit-debuginfo` codegen option for macOS platforms.][79570] + This option controls whether debug information is split across multiple files + or packed into a single file. **Note** This option is unstable on other platforms. +- [Added tier 3\* support for `aarch64_be-unknown-linux-gnu`, + `aarch64-unknown-linux-gnu_ilp32`, and `aarch64_be-unknown-linux-gnu_ilp32` targets.][81455] +- [Added tier 3 support for `i386-unknown-linux-gnu` and `i486-unknown-linux-gnu` targets.][80662] +- [The `target-cpu=native` option will now detect individual features of CPUs.][80749] + +\* Refer to Rust's [platform support page][platform-support-doc] for more +information on Rust's tiered platform support. + +Libraries +--------- + +- [`Box::downcast` is now also implemented for any `dyn Any + Send + Sync` object.][80945] +- [`str` now implements `AsMut`.][80279] +- [`u64` and `u128` now implement `From`.][79502] +- [`Error` is now implemented for `&T` where `T` implements `Error`.][75180] +- [`Poll::{map_ok, map_err}` are now implemented for `Poll>>`.][80968] +- [`unsigned_abs` is now implemented for all signed integer types.][80959] +- [`io::Empty` now implements `io::Seek`.][78044] +- [`rc::Weak` and `sync::Weak`'s methods such as `as_ptr` are now implemented for + `T: ?Sized` types.][80764] +- [`Div` and `Rem` by their `NonZero` variant is now implemented for all unsigned integers.][79134] + + +Stabilized APIs +--------------- + +- [`Arc::decrement_strong_count`] +- [`Arc::increment_strong_count`] +- [`Once::call_once_force`] +- [`Peekable::next_if_eq`] +- [`Peekable::next_if`] +- [`Seek::stream_position`] +- [`array::IntoIter`] +- [`panic::panic_any`] +- [`ptr::addr_of!`] +- [`ptr::addr_of_mut!`] +- [`slice::fill_with`] +- [`slice::split_inclusive_mut`] +- [`slice::split_inclusive`] +- [`slice::strip_prefix`] +- [`slice::strip_suffix`] +- [`str::split_inclusive`] +- [`sync::OnceState`] +- [`task::Wake`] +- [`VecDeque::range`] +- [`VecDeque::range_mut`] + +Cargo +----- +- [Added the `split-debuginfo` profile option to control the -Csplit-debuginfo + codegen option.][cargo/9112] +- [Added the `resolver` field to `Cargo.toml` to enable the new feature resolver + and CLI option behavior.][cargo/8997] Version 2 of the feature resolver will try + to avoid unifying features of dependencies where that unification could be unwanted. + Such as using the same dependency with a `std` feature in a build scripts and + proc-macros, while using the `no-std` feature in the final binary. See the + [Cargo book documentation][feature-resolver@2.0] for more information on the feature. + +Rustdoc +------- + +- [Rustdoc will now include documentation for methods available from _nested_ `Deref` traits.][80653] +- [You can now provide a `--default-theme` flag which sets the default theme to use for + documentation.][79642] + +Various improvements to intra-doc links: + +- [You can link to non-path primitives such as `slice`.][80181] +- [You can link to associated items.][74489] +- [You can now include generic parameters when linking to items, like `Vec`.][76934] + +Misc +---- +- [You can now pass `--include-ignored` to tests (e.g. with + `cargo test -- --include-ignored`) to include testing tests marked `#[ignore]`.][80053] + +Compatibility Notes +------------------- + +- [WASI platforms no longer use the `wasm-bindgen` ABI, and instead use the wasm32 ABI.][79998] +- [`rustc` no longer promotes division, modulo and indexing operations to `const` that + could fail.][80579] +- [The minimum version of glibc for the following platforms has been bumped to version 2.31 + for the distributed artifacts.][81521] + - `armv5te-unknown-linux-gnueabi` + - `sparc64-unknown-linux-gnu` + - `thumbv7neon-unknown-linux-gnueabihf` + - `armv7-unknown-linux-gnueabi` + - `x86_64-unknown-linux-gnux32` +- [`atomic::spin_loop_hint` has been deprecated.][80966] It's recommended to use `hint::spin_loop` instead. + +Internal Only +------------- + +- [Consistently avoid constructing optimized MIR when not doing codegen][80718] + +[79135]: https://github.com/rust-lang/rust/pull/79135 +[74489]: https://github.com/rust-lang/rust/pull/74489 +[76934]: https://github.com/rust-lang/rust/pull/76934 +[79570]: https://github.com/rust-lang/rust/pull/79570 +[80181]: https://github.com/rust-lang/rust/pull/80181 +[79642]: https://github.com/rust-lang/rust/pull/79642 +[80945]: https://github.com/rust-lang/rust/pull/80945 +[80279]: https://github.com/rust-lang/rust/pull/80279 +[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 +[80718]: https://github.com/rust-lang/rust/pull/80718 +[80653]: https://github.com/rust-lang/rust/pull/80653 +[80579]: https://github.com/rust-lang/rust/pull/80579 +[79998]: https://github.com/rust-lang/rust/pull/79998 +[78044]: https://github.com/rust-lang/rust/pull/78044 +[81455]: https://github.com/rust-lang/rust/pull/81455 +[80764]: https://github.com/rust-lang/rust/pull/80764 +[80749]: https://github.com/rust-lang/rust/pull/80749 +[80662]: https://github.com/rust-lang/rust/pull/80662 +[79134]: https://github.com/rust-lang/rust/pull/79134 +[80966]: https://github.com/rust-lang/rust/pull/80966 +[cargo/8997]: https://github.com/rust-lang/cargo/pull/8997 +[cargo/9112]: https://github.com/rust-lang/cargo/pull/9112 +[feature-resolver@2.0]: https://doc.rust-lang.org/nightly/cargo/reference/features.html#feature-resolver-version-2 +[`Once::call_once_force`]: https://doc.rust-lang.org/stable/std/sync/struct.Once.html#method.call_once_force +[`sync::OnceState`]: https://doc.rust-lang.org/stable/std/sync/struct.OnceState.html +[`panic::panic_any`]: https://doc.rust-lang.org/stable/std/panic/fn.panic_any.html +[`slice::strip_prefix`]: https://doc.rust-lang.org/stable/std/primitive.slice.html#method.strip_prefix +[`slice::strip_suffix`]: https://doc.rust-lang.org/stable/std/primitive.slice.html#method.strip_prefix +[`Arc::increment_strong_count`]: https://doc.rust-lang.org/nightly/std/sync/struct.Arc.html#method.increment_strong_count +[`Arc::decrement_strong_count`]: https://doc.rust-lang.org/nightly/std/sync/struct.Arc.html#method.decrement_strong_count +[`slice::fill_with`]: https://doc.rust-lang.org/nightly/std/primitive.slice.html#method.fill_with +[`ptr::addr_of!`]: https://doc.rust-lang.org/nightly/std/ptr/macro.addr_of.html +[`ptr::addr_of_mut!`]: https://doc.rust-lang.org/nightly/std/ptr/macro.addr_of_mut.html +[`array::IntoIter`]: https://doc.rust-lang.org/nightly/std/array/struct.IntoIter.html +[`slice::split_inclusive`]: https://doc.rust-lang.org/nightly/std/primitive.slice.html#method.split_inclusive +[`slice::split_inclusive_mut`]: https://doc.rust-lang.org/nightly/std/primitive.slice.html#method.split_inclusive_mut +[`str::split_inclusive`]: https://doc.rust-lang.org/nightly/std/primitive.str.html#method.split_inclusive +[`task::Wake`]: https://doc.rust-lang.org/nightly/std/task/trait.Wake.html +[`Seek::stream_position`]: https://doc.rust-lang.org/nightly/std/io/trait.Seek.html#method.stream_position +[`Peekable::next_if`]: https://doc.rust-lang.org/nightly/std/iter/struct.Peekable.html#method.next_if +[`Peekable::next_if_eq`]: https://doc.rust-lang.org/nightly/std/iter/struct.Peekable.html#method.next_if_eq +[`VecDeque::range`]: https://doc.rust-lang.org/nightly/std/collections/struct.VecDeque.html#method.range +[`VecDeque::range_mut`]: https://doc.rust-lang.org/nightly/std/collections/struct.VecDeque.html#method.range_mut + Version 1.50.0 (2021-02-11) ============================ @@ -14,7 +192,7 @@ Compiler - [The `x86_64-unknown-freebsd` is now built with the full toolset.][79484] - [Dropped support for all cloudabi targets.][78439] -\* Refer to Rust's [platform support page][forge-platform-support] for more +\* Refer to Rust's [platform support page][platform-support-doc] for more information on Rust's tiered platform support. Libraries @@ -191,7 +369,7 @@ Compiler - [Output from threads spawned in tests is now captured.][78227] - [Change os and vendor values to "none" and "unknown" for some targets][78951] -\* Refer to Rust's [platform support page][forge-platform-support] for more +\* Refer to Rust's [platform support page][platform-support-doc] for more information on Rust's tiered platform support. Libraries @@ -296,7 +474,7 @@ Compiler Note: If you're using cargo you must explicitly pass the `--target` flag. - [Added tier 2\* support for `aarch64-unknown-linux-musl`.][76420] -\* Refer to Rust's [platform support page][forge-platform-support] for more +\* Refer to Rust's [platform support page][platform-support-doc] for more information on Rust's tiered platform support. Libraries @@ -435,7 +613,7 @@ Compiler - [Upgrade the FreeBSD toolchain to version 11.4][75204] - [`RUST_BACKTRACE`'s output is now more compact.][75048] -\* Refer to Rust's [platform support page][forge-platform-support] for more +\* Refer to Rust's [platform support page][platform-support-doc] for more information on Rust's tiered platform support. Libraries @@ -720,7 +898,7 @@ Compiler - [Added tier 3 support for the `thumbv7a-uwp-windows-msvc` target.][72133] - [Upgraded to LLVM 10.][67759] -\* Refer to Rust's [platform support page][forge-platform-support] for more +\* Refer to Rust's [platform support page][platform-support-doc] for more information on Rust's tiered platform support. @@ -1229,7 +1407,7 @@ Compiler pointing to the location where they were called, rather than `core`'s internals. ][67887] -\* Refer to Rust's [platform support page][forge-platform-support] for more +\* Refer to Rust's [platform support page][platform-support-doc] for more information on Rust's tiered platform support. Libraries @@ -1327,7 +1505,7 @@ Compiler - [You can now provide `--extern` flag without a path, indicating that it is available from the search path or specified with an `-L` flag.][64882] -\* Refer to Rust's [platform support page][forge-platform-support] for more +\* Refer to Rust's [platform support page][platform-support-doc] for more information on Rust's tiered platform support. [argfile-docs]: https://doc.rust-lang.org/nightly/rustc/command-line-arguments.html#path-load-command-line-flags-from-a-path @@ -1451,7 +1629,7 @@ Compiler - [Added tier 3 support for the `mips64-unknown-linux-muslabi64`, and `mips64el-unknown-linux-muslabi64` targets.][65843] -\* Refer to Rust's [platform support page][forge-platform-support] for more +\* Refer to Rust's [platform support page][platform-support-doc] for more information on Rust's tiered platform support. Libraries @@ -1601,7 +1779,7 @@ Compiler output of successful tests.][62600] -\* Refer to Rust's [platform support page][forge-platform-support] for more +\* Refer to Rust's [platform support page][platform-support-doc] for more information on Rust's tiered platform support. Libraries @@ -1695,7 +1873,7 @@ Compiler - [Added tier 3 support for the `riscv32i-unknown-none-elf` target.][62784] - [Upgraded to LLVM 9.][62592] -\* Refer to Rust's [platform support page][forge-platform-support] for more +\* Refer to Rust's [platform support page][platform-support-doc] for more information on Rust's tiered platform support. Libraries @@ -1737,6 +1915,11 @@ Cargo - [You can now pass the `--features` option multiple times to enable multiple features.][cargo/7084] +Rustdoc +------- + +- [Documentation on `pub use` statements is prepended to the documentation of the re-exported item][63048] + Misc ---- - [`rustc` will now warn about some incorrect uses of @@ -1771,6 +1954,7 @@ Compatibility Notes [63421]: https://github.com/rust-lang/rust/pull/63421/ [cargo/7084]: https://github.com/rust-lang/cargo/pull/7084/ [cargo/7143]: https://github.com/rust-lang/cargo/pull/7143/ +[63048]: https://github.com/rust-lang/rust/pull/63048 [`<*const T>::cast`]: https://doc.rust-lang.org/std/primitive.pointer.html#method.cast [`<*mut T>::cast`]: https://doc.rust-lang.org/std/primitive.pointer.html#method.cast [`Duration::as_secs_f32`]: https://doc.rust-lang.org/std/time/struct.Duration.html#method.as_secs_f32 @@ -1782,7 +1966,7 @@ Compatibility Notes [`Duration::mul_f32`]: https://doc.rust-lang.org/std/time/struct.Duration.html#method.mul_f32 [`Duration::mul_f64`]: https://doc.rust-lang.org/std/time/struct.Duration.html#method.mul_f64 [`any::type_name`]: https://doc.rust-lang.org/std/any/fn.type_name.html -[forge-platform-support]: https://forge.rust-lang.org/release/platform-support.html +[platform-support-doc]: https://doc.rust-lang.org/nightly/rustc/platform-support.html [pipeline-internals]: https://internals.rust-lang.org/t/evaluating-pipelined-rustc-compilation/10199 Version 1.37.0 (2019-08-15) diff --git a/compiler/rustc/Cargo.toml b/compiler/rustc/Cargo.toml index 6e6c0c71a1f3b..ca6055c46a647 100644 --- a/compiler/rustc/Cargo.toml +++ b/compiler/rustc/Cargo.toml @@ -11,12 +11,16 @@ rustc_driver = { path = "../rustc_driver" } # crate is intended to be used by codegen backends, which may not be in-tree. rustc_codegen_ssa = { path = "../rustc_codegen_ssa" } -[dependencies.jemalloc-sys] -version = '0.3.0' +[dependencies.tikv-jemalloc-sys] +version = '0.4.0' optional = true features = ['unprefixed_malloc_on_supported_platforms'] +[dependencies.tikv-jemallocator] +version = '0.4.0' +optional = true + [features] -jemalloc = ['jemalloc-sys'] +jemalloc = ['tikv-jemalloc-sys', 'tikv-jemallocator'] llvm = ['rustc_driver/llvm'] max_level_info = ['rustc_driver/max_level_info'] diff --git a/compiler/rustc/src/main.rs b/compiler/rustc/src/main.rs index 6bc5aa6382c4c..c80fab99496bc 100644 --- a/compiler/rustc/src/main.rs +++ b/compiler/rustc/src/main.rs @@ -1,13 +1,26 @@ +// Configure jemalloc as the `global_allocator` when configured. This is +// so that we use the sized deallocation apis jemalloc provides +// (namely `sdallocx`). +// +// The symbol overrides documented below are also performed so that we can +// ensure that we use a consistent allocator across the rustc <-> llvm boundary +#[cfg(feature = "jemalloc")] +#[global_allocator] +static ALLOC: tikv_jemallocator::Jemalloc = tikv_jemallocator::Jemalloc; + +#[cfg(feature = "tikv-jemalloc-sys")] +use tikv_jemalloc_sys as jemalloc_sys; + fn main() { // Pull in jemalloc when enabled. // // Note that we're pulling in a static copy of jemalloc which means that to // pull it in we need to actually reference its symbols for it to get // linked. The two crates we link to here, std and rustc_driver, are both - // dynamic libraries. That means to pull in jemalloc we need to actually + // dynamic libraries. That means to pull in jemalloc we actually need to // reference allocation symbols one way or another (as this file is the only // object code in the rustc executable). - #[cfg(feature = "jemalloc-sys")] + #[cfg(feature = "tikv-jemalloc-sys")] { use std::os::raw::{c_int, c_void}; @@ -24,6 +37,20 @@ fn main() { static _F5: unsafe extern "C" fn(*mut c_void, usize) -> *mut c_void = jemalloc_sys::realloc; #[used] static _F6: unsafe extern "C" fn(*mut c_void) = jemalloc_sys::free; + + // On OSX, jemalloc doesn't directly override malloc/free, but instead + // registers itself with the allocator's zone APIs in a ctor. However, + // the linker doesn't seem to consider ctors as "used" when statically + // linking, so we need to explicitly depend on the function. + #[cfg(target_os = "macos")] + { + extern "C" { + fn _rjem_je_zone_register(); + } + + #[used] + static _F7: unsafe extern "C" fn() = _rjem_je_zone_register; + } } rustc_driver::set_sigpipe_handler(); diff --git a/compiler/rustc_apfloat/src/ieee.rs b/compiler/rustc_apfloat/src/ieee.rs index 71bcb8f090d07..96277950cfe1a 100644 --- a/compiler/rustc_apfloat/src/ieee.rs +++ b/compiler/rustc_apfloat/src/ieee.rs @@ -2273,6 +2273,7 @@ impl Loss { mod sig { use super::{limbs_for_bits, ExpInt, Limb, Loss, LIMB_BITS}; use core::cmp::Ordering; + use core::iter; use core::mem; pub(super) fn is_all_zeros(limbs: &[Limb]) -> bool { @@ -2483,7 +2484,7 @@ mod sig { pub(super) fn add(a: &mut [Limb], b: &[Limb], mut c: Limb) -> Limb { assert!(c <= 1); - for (a, &b) in a.iter_mut().zip(b) { + for (a, &b) in iter::zip(a, b) { let (r, overflow) = a.overflowing_add(b); let (r, overflow2) = r.overflowing_add(c); *a = r; @@ -2497,7 +2498,7 @@ mod sig { pub(super) fn sub(a: &mut [Limb], b: &[Limb], mut c: Limb) -> Limb { assert!(c <= 1); - for (a, &b) in a.iter_mut().zip(b) { + for (a, &b) in iter::zip(a, b) { let (r, overflow) = a.overflowing_sub(b); let (r, overflow2) = r.overflowing_sub(c); *a = r; diff --git a/compiler/rustc_apfloat/src/lib.rs b/compiler/rustc_apfloat/src/lib.rs index 4a845fcb6917b..c648147d108fb 100644 --- a/compiler/rustc_apfloat/src/lib.rs +++ b/compiler/rustc_apfloat/src/lib.rs @@ -33,8 +33,9 @@ #![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")] #![no_std] #![forbid(unsafe_code)] +#![feature(iter_zip)] #![feature(nll)] -#![feature(or_patterns)] +#![cfg_attr(bootstrap, feature(or_patterns))] #[macro_use] extern crate alloc; diff --git a/compiler/rustc_arena/Cargo.toml b/compiler/rustc_arena/Cargo.toml index f2d039c82ab7f..5d4d47527d3da 100644 --- a/compiler/rustc_arena/Cargo.toml +++ b/compiler/rustc_arena/Cargo.toml @@ -5,4 +5,5 @@ version = "0.0.0" edition = "2018" [dependencies] +rustc_data_structures = { path = "../rustc_data_structures" } smallvec = { version = "1.6.1", features = ["union", "may_dangle"] } diff --git a/compiler/rustc_arena/src/lib.rs b/compiler/rustc_arena/src/lib.rs index 721cfdd4459e5..c3e4945c4464c 100644 --- a/compiler/rustc_arena/src/lib.rs +++ b/compiler/rustc_arena/src/lib.rs @@ -17,6 +17,7 @@ #![feature(min_specialization)] #![cfg_attr(test, feature(test))] +use rustc_data_structures::sync; use smallvec::SmallVec; use std::alloc::Layout; @@ -235,26 +236,6 @@ impl TypedArena { start_ptr } - /// Allocates a slice of objects that are copied into the `TypedArena`, returning a mutable - /// reference to it. Will panic if passed a zero-sized types. - /// - /// Panics: - /// - /// - Zero-sized types - /// - Zero-length slices - #[inline] - pub fn alloc_slice(&self, slice: &[T]) -> &mut [T] - where - T: Copy, - { - unsafe { - let len = slice.len(); - let start_ptr = self.alloc_raw_slice(len); - slice.as_ptr().copy_to_nonoverlapping(start_ptr, len); - slice::from_raw_parts_mut(start_ptr, len) - } - } - #[inline] pub fn alloc_from_iter>(&self, iter: I) -> &mut [T] { assert!(mem::size_of::() != 0); @@ -297,22 +278,6 @@ impl TypedArena { } } - /// Clears the arena. Deallocates all but the longest chunk which may be reused. - pub fn clear(&mut self) { - unsafe { - // Clear the last chunk, which is partially filled. - let mut chunks_borrow = self.chunks.borrow_mut(); - if let Some(mut last_chunk) = chunks_borrow.last_mut() { - self.clear_last_chunk(&mut last_chunk); - let len = chunks_borrow.len(); - // If `T` is ZST, code below has no effect. - for mut chunk in chunks_borrow.drain(..len - 1) { - chunk.destroy(chunk.entries); - } - } - } - } - // Drops the contents of the last chunk. The last chunk is partially empty, unlike all other // chunks. fn clear_last_chunk(&self, last_chunk: &mut TypedArenaChunk) { @@ -556,8 +521,19 @@ struct DropType { obj: *mut u8, } -unsafe fn drop_for_type(to_drop: *mut u8) { - std::ptr::drop_in_place(to_drop as *mut T) +// SAFETY: we require `T: Send` before type-erasing into `DropType`. +#[cfg(parallel_compiler)] +unsafe impl sync::Send for DropType {} + +impl DropType { + #[inline] + unsafe fn new(obj: *mut T) -> Self { + unsafe fn drop_for_type(to_drop: *mut u8) { + std::ptr::drop_in_place(to_drop as *mut T) + } + + DropType { drop_fn: drop_for_type::, obj: obj as *mut u8 } + } } impl Drop for DropType { @@ -585,21 +561,26 @@ pub struct DropArena { impl DropArena { #[inline] - pub unsafe fn alloc(&self, object: T) -> &mut T { + pub unsafe fn alloc(&self, object: T) -> &mut T + where + T: sync::Send, + { let mem = self.arena.alloc_raw(Layout::new::()) as *mut T; // Write into uninitialized memory. ptr::write(mem, object); let result = &mut *mem; // Record the destructor after doing the allocation as that may panic // and would cause `object`'s destructor to run twice if it was recorded before. - self.destructors - .borrow_mut() - .push(DropType { drop_fn: drop_for_type::, obj: result as *mut T as *mut u8 }); + self.destructors.borrow_mut().push(DropType::new(result)); result } #[inline] - pub unsafe fn alloc_from_iter>(&self, iter: I) -> &mut [T] { + pub unsafe fn alloc_from_iter(&self, iter: I) -> &mut [T] + where + T: sync::Send, + I: IntoIterator, + { let mut vec: SmallVec<[_; 8]> = iter.into_iter().collect(); if vec.is_empty() { return &mut []; @@ -620,8 +601,7 @@ impl DropArena { // Record the destructors after doing the allocation as that may panic // and would cause `object`'s destructor to run twice if it was recorded before. for i in 0..len { - destructors - .push(DropType { drop_fn: drop_for_type::, obj: start_ptr.add(i) as *mut u8 }); + destructors.push(DropType::new(start_ptr.add(i))); } slice::from_raw_parts_mut(start_ptr, len) diff --git a/compiler/rustc_arena/src/tests.rs b/compiler/rustc_arena/src/tests.rs index e8a1f2db1a16b..911e577c1edc7 100644 --- a/compiler/rustc_arena/src/tests.rs +++ b/compiler/rustc_arena/src/tests.rs @@ -11,6 +11,24 @@ struct Point { z: i32, } +impl TypedArena { + /// Clears the arena. Deallocates all but the longest chunk which may be reused. + fn clear(&mut self) { + unsafe { + // Clear the last chunk, which is partially filled. + let mut chunks_borrow = self.chunks.borrow_mut(); + if let Some(mut last_chunk) = chunks_borrow.last_mut() { + self.clear_last_chunk(&mut last_chunk); + let len = chunks_borrow.len(); + // If `T` is ZST, code below has no effect. + for mut chunk in chunks_borrow.drain(..len - 1) { + chunk.destroy(chunk.entries); + } + } + } + } +} + #[test] pub fn test_unused() { let arena: TypedArena = TypedArena::default(); diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs index de44a2031ab82..e7f19f06ebef5 100644 --- a/compiler/rustc_ast/src/ast.rs +++ b/compiler/rustc_ast/src/ast.rs @@ -100,6 +100,7 @@ pub struct Path { } impl PartialEq for Path { + #[inline] fn eq(&self, symbol: &Symbol) -> bool { self.segments.len() == 1 && { self.segments[0].ident.name == *symbol } } @@ -149,9 +150,17 @@ impl PathSegment { pub fn from_ident(ident: Ident) -> Self { PathSegment { ident, id: DUMMY_NODE_ID, args: None } } + pub fn path_root(span: Span) -> Self { PathSegment::from_ident(Ident::new(kw::PathRoot, span)) } + + pub fn span(&self) -> Span { + match &self.args { + Some(args) => self.ident.span.to(args.span()), + None => self.ident.span, + } + } } /// The arguments of a path segment. @@ -647,7 +656,7 @@ impl Pat { /// are treated the same as `x: x, y: ref y, z: ref mut z`, /// except when `is_shorthand` is true. #[derive(Clone, Encodable, Decodable, Debug)] -pub struct FieldPat { +pub struct PatField { /// The identifier for the field. pub ident: Ident, /// The pattern the field is destructured to. @@ -692,7 +701,7 @@ pub enum PatKind { /// A struct or struct variant pattern (e.g., `Variant {x, y, ..}`). /// The `bool` is `true` in the presence of a `..`. - Struct(Path, Vec, /* recovered */ bool), + Struct(Path, Vec, /* recovered */ bool), /// A tuple struct/variant pattern (`Variant(x, y, .., z)`). TupleStruct(Path, Vec>), @@ -754,14 +763,6 @@ pub enum Mutability { } impl Mutability { - /// Returns `MutMutable` only if both `self` and `other` are mutable. - pub fn and(self, other: Self) -> Self { - match self { - Mutability::Mut => other, - Mutability::Not => Mutability::Not, - } - } - pub fn invert(self) -> Self { match self { Mutability::Mut => Mutability::Not, @@ -915,16 +916,6 @@ impl Stmt { } } - pub fn tokens_mut(&mut self) -> Option<&mut LazyTokenStream> { - match self.kind { - StmtKind::Local(ref mut local) => local.tokens.as_mut(), - StmtKind::Item(ref mut item) => item.tokens.as_mut(), - StmtKind::Expr(ref mut expr) | StmtKind::Semi(ref mut expr) => expr.tokens.as_mut(), - StmtKind::Empty => None, - StmtKind::MacCall(ref mut mac) => mac.tokens.as_mut(), - } - } - pub fn has_trailing_semicolon(&self) -> bool { match &self.kind { StmtKind::Semi(_) => true, @@ -1037,9 +1028,9 @@ pub struct Arm { pub is_placeholder: bool, } -/// Access of a named (e.g., `obj.foo`) or unnamed (e.g., `obj.0`) struct field. +/// A single field in a struct expression, e.g. `x: value` and `y` in `Foo { x: value, y }`. #[derive(Clone, Encodable, Decodable, Debug)] -pub struct Field { +pub struct ExprField { pub attrs: AttrVec, pub id: NodeId, pub span: Span, @@ -1083,8 +1074,8 @@ pub struct Expr { } // `Expr` is used a lot. Make sure it doesn't unintentionally get bigger. -#[cfg(target_arch = "x86_64")] -rustc_data_structures::static_assert_size!(Expr, 120); +#[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))] +rustc_data_structures::static_assert_size!(Expr, 104); impl Expr { /// Returns `true` if this expression would be valid somewhere that expects a value; @@ -1139,6 +1130,14 @@ impl Expr { } } + pub fn peel_parens(&self) -> &Expr { + let mut expr = self; + while let ExprKind::Paren(inner) = &expr.kind { + expr = &inner; + } + expr + } + /// Attempts to reparse as `Ty` (for diagnostic purposes). pub fn to_ty(&self) -> Option> { let kind = match &self.kind { @@ -1246,6 +1245,13 @@ pub enum StructRest { None, } +#[derive(Clone, Encodable, Decodable, Debug)] +pub struct StructExpr { + pub path: Path, + pub fields: Vec, + pub rest: StructRest, +} + #[derive(Clone, Encodable, Decodable, Debug)] pub enum ExprKind { /// A `box x` expression. @@ -1340,7 +1346,7 @@ pub enum ExprKind { Field(P, Ident), /// An indexing operation (e.g., `foo[2]`). Index(P, P), - /// A range (e.g., `1..2`, `1..`, `..2`, `1..=2`, `..=2`; and `..` in destructuring assingment). + /// A range (e.g., `1..2`, `1..`, `..2`, `1..=2`, `..=2`; and `..` in destructuring assignment). Range(Option>, Option>, RangeLimits), /// An underscore, used in destructuring assignment to ignore a value. Underscore, @@ -1371,7 +1377,7 @@ pub enum ExprKind { /// A struct literal expression. /// /// E.g., `Foo {x: 1, y: 2}`, or `Foo {x: 1, .. rest}`. - Struct(Path, Vec, StructRest), + Struct(P), /// An array literal constructed from one repeated element. /// @@ -1709,13 +1715,6 @@ impl FloatTy { FloatTy::F64 => sym::f64, } } - - pub fn bit_width(self) -> u64 { - match self { - FloatTy::F32 => 32, - FloatTy::F64 => 64, - } - } } #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)] @@ -1751,29 +1750,6 @@ impl IntTy { IntTy::I128 => sym::i128, } } - - pub fn bit_width(&self) -> Option { - Some(match *self { - IntTy::Isize => return None, - IntTy::I8 => 8, - IntTy::I16 => 16, - IntTy::I32 => 32, - IntTy::I64 => 64, - IntTy::I128 => 128, - }) - } - - pub fn normalize(&self, target_width: u32) -> Self { - match self { - IntTy::Isize => match target_width { - 16 => IntTy::I16, - 32 => IntTy::I32, - 64 => IntTy::I64, - _ => unreachable!(), - }, - _ => *self, - } - } } #[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Copy, Debug)] @@ -1809,29 +1785,6 @@ impl UintTy { UintTy::U128 => sym::u128, } } - - pub fn bit_width(&self) -> Option { - Some(match *self { - UintTy::Usize => return None, - UintTy::U8 => 8, - UintTy::U16 => 16, - UintTy::U32 => 32, - UintTy::U64 => 64, - UintTy::U128 => 128, - }) - } - - pub fn normalize(&self, target_width: u32) -> Self { - match self { - UintTy::Usize => match target_width { - 16 => UintTy::U16, - 32 => UintTy::U32, - 64 => UintTy::U64, - _ => unreachable!(), - }, - _ => *self, - } - } } /// A constraint on an associated type (e.g., `A = Bar` in `Foo` or @@ -1951,7 +1904,7 @@ impl TyKind { } /// Syntax used to declare a trait object. -#[derive(Clone, Copy, PartialEq, Encodable, Decodable, Debug)] +#[derive(Clone, Copy, PartialEq, Encodable, Decodable, Debug, HashStable_Generic)] pub enum TraitObjectSyntax { Dyn, None, @@ -2046,7 +1999,7 @@ pub enum InlineAsmOperand { out_expr: Option>, }, Const { - expr: P, + anon_const: AnonConst, }, Sym { expr: P, @@ -2202,9 +2155,6 @@ pub struct FnDecl { } impl FnDecl { - pub fn get_self(&self) -> Option { - self.inputs.get(0).and_then(Param::to_self) - } pub fn has_self(&self) -> bool { self.inputs.get(0).map_or(false, Param::is_self) } @@ -2299,7 +2249,7 @@ impl FnRetTy { } } -#[derive(Clone, PartialEq, Encodable, Decodable, Debug)] +#[derive(Clone, Copy, PartialEq, Encodable, Decodable, Debug)] pub enum Inline { Yes, No, @@ -2521,11 +2471,11 @@ impl VisibilityKind { } } -/// Field of a struct. +/// Field definition in a struct, variant or union. /// /// E.g., `bar: usize` as in `struct Foo { bar: usize }`. #[derive(Clone, Encodable, Decodable, Debug)] -pub struct StructField { +pub struct FieldDef { pub attrs: Vec, pub id: NodeId, pub span: Span, @@ -2542,11 +2492,11 @@ pub enum VariantData { /// Struct variant. /// /// E.g., `Bar { .. }` as in `enum Foo { Bar { .. } }`. - Struct(Vec, bool), + Struct(Vec, bool), /// Tuple variant. /// /// E.g., `Bar(..)` as in `enum Foo { Bar(..) }`. - Tuple(Vec, NodeId), + Tuple(Vec, NodeId), /// Unit variant. /// /// E.g., `Bar = ..` as in `enum Foo { Bar = .. }`. @@ -2555,7 +2505,7 @@ pub enum VariantData { impl VariantData { /// Return the fields of this variant. - pub fn fields(&self) -> &[StructField] { + pub fn fields(&self) -> &[FieldDef] { match *self { VariantData::Struct(ref fields, ..) | VariantData::Tuple(ref fields, _) => fields, _ => &[], @@ -2757,7 +2707,7 @@ pub enum ItemKind { MacroDef(MacroDef), } -#[cfg(target_arch = "x86_64")] +#[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))] rustc_data_structures::static_assert_size!(ItemKind, 112); impl ItemKind { @@ -2831,7 +2781,7 @@ pub enum AssocItemKind { MacCall(MacCall), } -#[cfg(target_arch = "x86_64")] +#[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))] rustc_data_structures::static_assert_size!(AssocItemKind, 72); impl AssocItemKind { @@ -2883,7 +2833,7 @@ pub enum ForeignItemKind { MacCall(MacCall), } -#[cfg(target_arch = "x86_64")] +#[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))] rustc_data_structures::static_assert_size!(ForeignItemKind, 72); impl From for ItemKind { diff --git a/compiler/rustc_ast/src/ast_like.rs b/compiler/rustc_ast/src/ast_like.rs index 6649cda69a0b0..945a44ab66371 100644 --- a/compiler/rustc_ast/src/ast_like.rs +++ b/compiler/rustc_ast/src/ast_like.rs @@ -1,34 +1,89 @@ use super::ptr::P; +use super::token::Nonterminal; use super::tokenstream::LazyTokenStream; -use super::{Arm, Field, FieldPat, GenericParam, Param, StructField, Variant}; -use super::{AssocItem, Expr, ForeignItem, Item, Local}; +use super::{Arm, ExprField, FieldDef, GenericParam, Param, PatField, Variant}; +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; + /// 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 { +pub trait AstLike: Sized + 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 + /// + /// If this is `false`, then this `AstLike` definitely does + /// not support 'custom' inner attributes, which enables some optimizations + /// during token collection. + const SUPPORTS_CUSTOM_INNER_ATTRS: bool; fn attrs(&self) -> &[Attribute]; fn visit_attrs(&mut self, f: impl FnOnce(&mut Vec)); - /// Called by `Parser::collect_tokens` to store the collected - /// tokens inside an AST node - fn finalize_tokens(&mut self, _tokens: LazyTokenStream) { - // This default impl makes this trait easier to implement - // in tools like `rust-analyzer` - panic!("`finalize_tokens` is not supported!") - } + fn tokens_mut(&mut self) -> Option<&mut Option>; } impl AstLike for P { + const SUPPORTS_CUSTOM_INNER_ATTRS: bool = T::SUPPORTS_CUSTOM_INNER_ATTRS; fn attrs(&self) -> &[Attribute] { (**self).attrs() } fn visit_attrs(&mut self, f: impl FnOnce(&mut Vec)) { (**self).visit_attrs(f); } - fn finalize_tokens(&mut self, tokens: LazyTokenStream) { - (**self).finalize_tokens(tokens) + fn tokens_mut(&mut self) -> Option<&mut Option> { + (**self).tokens_mut() + } +} + +impl AstLike for crate::token::Nonterminal { + const SUPPORTS_CUSTOM_INNER_ATTRS: bool = true; + fn attrs(&self) -> &[Attribute] { + match self { + Nonterminal::NtItem(item) => item.attrs(), + Nonterminal::NtStmt(stmt) => stmt.attrs(), + Nonterminal::NtExpr(expr) | Nonterminal::NtLiteral(expr) => expr.attrs(), + Nonterminal::NtPat(_) + | Nonterminal::NtTy(_) + | Nonterminal::NtMeta(_) + | Nonterminal::NtPath(_) + | Nonterminal::NtVis(_) + | Nonterminal::NtTT(_) + | Nonterminal::NtBlock(_) + | Nonterminal::NtIdent(..) + | Nonterminal::NtLifetime(_) => &[], + } + } + fn visit_attrs(&mut self, f: impl FnOnce(&mut Vec)) { + match self { + Nonterminal::NtItem(item) => item.visit_attrs(f), + Nonterminal::NtStmt(stmt) => stmt.visit_attrs(f), + Nonterminal::NtExpr(expr) | Nonterminal::NtLiteral(expr) => expr.visit_attrs(f), + Nonterminal::NtPat(_) + | Nonterminal::NtTy(_) + | Nonterminal::NtMeta(_) + | Nonterminal::NtPath(_) + | Nonterminal::NtVis(_) + | Nonterminal::NtTT(_) + | Nonterminal::NtBlock(_) + | Nonterminal::NtIdent(..) + | Nonterminal::NtLifetime(_) => {} + } + } + fn tokens_mut(&mut self) -> Option<&mut Option> { + match self { + Nonterminal::NtItem(item) => item.tokens_mut(), + Nonterminal::NtStmt(stmt) => stmt.tokens_mut(), + Nonterminal::NtExpr(expr) | Nonterminal::NtLiteral(expr) => expr.tokens_mut(), + Nonterminal::NtPat(pat) => pat.tokens_mut(), + Nonterminal::NtTy(ty) => ty.tokens_mut(), + Nonterminal::NtMeta(attr_item) => attr_item.tokens_mut(), + Nonterminal::NtPath(path) => path.tokens_mut(), + Nonterminal::NtVis(vis) => vis.tokens_mut(), + _ => panic!("Called tokens_mut on {:?}", self), + } } } @@ -41,13 +96,17 @@ fn visit_attrvec(attrs: &mut AttrVec, f: impl FnOnce(&mut Vec)) { } impl AstLike for StmtKind { + // This might be an `StmtKind::Item`, which contains + // an item that supports inner attrs + const SUPPORTS_CUSTOM_INNER_ATTRS: bool = true; + fn attrs(&self) -> &[Attribute] { - match *self { - StmtKind::Local(ref local) => local.attrs(), - StmtKind::Expr(ref expr) | StmtKind::Semi(ref expr) => expr.attrs(), - StmtKind::Item(ref item) => item.attrs(), + match self { + StmtKind::Local(local) => local.attrs(), + StmtKind::Expr(expr) | StmtKind::Semi(expr) => expr.attrs(), + StmtKind::Item(item) => item.attrs(), StmtKind::Empty => &[], - StmtKind::MacCall(ref mac) => &*mac.attrs, + StmtKind::MacCall(mac) => &mac.attrs, } } @@ -60,21 +119,20 @@ impl AstLike for StmtKind { StmtKind::MacCall(mac) => visit_attrvec(&mut mac.attrs, f), } } - fn finalize_tokens(&mut self, tokens: LazyTokenStream) { - let stmt_tokens = match self { - StmtKind::Local(ref mut local) => &mut local.tokens, - StmtKind::Item(ref mut item) => &mut item.tokens, - StmtKind::Expr(ref mut expr) | StmtKind::Semi(ref mut expr) => &mut expr.tokens, - StmtKind::Empty => return, - StmtKind::MacCall(ref mut mac) => &mut mac.tokens, - }; - if stmt_tokens.is_none() { - *stmt_tokens = Some(tokens); - } + fn tokens_mut(&mut self) -> Option<&mut Option> { + Some(match self { + StmtKind::Local(local) => &mut local.tokens, + StmtKind::Item(item) => &mut item.tokens, + StmtKind::Expr(expr) | StmtKind::Semi(expr) => &mut expr.tokens, + StmtKind::Empty => return None, + StmtKind::MacCall(mac) => &mut mac.tokens, + }) } } impl AstLike for Stmt { + const SUPPORTS_CUSTOM_INNER_ATTRS: bool = StmtKind::SUPPORTS_CUSTOM_INNER_ATTRS; + fn attrs(&self) -> &[Attribute] { self.kind.attrs() } @@ -82,31 +140,31 @@ impl AstLike for Stmt { fn visit_attrs(&mut self, f: impl FnOnce(&mut Vec)) { self.kind.visit_attrs(f); } - fn finalize_tokens(&mut self, tokens: LazyTokenStream) { - self.kind.finalize_tokens(tokens) + fn tokens_mut(&mut self) -> Option<&mut Option> { + self.kind.tokens_mut() } } impl AstLike for Attribute { + const SUPPORTS_CUSTOM_INNER_ATTRS: bool = false; + fn attrs(&self) -> &[Attribute] { &[] } fn visit_attrs(&mut self, _f: impl FnOnce(&mut Vec)) {} - fn finalize_tokens(&mut self, tokens: LazyTokenStream) { - match &mut self.kind { - AttrKind::Normal(_, attr_tokens) => { - if attr_tokens.is_none() { - *attr_tokens = Some(tokens); - } - } - AttrKind::DocComment(..) => { - panic!("Called finalize_tokens on doc comment attr {:?}", self) + fn tokens_mut(&mut self) -> Option<&mut Option> { + Some(match &mut self.kind { + AttrKind::Normal(_, tokens) => tokens, + kind @ AttrKind::DocComment(..) => { + panic!("Called tokens_mut on doc comment attr {:?}", kind) } - } + }) } } impl AstLike for Option { + const SUPPORTS_CUSTOM_INNER_ATTRS: bool = T::SUPPORTS_CUSTOM_INNER_ATTRS; + fn attrs(&self) -> &[Attribute] { self.as_ref().map(|inner| inner.attrs()).unwrap_or(&[]) } @@ -115,10 +173,8 @@ impl AstLike for Option { inner.visit_attrs(f); } } - fn finalize_tokens(&mut self, tokens: LazyTokenStream) { - if let Some(inner) = self { - inner.finalize_tokens(tokens); - } + fn tokens_mut(&mut self) -> Option<&mut Option> { + self.as_mut().and_then(|inner| inner.tokens_mut()) } } @@ -142,8 +198,13 @@ impl VecOrAttrVec for AttrVec { } macro_rules! derive_has_tokens_and_attrs { - ($($ty:path),*) => { $( + ( + const SUPPORTS_CUSTOM_INNER_ATTRS: bool = $inner_attrs:literal; + $($ty:path),* + ) => { $( impl AstLike for $ty { + const SUPPORTS_CUSTOM_INNER_ATTRS: bool = $inner_attrs; + fn attrs(&self) -> &[Attribute] { &self.attrs } @@ -152,12 +213,10 @@ macro_rules! derive_has_tokens_and_attrs { VecOrAttrVec::visit(&mut self.attrs, f) } - fn finalize_tokens(&mut self, tokens: LazyTokenStream) { - if self.tokens.is_none() { - self.tokens = Some(tokens); - } - + fn tokens_mut(&mut self) -> Option<&mut Option> { + Some(&mut self.tokens) } + } )* } } @@ -165,6 +224,8 @@ macro_rules! derive_has_tokens_and_attrs { macro_rules! derive_has_attrs_no_tokens { ($($ty:path),*) => { $( impl AstLike for $ty { + const SUPPORTS_CUSTOM_INNER_ATTRS: bool = false; + fn attrs(&self) -> &[Attribute] { &self.attrs } @@ -173,7 +234,9 @@ macro_rules! derive_has_attrs_no_tokens { VecOrAttrVec::visit(&mut self.attrs, f) } - fn finalize_tokens(&mut self, _tokens: LazyTokenStream) {} + fn tokens_mut(&mut self) -> Option<&mut Option> { + None + } } )* } } @@ -181,34 +244,38 @@ macro_rules! derive_has_attrs_no_tokens { macro_rules! derive_has_tokens_no_attrs { ($($ty:path),*) => { $( impl AstLike for $ty { + const SUPPORTS_CUSTOM_INNER_ATTRS: bool = false; + fn attrs(&self) -> &[Attribute] { &[] } - fn visit_attrs(&mut self, _f: impl FnOnce(&mut Vec)) { - } - - fn finalize_tokens(&mut self, tokens: LazyTokenStream) { - if self.tokens.is_none() { - self.tokens = Some(tokens); - } - + fn visit_attrs(&mut self, _f: impl FnOnce(&mut Vec)) {} + fn tokens_mut(&mut self) -> Option<&mut Option> { + Some(&mut self.tokens) } } )* } } -// These AST nodes support both inert and active -// attributes, so they also have tokens. +// These ast nodes support both active and inert attributes, +// so they have tokens collected to pass to proc macros +derive_has_tokens_and_attrs! { + // Both `Item` and `AssocItem` can have bodies, which + // can contain inner attributes + const SUPPORTS_CUSTOM_INNER_ATTRS: bool = true; + Item, AssocItem, ForeignItem +} + derive_has_tokens_and_attrs! { - Item, Expr, Local, AssocItem, ForeignItem + const SUPPORTS_CUSTOM_INNER_ATTRS: bool = false; + Local, MacCallStmt, Expr } // These ast nodes only support inert attributes, so they don't // store tokens (since nothing can observe them) derive_has_attrs_no_tokens! { - StructField, Arm, - Field, FieldPat, Variant, Param, GenericParam + FieldDef, Arm, ExprField, PatField, Variant, Param, GenericParam } // These AST nodes don't support attributes, but can diff --git a/compiler/rustc_ast/src/attr/mod.rs b/compiler/rustc_ast/src/attr/mod.rs index 1e224dbf83390..41121d095f3ed 100644 --- a/compiler/rustc_ast/src/attr/mod.rs +++ b/compiler/rustc_ast/src/attr/mod.rs @@ -6,7 +6,9 @@ use crate::ast::{Lit, LitKind}; use crate::ast::{MacArgs, MacDelimiter, MetaItem, MetaItemKind, NestedMetaItem}; use crate::ast::{Path, PathSegment}; use crate::token::{self, CommentKind, Token}; -use crate::tokenstream::{DelimSpan, LazyTokenStream, TokenStream, TokenTree, TreeAndSpacing}; +use crate::tokenstream::{AttrAnnotatedTokenStream, AttrAnnotatedTokenTree}; +use crate::tokenstream::{DelimSpan, Spacing, TokenTree, TreeAndSpacing}; +use crate::tokenstream::{LazyTokenStream, TokenStream}; use rustc_index::bit_set::GrowableBitSet; use rustc_span::source_map::BytePos; @@ -33,10 +35,6 @@ impl MarkedAttrs { } } -pub fn is_known_lint_tool(m_item: Ident) -> bool { - [sym::clippy, sym::rustc, sym::rustdoc].contains(&m_item.name) -} - impl NestedMetaItem { /// Returns the `MetaItem` if `self` is a `NestedMetaItem::MetaItem`. pub fn meta_item(&self) -> Option<&MetaItem> { @@ -104,22 +102,14 @@ impl NestedMetaItem { self.meta_item().map_or(false, |meta_item| meta_item.is_word()) } - /// Returns `true` if `self` is a `MetaItem` and the meta item is a `ValueString`. - pub fn is_value_str(&self) -> bool { - self.value_str().is_some() - } - - /// Returns `true` if `self` is a `MetaItem` and the meta item is a list. - pub fn is_meta_item_list(&self) -> bool { - self.meta_item_list().is_some() - } - + /// See [`MetaItem::name_value_literal_span`]. pub fn name_value_literal_span(&self) -> Option { self.meta_item()?.name_value_literal_span() } } impl Attribute { + #[inline] pub fn has_name(&self, name: Symbol) -> bool { match self.kind { AttrKind::Normal(ref item, _) => item.path == name, @@ -168,31 +158,6 @@ impl Attribute { false } } - - pub fn is_meta_item_list(&self) -> bool { - self.meta_item_list().is_some() - } - - /// Indicates if the attribute is a `ValueString`. - pub fn is_value_str(&self) -> bool { - self.value_str().is_some() - } - - /// This is used in case you want the value span instead of the whole attribute. Example: - /// - /// ```text - /// #[doc(alias = "foo")] - /// ``` - /// - /// In here, it'll return a span for `"foo"`. - pub fn name_value_literal_span(&self) -> Option { - match self.kind { - AttrKind::Normal(ref item, _) => { - item.meta(self.span).and_then(|meta| meta.name_value_literal_span()) - } - AttrKind::DocComment(..) => None, - } - } } impl MetaItem { @@ -239,10 +204,6 @@ impl MetaItem { self.path == name } - pub fn is_value_str(&self) -> bool { - self.value_str().is_some() - } - /// This is used in case you want the value span instead of the whole attribute. Example: /// /// ```text @@ -309,14 +270,18 @@ impl Attribute { } } - pub fn tokens(&self) -> TokenStream { + pub fn tokens(&self) -> AttrAnnotatedTokenStream { match self.kind { AttrKind::Normal(_, ref tokens) => tokens .as_ref() .unwrap_or_else(|| panic!("attribute is missing tokens: {:?}", self)) .create_token_stream(), - AttrKind::DocComment(comment_kind, data) => TokenStream::from(TokenTree::Token( - Token::new(token::DocComment(comment_kind, self.style, data), self.span), + AttrKind::DocComment(comment_kind, data) => AttrAnnotatedTokenStream::from(( + AttrAnnotatedTokenTree::Token(Token::new( + token::DocComment(comment_kind, self.style, data), + self.span, + )), + Spacing::Alone, )), } } diff --git a/compiler/rustc_ast/src/expand/mod.rs b/compiler/rustc_ast/src/expand/mod.rs index eebfc38bdf4ec..2ee1bfe0ae71b 100644 --- a/compiler/rustc_ast/src/expand/mod.rs +++ b/compiler/rustc_ast/src/expand/mod.rs @@ -1,3 +1,3 @@ -//! Definitions shared by macros / syntax extensions and e.g. librustc_middle. +//! Definitions shared by macros / syntax extensions and e.g. `rustc_middle`. pub mod allocator; diff --git a/compiler/rustc_ast/src/lib.rs b/compiler/rustc_ast/src/lib.rs index 4eaef85043ccd..1e6da044ec039 100644 --- a/compiler/rustc_ast/src/lib.rs +++ b/compiler/rustc_ast/src/lib.rs @@ -14,9 +14,10 @@ #![feature(const_fn_transmute)] #![feature(const_panic)] #![feature(crate_visibility_modifier)] +#![feature(iter_zip)] #![feature(label_break_value)] #![feature(nll)] -#![feature(or_patterns)] +#![cfg_attr(bootstrap, feature(or_patterns))] #![recursion_limit = "256"] #[macro_use] @@ -42,7 +43,6 @@ pub mod util { pub mod ast; pub mod ast_like; pub mod attr; -pub mod crate_disambiguator; pub mod entry; pub mod expand; pub mod mut_visit; @@ -59,7 +59,7 @@ use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; /// Requirements for a `StableHashingContext` to be used in this crate. /// This is a hack to allow using the `HashStable_Generic` derive macro -/// instead of implementing everything in librustc_middle. +/// instead of implementing everything in `rustc_middle`. pub trait HashStableContext: rustc_span::HashStableContext { fn hash_attr(&mut self, _: &ast::Attribute, hasher: &mut StableHasher); } diff --git a/compiler/rustc_ast/src/mut_visit.rs b/compiler/rustc_ast/src/mut_visit.rs index c286738811ca1..05f57f978c7a4 100644 --- a/compiler/rustc_ast/src/mut_visit.rs +++ b/compiler/rustc_ast/src/mut_visit.rs @@ -102,8 +102,8 @@ pub trait MutVisitor: Sized { noop_visit_fn_header(header, self); } - fn flat_map_struct_field(&mut self, sf: StructField) -> SmallVec<[StructField; 1]> { - noop_flat_map_struct_field(sf, self) + fn flat_map_field_def(&mut self, fd: FieldDef) -> SmallVec<[FieldDef; 1]> { + noop_flat_map_field_def(fd, self) } fn visit_item_kind(&mut self, i: &mut ItemKind) { @@ -254,8 +254,8 @@ pub trait MutVisitor: Sized { noop_visit_mt(mt, self); } - fn flat_map_field(&mut self, f: Field) -> SmallVec<[Field; 1]> { - noop_flat_map_field(f, self) + fn flat_map_expr_field(&mut self, f: ExprField) -> SmallVec<[ExprField; 1]> { + noop_flat_map_expr_field(f, self) } fn visit_where_clause(&mut self, where_clause: &mut WhereClause) { @@ -278,8 +278,8 @@ pub trait MutVisitor: Sized { // Do nothing. } - fn flat_map_field_pattern(&mut self, fp: FieldPat) -> SmallVec<[FieldPat; 1]> { - noop_flat_map_field_pattern(fp, self) + fn flat_map_pat_field(&mut self, fp: PatField) -> SmallVec<[PatField; 1]> { + noop_flat_map_pat_field(fp, self) } } @@ -385,11 +385,11 @@ pub fn visit_delim_span(dspan: &mut DelimSpan, vis: &mut T) { vis.visit_span(&mut dspan.close); } -pub fn noop_flat_map_field_pattern( - mut fp: FieldPat, +pub fn noop_flat_map_pat_field( + mut fp: PatField, vis: &mut T, -) -> SmallVec<[FieldPat; 1]> { - let FieldPat { attrs, id, ident, is_placeholder: _, is_shorthand: _, pat, span } = &mut fp; +) -> SmallVec<[PatField; 1]> { + let PatField { attrs, id, ident, is_placeholder: _, is_shorthand: _, pat, span } = &mut fp; vis.visit_id(id); vis.visit_ident(ident); vis.visit_pat(pat); @@ -630,6 +630,33 @@ pub fn noop_flat_map_param(mut param: Param, vis: &mut T) -> Smal smallvec![param] } +// No `noop_` prefix because there isn't a corresponding method in `MutVisitor`. +pub fn visit_attr_annotated_tt(tt: &mut AttrAnnotatedTokenTree, vis: &mut T) { + match tt { + AttrAnnotatedTokenTree::Token(token) => { + visit_token(token, vis); + } + AttrAnnotatedTokenTree::Delimited(DelimSpan { open, close }, _delim, tts) => { + vis.visit_span(open); + vis.visit_span(close); + visit_attr_annotated_tts(tts, vis); + } + AttrAnnotatedTokenTree::Attributes(data) => { + for attr in &mut *data.attrs { + match &mut attr.kind { + AttrKind::Normal(_, attr_tokens) => { + visit_lazy_tts(attr_tokens, vis); + } + AttrKind::DocComment(..) => { + vis.visit_span(&mut attr.span); + } + } + } + visit_lazy_tts_opt_mut(Some(&mut data.tokens), vis); + } + } +} + // No `noop_` prefix because there isn't a corresponding method in `MutVisitor`. pub fn visit_tt(tt: &mut TokenTree, vis: &mut T) { match tt { @@ -652,16 +679,30 @@ pub fn visit_tts(TokenStream(tts): &mut TokenStream, vis: &mut T) } } -pub fn visit_lazy_tts(lazy_tts: &mut Option, vis: &mut T) { +pub fn visit_attr_annotated_tts( + AttrAnnotatedTokenStream(tts): &mut AttrAnnotatedTokenStream, + vis: &mut T, +) { + if vis.token_visiting_enabled() && !tts.is_empty() { + let tts = Lrc::make_mut(tts); + visit_vec(tts, |(tree, _is_joint)| visit_attr_annotated_tt(tree, vis)); + } +} + +pub fn visit_lazy_tts_opt_mut(lazy_tts: Option<&mut LazyTokenStream>, vis: &mut T) { if vis.token_visiting_enabled() { - visit_opt(lazy_tts, |lazy_tts| { + if let Some(lazy_tts) = lazy_tts { let mut tts = lazy_tts.create_token_stream(); - visit_tts(&mut tts, vis); + visit_attr_annotated_tts(&mut tts, vis); *lazy_tts = LazyTokenStream::new(tts); - }) + } } } +pub fn visit_lazy_tts(lazy_tts: &mut Option, vis: &mut T) { + visit_lazy_tts_opt_mut(lazy_tts.as_mut(), vis); +} + // No `noop_` prefix because there isn't a corresponding method in `MutVisitor`. // Applies ident visitor if it's an ident; applies other visits to interpolated nodes. // In practice the ident part is not actually used by specific visitors right now, @@ -842,10 +883,10 @@ pub fn noop_visit_where_predicate(pred: &mut WherePredicate, vis: pub fn noop_visit_variant_data(vdata: &mut VariantData, vis: &mut T) { match vdata { VariantData::Struct(fields, ..) => { - fields.flat_map_in_place(|field| vis.flat_map_struct_field(field)); + fields.flat_map_in_place(|field| vis.flat_map_field_def(field)); } VariantData::Tuple(fields, id) => { - fields.flat_map_in_place(|field| vis.flat_map_struct_field(field)); + fields.flat_map_in_place(|field| vis.flat_map_field_def(field)); vis.visit_id(id); } VariantData::Unit(id) => vis.visit_id(id), @@ -864,22 +905,25 @@ pub fn noop_visit_poly_trait_ref(p: &mut PolyTraitRef, vis: &mut vis.visit_span(span); } -pub fn noop_flat_map_struct_field( - mut sf: StructField, +pub fn noop_flat_map_field_def( + mut fd: FieldDef, visitor: &mut T, -) -> SmallVec<[StructField; 1]> { - let StructField { span, ident, vis, id, ty, attrs, is_placeholder: _ } = &mut sf; +) -> SmallVec<[FieldDef; 1]> { + let FieldDef { span, ident, vis, id, ty, attrs, is_placeholder: _ } = &mut fd; visitor.visit_span(span); visit_opt(ident, |ident| visitor.visit_ident(ident)); visitor.visit_vis(vis); visitor.visit_id(id); visitor.visit_ty(ty); visit_attrs(attrs, visitor); - smallvec![sf] + smallvec![fd] } -pub fn noop_flat_map_field(mut f: Field, vis: &mut T) -> SmallVec<[Field; 1]> { - let Field { ident, expr, span, is_shorthand: _, attrs, id, is_placeholder: _ } = &mut f; +pub fn noop_flat_map_expr_field( + mut f: ExprField, + vis: &mut T, +) -> SmallVec<[ExprField; 1]> { + let ExprField { ident, expr, span, is_shorthand: _, attrs, id, is_placeholder: _ } = &mut f; vis.visit_ident(ident); vis.visit_expr(expr); vis.visit_id(id); @@ -1102,7 +1146,7 @@ pub fn noop_visit_pat(pat: &mut P, vis: &mut T) { } PatKind::Struct(path, fields, _etc) => { vis.visit_path(path); - fields.flat_map_in_place(|field| vis.flat_map_field_pattern(field)); + fields.flat_map_in_place(|field| vis.flat_map_pat_field(field)); } PatKind::Box(inner) => vis.visit_pat(inner), PatKind::Ref(inner, _mutbl) => vis.visit_pat(inner), @@ -1249,7 +1293,6 @@ pub fn noop_visit_expr( match op { InlineAsmOperand::In { expr, .. } | InlineAsmOperand::InOut { expr, .. } - | InlineAsmOperand::Const { expr, .. } | InlineAsmOperand::Sym { expr, .. } => vis.visit_expr(expr), InlineAsmOperand::Out { expr, .. } => { if let Some(expr) = expr { @@ -1262,6 +1305,7 @@ pub fn noop_visit_expr( vis.visit_expr(out_expr); } } + InlineAsmOperand::Const { anon_const, .. } => vis.visit_anon_const(anon_const), } } } @@ -1283,10 +1327,11 @@ pub fn noop_visit_expr( visit_vec(inputs, |(_c, expr)| vis.visit_expr(expr)); } ExprKind::MacCall(mac) => vis.visit_mac_call(mac), - ExprKind::Struct(path, fields, expr) => { + ExprKind::Struct(se) => { + let StructExpr { path, fields, rest } = se.deref_mut(); vis.visit_path(path); - fields.flat_map_in_place(|field| vis.flat_map_field(field)); - match expr { + fields.flat_map_in_place(|field| vis.flat_map_expr_field(field)); + match rest { StructRest::Base(expr) => vis.visit_expr(expr), StructRest::Rest(_span) => {} StructRest::None => {} diff --git a/compiler/rustc_ast/src/token.rs b/compiler/rustc_ast/src/token.rs index 90bfb01d6c791..10d48a55bb54e 100644 --- a/compiler/rustc_ast/src/token.rs +++ b/compiler/rustc_ast/src/token.rs @@ -11,11 +11,9 @@ use crate::tokenstream::TokenTree; use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; use rustc_data_structures::sync::Lrc; use rustc_macros::HashStable_Generic; -use rustc_span::hygiene::ExpnKind; -use rustc_span::source_map::SourceMap; use rustc_span::symbol::{kw, sym}; use rustc_span::symbol::{Ident, Symbol}; -use rustc_span::{self, edition::Edition, FileName, RealFileName, Span, DUMMY_SP}; +use rustc_span::{self, edition::Edition, Span, DUMMY_SP}; use std::borrow::Cow; use std::{fmt, mem}; @@ -244,7 +242,7 @@ pub enum TokenKind { } // `TokenKind` is used a lot. Make sure it doesn't unintentionally get bigger. -#[cfg(target_arch = "x86_64")] +#[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))] rustc_data_structures::static_assert_size!(TokenKind, 16); #[derive(Clone, PartialEq, Encodable, Decodable, Debug, HashStable_Generic)] @@ -682,7 +680,7 @@ pub enum Nonterminal { } // `Nonterminal` is used a lot. Make sure it doesn't unintentionally get bigger. -#[cfg(target_arch = "x86_64")] +#[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))] rustc_data_structures::static_assert_size!(Nonterminal, 48); #[derive(Debug, Copy, Clone, PartialEq, Encodable, Decodable)] @@ -690,13 +688,13 @@ pub enum NonterminalKind { Item, Block, Stmt, - Pat2018 { - /// Keep track of whether the user used `:pat2018` or `:pat` and we inferred it from the + Pat2015 { + /// Keep track of whether the user used `:pat2015` or `:pat` and we inferred it from the /// edition of the span. This is used for diagnostics. inferred: bool, }, Pat2021 { - /// Keep track of whether the user used `:pat2018` or `:pat` and we inferred it from the + /// Keep track of whether the user used `:pat2015` or `:pat` and we inferred it from the /// edition of the span. This is used for diagnostics. inferred: bool, }, @@ -724,11 +722,11 @@ impl NonterminalKind { sym::stmt => NonterminalKind::Stmt, sym::pat => match edition() { Edition::Edition2015 | Edition::Edition2018 => { - NonterminalKind::Pat2018 { inferred: true } + NonterminalKind::Pat2015 { inferred: true } } Edition::Edition2021 => NonterminalKind::Pat2021 { inferred: true }, }, - sym::pat2018 => NonterminalKind::Pat2018 { inferred: false }, + sym::pat2015 => NonterminalKind::Pat2015 { inferred: false }, sym::pat2021 => NonterminalKind::Pat2021 { inferred: false }, sym::expr => NonterminalKind::Expr, sym::ty => NonterminalKind::Ty, @@ -747,9 +745,9 @@ impl NonterminalKind { NonterminalKind::Item => sym::item, NonterminalKind::Block => sym::block, NonterminalKind::Stmt => sym::stmt, - NonterminalKind::Pat2018 { inferred: false } => sym::pat2018, + NonterminalKind::Pat2015 { inferred: false } => sym::pat2015, NonterminalKind::Pat2021 { inferred: false } => sym::pat2021, - NonterminalKind::Pat2018 { inferred: true } + NonterminalKind::Pat2015 { inferred: true } | NonterminalKind::Pat2021 { inferred: true } => sym::pat, NonterminalKind::Expr => sym::expr, NonterminalKind::Ty => sym::ty, @@ -786,79 +784,6 @@ impl Nonterminal { NtTT(tt) => tt.span(), } } - - /// This nonterminal looks like some specific enums from - /// `proc-macro-hack` and `procedural-masquerade` crates. - /// We need to maintain some special pretty-printing behavior for them due to incorrect - /// asserts in old versions of those crates and their wide use in the ecosystem. - /// See issue #73345 for more details. - /// FIXME(#73933): Remove this eventually. - pub fn pretty_printing_compatibility_hack(&self) -> bool { - let item = match self { - NtItem(item) => item, - NtStmt(stmt) => match &stmt.kind { - ast::StmtKind::Item(item) => item, - _ => return false, - }, - _ => return false, - }; - - let name = item.ident.name; - if name == sym::ProceduralMasqueradeDummyType || name == sym::ProcMacroHack { - if let ast::ItemKind::Enum(enum_def, _) = &item.kind { - if let [variant] = &*enum_def.variants { - return variant.ident.name == sym::Input; - } - } - } - false - } - - // See issue #74616 for details - pub fn ident_name_compatibility_hack( - &self, - orig_span: Span, - source_map: &SourceMap, - ) -> Option<(Ident, bool)> { - if let NtIdent(ident, is_raw) = self { - if let ExpnKind::Macro(_, macro_name) = orig_span.ctxt().outer_expn_data().kind { - let filename = source_map.span_to_filename(orig_span); - if let FileName::Real(RealFileName::Named(path)) = filename { - let matches_prefix = |prefix, filename| { - // Check for a path that ends with 'prefix*/src/' - let mut iter = path.components().rev(); - iter.next().and_then(|p| p.as_os_str().to_str()) == Some(filename) - && iter.next().and_then(|p| p.as_os_str().to_str()) == Some("src") - && iter - .next() - .and_then(|p| p.as_os_str().to_str()) - .map_or(false, |p| p.starts_with(prefix)) - }; - - if (macro_name == sym::impl_macros - && matches_prefix("time-macros-impl", "lib.rs")) - || (macro_name == sym::arrays && matches_prefix("js-sys", "lib.rs")) - { - let snippet = source_map.span_to_snippet(orig_span); - if snippet.as_deref() == Ok("$name") { - return Some((*ident, *is_raw)); - } - } - - if macro_name == sym::tuple_from_req - && (matches_prefix("actix-web", "extract.rs") - || matches_prefix("actori-web", "extract.rs")) - { - let snippet = source_map.span_to_snippet(orig_span); - if snippet.as_deref() == Ok("$T") { - return Some((*ident, *is_raw)); - } - } - } - } - } - None - } } impl PartialEq for Nonterminal { diff --git a/compiler/rustc_ast/src/tokenstream.rs b/compiler/rustc_ast/src/tokenstream.rs index 9ac05f316f034..2d463a4588c56 100644 --- a/compiler/rustc_ast/src/tokenstream.rs +++ b/compiler/rustc_ast/src/tokenstream.rs @@ -14,6 +14,7 @@ //! ownership of the original. use crate::token::{self, DelimToken, Token, TokenKind}; +use crate::AttrVec; use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; use rustc_data_structures::sync::{self, Lrc}; @@ -89,10 +90,6 @@ impl TokenTree { } } - pub fn joint(self) -> TokenStream { - TokenStream::new(vec![(self, Spacing::Joint)]) - } - pub fn token(kind: TokenKind, span: Span) -> TokenTree { TokenTree::Token(Token::new(kind, span)) } @@ -127,11 +124,11 @@ where } pub trait CreateTokenStream: sync::Send + sync::Sync { - fn create_token_stream(&self) -> TokenStream; + fn create_token_stream(&self) -> AttrAnnotatedTokenStream; } -impl CreateTokenStream for TokenStream { - fn create_token_stream(&self) -> TokenStream { +impl CreateTokenStream for AttrAnnotatedTokenStream { + fn create_token_stream(&self) -> AttrAnnotatedTokenStream { self.clone() } } @@ -147,14 +144,14 @@ impl LazyTokenStream { LazyTokenStream(Lrc::new(Box::new(inner))) } - pub fn create_token_stream(&self) -> TokenStream { + pub fn create_token_stream(&self) -> AttrAnnotatedTokenStream { self.0.create_token_stream() } } impl fmt::Debug for LazyTokenStream { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt::Debug::fmt("LazyTokenStream", f) + write!(f, "LazyTokenStream({:?})", self.create_token_stream()) } } @@ -177,19 +174,158 @@ impl HashStable for LazyTokenStream { } } +/// A `AttrAnnotatedTokenStream` is similar to a `TokenStream`, but with extra +/// information about the tokens for attribute targets. This is used +/// during expansion to perform early cfg-expansion, and to process attributes +/// during proc-macro invocations. +#[derive(Clone, Debug, Default, Encodable, Decodable)] +pub struct AttrAnnotatedTokenStream(pub Lrc>); + +/// Like `TokenTree`, but for `AttrAnnotatedTokenStream` +#[derive(Clone, Debug, Encodable, Decodable)] +pub enum AttrAnnotatedTokenTree { + Token(Token), + Delimited(DelimSpan, DelimToken, AttrAnnotatedTokenStream), + /// Stores the attributes for an attribute target, + /// along with the tokens for that attribute target. + /// See `AttributesData` for more information + Attributes(AttributesData), +} + +impl AttrAnnotatedTokenStream { + pub fn new(tokens: Vec<(AttrAnnotatedTokenTree, Spacing)>) -> AttrAnnotatedTokenStream { + AttrAnnotatedTokenStream(Lrc::new(tokens)) + } + + /// Converts this `AttrAnnotatedTokenStream` to a plain `TokenStream + /// During conversion, `AttrAnnotatedTokenTree::Attributes` get 'flattened' + /// back to a `TokenStream` of the form `outer_attr attr_target`. + /// If there are inner attributes, they are inserted into the proper + /// place in the attribute target tokens. + pub fn to_tokenstream(&self) -> TokenStream { + let trees: Vec<_> = self + .0 + .iter() + .flat_map(|tree| match &tree.0 { + AttrAnnotatedTokenTree::Token(inner) => { + smallvec![(TokenTree::Token(inner.clone()), tree.1)].into_iter() + } + AttrAnnotatedTokenTree::Delimited(span, delim, stream) => smallvec![( + TokenTree::Delimited(*span, *delim, stream.to_tokenstream()), + tree.1, + )] + .into_iter(), + AttrAnnotatedTokenTree::Attributes(data) => { + let mut outer_attrs = Vec::new(); + let mut inner_attrs = Vec::new(); + let attrs: Vec<_> = data.attrs.clone().into(); + for attr in attrs { + match attr.style { + crate::AttrStyle::Outer => { + assert!( + inner_attrs.len() == 0, + "Found outer attribute {:?} after inner attrs {:?}", + attr, + inner_attrs + ); + outer_attrs.push(attr); + } + crate::AttrStyle::Inner => { + inner_attrs.push(attr); + } + } + } + + let mut target_tokens: Vec<_> = data + .tokens + .create_token_stream() + .to_tokenstream() + .0 + .iter() + .cloned() + .collect(); + if !inner_attrs.is_empty() { + let mut found = false; + // Check the last two trees (to account for a trailing semi) + for (tree, _) in target_tokens.iter_mut().rev().take(2) { + if let TokenTree::Delimited(span, delim, delim_tokens) = tree { + // Inner attributes are only supported on extern blocks, functions, impls, + // and modules. All of these have their inner attributes placed at + // the beginning of the rightmost outermost braced group: + // e.g. fn foo() { #![my_attr} } + // + // Therefore, we can insert them back into the right location + // without needing to do any extra position tracking. + // + // Note: Outline modules are an exception - they can + // have attributes like `#![my_attr]` at the start of a file. + // Support for custom attributes in this position is not + // properly implemented - we always synthesize fake tokens, + // so we never reach this code. + + let mut builder = TokenStreamBuilder::new(); + for inner_attr in &inner_attrs { + builder.push(inner_attr.tokens().to_tokenstream()); + } + builder.push(delim_tokens.clone()); + *tree = TokenTree::Delimited(*span, *delim, builder.build()); + found = true; + break; + } + } + + assert!( + found, + "Failed to find trailing delimited group in: {:?}", + target_tokens + ); + } + let mut flat: SmallVec<[_; 1]> = SmallVec::new(); + for attr in outer_attrs { + // FIXME: Make this more efficient + flat.extend(attr.tokens().to_tokenstream().0.clone().iter().cloned()); + } + flat.extend(target_tokens); + flat.into_iter() + } + }) + .collect(); + TokenStream::new(trees) + } +} + +/// Stores the tokens for an attribute target, along +/// with its attributes. +/// +/// This is constructed during parsing when we need to capture +/// tokens. +/// +/// For example, `#[cfg(FALSE)] struct Foo {}` would +/// have an `attrs` field containing the `#[cfg(FALSE)]` attr, +/// and a `tokens` field storing the (unparesd) tokens `struct Foo {}` +#[derive(Clone, Debug, Encodable, Decodable)] +pub struct AttributesData { + /// Attributes, both outer and inner. + /// These are stored in the original order that they were parsed in. + pub attrs: AttrVec, + /// The underlying tokens for the attribute target that `attrs` + /// are applied to + pub tokens: LazyTokenStream, +} + /// A `TokenStream` is an abstract sequence of tokens, organized into [`TokenTree`]s. /// /// The goal is for procedural macros to work with `TokenStream`s and `TokenTree`s /// instead of a representation of the abstract syntax tree. /// Today's `TokenTree`s can still contain AST via `token::Interpolated` for -/// backwards compatability. +/// backwards compatibility. #[derive(Clone, Debug, Default, Encodable, Decodable)] pub struct TokenStream(pub(crate) Lrc>); pub type TreeAndSpacing = (TokenTree, Spacing); // `TokenStream` is used a lot. Make sure it doesn't unintentionally get bigger. -#[cfg(target_arch = "x86_64")] +#[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))] rustc_data_structures::static_assert_size!(TokenStream, 8); #[derive(Clone, Copy, Debug, PartialEq, Encodable, Decodable)] @@ -239,6 +375,12 @@ impl TokenStream { } } +impl From<(AttrAnnotatedTokenTree, Spacing)> for AttrAnnotatedTokenStream { + fn from((tree, spacing): (AttrAnnotatedTokenTree, Spacing)) -> AttrAnnotatedTokenStream { + AttrAnnotatedTokenStream::new(vec![(tree, spacing)]) + } +} + impl From for TokenStream { fn from(tree: TokenTree) -> TokenStream { TokenStream::new(vec![(tree, Spacing::Alone)]) @@ -278,14 +420,6 @@ impl TokenStream { self.0.len() } - pub fn span(&self) -> Option { - match &**self.0 { - [] => None, - [(tt, _)] => Some(tt.span()), - [(tt_start, _), .., (tt_end, _)] => Some(tt_start.span().to(tt_end.span())), - } - } - pub fn from_streams(mut streams: SmallVec<[TokenStream; 2]>) -> TokenStream { match streams.len() { 0 => TokenStream::default(), @@ -325,10 +459,6 @@ impl TokenStream { } } - pub fn trees_ref(&self) -> CursorRef<'_> { - CursorRef::new(self) - } - pub fn trees(&self) -> Cursor { self.clone().into_trees() } @@ -341,7 +471,7 @@ impl TokenStream { pub fn eq_unspanned(&self, other: &TokenStream) -> bool { let mut t1 = self.trees(); let mut t2 = other.trees(); - for (t1, t2) in t1.by_ref().zip(t2.by_ref()) { + for (t1, t2) in iter::zip(&mut t1, &mut t2) { if !t1.eq_unspanned(&t2) { return false; } @@ -427,10 +557,6 @@ pub struct CursorRef<'t> { } impl<'t> CursorRef<'t> { - fn new(stream: &TokenStream) -> CursorRef<'_> { - CursorRef { stream, index: 0 } - } - fn next_with_spacing(&mut self) -> Option<&'t TreeAndSpacing> { self.stream.0.get(self.index).map(|tree| { self.index += 1; @@ -477,6 +603,10 @@ impl Cursor { } } + pub fn index(&self) -> usize { + self.index + } + pub fn append(&mut self, new_stream: TokenStream) { if new_stream.is_empty() { return; diff --git a/compiler/rustc_ast/src/visit.rs b/compiler/rustc_ast/src/visit.rs index 32b9dd46baef4..3f35919ae6a2a 100644 --- a/compiler/rustc_ast/src/visit.rs +++ b/compiler/rustc_ast/src/visit.rs @@ -151,8 +151,8 @@ pub trait Visitor<'ast>: Sized { fn visit_variant_data(&mut self, s: &'ast VariantData) { walk_struct_def(self, s) } - fn visit_struct_field(&mut self, s: &'ast StructField) { - walk_struct_field(self, s) + fn visit_field_def(&mut self, s: &'ast FieldDef) { + walk_field_def(self, s) } fn visit_enum_def( &mut self, @@ -208,11 +208,11 @@ pub trait Visitor<'ast>: Sized { fn visit_fn_header(&mut self, _header: &'ast FnHeader) { // Nothing to do } - fn visit_field(&mut self, f: &'ast Field) { - walk_field(self, f) + fn visit_expr_field(&mut self, f: &'ast ExprField) { + walk_expr_field(self, f) } - fn visit_field_pattern(&mut self, fp: &'ast FieldPat) { - walk_field_pattern(self, fp) + fn visit_pat_field(&mut self, fp: &'ast PatField) { + walk_pat_field(self, fp) } } @@ -364,13 +364,13 @@ where walk_list!(visitor, visit_attribute, &variant.attrs); } -pub fn walk_field<'a, V: Visitor<'a>>(visitor: &mut V, f: &'a Field) { +pub fn walk_expr_field<'a, V: Visitor<'a>>(visitor: &mut V, f: &'a ExprField) { visitor.visit_expr(&f.expr); visitor.visit_ident(f.ident); walk_list!(visitor, visit_attribute, f.attrs.iter()); } -pub fn walk_field_pattern<'a, V: Visitor<'a>>(visitor: &mut V, fp: &'a FieldPat) { +pub fn walk_pat_field<'a, V: Visitor<'a>>(visitor: &mut V, fp: &'a PatField) { visitor.visit_ident(fp.ident); visitor.visit_pat(&fp.pat); walk_list!(visitor, visit_attribute, fp.attrs.iter()); @@ -509,7 +509,7 @@ pub fn walk_pat<'a, V: Visitor<'a>>(visitor: &mut V, pattern: &'a Pat) { } PatKind::Struct(ref path, ref fields, _) => { visitor.visit_path(path, pattern.id); - walk_list!(visitor, visit_field_pattern, fields); + walk_list!(visitor, visit_pat_field, fields); } PatKind::Box(ref subpattern) | PatKind::Ref(ref subpattern, _) @@ -668,16 +668,16 @@ pub fn walk_assoc_item<'a, V: Visitor<'a>>(visitor: &mut V, item: &'a AssocItem, } pub fn walk_struct_def<'a, V: Visitor<'a>>(visitor: &mut V, struct_definition: &'a VariantData) { - walk_list!(visitor, visit_struct_field, struct_definition.fields()); + walk_list!(visitor, visit_field_def, struct_definition.fields()); } -pub fn walk_struct_field<'a, V: Visitor<'a>>(visitor: &mut V, struct_field: &'a StructField) { - visitor.visit_vis(&struct_field.vis); - if let Some(ident) = struct_field.ident { +pub fn walk_field_def<'a, V: Visitor<'a>>(visitor: &mut V, field: &'a FieldDef) { + visitor.visit_vis(&field.vis); + if let Some(ident) = field.ident { visitor.visit_ident(ident); } - visitor.visit_ty(&struct_field.ty); - walk_list!(visitor, visit_attribute, &struct_field.attrs); + visitor.visit_ty(&field.ty); + walk_list!(visitor, visit_attribute, &field.attrs); } pub fn walk_block<'a, V: Visitor<'a>>(visitor: &mut V, block: &'a Block) { @@ -721,10 +721,10 @@ pub fn walk_expr<'a, V: Visitor<'a>>(visitor: &mut V, expression: &'a Expr) { visitor.visit_expr(element); visitor.visit_anon_const(count) } - ExprKind::Struct(ref path, ref fields, ref optional_base) => { - visitor.visit_path(path, expression.id); - walk_list!(visitor, visit_field, fields); - match optional_base { + ExprKind::Struct(ref se) => { + visitor.visit_path(&se.path, expression.id); + walk_list!(visitor, visit_expr_field, &se.fields); + match &se.rest { StructRest::Base(expr) => visitor.visit_expr(expr), StructRest::Rest(_span) => {} StructRest::None => {} @@ -835,7 +835,6 @@ pub fn walk_expr<'a, V: Visitor<'a>>(visitor: &mut V, expression: &'a Expr) { match op { InlineAsmOperand::In { expr, .. } | InlineAsmOperand::InOut { expr, .. } - | InlineAsmOperand::Const { expr, .. } | InlineAsmOperand::Sym { expr, .. } => visitor.visit_expr(expr), InlineAsmOperand::Out { expr, .. } => { if let Some(expr) = expr { @@ -848,6 +847,9 @@ pub fn walk_expr<'a, V: Visitor<'a>>(visitor: &mut V, expression: &'a Expr) { visitor.visit_expr(out_expr); } } + InlineAsmOperand::Const { anon_const, .. } => { + visitor.visit_anon_const(anon_const) + } } } } diff --git a/compiler/rustc_ast_lowering/src/expr.rs b/compiler/rustc_ast_lowering/src/expr.rs index 82b41e13cccff..75dfe951c9480 100644 --- a/compiler/rustc_ast_lowering/src/expr.rs +++ b/compiler/rustc_ast_lowering/src/expr.rs @@ -97,6 +97,23 @@ impl<'hir> LoweringContext<'_, 'hir> { ExprKind::Let(ref pat, ref scrutinee) => { self.lower_expr_if_let(e.span, pat, scrutinee, then, else_opt.as_deref()) } + ExprKind::Paren(ref paren) => match paren.peel_parens().kind { + ExprKind::Let(ref pat, ref scrutinee) => { + // A user has written `if (let Some(x) = foo) {`, we want to avoid + // confusing them with mentions of nightly features. + // If this logic is changed, you will also likely need to touch + // `unused::UnusedParens::check_expr`. + self.if_let_expr_with_parens(cond, &paren.peel_parens()); + self.lower_expr_if_let( + e.span, + pat, + scrutinee, + then, + else_opt.as_deref(), + ) + } + _ => self.lower_expr_if(cond, then, else_opt.as_deref()), + }, _ => self.lower_expr_if(cond, then, else_opt.as_deref()), }, ExprKind::While(ref cond, ref body, opt_label) => self @@ -207,8 +224,8 @@ impl<'hir> LoweringContext<'_, 'hir> { } ExprKind::InlineAsm(ref asm) => self.lower_expr_asm(e.span, asm), ExprKind::LlvmInlineAsm(ref asm) => self.lower_expr_llvm_asm(asm), - ExprKind::Struct(ref path, ref fields, ref rest) => { - let rest = match rest { + ExprKind::Struct(ref se) => { + let rest = match &se.rest { StructRest::Base(e) => Some(self.lower_expr(e)), StructRest::Rest(sp) => { self.sess @@ -223,11 +240,12 @@ impl<'hir> LoweringContext<'_, 'hir> { self.arena.alloc(self.lower_qpath( e.id, &None, - path, + &se.path, ParamMode::Optional, ImplTraitContext::disallowed(), )), - self.arena.alloc_from_iter(fields.iter().map(|x| self.lower_field(x))), + self.arena + .alloc_from_iter(se.fields.iter().map(|x| self.lower_expr_field(x))), rest, ) } @@ -241,9 +259,18 @@ impl<'hir> LoweringContext<'_, 'hir> { ex.span = e.span; } // Merge attributes into the inner expression. - let mut attrs: Vec<_> = e.attrs.iter().map(|a| self.lower_attr(a)).collect(); - attrs.extend::>(ex.attrs.into()); - ex.attrs = attrs.into(); + if !e.attrs.is_empty() { + let old_attrs = self.attrs.get(&ex.hir_id).map(|la| *la).unwrap_or(&[]); + self.attrs.insert( + ex.hir_id, + &*self.arena.alloc_from_iter( + e.attrs + .iter() + .map(|a| self.lower_attr(a)) + .chain(old_attrs.iter().cloned()), + ), + ); + } return ex; } @@ -255,12 +282,9 @@ impl<'hir> LoweringContext<'_, 'hir> { ExprKind::MacCall(_) => panic!("{:?} shouldn't exist here", e.span), }; - hir::Expr { - hir_id: self.lower_node_id(e.id), - kind, - span: e.span, - attrs: e.attrs.iter().map(|a| self.lower_attr(a)).collect::>().into(), - } + let hir_id = self.lower_node_id(e.id); + self.lower_attrs(hir_id, &e.attrs); + hir::Expr { hir_id, kind, span: e.span } }) } @@ -346,6 +370,25 @@ impl<'hir> LoweringContext<'_, 'hir> { hir::ExprKind::Call(f, self.lower_exprs(&real_args)) } + fn if_let_expr_with_parens(&mut self, cond: &Expr, paren: &Expr) { + let start = cond.span.until(paren.span); + let end = paren.span.shrink_to_hi().until(cond.span.shrink_to_hi()); + self.sess + .struct_span_err( + vec![start, end], + "invalid parentheses around `let` expression in `if let`", + ) + .multipart_suggestion( + "`if let` needs to be written without parentheses", + vec![(start, String::new()), (end, String::new())], + rustc_errors::Applicability::MachineApplicable, + ) + .emit(); + // Ideally, we'd remove the feature gating of a `let` expression since we are already + // complaining about it here, but `feature_gate::check_crate` has already run by now: + // self.sess.parse_sess.gated_spans.ungate_last(sym::let_chains, paren.span); + } + /// Emit an error and lower `ast::ExprKind::Let(pat, scrutinee)` into: /// ```rust /// match scrutinee { pats => true, _ => false } @@ -356,8 +399,10 @@ impl<'hir> LoweringContext<'_, 'hir> { if self.sess.opts.unstable_features.is_nightly_build() { self.sess .struct_span_err(span, "`let` expressions are not supported here") - .note("only supported directly in conditions of `if`- and `while`-expressions") - .note("as well as when nested within `&&` and parenthesis in those conditions") + .note( + "only supported directly without parentheses in conditions of `if`- and \ + `while`-expressions, as well as in `let` chains within parentheses", + ) .emit(); } else { self.sess @@ -580,14 +625,9 @@ impl<'hir> LoweringContext<'_, 'hir> { hir::Guard::If(self.lower_expr(cond)) } }); - hir::Arm { - hir_id: self.next_id(), - attrs: self.lower_attrs(&arm.attrs), - pat, - guard, - body: self.lower_expr(&arm.body), - span: arm.span, - } + let hir_id = self.next_id(); + self.lower_attrs(hir_id, &arm.attrs); + hir::Arm { hir_id, pat, guard, body: self.lower_expr(&arm.body), span: arm.span } } /// Lower an `async` construct to a generator that is then wrapped so it implements `Future`. @@ -631,7 +671,7 @@ impl<'hir> LoweringContext<'_, 'hir> { Ident::with_dummy_span(sym::_task_context), hir::BindingAnnotation::Mutable, ); - let param = hir::Param { attrs: &[], hir_id: self.next_id(), pat, ty_span: span, span }; + let param = hir::Param { hir_id: self.next_id(), pat, ty_span: span, span }; let params = arena_vec![self; param]; let body_id = self.lower_body(move |this| { @@ -652,12 +692,8 @@ impl<'hir> LoweringContext<'_, 'hir> { span, Some(hir::Movability::Static), ); - let generator = hir::Expr { - hir_id: self.lower_node_id(closure_node_id), - kind: generator_kind, - span, - attrs: ThinVec::new(), - }; + let generator = + hir::Expr { hir_id: self.lower_node_id(closure_node_id), kind: generator_kind, span }; // `future::from_generator`: let unstable_span = @@ -811,7 +847,6 @@ impl<'hir> LoweringContext<'_, 'hir> { hir_id: loop_hir_id, kind: hir::ExprKind::Loop(loop_block, None, hir::LoopSource::Loop, span), span, - attrs: ThinVec::new(), }); // mut pinned => loop { ... } @@ -988,7 +1023,7 @@ impl<'hir> LoweringContext<'_, 'hir> { // Introduce a `let` for destructuring: `let (lhs1, lhs2) = t`. let destructure_let = self.stmt_let_pat( - ThinVec::new(), + None, whole_span, Some(rhs), pat, @@ -1076,10 +1111,10 @@ impl<'hir> LoweringContext<'_, 'hir> { } } // Structs. - ExprKind::Struct(path, fields, rest) => { - let field_pats = self.arena.alloc_from_iter(fields.iter().map(|f| { + ExprKind::Struct(se) => { + let field_pats = self.arena.alloc_from_iter(se.fields.iter().map(|f| { let pat = self.destructure_assign(&f.expr, eq_sign_span, assignments); - hir::FieldPat { + hir::PatField { hir_id: self.next_id(), ident: f.ident, pat, @@ -1090,11 +1125,11 @@ impl<'hir> LoweringContext<'_, 'hir> { let qpath = self.lower_qpath( lhs.id, &None, - path, + &se.path, ParamMode::Optional, ImplTraitContext::disallowed(), ); - let fields_omitted = match rest { + let fields_omitted = match &se.rest { StructRest::Base(e) => { self.sess .struct_span_err( @@ -1210,7 +1245,7 @@ impl<'hir> LoweringContext<'_, 'hir> { 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), e.span); - self.field(ident, expr, e.span) + self.expr_field(ident, expr, e.span) }), ); @@ -1297,104 +1332,97 @@ impl<'hir> LoweringContext<'_, 'hir> { } fn lower_expr_asm(&mut self, sp: Span, asm: &InlineAsm) -> hir::ExprKind<'hir> { - if self.sess.asm_arch.is_none() { + // Rustdoc needs to support asm! from foriegn architectures: don't try + // lowering the register contraints in this case. + let asm_arch = if self.sess.opts.actually_rustdoc { None } else { self.sess.asm_arch }; + if asm_arch.is_none() && !self.sess.opts.actually_rustdoc { struct_span_err!(self.sess, sp, E0472, "asm! is unsupported on this target").emit(); } if asm.options.contains(InlineAsmOptions::ATT_SYNTAX) - && !matches!( - self.sess.asm_arch, - Some(asm::InlineAsmArch::X86 | asm::InlineAsmArch::X86_64) - ) + && !matches!(asm_arch, Some(asm::InlineAsmArch::X86 | asm::InlineAsmArch::X86_64)) + && !self.sess.opts.actually_rustdoc { self.sess .struct_span_err(sp, "the `att_syntax` option is only supported on x86") .emit(); } - // Lower operands to HIR, filter_map skips any operands with invalid - // register classes. + // Lower operands to HIR. We use dummy register classes if an error + // occurs during lowering because we still need to be able to produce a + // valid HIR. let sess = self.sess; let operands: Vec<_> = asm .operands .iter() - .filter_map(|(op, op_sp)| { - let lower_reg = |reg| { - Some(match reg { - InlineAsmRegOrRegClass::Reg(s) => asm::InlineAsmRegOrRegClass::Reg( + .map(|(op, op_sp)| { + let lower_reg = |reg| match reg { + InlineAsmRegOrRegClass::Reg(s) => { + asm::InlineAsmRegOrRegClass::Reg(if let Some(asm_arch) = asm_arch { asm::InlineAsmReg::parse( - sess.asm_arch?, + asm_arch, |feature| sess.target_features.contains(&Symbol::intern(feature)), &sess.target, s, ) - .map_err(|e| { + .unwrap_or_else(|e| { let msg = format!("invalid register `{}`: {}", s.as_str(), e); sess.struct_span_err(*op_sp, &msg).emit(); + asm::InlineAsmReg::Err }) - .ok()?, - ), - InlineAsmRegOrRegClass::RegClass(s) => { - asm::InlineAsmRegOrRegClass::RegClass( - asm::InlineAsmRegClass::parse(sess.asm_arch?, s) - .map_err(|e| { - let msg = format!( - "invalid register class `{}`: {}", - s.as_str(), - e - ); - sess.struct_span_err(*op_sp, &msg).emit(); - }) - .ok()?, - ) - } - }) + } else { + asm::InlineAsmReg::Err + }) + } + InlineAsmRegOrRegClass::RegClass(s) => { + asm::InlineAsmRegOrRegClass::RegClass(if let Some(asm_arch) = asm_arch { + asm::InlineAsmRegClass::parse(asm_arch, s).unwrap_or_else(|e| { + let msg = format!("invalid register class `{}`: {}", s.as_str(), e); + sess.struct_span_err(*op_sp, &msg).emit(); + asm::InlineAsmRegClass::Err + }) + } else { + asm::InlineAsmRegClass::Err + }) + } }; - // lower_reg is executed last because we need to lower all - // sub-expressions even if we throw them away later. let op = match *op { InlineAsmOperand::In { reg, ref expr } => hir::InlineAsmOperand::In { + reg: lower_reg(reg), expr: self.lower_expr_mut(expr), - reg: lower_reg(reg)?, }, InlineAsmOperand::Out { reg, late, ref expr } => hir::InlineAsmOperand::Out { + reg: lower_reg(reg), late, expr: expr.as_ref().map(|expr| self.lower_expr_mut(expr)), - reg: lower_reg(reg)?, }, InlineAsmOperand::InOut { reg, late, ref expr } => { hir::InlineAsmOperand::InOut { + reg: lower_reg(reg), late, expr: self.lower_expr_mut(expr), - reg: lower_reg(reg)?, } } InlineAsmOperand::SplitInOut { reg, late, ref in_expr, ref out_expr } => { hir::InlineAsmOperand::SplitInOut { + reg: lower_reg(reg), late, in_expr: self.lower_expr_mut(in_expr), out_expr: out_expr.as_ref().map(|expr| self.lower_expr_mut(expr)), - reg: lower_reg(reg)?, } } - InlineAsmOperand::Const { ref expr } => { - hir::InlineAsmOperand::Const { expr: self.lower_expr_mut(expr) } - } + InlineAsmOperand::Const { ref anon_const } => hir::InlineAsmOperand::Const { + anon_const: self.lower_anon_const(anon_const), + }, InlineAsmOperand::Sym { ref expr } => { hir::InlineAsmOperand::Sym { expr: self.lower_expr_mut(expr) } } }; - Some((op, *op_sp)) + (op, *op_sp) }) .collect(); - // Stop if there were any errors when lowering the register classes - if operands.len() != asm.operands.len() || sess.asm_arch.is_none() { - return hir::ExprKind::Err; - } - // Validate template modifiers against the register classes for the operands - let asm_arch = sess.asm_arch.unwrap(); for p in &asm.template { if let InlineAsmTemplatePiece::Placeholder { operand_idx, @@ -1409,7 +1437,10 @@ impl<'hir> LoweringContext<'_, 'hir> { | hir::InlineAsmOperand::InOut { reg, .. } | hir::InlineAsmOperand::SplitInOut { reg, .. } => { let class = reg.reg_class(); - let valid_modifiers = class.valid_modifiers(asm_arch); + if class == asm::InlineAsmRegClass::Err { + continue; + } + let valid_modifiers = class.valid_modifiers(asm_arch.unwrap()); if !valid_modifiers.contains(&modifier) { let mut err = sess.struct_span_err( placeholder_span, @@ -1468,43 +1499,64 @@ impl<'hir> LoweringContext<'_, 'hir> { // previous iteration. required_features.clear(); - // Validate register classes against currently enabled target - // features. We check that at least one type is available for - // the current target. let reg_class = reg.reg_class(); - for &(_, feature) in reg_class.supported_types(asm_arch) { - if let Some(feature) = feature { - if self.sess.target_features.contains(&Symbol::intern(feature)) { + if reg_class == asm::InlineAsmRegClass::Err { + continue; + } + + // We ignore target feature requirements for clobbers: if the + // feature is disabled then the compiler doesn't care what we + // do with the registers. + // + // Note that this is only possible for explicit register + // operands, which cannot be used in the asm string. + let is_clobber = matches!( + op, + hir::InlineAsmOperand::Out { + reg: asm::InlineAsmRegOrRegClass::Reg(_), + late: _, + expr: None + } + ); + + if !is_clobber { + // Validate register classes against currently enabled target + // features. We check that at least one type is available for + // the current target. + for &(_, feature) in reg_class.supported_types(asm_arch.unwrap()) { + if let Some(feature) = feature { + if self.sess.target_features.contains(&Symbol::intern(feature)) { + required_features.clear(); + break; + } else { + required_features.push(feature); + } + } else { required_features.clear(); break; - } else { - required_features.push(feature); } - } else { - required_features.clear(); - break; - } - } - // We are sorting primitive strs here and can use unstable sort here - required_features.sort_unstable(); - required_features.dedup(); - match &required_features[..] { - [] => {} - [feature] => { - let msg = format!( - "register class `{}` requires the `{}` target feature", - reg_class.name(), - feature - ); - sess.struct_span_err(op_sp, &msg).emit(); } - features => { - let msg = format!( - "register class `{}` requires at least one target feature: {}", - reg_class.name(), - features.join(", ") - ); - sess.struct_span_err(op_sp, &msg).emit(); + // We are sorting primitive strs here and can use unstable sort here + required_features.sort_unstable(); + required_features.dedup(); + match &required_features[..] { + [] => {} + [feature] => { + let msg = format!( + "register class `{}` requires the `{}` target feature", + reg_class.name(), + feature + ); + sess.struct_span_err(op_sp, &msg).emit(); + } + features => { + let msg = format!( + "register class `{}` requires at least one target feature: {}", + reg_class.name(), + features.join(", ") + ); + sess.struct_span_err(op_sp, &msg).emit(); + } } } @@ -1624,8 +1676,8 @@ impl<'hir> LoweringContext<'_, 'hir> { hir::ExprKind::LlvmInlineAsm(self.arena.alloc(hir_asm)) } - fn lower_field(&mut self, f: &Field) -> hir::Field<'hir> { - hir::Field { + fn lower_expr_field(&mut self, f: &ExprField) -> hir::ExprField<'hir> { + hir::ExprField { hir_id: self.next_id(), ident: f.ident, expr: self.lower_expr(&f.expr), @@ -1747,7 +1799,7 @@ impl<'hir> LoweringContext<'_, 'hir> { // `let mut __next` let next_let = self.stmt_let_pat( - ThinVec::new(), + None, desugared_span, None, next_pat, @@ -1757,7 +1809,7 @@ impl<'hir> LoweringContext<'_, 'hir> { // `let = __next` let pat = self.lower_pat(pat); let pat_let = self.stmt_let_pat( - ThinVec::new(), + None, desugared_span, Some(next_expr), pat, @@ -1781,12 +1833,8 @@ impl<'hir> LoweringContext<'_, 'hir> { hir::LoopSource::ForLoop, e.span.with_hi(orig_head_span.hi()), ); - let loop_expr = self.arena.alloc(hir::Expr { - hir_id: self.lower_node_id(e.id), - kind, - span: e.span, - attrs: ThinVec::new(), - }); + let loop_expr = + self.arena.alloc(hir::Expr { hir_id: self.lower_node_id(e.id), kind, span: e.span }); // `mut iter => { ... }` let iter_arm = self.arm(iter_pat, loop_expr); @@ -2121,21 +2169,21 @@ impl<'hir> LoweringContext<'_, 'hir> { kind: hir::ExprKind<'hir>, attrs: AttrVec, ) -> hir::Expr<'hir> { - hir::Expr { hir_id: self.next_id(), kind, span, attrs } + let hir_id = self.next_id(); + self.lower_attrs(hir_id, &attrs); + hir::Expr { hir_id, kind, span } } - fn field(&mut self, ident: Ident, expr: &'hir hir::Expr<'hir>, span: Span) -> hir::Field<'hir> { - hir::Field { hir_id: self.next_id(), ident, span, expr, is_shorthand: false } + fn expr_field( + &mut self, + ident: Ident, + expr: &'hir hir::Expr<'hir>, + span: Span, + ) -> hir::ExprField<'hir> { + hir::ExprField { hir_id: self.next_id(), ident, span, expr, is_shorthand: false } } fn arm(&mut self, pat: &'hir hir::Pat<'hir>, expr: &'hir hir::Expr<'hir>) -> hir::Arm<'hir> { - hir::Arm { - hir_id: self.next_id(), - attrs: &[], - pat, - guard: None, - span: expr.span, - body: expr, - } + hir::Arm { hir_id: self.next_id(), pat, guard: None, span: expr.span, body: expr } } } diff --git a/compiler/rustc_ast_lowering/src/item.rs b/compiler/rustc_ast_lowering/src/item.rs index 8b740b7774089..5fd8f7eb33a1f 100644 --- a/compiler/rustc_ast_lowering/src/item.rs +++ b/compiler/rustc_ast_lowering/src/item.rs @@ -18,6 +18,7 @@ use rustc_target::spec::abi; use smallvec::{smallvec, SmallVec}; use tracing::debug; +use std::iter; use std::mem; pub(super) struct ItemLowerer<'a, 'lowering, 'hir> { @@ -206,7 +207,7 @@ impl<'hir> LoweringContext<'_, 'hir> { UseTreeKind::Glob => {} UseTreeKind::Simple(_, id1, id2) => { for (_, &id) in - self.expect_full_res_from_use(base_id).skip(1).zip([id1, id2].iter()) + iter::zip(self.expect_full_res_from_use(base_id).skip(1), &[id1, id2]) { vec.push(id); } @@ -217,44 +218,41 @@ impl<'hir> LoweringContext<'_, 'hir> { pub fn lower_item(&mut self, i: &Item) -> Option> { let mut ident = i.ident; let mut vis = self.lower_visibility(&i.vis, None); - let attrs = self.lower_attrs(&i.attrs); if let ItemKind::MacroDef(MacroDef { ref body, macro_rules }) = i.kind { if !macro_rules || self.sess.contains_name(&i.attrs, sym::macro_export) { - let def_id = self.lower_node_id(i.id).expect_owner(); + let hir_id = self.lower_node_id(i.id); + self.lower_attrs(hir_id, &i.attrs); let body = P(self.lower_mac_args(body)); self.exported_macros.push(hir::MacroDef { ident, vis, - attrs, - def_id, + def_id: hir_id.expect_owner(), span: i.span, ast: MacroDef { body, macro_rules }, }); } else { - self.non_exported_macro_attrs.extend(attrs.iter().cloned()); + for a in i.attrs.iter() { + let a = self.lower_attr(a); + self.non_exported_macro_attrs.push(a); + } } return None; } - let kind = self.lower_item_kind(i.span, i.id, &mut ident, attrs, &mut vis, &i.kind); - - Some(hir::Item { - def_id: self.lower_node_id(i.id).expect_owner(), - ident, - attrs, - kind, - vis, - span: i.span, - }) + let hir_id = self.lower_node_id(i.id); + let attrs = self.lower_attrs(hir_id, &i.attrs); + let kind = self.lower_item_kind(i.span, i.id, hir_id, &mut ident, attrs, &mut vis, &i.kind); + Some(hir::Item { def_id: hir_id.expect_owner(), ident, kind, vis, span: i.span }) } fn lower_item_kind( &mut self, span: Span, id: NodeId, + hir_id: hir::HirId, ident: &mut Ident, - attrs: &'hir [Attribute], + attrs: Option<&'hir [Attribute]>, vis: &mut hir::Visibility<'hir>, i: &ItemKind, ) -> hir::ItemKind<'hir> { @@ -322,10 +320,10 @@ impl<'hir> LoweringContext<'_, 'hir> { }, ItemKind::ForeignMod(ref fm) => { if fm.abi.is_none() { - self.maybe_lint_missing_abi(span, id, abi::Abi::C); + self.maybe_lint_missing_abi(span, id, abi::Abi::C { unwind: false }); } hir::ItemKind::ForeignMod { - abi: fm.abi.map_or(abi::Abi::C, |abi| self.lower_abi(abi)), + abi: fm.abi.map_or(abi::Abi::C { unwind: false }, |abi| self.lower_abi(abi)), items: self .arena .alloc_from_iter(fm.items.iter().map(|x| self.lower_foreign_item_ref(x))), @@ -345,7 +343,7 @@ impl<'hir> LoweringContext<'_, 'hir> { ty, ImplTraitContext::OtherOpaqueTy { capturable_lifetimes: &mut FxHashSet::default(), - origin: hir::OpaqueTyOrigin::Misc, + origin: hir::OpaqueTyOrigin::TyAlias, }, ); let generics = self.lower_generics(gen, ImplTraitContext::disallowed()); @@ -365,14 +363,14 @@ impl<'hir> LoweringContext<'_, 'hir> { self.lower_generics(generics, ImplTraitContext::disallowed()), ), ItemKind::Struct(ref struct_def, ref generics) => { - let struct_def = self.lower_variant_data(struct_def); + let struct_def = self.lower_variant_data(hir_id, struct_def); hir::ItemKind::Struct( struct_def, self.lower_generics(generics, ImplTraitContext::disallowed()), ) } ItemKind::Union(ref vdata, ref generics) => { - let vdata = self.lower_variant_data(vdata); + let vdata = self.lower_variant_data(hir_id, vdata); hir::ItemKind::Union( vdata, self.lower_generics(generics, ImplTraitContext::disallowed()), @@ -505,7 +503,7 @@ impl<'hir> LoweringContext<'_, 'hir> { id: NodeId, vis: &mut hir::Visibility<'hir>, ident: &mut Ident, - attrs: &'hir [Attribute], + attrs: Option<&'hir [Attribute]>, ) -> hir::ItemKind<'hir> { debug!("lower_use_tree(tree={:?})", tree); debug!("lower_use_tree: vis = {:?}", vis); @@ -540,7 +538,7 @@ impl<'hir> LoweringContext<'_, 'hir> { // won't be dealing with macros in the rest of the compiler. // Essentially a single `use` which imports two names is desugared into // two imports. - for (res, &new_node_id) in resolutions.zip([id1, id2].iter()) { + for (res, &new_node_id) in iter::zip(resolutions, &[id1, id2]) { let ident = *ident; let mut path = path.clone(); for seg in &mut path.segments { @@ -554,11 +552,13 @@ impl<'hir> LoweringContext<'_, 'hir> { let path = this.lower_path_extra(res, &path, ParamMode::Explicit, None); let kind = hir::ItemKind::Use(path, hir::UseKind::Single); let vis = this.rebuild_vis(&vis); + if let Some(attrs) = attrs { + this.attrs.insert(new_id, attrs); + } this.insert_item(hir::Item { def_id: new_id.expect_owner(), ident, - attrs, kind, vis, span, @@ -626,11 +626,13 @@ impl<'hir> LoweringContext<'_, 'hir> { let kind = this.lower_use_tree(use_tree, &prefix, id, &mut vis, &mut ident, attrs); + if let Some(attrs) = attrs { + this.attrs.insert(new_hir_id, attrs); + } this.insert_item(hir::Item { def_id: new_hir_id.expect_owner(), ident, - attrs, kind, vis, span: use_tree.span, @@ -699,11 +701,12 @@ impl<'hir> LoweringContext<'_, 'hir> { } fn lower_foreign_item(&mut self, i: &ForeignItem) -> hir::ForeignItem<'hir> { - let def_id = self.resolver.local_def_id(i.id); + let hir_id = self.lower_node_id(i.id); + let def_id = hir_id.expect_owner(); + self.lower_attrs(hir_id, &i.attrs); hir::ForeignItem { def_id, ident: i.ident, - attrs: self.lower_attrs(&i.attrs), kind: match i.kind { ForeignItemKind::Fn(box FnKind(_, ref sig, ref generics, _)) => { let fdec = &sig.decl; @@ -748,33 +751,47 @@ impl<'hir> LoweringContext<'_, 'hir> { } fn lower_variant(&mut self, v: &Variant) -> hir::Variant<'hir> { + let id = self.lower_node_id(v.id); + self.lower_attrs(id, &v.attrs); hir::Variant { - attrs: self.lower_attrs(&v.attrs), - data: self.lower_variant_data(&v.data), + id, + data: self.lower_variant_data(id, &v.data), disr_expr: v.disr_expr.as_ref().map(|e| self.lower_anon_const(e)), - id: self.lower_node_id(v.id), ident: v.ident, span: v.span, } } - fn lower_variant_data(&mut self, vdata: &VariantData) -> hir::VariantData<'hir> { + fn lower_variant_data( + &mut self, + parent_id: hir::HirId, + vdata: &VariantData, + ) -> hir::VariantData<'hir> { match *vdata { VariantData::Struct(ref fields, recovered) => hir::VariantData::Struct( self.arena - .alloc_from_iter(fields.iter().enumerate().map(|f| self.lower_struct_field(f))), + .alloc_from_iter(fields.iter().enumerate().map(|f| self.lower_field_def(f))), recovered, ), - VariantData::Tuple(ref fields, id) => hir::VariantData::Tuple( - self.arena - .alloc_from_iter(fields.iter().enumerate().map(|f| self.lower_struct_field(f))), - self.lower_node_id(id), - ), - VariantData::Unit(id) => hir::VariantData::Unit(self.lower_node_id(id)), + VariantData::Tuple(ref fields, id) => { + let ctor_id = self.lower_node_id(id); + self.alias_attrs(ctor_id, parent_id); + hir::VariantData::Tuple( + self.arena.alloc_from_iter( + fields.iter().enumerate().map(|f| self.lower_field_def(f)), + ), + ctor_id, + ) + } + VariantData::Unit(id) => { + let ctor_id = self.lower_node_id(id); + self.alias_attrs(ctor_id, parent_id); + hir::VariantData::Unit(ctor_id) + } } } - fn lower_struct_field(&mut self, (index, f): (usize, &StructField)) -> hir::StructField<'hir> { + fn lower_field_def(&mut self, (index, f): (usize, &FieldDef)) -> hir::FieldDef<'hir> { let ty = if let TyKind::Path(ref qself, ref path) = f.ty.kind { let t = self.lower_path_ty( &f.ty, @@ -787,9 +804,11 @@ impl<'hir> LoweringContext<'_, 'hir> { } else { self.lower_ty(&f.ty, ImplTraitContext::disallowed()) }; - hir::StructField { + let hir_id = self.lower_node_id(f.id); + self.lower_attrs(hir_id, &f.attrs); + hir::FieldDef { span: f.span, - hir_id: self.lower_node_id(f.id), + hir_id, ident: match f.ident { Some(ident) => ident, // FIXME(jseyfried): positional field hygiene. @@ -797,12 +816,12 @@ impl<'hir> LoweringContext<'_, 'hir> { }, vis: self.lower_visibility(&f.vis, None), ty, - attrs: self.lower_attrs(&f.attrs), } } fn lower_trait_item(&mut self, i: &AssocItem) -> hir::TraitItem<'hir> { - let trait_item_def_id = self.resolver.local_def_id(i.id); + let hir_id = self.lower_node_id(i.id); + let trait_item_def_id = hir_id.expect_owner(); let (generics, kind) = match i.kind { AssocItemKind::Const(_, ref ty, ref default) => { @@ -817,9 +836,17 @@ impl<'hir> LoweringContext<'_, 'hir> { (generics, hir::TraitItemKind::Fn(sig, hir::TraitFn::Required(names))) } AssocItemKind::Fn(box FnKind(_, ref sig, ref generics, Some(ref body))) => { - let body_id = self.lower_fn_body_block(i.span, &sig.decl, Some(body)); - let (generics, sig) = - self.lower_method_sig(generics, sig, trait_item_def_id, false, None, i.id); + let asyncness = sig.header.asyncness; + let body_id = + self.lower_maybe_async_body(i.span, &sig.decl, asyncness, Some(&body)); + let (generics, sig) = self.lower_method_sig( + generics, + sig, + trait_item_def_id, + false, + asyncness.opt_return_id(), + i.id, + ); (generics, hir::TraitItemKind::Fn(sig, hir::TraitFn::Provided(body_id))) } AssocItemKind::TyAlias(box TyAliasKind(_, ref generics, ref bounds, ref default)) => { @@ -835,14 +862,8 @@ impl<'hir> LoweringContext<'_, 'hir> { AssocItemKind::MacCall(..) => panic!("macro item shouldn't exist at this point"), }; - hir::TraitItem { - def_id: trait_item_def_id, - ident: i.ident, - attrs: self.lower_attrs(&i.attrs), - generics, - kind, - span: i.span, - } + self.lower_attrs(hir_id, &i.attrs); + hir::TraitItem { def_id: trait_item_def_id, ident: i.ident, generics, kind, span: i.span } } fn lower_trait_item_ref(&mut self, i: &AssocItem) -> hir::TraitItemRef { @@ -906,7 +927,7 @@ impl<'hir> LoweringContext<'_, 'hir> { ty, ImplTraitContext::OtherOpaqueTy { capturable_lifetimes: &mut FxHashSet::default(), - origin: hir::OpaqueTyOrigin::Misc, + origin: hir::OpaqueTyOrigin::TyAlias, }, ); hir::ImplItemKind::TyAlias(ty) @@ -920,10 +941,11 @@ impl<'hir> LoweringContext<'_, 'hir> { // Since `default impl` is not yet implemented, this is always true in impls. let has_value = true; let (defaultness, _) = self.lower_defaultness(i.kind.defaultness(), has_value); + let hir_id = self.lower_node_id(i.id); + self.lower_attrs(hir_id, &i.attrs); hir::ImplItem { - def_id: self.lower_node_id(i.id).expect_owner(), + def_id: hir_id.expect_owner(), ident: i.ident, - attrs: self.lower_attrs(&i.attrs), generics, vis: self.lower_visibility(&i.vis, None), defaultness, @@ -1024,9 +1046,10 @@ impl<'hir> LoweringContext<'_, 'hir> { } fn lower_param(&mut self, param: &Param) -> hir::Param<'hir> { + let hir_id = self.lower_node_id(param.id); + self.lower_attrs(hir_id, ¶m.attrs); hir::Param { - attrs: self.lower_attrs(¶m.attrs), - hir_id: self.lower_node_id(param.id), + hir_id, pat: self.lower_pat(¶m.pat), ty_span: param.ty.span, span: param.span, @@ -1158,11 +1181,9 @@ impl<'hir> LoweringContext<'_, 'hir> { // // If this is the simple case, this parameter will end up being the same as the // original parameter, but with a different pattern id. - let mut stmt_attrs = AttrVec::new(); - stmt_attrs.extend(parameter.attrs.iter().cloned()); + let stmt_attrs = this.attrs.get(¶meter.hir_id).copied(); let (new_parameter_pat, new_parameter_id) = this.pat_ident(desugared_span, ident); let new_parameter = hir::Param { - attrs: parameter.attrs, hir_id: parameter.hir_id, pat: new_parameter_pat, ty_span: parameter.ty_span, @@ -1205,7 +1226,7 @@ impl<'hir> LoweringContext<'_, 'hir> { ); let move_expr = this.expr_ident(desugared_span, ident, new_parameter_id); let move_stmt = this.stmt_let_pat( - AttrVec::new(), + None, desugared_span, Some(move_expr), move_pat, @@ -1322,8 +1343,8 @@ impl<'hir> LoweringContext<'_, 'hir> { match ext { Extern::None => abi::Abi::Rust, Extern::Implicit => { - self.maybe_lint_missing_abi(span, id, abi::Abi::C); - abi::Abi::C + self.maybe_lint_missing_abi(span, id, abi::Abi::C { unwind: false }); + abi::Abi::C { unwind: false } } Extern::Explicit(abi) => self.lower_abi(abi), } diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs index c564ada0395e9..44056df4ab911 100644 --- a/compiler/rustc_ast_lowering/src/lib.rs +++ b/compiler/rustc_ast_lowering/src/lib.rs @@ -12,7 +12,7 @@ //! For the simpler lowering steps, IDs and spans should be preserved. Unlike //! expansion we do not preserve the process of lowering in the spans, so spans //! should not be modified here. When creating a new node (as opposed to -//! 'folding' an existing one), then you create a new ID using `next_id()`. +//! "folding" an existing one), create a new ID using `next_id()`. //! //! You must ensure that IDs are unique. That means that you should only use the //! ID from an AST node in a single HIR node (you can assume that AST node-IDs @@ -26,18 +26,19 @@ //! span and spans don't need to be kept in order, etc. Where code is preserved //! by lowering, it should have the same span as in the AST. Where HIR nodes are //! new it is probably best to give a span for the whole AST node being lowered. -//! All nodes should have real spans, don't use dummy spans. Tools are likely to +//! All nodes should have real spans; don't use dummy spans. Tools are likely to //! get confused if the spans from leaf AST nodes occur in multiple places //! in the HIR, especially for multiple identifiers. #![feature(crate_visibility_modifier)] -#![feature(or_patterns)] +#![cfg_attr(bootstrap, feature(or_patterns))] #![feature(box_patterns)] +#![feature(iter_zip)] #![recursion_limit = "256"] use rustc_ast::node_id::NodeMap; -use rustc_ast::token::{self, DelimToken, Nonterminal, Token}; -use rustc_ast::tokenstream::{CanSynthesizeMissingTokens, DelimSpan, TokenStream, TokenTree}; +use rustc_ast::token::{self, Token}; +use rustc_ast::tokenstream::{CanSynthesizeMissingTokens, TokenStream, TokenTree}; use rustc_ast::visit::{self, AssocCtxt, Visitor}; use rustc_ast::walk_list; use rustc_ast::{self as ast, *}; @@ -55,7 +56,7 @@ use rustc_hir::{ConstArg, GenericArg, ParamName}; use rustc_index::vec::{Idx, IndexVec}; use rustc_session::lint::builtin::{BARE_TRAIT_OBJECTS, MISSING_ABI}; use rustc_session::lint::{BuiltinLintDiagnostics, LintBuffer}; -use rustc_session::parse::ParseSess; +use rustc_session::utils::{FlattenNonterminals, NtToTokenstream}; use rustc_session::Session; use rustc_span::hygiene::ExpnId; use rustc_span::source_map::{respan, DesugaringKind}; @@ -92,10 +93,10 @@ struct LoweringContext<'a, 'hir: 'a> { /// HACK(Centril): there is a cyclic dependency between the parser and lowering /// if we don't have this function pointer. To avoid that dependency so that - /// librustc_middle is independent of the parser, we use dynamic dispatch here. + /// `rustc_middle` is independent of the parser, we use dynamic dispatch here. nt_to_tokenstream: NtToTokenstream, - /// Used to allocate HIR nodes + /// Used to allocate HIR nodes. arena: &'hir Arena<'hir>, /// The items being lowered are collected here. @@ -114,6 +115,8 @@ struct LoweringContext<'a, 'hir: 'a> { generator_kind: Option, + attrs: BTreeMap, + /// When inside an `async` context, this is the `HirId` of the /// `task_context` local bound to the resume argument of the generator. task_context: Option, @@ -128,7 +131,7 @@ struct LoweringContext<'a, 'hir: 'a> { is_in_trait_impl: bool, is_in_dyn_type: bool, - /// What to do when we encounter either an "anonymous lifetime + /// What to do when we encounter an "anonymous lifetime /// reference". The term "anonymous" is meant to encompass both /// `'_` lifetimes as well as fully elided cases where nothing is /// written at all (e.g., `&T` or `std::cell::Ref`). @@ -210,8 +213,6 @@ pub trait ResolverAstLowering { ) -> LocalDefId; } -type NtToTokenstream = fn(&Nonterminal, &ParseSess, CanSynthesizeMissingTokens) -> TokenStream; - /// Context of `impl Trait` in code, which determines whether it is allowed in an HIR subtree, /// and if so, what meaning it has. #[derive(Debug)] @@ -221,7 +222,7 @@ enum ImplTraitContext<'b, 'a> { /// equivalent to a fresh universal parameter like `fn foo(x: T)`. /// /// Newly generated parameters should be inserted into the given `Vec`. - Universal(&'b mut Vec>), + Universal(&'b mut Vec>, LocalDefId), /// Treat `impl Trait` as shorthand for a new opaque type. /// Example: `fn foo() -> impl Debug`, where `impl Debug` is conceptually @@ -238,11 +239,13 @@ enum ImplTraitContext<'b, 'a> { OtherOpaqueTy { /// Set of lifetimes that this opaque type can capture, if it uses /// them. This includes lifetimes bound since we entered this context. - /// For example, in + /// For example: /// + /// ``` /// type A<'b> = impl for<'a> Trait<'a, Out = impl Sized + 'a>; + /// ``` /// - /// the inner opaque type captures `'a` because it uses it. It doesn't + /// Here the inner opaque type captures `'a` because it uses it. It doesn't /// need to capture `'b` because it already inherits the lifetime /// parameter from `A`. // FIXME(impl_trait): but `required_region_bounds` will ICE later @@ -274,7 +277,7 @@ impl<'a> ImplTraitContext<'_, 'a> { fn reborrow<'this>(&'this mut self) -> ImplTraitContext<'this, 'a> { use self::ImplTraitContext::*; match self { - Universal(params) => Universal(params), + Universal(params, parent) => Universal(params, *parent), ReturnPositionOpaqueTy { fn_def_id, origin } => { ReturnPositionOpaqueTy { fn_def_id: *fn_def_id, origin: *origin } } @@ -307,6 +310,7 @@ pub fn lower_crate<'a, 'hir>( bodies: BTreeMap::new(), trait_impls: BTreeMap::new(), modules: BTreeMap::new(), + attrs: BTreeMap::default(), exported_macros: Vec::new(), non_exported_macro_attrs: Vec::new(), catch_scopes: Vec::new(), @@ -397,67 +401,6 @@ enum AnonymousLifetimeMode { PassThrough, } -struct TokenStreamLowering<'a> { - parse_sess: &'a ParseSess, - synthesize_tokens: CanSynthesizeMissingTokens, - nt_to_tokenstream: NtToTokenstream, -} - -impl<'a> TokenStreamLowering<'a> { - fn lower_token_stream(&mut self, tokens: TokenStream) -> TokenStream { - tokens.into_trees().flat_map(|tree| self.lower_token_tree(tree).into_trees()).collect() - } - - fn lower_token_tree(&mut self, tree: TokenTree) -> TokenStream { - match tree { - TokenTree::Token(token) => self.lower_token(token), - TokenTree::Delimited(span, delim, tts) => { - TokenTree::Delimited(span, delim, self.lower_token_stream(tts)).into() - } - } - } - - fn lower_token(&mut self, token: Token) -> TokenStream { - match token.kind { - token::Interpolated(nt) => { - let tts = (self.nt_to_tokenstream)(&nt, self.parse_sess, self.synthesize_tokens); - TokenTree::Delimited( - DelimSpan::from_single(token.span), - DelimToken::NoDelim, - self.lower_token_stream(tts), - ) - .into() - } - _ => TokenTree::Token(token).into(), - } - } -} - -struct ImplTraitTypeIdVisitor<'a> { - ids: &'a mut SmallVec<[NodeId; 1]>, -} - -impl Visitor<'_> for ImplTraitTypeIdVisitor<'_> { - fn visit_ty(&mut self, ty: &Ty) { - match ty.kind { - TyKind::Typeof(_) | TyKind::BareFn(_) => return, - - TyKind::ImplTrait(id, _) => self.ids.push(id), - _ => {} - } - visit::walk_ty(self, ty); - } - - fn visit_path_segment(&mut self, path_span: Span, path_segment: &PathSegment) { - if let Some(ref p) = path_segment.args { - if let GenericArgs::Parenthesized(_) = **p { - return; - } - } - visit::walk_path_segment(self, path_span, path_segment) - } -} - impl<'a, 'hir> LoweringContext<'a, 'hir> { fn lower_crate(mut self, c: &Crate) -> hir::Crate<'hir> { /// Full-crate AST visitor that inserts into a fresh @@ -470,25 +413,18 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { } impl MiscCollector<'_, '_, '_> { - fn allocate_use_tree_hir_id_counters(&mut self, tree: &UseTree, owner: LocalDefId) { + fn allocate_use_tree_hir_id_counters(&mut self, tree: &UseTree) { match tree.kind { UseTreeKind::Simple(_, id1, id2) => { for &id in &[id1, id2] { - self.lctx.resolver.create_def( - owner, - id, - DefPathData::Misc, - ExpnId::root(), - tree.prefix.span, - ); self.lctx.allocate_hir_id_counter(id); } } UseTreeKind::Glob => (), UseTreeKind::Nested(ref trees) => { for &(ref use_tree, id) in trees { - let hir_id = self.lctx.allocate_hir_id_counter(id); - self.allocate_use_tree_hir_id_counters(use_tree, hir_id.owner); + self.lctx.allocate_hir_id_counter(id); + self.allocate_use_tree_hir_id_counters(use_tree); } } } @@ -497,7 +433,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { impl<'tcx> Visitor<'tcx> for MiscCollector<'tcx, '_, '_> { fn visit_item(&mut self, item: &'tcx Item) { - let hir_id = self.lctx.allocate_hir_id_counter(item.id); + self.lctx.allocate_hir_id_counter(item.id); match item.kind { ItemKind::Struct(_, ref generics) @@ -516,7 +452,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { self.lctx.type_def_lifetime_params.insert(def_id.to_def_id(), count); } ItemKind::Use(ref use_tree) => { - self.allocate_use_tree_hir_id_counters(use_tree, hir_id.owner); + self.allocate_use_tree_hir_id_counters(use_tree); } _ => {} } @@ -547,10 +483,6 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { } self.visit_fn_ret_ty(&f.decl.output) } - TyKind::ImplTrait(def_node_id, _) => { - self.lctx.allocate_hir_id_counter(def_node_id); - visit::walk_ty(self, t); - } _ => visit::walk_ty(self, t), } } @@ -563,7 +495,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { visit::walk_crate(&mut item::ItemLowerer { lctx: &mut self }, c); let module = self.lower_mod(&c.items, c.span); - let attrs = self.lower_attrs(&c.attrs); + self.lower_attrs(hir::CRATE_HIR_ID, &c.attrs); let body_ids = body_ids(&self.bodies); let proc_macros = c.proc_macros.iter().map(|id| self.node_id_to_hir_id[*id].unwrap()).collect(); @@ -590,8 +522,16 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { self.resolver.definitions().init_def_id_to_hir_id_mapping(def_id_to_hir_id); + #[cfg(debug_assertions)] + for (&id, attrs) in self.attrs.iter() { + // Verify that we do not store empty slices in the map. + if attrs.is_empty() { + panic!("Stored empty attributes for {:?}", id); + } + } + hir::Crate { - item: hir::CrateItem { module, attrs, span: c.span }, + item: module, exported_macros: self.arena.alloc_from_iter(self.exported_macros), non_exported_macro_attrs: self.arena.alloc_from_iter(self.non_exported_macro_attrs), items: self.items, @@ -604,6 +544,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { modules: self.modules, proc_macros, trait_map, + attrs: self.attrs, } } @@ -832,7 +773,6 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { hir::GenericParam { hir_id: self.lower_node_id(node_id), name: hir_name, - attrs: &[], bounds: &[], span, pure_wrt_drop: false, @@ -926,8 +866,13 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { // `lifetimes_to_define`. If we swapped the order of these two, // in-band-lifetimes introduced by generics or where-clauses // wouldn't have been added yet. - let generics = - this.lower_generics_mut(generics, ImplTraitContext::Universal(&mut params)); + let generics = this.lower_generics_mut( + generics, + ImplTraitContext::Universal( + &mut params, + this.current_hir_id_owner.last().unwrap().0, + ), + ); let res = f(this, &mut params); (params, (generics, res)) }) @@ -965,11 +910,18 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { ret } - fn lower_attrs(&mut self, attrs: &[Attribute]) -> &'hir [Attribute] { - self.arena.alloc_from_iter(attrs.iter().map(|a| self.lower_attr(a))) + fn lower_attrs(&mut self, id: hir::HirId, attrs: &[Attribute]) -> Option<&'hir [Attribute]> { + if attrs.is_empty() { + None + } else { + let ret = self.arena.alloc_from_iter(attrs.iter().map(|a| self.lower_attr(a))); + debug_assert!(!ret.is_empty()); + self.attrs.insert(id, ret); + Some(ret) + } } - fn lower_attr(&mut self, attr: &Attribute) -> Attribute { + fn lower_attr(&self, attr: &Attribute) -> Attribute { // Note that we explicitly do not walk the path. Since we don't really // lower attributes (we use the AST version) there is nowhere to keep // the `HirId`s. We don't actually need HIR version of attributes anyway. @@ -989,7 +941,14 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { Attribute { kind, id: attr.id, style: attr.style, span: attr.span } } - fn lower_mac_args(&mut self, args: &MacArgs) -> MacArgs { + fn alias_attrs(&mut self, id: hir::HirId, target_id: hir::HirId) { + if let Some(&a) = self.attrs.get(&target_id) { + debug_assert!(!a.is_empty()); + self.attrs.insert(id, a); + } + } + + fn lower_mac_args(&self, args: &MacArgs) -> MacArgs { match *args { MacArgs::Empty => MacArgs::Empty, MacArgs::Delimited(dspan, delim, ref tokens) => { @@ -1040,12 +999,12 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { } } - let tokens = TokenStreamLowering { + let tokens = FlattenNonterminals { parse_sess: &self.sess.parse_sess, synthesize_tokens: CanSynthesizeMissingTokens::Yes, nt_to_tokenstream: self.nt_to_tokenstream, } - .lower_token(token.clone()); + .process_token(token.clone()); MacArgs::Eq(eq_span, unwrap_single_token(self.sess, tokens, token.span)) } } @@ -1056,12 +1015,12 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { tokens: TokenStream, synthesize_tokens: CanSynthesizeMissingTokens, ) -> TokenStream { - TokenStreamLowering { + FlattenNonterminals { parse_sess: &self.sess.parse_sess, synthesize_tokens, nt_to_tokenstream: self.nt_to_tokenstream, } - .lower_token_stream(tokens) + .process_token_stream(tokens) } /// Given an associated type constraint like one of these: @@ -1118,6 +1077,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { } AssocTyConstraintKind::Bound { ref bounds } => { let mut capturable_lifetimes; + let mut parent_def_id = self.current_hir_id_owner.last().unwrap().0; // Piggy-back on the `impl Trait` context to figure out the correct behavior. let (desugar_to_impl_trait, itctx) = match itctx { // We are in the return position: @@ -1137,7 +1097,10 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { // so desugar to // // fn foo(x: dyn Iterator) - ImplTraitContext::Universal(..) if self.is_in_dyn_type => (true, itctx), + ImplTraitContext::Universal(_, parent) if self.is_in_dyn_type => { + parent_def_id = parent; + (true, itctx) + } // In `type Foo = dyn Iterator` we desugar to // `type Foo = dyn Iterator` but we have to override the @@ -1171,7 +1134,6 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { // constructing the HIR for `impl bounds...` and then lowering that. let impl_trait_node_id = self.resolver.next_node_id(); - let parent_def_id = self.current_hir_id_owner.last().unwrap().0; self.resolver.create_def( parent_def_id, impl_trait_node_id, @@ -1393,7 +1355,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { if kind != TraitObjectSyntax::Dyn { self.maybe_lint_bare_trait(t.span, t.id, false); } - hir::TyKind::TraitObject(bounds, lifetime_bound) + hir::TyKind::TraitObject(bounds, lifetime_bound, kind) } TyKind::ImplTrait(def_node_id, ref bounds) => { let span = t.span; @@ -1424,25 +1386,20 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { |this| this.lower_param_bounds(bounds, nested_itctx), ) } - ImplTraitContext::Universal(in_band_ty_params) => { + ImplTraitContext::Universal(in_band_ty_params, parent_def_id) => { // Add a definition for the in-band `Param`. let def_id = self.resolver.local_def_id(def_node_id); - self.allocate_hir_id_counter(def_node_id); - - let hir_bounds = self.with_hir_id_owner(def_node_id, |this| { - this.lower_param_bounds( - bounds, - ImplTraitContext::Universal(in_band_ty_params), - ) - }); + let hir_bounds = self.lower_param_bounds( + bounds, + ImplTraitContext::Universal(in_band_ty_params, parent_def_id), + ); // Set the name to `impl Bound1 + Bound2`. let ident = Ident::from_str_and_span(&pprust::ty_to_string(t), span); in_band_ty_params.push(hir::GenericParam { hir_id: self.lower_node_id(def_node_id), name: ParamName::Plain(ident), pure_wrt_drop: false, - attrs: &[], bounds: hir_bounds, span, kind: hir::GenericParamKind::Type { @@ -1570,7 +1527,6 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { let opaque_ty_item = hir::Item { def_id: opaque_ty_id, ident: Ident::invalid(), - attrs: Default::default(), kind: opaque_ty_item_kind, vis: respan(span.shrink_to_lo(), hir::VisibilityKind::Inherited), span: opaque_ty_span, @@ -1731,7 +1687,6 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { name, span: lifetime.span, pure_wrt_drop: false, - attrs: &[], bounds: &[], kind: hir::GenericParamKind::Lifetime { kind }, }); @@ -1764,14 +1719,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { ) } - fn lower_local(&mut self, l: &Local) -> (hir::Local<'hir>, SmallVec<[NodeId; 1]>) { - let mut ids = SmallVec::<[NodeId; 1]>::new(); - if self.sess.features_untracked().impl_trait_in_bindings { - if let Some(ref ty) = l.ty { - let mut visitor = ImplTraitTypeIdVisitor { ids: &mut ids }; - visitor.visit_ty(ty); - } - } + fn lower_local(&mut self, l: &Local) -> hir::Local<'hir> { let ty = l.ty.as_ref().map(|t| { let mut capturable_lifetimes; self.lower_ty( @@ -1788,18 +1736,16 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { ) }); let init = l.init.as_ref().map(|e| self.lower_expr(e)); - ( - hir::Local { - hir_id: self.lower_node_id(l.id), - ty, - pat: self.lower_pat(&l.pat), - init, - span: l.span, - attrs: l.attrs.iter().map(|a| self.lower_attr(a)).collect::>().into(), - source: hir::LocalSource::Normal, - }, - ids, - ) + let hir_id = self.lower_node_id(l.id); + self.lower_attrs(hir_id, &l.attrs); + hir::Local { + hir_id, + ty, + pat: self.lower_pat(&l.pat), + init, + span: l.span, + source: hir::LocalSource::Normal, + } } fn lower_fn_params_to_names(&mut self, decl: &FnDecl) -> &'hir [Ident] { @@ -1866,7 +1812,13 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { } this.arena.alloc_from_iter(inputs.iter().map(|param| { if let Some((_, ibty)) = &mut in_band_ty_params { - this.lower_ty_direct(¶m.ty, ImplTraitContext::Universal(ibty)) + this.lower_ty_direct( + ¶m.ty, + ImplTraitContext::Universal( + ibty, + this.current_hir_id_owner.last().unwrap().0, + ), + ) } else { this.lower_ty_direct(¶m.ty, ImplTraitContext::disallowed()) } @@ -2110,7 +2062,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { hir::FnRetTy::Return(self.arena.alloc(opaque_ty)) } - /// Transforms `-> T` into `Future` + /// Transforms `-> T` into `Future`. fn lower_async_fn_output_type_to_future_bound( &mut self, output: &FnRetTy, @@ -2269,13 +2221,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { let kind = hir::GenericParamKind::Type { default: default.as_ref().map(|x| { - self.lower_ty( - x, - ImplTraitContext::OtherOpaqueTy { - capturable_lifetimes: &mut FxHashSet::default(), - origin: hir::OpaqueTyOrigin::Misc, - }, - ) + self.lower_ty(x, ImplTraitContext::Disallowed(ImplTraitPosition::Other)) }), synthetic: param .attrs @@ -2293,17 +2239,17 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { this.lower_ty(&ty, ImplTraitContext::disallowed()) }); let default = default.as_ref().map(|def| self.lower_anon_const(def)); - (hir::ParamName::Plain(param.ident), hir::GenericParamKind::Const { ty, default }) } }; + let hir_id = self.lower_node_id(param.id); + self.lower_attrs(hir_id, ¶m.attrs); hir::GenericParam { - hir_id: self.lower_node_id(param.id), + hir_id, name, span: param.ident.span, pure_wrt_drop: self.sess.contains_name(¶m.attrs, sym::may_dangle), - attrs: self.lower_attrs(¶m.attrs), bounds: self.arena.alloc_from_iter(bounds), kind, } @@ -2383,26 +2329,12 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { } fn lower_block_noalloc(&mut self, b: &Block, targeted_by_break: bool) -> hir::Block<'hir> { - let mut expr: Option<&'hir _> = None; - - let stmts = self.arena.alloc_from_iter( - b.stmts - .iter() - .enumerate() - .filter_map(|(index, stmt)| { - if index == b.stmts.len() - 1 { - if let StmtKind::Expr(ref e) = stmt.kind { - expr = Some(self.lower_expr(e)); - None - } else { - Some(self.lower_stmt(stmt)) - } - } else { - Some(self.lower_stmt(stmt)) - } - }) - .flatten(), - ); + let (stmts, expr) = match &*b.stmts { + [stmts @ .., Stmt { kind: StmtKind::Expr(e), .. }] => (stmts, Some(&*e)), + stmts => (stmts, None), + }; + let stmts = self.arena.alloc_from_iter(stmts.iter().flat_map(|stmt| self.lower_stmt(stmt))); + let expr = expr.map(|e| self.lower_expr(e)); let rules = self.lower_block_check_mode(&b.rules); let hir_id = self.lower_node_id(b.id); @@ -2424,27 +2356,16 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { } fn lower_stmt(&mut self, s: &Stmt) -> SmallVec<[hir::Stmt<'hir>; 1]> { - let kind = match s.kind { + let (hir_id, kind) = match s.kind { StmtKind::Local(ref l) => { - let (l, item_ids) = self.lower_local(l); - let mut ids: SmallVec<[hir::Stmt<'hir>; 1]> = item_ids - .into_iter() - .map(|item_id| { - let item_id = hir::ItemId { - // All the items that `lower_local` finds are `impl Trait` types. - def_id: self.lower_node_id(item_id).expect_owner(), - }; - self.stmt(s.span, hir::StmtKind::Item(item_id)) - }) - .collect(); - ids.push({ - hir::Stmt { - hir_id: self.lower_node_id(s.id), - kind: hir::StmtKind::Local(self.arena.alloc(l)), - span: s.span, - } - }); - return ids; + let l = self.lower_local(l); + let hir_id = self.lower_node_id(s.id); + self.alias_attrs(hir_id, l.hir_id); + return smallvec![hir::Stmt { + hir_id, + kind: hir::StmtKind::Local(self.arena.alloc(l)), + span: s.span, + }]; } StmtKind::Item(ref it) => { // Can only use the ID once. @@ -2462,12 +2383,22 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { }) .collect(); } - StmtKind::Expr(ref e) => hir::StmtKind::Expr(self.lower_expr(e)), - StmtKind::Semi(ref e) => hir::StmtKind::Semi(self.lower_expr(e)), + StmtKind::Expr(ref e) => { + let e = self.lower_expr(e); + let hir_id = self.lower_node_id(s.id); + self.alias_attrs(hir_id, e.hir_id); + (hir_id, hir::StmtKind::Expr(e)) + } + StmtKind::Semi(ref e) => { + let e = self.lower_expr(e); + let hir_id = self.lower_node_id(s.id); + self.alias_attrs(hir_id, e.hir_id); + (hir_id, hir::StmtKind::Semi(e)) + } StmtKind::Empty => return smallvec![], StmtKind::MacCall(..) => panic!("shouldn't exist here"), }; - smallvec![hir::Stmt { hir_id: self.lower_node_id(s.id), kind, span: s.span }] + smallvec![hir::Stmt { hir_id, kind, span: s.span }] } fn lower_block_check_mode(&mut self, b: &BlockCheckMode) -> hir::BlockCheckMode { @@ -2511,13 +2442,18 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { fn stmt_let_pat( &mut self, - attrs: AttrVec, + attrs: Option<&'hir [Attribute]>, span: Span, init: Option<&'hir hir::Expr<'hir>>, pat: &'hir hir::Pat<'hir>, source: hir::LocalSource, ) -> hir::Stmt<'hir> { - let local = hir::Local { attrs, hir_id: self.next_id(), init, pat, source, span, ty: None }; + let hir_id = self.next_id(); + if let Some(a) = attrs { + debug_assert!(!a.is_empty()); + self.attrs.insert(hir_id, a); + } + let local = hir::Local { hir_id, init, pat, source, span, ty: None }; self.stmt(span, hir::StmtKind::Local(self.arena.alloc(local))) } @@ -2571,8 +2507,8 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { &mut self, span: Span, pat: &'hir hir::Pat<'hir>, - ) -> &'hir [hir::FieldPat<'hir>] { - let field = hir::FieldPat { + ) -> &'hir [hir::PatField<'hir>] { + let field = hir::PatField { hir_id: self.next_id(), ident: Ident::new(sym::integer(0), span), is_shorthand: false, @@ -2586,7 +2522,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { &mut self, span: Span, lang_item: hir::LangItem, - fields: &'hir [hir::FieldPat<'hir>], + fields: &'hir [hir::PatField<'hir>], ) -> &'hir hir::Pat<'hir> { let qpath = hir::QPath::LangItem(lang_item, span); self.pat(span, hir::PatKind::Struct(qpath, fields, false)) @@ -2660,6 +2596,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { hir::TyKind::TraitObject( arena_vec![self; principal], self.elided_dyn_bound(span), + TraitObjectSyntax::None, ) } _ => hir::TyKind::Path(hir::QPath::Resolved(None, path)), diff --git a/compiler/rustc_ast_lowering/src/pat.rs b/compiler/rustc_ast_lowering/src/pat.rs index e4e7b24d29e52..2451409aac88e 100644 --- a/compiler/rustc_ast_lowering/src/pat.rs +++ b/compiler/rustc_ast_lowering/src/pat.rs @@ -56,7 +56,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { ImplTraitContext::disallowed(), ); - let fs = self.arena.alloc_from_iter(fields.iter().map(|f| hir::FieldPat { + let fs = self.arena.alloc_from_iter(fields.iter().map(|f| hir::PatField { hir_id: self.next_id(), ident: f.ident, pat: self.lower_pat(&f.pat), diff --git a/compiler/rustc_ast_lowering/src/path.rs b/compiler/rustc_ast_lowering/src/path.rs index cb4d5ea6ee650..46dac2f1af4f4 100644 --- a/compiler/rustc_ast_lowering/src/path.rs +++ b/compiler/rustc_ast_lowering/src/path.rs @@ -30,6 +30,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { let partial_res = self.resolver.get_partial_res(id).unwrap_or_else(|| PartialRes::new(Res::Err)); + let path_span_lo = p.span.shrink_to_lo(); let proj_start = p.segments.len() - partial_res.unresolved_segments(); let path = self.arena.alloc(hir::Path { res: self.lower_res(partial_res.base_res()), @@ -108,7 +109,9 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { ) }, )), - span: p.span, + span: p.segments[..proj_start] + .last() + .map_or(path_span_lo, |segment| path_span_lo.to(segment.span())), }); // Simple case, either no projections, or only fully-qualified. @@ -127,7 +130,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { // e.g., `Vec` in `Vec::new` or `::Item` in // `::Item::default`. let new_id = self.next_id(); - self.arena.alloc(self.ty_path(new_id, p.span, hir::QPath::Resolved(qself, path))) + self.arena.alloc(self.ty_path(new_id, path.span, hir::QPath::Resolved(qself, path))) }; // Anything after the base path are associated "extensions", @@ -141,7 +144,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { // 3. `<>::IntoIter>::Item` // * final path is `<<>::IntoIter>::Item>::clone` for (i, segment) in p.segments.iter().enumerate().skip(proj_start) { - let segment = self.arena.alloc(self.lower_path_segment( + let hir_segment = self.arena.alloc(self.lower_path_segment( p.span, segment, param_mode, @@ -150,7 +153,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { itctx.reborrow(), None, )); - let qpath = hir::QPath::TypeRelative(ty, segment); + let qpath = hir::QPath::TypeRelative(ty, hir_segment); // It's finished, return the extension of the right node type. if i == p.segments.len() - 1 { @@ -159,7 +162,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { // Wrap the associated extension in another type node. let new_id = self.next_id(); - ty = self.arena.alloc(self.ty_path(new_id, p.span, qpath)); + ty = self.arena.alloc(self.ty_path(new_id, path_span_lo.to(segment.span()), qpath)); } // We should've returned in the for loop above. diff --git a/compiler/rustc_ast_passes/src/ast_validation.rs b/compiler/rustc_ast_passes/src/ast_validation.rs index 563bcda519065..8a3f76415968e 100644 --- a/compiler/rustc_ast_passes/src/ast_validation.rs +++ b/compiler/rustc_ast_passes/src/ast_validation.rs @@ -532,6 +532,25 @@ impl<'a> AstValidator<'a> { } } + /// An item in `extern { ... }` cannot use non-ascii identifier. + fn check_foreign_item_ascii_only(&self, ident: Ident) { + let symbol_str = ident.as_str(); + if !symbol_str.is_ascii() { + let n = 83942; + self.err_handler() + .struct_span_err( + ident.span, + "items in `extern` blocks cannot use non-ascii identifiers", + ) + .span_label(self.current_extern_span(), "in this `extern` block") + .note(&format!( + "This limitation may be lifted in the future; see issue #{} for more information", + n, n, + )) + .emit(); + } + } + /// Reject C-varadic type unless the function is foreign, /// or free and `unsafe extern "C"` semantically. fn check_c_varadic_type(&self, fk: FnKind<'a>) { @@ -592,7 +611,7 @@ impl<'a> AstValidator<'a> { self.session, ident.span, E0754, - "trying to load file for module `{}` with non ascii identifer name", + "trying to load file for module `{}` with non-ascii identifier name", ident.name ) .help("consider using `#[path]` attribute to specify filesystem path") @@ -1103,15 +1122,18 @@ impl<'a> Visitor<'a> for AstValidator<'a> { self.check_defaultness(fi.span, *def); self.check_foreign_fn_bodyless(fi.ident, body.as_deref()); self.check_foreign_fn_headerless(fi.ident, fi.span, sig.header); + self.check_foreign_item_ascii_only(fi.ident); } ForeignItemKind::TyAlias(box TyAliasKind(def, generics, bounds, body)) => { self.check_defaultness(fi.span, *def); self.check_foreign_kind_bodyless(fi.ident, "type", body.as_ref().map(|b| b.span)); self.check_type_no_bounds(bounds, "`extern` blocks"); self.check_foreign_ty_genericless(generics); + self.check_foreign_item_ascii_only(fi.ident); } ForeignItemKind::Static(_, _, body) => { self.check_foreign_kind_bodyless(fi.ident, "static", body.as_ref().map(|b| b.span)); + self.check_foreign_item_ascii_only(fi.ident); } ForeignItemKind::MacCall(..) => {} } @@ -1150,20 +1172,23 @@ impl<'a> Visitor<'a> for AstValidator<'a> { } fn visit_generics(&mut self, generics: &'a Generics) { - let mut prev_ty_default = None; + let cg_defaults = self.session.features_untracked().const_generics_defaults; + + let mut prev_param_default = None; for param in &generics.params { match param.kind { GenericParamKind::Lifetime => (), - GenericParamKind::Type { default: Some(_), .. } => { - prev_ty_default = Some(param.ident.span); + GenericParamKind::Type { default: Some(_), .. } + | GenericParamKind::Const { default: Some(_), .. } => { + prev_param_default = Some(param.ident.span); } GenericParamKind::Type { .. } | GenericParamKind::Const { .. } => { - if let Some(span) = prev_ty_default { + if let Some(span) = prev_param_default { let mut err = self.err_handler().struct_span_err( span, - "type parameters with a default must be trailing", + "generic parameters with a default must be trailing", ); - if matches!(param.kind, GenericParamKind::Const { .. }) { + if matches!(param.kind, GenericParamKind::Const { .. }) && !cg_defaults { err.note( "using type defaults and const parameters \ in the same parameter list is currently not permitted", @@ -1188,8 +1213,41 @@ impl<'a> Visitor<'a> for AstValidator<'a> { deny_equality_constraints(self, predicate, generics); } } - - visit::walk_generics(self, generics) + walk_list!(self, visit_generic_param, &generics.params); + for predicate in &generics.where_clause.predicates { + match predicate { + WherePredicate::BoundPredicate(bound_pred) => { + // A type binding, eg `for<'c> Foo: Send+Clone+'c` + self.check_late_bound_lifetime_defs(&bound_pred.bound_generic_params); + + // This is slightly complicated. Our representation for poly-trait-refs contains a single + // binder and thus we only allow a single level of quantification. However, + // the syntax of Rust permits quantification in two places in where clauses, + // e.g., `T: for <'a> Foo<'a>` and `for <'a, 'b> &'b T: Foo<'a>`. If both are + // defined, then error. + if !bound_pred.bound_generic_params.is_empty() { + for bound in &bound_pred.bounds { + match bound { + GenericBound::Trait(t, _) => { + if !t.bound_generic_params.is_empty() { + struct_span_err!( + self.err_handler(), + t.span, + E0316, + "nested quantification of lifetimes" + ) + .emit(); + } + } + GenericBound::Outlives(_) => {} + } + } + } + } + _ => {} + } + self.visit_where_predicate(predicate); + } } fn visit_generic_param(&mut self, param: &'a GenericParam) { @@ -1238,14 +1296,6 @@ impl<'a> Visitor<'a> for AstValidator<'a> { visit::walk_pat(self, pat) } - fn visit_where_predicate(&mut self, p: &'a WherePredicate) { - if let &WherePredicate::BoundPredicate(ref bound_predicate) = p { - // A type binding, eg `for<'c> Foo: Send+Clone+'c` - self.check_late_bound_lifetime_defs(&bound_predicate.bound_generic_params); - } - visit::walk_where_predicate(self, p); - } - fn visit_poly_trait_ref(&mut self, t: &'a PolyTraitRef, m: &'a TraitBoundModifier) { self.check_late_bound_lifetime_defs(&t.bound_generic_params); visit::walk_poly_trait_ref(self, t, m); diff --git a/compiler/rustc_ast_passes/src/feature_gate.rs b/compiler/rustc_ast_passes/src/feature_gate.rs index 474ec2b589b71..0679ccabe726c 100644 --- a/compiler/rustc_ast_passes/src/feature_gate.rs +++ b/compiler/rustc_ast_passes/src/feature_gate.rs @@ -8,7 +8,7 @@ use rustc_feature::{Features, GateIssue}; use rustc_session::parse::{feature_err, feature_err_issue}; use rustc_session::Session; use rustc_span::source_map::Spanned; -use rustc_span::symbol::{sym, Symbol}; +use rustc_span::symbol::sym; use rustc_span::Span; use tracing::debug; @@ -164,6 +164,46 @@ impl<'a> PostExpansionVisitor<'a> { "C-cmse-nonsecure-call ABI is experimental and subject to change" ); } + "C-unwind" => { + gate_feature_post!( + &self, + c_unwind, + span, + "C-unwind ABI is experimental and subject to change" + ); + } + "stdcall-unwind" => { + gate_feature_post!( + &self, + c_unwind, + span, + "stdcall-unwind ABI is experimental and subject to change" + ); + } + "system-unwind" => { + gate_feature_post!( + &self, + c_unwind, + span, + "system-unwind ABI is experimental and subject to change" + ); + } + "thiscall-unwind" => { + gate_feature_post!( + &self, + c_unwind, + span, + "thiscall-unwind ABI is experimental and subject to change" + ); + } + "wasm" => { + gate_feature_post!( + &self, + wasm_abi, + span, + "wasm ABI is experimental and subject to change" + ); + } abi => self .sess .parse_sess @@ -247,7 +287,7 @@ impl<'a> PostExpansionVisitor<'a> { if let ast::TyKind::ImplTrait(..) = ty.kind { gate_feature_post!( &self.vis, - type_alias_impl_trait, + min_type_alias_impl_trait, ty.span, "`impl Trait` in type aliases is unstable" ); @@ -281,24 +321,13 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> { include => external_doc cfg => doc_cfg masked => doc_masked - spotlight => doc_spotlight + notable_trait => doc_notable_trait keyword => doc_keyword ); } } } - fn visit_name(&mut self, sp: Span, name: Symbol) { - if !name.as_str().is_ascii() { - gate_feature_post!( - &self, - non_ascii_idents, - self.sess.parse_sess.source_map().guess_head_span(sp), - "non-ascii idents are not fully supported" - ); - } - } - fn visit_item(&mut self, i: &'a ast::Item) { match i.kind { ast::ItemKind::ForeignMod(ref foreign_module) => { @@ -326,16 +355,6 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> { over time" ); } - if self.sess.contains_name(&i.attrs[..], sym::main) { - gate_feature_post!( - &self, - main, - i.span, - "declaration of a non-standard `#[main]` \ - function may change over time, for now \ - a top-level `fn main()` is required" - ); - } } ast::ItemKind::Struct(..) => { @@ -638,15 +657,22 @@ pub fn check_crate(krate: &ast::Crate, sess: &Session) { } }; } - gate_all!(if_let_guard, "`if let` guards are experimental"); - gate_all!(let_chains, "`let` expressions in this position are experimental"); + gate_all!( + if_let_guard, + "`if let` guards are experimental", + "you can write `if matches!(, )` instead of `if let = `" + ); + gate_all!( + let_chains, + "`let` expressions in this position are experimental", + "you can write `matches!(, )` instead of `let = `" + ); gate_all!( async_closure, "async closures are unstable", "to use an async block, remove the `||`: `async {`" ); gate_all!(generators, "yield syntax is experimental"); - gate_all!(or_patterns, "or-patterns syntax is experimental"); gate_all!(raw_ref_op, "raw address of syntax is experimental"); gate_all!(const_trait_bound_opt_out, "`?const` on trait bounds is experimental"); gate_all!(const_trait_impl, "const trait impls are experimental"); @@ -701,16 +727,46 @@ pub fn check_crate(krate: &ast::Crate, sess: &Session) { } fn maybe_stage_features(sess: &Session, krate: &ast::Crate) { + use rustc_errors::Applicability; + if !sess.opts.unstable_features.is_nightly_build() { + let lang_features = &sess.features_untracked().declared_lang_features; for attr in krate.attrs.iter().filter(|attr| sess.check_name(attr, sym::feature)) { - struct_span_err!( + let mut err = struct_span_err!( sess.parse_sess.span_diagnostic, attr.span, E0554, "`#![feature]` may not be used on the {} release channel", option_env!("CFG_RELEASE_CHANNEL").unwrap_or("(unknown)") - ) - .emit(); + ); + let mut all_stable = true; + for ident in + attr.meta_item_list().into_iter().flatten().map(|nested| nested.ident()).flatten() + { + let name = ident.name; + let stable_since = lang_features + .iter() + .flat_map(|&(feature, _, since)| if feature == name { since } else { None }) + .next(); + if let Some(since) = stable_since { + err.help(&format!( + "the feature `{}` has been stable since {} and no longer requires \ + an attribute to enable", + name, since + )); + } else { + all_stable = false; + } + } + if all_stable { + err.span_suggestion( + attr.span, + "remove the attribute", + String::new(), + Applicability::MachineApplicable, + ); + } + err.emit(); } } } diff --git a/compiler/rustc_ast_passes/src/node_count.rs b/compiler/rustc_ast_passes/src/node_count.rs index fb7e0d3450fde..3980e6da68250 100644 --- a/compiler/rustc_ast_passes/src/node_count.rs +++ b/compiler/rustc_ast_passes/src/node_count.rs @@ -88,9 +88,9 @@ impl<'ast> Visitor<'ast> for NodeCounter { self.count += 1; walk_struct_def(self, s) } - fn visit_struct_field(&mut self, s: &StructField) { + fn visit_field_def(&mut self, s: &FieldDef) { self.count += 1; - walk_struct_field(self, s) + walk_field_def(self, s) } fn visit_enum_def( &mut self, diff --git a/compiler/rustc_ast_pretty/src/lib.rs b/compiler/rustc_ast_pretty/src/lib.rs index d869baad012ed..67b66284f66f0 100644 --- a/compiler/rustc_ast_pretty/src/lib.rs +++ b/compiler/rustc_ast_pretty/src/lib.rs @@ -1,6 +1,6 @@ #![feature(bool_to_option)] #![feature(crate_visibility_modifier)] -#![feature(or_patterns)] +#![cfg_attr(bootstrap, feature(or_patterns))] #![feature(box_patterns)] #![recursion_limit = "256"] diff --git a/compiler/rustc_ast_pretty/src/pprust/mod.rs b/compiler/rustc_ast_pretty/src/pprust/mod.rs index b88699f6ee176..976725b308e02 100644 --- a/compiler/rustc_ast_pretty/src/pprust/mod.rs +++ b/compiler/rustc_ast_pretty/src/pprust/mod.rs @@ -22,10 +22,6 @@ pub fn token_to_string(token: &Token) -> String { State::new().token_to_string(token) } -pub fn token_to_string_ext(token: &Token, convert_dollar_crate: bool) -> String { - State::new().token_to_string_ext(token, convert_dollar_crate) -} - pub fn ty_to_string(ty: &ast::Ty) -> String { State::new().ty_to_string(ty) } @@ -50,18 +46,10 @@ pub fn tts_to_string(tokens: &TokenStream) -> String { State::new().tts_to_string(tokens) } -pub fn stmt_to_string(stmt: &ast::Stmt) -> String { - State::new().stmt_to_string(stmt) -} - pub fn item_to_string(i: &ast::Item) -> String { State::new().item_to_string(i) } -pub fn generic_params_to_string(generic_params: &[ast::GenericParam]) -> String { - State::new().generic_params_to_string(generic_params) -} - pub fn path_to_string(p: &ast::Path) -> String { State::new().path_to_string(p) } @@ -74,26 +62,14 @@ pub fn vis_to_string(v: &ast::Visibility) -> String { State::new().vis_to_string(v) } -pub fn block_to_string(blk: &ast::Block) -> String { - State::new().block_to_string(blk) -} - pub fn meta_list_item_to_string(li: &ast::NestedMetaItem) -> String { State::new().meta_list_item_to_string(li) } -pub fn attr_item_to_string(ai: &ast::AttrItem) -> String { - State::new().attr_item_to_string(ai) -} - pub fn attribute_to_string(attr: &ast::Attribute) -> String { State::new().attribute_to_string(attr) } -pub fn param_to_string(arg: &ast::Param) -> String { - State::new().param_to_string(arg) -} - pub fn to_string(f: impl FnOnce(&mut State<'_>)) -> String { State::new().to_string(f) } diff --git a/compiler/rustc_ast_pretty/src/pprust/state.rs b/compiler/rustc_ast_pretty/src/pprust/state.rs index 82f6e936b766e..789d2c296e291 100644 --- a/compiler/rustc_ast_pretty/src/pprust/state.rs +++ b/compiler/rustc_ast_pretty/src/pprust/state.rs @@ -1711,7 +1711,7 @@ impl<'a> State<'a> { fn print_expr_struct( &mut self, path: &ast::Path, - fields: &[ast::Field], + fields: &[ast::ExprField], rest: &ast::StructRest, attrs: &[ast::Attribute], ) { @@ -1873,8 +1873,8 @@ impl<'a> State<'a> { ast::ExprKind::Repeat(ref element, ref count) => { self.print_expr_repeat(element, count, attrs); } - ast::ExprKind::Struct(ref path, ref fields, ref rest) => { - self.print_expr_struct(path, &fields[..], rest, attrs); + ast::ExprKind::Struct(ref se) => { + self.print_expr_struct(&se.path, &se.fields, &se.rest, attrs); } ast::ExprKind::Tup(ref exprs) => { self.print_expr_tup(&exprs[..], attrs); @@ -2149,10 +2149,10 @@ impl<'a> State<'a> { None => s.word("_"), } } - InlineAsmOperand::Const { expr } => { + InlineAsmOperand::Const { anon_const } => { s.word("const"); s.space(); - s.print_expr(expr); + s.print_expr(&anon_const.value); } InlineAsmOperand::Sym { expr } => { s.word("sym"); @@ -2292,10 +2292,6 @@ impl<'a> State<'a> { } } - pub fn print_usize(&mut self, i: usize) { - self.s.word(i.to_string()) - } - crate fn print_name(&mut self, name: Symbol) { self.s.word(name.to_string()); self.ann.post(self, AnnNode::Name(&name)) @@ -2659,8 +2655,10 @@ impl<'a> State<'a> { s.word_space(":"); s.print_type(ty); s.print_type_bounds(":", ¶m.bounds); - if let Some(ref _default) = default { - // FIXME(const_generics_defaults): print the `default` value here + if let Some(ref default) = default { + s.s.space(); + s.word_space("="); + s.print_expr(&default.value); } } } diff --git a/compiler/rustc_attr/src/builtin.rs b/compiler/rustc_attr/src/builtin.rs index e58b266fdb9e1..20971ebb95748 100644 --- a/compiler/rustc_attr/src/builtin.rs +++ b/compiler/rustc_attr/src/builtin.rs @@ -862,18 +862,6 @@ pub fn find_repr_attrs(sess: &Session, attr: &Attribute) -> Vec { if let Some(items) = attr.meta_item_list() { sess.mark_attr_used(attr); for item in items { - if !item.is_meta_item() { - handle_errors( - &sess.parse_sess, - item.span(), - AttrError::UnsupportedLiteral( - "meta item in `repr` must be an identifier", - false, - ), - ); - continue; - } - let mut recognised = false; if item.is_word() { let hint = match item.name_or_empty() { @@ -890,23 +878,6 @@ pub fn find_repr_attrs(sess: &Session, attr: &Attribute) -> Vec { acc.push(h); } } else if let Some((name, value)) = item.name_value_literal() { - let parse_alignment = |node: &ast::LitKind| -> Result { - if let ast::LitKind::Int(literal, ast::LitIntType::Unsuffixed) = node { - if literal.is_power_of_two() { - // rustc_middle::ty::layout::Align restricts align to <= 2^29 - if *literal <= 1 << 29 { - Ok(*literal as u32) - } else { - Err("larger than 2^29") - } - } else { - Err("not a power of two") - } - } else { - Err("not an unsuffixed integer") - } - }; - let mut literal_error = None; if name == sym::align { recognised = true; @@ -966,13 +937,7 @@ pub fn find_repr_attrs(sess: &Session, attr: &Attribute) -> Vec { } if !recognised { // Not a word we recognize - struct_span_err!( - diagnostic, - item.span(), - E0552, - "unrecognized representation hint" - ) - .emit(); + diagnostic.delay_span_bug(item.span(), "unrecognized representation hint"); } } } @@ -1080,3 +1045,16 @@ fn allow_unstable<'a>( name }) } + +pub fn parse_alignment(node: &ast::LitKind) -> Result { + if let ast::LitKind::Int(literal, ast::LitIntType::Unsuffixed) = node { + if literal.is_power_of_two() { + // rustc_middle::ty::layout::Align restricts align to <= 2^29 + if *literal <= 1 << 29 { Ok(*literal as u32) } else { Err("larger than 2^29") } + } else { + Err("not a power of two") + } + } else { + Err("not an unsuffixed integer") + } +} diff --git a/compiler/rustc_attr/src/lib.rs b/compiler/rustc_attr/src/lib.rs index 149a950f7d417..ab68d24e4b339 100644 --- a/compiler/rustc_attr/src/lib.rs +++ b/compiler/rustc_attr/src/lib.rs @@ -4,7 +4,7 @@ //! The goal is to move the definition of `MetaItem` and things that don't need to be in `syntax` //! to this crate. -#![feature(or_patterns)] +#![cfg_attr(bootstrap, feature(or_patterns))] #[macro_use] extern crate rustc_macros; diff --git a/compiler/rustc_builtin_macros/src/asm.rs b/compiler/rustc_builtin_macros/src/asm.rs index 36cd6c281b42a..fd976b119b748 100644 --- a/compiler/rustc_builtin_macros/src/asm.rs +++ b/compiler/rustc_builtin_macros/src/asm.rs @@ -7,8 +7,10 @@ use rustc_errors::{Applicability, DiagnosticBuilder}; use rustc_expand::base::{self, *}; use rustc_parse::parser::Parser; use rustc_parse_format as parse; +use rustc_session::lint; use rustc_span::symbol::{kw, sym, Symbol}; use rustc_span::{InnerSpan, Span}; +use rustc_target::asm::InlineAsmArch; struct AsmArgs { templates: Vec>, @@ -134,8 +136,8 @@ fn parse_args<'a>( ast::InlineAsmOperand::InOut { reg, expr, late: true } } } else if p.eat_keyword(kw::Const) { - let expr = p.parse_expr()?; - ast::InlineAsmOperand::Const { expr } + let anon_const = p.parse_anon_const_expr()?; + ast::InlineAsmOperand::Const { anon_const } } else if p.eat_keyword(sym::sym) { let expr = p.parse_expr()?; match expr.kind { @@ -424,6 +426,40 @@ fn expand_preparsed_asm(ecx: &mut ExtCtxt<'_>, sp: Span, args: AsmArgs) -> P Span { + if let Some(snippet) = &template_snippet { + if let Some(pos) = snippet.find(needle) { + let end = pos + + &snippet[pos..] + .find(|c| matches!(c, '\n' | ';' | '\\' | '"')) + .unwrap_or(snippet[pos..].len() - 1); + let inner = InnerSpan::new(pos, end); + return template_sp.from_inner(inner); + } + } + template_sp + }; + + if template_str.contains(".intel_syntax") { + ecx.parse_sess().buffer_lint( + lint::builtin::BAD_ASM_STYLE, + find_span(".intel_syntax"), + ecx.resolver.lint_node_id(ecx.current_expansion.id), + "avoid using `.intel_syntax`, Intel syntax is the default", + ); + } + if template_str.contains(".att_syntax") { + ecx.parse_sess().buffer_lint( + lint::builtin::BAD_ASM_STYLE, + find_span(".att_syntax"), + ecx.resolver.lint_node_id(ecx.current_expansion.id), + "avoid using `.att_syntax`, prefer using `options(att_syntax)` instead", + ); + } + } + let mut parser = parse::Parser::new( template_str, str_style, diff --git a/compiler/rustc_builtin_macros/src/cfg_eval.rs b/compiler/rustc_builtin_macros/src/cfg_eval.rs new file mode 100644 index 0000000000000..79dc857074d59 --- /dev/null +++ b/compiler/rustc_builtin_macros/src/cfg_eval.rs @@ -0,0 +1,269 @@ +use crate::util::check_builtin_macro_attribute; + +use rustc_ast as ast; +use rustc_ast::mut_visit::MutVisitor; +use rustc_ast::tokenstream::CanSynthesizeMissingTokens; +use rustc_ast::visit::Visitor; +use rustc_ast::{mut_visit, visit}; +use rustc_ast::{AstLike, Attribute}; +use rustc_expand::base::{Annotatable, ExtCtxt}; +use rustc_expand::config::StripUnconfigured; +use rustc_expand::configure; +use rustc_parse::parser::ForceCollect; +use rustc_session::utils::FlattenNonterminals; + +use rustc_ast::ptr::P; +use rustc_span::symbol::sym; +use rustc_span::Span; +use smallvec::SmallVec; + +crate fn expand( + ecx: &mut ExtCtxt<'_>, + _span: Span, + meta_item: &ast::MetaItem, + annotatable: Annotatable, +) -> Vec { + check_builtin_macro_attribute(ecx, meta_item, sym::cfg_eval); + cfg_eval(ecx, annotatable) +} + +crate fn cfg_eval(ecx: &ExtCtxt<'_>, annotatable: Annotatable) -> Vec { + let mut visitor = CfgEval { + cfg: &mut StripUnconfigured { + sess: ecx.sess, + features: ecx.ecfg.features, + config_tokens: true, + }, + }; + let annotatable = visitor.configure_annotatable(annotatable); + vec![annotatable] +} + +struct CfgEval<'a, 'b> { + cfg: &'a mut StripUnconfigured<'b>, +} + +fn flat_map_annotatable(vis: &mut impl MutVisitor, annotatable: Annotatable) -> Annotatable { + // Since the item itself has already been configured by the InvocationCollector, + // we know that fold result vector will contain exactly one element + match annotatable { + Annotatable::Item(item) => Annotatable::Item(vis.flat_map_item(item).pop().unwrap()), + Annotatable::TraitItem(item) => { + Annotatable::TraitItem(vis.flat_map_trait_item(item).pop().unwrap()) + } + Annotatable::ImplItem(item) => { + Annotatable::ImplItem(vis.flat_map_impl_item(item).pop().unwrap()) + } + Annotatable::ForeignItem(item) => { + Annotatable::ForeignItem(vis.flat_map_foreign_item(item).pop().unwrap()) + } + Annotatable::Stmt(stmt) => { + Annotatable::Stmt(stmt.map(|stmt| vis.flat_map_stmt(stmt).pop().unwrap())) + } + Annotatable::Expr(mut expr) => Annotatable::Expr({ + vis.visit_expr(&mut expr); + expr + }), + Annotatable::Arm(arm) => Annotatable::Arm(vis.flat_map_arm(arm).pop().unwrap()), + Annotatable::ExprField(field) => { + Annotatable::ExprField(vis.flat_map_expr_field(field).pop().unwrap()) + } + Annotatable::PatField(fp) => { + Annotatable::PatField(vis.flat_map_pat_field(fp).pop().unwrap()) + } + Annotatable::GenericParam(param) => { + Annotatable::GenericParam(vis.flat_map_generic_param(param).pop().unwrap()) + } + Annotatable::Param(param) => Annotatable::Param(vis.flat_map_param(param).pop().unwrap()), + Annotatable::FieldDef(sf) => { + Annotatable::FieldDef(vis.flat_map_field_def(sf).pop().unwrap()) + } + Annotatable::Variant(v) => Annotatable::Variant(vis.flat_map_variant(v).pop().unwrap()), + } +} + +struct CfgFinder { + has_cfg_or_cfg_attr: bool, +} + +impl CfgFinder { + fn has_cfg_or_cfg_attr(annotatable: &Annotatable) -> bool { + let mut finder = CfgFinder { has_cfg_or_cfg_attr: false }; + match annotatable { + Annotatable::Item(item) => finder.visit_item(&item), + Annotatable::TraitItem(item) => finder.visit_assoc_item(&item, visit::AssocCtxt::Trait), + Annotatable::ImplItem(item) => finder.visit_assoc_item(&item, visit::AssocCtxt::Impl), + Annotatable::ForeignItem(item) => finder.visit_foreign_item(&item), + Annotatable::Stmt(stmt) => finder.visit_stmt(&stmt), + Annotatable::Expr(expr) => finder.visit_expr(&expr), + Annotatable::Arm(arm) => finder.visit_arm(&arm), + Annotatable::ExprField(field) => finder.visit_expr_field(&field), + Annotatable::PatField(field) => finder.visit_pat_field(&field), + Annotatable::GenericParam(param) => finder.visit_generic_param(¶m), + Annotatable::Param(param) => finder.visit_param(¶m), + Annotatable::FieldDef(field) => finder.visit_field_def(&field), + Annotatable::Variant(variant) => finder.visit_variant(&variant), + }; + finder.has_cfg_or_cfg_attr + } +} + +impl<'ast> visit::Visitor<'ast> for CfgFinder { + fn visit_attribute(&mut self, attr: &'ast Attribute) { + // We want short-circuiting behavior, so don't use the '|=' operator. + self.has_cfg_or_cfg_attr = self.has_cfg_or_cfg_attr + || attr + .ident() + .map_or(false, |ident| ident.name == sym::cfg || ident.name == sym::cfg_attr); + } +} + +impl CfgEval<'_, '_> { + fn configure(&mut self, node: T) -> Option { + self.cfg.configure(node) + } + + pub fn configure_annotatable(&mut self, mut annotatable: Annotatable) -> Annotatable { + // Tokenizing and re-parsing the `Annotatable` can have a significant + // performance impact, so try to avoid it if possible + if !CfgFinder::has_cfg_or_cfg_attr(&annotatable) { + return annotatable; + } + + // The majority of parsed attribute targets will never need to have early cfg-expansion + // run (e.g. they are not part of a `#[derive]` or `#[cfg_eval]` macro inoput). + // Therefore, we normally do not capture the necessary information about `#[cfg]` + // and `#[cfg_attr]` attributes during parsing. + // + // Therefore, when we actually *do* run early cfg-expansion, we need to tokenize + // and re-parse the attribute target, this time capturing information about + // the location of `#[cfg]` and `#[cfg_attr]` in the token stream. The tokenization + // process is lossless, so this process is invisible to proc-macros. + + // FIXME - get rid of this clone + let nt = annotatable.clone().into_nonterminal(); + + let mut orig_tokens = rustc_parse::nt_to_tokenstream( + &nt, + &self.cfg.sess.parse_sess, + CanSynthesizeMissingTokens::No, + ); + + // 'Flatten' all nonterminals (i.e. `TokenKind::Interpolated`) + // to `None`-delimited groups containing the corresponding tokens. This + // is normally delayed until the proc-macro server actually needs to + // provide a `TokenKind::Interpolated` to a proc-macro. We do this earlier, + // so that we can handle cases like: + // + // ```rust + // #[cfg_eval] #[cfg] $item + //``` + // + // where `$item` is `#[cfg_attr] struct Foo {}`. We want to make + // sure to evaluate *all* `#[cfg]` and `#[cfg_attr]` attributes - the simplest + // way to do this is to do a single parse of a stream without any nonterminals. + let mut flatten = FlattenNonterminals { + nt_to_tokenstream: rustc_parse::nt_to_tokenstream, + parse_sess: &self.cfg.sess.parse_sess, + synthesize_tokens: CanSynthesizeMissingTokens::No, + }; + orig_tokens = flatten.process_token_stream(orig_tokens); + + // Re-parse the tokens, setting the `capture_cfg` flag to save extra information + // to the captured `AttrAnnotatedTokenStream` (specifically, we capture + // `AttrAnnotatedTokenTree::AttributesData` for all occurences of `#[cfg]` and `#[cfg_attr]`) + let mut parser = + rustc_parse::stream_to_parser(&self.cfg.sess.parse_sess, orig_tokens, None); + parser.capture_cfg = true; + annotatable = match annotatable { + Annotatable::Item(_) => { + Annotatable::Item(parser.parse_item(ForceCollect::Yes).unwrap().unwrap()) + } + Annotatable::TraitItem(_) => Annotatable::TraitItem( + parser.parse_trait_item(ForceCollect::Yes).unwrap().unwrap().unwrap(), + ), + Annotatable::ImplItem(_) => Annotatable::ImplItem( + parser.parse_impl_item(ForceCollect::Yes).unwrap().unwrap().unwrap(), + ), + Annotatable::ForeignItem(_) => Annotatable::ForeignItem( + parser.parse_foreign_item(ForceCollect::Yes).unwrap().unwrap().unwrap(), + ), + Annotatable::Stmt(_) => { + Annotatable::Stmt(P(parser.parse_stmt(ForceCollect::Yes).unwrap().unwrap())) + } + Annotatable::Expr(_) => Annotatable::Expr(parser.parse_expr_force_collect().unwrap()), + _ => unreachable!(), + }; + + // Now that we have our re-parsed `AttrAnnotatedTokenStream`, recursively configuring + // our attribute target will correctly the tokens as well. + flat_map_annotatable(self, annotatable) + } +} + +impl MutVisitor for CfgEval<'_, '_> { + fn visit_expr(&mut self, expr: &mut P) { + self.cfg.configure_expr(expr); + mut_visit::noop_visit_expr(expr, self); + } + + fn filter_map_expr(&mut self, expr: P) -> Option> { + let mut expr = configure!(self, expr); + mut_visit::noop_visit_expr(&mut expr, self); + Some(expr) + } + + fn flat_map_generic_param( + &mut self, + param: ast::GenericParam, + ) -> SmallVec<[ast::GenericParam; 1]> { + mut_visit::noop_flat_map_generic_param(configure!(self, param), self) + } + + fn flat_map_stmt(&mut self, stmt: ast::Stmt) -> SmallVec<[ast::Stmt; 1]> { + mut_visit::noop_flat_map_stmt(configure!(self, stmt), self) + } + + fn flat_map_item(&mut self, item: P) -> SmallVec<[P; 1]> { + mut_visit::noop_flat_map_item(configure!(self, item), self) + } + + fn flat_map_impl_item(&mut self, item: P) -> SmallVec<[P; 1]> { + mut_visit::noop_flat_map_assoc_item(configure!(self, item), self) + } + + fn flat_map_trait_item(&mut self, item: P) -> SmallVec<[P; 1]> { + mut_visit::noop_flat_map_assoc_item(configure!(self, item), self) + } + + fn flat_map_foreign_item( + &mut self, + foreign_item: P, + ) -> SmallVec<[P; 1]> { + mut_visit::noop_flat_map_foreign_item(configure!(self, foreign_item), self) + } + + fn flat_map_arm(&mut self, arm: ast::Arm) -> SmallVec<[ast::Arm; 1]> { + mut_visit::noop_flat_map_arm(configure!(self, arm), self) + } + + fn flat_map_expr_field(&mut self, field: ast::ExprField) -> SmallVec<[ast::ExprField; 1]> { + mut_visit::noop_flat_map_expr_field(configure!(self, field), self) + } + + fn flat_map_pat_field(&mut self, fp: ast::PatField) -> SmallVec<[ast::PatField; 1]> { + mut_visit::noop_flat_map_pat_field(configure!(self, fp), self) + } + + fn flat_map_param(&mut self, p: ast::Param) -> SmallVec<[ast::Param; 1]> { + mut_visit::noop_flat_map_param(configure!(self, p), self) + } + + fn flat_map_field_def(&mut self, sf: ast::FieldDef) -> SmallVec<[ast::FieldDef; 1]> { + mut_visit::noop_flat_map_field_def(configure!(self, sf), self) + } + + fn flat_map_variant(&mut self, variant: ast::Variant) -> SmallVec<[ast::Variant; 1]> { + mut_visit::noop_flat_map_variant(configure!(self, variant), self) + } +} diff --git a/compiler/rustc_builtin_macros/src/derive.rs b/compiler/rustc_builtin_macros/src/derive.rs index fad64858ce3ff..1bb050a40cee2 100644 --- a/compiler/rustc_builtin_macros/src/derive.rs +++ b/compiler/rustc_builtin_macros/src/derive.rs @@ -1,7 +1,8 @@ -use rustc_ast::{self as ast, token, ItemKind, MetaItemKind, NestedMetaItem, StmtKind}; +use crate::cfg_eval::cfg_eval; + +use rustc_ast::{self as ast, attr, token, ItemKind, MetaItemKind, NestedMetaItem, StmtKind}; use rustc_errors::{struct_span_err, Applicability}; use rustc_expand::base::{Annotatable, ExpandResult, ExtCtxt, Indeterminate, MultiItemModifier}; -use rustc_expand::config::StripUnconfigured; use rustc_feature::AttributeTemplate; use rustc_parse::validate_attr; use rustc_session::Session; @@ -25,52 +26,40 @@ impl MultiItemModifier for Expander { return ExpandResult::Ready(vec![item]); } - let template = - AttributeTemplate { list: Some("Trait1, Trait2, ..."), ..Default::default() }; - let attr = ecx.attribute(meta_item.clone()); - validate_attr::check_builtin_attribute(&sess.parse_sess, &attr, sym::derive, template); + let result = + ecx.resolver.resolve_derives(ecx.current_expansion.id, ecx.force_mode, &|| { + let template = + AttributeTemplate { list: Some("Trait1, Trait2, ..."), ..Default::default() }; + let attr = attr::mk_attr_outer(meta_item.clone()); + validate_attr::check_builtin_attribute( + &sess.parse_sess, + &attr, + sym::derive, + template, + ); - let derives: Vec<_> = attr - .meta_item_list() - .unwrap_or_default() - .into_iter() - .filter_map(|nested_meta| match nested_meta { - NestedMetaItem::MetaItem(meta) => Some(meta), - NestedMetaItem::Literal(lit) => { - // Reject `#[derive("Debug")]`. - report_unexpected_literal(sess, &lit); - None - } - }) - .map(|meta| { - // Reject `#[derive(Debug = "value", Debug(abc))]`, but recover the paths. - report_path_args(sess, &meta); - meta.path - }) - .collect(); + attr.meta_item_list() + .unwrap_or_default() + .into_iter() + .filter_map(|nested_meta| match nested_meta { + NestedMetaItem::MetaItem(meta) => Some(meta), + NestedMetaItem::Literal(lit) => { + // Reject `#[derive("Debug")]`. + report_unexpected_literal(sess, &lit); + None + } + }) + .map(|meta| { + // Reject `#[derive(Debug = "value", Debug(abc))]`, but recover the paths. + report_path_args(sess, &meta); + meta.path + }) + .map(|path| (path, None)) + .collect() + }); - // FIXME: Try to cache intermediate results to avoid collecting same paths multiple times. - match ecx.resolver.resolve_derives(ecx.current_expansion.id, derives, ecx.force_mode) { - Ok(()) => { - let mut visitor = - StripUnconfigured { sess, features: ecx.ecfg.features, modified: false }; - let mut item = visitor.fully_configure(item); - if visitor.modified { - // Erase the tokens if cfg-stripping modified the item - // This will cause us to synthesize fake tokens - // when `nt_to_tokenstream` is called on this item. - match &mut item { - Annotatable::Item(item) => item, - Annotatable::Stmt(stmt) => match &mut stmt.kind { - StmtKind::Item(item) => item, - _ => unreachable!(), - }, - _ => unreachable!(), - } - .tokens = None; - } - ExpandResult::Ready(vec![item]) - } + match result { + Ok(()) => ExpandResult::Ready(cfg_eval(ecx, item)), Err(Indeterminate) => ExpandResult::Retry(item), } } diff --git a/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs b/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs index d498c8e172799..a3decff3ae7e1 100644 --- a/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs +++ b/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs @@ -541,7 +541,7 @@ impl<'a> TraitDef<'a> { self.generics.to_generics(cx, self.span, type_ident, generics); // Create the generic parameters - params.extend(generics.params.iter().map(|param| match param.kind { + params.extend(generics.params.iter().map(|param| match ¶m.kind { GenericParamKind::Lifetime { .. } => param.clone(), GenericParamKind::Type { .. } => { // I don't think this can be moved out of the loop, since @@ -561,7 +561,18 @@ impl<'a> TraitDef<'a> { cx.typaram(self.span, param.ident, vec![], bounds, None) } - GenericParamKind::Const { .. } => param.clone(), + GenericParamKind::Const { ty, kw_span, .. } => { + let const_nodefault_kind = GenericParamKind::Const { + ty: ty.clone(), + kw_span: kw_span.clone(), + + // We can't have default values inside impl block + default: None, + }; + let mut param_clone = param.clone(); + param_clone.kind = const_nodefault_kind; + param_clone + } })); // and similarly for where clauses @@ -1034,7 +1045,7 @@ impl<'a> MethodDef<'a> { // make a series of nested matches, to destructure the // structs. This is actually right-to-left, but it shouldn't // matter. - for (arg_expr, pat) in self_args.iter().zip(patterns) { + for (arg_expr, pat) in iter::zip(self_args, patterns) { body = cx.expr_match( trait_.span, arg_expr.clone(), @@ -1351,7 +1362,7 @@ impl<'a> MethodDef<'a> { let mut discriminant_test = cx.expr_bool(sp, true); let mut first_ident = None; - for (&ident, self_arg) in vi_idents.iter().zip(&self_args) { + for (&ident, self_arg) in iter::zip(&vi_idents, &self_args) { let self_addr = cx.expr_addr_of(sp, self_arg.clone()); let variant_value = deriving::call_intrinsic(cx, sp, sym::discriminant_value, vec![self_addr]); @@ -1571,14 +1582,12 @@ impl<'a> TraitDef<'a> { let subpats = self.create_subpatterns(cx, paths, mutbl, use_temporaries); let pattern = match *struct_def { VariantData::Struct(..) => { - let field_pats = subpats - .into_iter() - .zip(&ident_exprs) + let field_pats = iter::zip(subpats, &ident_exprs) .map(|(pat, &(sp, ident, ..))| { if ident.is_none() { cx.span_bug(sp, "a braced struct with unnamed fields in `derive`"); } - ast::FieldPat { + ast::PatField { ident: ident.unwrap(), is_shorthand: false, attrs: ast::AttrVec::new(), diff --git a/compiler/rustc_builtin_macros/src/global_allocator.rs b/compiler/rustc_builtin_macros/src/global_allocator.rs index 9b43c11f0f3d3..a97cac7e514c9 100644 --- a/compiler/rustc_builtin_macros/src/global_allocator.rs +++ b/compiler/rustc_builtin_macros/src/global_allocator.rs @@ -14,31 +14,31 @@ pub fn expand( ecx: &mut ExtCtxt<'_>, _span: Span, meta_item: &ast::MetaItem, - mut item: Annotatable, + item: Annotatable, ) -> Vec { check_builtin_macro_attribute(ecx, meta_item, sym::global_allocator); - let not_static = |item: Annotatable| { + let orig_item = item.clone(); + let not_static = || { ecx.sess.parse_sess.span_diagnostic.span_err(item.span(), "allocators must be statics"); - vec![item] + vec![orig_item.clone()] }; - let orig_item = item.clone(); - let mut is_stmt = false; // Allow using `#[global_allocator]` on an item statement - if let Annotatable::Stmt(stmt) = &item { - if let StmtKind::Item(item_) = &stmt.kind { - item = Annotatable::Item(item_.clone()); - is_stmt = true; - } - } - - let item = match item { + // FIXME - if we get deref patterns, use them to reduce duplication here + let (item, is_stmt) = match &item { Annotatable::Item(item) => match item.kind { - ItemKind::Static(..) => item, - _ => return not_static(Annotatable::Item(item)), + ItemKind::Static(..) => (item, false), + _ => return not_static(), + }, + Annotatable::Stmt(stmt) => match &stmt.kind { + StmtKind::Item(item_) => match item_.kind { + ItemKind::Static(..) => (item_, true), + _ => return not_static(), + }, + _ => return not_static(), }, - _ => return not_static(item), + _ => return not_static(), }; // Generate a bunch of new items using the AllocFnFactory diff --git a/compiler/rustc_builtin_macros/src/lib.rs b/compiler/rustc_builtin_macros/src/lib.rs index 9a3c914337ca2..d7926ed0e0bce 100644 --- a/compiler/rustc_builtin_macros/src/lib.rs +++ b/compiler/rustc_builtin_macros/src/lib.rs @@ -7,10 +7,12 @@ #![feature(bool_to_option)] #![feature(crate_visibility_modifier)] #![feature(decl_macro)] +#![feature(iter_zip)] #![feature(nll)] -#![feature(or_patterns)] +#![cfg_attr(bootstrap, feature(or_patterns))] #![feature(proc_macro_internals)] #![feature(proc_macro_quote)] +#![recursion_limit = "256"] extern crate proc_macro; @@ -24,6 +26,7 @@ mod asm; mod assert; mod cfg; mod cfg_accessible; +mod cfg_eval; mod compile_error; mod concat; mod concat_idents; @@ -89,6 +92,7 @@ pub fn register_builtin_macros(resolver: &mut dyn ResolverExpand) { register_attr! { bench: test::expand_bench, cfg_accessible: cfg_accessible::Expander, + cfg_eval: cfg_eval::expand, derive: derive::Expander, global_allocator: global_allocator::expand, test: test::expand_test, diff --git a/compiler/rustc_builtin_macros/src/source_util.rs b/compiler/rustc_builtin_macros/src/source_util.rs index 28efd483c8670..4aafcb2fb6dfe 100644 --- a/compiler/rustc_builtin_macros/src/source_util.rs +++ b/compiler/rustc_builtin_macros/src/source_util.rs @@ -4,7 +4,7 @@ use rustc_ast::token; use rustc_ast::tokenstream::TokenStream; use rustc_ast_pretty::pprust; use rustc_expand::base::{self, *}; -use rustc_expand::module::DirectoryOwnership; +use rustc_expand::module::DirOwnership; use rustc_parse::parser::{ForceCollect, Parser}; use rustc_parse::{self, new_parser_from_file}; use rustc_session::lint::builtin::INCOMPLETE_INCLUDE; @@ -101,7 +101,7 @@ pub fn expand_include<'cx>( None => return DummyResult::any(sp), }; // The file will be added to the code map by the parser - let mut file = match cx.resolve_path(file, sp) { + let file = match cx.resolve_path(file, sp) { Ok(f) => f, Err(mut err) => { err.emit(); @@ -114,10 +114,9 @@ pub fn expand_include<'cx>( // then the path of `bar.rs` should be relative to the directory of `file`. // See https://github.com/rust-lang/rust/pull/69838/files#r395217057 for a discussion. // `MacroExpander::fully_expand_fragment` later restores, so "stack discipline" is maintained. - file.pop(); - cx.current_expansion.directory_ownership = DirectoryOwnership::Owned { relative: None }; - let mod_path = cx.current_expansion.module.mod_path.clone(); - cx.current_expansion.module = Rc::new(ModuleData { mod_path, directory: file }); + let dir_path = file.parent().unwrap_or(&file).to_owned(); + cx.current_expansion.module = Rc::new(cx.current_expansion.module.with_dir_path(dir_path)); + cx.current_expansion.dir_ownership = DirOwnership::Owned { relative: None }; struct ExpandResult<'a> { p: Parser<'a>, diff --git a/compiler/rustc_builtin_macros/src/standard_library_imports.rs b/compiler/rustc_builtin_macros/src/standard_library_imports.rs index f446e6be84ca4..fbd8be22a9de2 100644 --- a/compiler/rustc_builtin_macros/src/standard_library_imports.rs +++ b/compiler/rustc_builtin_macros/src/standard_library_imports.rs @@ -2,7 +2,7 @@ use rustc_ast as ast; use rustc_expand::base::{ExtCtxt, ResolverExpand}; use rustc_expand::expand::ExpansionConfig; use rustc_session::Session; -use rustc_span::edition::Edition; +use rustc_span::edition::Edition::*; use rustc_span::hygiene::AstPass; use rustc_span::symbol::{kw, sym, Ident, Symbol}; use rustc_span::DUMMY_SP; @@ -13,7 +13,7 @@ pub fn inject( sess: &Session, alt_std_name: Option, ) -> ast::Crate { - let rust_2018 = sess.parse_sess.edition >= Edition::Edition2018; + let edition = sess.parse_sess.edition; // the first name in this list is the crate name of the crate with the prelude let names: &[Symbol] = if sess.contains_name(&krate.attrs, sym::no_core) { @@ -42,7 +42,11 @@ pub fn inject( // .rev() to preserve ordering above in combination with insert(0, ...) for &name in names.iter().rev() { - let ident = if rust_2018 { Ident::new(name, span) } else { Ident::new(name, call_site) }; + let ident = if edition >= Edition2018 { + Ident::new(name, span) + } else { + Ident::new(name, call_site) + }; krate.items.insert( 0, cx.item( @@ -58,14 +62,18 @@ pub fn inject( // the one with the prelude. let name = names[0]; - let import_path = if rust_2018 { - [name, sym::prelude, sym::v1].iter().map(|symbol| Ident::new(*symbol, span)).collect() - } else { - [kw::PathRoot, name, sym::prelude, sym::v1] - .iter() - .map(|symbol| Ident::new(*symbol, span)) - .collect() - }; + let root = (edition == Edition2015).then(|| kw::PathRoot); + + let import_path = root + .iter() + .chain(&[name, sym::prelude]) + .chain(&[match edition { + Edition2015 => sym::rust_2015, + Edition2018 => sym::rust_2018, + Edition2021 => sym::rust_2021, + }]) + .map(|&symbol| Ident::new(symbol, span)) + .collect(); let use_item = cx.item( span, diff --git a/compiler/rustc_builtin_macros/src/test_harness.rs b/compiler/rustc_builtin_macros/src/test_harness.rs index 28e8259784387..c8a7ff67b4d50 100644 --- a/compiler/rustc_builtin_macros/src/test_harness.rs +++ b/compiler/rustc_builtin_macros/src/test_harness.rs @@ -142,7 +142,7 @@ fn entry_point_type(sess: &Session, item: &ast::Item, depth: usize) -> EntryPoin ast::ItemKind::Fn(..) => { if sess.contains_name(&item.attrs, sym::start) { EntryPointType::Start - } else if sess.contains_name(&item.attrs, sym::main) { + } else if sess.contains_name(&item.attrs, sym::rustc_main) { EntryPointType::MainAttr } else if item.ident.name == sym::main { if depth == 1 { @@ -187,7 +187,7 @@ impl<'a> MutVisitor for EntryPointCleaner<'a> { let attrs = attrs .into_iter() .filter(|attr| { - !self.sess.check_name(attr, sym::main) + !self.sess.check_name(attr, sym::rustc_main) && !self.sess.check_name(attr, sym::start) }) .chain(iter::once(allow_dead_code)) @@ -220,7 +220,7 @@ fn generate_test_harness( let expn_id = ext_cx.resolver.expansion_for_ast_pass( DUMMY_SP, AstPass::TestHarness, - &[sym::main, sym::test, sym::rustc_attrs], + &[sym::test, sym::rustc_attrs], None, ); let def_site = DUMMY_SP.with_def_site_ctxt(expn_id); @@ -247,7 +247,7 @@ fn generate_test_harness( /// By default this expands to /// /// ``` -/// #[main] +/// #[rustc_main] /// pub fn main() { /// extern crate test; /// test::test_main_static(&[ @@ -297,8 +297,8 @@ fn mk_main(cx: &mut TestCtxt<'_>) -> P { let test_extern_stmt = ecx.stmt_item(sp, ecx.item(sp, test_id, vec![], ast::ItemKind::ExternCrate(None))); - // #[main] - let main_meta = ecx.meta_word(sp, sym::main); + // #[rustc_main] + let main_meta = ecx.meta_word(sp, sym::rustc_main); let main_attr = ecx.attribute(main_meta); // pub fn main() { ... } diff --git a/compiler/rustc_codegen_cranelift/.cirrus.yml b/compiler/rustc_codegen_cranelift/.cirrus.yml new file mode 100644 index 0000000000000..e173df423a765 --- /dev/null +++ b/compiler/rustc_codegen_cranelift/.cirrus.yml @@ -0,0 +1,25 @@ +task: + name: freebsd + freebsd_instance: + image: freebsd-12-1-release-amd64 + setup_rust_script: + - pkg install -y curl git bash + - curl https://sh.rustup.rs -sSf --output rustup.sh + - sh rustup.sh --default-toolchain none -y --profile=minimal + cargo_bin_cache: + folder: ~/.cargo/bin + target_cache: + folder: target + prepare_script: + - . $HOME/.cargo/env + - git config --global user.email "user@example.com" + - git config --global user.name "User" + - ./prepare.sh + test_script: + - . $HOME/.cargo/env + - # Enable backtraces for easier debugging + - export RUST_BACKTRACE=1 + - # Reduce amount of benchmark runs as they are slow + - export COMPILE_RUNS=2 + - export RUN_RUNS=2 + - ./test.sh diff --git a/compiler/rustc_codegen_cranelift/.github/workflows/bootstrap_rustc.yml b/compiler/rustc_codegen_cranelift/.github/workflows/bootstrap_rustc.yml deleted file mode 100644 index 8c94a0aa5e6eb..0000000000000 --- a/compiler/rustc_codegen_cranelift/.github/workflows/bootstrap_rustc.yml +++ /dev/null @@ -1,44 +0,0 @@ -name: Bootstrap rustc using cg_clif - -on: - - push - -jobs: - bootstrap_rustc: - runs-on: ubuntu-latest - - steps: - - uses: actions/checkout@v2 - - - name: Cache cargo installed crates - uses: actions/cache@v2 - with: - path: ~/.cargo/bin - key: ${{ runner.os }}-cargo-installed-crates - - - name: Cache cargo registry and index - uses: actions/cache@v2 - with: - path: | - ~/.cargo/registry - ~/.cargo/git - key: ${{ runner.os }}-cargo-registry-and-index-${{ hashFiles('**/Cargo.lock') }} - - - name: Cache cargo target dir - uses: actions/cache@v2 - with: - path: target - key: ${{ runner.os }}-cargo-build-target-${{ hashFiles('rust-toolchain', '**/Cargo.lock') }} - - - name: Prepare dependencies - run: | - git config --global user.email "user@example.com" - git config --global user.name "User" - ./prepare.sh - - - name: Test - run: | - # Enable backtraces for easier debugging - export RUST_BACKTRACE=1 - - ./scripts/test_bootstrap.sh diff --git a/compiler/rustc_codegen_cranelift/.github/workflows/main.yml b/compiler/rustc_codegen_cranelift/.github/workflows/main.yml index 20c58423a0c50..2ac516381cf7a 100644 --- a/compiler/rustc_codegen_cranelift/.github/workflows/main.yml +++ b/compiler/rustc_codegen_cranelift/.github/workflows/main.yml @@ -7,14 +7,18 @@ on: jobs: build: runs-on: ${{ matrix.os }} + timeout-minutes: 60 strategy: fail-fast: false matrix: - os: [ubuntu-latest, macos-latest] - env: - - BACKEND: "" - - BACKEND: --oldbe + include: + - os: ubuntu-latest + - os: macos-latest + # cross-compile from Linux to Windows using mingw + - os: ubuntu-latest + env: + TARGET_TRIPLE: x86_64-pc-windows-gnu steps: - uses: actions/checkout@v2 @@ -39,6 +43,12 @@ jobs: path: target key: ${{ runner.os }}-cargo-build-target-${{ hashFiles('rust-toolchain', '**/Cargo.lock') }} + - name: Install MinGW toolchain and wine + if: matrix.os == 'ubuntu-latest' && matrix.env.TARGET_TRIPLE == 'x86_64-pc-windows-gnu' + run: | + sudo apt-get install -y gcc-mingw-w64-x86-64 wine-stable + rustup target add x86_64-pc-windows-gnu + - name: Prepare dependencies run: | git config --global user.email "user@example.com" @@ -46,6 +56,8 @@ jobs: ./prepare.sh - name: Test + env: + TARGET_TRIPLE: ${{ matrix.env.TARGET_TRIPLE }} run: | # Enable backtraces for easier debugging export RUST_BACKTRACE=1 @@ -54,12 +66,16 @@ jobs: export COMPILE_RUNS=2 export RUN_RUNS=2 - ./test.sh $BACKEND + # Enable extra checks + export CG_CLIF_ENABLE_VERIFIER=1 + + ./test.sh - name: Package prebuilt cg_clif run: tar cvfJ cg_clif.tar.xz build - name: Upload prebuilt cg_clif + if: matrix.env.TARGET_TRIPLE != 'x86_64-pc-windows-gnu' uses: actions/upload-artifact@v2 with: name: cg_clif-${{ runner.os }} diff --git a/compiler/rustc_codegen_cranelift/.github/workflows/rustc.yml b/compiler/rustc_codegen_cranelift/.github/workflows/rustc.yml new file mode 100644 index 0000000000000..e01a92598bab7 --- /dev/null +++ b/compiler/rustc_codegen_cranelift/.github/workflows/rustc.yml @@ -0,0 +1,82 @@ +name: Various rustc tests + +on: + - push + +jobs: + bootstrap_rustc: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v2 + + - name: Cache cargo installed crates + uses: actions/cache@v2 + with: + path: ~/.cargo/bin + key: ${{ runner.os }}-cargo-installed-crates + + - name: Cache cargo registry and index + uses: actions/cache@v2 + with: + path: | + ~/.cargo/registry + ~/.cargo/git + key: ${{ runner.os }}-cargo-registry-and-index-${{ hashFiles('**/Cargo.lock') }} + + - name: Cache cargo target dir + uses: actions/cache@v2 + with: + path: target + key: ${{ runner.os }}-cargo-build-target-${{ hashFiles('rust-toolchain', '**/Cargo.lock') }} + + - name: Prepare dependencies + run: | + git config --global user.email "user@example.com" + git config --global user.name "User" + ./prepare.sh + + - name: Test + run: | + # Enable backtraces for easier debugging + export RUST_BACKTRACE=1 + + ./scripts/test_bootstrap.sh + rustc_test_suite: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v2 + + - name: Cache cargo installed crates + uses: actions/cache@v2 + with: + path: ~/.cargo/bin + key: ${{ runner.os }}-cargo-installed-crates + + - name: Cache cargo registry and index + uses: actions/cache@v2 + with: + path: | + ~/.cargo/registry + ~/.cargo/git + key: ${{ runner.os }}-cargo-registry-and-index-${{ hashFiles('**/Cargo.lock') }} + + - name: Cache cargo target dir + uses: actions/cache@v2 + with: + path: target + key: ${{ runner.os }}-cargo-build-target-${{ hashFiles('rust-toolchain', '**/Cargo.lock') }} + + - name: Prepare dependencies + run: | + git config --global user.email "user@example.com" + git config --global user.name "User" + ./prepare.sh + + - name: Test + run: | + # Enable backtraces for easier debugging + export RUST_BACKTRACE=1 + + ./scripts/test_rustc_tests.sh diff --git a/compiler/rustc_codegen_cranelift/.vscode/settings.json b/compiler/rustc_codegen_cranelift/.vscode/settings.json index 19ea41563dfd6..0cd576e160f86 100644 --- a/compiler/rustc_codegen_cranelift/.vscode/settings.json +++ b/compiler/rustc_codegen_cranelift/.vscode/settings.json @@ -1,8 +1,8 @@ { // source for rustc_* is not included in the rust-src component; disable the errors about this - "rust-analyzer.diagnostics.disabled": ["unresolved-extern-crate"], + "rust-analyzer.diagnostics.disabled": ["unresolved-extern-crate", "macro-error"], "rust-analyzer.assist.importMergeBehavior": "last", - "rust-analyzer.cargo.loadOutDirsFromCheck": true, + "rust-analyzer.cargo.runBuildScripts": true, "rust-analyzer.linkedProjects": [ "./Cargo.toml", //"./build_sysroot/sysroot_src/src/libstd/Cargo.toml", diff --git a/compiler/rustc_codegen_cranelift/Cargo.lock b/compiler/rustc_codegen_cranelift/Cargo.lock index 5495cfa5eaa0d..dc1cd336e1599 100644 --- a/compiler/rustc_codegen_cranelift/Cargo.lock +++ b/compiler/rustc_codegen_cranelift/Cargo.lock @@ -1,5 +1,7 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. +version = 3 + [[package]] name = "anyhow" version = "1.0.38" @@ -29,18 +31,6 @@ version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ae44d1a3d5a19df61dd0c8beb138458ac2a53a7ac09eba97d55592540004306b" -[[package]] -name = "cc" -version = "1.0.66" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c0496836a84f8d0495758516b8621a622beb77c0fed418570e50764093ced48" - -[[package]] -name = "cfg-if" -version = "0.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" - [[package]] name = "cfg-if" version = "1.0.0" @@ -49,16 +39,16 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "cranelift-bforest" -version = "0.69.0" -source = "git+https://github.com/bytecodealliance/wasmtime/?branch=main#986b5768f9e68f1564b43f32b8a4080a6582c8ca" +version = "0.72.0" +source = "git+https://github.com/bytecodealliance/wasmtime/?branch=main#8e43e96410a14143d368273cf1e708f8094bb8e0" dependencies = [ "cranelift-entity", ] [[package]] name = "cranelift-codegen" -version = "0.69.0" -source = "git+https://github.com/bytecodealliance/wasmtime/?branch=main#986b5768f9e68f1564b43f32b8a4080a6582c8ca" +version = "0.72.0" +source = "git+https://github.com/bytecodealliance/wasmtime/?branch=main#8e43e96410a14143d368273cf1e708f8094bb8e0" dependencies = [ "byteorder", "cranelift-bforest", @@ -75,8 +65,8 @@ dependencies = [ [[package]] name = "cranelift-codegen-meta" -version = "0.69.0" -source = "git+https://github.com/bytecodealliance/wasmtime/?branch=main#986b5768f9e68f1564b43f32b8a4080a6582c8ca" +version = "0.72.0" +source = "git+https://github.com/bytecodealliance/wasmtime/?branch=main#8e43e96410a14143d368273cf1e708f8094bb8e0" dependencies = [ "cranelift-codegen-shared", "cranelift-entity", @@ -84,18 +74,18 @@ dependencies = [ [[package]] name = "cranelift-codegen-shared" -version = "0.69.0" -source = "git+https://github.com/bytecodealliance/wasmtime/?branch=main#986b5768f9e68f1564b43f32b8a4080a6582c8ca" +version = "0.72.0" +source = "git+https://github.com/bytecodealliance/wasmtime/?branch=main#8e43e96410a14143d368273cf1e708f8094bb8e0" [[package]] name = "cranelift-entity" -version = "0.69.0" -source = "git+https://github.com/bytecodealliance/wasmtime/?branch=main#986b5768f9e68f1564b43f32b8a4080a6582c8ca" +version = "0.72.0" +source = "git+https://github.com/bytecodealliance/wasmtime/?branch=main#8e43e96410a14143d368273cf1e708f8094bb8e0" [[package]] name = "cranelift-frontend" -version = "0.69.0" -source = "git+https://github.com/bytecodealliance/wasmtime/?branch=main#986b5768f9e68f1564b43f32b8a4080a6582c8ca" +version = "0.72.0" +source = "git+https://github.com/bytecodealliance/wasmtime/?branch=main#8e43e96410a14143d368273cf1e708f8094bb8e0" dependencies = [ "cranelift-codegen", "log", @@ -105,8 +95,8 @@ dependencies = [ [[package]] name = "cranelift-jit" -version = "0.69.0" -source = "git+https://github.com/bytecodealliance/wasmtime/?branch=main#986b5768f9e68f1564b43f32b8a4080a6582c8ca" +version = "0.72.0" +source = "git+https://github.com/bytecodealliance/wasmtime/?branch=main#8e43e96410a14143d368273cf1e708f8094bb8e0" dependencies = [ "anyhow", "cranelift-codegen", @@ -123,8 +113,8 @@ dependencies = [ [[package]] name = "cranelift-module" -version = "0.69.0" -source = "git+https://github.com/bytecodealliance/wasmtime/?branch=main#986b5768f9e68f1564b43f32b8a4080a6582c8ca" +version = "0.72.0" +source = "git+https://github.com/bytecodealliance/wasmtime/?branch=main#8e43e96410a14143d368273cf1e708f8094bb8e0" dependencies = [ "anyhow", "cranelift-codegen", @@ -135,18 +125,17 @@ dependencies = [ [[package]] name = "cranelift-native" -version = "0.69.0" -source = "git+https://github.com/bytecodealliance/wasmtime/?branch=main#986b5768f9e68f1564b43f32b8a4080a6582c8ca" +version = "0.72.0" +source = "git+https://github.com/bytecodealliance/wasmtime/?branch=main#8e43e96410a14143d368273cf1e708f8094bb8e0" dependencies = [ "cranelift-codegen", - "raw-cpuid", "target-lexicon", ] [[package]] name = "cranelift-object" -version = "0.69.0" -source = "git+https://github.com/bytecodealliance/wasmtime/?branch=main#986b5768f9e68f1564b43f32b8a4080a6582c8ca" +version = "0.72.0" +source = "git+https://github.com/bytecodealliance/wasmtime/?branch=main#8e43e96410a14143d368273cf1e708f8094bb8e0" dependencies = [ "anyhow", "cranelift-codegen", @@ -162,7 +151,7 @@ version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "81156fece84ab6a9f2afdb109ce3ae577e42b1228441eded99bd77f627953b1a" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", ] [[package]] @@ -219,9 +208,9 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.82" +version = "0.2.86" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89203f3fba0a3795506acaad8ebce3c80c0af93f994d5a1d7a0b1eeb23271929" +checksum = "b7282d924be3275cec7f6756ff4121987bc6481325397dde6ba3e7802b1a8b1c" [[package]] name = "libloading" @@ -229,17 +218,17 @@ version = "0.6.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "351a32417a12d5f7e82c368a66781e307834dae04c6ce0cd4456d52989229883" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", "winapi", ] [[package]] name = "log" -version = "0.4.13" +version = "0.4.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fcf3805d4480bb5b86070dcfeb9e2cb2ebc148adb753c5cca5f884d1d65a42b2" +checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710" dependencies = [ - "cfg-if 0.1.10", + "cfg-if", ] [[package]] @@ -253,9 +242,9 @@ dependencies = [ [[package]] name = "object" -version = "0.22.0" +version = "0.23.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d3b63360ec3cb337817c2dbd47ab4a0f170d285d8e5a2064600f3def1402397" +checksum = "a9a7ab5d64814df0fe4a4b5ead45ed6c5f181ee3ff04ba344313a6c80446c5d4" dependencies = [ "crc32fast", "indexmap", @@ -272,24 +261,13 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.8" +version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "991431c3519a3f36861882da93630ce66b52918dcf1b8e2fd66b397fc96f28df" +checksum = "c3d0b9745dc2debf507c8422de05d7226cc1f0644216dfdfead988f9b1ab32a7" dependencies = [ "proc-macro2", ] -[[package]] -name = "raw-cpuid" -version = "8.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fdf7d9dbd43f3d81d94a49c1c3df73cc2b3827995147e6cf7f89d4ec5483e73" -dependencies = [ - "bitflags", - "cc", - "rustc_version", -] - [[package]] name = "regalloc" version = "0.0.31" @@ -337,30 +315,6 @@ dependencies = [ "target-lexicon", ] -[[package]] -name = "rustc_version" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" -dependencies = [ - "semver", -] - -[[package]] -name = "semver" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" -dependencies = [ - "semver-parser", -] - -[[package]] -name = "semver-parser" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" - [[package]] name = "smallvec" version = "1.6.1" @@ -369,9 +323,9 @@ checksum = "fe0f37c9e8f3c5a4a66ad655a93c74daac4ad00c441533bf5c6e7990bb42604e" [[package]] name = "syn" -version = "1.0.58" +version = "1.0.60" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc60a3d73ea6594cd712d830cc1f0390fd71542d8c8cd24e70cc54cdfd5e05d5" +checksum = "c700597eca8a5a762beb35753ef6b94df201c81cca676604f547495a0d7f0081" dependencies = [ "proc-macro2", "quote", @@ -380,24 +334,24 @@ dependencies = [ [[package]] name = "target-lexicon" -version = "0.11.1" +version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ee5a98e506fb7231a304c3a1bd7c132a55016cf65001e0282480665870dfcb9" +checksum = "422045212ea98508ae3d28025bc5aaa2bd4a9cdaecd442a08da2ee620ee9ea95" [[package]] name = "thiserror" -version = "1.0.23" +version = "1.0.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76cc616c6abf8c8928e2fdcc0dbfab37175edd8fb49a4641066ad1364fdab146" +checksum = "e0f4a65597094d4483ddaed134f409b2cb7c1beccf25201a9f73c719254fa98e" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.23" +version = "1.0.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9be73a2caec27583d0046ef3796c3794f868a5bc813db689eed00c7631275cd1" +checksum = "7765189610d8241a44529806d6fd1f2e0a08734313a35d5b3a556f92b381f3c0" dependencies = [ "proc-macro2", "quote", diff --git a/compiler/rustc_codegen_cranelift/Cargo.toml b/compiler/rustc_codegen_cranelift/Cargo.toml index 3820fce6d1e0d..60946ab280858 100644 --- a/compiler/rustc_codegen_cranelift/Cargo.toml +++ b/compiler/rustc_codegen_cranelift/Cargo.toml @@ -9,14 +9,14 @@ crate-type = ["dylib"] [dependencies] # These have to be in sync with each other -cranelift-codegen = { git = "https://github.com/bytecodealliance/wasmtime/", branch = "main", features = ["unwind", "x86", "x64"] } +cranelift-codegen = { git = "https://github.com/bytecodealliance/wasmtime/", branch = "main", features = ["unwind", "x64"] } cranelift-frontend = { git = "https://github.com/bytecodealliance/wasmtime/", branch = "main" } cranelift-module = { git = "https://github.com/bytecodealliance/wasmtime/", branch = "main" } cranelift-jit = { git = "https://github.com/bytecodealliance/wasmtime/", branch = "main", optional = true } cranelift-object = { git = "https://github.com/bytecodealliance/wasmtime/", branch = "main" } target-lexicon = "0.11.0" gimli = { version = "0.23.0", default-features = false, features = ["write"]} -object = { version = "0.22.0", default-features = false, features = ["std", "read_core", "write", "coff", "elf", "macho", "pe"] } +object = { version = "0.23.0", default-features = false, features = ["std", "read_core", "write", "archive", "coff", "elf", "macho", "pe"] } ar = { git = "https://github.com/bjorn3/rust-ar.git", branch = "do_not_remove_cg_clif_ranlib" } indexmap = "1.0.2" @@ -38,7 +38,6 @@ smallvec = "1.6.1" default = ["jit", "inline_asm"] jit = ["cranelift-jit", "libloading"] inline_asm = [] -oldbe = [] [profile.dev] # By compiling dependencies with optimizations, performing tests gets much faster. @@ -76,3 +75,6 @@ debug = false [profile.release.package.syn] opt-level = 0 debug = false + +[package.metadata.rust-analyzer] +rustc_private = true diff --git a/compiler/rustc_codegen_cranelift/Readme.md b/compiler/rustc_codegen_cranelift/Readme.md index 6fa5eebdc2f3d..ffe1d9a1e6580 100644 --- a/compiler/rustc_codegen_cranelift/Readme.md +++ b/compiler/rustc_codegen_cranelift/Readme.md @@ -34,70 +34,19 @@ rustc_codegen_cranelift can be used as a near-drop-in replacement for `cargo bui Assuming `$cg_clif_dir` is the directory you cloned this repo into and you followed the instructions (`prepare.sh` and `build.sh` or `test.sh`). -### Cargo - In the directory with your project (where you can do the usual `cargo build`), run: ```bash -$ $cg_clif_dir/build/cargo.sh run -``` - -This should build and run your project with rustc_codegen_cranelift instead of the usual LLVM backend. - -### Rustc - -> You should prefer using the Cargo method. - -```bash -$ $cg_clif_dir/build/bin/cg_clif my_crate.rs -``` - -### Jit mode - -In jit mode cg_clif will immediately execute your code without creating an executable file. - -> This requires all dependencies to be available as dynamic library. -> The jit mode will probably need cargo integration to make this possible. - -```bash -$ $cg_clif_dir/build/cargo.sh jit -``` - -or - -```bash -$ $cg_clif_dir/build/bin/cg_clif -Cllvm-args=mode=jit -Cprefer-dynamic my_crate.rs -``` - -There is also an experimental lazy jit mode. In this mode functions are only compiled once they are -first called. It currently does not work with multi-threaded programs. When a not yet compiled -function is called from another thread than the main thread, you will get an ICE. - -```bash -$ $cg_clif_dir/build/cargo.sh lazy-jit +$ $cg_clif_dir/build/cargo.sh build ``` -### Shell - -These are a few functions that allow you to easily run rust code from the shell using cg_clif as jit. - -```bash -function jit_naked() { - echo "$@" | $cg_clif_dir/build/bin/cg_clif - -Cllvm-args=mode=jit -Cprefer-dynamic -} - -function jit() { - jit_naked "fn main() { $@ }" -} +This will build your project with rustc_codegen_cranelift instead of the usual LLVM backend. -function jit_calc() { - jit 'println!("0x{:x}", ' $@ ');'; -} -``` +For additional ways to use rustc_codegen_cranelift like the JIT mode see [usage.md](docs/usage.md). ## Env vars -[see env_vars.md](docs/env_vars.md) +See [env_vars.md](docs/env_vars.md) for all env vars used by rustc_codegen_cranelift. ## Not yet supported @@ -106,3 +55,20 @@ function jit_calc() { `llvm_asm!` will remain unimplemented forever. `asm!` doesn't yet support reg classes. You have to specify specific registers instead. * SIMD ([tracked here](https://github.com/bjorn3/rustc_codegen_cranelift/issues/171), some basic things work) + +## License + +Licensed under either of + + * Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or + http://www.apache.org/licenses/LICENSE-2.0) + * MIT license ([LICENSE-MIT](LICENSE-MIT) or + http://opensource.org/licenses/MIT) + +at your option. + +### Contribution + +Unless you explicitly state otherwise, any contribution intentionally submitted +for inclusion in the work by you shall be dual licensed as above, without any +additional terms or conditions. diff --git a/compiler/rustc_codegen_cranelift/build.sh b/compiler/rustc_codegen_cranelift/build.sh index 598ce35eceaac..76bc1884334af 100755 --- a/compiler/rustc_codegen_cranelift/build.sh +++ b/compiler/rustc_codegen_cranelift/build.sh @@ -1,11 +1,10 @@ -#!/bin/bash +#!/usr/bin/env bash set -e # Settings export CHANNEL="release" build_sysroot="clif" target_dir='build' -oldbe='' while [[ $# != 0 ]]; do case $1 in "--debug") @@ -19,12 +18,9 @@ while [[ $# != 0 ]]; do target_dir=$2 shift ;; - "--oldbe") - oldbe='--features oldbe' - ;; *) echo "Unknown flag '$1'" - echo "Usage: ./build.sh [--debug] [--sysroot none|clif|llvm] [--target-dir DIR] [--oldbe]" + echo "Usage: ./build.sh [--debug] [--sysroot none|clif|llvm] [--target-dir DIR]" exit 1 ;; esac @@ -34,19 +30,19 @@ done # Build cg_clif unset CARGO_TARGET_DIR unamestr=$(uname) -if [[ "$unamestr" == 'Linux' ]]; then +if [[ "$unamestr" == 'Linux' || "$unamestr" == "FreeBSD" ]]; then export RUSTFLAGS='-Clink-arg=-Wl,-rpath=$ORIGIN/../lib '$RUSTFLAGS elif [[ "$unamestr" == 'Darwin' ]]; then export RUSTFLAGS='-Csplit-debuginfo=unpacked -Clink-arg=-Wl,-rpath,@loader_path/../lib -Zosx-rpath-install-name '$RUSTFLAGS dylib_ext='dylib' else - echo "Unsupported os" + echo "Unsupported os $unamestr" exit 1 fi if [[ "$CHANNEL" == "release" ]]; then - cargo build $oldbe --release + cargo build --release else - cargo build $oldbe + cargo build fi source scripts/ext_config.sh @@ -59,6 +55,7 @@ ln target/$CHANNEL/*rustc_codegen_cranelift* "$target_dir"/lib ln rust-toolchain scripts/config.sh scripts/cargo.sh "$target_dir" mkdir -p "$target_dir/lib/rustlib/$TARGET_TRIPLE/lib/" +mkdir -p "$target_dir/lib/rustlib/$HOST_TRIPLE/lib/" if [[ "$TARGET_TRIPLE" == "x86_64-pc-windows-gnu" ]]; then cp $(rustc --print sysroot)/lib/rustlib/$TARGET_TRIPLE/lib/*.o "$target_dir/lib/rustlib/$TARGET_TRIPLE/lib/" fi @@ -68,12 +65,18 @@ case "$build_sysroot" in ;; "llvm") cp -r $(rustc --print sysroot)/lib/rustlib/$TARGET_TRIPLE/lib "$target_dir/lib/rustlib/$TARGET_TRIPLE/" + if [[ "$HOST_TRIPLE" != "$TARGET_TRIPLE" ]]; then + cp -r $(rustc --print sysroot)/lib/rustlib/$HOST_TRIPLE/lib "$target_dir/lib/rustlib/$HOST_TRIPLE/" + fi ;; "clif") echo "[BUILD] sysroot" dir=$(pwd) cd "$target_dir" time "$dir/build_sysroot/build_sysroot.sh" + if [[ "$HOST_TRIPLE" != "$TARGET_TRIPLE" ]]; then + time TARGET_TRIPLE="$HOST_TRIPLE" "$dir/build_sysroot/build_sysroot.sh" + fi cp lib/rustlib/*/lib/libstd-* lib/ ;; *) diff --git a/compiler/rustc_codegen_cranelift/build_sysroot/Cargo.lock b/compiler/rustc_codegen_cranelift/build_sysroot/Cargo.lock index 0da9999c17285..09c5d7590ab86 100644 --- a/compiler/rustc_codegen_cranelift/build_sysroot/Cargo.lock +++ b/compiler/rustc_codegen_cranelift/build_sysroot/Cargo.lock @@ -1,5 +1,7 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. +version = 3 + [[package]] name = "addr2line" version = "0.14.1" @@ -14,9 +16,9 @@ dependencies = [ [[package]] name = "adler" -version = "0.2.3" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee2a4ec343196209d6594e19543ae87a39f96d5534d7174822a3ad825dd6ed7e" +checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" dependencies = [ "compiler_builtins", "rustc-std-workspace-core", @@ -30,15 +32,6 @@ dependencies = [ "core", ] -[[package]] -name = "alloc_system" -version = "0.0.0" -dependencies = [ - "compiler_builtins", - "core", - "libc", -] - [[package]] name = "autocfg" version = "1.0.1" @@ -47,9 +40,9 @@ checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" [[package]] name = "cc" -version = "1.0.66" +version = "1.0.67" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c0496836a84f8d0495758516b8621a622beb77c0fed418570e50764093ced48" +checksum = "e3c69b077ad434294d3ce9f1f6143a2a4b89a8a2d54ef813d85003a4fd1137fd" [[package]] name = "cfg-if" @@ -117,9 +110,9 @@ dependencies = [ [[package]] name = "hashbrown" -version = "0.9.1" +version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7afe4a420e3fe79967a00898cc1f4db7c8a49a9333a29f8a4bd76a253d5cd04" +checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e" dependencies = [ "compiler_builtins", "rustc-std-workspace-alloc", @@ -139,18 +132,18 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.84" +version = "0.2.91" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1cca32fa0182e8c0989459524dc356b8f2b5c10f1b9eb521b7d182c03cf8c5ff" +checksum = "8916b1f6ca17130ec6568feccee27c156ad12037880833a3b842a823236502e7" dependencies = [ "rustc-std-workspace-core", ] [[package]] name = "miniz_oxide" -version = "0.4.3" +version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f2d26ec3309788e423cfbf68ad1800f061638098d76a83681af979dc4eda19d" +checksum = "a92518e98c078586bc6c934028adcca4c92a53d6a958196de835170a01d84e4b" dependencies = [ "adler", "autocfg", @@ -258,7 +251,6 @@ name = "sysroot" version = "0.0.0" dependencies = [ "alloc", - "alloc_system", "compiler_builtins", "core", "std", diff --git a/compiler/rustc_codegen_cranelift/build_sysroot/Cargo.toml b/compiler/rustc_codegen_cranelift/build_sysroot/Cargo.toml index 82516c98af2a2..04748d5dbab59 100644 --- a/compiler/rustc_codegen_cranelift/build_sysroot/Cargo.toml +++ b/compiler/rustc_codegen_cranelift/build_sysroot/Cargo.toml @@ -9,8 +9,6 @@ alloc = { path = "./sysroot_src/library/alloc" } std = { path = "./sysroot_src/library/std", features = ["panic_unwind", "backtrace"] } test = { path = "./sysroot_src/library/test" } -alloc_system = { path = "./alloc_system" } - compiler_builtins = { version = "0.1.39", default-features = false, features = ["no-asm"] } [patch.crates-io] diff --git a/compiler/rustc_codegen_cranelift/build_sysroot/alloc_system/Cargo.toml b/compiler/rustc_codegen_cranelift/build_sysroot/alloc_system/Cargo.toml deleted file mode 100644 index 9fffca843006f..0000000000000 --- a/compiler/rustc_codegen_cranelift/build_sysroot/alloc_system/Cargo.toml +++ /dev/null @@ -1,13 +0,0 @@ -[package] -authors = ["The Rust Project Developers", "bjorn3 (edited to be usable outside the rust source)"] -name = "alloc_system" -version = "0.0.0" -[lib] -name = "alloc_system" -path = "lib.rs" -test = false -doc = false -[dependencies] -core = { path = "../sysroot_src/library/core" } -libc = { version = "0.2.43", features = ['rustc-dep-of-std'], default-features = false } -compiler_builtins = "0.1" diff --git a/compiler/rustc_codegen_cranelift/build_sysroot/alloc_system/lib.rs b/compiler/rustc_codegen_cranelift/build_sysroot/alloc_system/lib.rs deleted file mode 100644 index c832d5e5ebb9b..0000000000000 --- a/compiler/rustc_codegen_cranelift/build_sysroot/alloc_system/lib.rs +++ /dev/null @@ -1,342 +0,0 @@ -// Copyright 2015 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. -#![no_std] -#![allow(unused_attributes)] -#![unstable(feature = "alloc_system", - reason = "this library is unlikely to be stabilized in its current \ - form or name", - issue = "32838")] -#![feature(allocator_api)] -#![feature(core_intrinsics)] -#![feature(nll)] -#![feature(staged_api)] -#![feature(rustc_attrs)] -#![feature(alloc_layout_extra)] -#![cfg_attr( - all(target_arch = "wasm32", not(target_os = "emscripten")), - feature(integer_atomics, stdsimd) -)] -#![cfg_attr(any(unix, target_os = "redox"), feature(libc))] -// The minimum alignment guaranteed by the architecture. This value is used to -// add fast paths for low alignment values. -#[cfg(all(any(target_arch = "x86", - target_arch = "arm", - target_arch = "mips", - target_arch = "powerpc", - target_arch = "powerpc64", - target_arch = "asmjs", - target_arch = "wasm32")))] -#[allow(dead_code)] -const MIN_ALIGN: usize = 8; -#[cfg(all(any(target_arch = "x86_64", - target_arch = "aarch64", - target_arch = "mips64", - target_arch = "s390x", - target_arch = "sparc64")))] -#[allow(dead_code)] -const MIN_ALIGN: usize = 16; - -/// The default memory allocator provided by the operating system. -/// -/// This is based on `malloc` on Unix platforms and `HeapAlloc` on Windows, -/// plus related functions. -/// -/// This type can be used in a `static` item -/// with the `#[global_allocator]` attribute -/// to force the global allocator to be the system’s one. -/// (The default is jemalloc for executables, on some platforms.) -/// -/// ```rust -/// use std::alloc::System; -/// -/// #[global_allocator] -/// static A: System = System; -/// -/// fn main() { -/// let a = Box::new(4); // Allocates from the system allocator. -/// println!("{}", a); -/// } -/// ``` -/// -/// It can also be used directly to allocate memory -/// independently of the standard library’s global allocator. -#[stable(feature = "alloc_system_type", since = "1.28.0")] -pub struct System; -#[cfg(any(windows, unix, target_os = "redox"))] -mod realloc_fallback { - use core::alloc::{GlobalAlloc, Layout}; - use core::cmp; - use core::ptr; - impl super::System { - pub(crate) unsafe fn realloc_fallback(&self, ptr: *mut u8, old_layout: Layout, - new_size: usize) -> *mut u8 { - // Docs for GlobalAlloc::realloc require this to be valid: - let new_layout = Layout::from_size_align_unchecked(new_size, old_layout.align()); - let new_ptr = GlobalAlloc::alloc(self, new_layout); - if !new_ptr.is_null() { - let size = cmp::min(old_layout.size(), new_size); - ptr::copy_nonoverlapping(ptr, new_ptr, size); - GlobalAlloc::dealloc(self, ptr, old_layout); - } - new_ptr - } - } -} -#[cfg(any(unix, target_os = "redox"))] -mod platform { - extern crate libc; - use core::ptr; - use MIN_ALIGN; - use System; - use core::alloc::{GlobalAlloc, Layout}; - #[stable(feature = "alloc_system_type", since = "1.28.0")] - unsafe impl GlobalAlloc for System { - #[inline] - unsafe fn alloc(&self, layout: Layout) -> *mut u8 { - if layout.align() <= MIN_ALIGN && layout.align() <= layout.size() { - libc::malloc(layout.size()) as *mut u8 - } else { - #[cfg(target_os = "macos")] - { - if layout.align() > (1 << 31) { - return ptr::null_mut() - } - } - aligned_malloc(&layout) - } - } - #[inline] - unsafe fn alloc_zeroed(&self, layout: Layout) -> *mut u8 { - if layout.align() <= MIN_ALIGN && layout.align() <= layout.size() { - libc::calloc(layout.size(), 1) as *mut u8 - } else { - let ptr = self.alloc(layout.clone()); - if !ptr.is_null() { - ptr::write_bytes(ptr, 0, layout.size()); - } - ptr - } - } - #[inline] - unsafe fn dealloc(&self, ptr: *mut u8, _layout: Layout) { - libc::free(ptr as *mut libc::c_void) - } - #[inline] - unsafe fn realloc(&self, ptr: *mut u8, layout: Layout, new_size: usize) -> *mut u8 { - if layout.align() <= MIN_ALIGN && layout.align() <= new_size { - libc::realloc(ptr as *mut libc::c_void, new_size) as *mut u8 - } else { - self.realloc_fallback(ptr, layout, new_size) - } - } - } - #[cfg(any(target_os = "android", - target_os = "hermit", - target_os = "redox", - target_os = "solaris"))] - #[inline] - unsafe fn aligned_malloc(layout: &Layout) -> *mut u8 { - // On android we currently target API level 9 which unfortunately - // doesn't have the `posix_memalign` API used below. Instead we use - // `memalign`, but this unfortunately has the property on some systems - // where the memory returned cannot be deallocated by `free`! - // - // Upon closer inspection, however, this appears to work just fine with - // Android, so for this platform we should be fine to call `memalign` - // (which is present in API level 9). Some helpful references could - // possibly be chromium using memalign [1], attempts at documenting that - // memalign + free is ok [2] [3], or the current source of chromium - // which still uses memalign on android [4]. - // - // [1]: https://codereview.chromium.org/10796020/ - // [2]: https://code.google.com/p/android/issues/detail?id=35391 - // [3]: https://bugs.chromium.org/p/chromium/issues/detail?id=138579 - // [4]: https://chromium.googlesource.com/chromium/src/base/+/master/ - // /memory/aligned_memory.cc - libc::memalign(layout.align(), layout.size()) as *mut u8 - } - #[cfg(not(any(target_os = "android", - target_os = "hermit", - target_os = "redox", - target_os = "solaris")))] - #[inline] - unsafe fn aligned_malloc(layout: &Layout) -> *mut u8 { - let mut out = ptr::null_mut(); - let ret = libc::posix_memalign(&mut out, layout.align(), layout.size()); - if ret != 0 { - ptr::null_mut() - } else { - out as *mut u8 - } - } -} -#[cfg(windows)] -#[allow(nonstandard_style)] -mod platform { - use MIN_ALIGN; - use System; - use core::alloc::{GlobalAlloc, Layout}; - type LPVOID = *mut u8; - type HANDLE = LPVOID; - type SIZE_T = usize; - type DWORD = u32; - type BOOL = i32; - extern "system" { - fn GetProcessHeap() -> HANDLE; - fn HeapAlloc(hHeap: HANDLE, dwFlags: DWORD, dwBytes: SIZE_T) -> LPVOID; - fn HeapReAlloc(hHeap: HANDLE, dwFlags: DWORD, lpMem: LPVOID, dwBytes: SIZE_T) -> LPVOID; - fn HeapFree(hHeap: HANDLE, dwFlags: DWORD, lpMem: LPVOID) -> BOOL; - fn GetLastError() -> DWORD; - } - #[repr(C)] - struct Header(*mut u8); - const HEAP_ZERO_MEMORY: DWORD = 0x00000008; - unsafe fn get_header<'a>(ptr: *mut u8) -> &'a mut Header { - &mut *(ptr as *mut Header).offset(-1) - } - unsafe fn align_ptr(ptr: *mut u8, align: usize) -> *mut u8 { - let aligned = ptr.add(align - (ptr as usize & (align - 1))); - *get_header(aligned) = Header(ptr); - aligned - } - #[inline] - unsafe fn allocate_with_flags(layout: Layout, flags: DWORD) -> *mut u8 { - let ptr = if layout.align() <= MIN_ALIGN { - HeapAlloc(GetProcessHeap(), flags, layout.size()) - } else { - let size = layout.size() + layout.align(); - let ptr = HeapAlloc(GetProcessHeap(), flags, size); - if ptr.is_null() { - ptr - } else { - align_ptr(ptr, layout.align()) - } - }; - ptr as *mut u8 - } - #[stable(feature = "alloc_system_type", since = "1.28.0")] - unsafe impl GlobalAlloc for System { - #[inline] - unsafe fn alloc(&self, layout: Layout) -> *mut u8 { - allocate_with_flags(layout, 0) - } - #[inline] - unsafe fn alloc_zeroed(&self, layout: Layout) -> *mut u8 { - allocate_with_flags(layout, HEAP_ZERO_MEMORY) - } - #[inline] - unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) { - if layout.align() <= MIN_ALIGN { - let err = HeapFree(GetProcessHeap(), 0, ptr as LPVOID); - debug_assert!(err != 0, "Failed to free heap memory: {}", - GetLastError()); - } else { - let header = get_header(ptr); - let err = HeapFree(GetProcessHeap(), 0, header.0 as LPVOID); - debug_assert!(err != 0, "Failed to free heap memory: {}", - GetLastError()); - } - } - #[inline] - unsafe fn realloc(&self, ptr: *mut u8, layout: Layout, new_size: usize) -> *mut u8 { - if layout.align() <= MIN_ALIGN { - HeapReAlloc(GetProcessHeap(), 0, ptr as LPVOID, new_size) as *mut u8 - } else { - self.realloc_fallback(ptr, layout, new_size) - } - } - } -} -// This is an implementation of a global allocator on the wasm32 platform when -// emscripten is not in use. In that situation there's no actual runtime for us -// to lean on for allocation, so instead we provide our own! -// -// The wasm32 instruction set has two instructions for getting the current -// amount of memory and growing the amount of memory. These instructions are the -// foundation on which we're able to build an allocator, so we do so! Note that -// the instructions are also pretty "global" and this is the "global" allocator -// after all! -// -// The current allocator here is the `dlmalloc` crate which we've got included -// in the rust-lang/rust repository as a submodule. The crate is a port of -// dlmalloc.c from C to Rust and is basically just so we can have "pure Rust" -// for now which is currently technically required (can't link with C yet). -// -// The crate itself provides a global allocator which on wasm has no -// synchronization as there are no threads! -#[cfg(all(target_arch = "wasm32", not(target_os = "emscripten")))] -mod platform { - extern crate dlmalloc; - use core::alloc::{GlobalAlloc, Layout}; - use System; - static mut DLMALLOC: dlmalloc::Dlmalloc = dlmalloc::DLMALLOC_INIT; - #[stable(feature = "alloc_system_type", since = "1.28.0")] - unsafe impl GlobalAlloc for System { - #[inline] - unsafe fn alloc(&self, layout: Layout) -> *mut u8 { - let _lock = lock::lock(); - DLMALLOC.malloc(layout.size(), layout.align()) - } - #[inline] - unsafe fn alloc_zeroed(&self, layout: Layout) -> *mut u8 { - let _lock = lock::lock(); - DLMALLOC.calloc(layout.size(), layout.align()) - } - #[inline] - unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) { - let _lock = lock::lock(); - DLMALLOC.free(ptr, layout.size(), layout.align()) - } - #[inline] - unsafe fn realloc(&self, ptr: *mut u8, layout: Layout, new_size: usize) -> *mut u8 { - let _lock = lock::lock(); - DLMALLOC.realloc(ptr, layout.size(), layout.align(), new_size) - } - } - #[cfg(target_feature = "atomics")] - mod lock { - use core::arch::wasm32; - use core::sync::atomic::{AtomicI32, Ordering::SeqCst}; - static LOCKED: AtomicI32 = AtomicI32::new(0); - pub struct DropLock; - pub fn lock() -> DropLock { - loop { - if LOCKED.swap(1, SeqCst) == 0 { - return DropLock - } - unsafe { - let r = wasm32::atomic::wait_i32( - &LOCKED as *const AtomicI32 as *mut i32, - 1, // expected value - -1, // timeout - ); - debug_assert!(r == 0 || r == 1); - } - } - } - impl Drop for DropLock { - fn drop(&mut self) { - let r = LOCKED.swap(0, SeqCst); - debug_assert_eq!(r, 1); - unsafe { - wasm32::atomic::wake( - &LOCKED as *const AtomicI32 as *mut i32, - 1, // only one thread - ); - } - } - } - } - #[cfg(not(target_feature = "atomics"))] - mod lock { - #[inline] - pub fn lock() {} // no atomics, no threads, that's easy! - } -} diff --git a/compiler/rustc_codegen_cranelift/build_sysroot/build_sysroot.sh b/compiler/rustc_codegen_cranelift/build_sysroot/build_sysroot.sh index 282ce4a582c4b..0354304e55bf7 100755 --- a/compiler/rustc_codegen_cranelift/build_sysroot/build_sysroot.sh +++ b/compiler/rustc_codegen_cranelift/build_sysroot/build_sysroot.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # Requires the CHANNEL env var to be set to `debug` or `release.` @@ -28,7 +28,7 @@ export __CARGO_DEFAULT_LIB_METADATA="cg_clif" if [[ "$1" != "--debug" ]]; then sysroot_channel='release' # FIXME Enable incremental again once rust-lang/rust#74946 is fixed - CARGO_INCREMENTAL=0 RUSTFLAGS="$RUSTFLAGS -Zmir-opt-level=2" cargo build --target "$TARGET_TRIPLE" --release + CARGO_INCREMENTAL=0 RUSTFLAGS="$RUSTFLAGS -Zmir-opt-level=3" cargo build --target "$TARGET_TRIPLE" --release else sysroot_channel='debug' cargo build --target "$TARGET_TRIPLE" diff --git a/compiler/rustc_codegen_cranelift/build_sysroot/prepare_sysroot_src.sh b/compiler/rustc_codegen_cranelift/build_sysroot/prepare_sysroot_src.sh index d3b87e02ba891..c90205db0fbd0 100755 --- a/compiler/rustc_codegen_cranelift/build_sysroot/prepare_sysroot_src.sh +++ b/compiler/rustc_codegen_cranelift/build_sysroot/prepare_sysroot_src.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash set -e cd "$(dirname "$0")" @@ -33,7 +33,7 @@ git clone https://github.com/rust-lang/compiler-builtins.git || echo "rust-lang/ pushd compiler-builtins git checkout -- . git checkout 0.1.39 -git apply ../../crate_patches/0001-compiler-builtins-Remove-rotate_left-from-Int.patch +git apply ../../crate_patches/000*-compiler-builtins-*.patch popd echo "Successfully prepared sysroot source for building" diff --git a/compiler/rustc_codegen_cranelift/clean_all.sh b/compiler/rustc_codegen_cranelift/clean_all.sh index b47efe72bce03..a7bbeb05cac5a 100755 --- a/compiler/rustc_codegen_cranelift/clean_all.sh +++ b/compiler/rustc_codegen_cranelift/clean_all.sh @@ -1,4 +1,4 @@ -#!/bin/bash --verbose +#!/usr/bin/env bash set -e rm -rf target/ build/ build_sysroot/{sysroot_src/,target/,compiler-builtins/} perf.data{,.old} diff --git a/compiler/rustc_codegen_cranelift/crate_patches/0002-compiler-builtins-Disable-128bit-atomic-operations.patch b/compiler/rustc_codegen_cranelift/crate_patches/0002-compiler-builtins-Disable-128bit-atomic-operations.patch new file mode 100644 index 0000000000000..7daea99f5794d --- /dev/null +++ b/compiler/rustc_codegen_cranelift/crate_patches/0002-compiler-builtins-Disable-128bit-atomic-operations.patch @@ -0,0 +1,48 @@ +From 1d574bf5e32d51641dcacaf8ef777e95b44f6f2a Mon Sep 17 00:00:00 2001 +From: bjorn3 +Date: Thu, 18 Feb 2021 18:30:55 +0100 +Subject: [PATCH] Disable 128bit atomic operations + +Cranelift doesn't support them yet +--- + src/mem/mod.rs | 12 ------------ + 1 file changed, 12 deletions(-) + +diff --git a/src/mem/mod.rs b/src/mem/mod.rs +index 107762c..2d1ae10 100644 +--- a/src/mem/mod.rs ++++ b/src/mem/mod.rs +@@ -137,10 +137,6 @@ intrinsics! { + pub extern "C" fn __llvm_memcpy_element_unordered_atomic_8(dest: *mut u64, src: *const u64, bytes: usize) -> () { + memcpy_element_unordered_atomic(dest, src, bytes); + } +- #[cfg(target_has_atomic_load_store = "128")] +- pub extern "C" fn __llvm_memcpy_element_unordered_atomic_16(dest: *mut u128, src: *const u128, bytes: usize) -> () { +- memcpy_element_unordered_atomic(dest, src, bytes); +- } + + #[cfg(target_has_atomic_load_store = "8")] + pub extern "C" fn __llvm_memmove_element_unordered_atomic_1(dest: *mut u8, src: *const u8, bytes: usize) -> () { +@@ -158,10 +154,6 @@ intrinsics! { + pub extern "C" fn __llvm_memmove_element_unordered_atomic_8(dest: *mut u64, src: *const u64, bytes: usize) -> () { + memmove_element_unordered_atomic(dest, src, bytes); + } +- #[cfg(target_has_atomic_load_store = "128")] +- pub extern "C" fn __llvm_memmove_element_unordered_atomic_16(dest: *mut u128, src: *const u128, bytes: usize) -> () { +- memmove_element_unordered_atomic(dest, src, bytes); +- } + + #[cfg(target_has_atomic_load_store = "8")] + pub extern "C" fn __llvm_memset_element_unordered_atomic_1(s: *mut u8, c: u8, bytes: usize) -> () { +@@ -179,8 +171,4 @@ intrinsics! { + pub extern "C" fn __llvm_memset_element_unordered_atomic_8(s: *mut u64, c: u8, bytes: usize) -> () { + memset_element_unordered_atomic(s, c, bytes); + } +- #[cfg(target_has_atomic_load_store = "128")] +- pub extern "C" fn __llvm_memset_element_unordered_atomic_16(s: *mut u128, c: u8, bytes: usize) -> () { +- memset_element_unordered_atomic(s, c, bytes); +- } + } +-- +2.26.2.7.g19db9cfb68 + diff --git a/compiler/rustc_codegen_cranelift/docs/env_vars.md b/compiler/rustc_codegen_cranelift/docs/env_vars.md index f0a0a6ad42ef5..f7fde1b4f3a87 100644 --- a/compiler/rustc_codegen_cranelift/docs/env_vars.md +++ b/compiler/rustc_codegen_cranelift/docs/env_vars.md @@ -8,5 +8,8 @@ to make it possible to use incremental mode for all analyses performed by rustc without caching object files when their content should have been changed by a change to cg_clif.
CG_CLIF_DISPLAY_CG_TIME
-
If "1", display the time it took to perform codegen for a crate
+
If "1", display the time it took to perform codegen for a crate.
+
CG_CLIF_ENABLE_VERIFIER
+
Enable the Cranelift ir verifier for all compilation passes. If not set it will only run once + before passing the clif ir to Cranelift for compilation. diff --git a/compiler/rustc_codegen_cranelift/docs/usage.md b/compiler/rustc_codegen_cranelift/docs/usage.md new file mode 100644 index 0000000000000..3eee3b554e3b6 --- /dev/null +++ b/compiler/rustc_codegen_cranelift/docs/usage.md @@ -0,0 +1,66 @@ +# Usage + +rustc_codegen_cranelift can be used as a near-drop-in replacement for `cargo build` or `cargo run` for existing projects. + +Assuming `$cg_clif_dir` is the directory you cloned this repo into and you followed the instructions (`prepare.sh` and `build.sh` or `test.sh`). + +## Cargo + +In the directory with your project (where you can do the usual `cargo build`), run: + +```bash +$ $cg_clif_dir/build/cargo.sh build +``` + +This will build your project with rustc_codegen_cranelift instead of the usual LLVM backend. + +## Rustc + +> You should prefer using the Cargo method. + +```bash +$ $cg_clif_dir/build/bin/cg_clif my_crate.rs +``` + +## Jit mode + +In jit mode cg_clif will immediately execute your code without creating an executable file. + +> This requires all dependencies to be available as dynamic library. +> The jit mode will probably need cargo integration to make this possible. + +```bash +$ $cg_clif_dir/build/cargo.sh jit +``` + +or + +```bash +$ $cg_clif_dir/build/bin/cg_clif -Cllvm-args=mode=jit -Cprefer-dynamic my_crate.rs +``` + +There is also an experimental lazy jit mode. In this mode functions are only compiled once they are +first called. It currently does not work with multi-threaded programs. When a not yet compiled +function is called from another thread than the main thread, you will get an ICE. + +```bash +$ $cg_clif_dir/build/cargo.sh lazy-jit +``` + +## Shell + +These are a few functions that allow you to easily run rust code from the shell using cg_clif as jit. + +```bash +function jit_naked() { + echo "$@" | $cg_clif_dir/build/bin/cg_clif - -Cllvm-args=mode=jit -Cprefer-dynamic +} + +function jit() { + jit_naked "fn main() { $@ }" +} + +function jit_calc() { + jit 'println!("0x{:x}", ' $@ ');'; +} +``` diff --git a/compiler/rustc_codegen_cranelift/example/alloc_example.rs b/compiler/rustc_codegen_cranelift/example/alloc_example.rs index f59600ebb330c..71e93e87b6c41 100644 --- a/compiler/rustc_codegen_cranelift/example/alloc_example.rs +++ b/compiler/rustc_codegen_cranelift/example/alloc_example.rs @@ -1,4 +1,4 @@ -#![feature(start, box_syntax, alloc_system, core_intrinsics, alloc_prelude, alloc_error_handler)] +#![feature(start, box_syntax, core_intrinsics, alloc_prelude, alloc_error_handler)] #![no_std] extern crate alloc; diff --git a/compiler/rustc_codegen_cranelift/example/alloc_system.rs b/compiler/rustc_codegen_cranelift/example/alloc_system.rs new file mode 100644 index 0000000000000..5f66ca67f2d40 --- /dev/null +++ b/compiler/rustc_codegen_cranelift/example/alloc_system.rs @@ -0,0 +1,212 @@ +// Copyright 2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. +#![no_std] +#![feature(allocator_api, rustc_private)] +#![cfg_attr(any(unix, target_os = "redox"), feature(libc))] + +// The minimum alignment guaranteed by the architecture. This value is used to +// add fast paths for low alignment values. +#[cfg(all(any(target_arch = "x86", + target_arch = "arm", + target_arch = "mips", + target_arch = "powerpc", + target_arch = "powerpc64")))] +const MIN_ALIGN: usize = 8; +#[cfg(all(any(target_arch = "x86_64", + target_arch = "aarch64", + target_arch = "mips64", + target_arch = "s390x", + target_arch = "sparc64")))] +const MIN_ALIGN: usize = 16; + +pub struct System; +#[cfg(any(windows, unix, target_os = "redox"))] +mod realloc_fallback { + use core::alloc::{GlobalAlloc, Layout}; + use core::cmp; + use core::ptr; + impl super::System { + pub(crate) unsafe fn realloc_fallback(&self, ptr: *mut u8, old_layout: Layout, + new_size: usize) -> *mut u8 { + // Docs for GlobalAlloc::realloc require this to be valid: + let new_layout = Layout::from_size_align_unchecked(new_size, old_layout.align()); + let new_ptr = GlobalAlloc::alloc(self, new_layout); + if !new_ptr.is_null() { + let size = cmp::min(old_layout.size(), new_size); + ptr::copy_nonoverlapping(ptr, new_ptr, size); + GlobalAlloc::dealloc(self, ptr, old_layout); + } + new_ptr + } + } +} +#[cfg(any(unix, target_os = "redox"))] +mod platform { + extern crate libc; + use core::ptr; + use MIN_ALIGN; + use System; + use core::alloc::{GlobalAlloc, Layout}; + unsafe impl GlobalAlloc for System { + #[inline] + unsafe fn alloc(&self, layout: Layout) -> *mut u8 { + if layout.align() <= MIN_ALIGN && layout.align() <= layout.size() { + libc::malloc(layout.size()) as *mut u8 + } else { + #[cfg(target_os = "macos")] + { + if layout.align() > (1 << 31) { + return ptr::null_mut() + } + } + aligned_malloc(&layout) + } + } + #[inline] + unsafe fn alloc_zeroed(&self, layout: Layout) -> *mut u8 { + if layout.align() <= MIN_ALIGN && layout.align() <= layout.size() { + libc::calloc(layout.size(), 1) as *mut u8 + } else { + let ptr = self.alloc(layout.clone()); + if !ptr.is_null() { + ptr::write_bytes(ptr, 0, layout.size()); + } + ptr + } + } + #[inline] + unsafe fn dealloc(&self, ptr: *mut u8, _layout: Layout) { + libc::free(ptr as *mut libc::c_void) + } + #[inline] + unsafe fn realloc(&self, ptr: *mut u8, layout: Layout, new_size: usize) -> *mut u8 { + if layout.align() <= MIN_ALIGN && layout.align() <= new_size { + libc::realloc(ptr as *mut libc::c_void, new_size) as *mut u8 + } else { + self.realloc_fallback(ptr, layout, new_size) + } + } + } + #[cfg(any(target_os = "android", + target_os = "hermit", + target_os = "redox", + target_os = "solaris"))] + #[inline] + unsafe fn aligned_malloc(layout: &Layout) -> *mut u8 { + // On android we currently target API level 9 which unfortunately + // doesn't have the `posix_memalign` API used below. Instead we use + // `memalign`, but this unfortunately has the property on some systems + // where the memory returned cannot be deallocated by `free`! + // + // Upon closer inspection, however, this appears to work just fine with + // Android, so for this platform we should be fine to call `memalign` + // (which is present in API level 9). Some helpful references could + // possibly be chromium using memalign [1], attempts at documenting that + // memalign + free is ok [2] [3], or the current source of chromium + // which still uses memalign on android [4]. + // + // [1]: https://codereview.chromium.org/10796020/ + // [2]: https://code.google.com/p/android/issues/detail?id=35391 + // [3]: https://bugs.chromium.org/p/chromium/issues/detail?id=138579 + // [4]: https://chromium.googlesource.com/chromium/src/base/+/master/ + // /memory/aligned_memory.cc + libc::memalign(layout.align(), layout.size()) as *mut u8 + } + #[cfg(not(any(target_os = "android", + target_os = "hermit", + target_os = "redox", + target_os = "solaris")))] + #[inline] + unsafe fn aligned_malloc(layout: &Layout) -> *mut u8 { + let mut out = ptr::null_mut(); + let ret = libc::posix_memalign(&mut out, layout.align(), layout.size()); + if ret != 0 { + ptr::null_mut() + } else { + out as *mut u8 + } + } +} +#[cfg(windows)] +#[allow(nonstandard_style)] +mod platform { + use MIN_ALIGN; + use System; + use core::alloc::{GlobalAlloc, Layout}; + type LPVOID = *mut u8; + type HANDLE = LPVOID; + type SIZE_T = usize; + type DWORD = u32; + type BOOL = i32; + extern "system" { + fn GetProcessHeap() -> HANDLE; + fn HeapAlloc(hHeap: HANDLE, dwFlags: DWORD, dwBytes: SIZE_T) -> LPVOID; + fn HeapReAlloc(hHeap: HANDLE, dwFlags: DWORD, lpMem: LPVOID, dwBytes: SIZE_T) -> LPVOID; + fn HeapFree(hHeap: HANDLE, dwFlags: DWORD, lpMem: LPVOID) -> BOOL; + fn GetLastError() -> DWORD; + } + #[repr(C)] + struct Header(*mut u8); + const HEAP_ZERO_MEMORY: DWORD = 0x00000008; + unsafe fn get_header<'a>(ptr: *mut u8) -> &'a mut Header { + &mut *(ptr as *mut Header).offset(-1) + } + unsafe fn align_ptr(ptr: *mut u8, align: usize) -> *mut u8 { + let aligned = ptr.add(align - (ptr as usize & (align - 1))); + *get_header(aligned) = Header(ptr); + aligned + } + #[inline] + unsafe fn allocate_with_flags(layout: Layout, flags: DWORD) -> *mut u8 { + let ptr = if layout.align() <= MIN_ALIGN { + HeapAlloc(GetProcessHeap(), flags, layout.size()) + } else { + let size = layout.size() + layout.align(); + let ptr = HeapAlloc(GetProcessHeap(), flags, size); + if ptr.is_null() { + ptr + } else { + align_ptr(ptr, layout.align()) + } + }; + ptr as *mut u8 + } + unsafe impl GlobalAlloc for System { + #[inline] + unsafe fn alloc(&self, layout: Layout) -> *mut u8 { + allocate_with_flags(layout, 0) + } + #[inline] + unsafe fn alloc_zeroed(&self, layout: Layout) -> *mut u8 { + allocate_with_flags(layout, HEAP_ZERO_MEMORY) + } + #[inline] + unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) { + if layout.align() <= MIN_ALIGN { + let err = HeapFree(GetProcessHeap(), 0, ptr as LPVOID); + debug_assert!(err != 0, "Failed to free heap memory: {}", + GetLastError()); + } else { + let header = get_header(ptr); + let err = HeapFree(GetProcessHeap(), 0, header.0 as LPVOID); + debug_assert!(err != 0, "Failed to free heap memory: {}", + GetLastError()); + } + } + #[inline] + unsafe fn realloc(&self, ptr: *mut u8, layout: Layout, new_size: usize) -> *mut u8 { + if layout.align() <= MIN_ALIGN { + HeapReAlloc(GetProcessHeap(), 0, ptr as LPVOID, new_size) as *mut u8 + } else { + self.realloc_fallback(ptr, layout, new_size) + } + } + } +} diff --git a/compiler/rustc_codegen_cranelift/example/arbitrary_self_types_pointers_and_wrappers.rs b/compiler/rustc_codegen_cranelift/example/arbitrary_self_types_pointers_and_wrappers.rs index 0b0039a1370a4..ddeb752f93ed7 100644 --- a/compiler/rustc_codegen_cranelift/example/arbitrary_self_types_pointers_and_wrappers.rs +++ b/compiler/rustc_codegen_cranelift/example/arbitrary_self_types_pointers_and_wrappers.rs @@ -1,22 +1,12 @@ // Adapted from rustc run-pass test suite -#![feature(no_core, arbitrary_self_types, box_syntax)] +#![feature(arbitrary_self_types, unsize, coerce_unsized, dispatch_from_dyn)] #![feature(rustc_attrs)] -#![feature(start, lang_items)] -#![no_core] - -extern crate mini_core; - -use mini_core::*; - -macro_rules! assert_eq { - ($l:expr, $r: expr) => { - if $l != $r { - panic(stringify!($l != $r)); - } - } -} +use std::{ + ops::{Deref, CoerceUnsized, DispatchFromDyn}, + marker::Unsize, +}; struct Ptr(Box); @@ -67,16 +57,13 @@ impl Trait for i32 { } } -#[start] -fn main(_: isize, _: *const *const u8) -> isize { - let pw = Ptr(box Wrapper(5)) as Ptr>; +fn main() { + let pw = Ptr(Box::new(Wrapper(5))) as Ptr>; assert_eq!(pw.ptr_wrapper(), 5); - let wp = Wrapper(Ptr(box 6)) as Wrapper>; + let wp = Wrapper(Ptr(Box::new(6))) as Wrapper>; assert_eq!(wp.wrapper_ptr(), 6); - let wpw = Wrapper(Ptr(box Wrapper(7))) as Wrapper>>; + let wpw = Wrapper(Ptr(Box::new(Wrapper(7)))) as Wrapper>>; assert_eq!(wpw.wrapper_ptr_wrapper(), 7); - - 0 } diff --git a/compiler/rustc_codegen_cranelift/example/mini_core.rs b/compiler/rustc_codegen_cranelift/example/mini_core.rs index 002ec7e2e3d7a..c4834c8040871 100644 --- a/compiler/rustc_codegen_cranelift/example/mini_core.rs +++ b/compiler/rustc_codegen_cranelift/example/mini_core.rs @@ -365,6 +365,22 @@ impl PartialEq for Option { } } +#[lang = "shl"] +pub trait Shl { + type Output; + + #[must_use] + fn shl(self, rhs: RHS) -> Self::Output; +} + +impl Shl for u128 { + type Output = u128; + + fn shl(self, rhs: u128) -> u128 { + self << rhs + } +} + #[lang = "neg"] pub trait Neg { type Output; @@ -605,6 +621,7 @@ struct PanicLocation { } #[no_mangle] +#[cfg(not(windows))] pub fn get_tls() -> u8 { #[thread_local] static A: u8 = 42; diff --git a/compiler/rustc_codegen_cranelift/example/mini_core_hello_world.rs b/compiler/rustc_codegen_cranelift/example/mini_core_hello_world.rs index 4a8375afac3ce..ea37ca98b59a7 100644 --- a/compiler/rustc_codegen_cranelift/example/mini_core_hello_world.rs +++ b/compiler/rustc_codegen_cranelift/example/mini_core_hello_world.rs @@ -1,7 +1,4 @@ -#![feature( - no_core, start, lang_items, box_syntax, never_type, linkage, - extern_types, thread_local -)] +#![feature(no_core, lang_items, box_syntax, never_type, linkage, extern_types, thread_local)] #![no_core] #![allow(dead_code, non_camel_case_types)] @@ -239,7 +236,7 @@ fn main() { assert_eq!(((|()| 42u8) as fn(()) -> u8)(()), 42); - #[cfg(not(jit))] + #[cfg(not(any(jit, windows)))] { extern { #[linkage = "extern_weak"] @@ -264,6 +261,9 @@ fn main() { assert_eq!(f2 as i8, -128); assert_eq!(f2 as u8, 0); + let amount = 0; + assert_eq!(1u128 << amount, 1); + static ANOTHER_STATIC: &u8 = &A_STATIC; assert_eq!(*ANOTHER_STATIC, 42); @@ -289,7 +289,7 @@ fn main() { from_decimal_string(); - #[cfg(not(jit))] + #[cfg(not(any(jit, windows)))] test_tls(); #[cfg(all(not(jit), target_os = "linux"))] diff --git a/compiler/rustc_codegen_cranelift/patches/0022-core-Disable-not-compiling-tests.patch b/compiler/rustc_codegen_cranelift/patches/0022-core-Disable-not-compiling-tests.patch index 3eb10069adad6..8cfffe580a1f0 100644 --- a/compiler/rustc_codegen_cranelift/patches/0022-core-Disable-not-compiling-tests.patch +++ b/compiler/rustc_codegen_cranelift/patches/0022-core-Disable-not-compiling-tests.patch @@ -119,21 +119,5 @@ index 6609bc3..241b497 100644 #[test] #[should_panic(expected = "index 0 greater than length of slice")] -diff --git a/library/core/tests/num/ops.rs b/library/core/tests/num/ops.rs -index 9979cc8..d5d1d83 100644 ---- a/library/core/tests/num/ops.rs -+++ b/library/core/tests/num/ops.rs -@@ -238,7 +238,7 @@ macro_rules! test_shift_assign { - } - }; - } --test_shift!(test_shl_defined, Shl::shl); --test_shift_assign!(test_shl_assign_defined, ShlAssign::shl_assign); --test_shift!(test_shr_defined, Shr::shr); --test_shift_assign!(test_shr_assign_defined, ShrAssign::shr_assign); -+//test_shift!(test_shl_defined, Shl::shl); -+//test_shift_assign!(test_shl_assign_defined, ShlAssign::shl_assign); -+//test_shift!(test_shr_defined, Shr::shr); -+//test_shift_assign!(test_shr_assign_defined, ShrAssign::shr_assign); -- 2.21.0 (Apple Git-122) diff --git a/compiler/rustc_codegen_cranelift/patches/0027-Disable-128bit-atomic-operations.patch b/compiler/rustc_codegen_cranelift/patches/0027-Disable-128bit-atomic-operations.patch new file mode 100644 index 0000000000000..32e5930969061 --- /dev/null +++ b/compiler/rustc_codegen_cranelift/patches/0027-Disable-128bit-atomic-operations.patch @@ -0,0 +1,103 @@ +From 894e07dfec2624ba539129b1c1d63e1d7d812bda Mon Sep 17 00:00:00 2001 +From: bjorn3 +Date: Thu, 18 Feb 2021 18:45:28 +0100 +Subject: [PATCH] Disable 128bit atomic operations + +Cranelift doesn't support them yet +--- + library/core/src/sync/atomic.rs | 38 --------------------------------- + library/core/tests/atomic.rs | 4 ---- + library/std/src/panic.rs | 6 ------ + 3 files changed, 48 deletions(-) + +diff --git a/library/core/src/sync/atomic.rs b/library/core/src/sync/atomic.rs +index 81c9e1d..65c9503 100644 +--- a/library/core/src/sync/atomic.rs ++++ b/library/core/src/sync/atomic.rs +@@ -2228,44 +2228,6 @@ atomic_int! { + "AtomicU64::new(0)", + u64 AtomicU64 ATOMIC_U64_INIT + } +-#[cfg(target_has_atomic_load_store = "128")] +-atomic_int! { +- cfg(target_has_atomic = "128"), +- cfg(target_has_atomic_equal_alignment = "128"), +- unstable(feature = "integer_atomics", issue = "32976"), +- unstable(feature = "integer_atomics", issue = "32976"), +- unstable(feature = "integer_atomics", issue = "32976"), +- unstable(feature = "integer_atomics", issue = "32976"), +- unstable(feature = "integer_atomics", issue = "32976"), +- unstable(feature = "integer_atomics", issue = "32976"), +- rustc_const_stable(feature = "const_integer_atomics", since = "1.34.0"), +- unstable(feature = "integer_atomics", issue = "32976"), +- "i128", +- "#![feature(integer_atomics)]\n\n", +- atomic_min, atomic_max, +- 16, +- "AtomicI128::new(0)", +- i128 AtomicI128 ATOMIC_I128_INIT +-} +-#[cfg(target_has_atomic_load_store = "128")] +-atomic_int! { +- cfg(target_has_atomic = "128"), +- cfg(target_has_atomic_equal_alignment = "128"), +- unstable(feature = "integer_atomics", issue = "32976"), +- unstable(feature = "integer_atomics", issue = "32976"), +- unstable(feature = "integer_atomics", issue = "32976"), +- unstable(feature = "integer_atomics", issue = "32976"), +- unstable(feature = "integer_atomics", issue = "32976"), +- unstable(feature = "integer_atomics", issue = "32976"), +- rustc_const_stable(feature = "const_integer_atomics", since = "1.34.0"), +- unstable(feature = "integer_atomics", issue = "32976"), +- "u128", +- "#![feature(integer_atomics)]\n\n", +- atomic_umin, atomic_umax, +- 16, +- "AtomicU128::new(0)", +- u128 AtomicU128 ATOMIC_U128_INIT +-} + + macro_rules! atomic_int_ptr_sized { + ( $($target_pointer_width:literal $align:literal)* ) => { $( +diff --git a/library/core/tests/atomic.rs b/library/core/tests/atomic.rs +index 2d1e449..cb6da5d 100644 +--- a/library/core/tests/atomic.rs ++++ b/library/core/tests/atomic.rs +@@ -145,10 +145,6 @@ fn atomic_alignment() { + assert_eq!(align_of::(), size_of::()); + #[cfg(target_has_atomic = "64")] + assert_eq!(align_of::(), size_of::()); +- #[cfg(target_has_atomic = "128")] +- assert_eq!(align_of::(), size_of::()); +- #[cfg(target_has_atomic = "128")] +- assert_eq!(align_of::(), size_of::()); + #[cfg(target_has_atomic = "ptr")] + assert_eq!(align_of::(), size_of::()); + #[cfg(target_has_atomic = "ptr")] +diff --git a/library/std/src/panic.rs b/library/std/src/panic.rs +index 89a822a..779fd88 100644 +--- a/library/std/src/panic.rs ++++ b/library/std/src/panic.rs +@@ -279,9 +279,6 @@ impl RefUnwindSafe for atomic::AtomicI32 {} + #[cfg(target_has_atomic_load_store = "64")] + #[stable(feature = "integer_atomics_stable", since = "1.34.0")] + impl RefUnwindSafe for atomic::AtomicI64 {} +-#[cfg(target_has_atomic_load_store = "128")] +-#[unstable(feature = "integer_atomics", issue = "32976")] +-impl RefUnwindSafe for atomic::AtomicI128 {} + + #[cfg(target_has_atomic_load_store = "ptr")] + #[stable(feature = "unwind_safe_atomic_refs", since = "1.14.0")] +@@ -298,9 +295,6 @@ impl RefUnwindSafe for atomic::AtomicU32 {} + #[cfg(target_has_atomic_load_store = "64")] + #[stable(feature = "integer_atomics_stable", since = "1.34.0")] + impl RefUnwindSafe for atomic::AtomicU64 {} +-#[cfg(target_has_atomic_load_store = "128")] +-#[unstable(feature = "integer_atomics", issue = "32976")] +-impl RefUnwindSafe for atomic::AtomicU128 {} + + #[cfg(target_has_atomic_load_store = "8")] + #[stable(feature = "unwind_safe_atomic_refs", since = "1.14.0")] +-- +2.26.2.7.g19db9cfb68 + diff --git a/compiler/rustc_codegen_cranelift/prepare.sh b/compiler/rustc_codegen_cranelift/prepare.sh index 08e7cb1802999..64c097261c908 100755 --- a/compiler/rustc_codegen_cranelift/prepare.sh +++ b/compiler/rustc_codegen_cranelift/prepare.sh @@ -1,7 +1,6 @@ -#!/bin/bash --verbose +#!/usr/bin/env bash set -e -rustup component add rust-src rustc-dev llvm-tools-preview ./build_sysroot/prepare_sysroot_src.sh cargo install hyperfine || echo "Skipping hyperfine install" diff --git a/compiler/rustc_codegen_cranelift/rust-toolchain b/compiler/rustc_codegen_cranelift/rust-toolchain index a08f00d19c20f..2917fc7ee396d 100644 --- a/compiler/rustc_codegen_cranelift/rust-toolchain +++ b/compiler/rustc_codegen_cranelift/rust-toolchain @@ -1 +1,3 @@ -nightly-2021-01-30 +[toolchain] +channel = "nightly-2021-03-29" +components = ["rust-src", "rustc-dev", "llvm-tools-preview"] diff --git a/compiler/rustc_codegen_cranelift/rustfmt.toml b/compiler/rustc_codegen_cranelift/rustfmt.toml new file mode 100644 index 0000000000000..2bd8f7d1bc15d --- /dev/null +++ b/compiler/rustc_codegen_cranelift/rustfmt.toml @@ -0,0 +1,4 @@ +# Matches rustfmt.toml of rustc +version = "Two" +use_small_heuristics = "Max" +merge_derives = false diff --git a/compiler/rustc_codegen_cranelift/scripts/cargo.sh b/compiler/rustc_codegen_cranelift/scripts/cargo.sh index a3d6d303057b8..1daa5a78f7bd2 100755 --- a/compiler/rustc_codegen_cranelift/scripts/cargo.sh +++ b/compiler/rustc_codegen_cranelift/scripts/cargo.sh @@ -1,10 +1,10 @@ -#!/bin/bash +#!/usr/bin/env bash dir=$(dirname "$0") source "$dir/config.sh" # read nightly compiler from rust-toolchain file -TOOLCHAIN=$(cat "$dir/rust-toolchain") +TOOLCHAIN=$(cat "$dir/rust-toolchain" | grep channel | sed "s/channel = \"\(.*\)\"/\1/") cmd=$1 shift || true diff --git a/compiler/rustc_codegen_cranelift/scripts/config.sh b/compiler/rustc_codegen_cranelift/scripts/config.sh index 834708aa9a6fb..99b302ee1d94b 100644 --- a/compiler/rustc_codegen_cranelift/scripts/config.sh +++ b/compiler/rustc_codegen_cranelift/scripts/config.sh @@ -2,15 +2,7 @@ set -e -unamestr=$(uname) -if [[ "$unamestr" == 'Linux' ]]; then - dylib_ext='so' -elif [[ "$unamestr" == 'Darwin' ]]; then - dylib_ext='dylib' -else - echo "Unsupported os" - exit 1 -fi +dylib=$(echo "" | rustc --print file-names --crate-type dylib --crate-name rustc_codegen_cranelift -) if echo "$RUSTC_WRAPPER" | grep sccache; then echo @@ -24,10 +16,10 @@ dir=$(cd "$(dirname "${BASH_SOURCE[0]}")"; pwd) export RUSTC=$dir"/bin/cg_clif" export RUSTDOCFLAGS=$linker' -Cpanic=abort -Zpanic-abort-tests '\ -'-Zcodegen-backend='$dir'/lib/librustc_codegen_cranelift.'$dylib_ext' --sysroot '$dir +'-Zcodegen-backend='$dir'/lib/'$dylib' --sysroot '$dir -# FIXME remove once the atomic shim is gone -if [[ "$unamestr" == 'Darwin' ]]; then +# FIXME fix `#[linkage = "extern_weak"]` without this +if [[ "$(uname)" == 'Darwin' ]]; then export RUSTFLAGS="$RUSTFLAGS -Clink-arg=-undefined -Clink-arg=dynamic_lookup" fi diff --git a/compiler/rustc_codegen_cranelift/scripts/rustup.sh b/compiler/rustc_codegen_cranelift/scripts/rustup.sh index 430f5c469b4b2..fa7557653d879 100755 --- a/compiler/rustc_codegen_cranelift/scripts/rustup.sh +++ b/compiler/rustc_codegen_cranelift/scripts/rustup.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash set -e @@ -8,7 +8,7 @@ case $1 in echo "=> Installing new nightly" rustup toolchain install --profile minimal "nightly-${TOOLCHAIN}" # Sanity check to see if the nightly exists - echo "nightly-${TOOLCHAIN}" > rust-toolchain + sed -i "s/\"nightly-.*\"/\"nightly-${TOOLCHAIN}\"/" rust-toolchain rustup component add rustfmt || true echo "=> Uninstalling all old nighlies" diff --git a/compiler/rustc_codegen_cranelift/scripts/setup_rust_fork.sh b/compiler/rustc_codegen_cranelift/scripts/setup_rust_fork.sh new file mode 100644 index 0000000000000..e8bedf625f796 --- /dev/null +++ b/compiler/rustc_codegen_cranelift/scripts/setup_rust_fork.sh @@ -0,0 +1,68 @@ +#!/bin/bash +set -e + +./build.sh +source build/config.sh + +echo "[SETUP] Rust fork" +git clone https://github.com/rust-lang/rust.git || true +pushd rust +git fetch +git checkout -- . +git checkout "$(rustc -V | cut -d' ' -f3 | tr -d '(')" + +git apply - < config.toml < config.toml < res.txt - diff -u res.txt examples/regexdna-output.txt + ../build/cargo.sh build --example shootout-regex-dna --target $TARGET_TRIPLE + if [[ "$HOST_TRIPLE" = "$TARGET_TRIPLE" ]]; then + cat examples/regexdna-input.txt \ + | ../build/cargo.sh run --example shootout-regex-dna --target $TARGET_TRIPLE \ + | grep -v "Spawned thread" > res.txt + diff -u res.txt examples/regexdna-output.txt + fi - echo "[TEST] rust-lang/regex tests" - ../build/cargo.sh test --tests -- --exclude-should-panic --test-threads 1 -Zunstable-options -q + if [[ "$HOST_TRIPLE" = "$TARGET_TRIPLE" ]]; then + echo "[TEST] rust-lang/regex tests" + ../build/cargo.sh test --tests -- --exclude-should-panic --test-threads 1 -Zunstable-options -q + else + echo "[AOT] rust-lang/regex tests" + ../build/cargo.sh build --tests --target $TARGET_TRIPLE + fi popd } diff --git a/compiler/rustc_codegen_cranelift/src/abi/comments.rs b/compiler/rustc_codegen_cranelift/src/abi/comments.rs index 9aab45b62e211..5fbaed7283a67 100644 --- a/compiler/rustc_codegen_cranelift/src/abi/comments.rs +++ b/compiler/rustc_codegen_cranelift/src/abi/comments.rs @@ -10,14 +10,16 @@ use cranelift_codegen::entity::EntityRef; use crate::prelude::*; -pub(super) fn add_args_header_comment(fx: &mut FunctionCx<'_, '_, impl Module>) { - fx.add_global_comment( - "kind loc.idx param pass mode ty".to_string(), - ); +pub(super) fn add_args_header_comment(fx: &mut FunctionCx<'_, '_, '_>) { + if fx.clif_comments.enabled() { + fx.add_global_comment( + "kind loc.idx param pass mode ty".to_string(), + ); + } } pub(super) fn add_arg_comment<'tcx>( - fx: &mut FunctionCx<'_, 'tcx, impl Module>, + fx: &mut FunctionCx<'_, '_, 'tcx>, kind: &str, local: Option, local_field: Option, @@ -25,6 +27,10 @@ pub(super) fn add_arg_comment<'tcx>( arg_abi_mode: PassMode, arg_layout: TyAndLayout<'tcx>, ) { + if !fx.clif_comments.enabled() { + return; + } + let local = if let Some(local) = local { Cow::Owned(format!("{:?}", local)) } else { @@ -42,11 +48,7 @@ pub(super) fn add_arg_comment<'tcx>( [param_a, param_b] => Cow::Owned(format!("= {:?},{:?}", param_a, param_b)), params => Cow::Owned(format!( "= {}", - params - .iter() - .map(ToString::to_string) - .collect::>() - .join(",") + params.iter().map(ToString::to_string).collect::>().join(",") )), }; @@ -62,27 +64,26 @@ pub(super) fn add_arg_comment<'tcx>( )); } -pub(super) fn add_locals_header_comment(fx: &mut FunctionCx<'_, '_, impl Module>) { - fx.add_global_comment(String::new()); - fx.add_global_comment( - "kind local ty size align (abi,pref)".to_string(), - ); +pub(super) fn add_locals_header_comment(fx: &mut FunctionCx<'_, '_, '_>) { + if fx.clif_comments.enabled() { + fx.add_global_comment(String::new()); + fx.add_global_comment( + "kind local ty size align (abi,pref)".to_string(), + ); + } } pub(super) fn add_local_place_comments<'tcx>( - fx: &mut FunctionCx<'_, 'tcx, impl Module>, + fx: &mut FunctionCx<'_, '_, 'tcx>, place: CPlace<'tcx>, local: Local, ) { + if !fx.clif_comments.enabled() { + return; + } let TyAndLayout { ty, layout } = place.layout(); - let rustc_target::abi::Layout { - size, - align, - abi: _, - variants: _, - fields: _, - largest_niche: _, - } = layout; + let rustc_target::abi::Layout { size, align, abi: _, variants: _, fields: _, largest_niche: _ } = + layout; let (kind, extra) = match *place.inner() { CPlaceInner::Var(place_local, var) => { @@ -91,10 +92,7 @@ pub(super) fn add_local_place_comments<'tcx>( } CPlaceInner::VarPair(place_local, var1, var2) => { assert_eq!(local, place_local); - ( - "ssa", - Cow::Owned(format!(",var=({}, {})", var1.index(), var2.index())), - ) + ("ssa", Cow::Owned(format!(",var=({}, {})", var1.index(), var2.index()))) } CPlaceInner::VarLane(_local, _var, _lane) => unreachable!(), CPlaceInner::Addr(ptr, meta) => { @@ -103,19 +101,16 @@ pub(super) fn add_local_place_comments<'tcx>( } else { Cow::Borrowed("") }; - match ptr.base_and_offset() { - (crate::pointer::PointerBase::Addr(addr), offset) => ( - "reuse", - format!("storage={}{}{}", addr, offset, meta).into(), - ), - (crate::pointer::PointerBase::Stack(stack_slot), offset) => ( - "stack", - format!("storage={}{}{}", stack_slot, offset, meta).into(), - ), - (crate::pointer::PointerBase::Dangling(align), offset) => ( - "zst", - format!("align={},offset={}", align.bytes(), offset).into(), - ), + match ptr.debug_base_and_offset() { + (crate::pointer::PointerBase::Addr(addr), offset) => { + ("reuse", format!("storage={}{}{}", addr, offset, meta).into()) + } + (crate::pointer::PointerBase::Stack(stack_slot), offset) => { + ("stack", format!("storage={}{}{}", stack_slot, offset, meta).into()) + } + (crate::pointer::PointerBase::Dangling(align), offset) => { + ("zst", format!("align={},offset={}", align.bytes(), offset).into()) + } } } }; @@ -128,11 +123,7 @@ pub(super) fn add_local_place_comments<'tcx>( size.bytes(), align.abi.bytes(), align.pref.bytes(), - if extra.is_empty() { - "" - } else { - " " - }, + if extra.is_empty() { "" } else { " " }, extra, )); } diff --git a/compiler/rustc_codegen_cranelift/src/abi/mod.rs b/compiler/rustc_codegen_cranelift/src/abi/mod.rs index b2647e6c8d384..0e7829eaa26ac 100644 --- a/compiler/rustc_codegen_cranelift/src/abi/mod.rs +++ b/compiler/rustc_codegen_cranelift/src/abi/mod.rs @@ -1,6 +1,5 @@ //! Handling of everything related to the calling convention. Also fills `fx.local_map`. -#[cfg(debug_assertions)] mod comments; mod pass_mode; mod returning; @@ -38,25 +37,15 @@ fn clif_sig_from_fn_abi<'tcx>( | Conv::X86VectorCall | Conv::AmdGpuKernel | Conv::AvrInterrupt - | Conv::AvrNonBlockingInterrupt => { - todo!("{:?}", fn_abi.conv) - } + | Conv::AvrNonBlockingInterrupt => todo!("{:?}", fn_abi.conv), }; - let inputs = fn_abi - .args - .iter() - .map(|arg_abi| arg_abi.get_abi_param(tcx).into_iter()) - .flatten(); + let inputs = fn_abi.args.iter().map(|arg_abi| arg_abi.get_abi_param(tcx).into_iter()).flatten(); let (return_ptr, returns) = fn_abi.ret.get_abi_return(tcx); // Sometimes the first param is an pointer to the place where the return value needs to be stored. let params: Vec<_> = return_ptr.into_iter().chain(inputs).collect(); - Signature { - params, - returns, - call_conv, - } + Signature { params, returns, call_conv } } pub(crate) fn get_function_sig<'tcx>( @@ -65,37 +54,29 @@ pub(crate) fn get_function_sig<'tcx>( inst: Instance<'tcx>, ) -> Signature { assert!(!inst.substs.needs_infer()); - clif_sig_from_fn_abi( - tcx, - triple, - &FnAbi::of_instance(&RevealAllLayoutCx(tcx), inst, &[]), - ) + clif_sig_from_fn_abi(tcx, triple, &FnAbi::of_instance(&RevealAllLayoutCx(tcx), inst, &[])) } /// Instance must be monomorphized pub(crate) fn import_function<'tcx>( tcx: TyCtxt<'tcx>, - module: &mut impl Module, + module: &mut dyn Module, inst: Instance<'tcx>, ) -> FuncId { let name = tcx.symbol_name(inst).name.to_string(); let sig = get_function_sig(tcx, module.isa().triple(), inst); - module - .declare_function(&name, Linkage::Import, &sig) - .unwrap() + module.declare_function(&name, Linkage::Import, &sig).unwrap() } -impl<'tcx, M: Module> FunctionCx<'_, 'tcx, M> { +impl<'tcx> FunctionCx<'_, '_, 'tcx> { /// Instance must be monomorphized pub(crate) fn get_function_ref(&mut self, inst: Instance<'tcx>) -> FuncRef { - let func_id = import_function(self.tcx, &mut self.cx.module, inst); - let func_ref = self - .cx - .module - .declare_func_in_func(func_id, &mut self.bcx.func); + let func_id = import_function(self.tcx, self.cx.module, inst); + let func_ref = self.cx.module.declare_func_in_func(func_id, &mut self.bcx.func); - #[cfg(debug_assertions)] - self.add_comment(func_ref, format!("{:?}", inst)); + if self.clif_comments.enabled() { + self.add_comment(func_ref, format!("{:?}", inst)); + } func_ref } @@ -107,23 +88,11 @@ impl<'tcx, M: Module> FunctionCx<'_, 'tcx, M> { returns: Vec, args: &[Value], ) -> &[Value] { - let sig = Signature { - params, - returns, - call_conv: CallConv::triple_default(self.triple()), - }; - let func_id = self - .cx - .module - .declare_function(&name, Linkage::Import, &sig) - .unwrap(); - let func_ref = self - .cx - .module - .declare_func_in_func(func_id, &mut self.bcx.func); + let sig = Signature { params, returns, call_conv: CallConv::triple_default(self.triple()) }; + let func_id = self.cx.module.declare_function(&name, Linkage::Import, &sig).unwrap(); + let func_ref = self.cx.module.declare_func_in_func(func_id, &mut self.bcx.func); let call_inst = self.bcx.ins().call(func_ref, args); - #[cfg(debug_assertions)] - { + if self.clif_comments.enabled() { self.add_comment(call_inst, format!("easy_call {}", name)); } let results = self.bcx.inst_results(call_inst); @@ -140,17 +109,12 @@ impl<'tcx, M: Module> FunctionCx<'_, 'tcx, M> { let (input_tys, args): (Vec<_>, Vec<_>) = args .iter() .map(|arg| { - ( - AbiParam::new(self.clif_type(arg.layout().ty).unwrap()), - arg.load_scalar(self), - ) + (AbiParam::new(self.clif_type(arg.layout().ty).unwrap()), arg.load_scalar(self)) }) .unzip(); let return_layout = self.layout_of(return_ty); let return_tys = if let ty::Tuple(tup) = return_ty.kind() { - tup.types() - .map(|ty| AbiParam::new(self.clif_type(ty).unwrap())) - .collect() + tup.types().map(|ty| AbiParam::new(self.clif_type(ty).unwrap())).collect() } else { vec![AbiParam::new(self.clif_type(return_ty).unwrap())] }; @@ -169,7 +133,7 @@ impl<'tcx, M: Module> FunctionCx<'_, 'tcx, M> { /// Make a [`CPlace`] capable of holding value of the specified type. fn make_local_place<'tcx>( - fx: &mut FunctionCx<'_, 'tcx, impl Module>, + fx: &mut FunctionCx<'_, '_, 'tcx>, local: Local, layout: TyAndLayout<'tcx>, is_ssa: bool, @@ -184,16 +148,12 @@ fn make_local_place<'tcx>( CPlace::new_stack_slot(fx, layout) }; - #[cfg(debug_assertions)] self::comments::add_local_place_comments(fx, place, local); place } -pub(crate) fn codegen_fn_prelude<'tcx>( - fx: &mut FunctionCx<'_, 'tcx, impl Module>, - start_block: Block, -) { +pub(crate) fn codegen_fn_prelude<'tcx>(fx: &mut FunctionCx<'_, '_, 'tcx>, start_block: Block) { fx.bcx.append_block_params_for_function_params(start_block); fx.bcx.switch_to_block(start_block); @@ -201,16 +161,9 @@ pub(crate) fn codegen_fn_prelude<'tcx>( let ssa_analyzed = crate::analyze::analyze(fx); - #[cfg(debug_assertions)] self::comments::add_args_header_comment(fx); - let mut block_params_iter = fx - .bcx - .func - .dfg - .block_params(start_block) - .to_vec() - .into_iter(); + let mut block_params_iter = fx.bcx.func.dfg.block_params(start_block).to_vec().into_iter(); let ret_place = self::returning::codegen_return_param(fx, &ssa_analyzed, &mut block_params_iter); assert_eq!(fx.local_map.push(ret_place), RETURN_PLACE); @@ -272,7 +225,6 @@ pub(crate) fn codegen_fn_prelude<'tcx>( fx.fn_abi = Some(fn_abi); assert!(block_params_iter.next().is_none(), "arg_value left behind"); - #[cfg(debug_assertions)] self::comments::add_locals_header_comment(fx); for (local, arg_kind, ty) in func_params { @@ -286,10 +238,10 @@ pub(crate) fn codegen_fn_prelude<'tcx>( if let Some((addr, meta)) = val.try_to_ptr() { let local_decl = &fx.mir.local_decls[local]; // v this ! is important - let internally_mutable = !val.layout().ty.is_freeze( - fx.tcx.at(local_decl.source_info.span), - ParamEnv::reveal_all(), - ); + let internally_mutable = !val + .layout() + .ty + .is_freeze(fx.tcx.at(local_decl.source_info.span), ParamEnv::reveal_all()); if local_decl.mutability == mir::Mutability::Not && !internally_mutable { // We wont mutate this argument, so it is fine to borrow the backing storage // of this argument, to prevent a copy. @@ -300,7 +252,6 @@ pub(crate) fn codegen_fn_prelude<'tcx>( CPlace::for_ptr(addr, val.layout()) }; - #[cfg(debug_assertions)] self::comments::add_local_place_comments(fx, place, local); assert_eq!(fx.local_map.push(place), local); @@ -321,9 +272,7 @@ pub(crate) fn codegen_fn_prelude<'tcx>( ArgKind::Spread(params) => { for (i, param) in params.into_iter().enumerate() { if let Some(param) = param { - place - .place_field(fx, mir::Field::new(i)) - .write_cvalue(fx, param); + place.place_field(fx, mir::Field::new(i)).write_cvalue(fx, param); } } } @@ -340,13 +289,11 @@ pub(crate) fn codegen_fn_prelude<'tcx>( assert_eq!(fx.local_map.push(place), local); } - fx.bcx - .ins() - .jump(*fx.block_map.get(START_BLOCK).unwrap(), &[]); + fx.bcx.ins().jump(*fx.block_map.get(START_BLOCK).unwrap(), &[]); } pub(crate) fn codegen_terminator_call<'tcx>( - fx: &mut FunctionCx<'_, 'tcx, impl Module>, + fx: &mut FunctionCx<'_, '_, 'tcx>, span: Span, current_block: Block, func: &Operand<'tcx>, @@ -354,9 +301,8 @@ pub(crate) fn codegen_terminator_call<'tcx>( destination: Option<(Place<'tcx>, BasicBlock)>, ) { let fn_ty = fx.monomorphize(func.ty(fx.mir, fx.tcx)); - let fn_sig = fx - .tcx - .normalize_erasing_late_bound_regions(ParamEnv::reveal_all(), fn_ty.fn_sig(fx.tcx)); + let fn_sig = + fx.tcx.normalize_erasing_late_bound_regions(ParamEnv::reveal_all(), fn_ty.fn_sig(fx.tcx)); let destination = destination.map(|(place, bb)| (codegen_place(fx, place), bb)); @@ -404,20 +350,11 @@ pub(crate) fn codegen_terminator_call<'tcx>( let fn_abi = if let Some(instance) = instance { FnAbi::of_instance(&RevealAllLayoutCx(fx.tcx), instance, &extra_args) } else { - FnAbi::of_fn_ptr( - &RevealAllLayoutCx(fx.tcx), - fn_ty.fn_sig(fx.tcx), - &extra_args, - ) + FnAbi::of_fn_ptr(&RevealAllLayoutCx(fx.tcx), fn_ty.fn_sig(fx.tcx), &extra_args) }; let is_cold = instance - .map(|inst| { - fx.tcx - .codegen_fn_attrs(inst.def_id()) - .flags - .contains(CodegenFnAttrFlags::COLD) - }) + .map(|inst| fx.tcx.codegen_fn_attrs(inst.def_id()).flags.contains(CodegenFnAttrFlags::COLD)) .unwrap_or(false); if is_cold { fx.cold_blocks.insert(current_block); @@ -441,9 +378,7 @@ pub(crate) fn codegen_terminator_call<'tcx>( } args } else { - args.iter() - .map(|arg| codegen_operand(fx, arg)) - .collect::>() + args.iter().map(|arg| codegen_operand(fx, arg)).collect::>() }; // | indirect call target @@ -451,12 +386,8 @@ pub(crate) fn codegen_terminator_call<'tcx>( // v v let (func_ref, first_arg) = match instance { // Trait object call - Some(Instance { - def: InstanceDef::Virtual(_, idx), - .. - }) => { - #[cfg(debug_assertions)] - { + Some(Instance { def: InstanceDef::Virtual(_, idx), .. }) => { + if fx.clif_comments.enabled() { let nop_inst = fx.bcx.ins().nop(); fx.add_comment( nop_inst, @@ -477,8 +408,7 @@ pub(crate) fn codegen_terminator_call<'tcx>( // Indirect call None => { - #[cfg(debug_assertions)] - { + if fx.clif_comments.enabled() { let nop_inst = fx.bcx.ins().nop(); fx.add_comment(nop_inst, "indirect call"); } @@ -511,10 +441,7 @@ pub(crate) fn codegen_terminator_call<'tcx>( ) .collect::>(); - if instance - .map(|inst| inst.def.requires_caller_location(fx.tcx)) - .unwrap_or(false) - { + if instance.map(|inst| inst.def.requires_caller_location(fx.tcx)).unwrap_or(false) { // Pass the caller location for `#[track_caller]`. let caller_location = fx.get_caller_location(span); call_args.extend( @@ -542,11 +469,8 @@ pub(crate) fn codegen_terminator_call<'tcx>( // FIXME find a cleaner way to support varargs if fn_sig.c_variadic { - if fn_sig.abi != Abi::C { - fx.tcx.sess.span_fatal( - span, - &format!("Variadic call for non-C abi {:?}", fn_sig.abi), - ); + if !matches!(fn_sig.abi, Abi::C { .. }) { + fx.tcx.sess.span_fatal(span, &format!("Variadic call for non-C abi {:?}", fn_sig.abi)); } let sig_ref = fx.bcx.func.dfg.call_signature(call_inst).unwrap(); let abi_params = call_args @@ -555,9 +479,7 @@ pub(crate) fn codegen_terminator_call<'tcx>( let ty = fx.bcx.func.dfg.value_type(arg); if !ty.is_int() { // FIXME set %al to upperbound on float args once floats are supported - fx.tcx - .sess - .span_fatal(span, &format!("Non int ty {:?} for variadic call", ty)); + fx.tcx.sess.span_fatal(span, &format!("Non int ty {:?} for variadic call", ty)); } AbiParam::new(ty) }) @@ -574,7 +496,7 @@ pub(crate) fn codegen_terminator_call<'tcx>( } pub(crate) fn codegen_drop<'tcx>( - fx: &mut FunctionCx<'_, 'tcx, impl Module>, + fx: &mut FunctionCx<'_, '_, 'tcx>, span: Span, drop_place: CPlace<'tcx>, ) { @@ -611,10 +533,7 @@ pub(crate) fn codegen_drop<'tcx>( fx, fx.layout_of(fx.tcx.mk_ref( &ty::RegionKind::ReErased, - TypeAndMut { - ty, - mutbl: crate::rustc_hir::Mutability::Mut, - }, + TypeAndMut { ty, mutbl: crate::rustc_hir::Mutability::Mut }, )), ); let arg_value = adjust_arg_for_abi(fx, arg_value, &fn_abi.args[0]); diff --git a/compiler/rustc_codegen_cranelift/src/abi/pass_mode.rs b/compiler/rustc_codegen_cranelift/src/abi/pass_mode.rs index 1202c23dbe7b3..7c275965199e0 100644 --- a/compiler/rustc_codegen_cranelift/src/abi/pass_mode.rs +++ b/compiler/rustc_codegen_cranelift/src/abi/pass_mode.rs @@ -71,12 +71,7 @@ fn cast_target_to_abi_params(cast: CastTarget) -> SmallVec<[AbiParam; 2]> { .prefix .iter() .flatten() - .map(|&kind| { - reg_to_abi_param(Reg { - kind, - size: cast.prefix_chunk_size, - }) - }) + .map(|&kind| reg_to_abi_param(Reg { kind, size: cast.prefix_chunk_size })) .chain((0..rest_count).map(|_| reg_to_abi_param(cast.rest.unit))) .collect::>(); @@ -98,12 +93,10 @@ impl<'tcx> ArgAbiExt<'tcx> for ArgAbi<'tcx, Ty<'tcx>> { match self.mode { PassMode::Ignore => smallvec![], PassMode::Direct(attrs) => match &self.layout.abi { - Abi::Scalar(scalar) => { - smallvec![apply_arg_attrs_to_abi_param( - AbiParam::new(scalar_to_clif_type(tcx, scalar.clone())), - attrs - )] - } + Abi::Scalar(scalar) => smallvec![apply_arg_attrs_to_abi_param( + AbiParam::new(scalar_to_clif_type(tcx, scalar.clone())), + attrs + )], Abi::Vector { .. } => { let vector_ty = crate::intrinsics::clif_vector_type(tcx, self.layout).unwrap(); smallvec![AbiParam::new(vector_ty)] @@ -122,11 +115,7 @@ impl<'tcx> ArgAbiExt<'tcx> for ArgAbi<'tcx, Ty<'tcx>> { _ => unreachable!("{:?}", self.layout.abi), }, PassMode::Cast(cast) => cast_target_to_abi_params(cast), - PassMode::Indirect { - attrs, - extra_attrs: None, - on_stack, - } => { + PassMode::Indirect { attrs, extra_attrs: None, on_stack } => { if on_stack { let size = u32::try_from(self.layout.size.bytes()).unwrap(); smallvec![apply_arg_attrs_to_abi_param( @@ -134,17 +123,10 @@ impl<'tcx> ArgAbiExt<'tcx> for ArgAbi<'tcx, Ty<'tcx>> { attrs )] } else { - smallvec![apply_arg_attrs_to_abi_param( - AbiParam::new(pointer_ty(tcx)), - attrs - )] + smallvec![apply_arg_attrs_to_abi_param(AbiParam::new(pointer_ty(tcx)), attrs)] } } - PassMode::Indirect { - attrs, - extra_attrs: Some(extra_attrs), - on_stack, - } => { + PassMode::Indirect { attrs, extra_attrs: Some(extra_attrs), on_stack } => { assert!(!on_stack); smallvec![ apply_arg_attrs_to_abi_param(AbiParam::new(pointer_ty(tcx)), attrs), @@ -158,10 +140,9 @@ impl<'tcx> ArgAbiExt<'tcx> for ArgAbi<'tcx, Ty<'tcx>> { match self.mode { PassMode::Ignore => (None, vec![]), PassMode::Direct(_) => match &self.layout.abi { - Abi::Scalar(scalar) => ( - None, - vec![AbiParam::new(scalar_to_clif_type(tcx, scalar.clone()))], - ), + Abi::Scalar(scalar) => { + (None, vec![AbiParam::new(scalar_to_clif_type(tcx, scalar.clone()))]) + } Abi::Vector { .. } => { let vector_ty = crate::intrinsics::clif_vector_type(tcx, self.layout).unwrap(); (None, vec![AbiParam::new(vector_ty)]) @@ -177,31 +158,19 @@ impl<'tcx> ArgAbiExt<'tcx> for ArgAbi<'tcx, Ty<'tcx>> { _ => unreachable!("{:?}", self.layout.abi), }, PassMode::Cast(cast) => (None, cast_target_to_abi_params(cast).into_iter().collect()), - PassMode::Indirect { - attrs: _, - extra_attrs: None, - on_stack, - } => { + PassMode::Indirect { attrs: _, extra_attrs: None, on_stack } => { assert!(!on_stack); - ( - Some(AbiParam::special( - pointer_ty(tcx), - ArgumentPurpose::StructReturn, - )), - vec![], - ) + (Some(AbiParam::special(pointer_ty(tcx), ArgumentPurpose::StructReturn)), vec![]) + } + PassMode::Indirect { attrs: _, extra_attrs: Some(_), on_stack: _ } => { + unreachable!("unsized return value") } - PassMode::Indirect { - attrs: _, - extra_attrs: Some(_), - on_stack: _, - } => unreachable!("unsized return value"), } } } pub(super) fn to_casted_value<'tcx>( - fx: &mut FunctionCx<'_, 'tcx, impl Module>, + fx: &mut FunctionCx<'_, '_, 'tcx>, arg: CValue<'tcx>, cast: CastTarget, ) -> SmallVec<[Value; 2]> { @@ -211,9 +180,7 @@ pub(super) fn to_casted_value<'tcx>( cast_target_to_abi_params(cast) .into_iter() .map(|param| { - let val = ptr - .offset_i64(fx, offset) - .load(fx, param.value_type, MemFlags::new()); + let val = ptr.offset_i64(fx, offset).load(fx, param.value_type, MemFlags::new()); offset += i64::from(param.value_type.bytes()); val }) @@ -221,16 +188,13 @@ pub(super) fn to_casted_value<'tcx>( } pub(super) fn from_casted_value<'tcx>( - fx: &mut FunctionCx<'_, 'tcx, impl Module>, + fx: &mut FunctionCx<'_, '_, 'tcx>, block_params: &[Value], layout: TyAndLayout<'tcx>, cast: CastTarget, ) -> CValue<'tcx> { let abi_params = cast_target_to_abi_params(cast); - let abi_param_size: u32 = abi_params - .iter() - .map(|param| param.value_type.bytes()) - .sum(); + let abi_param_size: u32 = abi_params.iter().map(|param| param.value_type.bytes()).sum(); let layout_size = u32::try_from(layout.size.bytes()).unwrap(); let stack_slot = fx.bcx.create_stack_slot(StackSlotData { kind: StackSlotKind::ExplicitSlot, @@ -244,7 +208,7 @@ pub(super) fn from_casted_value<'tcx>( }); let ptr = Pointer::new(fx.bcx.ins().stack_addr(pointer_ty(fx.tcx), stack_slot, 0)); let mut offset = 0; - let mut block_params_iter = block_params.into_iter().copied(); + let mut block_params_iter = block_params.iter().copied(); for param in abi_params { let val = ptr.offset_i64(fx, offset).store( fx, @@ -260,7 +224,7 @@ pub(super) fn from_casted_value<'tcx>( /// Get a set of values to be passed as function arguments. pub(super) fn adjust_arg_for_abi<'tcx>( - fx: &mut FunctionCx<'_, 'tcx, impl Module>, + fx: &mut FunctionCx<'_, '_, 'tcx>, arg: CValue<'tcx>, arg_abi: &ArgAbi<'tcx, Ty<'tcx>>, ) -> SmallVec<[Value; 2]> { @@ -283,9 +247,9 @@ pub(super) fn adjust_arg_for_abi<'tcx>( /// Create a [`CValue`] containing the value of a function parameter adding clif function parameters /// as necessary. pub(super) fn cvalue_for_param<'tcx>( - fx: &mut FunctionCx<'_, 'tcx, impl Module>, - #[cfg_attr(not(debug_assertions), allow(unused_variables))] local: Option, - #[cfg_attr(not(debug_assertions), allow(unused_variables))] local_field: Option, + fx: &mut FunctionCx<'_, '_, 'tcx>, + local: Option, + local_field: Option, arg_abi: &ArgAbi<'tcx, Ty<'tcx>>, block_params_iter: &mut impl Iterator, ) -> Option> { @@ -294,15 +258,11 @@ pub(super) fn cvalue_for_param<'tcx>( .into_iter() .map(|abi_param| { let block_param = block_params_iter.next().unwrap(); - assert_eq!( - fx.bcx.func.dfg.value_type(block_param), - abi_param.value_type - ); + assert_eq!(fx.bcx.func.dfg.value_type(block_param), abi_param.value_type); block_param }) .collect::>(); - #[cfg(debug_assertions)] crate::abi::comments::add_arg_comment( fx, "arg", @@ -321,29 +281,14 @@ pub(super) fn cvalue_for_param<'tcx>( } PassMode::Pair(_, _) => { assert_eq!(block_params.len(), 2, "{:?}", block_params); - Some(CValue::by_val_pair( - block_params[0], - block_params[1], - arg_abi.layout, - )) + Some(CValue::by_val_pair(block_params[0], block_params[1], arg_abi.layout)) } PassMode::Cast(cast) => Some(from_casted_value(fx, &block_params, arg_abi.layout, cast)), - PassMode::Indirect { - attrs: _, - extra_attrs: None, - on_stack: _, - } => { + PassMode::Indirect { attrs: _, extra_attrs: None, on_stack: _ } => { assert_eq!(block_params.len(), 1, "{:?}", block_params); - Some(CValue::by_ref( - Pointer::new(block_params[0]), - arg_abi.layout, - )) + Some(CValue::by_ref(Pointer::new(block_params[0]), arg_abi.layout)) } - PassMode::Indirect { - attrs: _, - extra_attrs: Some(_), - on_stack: _, - } => { + PassMode::Indirect { attrs: _, extra_attrs: Some(_), on_stack: _ } => { assert_eq!(block_params.len(), 2, "{:?}", block_params); Some(CValue::by_ref_unsized( Pointer::new(block_params[0]), diff --git a/compiler/rustc_codegen_cranelift/src/abi/returning.rs b/compiler/rustc_codegen_cranelift/src/abi/returning.rs index a382963bf1ed7..e1c53224b4f84 100644 --- a/compiler/rustc_codegen_cranelift/src/abi/returning.rs +++ b/compiler/rustc_codegen_cranelift/src/abi/returning.rs @@ -8,14 +8,13 @@ use smallvec::{smallvec, SmallVec}; /// Can the given type be returned into an ssa var or does it need to be returned on the stack. pub(crate) fn can_return_to_ssa_var<'tcx>( - fx: &FunctionCx<'_, 'tcx, impl Module>, + fx: &FunctionCx<'_, '_, 'tcx>, func: &mir::Operand<'tcx>, args: &[mir::Operand<'tcx>], ) -> bool { let fn_ty = fx.monomorphize(func.ty(fx.mir, fx.tcx)); - let fn_sig = fx - .tcx - .normalize_erasing_late_bound_regions(ParamEnv::reveal_all(), fn_ty.fn_sig(fx.tcx)); + let fn_sig = + fx.tcx.normalize_erasing_late_bound_regions(ParamEnv::reveal_all(), fn_ty.fn_sig(fx.tcx)); // Handle special calls like instrinsics and empty drop glue. let instance = if let ty::FnDef(def_id, substs) = *fn_ty.kind() { @@ -42,11 +41,7 @@ pub(crate) fn can_return_to_ssa_var<'tcx>( let fn_abi = if let Some(instance) = instance { FnAbi::of_instance(&RevealAllLayoutCx(fx.tcx), instance, &extra_args) } else { - FnAbi::of_fn_ptr( - &RevealAllLayoutCx(fx.tcx), - fn_ty.fn_sig(fx.tcx), - &extra_args, - ) + FnAbi::of_fn_ptr(&RevealAllLayoutCx(fx.tcx), fn_ty.fn_sig(fx.tcx), &extra_args) }; match fn_abi.ret.mode { PassMode::Ignore | PassMode::Direct(_) | PassMode::Pair(_, _) => true, @@ -58,15 +53,12 @@ pub(crate) fn can_return_to_ssa_var<'tcx>( /// Return a place where the return value of the current function can be written to. If necessary /// this adds an extra parameter pointing to where the return value needs to be stored. pub(super) fn codegen_return_param<'tcx>( - fx: &mut FunctionCx<'_, 'tcx, impl Module>, + fx: &mut FunctionCx<'_, '_, 'tcx>, ssa_analyzed: &rustc_index::vec::IndexVec, block_params_iter: &mut impl Iterator, ) -> CPlace<'tcx> { let (ret_place, ret_param): (_, SmallVec<[_; 2]>) = match fx.fn_abi.as_ref().unwrap().ret.mode { - PassMode::Ignore => ( - CPlace::no_place(fx.fn_abi.as_ref().unwrap().ret.layout), - smallvec![], - ), + PassMode::Ignore => (CPlace::no_place(fx.fn_abi.as_ref().unwrap().ret.layout), smallvec![]), PassMode::Direct(_) | PassMode::Pair(_, _) | PassMode::Cast(_) => { let is_ssa = ssa_analyzed[RETURN_PLACE] == crate::analyze::SsaKind::Ssa; ( @@ -79,32 +71,19 @@ pub(super) fn codegen_return_param<'tcx>( smallvec![], ) } - PassMode::Indirect { - attrs: _, - extra_attrs: None, - on_stack: _, - } => { + PassMode::Indirect { attrs: _, extra_attrs: None, on_stack: _ } => { let ret_param = block_params_iter.next().unwrap(); assert_eq!(fx.bcx.func.dfg.value_type(ret_param), pointer_ty(fx.tcx)); ( - CPlace::for_ptr( - Pointer::new(ret_param), - fx.fn_abi.as_ref().unwrap().ret.layout, - ), + CPlace::for_ptr(Pointer::new(ret_param), fx.fn_abi.as_ref().unwrap().ret.layout), smallvec![ret_param], ) } - PassMode::Indirect { - attrs: _, - extra_attrs: Some(_), - on_stack: _, - } => unreachable!("unsized return value"), + PassMode::Indirect { attrs: _, extra_attrs: Some(_), on_stack: _ } => { + unreachable!("unsized return value") + } }; - #[cfg(not(debug_assertions))] - let _ = ret_param; - - #[cfg(debug_assertions)] crate::abi::comments::add_arg_comment( fx, "ret", @@ -120,27 +99,21 @@ pub(super) fn codegen_return_param<'tcx>( /// Invokes the closure with if necessary a value representing the return pointer. When the closure /// returns the call return value(s) if any are written to the correct place. -pub(super) fn codegen_with_call_return_arg<'tcx, M: Module, T>( - fx: &mut FunctionCx<'_, 'tcx, M>, +pub(super) fn codegen_with_call_return_arg<'tcx, T>( + fx: &mut FunctionCx<'_, '_, 'tcx>, ret_arg_abi: &ArgAbi<'tcx, Ty<'tcx>>, ret_place: Option>, - f: impl FnOnce(&mut FunctionCx<'_, 'tcx, M>, Option) -> (Inst, T), + f: impl FnOnce(&mut FunctionCx<'_, '_, 'tcx>, Option) -> (Inst, T), ) -> (Inst, T) { let return_ptr = match ret_arg_abi.mode { PassMode::Ignore => None, - PassMode::Indirect { - attrs: _, - extra_attrs: None, - on_stack: _, - } => match ret_place { + PassMode::Indirect { attrs: _, extra_attrs: None, on_stack: _ } => match ret_place { Some(ret_place) => Some(ret_place.to_ptr().get_addr(fx)), None => Some(fx.bcx.ins().iconst(fx.pointer_type, 43)), // FIXME allocate temp stack slot }, - PassMode::Indirect { - attrs: _, - extra_attrs: Some(_), - on_stack: _, - } => unreachable!("unsized return value"), + PassMode::Indirect { attrs: _, extra_attrs: Some(_), on_stack: _ } => { + unreachable!("unsized return value") + } PassMode::Direct(_) | PassMode::Pair(_, _) | PassMode::Cast(_) => None, }; @@ -169,7 +142,7 @@ pub(super) fn codegen_with_call_return_arg<'tcx, M: Module, T>( let results = fx .bcx .inst_results(call_inst) - .into_iter() + .iter() .copied() .collect::>(); let result = @@ -177,37 +150,24 @@ pub(super) fn codegen_with_call_return_arg<'tcx, M: Module, T>( ret_place.write_cvalue(fx, result); } } - PassMode::Indirect { - attrs: _, - extra_attrs: None, - on_stack: _, - } => {} - PassMode::Indirect { - attrs: _, - extra_attrs: Some(_), - on_stack: _, - } => unreachable!("unsized return value"), + PassMode::Indirect { attrs: _, extra_attrs: None, on_stack: _ } => {} + PassMode::Indirect { attrs: _, extra_attrs: Some(_), on_stack: _ } => { + unreachable!("unsized return value") + } } (call_inst, meta) } /// Codegen a return instruction with the right return value(s) if any. -pub(crate) fn codegen_return(fx: &mut FunctionCx<'_, '_, impl Module>) { +pub(crate) fn codegen_return(fx: &mut FunctionCx<'_, '_, '_>) { match fx.fn_abi.as_ref().unwrap().ret.mode { - PassMode::Ignore - | PassMode::Indirect { - attrs: _, - extra_attrs: None, - on_stack: _, - } => { + PassMode::Ignore | PassMode::Indirect { attrs: _, extra_attrs: None, on_stack: _ } => { fx.bcx.ins().return_(&[]); } - PassMode::Indirect { - attrs: _, - extra_attrs: Some(_), - on_stack: _, - } => unreachable!("unsized return value"), + PassMode::Indirect { attrs: _, extra_attrs: Some(_), on_stack: _ } => { + unreachable!("unsized return value") + } PassMode::Direct(_) => { let place = fx.get_local_place(RETURN_PLACE); let ret_val = place.to_cvalue(fx).load_scalar(fx); diff --git a/compiler/rustc_codegen_cranelift/src/allocator.rs b/compiler/rustc_codegen_cranelift/src/allocator.rs index 6c5916550ff63..f60645a9f97bc 100644 --- a/compiler/rustc_codegen_cranelift/src/allocator.rs +++ b/compiler/rustc_codegen_cranelift/src/allocator.rs @@ -3,6 +3,7 @@ use crate::prelude::*; +use cranelift_codegen::binemit::{NullStackMapSink, NullTrapSink}; use rustc_ast::expand::allocator::{AllocatorKind, AllocatorTy, ALLOCATOR_METHODS}; use rustc_span::symbol::sym; @@ -66,13 +67,9 @@ fn codegen_inner( let callee_name = kind.fn_name(method.name); //eprintln!("Codegen allocator shim {} -> {} ({:?} -> {:?})", caller_name, callee_name, sig.params, sig.returns); - let func_id = module - .declare_function(&caller_name, Linkage::Export, &sig) - .unwrap(); + let func_id = module.declare_function(&caller_name, Linkage::Export, &sig).unwrap(); - let callee_func_id = module - .declare_function(&callee_name, Linkage::Import, &sig) - .unwrap(); + let callee_func_id = module.declare_function(&callee_name, Linkage::Import, &sig).unwrap(); let mut ctx = Context::new(); ctx.func = Function::with_name_signature(ExternalName::user(0, 0), sig.clone()); @@ -96,11 +93,7 @@ fn codegen_inner( bcx.finalize(); } module - .define_function( - func_id, - &mut ctx, - &mut cranelift_codegen::binemit::NullTrapSink {}, - ) + .define_function(func_id, &mut ctx, &mut NullTrapSink {}, &mut NullStackMapSink {}) .unwrap(); unwind_context.add_function(func_id, &ctx, module.isa()); } @@ -114,13 +107,10 @@ fn codegen_inner( let callee_name = kind.fn_name(sym::oom); //eprintln!("Codegen allocator shim {} -> {} ({:?} -> {:?})", caller_name, callee_name, sig.params, sig.returns); - let func_id = module - .declare_function("__rust_alloc_error_handler", Linkage::Export, &sig) - .unwrap(); + let func_id = + module.declare_function("__rust_alloc_error_handler", Linkage::Export, &sig).unwrap(); - let callee_func_id = module - .declare_function(&callee_name, Linkage::Import, &sig) - .unwrap(); + let callee_func_id = module.declare_function(&callee_name, Linkage::Import, &sig).unwrap(); let mut ctx = Context::new(); ctx.func = Function::with_name_signature(ExternalName::user(0, 0), sig); @@ -143,11 +133,7 @@ fn codegen_inner( bcx.finalize(); } module - .define_function( - func_id, - &mut ctx, - &mut cranelift_codegen::binemit::NullTrapSink {}, - ) + .define_function(func_id, &mut ctx, &mut NullTrapSink {}, &mut NullStackMapSink {}) .unwrap(); unwind_context.add_function(func_id, &ctx, module.isa()); } diff --git a/compiler/rustc_codegen_cranelift/src/analyze.rs b/compiler/rustc_codegen_cranelift/src/analyze.rs index 62fbcfe3f7a5d..efead25552f4d 100644 --- a/compiler/rustc_codegen_cranelift/src/analyze.rs +++ b/compiler/rustc_codegen_cranelift/src/analyze.rs @@ -11,7 +11,7 @@ pub(crate) enum SsaKind { Ssa, } -pub(crate) fn analyze(fx: &FunctionCx<'_, '_, impl Module>) -> IndexVec { +pub(crate) fn analyze(fx: &FunctionCx<'_, '_, '_>) -> IndexVec { let mut flag_map = fx .mir .local_decls @@ -40,12 +40,7 @@ pub(crate) fn analyze(fx: &FunctionCx<'_, '_, impl Module>) -> IndexVec { + TerminatorKind::Call { destination, func, args, .. } => { if let Some((dest_place, _dest_bb)) = destination { if !crate::abi::can_return_to_ssa_var(fx, func, args) { not_ssa(&mut flag_map, dest_place.local) diff --git a/compiler/rustc_codegen_cranelift/src/archive.rs b/compiler/rustc_codegen_cranelift/src/archive.rs index 96579054389ab..7583fc424071e 100644 --- a/compiler/rustc_codegen_cranelift/src/archive.rs +++ b/compiler/rustc_codegen_cranelift/src/archive.rs @@ -12,10 +12,7 @@ use object::{Object, ObjectSymbol, SymbolKind}; #[derive(Debug)] enum ArchiveEntry { - FromArchive { - archive_index: usize, - entry_index: usize, - }, + FromArchive { archive_index: usize, entry_index: usize }, File(PathBuf), } @@ -30,7 +27,6 @@ pub(crate) struct ArArchiveBuilder<'a> { // Don't use `HashMap` here, as the order is important. `rust.metadata.bin` must always be at // the end of an archive for linkers to not get confused. entries: Vec<(String, ArchiveEntry)>, - update_symbols: bool, } impl<'a> ArchiveBuilder<'a> for ArArchiveBuilder<'a> { @@ -46,10 +42,7 @@ impl<'a> ArchiveBuilder<'a> for ArArchiveBuilder<'a> { let entry = entry.unwrap(); entries.push(( String::from_utf8(entry.header().identifier().to_vec()).unwrap(), - ArchiveEntry::FromArchive { - archive_index: 0, - entry_index: i, - }, + ArchiveEntry::FromArchive { archive_index: 0, entry_index: i }, )); i += 1; } @@ -69,7 +62,6 @@ impl<'a> ArchiveBuilder<'a> for ArArchiveBuilder<'a> { src_archives, entries, - update_symbols: false, } } @@ -95,14 +87,9 @@ impl<'a> ArchiveBuilder<'a> for ArArchiveBuilder<'a> { fn add_native_library(&mut self, name: rustc_span::symbol::Symbol) { let location = find_library(name, &self.lib_search_paths, self.sess); - self.add_archive(location.clone(), |_| false) - .unwrap_or_else(|e| { - panic!( - "failed to add native library {}: {}", - location.to_string_lossy(), - e - ); - }); + self.add_archive(location.clone(), |_| false).unwrap_or_else(|e| { + panic!("failed to add native library {}: {}", location.to_string_lossy(), e); + }); } fn add_rlib( @@ -136,9 +123,7 @@ impl<'a> ArchiveBuilder<'a> for ArArchiveBuilder<'a> { }) } - fn update_symbols(&mut self) { - self.update_symbols = true; - } + fn update_symbols(&mut self) {} fn build(mut self) { enum BuilderKind { @@ -156,10 +141,7 @@ impl<'a> ArchiveBuilder<'a> for ArArchiveBuilder<'a> { // FIXME only read the symbol table of the object files to avoid having to keep all // object files in memory at once, or read them twice. let data = match entry { - ArchiveEntry::FromArchive { - archive_index, - entry_index, - } => { + ArchiveEntry::FromArchive { archive_index, entry_index } => { // FIXME read symbols from symtab use std::io::Read; let (ref _src_archive_path, ref mut src_archive) = @@ -225,10 +207,7 @@ impl<'a> ArchiveBuilder<'a> for ArArchiveBuilder<'a> { err )); }), - entries - .iter() - .map(|(name, _)| name.as_bytes().to_vec()) - .collect(), + entries.iter().map(|(name, _)| name.as_bytes().to_vec()).collect(), ar::GnuSymbolTableFormat::Size32, symbol_table, ) @@ -271,8 +250,7 @@ impl<'a> ArchiveBuilder<'a> for ArArchiveBuilder<'a> { .expect("Couldn't run ranlib"); if !status.success() { - self.sess - .fatal(&format!("Ranlib exited with code {:?}", status.code())); + self.sess.fatal(&format!("Ranlib exited with code {:?}", status.code())); } } } @@ -292,13 +270,8 @@ impl<'a> ArArchiveBuilder<'a> { let file_name = String::from_utf8(entry.header().identifier().to_vec()) .map_err(|err| std::io::Error::new(std::io::ErrorKind::InvalidData, err))?; if !skip(&file_name) { - self.entries.push(( - file_name, - ArchiveEntry::FromArchive { - archive_index, - entry_index: i, - }, - )); + self.entries + .push((file_name, ArchiveEntry::FromArchive { archive_index, entry_index: i })); } i += 1; } diff --git a/compiler/rustc_codegen_cranelift/src/atomic_shim.rs b/compiler/rustc_codegen_cranelift/src/atomic_shim.rs deleted file mode 100644 index 674e6d907510a..0000000000000 --- a/compiler/rustc_codegen_cranelift/src/atomic_shim.rs +++ /dev/null @@ -1,185 +0,0 @@ -//! Atomic intrinsics are implemented using a global lock for now, as Cranelift doesn't support -//! atomic operations yet. - -// FIXME implement atomic instructions in Cranelift. - -use crate::prelude::*; - -#[cfg(all(feature = "jit", unix))] -#[no_mangle] -static mut __cg_clif_global_atomic_mutex: libc::pthread_mutex_t = libc::PTHREAD_MUTEX_INITIALIZER; - -pub(crate) fn init_global_lock( - module: &mut impl Module, - bcx: &mut FunctionBuilder<'_>, - use_jit: bool, -) { - if use_jit { - // When using JIT, dylibs won't find the __cg_clif_global_atomic_mutex data object defined here, - // so instead we define it in the cg_clif dylib. - - return; - } - - let mut data_ctx = DataContext::new(); - data_ctx.define_zeroinit(1024); // 1024 bytes should be big enough on all platforms. - data_ctx.set_align(16); - let atomic_mutex = module - .declare_data( - "__cg_clif_global_atomic_mutex", - Linkage::Export, - true, - false, - ) - .unwrap(); - module.define_data(atomic_mutex, &data_ctx).unwrap(); - - let pthread_mutex_init = module - .declare_function( - "pthread_mutex_init", - Linkage::Import, - &cranelift_codegen::ir::Signature { - call_conv: module.target_config().default_call_conv, - params: vec![ - AbiParam::new( - module.target_config().pointer_type(), /* *mut pthread_mutex_t */ - ), - AbiParam::new( - module.target_config().pointer_type(), /* *const pthread_mutex_attr_t */ - ), - ], - returns: vec![AbiParam::new(types::I32 /* c_int */)], - }, - ) - .unwrap(); - - let pthread_mutex_init = module.declare_func_in_func(pthread_mutex_init, bcx.func); - - let atomic_mutex = module.declare_data_in_func(atomic_mutex, bcx.func); - let atomic_mutex = bcx - .ins() - .global_value(module.target_config().pointer_type(), atomic_mutex); - - let nullptr = bcx.ins().iconst(module.target_config().pointer_type(), 0); - - bcx.ins().call(pthread_mutex_init, &[atomic_mutex, nullptr]); -} - -pub(crate) fn init_global_lock_constructor( - module: &mut impl Module, - constructor_name: &str, -) -> FuncId { - let sig = Signature::new(CallConv::SystemV); - let init_func_id = module - .declare_function(constructor_name, Linkage::Export, &sig) - .unwrap(); - - let mut ctx = Context::new(); - ctx.func = Function::with_name_signature(ExternalName::user(0, 0), sig); - { - let mut func_ctx = FunctionBuilderContext::new(); - let mut bcx = FunctionBuilder::new(&mut ctx.func, &mut func_ctx); - - let block = bcx.create_block(); - bcx.switch_to_block(block); - - crate::atomic_shim::init_global_lock(module, &mut bcx, false); - - bcx.ins().return_(&[]); - bcx.seal_all_blocks(); - bcx.finalize(); - } - module - .define_function( - init_func_id, - &mut ctx, - &mut cranelift_codegen::binemit::NullTrapSink {}, - ) - .unwrap(); - - init_func_id -} - -pub(crate) fn lock_global_lock(fx: &mut FunctionCx<'_, '_, impl Module>) { - let atomic_mutex = fx - .cx - .module - .declare_data( - "__cg_clif_global_atomic_mutex", - Linkage::Import, - true, - false, - ) - .unwrap(); - - let pthread_mutex_lock = fx - .cx - .module - .declare_function( - "pthread_mutex_lock", - Linkage::Import, - &cranelift_codegen::ir::Signature { - call_conv: fx.cx.module.target_config().default_call_conv, - params: vec![AbiParam::new( - fx.cx.module.target_config().pointer_type(), /* *mut pthread_mutex_t */ - )], - returns: vec![AbiParam::new(types::I32 /* c_int */)], - }, - ) - .unwrap(); - - let pthread_mutex_lock = fx - .cx - .module - .declare_func_in_func(pthread_mutex_lock, fx.bcx.func); - - let atomic_mutex = fx.cx.module.declare_data_in_func(atomic_mutex, fx.bcx.func); - let atomic_mutex = fx - .bcx - .ins() - .global_value(fx.cx.module.target_config().pointer_type(), atomic_mutex); - - fx.bcx.ins().call(pthread_mutex_lock, &[atomic_mutex]); -} - -pub(crate) fn unlock_global_lock(fx: &mut FunctionCx<'_, '_, impl Module>) { - let atomic_mutex = fx - .cx - .module - .declare_data( - "__cg_clif_global_atomic_mutex", - Linkage::Import, - true, - false, - ) - .unwrap(); - - let pthread_mutex_unlock = fx - .cx - .module - .declare_function( - "pthread_mutex_unlock", - Linkage::Import, - &cranelift_codegen::ir::Signature { - call_conv: fx.cx.module.target_config().default_call_conv, - params: vec![AbiParam::new( - fx.cx.module.target_config().pointer_type(), /* *mut pthread_mutex_t */ - )], - returns: vec![AbiParam::new(types::I32 /* c_int */)], - }, - ) - .unwrap(); - - let pthread_mutex_unlock = fx - .cx - .module - .declare_func_in_func(pthread_mutex_unlock, fx.bcx.func); - - let atomic_mutex = fx.cx.module.declare_data_in_func(atomic_mutex, fx.bcx.func); - let atomic_mutex = fx - .bcx - .ins() - .global_value(fx.cx.module.target_config().pointer_type(), atomic_mutex); - - fx.bcx.ins().call(pthread_mutex_unlock, &[atomic_mutex]); -} diff --git a/compiler/rustc_codegen_cranelift/src/backend.rs b/compiler/rustc_codegen_cranelift/src/backend.rs index 0ce34c904bdcc..eb7927fc4adeb 100644 --- a/compiler/rustc_codegen_cranelift/src/backend.rs +++ b/compiler/rustc_codegen_cranelift/src/backend.rs @@ -8,7 +8,7 @@ use rustc_session::Session; use cranelift_module::FuncId; use object::write::*; -use object::{RelocationEncoding, RelocationKind, SectionKind, SymbolFlags}; +use object::{RelocationEncoding, SectionKind, SymbolFlags}; use cranelift_object::{ObjectBuilder, ObjectModule, ObjectProduct}; @@ -22,9 +22,7 @@ pub(crate) trait WriteMetadata { impl WriteMetadata for object::write::Object { fn add_rustc_section(&mut self, symbol_name: String, data: Vec, _is_like_osx: bool) { - let segment = self - .segment_name(object::write::StandardSegment::Data) - .to_vec(); + let segment = self.segment_name(object::write::StandardSegment::Data).to_vec(); let section_id = self.add_section(segment, b".rustc".to_vec(), object::SectionKind::Data); let offset = self.append_section_data(section_id, &data, 1); // For MachO and probably PE this is necessary to prevent the linker from throwing away the @@ -74,11 +72,7 @@ impl WriteDebugInfo for ObjectProduct { let section_id = self.object.add_section( segment, name, - if id == SectionId::EhFrame { - SectionKind::ReadOnlyData - } else { - SectionKind::Debug - }, + if id == SectionId::EhFrame { SectionKind::ReadOnlyData } else { SectionKind::Debug }, ); self.object .section_mut(section_id) @@ -118,49 +112,6 @@ impl WriteDebugInfo for ObjectProduct { } } -// FIXME remove once atomic instructions are implemented in Cranelift. -pub(crate) trait AddConstructor { - fn add_constructor(&mut self, func_id: FuncId); -} - -impl AddConstructor for ObjectProduct { - fn add_constructor(&mut self, func_id: FuncId) { - let symbol = self.function_symbol(func_id); - let segment = self - .object - .segment_name(object::write::StandardSegment::Data); - let init_array_section = - self.object - .add_section(segment.to_vec(), b".init_array".to_vec(), SectionKind::Data); - let address_size = self - .object - .architecture() - .address_size() - .expect("address_size must be known") - .bytes(); - self.object.append_section_data( - init_array_section, - &std::iter::repeat(0) - .take(address_size.into()) - .collect::>(), - 8, - ); - self.object - .add_relocation( - init_array_section, - object::write::Relocation { - offset: 0, - size: address_size * 8, - kind: RelocationKind::Absolute, - encoding: RelocationEncoding::Generic, - symbol, - addend: 0, - }, - ) - .unwrap(); - } -} - pub(crate) fn with_object(sess: &Session, name: &str, f: impl FnOnce(&mut Object)) -> Vec { let triple = crate::build_isa(sess).triple().clone(); @@ -175,10 +126,9 @@ pub(crate) fn with_object(sess: &Session, name: &str, f: impl FnOnce(&mut Object target_lexicon::Architecture::X86_64 => object::Architecture::X86_64, target_lexicon::Architecture::Arm(_) => object::Architecture::Arm, target_lexicon::Architecture::Aarch64(_) => object::Architecture::Aarch64, - architecture => sess.fatal(&format!( - "target architecture {:?} is unsupported", - architecture, - )), + architecture => { + sess.fatal(&format!("target architecture {:?} is unsupported", architecture,)) + } }; let endian = match triple.endianness().unwrap() { target_lexicon::Endianness::Little => object::Endianness::Little, diff --git a/compiler/rustc_codegen_cranelift/src/base.rs b/compiler/rustc_codegen_cranelift/src/base.rs index 4842628a99da7..b34a29c25b92e 100644 --- a/compiler/rustc_codegen_cranelift/src/base.rs +++ b/compiler/rustc_codegen_cranelift/src/base.rs @@ -1,5 +1,6 @@ //! Codegen of a single function +use cranelift_codegen::binemit::{NullStackMapSink, NullTrapSink}; use rustc_index::vec::IndexVec; use rustc_middle::ty::adjustment::PointerCast; use rustc_middle::ty::layout::FnAbiExt; @@ -7,11 +8,7 @@ use rustc_target::abi::call::FnAbi; use crate::prelude::*; -pub(crate) fn codegen_fn<'tcx>( - cx: &mut crate::CodegenCx<'tcx, impl Module>, - instance: Instance<'tcx>, - linkage: Linkage, -) { +pub(crate) fn codegen_fn<'tcx>(cx: &mut crate::CodegenCx<'_, 'tcx>, instance: Instance<'tcx>) { let tcx = cx.tcx; let _inst_guard = @@ -23,7 +20,7 @@ pub(crate) fn codegen_fn<'tcx>( // Declare function let name = tcx.symbol_name(instance).name.to_string(); let sig = get_function_sig(tcx, cx.module.isa().triple(), instance); - let func_id = cx.module.declare_function(&name, linkage, &sig).unwrap(); + let func_id = cx.module.declare_function(&name, Linkage::Local, &sig).unwrap(); cx.cached_context.clear(); @@ -38,9 +35,8 @@ pub(crate) fn codegen_fn<'tcx>( // Predefine blocks let start_block = bcx.create_block(); - let block_map: IndexVec = (0..mir.basic_blocks().len()) - .map(|_| bcx.create_block()) - .collect(); + let block_map: IndexVec = + (0..mir.basic_blocks().len()).map(|_| bcx.create_block()).collect(); // Make FunctionCx let pointer_type = cx.module.target_config().pointer_type(); @@ -68,22 +64,23 @@ pub(crate) fn codegen_fn<'tcx>( inline_asm_index: 0, }; - let arg_uninhabited = fx.mir.args_iter().any(|arg| { - fx.layout_of(fx.monomorphize(&fx.mir.local_decls[arg].ty)) - .abi - .is_uninhabited() - }); + let arg_uninhabited = fx + .mir + .args_iter() + .any(|arg| fx.layout_of(fx.monomorphize(&fx.mir.local_decls[arg].ty)).abi.is_uninhabited()); - if arg_uninhabited { - fx.bcx - .append_block_params_for_function_params(fx.block_map[START_BLOCK]); + if !crate::constant::check_constants(&mut fx) { + fx.bcx.append_block_params_for_function_params(fx.block_map[START_BLOCK]); + fx.bcx.switch_to_block(fx.block_map[START_BLOCK]); + crate::trap::trap_unreachable(&mut fx, "compilation should have been aborted"); + } else if arg_uninhabited { + fx.bcx.append_block_params_for_function_params(fx.block_map[START_BLOCK]); fx.bcx.switch_to_block(fx.block_map[START_BLOCK]); crate::trap::trap_unreachable(&mut fx, "function has uninhabited argument"); } else { tcx.sess.time("codegen clif ir", || { - tcx.sess.time("codegen prelude", || { - crate::abi::codegen_fn_prelude(&mut fx, start_block) - }); + tcx.sess + .time("codegen prelude", || crate::abi::codegen_fn_prelude(&mut fx, start_block)); codegen_fn_content(&mut fx); }); } @@ -131,11 +128,7 @@ pub(crate) fn codegen_fn<'tcx>( let module = &mut cx.module; tcx.sess.time("define function", || { module - .define_function( - func_id, - context, - &mut cranelift_codegen::binemit::NullTrapSink {}, - ) + .define_function(func_id, context, &mut NullTrapSink {}, &mut NullStackMapSink {}) .unwrap() }); @@ -149,14 +142,12 @@ pub(crate) fn codegen_fn<'tcx>( &clif_comments, ); - if let Some(mach_compile_result) = &context.mach_compile_result { - if let Some(disasm) = &mach_compile_result.disasm { - crate::pretty_clif::write_ir_file( - tcx, - &format!("{}.vcode", tcx.symbol_name(instance).name), - |file| file.write_all(disasm.as_bytes()), - ) - } + if let Some(disasm) = &context.mach_compile_result.as_ref().unwrap().disasm { + crate::pretty_clif::write_ir_file( + tcx, + &format!("{}.vcode", tcx.symbol_name(instance).name), + |file| file.write_all(disasm.as_bytes()), + ) } // Define debuginfo for function @@ -199,16 +190,13 @@ pub(crate) fn verify_func( Some(Box::new(writer)), err, ); - tcx.sess - .fatal(&format!("cranelift verify error:\n{}", pretty_error)); + tcx.sess.fatal(&format!("cranelift verify error:\n{}", pretty_error)); } } }); } -fn codegen_fn_content(fx: &mut FunctionCx<'_, '_, impl Module>) { - crate::constant::check_constants(fx); - +fn codegen_fn_content(fx: &mut FunctionCx<'_, '_, '_>) { for (bb, bb_data) in fx.mir.basic_blocks().iter_enumerated() { let block = fx.get_block(bb); fx.bcx.switch_to_block(block); @@ -228,14 +216,9 @@ fn codegen_fn_content(fx: &mut FunctionCx<'_, '_, impl Module>) { codegen_stmt(fx, block, stmt); } - #[cfg(debug_assertions)] - { + if fx.clif_comments.enabled() { let mut terminator_head = "\n".to_string(); - bb_data - .terminator() - .kind - .fmt_head(&mut terminator_head) - .unwrap(); + bb_data.terminator().kind.fmt_head(&mut terminator_head).unwrap(); let inst = fx.bcx.func.layout.last_inst(block).unwrap(); fx.add_comment(inst, terminator_head); } @@ -267,13 +250,7 @@ fn codegen_fn_content(fx: &mut FunctionCx<'_, '_, impl Module>) { TerminatorKind::Return => { crate::abi::codegen_return(fx); } - TerminatorKind::Assert { - cond, - expected, - msg, - target, - cleanup: _, - } => { + TerminatorKind::Assert { cond, expected, msg, target, cleanup: _ } => { if !fx.tcx.sess.overflow_checks() { if let mir::AssertKind::OverflowNeg(_) = *msg { let target = fx.get_block(*target); @@ -319,11 +296,7 @@ fn codegen_fn_content(fx: &mut FunctionCx<'_, '_, impl Module>) { } } - TerminatorKind::SwitchInt { - discr, - switch_ty, - targets, - } => { + TerminatorKind::SwitchInt { discr, switch_ty, targets } => { let discr = codegen_operand(fx, discr).load_scalar(fx); let use_bool_opt = switch_ty.kind() == fx.tcx.types.bool.kind() @@ -433,11 +406,7 @@ fn codegen_fn_content(fx: &mut FunctionCx<'_, '_, impl Module>) { | TerminatorKind::GeneratorDrop => { bug!("shouldn't exist at codegen {:?}", bb_data.terminator()); } - TerminatorKind::Drop { - place, - target, - unwind: _, - } => { + TerminatorKind::Drop { place, target, unwind: _ } => { let drop_place = codegen_place(fx, *place); crate::abi::codegen_drop(fx, bb_data.terminator().source_info.span, drop_place); @@ -452,7 +421,7 @@ fn codegen_fn_content(fx: &mut FunctionCx<'_, '_, impl Module>) { } fn codegen_stmt<'tcx>( - fx: &mut FunctionCx<'_, 'tcx, impl Module>, + fx: &mut FunctionCx<'_, '_, 'tcx>, #[allow(unused_variables)] cur_block: Block, stmt: &Statement<'tcx>, ) { @@ -460,20 +429,19 @@ fn codegen_stmt<'tcx>( fx.set_debug_loc(stmt.source_info); - #[cfg(false_debug_assertions)] + #[cfg(disabled)] match &stmt.kind { StatementKind::StorageLive(..) | StatementKind::StorageDead(..) => {} // Those are not very useful _ => { - let inst = fx.bcx.func.layout.last_inst(cur_block).unwrap(); - fx.add_comment(inst, format!("{:?}", stmt)); + if fx.clif_comments.enabled() { + let inst = fx.bcx.func.layout.last_inst(cur_block).unwrap(); + fx.add_comment(inst, format!("{:?}", stmt)); + } } } match &stmt.kind { - StatementKind::SetDiscriminant { - place, - variant_index, - } => { + StatementKind::SetDiscriminant { place, variant_index } => { let place = codegen_place(fx, **place); crate::discriminant::codegen_set_discriminant(fx, place, *variant_index); } @@ -494,16 +462,16 @@ fn codegen_stmt<'tcx>( let val = crate::constant::codegen_tls_ref(fx, def_id, lval.layout()); lval.write_cvalue(fx, val); } - Rvalue::BinaryOp(bin_op, ref lhs, ref rhs) => { - let lhs = codegen_operand(fx, lhs); - let rhs = codegen_operand(fx, rhs); + Rvalue::BinaryOp(bin_op, ref lhs_rhs) => { + let lhs = codegen_operand(fx, &lhs_rhs.0); + let rhs = codegen_operand(fx, &lhs_rhs.1); let res = crate::num::codegen_binop(fx, bin_op, lhs, rhs); lval.write_cvalue(fx, res); } - Rvalue::CheckedBinaryOp(bin_op, ref lhs, ref rhs) => { - let lhs = codegen_operand(fx, lhs); - let rhs = codegen_operand(fx, rhs); + Rvalue::CheckedBinaryOp(bin_op, ref lhs_rhs) => { + let lhs = codegen_operand(fx, &lhs_rhs.0); + let rhs = codegen_operand(fx, &lhs_rhs.1); let res = if !fx.tcx.sess.overflow_checks() { let val = @@ -594,19 +562,11 @@ fn codegen_stmt<'tcx>( let from_ty = operand.layout().ty; let to_ty = fx.monomorphize(to_ty); - fn is_fat_ptr<'tcx>( - fx: &FunctionCx<'_, 'tcx, impl Module>, - ty: Ty<'tcx>, - ) -> bool { + fn is_fat_ptr<'tcx>(fx: &FunctionCx<'_, '_, 'tcx>, ty: Ty<'tcx>) -> bool { ty.builtin_deref(true) - .map( - |ty::TypeAndMut { - ty: pointee_ty, - mutbl: _, - }| { - has_ptr_meta(fx.tcx, pointee_ty) - }, - ) + .map(|ty::TypeAndMut { ty: pointee_ty, mutbl: _ }| { + has_ptr_meta(fx.tcx, pointee_ty) + }) .unwrap_or(false) } @@ -626,50 +586,22 @@ fn codegen_stmt<'tcx>( ty::Uint(_) | ty::Int(_) => {} _ => unreachable!("cast adt {} -> {}", from_ty, to_ty), } + let to_clif_ty = fx.clif_type(to_ty).unwrap(); - use rustc_target::abi::{Int, TagEncoding, Variants}; - - match operand.layout().variants { - Variants::Single { index } => { - let discr = operand - .layout() - .ty - .discriminant_for_variant(fx.tcx, index) - .unwrap(); - let discr = if discr.ty.is_signed() { - fx.layout_of(discr.ty).size.sign_extend(discr.val) - } else { - discr.val - }; - let discr = discr.into(); - - let discr = CValue::const_val(fx, fx.layout_of(to_ty), discr); - lval.write_cvalue(fx, discr); - } - Variants::Multiple { - ref tag, - tag_field, - tag_encoding: TagEncoding::Direct, - variants: _, - } => { - let cast_to = fx.clif_type(dest_layout.ty).unwrap(); - - // Read the tag/niche-encoded discriminant from memory. - let encoded_discr = - operand.value_field(fx, mir::Field::new(tag_field)); - let encoded_discr = encoded_discr.load_scalar(fx); - - // Decode the discriminant (specifically if it's niche-encoded). - let signed = match tag.value { - Int(_, signed) => signed, - _ => false, - }; - let val = clif_intcast(fx, encoded_discr, cast_to, signed); - let val = CValue::by_val(val, dest_layout); - lval.write_cvalue(fx, val); - } - Variants::Multiple { .. } => unreachable!(), - } + let discriminant = crate::discriminant::codegen_get_discriminant( + fx, + operand, + fx.layout_of(operand.layout().ty.discriminant_ty(fx.tcx)), + ) + .load_scalar(fx); + + let res = crate::cast::clif_intcast( + fx, + discriminant, + to_clif_ty, + to_ty.is_signed(), + ); + lval.write_cvalue(fx, CValue::by_val(res, dest_layout)); } else { let to_clif_ty = fx.clif_type(to_ty).unwrap(); let from = operand.load_scalar(fx); @@ -725,13 +657,14 @@ fn codegen_stmt<'tcx>( .val .try_to_bits(fx.tcx.data_layout.pointer_size) .unwrap(); - if fx.clif_type(operand.layout().ty) == Some(types::I8) { + if operand.layout().size.bytes() == 0 { + // Do nothing for ZST's + } else if fx.clif_type(operand.layout().ty) == Some(types::I8) { let times = fx.bcx.ins().iconst(fx.pointer_type, times as i64); // FIXME use emit_small_memset where possible let addr = lval.to_ptr().get_addr(fx); let val = operand.load_scalar(fx); - fx.bcx - .call_memset(fx.cx.module.target_config(), addr, val, times); + fx.bcx.call_memset(fx.cx.module.target_config(), addr, val, times); } else { let loop_block = fx.bcx.create_block(); let loop_block2 = fx.bcx.create_block(); @@ -766,25 +699,19 @@ fn codegen_stmt<'tcx>( let content_ty = fx.monomorphize(content_ty); let layout = fx.layout_of(content_ty); let llsize = fx.bcx.ins().iconst(usize_type, layout.size.bytes() as i64); - let llalign = fx - .bcx - .ins() - .iconst(usize_type, layout.align.abi.bytes() as i64); + let llalign = fx.bcx.ins().iconst(usize_type, layout.align.abi.bytes() as i64); let box_layout = fx.layout_of(fx.tcx.mk_box(content_ty)); // Allocate space: - let def_id = match fx - .tcx - .lang_items() - .require(rustc_hir::LangItem::ExchangeMalloc) - { - Ok(id) => id, - Err(s) => { - fx.tcx - .sess - .fatal(&format!("allocation of `{}` {}", box_layout.ty, s)); - } - }; + let def_id = + match fx.tcx.lang_items().require(rustc_hir::LangItem::ExchangeMalloc) { + Ok(id) => id, + Err(s) => { + fx.tcx + .sess + .fatal(&format!("allocation of `{}` {}", box_layout.ty, s)); + } + }; let instance = ty::Instance::mono(fx.tcx, def_id).polymorphize(fx.tcx); let func_ref = fx.get_function_ref(instance); let call = fx.bcx.ins().call(func_ref, &[llsize, llalign]); @@ -792,10 +719,11 @@ fn codegen_stmt<'tcx>( lval.write_cvalue(fx, CValue::by_val(ptr, box_layout)); } Rvalue::NullaryOp(NullOp::SizeOf, ty) => { - assert!(lval - .layout() - .ty - .is_sized(fx.tcx.at(stmt.source_info.span), ParamEnv::reveal_all())); + assert!( + lval.layout() + .ty + .is_sized(fx.tcx.at(stmt.source_info.span), ParamEnv::reveal_all()) + ); let ty_size = fx.layout_of(fx.monomorphize(ty)).size.bytes(); let val = CValue::const_val(fx, fx.layout_of(fx.tcx.types.usize), ty_size.into()); @@ -823,11 +751,7 @@ fn codegen_stmt<'tcx>( StatementKind::LlvmInlineAsm(asm) => { use rustc_span::symbol::Symbol; - let LlvmInlineAsm { - asm, - outputs, - inputs, - } = &**asm; + let LlvmInlineAsm { asm, outputs, inputs } = &**asm; let rustc_hir::LlvmInlineAsmInner { asm: asm_code, // Name outputs: output_names, // Vec @@ -843,15 +767,9 @@ fn codegen_stmt<'tcx>( // Black box } "mov %rbx, %rsi\n cpuid\n xchg %rbx, %rsi" => { - assert_eq!( - input_names, - &[Symbol::intern("{eax}"), Symbol::intern("{ecx}")] - ); + assert_eq!(input_names, &[Symbol::intern("{eax}"), Symbol::intern("{ecx}")]); assert_eq!(output_names.len(), 4); - for (i, c) in (&["={eax}", "={esi}", "={ecx}", "={edx}"]) - .iter() - .enumerate() - { + for (i, c) in (&["={eax}", "={esi}", "={ecx}", "={edx}"]).iter().enumerate() { assert_eq!(&output_names[i].constraint.as_str(), c); assert!(!output_names[i].is_rw); assert!(!output_names[i].is_indirect); @@ -897,12 +815,7 @@ fn codegen_stmt<'tcx>( crate::trap::trap_unimplemented(fx, "_xgetbv arch intrinsic is not supported"); } // ___chkstk, ___chkstk_ms and __alloca are only used on Windows - _ if fx - .tcx - .symbol_name(fx.instance) - .name - .starts_with("___chkstk") => - { + _ if fx.tcx.symbol_name(fx.instance).name.starts_with("___chkstk") => { crate::trap::trap_unimplemented(fx, "Stack probes are not supported"); } _ if fx.tcx.symbol_name(fx.instance).name == "__alloca" => { @@ -919,30 +832,38 @@ fn codegen_stmt<'tcx>( } } StatementKind::Coverage { .. } => fx.tcx.sess.fatal("-Zcoverage is unimplemented"), + StatementKind::CopyNonOverlapping(inner) => { + let dst = codegen_operand(fx, &inner.dst); + let pointee = dst + .layout() + .pointee_info_at(fx, rustc_target::abi::Size::ZERO) + .expect("Expected pointer"); + let dst = dst.load_scalar(fx); + let src = codegen_operand(fx, &inner.src).load_scalar(fx); + let count = codegen_operand(fx, &inner.count).load_scalar(fx); + let elem_size: u64 = pointee.size.bytes(); + let bytes = + if elem_size != 1 { fx.bcx.ins().imul_imm(count, elem_size as i64) } else { count }; + fx.bcx.call_memcpy(fx.cx.module.target_config(), dst, src, bytes); + } } } -fn codegen_array_len<'tcx>( - fx: &mut FunctionCx<'_, 'tcx, impl Module>, - place: CPlace<'tcx>, -) -> Value { +fn codegen_array_len<'tcx>(fx: &mut FunctionCx<'_, '_, 'tcx>, place: CPlace<'tcx>) -> Value { match *place.layout().ty.kind() { ty::Array(_elem_ty, len) => { - let len = fx - .monomorphize(len) - .eval_usize(fx.tcx, ParamEnv::reveal_all()) as i64; + let len = fx.monomorphize(len).eval_usize(fx.tcx, ParamEnv::reveal_all()) as i64; fx.bcx.ins().iconst(fx.pointer_type, len) } - ty::Slice(_elem_ty) => place - .to_ptr_maybe_unsized() - .1 - .expect("Length metadata for slice place"), + ty::Slice(_elem_ty) => { + place.to_ptr_maybe_unsized().1.expect("Length metadata for slice place") + } _ => bug!("Rvalue::Len({:?})", place), } } pub(crate) fn codegen_place<'tcx>( - fx: &mut FunctionCx<'_, 'tcx, impl Module>, + fx: &mut FunctionCx<'_, '_, 'tcx>, place: Place<'tcx>, ) -> CPlace<'tcx> { let mut cplace = fx.get_local_place(place.local); @@ -959,11 +880,7 @@ pub(crate) fn codegen_place<'tcx>( let index = fx.get_local_place(local).to_cvalue(fx).load_scalar(fx); cplace = cplace.place_index(fx, index); } - PlaceElem::ConstantIndex { - offset, - min_length: _, - from_end, - } => { + PlaceElem::ConstantIndex { offset, min_length: _, from_end } => { let offset: u64 = offset; let index = if !from_end { fx.bcx.ins().iconst(fx.pointer_type, offset as i64) @@ -1014,7 +931,7 @@ pub(crate) fn codegen_place<'tcx>( } pub(crate) fn codegen_operand<'tcx>( - fx: &mut FunctionCx<'_, 'tcx, impl Module>, + fx: &mut FunctionCx<'_, '_, 'tcx>, operand: &Operand<'tcx>, ) -> CValue<'tcx> { match operand { @@ -1026,34 +943,24 @@ pub(crate) fn codegen_operand<'tcx>( } } -pub(crate) fn codegen_panic<'tcx>( - fx: &mut FunctionCx<'_, 'tcx, impl Module>, - msg_str: &str, - span: Span, -) { +pub(crate) fn codegen_panic<'tcx>(fx: &mut FunctionCx<'_, '_, 'tcx>, msg_str: &str, span: Span) { let location = fx.get_caller_location(span).load_scalar(fx); let msg_ptr = fx.anonymous_str("assert", msg_str); - let msg_len = fx - .bcx - .ins() - .iconst(fx.pointer_type, i64::try_from(msg_str.len()).unwrap()); + let msg_len = fx.bcx.ins().iconst(fx.pointer_type, i64::try_from(msg_str.len()).unwrap()); let args = [msg_ptr, msg_len, location]; codegen_panic_inner(fx, rustc_hir::LangItem::Panic, &args, span); } pub(crate) fn codegen_panic_inner<'tcx>( - fx: &mut FunctionCx<'_, 'tcx, impl Module>, + fx: &mut FunctionCx<'_, '_, 'tcx>, lang_item: rustc_hir::LangItem, args: &[Value], span: Span, ) { - let def_id = fx - .tcx - .lang_items() - .require(lang_item) - .unwrap_or_else(|s| fx.tcx.sess.span_fatal(span, &s)); + let def_id = + fx.tcx.lang_items().require(lang_item).unwrap_or_else(|s| fx.tcx.sess.span_fatal(span, &s)); let instance = Instance::mono(fx.tcx, def_id).polymorphize(fx.tcx); let symbol_name = fx.tcx.symbol_name(instance).name; diff --git a/compiler/rustc_codegen_cranelift/src/bin/cg_clif.rs b/compiler/rustc_codegen_cranelift/src/bin/cg_clif.rs index be369b07fddfe..983839d48d2d7 100644 --- a/compiler/rustc_codegen_cranelift/src/bin/cg_clif.rs +++ b/compiler/rustc_codegen_cranelift/src/bin/cg_clif.rs @@ -27,13 +27,7 @@ impl rustc_driver::Callbacks for CraneliftPassesCallbacks { config.opts.cg.panic = Some(PanicStrategy::Abort); config.opts.debugging_opts.panic_abort_tests = true; config.opts.maybe_sysroot = Some(config.opts.maybe_sysroot.clone().unwrap_or_else(|| { - std::env::current_exe() - .unwrap() - .parent() - .unwrap() - .parent() - .unwrap() - .to_owned() + std::env::current_exe().unwrap().parent().unwrap().parent().unwrap().to_owned() })); } } diff --git a/compiler/rustc_codegen_cranelift/src/bin/cg_clif_build_sysroot.rs b/compiler/rustc_codegen_cranelift/src/bin/cg_clif_build_sysroot.rs index 83e5dc6e6724d..e7cd5edbbf654 100644 --- a/compiler/rustc_codegen_cranelift/src/bin/cg_clif_build_sysroot.rs +++ b/compiler/rustc_codegen_cranelift/src/bin/cg_clif_build_sysroot.rs @@ -46,15 +46,8 @@ impl rustc_driver::Callbacks for CraneliftPassesCallbacks { config.opts.cg.panic = Some(PanicStrategy::Abort); config.opts.debugging_opts.panic_abort_tests = true; - config.opts.maybe_sysroot = Some( - std::env::current_exe() - .unwrap() - .parent() - .unwrap() - .parent() - .unwrap() - .to_owned(), - ); + config.opts.maybe_sysroot = + Some(std::env::current_exe().unwrap().parent().unwrap().parent().unwrap().to_owned()); } } diff --git a/compiler/rustc_codegen_cranelift/src/cast.rs b/compiler/rustc_codegen_cranelift/src/cast.rs index 57204de1135be..74c5e09f08da0 100644 --- a/compiler/rustc_codegen_cranelift/src/cast.rs +++ b/compiler/rustc_codegen_cranelift/src/cast.rs @@ -3,7 +3,7 @@ use crate::prelude::*; pub(crate) fn clif_intcast( - fx: &mut FunctionCx<'_, '_, impl Module>, + fx: &mut FunctionCx<'_, '_, '_>, val: Value, to: Type, signed: bool, @@ -40,18 +40,14 @@ pub(crate) fn clif_intcast( // reduce (types::I128, _) => { let (lsb, _msb) = fx.bcx.ins().isplit(val); - if to == types::I64 { - lsb - } else { - fx.bcx.ins().ireduce(to, lsb) - } + if to == types::I64 { lsb } else { fx.bcx.ins().ireduce(to, lsb) } } (_, _) => fx.bcx.ins().ireduce(to, val), } } pub(crate) fn clif_int_or_float_cast( - fx: &mut FunctionCx<'_, '_, impl Module>, + fx: &mut FunctionCx<'_, '_, '_>, from: Value, from_signed: bool, to_ty: Type, @@ -87,11 +83,7 @@ pub(crate) fn clif_int_or_float_cast( }, ); - let from_rust_ty = if from_signed { - fx.tcx.types.i128 - } else { - fx.tcx.types.u128 - }; + let from_rust_ty = if from_signed { fx.tcx.types.i128 } else { fx.tcx.types.u128 }; let to_rust_ty = match to_ty { types::F32 => fx.tcx.types.f32, @@ -100,11 +92,7 @@ pub(crate) fn clif_int_or_float_cast( }; return fx - .easy_call( - &name, - &[CValue::by_val(from, fx.layout_of(from_rust_ty))], - to_rust_ty, - ) + .easy_call(&name, &[CValue::by_val(from, fx.layout_of(from_rust_ty))], to_rust_ty) .load_scalar(fx); } @@ -138,18 +126,10 @@ pub(crate) fn clif_int_or_float_cast( _ => unreachable!(), }; - let to_rust_ty = if to_signed { - fx.tcx.types.i128 - } else { - fx.tcx.types.u128 - }; + let to_rust_ty = if to_signed { fx.tcx.types.i128 } else { fx.tcx.types.u128 }; return fx - .easy_call( - &name, - &[CValue::by_val(from, fx.layout_of(from_rust_ty))], - to_rust_ty, - ) + .easy_call(&name, &[CValue::by_val(from, fx.layout_of(from_rust_ty))], to_rust_ty) .load_scalar(fx); } diff --git a/compiler/rustc_codegen_cranelift/src/codegen_i128.rs b/compiler/rustc_codegen_cranelift/src/codegen_i128.rs index 866ba90e4ae4b..ffe1922ab9056 100644 --- a/compiler/rustc_codegen_cranelift/src/codegen_i128.rs +++ b/compiler/rustc_codegen_cranelift/src/codegen_i128.rs @@ -5,13 +5,17 @@ use cranelift_codegen::ir::ArgumentPurpose; use crate::prelude::*; pub(crate) fn maybe_codegen<'tcx>( - fx: &mut FunctionCx<'_, 'tcx, impl Module>, + fx: &mut FunctionCx<'_, '_, 'tcx>, bin_op: BinOp, checked: bool, lhs: CValue<'tcx>, rhs: CValue<'tcx>, ) -> Option> { - if lhs.layout().ty != fx.tcx.types.u128 && lhs.layout().ty != fx.tcx.types.i128 { + if lhs.layout().ty != fx.tcx.types.u128 + && lhs.layout().ty != fx.tcx.types.i128 + && rhs.layout().ty != fx.tcx.types.u128 + && rhs.layout().ty != fx.tcx.types.i128 + { return None; } @@ -27,27 +31,57 @@ pub(crate) fn maybe_codegen<'tcx>( } BinOp::Add | BinOp::Sub if !checked => None, BinOp::Mul if !checked => { - let val_ty = if is_signed { - fx.tcx.types.i128 + let val_ty = if is_signed { fx.tcx.types.i128 } else { fx.tcx.types.u128 }; + if fx.tcx.sess.target.is_like_windows { + let ret_place = CPlace::new_stack_slot(fx, lhs.layout()); + let (lhs_ptr, lhs_extra) = lhs.force_stack(fx); + let (rhs_ptr, rhs_extra) = rhs.force_stack(fx); + assert!(lhs_extra.is_none()); + assert!(rhs_extra.is_none()); + let args = + [ret_place.to_ptr().get_addr(fx), lhs_ptr.get_addr(fx), rhs_ptr.get_addr(fx)]; + fx.lib_call( + "__multi3", + vec![ + AbiParam::special(pointer_ty(fx.tcx), ArgumentPurpose::StructReturn), + AbiParam::new(pointer_ty(fx.tcx)), + AbiParam::new(pointer_ty(fx.tcx)), + ], + vec![], + &args, + ); + Some(ret_place.to_cvalue(fx)) } else { - fx.tcx.types.u128 - }; - Some(fx.easy_call("__multi3", &[lhs, rhs], val_ty)) + Some(fx.easy_call("__multi3", &[lhs, rhs], val_ty)) + } } BinOp::Add | BinOp::Sub | BinOp::Mul => { assert!(checked); let out_ty = fx.tcx.mk_tup([lhs.layout().ty, fx.tcx.types.bool].iter()); let out_place = CPlace::new_stack_slot(fx, fx.layout_of(out_ty)); - let param_types = vec![ - AbiParam::special(pointer_ty(fx.tcx), ArgumentPurpose::StructReturn), - AbiParam::new(types::I128), - AbiParam::new(types::I128), - ]; - let args = [ - out_place.to_ptr().get_addr(fx), - lhs.load_scalar(fx), - rhs.load_scalar(fx), - ]; + let (param_types, args) = if fx.tcx.sess.target.is_like_windows { + let (lhs_ptr, lhs_extra) = lhs.force_stack(fx); + let (rhs_ptr, rhs_extra) = rhs.force_stack(fx); + assert!(lhs_extra.is_none()); + assert!(rhs_extra.is_none()); + ( + vec![ + AbiParam::special(pointer_ty(fx.tcx), ArgumentPurpose::StructReturn), + AbiParam::new(pointer_ty(fx.tcx)), + AbiParam::new(pointer_ty(fx.tcx)), + ], + [out_place.to_ptr().get_addr(fx), lhs_ptr.get_addr(fx), rhs_ptr.get_addr(fx)], + ) + } else { + ( + vec![ + AbiParam::special(pointer_ty(fx.tcx), ArgumentPurpose::StructReturn), + AbiParam::new(types::I128), + AbiParam::new(types::I128), + ], + [out_place.to_ptr().get_addr(fx), lhs.load_scalar(fx), rhs.load_scalar(fx)], + ) + }; let name = match (bin_op, is_signed) { (BinOp::Add, false) => "__rust_u128_addo", (BinOp::Add, true) => "__rust_i128_addo", @@ -61,20 +95,33 @@ pub(crate) fn maybe_codegen<'tcx>( Some(out_place.to_cvalue(fx)) } BinOp::Offset => unreachable!("offset should only be used on pointers, not 128bit ints"), - BinOp::Div => { - assert!(!checked); - if is_signed { - Some(fx.easy_call("__divti3", &[lhs, rhs], fx.tcx.types.i128)) - } else { - Some(fx.easy_call("__udivti3", &[lhs, rhs], fx.tcx.types.u128)) - } - } - BinOp::Rem => { + BinOp::Div | BinOp::Rem => { assert!(!checked); - if is_signed { - Some(fx.easy_call("__modti3", &[lhs, rhs], fx.tcx.types.i128)) + let name = match (bin_op, is_signed) { + (BinOp::Div, false) => "__udivti3", + (BinOp::Div, true) => "__divti3", + (BinOp::Rem, false) => "__umodti3", + (BinOp::Rem, true) => "__modti3", + _ => unreachable!(), + }; + if fx.tcx.sess.target.is_like_windows { + let (lhs_ptr, lhs_extra) = lhs.force_stack(fx); + let (rhs_ptr, rhs_extra) = rhs.force_stack(fx); + assert!(lhs_extra.is_none()); + assert!(rhs_extra.is_none()); + let args = [lhs_ptr.get_addr(fx), rhs_ptr.get_addr(fx)]; + let ret = fx.lib_call( + name, + vec![AbiParam::new(pointer_ty(fx.tcx)), AbiParam::new(pointer_ty(fx.tcx))], + vec![AbiParam::new(types::I64X2)], + &args, + )[0]; + // FIXME use bitcast instead of store to get from i64x2 to i128 + let ret_place = CPlace::new_stack_slot(fx, lhs.layout()); + ret_place.to_ptr().store(fx, ret, MemFlags::trusted()); + Some(ret_place.to_cvalue(fx)) } else { - Some(fx.easy_call("__umodti3", &[lhs, rhs], fx.tcx.types.u128)) + Some(fx.easy_call(name, &[lhs, rhs], lhs.layout().ty)) } } BinOp::Lt | BinOp::Le | BinOp::Eq | BinOp::Ge | BinOp::Gt | BinOp::Ne => { @@ -97,70 +144,23 @@ pub(crate) fn maybe_codegen<'tcx>( None }; - // Optimize `val >> 64`, because compiler_builtins uses it to deconstruct an 128bit - // integer into its lsb and msb. - // https://github.com/rust-lang-nursery/compiler-builtins/blob/79a6a1603d5672cbb9187ff41ff4d9b5048ac1cb/src/int/mod.rs#L217 - if resolve_value_imm(fx.bcx.func, rhs_val) == Some(64) { - let (lhs_lsb, lhs_msb) = fx.bcx.ins().isplit(lhs_val); - let all_zeros = fx.bcx.ins().iconst(types::I64, 0); - let val = match (bin_op, is_signed) { - (BinOp::Shr, false) => { - let val = fx.bcx.ins().iconcat(lhs_msb, all_zeros); - Some(CValue::by_val(val, fx.layout_of(fx.tcx.types.u128))) - } - (BinOp::Shr, true) => { - let sign = fx.bcx.ins().icmp_imm(IntCC::SignedLessThan, lhs_msb, 0); - let all_ones = fx.bcx.ins().iconst(types::I64, u64::MAX as i64); - let all_sign_bits = fx.bcx.ins().select(sign, all_zeros, all_ones); - - let val = fx.bcx.ins().iconcat(lhs_msb, all_sign_bits); - Some(CValue::by_val(val, fx.layout_of(fx.tcx.types.i128))) - } - (BinOp::Shl, _) => { - let val_ty = if is_signed { - fx.tcx.types.i128 - } else { - fx.tcx.types.u128 - }; - let val = fx.bcx.ins().iconcat(all_zeros, lhs_lsb); - Some(CValue::by_val(val, fx.layout_of(val_ty))) - } - _ => None, - }; - if let Some(val) = val { - if let Some(is_overflow) = is_overflow { - let out_ty = fx.tcx.mk_tup([lhs.layout().ty, fx.tcx.types.bool].iter()); - let val = val.load_scalar(fx); - return Some(CValue::by_val_pair(val, is_overflow, fx.layout_of(out_ty))); + let truncated_rhs = clif_intcast(fx, rhs_val, types::I32, false); + let val = match bin_op { + BinOp::Shl => fx.bcx.ins().ishl(lhs_val, truncated_rhs), + BinOp::Shr => { + if is_signed { + fx.bcx.ins().sshr(lhs_val, truncated_rhs) } else { - return Some(val); + fx.bcx.ins().ushr(lhs_val, truncated_rhs) } } - } - - let truncated_rhs = clif_intcast(fx, rhs_val, types::I32, false); - let truncated_rhs = CValue::by_val(truncated_rhs, fx.layout_of(fx.tcx.types.u32)); - let val = match (bin_op, is_signed) { - (BinOp::Shl, false) => { - fx.easy_call("__ashlti3", &[lhs, truncated_rhs], fx.tcx.types.u128) - } - (BinOp::Shl, true) => { - fx.easy_call("__ashlti3", &[lhs, truncated_rhs], fx.tcx.types.i128) - } - (BinOp::Shr, false) => { - fx.easy_call("__lshrti3", &[lhs, truncated_rhs], fx.tcx.types.u128) - } - (BinOp::Shr, true) => { - fx.easy_call("__ashrti3", &[lhs, truncated_rhs], fx.tcx.types.i128) - } - (_, _) => unreachable!(), + _ => unreachable!(), }; if let Some(is_overflow) = is_overflow { let out_ty = fx.tcx.mk_tup([lhs.layout().ty, fx.tcx.types.bool].iter()); - let val = val.load_scalar(fx); Some(CValue::by_val_pair(val, is_overflow, fx.layout_of(out_ty))) } else { - Some(val) + Some(CValue::by_val(val, lhs.layout())) } } } diff --git a/compiler/rustc_codegen_cranelift/src/common.rs b/compiler/rustc_codegen_cranelift/src/common.rs index fbee84e09f7a6..b5874f62535ca 100644 --- a/compiler/rustc_codegen_cranelift/src/common.rs +++ b/compiler/rustc_codegen_cranelift/src/common.rs @@ -3,8 +3,6 @@ use rustc_target::abi::call::FnAbi; use rustc_target::abi::{Integer, Primitive}; use rustc_target::spec::{HasTargetSpec, Target}; -use cranelift_codegen::ir::{InstructionData, Opcode, ValueDef}; - use crate::prelude::*; pub(crate) fn pointer_ty(tcx: TyCtxt<'_>) -> types::Type { @@ -56,11 +54,7 @@ fn clif_type_from_ty<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> Option types::F64, }, ty::FnPtr(_) => pointer_ty(tcx), - ty::RawPtr(TypeAndMut { - ty: pointee_ty, - mutbl: _, - }) - | ty::Ref(_, pointee_ty, _) => { + ty::RawPtr(TypeAndMut { ty: pointee_ty, mutbl: _ }) | ty::Ref(_, pointee_ty, _) => { if has_ptr_meta(tcx, pointee_ty) { return None; } else { @@ -99,11 +93,7 @@ fn clif_pair_type_from_ty<'tcx>( } (a, b) } - ty::RawPtr(TypeAndMut { - ty: pointee_ty, - mutbl: _, - }) - | ty::Ref(_, pointee_ty, _) => { + ty::RawPtr(TypeAndMut { ty: pointee_ty, mutbl: _ }) | ty::Ref(_, pointee_ty, _) => { if has_ptr_meta(tcx, pointee_ty) { (pointer_ty(tcx), pointer_ty(tcx)) } else { @@ -116,15 +106,8 @@ fn clif_pair_type_from_ty<'tcx>( /// Is a pointer to this type a fat ptr? pub(crate) fn has_ptr_meta<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> bool { - let ptr_ty = tcx.mk_ptr(TypeAndMut { - ty, - mutbl: rustc_hir::Mutability::Not, - }); - match &tcx - .layout_of(ParamEnv::reveal_all().and(ptr_ty)) - .unwrap() - .abi - { + let ptr_ty = tcx.mk_ptr(TypeAndMut { ty, mutbl: rustc_hir::Mutability::Not }); + match &tcx.layout_of(ParamEnv::reveal_all().and(ptr_ty)).unwrap().abi { Abi::Scalar(_) => false, Abi::ScalarPair(_, _) => true, abi => unreachable!("Abi of ptr to {:?} is {:?}???", ty, abi), @@ -132,7 +115,7 @@ pub(crate) fn has_ptr_meta<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> bool { } pub(crate) fn codegen_icmp_imm( - fx: &mut FunctionCx<'_, '_, impl Module>, + fx: &mut FunctionCx<'_, '_, '_>, intcc: IntCC, lhs: Value, rhs: i128, @@ -175,51 +158,6 @@ pub(crate) fn codegen_icmp_imm( } } -fn resolve_normal_value_imm(func: &Function, val: Value) -> Option { - if let ValueDef::Result(inst, 0 /*param*/) = func.dfg.value_def(val) { - if let InstructionData::UnaryImm { - opcode: Opcode::Iconst, - imm, - } = func.dfg[inst] - { - Some(imm.into()) - } else { - None - } - } else { - None - } -} - -fn resolve_128bit_value_imm(func: &Function, val: Value) -> Option { - let (lsb, msb) = if let ValueDef::Result(inst, 0 /*param*/) = func.dfg.value_def(val) { - if let InstructionData::Binary { - opcode: Opcode::Iconcat, - args: [lsb, msb], - } = func.dfg[inst] - { - (lsb, msb) - } else { - return None; - } - } else { - return None; - }; - - let lsb = u128::from(resolve_normal_value_imm(func, lsb)? as u64); - let msb = u128::from(resolve_normal_value_imm(func, msb)? as u64); - - Some(msb << 64 | lsb) -} - -pub(crate) fn resolve_value_imm(func: &Function, val: Value) -> Option { - if func.dfg.value_type(val) == types::I128 { - resolve_128bit_value_imm(func, val) - } else { - resolve_normal_value_imm(func, val).map(|imm| u128::from(imm as u64)) - } -} - pub(crate) fn type_min_max_value( bcx: &mut FunctionBuilder<'_>, ty: Type, @@ -288,8 +226,8 @@ pub(crate) fn type_sign(ty: Ty<'_>) -> bool { } } -pub(crate) struct FunctionCx<'clif, 'tcx, M: Module> { - pub(crate) cx: &'clif mut crate::CodegenCx<'tcx, M>, +pub(crate) struct FunctionCx<'m, 'clif, 'tcx> { + pub(crate) cx: &'clif mut crate::CodegenCx<'m, 'tcx>, pub(crate) tcx: TyCtxt<'tcx>, pub(crate) pointer_type: Type, // Cached from module @@ -316,7 +254,7 @@ pub(crate) struct FunctionCx<'clif, 'tcx, M: Module> { pub(crate) inline_asm_index: u32, } -impl<'tcx, M: Module> LayoutOf for FunctionCx<'_, 'tcx, M> { +impl<'tcx> LayoutOf for FunctionCx<'_, '_, 'tcx> { type Ty = Ty<'tcx>; type TyAndLayout = TyAndLayout<'tcx>; @@ -325,31 +263,31 @@ impl<'tcx, M: Module> LayoutOf for FunctionCx<'_, 'tcx, M> { } } -impl<'tcx, M: Module> layout::HasTyCtxt<'tcx> for FunctionCx<'_, 'tcx, M> { +impl<'tcx> layout::HasTyCtxt<'tcx> for FunctionCx<'_, '_, 'tcx> { fn tcx<'b>(&'b self) -> TyCtxt<'tcx> { self.tcx } } -impl<'tcx, M: Module> rustc_target::abi::HasDataLayout for FunctionCx<'_, 'tcx, M> { +impl<'tcx> rustc_target::abi::HasDataLayout for FunctionCx<'_, '_, 'tcx> { fn data_layout(&self) -> &rustc_target::abi::TargetDataLayout { &self.tcx.data_layout } } -impl<'tcx, M: Module> layout::HasParamEnv<'tcx> for FunctionCx<'_, 'tcx, M> { +impl<'tcx> layout::HasParamEnv<'tcx> for FunctionCx<'_, '_, 'tcx> { fn param_env(&self) -> ParamEnv<'tcx> { ParamEnv::reveal_all() } } -impl<'tcx, M: Module> HasTargetSpec for FunctionCx<'_, 'tcx, M> { +impl<'tcx> HasTargetSpec for FunctionCx<'_, '_, 'tcx> { fn target_spec(&self) -> &Target { &self.tcx.sess.target } } -impl<'tcx, M: Module> FunctionCx<'_, 'tcx, M> { +impl<'tcx> FunctionCx<'_, '_, 'tcx> { pub(crate) fn monomorphize(&self, value: T) -> T where T: TypeFoldable<'tcx> + Copy, @@ -416,20 +354,14 @@ impl<'tcx, M: Module> FunctionCx<'_, 'tcx, M> { let msg_id = self .cx .module - .declare_data( - &format!("__{}_{:08x}", prefix, msg_hash), - Linkage::Local, - false, - false, - ) + .declare_data(&format!("__{}_{:08x}", prefix, msg_hash), Linkage::Local, false, false) .unwrap(); // Ignore DuplicateDefinition error, as the data will be the same let _ = self.cx.module.define_data(msg_id, &data_ctx); let local_msg_id = self.cx.module.declare_data_in_func(msg_id, self.bcx.func); - #[cfg(debug_assertions)] - { + if self.clif_comments.enabled() { self.add_comment(local_msg_id, msg); } self.bcx.ins().global_value(self.pointer_type, local_msg_id) @@ -444,15 +376,13 @@ impl<'tcx> LayoutOf for RevealAllLayoutCx<'tcx> { fn layout_of(&self, ty: Ty<'tcx>) -> TyAndLayout<'tcx> { assert!(!ty.still_further_specializable()); - self.0 - .layout_of(ParamEnv::reveal_all().and(&ty)) - .unwrap_or_else(|e| { - if let layout::LayoutError::SizeOverflow(_) = e { - self.0.sess.fatal(&e.to_string()) - } else { - bug!("failed to get layout for `{}`: {}", ty, e) - } - }) + self.0.layout_of(ParamEnv::reveal_all().and(&ty)).unwrap_or_else(|e| { + if let layout::LayoutError::SizeOverflow(_) = e { + self.0.sess.fatal(&e.to_string()) + } else { + bug!("failed to get layout for `{}`: {}", ty, e) + } + }) } } diff --git a/compiler/rustc_codegen_cranelift/src/compiler_builtins.rs b/compiler/rustc_codegen_cranelift/src/compiler_builtins.rs new file mode 100644 index 0000000000000..177f850afb398 --- /dev/null +++ b/compiler/rustc_codegen_cranelift/src/compiler_builtins.rs @@ -0,0 +1,41 @@ +macro builtin_functions($register:ident; $(fn $name:ident($($arg_name:ident: $arg_ty:ty),*) -> $ret_ty:ty;)*) { + #[cfg(feature = "jit")] + #[allow(improper_ctypes)] + extern "C" { + $(fn $name($($arg_name: $arg_ty),*) -> $ret_ty;)* + } + + #[cfg(feature = "jit")] + pub(crate) fn $register(builder: &mut cranelift_jit::JITBuilder) { + for &(name, val) in &[$((stringify!($name), $name as *const u8)),*] { + builder.symbol(name, val); + } + } +} + +builtin_functions! { + register_functions_for_jit; + + // integers + fn __multi3(a: i128, b: i128) -> i128; + fn __udivti3(n: u128, d: u128) -> u128; + fn __divti3(n: i128, d: i128) -> i128; + fn __umodti3(n: u128, d: u128) -> u128; + fn __modti3(n: i128, d: i128) -> i128; + fn __rust_u128_addo(a: u128, b: u128) -> (u128, bool); + fn __rust_i128_addo(a: i128, b: i128) -> (i128, bool); + fn __rust_u128_subo(a: u128, b: u128) -> (u128, bool); + fn __rust_i128_subo(a: i128, b: i128) -> (i128, bool); + fn __rust_u128_mulo(a: u128, b: u128) -> (u128, bool); + fn __rust_i128_mulo(a: i128, b: i128) -> (i128, bool); + + // floats + fn __floattisf(i: i128) -> f32; + fn __floattidf(i: i128) -> f64; + fn __floatuntisf(i: u128) -> f32; + fn __floatuntidf(i: u128) -> f64; + fn __fixsfti(f: f32) -> i128; + fn __fixdfti(f: f64) -> i128; + fn __fixunssfti(f: f32) -> u128; + fn __fixunsdfti(f: f64) -> u128; +} diff --git a/compiler/rustc_codegen_cranelift/src/constant.rs b/compiler/rustc_codegen_cranelift/src/constant.rs index 5702832bcb67d..fcd41c844659d 100644 --- a/compiler/rustc_codegen_cranelift/src/constant.rs +++ b/compiler/rustc_codegen_cranelift/src/constant.rs @@ -8,7 +8,7 @@ use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags; use rustc_middle::mir::interpret::{ read_target_uint, AllocId, Allocation, ConstValue, ErrorHandled, GlobalAlloc, Pointer, Scalar, }; -use rustc_middle::ty::{Const, ConstKind}; +use rustc_middle::ty::ConstKind; use cranelift_codegen::ir::GlobalValueData; use cranelift_module::*; @@ -28,7 +28,7 @@ enum TodoItem { } impl ConstantCx { - pub(crate) fn finalize(mut self, tcx: TyCtxt<'_>, module: &mut impl Module) { + pub(crate) fn finalize(mut self, tcx: TyCtxt<'_>, module: &mut dyn Module) { //println!("todo {:?}", self.todo); define_all_allocs(tcx, module, &mut self); //println!("done {:?}", self.done); @@ -36,21 +36,23 @@ impl ConstantCx { } } -pub(crate) fn check_constants(fx: &mut FunctionCx<'_, '_, impl Module>) { +pub(crate) fn check_constants(fx: &mut FunctionCx<'_, '_, '_>) -> bool { + let mut all_constants_ok = true; for constant in &fx.mir.required_consts { - let const_ = fx.monomorphize(constant.literal); + let const_ = match fx.monomorphize(constant.literal) { + ConstantKind::Ty(ct) => ct, + ConstantKind::Val(..) => continue, + }; match const_.val { ConstKind::Value(_) => {} - ConstKind::Unevaluated(def, ref substs, promoted) => { + ConstKind::Unevaluated(unevaluated) => { if let Err(err) = - fx.tcx - .const_eval_resolve(ParamEnv::reveal_all(), def, substs, promoted, None) + fx.tcx.const_eval_resolve(ParamEnv::reveal_all(), unevaluated, None) { + all_constants_ok = false; match err { ErrorHandled::Reported(ErrorReported) | ErrorHandled::Linted => { - fx.tcx - .sess - .span_err(constant.span, "erroneous constant encountered"); + fx.tcx.sess.span_err(constant.span, "erroneous constant encountered"); } ErrorHandled::TooGeneric => { span_bug!( @@ -69,6 +71,7 @@ pub(crate) fn check_constants(fx: &mut FunctionCx<'_, '_, impl Module>) { | ConstKind::Error(_) => unreachable!("{:?}", const_), } } + all_constants_ok } pub(crate) fn codegen_static(constants_cx: &mut ConstantCx, def_id: DefId) { @@ -76,27 +79,29 @@ pub(crate) fn codegen_static(constants_cx: &mut ConstantCx, def_id: DefId) { } pub(crate) fn codegen_tls_ref<'tcx>( - fx: &mut FunctionCx<'_, 'tcx, impl Module>, + fx: &mut FunctionCx<'_, '_, 'tcx>, def_id: DefId, layout: TyAndLayout<'tcx>, ) -> CValue<'tcx> { - let data_id = data_id_for_static(fx.tcx, &mut fx.cx.module, def_id, false); + let data_id = data_id_for_static(fx.tcx, fx.cx.module, def_id, false); let local_data_id = fx.cx.module.declare_data_in_func(data_id, &mut fx.bcx.func); - #[cfg(debug_assertions)] - fx.add_comment(local_data_id, format!("tls {:?}", def_id)); + if fx.clif_comments.enabled() { + fx.add_comment(local_data_id, format!("tls {:?}", def_id)); + } let tls_ptr = fx.bcx.ins().tls_value(fx.pointer_type, local_data_id); CValue::by_val(tls_ptr, layout) } fn codegen_static_ref<'tcx>( - fx: &mut FunctionCx<'_, 'tcx, impl Module>, + fx: &mut FunctionCx<'_, '_, 'tcx>, def_id: DefId, layout: TyAndLayout<'tcx>, ) -> CPlace<'tcx> { - let data_id = data_id_for_static(fx.tcx, &mut fx.cx.module, def_id, false); + let data_id = data_id_for_static(fx.tcx, fx.cx.module, def_id, false); let local_data_id = fx.cx.module.declare_data_in_func(data_id, &mut fx.bcx.func); - #[cfg(debug_assertions)] - fx.add_comment(local_data_id, format!("{:?}", def_id)); + if fx.clif_comments.enabled() { + fx.add_comment(local_data_id, format!("{:?}", def_id)); + } let global_ptr = fx.bcx.ins().global_value(fx.pointer_type, local_data_id); assert!(!layout.is_unsized(), "unsized statics aren't supported"); assert!( @@ -110,38 +115,28 @@ fn codegen_static_ref<'tcx>( } pub(crate) fn codegen_constant<'tcx>( - fx: &mut FunctionCx<'_, 'tcx, impl Module>, + fx: &mut FunctionCx<'_, '_, 'tcx>, constant: &Constant<'tcx>, ) -> CValue<'tcx> { - let const_ = fx.monomorphize(constant.literal); + let const_ = match fx.monomorphize(constant.literal) { + ConstantKind::Ty(ct) => ct, + ConstantKind::Val(val, ty) => return codegen_const_value(fx, val, ty), + }; let const_val = match const_.val { ConstKind::Value(const_val) => const_val, - ConstKind::Unevaluated(def, ref substs, promoted) if fx.tcx.is_static(def.did) => { + ConstKind::Unevaluated(ty::Unevaluated { def, substs, promoted }) + if fx.tcx.is_static(def.did) => + { assert!(substs.is_empty()); assert!(promoted.is_none()); - return codegen_static_ref( - fx, - def.did, - fx.layout_of(fx.monomorphize(&constant.literal.ty)), - ) - .to_cvalue(fx); + return codegen_static_ref(fx, def.did, fx.layout_of(const_.ty)).to_cvalue(fx); } - ConstKind::Unevaluated(def, ref substs, promoted) => { - match fx - .tcx - .const_eval_resolve(ParamEnv::reveal_all(), def, substs, promoted, None) - { + ConstKind::Unevaluated(unevaluated) => { + match fx.tcx.const_eval_resolve(ParamEnv::reveal_all(), unevaluated, None) { Ok(const_val) => const_val, Err(_) => { - fx.tcx - .sess - .span_err(constant.span, "erroneous constant encountered"); - return crate::trap::trap_unreachable_ret_value( - fx, - fx.layout_of(const_.ty), - "erroneous constant encountered", - ); + span_bug!(constant.span, "erroneous constant not captured by required_consts"); } } } @@ -156,7 +151,7 @@ pub(crate) fn codegen_constant<'tcx>( } pub(crate) fn codegen_const_value<'tcx>( - fx: &mut FunctionCx<'_, 'tcx, impl Module>, + fx: &mut FunctionCx<'_, '_, 'tcx>, const_val: ConstValue<'tcx>, ty: Ty<'tcx>, ) -> CValue<'tcx> { @@ -172,9 +167,7 @@ pub(crate) fn codegen_const_value<'tcx>( if fx.clif_type(layout.ty).is_none() { let (size, align) = (layout.size, layout.align.pref); let mut alloc = Allocation::from_bytes( - std::iter::repeat(0) - .take(size.bytes_usize()) - .collect::>(), + std::iter::repeat(0).take(size.bytes_usize()).collect::>(), align, ); let ptr = Pointer::new(AllocId(!0), Size::ZERO); // The alloc id is never used @@ -190,40 +183,36 @@ pub(crate) fn codegen_const_value<'tcx>( let base_addr = match alloc_kind { Some(GlobalAlloc::Memory(alloc)) => { fx.cx.constants_cx.todo.push(TodoItem::Alloc(ptr.alloc_id)); - let data_id = data_id_for_alloc_id( - &mut fx.cx.module, - ptr.alloc_id, - alloc.mutability, - ); + let data_id = + data_id_for_alloc_id(fx.cx.module, ptr.alloc_id, alloc.mutability); let local_data_id = fx.cx.module.declare_data_in_func(data_id, &mut fx.bcx.func); - #[cfg(debug_assertions)] - fx.add_comment(local_data_id, format!("{:?}", ptr.alloc_id)); + if fx.clif_comments.enabled() { + fx.add_comment(local_data_id, format!("{:?}", ptr.alloc_id)); + } fx.bcx.ins().global_value(fx.pointer_type, local_data_id) } Some(GlobalAlloc::Function(instance)) => { let func_id = - crate::abi::import_function(fx.tcx, &mut fx.cx.module, instance); + crate::abi::import_function(fx.tcx, fx.cx.module, instance); let local_func_id = fx.cx.module.declare_func_in_func(func_id, &mut fx.bcx.func); fx.bcx.ins().func_addr(fx.pointer_type, local_func_id) } Some(GlobalAlloc::Static(def_id)) => { assert!(fx.tcx.is_static(def_id)); - let data_id = - data_id_for_static(fx.tcx, &mut fx.cx.module, def_id, false); + let data_id = data_id_for_static(fx.tcx, fx.cx.module, def_id, false); let local_data_id = fx.cx.module.declare_data_in_func(data_id, &mut fx.bcx.func); - #[cfg(debug_assertions)] - fx.add_comment(local_data_id, format!("{:?}", def_id)); + if fx.clif_comments.enabled() { + fx.add_comment(local_data_id, format!("{:?}", def_id)); + } fx.bcx.ins().global_value(fx.pointer_type, local_data_id) } None => bug!("missing allocation {:?}", ptr.alloc_id), }; let val = if ptr.offset.bytes() != 0 { - fx.bcx - .ins() - .iadd_imm(base_addr, i64::try_from(ptr.offset.bytes()).unwrap()) + fx.bcx.ins().iadd_imm(base_addr, i64::try_from(ptr.offset.bytes()).unwrap()) } else { base_addr }; @@ -240,32 +229,33 @@ pub(crate) fn codegen_const_value<'tcx>( let ptr = pointer_for_allocation(fx, data) .offset_i64(fx, i64::try_from(start).unwrap()) .get_addr(fx); - let len = fx.bcx.ins().iconst( - fx.pointer_type, - i64::try_from(end.checked_sub(start).unwrap()).unwrap(), - ); + let len = fx + .bcx + .ins() + .iconst(fx.pointer_type, i64::try_from(end.checked_sub(start).unwrap()).unwrap()); CValue::by_val_pair(ptr, len, layout) } } } fn pointer_for_allocation<'tcx>( - fx: &mut FunctionCx<'_, 'tcx, impl Module>, + fx: &mut FunctionCx<'_, '_, 'tcx>, alloc: &'tcx Allocation, ) -> crate::pointer::Pointer { let alloc_id = fx.tcx.create_memory_alloc(alloc); fx.cx.constants_cx.todo.push(TodoItem::Alloc(alloc_id)); - let data_id = data_id_for_alloc_id(&mut fx.cx.module, alloc_id, alloc.mutability); + let data_id = data_id_for_alloc_id(fx.cx.module, alloc_id, alloc.mutability); let local_data_id = fx.cx.module.declare_data_in_func(data_id, &mut fx.bcx.func); - #[cfg(debug_assertions)] - fx.add_comment(local_data_id, format!("{:?}", alloc_id)); + if fx.clif_comments.enabled() { + fx.add_comment(local_data_id, format!("{:?}", alloc_id)); + } let global_ptr = fx.bcx.ins().global_value(fx.pointer_type, local_data_id); crate::pointer::Pointer::new(global_ptr) } fn data_id_for_alloc_id( - module: &mut impl Module, + module: &mut dyn Module, alloc_id: AllocId, mutability: rustc_hir::Mutability, ) -> DataId { @@ -281,7 +271,7 @@ fn data_id_for_alloc_id( fn data_id_for_static( tcx: TyCtxt<'_>, - module: &mut impl Module, + module: &mut dyn Module, def_id: DefId, definition: bool, ) -> DataId { @@ -304,12 +294,7 @@ fn data_id_for_static( } else { !ty.is_freeze(tcx.at(DUMMY_SP), ParamEnv::reveal_all()) }; - let align = tcx - .layout_of(ParamEnv::reveal_all().and(ty)) - .unwrap() - .align - .pref - .bytes(); + let align = tcx.layout_of(ParamEnv::reveal_all().and(ty)).unwrap().align.pref.bytes(); let attrs = tcx.codegen_fn_attrs(def_id); @@ -332,17 +317,11 @@ fn data_id_for_static( // zero. let ref_name = format!("_rust_extern_with_linkage_{}", symbol_name); - let ref_data_id = module - .declare_data(&ref_name, Linkage::Local, false, false) - .unwrap(); + let ref_data_id = module.declare_data(&ref_name, Linkage::Local, false, false).unwrap(); let mut data_ctx = DataContext::new(); data_ctx.set_align(align); let data = module.declare_data_in_data(data_id, &mut data_ctx); - data_ctx.define( - std::iter::repeat(0) - .take(pointer_ty(tcx).bytes() as usize) - .collect(), - ); + data_ctx.define(std::iter::repeat(0).take(pointer_ty(tcx).bytes() as usize).collect()); data_ctx.write_data_addr(0, data, 0); match module.define_data(ref_data_id, &data_ctx) { // Every time the static is referenced there will be another definition of this global, @@ -356,7 +335,7 @@ fn data_id_for_static( } } -fn define_all_allocs(tcx: TyCtxt<'_>, module: &mut impl Module, cx: &mut ConstantCx) { +fn define_all_allocs(tcx: TyCtxt<'_>, module: &mut dyn Module, cx: &mut ConstantCx) { while let Some(todo_item) = cx.todo.pop() { let (data_id, alloc, section_name) = match todo_item { TodoItem::Alloc(alloc_id) => { @@ -371,10 +350,7 @@ fn define_all_allocs(tcx: TyCtxt<'_>, module: &mut impl Module, cx: &mut Constan TodoItem::Static(def_id) => { //println!("static {:?}", def_id); - let section_name = tcx - .codegen_fn_attrs(def_id) - .link_section - .map(|s| s.as_str()); + let section_name = tcx.codegen_fn_attrs(def_id).link_section.map(|s| s.as_str()); let alloc = tcx.eval_static_initializer(def_id).unwrap(); @@ -396,9 +372,7 @@ fn define_all_allocs(tcx: TyCtxt<'_>, module: &mut impl Module, cx: &mut Constan data_ctx.set_segment_section("", &*section_name); } - let bytes = alloc - .inspect_with_uninit_and_ptr_outside_interpreter(0..alloc.len()) - .to_vec(); + let bytes = alloc.inspect_with_uninit_and_ptr_outside_interpreter(0..alloc.len()).to_vec(); data_ctx.define(bytes.into_boxed_slice()); for &(offset, (_tag, reloc)) in alloc.relocations().iter() { @@ -426,10 +400,7 @@ fn define_all_allocs(tcx: TyCtxt<'_>, module: &mut impl Module, cx: &mut Constan data_id_for_alloc_id(module, reloc, target_alloc.mutability) } GlobalAlloc::Static(def_id) => { - if tcx - .codegen_fn_attrs(def_id) - .flags - .contains(CodegenFnAttrFlags::THREAD_LOCAL) + if tcx.codegen_fn_attrs(def_id).flags.contains(CodegenFnAttrFlags::THREAD_LOCAL) { tcx.sess.fatal(&format!( "Allocation {:?} contains reference to TLS value {:?}", @@ -457,14 +428,16 @@ fn define_all_allocs(tcx: TyCtxt<'_>, module: &mut impl Module, cx: &mut Constan } pub(crate) fn mir_operand_get_const_val<'tcx>( - fx: &FunctionCx<'_, 'tcx, impl Module>, + fx: &FunctionCx<'_, '_, 'tcx>, operand: &Operand<'tcx>, -) -> Option<&'tcx Const<'tcx>> { +) -> Option> { match operand { Operand::Copy(_) | Operand::Move(_) => None, - Operand::Constant(const_) => Some( - fx.monomorphize(const_.literal) - .eval(fx.tcx, ParamEnv::reveal_all()), - ), + Operand::Constant(const_) => match const_.literal { + ConstantKind::Ty(const_) => { + fx.monomorphize(const_).eval(fx.tcx, ParamEnv::reveal_all()).val.try_to_value() + } + ConstantKind::Val(val, _) => Some(val), + }, } } diff --git a/compiler/rustc_codegen_cranelift/src/debuginfo/emit.rs b/compiler/rustc_codegen_cranelift/src/debuginfo/emit.rs index 6160f9b78d8b3..6018eefcd42fb 100644 --- a/compiler/rustc_codegen_cranelift/src/debuginfo/emit.rs +++ b/compiler/rustc_codegen_cranelift/src/debuginfo/emit.rs @@ -14,10 +14,7 @@ impl DebugContext<'_> { let unit_range_list_id = self.dwarf.unit.ranges.add(self.unit_range_list.clone()); let root = self.dwarf.unit.root(); let root = self.dwarf.unit.get_mut(root); - root.set( - gimli::DW_AT_ranges, - AttributeValue::RangeListRef(unit_range_list_id), - ); + root.set(gimli::DW_AT_ranges, AttributeValue::RangeListRef(unit_range_list_id)); let mut sections = Sections::new(WriterRelocate::new(self.endian)); self.dwarf.write(&mut sections).unwrap(); @@ -66,10 +63,7 @@ pub(super) struct WriterRelocate { impl WriterRelocate { pub(super) fn new(endian: RunTimeEndian) -> Self { - WriterRelocate { - relocs: Vec::new(), - writer: EndianVec::new(endian), - } + WriterRelocate { relocs: Vec::new(), writer: EndianVec::new(endian) } } /// Perform the collected relocations to be usable for JIT usage. @@ -85,9 +79,7 @@ impl WriterRelocate { cranelift_module::FuncId::from_u32(sym.try_into().unwrap()), ); let val = (addr as u64 as i64 + reloc.addend) as u64; - self.writer - .write_udata_at(reloc.offset as usize, val, reloc.size) - .unwrap(); + self.writer.write_udata_at(reloc.offset as usize, val, reloc.size).unwrap(); } } } diff --git a/compiler/rustc_codegen_cranelift/src/debuginfo/line_info.rs b/compiler/rustc_codegen_cranelift/src/debuginfo/line_info.rs index d226755d85de0..8578ab33ced68 100644 --- a/compiler/rustc_codegen_cranelift/src/debuginfo/line_info.rs +++ b/compiler/rustc_codegen_cranelift/src/debuginfo/line_info.rs @@ -39,11 +39,11 @@ fn osstr_as_utf8_bytes(path: &OsStr) -> &[u8] { #[cfg(unix)] { use std::os::unix::ffi::OsStrExt; - return path.as_bytes(); + path.as_bytes() } #[cfg(not(unix))] { - return path.to_str().unwrap().as_bytes(); + path.to_str().unwrap().as_bytes() } } @@ -53,11 +53,7 @@ pub(crate) fn make_file_info(hash: SourceFileHash) -> Option { if hash.kind == SourceFileHashAlgorithm::Md5 { let mut buf = [0u8; MD5_LEN]; buf.copy_from_slice(hash.hash_bytes()); - Some(FileInfo { - timestamp: 0, - size: 0, - md5: buf, - }) + Some(FileInfo { timestamp: 0, size: 0, md5: buf }) } else { None } @@ -112,24 +108,14 @@ impl<'tcx> DebugContext<'tcx> { let entry = self.dwarf.unit.get_mut(entry_id); - entry.set( - gimli::DW_AT_decl_file, - AttributeValue::FileIndex(Some(file_id)), - ); - entry.set( - gimli::DW_AT_decl_line, - AttributeValue::Udata(loc.line as u64), - ); + entry.set(gimli::DW_AT_decl_file, AttributeValue::FileIndex(Some(file_id))); + entry.set(gimli::DW_AT_decl_line, AttributeValue::Udata(loc.line as u64)); // FIXME: probably omit this - entry.set( - gimli::DW_AT_decl_column, - AttributeValue::Udata(loc.col.to_usize() as u64), - ); + entry.set(gimli::DW_AT_decl_column, AttributeValue::Udata(loc.col.to_usize() as u64)); } pub(super) fn create_debug_lines( &mut self, - isa: &dyn cranelift_codegen::isa::TargetIsa, symbol: usize, entry_id: UnitEntryId, context: &Context, @@ -138,7 +124,6 @@ impl<'tcx> DebugContext<'tcx> { ) -> CodeOffset { let tcx = self.tcx; let line_program = &mut self.dwarf.unit.line_program; - let func = &context.func; let line_strings = &mut self.dwarf.line_strings; let mut last_span = None; @@ -202,43 +187,22 @@ impl<'tcx> DebugContext<'tcx> { let mut func_end = 0; - if let Some(ref mcr) = &context.mach_compile_result { - for &MachSrcLoc { start, end, loc } in mcr.buffer.get_srclocs_sorted() { - line_program.row().address_offset = u64::from(start); - if !loc.is_default() { - let source_info = *source_info_set.get_index(loc.bits() as usize).unwrap(); - create_row_for_span(line_program, source_info.span); - } else { - create_row_for_span(line_program, function_span); - } - func_end = end; - } - - line_program.end_sequence(u64::from(func_end)); - - func_end = mcr.buffer.total_size(); - } else { - let encinfo = isa.encoding_info(); - let mut blocks = func.layout.blocks().collect::>(); - blocks.sort_by_key(|block| func.offsets[*block]); // Ensure inst offsets always increase - - for block in blocks { - for (offset, inst, size) in func.inst_offsets(block, &encinfo) { - let srcloc = func.srclocs[inst]; - line_program.row().address_offset = u64::from(offset); - if !srcloc.is_default() { - let source_info = - *source_info_set.get_index(srcloc.bits() as usize).unwrap(); - create_row_for_span(line_program, source_info.span); - } else { - create_row_for_span(line_program, function_span); - } - func_end = offset + size; - } + let mcr = context.mach_compile_result.as_ref().unwrap(); + for &MachSrcLoc { start, end, loc } in mcr.buffer.get_srclocs_sorted() { + line_program.row().address_offset = u64::from(start); + if !loc.is_default() { + let source_info = *source_info_set.get_index(loc.bits() as usize).unwrap(); + create_row_for_span(line_program, source_info.span); + } else { + create_row_for_span(line_program, function_span); } - line_program.end_sequence(u64::from(func_end)); + func_end = end; } + line_program.end_sequence(u64::from(func_end)); + + let func_end = mcr.buffer.total_size(); + assert_ne!(func_end, 0); let entry = self.dwarf.unit.get_mut(entry_id); @@ -246,10 +210,7 @@ impl<'tcx> DebugContext<'tcx> { gimli::DW_AT_low_pc, AttributeValue::Address(Address::Symbol { symbol, addend: 0 }), ); - entry.set( - gimli::DW_AT_high_pc, - AttributeValue::Udata(u64::from(func_end)), - ); + entry.set(gimli::DW_AT_high_pc, AttributeValue::Udata(u64::from(func_end))); self.emit_location(entry_id, function_span); diff --git a/compiler/rustc_codegen_cranelift/src/debuginfo/mod.rs b/compiler/rustc_codegen_cranelift/src/debuginfo/mod.rs index a6f4ded41b64b..dc8bc8d9cb741 100644 --- a/compiler/rustc_codegen_cranelift/src/debuginfo/mod.rs +++ b/compiler/rustc_codegen_cranelift/src/debuginfo/mod.rs @@ -9,7 +9,7 @@ use crate::prelude::*; use rustc_index::vec::IndexVec; use cranelift_codegen::entity::EntityRef; -use cranelift_codegen::ir::{StackSlots, ValueLabel, ValueLoc}; +use cranelift_codegen::ir::{LabelValueLoc, StackSlots, ValueLabel, ValueLoc}; use cranelift_codegen::isa::TargetIsa; use cranelift_codegen::ValueLocRange; @@ -39,7 +39,6 @@ pub(crate) struct DebugContext<'tcx> { dwarf: DwarfUnit, unit_range_list: RangeList, - clif_types: FxHashMap, types: FxHashMap, UnitEntryId>, } @@ -91,20 +90,11 @@ impl<'tcx> DebugContext<'tcx> { let root = dwarf.unit.root(); let root = dwarf.unit.get_mut(root); - root.set( - gimli::DW_AT_producer, - AttributeValue::StringRef(dwarf.strings.add(producer)), - ); - root.set( - gimli::DW_AT_language, - AttributeValue::Language(gimli::DW_LANG_Rust), - ); + root.set(gimli::DW_AT_producer, AttributeValue::StringRef(dwarf.strings.add(producer))); + root.set(gimli::DW_AT_language, AttributeValue::Language(gimli::DW_LANG_Rust)); root.set(gimli::DW_AT_name, AttributeValue::StringRef(name)); root.set(gimli::DW_AT_comp_dir, AttributeValue::StringRef(comp_dir)); - root.set( - gimli::DW_AT_low_pc, - AttributeValue::Address(Address::Constant(0)), - ); + root.set(gimli::DW_AT_low_pc, AttributeValue::Address(Address::Constant(0))); } DebugContext { @@ -115,48 +105,10 @@ impl<'tcx> DebugContext<'tcx> { dwarf, unit_range_list: RangeList(Vec::new()), - clif_types: FxHashMap::default(), types: FxHashMap::default(), } } - fn dwarf_ty_for_clif_ty(&mut self, ty: Type) -> UnitEntryId { - if let Some(type_id) = self.clif_types.get(&ty) { - return *type_id; - } - - let new_entry = |dwarf: &mut DwarfUnit, tag| dwarf.unit.add(dwarf.unit.root(), tag); - - let primitive = |dwarf: &mut DwarfUnit, ate| { - let type_id = new_entry(dwarf, gimli::DW_TAG_base_type); - let type_entry = dwarf.unit.get_mut(type_id); - type_entry.set(gimli::DW_AT_encoding, AttributeValue::Encoding(ate)); - type_id - }; - - let type_id = if ty.is_bool() { - primitive(&mut self.dwarf, gimli::DW_ATE_boolean) - } else if ty.is_int() { - primitive(&mut self.dwarf, gimli::DW_ATE_address) - } else if ty.is_float() { - primitive(&mut self.dwarf, gimli::DW_ATE_float) - } else { - new_entry(&mut self.dwarf, gimli::DW_TAG_structure_type) - }; - - let type_entry = self.dwarf.unit.get_mut(type_id); - type_entry.set( - gimli::DW_AT_name, - AttributeValue::String(format!("{}", ty).replace('i', "u").into_bytes()), - ); - type_entry.set( - gimli::DW_AT_byte_size, - AttributeValue::Udata(u64::from(ty.bytes())), - ); - - type_id - } - fn dwarf_ty(&mut self, ty: Ty<'tcx>) -> UnitEntryId { if let Some(type_id) = self.types.get(ty) { return *type_id; @@ -181,10 +133,7 @@ impl<'tcx> DebugContext<'tcx> { ty::Int(_) => primitive(&mut self.dwarf, gimli::DW_ATE_signed), ty::Float(_) => primitive(&mut self.dwarf, gimli::DW_ATE_float), ty::Ref(_, pointee_ty, _mutbl) - | ty::RawPtr(ty::TypeAndMut { - ty: pointee_ty, - mutbl: _mutbl, - }) => { + | ty::RawPtr(ty::TypeAndMut { ty: pointee_ty, mutbl: _mutbl }) => { let type_id = new_entry(&mut self.dwarf, gimli::DW_TAG_pointer_type); // Ensure that type is inserted before recursing to avoid duplicates @@ -211,10 +160,7 @@ impl<'tcx> DebugContext<'tcx> { let field_offset = layout.fields.offset(field_idx); let field_layout = layout .field( - &layout::LayoutCx { - tcx: self.tcx, - param_env: ParamEnv::reveal_all(), - }, + &layout::LayoutCx { tcx: self.tcx, param_env: ParamEnv::reveal_all() }, field_idx, ) .unwrap(); @@ -243,10 +189,7 @@ impl<'tcx> DebugContext<'tcx> { let type_entry = self.dwarf.unit.get_mut(type_id); type_entry.set(gimli::DW_AT_name, AttributeValue::String(name.into_bytes())); - type_entry.set( - gimli::DW_AT_byte_size, - AttributeValue::Udata(layout.size.bytes()), - ); + type_entry.set(gimli::DW_AT_byte_size, AttributeValue::Udata(layout.size.bytes())); self.types.insert(ty, type_id); @@ -286,23 +229,15 @@ impl<'tcx> DebugContext<'tcx> { let name_id = self.dwarf.strings.add(name); // Gdb requires DW_AT_name. Otherwise the DW_TAG_subprogram is skipped. entry.set(gimli::DW_AT_name, AttributeValue::StringRef(name_id)); - entry.set( - gimli::DW_AT_linkage_name, - AttributeValue::StringRef(name_id), - ); + entry.set(gimli::DW_AT_linkage_name, AttributeValue::StringRef(name_id)); - let end = - self.create_debug_lines(isa, symbol, entry_id, context, mir.span, source_info_set); + let end = self.create_debug_lines(symbol, entry_id, context, mir.span, source_info_set); self.unit_range_list.0.push(Range::StartLength { begin: Address::Symbol { symbol, addend: 0 }, length: u64::from(end), }); - if isa.get_mach_backend().is_some() { - return; // Not yet implemented for the AArch64 backend. - } - let func_entry = self.dwarf.unit.get_mut(entry_id); // Gdb requires both DW_AT_low_pc and DW_AT_high_pc. Otherwise the DW_TAG_subprogram is skipped. func_entry.set( @@ -312,51 +247,6 @@ impl<'tcx> DebugContext<'tcx> { // Using Udata for DW_AT_high_pc requires at least DWARF4 func_entry.set(gimli::DW_AT_high_pc, AttributeValue::Udata(u64::from(end))); - // FIXME Remove once actual debuginfo for locals works. - for (i, (param, &val)) in context - .func - .signature - .params - .iter() - .zip( - context - .func - .dfg - .block_params(context.func.layout.entry_block().unwrap()), - ) - .enumerate() - { - use cranelift_codegen::ir::ArgumentPurpose; - let base_name = match param.purpose { - ArgumentPurpose::Normal => "arg", - ArgumentPurpose::StructArgument(_) => "struct_arg", - ArgumentPurpose::StructReturn => "sret", - ArgumentPurpose::Link - | ArgumentPurpose::FramePointer - | ArgumentPurpose::CalleeSaved => continue, - ArgumentPurpose::VMContext - | ArgumentPurpose::SignatureId - | ArgumentPurpose::CallerTLS - | ArgumentPurpose::CalleeTLS - | ArgumentPurpose::StackLimit => unreachable!(), - }; - let name = format!("{}{}", base_name, i); - - let dw_ty = self.dwarf_ty_for_clif_ty(param.value_type); - let loc = - translate_loc(isa, context.func.locations[val], &context.func.stack_slots).unwrap(); - - let arg_id = self - .dwarf - .unit - .add(entry_id, gimli::DW_TAG_formal_parameter); - let var_entry = self.dwarf.unit.get_mut(arg_id); - - var_entry.set(gimli::DW_AT_name, AttributeValue::String(name.into_bytes())); - var_entry.set(gimli::DW_AT_type, AttributeValue::UnitRef(dw_ty)); - var_entry.set(gimli::DW_AT_location, AttributeValue::Exprloc(loc)); - } - // FIXME make it more reliable and implement scopes before re-enabling this. if false { let value_labels_ranges = context.build_value_labels_ranges(isa).unwrap(); @@ -376,10 +266,7 @@ impl<'tcx> DebugContext<'tcx> { context, &local_map, &value_labels_ranges, - Place { - local, - projection: ty::List::empty(), - }, + Place { local, projection: ty::List::empty() }, ); let var_entry = self.dwarf.unit.get_mut(var_id); @@ -417,10 +304,7 @@ fn place_location<'tcx>( symbol, addend: i64::from(value_loc_range.start), }, - end: Address::Symbol { - symbol, - addend: i64::from(value_loc_range.end), - }, + end: Address::Symbol { symbol, addend: i64::from(value_loc_range.end) }, data: translate_loc( isa, value_loc_range.loc, @@ -463,17 +347,17 @@ fn place_location<'tcx>( // Adapted from https://github.com/CraneStation/wasmtime/blob/5a1845b4caf7a5dba8eda1fef05213a532ed4259/crates/debug/src/transform/expression.rs#L59-L137 fn translate_loc( isa: &dyn TargetIsa, - loc: ValueLoc, + loc: LabelValueLoc, stack_slots: &StackSlots, ) -> Option { match loc { - ValueLoc::Reg(reg) => { + LabelValueLoc::ValueLoc(ValueLoc::Reg(reg)) => { let machine_reg = isa.map_dwarf_register(reg).unwrap(); let mut expr = Expression::new(); expr.op_reg(gimli::Register(machine_reg)); Some(expr) } - ValueLoc::Stack(ss) => { + LabelValueLoc::ValueLoc(ValueLoc::Stack(ss)) => { if let Some(ss_offset) = stack_slots[ss].offset { let mut expr = Expression::new(); expr.op_breg(X86_64::RBP, i64::from(ss_offset) + 16); @@ -482,6 +366,17 @@ fn translate_loc( None } } - _ => None, + LabelValueLoc::ValueLoc(ValueLoc::Unassigned) => unreachable!(), + LabelValueLoc::Reg(reg) => { + let machine_reg = isa.map_regalloc_reg_to_dwarf(reg).unwrap(); + let mut expr = Expression::new(); + expr.op_reg(gimli::Register(machine_reg)); + Some(expr) + } + LabelValueLoc::SPOffset(offset) => { + let mut expr = Expression::new(); + expr.op_breg(X86_64::RSP, offset); + Some(expr) + } } } diff --git a/compiler/rustc_codegen_cranelift/src/debuginfo/unwind.rs b/compiler/rustc_codegen_cranelift/src/debuginfo/unwind.rs index 49de927cdba05..357c9fe6ed83a 100644 --- a/compiler/rustc_codegen_cranelift/src/debuginfo/unwind.rs +++ b/compiler/rustc_codegen_cranelift/src/debuginfo/unwind.rs @@ -28,11 +28,7 @@ impl<'tcx> UnwindContext<'tcx> { None }; - UnwindContext { - tcx, - frame_table, - cie_id, - } + UnwindContext { tcx, frame_table, cie_id } } pub(crate) fn add_function(&mut self, func_id: FuncId, context: &Context, isa: &dyn TargetIsa) { @@ -46,10 +42,8 @@ impl<'tcx> UnwindContext<'tcx> { UnwindInfo::SystemV(unwind_info) => { self.frame_table.add_fde( self.cie_id.unwrap(), - unwind_info.to_fde(Address::Symbol { - symbol: func_id.as_u32() as usize, - addend: 0, - }), + unwind_info + .to_fde(Address::Symbol { symbol: func_id.as_u32() as usize, addend: 0 }), ); } UnwindInfo::WindowsX64(_) => { @@ -60,9 +54,8 @@ impl<'tcx> UnwindContext<'tcx> { } pub(crate) fn emit(self, product: &mut P) { - let mut eh_frame = EhFrame::from(super::emit::WriterRelocate::new(super::target_endian( - self.tcx, - ))); + let mut eh_frame = + EhFrame::from(super::emit::WriterRelocate::new(super::target_endian(self.tcx))); self.frame_table.write_eh_frame(&mut eh_frame).unwrap(); if !eh_frame.0.writer.slice().is_empty() { @@ -82,9 +75,8 @@ impl<'tcx> UnwindContext<'tcx> { self, jit_module: &cranelift_jit::JITModule, ) -> Option { - let mut eh_frame = EhFrame::from(super::emit::WriterRelocate::new(super::target_endian( - self.tcx, - ))); + let mut eh_frame = + EhFrame::from(super::emit::WriterRelocate::new(super::target_endian(self.tcx))); self.frame_table.write_eh_frame(&mut eh_frame).unwrap(); if eh_frame.0.writer.slice().is_empty() { @@ -130,10 +122,7 @@ impl<'tcx> UnwindContext<'tcx> { registrations.push(ptr as usize); } - Some(UnwindRegistry { - _frame_table: eh_frame, - registrations, - }) + Some(UnwindRegistry { _frame_table: eh_frame, registrations }) } } diff --git a/compiler/rustc_codegen_cranelift/src/discriminant.rs b/compiler/rustc_codegen_cranelift/src/discriminant.rs index ad635016a9136..3326f87f00075 100644 --- a/compiler/rustc_codegen_cranelift/src/discriminant.rs +++ b/compiler/rustc_codegen_cranelift/src/discriminant.rs @@ -7,7 +7,7 @@ use rustc_target::abi::{Int, TagEncoding, Variants}; use crate::prelude::*; pub(crate) fn codegen_set_discriminant<'tcx>( - fx: &mut FunctionCx<'_, 'tcx, impl Module>, + fx: &mut FunctionCx<'_, '_, 'tcx>, place: CPlace<'tcx>, variant_index: VariantIdx, ) { @@ -26,11 +26,7 @@ pub(crate) fn codegen_set_discriminant<'tcx>( variants: _, } => { let ptr = place.place_field(fx, mir::Field::new(tag_field)); - let to = layout - .ty - .discriminant_for_variant(fx.tcx, variant_index) - .unwrap() - .val; + let to = layout.ty.discriminant_for_variant(fx.tcx, variant_index).unwrap().val; let to = if ptr.layout().abi.is_signed() { ty::ScalarInt::try_from_int( ptr.layout().size.sign_extend(to) as i128, @@ -46,12 +42,7 @@ pub(crate) fn codegen_set_discriminant<'tcx>( Variants::Multiple { tag: _, tag_field, - tag_encoding: - TagEncoding::Niche { - dataful_variant, - ref niche_variants, - niche_start, - }, + tag_encoding: TagEncoding::Niche { dataful_variant, ref niche_variants, niche_start }, variants: _, } => { if variant_index != dataful_variant { @@ -70,7 +61,7 @@ pub(crate) fn codegen_set_discriminant<'tcx>( } pub(crate) fn codegen_get_discriminant<'tcx>( - fx: &mut FunctionCx<'_, 'tcx, impl Module>, + fx: &mut FunctionCx<'_, '_, 'tcx>, value: CValue<'tcx>, dest_layout: TyAndLayout<'tcx>, ) -> CValue<'tcx> { @@ -101,12 +92,9 @@ pub(crate) fn codegen_get_discriminant<'tcx>( }; return CValue::const_val(fx, dest_layout, discr_val); } - Variants::Multiple { - tag, - tag_field, - tag_encoding, - variants: _, - } => (tag, *tag_field, tag_encoding), + Variants::Multiple { tag, tag_field, tag_encoding, variants: _ } => { + (tag, *tag_field, tag_encoding) + } }; let cast_to = fx.clif_type(dest_layout.ty).unwrap(); @@ -125,11 +113,7 @@ pub(crate) fn codegen_get_discriminant<'tcx>( let val = clif_intcast(fx, tag, cast_to, signed); CValue::by_val(val, dest_layout) } - TagEncoding::Niche { - dataful_variant, - ref niche_variants, - niche_start, - } => { + TagEncoding::Niche { dataful_variant, ref niche_variants, niche_start } => { // Rebase from niche values to discriminants, and check // whether the result is in range for the niche variants. @@ -146,9 +130,7 @@ pub(crate) fn codegen_get_discriminant<'tcx>( tag } else { // FIXME handle niche_start > i64::MAX - fx.bcx - .ins() - .iadd_imm(tag, -i64::try_from(niche_start).unwrap()) + fx.bcx.ins().iadd_imm(tag, -i64::try_from(niche_start).unwrap()) }; let relative_max = niche_variants.end().as_u32() - niche_variants.start().as_u32(); let is_niche = { @@ -176,15 +158,10 @@ pub(crate) fn codegen_get_discriminant<'tcx>( } else { clif_intcast(fx, relative_discr, cast_to, false) }; - fx.bcx - .ins() - .iadd_imm(relative_discr, i64::from(niche_variants.start().as_u32())) + fx.bcx.ins().iadd_imm(relative_discr, i64::from(niche_variants.start().as_u32())) }; - let dataful_variant = fx - .bcx - .ins() - .iconst(cast_to, i64::from(dataful_variant.as_u32())); + let dataful_variant = fx.bcx.ins().iconst(cast_to, i64::from(dataful_variant.as_u32())); let discr = fx.bcx.ins().select(is_niche, niche_discr, dataful_variant); CValue::by_val(discr, dest_layout) } diff --git a/compiler/rustc_codegen_cranelift/src/driver/aot.rs b/compiler/rustc_codegen_cranelift/src/driver/aot.rs index 39781e2482a6a..ed3bdedddced5 100644 --- a/compiler/rustc_codegen_cranelift/src/driver/aot.rs +++ b/compiler/rustc_codegen_cranelift/src/driver/aot.rs @@ -12,11 +12,9 @@ use rustc_middle::mir::mono::{CodegenUnit, MonoItem}; use rustc_session::cgu_reuse_tracker::CguReuse; use rustc_session::config::{DebugInfo, OutputType}; -use cranelift_object::{ObjectModule, ObjectProduct}; +use cranelift_object::ObjectModule; -use crate::prelude::*; - -use crate::backend::AddConstructor; +use crate::{prelude::*, BackendConfig}; fn new_module(tcx: TyCtxt<'_>, name: String) -> ObjectModule { let module = crate::backend::make_module(tcx.sess, name); @@ -39,7 +37,6 @@ fn emit_module( module: ObjectModule, debug: Option>, unwind_context: UnwindContext<'_>, - map_product: impl FnOnce(ObjectProduct) -> ObjectProduct, ) -> ModuleCodegenResult { let mut product = module.finish(); @@ -49,15 +46,10 @@ fn emit_module( unwind_context.emit(&mut product); - let product = map_product(product); - - let tmp_file = tcx - .output_filenames(LOCAL_CRATE) - .temp_path(OutputType::Object, Some(&name)); + let tmp_file = tcx.output_filenames(LOCAL_CRATE).temp_path(OutputType::Object, Some(&name)); let obj = product.object.write().unwrap(); if let Err(err) = std::fs::write(&tmp_file, obj) { - tcx.sess - .fatal(&format!("error writing object file: {}", err)); + tcx.sess.fatal(&format!("error writing object file: {}", err)); } let work_product = if std::env::var("CG_CLIF_INCR_CACHE_DISABLED").is_ok() { @@ -71,13 +63,7 @@ fn emit_module( }; ModuleCodegenResult( - CompiledModule { - name, - kind, - object: Some(tmp_file), - dwarf_object: None, - bytecode: None, - }, + CompiledModule { name, kind, object: Some(tmp_file), dwarf_object: None, bytecode: None }, work_product, ) } @@ -117,49 +103,26 @@ fn reuse_workproduct_for_cgu( } } -fn module_codegen(tcx: TyCtxt<'_>, cgu_name: rustc_span::Symbol) -> ModuleCodegenResult { +fn module_codegen( + tcx: TyCtxt<'_>, + (backend_config, cgu_name): (BackendConfig, rustc_span::Symbol), +) -> ModuleCodegenResult { let cgu = tcx.codegen_unit(cgu_name); let mono_items = cgu.items_in_deterministic_order(tcx); let mut module = new_module(tcx, cgu_name.as_str().to_string()); - // Initialize the global atomic mutex using a constructor for proc-macros. - // FIXME implement atomic instructions in Cranelift. - let mut init_atomics_mutex_from_constructor = None; - if tcx - .sess - .crate_types() - .contains(&rustc_session::config::CrateType::ProcMacro) - { - if mono_items.iter().any(|(mono_item, _)| match mono_item { - rustc_middle::mir::mono::MonoItem::Static(def_id) => tcx - .symbol_name(Instance::mono(tcx, *def_id)) - .name - .contains("__rustc_proc_macro_decls_"), - _ => false, - }) { - init_atomics_mutex_from_constructor = - Some(crate::atomic_shim::init_global_lock_constructor( - &mut module, - &format!("{}_init_atomics_mutex", cgu_name.as_str()), - )); - } - } - let mut cx = crate::CodegenCx::new( tcx, - module, + backend_config, + &mut module, tcx.sess.opts.debuginfo != DebugInfo::None, - true, ); super::predefine_mono_items(&mut cx, &mono_items); - for (mono_item, (linkage, visibility)) in mono_items { - let linkage = crate::linkage::get_clif_linkage(mono_item, linkage, visibility); + for (mono_item, _) in mono_items { match mono_item { MonoItem::Fn(inst) => { - cx.tcx.sess.time("codegen fn", || { - crate::base::codegen_fn(&mut cx, inst, linkage) - }); + cx.tcx.sess.time("codegen fn", || crate::base::codegen_fn(&mut cx, inst)); } MonoItem::Static(def_id) => { crate::constant::codegen_static(&mut cx.constants_cx, def_id) @@ -175,9 +138,9 @@ fn module_codegen(tcx: TyCtxt<'_>, cgu_name: rustc_span::Symbol) -> ModuleCodege } } } - let (mut module, global_asm, debug, mut unwind_context) = + let (global_asm, debug, mut unwind_context) = tcx.sess.time("finalize CodegenCx", || cx.finalize()); - crate::main_shim::maybe_create_entry_wrapper(tcx, &mut module, &mut unwind_context, false); + crate::main_shim::maybe_create_entry_wrapper(tcx, &mut module, &mut unwind_context); let codegen_result = emit_module( tcx, @@ -186,13 +149,6 @@ fn module_codegen(tcx: TyCtxt<'_>, cgu_name: rustc_span::Symbol) -> ModuleCodege module, debug, unwind_context, - |mut product| { - if let Some(func_id) = init_atomics_mutex_from_constructor { - product.add_constructor(func_id); - } - - product - }, ); codegen_global_asm(tcx, &cgu.name().as_str(), &global_asm); @@ -202,9 +158,25 @@ fn module_codegen(tcx: TyCtxt<'_>, cgu_name: rustc_span::Symbol) -> ModuleCodege pub(super) fn run_aot( tcx: TyCtxt<'_>, + backend_config: BackendConfig, metadata: EncodedMetadata, need_metadata_module: bool, ) -> Box<(CodegenResults, FxHashMap)> { + use rustc_span::symbol::sym; + + let crate_attrs = tcx.hir().attrs(rustc_hir::CRATE_HIR_ID); + let subsystem = tcx.sess.first_attr_value_str_by_name(crate_attrs, sym::windows_subsystem); + let windows_subsystem = subsystem.map(|subsystem| { + if subsystem != sym::windows && subsystem != sym::console { + tcx.sess.fatal(&format!( + "invalid windows subsystem `{}`, only \ + `windows` and `console` are allowed", + subsystem + )); + } + subsystem.to_string() + }); + let mut work_products = FxHashMap::default(); let cgus = if tcx.sess.opts.output_types.should_codegen() { @@ -225,9 +197,7 @@ pub(super) fn run_aot( cgus.iter() .map(|cgu| { let cgu_reuse = determine_cgu_reuse(tcx, cgu); - tcx.sess - .cgu_reuse_tracker - .set_actual_reuse(&cgu.name().as_str(), cgu_reuse); + tcx.sess.cgu_reuse_tracker.set_actual_reuse(&cgu.name().as_str(), cgu_reuse); match cgu_reuse { _ if std::env::var("CG_CLIF_INCR_CACHE_DISABLED").is_ok() => {} @@ -242,7 +212,7 @@ pub(super) fn run_aot( let (ModuleCodegenResult(module, work_product), _) = tcx.dep_graph.with_task( dep_node, tcx, - cgu.name(), + (backend_config, cgu.name()), module_codegen, rustc_middle::dep_graph::hash_result, ); @@ -271,7 +241,6 @@ pub(super) fn run_aot( allocator_module, None, allocator_unwind_context, - |product| product, ); if let Some((id, product)) = work_product { work_products.insert(id, product); @@ -301,8 +270,7 @@ pub(super) fn run_aot( }); if let Err(err) = std::fs::write(&tmp_file, obj) { - tcx.sess - .fatal(&format!("error writing metadata object file: {}", err)); + tcx.sess.fatal(&format!("error writing metadata object file: {}", err)); } (metadata_cgu_name, tmp_file) @@ -326,7 +294,7 @@ pub(super) fn run_aot( allocator_module, metadata_module, metadata, - windows_subsystem: None, // Windows is not yet supported + windows_subsystem, linker_info: LinkerInfo::new(tcx), crate_info: CrateInfo::new(tcx), }, @@ -356,8 +324,7 @@ fn codegen_global_asm(tcx: TyCtxt<'_>, cgu_name: &str, global_asm: &str) { "asm! and global_asm! support is disabled while compiling rustc_codegen_cranelift", ); } else { - tcx.sess - .fatal("asm! and global_asm! are not yet supported on macOS and Windows"); + tcx.sess.fatal("asm! and global_asm! are not yet supported on macOS and Windows"); } } @@ -367,19 +334,12 @@ fn codegen_global_asm(tcx: TyCtxt<'_>, cgu_name: &str, global_asm: &str) { // Remove all LLVM style comments let global_asm = global_asm .lines() - .map(|line| { - if let Some(index) = line.find("//") { - &line[0..index] - } else { - line - } - }) + .map(|line| if let Some(index) = line.find("//") { &line[0..index] } else { line }) .collect::>() .join("\n"); - let output_object_file = tcx - .output_filenames(LOCAL_CRATE) - .temp_path(OutputType::Object, Some(cgu_name)); + let output_object_file = + tcx.output_filenames(LOCAL_CRATE).temp_path(OutputType::Object, Some(cgu_name)); // Assemble `global_asm` let global_asm_object_file = add_file_stem_postfix(output_object_file.clone(), ".asm"); @@ -389,16 +349,10 @@ fn codegen_global_asm(tcx: TyCtxt<'_>, cgu_name: &str, global_asm: &str) { .stdin(Stdio::piped()) .spawn() .expect("Failed to spawn `as`."); - child - .stdin - .take() - .unwrap() - .write_all(global_asm.as_bytes()) - .unwrap(); + child.stdin.take().unwrap().write_all(global_asm.as_bytes()).unwrap(); let status = child.wait().expect("Failed to wait for `as`."); if !status.success() { - tcx.sess - .fatal(&format!("Failed to assemble `{}`", global_asm)); + tcx.sess.fatal(&format!("Failed to assemble `{}`", global_asm)); } // Link the global asm and main object file together @@ -442,11 +396,7 @@ fn determine_cgu_reuse<'tcx>(tcx: TyCtxt<'tcx>, cgu: &CodegenUnit<'tcx>) -> CguR } let work_product_id = &cgu.work_product_id(); - if tcx - .dep_graph - .previous_work_product(work_product_id) - .is_none() - { + if tcx.dep_graph.previous_work_product(work_product_id).is_none() { // We don't have anything cached for this CGU. This can happen // if the CGU did not exist in the previous session. return CguReuse::No; diff --git a/compiler/rustc_codegen_cranelift/src/driver/jit.rs b/compiler/rustc_codegen_cranelift/src/driver/jit.rs index f784d8d27cc74..dbe1ff083f0db 100644 --- a/compiler/rustc_codegen_cranelift/src/driver/jit.rs +++ b/compiler/rustc_codegen_cranelift/src/driver/jit.rs @@ -5,66 +5,36 @@ use std::cell::RefCell; use std::ffi::CString; use std::os::raw::{c_char, c_int}; +use cranelift_codegen::binemit::{NullStackMapSink, NullTrapSink}; use rustc_codegen_ssa::CrateInfo; use rustc_middle::mir::mono::MonoItem; +use rustc_session::config::EntryFnType; use cranelift_jit::{JITBuilder, JITModule}; -use crate::prelude::*; +use crate::{prelude::*, BackendConfig}; use crate::{CodegenCx, CodegenMode}; thread_local! { + pub static BACKEND_CONFIG: RefCell> = RefCell::new(None); pub static CURRENT_MODULE: RefCell> = RefCell::new(None); } -pub(super) fn run_jit(tcx: TyCtxt<'_>, codegen_mode: CodegenMode) -> ! { +pub(super) fn run_jit(tcx: TyCtxt<'_>, backend_config: BackendConfig) -> ! { if !tcx.sess.opts.output_types.should_codegen() { tcx.sess.fatal("JIT mode doesn't work with `cargo check`."); } - #[cfg(unix)] - unsafe { - // When not using our custom driver rustc will open us without the RTLD_GLOBAL flag, so - // __cg_clif_global_atomic_mutex will not be exported. We fix this by opening ourself again - // as global. - // FIXME remove once atomic_shim is gone - - let mut dl_info: libc::Dl_info = std::mem::zeroed(); - assert_ne!( - libc::dladdr(run_jit as *const libc::c_void, &mut dl_info), - 0 - ); - assert_ne!( - libc::dlopen(dl_info.dli_fname, libc::RTLD_NOW | libc::RTLD_GLOBAL), - std::ptr::null_mut(), - ); - } - let imported_symbols = load_imported_symbols_for_jit(tcx); - let mut jit_builder = JITBuilder::with_isa( - crate::build_isa(tcx.sess), - cranelift_module::default_libcall_names(), - ); - jit_builder.hotswap(matches!(codegen_mode, CodegenMode::JitLazy)); + let mut jit_builder = + JITBuilder::with_isa(crate::build_isa(tcx.sess), cranelift_module::default_libcall_names()); + jit_builder.hotswap(matches!(backend_config.codegen_mode, CodegenMode::JitLazy)); + crate::compiler_builtins::register_functions_for_jit(&mut jit_builder); jit_builder.symbols(imported_symbols); let mut jit_module = JITModule::new(jit_builder); assert_eq!(pointer_ty(tcx), jit_module.target_config().pointer_type()); - let sig = Signature { - params: vec![ - AbiParam::new(jit_module.target_config().pointer_type()), - AbiParam::new(jit_module.target_config().pointer_type()), - ], - returns: vec![AbiParam::new( - jit_module.target_config().pointer_type(), /*isize*/ - )], - call_conv: CallConv::triple_default(&crate::target_triple(tcx.sess)), - }; - let main_func_id = jit_module - .declare_function("main", Linkage::Import, &sig) - .unwrap(); - let (_, cgus) = tcx.collect_and_partition_mono_items(LOCAL_CRATE); let mono_items = cgus .iter() @@ -74,19 +44,16 @@ pub(super) fn run_jit(tcx: TyCtxt<'_>, codegen_mode: CodegenMode) -> ! { .into_iter() .collect::>(); - let mut cx = crate::CodegenCx::new(tcx, jit_module, false, false); + let mut cx = crate::CodegenCx::new(tcx, backend_config, &mut jit_module, false); super::time(tcx, "codegen mono items", || { super::predefine_mono_items(&mut cx, &mono_items); - for (mono_item, (linkage, visibility)) in mono_items { - let linkage = crate::linkage::get_clif_linkage(mono_item, linkage, visibility); + for (mono_item, _) in mono_items { match mono_item { - MonoItem::Fn(inst) => match codegen_mode { + MonoItem::Fn(inst) => match backend_config.codegen_mode { CodegenMode::Aot => unreachable!(), CodegenMode::Jit => { - cx.tcx.sess.time("codegen fn", || { - crate::base::codegen_fn(&mut cx, inst, linkage) - }); + cx.tcx.sess.time("codegen fn", || crate::base::codegen_fn(&mut cx, inst)); } CodegenMode::JitLazy => codegen_shim(&mut cx, inst), }, @@ -101,7 +68,7 @@ pub(super) fn run_jit(tcx: TyCtxt<'_>, codegen_mode: CodegenMode) -> ! { } }); - let (mut jit_module, global_asm, _debug, mut unwind_context) = + let (global_asm, _debug, mut unwind_context) = tcx.sess.time("finalize CodegenCx", || cx.finalize()); jit_module.finalize_definitions(); @@ -109,21 +76,16 @@ pub(super) fn run_jit(tcx: TyCtxt<'_>, codegen_mode: CodegenMode) -> ! { tcx.sess.fatal("Inline asm is not supported in JIT mode"); } - crate::main_shim::maybe_create_entry_wrapper(tcx, &mut jit_module, &mut unwind_context, true); crate::allocator::codegen(tcx, &mut jit_module, &mut unwind_context); tcx.sess.abort_if_errors(); jit_module.finalize_definitions(); - let _unwind_register_guard = unsafe { unwind_context.register_jit(&jit_module) }; - let finalized_main: *const u8 = jit_module.get_finalized_function(main_func_id); - - println!("Rustc codegen cranelift will JIT run the executable, because -Cllvm-args=mode=jit was passed"); - - let f: extern "C" fn(c_int, *const *const c_char) -> c_int = - unsafe { ::std::mem::transmute(finalized_main) }; + println!( + "Rustc codegen cranelift will JIT run the executable, because -Cllvm-args=mode=jit was passed" + ); let args = ::std::env::var("CG_CLIF_JIT_ARGS").unwrap_or_else(|_| String::new()); let args = std::iter::once(&*tcx.crate_name(LOCAL_CRATE).as_str().to_string()) @@ -136,12 +98,61 @@ pub(super) fn run_jit(tcx: TyCtxt<'_>, codegen_mode: CodegenMode) -> ! { // useful as some dynamic linkers use it as a marker to jump over. argv.push(std::ptr::null()); - CURRENT_MODULE - .with(|current_module| assert!(current_module.borrow_mut().replace(jit_module).is_none())); + BACKEND_CONFIG.with(|tls_backend_config| { + assert!(tls_backend_config.borrow_mut().replace(backend_config).is_none()) + }); + + let (main_def_id, entry_ty) = tcx.entry_fn(LOCAL_CRATE).unwrap(); + let instance = Instance::mono(tcx, main_def_id.to_def_id()).polymorphize(tcx); + + match entry_ty { + EntryFnType::Main => { + // FIXME set program arguments somehow + + let main_sig = Signature { + params: vec![], + returns: vec![], + call_conv: CallConv::triple_default(&crate::target_triple(tcx.sess)), + }; + let main_func_id = jit_module + .declare_function(tcx.symbol_name(instance).name, Linkage::Import, &main_sig) + .unwrap(); + let finalized_main: *const u8 = jit_module.get_finalized_function(main_func_id); + + CURRENT_MODULE.with(|current_module| { + assert!(current_module.borrow_mut().replace(jit_module).is_none()) + }); + + let f: extern "C" fn() = unsafe { ::std::mem::transmute(finalized_main) }; + f(); + std::process::exit(0); + } + EntryFnType::Start => { + let start_sig = Signature { + params: vec![ + AbiParam::new(jit_module.target_config().pointer_type()), + AbiParam::new(jit_module.target_config().pointer_type()), + ], + returns: vec![AbiParam::new( + jit_module.target_config().pointer_type(), /*isize*/ + )], + call_conv: CallConv::triple_default(&crate::target_triple(tcx.sess)), + }; + let start_func_id = jit_module + .declare_function(tcx.symbol_name(instance).name, Linkage::Import, &start_sig) + .unwrap(); + let finalized_start: *const u8 = jit_module.get_finalized_function(start_func_id); - let ret = f(args.len() as c_int, argv.as_ptr()); + CURRENT_MODULE.with(|current_module| { + assert!(current_module.borrow_mut().replace(jit_module).is_none()) + }); - std::process::exit(ret); + let f: extern "C" fn(c_int, *const *const c_char) -> c_int = + unsafe { ::std::mem::transmute(finalized_start) }; + let ret = f(args.len() as c_int, argv.as_ptr()); + std::process::exit(ret); + } + } } #[no_mangle] @@ -153,21 +164,18 @@ extern "C" fn __clif_jit_fn(instance_ptr: *const Instance<'static>) -> *const u8 CURRENT_MODULE.with(|jit_module| { let mut jit_module = jit_module.borrow_mut(); let jit_module = jit_module.as_mut().unwrap(); - let mut cx = crate::CodegenCx::new(tcx, jit_module, false, false); + let backend_config = + BACKEND_CONFIG.with(|backend_config| backend_config.borrow().clone().unwrap()); let name = tcx.symbol_name(instance).name.to_string(); - let sig = crate::abi::get_function_sig(tcx, cx.module.isa().triple(), instance); - let func_id = cx - .module - .declare_function(&name, Linkage::Export, &sig) - .unwrap(); - cx.module.prepare_for_function_redefine(func_id).unwrap(); + let sig = crate::abi::get_function_sig(tcx, jit_module.isa().triple(), instance); + let func_id = jit_module.declare_function(&name, Linkage::Export, &sig).unwrap(); + jit_module.prepare_for_function_redefine(func_id).unwrap(); - tcx.sess.time("codegen fn", || { - crate::base::codegen_fn(&mut cx, instance, Linkage::Export) - }); + let mut cx = crate::CodegenCx::new(tcx, backend_config, jit_module, false); + tcx.sess.time("codegen fn", || crate::base::codegen_fn(&mut cx, instance)); - let (jit_module, global_asm, _debug_context, unwind_context) = cx.finalize(); + let (global_asm, _debug_context, unwind_context) = cx.finalize(); assert!(global_asm.is_empty()); jit_module.finalize_definitions(); std::mem::forget(unsafe { unwind_context.register_jit(&jit_module) }); @@ -194,9 +202,8 @@ fn load_imported_symbols_for_jit(tcx: TyCtxt<'_>) -> Vec<(String, *const u8)> { Linkage::NotLinked | Linkage::IncludedFromDylib => {} Linkage::Static => { let name = tcx.crate_name(cnum); - let mut err = tcx - .sess - .struct_err(&format!("Can't load static lib {}", name.as_str())); + let mut err = + tcx.sess.struct_err(&format!("Can't load static lib {}", name.as_str())); err.note("rustc_codegen_cranelift can only load dylibs in JIT mode."); err.emit(); } @@ -217,6 +224,11 @@ fn load_imported_symbols_for_jit(tcx: TyCtxt<'_>) -> Vec<(String, *const u8)> { if name.is_empty() || !symbol.is_global() || symbol.is_undefined() { return None; } + if name.starts_with("rust_metadata_") { + // The metadata is part of a section that is not loaded by the dynamic linker in + // case of cg_llvm. + return None; + } let dlsym_name = if cfg!(target_os = "macos") { // On macOS `dlsym` expects the name without leading `_`. assert!(name.starts_with('_'), "{:?}", name); @@ -236,17 +248,14 @@ fn load_imported_symbols_for_jit(tcx: TyCtxt<'_>) -> Vec<(String, *const u8)> { imported_symbols } -pub(super) fn codegen_shim<'tcx>(cx: &mut CodegenCx<'tcx, impl Module>, inst: Instance<'tcx>) { +fn codegen_shim<'tcx>(cx: &mut CodegenCx<'_, 'tcx>, inst: Instance<'tcx>) { let tcx = cx.tcx; let pointer_type = cx.module.target_config().pointer_type(); let name = tcx.symbol_name(inst).name.to_string(); let sig = crate::abi::get_function_sig(tcx, cx.module.isa().triple(), inst); - let func_id = cx - .module - .declare_function(&name, Linkage::Export, &sig) - .unwrap(); + let func_id = cx.module.declare_function(&name, Linkage::Export, &sig).unwrap(); let instance_ptr = Box::into_raw(Box::new(inst)); @@ -267,28 +276,18 @@ pub(super) fn codegen_shim<'tcx>(cx: &mut CodegenCx<'tcx, impl Module>, inst: In let mut builder_ctx = FunctionBuilderContext::new(); let mut trampoline_builder = FunctionBuilder::new(&mut trampoline, &mut builder_ctx); - let jit_fn = cx - .module - .declare_func_in_func(jit_fn, trampoline_builder.func); + let jit_fn = cx.module.declare_func_in_func(jit_fn, trampoline_builder.func); let sig_ref = trampoline_builder.func.import_signature(sig); let entry_block = trampoline_builder.create_block(); trampoline_builder.append_block_params_for_function_params(entry_block); - let fn_args = trampoline_builder - .func - .dfg - .block_params(entry_block) - .to_vec(); + let fn_args = trampoline_builder.func.dfg.block_params(entry_block).to_vec(); trampoline_builder.switch_to_block(entry_block); - let instance_ptr = trampoline_builder - .ins() - .iconst(pointer_type, instance_ptr as u64 as i64); + let instance_ptr = trampoline_builder.ins().iconst(pointer_type, instance_ptr as u64 as i64); let jitted_fn = trampoline_builder.ins().call(jit_fn, &[instance_ptr]); let jitted_fn = trampoline_builder.func.dfg.inst_results(jitted_fn)[0]; - let call_inst = trampoline_builder - .ins() - .call_indirect(sig_ref, jitted_fn, &fn_args); + let call_inst = trampoline_builder.ins().call_indirect(sig_ref, jitted_fn, &fn_args); let ret_vals = trampoline_builder.func.dfg.inst_results(call_inst).to_vec(); trampoline_builder.ins().return_(&ret_vals); @@ -296,7 +295,8 @@ pub(super) fn codegen_shim<'tcx>(cx: &mut CodegenCx<'tcx, impl Module>, inst: In .define_function( func_id, &mut Context::for_function(trampoline), - &mut cranelift_codegen::binemit::NullTrapSink {}, + &mut NullTrapSink {}, + &mut NullStackMapSink {}, ) .unwrap(); } diff --git a/compiler/rustc_codegen_cranelift/src/driver/mod.rs b/compiler/rustc_codegen_cranelift/src/driver/mod.rs index 2497f9dfdfbcf..d49182a07b79e 100644 --- a/compiler/rustc_codegen_cranelift/src/driver/mod.rs +++ b/compiler/rustc_codegen_cranelift/src/driver/mod.rs @@ -17,43 +17,46 @@ pub(crate) fn codegen_crate( tcx: TyCtxt<'_>, metadata: EncodedMetadata, need_metadata_module: bool, - config: crate::BackendConfig, + backend_config: crate::BackendConfig, ) -> Box { tcx.sess.abort_if_errors(); - match config.codegen_mode { - CodegenMode::Aot => aot::run_aot(tcx, metadata, need_metadata_module), + match backend_config.codegen_mode { + CodegenMode::Aot => aot::run_aot(tcx, backend_config, metadata, need_metadata_module), CodegenMode::Jit | CodegenMode::JitLazy => { - let is_executable = tcx - .sess - .crate_types() - .contains(&rustc_session::config::CrateType::Executable); + let is_executable = + tcx.sess.crate_types().contains(&rustc_session::config::CrateType::Executable); if !is_executable { tcx.sess.fatal("can't jit non-executable crate"); } #[cfg(feature = "jit")] - let _: ! = jit::run_jit(tcx, config.codegen_mode); + let _: ! = jit::run_jit(tcx, backend_config); #[cfg(not(feature = "jit"))] - tcx.sess - .fatal("jit support was disabled when compiling rustc_codegen_cranelift"); + tcx.sess.fatal("jit support was disabled when compiling rustc_codegen_cranelift"); } } } fn predefine_mono_items<'tcx>( - cx: &mut crate::CodegenCx<'tcx, impl Module>, + cx: &mut crate::CodegenCx<'_, 'tcx>, mono_items: &[(MonoItem<'tcx>, (RLinkage, Visibility))], ) { cx.tcx.sess.time("predefine functions", || { + let is_compiler_builtins = cx.tcx.is_compiler_builtins(LOCAL_CRATE); for &(mono_item, (linkage, visibility)) in mono_items { match mono_item { MonoItem::Fn(instance) => { let name = cx.tcx.symbol_name(instance).name.to_string(); let _inst_guard = crate::PrintOnPanic(|| format!("{:?} {}", instance, name)); let sig = get_function_sig(cx.tcx, cx.module.isa().triple(), instance); - let linkage = crate::linkage::get_clif_linkage(mono_item, linkage, visibility); + let linkage = crate::linkage::get_clif_linkage( + mono_item, + linkage, + visibility, + is_compiler_builtins, + ); cx.module.declare_function(&name, linkage, &sig).unwrap(); } MonoItem::Static(_) | MonoItem::GlobalAsm(_) => {} @@ -63,21 +66,12 @@ fn predefine_mono_items<'tcx>( } fn time(tcx: TyCtxt<'_>, name: &'static str, f: impl FnOnce() -> R) -> R { - if std::env::var("CG_CLIF_DISPLAY_CG_TIME") - .as_ref() - .map(|val| &**val) - == Ok("1") - { + if std::env::var("CG_CLIF_DISPLAY_CG_TIME").as_ref().map(|val| &**val) == Ok("1") { println!("[{:<30}: {}] start", tcx.crate_name(LOCAL_CRATE), name); let before = std::time::Instant::now(); let res = tcx.sess.time(name, f); let after = std::time::Instant::now(); - println!( - "[{:<30}: {}] end time: {:?}", - tcx.crate_name(LOCAL_CRATE), - name, - after - before - ); + println!("[{:<30}: {}] end time: {:?}", tcx.crate_name(LOCAL_CRATE), name, after - before); res } else { tcx.sess.time(name, f) diff --git a/compiler/rustc_codegen_cranelift/src/inline_asm.rs b/compiler/rustc_codegen_cranelift/src/inline_asm.rs index 04aac780125d9..1fb5e86aed7df 100644 --- a/compiler/rustc_codegen_cranelift/src/inline_asm.rs +++ b/compiler/rustc_codegen_cranelift/src/inline_asm.rs @@ -9,7 +9,7 @@ use rustc_middle::mir::InlineAsmOperand; use rustc_target::asm::*; pub(crate) fn codegen_inline_asm<'tcx>( - fx: &mut FunctionCx<'_, 'tcx, impl Module>, + fx: &mut FunctionCx<'_, '_, 'tcx>, _span: Span, template: &[InlineAsmTemplatePiece], operands: &[InlineAsmOperand<'tcx>], @@ -20,6 +20,10 @@ pub(crate) fn codegen_inline_asm<'tcx>( if template.is_empty() { // Black box return; + } else if template[0] == InlineAsmTemplatePiece::String("int $$0x29".to_string()) { + let true_ = fx.bcx.ins().iconst(types::I32, 1); + fx.bcx.ins().trapnz(true_, TrapCode::User(1)); + return; } let mut slot_size = Size::from_bytes(0); @@ -53,11 +57,7 @@ pub(crate) fn codegen_inline_asm<'tcx>( crate::base::codegen_operand(fx, value).load_scalar(fx), )); } - InlineAsmOperand::Out { - reg, - late: _, - place, - } => { + InlineAsmOperand::Out { reg, late: _, place } => { let reg = expect_reg(reg); clobbered_regs.push((reg, new_slot(reg.reg_class()))); if let Some(place) = place { @@ -68,12 +68,7 @@ pub(crate) fn codegen_inline_asm<'tcx>( )); } } - InlineAsmOperand::InOut { - reg, - late: _, - ref in_value, - out_place, - } => { + InlineAsmOperand::InOut { reg, late: _, ref in_value, out_place } => { let reg = expect_reg(reg); clobbered_regs.push((reg, new_slot(reg.reg_class()))); inputs.push(( @@ -97,11 +92,8 @@ pub(crate) fn codegen_inline_asm<'tcx>( let inline_asm_index = fx.inline_asm_index; fx.inline_asm_index += 1; - let asm_name = format!( - "{}__inline_asm_{}", - fx.tcx.symbol_name(fx.instance).name, - inline_asm_index - ); + let asm_name = + format!("{}__inline_asm_{}", fx.tcx.symbol_name(fx.instance).name, inline_asm_index); let generated_asm = generate_asm_wrapper( &asm_name, @@ -129,12 +121,7 @@ fn generate_asm_wrapper( let mut generated_asm = String::new(); writeln!(generated_asm, ".globl {}", asm_name).unwrap(); writeln!(generated_asm, ".type {},@function", asm_name).unwrap(); - writeln!( - generated_asm, - ".section .text.{},\"ax\",@progbits", - asm_name - ) - .unwrap(); + writeln!(generated_asm, ".section .text.{},\"ax\",@progbits", asm_name).unwrap(); writeln!(generated_asm, "{}:", asm_name).unwrap(); generated_asm.push_str(".intel_syntax noprefix\n"); @@ -164,11 +151,7 @@ fn generate_asm_wrapper( InlineAsmTemplatePiece::String(s) => { generated_asm.push_str(s); } - InlineAsmTemplatePiece::Placeholder { - operand_idx: _, - modifier: _, - span: _, - } => todo!(), + InlineAsmTemplatePiece::Placeholder { operand_idx: _, modifier: _, span: _ } => todo!(), } } generated_asm.push('\n'); @@ -203,7 +186,7 @@ fn generate_asm_wrapper( } fn call_inline_asm<'tcx>( - fx: &mut FunctionCx<'_, 'tcx, impl Module>, + fx: &mut FunctionCx<'_, '_, 'tcx>, asm_name: &str, slot_size: Size, inputs: Vec<(InlineAsmReg, Size, Value)>, @@ -214,8 +197,9 @@ fn call_inline_asm<'tcx>( offset: None, size: u32::try_from(slot_size.bytes()).unwrap(), }); - #[cfg(debug_assertions)] - fx.add_comment(stack_slot, "inline asm scratch slot"); + if fx.clif_comments.enabled() { + fx.add_comment(stack_slot, "inline asm scratch slot"); + } let inline_asm_func = fx .cx @@ -230,17 +214,13 @@ fn call_inline_asm<'tcx>( }, ) .unwrap(); - let inline_asm_func = fx - .cx - .module - .declare_func_in_func(inline_asm_func, &mut fx.bcx.func); - #[cfg(debug_assertions)] - fx.add_comment(inline_asm_func, asm_name); + let inline_asm_func = fx.cx.module.declare_func_in_func(inline_asm_func, &mut fx.bcx.func); + if fx.clif_comments.enabled() { + fx.add_comment(inline_asm_func, asm_name); + } for (_reg, offset, value) in inputs { - fx.bcx - .ins() - .stack_store(value, stack_slot, i32::try_from(offset.bytes()).unwrap()); + fx.bcx.ins().stack_store(value, stack_slot, i32::try_from(offset.bytes()).unwrap()); } let stack_slot_addr = fx.bcx.ins().stack_addr(fx.pointer_type, stack_slot, 0); @@ -248,10 +228,7 @@ fn call_inline_asm<'tcx>( for (_reg, offset, place) in outputs { let ty = fx.clif_type(place.layout().ty).unwrap(); - let value = fx - .bcx - .ins() - .stack_load(ty, stack_slot, i32::try_from(offset.bytes()).unwrap()); + let value = fx.bcx.ins().stack_load(ty, stack_slot, i32::try_from(offset.bytes()).unwrap()); place.write_cvalue(fx, CValue::by_val(value, place.layout())); } } @@ -267,8 +244,7 @@ fn save_register(generated_asm: &mut String, arch: InlineAsmArch, reg: InlineAsm match arch { InlineAsmArch::X86_64 => { write!(generated_asm, " mov [rbp+0x{:x}], ", offset.bytes()).unwrap(); - reg.emit(generated_asm, InlineAsmArch::X86_64, None) - .unwrap(); + reg.emit(generated_asm, InlineAsmArch::X86_64, None).unwrap(); generated_asm.push('\n'); } _ => unimplemented!("save_register for {:?}", arch), @@ -284,8 +260,7 @@ fn restore_register( match arch { InlineAsmArch::X86_64 => { generated_asm.push_str(" mov "); - reg.emit(generated_asm, InlineAsmArch::X86_64, None) - .unwrap(); + reg.emit(generated_asm, InlineAsmArch::X86_64, None).unwrap(); writeln!(generated_asm, ", [rbp+0x{:x}]", offset.bytes()).unwrap(); } _ => unimplemented!("restore_register for {:?}", arch), diff --git a/compiler/rustc_codegen_cranelift/src/intrinsics/cpuid.rs b/compiler/rustc_codegen_cranelift/src/intrinsics/cpuid.rs index c1a1cdbe4eb7d..b27b0eddfbad6 100644 --- a/compiler/rustc_codegen_cranelift/src/intrinsics/cpuid.rs +++ b/compiler/rustc_codegen_cranelift/src/intrinsics/cpuid.rs @@ -6,7 +6,7 @@ use crate::prelude::*; /// /// This emulates an intel cpu with sse and sse2 support, but which doesn't support anything else. pub(crate) fn codegen_cpuid_call<'tcx>( - fx: &mut FunctionCx<'_, 'tcx, impl Module>, + fx: &mut FunctionCx<'_, '_, 'tcx>, leaf: Value, _subleaf: Value, ) -> (Value, Value, Value, Value) { @@ -31,54 +31,28 @@ pub(crate) fn codegen_cpuid_call<'tcx>( fx.bcx.switch_to_block(leaf_0); let max_basic_leaf = fx.bcx.ins().iconst(types::I32, 1); - let vend0 = fx - .bcx - .ins() - .iconst(types::I32, i64::from(u32::from_le_bytes(*b"Genu"))); - let vend2 = fx - .bcx - .ins() - .iconst(types::I32, i64::from(u32::from_le_bytes(*b"ineI"))); - let vend1 = fx - .bcx - .ins() - .iconst(types::I32, i64::from(u32::from_le_bytes(*b"ntel"))); - fx.bcx - .ins() - .jump(dest, &[max_basic_leaf, vend0, vend1, vend2]); + let vend0 = fx.bcx.ins().iconst(types::I32, i64::from(u32::from_le_bytes(*b"Genu"))); + let vend2 = fx.bcx.ins().iconst(types::I32, i64::from(u32::from_le_bytes(*b"ineI"))); + let vend1 = fx.bcx.ins().iconst(types::I32, i64::from(u32::from_le_bytes(*b"ntel"))); + fx.bcx.ins().jump(dest, &[max_basic_leaf, vend0, vend1, vend2]); fx.bcx.switch_to_block(leaf_1); let cpu_signature = fx.bcx.ins().iconst(types::I32, 0); let additional_information = fx.bcx.ins().iconst(types::I32, 0); let ecx_features = fx.bcx.ins().iconst(types::I32, 0); - let edx_features = fx - .bcx - .ins() - .iconst(types::I32, 1 << 25 /* sse */ | 1 << 26 /* sse2 */); - fx.bcx.ins().jump( - dest, - &[ - cpu_signature, - additional_information, - ecx_features, - edx_features, - ], - ); + let edx_features = fx.bcx.ins().iconst(types::I32, 1 << 25 /* sse */ | 1 << 26 /* sse2 */); + fx.bcx.ins().jump(dest, &[cpu_signature, additional_information, ecx_features, edx_features]); fx.bcx.switch_to_block(leaf_8000_0000); let extended_max_basic_leaf = fx.bcx.ins().iconst(types::I32, 0); let zero = fx.bcx.ins().iconst(types::I32, 0); - fx.bcx - .ins() - .jump(dest, &[extended_max_basic_leaf, zero, zero, zero]); + fx.bcx.ins().jump(dest, &[extended_max_basic_leaf, zero, zero, zero]); fx.bcx.switch_to_block(leaf_8000_0001); let zero = fx.bcx.ins().iconst(types::I32, 0); let proc_info_ecx = fx.bcx.ins().iconst(types::I32, 0); let proc_info_edx = fx.bcx.ins().iconst(types::I32, 0); - fx.bcx - .ins() - .jump(dest, &[zero, zero, proc_info_ecx, proc_info_edx]); + fx.bcx.ins().jump(dest, &[zero, zero, proc_info_ecx, proc_info_edx]); fx.bcx.switch_to_block(unsupported_leaf); crate::trap::trap_unreachable( diff --git a/compiler/rustc_codegen_cranelift/src/intrinsics/llvm.rs b/compiler/rustc_codegen_cranelift/src/intrinsics/llvm.rs index d58e4d4995842..83c91f789cd25 100644 --- a/compiler/rustc_codegen_cranelift/src/intrinsics/llvm.rs +++ b/compiler/rustc_codegen_cranelift/src/intrinsics/llvm.rs @@ -6,7 +6,7 @@ use crate::prelude::*; use rustc_middle::ty::subst::SubstsRef; pub(crate) fn codegen_llvm_intrinsic_call<'tcx>( - fx: &mut FunctionCx<'_, 'tcx, impl Module>, + fx: &mut FunctionCx<'_, '_, 'tcx>, intrinsic: &str, substs: SubstsRef<'tcx>, args: &[mir::Operand<'tcx>], @@ -53,7 +53,7 @@ pub(crate) fn codegen_llvm_intrinsic_call<'tcx>( }; llvm.x86.sse2.cmp.ps | llvm.x86.sse2.cmp.pd, (c x, c y, o kind) { let kind_const = crate::constant::mir_operand_get_const_val(fx, kind).expect("llvm.x86.sse2.cmp.* kind not const"); - let flt_cc = match kind_const.val.try_to_bits(Size::from_bytes(1)).unwrap_or_else(|| panic!("kind not scalar: {:?}", kind_const)) { + let flt_cc = match kind_const.try_to_bits(Size::from_bytes(1)).unwrap_or_else(|| panic!("kind not scalar: {:?}", kind_const)) { 0 => FloatCC::Equal, 1 => FloatCC::LessThan, 2 => FloatCC::LessThanOrEqual, @@ -84,7 +84,7 @@ pub(crate) fn codegen_llvm_intrinsic_call<'tcx>( llvm.x86.sse2.psrli.d, (c a, o imm8) { let imm8 = crate::constant::mir_operand_get_const_val(fx, imm8).expect("llvm.x86.sse2.psrli.d imm8 not const"); simd_for_each_lane(fx, a, ret, |fx, _lane_layout, res_lane_layout, lane| { - let res_lane = match imm8.val.try_to_bits(Size::from_bytes(4)).unwrap_or_else(|| panic!("imm8 not scalar: {:?}", imm8)) { + let res_lane = match imm8.try_to_bits(Size::from_bytes(4)).unwrap_or_else(|| panic!("imm8 not scalar: {:?}", imm8)) { imm8 if imm8 < 32 => fx.bcx.ins().ushr_imm(lane, i64::from(imm8 as u8)), _ => fx.bcx.ins().iconst(types::I32, 0), }; @@ -94,7 +94,7 @@ pub(crate) fn codegen_llvm_intrinsic_call<'tcx>( llvm.x86.sse2.pslli.d, (c a, o imm8) { let imm8 = crate::constant::mir_operand_get_const_val(fx, imm8).expect("llvm.x86.sse2.psrli.d imm8 not const"); simd_for_each_lane(fx, a, ret, |fx, _lane_layout, res_lane_layout, lane| { - let res_lane = match imm8.val.try_to_bits(Size::from_bytes(4)).unwrap_or_else(|| panic!("imm8 not scalar: {:?}", imm8)) { + let res_lane = match imm8.try_to_bits(Size::from_bytes(4)).unwrap_or_else(|| panic!("imm8 not scalar: {:?}", imm8)) { imm8 if imm8 < 32 => fx.bcx.ins().ishl_imm(lane, i64::from(imm8 as u8)), _ => fx.bcx.ins().iconst(types::I32, 0), }; diff --git a/compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs b/compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs index 8946ac43bc65a..39e047a98f9eb 100644 --- a/compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs +++ b/compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs @@ -9,6 +9,7 @@ pub(crate) use cpuid::codegen_cpuid_call; pub(crate) use llvm::codegen_llvm_intrinsic_call; use crate::prelude::*; +use cranelift_codegen::ir::AtomicRmwOp; use rustc_middle::ty::print::with_no_trimmed_paths; macro intrinsic_pat { @@ -112,38 +113,6 @@ macro call_intrinsic_match { } } -macro atomic_binop_return_old($fx:expr, $op:ident<$T:ident>($ptr:ident, $src:ident) -> $ret:ident) { - crate::atomic_shim::lock_global_lock($fx); - - let clif_ty = $fx.clif_type($T).unwrap(); - let old = $fx.bcx.ins().load(clif_ty, MemFlags::new(), $ptr, 0); - let new = $fx.bcx.ins().$op(old, $src); - $fx.bcx.ins().store(MemFlags::new(), new, $ptr, 0); - $ret.write_cvalue($fx, CValue::by_val(old, $fx.layout_of($T))); - - crate::atomic_shim::unlock_global_lock($fx); -} - -macro atomic_minmax($fx:expr, $cc:expr, <$T:ident> ($ptr:ident, $src:ident) -> $ret:ident) { - crate::atomic_shim::lock_global_lock($fx); - - // Read old - let clif_ty = $fx.clif_type($T).unwrap(); - let old = $fx.bcx.ins().load(clif_ty, MemFlags::new(), $ptr, 0); - - // Compare - let is_eq = $fx.bcx.ins().icmp(IntCC::SignedGreaterThan, old, $src); - let new = $fx.bcx.ins().select(is_eq, old, $src); - - // Write new - $fx.bcx.ins().store(MemFlags::new(), new, $ptr, 0); - - let ret_val = CValue::by_val(old, $ret.layout()); - $ret.write_cvalue($fx, ret_val); - - crate::atomic_shim::unlock_global_lock($fx); -} - macro validate_atomic_type($fx:ident, $intrinsic:ident, $span:ident, $ty:expr) { match $ty.kind() { ty::Uint(_) | ty::Int(_) | ty::RawPtr(..) => {} @@ -184,12 +153,12 @@ pub(crate) fn clif_vector_type<'tcx>(tcx: TyCtxt<'tcx>, layout: TyAndLayout<'tcx } } -fn simd_for_each_lane<'tcx, M: Module>( - fx: &mut FunctionCx<'_, 'tcx, M>, +fn simd_for_each_lane<'tcx>( + fx: &mut FunctionCx<'_, '_, 'tcx>, val: CValue<'tcx>, ret: CPlace<'tcx>, f: impl Fn( - &mut FunctionCx<'_, 'tcx, M>, + &mut FunctionCx<'_, '_, 'tcx>, TyAndLayout<'tcx>, TyAndLayout<'tcx>, Value, @@ -213,13 +182,13 @@ fn simd_for_each_lane<'tcx, M: Module>( } } -fn simd_pair_for_each_lane<'tcx, M: Module>( - fx: &mut FunctionCx<'_, 'tcx, M>, +fn simd_pair_for_each_lane<'tcx>( + fx: &mut FunctionCx<'_, '_, 'tcx>, x: CValue<'tcx>, y: CValue<'tcx>, ret: CPlace<'tcx>, f: impl Fn( - &mut FunctionCx<'_, 'tcx, M>, + &mut FunctionCx<'_, '_, 'tcx>, TyAndLayout<'tcx>, TyAndLayout<'tcx>, Value, @@ -246,11 +215,11 @@ fn simd_pair_for_each_lane<'tcx, M: Module>( } } -fn simd_reduce<'tcx, M: Module>( - fx: &mut FunctionCx<'_, 'tcx, M>, +fn simd_reduce<'tcx>( + fx: &mut FunctionCx<'_, '_, 'tcx>, val: CValue<'tcx>, ret: CPlace<'tcx>, - f: impl Fn(&mut FunctionCx<'_, 'tcx, M>, TyAndLayout<'tcx>, Value, Value) -> Value, + f: impl Fn(&mut FunctionCx<'_, '_, 'tcx>, TyAndLayout<'tcx>, Value, Value) -> Value, ) { let (lane_count, lane_ty) = val.layout().ty.simd_size_and_type(fx.tcx); let lane_layout = fx.layout_of(lane_ty); @@ -258,20 +227,19 @@ fn simd_reduce<'tcx, M: Module>( let mut res_val = val.value_field(fx, mir::Field::new(0)).load_scalar(fx); for lane_idx in 1..lane_count { - let lane = val - .value_field(fx, mir::Field::new(lane_idx.try_into().unwrap())) - .load_scalar(fx); + let lane = + val.value_field(fx, mir::Field::new(lane_idx.try_into().unwrap())).load_scalar(fx); res_val = f(fx, lane_layout, res_val, lane); } let res = CValue::by_val(res_val, lane_layout); ret.write_cvalue(fx, res); } -fn simd_reduce_bool<'tcx, M: Module>( - fx: &mut FunctionCx<'_, 'tcx, M>, +fn simd_reduce_bool<'tcx>( + fx: &mut FunctionCx<'_, '_, 'tcx>, val: CValue<'tcx>, ret: CPlace<'tcx>, - f: impl Fn(&mut FunctionCx<'_, 'tcx, M>, Value, Value) -> Value, + f: impl Fn(&mut FunctionCx<'_, '_, 'tcx>, Value, Value) -> Value, ) { let (lane_count, _lane_ty) = val.layout().ty.simd_size_and_type(fx.tcx); assert!(ret.layout().ty.is_bool()); @@ -279,9 +247,8 @@ fn simd_reduce_bool<'tcx, M: Module>( let res_val = val.value_field(fx, mir::Field::new(0)).load_scalar(fx); let mut res_val = fx.bcx.ins().band_imm(res_val, 1); // mask to boolean for lane_idx in 1..lane_count { - let lane = val - .value_field(fx, mir::Field::new(lane_idx.try_into().unwrap())) - .load_scalar(fx); + let lane = + val.value_field(fx, mir::Field::new(lane_idx.try_into().unwrap())).load_scalar(fx); let lane = fx.bcx.ins().band_imm(lane, 1); // mask to boolean res_val = f(fx, res_val, lane); } @@ -290,7 +257,7 @@ fn simd_reduce_bool<'tcx, M: Module>( } fn bool_to_zero_or_max_uint<'tcx>( - fx: &mut FunctionCx<'_, 'tcx, impl Module>, + fx: &mut FunctionCx<'_, '_, 'tcx>, layout: TyAndLayout<'tcx>, val: Value, ) -> CValue<'tcx> { @@ -424,7 +391,7 @@ macro simd_flt_binop($fx:expr, $op:ident($x:ident, $y:ident) -> $ret:ident) { } pub(crate) fn codegen_intrinsic_call<'tcx>( - fx: &mut FunctionCx<'_, 'tcx, impl Module>, + fx: &mut FunctionCx<'_, '_, 'tcx>, instance: Instance<'tcx>, args: &[mir::Operand<'tcx>], destination: Option<(CPlace<'tcx>, BasicBlock)>, @@ -912,136 +879,175 @@ pub(crate) fn codegen_intrinsic_call<'tcx>( }; _ if intrinsic.starts_with("atomic_fence"), () { - crate::atomic_shim::lock_global_lock(fx); - crate::atomic_shim::unlock_global_lock(fx); + fx.bcx.ins().fence(); }; _ if intrinsic.starts_with("atomic_singlethreadfence"), () { - crate::atomic_shim::lock_global_lock(fx); - crate::atomic_shim::unlock_global_lock(fx); + // FIXME use a compiler fence once Cranelift supports it + fx.bcx.ins().fence(); }; - _ if intrinsic.starts_with("atomic_load"), (c ptr) { - crate::atomic_shim::lock_global_lock(fx); + _ if intrinsic.starts_with("atomic_load"), (v ptr) { + validate_atomic_type!(fx, intrinsic, span, T); + let ty = fx.clif_type(T).unwrap(); - let inner_layout = - fx.layout_of(ptr.layout().ty.builtin_deref(true).unwrap().ty); - validate_atomic_type!(fx, intrinsic, span, inner_layout.ty); - let val = CValue::by_ref(Pointer::new(ptr.load_scalar(fx)), inner_layout); - ret.write_cvalue(fx, val); + let val = fx.bcx.ins().atomic_load(ty, MemFlags::trusted(), ptr); - crate::atomic_shim::unlock_global_lock(fx); + let val = CValue::by_val(val, fx.layout_of(T)); + ret.write_cvalue(fx, val); }; _ if intrinsic.starts_with("atomic_store"), (v ptr, c val) { validate_atomic_type!(fx, intrinsic, span, val.layout().ty); - crate::atomic_shim::lock_global_lock(fx); - - let dest = CPlace::for_ptr(Pointer::new(ptr), val.layout()); - dest.write_cvalue(fx, val); + let val = val.load_scalar(fx); - crate::atomic_shim::unlock_global_lock(fx); + fx.bcx.ins().atomic_store(MemFlags::trusted(), val, ptr); }; - _ if intrinsic.starts_with("atomic_xchg"), (v ptr, c src) { - validate_atomic_type!(fx, intrinsic, span, T); - - crate::atomic_shim::lock_global_lock(fx); + _ if intrinsic.starts_with("atomic_xchg"), (v ptr, c new) { + let layout = new.layout(); + validate_atomic_type!(fx, intrinsic, span, layout.ty); + let ty = fx.clif_type(layout.ty).unwrap(); - // Read old - let clif_ty = fx.clif_type(T).unwrap(); - let old = fx.bcx.ins().load(clif_ty, MemFlags::new(), ptr, 0); - ret.write_cvalue(fx, CValue::by_val(old, fx.layout_of(T))); + let new = new.load_scalar(fx); - // Write new - let dest = CPlace::for_ptr(Pointer::new(ptr), src.layout()); - dest.write_cvalue(fx, src); + let old = fx.bcx.ins().atomic_rmw(ty, MemFlags::trusted(), AtomicRmwOp::Xchg, ptr, new); - crate::atomic_shim::unlock_global_lock(fx); + let old = CValue::by_val(old, layout); + ret.write_cvalue(fx, old); }; - _ if intrinsic.starts_with("atomic_cxchg"), (v ptr, c test_old, c new) { // both atomic_cxchg_* and atomic_cxchgweak_* - validate_atomic_type!(fx, intrinsic, span, T); + _ if intrinsic.starts_with("atomic_cxchg"), (v ptr, c test_old, c new) { // both atomic_cxchg_* and atomic_cxchgweak_* + let layout = new.layout(); + validate_atomic_type!(fx, intrinsic, span, layout.ty); let test_old = test_old.load_scalar(fx); let new = new.load_scalar(fx); - crate::atomic_shim::lock_global_lock(fx); - - // Read old - let clif_ty = fx.clif_type(T).unwrap(); - let old = fx.bcx.ins().load(clif_ty, MemFlags::new(), ptr, 0); - - // Compare + let old = fx.bcx.ins().atomic_cas(MemFlags::trusted(), ptr, test_old, new); let is_eq = fx.bcx.ins().icmp(IntCC::Equal, old, test_old); - let new = fx.bcx.ins().select(is_eq, new, old); // Keep old if not equal to test_old - - // Write new - fx.bcx.ins().store(MemFlags::new(), new, ptr, 0); let ret_val = CValue::by_val_pair(old, fx.bcx.ins().bint(types::I8, is_eq), ret.layout()); - ret.write_cvalue(fx, ret_val); - - crate::atomic_shim::unlock_global_lock(fx); + ret.write_cvalue(fx, ret_val) }; - _ if intrinsic.starts_with("atomic_xadd"), (v ptr, c amount) { - validate_atomic_type!(fx, intrinsic, span, ret.layout().ty); + _ if intrinsic.starts_with("atomic_xadd"), (v ptr, c amount) { + let layout = amount.layout(); + validate_atomic_type!(fx, intrinsic, span, layout.ty); + let ty = fx.clif_type(layout.ty).unwrap(); + let amount = amount.load_scalar(fx); - atomic_binop_return_old! (fx, iadd(ptr, amount) -> ret); + + let old = fx.bcx.ins().atomic_rmw(ty, MemFlags::trusted(), AtomicRmwOp::Add, ptr, amount); + + let old = CValue::by_val(old, layout); + ret.write_cvalue(fx, old); }; - _ if intrinsic.starts_with("atomic_xsub"), (v ptr, c amount) { - validate_atomic_type!(fx, intrinsic, span, ret.layout().ty); + _ if intrinsic.starts_with("atomic_xsub"), (v ptr, c amount) { + let layout = amount.layout(); + validate_atomic_type!(fx, intrinsic, span, layout.ty); + let ty = fx.clif_type(layout.ty).unwrap(); + let amount = amount.load_scalar(fx); - atomic_binop_return_old! (fx, isub(ptr, amount) -> ret); + + let old = fx.bcx.ins().atomic_rmw(ty, MemFlags::trusted(), AtomicRmwOp::Sub, ptr, amount); + + let old = CValue::by_val(old, layout); + ret.write_cvalue(fx, old); }; - _ if intrinsic.starts_with("atomic_and"), (v ptr, c src) { - validate_atomic_type!(fx, intrinsic, span, ret.layout().ty); + _ if intrinsic.starts_with("atomic_and"), (v ptr, c src) { + let layout = src.layout(); + validate_atomic_type!(fx, intrinsic, span, layout.ty); + let ty = fx.clif_type(layout.ty).unwrap(); + let src = src.load_scalar(fx); - atomic_binop_return_old! (fx, band(ptr, src) -> ret); + + let old = fx.bcx.ins().atomic_rmw(ty, MemFlags::trusted(), AtomicRmwOp::And, ptr, src); + + let old = CValue::by_val(old, layout); + ret.write_cvalue(fx, old); }; - _ if intrinsic.starts_with("atomic_nand"), (v ptr, c src) { - validate_atomic_type!(fx, intrinsic, span, T); + _ if intrinsic.starts_with("atomic_or"), (v ptr, c src) { + let layout = src.layout(); + validate_atomic_type!(fx, intrinsic, span, layout.ty); + let ty = fx.clif_type(layout.ty).unwrap(); let src = src.load_scalar(fx); - crate::atomic_shim::lock_global_lock(fx); - - let clif_ty = fx.clif_type(T).unwrap(); - let old = fx.bcx.ins().load(clif_ty, MemFlags::new(), ptr, 0); - let and = fx.bcx.ins().band(old, src); - let new = fx.bcx.ins().bnot(and); - fx.bcx.ins().store(MemFlags::new(), new, ptr, 0); - ret.write_cvalue(fx, CValue::by_val(old, fx.layout_of(T))); + let old = fx.bcx.ins().atomic_rmw(ty, MemFlags::trusted(), AtomicRmwOp::Or, ptr, src); - crate::atomic_shim::unlock_global_lock(fx); + let old = CValue::by_val(old, layout); + ret.write_cvalue(fx, old); }; - _ if intrinsic.starts_with("atomic_or"), (v ptr, c src) { - validate_atomic_type!(fx, intrinsic, span, ret.layout().ty); + _ if intrinsic.starts_with("atomic_xor"), (v ptr, c src) { + let layout = src.layout(); + validate_atomic_type!(fx, intrinsic, span, layout.ty); + let ty = fx.clif_type(layout.ty).unwrap(); + let src = src.load_scalar(fx); - atomic_binop_return_old! (fx, bor(ptr, src) -> ret); + + let old = fx.bcx.ins().atomic_rmw(ty, MemFlags::trusted(), AtomicRmwOp::Xor, ptr, src); + + let old = CValue::by_val(old, layout); + ret.write_cvalue(fx, old); }; - _ if intrinsic.starts_with("atomic_xor"), (v ptr, c src) { - validate_atomic_type!(fx, intrinsic, span, ret.layout().ty); + + // FIXME https://github.com/bytecodealliance/wasmtime/issues/2647 + _ if intrinsic.starts_with("atomic_nand"), (v ptr, c src) { + let layout = src.layout(); + validate_atomic_type!(fx, intrinsic, span, layout.ty); + let ty = fx.clif_type(layout.ty).unwrap(); + let src = src.load_scalar(fx); - atomic_binop_return_old! (fx, bxor(ptr, src) -> ret); + + let old = fx.bcx.ins().atomic_rmw(ty, MemFlags::trusted(), AtomicRmwOp::Nand, ptr, src); + + let old = CValue::by_val(old, layout); + ret.write_cvalue(fx, old); }; + _ if intrinsic.starts_with("atomic_max"), (v ptr, c src) { + let layout = src.layout(); + validate_atomic_type!(fx, intrinsic, span, layout.ty); + let ty = fx.clif_type(layout.ty).unwrap(); - _ if intrinsic.starts_with("atomic_max"), (v ptr, c src) { - validate_atomic_type!(fx, intrinsic, span, ret.layout().ty); let src = src.load_scalar(fx); - atomic_minmax!(fx, IntCC::SignedGreaterThan, (ptr, src) -> ret); + + let old = fx.bcx.ins().atomic_rmw(ty, MemFlags::trusted(), AtomicRmwOp::Smax, ptr, src); + + let old = CValue::by_val(old, layout); + ret.write_cvalue(fx, old); }; - _ if intrinsic.starts_with("atomic_umax"), (v ptr, c src) { - validate_atomic_type!(fx, intrinsic, span, ret.layout().ty); + _ if intrinsic.starts_with("atomic_umax"), (v ptr, c src) { + let layout = src.layout(); + validate_atomic_type!(fx, intrinsic, span, layout.ty); + let ty = fx.clif_type(layout.ty).unwrap(); + let src = src.load_scalar(fx); - atomic_minmax!(fx, IntCC::UnsignedGreaterThan, (ptr, src) -> ret); + + let old = fx.bcx.ins().atomic_rmw(ty, MemFlags::trusted(), AtomicRmwOp::Umax, ptr, src); + + let old = CValue::by_val(old, layout); + ret.write_cvalue(fx, old); }; - _ if intrinsic.starts_with("atomic_min"), (v ptr, c src) { - validate_atomic_type!(fx, intrinsic, span, ret.layout().ty); + _ if intrinsic.starts_with("atomic_min"), (v ptr, c src) { + let layout = src.layout(); + validate_atomic_type!(fx, intrinsic, span, layout.ty); + let ty = fx.clif_type(layout.ty).unwrap(); + let src = src.load_scalar(fx); - atomic_minmax!(fx, IntCC::SignedLessThan, (ptr, src) -> ret); + + let old = fx.bcx.ins().atomic_rmw(ty, MemFlags::trusted(), AtomicRmwOp::Smin, ptr, src); + + let old = CValue::by_val(old, layout); + ret.write_cvalue(fx, old); }; - _ if intrinsic.starts_with("atomic_umin"), (v ptr, c src) { - validate_atomic_type!(fx, intrinsic, span, ret.layout().ty); + _ if intrinsic.starts_with("atomic_umin"), (v ptr, c src) { + let layout = src.layout(); + validate_atomic_type!(fx, intrinsic, span, layout.ty); + let ty = fx.clif_type(layout.ty).unwrap(); + let src = src.load_scalar(fx); - atomic_minmax!(fx, IntCC::UnsignedLessThan, (ptr, src) -> ret); + + let old = fx.bcx.ins().atomic_rmw(ty, MemFlags::trusted(), AtomicRmwOp::Umin, ptr, src); + + let old = CValue::by_val(old, layout); + ret.write_cvalue(fx, old); }; minnumf32, (v a, v b) { diff --git a/compiler/rustc_codegen_cranelift/src/intrinsics/simd.rs b/compiler/rustc_codegen_cranelift/src/intrinsics/simd.rs index e0eb5c59590ff..27fc2abedc7e9 100644 --- a/compiler/rustc_codegen_cranelift/src/intrinsics/simd.rs +++ b/compiler/rustc_codegen_cranelift/src/intrinsics/simd.rs @@ -4,7 +4,7 @@ use super::*; use crate::prelude::*; pub(super) fn codegen_simd_intrinsic_call<'tcx>( - fx: &mut FunctionCx<'_, 'tcx, impl Module>, + fx: &mut FunctionCx<'_, '_, 'tcx>, instance: Instance<'tcx>, args: &[mir::Operand<'tcx>], ret: CPlace<'tcx>, @@ -85,10 +85,10 @@ pub(super) fn codegen_simd_intrinsic_call<'tcx>( use rustc_middle::mir::interpret::*; let idx_const = crate::constant::mir_operand_get_const_val(fx, idx).expect("simd_shuffle* idx not const"); - let idx_bytes = match idx_const.val { - ty::ConstKind::Value(ConstValue::ByRef { alloc, offset }) => { + let idx_bytes = match idx_const { + ConstValue::ByRef { alloc, offset } => { let ptr = Pointer::new(AllocId(0 /* dummy */), offset); - let size = Size::from_bytes(4 * u64::from(ret_lane_count) /* size_of([u32; ret_lane_count]) */); + let size = Size::from_bytes(4 * ret_lane_count /* size_of([u32; ret_lane_count]) */); alloc.get_bytes(fx, ptr, size).unwrap() } _ => unreachable!("{:?}", idx_const), @@ -130,7 +130,7 @@ pub(super) fn codegen_simd_intrinsic_call<'tcx>( ); }; - let idx = idx_const.val.try_to_bits(Size::from_bytes(4 /* u32*/)).unwrap_or_else(|| panic!("kind not scalar: {:?}", idx_const)); + let idx = idx_const.try_to_bits(Size::from_bytes(4 /* u32*/)).unwrap_or_else(|| panic!("kind not scalar: {:?}", idx_const)); let (lane_count, _lane_ty) = base.layout().ty.simd_size_and_type(fx.tcx); if idx >= lane_count.into() { fx.tcx.sess.span_fatal(fx.mir.span, &format!("[simd_insert] idx {} >= lane_count {}", idx, lane_count)); @@ -159,7 +159,7 @@ pub(super) fn codegen_simd_intrinsic_call<'tcx>( return; }; - let idx = idx_const.val.try_to_bits(Size::from_bytes(4 /* u32*/)).unwrap_or_else(|| panic!("kind not scalar: {:?}", idx_const)); + let idx = idx_const.try_to_bits(Size::from_bytes(4 /* u32*/)).unwrap_or_else(|| panic!("kind not scalar: {:?}", idx_const)); let (lane_count, _lane_ty) = v.layout().ty.simd_size_and_type(fx.tcx); if idx >= lane_count.into() { fx.tcx.sess.span_fatal(fx.mir.span, &format!("[simd_extract] idx {} >= lane_count {}", idx, lane_count)); @@ -276,5 +276,8 @@ pub(super) fn codegen_simd_intrinsic_call<'tcx>( // simd_bitmask // simd_select // simd_rem + // simd_neg + // simd_trunc + // simd_floor } } diff --git a/compiler/rustc_codegen_cranelift/src/lib.rs b/compiler/rustc_codegen_cranelift/src/lib.rs index 1480ab2513378..720d2a1253445 100644 --- a/compiler/rustc_codegen_cranelift/src/lib.rs +++ b/compiler/rustc_codegen_cranelift/src/lib.rs @@ -1,18 +1,8 @@ -#![feature( - rustc_private, - decl_macro, - type_alias_impl_trait, - associated_type_bounds, - never_type, - try_blocks, - hash_drain_filter -)] +#![feature(rustc_private, decl_macro, never_type, hash_drain_filter)] #![warn(rust_2018_idioms)] #![warn(unused_lifetimes)] #![warn(unreachable_pub)] -#[cfg(feature = "jit")] -extern crate libc; extern crate snap; #[macro_use] extern crate rustc_middle; @@ -53,12 +43,12 @@ mod abi; mod allocator; mod analyze; mod archive; -mod atomic_shim; mod backend; mod base; mod cast; mod codegen_i128; mod common; +mod compiler_builtins; mod constant; mod debuginfo; mod discriminant; @@ -129,9 +119,9 @@ impl String> Drop for PrintOnPanic { } } -struct CodegenCx<'tcx, M: Module> { +struct CodegenCx<'m, 'tcx: 'm> { tcx: TyCtxt<'tcx>, - module: M, + module: &'m mut dyn Module, global_asm: String, constants_cx: ConstantCx, cached_context: Context, @@ -140,14 +130,20 @@ struct CodegenCx<'tcx, M: Module> { unwind_context: UnwindContext<'tcx>, } -impl<'tcx, M: Module> CodegenCx<'tcx, M> { - fn new(tcx: TyCtxt<'tcx>, module: M, debug_info: bool, pic_eh_frame: bool) -> Self { - let unwind_context = UnwindContext::new(tcx, module.isa(), pic_eh_frame); - let debug_context = if debug_info { - Some(DebugContext::new(tcx, module.isa())) - } else { - None - }; +impl<'m, 'tcx> CodegenCx<'m, 'tcx> { + fn new( + tcx: TyCtxt<'tcx>, + backend_config: BackendConfig, + module: &'m mut dyn Module, + debug_info: bool, + ) -> Self { + let unwind_context = UnwindContext::new( + tcx, + module.isa(), + matches!(backend_config.codegen_mode, CodegenMode::Aot), + ); + let debug_context = + if debug_info { Some(DebugContext::new(tcx, module.isa())) } else { None }; CodegenCx { tcx, module, @@ -160,14 +156,9 @@ impl<'tcx, M: Module> CodegenCx<'tcx, M> { } } - fn finalize(mut self) -> (M, String, Option>, UnwindContext<'tcx>) { - self.constants_cx.finalize(self.tcx, &mut self.module); - ( - self.module, - self.global_asm, - self.debug_context, - self.unwind_context, - ) + fn finalize(self) -> (String, Option>, UnwindContext<'tcx>) { + self.constants_cx.finalize(self.tcx, self.module); + (self.global_asm, self.debug_context, self.unwind_context) } } @@ -225,8 +216,10 @@ pub struct CraneliftCodegenBackend { impl CodegenBackend for CraneliftCodegenBackend { fn init(&self, sess: &Session) { - if sess.lto() != rustc_session::config::Lto::No && sess.opts.cg.embed_bitcode { - sess.warn("LTO is not supported. You may get a linker error."); + use rustc_session::config::Lto; + match sess.lto() { + Lto::No | Lto::ThinLocal => {} + Lto::Thin | Lto::Fat => sess.warn("LTO is not supported. You may get a linker error."), } } @@ -241,9 +234,9 @@ impl CodegenBackend for CraneliftCodegenBackend { vec![] } - fn codegen_crate<'tcx>( + fn codegen_crate( &self, - tcx: TyCtxt<'tcx>, + tcx: TyCtxt<'_>, metadata: EncodedMetadata, need_metadata_module: bool, ) -> Box { @@ -253,9 +246,7 @@ impl CodegenBackend for CraneliftCodegenBackend { BackendConfig::from_opts(&tcx.sess.opts.cg.llvm_args) .unwrap_or_else(|err| tcx.sess.fatal(&err)) }; - let res = driver::codegen_crate(tcx, metadata, need_metadata_module, config); - - res + driver::codegen_crate(tcx, metadata, need_metadata_module, config) } fn join_codegen( @@ -301,16 +292,9 @@ fn build_isa(sess: &Session) -> Box { let mut flags_builder = settings::builder(); flags_builder.enable("is_pic").unwrap(); flags_builder.set("enable_probestack", "false").unwrap(); // __cranelift_probestack is not provided - flags_builder - .set( - "enable_verifier", - if cfg!(debug_assertions) { - "true" - } else { - "false" - }, - ) - .unwrap(); + let enable_verifier = + cfg!(debug_assertions) || std::env::var("CG_CLIF_ENABLE_VERIFIER").is_ok(); + flags_builder.set("enable_verifier", if enable_verifier { "true" } else { "false" }).unwrap(); let tls_model = match target_triple.binary_format { BinaryFormat::Elf => "elf_gd", @@ -322,27 +306,22 @@ fn build_isa(sess: &Session) -> Box { flags_builder.set("enable_simd", "true").unwrap(); + flags_builder.set("enable_llvm_abi_extensions", "true").unwrap(); + use rustc_session::config::OptLevel; match sess.opts.optimize { OptLevel::No => { flags_builder.set("opt_level", "none").unwrap(); } OptLevel::Less | OptLevel::Default => {} - OptLevel::Aggressive => { + OptLevel::Size | OptLevel::SizeMin | OptLevel::Aggressive => { flags_builder.set("opt_level", "speed_and_size").unwrap(); } - OptLevel::Size | OptLevel::SizeMin => { - sess.warn("Optimizing for size is not supported. Just ignoring the request"); - } } let flags = settings::Flags::new(flags_builder); - let variant = if cfg!(feature = "oldbe") { - cranelift_codegen::isa::BackendVariant::Legacy - } else { - cranelift_codegen::isa::BackendVariant::MachInst - }; + let variant = cranelift_codegen::isa::BackendVariant::MachInst; let mut isa_builder = cranelift_codegen::isa::lookup_variant(target_triple, variant).unwrap(); // Don't use "haswell", as it implies `has_lzcnt`.macOS CI is still at Ivy Bridge EP, so `lzcnt` // is interpreted as `bsr`. diff --git a/compiler/rustc_codegen_cranelift/src/linkage.rs b/compiler/rustc_codegen_cranelift/src/linkage.rs index dc1e2107ce712..a564a59f72510 100644 --- a/compiler/rustc_codegen_cranelift/src/linkage.rs +++ b/compiler/rustc_codegen_cranelift/src/linkage.rs @@ -6,8 +6,10 @@ pub(crate) fn get_clif_linkage( mono_item: MonoItem<'_>, linkage: RLinkage, visibility: Visibility, + is_compiler_builtins: bool, ) -> Linkage { match (linkage, visibility) { + (RLinkage::External, Visibility::Default) if is_compiler_builtins => Linkage::Hidden, (RLinkage::External, Visibility::Default) => Linkage::Export, (RLinkage::Internal, Visibility::Default) => Linkage::Local, (RLinkage::External, Visibility::Hidden) => Linkage::Hidden, diff --git a/compiler/rustc_codegen_cranelift/src/main_shim.rs b/compiler/rustc_codegen_cranelift/src/main_shim.rs index b193cea877dad..a6266f507765f 100644 --- a/compiler/rustc_codegen_cranelift/src/main_shim.rs +++ b/compiler/rustc_codegen_cranelift/src/main_shim.rs @@ -1,3 +1,4 @@ +use cranelift_codegen::binemit::{NullStackMapSink, NullTrapSink}; use rustc_hir::LangItem; use rustc_session::config::EntryFnType; @@ -9,7 +10,6 @@ pub(crate) fn maybe_create_entry_wrapper( tcx: TyCtxt<'_>, module: &mut impl Module, unwind_context: &mut UnwindContext<'_>, - use_jit: bool, ) { let (main_def_id, use_start_lang_item) = match tcx.entry_fn(LOCAL_CRATE) { Some((def_id, entry_ty)) => ( @@ -27,14 +27,7 @@ pub(crate) fn maybe_create_entry_wrapper( return; } - create_entry_fn( - tcx, - module, - unwind_context, - main_def_id, - use_start_lang_item, - use_jit, - ); + create_entry_fn(tcx, module, unwind_context, main_def_id, use_start_lang_item); fn create_entry_fn( tcx: TyCtxt<'_>, @@ -42,7 +35,6 @@ pub(crate) fn maybe_create_entry_wrapper( unwind_context: &mut UnwindContext<'_>, rust_main_def_id: DefId, use_start_lang_item: bool, - use_jit: bool, ) { let main_ret_ty = tcx.fn_sig(rust_main_def_id).output(); // Given that `main()` has no arguments, @@ -57,23 +49,17 @@ pub(crate) fn maybe_create_entry_wrapper( AbiParam::new(m.target_config().pointer_type()), AbiParam::new(m.target_config().pointer_type()), ], - returns: vec![AbiParam::new( - m.target_config().pointer_type(), /*isize*/ - )], + returns: vec![AbiParam::new(m.target_config().pointer_type() /*isize*/)], call_conv: CallConv::triple_default(m.isa().triple()), }; - let cmain_func_id = m - .declare_function("main", Linkage::Export, &cmain_sig) - .unwrap(); + let cmain_func_id = m.declare_function("main", Linkage::Export, &cmain_sig).unwrap(); let instance = Instance::mono(tcx, rust_main_def_id).polymorphize(tcx); let main_name = tcx.symbol_name(instance).name.to_string(); let main_sig = get_function_sig(tcx, m.isa().triple(), instance); - let main_func_id = m - .declare_function(&main_name, Linkage::Import, &main_sig) - .unwrap(); + let main_func_id = m.declare_function(&main_name, Linkage::Import, &main_sig).unwrap(); let mut ctx = Context::new(); ctx.func = Function::with_name_signature(ExternalName::user(0, 0), cmain_sig); @@ -86,8 +72,6 @@ pub(crate) fn maybe_create_entry_wrapper( let arg_argc = bcx.append_block_param(block, m.target_config().pointer_type()); let arg_argv = bcx.append_block_param(block, m.target_config().pointer_type()); - crate::atomic_shim::init_global_lock(m, &mut bcx, use_jit); - let main_func_ref = m.declare_func_in_func(main_func_id, &mut bcx.func); let call_inst = if use_start_lang_item { @@ -103,9 +87,7 @@ pub(crate) fn maybe_create_entry_wrapper( .polymorphize(tcx); let start_func_id = import_function(tcx, m, start_instance); - let main_val = bcx - .ins() - .func_addr(m.target_config().pointer_type(), main_func_ref); + let main_val = bcx.ins().func_addr(m.target_config().pointer_type(), main_func_ref); let func_ref = m.declare_func_in_func(start_func_id, &mut bcx.func); bcx.ins().call(func_ref, &[main_val, arg_argc, arg_argv]) @@ -119,12 +101,8 @@ pub(crate) fn maybe_create_entry_wrapper( bcx.seal_all_blocks(); bcx.finalize(); } - m.define_function( - cmain_func_id, - &mut ctx, - &mut cranelift_codegen::binemit::NullTrapSink {}, - ) - .unwrap(); + m.define_function(cmain_func_id, &mut ctx, &mut NullTrapSink {}, &mut NullStackMapSink {}) + .unwrap(); unwind_context.add_function(cmain_func_id, &ctx, m.isa()); } } diff --git a/compiler/rustc_codegen_cranelift/src/metadata.rs b/compiler/rustc_codegen_cranelift/src/metadata.rs index 2e3b9fb8364e4..dbdc8cbad44c4 100644 --- a/compiler/rustc_codegen_cranelift/src/metadata.rs +++ b/compiler/rustc_codegen_cranelift/src/metadata.rs @@ -1,10 +1,10 @@ //! Reading and writing of the rustc metadata for rlibs and dylibs -use std::convert::TryFrom; use std::fs::File; use std::path::Path; use rustc_codegen_ssa::METADATA_FILENAME; +use rustc_data_structures::memmap::Mmap; use rustc_data_structures::owning_ref::OwningRef; use rustc_data_structures::rustc_erase_owner; use rustc_data_structures::sync::MetadataRef; @@ -17,38 +17,43 @@ use crate::backend::WriteMetadata; pub(crate) struct CraneliftMetadataLoader; +fn load_metadata_with( + path: &Path, + f: impl for<'a> FnOnce(&'a [u8]) -> Result<&'a [u8], String>, +) -> Result { + let file = File::open(path).map_err(|e| format!("{:?}", e))?; + let data = unsafe { Mmap::map(file) }.map_err(|e| format!("{:?}", e))?; + let metadata = OwningRef::new(data).try_map(f)?; + return Ok(rustc_erase_owner!(metadata.map_owner_box())); +} + impl MetadataLoader for CraneliftMetadataLoader { fn get_rlib_metadata(&self, _target: &Target, path: &Path) -> Result { - let mut archive = ar::Archive::new(File::open(path).map_err(|e| format!("{:?}", e))?); - // Iterate over all entries in the archive: - while let Some(entry_result) = archive.next_entry() { - let mut entry = entry_result.map_err(|e| format!("{:?}", e))?; - if entry.header().identifier() == METADATA_FILENAME.as_bytes() { - let mut buf = Vec::with_capacity( - usize::try_from(entry.header().size()) - .expect("Rlib metadata file too big to load into memory."), - ); - ::std::io::copy(&mut entry, &mut buf).map_err(|e| format!("{:?}", e))?; - let buf: OwningRef, [u8]> = OwningRef::new(buf); - return Ok(rustc_erase_owner!(buf.map_owner_box())); + load_metadata_with(path, |data| { + let archive = object::read::archive::ArchiveFile::parse(&*data) + .map_err(|e| format!("{:?}", e))?; + + for entry_result in archive.members() { + let entry = entry_result.map_err(|e| format!("{:?}", e))?; + if entry.name() == METADATA_FILENAME.as_bytes() { + return Ok(entry.data()); + } } - } - Err("couldn't find metadata entry".to_string()) + Err("couldn't find metadata entry".to_string()) + }) } fn get_dylib_metadata(&self, _target: &Target, path: &Path) -> Result { use object::{Object, ObjectSection}; - let file = std::fs::read(path).map_err(|e| format!("read:{:?}", e))?; - let file = object::File::parse(&file).map_err(|e| format!("parse: {:?}", e))?; - let buf = file - .section_by_name(".rustc") - .ok_or("no .rustc section")? - .data() - .map_err(|e| format!("failed to read .rustc section: {:?}", e))? - .to_owned(); - let buf: OwningRef, [u8]> = OwningRef::new(buf); - Ok(rustc_erase_owner!(buf.map_owner_box())) + + load_metadata_with(path, |data| { + let file = object::File::parse(&data).map_err(|e| format!("parse: {:?}", e))?; + file.section_by_name(".rustc") + .ok_or("no .rustc section")? + .data() + .map_err(|e| format!("failed to read .rustc section: {:?}", e)) + }) } } @@ -94,9 +99,7 @@ pub(crate) fn write_metadata( assert!(kind == MetadataKind::Compressed); let mut compressed = tcx.metadata_encoding_version(); - FrameEncoder::new(&mut compressed) - .write_all(&metadata.raw_data) - .unwrap(); + FrameEncoder::new(&mut compressed).write_all(&metadata.raw_data).unwrap(); product.add_rustc_section( rustc_middle::middle::exported_symbols::metadata_symbol_name(tcx), diff --git a/compiler/rustc_codegen_cranelift/src/num.rs b/compiler/rustc_codegen_cranelift/src/num.rs index d1d2b3b872a4b..2ebf30da2d8ba 100644 --- a/compiler/rustc_codegen_cranelift/src/num.rs +++ b/compiler/rustc_codegen_cranelift/src/num.rs @@ -41,7 +41,7 @@ pub(crate) fn bin_op_to_intcc(bin_op: BinOp, signed: bool) -> Option { } fn codegen_compare_bin_op<'tcx>( - fx: &mut FunctionCx<'_, 'tcx, impl Module>, + fx: &mut FunctionCx<'_, '_, 'tcx>, bin_op: BinOp, signed: bool, lhs: Value, @@ -54,7 +54,7 @@ fn codegen_compare_bin_op<'tcx>( } pub(crate) fn codegen_binop<'tcx>( - fx: &mut FunctionCx<'_, 'tcx, impl Module>, + fx: &mut FunctionCx<'_, '_, 'tcx>, bin_op: BinOp, in_lhs: CValue<'tcx>, in_rhs: CValue<'tcx>, @@ -93,17 +93,12 @@ pub(crate) fn codegen_binop<'tcx>( ty::Uint(_) | ty::Int(_) => crate::num::codegen_int_binop(fx, bin_op, in_lhs, in_rhs), ty::Float(_) => crate::num::codegen_float_binop(fx, bin_op, in_lhs, in_rhs), ty::RawPtr(..) | ty::FnPtr(..) => crate::num::codegen_ptr_binop(fx, bin_op, in_lhs, in_rhs), - _ => unreachable!( - "{:?}({:?}, {:?})", - bin_op, - in_lhs.layout().ty, - in_rhs.layout().ty - ), + _ => unreachable!("{:?}({:?}, {:?})", bin_op, in_lhs.layout().ty, in_rhs.layout().ty), } } pub(crate) fn codegen_bool_binop<'tcx>( - fx: &mut FunctionCx<'_, 'tcx, impl Module>, + fx: &mut FunctionCx<'_, '_, 'tcx>, bin_op: BinOp, in_lhs: CValue<'tcx>, in_rhs: CValue<'tcx>, @@ -124,7 +119,7 @@ pub(crate) fn codegen_bool_binop<'tcx>( } pub(crate) fn codegen_int_binop<'tcx>( - fx: &mut FunctionCx<'_, 'tcx, impl Module>, + fx: &mut FunctionCx<'_, '_, 'tcx>, bin_op: BinOp, in_lhs: CValue<'tcx>, in_rhs: CValue<'tcx>, @@ -171,13 +166,11 @@ pub(crate) fn codegen_int_binop<'tcx>( BinOp::Shl => { let lhs_ty = fx.bcx.func.dfg.value_type(lhs); let actual_shift = fx.bcx.ins().band_imm(rhs, i64::from(lhs_ty.bits() - 1)); - let actual_shift = clif_intcast(fx, actual_shift, types::I8, false); fx.bcx.ins().ishl(lhs, actual_shift) } BinOp::Shr => { let lhs_ty = fx.bcx.func.dfg.value_type(lhs); let actual_shift = fx.bcx.ins().band_imm(rhs, i64::from(lhs_ty.bits() - 1)); - let actual_shift = clif_intcast(fx, actual_shift, types::I8, false); if signed { fx.bcx.ins().sshr(lhs, actual_shift) } else { @@ -185,19 +178,14 @@ pub(crate) fn codegen_int_binop<'tcx>( } } // Compare binops handles by `codegen_binop`. - _ => unreachable!( - "{:?}({:?}, {:?})", - bin_op, - in_lhs.layout().ty, - in_rhs.layout().ty - ), + _ => unreachable!("{:?}({:?}, {:?})", bin_op, in_lhs.layout().ty, in_rhs.layout().ty), }; CValue::by_val(val, in_lhs.layout()) } pub(crate) fn codegen_checked_int_binop<'tcx>( - fx: &mut FunctionCx<'_, 'tcx, impl Module>, + fx: &mut FunctionCx<'_, '_, 'tcx>, bin_op: BinOp, in_lhs: CValue<'tcx>, in_rhs: CValue<'tcx>, @@ -268,9 +256,7 @@ pub(crate) fn codegen_checked_int_binop<'tcx>( let rhs = fx.bcx.ins().sextend(ty.double_width().unwrap(), rhs); let val = fx.bcx.ins().imul(lhs, rhs); let has_underflow = - fx.bcx - .ins() - .icmp_imm(IntCC::SignedLessThan, val, -(1 << (ty.bits() - 1))); + fx.bcx.ins().icmp_imm(IntCC::SignedLessThan, val, -(1 << (ty.bits() - 1))); let has_overflow = fx.bcx.ins().icmp_imm( IntCC::SignedGreaterThan, val, @@ -309,10 +295,7 @@ pub(crate) fn codegen_checked_int_binop<'tcx>( let val = fx.bcx.ins().ishl(lhs, actual_shift); let ty = fx.bcx.func.dfg.value_type(val); let max_shift = i64::from(ty.bits()) - 1; - let has_overflow = fx - .bcx - .ins() - .icmp_imm(IntCC::UnsignedGreaterThan, rhs, max_shift); + let has_overflow = fx.bcx.ins().icmp_imm(IntCC::UnsignedGreaterThan, rhs, max_shift); (val, has_overflow) } BinOp::Shr => { @@ -326,38 +309,20 @@ pub(crate) fn codegen_checked_int_binop<'tcx>( }; let ty = fx.bcx.func.dfg.value_type(val); let max_shift = i64::from(ty.bits()) - 1; - let has_overflow = fx - .bcx - .ins() - .icmp_imm(IntCC::UnsignedGreaterThan, rhs, max_shift); + let has_overflow = fx.bcx.ins().icmp_imm(IntCC::UnsignedGreaterThan, rhs, max_shift); (val, has_overflow) } - _ => bug!( - "binop {:?} on checked int/uint lhs: {:?} rhs: {:?}", - bin_op, - in_lhs, - in_rhs - ), + _ => bug!("binop {:?} on checked int/uint lhs: {:?} rhs: {:?}", bin_op, in_lhs, in_rhs), }; let has_overflow = fx.bcx.ins().bint(types::I8, has_overflow); - // FIXME directly write to result place instead - let out_place = CPlace::new_stack_slot( - fx, - fx.layout_of( - fx.tcx - .mk_tup([in_lhs.layout().ty, fx.tcx.types.bool].iter()), - ), - ); - let out_layout = out_place.layout(); - out_place.write_cvalue(fx, CValue::by_val_pair(res, has_overflow, out_layout)); - - out_place.to_cvalue(fx) + let out_layout = fx.layout_of(fx.tcx.mk_tup([in_lhs.layout().ty, fx.tcx.types.bool].iter())); + CValue::by_val_pair(res, has_overflow, out_layout) } pub(crate) fn codegen_float_binop<'tcx>( - fx: &mut FunctionCx<'_, 'tcx, impl Module>, + fx: &mut FunctionCx<'_, '_, 'tcx>, bin_op: BinOp, in_lhs: CValue<'tcx>, in_rhs: CValue<'tcx>, @@ -402,7 +367,7 @@ pub(crate) fn codegen_float_binop<'tcx>( } pub(crate) fn codegen_ptr_binop<'tcx>( - fx: &mut FunctionCx<'_, 'tcx, impl Module>, + fx: &mut FunctionCx<'_, '_, 'tcx>, bin_op: BinOp, in_lhs: CValue<'tcx>, in_rhs: CValue<'tcx>, @@ -420,7 +385,7 @@ pub(crate) fn codegen_ptr_binop<'tcx>( let lhs = in_lhs.load_scalar(fx); let rhs = in_rhs.load_scalar(fx); - return codegen_compare_bin_op(fx, bin_op, false, lhs, rhs); + codegen_compare_bin_op(fx, bin_op, false, lhs, rhs) } BinOp::Offset => { let pointee_ty = in_lhs.layout().ty.builtin_deref(true).unwrap().ty; @@ -429,10 +394,10 @@ pub(crate) fn codegen_ptr_binop<'tcx>( let ptr_diff = fx.bcx.ins().imul_imm(offset, pointee_size as i64); let base_val = base.load_scalar(fx); let res = fx.bcx.ins().iadd(base_val, ptr_diff); - return CValue::by_val(res, base.layout()); + CValue::by_val(res, base.layout()) } _ => unreachable!("{:?}({:?}, {:?})", bin_op, in_lhs, in_rhs), - }; + } } else { let (lhs_ptr, lhs_extra) = in_lhs.load_scalar_pair(fx); let (rhs_ptr, rhs_extra) = in_rhs.load_scalar_pair(fx); @@ -452,9 +417,7 @@ pub(crate) fn codegen_ptr_binop<'tcx>( let ptr_eq = fx.bcx.ins().icmp(IntCC::Equal, lhs_ptr, rhs_ptr); let ptr_cmp = - fx.bcx - .ins() - .icmp(bin_op_to_intcc(bin_op, false).unwrap(), lhs_ptr, rhs_ptr); + fx.bcx.ins().icmp(bin_op_to_intcc(bin_op, false).unwrap(), lhs_ptr, rhs_ptr); let extra_cmp = fx.bcx.ins().icmp( bin_op_to_intcc(bin_op, false).unwrap(), lhs_extra, @@ -466,9 +429,6 @@ pub(crate) fn codegen_ptr_binop<'tcx>( _ => panic!("bin_op {:?} on ptr", bin_op), }; - CValue::by_val( - fx.bcx.ins().bint(types::I8, res), - fx.layout_of(fx.tcx.types.bool), - ) + CValue::by_val(fx.bcx.ins().bint(types::I8, res), fx.layout_of(fx.tcx.types.bool)) } } diff --git a/compiler/rustc_codegen_cranelift/src/optimize/code_layout.rs b/compiler/rustc_codegen_cranelift/src/optimize/code_layout.rs index f02732014d1e3..ca9ff15ec10ff 100644 --- a/compiler/rustc_codegen_cranelift/src/optimize/code_layout.rs +++ b/compiler/rustc_codegen_cranelift/src/optimize/code_layout.rs @@ -15,10 +15,7 @@ pub(super) fn optimize_function(ctx: &mut Context, cold_blocks: &EntitySet>(); for &inst in &insts { ctx.func.layout.remove_inst(inst); @@ -28,10 +25,7 @@ pub(super) fn optimize_function(ctx: &mut Context, cold_blocks: &EntitySet( if tcx.sess.opts.optimize == rustc_session::config::OptLevel::No { return; // FIXME classify optimizations over opt levels } - self::stack2reg::optimize_function(ctx, clif_comments); + + // FIXME(#1142) stack2reg miscompiles lewton + if false { + self::stack2reg::optimize_function(ctx, clif_comments); + } + crate::pretty_clif::write_clif_file(tcx, "stack2reg", None, instance, &ctx, &*clif_comments); crate::base::verify_func(tcx, &*clif_comments, &ctx.func); } diff --git a/compiler/rustc_codegen_cranelift/src/optimize/peephole.rs b/compiler/rustc_codegen_cranelift/src/optimize/peephole.rs index a575ed8dc35f8..b95e2d72877d9 100644 --- a/compiler/rustc_codegen_cranelift/src/optimize/peephole.rs +++ b/compiler/rustc_codegen_cranelift/src/optimize/peephole.rs @@ -10,10 +10,7 @@ use cranelift_frontend::FunctionBuilder; pub(crate) fn maybe_unwrap_bint(bcx: &mut FunctionBuilder<'_>, arg: Value) -> Value { if let ValueDef::Result(arg_inst, 0) = bcx.func.dfg.value_def(arg) { match bcx.func.dfg[arg_inst] { - InstructionData::Unary { - opcode: Opcode::Bint, - arg, - } => arg, + InstructionData::Unary { opcode: Opcode::Bint, arg } => arg, _ => arg, } } else { @@ -54,12 +51,7 @@ pub(crate) fn make_branchable_value(bcx: &mut FunctionBuilder<'_>, arg: Value) - match bcx.func.dfg[arg_inst] { // This is the lowering of Rvalue::Not - InstructionData::Load { - opcode: Opcode::Load, - arg: ptr, - flags, - offset, - } => { + InstructionData::Load { opcode: Opcode::Load, arg: ptr, flags, offset } => { // Using `load.i8 + uextend.i32` would legalize to `uload8 + ireduce.i8 + // uextend.i32`. Just `uload8` is much faster. match bcx.func.dfg.ctrl_typevar(arg_inst) { @@ -95,20 +87,14 @@ pub(crate) fn maybe_known_branch_taken( }; match bcx.func.dfg[arg_inst] { - InstructionData::UnaryBool { - opcode: Opcode::Bconst, - imm, - } => { + InstructionData::UnaryBool { opcode: Opcode::Bconst, imm } => { if test_zero { Some(!imm) } else { Some(imm) } } - InstructionData::UnaryImm { - opcode: Opcode::Iconst, - imm, - } => { + InstructionData::UnaryImm { opcode: Opcode::Iconst, imm } => { if test_zero { Some(imm.bits() == 0) } else { diff --git a/compiler/rustc_codegen_cranelift/src/optimize/stack2reg.rs b/compiler/rustc_codegen_cranelift/src/optimize/stack2reg.rs index 3c939d5a58639..8bb02a3e55854 100644 --- a/compiler/rustc_codegen_cranelift/src/optimize/stack2reg.rs +++ b/compiler/rustc_codegen_cranelift/src/optimize/stack2reg.rs @@ -175,16 +175,13 @@ impl<'a> OptimizeContext<'a> { } } - OptimizeContext { - ctx, - stack_slot_usage_map, - } + OptimizeContext { ctx, stack_slot_usage_map } } } pub(super) fn optimize_function( ctx: &mut Context, - #[cfg_attr(not(debug_assertions), allow(unused_variables))] clif_comments: &mut crate::pretty_clif::CommentWriter, + clif_comments: &mut crate::pretty_clif::CommentWriter, ) { combine_stack_addr_with_load_store(&mut ctx.func); @@ -194,8 +191,7 @@ pub(super) fn optimize_function( remove_unused_stack_addr_and_stack_load(&mut opt_ctx); - #[cfg(debug_assertions)] - { + if clif_comments.enabled() { for (&OrdStackSlot(stack_slot), usage) in &opt_ctx.stack_slot_usage_map { clif_comments.add_comment(stack_slot, format!("used by: {:?}", usage)); } @@ -211,25 +207,27 @@ pub(super) fn optimize_function( for load in users.stack_load.clone().into_iter() { let potential_stores = users.potential_stores_for_load(&opt_ctx.ctx, load); - #[cfg(debug_assertions)] - for &store in &potential_stores { - clif_comments.add_comment( - load, - format!( - "Potential store -> load forwarding {} -> {} ({:?}, {:?})", - opt_ctx.ctx.func.dfg.display_inst(store, None), - opt_ctx.ctx.func.dfg.display_inst(load, None), - spatial_overlap(&opt_ctx.ctx.func, store, load), - temporal_order(&opt_ctx.ctx, store, load), - ), - ); + if clif_comments.enabled() { + for &store in &potential_stores { + clif_comments.add_comment( + load, + format!( + "Potential store -> load forwarding {} -> {} ({:?}, {:?})", + opt_ctx.ctx.func.dfg.display_inst(store, None), + opt_ctx.ctx.func.dfg.display_inst(load, None), + spatial_overlap(&opt_ctx.ctx.func, store, load), + temporal_order(&opt_ctx.ctx, store, load), + ), + ); + } } match *potential_stores { [] => { - #[cfg(debug_assertions)] - clif_comments - .add_comment(load, "[BUG?] Reading uninitialized memory".to_string()); + if clif_comments.enabled() { + clif_comments + .add_comment(load, "[BUG?] Reading uninitialized memory".to_string()); + } } [store] if spatial_overlap(&opt_ctx.ctx.func, store, load) == SpatialOverlap::Full @@ -239,9 +237,12 @@ pub(super) fn optimize_function( // Only one store could have been the origin of the value. let stored_value = opt_ctx.ctx.func.dfg.inst_args(store)[0]; - #[cfg(debug_assertions)] - clif_comments - .add_comment(load, format!("Store to load forward {} -> {}", store, load)); + if clif_comments.enabled() { + clif_comments.add_comment( + load, + format!("Store to load forward {} -> {}", store, load), + ); + } users.change_load_to_alias(&mut opt_ctx.ctx.func, load, stored_value); } @@ -252,33 +253,35 @@ pub(super) fn optimize_function( for store in users.stack_store.clone().into_iter() { let potential_loads = users.potential_loads_of_store(&opt_ctx.ctx, store); - #[cfg(debug_assertions)] - for &load in &potential_loads { - clif_comments.add_comment( - store, - format!( - "Potential load from store {} <- {} ({:?}, {:?})", - opt_ctx.ctx.func.dfg.display_inst(load, None), - opt_ctx.ctx.func.dfg.display_inst(store, None), - spatial_overlap(&opt_ctx.ctx.func, store, load), - temporal_order(&opt_ctx.ctx, store, load), - ), - ); + if clif_comments.enabled() { + for &load in &potential_loads { + clif_comments.add_comment( + store, + format!( + "Potential load from store {} <- {} ({:?}, {:?})", + opt_ctx.ctx.func.dfg.display_inst(load, None), + opt_ctx.ctx.func.dfg.display_inst(store, None), + spatial_overlap(&opt_ctx.ctx.func, store, load), + temporal_order(&opt_ctx.ctx, store, load), + ), + ); + } } if potential_loads.is_empty() { // Never loaded; can safely remove all stores and the stack slot. // FIXME also remove stores when there is always a next store before a load. - #[cfg(debug_assertions)] - clif_comments.add_comment( - store, - format!( - "Remove dead stack store {} of {}", - opt_ctx.ctx.func.dfg.display_inst(store, None), - stack_slot.0 - ), - ); + if clif_comments.enabled() { + clif_comments.add_comment( + store, + format!( + "Remove dead stack store {} of {}", + opt_ctx.ctx.func.dfg.display_inst(store, None), + stack_slot.0 + ), + ); + } users.remove_dead_store(&mut opt_ctx.ctx.func, store); } @@ -296,12 +299,7 @@ fn combine_stack_addr_with_load_store(func: &mut Function) { while let Some(_block) = cursor.next_block() { while let Some(inst) = cursor.next_inst() { match cursor.func.dfg[inst] { - InstructionData::Load { - opcode: Opcode::Load, - arg: addr, - flags: _, - offset, - } => { + InstructionData::Load { opcode: Opcode::Load, arg: addr, flags: _, offset } => { if cursor.func.dfg.ctrl_typevar(inst) == types::I128 || cursor.func.dfg.ctrl_typevar(inst).is_vector() { @@ -391,20 +389,14 @@ fn remove_unused_stack_addr_and_stack_load(opt_ctx: &mut OptimizeContext<'_>) { stack_slot_users .stack_addr .drain_filter(|inst| { - stack_addr_load_insts_users - .get(inst) - .map(|users| users.is_empty()) - .unwrap_or(true) + stack_addr_load_insts_users.get(inst).map(|users| users.is_empty()).unwrap_or(true) }) .for_each(|inst| StackSlotUsage::remove_unused_stack_addr(&mut func, inst)); stack_slot_users .stack_load .drain_filter(|inst| { - stack_addr_load_insts_users - .get(inst) - .map(|users| users.is_empty()) - .unwrap_or(true) + stack_addr_load_insts_users.get(inst).map(|users| users.is_empty()).unwrap_or(true) }) .for_each(|inst| StackSlotUsage::remove_unused_load(&mut func, inst)); } @@ -415,11 +407,8 @@ fn try_get_stack_slot_and_offset_for_addr( addr: Value, ) -> Option<(StackSlot, Offset32)> { if let ValueDef::Result(addr_inst, 0) = func.dfg.value_def(addr) { - if let InstructionData::StackLoad { - opcode: Opcode::StackAddr, - stack_slot, - offset, - } = func.dfg[addr_inst] + if let InstructionData::StackLoad { opcode: Opcode::StackAddr, stack_slot, offset } = + func.dfg[addr_inst] { return Some((stack_slot, offset)); } @@ -437,16 +426,8 @@ enum SpatialOverlap { fn spatial_overlap(func: &Function, src: Inst, dest: Inst) -> SpatialOverlap { fn inst_info(func: &Function, inst: Inst) -> (StackSlot, Offset32, u32) { match func.dfg[inst] { - InstructionData::StackLoad { - opcode: Opcode::StackAddr, - stack_slot, - offset, - } - | InstructionData::StackLoad { - opcode: Opcode::StackLoad, - stack_slot, - offset, - } + InstructionData::StackLoad { opcode: Opcode::StackAddr, stack_slot, offset } + | InstructionData::StackLoad { opcode: Opcode::StackLoad, stack_slot, offset } | InstructionData::StackStore { opcode: Opcode::StackStore, stack_slot, @@ -471,10 +452,7 @@ fn spatial_overlap(func: &Function, src: Inst, dest: Inst) -> SpatialOverlap { } let src_end: i64 = src_offset.try_add_i64(i64::from(src_size)).unwrap().into(); - let dest_end: i64 = dest_offset - .try_add_i64(i64::from(dest_size)) - .unwrap() - .into(); + let dest_end: i64 = dest_offset.try_add_i64(i64::from(dest_size)).unwrap().into(); if src_end <= dest_offset.into() || dest_end <= src_offset.into() { return SpatialOverlap::No; } diff --git a/compiler/rustc_codegen_cranelift/src/pointer.rs b/compiler/rustc_codegen_cranelift/src/pointer.rs index b2036d7bcd452..31d827f83bfab 100644 --- a/compiler/rustc_codegen_cranelift/src/pointer.rs +++ b/compiler/rustc_codegen_cranelift/src/pointer.rs @@ -23,82 +23,48 @@ pub(crate) enum PointerBase { impl Pointer { pub(crate) fn new(addr: Value) -> Self { - Pointer { - base: PointerBase::Addr(addr), - offset: Offset32::new(0), - } + Pointer { base: PointerBase::Addr(addr), offset: Offset32::new(0) } } pub(crate) fn stack_slot(stack_slot: StackSlot) -> Self { - Pointer { - base: PointerBase::Stack(stack_slot), - offset: Offset32::new(0), - } + Pointer { base: PointerBase::Stack(stack_slot), offset: Offset32::new(0) } } - pub(crate) fn const_addr<'a, 'tcx>( - fx: &mut FunctionCx<'a, 'tcx, impl Module>, - addr: i64, - ) -> Self { + pub(crate) fn const_addr(fx: &mut FunctionCx<'_, '_, '_>, addr: i64) -> Self { let addr = fx.bcx.ins().iconst(fx.pointer_type, addr); - Pointer { - base: PointerBase::Addr(addr), - offset: Offset32::new(0), - } + Pointer { base: PointerBase::Addr(addr), offset: Offset32::new(0) } } pub(crate) fn dangling(align: Align) -> Self { - Pointer { - base: PointerBase::Dangling(align), - offset: Offset32::new(0), - } + Pointer { base: PointerBase::Dangling(align), offset: Offset32::new(0) } } - #[cfg(debug_assertions)] - pub(crate) fn base_and_offset(self) -> (PointerBase, Offset32) { + pub(crate) fn debug_base_and_offset(self) -> (PointerBase, Offset32) { (self.base, self.offset) } - pub(crate) fn get_addr<'a, 'tcx>(self, fx: &mut FunctionCx<'a, 'tcx, impl Module>) -> Value { + pub(crate) fn get_addr(self, fx: &mut FunctionCx<'_, '_, '_>) -> Value { match self.base { PointerBase::Addr(base_addr) => { let offset: i64 = self.offset.into(); - if offset == 0 { - base_addr - } else { - fx.bcx.ins().iadd_imm(base_addr, offset) - } + if offset == 0 { base_addr } else { fx.bcx.ins().iadd_imm(base_addr, offset) } } PointerBase::Stack(stack_slot) => { - fx.bcx - .ins() - .stack_addr(fx.pointer_type, stack_slot, self.offset) + fx.bcx.ins().stack_addr(fx.pointer_type, stack_slot, self.offset) + } + PointerBase::Dangling(align) => { + fx.bcx.ins().iconst(fx.pointer_type, i64::try_from(align.bytes()).unwrap()) } - PointerBase::Dangling(align) => fx - .bcx - .ins() - .iconst(fx.pointer_type, i64::try_from(align.bytes()).unwrap()), } } - pub(crate) fn offset<'a, 'tcx>( - self, - fx: &mut FunctionCx<'a, 'tcx, impl Module>, - extra_offset: Offset32, - ) -> Self { + pub(crate) fn offset(self, fx: &mut FunctionCx<'_, '_, '_>, extra_offset: Offset32) -> Self { self.offset_i64(fx, extra_offset.into()) } - pub(crate) fn offset_i64<'a, 'tcx>( - self, - fx: &mut FunctionCx<'a, 'tcx, impl Module>, - extra_offset: i64, - ) -> Self { + pub(crate) fn offset_i64(self, fx: &mut FunctionCx<'_, '_, '_>, extra_offset: i64) -> Self { if let Some(new_offset) = self.offset.try_add_i64(extra_offset) { - Pointer { - base: self.base, - offset: new_offset, - } + Pointer { base: self.base, offset: new_offset } } else { let base_offset: i64 = self.offset.into(); if let Some(new_offset) = base_offset.checked_add(extra_offset) { @@ -107,16 +73,12 @@ impl Pointer { PointerBase::Stack(stack_slot) => { fx.bcx.ins().stack_addr(fx.pointer_type, stack_slot, 0) } - PointerBase::Dangling(align) => fx - .bcx - .ins() - .iconst(fx.pointer_type, i64::try_from(align.bytes()).unwrap()), + PointerBase::Dangling(align) => { + fx.bcx.ins().iconst(fx.pointer_type, i64::try_from(align.bytes()).unwrap()) + } }; let addr = fx.bcx.ins().iadd_imm(base_addr, new_offset); - Pointer { - base: PointerBase::Addr(addr), - offset: Offset32::new(0), - } + Pointer { base: PointerBase::Addr(addr), offset: Offset32::new(0) } } else { panic!( "self.offset ({}) + extra_offset ({}) not representable in i64", @@ -126,31 +88,22 @@ impl Pointer { } } - pub(crate) fn offset_value<'a, 'tcx>( - self, - fx: &mut FunctionCx<'a, 'tcx, impl Module>, - extra_offset: Value, - ) -> Self { + pub(crate) fn offset_value(self, fx: &mut FunctionCx<'_, '_, '_>, extra_offset: Value) -> Self { match self.base { PointerBase::Addr(addr) => Pointer { base: PointerBase::Addr(fx.bcx.ins().iadd(addr, extra_offset)), offset: self.offset, }, PointerBase::Stack(stack_slot) => { - let base_addr = fx - .bcx - .ins() - .stack_addr(fx.pointer_type, stack_slot, self.offset); + let base_addr = fx.bcx.ins().stack_addr(fx.pointer_type, stack_slot, self.offset); Pointer { base: PointerBase::Addr(fx.bcx.ins().iadd(base_addr, extra_offset)), offset: Offset32::new(0), } } PointerBase::Dangling(align) => { - let addr = fx - .bcx - .ins() - .iconst(fx.pointer_type, i64::try_from(align.bytes()).unwrap()); + let addr = + fx.bcx.ins().iconst(fx.pointer_type, i64::try_from(align.bytes()).unwrap()); Pointer { base: PointerBase::Addr(fx.bcx.ins().iadd(addr, extra_offset)), offset: self.offset, @@ -159,46 +112,21 @@ impl Pointer { } } - pub(crate) fn load<'a, 'tcx>( - self, - fx: &mut FunctionCx<'a, 'tcx, impl Module>, - ty: Type, - flags: MemFlags, - ) -> Value { + pub(crate) fn load(self, fx: &mut FunctionCx<'_, '_, '_>, ty: Type, flags: MemFlags) -> Value { match self.base { PointerBase::Addr(base_addr) => fx.bcx.ins().load(ty, flags, base_addr, self.offset), - PointerBase::Stack(stack_slot) => { - if ty == types::I128 || ty.is_vector() { - // WORKAROUND for stack_load.i128 and stack_load.iXxY not being implemented - let base_addr = fx.bcx.ins().stack_addr(fx.pointer_type, stack_slot, 0); - fx.bcx.ins().load(ty, flags, base_addr, self.offset) - } else { - fx.bcx.ins().stack_load(ty, stack_slot, self.offset) - } - } + PointerBase::Stack(stack_slot) => fx.bcx.ins().stack_load(ty, stack_slot, self.offset), PointerBase::Dangling(_align) => unreachable!(), } } - pub(crate) fn store<'a, 'tcx>( - self, - fx: &mut FunctionCx<'a, 'tcx, impl Module>, - value: Value, - flags: MemFlags, - ) { + pub(crate) fn store(self, fx: &mut FunctionCx<'_, '_, '_>, value: Value, flags: MemFlags) { match self.base { PointerBase::Addr(base_addr) => { fx.bcx.ins().store(flags, value, base_addr, self.offset); } PointerBase::Stack(stack_slot) => { - let val_ty = fx.bcx.func.dfg.value_type(value); - if val_ty == types::I128 || val_ty.is_vector() { - // WORKAROUND for stack_store.i128 and stack_store.iXxY not being implemented - let base_addr = fx.bcx.ins().stack_addr(fx.pointer_type, stack_slot, 0); - fx.bcx.ins().store(flags, value, base_addr, self.offset); - } else { - fx.bcx.ins().stack_store(value, stack_slot, self.offset); - } + fx.bcx.ins().stack_store(value, stack_slot, self.offset); } PointerBase::Dangling(_align) => unreachable!(), } diff --git a/compiler/rustc_codegen_cranelift/src/pretty_clif.rs b/compiler/rustc_codegen_cranelift/src/pretty_clif.rs index f4a15ab12d511..d22ea3772eee7 100644 --- a/compiler/rustc_codegen_cranelift/src/pretty_clif.rs +++ b/compiler/rustc_codegen_cranelift/src/pretty_clif.rs @@ -69,36 +69,36 @@ use crate::prelude::*; #[derive(Debug)] pub(crate) struct CommentWriter { + enabled: bool, global_comments: Vec, entity_comments: FxHashMap, } impl CommentWriter { pub(crate) fn new<'tcx>(tcx: TyCtxt<'tcx>, instance: Instance<'tcx>) -> Self { - let global_comments = if cfg!(debug_assertions) { + let enabled = should_write_ir(tcx); + let global_comments = if enabled { vec![ format!("symbol {}", tcx.symbol_name(instance).name), format!("instance {:?}", instance), - format!( - "abi {:?}", - FnAbi::of_instance(&RevealAllLayoutCx(tcx), instance, &[]) - ), + format!("abi {:?}", FnAbi::of_instance(&RevealAllLayoutCx(tcx), instance, &[])), String::new(), ] } else { vec![] }; - CommentWriter { - global_comments, - entity_comments: FxHashMap::default(), - } + CommentWriter { enabled, global_comments, entity_comments: FxHashMap::default() } } } -#[cfg(debug_assertions)] impl CommentWriter { + pub(crate) fn enabled(&self) -> bool { + self.enabled + } + pub(crate) fn add_global_comment>(&mut self, comment: S) { + debug_assert!(self.enabled); self.global_comments.push(comment.into()); } @@ -107,6 +107,8 @@ impl CommentWriter { entity: E, comment: S, ) { + debug_assert!(self.enabled); + use std::collections::hash_map::Entry; match self.entity_comments.entry(entity.into()) { Entry::Occupied(mut occ) => { @@ -185,8 +187,7 @@ impl FuncWriter for &'_ CommentWriter { } } -#[cfg(debug_assertions)] -impl FunctionCx<'_, '_, M> { +impl FunctionCx<'_, '_, '_> { pub(crate) fn add_global_comment>(&mut self, comment: S) { self.clif_comments.add_global_comment(comment); } @@ -201,16 +202,11 @@ impl FunctionCx<'_, '_, M> { } pub(crate) fn should_write_ir(tcx: TyCtxt<'_>) -> bool { - cfg!(debug_assertions) - || tcx - .sess - .opts - .output_types - .contains_key(&OutputType::LlvmAssembly) + tcx.sess.opts.output_types.contains_key(&OutputType::LlvmAssembly) } -pub(crate) fn write_ir_file<'tcx>( - tcx: TyCtxt<'tcx>, +pub(crate) fn write_ir_file( + tcx: TyCtxt<'_>, name: &str, write: impl FnOnce(&mut dyn Write) -> std::io::Result<()>, ) { @@ -228,10 +224,7 @@ pub(crate) fn write_ir_file<'tcx>( let clif_file_name = clif_output_dir.join(name); - let res: std::io::Result<()> = try { - let mut file = std::fs::File::create(clif_file_name)?; - write(&mut file)?; - }; + let res = std::fs::File::create(clif_file_name).and_then(|mut file| write(&mut file)); if let Err(err) = res { tcx.sess.warn(&format!("error writing ir file: {}", err)); } @@ -245,40 +238,33 @@ pub(crate) fn write_clif_file<'tcx>( context: &cranelift_codegen::Context, mut clif_comments: &CommentWriter, ) { - write_ir_file( - tcx, - &format!("{}.{}.clif", tcx.symbol_name(instance).name, postfix), - |file| { - let value_ranges = isa.map(|isa| { - context - .build_value_labels_ranges(isa) - .expect("value location ranges") - }); + write_ir_file(tcx, &format!("{}.{}.clif", tcx.symbol_name(instance).name, postfix), |file| { + let value_ranges = + isa.map(|isa| context.build_value_labels_ranges(isa).expect("value location ranges")); - let mut clif = String::new(); - cranelift_codegen::write::decorate_function( - &mut clif_comments, - &mut clif, - &context.func, - &DisplayFunctionAnnotations { - isa: Some(&*crate::build_isa(tcx.sess)), - value_ranges: value_ranges.as_ref(), - }, - ) - .unwrap(); + let mut clif = String::new(); + cranelift_codegen::write::decorate_function( + &mut clif_comments, + &mut clif, + &context.func, + &DisplayFunctionAnnotations { + isa: Some(&*crate::build_isa(tcx.sess)), + value_ranges: value_ranges.as_ref(), + }, + ) + .unwrap(); - writeln!(file, "test compile")?; - writeln!(file, "set is_pic")?; - writeln!(file, "set enable_simd")?; - writeln!(file, "target {} haswell", crate::target_triple(tcx.sess))?; - writeln!(file)?; - file.write_all(clif.as_bytes())?; - Ok(()) - }, - ); + writeln!(file, "test compile")?; + writeln!(file, "set is_pic")?; + writeln!(file, "set enable_simd")?; + writeln!(file, "target {} haswell", crate::target_triple(tcx.sess))?; + writeln!(file)?; + file.write_all(clif.as_bytes())?; + Ok(()) + }); } -impl fmt::Debug for FunctionCx<'_, '_, M> { +impl fmt::Debug for FunctionCx<'_, '_, '_> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { writeln!(f, "{:?}", self.instance.substs)?; writeln!(f, "{:?}", self.local_map)?; diff --git a/compiler/rustc_codegen_cranelift/src/toolchain.rs b/compiler/rustc_codegen_cranelift/src/toolchain.rs index 735c59d70c120..484a9b699a0aa 100644 --- a/compiler/rustc_codegen_cranelift/src/toolchain.rs +++ b/compiler/rustc_codegen_cranelift/src/toolchain.rs @@ -71,12 +71,9 @@ fn linker_and_flavor(sess: &Session) -> (PathBuf, LinkerFlavor) { flavor, )), (Some(linker), None) => { - let stem = linker - .file_stem() - .and_then(|stem| stem.to_str()) - .unwrap_or_else(|| { - sess.fatal("couldn't extract file stem from specified linker") - }); + let stem = linker.file_stem().and_then(|stem| stem.to_str()).unwrap_or_else(|| { + sess.fatal("couldn't extract file stem from specified linker") + }); let flavor = if stem == "emcc" { LinkerFlavor::Em @@ -105,11 +102,7 @@ fn linker_and_flavor(sess: &Session) -> (PathBuf, LinkerFlavor) { // linker and linker flavor specified via command line have precedence over what the target // specification specifies - if let Some(ret) = infer_from( - sess, - sess.opts.cg.linker.clone(), - sess.opts.cg.linker_flavor, - ) { + if let Some(ret) = infer_from(sess, sess.opts.cg.linker.clone(), sess.opts.cg.linker_flavor) { return ret; } diff --git a/compiler/rustc_codegen_cranelift/src/trap.rs b/compiler/rustc_codegen_cranelift/src/trap.rs index 67495c7414840..1ab0703e981e7 100644 --- a/compiler/rustc_codegen_cranelift/src/trap.rs +++ b/compiler/rustc_codegen_cranelift/src/trap.rs @@ -2,7 +2,7 @@ use crate::prelude::*; -fn codegen_print(fx: &mut FunctionCx<'_, '_, impl Module>, msg: &str) { +fn codegen_print(fx: &mut FunctionCx<'_, '_, '_>, msg: &str) { let puts = fx .cx .module @@ -17,8 +17,7 @@ fn codegen_print(fx: &mut FunctionCx<'_, '_, impl Module>, msg: &str) { ) .unwrap(); let puts = fx.cx.module.declare_func_in_func(puts, &mut fx.bcx.func); - #[cfg(debug_assertions)] - { + if fx.clif_comments.enabled() { fx.add_comment(puts, "puts"); } @@ -29,7 +28,7 @@ fn codegen_print(fx: &mut FunctionCx<'_, '_, impl Module>, msg: &str) { } /// Trap code: user1 -pub(crate) fn trap_abort(fx: &mut FunctionCx<'_, '_, impl Module>, msg: impl AsRef) { +pub(crate) fn trap_abort(fx: &mut FunctionCx<'_, '_, '_>, msg: impl AsRef) { codegen_print(fx, msg.as_ref()); fx.bcx.ins().trap(TrapCode::User(1)); } @@ -38,7 +37,7 @@ pub(crate) fn trap_abort(fx: &mut FunctionCx<'_, '_, impl Module>, msg: impl AsR /// so you can **not** add instructions to it afterwards. /// /// Trap code: user65535 -pub(crate) fn trap_unreachable(fx: &mut FunctionCx<'_, '_, impl Module>, msg: impl AsRef) { +pub(crate) fn trap_unreachable(fx: &mut FunctionCx<'_, '_, '_>, msg: impl AsRef) { codegen_print(fx, msg.as_ref()); fx.bcx.ins().trap(TrapCode::UnreachableCodeReached); } @@ -47,7 +46,7 @@ pub(crate) fn trap_unreachable(fx: &mut FunctionCx<'_, '_, impl Module>, msg: im /// /// Trap code: user65535 pub(crate) fn trap_unreachable_ret_value<'tcx>( - fx: &mut FunctionCx<'_, 'tcx, impl Module>, + fx: &mut FunctionCx<'_, '_, 'tcx>, dest_layout: TyAndLayout<'tcx>, msg: impl AsRef, ) -> CValue<'tcx> { @@ -62,7 +61,7 @@ pub(crate) fn trap_unreachable_ret_value<'tcx>( /// to it afterwards. /// /// Trap code: user65535 -pub(crate) fn trap_unimplemented(fx: &mut FunctionCx<'_, '_, impl Module>, msg: impl AsRef) { +pub(crate) fn trap_unimplemented(fx: &mut FunctionCx<'_, '_, '_>, msg: impl AsRef) { codegen_print(fx, msg.as_ref()); let true_ = fx.bcx.ins().iconst(types::I32, 1); fx.bcx.ins().trapnz(true_, TrapCode::User(!0)); @@ -72,7 +71,7 @@ pub(crate) fn trap_unimplemented(fx: &mut FunctionCx<'_, '_, impl Module>, msg: /// /// Trap code: user65535 pub(crate) fn trap_unimplemented_ret_value<'tcx>( - fx: &mut FunctionCx<'_, 'tcx, impl Module>, + fx: &mut FunctionCx<'_, '_, 'tcx>, dest_layout: TyAndLayout<'tcx>, msg: impl AsRef, ) -> CValue<'tcx> { diff --git a/compiler/rustc_codegen_cranelift/src/unsize.rs b/compiler/rustc_codegen_cranelift/src/unsize.rs index c77ff5d56ba62..042583cd5720f 100644 --- a/compiler/rustc_codegen_cranelift/src/unsize.rs +++ b/compiler/rustc_codegen_cranelift/src/unsize.rs @@ -13,19 +13,18 @@ use crate::prelude::*; /// in an upcast, where the new vtable for an object will be derived /// from the old one. pub(crate) fn unsized_info<'tcx>( - fx: &mut FunctionCx<'_, 'tcx, impl Module>, + fx: &mut FunctionCx<'_, '_, 'tcx>, source: Ty<'tcx>, target: Ty<'tcx>, old_info: Option, ) -> Value { let (source, target) = - fx.tcx - .struct_lockstep_tails_erasing_lifetimes(source, target, ParamEnv::reveal_all()); + fx.tcx.struct_lockstep_tails_erasing_lifetimes(source, target, ParamEnv::reveal_all()); match (&source.kind(), &target.kind()) { - (&ty::Array(_, len), &ty::Slice(_)) => fx.bcx.ins().iconst( - fx.pointer_type, - len.eval_usize(fx.tcx, ParamEnv::reveal_all()) as i64, - ), + (&ty::Array(_, len), &ty::Slice(_)) => fx + .bcx + .ins() + .iconst(fx.pointer_type, len.eval_usize(fx.tcx, ParamEnv::reveal_all()) as i64), (&ty::Dynamic(..), &ty::Dynamic(..)) => { // For now, upcasts are limited to changes in marker // traits, and hence never actually require an actual @@ -35,17 +34,13 @@ pub(crate) fn unsized_info<'tcx>( (_, &ty::Dynamic(ref data, ..)) => { crate::vtable::get_vtable(fx, fx.layout_of(source), data.principal()) } - _ => bug!( - "unsized_info: invalid unsizing {:?} -> {:?}", - source, - target - ), + _ => bug!("unsized_info: invalid unsizing {:?} -> {:?}", source, target), } } /// Coerce `src` to `dst_ty`. `src_ty` must be a thin pointer. fn unsize_thin_ptr<'tcx>( - fx: &mut FunctionCx<'_, 'tcx, impl Module>, + fx: &mut FunctionCx<'_, '_, 'tcx>, src: Value, src_layout: TyAndLayout<'tcx>, dst_layout: TyAndLayout<'tcx>, @@ -89,24 +84,22 @@ fn unsize_thin_ptr<'tcx>( /// Coerce `src`, which is a reference to a value of type `src_ty`, /// to a value of type `dst_ty` and store the result in `dst` pub(crate) fn coerce_unsized_into<'tcx>( - fx: &mut FunctionCx<'_, 'tcx, impl Module>, + fx: &mut FunctionCx<'_, '_, 'tcx>, src: CValue<'tcx>, dst: CPlace<'tcx>, ) { let src_ty = src.layout().ty; let dst_ty = dst.layout().ty; let mut coerce_ptr = || { - let (base, info) = if fx - .layout_of(src.layout().ty.builtin_deref(true).unwrap().ty) - .is_unsized() - { - // fat-ptr to fat-ptr unsize preserves the vtable - // i.e., &'a fmt::Debug+Send => &'a fmt::Debug - src.load_scalar_pair(fx) - } else { - let base = src.load_scalar(fx); - unsize_thin_ptr(fx, base, src.layout(), dst.layout()) - }; + let (base, info) = + if fx.layout_of(src.layout().ty.builtin_deref(true).unwrap().ty).is_unsized() { + // fat-ptr to fat-ptr unsize preserves the vtable + // i.e., &'a fmt::Debug+Send => &'a fmt::Debug + src.load_scalar_pair(fx) + } else { + let base = src.load_scalar(fx); + unsize_thin_ptr(fx, base, src.layout(), dst.layout()) + }; dst.write_cvalue(fx, CValue::by_val_pair(base, info, dst.layout())); }; match (&src_ty.kind(), &dst_ty.kind()) { @@ -131,39 +124,26 @@ pub(crate) fn coerce_unsized_into<'tcx>( } } } - _ => bug!( - "coerce_unsized_into: invalid coercion {:?} -> {:?}", - src_ty, - dst_ty - ), + _ => bug!("coerce_unsized_into: invalid coercion {:?} -> {:?}", src_ty, dst_ty), } } // Adapted from https://github.com/rust-lang/rust/blob/2a663555ddf36f6b041445894a8c175cd1bc718c/src/librustc_codegen_ssa/glue.rs pub(crate) fn size_and_align_of_dst<'tcx>( - fx: &mut FunctionCx<'_, 'tcx, impl Module>, + fx: &mut FunctionCx<'_, '_, 'tcx>, layout: TyAndLayout<'tcx>, info: Value, ) -> (Value, Value) { if !layout.is_unsized() { - let size = fx - .bcx - .ins() - .iconst(fx.pointer_type, layout.size.bytes() as i64); - let align = fx - .bcx - .ins() - .iconst(fx.pointer_type, layout.align.abi.bytes() as i64); + let size = fx.bcx.ins().iconst(fx.pointer_type, layout.size.bytes() as i64); + let align = fx.bcx.ins().iconst(fx.pointer_type, layout.align.abi.bytes() as i64); return (size, align); } match layout.ty.kind() { ty::Dynamic(..) => { // load size/align from vtable - ( - crate::vtable::size_of_obj(fx, info), - crate::vtable::min_align_of_obj(fx, info), - ) + (crate::vtable::size_of_obj(fx, info), crate::vtable::min_align_of_obj(fx, info)) } ty::Slice(_) | ty::Str => { let unit = layout.field(fx, 0); @@ -171,9 +151,7 @@ pub(crate) fn size_and_align_of_dst<'tcx>( // times the unit size. ( fx.bcx.ins().imul_imm(info, unit.size.bytes() as i64), - fx.bcx - .ins() - .iconst(fx.pointer_type, unit.align.abi.bytes() as i64), + fx.bcx.ins().iconst(fx.pointer_type, unit.align.abi.bytes() as i64), ) } _ => { @@ -211,10 +189,7 @@ pub(crate) fn size_and_align_of_dst<'tcx>( // Choose max of two known alignments (combined value must // be aligned according to more restrictive of the two). - let cmp = fx - .bcx - .ins() - .icmp(IntCC::UnsignedGreaterThan, sized_align, unsized_align); + let cmp = fx.bcx.ins().icmp(IntCC::UnsignedGreaterThan, sized_align, unsized_align); let align = fx.bcx.ins().select(cmp, sized_align, unsized_align); // Issue #27023: must add any necessary padding to `size` diff --git a/compiler/rustc_codegen_cranelift/src/value_and_place.rs b/compiler/rustc_codegen_cranelift/src/value_and_place.rs index 765604e0f984e..b97d39009847a 100644 --- a/compiler/rustc_codegen_cranelift/src/value_and_place.rs +++ b/compiler/rustc_codegen_cranelift/src/value_and_place.rs @@ -2,11 +2,10 @@ use crate::prelude::*; -use cranelift_codegen::entity::EntityRef; use cranelift_codegen::ir::immediates::Offset32; fn codegen_field<'tcx>( - fx: &mut FunctionCx<'_, 'tcx, impl Module>, + fx: &mut FunctionCx<'_, '_, 'tcx>, base: Pointer, extra: Option, layout: TyAndLayout<'tcx>, @@ -15,11 +14,8 @@ fn codegen_field<'tcx>( let field_offset = layout.fields.offset(field.index()); let field_layout = layout.field(&*fx, field.index()); - let simple = |fx: &mut FunctionCx<'_, '_, _>| { - ( - base.offset_i64(fx, i64::try_from(field_offset.bytes()).unwrap()), - field_layout, - ) + let simple = |fx: &mut FunctionCx<'_, '_, '_>| { + (base.offset_i64(fx, i64::try_from(field_offset.bytes()).unwrap()), field_layout) }; if let Some(extra) = extra { @@ -58,10 +54,7 @@ fn scalar_pair_calculate_b_offset( a_scalar: &Scalar, b_scalar: &Scalar, ) -> Offset32 { - let b_offset = a_scalar - .value - .size(&tcx) - .align_to(b_scalar.value.align(&tcx).abi); + let b_offset = a_scalar.value.size(&tcx).align_to(b_scalar.value.align(&tcx).abi); Offset32::new(b_offset.bytes().try_into().unwrap()) } @@ -106,10 +99,7 @@ impl<'tcx> CValue<'tcx> { } // FIXME remove - pub(crate) fn force_stack( - self, - fx: &mut FunctionCx<'_, 'tcx, impl Module>, - ) -> (Pointer, Option) { + pub(crate) fn force_stack(self, fx: &mut FunctionCx<'_, '_, 'tcx>) -> (Pointer, Option) { let layout = self.1; match self.0 { CValueInner::ByRef(ptr, meta) => (ptr, meta), @@ -129,7 +119,7 @@ impl<'tcx> CValue<'tcx> { } /// Load a value with layout.abi of scalar - pub(crate) fn load_scalar(self, fx: &mut FunctionCx<'_, 'tcx, impl Module>) -> Value { + pub(crate) fn load_scalar(self, fx: &mut FunctionCx<'_, '_, 'tcx>) -> Value { let layout = self.1; match self.0 { CValueInner::ByRef(ptr, None) => { @@ -153,10 +143,7 @@ impl<'tcx> CValue<'tcx> { } /// Load a value pair with layout.abi of scalar pair - pub(crate) fn load_scalar_pair( - self, - fx: &mut FunctionCx<'_, 'tcx, impl Module>, - ) -> (Value, Value) { + pub(crate) fn load_scalar_pair(self, fx: &mut FunctionCx<'_, '_, 'tcx>) -> (Value, Value) { let layout = self.1; match self.0 { CValueInner::ByRef(ptr, None) => { @@ -183,7 +170,7 @@ impl<'tcx> CValue<'tcx> { pub(crate) fn value_field( self, - fx: &mut FunctionCx<'_, 'tcx, impl Module>, + fx: &mut FunctionCx<'_, '_, 'tcx>, field: mir::Field, ) -> CValue<'tcx> { let layout = self.1; @@ -219,21 +206,17 @@ impl<'tcx> CValue<'tcx> { } } - pub(crate) fn unsize_value( - self, - fx: &mut FunctionCx<'_, 'tcx, impl Module>, - dest: CPlace<'tcx>, - ) { + pub(crate) fn unsize_value(self, fx: &mut FunctionCx<'_, '_, 'tcx>, dest: CPlace<'tcx>) { crate::unsize::coerce_unsized_into(fx, self, dest); } /// If `ty` is signed, `const_val` must already be sign extended. pub(crate) fn const_val( - fx: &mut FunctionCx<'_, 'tcx, impl Module>, + fx: &mut FunctionCx<'_, '_, 'tcx>, layout: TyAndLayout<'tcx>, const_val: ty::ScalarInt, ) -> CValue<'tcx> { - assert_eq!(const_val.size(), layout.size); + assert_eq!(const_val.size(), layout.size, "{:#?}: {:?}", const_val, layout); use cranelift_codegen::ir::immediates::{Ieee32, Ieee64}; let clif_ty = fx.clif_type(layout.ty).unwrap(); @@ -250,18 +233,11 @@ impl<'tcx> CValue<'tcx> { ty::Uint(UintTy::U128) | ty::Int(IntTy::I128) => { let const_val = const_val.to_bits(layout.size).unwrap(); let lsb = fx.bcx.ins().iconst(types::I64, const_val as u64 as i64); - let msb = fx - .bcx - .ins() - .iconst(types::I64, (const_val >> 64) as u64 as i64); + let msb = fx.bcx.ins().iconst(types::I64, (const_val >> 64) as u64 as i64); fx.bcx.ins().iconcat(lsb, msb) } - ty::Bool | ty::Char | ty::Uint(_) | ty::Int(_) | ty::Ref(..) - | ty::RawPtr(..) => { - fx - .bcx - .ins() - .iconst(clif_ty, const_val.to_bits(layout.size).unwrap() as i64) + ty::Bool | ty::Char | ty::Uint(_) | ty::Int(_) | ty::Ref(..) | ty::RawPtr(..) => { + fx.bcx.ins().iconst(clif_ty, const_val.to_bits(layout.size).unwrap() as i64) } ty::Float(FloatTy::F32) => { fx.bcx.ins().f32const(Ieee32::with_bits(u32::try_from(const_val).unwrap())) @@ -279,14 +255,8 @@ impl<'tcx> CValue<'tcx> { } pub(crate) fn cast_pointer_to(self, layout: TyAndLayout<'tcx>) -> Self { - assert!(matches!( - self.layout().ty.kind(), - ty::Ref(..) | ty::RawPtr(..) | ty::FnPtr(..) - )); - assert!(matches!( - layout.ty.kind(), - ty::Ref(..) | ty::RawPtr(..) | ty::FnPtr(..) - )); + assert!(matches!(self.layout().ty.kind(), ty::Ref(..) | ty::RawPtr(..) | ty::FnPtr(..))); + assert!(matches!(layout.ty.kind(), ty::Ref(..) | ty::RawPtr(..) | ty::FnPtr(..))); assert_eq!(self.layout().abi, layout.abi); CValue(self.0, layout) } @@ -317,14 +287,11 @@ impl<'tcx> CPlace<'tcx> { } pub(crate) fn no_place(layout: TyAndLayout<'tcx>) -> CPlace<'tcx> { - CPlace { - inner: CPlaceInner::Addr(Pointer::dangling(layout.align.pref), None), - layout, - } + CPlace { inner: CPlaceInner::Addr(Pointer::dangling(layout.align.pref), None), layout } } pub(crate) fn new_stack_slot( - fx: &mut FunctionCx<'_, 'tcx, impl Module>, + fx: &mut FunctionCx<'_, '_, 'tcx>, layout: TyAndLayout<'tcx>, ) -> CPlace<'tcx> { assert!(!layout.is_unsized()); @@ -339,28 +306,22 @@ impl<'tcx> CPlace<'tcx> { size: (u32::try_from(layout.size.bytes()).unwrap() + 15) / 16 * 16, offset: None, }); - CPlace { - inner: CPlaceInner::Addr(Pointer::stack_slot(stack_slot), None), - layout, - } + CPlace { inner: CPlaceInner::Addr(Pointer::stack_slot(stack_slot), None), layout } } pub(crate) fn new_var( - fx: &mut FunctionCx<'_, 'tcx, impl Module>, + fx: &mut FunctionCx<'_, '_, 'tcx>, local: Local, layout: TyAndLayout<'tcx>, ) -> CPlace<'tcx> { let var = Variable::with_u32(fx.next_ssa_var); fx.next_ssa_var += 1; fx.bcx.declare_var(var, fx.clif_type(layout.ty).unwrap()); - CPlace { - inner: CPlaceInner::Var(local, var), - layout, - } + CPlace { inner: CPlaceInner::Var(local, var), layout } } pub(crate) fn new_var_pair( - fx: &mut FunctionCx<'_, 'tcx, impl Module>, + fx: &mut FunctionCx<'_, '_, 'tcx>, local: Local, layout: TyAndLayout<'tcx>, ) -> CPlace<'tcx> { @@ -372,17 +333,11 @@ impl<'tcx> CPlace<'tcx> { let (ty1, ty2) = fx.clif_pair_type(layout.ty).unwrap(); fx.bcx.declare_var(var1, ty1); fx.bcx.declare_var(var2, ty2); - CPlace { - inner: CPlaceInner::VarPair(local, var1, var2), - layout, - } + CPlace { inner: CPlaceInner::VarPair(local, var1, var2), layout } } pub(crate) fn for_ptr(ptr: Pointer, layout: TyAndLayout<'tcx>) -> CPlace<'tcx> { - CPlace { - inner: CPlaceInner::Addr(ptr, None), - layout, - } + CPlace { inner: CPlaceInner::Addr(ptr, None), layout } } pub(crate) fn for_ptr_with_extra( @@ -390,34 +345,27 @@ impl<'tcx> CPlace<'tcx> { extra: Value, layout: TyAndLayout<'tcx>, ) -> CPlace<'tcx> { - CPlace { - inner: CPlaceInner::Addr(ptr, Some(extra)), - layout, - } + CPlace { inner: CPlaceInner::Addr(ptr, Some(extra)), layout } } - pub(crate) fn to_cvalue(self, fx: &mut FunctionCx<'_, 'tcx, impl Module>) -> CValue<'tcx> { + pub(crate) fn to_cvalue(self, fx: &mut FunctionCx<'_, '_, 'tcx>) -> CValue<'tcx> { let layout = self.layout(); match self.inner { CPlaceInner::Var(_local, var) => { let val = fx.bcx.use_var(var); - fx.bcx - .set_val_label(val, cranelift_codegen::ir::ValueLabel::new(var.index())); + //fx.bcx.set_val_label(val, cranelift_codegen::ir::ValueLabel::new(var.index())); CValue::by_val(val, layout) } CPlaceInner::VarPair(_local, var1, var2) => { let val1 = fx.bcx.use_var(var1); - fx.bcx - .set_val_label(val1, cranelift_codegen::ir::ValueLabel::new(var1.index())); + //fx.bcx.set_val_label(val1, cranelift_codegen::ir::ValueLabel::new(var1.index())); let val2 = fx.bcx.use_var(var2); - fx.bcx - .set_val_label(val2, cranelift_codegen::ir::ValueLabel::new(var2.index())); + //fx.bcx.set_val_label(val2, cranelift_codegen::ir::ValueLabel::new(var2.index())); CValue::by_val_pair(val1, val2, layout) } CPlaceInner::VarLane(_local, var, lane) => { let val = fx.bcx.use_var(var); - fx.bcx - .set_val_label(val, cranelift_codegen::ir::ValueLabel::new(var.index())); + //fx.bcx.set_val_label(val, cranelift_codegen::ir::ValueLabel::new(var.index())); let val = fx.bcx.ins().extractlane(val, lane); CValue::by_val(val, layout) } @@ -447,11 +395,7 @@ impl<'tcx> CPlace<'tcx> { } } - pub(crate) fn write_cvalue( - self, - fx: &mut FunctionCx<'_, 'tcx, impl Module>, - from: CValue<'tcx>, - ) { + pub(crate) fn write_cvalue(self, fx: &mut FunctionCx<'_, '_, 'tcx>, from: CValue<'tcx>) { assert_assignable(fx, from.layout().ty, self.layout().ty); self.write_cvalue_maybe_transmute(fx, from, "write_cvalue"); @@ -459,7 +403,7 @@ impl<'tcx> CPlace<'tcx> { pub(crate) fn write_cvalue_transmute( self, - fx: &mut FunctionCx<'_, 'tcx, impl Module>, + fx: &mut FunctionCx<'_, '_, 'tcx>, from: CValue<'tcx>, ) { self.write_cvalue_maybe_transmute(fx, from, "write_cvalue_transmute"); @@ -467,12 +411,12 @@ impl<'tcx> CPlace<'tcx> { fn write_cvalue_maybe_transmute( self, - fx: &mut FunctionCx<'_, 'tcx, impl Module>, + fx: &mut FunctionCx<'_, '_, 'tcx>, from: CValue<'tcx>, - #[cfg_attr(not(debug_assertions), allow(unused_variables))] method: &'static str, + method: &'static str, ) { fn transmute_value<'tcx>( - fx: &mut FunctionCx<'_, 'tcx, impl Module>, + fx: &mut FunctionCx<'_, '_, 'tcx>, var: Variable, data: Value, dst_ty: Type, @@ -511,15 +455,13 @@ impl<'tcx> CPlace<'tcx> { } _ => unreachable!("write_cvalue_transmute: {:?} -> {:?}", src_ty, dst_ty), }; - fx.bcx - .set_val_label(data, cranelift_codegen::ir::ValueLabel::new(var.index())); + //fx.bcx.set_val_label(data, cranelift_codegen::ir::ValueLabel::new(var.index())); fx.bcx.def_var(var, data); } assert_eq!(self.layout().size, from.layout().size); - #[cfg(debug_assertions)] - { + if fx.clif_comments.enabled() { use cranelift_codegen::cursor::{Cursor, CursorPosition}; let cur_block = match fx.bcx.cursor().position() { CursorPosition::After(block) => block, @@ -558,15 +500,13 @@ impl<'tcx> CPlace<'tcx> { // First get the old vector let vector = fx.bcx.use_var(var); - fx.bcx - .set_val_label(vector, cranelift_codegen::ir::ValueLabel::new(var.index())); + //fx.bcx.set_val_label(vector, cranelift_codegen::ir::ValueLabel::new(var.index())); // Next insert the written lane into the vector let vector = fx.bcx.ins().insertlane(vector, data, lane); // Finally write the new vector - fx.bcx - .set_val_label(vector, cranelift_codegen::ir::ValueLabel::new(var.index())); + //fx.bcx.set_val_label(vector, cranelift_codegen::ir::ValueLabel::new(var.index())); fx.bcx.def_var(var, vector); return; @@ -604,10 +544,7 @@ impl<'tcx> CPlace<'tcx> { to_ptr.store(fx, val, flags); } CValueInner::ByValPair(_, _) => { - bug!( - "Non ScalarPair abi {:?} for ByValPair CValue", - dst_layout.abi - ); + bug!("Non ScalarPair abi {:?} for ByValPair CValue", dst_layout.abi); } CValueInner::ByRef(from_ptr, None) => { let from_addr = from_ptr.get_addr(fx); @@ -632,7 +569,7 @@ impl<'tcx> CPlace<'tcx> { pub(crate) fn place_field( self, - fx: &mut FunctionCx<'_, 'tcx, impl Module>, + fx: &mut FunctionCx<'_, '_, 'tcx>, field: mir::Field, ) -> CPlace<'tcx> { let layout = self.layout(); @@ -650,18 +587,8 @@ impl<'tcx> CPlace<'tcx> { let layout = layout.field(&*fx, field.index()); match field.as_u32() { - 0 => { - return CPlace { - inner: CPlaceInner::Var(local, var1), - layout, - } - } - 1 => { - return CPlace { - inner: CPlaceInner::Var(local, var2), - layout, - } - } + 0 => return CPlace { inner: CPlaceInner::Var(local, var1), layout }, + 1 => return CPlace { inner: CPlaceInner::Var(local, var2), layout }, _ => unreachable!("field should be 0 or 1"), } } @@ -680,7 +607,7 @@ impl<'tcx> CPlace<'tcx> { pub(crate) fn place_index( self, - fx: &mut FunctionCx<'_, 'tcx, impl Module>, + fx: &mut FunctionCx<'_, '_, 'tcx>, index: Value, ) -> CPlace<'tcx> { let (elem_layout, ptr) = match self.layout().ty.kind() { @@ -689,30 +616,24 @@ impl<'tcx> CPlace<'tcx> { _ => bug!("place_index({:?})", self.layout().ty), }; - let offset = fx - .bcx - .ins() - .imul_imm(index, elem_layout.size.bytes() as i64); + let offset = fx.bcx.ins().imul_imm(index, elem_layout.size.bytes() as i64); CPlace::for_ptr(ptr.offset_value(fx, offset), elem_layout) } - pub(crate) fn place_deref(self, fx: &mut FunctionCx<'_, 'tcx, impl Module>) -> CPlace<'tcx> { + pub(crate) fn place_deref(self, fx: &mut FunctionCx<'_, '_, 'tcx>) -> CPlace<'tcx> { let inner_layout = fx.layout_of(self.layout().ty.builtin_deref(true).unwrap().ty); if has_ptr_meta(fx.tcx, inner_layout.ty) { let (addr, extra) = self.to_cvalue(fx).load_scalar_pair(fx); CPlace::for_ptr_with_extra(Pointer::new(addr), extra, inner_layout) } else { - CPlace::for_ptr( - Pointer::new(self.to_cvalue(fx).load_scalar(fx)), - inner_layout, - ) + CPlace::for_ptr(Pointer::new(self.to_cvalue(fx).load_scalar(fx)), inner_layout) } } pub(crate) fn place_ref( self, - fx: &mut FunctionCx<'_, 'tcx, impl Module>, + fx: &mut FunctionCx<'_, '_, 'tcx>, layout: TyAndLayout<'tcx>, ) -> CValue<'tcx> { if has_ptr_meta(fx.tcx, self.layout().ty) { @@ -729,21 +650,18 @@ impl<'tcx> CPlace<'tcx> { pub(crate) fn downcast_variant( self, - fx: &FunctionCx<'_, 'tcx, impl Module>, + fx: &FunctionCx<'_, '_, 'tcx>, variant: VariantIdx, ) -> Self { assert!(!self.layout().is_unsized()); let layout = self.layout().for_variant(fx, variant); - CPlace { - inner: self.inner, - layout, - } + CPlace { inner: self.inner, layout } } } #[track_caller] pub(crate) fn assert_assignable<'tcx>( - fx: &FunctionCx<'_, 'tcx, impl Module>, + fx: &FunctionCx<'_, '_, 'tcx>, from_ty: Ty<'tcx>, to_ty: Ty<'tcx>, ) { @@ -776,12 +694,9 @@ pub(crate) fn assert_assignable<'tcx>( } (&ty::Dynamic(from_traits, _), &ty::Dynamic(to_traits, _)) => { for (from, to) in from_traits.iter().zip(to_traits) { - let from = fx - .tcx - .normalize_erasing_late_bound_regions(ParamEnv::reveal_all(), from); - let to = fx - .tcx - .normalize_erasing_late_bound_regions(ParamEnv::reveal_all(), to); + let from = + fx.tcx.normalize_erasing_late_bound_regions(ParamEnv::reveal_all(), from); + let to = fx.tcx.normalize_erasing_late_bound_regions(ParamEnv::reveal_all(), to); assert_eq!( from, to, "Can't write trait object of incompatible traits {:?} to place with traits {:?}\n\n{:#?}", @@ -790,6 +705,19 @@ pub(crate) fn assert_assignable<'tcx>( } // dyn for<'r> Trait<'r> -> dyn Trait<'_> is allowed } + (&ty::Adt(adt_def_a, substs_a), &ty::Adt(adt_def_b, substs_b)) + if adt_def_a.did == adt_def_b.did => + { + let mut types_a = substs_a.types(); + let mut types_b = substs_b.types(); + loop { + match (types_a.next(), types_b.next()) { + (Some(a), Some(b)) => assert_assignable(fx, a, b), + (None, None) => return, + (Some(_), None) | (None, Some(_)) => panic!("{:#?}/{:#?}", from_ty, to_ty), + } + } + } _ => { assert_eq!( from_ty, to_ty, diff --git a/compiler/rustc_codegen_cranelift/src/vtable.rs b/compiler/rustc_codegen_cranelift/src/vtable.rs index 8f15586a9dc06..9053d1aa1b05a 100644 --- a/compiler/rustc_codegen_cranelift/src/vtable.rs +++ b/compiler/rustc_codegen_cranelift/src/vtable.rs @@ -1,6 +1,6 @@ //! Codegen vtables and vtable accesses. //! -//! See librustc_codegen_llvm/meth.rs for reference +//! See `rustc_codegen_ssa/src/meth.rs` for reference. // FIXME dedup this logic between miri, cg_llvm and cg_clif use crate::prelude::*; @@ -15,7 +15,7 @@ fn vtable_memflags() -> MemFlags { flags } -pub(crate) fn drop_fn_of_obj(fx: &mut FunctionCx<'_, '_, impl Module>, vtable: Value) -> Value { +pub(crate) fn drop_fn_of_obj(fx: &mut FunctionCx<'_, '_, '_>, vtable: Value) -> Value { let usize_size = fx.layout_of(fx.tcx.types.usize).size.bytes() as usize; fx.bcx.ins().load( pointer_ty(fx.tcx), @@ -25,7 +25,7 @@ pub(crate) fn drop_fn_of_obj(fx: &mut FunctionCx<'_, '_, impl Module>, vtable: V ) } -pub(crate) fn size_of_obj(fx: &mut FunctionCx<'_, '_, impl Module>, vtable: Value) -> Value { +pub(crate) fn size_of_obj(fx: &mut FunctionCx<'_, '_, '_>, vtable: Value) -> Value { let usize_size = fx.layout_of(fx.tcx.types.usize).size.bytes() as usize; fx.bcx.ins().load( pointer_ty(fx.tcx), @@ -35,7 +35,7 @@ pub(crate) fn size_of_obj(fx: &mut FunctionCx<'_, '_, impl Module>, vtable: Valu ) } -pub(crate) fn min_align_of_obj(fx: &mut FunctionCx<'_, '_, impl Module>, vtable: Value) -> Value { +pub(crate) fn min_align_of_obj(fx: &mut FunctionCx<'_, '_, '_>, vtable: Value) -> Value { let usize_size = fx.layout_of(fx.tcx.types.usize).size.bytes() as usize; fx.bcx.ins().load( pointer_ty(fx.tcx), @@ -46,7 +46,7 @@ pub(crate) fn min_align_of_obj(fx: &mut FunctionCx<'_, '_, impl Module>, vtable: } pub(crate) fn get_ptr_and_method_ref<'tcx>( - fx: &mut FunctionCx<'_, 'tcx, impl Module>, + fx: &mut FunctionCx<'_, '_, 'tcx>, arg: CValue<'tcx>, idx: usize, ) -> (Value, Value) { @@ -68,7 +68,7 @@ pub(crate) fn get_ptr_and_method_ref<'tcx>( } pub(crate) fn get_vtable<'tcx>( - fx: &mut FunctionCx<'_, 'tcx, impl Module>, + fx: &mut FunctionCx<'_, '_, 'tcx>, layout: TyAndLayout<'tcx>, trait_ref: Option>, ) -> Value { @@ -85,7 +85,7 @@ pub(crate) fn get_vtable<'tcx>( } fn build_vtable<'tcx>( - fx: &mut FunctionCx<'_, 'tcx, impl Module>, + fx: &mut FunctionCx<'_, '_, 'tcx>, layout: TyAndLayout<'tcx>, trait_ref: Option>, ) -> DataId { @@ -94,7 +94,7 @@ fn build_vtable<'tcx>( let drop_in_place_fn = import_function( tcx, - &mut fx.cx.module, + fx.cx.module, Instance::resolve_drop_in_place(tcx, layout.ty).polymorphize(fx.tcx), ); @@ -111,7 +111,7 @@ fn build_vtable<'tcx>( opt_mth.map(|(def_id, substs)| { import_function( tcx, - &mut fx.cx.module, + fx.cx.module, Instance::resolve_for_vtable(tcx, ParamEnv::reveal_all(), def_id, substs) .unwrap() .polymorphize(fx.tcx), @@ -165,11 +165,8 @@ fn build_vtable<'tcx>( } fn write_usize(tcx: TyCtxt<'_>, buf: &mut [u8], idx: usize, num: u64) { - let pointer_size = tcx - .layout_of(ParamEnv::reveal_all().and(tcx.types.usize)) - .unwrap() - .size - .bytes() as usize; + let pointer_size = + tcx.layout_of(ParamEnv::reveal_all().and(tcx.types.usize)).unwrap().size.bytes() as usize; let target = &mut buf[idx * pointer_size..(idx + 1) * pointer_size]; match tcx.data_layout.endian { diff --git a/compiler/rustc_codegen_cranelift/test.sh b/compiler/rustc_codegen_cranelift/test.sh index 5ab10e0e905c7..e222adc7b8058 100755 --- a/compiler/rustc_codegen_cranelift/test.sh +++ b/compiler/rustc_codegen_cranelift/test.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash set -e ./build.sh --sysroot none "$@" diff --git a/compiler/rustc_codegen_llvm/src/abi.rs b/compiler/rustc_codegen_llvm/src/abi.rs index d9393ffe534ac..854e3ccc21b4f 100644 --- a/compiler/rustc_codegen_llvm/src/abi.rs +++ b/compiler/rustc_codegen_llvm/src/abi.rs @@ -1,6 +1,7 @@ use crate::builder::Builder; use crate::context::CodegenCx; use crate::llvm::{self, AttributePlace}; +use crate::llvm_util; use crate::type_::Type; use crate::type_of::LayoutLlvmExt; use crate::value::Value; @@ -41,12 +42,29 @@ impl ArgAttributeExt for ArgAttribute { } pub trait ArgAttributesExt { - fn apply_attrs_to_llfn(&self, idx: AttributePlace, llfn: &Value); - fn apply_attrs_to_callsite(&self, idx: AttributePlace, callsite: &Value); + fn apply_attrs_to_llfn(&self, idx: AttributePlace, cx: &CodegenCx<'_, '_>, llfn: &Value); + fn apply_attrs_to_callsite( + &self, + idx: AttributePlace, + cx: &CodegenCx<'_, '_>, + callsite: &Value, + ); +} + +fn should_use_mutable_noalias(cx: &CodegenCx<'_, '_>) -> bool { + // LLVM prior to version 12 has known miscompiles in the presence of + // noalias attributes (see #54878). Only enable mutable noalias by + // default for versions we believe to be safe. + cx.tcx + .sess + .opts + .debugging_opts + .mutable_noalias + .unwrap_or_else(|| llvm_util::get_version() >= (12, 0, 0)) } impl ArgAttributesExt for ArgAttributes { - fn apply_attrs_to_llfn(&self, idx: AttributePlace, llfn: &Value) { + fn apply_attrs_to_llfn(&self, idx: AttributePlace, cx: &CodegenCx<'_, '_>, llfn: &Value) { let mut regular = self.regular; unsafe { let deref = self.pointee_size.bytes(); @@ -62,6 +80,9 @@ impl ArgAttributesExt for ArgAttributes { llvm::LLVMRustAddAlignmentAttr(llfn, idx.as_uint(), align.bytes() as u32); } regular.for_each_kind(|attr| attr.apply_llfn(idx, llfn)); + if regular.contains(ArgAttribute::NoAliasMutRef) && should_use_mutable_noalias(cx) { + llvm::Attribute::NoAlias.apply_llfn(idx, llfn); + } match self.arg_ext { ArgExtension::None => {} ArgExtension::Zext => { @@ -74,7 +95,12 @@ impl ArgAttributesExt for ArgAttributes { } } - fn apply_attrs_to_callsite(&self, idx: AttributePlace, callsite: &Value) { + fn apply_attrs_to_callsite( + &self, + idx: AttributePlace, + cx: &CodegenCx<'_, '_>, + callsite: &Value, + ) { let mut regular = self.regular; unsafe { let deref = self.pointee_size.bytes(); @@ -98,6 +124,9 @@ impl ArgAttributesExt for ArgAttributes { ); } regular.for_each_kind(|attr| attr.apply_callsite(idx, callsite)); + if regular.contains(ArgAttribute::NoAliasMutRef) && should_use_mutable_noalias(cx) { + llvm::Attribute::NoAlias.apply_callsite(idx, callsite); + } match self.arg_ext { ArgExtension::None => {} ArgExtension::Zext => { @@ -419,13 +448,13 @@ impl<'tcx> FnAbiLlvmExt<'tcx> for FnAbi<'tcx, Ty<'tcx>> { let mut i = 0; let mut apply = |attrs: &ArgAttributes| { - attrs.apply_attrs_to_llfn(llvm::AttributePlace::Argument(i), llfn); + attrs.apply_attrs_to_llfn(llvm::AttributePlace::Argument(i), cx, llfn); i += 1; i - 1 }; match self.ret.mode { PassMode::Direct(ref attrs) => { - attrs.apply_attrs_to_llfn(llvm::AttributePlace::ReturnValue, llfn); + attrs.apply_attrs_to_llfn(llvm::AttributePlace::ReturnValue, cx, llfn); } PassMode::Indirect { ref attrs, extra_attrs: _, on_stack } => { assert!(!on_stack); @@ -480,18 +509,18 @@ impl<'tcx> FnAbiLlvmExt<'tcx> for FnAbi<'tcx, Ty<'tcx>> { // FIXME(wesleywiser, eddyb): We should apply `nounwind` and `noreturn` as appropriate to this callsite. let mut i = 0; - let mut apply = |attrs: &ArgAttributes| { - attrs.apply_attrs_to_callsite(llvm::AttributePlace::Argument(i), callsite); + let mut apply = |cx: &CodegenCx<'_, '_>, attrs: &ArgAttributes| { + attrs.apply_attrs_to_callsite(llvm::AttributePlace::Argument(i), cx, callsite); i += 1; i - 1 }; match self.ret.mode { PassMode::Direct(ref attrs) => { - attrs.apply_attrs_to_callsite(llvm::AttributePlace::ReturnValue, callsite); + attrs.apply_attrs_to_callsite(llvm::AttributePlace::ReturnValue, &bx.cx, callsite); } PassMode::Indirect { ref attrs, extra_attrs: _, on_stack } => { assert!(!on_stack); - let i = apply(attrs); + let i = apply(bx.cx, attrs); unsafe { llvm::LLVMRustAddStructRetCallSiteAttr( callsite, @@ -517,12 +546,12 @@ impl<'tcx> FnAbiLlvmExt<'tcx> for FnAbi<'tcx, Ty<'tcx>> { } for arg in &self.args { if arg.pad.is_some() { - apply(&ArgAttributes::new()); + apply(bx.cx, &ArgAttributes::new()); } match arg.mode { PassMode::Ignore => {} PassMode::Indirect { ref attrs, extra_attrs: None, on_stack: true } => { - let i = apply(attrs); + let i = apply(bx.cx, attrs); unsafe { llvm::LLVMRustAddByValCallSiteAttr( callsite, @@ -533,22 +562,22 @@ impl<'tcx> FnAbiLlvmExt<'tcx> for FnAbi<'tcx, Ty<'tcx>> { } PassMode::Direct(ref attrs) | PassMode::Indirect { ref attrs, extra_attrs: None, on_stack: false } => { - apply(attrs); + apply(bx.cx, attrs); } PassMode::Indirect { ref attrs, extra_attrs: Some(ref extra_attrs), on_stack: _, } => { - apply(attrs); - apply(extra_attrs); + apply(bx.cx, attrs); + apply(bx.cx, extra_attrs); } PassMode::Pair(ref a, ref b) => { - apply(a); - apply(b); + apply(bx.cx, a); + apply(bx.cx, b); } PassMode::Cast(_) => { - apply(&ArgAttributes::new()); + apply(bx.cx, &ArgAttributes::new()); } } } diff --git a/compiler/rustc_codegen_llvm/src/asm.rs b/compiler/rustc_codegen_llvm/src/asm.rs index 38c8ae711a4cb..84b091d8d4d79 100644 --- a/compiler/rustc_codegen_llvm/src/asm.rs +++ b/compiler/rustc_codegen_llvm/src/asm.rs @@ -14,7 +14,7 @@ use rustc_data_structures::fx::FxHashMap; use rustc_hir as hir; use rustc_middle::ty::layout::TyAndLayout; use rustc_middle::{bug, span_bug}; -use rustc_span::{Pos, Span}; +use rustc_span::{Pos, Span, Symbol}; use rustc_target::abi::*; use rustc_target::asm::*; @@ -125,15 +125,39 @@ impl AsmBuilderMethods<'tcx> for Builder<'a, 'll, 'tcx> { // Collect the types of output operands let mut constraints = vec![]; + let mut clobbers = vec![]; let mut output_types = vec![]; let mut op_idx = FxHashMap::default(); for (idx, op) in operands.iter().enumerate() { match *op { InlineAsmOperandRef::Out { reg, late, place } => { + let is_target_supported = |reg_class: InlineAsmRegClass| { + for &(_, feature) in reg_class.supported_types(asm_arch) { + if let Some(feature) = feature { + if self.tcx.sess.target_features.contains(&Symbol::intern(feature)) + { + return true; + } + } else { + // Register class is unconditionally supported + return true; + } + } + false + }; + let mut layout = None; let ty = if let Some(ref place) = place { layout = Some(&place.layout); llvm_fixup_output_type(self.cx, reg.reg_class(), &place.layout) + } else if !is_target_supported(reg.reg_class()) { + // We turn discarded outputs into clobber constraints + // if the target feature needed by the register class is + // disabled. This is necessary otherwise LLVM will try + // to actually allocate a register for the dummy output. + assert!(matches!(reg, InlineAsmRegOrRegClass::Reg(_))); + clobbers.push(format!("~{}", reg_to_llvm(reg, None))); + continue; } else { // If the output is discarded, we don't really care what // type is used. We're just using this to tell LLVM to @@ -244,6 +268,7 @@ impl AsmBuilderMethods<'tcx> for Builder<'a, 'll, 'tcx> { } } + constraints.append(&mut clobbers); if !options.contains(InlineAsmOptions::PRESERVES_FLAGS) { match asm_arch { InlineAsmArch::AArch64 | InlineAsmArch::Arm => { @@ -528,6 +553,7 @@ fn reg_to_llvm(reg: InlineAsmRegOrRegClass, layout: Option<&TyAndLayout<'tcx>>) InlineAsmRegClass::SpirV(SpirVInlineAsmRegClass::reg) => { bug!("LLVM backend does not support SPIR-V") } + InlineAsmRegClass::Err => unreachable!(), } .to_string(), } @@ -594,6 +620,7 @@ fn modifier_to_llvm( InlineAsmRegClass::SpirV(SpirVInlineAsmRegClass::reg) => { bug!("LLVM backend does not support SPIR-V") } + InlineAsmRegClass::Err => unreachable!(), } } @@ -637,6 +664,7 @@ fn dummy_output_type(cx: &CodegenCx<'ll, 'tcx>, reg: InlineAsmRegClass) -> &'ll InlineAsmRegClass::SpirV(SpirVInlineAsmRegClass::reg) => { bug!("LLVM backend does not support SPIR-V") } + InlineAsmRegClass::Err => unreachable!(), } } diff --git a/compiler/rustc_codegen_llvm/src/attributes.rs b/compiler/rustc_codegen_llvm/src/attributes.rs index 09ece6164ebd1..9e5e2b1039efe 100644 --- a/compiler/rustc_codegen_llvm/src/attributes.rs +++ b/compiler/rustc_codegen_llvm/src/attributes.rs @@ -11,9 +11,10 @@ use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags; use rustc_middle::ty::layout::HasTyCtxt; use rustc_middle::ty::query::Providers; use rustc_middle::ty::{self, TyCtxt}; -use rustc_session::config::{OptLevel, SanitizerSet}; +use rustc_session::config::OptLevel; use rustc_session::Session; -use rustc_target::spec::StackProbeType; +use rustc_target::spec::abi::Abi; +use rustc_target::spec::{SanitizerSet, StackProbeType}; use crate::attributes; use crate::llvm::AttributePlace::Function; @@ -152,18 +153,6 @@ fn set_probestack(cx: &CodegenCx<'ll, '_>, llfn: &'ll Value) { } } -pub fn llvm_target_features(sess: &Session) -> impl Iterator { - const RUSTC_SPECIFIC_FEATURES: &[&str] = &["crt-static"]; - - let cmdline = sess - .opts - .cg - .target_feature - .split(',') - .filter(|f| !RUSTC_SPECIFIC_FEATURES.iter().any(|s| f.contains(s))); - sess.target.features.split(',').chain(cmdline).filter(|l| !l.is_empty()) -} - pub fn apply_target_cpu_attr(cx: &CodegenCx<'ll, '_>, llfn: &'ll Value) { let target_cpu = SmallCStr::new(llvm_util::target_cpu(cx.tcx.sess)); llvm::AddFunctionAttrStringValue( @@ -266,6 +255,7 @@ pub fn from_fn_attrs(cx: &CodegenCx<'ll, 'tcx>, llfn: &'ll Value, instance: ty:: attributes::emit_uwtable(llfn, true); } + // FIXME: none of these three functions interact with source level attributes. set_frame_pointer_elimination(cx, llfn); set_instrument_function(cx, llfn); set_probestack(cx, llfn); @@ -291,6 +281,9 @@ pub fn from_fn_attrs(cx: &CodegenCx<'ll, 'tcx>, llfn: &'ll Value, instance: ty:: if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::CMSE_NONSECURE_ENTRY) { llvm::AddFunctionAttrString(llfn, Function, cstr!("cmse_nonsecure_entry")); } + if let Some(align) = codegen_fn_attrs.alignment { + llvm::set_alignment(llfn, align as usize); + } sanitize(cx, codegen_fn_attrs.no_sanitize, llfn); // Always annotate functions with the target-cpu they are compiled for. @@ -301,33 +294,22 @@ pub fn from_fn_attrs(cx: &CodegenCx<'ll, 'tcx>, llfn: &'ll Value, instance: ty:: // The target doesn't care; the subtarget reads our attribute. apply_tune_cpu_attr(cx, llfn); - let features = llvm_target_features(cx.tcx.sess) - .map(|s| s.to_string()) - .chain(codegen_fn_attrs.target_features.iter().map(|f| { + let mut function_features = codegen_fn_attrs + .target_features + .iter() + .map(|f| { let feature = &f.as_str(); format!("+{}", llvm_util::to_llvm_feature(cx.tcx.sess, feature)) - })) + }) .chain(codegen_fn_attrs.instruction_set.iter().map(|x| match x { InstructionSetAttr::ArmA32 => "-thumb-mode".to_string(), InstructionSetAttr::ArmT32 => "+thumb-mode".to_string(), })) - .collect::>() - .join(","); + .collect::>(); - if !features.is_empty() { - let val = CString::new(features).unwrap(); - llvm::AddFunctionAttrStringValue( - llfn, - llvm::AttributePlace::Function, - cstr!("target-features"), - &val, - ); - } - - // Note that currently the `wasm-import-module` doesn't do anything, but - // eventually LLVM 7 should read this and ferry the appropriate import - // module to the output file. - if cx.tcx.sess.target.arch == "wasm32" { + if cx.tcx.sess.target.is_like_wasm { + // If this function is an import from the environment but the wasm + // import has a specific module/name, apply them here. if let Some(module) = wasm_import_module(cx.tcx, instance.def_id()) { llvm::AddFunctionAttrStringValue( llfn, @@ -346,6 +328,30 @@ pub fn from_fn_attrs(cx: &CodegenCx<'ll, 'tcx>, llfn: &'ll Value, instance: ty:: &name, ); } + + // The `"wasm"` abi on wasm targets automatically enables the + // `+multivalue` feature because the purpose of the wasm abi is to match + // the WebAssembly specification, which has this feature. This won't be + // needed when LLVM enables this `multivalue` feature by default. + if !cx.tcx.is_closure(instance.def_id()) { + let abi = cx.tcx.fn_sig(instance.def_id()).abi(); + if abi == Abi::Wasm { + function_features.push("+multivalue".to_string()); + } + } + } + + if !function_features.is_empty() { + let mut global_features = llvm_util::llvm_global_features(cx.tcx.sess); + global_features.extend(function_features.into_iter()); + let features = global_features.join(","); + let val = CString::new(features).unwrap(); + llvm::AddFunctionAttrStringValue( + llfn, + llvm::AttributePlace::Function, + cstr!("target-features"), + &val, + ); } } diff --git a/compiler/rustc_codegen_llvm/src/back/lto.rs b/compiler/rustc_codegen_llvm/src/back/lto.rs index 5effe68752808..4226ed7d99be1 100644 --- a/compiler/rustc_codegen_llvm/src/back/lto.rs +++ b/compiler/rustc_codegen_llvm/src/back/lto.rs @@ -24,6 +24,7 @@ use tracing::{debug, info}; use std::ffi::{CStr, CString}; use std::fs::File; use std::io; +use std::iter; use std::path::Path; use std::ptr; use std::slice; @@ -916,9 +917,7 @@ impl ThinLTOKeysMap { modules: &[llvm::ThinLTOModule], names: &[CString], ) -> Self { - let keys = modules - .iter() - .zip(names.iter()) + let keys = iter::zip(modules, names) .map(|(module, name)| { let key = build_string(|rust_str| unsafe { llvm::LLVMRustComputeLTOCacheKey(rust_str, module.identifier, data.0); diff --git a/compiler/rustc_codegen_llvm/src/back/write.rs b/compiler/rustc_codegen_llvm/src/back/write.rs index c224da7885bd0..b628ae3ae3afc 100644 --- a/compiler/rustc_codegen_llvm/src/back/write.rs +++ b/compiler/rustc_codegen_llvm/src/back/write.rs @@ -1,4 +1,3 @@ -use crate::attributes; use crate::back::lto::ThinBuffer; use crate::back::profiling::{ selfprofile_after_pass_callback, selfprofile_before_pass_callback, LlvmSelfProfiler, @@ -24,11 +23,11 @@ use rustc_fs_util::{link_or_copy, path_to_c_string}; use rustc_hir::def_id::LOCAL_CRATE; use rustc_middle::bug; use rustc_middle::ty::TyCtxt; -use rustc_session::config::{self, Lto, OutputType, Passes, SanitizerSet, SwitchWithOptPath}; +use rustc_session::config::{self, Lto, OutputType, Passes, SwitchWithOptPath}; use rustc_session::Session; use rustc_span::symbol::sym; use rustc_span::InnerSpan; -use rustc_target::spec::{CodeModel, RelocModel, SplitDebuginfo}; +use rustc_target::spec::{CodeModel, RelocModel, SanitizerSet, SplitDebuginfo}; use tracing::debug; use libc::{c_char, c_int, c_uint, c_void, size_t}; @@ -140,7 +139,7 @@ fn to_llvm_relocation_model(relocation_model: RelocModel) -> llvm::RelocModel { } } -fn to_llvm_code_model(code_model: Option) -> llvm::CodeModel { +pub(crate) fn to_llvm_code_model(code_model: Option) -> llvm::CodeModel { match code_model { Some(CodeModel::Tiny) => llvm::CodeModel::Tiny, Some(CodeModel::Small) => llvm::CodeModel::Small, @@ -166,23 +165,18 @@ pub fn target_machine_factory( let code_model = to_llvm_code_model(sess.code_model()); - let mut features = llvm_util::handle_native_features(sess); - features.extend(attributes::llvm_target_features(sess).map(|s| s.to_owned())); let mut singlethread = sess.target.singlethread; // On the wasm target once the `atomics` feature is enabled that means that // we're no longer single-threaded, or otherwise we don't want LLVM to // lower atomic operations to single-threaded operations. - if singlethread - && sess.target.llvm_target.contains("wasm32") - && sess.target_features.contains(&sym::atomics) - { + if singlethread && sess.target.is_like_wasm && sess.target_features.contains(&sym::atomics) { singlethread = false; } let triple = SmallCStr::new(&sess.target.llvm_target); let cpu = SmallCStr::new(llvm_util::target_cpu(sess)); - let features = features.join(","); + let features = llvm_util::llvm_global_features(sess).join(","); let features = CString::new(features).unwrap(); let abi = SmallCStr::new(&sess.target.llvm_abiname); let trap_unreachable = @@ -551,6 +545,15 @@ pub(crate) unsafe fn optimize( llvm::LLVMRustAddPass(fpm, find_pass("lint").unwrap()); continue; } + if pass_name == "insert-gcov-profiling" || pass_name == "instrprof" { + // Instrumentation must be inserted before optimization, + // otherwise LLVM may optimize some functions away which + // breaks llvm-cov. + // + // This mirrors what Clang does in lib/CodeGen/BackendUtil.cpp. + llvm::LLVMRustAddPass(mpm, find_pass(pass_name).unwrap()); + continue; + } if let Some(pass) = find_pass(pass_name) { extra_passes.push(pass); @@ -1044,7 +1047,7 @@ pub unsafe fn with_llvm_pmb( // thresholds copied from clang. match (opt_level, opt_size, inline_threshold) { (.., Some(t)) => { - llvm::LLVMPassManagerBuilderUseInlinerWithThreshold(builder, t as u32); + llvm::LLVMPassManagerBuilderUseInlinerWithThreshold(builder, t); } (llvm::CodeGenOptLevel::Aggressive, ..) => { llvm::LLVMPassManagerBuilderUseInlinerWithThreshold(builder, 275); diff --git a/compiler/rustc_codegen_llvm/src/base.rs b/compiler/rustc_codegen_llvm/src/base.rs index d5be3132dee10..6f6c649bb0b18 100644 --- a/compiler/rustc_codegen_llvm/src/base.rs +++ b/compiler/rustc_codegen_llvm/src/base.rs @@ -32,8 +32,9 @@ use rustc_middle::middle::cstore::EncodedMetadata; use rustc_middle::middle::exported_symbols; use rustc_middle::mir::mono::{Linkage, Visibility}; use rustc_middle::ty::TyCtxt; -use rustc_session::config::{DebugInfo, SanitizerSet}; +use rustc_session::config::DebugInfo; use rustc_span::symbol::Symbol; +use rustc_target::spec::SanitizerSet; use std::ffi::CString; use std::time::Instant; @@ -143,7 +144,7 @@ pub fn compile_codegen_unit( // Finalize code coverage by injecting the coverage map. Note, the coverage map will // also be added to the `llvm.used` variable, created next. - if cx.sess().opts.debugging_opts.instrument_coverage { + if cx.sess().instrument_coverage() { cx.coverageinfo_finalize(); } diff --git a/compiler/rustc_codegen_llvm/src/builder.rs b/compiler/rustc_codegen_llvm/src/builder.rs index f4852c91e53d5..053cda1e7cc9c 100644 --- a/compiler/rustc_codegen_llvm/src/builder.rs +++ b/compiler/rustc_codegen_llvm/src/builder.rs @@ -2,6 +2,7 @@ use crate::common::Funclet; use crate::context::CodegenCx; use crate::llvm::{self, BasicBlock, False}; use crate::llvm::{AtomicOrdering, AtomicRmwBinOp, SynchronizationScope}; +use crate::llvm_util; use crate::type_::Type; use crate::type_of::LayoutLlvmExt; use crate::value::Value; @@ -16,11 +17,12 @@ use rustc_data_structures::small_c_str::SmallCStr; use rustc_hir::def_id::DefId; use rustc_middle::ty::layout::TyAndLayout; use rustc_middle::ty::{self, Ty, TyCtxt}; -use rustc_span::{sym, Span}; +use rustc_span::Span; use rustc_target::abi::{self, Align, Size}; use rustc_target::spec::{HasTargetSpec, Target}; use std::borrow::Cow; use std::ffi::CStr; +use std::iter; use std::ops::{Deref, Range}; use std::ptr; use tracing::debug; @@ -260,7 +262,7 @@ impl BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> { fn fadd_fast(&mut self, lhs: &'ll Value, rhs: &'ll Value) -> &'ll Value { unsafe { let instr = llvm::LLVMBuildFAdd(self.llbuilder, lhs, rhs, UNNAMED); - llvm::LLVMRustSetHasUnsafeAlgebra(instr); + llvm::LLVMRustSetFastMath(instr); instr } } @@ -268,7 +270,7 @@ impl BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> { fn fsub_fast(&mut self, lhs: &'ll Value, rhs: &'ll Value) -> &'ll Value { unsafe { let instr = llvm::LLVMBuildFSub(self.llbuilder, lhs, rhs, UNNAMED); - llvm::LLVMRustSetHasUnsafeAlgebra(instr); + llvm::LLVMRustSetFastMath(instr); instr } } @@ -276,7 +278,7 @@ impl BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> { fn fmul_fast(&mut self, lhs: &'ll Value, rhs: &'ll Value) -> &'ll Value { unsafe { let instr = llvm::LLVMBuildFMul(self.llbuilder, lhs, rhs, UNNAMED); - llvm::LLVMRustSetHasUnsafeAlgebra(instr); + llvm::LLVMRustSetFastMath(instr); instr } } @@ -284,7 +286,7 @@ impl BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> { fn fdiv_fast(&mut self, lhs: &'ll Value, rhs: &'ll Value) -> &'ll Value { unsafe { let instr = llvm::LLVMBuildFDiv(self.llbuilder, lhs, rhs, UNNAMED); - llvm::LLVMRustSetHasUnsafeAlgebra(instr); + llvm::LLVMRustSetFastMath(instr); instr } } @@ -292,7 +294,7 @@ impl BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> { fn frem_fast(&mut self, lhs: &'ll Value, rhs: &'ll Value) -> &'ll Value { unsafe { let instr = llvm::LLVMBuildFRem(self.llbuilder, lhs, rhs, UNNAMED); - llvm::LLVMRustSetHasUnsafeAlgebra(instr); + llvm::LLVMRustSetFastMath(instr); instr } } @@ -668,81 +670,47 @@ impl BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> { } fn fptoui_sat(&mut self, val: &'ll Value, dest_ty: &'ll Type) -> Option<&'ll Value> { - // WebAssembly has saturating floating point to integer casts if the - // `nontrapping-fptoint` target feature is activated. We'll use those if - // they are available. - if self.sess().target.arch == "wasm32" - && self.sess().target_features.contains(&sym::nontrapping_dash_fptoint) - { + if llvm_util::get_version() >= (12, 0, 0) && !self.fptoint_sat_broken_in_llvm() { let src_ty = self.cx.val_ty(val); let float_width = self.cx.float_width(src_ty); let int_width = self.cx.int_width(dest_ty); - let name = match (int_width, float_width) { - (32, 32) => Some("llvm.wasm.trunc.saturate.unsigned.i32.f32"), - (32, 64) => Some("llvm.wasm.trunc.saturate.unsigned.i32.f64"), - (64, 32) => Some("llvm.wasm.trunc.saturate.unsigned.i64.f32"), - (64, 64) => Some("llvm.wasm.trunc.saturate.unsigned.i64.f64"), - _ => None, - }; - if let Some(name) = name { - let intrinsic = self.get_intrinsic(name); - return Some(self.call(intrinsic, &[val], None)); - } + let name = format!("llvm.fptoui.sat.i{}.f{}", int_width, float_width); + let intrinsic = self.get_intrinsic(&name); + return Some(self.call(intrinsic, &[val], None)); } + None } fn fptosi_sat(&mut self, val: &'ll Value, dest_ty: &'ll Type) -> Option<&'ll Value> { - // WebAssembly has saturating floating point to integer casts if the - // `nontrapping-fptoint` target feature is activated. We'll use those if - // they are available. - if self.sess().target.arch == "wasm32" - && self.sess().target_features.contains(&sym::nontrapping_dash_fptoint) - { + if llvm_util::get_version() >= (12, 0, 0) && !self.fptoint_sat_broken_in_llvm() { let src_ty = self.cx.val_ty(val); let float_width = self.cx.float_width(src_ty); let int_width = self.cx.int_width(dest_ty); - let name = match (int_width, float_width) { - (32, 32) => Some("llvm.wasm.trunc.saturate.signed.i32.f32"), - (32, 64) => Some("llvm.wasm.trunc.saturate.signed.i32.f64"), - (64, 32) => Some("llvm.wasm.trunc.saturate.signed.i64.f32"), - (64, 64) => Some("llvm.wasm.trunc.saturate.signed.i64.f64"), - _ => None, - }; - if let Some(name) = name { - let intrinsic = self.get_intrinsic(name); - return Some(self.call(intrinsic, &[val], None)); - } + let name = format!("llvm.fptosi.sat.i{}.f{}", int_width, float_width); + let intrinsic = self.get_intrinsic(&name); + return Some(self.call(intrinsic, &[val], None)); } - None - } - fn fptosui_may_trap(&self, val: &'ll Value, dest_ty: &'ll Type) -> bool { - // Most of the time we'll be generating the `fptosi` or `fptoui` - // instruction for floating-point-to-integer conversions. These - // instructions by definition in LLVM do not trap. For the WebAssembly - // target, however, we'll lower in some cases to intrinsic calls instead - // which may trap. If we detect that this is a situation where we'll be - // using the intrinsics then we report that the call map trap, which - // callers might need to handle. - if !self.wasm_and_missing_nontrapping_fptoint() { - return false; - } - let src_ty = self.cx.val_ty(val); - let float_width = self.cx.float_width(src_ty); - let int_width = self.cx.int_width(dest_ty); - matches!((int_width, float_width), (32, 32) | (32, 64) | (64, 32) | (64, 64)) + None } fn fptoui(&mut self, val: &'ll Value, dest_ty: &'ll Type) -> &'ll Value { - // When we can, use the native wasm intrinsics which have tighter - // codegen. Note that this has a semantic difference in that the - // intrinsic can trap whereas `fptoui` never traps. That difference, - // however, is handled by `fptosui_may_trap` above. + // On WebAssembly the `fptoui` and `fptosi` instructions currently have + // poor codegen. The reason for this is that the corresponding wasm + // instructions, `i32.trunc_f32_s` for example, will trap when the float + // is out-of-bounds, infinity, or nan. This means that LLVM + // automatically inserts control flow around `fptoui` and `fptosi` + // because the LLVM instruction `fptoui` is defined as producing a + // poison value, not having UB on out-of-bounds values. // - // Note that we skip the wasm intrinsics for vector types where `fptoui` - // must be used instead. - if self.wasm_and_missing_nontrapping_fptoint() { + // This method, however, is only used with non-saturating casts that + // have UB on out-of-bounds values. This means that it's ok if we use + // the raw wasm instruction since out-of-bounds values can do whatever + // we like. To ensure that LLVM picks the right instruction we choose + // the raw wasm intrinsic functions which avoid LLVM inserting all the + // other control flow automatically. + if self.sess().target.arch == "wasm32" { let src_ty = self.cx.val_ty(val); if self.cx.type_kind(src_ty) != TypeKind::Vector { let float_width = self.cx.float_width(src_ty); @@ -764,7 +732,8 @@ impl BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> { } fn fptosi(&mut self, val: &'ll Value, dest_ty: &'ll Type) -> &'ll Value { - if self.wasm_and_missing_nontrapping_fptoint() { + // see `fptoui` above for why wasm is different here + if self.sess().target.arch == "wasm32" { let src_ty = self.cx.val_ty(val); if self.cx.type_kind(src_ty) != TypeKind::Vector { let float_width = self.cx.float_width(src_ty); @@ -1241,14 +1210,14 @@ impl Builder<'a, 'll, 'tcx> { pub fn vector_reduce_fadd_fast(&mut self, acc: &'ll Value, src: &'ll Value) -> &'ll Value { unsafe { let instr = llvm::LLVMRustBuildVectorReduceFAdd(self.llbuilder, acc, src); - llvm::LLVMRustSetHasUnsafeAlgebra(instr); + llvm::LLVMRustSetFastMath(instr); instr } } pub fn vector_reduce_fmul_fast(&mut self, acc: &'ll Value, src: &'ll Value) -> &'ll Value { unsafe { let instr = llvm::LLVMRustBuildVectorReduceFMul(self.llbuilder, acc, src); - llvm::LLVMRustSetHasUnsafeAlgebra(instr); + llvm::LLVMRustSetFastMath(instr); instr } } @@ -1281,7 +1250,7 @@ impl Builder<'a, 'll, 'tcx> { unsafe { let instr = llvm::LLVMRustBuildVectorReduceFMin(self.llbuilder, src, /*NoNaNs:*/ true); - llvm::LLVMRustSetHasUnsafeAlgebra(instr); + llvm::LLVMRustSetFastMath(instr); instr } } @@ -1289,7 +1258,7 @@ impl Builder<'a, 'll, 'tcx> { unsafe { let instr = llvm::LLVMRustBuildVectorReduceFMax(self.llbuilder, src, /*NoNaNs:*/ true); - llvm::LLVMRustSetHasUnsafeAlgebra(instr); + llvm::LLVMRustSetFastMath(instr); instr } } @@ -1352,18 +1321,14 @@ impl Builder<'a, 'll, 'tcx> { let param_tys = self.cx.func_params_types(fn_ty); - let all_args_match = param_tys - .iter() - .zip(args.iter().map(|&v| self.val_ty(v))) + let all_args_match = iter::zip(¶m_tys, args.iter().map(|&v| self.val_ty(v))) .all(|(expected_ty, actual_ty)| *expected_ty == actual_ty); if all_args_match { return Cow::Borrowed(args); } - let casted_args: Vec<_> = param_tys - .into_iter() - .zip(args.iter()) + let casted_args: Vec<_> = iter::zip(param_tys, args) .enumerate() .map(|(i, (expected_ty, &actual_val))| { let actual_ty = self.val_ty(actual_val); @@ -1423,8 +1388,11 @@ impl Builder<'a, 'll, 'tcx> { } } - fn wasm_and_missing_nontrapping_fptoint(&self) -> bool { - self.sess().target.arch == "wasm32" - && !self.sess().target_features.contains(&sym::nontrapping_dash_fptoint) + fn fptoint_sat_broken_in_llvm(&self) -> bool { + match self.tcx.sess.target.arch.as_str() { + // FIXME - https://bugs.llvm.org/show_bug.cgi?id=50083 + "riscv64" => llvm_util::get_version() < (13, 0, 0), + _ => false, + } } } diff --git a/compiler/rustc_codegen_llvm/src/callee.rs b/compiler/rustc_codegen_llvm/src/callee.rs index 367c1f4811cdb..b26969a50120f 100644 --- a/compiler/rustc_codegen_llvm/src/callee.rs +++ b/compiler/rustc_codegen_llvm/src/callee.rs @@ -14,6 +14,7 @@ use tracing::debug; use rustc_middle::ty::layout::{FnAbiExt, HasTyCtxt}; use rustc_middle::ty::{self, Instance, TypeFoldable}; +use rustc_target::spec::RelocModel; /// Codegens a reference to a fn/method item, monomorphizing and /// inlining as it goes. @@ -170,17 +171,19 @@ pub fn get_fn(cx: &CodegenCx<'ll, 'tcx>, instance: Instance<'tcx>) -> &'ll Value } } } - } - // MinGW: For backward compatibility we rely on the linker to decide whether it - // should use dllimport for functions. - if cx.use_dll_storage_attrs - && tcx.is_dllimport_foreign_item(instance_def_id) - && tcx.sess.target.env != "gnu" - { - unsafe { + // MinGW: For backward compatibility we rely on the linker to decide whether it + // should use dllimport for functions. + if cx.use_dll_storage_attrs + && tcx.is_dllimport_foreign_item(instance_def_id) + && tcx.sess.target.env != "gnu" + { llvm::LLVMSetDLLStorageClass(llfn, llvm::DLLStorageClass::DllImport); } + + if cx.tcx.sess.relocation_model() == RelocModel::Static { + llvm::LLVMRustSetDSOLocal(llfn, true); + } } llfn diff --git a/compiler/rustc_codegen_llvm/src/context.rs b/compiler/rustc_codegen_llvm/src/context.rs index 9f6a2ae3ca1b7..f5c54b11c08e7 100644 --- a/compiler/rustc_codegen_llvm/src/context.rs +++ b/compiler/rustc_codegen_llvm/src/context.rs @@ -1,4 +1,5 @@ use crate::attributes; +use crate::back::write::to_llvm_code_model; use crate::callee::get_fn; use crate::coverageinfo; use crate::debuginfo; @@ -78,7 +79,7 @@ pub struct CodegenCx<'ll, 'tcx> { pub pointee_infos: RefCell, Size), Option>>, pub isize_ty: &'ll Type, - pub coverage_cx: Option>, + pub coverage_cx: Option>, pub dbg_cx: Option>, eh_personality: Cell>, @@ -100,10 +101,6 @@ fn to_llvm_tls_model(tls_model: TlsModel) -> llvm::ThreadLocalMode { } } -fn strip_x86_address_spaces(data_layout: String) -> String { - data_layout.replace("-p270:32:32-p271:32:32-p272:64:64-", "-") -} - fn strip_powerpc64_vectors(data_layout: String) -> String { data_layout.replace("-v256:256:256-v512:512:512", "") } @@ -118,11 +115,6 @@ pub unsafe fn create_module( let llmod = llvm::LLVMModuleCreateWithNameInContext(mod_name.as_ptr(), llcx); let mut target_data_layout = sess.target.data_layout.clone(); - if llvm_util::get_version() < (10, 0, 0) - && (sess.target.arch == "x86" || sess.target.arch == "x86_64") - { - target_data_layout = strip_x86_address_spaces(target_data_layout); - } if llvm_util::get_version() < (12, 0, 0) && sess.target.arch == "powerpc64" { target_data_layout = strip_powerpc64_vectors(target_data_layout); } @@ -181,6 +173,13 @@ pub unsafe fn create_module( } } + // Linking object files with different code models is undefined behavior + // because the compiler would have to generate additional code (to span + // longer jumps) if a larger code model is used with a smaller one. + // + // See https://reviews.llvm.org/D52322 and https://reviews.llvm.org/D52323. + llvm::LLVMRustSetModuleCodeModel(llmod, to_llvm_code_model(sess.code_model())); + // If skipping the PLT is enabled, we need to add some module metadata // to ensure intrinsic calls don't use it. if !sess.needs_plt() { @@ -272,7 +271,7 @@ impl<'ll, 'tcx> CodegenCx<'ll, 'tcx> { let (llcx, llmod) = (&*llvm_module.llcx, llvm_module.llmod()); - let coverage_cx = if tcx.sess.opts.debugging_opts.instrument_coverage { + let coverage_cx = if tcx.sess.instrument_coverage() { let covctx = coverageinfo::CrateCoverageContext::new(); Some(covctx) } else { @@ -323,7 +322,7 @@ impl<'ll, 'tcx> CodegenCx<'ll, 'tcx> { } #[inline] - pub fn coverage_context(&'a self) -> Option<&'a coverageinfo::CrateCoverageContext<'tcx>> { + pub fn coverage_context(&'a self) -> Option<&'a coverageinfo::CrateCoverageContext<'ll, 'tcx>> { self.coverage_cx.as_ref() } } @@ -504,14 +503,6 @@ impl CodegenCx<'b, 'tcx> { let t_f32 = self.type_f32(); let t_f64 = self.type_f64(); - ifn!("llvm.wasm.trunc.saturate.unsigned.i32.f32", fn(t_f32) -> t_i32); - ifn!("llvm.wasm.trunc.saturate.unsigned.i32.f64", fn(t_f64) -> t_i32); - ifn!("llvm.wasm.trunc.saturate.unsigned.i64.f32", fn(t_f32) -> t_i64); - ifn!("llvm.wasm.trunc.saturate.unsigned.i64.f64", fn(t_f64) -> t_i64); - ifn!("llvm.wasm.trunc.saturate.signed.i32.f32", fn(t_f32) -> t_i32); - ifn!("llvm.wasm.trunc.saturate.signed.i32.f64", fn(t_f64) -> t_i32); - ifn!("llvm.wasm.trunc.saturate.signed.i64.f32", fn(t_f32) -> t_i64); - ifn!("llvm.wasm.trunc.saturate.signed.i64.f64", fn(t_f64) -> t_i64); ifn!("llvm.wasm.trunc.unsigned.i32.f32", fn(t_f32) -> t_i32); ifn!("llvm.wasm.trunc.unsigned.i32.f64", fn(t_f64) -> t_i32); ifn!("llvm.wasm.trunc.unsigned.i64.f32", fn(t_f32) -> t_i64); @@ -521,6 +512,28 @@ impl CodegenCx<'b, 'tcx> { ifn!("llvm.wasm.trunc.signed.i64.f32", fn(t_f32) -> t_i64); ifn!("llvm.wasm.trunc.signed.i64.f64", fn(t_f64) -> t_i64); + ifn!("llvm.fptosi.sat.i8.f32", fn(t_f32) -> t_i8); + ifn!("llvm.fptosi.sat.i16.f32", fn(t_f32) -> t_i16); + ifn!("llvm.fptosi.sat.i32.f32", fn(t_f32) -> t_i32); + ifn!("llvm.fptosi.sat.i64.f32", fn(t_f32) -> t_i64); + ifn!("llvm.fptosi.sat.i128.f32", fn(t_f32) -> t_i128); + ifn!("llvm.fptosi.sat.i8.f64", fn(t_f64) -> t_i8); + ifn!("llvm.fptosi.sat.i16.f64", fn(t_f64) -> t_i16); + ifn!("llvm.fptosi.sat.i32.f64", fn(t_f64) -> t_i32); + ifn!("llvm.fptosi.sat.i64.f64", fn(t_f64) -> t_i64); + ifn!("llvm.fptosi.sat.i128.f64", fn(t_f64) -> t_i128); + + ifn!("llvm.fptoui.sat.i8.f32", fn(t_f32) -> t_i8); + ifn!("llvm.fptoui.sat.i16.f32", fn(t_f32) -> t_i16); + ifn!("llvm.fptoui.sat.i32.f32", fn(t_f32) -> t_i32); + ifn!("llvm.fptoui.sat.i64.f32", fn(t_f32) -> t_i64); + ifn!("llvm.fptoui.sat.i128.f32", fn(t_f32) -> t_i128); + ifn!("llvm.fptoui.sat.i8.f64", fn(t_f64) -> t_i8); + ifn!("llvm.fptoui.sat.i16.f64", fn(t_f64) -> t_i16); + ifn!("llvm.fptoui.sat.i32.f64", fn(t_f64) -> t_i32); + ifn!("llvm.fptoui.sat.i64.f64", fn(t_f64) -> t_i64); + ifn!("llvm.fptoui.sat.i128.f64", fn(t_f64) -> t_i128); + ifn!("llvm.trap", fn() -> void); ifn!("llvm.debugtrap", fn() -> void); ifn!("llvm.frameaddress", fn(t_i32) -> i8p); @@ -704,7 +717,7 @@ impl CodegenCx<'b, 'tcx> { ifn!("llvm.va_end", fn(i8p) -> void); ifn!("llvm.va_copy", fn(i8p, i8p) -> void); - if self.sess().opts.debugging_opts.instrument_coverage { + if self.sess().instrument_coverage() { ifn!("llvm.instrprof.increment", fn(i8p, t_i64, t_i32, t_i32) -> void); } diff --git a/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen.rs b/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen.rs index 444a9d4ba0463..2ac814bf22838 100644 --- a/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen.rs +++ b/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen.rs @@ -3,13 +3,12 @@ use crate::coverageinfo; use crate::llvm; use llvm::coverageinfo::CounterMappingRegion; -use rustc_codegen_ssa::coverageinfo::map::{Counter, CounterExpression, FunctionCoverage}; -use rustc_codegen_ssa::traits::ConstMethods; +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, LOCAL_CRATE}; use rustc_llvm::RustString; use rustc_middle::mir::coverage::CodeRegion; -use rustc_middle::ty::{Instance, TyCtxt}; use rustc_span::Symbol; use std::ffi::CString; @@ -20,16 +19,17 @@ use tracing::debug; /// /// This Coverage Map complies with Coverage Mapping Format version 4 (zero-based encoded as 3), /// as defined at [LLVM Code Coverage Mapping Format](https://github.com/rust-lang/llvm-project/blob/rustc/11.0-2020-10-12/llvm/docs/CoverageMappingFormat.rst#llvm-code-coverage-mapping-format) -/// and published in Rust's current (November 2020) fork of LLVM. This version is supported by the -/// LLVM coverage tools (`llvm-profdata` and `llvm-cov`) bundled with Rust's fork of LLVM. +/// and published in Rust's November 2020 fork of LLVM. This version is supported by the LLVM +/// coverage tools (`llvm-profdata` and `llvm-cov`) bundled with Rust's fork of LLVM. /// /// Consequently, Rust's bundled version of Clang also generates Coverage Maps compliant with -/// version 3. Clang's implementation of Coverage Map generation was referenced when implementing -/// this Rust version, and though the format documentation is very explicit and detailed, some -/// undocumented details in Clang's implementation (that may or may not be important) were also -/// replicated for Rust's Coverage Map. +/// the same version. Clang's implementation of Coverage Map generation was referenced when +/// implementing this Rust version, and though the format documentation is very explicit and +/// detailed, some undocumented details in Clang's implementation (that may or may not be important) +/// were also replicated for Rust's Coverage Map. pub fn finalize<'ll, 'tcx>(cx: &CodegenCx<'ll, 'tcx>) { let tcx = cx.tcx; + // Ensure LLVM supports Coverage Map Version 4 (encoded as a zero-based value: 3). // If not, the LLVM Version must be less than 11. let version = coverageinfo::mapping_version(); @@ -39,17 +39,24 @@ pub fn finalize<'ll, 'tcx>(cx: &CodegenCx<'ll, 'tcx>) { debug!("Generating coverage map for CodegenUnit: `{}`", cx.codegen_unit.name()); - let mut function_coverage_map = match cx.coverage_context() { + // In order to show that unused functions have coverage counts of zero (0), LLVM requires the + // 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() { + add_unused_functions(cx); + } + + let function_coverage_map = match cx.coverage_context() { Some(ctx) => ctx.take_function_coverage_map(), None => return, }; + if function_coverage_map.is_empty() { // This module has no functions with coverage instrumentation return; } - add_unreachable_coverage(tcx, &mut function_coverage_map); - let mut mapgen = CoverageMapGenerator::new(); // Encode coverage mappings and generate function records @@ -57,7 +64,8 @@ pub fn finalize<'ll, 'tcx>(cx: &CodegenCx<'ll, 'tcx>) { for (instance, function_coverage) in function_coverage_map { debug!("Generate function coverage for {}, {:?}", cx.codegen_unit.name(), instance); let mangled_function_name = tcx.symbol_name(instance).to_string(); - let function_source_hash = function_coverage.source_hash(); + let source_hash = function_coverage.source_hash(); + let is_used = function_coverage.is_used(); let (expressions, counter_regions) = function_coverage.get_expressions_and_counter_regions(); @@ -69,7 +77,7 @@ pub fn finalize<'ll, 'tcx>(cx: &CodegenCx<'ll, 'tcx>) { "Every `FunctionCoverage` should have at least one counter" ); - function_data.push((mangled_function_name, function_source_hash, coverage_mapping_buffer)); + function_data.push((mangled_function_name, source_hash, is_used, coverage_mapping_buffer)); } // Encode all filenames referenced by counters/expressions in this module @@ -84,13 +92,14 @@ pub fn finalize<'ll, 'tcx>(cx: &CodegenCx<'ll, 'tcx>) { // Generate the LLVM IR representation of the coverage map and store it in a well-known global let cov_data_val = mapgen.generate_coverage_map(cx, version, filenames_size, filenames_val); - for (mangled_function_name, function_source_hash, coverage_mapping_buffer) in function_data { + for (mangled_function_name, source_hash, is_used, coverage_mapping_buffer) in function_data { save_function_record( cx, mangled_function_name, - function_source_hash, + source_hash, filenames_ref, coverage_mapping_buffer, + is_used, ); } @@ -201,9 +210,10 @@ impl CoverageMapGenerator { fn save_function_record( cx: &CodegenCx<'ll, 'tcx>, mangled_function_name: String, - function_source_hash: u64, + source_hash: u64, filenames_ref: u64, coverage_mapping_buffer: Vec, + is_used: bool, ) { // Concatenate the encoded coverage mappings let coverage_mapping_size = coverage_mapping_buffer.len(); @@ -212,128 +222,120 @@ fn save_function_record( let func_name_hash = coverageinfo::hash_str(&mangled_function_name); let func_name_hash_val = cx.const_u64(func_name_hash); let coverage_mapping_size_val = cx.const_u32(coverage_mapping_size as u32); - let func_hash_val = cx.const_u64(function_source_hash); + let source_hash_val = cx.const_u64(source_hash); let filenames_ref_val = cx.const_u64(filenames_ref); let func_record_val = cx.const_struct( &[ func_name_hash_val, coverage_mapping_size_val, - func_hash_val, + source_hash_val, filenames_ref_val, coverage_mapping_val, ], /*packed=*/ true, ); - // At the present time, the coverage map for Rust assumes every instrumented function `is_used`. - // Note that Clang marks functions as "unused" in `CodeGenPGO::emitEmptyCounterMapping`. (See: - // https://github.com/rust-lang/llvm-project/blob/de02a75e398415bad4df27b4547c25b896c8bf3b/clang%2Flib%2FCodeGen%2FCodeGenPGO.cpp#L877-L878 - // for example.) - // - // It's not yet clear if or how this may be applied to Rust in the future, but the `is_used` - // argument is available and handled similarly. - let is_used = true; coverageinfo::save_func_record_to_mod(cx, func_name_hash, func_record_val, is_used); } /// When finalizing the coverage map, `FunctionCoverage` only has the `CodeRegion`s and counters for /// the functions that went through codegen; such as public functions and "used" functions /// (functions referenced by other "used" or public items). Any other functions considered unused, -/// or "Unreachable" were still parsed and processed through the MIR stage. +/// or "Unreachable", were still parsed and processed through the MIR stage, but were not +/// codegenned. (Note that `-Clink-dead-code` can force some unused code to be codegenned, but +/// that flag is known to cause other errors, when combined with `-Z instrument-coverage`; and +/// `-Clink-dead-code` will not generate code for unused generic functions.) /// -/// We can find the unreachable functions by the set difference of all MIR `DefId`s (`tcx` query -/// `mir_keys`) minus the codegenned `DefId`s (`tcx` query `collect_and_partition_mono_items`). +/// We can find the unused functions (including generic functions) by the set difference of all MIR +/// `DefId`s (`tcx` query `mir_keys`) minus the codegenned `DefId`s (`tcx` query +/// `collect_and_partition_mono_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 "Unreachable" functions's -/// `CodeRegion`s more than once, so we have to pick which CGU's `function_coverage_map` to add -/// each "Unreachable" function to. -/// -/// Some constraints: -/// -/// 1. The file name of an "Unreachable" function must match the file name of the existing -/// codegenned (covered) function to which the unreachable code regions will be added. -/// 2. The function to which the unreachable code regions will be added must not be a genaric -/// function (must not have type parameters) because the coverage tools will get confused -/// if the codegenned function has more than one instantiation and additional `CodeRegion`s -/// attached to only one of those instantiations. -fn add_unreachable_coverage<'tcx>( - tcx: TyCtxt<'tcx>, - function_coverage_map: &mut FxHashMap, FunctionCoverage<'tcx>>, -) { +/// 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. +fn add_unused_functions<'ll, 'tcx>(cx: &CodegenCx<'ll, 'tcx>) { + let tcx = cx.tcx; + // 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)? - // Note: If the crate *only* defines generic functions, there are no codegenerated non-generic - // functions to add any unreachable code to. In this case, the unreachable code regions will - // have no coverage, instead of having coverage with zero executions. - // - // This is probably still an improvement over Clang, which does not generate any coverage - // for uninstantiated template functions. - - let has_non_generic_def_ids = - function_coverage_map.keys().any(|instance| instance.def.attrs(tcx).len() == 0); - - if !has_non_generic_def_ids { - // There are no non-generic functions to add unreachable `CodeRegion`s to - return; - } + let ignore_unused_generics = tcx.sess.instrument_coverage_except_unused_generics(); - let all_def_ids: DefIdSet = - tcx.mir_keys(LOCAL_CRATE).iter().map(|local_def_id| local_def_id.to_def_id()).collect(); + let all_def_ids: DefIdSet = tcx + .mir_keys(LOCAL_CRATE) + .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) { + return None; + } + Some(local_def_id.to_def_id()) + }) + .collect(); - let (codegenned_def_ids, _) = tcx.collect_and_partition_mono_items(LOCAL_CRATE); + let codegenned_def_ids = tcx.codegened_and_inlined_items(LOCAL_CRATE); - let mut unreachable_def_ids_by_file: FxHashMap> = FxHashMap::default(); + let mut unused_def_ids_by_file: FxHashMap> = FxHashMap::default(); for &non_codegenned_def_id in all_def_ids.difference(codegenned_def_ids) { - // Make sure the non-codegenned (unreachable) function has a file_name + // Make sure the non-codegenned (unused) function has a file_name if let Some(non_codegenned_file_name) = tcx.covered_file_name(non_codegenned_def_id) { - let def_ids = unreachable_def_ids_by_file - .entry(*non_codegenned_file_name) - .or_insert_with(Vec::new); + 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); } } - if unreachable_def_ids_by_file.is_empty() { - // There are no unreachable functions with file names to add (in any CGU) + if unused_def_ids_by_file.is_empty() { + // There are no unused functions with file names to add (in any CGU) return; } - // Since there may be multiple `CodegenUnit`s, some codegenned_def_ids may be codegenned in a - // different CGU, and will be added to the function_coverage_map for each CGU. Determine which - // function_coverage_map has the responsibility for publishing unreachable coverage - // based on file name: + // 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. // - // For each covered file name, sort ONLY the non-generic codegenned_def_ids, and if - // covered_def_ids.contains(the first def_id) for a given file_name, add the unreachable code - // region in this function_coverage_map. Otherwise, ignore it and assume another CGU's - // function_coverage_map will be adding it (because it will be first for one, and only one, - // of them). + // 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 = codegenned_def_ids.iter().map(|def_id| *def_id).collect(); sorted_codegenned_def_ids.sort_unstable(); let mut first_covered_def_id_by_file: FxHashMap = FxHashMap::default(); for &def_id in sorted_codegenned_def_ids.iter() { - // Only consider non-generic functions, to potentially add unreachable code regions - if tcx.generics_of(def_id).count() == 0 { - if let Some(covered_file_name) = tcx.covered_file_name(def_id) { - // Only add files known to have unreachable functions - if unreachable_def_ids_by_file.contains_key(covered_file_name) { - first_covered_def_id_by_file.entry(*covered_file_name).or_insert(def_id); - } + 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); } } } // Get the set of def_ids with coverage regions, known by *this* CoverageContext. - let cgu_covered_def_ids: DefIdSet = - function_coverage_map.keys().map(|instance| instance.def.def_id()).collect(); + 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 mut cgu_covered_files: FxHashSet = first_covered_def_id_by_file + let cgu_covered_files: FxHashSet = first_covered_def_id_by_file .iter() .filter_map( |(&file_name, def_id)| { @@ -342,49 +344,13 @@ fn add_unreachable_coverage<'tcx>( ) .collect(); - // Find the first covered, non-generic function (instance) for each cgu_covered_file. Take the - // unreachable code regions for that file, and add them to the function. - // - // There are three `for` loops here, but (a) the lists have already been reduced to the minimum - // required values, the lists are further reduced (by `remove()` calls) when elements are no - // longer needed, and there are several opportunities to branch out of loops early. - for (instance, function_coverage) in function_coverage_map.iter_mut() { - if instance.def.attrs(tcx).len() > 0 { - continue; - } - // The covered function is not generic... - let covered_def_id = instance.def.def_id(); - if let Some(covered_file_name) = tcx.covered_file_name(covered_def_id) { - if !cgu_covered_files.remove(&covered_file_name) { - continue; - } - // The covered function's file is one of the files with unreachable code regions, so - // all of the unreachable code regions for this file will be added to this function. - for def_id in - unreachable_def_ids_by_file.remove(&covered_file_name).into_iter().flatten() - { - // Note, this loop adds an unreachable code regions for each MIR-derived region. - // Alternatively, we could add a single code region for the maximum span of all - // code regions here. - // - // Observed downsides of this approach are: - // - // 1. The coverage results will appear inconsistent compared with the same (or - // similar) code in a function that is reached. - // 2. If the function is unreachable from one crate but reachable when compiling - // another referencing crate (such as a cross-crate reference to a - // generic function or inlined function), actual coverage regions overlaid - // on a single larger code span of `Zero` coverage can appear confusing or - // wrong. Chaning the unreachable coverage from a `code_region` to a - // `gap_region` can help, but still can look odd with `0` line counts for - // lines between executed (> 0) lines (such as for blank lines or comments). - for ®ion in tcx.covered_code_regions(def_id) { - function_coverage.add_unreachable_region(region.clone()); - } - } - if cgu_covered_files.is_empty() { - break; - } + // 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); } } } diff --git a/compiler/rustc_codegen_llvm/src/coverageinfo/mod.rs b/compiler/rustc_codegen_llvm/src/coverageinfo/mod.rs index e47b8fde40fee..afc2bdbfd52ec 100644 --- a/compiler/rustc_codegen_llvm/src/coverageinfo/mod.rs +++ b/compiler/rustc_codegen_llvm/src/coverageinfo/mod.rs @@ -1,5 +1,6 @@ use crate::llvm; +use crate::abi::{Abi, FnAbi}; use crate::builder::Builder; use crate::common::CodegenCx; @@ -7,33 +8,47 @@ use libc::c_uint; use llvm::coverageinfo::CounterMappingRegion; use rustc_codegen_ssa::coverageinfo::map::{CounterExpression, FunctionCoverage}; use rustc_codegen_ssa::traits::{ - BaseTypeMethods, CoverageInfoBuilderMethods, CoverageInfoMethods, MiscMethods, StaticMethods, + BaseTypeMethods, BuilderMethods, ConstMethods, CoverageInfoBuilderMethods, CoverageInfoMethods, + MiscMethods, StaticMethods, }; use rustc_data_structures::fx::FxHashMap; +use rustc_hir as hir; +use rustc_hir::def_id::DefId; use rustc_llvm::RustString; +use rustc_middle::bug; use rustc_middle::mir::coverage::{ CodeRegion, CounterValueReference, ExpressionOperandId, InjectedExpressionId, Op, }; +use rustc_middle::ty; +use rustc_middle::ty::layout::FnAbiExt; +use rustc_middle::ty::subst::InternalSubsts; use rustc_middle::ty::Instance; use std::cell::RefCell; use std::ffi::CString; +use std::iter; use tracing::debug; pub mod mapgen; +const UNUSED_FUNCTION_COUNTER_ID: CounterValueReference = CounterValueReference::START; + const VAR_ALIGN_BYTES: usize = 8; /// A context object for maintaining all state needed by the coverageinfo module. -pub struct CrateCoverageContext<'tcx> { +pub struct CrateCoverageContext<'ll, 'tcx> { // Coverage data for each instrumented function identified by DefId. pub(crate) function_coverage_map: RefCell, FunctionCoverage<'tcx>>>, + pub(crate) pgo_func_name_var_map: RefCell, &'ll llvm::Value>>, } -impl<'tcx> CrateCoverageContext<'tcx> { +impl<'ll, 'tcx> CrateCoverageContext<'ll, 'tcx> { pub fn new() -> Self { - Self { function_coverage_map: Default::default() } + Self { + function_coverage_map: Default::default(), + pgo_func_name_var_map: Default::default(), + } } pub fn take_function_coverage_map(&self) -> FxHashMap, FunctionCoverage<'tcx>> { @@ -41,23 +56,47 @@ impl<'tcx> CrateCoverageContext<'tcx> { } } -impl CoverageInfoMethods for CodegenCx<'ll, 'tcx> { +impl CoverageInfoMethods<'tcx> for CodegenCx<'ll, 'tcx> { fn coverageinfo_finalize(&self) { mapgen::finalize(self) } -} -impl CoverageInfoBuilderMethods<'tcx> for Builder<'a, 'll, 'tcx> { - /// Calls llvm::createPGOFuncNameVar() with the given function instance's mangled function name. - /// The LLVM API returns an llvm::GlobalVariable containing the function name, with the specific - /// variable name and linkage required by LLVM InstrProf source-based coverage instrumentation. - fn create_pgo_func_name_var(&self, instance: Instance<'tcx>) -> Self::Value { - let llfn = self.cx.get_fn(instance); - let mangled_fn_name = CString::new(self.tcx.symbol_name(instance).name) - .expect("error converting function name to C string"); - unsafe { llvm::LLVMRustCoverageCreatePGOFuncNameVar(llfn, mangled_fn_name.as_ptr()) } + fn get_pgo_func_name_var(&self, instance: Instance<'tcx>) -> &'ll llvm::Value { + if let Some(coverage_context) = self.coverage_context() { + debug!("getting pgo_func_name_var for instance={:?}", instance); + let mut pgo_func_name_var_map = coverage_context.pgo_func_name_var_map.borrow_mut(); + pgo_func_name_var_map + .entry(instance) + .or_insert_with(|| create_pgo_func_name_var(self, instance)) + } else { + bug!("Could not get the `coverage_context`"); + } + } + + /// Functions with MIR-based coverage are normally codegenned _only_ if + /// called. LLVM coverage tools typically expect every function to be + /// defined (even if unused), with at least one call to LLVM intrinsic + /// `instrprof.increment`. + /// + /// Codegen a small function that will never be called, with one counter + /// that will never be incremented. + /// + /// For used/called functions, the coverageinfo was already added to the + /// `function_coverage_map` (keyed by function `Instance`) during codegen. + /// But in this case, since the unused function was _not_ previously + /// codegenned, collect the coverage `CodeRegion`s from the MIR and add + /// them. The first `CodeRegion` is used to add a single counter, with the + /// same counter ID used in the injected `instrprof.increment` intrinsic + /// call. Since the function is never called, all other `CodeRegion`s can be + /// added as `unreachable_region`s. + fn define_unused_fn(&self, def_id: DefId) { + let instance = declare_unused_fn(self, &def_id); + codegen_unused_fn_and_counter(self, instance); + add_unused_function_coverage(self, instance, def_id); } +} +impl CoverageInfoBuilderMethods<'tcx> for Builder<'a, 'll, 'tcx> { fn set_function_source_hash( &mut self, instance: Instance<'tcx>, @@ -145,6 +184,100 @@ impl CoverageInfoBuilderMethods<'tcx> for Builder<'a, 'll, 'tcx> { } } +fn declare_unused_fn(cx: &CodegenCx<'ll, 'tcx>, def_id: &DefId) -> Instance<'tcx> { + let tcx = cx.tcx; + + let instance = Instance::new( + *def_id, + InternalSubsts::for_item(tcx, *def_id, |param, _| { + if let ty::GenericParamDefKind::Lifetime = param.kind { + tcx.lifetimes.re_erased.into() + } else { + tcx.mk_param_from_def(param) + } + }), + ); + + let llfn = cx.declare_fn( + &tcx.symbol_name(instance).name, + &FnAbi::of_fn_ptr( + cx, + ty::Binder::dummy(tcx.mk_fn_sig( + iter::once(tcx.mk_unit()), + tcx.mk_unit(), + false, + hir::Unsafety::Unsafe, + Abi::Rust, + )), + &[], + ), + ); + + llvm::set_linkage(llfn, llvm::Linkage::WeakAnyLinkage); + llvm::set_visibility(llfn, llvm::Visibility::Hidden); + + assert!(cx.instances.borrow_mut().insert(instance, llfn).is_none()); + + instance +} + +fn codegen_unused_fn_and_counter(cx: &CodegenCx<'ll, 'tcx>, instance: Instance<'tcx>) { + let llfn = cx.get_fn(instance); + let mut bx = Builder::new_block(cx, llfn, "unused_function"); + let fn_name = bx.get_pgo_func_name_var(instance); + let hash = bx.const_u64(0); + let num_counters = bx.const_u32(1); + let index = bx.const_u32(u32::from(UNUSED_FUNCTION_COUNTER_ID)); + debug!( + "codegen intrinsic instrprof.increment(fn_name={:?}, hash={:?}, num_counters={:?}, + index={:?}) for unused function: {:?}", + fn_name, hash, num_counters, index, instance + ); + bx.instrprof_increment(fn_name, hash, num_counters, index); + bx.ret_void(); +} + +fn add_unused_function_coverage( + cx: &CodegenCx<'ll, 'tcx>, + instance: Instance<'tcx>, + def_id: DefId, +) { + let tcx = cx.tcx; + + let mut function_coverage = FunctionCoverage::unused(tcx, instance); + for (index, &code_region) in tcx.covered_code_regions(def_id).iter().enumerate() { + if index == 0 { + // Insert at least one real counter so the LLVM CoverageMappingReader will find expected + // definitions. + function_coverage.add_counter(UNUSED_FUNCTION_COUNTER_ID, code_region.clone()); + } else { + function_coverage.add_unreachable_region(code_region.clone()); + } + } + + if let Some(coverage_context) = cx.coverage_context() { + coverage_context.function_coverage_map.borrow_mut().insert(instance, function_coverage); + } else { + bug!("Could not get the `coverage_context`"); + } +} + +/// Calls llvm::createPGOFuncNameVar() with the given function instance's +/// mangled function name. The LLVM API returns an llvm::GlobalVariable +/// containing the function name, with the specific variable name and linkage +/// required by LLVM InstrProf source-based coverage instrumentation. Use +/// `bx.get_pgo_func_name_var()` to ensure the variable is only created once per +/// `Instance`. +fn create_pgo_func_name_var( + cx: &CodegenCx<'ll, 'tcx>, + instance: Instance<'tcx>, +) -> &'ll llvm::Value { + let mangled_fn_name = CString::new(cx.tcx.symbol_name(instance).name) + .expect("error converting function name to C string"); + let llfn = cx.get_fn(instance); + unsafe { llvm::LLVMRustCoverageCreatePGOFuncNameVar(llfn, mangled_fn_name.as_ptr()) } +} + pub(crate) fn write_filenames_section_to_buffer<'a>( filenames: impl IntoIterator, buffer: &RustString, @@ -177,6 +310,7 @@ pub(crate) fn write_mapping_to_buffer( ); } } + pub(crate) fn hash_str(strval: &str) -> u64 { let strval = CString::new(strval).expect("null error converting hashable str to C string"); unsafe { llvm::LLVMRustCoverageHashCString(strval.as_ptr()) } diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/create_scope_map.rs b/compiler/rustc_codegen_llvm/src/debuginfo/create_scope_map.rs index 7673dfb744c5a..c2725b83f50d6 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/create_scope_map.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/create_scope_map.rs @@ -1,4 +1,4 @@ -use super::metadata::{file_metadata, UNKNOWN_COLUMN_NUMBER, UNKNOWN_LINE_NUMBER}; +use super::metadata::file_metadata; use super::utils::DIB; use rustc_codegen_ssa::mir::debuginfo::{DebugScope, FunctionDebugContext}; use rustc_codegen_ssa::traits::*; @@ -102,8 +102,8 @@ fn make_mir_scope( DIB(cx), parent_scope.dbg_scope.unwrap(), file_metadata, - loc.line.unwrap_or(UNKNOWN_LINE_NUMBER), - loc.col.unwrap_or(UNKNOWN_COLUMN_NUMBER), + loc.line, + loc.col, ) }, }; diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/doc.md b/compiler/rustc_codegen_llvm/src/debuginfo/doc.md new file mode 100644 index 0000000000000..f983d09203904 --- /dev/null +++ b/compiler/rustc_codegen_llvm/src/debuginfo/doc.md @@ -0,0 +1,180 @@ +# Debug Info Module + +This module serves the purpose of generating debug symbols. We use LLVM's +[source level debugging](https://llvm.org/docs/SourceLevelDebugging.html) +features for generating the debug information. The general principle is +this: + +Given the right metadata in the LLVM IR, the LLVM code generator is able to +create DWARF debug symbols for the given code. The +[metadata](https://llvm.org/docs/LangRef.html#metadata-type) is structured +much like DWARF *debugging information entries* (DIE), representing type +information such as datatype layout, function signatures, block layout, +variable location and scope information, etc. It is the purpose of this +module to generate correct metadata and insert it into the LLVM IR. + +As the exact format of metadata trees may change between different LLVM +versions, we now use LLVM +[DIBuilder](https://llvm.org/docs/doxygen/html/classllvm_1_1DIBuilder.html) +to create metadata where possible. This will hopefully ease the adaption of +this module to future LLVM versions. + +The public API of the module is a set of functions that will insert the +correct metadata into the LLVM IR when called with the right parameters. +The module is thus driven from an outside client with functions like +`debuginfo::create_local_var_metadata(bx: block, local: &ast::local)`. + +Internally the module will try to reuse already created metadata by +utilizing a cache. The way to get a shared metadata node when needed is +thus to just call the corresponding function in this module: + + let file_metadata = file_metadata(cx, file); + +The function will take care of probing the cache for an existing node for +that exact file path. + +All private state used by the module is stored within either the +CrateDebugContext struct (owned by the CodegenCx) or the +FunctionDebugContext (owned by the FunctionCx). + +This file consists of three conceptual sections: +1. The public interface of the module +2. Module-internal metadata creation functions +3. Minor utility functions + + +## Recursive Types + +Some kinds of types, such as structs and enums can be recursive. That means +that the type definition of some type X refers to some other type which in +turn (transitively) refers to X. This introduces cycles into the type +referral graph. A naive algorithm doing an on-demand, depth-first traversal +of this graph when describing types, can get trapped in an endless loop +when it reaches such a cycle. + +For example, the following simple type for a singly-linked list... + +``` +struct List { + value: i32, + tail: Option>, +} +``` + +will generate the following callstack with a naive DFS algorithm: + +``` +describe(t = List) + describe(t = i32) + describe(t = Option>) + describe(t = Box) + describe(t = List) // at the beginning again... + ... +``` + +To break cycles like these, we use "forward declarations". That is, when +the algorithm encounters a possibly recursive type (any struct or enum), it +immediately creates a type description node and inserts it into the cache +*before* describing the members of the type. This type description is just +a stub (as type members are not described and added to it yet) but it +allows the algorithm to already refer to the type. After the stub is +inserted into the cache, the algorithm continues as before. If it now +encounters a recursive reference, it will hit the cache and does not try to +describe the type anew. + +This behavior is encapsulated in the 'RecursiveTypeDescription' enum, +which represents a kind of continuation, storing all state needed to +continue traversal at the type members after the type has been registered +with the cache. (This implementation approach might be a tad over- +engineered and may change in the future) + + +## Source Locations and Line Information + +In addition to data type descriptions the debugging information must also +allow to map machine code locations back to source code locations in order +to be useful. This functionality is also handled in this module. The +following functions allow to control source mappings: + ++ `set_source_location()` ++ `clear_source_location()` ++ `start_emitting_source_locations()` + +`set_source_location()` allows to set the current source location. All IR +instructions created after a call to this function will be linked to the +given source location, until another location is specified with +`set_source_location()` or the source location is cleared with +`clear_source_location()`. In the later case, subsequent IR instruction +will not be linked to any source location. As you can see, this is a +stateful API (mimicking the one in LLVM), so be careful with source +locations set by previous calls. It's probably best to not rely on any +specific state being present at a given point in code. + +One topic that deserves some extra attention is *function prologues*. At +the beginning of a function's machine code there are typically a few +instructions for loading argument values into allocas and checking if +there's enough stack space for the function to execute. This *prologue* is +not visible in the source code and LLVM puts a special PROLOGUE END marker +into the line table at the first non-prologue instruction of the function. +In order to find out where the prologue ends, LLVM looks for the first +instruction in the function body that is linked to a source location. So, +when generating prologue instructions we have to make sure that we don't +emit source location information until the 'real' function body begins. For +this reason, source location emission is disabled by default for any new +function being codegened and is only activated after a call to the third +function from the list above, `start_emitting_source_locations()`. This +function should be called right before regularly starting to codegen the +top-level block of the given function. + +There is one exception to the above rule: `llvm.dbg.declare` instruction +must be linked to the source location of the variable being declared. For +function parameters these `llvm.dbg.declare` instructions typically occur +in the middle of the prologue, however, they are ignored by LLVM's prologue +detection. The `create_argument_metadata()` and related functions take care +of linking the `llvm.dbg.declare` instructions to the correct source +locations even while source location emission is still disabled, so there +is no need to do anything special with source location handling here. + +## Unique Type Identification + +In order for link-time optimization to work properly, LLVM needs a unique +type identifier that tells it across compilation units which types are the +same as others. This type identifier is created by +`TypeMap::get_unique_type_id_of_type()` using the following algorithm: + +1. Primitive types have their name as ID + +2. Structs, enums and traits have a multipart identifier + + 1. The first part is the SVH (strict version hash) of the crate they + were originally defined in + + 2. The second part is the ast::NodeId of the definition in their + original crate + + 3. The final part is a concatenation of the type IDs of their concrete + type arguments if they are generic types. + +3. Tuple-, pointer-, and function types are structurally identified, which + means that they are equivalent if their component types are equivalent + (i.e., `(i32, i32)` is the same regardless in which crate it is used). + +This algorithm also provides a stable ID for types that are defined in one +crate but instantiated from metadata within another crate. We just have to +take care to always map crate and `NodeId`s back to the original crate +context. + +As a side-effect these unique type IDs also help to solve a problem arising +from lifetime parameters. Since lifetime parameters are completely omitted +in debuginfo, more than one `Ty` instance may map to the same debuginfo +type metadata, that is, some struct `Struct<'a>` may have N instantiations +with different concrete substitutions for `'a`, and thus there will be N +`Ty` instances for the type `Struct<'a>` even though it is not generic +otherwise. Unfortunately this means that we cannot use `ty::type_id()` as +cheap identifier for type metadata -- we have done this in the past, but it +led to unnecessary metadata duplication in the best case and LLVM +assertions in the worst. However, the unique type ID as described above +*can* be used as identifier. Since it is comparatively expensive to +construct, though, `ty::type_id()` is still used additionally as an +optimization for cases where the exact same type has been seen before +(which is most of the time). diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/doc.rs b/compiler/rustc_codegen_llvm/src/debuginfo/doc.rs deleted file mode 100644 index 10dd590652949..0000000000000 --- a/compiler/rustc_codegen_llvm/src/debuginfo/doc.rs +++ /dev/null @@ -1,179 +0,0 @@ -//! # Debug Info Module -//! -//! This module serves the purpose of generating debug symbols. We use LLVM's -//! [source level debugging](https://llvm.org/docs/SourceLevelDebugging.html) -//! features for generating the debug information. The general principle is -//! this: -//! -//! Given the right metadata in the LLVM IR, the LLVM code generator is able to -//! create DWARF debug symbols for the given code. The -//! [metadata](https://llvm.org/docs/LangRef.html#metadata-type) is structured -//! much like DWARF *debugging information entries* (DIE), representing type -//! information such as datatype layout, function signatures, block layout, -//! variable location and scope information, etc. It is the purpose of this -//! module to generate correct metadata and insert it into the LLVM IR. -//! -//! As the exact format of metadata trees may change between different LLVM -//! versions, we now use LLVM -//! [DIBuilder](https://llvm.org/docs/doxygen/html/classllvm_1_1DIBuilder.html) -//! to create metadata where possible. This will hopefully ease the adaption of -//! this module to future LLVM versions. -//! -//! The public API of the module is a set of functions that will insert the -//! correct metadata into the LLVM IR when called with the right parameters. -//! The module is thus driven from an outside client with functions like -//! `debuginfo::create_local_var_metadata(bx: block, local: &ast::local)`. -//! -//! Internally the module will try to reuse already created metadata by -//! utilizing a cache. The way to get a shared metadata node when needed is -//! thus to just call the corresponding function in this module: -//! -//! let file_metadata = file_metadata(cx, file); -//! -//! The function will take care of probing the cache for an existing node for -//! that exact file path. -//! -//! All private state used by the module is stored within either the -//! CrateDebugContext struct (owned by the CodegenCx) or the -//! FunctionDebugContext (owned by the FunctionCx). -//! -//! This file consists of three conceptual sections: -//! 1. The public interface of the module -//! 2. Module-internal metadata creation functions -//! 3. Minor utility functions -//! -//! -//! ## Recursive Types -//! -//! Some kinds of types, such as structs and enums can be recursive. That means -//! that the type definition of some type X refers to some other type which in -//! turn (transitively) refers to X. This introduces cycles into the type -//! referral graph. A naive algorithm doing an on-demand, depth-first traversal -//! of this graph when describing types, can get trapped in an endless loop -//! when it reaches such a cycle. -//! -//! For example, the following simple type for a singly-linked list... -//! -//! ``` -//! struct List { -//! value: i32, -//! tail: Option>, -//! } -//! ``` -//! -//! will generate the following callstack with a naive DFS algorithm: -//! -//! ``` -//! describe(t = List) -//! describe(t = i32) -//! describe(t = Option>) -//! describe(t = Box) -//! describe(t = List) // at the beginning again... -//! ... -//! ``` -//! -//! To break cycles like these, we use "forward declarations". That is, when -//! the algorithm encounters a possibly recursive type (any struct or enum), it -//! immediately creates a type description node and inserts it into the cache -//! *before* describing the members of the type. This type description is just -//! a stub (as type members are not described and added to it yet) but it -//! allows the algorithm to already refer to the type. After the stub is -//! inserted into the cache, the algorithm continues as before. If it now -//! encounters a recursive reference, it will hit the cache and does not try to -//! describe the type anew. -//! -//! This behavior is encapsulated in the 'RecursiveTypeDescription' enum, -//! which represents a kind of continuation, storing all state needed to -//! continue traversal at the type members after the type has been registered -//! with the cache. (This implementation approach might be a tad over- -//! engineered and may change in the future) -//! -//! -//! ## Source Locations and Line Information -//! -//! In addition to data type descriptions the debugging information must also -//! allow to map machine code locations back to source code locations in order -//! to be useful. This functionality is also handled in this module. The -//! following functions allow to control source mappings: -//! -//! + set_source_location() -//! + clear_source_location() -//! + start_emitting_source_locations() -//! -//! `set_source_location()` allows to set the current source location. All IR -//! instructions created after a call to this function will be linked to the -//! given source location, until another location is specified with -//! `set_source_location()` or the source location is cleared with -//! `clear_source_location()`. In the later case, subsequent IR instruction -//! will not be linked to any source location. As you can see, this is a -//! stateful API (mimicking the one in LLVM), so be careful with source -//! locations set by previous calls. It's probably best to not rely on any -//! specific state being present at a given point in code. -//! -//! One topic that deserves some extra attention is *function prologues*. At -//! the beginning of a function's machine code there are typically a few -//! instructions for loading argument values into allocas and checking if -//! there's enough stack space for the function to execute. This *prologue* is -//! not visible in the source code and LLVM puts a special PROLOGUE END marker -//! into the line table at the first non-prologue instruction of the function. -//! In order to find out where the prologue ends, LLVM looks for the first -//! instruction in the function body that is linked to a source location. So, -//! when generating prologue instructions we have to make sure that we don't -//! emit source location information until the 'real' function body begins. For -//! this reason, source location emission is disabled by default for any new -//! function being codegened and is only activated after a call to the third -//! function from the list above, `start_emitting_source_locations()`. This -//! function should be called right before regularly starting to codegen the -//! top-level block of the given function. -//! -//! There is one exception to the above rule: `llvm.dbg.declare` instruction -//! must be linked to the source location of the variable being declared. For -//! function parameters these `llvm.dbg.declare` instructions typically occur -//! in the middle of the prologue, however, they are ignored by LLVM's prologue -//! detection. The `create_argument_metadata()` and related functions take care -//! of linking the `llvm.dbg.declare` instructions to the correct source -//! locations even while source location emission is still disabled, so there -//! is no need to do anything special with source location handling here. -//! -//! ## Unique Type Identification -//! -//! In order for link-time optimization to work properly, LLVM needs a unique -//! type identifier that tells it across compilation units which types are the -//! same as others. This type identifier is created by -//! `TypeMap::get_unique_type_id_of_type()` using the following algorithm: -//! -//! (1) Primitive types have their name as ID -//! (2) Structs, enums and traits have a multipart identifier -//! -//! (1) The first part is the SVH (strict version hash) of the crate they -//! were originally defined in -//! -//! (2) The second part is the ast::NodeId of the definition in their -//! original crate -//! -//! (3) The final part is a concatenation of the type IDs of their concrete -//! type arguments if they are generic types. -//! -//! (3) Tuple-, pointer and function types are structurally identified, which -//! means that they are equivalent if their component types are equivalent -//! (i.e., (i32, i32) is the same regardless in which crate it is used). -//! -//! This algorithm also provides a stable ID for types that are defined in one -//! crate but instantiated from metadata within another crate. We just have to -//! take care to always map crate and `NodeId`s back to the original crate -//! context. -//! -//! As a side-effect these unique type IDs also help to solve a problem arising -//! from lifetime parameters. Since lifetime parameters are completely omitted -//! in debuginfo, more than one `Ty` instance may map to the same debuginfo -//! type metadata, that is, some struct `Struct<'a>` may have N instantiations -//! with different concrete substitutions for `'a`, and thus there will be N -//! `Ty` instances for the type `Struct<'a>` even though it is not generic -//! otherwise. Unfortunately this means that we cannot use `ty::type_id()` as -//! cheap identifier for type metadata -- we have done this in the past, but it -//! led to unnecessary metadata duplication in the best case and LLVM -//! assertions in the worst. However, the unique type ID as described above -//! *can* be used as identifier. Since it is comparatively expensive to -//! construct, though, `ty::type_id()` is still used additionally as an -//! optimization for cases where the exact same type has been seen before -//! (which is most of the time). diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs b/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs index b2e0a4311a73d..e6fa852155b51 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs @@ -1083,9 +1083,9 @@ pub fn compile_unit_metadata( ); } - // Insert `llvm.ident` metadata on the wasm32 targets since that will + // Insert `llvm.ident` metadata on the wasm targets since that will // get hooked up to the "producer" sections `processed-by` information. - if tcx.sess.opts.target_triple.triple().starts_with("wasm32") { + if tcx.sess.target.is_like_wasm { let name_metadata = llvm::LLVMMDStringInContext( debug_context.llcontext, rustc_producer.as_ptr().cast(), @@ -1842,10 +1842,7 @@ impl<'tcx> VariantInfo<'_, 'tcx> { .span; if !span.is_dummy() { let loc = cx.lookup_debug_loc(span.lo()); - return Some(SourceInfo { - file: file_metadata(cx, &loc.file), - line: loc.line.unwrap_or(UNKNOWN_LINE_NUMBER), - }); + return Some(SourceInfo { file: file_metadata(cx, &loc.file), line: loc.line }); } } _ => {} @@ -1965,9 +1962,7 @@ fn prepare_enum_metadata( let discriminant_type_metadata = |discr: Primitive| { let enumerators_metadata: Vec<_> = match enum_type.kind() { - ty::Adt(def, _) => def - .discriminants(tcx) - .zip(&def.variants) + ty::Adt(def, _) => iter::zip(def.discriminants(tcx), &def.variants) .map(|((_, discr), v)| { let name = v.ident.as_str(); let is_unsigned = match discr.ty.kind() { @@ -2339,9 +2334,7 @@ fn compute_type_parameters(cx: &CodegenCx<'ll, 'tcx>, ty: Ty<'tcx>) -> &'ll DIAr if substs.types().next().is_some() { let generics = cx.tcx.generics_of(def.did); let names = get_parameter_names(cx, generics); - let template_params: Vec<_> = substs - .iter() - .zip(names) + let template_params: Vec<_> = iter::zip(substs, names) .filter_map(|(kind, name)| { if let GenericArgKind::Type(ty) = kind.unpack() { let actual_type = @@ -2484,7 +2477,7 @@ pub fn create_global_var_metadata(cx: &CodegenCx<'ll, '_>, def_id: DefId, global let loc = cx.lookup_debug_loc(span.lo()); (file_metadata(cx, &loc.file), loc.line) } else { - (unknown_file_metadata(cx), None) + (unknown_file_metadata(cx), UNKNOWN_LINE_NUMBER) }; let is_local_to_unit = is_node_local_to_unit(cx, def_id); @@ -2507,7 +2500,7 @@ pub fn create_global_var_metadata(cx: &CodegenCx<'ll, '_>, def_id: DefId, global linkage_name.as_ptr().cast(), linkage_name.len(), file_metadata, - line_number.unwrap_or(UNKNOWN_LINE_NUMBER), + line_number, type_metadata, is_local_to_unit, global, diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs b/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs index c28b0d64651e3..e157a38aa03d5 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs @@ -1,5 +1,4 @@ -// See doc.rs for documentation. -mod doc; +#![doc = include_str!("doc.md")] use rustc_codegen_ssa::mir::debuginfo::VariableKind::*; @@ -38,6 +37,7 @@ use rustc_target::abi::{LayoutOf, Primitive, Size}; use libc::c_uint; use smallvec::SmallVec; use std::cell::RefCell; +use std::iter; use tracing::debug; mod create_scope_map; @@ -224,9 +224,9 @@ pub struct DebugLoc { /// Information about the original source file. pub file: Lrc, /// The (1-based) line number. - pub line: Option, + pub line: u32, /// The (1-based) column number. - pub col: Option, + pub col: u32, } impl CodegenCx<'ll, '_> { @@ -243,16 +243,16 @@ impl CodegenCx<'ll, '_> { let line = (line + 1) as u32; let col = (pos - line_pos).to_u32() + 1; - (file, Some(line), Some(col)) + (file, line, col) } - Err(file) => (file, None, None), + Err(file) => (file, UNKNOWN_LINE_NUMBER, UNKNOWN_COLUMN_NUMBER), }; // For MSVC, omit the column number. // Otherwise, emit it. This mimics clang behaviour. // See discussion in https://github.com/rust-lang/rust/issues/42921 if self.sess().target.is_like_msvc { - DebugLoc { file, line, col: None } + DebugLoc { file, line, col: UNKNOWN_COLUMN_NUMBER } } else { DebugLoc { file, line, col } } @@ -358,9 +358,9 @@ impl DebugInfoMethods<'tcx> for CodegenCx<'ll, 'tcx> { linkage_name.as_ptr().cast(), linkage_name.len(), file_metadata, - loc.line.unwrap_or(UNKNOWN_LINE_NUMBER), + loc.line, function_type_metadata, - scope_line.unwrap_or(UNKNOWN_LINE_NUMBER), + scope_line, flags, spflags, maybe_definition_llfn, @@ -449,9 +449,7 @@ impl DebugInfoMethods<'tcx> for CodegenCx<'ll, 'tcx> { // Again, only create type information if full debuginfo is enabled let template_params: Vec<_> = if cx.sess().opts.debuginfo == DebugInfo::Full { let names = get_parameter_names(cx, generics); - substs - .iter() - .zip(names) + iter::zip(substs, names) .filter_map(|(kind, name)| { if let GenericArgKind::Type(ty) = kind.unpack() { let actual_type = @@ -550,14 +548,7 @@ impl DebugInfoMethods<'tcx> for CodegenCx<'ll, 'tcx> { ) -> &'ll DILocation { let DebugLoc { line, col, .. } = self.lookup_debug_loc(span.lo()); - unsafe { - llvm::LLVMRustDIBuilderCreateDebugLocation( - line.unwrap_or(UNKNOWN_LINE_NUMBER), - col.unwrap_or(UNKNOWN_COLUMN_NUMBER), - scope, - inlined_at, - ) - } + unsafe { llvm::LLVMRustDIBuilderCreateDebugLocation(line, col, scope, inlined_at) } } fn create_vtable_metadata(&self, ty: Ty<'tcx>, vtable: Self::Value) { @@ -606,7 +597,7 @@ impl DebugInfoMethods<'tcx> for CodegenCx<'ll, 'tcx> { name.as_ptr().cast(), name.len(), file_metadata, - loc.line.unwrap_or(UNKNOWN_LINE_NUMBER), + loc.line, type_metadata, true, DIFlags::FlagZero, diff --git a/compiler/rustc_codegen_llvm/src/intrinsic.rs b/compiler/rustc_codegen_llvm/src/intrinsic.rs index 668daa52ed262..fc6c1abf4af56 100644 --- a/compiler/rustc_codegen_llvm/src/intrinsic.rs +++ b/compiler/rustc_codegen_llvm/src/intrinsic.rs @@ -334,8 +334,11 @@ impl IntrinsicCallMethods<'tcx> for Builder<'a, 'll, 'tcx> { self.call(expect, &[cond, self.const_bool(expected)], None) } - fn sideeffect(&mut self, unconditional: bool) { - if unconditional || self.tcx.sess.opts.debugging_opts.insert_sideeffect { + fn sideeffect(&mut self) { + // This kind of check would make a ton of sense in the caller, but currently the only + // caller of this function is in `rustc_codegen_ssa`, which is agnostic to whether LLVM + // codegen backend being used, and so is unable to check the LLVM version. + if unsafe { llvm::LLVMRustVersionMajor() } < 12 { let fnname = self.get_intrinsic(&("llvm.sideeffect")); self.call(fnname, &[], None); } @@ -390,7 +393,6 @@ fn codegen_msvc_try( ) { let llfn = get_rust_try_fn(bx, &mut |mut bx| { bx.set_personality_fn(bx.eh_personality()); - bx.sideeffect(false); let mut normal = bx.build_sibling_block("normal"); let mut catchswitch = bx.build_sibling_block("catchswitch"); @@ -552,9 +554,6 @@ fn codegen_gnu_try( // (%ptr, _) = landingpad // call %catch_func(%data, %ptr) // ret 1 - - bx.sideeffect(false); - let mut then = bx.build_sibling_block("then"); let mut catch = bx.build_sibling_block("catch"); @@ -614,9 +613,6 @@ fn codegen_emcc_try( // %catch_data[1] = %is_rust_panic // call %catch_func(%data, %catch_data) // ret 1 - - bx.sideeffect(false); - let mut then = bx.build_sibling_block("then"); let mut catch = bx.build_sibling_block("catch"); @@ -1057,46 +1053,48 @@ fn generic_simd_intrinsic( let vec_ty = bx.type_vector(elem_ty, in_len); let (intr_name, fn_ty) = match name { - sym::simd_fsqrt => ("sqrt", bx.type_func(&[vec_ty], vec_ty)), - sym::simd_fsin => ("sin", bx.type_func(&[vec_ty], vec_ty)), - sym::simd_fcos => ("cos", bx.type_func(&[vec_ty], vec_ty)), - sym::simd_fabs => ("fabs", bx.type_func(&[vec_ty], vec_ty)), - sym::simd_floor => ("floor", bx.type_func(&[vec_ty], vec_ty)), sym::simd_ceil => ("ceil", bx.type_func(&[vec_ty], vec_ty)), - sym::simd_fexp => ("exp", bx.type_func(&[vec_ty], vec_ty)), + sym::simd_fabs => ("fabs", bx.type_func(&[vec_ty], vec_ty)), + sym::simd_fcos => ("cos", bx.type_func(&[vec_ty], vec_ty)), sym::simd_fexp2 => ("exp2", bx.type_func(&[vec_ty], vec_ty)), + sym::simd_fexp => ("exp", bx.type_func(&[vec_ty], vec_ty)), sym::simd_flog10 => ("log10", bx.type_func(&[vec_ty], vec_ty)), sym::simd_flog2 => ("log2", bx.type_func(&[vec_ty], vec_ty)), sym::simd_flog => ("log", bx.type_func(&[vec_ty], vec_ty)), + sym::simd_floor => ("floor", bx.type_func(&[vec_ty], vec_ty)), + sym::simd_fma => ("fma", bx.type_func(&[vec_ty, vec_ty, vec_ty], vec_ty)), sym::simd_fpowi => ("powi", bx.type_func(&[vec_ty, bx.type_i32()], vec_ty)), sym::simd_fpow => ("pow", bx.type_func(&[vec_ty, vec_ty], vec_ty)), - sym::simd_fma => ("fma", bx.type_func(&[vec_ty, vec_ty, vec_ty], vec_ty)), + sym::simd_fsin => ("sin", bx.type_func(&[vec_ty], vec_ty)), + sym::simd_fsqrt => ("sqrt", bx.type_func(&[vec_ty], vec_ty)), + sym::simd_round => ("round", bx.type_func(&[vec_ty], vec_ty)), + sym::simd_trunc => ("trunc", bx.type_func(&[vec_ty], vec_ty)), _ => return_error!("unrecognized intrinsic `{}`", name), }; - let llvm_name = &format!("llvm.{0}.v{1}{2}", intr_name, in_len, elem_ty_str); let f = bx.declare_cfn(&llvm_name, llvm::UnnamedAddr::No, fn_ty); let c = bx.call(f, &args.iter().map(|arg| arg.immediate()).collect::>(), None); - unsafe { llvm::LLVMRustSetHasUnsafeAlgebra(c) }; Ok(c) } if std::matches!( name, - sym::simd_fsqrt - | sym::simd_fsin - | sym::simd_fcos + sym::simd_ceil | sym::simd_fabs - | sym::simd_floor - | sym::simd_ceil - | sym::simd_fexp + | sym::simd_fcos | sym::simd_fexp2 + | sym::simd_fexp | sym::simd_flog10 | sym::simd_flog2 | sym::simd_flog - | sym::simd_fpowi - | sym::simd_fpow + | sym::simd_floor | sym::simd_fma + | sym::simd_fpow + | sym::simd_fpowi + | sym::simd_fsin + | sym::simd_fsqrt + | sym::simd_round + | sym::simd_trunc ) { return simd_simple_float_intrinsic(name, in_elem, in_ty, in_len, bx, span, args); } @@ -1632,7 +1630,7 @@ unsupported {} from `{}` with element `{}` of size `{}` to `{}`"#, out_elem ); } - macro_rules! arith { + macro_rules! arith_binary { ($($name: ident: $($($p: ident),* => $call: ident),*;)*) => { $(if name == sym::$name { match in_elem.kind() { @@ -1648,7 +1646,7 @@ unsupported {} from `{}` with element `{}` of size `{}` to `{}`"#, })* } } - arith! { + arith_binary! { simd_add: Uint, Int => add, Float => fadd; simd_sub: Uint, Int => sub, Float => fsub; simd_mul: Uint, Int => mul, Float => fmul; @@ -1663,6 +1661,25 @@ unsupported {} from `{}` with element `{}` of size `{}` to `{}`"#, simd_fmin: Float => minnum; } + macro_rules! arith_unary { + ($($name: ident: $($($p: ident),* => $call: ident),*;)*) => { + $(if name == sym::$name { + match in_elem.kind() { + $($(ty::$p(_))|* => { + return Ok(bx.$call(args[0].immediate())) + })* + _ => {}, + } + require!(false, + "unsupported operation on `{}` with element `{}`", + in_ty, + in_elem) + })* + } + } + arith_unary! { + simd_neg: Int => neg, Float => fneg; + } if name == sym::simd_saturating_add || name == sym::simd_saturating_sub { let lhs = args[0].immediate(); diff --git a/compiler/rustc_codegen_llvm/src/lib.rs b/compiler/rustc_codegen_llvm/src/lib.rs index d11c1592f99d1..5ca4b226c38fb 100644 --- a/compiler/rustc_codegen_llvm/src/lib.rs +++ b/compiler/rustc_codegen_llvm/src/lib.rs @@ -8,10 +8,12 @@ #![feature(bool_to_option)] #![feature(const_cstr_unchecked)] #![feature(crate_visibility_modifier)] +#![feature(extended_key_value_attributes)] #![feature(extern_types)] #![feature(in_band_lifetimes)] +#![feature(iter_zip)] #![feature(nll)] -#![feature(or_patterns)] +#![cfg_attr(bootstrap, feature(or_patterns))] #![recursion_limit = "256"] use back::write::{create_informational_target_machine, create_target_machine}; diff --git a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs index e6d60044c84b4..32b1526f6e44c 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs @@ -190,33 +190,6 @@ pub enum RealPredicate { RealPredicateTrue = 15, } -impl RealPredicate { - pub fn from_generic(realpred: rustc_codegen_ssa::common::RealPredicate) -> Self { - match realpred { - rustc_codegen_ssa::common::RealPredicate::RealPredicateFalse => { - RealPredicate::RealPredicateFalse - } - rustc_codegen_ssa::common::RealPredicate::RealOEQ => RealPredicate::RealOEQ, - rustc_codegen_ssa::common::RealPredicate::RealOGT => RealPredicate::RealOGT, - rustc_codegen_ssa::common::RealPredicate::RealOGE => RealPredicate::RealOGE, - rustc_codegen_ssa::common::RealPredicate::RealOLT => RealPredicate::RealOLT, - rustc_codegen_ssa::common::RealPredicate::RealOLE => RealPredicate::RealOLE, - rustc_codegen_ssa::common::RealPredicate::RealONE => RealPredicate::RealONE, - rustc_codegen_ssa::common::RealPredicate::RealORD => RealPredicate::RealORD, - rustc_codegen_ssa::common::RealPredicate::RealUNO => RealPredicate::RealUNO, - rustc_codegen_ssa::common::RealPredicate::RealUEQ => RealPredicate::RealUEQ, - rustc_codegen_ssa::common::RealPredicate::RealUGT => RealPredicate::RealUGT, - rustc_codegen_ssa::common::RealPredicate::RealUGE => RealPredicate::RealUGE, - rustc_codegen_ssa::common::RealPredicate::RealULT => RealPredicate::RealULT, - rustc_codegen_ssa::common::RealPredicate::RealULE => RealPredicate::RealULE, - rustc_codegen_ssa::common::RealPredicate::RealUNE => RealPredicate::RealUNE, - rustc_codegen_ssa::common::RealPredicate::RealPredicateTrue => { - RealPredicate::RealPredicateTrue - } - } - } -} - /// LLVMTypeKind #[derive(Copy, Clone, PartialEq, Debug)] #[repr(C)] @@ -711,7 +684,7 @@ pub mod coverageinfo { } impl CounterMappingRegion { - pub fn code_region( + crate fn code_region( counter: coverage_map::Counter, file_id: u32, start_line: u32, @@ -731,7 +704,10 @@ pub mod coverageinfo { } } - pub fn expansion_region( + // This function might be used in the future; the LLVM API is still evolving, as is coverage + // support. + #[allow(dead_code)] + crate fn expansion_region( file_id: u32, expanded_file_id: u32, start_line: u32, @@ -751,7 +727,10 @@ pub mod coverageinfo { } } - pub fn skipped_region( + // This function might be used in the future; the LLVM API is still evolving, as is coverage + // support. + #[allow(dead_code)] + crate fn skipped_region( file_id: u32, start_line: u32, start_col: u32, @@ -770,7 +749,10 @@ pub mod coverageinfo { } } - pub fn gap_region( + // This function might be used in the future; the LLVM API is still evolving, as is coverage + // support. + #[allow(dead_code)] + crate fn gap_region( counter: coverage_map::Counter, file_id: u32, start_line: u32, @@ -1031,6 +1013,7 @@ extern "C" { pub fn LLVMSetSection(Global: &Value, Section: *const c_char); pub fn LLVMRustGetVisibility(Global: &Value) -> Visibility; pub fn LLVMRustSetVisibility(Global: &Value, Viz: Visibility); + pub fn LLVMRustSetDSOLocal(Global: &Value, is_dso_local: bool); pub fn LLVMGetAlignment(Global: &Value) -> c_uint; pub fn LLVMSetAlignment(Global: &Value, Bytes: c_uint); pub fn LLVMSetDLLStorageClass(V: &Value, C: DLLStorageClass); @@ -1371,7 +1354,7 @@ extern "C" { pub fn LLVMBuildNeg(B: &Builder<'a>, V: &'a Value, Name: *const c_char) -> &'a Value; pub fn LLVMBuildFNeg(B: &Builder<'a>, V: &'a Value, Name: *const c_char) -> &'a Value; pub fn LLVMBuildNot(B: &Builder<'a>, V: &'a Value, Name: *const c_char) -> &'a Value; - pub fn LLVMRustSetHasUnsafeAlgebra(Instr: &Value); + pub fn LLVMRustSetFastMath(Instr: &Value); // Memory pub fn LLVMBuildAlloca(B: &Builder<'a>, Ty: &'a Type, Name: *const c_char) -> &'a Value; @@ -2145,7 +2128,13 @@ extern "C" { pub fn LLVMRustHasFeature(T: &TargetMachine, s: *const c_char) -> bool; pub fn LLVMRustPrintTargetCPUs(T: &TargetMachine); - pub fn LLVMRustPrintTargetFeatures(T: &TargetMachine); + pub fn LLVMRustGetTargetFeaturesCount(T: &TargetMachine) -> size_t; + pub fn LLVMRustGetTargetFeature( + T: &TargetMachine, + Index: size_t, + Feature: &mut *const c_char, + Desc: &mut *const c_char, + ); pub fn LLVMRustGetHostCPUName(len: *mut usize) -> *const c_char; pub fn LLVMRustCreateTargetMachine( @@ -2326,6 +2315,7 @@ extern "C" { pub fn LLVMRustUnsetComdat(V: &Value); pub fn LLVMRustSetModulePICLevel(M: &Module); pub fn LLVMRustSetModulePIELevel(M: &Module); + pub fn LLVMRustSetModuleCodeModel(M: &Module, Model: CodeModel); pub fn LLVMRustModuleBufferCreate(M: &Module) -> &'static mut ModuleBuffer; pub fn LLVMRustModuleBufferPtr(p: &ModuleBuffer) -> *const u8; pub fn LLVMRustModuleBufferLen(p: &ModuleBuffer) -> usize; diff --git a/compiler/rustc_codegen_llvm/src/llvm_util.rs b/compiler/rustc_codegen_llvm/src/llvm_util.rs index 544ef38c12c6d..b44553e4f6d3b 100644 --- a/compiler/rustc_codegen_llvm/src/llvm_util.rs +++ b/compiler/rustc_codegen_llvm/src/llvm_util.rs @@ -10,6 +10,7 @@ use rustc_span::symbol::Symbol; use rustc_target::spec::{MergeFunctions, PanicStrategy}; use std::ffi::{CStr, CString}; +use std::ptr; use std::slice; use std::str; use std::sync::atomic::{AtomicBool, Ordering}; @@ -98,6 +99,9 @@ unsafe fn configure_llvm(sess: &Session) { // during inlining. Unfortunately these may block other optimizations. add("-preserve-alignment-assumptions-during-inlining=false", false); + // Use non-zero `import-instr-limit` multiplier for cold callsites. + add("-import-cold-multiplier=0.1", false); + for arg in sess_args { add(&(*arg), true); } @@ -189,15 +193,77 @@ pub fn print_passes() { } } +fn llvm_target_features(tm: &llvm::TargetMachine) -> Vec<(&str, &str)> { + let len = unsafe { llvm::LLVMRustGetTargetFeaturesCount(tm) }; + let mut ret = Vec::with_capacity(len); + for i in 0..len { + unsafe { + let mut feature = ptr::null(); + let mut desc = ptr::null(); + llvm::LLVMRustGetTargetFeature(tm, i, &mut feature, &mut desc); + if feature.is_null() || desc.is_null() { + bug!("LLVM returned a `null` target feature string"); + } + let feature = CStr::from_ptr(feature).to_str().unwrap_or_else(|e| { + bug!("LLVM returned a non-utf8 feature string: {}", e); + }); + let desc = CStr::from_ptr(desc).to_str().unwrap_or_else(|e| { + bug!("LLVM returned a non-utf8 feature string: {}", e); + }); + ret.push((feature, desc)); + } + } + ret +} + +fn print_target_features(sess: &Session, tm: &llvm::TargetMachine) { + let mut target_features = llvm_target_features(tm); + let mut rustc_target_features = supported_target_features(sess) + .iter() + .filter_map(|(feature, _gate)| { + let llvm_feature = to_llvm_feature(sess, *feature); + // LLVM asserts that these are sorted. LLVM and Rust both use byte comparison for these strings. + target_features.binary_search_by_key(&llvm_feature, |(f, _d)| *f).ok().map(|index| { + let (_f, desc) = target_features.remove(index); + (*feature, desc) + }) + }) + .collect::>(); + rustc_target_features.extend_from_slice(&[( + "crt-static", + "Enables C Run-time Libraries to be statically linked", + )]); + let max_feature_len = target_features + .iter() + .chain(rustc_target_features.iter()) + .map(|(feature, _desc)| feature.len()) + .max() + .unwrap_or(0); + + println!("Features supported by rustc for this target:"); + for (feature, desc) in &rustc_target_features { + println!(" {1:0$} - {2}.", max_feature_len, feature, desc); + } + println!("\nCode-generation features supported by LLVM for this target:"); + for (feature, desc) in &target_features { + println!(" {1:0$} - {2}.", max_feature_len, feature, desc); + } + if target_features.len() == 0 { + println!(" Target features listing is not supported by this LLVM version."); + } + println!("\nUse +feature to enable a feature, or -feature to disable it."); + println!("For example, rustc -C target-cpu=mycpu -C target-feature=+feature1,-feature2\n"); + println!("Code-generation features cannot be used in cfg or #[target_feature],"); + println!("and may be renamed or removed in a future version of LLVM or rustc.\n"); +} + pub(crate) fn print(req: PrintRequest, sess: &Session) { require_inited(); let tm = create_informational_target_machine(sess); - unsafe { - match req { - PrintRequest::TargetCPUs => llvm::LLVMRustPrintTargetCPUs(tm), - PrintRequest::TargetFeatures => llvm::LLVMRustPrintTargetFeatures(tm), - _ => bug!("rustc_codegen_llvm can't handle print request: {:?}", req), - } + match req { + PrintRequest::TargetCPUs => unsafe { llvm::LLVMRustPrintTargetCPUs(tm) }, + PrintRequest::TargetFeatures => print_target_features(sess, tm), + _ => bug!("rustc_codegen_llvm can't handle print request: {:?}", req), } } @@ -218,13 +284,39 @@ pub fn target_cpu(sess: &Session) -> &str { handle_native(name) } -pub fn handle_native_features(sess: &Session) -> Vec { +/// The list of LLVM features computed from CLI flags (`-Ctarget-cpu`, `-Ctarget-feature`, +/// `--target` and similar). +// FIXME(nagisa): Cache the output of this somehow? Maybe make this a query? We're calling this +// for every function that has `#[target_feature]` on it. The global features won't change between +// the functions; only crates, maybe… +pub fn llvm_global_features(sess: &Session) -> Vec { + // FIXME(nagisa): this should definitely be available more centrally and to other codegen backends. + /// These features control behaviour of rustc rather than llvm. + const RUSTC_SPECIFIC_FEATURES: &[&str] = &["crt-static"]; + + // Features that come earlier are overriden by conflicting features later in the string. + // Typically we'll want more explicit settings to override the implicit ones, so: + // + // * Features from -Ctarget-cpu=*; are overriden by [^1] + // * Features implied by --target; are overriden by + // * Features from -Ctarget-feature; are overriden by + // * function specific features. + // + // [^1]: target-cpu=native is handled here, other target-cpu values are handled implicitly + // through LLVM TargetMachine implementation. + // + // FIXME(nagisa): it isn't clear what's the best interaction between features implied by + // `-Ctarget-cpu` and `--target` are. On one hand, you'd expect CLI arguments to always + // override anything that's implicit, so e.g. when there's no `--target` flag, features implied + // the host target are overriden by `-Ctarget-cpu=*`. On the other hand, what about when both + // `--target` and `-Ctarget-cpu=*` are specified? Both then imply some target features and both + // flags are specified by the user on the CLI. It isn't as clear-cut which order of precedence + // should be taken in cases like these. + let mut features = vec![]; + + // -Ctarget-cpu=native match sess.opts.cg.target_cpu { - Some(ref s) => { - if s != "native" { - return vec![]; - } - + Some(ref s) if s == "native" => { let features_string = unsafe { let ptr = llvm::LLVMGetHostCPUFeatures(); let features_string = if !ptr.is_null() { @@ -242,11 +334,31 @@ pub fn handle_native_features(sess: &Session) -> Vec { features_string }; - - features_string.split(",").map(|s| s.to_owned()).collect() + features.extend(features_string.split(",").map(String::from)); } - None => vec![], - } + Some(_) | None => {} + }; + + // Features implied by an implicit or explicit `--target`. + features.extend( + sess.target + .features + .split(',') + .filter(|f| !f.is_empty() && !RUSTC_SPECIFIC_FEATURES.iter().any(|s| f.contains(s))) + .map(String::from), + ); + + // -Ctarget-features + features.extend( + sess.opts + .cg + .target_feature + .split(',') + .filter(|f| !f.is_empty() && !RUSTC_SPECIFIC_FEATURES.iter().any(|s| f.contains(s))) + .map(String::from), + ); + + features } pub fn tune_cpu(sess: &Session) -> Option<&str> { diff --git a/compiler/rustc_codegen_llvm/src/mono_item.rs b/compiler/rustc_codegen_llvm/src/mono_item.rs index 992e83d08fcae..fc1f364e9c6bc 100644 --- a/compiler/rustc_codegen_llvm/src/mono_item.rs +++ b/compiler/rustc_codegen_llvm/src/mono_item.rs @@ -10,7 +10,9 @@ pub use rustc_middle::mir::mono::MonoItem; use rustc_middle::mir::mono::{Linkage, Visibility}; use rustc_middle::ty::layout::FnAbiExt; use rustc_middle::ty::{self, Instance, TypeFoldable}; +use rustc_session::config::CrateType; use rustc_target::abi::LayoutOf; +use rustc_target::spec::RelocModel; use tracing::debug; impl PreDefineMethods<'tcx> for CodegenCx<'ll, 'tcx> { @@ -35,6 +37,9 @@ impl PreDefineMethods<'tcx> for CodegenCx<'ll, 'tcx> { unsafe { llvm::LLVMRustSetLinkage(g, base::linkage_to_llvm(linkage)); llvm::LLVMRustSetVisibility(g, base::visibility_to_llvm(visibility)); + if self.should_assume_dso_local(linkage, visibility) { + llvm::LLVMRustSetDSOLocal(g, true); + } } self.instances.borrow_mut().insert(instance, g); @@ -79,6 +84,42 @@ impl PreDefineMethods<'tcx> for CodegenCx<'ll, 'tcx> { attributes::from_fn_attrs(self, lldecl, instance); + unsafe { + if self.should_assume_dso_local(linkage, visibility) { + llvm::LLVMRustSetDSOLocal(lldecl, true); + } + } + self.instances.borrow_mut().insert(instance, lldecl); } } + +impl CodegenCx<'ll, 'tcx> { + /// Whether a definition (NB: not declaration!) can be assumed to be local to a group of + /// libraries that form a single DSO or executable. + pub(crate) unsafe fn should_assume_dso_local( + &self, + linkage: Linkage, + visibility: Visibility, + ) -> bool { + if matches!(linkage, Linkage::Internal | Linkage::Private) { + return true; + } + + if visibility != Visibility::Default && linkage != Linkage::ExternalWeak { + return true; + } + + // Static relocation model should force copy relocations everywhere. + if self.tcx.sess.relocation_model() == RelocModel::Static { + return true; + } + + // Symbols from executables can't really be imported any further. + if self.tcx.sess.crate_types().iter().all(|ty| *ty == CrateType::Executable) { + return true; + } + + return false; + } +} diff --git a/compiler/rustc_codegen_ssa/Cargo.toml b/compiler/rustc_codegen_ssa/Cargo.toml index 5e2f01b2c9da8..12f3b52a25049 100644 --- a/compiler/rustc_codegen_ssa/Cargo.toml +++ b/compiler/rustc_codegen_ssa/Cargo.toml @@ -11,10 +11,9 @@ test = false bitflags = "1.2.1" cc = "1.0.1" itertools = "0.9" -memmap = "0.7" tracing = "0.1" libc = "0.2.50" -jobserver = "0.1.11" +jobserver = "0.1.22" tempfile = "3.1" pathdiff = "0.2.0" diff --git a/compiler/rustc_codegen_ssa/src/back/link.rs b/compiler/rustc_codegen_ssa/src/back/link.rs index caa6a6a8e3a26..ea75943d6f314 100644 --- a/compiler/rustc_codegen_ssa/src/back/link.rs +++ b/compiler/rustc_codegen_ssa/src/back/link.rs @@ -6,7 +6,7 @@ use rustc_hir::def_id::CrateNum; use rustc_middle::middle::cstore::{EncodedMetadata, LibSource}; use rustc_middle::middle::dependency_format::Linkage; use rustc_session::config::{self, CFGuard, CrateType, DebugInfo}; -use rustc_session::config::{OutputFilenames, OutputType, PrintRequest, SanitizerSet}; +use rustc_session::config::{OutputFilenames, OutputType, PrintRequest}; use rustc_session::output::{check_file_is_writeable, invalid_output_for_target, out_filename}; use rustc_session::search_paths::PathKind; use rustc_session::utils::NativeLibKind; @@ -16,7 +16,7 @@ use rustc_session::{filesearch, Session}; use rustc_span::symbol::Symbol; use rustc_target::spec::crt_objects::{CrtObjects, CrtObjectsFallback}; use rustc_target::spec::{LinkOutputKind, LinkerFlavor, LldFlavor, SplitDebuginfo}; -use rustc_target::spec::{PanicStrategy, RelocModel, RelroLevel, Target}; +use rustc_target::spec::{PanicStrategy, RelocModel, RelroLevel, SanitizerSet, Target}; use super::archive::ArchiveBuilder; use super::command::Command; @@ -711,7 +711,7 @@ fn link_natively<'a, B: ArchiveBuilder<'a>>( status.signal() == Some(libc::SIGILL) } - #[cfg(windows)] + #[cfg(not(unix))] fn is_illegal_instruction(_status: &ExitStatus) -> bool { false } @@ -922,28 +922,20 @@ fn link_sanitizer_runtime(sess: &Session, linker: &mut dyn Linker, name: &str) { .map(|channel| format!("-{}", channel)) .unwrap_or_default(); - match sess.opts.target_triple.triple() { - "aarch64-apple-darwin" | "x86_64-apple-darwin" => { - // On Apple platforms, the sanitizer is always built as a dylib, and - // LLVM will link to `@rpath/*.dylib`, so we need to specify an - // rpath to the library as well (the rpath should be absolute, see - // PR #41352 for details). - let filename = format!("rustc{}_rt.{}", channel, name); - let path = find_sanitizer_runtime(&sess, &filename); - let rpath = path.to_str().expect("non-utf8 component in path"); - linker.args(&["-Wl,-rpath", "-Xlinker", rpath]); - linker.link_dylib(Symbol::intern(&filename)); - } - "aarch64-fuchsia" - | "aarch64-unknown-linux-gnu" - | "x86_64-fuchsia" - | "x86_64-unknown-freebsd" - | "x86_64-unknown-linux-gnu" => { - let filename = format!("librustc{}_rt.{}.a", channel, name); - let path = find_sanitizer_runtime(&sess, &filename).join(&filename); - linker.link_whole_rlib(&path); - } - _ => {} + if sess.target.is_like_osx { + // On Apple platforms, the sanitizer is always built as a dylib, and + // LLVM will link to `@rpath/*.dylib`, so we need to specify an + // rpath to the library as well (the rpath should be absolute, see + // PR #41352 for details). + let filename = format!("rustc{}_rt.{}", channel, name); + let path = find_sanitizer_runtime(&sess, &filename); + let rpath = path.to_str().expect("non-utf8 component in path"); + linker.args(&["-Wl,-rpath", "-Xlinker", rpath]); + linker.link_dylib(Symbol::intern(&filename)); + } else { + let filename = format!("librustc{}_rt.{}.a", channel, name); + let path = find_sanitizer_runtime(&sess, &filename).join(&filename); + linker.link_whole_rlib(&path); } } @@ -1198,7 +1190,7 @@ fn exec_linker( flush_linked_file(&output, out_filename)?; return output; - #[cfg(unix)] + #[cfg(not(windows))] fn flush_linked_file(_: &io::Result, _: &Path) -> io::Result<()> { Ok(()) } @@ -1238,6 +1230,11 @@ fn exec_linker( err.raw_os_error() == Some(ERROR_FILENAME_EXCED_RANGE) } + #[cfg(not(any(unix, windows)))] + fn command_line_too_big(_: &io::Error) -> bool { + false + } + struct Escape<'a> { arg: &'a str, is_like_msvc: bool, @@ -1414,15 +1411,10 @@ fn add_link_script(cmd: &mut dyn Linker, sess: &Session, tmpdir: &Path, crate_ty } } -/// Add arbitrary "user defined" args defined from command line and by `#[link_args]` attributes. +/// Add arbitrary "user defined" args defined from command line. /// FIXME: Determine where exactly these args need to be inserted. -fn add_user_defined_link_args( - cmd: &mut dyn Linker, - sess: &Session, - codegen_results: &CodegenResults, -) { +fn add_user_defined_link_args(cmd: &mut dyn Linker, sess: &Session) { cmd.args(&sess.opts.cg.link_args); - cmd.args(&*codegen_results.crate_info.link_args); } /// Add arbitrary "late link" args defined by the target spec. @@ -1646,6 +1638,16 @@ fn linker_with_args<'a, B: ArchiveBuilder<'a>>( cmd.add_eh_frame_header(); } + // NO-OPT-OUT, OBJECT-FILES-NO, AUDIT-ORDER + // Make the binary compatible with data execution prevention schemes. + cmd.add_no_exec(); + + // NO-OPT-OUT, OBJECT-FILES-NO + // Avoid linking to dynamic libraries unless they satisfy some undefined symbols + // at the point at which they are specified on the command line. + // Must be passed before any dynamic libraries. + cmd.add_as_needed(); + // NO-OPT-OUT, OBJECT-FILES-NO if crt_objects_fallback { cmd.no_crt_objects(); @@ -1741,7 +1743,7 @@ fn linker_with_args<'a, B: ArchiveBuilder<'a>>( ); // OBJECT-FILES-NO, AUDIT-ORDER - if sess.opts.cg.profile_generate.enabled() || sess.opts.debugging_opts.instrument_coverage { + if sess.opts.cg.profile_generate.enabled() || sess.instrument_coverage() { cmd.pgo_gen(); } @@ -1754,7 +1756,7 @@ fn linker_with_args<'a, B: ArchiveBuilder<'a>>( add_rpath_args(cmd, sess, codegen_results, out_filename); // OBJECT-FILES-MAYBE, CUSTOMIZATION-POINT - add_user_defined_link_args(cmd, sess, codegen_results); + add_user_defined_link_args(cmd, sess); // NO-OPT-OUT, OBJECT-FILES-NO, AUDIT-ORDER cmd.finalize(); diff --git a/compiler/rustc_codegen_ssa/src/back/linker.rs b/compiler/rustc_codegen_ssa/src/back/linker.rs index bb35e7ec89439..77d8ab49ff258 100644 --- a/compiler/rustc_codegen_ssa/src/back/linker.rs +++ b/compiler/rustc_codegen_ssa/src/back/linker.rs @@ -130,6 +130,8 @@ pub trait Linker { fn group_end(&mut self); fn linker_plugin_lto(&mut self); fn add_eh_frame_header(&mut self) {} + fn add_no_exec(&mut self) {} + fn add_as_needed(&mut self) {} fn finalize(&mut self); } @@ -184,7 +186,7 @@ impl<'a> GccLinker<'a> { // * On OSX they have their own linker, not binutils' // * For WebAssembly the only functional linker is LLD, which doesn't // support hint flags - !self.sess.target.is_like_osx && self.sess.target.arch != "wasm32" + !self.sess.target.is_like_osx && !self.sess.target.is_like_wasm } // Some platforms take hints about whether a library is static or dynamic. @@ -641,6 +643,20 @@ impl<'a> Linker for GccLinker<'a> { fn add_eh_frame_header(&mut self) { self.linker_arg("--eh-frame-hdr"); } + + fn add_no_exec(&mut self) { + if self.sess.target.is_like_windows { + self.linker_arg("--nxcompat"); + } else if self.sess.target.linker_is_gnu { + self.linker_arg("-znoexecstack"); + } + } + + fn add_as_needed(&mut self) { + if self.sess.target.linker_is_gnu { + self.linker_arg("--as-needed"); + } + } } pub struct MsvcLinker<'a> { @@ -878,6 +894,10 @@ impl<'a> Linker for MsvcLinker<'a> { fn linker_plugin_lto(&mut self) { // Do nothing } + + fn add_no_exec(&mut self) { + self.cmd.arg("/NXCOMPAT"); + } } pub struct EmLinker<'a> { diff --git a/compiler/rustc_codegen_ssa/src/back/lto.rs b/compiler/rustc_codegen_ssa/src/back/lto.rs index 0d7f4447696f2..0ff05229466ac 100644 --- a/compiler/rustc_codegen_ssa/src/back/lto.rs +++ b/compiler/rustc_codegen_ssa/src/back/lto.rs @@ -2,6 +2,7 @@ use super::write::CodegenContext; use crate::traits::*; use crate::ModuleCodegen; +use rustc_data_structures::memmap::Mmap; use rustc_errors::FatalError; use std::ffi::CString; @@ -93,7 +94,7 @@ impl LtoModuleCodegen { pub enum SerializedModule { Local(M), FromRlib(Vec), - FromUncompressedFile(memmap::Mmap), + FromUncompressedFile(Mmap), } impl SerializedModule { diff --git a/compiler/rustc_codegen_ssa/src/back/rpath.rs b/compiler/rustc_codegen_ssa/src/back/rpath.rs index 005d2efdd3b26..5f21046b05e47 100644 --- a/compiler/rustc_codegen_ssa/src/back/rpath.rs +++ b/compiler/rustc_codegen_ssa/src/back/rpath.rs @@ -24,7 +24,7 @@ pub fn get_rpath_flags(config: &mut RPathConfig<'_>) -> Vec { debug!("preparing the RPATH!"); - let libs = config.used_crates.clone(); + let libs = config.used_crates; let libs = libs.iter().filter_map(|&(_, ref l)| l.option()).collect::>(); let rpaths = get_rpaths(config, &libs); let mut flags = rpaths_to_flags(&rpaths); diff --git a/compiler/rustc_codegen_ssa/src/back/symbol_export.rs b/compiler/rustc_codegen_ssa/src/back/symbol_export.rs index 9a6f8cde1b25d..b8f277c8ff5e3 100644 --- a/compiler/rustc_codegen_ssa/src/back/symbol_export.rs +++ b/compiler/rustc_codegen_ssa/src/back/symbol_export.rs @@ -15,7 +15,8 @@ use rustc_middle::ty::query::Providers; use rustc_middle::ty::subst::{GenericArgKind, SubstsRef}; use rustc_middle::ty::Instance; use rustc_middle::ty::{SymbolName, TyCtxt}; -use rustc_session::config::{CrateType, SanitizerSet}; +use rustc_session::config::CrateType; +use rustc_target::spec::SanitizerSet; pub fn threshold(tcx: TyCtxt<'_>) -> SymbolExportLevel { crates_export_threshold(&tcx.sess.crate_types()) @@ -188,9 +189,7 @@ fn exported_symbols_provider_local( } } - if tcx.sess.opts.debugging_opts.instrument_coverage - || tcx.sess.opts.cg.profile_generate.enabled() - { + if tcx.sess.instrument_coverage() || tcx.sess.opts.cg.profile_generate.enabled() { // These are weak symbols that point to the profile version and the // profile name, which need to be treated as exported so LTO doesn't nix // them. diff --git a/compiler/rustc_codegen_ssa/src/back/write.rs b/compiler/rustc_codegen_ssa/src/back/write.rs index 7b8ce157fc2b4..c8688faa80bc3 100644 --- a/compiler/rustc_codegen_ssa/src/back/write.rs +++ b/compiler/rustc_codegen_ssa/src/back/write.rs @@ -10,6 +10,7 @@ use crate::{ use crate::traits::*; use jobserver::{Acquired, Client}; use rustc_data_structures::fx::FxHashMap; +use rustc_data_structures::memmap::Mmap; use rustc_data_structures::profiling::SelfProfilerRef; use rustc_data_structures::profiling::TimingGuard; use rustc_data_structures::profiling::VerboseTimingGuard; @@ -27,12 +28,12 @@ use rustc_middle::middle::exported_symbols::SymbolExportLevel; use rustc_middle::ty::TyCtxt; use rustc_session::cgu_reuse_tracker::CguReuseTracker; use rustc_session::config::{self, CrateType, Lto, OutputFilenames, OutputType}; -use rustc_session::config::{Passes, SanitizerSet, SwitchWithOptPath}; +use rustc_session::config::{Passes, SwitchWithOptPath}; use rustc_session::Session; use rustc_span::source_map::SourceMap; use rustc_span::symbol::{sym, Symbol}; use rustc_span::{BytePos, FileName, InnerSpan, Pos, Span}; -use rustc_target::spec::{MergeFunctions, PanicStrategy}; +use rustc_target::spec::{MergeFunctions, PanicStrategy, SanitizerSet}; use std::any::Any; use std::fs; @@ -106,7 +107,7 @@ pub struct ModuleConfig { pub vectorize_loop: bool, pub vectorize_slp: bool, pub merge_functions: bool, - pub inline_threshold: Option, + pub inline_threshold: Option, pub new_llvm_pass_manager: bool, pub emit_lifetime_markers: bool, } @@ -176,7 +177,7 @@ impl ModuleConfig { // The rustc option `-Zinstrument_coverage` injects intrinsic calls to // `llvm.instrprof.increment()`, which requires the LLVM `instrprof` pass. - if sess.opts.debugging_opts.instrument_coverage { + if sess.instrument_coverage() { passes.push("instrprof".to_owned()); } passes @@ -433,12 +434,10 @@ pub fn start_async_codegen( let sess = tcx.sess; let crate_name = tcx.crate_name(LOCAL_CRATE); - let no_builtins = tcx.sess.contains_name(&tcx.hir().krate().item.attrs, sym::no_builtins); - let is_compiler_builtins = - tcx.sess.contains_name(&tcx.hir().krate().item.attrs, sym::compiler_builtins); - let subsystem = tcx - .sess - .first_attr_value_str_by_name(&tcx.hir().krate().item.attrs, sym::windows_subsystem); + let crate_attrs = tcx.hir().attrs(rustc_hir::CRATE_HIR_ID); + let no_builtins = tcx.sess.contains_name(crate_attrs, sym::no_builtins); + let is_compiler_builtins = tcx.sess.contains_name(crate_attrs, sym::compiler_builtins); + let subsystem = tcx.sess.first_attr_value_str_by_name(crate_attrs, sym::windows_subsystem); let windows_subsystem = subsystem.map(|subsystem| { if subsystem != sym::windows && subsystem != sym::console { tcx.sess.fatal(&format!( @@ -1095,7 +1094,7 @@ fn start_executing_work( // only place where we have access to the compiler `Session`. // - LLVM work can be done on any thread. // - Codegen can only happen on the main thread. - // - Each thread doing substantial work most be in possession of a `Token` + // - Each thread doing substantial work must be in possession of a `Token` // from the `Jobserver`. // - The compiler process always holds one `Token`. Any additional `Tokens` // have to be requested from the `Jobserver`. @@ -1147,7 +1146,7 @@ fn start_executing_work( // if possible. These two goals are at odds with each other: If memory // consumption were not an issue, we could just let the main thread produce // LLVM WorkItems at full speed, assuring maximal utilization of - // Tokens/LLVM worker threads. However, since codegen usual is faster + // Tokens/LLVM worker threads. However, since codegen is usually faster // than LLVM processing, the queue of LLVM WorkItems would fill up and each // WorkItem potentially holds on to a substantial amount of memory. // @@ -1960,7 +1959,7 @@ pub fn submit_pre_lto_module_to_llvm( .unwrap_or_else(|e| panic!("failed to open bitcode file `{}`: {}", bc_path.display(), e)); let mmap = unsafe { - memmap::Mmap::map(&file).unwrap_or_else(|e| { + Mmap::map(file).unwrap_or_else(|e| { panic!("failed to mmap bitcode file `{}`: {}", bc_path.display(), e) }) }; diff --git a/compiler/rustc_codegen_ssa/src/base.rs b/compiler/rustc_codegen_ssa/src/base.rs index 08e31c3b37f34..318eed76acf2b 100644 --- a/compiler/rustc_codegen_ssa/src/base.rs +++ b/compiler/rustc_codegen_ssa/src/base.rs @@ -754,7 +754,6 @@ impl CrateInfo { is_no_builtins: Default::default(), native_libraries: Default::default(), used_libraries: tcx.native_libraries(LOCAL_CRATE).iter().map(Into::into).collect(), - link_args: tcx.link_args(LOCAL_CRATE), crate_name: Default::default(), used_crates_dynamic: cstore::used_crates(tcx, LinkagePreference::RequireDynamic), used_crates_static: cstore::used_crates(tcx, LinkagePreference::RequireStatic), diff --git a/compiler/rustc_codegen_ssa/src/coverageinfo/ffi.rs b/compiler/rustc_codegen_ssa/src/coverageinfo/ffi.rs index af6c476292bd1..962c01c2ee7a6 100644 --- a/compiler/rustc_codegen_ssa/src/coverageinfo/ffi.rs +++ b/compiler/rustc_codegen_ssa/src/coverageinfo/ffi.rs @@ -24,21 +24,39 @@ pub enum CounterKind { pub struct Counter { // Important: The layout (order and types of fields) must match its C++ counterpart. pub kind: CounterKind, - pub id: u32, + id: u32, } impl Counter { + /// Constructs a new `Counter` of kind `Zero`. For this `CounterKind`, the + /// `id` is not used. pub fn zero() -> Self { Self { kind: CounterKind::Zero, id: 0 } } + /// Constructs a new `Counter` of kind `CounterValueReference`, and converts + /// the given 1-based counter_id to the required 0-based equivalent for + /// the `Counter` encoding. pub fn counter_value_reference(counter_id: CounterValueReference) -> Self { - Self { kind: CounterKind::CounterValueReference, id: counter_id.into() } + Self { kind: CounterKind::CounterValueReference, id: counter_id.zero_based_index() } } + /// Constructs a new `Counter` of kind `Expression`. pub fn expression(mapped_expression_index: MappedExpressionIndex) -> Self { Self { kind: CounterKind::Expression, id: mapped_expression_index.into() } } + + /// Returns true if the `Counter` kind is `Zero`. + pub fn is_zero(&self) -> bool { + matches!(self.kind, CounterKind::Zero) + } + + /// An explicitly-named function to get the ID value, making it more obvious + /// that the stored value is now 0-based. + pub fn zero_based_id(&self) -> u32 { + debug_assert!(!self.is_zero(), "`id` is undefined for CounterKind::Zero"); + self.id + } } /// Aligns with [llvm::coverage::CounterExpression::ExprKind](https://github.com/rust-lang/llvm-project/blob/rustc/11.0-2020-10-12/llvm/include/llvm/ProfileData/Coverage/CoverageMapping.h#L147) diff --git a/compiler/rustc_codegen_ssa/src/coverageinfo/map.rs b/compiler/rustc_codegen_ssa/src/coverageinfo/map.rs index 549b8d41f5130..4458fd686788f 100644 --- a/compiler/rustc_codegen_ssa/src/coverageinfo/map.rs +++ b/compiler/rustc_codegen_ssa/src/coverageinfo/map.rs @@ -8,7 +8,7 @@ use rustc_middle::mir::coverage::{ use rustc_middle::ty::Instance; use rustc_middle::ty::TyCtxt; -#[derive(Clone, Debug)] +#[derive(Clone, Debug, PartialEq)] pub struct Expression { lhs: ExpressionOperandId, op: Op, @@ -31,27 +31,44 @@ pub struct Expression { pub struct FunctionCoverage<'tcx> { instance: Instance<'tcx>, source_hash: u64, + is_used: bool, counters: IndexVec>, expressions: IndexVec>, unreachable_regions: Vec, } impl<'tcx> FunctionCoverage<'tcx> { + /// Creates a new set of coverage data for a used (called) function. pub fn new(tcx: TyCtxt<'tcx>, instance: Instance<'tcx>) -> Self { + Self::create(tcx, instance, true) + } + + /// Creates a new set of coverage data for an unused (never called) function. + pub fn unused(tcx: TyCtxt<'tcx>, instance: Instance<'tcx>) -> Self { + Self::create(tcx, instance, false) + } + + fn create(tcx: TyCtxt<'tcx>, instance: Instance<'tcx>, is_used: bool) -> Self { let coverageinfo = tcx.coverageinfo(instance.def_id()); debug!( - "FunctionCoverage::new(instance={:?}) has coverageinfo={:?}", - instance, coverageinfo + "FunctionCoverage::new(instance={:?}) has coverageinfo={:?}. is_used={}", + instance, coverageinfo, is_used ); Self { instance, source_hash: 0, // will be set with the first `add_counter()` + is_used, counters: IndexVec::from_elem_n(None, coverageinfo.num_counters as usize), expressions: IndexVec::from_elem_n(None, coverageinfo.num_expressions as usize), unreachable_regions: Vec::new(), } } + /// Returns true for a used (called) function, and false for an unused function. + pub fn is_used(&self) -> bool { + self.is_used + } + /// Sets the function source hash value. If called multiple times for the same function, all /// calls should have the same hash value. pub fn set_function_source_hash(&mut self, source_hash: u64) { @@ -64,7 +81,9 @@ impl<'tcx> FunctionCoverage<'tcx> { /// Adds a code region to be counted by an injected counter intrinsic. pub fn add_counter(&mut self, id: CounterValueReference, region: CodeRegion) { - self.counters[id].replace(region).expect_none("add_counter called with duplicate `id`"); + if let Some(previous_region) = self.counters[id].replace(region.clone()) { + assert_eq!(previous_region, region, "add_counter: code region for id changed"); + } } /// Both counters and "counter expressions" (or simply, "expressions") can be operands in other @@ -94,9 +113,18 @@ impl<'tcx> FunctionCoverage<'tcx> { expression_id, lhs, op, rhs, region ); let expression_index = self.expression_index(u32::from(expression_id)); - self.expressions[expression_index] - .replace(Expression { lhs, op, rhs, region }) - .expect_none("add_counter_expression called with duplicate `id_descending_from_max`"); + if let Some(previous_expression) = self.expressions[expression_index].replace(Expression { + lhs, + op, + rhs, + region: region.clone(), + }) { + assert_eq!( + previous_expression, + Expression { lhs, op, rhs, region }, + "add_counter_expression: expression for id changed" + ); + } } /// Add a region that will be marked as "unreachable", with a constant "zero counter". @@ -117,8 +145,8 @@ impl<'tcx> FunctionCoverage<'tcx> { &'a self, ) -> (Vec, impl Iterator) { assert!( - self.source_hash != 0, - "No counters provided the source_hash for function: {:?}", + self.source_hash != 0 || !self.is_used, + "No counters provided the source_hash for used function: {:?}", self.instance ); @@ -135,9 +163,7 @@ impl<'tcx> FunctionCoverage<'tcx> { self.counters.iter_enumerated().filter_map(|(index, entry)| { // Option::map() will return None to filter out missing counters. This may happen // if, for example, a MIR-instrumented counter is removed during an optimization. - entry.as_ref().map(|region| { - (Counter::counter_value_reference(index as CounterValueReference), region) - }) + entry.as_ref().map(|region| (Counter::counter_value_reference(index), region)) }) } @@ -178,9 +204,15 @@ impl<'tcx> FunctionCoverage<'tcx> { if id == ExpressionOperandId::ZERO { Some(Counter::zero()) } else if id.index() < self.counters.len() { + debug_assert!( + id.index() > 0, + "ExpressionOperandId indexes for counters are 1-based, but this id={}", + id.index() + ); // Note: Some codegen-injected Counters may be only referenced by `Expression`s, // and may not have their own `CodeRegion`s, let index = CounterValueReference::from(id.index()); + // Note, the conversion to LLVM `Counter` adjusts the index to be zero-based. Some(Counter::counter_value_reference(index)) } else { let index = self.expression_index(u32::from(id)); @@ -205,19 +237,60 @@ impl<'tcx> FunctionCoverage<'tcx> { let optional_region = &expression.region; let Expression { lhs, op, rhs, .. } = *expression; - if let Some(Some((lhs_counter, rhs_counter))) = - id_to_counter(&new_indexes, lhs).map(|lhs_counter| { + if let Some(Some((lhs_counter, mut rhs_counter))) = id_to_counter(&new_indexes, lhs) + .map(|lhs_counter| { id_to_counter(&new_indexes, rhs).map(|rhs_counter| (lhs_counter, rhs_counter)) }) { + if lhs_counter.is_zero() && op.is_subtract() { + // The left side of a subtraction was probably optimized out. As an example, + // a branch condition might be evaluated as a constant expression, and the + // branch could be removed, dropping unused counters in the process. + // + // Since counters are unsigned, we must assume the result of the expression + // can be no more and no less than zero. An expression known to evaluate to zero + // does not need to be added to the coverage map. + // + // Coverage test `loops_branches.rs` includes multiple variations of branches + // based on constant conditional (literal `true` or `false`), and demonstrates + // that the expected counts are still correct. + debug!( + "Expression subtracts from zero (assume unreachable): \ + original_index={:?}, lhs={:?}, op={:?}, rhs={:?}, region={:?}", + original_index, lhs, op, rhs, optional_region, + ); + rhs_counter = Counter::zero(); + } debug_assert!( - (lhs_counter.id as usize) - < usize::max(self.counters.len(), self.expressions.len()) + lhs_counter.is_zero() + // Note: with `as usize` the ID _could_ overflow/wrap if `usize = u16` + || ((lhs_counter.zero_based_id() as usize) + <= usize::max(self.counters.len(), self.expressions.len())), + "lhs id={} > both counters.len()={} and expressions.len()={} + ({:?} {:?} {:?})", + lhs_counter.zero_based_id(), + self.counters.len(), + self.expressions.len(), + lhs_counter, + op, + rhs_counter, ); + debug_assert!( - (rhs_counter.id as usize) - < usize::max(self.counters.len(), self.expressions.len()) + rhs_counter.is_zero() + // Note: with `as usize` the ID _could_ overflow/wrap if `usize = u16` + || ((rhs_counter.zero_based_id() as usize) + <= usize::max(self.counters.len(), self.expressions.len())), + "rhs id={} > both counters.len()={} and expressions.len()={} + ({:?} {:?} {:?})", + rhs_counter.zero_based_id(), + self.counters.len(), + self.expressions.len(), + lhs_counter, + op, + rhs_counter, ); + // Both operands exist. `Expression` operands exist in `self.expressions` and have // been assigned a `new_index`. let mapped_expression_index = @@ -240,11 +313,15 @@ impl<'tcx> FunctionCoverage<'tcx> { expression_regions.push((Counter::expression(mapped_expression_index), region)); } } else { - debug!( - "Ignoring expression with one or more missing operands: \ - original_index={:?}, lhs={:?}, op={:?}, rhs={:?}, region={:?}", - original_index, lhs, op, rhs, optional_region, - ) + bug!( + "expression has one or more missing operands \ + original_index={:?}, lhs={:?}, op={:?}, rhs={:?}, region={:?}", + original_index, + lhs, + op, + rhs, + optional_region, + ); } } (counter_expressions, expression_regions.into_iter()) diff --git a/compiler/rustc_codegen_ssa/src/lib.rs b/compiler/rustc_codegen_ssa/src/lib.rs index 0307117e1c8b2..f0f45b067b352 100644 --- a/compiler/rustc_codegen_ssa/src/lib.rs +++ b/compiler/rustc_codegen_ssa/src/lib.rs @@ -1,14 +1,16 @@ #![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")] +#![feature(assert_matches)] #![feature(bool_to_option)] -#![feature(option_expect_none)] #![feature(box_patterns)] #![feature(drain_filter)] #![feature(try_blocks)] #![feature(in_band_lifetimes)] #![feature(nll)] -#![feature(or_patterns)] +#![cfg_attr(bootstrap, feature(or_patterns))] #![feature(associated_type_bounds)] +#![feature(iter_zip)] #![recursion_limit = "256"] +#![feature(box_syntax)] //! This crate contains codegen code that is used by all codegen backends (LLVM and others). //! The backend-agnostic functions of this crate use functions defined in various traits that @@ -137,7 +139,6 @@ pub struct CrateInfo { pub native_libraries: FxHashMap>, pub crate_name: FxHashMap, pub used_libraries: Vec, - pub link_args: Lrc>, pub used_crate_source: FxHashMap>, pub used_crates_static: Vec<(CrateNum, LibSource)>, pub used_crates_dynamic: Vec<(CrateNum, LibSource)>, diff --git a/compiler/rustc_codegen_ssa/src/mir/analyze.rs b/compiler/rustc_codegen_ssa/src/mir/analyze.rs index 289629d921545..38e928145a816 100644 --- a/compiler/rustc_codegen_ssa/src/mir/analyze.rs +++ b/compiler/rustc_codegen_ssa/src/mir/analyze.rs @@ -231,7 +231,7 @@ impl<'mir, 'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> Visitor<'tcx> fn visit_terminator(&mut self, terminator: &mir::Terminator<'tcx>, location: Location) { let check = match terminator.kind { mir::TerminatorKind::Call { func: mir::Operand::Constant(ref c), ref args, .. } => { - match *c.literal.ty.kind() { + match *c.ty().kind() { ty::FnDef(did, _) => Some((did, args)), _ => None, } @@ -281,7 +281,18 @@ impl<'mir, 'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> Visitor<'tcx> Some(assignment_location) => { assignment_location.dominates(location, &self.dominators) } - None => false, + None => { + debug!("No first assignment found for {:?}", local); + // We have not seen any assignment to the local yet, + // but before marking not_ssa, check if it is a ZST, + // in which case we don't need to initialize the local. + let ty = self.fx.mir.local_decls[local].ty; + let ty = self.fx.monomorphize(ty); + + let is_zst = self.fx.cx.layout_of(ty).is_zst(); + debug!("is_zst: {}", is_zst); + is_zst + } }; if !ssa_read { self.not_ssa(local); diff --git a/compiler/rustc_codegen_ssa/src/mir/block.rs b/compiler/rustc_codegen_ssa/src/mir/block.rs index 9ce9066980066..fd3f89a2aee96 100644 --- a/compiler/rustc_codegen_ssa/src/mir/block.rs +++ b/compiler/rustc_codegen_ssa/src/mir/block.rs @@ -146,24 +146,6 @@ impl<'a, 'tcx> TerminatorCodegenHelper<'tcx> { } } } - - // Generate sideeffect intrinsic if jumping to any of the targets can form - // a loop. - fn maybe_sideeffect>( - &self, - mir: &'tcx mir::Body<'tcx>, - bx: &mut Bx, - targets: &[mir::BasicBlock], - ) { - if bx.tcx().sess.opts.debugging_opts.insert_sideeffect { - if targets.iter().any(|&target| { - target <= self.bb - && target.start_location().is_predecessor_of(self.bb.start_location(), mir) - }) { - bx.sideeffect(false); - } - } - } } /// Codegen implementations for some terminator variants. @@ -198,8 +180,6 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { let discr = self.codegen_operand(&mut bx, &discr); // `switch_ty` is redundant, sanity-check that. assert_eq!(discr.layout.ty, switch_ty); - helper.maybe_sideeffect(self.mir, &mut bx, targets.all_targets()); - let mut target_iter = targets.iter(); if target_iter.len() == 1 { // If there are two targets (one conditional, one fallback), emit br instead of switch @@ -308,7 +288,6 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { if let ty::InstanceDef::DropGlue(_, None) = drop_fn.def { // we don't actually need to drop anything. - helper.maybe_sideeffect(self.mir, &mut bx, &[target]); helper.funclet_br(self, &mut bx, target); return; } @@ -337,7 +316,6 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { } _ => (bx.get_fn_addr(drop_fn), FnAbi::of_instance(&bx, drop_fn, &[])), }; - helper.maybe_sideeffect(self.mir, &mut bx, &[target]); helper.do_call( self, &mut bx, @@ -379,7 +357,6 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { // Don't codegen the panic block if success if known. if const_cond == Some(expected) { - helper.maybe_sideeffect(self.mir, &mut bx, &[target]); helper.funclet_br(self, &mut bx, target); return; } @@ -390,7 +367,6 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { // Create the failure block and the conditional branch to it. let lltarget = helper.llblock(self, target); let panic_block = self.new_block("panic"); - helper.maybe_sideeffect(self.mir, &mut bx, &[target]); if expected { bx.cond_br(cond, lltarget, panic_block.llbb()); } else { @@ -491,9 +467,6 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { let fn_abi = FnAbi::of_instance(bx, instance, &[]); let llfn = bx.get_fn_addr(instance); - if let Some((_, target)) = destination.as_ref() { - helper.maybe_sideeffect(self.mir, bx, &[*target]); - } // Codegen the actual panic invoke/call. helper.do_call( self, @@ -507,7 +480,6 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { } else { // a NOP let target = destination.as_ref().unwrap().1; - helper.maybe_sideeffect(self.mir, bx, &[target]); helper.funclet_br(self, bx, target) } true @@ -551,7 +523,6 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { if let Some(ty::InstanceDef::DropGlue(_, None)) = def { // Empty drop glue; a no-op. let &(_, target) = destination.as_ref().unwrap(); - helper.maybe_sideeffect(self.mir, &mut bx, &[target]); helper.funclet_br(self, &mut bx, target); return; } @@ -586,7 +557,6 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { if let Some(destination_ref) = destination.as_ref() { let &(dest, target) = destination_ref; self.codegen_transmute(&mut bx, &args[0], dest); - helper.maybe_sideeffect(self.mir, &mut bx, &[target]); helper.funclet_br(self, &mut bx, target); } else { // If we are trying to transmute to an uninhabited type, @@ -634,74 +604,73 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { location.val.store(&mut bx, tmp); } self.store_return(&mut bx, ret_dest, &fn_abi.ret, location.immediate()); - - helper.maybe_sideeffect(self.mir, &mut bx, &[*target]); helper.funclet_br(self, &mut bx, *target); } return; } - if intrinsic.is_some() && intrinsic != Some(sym::drop_in_place) { - let intrinsic = intrinsic.unwrap(); - let dest = match ret_dest { - _ if fn_abi.ret.is_indirect() => llargs[0], - ReturnDest::Nothing => { - bx.const_undef(bx.type_ptr_to(bx.arg_memory_ty(&fn_abi.ret))) - } - ReturnDest::IndirectOperand(dst, _) | ReturnDest::Store(dst) => dst.llval, - ReturnDest::DirectOperand(_) => { - bug!("Cannot use direct operand with an intrinsic call") - } - }; + match intrinsic { + None | Some(sym::drop_in_place) => {} + Some(sym::copy_nonoverlapping) => unreachable!(), + Some(intrinsic) => { + let dest = match ret_dest { + _ if fn_abi.ret.is_indirect() => llargs[0], + ReturnDest::Nothing => { + bx.const_undef(bx.type_ptr_to(bx.arg_memory_ty(&fn_abi.ret))) + } + ReturnDest::IndirectOperand(dst, _) | ReturnDest::Store(dst) => dst.llval, + ReturnDest::DirectOperand(_) => { + bug!("Cannot use direct operand with an intrinsic call") + } + }; - let args: Vec<_> = args - .iter() - .enumerate() - .map(|(i, arg)| { - // The indices passed to simd_shuffle* in the - // third argument must be constant. This is - // checked by const-qualification, which also - // promotes any complex rvalues to constants. - if i == 2 && intrinsic.as_str().starts_with("simd_shuffle") { - if let mir::Operand::Constant(constant) = arg { - let c = self.eval_mir_constant(constant); - let (llval, ty) = self.simd_shuffle_indices( - &bx, - constant.span, - constant.literal.ty, - c, - ); - return OperandRef { val: Immediate(llval), layout: bx.layout_of(ty) }; - } else { - span_bug!(span, "shuffle indices must be constant"); + let args: Vec<_> = args + .iter() + .enumerate() + .map(|(i, arg)| { + // The indices passed to simd_shuffle* in the + // third argument must be constant. This is + // checked by const-qualification, which also + // promotes any complex rvalues to constants. + if i == 2 && intrinsic.as_str().starts_with("simd_shuffle") { + if let mir::Operand::Constant(constant) = arg { + let c = self.eval_mir_constant(constant); + let (llval, ty) = + self.simd_shuffle_indices(&bx, constant.span, constant.ty(), c); + return OperandRef { + val: Immediate(llval), + layout: bx.layout_of(ty), + }; + } else { + span_bug!(span, "shuffle indices must be constant"); + } } - } - self.codegen_operand(&mut bx, arg) - }) - .collect(); + self.codegen_operand(&mut bx, arg) + }) + .collect(); + + Self::codegen_intrinsic_call( + &mut bx, + *instance.as_ref().unwrap(), + &fn_abi, + &args, + dest, + span, + ); - Self::codegen_intrinsic_call( - &mut bx, - *instance.as_ref().unwrap(), - &fn_abi, - &args, - dest, - span, - ); + if let ReturnDest::IndirectOperand(dst, _) = ret_dest { + self.store_return(&mut bx, ret_dest, &fn_abi.ret, dst.llval); + } - if let ReturnDest::IndirectOperand(dst, _) = ret_dest { - self.store_return(&mut bx, ret_dest, &fn_abi.ret, dst.llval); - } + if let Some((_, target)) = *destination { + helper.funclet_br(self, &mut bx, target); + } else { + bx.unreachable(); + } - if let Some((_, target)) = *destination { - helper.maybe_sideeffect(self.mir, &mut bx, &[target]); - helper.funclet_br(self, &mut bx, target); - } else { - bx.unreachable(); + return; } - - return; } // Split the rust-call tupled arguments off. @@ -811,9 +780,6 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { _ => span_bug!(span, "no llfn for call"), }; - if let Some((_, target)) = destination.as_ref() { - helper.maybe_sideeffect(self.mir, &mut bx, &[*target]); - } helper.do_call( self, &mut bx, @@ -856,45 +822,41 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { InlineAsmOperandRef::InOut { reg, late, in_value, out_place } } mir::InlineAsmOperand::Const { ref value } => { - if let mir::Operand::Constant(constant) = value { - let const_value = self - .eval_mir_constant(constant) - .unwrap_or_else(|_| span_bug!(span, "asm const cannot be resolved")); - let ty = constant.literal.ty; - let size = bx.layout_of(ty).size; - let scalar = match const_value { - ConstValue::Scalar(s) => s, - _ => span_bug!( - span, - "expected Scalar for promoted asm const, but got {:#?}", - const_value - ), - }; - let value = scalar.assert_bits(size); - let string = match ty.kind() { - ty::Uint(_) => value.to_string(), - ty::Int(int_ty) => { - match int_ty.normalize(bx.tcx().sess.target.pointer_width) { - ty::IntTy::I8 => (value as i8).to_string(), - ty::IntTy::I16 => (value as i16).to_string(), - ty::IntTy::I32 => (value as i32).to_string(), - ty::IntTy::I64 => (value as i64).to_string(), - ty::IntTy::I128 => (value as i128).to_string(), - ty::IntTy::Isize => unreachable!(), - } + let const_value = self + .eval_mir_constant(value) + .unwrap_or_else(|_| span_bug!(span, "asm const cannot be resolved")); + let ty = value.ty(); + let size = bx.layout_of(ty).size; + let scalar = match const_value { + ConstValue::Scalar(s) => s, + _ => span_bug!( + span, + "expected Scalar for promoted asm const, but got {:#?}", + const_value + ), + }; + let value = scalar.assert_bits(size); + let string = match ty.kind() { + ty::Uint(_) => value.to_string(), + ty::Int(int_ty) => { + match int_ty.normalize(bx.tcx().sess.target.pointer_width) { + ty::IntTy::I8 => (value as i8).to_string(), + ty::IntTy::I16 => (value as i16).to_string(), + ty::IntTy::I32 => (value as i32).to_string(), + ty::IntTy::I64 => (value as i64).to_string(), + ty::IntTy::I128 => (value as i128).to_string(), + ty::IntTy::Isize => unreachable!(), } - ty::Float(ty::FloatTy::F32) => f32::from_bits(value as u32).to_string(), - ty::Float(ty::FloatTy::F64) => f64::from_bits(value as u64).to_string(), - _ => span_bug!(span, "asm const has bad type {}", ty), - }; - InlineAsmOperandRef::Const { string } - } else { - span_bug!(span, "asm const is not a constant"); - } + } + ty::Float(ty::FloatTy::F32) => f32::from_bits(value as u32).to_string(), + ty::Float(ty::FloatTy::F64) => f64::from_bits(value as u64).to_string(), + _ => span_bug!(span, "asm const has bad type {}", ty), + }; + InlineAsmOperandRef::Const { string } } mir::InlineAsmOperand::SymFn { ref value } => { let literal = self.monomorphize(value.literal); - if let ty::FnDef(def_id, substs) = *literal.ty.kind() { + if let ty::FnDef(def_id, substs) = *literal.ty().kind() { let instance = ty::Instance::resolve_for_fn_ptr( bx.tcx(), ty::ParamEnv::reveal_all(), @@ -963,22 +925,16 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { mir::TerminatorKind::Goto { target } => { if bb == target { - // This is an unconditional branch back to this same basic - // block. That means we have something like a `loop {}` - // statement. Currently LLVM miscompiles this because it - // assumes forward progress. We want to prevent this in all - // cases, but that has a fairly high cost to compile times - // currently. Instead, try to handle this specific case - // which comes up commonly in practice (e.g., in embedded - // code). + // This is an unconditional branch back to this same basic block. That means we + // have something like a `loop {}` statement. LLVM versions before 12.0 + // miscompile this because they assume forward progress. For older versions + // try to handle just this specific case which comes up commonly in practice + // (e.g., in embedded code). // - // The `true` here means we insert side effects regardless - // of -Zinsert-sideeffect being passed on unconditional - // branching to the same basic block. - bx.sideeffect(true); - } else { - helper.maybe_sideeffect(self.mir, &mut bx, &[target]); + // NB: the `sideeffect` currently checks for the LLVM version used internally. + bx.sideeffect(); } + helper.funclet_br(self, &mut bx, target); } diff --git a/compiler/rustc_codegen_ssa/src/mir/constant.rs b/compiler/rustc_codegen_ssa/src/mir/constant.rs index b79a221a0e74a..fa8a53e60b169 100644 --- a/compiler/rustc_codegen_ssa/src/mir/constant.rs +++ b/compiler/rustc_codegen_ssa/src/mir/constant.rs @@ -16,7 +16,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { constant: &mir::Constant<'tcx>, ) -> Result, ErrorHandled> { let val = self.eval_mir_constant(constant)?; - let ty = self.monomorphize(constant.literal.ty); + let ty = self.monomorphize(constant.ty()); Ok(OperandRef::from_const(bx, val, ty)) } @@ -24,11 +24,16 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { &self, constant: &mir::Constant<'tcx>, ) -> Result, ErrorHandled> { - match self.monomorphize(constant.literal).val { - ty::ConstKind::Unevaluated(def, substs, promoted) => self + let ct = self.monomorphize(constant.literal); + let ct = match ct { + mir::ConstantKind::Ty(ct) => ct, + mir::ConstantKind::Val(val, _) => return Ok(val), + }; + match ct.val { + ty::ConstKind::Unevaluated(ct) => self .cx .tcx() - .const_eval_resolve(ty::ParamEnv::reveal_all(), def, substs, promoted, None) + .const_eval_resolve(ty::ParamEnv::reveal_all(), ct, None) .map_err(|err| { self.cx.tcx().sess.span_err(constant.span, "erroneous constant encountered"); err diff --git a/compiler/rustc_codegen_ssa/src/mir/coverageinfo.rs b/compiler/rustc_codegen_ssa/src/mir/coverageinfo.rs index a115d35866638..621ec0519c956 100644 --- a/compiler/rustc_codegen_ssa/src/mir/coverageinfo.rs +++ b/compiler/rustc_codegen_ssa/src/mir/coverageinfo.rs @@ -2,30 +2,41 @@ use crate::traits::*; use rustc_middle::mir::coverage::*; use rustc_middle::mir::Coverage; +use rustc_middle::mir::SourceScope; use super::FunctionCx; impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { - pub fn codegen_coverage(&self, bx: &mut Bx, coverage: Coverage) { + pub fn codegen_coverage(&self, bx: &mut Bx, coverage: Coverage, scope: SourceScope) { + // Determine the instance that coverage data was originally generated for. + let scope_data = &self.mir.source_scopes[scope]; + let instance = if let Some((inlined_instance, _)) = scope_data.inlined { + self.monomorphize(inlined_instance) + } else if let Some(inlined_scope) = scope_data.inlined_parent_scope { + self.monomorphize(self.mir.source_scopes[inlined_scope].inlined.unwrap().0) + } else { + self.instance + }; + let Coverage { kind, code_region } = coverage; match kind { CoverageKind::Counter { function_source_hash, id } => { - if bx.set_function_source_hash(self.instance, function_source_hash) { + if bx.set_function_source_hash(instance, function_source_hash) { // If `set_function_source_hash()` returned true, the coverage map is enabled, // so continue adding the counter. if let Some(code_region) = code_region { // Note: Some counters do not have code regions, but may still be referenced // from expressions. In that case, don't add the counter to the coverage map, // but do inject the counter intrinsic. - bx.add_coverage_counter(self.instance, id, code_region); + bx.add_coverage_counter(instance, id, code_region); } - let coverageinfo = bx.tcx().coverageinfo(self.instance.def_id()); + let coverageinfo = bx.tcx().coverageinfo(instance.def_id()); - let fn_name = bx.create_pgo_func_name_var(self.instance); + let fn_name = bx.get_pgo_func_name_var(instance); let hash = bx.const_u64(function_source_hash); let num_counters = bx.const_u32(coverageinfo.num_counters); - let index = bx.const_u32(u32::from(id)); + let index = bx.const_u32(id.zero_based_index()); debug!( "codegen intrinsic instrprof.increment(fn_name={:?}, hash={:?}, num_counters={:?}, index={:?})", fn_name, hash, num_counters, index, @@ -34,11 +45,11 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { } } CoverageKind::Expression { id, lhs, op, rhs } => { - bx.add_coverage_counter_expression(self.instance, id, lhs, op, rhs, code_region); + bx.add_coverage_counter_expression(instance, id, lhs, op, rhs, code_region); } CoverageKind::Unreachable => { bx.add_coverage_unreachable( - self.instance, + instance, code_region.expect("unreachable regions always have code regions"), ); } diff --git a/compiler/rustc_codegen_ssa/src/mir/debuginfo.rs b/compiler/rustc_codegen_ssa/src/mir/debuginfo.rs index ea59e1831188b..6bb20545f07be 100644 --- a/compiler/rustc_codegen_ssa/src/mir/debuginfo.rs +++ b/compiler/rustc_codegen_ssa/src/mir/debuginfo.rs @@ -6,7 +6,7 @@ use rustc_middle::ty; use rustc_session::config::DebugInfo; use rustc_span::symbol::{kw, Symbol}; use rustc_span::{BytePos, Span}; -use rustc_target::abi::{LayoutOf, Size}; +use rustc_target::abi::Size; use super::operand::{OperandRef, OperandValue}; use super::place::PlaceRef; @@ -265,33 +265,25 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { None => continue, }; - let mut layout = base.layout; let mut direct_offset = Size::ZERO; // FIXME(eddyb) use smallvec here. let mut indirect_offsets = vec![]; + let mut place = base; for elem in &var.projection[..] { match *elem { mir::ProjectionElem::Deref => { indirect_offsets.push(Size::ZERO); - layout = bx.cx().layout_of( - layout - .ty - .builtin_deref(true) - .unwrap_or_else(|| { - span_bug!(var.source_info.span, "cannot deref `{}`", layout.ty) - }) - .ty, - ); + place = place.project_deref(bx); } mir::ProjectionElem::Field(field, _) => { let i = field.index(); let offset = indirect_offsets.last_mut().unwrap_or(&mut direct_offset); - *offset += layout.fields.offset(i); - layout = layout.field(bx.cx(), i); + *offset += place.layout.fields.offset(i); + place = place.project_field(bx, i); } mir::ProjectionElem::Downcast(_, variant) => { - layout = layout.for_variant(bx.cx(), variant); + place = place.project_downcast(bx, variant); } _ => span_bug!( var.source_info.span, @@ -301,7 +293,39 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { } } - bx.dbg_var_addr(dbg_var, dbg_loc, base.llval, direct_offset, &indirect_offsets); + // When targeting MSVC, create extra allocas for arguments instead of pointing multiple + // dbg_var_addr() calls into the same alloca with offsets. MSVC uses CodeView records + // not DWARF and LLVM doesn't support translating the resulting + // [DW_OP_deref, DW_OP_plus_uconst, offset, DW_OP_deref] debug info to CodeView. + // Creating extra allocas on the stack makes the resulting debug info simple enough + // that LLVM can generate correct CodeView records and thus the values appear in the + // debugger. (#83709) + let should_create_individual_allocas = bx.cx().sess().target.is_like_msvc + && self.mir.local_kind(local) == mir::LocalKind::Arg + // LLVM can handle simple things but anything more complex than just a direct + // offset or one indirect offset of 0 is too complex for it to generate CV records + // correctly. + && (direct_offset != Size::ZERO + || !matches!(&indirect_offsets[..], [Size::ZERO] | [])); + + if should_create_individual_allocas { + // Create a variable which will be a pointer to the actual value + let ptr_ty = bx.tcx().mk_ty(ty::RawPtr(ty::TypeAndMut { + mutbl: mir::Mutability::Mut, + ty: place.layout.ty, + })); + let ptr_layout = bx.layout_of(ptr_ty); + let alloca = PlaceRef::alloca(bx, ptr_layout); + bx.set_var_name(alloca.llval, &(var.name.to_string() + ".dbg.spill")); + + // Write the pointer to the variable + bx.store(place.llval, alloca.llval, alloca.align); + + // Point the debug info to `*alloca` for the current variable + bx.dbg_var_addr(dbg_var, dbg_loc, alloca.llval, Size::ZERO, &[Size::ZERO]); + } else { + bx.dbg_var_addr(dbg_var, dbg_loc, base.llval, direct_offset, &indirect_offsets); + } } } @@ -372,7 +396,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { (var_ty, var_kind) } mir::VarDebugInfoContents::Const(c) => { - let ty = self.monomorphize(c.literal.ty); + let ty = self.monomorphize(c.ty()); (ty, VariableKind::LocalVariable) } }; diff --git a/compiler/rustc_codegen_ssa/src/mir/intrinsic.rs b/compiler/rustc_codegen_ssa/src/mir/intrinsic.rs index 80e3ed75b8585..8502309b90e5a 100644 --- a/compiler/rustc_codegen_ssa/src/mir/intrinsic.rs +++ b/compiler/rustc_codegen_ssa/src/mir/intrinsic.rs @@ -125,19 +125,6 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { let offset = args[1].immediate(); bx.gep(ptr, &[offset]) } - - sym::copy_nonoverlapping => { - copy_intrinsic( - bx, - false, - false, - substs.type_at(0), - args[1].immediate(), - args[0].immediate(), - args[2].immediate(), - ); - return; - } sym::copy => { copy_intrinsic( bx, diff --git a/compiler/rustc_codegen_ssa/src/mir/mod.rs b/compiler/rustc_codegen_ssa/src/mir/mod.rs index d31ececf13062..91df67b53d21f 100644 --- a/compiler/rustc_codegen_ssa/src/mir/mod.rs +++ b/compiler/rustc_codegen_ssa/src/mir/mod.rs @@ -149,8 +149,6 @@ pub fn codegen_mir<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( bx.set_personality_fn(cx.eh_personality()); } - bx.sideeffect(false); - let cleanup_kinds = analyze::cleanup_kinds(&mir); // Allocate a `Block` for every basic block, except // the start block, if nothing loops back to it. @@ -284,9 +282,7 @@ fn create_funclets<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( IndexVec>, IndexVec>, ) { - block_bxs - .iter_enumerated() - .zip(cleanup_kinds) + iter::zip(block_bxs.iter_enumerated(), cleanup_kinds) .map(|((bb, &llbb), cleanup_kind)| { match *cleanup_kind { CleanupKind::Funclet if base::wants_msvc_seh(bx.sess()) => {} diff --git a/compiler/rustc_codegen_ssa/src/mir/place.rs b/compiler/rustc_codegen_ssa/src/mir/place.rs index 66d9d1a1e0c49..a9e7ebf6d43f7 100644 --- a/compiler/rustc_codegen_ssa/src/mir/place.rs +++ b/compiler/rustc_codegen_ssa/src/mir/place.rs @@ -402,6 +402,18 @@ impl<'a, 'tcx, V: CodegenObject> PlaceRef<'tcx, V> { downcast } + pub fn project_deref>(&self, bx: &mut Bx) -> Self { + let target_ty = self.layout.ty.builtin_deref(true).expect("failed to deref"); + let layout = bx.layout_of(target_ty.ty); + + PlaceRef { + llval: bx.load(self.llval, self.align), + llextra: None, + layout, + align: layout.align.abi, + } + } + pub fn storage_live>(&self, bx: &mut Bx) { bx.lifetime_start(self.llval, self.layout.size); } diff --git a/compiler/rustc_codegen_ssa/src/mir/rvalue.rs b/compiler/rustc_codegen_ssa/src/mir/rvalue.rs index e3a6cabd60057..9917c23f12150 100644 --- a/compiler/rustc_codegen_ssa/src/mir/rvalue.rs +++ b/compiler/rustc_codegen_ssa/src/mir/rvalue.rs @@ -11,7 +11,7 @@ use rustc_apfloat::{ieee, Float, Round, Status}; use rustc_hir::lang_items::LangItem; use rustc_middle::mir; use rustc_middle::ty::cast::{CastTy, IntTy}; -use rustc_middle::ty::layout::{HasTyCtxt, TyAndLayout}; +use rustc_middle::ty::layout::HasTyCtxt; use rustc_middle::ty::{self, adjustment::PointerCast, Instance, Ty, TyCtxt}; use rustc_span::source_map::{Span, DUMMY_SP}; use rustc_span::symbol::sym; @@ -325,7 +325,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { let er = scalar.valid_range_exclusive(bx.cx()); if er.end != er.start - && scalar.valid_range.end() > scalar.valid_range.start() + && scalar.valid_range.end() >= scalar.valid_range.start() { // We want `table[e as usize ± k]` to not // have bound checks, and this is the most @@ -385,10 +385,10 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { bx.inttoptr(usize_llval, ll_t_out) } (CastTy::Float, CastTy::Int(IntTy::I)) => { - cast_float_to_int(&mut bx, true, llval, ll_t_in, ll_t_out, cast) + cast_float_to_int(&mut bx, true, llval, ll_t_in, ll_t_out) } (CastTy::Float, CastTy::Int(_)) => { - cast_float_to_int(&mut bx, false, llval, ll_t_in, ll_t_out, cast) + cast_float_to_int(&mut bx, false, llval, ll_t_in, ll_t_out) } _ => bug!("unsupported cast: {:?} to {:?}", operand.layout.ty, cast.ty), }; @@ -424,7 +424,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { (bx, operand) } - mir::Rvalue::BinaryOp(op, ref lhs, ref rhs) => { + mir::Rvalue::BinaryOp(op, box (ref lhs, ref rhs)) => { let lhs = self.codegen_operand(&mut bx, lhs); let rhs = self.codegen_operand(&mut bx, rhs); let llresult = match (lhs.val, rhs.val) { @@ -453,7 +453,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { }; (bx, operand) } - mir::Rvalue::CheckedBinaryOp(op, ref lhs, ref rhs) => { + mir::Rvalue::CheckedBinaryOp(op, box (ref lhs, ref rhs)) => { let lhs = self.codegen_operand(&mut bx, lhs); let rhs = self.codegen_operand(&mut bx, rhs); let result = self.codegen_scalar_checked_binop( @@ -790,7 +790,6 @@ fn cast_float_to_int<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( x: Bx::Value, float_ty: Bx::Type, int_ty: Bx::Type, - int_layout: TyAndLayout<'tcx>, ) -> Bx::Value { if let Some(false) = bx.cx().sess().opts.debugging_opts.saturating_float_casts { return if signed { bx.fptosi(x, int_ty) } else { bx.fptoui(x, int_ty) }; @@ -891,134 +890,39 @@ fn cast_float_to_int<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( let int_min = bx.cx().const_uint_big(int_ty, int_min(signed, int_width) as u128); let zero = bx.cx().const_uint(int_ty, 0); - // The codegen here differs quite a bit depending on whether our builder's - // `fptosi` and `fptoui` instructions may trap for out-of-bounds values. If - // they don't trap then we can start doing everything inline with a - // `select` instruction because it's ok to execute `fptosi` and `fptoui` - // even if we don't use the results. - if !bx.fptosui_may_trap(x, int_ty) { - // Step 1 ... - let fptosui_result = if signed { bx.fptosi(x, int_ty) } else { bx.fptoui(x, int_ty) }; - let less_or_nan = bx.fcmp(RealPredicate::RealULT, x, f_min); - let greater = bx.fcmp(RealPredicate::RealOGT, x, f_max); - - // Step 2: We use two comparisons and two selects, with %s1 being the - // result: - // %less_or_nan = fcmp ult %x, %f_min - // %greater = fcmp olt %x, %f_max - // %s0 = select %less_or_nan, int_ty::MIN, %fptosi_result - // %s1 = select %greater, int_ty::MAX, %s0 - // Note that %less_or_nan uses an *unordered* comparison. This - // comparison is true if the operands are not comparable (i.e., if x is - // NaN). The unordered comparison ensures that s1 becomes int_ty::MIN if - // x is NaN. - // - // Performance note: Unordered comparison can be lowered to a "flipped" - // comparison and a negation, and the negation can be merged into the - // select. Therefore, it not necessarily any more expensive than a - // ordered ("normal") comparison. Whether these optimizations will be - // performed is ultimately up to the backend, but at least x86 does - // perform them. - let s0 = bx.select(less_or_nan, int_min, fptosui_result); - let s1 = bx.select(greater, int_max, s0); - - // Step 3: NaN replacement. - // For unsigned types, the above step already yielded int_ty::MIN == 0 if x is NaN. - // Therefore we only need to execute this step for signed integer types. - if signed { - // LLVM has no isNaN predicate, so we use (x == x) instead - let cmp = bx.fcmp(RealPredicate::RealOEQ, x, x); - bx.select(cmp, s1, zero) - } else { - s1 - } + // Step 1 ... + let fptosui_result = if signed { bx.fptosi(x, int_ty) } else { bx.fptoui(x, int_ty) }; + let less_or_nan = bx.fcmp(RealPredicate::RealULT, x, f_min); + let greater = bx.fcmp(RealPredicate::RealOGT, x, f_max); + + // Step 2: We use two comparisons and two selects, with %s1 being the + // result: + // %less_or_nan = fcmp ult %x, %f_min + // %greater = fcmp olt %x, %f_max + // %s0 = select %less_or_nan, int_ty::MIN, %fptosi_result + // %s1 = select %greater, int_ty::MAX, %s0 + // Note that %less_or_nan uses an *unordered* comparison. This + // comparison is true if the operands are not comparable (i.e., if x is + // NaN). The unordered comparison ensures that s1 becomes int_ty::MIN if + // x is NaN. + // + // Performance note: Unordered comparison can be lowered to a "flipped" + // comparison and a negation, and the negation can be merged into the + // select. Therefore, it not necessarily any more expensive than a + // ordered ("normal") comparison. Whether these optimizations will be + // performed is ultimately up to the backend, but at least x86 does + // perform them. + let s0 = bx.select(less_or_nan, int_min, fptosui_result); + let s1 = bx.select(greater, int_max, s0); + + // Step 3: NaN replacement. + // For unsigned types, the above step already yielded int_ty::MIN == 0 if x is NaN. + // Therefore we only need to execute this step for signed integer types. + if signed { + // LLVM has no isNaN predicate, so we use (x == x) instead + let cmp = bx.fcmp(RealPredicate::RealOEQ, x, x); + bx.select(cmp, s1, zero) } else { - // In this case we cannot execute `fptosi` or `fptoui` and then later - // discard the result. The builder is telling us that these instructions - // will trap on out-of-bounds values, so we need to use basic blocks and - // control flow to avoid executing the `fptosi` and `fptoui` - // instructions. - // - // The general idea of what we're constructing here is, for f64 -> i32: - // - // ;; block so far... %0 is the argument - // %result = alloca i32, align 4 - // %inbound_lower = fcmp oge double %0, 0xC1E0000000000000 - // %inbound_upper = fcmp ole double %0, 0x41DFFFFFFFC00000 - // ;; match (inbound_lower, inbound_upper) { - // ;; (true, true) => %0 can be converted without trapping - // ;; (false, false) => %0 is a NaN - // ;; (true, false) => %0 is too large - // ;; (false, true) => %0 is too small - // ;; } - // ;; - // ;; The (true, true) check, go to %convert if so. - // %inbounds = and i1 %inbound_lower, %inbound_upper - // br i1 %inbounds, label %convert, label %specialcase - // - // convert: - // %cvt = call i32 @llvm.wasm.trunc.signed.i32.f64(double %0) - // store i32 %cvt, i32* %result, align 4 - // br label %done - // - // specialcase: - // ;; Handle the cases where the number is NaN, too large or too small - // - // ;; Either (true, false) or (false, true) - // %is_not_nan = or i1 %inbound_lower, %inbound_upper - // ;; Figure out which saturated value we are interested in if not `NaN` - // %saturated = select i1 %inbound_lower, i32 2147483647, i32 -2147483648 - // ;; Figure out between saturated and NaN representations - // %result_nan = select i1 %is_not_nan, i32 %saturated, i32 0 - // store i32 %result_nan, i32* %result, align 4 - // br label %done - // - // done: - // %r = load i32, i32* %result, align 4 - // ;; ... - let done = bx.build_sibling_block("float_cast_done"); - let mut convert = bx.build_sibling_block("float_cast_convert"); - let mut specialcase = bx.build_sibling_block("float_cast_specialcase"); - - let result = PlaceRef::alloca(bx, int_layout); - result.storage_live(bx); - - // Use control flow to figure out whether we can execute `fptosi` in a - // basic block, or whether we go to a different basic block to implement - // the saturating logic. - let inbound_lower = bx.fcmp(RealPredicate::RealOGE, x, f_min); - let inbound_upper = bx.fcmp(RealPredicate::RealOLE, x, f_max); - let inbounds = bx.and(inbound_lower, inbound_upper); - bx.cond_br(inbounds, convert.llbb(), specialcase.llbb()); - - // Translation of the `convert` basic block - let cvt = if signed { convert.fptosi(x, int_ty) } else { convert.fptoui(x, int_ty) }; - convert.store(cvt, result.llval, result.align); - convert.br(done.llbb()); - - // Translation of the `specialcase` basic block. Note that like above - // we try to be a bit clever here for unsigned conversions. In those - // cases the `int_min` is zero so we don't need two select instructions, - // just one to choose whether we need `int_max` or not. If - // `inbound_lower` is true then we're guaranteed to not be `NaN` and - // since we're greater than zero we must be saturating to `int_max`. If - // `inbound_lower` is false then we're either NaN or less than zero, so - // we saturate to zero. - let result_nan = if signed { - let is_not_nan = specialcase.or(inbound_lower, inbound_upper); - let saturated = specialcase.select(inbound_lower, int_max, int_min); - specialcase.select(is_not_nan, saturated, zero) - } else { - specialcase.select(inbound_lower, int_max, int_min) - }; - specialcase.store(result_nan, result.llval, result.align); - specialcase.br(done.llbb()); - - // Translation of the `done` basic block, positioning ourselves to - // continue from that point as well. - *bx = done; - let ret = bx.load(result.llval, result.align); - result.storage_dead(bx); - ret + s1 } } diff --git a/compiler/rustc_codegen_ssa/src/mir/statement.rs b/compiler/rustc_codegen_ssa/src/mir/statement.rs index 6f74ba77d4c16..fe7f6288adb27 100644 --- a/compiler/rustc_codegen_ssa/src/mir/statement.rs +++ b/compiler/rustc_codegen_ssa/src/mir/statement.rs @@ -112,7 +112,27 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { bx } mir::StatementKind::Coverage(box ref coverage) => { - self.codegen_coverage(&mut bx, coverage.clone()); + self.codegen_coverage(&mut bx, coverage.clone(), statement.source_info.scope); + bx + } + mir::StatementKind::CopyNonOverlapping(box mir::CopyNonOverlapping { + ref src, + ref dst, + ref count, + }) => { + let dst_val = self.codegen_operand(&mut bx, dst); + let src_val = self.codegen_operand(&mut bx, src); + let count = self.codegen_operand(&mut bx, count).immediate(); + let pointee_layout = dst_val + .layout + .pointee_info_at(&mut bx, rustc_target::abi::Size::ZERO) + .expect("Expected pointer"); + let bytes = bx.mul(count, bx.const_usize(pointee_layout.size.bytes())); + + let align = pointee_layout.align; + let dst = dst_val.immediate(); + let src = src_val.immediate(); + bx.memcpy(dst, align, src, align, bytes, crate::MemFlags::empty()); bx } mir::StatementKind::FakeRead(..) diff --git a/compiler/rustc_codegen_ssa/src/target_features.rs b/compiler/rustc_codegen_ssa/src/target_features.rs index fd18f42f2dd4f..4e987908b4ea7 100644 --- a/compiler/rustc_codegen_ssa/src/target_features.rs +++ b/compiler/rustc_codegen_ssa/src/target_features.rs @@ -26,6 +26,7 @@ const ARM_ALLOWED_FEATURES: &[(&str, Option)] = &[ ("vfp2", Some(sym::arm_target_feature)), ("vfp3", Some(sym::arm_target_feature)), ("vfp4", Some(sym::arm_target_feature)), + ("fp-armv8", Some(sym::arm_target_feature)), // This is needed for inline assembly, but shouldn't be stabilized as-is // since it should be enabled per-function using #[instruction_set], not // #[target_feature]. @@ -160,7 +161,7 @@ pub fn supported_target_features(sess: &Session) -> &'static [(&'static str, Opt "mips" | "mips64" => MIPS_ALLOWED_FEATURES, "powerpc" | "powerpc64" => POWERPC_ALLOWED_FEATURES, "riscv32" | "riscv64" => RISCV_ALLOWED_FEATURES, - "wasm32" => WASM_ALLOWED_FEATURES, + "wasm32" | "wasm64" => WASM_ALLOWED_FEATURES, _ => &[], } } diff --git a/compiler/rustc_codegen_ssa/src/traits/builder.rs b/compiler/rustc_codegen_ssa/src/traits/builder.rs index d5bd2780388e6..1bc05f30e5c37 100644 --- a/compiler/rustc_codegen_ssa/src/traits/builder.rs +++ b/compiler/rustc_codegen_ssa/src/traits/builder.rs @@ -171,7 +171,6 @@ pub trait BuilderMethods<'a, 'tcx>: fn sext(&mut self, val: Self::Value, dest_ty: Self::Type) -> Self::Value; fn fptoui_sat(&mut self, val: Self::Value, dest_ty: Self::Type) -> Option; fn fptosi_sat(&mut self, val: Self::Value, dest_ty: Self::Type) -> Option; - fn fptosui_may_trap(&self, val: Self::Value, dest_ty: Self::Type) -> bool; fn fptoui(&mut self, val: Self::Value, dest_ty: Self::Type) -> Self::Value; fn fptosi(&mut self, val: Self::Value, dest_ty: Self::Type) -> Self::Value; fn uitofp(&mut self, val: Self::Value, dest_ty: Self::Type) -> Self::Value; diff --git a/compiler/rustc_codegen_ssa/src/traits/coverageinfo.rs b/compiler/rustc_codegen_ssa/src/traits/coverageinfo.rs index 95bddfb4b41d1..cbf570dba4c3e 100644 --- a/compiler/rustc_codegen_ssa/src/traits/coverageinfo.rs +++ b/compiler/rustc_codegen_ssa/src/traits/coverageinfo.rs @@ -1,14 +1,26 @@ use super::BackendTypes; +use rustc_hir::def_id::DefId; use rustc_middle::mir::coverage::*; use rustc_middle::ty::Instance; -pub trait CoverageInfoMethods: BackendTypes { +pub trait CoverageInfoMethods<'tcx>: BackendTypes { fn coverageinfo_finalize(&self); + + /// Codegen a small function that will never be called, with one counter + /// that will never be incremented, that gives LLVM coverage tools a + /// function definition it needs in order to resolve coverage map references + /// to unused functions. This is necessary so unused functions will appear + /// as uncovered (coverage execution count `0`) in LLVM coverage reports. + fn define_unused_fn(&self, def_id: DefId); + + /// For LLVM codegen, returns a function-specific `Value` for a global + /// string, to hold the function name passed to LLVM intrinsic + /// `instrprof.increment()`. The `Value` is only created once per instance. + /// Multiple invocations with the same instance return the same `Value`. + fn get_pgo_func_name_var(&self, instance: Instance<'tcx>) -> Self::Value; } pub trait CoverageInfoBuilderMethods<'tcx>: BackendTypes { - fn create_pgo_func_name_var(&self, instance: Instance<'tcx>) -> Self::Value; - /// Returns true if the function source hash was added to the coverage map (even if it had /// already been added, for this instance). Returns false *only* if `-Z instrument-coverage` is /// not enabled (a coverage map is not being generated). diff --git a/compiler/rustc_codegen_ssa/src/traits/intrinsic.rs b/compiler/rustc_codegen_ssa/src/traits/intrinsic.rs index ac3c99f9c908d..777436ad2ae8f 100644 --- a/compiler/rustc_codegen_ssa/src/traits/intrinsic.rs +++ b/compiler/rustc_codegen_ssa/src/traits/intrinsic.rs @@ -20,9 +20,10 @@ pub trait IntrinsicCallMethods<'tcx>: BackendTypes { fn abort(&mut self); fn assume(&mut self, val: Self::Value); fn expect(&mut self, cond: Self::Value, expected: bool) -> Self::Value; - /// Normally, sideeffect is only emitted if -Zinsert-sideeffect is passed; - /// in some cases though we want to emit it regardless. - fn sideeffect(&mut self, unconditional: bool); + /// Emits a forced side effect. + /// + /// Currently has any effect only when LLVM versions prior to 12.0 are used as the backend. + fn sideeffect(&mut self); /// Trait method used to inject `va_start` on the "spoofed" `VaListImpl` in /// Rust defined C-variadic functions. fn va_start(&mut self, val: Self::Value) -> Self::Value; diff --git a/compiler/rustc_codegen_ssa/src/traits/mod.rs b/compiler/rustc_codegen_ssa/src/traits/mod.rs index 8ada6c10479da..be2e0ea230f30 100644 --- a/compiler/rustc_codegen_ssa/src/traits/mod.rs +++ b/compiler/rustc_codegen_ssa/src/traits/mod.rs @@ -58,7 +58,7 @@ pub trait CodegenMethods<'tcx>: + MiscMethods<'tcx> + ConstMethods<'tcx> + StaticMethods - + CoverageInfoMethods + + CoverageInfoMethods<'tcx> + DebugInfoMethods<'tcx> + AsmMethods + PreDefineMethods<'tcx> @@ -74,7 +74,7 @@ impl<'tcx, T> CodegenMethods<'tcx> for T where + MiscMethods<'tcx> + ConstMethods<'tcx> + StaticMethods - + CoverageInfoMethods + + CoverageInfoMethods<'tcx> + DebugInfoMethods<'tcx> + AsmMethods + PreDefineMethods<'tcx> diff --git a/compiler/rustc_data_structures/Cargo.toml b/compiler/rustc_data_structures/Cargo.toml index 818c43642566c..d0a5fe1f2a63c 100644 --- a/compiler/rustc_data_structures/Cargo.toml +++ b/compiler/rustc_data_structures/Cargo.toml @@ -8,7 +8,7 @@ edition = "2018" doctest = false [dependencies] -arrayvec = { version = "0.5.1", default-features = false } +arrayvec = { version = "0.7", default-features = false } ena = "0.14" indexmap = "1.5.1" tracing = "0.1" @@ -19,8 +19,8 @@ rustc_graphviz = { path = "../rustc_graphviz" } cfg-if = "0.1.2" crossbeam-utils = { version = "0.7", features = ["nightly"] } stable_deref_trait = "1.0.0" -rayon = { version = "0.3.0", package = "rustc-rayon" } -rayon-core = { version = "0.3.0", package = "rustc-rayon-core" } +rayon = { version = "0.3.1", package = "rustc-rayon" } +rayon-core = { version = "0.3.1", package = "rustc-rayon-core" } rustc-hash = "1.1.0" smallvec = { version = "1.6.1", features = ["union", "may_dangle"] } rustc_index = { path = "../rustc_index", package = "rustc_index" } @@ -36,3 +36,6 @@ features = ["nightly"] [target.'cfg(windows)'.dependencies] winapi = { version = "0.3", features = ["fileapi", "psapi"] } + +[target.'cfg(not(target_arch = "wasm32"))'.dependencies] +memmap2 = "0.2.1" diff --git a/compiler/rustc_data_structures/src/fingerprint.rs b/compiler/rustc_data_structures/src/fingerprint.rs index 08c3419a8421d..c0c0e7be3ca85 100644 --- a/compiler/rustc_data_structures/src/fingerprint.rs +++ b/compiler/rustc_data_structures/src/fingerprint.rs @@ -1,17 +1,20 @@ use crate::stable_hasher; -use rustc_serialize::{ - opaque::{self, EncodeResult, FileEncodeResult}, - Decodable, Encodable, -}; +use rustc_serialize::{Decodable, Encodable}; +use std::convert::TryInto; use std::hash::{Hash, Hasher}; -use std::mem::{self, MaybeUninit}; #[derive(Eq, PartialEq, Ord, PartialOrd, Debug, Clone, Copy)] +#[repr(C)] pub struct Fingerprint(u64, u64); impl Fingerprint { pub const ZERO: Fingerprint = Fingerprint(0, 0); + #[inline] + pub fn new(_0: u64, _1: u64) -> Fingerprint { + Fingerprint(_0, _1) + } + #[inline] pub fn from_smaller_hash(hash: u64) -> Fingerprint { Fingerprint(hash, hash) @@ -19,7 +22,12 @@ impl Fingerprint { #[inline] pub fn to_smaller_hash(&self) -> u64 { - self.0 + // Even though both halves of the fingerprint are expected to be good + // quality hash values, let's still combine the two values because the + // Fingerprints in DefPathHash have the StableCrateId portion which is + // the same for all DefPathHashes from the same crate. Combining the + // two halfs makes sure we get a good quality hash in such cases too. + self.0.wrapping_mul(3).wrapping_add(self.1) } #[inline] @@ -53,14 +61,27 @@ impl Fingerprint { format!("{:x}{:x}", self.0, self.1) } - pub fn decode_opaque(decoder: &mut opaque::Decoder<'_>) -> Result { - let mut bytes: [MaybeUninit; 16] = MaybeUninit::uninit_array(); + #[inline] + pub fn to_le_bytes(&self) -> [u8; 16] { + // This seems to optimize to the same machine code as + // `unsafe { mem::transmute(*k) }`. Well done, LLVM! :) + let mut result = [0u8; 16]; - decoder.read_raw_bytes(&mut bytes)?; + let first_half: &mut [u8; 8] = (&mut result[0..8]).try_into().unwrap(); + *first_half = self.0.to_le_bytes(); - let [l, r]: [u64; 2] = unsafe { mem::transmute(bytes) }; + let second_half: &mut [u8; 8] = (&mut result[8..16]).try_into().unwrap(); + *second_half = self.1.to_le_bytes(); - Ok(Fingerprint(u64::from_le(l), u64::from_le(r))) + result + } + + #[inline] + pub fn from_le_bytes(bytes: [u8; 16]) -> Fingerprint { + Fingerprint( + u64::from_le_bytes(bytes[0..8].try_into().unwrap()), + u64::from_le_bytes(bytes[8..16].try_into().unwrap()), + ) } } @@ -92,8 +113,19 @@ impl FingerprintHasher for H { impl FingerprintHasher for crate::unhash::Unhasher { #[inline] fn write_fingerprint(&mut self, fingerprint: &Fingerprint) { - // `Unhasher` only wants a single `u64` - self.write_u64(fingerprint.0); + // Even though both halves of the fingerprint are expected to be good + // quality hash values, let's still combine the two values because the + // Fingerprints in DefPathHash have the StableCrateId portion which is + // the same for all DefPathHashes from the same crate. Combining the + // two halfs makes sure we get a good quality hash in such cases too. + // + // Since `Unhasher` is used only in the context of HashMaps, it is OK + // to combine the two components in an order-independent way (which is + // cheaper than the more robust Fingerprint::to_smaller_hash()). For + // HashMaps we don't really care if Fingerprint(x,y) and + // Fingerprint(y, x) result in the same hash value. Collision + // probability will still be much better than with FxHash. + self.write_u64(fingerprint.0.wrapping_add(fingerprint.1)); } } @@ -108,55 +140,19 @@ impl stable_hasher::StableHasherResult for Fingerprint { impl_stable_hash_via_hash!(Fingerprint); impl Encodable for Fingerprint { + #[inline] fn encode(&self, s: &mut E) -> Result<(), E::Error> { - s.encode_fingerprint(self) + s.emit_raw_bytes(&self.to_le_bytes()[..])?; + Ok(()) } } impl Decodable for Fingerprint { + #[inline] fn decode(d: &mut D) -> Result { - d.decode_fingerprint() - } -} - -pub trait FingerprintEncoder: rustc_serialize::Encoder { - fn encode_fingerprint(&mut self, f: &Fingerprint) -> Result<(), Self::Error>; -} - -pub trait FingerprintDecoder: rustc_serialize::Decoder { - fn decode_fingerprint(&mut self) -> Result; -} - -impl FingerprintEncoder for E { - default fn encode_fingerprint(&mut self, _: &Fingerprint) -> Result<(), E::Error> { - panic!("Cannot encode `Fingerprint` with `{}`", std::any::type_name::()); - } -} - -impl FingerprintEncoder for opaque::Encoder { - fn encode_fingerprint(&mut self, f: &Fingerprint) -> EncodeResult { - let bytes: [u8; 16] = unsafe { mem::transmute([f.0.to_le(), f.1.to_le()]) }; - self.emit_raw_bytes(&bytes); - Ok(()) - } -} - -impl FingerprintEncoder for opaque::FileEncoder { - fn encode_fingerprint(&mut self, f: &Fingerprint) -> FileEncodeResult { - let bytes: [u8; 16] = unsafe { mem::transmute([f.0.to_le(), f.1.to_le()]) }; - self.emit_raw_bytes(&bytes) - } -} - -impl FingerprintDecoder for D { - default fn decode_fingerprint(&mut self) -> Result { - panic!("Cannot decode `Fingerprint` with `{}`", std::any::type_name::()); - } -} - -impl FingerprintDecoder for opaque::Decoder<'_> { - fn decode_fingerprint(&mut self) -> Result { - Fingerprint::decode_opaque(self) + let mut bytes = [0u8; 16]; + d.read_raw_bytes_into(&mut bytes[..])?; + Ok(Fingerprint::from_le_bytes(bytes)) } } diff --git a/compiler/rustc_data_structures/src/lib.rs b/compiler/rustc_data_structures/src/lib.rs index fcb2bca7b4cc1..adbb98fa75047 100644 --- a/compiler/rustc_data_structures/src/lib.rs +++ b/compiler/rustc_data_structures/src/lib.rs @@ -13,7 +13,6 @@ #![feature(unboxed_closures)] #![feature(generator_trait)] #![feature(fn_traits)] -#![feature(int_bits_const)] #![feature(min_specialization)] #![feature(auto_traits)] #![feature(nll)] @@ -85,6 +84,7 @@ pub mod snapshot_map; pub mod stable_map; pub mod svh; pub use ena::snapshot_vec; +pub mod memmap; pub mod sorted_map; pub mod stable_set; #[macro_use] diff --git a/compiler/rustc_data_structures/src/macros.rs b/compiler/rustc_data_structures/src/macros.rs index b918ed9458cda..48dfbba7504ef 100644 --- a/compiler/rustc_data_structures/src/macros.rs +++ b/compiler/rustc_data_structures/src/macros.rs @@ -9,11 +9,11 @@ macro_rules! static_assert_size { #[macro_export] macro_rules! enum_from_u32 { ($(#[$attr:meta])* pub enum $name:ident { - $($variant:ident = $e:expr,)* + $($(#[$var_attr:meta])* $variant:ident = $e:expr,)* }) => { $(#[$attr])* pub enum $name { - $($variant = $e),* + $($(#[$var_attr])* $variant = $e),* } impl $name { @@ -26,11 +26,11 @@ macro_rules! enum_from_u32 { } }; ($(#[$attr:meta])* pub enum $name:ident { - $($variant:ident,)* + $($(#[$var_attr:meta])* $variant:ident,)* }) => { $(#[$attr])* pub enum $name { - $($variant,)* + $($(#[$var_attr])* $variant,)* } impl $name { diff --git a/compiler/rustc_data_structures/src/memmap.rs b/compiler/rustc_data_structures/src/memmap.rs new file mode 100644 index 0000000000000..26b26415eea0f --- /dev/null +++ b/compiler/rustc_data_structures/src/memmap.rs @@ -0,0 +1,47 @@ +use std::fs::File; +use std::io; +use std::ops::Deref; + +use crate::owning_ref::StableAddress; + +/// A trivial wrapper for [`memmap2::Mmap`] that implements [`StableAddress`]. +#[cfg(not(target_arch = "wasm32"))] +pub struct Mmap(memmap2::Mmap); + +#[cfg(target_arch = "wasm32")] +pub struct Mmap(Vec); + +#[cfg(not(target_arch = "wasm32"))] +impl Mmap { + #[inline] + pub unsafe fn map(file: File) -> io::Result { + memmap2::Mmap::map(&file).map(Mmap) + } +} + +#[cfg(target_arch = "wasm32")] +impl Mmap { + #[inline] + pub unsafe fn map(mut file: File) -> io::Result { + use std::io::Read; + + let mut data = Vec::new(); + file.read_to_end(&mut data)?; + Ok(Mmap(data)) + } +} + +impl Deref for Mmap { + type Target = [u8]; + + #[inline] + fn deref(&self) -> &[u8] { + &*self.0 + } +} + +// SAFETY: On architectures other than WASM, mmap is used as backing storage. The address of this +// memory map is stable. On WASM, `Vec` is used as backing storage. The `Mmap` type doesn't +// export any function that can cause the `Vec` to be re-allocated. As such the address of the +// bytes inside this `Vec` is stable. +unsafe impl StableAddress for Mmap {} diff --git a/compiler/rustc_data_structures/src/sso/map.rs b/compiler/rustc_data_structures/src/sso/map.rs index 06e8442d47539..e249886e9bc01 100644 --- a/compiler/rustc_data_structures/src/sso/map.rs +++ b/compiler/rustc_data_structures/src/sso/map.rs @@ -18,11 +18,8 @@ use std::ops::Index; // for reasonably small arrays that stay // small in vast majority of cases. // -// '8' is choosen as a sane default, to be +// '8' is chosen as a sane default, to be // reevaluated later. -// -// Note: As of now ArrayVec design prevents -// us from making it user-customizable. const SSO_ARRAY_SIZE: usize = 8; /// Small-storage-optimized implementation of a map. @@ -70,7 +67,7 @@ const SSO_ARRAY_SIZE: usize = 8; #[derive(Clone)] pub enum SsoHashMap { - Array(ArrayVec<[(K, V); SSO_ARRAY_SIZE]>), + Array(ArrayVec<(K, V), SSO_ARRAY_SIZE>), Map(FxHashMap), } @@ -411,7 +408,7 @@ where impl IntoIterator for SsoHashMap { type IntoIter = EitherIter< - as IntoIterator>::IntoIter, + as IntoIterator>::IntoIter, as IntoIterator>::IntoIter, >; type Item = ::Item; @@ -441,7 +438,7 @@ fn adapt_array_mut_it(pair: &'a mut (K, V)) -> (&'a K, &'a mut V) { impl<'a, K, V> IntoIterator for &'a SsoHashMap { type IntoIter = EitherIter< std::iter::Map< - <&'a ArrayVec<[(K, V); 8]> as IntoIterator>::IntoIter, + <&'a ArrayVec<(K, V), 8> as IntoIterator>::IntoIter, fn(&'a (K, V)) -> (&'a K, &'a V), >, <&'a FxHashMap as IntoIterator>::IntoIter, @@ -459,7 +456,7 @@ impl<'a, K, V> IntoIterator for &'a SsoHashMap { impl<'a, K, V> IntoIterator for &'a mut SsoHashMap { type IntoIter = EitherIter< std::iter::Map< - <&'a mut ArrayVec<[(K, V); 8]> as IntoIterator>::IntoIter, + <&'a mut ArrayVec<(K, V), 8> as IntoIterator>::IntoIter, fn(&'a mut (K, V)) -> (&'a K, &'a mut V), >, <&'a mut FxHashMap as IntoIterator>::IntoIter, diff --git a/compiler/rustc_data_structures/src/stable_hasher.rs b/compiler/rustc_data_structures/src/stable_hasher.rs index 3850c9b74fddc..ff28784a1dc42 100644 --- a/compiler/rustc_data_structures/src/stable_hasher.rs +++ b/compiler/rustc_data_structures/src/stable_hasher.rs @@ -35,6 +35,7 @@ impl StableHasher { StableHasher { state: SipHasher128::new_with_keys(0, 0) } } + #[inline] pub fn finish(self) -> W { W::finish(self) } diff --git a/compiler/rustc_data_structures/src/svh.rs b/compiler/rustc_data_structures/src/svh.rs index 02103de2e8df9..ce90fbacaa46c 100644 --- a/compiler/rustc_data_structures/src/svh.rs +++ b/compiler/rustc_data_structures/src/svh.rs @@ -19,7 +19,7 @@ pub struct Svh { impl Svh { /// Creates a new `Svh` given the hash. If you actually want to /// compute the SVH from some HIR, you want the `calculate_svh` - /// function found in `librustc_incremental`. + /// function found in `rustc_incremental`. pub fn new(hash: u64) -> Svh { Svh { hash } } diff --git a/compiler/rustc_data_structures/src/tagged_ptr/drop.rs b/compiler/rustc_data_structures/src/tagged_ptr/drop.rs index 63f64beae5a07..d44ccd368b3cb 100644 --- a/compiler/rustc_data_structures/src/tagged_ptr/drop.rs +++ b/compiler/rustc_data_structures/src/tagged_ptr/drop.rs @@ -42,18 +42,9 @@ where pub fn pointer_ref(&self) -> &P::Target { self.raw.pointer_ref() } - pub fn pointer_mut(&mut self) -> &mut P::Target - where - P: std::ops::DerefMut, - { - self.raw.pointer_mut() - } pub fn tag(&self) -> T { self.raw.tag() } - pub fn set_tag(&mut self, tag: T) { - self.raw.set_tag(tag); - } } impl std::ops::Deref for TaggedPtr diff --git a/compiler/rustc_data_structures/src/thin_vec.rs b/compiler/rustc_data_structures/src/thin_vec.rs index 4d673fd5cf983..00e304734983f 100644 --- a/compiler/rustc_data_structures/src/thin_vec.rs +++ b/compiler/rustc_data_structures/src/thin_vec.rs @@ -1,5 +1,7 @@ use crate::stable_hasher::{HashStable, StableHasher}; +use std::iter::FromIterator; + /// A vector type optimized for cases where this size is usually 0 (cf. `SmallVector`). /// The `Option>` wrapping allows us to represent a zero sized vector with `None`, /// which uses only a single (null) pointer. @@ -10,6 +12,14 @@ impl ThinVec { pub fn new() -> Self { ThinVec(None) } + + pub fn iter(&self) -> std::slice::Iter<'_, T> { + self.into_iter() + } + + pub fn iter_mut(&mut self) -> std::slice::IterMut<'_, T> { + self.into_iter() + } } impl From> for ThinVec { @@ -46,6 +56,42 @@ impl ::std::ops::DerefMut for ThinVec { } } +impl FromIterator for ThinVec { + fn from_iter>(iter: I) -> Self { + // `Vec::from_iter()` should not allocate if the iterator is empty. + let vec: Vec<_> = iter.into_iter().collect(); + if vec.is_empty() { ThinVec(None) } else { ThinVec(Some(Box::new(vec))) } + } +} + +impl IntoIterator for ThinVec { + type Item = T; + type IntoIter = std::vec::IntoIter; + + fn into_iter(self) -> Self::IntoIter { + // This is still performant because `Vec::new()` does not allocate. + self.0.map_or_else(Vec::new, |ptr| *ptr).into_iter() + } +} + +impl<'a, T> IntoIterator for &'a ThinVec { + type Item = &'a T; + type IntoIter = std::slice::Iter<'a, T>; + + fn into_iter(self) -> Self::IntoIter { + self.as_ref().iter() + } +} + +impl<'a, T> IntoIterator for &'a mut ThinVec { + type Item = &'a mut T; + type IntoIter = std::slice::IterMut<'a, T>; + + fn into_iter(self) -> Self::IntoIter { + self.as_mut().iter_mut() + } +} + impl Extend for ThinVec { fn extend>(&mut self, iter: I) { match *self { @@ -80,3 +126,6 @@ impl Default for ThinVec { Self(None) } } + +#[cfg(test)] +mod tests; diff --git a/compiler/rustc_data_structures/src/thin_vec/tests.rs b/compiler/rustc_data_structures/src/thin_vec/tests.rs new file mode 100644 index 0000000000000..5abfd939373c7 --- /dev/null +++ b/compiler/rustc_data_structures/src/thin_vec/tests.rs @@ -0,0 +1,42 @@ +use super::*; + +impl ThinVec { + fn into_vec(self) -> Vec { + self.into() + } +} + +#[test] +fn test_from_iterator() { + assert_eq!(std::iter::empty().collect::>().into_vec(), Vec::::new()); + assert_eq!(std::iter::once(42).collect::>().into_vec(), vec![42]); + assert_eq!(vec![1, 2].into_iter().collect::>().into_vec(), vec![1, 2]); + assert_eq!(vec![1, 2, 3].into_iter().collect::>().into_vec(), vec![1, 2, 3]); +} + +#[test] +fn test_into_iterator_owned() { + assert_eq!(ThinVec::new().into_iter().collect::>(), Vec::::new()); + assert_eq!(ThinVec::from(vec![1]).into_iter().collect::>(), vec![1]); + assert_eq!(ThinVec::from(vec![1, 2]).into_iter().collect::>(), vec![1, 2]); + assert_eq!(ThinVec::from(vec![1, 2, 3]).into_iter().collect::>(), vec![1, 2, 3]); +} + +#[test] +fn test_into_iterator_ref() { + assert_eq!(ThinVec::new().iter().collect::>(), Vec::<&String>::new()); + assert_eq!(ThinVec::from(vec![1]).iter().collect::>(), vec![&1]); + assert_eq!(ThinVec::from(vec![1, 2]).iter().collect::>(), vec![&1, &2]); + assert_eq!(ThinVec::from(vec![1, 2, 3]).iter().collect::>(), vec![&1, &2, &3]); +} + +#[test] +fn test_into_iterator_ref_mut() { + assert_eq!(ThinVec::new().iter_mut().collect::>(), Vec::<&mut String>::new()); + assert_eq!(ThinVec::from(vec![1]).iter_mut().collect::>(), vec![&mut 1]); + assert_eq!(ThinVec::from(vec![1, 2]).iter_mut().collect::>(), vec![&mut 1, &mut 2]); + assert_eq!( + ThinVec::from(vec![1, 2, 3]).iter_mut().collect::>(), + vec![&mut 1, &mut 2, &mut 3], + ); +} diff --git a/compiler/rustc_data_structures/src/tiny_list.rs b/compiler/rustc_data_structures/src/tiny_list.rs index e94a0c6eb5943..f88bcc2948150 100644 --- a/compiler/rustc_data_structures/src/tiny_list.rs +++ b/compiler/rustc_data_structures/src/tiny_list.rs @@ -15,7 +15,7 @@ mod tests; #[derive(Clone)] -pub struct TinyList { +pub struct TinyList { head: Option>, } @@ -56,20 +56,10 @@ impl TinyList { } false } - - #[inline] - pub fn len(&self) -> usize { - let (mut elem, mut count) = (self.head.as_ref(), 0); - while let Some(ref e) = elem { - count += 1; - elem = e.next.as_deref(); - } - count - } } #[derive(Clone)] -struct Element { +struct Element { data: T, next: Option>>, } diff --git a/compiler/rustc_data_structures/src/tiny_list/tests.rs b/compiler/rustc_data_structures/src/tiny_list/tests.rs index a8ae2bc872789..c0334d2e23e55 100644 --- a/compiler/rustc_data_structures/src/tiny_list/tests.rs +++ b/compiler/rustc_data_structures/src/tiny_list/tests.rs @@ -3,6 +3,17 @@ use super::*; extern crate test; use test::{black_box, Bencher}; +impl TinyList { + fn len(&self) -> usize { + let (mut elem, mut count) = (self.head.as_ref(), 0); + while let Some(ref e) = elem { + count += 1; + elem = e.next.as_deref(); + } + count + } +} + #[test] fn test_contains_and_insert() { fn do_insert(i: u32) -> bool { diff --git a/compiler/rustc_data_structures/src/transitive_relation.rs b/compiler/rustc_data_structures/src/transitive_relation.rs index 2e1512b3929ca..ccf8bd69ebd06 100644 --- a/compiler/rustc_data_structures/src/transitive_relation.rs +++ b/compiler/rustc_data_structures/src/transitive_relation.rs @@ -9,7 +9,7 @@ use std::mem; mod tests; #[derive(Clone, Debug)] -pub struct TransitiveRelation { +pub struct TransitiveRelation { // List of elements. This is used to map from a T to a usize. elements: FxIndexSet, @@ -49,7 +49,7 @@ struct Edge { target: Index, } -impl TransitiveRelation { +impl TransitiveRelation { pub fn is_empty(&self) -> bool { self.edges.is_empty() } @@ -322,12 +322,6 @@ impl TransitiveRelation { .collect() } - /// A "best" parent in some sense. See `parents` and - /// `postdom_upper_bound` for more details. - pub fn postdom_parent(&self, a: &T) -> Option<&T> { - self.mutual_immediate_postdominator(self.parents(a)) - } - fn with_closure(&self, op: OP) -> R where OP: FnOnce(&BitMatrix) -> R, diff --git a/compiler/rustc_data_structures/src/transitive_relation/tests.rs b/compiler/rustc_data_structures/src/transitive_relation/tests.rs index ca90ba176ae1a..9fa7224376c1c 100644 --- a/compiler/rustc_data_structures/src/transitive_relation/tests.rs +++ b/compiler/rustc_data_structures/src/transitive_relation/tests.rs @@ -1,5 +1,13 @@ use super::*; +impl TransitiveRelation { + /// A "best" parent in some sense. See `parents` and + /// `postdom_upper_bound` for more details. + fn postdom_parent(&self, a: &T) -> Option<&T> { + self.mutual_immediate_postdominator(self.parents(a)) + } +} + #[test] fn test_one_step() { let mut relation = TransitiveRelation::default(); diff --git a/compiler/rustc_data_structures/src/work_queue.rs b/compiler/rustc_data_structures/src/work_queue.rs index cc562bc1e4d91..10317f1afff67 100644 --- a/compiler/rustc_data_structures/src/work_queue.rs +++ b/compiler/rustc_data_structures/src/work_queue.rs @@ -41,10 +41,4 @@ impl WorkQueue { None } } - - /// Returns `true` if nothing is enqueued. - #[inline] - pub fn is_empty(&self) -> bool { - self.deque.is_empty() - } } diff --git a/compiler/rustc_driver/Cargo.toml b/compiler/rustc_driver/Cargo.toml index 47cff34cd3e8c..c521f2041d891 100644 --- a/compiler/rustc_driver/Cargo.toml +++ b/compiler/rustc_driver/Cargo.toml @@ -12,7 +12,7 @@ libc = "0.2" atty = "0.2" tracing = { version = "0.1.25" } tracing-subscriber = { version = "0.2.16", default-features = false, features = ["fmt", "env-filter", "smallvec", "parking_lot", "ansi"] } -tracing-tree = "0.1.8" +tracing-tree = "0.1.9" rustc_middle = { path = "../rustc_middle" } rustc_ast_pretty = { path = "../rustc_ast_pretty" } rustc_target = { path = "../rustc_target" } @@ -34,6 +34,8 @@ rustc_interface = { path = "../rustc_interface" } rustc_serialize = { path = "../rustc_serialize" } rustc_ast = { path = "../rustc_ast" } rustc_span = { path = "../rustc_span" } +rustc_mir_build = { path = "../rustc_mir_build" } +rustc_typeck = { path = "../rustc_typeck" } [target.'cfg(windows)'.dependencies] winapi = { version = "0.3", features = ["consoleapi", "debugapi", "processenv"] } diff --git a/compiler/rustc_driver/src/lib.rs b/compiler/rustc_driver/src/lib.rs index 79bb21b29fc9b..51699403a3705 100644 --- a/compiler/rustc_driver/src/lib.rs +++ b/compiler/rustc_driver/src/lib.rs @@ -146,6 +146,7 @@ impl<'a, 'b> RunCompiler<'a, 'b> { pub fn new(at_args: &'a [String], callbacks: &'b mut (dyn Callbacks + Send)) -> Self { Self { at_args, callbacks, file_loader: None, emitter: None, make_codegen_backend: None } } + /// Used by cg_clif. pub fn set_make_codegen_backend( &mut self, make_codegen_backend: Option< @@ -155,10 +156,12 @@ impl<'a, 'b> RunCompiler<'a, 'b> { self.make_codegen_backend = make_codegen_backend; self } + /// Used by RLS. pub fn set_emitter(&mut self, emitter: Option>) -> &mut Self { self.emitter = emitter; self } + /// Used by RLS. pub fn set_file_loader( &mut self, file_loader: Option>, @@ -215,6 +218,7 @@ fn run_compiler( diagnostic_output, stderr: None, lint_caps: Default::default(), + parse_sess_created: None, register_lints: None, override_queries: None, make_codegen_backend: make_codegen_backend.take().unwrap(), @@ -298,6 +302,7 @@ fn run_compiler( diagnostic_output, stderr: None, lint_caps: Default::default(), + parse_sess_created: None, register_lints: None, override_queries: None, make_codegen_backend: make_codegen_backend.unwrap(), @@ -790,7 +795,7 @@ pub fn version(binary: &str, matches: &getopts::Matches) { println!("host: {}", config::host_triple()); println!("release: {}", unw(util::release_str())); if cfg!(feature = "llvm") { - get_builtin_codegen_backend("llvm")().print_version(); + get_builtin_codegen_backend(&None, "llvm")().print_version(); } } } @@ -840,7 +845,8 @@ the command line flag directly. ); } -fn describe_lints(sess: &Session, lint_store: &LintStore, loaded_plugins: bool) { +/// Write to stdout lint command options, together with a list of all available lints +pub fn describe_lints(sess: &Session, lint_store: &LintStore, loaded_plugins: bool) { println!( " Available lint options: @@ -893,7 +899,12 @@ Available lint options: let print_lints = |lints: Vec<&Lint>| { for lint in lints { let name = lint.name_lower().replace("_", "-"); - println!(" {} {:7.7} {}", padded(&name), lint.default_level.as_str(), lint.desc); + println!( + " {} {:7.7} {}", + padded(&name), + lint.default_level(sess.edition()).as_str(), + lint.desc + ); } println!("\n"); }; @@ -1079,7 +1090,7 @@ pub fn handle_options(args: &[String]) -> Option { if cg_flags.iter().any(|x| *x == "passes=list") { if cfg!(feature = "llvm") { - get_builtin_codegen_backend("llvm")().print_passes(); + get_builtin_codegen_backend(&None, "llvm")().print_passes(); } return None; } diff --git a/compiler/rustc_driver/src/pretty.rs b/compiler/rustc_driver/src/pretty.rs index 38c493a920d26..e0c140b143be8 100644 --- a/compiler/rustc_driver/src/pretty.rs +++ b/compiler/rustc_driver/src/pretty.rs @@ -9,12 +9,14 @@ use rustc_hir_pretty as pprust_hir; use rustc_middle::hir::map as hir_map; use rustc_middle::ty::{self, TyCtxt}; use rustc_mir::util::{write_mir_graphviz, write_mir_pretty}; +use rustc_mir_build::thir; use rustc_session::config::{Input, PpAstTreeMode, PpHirMode, PpMode, PpSourceMode}; use rustc_session::Session; use rustc_span::symbol::Ident; use rustc_span::FileName; use std::cell::Cell; +use std::fmt::Write; use std::path::Path; pub use self::PpMode::*; @@ -106,13 +108,6 @@ trait HirPrinterSupport<'hir>: pprust_hir::PpAnn { /// (Rust does not yet support upcasting from a trait object to /// an object for one of its super-traits.) fn pp_ann(&self) -> &dyn pprust_hir::PpAnn; - - /// Computes an user-readable representation of a path, if possible. - fn node_path(&self, id: hir::HirId) -> Option { - self.hir_map().and_then(|map| map.def_path_from_hir_id(id)).map(|path| { - path.data.into_iter().map(|elem| elem.data.to_string()).collect::>().join("::") - }) - } } struct NoAnn<'hir> { @@ -325,10 +320,6 @@ impl<'tcx> HirPrinterSupport<'tcx> for TypedAnnotation<'tcx> { fn pp_ann(&self) -> &dyn pprust_hir::PpAnn { self } - - fn node_path(&self, id: hir::HirId) -> Option { - Some(self.tcx.def_path_str(self.tcx.hir().local_def_id(id).to_def_id())) - } } impl<'tcx> pprust_hir::PpAnn for TypedAnnotation<'tcx> { @@ -484,18 +475,40 @@ fn print_with_analysis( ppm: PpMode, ofile: Option<&Path>, ) -> Result<(), ErrorReported> { - let mut out = Vec::new(); - tcx.analysis(LOCAL_CRATE)?; - match ppm { - Mir => write_mir_pretty(tcx, None, &mut out).unwrap(), - MirCFG => write_mir_graphviz(tcx, None, &mut out).unwrap(), + let out = match ppm { + Mir => { + let mut out = Vec::new(); + write_mir_pretty(tcx, None, &mut out).unwrap(); + String::from_utf8(out).unwrap() + } + + MirCFG => { + let mut out = Vec::new(); + write_mir_graphviz(tcx, None, &mut out).unwrap(); + String::from_utf8(out).unwrap() + } + + ThirTree => { + let mut out = String::new(); + abort_on_err(rustc_typeck::check_crate(tcx), tcx.sess); + debug!("pretty printing THIR tree"); + for did in tcx.body_owners() { + let hir = tcx.hir(); + let body = hir.body(hir.body_owned_by(hir.local_def_id_to_hir_id(did))); + let arena = thir::Arena::default(); + let thir = + thir::build_thir(tcx, ty::WithOptConstParam::unknown(did), &arena, &body.value); + let _ = writeln!(out, "{:?}:\n{:#?}\n", did, thir); + } + out + } + _ => unreachable!(), - } + }; - let out = std::str::from_utf8(&out).unwrap(); - write_or_print(out, ofile); + write_or_print(&out, ofile); Ok(()) } diff --git a/compiler/rustc_error_codes/src/error_codes/E0128.md b/compiler/rustc_error_codes/src/error_codes/E0128.md index 6f8dfe3a73b9e..2ea8ae68ef877 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0128.md +++ b/compiler/rustc_error_codes/src/error_codes/E0128.md @@ -7,7 +7,7 @@ struct Foo { field1: T, field2: U, } -// error: type parameters with a default cannot use forward declared +// error: generic parameters with a default cannot use forward declared // identifiers ``` diff --git a/compiler/rustc_error_codes/src/error_codes/E0137.md b/compiler/rustc_error_codes/src/error_codes/E0137.md index 0a02913d2364d..d4e19170f3f7b 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0137.md +++ b/compiler/rustc_error_codes/src/error_codes/E0137.md @@ -1,8 +1,10 @@ +#### Note: this error code is no longer emitted by the compiler. + More than one function was declared with the `#[main]` attribute. Erroneous code example: -```compile_fail,E0137 +```compile_fail #![feature(main)] #[main] @@ -16,7 +18,7 @@ This error indicates that the compiler found multiple functions with the `#[main]` attribute. This is an error because there must be a unique entry point into a Rust program. Example: -``` +```compile_fail #![feature(main)] #[main] diff --git a/compiler/rustc_error_codes/src/error_codes/E0404.md b/compiler/rustc_error_codes/src/error_codes/E0404.md index 1360cc99afcc4..d6fa51e618c4c 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0404.md +++ b/compiler/rustc_error_codes/src/error_codes/E0404.md @@ -8,14 +8,15 @@ struct Foo; struct Bar; impl Foo for Bar {} // error: `Foo` is not a trait +fn baz(t: T) {} // error: `Foo` is not a trait ``` Another erroneous code example: ```compile_fail,E0404 -struct Foo; +type Foo = Iterator; -fn bar(t: T) {} // error: `Foo` is not a trait +fn bar(t: T) {} // error: `Foo` is a type alias ``` Please verify that the trait's name was not misspelled or that the right @@ -30,14 +31,27 @@ struct Bar; impl Foo for Bar { // ok! // functions implementation } + +fn baz(t: T) {} // ok! ``` -or: +Alternatively, you could introduce a new trait with your desired restrictions +as a super trait: ``` -trait Foo { - // some functions -} +# trait Foo {} +# struct Bar; +# impl Foo for Bar {} +trait Qux: Foo {} // Anything that implements Qux also needs to implement Foo +fn baz(t: T) {} // also ok! +``` + +Finally, if you are on nightly and want to use a trait alias +instead of a type alias, you should use `#![feature(trait_alias)]`: + +``` +#![feature(trait_alias)] +trait Foo = Iterator; fn bar(t: T) {} // ok! ``` diff --git a/compiler/rustc_error_codes/src/error_codes/E0554.md b/compiler/rustc_error_codes/src/error_codes/E0554.md index e55fa4c6ede8b..3178bf21919e8 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0554.md +++ b/compiler/rustc_error_codes/src/error_codes/E0554.md @@ -4,8 +4,8 @@ beta compilers will not comply. Erroneous code example: ```ignore (depends on release channel) -#![feature(non_ascii_idents)] // error: `#![feature]` may not be used on the - // stable release channel +#![feature(lang_items)] // error: `#![feature]` may not be used on the + // stable release channel ``` If you need the feature, make sure to use a nightly release of the compiler diff --git a/compiler/rustc_error_codes/src/error_codes/E0754.md b/compiler/rustc_error_codes/src/error_codes/E0754.md index 9f4b19cfda6b4..acddb69aaba56 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0754.md +++ b/compiler/rustc_error_codes/src/error_codes/E0754.md @@ -3,7 +3,6 @@ A non-ASCII identifier was used in an invalid context. Erroneous code examples: ```compile_fail,E0754 -# #![feature(non_ascii_idents)] mod řųśť; // error! @@ -17,8 +16,6 @@ Non-ASCII can be used as module names if it is inlined or if a `#[path]` attribute is specified. For example: ``` -# #![feature(non_ascii_idents)] - mod řųśť { // ok! const IS_GREAT: bool = true; } diff --git a/compiler/rustc_error_codes/src/lib.rs b/compiler/rustc_error_codes/src/lib.rs index 14ddb3e20793a..f2432f6165353 100644 --- a/compiler/rustc_error_codes/src/lib.rs +++ b/compiler/rustc_error_codes/src/lib.rs @@ -1,5 +1,4 @@ -#![cfg_attr(bootstrap, deny(invalid_codeblock_attributes))] -#![cfg_attr(not(bootstrap), deny(rustdoc::invalid_codeblock_attributes))] +#![deny(rustdoc::invalid_codeblock_attributes)] //! This library is used to gather all error codes into one place, //! the goal being to make their maintenance easier. diff --git a/compiler/rustc_errors/src/diagnostic.rs b/compiler/rustc_errors/src/diagnostic.rs index ce5b130dd97fe..b2f6a0c10142d 100644 --- a/compiler/rustc_errors/src/diagnostic.rs +++ b/compiler/rustc_errors/src/diagnostic.rs @@ -69,10 +69,6 @@ impl DiagnosticStyledString { pub fn highlighted>(t: S) -> DiagnosticStyledString { DiagnosticStyledString(vec![StringPart::Highlighted(t.into())]) } - - pub fn content(&self) -> String { - self.0.iter().map(|x| x.content()).collect::() - } } #[derive(Debug, PartialEq, Eq)] @@ -81,14 +77,6 @@ pub enum StringPart { Highlighted(String), } -impl StringPart { - pub fn content(&self) -> &str { - match self { - &StringPart::Normal(ref s) | &StringPart::Highlighted(ref s) => s, - } - } -} - impl Diagnostic { pub fn new(level: Level, message: &str) -> Self { Diagnostic::new_with_code(level, None, message) @@ -156,7 +144,7 @@ impl Diagnostic { self } - pub fn note_expected_found( + crate fn note_expected_found( &mut self, expected_label: &dyn fmt::Display, expected: DiagnosticStyledString, @@ -166,7 +154,7 @@ impl Diagnostic { self.note_expected_found_extra(expected_label, expected, found_label, found, &"", &"") } - pub fn note_unsuccessful_coercion( + crate fn note_unsuccessful_coercion( &mut self, expected: DiagnosticStyledString, found: DiagnosticStyledString, @@ -256,33 +244,33 @@ impl Diagnostic { /// Prints the span with a note above it. /// This is like [`Diagnostic::note()`], but it gets its own span. - pub fn span_note>(&mut self, sp: S, msg: &str) -> &mut Self { + crate fn span_note>(&mut self, sp: S, msg: &str) -> &mut Self { self.sub(Level::Note, msg, sp.into(), None); self } /// Add a warning attached to this diagnostic. - pub fn warn(&mut self, msg: &str) -> &mut Self { + crate fn warn(&mut self, msg: &str) -> &mut Self { self.sub(Level::Warning, msg, MultiSpan::new(), None); self } /// Prints the span with a warning above it. /// This is like [`Diagnostic::warn()`], but it gets its own span. - pub fn span_warn>(&mut self, sp: S, msg: &str) -> &mut Self { + crate fn span_warn>(&mut self, sp: S, msg: &str) -> &mut Self { self.sub(Level::Warning, msg, sp.into(), None); self } /// Add a help message attached to this diagnostic. - pub fn help(&mut self, msg: &str) -> &mut Self { + crate fn help(&mut self, msg: &str) -> &mut Self { self.sub(Level::Help, msg, MultiSpan::new(), None); self } /// Prints the span with some help above it. /// This is like [`Diagnostic::help()`], but it gets its own span. - pub fn span_help>(&mut self, sp: S, msg: &str) -> &mut Self { + crate fn span_help>(&mut self, sp: S, msg: &str) -> &mut Self { self.sub(Level::Help, msg, sp.into(), None); self } @@ -311,36 +299,6 @@ impl Diagnostic { self } - /// Show multiple suggestions that have multiple parts. - /// See also [`Diagnostic::multipart_suggestion()`]. - pub fn multipart_suggestions( - &mut self, - msg: &str, - suggestions: Vec>, - applicability: Applicability, - ) -> &mut Self { - assert!(!suggestions.is_empty()); - for s in &suggestions { - assert!(!s.is_empty()); - } - self.suggestions.push(CodeSuggestion { - substitutions: suggestions - .into_iter() - .map(|suggestion| Substitution { - parts: suggestion - .into_iter() - .map(|(span, snippet)| SubstitutionPart { snippet, span }) - .collect(), - }) - .collect(), - msg: msg.to_owned(), - style: SuggestionStyle::ShowCode, - applicability, - tool_metadata: Default::default(), - }); - self - } - /// Prints out a message with for a multipart suggestion without showing the suggested code. /// /// This is intended to be used for suggestions that are obvious in what the changes need to @@ -567,7 +525,7 @@ impl Diagnostic { self.code.clone() } - pub fn set_primary_message>(&mut self, msg: M) -> &mut Self { + crate fn set_primary_message>(&mut self, msg: M) -> &mut Self { self.message[0] = (msg.into(), Style::NoStyle); self } @@ -582,6 +540,8 @@ impl Diagnostic { /// Convenience function for internal use, clients should use one of the /// public methods above. + /// + /// Used by `proc_macro_server` for implementing `server::Diagnostic`. pub fn sub( &mut self, level: Level, diff --git a/compiler/rustc_errors/src/diagnostic_builder.rs b/compiler/rustc_errors/src/diagnostic_builder.rs index c09cce21bf24c..282877d5dd109 100644 --- a/compiler/rustc_errors/src/diagnostic_builder.rs +++ b/compiler/rustc_errors/src/diagnostic_builder.rs @@ -30,15 +30,6 @@ struct DiagnosticBuilderInner<'a> { allow_suggestions: bool, } -/// This is a helper macro for [`forward!`] that allows automatically adding documentation -/// that uses tokens from [`forward!`]'s input. -macro_rules! forward_inner_docs { - ($e:expr => $i:item) => { - #[doc = $e] - $i - }; -} - /// In general, the `DiagnosticBuilder` uses deref to allow access to /// the fields and methods of the embedded `diagnostic` in a /// transparent way. *However,* many of the methods are intended to @@ -54,11 +45,11 @@ macro_rules! forward { pub fn $n:ident(&self, $($name:ident: $ty:ty),* $(,)?) -> &Self ) => { $(#[$attrs])* - forward_inner_docs!(concat!("See [`Diagnostic::", stringify!($n), "()`].") => + #[doc = concat!("See [`Diagnostic::", stringify!($n), "()`].")] pub fn $n(&self, $($name: $ty),*) -> &Self { self.diagnostic.$n($($name),*); self - }); + } }; // Forward pattern for &mut self -> &mut Self @@ -67,11 +58,11 @@ macro_rules! forward { pub fn $n:ident(&mut self, $($name:ident: $ty:ty),* $(,)?) -> &mut Self ) => { $(#[$attrs])* - forward_inner_docs!(concat!("See [`Diagnostic::", stringify!($n), "()`].") => + #[doc = concat!("See [`Diagnostic::", stringify!($n), "()`].")] pub fn $n(&mut self, $($name: $ty),*) -> &mut Self { self.0.diagnostic.$n($($name),*); self - }); + } }; // Forward pattern for &mut self -> &mut Self, with generic parameters. @@ -84,11 +75,11 @@ macro_rules! forward { ) -> &mut Self ) => { $(#[$attrs])* - forward_inner_docs!(concat!("See [`Diagnostic::", stringify!($n), "()`].") => + #[doc = concat!("See [`Diagnostic::", stringify!($n), "()`].")] pub fn $n<$($generic: $bound),*>(&mut self, $($name: $ty),*) -> &mut Self { self.0.diagnostic.$n($($name),*); self - }); + } }; } @@ -166,19 +157,6 @@ impl<'a> DiagnosticBuilder<'a> { buffered_diagnostics.extend(self.into_diagnostic().map(|(diag, _)| diag)); } - /// Convenience function for internal use, clients should use one of the - /// span_* methods instead. - pub fn sub>( - &mut self, - level: Level, - message: &str, - span: Option, - ) -> &mut Self { - let span = span.map(|s| s.into()).unwrap_or_else(MultiSpan::new); - self.0.diagnostic.sub(level, message, span, None); - self - } - /// Delay emission of this diagnostic as a bug. /// /// This can be useful in contexts where an error indicates a bug but @@ -279,20 +257,6 @@ impl<'a> DiagnosticBuilder<'a> { self } - /// See [`Diagnostic::multipart_suggestions()`]. - pub fn multipart_suggestions( - &mut self, - msg: &str, - suggestions: Vec>, - applicability: Applicability, - ) -> &mut Self { - if !self.0.allow_suggestions { - return self; - } - self.0.diagnostic.multipart_suggestions(msg, suggestions, applicability); - self - } - /// See [`Diagnostic::tool_only_multipart_suggestion()`]. pub fn tool_only_multipart_suggestion( &mut self, diff --git a/compiler/rustc_errors/src/emitter.rs b/compiler/rustc_errors/src/emitter.rs index 9b6f67166bdaa..a58caf2667b06 100644 --- a/compiler/rustc_errors/src/emitter.rs +++ b/compiler/rustc_errors/src/emitter.rs @@ -195,6 +195,9 @@ pub trait Emitter { fn emit_future_breakage_report(&mut self, _diags: Vec<(FutureBreakage, Diagnostic)>) {} + /// Emit list of unused externs + fn emit_unused_externs(&mut self, _lint_level: &str, _unused_externs: &[&str]) {} + /// Checks if should show explanations about "rustc --explain" fn should_show_explain(&self) -> bool { true @@ -434,9 +437,15 @@ pub trait Emitter { span: &mut MultiSpan, children: &mut Vec, ) { + let source_map = if let Some(ref sm) = source_map { + sm + } else { + return; + }; debug!("fix_multispans_in_extern_macros: before: span={:?} children={:?}", span, children); - for span in iter::once(&mut *span).chain(children.iter_mut().map(|child| &mut child.span)) { - self.fix_multispan_in_extern_macros(source_map, span); + self.fix_multispan_in_extern_macros(source_map, span); + for child in children.iter_mut() { + self.fix_multispan_in_extern_macros(source_map, &mut child.span); } debug!("fix_multispans_in_extern_macros: after: span={:?} children={:?}", span, children); } @@ -444,16 +453,7 @@ pub trait Emitter { // This "fixes" MultiSpans that contain `Span`s pointing to locations inside of external macros. // Since these locations are often difficult to read, // we move these spans from the external macros to their corresponding use site. - fn fix_multispan_in_extern_macros( - &self, - source_map: &Option>, - span: &mut MultiSpan, - ) { - let sm = match source_map { - Some(ref sm) => sm, - None => return, - }; - + fn fix_multispan_in_extern_macros(&self, source_map: &Lrc, span: &mut MultiSpan) { // First, find all the spans in external macros and point instead at their use site. let replacements: Vec<(Span, Span)> = span .primary_spans() @@ -461,7 +461,7 @@ pub trait Emitter { .copied() .chain(span.span_labels().iter().map(|sp_label| sp_label.span)) .filter_map(|sp| { - if !sp.is_dummy() && sm.is_imported(sp) { + if !sp.is_dummy() && source_map.is_imported(sp) { let maybe_callsite = sp.source_callsite(); if sp != maybe_callsite { return Some((sp, maybe_callsite)); @@ -1232,7 +1232,6 @@ impl EmitterWriter { is_secondary: bool, ) -> io::Result<()> { let mut buffer = StyledBuffer::new(); - let header_style = if is_secondary { Style::HeaderMsg } else { Style::MainHeaderMsg }; if !msp.has_primary_spans() && !msp.has_span_labels() && is_secondary && !self.short_message { @@ -1257,11 +1256,12 @@ impl EmitterWriter { buffer.append(0, &code, Style::Level(*level)); buffer.append(0, "]", Style::Level(*level)); } + let header_style = if is_secondary { Style::HeaderMsg } else { Style::MainHeaderMsg }; if *level != Level::FailureNote { buffer.append(0, ": ", header_style); } for &(ref text, _) in msg.iter() { - buffer.append(0, text, header_style); + buffer.append(0, &replace_tabs(text), header_style); } } @@ -1470,9 +1470,7 @@ impl EmitterWriter { let mut to_add = FxHashMap::default(); for (depth, style) in depths { - if multilines.get(&depth).is_some() { - multilines.remove(&depth); - } else { + if multilines.remove(&depth).is_none() { to_add.insert(depth, style); } } @@ -1726,14 +1724,13 @@ impl EmitterWriter { if !self.short_message { draw_col_separator_no_space(&mut buffer, 0, max_line_num_len + 1); } - match emit_to_destination( + if let Err(e) = emit_to_destination( &buffer.render(), level, &mut self.dst, self.short_message, ) { - Ok(()) => (), - Err(e) => panic!("failed to emit error: {}", e), + panic!("failed to emit error: {}", e) } } if !self.short_message { @@ -2220,9 +2217,7 @@ pub fn is_case_difference(sm: &SourceMap, suggested: &str, sp: Span) -> bool { }; let ascii_confusables = &['c', 'f', 'i', 'k', 'o', 's', 'u', 'v', 'w', 'x', 'y', 'z']; // All the chars that differ in capitalization are confusable (above): - let confusable = found - .chars() - .zip(suggested.chars()) + let confusable = iter::zip(found.chars(), suggested.chars()) .filter(|(f, s)| f != s) .all(|(f, s)| (ascii_confusables.contains(&f) || ascii_confusables.contains(&s))); confusable && found.to_lowercase() == suggested.to_lowercase() diff --git a/compiler/rustc_errors/src/json.rs b/compiler/rustc_errors/src/json.rs index c27b39a9d62ff..40277006462d2 100644 --- a/compiler/rustc_errors/src/json.rs +++ b/compiler/rustc_errors/src/json.rs @@ -159,6 +159,19 @@ impl Emitter for JsonEmitter { } } + fn emit_unused_externs(&mut self, lint_level: &str, unused_externs: &[&str]) { + let data = UnusedExterns { lint_level, unused_extern_names: unused_externs }; + let result = if self.pretty { + writeln!(&mut self.dst, "{}", as_pretty_json(&data)) + } else { + writeln!(&mut self.dst, "{}", as_json(&data)) + } + .and_then(|_| self.dst.flush()); + if let Err(e) = result { + panic!("failed to print unused externs: {:?}", e); + } + } + fn source_map(&self) -> Option<&Lrc> { Some(&self.sm) } @@ -322,6 +335,18 @@ struct FutureIncompatReport { future_incompat_report: Vec, } +// NOTE: Keep this in sync with the equivalent structs in rustdoc's +// doctest component (as well as cargo). +// We could unify this struct the one in rustdoc but they have different +// ownership semantics, so doing so would create wasteful allocations. +#[derive(Encodable)] +struct UnusedExterns<'a, 'b, 'c> { + /// The severity level of the unused dependencies lint + lint_level: &'a str, + /// List of unused externs by their names. + unused_extern_names: &'b [&'c str], +} + impl Diagnostic { fn from_errors_diagnostic(diag: &crate::Diagnostic, je: &JsonEmitter) -> Diagnostic { let sugg = diag.suggestions.iter().map(|sugg| Diagnostic { @@ -493,7 +518,7 @@ impl DiagnosticSpanLine { h_end: usize, ) -> DiagnosticSpanLine { DiagnosticSpanLine { - text: sf.get_line(index).map_or(String::new(), |l| l.into_owned()), + text: sf.get_line(index).map_or_else(String::new, |l| l.into_owned()), highlight_start: h_start, highlight_end: h_end, } diff --git a/compiler/rustc_errors/src/lib.rs b/compiler/rustc_errors/src/lib.rs index a0be7442d597a..f1a31f0d4f5c9 100644 --- a/compiler/rustc_errors/src/lib.rs +++ b/compiler/rustc_errors/src/lib.rs @@ -5,6 +5,8 @@ #![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")] #![feature(crate_visibility_modifier)] #![feature(backtrace)] +#![feature(extended_key_value_attributes)] +#![feature(iter_zip)] #![feature(nll)] #[macro_use] @@ -52,7 +54,7 @@ pub type PResult<'a, T> = Result>; // `PResult` is used a lot. Make sure it doesn't unintentionally get bigger. // (See also the comment on `DiagnosticBuilderInner`.) -#[cfg(target_arch = "x86_64")] +#[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))] rustc_data_structures::static_assert_size!(PResult<'_, bool>, 16); #[derive(Debug, PartialEq, Eq, Clone, Copy, Hash, Encodable, Decodable)] @@ -319,7 +321,7 @@ struct HandlerInner { /// This set contains the `DiagnosticId` of all emitted diagnostics to avoid /// emitting the same diagnostic with extended help (`--teach`) twice, which - /// would be uneccessary repetition. + /// would be unnecessary repetition. taught_diagnostics: FxHashSet, /// Used to suggest rustc --explain @@ -689,10 +691,6 @@ impl Handler { db } - pub fn failure(&self, msg: &str) { - self.inner.borrow_mut().failure(msg); - } - pub fn fatal(&self, msg: &str) -> FatalError { self.inner.borrow_mut().fatal(msg) } @@ -767,6 +765,10 @@ impl Handler { self.inner.borrow_mut().emitter.emit_future_breakage_report(diags) } + pub fn emit_unused_externs(&self, lint_level: &str, unused_externs: &[&str]) { + self.inner.borrow_mut().emit_unused_externs(lint_level, unused_externs) + } + pub fn delay_as_bug(&self, diagnostic: Diagnostic) { self.inner.borrow_mut().delay_as_bug(diagnostic) } @@ -841,6 +843,10 @@ impl HandlerInner { self.emitter.emit_artifact_notification(path, artifact_type); } + fn emit_unused_externs(&mut self, lint_level: &str, unused_externs: &[&str]) { + self.emitter.emit_unused_externs(lint_level, unused_externs); + } + fn treat_err_as_bug(&self) -> bool { self.flags.treat_err_as_bug.map_or(false, |c| self.err_count() >= c.get()) } diff --git a/compiler/rustc_errors/src/registry.rs b/compiler/rustc_errors/src/registry.rs index b1d770d5bd523..da764d993bbdb 100644 --- a/compiler/rustc_errors/src/registry.rs +++ b/compiler/rustc_errors/src/registry.rs @@ -13,10 +13,6 @@ impl Registry { Registry { long_descriptions: long_descriptions.iter().copied().collect() } } - /// This will panic if an invalid error code is passed in - pub fn find_description(&self, code: &str) -> Option<&'static str> { - self.long_descriptions[code] - } /// Returns `InvalidErrorCode` if the code requested does not exist in the /// registry. Otherwise, returns an `Option` where `None` means the error /// code is valid but has no extended information. diff --git a/compiler/rustc_errors/src/snippet.rs b/compiler/rustc_errors/src/snippet.rs index acb88e57db5ee..3fe02bd0ceecf 100644 --- a/compiler/rustc_errors/src/snippet.rs +++ b/compiler/rustc_errors/src/snippet.rs @@ -121,16 +121,6 @@ impl Annotation { matches!(self.annotation_type, AnnotationType::MultilineLine(_)) } - pub fn is_multiline(&self) -> bool { - matches!( - self.annotation_type, - AnnotationType::Multiline(_) - | AnnotationType::MultilineStart(_) - | AnnotationType::MultilineLine(_) - | AnnotationType::MultilineEnd(_) - ) - } - pub fn len(&self) -> usize { // Account for usize underflows if self.end_col > self.start_col { diff --git a/compiler/rustc_errors/src/styled_buffer.rs b/compiler/rustc_errors/src/styled_buffer.rs index ef71ee36ea36c..ec122e7be6e8e 100644 --- a/compiler/rustc_errors/src/styled_buffer.rs +++ b/compiler/rustc_errors/src/styled_buffer.rs @@ -1,6 +1,7 @@ // Code for creating styled buffers use crate::snippet::{Style, StyledString}; +use std::iter; #[derive(Debug)] pub struct StyledBuffer { @@ -20,11 +21,11 @@ impl StyledBuffer { let mut output: Vec> = vec![]; let mut styled_vec: Vec = vec![]; - for (row, row_style) in self.text.iter().zip(&self.styles) { + for (row, row_style) in iter::zip(&self.text, &self.styles) { let mut current_style = Style::NoStyle; let mut current_text = String::new(); - for (&c, &s) in row.iter().zip(row_style) { + for (&c, &s) in iter::zip(row, row_style) { if s != current_style { if !current_text.is_empty() { styled_vec.push(StyledString { text: current_text, style: current_style }); diff --git a/compiler/rustc_expand/src/base.rs b/compiler/rustc_expand/src/base.rs index 929cc56294d3d..5950584281649 100644 --- a/compiler/rustc_expand/src/base.rs +++ b/compiler/rustc_expand/src/base.rs @@ -1,15 +1,17 @@ use crate::expand::{self, AstFragment, Invocation}; -use crate::module::DirectoryOwnership; +use crate::module::DirOwnership; use rustc_ast::ptr::P; use rustc_ast::token::{self, Nonterminal}; -use rustc_ast::tokenstream::{CanSynthesizeMissingTokens, LazyTokenStream, TokenStream}; +use rustc_ast::tokenstream::{CanSynthesizeMissingTokens, TokenStream}; use rustc_ast::visit::{AssocCtxt, Visitor}; -use rustc_ast::{self as ast, AstLike, Attribute, NodeId, PatKind}; +use rustc_ast::{self as ast, AstLike, Attribute, Item, NodeId, PatKind}; use rustc_attr::{self as attr, Deprecation, Stability}; use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::sync::{self, Lrc}; use rustc_errors::{DiagnosticBuilder, ErrorReported}; +use rustc_lint_defs::builtin::PROC_MACRO_BACK_COMPAT; +use rustc_lint_defs::BuiltinLintDiagnostics; use rustc_parse::{self, nt_to_tokenstream, parser, MACRO_ARGUMENTS}; use rustc_session::{parse::ParseSess, Limit, Session}; use rustc_span::def_id::DefId; @@ -36,34 +38,34 @@ pub enum Annotatable { Stmt(P), Expr(P), Arm(ast::Arm), - Field(ast::Field), - FieldPat(ast::FieldPat), + ExprField(ast::ExprField), + PatField(ast::PatField), GenericParam(ast::GenericParam), Param(ast::Param), - StructField(ast::StructField), + FieldDef(ast::FieldDef), Variant(ast::Variant), } -impl AstLike for Annotatable { - fn attrs(&self) -> &[Attribute] { +impl Annotatable { + pub fn span(&self) -> Span { match *self { - Annotatable::Item(ref item) => &item.attrs, - Annotatable::TraitItem(ref trait_item) => &trait_item.attrs, - Annotatable::ImplItem(ref impl_item) => &impl_item.attrs, - Annotatable::ForeignItem(ref foreign_item) => &foreign_item.attrs, - Annotatable::Stmt(ref stmt) => stmt.attrs(), - Annotatable::Expr(ref expr) => &expr.attrs, - Annotatable::Arm(ref arm) => &arm.attrs, - Annotatable::Field(ref field) => &field.attrs, - Annotatable::FieldPat(ref fp) => &fp.attrs, - Annotatable::GenericParam(ref gp) => &gp.attrs, - Annotatable::Param(ref p) => &p.attrs, - Annotatable::StructField(ref sf) => &sf.attrs, - Annotatable::Variant(ref v) => &v.attrs(), + Annotatable::Item(ref item) => item.span, + Annotatable::TraitItem(ref trait_item) => trait_item.span, + Annotatable::ImplItem(ref impl_item) => impl_item.span, + Annotatable::ForeignItem(ref foreign_item) => foreign_item.span, + Annotatable::Stmt(ref stmt) => stmt.span, + Annotatable::Expr(ref expr) => expr.span, + Annotatable::Arm(ref arm) => arm.span, + Annotatable::ExprField(ref field) => field.span, + Annotatable::PatField(ref fp) => fp.pat.span, + Annotatable::GenericParam(ref gp) => gp.ident.span, + Annotatable::Param(ref p) => p.span, + Annotatable::FieldDef(ref sf) => sf.span, + Annotatable::Variant(ref v) => v.span, } } - fn visit_attrs(&mut self, f: impl FnOnce(&mut Vec)) { + pub fn visit_attrs(&mut self, f: impl FnOnce(&mut Vec)) { match self { Annotatable::Item(item) => item.visit_attrs(f), Annotatable::TraitItem(trait_item) => trait_item.visit_attrs(f), @@ -72,39 +74,15 @@ impl AstLike for Annotatable { Annotatable::Stmt(stmt) => stmt.visit_attrs(f), Annotatable::Expr(expr) => expr.visit_attrs(f), Annotatable::Arm(arm) => arm.visit_attrs(f), - Annotatable::Field(field) => field.visit_attrs(f), - Annotatable::FieldPat(fp) => fp.visit_attrs(f), + Annotatable::ExprField(field) => field.visit_attrs(f), + Annotatable::PatField(fp) => fp.visit_attrs(f), Annotatable::GenericParam(gp) => gp.visit_attrs(f), Annotatable::Param(p) => p.visit_attrs(f), - Annotatable::StructField(sf) => sf.visit_attrs(f), + Annotatable::FieldDef(sf) => sf.visit_attrs(f), Annotatable::Variant(v) => v.visit_attrs(f), } } - fn finalize_tokens(&mut self, tokens: LazyTokenStream) { - panic!("Called finalize_tokens on an Annotatable: {:?}", tokens); - } -} - -impl Annotatable { - pub fn span(&self) -> Span { - match *self { - Annotatable::Item(ref item) => item.span, - Annotatable::TraitItem(ref trait_item) => trait_item.span, - Annotatable::ImplItem(ref impl_item) => impl_item.span, - Annotatable::ForeignItem(ref foreign_item) => foreign_item.span, - Annotatable::Stmt(ref stmt) => stmt.span, - Annotatable::Expr(ref expr) => expr.span, - Annotatable::Arm(ref arm) => arm.span, - Annotatable::Field(ref field) => field.span, - Annotatable::FieldPat(ref fp) => fp.pat.span, - Annotatable::GenericParam(ref gp) => gp.ident.span, - Annotatable::Param(ref p) => p.span, - Annotatable::StructField(ref sf) => sf.span, - Annotatable::Variant(ref v) => v.span, - } - } - pub fn visit_with<'a, V: Visitor<'a>>(&'a self, visitor: &mut V) { match self { Annotatable::Item(item) => visitor.visit_item(item), @@ -114,16 +92,16 @@ impl Annotatable { Annotatable::Stmt(stmt) => visitor.visit_stmt(stmt), Annotatable::Expr(expr) => visitor.visit_expr(expr), Annotatable::Arm(arm) => visitor.visit_arm(arm), - Annotatable::Field(field) => visitor.visit_field(field), - Annotatable::FieldPat(fp) => visitor.visit_field_pattern(fp), + Annotatable::ExprField(field) => visitor.visit_expr_field(field), + Annotatable::PatField(fp) => visitor.visit_pat_field(fp), Annotatable::GenericParam(gp) => visitor.visit_generic_param(gp), Annotatable::Param(p) => visitor.visit_param(p), - Annotatable::StructField(sf) => visitor.visit_struct_field(sf), + Annotatable::FieldDef(sf) => visitor.visit_field_def(sf), Annotatable::Variant(v) => visitor.visit_variant(v), } } - crate fn into_nonterminal(self) -> Nonterminal { + pub fn into_nonterminal(self) -> Nonterminal { match self { Annotatable::Item(item) => token::NtItem(item), Annotatable::TraitItem(item) | Annotatable::ImplItem(item) => { @@ -135,20 +113,17 @@ impl Annotatable { Annotatable::Stmt(stmt) => token::NtStmt(stmt.into_inner()), Annotatable::Expr(expr) => token::NtExpr(expr), Annotatable::Arm(..) - | Annotatable::Field(..) - | Annotatable::FieldPat(..) + | Annotatable::ExprField(..) + | Annotatable::PatField(..) | Annotatable::GenericParam(..) | Annotatable::Param(..) - | Annotatable::StructField(..) + | Annotatable::FieldDef(..) | Annotatable::Variant(..) => panic!("unexpected annotatable"), } } crate fn into_tokens(self, sess: &ParseSess) -> TokenStream { - // Tokens of an attribute target may be invalidated by some outer `#[derive]` performing - // "full configuration" (attributes following derives on the same item should be the most - // common case), that's why synthesizing tokens is allowed. - nt_to_tokenstream(&self.into_nonterminal(), sess, CanSynthesizeMissingTokens::Yes) + nt_to_tokenstream(&self.into_nonterminal(), sess, CanSynthesizeMissingTokens::No) } pub fn expect_item(self) -> P { @@ -200,16 +175,16 @@ impl Annotatable { } } - pub fn expect_field(self) -> ast::Field { + pub fn expect_expr_field(self) -> ast::ExprField { match self { - Annotatable::Field(field) => field, + Annotatable::ExprField(field) => field, _ => panic!("expected field"), } } - pub fn expect_field_pattern(self) -> ast::FieldPat { + pub fn expect_pat_field(self) -> ast::PatField { match self { - Annotatable::FieldPat(fp) => fp, + Annotatable::PatField(fp) => fp, _ => panic!("expected field pattern"), } } @@ -228,9 +203,9 @@ impl Annotatable { } } - pub fn expect_struct_field(self) -> ast::StructField { + pub fn expect_field_def(self) -> ast::FieldDef { match self { - Annotatable::StructField(sf) => sf, + Annotatable::FieldDef(sf) => sf, _ => panic!("expected struct field"), } } @@ -416,11 +391,11 @@ pub trait MacResult { None } - fn make_fields(self: Box) -> Option> { + fn make_expr_fields(self: Box) -> Option> { None } - fn make_field_patterns(self: Box) -> Option> { + fn make_pat_fields(self: Box) -> Option> { None } @@ -432,7 +407,7 @@ pub trait MacResult { None } - fn make_struct_fields(self: Box) -> Option> { + fn make_field_defs(self: Box) -> Option> { None } @@ -616,11 +591,11 @@ impl MacResult for DummyResult { Some(SmallVec::new()) } - fn make_fields(self: Box) -> Option> { + fn make_expr_fields(self: Box) -> Option> { Some(SmallVec::new()) } - fn make_field_patterns(self: Box) -> Option> { + fn make_pat_fields(self: Box) -> Option> { Some(SmallVec::new()) } @@ -632,7 +607,7 @@ impl MacResult for DummyResult { Some(SmallVec::new()) } - fn make_struct_fields(self: Box) -> Option> { + fn make_field_defs(self: Box) -> Option> { Some(SmallVec::new()) } @@ -761,7 +736,7 @@ impl SyntaxExtension { attrs: &[ast::Attribute], ) -> SyntaxExtension { let allow_internal_unstable = - Some(attr::allow_internal_unstable(sess, &attrs).collect::>().into()); + attr::allow_internal_unstable(sess, &attrs).collect::>(); let mut local_inner_macros = false; if let Some(macro_export) = sess.find_by_name(attrs, sym::macro_export) { @@ -789,7 +764,8 @@ impl SyntaxExtension { SyntaxExtension { kind, span, - allow_internal_unstable, + allow_internal_unstable: (!allow_internal_unstable.is_empty()) + .then(|| allow_internal_unstable.into()), allow_internal_unsafe: sess.contains_name(attrs, sym::allow_internal_unsafe), local_inner_macros, stability: stability.map(|(s, _)| s), @@ -851,6 +827,8 @@ impl SyntaxExtension { /// Error type that denotes indeterminacy. pub struct Indeterminate; +pub type DeriveResolutions = Vec<(ast::Path, Option>)>; + pub trait ResolverExpand { fn next_node_id(&mut self) -> NodeId; @@ -887,23 +865,36 @@ pub trait ResolverExpand { fn resolve_derives( &mut self, expn_id: ExpnId, - derives: Vec, force: bool, + derive_paths: &dyn Fn() -> DeriveResolutions, ) -> Result<(), Indeterminate>; /// Take resolutions for paths inside the `#[derive(...)]` attribute with the given `ExpnId` /// back from resolver. - fn take_derive_resolutions( - &mut self, - expn_id: ExpnId, - ) -> Option, ast::Path)>>; + fn take_derive_resolutions(&mut self, expn_id: ExpnId) -> Option; /// Path resolution logic for `#[cfg_accessible(path)]`. fn cfg_accessible(&mut self, expn_id: ExpnId, path: &ast::Path) -> Result; } -#[derive(Clone)] +#[derive(Clone, Default)] pub struct ModuleData { + /// Path to the module starting from the crate name, like `my_crate::foo::bar`. pub mod_path: Vec, - pub directory: PathBuf, + /// Stack of paths to files loaded by out-of-line module items, + /// used to detect and report recursive module inclusions. + pub file_path_stack: Vec, + /// Directory to search child module files in, + /// often (but not necessarily) the parent of the top file path on the `file_path_stack`. + pub dir_path: PathBuf, +} + +impl ModuleData { + pub fn with_dir_path(&self, dir_path: PathBuf) -> ModuleData { + ModuleData { + mod_path: self.mod_path.clone(), + file_path_stack: self.file_path_stack.clone(), + dir_path, + } + } } #[derive(Clone)] @@ -911,10 +902,13 @@ pub struct ExpansionData { pub id: ExpnId, pub depth: usize, pub module: Rc, - pub directory_ownership: DirectoryOwnership, + pub dir_ownership: DirOwnership, pub prior_type_ascription: Option<(Span, bool)>, } +type OnExternModLoaded<'a> = + Option<&'a dyn Fn(Ident, Vec, Vec>, Span) -> (Vec, Vec>)>; + /// One of these is made during expansion and incrementally updated as we go; /// when a macro expansion occurs, the resulting nodes have the `backtrace() /// -> expn_data` of their expansion context stored into their span. @@ -932,7 +926,7 @@ pub struct ExtCtxt<'a> { /// Called directly after having parsed an external `mod foo;` in expansion. /// /// `Ident` is the module name. - pub(super) extern_mod_loaded: Option<&'a dyn Fn(&ast::Crate, Ident)>, + pub(super) extern_mod_loaded: OnExternModLoaded<'a>, } impl<'a> ExtCtxt<'a> { @@ -940,7 +934,7 @@ impl<'a> ExtCtxt<'a> { sess: &'a Session, ecfg: expand::ExpansionConfig<'a>, resolver: &'a mut dyn ResolverExpand, - extern_mod_loaded: Option<&'a dyn Fn(&ast::Crate, Ident)>, + extern_mod_loaded: OnExternModLoaded<'a>, ) -> ExtCtxt<'a> { ExtCtxt { sess, @@ -952,8 +946,8 @@ impl<'a> ExtCtxt<'a> { current_expansion: ExpansionData { id: ExpnId::root(), depth: 0, - module: Rc::new(ModuleData { mod_path: Vec::new(), directory: PathBuf::new() }), - directory_ownership: DirectoryOwnership::Owned { relative: None }, + module: Default::default(), + dir_ownership: DirOwnership::Owned { relative: None }, prior_type_ascription: None, }, force_mode: false, @@ -1207,3 +1201,41 @@ pub fn get_exprs_from_tts( } Some(es) } + +/// This nonterminal looks like some specific enums from +/// `proc-macro-hack` and `procedural-masquerade` crates. +/// We need to maintain some special pretty-printing behavior for them due to incorrect +/// asserts in old versions of those crates and their wide use in the ecosystem. +/// See issue #73345 for more details. +/// FIXME(#73933): Remove this eventually. +pub(crate) fn pretty_printing_compatibility_hack(nt: &Nonterminal, sess: &ParseSess) -> bool { + let item = match nt { + Nonterminal::NtItem(item) => item, + Nonterminal::NtStmt(stmt) => match &stmt.kind { + ast::StmtKind::Item(item) => item, + _ => return false, + }, + _ => return false, + }; + + let name = item.ident.name; + if name == sym::ProceduralMasqueradeDummyType { + if let ast::ItemKind::Enum(enum_def, _) = &item.kind { + if let [variant] = &*enum_def.variants { + if variant.ident.name == sym::Input { + sess.buffer_lint_with_diagnostic( + &PROC_MACRO_BACK_COMPAT, + item.ident.span, + ast::CRATE_NODE_ID, + "using `procedural-masquerade` crate", + BuiltinLintDiagnostics::ProcMacroBackCompat( + "The `procedural-masquerade` crate has been unnecessary since Rust 1.30.0. \ + Versions of this crate below 0.1.7 will eventually stop compiling.".to_string()) + ); + return true; + } + } + } + } + false +} diff --git a/compiler/rustc_expand/src/build.rs b/compiler/rustc_expand/src/build.rs index fe67b401fccf9..cb8b9398283ef 100644 --- a/compiler/rustc_expand/src/build.rs +++ b/compiler/rustc_expand/src/build.rs @@ -253,22 +253,11 @@ impl<'a> ExtCtxt<'a> { let pathexpr = self.expr_path(self.path_global(sp, fn_path)); self.expr_call(sp, pathexpr, args) } - pub fn expr_method_call( - &self, - span: Span, - expr: P, - ident: Ident, - mut args: Vec>, - ) -> P { - args.insert(0, expr); - let segment = ast::PathSegment::from_ident(ident.with_span_pos(span)); - self.expr(span, ast::ExprKind::MethodCall(segment, args, span)) - } pub fn expr_block(&self, b: P) -> P { self.expr(b.span, ast::ExprKind::Block(b, None)) } - pub fn field_imm(&self, span: Span, ident: Ident, e: P) -> ast::Field { - ast::Field { + pub fn field_imm(&self, span: Span, ident: Ident, e: P) -> ast::ExprField { + ast::ExprField { ident: ident.with_span_pos(span), expr: e, span, @@ -282,15 +271,18 @@ impl<'a> ExtCtxt<'a> { &self, span: Span, path: ast::Path, - fields: Vec, + fields: Vec, ) -> P { - self.expr(span, ast::ExprKind::Struct(path, fields, ast::StructRest::None)) + self.expr( + span, + ast::ExprKind::Struct(P(ast::StructExpr { path, fields, rest: ast::StructRest::None })), + ) } pub fn expr_struct_ident( &self, span: Span, id: Ident, - fields: Vec, + fields: Vec, ) -> P { self.expr_struct(span, self.path_ident(span, id), fields) } @@ -419,7 +411,7 @@ impl<'a> ExtCtxt<'a> { &self, span: Span, path: ast::Path, - field_pats: Vec, + field_pats: Vec, ) -> P { self.pat(span, PatKind::Struct(path, field_pats, false)) } diff --git a/compiler/rustc_expand/src/config.rs b/compiler/rustc_expand/src/config.rs index 7d0becf1f5d85..03c83f9c07b5d 100644 --- a/compiler/rustc_expand/src/config.rs +++ b/compiler/rustc_expand/src/config.rs @@ -1,12 +1,11 @@ //! Conditional compilation stripping. -use crate::base::Annotatable; - -use rustc_ast::mut_visit::*; use rustc_ast::ptr::P; use rustc_ast::token::{DelimToken, Token, TokenKind}; -use rustc_ast::tokenstream::{DelimSpan, LazyTokenStream, Spacing, TokenStream, TokenTree}; -use rustc_ast::{self as ast, AstLike, AttrItem, Attribute, MetaItem}; +use rustc_ast::tokenstream::{AttrAnnotatedTokenStream, AttrAnnotatedTokenTree}; +use rustc_ast::tokenstream::{DelimSpan, Spacing}; +use rustc_ast::tokenstream::{LazyTokenStream, TokenTree}; +use rustc_ast::{self as ast, AstLike, AttrItem, AttrStyle, Attribute, MetaItem}; use rustc_attr as attr; use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::map_in_place::MapInPlace; @@ -22,13 +21,14 @@ use rustc_span::edition::{Edition, ALL_EDITIONS}; use rustc_span::symbol::{sym, Symbol}; use rustc_span::{Span, DUMMY_SP}; -use smallvec::SmallVec; - /// A folder that strips out items that do not belong in the current configuration. pub struct StripUnconfigured<'a> { pub sess: &'a Session, pub features: Option<&'a Features>, - pub modified: bool, + /// If `true`, perform cfg-stripping on attached tokens. + /// This is only used for the input to derive macros, + /// which needs eager expansion of `cfg` and `cfg_attr` + pub config_tokens: bool, } fn get_features( @@ -199,7 +199,7 @@ fn get_features( // `cfg_attr`-process the crate's attributes and compute the crate's features. pub fn features(sess: &Session, mut krate: ast::Crate) -> (ast::Crate, Features) { - let mut strip_unconfigured = StripUnconfigured { sess, features: None, modified: false }; + let mut strip_unconfigured = StripUnconfigured { sess, features: None, config_tokens: false }; let unconfigured_attrs = krate.attrs.clone(); let diag = &sess.parse_sess.span_diagnostic; @@ -246,24 +246,83 @@ impl<'a> StripUnconfigured<'a> { pub fn configure(&mut self, mut node: T) -> Option { self.process_cfg_attrs(&mut node); if self.in_cfg(node.attrs()) { + self.try_configure_tokens(&mut node); Some(node) } else { - self.modified = true; None } } + fn try_configure_tokens(&mut self, node: &mut T) { + if self.config_tokens { + if let Some(Some(tokens)) = node.tokens_mut() { + let attr_annotated_tokens = tokens.create_token_stream(); + *tokens = LazyTokenStream::new(self.configure_tokens(&attr_annotated_tokens)); + } + } + } + fn configure_krate_attrs( &mut self, mut attrs: Vec, ) -> Option> { attrs.flat_map_in_place(|attr| self.process_cfg_attr(attr)); - if self.in_cfg(&attrs) { - Some(attrs) - } else { - self.modified = true; - None + if self.in_cfg(&attrs) { Some(attrs) } else { None } + } + + /// Performs cfg-expansion on `stream`, producing a new `AttrAnnotatedTokenStream`. + /// 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 can_skip(stream: &AttrAnnotatedTokenStream) -> bool { + stream.0.iter().all(|(tree, _spacing)| match tree { + AttrAnnotatedTokenTree::Attributes(_) => false, + AttrAnnotatedTokenTree::Token(_) => true, + AttrAnnotatedTokenTree::Delimited(_, _, inner) => can_skip(inner), + }) + } + + if can_skip(stream) { + return stream.clone(); } + + let trees: Vec<_> = stream + .0 + .iter() + .flat_map(|(tree, spacing)| match tree.clone() { + AttrAnnotatedTokenTree::Attributes(mut data) => { + let mut attrs: Vec<_> = std::mem::take(&mut data.attrs).into(); + attrs.flat_map_in_place(|attr| self.process_cfg_attr(attr)); + data.attrs = attrs.into(); + + if self.in_cfg(&data.attrs) { + data.tokens = LazyTokenStream::new( + self.configure_tokens(&data.tokens.create_token_stream()), + ); + Some((AttrAnnotatedTokenTree::Attributes(data), *spacing)).into_iter() + } else { + None.into_iter() + } + } + AttrAnnotatedTokenTree::Delimited(sp, delim, mut inner) => { + inner = self.configure_tokens(&inner); + Some((AttrAnnotatedTokenTree::Delimited(sp, delim, inner), *spacing)) + .into_iter() + } + AttrAnnotatedTokenTree::Token(token) => { + if let TokenKind::Interpolated(nt) = token.kind { + panic!( + "Nonterminal should have been flattened at {:?}: {:?}", + token.span, nt + ); + } else { + Some((AttrAnnotatedTokenTree::Token(token), *spacing)).into_iter() + } + } + }) + .collect(); + AttrAnnotatedTokenStream::new(trees) } /// Parse and expand all `cfg_attr` attributes into a list of attributes @@ -272,7 +331,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. - pub fn process_cfg_attrs(&mut self, node: &mut T) { + fn process_cfg_attrs(&mut self, node: &mut T) { node.visit_attrs(|attrs| { attrs.flat_map_in_place(|attr| self.process_cfg_attr(attr)); }); @@ -290,9 +349,6 @@ impl<'a> StripUnconfigured<'a> { return vec![attr]; } - // A `#[cfg_attr]` either gets removed, or replaced with a new attribute - self.modified = true; - let (cfg_predicate, expanded_attrs) = match self.parse_cfg_attr(&attr) { None => return vec![], Some(r) => r, @@ -316,7 +372,7 @@ impl<'a> StripUnconfigured<'a> { expanded_attrs .into_iter() .flat_map(|(item, span)| { - let orig_tokens = attr.tokens(); + let orig_tokens = attr.tokens().to_tokenstream(); // We are taking an attribute of the form `#[cfg_attr(pred, attr)]` // and producing an attribute of the form `#[attr]`. We @@ -326,25 +382,34 @@ impl<'a> StripUnconfigured<'a> { // Use the `#` in `#[cfg_attr(pred, attr)]` as the `#` token // for `attr` when we expand it to `#[attr]` - let pound_token = orig_tokens.trees().next().unwrap(); - if !matches!(pound_token, TokenTree::Token(Token { kind: TokenKind::Pound, .. })) { - panic!("Bad tokens for attribute {:?}", attr); + let mut orig_trees = orig_tokens.trees(); + let pound_token = match orig_trees.next().unwrap() { + TokenTree::Token(token @ Token { kind: TokenKind::Pound, .. }) => token, + _ => panic!("Bad tokens for attribute {:?}", attr), + }; + let pound_span = pound_token.span; + + let mut trees = vec![(AttrAnnotatedTokenTree::Token(pound_token), Spacing::Alone)]; + if attr.style == AttrStyle::Inner { + // For inner attributes, we do the same thing for the `!` in `#![some_attr]` + let bang_token = match orig_trees.next().unwrap() { + TokenTree::Token(token @ Token { kind: TokenKind::Not, .. }) => token, + _ => panic!("Bad tokens for attribute {:?}", attr), + }; + trees.push((AttrAnnotatedTokenTree::Token(bang_token), Spacing::Alone)); } // We don't really have a good span to use for the syntheized `[]` // in `#[attr]`, so just use the span of the `#` token. - let bracket_group = TokenTree::Delimited( - DelimSpan::from_single(pound_token.span()), + let bracket_group = AttrAnnotatedTokenTree::Delimited( + DelimSpan::from_single(pound_span), DelimToken::Bracket, item.tokens .as_ref() .unwrap_or_else(|| panic!("Missing tokens for {:?}", item)) .create_token_stream(), ); - let tokens = Some(LazyTokenStream::new(TokenStream::new(vec![ - (pound_token, Spacing::Alone), - (bracket_group, Spacing::Alone), - ]))); - + trees.push((bracket_group, Spacing::Alone)); + let tokens = Some(LazyTokenStream::new(AttrAnnotatedTokenStream::new(trees))); self.process_cfg_attr(attr::mk_attr_from_item(item, tokens, attr.style, span)) }) .collect() @@ -387,7 +452,7 @@ impl<'a> StripUnconfigured<'a> { } /// Determines if a node with the given attributes should be included in this configuration. - pub fn in_cfg(&self, attrs: &[Attribute]) -> bool { + fn in_cfg(&self, attrs: &[Attribute]) -> bool { attrs.iter().all(|attr| { if !is_cfg(self.sess, attr) { return true; @@ -427,16 +492,8 @@ impl<'a> StripUnconfigured<'a> { }) } - /// Visit attributes on expression and statements (but not attributes on items in blocks). - fn visit_expr_attrs(&mut self, attrs: &[Attribute]) { - // flag the offending attributes - for attr in attrs.iter() { - self.maybe_emit_expr_attr_err(attr); - } - } - /// If attributes are not allowed on expressions, emit an error for `attr` - pub fn maybe_emit_expr_attr_err(&self, attr: &Attribute) { + crate fn maybe_emit_expr_attr_err(&self, attr: &Attribute) { if !self.features.map_or(true, |features| features.stmt_expr_attributes) { let mut err = feature_err( &self.sess.parse_sess, @@ -453,49 +510,10 @@ impl<'a> StripUnconfigured<'a> { } } - pub fn configure_foreign_mod(&mut self, foreign_mod: &mut ast::ForeignMod) { - let ast::ForeignMod { unsafety: _, abi: _, items } = foreign_mod; - items.flat_map_in_place(|item| self.configure(item)); - } - - fn configure_variant_data(&mut self, vdata: &mut ast::VariantData) { - match vdata { - ast::VariantData::Struct(fields, ..) | ast::VariantData::Tuple(fields, _) => { - fields.flat_map_in_place(|field| self.configure(field)) - } - ast::VariantData::Unit(_) => {} - } - } - - pub fn configure_item_kind(&mut self, item: &mut ast::ItemKind) { - match item { - ast::ItemKind::Struct(def, _generics) | ast::ItemKind::Union(def, _generics) => { - self.configure_variant_data(def) - } - ast::ItemKind::Enum(ast::EnumDef { variants }, _generics) => { - variants.flat_map_in_place(|variant| self.configure(variant)); - for variant in variants { - self.configure_variant_data(&mut variant.data); - } - } - _ => {} - } - } - - pub fn configure_expr_kind(&mut self, expr_kind: &mut ast::ExprKind) { - match expr_kind { - ast::ExprKind::Match(_m, arms) => { - arms.flat_map_in_place(|arm| self.configure(arm)); - } - ast::ExprKind::Struct(_path, fields, _base) => { - fields.flat_map_in_place(|field| self.configure(field)); - } - _ => {} - } - } - pub fn configure_expr(&mut self, expr: &mut P) { - self.visit_expr_attrs(expr.attrs()); + for attr in expr.attrs.iter() { + self.maybe_emit_expr_attr_err(attr); + } // If an expr is valid to cfg away it will have been removed by the // outer stmt or expression folder before descending in here. @@ -509,118 +527,8 @@ impl<'a> StripUnconfigured<'a> { self.sess.parse_sess.span_diagnostic.span_err(attr.span, msg); } - self.process_cfg_attrs(expr) - } - - pub fn configure_pat(&mut self, pat: &mut P) { - if let ast::PatKind::Struct(_path, fields, _etc) = &mut pat.kind { - fields.flat_map_in_place(|field| self.configure(field)); - } - } - - pub fn configure_fn_decl(&mut self, fn_decl: &mut ast::FnDecl) { - fn_decl.inputs.flat_map_in_place(|arg| self.configure(arg)); - } - - pub fn fully_configure(&mut self, item: Annotatable) -> Annotatable { - // Since the item itself has already been configured by the InvocationCollector, - // we know that fold result vector will contain exactly one element - match item { - Annotatable::Item(item) => Annotatable::Item(self.flat_map_item(item).pop().unwrap()), - Annotatable::TraitItem(item) => { - Annotatable::TraitItem(self.flat_map_trait_item(item).pop().unwrap()) - } - Annotatable::ImplItem(item) => { - Annotatable::ImplItem(self.flat_map_impl_item(item).pop().unwrap()) - } - Annotatable::ForeignItem(item) => { - Annotatable::ForeignItem(self.flat_map_foreign_item(item).pop().unwrap()) - } - Annotatable::Stmt(stmt) => { - Annotatable::Stmt(stmt.map(|stmt| self.flat_map_stmt(stmt).pop().unwrap())) - } - Annotatable::Expr(mut expr) => Annotatable::Expr({ - self.visit_expr(&mut expr); - expr - }), - Annotatable::Arm(arm) => Annotatable::Arm(self.flat_map_arm(arm).pop().unwrap()), - Annotatable::Field(field) => { - Annotatable::Field(self.flat_map_field(field).pop().unwrap()) - } - Annotatable::FieldPat(fp) => { - Annotatable::FieldPat(self.flat_map_field_pattern(fp).pop().unwrap()) - } - Annotatable::GenericParam(param) => { - Annotatable::GenericParam(self.flat_map_generic_param(param).pop().unwrap()) - } - Annotatable::Param(param) => { - Annotatable::Param(self.flat_map_param(param).pop().unwrap()) - } - Annotatable::StructField(sf) => { - Annotatable::StructField(self.flat_map_struct_field(sf).pop().unwrap()) - } - Annotatable::Variant(v) => { - Annotatable::Variant(self.flat_map_variant(v).pop().unwrap()) - } - } - } -} - -impl<'a> MutVisitor for StripUnconfigured<'a> { - fn visit_foreign_mod(&mut self, foreign_mod: &mut ast::ForeignMod) { - self.configure_foreign_mod(foreign_mod); - noop_visit_foreign_mod(foreign_mod, self); - } - - fn visit_item_kind(&mut self, item: &mut ast::ItemKind) { - self.configure_item_kind(item); - noop_visit_item_kind(item, self); - } - - fn visit_expr(&mut self, expr: &mut P) { - self.configure_expr(expr); - self.configure_expr_kind(&mut expr.kind); - noop_visit_expr(expr, self); - } - - fn filter_map_expr(&mut self, expr: P) -> Option> { - let mut expr = configure!(self, expr); - self.configure_expr_kind(&mut expr.kind); - noop_visit_expr(&mut expr, self); - Some(expr) - } - - fn flat_map_generic_param( - &mut self, - param: ast::GenericParam, - ) -> SmallVec<[ast::GenericParam; 1]> { - noop_flat_map_generic_param(configure!(self, param), self) - } - - fn flat_map_stmt(&mut self, stmt: ast::Stmt) -> SmallVec<[ast::Stmt; 1]> { - noop_flat_map_stmt(configure!(self, stmt), self) - } - - fn flat_map_item(&mut self, item: P) -> SmallVec<[P; 1]> { - noop_flat_map_item(configure!(self, item), self) - } - - fn flat_map_impl_item(&mut self, item: P) -> SmallVec<[P; 1]> { - noop_flat_map_assoc_item(configure!(self, item), self) - } - - fn flat_map_trait_item(&mut self, item: P) -> SmallVec<[P; 1]> { - noop_flat_map_assoc_item(configure!(self, item), self) - } - - fn visit_pat(&mut self, pat: &mut P) { - self.configure_pat(pat); - noop_visit_pat(pat, self) - } - - fn visit_fn_decl(&mut self, mut fn_decl: &mut P) { - self.configure_fn_decl(&mut fn_decl); - noop_visit_fn_decl(fn_decl, self); + self.process_cfg_attrs(expr); + self.try_configure_tokens(&mut *expr); } } diff --git a/compiler/rustc_expand/src/expand.rs b/compiler/rustc_expand/src/expand.rs index b474cad1242e8..529ef7e4611e2 100644 --- a/compiler/rustc_expand/src/expand.rs +++ b/compiler/rustc_expand/src/expand.rs @@ -3,7 +3,7 @@ use crate::config::StripUnconfigured; use crate::configure; use crate::hygiene::SyntaxContext; use crate::mbe::macro_rules::annotate_err_with_kind; -use crate::module::{parse_external_mod, push_directory, Directory, DirectoryOwnership}; +use crate::module::{mod_dir_path, parse_external_mod, DirOwnership, ParsedExternalMod}; use crate::placeholders::{placeholder, PlaceholderExpander}; use rustc_ast as ast; @@ -12,7 +12,7 @@ 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, AttrItem, AttrStyle, Block, Inline, ItemKind, LitKind, MacArgs}; +use rustc_ast::{AstLike, AttrItem, Block, Inline, ItemKind, LitKind, MacArgs}; use rustc_ast::{MacCallStmt, MacStmtStyle, MetaItemKind, ModKind, NestedMetaItem}; use rustc_ast::{NodeId, PatKind, Path, StmtKind, Unsafe}; use rustc_ast_pretty::pprust; @@ -22,7 +22,7 @@ use rustc_data_structures::stack::ensure_sufficient_stack; use rustc_data_structures::sync::Lrc; use rustc_errors::{Applicability, PResult}; use rustc_feature::Features; -use rustc_parse::parser::{AttemptLocalParseRecovery, ForceCollect, GateOr, Parser, RecoverComma}; +use rustc_parse::parser::{AttemptLocalParseRecovery, ForceCollect, Parser, RecoverComma}; use rustc_parse::validate_attr; use rustc_session::lint::builtin::UNUSED_DOC_COMMENTS; use rustc_session::lint::BuiltinLintDiagnostics; @@ -177,14 +177,14 @@ ast_fragments! { Arms(SmallVec<[ast::Arm; 1]>) { "match arm"; many fn flat_map_arm; fn visit_arm(); fn make_arms; } - Fields(SmallVec<[ast::Field; 1]>) { - "field expression"; many fn flat_map_field; fn visit_field(); fn make_fields; + Fields(SmallVec<[ast::ExprField; 1]>) { + "field expression"; many fn flat_map_expr_field; fn visit_expr_field(); fn make_expr_fields; } - FieldPats(SmallVec<[ast::FieldPat; 1]>) { + FieldPats(SmallVec<[ast::PatField; 1]>) { "field pattern"; - many fn flat_map_field_pattern; - fn visit_field_pattern(); - fn make_field_patterns; + many fn flat_map_pat_field; + fn visit_pat_field(); + fn make_pat_fields; } GenericParams(SmallVec<[ast::GenericParam; 1]>) { "generic parameter"; @@ -195,41 +195,47 @@ ast_fragments! { Params(SmallVec<[ast::Param; 1]>) { "function parameter"; many fn flat_map_param; fn visit_param(); fn make_params; } - StructFields(SmallVec<[ast::StructField; 1]>) { + StructFields(SmallVec<[ast::FieldDef; 1]>) { "field"; - many fn flat_map_struct_field; - fn visit_struct_field(); - fn make_struct_fields; + many fn flat_map_field_def; + fn visit_field_def(); + fn make_field_defs; } Variants(SmallVec<[ast::Variant; 1]>) { "variant"; many fn flat_map_variant; fn visit_variant(); fn make_variants; } } +pub enum SupportsMacroExpansion { + No, + Yes { supports_inner_attrs: bool }, +} + impl AstFragmentKind { crate fn dummy(self, span: Span) -> AstFragment { self.make_from(DummyResult::any(span)).expect("couldn't create a dummy AST fragment") } - /// Fragment supports macro expansion and not just inert attributes, `cfg` and `cfg_attr`. - pub fn supports_macro_expansion(self) -> bool { + pub fn supports_macro_expansion(self) -> SupportsMacroExpansion { match self { AstFragmentKind::OptExpr | AstFragmentKind::Expr - | AstFragmentKind::Pat - | AstFragmentKind::Ty | AstFragmentKind::Stmts - | AstFragmentKind::Items + | AstFragmentKind::Ty + | AstFragmentKind::Pat => SupportsMacroExpansion::Yes { supports_inner_attrs: false }, + AstFragmentKind::Items | AstFragmentKind::TraitItems | AstFragmentKind::ImplItems - | AstFragmentKind::ForeignItems => true, + | AstFragmentKind::ForeignItems => { + SupportsMacroExpansion::Yes { supports_inner_attrs: true } + } AstFragmentKind::Arms | AstFragmentKind::Fields | AstFragmentKind::FieldPats | AstFragmentKind::GenericParams | AstFragmentKind::Params | AstFragmentKind::StructFields - | AstFragmentKind::Variants => false, + | AstFragmentKind::Variants => SupportsMacroExpansion::No, } } @@ -243,10 +249,10 @@ impl AstFragmentKind { AstFragment::Arms(items.map(Annotatable::expect_arm).collect()) } AstFragmentKind::Fields => { - AstFragment::Fields(items.map(Annotatable::expect_field).collect()) + AstFragment::Fields(items.map(Annotatable::expect_expr_field).collect()) } AstFragmentKind::FieldPats => { - AstFragment::FieldPats(items.map(Annotatable::expect_field_pattern).collect()) + AstFragment::FieldPats(items.map(Annotatable::expect_pat_field).collect()) } AstFragmentKind::GenericParams => { AstFragment::GenericParams(items.map(Annotatable::expect_generic_param).collect()) @@ -255,7 +261,7 @@ impl AstFragmentKind { AstFragment::Params(items.map(Annotatable::expect_param).collect()) } AstFragmentKind::StructFields => { - AstFragment::StructFields(items.map(Annotatable::expect_struct_field).collect()) + AstFragment::StructFields(items.map(Annotatable::expect_field_def).collect()) } AstFragmentKind::Variants => { AstFragment::Variants(items.map(Annotatable::expect_variant).collect()) @@ -321,8 +327,8 @@ impl InvocationKind { // The assumption is that the attribute expansion cannot change field visibilities, // and it holds because only inert attributes are supported in this position. match self { - InvocationKind::Attr { item: Annotatable::StructField(field), .. } - | InvocationKind::Derive { item: Annotatable::StructField(field), .. } + InvocationKind::Attr { item: Annotatable::FieldDef(field), .. } + | InvocationKind::Derive { item: Annotatable::FieldDef(field), .. } if field.ident.is_none() => { Some(field.vis.clone()) @@ -355,16 +361,17 @@ impl<'a, 'b> MacroExpander<'a, 'b> { // FIXME: Avoid visiting the crate as a `Mod` item, // make crate a first class expansion target instead. pub fn expand_crate(&mut self, mut krate: ast::Crate) -> ast::Crate { - let mut module = ModuleData { - mod_path: vec![Ident::from_str(&self.cx.ecfg.crate_name)], - directory: match self.cx.source_map().span_to_unmapped_path(krate.span) { - FileName::Real(name) => name.into_local_path(), - other => PathBuf::from(other.to_string()), - }, + let file_path = match self.cx.source_map().span_to_unmapped_path(krate.span) { + FileName::Real(name) => name.into_local_path(), + other => PathBuf::from(other.to_string()), }; - module.directory.pop(); - self.cx.root_path = module.directory.clone(); - self.cx.current_expansion.module = Rc::new(module); + let dir_path = file_path.parent().unwrap_or(&file_path).to_owned(); + self.cx.root_path = dir_path.clone(); + self.cx.current_expansion.module = Rc::new(ModuleData { + mod_path: vec![Ident::from_str(&self.cx.ecfg.crate_name)], + file_path_stack: vec![file_path], + dir_path, + }); let krate_item = AstFragment::Items(smallvec![P(ast::Item { attrs: krate.attrs, @@ -484,6 +491,7 @@ impl<'a, 'b> MacroExpander<'a, 'b> { let fragment_kind = invoc.fragment_kind; let (expanded_fragment, new_invocations) = match self.expand_invoc(invoc, &ext.kind) { ExpandResult::Ready(fragment) => { + let mut derive_invocations = Vec::new(); let derive_placeholders = self .cx .resolver @@ -505,14 +513,14 @@ impl<'a, 'b> MacroExpander<'a, 'b> { _ => unreachable!(), }; - invocations.reserve(derives.len()); + derive_invocations.reserve(derives.len()); derives .into_iter() - .map(|(_exts, path)| { + .map(|(path, _exts)| { // FIXME: Consider using the derive resolutions (`_exts`) // instead of enqueuing the derives to be resolved again later. let expn_id = ExpnId::fresh(None); - invocations.push(( + derive_invocations.push(( Invocation { kind: InvocationKind::Derive { path, @@ -539,7 +547,12 @@ impl<'a, 'b> MacroExpander<'a, 'b> { }) .unwrap_or_default(); - self.collect_invocations(fragment, &derive_placeholders) + let (fragment, collected_invocations) = + self.collect_invocations(fragment, &derive_placeholders); + // We choose to expand any derive invocations associated with this macro invocation + // *before* any macro invocations collected from the output fragment + derive_invocations.extend(collected_invocations); + (fragment, derive_invocations) } ExpandResult::Retry(invoc) => { if force { @@ -598,10 +611,15 @@ impl<'a, 'b> MacroExpander<'a, 'b> { let invocations = { let mut collector = InvocationCollector { + // Non-derive macro invocations cannot see the results of cfg expansion - they + // will either be removed along with the item, or invoked before the cfg/cfg_attr + // attribute is expanded. Therefore, we don't need to configure the tokens + // Derive macros *can* see the results of cfg-expansion - they are handled + // specially in `fully_expand_fragment` cfg: StripUnconfigured { sess: &self.cx.sess, features: self.cx.ecfg.features, - modified: false, + config_tokens: false, }, cx: self.cx, invocations: Vec::new(), @@ -696,13 +714,26 @@ impl<'a, 'b> MacroExpander<'a, 'b> { SyntaxExtensionKind::Attr(expander) => { self.gate_proc_macro_input(&item); self.gate_proc_macro_attr_item(span, &item); - let tokens = match attr.style { - AttrStyle::Outer => item.into_tokens(&self.cx.sess.parse_sess), - // FIXME: Properly collect tokens for inner attributes - AttrStyle::Inner => rustc_parse::fake_token_stream( + let mut fake_tokens = false; + if let Annotatable::Item(item_inner) = &item { + if let ItemKind::Mod(_, mod_kind) = &item_inner.kind { + // FIXME: Collect tokens and use them instead of generating + // fake ones. These are unstable, so it needs to be + // fixed prior to stabilization + // Fake tokens when we are invoking an inner attribute, and: + fake_tokens = matches!(attr.style, ast::AttrStyle::Inner) && + // We are invoking an attribute on the crate root, or an outline + // module + (item_inner.ident.name.is_empty() || !matches!(mod_kind, ast::ModKind::Loaded(_, Inline::Yes, _))); + } + } + let tokens = if fake_tokens { + rustc_parse::fake_token_stream( &self.cx.sess.parse_sess, &item.into_nonterminal(), - ), + ) + } else { + item.into_tokens(&self.cx.sess.parse_sess) }; let attr_item = attr.unwrap_normal_item(); if let MacArgs::Eq(..) = attr_item.args { @@ -728,7 +759,14 @@ impl<'a, 'b> MacroExpander<'a, 'b> { }); } }; - fragment_kind.expect_from_annotatables(items) + if fragment_kind == AstFragmentKind::Expr && items.is_empty() { + let msg = + "removing an expression is not supported in this position"; + self.cx.span_err(span, msg); + fragment_kind.dummy(span) + } else { + fragment_kind.expect_from_annotatables(items) + } } Err(mut err) => { err.emit(); @@ -786,11 +824,11 @@ impl<'a, 'b> MacroExpander<'a, 'b> { } Annotatable::Expr(_) => "expressions", Annotatable::Arm(..) - | Annotatable::Field(..) - | Annotatable::FieldPat(..) + | Annotatable::ExprField(..) + | Annotatable::PatField(..) | Annotatable::GenericParam(..) | Annotatable::Param(..) - | Annotatable::StructField(..) + | Annotatable::FieldDef(..) | Annotatable::Variant(..) => panic!("unexpected annotatable"), }; if self.cx.ecfg.proc_macro_hygiene() { @@ -877,21 +915,21 @@ pub fn parse_ast_fragment<'a>( } AstFragmentKind::TraitItems => { let mut items = SmallVec::new(); - while let Some(item) = this.parse_trait_item()? { + while let Some(item) = this.parse_trait_item(ForceCollect::No)? { items.extend(item); } AstFragment::TraitItems(items) } AstFragmentKind::ImplItems => { let mut items = SmallVec::new(); - while let Some(item) = this.parse_impl_item()? { + while let Some(item) = this.parse_impl_item(ForceCollect::No)? { items.extend(item); } AstFragment::ImplItems(items) } AstFragmentKind::ForeignItems => { let mut items = SmallVec::new(); - while let Some(item) = this.parse_foreign_item()? { + while let Some(item) = this.parse_foreign_item(ForceCollect::No)? { items.extend(item); } AstFragment::ForeignItems(items) @@ -916,7 +954,7 @@ pub fn parse_ast_fragment<'a>( } AstFragmentKind::Ty => AstFragment::Ty(this.parse_ty()?), AstFragmentKind::Pat => { - AstFragment::Pat(this.parse_pat_allow_top_alt(None, GateOr::Yes, RecoverComma::No)?) + AstFragment::Pat(this.parse_pat_allow_top_alt(None, RecoverComma::No)?) } AstFragmentKind::Arms | AstFragmentKind::Fields @@ -1053,13 +1091,23 @@ impl<'a, 'b> InvocationCollector<'a, 'b> { // since they will not be detected after macro expansion. fn check_attributes(&mut self, attrs: &[ast::Attribute]) { let features = self.cx.ecfg.features.unwrap(); - for attr in attrs.iter() { + let mut attrs = attrs.iter().peekable(); + let mut span: Option = None; + while let Some(attr) = attrs.next() { rustc_ast_passes::feature_gate::check_attribute(attr, self.cx.sess, features); validate_attr::check_meta(&self.cx.sess.parse_sess, attr); + + let current_span = if let Some(sp) = span { sp.to(attr.span) } else { attr.span }; + span = Some(current_span); + + if attrs.peek().map_or(false, |next_attr| next_attr.doc_str().is_some()) { + continue; + } + if attr.doc_str().is_some() { self.cx.sess.parse_sess.buffer_lint_with_diagnostic( &UNUSED_DOC_COMMENTS, - attr.span, + current_span, ast::CRATE_NODE_ID, "unused doc comment", BuiltinLintDiagnostics::UnusedDocComment(attr.span), @@ -1107,28 +1155,28 @@ impl<'a, 'b> MutVisitor for InvocationCollector<'a, 'b> { noop_flat_map_arm(arm, self) } - fn flat_map_field(&mut self, field: ast::Field) -> SmallVec<[ast::Field; 1]> { + 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::Field(field), AstFragmentKind::Fields) - .make_fields(); + .collect_attr(attr, Annotatable::ExprField(field), AstFragmentKind::Fields) + .make_expr_fields(); } - noop_flat_map_field(field, self) + noop_flat_map_expr_field(field, self) } - fn flat_map_field_pattern(&mut self, fp: ast::FieldPat) -> SmallVec<[ast::FieldPat; 1]> { + 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::FieldPat(fp), AstFragmentKind::FieldPats) - .make_field_patterns(); + .collect_attr(attr, Annotatable::PatField(fp), AstFragmentKind::FieldPats) + .make_pat_fields(); } - noop_flat_map_field_pattern(fp, self) + noop_flat_map_pat_field(fp, self) } fn flat_map_param(&mut self, p: ast::Param) -> SmallVec<[ast::Param; 1]> { @@ -1143,16 +1191,16 @@ impl<'a, 'b> MutVisitor for InvocationCollector<'a, 'b> { noop_flat_map_param(p, self) } - fn flat_map_struct_field(&mut self, sf: ast::StructField) -> SmallVec<[ast::StructField; 1]> { + 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::StructField(sf), AstFragmentKind::StructFields) - .make_struct_fields(); + .collect_attr(attr, Annotatable::FieldDef(sf), AstFragmentKind::StructFields) + .make_field_defs(); } - noop_flat_map_struct_field(sf, self) + noop_flat_map_field_def(sf, self) } fn flat_map_variant(&mut self, variant: ast::Variant) -> SmallVec<[ast::Variant; 1]> { @@ -1245,10 +1293,12 @@ impl<'a, 'b> MutVisitor for InvocationCollector<'a, 'b> { } fn visit_block(&mut self, block: &mut P) { - let old_directory_ownership = self.cx.current_expansion.directory_ownership; - self.cx.current_expansion.directory_ownership = DirectoryOwnership::UnownedViaBlock; + let orig_dir_ownership = mem::replace( + &mut self.cx.current_expansion.dir_ownership, + DirOwnership::UnownedViaBlock, + ); noop_visit_block(block, self); - self.cx.current_expansion.directory_ownership = old_directory_ownership; + self.cx.current_expansion.dir_ownership = orig_dir_ownership; } fn flat_map_item(&mut self, item: P) -> SmallVec<[P; 1]> { @@ -1276,63 +1326,82 @@ impl<'a, 'b> MutVisitor for InvocationCollector<'a, 'b> { }) } ast::ItemKind::Mod(_, ref mut mod_kind) if ident != Ident::invalid() => { - let sess = &self.cx.sess.parse_sess; - let orig_ownership = self.cx.current_expansion.directory_ownership; - let mut module = (*self.cx.current_expansion.module).clone(); - - let pushed = &mut false; // Record `parse_external_mod` pushing so we can pop. - let dir = Directory { ownership: orig_ownership, path: module.directory }; - let Directory { ownership, path } = match mod_kind { - ModKind::Loaded(_, Inline::Yes, _) => { + 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; - push_directory(&self.cx.sess, ident, &item.attrs, dir) - } - ModKind::Loaded(_, Inline::No, _) => { - panic!("`mod` item is loaded from a file for the second time") + (None, dir_path, dir_ownership) } ModKind::Unloaded => { // We have an outline `mod foo;` so we need to parse the file. - let (items, inner_span, dir) = - parse_external_mod(&self.cx.sess, ident, span, dir, &mut attrs, pushed); + 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, + ); - let krate = - ast::Crate { attrs, items, span: inner_span, proc_macros: vec![] }; if let Some(extern_mod_loaded) = self.cx.extern_mod_loaded { - extern_mod_loaded(&krate, ident); + (attrs, items) = extern_mod_loaded(ident, attrs, items, inner_span); } - *mod_kind = ModKind::Loaded(krate.items, Inline::No, inner_span); - item.attrs = krate.attrs; - // File can have inline attributes, e.g., `#![cfg(...)]` & co. => Reconfigure. - item = match self.configure(item) { - Some(node) => node, - None => { - if *pushed { - sess.included_mod_stack.borrow_mut().pop(); - } - return Default::default(); + *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(); } - }; - dir + } + (Some(file_path), dir_path, dir_ownership) } }; // Set the module info before we flat map. - self.cx.current_expansion.directory_ownership = ownership; - module.directory = path; + 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 = 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; - self.cx.current_expansion.directory_ownership = orig_ownership; - if *pushed { - sess.included_mod_stack.borrow_mut().pop(); - } + result } _ => { diff --git a/compiler/rustc_expand/src/lib.rs b/compiler/rustc_expand/src/lib.rs index c5d8ff25ea94b..5fb8586750167 100644 --- a/compiler/rustc_expand/src/lib.rs +++ b/compiler/rustc_expand/src/lib.rs @@ -1,6 +1,9 @@ +#![feature(bool_to_option)] #![feature(crate_visibility_modifier)] #![feature(decl_macro)] -#![feature(or_patterns)] +#![feature(destructuring_assignment)] +#![feature(iter_zip)] +#![cfg_attr(bootstrap, feature(or_patterns))] #![feature(proc_macro_diagnostic)] #![feature(proc_macro_internals)] #![feature(proc_macro_span)] diff --git a/compiler/rustc_expand/src/mbe.rs b/compiler/rustc_expand/src/mbe.rs index cbc4d14a65a1b..5244ac36bba5d 100644 --- a/compiler/rustc_expand/src/mbe.rs +++ b/compiler/rustc_expand/src/mbe.rs @@ -69,7 +69,7 @@ enum KleeneOp { ZeroOrMore, /// Kleene plus (`+`) for one or more repetitions OneOrMore, - /// Kleene optional (`?`) for zero or one reptitions + /// Kleene optional (`?`) for zero or one repetitions ZeroOrOne, } diff --git a/compiler/rustc_expand/src/mbe/macro_check.rs b/compiler/rustc_expand/src/mbe/macro_check.rs index 91add4f921844..3497e5ad543a1 100644 --- a/compiler/rustc_expand/src/mbe/macro_check.rs +++ b/compiler/rustc_expand/src/mbe/macro_check.rs @@ -116,6 +116,8 @@ use rustc_span::{symbol::MacroRulesNormalizedIdent, MultiSpan, Span}; use smallvec::SmallVec; +use std::iter; + /// Stack represented as linked list. /// /// Those are used for environments because they grow incrementally and are not mutable. @@ -204,7 +206,7 @@ pub(super) fn check_meta_variables( sess.span_diagnostic.span_bug(span, "length mismatch between LHSes and RHSes") } let mut valid = true; - for (lhs, rhs) in lhses.iter().zip(rhses.iter()) { + for (lhs, rhs) in iter::zip(lhses, rhses) { let mut binders = Binders::default(); check_binders(sess, node_id, lhs, &Stack::Empty, &mut binders, &Stack::Empty, &mut valid); check_occurrences(sess, node_id, rhs, &Stack::Empty, &binders, &Stack::Empty, &mut valid); diff --git a/compiler/rustc_expand/src/mbe/macro_rules.rs b/compiler/rustc_expand/src/mbe/macro_rules.rs index 73fbde70bda9f..bc45c57596e15 100644 --- a/compiler/rustc_expand/src/mbe/macro_rules.rs +++ b/compiler/rustc_expand/src/mbe/macro_rules.rs @@ -18,7 +18,8 @@ use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::sync::Lrc; use rustc_errors::{Applicability, DiagnosticBuilder}; use rustc_feature::Features; -use rustc_lint_defs::builtin::SEMICOLON_IN_EXPRESSIONS_FROM_MACROS; +use rustc_lint_defs::builtin::{OR_PATTERNS_BACK_COMPAT, SEMICOLON_IN_EXPRESSIONS_FROM_MACROS}; +use rustc_lint_defs::BuiltinLintDiagnostics; use rustc_parse::parser::Parser; use rustc_session::parse::ParseSess; use rustc_session::Session; @@ -951,8 +952,32 @@ fn check_matcher_core( // Now `last` holds the complete set of NT tokens that could // end the sequence before SUFFIX. Check that every one works with `suffix`. for token in &last.tokens { - if let TokenTree::MetaVarDecl(_, name, Some(kind)) = *token { + if let TokenTree::MetaVarDecl(span, name, Some(kind)) = *token { for next_token in &suffix_first.tokens { + // Check if the old pat is used and the next token is `|`. + if let NonterminalKind::Pat2015 { inferred: true } = kind { + if let TokenTree::Token(token) = next_token { + if let BinOp(token) = token.kind { + if let token::BinOpToken::Or = token { + // It is suggestion to use pat2015, for example: $x:pat -> $x:pat2015. + let suggestion = quoted_tt_to_string(&TokenTree::MetaVarDecl( + span, + name, + Some(NonterminalKind::Pat2015 { inferred: false }), + )); + sess.buffer_lint_with_diagnostic( + &OR_PATTERNS_BACK_COMPAT, + span, + ast::CRATE_NODE_ID, + &*format!("the meaning of the `pat` fragment specifier is changing in Rust 2021, which may affect this macro",), + BuiltinLintDiagnostics::OrPatternsBackCompat( + span, suggestion, + ), + ); + } + } + } + } match is_in_follow(next_token, kind) { IsInFollow::Yes => {} IsInFollow::No(possible) => { @@ -1080,7 +1105,7 @@ fn is_in_follow(tok: &mbe::TokenTree, kind: NonterminalKind) -> IsInFollow { _ => IsInFollow::No(TOKENS), } } - NonterminalKind::Pat2018 { .. } | NonterminalKind::Pat2021 { .. } => { + NonterminalKind::Pat2015 { .. } => { const TOKENS: &[&str] = &["`=>`", "`,`", "`=`", "`|`", "`if`", "`in`"]; match tok { TokenTree::Token(token) => match token.kind { @@ -1091,6 +1116,17 @@ fn is_in_follow(tok: &mbe::TokenTree, kind: NonterminalKind) -> IsInFollow { _ => IsInFollow::No(TOKENS), } } + NonterminalKind::Pat2021 { .. } => { + const TOKENS: &[&str] = &["`=>`", "`,`", "`=`", "`if`", "`in`"]; + match tok { + TokenTree::Token(token) => match token.kind { + FatArrow | Comma | Eq => IsInFollow::Yes, + Ident(name, false) if name == kw::If || name == kw::In => IsInFollow::Yes, + _ => IsInFollow::No(TOKENS), + }, + _ => IsInFollow::No(TOKENS), + } + } NonterminalKind::Path | NonterminalKind::Ty => { const TOKENS: &[&str] = &[ "`{`", "`[`", "`=>`", "`,`", "`>`", "`=`", "`:`", "`;`", "`|`", "`as`", diff --git a/compiler/rustc_expand/src/mbe/quoted.rs b/compiler/rustc_expand/src/mbe/quoted.rs index c8049495d223c..e205cb65d0229 100644 --- a/compiler/rustc_expand/src/mbe/quoted.rs +++ b/compiler/rustc_expand/src/mbe/quoted.rs @@ -63,13 +63,13 @@ pub(super) fn parse( let span = token.span.with_lo(start_sp.lo()); match frag.name { - sym::pat2018 | sym::pat2021 => { + sym::pat2015 | sym::pat2021 => { if !features.edition_macro_pats { feature_err( sess, sym::edition_macro_pats, frag.span, - "`pat2018` and `pat2021` are unstable.", + "`pat2015` and `pat2021` are unstable.", ) .emit(); } diff --git a/compiler/rustc_expand/src/mbe/transcribe.rs b/compiler/rustc_expand/src/mbe/transcribe.rs index dde65d998d81b..f9e7c4254bc49 100644 --- a/compiler/rustc_expand/src/mbe/transcribe.rs +++ b/compiler/rustc_expand/src/mbe/transcribe.rs @@ -209,7 +209,7 @@ pub(super) fn transcribe<'a>( } } else { // 0 is the initial counter (we have done 0 repretitions so far). `len` - // is the total number of reptitions we should generate. + // is the total number of repetitions we should generate. repeats.push((0, len)); // The first time we encounter the sequence we push it to the stack. It @@ -362,7 +362,7 @@ impl LockstepIterSize { /// appropriate meta-vars in `interpolations`. /// /// Note that if `repeats` does not match the exact correct depth of a meta-var, -/// `lookup_cur_matched` will return `None`, which is why this still works even in the presnece of +/// `lookup_cur_matched` will return `None`, which is why this still works even in the presence of /// multiple nested matcher sequences. fn lockstep_iter_size( tree: &mbe::TokenTree, diff --git a/compiler/rustc_expand/src/module.rs b/compiler/rustc_expand/src/module.rs index 076d3b02be93f..c5ce0baaa8f6a 100644 --- a/compiler/rustc_expand/src/module.rs +++ b/compiler/rustc_expand/src/module.rs @@ -1,232 +1,175 @@ +use crate::base::ModuleData; use rustc_ast::ptr::P; -use rustc_ast::{token, Attribute, Item}; -use rustc_errors::{struct_span_err, PResult}; +use rustc_ast::{token, Attribute, Inline, Item}; +use rustc_errors::{struct_span_err, DiagnosticBuilder}; use rustc_parse::new_parser_from_file; use rustc_session::parse::ParseSess; use rustc_session::Session; -use rustc_span::source_map::{FileName, Span}; use rustc_span::symbol::{sym, Ident}; +use rustc_span::Span; use std::path::{self, Path, PathBuf}; -#[derive(Clone)] -pub struct Directory { - pub path: PathBuf, - pub ownership: DirectoryOwnership, -} - #[derive(Copy, Clone)] -pub enum DirectoryOwnership { +pub enum DirOwnership { Owned { // None if `mod.rs`, `Some("foo")` if we're in `foo.rs`. relative: Option, }, UnownedViaBlock, - UnownedViaMod, } -/// Information about the path to a module. // Public for rustfmt usage. -pub struct ModulePath<'a> { - name: String, - path_exists: bool, - pub result: PResult<'a, ModulePathSuccess>, +pub struct ModulePathSuccess { + pub file_path: PathBuf, + pub dir_ownership: DirOwnership, } -// Public for rustfmt usage. -pub struct ModulePathSuccess { - pub path: PathBuf, - pub ownership: DirectoryOwnership, +crate struct ParsedExternalMod { + pub items: Vec>, + pub inner_span: Span, + pub file_path: PathBuf, + pub dir_path: PathBuf, + pub dir_ownership: DirOwnership, +} + +pub enum ModError<'a> { + CircularInclusion(Vec), + ModInBlock(Option), + FileNotFound(Ident, PathBuf), + MultipleCandidates(Ident, String, String), + ParserError(DiagnosticBuilder<'a>), } crate fn parse_external_mod( sess: &Session, - id: Ident, + ident: Ident, span: Span, // The span to blame on errors. - Directory { mut ownership, path }: Directory, + module: &ModuleData, + mut dir_ownership: DirOwnership, attrs: &mut Vec, - pop_mod_stack: &mut bool, -) -> (Vec>, Span, Directory) { +) -> ParsedExternalMod { // We bail on the first error, but that error does not cause a fatal error... (1) - let result: PResult<'_, _> = try { + let result: Result<_, ModError<'_>> = try { // Extract the file path and the new ownership. - let mp = submod_path(sess, id, span, &attrs, ownership, &path)?; - ownership = mp.ownership; + let mp = mod_file_path(sess, ident, &attrs, &module.dir_path, dir_ownership)?; + dir_ownership = mp.dir_ownership; // Ensure file paths are acyclic. - let mut included_mod_stack = sess.parse_sess.included_mod_stack.borrow_mut(); - error_on_circular_module(&sess.parse_sess, span, &mp.path, &included_mod_stack)?; - included_mod_stack.push(mp.path.clone()); - *pop_mod_stack = true; // We have pushed, so notify caller. - drop(included_mod_stack); + if let Some(pos) = module.file_path_stack.iter().position(|p| p == &mp.file_path) { + Err(ModError::CircularInclusion(module.file_path_stack[pos..].to_vec()))?; + } // Actually parse the external file as a module. - let mut parser = new_parser_from_file(&sess.parse_sess, &mp.path, Some(span)); - let (mut inner_attrs, items, inner_span) = parser.parse_mod(&token::Eof)?; + let mut parser = new_parser_from_file(&sess.parse_sess, &mp.file_path, Some(span)); + let (mut inner_attrs, items, inner_span) = + parser.parse_mod(&token::Eof).map_err(|err| ModError::ParserError(err))?; attrs.append(&mut inner_attrs); - (items, inner_span) + (items, inner_span, mp.file_path) }; // (1) ...instead, we return a dummy module. - let (items, inner_span) = result.map_err(|mut err| err.emit()).unwrap_or_default(); - - // Extract the directory path for submodules of the module. - let path = sess.source_map().span_to_unmapped_path(inner_span); - let mut path = match path { - FileName::Real(name) => name.into_local_path(), - other => PathBuf::from(other.to_string()), - }; - path.pop(); + let (items, inner_span, file_path) = + result.map_err(|err| err.report(sess, span)).unwrap_or_default(); - (items, inner_span, Directory { ownership, path }) -} + // Extract the directory path for submodules of the module. + let dir_path = file_path.parent().unwrap_or(&file_path).to_owned(); -fn error_on_circular_module<'a>( - sess: &'a ParseSess, - span: Span, - path: &Path, - included_mod_stack: &[PathBuf], -) -> PResult<'a, ()> { - if let Some(i) = included_mod_stack.iter().position(|p| *p == path) { - let mut err = String::from("circular modules: "); - for p in &included_mod_stack[i..] { - err.push_str(&p.to_string_lossy()); - err.push_str(" -> "); - } - err.push_str(&path.to_string_lossy()); - return Err(sess.span_diagnostic.struct_span_err(span, &err[..])); - } - Ok(()) + ParsedExternalMod { items, inner_span, file_path, dir_path, dir_ownership } } -crate fn push_directory( +crate fn mod_dir_path( sess: &Session, - id: Ident, + ident: Ident, attrs: &[Attribute], - Directory { mut ownership, mut path }: Directory, -) -> Directory { - if let Some(filename) = sess.first_attr_value_str_by_name(attrs, sym::path) { - path.push(&*filename.as_str()); - ownership = DirectoryOwnership::Owned { relative: None }; - } else { - // We have to push on the current module name in the case of relative - // paths in order to ensure that any additional module paths from inline - // `mod x { ... }` come after the relative extension. - // - // For example, a `mod z { ... }` inside `x/y.rs` should set the current - // directory path to `/x/y/z`, not `/x/z` with a relative offset of `y`. - if let DirectoryOwnership::Owned { relative } = &mut ownership { - if let Some(ident) = relative.take() { - // Remove the relative offset. - path.push(&*ident.as_str()); + module: &ModuleData, + mut dir_ownership: DirOwnership, + inline: Inline, +) -> (PathBuf, DirOwnership) { + match inline { + Inline::Yes => { + if let Some(file_path) = mod_file_path_from_attr(sess, attrs, &module.dir_path) { + // For inline modules file path from `#[path]` is actually the directory path + // for historical reasons, so we don't pop the last segment here. + return (file_path, DirOwnership::Owned { relative: None }); } - } - path.push(&*id.as_str()); - } - Directory { ownership, path } -} -fn submod_path<'a>( - sess: &'a Session, - id: Ident, - span: Span, - attrs: &[Attribute], - ownership: DirectoryOwnership, - dir_path: &Path, -) -> PResult<'a, ModulePathSuccess> { - if let Some(path) = submod_path_from_attr(sess, attrs, dir_path) { - let ownership = match path.file_name().and_then(|s| s.to_str()) { - // All `#[path]` files are treated as though they are a `mod.rs` file. - // This means that `mod foo;` declarations inside `#[path]`-included - // files are siblings, + // We have to push on the current module name in the case of relative + // paths in order to ensure that any additional module paths from inline + // `mod x { ... }` come after the relative extension. // - // Note that this will produce weirdness when a file named `foo.rs` is - // `#[path]` included and contains a `mod foo;` declaration. - // If you encounter this, it's your own darn fault :P - Some(_) => DirectoryOwnership::Owned { relative: None }, - _ => DirectoryOwnership::UnownedViaMod, - }; - return Ok(ModulePathSuccess { ownership, path }); - } + // For example, a `mod z { ... }` inside `x/y.rs` should set the current + // directory path to `/x/y/z`, not `/x/z` with a relative offset of `y`. + let mut dir_path = module.dir_path.clone(); + if let DirOwnership::Owned { relative } = &mut dir_ownership { + if let Some(ident) = relative.take() { + // Remove the relative offset. + dir_path.push(&*ident.as_str()); + } + } + dir_path.push(&*ident.as_str()); - let relative = match ownership { - DirectoryOwnership::Owned { relative } => relative, - DirectoryOwnership::UnownedViaBlock | DirectoryOwnership::UnownedViaMod => None, - }; - let ModulePath { path_exists, name, result } = - default_submod_path(&sess.parse_sess, id, span, relative, dir_path); - match ownership { - DirectoryOwnership::Owned { .. } => Ok(result?), - DirectoryOwnership::UnownedViaBlock => { - let _ = result.map_err(|mut err| err.cancel()); - error_decl_mod_in_block(&sess.parse_sess, span, path_exists, &name) + (dir_path, dir_ownership) } - DirectoryOwnership::UnownedViaMod => { - let _ = result.map_err(|mut err| err.cancel()); - error_cannot_declare_mod_here(&sess.parse_sess, span, path_exists, &name) + Inline::No => { + // FIXME: This is a subset of `parse_external_mod` without actual parsing, + // check whether the logic for unloaded, loaded and inline modules can be unified. + let file_path = mod_file_path(sess, ident, &attrs, &module.dir_path, dir_ownership) + .map(|mp| { + dir_ownership = mp.dir_ownership; + mp.file_path + }) + .unwrap_or_default(); + + // Extract the directory path for submodules of the module. + let dir_path = file_path.parent().unwrap_or(&file_path).to_owned(); + + (dir_path, dir_ownership) } } } -fn error_decl_mod_in_block<'a, T>( - sess: &'a ParseSess, - span: Span, - path_exists: bool, - name: &str, -) -> PResult<'a, T> { - let msg = "Cannot declare a non-inline module inside a block unless it has a path attribute"; - let mut err = sess.span_diagnostic.struct_span_err(span, msg); - if path_exists { - let msg = format!("Maybe `use` the module `{}` instead of redeclaring it", name); - err.span_note(span, &msg); +fn mod_file_path<'a>( + sess: &'a Session, + ident: Ident, + attrs: &[Attribute], + dir_path: &Path, + dir_ownership: DirOwnership, +) -> Result> { + if let Some(file_path) = mod_file_path_from_attr(sess, attrs, dir_path) { + // All `#[path]` files are treated as though they are a `mod.rs` file. + // This means that `mod foo;` declarations inside `#[path]`-included + // files are siblings, + // + // Note that this will produce weirdness when a file named `foo.rs` is + // `#[path]` included and contains a `mod foo;` declaration. + // If you encounter this, it's your own darn fault :P + let dir_ownership = DirOwnership::Owned { relative: None }; + return Ok(ModulePathSuccess { file_path, dir_ownership }); } - Err(err) -} -fn error_cannot_declare_mod_here<'a, T>( - sess: &'a ParseSess, - span: Span, - path_exists: bool, - name: &str, -) -> PResult<'a, T> { - let mut err = - sess.span_diagnostic.struct_span_err(span, "cannot declare a new module at this location"); - if !span.is_dummy() { - if let FileName::Real(src_name) = sess.source_map().span_to_filename(span) { - let src_path = src_name.into_local_path(); - if let Some(stem) = src_path.file_stem() { - let mut dest_path = src_path.clone(); - dest_path.set_file_name(stem); - dest_path.push("mod.rs"); - err.span_note( - span, - &format!( - "maybe move this module `{}` to its own directory via `{}`", - src_path.display(), - dest_path.display() - ), - ); - } - } - } - if path_exists { - err.span_note( - span, - &format!("... or maybe `use` the module `{}` instead of possibly redeclaring it", name), - ); + let relative = match dir_ownership { + DirOwnership::Owned { relative } => relative, + DirOwnership::UnownedViaBlock => None, + }; + let result = default_submod_path(&sess.parse_sess, ident, relative, dir_path); + match dir_ownership { + DirOwnership::Owned { .. } => result, + DirOwnership::UnownedViaBlock => Err(ModError::ModInBlock(match result { + Ok(_) | Err(ModError::MultipleCandidates(..)) => Some(ident), + _ => None, + })), } - Err(err) } /// Derive a submodule path from the first found `#[path = "path_string"]`. /// The provided `dir_path` is joined with the `path_string`. -pub(super) fn submod_path_from_attr( +fn mod_file_path_from_attr( sess: &Session, attrs: &[Attribute], dir_path: &Path, ) -> Option { // Extract path string from first `#[path = "path_string"]` attribute. - let path_string = sess.first_attr_value_str_by_name(attrs, sym::path)?; - let path_string = path_string.as_str(); + let path_string = sess.first_attr_value_str_by_name(attrs, sym::path)?.as_str(); // On windows, the base path might have the form // `\\?\foo\bar` in which case it does not tolerate @@ -242,15 +185,14 @@ pub(super) fn submod_path_from_attr( // Public for rustfmt usage. pub fn default_submod_path<'a>( sess: &'a ParseSess, - id: Ident, - span: Span, + ident: Ident, relative: Option, dir_path: &Path, -) -> ModulePath<'a> { +) -> Result> { // If we're in a foo.rs file instead of a mod.rs file, // we need to look for submodules in - // `./foo/.rs` and `./foo//mod.rs` rather than - // `./.rs` and `.//mod.rs`. + // `./foo/.rs` and `./foo//mod.rs` rather than + // `./.rs` and `.//mod.rs`. let relative_prefix_string; let relative_prefix = if let Some(ident) = relative { relative_prefix_string = format!("{}{}", ident.name, path::MAIN_SEPARATOR); @@ -259,7 +201,7 @@ pub fn default_submod_path<'a>( "" }; - let mod_name = id.name.to_string(); + let mod_name = ident.name.to_string(); let default_path_str = format!("{}{}.rs", relative_prefix, mod_name); let secondary_path_str = format!("{}{}{}mod.rs", relative_prefix, mod_name, path::MAIN_SEPARATOR); @@ -268,44 +210,74 @@ pub fn default_submod_path<'a>( let default_exists = sess.source_map().file_exists(&default_path); let secondary_exists = sess.source_map().file_exists(&secondary_path); - let result = match (default_exists, secondary_exists) { + match (default_exists, secondary_exists) { (true, false) => Ok(ModulePathSuccess { - path: default_path, - ownership: DirectoryOwnership::Owned { relative: Some(id) }, + file_path: default_path, + dir_ownership: DirOwnership::Owned { relative: Some(ident) }, }), (false, true) => Ok(ModulePathSuccess { - path: secondary_path, - ownership: DirectoryOwnership::Owned { relative: None }, + file_path: secondary_path, + dir_ownership: DirOwnership::Owned { relative: None }, }), - (false, false) => { - let mut err = struct_span_err!( - sess.span_diagnostic, - span, - E0583, - "file not found for module `{}`", - mod_name, - ); - err.help(&format!( - "to create the module `{}`, create file \"{}\"", - mod_name, - default_path.display(), - )); - Err(err) - } + (false, false) => Err(ModError::FileNotFound(ident, default_path)), (true, true) => { - let mut err = struct_span_err!( - sess.span_diagnostic, - span, - E0761, - "file for module `{}` found at both {} and {}", - mod_name, - default_path_str, - secondary_path_str, - ); - err.help("delete or rename one of them to remove the ambiguity"); - Err(err) + Err(ModError::MultipleCandidates(ident, default_path_str, secondary_path_str)) } - }; + } +} - ModulePath { name: mod_name, path_exists: default_exists || secondary_exists, result } +impl ModError<'_> { + fn report(self, sess: &Session, span: Span) { + let diag = &sess.parse_sess.span_diagnostic; + match self { + ModError::CircularInclusion(file_paths) => { + let mut msg = String::from("circular modules: "); + for file_path in &file_paths { + msg.push_str(&file_path.display().to_string()); + msg.push_str(" -> "); + } + msg.push_str(&file_paths[0].display().to_string()); + diag.struct_span_err(span, &msg) + } + ModError::ModInBlock(ident) => { + let msg = "cannot declare a non-inline module inside a block unless it has a path attribute"; + let mut err = diag.struct_span_err(span, msg); + if let Some(ident) = ident { + let note = + format!("maybe `use` the module `{}` instead of redeclaring it", ident); + err.span_note(span, ¬e); + } + err + } + ModError::FileNotFound(ident, default_path) => { + let mut err = struct_span_err!( + diag, + span, + E0583, + "file not found for module `{}`", + ident, + ); + err.help(&format!( + "to create the module `{}`, create file \"{}\"", + ident, + default_path.display(), + )); + err + } + ModError::MultipleCandidates(ident, default_path_short, secondary_path_short) => { + let mut err = struct_span_err!( + diag, + span, + E0761, + "file for module `{}` found at both {} and {}", + ident, + default_path_short, + secondary_path_short, + ); + err.help("delete or rename one of them to remove the ambiguity"); + err + } + ModError::ParserError(err) => err, + }.emit() + } } diff --git a/compiler/rustc_expand/src/placeholders.rs b/compiler/rustc_expand/src/placeholders.rs index 98682ba42959a..6586ba138fb99 100644 --- a/compiler/rustc_expand/src/placeholders.rs +++ b/compiler/rustc_expand/src/placeholders.rs @@ -117,7 +117,7 @@ pub fn placeholder( span, is_placeholder: true, }]), - AstFragmentKind::Fields => AstFragment::Fields(smallvec![ast::Field { + AstFragmentKind::Fields => AstFragment::Fields(smallvec![ast::ExprField { attrs: Default::default(), expr: expr_placeholder(), id, @@ -126,7 +126,7 @@ pub fn placeholder( span, is_placeholder: true, }]), - AstFragmentKind::FieldPats => AstFragment::FieldPats(smallvec![ast::FieldPat { + AstFragmentKind::FieldPats => AstFragment::FieldPats(smallvec![ast::PatField { attrs: Default::default(), id, ident, @@ -153,7 +153,7 @@ pub fn placeholder( ty: ty(), is_placeholder: true, }]), - AstFragmentKind::StructFields => AstFragment::StructFields(smallvec![ast::StructField { + AstFragmentKind::StructFields => AstFragment::StructFields(smallvec![ast::FieldDef { attrs: Default::default(), id, ident: None, @@ -205,19 +205,19 @@ impl<'a, 'b> MutVisitor for PlaceholderExpander<'a, 'b> { } } - fn flat_map_field(&mut self, field: ast::Field) -> SmallVec<[ast::Field; 1]> { + fn flat_map_expr_field(&mut self, field: ast::ExprField) -> SmallVec<[ast::ExprField; 1]> { if field.is_placeholder { - self.remove(field.id).make_fields() + self.remove(field.id).make_expr_fields() } else { - noop_flat_map_field(field, self) + noop_flat_map_expr_field(field, self) } } - fn flat_map_field_pattern(&mut self, fp: ast::FieldPat) -> SmallVec<[ast::FieldPat; 1]> { + fn flat_map_pat_field(&mut self, fp: ast::PatField) -> SmallVec<[ast::PatField; 1]> { if fp.is_placeholder { - self.remove(fp.id).make_field_patterns() + self.remove(fp.id).make_pat_fields() } else { - noop_flat_map_field_pattern(fp, self) + noop_flat_map_pat_field(fp, self) } } @@ -240,11 +240,11 @@ impl<'a, 'b> MutVisitor for PlaceholderExpander<'a, 'b> { } } - fn flat_map_struct_field(&mut self, sf: ast::StructField) -> SmallVec<[ast::StructField; 1]> { + fn flat_map_field_def(&mut self, sf: ast::FieldDef) -> SmallVec<[ast::FieldDef; 1]> { if sf.is_placeholder { - self.remove(sf.id).make_struct_fields() + self.remove(sf.id).make_field_defs() } else { - noop_flat_map_struct_field(sf, self) + noop_flat_map_field_def(sf, self) } } diff --git a/compiler/rustc_expand/src/proc_macro.rs b/compiler/rustc_expand/src/proc_macro.rs index 8cbaa7c945a81..3f84979ac05e7 100644 --- a/compiler/rustc_expand/src/proc_macro.rs +++ b/compiler/rustc_expand/src/proc_macro.rs @@ -90,10 +90,11 @@ impl MultiItemModifier for ProcMacroDerive { } _ => unreachable!(), }; - let input = if item.pretty_printing_compatibility_hack() { + let input = if crate::base::pretty_printing_compatibility_hack(&item, &ecx.sess.parse_sess) + { TokenTree::token(token::Interpolated(Lrc::new(item)), DUMMY_SP).into() } else { - nt_to_tokenstream(&item, &ecx.sess.parse_sess, CanSynthesizeMissingTokens::Yes) + nt_to_tokenstream(&item, &ecx.sess.parse_sess, CanSynthesizeMissingTokens::No) }; let server = proc_macro_server::Rustc::new(ecx); diff --git a/compiler/rustc_expand/src/proc_macro_server.rs b/compiler/rustc_expand/src/proc_macro_server.rs index b6195d3bbc4a6..1ea26b4eab44b 100644 --- a/compiler/rustc_expand/src/proc_macro_server.rs +++ b/compiler/rustc_expand/src/proc_macro_server.rs @@ -2,16 +2,21 @@ use crate::base::ExtCtxt; use rustc_ast as ast; use rustc_ast::token; +use rustc_ast::token::Nonterminal; +use rustc_ast::token::NtIdent; use rustc_ast::tokenstream::{self, CanSynthesizeMissingTokens}; use rustc_ast::tokenstream::{DelimSpan, Spacing::*, TokenStream, TreeAndSpacing}; use rustc_ast_pretty::pprust; use rustc_data_structures::sync::Lrc; use rustc_errors::Diagnostic; +use rustc_lint_defs::builtin::PROC_MACRO_BACK_COMPAT; +use rustc_lint_defs::BuiltinLintDiagnostics; use rustc_parse::lexer::nfc_normalize; use rustc_parse::{nt_to_tokenstream, parse_stream_from_source_str}; use rustc_session::parse::ParseSess; +use rustc_span::hygiene::ExpnKind; use rustc_span::symbol::{self, kw, sym, Symbol}; -use rustc_span::{BytePos, FileName, MultiSpan, Pos, SourceFile, Span}; +use rustc_span::{BytePos, FileName, MultiSpan, Pos, RealFileName, SourceFile, Span}; use pm::bridge::{server, TokenTree}; use pm::{Delimiter, Level, LineColumn, Spacing}; @@ -48,11 +53,11 @@ impl ToInternal for Delimiter { } } -impl FromInternal<(TreeAndSpacing, &'_ ParseSess, &'_ mut Vec)> +impl FromInternal<(TreeAndSpacing, &'_ mut Vec, &mut Rustc<'_>)> for TokenTree { fn from_internal( - ((tree, spacing), sess, stack): (TreeAndSpacing, &ParseSess, &mut Vec), + ((tree, spacing), stack, rustc): (TreeAndSpacing, &mut Vec, &mut Rustc<'_>), ) -> Self { use rustc_ast::token::*; @@ -141,10 +146,10 @@ impl FromInternal<(TreeAndSpacing, &'_ ParseSess, &'_ mut Vec)> SingleQuote => op!('\''), Ident(name, false) if name == kw::DollarCrate => tt!(Ident::dollar_crate()), - Ident(name, is_raw) => tt!(Ident::new(sess, name, is_raw)), + Ident(name, is_raw) => tt!(Ident::new(rustc.sess, name, is_raw)), Lifetime(name) => { let ident = symbol::Ident::new(name, span).without_first_quote(); - stack.push(tt!(Ident::new(sess, ident.name, false))); + stack.push(tt!(Ident::new(rustc.sess, ident.name, false))); tt!(Punct::new('\'', true)) } Literal(lit) => tt!(Literal { lit }), @@ -174,17 +179,15 @@ impl FromInternal<(TreeAndSpacing, &'_ ParseSess, &'_ mut Vec)> } Interpolated(nt) => { - if let Some((name, is_raw)) = - nt.ident_name_compatibility_hack(span, sess.source_map()) - { - TokenTree::Ident(Ident::new(sess, name.name, is_raw, name.span)) + if let Some((name, is_raw)) = ident_name_compatibility_hack(&nt, span, rustc) { + TokenTree::Ident(Ident::new(rustc.sess, name.name, is_raw, name.span)) } else { - let stream = nt_to_tokenstream(&nt, sess, CanSynthesizeMissingTokens::No); + let stream = nt_to_tokenstream(&nt, rustc.sess, CanSynthesizeMissingTokens::No); TokenTree::Group(Group { delimiter: Delimiter::None, stream, span: DelimSpan::from_single(span), - flatten: nt.pretty_printing_compatibility_hack(), + flatten: crate::base::pretty_printing_compatibility_hack(&nt, rustc.sess), }) } } @@ -446,7 +449,7 @@ impl server::TokenStreamIter for Rustc<'_> { loop { let tree = iter.stack.pop().or_else(|| { let next = iter.cursor.next_with_spacing()?; - Some(TokenTree::from_internal((next, self.sess, &mut iter.stack))) + Some(TokenTree::from_internal((next, &mut iter.stack, self))) })?; // A hack used to pass AST fragments to attribute and derive macros // as a single nonterminal token instead of a token stream. @@ -711,3 +714,100 @@ impl server::Span for Rustc<'_> { self.sess.source_map().span_to_snippet(span).ok() } } + +// See issue #74616 for details +fn ident_name_compatibility_hack( + nt: &Nonterminal, + orig_span: Span, + rustc: &mut Rustc<'_>, +) -> Option<(rustc_span::symbol::Ident, bool)> { + if let NtIdent(ident, is_raw) = nt { + if let ExpnKind::Macro(_, macro_name) = orig_span.ctxt().outer_expn_data().kind { + let source_map = rustc.sess.source_map(); + let filename = source_map.span_to_filename(orig_span); + if let FileName::Real(RealFileName::Named(path)) = filename { + let matches_prefix = |prefix, filename| { + // Check for a path that ends with 'prefix*/src/' + let mut iter = path.components().rev(); + iter.next().and_then(|p| p.as_os_str().to_str()) == Some(filename) + && iter.next().and_then(|p| p.as_os_str().to_str()) == Some("src") + && iter + .next() + .and_then(|p| p.as_os_str().to_str()) + .map_or(false, |p| p.starts_with(prefix)) + }; + + let time_macros_impl = + macro_name == sym::impl_macros && matches_prefix("time-macros-impl", "lib.rs"); + let js_sys = macro_name == sym::arrays && matches_prefix("js-sys", "lib.rs"); + if time_macros_impl || js_sys { + let snippet = source_map.span_to_snippet(orig_span); + if snippet.as_deref() == Ok("$name") { + if time_macros_impl { + rustc.sess.buffer_lint_with_diagnostic( + &PROC_MACRO_BACK_COMPAT, + orig_span, + ast::CRATE_NODE_ID, + "using an old version of `time-macros-impl`", + BuiltinLintDiagnostics::ProcMacroBackCompat( + "the `time-macros-impl` crate will stop compiling in futures version of Rust. \ + Please update to the latest version of the `time` crate to avoid breakage".to_string()) + ); + return Some((*ident, *is_raw)); + } + if js_sys { + if let Some(c) = path + .components() + .flat_map(|c| c.as_os_str().to_str()) + .find(|c| c.starts_with("js-sys")) + { + let mut version = c.trim_start_matches("js-sys-").split("."); + if version.next() == Some("0") + && version.next() == Some("3") + && version + .next() + .and_then(|c| c.parse::().ok()) + .map_or(false, |v| v < 40) + { + rustc.sess.buffer_lint_with_diagnostic( + &PROC_MACRO_BACK_COMPAT, + orig_span, + ast::CRATE_NODE_ID, + "using an old version of `js-sys`", + BuiltinLintDiagnostics::ProcMacroBackCompat( + "older versions of the `js-sys` crate will stop compiling in future versions of Rust; \ + please update to `js-sys` v0.3.40 or above".to_string()) + ); + return Some((*ident, *is_raw)); + } + } + } + } + } + + if macro_name == sym::tuple_from_req && matches_prefix("actix-web", "extract.rs") { + let snippet = source_map.span_to_snippet(orig_span); + if snippet.as_deref() == Ok("$T") { + if let FileName::Real(RealFileName::Named(macro_path)) = + source_map.span_to_filename(rustc.def_site) + { + if macro_path.to_string_lossy().contains("pin-project-internal-0.") { + rustc.sess.buffer_lint_with_diagnostic( + &PROC_MACRO_BACK_COMPAT, + orig_span, + ast::CRATE_NODE_ID, + "using an old version of `actix-web`", + BuiltinLintDiagnostics::ProcMacroBackCompat( + "the version of `actix-web` you are using might stop compiling in future versions of Rust; \ + please update to the latest version of the `actix-web` crate to avoid breakage".to_string()) + ); + return Some((*ident, *is_raw)); + } + } + } + } + } + } + } + None +} diff --git a/compiler/rustc_expand/src/tokenstream/tests.rs b/compiler/rustc_expand/src/tokenstream/tests.rs index 4e818e9feb08b..8b546e7e4a343 100644 --- a/compiler/rustc_expand/src/tokenstream/tests.rs +++ b/compiler/rustc_expand/src/tokenstream/tests.rs @@ -1,7 +1,7 @@ use crate::tests::string_to_stream; use rustc_ast::token; -use rustc_ast::tokenstream::{TokenStream, TokenStreamBuilder, TokenTree}; +use rustc_ast::tokenstream::{Spacing, TokenStream, TokenStreamBuilder, TokenTree}; use rustc_span::with_default_session_globals; use rustc_span::{BytePos, Span, Symbol}; use smallvec::smallvec; @@ -14,6 +14,10 @@ fn sp(a: u32, b: u32) -> Span { Span::with_root_ctxt(BytePos(a), BytePos(b)) } +fn joint(tree: TokenTree) -> TokenStream { + TokenStream::new(vec![(tree, Spacing::Joint)]) +} + #[test] fn test_concat() { with_default_session_globals(|| { @@ -99,8 +103,8 @@ fn test_is_empty() { fn test_dotdotdot() { with_default_session_globals(|| { let mut builder = TokenStreamBuilder::new(); - builder.push(TokenTree::token(token::Dot, sp(0, 1)).joint()); - builder.push(TokenTree::token(token::Dot, sp(1, 2)).joint()); + builder.push(joint(TokenTree::token(token::Dot, sp(0, 1)))); + builder.push(joint(TokenTree::token(token::Dot, sp(1, 2)))); builder.push(TokenTree::token(token::Dot, sp(2, 3))); let stream = builder.build(); assert!(stream.eq_unspanned(&string_to_ts("..."))); diff --git a/compiler/rustc_feature/src/accepted.rs b/compiler/rustc_feature/src/accepted.rs index 5012d500cee20..e8642a52749c4 100644 --- a/compiler/rustc_feature/src/accepted.rs +++ b/compiler/rustc_feature/src/accepted.rs @@ -275,6 +275,12 @@ declare_features! ( (accepted, move_ref_pattern, "1.49.0", Some(68354), None), /// The smallest useful subset of `const_generics`. (accepted, min_const_generics, "1.51.0", Some(74878), None), + /// The `unsafe_op_in_unsafe_fn` lint (allowed by default): no longer treat an unsafe function as an unsafe block. + (accepted, unsafe_block_in_unsafe_fn, "1.52.0", Some(71668), None), + /// Allows the use of or-patterns (e.g., `0 | 1`). + (accepted, or_patterns, "1.53.0", Some(54883), None), + /// Allows defining identifiers beyond ASCII. + (accepted, non_ascii_idents, "1.53.0", Some(55467), None), // ------------------------------------------------------------------------- // feature-group-end: accepted features diff --git a/compiler/rustc_feature/src/active.rs b/compiler/rustc_feature/src/active.rs index 8ee995a59d80d..a410826d3fda6 100644 --- a/compiler/rustc_feature/src/active.rs +++ b/compiler/rustc_feature/src/active.rs @@ -134,9 +134,6 @@ declare_features! ( /// Allows using the `box $expr` syntax. (active, box_syntax, "1.0.0", Some(49733), None), - /// Allows using `#[main]` to replace the entrypoint `#[lang = "start"]` calls. - (active, main, "1.0.0", Some(29634), None), - /// Allows using `#[start]` on a function indicating that it is the program entrypoint. (active, start, "1.0.0", Some(29633), None), @@ -216,6 +213,10 @@ declare_features! ( /// Renamed from `optin_builtin_traits`. (active, auto_traits, "1.50.0", Some(13231), None), + /// Allows `#[doc(notable_trait)]`. + /// Renamed from `doc_spotlight`. + (active, doc_notable_trait, "1.52.0", Some(45040), None), + // no-tracking-issue-end // ------------------------------------------------------------------------- @@ -254,12 +255,6 @@ declare_features! ( // feature-group-start: actual feature gates // ------------------------------------------------------------------------- - /// Allows using the `#[link_args]` attribute. - (active, link_args, "1.0.0", Some(29596), None), - - /// Allows defining identifiers beyond ASCII. - (active, non_ascii_idents, "1.0.0", Some(55467), None), - /// Allows using `#[plugin_registrar]` on functions. (active, plugin_registrar, "1.0.0", Some(29597), None), @@ -374,9 +369,6 @@ declare_features! ( /// Allows `#[doc(masked)]`. (active, doc_masked, "1.21.0", Some(44027), None), - /// Allows `#[doc(spotlight)]`. - (active, doc_spotlight, "1.22.0", Some(45040), None), - /// Allows `#[doc(include = "some-file")]`. (active, external_doc, "1.22.0", Some(44732), None), @@ -488,9 +480,6 @@ declare_features! ( /// Allows `impl Trait` to be used inside type aliases (RFC 2515). (active, type_alias_impl_trait, "1.38.0", Some(63063), None), - /// Allows the use of or-patterns (e.g., `0 | 1`). - (active, or_patterns, "1.38.0", Some(54883), None), - /// Allows the definition of `const extern fn` and `const unsafe extern fn`. (active, const_extern_fn, "1.40.0", Some(64926), None), @@ -557,9 +546,6 @@ declare_features! ( /// Allows the use of `#[ffi_const]` on foreign functions. (active, ffi_const, "1.45.0", Some(58328), None), - /// No longer treat an unsafe function as an unsafe block. - (active, unsafe_block_in_unsafe_fn, "1.45.0", Some(71668), None), - /// Allows `extern "avr-interrupt" fn()` and `extern "avr-non-blocking-interrupt" fn()`. (active, abi_avr_interrupt, "1.45.0", Some(69664), None), @@ -617,7 +603,7 @@ declare_features! ( /// Allows arbitrary expressions in key-value attributes at parse time. (active, extended_key_value_attributes, "1.50.0", Some(78835), None), - /// `:pat2018` and `:pat2021` macro matchers. + /// `:pat2015` and `:pat2021` macro matchers. (active, edition_macro_pats, "1.51.0", Some(54883), None), /// Allows const generics to have default values (e.g. `struct Foo(...);`). @@ -641,9 +627,21 @@ declare_features! ( /// Allows `pub` on `macro_rules` items. (active, pub_macro_rules, "1.52.0", Some(78855), None), + /// Allows the use of type alias impl trait in function return positions + (active, min_type_alias_impl_trait, "1.52.0", Some(63063), None), + /// Allows associated types in inherent impls. (active, inherent_associated_types, "1.52.0", Some(8995), None), + /// Allows `extern "C-unwind" fn` to enable unwinding across ABI boundaries. + (active, c_unwind, "1.52.0", Some(74990), None), + + /// Allows using `#[repr(align(...))]` on function items + (active, fn_align, "1.53.0", Some(82232), None), + + /// Allows `extern "wasm" fn` + (active, wasm_abi, "1.53.0", Some(83788), None), + // ------------------------------------------------------------------------- // feature-group-end: actual feature gates // ------------------------------------------------------------------------- @@ -670,6 +668,7 @@ pub const INCOMPLETE_FEATURES: &[Symbol] = &[ sym::capture_disjoint_fields, sym::const_generics_defaults, sym::inherent_associated_types, + sym::type_alias_impl_trait, ]; /// Some features are not allowed to be used together at the same time, if diff --git a/compiler/rustc_feature/src/builtin_attrs.rs b/compiler/rustc_feature/src/builtin_attrs.rs index 072062dd615d8..43054f5bf5e70 100644 --- a/compiler/rustc_feature/src/builtin_attrs.rs +++ b/compiler/rustc_feature/src/builtin_attrs.rs @@ -227,8 +227,8 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[ template!(List: r#"name = "...", /*opt*/ kind = "dylib|static|...", /*opt*/ wasm_import_module = "...""#), ), ungated!(link_name, AssumedUsed, template!(NameValueStr: "name")), - ungated!(no_link, Normal, template!(Word)), - ungated!(repr, Normal, template!(List: "C")), + ungated!(no_link, AssumedUsed, template!(Word)), + ungated!(repr, AssumedUsed, template!(List: "C")), ungated!(export_name, AssumedUsed, template!(NameValueStr: "name")), ungated!(link_section, AssumedUsed, template!(NameValueStr: "name")), ungated!(no_mangle, AssumedUsed, template!(Word)), @@ -279,11 +279,6 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[ // Linking: gated!(naked, AssumedUsed, template!(Word), naked_functions, experimental!(naked)), - gated!( - link_args, Normal, template!(NameValueStr: "args"), - "the `link_args` attribute is experimental and not portable across platforms, \ - it is recommended to use `#[link(name = \"foo\")] instead", - ), gated!( link_ordinal, AssumedUsed, template!(List: "ordinal"), raw_dylib, experimental!(link_ordinal) @@ -322,7 +317,7 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[ "custom test frameworks are an unstable feature", ), // RFC #1268 - gated!(marker, Normal, template!(Word), marker_trait_attr, experimental!(marker)), + gated!(marker, AssumedUsed, template!(Word), marker_trait_attr, experimental!(marker)), gated!( thread_local, AssumedUsed, template!(Word), "`#[thread_local]` is an experimental feature, and does not currently handle destructors", @@ -541,6 +536,10 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[ rustc_specialization_trait, Normal, template!(Word), "the `#[rustc_specialization_trait]` attribute is used to check specializations" ), + rustc_attr!( + rustc_main, Normal, template!(Word), + "the `#[rustc_main]` attribute is used internally to specify test entry point function", + ), // ========================================================================== // Internal attributes, Testing: diff --git a/compiler/rustc_feature/src/lib.rs b/compiler/rustc_feature/src/lib.rs index 2a7c2a02fbaf4..654d2408580fe 100644 --- a/compiler/rustc_feature/src/lib.rs +++ b/compiler/rustc_feature/src/lib.rs @@ -1,7 +1,7 @@ //! # Feature gates //! //! This crate declares the set of past and present unstable features in the compiler. -//! Feature gate checking itself is done in `librustc_ast_passes/feature_gate.rs` +//! Feature gate checking itself is done in `rustc_ast_passes/src/feature_gate.rs` //! at the moment. //! //! Features are enabled in programs via the crate-level attributes of diff --git a/compiler/rustc_feature/src/removed.rs b/compiler/rustc_feature/src/removed.rs index 38a3a4e3d4411..c2ad339ed4126 100644 --- a/compiler/rustc_feature/src/removed.rs +++ b/compiler/rustc_feature/src/removed.rs @@ -80,6 +80,11 @@ declare_features! ( Some("subsumed by `#![feature(allocator_internals)]`")), /// Allows identifying crates that contain sanitizer runtimes. (removed, sanitizer_runtime, "1.17.0", None, None, None), + /// Allows `#[doc(spotlight)]`. + /// The attribute was renamed to `#[doc(notable_trait)]` + /// and the feature to `doc_notable_trait`. + (removed, doc_spotlight, "1.22.0", Some(45040), None, + Some("renamed to `doc_notable_trait`")), (removed, proc_macro_mod, "1.27.0", Some(54727), None, Some("subsumed by `#![feature(proc_macro_hygiene)]`")), (removed, proc_macro_expr, "1.27.0", Some(54727), None, @@ -106,7 +111,7 @@ declare_features! ( Some("subsumed by `.await` syntax")), /// Allows defining `existential type`s. (removed, existential_type, "1.38.0", Some(63063), None, - Some("removed in favor of `#![feature(type_alias_impl_trait)]`")), + Some("removed in favor of `#![feature(min_type_alias_impl_trait)]`")), /// Allows using the macros: /// + `__diagnostic_used` /// + `__register_diagnostic` @@ -123,6 +128,12 @@ declare_features! ( /// Allows comparing raw pointers during const eval. (removed, const_compare_raw_pointers, "1.46.0", Some(53020), None, Some("cannot be allowed in const eval in any meaningful way")), + /// Allows using the `#[link_args]` attribute. + (removed, link_args, "1.53.0", Some(29596), None, + Some("removed in favor of using `-C link-arg=ARG` on command line, \ + which is available from cargo build scripts with `cargo:rustc-link-arg` now")), + /// Allows using `#[main]` to replace the entrypoint `#[lang = "start"]` calls. + (removed, main, "1.53.0", Some(29634), None, None), // ------------------------------------------------------------------------- // feature-group-end: removed features diff --git a/compiler/rustc_graphviz/src/lib.rs b/compiler/rustc_graphviz/src/lib.rs index 9653ff022f192..db70beb59141b 100644 --- a/compiler/rustc_graphviz/src/lib.rs +++ b/compiler/rustc_graphviz/src/lib.rs @@ -413,10 +413,6 @@ impl<'a> Id<'a> { pub fn as_slice(&'a self) -> &'a str { &*self.name } - - pub fn name(self) -> Cow<'a, str> { - self.name - } } /// Each instance of a type that implements `Label` maps to a @@ -484,10 +480,6 @@ impl<'a> LabelText<'a> { LabelStr(s.into()) } - pub fn escaped>>(s: S) -> LabelText<'a> { - EscStr(s.into()) - } - pub fn html>>(s: S) -> LabelText<'a> { HtmlStr(s.into()) } @@ -543,11 +535,6 @@ impl<'a> LabelText<'a> { } } - /// Puts `prefix` on a line above this label, with a blank line separator. - pub fn prefix_line(self, prefix: LabelText<'_>) -> LabelText<'static> { - prefix.suffix_line(self) - } - /// Puts `suffix` on a line below this label, with a blank line separator. pub fn suffix_line(self, suffix: LabelText<'_>) -> LabelText<'static> { let mut prefix = self.pre_escaped_content().into_owned(); @@ -602,11 +589,6 @@ pub enum RenderOption { DarkTheme, } -/// Returns vec holding all the default render options. -pub fn default_options() -> Vec { - vec![] -} - /// Renders directed graph `g` into the writer `w` in DOT syntax. /// (Simple wrapper around `render_opts` that passes a default set of options.) pub fn render<'a, N, E, G, W>(g: &'a G, w: &mut W) -> io::Result<()> diff --git a/compiler/rustc_graphviz/src/tests.rs b/compiler/rustc_graphviz/src/tests.rs index 70b8197f5ef38..a297bac86c410 100644 --- a/compiler/rustc_graphviz/src/tests.rs +++ b/compiler/rustc_graphviz/src/tests.rs @@ -111,7 +111,7 @@ impl<'a> Labeller<'a> for LabelledGraph { fn node_label(&'a self, n: &Node) -> LabelText<'a> { match self.node_labels[*n] { Some(l) => LabelStr(l.into()), - None => LabelStr(id_name(n).name()), + None => LabelStr(id_name(n).name), } } fn edge_label(&'a self, e: &&'a Edge) -> LabelText<'a> { diff --git a/compiler/rustc_hir/src/arena.rs b/compiler/rustc_hir/src/arena.rs index c7dc66b70fef1..ddf8218616988 100644 --- a/compiler/rustc_hir/src/arena.rs +++ b/compiler/rustc_hir/src/arena.rs @@ -25,8 +25,8 @@ macro_rules! arena_types { [] generic_bound: rustc_hir::GenericBound<$tcx>, [] generic_param: rustc_hir::GenericParam<$tcx>, [] expr: rustc_hir::Expr<$tcx>, - [] field: rustc_hir::Field<$tcx>, - [] field_pat: rustc_hir::FieldPat<$tcx>, + [] expr_field: rustc_hir::ExprField<$tcx>, + [] pat_field: rustc_hir::PatField<$tcx>, [] fn_decl: rustc_hir::FnDecl<$tcx>, [] foreign_item: rustc_hir::ForeignItem<$tcx>, [few] foreign_item_ref: rustc_hir::ForeignItemRef<$tcx>, @@ -42,7 +42,7 @@ macro_rules! arena_types { [] poly_trait_ref: rustc_hir::PolyTraitRef<$tcx>, [] qpath: rustc_hir::QPath<$tcx>, [] stmt: rustc_hir::Stmt<$tcx>, - [] struct_field: rustc_hir::StructField<$tcx>, + [] field_def: rustc_hir::FieldDef<$tcx>, [] trait_item_ref: rustc_hir::TraitItemRef, [] ty: rustc_hir::Ty<$tcx>, [] type_binding: rustc_hir::TypeBinding<$tcx>, diff --git a/compiler/rustc_hir/src/def.rs b/compiler/rustc_hir/src/def.rs index a81eb747a335a..de10d88c1d250 100644 --- a/compiler/rustc_hir/src/def.rs +++ b/compiler/rustc_hir/src/def.rs @@ -20,6 +20,7 @@ pub enum CtorOf { Variant, } +/// What kind of constructor something is. #[derive(Clone, Copy, PartialEq, Eq, Encodable, Decodable, Hash, Debug)] #[derive(HashStable_Generic)] pub enum CtorKind { @@ -31,6 +32,7 @@ pub enum CtorKind { Fictive, } +/// An attribute that is not a macro; e.g., `#[inline]` or `#[rustfmt::skip]`. #[derive(Clone, Copy, PartialEq, Eq, Encodable, Decodable, Hash, Debug)] #[derive(HashStable_Generic)] pub enum NonMacroAttrKind { @@ -47,33 +49,51 @@ pub enum NonMacroAttrKind { Registered, } +/// What kind of definition something is; e.g., `mod` vs `struct`. #[derive(Clone, Copy, PartialEq, Eq, Encodable, Decodable, Hash, Debug)] #[derive(HashStable_Generic)] pub enum DefKind { // Type namespace Mod, - /// Refers to the struct itself, `DefKind::Ctor` refers to its constructor if it exists. + /// Refers to the struct itself, [`DefKind::Ctor`] refers to its constructor if it exists. Struct, Union, Enum, - /// Refers to the variant itself, `DefKind::Ctor` refers to its constructor if it exists. + /// Refers to the variant itself, [`DefKind::Ctor`] refers to its constructor if it exists. Variant, Trait, - /// `type Foo = Bar;` + /// Type alias: `type Foo = Bar;` TyAlias, + /// Type from an `extern` block. ForeignTy, + /// Trait alias: `trait IntIterator = Iterator;` TraitAlias, + /// Associated type: `trait MyTrait { type Assoc; }` AssocTy, + /// Type parameter: the `T` in `struct Vec { ... }` TyParam, // Value namespace Fn, Const, + /// Constant generic parameter: `struct Foo { ... }` ConstParam, Static, /// Refers to the struct or enum variant's constructor. + /// + /// The reason `Ctor` exists in addition to [`DefKind::Struct`] and + /// [`DefKind::Variant`] is because structs and enum variants exist + /// in the *type* namespace, whereas struct and enum variant *constructors* + /// exist in the *value* namespace. + /// + /// You may wonder why enum variants exist in the type namespace as opposed + /// to the value namespace. Check out [RFC 2593] for intuition on why that is. + /// + /// [RFC 2593]: https://github.com/rust-lang/rfcs/pull/2593 Ctor(CtorOf, CtorKind), + /// Associated function: `impl MyStruct { fn associated() {} }` AssocFn, + /// Associated constant: `trait MyTrait { const ASSOC: usize; }` AssocConst, // Macro namespace @@ -82,11 +102,16 @@ pub enum DefKind { // Not namespaced (or they are, but we don't treat them so) ExternCrate, Use, + /// An `extern` block. ForeignMod, + /// Anonymous constant, e.g. the `1 + 2` in `[u8; 1 + 2]`, or `const { 1 + 2}` AnonConst, + /// Opaque type, aka `impl Trait`. OpaqueTy, Field, + /// Lifetime parameter: the `'a` in `struct Foo<'a> { ... }` LifetimeParam, + /// A use of [`global_asm!`]. GlobalAsm, Impl, Closure, @@ -196,35 +221,130 @@ impl DefKind { } /// The resolution of a path or export. +/// +/// For every path or identifier in Rust, the compiler must determine +/// what the path refers to. This process is called name resolution, +/// and `Res` is the primary result of name resolution. +/// +/// For example, everything prefixed with `/* Res */` in this example has +/// an associated `Res`: +/// +/// ``` +/// fn str_to_string(s: & /* Res */ str) -> /* Res */ String { +/// /* Res */ String::from(/* Res */ s) +/// } +/// +/// /* Res */ str_to_string("hello"); +/// ``` +/// +/// The associated `Res`s will be: +/// +/// - `str` will resolve to [`Res::PrimTy`]; +/// - `String` will resolve to [`Res::Def`], and the `Res` will include the [`DefId`] +/// for `String` as defined in the standard library; +/// - `String::from` will also resolve to [`Res::Def`], with the [`DefId`] +/// pointing to `String::from`; +/// - `s` will resolve to [`Res::Local`]; +/// - the call to `str_to_string` will resolve to [`Res::Def`], with the [`DefId`] +/// pointing to the definition of `str_to_string` in the current crate. +// #[derive(Clone, Copy, PartialEq, Eq, Encodable, Decodable, Hash, Debug)] #[derive(HashStable_Generic)] pub enum Res { + /// Definition having a unique ID (`DefId`), corresponds to something defined in user code. + /// + /// **Not bound to a specific namespace.** Def(DefKind, DefId), // Type namespace + /// A primitive type such as `i32` or `str`. + /// + /// **Belongs to the type namespace.** PrimTy(hir::PrimTy), - /// `Self`, with both an optional trait and impl `DefId`. + /// The `Self` type, optionally with the trait it is associated with + /// and optionally with the [`DefId`] of the impl it is associated with. + /// + /// **Belongs to the type namespace.** + /// + /// For example, the `Self` in /// - /// HACK(min_const_generics): impl self types also have an optional requirement to not mention + /// ``` + /// trait Foo { + /// fn foo() -> Box; + /// } + /// ``` + /// + /// would have the [`DefId`] of `Foo` associated with it. The `Self` in + /// + /// ``` + /// struct Bar; + /// + /// impl Bar { + /// fn new() -> Self { Bar } + /// } + /// ``` + /// + /// would have the [`DefId`] of the impl associated with it. Finally, the `Self` in + /// + /// ``` + /// impl Foo for Bar { + /// fn foo() -> Box { Box::new(Bar) } + /// } + /// ``` + /// + /// would have both the [`DefId`] of `Foo` and the [`DefId`] of the impl + /// associated with it. + /// + /// *See also [`Res::SelfCtor`].* + /// + /// ----- + /// + /// HACK(min_const_generics): impl self types also have an optional requirement to **not** mention /// any generic parameters to allow the following with `min_const_generics`: - /// ```rust - /// impl Foo { fn test() -> [u8; std::mem::size_of::()] {} } + /// ``` + /// impl Foo { fn test() -> [u8; std::mem::size_of::()] { todo!() } } /// ``` /// We do however allow `Self` in repeat expression even if it is generic to not break code /// which already works on stable while causing the `const_evaluatable_unchecked` future compat lint. /// /// FIXME(lazy_normalization_consts): Remove this bodge once that feature is stable. - SelfTy(Option /* trait */, Option<(DefId, bool)> /* impl */), - ToolMod, // e.g., `rustfmt` in `#[rustfmt::skip]` + SelfTy( + /// Optionally, the trait associated with this `Self` type. + Option, + /// Optionally, the impl associated with this `Self` type. + Option<(DefId, bool)>, + ), + /// A tool attribute module; e.g., the `rustfmt` in `#[rustfmt::skip]`. + /// + /// **Belongs to the type namespace.** + ToolMod, // Value namespace - SelfCtor(DefId /* impl */), // `DefId` refers to the impl + /// The `Self` constructor, along with the [`DefId`] + /// of the impl it is associated with. + /// + /// **Belongs to the value namespace.** + /// + /// *See also [`Res::SelfTy`].* + SelfCtor(DefId), + /// A local variable or function parameter. + /// + /// **Belongs to the value namespace.** Local(Id), // Macro namespace + /// An attribute that is *not* implemented via macro. + /// E.g., `#[inline]` and `#[rustfmt::skip]`, which are essentially directives, + /// as opposed to `#[test]`, which is a builtin macro. + /// + /// **Belongs to the macro namespace.** NonMacroAttr(NonMacroAttrKind), // e.g., `#[inline]` or `#[rustfmt::skip]` // All namespaces + /// Name resolution failed. We use a dummy `Res` variant so later phases + /// of the compiler won't crash and can instead report more errors. + /// + /// **Not bound to a specific namespace.** Err, } @@ -275,17 +395,26 @@ impl PartialRes { } } -/// Different kinds of symbols don't influence each other. -/// -/// Therefore, they have a separate universe (namespace). +/// Different kinds of symbols can coexist even if they share the same textual name. +/// Therefore, they each have a separate universe (known as a "namespace"). #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)] pub enum Namespace { + /// The type namespace includes `struct`s, `enum`s, `union`s, `trait`s, and `mod`s + /// (and, by extension, crates). + /// + /// Note that the type namespace includes other items; this is not an + /// exhaustive list. TypeNS, + /// The value namespace includes `fn`s, `const`s, `static`s, and local variables (including function arguments). ValueNS, + /// The macro namespace includes `macro_rules!` macros, declarative `macro`s, + /// procedural macros, attribute macros, `derive` macros, and non-macro attributes + /// like `#[inline]` and `#[rustfmt::skip]`. MacroNS, } impl Namespace { + /// The English description of the namespace. pub fn descr(self) -> &'static str { match self { Self::TypeNS => "type", diff --git a/compiler/rustc_hir/src/definitions.rs b/compiler/rustc_hir/src/definitions.rs index 6a1b9bdbb9491..0f77de9fb250f 100644 --- a/compiler/rustc_hir/src/definitions.rs +++ b/compiler/rustc_hir/src/definitions.rs @@ -5,13 +5,16 @@ //! expressions) that are mostly just leftovers. pub use crate::def_id::DefPathHash; -use crate::def_id::{CrateNum, DefId, DefIndex, LocalDefId, CRATE_DEF_INDEX, LOCAL_CRATE}; +use crate::def_id::{ + CrateNum, DefId, DefIndex, LocalDefId, StableCrateId, CRATE_DEF_INDEX, LOCAL_CRATE, +}; use crate::hir; -use rustc_ast::crate_disambiguator::CrateDisambiguator; use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::stable_hasher::StableHasher; +use rustc_data_structures::unhash::UnhashMap; use rustc_index::vec::IndexVec; +use rustc_span::crate_disambiguator::CrateDisambiguator; use rustc_span::hygiene::ExpnId; use rustc_span::symbol::{kw, sym, Symbol}; @@ -27,6 +30,7 @@ use tracing::debug; pub struct DefPathTable { index_to_key: IndexVec, def_path_hashes: IndexVec, + def_path_hash_to_index: UnhashMap, } impl DefPathTable { @@ -39,6 +43,35 @@ impl DefPathTable { }; self.def_path_hashes.push(def_path_hash); debug_assert!(self.def_path_hashes.len() == self.index_to_key.len()); + + // Check for hash collisions of DefPathHashes. These should be + // exceedingly rare. + if let Some(existing) = self.def_path_hash_to_index.insert(def_path_hash, index) { + let def_path1 = DefPath::make(LOCAL_CRATE, existing, |idx| self.def_key(idx)); + let def_path2 = DefPath::make(LOCAL_CRATE, index, |idx| self.def_key(idx)); + + // Continuing with colliding DefPathHashes can lead to correctness + // issues. We must abort compilation. + // + // The likelyhood of such a collision is very small, so actually + // running into one could be indicative of a poor hash function + // being used. + // + // See the documentation for DefPathHash for more information. + panic!( + "found DefPathHash collsion between {:?} and {:?}. \ + Compilation cannot continue.", + def_path1, def_path2 + ); + } + + // Assert that all DefPathHashes correctly contain the local crate's + // StableCrateId + #[cfg(debug_assertions)] + if let Some(root) = self.def_path_hashes.get(CRATE_DEF_INDEX) { + assert!(def_path_hash.stable_crate_id() == root.stable_crate_id()); + } + index } @@ -54,6 +87,7 @@ impl DefPathTable { hash } + /// Used by librustdoc for fake DefIds. pub fn num_def_ids(&self) -> usize { self.index_to_key.len() } @@ -108,13 +142,10 @@ pub struct DefKey { } impl DefKey { - fn compute_stable_hash(&self, parent_hash: DefPathHash) -> DefPathHash { + pub(crate) fn compute_stable_hash(&self, parent: DefPathHash) -> DefPathHash { let mut hasher = StableHasher::new(); - // We hash a `0u8` here to disambiguate between regular `DefPath` hashes, - // and the special "root_parent" below. - 0u8.hash(&mut hasher); - parent_hash.hash(&mut hasher); + parent.hash(&mut hasher); let DisambiguatedDefPathData { ref data, disambiguator } = self.disambiguated_data; @@ -127,19 +158,13 @@ impl DefKey { disambiguator.hash(&mut hasher); - DefPathHash(hasher.finish()) - } + let local_hash: u64 = hasher.finish(); - fn root_parent_stable_hash( - crate_name: &str, - crate_disambiguator: CrateDisambiguator, - ) -> DefPathHash { - let mut hasher = StableHasher::new(); - // Disambiguate this from a regular `DefPath` hash; see `compute_stable_hash()` above. - 1u8.hash(&mut hasher); - crate_name.hash(&mut hasher); - crate_disambiguator.hash(&mut hasher); - DefPathHash(hasher.finish()) + // Construct the new DefPathHash, making sure that the `crate_id` + // portion of the hash is properly copied from the parent. This way the + // `crate_id` part will be recursively propagated from the root to all + // DefPathHashes in this DefPathTable. + DefPathHash::new(parent.stable_crate_id(), local_hash) } } @@ -312,11 +337,6 @@ impl Definitions { self.def_id_to_hir_id[id].unwrap() } - #[inline] - pub fn opt_local_def_id_to_hir_id(&self, id: LocalDefId) -> Option { - self.def_id_to_hir_id[id] - } - #[inline] pub fn opt_hir_id_to_local_def_id(&self, hir_id: hir::HirId) -> Option { self.hir_id_to_def_id.get(&hir_id).copied() @@ -332,7 +352,8 @@ impl Definitions { }, }; - let parent_hash = DefKey::root_parent_stable_hash(crate_name, crate_disambiguator); + let stable_crate_id = StableCrateId::new(crate_name, crate_disambiguator); + let parent_hash = DefPathHash::new(stable_crate_id, 0); let def_path_hash = key.compute_stable_hash(parent_hash); // Create the root definition. diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs index 3cc501e423c0b..685429863faff 100644 --- a/compiler/rustc_hir/src/hir.rs +++ b/compiler/rustc_hir/src/hir.rs @@ -1,12 +1,12 @@ // ignore-tidy-filelength -use crate::def::{CtorKind, DefKind, Namespace, Res}; +use crate::def::{CtorKind, DefKind, Res}; use crate::def_id::DefId; crate use crate::hir_id::HirId; use crate::{itemlikevisit, LangItem}; use rustc_ast::util::parser::ExprPrecedence; use rustc_ast::{self as ast, CrateSugar, LlvmAsmDialect}; -use rustc_ast::{AttrVec, Attribute, FloatTy, IntTy, Label, LitKind, StrStyle, UintTy}; +use rustc_ast::{Attribute, FloatTy, IntTy, Label, LitKind, StrStyle, TraitObjectSyntax, UintTy}; pub use rustc_ast::{BorrowKind, ImplPolarity, IsAuto}; pub use rustc_ast::{CaptureBy, Movability, Mutability}; use rustc_ast::{InlineAsmOptions, InlineAsmTemplatePiece}; @@ -402,7 +402,7 @@ pub enum TraitBoundModifier { /// `typeck::collect::compute_bounds` matches these against /// the "special" built-in traits (see `middle::lang_items`) and /// detects `Copy`, `Send` and `Sync`. -#[derive(Debug, HashStable_Generic)] +#[derive(Clone, Debug, HashStable_Generic)] pub enum GenericBound<'hir> { Trait(PolyTraitRef<'hir>, TraitBoundModifier), // FIXME(davidtwco): Introduce `PolyTraitRef::LangItem` @@ -469,7 +469,6 @@ pub enum GenericParamKind<'hir> { pub struct GenericParam<'hir> { pub hir_id: HirId, pub name: ParamName, - pub attrs: &'hir [Attribute], pub bounds: GenericBounds<'hir>, pub span: Span, pub pure_wrt_drop: bool, @@ -626,14 +625,6 @@ pub struct ModuleItems { pub foreign_items: BTreeSet, } -/// A type representing only the top-level module. -#[derive(Encodable, Debug, HashStable_Generic)] -pub struct CrateItem<'hir> { - pub module: Mod<'hir>, - pub attrs: &'hir [Attribute], - pub span: Span, -} - /// The top-level data structure that stores the entire contents of /// the crate currently being compiled. /// @@ -642,7 +633,7 @@ pub struct CrateItem<'hir> { /// [rustc dev guide]: https://rustc-dev-guide.rust-lang.org/hir.html #[derive(Debug)] pub struct Crate<'hir> { - pub item: CrateItem<'hir>, + pub item: Mod<'hir>, pub exported_macros: &'hir [MacroDef<'hir>], // Attributes from non-exported macros, kept only for collecting the library feature list. pub non_exported_macro_attrs: &'hir [Attribute], @@ -675,6 +666,9 @@ pub struct Crate<'hir> { pub proc_macros: Vec, pub trait_map: BTreeMap>, + + /// Collected attributes from HIR nodes. + pub attrs: BTreeMap, } impl Crate<'hir> { @@ -766,7 +760,6 @@ impl Crate<'_> { pub struct MacroDef<'hir> { pub ident: Ident, pub vis: Visibility<'hir>, - pub attrs: &'hir [Attribute], pub def_id: LocalDefId, pub span: Span, pub ast: ast::MacroDef, @@ -882,7 +875,7 @@ impl<'hir> Pat<'hir> { /// are treated the same as` x: x, y: ref y, z: ref mut z`, /// except `is_shorthand` is true. #[derive(Debug, HashStable_Generic)] -pub struct FieldPat<'hir> { +pub struct PatField<'hir> { #[stable_hasher(ignore)] pub hir_id: HirId, /// The identifier for the field. @@ -946,7 +939,7 @@ pub enum PatKind<'hir> { /// A struct or struct variant pattern (e.g., `Variant {x, y, ..}`). /// The `bool` is `true` in the presence of a `..`. - Struct(QPath<'hir>, &'hir [FieldPat<'hir>], bool), + Struct(QPath<'hir>, &'hir [PatField<'hir>], bool), /// A tuple struct/variant pattern `Variant(x, y, .., z)`. /// If the `..` pattern fragment is present, then `Option` denotes its position. @@ -1166,16 +1159,6 @@ pub enum StmtKind<'hir> { Semi(&'hir Expr<'hir>), } -impl<'hir> StmtKind<'hir> { - pub fn attrs(&self, get_item: impl FnOnce(ItemId) -> &'hir Item<'hir>) -> &'hir [Attribute] { - match *self { - StmtKind::Local(ref l) => &l.attrs, - StmtKind::Item(ref item_id) => &get_item(*item_id).attrs, - StmtKind::Expr(ref e) | StmtKind::Semi(ref e) => &e.attrs, - } - } -} - /// Represents a `let` statement (i.e., `let : = ;`). #[derive(Debug, HashStable_Generic)] pub struct Local<'hir> { @@ -1186,7 +1169,6 @@ pub struct Local<'hir> { pub init: Option<&'hir Expr<'hir>>, pub hir_id: HirId, pub span: Span, - pub attrs: AttrVec, /// Can be `ForLoopDesugar` if the `let` statement is part of a `for` loop /// desugaring. Otherwise will be `Normal`. pub source: LocalSource, @@ -1199,7 +1181,6 @@ pub struct Arm<'hir> { #[stable_hasher(ignore)] pub hir_id: HirId, pub span: Span, - pub attrs: &'hir [Attribute], /// If this pattern and the optional guard matches, then `body` is evaluated. pub pat: &'hir Pat<'hir>, /// Optional guard clause. @@ -1215,7 +1196,7 @@ pub enum Guard<'hir> { } #[derive(Debug, HashStable_Generic)] -pub struct Field<'hir> { +pub struct ExprField<'hir> { #[stable_hasher(ignore)] pub hir_id: HirId, pub ident: Ident, @@ -1458,7 +1439,6 @@ pub struct AnonConst { pub struct Expr<'hir> { pub hir_id: HirId, pub kind: ExprKind<'hir>, - pub attrs: AttrVec, pub span: Span, } @@ -1775,7 +1755,7 @@ pub enum ExprKind<'hir> { /// /// E.g., `Foo {x: 1, y: 2}`, or `Foo {x: 1, .. base}`, /// where `base` is the `Option`. - Struct(&'hir QPath<'hir>, &'hir [Field<'hir>], Option<&'hir Expr<'hir>>), + Struct(&'hir QPath<'hir>, &'hir [ExprField<'hir>], Option<&'hir Expr<'hir>>), /// An array literal constructed from one repeated element. /// @@ -1822,7 +1802,7 @@ impl<'hir> QPath<'hir> { pub fn span(&self) -> Span { match *self { QPath::Resolved(_, path) => path.span, - QPath::TypeRelative(_, ps) => ps.ident.span, + QPath::TypeRelative(qself, ps) => qself.span.to(ps.ident.span), QPath::LangItem(_, span) => span, } } @@ -2040,7 +2020,6 @@ impl TraitItemId { pub struct TraitItem<'hir> { pub ident: Ident, pub def_id: LocalDefId, - pub attrs: &'hir [Attribute], pub generics: Generics<'hir>, pub kind: TraitItemKind<'hir>, pub span: Span, @@ -2103,7 +2082,6 @@ pub struct ImplItem<'hir> { pub def_id: LocalDefId, pub vis: Visibility<'hir>, pub defaultness: Defaultness, - pub attrs: &'hir [Attribute], pub generics: Generics<'hir>, pub kind: ImplItemKind<'hir>, pub span: Span, @@ -2133,15 +2111,6 @@ pub enum ImplItemKind<'hir> { TyAlias(&'hir Ty<'hir>), } -impl ImplItemKind<'_> { - pub fn namespace(&self) -> Namespace { - match self { - ImplItemKind::TyAlias(..) => Namespace::TypeNS, - ImplItemKind::Const(..) | ImplItemKind::Fn(..) => Namespace::ValueNS, - } - } -} - // The name of the associated type for `Fn` return types. pub const FN_OUTPUT_NAME: Symbol = sym::Output; @@ -2230,6 +2199,9 @@ impl PrimTy { Self::Str, ]; + /// Like [`PrimTy::name`], but returns a &str instead of a symbol. + /// + /// Used by clippy. pub fn name_str(self) -> &'static str { match self { PrimTy::Int(i) => i.name_str(), @@ -2306,7 +2278,9 @@ pub enum OpaqueTyOrigin { AsyncFn, /// `let _: impl Trait = ...` Binding, - /// Impl trait in type aliases, consts, statics, bounds. + /// type aliases: `type Foo = impl Trait;` + TyAlias, + /// Impl trait consts, statics, bounds. Misc, } @@ -2340,7 +2314,7 @@ pub enum TyKind<'hir> { OpaqueDef(ItemId, &'hir [GenericArg<'hir>]), /// A trait object type `Bound1 + Bound2 + Bound3` /// where `Bound` is a trait or a lifetime. - TraitObject(&'hir [PolyTraitRef<'hir>], Lifetime), + TraitObject(&'hir [PolyTraitRef<'hir>], Lifetime, TraitObjectSyntax), /// Unused for now. Typeof(AnonConst), /// `TyKind::Infer` means the type should be inferred instead of it having been @@ -2373,7 +2347,7 @@ pub enum InlineAsmOperand<'hir> { out_expr: Option>, }, Const { - expr: Expr<'hir>, + anon_const: AnonConst, }, Sym { expr: Expr<'hir>, @@ -2433,7 +2407,6 @@ pub struct LlvmInlineAsm<'hir> { /// Represents a parameter in a function header. #[derive(Debug, HashStable_Generic)] pub struct Param<'hir> { - pub attrs: &'hir [Attribute], pub hir_id: HirId, pub pat: &'hir Pat<'hir>, pub ty_span: Span, @@ -2551,8 +2524,6 @@ pub struct Variant<'hir> { /// Name of the variant. #[stable_hasher(project(name))] pub ident: Ident, - /// Attributes of the variant. - pub attrs: &'hir [Attribute], /// Id of the variant (not the constructor, see `VariantData::ctor_hir_id()`). pub id: HirId, /// Fields and constructor id of the variant. @@ -2585,7 +2556,7 @@ pub enum UseKind { /// that the `ref_id` is for. Note that `ref_id`'s value is not the `HirId` of the /// trait being referred to but just a unique `HirId` that serves as a key /// within the resolution map. -#[derive(Debug, HashStable_Generic)] +#[derive(Clone, Debug, HashStable_Generic)] pub struct TraitRef<'hir> { pub path: &'hir Path<'hir>, // Don't hash the `ref_id`. It is tracked via the thing it is used to access. @@ -2604,7 +2575,7 @@ impl TraitRef<'_> { } } -#[derive(Debug, HashStable_Generic)] +#[derive(Clone, Debug, HashStable_Generic)] pub struct PolyTraitRef<'hir> { /// The `'a` in `for<'a> Foo<&'a T>`. pub bound_generic_params: &'hir [GenericParam<'hir>], @@ -2639,17 +2610,16 @@ impl VisibilityKind<'_> { } #[derive(Debug, HashStable_Generic)] -pub struct StructField<'hir> { +pub struct FieldDef<'hir> { pub span: Span, #[stable_hasher(project(name))] pub ident: Ident, pub vis: Visibility<'hir>, pub hir_id: HirId, pub ty: &'hir Ty<'hir>, - pub attrs: &'hir [Attribute], } -impl StructField<'_> { +impl FieldDef<'_> { // Still necessary in couple of places pub fn is_positional(&self) -> bool { let first = self.ident.as_str().as_bytes()[0]; @@ -2663,11 +2633,11 @@ pub enum VariantData<'hir> { /// A struct variant. /// /// E.g., `Bar { .. }` as in `enum Foo { Bar { .. } }`. - Struct(&'hir [StructField<'hir>], /* recovered */ bool), + Struct(&'hir [FieldDef<'hir>], /* recovered */ bool), /// A tuple variant. /// /// E.g., `Bar(..)` as in `enum Foo { Bar(..) }`. - Tuple(&'hir [StructField<'hir>], HirId), + Tuple(&'hir [FieldDef<'hir>], HirId), /// A unit variant. /// /// E.g., `Bar = ..` as in `enum Foo { Bar = .. }`. @@ -2676,7 +2646,7 @@ pub enum VariantData<'hir> { impl VariantData<'hir> { /// Return the fields of this variant. - pub fn fields(&self) -> &'hir [StructField<'hir>] { + pub fn fields(&self) -> &'hir [FieldDef<'hir>] { match *self { VariantData::Struct(ref fields, ..) | VariantData::Tuple(ref fields, ..) => fields, _ => &[], @@ -2715,7 +2685,6 @@ impl ItemId { pub struct Item<'hir> { pub ident: Ident, pub def_id: LocalDefId, - pub attrs: &'hir [Attribute], pub kind: ItemKind<'hir>, pub vis: Visibility<'hir>, pub span: Span, @@ -2932,7 +2901,6 @@ pub struct ForeignItemRef<'hir> { #[derive(Debug)] pub struct ForeignItem<'hir> { pub ident: Ident, - pub attrs: &'hir [Attribute], pub kind: ForeignItemKind<'hir>, pub def_id: LocalDefId, pub span: Span, @@ -2986,7 +2954,7 @@ pub enum Node<'hir> { TraitItem(&'hir TraitItem<'hir>), ImplItem(&'hir ImplItem<'hir>), Variant(&'hir Variant<'hir>), - Field(&'hir StructField<'hir>), + Field(&'hir FieldDef<'hir>), AnonConst(&'hir AnonConst), Expr(&'hir Expr<'hir>), Stmt(&'hir Stmt<'hir>), @@ -3008,7 +2976,7 @@ pub enum Node<'hir> { GenericParam(&'hir GenericParam<'hir>), Visibility(&'hir Visibility<'hir>), - Crate(&'hir CrateItem<'hir>), + Crate(&'hir Mod<'hir>), } impl<'hir> Node<'hir> { @@ -3017,7 +2985,7 @@ impl<'hir> Node<'hir> { Node::TraitItem(TraitItem { ident, .. }) | Node::ImplItem(ImplItem { ident, .. }) | Node::ForeignItem(ForeignItem { ident, .. }) - | Node::Field(StructField { ident, .. }) + | Node::Field(FieldDef { ident, .. }) | Node::Variant(Variant { ident, .. }) | Node::MacroDef(MacroDef { ident, .. }) | Node::Item(Item { ident, .. }) => Some(*ident), @@ -3065,7 +3033,7 @@ impl<'hir> Node<'hir> { | Node::ImplItem(ImplItem { def_id, .. }) | Node::ForeignItem(ForeignItem { def_id, .. }) | Node::MacroDef(MacroDef { def_id, .. }) => Some(HirId::make_owner(*def_id)), - Node::Field(StructField { hir_id, .. }) + Node::Field(FieldDef { hir_id, .. }) | Node::AnonConst(AnonConst { hir_id, .. }) | Node::Expr(Expr { hir_id, .. }) | Node::Stmt(Stmt { hir_id, .. }) @@ -3088,16 +3056,16 @@ impl<'hir> Node<'hir> { } // Some nodes are used a lot. Make sure they don't unintentionally get bigger. -#[cfg(target_arch = "x86_64")] +#[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))] mod size_asserts { rustc_data_structures::static_assert_size!(super::Block<'static>, 48); - rustc_data_structures::static_assert_size!(super::Expr<'static>, 72); + rustc_data_structures::static_assert_size!(super::Expr<'static>, 64); rustc_data_structures::static_assert_size!(super::Pat<'static>, 88); rustc_data_structures::static_assert_size!(super::QPath<'static>, 24); rustc_data_structures::static_assert_size!(super::Ty<'static>, 72); - rustc_data_structures::static_assert_size!(super::Item<'static>, 200); - rustc_data_structures::static_assert_size!(super::TraitItem<'static>, 144); - rustc_data_structures::static_assert_size!(super::ImplItem<'static>, 168); - rustc_data_structures::static_assert_size!(super::ForeignItem<'static>, 152); + rustc_data_structures::static_assert_size!(super::Item<'static>, 184); + rustc_data_structures::static_assert_size!(super::TraitItem<'static>, 128); + rustc_data_structures::static_assert_size!(super::ImplItem<'static>, 152); + rustc_data_structures::static_assert_size!(super::ForeignItem<'static>, 136); } diff --git a/compiler/rustc_hir/src/hir_id.rs b/compiler/rustc_hir/src/hir_id.rs index e24eb5e449002..0b25ebc27bd3f 100644 --- a/compiler/rustc_hir/src/hir_id.rs +++ b/compiler/rustc_hir/src/hir_id.rs @@ -1,4 +1,5 @@ use crate::def_id::{LocalDefId, CRATE_DEF_INDEX}; +use rustc_index::vec::IndexVec; use std::fmt; /// Uniquely identifies a node in the HIR of the current crate. It is @@ -61,3 +62,70 @@ pub const CRATE_HIR_ID: HirId = HirId { owner: LocalDefId { local_def_index: CRATE_DEF_INDEX }, local_id: ItemLocalId::from_u32(0), }; + +/// N.B. This collection is currently unused, but will be used by #72015 and future PRs. +#[derive(Clone, Default, Debug, Encodable, Decodable)] +pub struct HirIdVec { + map: IndexVec>, +} + +impl HirIdVec { + pub fn push_owner(&mut self, id: LocalDefId) { + self.map.ensure_contains_elem(id, IndexVec::new); + } + + pub fn push(&mut self, id: HirId, value: T) { + if id.local_id == ItemLocalId::from_u32(0) { + self.push_owner(id.owner); + } + let submap = &mut self.map[id.owner]; + let _ret_id = submap.push(value); + debug_assert_eq!(_ret_id, id.local_id); + } + + pub fn push_sparse(&mut self, id: HirId, value: T) + where + T: Default, + { + self.map.ensure_contains_elem(id.owner, IndexVec::new); + let submap = &mut self.map[id.owner]; + let i = id.local_id.index(); + let len = submap.len(); + if i >= len { + submap.extend(std::iter::repeat_with(T::default).take(i - len + 1)); + } + submap[id.local_id] = value; + } + + pub fn get(&self, id: HirId) -> Option<&T> { + self.map.get(id.owner)?.get(id.local_id) + } + + pub fn get_owner(&self, id: LocalDefId) -> &IndexVec { + &self.map[id] + } + + pub fn iter(&self) -> impl Iterator { + self.map.iter().flat_map(|la| la.iter()) + } + + pub fn iter_enumerated(&self) -> impl Iterator { + self.map.iter_enumerated().flat_map(|(owner, la)| { + la.iter_enumerated().map(move |(local_id, attr)| (HirId { owner, local_id }, attr)) + }) + } +} + +impl std::ops::Index for HirIdVec { + type Output = T; + + fn index(&self, id: HirId) -> &T { + &self.map[id.owner][id.local_id] + } +} + +impl std::ops::IndexMut for HirIdVec { + fn index_mut(&mut self, id: HirId) -> &mut T { + &mut self.map[id.owner][id.local_id] + } +} diff --git a/compiler/rustc_hir/src/intravisit.rs b/compiler/rustc_hir/src/intravisit.rs index 6a2719c2d6677..0ce04a77a5056 100644 --- a/compiler/rustc_hir/src/intravisit.rs +++ b/compiler/rustc_hir/src/intravisit.rs @@ -98,32 +98,24 @@ where } } -#[derive(Copy, Clone)] +#[derive(Copy, Clone, Debug)] pub enum FnKind<'a> { /// `#[xxx] pub async/const/extern "Abi" fn foo()` - ItemFn(Ident, &'a Generics<'a>, FnHeader, &'a Visibility<'a>, &'a [Attribute]), + ItemFn(Ident, &'a Generics<'a>, FnHeader, &'a Visibility<'a>), /// `fn foo(&self)` - Method(Ident, &'a FnSig<'a>, Option<&'a Visibility<'a>>, &'a [Attribute]), + Method(Ident, &'a FnSig<'a>, Option<&'a Visibility<'a>>), /// `|x, y| {}` - Closure(&'a [Attribute]), + Closure, } impl<'a> FnKind<'a> { - pub fn attrs(&self) -> &'a [Attribute] { - match *self { - FnKind::ItemFn(.., attrs) => attrs, - FnKind::Method(.., attrs) => attrs, - FnKind::Closure(attrs) => attrs, - } - } - pub fn header(&self) -> Option<&FnHeader> { match *self { - FnKind::ItemFn(_, _, ref header, _, _) => Some(header), - FnKind::Method(_, ref sig, _, _) => Some(&sig.header), - FnKind::Closure(_) => None, + FnKind::ItemFn(_, _, ref header, _) => Some(header), + FnKind::Method(_, ref sig, _) => Some(&sig.header), + FnKind::Closure => None, } } } @@ -374,6 +366,9 @@ pub trait Visitor<'v>: Sized { fn visit_generic_param(&mut self, p: &'v GenericParam<'v>) { walk_generic_param(self, p) } + fn visit_const_param_default(&mut self, _param: HirId, ct: &'v AnonConst) { + walk_const_param_default(self, ct) + } fn visit_generics(&mut self, g: &'v Generics<'v>) { walk_generics(self, g) } @@ -423,8 +418,8 @@ pub trait Visitor<'v>: Sized { ) { walk_struct_def(self, s) } - fn visit_struct_field(&mut self, s: &'v StructField<'v>) { - walk_struct_field(self, s) + fn visit_field_def(&mut self, s: &'v FieldDef<'v>) { + walk_field_def(self, s) } fn visit_enum_def( &mut self, @@ -466,7 +461,7 @@ pub trait Visitor<'v>: Sized { fn visit_assoc_type_binding(&mut self, type_binding: &'v TypeBinding<'v>) { walk_assoc_type_binding(self, type_binding) } - fn visit_attribute(&mut self, _attr: &'v Attribute) {} + fn visit_attribute(&mut self, _id: HirId, _attr: &'v Attribute) {} fn visit_macro_def(&mut self, macro_def: &'v MacroDef<'v>) { walk_macro_def(self, macro_def) } @@ -483,15 +478,18 @@ pub trait Visitor<'v>: Sized { /// Walks the contents of a crate. See also `Crate::visit_all_items`. pub fn walk_crate<'v, V: Visitor<'v>>(visitor: &mut V, krate: &'v Crate<'v>) { - visitor.visit_mod(&krate.item.module, krate.item.span, CRATE_HIR_ID); - walk_list!(visitor, visit_attribute, krate.item.attrs); + visitor.visit_mod(&krate.item, krate.item.inner, CRATE_HIR_ID); walk_list!(visitor, visit_macro_def, krate.exported_macros); + for (&id, attrs) in krate.attrs.iter() { + for a in *attrs { + visitor.visit_attribute(id, a) + } + } } pub fn walk_macro_def<'v, V: Visitor<'v>>(visitor: &mut V, macro_def: &'v MacroDef<'v>) { visitor.visit_id(macro_def.hir_id()); visitor.visit_ident(macro_def.ident); - walk_list!(visitor, visit_attribute, macro_def.attrs); } pub fn walk_mod<'v, V: Visitor<'v>>(visitor: &mut V, module: &'v Mod<'v>, mod_hir_id: HirId) { @@ -510,7 +508,6 @@ pub fn walk_local<'v, V: Visitor<'v>>(visitor: &mut V, local: &'v Local<'v>) { // Intentionally visiting the expr first - the initialization expr // dominates the local's definition. walk_list!(visitor, visit_expr, &local.init); - walk_list!(visitor, visit_attribute, local.attrs.iter()); visitor.visit_id(local.hir_id); visitor.visit_pat(&local.pat); walk_list!(visitor, visit_ty, &local.ty); @@ -557,7 +554,6 @@ pub fn walk_trait_ref<'v, V: Visitor<'v>>(visitor: &mut V, trait_ref: &'v TraitR pub fn walk_param<'v, V: Visitor<'v>>(visitor: &mut V, param: &'v Param<'v>) { visitor.visit_id(param.hir_id); visitor.visit_pat(¶m.pat); - walk_list!(visitor, visit_attribute, param.attrs); } pub fn walk_item<'v, V: Visitor<'v>>(visitor: &mut V, item: &'v Item<'v>) { @@ -579,7 +575,7 @@ pub fn walk_item<'v, V: Visitor<'v>>(visitor: &mut V, item: &'v Item<'v>) { visitor.visit_nested_body(body); } ItemKind::Fn(ref sig, ref generics, body_id) => visitor.visit_fn( - FnKind::ItemFn(item.ident, generics, sig.header, &item.vis, &item.attrs), + FnKind::ItemFn(item.ident, generics, sig.header, &item.vis), &sig.decl, body_id, item.span, @@ -652,7 +648,6 @@ pub fn walk_item<'v, V: Visitor<'v>>(visitor: &mut V, item: &'v Item<'v>) { walk_list!(visitor, visit_param_bound, bounds); } } - walk_list!(visitor, visit_attribute, item.attrs); } pub fn walk_use<'v, V: Visitor<'v>>(visitor: &mut V, path: &'v Path<'v>, hir_id: HirId) { @@ -686,7 +681,6 @@ pub fn walk_variant<'v, V: Visitor<'v>>( variant.span, ); walk_list!(visitor, visit_anon_const, &variant.disr_expr); - walk_list!(visitor, visit_attribute, variant.attrs); } pub fn walk_ty<'v, V: Visitor<'v>>(visitor: &mut V, typ: &'v Ty<'v>) { @@ -718,7 +712,7 @@ pub fn walk_ty<'v, V: Visitor<'v>>(visitor: &mut V, typ: &'v Ty<'v>) { visitor.visit_ty(ty); visitor.visit_anon_const(length) } - TyKind::TraitObject(bounds, ref lifetime) => { + TyKind::TraitObject(bounds, ref lifetime, _syntax) => { for bound in bounds { visitor.visit_poly_trait_ref(bound, TraitBoundModifier::None); } @@ -851,8 +845,6 @@ pub fn walk_foreign_item<'v, V: Visitor<'v>>(visitor: &mut V, foreign_item: &'v ForeignItemKind::Static(ref typ, _) => visitor.visit_ty(typ), ForeignItemKind::Type => (), } - - walk_list!(visitor, visit_attribute, foreign_item.attrs); } pub fn walk_param_bound<'v, V: Visitor<'v>>(visitor: &mut V, bound: &'v GenericBound<'v>) { @@ -870,7 +862,6 @@ pub fn walk_param_bound<'v, V: Visitor<'v>>(visitor: &mut V, bound: &'v GenericB pub fn walk_generic_param<'v, V: Visitor<'v>>(visitor: &mut V, param: &'v GenericParam<'v>) { visitor.visit_id(param.hir_id); - walk_list!(visitor, visit_attribute, param.attrs); match param.name { ParamName::Plain(ident) => visitor.visit_ident(ident), ParamName::Error | ParamName::Fresh(_) => {} @@ -881,13 +872,17 @@ pub fn walk_generic_param<'v, V: Visitor<'v>>(visitor: &mut V, param: &'v Generi GenericParamKind::Const { ref ty, ref default } => { visitor.visit_ty(ty); if let Some(ref default) = default { - visitor.visit_anon_const(default); + visitor.visit_const_param_default(param.hir_id, default); } } } walk_list!(visitor, visit_param_bound, param.bounds); } +pub fn walk_const_param_default<'v, V: Visitor<'v>>(visitor: &mut V, ct: &'v AnonConst) { + visitor.visit_anon_const(ct) +} + pub fn walk_generics<'v, V: Visitor<'v>>(visitor: &mut V, generics: &'v Generics<'v>) { walk_list!(visitor, visit_generic_param, generics.params); walk_list!(visitor, visit_where_predicate, generics.where_clause.predicates); @@ -940,7 +935,7 @@ pub fn walk_fn_kind<'v, V: Visitor<'v>>(visitor: &mut V, function_kind: FnKind<' FnKind::ItemFn(_, generics, ..) => { visitor.visit_generics(generics); } - FnKind::Method(..) | FnKind::Closure(_) => {} + FnKind::Method(..) | FnKind::Closure => {} } } @@ -960,7 +955,6 @@ pub fn walk_fn<'v, V: Visitor<'v>>( pub fn walk_trait_item<'v, V: Visitor<'v>>(visitor: &mut V, trait_item: &'v TraitItem<'v>) { visitor.visit_ident(trait_item.ident); - walk_list!(visitor, visit_attribute, trait_item.attrs); visitor.visit_generics(&trait_item.generics); match trait_item.kind { TraitItemKind::Const(ref ty, default) => { @@ -977,7 +971,7 @@ pub fn walk_trait_item<'v, V: Visitor<'v>>(visitor: &mut V, trait_item: &'v Trai } TraitItemKind::Fn(ref sig, TraitFn::Provided(body_id)) => { visitor.visit_fn( - FnKind::Method(trait_item.ident, sig, None, &trait_item.attrs), + FnKind::Method(trait_item.ident, sig, None), &sig.decl, body_id, trait_item.span, @@ -1003,21 +997,12 @@ pub fn walk_trait_item_ref<'v, V: Visitor<'v>>(visitor: &mut V, trait_item_ref: pub fn walk_impl_item<'v, V: Visitor<'v>>(visitor: &mut V, impl_item: &'v ImplItem<'v>) { // N.B., deliberately force a compilation error if/when new fields are added. - let ImplItem { - def_id: _, - ident, - ref vis, - ref defaultness, - attrs, - ref generics, - ref kind, - span: _, - } = *impl_item; + let ImplItem { def_id: _, ident, ref vis, ref defaultness, ref generics, ref kind, span: _ } = + *impl_item; visitor.visit_ident(ident); visitor.visit_vis(vis); visitor.visit_defaultness(defaultness); - walk_list!(visitor, visit_attribute, attrs); visitor.visit_generics(generics); match *kind { ImplItemKind::Const(ref ty, body) => { @@ -1027,7 +1012,7 @@ pub fn walk_impl_item<'v, V: Visitor<'v>>(visitor: &mut V, impl_item: &'v ImplIt } ImplItemKind::Fn(ref sig, body_id) => { visitor.visit_fn( - FnKind::Method(impl_item.ident, sig, Some(&impl_item.vis), &impl_item.attrs), + FnKind::Method(impl_item.ident, sig, Some(&impl_item.vis)), &sig.decl, body_id, impl_item.span, @@ -1067,15 +1052,14 @@ pub fn walk_struct_def<'v, V: Visitor<'v>>( struct_definition: &'v VariantData<'v>, ) { walk_list!(visitor, visit_id, struct_definition.ctor_hir_id()); - walk_list!(visitor, visit_struct_field, struct_definition.fields()); + walk_list!(visitor, visit_field_def, struct_definition.fields()); } -pub fn walk_struct_field<'v, V: Visitor<'v>>(visitor: &mut V, struct_field: &'v StructField<'v>) { - visitor.visit_id(struct_field.hir_id); - visitor.visit_vis(&struct_field.vis); - visitor.visit_ident(struct_field.ident); - visitor.visit_ty(&struct_field.ty); - walk_list!(visitor, visit_attribute, struct_field.attrs); +pub fn walk_field_def<'v, V: Visitor<'v>>(visitor: &mut V, field: &'v FieldDef<'v>) { + visitor.visit_id(field.hir_id); + visitor.visit_vis(&field.vis); + visitor.visit_ident(field.ident); + visitor.visit_ty(&field.ty); } pub fn walk_block<'v, V: Visitor<'v>>(visitor: &mut V, block: &'v Block<'v>) { @@ -1102,7 +1086,6 @@ pub fn walk_anon_const<'v, V: Visitor<'v>>(visitor: &mut V, constant: &'v AnonCo pub fn walk_expr<'v, V: Visitor<'v>>(visitor: &mut V, expression: &'v Expr<'v>) { visitor.visit_id(expression.hir_id); - walk_list!(visitor, visit_attribute, expression.attrs.iter()); match expression.kind { ExprKind::Box(ref subexpression) => visitor.visit_expr(subexpression), ExprKind::Array(subexpressions) => { @@ -1162,7 +1145,7 @@ pub fn walk_expr<'v, V: Visitor<'v>>(visitor: &mut V, expression: &'v Expr<'v>) } ExprKind::Closure(_, ref function_declaration, body, _fn_decl_span, _gen) => visitor .visit_fn( - FnKind::Closure(&expression.attrs), + FnKind::Closure, function_declaration, body, expression.span, @@ -1206,7 +1189,6 @@ pub fn walk_expr<'v, V: Visitor<'v>>(visitor: &mut V, expression: &'v Expr<'v>) match op { InlineAsmOperand::In { expr, .. } | InlineAsmOperand::InOut { expr, .. } - | InlineAsmOperand::Const { expr, .. } | InlineAsmOperand::Sym { expr, .. } => visitor.visit_expr(expr), InlineAsmOperand::Out { expr, .. } => { if let Some(expr) = expr { @@ -1219,6 +1201,9 @@ pub fn walk_expr<'v, V: Visitor<'v>>(visitor: &mut V, expression: &'v Expr<'v>) visitor.visit_expr(out_expr); } } + InlineAsmOperand::Const { anon_const, .. } => { + visitor.visit_anon_const(anon_const) + } } } } @@ -1246,7 +1231,6 @@ pub fn walk_arm<'v, V: Visitor<'v>>(visitor: &mut V, arm: &'v Arm<'v>) { } } visitor.visit_expr(&arm.body); - walk_list!(visitor, visit_attribute, arm.attrs); } pub fn walk_vis<'v, V: Visitor<'v>>(visitor: &mut V, vis: &'v Visibility<'v>) { diff --git a/compiler/rustc_hir/src/lang_items.rs b/compiler/rustc_hir/src/lang_items.rs index 03524569ce7a9..498000db50f43 100644 --- a/compiler/rustc_hir/src/lang_items.rs +++ b/compiler/rustc_hir/src/lang_items.rs @@ -38,27 +38,34 @@ macro_rules! expand_group { // So you probably just want to nip down to the end. macro_rules! language_item_table { ( - $( $variant:ident $($group:expr)?, $name:expr, $method:ident, $target:expr; )* + $( $(#[$attr:meta])* $variant:ident $($group:expr)?, $module:ident :: $name:ident, $method:ident, $target:expr; )* ) => { enum_from_u32! { /// A representation of all the valid language items in Rust. #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, Encodable, Decodable)] pub enum LangItem { - $($variant,)* + $( + #[doc = concat!("The `", stringify!($name), "` lang item.")] + /// + $(#[$attr])* + $variant, + )* } } impl LangItem { /// Returns the `name` symbol in `#[lang = "$name"]`. - /// For example, `LangItem::EqTraitLangItem`, - /// that is `#[lang = "eq"]` would result in `sym::eq`. + /// For example, [`LangItem::PartialEq`]`.name()` + /// would result in [`sym::eq`] since it is `#[lang = "eq"]`. pub fn name(self) -> Symbol { match self { - $( LangItem::$variant => $name, )* + $( LangItem::$variant => $module::$name, )* } } + /// The [group](LangItemGroup) that this lang item belongs to, + /// or `None` if it doesn't belong to a group. pub fn group(self) -> Option { use LangItemGroup::*; match self { @@ -67,15 +74,17 @@ macro_rules! language_item_table { } } + /// All of the language items, defined or not. + /// Defined lang items can come from the current crate or its dependencies. #[derive(HashStable_Generic, Debug)] pub struct LanguageItems { - /// Mappings from lang items to their possibly found `DefId`s. - /// The index corresponds to the order in `LangItem`. + /// Mappings from lang items to their possibly found [`DefId`]s. + /// The index corresponds to the order in [`LangItem`]. pub items: Vec>, /// Lang items that were not found during collection. pub missing: Vec, - /// Mapping from `LangItemGroup` discriminants to all - /// `DefId`s of lang items in that group. + /// Mapping from [`LangItemGroup`] discriminants to all + /// [`DefId`]s of lang items in that group. pub groups: [Vec; NUM_GROUPS], } @@ -103,14 +112,13 @@ macro_rules! language_item_table { self.items[it as usize].ok_or_else(|| format!("requires `{}` lang_item", it.name())) } + /// Returns the [`DefId`]s of all lang items in a group. pub fn group(&self, group: LangItemGroup) -> &[DefId] { self.groups[group as usize].as_ref() } $( - /// Returns the corresponding `DefId` for the lang item if it - /// exists. - #[allow(dead_code)] + #[doc = concat!("Returns the [`DefId`] of the `", stringify!($name), "` lang item if it is defined.")] pub fn $method(&self) -> Option { self.items[LangItem::$variant as usize] } @@ -120,7 +128,7 @@ macro_rules! language_item_table { /// A mapping from the name of the lang item to its order and the form it must be of. pub static ITEM_REFS: SyncLazy> = SyncLazy::new(|| { let mut item_refs = FxHashMap::default(); - $( item_refs.insert($name, (LangItem::$variant as usize, $target)); )* + $( item_refs.insert($module::$name, (LangItem::$variant as usize, $target)); )* item_refs }); @@ -140,7 +148,7 @@ impl HashStable for LangItem { /// /// 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 `librustc_hir` to depend on `librustc_session`, so we +/// 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 @@ -190,15 +198,15 @@ language_item_table! { Sized, sym::sized, sized_trait, Target::Trait; Unsize, sym::unsize, unsize_trait, Target::Trait; - // Trait injected by #[derive(PartialEq)], (i.e. "Partial EQ"). + /// Trait injected by `#[derive(PartialEq)]`, (i.e. "Partial EQ"). StructuralPeq, sym::structural_peq, structural_peq_trait, Target::Trait; - // Trait injected by #[derive(Eq)], (i.e. "Total EQ"; no, I will not apologize). + /// Trait injected by `#[derive(Eq)]`, (i.e. "Total EQ"; no, I will not apologize). StructuralTeq, sym::structural_teq, structural_teq_trait, Target::Trait; Copy, sym::copy, copy_trait, Target::Trait; Clone, sym::clone, clone_trait, Target::Trait; Sync, sym::sync, sync_trait, Target::Trait; DiscriminantKind, sym::discriminant_kind, discriminant_kind_trait, Target::Trait; - // The associated item of `trait DiscriminantKind`. + /// The associated item of the [`DiscriminantKind`] trait. Discriminant, sym::discriminant_type, discriminant_type, Target::AssocTy; PointeeTrait, sym::pointee_trait, pointee_trait, Target::Trait; @@ -273,7 +281,7 @@ language_item_table! { PanicInfo, sym::panic_info, panic_info, Target::Struct; PanicLocation, sym::panic_location, panic_location, Target::Struct; PanicImpl, sym::panic_impl, panic_impl, Target::Fn; - // libstd panic entry point. Necessary for const eval to be able to catch it + /// libstd panic entry point. Necessary for const eval to be able to catch it BeginPanic, sym::begin_panic, begin_panic_fn, Target::Fn; ExchangeMalloc, sym::exchange_malloc, exchange_malloc_fn, Target::Fn; @@ -295,7 +303,7 @@ language_item_table! { MaybeUninit, sym::maybe_uninit, maybe_uninit, Target::Union; - // Align offset for stride != 1; must not panic. + /// Align offset for stride != 1; must not panic. AlignOffset, sym::align_offset, align_offset_fn, Target::Fn; Termination, sym::termination, termination, Target::Trait; diff --git a/compiler/rustc_hir/src/lib.rs b/compiler/rustc_hir/src/lib.rs index c69a9b063aeca..36a30900fb26c 100644 --- a/compiler/rustc_hir/src/lib.rs +++ b/compiler/rustc_hir/src/lib.rs @@ -5,9 +5,10 @@ #![feature(crate_visibility_modifier)] #![feature(const_fn)] // For the unsizing cast on `&[]` #![feature(const_panic)] +#![feature(extended_key_value_attributes)] #![feature(in_band_lifetimes)] #![feature(once_cell)] -#![feature(or_patterns)] +#![cfg_attr(bootstrap, feature(or_patterns))] #![recursion_limit = "256"] #[macro_use] @@ -30,6 +31,9 @@ mod stable_hash_impls; mod target; pub mod weak_lang_items; +#[cfg(test)] +mod tests; + pub use hir::*; pub use hir_id::*; pub use lang_items::{LangItem, LanguageItems}; diff --git a/compiler/rustc_hir/src/pat_util.rs b/compiler/rustc_hir/src/pat_util.rs index 9e0a6aae24272..b1f78a83e7407 100644 --- a/compiler/rustc_hir/src/pat_util.rs +++ b/compiler/rustc_hir/src/pat_util.rs @@ -1,6 +1,7 @@ use crate::def::{CtorOf, DefKind, Res}; use crate::def_id::DefId; use crate::hir::{self, HirId, PatKind}; +use rustc_data_structures::stable_set::FxHashSet; use rustc_span::symbol::Ident; use rustc_span::Span; @@ -89,26 +90,6 @@ impl hir::Pat<'_> { }) } - /// Checks if the pattern contains any patterns that bind something to - /// an ident, e.g., `foo`, or `Foo(foo)` or `foo @ Bar(..)`. - pub fn contains_bindings(&self) -> bool { - self.satisfies(|p| matches!(p.kind, PatKind::Binding(..))) - } - - /// Checks if the pattern satisfies the given predicate on some sub-pattern. - fn satisfies(&self, pred: impl Fn(&hir::Pat<'_>) -> bool) -> bool { - let mut satisfies = false; - self.walk_short(|p| { - if pred(p) { - satisfies = true; - false // Found one, can short circuit now. - } else { - true - } - }); - satisfies - } - pub fn simple_ident(&self) -> Option { match self.kind { PatKind::Binding( @@ -138,8 +119,10 @@ impl hir::Pat<'_> { } _ => true, }); - variants.sort(); - variants.dedup(); + // We remove duplicates by inserting into a `FxHashSet` to avoid re-ordering + // the bounds + let mut duplicates = FxHashSet::default(); + variants.retain(|def_id| duplicates.insert(*def_id)); variants } diff --git a/compiler/rustc_hir/src/stable_hash_impls.rs b/compiler/rustc_hir/src/stable_hash_impls.rs index 5fb4b8a58c210..0232654aaa524 100644 --- a/compiler/rustc_hir/src/stable_hash_impls.rs +++ b/compiler/rustc_hir/src/stable_hash_impls.rs @@ -9,7 +9,7 @@ use rustc_span::def_id::{DefPathHash, LocalDefId}; /// Requirements for a `StableHashingContext` to be used in this crate. /// This is a hack to allow using the `HashStable_Generic` derive macro -/// instead of implementing everything in librustc_middle. +/// instead of implementing everything in `rustc_middle`. pub trait HashStableContext: rustc_ast::HashStableContext + rustc_target::HashStableContext { @@ -139,11 +139,10 @@ impl HashStable for VisibilityKind<'_> impl HashStable for TraitItem<'_> { fn hash_stable(&self, hcx: &mut HirCtx, hasher: &mut StableHasher) { - let TraitItem { def_id: _, ident, ref attrs, ref generics, ref kind, span } = *self; + let TraitItem { def_id: _, ident, ref generics, ref kind, span } = *self; hcx.hash_hir_item_like(|hcx| { ident.name.hash_stable(hcx, hasher); - attrs.hash_stable(hcx, hasher); generics.hash_stable(hcx, hasher); kind.hash_stable(hcx, hasher); span.hash_stable(hcx, hasher); @@ -153,22 +152,13 @@ impl HashStable for TraitItem<'_> { impl HashStable for ImplItem<'_> { fn hash_stable(&self, hcx: &mut HirCtx, hasher: &mut StableHasher) { - let ImplItem { - def_id: _, - ident, - ref vis, - defaultness, - ref attrs, - ref generics, - ref kind, - span, - } = *self; + let ImplItem { def_id: _, ident, ref vis, defaultness, ref generics, ref kind, span } = + *self; hcx.hash_hir_item_like(|hcx| { ident.name.hash_stable(hcx, hasher); vis.hash_stable(hcx, hasher); defaultness.hash_stable(hcx, hasher); - attrs.hash_stable(hcx, hasher); generics.hash_stable(hcx, hasher); kind.hash_stable(hcx, hasher); span.hash_stable(hcx, hasher); @@ -178,11 +168,10 @@ impl HashStable for ImplItem<'_> { impl HashStable for ForeignItem<'_> { fn hash_stable(&self, hcx: &mut HirCtx, hasher: &mut StableHasher) { - let ForeignItem { def_id: _, ident, ref attrs, ref kind, span, ref vis } = *self; + let ForeignItem { def_id: _, ident, ref kind, span, ref vis } = *self; hcx.hash_hir_item_like(|hcx| { ident.name.hash_stable(hcx, hasher); - attrs.hash_stable(hcx, hasher); kind.hash_stable(hcx, hasher); span.hash_stable(hcx, hasher); vis.hash_stable(hcx, hasher); @@ -192,11 +181,10 @@ impl HashStable for ForeignItem<'_> { impl HashStable for Item<'_> { fn hash_stable(&self, hcx: &mut HirCtx, hasher: &mut StableHasher) { - let Item { ident, ref attrs, def_id: _, ref kind, ref vis, span } = *self; + let Item { ident, def_id: _, ref kind, ref vis, span } = *self; hcx.hash_hir_item_like(|hcx| { ident.name.hash_stable(hcx, hasher); - attrs.hash_stable(hcx, hasher); kind.hash_stable(hcx, hasher); vis.hash_stable(hcx, hasher); span.hash_stable(hcx, hasher); @@ -206,11 +194,10 @@ impl HashStable for Item<'_> { impl HashStable for MacroDef<'_> { fn hash_stable(&self, hcx: &mut HirCtx, hasher: &mut StableHasher) { - let MacroDef { ident, ref attrs, def_id: _, ref ast, ref vis, span } = *self; + let MacroDef { ident, def_id: _, ref ast, ref vis, span } = *self; hcx.hash_hir_item_like(|hcx| { ident.name.hash_stable(hcx, hasher); - attrs.hash_stable(hcx, hasher); ast.hash_stable(hcx, hasher); vis.hash_stable(hcx, hasher); span.hash_stable(hcx, hasher); diff --git a/compiler/rustc_hir/src/tests.rs b/compiler/rustc_hir/src/tests.rs new file mode 100644 index 0000000000000..2aafc6afa236d --- /dev/null +++ b/compiler/rustc_hir/src/tests.rs @@ -0,0 +1,39 @@ +use crate::definitions::{DefKey, DefPathData, DisambiguatedDefPathData}; +use rustc_data_structures::fingerprint::Fingerprint; +use rustc_span::crate_disambiguator::CrateDisambiguator; +use rustc_span::def_id::{DefPathHash, StableCrateId}; + +#[test] +fn def_path_hash_depends_on_crate_id() { + // This test makes sure that *both* halves of a DefPathHash depend on + // the crate-id of the defining crate. This is a desirable property + // because the crate-id can be more easily changed than the DefPath + // of an item, so, in the case of a crate-local DefPathHash collision, + // the user can simply "role the dice again" for all DefPathHashes in + // the crate by changing the crate disambiguator (e.g. via bumping the + // crate's version number). + + let d0 = CrateDisambiguator::from(Fingerprint::new(12, 34)); + let d1 = CrateDisambiguator::from(Fingerprint::new(56, 78)); + + let h0 = mk_test_hash("foo", d0); + let h1 = mk_test_hash("foo", d1); + + assert_ne!(h0.stable_crate_id(), h1.stable_crate_id()); + assert_ne!(h0.local_hash(), h1.local_hash()); + + fn mk_test_hash(crate_name: &str, crate_disambiguator: CrateDisambiguator) -> DefPathHash { + let stable_crate_id = StableCrateId::new(crate_name, crate_disambiguator); + let parent_hash = DefPathHash::new(stable_crate_id, 0); + + let key = DefKey { + parent: None, + disambiguated_data: DisambiguatedDefPathData { + data: DefPathData::CrateRoot, + disambiguator: 0, + }, + }; + + key.compute_stable_hash(parent_hash) + } +} diff --git a/compiler/rustc_hir/src/weak_lang_items.rs b/compiler/rustc_hir/src/weak_lang_items.rs index b8cd15e7f00d6..58c3065240c94 100644 --- a/compiler/rustc_hir/src/weak_lang_items.rs +++ b/compiler/rustc_hir/src/weak_lang_items.rs @@ -18,8 +18,8 @@ pub static WEAK_ITEMS_REFS: SyncLazy> = SyncLazy::ne map }); -/// The `check_name` argument avoids the need for `librustc_hir` to depend on -/// `librustc_session`. +/// 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 where F: Fn(&'a ast::Attribute, Symbol) -> bool diff --git a/compiler/rustc_hir_pretty/src/lib.rs b/compiler/rustc_hir_pretty/src/lib.rs index 416918e3344ee..5820e7a261230 100644 --- a/compiler/rustc_hir_pretty/src/lib.rs +++ b/compiler/rustc_hir_pretty/src/lib.rs @@ -1,4 +1,4 @@ -#![feature(or_patterns)] +#![cfg_attr(bootstrap, feature(or_patterns))] #![recursion_limit = "256"] use rustc_ast as ast; @@ -16,6 +16,7 @@ use rustc_target::spec::abi::Abi; use std::borrow::Cow; use std::cell::Cell; +use std::collections::BTreeMap; use std::vec; pub fn id_to_string(map: &dyn rustc_hir::intravisit::Map<'_>, hir_id: hir::HirId) -> String { @@ -82,6 +83,7 @@ impl PpAnn for &dyn rustc_hir::intravisit::Map<'_> { pub struct State<'a> { pub s: pp::Printer, comments: Option>, + attrs: &'a BTreeMap, ann: &'a (dyn PpAnn + 'a), } @@ -112,7 +114,7 @@ impl<'a> State<'a> { Node::Lifetime(a) => self.print_lifetime(&a), Node::Visibility(a) => self.print_visibility(&a), Node::GenericParam(_) => panic!("cannot print Node::GenericParam"), - Node::Field(_) => panic!("cannot print StructField"), + Node::Field(_) => panic!("cannot print Node::Field"), // These cases do not carry enough information in the // `hir_map` to reconstruct their full structure for pretty // printing. @@ -163,12 +165,12 @@ pub fn print_crate<'a>( input: String, ann: &'a dyn PpAnn, ) -> String { - let mut s = State::new_from_input(sm, filename, input, ann); + let mut s = State::new_from_input(sm, filename, input, &krate.attrs, ann); // When printing the AST, we sometimes need to inject `#[no_std]` here. // Since you can't compile the HIR, it's not necessary. - s.print_mod(&krate.item.module, &krate.item.attrs); + s.print_mod(&krate.item, s.attrs(hir::CRATE_HIR_ID)); s.print_remaining_comments(); s.s.eof() } @@ -178,9 +180,19 @@ impl<'a> State<'a> { sm: &'a SourceMap, filename: FileName, input: String, + attrs: &'a BTreeMap, ann: &'a dyn PpAnn, ) -> State<'a> { - State { s: pp::mk_printer(), comments: Some(Comments::new(sm, filename, input)), ann } + State { + s: pp::mk_printer(), + comments: Some(Comments::new(sm, filename, input)), + attrs, + ann, + } + } + + fn attrs(&self, id: hir::HirId) -> &'a [ast::Attribute] { + self.attrs.get(&id).map_or(&[], |la| *la) } } @@ -188,7 +200,8 @@ pub fn to_string(ann: &dyn PpAnn, f: F) -> String where F: FnOnce(&mut State<'_>), { - let mut printer = State { s: pp::mk_printer(), comments: None, ann }; + let mut printer = + State { s: pp::mk_printer(), comments: None, attrs: &BTreeMap::default(), ann }; f(&mut printer); printer.s.eof() } @@ -208,10 +221,6 @@ pub fn bounds_to_string<'b>(bounds: impl IntoIterator) -> String { - to_string(NO_ANN, |s| s.print_param(arg)) -} - pub fn ty_to_string(ty: &hir::Ty<'_>) -> String { to_string(NO_ANN, |s| s.print_type(ty)) } @@ -397,7 +406,10 @@ impl<'a> State<'a> { } hir::TyKind::OpaqueDef(..) => self.s.word("/*impl Trait*/"), hir::TyKind::Path(ref qpath) => self.print_qpath(qpath, false), - hir::TyKind::TraitObject(bounds, ref lifetime) => { + hir::TyKind::TraitObject(bounds, ref lifetime, syntax) => { + if syntax == ast::TraitObjectSyntax::Dyn { + self.word_space("dyn"); + } let mut first = true; for bound in bounds { if first { @@ -441,7 +453,7 @@ impl<'a> State<'a> { pub fn print_foreign_item(&mut self, item: &hir::ForeignItem<'_>) { self.hardbreak_if_not_bol(); self.maybe_print_comment(item.span.lo()); - self.print_outer_attributes(&item.attrs); + self.print_outer_attributes(self.attrs(item.hir_id())); match item.kind { hir::ForeignItemKind::Fn(ref decl, ref arg_names, ref generics) => { self.head(""); @@ -549,7 +561,8 @@ impl<'a> State<'a> { pub fn print_item(&mut self, item: &hir::Item<'_>) { self.hardbreak_if_not_bol(); self.maybe_print_comment(item.span.lo()); - self.print_outer_attributes(&item.attrs); + let attrs = self.attrs(item.hir_id()); + self.print_outer_attributes(attrs); self.ann.pre(self, AnnNode::Item(item)); match item.kind { hir::ItemKind::ExternCrate(orig_name) => { @@ -634,14 +647,14 @@ impl<'a> State<'a> { self.print_ident(item.ident); self.nbsp(); self.bopen(); - self.print_mod(_mod, &item.attrs); + self.print_mod(_mod, attrs); self.bclose(item.span); } hir::ItemKind::ForeignMod { abi, items } => { self.head("extern"); self.word_nbsp(abi.to_string()); self.bopen(); - self.print_inner_attributes(item.attrs); + self.print_inner_attributes(self.attrs(item.hir_id())); for item in items { self.ann.nested(self, Nested::ForeignItem(item.id)); } @@ -725,7 +738,7 @@ impl<'a> State<'a> { self.s.space(); self.bopen(); - self.print_inner_attributes(&item.attrs); + self.print_inner_attributes(attrs); for impl_item in items { self.ann.nested(self, Nested::ImplItem(impl_item.id)); } @@ -822,7 +835,7 @@ impl<'a> State<'a> { for v in variants { self.space_if_not_bol(); self.maybe_print_comment(v.span.lo()); - self.print_outer_attributes(&v.attrs); + self.print_outer_attributes(self.attrs(v.id)); self.ibox(INDENT_UNIT); self.print_variant(v); self.s.word(","); @@ -876,7 +889,7 @@ impl<'a> State<'a> { self.popen(); self.commasep(Inconsistent, struct_def.fields(), |s, field| { s.maybe_print_comment(field.span.lo()); - s.print_outer_attributes(&field.attrs); + s.print_outer_attributes(s.attrs(field.hir_id)); s.print_visibility(&field.vis); s.print_type(&field.ty) }); @@ -898,7 +911,7 @@ impl<'a> State<'a> { for field in struct_def.fields() { self.hardbreak_if_not_bol(); self.maybe_print_comment(field.span.lo()); - self.print_outer_attributes(&field.attrs); + self.print_outer_attributes(self.attrs(field.hir_id)); self.print_visibility(&field.vis); self.print_ident(field.ident); self.word_nbsp(":"); @@ -937,7 +950,7 @@ impl<'a> State<'a> { self.ann.pre(self, AnnNode::SubItem(ti.hir_id())); self.hardbreak_if_not_bol(); self.maybe_print_comment(ti.span.lo()); - self.print_outer_attributes(&ti.attrs); + self.print_outer_attributes(self.attrs(ti.hir_id())); match ti.kind { hir::TraitItemKind::Const(ref ty, default) => { let vis = @@ -976,7 +989,7 @@ impl<'a> State<'a> { self.ann.pre(self, AnnNode::SubItem(ii.hir_id())); self.hardbreak_if_not_bol(); self.maybe_print_comment(ii.span.lo()); - self.print_outer_attributes(&ii.attrs); + self.print_outer_attributes(self.attrs(ii.hir_id())); self.print_defaultness(ii.defaultness); match ii.kind { @@ -1193,7 +1206,7 @@ impl<'a> State<'a> { fn print_expr_struct( &mut self, qpath: &hir::QPath<'_>, - fields: &[hir::Field<'_>], + fields: &[hir::ExprField<'_>], wth: &Option<&hir::Expr<'_>>, ) { self.print_qpath(qpath, true); @@ -1321,7 +1334,7 @@ impl<'a> State<'a> { pub fn print_expr(&mut self, expr: &hir::Expr<'_>) { self.maybe_print_comment(expr.span.lo()); - self.print_outer_attributes(&expr.attrs); + self.print_outer_attributes(self.attrs(expr.hir_id)); self.ibox(INDENT_UNIT); self.ann.pre(self, AnnNode::Expr(expr)); match expr.kind { @@ -1557,10 +1570,10 @@ impl<'a> State<'a> { None => s.word("_"), } } - hir::InlineAsmOperand::Const { expr } => { + hir::InlineAsmOperand::Const { anon_const } => { s.word("const"); s.space(); - s.print_expr(expr); + s.print_anon_const(anon_const); } hir::InlineAsmOperand::Sym { expr } => { s.word("sym"); @@ -1684,21 +1697,10 @@ impl<'a> State<'a> { } } - pub fn print_usize(&mut self, i: usize) { - self.s.word(i.to_string()) - } - pub fn print_name(&mut self, name: Symbol) { self.print_ident(Ident::with_dummy_span(name)) } - pub fn print_for_decl(&mut self, loc: &hir::Local<'_>, coll: &hir::Expr<'_>) { - self.print_local_decl(loc); - self.s.space(); - self.word_space("in"); - self.print_expr(coll) - } - pub fn print_path(&mut self, path: &hir::Path<'_>, colons_before_params: bool) { self.maybe_print_comment(path.span.lo()); @@ -2020,20 +2022,20 @@ impl<'a> State<'a> { } pub fn print_param(&mut self, arg: &hir::Param<'_>) { - self.print_outer_attributes(&arg.attrs); + self.print_outer_attributes(self.attrs(arg.hir_id)); self.print_pat(&arg.pat); } pub fn print_arm(&mut self, arm: &hir::Arm<'_>) { // I have no idea why this check is necessary, but here it // is :( - if arm.attrs.is_empty() { + if self.attrs(arm.hir_id).is_empty() { self.s.space(); } self.cbox(INDENT_UNIT); self.ann.pre(self, AnnNode::Arm(arm)); self.ibox(0); - self.print_outer_attributes(&arm.attrs); + self.print_outer_attributes(&self.attrs(arm.hir_id)); self.print_pat(&arm.pat); self.s.space(); if let Some(ref g) = arm.guard { @@ -2249,8 +2251,10 @@ impl<'a> State<'a> { GenericParamKind::Const { ref ty, ref default } => { self.word_space(":"); self.print_type(ty); - if let Some(ref _default) = default { - // FIXME(const_generics_defaults): print the `default` value here + if let Some(ref default) = default { + self.s.space(); + self.word_space("="); + self.print_anon_const(&default) } } } @@ -2411,24 +2415,6 @@ impl<'a> State<'a> { } } - pub fn print_opt_abi_and_extern_if_nondefault(&mut self, opt_abi: Option) { - match opt_abi { - Some(Abi::Rust) => {} - Some(abi) => { - self.word_nbsp("extern"); - self.word_nbsp(abi.to_string()) - } - None => {} - } - } - - pub fn print_extern_opt_abi(&mut self, opt_abi: Option) { - if let Some(abi) = opt_abi { - self.word_nbsp("extern"); - self.word_nbsp(abi.to_string()) - } - } - pub fn print_fn_header_info(&mut self, header: hir::FnHeader, vis: &hir::Visibility<'_>) { self.s.word(visibility_qualified(vis, "")); diff --git a/compiler/rustc_incremental/src/assert_dep_graph.rs b/compiler/rustc_incremental/src/assert_dep_graph.rs index 1162379d3d981..b5680beae142d 100644 --- a/compiler/rustc_incremental/src/assert_dep_graph.rs +++ b/compiler/rustc_incremental/src/assert_dep_graph.rs @@ -40,8 +40,9 @@ use rustc_graphviz as dot; use rustc_hir as hir; use rustc_hir::def_id::DefId; use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor}; -use rustc_middle::dep_graph::debug::{DepNodeFilter, EdgeFilter}; -use rustc_middle::dep_graph::{DepGraphQuery, DepKind, DepNode, DepNodeExt}; +use rustc_middle::dep_graph::{ + DepGraphQuery, DepKind, DepNode, DepNodeExt, DepNodeFilter, EdgeFilter, +}; use rustc_middle::hir::map::Map; use rustc_middle::ty::TyCtxt; use rustc_span::symbol::{sym, Symbol}; @@ -54,7 +55,11 @@ use std::io::{BufWriter, Write}; pub fn assert_dep_graph(tcx: TyCtxt<'_>) { tcx.dep_graph.with_ignore(|| { if tcx.sess.opts.debugging_opts.dump_dep_graph { - dump_graph(tcx); + tcx.dep_graph.with_query(dump_graph); + } + + if !tcx.sess.opts.debugging_opts.query_dep_graph { + return; } // if the `rustc_attrs` feature is not enabled, then the @@ -68,7 +73,7 @@ pub fn assert_dep_graph(tcx: TyCtxt<'_>) { let (if_this_changed, then_this_would_need) = { let mut visitor = IfThisChanged { tcx, if_this_changed: vec![], then_this_would_need: vec![] }; - visitor.process_attrs(hir::CRATE_HIR_ID, &tcx.hir().krate().item.attrs); + visitor.process_attrs(hir::CRATE_HIR_ID); tcx.hir().krate().visit_all_item_likes(&mut visitor.as_deep_visitor()); (visitor.if_this_changed, visitor.then_this_would_need) }; @@ -113,9 +118,10 @@ impl IfThisChanged<'tcx> { value } - fn process_attrs(&mut self, hir_id: hir::HirId, attrs: &[ast::Attribute]) { + fn process_attrs(&mut self, hir_id: hir::HirId) { let def_id = self.tcx.hir().local_def_id(hir_id); let def_path_hash = self.tcx.def_path_hash(def_id.to_def_id()); + let attrs = self.tcx.hir().attrs(hir_id); for attr in attrs { if self.tcx.sess.check_name(attr, sym::rustc_if_this_changed) { let dep_node_interned = self.argument(attr); @@ -167,23 +173,23 @@ impl Visitor<'tcx> for IfThisChanged<'tcx> { } fn visit_item(&mut self, item: &'tcx hir::Item<'tcx>) { - self.process_attrs(item.hir_id(), &item.attrs); + self.process_attrs(item.hir_id()); intravisit::walk_item(self, item); } fn visit_trait_item(&mut self, trait_item: &'tcx hir::TraitItem<'tcx>) { - self.process_attrs(trait_item.hir_id(), &trait_item.attrs); + self.process_attrs(trait_item.hir_id()); intravisit::walk_trait_item(self, trait_item); } fn visit_impl_item(&mut self, impl_item: &'tcx hir::ImplItem<'tcx>) { - self.process_attrs(impl_item.hir_id(), &impl_item.attrs); + self.process_attrs(impl_item.hir_id()); intravisit::walk_impl_item(self, impl_item); } - fn visit_struct_field(&mut self, s: &'tcx hir::StructField<'tcx>) { - self.process_attrs(s.hir_id, &s.attrs); - intravisit::walk_struct_field(self, s); + fn visit_field_def(&mut self, s: &'tcx hir::FieldDef<'tcx>) { + self.process_attrs(s.hir_id); + intravisit::walk_field_def(self, s); } } @@ -195,29 +201,29 @@ fn check_paths<'tcx>(tcx: TyCtxt<'tcx>, if_this_changed: &Sources, then_this_wou } return; } - let query = tcx.dep_graph.query(); - for &(_, source_def_id, ref source_dep_node) in if_this_changed { - let dependents = query.transitive_predecessors(source_dep_node); - for &(target_span, ref target_pass, _, ref target_dep_node) in then_this_would_need { - if !dependents.contains(&target_dep_node) { - tcx.sess.span_err( - target_span, - &format!( - "no path from `{}` to `{}`", - tcx.def_path_str(source_def_id), - target_pass - ), - ); - } else { - tcx.sess.span_err(target_span, "OK"); + tcx.dep_graph.with_query(|query| { + for &(_, source_def_id, ref source_dep_node) in if_this_changed { + let dependents = query.transitive_predecessors(source_dep_node); + for &(target_span, ref target_pass, _, ref target_dep_node) in then_this_would_need { + if !dependents.contains(&target_dep_node) { + tcx.sess.span_err( + target_span, + &format!( + "no path from `{}` to `{}`", + tcx.def_path_str(source_def_id), + target_pass + ), + ); + } else { + tcx.sess.span_err(target_span, "OK"); + } } } - } + }); } -fn dump_graph(tcx: TyCtxt<'_>) { +fn dump_graph(query: &DepGraphQuery) { let path: String = env::var("RUST_DEP_GRAPH").unwrap_or_else(|_| "dep_graph".to_string()); - let query = tcx.dep_graph.query(); let nodes = match env::var("RUST_DEP_GRAPH_FILTER") { Ok(string) => { diff --git a/compiler/rustc_incremental/src/assert_module_sources.rs b/compiler/rustc_incremental/src/assert_module_sources.rs index 17d8ac9c88297..5fb2c1cb9c923 100644 --- a/compiler/rustc_incremental/src/assert_module_sources.rs +++ b/compiler/rustc_incremental/src/assert_module_sources.rs @@ -44,7 +44,7 @@ pub fn assert_module_sources(tcx: TyCtxt<'_>) { let ams = AssertModuleSource { tcx, available_cgus }; - for attr in tcx.hir().krate().item.attrs { + for attr in tcx.hir().attrs(rustc_hir::CRATE_HIR_ID) { ams.check_attr(attr); } }) diff --git a/compiler/rustc_incremental/src/lib.rs b/compiler/rustc_incremental/src/lib.rs index 95456c07b10aa..f089cbcfca6e5 100644 --- a/compiler/rustc_incremental/src/lib.rs +++ b/compiler/rustc_incremental/src/lib.rs @@ -14,7 +14,7 @@ mod assert_dep_graph; pub mod assert_module_sources; mod persist; -pub use assert_dep_graph::assert_dep_graph; +use assert_dep_graph::assert_dep_graph; pub use persist::copy_cgu_workproduct_to_incr_comp_cache_dir; pub use persist::delete_workproduct_files; pub use persist::finalize_session_directory; @@ -26,4 +26,4 @@ pub use persist::prepare_session_directory; pub use persist::save_dep_graph; pub use persist::save_work_product_index; pub use persist::LoadResult; -pub use persist::{load_dep_graph, DepGraphFuture}; +pub use persist::{build_dep_graph, load_dep_graph, DepGraphFuture}; diff --git a/compiler/rustc_incremental/src/persist/dirty_clean.rs b/compiler/rustc_incremental/src/persist/dirty_clean.rs index 8a83149d73235..e7bd488af8ebf 100644 --- a/compiler/rustc_incremental/src/persist/dirty_clean.rs +++ b/compiler/rustc_incremental/src/persist/dirty_clean.rs @@ -14,7 +14,6 @@ //! the required condition is not met. use rustc_ast::{self as ast, Attribute, NestedMetaItem}; -use rustc_data_structures::fingerprint::Fingerprint; use rustc_data_structures::fx::FxHashSet; use rustc_hir as hir; use rustc_hir::def_id::{DefId, LocalDefId}; @@ -74,16 +73,6 @@ const BASE_STRUCT: &[&str] = &[label_strs::generics_of, label_strs::predicates_of, label_strs::type_of]; /// Trait definition `DepNode`s. -const BASE_TRAIT_DEF: &[&str] = &[ - label_strs::associated_item_def_ids, - label_strs::generics_of, - label_strs::object_safety_violations, - label_strs::predicates_of, - label_strs::specialization_graph_of, - label_strs::trait_def, - label_strs::trait_impls_of, -]; - /// Extra `DepNode`s for functions and methods. const EXTRA_ASSOCIATED: &[&str] = &[label_strs::associated_item]; @@ -118,10 +107,6 @@ const LABELS_IMPL: &[&[&str]] = &[BASE_HIR, BASE_IMPL]; /// Abstract data type (struct, enum, union) `DepNode`s. const LABELS_ADT: &[&[&str]] = &[BASE_HIR, BASE_STRUCT]; -/// Trait definition `DepNode`s. -#[allow(dead_code)] -const LABELS_TRAIT: &[&[&str]] = &[BASE_HIR, BASE_TRAIT_DEF]; - // FIXME: Struct/Enum/Unions Fields (there is currently no way to attach these) // // Fields are kind of separate from their containers, as they can change independently from @@ -148,6 +133,10 @@ impl Assertion { } pub fn check_dirty_clean_annotations(tcx: TyCtxt<'_>) { + if !tcx.sess.opts.debugging_opts.query_dep_graph { + return; + } + // can't add `#[rustc_dirty]` etc without opting in to this feature if !tcx.features().rustc_attrs { return; @@ -168,7 +157,7 @@ pub fn check_dirty_clean_annotations(tcx: TyCtxt<'_>) { // Note that we cannot use the existing "unused attribute"-infrastructure // here, since that is running before codegen. This is also the reason why // all codegen-specific attributes are `AssumedUsed` in rustc_ast::feature_gate. - all_attrs.report_unchecked_attrs(&dirty_clean_visitor.checked_attrs); + all_attrs.report_unchecked_attrs(dirty_clean_visitor.checked_attrs); }) } @@ -391,10 +380,7 @@ impl DirtyCleanVisitor<'tcx> { fn assert_dirty(&self, item_span: Span, dep_node: DepNode) { debug!("assert_dirty({:?})", dep_node); - let current_fingerprint = self.get_fingerprint(&dep_node); - let prev_fingerprint = self.tcx.dep_graph.prev_fingerprint_of(&dep_node); - - if current_fingerprint == prev_fingerprint { + if self.tcx.dep_graph.is_green(&dep_node) { let dep_node_str = self.dep_node_str(&dep_node); self.tcx .sess @@ -402,28 +388,10 @@ impl DirtyCleanVisitor<'tcx> { } } - fn get_fingerprint(&self, dep_node: &DepNode) -> Option { - if self.tcx.dep_graph.dep_node_exists(dep_node) { - let dep_node_index = self.tcx.dep_graph.dep_node_index_of(dep_node); - Some(self.tcx.dep_graph.fingerprint_of(dep_node_index)) - } else { - None - } - } - fn assert_clean(&self, item_span: Span, dep_node: DepNode) { debug!("assert_clean({:?})", dep_node); - let current_fingerprint = self.get_fingerprint(&dep_node); - let prev_fingerprint = self.tcx.dep_graph.prev_fingerprint_of(&dep_node); - - // if the node wasn't previously evaluated and now is (or vice versa), - // then the node isn't actually clean or dirty. - if (current_fingerprint == None) ^ (prev_fingerprint == None) { - return; - } - - if current_fingerprint != prev_fingerprint { + if self.tcx.dep_graph.is_red(&dep_node) { let dep_node_str = self.dep_node_str(&dep_node); self.tcx .sess @@ -535,13 +503,14 @@ impl FindAllAttrs<'_, 'tcx> { false } - fn report_unchecked_attrs(&self, checked_attrs: &FxHashSet) { + fn report_unchecked_attrs(&self, mut checked_attrs: FxHashSet) { for attr in &self.found_attrs { if !checked_attrs.contains(&attr.id) { self.tcx.sess.span_err( attr.span, "found unchecked `#[rustc_dirty]` / `#[rustc_clean]` attribute", ); + checked_attrs.insert(attr.id); } } } @@ -554,7 +523,7 @@ impl intravisit::Visitor<'tcx> for FindAllAttrs<'_, 'tcx> { intravisit::NestedVisitorMap::All(self.tcx.hir()) } - fn visit_attribute(&mut self, attr: &'tcx Attribute) { + fn visit_attribute(&mut self, _: hir::HirId, attr: &'tcx Attribute) { if self.is_active_attr(attr) { self.found_attrs.push(attr); } diff --git a/compiler/rustc_incremental/src/persist/file_format.rs b/compiler/rustc_incremental/src/persist/file_format.rs index 374a9eb41e5c7..b821ed6cff9f8 100644 --- a/compiler/rustc_incremental/src/persist/file_format.rs +++ b/compiler/rustc_incremental/src/persist/file_format.rs @@ -15,6 +15,7 @@ use std::io::{self, Read}; use std::path::Path; use rustc_serialize::opaque::{FileEncodeResult, FileEncoder}; +use rustc_serialize::Encoder; /// The first few bytes of files generated by incremental compilation. const FILE_MAGIC: &[u8] = b"RSIC"; diff --git a/compiler/rustc_incremental/src/persist/fs.rs b/compiler/rustc_incremental/src/persist/fs.rs index c7a6c1195c503..30c6c408bc7c0 100644 --- a/compiler/rustc_incremental/src/persist/fs.rs +++ b/compiler/rustc_incremental/src/persist/fs.rs @@ -122,6 +122,7 @@ mod tests; const LOCK_FILE_EXT: &str = ".lock"; const DEP_GRAPH_FILENAME: &str = "dep-graph.bin"; +const STAGING_DEP_GRAPH_FILENAME: &str = "dep-graph.part.bin"; const WORK_PRODUCTS_FILENAME: &str = "work-products.bin"; const QUERY_CACHE_FILENAME: &str = "query-cache.bin"; @@ -134,6 +135,9 @@ const INT_ENCODE_BASE: usize = base_n::CASE_INSENSITIVE; pub fn dep_graph_path(sess: &Session) -> PathBuf { in_incr_comp_dir_sess(sess, DEP_GRAPH_FILENAME) } +pub fn staging_dep_graph_path(sess: &Session) -> PathBuf { + in_incr_comp_dir_sess(sess, STAGING_DEP_GRAPH_FILENAME) +} pub fn dep_graph_path_from(incr_comp_session_dir: &Path) -> PathBuf { in_incr_comp_dir(incr_comp_session_dir, DEP_GRAPH_FILENAME) } diff --git a/compiler/rustc_incremental/src/persist/load.rs b/compiler/rustc_incremental/src/persist/load.rs index 2b5649bb0594f..259e540c6125e 100644 --- a/compiler/rustc_incremental/src/persist/load.rs +++ b/compiler/rustc_incremental/src/persist/load.rs @@ -5,7 +5,7 @@ use rustc_hir::definitions::Definitions; use rustc_middle::dep_graph::{PreviousDepGraph, SerializedDepGraph, WorkProduct, WorkProductId}; use rustc_middle::ty::query::OnDiskCache; use rustc_serialize::opaque::Decoder; -use rustc_serialize::Decodable as RustcDecodable; +use rustc_serialize::Decodable; use rustc_session::Session; use std::path::Path; @@ -120,7 +120,7 @@ pub fn load_dep_graph(sess: &Session) -> DepGraphFuture { // Decode the list of work_products let mut work_product_decoder = Decoder::new(&work_products_data[..], start_pos); let work_products: Vec = - RustcDecodable::decode(&mut work_product_decoder).unwrap_or_else(|e| { + Decodable::decode(&mut work_product_decoder).unwrap_or_else(|e| { let msg = format!( "Error decoding `work-products` from incremental \ compilation session directory: {}", diff --git a/compiler/rustc_incremental/src/persist/mod.rs b/compiler/rustc_incremental/src/persist/mod.rs index 8821b34b50212..1336189bc0d26 100644 --- a/compiler/rustc_incremental/src/persist/mod.rs +++ b/compiler/rustc_incremental/src/persist/mod.rs @@ -18,6 +18,7 @@ pub use fs::prepare_session_directory; pub use load::load_query_result_cache; pub use load::LoadResult; pub use load::{load_dep_graph, DepGraphFuture}; +pub use save::build_dep_graph; pub use save::save_dep_graph; pub use save::save_work_product_index; pub use work_product::copy_cgu_workproduct_to_incr_comp_cache_dir; diff --git a/compiler/rustc_incremental/src/persist/save.rs b/compiler/rustc_incremental/src/persist/save.rs index 45d474b89b8df..d558af3c1d558 100644 --- a/compiler/rustc_incremental/src/persist/save.rs +++ b/compiler/rustc_incremental/src/persist/save.rs @@ -1,6 +1,6 @@ use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::sync::join; -use rustc_middle::dep_graph::{DepGraph, WorkProduct, WorkProductId}; +use rustc_middle::dep_graph::{DepGraph, PreviousDepGraph, WorkProduct, WorkProductId}; use rustc_middle::ty::TyCtxt; use rustc_serialize::opaque::{FileEncodeResult, FileEncoder}; use rustc_serialize::Encodable as RustcEncodable; @@ -15,6 +15,9 @@ use super::file_format; use super::fs::*; use super::work_product; +/// Save and dump the DepGraph. +/// +/// No query must be invoked after this function. pub fn save_dep_graph(tcx: TyCtxt<'_>) { debug!("save_dep_graph()"); tcx.dep_graph.with_ignore(|| { @@ -29,6 +32,14 @@ pub fn save_dep_graph(tcx: TyCtxt<'_>) { let query_cache_path = query_cache_path(sess); let dep_graph_path = dep_graph_path(sess); + let staging_dep_graph_path = staging_dep_graph_path(sess); + + sess.time("assert_dep_graph", || crate::assert_dep_graph(tcx)); + sess.time("check_dirty_clean", || dirty_clean::check_dirty_clean_annotations(tcx)); + + if sess.opts.debugging_opts.incremental_info { + tcx.dep_graph.print_incremental_info() + } join( move || { @@ -36,16 +47,26 @@ pub fn save_dep_graph(tcx: TyCtxt<'_>) { save_in(sess, query_cache_path, "query cache", |e| encode_query_cache(tcx, e)); }); }, - || { + move || { sess.time("incr_comp_persist_dep_graph", || { - save_in(sess, dep_graph_path, "dependency graph", |e| { - sess.time("incr_comp_encode_dep_graph", || encode_dep_graph(tcx, e)) - }); + if let Err(err) = tcx.dep_graph.encode(&tcx.sess.prof) { + sess.err(&format!( + "failed to write dependency graph to `{}`: {}", + staging_dep_graph_path.display(), + err + )); + } + if let Err(err) = fs::rename(&staging_dep_graph_path, &dep_graph_path) { + sess.err(&format!( + "failed to move dependency graph from `{}` to `{}`: {}", + staging_dep_graph_path.display(), + dep_graph_path.display(), + err + )); + } }); }, ); - - dirty_clean::check_dirty_clean_annotations(tcx); }) } @@ -92,7 +113,7 @@ pub fn save_work_product_index( }); } -fn save_in(sess: &Session, path_buf: PathBuf, name: &str, encode: F) +pub(crate) fn save_in(sess: &Session, path_buf: PathBuf, name: &str, encode: F) where F: FnOnce(&mut FileEncoder) -> FileEncodeResult, { @@ -144,21 +165,6 @@ where debug!("save: data written to disk successfully"); } -fn encode_dep_graph(tcx: TyCtxt<'_>, encoder: &mut FileEncoder) -> FileEncodeResult { - // First encode the commandline arguments hash - tcx.sess.opts.dep_tracking_hash().encode(encoder)?; - - if tcx.sess.opts.debugging_opts.incremental_info { - tcx.dep_graph.print_incremental_info(); - } - - // There is a tiny window between printing the incremental info above and encoding the dep - // graph below in which the dep graph could change, thus making the printed incremental info - // slightly out of date. If this matters to you, please feel free to submit a patch. :) - - tcx.sess.time("incr_comp_encode_serialized_dep_graph", || tcx.dep_graph.encode(encoder)) -} - fn encode_work_product_index( work_products: &FxHashMap, encoder: &mut FileEncoder, @@ -177,3 +183,56 @@ fn encode_work_product_index( fn encode_query_cache(tcx: TyCtxt<'_>, encoder: &mut FileEncoder) -> FileEncodeResult { tcx.sess.time("incr_comp_serialize_result_cache", || tcx.serialize_query_result_cache(encoder)) } + +pub fn build_dep_graph( + sess: &Session, + prev_graph: PreviousDepGraph, + prev_work_products: FxHashMap, +) -> Option { + if sess.opts.incremental.is_none() { + // No incremental compilation. + return None; + } + + // Stream the dep-graph to an alternate file, to avoid overwriting anything in case of errors. + let path_buf = staging_dep_graph_path(sess); + + let mut encoder = match FileEncoder::new(&path_buf) { + Ok(encoder) => encoder, + Err(err) => { + sess.err(&format!( + "failed to create dependency graph at `{}`: {}", + path_buf.display(), + err + )); + return None; + } + }; + + if let Err(err) = file_format::write_file_header(&mut encoder, sess.is_nightly_build()) { + sess.err(&format!( + "failed to write dependency graph header to `{}`: {}", + path_buf.display(), + err + )); + return None; + } + + // First encode the commandline arguments hash + if let Err(err) = sess.opts.dep_tracking_hash().encode(&mut encoder) { + sess.err(&format!( + "failed to write dependency graph hash `{}`: {}", + path_buf.display(), + err + )); + return None; + } + + Some(DepGraph::new( + prev_graph, + prev_work_products, + encoder, + sess.opts.debugging_opts.query_dep_graph, + sess.opts.debugging_opts.incremental_info, + )) +} diff --git a/compiler/rustc_index/Cargo.toml b/compiler/rustc_index/Cargo.toml index 6e1471df195b1..4b1f0b86475a2 100644 --- a/compiler/rustc_index/Cargo.toml +++ b/compiler/rustc_index/Cargo.toml @@ -8,6 +8,6 @@ edition = "2018" doctest = false [dependencies] -arrayvec = { version = "0.5.1", default-features = false } +arrayvec = { version = "0.7", default-features = false } rustc_serialize = { path = "../rustc_serialize" } rustc_macros = { path = "../rustc_macros" } diff --git a/compiler/rustc_index/src/bit_set.rs b/compiler/rustc_index/src/bit_set.rs index 100824f4b9448..d26ab1939e3bf 100644 --- a/compiler/rustc_index/src/bit_set.rs +++ b/compiler/rustc_index/src/bit_set.rs @@ -356,7 +356,7 @@ where { assert_eq!(out_vec.len(), in_vec.len()); let mut changed = false; - for (out_elem, in_elem) in out_vec.iter_mut().zip(in_vec.iter()) { + for (out_elem, in_elem) in iter::zip(out_vec, in_vec) { let old_val = *out_elem; let new_val = op(old_val, *in_elem); *out_elem = new_val; @@ -375,7 +375,7 @@ const SPARSE_MAX: usize = 8; #[derive(Clone, Debug)] pub struct SparseBitSet { domain_size: usize, - elems: ArrayVec<[T; SPARSE_MAX]>, + elems: ArrayVec, } impl SparseBitSet { @@ -842,7 +842,7 @@ impl BitMatrix { let (write_start, write_end) = self.range(write); let words = &mut self.words[..]; let mut changed = false; - for (read_index, write_index) in (read_start..read_end).zip(write_start..write_end) { + for (read_index, write_index) in iter::zip(read_start..read_end, write_start..write_end) { let word = words[write_index]; let new_word = word | words[read_index]; words[write_index] = new_word; @@ -858,7 +858,7 @@ impl BitMatrix { assert_eq!(with.domain_size(), self.num_columns); let (write_start, write_end) = self.range(write); let mut changed = false; - for (read_index, write_index) in (0..with.words().len()).zip(write_start..write_end) { + for (read_index, write_index) in iter::zip(0..with.words().len(), write_start..write_end) { let word = self.words[write_index]; let new_word = word | with.words()[read_index]; self.words[write_index] = new_word; diff --git a/compiler/rustc_index/src/lib.rs b/compiler/rustc_index/src/lib.rs index 995034e81da28..3ced3920cfdfe 100644 --- a/compiler/rustc_index/src/lib.rs +++ b/compiler/rustc_index/src/lib.rs @@ -2,6 +2,7 @@ #![feature(const_fn)] #![feature(const_panic)] #![feature(extend_one)] +#![feature(iter_zip)] #![feature(unboxed_closures)] #![feature(test)] #![feature(fn_traits)] diff --git a/compiler/rustc_index/src/vec.rs b/compiler/rustc_index/src/vec.rs index 7cc4a5e9785e5..1b1a59a254e6f 100644 --- a/compiler/rustc_index/src/vec.rs +++ b/compiler/rustc_index/src/vec.rs @@ -124,7 +124,8 @@ macro_rules! newtype_index { #[inline] $v const fn from_usize(value: usize) -> Self { - assert!(value <= ($max as usize)); + // FIXME: replace with `assert!(value <= ($max as usize));` once `const_panic` is stable + [()][(value > ($max as usize)) as usize]; unsafe { Self::from_u32_unchecked(value as u32) } @@ -132,7 +133,8 @@ macro_rules! newtype_index { #[inline] $v const fn from_u32(value: u32) -> Self { - assert!(value <= $max); + // FIXME: replace with `assert!(value <= $max);` once `const_panic` is stable + [()][(value > $max) as usize]; unsafe { Self::from_u32_unchecked(value) } @@ -695,9 +697,7 @@ impl IndexVec { pub fn convert_index_type(self) -> IndexVec { IndexVec { raw: self.raw, _marker: PhantomData } } -} -impl IndexVec { /// Grows the index vector so that it contains an entry for /// `elem`; if that is already true, then has no /// effect. Otherwise, inserts new values as needed by invoking @@ -710,11 +710,6 @@ impl IndexVec { } } - #[inline] - pub fn resize(&mut self, new_len: usize, value: T) { - self.raw.resize(new_len, value) - } - #[inline] pub fn resize_to_elem(&mut self, elem: I, fill_value: impl FnMut() -> T) { let min_new_len = elem.index() + 1; @@ -722,6 +717,13 @@ impl IndexVec { } } +impl IndexVec { + #[inline] + pub fn resize(&mut self, new_len: usize, value: T) { + self.raw.resize(new_len, value) + } +} + impl IndexVec { #[inline] pub fn binary_search(&self, value: &T) -> Result { diff --git a/compiler/rustc_infer/src/infer/canonical/canonicalizer.rs b/compiler/rustc_infer/src/infer/canonical/canonicalizer.rs index aa4fd055d5ee0..c68705da413f8 100644 --- a/compiler/rustc_infer/src/infer/canonical/canonicalizer.rs +++ b/compiler/rustc_infer/src/infer/canonical/canonicalizer.rs @@ -293,7 +293,7 @@ impl<'cx, 'tcx> TypeFolder<'tcx> for Canonicalizer<'cx, 'tcx> { self.tcx } - fn fold_binder(&mut self, t: ty::Binder) -> ty::Binder + fn fold_binder(&mut self, t: ty::Binder<'tcx, T>) -> ty::Binder<'tcx, T> where T: TypeFoldable<'tcx>, { @@ -621,7 +621,7 @@ impl<'cx, 'tcx> Canonicalizer<'cx, 'tcx> { r: ty::Region<'tcx>, ) -> ty::Region<'tcx> { let var = self.canonical_var(info, r.into()); - let br = ty::BoundRegion { kind: ty::BrAnon(var.as_u32()) }; + let br = ty::BoundRegion { var, kind: ty::BrAnon(var.as_u32()) }; let region = ty::ReLateBound(self.binder_index, br); self.tcx().mk_region(region) } diff --git a/compiler/rustc_infer/src/infer/canonical/query_response.rs b/compiler/rustc_infer/src/infer/canonical/query_response.rs index 2ec9b9e0be4a8..b8ecc949588fb 100644 --- a/compiler/rustc_infer/src/infer/canonical/query_response.rs +++ b/compiler/rustc_infer/src/infer/canonical/query_response.rs @@ -27,6 +27,7 @@ use rustc_middle::ty::relate::TypeRelation; use rustc_middle::ty::subst::{GenericArg, GenericArgKind}; use rustc_middle::ty::{self, BoundVar, Const, ToPredicate, Ty, TyCtxt}; use std::fmt::Debug; +use std::iter; impl<'cx, 'tcx> InferCtxt<'cx, 'tcx> { /// This method is meant to be invoked as the final step of a canonical query @@ -418,7 +419,8 @@ impl<'cx, 'tcx> InferCtxt<'cx, 'tcx> { // In terms of our example above, we are iterating over pairs like: // [(?A, Vec), ('static, '?1), (?B, ?0)] - for (original_value, result_value) in original_values.var_values.iter().zip(result_values) { + for (original_value, result_value) in iter::zip(&original_values.var_values, result_values) + { match result_value.unpack() { GenericArgKind::Type(result_value) => { // e.g., here `result_value` might be `?0` in the example above... @@ -437,7 +439,7 @@ impl<'cx, 'tcx> InferCtxt<'cx, 'tcx> { // We only allow a `ty::INNERMOST` index in substitutions. assert_eq!(debruijn, ty::INNERMOST); - opt_values[br.assert_bound_var()] = Some(*original_value); + opt_values[br.var] = Some(*original_value); } } GenericArgKind::Const(result_value) => { diff --git a/compiler/rustc_infer/src/infer/canonical/substitute.rs b/compiler/rustc_infer/src/infer/canonical/substitute.rs index 387f480814ae7..553a11d4393f9 100644 --- a/compiler/rustc_infer/src/infer/canonical/substitute.rs +++ b/compiler/rustc_infer/src/infer/canonical/substitute.rs @@ -71,11 +71,10 @@ where if var_values.var_values.is_empty() { value } else { - let fld_r = - |br: ty::BoundRegion| match var_values.var_values[br.assert_bound_var()].unpack() { - GenericArgKind::Lifetime(l) => l, - r => bug!("{:?} is a region but value is {:?}", br, r), - }; + let fld_r = |br: ty::BoundRegion| match var_values.var_values[br.var].unpack() { + GenericArgKind::Lifetime(l) => l, + r => bug!("{:?} is a region but value is {:?}", br, r), + }; let fld_t = |bound_ty: ty::BoundTy| match var_values.var_values[bound_ty.var].unpack() { GenericArgKind::Type(ty) => ty, diff --git a/compiler/rustc_infer/src/infer/combine.rs b/compiler/rustc_infer/src/infer/combine.rs index 5e11932eafc4e..30214e94203d8 100644 --- a/compiler/rustc_infer/src/infer/combine.rs +++ b/compiler/rustc_infer/src/infer/combine.rs @@ -191,7 +191,7 @@ impl<'infcx, 'tcx> InferCtxt<'infcx, 'tcx> { /// /// This also tests if the given const `ct` contains an inference variable which was previously /// unioned with `target_vid`. If this is the case, inferring `target_vid` to `ct` - /// would result in an infinite type as we continously replace an inference variable + /// would result in an infinite type as we continuously replace an inference variable /// in `ct` with `ct` itself. /// /// This is especially important as unevaluated consts use their parents generics. @@ -543,15 +543,11 @@ impl TypeRelation<'tcx> for Generalizer<'_, 'tcx> { true } - fn visit_ct_substs(&self) -> bool { - true - } - fn binders( &mut self, - a: ty::Binder, - b: ty::Binder, - ) -> RelateResult<'tcx, ty::Binder> + a: ty::Binder<'tcx, T>, + b: ty::Binder<'tcx, T>, + ) -> RelateResult<'tcx, ty::Binder<'tcx, T>> where T: Relate<'tcx>, { @@ -737,6 +733,16 @@ impl TypeRelation<'tcx> for Generalizer<'_, 'tcx> { } } } + ty::ConstKind::Unevaluated(ty::Unevaluated { def, substs, promoted }) + if self.tcx().lazy_normalization() => + { + assert_eq!(promoted, None); + let substs = self.relate_with_variance(ty::Variance::Invariant, substs, substs)?; + Ok(self.tcx().mk_const(ty::Const { + ty: c.ty, + val: ty::ConstKind::Unevaluated(ty::Unevaluated { def, substs, promoted }), + })) + } _ => relate::super_relate_consts(self, c, c), } } @@ -822,10 +828,6 @@ impl TypeRelation<'tcx> for ConstInferUnifier<'_, 'tcx> { true } - fn visit_ct_substs(&self) -> bool { - true - } - fn relate_with_variance>( &mut self, _variance: ty::Variance, @@ -838,9 +840,9 @@ impl TypeRelation<'tcx> for ConstInferUnifier<'_, 'tcx> { fn binders( &mut self, - a: ty::Binder, - b: ty::Binder, - ) -> RelateResult<'tcx, ty::Binder> + a: ty::Binder<'tcx, T>, + b: ty::Binder<'tcx, T>, + ) -> RelateResult<'tcx, ty::Binder<'tcx, T>> where T: Relate<'tcx>, { @@ -959,6 +961,16 @@ impl TypeRelation<'tcx> for ConstInferUnifier<'_, 'tcx> { } } } + ty::ConstKind::Unevaluated(ty::Unevaluated { def, substs, promoted }) + if self.tcx().lazy_normalization() => + { + assert_eq!(promoted, None); + let substs = self.relate_with_variance(ty::Variance::Invariant, substs, substs)?; + Ok(self.tcx().mk_const(ty::Const { + ty: c.ty, + val: ty::ConstKind::Unevaluated(ty::Unevaluated { def, substs, promoted }), + })) + } _ => relate::super_relate_consts(self, c, c), } } diff --git a/compiler/rustc_infer/src/infer/equate.rs b/compiler/rustc_infer/src/infer/equate.rs index 7c388b5503ee3..45ba50bb6349c 100644 --- a/compiler/rustc_infer/src/infer/equate.rs +++ b/compiler/rustc_infer/src/infer/equate.rs @@ -124,9 +124,9 @@ impl TypeRelation<'tcx> for Equate<'combine, 'infcx, 'tcx> { fn binders( &mut self, - a: ty::Binder, - b: ty::Binder, - ) -> RelateResult<'tcx, ty::Binder> + a: ty::Binder<'tcx, T>, + b: ty::Binder<'tcx, T>, + ) -> RelateResult<'tcx, ty::Binder<'tcx, T>> where T: Relate<'tcx>, { diff --git a/compiler/rustc_infer/src/infer/error_reporting/mod.rs b/compiler/rustc_infer/src/infer/error_reporting/mod.rs index eeff48a63950e..a91bd9ce2ff74 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/mod.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/mod.rs @@ -67,13 +67,13 @@ use rustc_hir::{Item, ItemKind, Node}; use rustc_middle::ty::error::TypeError; use rustc_middle::ty::{ self, - subst::{Subst, SubstsRef}, + subst::{GenericArgKind, Subst, SubstsRef}, Region, Ty, TyCtxt, TypeFoldable, }; use rustc_span::{sym, BytePos, DesugaringKind, Pos, Span}; use rustc_target::spec::abi; use std::ops::ControlFlow; -use std::{cmp, fmt}; +use std::{cmp, fmt, iter}; mod note; @@ -514,7 +514,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { fn print_dyn_existential( self, - _predicates: &'tcx ty::List>>, + _predicates: &'tcx ty::List>>, ) -> Result { Err(NonTrivialPath) } @@ -957,33 +957,27 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { ) -> SubstsRef<'tcx> { let generics = self.tcx.generics_of(def_id); let mut num_supplied_defaults = 0; - let mut type_params = generics - .params - .iter() - .rev() - .filter_map(|param| match param.kind { - ty::GenericParamDefKind::Lifetime => None, - ty::GenericParamDefKind::Type { has_default, .. } => { - Some((param.def_id, has_default)) - } - ty::GenericParamDefKind::Const => None, // FIXME(const_generics_defaults) - }) - .peekable(); - let has_default = { - let has_default = type_params.peek().map(|(_, has_default)| has_default); - *has_default.unwrap_or(&false) - }; - if has_default { - let types = substs.types().rev(); - for ((def_id, has_default), actual) in type_params.zip(types) { - if !has_default { - break; + + let default_params = generics.params.iter().rev().filter_map(|param| match param.kind { + ty::GenericParamDefKind::Type { has_default: true, .. } => Some(param.def_id), + ty::GenericParamDefKind::Const { has_default: true } => Some(param.def_id), + _ => None, + }); + for (def_id, actual) in iter::zip(default_params, substs.iter().rev()) { + match actual.unpack() { + GenericArgKind::Const(c) => { + if self.tcx.const_param_default(def_id).subst(self.tcx, substs) != c { + break; + } } - if self.tcx.type_of(def_id).subst(self.tcx, substs) != actual { - break; + GenericArgKind::Type(ty) => { + if self.tcx.type_of(def_id).subst(self.tcx, substs) != ty { + break; + } } - num_supplied_defaults += 1; + _ => break, } + num_supplied_defaults += 1; } let len = generics.params.len(); let mut generics = generics.clone(); @@ -1046,7 +1040,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { let len1 = sig1.inputs().len(); let len2 = sig2.inputs().len(); if len1 == len2 { - for (i, (l, r)) in sig1.inputs().iter().zip(sig2.inputs().iter()).enumerate() { + for (i, (l, r)) in iter::zip(sig1.inputs(), sig2.inputs()).enumerate() { let (x1, x2) = self.cmp(l, r); (values.0).0.extend(x1.0); (values.1).0.extend(x2.0); @@ -1167,12 +1161,10 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { let common_len = cmp::min(len1, len2); let remainder1: Vec<_> = sub1.types().skip(common_len).collect(); let remainder2: Vec<_> = sub2.types().skip(common_len).collect(); - let common_default_params = remainder1 - .iter() - .rev() - .zip(remainder2.iter().rev()) - .filter(|(a, b)| a == b) - .count(); + let common_default_params = + iter::zip(remainder1.iter().rev(), remainder2.iter().rev()) + .filter(|(a, b)| a == b) + .count(); let len = sub1.len() - common_default_params; let consts_offset = len - sub1.consts().count(); @@ -1303,12 +1295,11 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { const SEPARATOR: &str = "::"; let separator_len = SEPARATOR.len(); - let split_idx: usize = t1_str - .split(SEPARATOR) - .zip(t2_str.split(SEPARATOR)) - .take_while(|(mod1_str, mod2_str)| mod1_str == mod2_str) - .map(|(mod_str, _)| mod_str.len() + separator_len) - .sum(); + let split_idx: usize = + iter::zip(t1_str.split(SEPARATOR), t2_str.split(SEPARATOR)) + .take_while(|(mod1_str, mod2_str)| mod1_str == mod2_str) + .map(|(mod_str, _)| mod_str.len() + separator_len) + .sum(); debug!( "cmp: separator_len={}, split_idx={}, min_len={}", @@ -1913,7 +1904,9 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { .find_map(|(path, msg)| (&path_str == path).then_some(msg)) { let mut show_suggestion = true; - for (exp_ty, found_ty) in exp_substs.types().zip(found_substs.types()) { + for (exp_ty, found_ty) in + iter::zip(exp_substs.types(), found_substs.types()) + { match *exp_ty.kind() { ty::Ref(_, exp_ty, _) => { match (exp_ty.kind(), found_ty.kind()) { 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 d533e267fd702..d9a1193aac4ba 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 @@ -287,6 +287,7 @@ pub struct InferenceDiagnosticsData { pub struct InferenceDiagnosticsParentData { pub prefix: &'static str, pub name: String, + pub def_id: DefId, } pub enum UnderspecifiedArgKind { @@ -328,6 +329,7 @@ impl InferenceDiagnosticsParentData { Some(InferenceDiagnosticsParentData { prefix: tcx.def_kind(parent_def_id).descr(parent_def_id), name: parent_name, + def_id: parent_def_id, }) } } @@ -754,12 +756,30 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { if let (UnderspecifiedArgKind::Const { .. }, Some(parent_data)) = (&arg_data.kind, &arg_data.parent) { - err.span_suggestion_verbose( - span, - "consider specifying the const argument", - format!("{}::<{}>", parent_data.name, arg_data.name), - Applicability::MaybeIncorrect, - ); + let has_impl_trait = + self.tcx.generics_of(parent_data.def_id).params.iter().any(|param| { + matches!( + param.kind, + ty::GenericParamDefKind::Type { + synthetic: Some( + hir::SyntheticTyParamKind::ImplTrait + | hir::SyntheticTyParamKind::FromAttr, + ), + .. + } + ) + }); + + // (#83606): Do not emit a suggestion if the parent has an `impl Trait` + // as an argument otherwise it will cause the E0282 error. + if !has_impl_trait { + err.span_suggestion_verbose( + span, + "consider specifying the const argument", + format!("{}::<{}>", parent_data.name, arg_data.name), + Applicability::MaybeIncorrect, + ); + } } err.span_label( diff --git a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/find_anon_type.rs b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/find_anon_type.rs index d9ab831904551..58eb1e9aa12cb 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/find_anon_type.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/find_anon_type.rs @@ -99,7 +99,7 @@ impl Visitor<'tcx> for FindNestedTypeVisitor<'tcx> { return; } - hir::TyKind::TraitObject(bounds, _) => { + hir::TyKind::TraitObject(bounds, ..) => { for bound in bounds { self.current_index.shift_in(1); self.visit_poly_trait_ref(bound, hir::TraitBoundModifier::None); @@ -115,7 +115,7 @@ impl Visitor<'tcx> for FindNestedTypeVisitor<'tcx> { // error. We will then search the function parameters for a bound // region at the right depth with the same index ( - Some(rl::Region::LateBoundAnon(debruijn_index, anon_index)), + Some(rl::Region::LateBoundAnon(debruijn_index, _, anon_index)), ty::BrAnon(br_index), ) => { debug!( @@ -143,7 +143,7 @@ impl Visitor<'tcx> for FindNestedTypeVisitor<'tcx> { // error. We will then search the function parameters for a bound // region at the right depth with the same index ( - Some(rl::Region::LateBound(debruijn_index, id, _)), + Some(rl::Region::LateBound(debruijn_index, _, id, _)), ty::BrNamed(def_id, _), ) => { debug!( @@ -162,8 +162,8 @@ impl Visitor<'tcx> for FindNestedTypeVisitor<'tcx> { rl::Region::Static | rl::Region::Free(_, _) | rl::Region::EarlyBound(_, _, _) - | rl::Region::LateBound(_, _, _) - | rl::Region::LateBoundAnon(_, _), + | rl::Region::LateBound(_, _, _, _) + | rl::Region::LateBoundAnon(_, _, _), ) | None, _, @@ -217,7 +217,10 @@ impl Visitor<'tcx> for TyPathVisitor<'tcx> { fn visit_lifetime(&mut self, lifetime: &hir::Lifetime) { match (self.tcx.named_region(lifetime.hir_id), self.bound_region) { // the lifetime of the TyPath! - (Some(rl::Region::LateBoundAnon(debruijn_index, anon_index)), ty::BrAnon(br_index)) => { + ( + Some(rl::Region::LateBoundAnon(debruijn_index, _, anon_index)), + ty::BrAnon(br_index), + ) => { if debruijn_index == self.current_index && anon_index == br_index { self.found_it = true; return; @@ -232,7 +235,7 @@ impl Visitor<'tcx> for TyPathVisitor<'tcx> { } } - (Some(rl::Region::LateBound(debruijn_index, id, _)), ty::BrNamed(def_id, _)) => { + (Some(rl::Region::LateBound(debruijn_index, _, id, _)), ty::BrNamed(def_id, _)) => { debug!("FindNestedTypeVisitor::visit_ty: LateBound depth = {:?}", debruijn_index,); debug!("id={:?}", id); debug!("def_id={:?}", def_id); @@ -246,8 +249,8 @@ impl Visitor<'tcx> for TyPathVisitor<'tcx> { Some( rl::Region::Static | rl::Region::EarlyBound(_, _, _) - | rl::Region::LateBound(_, _, _) - | rl::Region::LateBoundAnon(_, _) + | rl::Region::LateBound(_, _, _, _) + | rl::Region::LateBoundAnon(_, _, _) | rl::Region::Free(_, _), ) | None, diff --git a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/static_impl_trait.rs b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/static_impl_trait.rs index fa0d5b8301349..1e926989263c9 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/static_impl_trait.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/static_impl_trait.rs @@ -292,7 +292,7 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { ); } } - TyKind::TraitObject(_, lt) => match lt.name { + TyKind::TraitObject(_, lt, _) => match lt.name { LifetimeName::ImplicitObjectLifetimeDefault => { err.span_suggestion_verbose( fn_return.span.shrink_to_hi(), @@ -498,6 +498,7 @@ impl<'tcx> Visitor<'tcx> for HirTraitObjectVisitor { if let TyKind::TraitObject( poly_trait_refs, Lifetime { name: LifetimeName::ImplicitObjectLifetimeDefault, .. }, + _, ) = t.kind { for ptr in poly_trait_refs { diff --git a/compiler/rustc_infer/src/infer/glb.rs b/compiler/rustc_infer/src/infer/glb.rs index ccba904df9e00..02662043dba79 100644 --- a/compiler/rustc_infer/src/infer/glb.rs +++ b/compiler/rustc_infer/src/infer/glb.rs @@ -85,9 +85,9 @@ impl TypeRelation<'tcx> for Glb<'combine, 'infcx, 'tcx> { fn binders( &mut self, - a: ty::Binder, - b: ty::Binder, - ) -> RelateResult<'tcx, ty::Binder> + a: ty::Binder<'tcx, T>, + b: ty::Binder<'tcx, T>, + ) -> RelateResult<'tcx, ty::Binder<'tcx, T>> where T: Relate<'tcx>, { diff --git a/compiler/rustc_infer/src/infer/higher_ranked/mod.rs b/compiler/rustc_infer/src/infer/higher_ranked/mod.rs index e794903fca3aa..d460222df8ad0 100644 --- a/compiler/rustc_infer/src/infer/higher_ranked/mod.rs +++ b/compiler/rustc_infer/src/infer/higher_ranked/mod.rs @@ -11,10 +11,10 @@ use rustc_middle::ty::{self, Binder, TypeFoldable}; impl<'a, 'tcx> CombineFields<'a, 'tcx> { pub fn higher_ranked_sub( &mut self, - a: Binder, - b: Binder, + a: Binder<'tcx, T>, + b: Binder<'tcx, T>, a_is_expected: bool, - ) -> RelateResult<'tcx, Binder> + ) -> RelateResult<'tcx, Binder<'tcx, T>> where T: Relate<'tcx>, { @@ -50,7 +50,10 @@ impl<'a, 'tcx> CombineFields<'a, 'tcx> { debug!("higher_ranked_sub: OK result={:?}", result); - Ok(ty::Binder::bind(result)) + // We related `a_prime` and `b_prime`, which just had any bound vars + // replaced with placeholders or infer vars, respectively. Relating + // them should not introduce new bound vars. + Ok(ty::Binder::dummy(result)) }) } } @@ -66,7 +69,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { /// the [rustc dev guide]. /// /// [rustc dev guide]: https://rustc-dev-guide.rust-lang.org/traits/hrtb.html - pub fn replace_bound_vars_with_placeholders(&self, binder: ty::Binder) -> T + pub fn replace_bound_vars_with_placeholders(&self, binder: ty::Binder<'tcx, T>) -> T where T: TypeFoldable<'tcx>, { diff --git a/compiler/rustc_infer/src/infer/lub.rs b/compiler/rustc_infer/src/infer/lub.rs index 9f43fac0916b5..4fa8f2f1a6a42 100644 --- a/compiler/rustc_infer/src/infer/lub.rs +++ b/compiler/rustc_infer/src/infer/lub.rs @@ -85,9 +85,9 @@ impl TypeRelation<'tcx> for Lub<'combine, 'infcx, 'tcx> { fn binders( &mut self, - a: ty::Binder, - b: ty::Binder, - ) -> RelateResult<'tcx, ty::Binder> + a: ty::Binder<'tcx, T>, + b: ty::Binder<'tcx, T>, + ) -> RelateResult<'tcx, ty::Binder<'tcx, T>> where T: Relate<'tcx>, { diff --git a/compiler/rustc_infer/src/infer/mod.rs b/compiler/rustc_infer/src/infer/mod.rs index 09eecd715f03b..eaec6b46bcd14 100644 --- a/compiler/rustc_infer/src/infer/mod.rs +++ b/compiler/rustc_infer/src/infer/mod.rs @@ -18,7 +18,6 @@ use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_middle::infer::canonical::{Canonical, CanonicalVarValues}; use rustc_middle::infer::unify_key::{ConstVarValue, ConstVariableValue}; use rustc_middle::infer::unify_key::{ConstVariableOrigin, ConstVariableOriginKind, ToType}; -use rustc_middle::mir; use rustc_middle::mir::interpret::EvalToConstValueResult; use rustc_middle::traits::select; use rustc_middle::ty::error::{ExpectedFound, TypeError, UnconstrainedNumeric}; @@ -408,7 +407,7 @@ pub enum SubregionOrigin<'tcx> { } // `SubregionOrigin` is used a lot. Make sure it doesn't unintentionally get bigger. -#[cfg(target_arch = "x86_64")] +#[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))] static_assert_size!(SubregionOrigin<'_>, 32); /// Times when we replace late-bound regions with variables: @@ -1267,15 +1266,6 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { self.resolve_vars_if_possible(t).to_string() } - pub fn tys_to_string(&self, ts: &[Ty<'tcx>]) -> String { - let tstrs: Vec = ts.iter().map(|t| self.ty_to_string(*t)).collect(); - format!("({})", tstrs.join(", ")) - } - - pub fn trait_ref_to_string(&self, t: ty::TraitRef<'tcx>) -> String { - self.resolve_vars_if_possible(t).print_only_trait_path().to_string() - } - /// If `TyVar(vid)` resolves to a type, return that type. Else, return the /// universe index of `TyVar(vid)`. pub fn probe_ty_var(&self, vid: TyVid) -> Result, ty::UniverseIndex> { @@ -1416,7 +1406,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { &self, span: Span, lbrct: LateBoundRegionConversionTime, - value: ty::Binder, + value: ty::Binder<'tcx, T>, ) -> (T, BTreeMap>) where T: TypeFoldable<'tcx>, @@ -1499,9 +1489,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { pub fn const_eval_resolve( &self, param_env: ty::ParamEnv<'tcx>, - def: ty::WithOptConstParam, - substs: SubstsRef<'tcx>, - promoted: Option, + ty::Unevaluated { def, substs, promoted }: ty::Unevaluated<'tcx>, span: Option, ) -> EvalToConstValueResult<'tcx> { let mut original_values = OriginalQueryValues::default(); @@ -1510,7 +1498,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { let (param_env, substs) = canonical.value; // The return value is the evaluated value which doesn't contain any reference to inference // variables, thus we don't need to substitute back the original values. - self.tcx.const_eval_resolve(param_env, def, substs, promoted, span) + self.tcx.const_eval_resolve(param_env, ty::Unevaluated { def, substs, promoted }, span) } /// If `typ` is a type variable of some kind, resolve it one level @@ -1707,14 +1695,6 @@ impl<'tcx> TypeTrace<'tcx> { ) -> TypeTrace<'tcx> { TypeTrace { cause: cause.clone(), values: Consts(ExpectedFound::new(a_is_expected, a, b)) } } - - pub fn dummy(tcx: TyCtxt<'tcx>) -> TypeTrace<'tcx> { - let err = tcx.ty_error(); - TypeTrace { - cause: ObligationCause::dummy(), - values: Types(ExpectedFound { expected: err, found: err }), - } - } } impl<'tcx> SubregionOrigin<'tcx> { diff --git a/compiler/rustc_infer/src/infer/nll_relate/mod.rs b/compiler/rustc_infer/src/infer/nll_relate/mod.rs index e5eb771603cd7..077d2cc20a25c 100644 --- a/compiler/rustc_infer/src/infer/nll_relate/mod.rs +++ b/compiler/rustc_infer/src/infer/nll_relate/mod.rs @@ -157,7 +157,7 @@ where fn create_scope( &mut self, - value: ty::Binder>, + value: ty::Binder<'tcx, impl Relate<'tcx>>, universally_quantified: UniversallyQuantified, ) -> BoundRegionScope<'tcx> { let mut scope = BoundRegionScope::default(); @@ -279,7 +279,7 @@ where /// Relate a type inference variable with a value type. This works /// by creating a "generalization" G of the value where all the /// lifetimes are replaced with fresh inference values. This - /// genearlization G becomes the value of the inference variable, + /// generalization G becomes the value of the inference variable, /// and is then related in turn to the value. So e.g. if you had /// `vid = ?0` and `value = &'a u32`, we might first instantiate /// `?0` to a type like `&'0 u32` where `'0` is a fresh variable, @@ -608,9 +608,9 @@ where fn binders( &mut self, - a: ty::Binder, - b: ty::Binder, - ) -> RelateResult<'tcx, ty::Binder> + a: ty::Binder<'tcx, T>, + b: ty::Binder<'tcx, T>, + ) -> RelateResult<'tcx, ty::Binder<'tcx, T>> where T: Relate<'tcx>, { @@ -744,7 +744,7 @@ struct ScopeInstantiator<'me, 'tcx> { impl<'me, 'tcx> TypeVisitor<'tcx> for ScopeInstantiator<'me, 'tcx> { fn visit_binder>( &mut self, - t: &ty::Binder, + t: &ty::Binder<'tcx, T>, ) -> ControlFlow { self.target_index.shift_in(1); t.super_visit_with(self); @@ -997,9 +997,9 @@ where fn binders( &mut self, - a: ty::Binder, - _: ty::Binder, - ) -> RelateResult<'tcx, ty::Binder> + a: ty::Binder<'tcx, T>, + _: ty::Binder<'tcx, T>, + ) -> RelateResult<'tcx, ty::Binder<'tcx, T>> where T: Relate<'tcx>, { diff --git a/compiler/rustc_infer/src/infer/outlives/env.rs b/compiler/rustc_infer/src/infer/outlives/env.rs index 1a9e20e79fe1e..9e04773c5fa20 100644 --- a/compiler/rustc_infer/src/infer/outlives/env.rs +++ b/compiler/rustc_infer/src/infer/outlives/env.rs @@ -92,11 +92,6 @@ impl<'a, 'tcx> OutlivesEnvironment<'tcx> { &self.region_bound_pairs_map } - /// Returns ownership of the `free_region_map`. - pub fn into_free_region_map(self) -> FreeRegionMap<'tcx> { - self.free_region_map - } - /// This is a hack to support the old-skool regionck, which /// processes region constraints from the main function and the /// closure together. In that context, when we enter a closure, we diff --git a/compiler/rustc_infer/src/infer/outlives/obligations.rs b/compiler/rustc_infer/src/infer/outlives/obligations.rs index 16d86e6243de4..3e2978fd170ae 100644 --- a/compiler/rustc_infer/src/infer/outlives/obligations.rs +++ b/compiler/rustc_infer/src/infer/outlives/obligations.rs @@ -186,28 +186,6 @@ impl<'cx, 'tcx> InferCtxt<'cx, 'tcx> { } } } - - /// Processes a single ad-hoc region obligation that was not - /// registered in advance. - pub fn type_must_outlive( - &self, - region_bound_pairs: &RegionBoundPairs<'tcx>, - implicit_region_bound: Option>, - param_env: ty::ParamEnv<'tcx>, - origin: infer::SubregionOrigin<'tcx>, - ty: Ty<'tcx>, - region: ty::Region<'tcx>, - ) { - let outlives = &mut TypeOutlives::new( - self, - self.tcx, - region_bound_pairs, - implicit_region_bound, - param_env, - ); - let ty = self.resolve_vars_if_possible(ty); - outlives.type_must_outlive(origin, ty, region); - } } /// The `TypeOutlives` struct has the job of "lowering" a `T: 'a` diff --git a/compiler/rustc_infer/src/infer/region_constraints/mod.rs b/compiler/rustc_infer/src/infer/region_constraints/mod.rs index 2902c41a6bcae..9ffcddfae9944 100644 --- a/compiler/rustc_infer/src/infer/region_constraints/mod.rs +++ b/compiler/rustc_infer/src/infer/region_constraints/mod.rs @@ -309,31 +309,6 @@ pub struct RegionSnapshot { any_unifications: bool, } -/// When working with placeholder regions, we often wish to find all of -/// the regions that are either reachable from a placeholder region, or -/// which can reach a placeholder region, or both. We call such regions -/// *tainted* regions. This struct allows you to decide what set of -/// tainted regions you want. -#[derive(Debug)] -pub struct TaintDirections { - incoming: bool, - outgoing: bool, -} - -impl TaintDirections { - pub fn incoming() -> Self { - TaintDirections { incoming: true, outgoing: false } - } - - pub fn outgoing() -> Self { - TaintDirections { incoming: false, outgoing: true } - } - - pub fn both() -> Self { - TaintDirections { incoming: true, outgoing: true } - } -} - impl<'tcx> RegionConstraintStorage<'tcx> { pub fn new() -> Self { Self::default() @@ -472,11 +447,6 @@ impl<'tcx> RegionConstraintCollector<'_, 'tcx> { self.var_infos[vid].universe } - /// Returns the origin for the given variable. - pub fn var_origin(&self, vid: RegionVid) -> RegionVariableOrigin { - self.var_infos[vid].origin - } - fn add_constraint(&mut self, constraint: Constraint<'tcx>, origin: SubregionOrigin<'tcx>) { // cannot add constraints once regions are resolved debug!("RegionConstraintCollector: add_constraint({:?})", constraint); @@ -795,16 +765,6 @@ impl<'tcx> VerifyBound<'tcx> { VerifyBound::AnyBound(vec![self, vb]) } } - - pub fn and(self, vb: VerifyBound<'tcx>) -> VerifyBound<'tcx> { - if self.must_hold() && vb.must_hold() { - self - } else if self.cannot_hold() && vb.cannot_hold() { - self - } else { - VerifyBound::AllBounds(vec![self, vb]) - } - } } impl<'tcx> RegionConstraintData<'tcx> { diff --git a/compiler/rustc_infer/src/infer/resolve.rs b/compiler/rustc_infer/src/infer/resolve.rs index d72be0134fb4c..48b8ee17594e3 100644 --- a/compiler/rustc_infer/src/infer/resolve.rs +++ b/compiler/rustc_infer/src/infer/resolve.rs @@ -1,5 +1,6 @@ use super::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; use super::{FixupError, FixupResult, InferCtxt, Span}; +use rustc_middle::mir; use rustc_middle::ty::fold::{TypeFolder, TypeVisitor}; use rustc_middle::ty::{self, Const, InferConst, Ty, TyCtxt, TypeFoldable}; @@ -46,6 +47,10 @@ impl<'a, 'tcx> TypeFolder<'tcx> for OpportunisticVarResolver<'a, 'tcx> { ct.super_fold_with(self) } } + + fn fold_mir_const(&mut self, constant: mir::ConstantKind<'tcx>) -> mir::ConstantKind<'tcx> { + constant.super_fold_with(self) + } } /// The opportunistic region resolver opportunistically resolves regions diff --git a/compiler/rustc_infer/src/infer/sub.rs b/compiler/rustc_infer/src/infer/sub.rs index 668719851583c..bf5f328233dfd 100644 --- a/compiler/rustc_infer/src/infer/sub.rs +++ b/compiler/rustc_infer/src/infer/sub.rs @@ -162,9 +162,9 @@ impl TypeRelation<'tcx> for Sub<'combine, 'infcx, 'tcx> { fn binders( &mut self, - a: ty::Binder, - b: ty::Binder, - ) -> RelateResult<'tcx, ty::Binder> + a: ty::Binder<'tcx, T>, + b: ty::Binder<'tcx, T>, + ) -> RelateResult<'tcx, ty::Binder<'tcx, T>> where T: Relate<'tcx>, { diff --git a/compiler/rustc_infer/src/infer/type_variable.rs b/compiler/rustc_infer/src/infer/type_variable.rs index 35b97fff3da1f..683c1df783e63 100644 --- a/compiler/rustc_infer/src/infer/type_variable.rs +++ b/compiler/rustc_infer/src/infer/type_variable.rs @@ -146,9 +146,7 @@ impl<'tcx> TypeVariableValue<'tcx> { } } -pub(crate) struct Instantiate { - vid: ty::TyVid, -} +pub(crate) struct Instantiate; pub(crate) struct Delegate; @@ -224,7 +222,7 @@ impl<'tcx> TypeVariableTable<'_, 'tcx> { // Hack: we only need this so that `types_escaping_snapshot` // can see what has been unified; see the Delegate impl for // more details. - self.undo_log.push(Instantiate { vid }); + self.undo_log.push(Instantiate); } /// Creates a new type variable. @@ -346,56 +344,6 @@ impl<'tcx> TypeVariableTable<'_, 'tcx> { ) } - /// Finds the set of type variables that existed *before* `s` - /// but which have only been unified since `s` started, and - /// return the types with which they were unified. So if we had - /// a type variable `V0`, then we started the snapshot, then we - /// created a type variable `V1`, unified `V0` with `T0`, and - /// unified `V1` with `T1`, this function would return `{T0}`. - pub fn types_escaping_snapshot(&mut self, s: &super::Snapshot<'tcx>) -> Vec> { - let mut new_elem_threshold = u32::MAX; - let mut escaping_types = Vec::new(); - let actions_since_snapshot = self.undo_log.actions_since_snapshot(s); - debug!("actions_since_snapshot.len() = {}", actions_since_snapshot.len()); - for i in 0..actions_since_snapshot.len() { - let actions_since_snapshot = self.undo_log.actions_since_snapshot(s); - match actions_since_snapshot[i] { - super::UndoLog::TypeVariables(UndoLog::Values(sv::UndoLog::NewElem(index))) => { - // if any new variables were created during the - // snapshot, remember the lower index (which will - // always be the first one we see). Note that this - // action must precede those variables being - // specified. - new_elem_threshold = cmp::min(new_elem_threshold, index as u32); - debug!("NewElem({}) new_elem_threshold={}", index, new_elem_threshold); - } - - super::UndoLog::TypeVariables(UndoLog::Values(sv::UndoLog::Other( - Instantiate { vid, .. }, - ))) => { - if vid.index < new_elem_threshold { - // quick check to see if this variable was - // created since the snapshot started or not. - let mut eq_relations = ut::UnificationTable::with_log( - &mut self.storage.eq_relations, - &mut *self.undo_log, - ); - let escaping_type = match eq_relations.probe_value(vid) { - TypeVariableValue::Unknown { .. } => bug!(), - TypeVariableValue::Known { value } => value, - }; - escaping_types.push(escaping_type); - } - debug!("SpecifyVar({:?}) new_elem_threshold={}", vid, new_elem_threshold); - } - - _ => {} - } - } - - escaping_types - } - /// Returns indices of all variables that are not yet /// instantiated. pub fn unsolved_variables(&mut self) -> Vec { diff --git a/compiler/rustc_infer/src/infer/undo_log.rs b/compiler/rustc_infer/src/infer/undo_log.rs index 4be0e7948f70b..f41e872e00488 100644 --- a/compiler/rustc_infer/src/infer/undo_log.rs +++ b/compiler/rustc_infer/src/infer/undo_log.rs @@ -165,10 +165,6 @@ impl<'tcx> InferCtxtInner<'tcx> { } impl<'tcx> InferCtxtUndoLogs<'tcx> { - pub fn actions_since_snapshot(&self, snapshot: &Snapshot<'tcx>) -> &[UndoLog<'tcx>] { - &self.logs[snapshot.undo_len..] - } - pub fn start_snapshot(&mut self) -> Snapshot<'tcx> { self.num_open_snapshots += 1; Snapshot { undo_len: self.logs.len(), _marker: PhantomData } diff --git a/compiler/rustc_infer/src/lib.rs b/compiler/rustc_infer/src/lib.rs index 3690a88c0d973..25a262d7e482b 100644 --- a/compiler/rustc_infer/src/lib.rs +++ b/compiler/rustc_infer/src/lib.rs @@ -2,7 +2,7 @@ //! //! - **Type inference.** The type inference code can be found in the `infer` module; //! this code handles low-level equality and subtyping operations. The -//! type check pass in the compiler is found in the `librustc_typeck` crate. +//! type check pass in the compiler is found in the `rustc_typeck` crate. //! //! For more information about how rustc works, see the [rustc dev guide]. //! @@ -19,15 +19,16 @@ #![feature(const_fn)] #![feature(const_panic)] #![feature(extend_one)] +#![feature(iter_zip)] #![feature(never_type)] -#![feature(or_patterns)] +#![cfg_attr(bootstrap, feature(or_patterns))] #![feature(in_band_lifetimes)] #![feature(control_flow_enum)] #![recursion_limit = "512"] // For rustdoc #[macro_use] extern crate rustc_macros; -#[cfg(target_arch = "x86_64")] +#[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))] #[macro_use] extern crate rustc_data_structures; #[macro_use] diff --git a/compiler/rustc_infer/src/traits/error_reporting/mod.rs b/compiler/rustc_infer/src/traits/error_reporting/mod.rs index 835f75ec8ef06..0ac4b6b25bbfe 100644 --- a/compiler/rustc_infer/src/traits/error_reporting/mod.rs +++ b/compiler/rustc_infer/src/traits/error_reporting/mod.rs @@ -9,6 +9,7 @@ use rustc_middle::ty::TyCtxt; use rustc_span::symbol::Symbol; use rustc_span::{MultiSpan, Span}; use std::fmt; +use std::iter; impl<'a, 'tcx> InferCtxt<'a, 'tcx> { pub fn report_extra_impl_obligation( @@ -94,7 +95,7 @@ pub fn report_object_safety_error( note_span .push_span_label(trait_span, "this trait cannot be made into an object...".to_string()); } - for (span, msg) in multi_span.into_iter().zip(messages.into_iter()) { + for (span, msg) in iter::zip(multi_span, messages) { note_span.push_span_label(span, msg); } err.span_note( @@ -104,7 +105,7 @@ pub fn report_object_safety_error( ", ); - if tcx.sess.trait_methods_not_found.borrow().contains(&span) { + if tcx.sess.trait_methods_not_found.borrow().iter().any(|full_span| full_span.contains(span)) { // Avoid emitting error caused by non-existing method (#58734) err.cancel(); } diff --git a/compiler/rustc_infer/src/traits/mod.rs b/compiler/rustc_infer/src/traits/mod.rs index aaf5e958c269d..a33234a91faff 100644 --- a/compiler/rustc_infer/src/traits/mod.rs +++ b/compiler/rustc_infer/src/traits/mod.rs @@ -56,7 +56,7 @@ pub type PredicateObligation<'tcx> = Obligation<'tcx, ty::Predicate<'tcx>>; pub type TraitObligation<'tcx> = Obligation<'tcx, ty::PolyTraitPredicate<'tcx>>; // `PredicateObligation` is used a lot. Make sure it doesn't unintentionally get bigger. -#[cfg(target_arch = "x86_64")] +#[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))] static_assert_size!(PredicateObligation<'_>, 32); pub type PredicateObligations<'tcx> = Vec>; @@ -128,7 +128,7 @@ impl<'tcx> FulfillmentError<'tcx> { } impl<'tcx> TraitObligation<'tcx> { - pub fn self_ty(&self) -> ty::Binder> { + pub fn self_ty(&self) -> ty::Binder<'tcx, Ty<'tcx>> { self.predicate.map_bound(|p| p.self_ty()) } } diff --git a/compiler/rustc_interface/Cargo.toml b/compiler/rustc_interface/Cargo.toml index cfe98a630c1be..3bfe8da505f1c 100644 --- a/compiler/rustc_interface/Cargo.toml +++ b/compiler/rustc_interface/Cargo.toml @@ -10,8 +10,8 @@ doctest = false [dependencies] libc = "0.2" tracing = "0.1" -rustc-rayon-core = "0.3.0" -rayon = { version = "0.3.0", package = "rustc-rayon" } +rustc-rayon-core = "0.3.1" +rayon = { version = "0.3.1", package = "rustc-rayon" } smallvec = { version = "1.6.1", features = ["union", "may_dangle"] } rustc_ast = { path = "../rustc_ast" } rustc_attr = { path = "../rustc_attr" } diff --git a/compiler/rustc_interface/src/callbacks.rs b/compiler/rustc_interface/src/callbacks.rs index 7fa1a3eb0f591..3b852b8ccf9d0 100644 --- a/compiler/rustc_interface/src/callbacks.rs +++ b/compiler/rustc_interface/src/callbacks.rs @@ -1,6 +1,6 @@ //! Throughout the compiler tree, there are several places which want to have //! access to state or queries while being inside crates that are dependencies -//! of librustc_middle. To facilitate this, we have the +//! of `rustc_middle`. To facilitate this, we have the //! `rustc_data_structures::AtomicRef` type, which allows us to setup a global //! static which can then be set in this file at program startup. //! @@ -13,8 +13,8 @@ use rustc_errors::{Diagnostic, TRACK_DIAGNOSTICS}; use rustc_middle::ty::tls; use std::fmt; -/// This is a callback from librustc_ast as it cannot access the implicit state -/// in librustc_middle otherwise. +/// This is a callback from `rustc_ast` as it cannot access the implicit state +/// in `rustc_middle` otherwise. fn span_debug(span: rustc_span::Span, f: &mut fmt::Formatter<'_>) -> fmt::Result { tls::with_opt(|tcx| { if let Some(tcx) = tcx { @@ -25,8 +25,8 @@ fn span_debug(span: rustc_span::Span, f: &mut fmt::Formatter<'_>) -> fmt::Result }) } -/// This is a callback from librustc_ast as it cannot access the implicit state -/// in librustc_middle otherwise. It is used to when diagnostic messages are +/// This is a callback from `rustc_ast` as it cannot access the implicit state +/// in `rustc_middle` otherwise. It is used to when diagnostic messages are /// emitted and stores them in the current query, if there is one. fn track_diagnostic(diagnostic: &Diagnostic) { tls::with_context_opt(|icx| { @@ -39,8 +39,8 @@ fn track_diagnostic(diagnostic: &Diagnostic) { }) } -/// This is a callback from librustc_hir as it cannot access the implicit state -/// in librustc_middle otherwise. +/// This is a callback from `rustc_hir` as it cannot access the implicit state +/// in `rustc_middle` otherwise. fn def_id_debug(def_id: rustc_hir::def_id::DefId, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "DefId({}:{}", def_id.krate, def_id.index.index())?; tls::with_opt(|opt_tcx| { diff --git a/compiler/rustc_interface/src/interface.rs b/compiler/rustc_interface/src/interface.rs index 502e7155c2e0d..a1090ee316db4 100644 --- a/compiler/rustc_interface/src/interface.rs +++ b/compiler/rustc_interface/src/interface.rs @@ -142,6 +142,9 @@ pub struct Config { pub lint_caps: FxHashMap, + /// This is a callback from the driver that is called when [`ParseSess`] is created. + pub parse_sess_created: Option>, + /// This is a callback from the driver that is called when we're registering lints; /// it is called during plugin registration when we have the LintStore in a non-shared state. /// @@ -166,7 +169,7 @@ pub struct Config { pub fn create_compiler_and_run(config: Config, f: impl FnOnce(&Compiler) -> R) -> R { let registry = &config.registry; - let (sess, codegen_backend) = util::create_session( + let (mut sess, codegen_backend) = util::create_session( config.opts, config.crate_cfg, config.diagnostic_output, @@ -177,6 +180,14 @@ pub fn create_compiler_and_run(config: Config, f: impl FnOnce(&Compiler) -> R registry.clone(), ); + if let Some(parse_sess_created) = config.parse_sess_created { + parse_sess_created( + &mut Lrc::get_mut(&mut sess) + .expect("create_session() should never share the returned session") + .parse_sess, + ); + } + let compiler = Compiler { sess, codegen_backend, diff --git a/compiler/rustc_interface/src/passes.rs b/compiler/rustc_interface/src/passes.rs index 5217066bbefde..02e62a2cee951 100644 --- a/compiler/rustc_interface/src/passes.rs +++ b/compiler/rustc_interface/src/passes.rs @@ -15,8 +15,8 @@ use rustc_expand::base::ExtCtxt; use rustc_hir::def_id::{CrateNum, LOCAL_CRATE}; use rustc_hir::definitions::Definitions; use rustc_hir::Crate; -use rustc_index::vec::IndexVec; use rustc_lint::LintStore; +use rustc_metadata::creader::CStore; use rustc_middle::arena::Arena; use rustc_middle::dep_graph::DepGraph; use rustc_middle::middle; @@ -302,8 +302,10 @@ fn configure_and_expand_inner<'a>( ..rustc_expand::expand::ExpansionConfig::default(crate_name.to_string()) }; - let extern_mod_loaded = |k: &ast::Crate, ident: Ident| { - pre_expansion_lint(sess, lint_store, k, &*ident.name.as_str()) + let extern_mod_loaded = |ident: Ident, attrs, items, span| { + let krate = ast::Crate { attrs, items, span, proc_macros: vec![] }; + pre_expansion_lint(sess, lint_store, &krate, &ident.name.as_str()); + (krate.attrs, krate.items) }; let mut ecx = ExtCtxt::new(&sess, cfg, &mut resolver, Some(&extern_mod_loaded)); @@ -786,13 +788,7 @@ pub fn create_global_ctxt<'tcx>( callback(sess, &mut local_providers, &mut extern_providers); } - let queries = { - let crates = resolver_outputs.cstore.crates_untracked(); - let max_cnum = crates.iter().map(|c| c.as_usize()).max().unwrap_or(0); - let mut providers = IndexVec::from_elem_n(extern_providers, max_cnum + 1); - providers[LOCAL_CRATE] = local_providers; - queries.get_or_init(|| TcxQueries::new(providers, extern_providers)) - }; + let queries = queries.get_or_init(|| TcxQueries::new(local_providers, extern_providers)); let gcx = sess.time("setup_global_ctxt", || { global_ctxt.get_or_init(|| { @@ -836,6 +832,12 @@ fn analysis(tcx: TyCtxt<'_>, cnum: CrateNum) -> Result<()> { }); sess.time("looking_for_derive_registrar", || proc_macro_decls::find(tcx)); + + let cstore = tcx + .cstore_as_any() + .downcast_ref::() + .expect("`tcx.cstore` is not a `CStore`"); + cstore.report_unused_deps(tcx); }, { par_iter(&tcx.hir().krate().modules).for_each(|(&module, _)| { @@ -988,7 +990,7 @@ fn encode_and_write_metadata( .unwrap_or_else(|err| tcx.sess.fatal(&format!("couldn't create a temp dir: {}", err))); let metadata_tmpdir = MaybeTempDir::new(metadata_tmpdir, tcx.sess.opts.cg.save_temps); let metadata_filename = emit_metadata(tcx.sess, &metadata, &metadata_tmpdir); - if let Err(e) = fs::rename(&metadata_filename, &out_filename) { + if let Err(e) = util::non_durable_rename(&metadata_filename, &out_filename) { tcx.sess.fatal(&format!("failed to write {}: {}", out_filename.display(), e)); } if tcx.sess.opts.json_artifact_notifications { @@ -1026,9 +1028,6 @@ pub fn start_codegen<'tcx>( rustc_symbol_mangling::test::report_symbol_names(tcx); } - tcx.sess.time("assert_dep_graph", || rustc_incremental::assert_dep_graph(tcx)); - tcx.sess.time("serialize_dep_graph", || rustc_incremental::save_dep_graph(tcx)); - info!("Post-codegen\n{:?}", tcx.debug_stats()); if tcx.sess.opts.output_types.contains_key(&OutputType::Mir) { diff --git a/compiler/rustc_interface/src/proc_macro_decls.rs b/compiler/rustc_interface/src/proc_macro_decls.rs index d0262935c894c..4637055a82ded 100644 --- a/compiler/rustc_interface/src/proc_macro_decls.rs +++ b/compiler/rustc_interface/src/proc_macro_decls.rs @@ -25,7 +25,8 @@ struct Finder<'tcx> { impl<'v> ItemLikeVisitor<'v> for Finder<'_> { fn visit_item(&mut self, item: &hir::Item<'_>) { - if self.tcx.sess.contains_name(&item.attrs, sym::rustc_proc_macro_decls) { + let attrs = self.tcx.hir().attrs(item.hir_id()); + if self.tcx.sess.contains_name(attrs, sym::rustc_proc_macro_decls) { self.decls = Some(item.hir_id()); } } diff --git a/compiler/rustc_interface/src/queries.rs b/compiler/rustc_interface/src/queries.rs index 9c38d2b91ab31..01853eab530da 100644 --- a/compiler/rustc_interface/src/queries.rs +++ b/compiler/rustc_interface/src/queries.rs @@ -207,7 +207,13 @@ impl<'tcx> Queries<'tcx> { }) .open(self.session()) }); - DepGraph::new(prev_graph, prev_work_products) + + rustc_incremental::build_dep_graph( + self.session(), + prev_graph, + prev_work_products, + ) + .unwrap_or_else(DepGraph::new_disabled) } }) }) @@ -435,6 +441,9 @@ impl Compiler { if self.session().opts.debugging_opts.query_stats { gcx.enter(rustc_query_impl::print_stats); } + + self.session() + .time("serialize_dep_graph", || gcx.enter(rustc_incremental::save_dep_graph)); } _timer = Some(self.session().timer("free_global_ctxt")); diff --git a/compiler/rustc_interface/src/tests.rs b/compiler/rustc_interface/src/tests.rs index 9a11b53488786..9685d21762b7c 100644 --- a/compiler/rustc_interface/src/tests.rs +++ b/compiler/rustc_interface/src/tests.rs @@ -2,12 +2,13 @@ use crate::interface::parse_cfgspecs; use rustc_data_structures::fx::FxHashSet; use rustc_errors::{emitter::HumanReadableErrorType, registry, ColorConfig}; +use rustc_session::config::InstrumentCoverage; use rustc_session::config::Strip; use rustc_session::config::{build_configuration, build_session_options, to_crate_config}; use rustc_session::config::{rustc_optgroups, ErrorOutputType, ExternLocation, Options, Passes}; use rustc_session::config::{CFGuard, ExternEntry, LinkerPluginLto, LtoCli, SwitchWithOptPath}; use rustc_session::config::{ - Externs, OutputType, OutputTypes, SanitizerSet, SymbolManglingVersion, WasiExecModel, + Externs, OutputType, OutputTypes, SymbolManglingVersion, WasiExecModel, }; use rustc_session::lint::Level; use rustc_session::search_paths::SearchPath; @@ -17,7 +18,7 @@ use rustc_span::edition::{Edition, DEFAULT_EDITION}; use rustc_span::symbol::sym; use rustc_span::SourceFileHashAlgorithm; use rustc_target::spec::{CodeModel, LinkerFlavor, MergeFunctions, PanicStrategy}; -use rustc_target::spec::{RelocModel, RelroLevel, SplitDebuginfo, TlsModel}; +use rustc_target::spec::{RelocModel, RelroLevel, SanitizerSet, SplitDebuginfo, TlsModel}; use std::collections::{BTreeMap, BTreeSet}; use std::iter::FromIterator; use std::num::NonZeroUsize; @@ -404,7 +405,6 @@ fn test_codegen_options_tracking_hash() { untracked!(incremental, Some(String::from("abc"))); // `link_arg` is omitted because it just forwards to `link_args`. untracked!(link_args, vec![String::from("abc"), String::from("def")]); - untracked!(link_dead_code, Some(true)); untracked!(link_self_contained, Some(true)); untracked!(linker, Some(PathBuf::from("linker"))); untracked!(linker_flavor, Some(LinkerFlavor::Gcc)); @@ -432,6 +432,7 @@ fn test_codegen_options_tracking_hash() { tracked!(force_unwind_tables, Some(true)); tracked!(inline_threshold, Some(0xf007ba11)); tracked!(linker_plugin_lto, LinkerPluginLto::LinkerPluginAuto); + tracked!(link_dead_code, Some(true)); tracked!(llvm_args, vec![String::from("1"), String::from("2")]); tracked!(lto, LtoCli::Fat); tracked!(metadata, vec![String::from("A"), String::from("B")]); @@ -470,7 +471,6 @@ fn test_debugging_options_tracking_hash() { untracked!(ast_json, true); untracked!(ast_json_noexpand, true); untracked!(borrowck, String::from("other")); - untracked!(borrowck_stats, true); untracked!(deduplicate_diagnostics, true); untracked!(dep_tasks, true); untracked!(dont_buffer_diagnostics, true); @@ -560,14 +560,13 @@ fn test_debugging_options_tracking_hash() { tracked!(inline_mir, Some(true)); tracked!(inline_mir_threshold, Some(123)); tracked!(inline_mir_hint_threshold, Some(123)); - tracked!(insert_sideeffect, true); - tracked!(instrument_coverage, true); + tracked!(instrument_coverage, Some(InstrumentCoverage::All)); tracked!(instrument_mcount, true); tracked!(link_only, true); tracked!(merge_functions, Some(MergeFunctions::Disabled)); tracked!(mir_emit_retag, true); - tracked!(mir_opt_level, 3); - tracked!(mutable_noalias, true); + tracked!(mir_opt_level, Some(4)); + tracked!(mutable_noalias, Some(true)); tracked!(new_llvm_pass_manager, true); tracked!(no_codegen, true); tracked!(no_generate_arange_section, true); diff --git a/compiler/rustc_interface/src/util.rs b/compiler/rustc_interface/src/util.rs index 798996263c700..59488fc80a5e2 100644 --- a/compiler/rustc_interface/src/util.rs +++ b/compiler/rustc_interface/src/util.rs @@ -265,7 +265,7 @@ pub fn get_codegen_backend(sopts: &config::Options) -> Box { let backend = match codegen_name { filename if filename.contains('.') => load_backend_from_dylib(filename.as_ref()), - codegen_name => get_builtin_codegen_backend(codegen_name), + codegen_name => get_builtin_codegen_backend(&sopts.maybe_sysroot, codegen_name), }; unsafe { @@ -390,15 +390,21 @@ fn sysroot_candidates() -> Vec { } } -pub fn get_builtin_codegen_backend(backend_name: &str) -> fn() -> Box { +pub fn get_builtin_codegen_backend( + maybe_sysroot: &Option, + backend_name: &str, +) -> fn() -> Box { match backend_name { #[cfg(feature = "llvm")] "llvm" => rustc_codegen_llvm::LlvmCodegenBackend::new, - _ => get_codegen_sysroot(backend_name), + _ => get_codegen_sysroot(maybe_sysroot, backend_name), } } -pub fn get_codegen_sysroot(backend_name: &str) -> fn() -> Box { +pub fn get_codegen_sysroot( + maybe_sysroot: &Option, + backend_name: &str, +) -> fn() -> Box { // For now we only allow this function to be called once as it'll dlopen a // few things, which seems to work best if we only do that once. In // general this assertion never trips due to the once guard in `get_codegen_backend`, @@ -413,8 +419,9 @@ pub fn get_codegen_sysroot(backend_name: &str) -> fn() -> Box fn() -> Box = None; - let expected_name = - format!("rustc_codegen_{}-{}", backend_name, release_str().expect("CFG_RELEASE")); + let expected_names = &[ + format!("rustc_codegen_{}-{}", backend_name, release_str().expect("CFG_RELEASE")), + format!("rustc_codegen_{}", backend_name), + ]; for entry in d.filter_map(|e| e.ok()) { let path = entry.path(); let filename = match path.file_name().and_then(|s| s.to_str()) { @@ -462,7 +471,7 @@ pub fn get_codegen_sysroot(backend_name: &str) -> fn() -> Box std::io::Result<()> { + std::fs::rename(src, dst) +} + +/// This function attempts to bypass the auto_da_alloc heuristic implemented by some filesystems +/// such as btrfs and ext4. When renaming over a file that already exists then they will "helpfully" +/// write back the source file before committing the rename in case a developer forgot some of +/// the fsyncs in the open/write/fsync(file)/rename/fsync(dir) dance for atomic file updates. +/// +/// To avoid triggering this heuristic we delete the destination first, if it exists. +/// The cost of an extra syscall is much lower than getting descheduled for the sync IO. +#[cfg(target_os = "linux")] +pub fn non_durable_rename(src: &Path, dst: &Path) -> std::io::Result<()> { + let _ = std::fs::remove_file(dst); + std::fs::rename(src, dst) +} + +/// Replaces function bodies with `loop {}` (an infinite loop). This gets rid of +/// all semantic errors in the body while still satisfying the return type, +/// except in certain cases, see below for more. +/// +/// This pass is known as `everybody_loops`. Very punny. +/// +/// As of March 2021, `everybody_loops` is only used for the +/// `-Z unpretty=everybody_loops` debugging option. +/// +/// FIXME: Currently the `everybody_loops` transformation is not applied to: +/// * `const fn`; support could be added, but hasn't. Originally `const fn` +/// was skipped due to issue #43636 that `loop` was not supported for +/// const evaluation. +/// * `impl Trait`, due to issue #43869 that functions returning impl Trait cannot be diverging. +/// Solving this may require `!` to implement every trait, which relies on the an even more +/// ambitious form of the closed RFC #1637. See also [#34511]. +/// +/// [#34511]: https://github.com/rust-lang/rust/issues/34511#issuecomment-322340401 pub struct ReplaceBodyWithLoop<'a, 'b> { within_static_or_const: bool, nested_blocks: Option>, diff --git a/compiler/rustc_lexer/src/lib.rs b/compiler/rustc_lexer/src/lib.rs index 44fc4db7dc199..b9781581ff75d 100644 --- a/compiler/rustc_lexer/src/lib.rs +++ b/compiler/rustc_lexer/src/lib.rs @@ -1,13 +1,13 @@ //! Low-level Rust lexer. //! -//! The idea with `librustc_lexer` is to make a reusable library, +//! The idea with `rustc_lexer` is to make a reusable library, //! by separating out pure lexing and rustc-specific concerns, like spans, //! error reporting, and interning. So, rustc_lexer operates directly on `&str`, //! produces simple tokens which are a pair of type-tag and a bit of original text, //! and does not report errors, instead storing them as flags on the token. //! //! Tokens produced by this lexer are not yet ready for parsing the Rust syntax. -//! For that see [`librustc_parse::lexer`], which converts this basic token stream +//! For that see [`rustc_parse::lexer`], which converts this basic token stream //! into wide tokens used by actual parser. //! //! The purpose of this crate is to convert raw sources into a labeled sequence @@ -17,7 +17,7 @@ //! The main entity of this crate is the [`TokenKind`] enum which represents common //! lexeme types. //! -//! [`librustc_parse::lexer`]: ../rustc_parse/lexer/index.html +//! [`rustc_parse::lexer`]: ../rustc_parse/lexer/index.html // We want to be able to build this crate with a stable compiler, so no // `#![feature]` attributes should be added. diff --git a/compiler/rustc_lint/src/builtin.rs b/compiler/rustc_lint/src/builtin.rs index 4258a4b4237a8..3965a3dcdfd5d 100644 --- a/compiler/rustc_lint/src/builtin.rs +++ b/compiler/rustc_lint/src/builtin.rs @@ -1,3 +1,5 @@ +// ignore-tidy-filelength + //! Lints in the Rust compiler. //! //! This contains lints which can feasibly be implemented as their own @@ -481,7 +483,7 @@ fn has_doc(sess: &Session, attr: &ast::Attribute) -> bool { return false; } - if attr.is_value_str() { + if attr.value_str().is_some() { return true; } @@ -508,8 +510,7 @@ impl MissingDoc { fn check_missing_docs_attrs( &self, cx: &LateContext<'_>, - id: Option, - attrs: &[ast::Attribute], + id: hir::HirId, sp: Span, article: &'static str, desc: &'static str, @@ -528,12 +529,13 @@ impl MissingDoc { // Only check publicly-visible items, using the result from the privacy pass. // It's an option so the crate root can also use this function (it doesn't // have a `NodeId`). - if let Some(id) = id { + if id != hir::CRATE_HIR_ID { if !cx.access_levels.is_exported(id) { return; } } + let attrs = cx.tcx.hir().attrs(id); let has_doc = attrs.iter().any(|a| has_doc(cx.sess(), a)); if !has_doc { cx.struct_span_lint( @@ -565,10 +567,11 @@ impl<'tcx> LateLintPass<'tcx> for MissingDoc { } fn check_crate(&mut self, cx: &LateContext<'_>, krate: &hir::Crate<'_>) { - self.check_missing_docs_attrs(cx, None, &krate.item.attrs, krate.item.span, "the", "crate"); + self.check_missing_docs_attrs(cx, hir::CRATE_HIR_ID, krate.item.inner, "the", "crate"); for macro_def in krate.exported_macros { - let has_doc = macro_def.attrs.iter().any(|a| has_doc(cx.sess(), a)); + let attrs = cx.tcx.hir().attrs(macro_def.hir_id()); + let has_doc = attrs.iter().any(|a| has_doc(cx.sess(), a)); if !has_doc { cx.struct_span_lint( MISSING_DOCS, @@ -622,7 +625,7 @@ impl<'tcx> LateLintPass<'tcx> for MissingDoc { let (article, desc) = cx.tcx.article_and_description(it.def_id.to_def_id()); - self.check_missing_docs_attrs(cx, Some(it.hir_id()), &it.attrs, it.span, article, desc); + self.check_missing_docs_attrs(cx, it.hir_id(), it.span, article, desc); } fn check_trait_item(&mut self, cx: &LateContext<'_>, trait_item: &hir::TraitItem<'_>) { @@ -632,14 +635,7 @@ impl<'tcx> LateLintPass<'tcx> for MissingDoc { let (article, desc) = cx.tcx.article_and_description(trait_item.def_id.to_def_id()); - self.check_missing_docs_attrs( - cx, - Some(trait_item.hir_id()), - &trait_item.attrs, - trait_item.span, - article, - desc, - ); + self.check_missing_docs_attrs(cx, trait_item.hir_id(), trait_item.span, article, desc); } fn check_impl_item(&mut self, cx: &LateContext<'_>, impl_item: &hir::ImplItem<'_>) { @@ -649,43 +645,22 @@ impl<'tcx> LateLintPass<'tcx> for MissingDoc { } let (article, desc) = cx.tcx.article_and_description(impl_item.def_id.to_def_id()); - self.check_missing_docs_attrs( - cx, - Some(impl_item.hir_id()), - &impl_item.attrs, - impl_item.span, - article, - desc, - ); + self.check_missing_docs_attrs(cx, impl_item.hir_id(), impl_item.span, article, desc); } fn check_foreign_item(&mut self, cx: &LateContext<'_>, foreign_item: &hir::ForeignItem<'_>) { let (article, desc) = cx.tcx.article_and_description(foreign_item.def_id.to_def_id()); - self.check_missing_docs_attrs( - cx, - Some(foreign_item.hir_id()), - &foreign_item.attrs, - foreign_item.span, - article, - desc, - ); + self.check_missing_docs_attrs(cx, foreign_item.hir_id(), foreign_item.span, article, desc); } - fn check_struct_field(&mut self, cx: &LateContext<'_>, sf: &hir::StructField<'_>) { + fn check_field_def(&mut self, cx: &LateContext<'_>, sf: &hir::FieldDef<'_>) { if !sf.is_positional() { - self.check_missing_docs_attrs( - cx, - Some(sf.hir_id), - &sf.attrs, - sf.span, - "a", - "struct field", - ) + self.check_missing_docs_attrs(cx, sf.hir_id, sf.span, "a", "struct field") } } fn check_variant(&mut self, cx: &LateContext<'_>, v: &hir::Variant<'_>) { - self.check_missing_docs_attrs(cx, Some(v.id), &v.attrs, v.span, "a", "variant"); + self.check_missing_docs_attrs(cx, v.id, v.span, "a", "variant"); } } @@ -884,11 +859,10 @@ declare_lint! { /// ``` /// /// This syntax is now a hard error in the 2018 edition. In the 2015 - /// edition, this lint is "allow" by default, because the old code is - /// still valid, and warning for all old code can be noisy. This lint + /// edition, this lint is "warn" by default. This lint /// enables the [`cargo fix`] tool with the `--edition` flag to /// automatically transition old code from the 2015 edition to 2018. The - /// tool will switch this lint to "warn" and will automatically apply the + /// tool will run this lint and automatically apply the /// suggested fix from the compiler (which is to add `_` to each /// parameter). This provides a completely automated way to update old /// code for a new edition. See [issue #41686] for more details. @@ -896,7 +870,7 @@ declare_lint! { /// [issue #41686]: https://github.com/rust-lang/rust/issues/41686 /// [`cargo fix`]: https://doc.rust-lang.org/cargo/commands/cargo-fix.html pub ANONYMOUS_PARAMETERS, - Allow, + Warn, "detects anonymous parameters", @future_incompatible = FutureIncompatibleInfo { reference: "issue #41686 ", @@ -911,6 +885,10 @@ declare_lint_pass!( impl EarlyLintPass for AnonymousParameters { fn check_trait_item(&mut self, cx: &EarlyContext<'_>, it: &ast::AssocItem) { + if cx.sess.edition() != Edition::Edition2015 { + // This is a hard error in future editions; avoid linting and erroring + return; + } if let ast::AssocItemKind::Fn(box FnKind(_, ref sig, _, _)) = it.kind { for arg in sig.decl.inputs.iter() { if let ast::PatKind::Ident(_, ident, None) = arg.pat.kind { @@ -1016,7 +994,7 @@ fn warn_if_doc(cx: &EarlyContext<'_>, node_span: Span, node_kind: &str, attrs: & Some(sugared_span.map_or(attr.span, |span| span.with_hi(attr.span.hi()))); } - if attrs.peek().map(|next_attr| next_attr.is_doc_comment()).unwrap_or_default() { + if attrs.peek().map_or(false, |next_attr| next_attr.is_doc_comment()) { continue; } @@ -1119,9 +1097,10 @@ declare_lint_pass!(InvalidNoMangleItems => [NO_MANGLE_CONST_ITEMS, NO_MANGLE_GEN impl<'tcx> LateLintPass<'tcx> for InvalidNoMangleItems { fn check_item(&mut self, cx: &LateContext<'_>, it: &hir::Item<'_>) { + let attrs = cx.tcx.hir().attrs(it.hir_id()); match it.kind { hir::ItemKind::Fn(.., ref generics, _) => { - if let Some(no_mangle_attr) = cx.sess().find_by_name(&it.attrs, sym::no_mangle) { + if let Some(no_mangle_attr) = cx.sess().find_by_name(attrs, sym::no_mangle) { for param in generics.params { match param.kind { GenericParamKind::Lifetime { .. } => {} @@ -1147,7 +1126,7 @@ impl<'tcx> LateLintPass<'tcx> for InvalidNoMangleItems { } } hir::ItemKind::Const(..) => { - if cx.sess().contains_name(&it.attrs, sym::no_mangle) { + if cx.sess().contains_name(attrs, sym::no_mangle) { // Const items do not refer to a particular location in memory, and therefore // don't have anything to attach a symbol to cx.struct_span_lint(NO_MANGLE_CONST_ITEMS, it.span, |lint| { @@ -1371,7 +1350,7 @@ impl<'tcx> LateLintPass<'tcx> for UnreachablePub { ); } - fn check_struct_field(&mut self, cx: &LateContext<'_>, field: &hir::StructField<'_>) { + fn check_field_def(&mut self, cx: &LateContext<'_>, field: &hir::FieldDef<'_>) { self.perform_lint(cx, "field", field.hir_id, &field.vis, field.span, false); } @@ -1827,7 +1806,8 @@ impl<'tcx> LateLintPass<'tcx> for UnnameableTestItems { return; } - if let Some(attr) = cx.sess().find_by_name(&it.attrs, sym::rustc_test_marker) { + let attrs = cx.tcx.hir().attrs(it.hir_id()); + if let Some(attr) = cx.sess().find_by_name(attrs, sym::rustc_test_marker) { cx.struct_span_lint(UNNAMEABLE_TEST_ITEMS, attr.span, |lint| { lint.build("cannot test inner items").emit() }); @@ -2305,7 +2285,7 @@ declare_lint! { } declare_lint_pass!( - /// Check for used feature gates in `INCOMPLETE_FEATURES` in `librustc_feature/active.rs`. + /// Check for used feature gates in `INCOMPLETE_FEATURES` in `rustc_feature/src/active.rs`. IncompleteFeatures => [INCOMPLETE_FEATURES] ); @@ -2986,3 +2966,88 @@ impl<'tcx> LateLintPass<'tcx> for ClashingExternDeclarations { } } } + +declare_lint! { + /// The `deref_nullptr` lint detects when an null pointer is dereferenced, + /// which causes [undefined behavior]. + /// + /// ### Example + /// + /// ```rust,no_run + /// # #![allow(unused)] + /// use std::ptr; + /// unsafe { + /// let x = &*ptr::null::(); + /// let x = ptr::addr_of!(*ptr::null::()); + /// let x = *(0 as *const i32); + /// } + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// Dereferencing a null pointer causes [undefined behavior] even as a place expression, + /// like `&*(0 as *const i32)` or `addr_of!(*(0 as *const i32))`. + /// + /// [undefined behavior]: https://doc.rust-lang.org/reference/behavior-considered-undefined.html + pub DEREF_NULLPTR, + Warn, + "detects when an null pointer is dereferenced" +} + +declare_lint_pass!(DerefNullPtr => [DEREF_NULLPTR]); + +impl<'tcx> LateLintPass<'tcx> for DerefNullPtr { + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &hir::Expr<'_>) { + /// test if expression is a null ptr + fn is_null_ptr(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> bool { + match &expr.kind { + rustc_hir::ExprKind::Cast(ref expr, ref ty) => { + if let rustc_hir::TyKind::Ptr(_) = ty.kind { + return is_zero(expr) || is_null_ptr(cx, expr); + } + } + // check for call to `core::ptr::null` or `core::ptr::null_mut` + rustc_hir::ExprKind::Call(ref path, _) => { + if let rustc_hir::ExprKind::Path(ref qpath) = path.kind { + if let Some(def_id) = cx.qpath_res(qpath, path.hir_id).opt_def_id() { + return cx.tcx.is_diagnostic_item(sym::ptr_null, def_id) + || cx.tcx.is_diagnostic_item(sym::ptr_null_mut, def_id); + } + } + } + _ => {} + } + false + } + + /// test if expression is the literal `0` + fn is_zero(expr: &hir::Expr<'_>) -> bool { + match &expr.kind { + rustc_hir::ExprKind::Lit(ref lit) => { + if let LitKind::Int(a, _) = lit.node { + return a == 0; + } + } + _ => {} + } + false + } + + if let rustc_hir::ExprKind::Unary(ref un_op, ref expr_deref) = expr.kind { + if let rustc_hir::UnOp::Deref = un_op { + if is_null_ptr(cx, expr_deref) { + cx.struct_span_lint(DEREF_NULLPTR, expr.span, |lint| { + let mut err = lint.build("dereferencing a null pointer"); + err.span_label( + expr.span, + "this code causes undefined behavior when executed", + ); + err.emit(); + }); + } + } + } + } +} diff --git a/compiler/rustc_lint/src/context.rs b/compiler/rustc_lint/src/context.rs index 7d5577cdca663..b3a19bfbf7532 100644 --- a/compiler/rustc_lint/src/context.rs +++ b/compiler/rustc_lint/src/context.rs @@ -45,6 +45,7 @@ use rustc_target::abi::LayoutOf; use tracing::debug; use std::cell::Cell; +use std::iter; use std::slice; /// Information about the registered lints. @@ -100,6 +101,11 @@ enum TargetLint { /// Lint with this name existed previously, but has been removed/deprecated. /// The string argument is the reason for removal. Removed(String), + + /// A lint name that should give no warnings and have no effect. + /// + /// This is used by rustc to avoid warning about old rustdoc lints before rustdoc registers them as tool lints. + Ignored, } pub enum FindLintError { @@ -171,6 +177,7 @@ impl LintStore { self.early_passes.push(Box::new(pass)); } + /// Used by clippy. pub fn register_pre_expansion_pass( &mut self, pass: impl Fn() -> EarlyLintPassObject + 'static + sync::Send + sync::Sync, @@ -266,6 +273,33 @@ impl LintStore { } } + /// This lint should be available with either the old or the new name. + /// + /// Using the old name will not give a warning. + /// You must register a lint with the new name before calling this function. + #[track_caller] + pub fn register_alias(&mut self, old_name: &str, new_name: &str) { + let target = match self.by_name.get(new_name) { + Some(&Id(lint_id)) => lint_id, + _ => bug!("cannot add alias {} for lint {} that does not exist", old_name, new_name), + }; + match self.by_name.insert(old_name.to_string(), Id(target)) { + None | Some(Ignored) => {} + Some(x) => bug!("duplicate specification of lint {} (was {:?})", old_name, x), + } + } + + /// This lint should give no warning and have no effect. + /// + /// This is used by rustc to avoid warning about old rustdoc lints before rustdoc registers them as tool lints. + #[track_caller] + pub fn register_ignored(&mut self, name: &str) { + if self.by_name.insert(name.to_string(), Ignored).is_some() { + bug!("duplicate specification of lint {}", name); + } + } + + /// This lint has been renamed; warn about using the new name and apply the lint. #[track_caller] pub fn register_renamed(&mut self, old_name: &str, new_name: &str) { let target = match self.by_name.get(new_name) { @@ -284,6 +318,7 @@ impl LintStore { Some(&Id(lint_id)) => Ok(vec![lint_id]), Some(&Renamed(_, lint_id)) => Ok(vec![lint_id]), Some(&Removed(_)) => Err(FindLintError::Removed), + Some(&Ignored) => Ok(vec![]), None => loop { return match self.lint_groups.get(lint_name) { Some(LintGroup { lint_ids, depr, .. }) => { @@ -427,6 +462,7 @@ impl LintStore { } }, Some(&Id(ref id)) => CheckLintNameResult::Ok(slice::from_ref(id)), + Some(&Ignored) => CheckLintNameResult::Ok(&[]), } } @@ -670,6 +706,12 @@ pub trait LintContext: Sized { json ); } + BuiltinLintDiagnostics::ProcMacroBackCompat(note) => { + db.note(¬e); + } + BuiltinLintDiagnostics::OrPatternsBackCompat(span,suggestion) => { + db.span_suggestion(span, "use pat2015 to preserve semantics", suggestion, Applicability::MachineApplicable); + } } // Rewrap `db`, and pass control to the user. decorate(LintDiagnosticBuilder::new(db)); @@ -711,7 +753,7 @@ impl<'a> EarlyContext<'a> { sess, krate, lint_store, - builder: LintLevelsBuilder::new(sess, warn_about_weird_lints, lint_store), + builder: LintLevelsBuilder::new(sess, warn_about_weird_lints, lint_store, &krate.attrs), buffered, } } @@ -824,10 +866,12 @@ impl<'tcx> LateContext<'tcx> { /// // The given `def_id` is that of an `Option` type /// } /// ``` + /// + /// Used by clippy, but should be replaced by diagnostic items eventually. pub fn match_def_path(&self, def_id: DefId, path: &[Symbol]) -> bool { let names = self.get_def_path(def_id); - names.len() == path.len() && names.into_iter().zip(path.iter()).all(|(a, &b)| a == b) + names.len() == path.len() && iter::zip(names, path).all(|(a, &b)| a == b) } /// Gets the absolute path of `def_id` as a vector of `Symbol`. @@ -868,7 +912,7 @@ impl<'tcx> LateContext<'tcx> { fn print_dyn_existential( self, - _predicates: &'tcx ty::List>>, + _predicates: &'tcx ty::List>>, ) -> Result { Ok(()) } diff --git a/compiler/rustc_lint/src/early.rs b/compiler/rustc_lint/src/early.rs index 0c7b843831aba..54fcaef414f22 100644 --- a/compiler/rustc_lint/src/early.rs +++ b/compiler/rustc_lint/src/early.rs @@ -109,6 +109,7 @@ impl<'a, T: EarlyLintPass> ast_visit::Visitor<'a> for EarlyContextAndPass<'a, T> fn visit_anon_const(&mut self, c: &'a ast::AnonConst) { run_early_pass!(self, check_anon_const, c); + self.check_id(c.id); ast_visit::walk_anon_const(self, c); } @@ -163,10 +164,10 @@ impl<'a, T: EarlyLintPass> ast_visit::Visitor<'a> for EarlyContextAndPass<'a, T> run_early_pass!(self, check_struct_def_post, s); } - fn visit_struct_field(&mut self, s: &'a ast::StructField) { + fn visit_field_def(&mut self, s: &'a ast::FieldDef) { self.with_lint_attrs(s.id, &s.attrs, |cx| { - run_early_pass!(cx, check_struct_field, s); - ast_visit::walk_struct_field(cx, s); + run_early_pass!(cx, check_field_def, s); + ast_visit::walk_field_def(cx, s); }) } diff --git a/compiler/rustc_lint/src/internal.rs b/compiler/rustc_lint/src/internal.rs index 26e536e8f1ddb..9b1a339572ec3 100644 --- a/compiler/rustc_lint/src/internal.rs +++ b/compiler/rustc_lint/src/internal.rs @@ -283,7 +283,7 @@ fn is_doc_keyword(s: Symbol) -> bool { impl<'tcx> LateLintPass<'tcx> for ExistingDocKeyword { fn check_item(&mut self, cx: &LateContext<'_>, item: &rustc_hir::Item<'_>) { - for attr in item.attrs { + for attr in cx.tcx.hir().attrs(item.hir_id()) { if !attr.has_name(sym::doc) { continue; } diff --git a/compiler/rustc_lint/src/late.rs b/compiler/rustc_lint/src/late.rs index e019b621aa359..d325b5fe7f8c6 100644 --- a/compiler/rustc_lint/src/late.rs +++ b/compiler/rustc_lint/src/late.rs @@ -16,7 +16,6 @@ use crate::{passes::LateLintPassObject, LateContext, LateLintPass, LintStore}; use rustc_ast as ast; -use rustc_ast::walk_list; use rustc_data_structures::sync::{join, par_iter, ParallelIterator}; use rustc_hir as hir; use rustc_hir::def_id::{LocalDefId, LOCAL_CRATE}; @@ -53,10 +52,11 @@ impl<'tcx, T: LateLintPass<'tcx>> LateContextAndPass<'tcx, T> { /// Merge the lints specified by any lint attributes into the /// current lint context, call the provided function, then reset the /// lints in effect to their previous state. - fn with_lint_attrs(&mut self, id: hir::HirId, attrs: &'tcx [ast::Attribute], f: F) + fn with_lint_attrs(&mut self, id: hir::HirId, f: F) where F: FnOnce(&mut Self), { + let attrs = self.context.tcx.hir().attrs(id); let prev = self.context.last_node_with_lint_attrs; self.context.last_node_with_lint_attrs = id; self.enter_attrs(attrs); @@ -125,7 +125,7 @@ impl<'tcx, T: LateLintPass<'tcx>> hir_visit::Visitor<'tcx> for LateContextAndPas } fn visit_param(&mut self, param: &'tcx hir::Param<'tcx>) { - self.with_lint_attrs(param.hir_id, ¶m.attrs, |cx| { + self.with_lint_attrs(param.hir_id, |cx| { lint_callback!(cx, check_param, param); hir_visit::walk_param(cx, param); }); @@ -142,7 +142,7 @@ impl<'tcx, T: LateLintPass<'tcx>> hir_visit::Visitor<'tcx> for LateContextAndPas self.context.generics = it.kind.generics(); let old_cached_typeck_results = self.context.cached_typeck_results.take(); let old_enclosing_body = self.context.enclosing_body.take(); - self.with_lint_attrs(it.hir_id(), &it.attrs, |cx| { + self.with_lint_attrs(it.hir_id(), |cx| { cx.with_param_env(it.hir_id(), |cx| { lint_callback!(cx, check_item, it); hir_visit::walk_item(cx, it); @@ -155,7 +155,7 @@ impl<'tcx, T: LateLintPass<'tcx>> hir_visit::Visitor<'tcx> for LateContextAndPas } fn visit_foreign_item(&mut self, it: &'tcx hir::ForeignItem<'tcx>) { - self.with_lint_attrs(it.hir_id(), &it.attrs, |cx| { + self.with_lint_attrs(it.hir_id(), |cx| { cx.with_param_env(it.hir_id(), |cx| { lint_callback!(cx, check_foreign_item, it); hir_visit::walk_foreign_item(cx, it); @@ -170,7 +170,7 @@ impl<'tcx, T: LateLintPass<'tcx>> hir_visit::Visitor<'tcx> for LateContextAndPas } fn visit_expr(&mut self, e: &'tcx hir::Expr<'tcx>) { - self.with_lint_attrs(e.hir_id, &e.attrs, |cx| { + self.with_lint_attrs(e.hir_id, |cx| { lint_callback!(cx, check_expr, e); hir_visit::walk_expr(cx, e); lint_callback!(cx, check_expr_post, e); @@ -178,11 +178,9 @@ impl<'tcx, T: LateLintPass<'tcx>> hir_visit::Visitor<'tcx> for LateContextAndPas } fn visit_stmt(&mut self, s: &'tcx hir::Stmt<'tcx>) { - let get_item = |id: hir::ItemId| self.context.tcx.hir().item(id); - let attrs = &s.kind.attrs(get_item); // See `EarlyContextAndPass::visit_stmt` for an explanation // of why we call `walk_stmt` outside of `with_lint_attrs` - self.with_lint_attrs(s.hir_id, attrs, |cx| { + self.with_lint_attrs(s.hir_id, |cx| { lint_callback!(cx, check_stmt, s); }); hir_visit::walk_stmt(self, s); @@ -221,10 +219,10 @@ impl<'tcx, T: LateLintPass<'tcx>> hir_visit::Visitor<'tcx> for LateContextAndPas lint_callback!(self, check_struct_def_post, s); } - fn visit_struct_field(&mut self, s: &'tcx hir::StructField<'tcx>) { - self.with_lint_attrs(s.hir_id, &s.attrs, |cx| { - lint_callback!(cx, check_struct_field, s); - hir_visit::walk_struct_field(cx, s); + fn visit_field_def(&mut self, s: &'tcx hir::FieldDef<'tcx>) { + self.with_lint_attrs(s.hir_id, |cx| { + lint_callback!(cx, check_field_def, s); + hir_visit::walk_field_def(cx, s); }) } @@ -234,7 +232,7 @@ impl<'tcx, T: LateLintPass<'tcx>> hir_visit::Visitor<'tcx> for LateContextAndPas g: &'tcx hir::Generics<'tcx>, item_id: hir::HirId, ) { - self.with_lint_attrs(v.id, &v.attrs, |cx| { + self.with_lint_attrs(v.id, |cx| { lint_callback!(cx, check_variant, v); hir_visit::walk_variant(cx, v, g, item_id); lint_callback!(cx, check_variant_post, v); @@ -257,7 +255,7 @@ impl<'tcx, T: LateLintPass<'tcx>> hir_visit::Visitor<'tcx> for LateContextAndPas } fn visit_local(&mut self, l: &'tcx hir::Local<'tcx>) { - self.with_lint_attrs(l.hir_id, &l.attrs, |cx| { + self.with_lint_attrs(l.hir_id, |cx| { lint_callback!(cx, check_local, l); hir_visit::walk_local(cx, l); }) @@ -301,7 +299,7 @@ impl<'tcx, T: LateLintPass<'tcx>> hir_visit::Visitor<'tcx> for LateContextAndPas fn visit_trait_item(&mut self, trait_item: &'tcx hir::TraitItem<'tcx>) { let generics = self.context.generics.take(); self.context.generics = Some(&trait_item.generics); - self.with_lint_attrs(trait_item.hir_id(), &trait_item.attrs, |cx| { + self.with_lint_attrs(trait_item.hir_id(), |cx| { cx.with_param_env(trait_item.hir_id(), |cx| { lint_callback!(cx, check_trait_item, trait_item); hir_visit::walk_trait_item(cx, trait_item); @@ -314,7 +312,7 @@ impl<'tcx, T: LateLintPass<'tcx>> hir_visit::Visitor<'tcx> for LateContextAndPas fn visit_impl_item(&mut self, impl_item: &'tcx hir::ImplItem<'tcx>) { let generics = self.context.generics.take(); self.context.generics = Some(&impl_item.generics); - self.with_lint_attrs(impl_item.hir_id(), &impl_item.attrs, |cx| { + self.with_lint_attrs(impl_item.hir_id(), |cx| { cx.with_param_env(impl_item.hir_id(), |cx| { lint_callback!(cx, check_impl_item, impl_item); hir_visit::walk_impl_item(cx, impl_item); @@ -334,8 +332,10 @@ impl<'tcx, T: LateLintPass<'tcx>> hir_visit::Visitor<'tcx> for LateContextAndPas hir_visit::walk_path(self, p); } - fn visit_attribute(&mut self, attr: &'tcx ast::Attribute) { - lint_callback!(self, check_attribute, attr); + fn visit_attribute(&mut self, hir_id: hir::HirId, attr: &'tcx ast::Attribute) { + self.with_lint_attrs(hir_id, |cx| { + lint_callback!(cx, check_attribute, attr); + }) } } @@ -396,7 +396,9 @@ fn late_lint_mod_pass<'tcx, T: LateLintPass<'tcx>>( // Visit the crate attributes if hir_id == hir::CRATE_HIR_ID { - walk_list!(cx, visit_attribute, tcx.hir().attrs(hir::CRATE_HIR_ID)); + for attr in tcx.hir().attrs(hir::CRATE_HIR_ID).iter() { + cx.visit_attribute(hir_id, attr) + } } } @@ -440,7 +442,7 @@ fn late_lint_pass_crate<'tcx, T: LateLintPass<'tcx>>(tcx: TyCtxt<'tcx>, pass: T) let mut cx = LateContextAndPass { context, pass }; // Visit the whole crate. - cx.with_lint_attrs(hir::CRATE_HIR_ID, &krate.item.attrs, |cx| { + cx.with_lint_attrs(hir::CRATE_HIR_ID, |cx| { // since the root module isn't visited as an item (because it isn't an // item), warn for it here. lint_callback!(cx, check_crate, krate); diff --git a/compiler/rustc_lint/src/levels.rs b/compiler/rustc_lint/src/levels.rs index dbcab72ddf2eb..54909381a10a5 100644 --- a/compiler/rustc_lint/src/levels.rs +++ b/compiler/rustc_lint/src/levels.rs @@ -1,13 +1,12 @@ use crate::context::{CheckLintNameResult, LintStore}; use crate::late::unerased_lint_store; use rustc_ast as ast; -use rustc_ast::attr; use rustc_ast::unwrap_or; use rustc_ast_pretty::pprust; use rustc_data_structures::fx::FxHashMap; use rustc_errors::{struct_span_err, Applicability, DiagnosticBuilder}; use rustc_hir as hir; -use rustc_hir::def_id::{CrateNum, LOCAL_CRATE}; +use rustc_hir::def_id::{CrateNum, DefId, CRATE_DEF_INDEX, LOCAL_CRATE}; use rustc_hir::{intravisit, HirId}; use rustc_middle::hir::map::Map; use rustc_middle::lint::LevelAndSource; @@ -32,13 +31,14 @@ use std::cmp; fn lint_levels(tcx: TyCtxt<'_>, cnum: CrateNum) -> LintLevelMap { assert_eq!(cnum, LOCAL_CRATE); let store = unerased_lint_store(tcx); - let levels = LintLevelsBuilder::new(tcx.sess, false, &store); + let crate_attrs = tcx.get_attrs(DefId { krate: cnum, index: CRATE_DEF_INDEX }); + let levels = LintLevelsBuilder::new(tcx.sess, false, &store, crate_attrs); let mut builder = LintLevelMapBuilder { levels, tcx, store }; let krate = tcx.hir().krate(); builder.levels.id_to_set.reserve(krate.exported_macros.len() + 1); - let push = builder.levels.push(&krate.item.attrs, &store, true); + let push = builder.levels.push(tcx.hir().attrs(hir::CRATE_HIR_ID), &store, true); builder.levels.register_id(hir::CRATE_HIR_ID); for macro_def in krate.exported_macros { builder.levels.register_id(macro_def.hir_id()); @@ -56,6 +56,7 @@ pub struct LintLevelsBuilder<'s> { cur: u32, warn_about_weird_lints: bool, store: &'s LintStore, + crate_attrs: &'s [ast::Attribute], } pub struct BuilderPush { @@ -64,7 +65,12 @@ pub struct BuilderPush { } impl<'s> LintLevelsBuilder<'s> { - pub fn new(sess: &'s Session, warn_about_weird_lints: bool, store: &'s LintStore) -> Self { + pub fn new( + sess: &'s Session, + warn_about_weird_lints: bool, + store: &'s LintStore, + crate_attrs: &'s [ast::Attribute], + ) -> Self { let mut builder = LintLevelsBuilder { sess, sets: LintLevelSets::new(), @@ -72,6 +78,7 @@ impl<'s> LintLevelsBuilder<'s> { id_to_set: Default::default(), warn_about_weird_lints, store, + crate_attrs, }; builder.process_command_line(sess, store); assert_eq!(builder.sets.list.len(), 1); @@ -229,10 +236,9 @@ impl<'s> LintLevelsBuilder<'s> { Some(lvl) => lvl, }; - let meta = unwrap_or!(attr.meta(), continue); self.sess.mark_attr_used(attr); - let mut metas = unwrap_or!(meta.meta_item_list(), continue); + let mut metas = unwrap_or!(attr.meta_item_list(), continue); if metas.is_empty() { // FIXME (#55112): issue unused-attributes lint for `#[level()]` @@ -248,8 +254,6 @@ impl<'s> LintLevelsBuilder<'s> { ast::MetaItemKind::Word => {} // actual lint names handled later ast::MetaItemKind::NameValue(ref name_value) => { if item.path == sym::reason { - // found reason, reslice meta list to exclude it - metas = &metas[0..metas.len() - 1]; // FIXME (#55112): issue unused-attributes lint if we thereby // don't have any lint names (`#[level(reason = "foo")]`) if let ast::LitKind::Str(rationale, _) = name_value.kind { @@ -268,6 +272,8 @@ impl<'s> LintLevelsBuilder<'s> { .span_label(name_value.span, "reason must be a string literal") .emit(); } + // found reason, reslice meta list to exclude it + metas.pop().unwrap(); } else { bad_attr(item.span) .span_label(item.span, "bad attribute argument") @@ -281,10 +287,10 @@ impl<'s> LintLevelsBuilder<'s> { } for li in metas { - let meta_item = match li.meta_item() { - Some(meta_item) if meta_item.is_word() => meta_item, + let sp = li.span(); + let mut meta_item = match li { + ast::NestedMetaItem::MetaItem(meta_item) if meta_item.is_word() => meta_item, _ => { - let sp = li.span(); let mut err = bad_attr(sp); let mut add_label = true; if let Some(item) = li.meta_item() { @@ -304,27 +310,38 @@ impl<'s> LintLevelsBuilder<'s> { }; let tool_name = if meta_item.path.segments.len() > 1 { let tool_ident = meta_item.path.segments[0].ident; - if !attr::is_known_lint_tool(tool_ident) { - struct_span_err!( + if !is_known_lint_tool(tool_ident.name, sess, &self.crate_attrs) { + let mut err = struct_span_err!( sess, tool_ident.span, E0710, - "an unknown tool name found in scoped lint: `{}`", + "unknown tool name `{}` found in scoped lint: `{}`", + tool_ident.name, pprust::path_to_string(&meta_item.path), - ) - .emit(); + ); + if sess.is_nightly_build() { + err.help(&format!( + "add `#![register_tool({})]` to the crate root", + tool_ident.name + )); + } + err.emit(); continue; } - Some(tool_ident.name) + Some(meta_item.path.segments.remove(0).ident.name) } else { None }; - let name = meta_item.path.segments.last().expect("empty lint name").ident.name; - let lint_result = store.check_lint_name(&name.as_str(), tool_name); + let name = pprust::path_to_string(&meta_item.path); + let lint_result = store.check_lint_name(&name, tool_name); match &lint_result { CheckLintNameResult::Ok(ids) => { - let src = LintLevelSource::Node(name, li.span(), reason); + let src = LintLevelSource::Node( + meta_item.path.segments.last().expect("empty lint name").ident.name, + sp, + reason, + ); for &id in *ids { self.check_gated_lint(id, attr.span); self.insert_spec(&mut specs, id, (level, src)); @@ -337,7 +354,7 @@ impl<'s> LintLevelsBuilder<'s> { let complete_name = &format!("{}::{}", tool_name.unwrap(), name); let src = LintLevelSource::Node( Symbol::intern(complete_name), - li.span(), + sp, reason, ); for id in ids { @@ -353,7 +370,7 @@ impl<'s> LintLevelsBuilder<'s> { lint, lvl, src, - Some(li.span().into()), + Some(sp.into()), |lint| { let msg = format!( "lint name `{}` is deprecated \ @@ -362,7 +379,7 @@ impl<'s> LintLevelsBuilder<'s> { ); lint.build(&msg) .span_suggestion( - li.span(), + sp, "change it to", new_lint_name.to_string(), Applicability::MachineApplicable, @@ -373,7 +390,7 @@ impl<'s> LintLevelsBuilder<'s> { let src = LintLevelSource::Node( Symbol::intern(&new_lint_name), - li.span(), + sp, reason, ); for id in ids { @@ -400,12 +417,12 @@ impl<'s> LintLevelsBuilder<'s> { lint, renamed_lint_level, src, - Some(li.span().into()), + Some(sp.into()), |lint| { let mut err = lint.build(&msg); if let Some(new_name) = &renamed { err.span_suggestion( - li.span(), + sp, "use the new name", new_name.to_string(), Applicability::MachineApplicable, @@ -419,30 +436,23 @@ impl<'s> LintLevelsBuilder<'s> { let lint = builtin::UNKNOWN_LINTS; let (level, src) = self.sets.get_lint_level(lint, self.cur, Some(&specs), self.sess); - struct_lint_level( - self.sess, - lint, - level, - src, - Some(li.span().into()), - |lint| { - let name = if let Some(tool_name) = tool_name { - format!("{}::{}", tool_name, name) - } else { - name.to_string() - }; - let mut db = lint.build(&format!("unknown lint: `{}`", name)); - if let Some(suggestion) = suggestion { - db.span_suggestion( - li.span(), - "did you mean", - suggestion.to_string(), - Applicability::MachineApplicable, - ); - } - db.emit(); - }, - ); + struct_lint_level(self.sess, lint, level, src, Some(sp.into()), |lint| { + let name = if let Some(tool_name) = tool_name { + format!("{}::{}", tool_name, name) + } else { + name.to_string() + }; + let mut db = lint.build(&format!("unknown lint: `{}`", name)); + if let Some(suggestion) = suggestion { + db.span_suggestion( + sp, + "did you mean", + suggestion.to_string(), + Applicability::MachineApplicable, + ); + } + db.emit(); + }); } } // If this lint was renamed, apply the new lint instead of ignoring the attribute. @@ -450,15 +460,15 @@ impl<'s> LintLevelsBuilder<'s> { // we don't warn about the name change. if let CheckLintNameResult::Warning(_, Some(new_name)) = lint_result { // Ignore any errors or warnings that happen because the new name is inaccurate - if let CheckLintNameResult::Ok(ids) = - store.check_lint_name(&new_name, tool_name) - { - let src = - LintLevelSource::Node(Symbol::intern(&new_name), li.span(), reason); + // NOTE: `new_name` already includes the tool name, so we don't have to add it again. + if let CheckLintNameResult::Ok(ids) = store.check_lint_name(&new_name, None) { + let src = LintLevelSource::Node(Symbol::intern(&new_name), sp, reason); for &id in ids { self.check_gated_lint(id, attr.span); self.insert_spec(&mut specs, id, (level, src)); } + } else { + panic!("renamed lint does not exist: {}", new_name); } } } @@ -550,15 +560,25 @@ impl<'s> LintLevelsBuilder<'s> { self.id_to_set.insert(id, self.cur); } - pub fn build(self) -> LintLevelSets { - self.sets - } - pub fn build_map(self) -> LintLevelMap { LintLevelMap { sets: self.sets, id_to_set: self.id_to_set } } } +fn is_known_lint_tool(m_item: Symbol, sess: &Session, attrs: &[ast::Attribute]) -> bool { + if [sym::clippy, sym::rustc, sym::rustdoc].contains(&m_item) { + return true; + } + // Look for registered tools + // NOTE: does no error handling; error handling is done by rustc_resolve. + sess.filter_by_name(attrs, sym::register_tool) + .filter_map(|attr| attr.meta_item_list()) + .flat_map(std::convert::identity) + .filter_map(|nested_meta| nested_meta.ident()) + .map(|ident| ident.name) + .any(|name| name == m_item) +} + struct LintLevelMapBuilder<'a, 'tcx> { levels: LintLevelsBuilder<'tcx>, tcx: TyCtxt<'tcx>, @@ -566,11 +586,12 @@ struct LintLevelMapBuilder<'a, 'tcx> { } impl LintLevelMapBuilder<'_, '_> { - fn with_lint_attrs(&mut self, id: hir::HirId, attrs: &[ast::Attribute], f: F) + fn with_lint_attrs(&mut self, id: hir::HirId, f: F) where F: FnOnce(&mut Self), { let is_crate_hir = id == hir::CRATE_HIR_ID; + let attrs = self.tcx.hir().attrs(id); let push = self.levels.push(attrs, self.store, is_crate_hir); if push.changed { self.levels.register_id(id); @@ -588,19 +609,19 @@ impl<'tcx> intravisit::Visitor<'tcx> for LintLevelMapBuilder<'_, 'tcx> { } fn visit_param(&mut self, param: &'tcx hir::Param<'tcx>) { - self.with_lint_attrs(param.hir_id, ¶m.attrs, |builder| { + self.with_lint_attrs(param.hir_id, |builder| { intravisit::walk_param(builder, param); }); } fn visit_item(&mut self, it: &'tcx hir::Item<'tcx>) { - self.with_lint_attrs(it.hir_id(), &it.attrs, |builder| { + self.with_lint_attrs(it.hir_id(), |builder| { intravisit::walk_item(builder, it); }); } fn visit_foreign_item(&mut self, it: &'tcx hir::ForeignItem<'tcx>) { - self.with_lint_attrs(it.hir_id(), &it.attrs, |builder| { + self.with_lint_attrs(it.hir_id(), |builder| { intravisit::walk_foreign_item(builder, it); }) } @@ -613,14 +634,14 @@ impl<'tcx> intravisit::Visitor<'tcx> for LintLevelMapBuilder<'_, 'tcx> { } fn visit_expr(&mut self, e: &'tcx hir::Expr<'tcx>) { - self.with_lint_attrs(e.hir_id, &e.attrs, |builder| { + self.with_lint_attrs(e.hir_id, |builder| { intravisit::walk_expr(builder, e); }) } - fn visit_struct_field(&mut self, s: &'tcx hir::StructField<'tcx>) { - self.with_lint_attrs(s.hir_id, &s.attrs, |builder| { - intravisit::walk_struct_field(builder, s); + fn visit_field_def(&mut self, s: &'tcx hir::FieldDef<'tcx>) { + self.with_lint_attrs(s.hir_id, |builder| { + intravisit::walk_field_def(builder, s); }) } @@ -630,31 +651,31 @@ impl<'tcx> intravisit::Visitor<'tcx> for LintLevelMapBuilder<'_, 'tcx> { g: &'tcx hir::Generics<'tcx>, item_id: hir::HirId, ) { - self.with_lint_attrs(v.id, &v.attrs, |builder| { + self.with_lint_attrs(v.id, |builder| { intravisit::walk_variant(builder, v, g, item_id); }) } fn visit_local(&mut self, l: &'tcx hir::Local<'tcx>) { - self.with_lint_attrs(l.hir_id, &l.attrs, |builder| { + self.with_lint_attrs(l.hir_id, |builder| { intravisit::walk_local(builder, l); }) } fn visit_arm(&mut self, a: &'tcx hir::Arm<'tcx>) { - self.with_lint_attrs(a.hir_id, &a.attrs, |builder| { + self.with_lint_attrs(a.hir_id, |builder| { intravisit::walk_arm(builder, a); }) } fn visit_trait_item(&mut self, trait_item: &'tcx hir::TraitItem<'tcx>) { - self.with_lint_attrs(trait_item.hir_id(), &trait_item.attrs, |builder| { + self.with_lint_attrs(trait_item.hir_id(), |builder| { intravisit::walk_trait_item(builder, trait_item); }); } fn visit_impl_item(&mut self, impl_item: &'tcx hir::ImplItem<'tcx>) { - self.with_lint_attrs(impl_item.hir_id(), &impl_item.attrs, |builder| { + self.with_lint_attrs(impl_item.hir_id(), |builder| { intravisit::walk_impl_item(builder, impl_item); }); } diff --git a/compiler/rustc_lint/src/lib.rs b/compiler/rustc_lint/src/lib.rs index 547779dd6856a..2f46969b021e6 100644 --- a/compiler/rustc_lint/src/lib.rs +++ b/compiler/rustc_lint/src/lib.rs @@ -33,9 +33,10 @@ #![feature(box_patterns)] #![feature(crate_visibility_modifier)] #![feature(iter_order_by)] +#![feature(iter_zip)] #![feature(never_type)] #![feature(nll)] -#![feature(or_patterns)] +#![cfg_attr(bootstrap, feature(or_patterns))] #![feature(half_open_range_patterns)] #![feature(exclusive_range_pattern)] #![feature(control_flow_enum)] @@ -57,6 +58,7 @@ mod methods; mod non_ascii_idents; mod non_fmt_panic; mod nonstandard_style; +mod noop_method_call; mod passes; mod redundant_semicolon; mod traits; @@ -81,6 +83,7 @@ use methods::*; use non_ascii_idents::*; use non_fmt_panic::NonPanicFmt; use nonstandard_style::*; +use noop_method_call::*; use redundant_semicolon::*; use traits::*; use types::*; @@ -168,6 +171,7 @@ macro_rules! late_lint_passes { DropTraitConstraints: DropTraitConstraints, TemporaryCStringAsPtr: TemporaryCStringAsPtr, NonPanicFmt: NonPanicFmt, + NoopMethodCall: NoopMethodCall, ] ); }; @@ -202,6 +206,7 @@ macro_rules! late_lint_mod_passes { UnreachablePub: UnreachablePub, ExplicitOutlivesRequirements: ExplicitOutlivesRequirements, InvalidValue: InvalidValue, + DerefNullPtr: DerefNullPtr, ] ); }; @@ -322,6 +327,7 @@ fn register_builtins(store: &mut LintStore, no_interleave_lints: bool) { store.register_renamed("exceeding_bitshifts", "arithmetic_overflow"); store.register_renamed("redundant_semicolon", "redundant_semicolons"); store.register_renamed("overlapping_patterns", "overlapping_range_endpoints"); + store.register_renamed("safe_packed_borrows", "unaligned_references"); // These were moved to tool lints, but rustc still sees them when compiling normally, before // tool lints are registered, so `check_tool_name_for_backwards_compat` doesn't work. Use @@ -337,12 +343,13 @@ fn register_builtins(store: &mut LintStore, no_interleave_lints: bool) { "non_autolinks", ]; for rustdoc_lint in RUSTDOC_LINTS { - store.register_removed(rustdoc_lint, &format!("use `rustdoc::{}` instead", rustdoc_lint)); + store.register_ignored(rustdoc_lint); } store.register_removed( "intra_doc_link_resolution_failure", "use `rustdoc::broken_intra_doc_links` instead", ); + store.register_removed("rustdoc", "use `rustdoc::all` instead"); store.register_removed("unknown_features", "replaced by an error"); store.register_removed("unsigned_negation", "replaced by negate_unsigned feature gate"); diff --git a/compiler/rustc_lint/src/non_ascii_idents.rs b/compiler/rustc_lint/src/non_ascii_idents.rs index a1c7e47e749ad..301e607fc58ad 100644 --- a/compiler/rustc_lint/src/non_ascii_idents.rs +++ b/compiler/rustc_lint/src/non_ascii_idents.rs @@ -10,7 +10,6 @@ declare_lint! { /// /// ```rust,compile_fail /// # #![allow(unused)] - /// #![feature(non_ascii_idents)] /// #![deny(non_ascii_idents)] /// fn main() { /// let föö = 1; @@ -21,14 +20,11 @@ declare_lint! { /// /// ### Explanation /// - /// Currently on stable Rust, identifiers must contain ASCII characters. - /// The [`non_ascii_idents`] nightly-only feature allows identifiers to - /// contain non-ASCII characters. This lint allows projects that wish to - /// retain the limit of only using ASCII characters to switch this lint to - /// "forbid" (for example to ease collaboration or for security reasons). + /// This lint allows projects that wish to retain the limit of only using + /// ASCII characters to switch this lint to "forbid" (for example to ease + /// collaboration or for security reasons). /// See [RFC 2457] for more details. /// - /// [`non_ascii_idents`]: https://doc.rust-lang.org/nightly/unstable-book/language-features/non-ascii-idents.html /// [RFC 2457]: https://github.com/rust-lang/rfcs/blob/master/text/2457-non-ascii-idents.md pub NON_ASCII_IDENTS, Allow, @@ -44,7 +40,6 @@ declare_lint! { /// /// ```rust /// # #![allow(unused)] - /// #![feature(non_ascii_idents)] /// const µ: f64 = 0.000001; /// ``` /// @@ -52,10 +47,8 @@ declare_lint! { /// /// ### Explanation /// - /// With the [`non_ascii_idents`] nightly-only feature enabled, - /// identifiers are allowed to use non-ASCII characters. This lint warns - /// about using characters which are not commonly used, and may cause - /// visual confusion. + /// This lint warns about using characters which are not commonly used, and may + /// cause visual confusion. /// /// This lint is triggered by identifiers that contain a codepoint that is /// not part of the set of "Allowed" codepoints as described by [Unicode® @@ -66,7 +59,6 @@ declare_lint! { /// that if you "forbid" this lint that existing code may fail in the /// future. /// - /// [`non_ascii_idents`]: https://doc.rust-lang.org/nightly/unstable-book/language-features/non-ascii-idents.html /// [TR39Allowed]: https://www.unicode.org/reports/tr39/#General_Security_Profile pub UNCOMMON_CODEPOINTS, Warn, @@ -81,8 +73,6 @@ declare_lint! { /// ### Example /// /// ```rust - /// #![feature(non_ascii_idents)] - /// /// // Latin Capital Letter E With Caron /// pub const Ě: i32 = 1; /// // Latin Capital Letter E With Breve @@ -93,10 +83,8 @@ declare_lint! { /// /// ### Explanation /// - /// With the [`non_ascii_idents`] nightly-only feature enabled, - /// identifiers are allowed to use non-ASCII characters. This lint warns - /// when different identifiers may appear visually similar, which can - /// cause confusion. + /// This lint warns when different identifiers may appear visually similar, + /// which can cause confusion. /// /// The confusable detection algorithm is based on [Unicode® Technical /// Standard #39 Unicode Security Mechanisms Section 4 Confusable @@ -110,7 +98,6 @@ declare_lint! { /// Beware that if you "forbid" this lint that existing code may fail in /// the future. /// - /// [`non_ascii_idents`]: https://doc.rust-lang.org/nightly/unstable-book/language-features/non-ascii-idents.html /// [TR39Confusable]: https://www.unicode.org/reports/tr39/#Confusable_Detection pub CONFUSABLE_IDENTS, Warn, @@ -127,8 +114,6 @@ declare_lint! { /// ### Example /// /// ```rust - /// #![feature(non_ascii_idents)] - /// /// // The Japanese katakana character エ can be confused with the Han character 工. /// const エ: &'static str = "アイウ"; /// ``` @@ -137,10 +122,8 @@ declare_lint! { /// /// ### Explanation /// - /// With the [`non_ascii_idents`] nightly-only feature enabled, - /// identifiers are allowed to use non-ASCII characters. This lint warns - /// when characters between different scripts may appear visually similar, - /// which can cause confusion. + /// This lint warns when characters between different scripts may appear + /// visually similar, which can cause confusion. /// /// If the crate contains other identifiers in the same script that have /// non-confusable characters, then this lint will *not* be issued. For @@ -152,8 +135,6 @@ declare_lint! { /// Note that the set of confusable characters may change over time. /// Beware that if you "forbid" this lint that existing code may fail in /// the future. - /// - /// [`non_ascii_idents`]: https://doc.rust-lang.org/nightly/unstable-book/language-features/non-ascii-idents.html pub MIXED_SCRIPT_CONFUSABLES, Warn, "detects Unicode scripts whose mixed script confusables codepoints are solely used", diff --git a/compiler/rustc_lint/src/nonstandard_style.rs b/compiler/rustc_lint/src/nonstandard_style.rs index 121dde325f74b..be9c6eafb6fdb 100644 --- a/compiler/rustc_lint/src/nonstandard_style.rs +++ b/compiler/rustc_lint/src/nonstandard_style.rs @@ -400,14 +400,15 @@ impl<'tcx> LateLintPass<'tcx> for NonSnakeCase { } _ => (), }, - FnKind::ItemFn(ident, _, header, _, attrs) => { + FnKind::ItemFn(ident, _, header, _) => { + let attrs = cx.tcx.hir().attrs(id); // Skip foreign-ABI #[no_mangle] functions (Issue #31924) if header.abi != Abi::Rust && cx.sess().contains_name(attrs, sym::no_mangle) { return; } self.check_snake_case(cx, "function", ident); } - FnKind::Closure(_) => (), + FnKind::Closure => (), } } @@ -504,8 +505,9 @@ impl NonUpperCaseGlobals { impl<'tcx> LateLintPass<'tcx> for NonUpperCaseGlobals { fn check_item(&mut self, cx: &LateContext<'_>, it: &hir::Item<'_>) { + let attrs = cx.tcx.hir().attrs(it.hir_id()); match it.kind { - hir::ItemKind::Static(..) if !cx.sess().contains_name(&it.attrs, sym::no_mangle) => { + hir::ItemKind::Static(..) if !cx.sess().contains_name(attrs, sym::no_mangle) => { NonUpperCaseGlobals::check_upper_case(cx, "static variable", &it.ident); } hir::ItemKind::Const(..) => { diff --git a/compiler/rustc_lint/src/noop_method_call.rs b/compiler/rustc_lint/src/noop_method_call.rs new file mode 100644 index 0000000000000..479cc00199f6a --- /dev/null +++ b/compiler/rustc_lint/src/noop_method_call.rs @@ -0,0 +1,111 @@ +use crate::context::LintContext; +use crate::rustc_middle::ty::TypeFoldable; +use crate::LateContext; +use crate::LateLintPass; +use rustc_hir::def::DefKind; +use rustc_hir::{Expr, ExprKind}; +use rustc_middle::ty; +use rustc_span::symbol::sym; + +declare_lint! { + /// The `noop_method_call` lint detects specific calls to noop methods + /// such as a calling `<&T as Clone>::clone` where `T: !Clone`. + /// + /// ### Example + /// + /// ```rust + /// # #![allow(unused)] + /// #![warn(noop_method_call)] + /// struct Foo; + /// let foo = &Foo; + /// let clone: &Foo = foo.clone(); + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// Some method calls are noops meaning that they do nothing. Usually such methods + /// are the result of blanket implementations that happen to create some method invocations + /// that end up not doing anything. For instance, `Clone` is implemented on all `&T`, but + /// calling `clone` on a `&T` where `T` does not implement clone, actually doesn't do anything + /// as references are copy. This lint detects these calls and warns the user about them. + pub NOOP_METHOD_CALL, + Allow, + "detects the use of well-known noop methods" +} + +declare_lint_pass!(NoopMethodCall => [NOOP_METHOD_CALL]); + +impl<'tcx> LateLintPass<'tcx> for NoopMethodCall { + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { + // We only care about method calls. + let (call, elements) = match expr.kind { + ExprKind::MethodCall(call, _, elements, _) => (call, elements), + _ => return, + }; + // We only care about method calls corresponding to the `Clone`, `Deref` and `Borrow` + // traits and ignore any other method call. + let (trait_id, did) = match cx.typeck_results().type_dependent_def(expr.hir_id) { + // Verify we are dealing with a method/associated function. + Some((DefKind::AssocFn, did)) => match cx.tcx.trait_of_item(did) { + // Check that we're dealing with a trait method for one of the traits we care about. + Some(trait_id) + if [sym::Clone, sym::Deref, sym::Borrow] + .iter() + .any(|s| cx.tcx.is_diagnostic_item(*s, trait_id)) => + { + (trait_id, did) + } + _ => return, + }, + _ => return, + }; + let substs = cx.typeck_results().node_substs(expr.hir_id); + if substs.needs_subst() { + // We can't resolve on types that require monomorphization, so we don't handle them if + // we need to perfom substitution. + return; + } + let param_env = cx.tcx.param_env(trait_id); + // Resolve the trait method instance. + let i = match ty::Instance::resolve(cx.tcx, param_env, did, substs) { + Ok(Some(i)) => i, + _ => return, + }; + // (Re)check that it implements the noop diagnostic. + for s in [sym::noop_method_clone, sym::noop_method_deref, sym::noop_method_borrow].iter() { + if cx.tcx.is_diagnostic_item(*s, i.def_id()) { + let method = &call.ident.name; + let receiver = &elements[0]; + let receiver_ty = cx.typeck_results().expr_ty(receiver); + let expr_ty = cx.typeck_results().expr_ty_adjusted(expr); + if receiver_ty != expr_ty { + // This lint will only trigger if the receiver type and resulting expression \ + // type are the same, implying that the method call is unnecessary. + return; + } + let expr_span = expr.span; + let note = format!( + "the type `{:?}` which `{}` is being called on is the same as \ + the type returned from `{}`, so the method call does not do \ + anything and can be removed", + receiver_ty, method, method, + ); + + let span = expr_span.with_lo(receiver.span.hi()); + cx.struct_span_lint(NOOP_METHOD_CALL, span, |lint| { + let method = &call.ident.name; + let message = format!( + "call to `.{}()` on a reference in this situation does nothing", + &method, + ); + lint.build(&message) + .span_label(span, "unnecessary method call") + .note(¬e) + .emit() + }); + } + } + } +} diff --git a/compiler/rustc_lint/src/passes.rs b/compiler/rustc_lint/src/passes.rs index ffbed3a0aff2d..bbe17dcf4b736 100644 --- a/compiler/rustc_lint/src/passes.rs +++ b/compiler/rustc_lint/src/passes.rs @@ -57,7 +57,7 @@ macro_rules! late_lint_methods { fn check_impl_item_post(a: &$hir hir::ImplItem<$hir>); fn check_struct_def(a: &$hir hir::VariantData<$hir>); fn check_struct_def_post(a: &$hir hir::VariantData<$hir>); - fn check_struct_field(a: &$hir hir::StructField<$hir>); + fn check_field_def(a: &$hir hir::FieldDef<$hir>); fn check_variant(a: &$hir hir::Variant<$hir>); fn check_variant_post(a: &$hir hir::Variant<$hir>); fn check_lifetime(a: &$hir hir::Lifetime); @@ -193,7 +193,7 @@ macro_rules! early_lint_methods { fn check_impl_item_post(a: &ast::AssocItem); fn check_struct_def(a: &ast::VariantData); fn check_struct_def_post(a: &ast::VariantData); - fn check_struct_field(a: &ast::StructField); + fn check_field_def(a: &ast::FieldDef); fn check_variant(a: &ast::Variant); fn check_variant_post(a: &ast::Variant); fn check_lifetime(a: &ast::Lifetime); diff --git a/compiler/rustc_lint/src/types.rs b/compiler/rustc_lint/src/types.rs index 792655ff35a54..9c94bab04e98f 100644 --- a/compiler/rustc_lint/src/types.rs +++ b/compiler/rustc_lint/src/types.rs @@ -17,6 +17,7 @@ use rustc_target::abi::{Integer, LayoutOf, TagEncoding, VariantIdx, Variants}; use rustc_target::spec::abi::Abi as SpecAbi; use std::cmp; +use std::iter; use std::ops::ControlFlow; use tracing::debug; @@ -217,7 +218,11 @@ fn report_bin_hex_error( cx.struct_span_lint(OVERFLOWING_LITERALS, expr.span, |lint| { let (t, actually) = match ty { attr::IntType::SignedInt(t) => { - let actually = size.sign_extend(val) as i128; + let actually = if negative { + -(size.sign_extend(val) as i128) + } else { + size.sign_extend(val) as i128 + }; (t.name_str(), actually.to_string()) } attr::IntType::UnsignedInt(t) => { @@ -226,11 +231,22 @@ fn report_bin_hex_error( } }; let mut err = lint.build(&format!("literal out of range for `{}`", t)); - err.note(&format!( - "the literal `{}` (decimal `{}`) does not fit into \ - the type `{}` and will become `{}{}`", - repr_str, val, t, actually, t - )); + if negative { + // If the value is negative, + // emits a note about the value itself, apart from the literal. + err.note(&format!( + "the literal `{}` (decimal `{}`) does not fit into \ + the type `{}`", + repr_str, val, t + )); + err.note(&format!("and the value `-{}` will become `{}{}`", repr_str, actually, t)); + } else { + err.note(&format!( + "the literal `{}` (decimal `{}`) does not fit into \ + the type `{}` and will become `{}{}`", + repr_str, val, t, actually, t + )); + } if let Some(sugg_ty) = get_type_suggestion(&cx.typeck_results().node_type(expr.hir_id), val, negative) { @@ -1240,7 +1256,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { let sig = self.cx.tcx.fn_sig(def_id); let sig = self.cx.tcx.erase_late_bound_regions(sig); - for (input_ty, input_hir) in sig.inputs().iter().zip(decl.inputs) { + for (input_ty, input_hir) in iter::zip(sig.inputs(), decl.inputs) { self.check_type_for_ffi_and_report_errors(input_hir.span, input_ty, false, false); } @@ -1340,10 +1356,7 @@ impl<'tcx> LateLintPass<'tcx> for VariantSizeDifferences { layout ); - let (largest, slargest, largest_index) = enum_definition - .variants - .iter() - .zip(variants) + let (largest, slargest, largest_index) = iter::zip(enum_definition.variants, variants) .map(|(variant, variant_layout)| { // Subtract the size of the enum tag. let bytes = variant_layout.size.bytes().saturating_sub(tag_size); diff --git a/compiler/rustc_lint/src/unused.rs b/compiler/rustc_lint/src/unused.rs index b611aebad01b0..67946dfb292a6 100644 --- a/compiler/rustc_lint/src/unused.rs +++ b/compiler/rustc_lint/src/unused.rs @@ -406,6 +406,8 @@ impl<'tcx> LateLintPass<'tcx> for UnusedAttributes { if !cx.sess().is_attr_used(attr) { debug!("emitting warning for: {:?}", attr); cx.struct_span_lint(UNUSED_ATTRIBUTES, attr.span, |lint| { + // Mark as used to avoid duplicate warnings. + cx.sess().mark_attr_used(attr); lint.build("unused attribute").emit() }); // Is it a builtin attribute that must be used at the crate level? @@ -602,7 +604,7 @@ trait UnusedDelimLint { use rustc_ast::ExprKind::*; let (value, ctx, followed_by_block, left_pos, right_pos) = match e.kind { // Do not lint `unused_braces` in `if let` expressions. - If(ref cond, ref block, ..) + If(ref cond, ref block, _) if !matches!(cond.kind, Let(_, _)) || Self::LINT_EXPR_IN_PATTERN_MATCHING_CTX => { let left = e.span.lo() + rustc_span::BytePos(2); @@ -816,8 +818,33 @@ impl UnusedParens { impl EarlyLintPass for UnusedParens { fn check_expr(&mut self, cx: &EarlyContext<'_>, e: &ast::Expr) { - if let ExprKind::Let(ref pat, ..) | ExprKind::ForLoop(ref pat, ..) = e.kind { - self.check_unused_parens_pat(cx, pat, false, false); + match e.kind { + ExprKind::Let(ref pat, _) | ExprKind::ForLoop(ref pat, ..) => { + self.check_unused_parens_pat(cx, pat, false, false); + } + // We ignore parens in cases like `if (((let Some(0) = Some(1))))` because we already + // handle a hard error for them during AST lowering in `lower_expr_mut`, but we still + // want to complain about things like `if let 42 = (42)`. + ExprKind::If(ref cond, ref block, ref else_) + if matches!(cond.peel_parens().kind, ExprKind::Let(..)) => + { + self.check_unused_delims_expr( + cx, + cond.peel_parens(), + UnusedDelimsCtx::LetScrutineeExpr, + true, + None, + None, + ); + for stmt in &block.stmts { + ::check_stmt(self, cx, stmt); + } + if let Some(e) = else_ { + ::check_expr(self, cx, e); + } + return; + } + _ => {} } ::check_expr(self, cx, e) @@ -847,7 +874,7 @@ impl EarlyLintPass for UnusedParens { fn check_stmt(&mut self, cx: &EarlyContext<'_>, s: &ast::Stmt) { if let StmtKind::Local(ref local) = s.kind { - self.check_unused_parens_pat(cx, &local.pat, false, false); + self.check_unused_parens_pat(cx, &local.pat, true, false); } ::check_stmt(self, cx, s) diff --git a/compiler/rustc_lint_defs/src/builtin.rs b/compiler/rustc_lint_defs/src/builtin.rs index 12d849e3b9466..f15a7cc5ec2ca 100644 --- a/compiler/rustc_lint_defs/src/builtin.rs +++ b/compiler/rustc_lint_defs/src/builtin.rs @@ -6,9 +6,8 @@ //! compiler code, rather than using their own custom pass. Those //! lints are all available in `rustc_lint::builtin`. -use crate::{declare_lint, declare_lint_pass}; +use crate::{declare_lint, declare_lint_pass, FutureBreakage}; use rustc_span::edition::Edition; -use rustc_span::symbol::sym; declare_lint! { /// The `forbidden_lint_groups` lint detects violations of @@ -548,7 +547,7 @@ declare_lint! { /// Also consider if you intended to use an _inner attribute_ (with a `!` /// such as `#![allow(unused)]`) which applies to the item the attribute /// is within, or an _outer attribute_ (without a `!` such as - /// `#[allow(unsued)]`) which applies to the item *following* the + /// `#[allow(unused)]`) which applies to the item *following* the /// attribute. /// /// [attributes]: https://doc.rust-lang.org/reference/attributes.html @@ -1058,6 +1057,7 @@ declare_lint! { /// unsafe { /// let foo = Foo { field1: 0, field2: 0 }; /// let _ = &foo.field1; + /// println!("{}", foo.field1); // An implicit `&` is added here, triggering the lint. /// } /// } /// ``` @@ -1066,20 +1066,20 @@ declare_lint! { /// /// ### Explanation /// - /// Creating a reference to an insufficiently aligned packed field is - /// [undefined behavior] and should be disallowed. - /// - /// This lint is "allow" by default because there is no stable - /// alternative, and it is not yet certain how widespread existing code - /// will trigger this lint. - /// - /// See [issue #27060] for more discussion. + /// Creating a reference to an insufficiently aligned packed field is [undefined behavior] and + /// should be disallowed. Using an `unsafe` block does not change anything about this. Instead, + /// the code should do a copy of the data in the packed field or use raw pointers and unaligned + /// accesses. See [issue #82523] for more information. /// /// [undefined behavior]: https://doc.rust-lang.org/reference/behavior-considered-undefined.html - /// [issue #27060]: https://github.com/rust-lang/rust/issues/27060 + /// [issue #82523]: https://github.com/rust-lang/rust/issues/82523 pub UNALIGNED_REFERENCES, - Allow, + Warn, "detects unaligned references to fields of packed structs", + @future_incompatible = FutureIncompatibleInfo { + reference: "issue #82523 ", + edition: None, + }; report_in_external_macro } @@ -1151,49 +1151,6 @@ declare_lint! { "detects attempts to mutate a `const` item", } -declare_lint! { - /// The `safe_packed_borrows` lint detects borrowing a field in the - /// interior of a packed structure with alignment other than 1. - /// - /// ### Example - /// - /// ```rust - /// #[repr(packed)] - /// pub struct Unaligned(pub T); - /// - /// pub struct Foo { - /// start: u8, - /// data: Unaligned, - /// } - /// - /// fn main() { - /// let x = Foo { start: 0, data: Unaligned(1) }; - /// let y = &x.data.0; - /// } - /// ``` - /// - /// {{produces}} - /// - /// ### Explanation - /// - /// This type of borrow is unsafe and can cause errors on some platforms - /// and violates some assumptions made by the compiler. This was - /// previously allowed unintentionally. This is a [future-incompatible] - /// lint to transition this to a hard error in the future. See [issue - /// #46043] for more details, including guidance on how to solve the - /// problem. - /// - /// [issue #46043]: https://github.com/rust-lang/rust/issues/46043 - /// [future-incompatible]: ../index.md#future-incompatible-lints - pub SAFE_PACKED_BORROWS, - Warn, - "safe borrows of fields of packed structs were erroneously allowed", - @future_incompatible = FutureIncompatibleInfo { - reference: "issue #46043 ", - edition: None, - }; -} - declare_lint! { /// The `patterns_in_fns_without_body` lint detects `mut` identifier /// patterns as a parameter in functions without a body. @@ -1978,7 +1935,7 @@ declare_lint! { Warn, "detects proc macro derives using inaccessible names from parent modules", @future_incompatible = FutureIncompatibleInfo { - reference: "issue #50504 ", + reference: "issue #83583 ", edition: None, }; } @@ -2488,17 +2445,58 @@ declare_lint! { } declare_lint! { - /// The `unsafe_op_in_unsafe_fn` lint detects unsafe operations in unsafe - /// functions without an explicit unsafe block. This lint only works on - /// the [**nightly channel**] with the - /// `#![feature(unsafe_block_in_unsafe_fn)]` feature. + /// The `bad_asm_style` lint detects the use of the `.intel_syntax` and + /// `.att_syntax` directives. + /// + /// ### Example + /// + /// ```rust,ignore (fails on system llvm) + /// #![feature(asm)] + /// + /// fn main() { + /// #[cfg(target_arch="x86_64")] + /// unsafe { + /// asm!( + /// ".att_syntax", + /// "movl {0}, {0}", in(reg) 0usize + /// ); + /// } + /// } + /// ``` + /// + /// This will produce: + /// + /// ```text + /// warning: avoid using `.att_syntax`, prefer using `options(att_syntax)` instead + /// --> test.rs:7:14 + /// | + /// 7 | ".att_syntax", + /// | ^^^^^^^^^^^ + /// 8 | "movq {0}, {0}", out(reg) _, + /// 9 | ); + /// | - help: add option: `, options(att_syntax)` + /// | + /// = note: `#[warn(bad_asm_style)]` on by default + /// ``` + /// + /// ### Explanation /// - /// [**nightly channel**]: https://doc.rust-lang.org/book/appendix-07-nightly-rust.html + /// On x86, `asm!` uses the intel assembly syntax by default. While this + /// can be switched using assembler directives like `.att_syntax`, using the + /// `att_syntax` option is recommended instead because it will also properly + /// prefix register placeholders with `%` as required by AT&T syntax. + pub BAD_ASM_STYLE, + Warn, + "incorrect use of inline assembly", +} + +declare_lint! { + /// The `unsafe_op_in_unsafe_fn` lint detects unsafe operations in unsafe + /// functions without an explicit unsafe block. /// /// ### Example /// /// ```rust,compile_fail - /// #![feature(unsafe_block_in_unsafe_fn)] /// #![deny(unsafe_op_in_unsafe_fn)] /// /// unsafe fn foo() {} @@ -2524,9 +2522,10 @@ declare_lint! { /// /// The fix to this is to wrap the unsafe code in an `unsafe` block. /// - /// This lint is "allow" by default because it has not yet been - /// stabilized, and is not yet complete. See [RFC #2585] and [issue - /// #71668] for more details + /// This lint is "allow" by default since this will affect a large amount + /// of existing code, and the exact plan for increasing the severity is + /// still being considered. See [RFC #2585] and [issue #71668] for more + /// details. /// /// [`unsafe fn`]: https://doc.rust-lang.org/reference/unsafe-functions.html /// [`unsafe` block]: https://doc.rust-lang.org/reference/expressions/block-expr.html#unsafe-blocks @@ -2536,7 +2535,6 @@ declare_lint! { pub UNSAFE_OP_IN_UNSAFE_FN, Allow, "unsafe operations in unsafe functions without an explicit unsafe block are deprecated", - @feature_gate = sym::unsafe_block_in_unsafe_fn; } declare_lint! { @@ -2680,7 +2678,7 @@ declare_lint! { /// Statics with an uninhabited type can never be initialized, so they are impossible to define. /// However, this can be side-stepped with an `extern static`, leading to problems later in the /// compiler which assumes that there are no initialized uninhabited places (such as locals or - /// statics). This was accientally allowed, but is being phased out. + /// statics). This was accidentally allowed, but is being phased out. pub UNINHABITED_STATIC, Warn, "uninhabited static", @@ -2914,7 +2912,6 @@ declare_lint_pass! { RENAMED_AND_REMOVED_LINTS, UNALIGNED_REFERENCES, CONST_ITEM_MUTATION, - SAFE_PACKED_BORROWS, PATTERNS_IN_FNS_WITHOUT_BODY, MISSING_FRAGMENT_SPECIFIER, LATE_BOUND_LIFETIME_ARGUMENTS, @@ -2948,6 +2945,7 @@ declare_lint_pass! { NONTRIVIAL_STRUCTURAL_MATCH, SOFT_UNSTABLE, INLINE_NO_SANITIZE, + BAD_ASM_STYLE, ASM_SUB_REGISTER, UNSAFE_OP_IN_UNSAFE_FN, INCOMPLETE_INCLUDE, @@ -2962,6 +2960,8 @@ declare_lint_pass! { SEMICOLON_IN_EXPRESSIONS_FROM_MACROS, DISJOINT_CAPTURE_DROP_REORDER, LEGACY_DERIVE_HELPERS, + PROC_MACRO_BACK_COMPAT, + OR_PATTERNS_BACK_COMPAT, ] } @@ -3059,3 +3059,117 @@ declare_lint! { Allow, "No declared ABI for extern declaration" } + +declare_lint! { + /// The `invalid_doc_attributes` lint detects when the `#[doc(...)]` is + /// misused. + /// + /// ### Example + /// + /// ```rust,compile_fail + /// #![deny(warnings)] + /// + /// pub mod submodule { + /// #![doc(test(no_crate_inject))] + /// } + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// Previously, there were very like checks being performed on `#[doc(..)]` + /// unlike the other attributes. It'll now catch all the issues that it + /// silently ignored previously. + pub INVALID_DOC_ATTRIBUTES, + Warn, + "detects invalid `#[doc(...)]` attributes", + @future_incompatible = FutureIncompatibleInfo { + reference: "issue #82730 ", + edition: None, + }; +} + +declare_lint! { + /// The `proc_macro_back_compat` lint detects uses of old versions of certain + /// proc-macro crates, which have hardcoded workarounds in the compiler. + /// + /// ### Example + /// + /// ```rust,ignore (needs-dependency) + /// + /// use time_macros_impl::impl_macros; + /// struct Foo; + /// impl_macros!(Foo); + /// ``` + /// + /// This will produce: + /// + /// ```text + /// warning: using an old version of `time-macros-impl` + /// ::: $DIR/group-compat-hack.rs:27:5 + /// | + /// LL | impl_macros!(Foo); + /// | ------------------ in this macro invocation + /// | + /// = note: `#[warn(proc_macro_back_compat)]` on by default + /// = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + /// = note: for more information, see issue #83125 + /// = note: the `time-macros-impl` crate will stop compiling in futures version of Rust. Please update to the latest version of the `time` crate to avoid breakage + /// = note: this warning originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) + /// ``` + /// + /// ### Explanation + /// + /// Eventually, the backwards-compatibility hacks present in the compiler will be removed, + /// causing older versions of certain crates to stop compiling. + /// This is a [future-incompatible] lint to ease the transition to an error. + /// See [issue #83125] for more details. + /// + /// [issue #83125]: https://github.com/rust-lang/rust/issues/83125 + /// [future-incompatible]: ../index.md#future-incompatible-lints + pub PROC_MACRO_BACK_COMPAT, + Warn, + "detects usage of old versions of certain proc-macro crates", + @future_incompatible = FutureIncompatibleInfo { + reference: "issue #83125 ", + edition: None, + future_breakage: Some(FutureBreakage { + date: None + }) + }; +} + +declare_lint! { + /// The `or_patterns_back_compat` lint detects usage of old versions of or-patterns. + /// + /// ### Example + /// + /// ```rust,compile_fail + /// #![deny(or_patterns_back_compat)] + /// macro_rules! match_any { + /// ( $expr:expr , $( $( $pat:pat )|+ => $expr_arm:expr ),+ ) => { + /// match $expr { + /// $( + /// $( $pat => $expr_arm, )+ + /// )+ + /// } + /// }; + /// } + /// + /// fn main() { + /// let result: Result = Err(42); + /// let int: i64 = match_any!(result, Ok(i) | Err(i) => i.into()); + /// assert_eq!(int, 42); + /// } + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// In Rust 2021, the pat matcher will match new patterns, which include the | character. + pub OR_PATTERNS_BACK_COMPAT, + Allow, + "detects usage of old versions of or-patterns", +} diff --git a/compiler/rustc_lint_defs/src/lib.rs b/compiler/rustc_lint_defs/src/lib.rs index 4c7d3f6c8c072..70475563a4abe 100644 --- a/compiler/rustc_lint_defs/src/lib.rs +++ b/compiler/rustc_lint_defs/src/lib.rs @@ -266,6 +266,8 @@ pub enum BuiltinLintDiagnostics { PatternsInFnsWithoutBody(Span, Ident), LegacyDeriveHelpers(Span), ExternDepSpec(String, ExternDepSpec), + ProcMacroBackCompat(String), + OrPatternsBackCompat(Span, String), } /// Lints that are buffered up early on in the `Session` before the diff --git a/compiler/rustc_llvm/build.rs b/compiler/rustc_llvm/build.rs index 621363bed80e1..301ed639f3b51 100644 --- a/compiler/rustc_llvm/build.rs +++ b/compiler/rustc_llvm/build.rs @@ -88,16 +88,6 @@ fn main() { "riscv", ]; - let mut version_cmd = Command::new(&llvm_config); - version_cmd.arg("--version"); - let version_output = output(&mut version_cmd); - let mut parts = version_output.split('.').take(2).filter_map(|s| s.parse::().ok()); - let (major, _minor) = if let (Some(major), Some(minor)) = (parts.next(), parts.next()) { - (major, minor) - } else { - (8, 0) - }; - let required_components = &[ "ipo", "bitreader", @@ -123,10 +113,6 @@ fn main() { println!("cargo:rustc-cfg=llvm_component=\"{}\"", component); } - if major >= 9 { - println!("cargo:rustc-cfg=llvm_has_msp430_asm_parser"); - } - // Link in our own LLVM shims, compiled with the same flags as LLVM let mut cmd = Command::new(&llvm_config); cmd.arg("--cxxflags"); diff --git a/compiler/rustc_llvm/llvm-wrapper/ArchiveWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/ArchiveWrapper.cpp index 2797fe8df4a8e..97541e615da83 100644 --- a/compiler/rustc_llvm/llvm-wrapper/ArchiveWrapper.cpp +++ b/compiler/rustc_llvm/llvm-wrapper/ArchiveWrapper.cpp @@ -91,11 +91,7 @@ extern "C" void LLVMRustDestroyArchive(LLVMRustArchiveRef RustArchive) { extern "C" LLVMRustArchiveIteratorRef LLVMRustArchiveIteratorNew(LLVMRustArchiveRef RustArchive) { Archive *Archive = RustArchive->getBinary(); -#if LLVM_VERSION_GE(10, 0) std::unique_ptr Err = std::make_unique(Error::success()); -#else - std::unique_ptr Err = llvm::make_unique(Error::success()); -#endif auto Cur = Archive->child_begin(*Err); if (*Err) { LLVMRustSetLastError(toString(std::move(*Err)).c_str()); diff --git a/compiler/rustc_llvm/llvm-wrapper/CoverageMappingWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/CoverageMappingWrapper.cpp index e97d96e3a4eab..35cca04b20f75 100644 --- a/compiler/rustc_llvm/llvm-wrapper/CoverageMappingWrapper.cpp +++ b/compiler/rustc_llvm/llvm-wrapper/CoverageMappingWrapper.cpp @@ -23,10 +23,17 @@ extern "C" void LLVMRustCoverageWriteFilenamesSectionToBuffer( const char* const Filenames[], size_t FilenamesLen, RustStringRef BufferOut) { +#if LLVM_VERSION_GE(13,0) + SmallVector FilenameRefs; + for (size_t i = 0; i < FilenamesLen; i++) { + FilenameRefs.push_back(std::string(Filenames[i])); + } +#else SmallVector FilenameRefs; for (size_t i = 0; i < FilenamesLen; i++) { FilenameRefs.push_back(StringRef(Filenames[i])); } +#endif auto FilenamesWriter = coverage::CoverageFilenamesSectionWriter( makeArrayRef(FilenameRefs)); RawRustStringOstream OS(BufferOut); diff --git a/compiler/rustc_llvm/llvm-wrapper/LLVMWrapper.h b/compiler/rustc_llvm/llvm-wrapper/LLVMWrapper.h index f67e06706ea57..0b1b68d83b7b9 100644 --- a/compiler/rustc_llvm/llvm-wrapper/LLVMWrapper.h +++ b/compiler/rustc_llvm/llvm-wrapper/LLVMWrapper.h @@ -33,13 +33,6 @@ (LLVM_VERSION_MAJOR > (major) || \ LLVM_VERSION_MAJOR == (major) && LLVM_VERSION_MINOR >= (minor)) -#define LLVM_VERSION_EQ(major, minor) \ - (LLVM_VERSION_MAJOR == (major) && LLVM_VERSION_MINOR == (minor)) - -#define LLVM_VERSION_LE(major, minor) \ - (LLVM_VERSION_MAJOR < (major) || \ - LLVM_VERSION_MAJOR == (major) && LLVM_VERSION_MINOR <= (minor)) - #define LLVM_VERSION_LT(major, minor) (!LLVM_VERSION_GE((major), (minor))) #include "llvm/IR/LegacyPassManager.h" diff --git a/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp index 2f28162f908ac..617b2ed970eee 100644 --- a/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp +++ b/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp @@ -65,13 +65,9 @@ extern "C" void LLVMInitializePasses() { } extern "C" void LLVMTimeTraceProfilerInitialize() { -#if LLVM_VERSION_GE(10, 0) timeTraceProfilerInitialize( /* TimeTraceGranularity */ 0, /* ProcName */ "rustc"); -#else - timeTraceProfilerInitialize(); -#endif } extern "C" void LLVMTimeTraceProfilerFinish(const char* FileName) { @@ -408,26 +404,21 @@ extern "C" void LLVMRustPrintTargetCPUs(LLVMTargetMachineRef TM) { printf("\n"); } -extern "C" void LLVMRustPrintTargetFeatures(LLVMTargetMachineRef TM) { +extern "C" size_t LLVMRustGetTargetFeaturesCount(LLVMTargetMachineRef TM) { const TargetMachine *Target = unwrap(TM); const MCSubtargetInfo *MCInfo = Target->getMCSubtargetInfo(); const ArrayRef FeatTable = MCInfo->getFeatureTable(); - unsigned MaxFeatLen = getLongestEntryLength(FeatTable); - - printf("Available features for this target:\n"); - for (auto &Feature : FeatTable) - printf(" %-*s - %s.\n", MaxFeatLen, Feature.Key, Feature.Desc); - printf("\nRust-specific features:\n"); - printf(" %-*s - %s.\n", - MaxFeatLen, - "crt-static", - "Enables libraries with C Run-time Libraries(CRT) to be statically linked" - ); - printf("\n"); + return FeatTable.size(); +} - printf("Use +feature to enable a feature, or -feature to disable it.\n" - "For example, rustc -C -target-cpu=mycpu -C " - "target-feature=+feature1,-feature2\n\n"); +extern "C" void LLVMRustGetTargetFeature(LLVMTargetMachineRef TM, size_t Index, + const char** Feature, const char** Desc) { + const TargetMachine *Target = unwrap(TM); + const MCSubtargetInfo *MCInfo = Target->getMCSubtargetInfo(); + const ArrayRef FeatTable = MCInfo->getFeatureTable(); + const SubtargetFeatureKV Feat = FeatTable[Index]; + *Feature = Feat.Key; + *Desc = Feat.Desc; } #else @@ -436,9 +427,11 @@ extern "C" void LLVMRustPrintTargetCPUs(LLVMTargetMachineRef) { printf("Target CPU help is not supported by this LLVM version.\n\n"); } -extern "C" void LLVMRustPrintTargetFeatures(LLVMTargetMachineRef) { - printf("Target features help is not supported by this LLVM version.\n\n"); +extern "C" size_t LLVMRustGetTargetFeaturesCount(LLVMTargetMachineRef) { + return 0; } + +extern "C" void LLVMRustGetTargetFeature(LLVMTargetMachineRef, const char**, const char**) {} #endif extern "C" const char* LLVMRustGetHostCPUName(size_t *len) { @@ -596,7 +589,6 @@ enum class LLVMRustFileType { ObjectFile, }; -#if LLVM_VERSION_GE(10, 0) static CodeGenFileType fromRust(LLVMRustFileType Type) { switch (Type) { case LLVMRustFileType::AssemblyFile: @@ -607,18 +599,6 @@ static CodeGenFileType fromRust(LLVMRustFileType Type) { report_fatal_error("Bad FileType."); } } -#else -static TargetMachine::CodeGenFileType fromRust(LLVMRustFileType Type) { - switch (Type) { - case LLVMRustFileType::AssemblyFile: - return TargetMachine::CGFT_AssemblyFile; - case LLVMRustFileType::ObjectFile: - return TargetMachine::CGFT_ObjectFile; - default: - report_fatal_error("Bad FileType."); - } -} -#endif extern "C" LLVMRustResult LLVMRustWriteOutputFile(LLVMTargetMachineRef Target, LLVMPassManagerRef PMR, @@ -772,14 +752,18 @@ LLVMRustOptimizeWithNewPassManager( TargetMachine *TM = unwrap(TMRef); PassBuilder::OptimizationLevel OptLevel = fromRust(OptLevelRust); - // FIXME: MergeFunctions is not supported by NewPM yet. - (void) MergeFunctions; PipelineTuningOptions PTO; PTO.LoopUnrolling = UnrollLoops; PTO.LoopInterleaving = UnrollLoops; PTO.LoopVectorization = LoopVectorize; PTO.SLPVectorization = SLPVectorize; +#if LLVM_VERSION_GE(12, 0) + PTO.MergeFunctions = MergeFunctions; +#else + // MergeFunctions is not supported by NewPM in older LLVM versions. + (void) MergeFunctions; +#endif // FIXME: We may want to expose this as an option. bool DebugPassManager = false; @@ -864,13 +848,11 @@ LLVMRustOptimizeWithNewPassManager( } ); #else -#if LLVM_VERSION_GE(10, 0) PipelineStartEPCallbacks.push_back( [Options](ModulePassManager &MPM, PassBuilder::OptimizationLevel Level) { MPM.addPass(MemorySanitizerPass(Options)); } ); -#endif OptimizerLastEPCallbacks.push_back( [Options](FunctionPassManager &FPM, PassBuilder::OptimizationLevel Level) { FPM.addPass(MemorySanitizerPass(Options)); @@ -888,13 +870,11 @@ LLVMRustOptimizeWithNewPassManager( } ); #else -#if LLVM_VERSION_GE(10, 0) PipelineStartEPCallbacks.push_back( [](ModulePassManager &MPM, PassBuilder::OptimizationLevel Level) { MPM.addPass(ThreadSanitizerPass()); } ); -#endif OptimizerLastEPCallbacks.push_back( [](FunctionPassManager &FPM, PassBuilder::OptimizationLevel Level) { FPM.addPass(ThreadSanitizerPass()); @@ -985,13 +965,11 @@ LLVMRustOptimizeWithNewPassManager( MPM.addPass(AlwaysInlinerPass(EmitLifetimeMarkers)); -# if LLVM_VERSION_GE(10, 0) if (PGOOpt) { PB.addPGOInstrPassesForO0( MPM, DebugPassManager, PGOOpt->Action == PGOOptions::IRInstr, /*IsCS=*/false, PGOOpt->ProfileFile, PGOOpt->ProfileRemappingFile); } -# endif #endif } else { #if LLVM_VERSION_GE(12, 0) @@ -1260,6 +1238,14 @@ extern "C" void LLVMRustSetModulePIELevel(LLVMModuleRef M) { unwrap(M)->setPIELevel(PIELevel::Level::Large); } +extern "C" void LLVMRustSetModuleCodeModel(LLVMModuleRef M, + LLVMRustCodeModel Model) { + auto CM = fromRust(Model); + if (!CM.hasValue()) + return; + unwrap(M)->setCodeModel(*CM); +} + // Here you'll find an implementation of ThinLTO as used by the Rust compiler // right now. This ThinLTO support is only enabled on "recent ish" versions of // LLVM, and otherwise it's just blanket rejected from other compilers. @@ -1354,11 +1340,7 @@ LLVMRustCreateThinLTOData(LLVMRustThinLTOModule *modules, int num_modules, const char **preserved_symbols, int num_symbols) { -#if LLVM_VERSION_GE(10, 0) auto Ret = std::make_unique(); -#else - auto Ret = llvm::make_unique(); -#endif // Load each module's summary and merge it into one combined index for (int i = 0; i < num_modules; i++) { @@ -1425,9 +1407,17 @@ LLVMRustCreateThinLTOData(LLVMRustThinLTOModule *modules, Ret->ResolvedODR[ModuleIdentifier][GUID] = NewLinkage; }; +#if LLVM_VERSION_GE(13,0) + // Uses FromPrevailing visibility scheme which works for many binary + // formats. We probably could and should use ELF visibility scheme for many of + // our targets, however. + lto::Config conf; + thinLTOResolvePrevailingInIndex(conf, Ret->Index, isPrevailing, recordNewLinkage, + Ret->GUIDPreservedSymbols); +#else thinLTOResolvePrevailingInIndex(Ret->Index, isPrevailing, recordNewLinkage, Ret->GUIDPreservedSymbols); - +#endif // Here we calculate an `ExportedGUIDs` set for use in the `isExported` // callback below. This callback below will dictate the linkage for all // summaries in the index, and we basically just only want to ensure that dead @@ -1443,7 +1433,6 @@ LLVMRustCreateThinLTOData(LLVMRustThinLTOModule *modules, ExportedGUIDs.insert(GUID); } } -#if LLVM_VERSION_GE(10, 0) auto isExported = [&](StringRef ModuleIdentifier, ValueInfo VI) { const auto &ExportList = Ret->ExportLists.find(ModuleIdentifier); return (ExportList != Ret->ExportLists.end() && @@ -1451,15 +1440,6 @@ LLVMRustCreateThinLTOData(LLVMRustThinLTOModule *modules, ExportedGUIDs.count(VI.getGUID()); }; thinLTOInternalizeAndPromoteInIndex(Ret->Index, isExported, isPrevailing); -#else - auto isExported = [&](StringRef ModuleIdentifier, GlobalValue::GUID GUID) { - const auto &ExportList = Ret->ExportLists.find(ModuleIdentifier); - return (ExportList != Ret->ExportLists.end() && - ExportList->second.count(GUID)) || - ExportedGUIDs.count(GUID); - }; - thinLTOInternalizeAndPromoteInIndex(Ret->Index, isExported); -#endif return Ret.release(); } @@ -1616,11 +1596,7 @@ struct LLVMRustThinLTOBuffer { extern "C" LLVMRustThinLTOBuffer* LLVMRustThinLTOBufferCreate(LLVMModuleRef M) { -#if LLVM_VERSION_GE(10, 0) auto Ret = std::make_unique(); -#else - auto Ret = llvm::make_unique(); -#endif { raw_string_ostream OS(Ret->data); { diff --git a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp index a853659540442..2e135fbe2bd8f 100644 --- a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp +++ b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp @@ -349,8 +349,10 @@ extern "C" void LLVMRustRemoveFunctionAttributes(LLVMValueRef Fn, F->setAttributes(PALNew); } -// enable fpmath flag UnsafeAlgebra -extern "C" void LLVMRustSetHasUnsafeAlgebra(LLVMValueRef V) { +// Enable a fast-math flag +// +// https://llvm.org/docs/LangRef.html#fast-math-flags +extern "C" void LLVMRustSetFastMath(LLVMValueRef V) { if (auto I = dyn_cast(unwrap(V))) { I->setFast(true); } @@ -382,9 +384,18 @@ LLVMRustBuildAtomicCmpXchg(LLVMBuilderRef B, LLVMValueRef Target, LLVMValueRef Old, LLVMValueRef Source, LLVMAtomicOrdering Order, LLVMAtomicOrdering FailureOrder, LLVMBool Weak) { +#if LLVM_VERSION_GE(13,0) + // Rust probably knows the alignment of the target value and should be able to + // specify something more precise than MaybeAlign here. See also + // https://reviews.llvm.org/D97224 which may be a useful reference. + AtomicCmpXchgInst *ACXI = unwrap(B)->CreateAtomicCmpXchg( + unwrap(Target), unwrap(Old), unwrap(Source), llvm::MaybeAlign(), fromRust(Order), + fromRust(FailureOrder)); +#else AtomicCmpXchgInst *ACXI = unwrap(B)->CreateAtomicCmpXchg( unwrap(Target), unwrap(Old), unwrap(Source), fromRust(Order), fromRust(FailureOrder)); +#endif ACXI->setWeak(Weak); return wrap(ACXI); } @@ -532,11 +543,6 @@ static DINode::DIFlags fromRust(LLVMRustDIFlags Flags) { if (isSet(Flags & LLVMRustDIFlags::FlagAppleBlock)) { Result |= DINode::DIFlags::FlagAppleBlock; } -#if LLVM_VERSION_LT(10, 0) - if (isSet(Flags & LLVMRustDIFlags::FlagBlockByrefStruct)) { - Result |= DINode::DIFlags::FlagBlockByrefStruct; - } -#endif if (isSet(Flags & LLVMRustDIFlags::FlagVirtual)) { Result |= DINode::DIFlags::FlagVirtual; } @@ -901,9 +907,7 @@ extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateStaticVariable( unwrapDI(Context), StringRef(Name, NameLen), StringRef(LinkageName, LinkageNameLen), unwrapDI(File), LineNo, unwrapDI(Ty), IsLocalToUnit, -#if LLVM_VERSION_GE(10, 0) /* isDefined */ true, -#endif InitExpr, unwrapDIPtr(Decl), /* templateParams */ nullptr, AlignInBits); @@ -1090,19 +1094,11 @@ inline section_iterator *unwrap(LLVMSectionIteratorRef SI) { extern "C" size_t LLVMRustGetSectionName(LLVMSectionIteratorRef SI, const char **Ptr) { -#if LLVM_VERSION_GE(10, 0) auto NameOrErr = (*unwrap(SI))->getName(); if (!NameOrErr) report_fatal_error(NameOrErr.takeError()); *Ptr = NameOrErr->data(); return NameOrErr->size(); -#else - StringRef Ret; - if (std::error_code EC = (*unwrap(SI))->getName(Ret)) - report_fatal_error(EC.message()); - *Ptr = Ret.data(); - return Ret.size(); -#endif } // LLVMArrayType function does not support 64-bit ElementCount @@ -1304,9 +1300,19 @@ extern "C" LLVMTypeKind LLVMRustGetTypeKind(LLVMTypeRef Ty) { DEFINE_SIMPLE_CONVERSION_FUNCTIONS(SMDiagnostic, LLVMSMDiagnosticRef) +#if LLVM_VERSION_LT(13, 0) +using LLVMInlineAsmDiagHandlerTy = LLVMContext::InlineAsmDiagHandlerTy; +#else +using LLVMInlineAsmDiagHandlerTy = void*; +#endif + extern "C" void LLVMRustSetInlineAsmDiagnosticHandler( - LLVMContextRef C, LLVMContext::InlineAsmDiagHandlerTy H, void *CX) { + LLVMContextRef C, LLVMInlineAsmDiagHandlerTy H, void *CX) { + // Diagnostic handlers were unified in LLVM change 5de2d189e6ad, so starting + // with LLVM 13 this function is gone. +#if LLVM_VERSION_LT(13, 0) unwrap(C)->setInlineAsmDiagnosticHandler(H, CX); +#endif } extern "C" bool LLVMRustUnpackSMDiagnostic(LLVMSMDiagnosticRef DRef, @@ -1441,47 +1447,28 @@ extern "C" LLVMValueRef LLVMRustBuildMemCpy(LLVMBuilderRef B, LLVMValueRef Dst, unsigned DstAlign, LLVMValueRef Src, unsigned SrcAlign, LLVMValueRef Size, bool IsVolatile) { -#if LLVM_VERSION_GE(10, 0) return wrap(unwrap(B)->CreateMemCpy( unwrap(Dst), MaybeAlign(DstAlign), unwrap(Src), MaybeAlign(SrcAlign), unwrap(Size), IsVolatile)); -#else - return wrap(unwrap(B)->CreateMemCpy( - unwrap(Dst), DstAlign, - unwrap(Src), SrcAlign, - unwrap(Size), IsVolatile)); -#endif } extern "C" LLVMValueRef LLVMRustBuildMemMove(LLVMBuilderRef B, LLVMValueRef Dst, unsigned DstAlign, LLVMValueRef Src, unsigned SrcAlign, LLVMValueRef Size, bool IsVolatile) { -#if LLVM_VERSION_GE(10, 0) return wrap(unwrap(B)->CreateMemMove( unwrap(Dst), MaybeAlign(DstAlign), unwrap(Src), MaybeAlign(SrcAlign), unwrap(Size), IsVolatile)); -#else - return wrap(unwrap(B)->CreateMemMove( - unwrap(Dst), DstAlign, - unwrap(Src), SrcAlign, - unwrap(Size), IsVolatile)); -#endif } extern "C" LLVMValueRef LLVMRustBuildMemSet(LLVMBuilderRef B, LLVMValueRef Dst, unsigned DstAlign, LLVMValueRef Val, LLVMValueRef Size, bool IsVolatile) { -#if LLVM_VERSION_GE(10, 0) return wrap(unwrap(B)->CreateMemSet( unwrap(Dst), unwrap(Val), unwrap(Size), MaybeAlign(DstAlign), IsVolatile)); -#else - return wrap(unwrap(B)->CreateMemSet( - unwrap(Dst), unwrap(Val), unwrap(Size), DstAlign, IsVolatile)); -#endif } extern "C" LLVMValueRef @@ -1661,17 +1648,17 @@ extern "C" void LLVMRustSetVisibility(LLVMValueRef V, LLVMSetVisibility(V, fromRust(RustVisibility)); } +extern "C" void LLVMRustSetDSOLocal(LLVMValueRef Global, bool is_dso_local) { + unwrap(Global)->setDSOLocal(is_dso_local); +} + struct LLVMRustModuleBuffer { std::string data; }; extern "C" LLVMRustModuleBuffer* LLVMRustModuleBufferCreate(LLVMModuleRef M) { -#if LLVM_VERSION_GE(10, 0) auto Ret = std::make_unique(); -#else - auto Ret = llvm::make_unique(); -#endif { raw_string_ostream OS(Ret->data); { diff --git a/compiler/rustc_llvm/src/lib.rs b/compiler/rustc_llvm/src/lib.rs index 592010d78cf87..555aefb192948 100644 --- a/compiler/rustc_llvm/src/lib.rs +++ b/compiler/rustc_llvm/src/lib.rs @@ -125,10 +125,7 @@ pub fn initialize_available_targets() { LLVMInitializeMSP430TargetInfo, LLVMInitializeMSP430Target, LLVMInitializeMSP430TargetMC, - LLVMInitializeMSP430AsmPrinter - ); - init_target!( - all(llvm_component = "msp430", llvm_has_msp430_asm_parser), + LLVMInitializeMSP430AsmPrinter, LLVMInitializeMSP430AsmParser ); init_target!( diff --git a/compiler/rustc_macros/src/hash_stable.rs b/compiler/rustc_macros/src/hash_stable.rs index c955c13778276..30569f20793fb 100644 --- a/compiler/rustc_macros/src/hash_stable.rs +++ b/compiler/rustc_macros/src/hash_stable.rs @@ -74,6 +74,7 @@ pub fn hash_stable_generic_derive(mut s: synstructure::Structure<'_>) -> proc_ma s.bound_impl( quote!(::rustc_data_structures::stable_hasher::HashStable<__CTX>), quote! { + #[inline] fn hash_stable( &self, __hcx: &mut __CTX, @@ -119,6 +120,7 @@ pub fn hash_stable_derive(mut s: synstructure::Structure<'_>) -> proc_macro2::To > ), quote! { + #[inline] fn hash_stable( &self, __hcx: &mut ::rustc_middle::ich::StableHashingContext<'__ctx>, diff --git a/compiler/rustc_macros/src/symbols/tests.rs b/compiler/rustc_macros/src/symbols/tests.rs index 82b4b876978f2..842d2a977189d 100644 --- a/compiler/rustc_macros/src/symbols/tests.rs +++ b/compiler/rustc_macros/src/symbols/tests.rs @@ -43,7 +43,7 @@ fn test_symbols_macro(input: TokenStream, expected_errors: &[&str]) { "Macro generated a different number of errors than expected" ); - for (found_error, &expected_error) in found_errors.iter().zip(expected_errors.iter()) { + for (found_error, &expected_error) in found_errors.iter().zip(expected_errors) { let found_error_str = format!("{}", found_error); assert_eq!(found_error_str, expected_error); } diff --git a/compiler/rustc_metadata/Cargo.toml b/compiler/rustc_metadata/Cargo.toml index 2aabc2c407b1a..29fa0b7006992 100644 --- a/compiler/rustc_metadata/Cargo.toml +++ b/compiler/rustc_metadata/Cargo.toml @@ -11,7 +11,6 @@ doctest = false libc = "0.2" snap = "1" tracing = "0.1" -memmap = "0.7" smallvec = { version = "1.6.1", features = ["union", "may_dangle"] } rustc_middle = { path = "../rustc_middle" } rustc_attr = { path = "../rustc_attr" } diff --git a/compiler/rustc_metadata/src/creader.rs b/compiler/rustc_metadata/src/creader.rs index 63c6f369eb685..26db3a5f39d7c 100644 --- a/compiler/rustc_metadata/src/creader.rs +++ b/compiler/rustc_metadata/src/creader.rs @@ -6,11 +6,11 @@ use crate::rmeta::{CrateDep, CrateMetadata, CrateNumMap, CrateRoot, MetadataBlob use rustc_ast::expand::allocator::AllocatorKind; use rustc_ast::{self as ast, *}; -use rustc_data_structures::fx::FxHashSet; +use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_data_structures::svh::Svh; use rustc_data_structures::sync::Lrc; use rustc_expand::base::SyntaxExtension; -use rustc_hir::def_id::{CrateNum, LocalDefId, LOCAL_CRATE}; +use rustc_hir::def_id::{CrateNum, LocalDefId, StableCrateId, LOCAL_CRATE}; use rustc_hir::definitions::Definitions; use rustc_index::vec::IndexVec; use rustc_middle::middle::cstore::{CrateDepKind, CrateSource, ExternCrate}; @@ -42,6 +42,13 @@ pub struct CStore { allocator_kind: Option, /// This crate has a `#[global_allocator]` item. has_global_allocator: bool, + + /// This map is used to verify we get no hash conflicts between + /// `StableCrateId` values. + stable_crate_ids: FxHashMap, + + /// Unused externs of the crate + unused_externs: Vec, } pub struct CrateLoader<'a> { @@ -186,6 +193,27 @@ impl CStore { crate fn has_global_allocator(&self) -> bool { self.has_global_allocator } + + pub fn report_unused_deps(&self, tcx: TyCtxt<'_>) { + // We put the check for the option before the lint_level_at_node call + // because the call mutates internal state and introducing it + // leads to some ui tests failing. + if !tcx.sess.opts.json_unused_externs { + return; + } + let level = tcx + .lint_level_at_node(lint::builtin::UNUSED_CRATE_DEPENDENCIES, rustc_hir::CRATE_HIR_ID) + .0; + if level != lint::Level::Allow { + let unused_externs = + self.unused_externs.iter().map(|ident| ident.to_ident_string()).collect::>(); + let unused_externs = unused_externs.iter().map(String::as_str).collect::>(); + tcx.sess + .parse_sess + .span_diagnostic + .emit_unused_externs(level.as_str(), &unused_externs); + } + } } impl<'a> CrateLoader<'a> { @@ -194,6 +222,11 @@ impl<'a> CrateLoader<'a> { metadata_loader: &'a MetadataLoaderDyn, local_crate_name: &str, ) -> Self { + let local_crate_stable_id = + StableCrateId::new(local_crate_name, sess.local_crate_disambiguator()); + let mut stable_crate_ids = FxHashMap::default(); + stable_crate_ids.insert(local_crate_stable_id, LOCAL_CRATE); + CrateLoader { sess, metadata_loader, @@ -207,6 +240,8 @@ impl<'a> CrateLoader<'a> { injected_panic_runtime: None, allocator_kind: None, has_global_allocator: false, + stable_crate_ids, + unused_externs: Vec::new(), }, used_extern_options: Default::default(), } @@ -313,6 +348,20 @@ impl<'a> CrateLoader<'a> { res } + fn verify_no_stable_crate_id_hash_conflicts( + &mut self, + root: &CrateRoot<'_>, + cnum: CrateNum, + ) -> Result<(), CrateError> { + if let Some(existing) = self.cstore.stable_crate_ids.insert(root.stable_crate_id(), cnum) { + let crate_name0 = root.name(); + let crate_name1 = self.cstore.get_crate_data(existing).name(); + return Err(CrateError::StableCrateIdCollision(crate_name0, crate_name1)); + } + + Ok(()) + } + fn register_crate( &mut self, host_lib: Option, @@ -326,7 +375,6 @@ impl<'a> CrateLoader<'a> { let Library { source, metadata } = lib; let crate_root = metadata.get_root(); let host_hash = host_lib.as_ref().map(|lib| lib.metadata.get_root().hash()); - self.verify_no_symbol_conflicts(&crate_root)?; let private_dep = self.sess.opts.externs.get(&name.as_str()).map_or(false, |e| e.is_private_dep); @@ -368,6 +416,14 @@ impl<'a> CrateLoader<'a> { None }; + // Perform some verification *after* resolve_crate_deps() above is + // known to have been successful. It seems that - in error cases - the + // cstore can be in a temporarily invalid state between cnum allocation + // and dependency resolution and the verification code would produce + // ICEs in that case (see #83045). + self.verify_no_symbol_conflicts(&crate_root)?; + self.verify_no_stable_crate_id_hash_conflicts(&crate_root, cnum)?; + let crate_metadata = CrateMetadata::new( self.sess, metadata, @@ -710,7 +766,7 @@ impl<'a> CrateLoader<'a> { } fn inject_profiler_runtime(&mut self, krate: &ast::Crate) { - if (self.sess.opts.debugging_opts.instrument_coverage + if (self.sess.instrument_coverage() || self.sess.opts.debugging_opts.profile || self.sess.opts.cg.profile_generate.enabled()) && !self.sess.opts.debugging_opts.no_profiler_runtime @@ -873,11 +929,17 @@ impl<'a> CrateLoader<'a> { // Don't worry about pathless `--extern foo` sysroot references continue; } - if self.used_extern_options.contains(&Symbol::intern(name)) { + let name_interned = Symbol::intern(name); + if self.used_extern_options.contains(&name_interned) { continue; } // Got a real unused --extern + if self.sess.opts.json_unused_externs { + self.cstore.unused_externs.push(name_interned); + continue; + } + let diag = match self.sess.opts.extern_dep_specs.get(name) { Some(loc) => BuiltinLintDiagnostics::ExternDepSpec(name.clone(), loc.into()), None => { @@ -910,9 +972,9 @@ impl<'a> CrateLoader<'a> { self.inject_allocator_crate(krate); self.inject_panic_runtime(krate); - info!("{:?}", CrateDump(&self.cstore)); - self.report_unused_deps(krate); + + info!("{:?}", CrateDump(&self.cstore)); } pub fn process_extern_crate( diff --git a/compiler/rustc_metadata/src/lib.rs b/compiler/rustc_metadata/src/lib.rs index 2560cfa74624a..c4d9e3f77f071 100644 --- a/compiler/rustc_metadata/src/lib.rs +++ b/compiler/rustc_metadata/src/lib.rs @@ -5,7 +5,7 @@ #![feature(in_band_lifetimes)] #![feature(nll)] #![feature(once_cell)] -#![feature(or_patterns)] +#![cfg_attr(bootstrap, feature(or_patterns))] #![feature(proc_macro_internals)] #![feature(min_specialization)] #![feature(stmt_expr_attributes)] @@ -26,7 +26,6 @@ pub use rmeta::{provide, provide_extern}; mod dependency_format; mod foreign_modules; -mod link_args; mod native_libs; mod rmeta; diff --git a/compiler/rustc_metadata/src/link_args.rs b/compiler/rustc_metadata/src/link_args.rs deleted file mode 100644 index d088288c50767..0000000000000 --- a/compiler/rustc_metadata/src/link_args.rs +++ /dev/null @@ -1,55 +0,0 @@ -use rustc_hir as hir; -use rustc_hir::itemlikevisit::ItemLikeVisitor; -use rustc_middle::ty::TyCtxt; -use rustc_span::symbol::{sym, Symbol}; -use rustc_target::spec::abi::Abi; - -crate fn collect(tcx: TyCtxt<'_>) -> Vec { - let mut collector = Collector { tcx, args: Vec::new() }; - tcx.hir().krate().visit_all_item_likes(&mut collector); - - for attr in tcx.hir().krate().item.attrs.iter() { - if attr.has_name(sym::link_args) { - if let Some(linkarg) = attr.value_str() { - collector.add_link_args(linkarg); - } - } - } - - collector.args -} - -struct Collector<'tcx> { - tcx: TyCtxt<'tcx>, - args: Vec, -} - -impl<'tcx> ItemLikeVisitor<'tcx> for Collector<'tcx> { - fn visit_item(&mut self, it: &'tcx hir::Item<'tcx>) { - let abi = match it.kind { - hir::ItemKind::ForeignMod { abi, .. } => abi, - _ => return, - }; - if abi == Abi::Rust || abi == Abi::RustIntrinsic || abi == Abi::PlatformIntrinsic { - return; - } - - // First, add all of the custom #[link_args] attributes - let sess = &self.tcx.sess; - for m in it.attrs.iter().filter(|a| sess.check_name(a, sym::link_args)) { - if let Some(linkarg) = m.value_str() { - self.add_link_args(linkarg); - } - } - } - - fn visit_trait_item(&mut self, _it: &'tcx hir::TraitItem<'tcx>) {} - fn visit_impl_item(&mut self, _it: &'tcx hir::ImplItem<'tcx>) {} - fn visit_foreign_item(&mut self, _it: &'tcx hir::ForeignItem<'tcx>) {} -} - -impl<'tcx> Collector<'tcx> { - fn add_link_args(&mut self, args: Symbol) { - self.args.extend(args.as_str().split(' ').filter(|s| !s.is_empty()).map(|s| s.to_string())) - } -} diff --git a/compiler/rustc_metadata/src/locator.rs b/compiler/rustc_metadata/src/locator.rs index b66c6cffb1b26..7f6311861c1b2 100644 --- a/compiler/rustc_metadata/src/locator.rs +++ b/compiler/rustc_metadata/src/locator.rs @@ -216,6 +216,7 @@ use crate::creader::Library; use crate::rmeta::{rustc_version, MetadataBlob, METADATA_HEADER}; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; +use rustc_data_structures::memmap::Mmap; use rustc_data_structures::owning_ref::OwningRef; use rustc_data_structures::svh::Svh; use rustc_data_structures::sync::MetadataRef; @@ -232,7 +233,6 @@ use rustc_target::spec::{Target, TargetTriple}; use snap::read::FrameDecoder; use std::io::{Read, Result as IoResult, Write}; -use std::ops::Deref; use std::path::{Path, PathBuf}; use std::{cmp, fmt, fs}; use tracing::{debug, info, warn}; @@ -727,19 +727,6 @@ impl<'a> CrateLocator<'a> { } } -/// A trivial wrapper for `Mmap` that implements `StableDeref`. -struct StableDerefMmap(memmap::Mmap); - -impl Deref for StableDerefMmap { - type Target = [u8]; - - fn deref(&self) -> &[u8] { - self.0.deref() - } -} - -unsafe impl stable_deref_trait::StableDeref for StableDerefMmap {} - fn get_metadata_section( target: &Target, flavor: CrateFlavor, @@ -779,11 +766,11 @@ fn get_metadata_section( // mmap the file, because only a small fraction of it is read. let file = std::fs::File::open(filename) .map_err(|_| format!("failed to open rmeta metadata: '{}'", filename.display()))?; - let mmap = unsafe { memmap::Mmap::map(&file) }; + let mmap = unsafe { Mmap::map(file) }; let mmap = mmap .map_err(|_| format!("failed to mmap rmeta metadata: '{}'", filename.display()))?; - rustc_erase_owner!(OwningRef::new(StableDerefMmap(mmap)).map_owner_box()) + rustc_erase_owner!(OwningRef::new(mmap).map_owner_box()) } }; let blob = MetadataBlob::new(raw_bytes); @@ -888,6 +875,7 @@ crate enum CrateError { MultipleMatchingCrates(Symbol, FxHashMap), SymbolConflictsCurrent(Symbol), SymbolConflictsOthers(Symbol), + StableCrateIdCollision(Symbol, Symbol), DlOpen(String), DlSym(String), LocatorCombined(CombinedLocatorError), @@ -970,6 +958,13 @@ impl CrateError { `-C metadata`. This will result in symbol conflicts between the two.", root_name, ), + CrateError::StableCrateIdCollision(crate_name0, crate_name1) => { + let msg = format!( + "found crates (`{}` and `{}`) with colliding StableCrateId values.", + crate_name0, crate_name1 + ); + sess.struct_span_err(span, &msg) + } CrateError::DlOpen(s) | CrateError::DlSym(s) => sess.struct_span_err(span, &s), CrateError::LocatorCombined(locator) => { let crate_name = locator.crate_name; diff --git a/compiler/rustc_metadata/src/native_libs.rs b/compiler/rustc_metadata/src/native_libs.rs index 4d63b6d074af0..523e016eeb9f2 100644 --- a/compiler/rustc_metadata/src/native_libs.rs +++ b/compiler/rustc_metadata/src/native_libs.rs @@ -44,7 +44,8 @@ impl ItemLikeVisitor<'tcx> for Collector<'tcx> { // Process all of the #[link(..)]-style arguments let sess = &self.tcx.sess; - for m in it.attrs.iter().filter(|a| sess.check_name(a, sym::link)) { + for m in self.tcx.hir().attrs(it.hir_id()).iter().filter(|a| sess.check_name(a, sym::link)) + { let items = match m.meta_item_list() { Some(item) => item, None => continue, diff --git a/compiler/rustc_metadata/src/rmeta/decoder.rs b/compiler/rustc_metadata/src/rmeta/decoder.rs index e3c3539079857..3d0a9d553b028 100644 --- a/compiler/rustc_metadata/src/rmeta/decoder.rs +++ b/compiler/rustc_metadata/src/rmeta/decoder.rs @@ -7,7 +7,6 @@ use crate::rmeta::*; use rustc_ast as ast; use rustc_attr as attr; use rustc_data_structures::captures::Captures; -use rustc_data_structures::fingerprint::{Fingerprint, FingerprintDecoder}; use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::svh::Svh; use rustc_data_structures::sync::{Lock, LockGuard, Lrc, OnceCell}; @@ -351,12 +350,6 @@ impl<'a, 'tcx> Decodable> for DefIndex { } } -impl<'a, 'tcx> FingerprintDecoder for DecodeContext<'a, 'tcx> { - fn decode_fingerprint(&mut self) -> Result { - Fingerprint::decode_opaque(&mut self.opaque) - } -} - impl<'a, 'tcx> Decodable> for SyntaxContext { fn decode(decoder: &mut DecodeContext<'a, 'tcx>) -> Result { let cdata = decoder.cdata(); @@ -635,6 +628,10 @@ impl CrateRoot<'_> { self.hash } + crate fn stable_crate_id(&self) -> StableCrateId { + self.stable_crate_id + } + crate fn triple(&self) -> &TargetTriple { &self.triple } @@ -956,6 +953,14 @@ impl<'a, 'tcx> CrateMetadataRef<'a> { self.root.tables.expn_that_defined.get(self, id).unwrap().decode((self, sess)) } + fn get_const_param_default( + &self, + tcx: TyCtxt<'tcx>, + id: DefIndex, + ) -> rustc_middle::ty::Const<'tcx> { + self.root.tables.const_defaults.get(self, id).unwrap().decode((self, tcx)) + } + /// Iterates over all the stability attributes in the given crate. fn get_lib_features(&self, tcx: TyCtxt<'tcx>) -> &'tcx [(Symbol, Option)] { // FIXME: For a proc macro crate, not sure whether we should return the "host" diff --git a/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs b/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs index 0f860d11dc21a..bebee9dac3b73 100644 --- a/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs +++ b/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs @@ -1,6 +1,5 @@ use crate::creader::{CStore, LoadedMacro}; use crate::foreign_modules; -use crate::link_args; use crate::native_libs; use crate::rmeta::{self, encoder}; @@ -122,6 +121,7 @@ provide! { <'tcx> tcx, def_id, other, cdata, promoted_mir => { tcx.arena.alloc(cdata.get_promoted_mir(tcx, def_id.index)) } mir_abstract_const => { cdata.get_mir_abstract_const(tcx, def_id.index) } unused_generic_params => { cdata.get_unused_generic_params(def_id.index) } + const_param_default => { tcx.mk_const(cdata.get_const_param_default(tcx, def_id.index)) } mir_const_qualif => { cdata.mir_const_qualif(def_id.index) } fn_sig => { cdata.fn_sig(def_id.index, tcx) } inherent_impls => { cdata.get_inherent_implementations_for_type(tcx, def_id.index) } @@ -294,10 +294,6 @@ pub fn provide(providers: &mut Providers) { foreign_modules::collect(tcx).into_iter().map(|m| (m.def_id, m)).collect(); Lrc::new(modules) }, - link_args: |tcx, cnum| { - assert_eq!(cnum, LOCAL_CRATE); - Lrc::new(link_args::collect(tcx)) - }, // Returns a map from a sufficiently visible external item (i.e., an // external item that is visible from at least one local module) to a diff --git a/compiler/rustc_metadata/src/rmeta/encoder.rs b/compiler/rustc_metadata/src/rmeta/encoder.rs index 61265d7204c0b..a5157854e15c0 100644 --- a/compiler/rustc_metadata/src/rmeta/encoder.rs +++ b/compiler/rustc_metadata/src/rmeta/encoder.rs @@ -1,7 +1,6 @@ use crate::rmeta::table::{FixedSizeEncoding, TableBuilder}; use crate::rmeta::*; -use rustc_data_structures::fingerprint::{Fingerprint, FingerprintEncoder}; use rustc_data_structures::fx::{FxHashMap, FxIndexSet}; use rustc_data_structures::stable_hasher::StableHasher; use rustc_data_structures::sync::{join, par_iter, Lrc, ParallelIterator}; @@ -116,6 +115,7 @@ impl<'a, 'tcx> Encoder for EncodeContext<'a, 'tcx> { emit_f32(f32); emit_char(char); emit_str(&str); + emit_raw_bytes(&[u8]); } } @@ -307,12 +307,6 @@ impl<'a, 'tcx> Encodable> for Span { } } -impl<'a, 'tcx> FingerprintEncoder for EncodeContext<'a, 'tcx> { - fn encode_fingerprint(&mut self, f: &Fingerprint) -> Result<(), Self::Error> { - self.opaque.encode_fingerprint(f) - } -} - impl<'a, 'tcx> TyEncoder<'tcx> for EncodeContext<'a, 'tcx> { const CLEAR_CROSS_CRATE: bool = true; @@ -433,7 +427,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { fn encode_info_for_items(&mut self) { let krate = self.tcx.hir().krate(); - self.encode_info_for_mod(CRATE_DEF_ID, &krate.item.module); + self.encode_info_for_mod(CRATE_DEF_ID, &krate.item); // Proc-macro crates only export proc-macro items, which are looked // up using `proc_macro_data` @@ -653,6 +647,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { triple: tcx.sess.opts.target_triple.clone(), hash: tcx.crate_hash(LOCAL_CRATE), disambiguator: tcx.sess.local_crate_disambiguator(), + stable_crate_id: tcx.def_path_hash(LOCAL_CRATE.as_def_id()).stable_crate_id(), panic_strategy: tcx.sess.panic_strategy(), edition: tcx.sess.edition(), has_global_allocator: tcx.has_global_allocator(LOCAL_CRATE), @@ -835,6 +830,76 @@ fn should_encode_mir(tcx: TyCtxt<'_>, def_id: LocalDefId) -> (bool, bool) { } } +fn should_encode_variances(def_kind: DefKind) -> bool { + match def_kind { + DefKind::Struct + | DefKind::Union + | DefKind::Enum + | DefKind::Variant + | DefKind::Fn + | DefKind::Ctor(..) + | DefKind::AssocFn => true, + DefKind::Mod + | DefKind::Field + | DefKind::AssocTy + | DefKind::AssocConst + | DefKind::TyParam + | DefKind::ConstParam + | DefKind::Static + | DefKind::Const + | DefKind::ForeignMod + | DefKind::TyAlias + | DefKind::OpaqueTy + | DefKind::Impl + | DefKind::Trait + | DefKind::TraitAlias + | DefKind::Macro(..) + | DefKind::ForeignTy + | DefKind::Use + | DefKind::LifetimeParam + | DefKind::AnonConst + | DefKind::GlobalAsm + | DefKind::Closure + | DefKind::Generator + | DefKind::ExternCrate => false, + } +} + +fn should_encode_generics(def_kind: DefKind) -> bool { + match def_kind { + DefKind::Struct + | DefKind::Union + | DefKind::Enum + | DefKind::Variant + | DefKind::Trait + | DefKind::TyAlias + | DefKind::ForeignTy + | DefKind::TraitAlias + | DefKind::AssocTy + | DefKind::Fn + | DefKind::Const + | DefKind::Static + | DefKind::Ctor(..) + | DefKind::AssocFn + | DefKind::AssocConst + | DefKind::AnonConst + | DefKind::OpaqueTy + | DefKind::Impl + | DefKind::Closure + | DefKind::Generator => true, + DefKind::Mod + | DefKind::Field + | DefKind::ForeignMod + | DefKind::TyParam + | DefKind::ConstParam + | DefKind::Macro(..) + | DefKind::Use + | DefKind::LifetimeParam + | DefKind::GlobalAsm + | DefKind::ExternCrate => false, + } +} + impl EncodeContext<'a, 'tcx> { fn encode_def_ids(&mut self) { if self.is_proc_macro { @@ -863,12 +928,34 @@ impl EncodeContext<'a, 'tcx> { self.encode_const_stability(def_id); self.encode_deprecation(def_id); } + if should_encode_variances(def_kind) { + let v = self.tcx.variances_of(def_id); + record!(self.tables.variances[def_id] <- v); + } + if should_encode_generics(def_kind) { + let g = tcx.generics_of(def_id); + record!(self.tables.generics[def_id] <- g); + record!(self.tables.explicit_predicates[def_id] <- self.tcx.explicit_predicates_of(def_id)); + let inferred_outlives = self.tcx.inferred_outlives_of(def_id); + if !inferred_outlives.is_empty() { + record!(self.tables.inferred_outlives[def_id] <- inferred_outlives); + } + } + if let DefKind::Trait | DefKind::TraitAlias = def_kind { + record!(self.tables.super_predicates[def_id] <- self.tcx.super_predicates_of(def_id)); + } + } + let inherent_impls = tcx.crate_inherent_impls(LOCAL_CRATE); + for (def_id, implementations) in inherent_impls.inherent_impls.iter() { + assert!(def_id.is_local()); + if implementations.is_empty() { + continue; + } + record!(self.tables.inherent_impls[def_id] <- implementations.iter().map(|&def_id| { + assert!(def_id.is_local()); + def_id.index + })); } - } - - fn encode_variances_of(&mut self, def_id: DefId) { - debug!("EncodeContext::encode_variances_of({:?})", def_id); - record!(self.tables.variances[def_id] <- self.tcx.variances_of(def_id)); } fn encode_item_type(&mut self, def_id: DefId) { @@ -901,12 +988,7 @@ impl EncodeContext<'a, 'tcx> { if let Some(ctor_def_id) = variant.ctor_def_id { record!(self.tables.fn_sig[def_id] <- tcx.fn_sig(ctor_def_id)); } - // FIXME(eddyb) is this ever used? - self.encode_variances_of(def_id); } - self.encode_generics(def_id); - self.encode_explicit_predicates(def_id); - self.encode_inferred_outlives(def_id); } fn encode_enum_variant_ctor(&mut self, def: &ty::AdtDef, index: VariantIdx) { @@ -927,11 +1009,7 @@ impl EncodeContext<'a, 'tcx> { self.encode_item_type(def_id); if variant.ctor_kind == CtorKind::Fn { record!(self.tables.fn_sig[def_id] <- tcx.fn_sig(def_id)); - self.encode_variances_of(def_id); } - self.encode_generics(def_id); - self.encode_explicit_predicates(def_id); - self.encode_inferred_outlives(def_id); } fn encode_info_for_mod(&mut self, local_def_id: LocalDefId, md: &hir::Mod<'_>) { @@ -990,9 +1068,6 @@ impl EncodeContext<'a, 'tcx> { record!(self.tables.kind[def_id] <- EntryKind::Field); self.encode_ident_span(def_id, field.ident); self.encode_item_type(def_id); - self.encode_generics(def_id); - self.encode_explicit_predicates(def_id); - self.encode_inferred_outlives(def_id); } fn encode_struct_ctor(&mut self, adt_def: &ty::AdtDef, def_id: DefId) { @@ -1011,35 +1086,7 @@ impl EncodeContext<'a, 'tcx> { self.encode_item_type(def_id); if variant.ctor_kind == CtorKind::Fn { record!(self.tables.fn_sig[def_id] <- tcx.fn_sig(def_id)); - self.encode_variances_of(def_id); } - self.encode_generics(def_id); - self.encode_explicit_predicates(def_id); - self.encode_inferred_outlives(def_id); - } - - fn encode_generics(&mut self, def_id: DefId) { - debug!("EncodeContext::encode_generics({:?})", def_id); - record!(self.tables.generics[def_id] <- self.tcx.generics_of(def_id)); - } - - fn encode_explicit_predicates(&mut self, def_id: DefId) { - debug!("EncodeContext::encode_explicit_predicates({:?})", def_id); - record!(self.tables.explicit_predicates[def_id] <- - self.tcx.explicit_predicates_of(def_id)); - } - - fn encode_inferred_outlives(&mut self, def_id: DefId) { - debug!("EncodeContext::encode_inferred_outlives({:?})", def_id); - let inferred_outlives = self.tcx.inferred_outlives_of(def_id); - if !inferred_outlives.is_empty() { - record!(self.tables.inferred_outlives[def_id] <- inferred_outlives); - } - } - - fn encode_super_predicates(&mut self, def_id: DefId) { - debug!("EncodeContext::encode_super_predicates({:?})", def_id); - record!(self.tables.super_predicates[def_id] <- self.tcx.super_predicates_of(def_id)); } fn encode_explicit_item_bounds(&mut self, def_id: DefId) { @@ -1116,11 +1163,7 @@ impl EncodeContext<'a, 'tcx> { } if trait_item.kind == ty::AssocKind::Fn { record!(self.tables.fn_sig[def_id] <- tcx.fn_sig(def_id)); - self.encode_variances_of(def_id); } - self.encode_generics(def_id); - self.encode_explicit_predicates(def_id); - self.encode_inferred_outlives(def_id); } fn encode_info_for_impl_item(&mut self, def_id: DefId) { @@ -1177,11 +1220,7 @@ impl EncodeContext<'a, 'tcx> { self.encode_item_type(def_id); if impl_item.kind == ty::AssocKind::Fn { record!(self.tables.fn_sig[def_id] <- tcx.fn_sig(def_id)); - self.encode_variances_of(def_id); } - self.encode_generics(def_id); - self.encode_explicit_predicates(def_id); - self.encode_inferred_outlives(def_id); } fn encode_fn_param_names_for_body(&mut self, body_id: hir::BodyId) -> Lazy<[Ident]> { @@ -1236,18 +1275,6 @@ impl EncodeContext<'a, 'tcx> { } } - // Encodes the inherent implementations of a structure, enumeration, or trait. - fn encode_inherent_implementations(&mut self, def_id: DefId) { - debug!("EncodeContext::encode_inherent_implementations({:?})", def_id); - let implementations = self.tcx.inherent_impls(def_id); - if !implementations.is_empty() { - record!(self.tables.inherent_impls[def_id] <- implementations.iter().map(|&def_id| { - assert!(def_id.is_local()); - def_id.index - })); - } - } - fn encode_stability(&mut self, def_id: DefId) { debug!("EncodeContext::encode_stability({:?})", def_id); @@ -1458,38 +1485,6 @@ impl EncodeContext<'a, 'tcx> { record!(self.tables.impl_trait_ref[def_id] <- trait_ref); } } - self.encode_inherent_implementations(def_id); - match item.kind { - hir::ItemKind::Enum(..) - | hir::ItemKind::Struct(..) - | hir::ItemKind::Union(..) - | hir::ItemKind::Fn(..) => self.encode_variances_of(def_id), - _ => {} - } - match item.kind { - hir::ItemKind::Static(..) - | hir::ItemKind::Const(..) - | hir::ItemKind::Fn(..) - | hir::ItemKind::TyAlias(..) - | hir::ItemKind::Enum(..) - | hir::ItemKind::Struct(..) - | hir::ItemKind::Union(..) - | hir::ItemKind::Impl { .. } - | hir::ItemKind::OpaqueTy(..) - | hir::ItemKind::Trait(..) - | hir::ItemKind::TraitAlias(..) => { - self.encode_generics(def_id); - self.encode_explicit_predicates(def_id); - self.encode_inferred_outlives(def_id); - } - _ => {} - } - match item.kind { - hir::ItemKind::Trait(..) | hir::ItemKind::TraitAlias(..) => { - self.encode_super_predicates(def_id); - } - _ => {} - } } /// Serialize the text of exported macros @@ -1530,7 +1525,6 @@ impl EncodeContext<'a, 'tcx> { if let ty::Closure(def_id, substs) = *ty.kind() { record!(self.tables.fn_sig[def_id] <- substs.as_closure().sig()); } - self.encode_generics(def_id.to_def_id()); } fn encode_info_for_anon_const(&mut self, def_id: LocalDefId) { @@ -1542,9 +1536,6 @@ impl EncodeContext<'a, 'tcx> { record!(self.tables.kind[def_id.to_def_id()] <- EntryKind::AnonConst(qualifs, const_data)); self.encode_item_type(def_id.to_def_id()); - self.encode_generics(def_id.to_def_id()); - self.encode_explicit_predicates(def_id.to_def_id()); - self.encode_inferred_outlives(def_id.to_def_id()); } fn encode_native_libraries(&mut self) -> Lazy<[NativeLib]> { @@ -1821,14 +1812,9 @@ impl EncodeContext<'a, 'tcx> { } self.encode_ident_span(def_id, nitem.ident); self.encode_item_type(def_id); - self.encode_inherent_implementations(def_id); if let hir::ForeignItemKind::Fn(..) = nitem.kind { record!(self.tables.fn_sig[def_id] <- tcx.fn_sig(def_id)); - self.encode_variances_of(def_id); } - self.encode_generics(def_id); - self.encode_explicit_predicates(def_id); - self.encode_inferred_outlives(def_id); } } @@ -1890,13 +1876,12 @@ impl EncodeContext<'a, 'tcx> { default.is_some(), ); } - GenericParamKind::Const { .. } => { - self.encode_info_for_generic_param( - def_id.to_def_id(), - EntryKind::ConstParam, - true, - ); - // FIXME(const_generics_defaults) + GenericParamKind::Const { ref default, .. } => { + let def_id = def_id.to_def_id(); + self.encode_info_for_generic_param(def_id, EntryKind::ConstParam, true); + if default.is_some() { + record!(self.tables.const_defaults[def_id] <- self.tcx.const_param_default(def_id)) + } } } } @@ -2072,10 +2057,10 @@ pub(super) fn encode_metadata(tcx: TyCtxt<'_>) -> EncodedMetadata { fn encode_metadata_impl(tcx: TyCtxt<'_>) -> EncodedMetadata { let mut encoder = opaque::Encoder::new(vec![]); - encoder.emit_raw_bytes(METADATA_HEADER); + encoder.emit_raw_bytes(METADATA_HEADER).unwrap(); // Will be filled with the root position after encoding everything. - encoder.emit_raw_bytes(&[0, 0, 0, 0]); + encoder.emit_raw_bytes(&[0, 0, 0, 0]).unwrap(); let source_map_files = tcx.sess.source_map().files(); let source_file_cache = (source_map_files[0].clone(), 0); diff --git a/compiler/rustc_metadata/src/rmeta/mod.rs b/compiler/rustc_metadata/src/rmeta/mod.rs index b44c3bfcac647..7cfb051e703c5 100644 --- a/compiler/rustc_metadata/src/rmeta/mod.rs +++ b/compiler/rustc_metadata/src/rmeta/mod.rs @@ -7,7 +7,7 @@ use rustc_data_structures::svh::Svh; use rustc_data_structures::sync::MetadataRef; use rustc_hir as hir; use rustc_hir::def::{CtorKind, DefKind}; -use rustc_hir::def_id::{DefId, DefIndex, DefPathHash}; +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}; @@ -203,6 +203,7 @@ crate struct CrateRoot<'tcx> { extra_filename: String, hash: Svh, disambiguator: CrateDisambiguator, + stable_crate_id: StableCrateId, panic_strategy: PanicStrategy, edition: Edition, has_global_allocator: bool, @@ -306,13 +307,14 @@ define_tables! { mir_for_ctfe: Table)>, promoted_mir: Table>)>, mir_abstract_consts: Table])>, + const_defaults: Table>>, unused_generic_params: Table>>, // `def_keys` and `def_path_hashes` represent a lazy version of a // `DefPathTable`. This allows us to avoid deserializing an entire // `DefPathTable` up front, since we may only ever use a few // definitions from any given crate. def_keys: Table>, - def_path_hashes: Table> + def_path_hashes: Table>, } #[derive(Copy, Clone, MetadataEncodable, MetadataDecodable)] diff --git a/compiler/rustc_metadata/src/rmeta/table.rs b/compiler/rustc_metadata/src/rmeta/table.rs index 03bd4170ea990..62c0ce1584594 100644 --- a/compiler/rustc_metadata/src/rmeta/table.rs +++ b/compiler/rustc_metadata/src/rmeta/table.rs @@ -2,6 +2,7 @@ use crate::rmeta::*; use rustc_index::vec::Idx; use rustc_serialize::opaque::Encoder; +use rustc_serialize::Encoder as _; use std::convert::TryInto; use std::marker::PhantomData; use std::num::NonZeroUsize; @@ -172,7 +173,7 @@ where pub(crate) fn encode(&self, buf: &mut Encoder) -> Lazy> { let pos = buf.position(); - buf.emit_raw_bytes(&self.bytes); + buf.emit_raw_bytes(&self.bytes).unwrap(); Lazy::from_position_and_meta(NonZeroUsize::new(pos as usize).unwrap(), self.bytes.len()) } } diff --git a/compiler/rustc_middle/Cargo.toml b/compiler/rustc_middle/Cargo.toml index 0674233165501..8cb30e72f7921 100644 --- a/compiler/rustc_middle/Cargo.toml +++ b/compiler/rustc_middle/Cargo.toml @@ -11,7 +11,7 @@ doctest = false rustc_arena = { path = "../rustc_arena" } bitflags = "1.2.1" tracing = "0.1" -rustc-rayon-core = "0.3.0" +rustc-rayon-core = "0.3.1" polonius-engine = "0.12.0" rustc_apfloat = { path = "../rustc_apfloat" } rustc_attr = { path = "../rustc_attr" } diff --git a/compiler/rustc_middle/src/dep_graph/dep_node.rs b/compiler/rustc_middle/src/dep_graph/dep_node.rs index ba9d0a40732e6..aa54d1ae7b9d1 100644 --- a/compiler/rustc_middle/src/dep_graph/dep_node.rs +++ b/compiler/rustc_middle/src/dep_graph/dep_node.rs @@ -32,8 +32,8 @@ //! `DepNode` definition happens in the `define_dep_nodes!()` macro. This macro //! defines the `DepKind` enum. Each `DepKind` has its own parameters that are //! needed at runtime in order to construct a valid `DepNode` fingerprint. -//! However, only `CompileCodegenUnit` is constructed explicitly (with -//! `make_compile_codegen_unit`). +//! However, only `CompileCodegenUnit` and `CompileMonoItem` are constructed +//! explicitly (with `make_compile_codegen_unit` cq `make_compile_mono_item`). //! //! Because the macro sees what parameters a given `DepKind` requires, it can //! "infer" some properties for each kind of `DepNode`: @@ -46,15 +46,17 @@ //! `DefId` it was computed from. In other cases, too much information gets //! lost during fingerprint computation. //! -//! `make_compile_codegen_unit`, together with `DepNode::new()`, ensures that only -//! valid `DepNode` instances can be constructed. For example, the API does not -//! allow for constructing parameterless `DepNode`s with anything other -//! than a zeroed out fingerprint. More generally speaking, it relieves the -//! user of the `DepNode` API of having to know how to compute the expected -//! fingerprint for a given set of node parameters. +//! `make_compile_codegen_unit` and `make_compile_mono_items`, together with +//! `DepNode::new()`, ensures that only valid `DepNode` instances can be +//! constructed. For example, the API does not allow for constructing +//! parameterless `DepNode`s with anything other than a zeroed out fingerprint. +//! More generally speaking, it relieves the user of the `DepNode` API of +//! having to know how to compute the expected fingerprint for a given set of +//! node parameters. //! //! [dependency graph]: https://rustc-dev-guide.rust-lang.org/query.html +use crate::mir::mono::MonoItem; use crate::ty::TyCtxt; use rustc_data_structures::fingerprint::Fingerprint; @@ -175,6 +177,14 @@ pub mod dep_kind { can_reconstruct_query_key: || false, }; + pub const CompileMonoItem: DepKindStruct = DepKindStruct { + has_params: true, + is_anon: false, + is_eval_always: false, + + can_reconstruct_query_key: || false, + }; + macro_rules! define_query_dep_kinds { ($( [$($attrs:tt)*] @@ -251,6 +261,10 @@ rustc_dep_node_append!([define_dep_nodes!][ <'tcx> // WARNING: if `Symbol` is changed, make sure you update `make_compile_codegen_unit` below. [] CompileCodegenUnit(Symbol), + + // WARNING: if `MonoItem` is changed, make sure you update `make_compile_mono_item` below. + // Only used by rustc_codegen_cranelift + [] CompileMonoItem(MonoItem), ]); // WARNING: `construct` is generic and does not know that `CompileCodegenUnit` takes `Symbol`s as keys. @@ -259,6 +273,12 @@ crate fn make_compile_codegen_unit(tcx: TyCtxt<'_>, name: Symbol) -> DepNode { DepNode::construct(tcx, DepKind::CompileCodegenUnit, &name) } +// WARNING: `construct` is generic and does not know that `CompileMonoItem` takes `MonoItem`s as keys. +// Be very careful changing this type signature! +crate fn make_compile_mono_item(tcx: TyCtxt<'tcx>, mono_item: &MonoItem<'tcx>) -> DepNode { + DepNode::construct(tcx, DepKind::CompileMonoItem, mono_item) +} + pub type DepNode = rustc_query_system::dep_graph::DepNode; // We keep a lot of `DepNode`s in memory during compilation. It's not diff --git a/compiler/rustc_middle/src/dep_graph/mod.rs b/compiler/rustc_middle/src/dep_graph/mod.rs index c688b23be1d02..31bea8329587d 100644 --- a/compiler/rustc_middle/src/dep_graph/mod.rs +++ b/compiler/rustc_middle/src/dep_graph/mod.rs @@ -8,18 +8,19 @@ use rustc_session::Session; mod dep_node; pub use rustc_query_system::dep_graph::{ - debug, hash_result, DepContext, DepNodeColor, DepNodeIndex, SerializedDepNodeIndex, - WorkProduct, WorkProductId, + debug::DepNodeFilter, hash_result, DepContext, DepNodeColor, DepNodeIndex, + SerializedDepNodeIndex, WorkProduct, WorkProductId, }; -crate use dep_node::make_compile_codegen_unit; pub use dep_node::{label_strs, DepKind, DepNode, DepNodeExt}; +crate use dep_node::{make_compile_codegen_unit, make_compile_mono_item}; pub type DepGraph = rustc_query_system::dep_graph::DepGraph; pub type TaskDeps = rustc_query_system::dep_graph::TaskDeps; pub type DepGraphQuery = rustc_query_system::dep_graph::DepGraphQuery; pub type PreviousDepGraph = rustc_query_system::dep_graph::PreviousDepGraph; pub type SerializedDepGraph = rustc_query_system::dep_graph::SerializedDepGraph; +pub type EdgeFilter = rustc_query_system::dep_graph::debug::EdgeFilter; impl rustc_query_system::dep_graph::DepKind for DepKind { const NULL: Self = DepKind::Null; diff --git a/compiler/rustc_middle/src/hir/map/blocks.rs b/compiler/rustc_middle/src/hir/map/blocks.rs index 9222ce1015eba..706c79009496b 100644 --- a/compiler/rustc_middle/src/hir/map/blocks.rs +++ b/compiler/rustc_middle/src/hir/map/blocks.rs @@ -12,7 +12,6 @@ //! for the `Code` associated with a particular NodeId. use crate::hir::map::Map; -use rustc_ast::Attribute; use rustc_hir as hir; use rustc_hir::intravisit::FnKind; use rustc_hir::{Expr, FnDecl, Node}; @@ -105,7 +104,6 @@ struct ItemFnParts<'a> { body: hir::BodyId, id: hir::HirId, span: Span, - attrs: &'a [Attribute], } /// These are all the components one can extract from a closure expr @@ -115,18 +113,11 @@ struct ClosureParts<'a> { body: hir::BodyId, id: hir::HirId, span: Span, - attrs: &'a [Attribute], } impl<'a> ClosureParts<'a> { - fn new( - d: &'a FnDecl<'a>, - b: hir::BodyId, - id: hir::HirId, - s: Span, - attrs: &'a [Attribute], - ) -> Self { - ClosureParts { decl: d, body: b, id, span: s, attrs } + fn new(d: &'a FnDecl<'a>, b: hir::BodyId, id: hir::HirId, s: Span) -> Self { + ClosureParts { decl: d, body: b, id, span: s } } } @@ -146,7 +137,7 @@ impl<'a> FnLikeNode<'a> { pub fn body(self) -> hir::BodyId { self.handle( |i: ItemFnParts<'a>| i.body, - |_, _, _: &'a hir::FnSig<'a>, _, body: hir::BodyId, _, _| body, + |_, _, _: &'a hir::FnSig<'a>, _, body: hir::BodyId, _| body, |c: ClosureParts<'a>| c.body, ) } @@ -154,7 +145,7 @@ impl<'a> FnLikeNode<'a> { pub fn decl(self) -> &'a FnDecl<'a> { self.handle( |i: ItemFnParts<'a>| &*i.decl, - |_, _, sig: &'a hir::FnSig<'a>, _, _, _, _| &sig.decl, + |_, _, sig: &'a hir::FnSig<'a>, _, _, _| &sig.decl, |c: ClosureParts<'a>| c.decl, ) } @@ -162,7 +153,7 @@ impl<'a> FnLikeNode<'a> { pub fn span(self) -> Span { self.handle( |i: ItemFnParts<'_>| i.span, - |_, _, _: &'a hir::FnSig<'a>, _, _, span, _| span, + |_, _, _: &'a hir::FnSig<'a>, _, _, span| span, |c: ClosureParts<'_>| c.span, ) } @@ -170,7 +161,7 @@ impl<'a> FnLikeNode<'a> { pub fn id(self) -> hir::HirId { self.handle( |i: ItemFnParts<'_>| i.id, - |id, _, _: &'a hir::FnSig<'a>, _, _, _, _| id, + |id, _, _: &'a hir::FnSig<'a>, _, _, _| id, |c: ClosureParts<'_>| c.id, ) } @@ -189,12 +180,11 @@ impl<'a> FnLikeNode<'a> { pub fn kind(self) -> FnKind<'a> { let item = |p: ItemFnParts<'a>| -> FnKind<'a> { - FnKind::ItemFn(p.ident, p.generics, p.header, p.vis, p.attrs) - }; - let closure = |c: ClosureParts<'a>| FnKind::Closure(c.attrs); - let method = |_, ident: Ident, sig: &'a hir::FnSig<'a>, vis, _, _, attrs| { - FnKind::Method(ident, sig, vis, attrs) + FnKind::ItemFn(p.ident, p.generics, p.header, p.vis) }; + let closure = |_: ClosureParts<'a>| FnKind::Closure; + let method = + |_, ident: Ident, sig: &'a hir::FnSig<'a>, vis, _, _| FnKind::Method(ident, sig, vis); self.handle(item, method, closure) } @@ -208,7 +198,6 @@ impl<'a> FnLikeNode<'a> { Option<&'a hir::Visibility<'a>>, hir::BodyId, Span, - &'a [Attribute], ) -> A, C: FnOnce(ClosureParts<'a>) -> A, { @@ -221,7 +210,6 @@ impl<'a> FnLikeNode<'a> { body: block, vis: &i.vis, span: i.span, - attrs: &i.attrs, header: sig.header, generics, }), @@ -229,19 +217,19 @@ impl<'a> FnLikeNode<'a> { }, Node::TraitItem(ti) => match ti.kind { hir::TraitItemKind::Fn(ref sig, hir::TraitFn::Provided(body)) => { - method(ti.hir_id(), ti.ident, sig, None, body, ti.span, &ti.attrs) + method(ti.hir_id(), ti.ident, sig, None, body, ti.span) } _ => bug!("trait method FnLikeNode that is not fn-like"), }, Node::ImplItem(ii) => match ii.kind { hir::ImplItemKind::Fn(ref sig, body) => { - method(ii.hir_id(), ii.ident, sig, Some(&ii.vis), body, ii.span, &ii.attrs) + method(ii.hir_id(), ii.ident, sig, Some(&ii.vis), body, ii.span) } _ => bug!("impl method FnLikeNode that is not fn-like"), }, Node::Expr(e) => match e.kind { hir::ExprKind::Closure(_, ref decl, block, _fn_decl_span, _gen) => { - closure(ClosureParts::new(&decl, block, e.hir_id, e.span, &e.attrs)) + closure(ClosureParts::new(&decl, block, e.hir_id, e.span)) } _ => bug!("expr FnLikeNode that is not fn-like"), }, diff --git a/compiler/rustc_middle/src/hir/map/collector.rs b/compiler/rustc_middle/src/hir/map/collector.rs index a5ffa9c7a54b0..501e7d624d2e4 100644 --- a/compiler/rustc_middle/src/hir/map/collector.rs +++ b/compiler/rustc_middle/src/hir/map/collector.rs @@ -52,6 +52,7 @@ fn insert_vec_map(map: &mut IndexVec>, k: K, v: V if i >= len { map.extend(repeat(None).take(i - len + 1)); } + debug_assert!(map[k].is_none()); map[k] = Some(v); } @@ -116,6 +117,7 @@ impl<'a, 'hir> NodeCollector<'a, 'hir> { modules: _, proc_macros: _, trait_map: _, + attrs: _, } = *krate; hash_body(&mut hcx, root_mod_def_path_hash, item, &mut hir_body_nodes) @@ -215,11 +217,21 @@ impl<'a, 'hir> NodeCollector<'a, 'hir> { // Overwrite the dummy hash with the real HIR owner hash. nodes.hash = hash; - // FIXME: feature(impl_trait_in_bindings) broken and trigger this assert - //assert!(data.signature.is_none()); - + debug_assert!(data.signature.is_none()); data.signature = Some(self.arena.alloc(Owner { parent: entry.parent, node: entry.node })); + + let dk_parent = self.definitions.def_key(id.owner).parent; + if let Some(dk_parent) = dk_parent { + let dk_parent = LocalDefId { local_def_index: dk_parent }; + let dk_parent = self.definitions.local_def_id_to_hir_id(dk_parent); + if dk_parent.owner != entry.parent.owner { + panic!( + "Different parents for {:?} => dk_parent={:?} actual={:?}", + id.owner, dk_parent, entry.parent, + ) + } + } } else { assert_eq!(entry.parent.owner, id.owner); insert_vec_map( @@ -361,26 +373,12 @@ impl<'a, 'hir> Visitor<'hir> for NodeCollector<'a, 'hir> { } fn visit_generic_param(&mut self, param: &'hir GenericParam<'hir>) { - if let hir::GenericParamKind::Type { - synthetic: Some(hir::SyntheticTyParamKind::ImplTrait), - .. - } = param.kind - { - debug_assert_eq!( - param.hir_id.owner, - self.definitions.opt_hir_id_to_local_def_id(param.hir_id).unwrap() - ); - self.with_dep_node_owner(param.hir_id.owner, param, |this, hash| { - this.insert_with_hash(param.span, param.hir_id, Node::GenericParam(param), hash); + self.insert(param.span, param.hir_id, Node::GenericParam(param)); + intravisit::walk_generic_param(self, param); + } - this.with_parent(param.hir_id, |this| { - intravisit::walk_generic_param(this, param); - }); - }); - } else { - self.insert(param.span, param.hir_id, Node::GenericParam(param)); - intravisit::walk_generic_param(self, param); - } + fn visit_const_param_default(&mut self, param: HirId, ct: &'hir AnonConst) { + self.with_parent(param, |this| intravisit::walk_const_param_default(this, ct)) } fn visit_trait_item(&mut self, ti: &'hir TraitItem<'hir>) { @@ -541,10 +539,10 @@ impl<'a, 'hir> Visitor<'hir> for NodeCollector<'a, 'hir> { }); } - fn visit_struct_field(&mut self, field: &'hir StructField<'hir>) { + fn visit_field_def(&mut self, field: &'hir FieldDef<'hir>) { self.insert(field.span, field.hir_id, Node::Field(field)); self.with_parent(field.hir_id, |this| { - intravisit::walk_struct_field(this, field); + intravisit::walk_field_def(this, field); }); } diff --git a/compiler/rustc_middle/src/hir/map/mod.rs b/compiler/rustc_middle/src/hir/map/mod.rs index 5c2bd575e7d55..d155276051e4a 100644 --- a/compiler/rustc_middle/src/hir/map/mod.rs +++ b/compiler/rustc_middle/src/hir/map/mod.rs @@ -180,11 +180,6 @@ impl<'hir> Map<'hir> { self.tcx.definitions.local_def_id_to_hir_id(def_id) } - #[inline] - pub fn opt_local_def_id_to_hir_id(&self, def_id: LocalDefId) -> Option { - self.tcx.definitions.opt_local_def_id_to_hir_id(def_id) - } - pub fn iter_local_def_id(&self) -> impl Iterator + '_ { self.tcx.definitions.iter_local_def_id() } @@ -457,17 +452,14 @@ impl<'hir> Map<'hir> { /// invoking `krate.attrs` because it registers a tighter /// dep-graph access. pub fn krate_attrs(&self) -> &'hir [ast::Attribute] { - match self.get_entry(CRATE_HIR_ID).node { - Node::Crate(item) => item.attrs, - _ => bug!(), - } + self.attrs(CRATE_HIR_ID) } pub fn get_module(&self, module: LocalDefId) -> (&'hir Mod<'hir>, Span, HirId) { let hir_id = self.local_def_id_to_hir_id(module); match self.get_entry(hir_id).node { Node::Item(&Item { span, kind: ItemKind::Mod(ref m), .. }) => (m, span, hir_id), - Node::Crate(item) => (&item.module, item.span, hir_id), + Node::Crate(item) => (&item, item.inner, hir_id), node => panic!("not a module: {:?}", node), } } @@ -558,24 +550,6 @@ impl<'hir> Map<'hir> { ParentHirIterator { current_id, map: self } } - /// Checks if the node is an argument. An argument is a local variable whose - /// immediate parent is an item or a closure. - pub fn is_argument(&self, id: HirId) -> bool { - match self.find(id) { - Some(Node::Binding(_)) => (), - _ => return false, - } - matches!( - self.find(self.get_parent_node(id)), - Some( - Node::Item(_) - | Node::TraitItem(_) - | Node::ImplItem(_) - | Node::Expr(Expr { kind: ExprKind::Closure(..), .. }), - ) - ) - } - /// Checks if the node is left-hand side of an assignment. pub fn is_lhs(&self, id: HirId) -> bool { match self.find(self.get_parent_node(id)) { @@ -787,17 +761,6 @@ impl<'hir> Map<'hir> { } } - pub fn expect_variant_data(&self, id: HirId) -> &'hir VariantData<'hir> { - match self.find(id) { - Some( - Node::Ctor(vd) - | Node::Item(Item { kind: ItemKind::Struct(vd, _) | ItemKind::Union(vd, _), .. }), - ) => vd, - Some(Node::Variant(variant)) => &variant.data, - _ => bug!("expected struct or variant, found {}", self.node_to_string(id)), - } - } - pub fn expect_variant(&self, id: HirId) -> &'hir Variant<'hir> { match self.find(id) { Some(Node::Variant(variant)) => variant, @@ -853,34 +816,7 @@ impl<'hir> Map<'hir> { /// Given a node ID, gets a list of attributes associated with the AST /// corresponding to the node-ID. pub fn attrs(&self, id: HirId) -> &'hir [ast::Attribute] { - self.find_entry(id).map_or(&[], |entry| match entry.node { - Node::Param(a) => a.attrs, - Node::Local(l) => &l.attrs[..], - Node::Item(i) => i.attrs, - Node::ForeignItem(fi) => fi.attrs, - Node::TraitItem(ref ti) => ti.attrs, - Node::ImplItem(ref ii) => ii.attrs, - Node::Variant(ref v) => v.attrs, - Node::Field(ref f) => f.attrs, - Node::Expr(ref e) => &*e.attrs, - Node::Stmt(ref s) => s.kind.attrs(|id| self.item(id)), - Node::Arm(ref a) => &*a.attrs, - Node::GenericParam(param) => param.attrs, - // Unit/tuple structs/variants take the attributes straight from - // the struct/variant definition. - Node::Ctor(..) => self.attrs(self.get_parent_item(id)), - Node::Crate(item) => item.attrs, - Node::MacroDef(def) => def.attrs, - Node::AnonConst(..) - | Node::PathSegment(..) - | Node::Ty(..) - | Node::Pat(..) - | Node::Binding(..) - | Node::TraitRef(..) - | Node::Block(..) - | Node::Lifetime(..) - | Node::Visibility(..) => &[], - }) + self.tcx.hir_attrs(id.owner).get(id.local_id) } /// Gets the span of the definition of the specified HIR node. @@ -932,7 +868,7 @@ impl<'hir> Map<'hir> { Node::Visibility(v) => bug!("unexpected Visibility {:?}", v), Node::Local(local) => local.span, Node::MacroDef(macro_def) => macro_def.span, - Node::Crate(item) => item.span, + Node::Crate(item) => item.inner, }; Some(span) } diff --git a/compiler/rustc_middle/src/hir/mod.rs b/compiler/rustc_middle/src/hir/mod.rs index 5f9cf8771eada..cf4e473d8aca1 100644 --- a/compiler/rustc_middle/src/hir/mod.rs +++ b/compiler/rustc_middle/src/hir/mod.rs @@ -9,6 +9,7 @@ pub mod place; use crate::ich::StableHashingContext; use crate::ty::query::Providers; use crate::ty::TyCtxt; +use rustc_ast::Attribute; use rustc_data_structures::fingerprint::Fingerprint; use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; @@ -16,6 +17,7 @@ use rustc_hir::def_id::{LocalDefId, LOCAL_CRATE}; use rustc_hir::*; use rustc_index::vec::IndexVec; use rustc_span::DUMMY_SP; +use std::collections::BTreeMap; #[derive(Debug)] pub struct Owner<'tcx> { @@ -55,6 +57,48 @@ impl<'a, 'tcx> HashStable> for OwnerNodes<'tcx> { } } +#[derive(Copy, Clone)] +pub struct AttributeMap<'tcx> { + map: &'tcx BTreeMap, + prefix: LocalDefId, +} + +impl<'a, 'tcx> HashStable> for AttributeMap<'tcx> { + fn hash_stable(&self, hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { + let range = self.range(); + + range.clone().count().hash_stable(hcx, hasher); + for (key, value) in range { + key.hash_stable(hcx, hasher); + value.hash_stable(hcx, hasher); + } + } +} + +impl<'tcx> std::fmt::Debug for AttributeMap<'tcx> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("AttributeMap") + .field("prefix", &self.prefix) + .field("range", &&self.range().collect::>()[..]) + .finish() + } +} + +impl<'tcx> AttributeMap<'tcx> { + fn get(&self, id: ItemLocalId) -> &'tcx [Attribute] { + self.map.get(&HirId { owner: self.prefix, local_id: id }).copied().unwrap_or(&[]) + } + + fn range(&self) -> std::collections::btree_map::Range<'_, rustc_hir::HirId, &[Attribute]> { + let local_zero = ItemLocalId::from_u32(0); + let range = HirId { owner: self.prefix, local_id: local_zero }..HirId { + owner: LocalDefId { local_def_index: self.prefix.local_def_index + 1 }, + local_id: local_zero, + }; + self.map.range(range) + } +} + impl<'tcx> TyCtxt<'tcx> { #[inline(always)] pub fn hir(self) -> map::Map<'tcx> { @@ -76,6 +120,7 @@ pub fn provide(providers: &mut Providers) { providers.hir_module_items = |tcx, id| &tcx.untracked_crate.modules[&id]; providers.hir_owner = |tcx, id| tcx.index_hir(LOCAL_CRATE).map[id].signature; providers.hir_owner_nodes = |tcx, id| tcx.index_hir(LOCAL_CRATE).map[id].with_bodies.as_deref(); + providers.hir_attrs = |tcx, id| AttributeMap { map: &tcx.untracked_crate.attrs, prefix: id }; providers.def_span = |tcx, def_id| tcx.hir().span_if_local(def_id).unwrap_or(DUMMY_SP); providers.fn_arg_names = |tcx, id| { let hir = tcx.hir(); diff --git a/compiler/rustc_middle/src/ich/hcx.rs b/compiler/rustc_middle/src/ich/hcx.rs index 51b650e5adef8..b2fef731b7e20 100644 --- a/compiler/rustc_middle/src/ich/hcx.rs +++ b/compiler/rustc_middle/src/ich/hcx.rs @@ -119,11 +119,6 @@ impl<'a> StableHashingContext<'a> { Self::new_with_or_without_spans(sess, krate, definitions, cstore, always_ignore_spans) } - #[inline] - pub fn sess(&self) -> &'a Session { - self.sess - } - #[inline] pub fn while_hashing_hir_bodies(&mut self, hash_bodies: bool, f: F) { let prev_hash_bodies = self.hash_bodies; @@ -250,13 +245,6 @@ impl<'a> rustc_span::HashStableContext for StableHashingContext<'a> { &CACHE } - fn byte_pos_to_line_and_col( - &mut self, - byte: BytePos, - ) -> Option<(Lrc, usize, BytePos)> { - self.source_map().byte_pos_to_line_and_col(byte) - } - fn span_data_to_lines_and_cols( &mut self, span: &SpanData, diff --git a/compiler/rustc_middle/src/ich/impls_hir.rs b/compiler/rustc_middle/src/ich/impls_hir.rs index 5ef70a89051ea..abf56832329b2 100644 --- a/compiler/rustc_middle/src/ich/impls_hir.rs +++ b/compiler/rustc_middle/src/ich/impls_hir.rs @@ -66,11 +66,10 @@ impl<'ctx> rustc_hir::HashStableContext for StableHashingContext<'ctx> { fn hash_hir_expr(&mut self, expr: &hir::Expr<'_>, hasher: &mut StableHasher) { self.while_hashing_hir_bodies(true, |hcx| { - let hir::Expr { hir_id: _, ref span, ref kind, ref attrs } = *expr; + let hir::Expr { hir_id: _, ref span, ref kind } = *expr; span.hash_stable(hcx, hasher); kind.hash_stable(hcx, hasher); - attrs.hash_stable(hcx, hasher); }) } diff --git a/compiler/rustc_middle/src/ich/impls_syntax.rs b/compiler/rustc_middle/src/ich/impls_syntax.rs index bfbe15749ee1c..b93b25d6b5c9a 100644 --- a/compiler/rustc_middle/src/ich/impls_syntax.rs +++ b/compiler/rustc_middle/src/ich/impls_syntax.rs @@ -1,5 +1,5 @@ //! This module contains `HashStable` implementations for various data types -//! from librustc_ast in no particular order. +//! from `rustc_ast` in no particular order. use crate::ich::StableHashingContext; @@ -45,7 +45,11 @@ impl<'ctx> rustc_ast::HashStableContext for StableHashingContext<'ctx> { item.hash_stable(self, hasher); style.hash_stable(self, hasher); span.hash_stable(self, hasher); - tokens.as_ref().expect_none("Tokens should have been removed during lowering!"); + assert_matches!( + tokens.as_ref(), + None, + "Tokens should have been removed during lowering!" + ); } else { unreachable!(); } diff --git a/compiler/rustc_middle/src/ich/impls_ty.rs b/compiler/rustc_middle/src/ich/impls_ty.rs index 573b514e8445f..8e53e4ba94805 100644 --- a/compiler/rustc_middle/src/ich/impls_ty.rs +++ b/compiler/rustc_middle/src/ich/impls_ty.rs @@ -70,16 +70,16 @@ impl<'a> HashStable> for ty::RegionKind { ty::ReEmpty(universe) => { universe.hash_stable(hcx, hasher); } - ty::ReLateBound(db, ty::BoundRegion { kind: ty::BrAnon(i) }) => { + ty::ReLateBound(db, ty::BoundRegion { kind: ty::BrAnon(i), .. }) => { db.hash_stable(hcx, hasher); i.hash_stable(hcx, hasher); } - ty::ReLateBound(db, ty::BoundRegion { kind: ty::BrNamed(def_id, name) }) => { + ty::ReLateBound(db, ty::BoundRegion { kind: ty::BrNamed(def_id, name), .. }) => { db.hash_stable(hcx, hasher); def_id.hash_stable(hcx, hasher); name.hash_stable(hcx, hasher); } - ty::ReLateBound(db, ty::BoundRegion { kind: ty::BrEnv }) => { + ty::ReLateBound(db, ty::BoundRegion { kind: ty::BrEnv, .. }) => { db.hash_stable(hcx, hasher); } ty::ReEarlyBound(ty::EarlyBoundRegion { def_id, index, name }) => { @@ -118,12 +118,13 @@ impl<'tcx> HashStable> for ty::BoundVar { } } -impl<'a, T> HashStable> for ty::Binder +impl<'a, 'tcx, T> HashStable> for ty::Binder<'tcx, T> where T: HashStable>, { fn hash_stable(&self, hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { self.as_ref().skip_binder().hash_stable(hcx, hasher); + self.bound_vars().hash_stable(hcx, hasher); } } diff --git a/compiler/rustc_middle/src/infer/canonical.rs b/compiler/rustc_middle/src/infer/canonical.rs index e106db38b2c9a..5df2f91f09fff 100644 --- a/compiler/rustc_middle/src/infer/canonical.rs +++ b/compiler/rustc_middle/src/infer/canonical.rs @@ -27,6 +27,7 @@ use crate::ty::{self, BoundVar, List, Region, TyCtxt}; use rustc_index::vec::IndexVec; use rustc_macros::HashStable; use smallvec::SmallVec; +use std::iter; use std::ops::Index; /// A "canonicalized" type `V` is one where all free inference @@ -227,20 +228,12 @@ impl Certainty { Certainty::Ambiguous => false, } } - - pub fn is_ambiguous(&self) -> bool { - !self.is_proven() - } } impl<'tcx, R> QueryResponse<'tcx, R> { pub fn is_proven(&self) -> bool { self.certainty.is_proven() } - - pub fn is_ambiguous(&self) -> bool { - !self.is_proven() - } } impl<'tcx, R> Canonical<'tcx, QueryResponse<'tcx, R>> { @@ -284,7 +277,7 @@ impl<'tcx, V> Canonical<'tcx, V> { } pub type QueryOutlivesConstraint<'tcx> = - ty::Binder, Region<'tcx>>>; + ty::Binder<'tcx, ty::OutlivesPredicate, Region<'tcx>>>; TrivialTypeFoldableAndLiftImpls! { for <'tcx> { @@ -315,16 +308,14 @@ impl<'tcx> CanonicalVarValues<'tcx> { use crate::ty::subst::GenericArgKind; CanonicalVarValues { - var_values: self - .var_values - .iter() - .zip(0..) + var_values: iter::zip(&self.var_values, 0..) .map(|(kind, i)| match kind.unpack() { GenericArgKind::Type(..) => { tcx.mk_ty(ty::Bound(ty::INNERMOST, ty::BoundVar::from_u32(i).into())).into() } GenericArgKind::Lifetime(..) => { - let br = ty::BoundRegion { kind: ty::BrAnon(i) }; + let br = + ty::BoundRegion { var: ty::BoundVar::from_u32(i), kind: ty::BrAnon(i) }; tcx.mk_region(ty::ReLateBound(ty::INNERMOST, br)).into() } GenericArgKind::Const(ct) => tcx diff --git a/compiler/rustc_middle/src/infer/unify_key.rs b/compiler/rustc_middle/src/infer/unify_key.rs index 8318bdefc8e18..641cf23781e2c 100644 --- a/compiler/rustc_middle/src/infer/unify_key.rs +++ b/compiler/rustc_middle/src/infer/unify_key.rs @@ -97,13 +97,6 @@ impl<'tcx> ConstVariableValue<'tcx> { ConstVariableValue::Known { value } => Some(value), } } - - pub fn is_unknown(&self) -> bool { - match *self { - ConstVariableValue::Unknown { .. } => true, - ConstVariableValue::Known { .. } => false, - } - } } #[derive(Copy, Clone, Debug)] diff --git a/compiler/rustc_middle/src/lib.rs b/compiler/rustc_middle/src/lib.rs index d9e8826505002..1db03e9165b88 100644 --- a/compiler/rustc_middle/src/lib.rs +++ b/compiler/rustc_middle/src/lib.rs @@ -24,12 +24,11 @@ #![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")] #![feature(array_windows)] -#![feature(assoc_char_funcs)] +#![feature(assert_matches)] #![feature(backtrace)] #![feature(bool_to_option)] #![feature(box_patterns)] #![feature(box_syntax)] -#![feature(cmp_min_max_by)] #![feature(const_fn)] #![feature(const_panic)] #![feature(core_intrinsics)] @@ -38,8 +37,7 @@ #![feature(extern_types)] #![feature(nll)] #![feature(once_cell)] -#![feature(option_expect_none)] -#![feature(or_patterns)] +#![cfg_attr(bootstrap, feature(or_patterns))] #![feature(min_specialization)] #![feature(trusted_len)] #![feature(test)] @@ -52,6 +50,7 @@ #![feature(exclusive_range_pattern)] #![feature(control_flow_enum)] #![feature(associated_type_defaults)] +#![feature(iter_zip)] #![recursion_limit = "512"] #[macro_use] diff --git a/compiler/rustc_middle/src/middle/codegen_fn_attrs.rs b/compiler/rustc_middle/src/middle/codegen_fn_attrs.rs index 5f2ffda642cee..7024d9a3d21db 100644 --- a/compiler/rustc_middle/src/middle/codegen_fn_attrs.rs +++ b/compiler/rustc_middle/src/middle/codegen_fn_attrs.rs @@ -1,7 +1,7 @@ use crate::mir::mono::Linkage; use rustc_attr::{InlineAttr, InstructionSetAttr, OptimizeAttr}; -use rustc_session::config::SanitizerSet; use rustc_span::symbol::Symbol; +use rustc_target::spec::SanitizerSet; #[derive(Clone, TyEncodable, TyDecodable, HashStable, Debug)] pub struct CodegenFnAttrs { @@ -38,6 +38,9 @@ pub struct CodegenFnAttrs { /// be generated against a specific instruction set. Only usable on architectures which allow /// switching between multiple instruction sets. pub instruction_set: Option, + /// The `#[repr(align(...))]` attribute. Indicates the value of which the function should be + /// aligned to. + pub alignment: Option, } bitflags! { @@ -103,6 +106,7 @@ impl CodegenFnAttrs { link_section: None, no_sanitize: SanitizerSet::empty(), instruction_set: None, + alignment: None, } } diff --git a/compiler/rustc_middle/src/middle/region.rs b/compiler/rustc_middle/src/middle/region.rs index eb48198991c29..f44267a404bf3 100644 --- a/compiler/rustc_middle/src/middle/region.rs +++ b/compiler/rustc_middle/src/middle/region.rs @@ -235,18 +235,6 @@ pub struct ScopeTree { /// escape into 'static and should have no local cleanup scope. rvalue_scopes: FxHashMap>, - /// Encodes the hierarchy of fn bodies. Every fn body (including - /// closures) forms its own distinct region hierarchy, rooted in - /// the block that is the fn body. This map points from the ID of - /// that root block to the ID of the root block for the enclosing - /// fn, if any. Thus the map structures the fn bodies into a - /// hierarchy based on their lexical mapping. This is used to - /// handle the relationships between regions in a fn and in a - /// closure defined by that fn. See the "Modeling closures" - /// section of the README in infer::region_constraints for - /// more details. - closure_tree: FxHashMap, - /// If there are any `yield` nested within a scope, this map /// stores the `Span` of the last one and its index in the /// postorder of the Visitor traversal on the HIR. @@ -356,23 +344,6 @@ impl ScopeTree { self.destruction_scopes.get(&n).cloned() } - /// Records that `sub_closure` is defined within `sup_closure`. These IDs - /// should be the ID of the block that is the fn body, which is - /// also the root of the region hierarchy for that fn. - pub fn record_closure_parent( - &mut self, - sub_closure: hir::ItemLocalId, - sup_closure: hir::ItemLocalId, - ) { - debug!( - "record_closure_parent(sub_closure={:?}, sup_closure={:?})", - sub_closure, sup_closure - ); - assert!(sub_closure != sup_closure); - let previous = self.closure_tree.insert(sub_closure, sup_closure); - assert!(previous.is_none()); - } - pub fn record_var_scope(&mut self, var: hir::ItemLocalId, lifetime: Scope) { debug!("record_var_scope(sub={:?}, sup={:?})", var, lifetime); assert!(var != lifetime.item_local_id()); @@ -430,6 +401,8 @@ impl ScopeTree { /// Returns `true` if `subscope` is equal to or is lexically nested inside `superscope`, and /// `false` otherwise. + /// + /// Used by clippy. pub fn is_subscope_of(&self, subscope: Scope, superscope: Scope) -> bool { let mut s = subscope; debug!("is_subscope_of({:?}, {:?})", subscope, superscope); @@ -472,7 +445,6 @@ impl<'a> HashStable> for ScopeTree { ref var_map, ref destruction_scopes, ref rvalue_scopes, - ref closure_tree, ref yield_in_scope, } = *self; @@ -486,7 +458,6 @@ impl<'a> HashStable> for ScopeTree { var_map.hash_stable(hcx, hasher); destruction_scopes.hash_stable(hcx, hasher); rvalue_scopes.hash_stable(hcx, hasher); - closure_tree.hash_stable(hcx, hasher); yield_in_scope.hash_stable(hcx, hasher); } } diff --git a/compiler/rustc_middle/src/middle/resolve_lifetime.rs b/compiler/rustc_middle/src/middle/resolve_lifetime.rs index 1b7d0e620a4c5..aa6488b329eba 100644 --- a/compiler/rustc_middle/src/middle/resolve_lifetime.rs +++ b/compiler/rustc_middle/src/middle/resolve_lifetime.rs @@ -39,8 +39,13 @@ impl LifetimeDefOrigin { pub enum Region { Static, EarlyBound(/* index */ u32, /* lifetime decl */ DefId, LifetimeDefOrigin), - LateBound(ty::DebruijnIndex, /* lifetime decl */ DefId, LifetimeDefOrigin), - LateBoundAnon(ty::DebruijnIndex, /* anon index */ u32), + LateBound( + ty::DebruijnIndex, + /* late-bound index */ u32, + /* lifetime decl */ DefId, + LifetimeDefOrigin, + ), + LateBoundAnon(ty::DebruijnIndex, /* late-bound index */ u32, /* anon index */ u32), Free(DefId, /* lifetime decl */ DefId), } @@ -79,8 +84,5 @@ pub struct ResolveLifetimes { /// (b) it DOES appear in the arguments. pub late_bound: FxHashMap>, - /// For each type and trait definition, maps type parameters - /// to the trait object lifetime defaults computed from them. - pub object_lifetime_defaults: - FxHashMap>>, + pub late_bound_vars: FxHashMap>>, } diff --git a/compiler/rustc_middle/src/middle/stability.rs b/compiler/rustc_middle/src/middle/stability.rs index 89ca8eed39a9b..fc9a2970e00ae 100644 --- a/compiler/rustc_middle/src/middle/stability.rs +++ b/compiler/rustc_middle/src/middle/stability.rs @@ -29,12 +29,6 @@ pub enum StabilityLevel { Stable, } -impl StabilityLevel { - pub fn from_attr_level(level: &attr::StabilityLevel) -> Self { - if level.is_stable() { Stable } else { Unstable } - } -} - /// An entry in the `depr_map`. #[derive(Clone, HashStable, Debug)] pub struct DeprecationEntry { diff --git a/compiler/rustc_middle/src/mir/abstract_const.rs b/compiler/rustc_middle/src/mir/abstract_const.rs index b85f1e6e5ded0..776a777b1bdb5 100644 --- a/compiler/rustc_middle/src/mir/abstract_const.rs +++ b/compiler/rustc_middle/src/mir/abstract_const.rs @@ -18,3 +18,20 @@ pub enum Node<'tcx> { UnaryOp(mir::UnOp, NodeId), FunctionCall(NodeId, &'tcx [NodeId]), } + +#[derive(Debug, Copy, Clone, PartialEq, Eq, HashStable, TyEncodable, TyDecodable)] +pub enum NotConstEvaluatable { + Error(rustc_errors::ErrorReported), + MentionsInfer, + MentionsParam, +} + +impl From for NotConstEvaluatable { + fn from(e: rustc_errors::ErrorReported) -> NotConstEvaluatable { + NotConstEvaluatable::Error(e) + } +} + +TrivialTypeFoldableAndLiftImpls! { + NotConstEvaluatable, +} diff --git a/compiler/rustc_middle/src/mir/coverage.rs b/compiler/rustc_middle/src/mir/coverage.rs index eae02a8cbfcf1..ddb1a84fe7bdf 100644 --- a/compiler/rustc_middle/src/mir/coverage.rs +++ b/compiler/rustc_middle/src/mir/coverage.rs @@ -41,8 +41,16 @@ rustc_index::newtype_index! { } impl CounterValueReference { - // Counters start at 1 to reserve 0 for ExpressionOperandId::ZERO. + /// Counters start at 1 to reserve 0 for ExpressionOperandId::ZERO. pub const START: Self = Self::from_u32(1); + + /// Returns explicitly-requested zero-based version of the counter id, used + /// during codegen. LLVM expects zero-based indexes. + pub fn zero_based_index(&self) -> u32 { + let one_based_index = self.as_u32(); + debug_assert!(one_based_index > 0); + one_based_index - 1 + } } rustc_index::newtype_index! { @@ -117,17 +125,9 @@ impl CoverageKind { } } - pub fn is_counter(&self) -> bool { - matches!(self, Self::Counter { .. }) - } - pub fn is_expression(&self) -> bool { matches!(self, Self::Expression { .. }) } - - pub fn is_unreachable(&self) -> bool { - *self == Self::Unreachable - } } impl Debug for CoverageKind { @@ -183,3 +183,13 @@ pub enum Op { Subtract, Add, } + +impl Op { + pub fn is_add(&self) -> bool { + matches!(self, Self::Add) + } + + pub fn is_subtract(&self) -> bool { + matches!(self, Self::Subtract) + } +} diff --git a/compiler/rustc_middle/src/mir/interpret/allocation.rs b/compiler/rustc_middle/src/mir/interpret/allocation.rs index 48595f2badc8f..766d6a06f7e59 100644 --- a/compiler/rustc_middle/src/mir/interpret/allocation.rs +++ b/compiler/rustc_middle/src/mir/interpret/allocation.rs @@ -339,7 +339,7 @@ impl<'tcx, Tag: Copy, Extra: AllocationExtra> Allocation { for dest in bytes { *dest = src.next().expect("iterator was shorter than it said it would be"); } - src.next().expect_none("iterator was longer than it said it would be"); + assert_matches!(src.next(), None, "iterator was longer than it said it would be"); Ok(()) } diff --git a/compiler/rustc_middle/src/mir/interpret/error.rs b/compiler/rustc_middle/src/mir/interpret/error.rs index 1589ab28e4043..cc0df127434e5 100644 --- a/compiler/rustc_middle/src/mir/interpret/error.rs +++ b/compiler/rustc_middle/src/mir/interpret/error.rs @@ -40,13 +40,13 @@ pub fn struct_error<'tcx>(tcx: TyCtxtAt<'tcx>, msg: &str) -> DiagnosticBuilder<' struct_span_err!(tcx.sess, tcx.span, E0080, "{}", msg) } -#[cfg(target_arch = "x86_64")] +#[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))] static_assert_size!(InterpErrorInfo<'_>, 8); /// Packages the kind of error we got from the const code interpreter /// up with a Rust-level backtrace of where the error occurred. -/// Thsese should always be constructed by calling `.into()` on -/// a `InterpError`. In `librustc_mir::interpret`, we have `throw_err_*` +/// These should always be constructed by calling `.into()` on +/// a `InterpError`. In `rustc_mir::interpret`, we have `throw_err_*` /// macros for this. #[derive(Debug)] pub struct InterpErrorInfo<'tcx>(Box>); @@ -444,7 +444,7 @@ impl dyn MachineStopType { } } -#[cfg(target_arch = "x86_64")] +#[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))] static_assert_size!(InterpError<'_>, 72); pub enum InterpError<'tcx> { diff --git a/compiler/rustc_middle/src/mir/interpret/queries.rs b/compiler/rustc_middle/src/mir/interpret/queries.rs index 3e7b93b32a6a5..fa7c0670e8ce6 100644 --- a/compiler/rustc_middle/src/mir/interpret/queries.rs +++ b/compiler/rustc_middle/src/mir/interpret/queries.rs @@ -1,7 +1,7 @@ use super::{ErrorHandled, EvalToConstValueResult, GlobalId}; use crate::mir; -use crate::ty::subst::{InternalSubsts, SubstsRef}; +use crate::ty::subst::InternalSubsts; use crate::ty::{self, TyCtxt}; use rustc_hir::def_id::DefId; use rustc_span::Span; @@ -35,14 +35,12 @@ impl<'tcx> TyCtxt<'tcx> { pub fn const_eval_resolve( self, param_env: ty::ParamEnv<'tcx>, - def: ty::WithOptConstParam, - substs: SubstsRef<'tcx>, - promoted: Option, + ct: ty::Unevaluated<'tcx>, span: Option, ) -> EvalToConstValueResult<'tcx> { - match ty::Instance::resolve_opt_const_arg(self, param_env, def, substs) { + match ty::Instance::resolve_opt_const_arg(self, param_env, ct.def, ct.substs) { Ok(Some(instance)) => { - let cid = GlobalId { instance, promoted }; + let cid = GlobalId { instance, promoted: ct.promoted }; self.const_eval_global_id(param_env, cid, span) } Ok(None) => Err(ErrorHandled::TooGeneric), diff --git a/compiler/rustc_middle/src/mir/interpret/value.rs b/compiler/rustc_middle/src/mir/interpret/value.rs index 4bb39fe4a527e..888777a9418b3 100644 --- a/compiler/rustc_middle/src/mir/interpret/value.rs +++ b/compiler/rustc_middle/src/mir/interpret/value.rs @@ -1,4 +1,4 @@ -use std::convert::TryFrom; +use std::convert::{TryFrom, TryInto}; use std::fmt; use rustc_apfloat::{ @@ -8,12 +8,12 @@ use rustc_apfloat::{ use rustc_macros::HashStable; use rustc_target::abi::{HasDataLayout, Size, TargetDataLayout}; -use crate::ty::{ParamEnv, ScalarInt, Ty, TyCtxt}; +use crate::ty::{Lift, ParamEnv, ScalarInt, Ty, TyCtxt}; use super::{AllocId, Allocation, InterpResult, Pointer, PointerArithmetic}; /// Represents the result of const evaluation via the `eval_to_allocation` query. -#[derive(Clone, HashStable, TyEncodable, TyDecodable, Debug)] +#[derive(Copy, Clone, HashStable, TyEncodable, TyDecodable, Debug, Hash, Eq, PartialEq)] pub struct ConstAlloc<'tcx> { // the value lives here, at offset 0, and that allocation definitely is a `AllocKind::Memory` // (so you can use `AllocMap::unwrap_memory`). @@ -44,9 +44,30 @@ pub enum ConstValue<'tcx> { }, } -#[cfg(target_arch = "x86_64")] +#[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))] static_assert_size!(ConstValue<'_>, 32); +impl From for ConstValue<'tcx> { + fn from(s: Scalar) -> Self { + Self::Scalar(s) + } +} + +impl<'a, 'tcx> Lift<'tcx> for ConstValue<'a> { + type Lifted = ConstValue<'tcx>; + fn lift_to_tcx(self, tcx: TyCtxt<'tcx>) -> Option> { + Some(match self { + ConstValue::Scalar(s) => ConstValue::Scalar(s), + ConstValue::Slice { data, start, end } => { + ConstValue::Slice { data: tcx.lift(data)?, start, end } + } + ConstValue::ByRef { alloc, offset } => { + ConstValue::ByRef { alloc: tcx.lift(alloc)?, offset } + } + }) + } +} + impl<'tcx> ConstValue<'tcx> { #[inline] pub fn try_to_scalar(&self) -> Option { @@ -56,20 +77,20 @@ impl<'tcx> ConstValue<'tcx> { } } + pub fn try_to_scalar_int(&self) -> Option { + Some(self.try_to_scalar()?.assert_int()) + } + pub fn try_to_bits(&self, size: Size) -> Option { - self.try_to_scalar()?.to_bits(size).ok() + self.try_to_scalar_int()?.to_bits(size).ok() } pub fn try_to_bool(&self) -> Option { - match self.try_to_bits(Size::from_bytes(1))? { - 0 => Some(false), - 1 => Some(true), - _ => None, - } + self.try_to_scalar_int()?.try_into().ok() } pub fn try_to_machine_usize(&self, tcx: TyCtxt<'tcx>) -> Option { - Some(self.try_to_bits(tcx.data_layout.pointer_size)? as u64) + self.try_to_scalar_int()?.try_to_machine_usize(tcx).ok() } pub fn try_to_bits_for_ty( @@ -111,7 +132,7 @@ pub enum Scalar { Ptr(Pointer), } -#[cfg(target_arch = "x86_64")] +#[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))] static_assert_size!(Scalar, 24); // We want the `Debug` output to be readable as it is used by `derive(Debug)` for @@ -286,16 +307,6 @@ impl<'tcx, Tag> Scalar { .unwrap_or_else(|| bug!("Signed value {:#x} does not fit in {} bits", i, size.bits())) } - #[inline] - pub fn from_i8(i: i8) -> Self { - Self::from_int(i, Size::from_bits(8)) - } - - #[inline] - pub fn from_i16(i: i16) -> Self { - Self::from_int(i, Size::from_bits(16)) - } - #[inline] pub fn from_i32(i: i32) -> Self { Self::from_int(i, Size::from_bits(32)) @@ -503,13 +514,20 @@ impl From> for Scalar { } } +impl From for Scalar { + #[inline(always)] + fn from(ptr: ScalarInt) -> Self { + Scalar::Int(ptr) + } +} + #[derive(Clone, Copy, Eq, PartialEq, TyEncodable, TyDecodable, HashStable, Hash)] pub enum ScalarMaybeUninit { Scalar(Scalar), Uninit, } -#[cfg(target_arch = "x86_64")] +#[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))] static_assert_size!(ScalarMaybeUninit, 24); impl From> for ScalarMaybeUninit { diff --git a/compiler/rustc_middle/src/mir/mod.rs b/compiler/rustc_middle/src/mir/mod.rs index 4d8615a215fc0..998868211401f 100644 --- a/compiler/rustc_middle/src/mir/mod.rs +++ b/compiler/rustc_middle/src/mir/mod.rs @@ -11,12 +11,12 @@ use crate::ty::fold::{TypeFoldable, TypeFolder, TypeVisitor}; use crate::ty::print::{FmtPrinter, Printer}; use crate::ty::subst::{Subst, SubstsRef}; use crate::ty::{self, List, Ty, TyCtxt}; -use crate::ty::{AdtDef, InstanceDef, Region, UserTypeAnnotationIndex}; +use crate::ty::{AdtDef, InstanceDef, Region, ScalarInt, UserTypeAnnotationIndex}; use rustc_hir as hir; use rustc_hir::def::{CtorKind, Namespace}; use rustc_hir::def_id::{DefId, CRATE_DEF_INDEX}; use rustc_hir::{self, GeneratorKind}; -use rustc_target::abi::VariantIdx; +use rustc_target::abi::{Size, VariantIdx}; use polonius_engine::Atom; pub use rustc_ast::Mutability; @@ -30,6 +30,7 @@ use rustc_span::symbol::Symbol; use rustc_span::{Span, DUMMY_SP}; use rustc_target::asm::InlineAsmRegOrRegClass; use std::borrow::Cow; +use std::convert::TryInto; use std::fmt::{self, Debug, Display, Formatter, Write}; use std::ops::{ControlFlow, Index, IndexMut}; use std::slice; @@ -378,24 +379,6 @@ impl<'tcx> Body<'tcx> { } } - /// Returns an iterator over all temporaries. - #[inline] - pub fn temps_iter<'a>(&'a self) -> impl Iterator + 'a { - (self.arg_count + 1..self.local_decls.len()).filter_map(move |index| { - let local = Local::new(index); - if self.local_decls[local].is_user_variable() { None } else { Some(local) } - }) - } - - /// Returns an iterator over all user-declared locals. - #[inline] - pub fn vars_iter<'a>(&'a self) -> impl Iterator + 'a { - (self.arg_count + 1..self.local_decls.len()).filter_map(move |index| { - let local = Local::new(index); - self.local_decls[local].is_user_variable().then_some(local) - }) - } - /// Returns an iterator over all user-declared mutable locals. #[inline] pub fn mut_vars_iter<'a>(&'a self) -> impl Iterator + 'a { @@ -951,7 +934,7 @@ pub struct LocalDecl<'tcx> { } // `LocalDecl` is used a lot. Make sure it doesn't unintentionally get bigger. -#[cfg(target_arch = "x86_64")] +#[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))] static_assert_size!(LocalDecl<'_>, 56); /// Extra information about a some locals that's used for diagnostics and for @@ -1230,7 +1213,7 @@ pub enum InlineAsmOperand<'tcx> { out_place: Option>, }, Const { - value: Operand<'tcx>, + value: Box>, }, SymFn { value: Box>, @@ -1468,7 +1451,7 @@ pub struct Statement<'tcx> { } // `Statement` is used a lot. Make sure it doesn't unintentionally get bigger. -#[cfg(target_arch = "x86_64")] +#[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))] static_assert_size!(Statement<'_>, 32); impl Statement<'_> { @@ -1499,7 +1482,7 @@ pub enum StatementKind<'tcx> { /// /// Note that this also is emitted for regular `let` bindings to ensure that locals that are /// never accessed still get some sanity checks for, e.g., `let x: ! = ..;` - FakeRead(FakeReadCause, Box>), + FakeRead(Box<(FakeReadCause, Place<'tcx>)>), /// Write the discriminant for a variant to the enum Place. SetDiscriminant { place: Box>, variant_index: VariantIdx }, @@ -1536,11 +1519,17 @@ pub enum StatementKind<'tcx> { AscribeUserType(Box<(Place<'tcx>, UserTypeProjection)>, ty::Variance), /// Marks the start of a "coverage region", injected with '-Zinstrument-coverage'. A - /// `CoverageInfo` statement carries metadata about the coverage region, used to inject a coverage - /// map into the binary. The `Counter` kind also generates executable code, to increment a - /// counter varible at runtime, each time the code region is executed. + /// `Coverage` statement carries metadata about the coverage region, used to inject a coverage + /// map into the binary. If `Coverage::kind` is a `Counter`, the statement also generates + /// executable code, to increment a counter variable at runtime, each time the code region is + /// executed. Coverage(Box), + /// Denotes a call to the intrinsic function copy_overlapping, where `src_dst` denotes the + /// memory being read from and written to(one field to save memory), and size + /// indicates how many bytes are being copied over. + CopyNonOverlapping(Box>), + /// No-op. Useful for deleting instructions without affecting statement indices. Nop, } @@ -1586,7 +1575,12 @@ pub enum FakeReadCause { /// `let x: !; match x {}` doesn't generate any read of x so we need to /// generate a read of x to check that it is initialized and safe. - ForMatchedPlace, + /// + /// If a closure pattern matches a Place starting with an Upvar, then we introduce a + /// FakeRead for that Place outside the closure, in such a case this option would be + /// Some(closure_def_id). + /// Otherwise, the value of the optional DefId will be None. + ForMatchedPlace(Option), /// A fake read of the RefWithinGuard version of a bind-by-value variable /// in a match guard to ensure that it's value hasn't change by the time @@ -1605,7 +1599,12 @@ pub enum FakeReadCause { /// but in some cases it can affect the borrow checker, as in #53695. /// Therefore, we insert a "fake read" here to ensure that we get /// appropriate errors. - ForLet, + /// + /// If a closure pattern matches a Place starting with an Upvar, then we introduce a + /// FakeRead for that Place outside the closure, in such a case this option would be + /// Some(closure_def_id). + /// Otherwise, the value of the optional DefId will be None. + ForLet(Option), /// If we have an index expression like /// @@ -1629,7 +1628,9 @@ impl Debug for Statement<'_> { use self::StatementKind::*; match self.kind { Assign(box (ref place, ref rv)) => write!(fmt, "{:?} = {:?}", place, rv), - FakeRead(ref cause, ref place) => write!(fmt, "FakeRead({:?}, {:?})", cause, place), + FakeRead(box (ref cause, ref place)) => { + write!(fmt, "FakeRead({:?}, {:?})", cause, place) + } Retag(ref kind, ref place) => write!( fmt, "Retag({}{:?})", @@ -1659,6 +1660,13 @@ impl Debug for Statement<'_> { write!(fmt, "Coverage::{:?}", coverage.kind) } } + CopyNonOverlapping(box crate::mir::CopyNonOverlapping { + ref src, + ref dst, + ref count, + }) => { + write!(fmt, "copy_nonoverlapping(src={:?}, dst={:?}, count={:?})", src, dst, count) + } Nop => write!(fmt, "nop"), } } @@ -1670,6 +1678,14 @@ pub struct Coverage { pub code_region: Option, } +#[derive(Clone, Debug, PartialEq, TyEncodable, TyDecodable, Hash, HashStable, TypeFoldable)] +pub struct CopyNonOverlapping<'tcx> { + pub src: Operand<'tcx>, + pub dst: Operand<'tcx>, + /// Number of elements to copy from src to dest, not bytes. + pub count: Operand<'tcx>, +} + /////////////////////////////////////////////////////////////////////////// // Places @@ -1683,6 +1699,9 @@ pub struct Place<'tcx> { pub projection: &'tcx List>, } +#[cfg(target_arch = "x86_64")] +static_assert_size!(Place<'_>, 16); + #[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] #[derive(TyEncodable, TyDecodable, HashStable)] pub enum ProjectionElem { @@ -1752,7 +1771,7 @@ impl ProjectionElem { pub type PlaceElem<'tcx> = ProjectionElem>; // At least on 64 bit systems, `PlaceElem` should not be larger than two pointers. -#[cfg(target_arch = "x86_64")] +#[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))] static_assert_size!(PlaceElem<'_>, 24); /// Alias for projections as they appear in `UserTypeProjection`, where we @@ -1981,6 +2000,9 @@ pub enum Operand<'tcx> { Constant(Box>), } +#[cfg(target_arch = "x86_64")] +static_assert_size!(Operand<'_>, 24); + impl<'tcx> Debug for Operand<'tcx> { fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result { use self::Operand::*; @@ -2006,7 +2028,7 @@ impl<'tcx> Operand<'tcx> { Operand::Constant(box Constant { span, user_ty: None, - literal: ty::Const::zero_sized(tcx, ty), + literal: ConstantKind::Ty(ty::Const::zero_sized(tcx, ty)), }) } @@ -2037,7 +2059,7 @@ impl<'tcx> Operand<'tcx> { Operand::Constant(box Constant { span, user_ty: None, - literal: ty::Const::from_scalar(tcx, val, ty), + literal: ConstantKind::Val(val.into(), ty), }) } @@ -2096,8 +2118,8 @@ pub enum Rvalue<'tcx> { Cast(CastKind, Operand<'tcx>, Ty<'tcx>), - BinaryOp(BinOp, Operand<'tcx>, Operand<'tcx>), - CheckedBinaryOp(BinOp, Operand<'tcx>, Operand<'tcx>), + BinaryOp(BinOp, Box<(Operand<'tcx>, Operand<'tcx>)>), + CheckedBinaryOp(BinOp, Box<(Operand<'tcx>, Operand<'tcx>)>), NullaryOp(NullOp, Ty<'tcx>), UnaryOp(UnOp, Operand<'tcx>), @@ -2116,6 +2138,9 @@ pub enum Rvalue<'tcx> { Aggregate(Box>, Vec>), } +#[cfg(target_arch = "x86_64")] +static_assert_size!(Rvalue<'_>, 40); + #[derive(Clone, Copy, Debug, PartialEq, Eq, TyEncodable, TyDecodable, Hash, HashStable)] pub enum CastKind { Misc, @@ -2139,6 +2164,9 @@ pub enum AggregateKind<'tcx> { Generator(DefId, SubstsRef<'tcx>, hir::Movability), } +#[cfg(target_arch = "x86_64")] +static_assert_size!(AggregateKind<'_>, 48); + #[derive(Copy, Clone, Debug, PartialEq, PartialOrd, Eq, TyEncodable, TyDecodable, Hash, HashStable)] pub enum BinOp { /// The `+` operator (addition) @@ -2215,8 +2243,8 @@ impl<'tcx> Debug for Rvalue<'tcx> { Cast(ref kind, ref place, ref ty) => { write!(fmt, "{:?} as {:?} ({:?})", place, ty, kind) } - BinaryOp(ref op, ref a, ref b) => write!(fmt, "{:?}({:?}, {:?})", op, a, b), - CheckedBinaryOp(ref op, ref a, ref b) => { + BinaryOp(ref op, box (ref a, ref b)) => write!(fmt, "{:?}({:?}, {:?})", op, a, b), + CheckedBinaryOp(ref op, box (ref a, ref b)) => { write!(fmt, "Checked{:?}({:?}, {:?})", op, a, b) } UnaryOp(ref op, ref a) => write!(fmt, "{:?}({:?})", op, a), @@ -2295,7 +2323,7 @@ impl<'tcx> Debug for Rvalue<'tcx> { CtorKind::Fn => fmt_tuple(fmt, &name), CtorKind::Fictive => { let mut struct_fmt = fmt.debug_struct(&name); - for (field, place) in variant_def.fields.iter().zip(places) { + for (field, place) in iter::zip(&variant_def.fields, places) { struct_fmt.field(&field.ident.as_str(), place); } struct_fmt.finish() @@ -2319,7 +2347,7 @@ impl<'tcx> Debug for Rvalue<'tcx> { let mut struct_fmt = fmt.debug_struct(&name); if let Some(upvars) = tcx.upvars_mentioned(def_id) { - for (&var_id, place) in upvars.keys().zip(places) { + for (&var_id, place) in iter::zip(upvars.keys(), places) { let var_name = tcx.hir().name(var_id); struct_fmt.field(&var_name.as_str(), place); } @@ -2338,7 +2366,7 @@ impl<'tcx> Debug for Rvalue<'tcx> { let mut struct_fmt = fmt.debug_struct(&name); if let Some(upvars) = tcx.upvars_mentioned(def_id) { - for (&var_id, place) in upvars.keys().zip(places) { + for (&var_id, place) in iter::zip(upvars.keys(), places) { let var_name = tcx.hir().name(var_id); struct_fmt.field(&var_name.as_str(), place); } @@ -2373,12 +2401,22 @@ pub struct Constant<'tcx> { /// Needed for NLL to impose user-given type constraints. pub user_ty: Option, - pub literal: &'tcx ty::Const<'tcx>, + pub literal: ConstantKind<'tcx>, +} + +#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, TyEncodable, TyDecodable, Hash, HashStable, Debug)] +#[derive(Lift)] +pub enum ConstantKind<'tcx> { + /// This constant came from the type system + Ty(&'tcx ty::Const<'tcx>), + /// This constant cannot go back into the type system, as it represents + /// something the type system cannot handle (e.g. pointers). + Val(interpret::ConstValue<'tcx>, Ty<'tcx>), } impl Constant<'tcx> { pub fn check_static_ptr(&self, tcx: TyCtxt<'_>) -> Option { - match self.literal.val.try_to_scalar() { + match self.literal.const_for_ty()?.val.try_to_scalar() { Some(Scalar::Ptr(ptr)) => match tcx.global_alloc(ptr.alloc_id) { GlobalAlloc::Static(def_id) => { assert!(!tcx.is_thread_local_static(def_id)); @@ -2389,6 +2427,94 @@ impl Constant<'tcx> { _ => None, } } + pub fn ty(&self) -> Ty<'tcx> { + self.literal.ty() + } +} + +impl From<&'tcx ty::Const<'tcx>> for ConstantKind<'tcx> { + fn from(ct: &'tcx ty::Const<'tcx>) -> Self { + Self::Ty(ct) + } +} + +impl ConstantKind<'tcx> { + /// Returns `None` if the constant is not trivially safe for use in the type system. + pub fn const_for_ty(&self) -> Option<&'tcx ty::Const<'tcx>> { + match self { + ConstantKind::Ty(c) => Some(c), + ConstantKind::Val(..) => None, + } + } + + pub fn ty(&self) -> Ty<'tcx> { + match self { + ConstantKind::Ty(c) => c.ty, + ConstantKind::Val(_, ty) => ty, + } + } + + #[inline] + pub fn try_to_value(self) -> Option> { + match self { + ConstantKind::Ty(c) => c.val.try_to_value(), + ConstantKind::Val(val, _) => Some(val), + } + } + + #[inline] + pub fn try_to_scalar(self) -> Option { + self.try_to_value()?.try_to_scalar() + } + + #[inline] + pub fn try_to_scalar_int(self) -> Option { + Some(self.try_to_value()?.try_to_scalar()?.assert_int()) + } + + #[inline] + pub fn try_to_bits(self, size: Size) -> Option { + self.try_to_scalar_int()?.to_bits(size).ok() + } + + #[inline] + pub fn try_to_bool(self) -> Option { + self.try_to_scalar_int()?.try_into().ok() + } + + #[inline] + pub fn try_eval_bits( + &self, + tcx: TyCtxt<'tcx>, + param_env: ty::ParamEnv<'tcx>, + ty: Ty<'tcx>, + ) -> Option { + match self { + Self::Ty(ct) => ct.try_eval_bits(tcx, param_env, ty), + Self::Val(val, t) => { + assert_eq!(*t, ty); + let size = + tcx.layout_of(param_env.with_reveal_all_normalized(tcx).and(ty)).ok()?.size; + val.try_to_bits(size) + } + } + } + + #[inline] + pub fn try_eval_bool(&self, tcx: TyCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>) -> Option { + match self { + Self::Ty(ct) => ct.try_eval_bool(tcx, param_env), + Self::Val(val, _) => val.try_to_bool(), + } + } + + #[inline] + pub fn try_eval_usize(&self, tcx: TyCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>) -> Option { + match self { + Self::Ty(ct) => ct.try_eval_usize(tcx, param_env), + Self::Val(val, _) => val.try_to_machine_usize(tcx), + } + } } /// A collection of projections into user types. @@ -2574,11 +2700,20 @@ impl<'tcx> Debug for Constant<'tcx> { impl<'tcx> Display for Constant<'tcx> { fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result { - match self.literal.ty.kind() { + match self.ty().kind() { ty::FnDef(..) => {} _ => write!(fmt, "const ")?, } - pretty_print_const(self.literal, fmt, true) + Display::fmt(&self.literal, fmt) + } +} + +impl<'tcx> Display for ConstantKind<'tcx> { + fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result { + match *self { + ConstantKind::Ty(c) => pretty_print_const(c, fmt, true), + ConstantKind::Val(val, ty) => pretty_print_const_value(val, ty, fmt, true), + } } } @@ -2597,6 +2732,23 @@ fn pretty_print_const( }) } +fn pretty_print_const_value( + val: interpret::ConstValue<'tcx>, + ty: Ty<'tcx>, + fmt: &mut Formatter<'_>, + print_types: bool, +) -> fmt::Result { + use crate::ty::print::PrettyPrinter; + ty::tls::with(|tcx| { + let val = tcx.lift(val).unwrap(); + let ty = tcx.lift(ty).unwrap(); + let mut cx = FmtPrinter::new(tcx, fmt, Namespace::ValueNS); + cx.print_alloc_ids = true; + cx.pretty_print_const_value(val, ty, print_types)?; + Ok(()) + }) +} + impl<'tcx> graph::DirectedGraph for Body<'tcx> { type Node = BasicBlock; } diff --git a/compiler/rustc_middle/src/mir/mono.rs b/compiler/rustc_middle/src/mir/mono.rs index 6c2468b9ffe0b..92a1094bbcdc1 100644 --- a/compiler/rustc_middle/src/mir/mono.rs +++ b/compiler/rustc_middle/src/mir/mono.rs @@ -181,6 +181,11 @@ impl<'tcx> MonoItem<'tcx> { } .map(|hir_id| tcx.hir().span(hir_id)) } + + // Only used by rustc_codegen_cranelift + pub fn codegen_dep_node(&self, tcx: TyCtxt<'tcx>) -> DepNode { + crate::dep_graph::make_compile_mono_item(tcx, self) + } } impl<'a, 'tcx> HashStable> for MonoItem<'tcx> { diff --git a/compiler/rustc_middle/src/mir/query.rs b/compiler/rustc_middle/src/mir/query.rs index c293fbe4ef8ca..ad3baccf15496 100644 --- a/compiler/rustc_middle/src/mir/query.rs +++ b/compiler/rustc_middle/src/mir/query.rs @@ -23,17 +23,9 @@ pub enum UnsafetyViolationKind { General, /// Permitted both in `const fn`s and regular `fn`s. GeneralAndConstFn, - /// Borrow of packed field. - /// Has to be handled as a lint for backwards compatibility. - BorrowPacked, /// Unsafe operation in an `unsafe fn` but outside an `unsafe` block. /// Has to be handled as a lint for backwards compatibility. - /// Should stay gated under `#![feature(unsafe_block_in_unsafe_fn)]`. UnsafeFn, - /// Borrow of packed field in an `unsafe fn` but outside an `unsafe` block. - /// Has to be handled as a lint for backwards compatibility. - /// Should stay gated under `#![feature(unsafe_block_in_unsafe_fn)]`. - UnsafeFnBorrowPacked, } #[derive(Copy, Clone, PartialEq, TyEncodable, TyDecodable, HashStable, Debug)] @@ -438,18 +430,6 @@ impl<'tcx> TyCtxt<'tcx> { } } - #[inline] - pub fn optimized_mir_or_const_arg_mir( - self, - def: ty::WithOptConstParam, - ) -> &'tcx Body<'tcx> { - if let Some((did, param_did)) = def.as_const_arg() { - self.mir_for_ctfe_of_const_arg((did, param_did)) - } else { - self.optimized_mir(def.did) - } - } - #[inline] pub fn mir_for_ctfe_opt_const_arg(self, def: ty::WithOptConstParam) -> &'tcx Body<'tcx> { if let Some((did, param_did)) = def.as_const_arg() { diff --git a/compiler/rustc_middle/src/mir/tcx.rs b/compiler/rustc_middle/src/mir/tcx.rs index 92f46ef6a638c..6e81914597698 100644 --- a/compiler/rustc_middle/src/mir/tcx.rs +++ b/compiler/rustc_middle/src/mir/tcx.rs @@ -17,7 +17,7 @@ pub struct PlaceTy<'tcx> { } // At least on 64 bit systems, `PlaceTy` should not be larger than two or three pointers. -#[cfg(target_arch = "x86_64")] +#[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))] static_assert_size!(PlaceTy<'_>, 16); impl<'tcx> PlaceTy<'tcx> { @@ -182,12 +182,12 @@ impl<'tcx> Rvalue<'tcx> { } Rvalue::Len(..) => tcx.types.usize, Rvalue::Cast(.., ty) => ty, - Rvalue::BinaryOp(op, ref lhs, ref rhs) => { + Rvalue::BinaryOp(op, box (ref lhs, ref rhs)) => { let lhs_ty = lhs.ty(local_decls, tcx); let rhs_ty = rhs.ty(local_decls, tcx); op.ty(tcx, lhs_ty, rhs_ty) } - Rvalue::CheckedBinaryOp(op, ref lhs, ref rhs) => { + Rvalue::CheckedBinaryOp(op, box (ref lhs, ref rhs)) => { let lhs_ty = lhs.ty(local_decls, tcx); let rhs_ty = rhs.ty(local_decls, tcx); let ty = op.ty(tcx, lhs_ty, rhs_ty); @@ -227,7 +227,7 @@ impl<'tcx> Operand<'tcx> { { match self { &Operand::Copy(ref l) | &Operand::Move(ref l) => l.ty(local_decls, tcx).ty, - &Operand::Constant(ref c) => c.literal.ty, + &Operand::Constant(ref c) => c.literal.ty(), } } } diff --git a/compiler/rustc_middle/src/mir/terminator.rs b/compiler/rustc_middle/src/mir/terminator.rs index 887dbefa9f2da..c8db4aeb449b8 100644 --- a/compiler/rustc_middle/src/mir/terminator.rs +++ b/compiler/rustc_middle/src/mir/terminator.rs @@ -67,7 +67,7 @@ impl SwitchTargets { /// /// Note that this may yield 0 elements. Only the `otherwise` branch is mandatory. pub fn iter(&self) -> SwitchTargetsIter<'_> { - SwitchTargetsIter { inner: self.values.iter().zip(self.targets.iter()) } + SwitchTargetsIter { inner: iter::zip(&self.values, &self.targets) } } /// Returns a slice with all possible jump targets (including the fallback target). diff --git a/compiler/rustc_middle/src/mir/traversal.rs b/compiler/rustc_middle/src/mir/traversal.rs index 36e277d1a88f3..725448584dd29 100644 --- a/compiler/rustc_middle/src/mir/traversal.rs +++ b/compiler/rustc_middle/src/mir/traversal.rs @@ -264,10 +264,6 @@ impl<'a, 'tcx> ReversePostorder<'a, 'tcx> { ReversePostorder { body, blocks, idx: len } } - - pub fn reset(&mut self) { - self.idx = self.blocks.len(); - } } pub fn reverse_postorder<'a, 'tcx>(body: &'a Body<'tcx>) -> ReversePostorder<'a, 'tcx> { diff --git a/compiler/rustc_middle/src/mir/type_foldable.rs b/compiler/rustc_middle/src/mir/type_foldable.rs index da8e189ba9d40..f3124e5bf424e 100644 --- a/compiler/rustc_middle/src/mir/type_foldable.rs +++ b/compiler/rustc_middle/src/mir/type_foldable.rs @@ -181,9 +181,11 @@ impl<'tcx> TypeFoldable<'tcx> for Rvalue<'tcx> { AddressOf(mutability, place) => AddressOf(mutability, place.fold_with(folder)), Len(place) => Len(place.fold_with(folder)), Cast(kind, op, ty) => Cast(kind, op.fold_with(folder), ty.fold_with(folder)), - BinaryOp(op, rhs, lhs) => BinaryOp(op, rhs.fold_with(folder), lhs.fold_with(folder)), - CheckedBinaryOp(op, rhs, lhs) => { - CheckedBinaryOp(op, rhs.fold_with(folder), lhs.fold_with(folder)) + BinaryOp(op, box (rhs, lhs)) => { + BinaryOp(op, box (rhs.fold_with(folder), lhs.fold_with(folder))) + } + CheckedBinaryOp(op, box (rhs, lhs)) => { + CheckedBinaryOp(op, box (rhs.fold_with(folder), lhs.fold_with(folder))) } UnaryOp(op, val) => UnaryOp(op, val.fold_with(folder)), Discriminant(place) => Discriminant(place.fold_with(folder)), @@ -227,7 +229,7 @@ impl<'tcx> TypeFoldable<'tcx> for Rvalue<'tcx> { op.visit_with(visitor)?; ty.visit_with(visitor) } - BinaryOp(_, ref rhs, ref lhs) | CheckedBinaryOp(_, ref rhs, ref lhs) => { + BinaryOp(_, box (ref rhs, ref lhs)) | CheckedBinaryOp(_, box (ref rhs, ref lhs)) => { rhs.visit_with(visitor)?; lhs.visit_with(visitor) } @@ -340,6 +342,28 @@ impl<'tcx> TypeFoldable<'tcx> for Constant<'tcx> { } } fn super_visit_with>(&self, visitor: &mut V) -> ControlFlow { - self.literal.visit_with(visitor) + self.literal.visit_with(visitor)?; + self.user_ty.visit_with(visitor) + } +} + +impl<'tcx> TypeFoldable<'tcx> for ConstantKind<'tcx> { + #[inline(always)] + fn fold_with>(self, folder: &mut F) -> Self { + folder.fold_mir_const(self) + } + + fn super_fold_with>(self, folder: &mut F) -> Self { + match self { + ConstantKind::Ty(c) => ConstantKind::Ty(c.fold_with(folder)), + ConstantKind::Val(v, t) => ConstantKind::Val(v, t.fold_with(folder)), + } + } + + fn super_visit_with>(&self, visitor: &mut V) -> ControlFlow { + match *self { + ConstantKind::Ty(c) => c.visit_with(visitor), + ConstantKind::Val(_, t) => t.visit_with(visitor), + } } } diff --git a/compiler/rustc_middle/src/mir/visit.rs b/compiler/rustc_middle/src/mir/visit.rs index 3e92844c4f94b..5516a045c1db0 100644 --- a/compiler/rustc_middle/src/mir/visit.rs +++ b/compiler/rustc_middle/src/mir/visit.rs @@ -380,7 +380,7 @@ macro_rules! make_mir_visitor { ) => { self.visit_assign(place, rvalue, location); } - StatementKind::FakeRead(_, place) => { + StatementKind::FakeRead(box (_, place)) => { self.visit_place( place, PlaceContext::NonMutatingUse(NonMutatingUseContext::Inspect), @@ -436,6 +436,15 @@ macro_rules! make_mir_visitor { location ) } + StatementKind::CopyNonOverlapping(box crate::mir::CopyNonOverlapping{ + ref $($mutability)? src, + ref $($mutability)? dst, + ref $($mutability)? count, + }) => { + self.visit_operand(src, location); + self.visit_operand(dst, location); + self.visit_operand(count, location) + } StatementKind::Nop => {} } } @@ -575,8 +584,7 @@ macro_rules! make_mir_visitor { } => { for op in operands { match op { - InlineAsmOperand::In { value, .. } - | InlineAsmOperand::Const { value } => { + InlineAsmOperand::In { value, .. } => { self.visit_operand(value, location); } InlineAsmOperand::Out { place, .. } => { @@ -598,7 +606,8 @@ macro_rules! make_mir_visitor { ); } } - InlineAsmOperand::SymFn { value } => { + InlineAsmOperand::Const { value } + | InlineAsmOperand::SymFn { value } => { self.visit_constant(value, location); } InlineAsmOperand::SymStatic { def_id: _ } => {} @@ -687,8 +696,8 @@ macro_rules! make_mir_visitor { self.visit_ty(ty, TyContext::Location(location)); } - Rvalue::BinaryOp(_bin_op, lhs, rhs) - | Rvalue::CheckedBinaryOp(_bin_op, lhs, rhs) => { + Rvalue::BinaryOp(_bin_op, box(lhs, rhs)) + | Rvalue::CheckedBinaryOp(_bin_op, box(lhs, rhs)) => { self.visit_operand(lhs, location); self.visit_operand(rhs, location); } @@ -862,7 +871,10 @@ macro_rules! make_mir_visitor { self.visit_span(span); drop(user_ty); // no visit method for this - self.visit_const(literal, location); + match literal { + ConstantKind::Ty(ct) => self.visit_const(ct, location), + ConstantKind::Val(_, t) => self.visit_ty(t, TyContext::Location(location)), + } } fn super_span(&mut self, _span: & $($mutability)? Span) { @@ -1235,12 +1247,6 @@ impl PlaceContext { matches!(self, PlaceContext::MutatingUse(..)) } - /// Returns `true` if this place context represents a use that does not change the value. - #[inline] - pub fn is_nonmutating_use(&self) -> bool { - matches!(self, PlaceContext::NonMutatingUse(..)) - } - /// Returns `true` if this place context represents a use. #[inline] pub fn is_use(&self) -> bool { diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs index a8600af1de28e..08fa12aa3718f 100644 --- a/compiler/rustc_middle/src/query/mod.rs +++ b/compiler/rustc_middle/src/query/mod.rs @@ -61,6 +61,15 @@ rustc_queries! { desc { |tcx| "HIR owner items in `{}`", tcx.def_path_str(key.to_def_id()) } } + /// Gives access to the HIR attributes inside the HIR owner `key`. + /// + /// This can be conveniently accessed by methods on `tcx.hir()`. + /// Avoid calling this query directly. + query hir_attrs(key: LocalDefId) -> rustc_middle::hir::AttributeMap<'tcx> { + eval_always + desc { |tcx| "HIR owner attributes in `{}`", tcx.def_path_str(key.to_def_id()) } + } + /// Computes the `DefId` of the corresponding const parameter in case the `key` is a /// const argument and returns `None` otherwise. /// @@ -84,6 +93,12 @@ rustc_queries! { desc { |tcx| "computing the optional const parameter of `{}`", tcx.def_path_str(key.to_def_id()) } } + /// Given the def_id of a const-generic parameter, computes the associated default const + /// parameter. e.g. `fn example` called on `N` would return `3`. + query const_param_default(param: DefId) -> &'tcx ty::Const<'tcx> { + desc { |tcx| "compute const default for a given parameter `{}`", tcx.def_path_str(param) } + } + /// Records the type of every item. query type_of(key: DefId) -> Ty<'tcx> { desc { |tcx| "computing type of `{}`", tcx.def_path_str(key) } @@ -319,7 +334,10 @@ rustc_queries! { /// Returns the name of the file that contains the function body, if instrumented for coverage. query covered_file_name(key: DefId) -> Option { - desc { |tcx| "retrieving the covered file name, if instrumented, for `{}`", tcx.def_path_str(key) } + 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() } } @@ -327,7 +345,10 @@ rustc_queries! { /// 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> { - desc { |tcx| "retrieving the covered `CodeRegion`s, if instrumented, for `{}`", tcx.def_path_str(key) } + desc { + |tcx| "retrieving the covered `CodeRegion`s, if instrumented, for `{}`", + tcx.def_path_str(key) + } storage(ArenaCacheSelector<'tcx>) cache_on_disk_if { key.is_local() } } @@ -543,7 +564,7 @@ rustc_queries! { } /// Collects the associated items defined on a trait or impl. - query associated_items(key: DefId) -> ty::AssociatedItems<'tcx> { + query associated_items(key: DefId) -> ty::AssocItems<'tcx> { storage(ArenaCacheSelector<'tcx>) desc { |tcx| "collecting associated items of {}", tcx.def_path_str(key) } } @@ -776,6 +797,14 @@ rustc_queries! { cache_on_disk_if { true } } + /// Convert an evaluated constant to a type level constant or + /// return `None` if that is not possible. + query const_to_valtree( + key: ty::ParamEnvAnd<'tcx, ConstAlloc<'tcx>> + ) -> Option> { + desc { "destructure constant" } + } + /// Destructure a constant ADT or array into its variant index and its /// field values. query destructure_const( @@ -976,6 +1005,10 @@ rustc_queries! { query is_freeze_raw(env: ty::ParamEnvAnd<'tcx, Ty<'tcx>>) -> bool { desc { "computing whether `{}` is freeze", env.value } } + /// Query backing `TyS::is_unpin`. + query is_unpin_raw(env: ty::ParamEnvAnd<'tcx, Ty<'tcx>>) -> bool { + desc { "computing whether `{}` is `Unpin`", env.value } + } /// Query backing `TyS::needs_drop`. query needs_drop_raw(env: ty::ParamEnvAnd<'tcx, Ty<'tcx>>) -> bool { desc { "computing whether `{}` needs drop", env.value } @@ -1022,6 +1055,8 @@ rustc_queries! { desc { "checking if the crate is_compiler_builtins" } } query has_global_allocator(_: CrateNum) -> bool { + // This query depends on untracked global state in CStore + eval_always fatal_cycle desc { "checking if the crate has_global_allocator" } } @@ -1220,13 +1255,19 @@ rustc_queries! { desc { |tcx| "native_library_kind({})", tcx.def_path_str(def_id) } } - query link_args(_: CrateNum) -> Lrc> { - eval_always - desc { "looking up link arguments for a crate" } + /// Does lifetime resolution, but does not descend into trait items. This + /// should only be used for resolving lifetimes of on trait definitions, + /// and is used to avoid cycles. Importantly, `resolve_lifetimes` still visits + /// the same lifetimes and is responsible for diagnostics. + /// See `rustc_resolve::late::lifetimes for details. + query resolve_lifetimes_trait_definition(_: LocalDefId) -> ResolveLifetimes { + storage(ArenaCacheSelector<'tcx>) + desc { "resolving lifetimes for a trait definition" } } - - /// Lifetime resolution. See `middle::resolve_lifetimes`. - query resolve_lifetimes(_: CrateNum) -> ResolveLifetimes { + /// Does lifetime resolution on items. Importantly, we can't resolve + /// lifetimes directly on things like trait methods, because of trait params. + /// See `rustc_resolve::late::lifetimes for details. + query resolve_lifetimes(_: LocalDefId) -> ResolveLifetimes { storage(ArenaCacheSelector<'tcx>) desc { "resolving lifetimes" } } @@ -1238,9 +1279,17 @@ rustc_queries! { Option<(LocalDefId, &'tcx FxHashSet)> { desc { "testing if a region is late bound" } } + /// For a given item (like a struct), gets the default lifetimes to be used + /// for each parameter if a trait object were to be passed for that parameter. + /// For example, for `struct Foo<'a, T, U>`, this would be `['static, 'static]`. + /// For `struct Foo<'a, T: 'a, U>`, this would instead be `['a, 'static]`. query object_lifetime_defaults_map(_: LocalDefId) - -> Option<&'tcx FxHashMap>> { - desc { "looking up lifetime defaults for a region" } + -> Option> { + desc { "looking up lifetime defaults for a region on an item" } + } + query late_bound_vars_map(_: LocalDefId) + -> Option<&'tcx FxHashMap>> { + desc { "looking up late bound vars" } } query visibility(def_id: DefId) -> ty::Visibility { @@ -1269,6 +1318,8 @@ rustc_queries! { desc { |tcx| "collecting child items of `{}`", tcx.def_path_str(def_id) } } query extern_mod_stmt_cnum(def_id: LocalDefId) -> Option { + // This depends on untracked global state (`tcx.extern_crate_map`) + eval_always desc { |tcx| "computing crate imported by `{}`", tcx.def_path_str(def_id.to_def_id()) } } @@ -1388,6 +1439,14 @@ rustc_queries! { query is_codegened_item(def_id: DefId) -> bool { desc { |tcx| "determining whether `{}` needs codegen", tcx.def_path_str(def_id) } } + + /// All items participating in code generation together with items inlined into them. + query codegened_and_inlined_items(_: CrateNum) + -> &'tcx DefIdSet { + eval_always + desc { "codegened_and_inlined_items" } + } + query codegen_unit(_: Symbol) -> &'tcx CodegenUnit<'tcx> { desc { "codegen_unit" } } @@ -1424,6 +1483,13 @@ rustc_queries! { desc { "normalizing `{}`", goal.value } } + /// Do not call this query directly: invoke `normalize_erasing_regions` instead. + query normalize_mir_const_after_erasing_regions( + goal: ParamEnvAnd<'tcx, mir::ConstantKind<'tcx>> + ) -> mir::ConstantKind<'tcx> { + desc { "normalizing `{}`", goal.value } + } + query implied_outlives_bounds( goal: CanonicalTyGoal<'tcx> ) -> Result< diff --git a/compiler/rustc_middle/src/traits/mod.rs b/compiler/rustc_middle/src/traits/mod.rs index 9deeaf462d65d..00dec3b355f8f 100644 --- a/compiler/rustc_middle/src/traits/mod.rs +++ b/compiler/rustc_middle/src/traits/mod.rs @@ -9,7 +9,7 @@ pub mod specialization_graph; mod structural_impls; use crate::infer::canonical::Canonical; -use crate::mir::interpret::ErrorHandled; +use crate::mir::abstract_const::NotConstEvaluatable; use crate::ty::subst::SubstsRef; use crate::ty::{self, AdtKind, Ty, TyCtxt}; @@ -323,6 +323,9 @@ pub enum ObligationCauseCode<'tcx> { /// #[feature(trivial_bounds)] is not enabled TrivialBound, + + /// If `X` is the concrete type of an opaque type `impl Y`, then `X` must implement `Y` + OpaqueType, } impl ObligationCauseCode<'_> { @@ -340,8 +343,8 @@ impl ObligationCauseCode<'_> { } // `ObligationCauseCode` is used a lot. Make sure it doesn't unintentionally get bigger. -#[cfg(target_arch = "x86_64")] -static_assert_size!(ObligationCauseCode<'_>, 32); +#[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))] +static_assert_size!(ObligationCauseCode<'_>, 40); #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] pub enum StatementAsExpression { @@ -398,7 +401,7 @@ pub enum SelectionError<'tcx> { ty::error::TypeError<'tcx>, ), TraitNotObjectSafe(DefId), - ConstEvalFailure(ErrorHandled), + NotConstEvaluatable(NotConstEvaluatable), Overflow, } diff --git a/compiler/rustc_middle/src/traits/query.rs b/compiler/rustc_middle/src/traits/query.rs index f9cadb3bb2dbc..b0ab0c9ae52ce 100644 --- a/compiler/rustc_middle/src/traits/query.rs +++ b/compiler/rustc_middle/src/traits/query.rs @@ -3,7 +3,7 @@ //! which makes a canonical query by replacing unbound inference //! variables and regions, so that results can be reused more broadly. //! The providers for the queries defined here can be found in -//! `librustc_traits`. +//! `rustc_traits`. use crate::ich::StableHashingContext; use crate::infer::canonical::{Canonical, QueryResponse}; @@ -44,24 +44,12 @@ pub mod type_op { pub b: Ty<'tcx>, } - impl<'tcx> Eq<'tcx> { - pub fn new(a: Ty<'tcx>, b: Ty<'tcx>) -> Self { - Self { a, b } - } - } - #[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, HashStable, TypeFoldable, Lift)] pub struct Subtype<'tcx> { pub sub: Ty<'tcx>, pub sup: Ty<'tcx>, } - impl<'tcx> Subtype<'tcx> { - pub fn new(sub: Ty<'tcx>, sup: Ty<'tcx>) -> Self { - Self { sub, sup } - } - } - #[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, HashStable, TypeFoldable, Lift)] pub struct ProvePredicate<'tcx> { pub predicate: Predicate<'tcx>, diff --git a/compiler/rustc_middle/src/ty/_match.rs b/compiler/rustc_middle/src/ty/_match.rs index a5962e3b3ba57..8e2c79701af90 100644 --- a/compiler/rustc_middle/src/ty/_match.rs +++ b/compiler/rustc_middle/src/ty/_match.rs @@ -112,9 +112,9 @@ impl TypeRelation<'tcx> for Match<'tcx> { fn binders( &mut self, - a: ty::Binder, - b: ty::Binder, - ) -> RelateResult<'tcx, ty::Binder> + a: ty::Binder<'tcx, T>, + b: ty::Binder<'tcx, T>, + ) -> RelateResult<'tcx, ty::Binder<'tcx, T>> where T: Relate<'tcx>, { diff --git a/compiler/rustc_middle/src/ty/adt.rs b/compiler/rustc_middle/src/ty/adt.rs new file mode 100644 index 0000000000000..95159ea46aec3 --- /dev/null +++ b/compiler/rustc_middle/src/ty/adt.rs @@ -0,0 +1,482 @@ +use crate::ich::StableHashingContext; +use crate::mir::interpret::ErrorHandled; +use crate::ty; +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::{HashStable, StableHasher}; +use rustc_errors::ErrorReported; +use rustc_hir::def::{DefKind, Res}; +use rustc_hir::def_id::DefId; +use rustc_index::vec::{Idx, IndexVec}; +use rustc_serialize::{self, Encodable, Encoder}; +use rustc_session::DataTypeKind; +use rustc_span::symbol::sym; +use rustc_target::abi::VariantIdx; + +use std::cell::RefCell; +use std::cmp::Ordering; +use std::hash::{Hash, Hasher}; +use std::ops::Range; +use std::{ptr, str}; + +use super::{ + Destructor, FieldDef, GenericPredicates, ReprOptions, Ty, TyCtxt, VariantDef, VariantDiscr, +}; + +#[derive(Clone, HashStable, Debug)] +pub struct AdtSizedConstraint<'tcx>(pub &'tcx [Ty<'tcx>]); + +bitflags! { + #[derive(HashStable)] + pub struct AdtFlags: u32 { + const NO_ADT_FLAGS = 0; + /// Indicates whether the ADT is an enum. + const IS_ENUM = 1 << 0; + /// Indicates whether the ADT is a union. + const IS_UNION = 1 << 1; + /// Indicates whether the ADT is a struct. + const IS_STRUCT = 1 << 2; + /// Indicates whether the ADT is a struct and has a constructor. + const HAS_CTOR = 1 << 3; + /// Indicates whether the type is `PhantomData`. + const IS_PHANTOM_DATA = 1 << 4; + /// Indicates whether the type has a `#[fundamental]` attribute. + const IS_FUNDAMENTAL = 1 << 5; + /// Indicates whether the type is `Box`. + const IS_BOX = 1 << 6; + /// Indicates whether the type is `ManuallyDrop`. + const IS_MANUALLY_DROP = 1 << 7; + /// Indicates whether the variant list of this ADT is `#[non_exhaustive]`. + /// (i.e., this flag is never set unless this ADT is an enum). + const IS_VARIANT_LIST_NON_EXHAUSTIVE = 1 << 8; + } +} + +/// The definition of a user-defined type, e.g., a `struct`, `enum`, or `union`. +/// +/// These are all interned (by `alloc_adt_def`) into the global arena. +/// +/// The initialism *ADT* stands for an [*algebraic data type (ADT)*][adt]. +/// This is slightly wrong because `union`s are not ADTs. +/// Moreover, Rust only allows recursive data types through indirection. +/// +/// [adt]: https://en.wikipedia.org/wiki/Algebraic_data_type +pub struct AdtDef { + /// The `DefId` of the struct, enum or union item. + pub did: DefId, + /// Variants of the ADT. If this is a struct or union, then there will be a single variant. + pub variants: IndexVec, + /// Flags of the ADT (e.g., is this a struct? is this non-exhaustive?). + flags: AdtFlags, + /// Repr options provided by the user. + pub repr: ReprOptions, +} + +impl PartialOrd for AdtDef { + fn partial_cmp(&self, other: &AdtDef) -> Option { + Some(self.cmp(&other)) + } +} + +/// There should be only one AdtDef for each `did`, therefore +/// it is fine to implement `Ord` only based on `did`. +impl Ord for AdtDef { + fn cmp(&self, other: &AdtDef) -> Ordering { + self.did.cmp(&other.did) + } +} + +impl PartialEq for AdtDef { + // `AdtDef`s are always interned, and this is part of `TyS` equality. + #[inline] + fn eq(&self, other: &Self) -> bool { + ptr::eq(self, other) + } +} + +impl Eq for AdtDef {} + +impl Hash for AdtDef { + #[inline] + fn hash(&self, s: &mut H) { + (self as *const AdtDef).hash(s) + } +} + +impl Encodable for AdtDef { + fn encode(&self, s: &mut S) -> Result<(), S::Error> { + self.did.encode(s) + } +} + +impl<'a> HashStable> for AdtDef { + fn hash_stable(&self, hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { + thread_local! { + static CACHE: RefCell> = 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 ty::AdtDef { did, ref variants, ref flags, ref repr } = *self; + + let mut hasher = StableHasher::new(); + did.hash_stable(hcx, &mut hasher); + variants.hash_stable(hcx, &mut hasher); + flags.hash_stable(hcx, &mut hasher); + repr.hash_stable(hcx, &mut hasher); + + hasher.finish() + }) + }); + + hash.hash_stable(hcx, hasher); + } +} + +#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)] +pub enum AdtKind { + Struct, + Union, + Enum, +} + +impl Into for AdtKind { + fn into(self) -> DataTypeKind { + match self { + AdtKind::Struct => DataTypeKind::Struct, + AdtKind::Union => DataTypeKind::Union, + AdtKind::Enum => DataTypeKind::Enum, + } + } +} + +impl<'tcx> AdtDef { + /// Creates a new `AdtDef`. + pub(super) fn new( + tcx: TyCtxt<'_>, + did: DefId, + kind: AdtKind, + variants: IndexVec, + repr: ReprOptions, + ) -> Self { + debug!("AdtDef::new({:?}, {:?}, {:?}, {:?})", did, kind, variants, repr); + let mut flags = AdtFlags::NO_ADT_FLAGS; + + if kind == AdtKind::Enum && tcx.has_attr(did, sym::non_exhaustive) { + debug!("found non-exhaustive variant list for {:?}", did); + flags = flags | AdtFlags::IS_VARIANT_LIST_NON_EXHAUSTIVE; + } + + flags |= match kind { + AdtKind::Enum => AdtFlags::IS_ENUM, + AdtKind::Union => AdtFlags::IS_UNION, + AdtKind::Struct => AdtFlags::IS_STRUCT, + }; + + if kind == AdtKind::Struct && variants[VariantIdx::new(0)].ctor_def_id.is_some() { + flags |= AdtFlags::HAS_CTOR; + } + + let attrs = tcx.get_attrs(did); + if tcx.sess.contains_name(&attrs, sym::fundamental) { + flags |= AdtFlags::IS_FUNDAMENTAL; + } + if Some(did) == tcx.lang_items().phantom_data() { + flags |= AdtFlags::IS_PHANTOM_DATA; + } + if Some(did) == tcx.lang_items().owned_box() { + flags |= AdtFlags::IS_BOX; + } + if Some(did) == tcx.lang_items().manually_drop() { + flags |= AdtFlags::IS_MANUALLY_DROP; + } + + AdtDef { did, variants, flags, repr } + } + + /// Returns `true` if this is a struct. + #[inline] + pub fn is_struct(&self) -> bool { + self.flags.contains(AdtFlags::IS_STRUCT) + } + + /// Returns `true` if this is a union. + #[inline] + pub fn is_union(&self) -> bool { + self.flags.contains(AdtFlags::IS_UNION) + } + + /// Returns `true` if this is a enum. + #[inline] + pub fn is_enum(&self) -> bool { + self.flags.contains(AdtFlags::IS_ENUM) + } + + /// Returns `true` if the variant list of this ADT is `#[non_exhaustive]`. + #[inline] + pub fn is_variant_list_non_exhaustive(&self) -> bool { + self.flags.contains(AdtFlags::IS_VARIANT_LIST_NON_EXHAUSTIVE) + } + + /// Returns the kind of the ADT. + #[inline] + pub fn adt_kind(&self) -> AdtKind { + if self.is_enum() { + AdtKind::Enum + } else if self.is_union() { + AdtKind::Union + } else { + AdtKind::Struct + } + } + + /// Returns a description of this abstract data type. + pub fn descr(&self) -> &'static str { + match self.adt_kind() { + AdtKind::Struct => "struct", + AdtKind::Union => "union", + AdtKind::Enum => "enum", + } + } + + /// Returns a description of a variant of this abstract data type. + #[inline] + pub fn variant_descr(&self) -> &'static str { + match self.adt_kind() { + AdtKind::Struct => "struct", + AdtKind::Union => "union", + AdtKind::Enum => "variant", + } + } + + /// If this function returns `true`, it implies that `is_struct` must return `true`. + #[inline] + pub fn has_ctor(&self) -> bool { + self.flags.contains(AdtFlags::HAS_CTOR) + } + + /// Returns `true` if this type is `#[fundamental]` for the purposes + /// of coherence checking. + #[inline] + pub fn is_fundamental(&self) -> bool { + self.flags.contains(AdtFlags::IS_FUNDAMENTAL) + } + + /// Returns `true` if this is `PhantomData`. + #[inline] + pub fn is_phantom_data(&self) -> bool { + self.flags.contains(AdtFlags::IS_PHANTOM_DATA) + } + + /// Returns `true` if this is Box. + #[inline] + pub fn is_box(&self) -> bool { + self.flags.contains(AdtFlags::IS_BOX) + } + + /// Returns `true` if this is `ManuallyDrop`. + #[inline] + pub fn is_manually_drop(&self) -> bool { + self.flags.contains(AdtFlags::IS_MANUALLY_DROP) + } + + /// Returns `true` if this type has a destructor. + pub fn has_dtor(&self, tcx: TyCtxt<'tcx>) -> bool { + self.destructor(tcx).is_some() + } + + /// Asserts this is a struct or union and returns its unique variant. + pub fn non_enum_variant(&self) -> &VariantDef { + assert!(self.is_struct() || self.is_union()); + &self.variants[VariantIdx::new(0)] + } + + #[inline] + pub fn predicates(&self, tcx: TyCtxt<'tcx>) -> GenericPredicates<'tcx> { + tcx.predicates_of(self.did) + } + + /// Returns an iterator over all fields contained + /// by this ADT. + #[inline] + pub fn all_fields(&self) -> impl Iterator + Clone { + self.variants.iter().flat_map(|v| v.fields.iter()) + } + + /// Whether the ADT lacks fields. Note that this includes uninhabited enums, + /// e.g., `enum Void {}` is considered payload free as well. + pub fn is_payloadfree(&self) -> bool { + self.variants.iter().all(|v| v.fields.is_empty()) + } + + /// Return a `VariantDef` given a variant id. + pub fn variant_with_id(&self, vid: DefId) -> &VariantDef { + self.variants.iter().find(|v| v.def_id == vid).expect("variant_with_id: unknown variant") + } + + /// Return a `VariantDef` given a constructor id. + pub fn variant_with_ctor_id(&self, cid: DefId) -> &VariantDef { + self.variants + .iter() + .find(|v| v.ctor_def_id == Some(cid)) + .expect("variant_with_ctor_id: unknown variant") + } + + /// Return the index of `VariantDef` given a variant id. + pub fn variant_index_with_id(&self, vid: DefId) -> VariantIdx { + self.variants + .iter_enumerated() + .find(|(_, v)| v.def_id == vid) + .expect("variant_index_with_id: unknown variant") + .0 + } + + /// Return the index of `VariantDef` given a constructor id. + pub fn variant_index_with_ctor_id(&self, cid: DefId) -> VariantIdx { + self.variants + .iter_enumerated() + .find(|(_, v)| v.ctor_def_id == Some(cid)) + .expect("variant_index_with_ctor_id: unknown variant") + .0 + } + + pub fn variant_of_res(&self, res: Res) -> &VariantDef { + match res { + Res::Def(DefKind::Variant, vid) => self.variant_with_id(vid), + Res::Def(DefKind::Ctor(..), cid) => self.variant_with_ctor_id(cid), + Res::Def(DefKind::Struct, _) + | Res::Def(DefKind::Union, _) + | Res::Def(DefKind::TyAlias, _) + | Res::Def(DefKind::AssocTy, _) + | Res::SelfTy(..) + | Res::SelfCtor(..) => self.non_enum_variant(), + _ => bug!("unexpected res {:?} in variant_of_res", res), + } + } + + #[inline] + pub fn eval_explicit_discr(&self, tcx: TyCtxt<'tcx>, expr_did: DefId) -> Option> { + assert!(self.is_enum()); + let param_env = tcx.param_env(expr_did); + let repr_type = self.repr.discr_type(); + match tcx.const_eval_poly(expr_did) { + Ok(val) => { + let ty = repr_type.to_ty(tcx); + if let Some(b) = val.try_to_bits_for_ty(tcx, param_env, ty) { + trace!("discriminants: {} ({:?})", b, repr_type); + Some(Discr { val: b, ty }) + } else { + info!("invalid enum discriminant: {:#?}", val); + crate::mir::interpret::struct_error( + tcx.at(tcx.def_span(expr_did)), + "constant evaluation of enum discriminant resulted in non-integer", + ) + .emit(); + None + } + } + Err(err) => { + let msg = match err { + ErrorHandled::Reported(ErrorReported) | ErrorHandled::Linted => { + "enum discriminant evaluation failed" + } + ErrorHandled::TooGeneric => "enum discriminant depends on generics", + }; + tcx.sess.delay_span_bug(tcx.def_span(expr_did), msg); + None + } + } + } + + #[inline] + pub fn discriminants( + &'tcx self, + tcx: TyCtxt<'tcx>, + ) -> impl Iterator)> + Captures<'tcx> { + assert!(self.is_enum()); + let repr_type = self.repr.discr_type(); + let initial = repr_type.initial_discriminant(tcx); + let mut prev_discr = None::>; + self.variants.iter_enumerated().map(move |(i, v)| { + let mut discr = prev_discr.map_or(initial, |d| d.wrap_incr(tcx)); + if let VariantDiscr::Explicit(expr_did) = v.discr { + if let Some(new_discr) = self.eval_explicit_discr(tcx, expr_did) { + discr = new_discr; + } + } + prev_discr = Some(discr); + + (i, discr) + }) + } + + #[inline] + pub fn variant_range(&self) -> Range { + VariantIdx::new(0)..VariantIdx::new(self.variants.len()) + } + + /// Computes the discriminant value used by a specific variant. + /// Unlike `discriminants`, this is (amortized) constant-time, + /// only doing at most one query for evaluating an explicit + /// discriminant (the last one before the requested variant), + /// assuming there are no constant-evaluation errors there. + #[inline] + pub fn discriminant_for_variant( + &self, + tcx: TyCtxt<'tcx>, + variant_index: VariantIdx, + ) -> Discr<'tcx> { + assert!(self.is_enum()); + let (val, offset) = self.discriminant_def_for_variant(variant_index); + let explicit_value = val + .and_then(|expr_did| self.eval_explicit_discr(tcx, expr_did)) + .unwrap_or_else(|| self.repr.discr_type().initial_discriminant(tcx)); + explicit_value.checked_add(tcx, offset as u128).0 + } + + /// Yields a `DefId` for the discriminant and an offset to add to it + /// Alternatively, if there is no explicit discriminant, returns the + /// inferred discriminant directly. + pub fn discriminant_def_for_variant(&self, variant_index: VariantIdx) -> (Option, u32) { + assert!(!self.variants.is_empty()); + let mut explicit_index = variant_index.as_u32(); + let expr_did; + loop { + match self.variants[VariantIdx::from_u32(explicit_index)].discr { + ty::VariantDiscr::Relative(0) => { + expr_did = None; + break; + } + ty::VariantDiscr::Relative(distance) => { + explicit_index -= distance; + } + ty::VariantDiscr::Explicit(did) => { + expr_did = Some(did); + break; + } + } + } + (expr_did, variant_index.as_u32() - explicit_index) + } + + pub fn destructor(&self, tcx: TyCtxt<'tcx>) -> Option { + tcx.adt_destructor(self.did) + } + + /// Returns a list of types such that `Self: Sized` if and only + /// if that type is `Sized`, or `TyErr` if this type is recursive. + /// + /// Oddly enough, checking that the sized-constraint is `Sized` is + /// actually more expressive than checking all members: + /// the `Sized` trait is inductive, so an associated type that references + /// `Self` would prevent its containing ADT from being `Sized`. + /// + /// Due to normalization being eager, this applies even if + /// the associated type is behind a pointer (e.g., issue #31299). + pub fn sized_constraint(&self, tcx: TyCtxt<'tcx>) -> &'tcx [Ty<'tcx>] { + tcx.adt_sized_constraint(self.did).0 + } +} diff --git a/compiler/rustc_middle/src/ty/assoc.rs b/compiler/rustc_middle/src/ty/assoc.rs new file mode 100644 index 0000000000000..d005f63ed4383 --- /dev/null +++ b/compiler/rustc_middle/src/ty/assoc.rs @@ -0,0 +1,170 @@ +pub use self::AssocItemContainer::*; + +use crate::ty; +use rustc_data_structures::sorted_map::SortedIndexMultiMap; +use rustc_hir as hir; +use rustc_hir::def::{DefKind, Namespace}; +use rustc_hir::def_id::DefId; +use rustc_span::symbol::{Ident, Symbol}; + +use super::{TyCtxt, Visibility}; + +#[derive(Clone, Copy, PartialEq, Eq, Debug, HashStable, Hash)] +pub enum AssocItemContainer { + TraitContainer(DefId), + ImplContainer(DefId), +} + +impl AssocItemContainer { + /// Asserts that this is the `DefId` of an associated item declared + /// in a trait, and returns the trait `DefId`. + pub fn assert_trait(&self) -> DefId { + match *self { + TraitContainer(id) => id, + _ => bug!("associated item has wrong container type: {:?}", self), + } + } + + pub fn id(&self) -> DefId { + match *self { + TraitContainer(id) => id, + ImplContainer(id) => id, + } + } +} + +#[derive(Copy, Clone, Debug, PartialEq, HashStable, Eq, Hash)] +pub struct AssocItem { + pub def_id: DefId, + #[stable_hasher(project(name))] + pub ident: Ident, + pub kind: AssocKind, + pub vis: Visibility, + pub defaultness: hir::Defaultness, + pub container: AssocItemContainer, + + /// Whether this is a method with an explicit self + /// as its first parameter, allowing method calls. + pub fn_has_self_parameter: bool, +} + +impl AssocItem { + pub fn signature(&self, tcx: TyCtxt<'_>) -> String { + match self.kind { + ty::AssocKind::Fn => { + // We skip the binder here because the binder would deanonymize all + // late-bound regions, and we don't want method signatures to show up + // `as for<'r> fn(&'r MyType)`. Pretty-printing handles late-bound + // regions just fine, showing `fn(&MyType)`. + tcx.fn_sig(self.def_id).skip_binder().to_string() + } + ty::AssocKind::Type => format!("type {};", self.ident), + ty::AssocKind::Const => { + format!("const {}: {:?};", self.ident, tcx.type_of(self.def_id)) + } + } + } +} + +#[derive(Copy, Clone, PartialEq, Debug, HashStable, Eq, Hash)] +pub enum AssocKind { + Const, + Fn, + Type, +} + +impl AssocKind { + pub fn namespace(&self) -> Namespace { + match *self { + ty::AssocKind::Type => Namespace::TypeNS, + ty::AssocKind::Const | ty::AssocKind::Fn => Namespace::ValueNS, + } + } + + pub fn as_def_kind(&self) -> DefKind { + match self { + AssocKind::Const => DefKind::AssocConst, + AssocKind::Fn => DefKind::AssocFn, + AssocKind::Type => DefKind::AssocTy, + } + } +} + +/// A list of `ty::AssocItem`s in definition order that allows for efficient lookup by name. +/// +/// When doing lookup by name, we try to postpone hygienic comparison for as long as possible since +/// it is relatively expensive. Instead, items are indexed by `Symbol` and hygienic comparison is +/// done only on items with the same name. +#[derive(Debug, Clone, PartialEq, HashStable)] +pub struct AssocItems<'tcx> { + pub(super) items: SortedIndexMultiMap, +} + +impl<'tcx> AssocItems<'tcx> { + /// Constructs an `AssociatedItems` map from a series of `ty::AssocItem`s in definition order. + pub fn new(items_in_def_order: impl IntoIterator) -> Self { + let items = items_in_def_order.into_iter().map(|item| (item.ident.name, item)).collect(); + AssocItems { items } + } + + /// Returns a slice of associated items in the order they were defined. + /// + /// New code should avoid relying on definition order. If you need a particular associated item + /// for a known trait, make that trait a lang item instead of indexing this array. + pub fn in_definition_order(&self) -> impl '_ + Iterator { + self.items.iter().map(|(_, v)| *v) + } + + pub fn len(&self) -> usize { + self.items.len() + } + + /// Returns an iterator over all associated items with the given name, ignoring hygiene. + pub fn filter_by_name_unhygienic( + &self, + name: Symbol, + ) -> impl '_ + Iterator { + self.items.get_by_key(&name).copied() + } + + /// Returns an iterator over all associated items with the given name. + /// + /// Multiple items may have the same name if they are in different `Namespace`s. For example, + /// an associated type can have the same name as a method. Use one of the `find_by_name_and_*` + /// methods below if you know which item you are looking for. + pub fn filter_by_name( + &'a self, + tcx: TyCtxt<'a>, + ident: Ident, + parent_def_id: DefId, + ) -> impl 'a + Iterator { + self.filter_by_name_unhygienic(ident.name) + .filter(move |item| tcx.hygienic_eq(ident, item.ident, parent_def_id)) + } + + /// Returns the associated item with the given name and `AssocKind`, if one exists. + pub fn find_by_name_and_kind( + &self, + tcx: TyCtxt<'_>, + ident: Ident, + kind: AssocKind, + parent_def_id: DefId, + ) -> Option<&ty::AssocItem> { + self.filter_by_name_unhygienic(ident.name) + .filter(|item| item.kind == kind) + .find(|item| tcx.hygienic_eq(ident, item.ident, parent_def_id)) + } + + /// Returns the associated item with the given name in the given `Namespace`, if one exists. + pub fn find_by_name_and_namespace( + &self, + tcx: TyCtxt<'_>, + ident: Ident, + ns: Namespace, + parent_def_id: DefId, + ) -> Option<&ty::AssocItem> { + self.filter_by_name_unhygienic(ident.name) + .filter(|item| item.kind.namespace() == ns) + .find(|item| tcx.hygienic_eq(ident, item.ident, parent_def_id)) + } +} diff --git a/compiler/rustc_middle/src/ty/closure.rs b/compiler/rustc_middle/src/ty/closure.rs new file mode 100644 index 0000000000000..887a5831cd720 --- /dev/null +++ b/compiler/rustc_middle/src/ty/closure.rs @@ -0,0 +1,371 @@ +use crate::hir::place::{ + Place as HirPlace, PlaceBase as HirPlaceBase, ProjectionKind as HirProjectionKind, +}; +use crate::ty; + +use rustc_data_structures::fx::{FxHashMap, FxIndexMap}; +use rustc_hir as hir; +use rustc_hir::def_id::{DefId, LocalDefId}; +use rustc_span::Span; + +use super::{Ty, TyCtxt}; + +use self::BorrowKind::*; + +#[derive( + Clone, + Copy, + Debug, + PartialEq, + Eq, + Hash, + TyEncodable, + TyDecodable, + TypeFoldable, + HashStable +)] +pub struct UpvarPath { + pub hir_id: hir::HirId, +} + +/// Upvars do not get their own `NodeId`. Instead, we use the pair of +/// the original var ID (that is, the root variable that is referenced +/// by the upvar) and the ID of the closure expression. +#[derive(Clone, Copy, PartialEq, Eq, Hash, TyEncodable, TyDecodable, TypeFoldable, HashStable)] +pub struct UpvarId { + pub var_path: UpvarPath, + pub closure_expr_id: LocalDefId, +} + +impl UpvarId { + pub fn new(var_hir_id: hir::HirId, closure_def_id: LocalDefId) -> UpvarId { + UpvarId { var_path: UpvarPath { hir_id: var_hir_id }, closure_expr_id: closure_def_id } + } +} + +/// 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> { + /// 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), + + /// 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>, +} + +pub type UpvarListMap = FxHashMap>; +pub type UpvarCaptureMap<'tcx> = FxHashMap>; + +/// 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. +pub type MinCaptureInformationMap<'tcx> = FxHashMap>; + +/// Part of `MinCaptureInformationMap`; Maps a root variable to the list of `CapturedPlace`. +/// Used to track the minimum set of `Place`s that need to be captured to support all +/// Places captured by the closure starting at a given root variable. +/// +/// This provides a convenient and quick way of checking if a variable being used within +/// a closure is a capture of a local variable. +pub type RootVariableMinCaptureList<'tcx> = FxIndexMap>; + +/// Part of `MinCaptureInformationMap`; List of `CapturePlace`s. +pub type MinCaptureList<'tcx> = Vec>; + +/// Represents the various closure traits in the language. This +/// will determine the type of the environment (`self`, in the +/// desugaring) argument that the closure expects. +/// +/// You can get the environment type of a closure using +/// `tcx.closure_env_ty()`. +#[derive(Clone, Copy, PartialOrd, Ord, PartialEq, Eq, Hash, Debug, TyEncodable, TyDecodable)] +#[derive(HashStable)] +pub enum ClosureKind { + // Warning: Ordering is significant here! The ordering is chosen + // because the trait Fn is a subtrait of FnMut and so in turn, and + // hence we order it so that Fn < FnMut < FnOnce. + Fn, + FnMut, + FnOnce, +} + +impl<'tcx> ClosureKind { + // This is the initial value used when doing upvar inference. + pub const LATTICE_BOTTOM: ClosureKind = ClosureKind::Fn; + + /// Returns `true` if a type that impls this closure kind + /// must also implement `other`. + pub fn extends(self, other: ty::ClosureKind) -> bool { + matches!( + (self, other), + (ClosureKind::Fn, ClosureKind::Fn) + | (ClosureKind::Fn, ClosureKind::FnMut) + | (ClosureKind::Fn, ClosureKind::FnOnce) + | (ClosureKind::FnMut, ClosureKind::FnMut) + | (ClosureKind::FnMut, ClosureKind::FnOnce) + | (ClosureKind::FnOnce, ClosureKind::FnOnce) + ) + } + + /// Returns the representative scalar type for this closure kind. + /// See `TyS::to_opt_closure_kind` for more details. + pub fn to_ty(self, tcx: TyCtxt<'tcx>) -> Ty<'tcx> { + match self { + ty::ClosureKind::Fn => tcx.types.i8, + ty::ClosureKind::FnMut => tcx.types.i16, + ty::ClosureKind::FnOnce => tcx.types.i32, + } + } +} + +/// A composite describing a `Place` that is captured by a closure. +#[derive(PartialEq, Clone, Debug, TyEncodable, TyDecodable, TypeFoldable, HashStable)] +pub struct CapturedPlace<'tcx> { + /// The `Place` that is captured. + pub place: HirPlace<'tcx>, + + /// `CaptureKind` and expression(s) that resulted in such capture of `place`. + pub info: CaptureInfo<'tcx>, + + /// Represents if `place` can be mutated or not. + pub mutability: hir::Mutability, +} + +impl CapturedPlace<'tcx> { + /// Returns the hir-id of the root variable for the captured place. + /// e.g., if `a.b.c` was captured, would return the hir-id for `a`. + pub fn get_root_variable(&self) -> hir::HirId { + match self.place.base { + HirPlaceBase::Upvar(upvar_id) => upvar_id.var_path.hir_id, + base => bug!("Expected upvar, found={:?}", base), + } + } + + /// Returns the `LocalDefId` of the closure that captured this Place + pub fn get_closure_local_def_id(&self) -> LocalDefId { + match self.place.base { + HirPlaceBase::Upvar(upvar_id) => upvar_id.closure_expr_id, + base => bug!("expected upvar, found={:?}", base), + } + } + + /// Return span pointing to use that resulted in selecting the current capture kind + pub fn get_capture_kind_span(&self, tcx: TyCtxt<'tcx>) -> Span { + if let Some(capture_kind_expr_id) = self.info.capture_kind_expr_id { + tcx.hir().span(capture_kind_expr_id) + } else if let Some(path_expr_id) = self.info.path_expr_id { + tcx.hir().span(path_expr_id) + } else { + // Fallback on upvars mentioned if neither path or capture expr id is captured + + // Safe to unwrap since we know this place is captured by the closure, therefore the closure must have upvars. + tcx.upvars_mentioned(self.get_closure_local_def_id()).unwrap() + [&self.get_root_variable()] + .span + } + } +} + +/// Return true if the `proj_possible_ancestor` represents an ancestor path +/// to `proj_capture` or `proj_possible_ancestor` is same as `proj_capture`, +/// assuming they both start off of the same root variable. +/// +/// **Note:** It's the caller's responsibility to ensure that both lists of projections +/// start off of the same root variable. +/// +/// Eg: 1. `foo.x` which is represented using `projections=[Field(x)]` is an ancestor of +/// `foo.x.y` which is represented using `projections=[Field(x), Field(y)]`. +/// Note both `foo.x` and `foo.x.y` start off of the same root variable `foo`. +/// 2. Since we only look at the projections here function will return `bar.x` as an a valid +/// ancestor of `foo.x.y`. It's the caller's responsibility to ensure that both projections +/// list are being applied to the same root variable. +pub fn is_ancestor_or_same_capture( + proj_possible_ancestor: &[HirProjectionKind], + proj_capture: &[HirProjectionKind], +) -> bool { + // We want to make sure `is_ancestor_or_same_capture("x.0.0", "x.0")` to return false. + // Therefore we can't just check if all projections are same in the zipped iterator below. + if proj_possible_ancestor.len() > proj_capture.len() { + return false; + } + + proj_possible_ancestor.iter().zip(proj_capture).all(|(a, b)| a == b) +} + +/// Part of `MinCaptureInformationMap`; describes the capture kind (&, &mut, move) +/// 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> { + /// Expr Id pointing to use that resulted in selecting the current capture kind + /// + /// Eg: + /// ```rust,no_run + /// let mut t = (0,1); + /// + /// let c = || { + /// println!("{}",t); // L1 + /// t.1 = 4; // L2 + /// }; + /// ``` + /// `capture_kind_expr_id` will point to the use on L2 and `path_expr_id` will point to the + /// use on L1. + /// + /// If the user doesn't enable feature `capture_disjoint_fields` (RFC 2229) then, it is + /// possible that we don't see the use of a particular place resulting in capture_kind_expr_id being + /// None. In such case we fallback on uvpars_mentioned for span. + /// + /// Eg: + /// ```rust,no_run + /// let x = 5; + /// + /// let c = || { + /// let _ = x + /// }; + /// ``` + /// + /// In this example, if `capture_disjoint_fields` is **not** set, then x will be captured, + /// but we won't see it being used during capture analysis, since it's essentially a discard. + pub capture_kind_expr_id: Option, + /// Expr Id pointing to use that resulted the corresponding place being captured + /// + /// See `capture_kind_expr_id` for example. + /// + pub path_expr_id: Option, + + /// Capture mode that was selected + pub capture_kind: UpvarCapture<'tcx>, +} + +pub fn place_to_string_for_capture(tcx: TyCtxt<'tcx>, place: &HirPlace<'tcx>) -> String { + let name = match place.base { + HirPlaceBase::Upvar(upvar_id) => tcx.hir().name(upvar_id.var_path.hir_id).to_string(), + _ => bug!("Capture_information should only contain upvars"), + }; + let mut curr_string = name; + + for (i, proj) in place.projections.iter().enumerate() { + match proj.kind { + HirProjectionKind::Deref => { + curr_string = format!("*{}", curr_string); + } + HirProjectionKind::Field(idx, variant) => match place.ty_before_projection(i).kind() { + ty::Adt(def, ..) => { + curr_string = format!( + "{}.{}", + curr_string, + def.variants[variant].fields[idx as usize].ident.name.as_str() + ); + } + ty::Tuple(_) => { + curr_string = format!("{}.{}", curr_string, idx); + } + _ => { + bug!( + "Field projection applied to a type other than Adt or Tuple: {:?}.", + place.ty_before_projection(i).kind() + ) + } + }, + proj => bug!("{:?} unexpected because it isn't captured", proj), + } + } + + curr_string.to_string() +} + +#[derive(Clone, PartialEq, Debug, TyEncodable, TyDecodable, TypeFoldable, Copy, HashStable)] +pub enum BorrowKind { + /// Data must be immutable and is aliasable. + ImmBorrow, + + /// Data must be immutable but not aliasable. This kind of borrow + /// cannot currently be expressed by the user and is used only in + /// implicit closure bindings. It is needed when the closure + /// is borrowing or mutating a mutable referent, e.g.: + /// + /// ``` + /// let x: &mut isize = ...; + /// let y = || *x += 5; + /// ``` + /// + /// If we were to try to translate this closure into a more explicit + /// form, we'd encounter an error with the code as written: + /// + /// ``` + /// struct Env { x: & &mut isize } + /// let x: &mut isize = ...; + /// let y = (&mut Env { &x }, fn_ptr); // Closure is pair of env and fn + /// fn fn_ptr(env: &mut Env) { **env.x += 5; } + /// ``` + /// + /// This is then illegal because you cannot mutate a `&mut` found + /// in an aliasable location. To solve, you'd have to translate with + /// an `&mut` borrow: + /// + /// ``` + /// struct Env { x: & &mut isize } + /// let x: &mut isize = ...; + /// let y = (&mut Env { &mut x }, fn_ptr); // changed from &x to &mut x + /// fn fn_ptr(env: &mut Env) { **env.x += 5; } + /// ``` + /// + /// Now the assignment to `**env.x` is legal, but creating a + /// mutable pointer to `x` is not because `x` is not mutable. We + /// could fix this by declaring `x` as `let mut x`. This is ok in + /// user code, if awkward, but extra weird for closures, since the + /// borrow is hidden. + /// + /// So we introduce a "unique imm" borrow -- the referent is + /// immutable, but not aliasable. This solves the problem. For + /// simplicity, we don't give users the way to express this + /// borrow, it's just used when translating closures. + UniqueImmBorrow, + + /// Data is mutable and not aliasable. + MutBorrow, +} + +impl BorrowKind { + pub fn from_mutbl(m: hir::Mutability) -> BorrowKind { + match m { + hir::Mutability::Mut => MutBorrow, + hir::Mutability::Not => ImmBorrow, + } + } + + /// Returns a mutability `m` such that an `&m T` pointer could be used to obtain this borrow + /// kind. Because borrow kinds are richer than mutabilities, we sometimes have to pick a + /// mutability that is stronger than necessary so that it at least *would permit* the borrow in + /// question. + pub fn to_mutbl_lossy(self) -> hir::Mutability { + match self { + MutBorrow => hir::Mutability::Mut, + ImmBorrow => hir::Mutability::Not, + + // We have no type corresponding to a unique imm borrow, so + // use `&mut`. It gives all the capabilities of an `&uniq` + // and hence is a safe "over approximation". + UniqueImmBorrow => hir::Mutability::Mut, + } + } +} diff --git a/compiler/rustc_middle/src/ty/codec.rs b/compiler/rustc_middle/src/ty/codec.rs index 73ad87a9ef219..d7767dc39cb32 100644 --- a/compiler/rustc_middle/src/ty/codec.rs +++ b/compiler/rustc_middle/src/ty/codec.rs @@ -120,8 +120,9 @@ impl<'tcx, E: TyEncoder<'tcx>> Encodable for Ty<'tcx> { } } -impl<'tcx, E: TyEncoder<'tcx>> Encodable for ty::Binder> { +impl<'tcx, E: TyEncoder<'tcx>> Encodable for ty::Binder<'tcx, ty::PredicateKind<'tcx>> { fn encode(&self, e: &mut E) -> Result<(), E::Error> { + self.bound_vars().encode(e)?; encode_with_shorthand(e, &self.skip_binder(), TyEncoder::predicate_shorthands) } } @@ -188,7 +189,7 @@ pub trait TyDecoder<'tcx>: Decoder { } #[inline] -pub fn decode_arena_allocable<'tcx, D, T: ArenaAllocatable<'tcx> + Decodable>( +fn decode_arena_allocable<'tcx, D, T: ArenaAllocatable<'tcx> + Decodable>( decoder: &mut D, ) -> Result<&'tcx T, D::Error> where @@ -198,7 +199,7 @@ where } #[inline] -pub fn decode_arena_allocable_slice<'tcx, D, T: ArenaAllocatable<'tcx> + Decodable>( +fn decode_arena_allocable_slice<'tcx, D, T: ArenaAllocatable<'tcx> + Decodable>( decoder: &mut D, ) -> Result<&'tcx [T], D::Error> where @@ -226,18 +227,22 @@ impl<'tcx, D: TyDecoder<'tcx>> Decodable for Ty<'tcx> { } } -impl<'tcx, D: TyDecoder<'tcx>> Decodable for ty::Binder> { - fn decode(decoder: &mut D) -> Result>, D::Error> { +impl<'tcx, D: TyDecoder<'tcx>> Decodable for ty::Binder<'tcx, ty::PredicateKind<'tcx>> { + fn decode(decoder: &mut D) -> Result>, D::Error> { + let bound_vars = Decodable::decode(decoder)?; // Handle shorthands first, if we have an usize > 0x80. - Ok(ty::Binder::bind(if decoder.positioned_at_shorthand() { - let pos = decoder.read_usize()?; - assert!(pos >= SHORTHAND_OFFSET); - let shorthand = pos - SHORTHAND_OFFSET; - - decoder.with_position(shorthand, ty::PredicateKind::decode)? - } else { - ty::PredicateKind::decode(decoder)? - })) + Ok(ty::Binder::bind_with_vars( + if decoder.positioned_at_shorthand() { + let pos = decoder.read_usize()?; + assert!(pos >= SHORTHAND_OFFSET); + let shorthand = pos - SHORTHAND_OFFSET; + + decoder.with_position(shorthand, ty::PredicateKind::decode)? + } else { + ty::PredicateKind::decode(decoder)? + }, + bound_vars, + )) } } @@ -319,7 +324,7 @@ impl<'tcx, D: TyDecoder<'tcx>> RefDecodable<'tcx, D> for ty::List> { } impl<'tcx, D: TyDecoder<'tcx>> RefDecodable<'tcx, D> - for ty::List>> + for ty::List>> { fn decode(decoder: &mut D) -> Result<&'tcx Self, D::Error> { let len = decoder.read_usize()?; @@ -333,6 +338,16 @@ impl<'tcx, D: TyDecoder<'tcx>> RefDecodable<'tcx, D> for ty::Const<'tcx> { } } +impl<'tcx, D: TyDecoder<'tcx>> RefDecodable<'tcx, D> for [ty::ValTree<'tcx>] { + fn decode(decoder: &mut D) -> Result<&'tcx Self, D::Error> { + Ok(decoder.tcx().arena.alloc_from_iter( + (0..decoder.read_usize()?) + .map(|_| Decodable::decode(decoder)) + .collect::, _>>()?, + )) + } +} + impl<'tcx, D: TyDecoder<'tcx>> RefDecodable<'tcx, D> for Allocation { fn decode(decoder: &mut D) -> Result<&'tcx Self, D::Error> { Ok(decoder.tcx().intern_const_alloc(Decodable::decode(decoder)?)) @@ -369,15 +384,23 @@ impl<'tcx, D: TyDecoder<'tcx>> RefDecodable<'tcx, D> for [mir::abstract_const::N } } +impl<'tcx, D: TyDecoder<'tcx>> RefDecodable<'tcx, D> for ty::List { + fn decode(decoder: &mut D) -> Result<&'tcx Self, D::Error> { + let len = decoder.read_usize()?; + Ok(decoder.tcx().mk_bound_variable_kinds((0..len).map(|_| Decodable::decode(decoder)))?) + } +} + impl_decodable_via_ref! { &'tcx ty::TypeckResults<'tcx>, &'tcx ty::List>, - &'tcx ty::List>>, + &'tcx ty::List>>, &'tcx Allocation, &'tcx mir::Body<'tcx>, &'tcx mir::UnsafetyCheckResult, &'tcx mir::BorrowCheckResult<'tcx>, - &'tcx mir::coverage::CodeRegion + &'tcx mir::coverage::CodeRegion, + &'tcx ty::List } #[macro_export] @@ -462,6 +485,11 @@ macro_rules! implement_ty_decoder { read_str -> Cow<'_, str>; } + #[inline] + fn read_raw_bytes_into(&mut self, bytes: &mut [u8]) -> Result<(), Self::Error> { + self.opaque.read_raw_bytes_into(bytes) + } + fn error(&mut self, err: &str) -> Self::Error { self.opaque.error(err) } @@ -473,14 +501,16 @@ macro_rules! implement_ty_decoder { macro_rules! impl_binder_encode_decode { ($($t:ty),+ $(,)?) => { $( - impl<'tcx, E: TyEncoder<'tcx>> Encodable for ty::Binder<$t> { + impl<'tcx, E: TyEncoder<'tcx>> Encodable for ty::Binder<'tcx, $t> { fn encode(&self, e: &mut E) -> Result<(), E::Error> { + self.bound_vars().encode(e)?; self.as_ref().skip_binder().encode(e) } } - impl<'tcx, D: TyDecoder<'tcx>> Decodable for ty::Binder<$t> { + impl<'tcx, D: TyDecoder<'tcx>> Decodable for ty::Binder<'tcx, $t> { fn decode(decoder: &mut D) -> Result { - Ok(ty::Binder::bind(Decodable::decode(decoder)?)) + let bound_vars = Decodable::decode(decoder)?; + Ok(ty::Binder::bind_with_vars(Decodable::decode(decoder)?, bound_vars)) } } )* diff --git a/compiler/rustc_middle/src/ty/consts.rs b/compiler/rustc_middle/src/ty/consts.rs index ed953b981130a..c78151271c171 100644 --- a/compiler/rustc_middle/src/ty/consts.rs +++ b/compiler/rustc_middle/src/ty/consts.rs @@ -5,14 +5,16 @@ use crate::ty::{self, Ty, TyCtxt}; use crate::ty::{ParamEnv, ParamEnvAnd}; use rustc_errors::ErrorReported; use rustc_hir as hir; -use rustc_hir::def_id::LocalDefId; +use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_macros::HashStable; mod int; mod kind; +mod valtree; pub use int::*; pub use kind::*; +pub use valtree::*; /// Typed constant value. #[derive(Copy, Clone, Debug, Hash, TyEncodable, TyDecodable, Eq, PartialEq, Ord, PartialOrd)] @@ -23,7 +25,7 @@ pub struct Const<'tcx> { pub val: ConstKind<'tcx>, } -#[cfg(target_arch = "x86_64")] +#[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))] static_assert_size!(Const<'_>, 48); impl<'tcx> Const<'tcx> { @@ -96,18 +98,18 @@ impl<'tcx> Const<'tcx> { let name = tcx.hir().name(hir_id); ty::ConstKind::Param(ty::ParamConst::new(index, name)) } - _ => ty::ConstKind::Unevaluated( - def.to_global(), - InternalSubsts::identity_for_item(tcx, def.did.to_def_id()), - None, - ), + _ => ty::ConstKind::Unevaluated(ty::Unevaluated { + def: def.to_global(), + substs: InternalSubsts::identity_for_item(tcx, def.did.to_def_id()), + promoted: None, + }), }; tcx.mk_const(ty::Const { val, ty }) } - #[inline] /// Interns the given value as a constant. + #[inline] pub fn from_value(tcx: TyCtxt<'tcx>, val: ConstValue<'tcx>, ty: Ty<'tcx>) -> &'tcx Self { tcx.mk_const(Self { val: ConstKind::Value(val), ty }) } @@ -200,3 +202,18 @@ impl<'tcx> Const<'tcx> { .unwrap_or_else(|| bug!("expected usize, got {:#?}", self)) } } + +pub fn const_param_default<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId) -> &'tcx Const<'tcx> { + let hir_id = tcx.hir().local_def_id_to_hir_id(def_id.expect_local()); + let default_def_id = match tcx.hir().get(hir_id) { + hir::Node::GenericParam(hir::GenericParam { + kind: hir::GenericParamKind::Const { ty: _, default: Some(ac) }, + .. + }) => tcx.hir().local_def_id(ac.hir_id), + _ => span_bug!( + tcx.def_span(def_id), + "`const_param_default` expected a generic parameter with a constant" + ), + }; + Const::from_anon_const(tcx, default_def_id) +} diff --git a/compiler/rustc_middle/src/ty/consts/int.rs b/compiler/rustc_middle/src/ty/consts/int.rs index 63e95f25bb7f3..8ed8ea6a0bc55 100644 --- a/compiler/rustc_middle/src/ty/consts/int.rs +++ b/compiler/rustc_middle/src/ty/consts/int.rs @@ -5,6 +5,8 @@ use rustc_target::abi::{Size, TargetDataLayout}; use std::convert::{TryFrom, TryInto}; use std::fmt; +use crate::ty::TyCtxt; + #[derive(Copy, Clone)] /// A type for representing any integer. Only used for printing. pub struct ConstInt { @@ -239,6 +241,11 @@ impl ScalarInt { Err(self.size()) } } + + #[inline] + pub fn try_to_machine_usize(&self, tcx: TyCtxt<'tcx>) -> Result { + Ok(self.to_bits(tcx.data_layout.pointer_size)? as u64) + } } macro_rules! from { @@ -277,6 +284,18 @@ macro_rules! try_from { from!(u8, u16, u32, u64, u128, bool); try_from!(u8, u16, u32, u64, u128); +impl TryFrom for bool { + type Error = Size; + #[inline] + fn try_from(int: ScalarInt) -> Result { + int.to_bits(Size::from_bytes(1)).and_then(|u| match u { + 0 => Ok(false), + 1 => Ok(true), + _ => Err(Size::from_bytes(1)), + }) + } +} + impl From for ScalarInt { #[inline] fn from(c: char) -> Self { diff --git a/compiler/rustc_middle/src/ty/consts/kind.rs b/compiler/rustc_middle/src/ty/consts/kind.rs index a2638d8bddad0..875d8d00a93d3 100644 --- a/compiler/rustc_middle/src/ty/consts/kind.rs +++ b/compiler/rustc_middle/src/ty/consts/kind.rs @@ -1,3 +1,5 @@ +use std::convert::TryInto; + use crate::mir::interpret::ConstValue; use crate::mir::interpret::Scalar; use crate::mir::Promoted; @@ -9,9 +11,19 @@ use rustc_hir::def_id::DefId; use rustc_macros::HashStable; use rustc_target::abi::Size; +use super::ScalarInt; +/// An unevaluated, potentially generic, constant. +#[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord, TyEncodable, TyDecodable)] +#[derive(Hash, HashStable)] +pub struct Unevaluated<'tcx> { + pub def: ty::WithOptConstParam, + pub substs: SubstsRef<'tcx>, + pub promoted: Option, +} + /// Represents a constant in Rust. -#[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord, TyEncodable, TyDecodable, Hash)] -#[derive(HashStable)] +#[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord, TyEncodable, TyDecodable)] +#[derive(Hash, HashStable)] pub enum ConstKind<'tcx> { /// A const generic parameter. Param(ty::ParamConst), @@ -27,7 +39,7 @@ pub enum ConstKind<'tcx> { /// Used in the HIR by using `Unevaluated` everywhere and later normalizing to one of the other /// variants when the code is monomorphic enough for that. - Unevaluated(ty::WithOptConstParam, SubstsRef<'tcx>, Option), + Unevaluated(Unevaluated<'tcx>), /// Used to hold computed value. Value(ConstValue<'tcx>), @@ -37,7 +49,7 @@ pub enum ConstKind<'tcx> { Error(ty::DelaySpanBugEmitted), } -#[cfg(target_arch = "x86_64")] +#[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))] static_assert_size!(ConstKind<'_>, 40); impl<'tcx> ConstKind<'tcx> { @@ -51,14 +63,19 @@ impl<'tcx> ConstKind<'tcx> { self.try_to_value()?.try_to_scalar() } + #[inline] + pub fn try_to_scalar_int(self) -> Option { + Some(self.try_to_value()?.try_to_scalar()?.assert_int()) + } + #[inline] pub fn try_to_bits(self, size: Size) -> Option { - self.try_to_value()?.try_to_bits(size) + self.try_to_scalar_int()?.to_bits(size).ok() } #[inline] pub fn try_to_bool(self) -> Option { - self.try_to_value()?.try_to_bool() + self.try_to_scalar_int()?.try_into().ok() } #[inline] @@ -93,7 +110,7 @@ impl<'tcx> ConstKind<'tcx> { tcx: TyCtxt<'tcx>, param_env: ParamEnv<'tcx>, ) -> Option, ErrorReported>> { - if let ConstKind::Unevaluated(def, substs, promoted) = self { + if let ConstKind::Unevaluated(Unevaluated { def, substs, promoted }) = self { use crate::mir::interpret::ErrorHandled; // HACK(eddyb) this erases lifetimes even though `const_eval_resolve` @@ -123,7 +140,8 @@ impl<'tcx> ConstKind<'tcx> { let (param_env, substs) = param_env_and_substs.into_parts(); // try to resolve e.g. associated constants to their definition on an impl, and then // evaluate the const. - match tcx.const_eval_resolve(param_env, def, substs, promoted, None) { + match tcx.const_eval_resolve(param_env, ty::Unevaluated { def, substs, promoted }, None) + { // NOTE(eddyb) `val` contains no lifetimes/types/consts, // and we use the original type, so nothing from `substs` // (which may be identity substs, see above), diff --git a/compiler/rustc_middle/src/ty/consts/valtree.rs b/compiler/rustc_middle/src/ty/consts/valtree.rs new file mode 100644 index 0000000000000..f1b78c876328d --- /dev/null +++ b/compiler/rustc_middle/src/ty/consts/valtree.rs @@ -0,0 +1,34 @@ +use super::ScalarInt; +use rustc_macros::HashStable; + +#[derive(Copy, Clone, Debug, Hash, TyEncodable, TyDecodable, Eq, PartialEq, Ord, PartialOrd)] +#[derive(HashStable)] +/// This datastructure is used to represent the value of constants used in the type system. +/// +/// We explicitly choose a different datastructure from the way values are processed within +/// CTFE, as in the type system equal values (according to their `PartialEq`) must also have +/// equal representation (`==` on the rustc data structure, e.g. `ValTree`) and vice versa. +/// Since CTFE uses `AllocId` to represent pointers, it often happens that two different +/// `AllocId`s point to equal values. So we may end up with different representations for +/// two constants whose value is `&42`. Furthermore any kind of struct that has padding will +/// have arbitrary values within that padding, even if the values of the struct are the same. +/// +/// `ValTree` does not have this problem with representation, as it only contains integers or +/// lists of (nested) `ValTree`. +pub enum ValTree<'tcx> { + /// ZSTs, integers, `bool`, `char` are represented as scalars. + /// See the `ScalarInt` documentation for how `ScalarInt` guarantees that equal values + /// of these types have the same representation. + Leaf(ScalarInt), + /// The fields of any kind of aggregate. Structs, tuples and arrays are represented by + /// listing their fields' values in order. + /// Enums are represented by storing their discriminant as a field, followed by all + /// the fields of the variant. + Branch(&'tcx [ValTree<'tcx>]), +} + +impl ValTree<'tcx> { + pub fn zst() -> Self { + Self::Branch(&[]) + } +} diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs index e1d79248171a8..bb2b00cbaea81 100644 --- a/compiler/rustc_middle/src/ty/context.rs +++ b/compiler/rustc_middle/src/ty/context.rs @@ -30,9 +30,7 @@ use rustc_attr as attr; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_data_structures::profiling::SelfProfilerRef; use rustc_data_structures::sharded::{IntoPointer, ShardedHashMap}; -use rustc_data_structures::stable_hasher::{ - hash_stable_hashmap, HashStable, StableHasher, StableVec, -}; +use rustc_data_structures::stable_hasher::{HashStable, StableHasher, StableVec}; use rustc_data_structures::steal::Steal; use rustc_data_structures::sync::{self, Lock, Lrc, WorkerLocal}; use rustc_errors::ErrorReported; @@ -47,6 +45,7 @@ use rustc_hir::{ }; use rustc_index::vec::{Idx, IndexVec}; use rustc_macros::HashStable; +use rustc_middle::mir::FakeReadCause; use rustc_serialize::opaque::{FileEncodeResult, FileEncoder}; use rustc_session::config::{BorrowckMode, CrateType, OutputFilenames}; use rustc_session::lint::{Level, Lint}; @@ -88,12 +87,16 @@ pub struct CtxtInterners<'tcx> { substs: InternedSet<'tcx, InternalSubsts<'tcx>>, canonical_var_infos: InternedSet<'tcx, List>>, region: InternedSet<'tcx, RegionKind>, - poly_existential_predicates: InternedSet<'tcx, List>>>, + poly_existential_predicates: + InternedSet<'tcx, List>>>, predicate: InternedSet<'tcx, PredicateInner<'tcx>>, predicates: InternedSet<'tcx, List>>, projs: InternedSet<'tcx, List>, place_elems: InternedSet<'tcx, List>>, const_: InternedSet<'tcx, Const<'tcx>>, + /// Const allocations. + allocation: InternedSet<'tcx, Allocation>, + bound_variable_kinds: InternedSet<'tcx, List>, } impl<'tcx> CtxtInterners<'tcx> { @@ -111,6 +114,8 @@ impl<'tcx> CtxtInterners<'tcx> { projs: Default::default(), place_elems: Default::default(), const_: Default::default(), + allocation: Default::default(), + bound_variable_kinds: Default::default(), } } @@ -134,7 +139,10 @@ impl<'tcx> CtxtInterners<'tcx> { } #[inline(never)] - fn intern_predicate(&self, kind: Binder>) -> &'tcx PredicateInner<'tcx> { + fn intern_predicate( + &self, + kind: Binder<'tcx, PredicateKind<'tcx>>, + ) -> &'tcx PredicateInner<'tcx> { self.predicate .intern(kind, |kind| { let flags = super::flags::FlagComputation::for_predicate(kind); @@ -382,9 +390,6 @@ pub struct TypeckResults<'tcx> { /// pat_adjustments: ItemLocalMap>>, - /// Borrows - pub upvar_capture_map: ty::UpvarCaptureMap<'tcx>, - /// Records the reasons that we picked the kind of each closure; /// not all closures are present in the map. closure_kind_origins: ItemLocalMap<(Span, HirPlace<'tcx>)>, @@ -420,19 +425,37 @@ pub struct TypeckResults<'tcx> { /// by this function. pub concrete_opaque_types: FxHashMap>, - /// Given the closure ID this map provides the list of UpvarIDs used by it. - /// The upvarID contains the HIR node ID and it also contains the full path - /// leading to the member of the struct or tuple that is used instead of the - /// entire variable. - pub closure_captures: ty::UpvarListMap, - /// Tracks the minimum captures required for a closure; /// see `MinCaptureInformationMap` for more details. pub closure_min_captures: ty::MinCaptureInformationMap<'tcx>, + /// Tracks the fake reads required for a closure and the reason for the fake read. + /// When performing pattern matching for closures, there are times we don't end up + /// reading places that are mentioned in a closure (because of _ patterns). However, + /// to ensure the places are initialized, we introduce fake reads. + /// Consider these two examples: + /// ``` (discriminant matching with only wildcard arm) + /// let x: u8; + /// let c = || match x { _ => () }; + /// ``` + /// In this example, we don't need to actually read/borrow `x` in `c`, and so we don't + /// want to capture it. However, we do still want an error here, because `x` should have + /// to be initialized at the point where c is created. Therefore, we add a "fake read" + /// instead. + /// ``` (destructured assignments) + /// let c = || { + /// let (t1, t2) = t; + /// } + /// ``` + /// In the second example, we capture the disjoint fields of `t` (`t.0` & `t.1`), but + /// we never capture `t`. This becomes an issue when we build MIR as we require + /// information on `t` in order to create place `t.0` and `t.1`. We can solve this + /// issue by fake reading `t`. + pub closure_fake_reads: FxHashMap, FakeReadCause, hir::HirId)>>, + /// Stores the type, expression, span and optional scope span of all types /// that are live across the yield of this generator (if a generator). - pub generator_interior_types: ty::Binder>>, + pub generator_interior_types: ty::Binder<'tcx, Vec>>, /// We sometimes treat byte string literals (which are of type `&[u8; N]`) /// as `&[u8]`, depending on the pattern in which they are used. @@ -454,7 +477,6 @@ impl<'tcx> TypeckResults<'tcx> { adjustments: Default::default(), pat_binding_modes: Default::default(), pat_adjustments: Default::default(), - upvar_capture_map: Default::default(), closure_kind_origins: Default::default(), liberated_fn_sigs: Default::default(), fru_field_types: Default::default(), @@ -462,8 +484,8 @@ impl<'tcx> TypeckResults<'tcx> { used_trait_imports: Lrc::new(Default::default()), tainted_by_errors: None, concrete_opaque_types: Default::default(), - closure_captures: Default::default(), closure_min_captures: Default::default(), + closure_fake_reads: Default::default(), generator_interior_types: ty::Binder::dummy(Default::default()), treat_byte_string_as_slice: Default::default(), } @@ -646,10 +668,6 @@ impl<'tcx> TypeckResults<'tcx> { .flatten() } - pub fn upvar_capture(&self, upvar_id: ty::UpvarId) -> ty::UpvarCapture<'tcx> { - self.upvar_capture_map[&upvar_id] - } - pub fn closure_kind_origins(&self) -> LocalTableInContext<'_, (Span, HirPlace<'tcx>)> { LocalTableInContext { hir_owner: self.hir_owner, data: &self.closure_kind_origins } } @@ -703,23 +721,22 @@ impl<'a, 'tcx> HashStable> for TypeckResults<'tcx> { ref adjustments, ref pat_binding_modes, ref pat_adjustments, - ref upvar_capture_map, ref closure_kind_origins, ref liberated_fn_sigs, ref fru_field_types, - ref coercion_casts, - ref used_trait_imports, tainted_by_errors, ref concrete_opaque_types, - ref closure_captures, ref closure_min_captures, + ref closure_fake_reads, ref generator_interior_types, ref treat_byte_string_as_slice, } = *self; hcx.with_node_id_hashing_mode(NodeIdHashingMode::HashDefPath, |hcx| { + hcx.local_def_path_hash(hir_owner); + type_dependent_defs.hash_stable(hcx, hasher); field_indices.hash_stable(hcx, hasher); user_provided_types.hash_stable(hcx, hasher); @@ -729,17 +746,6 @@ impl<'a, 'tcx> HashStable> for TypeckResults<'tcx> { adjustments.hash_stable(hcx, hasher); pat_binding_modes.hash_stable(hcx, hasher); pat_adjustments.hash_stable(hcx, hasher); - hash_stable_hashmap(hcx, hasher, upvar_capture_map, |up_var_id, hcx| { - let ty::UpvarId { var_path, closure_expr_id } = *up_var_id; - - assert_eq!(var_path.hir_id.owner, hir_owner); - - ( - hcx.local_def_path_hash(var_path.hir_id.owner), - var_path.hir_id.local_id, - hcx.local_def_path_hash(closure_expr_id), - ) - }); closure_kind_origins.hash_stable(hcx, hasher); liberated_fn_sigs.hash_stable(hcx, hasher); @@ -748,8 +754,8 @@ impl<'a, 'tcx> HashStable> for TypeckResults<'tcx> { used_trait_imports.hash_stable(hcx, hasher); tainted_by_errors.hash_stable(hcx, hasher); concrete_opaque_types.hash_stable(hcx, hasher); - closure_captures.hash_stable(hcx, hasher); closure_min_captures.hash_stable(hcx, hasher); + closure_fake_reads.hash_stable(hcx, hasher); generator_interior_types.hash_stable(hcx, hasher); treat_byte_string_as_slice.hash_stable(hcx, hasher); }) @@ -789,7 +795,7 @@ impl CanonicalUserType<'tcx> { return false; } - user_substs.substs.iter().zip(BoundVar::new(0)..).all(|(kind, cvar)| { + iter::zip(user_substs.substs, BoundVar::new(0)..).all(|(kind, cvar)| { match kind.unpack() { GenericArgKind::Type(ty) => match ty.kind() { ty::Bound(debruijn, b) => { @@ -804,7 +810,7 @@ impl CanonicalUserType<'tcx> { ty::ReLateBound(debruijn, br) => { // We only allow a `ty::INNERMOST` index in substitutions. assert_eq!(*debruijn, ty::INNERMOST); - cvar == br.assert_bound_var() + cvar == br.var } _ => false, }, @@ -1013,9 +1019,6 @@ pub struct GlobalCtxt<'tcx> { /// `#[rustc_const_stable]` and `#[rustc_const_unstable]` attributes const_stability_interner: ShardedHashMap<&'tcx attr::ConstStability, ()>, - /// Stores the value of constants (and deduplicates the actual memory) - allocation_interner: ShardedHashMap<&'tcx Allocation, ()>, - /// Stores memory for globals (statics/consts). pub(crate) alloc_map: Lock>, @@ -1058,7 +1061,10 @@ impl<'tcx> TyCtxt<'tcx> { } pub fn intern_const_alloc(self, alloc: Allocation) -> &'tcx Allocation { - self.allocation_interner.intern(alloc, |alloc| self.arena.alloc(alloc)) + self.interners + .allocation + .intern(alloc, |alloc| Interned(self.interners.arena.alloc(alloc))) + .0 } /// Allocates a read-only byte or string literal for `mir::interpret`. @@ -1091,13 +1097,16 @@ impl<'tcx> TyCtxt<'tcx> { None => return Bound::Unbounded, }; debug!("layout_scalar_valid_range: attr={:?}", attr); - for meta in attr.meta_item_list().expect("rustc_layout_scalar_valid_range takes args") { - match meta.literal().expect("attribute takes lit").kind { - ast::LitKind::Int(a, _) => return Bound::Included(a), - _ => span_bug!(attr.span, "rustc_layout_scalar_valid_range expects int arg"), - } + if let Some( + &[ast::NestedMetaItem::Literal(ast::Lit { kind: ast::LitKind::Int(a, _), .. })], + ) = attr.meta_item_list().as_deref() + { + Bound::Included(a) + } else { + self.sess + .delay_span_bug(attr.span, "invalid rustc_layout_scalar_valid_range attribute"); + Bound::Unbounded } - span_bug!(attr.span, "no arguments to `rustc_layout_scalar_valid_range` attribute"); }; ( get(sym::rustc_layout_scalar_valid_range_start), @@ -1174,7 +1183,6 @@ impl<'tcx> TyCtxt<'tcx> { layout_interner: Default::default(), stability_interner: Default::default(), const_stability_interner: Default::default(), - allocation_interner: Default::default(), alloc_map: Lock::new(interpret::AllocMap::new()), output_filenames: Arc::new(output_filenames.clone()), } @@ -1610,13 +1618,15 @@ macro_rules! nop_list_lift { nop_lift! {type_; Ty<'a> => Ty<'tcx>} nop_lift! {region; Region<'a> => Region<'tcx>} nop_lift! {const_; &'a Const<'a> => &'tcx Const<'tcx>} +nop_lift! {allocation; &'a Allocation => &'tcx Allocation} nop_lift! {predicate; &'a PredicateInner<'a> => &'tcx PredicateInner<'tcx>} nop_list_lift! {type_list; Ty<'a> => Ty<'tcx>} -nop_list_lift! {poly_existential_predicates; ty::Binder> => ty::Binder>} +nop_list_lift! {poly_existential_predicates; ty::Binder<'a, ExistentialPredicate<'a>> => ty::Binder<'tcx, ExistentialPredicate<'tcx>>} nop_list_lift! {predicates; Predicate<'a> => Predicate<'tcx>} nop_list_lift! {canonical_var_infos; CanonicalVarInfo<'a> => CanonicalVarInfo<'tcx>} nop_list_lift! {projs; ProjectionKind => ProjectionKind} +nop_list_lift! {bound_variable_kinds; ty::BoundVariableKind => ty::BoundVariableKind} // This is the impl for `&'a InternalSubsts<'a>`. nop_list_lift! {substs; GenericArg<'a> => GenericArg<'tcx>} @@ -1900,7 +1910,7 @@ impl<'tcx> TyCtxt<'tcx> { "Const Stability interner: #{}", self.0.const_stability_interner.len() )?; - writeln!(fmt, "Allocation interner: #{}", self.0.allocation_interner.len())?; + writeln!(fmt, "Allocation interner: #{}", self.0.interners.allocation.len())?; writeln!(fmt, "Layout interner: #{}", self.0.layout_interner.len())?; Ok(()) @@ -1962,8 +1972,8 @@ impl<'tcx> Hash for Interned<'tcx, PredicateInner<'tcx>> { } } -impl<'tcx> Borrow>> for Interned<'tcx, PredicateInner<'tcx>> { - fn borrow<'a>(&'a self) -> &'a Binder> { +impl<'tcx> Borrow>> for Interned<'tcx, PredicateInner<'tcx>> { + fn borrow<'a>(&'a self) -> &'a Binder<'tcx, PredicateKind<'tcx>> { &self.0.kind } } @@ -2001,6 +2011,26 @@ impl<'tcx> Borrow> for Interned<'tcx, Const<'tcx>> { } } +impl<'tcx> Borrow for Interned<'tcx, Allocation> { + fn borrow<'a>(&'a self) -> &'a Allocation { + &self.0 + } +} + +impl<'tcx> PartialEq for Interned<'tcx, Allocation> { + fn eq(&self, other: &Self) -> bool { + self.0 == other.0 + } +} + +impl<'tcx> Eq for Interned<'tcx, Allocation> {} + +impl<'tcx> Hash for Interned<'tcx, Allocation> { + fn hash(&self, s: &mut H) { + self.0.hash(s) + } +} + macro_rules! direct_interners { ($($name:ident: $method:ident($ty:ty),)+) => { $(impl<'tcx> PartialEq for Interned<'tcx, $ty> { @@ -2049,10 +2079,11 @@ slice_interners!( substs: _intern_substs(GenericArg<'tcx>), canonical_var_infos: _intern_canonical_var_infos(CanonicalVarInfo<'tcx>), poly_existential_predicates: - _intern_poly_existential_predicates(ty::Binder>), + _intern_poly_existential_predicates(ty::Binder<'tcx, ExistentialPredicate<'tcx>>), predicates: _intern_predicates(Predicate<'tcx>), projs: _intern_projs(ProjectionKind), place_elems: _intern_place_elems(PlaceElem<'tcx>), + bound_variable_kinds: _intern_bound_variable_kinds(ty::BoundVariableKind), ); impl<'tcx> TyCtxt<'tcx> { @@ -2135,7 +2166,7 @@ impl<'tcx> TyCtxt<'tcx> { } #[inline] - pub fn mk_predicate(self, binder: Binder>) -> Predicate<'tcx> { + pub fn mk_predicate(self, binder: Binder<'tcx, PredicateKind<'tcx>>) -> Predicate<'tcx> { let inner = self.interners.intern_predicate(binder); Predicate { inner } } @@ -2144,7 +2175,7 @@ impl<'tcx> TyCtxt<'tcx> { pub fn reuse_or_mk_predicate( self, pred: Predicate<'tcx>, - binder: Binder>, + binder: Binder<'tcx, PredicateKind<'tcx>>, ) -> Predicate<'tcx> { if pred.kind() != binder { self.mk_predicate(binder) } else { pred } } @@ -2198,7 +2229,7 @@ impl<'tcx> TyCtxt<'tcx> { let adt_def = self.adt_def(wrapper_def_id); let substs = InternalSubsts::for_item(self, wrapper_def_id, |param, substs| match param.kind { - GenericParamDefKind::Lifetime | GenericParamDefKind::Const => bug!(), + GenericParamDefKind::Lifetime | GenericParamDefKind::Const { .. } => bug!(), GenericParamDefKind::Type { has_default, .. } => { if param.index == 0 { ty_param.into() @@ -2265,11 +2296,6 @@ impl<'tcx> TyCtxt<'tcx> { self.mk_ptr(TypeAndMut { ty, mutbl: hir::Mutability::Not }) } - #[inline] - pub fn mk_nil_ptr(self) -> Ty<'tcx> { - self.mk_imm_ptr(self.mk_unit()) - } - #[inline] pub fn mk_array(self, ty: Ty<'tcx>, n: u64) -> Ty<'tcx> { self.mk_ty(Array(ty, ty::Const::from_usize(self, n))) @@ -2316,7 +2342,7 @@ impl<'tcx> TyCtxt<'tcx> { #[inline] pub fn mk_dynamic( self, - obj: &'tcx List>>, + obj: &'tcx List>>, reg: ty::Region<'tcx>, ) -> Ty<'tcx> { self.mk_ty(Dynamic(obj, reg)) @@ -2343,7 +2369,7 @@ impl<'tcx> TyCtxt<'tcx> { } #[inline] - pub fn mk_generator_witness(self, types: ty::Binder<&'tcx List>>) -> Ty<'tcx> { + pub fn mk_generator_witness(self, types: ty::Binder<'tcx, &'tcx List>>) -> Ty<'tcx> { self.mk_ty(GeneratorWitness(types)) } @@ -2393,7 +2419,7 @@ impl<'tcx> TyCtxt<'tcx> { self.mk_region(ty::ReEarlyBound(param.to_early_bound_region_data())).into() } GenericParamDefKind::Type { .. } => self.mk_ty_param(param.index, param.name).into(), - GenericParamDefKind::Const => { + GenericParamDefKind::Const { .. } => { self.mk_const_param(param.index, param.name, self.type_of(param.def_id)).into() } } @@ -2448,8 +2474,8 @@ impl<'tcx> TyCtxt<'tcx> { pub fn intern_poly_existential_predicates( self, - eps: &[ty::Binder>], - ) -> &'tcx List>> { + eps: &[ty::Binder<'tcx, ExistentialPredicate<'tcx>>], + ) -> &'tcx List>> { assert!(!eps.is_empty()); assert!( eps.array_windows() @@ -2494,6 +2520,13 @@ impl<'tcx> TyCtxt<'tcx> { if ts.is_empty() { List::empty() } else { self._intern_canonical_var_infos(ts) } } + pub fn intern_bound_variable_kinds( + self, + ts: &[ty::BoundVariableKind], + ) -> &'tcx List { + if ts.is_empty() { List::empty() } else { self._intern_bound_variable_kinds(ts) } + } + pub fn mk_fn_sig( self, inputs: I, @@ -2515,8 +2548,8 @@ impl<'tcx> TyCtxt<'tcx> { pub fn mk_poly_existential_predicates< I: InternAs< - [ty::Binder>], - &'tcx List>>, + [ty::Binder<'tcx, ExistentialPredicate<'tcx>>], + &'tcx List>>, >, >( self, @@ -2554,6 +2587,15 @@ impl<'tcx> TyCtxt<'tcx> { self.mk_substs(iter::once(self_ty.into()).chain(rest.iter().cloned())) } + pub fn mk_bound_variable_kinds< + I: InternAs<[ty::BoundVariableKind], &'tcx List>, + >( + self, + iter: I, + ) -> I::Output { + iter.intern_with(|xs| self.intern_bound_variable_kinds(xs)) + } + /// Walks upwards from `id` to find a node which might change lint levels with attributes. /// It stops at `bound` and just returns it if reached. pub fn maybe_lint_level_root_bounded(self, mut id: HirId, bound: HirId) -> HirId { @@ -2618,6 +2660,7 @@ impl<'tcx> TyCtxt<'tcx> { } pub fn named_region(self, id: HirId) -> Option { + debug!(?id, "named_region"); self.named_region_map(id.owner).and_then(|map| map.get(&id.local_id).cloned()) } @@ -2626,9 +2669,19 @@ impl<'tcx> TyCtxt<'tcx> { .map_or(false, |(owner, set)| owner == id.owner && set.contains(&id.local_id)) } - pub fn object_lifetime_defaults(self, id: HirId) -> Option<&'tcx [ObjectLifetimeDefault]> { + pub fn object_lifetime_defaults(self, id: HirId) -> Option> { self.object_lifetime_defaults_map(id.owner) - .and_then(|map| map.get(&id.local_id).map(|v| &**v)) + } + + pub fn late_bound_vars(self, id: HirId) -> &'tcx List { + self.mk_bound_variable_kinds( + self.late_bound_vars_map(id.owner) + .and_then(|map| map.get(&id.local_id).cloned()) + .unwrap_or_else(|| { + bug!("No bound vars found for {:?} ({:?})", self.hir().node_to_string(id), id) + }) + .iter(), + ) } } diff --git a/compiler/rustc_middle/src/ty/diagnostics.rs b/compiler/rustc_middle/src/ty/diagnostics.rs index f41bb7e6d6350..982c8a354b4ab 100644 --- a/compiler/rustc_middle/src/ty/diagnostics.rs +++ b/compiler/rustc_middle/src/ty/diagnostics.rs @@ -314,6 +314,7 @@ impl<'v> hir::intravisit::Visitor<'v> for TraitObjectVisitor<'v> { hir::LifetimeName::ImplicitObjectLifetimeDefault | hir::LifetimeName::Static, .. }, + _, ) => { self.0.push(ty); } diff --git a/compiler/rustc_middle/src/ty/erase_regions.rs b/compiler/rustc_middle/src/ty/erase_regions.rs index 4412ba9408c6c..759d1a017aa2a 100644 --- a/compiler/rustc_middle/src/ty/erase_regions.rs +++ b/compiler/rustc_middle/src/ty/erase_regions.rs @@ -1,3 +1,4 @@ +use crate::mir; use crate::ty::fold::{TypeFoldable, TypeFolder}; use crate::ty::{self, Ty, TyCtxt, TypeFlags}; @@ -43,7 +44,7 @@ impl TypeFolder<'tcx> for RegionEraserVisitor<'tcx> { if ty.needs_infer() { ty.super_fold_with(self) } else { self.tcx.erase_regions_ty(ty) } } - fn fold_binder(&mut self, t: ty::Binder) -> ty::Binder + fn fold_binder(&mut self, t: ty::Binder<'tcx, T>) -> ty::Binder<'tcx, T> where T: TypeFoldable<'tcx>, { @@ -65,4 +66,8 @@ impl TypeFolder<'tcx> for RegionEraserVisitor<'tcx> { _ => self.tcx.lifetimes.re_erased, } } + + fn fold_mir_const(&mut self, c: mir::ConstantKind<'tcx>) -> mir::ConstantKind<'tcx> { + c.super_fold_with(self) + } } diff --git a/compiler/rustc_middle/src/ty/error.rs b/compiler/rustc_middle/src/ty/error.rs index bf315c81588a9..008e6d015e879 100644 --- a/compiler/rustc_middle/src/ty/error.rs +++ b/compiler/rustc_middle/src/ty/error.rs @@ -12,7 +12,6 @@ use rustc_target::spec::abi; use std::borrow::Cow; use std::fmt; -use std::ops::Deref; #[derive(Clone, Copy, Debug, PartialEq, Eq, TypeFoldable)] pub struct ExpectedFound { @@ -37,6 +36,7 @@ pub enum TypeError<'tcx> { UnsafetyMismatch(ExpectedFound), AbiMismatch(ExpectedFound), Mutability, + ArgumentMutability(usize), TupleSize(ExpectedFound), FixedArraySize(ExpectedFound), ArgCount, @@ -47,6 +47,7 @@ pub enum TypeError<'tcx> { RegionsPlaceholderMismatch, Sorts(ExpectedFound>), + ArgumentSorts(ExpectedFound>, usize), IntMismatch(ExpectedFound), FloatMismatch(ExpectedFound), Traits(ExpectedFound), @@ -58,7 +59,9 @@ pub enum TypeError<'tcx> { CyclicTy(Ty<'tcx>), CyclicConst(&'tcx ty::Const<'tcx>), ProjectionMismatched(ExpectedFound), - ExistentialMismatch(ExpectedFound<&'tcx ty::List>>>), + ExistentialMismatch( + ExpectedFound<&'tcx ty::List>>>, + ), ObjectUnsafeCoercion(DefId), ConstMismatch(ExpectedFound<&'tcx ty::Const<'tcx>>), @@ -109,7 +112,7 @@ impl<'tcx> fmt::Display for TypeError<'tcx> { AbiMismatch(values) => { write!(f, "expected {} fn, found {} fn", values.expected, values.found) } - Mutability => write!(f, "types differ in mutability"), + ArgumentMutability(_) | Mutability => write!(f, "types differ in mutability"), TupleSize(values) => write!( f, "expected a tuple with {} element{}, \ @@ -141,7 +144,7 @@ impl<'tcx> fmt::Display for TypeError<'tcx> { br_string(br) ), RegionsPlaceholderMismatch => write!(f, "one type is more general than the other"), - Sorts(values) => ty::tls::with(|tcx| { + ArgumentSorts(values, _) | Sorts(values) => ty::tls::with(|tcx| { report_maybe_different( f, &values.expected.sort_string(tcx), @@ -198,10 +201,11 @@ impl<'tcx> TypeError<'tcx> { use self::TypeError::*; match self { CyclicTy(_) | CyclicConst(_) | UnsafetyMismatch(_) | Mismatch | AbiMismatch(_) - | FixedArraySize(_) | Sorts(_) | IntMismatch(_) | FloatMismatch(_) - | VariadicMismatch(_) | TargetFeatureCast(_) => false, + | FixedArraySize(_) | ArgumentSorts(..) | Sorts(_) | IntMismatch(_) + | FloatMismatch(_) | VariadicMismatch(_) | TargetFeatureCast(_) => false, Mutability + | ArgumentMutability(_) | TupleSize(_) | ArgCount | RegionsDoesNotOutlive(..) @@ -338,7 +342,7 @@ impl<'tcx> TyCtxt<'tcx> { use self::TypeError::*; debug!("note_and_explain_type_err err={:?} cause={:?}", err, cause); match err { - Sorts(values) => { + ArgumentSorts(values, _) | Sorts(values) => { match (values.expected.kind(), values.found.kind()) { (ty::Closure(..), ty::Closure(..)) => { db.note("no two closures, even if identical, have the same type"); @@ -510,13 +514,18 @@ impl Trait for X { "consider constraining the associated type `{}` to `{}`", values.found, values.expected, ); - if !self.suggest_constraint( + if !(self.suggest_constraining_opaque_associated_type( + db, + &msg, + proj_ty, + values.expected, + ) || self.suggest_constraint( db, &msg, body_owner_def_id, proj_ty, values.expected, - ) { + )) { db.help(&msg); db.note( "for more information, visit \ @@ -548,7 +557,6 @@ impl Trait for X { TargetFeatureCast(def_id) => { let attrs = self.get_attrs(*def_id); let target_spans = attrs - .deref() .iter() .filter(|attr| attr.has_name(sym::target_feature)) .map(|attr| attr.span); @@ -701,20 +709,7 @@ impl Trait for X { } } - if let ty::Opaque(def_id, _) = *proj_ty.self_ty().kind() { - // When the expected `impl Trait` is not defined in the current item, it will come from - // a return type. This can occur when dealing with `TryStream` (#71035). - if self.constrain_associated_type_structured_suggestion( - db, - self.def_span(def_id), - &assoc, - proj_ty.trait_ref_and_own_substs(self).1, - values.found, - &msg, - ) { - return; - } - } + self.suggest_constraining_opaque_associated_type(db, &msg, proj_ty, values.found); if self.point_at_associated_type(db, body_owner_def_id, values.found) { return; @@ -752,6 +747,30 @@ fn foo(&self) -> Self::T { String::new() } } } + /// When the expected `impl Trait` is not defined in the current item, it will come from + /// a return type. This can occur when dealing with `TryStream` (#71035). + fn suggest_constraining_opaque_associated_type( + self, + db: &mut DiagnosticBuilder<'_>, + msg: &str, + proj_ty: &ty::ProjectionTy<'tcx>, + ty: Ty<'tcx>, + ) -> bool { + let assoc = self.associated_item(proj_ty.item_def_id); + if let ty::Opaque(def_id, _) = *proj_ty.self_ty().kind() { + self.constrain_associated_type_structured_suggestion( + db, + self.def_span(def_id), + &assoc, + proj_ty.trait_ref_and_own_substs(self).1, + ty, + &msg, + ) + } else { + false + } + } + fn point_at_methods_that_satisfy_associated_type( self, db: &mut DiagnosticBuilder<'_>, diff --git a/compiler/rustc_middle/src/ty/flags.rs b/compiler/rustc_middle/src/ty/flags.rs index 6ecd1ebf37039..01bc5cc761ca6 100644 --- a/compiler/rustc_middle/src/ty/flags.rs +++ b/compiler/rustc_middle/src/ty/flags.rs @@ -22,7 +22,7 @@ impl FlagComputation { result } - pub fn for_predicate(binder: ty::Binder>) -> FlagComputation { + pub fn for_predicate(binder: ty::Binder<'tcx, ty::PredicateKind<'_>>) -> FlagComputation { let mut result = FlagComputation::new(); result.add_predicate(binder); result @@ -53,7 +53,7 @@ impl FlagComputation { /// Adds the flags/depth from a set of types that appear within the current type, but within a /// region binder. - fn bound_computation(&mut self, value: ty::Binder, f: F) + fn bound_computation(&mut self, value: ty::Binder<'_, T>, f: F) where F: FnOnce(&mut Self, T), { @@ -204,7 +204,7 @@ impl FlagComputation { } } - fn add_predicate(&mut self, binder: ty::Binder>) { + fn add_predicate(&mut self, binder: ty::Binder<'tcx, ty::PredicateKind<'_>>) { self.bound_computation(binder, |computation, atom| computation.add_predicate_atom(atom)); } @@ -270,10 +270,7 @@ impl FlagComputation { fn add_const(&mut self, c: &ty::Const<'_>) { self.add_ty(c.ty); match c.val { - ty::ConstKind::Unevaluated(_, substs, _) => { - self.add_substs(substs); - self.add_flags(TypeFlags::HAS_CT_PROJECTION); - } + ty::ConstKind::Unevaluated(unevaluated) => self.add_unevaluated_const(unevaluated), ty::ConstKind::Infer(infer) => { self.add_flags(TypeFlags::STILL_FURTHER_SPECIALIZABLE); match infer { @@ -297,6 +294,11 @@ impl FlagComputation { } } + fn add_unevaluated_const(&mut self, ct: ty::Unevaluated<'tcx>) { + self.add_substs(ct.substs); + self.add_flags(TypeFlags::HAS_CT_PROJECTION); + } + fn add_existential_projection(&mut self, projection: &ty::ExistentialProjection<'_>) { self.add_substs(projection.substs); self.add_ty(projection.ty); diff --git a/compiler/rustc_middle/src/ty/fold.rs b/compiler/rustc_middle/src/ty/fold.rs index 4c7db4e803b8e..eb6d163312c83 100644 --- a/compiler/rustc_middle/src/ty/fold.rs +++ b/compiler/rustc_middle/src/ty/fold.rs @@ -30,11 +30,13 @@ //! //! These methods return true to indicate that the visitor has found what it is //! looking for, and does not need to visit anything else. +use crate::mir; use crate::ty::{self, flags::FlagComputation, Binder, Ty, TyCtxt, TypeFlags}; use rustc_hir as hir; use rustc_hir::def_id::DefId; use rustc_data_structures::fx::FxHashSet; +use rustc_data_structures::sso::SsoHashSet; use std::collections::BTreeMap; use std::fmt; use std::ops::ControlFlow; @@ -42,7 +44,7 @@ use std::ops::ControlFlow; /// This trait is implemented for every type that can be folded. /// Basically, every type that has a corresponding method in `TypeFolder`. /// -/// To implement this conveniently, use the derive macro located in librustc_macros. +/// To implement this conveniently, use the derive macro located in `rustc_macros`. pub trait TypeFoldable<'tcx>: fmt::Debug + Clone { fn super_fold_with>(self, folder: &mut F) -> Self; fn fold_with>(self, folder: &mut F) -> Self { @@ -161,7 +163,7 @@ impl TypeFoldable<'tcx> for hir::Constness { pub trait TypeFolder<'tcx>: Sized { fn tcx<'a>(&'a self) -> TyCtxt<'tcx>; - fn fold_binder(&mut self, t: Binder) -> Binder + fn fold_binder(&mut self, t: Binder<'tcx, T>) -> Binder<'tcx, T> where T: TypeFoldable<'tcx>, { @@ -179,12 +181,19 @@ pub trait TypeFolder<'tcx>: Sized { fn fold_const(&mut self, c: &'tcx ty::Const<'tcx>) -> &'tcx ty::Const<'tcx> { c.super_fold_with(self) } + + fn fold_mir_const(&mut self, c: mir::ConstantKind<'tcx>) -> mir::ConstantKind<'tcx> { + bug!("most type folders should not be folding MIR datastructures: {:?}", c) + } } pub trait TypeVisitor<'tcx>: Sized { type BreakTy = !; - fn visit_binder>(&mut self, t: &Binder) -> ControlFlow { + fn visit_binder>( + &mut self, + t: &Binder<'tcx, T>, + ) -> ControlFlow { t.super_visit_with(self) } @@ -322,7 +331,7 @@ impl<'tcx> TyCtxt<'tcx> { fn visit_binder>( &mut self, - t: &Binder, + t: &Binder<'tcx, T>, ) -> ControlFlow { self.outer_index.shift_in(1); let result = t.as_ref().skip_binder().visit_with(self); @@ -400,7 +409,10 @@ impl<'a, 'tcx> TypeFolder<'tcx> for RegionFolder<'a, 'tcx> { self.tcx } - fn fold_binder>(&mut self, t: ty::Binder) -> ty::Binder { + fn fold_binder>( + &mut self, + t: ty::Binder<'tcx, T>, + ) -> ty::Binder<'tcx, T> { self.current_index.shift_in(1); let t = t.super_fold_with(self); self.current_index.shift_out(1); @@ -439,18 +451,18 @@ struct BoundVarReplacer<'a, 'tcx> { /// the ones we have visited. current_index: ty::DebruijnIndex, - fld_r: &'a mut (dyn FnMut(ty::BoundRegion) -> ty::Region<'tcx> + 'a), - fld_t: &'a mut (dyn FnMut(ty::BoundTy) -> Ty<'tcx> + 'a), - fld_c: &'a mut (dyn FnMut(ty::BoundVar, Ty<'tcx>) -> &'tcx ty::Const<'tcx> + 'a), + fld_r: Option<&'a mut (dyn FnMut(ty::BoundRegion) -> ty::Region<'tcx> + 'a)>, + fld_t: Option<&'a mut (dyn FnMut(ty::BoundTy) -> Ty<'tcx> + 'a)>, + fld_c: Option<&'a mut (dyn FnMut(ty::BoundVar, Ty<'tcx>) -> &'tcx ty::Const<'tcx> + 'a)>, } impl<'a, 'tcx> BoundVarReplacer<'a, 'tcx> { - fn new(tcx: TyCtxt<'tcx>, fld_r: &'a mut F, fld_t: &'a mut G, fld_c: &'a mut H) -> Self - where - F: FnMut(ty::BoundRegion) -> ty::Region<'tcx>, - G: FnMut(ty::BoundTy) -> Ty<'tcx>, - H: FnMut(ty::BoundVar, Ty<'tcx>) -> &'tcx ty::Const<'tcx>, - { + fn new( + tcx: TyCtxt<'tcx>, + fld_r: Option<&'a mut (dyn FnMut(ty::BoundRegion) -> ty::Region<'tcx> + 'a)>, + fld_t: Option<&'a mut (dyn FnMut(ty::BoundTy) -> Ty<'tcx> + 'a)>, + fld_c: Option<&'a mut (dyn FnMut(ty::BoundVar, Ty<'tcx>) -> &'tcx ty::Const<'tcx> + 'a)>, + ) -> Self { BoundVarReplacer { tcx, current_index: ty::INNERMOST, fld_r, fld_t, fld_c } } } @@ -460,7 +472,10 @@ impl<'a, 'tcx> TypeFolder<'tcx> for BoundVarReplacer<'a, 'tcx> { self.tcx } - fn fold_binder>(&mut self, t: ty::Binder) -> ty::Binder { + fn fold_binder>( + &mut self, + t: ty::Binder<'tcx, T>, + ) -> ty::Binder<'tcx, T> { self.current_index.shift_in(1); let t = t.super_fold_with(self); self.current_index.shift_out(1); @@ -469,63 +484,58 @@ impl<'a, 'tcx> TypeFolder<'tcx> for BoundVarReplacer<'a, 'tcx> { fn fold_ty(&mut self, t: Ty<'tcx>) -> Ty<'tcx> { match *t.kind() { - ty::Bound(debruijn, bound_ty) => { - if debruijn == self.current_index { - let fld_t = &mut self.fld_t; + ty::Bound(debruijn, bound_ty) if debruijn == self.current_index => { + if let Some(fld_t) = self.fld_t.as_mut() { let ty = fld_t(bound_ty); - ty::fold::shift_vars(self.tcx, &ty, self.current_index.as_u32()) - } else { - t + return ty::fold::shift_vars(self.tcx, &ty, self.current_index.as_u32()); } } - _ => { - if !t.has_vars_bound_at_or_above(self.current_index) { - // Nothing more to substitute. - t - } else { - t.super_fold_with(self) - } + _ if t.has_vars_bound_at_or_above(self.current_index) => { + return t.super_fold_with(self); } + _ => {} } + t } fn fold_region(&mut self, r: ty::Region<'tcx>) -> ty::Region<'tcx> { match *r { ty::ReLateBound(debruijn, br) if debruijn == self.current_index => { - let fld_r = &mut self.fld_r; - let region = fld_r(br); - if let ty::ReLateBound(debruijn1, br) = *region { - // If the callback returns a late-bound region, - // that region should always use the INNERMOST - // debruijn index. Then we adjust it to the - // correct depth. - assert_eq!(debruijn1, ty::INNERMOST); - self.tcx.mk_region(ty::ReLateBound(debruijn, br)) - } else { - region + if let Some(fld_r) = self.fld_r.as_mut() { + let region = fld_r(br); + return if let ty::ReLateBound(debruijn1, br) = *region { + // If the callback returns a late-bound region, + // that region should always use the INNERMOST + // debruijn index. Then we adjust it to the + // correct depth. + assert_eq!(debruijn1, ty::INNERMOST); + self.tcx.mk_region(ty::ReLateBound(debruijn, br)) + } else { + region + }; } } - _ => r, + _ => {} } + r } fn fold_const(&mut self, ct: &'tcx ty::Const<'tcx>) -> &'tcx ty::Const<'tcx> { - if let ty::Const { val: ty::ConstKind::Bound(debruijn, bound_const), ty } = *ct { - if debruijn == self.current_index { - let fld_c = &mut self.fld_c; - let ct = fld_c(bound_const, ty); - ty::fold::shift_vars(self.tcx, &ct, self.current_index.as_u32()) - } else { - ct + match *ct { + ty::Const { val: ty::ConstKind::Bound(debruijn, bound_const), ty } + if debruijn == self.current_index => + { + if let Some(fld_c) = self.fld_c.as_mut() { + let ct = fld_c(bound_const, ty); + return ty::fold::shift_vars(self.tcx, &ct, self.current_index.as_u32()); + } } - } else { - if !ct.has_vars_bound_at_or_above(self.current_index) { - // Nothing more to substitute. - ct - } else { - ct.super_fold_with(self) + _ if ct.has_vars_bound_at_or_above(self.current_index) => { + return ct.super_fold_with(self); } + _ => {} } + ct } } @@ -543,21 +553,23 @@ impl<'tcx> TyCtxt<'tcx> { /// contain escaping bound types. pub fn replace_late_bound_regions( self, - value: Binder, + value: Binder<'tcx, T>, mut fld_r: F, ) -> (T, BTreeMap>) where F: FnMut(ty::BoundRegion) -> ty::Region<'tcx>, T: TypeFoldable<'tcx>, { - // identity for bound types and consts - let fld_t = |bound_ty| self.mk_ty(ty::Bound(ty::INNERMOST, bound_ty)); - let fld_c = |bound_ct, ty| { - self.mk_const(ty::Const { val: ty::ConstKind::Bound(ty::INNERMOST, bound_ct), ty }) - }; let mut region_map = BTreeMap::new(); - let real_fld_r = |br: ty::BoundRegion| *region_map.entry(br).or_insert_with(|| fld_r(br)); - let value = self.replace_escaping_bound_vars(value.skip_binder(), real_fld_r, fld_t, fld_c); + let mut real_fld_r = + |br: ty::BoundRegion| *region_map.entry(br).or_insert_with(|| fld_r(br)); + let value = value.skip_binder(); + let value = if !value.has_escaping_bound_vars() { + value + } else { + let mut replacer = BoundVarReplacer::new(self, Some(&mut real_fld_r), None, None); + value.fold_with(&mut replacer) + }; (value, region_map) } @@ -580,7 +592,8 @@ impl<'tcx> TyCtxt<'tcx> { if !value.has_escaping_bound_vars() { value } else { - let mut replacer = BoundVarReplacer::new(self, &mut fld_r, &mut fld_t, &mut fld_c); + let mut replacer = + BoundVarReplacer::new(self, Some(&mut fld_r), Some(&mut fld_t), Some(&mut fld_c)); value.fold_with(&mut replacer) } } @@ -590,7 +603,7 @@ impl<'tcx> TyCtxt<'tcx> { /// types. pub fn replace_bound_vars( self, - value: Binder, + value: Binder<'tcx, T>, mut fld_r: F, fld_t: G, fld_c: H, @@ -609,7 +622,11 @@ impl<'tcx> TyCtxt<'tcx> { /// Replaces any late-bound regions bound in `value` with /// free variants attached to `all_outlive_scope`. - pub fn liberate_late_bound_regions(self, all_outlive_scope: DefId, value: ty::Binder) -> T + pub fn liberate_late_bound_regions( + self, + all_outlive_scope: DefId, + value: ty::Binder<'tcx, T>, + ) -> T where T: TypeFoldable<'tcx>, { @@ -622,13 +639,49 @@ impl<'tcx> TyCtxt<'tcx> { .0 } + pub fn shift_bound_var_indices(self, bound_vars: usize, value: T) -> T + where + T: TypeFoldable<'tcx>, + { + self.replace_escaping_bound_vars( + value, + |r| { + self.mk_region(ty::ReLateBound( + ty::INNERMOST, + ty::BoundRegion { + var: ty::BoundVar::from_usize(r.var.as_usize() + bound_vars), + kind: r.kind, + }, + )) + }, + |t| { + self.mk_ty(ty::Bound( + ty::INNERMOST, + ty::BoundTy { + var: ty::BoundVar::from_usize(t.var.as_usize() + bound_vars), + kind: t.kind, + }, + )) + }, + |c, ty| { + self.mk_const(ty::Const { + val: ty::ConstKind::Bound( + ty::INNERMOST, + ty::BoundVar::from_usize(c.as_usize() + bound_vars), + ), + ty, + }) + }, + ) + } + /// Returns a set of all late-bound regions that are constrained /// by `value`, meaning that if we instantiate those LBR with /// variables and equate `value` with something else, those /// variables will also be equated. pub fn collect_constrained_late_bound_regions( self, - value: &Binder, + value: &Binder<'tcx, T>, ) -> FxHashSet where T: TypeFoldable<'tcx>, @@ -639,7 +692,7 @@ impl<'tcx> TyCtxt<'tcx> { /// Returns a set of all late-bound regions that appear in `value` anywhere. pub fn collect_referenced_late_bound_regions( self, - value: &Binder, + value: &Binder<'tcx, T>, ) -> FxHashSet where T: TypeFoldable<'tcx>, @@ -649,7 +702,7 @@ impl<'tcx> TyCtxt<'tcx> { fn collect_late_bound_regions( self, - value: &Binder, + value: &Binder<'tcx, T>, just_constraint: bool, ) -> FxHashSet where @@ -663,7 +716,7 @@ impl<'tcx> TyCtxt<'tcx> { /// Replaces any late-bound regions bound in `value` with `'erased`. Useful in codegen but also /// method lookup and a few other places where precise region relationships are not required. - pub fn erase_late_bound_regions(self, value: Binder) -> T + pub fn erase_late_bound_regions(self, value: Binder<'tcx, T>) -> T where T: TypeFoldable<'tcx>, { @@ -678,20 +731,205 @@ impl<'tcx> TyCtxt<'tcx> { /// `FnSig`s or `TraitRef`s which are equivalent up to region naming will become /// structurally identical. For example, `for<'a, 'b> fn(&'a isize, &'b isize)` and /// `for<'a, 'b> fn(&'b isize, &'a isize)` will become identical after anonymization. - pub fn anonymize_late_bound_regions(self, sig: Binder) -> Binder + pub fn anonymize_late_bound_regions(self, sig: Binder<'tcx, T>) -> Binder<'tcx, T> where T: TypeFoldable<'tcx>, { let mut counter = 0; - Binder::bind( - self.replace_late_bound_regions(sig, |_| { - let br = ty::BoundRegion { kind: ty::BrAnon(counter) }; + let inner = self + .replace_late_bound_regions(sig, |_| { + let br = ty::BoundRegion { + var: ty::BoundVar::from_u32(counter), + kind: ty::BrAnon(counter), + }; let r = self.mk_region(ty::ReLateBound(ty::INNERMOST, br)); counter += 1; r }) - .0, - ) + .0; + let bound_vars = self.mk_bound_variable_kinds( + (0..counter).map(|i| ty::BoundVariableKind::Region(ty::BrAnon(i))), + ); + Binder::bind_with_vars(inner, bound_vars) + } +} + +pub struct BoundVarsCollector<'tcx> { + binder_index: ty::DebruijnIndex, + vars: BTreeMap, + // We may encounter the same variable at different levels of binding, so + // this can't just be `Ty` + visited: SsoHashSet<(ty::DebruijnIndex, Ty<'tcx>)>, +} + +impl<'tcx> BoundVarsCollector<'tcx> { + pub fn new() -> Self { + BoundVarsCollector { + binder_index: ty::INNERMOST, + vars: BTreeMap::new(), + visited: SsoHashSet::default(), + } + } + + pub fn into_vars(self, tcx: TyCtxt<'tcx>) -> &'tcx ty::List { + let max = self.vars.iter().map(|(k, _)| *k).max().unwrap_or_else(|| 0); + for i in 0..max { + if let None = self.vars.get(&i) { + panic!("Unknown variable: {:?}", i); + } + } + + tcx.mk_bound_variable_kinds(self.vars.into_iter().map(|(_, v)| v)) + } +} + +impl<'tcx> TypeVisitor<'tcx> for BoundVarsCollector<'tcx> { + type BreakTy = (); + + fn visit_binder>( + &mut self, + t: &Binder<'tcx, T>, + ) -> ControlFlow { + self.binder_index.shift_in(1); + let result = t.super_visit_with(self); + self.binder_index.shift_out(1); + result + } + + fn visit_ty(&mut self, t: Ty<'tcx>) -> ControlFlow { + if t.outer_exclusive_binder < self.binder_index + || !self.visited.insert((self.binder_index, t)) + { + return ControlFlow::CONTINUE; + } + use std::collections::btree_map::Entry; + match *t.kind() { + ty::Bound(debruijn, bound_ty) if debruijn == self.binder_index => { + match self.vars.entry(bound_ty.var.as_u32()) { + Entry::Vacant(entry) => { + entry.insert(ty::BoundVariableKind::Ty(bound_ty.kind)); + } + Entry::Occupied(entry) => match entry.get() { + ty::BoundVariableKind::Ty(_) => {} + _ => bug!("Conflicting bound vars"), + }, + } + } + + _ => (), + }; + + t.super_visit_with(self) + } + + fn visit_region(&mut self, r: ty::Region<'tcx>) -> ControlFlow { + match r { + ty::ReLateBound(index, _br) if *index == self.binder_index => { + // If you hit this, you should be using `Binder::bind_with_vars` or `Binder::rebind` + bug!("Trying to collect bound vars with a bound region: {:?} {:?}", index, _br) + } + + _ => (), + }; + + r.super_visit_with(self) + } +} + +pub struct ValidateBoundVars<'tcx> { + bound_vars: &'tcx ty::List, + binder_index: ty::DebruijnIndex, + // We may encounter the same variable at different levels of binding, so + // this can't just be `Ty` + visited: SsoHashSet<(ty::DebruijnIndex, Ty<'tcx>)>, +} + +impl<'tcx> ValidateBoundVars<'tcx> { + pub fn new(bound_vars: &'tcx ty::List) -> Self { + ValidateBoundVars { + bound_vars, + binder_index: ty::INNERMOST, + visited: SsoHashSet::default(), + } + } +} + +impl<'tcx> TypeVisitor<'tcx> for ValidateBoundVars<'tcx> { + type BreakTy = (); + + fn visit_binder>( + &mut self, + t: &Binder<'tcx, T>, + ) -> ControlFlow { + self.binder_index.shift_in(1); + let result = t.super_visit_with(self); + self.binder_index.shift_out(1); + result + } + + fn visit_ty(&mut self, t: Ty<'tcx>) -> ControlFlow { + if t.outer_exclusive_binder < self.binder_index + || !self.visited.insert((self.binder_index, t)) + { + return ControlFlow::BREAK; + } + match *t.kind() { + ty::Bound(debruijn, bound_ty) if debruijn == self.binder_index => { + if self.bound_vars.len() <= bound_ty.var.as_usize() { + bug!("Not enough bound vars: {:?} not found in {:?}", t, self.bound_vars); + } + let list_var = self.bound_vars[bound_ty.var.as_usize()]; + match list_var { + ty::BoundVariableKind::Ty(kind) => { + if kind != bound_ty.kind { + bug!( + "Mismatched type kinds: {:?} doesn't var in list {:?}", + bound_ty.kind, + list_var + ); + } + } + _ => { + bug!("Mismatched bound variable kinds! Expected type, found {:?}", list_var) + } + } + } + + _ => (), + }; + + t.super_visit_with(self) + } + + fn visit_region(&mut self, r: ty::Region<'tcx>) -> ControlFlow { + match r { + ty::ReLateBound(index, br) if *index == self.binder_index => { + if self.bound_vars.len() <= br.var.as_usize() { + bug!("Not enough bound vars: {:?} not found in {:?}", *br, self.bound_vars); + } + let list_var = self.bound_vars[br.var.as_usize()]; + match list_var { + ty::BoundVariableKind::Region(kind) => { + if kind != br.kind { + bug!( + "Mismatched region kinds: {:?} doesn't match var ({:?}) in list ({:?})", + br.kind, + list_var, + self.bound_vars + ); + } + } + _ => bug!( + "Mismatched bound variable kinds! Expected region, found {:?}", + list_var + ), + } + } + + _ => (), + }; + + r.super_visit_with(self) } } @@ -721,7 +959,10 @@ impl TypeFolder<'tcx> for Shifter<'tcx> { self.tcx } - fn fold_binder>(&mut self, t: ty::Binder) -> ty::Binder { + fn fold_binder>( + &mut self, + t: ty::Binder<'tcx, T>, + ) -> ty::Binder<'tcx, T> { self.current_index.shift_in(1); let t = t.super_fold_with(self); self.current_index.shift_out(1); @@ -830,7 +1071,10 @@ struct HasEscapingVarsVisitor { impl<'tcx> TypeVisitor<'tcx> for HasEscapingVarsVisitor { type BreakTy = FoundEscapingVars; - fn visit_binder>(&mut self, t: &Binder) -> ControlFlow { + fn visit_binder>( + &mut self, + t: &Binder<'tcx, T>, + ) -> ControlFlow { self.outer_index.shift_in(1); let result = t.super_visit_with(self); self.outer_index.shift_out(1); @@ -976,7 +1220,10 @@ impl LateBoundRegionsCollector { } impl<'tcx> TypeVisitor<'tcx> for LateBoundRegionsCollector { - fn visit_binder>(&mut self, t: &Binder) -> ControlFlow { + fn visit_binder>( + &mut self, + t: &Binder<'tcx, T>, + ) -> ControlFlow { self.current_index.shift_in(1); let result = t.super_visit_with(self); self.current_index.shift_out(1); diff --git a/compiler/rustc_middle/src/ty/generics.rs b/compiler/rustc_middle/src/ty/generics.rs new file mode 100644 index 0000000000000..d30a8693959f3 --- /dev/null +++ b/compiler/rustc_middle/src/ty/generics.rs @@ -0,0 +1,261 @@ +use crate::middle::resolve_lifetime::ObjectLifetimeDefault; +use crate::ty; +use crate::ty::subst::{Subst, SubstsRef}; +use rustc_ast as ast; +use rustc_data_structures::fx::FxHashMap; +use rustc_hir as hir; +use rustc_hir::def_id::DefId; +use rustc_span::symbol::Symbol; +use rustc_span::Span; + +use super::{EarlyBoundRegion, InstantiatedPredicates, ParamConst, ParamTy, Predicate, TyCtxt}; + +#[derive(Clone, Debug, TyEncodable, TyDecodable, HashStable)] +pub enum GenericParamDefKind { + Lifetime, + Type { + has_default: bool, + object_lifetime_default: ObjectLifetimeDefault, + synthetic: Option, + }, + Const { + has_default: bool, + }, +} + +impl GenericParamDefKind { + pub fn descr(&self) -> &'static str { + match self { + GenericParamDefKind::Lifetime => "lifetime", + GenericParamDefKind::Type { .. } => "type", + GenericParamDefKind::Const { .. } => "constant", + } + } + pub fn to_ord(&self, tcx: TyCtxt<'_>) -> ast::ParamKindOrd { + match self { + GenericParamDefKind::Lifetime => ast::ParamKindOrd::Lifetime, + GenericParamDefKind::Type { .. } => ast::ParamKindOrd::Type, + GenericParamDefKind::Const { .. } => { + ast::ParamKindOrd::Const { unordered: tcx.features().const_generics } + } + } + } +} + +#[derive(Clone, Debug, TyEncodable, TyDecodable, HashStable)] +pub struct GenericParamDef { + pub name: Symbol, + pub def_id: DefId, + pub index: u32, + + /// `pure_wrt_drop`, set by the (unsafe) `#[may_dangle]` attribute + /// on generic parameter `'a`/`T`, asserts data behind the parameter + /// `'a`/`T` won't be accessed during the parent type's `Drop` impl. + pub pure_wrt_drop: bool, + + pub kind: GenericParamDefKind, +} + +impl GenericParamDef { + pub fn to_early_bound_region_data(&self) -> ty::EarlyBoundRegion { + if let GenericParamDefKind::Lifetime = self.kind { + ty::EarlyBoundRegion { def_id: self.def_id, index: self.index, name: self.name } + } else { + bug!("cannot convert a non-lifetime parameter def to an early bound region") + } + } +} + +#[derive(Default)] +pub struct GenericParamCount { + pub lifetimes: usize, + pub types: usize, + pub consts: usize, +} + +/// Information about the formal type/lifetime parameters associated +/// with an item or method. Analogous to `hir::Generics`. +/// +/// The ordering of parameters is the same as in `Subst` (excluding child generics): +/// `Self` (optionally), `Lifetime` params..., `Type` params... +#[derive(Clone, Debug, TyEncodable, TyDecodable, HashStable)] +pub struct Generics { + pub parent: Option, + pub parent_count: usize, + pub params: Vec, + + /// Reverse map to the `index` field of each `GenericParamDef`. + #[stable_hasher(ignore)] + pub param_def_id_to_index: FxHashMap, + + pub has_self: bool, + pub has_late_bound_regions: Option, +} + +impl<'tcx> Generics { + pub fn count(&self) -> usize { + self.parent_count + self.params.len() + } + + pub fn own_counts(&self) -> GenericParamCount { + // We could cache this as a property of `GenericParamCount`, but + // the aim is to refactor this away entirely eventually and the + // presence of this method will be a constant reminder. + let mut own_counts = GenericParamCount::default(); + + for param in &self.params { + match param.kind { + GenericParamDefKind::Lifetime => own_counts.lifetimes += 1, + GenericParamDefKind::Type { .. } => own_counts.types += 1, + GenericParamDefKind::Const { .. } => own_counts.consts += 1, + } + } + + own_counts + } + + pub fn own_defaults(&self) -> GenericParamCount { + let mut own_defaults = GenericParamCount::default(); + + for param in &self.params { + match param.kind { + GenericParamDefKind::Lifetime => (), + GenericParamDefKind::Type { has_default, .. } => { + own_defaults.types += has_default as usize; + } + GenericParamDefKind::Const { has_default } => { + own_defaults.consts += has_default as usize; + } + } + } + + own_defaults + } + + pub fn requires_monomorphization(&self, tcx: TyCtxt<'tcx>) -> bool { + if self.own_requires_monomorphization() { + return true; + } + + if let Some(parent_def_id) = self.parent { + let parent = tcx.generics_of(parent_def_id); + parent.requires_monomorphization(tcx) + } else { + false + } + } + + pub fn own_requires_monomorphization(&self) -> bool { + for param in &self.params { + match param.kind { + GenericParamDefKind::Type { .. } | GenericParamDefKind::Const { .. } => { + return true; + } + GenericParamDefKind::Lifetime => {} + } + } + false + } + + /// Returns the `GenericParamDef` with the given index. + pub fn param_at(&'tcx self, param_index: usize, tcx: TyCtxt<'tcx>) -> &'tcx GenericParamDef { + if let Some(index) = param_index.checked_sub(self.parent_count) { + &self.params[index] + } else { + tcx.generics_of(self.parent.expect("parent_count > 0 but no parent?")) + .param_at(param_index, tcx) + } + } + + /// Returns the `GenericParamDef` associated with this `EarlyBoundRegion`. + pub fn region_param( + &'tcx self, + param: &EarlyBoundRegion, + tcx: TyCtxt<'tcx>, + ) -> &'tcx GenericParamDef { + let param = self.param_at(param.index as usize, tcx); + match param.kind { + GenericParamDefKind::Lifetime => param, + _ => bug!("expected lifetime parameter, but found another generic parameter"), + } + } + + /// Returns the `GenericParamDef` associated with this `ParamTy`. + pub fn type_param(&'tcx self, param: &ParamTy, tcx: TyCtxt<'tcx>) -> &'tcx GenericParamDef { + let param = self.param_at(param.index as usize, tcx); + match param.kind { + GenericParamDefKind::Type { .. } => param, + _ => bug!("expected type parameter, but found another generic parameter"), + } + } + + /// Returns the `GenericParamDef` associated with this `ParamConst`. + pub fn const_param(&'tcx self, param: &ParamConst, tcx: TyCtxt<'tcx>) -> &GenericParamDef { + let param = self.param_at(param.index as usize, tcx); + match param.kind { + GenericParamDefKind::Const { .. } => param, + _ => bug!("expected const parameter, but found another generic parameter"), + } + } +} + +/// Bounds on generics. +#[derive(Copy, Clone, Default, Debug, TyEncodable, TyDecodable, HashStable)] +pub struct GenericPredicates<'tcx> { + pub parent: Option, + pub predicates: &'tcx [(Predicate<'tcx>, Span)], +} + +impl<'tcx> GenericPredicates<'tcx> { + pub fn instantiate( + &self, + tcx: TyCtxt<'tcx>, + substs: SubstsRef<'tcx>, + ) -> InstantiatedPredicates<'tcx> { + let mut instantiated = InstantiatedPredicates::empty(); + self.instantiate_into(tcx, &mut instantiated, substs); + instantiated + } + + pub fn instantiate_own( + &self, + tcx: TyCtxt<'tcx>, + substs: SubstsRef<'tcx>, + ) -> InstantiatedPredicates<'tcx> { + InstantiatedPredicates { + predicates: self.predicates.iter().map(|(p, _)| p.subst(tcx, substs)).collect(), + spans: self.predicates.iter().map(|(_, sp)| *sp).collect(), + } + } + + fn instantiate_into( + &self, + tcx: TyCtxt<'tcx>, + instantiated: &mut InstantiatedPredicates<'tcx>, + substs: SubstsRef<'tcx>, + ) { + if let Some(def_id) = self.parent { + tcx.predicates_of(def_id).instantiate_into(tcx, instantiated, substs); + } + instantiated.predicates.extend(self.predicates.iter().map(|(p, _)| p.subst(tcx, substs))); + instantiated.spans.extend(self.predicates.iter().map(|(_, sp)| *sp)); + } + + pub fn instantiate_identity(&self, tcx: TyCtxt<'tcx>) -> InstantiatedPredicates<'tcx> { + let mut instantiated = InstantiatedPredicates::empty(); + self.instantiate_identity_into(tcx, &mut instantiated); + instantiated + } + + fn instantiate_identity_into( + &self, + tcx: TyCtxt<'tcx>, + instantiated: &mut InstantiatedPredicates<'tcx>, + ) { + if let Some(def_id) = self.parent { + tcx.predicates_of(def_id).instantiate_identity_into(tcx, instantiated); + } + instantiated.predicates.extend(self.predicates.iter().map(|(p, _)| p)); + instantiated.spans.extend(self.predicates.iter().map(|(_, s)| s)); + } +} diff --git a/compiler/rustc_middle/src/ty/instance.rs b/compiler/rustc_middle/src/ty/instance.rs index de012a6957419..41d953216e0dd 100644 --- a/compiler/rustc_middle/src/ty/instance.rs +++ b/compiler/rustc_middle/src/ty/instance.rs @@ -216,9 +216,10 @@ impl<'tcx> InstanceDef<'tcx> { // drops of `Option::None` before LTO. We also respect the intent of // `#[inline]` on `Drop::drop` implementations. return ty.ty_adt_def().map_or(true, |adt_def| { - adt_def.destructor(tcx).map_or(adt_def.is_enum(), |dtor| { - tcx.codegen_fn_attrs(dtor.did).requests_inline() - }) + adt_def.destructor(tcx).map_or_else( + || adt_def.is_enum(), + |dtor| tcx.codegen_fn_attrs(dtor.did).requests_inline(), + ) }); } tcx.codegen_fn_attrs(self.def_id()).requests_inline() @@ -482,6 +483,7 @@ impl<'tcx> Instance<'tcx> { if let Some(substs) = self.substs_for_mir_body() { v.subst(tcx, substs) } else { *v } } + #[inline(always)] pub fn subst_mir_and_normalize_erasing_regions( &self, tcx: TyCtxt<'tcx>, @@ -499,7 +501,7 @@ impl<'tcx> Instance<'tcx> { } /// Returns a new `Instance` where generic parameters in `instance.substs` are replaced by - /// identify parameters if they are determined to be unused in `instance.def`. + /// identity parameters if they are determined to be unused in `instance.def`. pub fn polymorphize(self, tcx: TyCtxt<'tcx>) -> Self { debug!("polymorphize: running polymorphization analysis"); if !tcx.sess.opts.debugging_opts.polymorphize { @@ -593,7 +595,7 @@ fn polymorphize<'tcx>( }, // Simple case: If parameter is a const or type parameter.. - ty::GenericParamDefKind::Const | ty::GenericParamDefKind::Type { .. } if + ty::GenericParamDefKind::Const { .. } | ty::GenericParamDefKind::Type { .. } if // ..and is within range and unused.. unused.contains(param.index).unwrap_or(false) => // ..then use the identity for this parameter. diff --git a/compiler/rustc_middle/src/ty/layout.rs b/compiler/rustc_middle/src/ty/layout.rs index 6d2ab0e5f5a80..c2e9dba6c8e8a 100644 --- a/compiler/rustc_middle/src/ty/layout.rs +++ b/compiler/rustc_middle/src/ty/layout.rs @@ -1,3 +1,4 @@ +// ignore-tidy-filelength use crate::ich::StableHashingContext; use crate::middle::codegen_fn_attrs::CodegenFnAttrFlags; use crate::mir::{GeneratorLayout, GeneratorSavedLocal}; @@ -11,7 +12,7 @@ use rustc_hir as hir; use rustc_hir::lang_items::LangItem; use rustc_index::bit_set::BitSet; use rustc_index::vec::{Idx, IndexVec}; -use rustc_session::{DataTypeKind, FieldInfo, SizeKind, VariantInfo}; +use rustc_session::{config::OptLevel, DataTypeKind, FieldInfo, SizeKind, VariantInfo}; use rustc_span::symbol::{Ident, Symbol}; use rustc_span::DUMMY_SP; use rustc_target::abi::call::{ @@ -1251,13 +1252,13 @@ impl<'tcx> LayoutCx<'tcx, TyCtxt<'tcx>> { } else { // Try to use a ScalarPair for all tagged enums. let mut common_prim = None; - for (field_layouts, layout_variant) in variants.iter().zip(&layout_variants) { + for (field_layouts, layout_variant) in iter::zip(&variants, &layout_variants) { let offsets = match layout_variant.fields { FieldsShape::Arbitrary { ref offsets, .. } => offsets, _ => bug!(), }; let mut fields = - field_layouts.iter().zip(offsets).filter(|p| !p.0.is_zst()); + iter::zip(field_layouts, offsets).filter(|p| !p.0.is_zst()); let (field, offset) = match (fields.next(), fields.next()) { (None, None) => continue, (Some(pair), None) => pair, @@ -1626,7 +1627,7 @@ impl<'tcx> LayoutCx<'tcx, TyCtxt<'tcx>> { const INVALID_FIELD_IDX: u32 = !0; let mut combined_inverse_memory_index = vec![INVALID_FIELD_IDX; promoted_memory_index.len() + memory_index.len()]; - let mut offsets_and_memory_index = offsets.into_iter().zip(memory_index); + let mut offsets_and_memory_index = iter::zip(offsets, memory_index); let combined_offsets = variant_fields .iter() .enumerate() @@ -2318,31 +2319,30 @@ where ty::Ref(_, ty, mt) if offset.bytes() == 0 => { let address_space = addr_space_of_ty(ty); let tcx = cx.tcx(); - let is_freeze = ty.is_freeze(tcx.at(DUMMY_SP), cx.param_env()); - let kind = match mt { - hir::Mutability::Not => { - if is_freeze { - PointerKind::Frozen - } else { - PointerKind::Shared + let kind = if tcx.sess.opts.optimize == OptLevel::No { + // Use conservative pointer kind if not optimizing. This saves us the + // Freeze/Unpin queries, and can save time in the codegen backend (noalias + // attributes in LLVM have compile-time cost even in unoptimized builds). + PointerKind::Shared + } else { + match mt { + hir::Mutability::Not => { + if ty.is_freeze(tcx.at(DUMMY_SP), cx.param_env()) { + PointerKind::Frozen + } else { + PointerKind::Shared + } } - } - hir::Mutability::Mut => { - // Previously we would only emit noalias annotations for LLVM >= 6 or in - // panic=abort mode. That was deemed right, as prior versions had many bugs - // in conjunction with unwinding, but later versions didn’t seem to have - // said issues. See issue #31681. - // - // Alas, later on we encountered a case where noalias would generate wrong - // code altogether even with recent versions of LLVM in *safe* code with no - // unwinding involved. See #54462. - // - // For now, do not enable mutable_noalias by default at all, while the - // issue is being figured out. - if tcx.sess.opts.debugging_opts.mutable_noalias { - PointerKind::UniqueBorrowed - } else { - PointerKind::Shared + hir::Mutability::Mut => { + // References to self-referential structures should not be considered + // noalias, as another pointer to the structure can be obtained, that + // is not based-on the original reference. We consider all !Unpin + // types to be potentially self-referential here. + if ty.is_unpin(tcx.at(DUMMY_SP), cx.param_env()) { + PointerKind::UniqueBorrowed + } else { + PointerKind::Shared + } } } }; @@ -2482,21 +2482,42 @@ impl<'tcx> ty::Instance<'tcx> { ty::Closure(def_id, substs) => { let sig = substs.as_closure().sig(); - let env_ty = tcx.closure_env_ty(def_id, substs).unwrap(); - sig.map_bound(|sig| { + let bound_vars = tcx.mk_bound_variable_kinds( + sig.bound_vars() + .iter() + .chain(iter::once(ty::BoundVariableKind::Region(ty::BrEnv))), + ); + let br = ty::BoundRegion { + var: ty::BoundVar::from_usize(bound_vars.len() - 1), + kind: ty::BoundRegionKind::BrEnv, + }; + let env_region = ty::ReLateBound(ty::INNERMOST, br); + let env_ty = tcx.closure_env_ty(def_id, substs, env_region).unwrap(); + + let sig = sig.skip_binder(); + ty::Binder::bind_with_vars( tcx.mk_fn_sig( - iter::once(env_ty.skip_binder()).chain(sig.inputs().iter().cloned()), + iter::once(env_ty).chain(sig.inputs().iter().cloned()), sig.output(), sig.c_variadic, sig.unsafety, sig.abi, - ) - }) + ), + bound_vars, + ) } ty::Generator(_, substs, _) => { let sig = substs.as_generator().poly_sig(); - let br = ty::BoundRegion { kind: ty::BrEnv }; + let bound_vars = tcx.mk_bound_variable_kinds( + sig.bound_vars() + .iter() + .chain(iter::once(ty::BoundVariableKind::Region(ty::BrEnv))), + ); + let br = ty::BoundRegion { + var: ty::BoundVar::from_usize(bound_vars.len() - 1), + kind: ty::BoundRegionKind::BrEnv, + }; let env_region = ty::ReLateBound(ty::INNERMOST, br); let env_ty = tcx.mk_mut_ref(tcx.mk_region(env_region), ty); @@ -2505,21 +2526,21 @@ impl<'tcx> ty::Instance<'tcx> { let pin_substs = tcx.intern_substs(&[env_ty.into()]); let env_ty = tcx.mk_adt(pin_adt_ref, pin_substs); - sig.map_bound(|sig| { - let state_did = tcx.require_lang_item(LangItem::GeneratorState, None); - let state_adt_ref = tcx.adt_def(state_did); - let state_substs = - tcx.intern_substs(&[sig.yield_ty.into(), sig.return_ty.into()]); - let ret_ty = tcx.mk_adt(state_adt_ref, state_substs); - + let sig = sig.skip_binder(); + let state_did = tcx.require_lang_item(LangItem::GeneratorState, None); + let state_adt_ref = tcx.adt_def(state_did); + let state_substs = tcx.intern_substs(&[sig.yield_ty.into(), sig.return_ty.into()]); + let ret_ty = tcx.mk_adt(state_adt_ref, state_substs); + ty::Binder::bind_with_vars( tcx.mk_fn_sig( [env_ty, sig.resume_ty].iter(), &ret_ty, false, hir::Unsafety::Normal, rustc_target::spec::abi::Abi::Rust, - ) - }) + ), + bound_vars, + ) } _ => bug!("unexpected type {:?} in Instance::fn_sig", ty), } @@ -2562,6 +2583,7 @@ fn fn_can_unwind( panic_strategy: PanicStrategy, codegen_fn_attr_flags: CodegenFnAttrFlags, call_conv: Conv, + abi: SpecAbi, ) -> bool { if panic_strategy != PanicStrategy::Unwind { // In panic=abort mode we assume nothing can unwind anywhere, so @@ -2586,17 +2608,35 @@ fn fn_can_unwind( // // 2. A Rust item using a non-Rust ABI (like `extern "C" fn foo() { ... }`). // - // Foreign items (case 1) are assumed to not unwind; it is - // UB otherwise. (At least for now; see also - // rust-lang/rust#63909 and Rust RFC 2753.) - // - // Items defined in Rust with non-Rust ABIs (case 2) are also - // not supposed to unwind. Whether this should be enforced - // (versus stating it is UB) and *how* it would be enforced - // is currently under discussion; see rust-lang/rust#58794. - // - // In either case, we mark item as explicitly nounwind. - false + // In both of these cases, we should refer to the ABI to determine whether or not we + // should unwind. See Rust RFC 2945 for more information on this behavior, here: + // https://github.com/rust-lang/rfcs/blob/master/text/2945-c-unwind-abi.md + use SpecAbi::*; + match abi { + C { unwind } | Stdcall { unwind } | System { unwind } | Thiscall { unwind } => { + unwind + } + Cdecl + | Fastcall + | Vectorcall + | Aapcs + | Win64 + | SysV64 + | PtxKernel + | Msp430Interrupt + | X86Interrupt + | AmdGpuKernel + | EfiApi + | AvrInterrupt + | AvrNonBlockingInterrupt + | CCmseNonSecureCall + | Wasm + | RustIntrinsic + | PlatformIntrinsic + | Unadjusted => false, + // In the `if` above, we checked for functions with the Rust calling convention. + Rust | RustCall => unreachable!(), + } } } } @@ -2654,14 +2694,14 @@ where RustIntrinsic | PlatformIntrinsic | Rust | RustCall => Conv::Rust, // It's the ABI's job to select this, not ours. - System => bug!("system abi should be selected elsewhere"), + System { .. } => bug!("system abi should be selected elsewhere"), EfiApi => bug!("eficall abi should be selected elsewhere"), - Stdcall => Conv::X86Stdcall, + Stdcall { .. } => Conv::X86Stdcall, Fastcall => Conv::X86Fastcall, Vectorcall => Conv::X86VectorCall, - Thiscall => Conv::X86ThisCall, - C => Conv::C, + Thiscall { .. } => Conv::X86ThisCall, + C { .. } => Conv::C, Unadjusted => Conv::C, Win64 => Conv::X86_64Win64, SysV64 => Conv::X86_64SysV, @@ -2673,6 +2713,7 @@ where AmdGpuKernel => Conv::AmdGpuKernel, AvrInterrupt => Conv::AvrInterrupt, AvrNonBlockingInterrupt => Conv::AvrNonBlockingInterrupt, + Wasm => Conv::C, // These API constants ought to be more specific... Cdecl => Conv::C, @@ -2757,10 +2798,14 @@ where // and can be marked as both `readonly` and `noalias`, as // LLVM's definition of `noalias` is based solely on memory // dependencies rather than pointer equality + // + // Due to miscompiles in LLVM < 12, we apply a separate NoAliasMutRef attribute + // for UniqueBorrowed arguments, so that the codegen backend can decide + // whether or not to actually emit the attribute. let no_alias = match kind { - PointerKind::Shared => false, + PointerKind::Shared | PointerKind::UniqueBorrowed => false, PointerKind::UniqueOwned => true, - PointerKind::Frozen | PointerKind::UniqueBorrowed => !is_return, + PointerKind::Frozen => !is_return, }; if no_alias { attrs.set(ArgAttribute::NoAlias); @@ -2769,6 +2814,10 @@ where if kind == PointerKind::Frozen && !is_return { attrs.set(ArgAttribute::ReadOnly); } + + if kind == PointerKind::UniqueBorrowed && !is_return { + attrs.set(ArgAttribute::NoAliasMutRef); + } } } }; @@ -2823,7 +2872,12 @@ where c_variadic: sig.c_variadic, fixed_count: inputs.len(), conv, - can_unwind: fn_can_unwind(cx.tcx().sess.panic_strategy(), codegen_fn_attr_flags, conv), + can_unwind: fn_can_unwind( + cx.tcx().sess.panic_strategy(), + codegen_fn_attr_flags, + conv, + sig.abi, + ), }; fn_abi.adjust_for_abi(cx, sig.abi); debug!("FnAbi::new_internal = {:?}", fn_abi); diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs index 2e077827873d1..6574c9382604b 100644 --- a/compiler/rustc_middle/src/ty/mod.rs +++ b/compiler/rustc_middle/src/ty/mod.rs @@ -9,105 +9,78 @@ //! //! ["The `ty` module: representing types"]: https://rustc-dev-guide.rust-lang.org/ty.html -// ignore-tidy-filelength pub use self::fold::{TypeFoldable, TypeFolder, TypeVisitor}; pub use self::AssocItemContainer::*; pub use self::BorrowKind::*; pub use self::IntVarValue::*; pub use self::Variance::*; +pub use adt::*; +pub use assoc::*; +pub use closure::*; +pub use generics::*; use crate::hir::exports::ExportMap; -use crate::hir::place::{ - Place as HirPlace, PlaceBase as HirPlaceBase, ProjectionKind as HirProjectionKind, -}; use crate::ich::StableHashingContext; use crate::middle::cstore::CrateStoreDyn; -use crate::middle::resolve_lifetime::ObjectLifetimeDefault; -use crate::mir::interpret::ErrorHandled; -use crate::mir::Body; -use crate::mir::GeneratorLayout; +use crate::mir::{Body, GeneratorLayout}; use crate::traits::{self, Reveal}; use crate::ty; use crate::ty::subst::{GenericArg, InternalSubsts, Subst, SubstsRef}; -use crate::ty::util::{Discr, IntTypeExt}; +use crate::ty::util::Discr; use rustc_ast as ast; use rustc_attr as attr; use rustc_data_structures::captures::Captures; -use rustc_data_structures::fingerprint::Fingerprint; -use rustc_data_structures::fx::FxHashMap; -use rustc_data_structures::fx::FxHashSet; -use rustc_data_structures::fx::FxIndexMap; -use rustc_data_structures::sorted_map::SortedIndexMultiMap; +use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; use rustc_data_structures::sync::{self, par_iter, ParallelIterator}; use rustc_data_structures::tagged_ptr::CopyTaggedPtr; -use rustc_errors::ErrorReported; use rustc_hir as hir; -use rustc_hir::def::{CtorKind, CtorOf, DefKind, Namespace, Res}; +use rustc_hir::def::{CtorKind, CtorOf, DefKind, Res}; use rustc_hir::def_id::{CrateNum, DefId, DefIdMap, LocalDefId, CRATE_DEF_INDEX}; -use rustc_hir::lang_items::LangItem; use rustc_hir::{Constness, Node}; -use rustc_index::vec::{Idx, IndexVec}; use rustc_macros::HashStable; -use rustc_serialize::{self, Encodable, Encoder}; -use rustc_session::DataTypeKind; use rustc_span::hygiene::ExpnId; -use rustc_span::symbol::{kw, sym, Ident, Symbol}; +use rustc_span::symbol::{kw, Ident, Symbol}; use rustc_span::Span; -use rustc_target::abi::{Align, VariantIdx}; +use rustc_target::abi::Align; -use std::cell::RefCell; use std::cmp::Ordering; -use std::fmt; use std::hash::{Hash, Hasher}; -use std::ops::{ControlFlow, Range}; -use std::ptr; -use std::str; +use std::ops::ControlFlow; +use std::{fmt, ptr, str}; -pub use self::sty::BoundRegionKind::*; -pub use self::sty::RegionKind; -pub use self::sty::RegionKind::*; -pub use self::sty::TyKind::*; -pub use self::sty::{Binder, BoundTy, BoundTyKind, BoundVar}; -pub use self::sty::{BoundRegion, BoundRegionKind, EarlyBoundRegion, FreeRegion, Region}; -pub use self::sty::{CanonicalPolyFnSig, FnSig, GenSig, PolyFnSig, PolyGenSig}; -pub use self::sty::{ClosureSubsts, GeneratorSubsts, TypeAndMut, UpvarSubsts}; -pub use self::sty::{ClosureSubstsParts, GeneratorSubstsParts}; -pub use self::sty::{ConstVid, RegionVid}; -pub use self::sty::{ExistentialPredicate, ParamConst, ParamTy, ProjectionTy}; -pub use self::sty::{ExistentialProjection, PolyExistentialProjection}; -pub use self::sty::{ExistentialTraitRef, PolyExistentialTraitRef}; -pub use self::sty::{PolyTraitRef, TraitRef, TyKind}; pub use crate::ty::diagnostics::*; pub use rustc_type_ir::InferTy::*; pub use rustc_type_ir::*; pub use self::binding::BindingMode; pub use self::binding::BindingMode::*; - -pub use self::context::{tls, FreeRegionInfo, TyCtxt}; -pub use self::context::{ - CanonicalUserType, CanonicalUserTypeAnnotation, CanonicalUserTypeAnnotations, - DelaySpanBugEmitted, ResolvedOpaqueTy, UserType, UserTypeAnnotationIndex, -}; +pub use self::consts::{Const, ConstInt, ConstKind, InferConst, ScalarInt, Unevaluated, ValTree}; pub use self::context::{ - CtxtInterners, GeneratorInteriorTypeCause, GlobalCtxt, Lift, TypeckResults, + tls, CanonicalUserType, CanonicalUserTypeAnnotation, CanonicalUserTypeAnnotations, + CtxtInterners, DelaySpanBugEmitted, FreeRegionInfo, GeneratorInteriorTypeCause, GlobalCtxt, + Lift, ResolvedOpaqueTy, TyCtxt, TypeckResults, UserType, UserTypeAnnotationIndex, }; - pub use self::instance::{Instance, InstanceDef}; - pub use self::list::List; - +pub use self::sty::BoundRegionKind::*; +pub use self::sty::RegionKind::*; +pub use self::sty::TyKind::*; +pub use self::sty::{ + Binder, BoundRegion, BoundRegionKind, BoundTy, BoundTyKind, BoundVar, BoundVariableKind, + CanonicalPolyFnSig, ClosureSubsts, ClosureSubstsParts, ConstVid, EarlyBoundRegion, + ExistentialPredicate, ExistentialProjection, ExistentialTraitRef, FnSig, FreeRegion, GenSig, + GeneratorSubsts, GeneratorSubstsParts, ParamConst, ParamTy, PolyExistentialProjection, + PolyExistentialTraitRef, PolyFnSig, PolyGenSig, PolyTraitRef, ProjectionTy, Region, RegionKind, + RegionVid, TraitRef, TyKind, TypeAndMut, UpvarSubsts, +}; pub use self::trait_def::TraitDef; -pub use self::consts::{Const, ConstInt, ConstKind, InferConst, ScalarInt}; - pub mod _match; pub mod adjustment; pub mod binding; pub mod cast; pub mod codec; -mod erase_regions; pub mod error; pub mod fast_reject; pub mod flags; @@ -124,9 +97,14 @@ pub mod trait_def; pub mod util; pub mod walk; +mod adt; +mod assoc; +mod closure; mod consts; mod context; mod diagnostics; +mod erase_regions; +mod generics; mod instance; mod list; mod structural_impls; @@ -148,30 +126,6 @@ pub struct ResolverOutputs { pub extern_prelude: FxHashMap, } -#[derive(Clone, Copy, PartialEq, Eq, Debug, HashStable, Hash)] -pub enum AssocItemContainer { - TraitContainer(DefId), - ImplContainer(DefId), -} - -impl AssocItemContainer { - /// Asserts that this is the `DefId` of an associated item declared - /// in a trait, and returns the trait `DefId`. - pub fn assert_trait(&self) -> DefId { - match *self { - TraitContainer(id) => id, - _ => bug!("associated item has wrong container type: {:?}", self), - } - } - - pub fn id(&self) -> DefId { - match *self { - TraitContainer(id) => id, - ImplContainer(id) => id, - } - } -} - /// The "header" of an impl is everything outside the body: a Self type, a trait /// ref (in the case of a trait impl), and a set of predicates (from the /// bounds / where-clauses). @@ -196,142 +150,6 @@ pub enum ImplPolarity { Reservation, } -#[derive(Copy, Clone, Debug, PartialEq, HashStable, Eq, Hash)] -pub struct AssocItem { - pub def_id: DefId, - #[stable_hasher(project(name))] - pub ident: Ident, - pub kind: AssocKind, - pub vis: Visibility, - pub defaultness: hir::Defaultness, - pub container: AssocItemContainer, - - /// Whether this is a method with an explicit self - /// as its first parameter, allowing method calls. - pub fn_has_self_parameter: bool, -} - -#[derive(Copy, Clone, PartialEq, Debug, HashStable, Eq, Hash)] -pub enum AssocKind { - Const, - Fn, - Type, -} - -impl AssocKind { - pub fn namespace(&self) -> Namespace { - match *self { - ty::AssocKind::Type => Namespace::TypeNS, - ty::AssocKind::Const | ty::AssocKind::Fn => Namespace::ValueNS, - } - } - - pub fn as_def_kind(&self) -> DefKind { - match self { - AssocKind::Const => DefKind::AssocConst, - AssocKind::Fn => DefKind::AssocFn, - AssocKind::Type => DefKind::AssocTy, - } - } -} - -impl AssocItem { - pub fn signature(&self, tcx: TyCtxt<'_>) -> String { - match self.kind { - ty::AssocKind::Fn => { - // We skip the binder here because the binder would deanonymize all - // late-bound regions, and we don't want method signatures to show up - // `as for<'r> fn(&'r MyType)`. Pretty-printing handles late-bound - // regions just fine, showing `fn(&MyType)`. - tcx.fn_sig(self.def_id).skip_binder().to_string() - } - ty::AssocKind::Type => format!("type {};", self.ident), - ty::AssocKind::Const => { - format!("const {}: {:?};", self.ident, tcx.type_of(self.def_id)) - } - } - } -} - -/// A list of `ty::AssocItem`s in definition order that allows for efficient lookup by name. -/// -/// When doing lookup by name, we try to postpone hygienic comparison for as long as possible since -/// it is relatively expensive. Instead, items are indexed by `Symbol` and hygienic comparison is -/// done only on items with the same name. -#[derive(Debug, Clone, PartialEq, HashStable)] -pub struct AssociatedItems<'tcx> { - items: SortedIndexMultiMap, -} - -impl<'tcx> AssociatedItems<'tcx> { - /// Constructs an `AssociatedItems` map from a series of `ty::AssocItem`s in definition order. - pub fn new(items_in_def_order: impl IntoIterator) -> Self { - let items = items_in_def_order.into_iter().map(|item| (item.ident.name, item)).collect(); - AssociatedItems { items } - } - - /// Returns a slice of associated items in the order they were defined. - /// - /// New code should avoid relying on definition order. If you need a particular associated item - /// for a known trait, make that trait a lang item instead of indexing this array. - pub fn in_definition_order(&self) -> impl '_ + Iterator { - self.items.iter().map(|(_, v)| *v) - } - - pub fn len(&self) -> usize { - self.items.len() - } - - /// Returns an iterator over all associated items with the given name, ignoring hygiene. - pub fn filter_by_name_unhygienic( - &self, - name: Symbol, - ) -> impl '_ + Iterator { - self.items.get_by_key(&name).copied() - } - - /// Returns an iterator over all associated items with the given name. - /// - /// Multiple items may have the same name if they are in different `Namespace`s. For example, - /// an associated type can have the same name as a method. Use one of the `find_by_name_and_*` - /// methods below if you know which item you are looking for. - pub fn filter_by_name( - &'a self, - tcx: TyCtxt<'a>, - ident: Ident, - parent_def_id: DefId, - ) -> impl 'a + Iterator { - self.filter_by_name_unhygienic(ident.name) - .filter(move |item| tcx.hygienic_eq(ident, item.ident, parent_def_id)) - } - - /// Returns the associated item with the given name and `AssocKind`, if one exists. - pub fn find_by_name_and_kind( - &self, - tcx: TyCtxt<'_>, - ident: Ident, - kind: AssocKind, - parent_def_id: DefId, - ) -> Option<&ty::AssocItem> { - self.filter_by_name_unhygienic(ident.name) - .filter(|item| item.kind == kind) - .find(|item| tcx.hygienic_eq(ident, item.ident, parent_def_id)) - } - - /// Returns the associated item with the given name in the given `Namespace`, if one exists. - pub fn find_by_name_and_namespace( - &self, - tcx: TyCtxt<'_>, - ident: Ident, - ns: Namespace, - parent_def_id: DefId, - ) -> Option<&ty::AssocItem> { - self.filter_by_name_unhygienic(ident.name) - .filter(|item| item.kind.namespace() == ns) - .find(|item| tcx.hygienic_eq(ident, item.ident, parent_def_id)) - } -} - #[derive(Clone, Debug, PartialEq, Eq, Copy, Hash, TyEncodable, TyDecodable, HashStable)] pub enum Visibility { /// Visible everywhere (including in other crates). @@ -483,8 +301,8 @@ impl<'tcx> TyS<'tcx> { } // `TyS` is used a lot. Make sure it doesn't unintentionally get bigger. -#[cfg(target_arch = "x86_64")] -static_assert_size!(TyS<'_>, 32); +#[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))] +static_assert_size!(TyS<'_>, 40); impl<'tcx> Ord for TyS<'tcx> { fn cmp(&self, other: &TyS<'tcx>) -> Ordering { @@ -531,243 +349,6 @@ impl<'a, 'tcx> HashStable> for TyS<'tcx> { #[rustc_diagnostic_item = "Ty"] pub type Ty<'tcx> = &'tcx TyS<'tcx>; -#[derive( - Clone, - Copy, - Debug, - PartialEq, - Eq, - Hash, - TyEncodable, - TyDecodable, - TypeFoldable, - HashStable -)] -pub struct UpvarPath { - pub hir_id: hir::HirId, -} - -/// Upvars do not get their own `NodeId`. Instead, we use the pair of -/// the original var ID (that is, the root variable that is referenced -/// by the upvar) and the ID of the closure expression. -#[derive(Clone, Copy, PartialEq, Eq, Hash, TyEncodable, TyDecodable, TypeFoldable, HashStable)] -pub struct UpvarId { - pub var_path: UpvarPath, - pub closure_expr_id: LocalDefId, -} - -impl UpvarId { - pub fn new(var_hir_id: hir::HirId, closure_def_id: LocalDefId) -> UpvarId { - UpvarId { var_path: UpvarPath { hir_id: var_hir_id }, closure_expr_id: closure_def_id } - } -} - -#[derive(Clone, PartialEq, Debug, TyEncodable, TyDecodable, TypeFoldable, Copy, HashStable)] -pub enum BorrowKind { - /// Data must be immutable and is aliasable. - ImmBorrow, - - /// Data must be immutable but not aliasable. This kind of borrow - /// cannot currently be expressed by the user and is used only in - /// implicit closure bindings. It is needed when the closure - /// is borrowing or mutating a mutable referent, e.g.: - /// - /// ``` - /// let x: &mut isize = ...; - /// let y = || *x += 5; - /// ``` - /// - /// If we were to try to translate this closure into a more explicit - /// form, we'd encounter an error with the code as written: - /// - /// ``` - /// struct Env { x: & &mut isize } - /// let x: &mut isize = ...; - /// let y = (&mut Env { &x }, fn_ptr); // Closure is pair of env and fn - /// fn fn_ptr(env: &mut Env) { **env.x += 5; } - /// ``` - /// - /// This is then illegal because you cannot mutate a `&mut` found - /// in an aliasable location. To solve, you'd have to translate with - /// an `&mut` borrow: - /// - /// ``` - /// struct Env { x: & &mut isize } - /// let x: &mut isize = ...; - /// let y = (&mut Env { &mut x }, fn_ptr); // changed from &x to &mut x - /// fn fn_ptr(env: &mut Env) { **env.x += 5; } - /// ``` - /// - /// Now the assignment to `**env.x` is legal, but creating a - /// mutable pointer to `x` is not because `x` is not mutable. We - /// could fix this by declaring `x` as `let mut x`. This is ok in - /// user code, if awkward, but extra weird for closures, since the - /// borrow is hidden. - /// - /// So we introduce a "unique imm" borrow -- the referent is - /// immutable, but not aliasable. This solves the problem. For - /// simplicity, we don't give users the way to express this - /// borrow, it's just used when translating closures. - UniqueImmBorrow, - - /// Data is mutable and not aliasable. - MutBorrow, -} - -/// 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> { - /// 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), - - /// 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>, -} - -/// 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. -pub type MinCaptureInformationMap<'tcx> = FxHashMap>; - -/// Part of `MinCaptureInformationMap`; Maps a root variable to the list of `CapturedPlace`. -/// Used to track the minimum set of `Place`s that need to be captured to support all -/// Places captured by the closure starting at a given root variable. -/// -/// This provides a convenient and quick way of checking if a variable being used within -/// a closure is a capture of a local variable. -pub type RootVariableMinCaptureList<'tcx> = FxIndexMap>; - -/// Part of `MinCaptureInformationMap`; List of `CapturePlace`s. -pub type MinCaptureList<'tcx> = Vec>; - -/// A composite describing a `Place` that is captured by a closure. -#[derive(PartialEq, Clone, Debug, TyEncodable, TyDecodable, TypeFoldable, HashStable)] -pub struct CapturedPlace<'tcx> { - /// The `Place` that is captured. - pub place: HirPlace<'tcx>, - - /// `CaptureKind` and expression(s) that resulted in such capture of `place`. - pub info: CaptureInfo<'tcx>, - - /// Represents if `place` can be mutated or not. - pub mutability: hir::Mutability, -} - -impl CapturedPlace<'tcx> { - /// Returns the hir-id of the root variable for the captured place. - /// e.g., if `a.b.c` was captured, would return the hir-id for `a`. - pub fn get_root_variable(&self) -> hir::HirId { - match self.place.base { - HirPlaceBase::Upvar(upvar_id) => upvar_id.var_path.hir_id, - base => bug!("Expected upvar, found={:?}", base), - } - } -} - -pub fn place_to_string_for_capture(tcx: TyCtxt<'tcx>, place: &HirPlace<'tcx>) -> String { - let name = match place.base { - HirPlaceBase::Upvar(upvar_id) => tcx.hir().name(upvar_id.var_path.hir_id).to_string(), - _ => bug!("Capture_information should only contain upvars"), - }; - let mut curr_string = name; - - for (i, proj) in place.projections.iter().enumerate() { - match proj.kind { - HirProjectionKind::Deref => { - curr_string = format!("*{}", curr_string); - } - HirProjectionKind::Field(idx, variant) => match place.ty_before_projection(i).kind() { - ty::Adt(def, ..) => { - curr_string = format!( - "{}.{}", - curr_string, - def.variants[variant].fields[idx as usize].ident.name.as_str() - ); - } - ty::Tuple(_) => { - curr_string = format!("{}.{}", curr_string, idx); - } - _ => { - bug!( - "Field projection applied to a type other than Adt or Tuple: {:?}.", - place.ty_before_projection(i).kind() - ) - } - }, - proj => bug!("{:?} unexpected because it isn't captured", proj), - } - } - - curr_string.to_string() -} - -/// Part of `MinCaptureInformationMap`; describes the capture kind (&, &mut, move) -/// 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> { - /// Expr Id pointing to use that resulted in selecting the current capture kind - /// - /// Eg: - /// ```rust,no_run - /// let mut t = (0,1); - /// - /// let c = || { - /// println!("{}",t); // L1 - /// t.1 = 4; // L2 - /// }; - /// ``` - /// `capture_kind_expr_id` will point to the use on L2 and `path_expr_id` will point to the - /// use on L1. - /// - /// If the user doesn't enable feature `capture_disjoint_fields` (RFC 2229) then, it is - /// possible that we don't see the use of a particular place resulting in capture_kind_expr_id being - /// None. In such case we fallback on uvpars_mentioned for span. - /// - /// Eg: - /// ```rust,no_run - /// let x = 5; - /// - /// let c = || { - /// let _ = x - /// }; - /// ``` - /// - /// In this example, if `capture_disjoint_fields` is **not** set, then x will be captured, - /// but we won't see it being used during capture analysis, since it's essentially a discard. - pub capture_kind_expr_id: Option, - /// Expr Id pointing to use that resulted the corresponding place being captured - /// - /// See `capture_kind_expr_id` for example. - /// - pub path_expr_id: Option, - - /// Capture mode that was selected - pub capture_kind: UpvarCapture<'tcx>, -} - -pub type UpvarListMap = FxHashMap>; -pub type UpvarCaptureMap<'tcx> = FxHashMap>; - impl ty::EarlyBoundRegion { /// Does this early bound region have a name? Early bound regions normally /// always have names except when using anonymous lifetimes (`'_`). @@ -776,262 +357,16 @@ impl ty::EarlyBoundRegion { } } -#[derive(Clone, Debug, TyEncodable, TyDecodable, HashStable)] -pub enum GenericParamDefKind { - Lifetime, - Type { - has_default: bool, - object_lifetime_default: ObjectLifetimeDefault, - synthetic: Option, - }, - Const, -} - -impl GenericParamDefKind { - pub fn descr(&self) -> &'static str { - match self { - GenericParamDefKind::Lifetime => "lifetime", - GenericParamDefKind::Type { .. } => "type", - GenericParamDefKind::Const => "constant", - } - } - pub fn to_ord(&self, tcx: TyCtxt<'_>) -> ast::ParamKindOrd { - match self { - GenericParamDefKind::Lifetime => ast::ParamKindOrd::Lifetime, - GenericParamDefKind::Type { .. } => ast::ParamKindOrd::Type, - GenericParamDefKind::Const => { - ast::ParamKindOrd::Const { unordered: tcx.features().const_generics } - } - } - } -} - -#[derive(Clone, Debug, TyEncodable, TyDecodable, HashStable)] -pub struct GenericParamDef { - pub name: Symbol, - pub def_id: DefId, - pub index: u32, - - /// `pure_wrt_drop`, set by the (unsafe) `#[may_dangle]` attribute - /// on generic parameter `'a`/`T`, asserts data behind the parameter - /// `'a`/`T` won't be accessed during the parent type's `Drop` impl. - pub pure_wrt_drop: bool, - - pub kind: GenericParamDefKind, -} - -impl GenericParamDef { - pub fn to_early_bound_region_data(&self) -> ty::EarlyBoundRegion { - if let GenericParamDefKind::Lifetime = self.kind { - ty::EarlyBoundRegion { def_id: self.def_id, index: self.index, name: self.name } - } else { - bug!("cannot convert a non-lifetime parameter def to an early bound region") - } - } -} - -#[derive(Default)] -pub struct GenericParamCount { - pub lifetimes: usize, - pub types: usize, - pub consts: usize, -} - -/// Information about the formal type/lifetime parameters associated -/// with an item or method. Analogous to `hir::Generics`. -/// -/// The ordering of parameters is the same as in `Subst` (excluding child generics): -/// `Self` (optionally), `Lifetime` params..., `Type` params... -#[derive(Clone, Debug, TyEncodable, TyDecodable, HashStable)] -pub struct Generics { - pub parent: Option, - pub parent_count: usize, - pub params: Vec, - - /// Reverse map to the `index` field of each `GenericParamDef`. - #[stable_hasher(ignore)] - pub param_def_id_to_index: FxHashMap, - - pub has_self: bool, - pub has_late_bound_regions: Option, -} - -impl<'tcx> Generics { - pub fn count(&self) -> usize { - self.parent_count + self.params.len() - } - - pub fn own_counts(&self) -> GenericParamCount { - // We could cache this as a property of `GenericParamCount`, but - // the aim is to refactor this away entirely eventually and the - // presence of this method will be a constant reminder. - let mut own_counts = GenericParamCount::default(); - - for param in &self.params { - match param.kind { - GenericParamDefKind::Lifetime => own_counts.lifetimes += 1, - GenericParamDefKind::Type { .. } => own_counts.types += 1, - GenericParamDefKind::Const => own_counts.consts += 1, - } - } - - own_counts - } - - pub fn own_defaults(&self) -> GenericParamCount { - let mut own_defaults = GenericParamCount::default(); - - for param in &self.params { - match param.kind { - GenericParamDefKind::Lifetime => (), - GenericParamDefKind::Type { has_default, .. } => { - own_defaults.types += has_default as usize; - } - GenericParamDefKind::Const => { - // FIXME(const_generics:defaults) - } - } - } - - own_defaults - } - - pub fn requires_monomorphization(&self, tcx: TyCtxt<'tcx>) -> bool { - if self.own_requires_monomorphization() { - return true; - } - - if let Some(parent_def_id) = self.parent { - let parent = tcx.generics_of(parent_def_id); - parent.requires_monomorphization(tcx) - } else { - false - } - } - - pub fn own_requires_monomorphization(&self) -> bool { - for param in &self.params { - match param.kind { - GenericParamDefKind::Type { .. } | GenericParamDefKind::Const => return true, - GenericParamDefKind::Lifetime => {} - } - } - false - } - - /// Returns the `GenericParamDef` with the given index. - pub fn param_at(&'tcx self, param_index: usize, tcx: TyCtxt<'tcx>) -> &'tcx GenericParamDef { - if let Some(index) = param_index.checked_sub(self.parent_count) { - &self.params[index] - } else { - tcx.generics_of(self.parent.expect("parent_count > 0 but no parent?")) - .param_at(param_index, tcx) - } - } - - /// Returns the `GenericParamDef` associated with this `EarlyBoundRegion`. - pub fn region_param( - &'tcx self, - param: &EarlyBoundRegion, - tcx: TyCtxt<'tcx>, - ) -> &'tcx GenericParamDef { - let param = self.param_at(param.index as usize, tcx); - match param.kind { - GenericParamDefKind::Lifetime => param, - _ => bug!("expected lifetime parameter, but found another generic parameter"), - } - } - - /// Returns the `GenericParamDef` associated with this `ParamTy`. - pub fn type_param(&'tcx self, param: &ParamTy, tcx: TyCtxt<'tcx>) -> &'tcx GenericParamDef { - let param = self.param_at(param.index as usize, tcx); - match param.kind { - GenericParamDefKind::Type { .. } => param, - _ => bug!("expected type parameter, but found another generic parameter"), - } - } - - /// Returns the `GenericParamDef` associated with this `ParamConst`. - pub fn const_param(&'tcx self, param: &ParamConst, tcx: TyCtxt<'tcx>) -> &GenericParamDef { - let param = self.param_at(param.index as usize, tcx); - match param.kind { - GenericParamDefKind::Const => param, - _ => bug!("expected const parameter, but found another generic parameter"), - } - } -} - -/// Bounds on generics. -#[derive(Copy, Clone, Default, Debug, TyEncodable, TyDecodable, HashStable)] -pub struct GenericPredicates<'tcx> { - pub parent: Option, - pub predicates: &'tcx [(Predicate<'tcx>, Span)], -} - -impl<'tcx> GenericPredicates<'tcx> { - pub fn instantiate( - &self, - tcx: TyCtxt<'tcx>, - substs: SubstsRef<'tcx>, - ) -> InstantiatedPredicates<'tcx> { - let mut instantiated = InstantiatedPredicates::empty(); - self.instantiate_into(tcx, &mut instantiated, substs); - instantiated - } - - pub fn instantiate_own( - &self, - tcx: TyCtxt<'tcx>, - substs: SubstsRef<'tcx>, - ) -> InstantiatedPredicates<'tcx> { - InstantiatedPredicates { - predicates: self.predicates.iter().map(|(p, _)| p.subst(tcx, substs)).collect(), - spans: self.predicates.iter().map(|(_, sp)| *sp).collect(), - } - } - - fn instantiate_into( - &self, - tcx: TyCtxt<'tcx>, - instantiated: &mut InstantiatedPredicates<'tcx>, - substs: SubstsRef<'tcx>, - ) { - if let Some(def_id) = self.parent { - tcx.predicates_of(def_id).instantiate_into(tcx, instantiated, substs); - } - instantiated.predicates.extend(self.predicates.iter().map(|(p, _)| p.subst(tcx, substs))); - instantiated.spans.extend(self.predicates.iter().map(|(_, sp)| *sp)); - } - - pub fn instantiate_identity(&self, tcx: TyCtxt<'tcx>) -> InstantiatedPredicates<'tcx> { - let mut instantiated = InstantiatedPredicates::empty(); - self.instantiate_identity_into(tcx, &mut instantiated); - instantiated - } - - fn instantiate_identity_into( - &self, - tcx: TyCtxt<'tcx>, - instantiated: &mut InstantiatedPredicates<'tcx>, - ) { - if let Some(def_id) = self.parent { - tcx.predicates_of(def_id).instantiate_identity_into(tcx, instantiated); - } - instantiated.predicates.extend(self.predicates.iter().map(|(p, _)| p)); - instantiated.spans.extend(self.predicates.iter().map(|(_, s)| s)); - } -} - #[derive(Debug)] crate struct PredicateInner<'tcx> { - kind: Binder>, + kind: Binder<'tcx, PredicateKind<'tcx>>, flags: TypeFlags, /// See the comment for the corresponding field of [TyS]. outer_exclusive_binder: ty::DebruijnIndex, } -#[cfg(target_arch = "x86_64")] -static_assert_size!(PredicateInner<'_>, 40); +#[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))] +static_assert_size!(PredicateInner<'_>, 48); #[derive(Clone, Copy, Lift)] pub struct Predicate<'tcx> { @@ -1054,9 +389,9 @@ impl Hash for Predicate<'_> { impl<'tcx> Eq for Predicate<'tcx> {} impl<'tcx> Predicate<'tcx> { - /// Gets the inner `Binder>`. + /// Gets the inner `Binder<'tcx, PredicateKind<'tcx>>`. #[inline] - pub fn kind(self) -> Binder> { + pub fn kind(self) -> Binder<'tcx, PredicateKind<'tcx>> { self.inner.kind } } @@ -1208,10 +543,33 @@ impl<'tcx> Predicate<'tcx> { // substitution code expects equal binding levels in the values // from the substitution and the value being substituted into, and // this trick achieves that). - let substs = trait_ref.skip_binder().substs; - let pred = self.kind().skip_binder(); - let new = pred.subst(tcx, substs); - tcx.reuse_or_mk_predicate(self, ty::Binder::bind(new)) + + // Working through the second example: + // trait_ref: for<'x> T: Foo1<'^0.0>; substs: [T, '^0.0] + // predicate: for<'b> Self: Bar1<'a, '^0.0>; substs: [Self, 'a, '^0.0] + // We want to end up with: + // for<'x, 'b> T: Bar1<'^0.0, '^0.1> + // To do this: + // 1) We must shift all bound vars in predicate by the length + // of trait ref's bound vars. So, we would end up with predicate like + // Self: Bar1<'a, '^0.1> + // 2) We can then apply the trait substs to this, ending up with + // T: Bar1<'^0.0, '^0.1> + // 3) Finally, to create the final bound vars, we concatenate the bound + // vars of the trait ref with those of the predicate: + // ['x, 'b] + let bound_pred = self.kind(); + let pred_bound_vars = bound_pred.bound_vars(); + let trait_bound_vars = trait_ref.bound_vars(); + // 1) Self: Bar1<'a, '^0.0> -> Self: Bar1<'a, '^0.1> + let shifted_pred = + tcx.shift_bound_var_indices(trait_bound_vars.len(), bound_pred.skip_binder()); + // 2) Self: Bar1<'a, '^0.1> -> T: Bar1<'^0.0, '^0.1> + let new = shifted_pred.subst(tcx, trait_ref.skip_binder().substs); + // 3) ['x] + ['b] -> ['x, 'b] + let bound_vars = + tcx.mk_bound_variable_kinds(trait_bound_vars.iter().chain(pred_bound_vars)); + tcx.reuse_or_mk_predicate(self, ty::Binder::bind_with_vars(new, bound_vars)) } } @@ -1221,7 +579,7 @@ pub struct TraitPredicate<'tcx> { pub trait_ref: TraitRef<'tcx>, } -pub type PolyTraitPredicate<'tcx> = ty::Binder>; +pub type PolyTraitPredicate<'tcx> = ty::Binder<'tcx, TraitPredicate<'tcx>>; impl<'tcx> TraitPredicate<'tcx> { pub fn def_id(self) -> DefId { @@ -1239,7 +597,7 @@ impl<'tcx> PolyTraitPredicate<'tcx> { self.skip_binder().def_id() } - pub fn self_ty(self) -> ty::Binder> { + pub fn self_ty(self) -> ty::Binder<'tcx, Ty<'tcx>> { self.map_bound(|trait_ref| trait_ref.self_ty()) } } @@ -1249,8 +607,8 @@ impl<'tcx> PolyTraitPredicate<'tcx> { pub struct OutlivesPredicate(pub A, pub B); // `A: B` pub type RegionOutlivesPredicate<'tcx> = OutlivesPredicate, ty::Region<'tcx>>; pub type TypeOutlivesPredicate<'tcx> = OutlivesPredicate, ty::Region<'tcx>>; -pub type PolyRegionOutlivesPredicate<'tcx> = ty::Binder>; -pub type PolyTypeOutlivesPredicate<'tcx> = ty::Binder>; +pub type PolyRegionOutlivesPredicate<'tcx> = ty::Binder<'tcx, RegionOutlivesPredicate<'tcx>>; +pub type PolyTypeOutlivesPredicate<'tcx> = ty::Binder<'tcx, TypeOutlivesPredicate<'tcx>>; #[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, TyEncodable, TyDecodable)] #[derive(HashStable, TypeFoldable)] @@ -1259,7 +617,7 @@ pub struct SubtypePredicate<'tcx> { pub a: Ty<'tcx>, pub b: Ty<'tcx>, } -pub type PolySubtypePredicate<'tcx> = ty::Binder>; +pub type PolySubtypePredicate<'tcx> = ty::Binder<'tcx, SubtypePredicate<'tcx>>; /// This kind of predicate has no *direct* correspondent in the /// syntax, but it roughly corresponds to the syntactic forms: @@ -1280,25 +638,15 @@ pub struct ProjectionPredicate<'tcx> { pub ty: Ty<'tcx>, } -pub type PolyProjectionPredicate<'tcx> = Binder>; +pub type PolyProjectionPredicate<'tcx> = Binder<'tcx, ProjectionPredicate<'tcx>>; impl<'tcx> PolyProjectionPredicate<'tcx> { - /// Returns the `DefId` of the associated item being projected. - pub fn item_def_id(&self) -> DefId { - self.skip_binder().projection_ty.item_def_id - } - /// Returns the `DefId` of the trait of the associated item being projected. #[inline] pub fn trait_def_id(&self, tcx: TyCtxt<'tcx>) -> DefId { self.skip_binder().projection_ty.trait_def_id(tcx) } - #[inline] - pub fn projection_self_ty(&self) -> Binder> { - self.map_bound(|predicate| predicate.projection_ty.self_ty()) - } - /// Get the [PolyTraitRef] required for this projection to be well formed. /// Note that for generic associated types the predicates of the associated /// type also need to be checked. @@ -1312,7 +660,7 @@ impl<'tcx> PolyProjectionPredicate<'tcx> { self.map_bound(|predicate| predicate.projection_ty.trait_ref(tcx)) } - pub fn ty(&self) -> Binder> { + pub fn ty(&self) -> Binder<'tcx, Ty<'tcx>> { self.map_bound(|predicate| predicate.ty) } @@ -1346,7 +694,7 @@ pub trait ToPredicate<'tcx> { fn to_predicate(self, tcx: TyCtxt<'tcx>) -> Predicate<'tcx>; } -impl ToPredicate<'tcx> for Binder> { +impl ToPredicate<'tcx> for Binder<'tcx, PredicateKind<'tcx>> { #[inline(always)] fn to_predicate(self, tcx: TyCtxt<'tcx>) -> Predicate<'tcx> { tcx.mk_predicate(self) @@ -1369,11 +717,11 @@ impl<'tcx> ToPredicate<'tcx> for ConstnessAnd> { impl<'tcx> ToPredicate<'tcx> for ConstnessAnd> { fn to_predicate(self, tcx: TyCtxt<'tcx>) -> Predicate<'tcx> { - ConstnessAnd { - value: self.value.map_bound(|trait_ref| ty::TraitPredicate { trait_ref }), - constness: self.constness, - } - .to_predicate(tcx) + self.value + .map_bound(|trait_ref| { + PredicateKind::Trait(ty::TraitPredicate { trait_ref }, self.constness) + }) + .to_predicate(tcx) } } @@ -1704,10 +1052,6 @@ impl WithOptConstParam { None } - pub fn expect_local(self) -> WithOptConstParam { - self.as_local().unwrap() - } - pub fn is_local(self) -> bool { self.did.is_local() } @@ -1930,32 +1274,6 @@ pub struct Destructor { pub did: DefId, } -bitflags! { - #[derive(HashStable)] - pub struct AdtFlags: u32 { - const NO_ADT_FLAGS = 0; - /// Indicates whether the ADT is an enum. - const IS_ENUM = 1 << 0; - /// Indicates whether the ADT is a union. - const IS_UNION = 1 << 1; - /// Indicates whether the ADT is a struct. - const IS_STRUCT = 1 << 2; - /// Indicates whether the ADT is a struct and has a constructor. - const HAS_CTOR = 1 << 3; - /// Indicates whether the type is `PhantomData`. - const IS_PHANTOM_DATA = 1 << 4; - /// Indicates whether the type has a `#[fundamental]` attribute. - const IS_FUNDAMENTAL = 1 << 5; - /// Indicates whether the type is `Box`. - const IS_BOX = 1 << 6; - /// Indicates whether the type is `ManuallyDrop`. - const IS_MANUALLY_DROP = 1 << 7; - /// Indicates whether the variant list of this ADT is `#[non_exhaustive]`. - /// (i.e., this flag is never set unless this ADT is an enum). - const IS_VARIANT_LIST_NON_EXHAUSTIVE = 1 << 8; - } -} - bitflags! { #[derive(HashStable)] pub struct VariantFlags: u32 { @@ -2079,105 +1397,6 @@ pub struct FieldDef { pub vis: Visibility, } -/// The definition of a user-defined type, e.g., a `struct`, `enum`, or `union`. -/// -/// These are all interned (by `alloc_adt_def`) into the global arena. -/// -/// The initialism *ADT* stands for an [*algebraic data type (ADT)*][adt]. -/// This is slightly wrong because `union`s are not ADTs. -/// Moreover, Rust only allows recursive data types through indirection. -/// -/// [adt]: https://en.wikipedia.org/wiki/Algebraic_data_type -pub struct AdtDef { - /// The `DefId` of the struct, enum or union item. - pub did: DefId, - /// Variants of the ADT. If this is a struct or union, then there will be a single variant. - pub variants: IndexVec, - /// Flags of the ADT (e.g., is this a struct? is this non-exhaustive?). - flags: AdtFlags, - /// Repr options provided by the user. - pub repr: ReprOptions, -} - -impl PartialOrd for AdtDef { - fn partial_cmp(&self, other: &AdtDef) -> Option { - Some(self.cmp(&other)) - } -} - -/// There should be only one AdtDef for each `did`, therefore -/// it is fine to implement `Ord` only based on `did`. -impl Ord for AdtDef { - fn cmp(&self, other: &AdtDef) -> Ordering { - self.did.cmp(&other.did) - } -} - -impl PartialEq for AdtDef { - // `AdtDef`s are always interned, and this is part of `TyS` equality. - #[inline] - fn eq(&self, other: &Self) -> bool { - ptr::eq(self, other) - } -} - -impl Eq for AdtDef {} - -impl Hash for AdtDef { - #[inline] - fn hash(&self, s: &mut H) { - (self as *const AdtDef).hash(s) - } -} - -impl Encodable for AdtDef { - fn encode(&self, s: &mut S) -> Result<(), S::Error> { - self.did.encode(s) - } -} - -impl<'a> HashStable> for AdtDef { - fn hash_stable(&self, hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { - thread_local! { - static CACHE: RefCell> = 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 ty::AdtDef { did, ref variants, ref flags, ref repr } = *self; - - let mut hasher = StableHasher::new(); - did.hash_stable(hcx, &mut hasher); - variants.hash_stable(hcx, &mut hasher); - flags.hash_stable(hcx, &mut hasher); - repr.hash_stable(hcx, &mut hasher); - - hasher.finish() - }) - }); - - hash.hash_stable(hcx, hasher); - } -} - -#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)] -pub enum AdtKind { - Struct, - Union, - Enum, -} - -impl Into for AdtKind { - fn into(self) -> DataTypeKind { - match self { - AdtKind::Struct => DataTypeKind::Struct, - AdtKind::Union => DataTypeKind::Union, - AdtKind::Enum => DataTypeKind::Enum, - } - } -} - bitflags! { #[derive(TyEncodable, TyDecodable, Default, HashStable)] pub struct ReprFlags: u8 { @@ -2300,334 +1519,6 @@ impl ReprOptions { } } -impl<'tcx> AdtDef { - /// Creates a new `AdtDef`. - fn new( - tcx: TyCtxt<'_>, - did: DefId, - kind: AdtKind, - variants: IndexVec, - repr: ReprOptions, - ) -> Self { - debug!("AdtDef::new({:?}, {:?}, {:?}, {:?})", did, kind, variants, repr); - let mut flags = AdtFlags::NO_ADT_FLAGS; - - if kind == AdtKind::Enum && tcx.has_attr(did, sym::non_exhaustive) { - debug!("found non-exhaustive variant list for {:?}", did); - flags = flags | AdtFlags::IS_VARIANT_LIST_NON_EXHAUSTIVE; - } - - flags |= match kind { - AdtKind::Enum => AdtFlags::IS_ENUM, - AdtKind::Union => AdtFlags::IS_UNION, - AdtKind::Struct => AdtFlags::IS_STRUCT, - }; - - if kind == AdtKind::Struct && variants[VariantIdx::new(0)].ctor_def_id.is_some() { - flags |= AdtFlags::HAS_CTOR; - } - - let attrs = tcx.get_attrs(did); - if tcx.sess.contains_name(&attrs, sym::fundamental) { - flags |= AdtFlags::IS_FUNDAMENTAL; - } - if Some(did) == tcx.lang_items().phantom_data() { - flags |= AdtFlags::IS_PHANTOM_DATA; - } - if Some(did) == tcx.lang_items().owned_box() { - flags |= AdtFlags::IS_BOX; - } - if Some(did) == tcx.lang_items().manually_drop() { - flags |= AdtFlags::IS_MANUALLY_DROP; - } - - AdtDef { did, variants, flags, repr } - } - - /// Returns `true` if this is a struct. - #[inline] - pub fn is_struct(&self) -> bool { - self.flags.contains(AdtFlags::IS_STRUCT) - } - - /// Returns `true` if this is a union. - #[inline] - pub fn is_union(&self) -> bool { - self.flags.contains(AdtFlags::IS_UNION) - } - - /// Returns `true` if this is a enum. - #[inline] - pub fn is_enum(&self) -> bool { - self.flags.contains(AdtFlags::IS_ENUM) - } - - /// Returns `true` if the variant list of this ADT is `#[non_exhaustive]`. - #[inline] - pub fn is_variant_list_non_exhaustive(&self) -> bool { - self.flags.contains(AdtFlags::IS_VARIANT_LIST_NON_EXHAUSTIVE) - } - - /// Returns the kind of the ADT. - #[inline] - pub fn adt_kind(&self) -> AdtKind { - if self.is_enum() { - AdtKind::Enum - } else if self.is_union() { - AdtKind::Union - } else { - AdtKind::Struct - } - } - - /// Returns a description of this abstract data type. - pub fn descr(&self) -> &'static str { - match self.adt_kind() { - AdtKind::Struct => "struct", - AdtKind::Union => "union", - AdtKind::Enum => "enum", - } - } - - /// Returns a description of a variant of this abstract data type. - #[inline] - pub fn variant_descr(&self) -> &'static str { - match self.adt_kind() { - AdtKind::Struct => "struct", - AdtKind::Union => "union", - AdtKind::Enum => "variant", - } - } - - /// If this function returns `true`, it implies that `is_struct` must return `true`. - #[inline] - pub fn has_ctor(&self) -> bool { - self.flags.contains(AdtFlags::HAS_CTOR) - } - - /// Returns `true` if this type is `#[fundamental]` for the purposes - /// of coherence checking. - #[inline] - pub fn is_fundamental(&self) -> bool { - self.flags.contains(AdtFlags::IS_FUNDAMENTAL) - } - - /// Returns `true` if this is `PhantomData`. - #[inline] - pub fn is_phantom_data(&self) -> bool { - self.flags.contains(AdtFlags::IS_PHANTOM_DATA) - } - - /// Returns `true` if this is Box. - #[inline] - pub fn is_box(&self) -> bool { - self.flags.contains(AdtFlags::IS_BOX) - } - - /// Returns `true` if this is `ManuallyDrop`. - #[inline] - pub fn is_manually_drop(&self) -> bool { - self.flags.contains(AdtFlags::IS_MANUALLY_DROP) - } - - /// Returns `true` if this type has a destructor. - pub fn has_dtor(&self, tcx: TyCtxt<'tcx>) -> bool { - self.destructor(tcx).is_some() - } - - /// Asserts this is a struct or union and returns its unique variant. - pub fn non_enum_variant(&self) -> &VariantDef { - assert!(self.is_struct() || self.is_union()); - &self.variants[VariantIdx::new(0)] - } - - #[inline] - pub fn predicates(&self, tcx: TyCtxt<'tcx>) -> GenericPredicates<'tcx> { - tcx.predicates_of(self.did) - } - - /// Returns an iterator over all fields contained - /// by this ADT. - #[inline] - pub fn all_fields(&self) -> impl Iterator + Clone { - self.variants.iter().flat_map(|v| v.fields.iter()) - } - - /// Whether the ADT lacks fields. Note that this includes uninhabited enums, - /// e.g., `enum Void {}` is considered payload free as well. - pub fn is_payloadfree(&self) -> bool { - self.variants.iter().all(|v| v.fields.is_empty()) - } - - /// Return a `VariantDef` given a variant id. - pub fn variant_with_id(&self, vid: DefId) -> &VariantDef { - self.variants.iter().find(|v| v.def_id == vid).expect("variant_with_id: unknown variant") - } - - /// Return a `VariantDef` given a constructor id. - pub fn variant_with_ctor_id(&self, cid: DefId) -> &VariantDef { - self.variants - .iter() - .find(|v| v.ctor_def_id == Some(cid)) - .expect("variant_with_ctor_id: unknown variant") - } - - /// Return the index of `VariantDef` given a variant id. - pub fn variant_index_with_id(&self, vid: DefId) -> VariantIdx { - self.variants - .iter_enumerated() - .find(|(_, v)| v.def_id == vid) - .expect("variant_index_with_id: unknown variant") - .0 - } - - /// Return the index of `VariantDef` given a constructor id. - pub fn variant_index_with_ctor_id(&self, cid: DefId) -> VariantIdx { - self.variants - .iter_enumerated() - .find(|(_, v)| v.ctor_def_id == Some(cid)) - .expect("variant_index_with_ctor_id: unknown variant") - .0 - } - - pub fn variant_of_res(&self, res: Res) -> &VariantDef { - match res { - Res::Def(DefKind::Variant, vid) => self.variant_with_id(vid), - Res::Def(DefKind::Ctor(..), cid) => self.variant_with_ctor_id(cid), - Res::Def(DefKind::Struct, _) - | Res::Def(DefKind::Union, _) - | Res::Def(DefKind::TyAlias, _) - | Res::Def(DefKind::AssocTy, _) - | Res::SelfTy(..) - | Res::SelfCtor(..) => self.non_enum_variant(), - _ => bug!("unexpected res {:?} in variant_of_res", res), - } - } - - #[inline] - pub fn eval_explicit_discr(&self, tcx: TyCtxt<'tcx>, expr_did: DefId) -> Option> { - assert!(self.is_enum()); - let param_env = tcx.param_env(expr_did); - let repr_type = self.repr.discr_type(); - match tcx.const_eval_poly(expr_did) { - Ok(val) => { - let ty = repr_type.to_ty(tcx); - if let Some(b) = val.try_to_bits_for_ty(tcx, param_env, ty) { - trace!("discriminants: {} ({:?})", b, repr_type); - Some(Discr { val: b, ty }) - } else { - info!("invalid enum discriminant: {:#?}", val); - crate::mir::interpret::struct_error( - tcx.at(tcx.def_span(expr_did)), - "constant evaluation of enum discriminant resulted in non-integer", - ) - .emit(); - None - } - } - Err(err) => { - let msg = match err { - ErrorHandled::Reported(ErrorReported) | ErrorHandled::Linted => { - "enum discriminant evaluation failed" - } - ErrorHandled::TooGeneric => "enum discriminant depends on generics", - }; - tcx.sess.delay_span_bug(tcx.def_span(expr_did), msg); - None - } - } - } - - #[inline] - pub fn discriminants( - &'tcx self, - tcx: TyCtxt<'tcx>, - ) -> impl Iterator)> + Captures<'tcx> { - assert!(self.is_enum()); - let repr_type = self.repr.discr_type(); - let initial = repr_type.initial_discriminant(tcx); - let mut prev_discr = None::>; - self.variants.iter_enumerated().map(move |(i, v)| { - let mut discr = prev_discr.map_or(initial, |d| d.wrap_incr(tcx)); - if let VariantDiscr::Explicit(expr_did) = v.discr { - if let Some(new_discr) = self.eval_explicit_discr(tcx, expr_did) { - discr = new_discr; - } - } - prev_discr = Some(discr); - - (i, discr) - }) - } - - #[inline] - pub fn variant_range(&self) -> Range { - VariantIdx::new(0)..VariantIdx::new(self.variants.len()) - } - - /// Computes the discriminant value used by a specific variant. - /// Unlike `discriminants`, this is (amortized) constant-time, - /// only doing at most one query for evaluating an explicit - /// discriminant (the last one before the requested variant), - /// assuming there are no constant-evaluation errors there. - #[inline] - pub fn discriminant_for_variant( - &self, - tcx: TyCtxt<'tcx>, - variant_index: VariantIdx, - ) -> Discr<'tcx> { - assert!(self.is_enum()); - let (val, offset) = self.discriminant_def_for_variant(variant_index); - let explicit_value = val - .and_then(|expr_did| self.eval_explicit_discr(tcx, expr_did)) - .unwrap_or_else(|| self.repr.discr_type().initial_discriminant(tcx)); - explicit_value.checked_add(tcx, offset as u128).0 - } - - /// Yields a `DefId` for the discriminant and an offset to add to it - /// Alternatively, if there is no explicit discriminant, returns the - /// inferred discriminant directly. - pub fn discriminant_def_for_variant(&self, variant_index: VariantIdx) -> (Option, u32) { - assert!(!self.variants.is_empty()); - let mut explicit_index = variant_index.as_u32(); - let expr_did; - loop { - match self.variants[VariantIdx::from_u32(explicit_index)].discr { - ty::VariantDiscr::Relative(0) => { - expr_did = None; - break; - } - ty::VariantDiscr::Relative(distance) => { - explicit_index -= distance; - } - ty::VariantDiscr::Explicit(did) => { - expr_did = Some(did); - break; - } - } - } - (expr_did, variant_index.as_u32() - explicit_index) - } - - pub fn destructor(&self, tcx: TyCtxt<'tcx>) -> Option { - tcx.adt_destructor(self.did) - } - - /// Returns a list of types such that `Self: Sized` if and only - /// if that type is `Sized`, or `TyErr` if this type is recursive. - /// - /// Oddly enough, checking that the sized-constraint is `Sized` is - /// actually more expressive than checking all members: - /// the `Sized` trait is inductive, so an associated type that references - /// `Self` would prevent its containing ADT from being `Sized`. - /// - /// Due to normalization being eager, this applies even if - /// the associated type is behind a pointer (e.g., issue #31299). - pub fn sized_constraint(&self, tcx: TyCtxt<'tcx>) -> &'tcx [Ty<'tcx>] { - tcx.adt_sized_constraint(self.did).0 - } -} - impl<'tcx> FieldDef { /// Returns the type of this field. The `subst` is typically obtained /// via the second field of `TyKind::AdtDef`. @@ -2636,93 +1527,6 @@ impl<'tcx> FieldDef { } } -/// Represents the various closure traits in the language. This -/// will determine the type of the environment (`self`, in the -/// desugaring) argument that the closure expects. -/// -/// You can get the environment type of a closure using -/// `tcx.closure_env_ty()`. -#[derive(Clone, Copy, PartialOrd, Ord, PartialEq, Eq, Hash, Debug, TyEncodable, TyDecodable)] -#[derive(HashStable)] -pub enum ClosureKind { - // Warning: Ordering is significant here! The ordering is chosen - // because the trait Fn is a subtrait of FnMut and so in turn, and - // hence we order it so that Fn < FnMut < FnOnce. - Fn, - FnMut, - FnOnce, -} - -impl<'tcx> ClosureKind { - // This is the initial value used when doing upvar inference. - pub const LATTICE_BOTTOM: ClosureKind = ClosureKind::Fn; - - pub fn trait_did(&self, tcx: TyCtxt<'tcx>) -> DefId { - match *self { - ClosureKind::Fn => tcx.require_lang_item(LangItem::Fn, None), - ClosureKind::FnMut => tcx.require_lang_item(LangItem::FnMut, None), - ClosureKind::FnOnce => tcx.require_lang_item(LangItem::FnOnce, None), - } - } - - /// Returns `true` if a type that impls this closure kind - /// must also implement `other`. - pub fn extends(self, other: ty::ClosureKind) -> bool { - matches!( - (self, other), - (ClosureKind::Fn, ClosureKind::Fn) - | (ClosureKind::Fn, ClosureKind::FnMut) - | (ClosureKind::Fn, ClosureKind::FnOnce) - | (ClosureKind::FnMut, ClosureKind::FnMut) - | (ClosureKind::FnMut, ClosureKind::FnOnce) - | (ClosureKind::FnOnce, ClosureKind::FnOnce) - ) - } - - /// Returns the representative scalar type for this closure kind. - /// See `TyS::to_opt_closure_kind` for more details. - pub fn to_ty(self, tcx: TyCtxt<'tcx>) -> Ty<'tcx> { - match self { - ty::ClosureKind::Fn => tcx.types.i8, - ty::ClosureKind::FnMut => tcx.types.i16, - ty::ClosureKind::FnOnce => tcx.types.i32, - } - } -} - -impl BorrowKind { - pub fn from_mutbl(m: hir::Mutability) -> BorrowKind { - match m { - hir::Mutability::Mut => MutBorrow, - hir::Mutability::Not => ImmBorrow, - } - } - - /// Returns a mutability `m` such that an `&m T` pointer could be used to obtain this borrow - /// kind. Because borrow kinds are richer than mutabilities, we sometimes have to pick a - /// mutability that is stronger than necessary so that it at least *would permit* the borrow in - /// question. - pub fn to_mutbl_lossy(self) -> hir::Mutability { - match self { - MutBorrow => hir::Mutability::Mut, - ImmBorrow => hir::Mutability::Not, - - // We have no type corresponding to a unique imm borrow, so - // use `&mut`. It gives all the capabilities of an `&uniq` - // and hence is a safe "over approximation". - UniqueImmBorrow => hir::Mutability::Mut, - } - } - - pub fn to_user_str(&self) -> &'static str { - match *self { - MutBorrow => "mutable", - ImmBorrow => "immutable", - UniqueImmBorrow => "uniquely immutable", - } - } -} - pub type Attributes<'tcx> = &'tcx [ast::Attribute]; #[derive(Debug, PartialEq, Eq)] @@ -2963,7 +1767,10 @@ impl<'tcx> TyCtxt<'tcx> { | DefKind::AnonConst => self.mir_for_ctfe_opt_const_arg(def), // If the caller wants `mir_for_ctfe` of a function they should not be using // `instance_mir`, so we'll assume const fn also wants the optimized version. - _ => self.optimized_mir_or_const_arg_mir(def), + _ => { + assert_eq!(def.const_param_did, None); + self.optimized_mir(def.did) + } }, ty::InstanceDef::VtableShim(..) | ty::InstanceDef::ReifyShim(..) @@ -3077,9 +1884,6 @@ impl<'tcx> TyCtxt<'tcx> { } } -#[derive(Clone, HashStable, Debug)] -pub struct AdtSizedConstraint<'tcx>(pub &'tcx [Ty<'tcx>]); - /// Yields the parent function's `DefId` if `def_id` is an `impl Trait` definition. pub fn is_impl_trait_defn(tcx: TyCtxt<'_>, def_id: DefId) -> Option { if let Some(def_id) = def_id.as_local() { @@ -3154,6 +1958,7 @@ pub fn provide(providers: &mut ty::query::Providers) { trait_impls_of: trait_def::trait_impls_of_provider, all_local_trait_impls: trait_def::all_local_trait_impls, type_uninhabited_from: inhabitedness::type_uninhabited_from, + const_param_default: consts::const_param_default, ..*providers }; } diff --git a/compiler/rustc_middle/src/ty/normalize_erasing_regions.rs b/compiler/rustc_middle/src/ty/normalize_erasing_regions.rs index 9d97815a5f1b7..a4f736654af44 100644 --- a/compiler/rustc_middle/src/ty/normalize_erasing_regions.rs +++ b/compiler/rustc_middle/src/ty/normalize_erasing_regions.rs @@ -7,6 +7,7 @@ //! `normalize_generic_arg_after_erasing_regions` query for each type //! or constant found within. (This underlying query is what is cached.) +use crate::mir; use crate::ty::fold::{TypeFoldable, TypeFolder}; use crate::ty::subst::{Subst, SubstsRef}; use crate::ty::{self, Ty, TyCtxt}; @@ -38,7 +39,7 @@ impl<'tcx> TyCtxt<'tcx> { } } - /// If you have a `Binder`, you can do this to strip out the + /// If you have a `Binder<'tcx, T>`, you can do this to strip out the /// late-bound regions and then normalize the result, yielding up /// a `T` (with regions erased). This is appropriate when the /// binder is being instantiated at the call site. @@ -49,7 +50,7 @@ impl<'tcx> TyCtxt<'tcx> { pub fn normalize_erasing_late_bound_regions( self, param_env: ty::ParamEnv<'tcx>, - value: ty::Binder, + value: ty::Binder<'tcx, T>, ) -> T where T: TypeFoldable<'tcx>, @@ -101,4 +102,10 @@ impl TypeFolder<'tcx> for NormalizeAfterErasingRegionsFolder<'tcx> { let arg = self.param_env.and(c.into()); self.tcx.normalize_generic_arg_after_erasing_regions(arg).expect_const() } + + #[inline] + fn fold_mir_const(&mut self, c: mir::ConstantKind<'tcx>) -> mir::ConstantKind<'tcx> { + let arg = self.param_env.and(c); + self.tcx.normalize_mir_const_after_erasing_regions(arg) + } } diff --git a/compiler/rustc_middle/src/ty/print/mod.rs b/compiler/rustc_middle/src/ty/print/mod.rs index 77f1668893782..13e2122a619dc 100644 --- a/compiler/rustc_middle/src/ty/print/mod.rs +++ b/compiler/rustc_middle/src/ty/print/mod.rs @@ -63,7 +63,7 @@ pub trait Printer<'tcx>: Sized { fn print_dyn_existential( self, - predicates: &'tcx ty::List>>, + predicates: &'tcx ty::List>>, ) -> Result; fn print_const(self, ct: &'tcx ty::Const<'tcx>) -> Result; @@ -193,17 +193,19 @@ pub trait Printer<'tcx>: Sized { .params .iter() .rev() - .take_while(|param| { - match param.kind { - ty::GenericParamDefKind::Lifetime => false, - ty::GenericParamDefKind::Type { has_default, .. } => { - has_default - && substs[param.index as usize] - == GenericArg::from( - self.tcx().type_of(param.def_id).subst(self.tcx(), substs), - ) - } - ty::GenericParamDefKind::Const => false, // FIXME(const_generics_defaults) + .take_while(|param| match param.kind { + ty::GenericParamDefKind::Lifetime => false, + ty::GenericParamDefKind::Type { has_default, .. } => { + has_default + && substs[param.index as usize] + == GenericArg::from( + self.tcx().type_of(param.def_id).subst(self.tcx(), substs), + ) + } + ty::GenericParamDefKind::Const { has_default } => { + has_default + && substs[param.index as usize] + == GenericArg::from(self.tcx().const_param_default(param.def_id)) } }) .count(); @@ -344,7 +346,7 @@ impl<'tcx, P: Printer<'tcx>> Print<'tcx, P> for Ty<'tcx> { } impl<'tcx, P: Printer<'tcx>> Print<'tcx, P> - for &'tcx ty::List>> + for &'tcx ty::List>> { type Output = P::DynExistential; type Error = P::Error; diff --git a/compiler/rustc_middle/src/ty/print/pretty.rs b/compiler/rustc_middle/src/ty/print/pretty.rs index 53c164d44b3e1..1989c91a87962 100644 --- a/compiler/rustc_middle/src/ty/print/pretty.rs +++ b/compiler/rustc_middle/src/ty/print/pretty.rs @@ -4,6 +4,7 @@ use crate::ty::subst::{GenericArg, GenericArgKind, Subst}; use crate::ty::{self, ConstInt, DefIdTree, ParamConst, ScalarInt, Ty, TyCtxt, TypeFoldable}; use rustc_apfloat::ieee::{Double, Single}; use rustc_data_structures::fx::FxHashMap; +use rustc_data_structures::sso::SsoHashSet; use rustc_hir as hir; use rustc_hir::def::{self, CtorKind, DefKind, Namespace}; use rustc_hir::def_id::{CrateNum, DefId, DefIdSet, CRATE_DEF_INDEX, LOCAL_CRATE}; @@ -19,6 +20,7 @@ use std::char; use std::collections::BTreeMap; use std::convert::TryFrom; use std::fmt::{self, Write as _}; +use std::iter; use std::ops::{ControlFlow, Deref, DerefMut}; // `pretty` is a separate module only for organization. @@ -201,7 +203,7 @@ pub trait PrettyPrinter<'tcx>: self.print_def_path(def_id, substs) } - fn in_binder(self, value: &ty::Binder) -> Result + fn in_binder(self, value: &ty::Binder<'tcx, T>) -> Result where T: Print<'tcx, Self, Output = Self, Error = Self::Error> + TypeFoldable<'tcx>, { @@ -210,7 +212,7 @@ pub trait PrettyPrinter<'tcx>: fn wrap_binder Result>( self, - value: &ty::Binder, + value: &ty::Binder<'tcx, T>, f: F, ) -> Result where @@ -764,7 +766,7 @@ pub trait PrettyPrinter<'tcx>: fn pretty_print_dyn_existential( mut self, - predicates: &'tcx ty::List>>, + predicates: &'tcx ty::List>>, ) -> Result { // Generate the main trait ref, including associated types. let mut first = true; @@ -915,7 +917,7 @@ pub trait PrettyPrinter<'tcx>: } match ct.val { - ty::ConstKind::Unevaluated(def, substs, promoted) => { + ty::ConstKind::Unevaluated(ty::Unevaluated { def, substs, promoted }) => { if let Some(promoted) = promoted { p!(print_value_path(def.did, substs)); p!(write("::{:?}", promoted)); @@ -956,32 +958,40 @@ pub trait PrettyPrinter<'tcx>: } fn pretty_print_const_scalar( - mut self, + self, scalar: Scalar, ty: Ty<'tcx>, print_ty: bool, + ) -> Result { + match scalar { + Scalar::Ptr(ptr) => self.pretty_print_const_scalar_ptr(ptr, ty, print_ty), + Scalar::Int(int) => self.pretty_print_const_scalar_int(int, ty, print_ty), + } + } + + fn pretty_print_const_scalar_ptr( + mut self, + ptr: Pointer, + ty: Ty<'tcx>, + print_ty: bool, ) -> Result { define_scoped_cx!(self); - match (scalar, &ty.kind()) { + match ty.kind() { // Byte strings (&[u8; N]) - ( - Scalar::Ptr(ptr), - ty::Ref( - _, - ty::TyS { - kind: - ty::Array( - ty::TyS { kind: ty::Uint(ty::UintTy::U8), .. }, - ty::Const { - val: ty::ConstKind::Value(ConstValue::Scalar(int)), - .. - }, - ), - .. - }, - _, - ), + ty::Ref( + _, + ty::TyS { + kind: + ty::Array( + ty::TyS { kind: ty::Uint(ty::UintTy::U8), .. }, + ty::Const { + val: ty::ConstKind::Value(ConstValue::Scalar(int)), .. + }, + ), + .. + }, + _, ) => match self.tcx().get_global_alloc(ptr.alloc_id) { Some(GlobalAlloc::Memory(alloc)) => { let bytes = int.assert_bits(self.tcx().data_layout.pointer_size); @@ -997,28 +1007,59 @@ pub trait PrettyPrinter<'tcx>: Some(GlobalAlloc::Function(_)) => p!(""), None => p!(""), }, + ty::FnPtr(_) => { + // FIXME: We should probably have a helper method to share code with the "Byte strings" + // printing above (which also has to handle pointers to all sorts of things). + match self.tcx().get_global_alloc(ptr.alloc_id) { + Some(GlobalAlloc::Function(instance)) => { + self = self.typed_value( + |this| this.print_value_path(instance.def_id(), instance.substs), + |this| this.print_type(ty), + " as ", + )?; + } + _ => self = self.pretty_print_const_pointer(ptr, ty, print_ty)?, + } + } + // Any pointer values not covered by a branch above + _ => { + self = self.pretty_print_const_pointer(ptr, ty, print_ty)?; + } + } + Ok(self) + } + + fn pretty_print_const_scalar_int( + mut self, + int: ScalarInt, + ty: Ty<'tcx>, + print_ty: bool, + ) -> Result { + define_scoped_cx!(self); + + match ty.kind() { // Bool - (Scalar::Int(int), ty::Bool) if int == ScalarInt::FALSE => p!("false"), - (Scalar::Int(int), ty::Bool) if int == ScalarInt::TRUE => p!("true"), + ty::Bool if int == ScalarInt::FALSE => p!("false"), + ty::Bool if int == ScalarInt::TRUE => p!("true"), // Float - (Scalar::Int(int), ty::Float(ty::FloatTy::F32)) => { + ty::Float(ty::FloatTy::F32) => { p!(write("{}f32", Single::try_from(int).unwrap())) } - (Scalar::Int(int), ty::Float(ty::FloatTy::F64)) => { + ty::Float(ty::FloatTy::F64) => { p!(write("{}f64", Double::try_from(int).unwrap())) } // Int - (Scalar::Int(int), ty::Uint(_) | ty::Int(_)) => { + ty::Uint(_) | ty::Int(_) => { let int = ConstInt::new(int, matches!(ty.kind(), ty::Int(_)), ty.is_ptr_sized_integral()); if print_ty { p!(write("{:#?}", int)) } else { p!(write("{:?}", int)) } } // Char - (Scalar::Int(int), ty::Char) if char::try_from(int).is_ok() => { + ty::Char if char::try_from(int).is_ok() => { p!(write("{:?}", char::try_from(int).unwrap())) } // Raw pointers - (Scalar::Int(int), ty::RawPtr(_) | ty::FnPtr(_)) => { + ty::RawPtr(_) | ty::FnPtr(_) => { let data = int.assert_bits(self.tcx().data_layout.pointer_size); self = self.typed_value( |mut this| { @@ -1029,26 +1070,12 @@ pub trait PrettyPrinter<'tcx>: " as ", )?; } - (Scalar::Ptr(ptr), ty::FnPtr(_)) => { - // FIXME: We should probably have a helper method to share code with the "Byte strings" - // printing above (which also has to handle pointers to all sorts of things). - match self.tcx().get_global_alloc(ptr.alloc_id) { - Some(GlobalAlloc::Function(instance)) => { - self = self.typed_value( - |this| this.print_value_path(instance.def_id(), instance.substs), - |this| this.print_type(ty), - " as ", - )?; - } - _ => self = self.pretty_print_const_pointer(ptr, ty, print_ty)?, - } - } // For function type zsts just printing the path is enough - (Scalar::Int(int), ty::FnDef(d, s)) if int == ScalarInt::ZST => { + ty::FnDef(d, s) if int == ScalarInt::ZST => { p!(print_value_path(*d, s)) } // Nontrivial types with scalar bit representation - (Scalar::Int(int), _) => { + _ => { let print = |mut this: Self| { if int.size() == Size::ZERO { write!(this, "transmute(())")?; @@ -1063,10 +1090,6 @@ pub trait PrettyPrinter<'tcx>: print(self)? }; } - // Any pointer values not covered by a branch above - (Scalar::Ptr(p), _) => { - self = self.pretty_print_const_pointer(p, ty, print_ty)?; - } } Ok(self) } @@ -1202,7 +1225,7 @@ pub trait PrettyPrinter<'tcx>: CtorKind::Fictive => { p!(" {{ "); let mut first = true; - for (field_def, field) in variant_def.fields.iter().zip(fields) { + for (field_def, field) in iter::zip(&variant_def.fields, fields) { if !first { p!(", "); } @@ -1399,7 +1422,8 @@ impl Printer<'tcx> for FmtPrinter<'_, 'tcx, F> { } fn print_type(mut self, ty: Ty<'tcx>) -> Result { - if self.tcx.sess.type_length_limit().value_within_limit(self.printed_type_count) { + let type_length_limit = self.tcx.sess.type_length_limit(); + if type_length_limit.value_within_limit(self.printed_type_count) { self.printed_type_count += 1; self.pretty_print_type(ty) } else { @@ -1410,7 +1434,7 @@ impl Printer<'tcx> for FmtPrinter<'_, 'tcx, F> { fn print_dyn_existential( self, - predicates: &'tcx ty::List>>, + predicates: &'tcx ty::List>>, ) -> Result { self.pretty_print_dyn_existential(predicates) } @@ -1549,7 +1573,7 @@ impl PrettyPrinter<'tcx> for FmtPrinter<'_, 'tcx, F> { Ok(self) } - fn in_binder(self, value: &ty::Binder) -> Result + fn in_binder(self, value: &ty::Binder<'tcx, T>) -> Result where T: Print<'tcx, Self, Output = Self, Error = Self::Error> + TypeFoldable<'tcx>, { @@ -1558,7 +1582,7 @@ impl PrettyPrinter<'tcx> for FmtPrinter<'_, 'tcx, F> { fn wrap_binder Result>( self, - value: &ty::Binder, + value: &ty::Binder<'tcx, T>, f: C, ) -> Result where @@ -1614,7 +1638,7 @@ impl PrettyPrinter<'tcx> for FmtPrinter<'_, 'tcx, F> { data.name != kw::Empty && data.name != kw::UnderscoreLifetime } - ty::ReLateBound(_, ty::BoundRegion { kind: br }) + ty::ReLateBound(_, ty::BoundRegion { kind: br, .. }) | ty::ReFree(ty::FreeRegion { bound_region: br, .. }) | ty::RePlaceholder(ty::Placeholder { name: br, .. }) => { if let ty::BrNamed(_, name) = br { @@ -1693,7 +1717,7 @@ impl FmtPrinter<'_, '_, F> { return Ok(self); } } - ty::ReLateBound(_, ty::BoundRegion { kind: br }) + ty::ReLateBound(_, ty::BoundRegion { kind: br, .. }) | ty::ReFree(ty::FreeRegion { bound_region: br, .. }) | ty::RePlaceholder(ty::Placeholder { name: br, .. }) => { if let ty::BrNamed(_, name) = br { @@ -1741,7 +1765,7 @@ impl FmtPrinter<'_, '_, F> { impl FmtPrinter<'_, 'tcx, F> { pub fn name_all_regions( mut self, - value: &ty::Binder, + value: &ty::Binder<'tcx, T>, ) -> Result<(Self, (T, BTreeMap>)), fmt::Error> where T: Print<'tcx, Self, Output = Self, Error = fmt::Error> + TypeFoldable<'tcx>, @@ -1780,35 +1804,101 @@ impl FmtPrinter<'_, 'tcx, F> { define_scoped_cx!(self); let mut region_index = self.region_index; - let new_value = self.tcx.replace_late_bound_regions(value.clone(), |br| { - let _ = start_or_continue(&mut self, "for<", ", "); - let kind = match br.kind { - ty::BrNamed(_, name) => { - let _ = write!(self, "{}", name); - br.kind - } - ty::BrAnon(_) | ty::BrEnv => { - let name = loop { - let name = name_by_region_index(region_index); - region_index += 1; - if !self.used_region_names.contains(&name) { - break name; - } - }; - let _ = write!(self, "{}", name); - ty::BrNamed(DefId::local(CRATE_DEF_INDEX), name) + // If we want to print verbosly, then print *all* binders, even if they + // aren't named. Eventually, we might just want this as the default, but + // this is not *quite* right and changes the ordering of some output + // anyways. + let new_value = if self.tcx().sess.verbose() { + // anon index + 1 (BrEnv takes 0) -> name + let mut region_map: BTreeMap = BTreeMap::default(); + let bound_vars = value.bound_vars(); + for var in bound_vars { + match var { + ty::BoundVariableKind::Region(ty::BrNamed(_, name)) => { + let _ = start_or_continue(&mut self, "for<", ", "); + let _ = write!(self, "{}", name); + } + ty::BoundVariableKind::Region(ty::BrAnon(i)) => { + let _ = start_or_continue(&mut self, "for<", ", "); + let name = loop { + let name = name_by_region_index(region_index); + region_index += 1; + if !self.used_region_names.contains(&name) { + break name; + } + }; + let _ = write!(self, "{}", name); + region_map.insert(i + 1, name); + } + ty::BoundVariableKind::Region(ty::BrEnv) => { + let _ = start_or_continue(&mut self, "for<", ", "); + let name = loop { + let name = name_by_region_index(region_index); + region_index += 1; + if !self.used_region_names.contains(&name) { + break name; + } + }; + let _ = write!(self, "{}", name); + region_map.insert(0, name); + } + _ => continue, } - }; - self.tcx.mk_region(ty::ReLateBound(ty::INNERMOST, ty::BoundRegion { kind })) - }); - start_or_continue(&mut self, "", "> ")?; + } + start_or_continue(&mut self, "", "> ")?; + + self.tcx.replace_late_bound_regions(value.clone(), |br| { + let kind = match br.kind { + ty::BrNamed(_, _) => br.kind, + ty::BrAnon(i) => { + let name = region_map[&(i + 1)]; + ty::BrNamed(DefId::local(CRATE_DEF_INDEX), name) + } + ty::BrEnv => { + let name = region_map[&0]; + ty::BrNamed(DefId::local(CRATE_DEF_INDEX), name) + } + }; + self.tcx.mk_region(ty::ReLateBound( + ty::INNERMOST, + ty::BoundRegion { var: br.var, kind }, + )) + }) + } else { + let new_value = self.tcx.replace_late_bound_regions(value.clone(), |br| { + let _ = start_or_continue(&mut self, "for<", ", "); + let kind = match br.kind { + ty::BrNamed(_, name) => { + let _ = write!(self, "{}", name); + br.kind + } + ty::BrAnon(_) | ty::BrEnv => { + let name = loop { + let name = name_by_region_index(region_index); + region_index += 1; + if !self.used_region_names.contains(&name) { + break name; + } + }; + let _ = write!(self, "{}", name); + ty::BrNamed(DefId::local(CRATE_DEF_INDEX), name) + } + }; + self.tcx.mk_region(ty::ReLateBound( + ty::INNERMOST, + ty::BoundRegion { var: br.var, kind }, + )) + }); + start_or_continue(&mut self, "", "> ")?; + new_value + }; self.binder_depth += 1; self.region_index = region_index; Ok((self, new_value)) } - pub fn pretty_in_binder(self, value: &ty::Binder) -> Result + pub fn pretty_in_binder(self, value: &ty::Binder<'tcx, T>) -> Result where T: Print<'tcx, Self, Output = Self, Error = fmt::Error> + TypeFoldable<'tcx>, { @@ -1822,7 +1912,7 @@ impl FmtPrinter<'_, 'tcx, F> { pub fn pretty_wrap_binder Result>( self, - value: &ty::Binder, + value: &ty::Binder<'tcx, T>, f: C, ) -> Result where @@ -1836,28 +1926,52 @@ impl FmtPrinter<'_, 'tcx, F> { Ok(inner) } - fn prepare_late_bound_region_info(&mut self, value: &ty::Binder) + fn prepare_late_bound_region_info(&mut self, value: &ty::Binder<'tcx, T>) where T: TypeFoldable<'tcx>, { - struct LateBoundRegionNameCollector<'a>(&'a mut FxHashSet); - impl<'tcx> ty::fold::TypeVisitor<'tcx> for LateBoundRegionNameCollector<'_> { + debug!("prepare_late_bound_region_info(value: {:?})", value); + + struct LateBoundRegionNameCollector<'a, 'tcx> { + used_region_names: &'a mut FxHashSet, + type_collector: SsoHashSet>, + } + + impl<'tcx> ty::fold::TypeVisitor<'tcx> for LateBoundRegionNameCollector<'_, 'tcx> { + type BreakTy = (); + fn visit_region(&mut self, r: ty::Region<'tcx>) -> ControlFlow { - if let ty::ReLateBound(_, ty::BoundRegion { kind: ty::BrNamed(_, name) }) = *r { - self.0.insert(name); + debug!("LateBoundRegionNameCollector::visit_region(r: {:?}, address: {:p})", r, &r); + if let ty::ReLateBound(_, ty::BoundRegion { kind: ty::BrNamed(_, name), .. }) = *r { + self.used_region_names.insert(name); } r.super_visit_with(self) } + + // We collect types in order to prevent really large types from compiling for + // a really long time. See issue #83150 for why this is necessary. + fn visit_ty(&mut self, ty: Ty<'tcx>) -> ControlFlow { + debug!("LateBoundRegionNameCollector::visit_ty(ty: {:?}", ty); + let not_previously_inserted = self.type_collector.insert(ty); + if not_previously_inserted { + ty.super_visit_with(self) + } else { + ControlFlow::CONTINUE + } + } } self.used_region_names.clear(); - let mut collector = LateBoundRegionNameCollector(&mut self.used_region_names); + let mut collector = LateBoundRegionNameCollector { + used_region_names: &mut self.used_region_names, + type_collector: SsoHashSet::new(), + }; value.visit_with(&mut collector); self.region_index = 0; } } -impl<'tcx, T, P: PrettyPrinter<'tcx>> Print<'tcx, P> for ty::Binder +impl<'tcx, T, P: PrettyPrinter<'tcx>> Print<'tcx, P> for ty::Binder<'tcx, T> where T: Print<'tcx, P, Output = P, Error = P::Error> + TypeFoldable<'tcx>, { @@ -1944,28 +2058,28 @@ impl ty::TraitRef<'tcx> { } } -impl ty::Binder> { - pub fn print_only_trait_path(self) -> ty::Binder> { +impl ty::Binder<'tcx, ty::TraitRef<'tcx>> { + pub fn print_only_trait_path(self) -> ty::Binder<'tcx, TraitRefPrintOnlyTraitPath<'tcx>> { self.map_bound(|tr| tr.print_only_trait_path()) } } forward_display_to_print! { Ty<'tcx>, - &'tcx ty::List>>, + &'tcx ty::List>>, &'tcx ty::Const<'tcx>, // HACK(eddyb) these are exhaustive instead of generic, // because `for<'tcx>` isn't possible yet. - ty::Binder>, - ty::Binder>, - ty::Binder>, - ty::Binder>, - ty::Binder>, - ty::Binder>, - ty::Binder>, - ty::Binder, ty::Region<'tcx>>>, - ty::Binder, ty::Region<'tcx>>>, + ty::Binder<'tcx, ty::ExistentialPredicate<'tcx>>, + ty::Binder<'tcx, ty::TraitRef<'tcx>>, + ty::Binder<'tcx, TraitRefPrintOnlyTraitPath<'tcx>>, + ty::Binder<'tcx, ty::FnSig<'tcx>>, + ty::Binder<'tcx, ty::TraitPredicate<'tcx>>, + ty::Binder<'tcx, ty::SubtypePredicate<'tcx>>, + ty::Binder<'tcx, ty::ProjectionPredicate<'tcx>>, + ty::Binder<'tcx, ty::OutlivesPredicate, ty::Region<'tcx>>>, + ty::Binder<'tcx, ty::OutlivesPredicate, ty::Region<'tcx>>>, ty::OutlivesPredicate, ty::Region<'tcx>>, ty::OutlivesPredicate, ty::Region<'tcx>> diff --git a/compiler/rustc_middle/src/ty/query/mod.rs b/compiler/rustc_middle/src/ty/query/mod.rs index 51a214bc07bac..c170858ba85a1 100644 --- a/compiler/rustc_middle/src/ty/query/mod.rs +++ b/compiler/rustc_middle/src/ty/query/mod.rs @@ -14,8 +14,8 @@ use crate::middle::resolve_lifetime::{ObjectLifetimeDefault, Region, ResolveLife use crate::middle::stability::{self, DeprecationEntry}; use crate::mir; use crate::mir::interpret::GlobalId; +use crate::mir::interpret::{ConstAlloc, LitToConstError, LitToConstInput}; use crate::mir::interpret::{ConstValue, EvalToAllocationRawResult, EvalToConstValueResult}; -use crate::mir::interpret::{LitToConstError, LitToConstInput}; use crate::mir::mono::CodegenUnit; use crate::traits::query::{ CanonicalPredicateGoal, CanonicalProjectionGoal, CanonicalTyGoal, @@ -217,8 +217,11 @@ macro_rules! define_callbacks { fn default() -> Self { Providers { $($name: |_, key| bug!( - "`tcx.{}({:?})` unsupported by its crate", - stringify!($name), key + "`tcx.{}({:?})` unsupported by its crate; \ + perhaps the `{}` query was never assigned a provider function", + stringify!($name), + key, + stringify!($name), ),)* } } diff --git a/compiler/rustc_middle/src/ty/query/on_disk_cache.rs b/compiler/rustc_middle/src/ty/query/on_disk_cache.rs index d0cd8a48f99b3..416199b384000 100644 --- a/compiler/rustc_middle/src/ty/query/on_disk_cache.rs +++ b/compiler/rustc_middle/src/ty/query/on_disk_cache.rs @@ -4,7 +4,6 @@ use crate::mir::{self, interpret}; use crate::ty::codec::{RefDecodable, TyDecoder, TyEncoder}; use crate::ty::context::TyCtxt; use crate::ty::{self, Ty}; -use rustc_data_structures::fingerprint::{Fingerprint, FingerprintDecoder, FingerprintEncoder}; use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexSet}; use rustc_data_structures::sync::{HashMapExt, Lock, Lrc, OnceCell}; use rustc_data_structures::thin_vec::ThinVec; @@ -17,7 +16,7 @@ use rustc_index::vec::{Idx, IndexVec}; use rustc_query_system::dep_graph::DepContext; use rustc_query_system::query::QueryContext; use rustc_serialize::{ - opaque::{self, FileEncodeResult, FileEncoder}, + opaque::{self, FileEncodeResult, FileEncoder, IntEncodedWithFixedSize}, Decodable, Decoder, Encodable, Encoder, }; use rustc_session::{CrateDisambiguator, Session}; @@ -526,7 +525,7 @@ impl<'sess> OnDiskCache<'sess> { ) { let mut current_diagnostics = self.current_diagnostics.borrow_mut(); - let x = current_diagnostics.entry(dep_node_index).or_insert(Vec::new()); + let x = current_diagnostics.entry(dep_node_index).or_default(); x.extend(Into::>::into(diagnostics)); } @@ -913,12 +912,6 @@ impl<'a, 'tcx> Decodable> for DefId { } } -impl<'a, 'tcx> FingerprintDecoder for CacheDecoder<'a, 'tcx> { - fn decode_fingerprint(&mut self) -> Result { - Fingerprint::decode_opaque(&mut self.opaque) - } -} - impl<'a, 'tcx> Decodable> for &'tcx FxHashSet { fn decode(d: &mut CacheDecoder<'a, 'tcx>) -> Result { RefDecodable::decode(d) @@ -1011,12 +1004,6 @@ where } } -impl<'a, 'tcx, E: OpaqueEncoder> FingerprintEncoder for CacheEncoder<'a, 'tcx, E> { - fn encode_fingerprint(&mut self, f: &Fingerprint) -> Result<(), E::Error> { - self.encoder.encode_fingerprint(f) - } -} - impl<'a, 'tcx, E> Encodable> for SyntaxContext where E: 'a + OpaqueEncoder, @@ -1045,12 +1032,12 @@ where E: 'a + OpaqueEncoder, { fn encode(&self, s: &mut CacheEncoder<'a, 'tcx, E>) -> Result<(), E::Error> { - if *self == DUMMY_SP { + let span_data = self.data(); + if self.is_dummy() { TAG_PARTIAL_SPAN.encode(s)?; - return SyntaxContext::root().encode(s); + return span_data.ctxt.encode(s); } - let span_data = self.data(); let pos = s.source_map.byte_pos_to_line_and_col(span_data.lo); let partial_span = match &pos { Some((file_lo, _, _)) => !file_lo.contains(span_data.hi), @@ -1167,6 +1154,7 @@ where emit_f32(f32); emit_char(char); emit_str(&str); + emit_raw_bytes(&[u8]); } } @@ -1180,42 +1168,6 @@ impl<'a, 'tcx> Encodable> for [u8] { } } -// An integer that will always encode to 8 bytes. -struct IntEncodedWithFixedSize(u64); - -impl IntEncodedWithFixedSize { - pub const ENCODED_SIZE: usize = 8; -} - -impl Encodable for IntEncodedWithFixedSize { - fn encode(&self, e: &mut E) -> Result<(), E::Error> { - let start_pos = e.position(); - for i in 0..IntEncodedWithFixedSize::ENCODED_SIZE { - ((self.0 >> (i * 8)) as u8).encode(e)?; - } - let end_pos = e.position(); - assert_eq!((end_pos - start_pos), IntEncodedWithFixedSize::ENCODED_SIZE); - Ok(()) - } -} - -impl<'a> Decodable> for IntEncodedWithFixedSize { - fn decode(decoder: &mut opaque::Decoder<'a>) -> Result { - let mut value: u64 = 0; - let start_pos = decoder.position(); - - for i in 0..IntEncodedWithFixedSize::ENCODED_SIZE { - let byte: u8 = Decodable::decode(decoder)?; - value |= (byte as u64) << (i * 8); - } - - let end_pos = decoder.position(); - assert_eq!((end_pos - start_pos), IntEncodedWithFixedSize::ENCODED_SIZE); - - Ok(IntEncodedWithFixedSize(value)) - } -} - pub fn encode_query_results<'a, 'tcx, CTX, Q>( tcx: CTX, encoder: &mut CacheEncoder<'a, 'tcx, FileEncoder>, diff --git a/compiler/rustc_middle/src/ty/relate.rs b/compiler/rustc_middle/src/ty/relate.rs index 315e5d63d2bb3..b6f93c9bd59e7 100644 --- a/compiler/rustc_middle/src/ty/relate.rs +++ b/compiler/rustc_middle/src/ty/relate.rs @@ -4,7 +4,7 @@ //! types or regions but can be other things. Examples of type relations are //! subtyping, type equality, etc. -use crate::mir::interpret::{get_slice_bytes, ConstValue}; +use crate::mir::interpret::{get_slice_bytes, ConstValue, GlobalAlloc, Scalar}; use crate::ty::error::{ExpectedFound, TypeError}; use crate::ty::subst::{GenericArg, GenericArgKind, SubstsRef}; use crate::ty::{self, Ty, TyCtxt, TypeFoldable}; @@ -33,15 +33,6 @@ pub trait TypeRelation<'tcx>: Sized { /// relation. Just affects error messages. fn a_is_expected(&self) -> bool; - /// Whether we should look into the substs of unevaluated constants - /// even if `feature(const_evaluatable_checked)` is active. - /// - /// This is needed in `combine` to prevent accidentially creating - /// infinite types as we abuse `TypeRelation` to walk a type there. - fn visit_ct_substs(&self) -> bool { - false - } - fn with_cause(&mut self, _cause: Cause, f: F) -> R where F: FnOnce(&mut Self) -> R, @@ -102,9 +93,9 @@ pub trait TypeRelation<'tcx>: Sized { fn binders( &mut self, - a: ty::Binder, - b: ty::Binder, - ) -> RelateResult<'tcx, ty::Binder> + a: ty::Binder<'tcx, T>, + b: ty::Binder<'tcx, T>, + ) -> RelateResult<'tcx, ty::Binder<'tcx, T>> where T: Relate<'tcx>; } @@ -149,7 +140,7 @@ pub fn relate_substs>( ) -> RelateResult<'tcx, SubstsRef<'tcx>> { let tcx = relation.tcx(); - let params = a_subst.iter().zip(b_subst).enumerate().map(|(i, (a, b))| { + let params = iter::zip(a_subst, b_subst).enumerate().map(|(i, (a, b))| { let variance = variances.map_or(ty::Invariant, |v| v[i]); relation.relate_with_variance(variance, a, b) }); @@ -179,12 +170,8 @@ impl<'tcx> Relate<'tcx> for ty::FnSig<'tcx> { return Err(TypeError::ArgCount); } - let inputs_and_output = a - .inputs() - .iter() - .cloned() - .zip(b.inputs().iter().cloned()) - .map(|x| (x, false)) + let inputs_and_output = iter::zip(a.inputs(), b.inputs()) + .map(|(&a, &b)| ((a, b), false)) .chain(iter::once(((a.output(), b.output()), true))) .map(|((a, b), is_output)| { if is_output { @@ -192,6 +179,12 @@ impl<'tcx> Relate<'tcx> for ty::FnSig<'tcx> { } else { relation.relate_with_variance(ty::Contravariant, a, b) } + }) + .enumerate() + .map(|(i, r)| match r { + Err(TypeError::Sorts(exp_found)) => Err(TypeError::ArgumentSorts(exp_found, i)), + Err(TypeError::Mutability) => Err(TypeError::ArgumentMutability(i)), + r => r, }); Ok(ty::FnSig { inputs_and_output: tcx.mk_type_list(inputs_and_output)?, @@ -308,7 +301,7 @@ impl<'tcx> Relate<'tcx> for GeneratorWitness<'tcx> { ) -> RelateResult<'tcx, GeneratorWitness<'tcx>> { assert_eq!(a.0.len(), b.0.len()); let tcx = relation.tcx(); - let types = tcx.mk_type_list(a.0.iter().zip(b.0).map(|(a, b)| relation.relate(a, b)))?; + let types = tcx.mk_type_list(iter::zip(a.0, b.0).map(|(a, b)| relation.relate(a, b)))?; Ok(GeneratorWitness(types)) } } @@ -421,18 +414,20 @@ pub fn super_relate_tys>( let t = relation.relate(a_t, b_t)?; match relation.relate(sz_a, sz_b) { Ok(sz) => Ok(tcx.mk_ty(ty::Array(t, sz))), - // FIXME(#72219) Implement improved diagnostics for mismatched array - // length? - Err(err) if relation.tcx().lazy_normalization() => Err(err), Err(err) => { // Check whether the lengths are both concrete/known values, // but are unequal, for better diagnostics. + // + // It might seem dubious to eagerly evaluate these constants here, + // we however cannot end up with errors in `Relate` during both + // `type_of` and `predicates_of`. This means that evaluating the + // constants should not cause cycle errors here. let sz_a = sz_a.try_eval_usize(tcx, relation.param_env()); let sz_b = sz_b.try_eval_usize(tcx, relation.param_env()); match (sz_a, sz_b) { - (Some(sz_a_val), Some(sz_b_val)) => Err(TypeError::FixedArraySize( - expected_found(relation, sz_a_val, sz_b_val), - )), + (Some(sz_a_val), Some(sz_b_val)) if sz_a_val != sz_b_val => Err( + TypeError::FixedArraySize(expected_found(relation, sz_a_val, sz_b_val)), + ), _ => Err(err), } } @@ -447,7 +442,7 @@ pub fn super_relate_tys>( (&ty::Tuple(as_), &ty::Tuple(bs)) => { if as_.len() == bs.len() { Ok(tcx.mk_tup( - as_.iter().zip(bs).map(|(a, b)| relation.relate(a.expect_ty(), b.expect_ty())), + iter::zip(as_, bs).map(|(a, b)| relation.relate(a.expect_ty(), b.expect_ty())), )?) } else if !(as_.is_empty() || bs.is_empty()) { Err(TypeError::TupleSize(expected_found(relation, as_.len(), bs.len()))) @@ -496,123 +491,116 @@ pub fn super_relate_consts>( debug!("{}.super_relate_consts(a = {:?}, b = {:?})", relation.tag(), a, b); let tcx = relation.tcx(); - let eagerly_eval = |x: &'tcx ty::Const<'tcx>| x.eval(tcx, relation.param_env()).val; + // FIXME(oli-obk): once const generics can have generic types, this assertion + // will likely get triggered. Move to `normalize_erasing_regions` at that point. + let a_ty = tcx.erase_regions(a.ty); + let b_ty = tcx.erase_regions(b.ty); + if a_ty != b_ty { + relation.tcx().sess.delay_span_bug( + DUMMY_SP, + &format!("cannot relate constants of different types: {} != {}", a_ty, b_ty), + ); + } - // FIXME(eddyb) doesn't look like everything below checks that `a.ty == b.ty`. - // We could probably always assert it early, as const generic parameters - // are not allowed to depend on other generic parameters, i.e. are concrete. - // (although there could be normalization differences) + let eagerly_eval = |x: &'tcx ty::Const<'tcx>| x.eval(tcx, relation.param_env()); + let a = eagerly_eval(a); + let b = eagerly_eval(b); // Currently, the values that can be unified are primitive types, // and those that derive both `PartialEq` and `Eq`, corresponding // to structural-match types. - let new_const_val = match (eagerly_eval(a), eagerly_eval(b)) { + let is_match = match (a.val, b.val) { (ty::ConstKind::Infer(_), _) | (_, ty::ConstKind::Infer(_)) => { // The caller should handle these cases! bug!("var types encountered in super_relate_consts: {:?} {:?}", a, b) } - (ty::ConstKind::Error(d), _) | (_, ty::ConstKind::Error(d)) => Ok(ty::ConstKind::Error(d)), + (ty::ConstKind::Error(_), _) => return Ok(a), + (_, ty::ConstKind::Error(_)) => return Ok(b), - (ty::ConstKind::Param(a_p), ty::ConstKind::Param(b_p)) if a_p.index == b_p.index => { - return Ok(a); + (ty::ConstKind::Param(a_p), ty::ConstKind::Param(b_p)) => a_p.index == b_p.index, + (ty::ConstKind::Placeholder(p1), ty::ConstKind::Placeholder(p2)) => p1 == p2, + (ty::ConstKind::Value(a_val), ty::ConstKind::Value(b_val)) => { + check_const_value_eq(relation, a_val, b_val, a, b)? } - (ty::ConstKind::Placeholder(p1), ty::ConstKind::Placeholder(p2)) if p1 == p2 => { - return Ok(a); + + (ty::ConstKind::Unevaluated(au), ty::ConstKind::Unevaluated(bu)) + if tcx.features().const_evaluatable_checked => + { + tcx.try_unify_abstract_consts(((au.def, au.substs), (bu.def, bu.substs))) } - (ty::ConstKind::Value(a_val), ty::ConstKind::Value(b_val)) => { - let new_val = match (a_val, b_val) { - (ConstValue::Scalar(a_val), ConstValue::Scalar(b_val)) if a.ty == b.ty => { - if a_val == b_val { - Ok(ConstValue::Scalar(a_val)) - } else if let ty::FnPtr(_) = a.ty.kind() { - let a_instance = tcx.global_alloc(a_val.assert_ptr().alloc_id).unwrap_fn(); - let b_instance = tcx.global_alloc(b_val.assert_ptr().alloc_id).unwrap_fn(); - if a_instance == b_instance { - Ok(ConstValue::Scalar(a_val)) - } else { - Err(TypeError::ConstMismatch(expected_found(relation, a, b))) - } - } else { - Err(TypeError::ConstMismatch(expected_found(relation, a, b))) - } - } - (ConstValue::Slice { .. }, ConstValue::Slice { .. }) => { - let a_bytes = get_slice_bytes(&tcx, a_val); - let b_bytes = get_slice_bytes(&tcx, b_val); - if a_bytes == b_bytes { - Ok(a_val) - } else { - Err(TypeError::ConstMismatch(expected_found(relation, a, b))) - } - } + // While this is slightly incorrect, it shouldn't matter for `min_const_generics` + // and is the better alternative to waiting until `const_evaluatable_checked` can + // be stabilized. + (ty::ConstKind::Unevaluated(au), ty::ConstKind::Unevaluated(bu)) + if au.def == bu.def && au.promoted == bu.promoted => + { + let substs = + relation.relate_with_variance(ty::Variance::Invariant, au.substs, bu.substs)?; + return Ok(tcx.mk_const(ty::Const { + val: ty::ConstKind::Unevaluated(ty::Unevaluated { + def: au.def, + substs, + promoted: au.promoted, + }), + ty: a.ty, + })); + } + _ => false, + }; + if is_match { Ok(a) } else { Err(TypeError::ConstMismatch(expected_found(relation, a, b))) } +} - (ConstValue::ByRef { .. }, ConstValue::ByRef { .. }) => { - match a.ty.kind() { - ty::Array(..) | ty::Adt(..) | ty::Tuple(..) => { - let a_destructured = tcx.destructure_const(relation.param_env().and(a)); - let b_destructured = tcx.destructure_const(relation.param_env().and(b)); - - // Both the variant and each field have to be equal. - if a_destructured.variant == b_destructured.variant { - for (a_field, b_field) in - a_destructured.fields.iter().zip(b_destructured.fields.iter()) - { - relation.consts(a_field, b_field)?; - } - - Ok(a_val) - } else { - Err(TypeError::ConstMismatch(expected_found(relation, a, b))) - } - } - // FIXME(const_generics): There are probably some `TyKind`s - // which should be handled here. - _ => { - tcx.sess.delay_span_bug( - DUMMY_SP, - &format!("unexpected consts: a: {:?}, b: {:?}", a, b), - ); - Err(TypeError::ConstMismatch(expected_found(relation, a, b))) - } +fn check_const_value_eq>( + relation: &mut R, + a_val: ConstValue<'tcx>, + b_val: ConstValue<'tcx>, + // FIXME(oli-obk): these arguments should go away with valtrees + a: &'tcx ty::Const<'tcx>, + b: &'tcx ty::Const<'tcx>, + // FIXME(oli-obk): this should just be `bool` with valtrees +) -> RelateResult<'tcx, bool> { + let tcx = relation.tcx(); + Ok(match (a_val, b_val) { + (ConstValue::Scalar(Scalar::Int(a_val)), ConstValue::Scalar(Scalar::Int(b_val))) => { + a_val == b_val + } + (ConstValue::Scalar(Scalar::Ptr(a_val)), ConstValue::Scalar(Scalar::Ptr(b_val))) => { + a_val == b_val + || match (tcx.global_alloc(a_val.alloc_id), tcx.global_alloc(b_val.alloc_id)) { + (GlobalAlloc::Function(a_instance), GlobalAlloc::Function(b_instance)) => { + a_instance == b_instance } + _ => false, } + } - _ => Err(TypeError::ConstMismatch(expected_found(relation, a, b))), - }; - - new_val.map(ty::ConstKind::Value) + (ConstValue::Slice { .. }, ConstValue::Slice { .. }) => { + get_slice_bytes(&tcx, a_val) == get_slice_bytes(&tcx, b_val) } - ( - ty::ConstKind::Unevaluated(a_def, a_substs, None), - ty::ConstKind::Unevaluated(b_def, b_substs, None), - ) if tcx.features().const_evaluatable_checked && !relation.visit_ct_substs() => { - if tcx.try_unify_abstract_consts(((a_def, a_substs), (b_def, b_substs))) { - Ok(a.val) + (ConstValue::ByRef { .. }, ConstValue::ByRef { .. }) => { + let a_destructured = tcx.destructure_const(relation.param_env().and(a)); + let b_destructured = tcx.destructure_const(relation.param_env().and(b)); + + // Both the variant and each field have to be equal. + if a_destructured.variant == b_destructured.variant { + for (a_field, b_field) in iter::zip(a_destructured.fields, b_destructured.fields) { + relation.consts(a_field, b_field)?; + } + + true } else { - Err(TypeError::ConstMismatch(expected_found(relation, a, b))) + false } } - // While this is slightly incorrect, it shouldn't matter for `min_const_generics` - // and is the better alternative to waiting until `const_evaluatable_checked` can - // be stabilized. - ( - ty::ConstKind::Unevaluated(a_def, a_substs, a_promoted), - ty::ConstKind::Unevaluated(b_def, b_substs, b_promoted), - ) if a_def == b_def && a_promoted == b_promoted => { - let substs = - relation.relate_with_variance(ty::Variance::Invariant, a_substs, b_substs)?; - Ok(ty::ConstKind::Unevaluated(a_def, substs, a_promoted)) - } - _ => Err(TypeError::ConstMismatch(expected_found(relation, a, b))), - }; - new_const_val.map(|val| tcx.mk_const(ty::Const { val, ty: a.ty })) + _ => false, + }) } -impl<'tcx> Relate<'tcx> for &'tcx ty::List>> { +impl<'tcx> Relate<'tcx> for &'tcx ty::List>> { fn relate>( relation: &mut R, a: Self, @@ -634,13 +622,12 @@ impl<'tcx> Relate<'tcx> for &'tcx ty::List Ok(ty::Binder::bind(Trait( - relation.relate(ep_a.rebind(a), ep_b.rebind(b))?.skip_binder(), - ))), - (Projection(a), Projection(b)) => Ok(ty::Binder::bind(Projection( + (Trait(a), Trait(b)) => Ok(ep_a + .rebind(Trait(relation.relate(ep_a.rebind(a), ep_b.rebind(b))?.skip_binder()))), + (Projection(a), Projection(b)) => Ok(ep_a.rebind(Projection( relation.relate(ep_a.rebind(a), ep_b.rebind(b))?.skip_binder(), ))), (AutoTrait(a), AutoTrait(b)) if a == b => Ok(ep_a.rebind(AutoTrait(a))), @@ -703,12 +690,12 @@ impl<'tcx> Relate<'tcx> for &'tcx ty::Const<'tcx> { } } -impl<'tcx, T: Relate<'tcx>> Relate<'tcx> for ty::Binder { +impl<'tcx, T: Relate<'tcx>> Relate<'tcx> for ty::Binder<'tcx, T> { fn relate>( relation: &mut R, - a: ty::Binder, - b: ty::Binder, - ) -> RelateResult<'tcx, ty::Binder> { + a: ty::Binder<'tcx, T>, + b: ty::Binder<'tcx, T>, + ) -> RelateResult<'tcx, ty::Binder<'tcx, T>> { relation.binders(a, b) } } diff --git a/compiler/rustc_middle/src/ty/structural_impls.rs b/compiler/rustc_middle/src/ty/structural_impls.rs index 0ca94a9f1805d..7290c41d615df 100644 --- a/compiler/rustc_middle/src/ty/structural_impls.rs +++ b/compiler/rustc_middle/src/ty/structural_impls.rs @@ -454,10 +454,16 @@ impl<'a, 'tcx> Lift<'tcx> for ty::PredicateKind<'a> { } } -impl<'tcx, T: Lift<'tcx>> Lift<'tcx> for ty::Binder { - type Lifted = ty::Binder; +impl<'a, 'tcx, T: Lift<'tcx>> Lift<'tcx> for ty::Binder<'a, T> +where + >::Lifted: TypeFoldable<'tcx>, +{ + type Lifted = ty::Binder<'tcx, T::Lifted>; fn lift_to_tcx(self, tcx: TyCtxt<'tcx>) -> Option { - self.map_bound(|v| tcx.lift(v)).transpose() + let bound_vars = tcx.lift(self.bound_vars()); + tcx.lift(self.skip_binder()) + .zip(bound_vars) + .map(|(value, vars)| ty::Binder::bind_with_vars(value, vars)) } } @@ -581,6 +587,7 @@ impl<'a, 'tcx> Lift<'tcx> for ty::error::TypeError<'a> { UnsafetyMismatch(x) => UnsafetyMismatch(x), AbiMismatch(x) => AbiMismatch(x), Mutability => Mutability, + ArgumentMutability(i) => ArgumentMutability(i), TupleSize(x) => TupleSize(x), FixedArraySize(x) => FixedArraySize(x), ArgCount => ArgCount, @@ -601,6 +608,7 @@ impl<'a, 'tcx> Lift<'tcx> for ty::error::TypeError<'a> { CyclicTy(t) => return tcx.lift(t).map(|t| CyclicTy(t)), CyclicConst(ct) => return tcx.lift(ct).map(|ct| CyclicConst(ct)), ProjectionMismatched(x) => ProjectionMismatched(x), + ArgumentSorts(x, i) => return tcx.lift(x).map(|x| ArgumentSorts(x, i)), Sorts(x) => return tcx.lift(x).map(Sorts), ExistentialMismatch(x) => return tcx.lift(x).map(ExistentialMismatch), ConstMismatch(x) => return tcx.lift(x).map(ConstMismatch), @@ -749,7 +757,7 @@ impl<'tcx, T: TypeFoldable<'tcx>> TypeFoldable<'tcx> for Box<[T]> { } } -impl<'tcx, T: TypeFoldable<'tcx>> TypeFoldable<'tcx> for ty::Binder { +impl<'tcx, T: TypeFoldable<'tcx>> TypeFoldable<'tcx> for ty::Binder<'tcx, T> { fn super_fold_with>(self, folder: &mut F) -> Self { self.map_bound(|ty| ty.fold_with(folder)) } @@ -767,7 +775,7 @@ impl<'tcx, T: TypeFoldable<'tcx>> TypeFoldable<'tcx> for ty::Binder { } } -impl<'tcx> TypeFoldable<'tcx> for &'tcx ty::List>> { +impl<'tcx> TypeFoldable<'tcx> for &'tcx ty::List>> { fn super_fold_with>(self, folder: &mut F) -> Self { ty::util::fold_list(self, folder, |tcx, v| tcx.intern_poly_existential_predicates(v)) } @@ -1031,8 +1039,12 @@ impl<'tcx> TypeFoldable<'tcx> for ty::ConstKind<'tcx> { match self { ty::ConstKind::Infer(ic) => ty::ConstKind::Infer(ic.fold_with(folder)), ty::ConstKind::Param(p) => ty::ConstKind::Param(p.fold_with(folder)), - ty::ConstKind::Unevaluated(did, substs, promoted) => { - ty::ConstKind::Unevaluated(did, substs.fold_with(folder), promoted) + ty::ConstKind::Unevaluated(ty::Unevaluated { def, substs, promoted }) => { + ty::ConstKind::Unevaluated(ty::Unevaluated { + def, + substs: substs.fold_with(folder), + promoted, + }) } ty::ConstKind::Value(_) | ty::ConstKind::Bound(..) @@ -1045,7 +1057,7 @@ impl<'tcx> TypeFoldable<'tcx> for ty::ConstKind<'tcx> { match *self { ty::ConstKind::Infer(ic) => ic.visit_with(visitor), ty::ConstKind::Param(p) => p.visit_with(visitor), - ty::ConstKind::Unevaluated(_, substs, _) => substs.visit_with(visitor), + ty::ConstKind::Unevaluated(ct) => ct.substs.visit_with(visitor), ty::ConstKind::Value(_) | ty::ConstKind::Bound(..) | ty::ConstKind::Placeholder(_) diff --git a/compiler/rustc_middle/src/ty/sty.rs b/compiler/rustc_middle/src/ty/sty.rs index 6c074d3af5c4b..691bfcc98d105 100644 --- a/compiler/rustc_middle/src/ty/sty.rs +++ b/compiler/rustc_middle/src/ty/sty.rs @@ -5,6 +5,8 @@ use self::TyKind::*; use crate::infer::canonical::Canonical; +use crate::ty::fold::BoundVarsCollector; +use crate::ty::fold::ValidateBoundVars; use crate::ty::subst::{GenericArg, InternalSubsts, Subst, SubstsRef}; use crate::ty::InferTy::{self, *}; use crate::ty::{ @@ -62,22 +64,10 @@ pub enum BoundRegionKind { #[derive(Copy, Clone, PartialEq, Eq, Hash, TyEncodable, TyDecodable, Debug, PartialOrd, Ord)] #[derive(HashStable)] pub struct BoundRegion { + pub var: BoundVar, pub kind: BoundRegionKind, } -impl BoundRegion { - /// When canonicalizing, we replace unbound inference variables and free - /// regions with anonymous late bound regions. This method asserts that - /// we have an anonymous late bound region, which hence may refer to - /// a canonical variable. - pub fn assert_bound_var(&self) -> BoundVar { - match self.kind { - BoundRegionKind::BrAnon(var) => BoundVar::from_u32(var), - _ => bug!("bound region is not anonymous"), - } - } -} - impl BoundRegionKind { pub fn is_named(&self) -> bool { match *self { @@ -90,7 +80,7 @@ impl BoundRegionKind { /// Defines the kinds of types. /// /// N.B., if you change this, you'll probably want to change the corresponding -/// AST structure in `librustc_ast/ast.rs` as well. +/// AST structure in `rustc_ast/src/ast.rs` as well. #[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, TyEncodable, TyDecodable, Debug)] #[derive(HashStable)] #[rustc_diagnostic_item = "TyKind"] @@ -160,8 +150,8 @@ pub enum TyKind<'tcx> { /// ``` FnPtr(PolyFnSig<'tcx>), - /// A trait, defined with `trait`. - Dynamic(&'tcx List>>, ty::Region<'tcx>), + /// A trait object. Written as `dyn for<'b> Trait<'b, Assoc = u32> + Send + 'a`. + Dynamic(&'tcx List>>, ty::Region<'tcx>), /// The anonymous type of a closure. Used to represent the type of /// `|a| a`. @@ -173,7 +163,7 @@ pub enum TyKind<'tcx> { /// A type representing the types stored inside a generator. /// This should only appear in GeneratorInteriors. - GeneratorWitness(Binder<&'tcx List>>), + GeneratorWitness(Binder<'tcx, &'tcx List>>), /// The never type `!`. Never, @@ -231,8 +221,8 @@ impl TyKind<'tcx> { } // `TyKind` is used a lot. Make sure it doesn't unintentionally get bigger. -#[cfg(target_arch = "x86_64")] -static_assert_size!(TyKind<'_>, 24); +#[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))] +static_assert_size!(TyKind<'_>, 32); /// A closure can be modeled as a struct that looks like: /// @@ -747,7 +737,7 @@ impl<'tcx> ExistentialPredicate<'tcx> { } } -impl<'tcx> Binder> { +impl<'tcx> Binder<'tcx, ExistentialPredicate<'tcx>> { pub fn with_self_ty(&self, tcx: TyCtxt<'tcx>, self_ty: Ty<'tcx>) -> ty::Predicate<'tcx> { use crate::ty::ToPredicate; match self.skip_binder() { @@ -768,7 +758,7 @@ impl<'tcx> Binder> { } } -impl<'tcx> List>> { +impl<'tcx> List>> { /// Returns the "principal `DefId`" of this set of existential predicates. /// /// A Rust trait object type consists (in addition to a lifetime bound) @@ -794,7 +784,7 @@ impl<'tcx> List>> { /// is `{Send, Sync}`, while there is no principal. These trait objects /// have a "trivial" vtable consisting of just the size, alignment, /// and destructor. - pub fn principal(&self) -> Option>> { + pub fn principal(&self) -> Option>> { self[0] .map_bound(|this| match this { ExistentialPredicate::Trait(tr) => Some(tr), @@ -810,7 +800,7 @@ impl<'tcx> List>> { #[inline] pub fn projection_bounds<'a>( &'a self, - ) -> impl Iterator>> + 'a { + ) -> impl Iterator>> + 'a { self.iter().filter_map(|predicate| { predicate .map_bound(|pred| match pred { @@ -875,10 +865,10 @@ impl<'tcx> TraitRef<'tcx> { } } -pub type PolyTraitRef<'tcx> = Binder>; +pub type PolyTraitRef<'tcx> = Binder<'tcx, TraitRef<'tcx>>; impl<'tcx> PolyTraitRef<'tcx> { - pub fn self_ty(&self) -> Binder> { + pub fn self_ty(&self) -> Binder<'tcx, Ty<'tcx>> { self.map_bound_ref(|tr| tr.self_ty()) } @@ -931,7 +921,7 @@ impl<'tcx> ExistentialTraitRef<'tcx> { } } -pub type PolyExistentialTraitRef<'tcx> = Binder>; +pub type PolyExistentialTraitRef<'tcx> = Binder<'tcx, ExistentialTraitRef<'tcx>>; impl<'tcx> PolyExistentialTraitRef<'tcx> { pub fn def_id(&self) -> DefId { @@ -947,52 +937,56 @@ impl<'tcx> PolyExistentialTraitRef<'tcx> { } } +#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, TyEncodable, TyDecodable)] +#[derive(HashStable)] +pub enum BoundVariableKind { + Ty(BoundTyKind), + Region(BoundRegionKind), + Const, +} + /// Binder is a binder for higher-ranked lifetimes or types. It is part of the /// compiler's representation for things like `for<'a> Fn(&'a isize)` /// (which would be represented by the type `PolyTraitRef == -/// Binder`). Note that when we instantiate, +/// Binder<'tcx, TraitRef>`). Note that when we instantiate, /// erase, or otherwise "discharge" these bound vars, we change the -/// type from `Binder` to just `T` (see +/// type from `Binder<'tcx, T>` to just `T` (see /// e.g., `liberate_late_bound_regions`). /// /// `Decodable` and `Encodable` are implemented for `Binder` using the `impl_binder_encode_decode!` macro. #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)] -pub struct Binder(T); +pub struct Binder<'tcx, T>(T, &'tcx List); -impl Binder { +impl<'tcx, T> Binder<'tcx, T> +where + T: TypeFoldable<'tcx>, +{ /// Wraps `value` in a binder, asserting that `value` does not /// contain any bound vars that would be bound by the /// binder. This is commonly used to 'inject' a value T into a /// different binding level. - pub fn dummy<'tcx>(value: T) -> Binder - where - T: TypeFoldable<'tcx>, - { + pub fn dummy(value: T) -> Binder<'tcx, T> { debug_assert!(!value.has_escaping_bound_vars()); - Binder(value) + Binder(value, ty::List::empty()) } /// Wraps `value` in a binder, binding higher-ranked vars (if any). - pub fn bind(value: T) -> Binder { - Binder(value) + pub fn bind(value: T, tcx: TyCtxt<'tcx>) -> Binder<'tcx, T> { + let mut collector = BoundVarsCollector::new(); + value.visit_with(&mut collector); + Binder(value, collector.into_vars(tcx)) } - /// Wraps `value` in a binder without actually binding any currently - /// unbound variables. - /// - /// Note that this will shift all debrujin indices of escaping bound variables - /// by 1 to avoid accidential captures. - pub fn wrap_nonbinding(tcx: TyCtxt<'tcx>, value: T) -> Binder - where - T: TypeFoldable<'tcx>, - { - if value.has_escaping_bound_vars() { - Binder::bind(super::fold::shift_vars(tcx, value, 1)) - } else { - Binder::dummy(value) + pub fn bind_with_vars(value: T, vars: &'tcx List) -> Binder<'tcx, T> { + if cfg!(debug_assertions) { + let mut validator = ValidateBoundVars::new(vars); + value.visit_with(&mut validator); } + Binder(value, vars) } +} +impl<'tcx, T> Binder<'tcx, T> { /// Skips the binder and returns the "bound" value. This is a /// risky thing to do because it's easy to get confused about /// De Bruijn indices and the like. It is usually better to @@ -1013,22 +1007,39 @@ impl Binder { self.0 } - pub fn as_ref(&self) -> Binder<&T> { - Binder(&self.0) + pub fn bound_vars(&self) -> &'tcx List { + self.1 } - pub fn map_bound_ref(&self, f: F) -> Binder + pub fn as_ref(&self) -> Binder<'tcx, &T> { + Binder(&self.0, self.1) + } + + pub fn map_bound_ref_unchecked(&self, f: F) -> Binder<'tcx, U> + where + F: FnOnce(&T) -> U, + { + let value = f(&self.0); + Binder(value, self.1) + } + + pub fn map_bound_ref>(&self, f: F) -> Binder<'tcx, U> where F: FnOnce(&T) -> U, { self.as_ref().map_bound(f) } - pub fn map_bound(self, f: F) -> Binder + pub fn map_bound>(self, f: F) -> Binder<'tcx, U> where F: FnOnce(T) -> U, { - Binder(f(self.0)) + let value = f(self.0); + if cfg!(debug_assertions) { + let mut validator = ValidateBoundVars::new(self.1); + value.visit_with(&mut validator); + } + Binder(value, self.1) } /// Wraps a `value` in a binder, using the same bound variables as the @@ -1040,8 +1051,15 @@ impl Binder { /// don't actually track bound vars. However, semantically, it is different /// because bound vars aren't allowed to change here, whereas they are /// in `bind`. This may be (debug) asserted in the future. - pub fn rebind(&self, value: U) -> Binder { - Binder(value) + pub fn rebind(&self, value: U) -> Binder<'tcx, U> + where + U: TypeFoldable<'tcx>, + { + if cfg!(debug_assertions) { + let mut validator = ValidateBoundVars::new(self.bound_vars()); + value.visit_with(&mut validator); + } + Binder(value, self.1) } /// Unwraps and returns the value within, but only if it contains @@ -1054,45 +1072,32 @@ impl Binder { /// binders, but that would require adjusting the debruijn /// indices, and given the shallow binding structure we often use, /// would not be that useful.) - pub fn no_bound_vars<'tcx>(self) -> Option + pub fn no_bound_vars(self) -> Option where T: TypeFoldable<'tcx>, { if self.0.has_escaping_bound_vars() { None } else { Some(self.skip_binder()) } } - /// Given two things that have the same binder level, - /// and an operation that wraps on their contents, executes the operation - /// and then wraps its result. - /// - /// `f` should consider bound regions at depth 1 to be free, and - /// anything it produces with bound regions at depth 1 will be - /// bound in the resulting return value. - pub fn fuse(self, u: Binder, f: F) -> Binder - where - F: FnOnce(T, U) -> R, - { - Binder(f(self.0, u.0)) - } - /// Splits the contents into two things that share the same binder /// level as the original, returning two distinct binders. /// /// `f` should consider bound regions at depth 1 to be free, and /// anything it produces with bound regions at depth 1 will be /// bound in the resulting return values. - pub fn split(self, f: F) -> (Binder, Binder) + pub fn split(self, f: F) -> (Binder<'tcx, U>, Binder<'tcx, V>) where F: FnOnce(T) -> (U, V), { let (u, v) = f(self.0); - (Binder(u), Binder(v)) + (Binder(u, self.1), Binder(v, self.1)) } } -impl Binder> { - pub fn transpose(self) -> Option> { - self.0.map(Binder) +impl<'tcx, T> Binder<'tcx, Option> { + pub fn transpose(self) -> Option> { + let bound_vars = self.1; + self.0.map(|v| Binder(v, bound_vars)) } } @@ -1155,19 +1160,7 @@ pub struct GenSig<'tcx> { pub return_ty: Ty<'tcx>, } -pub type PolyGenSig<'tcx> = Binder>; - -impl<'tcx> PolyGenSig<'tcx> { - pub fn resume_ty(&self) -> ty::Binder> { - self.map_bound_ref(|sig| sig.resume_ty) - } - pub fn yield_ty(&self) -> ty::Binder> { - self.map_bound_ref(|sig| sig.yield_ty) - } - pub fn return_ty(&self) -> ty::Binder> { - self.map_bound_ref(|sig| sig.return_ty) - } -} +pub type PolyGenSig<'tcx> = Binder<'tcx, GenSig<'tcx>>; /// Signature of a function type, which we have arbitrarily /// decided to use to refer to the input/output types. @@ -1205,22 +1198,22 @@ impl<'tcx> FnSig<'tcx> { } } -pub type PolyFnSig<'tcx> = Binder>; +pub type PolyFnSig<'tcx> = Binder<'tcx, FnSig<'tcx>>; impl<'tcx> PolyFnSig<'tcx> { #[inline] - pub fn inputs(&self) -> Binder<&'tcx [Ty<'tcx>]> { - self.map_bound_ref(|fn_sig| fn_sig.inputs()) + pub fn inputs(&self) -> Binder<'tcx, &'tcx [Ty<'tcx>]> { + self.map_bound_ref_unchecked(|fn_sig| fn_sig.inputs()) } #[inline] - pub fn input(&self, index: usize) -> ty::Binder> { + pub fn input(&self, index: usize) -> ty::Binder<'tcx, Ty<'tcx>> { self.map_bound_ref(|fn_sig| fn_sig.inputs()[index]) } - pub fn inputs_and_output(&self) -> ty::Binder<&'tcx List>> { + pub fn inputs_and_output(&self) -> ty::Binder<'tcx, &'tcx List>> { self.map_bound_ref(|fn_sig| fn_sig.inputs_and_output) } #[inline] - pub fn output(&self) -> ty::Binder> { + pub fn output(&self) -> ty::Binder<'tcx, Ty<'tcx>> { self.map_bound_ref(|fn_sig| fn_sig.output()) } pub fn c_variadic(&self) -> bool { @@ -1234,7 +1227,7 @@ impl<'tcx> PolyFnSig<'tcx> { } } -pub type CanonicalPolyFnSig<'tcx> = Canonical<'tcx, Binder>>; +pub type CanonicalPolyFnSig<'tcx> = Canonical<'tcx, Binder<'tcx, FnSig<'tcx>>>; #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, TyEncodable, TyDecodable)] #[derive(HashStable)] @@ -1248,10 +1241,6 @@ impl<'tcx> ParamTy { ParamTy { index, name } } - pub fn for_self() -> ParamTy { - ParamTy::new(0, kw::SelfUpper) - } - pub fn for_def(def: &ty::GenericParamDef) -> ParamTy { ParamTy::new(def.index, def.name) } @@ -1269,7 +1258,7 @@ pub struct ParamConst { pub name: Symbol, } -impl<'tcx> ParamConst { +impl ParamConst { pub fn new(index: u32, name: Symbol) -> ParamConst { ParamConst { index, name } } @@ -1277,10 +1266,6 @@ impl<'tcx> ParamConst { pub fn for_def(def: &ty::GenericParamDef) -> ParamConst { ParamConst::new(def.index, def.name) } - - pub fn to_const(self, tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> &'tcx ty::Const<'tcx> { - tcx.mk_const_param(self.index, self.name, ty) - } } pub type Region<'tcx> = &'tcx RegionKind; @@ -1486,7 +1471,7 @@ pub struct ExistentialProjection<'tcx> { pub ty: Ty<'tcx>, } -pub type PolyExistentialProjection<'tcx> = Binder>; +pub type PolyExistentialProjection<'tcx> = Binder<'tcx, ExistentialProjection<'tcx>>; impl<'tcx> ExistentialProjection<'tcx> { /// Extracts the underlying existential trait reference from this projection. @@ -1580,35 +1565,6 @@ impl RegionKind { } } - /// Adjusts any De Bruijn indices so as to make `to_binder` the - /// innermost binder. That is, if we have something bound at `to_binder`, - /// it will now be bound at INNERMOST. This is an appropriate thing to do - /// when moving a region out from inside binders: - /// - /// ``` - /// for<'a> fn(for<'b> for<'c> fn(&'a u32), _) - /// // Binder: D3 D2 D1 ^^ - /// ``` - /// - /// Here, the region `'a` would have the De Bruijn index D3, - /// because it is the bound 3 binders out. However, if we wanted - /// to refer to that region `'a` in the second argument (the `_`), - /// those two binders would not be in scope. In that case, we - /// might invoke `shift_out_to_binder(D3)`. This would adjust the - /// De Bruijn index of `'a` to D1 (the innermost binder). - /// - /// If we invoke `shift_out_to_binder` and the region is in fact - /// bound by one of the binders we are shifting out of, that is an - /// error (and should fail an assertion failure). - pub fn shifted_out_to_binder(&self, to_binder: ty::DebruijnIndex) -> RegionKind { - match *self { - ty::ReLateBound(debruijn, r) => { - ty::ReLateBound(debruijn.shifted_out_to_binder(to_binder), r) - } - r => r, - } - } - pub fn type_flags(&self) -> TypeFlags { let mut flags = TypeFlags::empty(); @@ -2160,7 +2116,7 @@ impl<'tcx> TyS<'tcx> { /// /// Note that during type checking, we use an inference variable /// to represent the closure kind, because it has not yet been - /// inferred. Once upvar inference (in `src/librustc_typeck/check/upvar.rs`) + /// inferred. Once upvar inference (in `rustc_typeck/src/check/upvar.rs`) /// is complete, that type variable will be unified. pub fn to_opt_closure_kind(&self) -> Option { match self.kind() { diff --git a/compiler/rustc_middle/src/ty/subst.rs b/compiler/rustc_middle/src/ty/subst.rs index 5d1b976ae973d..c84ca61122fe2 100644 --- a/compiler/rustc_middle/src/ty/subst.rs +++ b/compiler/rustc_middle/src/ty/subst.rs @@ -1,5 +1,6 @@ // Type substitutions. +use crate::mir; use crate::ty::codec::{TyDecoder, TyEncoder}; use crate::ty::fold::{TypeFoldable, TypeFolder, TypeVisitor}; use crate::ty::sty::{ClosureSubsts, GeneratorSubsts}; @@ -448,7 +449,10 @@ impl<'a, 'tcx> TypeFolder<'tcx> for SubstFolder<'a, 'tcx> { self.tcx } - fn fold_binder>(&mut self, t: ty::Binder) -> ty::Binder { + fn fold_binder>( + &mut self, + t: ty::Binder<'tcx, T>, + ) -> ty::Binder<'tcx, T> { self.binders_passed += 1; let t = t.super_fold_with(self); self.binders_passed -= 1; @@ -503,6 +507,11 @@ impl<'a, 'tcx> TypeFolder<'tcx> for SubstFolder<'a, 'tcx> { c.super_fold_with(self) } } + + #[inline] + fn fold_mir_const(&mut self, c: mir::ConstantKind<'tcx>) -> mir::ConstantKind<'tcx> { + c.super_fold_with(self) + } } impl<'a, 'tcx> SubstFolder<'a, 'tcx> { diff --git a/compiler/rustc_middle/src/ty/trait_def.rs b/compiler/rustc_middle/src/ty/trait_def.rs index ce17a724e25d3..33065bc3a7bd3 100644 --- a/compiler/rustc_middle/src/ty/trait_def.rs +++ b/compiler/rustc_middle/src/ty/trait_def.rs @@ -69,6 +69,12 @@ pub struct TraitImpls { non_blanket_impls: FxHashMap>, } +impl TraitImpls { + pub fn blanket_impls(&self) -> &[DefId] { + self.blanket_impls.as_slice() + } +} + impl<'tcx> TraitDef { pub fn new( def_id: DefId, diff --git a/compiler/rustc_middle/src/ty/util.rs b/compiler/rustc_middle/src/ty/util.rs index 8edde8794ed27..7d09ca5152ffe 100644 --- a/compiler/rustc_middle/src/ty/util.rs +++ b/compiler/rustc_middle/src/ty/util.rs @@ -21,7 +21,7 @@ use rustc_macros::HashStable; use rustc_span::{Span, DUMMY_SP}; use rustc_target::abi::{Integer, Size, TargetDataLayout}; use smallvec::SmallVec; -use std::{cmp, fmt}; +use std::{cmp, fmt, iter}; #[derive(Copy, Clone, Debug)] pub struct Discr<'tcx> { @@ -414,9 +414,7 @@ impl<'tcx> TyCtxt<'tcx> { _ => bug!(), }; - let result = item_substs - .iter() - .zip(impl_substs.iter()) + let result = iter::zip(item_substs, impl_substs) .filter(|&(_, k)| { match k.unpack() { GenericArgKind::Lifetime(&ty::RegionKind::ReEarlyBound(ref ebr)) => { @@ -501,10 +499,9 @@ impl<'tcx> TyCtxt<'tcx> { self, closure_def_id: DefId, closure_substs: SubstsRef<'tcx>, - ) -> Option>> { + env_region: ty::RegionKind, + ) -> Option> { let closure_ty = self.mk_closure(closure_def_id, closure_substs); - let br = ty::BoundRegion { kind: ty::BrEnv }; - let env_region = ty::ReLateBound(ty::INNERMOST, br); let closure_kind_ty = closure_substs.as_closure().kind_ty(); let closure_kind = closure_kind_ty.to_opt_closure_kind()?; let env_ty = match closure_kind { @@ -512,7 +509,7 @@ impl<'tcx> TyCtxt<'tcx> { ty::ClosureKind::FnMut => self.mk_mut_ref(self.mk_region(env_region), closure_ty), ty::ClosureKind::FnOnce => closure_ty, }; - Some(ty::Binder::bind(env_ty)) + Some(env_ty) } /// Returns `true` if the node pointed to by `def_id` is a `static` item. @@ -701,7 +698,6 @@ impl<'tcx> ty::TyS<'tcx> { /// optimization as well as the rules around static values. Note /// that the `Freeze` trait is not exposed to end users and is /// effectively an implementation detail. - // FIXME: use `TyCtxtAt` instead of separate `Span`. pub fn is_freeze(&'tcx self, tcx_at: TyCtxtAt<'tcx>, param_env: ty::ParamEnv<'tcx>) -> bool { self.is_trivially_freeze() || tcx_at.is_freeze_raw(param_env.and(self)) } @@ -741,6 +737,46 @@ impl<'tcx> ty::TyS<'tcx> { } } + /// Checks whether values of this type `T` implement the `Unpin` trait. + pub fn is_unpin(&'tcx self, tcx_at: TyCtxtAt<'tcx>, param_env: ty::ParamEnv<'tcx>) -> bool { + self.is_trivially_unpin() || tcx_at.is_unpin_raw(param_env.and(self)) + } + + /// Fast path helper for testing if a type is `Unpin`. + /// + /// Returning true means the type is known to be `Unpin`. Returning + /// `false` means nothing -- could be `Unpin`, might not be. + fn is_trivially_unpin(&self) -> bool { + match self.kind() { + ty::Int(_) + | ty::Uint(_) + | ty::Float(_) + | ty::Bool + | ty::Char + | ty::Str + | ty::Never + | ty::Ref(..) + | ty::RawPtr(_) + | ty::FnDef(..) + | ty::Error(_) + | ty::FnPtr(_) => true, + ty::Tuple(_) => self.tuple_fields().all(Self::is_trivially_unpin), + ty::Slice(elem_ty) | ty::Array(elem_ty, _) => elem_ty.is_trivially_unpin(), + ty::Adt(..) + | ty::Bound(..) + | ty::Closure(..) + | ty::Dynamic(..) + | ty::Foreign(_) + | ty::Generator(..) + | ty::GeneratorWitness(_) + | ty::Infer(_) + | ty::Opaque(..) + | ty::Param(_) + | ty::Placeholder(_) + | ty::Projection(_) => false, + } + } + /// If `ty.needs_drop(...)` returns `true`, then `ty` is definitely /// non-copy and *might* have a destructor attached; if it returns /// `false`, then `ty` definitely has no destructor (i.e., no drop glue). diff --git a/compiler/rustc_middle/src/ty/walk.rs b/compiler/rustc_middle/src/ty/walk.rs index bb7fc661d2d8a..c2fe5f1ef3f62 100644 --- a/compiler/rustc_middle/src/ty/walk.rs +++ b/compiler/rustc_middle/src/ty/walk.rs @@ -195,8 +195,8 @@ fn push_inner<'tcx>(stack: &mut TypeWalkerStack<'tcx>, parent: GenericArg<'tcx>) | ty::ConstKind::Value(_) | ty::ConstKind::Error(_) => {} - ty::ConstKind::Unevaluated(_, substs, _) => { - stack.extend(substs.iter().rev()); + ty::ConstKind::Unevaluated(ct) => { + stack.extend(ct.substs.iter().rev()); } } } diff --git a/compiler/rustc_mir/src/borrow_check/diagnostics/conflict_errors.rs b/compiler/rustc_mir/src/borrow_check/diagnostics/conflict_errors.rs index b0b58a8d00367..9f19a474ca38b 100644 --- a/compiler/rustc_mir/src/borrow_check/diagnostics/conflict_errors.rs +++ b/compiler/rustc_mir/src/borrow_check/diagnostics/conflict_errors.rs @@ -10,16 +10,18 @@ use rustc_middle::mir::{ FakeReadCause, Local, LocalDecl, LocalInfo, LocalKind, Location, Operand, Place, PlaceRef, ProjectionElem, Rvalue, Statement, StatementKind, Terminator, TerminatorKind, VarBindingForm, }; -use rustc_middle::ty::{self, suggest_constraining_type_param, Instance, Ty}; -use rustc_span::{source_map::DesugaringKind, symbol::sym, Span}; +use rustc_middle::ty::{self, suggest_constraining_type_param, Ty, TypeFoldable}; +use rustc_span::source_map::DesugaringKind; +use rustc_span::symbol::sym; +use rustc_span::{Span, DUMMY_SP}; use crate::dataflow::drop_flag_effects; use crate::dataflow::indexes::{MoveOutIndex, MovePathIndex}; use crate::util::borrowck_errors; use crate::borrow_check::{ - borrow_set::BorrowData, prefixes::IsPrefixOf, InitializationRequiringAction, MirBorrowckCtxt, - PrefixSet, WriteKind, + borrow_set::BorrowData, diagnostics::Instance, prefixes::IsPrefixOf, + InitializationRequiringAction, MirBorrowckCtxt, PrefixSet, WriteKind, }; use super::{ @@ -195,7 +197,11 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { ); } } - FnSelfUseKind::Normal { self_arg, implicit_into_iter } => { + FnSelfUseKind::Normal { + self_arg, + implicit_into_iter, + is_option_or_result, + } => { if implicit_into_iter { err.span_label( fn_call_span, @@ -213,13 +219,22 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { ), ); } + if is_option_or_result { + err.span_suggestion_verbose( + fn_call_span.shrink_to_lo(), + "consider calling `.as_ref()` to borrow the type's contents", + "as_ref().".to_string(), + Applicability::MachineApplicable, + ); + } // Avoid pointing to the same function in multiple different - // error messages - if self.fn_self_span_reported.insert(self_arg.span) { + // error messages. + if span != DUMMY_SP && self.fn_self_span_reported.insert(self_arg.span) + { err.span_note( - self_arg.span, - &format!("this function takes ownership of the receiver `self`, which moves {}", place_name) - ); + self_arg.span, + &format!("this function takes ownership of the receiver `self`, which moves {}", place_name) + ); } } // Deref::deref takes &self, which cannot cause a move @@ -261,7 +276,24 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { if let Some(DesugaringKind::ForLoop(_)) = move_span.desugaring_kind() { let sess = self.infcx.tcx.sess; - if let Ok(snippet) = sess.source_map().span_to_snippet(move_span) { + let ty = used_place.ty(self.body, self.infcx.tcx).ty; + // If we have a `&mut` ref, we need to reborrow. + if let ty::Ref(_, _, hir::Mutability::Mut) = ty.kind() { + // If we are in a loop this will be suggested later. + if !is_loop_move { + err.span_suggestion_verbose( + move_span.shrink_to_lo(), + &format!( + "consider creating a fresh reborrow of {} here", + self.describe_place(moved_place.as_ref()) + .map(|n| format!("`{}`", n)) + .unwrap_or_else(|| "the mutable reference".to_string()), + ), + format!("&mut *"), + Applicability::MachineApplicable, + ); + } + } else if let Ok(snippet) = sess.source_map().span_to_snippet(move_span) { err.span_suggestion( move_span, "consider borrowing to avoid moving into the for loop", @@ -1267,6 +1299,29 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { if return_span != borrow_span { err.span_label(borrow_span, note); + + let tcx = self.infcx.tcx; + let ty_params = ty::List::empty(); + + let return_ty = self.regioncx.universal_regions().unnormalized_output_ty; + let return_ty = tcx.erase_regions(return_ty); + + // to avoid panics + if !return_ty.has_infer_types() { + if let Some(iter_trait) = tcx.get_diagnostic_item(sym::Iterator) { + if tcx.type_implements_trait((iter_trait, return_ty, ty_params, self.param_env)) + { + if let Ok(snippet) = tcx.sess.source_map().span_to_snippet(return_span) { + err.span_suggestion_hidden( + return_span, + "use `.collect()` to allocate the iterator", + format!("{}{}", snippet, ".collect::>()"), + Applicability::MaybeIncorrect, + ); + } + } + } + } } Some(err) @@ -1638,7 +1693,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { if decl.can_be_made_mutable() { err.span_suggestion( decl.source_info.span, - "make this binding mutable", + "consider making this binding mutable", format!("mut {}", name), Applicability::MachineApplicable, ); @@ -1702,7 +1757,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { impl<'tcx> Visitor<'tcx> for FakeReadCauseFinder<'tcx> { fn visit_statement(&mut self, statement: &Statement<'tcx>, _: Location) { match statement { - Statement { kind: StatementKind::FakeRead(cause, box place), .. } + Statement { kind: StatementKind::FakeRead(box (cause, place)), .. } if *place == self.place => { self.cause = Some(*cause); diff --git a/compiler/rustc_mir/src/borrow_check/diagnostics/explain_borrow.rs b/compiler/rustc_mir/src/borrow_check/diagnostics/explain_borrow.rs index 06e3f4b91f61f..2a388b8a72bb0 100644 --- a/compiler/rustc_mir/src/borrow_check/diagnostics/explain_borrow.rs +++ b/compiler/rustc_mir/src/borrow_check/diagnostics/explain_borrow.rs @@ -515,7 +515,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { let block = &self.body.basic_blocks()[location.block]; let kind = if let Some(&Statement { - kind: StatementKind::FakeRead(FakeReadCause::ForLet, _), + kind: StatementKind::FakeRead(box (FakeReadCause::ForLet(_), _)), .. }) = block.statements.get(location.statement_index) { diff --git a/compiler/rustc_mir/src/borrow_check/diagnostics/mod.rs b/compiler/rustc_mir/src/borrow_check/diagnostics/mod.rs index 04ea3cbd8b66d..aa9f18d999628 100644 --- a/compiler/rustc_mir/src/borrow_check/diagnostics/mod.rs +++ b/compiler/rustc_mir/src/borrow_check/diagnostics/mod.rs @@ -7,8 +7,8 @@ use rustc_hir::def_id::DefId; use rustc_hir::lang_items::LangItemGroup; use rustc_hir::GeneratorKind; use rustc_middle::mir::{ - AggregateKind, Constant, Field, Local, LocalInfo, LocalKind, Location, Operand, Place, - PlaceRef, ProjectionElem, Rvalue, Statement, StatementKind, Terminator, TerminatorKind, + AggregateKind, Constant, FakeReadCause, Field, Local, LocalInfo, LocalKind, Location, Operand, + Place, PlaceRef, ProjectionElem, Rvalue, Statement, StatementKind, Terminator, TerminatorKind, }; use rustc_middle::ty::print::Print; use rustc_middle::ty::{self, DefIdTree, Instance, Ty, TyCtxt}; @@ -18,6 +18,7 @@ use rustc_span::{ Span, }; use rustc_target::abi::VariantIdx; +use std::iter; use super::borrow_set::BorrowData; use super::MirBorrowckCtxt; @@ -81,12 +82,12 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { let terminator = self.body[location.block].terminator(); debug!("add_moved_or_invoked_closure_note: terminator={:?}", terminator); if let TerminatorKind::Call { - func: Operand::Constant(box Constant { literal: ty::Const { ty: const_ty, .. }, .. }), + func: Operand::Constant(box Constant { literal, .. }), args, .. } = &terminator.kind { - if let ty::FnDef(id, _) = *const_ty.kind() { + if let ty::FnDef(id, _) = *literal.ty().kind() { debug!("add_moved_or_invoked_closure_note: id={:?}", id); if self.infcx.tcx.parent(id) == self.infcx.tcx.lang_items().fn_once_trait() { let closure = match args.first() { @@ -388,10 +389,14 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { // so it's safe to call `expect_local`. // // We know the field exists so it's safe to call operator[] and `unwrap` here. - let (&var_id, _) = - self.infcx.tcx.typeck(def_id.expect_local()).closure_captures[&def_id] - .get_index(field.index()) - .unwrap(); + let var_id = self + .infcx + .tcx + .typeck(def_id.expect_local()) + .closure_min_captures_flattened(def_id) + .nth(field.index()) + .unwrap() + .get_root_variable(); self.infcx.tcx.hir().name(var_id).to_string() } @@ -497,7 +502,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { // lifetimes without names with the value `'0`. match ty.kind() { ty::Ref( - ty::RegionKind::ReLateBound(_, ty::BoundRegion { kind: br }) + ty::RegionKind::ReLateBound(_, ty::BoundRegion { kind: br, .. }) | ty::RegionKind::RePlaceholder(ty::PlaceholderRegion { name: br, .. }), _, _, @@ -518,7 +523,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { let region = match ty.kind() { ty::Ref(region, _, _) => { match region { - ty::RegionKind::ReLateBound(_, ty::BoundRegion { kind: br }) + ty::RegionKind::ReLateBound(_, ty::BoundRegion { kind: br, .. }) | ty::RegionKind::RePlaceholder(ty::PlaceholderRegion { name: br, .. }) => { printer.region_highlight_mode.highlighting_bound_region(*br, counter) } @@ -568,7 +573,13 @@ pub(super) enum UseSpans<'tcx> { #[derive(Copy, Clone, PartialEq, Eq, Debug)] pub(super) enum FnSelfUseKind<'tcx> { /// A normal method call of the form `receiver.foo(a, b, c)` - Normal { self_arg: Ident, implicit_into_iter: bool }, + Normal { + self_arg: Ident, + implicit_into_iter: bool, + /// Whether the self type of the method call has an `.as_ref()` method. + /// Used for better diagnostics. + is_option_or_result: bool, + }, /// A call to `FnOnce::call_once`, desugared from `my_closure(a, b, c)` FnOnceCall, /// A call to an operator trait, desuraged from operator syntax (e.g. `a << b`) @@ -790,6 +801,24 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { } } + // StatementKind::FakeRead only contains a def_id if they are introduced as a result + // of pattern matching within a closure. + if let StatementKind::FakeRead(box (cause, ref place)) = stmt.kind { + match cause { + FakeReadCause::ForMatchedPlace(Some(closure_def_id)) + | FakeReadCause::ForLet(Some(closure_def_id)) => { + debug!("move_spans: def_id={:?} place={:?}", closure_def_id, place); + let places = &[Operand::Move(*place)]; + if let Some((args_span, generator_kind, var_span)) = + self.closure_span(closure_def_id, moved_place, places) + { + return ClosureUse { generator_kind, args_span, var_span }; + } + } + _ => {} + } + } + let normal_ret = if moved_place.projection.iter().any(|p| matches!(p, ProjectionElem::Downcast(..))) { PatUse(stmt.source_info.span) @@ -877,7 +906,17 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { fn_call_span.desugaring_kind(), Some(DesugaringKind::ForLoop(ForLoopLoc::IntoIter)) ); - FnSelfUseKind::Normal { self_arg, implicit_into_iter } + let parent_self_ty = parent + .filter(|did| tcx.def_kind(*did) == rustc_hir::def::DefKind::Impl) + .and_then(|did| match tcx.type_of(did).kind() { + ty::Adt(def, ..) => Some(def.did), + _ => None, + }); + let is_option_or_result = parent_self_ty.map_or(false, |def_id| { + tcx.is_diagnostic_item(sym::option_type, def_id) + || tcx.is_diagnostic_item(sym::result_type, def_id) + }); + FnSelfUseKind::Normal { self_arg, implicit_into_iter, is_option_or_result } }); return FnSelfUse { @@ -966,12 +1005,13 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { let expr = &self.infcx.tcx.hir().expect_expr(hir_id).kind; debug!("closure_span: hir_id={:?} expr={:?}", hir_id, expr); if let hir::ExprKind::Closure(.., body_id, args_span, _) = expr { - for (upvar_hir_id, place) in - self.infcx.tcx.typeck(def_id.expect_local()).closure_captures[&def_id] - .keys() - .zip(places) - { - let span = self.infcx.tcx.upvars_mentioned(local_did)?[upvar_hir_id].span; + for (captured_place, place) in iter::zip( + self.infcx.tcx.typeck(def_id.expect_local()).closure_min_captures_flattened(def_id), + places, + ) { + let upvar_hir_id = captured_place.get_root_variable(); + //FIXME(project-rfc-2229#8): Use better span from captured_place + let span = self.infcx.tcx.upvars_mentioned(local_did)?[&upvar_hir_id].span; match place { Operand::Copy(place) | Operand::Move(place) if target_place == place.as_ref() => @@ -979,10 +1019,6 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { debug!("closure_span: found captured local {:?}", place); let body = self.infcx.tcx.hir().body(*body_id); let generator_kind = body.generator_kind(); - let upvar_id = ty::UpvarId { - var_path: ty::UpvarPath { hir_id: *upvar_hir_id }, - closure_expr_id: local_did, - }; // If we have a more specific span available, point to that. // We do this even though this span might be part of a borrow error @@ -990,11 +1026,11 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { // to a span that shows why the upvar is used in the closure, // so a move-related span is as good as any (and potentially better, // if the overall error is due to a move of the upvar). - let usage_span = - match self.infcx.tcx.typeck(local_did).upvar_capture(upvar_id) { - ty::UpvarCapture::ByValue(Some(span)) => span, - _ => span, - }; + + let usage_span = match captured_place.info.capture_kind { + ty::UpvarCapture::ByValue(Some(span)) => span, + _ => span, + }; return Some((*args_span, generator_kind, usage_span)); } _ => {} diff --git a/compiler/rustc_mir/src/borrow_check/diagnostics/mutability_errors.rs b/compiler/rustc_mir/src/borrow_check/diagnostics/mutability_errors.rs index 2f40a90fb5516..28f6508cab2da 100644 --- a/compiler/rustc_mir/src/borrow_check/diagnostics/mutability_errors.rs +++ b/compiler/rustc_mir/src/borrow_check/diagnostics/mutability_errors.rs @@ -510,24 +510,54 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { the_place_err: PlaceRef<'tcx>, err: &mut DiagnosticBuilder<'_>, ) { - let id = id.expect_local(); - let tables = tcx.typeck(id); - let hir_id = tcx.hir().local_def_id_to_hir_id(id); - if let Some((span, place)) = tables.closure_kind_origins().get(hir_id) { - let reason = if let PlaceBase::Upvar(upvar_id) = place.base { - let upvar = ty::place_to_string_for_capture(tcx, place); - match tables.upvar_capture(upvar_id) { - ty::UpvarCapture::ByRef(ty::UpvarBorrow { - kind: ty::BorrowKind::MutBorrow | ty::BorrowKind::UniqueImmBorrow, - .. - }) => { - format!("mutable borrow of `{}`", upvar) - } - ty::UpvarCapture::ByValue(_) => { - format!("possible mutation of `{}`", upvar) + let closure_local_def_id = id.expect_local(); + let tables = tcx.typeck(closure_local_def_id); + let closure_hir_id = tcx.hir().local_def_id_to_hir_id(closure_local_def_id); + if let Some((span, closure_kind_origin)) = + &tables.closure_kind_origins().get(closure_hir_id) + { + let reason = if let PlaceBase::Upvar(upvar_id) = closure_kind_origin.base { + let upvar = ty::place_to_string_for_capture(tcx, closure_kind_origin); + let root_hir_id = upvar_id.var_path.hir_id; + // we have a origin for this closure kind starting at this root variable so it's safe to unwrap here + let captured_places = tables.closure_min_captures[id].get(&root_hir_id).unwrap(); + + let origin_projection = closure_kind_origin + .projections + .iter() + .map(|proj| proj.kind) + .collect::>(); + let mut capture_reason = String::new(); + for captured_place in captured_places { + let captured_place_kinds = captured_place + .place + .projections + .iter() + .map(|proj| proj.kind) + .collect::>(); + if rustc_middle::ty::is_ancestor_or_same_capture( + &captured_place_kinds, + &origin_projection, + ) { + match captured_place.info.capture_kind { + ty::UpvarCapture::ByRef(ty::UpvarBorrow { + kind: ty::BorrowKind::MutBorrow | ty::BorrowKind::UniqueImmBorrow, + .. + }) => { + capture_reason = format!("mutable borrow of `{}`", upvar); + } + ty::UpvarCapture::ByValue(_) => { + capture_reason = format!("possible mutation of `{}`", upvar); + } + _ => bug!("upvar `{}` borrowed, but not mutably", upvar), + } + break; } - val => bug!("upvar `{}` borrowed, but not mutably: {:?}", upvar, val), } + if capture_reason.is_empty() { + bug!("upvar `{}` borrowed, but cannot find reason", upvar); + } + capture_reason } else { bug!("not an upvar") }; diff --git a/compiler/rustc_mir/src/borrow_check/diagnostics/outlives_suggestion.rs b/compiler/rustc_mir/src/borrow_check/diagnostics/outlives_suggestion.rs index 7505e6e2dd11e..7dc3434bf3338 100644 --- a/compiler/rustc_mir/src/borrow_check/diagnostics/outlives_suggestion.rs +++ b/compiler/rustc_mir/src/borrow_check/diagnostics/outlives_suggestion.rs @@ -1,4 +1,4 @@ -//! Contains utilities for generating suggestions for borrowck errors related to unsatisified +//! Contains utilities for generating suggestions for borrowck errors related to unsatisfied //! outlives constraints. use std::collections::BTreeMap; @@ -157,7 +157,7 @@ impl OutlivesSuggestionBuilder { debug!("Collected {:?}: {:?}", fr, outlived_fr); // Add to set of constraints for final help note. - self.constraints_to_add.entry(fr).or_insert(Vec::new()).push(outlived_fr); + self.constraints_to_add.entry(fr).or_default().push(outlived_fr); } /// Emit an intermediate note on the given `Diagnostic` if the involved regions are diff --git a/compiler/rustc_mir/src/borrow_check/diagnostics/region_name.rs b/compiler/rustc_mir/src/borrow_check/diagnostics/region_name.rs index 03738f1b40a1c..1f168c612f167 100644 --- a/compiler/rustc_mir/src/borrow_check/diagnostics/region_name.rs +++ b/compiler/rustc_mir/src/borrow_check/diagnostics/region_name.rs @@ -1,4 +1,5 @@ use std::fmt::{self, Display}; +use std::iter; use rustc_errors::DiagnosticBuilder; use rustc_hir as hir; @@ -536,7 +537,8 @@ impl<'tcx> MirBorrowckCtxt<'_, 'tcx> { // just worry about trying to match up the rustc type // with the HIR types: (ty::Tuple(elem_tys), hir::TyKind::Tup(elem_hir_tys)) => { - search_stack.extend(elem_tys.iter().map(|k| k.expect_ty()).zip(*elem_hir_tys)); + search_stack + .extend(iter::zip(elem_tys.iter().map(|k| k.expect_ty()), *elem_hir_tys)); } (ty::Slice(elem_ty), hir::TyKind::Slice(elem_hir_ty)) @@ -611,7 +613,7 @@ impl<'tcx> MirBorrowckCtxt<'_, 'tcx> { args: &'hir hir::GenericArgs<'hir>, search_stack: &mut Vec<(Ty<'tcx>, &'hir hir::Ty<'hir>)>, ) -> Option<&'hir hir::Lifetime> { - for (kind, hir_arg) in substs.iter().zip(args.args) { + for (kind, hir_arg) in iter::zip(substs, args.args) { match (kind.unpack(), hir_arg) { (GenericArgKind::Lifetime(r), hir::GenericArg::Lifetime(lt)) => { if r.to_region_vid() == needle_fr { diff --git a/compiler/rustc_mir/src/borrow_check/invalidation.rs b/compiler/rustc_mir/src/borrow_check/invalidation.rs index 8c05e6fd5d0e4..e621bafb671bb 100644 --- a/compiler/rustc_mir/src/borrow_check/invalidation.rs +++ b/compiler/rustc_mir/src/borrow_check/invalidation.rs @@ -5,6 +5,7 @@ use rustc_middle::mir::{BorrowKind, Mutability, Operand}; use rustc_middle::mir::{InlineAsmOperand, Terminator, TerminatorKind}; use rustc_middle::mir::{Statement, StatementKind}; use rustc_middle::ty::TyCtxt; +use std::iter; use crate::dataflow::indexes::BorrowIndex; @@ -62,14 +63,14 @@ impl<'cx, 'tcx> Visitor<'tcx> for InvalidationGenerator<'cx, 'tcx> { self.mutate_place(location, *lhs, Shallow(None), JustWrite); } - StatementKind::FakeRead(_, _) => { + StatementKind::FakeRead(box (_, _)) => { // Only relevant for initialized/liveness/safety checks. } StatementKind::SetDiscriminant { place, variant_index: _ } => { self.mutate_place(location, **place, Shallow(None), JustWrite); } StatementKind::LlvmInlineAsm(asm) => { - for (o, output) in asm.asm.outputs.iter().zip(asm.outputs.iter()) { + for (o, output) in iter::zip(&asm.asm.outputs, &*asm.outputs) { if o.is_indirect { // FIXME(eddyb) indirect inline asm outputs should // be encoded through MIR place derefs instead. @@ -92,6 +93,15 @@ impl<'cx, 'tcx> Visitor<'tcx> for InvalidationGenerator<'cx, 'tcx> { self.consume_operand(location, input); } } + StatementKind::CopyNonOverlapping(box rustc_middle::mir::CopyNonOverlapping { + ref src, + ref dst, + ref count, + }) => { + self.consume_operand(location, src); + self.consume_operand(location, dst); + self.consume_operand(location, count); + } StatementKind::Nop | StatementKind::Coverage(..) | StatementKind::AscribeUserType(..) @@ -165,7 +175,7 @@ impl<'cx, 'tcx> Visitor<'tcx> for InvalidationGenerator<'cx, 'tcx> { self.consume_operand(location, value); // Invalidate all borrows of local places - let borrow_set = self.borrow_set.clone(); + let borrow_set = self.borrow_set; let resume = self.location_table.start_index(resume.start_location()); for (i, data) in borrow_set.iter_enumerated() { if borrow_of_local_data(data.borrowed_place) { @@ -177,7 +187,7 @@ impl<'cx, 'tcx> Visitor<'tcx> for InvalidationGenerator<'cx, 'tcx> { } TerminatorKind::Resume | TerminatorKind::Return | TerminatorKind::GeneratorDrop => { // Invalidate all borrows of local places - let borrow_set = self.borrow_set.clone(); + let borrow_set = self.borrow_set; let start = self.location_table.start_index(location); for (i, data) in borrow_set.iter_enumerated() { if borrow_of_local_data(data.borrowed_place) { @@ -194,8 +204,7 @@ impl<'cx, 'tcx> Visitor<'tcx> for InvalidationGenerator<'cx, 'tcx> { } => { for op in operands { match *op { - InlineAsmOperand::In { reg: _, ref value } - | InlineAsmOperand::Const { ref value } => { + InlineAsmOperand::In { reg: _, ref value } => { self.consume_operand(location, value); } InlineAsmOperand::Out { reg: _, late: _, place, .. } => { @@ -209,7 +218,8 @@ impl<'cx, 'tcx> Visitor<'tcx> for InvalidationGenerator<'cx, 'tcx> { self.mutate_place(location, out_place, Shallow(None), JustWrite); } } - InlineAsmOperand::SymFn { value: _ } + InlineAsmOperand::Const { value: _ } + | InlineAsmOperand::SymFn { value: _ } | InlineAsmOperand::SymStatic { def_id: _ } => {} } } @@ -326,8 +336,8 @@ impl<'cx, 'tcx> InvalidationGenerator<'cx, 'tcx> { ); } - Rvalue::BinaryOp(_bin_op, ref operand1, ref operand2) - | Rvalue::CheckedBinaryOp(_bin_op, ref operand1, ref operand2) => { + Rvalue::BinaryOp(_bin_op, box (ref operand1, ref operand2)) + | Rvalue::CheckedBinaryOp(_bin_op, box (ref operand1, ref operand2)) => { self.consume_operand(location, operand1); self.consume_operand(location, operand2); } @@ -369,7 +379,7 @@ impl<'cx, 'tcx> InvalidationGenerator<'cx, 'tcx> { ); let tcx = self.tcx; let body = self.body; - let borrow_set = self.borrow_set.clone(); + let borrow_set = self.borrow_set; let indices = self.borrow_set.indices(); each_borrow_involving_path( self, @@ -377,7 +387,7 @@ impl<'cx, 'tcx> InvalidationGenerator<'cx, 'tcx> { body, location, (sd, place), - &borrow_set.clone(), + borrow_set, indices, |this, borrow_index, borrow| { match (rw, borrow.kind) { diff --git a/compiler/rustc_mir/src/borrow_check/mod.rs b/compiler/rustc_mir/src/borrow_check/mod.rs index 375d464917118..2d1d83b1655aa 100644 --- a/compiler/rustc_mir/src/borrow_check/mod.rs +++ b/compiler/rustc_mir/src/borrow_check/mod.rs @@ -25,6 +25,7 @@ use either::Either; use smallvec::SmallVec; use std::cell::RefCell; use std::collections::BTreeMap; +use std::iter; use std::mem; use std::rc::Rc; @@ -573,7 +574,7 @@ impl<'cx, 'tcx> dataflow::ResultsVisitor<'cx, 'tcx> for MirBorrowckCtxt<'cx, 'tc self.mutate_place(location, (*lhs, span), Shallow(None), JustWrite, flow_state); } - StatementKind::FakeRead(_, box ref place) => { + StatementKind::FakeRead(box (_, ref place)) => { // Read for match doesn't access any memory and is used to // assert that a place is safe and live. So we don't have to // do any checks here. @@ -595,7 +596,7 @@ impl<'cx, 'tcx> dataflow::ResultsVisitor<'cx, 'tcx> for MirBorrowckCtxt<'cx, 'tc self.mutate_place(location, (**place, span), Shallow(None), JustWrite, flow_state); } StatementKind::LlvmInlineAsm(ref asm) => { - for (o, output) in asm.asm.outputs.iter().zip(asm.outputs.iter()) { + for (o, output) in iter::zip(&asm.asm.outputs, &*asm.outputs) { if o.is_indirect { // FIXME(eddyb) indirect inline asm outputs should // be encoded through MIR place derefs instead. @@ -626,6 +627,15 @@ impl<'cx, 'tcx> dataflow::ResultsVisitor<'cx, 'tcx> for MirBorrowckCtxt<'cx, 'tc self.consume_operand(location, (input, span), flow_state); } } + + StatementKind::CopyNonOverlapping(box rustc_middle::mir::CopyNonOverlapping { + .. + }) => { + span_bug!( + span, + "Unexpected CopyNonOverlapping, should only appear after lower_intrinsics", + ) + } StatementKind::Nop | StatementKind::Coverage(..) | StatementKind::AscribeUserType(..) @@ -724,8 +734,7 @@ impl<'cx, 'tcx> dataflow::ResultsVisitor<'cx, 'tcx> for MirBorrowckCtxt<'cx, 'tc } => { for op in operands { match *op { - InlineAsmOperand::In { reg: _, ref value } - | InlineAsmOperand::Const { ref value } => { + InlineAsmOperand::In { reg: _, ref value } => { self.consume_operand(loc, (value, span), flow_state); } InlineAsmOperand::Out { reg: _, late: _, place, .. } => { @@ -751,7 +760,8 @@ impl<'cx, 'tcx> dataflow::ResultsVisitor<'cx, 'tcx> for MirBorrowckCtxt<'cx, 'tc ); } } - InlineAsmOperand::SymFn { value: _ } + InlineAsmOperand::Const { value: _ } + | InlineAsmOperand::SymFn { value: _ } | InlineAsmOperand::SymStatic { def_id: _ } => {} } } @@ -1316,8 +1326,8 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { ); } - Rvalue::BinaryOp(_bin_op, ref operand1, ref operand2) - | Rvalue::CheckedBinaryOp(_bin_op, ref operand1, ref operand2) => { + Rvalue::BinaryOp(_bin_op, box (ref operand1, ref operand2)) + | Rvalue::CheckedBinaryOp(_bin_op, box (ref operand1, ref operand2)) => { self.consume_operand(location, (operand1, span), flow_state); self.consume_operand(location, (operand2, span), flow_state); } diff --git a/compiler/rustc_mir/src/borrow_check/places_conflict.rs b/compiler/rustc_mir/src/borrow_check/places_conflict.rs index 02c7b7dc200c3..3654b51949e70 100644 --- a/compiler/rustc_mir/src/borrow_check/places_conflict.rs +++ b/compiler/rustc_mir/src/borrow_check/places_conflict.rs @@ -5,6 +5,7 @@ use rustc_hir as hir; use rustc_middle::mir::{Body, BorrowKind, Local, Place, PlaceElem, PlaceRef, ProjectionElem}; use rustc_middle::ty::{self, TyCtxt}; use std::cmp::max; +use std::iter; /// When checking if a place conflicts with another place, this enum is used to influence decisions /// where a place might be equal or disjoint with another place, such as if `a[i] == a[j]`. @@ -139,7 +140,7 @@ fn place_components_conflict<'tcx>( // loop invariant: borrow_c is always either equal to access_c or disjoint from it. for (i, (borrow_c, &access_c)) in - borrow_place.projection.iter().zip(access_place.projection.iter()).enumerate() + iter::zip(borrow_place.projection, access_place.projection).enumerate() { debug!("borrow_conflicts_with_place: borrow_c = {:?}", borrow_c); let borrow_proj_base = &borrow_place.projection[..i]; diff --git a/compiler/rustc_mir/src/borrow_check/region_infer/graphviz.rs b/compiler/rustc_mir/src/borrow_check/region_infer/graphviz.rs index a272e922a504e..7156612f4730f 100644 --- a/compiler/rustc_mir/src/borrow_check/region_infer/graphviz.rs +++ b/compiler/rustc_mir/src/borrow_check/region_infer/graphviz.rs @@ -1,5 +1,5 @@ //! This module provides linkage between RegionInferenceContext and -//! librustc_graphviz traits, specialized to attaching borrowck analysis +//! `rustc_graphviz` traits, specialized to attaching borrowck analysis //! data to rendered labels. use std::borrow::Cow; diff --git a/compiler/rustc_mir/src/borrow_check/region_infer/opaque_types.rs b/compiler/rustc_mir/src/borrow_check/region_infer/opaque_types.rs index f7c902355cb50..0d1d255104272 100644 --- a/compiler/rustc_mir/src/borrow_check/region_infer/opaque_types.rs +++ b/compiler/rustc_mir/src/borrow_check/region_infer/opaque_types.rs @@ -47,6 +47,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { /// Calling `universal_upper_bound` for such a region gives `fr_fn_body`, /// which has no `external_name` in which case we use `'empty` as the /// region to pass to `infer_opaque_definition_from_instantiation`. + #[instrument(skip(self, infcx))] pub(in crate::borrow_check) fn infer_opaque_types( &self, infcx: &InferCtxt<'_, 'tcx>, @@ -56,10 +57,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { opaque_ty_decls .into_iter() .map(|(opaque_def_id, ty::ResolvedOpaqueTy { concrete_type, substs })| { - debug!( - "infer_opaque_types(concrete_type = {:?}, substs = {:?})", - concrete_type, substs - ); + debug!(?concrete_type, ?substs); let mut subst_regions = vec![self.universal_regions.fr_static]; let universal_substs = @@ -110,10 +108,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { } }); - debug!( - "infer_opaque_types(universal_concrete_type = {:?}, universal_substs = {:?})", - universal_concrete_type, universal_substs - ); + debug!(?universal_concrete_type, ?universal_substs); let remapped_type = infcx.infer_opaque_definition_from_instantiation( opaque_def_id, diff --git a/compiler/rustc_mir/src/borrow_check/type_check/input_output.rs b/compiler/rustc_mir/src/borrow_check/type_check/input_output.rs index 77d9136622458..1bb447d105781 100644 --- a/compiler/rustc_mir/src/borrow_check/type_check/input_output.rs +++ b/compiler/rustc_mir/src/borrow_check/type_check/input_output.rs @@ -70,6 +70,12 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { // Equate expected input tys with those in the MIR. for (argument_index, &normalized_input_ty) in normalized_input_tys.iter().enumerate() { + if argument_index + 1 >= body.local_decls.len() { + self.tcx() + .sess + .delay_span_bug(body.span, "found more normalized_input_ty than local_decls"); + break; + } // In MIR, argument N is stored in local N+1. let local = Local::new(argument_index + 1); diff --git a/compiler/rustc_mir/src/borrow_check/type_check/mod.rs b/compiler/rustc_mir/src/borrow_check/type_check/mod.rs index fa5b342c6e250..3248554e20427 100644 --- a/compiler/rustc_mir/src/borrow_check/type_check/mod.rs +++ b/compiler/rustc_mir/src/borrow_check/type_check/mod.rs @@ -282,7 +282,7 @@ impl<'a, 'b, 'tcx> Visitor<'tcx> for TypeVerifier<'a, 'b, 'tcx> { fn visit_constant(&mut self, constant: &Constant<'tcx>, location: Location) { self.super_constant(constant, location); - let ty = self.sanitize_type(constant, constant.literal.ty); + let ty = self.sanitize_type(constant, constant.literal.ty()); self.cx.infcx.tcx.for_each_free_region(&ty, |live_region| { let live_region_vid = @@ -296,7 +296,7 @@ impl<'a, 'b, 'tcx> Visitor<'tcx> for TypeVerifier<'a, 'b, 'tcx> { if let Some(annotation_index) = constant.user_ty { if let Err(terr) = self.cx.relate_type_and_user_type( - constant.literal.ty, + constant.literal.ty(), ty::Variance::Invariant, &UserTypeProjection { base: annotation_index, projs: vec![] }, location.to_locations(), @@ -308,13 +308,20 @@ impl<'a, 'b, 'tcx> Visitor<'tcx> for TypeVerifier<'a, 'b, 'tcx> { constant, "bad constant user type {:?} vs {:?}: {:?}", annotation, - constant.literal.ty, + constant.literal.ty(), terr, ); } } else { let tcx = self.tcx(); - if let ty::ConstKind::Unevaluated(def, substs, promoted) = constant.literal.val { + let maybe_uneval = match constant.literal { + ConstantKind::Ty(ct) => match ct.val { + ty::ConstKind::Unevaluated(uv) => Some(uv), + _ => None, + }, + _ => None, + }; + if let Some(ty::Unevaluated { def, substs, promoted }) = maybe_uneval { if let Some(promoted) = promoted { let check_err = |verifier: &mut TypeVerifier<'a, 'b, 'tcx>, promoted: &Body<'tcx>, @@ -349,7 +356,7 @@ impl<'a, 'b, 'tcx> Visitor<'tcx> for TypeVerifier<'a, 'b, 'tcx> { location.to_locations(), ConstraintCategory::Boring, self.cx.param_env.and(type_op::ascribe_user_type::AscribeUserType::new( - constant.literal.ty, + constant.literal.ty(), def.did, UserSubsts { substs, user_self_ty: None }, )), @@ -367,7 +374,7 @@ impl<'a, 'b, 'tcx> Visitor<'tcx> for TypeVerifier<'a, 'b, 'tcx> { let unnormalized_ty = tcx.type_of(static_def_id); let locations = location.to_locations(); let normalized_ty = self.cx.normalize(unnormalized_ty, locations); - let literal_ty = constant.literal.ty.builtin_deref(true).unwrap().ty; + let literal_ty = constant.literal.ty().builtin_deref(true).unwrap().ty; if let Err(terr) = self.cx.eq_types( normalized_ty, @@ -379,7 +386,7 @@ impl<'a, 'b, 'tcx> Visitor<'tcx> for TypeVerifier<'a, 'b, 'tcx> { } } - if let ty::FnDef(def_id, substs) = *constant.literal.ty.kind() { + if let ty::FnDef(def_id, substs) = *constant.literal.ty().kind() { let instantiated_predicates = tcx.predicates_of(def_id).instantiate(tcx, substs); self.cx.normalize_and_prove_instantiated_predicates( instantiated_predicates, @@ -1520,6 +1527,12 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { ); } } + StatementKind::CopyNonOverlapping(box rustc_middle::mir::CopyNonOverlapping { + .. + }) => span_bug!( + stmt.source_info.span, + "Unexpected StatementKind::CopyNonOverlapping, should only appear after lowering_intrinsics", + ), StatementKind::FakeRead(..) | StatementKind::StorageLive(..) | StatementKind::StorageDead(..) @@ -1757,7 +1770,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { if args.len() < sig.inputs().len() || (args.len() > sig.inputs().len() && !sig.c_variadic) { span_mirbug!(self, term, "call to {:?} with wrong # of args", sig); } - for (n, (fn_arg, op_arg)) in sig.inputs().iter().zip(args).enumerate() { + for (n, (fn_arg, op_arg)) in iter::zip(sig.inputs(), args).enumerate() { let op_arg_ty = op_arg.ty(body, self.tcx()); let op_arg_ty = self.normalize(op_arg_ty, term_location); let category = if from_hir_call { @@ -2015,7 +2028,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { traits::ObligationCauseCode::RepeatVec(is_const_fn), ), self.param_env, - ty::Binder::bind(ty::TraitRef::new( + ty::Binder::dummy(ty::TraitRef::new( self.tcx().require_lang_item( LangItem::Copy, Some(self.last_span), @@ -2299,8 +2312,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { Rvalue::BinaryOp( BinOp::Eq | BinOp::Ne | BinOp::Lt | BinOp::Le | BinOp::Gt | BinOp::Ge, - left, - right, + box (left, right), ) => { let ty_left = left.ty(body, tcx); match ty_left.kind() { diff --git a/compiler/rustc_mir/src/borrow_check/universal_regions.rs b/compiler/rustc_mir/src/borrow_check/universal_regions.rs index 4b1acc1cd105e..c2ac1e289ce4e 100644 --- a/compiler/rustc_mir/src/borrow_check/universal_regions.rs +++ b/compiler/rustc_mir/src/borrow_check/universal_regions.rs @@ -580,7 +580,7 @@ impl<'cx, 'tcx> UniversalRegionsBuilder<'cx, 'tcx> { let global_mapping = iter::once((tcx.lifetimes.re_static, fr_static)); let subst_mapping = - identity_substs.regions().zip(fr_substs.regions().map(|r| r.to_region_vid())); + iter::zip(identity_substs.regions(), fr_substs.regions().map(|r| r.to_region_vid())); UniversalRegionIndices { indices: global_mapping.chain(subst_mapping).collect() } } @@ -589,31 +589,45 @@ impl<'cx, 'tcx> UniversalRegionsBuilder<'cx, 'tcx> { &self, indices: &UniversalRegionIndices<'tcx>, defining_ty: DefiningTy<'tcx>, - ) -> ty::Binder<&'tcx ty::List>> { + ) -> ty::Binder<'tcx, &'tcx ty::List>> { let tcx = self.infcx.tcx; match defining_ty { DefiningTy::Closure(def_id, substs) => { assert_eq!(self.mir_def.did.to_def_id(), def_id); let closure_sig = substs.as_closure().sig(); let inputs_and_output = closure_sig.inputs_and_output(); - let closure_ty = tcx.closure_env_ty(def_id, substs).unwrap(); - ty::Binder::fuse(closure_ty, inputs_and_output, |closure_ty, inputs_and_output| { - // The "inputs" of the closure in the - // signature appear as a tuple. The MIR side - // flattens this tuple. - let (&output, tuplized_inputs) = inputs_and_output.split_last().unwrap(); - assert_eq!(tuplized_inputs.len(), 1, "multiple closure inputs"); - let inputs = match tuplized_inputs[0].kind() { - ty::Tuple(inputs) => inputs, - _ => bug!("closure inputs not a tuple: {:?}", tuplized_inputs[0]), - }; + let bound_vars = tcx.mk_bound_variable_kinds( + inputs_and_output + .bound_vars() + .iter() + .chain(iter::once(ty::BoundVariableKind::Region(ty::BrEnv))), + ); + let br = ty::BoundRegion { + var: ty::BoundVar::from_usize(bound_vars.len() - 1), + kind: ty::BrEnv, + }; + let env_region = ty::ReLateBound(ty::INNERMOST, br); + let closure_ty = tcx.closure_env_ty(def_id, substs, env_region).unwrap(); + + // The "inputs" of the closure in the + // signature appear as a tuple. The MIR side + // flattens this tuple. + let (&output, tuplized_inputs) = + inputs_and_output.skip_binder().split_last().unwrap(); + assert_eq!(tuplized_inputs.len(), 1, "multiple closure inputs"); + let inputs = match tuplized_inputs[0].kind() { + ty::Tuple(inputs) => inputs, + _ => bug!("closure inputs not a tuple: {:?}", tuplized_inputs[0]), + }; + ty::Binder::bind_with_vars( tcx.mk_type_list( iter::once(closure_ty) .chain(inputs.iter().map(|k| k.expect_ty())) .chain(iter::once(output)), - ) - }) + ), + bound_vars, + ) } DefiningTy::Generator(def_id, substs, movability) => { @@ -657,7 +671,7 @@ trait InferCtxtExt<'tcx> { &self, origin: NllRegionVariableOrigin, all_outlive_scope: LocalDefId, - value: ty::Binder, + value: ty::Binder<'tcx, T>, indices: &mut UniversalRegionIndices<'tcx>, ) -> T where @@ -686,7 +700,7 @@ impl<'cx, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'cx, 'tcx> { &self, origin: NllRegionVariableOrigin, all_outlive_scope: LocalDefId, - value: ty::Binder, + value: ty::Binder<'tcx, T>, indices: &mut UniversalRegionIndices<'tcx>, ) -> T where diff --git a/compiler/rustc_mir/src/const_eval/eval_queries.rs b/compiler/rustc_mir/src/const_eval/eval_queries.rs index fa234ff5feb64..d51adc8864de5 100644 --- a/compiler/rustc_mir/src/const_eval/eval_queries.rs +++ b/compiler/rustc_mir/src/const_eval/eval_queries.rs @@ -5,6 +5,7 @@ use crate::interpret::{ Immediate, InternKind, InterpCx, InterpResult, MPlaceTy, MemoryKind, OpTy, RefTracking, Scalar, ScalarMaybeUninit, StackPopCleanup, }; +use crate::util::pretty::display_allocation; use rustc_errors::ErrorReported; use rustc_hir::def::DefKind; @@ -360,6 +361,15 @@ pub fn eval_to_allocation_raw_provider<'tcx>( "it is undefined behavior to use this value", |mut diag| { diag.note(note_on_undefined_behavior_error()); + diag.note(&format!( + "the raw bytes of the constant ({}", + display_allocation( + *ecx.tcx, + ecx.tcx + .global_alloc(mplace.ptr.assert_ptr().alloc_id) + .unwrap_memory() + ) + )); diag.emit(); }, )) diff --git a/compiler/rustc_mir/src/const_eval/machine.rs b/compiler/rustc_mir/src/const_eval/machine.rs index 61785a52729d5..8e9148f5b6643 100644 --- a/compiler/rustc_mir/src/const_eval/machine.rs +++ b/compiler/rustc_mir/src/const_eval/machine.rs @@ -53,7 +53,7 @@ impl<'mir, 'tcx> InterpCx<'mir, 'tcx, CompileTimeInterpreter<'mir, 'tcx>> { /// Extra machine state for CTFE, and the Machine instance pub struct CompileTimeInterpreter<'mir, 'tcx> { /// For now, the number of terminators that can be evaluated before we throw a resource - /// exhuastion error. + /// exhaustion error. /// /// Setting this to `0` disables the limit and allows the interpreter to run forever. pub steps_remaining: usize, diff --git a/compiler/rustc_mir/src/const_eval/mod.rs b/compiler/rustc_mir/src/const_eval/mod.rs index a4e1cd2faa3c0..3f14efc920f0e 100644 --- a/compiler/rustc_mir/src/const_eval/mod.rs +++ b/compiler/rustc_mir/src/const_eval/mod.rs @@ -3,12 +3,15 @@ use std::convert::TryFrom; use rustc_hir::Mutability; -use rustc_middle::mir; use rustc_middle::ty::{self, TyCtxt}; +use rustc_middle::{ + mir::{self, interpret::ConstAlloc}, + ty::ScalarInt, +}; use rustc_span::{source_map::DUMMY_SP, symbol::Symbol}; use crate::interpret::{ - intern_const_alloc_recursive, ConstValue, InternKind, InterpCx, MemPlaceMeta, Scalar, + intern_const_alloc_recursive, ConstValue, InternKind, InterpCx, MPlaceTy, MemPlaceMeta, Scalar, }; mod error; @@ -35,6 +38,100 @@ pub(crate) fn const_caller_location( ConstValue::Scalar(loc_place.ptr) } +/// Convert an evaluated constant to a type level constant +pub(crate) fn const_to_valtree<'tcx>( + tcx: TyCtxt<'tcx>, + param_env: ty::ParamEnv<'tcx>, + raw: ConstAlloc<'tcx>, +) -> Option> { + let ecx = mk_eval_cx( + tcx, DUMMY_SP, param_env, + // It is absolutely crucial for soundness that + // we do not read from static items or other mutable memory. + false, + ); + let place = ecx.raw_const_to_mplace(raw).unwrap(); + const_to_valtree_inner(&ecx, &place) +} + +fn const_to_valtree_inner<'tcx>( + ecx: &CompileTimeEvalContext<'tcx, 'tcx>, + place: &MPlaceTy<'tcx>, +) -> Option> { + let branches = |n, variant| { + let place = match variant { + Some(variant) => ecx.mplace_downcast(&place, variant).unwrap(), + None => *place, + }; + let variant = + variant.map(|variant| Some(ty::ValTree::Leaf(ScalarInt::from(variant.as_u32())))); + let fields = (0..n).map(|i| { + let field = ecx.mplace_field(&place, i).unwrap(); + const_to_valtree_inner(ecx, &field) + }); + // For enums, we preped their variant index before the variant's fields so we can figure out + // the variant again when just seeing a valtree. + let branches = variant.into_iter().chain(fields); + Some(ty::ValTree::Branch( + ecx.tcx.arena.alloc_from_iter(branches.collect::>>()?), + )) + }; + match place.layout.ty.kind() { + ty::FnDef(..) => Some(ty::ValTree::zst()), + ty::Bool | ty::Int(_) | ty::Uint(_) | ty::Float(_) | ty::Char => { + let val = ecx.read_immediate(&place.into()).unwrap(); + let val = val.to_scalar().unwrap(); + Some(ty::ValTree::Leaf(val.assert_int())) + } + + // Raw pointers are not allowed in type level constants, as we cannot properly test them for + // equality at compile-time (see `ptr_guaranteed_eq`/`_ne`). + // Technically we could allow function pointers (represented as `ty::Instance`), but this is not guaranteed to + // agree with runtime equality tests. + ty::FnPtr(_) | ty::RawPtr(_) => None, + ty::Ref(..) => unimplemented!("need to use deref_const"), + + // Trait objects are not allowed in type level constants, as we have no concept for + // resolving their backing type, even if we can do that at const eval time. We may + // hypothetically be able to allow `dyn StructuralEq` trait objects in the future, + // but it is unclear if this is useful. + ty::Dynamic(..) => None, + + ty::Slice(_) | ty::Str => { + unimplemented!("need to find the backing data of the slice/str and recurse on that") + } + ty::Tuple(substs) => branches(substs.len(), None), + ty::Array(_, len) => branches(usize::try_from(len.eval_usize(ecx.tcx.tcx, ecx.param_env)).unwrap(), None), + + ty::Adt(def, _) => { + if def.variants.is_empty() { + bug!("uninhabited types should have errored and never gotten converted to valtree") + } + + let variant = ecx.read_discriminant(&place.into()).unwrap().1; + + branches(def.variants[variant].fields.len(), def.is_enum().then_some(variant)) + } + + ty::Never + | ty::Error(_) + | ty::Foreign(..) + | ty::Infer(ty::FreshIntTy(_)) + | ty::Infer(ty::FreshFloatTy(_)) + | ty::Projection(..) + | ty::Param(_) + | ty::Bound(..) + | ty::Placeholder(..) + // FIXME(oli-obk): we could look behind opaque types + | ty::Opaque(..) + | ty::Infer(_) + // FIXME(oli-obk): we can probably encode closures just like structs + | ty::Closure(..) + | ty::Generator(..) + | ty::GeneratorWitness(..) => None, + } +} + /// This function uses `unwrap` copiously, because an already validated constant /// must have valid fields and can thus never fail outside of compiler bugs. However, it is /// invoked from the pretty printer, where it can receive enums with no variants and e.g. diff --git a/compiler/rustc_mir/src/dataflow/framework/cursor.rs b/compiler/rustc_mir/src/dataflow/framework/cursor.rs index 4942bed656cc6..c000e49c14bc1 100644 --- a/compiler/rustc_mir/src/dataflow/framework/cursor.rs +++ b/compiler/rustc_mir/src/dataflow/framework/cursor.rs @@ -64,10 +64,6 @@ where } } - pub fn body(&self) -> &'mir mir::Body<'tcx> { - self.body - } - /// Returns the underlying `Results`. pub fn results(&self) -> &Results<'tcx, A> { &self.results.borrow() diff --git a/compiler/rustc_mir/src/dataflow/framework/lattice.rs b/compiler/rustc_mir/src/dataflow/framework/lattice.rs index e7ef9267db5e5..f937b31f4cfc9 100644 --- a/compiler/rustc_mir/src/dataflow/framework/lattice.rs +++ b/compiler/rustc_mir/src/dataflow/framework/lattice.rs @@ -40,6 +40,7 @@ use rustc_index::bit_set::BitSet; use rustc_index::vec::{Idx, IndexVec}; +use std::iter; /// A [partially ordered set][poset] that has a [least upper bound][lub] for any pair of elements /// in the set. @@ -110,7 +111,7 @@ impl JoinSemiLattice for IndexVec { assert_eq!(self.len(), other.len()); let mut changed = false; - for (a, b) in self.iter_mut().zip(other.iter()) { + for (a, b) in iter::zip(self, other) { changed |= a.join(b); } changed @@ -122,7 +123,7 @@ impl MeetSemiLattice for IndexVec { assert_eq!(self.len(), other.len()); let mut changed = false; - for (a, b) in self.iter_mut().zip(other.iter()) { + for (a, b) in iter::zip(self, other) { changed |= a.meet(b); } changed diff --git a/compiler/rustc_mir/src/dataflow/framework/mod.rs b/compiler/rustc_mir/src/dataflow/framework/mod.rs index 3f7808c20901b..344d7b9becd19 100644 --- a/compiler/rustc_mir/src/dataflow/framework/mod.rs +++ b/compiler/rustc_mir/src/dataflow/framework/mod.rs @@ -510,7 +510,7 @@ impl EffectIndex { } } - /// Returns `true` if the effect at `self` should be applied eariler than the effect at `other` + /// Returns `true` if the effect at `self` should be applied earlier than the effect at `other` /// in forward order. fn precedes_in_forward_order(self, other: Self) -> bool { let ord = self diff --git a/compiler/rustc_mir/src/dataflow/impls/borrows.rs b/compiler/rustc_mir/src/dataflow/impls/borrows.rs index b149ffa9667a3..c92cff1433f1a 100644 --- a/compiler/rustc_mir/src/dataflow/impls/borrows.rs +++ b/compiler/rustc_mir/src/dataflow/impls/borrows.rs @@ -11,6 +11,7 @@ use crate::borrow_check::{ use crate::dataflow::{self, fmt::DebugWithContext, GenKill}; use std::fmt; +use std::iter; rustc_index::newtype_index! { pub struct BorrowIndex { @@ -292,7 +293,7 @@ impl<'tcx> dataflow::GenKillAnalysis<'tcx> for Borrows<'_, 'tcx> { } mir::StatementKind::LlvmInlineAsm(ref asm) => { - for (output, kind) in asm.outputs.iter().zip(&asm.asm.outputs) { + for (output, kind) in iter::zip(&*asm.outputs, &asm.asm.outputs) { if !kind.is_indirect && !kind.is_rw { self.kill_borrows_on_place(trans, *output); } @@ -305,6 +306,7 @@ impl<'tcx> dataflow::GenKillAnalysis<'tcx> for Borrows<'_, 'tcx> { | mir::StatementKind::Retag { .. } | mir::StatementKind::AscribeUserType(..) | mir::StatementKind::Coverage(..) + | mir::StatementKind::CopyNonOverlapping(..) | mir::StatementKind::Nop => {} } } diff --git a/compiler/rustc_mir/src/dataflow/impls/storage_liveness.rs b/compiler/rustc_mir/src/dataflow/impls/storage_liveness.rs index 9250cd408479a..792664597fd9a 100644 --- a/compiler/rustc_mir/src/dataflow/impls/storage_liveness.rs +++ b/compiler/rustc_mir/src/dataflow/impls/storage_liveness.rs @@ -149,6 +149,7 @@ impl<'mir, 'tcx> dataflow::GenKillAnalysis<'tcx> for MaybeRequiresStorage<'mir, | StatementKind::FakeRead(..) | StatementKind::Nop | StatementKind::Retag(..) + | StatementKind::CopyNonOverlapping(..) | StatementKind::StorageLive(..) => {} } } diff --git a/compiler/rustc_mir/src/dataflow/move_paths/builder.rs b/compiler/rustc_mir/src/dataflow/move_paths/builder.rs index ee78ff00c9b2b..994b403abf3bc 100644 --- a/compiler/rustc_mir/src/dataflow/move_paths/builder.rs +++ b/compiler/rustc_mir/src/dataflow/move_paths/builder.rs @@ -4,6 +4,7 @@ use rustc_middle::mir::*; use rustc_middle::ty::{self, TyCtxt}; use smallvec::{smallvec, SmallVec}; +use std::iter; use std::mem; use super::abs_domain::Lift; @@ -292,11 +293,11 @@ impl<'b, 'a, 'tcx> Gatherer<'b, 'a, 'tcx> { } self.gather_rvalue(rval); } - StatementKind::FakeRead(_, place) => { - self.create_move_path(**place); + StatementKind::FakeRead(box (_, place)) => { + self.create_move_path(*place); } StatementKind::LlvmInlineAsm(ref asm) => { - for (output, kind) in asm.outputs.iter().zip(&asm.asm.outputs) { + for (output, kind) in iter::zip(&*asm.outputs, &asm.asm.outputs) { if !kind.is_indirect { self.gather_init(output.as_ref(), InitKind::Deep); } @@ -318,6 +319,7 @@ impl<'b, 'a, 'tcx> Gatherer<'b, 'a, 'tcx> { StatementKind::Retag { .. } | StatementKind::AscribeUserType(..) | StatementKind::Coverage(..) + | StatementKind::CopyNonOverlapping(..) | StatementKind::Nop => {} } } @@ -329,8 +331,8 @@ impl<'b, 'a, 'tcx> Gatherer<'b, 'a, 'tcx> { | Rvalue::Repeat(ref operand, _) | Rvalue::Cast(_, ref operand, _) | Rvalue::UnaryOp(_, ref operand) => self.gather_operand(operand), - Rvalue::BinaryOp(ref _binop, ref lhs, ref rhs) - | Rvalue::CheckedBinaryOp(ref _binop, ref lhs, ref rhs) => { + Rvalue::BinaryOp(ref _binop, box (ref lhs, ref rhs)) + | Rvalue::CheckedBinaryOp(ref _binop, box (ref lhs, ref rhs)) => { self.gather_operand(lhs); self.gather_operand(rhs); } @@ -423,7 +425,7 @@ impl<'b, 'a, 'tcx> Gatherer<'b, 'a, 'tcx> { for op in operands { match *op { InlineAsmOperand::In { reg: _, ref value } - | InlineAsmOperand::Const { ref value } => { + => { self.gather_operand(value); } InlineAsmOperand::Out { reg: _, late: _, place, .. } => { @@ -439,7 +441,8 @@ impl<'b, 'a, 'tcx> Gatherer<'b, 'a, 'tcx> { self.gather_init(out_place.as_ref(), InitKind::Deep); } } - InlineAsmOperand::SymFn { value: _ } + InlineAsmOperand::Const { value: _ } + | InlineAsmOperand::SymFn { value: _ } | InlineAsmOperand::SymStatic { def_id: _ } => {} } } diff --git a/compiler/rustc_mir/src/interpret/eval_context.rs b/compiler/rustc_mir/src/interpret/eval_context.rs index 6b796eb372199..2d83d6cfbdc5e 100644 --- a/compiler/rustc_mir/src/interpret/eval_context.rs +++ b/compiler/rustc_mir/src/interpret/eval_context.rs @@ -232,6 +232,8 @@ impl<'mir, 'tcx, Tag, Extra> Frame<'mir, 'tcx, Tag, Extra> { /// this frame (can happen e.g. during frame initialization, and during unwinding on /// frames without cleanup code). /// We basically abuse `Result` as `Either`. + /// + /// Used by priroda. pub fn current_loc(&self) -> Result { self.loc } @@ -459,11 +461,6 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { ty.size.truncate(value) } - #[inline] - pub fn type_is_sized(&self, ty: Ty<'tcx>) -> bool { - ty.is_sized(self.tcx, self.param_env) - } - #[inline] pub fn type_is_freeze(&self, ty: Ty<'tcx>) -> bool { ty.is_freeze(self.tcx, self.param_env) @@ -527,6 +524,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { } } + #[inline(always)] pub fn layout_of_local( &self, frame: &Frame<'mir, 'tcx, M::PointerTag, M::FrameExtra>, @@ -689,7 +687,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { let span = const_.span; let const_ = self.subst_from_current_frame_and_normalize_erasing_regions(const_.literal); - self.const_to_op(const_, None).map_err(|err| { + self.mir_const_to_op(&const_, None).map_err(|err| { // If there was an error, set the span of the current frame to this constant. // Avoiding doing this when evaluation succeeds. self.frame_mut().loc = Err(span); diff --git a/compiler/rustc_mir/src/interpret/intrinsics.rs b/compiler/rustc_mir/src/interpret/intrinsics.rs index d36b3a7d9b56e..d74ef66a4b236 100644 --- a/compiler/rustc_mir/src/interpret/intrinsics.rs +++ b/compiler/rustc_mir/src/interpret/intrinsics.rs @@ -171,8 +171,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { }; let val = self.tcx.const_eval_global_id(self.param_env, gid, Some(self.tcx.span))?; - let const_ = ty::Const { val: ty::ConstKind::Value(val), ty }; - let val = self.const_to_op(&const_, None)?; + let val = self.const_val_to_op(val, ty, Some(dest.layout))?; self.copy_op(&val, dest)?; } @@ -323,28 +322,8 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { let result = Scalar::from_uint(truncated_bits, layout.size); self.write_scalar(result, dest)?; } - sym::copy | sym::copy_nonoverlapping => { - let elem_ty = instance.substs.type_at(0); - let elem_layout = self.layout_of(elem_ty)?; - let count = self.read_scalar(&args[2])?.to_machine_usize(self)?; - let elem_align = elem_layout.align.abi; - - let size = elem_layout.size.checked_mul(count, self).ok_or_else(|| { - err_ub_format!("overflow computing total size of `{}`", intrinsic_name) - })?; - let src = self.read_scalar(&args[0])?.check_init()?; - let src = self.memory.check_ptr_access(src, size, elem_align)?; - let dest = self.read_scalar(&args[1])?.check_init()?; - let dest = self.memory.check_ptr_access(dest, size, elem_align)?; - - if let (Some(src), Some(dest)) = (src, dest) { - self.memory.copy( - src, - dest, - size, - intrinsic_name == sym::copy_nonoverlapping, - )?; - } + sym::copy => { + self.copy(&args[0], &args[1], &args[2], /*nonoverlapping*/ false)?; } sym::offset => { let ptr = self.read_scalar(&args[0])?.check_init()?; diff --git a/compiler/rustc_mir/src/interpret/intrinsics/type_name.rs b/compiler/rustc_mir/src/interpret/intrinsics/type_name.rs index e1ec4cc5e973c..ae5e78ee33f47 100644 --- a/compiler/rustc_mir/src/interpret/intrinsics/type_name.rs +++ b/compiler/rustc_mir/src/interpret/intrinsics/type_name.rs @@ -74,7 +74,7 @@ impl<'tcx> Printer<'tcx> for AbsolutePathPrinter<'tcx> { fn print_dyn_existential( mut self, - predicates: &'tcx ty::List>>, + predicates: &'tcx ty::List>>, ) -> Result { let mut first = true; for p in predicates { diff --git a/compiler/rustc_mir/src/interpret/memory.rs b/compiler/rustc_mir/src/interpret/memory.rs index f3e373813ca53..fe5ebf0b6fe97 100644 --- a/compiler/rustc_mir/src/interpret/memory.rs +++ b/compiler/rustc_mir/src/interpret/memory.rs @@ -854,7 +854,11 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> { Some(ptr) => ptr, None => { // zero-sized access - src.next().expect_none("iterator said it was empty but returned an element"); + assert_matches!( + src.next(), + None, + "iterator said it was empty but returned an element" + ); return Ok(()); } }; @@ -880,7 +884,11 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> { Some(ptr) => ptr, None => { // zero-sized access - src.next().expect_none("iterator said it was empty but returned an element"); + assert_matches!( + src.next(), + None, + "iterator said it was empty but returned an element" + ); return Ok(()); } }; @@ -894,7 +902,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> { let offset_ptr = ptr.offset(Size::from_bytes(idx) * 2, &tcx)?; // `Size` multiplication allocation.write_scalar(&tcx, offset_ptr, val.into(), Size::from_bytes(2))?; } - src.next().expect_none("iterator was longer than it said it would be"); + assert_matches!(src.next(), None, "iterator was longer than it said it would be"); Ok(()) } diff --git a/compiler/rustc_mir/src/interpret/operand.rs b/compiler/rustc_mir/src/interpret/operand.rs index 901ed6809f24f..e5bc9320260c6 100644 --- a/compiler/rustc_mir/src/interpret/operand.rs +++ b/compiler/rustc_mir/src/interpret/operand.rs @@ -32,7 +32,7 @@ pub enum Immediate { ScalarPair(ScalarMaybeUninit, ScalarMaybeUninit), } -#[cfg(target_arch = "x86_64")] +#[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))] rustc_data_structures::static_assert_size!(Immediate, 56); impl From> for Immediate { @@ -77,14 +77,6 @@ impl<'tcx, Tag> Immediate { pub fn to_scalar(self) -> InterpResult<'tcx, Scalar> { self.to_scalar_or_uninit().check_init() } - - #[inline] - pub fn to_scalar_pair(self) -> InterpResult<'tcx, (Scalar, Scalar)> { - match self { - Immediate::Scalar(..) => bug!("Got a thin pointer where a scalar pair was expected"), - Immediate::ScalarPair(a, b) => Ok((a.check_init()?, b.check_init()?)), - } - } } // ScalarPair needs a type to interpret, so we often have an immediate and a type together @@ -95,7 +87,7 @@ pub struct ImmTy<'tcx, Tag = ()> { pub layout: TyAndLayout<'tcx>, } -#[cfg(target_arch = "x86_64")] +#[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))] rustc_data_structures::static_assert_size!(ImmTy<'_>, 72); impl std::fmt::Display for ImmTy<'tcx, Tag> { @@ -162,7 +154,7 @@ pub struct OpTy<'tcx, Tag = ()> { pub layout: TyAndLayout<'tcx>, } -#[cfg(target_arch = "x86_64")] +#[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))] rustc_data_structures::static_assert_size!(OpTy<'_, ()>, 80); impl<'tcx, Tag> std::ops::Deref for OpTy<'tcx, Tag> { @@ -233,7 +225,7 @@ impl<'tcx, Tag: Copy> ImmTy<'tcx, Tag> { } impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { - /// Normalice `place.ptr` to a `Pointer` if this is a place and not a ZST. + /// Normalize `place.ptr` to a `Pointer` if this is a place and not a ZST. /// Can be helpful to avoid lots of `force_ptr` calls later, if this place is used a lot. #[inline] pub fn force_op_ptr( @@ -532,7 +524,8 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { // * During ConstProp, with `TooGeneric` or since the `requried_consts` were not all // checked yet. // * During CTFE, since promoteds in `const`/`static` initializer bodies can fail. - self.const_to_op(val, layout)? + + self.mir_const_to_op(&val, layout)? } }; trace!("{:?}: {:?}", mir_op, *op); @@ -556,28 +549,45 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { val: &ty::Const<'tcx>, layout: Option>, ) -> InterpResult<'tcx, OpTy<'tcx, M::PointerTag>> { - let tag_scalar = |scalar| -> InterpResult<'tcx, _> { - Ok(match scalar { - Scalar::Ptr(ptr) => Scalar::Ptr(self.global_base_pointer(ptr)?), - Scalar::Int(int) => Scalar::Int(int), - }) - }; - // Early-return cases. - let val_val = match val.val { + match val.val { ty::ConstKind::Param(_) | ty::ConstKind::Bound(..) => throw_inval!(TooGeneric), ty::ConstKind::Error(_) => throw_inval!(AlreadyReported(ErrorReported)), - ty::ConstKind::Unevaluated(def, substs, promoted) => { + ty::ConstKind::Unevaluated(ty::Unevaluated { def, substs, promoted }) => { let instance = self.resolve(def, substs)?; - return Ok(self.eval_to_allocation(GlobalId { instance, promoted })?.into()); + Ok(self.eval_to_allocation(GlobalId { instance, promoted })?.into()) } ty::ConstKind::Infer(..) | ty::ConstKind::Placeholder(..) => { span_bug!(self.cur_span(), "const_to_op: Unexpected ConstKind {:?}", val) } - ty::ConstKind::Value(val_val) => val_val, - }; + ty::ConstKind::Value(val_val) => self.const_val_to_op(val_val, val.ty, layout), + } + } + + crate fn mir_const_to_op( + &self, + val: &mir::ConstantKind<'tcx>, + layout: Option>, + ) -> InterpResult<'tcx, OpTy<'tcx, M::PointerTag>> { + match val { + mir::ConstantKind::Ty(ct) => self.const_to_op(ct, layout), + mir::ConstantKind::Val(val, ty) => self.const_val_to_op(*val, ty, layout), + } + } + + crate fn const_val_to_op( + &self, + val_val: ConstValue<'tcx>, + ty: Ty<'tcx>, + layout: Option>, + ) -> InterpResult<'tcx, OpTy<'tcx, M::PointerTag>> { // Other cases need layout. - let layout = - from_known_layout(self.tcx, self.param_env, layout, || self.layout_of(val.ty))?; + let tag_scalar = |scalar| -> InterpResult<'tcx, _> { + Ok(match scalar { + Scalar::Ptr(ptr) => Scalar::Ptr(self.global_base_pointer(ptr)?), + Scalar::Int(int) => Scalar::Int(int), + }) + }; + let layout = from_known_layout(self.tcx, self.param_env, layout, || self.layout_of(ty))?; let op = match val_val { ConstValue::ByRef { alloc, offset } => { let id = self.tcx.create_memory_alloc(alloc); diff --git a/compiler/rustc_mir/src/interpret/place.rs b/compiler/rustc_mir/src/interpret/place.rs index 392f739e84fd6..699b531f50188 100644 --- a/compiler/rustc_mir/src/interpret/place.rs +++ b/compiler/rustc_mir/src/interpret/place.rs @@ -33,7 +33,7 @@ pub enum MemPlaceMeta { Poison, } -#[cfg(target_arch = "x86_64")] +#[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))] rustc_data_structures::static_assert_size!(MemPlaceMeta, 24); impl MemPlaceMeta { @@ -74,7 +74,7 @@ pub struct MemPlace { pub meta: MemPlaceMeta, } -#[cfg(target_arch = "x86_64")] +#[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))] rustc_data_structures::static_assert_size!(MemPlace, 56); #[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, HashStable)] @@ -87,7 +87,7 @@ pub enum Place { Local { frame: usize, local: mir::Local }, } -#[cfg(target_arch = "x86_64")] +#[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))] rustc_data_structures::static_assert_size!(Place, 64); #[derive(Copy, Clone, Debug)] @@ -96,7 +96,7 @@ pub struct PlaceTy<'tcx, Tag = ()> { pub layout: TyAndLayout<'tcx>, } -#[cfg(target_arch = "x86_64")] +#[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))] rustc_data_structures::static_assert_size!(PlaceTy<'_>, 80); impl<'tcx, Tag> std::ops::Deref for PlaceTy<'tcx, Tag> { @@ -114,7 +114,7 @@ pub struct MPlaceTy<'tcx, Tag = ()> { pub layout: TyAndLayout<'tcx>, } -#[cfg(target_arch = "x86_64")] +#[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))] rustc_data_structures::static_assert_size!(MPlaceTy<'_>, 72); impl<'tcx, Tag> std::ops::Deref for MPlaceTy<'tcx, Tag> { @@ -531,7 +531,7 @@ where base.offset(from_offset, meta, layout, self) } - pub(super) fn mplace_downcast( + pub(crate) fn mplace_downcast( &self, base: &MPlaceTy<'tcx, M::PointerTag>, variant: VariantIdx, diff --git a/compiler/rustc_mir/src/interpret/step.rs b/compiler/rustc_mir/src/interpret/step.rs index 64d7c8ef2c719..6084f67abd78e 100644 --- a/compiler/rustc_mir/src/interpret/step.rs +++ b/compiler/rustc_mir/src/interpret/step.rs @@ -2,6 +2,7 @@ //! //! The main entry point is the `step` method. +use crate::interpret::OpTy; use rustc_middle::mir; use rustc_middle::mir::interpret::{InterpResult, Scalar}; use rustc_target::abi::LayoutOf; @@ -113,6 +114,14 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { M::retag(self, *kind, &dest)?; } + // Call CopyNonOverlapping + CopyNonOverlapping(box rustc_middle::mir::CopyNonOverlapping { src, dst, count }) => { + let src = self.eval_operand(src, None)?; + let dst = self.eval_operand(dst, None)?; + let count = self.eval_operand(count, None)?; + self.copy(&src, &dst, &count, /* nonoverlapping */ true)?; + } + // Statements we do not track. AscribeUserType(..) => {} @@ -140,6 +149,37 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { Ok(()) } + pub(crate) fn copy( + &mut self, + src: &OpTy<'tcx, >::PointerTag>, + dst: &OpTy<'tcx, >::PointerTag>, + count: &OpTy<'tcx, >::PointerTag>, + nonoverlapping: bool, + ) -> InterpResult<'tcx> { + let count = self.read_scalar(&count)?.to_machine_usize(self)?; + let layout = self.layout_of(src.layout.ty.builtin_deref(true).unwrap().ty)?; + let (size, align) = (layout.size, layout.align.abi); + let size = size.checked_mul(count, self).ok_or_else(|| { + err_ub_format!( + "overflow computing total size of `{}`", + if nonoverlapping { "copy_nonoverlapping" } else { "copy" } + ) + })?; + + // Make sure we check both pointers for an access of the total size and aligment, + // *even if* the total size is 0. + let src = + self.memory.check_ptr_access(self.read_scalar(&src)?.check_init()?, size, align)?; + + let dst = + self.memory.check_ptr_access(self.read_scalar(&dst)?.check_init()?, size, align)?; + + if let (Some(src), Some(dst)) = (src, dst) { + self.memory.copy(src, dst, size, nonoverlapping)?; + } + Ok(()) + } + /// Evaluate an assignment statement. /// /// There is no separate `eval_rvalue` function. Instead, the code for handling each rvalue @@ -165,7 +205,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { self.copy_op(&op, &dest)?; } - BinaryOp(bin_op, ref left, ref right) => { + BinaryOp(bin_op, box (ref left, ref right)) => { let layout = binop_left_homogeneous(bin_op).then_some(dest.layout); let left = self.read_immediate(&self.eval_operand(left, layout)?)?; let layout = binop_right_homogeneous(bin_op).then_some(left.layout); @@ -173,7 +213,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { self.binop_ignore_overflow(bin_op, &left, &right, &dest)?; } - CheckedBinaryOp(bin_op, ref left, ref right) => { + CheckedBinaryOp(bin_op, box (ref left, ref right)) => { // Due to the extra boolean in the result, we can never reuse the `dest.layout`. let left = self.read_immediate(&self.eval_operand(left, None)?)?; let layout = binop_right_homogeneous(bin_op).then_some(left.layout); diff --git a/compiler/rustc_mir/src/interpret/terminator.rs b/compiler/rustc_mir/src/interpret/terminator.rs index 0807949a2d91b..4aa1360d53539 100644 --- a/compiler/rustc_mir/src/interpret/terminator.rs +++ b/compiler/rustc_mir/src/interpret/terminator.rs @@ -248,9 +248,9 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { }; if normalize_abi(caller_abi) != normalize_abi(callee_abi) { throw_ub_format!( - "calling a function with ABI {:?} using caller ABI {:?}", - callee_abi, - caller_abi + "calling a function with ABI {} using caller ABI {}", + callee_abi.name(), + caller_abi.name() ) } } diff --git a/compiler/rustc_mir/src/interpret/validity.rs b/compiler/rustc_mir/src/interpret/validity.rs index 2d2799f81e392..062ef7d8b4cbf 100644 --- a/compiler/rustc_mir/src/interpret/validity.rs +++ b/compiler/rustc_mir/src/interpret/validity.rs @@ -77,7 +77,7 @@ macro_rules! throw_validation_failure { /// macro_rules! try_validation { ($e:expr, $where:expr, - $( $( $p:pat )|+ => { $( $what_fmt:expr ),+ } $( expected { $( $expected_fmt:expr ),+ } )? ),+ $(,)? + $( $( $p:pat )|+ => { $( $what_fmt:expr ),+ } $( expected { $( $expected_fmt:expr ),+ } )? ),+ $(,)? ) => {{ match $e { Ok(x) => x, @@ -244,17 +244,20 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, ' // generators and closures. ty::Closure(def_id, _) | ty::Generator(def_id, _, _) => { let mut name = None; - if let Some(def_id) = def_id.as_local() { - let tables = self.ecx.tcx.typeck(def_id); - if let Some(upvars) = tables.closure_captures.get(&def_id.to_def_id()) { + // FIXME this should be more descriptive i.e. CapturePlace instead of CapturedVar + // https://github.com/rust-lang/project-rfc-2229/issues/46 + if let Some(local_def_id) = def_id.as_local() { + let tables = self.ecx.tcx.typeck(local_def_id); + if let Some(captured_place) = + tables.closure_min_captures_flattened(*def_id).nth(field) + { // Sometimes the index is beyond the number of upvars (seen // for a generator). - if let Some((&var_hir_id, _)) = upvars.get_index(field) { - let node = self.ecx.tcx.hir().get(var_hir_id); - if let hir::Node::Binding(pat) = node { - if let hir::PatKind::Binding(_, _, ident, _) = pat.kind { - name = Some(ident.name); - } + let var_hir_id = captured_place.get_root_variable(); + let node = self.ecx.tcx.hir().get(var_hir_id); + if let hir::Node::Binding(pat) = node { + if let hir::PatKind::Binding(_, _, ident, _) = pat.kind { + name = Some(ident.name); } } } diff --git a/compiler/rustc_mir/src/lib.rs b/compiler/rustc_mir/src/lib.rs index 508510a81e1fb..b0db4f9e649b3 100644 --- a/compiler/rustc_mir/src/lib.rs +++ b/compiler/rustc_mir/src/lib.rs @@ -7,6 +7,7 @@ Rust MIR: a lowered representation of Rust. #![feature(nll)] #![feature(in_band_lifetimes)] #![feature(array_windows)] +#![feature(assert_matches)] #![feature(bindings_after_at)] #![feature(bool_to_option)] #![feature(box_patterns)] @@ -17,15 +18,17 @@ Rust MIR: a lowered representation of Rust. #![feature(decl_macro)] #![feature(exact_size_is_empty)] #![feature(exhaustive_patterns)] +#![feature(iter_zip)] #![feature(never_type)] +#![feature(map_try_insert)] #![feature(min_specialization)] #![feature(trusted_len)] #![feature(try_blocks)] #![feature(associated_type_defaults)] #![feature(stmt_expr_attributes)] #![feature(trait_alias)] -#![feature(option_expect_none)] -#![feature(or_patterns)] +#![feature(option_get_or_insert_default)] +#![cfg_attr(bootstrap, feature(or_patterns))] #![feature(once_cell)] #![feature(control_flow_enum)] #![recursion_limit = "256"] @@ -62,6 +65,10 @@ pub fn provide(providers: &mut Providers) { let (param_env, value) = param_env_and_value.into_parts(); const_eval::destructure_const(tcx, param_env, value) }; + providers.const_to_valtree = |tcx, param_env_and_value| { + let (param_env, raw) = param_env_and_value.into_parts(); + const_eval::const_to_valtree(tcx, param_env, raw) + }; providers.deref_const = |tcx, param_env_and_value| { let (param_env, value) = param_env_and_value.into_parts(); const_eval::deref_const(tcx, param_env, value) diff --git a/compiler/rustc_mir/src/monomorphize/collector.rs b/compiler/rustc_mir/src/monomorphize/collector.rs index 20cb989196a98..1fda71d74bbf5 100644 --- a/compiler/rustc_mir/src/monomorphize/collector.rs +++ b/compiler/rustc_mir/src/monomorphize/collector.rs @@ -59,11 +59,15 @@ //! //! ### Discovering roots //! -//! The roots of the mono item graph correspond to the non-generic +//! The roots of the mono item graph correspond to the public non-generic //! syntactic items in the source code. We find them by walking the HIR of the -//! crate, and whenever we hit upon a function, method, or static item, we -//! create a mono item consisting of the items DefId and, since we only -//! consider non-generic items, an empty type-substitution set. +//! crate, and whenever we hit upon a public function, method, or static item, +//! we create a mono item consisting of the items DefId and, since we only +//! consider non-generic items, an empty type-substitution set. (In eager +//! collection mode, during incremental compilation, all non-generic functions +//! are considered as roots, as well as when the `-Clink-dead-code` option is +//! specified. Functions marked `#[no_mangle]` and functions called by inlinable +//! functions also always act as roots.) //! //! ### Finding neighbor nodes //! Given a mono item node, we can discover neighbors by inspecting its @@ -184,7 +188,6 @@ use rustc_hir::def_id::{DefId, DefIdMap, LocalDefId, LOCAL_CRATE}; use rustc_hir::itemlikevisit::ItemLikeVisitor; use rustc_hir::lang_items::LangItem; use rustc_index::bit_set::GrowableBitSet; -use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags; use rustc_middle::mir::interpret::{AllocId, ConstValue}; use rustc_middle::mir::interpret::{ErrorHandled, GlobalAlloc, Scalar}; use rustc_middle::mir::mono::{InstantiationMode, MonoItem}; @@ -193,6 +196,7 @@ use rustc_middle::mir::{self, Local, Location}; use rustc_middle::ty::adjustment::{CustomCoerceUnsized, PointerCast}; use rustc_middle::ty::subst::{GenericArgKind, InternalSubsts}; use rustc_middle::ty::{self, GenericParamDefKind, Instance, Ty, TyCtxt, TypeFoldable}; +use rustc_middle::{middle::codegen_fn_attrs::CodegenFnAttrFlags, mir::visit::TyContext}; use rustc_session::config::EntryFnType; use rustc_span::source_map::{dummy_spanned, respan, Span, Spanned, DUMMY_SP}; use smallvec::SmallVec; @@ -638,6 +642,35 @@ impl<'a, 'tcx> MirVisitor<'tcx> for MirNeighborCollector<'a, 'tcx> { self.super_rvalue(rvalue, location); } + /// This does not walk the constant, as it has been handled entirely here and trying + /// to walk it would attempt to evaluate the `ty::Const` inside, which doesn't necessarily + /// work, as some constants cannot be represented in the type system. + fn visit_constant(&mut self, constant: &mir::Constant<'tcx>, location: Location) { + let literal = self.monomorphize(constant.literal); + let val = match literal { + mir::ConstantKind::Val(val, _) => val, + mir::ConstantKind::Ty(ct) => match ct.val { + ty::ConstKind::Value(val) => val, + ty::ConstKind::Unevaluated(ct) => { + let param_env = ty::ParamEnv::reveal_all(); + match self.tcx.const_eval_resolve(param_env, ct, None) { + // The `monomorphize` call should have evaluated that constant already. + Ok(val) => val, + Err(ErrorHandled::Reported(ErrorReported) | ErrorHandled::Linted) => return, + Err(ErrorHandled::TooGeneric) => span_bug!( + self.body.source_info(location).span, + "collection encountered polymorphic constant: {:?}", + literal + ), + } + } + _ => return, + }, + }; + collect_const_value(self.tcx, val, self.output); + self.visit_ty(literal.ty(), TyContext::Location(location)); + } + fn visit_const(&mut self, constant: &&'tcx ty::Const<'tcx>, location: Location) { debug!("visiting const {:?} @ {:?}", *constant, location); @@ -646,9 +679,15 @@ impl<'a, 'tcx> MirVisitor<'tcx> for MirNeighborCollector<'a, 'tcx> { match substituted_constant.val { ty::ConstKind::Value(val) => collect_const_value(self.tcx, val, self.output), - ty::ConstKind::Unevaluated(def, substs, promoted) => { - match self.tcx.const_eval_resolve(param_env, def, substs, promoted, None) { - Ok(val) => collect_const_value(self.tcx, val, self.output), + ty::ConstKind::Unevaluated(unevaluated) => { + match self.tcx.const_eval_resolve(param_env, unevaluated, None) { + // The `monomorphize` call should have evaluated that constant already. + Ok(val) => span_bug!( + self.body.source_info(location).span, + "collection encountered the unevaluated constant {} which evaluated to {:?}", + substituted_constant, + val + ), Err(ErrorHandled::Reported(ErrorReported) | ErrorHandled::Linted) => {} Err(ErrorHandled::TooGeneric) => span_bug!( self.body.source_info(location).span, @@ -684,7 +723,7 @@ impl<'a, 'tcx> MirVisitor<'tcx> for MirNeighborCollector<'a, 'tcx> { for op in operands { match *op { mir::InlineAsmOperand::SymFn { ref value } => { - let fn_ty = self.monomorphize(value.literal.ty); + let fn_ty = self.monomorphize(value.literal.ty()); visit_fn_use(self.tcx, fn_ty, false, source, &mut self.output); } mir::InlineAsmOperand::SymStatic { def_id } => { @@ -1175,7 +1214,8 @@ fn create_mono_items_for_default_impls<'tcx>( let substs = InternalSubsts::for_item(tcx, method.def_id, |param, _| match param.kind { GenericParamDefKind::Lifetime => tcx.lifetimes.re_erased.into(), - GenericParamDefKind::Type { .. } | GenericParamDefKind::Const => { + GenericParamDefKind::Type { .. } + | GenericParamDefKind::Const { .. } => { trait_ref.substs[param.index as usize] } }); diff --git a/compiler/rustc_mir/src/monomorphize/mod.rs b/compiler/rustc_mir/src/monomorphize/mod.rs index d2586f0f84dff..9ca4b6687f1b5 100644 --- a/compiler/rustc_mir/src/monomorphize/mod.rs +++ b/compiler/rustc_mir/src/monomorphize/mod.rs @@ -8,14 +8,14 @@ pub mod collector; pub mod partitioning; pub mod polymorphize; -pub fn custom_coerce_unsize_info<'tcx>( +fn custom_coerce_unsize_info<'tcx>( tcx: TyCtxt<'tcx>, source_ty: Ty<'tcx>, target_ty: Ty<'tcx>, ) -> CustomCoerceUnsized { let def_id = tcx.require_lang_item(LangItem::CoerceUnsized, None); - let trait_ref = ty::Binder::bind(ty::TraitRef { + let trait_ref = ty::Binder::dummy(ty::TraitRef { def_id, substs: tcx.mk_substs_trait(source_ty, &[target_ty.into()]), }); diff --git a/compiler/rustc_mir/src/monomorphize/partitioning/mod.rs b/compiler/rustc_mir/src/monomorphize/partitioning/mod.rs index b68a8104fba72..dc2379fd92b83 100644 --- a/compiler/rustc_mir/src/monomorphize/partitioning/mod.rs +++ b/compiler/rustc_mir/src/monomorphize/partitioning/mod.rs @@ -424,8 +424,33 @@ fn collect_and_partition_mono_items<'tcx>( (tcx.arena.alloc(mono_items), codegen_units) } +fn codegened_and_inlined_items<'tcx>(tcx: TyCtxt<'tcx>, cnum: CrateNum) -> &'tcx DefIdSet { + let (items, cgus) = tcx.collect_and_partition_mono_items(cnum); + let mut visited = DefIdSet::default(); + let mut result = items.clone(); + + for cgu in cgus { + for (item, _) in cgu.items() { + if let MonoItem::Fn(ref instance) = item { + let did = instance.def_id(); + if !visited.insert(did) { + continue; + } + for scope in &tcx.instance_mir(instance.def).source_scopes { + if let Some((ref inlined, _)) = scope.inlined { + result.insert(inlined.def_id()); + } + } + } + } + } + + tcx.arena.alloc(result) +} + pub fn provide(providers: &mut Providers) { providers.collect_and_partition_mono_items = collect_and_partition_mono_items; + providers.codegened_and_inlined_items = codegened_and_inlined_items; providers.is_codegened_item = |tcx, def_id| { let (all_mono_items, _) = tcx.collect_and_partition_mono_items(LOCAL_CRATE); diff --git a/compiler/rustc_mir/src/monomorphize/polymorphize.rs b/compiler/rustc_mir/src/monomorphize/polymorphize.rs index 4ad71ab4913ba..30e758c7fdf05 100644 --- a/compiler/rustc_mir/src/monomorphize/polymorphize.rs +++ b/compiler/rustc_mir/src/monomorphize/polymorphize.rs @@ -30,9 +30,8 @@ pub fn provide(providers: &mut Providers) { /// Determine which generic parameters are used by the function/method/closure represented by /// `def_id`. Returns a bitset where bits representing unused parameters are set (`is_empty` /// indicates all parameters are used). +#[instrument(skip(tcx))] fn unused_generic_params(tcx: TyCtxt<'_>, def_id: DefId) -> FiniteBitSet { - debug!("unused_generic_params({:?})", def_id); - if !tcx.sess.opts.debugging_opts.polymorphize { // If polymorphization disabled, then all parameters are used. return FiniteBitSet::new_empty(); @@ -46,7 +45,7 @@ fn unused_generic_params(tcx: TyCtxt<'_>, def_id: DefId) -> FiniteBitSet { } let generics = tcx.generics_of(def_id); - debug!("unused_generic_params: generics={:?}", generics); + debug!(?generics); // Exit early when there are no parameters to be unused. if generics.count() == 0 { @@ -57,11 +56,11 @@ fn unused_generic_params(tcx: TyCtxt<'_>, def_id: DefId) -> FiniteBitSet { let context = tcx.hir().body_const_context(def_id.expect_local()); match context { Some(ConstContext::ConstFn) | None if !tcx.is_mir_available(def_id) => { - debug!("unused_generic_params: (no mir available) def_id={:?}", def_id); + debug!("no mir available"); return FiniteBitSet::new_empty(); } Some(_) if !tcx.is_ctfe_mir_available(def_id) => { - debug!("unused_generic_params: (no ctfe mir available) def_id={:?}", def_id); + debug!("no ctfe mir available"); return FiniteBitSet::new_empty(); } _ => {} @@ -72,9 +71,9 @@ fn unused_generic_params(tcx: TyCtxt<'_>, def_id: DefId) -> FiniteBitSet { generics.count().try_into().expect("more generic parameters than can fit into a `u32`"); let mut unused_parameters = FiniteBitSet::::new_empty(); unused_parameters.set_range(0..generics_count); - debug!("unused_generic_params: (start) unused_parameters={:?}", unused_parameters); + debug!(?unused_parameters, "(start)"); mark_used_by_default_parameters(tcx, def_id, generics, &mut unused_parameters); - debug!("unused_generic_params: (after default) unused_parameters={:?}", unused_parameters); + debug!(?unused_parameters, "(after default)"); // Visit MIR and accumululate used generic parameters. let body = match context { @@ -85,10 +84,10 @@ fn unused_generic_params(tcx: TyCtxt<'_>, def_id: DefId) -> FiniteBitSet { }; let mut vis = MarkUsedGenericParams { tcx, def_id, unused_parameters: &mut unused_parameters }; vis.visit_body(body); - debug!("unused_generic_params: (after visitor) unused_parameters={:?}", unused_parameters); + debug!(?unused_parameters, "(after visitor)"); mark_used_by_predicates(tcx, def_id, &mut unused_parameters); - debug!("unused_generic_params: (end) unused_parameters={:?}", unused_parameters); + debug!(?unused_parameters, "(end)"); // Emit errors for debugging and testing if enabled. if !unused_parameters.is_empty() { @@ -101,24 +100,55 @@ fn unused_generic_params(tcx: TyCtxt<'_>, def_id: DefId) -> FiniteBitSet { /// Some parameters are considered used-by-default, such as non-generic parameters and the dummy /// generic parameters from closures, this function marks them as used. `leaf_is_closure` should /// be `true` if the item that `unused_generic_params` was invoked on is a closure. +#[instrument(skip(tcx, def_id, generics, unused_parameters))] fn mark_used_by_default_parameters<'tcx>( tcx: TyCtxt<'tcx>, def_id: DefId, generics: &'tcx ty::Generics, unused_parameters: &mut FiniteBitSet, ) { - if !tcx.is_trait(def_id) && (tcx.is_closure(def_id) || tcx.type_of(def_id).is_generator()) { - for param in &generics.params { - debug!("mark_used_by_default_parameters: (closure/gen) param={:?}", param); - unused_parameters.clear(param.index); - } - } else { - for param in &generics.params { - debug!("mark_used_by_default_parameters: (other) param={:?}", param); - if let ty::GenericParamDefKind::Lifetime = param.kind { + match tcx.def_kind(def_id) { + DefKind::Closure | DefKind::Generator => { + for param in &generics.params { + debug!(?param, "(closure/gen)"); unused_parameters.clear(param.index); } } + DefKind::Mod + | DefKind::Struct + | DefKind::Union + | DefKind::Enum + | DefKind::Variant + | DefKind::Trait + | DefKind::TyAlias + | DefKind::ForeignTy + | DefKind::TraitAlias + | DefKind::AssocTy + | DefKind::TyParam + | DefKind::Fn + | DefKind::Const + | DefKind::ConstParam + | DefKind::Static + | DefKind::Ctor(_, _) + | DefKind::AssocFn + | DefKind::AssocConst + | DefKind::Macro(_) + | DefKind::ExternCrate + | DefKind::Use + | DefKind::ForeignMod + | DefKind::AnonConst + | DefKind::OpaqueTy + | DefKind::Field + | DefKind::LifetimeParam + | DefKind::GlobalAsm + | DefKind::Impl => { + for param in &generics.params { + debug!(?param, "(other)"); + if let ty::GenericParamDefKind::Lifetime = param.kind { + unused_parameters.clear(param.index); + } + } + } } if let Some(parent) = generics.parent { @@ -128,6 +158,7 @@ fn mark_used_by_default_parameters<'tcx>( /// Search the predicates on used generic parameters for any unused generic parameters, and mark /// those as used. +#[instrument(skip(tcx, def_id))] fn mark_used_by_predicates<'tcx>( tcx: TyCtxt<'tcx>, def_id: DefId, @@ -135,16 +166,12 @@ fn mark_used_by_predicates<'tcx>( ) { let def_id = tcx.closure_base_def_id(def_id); let predicates = tcx.explicit_predicates_of(def_id); - debug!("mark_used_by_predicates: predicates_of={:?}", predicates); let mut current_unused_parameters = FiniteBitSet::new_empty(); // Run to a fixed point to support `where T: Trait, U: Trait`, starting with an empty // bit set so that this is skipped if all parameters are already used. while current_unused_parameters != *unused_parameters { - debug!( - "mark_used_by_predicates: current_unused_parameters={:?} = unused_parameters={:?}", - current_unused_parameters, unused_parameters - ); + debug!(?current_unused_parameters, ?unused_parameters); current_unused_parameters = *unused_parameters; for (predicate, _) in predicates.predicates { @@ -169,13 +196,13 @@ fn mark_used_by_predicates<'tcx>( /// Emit errors for the function annotated by `#[rustc_polymorphize_error]`, labelling each generic /// parameter which was unused. +#[instrument(skip(tcx, generics))] fn emit_unused_generic_params_error<'tcx>( tcx: TyCtxt<'tcx>, def_id: DefId, generics: &'tcx ty::Generics, unused_parameters: &FiniteBitSet, ) { - debug!("emit_unused_generic_params_error: def_id={:?}", def_id); let base_def_id = tcx.closure_base_def_id(def_id); if !tcx .get_attrs(base_def_id) @@ -185,7 +212,6 @@ fn emit_unused_generic_params_error<'tcx>( return; } - debug!("emit_unused_generic_params_error: unused_parameters={:?}", unused_parameters); let fn_span = match tcx.opt_item_name(def_id) { Some(ident) => ident.span, _ => tcx.def_span(def_id), @@ -197,7 +223,7 @@ fn emit_unused_generic_params_error<'tcx>( while let Some(generics) = next_generics { for param in &generics.params { if unused_parameters.contains(param.index).unwrap_or(false) { - debug!("emit_unused_generic_params_error: param={:?}", param); + debug!(?param); let def_span = tcx.def_span(param.def_id); err.span_label(def_span, &format!("generic parameter `{}` is unused", param.name)); } @@ -219,25 +245,23 @@ struct MarkUsedGenericParams<'a, 'tcx> { impl<'a, 'tcx> MarkUsedGenericParams<'a, 'tcx> { /// Invoke `unused_generic_params` on a body contained within the current item (e.g. /// a closure, generator or constant). + #[instrument(skip(self, def_id, substs))] fn visit_child_body(&mut self, def_id: DefId, substs: SubstsRef<'tcx>) { let unused = self.tcx.unused_generic_params(def_id); - debug!( - "visit_child_body: unused_parameters={:?} unused={:?}", - self.unused_parameters, unused - ); + debug!(?self.unused_parameters, ?unused); for (i, arg) in substs.iter().enumerate() { let i = i.try_into().unwrap(); if !unused.contains(i).unwrap_or(false) { arg.visit_with(self); } } - debug!("visit_child_body: unused_parameters={:?}", self.unused_parameters); + debug!(?self.unused_parameters); } } impl<'a, 'tcx> Visitor<'tcx> for MarkUsedGenericParams<'a, 'tcx> { + #[instrument(skip(self, local))] fn visit_local_decl(&mut self, local: Local, local_decl: &LocalDecl<'tcx>) { - debug!("visit_local_decl: local_decl={:?}", local_decl); if local == Local::from_usize(1) { let def_kind = self.tcx.def_kind(self.def_id); if matches!(def_kind, DefKind::Closure | DefKind::Generator) { @@ -245,7 +269,7 @@ impl<'a, 'tcx> Visitor<'tcx> for MarkUsedGenericParams<'a, 'tcx> { // happens because the first argument to the closure is a reference to itself and // that will call `visit_substs`, resulting in each generic parameter captured being // considered used by default. - debug!("visit_local_decl: skipping closure substs"); + debug!("skipping closure substs"); return; } } @@ -263,19 +287,19 @@ impl<'a, 'tcx> Visitor<'tcx> for MarkUsedGenericParams<'a, 'tcx> { } impl<'a, 'tcx> TypeVisitor<'tcx> for MarkUsedGenericParams<'a, 'tcx> { + #[instrument(skip(self))] fn visit_const(&mut self, c: &'tcx Const<'tcx>) -> ControlFlow { - debug!("visit_const: c={:?}", c); if !c.has_param_types_or_consts() { return ControlFlow::CONTINUE; } match c.val { ty::ConstKind::Param(param) => { - debug!("visit_const: param={:?}", param); + debug!(?param); self.unused_parameters.clear(param.index); ControlFlow::CONTINUE } - ty::ConstKind::Unevaluated(def, _, Some(p)) + ty::ConstKind::Unevaluated(ty::Unevaluated { def, substs: _, promoted: Some(p)}) // Avoid considering `T` unused when constants are of the form: // `>::foo::promoted[p]` if self.def_id == def.did && !self.tcx.generics_of(def.did).has_self => @@ -286,25 +310,25 @@ impl<'a, 'tcx> TypeVisitor<'tcx> for MarkUsedGenericParams<'a, 'tcx> { self.visit_body(&promoted[p]); ControlFlow::CONTINUE } - ty::ConstKind::Unevaluated(def, unevaluated_substs, None) + ty::ConstKind::Unevaluated(ty::Unevaluated { def, substs, promoted: None }) if self.tcx.def_kind(def.did) == DefKind::AnonConst => { - self.visit_child_body(def.did, unevaluated_substs); + self.visit_child_body(def.did, substs); ControlFlow::CONTINUE } _ => c.super_visit_with(self), } } + #[instrument(skip(self))] fn visit_ty(&mut self, ty: Ty<'tcx>) -> ControlFlow { - debug!("visit_ty: ty={:?}", ty); if !ty.has_param_types_or_consts() { return ControlFlow::CONTINUE; } match *ty.kind() { ty::Closure(def_id, substs) | ty::Generator(def_id, substs, ..) => { - debug!("visit_ty: def_id={:?}", def_id); + debug!(?def_id); // Avoid cycle errors with generators. if def_id == self.def_id { return ControlFlow::CONTINUE; @@ -316,7 +340,7 @@ impl<'a, 'tcx> TypeVisitor<'tcx> for MarkUsedGenericParams<'a, 'tcx> { ControlFlow::CONTINUE } ty::Param(param) => { - debug!("visit_ty: param={:?}", param); + debug!(?param); self.unused_parameters.clear(param.index); ControlFlow::CONTINUE } @@ -333,8 +357,8 @@ struct HasUsedGenericParams<'a> { impl<'a, 'tcx> TypeVisitor<'tcx> for HasUsedGenericParams<'a> { type BreakTy = (); + #[instrument(skip(self))] fn visit_const(&mut self, c: &'tcx Const<'tcx>) -> ControlFlow { - debug!("visit_const: c={:?}", c); if !c.has_param_types_or_consts() { return ControlFlow::CONTINUE; } @@ -351,8 +375,8 @@ impl<'a, 'tcx> TypeVisitor<'tcx> for HasUsedGenericParams<'a> { } } + #[instrument(skip(self))] fn visit_ty(&mut self, ty: Ty<'tcx>) -> ControlFlow { - debug!("visit_ty: ty={:?}", ty); if !ty.has_param_types_or_consts() { return ControlFlow::CONTINUE; } diff --git a/compiler/rustc_mir/src/shim.rs b/compiler/rustc_mir/src/shim.rs index b246ec5c8146b..796d024771d7f 100644 --- a/compiler/rustc_mir/src/shim.rs +++ b/compiler/rustc_mir/src/shim.rs @@ -421,7 +421,7 @@ impl CloneShimBuilder<'tcx> { let func = Operand::Constant(box Constant { span: self.span, user_ty: None, - literal: ty::Const::zero_sized(tcx, func_ty), + literal: ty::Const::zero_sized(tcx, func_ty).into(), }); let ref_loc = self.make_place( @@ -463,7 +463,7 @@ impl CloneShimBuilder<'tcx> { let cond = self.make_place(Mutability::Mut, tcx.types.bool); let compute_cond = self.make_statement(StatementKind::Assign(box ( cond, - Rvalue::BinaryOp(BinOp::Ne, Operand::Copy(end), Operand::Copy(beg)), + Rvalue::BinaryOp(BinOp::Ne, box (Operand::Copy(end), Operand::Copy(beg))), ))); // `if end != beg { goto loop_body; } else { goto loop_end; }` @@ -478,7 +478,7 @@ impl CloneShimBuilder<'tcx> { box Constant { span: self.span, user_ty: None, - literal: ty::Const::from_usize(self.tcx, value), + literal: ty::Const::from_usize(self.tcx, value).into(), } } @@ -509,7 +509,7 @@ impl CloneShimBuilder<'tcx> { Rvalue::Use(Operand::Constant(box Constant { span: self.span, user_ty: None, - literal: len, + literal: len.into(), })), ))), ]; @@ -536,8 +536,7 @@ impl CloneShimBuilder<'tcx> { Place::from(beg), Rvalue::BinaryOp( BinOp::Add, - Operand::Copy(Place::from(beg)), - Operand::Constant(self.make_usize(1)), + box (Operand::Copy(Place::from(beg)), Operand::Constant(self.make_usize(1))), ), )))]; self.block(statements, TerminatorKind::Goto { target: BasicBlock::new(1) }, false); @@ -590,8 +589,7 @@ impl CloneShimBuilder<'tcx> { Place::from(beg), Rvalue::BinaryOp( BinOp::Add, - Operand::Copy(Place::from(beg)), - Operand::Constant(self.make_usize(1)), + box (Operand::Copy(Place::from(beg)), Operand::Constant(self.make_usize(1))), ), ))); self.block(vec![statement], TerminatorKind::Goto { target: BasicBlock::new(6) }, true); @@ -770,7 +768,7 @@ fn build_call_shim<'tcx>( Operand::Constant(box Constant { span, user_ty: None, - literal: ty::Const::zero_sized(tcx, ty), + literal: ty::Const::zero_sized(tcx, ty).into(), }), rcvr.into_iter().collect::>(), ) diff --git a/compiler/rustc_mir/src/transform/check_consts/post_drop_elaboration.rs b/compiler/rustc_mir/src/transform/check_consts/post_drop_elaboration.rs index 1a2d932ba1905..057092b8ef574 100644 --- a/compiler/rustc_mir/src/transform/check_consts/post_drop_elaboration.rs +++ b/compiler/rustc_mir/src/transform/check_consts/post_drop_elaboration.rs @@ -79,7 +79,9 @@ impl Visitor<'tcx> for CheckLiveDrops<'mir, 'tcx> { mir::TerminatorKind::Drop { place: dropped_place, .. } => { let dropped_ty = dropped_place.ty(self.body, self.tcx).ty; if !NeedsDrop::in_any_value_of_ty(self.ccx, dropped_ty) { - return; + bug!( + "Drop elaboration left behind a Drop for a type that does not need dropping" + ); } if dropped_place.is_indirect() { @@ -87,6 +89,10 @@ impl Visitor<'tcx> for CheckLiveDrops<'mir, 'tcx> { return; } + // Drop elaboration is not precise enough to accept code like + // `src/test/ui/consts/control-flow/drop-pass.rs`; e.g., when an `Option>` is + // initialized with `None` and never changed, it still emits drop glue. + // Hence we additionally check the qualifs here to allow more code to pass. if self.qualifs.needs_drop(self.ccx, dropped_place.local, location) { // Use the span where the dropped local was declared for the error. let span = self.body.local_decls[dropped_place.local].source_info.span; diff --git a/compiler/rustc_mir/src/transform/check_consts/qualifs.rs b/compiler/rustc_mir/src/transform/check_consts/qualifs.rs index 0ce1980f10a11..ac8c748ea8571 100644 --- a/compiler/rustc_mir/src/transform/check_consts/qualifs.rs +++ b/compiler/rustc_mir/src/transform/check_consts/qualifs.rs @@ -29,11 +29,11 @@ pub fn in_any_value_of_ty( /// Normally, we would determine what qualifications apply to each type and error when an illegal /// operation is performed on such a type. However, this was found to be too imprecise, especially /// in the presence of `enum`s. If only a single variant of an enum has a certain qualification, we -/// needn't reject code unless it actually constructs and operates on the qualifed variant. +/// needn't reject code unless it actually constructs and operates on the qualified variant. /// /// To accomplish this, const-checking and promotion use a value-based analysis (as opposed to a /// type-based one). Qualifications propagate structurally across variables: If a local (or a -/// projection of a local) is assigned a qualifed value, that local itself becomes qualifed. +/// projection of a local) is assigned a qualified value, that local itself becomes qualified. pub trait Qualif { /// The name of the file used to debug the dataflow analysis that computes this qualif. const ANALYSIS_NAME: &'static str; @@ -168,7 +168,7 @@ where | Rvalue::UnaryOp(_, operand) | Rvalue::Cast(_, operand, _) => in_operand::(cx, in_local, operand), - Rvalue::BinaryOp(_, lhs, rhs) | Rvalue::CheckedBinaryOp(_, lhs, rhs) => { + Rvalue::BinaryOp(_, box (lhs, rhs)) | Rvalue::CheckedBinaryOp(_, box (lhs, rhs)) => { in_operand::(cx, in_local, lhs) || in_operand::(cx, in_local, rhs) } @@ -246,25 +246,27 @@ where }; // Check the qualifs of the value of `const` items. - if let ty::ConstKind::Unevaluated(def, _, promoted) = constant.literal.val { - assert!(promoted.is_none()); - // Don't peek inside trait associated constants. - if cx.tcx.trait_of_item(def.did).is_none() { - let qualifs = if let Some((did, param_did)) = def.as_const_arg() { - cx.tcx.at(constant.span).mir_const_qualif_const_arg((did, param_did)) - } else { - cx.tcx.at(constant.span).mir_const_qualif(def.did) - }; - - if !Q::in_qualifs(&qualifs) { - return false; - } + if let Some(ct) = constant.literal.const_for_ty() { + if let ty::ConstKind::Unevaluated(ty::Unevaluated { def, substs: _, promoted }) = ct.val { + assert!(promoted.is_none()); + // Don't peek inside trait associated constants. + if cx.tcx.trait_of_item(def.did).is_none() { + let qualifs = if let Some((did, param_did)) = def.as_const_arg() { + cx.tcx.at(constant.span).mir_const_qualif_const_arg((did, param_did)) + } else { + cx.tcx.at(constant.span).mir_const_qualif(def.did) + }; + + if !Q::in_qualifs(&qualifs) { + return false; + } - // Just in case the type is more specific than - // the definition, e.g., impl associated const - // with type parameters, take it into account. + // Just in case the type is more specific than + // the definition, e.g., impl associated const + // with type parameters, take it into account. + } } } // Otherwise use the qualifs of the type. - Q::in_any_value_of_ty(cx, constant.literal.ty) + Q::in_any_value_of_ty(cx, constant.literal.ty()) } diff --git a/compiler/rustc_mir/src/transform/check_consts/validation.rs b/compiler/rustc_mir/src/transform/check_consts/validation.rs index baa27e9499fef..ce5e41d3e7c86 100644 --- a/compiler/rustc_mir/src/transform/check_consts/validation.rs +++ b/compiler/rustc_mir/src/transform/check_consts/validation.rs @@ -684,8 +684,8 @@ impl Visitor<'tcx> for Validator<'mir, 'tcx> { } } - Rvalue::BinaryOp(op, ref lhs, ref rhs) - | Rvalue::CheckedBinaryOp(op, ref lhs, ref rhs) => { + Rvalue::BinaryOp(op, box (ref lhs, ref rhs)) + | Rvalue::CheckedBinaryOp(op, box (ref lhs, ref rhs)) => { let lhs_ty = lhs.ty(self.body, self.tcx); let rhs_ty = rhs.ty(self.body, self.tcx); @@ -808,6 +808,7 @@ impl Visitor<'tcx> for Validator<'mir, 'tcx> { | StatementKind::Retag { .. } | StatementKind::AscribeUserType(..) | StatementKind::Coverage(..) + | StatementKind::CopyNonOverlapping(..) | StatementKind::Nop => {} } } @@ -849,9 +850,12 @@ impl Visitor<'tcx> for Validator<'mir, 'tcx> { let obligation = Obligation::new( ObligationCause::dummy(), param_env, - Binder::bind(TraitPredicate { - trait_ref: TraitRef::from_method(tcx, trait_id, substs), - }), + Binder::bind( + TraitPredicate { + trait_ref: TraitRef::from_method(tcx, trait_id, substs), + }, + tcx, + ), ); let implsrc = tcx.infer_ctxt().enter(|infcx| { diff --git a/compiler/rustc_mir/src/transform/check_packed_ref.rs b/compiler/rustc_mir/src/transform/check_packed_ref.rs index ee88daa83e783..13b7221046bda 100644 --- a/compiler/rustc_mir/src/transform/check_packed_ref.rs +++ b/compiler/rustc_mir/src/transform/check_packed_ref.rs @@ -1,11 +1,18 @@ +use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_middle::mir::visit::{PlaceContext, Visitor}; use rustc_middle::mir::*; +use rustc_middle::ty::query::Providers; use rustc_middle::ty::{self, TyCtxt}; use rustc_session::lint::builtin::UNALIGNED_REFERENCES; +use rustc_span::symbol::sym; use crate::transform::MirPass; use crate::util; +pub(crate) fn provide(providers: &mut Providers) { + *providers = Providers { unsafe_derive_on_repr_packed, ..*providers }; +} + pub struct CheckPackedRef; impl<'tcx> MirPass<'tcx> for CheckPackedRef { @@ -24,6 +31,41 @@ struct PackedRefChecker<'a, 'tcx> { source_info: SourceInfo, } +fn unsafe_derive_on_repr_packed(tcx: TyCtxt<'_>, def_id: LocalDefId) { + let lint_hir_id = tcx.hir().local_def_id_to_hir_id(def_id); + + tcx.struct_span_lint_hir(UNALIGNED_REFERENCES, lint_hir_id, tcx.def_span(def_id), |lint| { + // FIXME: when we make this a hard error, this should have its + // own error code. + let message = if tcx.generics_of(def_id).own_requires_monomorphization() { + "`#[derive]` can't be used on a `#[repr(packed)]` struct with \ + type or const parameters (error E0133)" + .to_string() + } else { + "`#[derive]` can't be used on a `#[repr(packed)]` struct that \ + does not derive Copy (error E0133)" + .to_string() + }; + lint.build(&message).emit() + }); +} + +fn builtin_derive_def_id(tcx: TyCtxt<'_>, def_id: DefId) -> Option { + debug!("builtin_derive_def_id({:?})", def_id); + if let Some(impl_def_id) = tcx.impl_of_method(def_id) { + if tcx.has_attr(impl_def_id, sym::automatically_derived) { + debug!("builtin_derive_def_id({:?}) - is {:?}", def_id, impl_def_id); + Some(impl_def_id) + } else { + debug!("builtin_derive_def_id({:?}) - not automatically derived", def_id); + None + } + } else { + debug!("builtin_derive_def_id({:?}) - not a method", def_id); + None + } +} + impl<'a, 'tcx> Visitor<'tcx> for PackedRefChecker<'a, 'tcx> { fn visit_terminator(&mut self, terminator: &Terminator<'tcx>, location: Location) { // Make sure we know where in the MIR we are. @@ -40,26 +82,33 @@ impl<'a, 'tcx> Visitor<'tcx> for PackedRefChecker<'a, 'tcx> { fn visit_place(&mut self, place: &Place<'tcx>, context: PlaceContext, _location: Location) { if context.is_borrow() { if util::is_disaligned(self.tcx, self.body, self.param_env, *place) { - let source_info = self.source_info; - let lint_root = self.body.source_scopes[source_info.scope] - .local_data - .as_ref() - .assert_crate_local() - .lint_root; - self.tcx.struct_span_lint_hir( - UNALIGNED_REFERENCES, - lint_root, - source_info.span, - |lint| { - lint.build("reference to packed field is unaligned") - .note( - "fields of packed structs are not properly aligned, and creating \ - a misaligned reference is undefined behavior (even if that \ - reference is never dereferenced)", - ) - .emit() - }, - ); + let def_id = self.body.source.instance.def_id(); + if let Some(impl_def_id) = builtin_derive_def_id(self.tcx, def_id) { + // If a method is defined in the local crate, + // the impl containing that method should also be. + self.tcx.ensure().unsafe_derive_on_repr_packed(impl_def_id.expect_local()); + } else { + let source_info = self.source_info; + let lint_root = self.body.source_scopes[source_info.scope] + .local_data + .as_ref() + .assert_crate_local() + .lint_root; + self.tcx.struct_span_lint_hir( + UNALIGNED_REFERENCES, + lint_root, + source_info.span, + |lint| { + lint.build("reference to packed field is unaligned") + .note( + "fields of packed structs are not properly aligned, and creating \ + a misaligned reference is undefined behavior (even if that \ + reference is never dereferenced)", + ) + .emit() + }, + ); + } } } } diff --git a/compiler/rustc_mir/src/transform/check_unsafety.rs b/compiler/rustc_mir/src/transform/check_unsafety.rs index f0472758dfb8e..09da9b2e4d6ff 100644 --- a/compiler/rustc_mir/src/transform/check_unsafety.rs +++ b/compiler/rustc_mir/src/transform/check_unsafety.rs @@ -10,14 +10,12 @@ use rustc_middle::mir::*; use rustc_middle::ty::cast::CastTy; use rustc_middle::ty::query::Providers; use rustc_middle::ty::{self, TyCtxt}; -use rustc_session::lint::builtin::{SAFE_PACKED_BORROWS, UNSAFE_OP_IN_UNSAFE_FN, UNUSED_UNSAFE}; +use rustc_session::lint::builtin::{UNSAFE_OP_IN_UNSAFE_FN, UNUSED_UNSAFE}; use rustc_session::lint::Level; -use rustc_span::symbol::sym; use std::ops::Bound; use crate::const_eval::is_min_const_fn; -use crate::util; pub struct UnsafetyChecker<'a, 'tcx> { body: &'a Body<'tcx>, @@ -123,6 +121,7 @@ impl<'a, 'tcx> Visitor<'tcx> for UnsafetyChecker<'a, 'tcx> { UnsafetyViolationKind::General, UnsafetyViolationDetails::UseOfInlineAssembly, ), + StatementKind::CopyNonOverlapping(..) => unreachable!(), } self.super_statement(statement, location); } @@ -181,18 +180,6 @@ impl<'a, 'tcx> Visitor<'tcx> for UnsafetyChecker<'a, 'tcx> { self.check_mut_borrowing_layout_constrained_field(*place, context.is_mutating_use()); } - // Check for borrows to packed fields. - // `is_disaligned` already traverses the place to consider all projections after the last - // `Deref`, so this only needs to be called once at the top level. - if context.is_borrow() { - if util::is_disaligned(self.tcx, self.body, self.param_env, *place) { - self.require_unsafe( - UnsafetyViolationKind::BorrowPacked, - UnsafetyViolationDetails::BorrowOfPackedField, - ); - } - } - // Some checks below need the extra metainfo of the local declaration. let decl = &self.body.local_decls[place.local]; @@ -316,47 +303,32 @@ impl<'a, 'tcx> UnsafetyChecker<'a, 'tcx> { // `unsafe` blocks are required in safe code Safety::Safe => { for violation in violations { - let mut violation = *violation; match violation.kind { UnsafetyViolationKind::GeneralAndConstFn | UnsafetyViolationKind::General => {} - UnsafetyViolationKind::BorrowPacked => { - if self.min_const_fn { - // const fns don't need to be backwards compatible and can - // emit these violations as a hard error instead of a backwards - // compat lint - violation.kind = UnsafetyViolationKind::General; - } - } - UnsafetyViolationKind::UnsafeFn - | UnsafetyViolationKind::UnsafeFnBorrowPacked => { + UnsafetyViolationKind::UnsafeFn => { bug!("`UnsafetyViolationKind::UnsafeFn` in an `Safe` context") } } - if !self.violations.contains(&violation) { - self.violations.push(violation) + if !self.violations.contains(violation) { + self.violations.push(*violation) } } false } // With the RFC 2585, no longer allow `unsafe` operations in `unsafe fn`s - Safety::FnUnsafe if self.tcx.features().unsafe_block_in_unsafe_fn => { + Safety::FnUnsafe => { for violation in violations { let mut violation = *violation; - if violation.kind == UnsafetyViolationKind::BorrowPacked { - violation.kind = UnsafetyViolationKind::UnsafeFnBorrowPacked; - } else { - violation.kind = UnsafetyViolationKind::UnsafeFn; - } + violation.kind = UnsafetyViolationKind::UnsafeFn; if !self.violations.contains(&violation) { self.violations.push(violation) } } false } - // `unsafe` function bodies allow unsafe without additional unsafe blocks (before RFC 2585) - Safety::BuiltinUnsafe | Safety::FnUnsafe => true, + Safety::BuiltinUnsafe => true, Safety::ExplicitUnsafe(hir_id) => { // mark unsafe block as used if there are any unsafe operations inside if !violations.is_empty() { @@ -369,8 +341,7 @@ impl<'a, 'tcx> UnsafetyChecker<'a, 'tcx> { // these unsafe things are stable in const fn UnsafetyViolationKind::GeneralAndConstFn => {} // these things are forbidden in const fns - UnsafetyViolationKind::General - | UnsafetyViolationKind::BorrowPacked => { + UnsafetyViolationKind::General => { let mut violation = *violation; // const fns don't need to be backwards compatible and can // emit these violations as a hard error instead of a backwards @@ -380,8 +351,7 @@ impl<'a, 'tcx> UnsafetyChecker<'a, 'tcx> { self.violations.push(violation) } } - UnsafetyViolationKind::UnsafeFn - | UnsafetyViolationKind::UnsafeFnBorrowPacked => bug!( + UnsafetyViolationKind::UnsafeFn => bug!( "`UnsafetyViolationKind::UnsafeFn` in an `ExplicitUnsafe` context" ), } @@ -464,7 +434,6 @@ pub(crate) fn provide(providers: &mut Providers) { ty::WithOptConstParam { did, const_param_did: Some(param_did) }, ) }, - unsafe_derive_on_repr_packed, ..*providers }; } @@ -544,25 +513,6 @@ fn unsafety_check_result<'tcx>( }) } -fn unsafe_derive_on_repr_packed(tcx: TyCtxt<'_>, def_id: LocalDefId) { - let lint_hir_id = tcx.hir().local_def_id_to_hir_id(def_id); - - tcx.struct_span_lint_hir(SAFE_PACKED_BORROWS, lint_hir_id, tcx.def_span(def_id), |lint| { - // FIXME: when we make this a hard error, this should have its - // own error code. - let message = if tcx.generics_of(def_id).own_requires_monomorphization() { - "`#[derive]` can't be used on a `#[repr(packed)]` struct with \ - type or const parameters (error E0133)" - .to_string() - } else { - "`#[derive]` can't be used on a `#[repr(packed)]` struct that \ - does not derive Copy (error E0133)" - .to_string() - }; - lint.build(&message).emit() - }); -} - /// Returns the `HirId` for an enclosing scope that is also `unsafe`. fn is_enclosed( tcx: TyCtxt<'_>, @@ -609,22 +559,6 @@ fn report_unused_unsafe(tcx: TyCtxt<'_>, used_unsafe: &FxHashSet, id }); } -fn builtin_derive_def_id(tcx: TyCtxt<'_>, def_id: DefId) -> Option { - debug!("builtin_derive_def_id({:?})", def_id); - if let Some(impl_def_id) = tcx.impl_of_method(def_id) { - if tcx.has_attr(impl_def_id, sym::automatically_derived) { - debug!("builtin_derive_def_id({:?}) - is {:?}", def_id, impl_def_id); - Some(impl_def_id) - } else { - debug!("builtin_derive_def_id({:?}) - not automatically derived", def_id); - None - } - } else { - debug!("builtin_derive_def_id({:?}) - not a method", def_id); - None - } -} - pub fn check_unsafety(tcx: TyCtxt<'_>, def_id: LocalDefId) { debug!("check_unsafety({:?})", def_id); @@ -657,27 +591,6 @@ pub fn check_unsafety(tcx: TyCtxt<'_>, def_id: LocalDefId) { .note(note) .emit(); } - UnsafetyViolationKind::BorrowPacked => { - if let Some(impl_def_id) = builtin_derive_def_id(tcx, def_id.to_def_id()) { - // If a method is defined in the local crate, - // the impl containing that method should also be. - tcx.ensure().unsafe_derive_on_repr_packed(impl_def_id.expect_local()); - } else { - tcx.struct_span_lint_hir( - SAFE_PACKED_BORROWS, - lint_root, - source_info.span, - |lint| { - lint.build(&format!( - "{} is unsafe and requires unsafe{} block (error E0133)", - description, unsafe_fn_msg, - )) - .note(note) - .emit() - }, - ) - } - } UnsafetyViolationKind::UnsafeFn => tcx.struct_span_lint_hir( UNSAFE_OP_IN_UNSAFE_FN, lint_root, @@ -692,35 +605,6 @@ pub fn check_unsafety(tcx: TyCtxt<'_>, def_id: LocalDefId) { .emit(); }, ), - UnsafetyViolationKind::UnsafeFnBorrowPacked => { - // When `unsafe_op_in_unsafe_fn` is disallowed, the behavior of safe and unsafe functions - // should be the same in terms of warnings and errors. Therefore, with `#[warn(safe_packed_borrows)]`, - // a safe packed borrow should emit a warning *but not an error* in an unsafe function, - // just like in a safe function, even if `unsafe_op_in_unsafe_fn` is `deny`. - // - // Also, `#[warn(unsafe_op_in_unsafe_fn)]` can't cause any new errors. Therefore, with - // `#[deny(safe_packed_borrows)]` and `#[warn(unsafe_op_in_unsafe_fn)]`, a packed borrow - // should only issue a warning for the sake of backwards compatibility. - // - // The solution those 2 expectations is to always take the minimum of both lints. - // This prevent any new errors (unless both lints are explicitly set to `deny`). - let lint = if tcx.lint_level_at_node(SAFE_PACKED_BORROWS, lint_root).0 - <= tcx.lint_level_at_node(UNSAFE_OP_IN_UNSAFE_FN, lint_root).0 - { - SAFE_PACKED_BORROWS - } else { - UNSAFE_OP_IN_UNSAFE_FN - }; - tcx.struct_span_lint_hir(&lint, lint_root, source_info.span, |lint| { - lint.build(&format!( - "{} is unsafe and requires unsafe block (error E0133)", - description, - )) - .span_label(source_info.span, description) - .note(note) - .emit(); - }) - } } } diff --git a/compiler/rustc_mir/src/transform/const_goto.rs b/compiler/rustc_mir/src/transform/const_goto.rs index 3eb2e757644c4..b5c8b4bebc360 100644 --- a/compiler/rustc_mir/src/transform/const_goto.rs +++ b/compiler/rustc_mir/src/transform/const_goto.rs @@ -28,7 +28,7 @@ pub struct ConstGoto; impl<'tcx> MirPass<'tcx> for ConstGoto { fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { - if tcx.sess.opts.debugging_opts.mir_opt_level < 3 { + if tcx.sess.mir_opt_level() < 4 { return; } trace!("Running ConstGoto on {:?}", body.source); diff --git a/compiler/rustc_mir/src/transform/const_prop.rs b/compiler/rustc_mir/src/transform/const_prop.rs index 2a0e5e2c9c5d2..7706316c96516 100644 --- a/compiler/rustc_mir/src/transform/const_prop.rs +++ b/compiler/rustc_mir/src/transform/const_prop.rs @@ -13,9 +13,9 @@ use rustc_middle::mir::visit::{ MutVisitor, MutatingUseContext, NonMutatingUseContext, PlaceContext, Visitor, }; use rustc_middle::mir::{ - AssertKind, BasicBlock, BinOp, Body, ClearCrossCrate, Constant, Local, LocalDecl, LocalKind, - Location, Operand, Place, Rvalue, SourceInfo, SourceScope, SourceScopeData, Statement, - StatementKind, Terminator, TerminatorKind, UnOp, RETURN_PLACE, + AssertKind, BasicBlock, BinOp, Body, ClearCrossCrate, Constant, ConstantKind, Local, LocalDecl, + LocalKind, Location, Operand, Place, Rvalue, SourceInfo, SourceScope, SourceScopeData, + Statement, StatementKind, Terminator, TerminatorKind, UnOp, RETURN_PLACE, }; use rustc_middle::ty::layout::{HasTyCtxt, LayoutError, TyAndLayout}; use rustc_middle::ty::subst::{InternalSubsts, Subst}; @@ -482,18 +482,25 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> { return None; } - match self.ecx.const_to_op(c.literal, None) { + match self.ecx.mir_const_to_op(&c.literal, None) { Ok(op) => Some(op), Err(error) => { let tcx = self.ecx.tcx.at(c.span); let err = ConstEvalErr::new(&self.ecx, error, Some(c.span)); if let Some(lint_root) = self.lint_root(source_info) { - let lint_only = match c.literal.val { - // Promoteds must lint and not error as the user didn't ask for them - ConstKind::Unevaluated(_, _, Some(_)) => true, - // Out of backwards compatibility we cannot report hard errors in unused - // generic functions using associated constants of the generic parameters. - _ => c.literal.needs_subst(), + let lint_only = match c.literal { + ConstantKind::Ty(ct) => match ct.val { + // Promoteds must lint and not error as the user didn't ask for them + ConstKind::Unevaluated(ty::Unevaluated { + def: _, + substs: _, + promoted: Some(_), + }) => true, + // Out of backwards compatibility we cannot report hard errors in unused + // generic functions using associated constants of the generic parameters. + _ => c.literal.needs_subst(), + }, + ConstantKind::Val(_, ty) => ty.needs_subst(), }; if lint_only { // Out of backwards compatibility we cannot report hard errors in unused @@ -676,11 +683,11 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> { trace!("checking UnaryOp(op = {:?}, arg = {:?})", op, arg); self.check_unary_op(*op, arg, source_info)?; } - Rvalue::BinaryOp(op, left, right) => { + Rvalue::BinaryOp(op, box (left, right)) => { trace!("checking BinaryOp(op = {:?}, left = {:?}, right = {:?})", op, left, right); self.check_binary_op(*op, left, right, source_info)?; } - Rvalue::CheckedBinaryOp(op, left, right) => { + Rvalue::CheckedBinaryOp(op, box (left, right)) => { trace!( "checking CheckedBinaryOp(op = {:?}, left = {:?}, right = {:?})", op, @@ -725,7 +732,7 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> { return None; } - if self.tcx.sess.opts.debugging_opts.mir_opt_level >= 3 { + if self.tcx.sess.mir_opt_level() >= 4 { self.eval_rvalue_with_identities(rvalue, place) } else { self.use_ecx(|this| this.ecx.eval_rvalue_into_place(rvalue, place)) @@ -740,7 +747,8 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> { ) -> Option<()> { self.use_ecx(|this| { match rvalue { - Rvalue::BinaryOp(op, left, right) | Rvalue::CheckedBinaryOp(op, left, right) => { + Rvalue::BinaryOp(op, box (left, right)) + | Rvalue::CheckedBinaryOp(op, box (left, right)) => { let l = this.ecx.eval_operand(left, None); let r = this.ecx.eval_operand(right, None); @@ -772,7 +780,7 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> { } BinOp::Mul => { if const_arg.layout.ty.is_integral() && arg_value == 0 { - if let Rvalue::CheckedBinaryOp(_, _, _) = rvalue { + if let Rvalue::CheckedBinaryOp(_, _) = rvalue { let val = Immediate::ScalarPair( const_arg.to_scalar()?.into(), Scalar::from_bool(false).into(), @@ -802,7 +810,7 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> { Operand::Constant(Box::new(Constant { span, user_ty: None, - literal: ty::Const::from_scalar(self.tcx, scalar, ty), + literal: ty::Const::from_scalar(self.tcx, scalar, ty).into(), })) } @@ -813,9 +821,12 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> { source_info: SourceInfo, ) { if let Rvalue::Use(Operand::Constant(c)) = rval { - if !matches!(c.literal.val, ConstKind::Unevaluated(..)) { - trace!("skipping replace of Rvalue::Use({:?} because it is already a const", c); - return; + match c.literal { + ConstantKind::Ty(c) if matches!(c.val, ConstKind::Unevaluated(..)) => {} + _ => { + trace!("skipping replace of Rvalue::Use({:?} because it is already a const", c); + return; + } } } @@ -882,13 +893,17 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> { *rval = Rvalue::Use(Operand::Constant(Box::new(Constant { span: source_info.span, user_ty: None, - literal: self.ecx.tcx.mk_const(ty::Const { - ty, - val: ty::ConstKind::Value(ConstValue::ByRef { - alloc, - offset: Size::ZERO, - }), - }), + literal: self + .ecx + .tcx + .mk_const(ty::Const { + ty, + val: ty::ConstKind::Value(ConstValue::ByRef { + alloc, + offset: Size::ZERO, + }), + }) + .into(), }))); } } @@ -903,7 +918,7 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> { /// Returns `true` if and only if this `op` should be const-propagated into. fn should_const_prop(&mut self, op: &OpTy<'tcx>) -> bool { - let mir_opt_level = self.tcx.sess.opts.debugging_opts.mir_opt_level; + let mir_opt_level = self.tcx.sess.mir_opt_level(); if mir_opt_level == 0 { return false; @@ -1071,9 +1086,9 @@ impl<'mir, 'tcx> MutVisitor<'tcx> for ConstPropagator<'mir, 'tcx> { fn visit_operand(&mut self, operand: &mut Operand<'tcx>, location: Location) { self.super_operand(operand, location); - // Only const prop copies and moves on `mir_opt_level=2` as doing so + // Only const prop copies and moves on `mir_opt_level=3` as doing so // currently slightly increases compile time in some cases. - if self.tcx.sess.opts.debugging_opts.mir_opt_level >= 2 { + if self.tcx.sess.mir_opt_level() >= 3 { self.propagate_operand(operand) } } @@ -1253,7 +1268,7 @@ impl<'mir, 'tcx> MutVisitor<'tcx> for ConstPropagator<'mir, 'tcx> { TerminatorKind::SwitchInt { ref mut discr, .. } => { // FIXME: This is currently redundant with `visit_operand`, but sadly // always visiting operands currently causes a perf regression in LLVM codegen, so - // `visit_operand` currently only runs for propagates places for `mir_opt_level=3`. + // `visit_operand` currently only runs for propagates places for `mir_opt_level=4`. self.propagate_operand(discr) } // None of these have Operands to const-propagate. @@ -1272,7 +1287,7 @@ impl<'mir, 'tcx> MutVisitor<'tcx> for ConstPropagator<'mir, 'tcx> { // Every argument in our function calls have already been propagated in `visit_operand`. // // NOTE: because LLVM codegen gives slight performance regressions with it, so this is - // gated on `mir_opt_level=2`. + // gated on `mir_opt_level=3`. TerminatorKind::Call { .. } => {} } diff --git a/compiler/rustc_mir/src/transform/coverage/debug.rs b/compiler/rustc_mir/src/transform/coverage/debug.rs index 2cd0dc6b1f2fd..48361483099f2 100644 --- a/compiler/rustc_mir/src/transform/coverage/debug.rs +++ b/compiler/rustc_mir/src/transform/coverage/debug.rs @@ -121,6 +121,7 @@ use rustc_middle::mir::coverage::*; use rustc_middle::mir::{self, BasicBlock, TerminatorKind}; use rustc_middle::ty::TyCtxt; +use std::iter; use std::lazy::SyncOnceCell; pub const NESTED_INDENT: &str = " "; @@ -285,10 +286,8 @@ impl DebugCounters { ), }; counters - .insert(id, DebugCounter::new(counter_kind.clone(), some_block_label)) - .expect_none( - "attempt to add the same counter_kind to DebugCounters more than once", - ); + .try_insert(id, DebugCounter::new(counter_kind.clone(), some_block_label)) + .expect("attempt to add the same counter_kind to DebugCounters more than once"); } } @@ -479,9 +478,9 @@ impl GraphvizData { counter_kind: &CoverageKind, ) { if let Some(edge_to_counter) = self.some_edge_to_counter.as_mut() { - edge_to_counter.insert((from_bcb, to_bb), counter_kind.clone()).expect_none( - "invalid attempt to insert more than one edge counter for the same edge", - ); + edge_to_counter + .try_insert((from_bcb, to_bb), counter_kind.clone()) + .expect("invalid attempt to insert more than one edge counter for the same edge"); } } @@ -705,9 +704,7 @@ pub(super) fn dump_coverage_graphviz( let edge_counters = from_terminator .successors() .map(|&successor_bb| graphviz_data.get_edge_counter(from_bcb, successor_bb)); - edge_labels - .iter() - .zip(edge_counters) + iter::zip(&edge_labels, edge_counters) .map(|(label, some_counter)| { if let Some(counter) = some_counter { format!("{}\n{}", label, debug_counters.format_counter(counter)) @@ -819,7 +816,7 @@ fn bcb_to_string_sections( sections } -/// Returns a simple string representation of a `TerminatorKind` variant, indenpendent of any +/// Returns a simple string representation of a `TerminatorKind` variant, independent of any /// values it might hold. pub(super) fn term_type(kind: &TerminatorKind<'tcx>) -> &'static str { match kind { diff --git a/compiler/rustc_mir/src/transform/coverage/graph.rs b/compiler/rustc_mir/src/transform/coverage/graph.rs index e58b915f1264c..6f5fa858e2537 100644 --- a/compiler/rustc_mir/src/transform/coverage/graph.rs +++ b/compiler/rustc_mir/src/transform/coverage/graph.rs @@ -392,10 +392,8 @@ impl BasicCoverageBlockData { } } let operand = counter_kind.as_operand_id(); - if let Some(replaced) = self - .edge_from_bcbs - .get_or_insert_with(FxHashMap::default) - .insert(from_bcb, counter_kind) + if let Some(replaced) = + self.edge_from_bcbs.get_or_insert_default().insert(from_bcb, counter_kind) { Error::from_string(format!( "attempt to set an edge counter more than once; from_bcb: \ diff --git a/compiler/rustc_mir/src/transform/coverage/mod.rs b/compiler/rustc_mir/src/transform/coverage/mod.rs index 93133e9b7f063..60757178bec0b 100644 --- a/compiler/rustc_mir/src/transform/coverage/mod.rs +++ b/compiler/rustc_mir/src/transform/coverage/mod.rs @@ -111,7 +111,8 @@ impl<'a, 'tcx> Instrumentor<'a, 'tcx> { let body_span = hir_body.value.span; let source_file = source_map.lookup_source_file(body_span.lo()); let fn_sig_span = match some_fn_sig.filter(|fn_sig| { - Lrc::ptr_eq(&source_file, &source_map.lookup_source_file(fn_sig.span.hi())) + fn_sig.span.ctxt() == body_span.ctxt() + && Lrc::ptr_eq(&source_file, &source_map.lookup_source_file(fn_sig.span.hi())) }) { Some(fn_sig) => fn_sig.span.with_hi(body_span.lo()), None => body_span.shrink_to_lo(), diff --git a/compiler/rustc_mir/src/transform/coverage/query.rs b/compiler/rustc_mir/src/transform/coverage/query.rs index 4b455a6a1ba72..2ba9d1bdc0c00 100644 --- a/compiler/rustc_mir/src/transform/coverage/query.rs +++ b/compiler/rustc_mir/src/transform/coverage/query.rs @@ -1,16 +1,14 @@ use super::*; use rustc_middle::mir::coverage::*; -use rustc_middle::mir::visit::Visitor; -use rustc_middle::mir::{self, Coverage, CoverageInfo, Location}; +use rustc_middle::mir::{self, Body, Coverage, CoverageInfo}; use rustc_middle::ty::query::Providers; use rustc_middle::ty::{self, TyCtxt}; use rustc_span::def_id::DefId; -/// The `query` provider for `CoverageInfo`, requested by `codegen_coverage()` (to inject each -/// counter) and `FunctionCoverage::new()` (to extract the coverage map metadata from the MIR). +/// A `query` provider for retrieving coverage information injected into MIR. pub(crate) fn provide(providers: &mut Providers) { - providers.coverageinfo = |tcx, def_id| coverageinfo_from_mir(tcx, def_id); + 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); } @@ -85,10 +83,21 @@ impl CoverageVisitor { } } } -} -impl Visitor<'_> for CoverageVisitor { - fn visit_coverage(&mut self, coverage: &Coverage, _location: Location) { + fn visit_body(&mut self, body: &Body<'_>) { + 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 is_inlined(body, statement) { + continue; + } + self.visit_coverage(coverage); + } + } + } + } + + fn visit_coverage(&mut self, coverage: &Coverage) { if self.add_missing_operands { match coverage.kind { CoverageKind::Expression { lhs, rhs, .. } => { @@ -111,7 +120,7 @@ impl Visitor<'_> for CoverageVisitor { } } -fn coverageinfo_from_mir<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId) -> CoverageInfo { +fn coverageinfo<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId) -> CoverageInfo { let mir_body = mir_body(tcx, def_id); let mut coverage_visitor = CoverageVisitor { @@ -129,35 +138,36 @@ fn coverageinfo_from_mir<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId) -> CoverageInfo } fn covered_file_name<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId) -> Option { - for bb_data in mir_body(tcx, def_id).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() { - return Some(code_region.file_name); + 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); + } } } } } - None -} - -/// This function ensures we obtain the correct MIR for the given item irrespective of -/// whether that means const mir or runtime mir. For `const fn` this opts for runtime -/// mir. -fn mir_body<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId) -> &'tcx mir::Body<'tcx> { - let id = ty::WithOptConstParam::unknown(def_id); - let def = ty::InstanceDef::Item(id); - tcx.instance_mir(def) + return None; } fn covered_code_regions<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId) -> Vec<&'tcx CodeRegion> { - mir_body(tcx, def_id) - .basic_blocks() + let body = mir_body(tcx, def_id); + body.basic_blocks() .iter() .map(|data| { data.statements.iter().filter_map(|statement| match statement.kind { StatementKind::Coverage(box ref coverage) => { - coverage.code_region.as_ref() // may be None + if is_inlined(body, statement) { + None + } else { + coverage.code_region.as_ref() // may be None + } } _ => None, }) @@ -165,3 +175,17 @@ fn covered_code_regions<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId) -> Vec<&'tcx Cod .flatten() .collect() } + +fn is_inlined(body: &Body<'_>, statement: &Statement<'_>) -> bool { + let scope_data = &body.source_scopes[statement.source_info.scope]; + scope_data.inlined.is_some() || scope_data.inlined_parent_scope.is_some() +} + +/// This function ensures we obtain the correct MIR for the given item irrespective of +/// whether that means const mir or runtime mir. For `const fn` this opts for runtime +/// mir. +fn mir_body<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId) -> &'tcx mir::Body<'tcx> { + let id = ty::WithOptConstParam::unknown(def_id); + let def = ty::InstanceDef::Item(id); + tcx.instance_mir(def) +} diff --git a/compiler/rustc_mir/src/transform/coverage/spans.rs b/compiler/rustc_mir/src/transform/coverage/spans.rs index fd3e782f6df43..249f5e835cd78 100644 --- a/compiler/rustc_mir/src/transform/coverage/spans.rs +++ b/compiler/rustc_mir/src/transform/coverage/spans.rs @@ -11,7 +11,7 @@ use rustc_middle::mir::{ use rustc_middle::ty::TyCtxt; use rustc_span::source_map::original_sp; -use rustc_span::{BytePos, Span, SyntaxContext}; +use rustc_span::{BytePos, Span}; use std::cmp::Ordering; @@ -240,7 +240,7 @@ impl<'a, 'tcx> CoverageSpans<'a, 'tcx> { /// to be). pub(super) fn generate_coverage_spans( mir_body: &'a mir::Body<'tcx>, - fn_sig_span: Span, + fn_sig_span: Span, // Ensured to be same SourceFile and SyntaxContext as `body_span` body_span: Span, basic_coverage_blocks: &'a CoverageGraph, ) -> Vec { @@ -683,10 +683,11 @@ pub(super) fn filtered_statement_span( // and `_1` is the `Place` for `somenum`. // // If and when the Issue is resolved, remove this special case match pattern: - StatementKind::FakeRead(cause, _) if cause == FakeReadCause::ForGuardBinding => None, + StatementKind::FakeRead(box (cause, _)) if cause == FakeReadCause::ForGuardBinding => None, // Retain spans from all other statements - StatementKind::FakeRead(_, _) // Not including `ForGuardBinding` + StatementKind::FakeRead(box (_, _)) // Not including `ForGuardBinding` + | StatementKind::CopyNonOverlapping(..) | StatementKind::Assign(_) | StatementKind::SetDiscriminant { .. } | StatementKind::LlvmInlineAsm(_) @@ -732,6 +733,6 @@ pub(super) fn filtered_terminator_span( #[inline] fn function_source_span(span: Span, body_span: Span) -> Span { - let span = original_sp(span, body_span).with_ctxt(SyntaxContext::root()); + let span = original_sp(span, body_span).with_ctxt(body_span.ctxt()); if body_span.contains(span) { span } else { body_span } } diff --git a/compiler/rustc_mir/src/transform/coverage/tests.rs b/compiler/rustc_mir/src/transform/coverage/tests.rs index 7a9bfaad88367..dee112443d337 100644 --- a/compiler/rustc_mir/src/transform/coverage/tests.rs +++ b/compiler/rustc_mir/src/transform/coverage/tests.rs @@ -17,7 +17,7 @@ //! Also note, some basic features of `Span` also rely on the `Span`s own "session globals", which //! are unrelated to the `TyCtxt` global. Without initializing the `Span` session globals, some //! basic, coverage-specific features would be impossible to test, but thankfully initializing these -//! globals is comparitively simpler. The easiest way is to wrap the test in a closure argument +//! globals is comparatively simpler. The easiest way is to wrap the test in a closure argument //! to: `rustc_span::with_default_session_globals(|| { test_here(); })`. use super::counters; diff --git a/compiler/rustc_mir/src/transform/deduplicate_blocks.rs b/compiler/rustc_mir/src/transform/deduplicate_blocks.rs index 5f09159e91b20..c41e71e09a4ef 100644 --- a/compiler/rustc_mir/src/transform/deduplicate_blocks.rs +++ b/compiler/rustc_mir/src/transform/deduplicate_blocks.rs @@ -1,7 +1,7 @@ //! This pass finds basic blocks that are completely equal, //! and replaces all uses with just one of them. -use std::{collections::hash_map::Entry, hash::Hash, hash::Hasher}; +use std::{collections::hash_map::Entry, hash::Hash, hash::Hasher, iter}; use crate::transform::MirPass; @@ -16,7 +16,7 @@ pub struct DeduplicateBlocks; impl<'tcx> MirPass<'tcx> for DeduplicateBlocks { fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { - if tcx.sess.opts.debugging_opts.mir_opt_level < 3 { + if tcx.sess.mir_opt_level() < 4 { return; } debug!("Running DeduplicateBlocks on `{:?}`", body.source); @@ -86,7 +86,7 @@ fn find_duplicates<'a, 'tcx>(body: &'a Body<'tcx>) -> FxHashMap {:?}", bb, value); - duplicates.insert(bb, value).expect_none("key was already inserted"); + duplicates.try_insert(bb, value).expect("key was already inserted"); } Entry::Vacant(vacant) => { vacant.insert(bb); @@ -115,11 +115,7 @@ impl<'tcx, 'a> PartialEq for BasicBlockHashable<'tcx, 'a> { fn eq(&self, other: &Self) -> bool { self.basic_block_data.statements.len() == other.basic_block_data.statements.len() && &self.basic_block_data.terminator().kind == &other.basic_block_data.terminator().kind - && self - .basic_block_data - .statements - .iter() - .zip(&other.basic_block_data.statements) + && iter::zip(&self.basic_block_data.statements, &other.basic_block_data.statements) .all(|(x, y)| statement_eq(&x.kind, &y.kind)) } } diff --git a/compiler/rustc_mir/src/transform/dest_prop.rs b/compiler/rustc_mir/src/transform/dest_prop.rs index 46de5dba6e0ed..29df86ca6cdb7 100644 --- a/compiler/rustc_mir/src/transform/dest_prop.rs +++ b/compiler/rustc_mir/src/transform/dest_prop.rs @@ -127,9 +127,14 @@ pub struct DestinationPropagation; impl<'tcx> MirPass<'tcx> for DestinationPropagation { fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { - // Only run at mir-opt-level=2 or higher for now (we don't fix up debuginfo and remove + // FIXME(#79191, #82678) + if !tcx.sess.opts.debugging_opts.unsound_mir_opts { + return; + } + + // Only run at mir-opt-level=3 or higher for now (we don't fix up debuginfo and remove // storage statements at the moment). - if tcx.sess.opts.debugging_opts.mir_opt_level <= 1 { + if tcx.sess.mir_opt_level() < 3 { return; } @@ -582,6 +587,7 @@ impl Conflicts<'a> { | StatementKind::FakeRead(..) | StatementKind::AscribeUserType(..) | StatementKind::Coverage(..) + | StatementKind::CopyNonOverlapping(..) | StatementKind::Nop => {} } } @@ -714,9 +720,6 @@ impl Conflicts<'a> { } } } - InlineAsmOperand::Const { value } => { - assert!(value.place().is_none()); - } InlineAsmOperand::InOut { reg: _, late: _, @@ -725,6 +728,7 @@ impl Conflicts<'a> { } | InlineAsmOperand::In { reg: _, value: _ } | InlineAsmOperand::Out { reg: _, late: _, place: None } + | InlineAsmOperand::Const { value: _ } | InlineAsmOperand::SymFn { value: _ } | InlineAsmOperand::SymStatic { def_id: _ } => {} } diff --git a/compiler/rustc_mir/src/transform/early_otherwise_branch.rs b/compiler/rustc_mir/src/transform/early_otherwise_branch.rs index b16a99d7f0dca..f7ea9faec4728 100644 --- a/compiler/rustc_mir/src/transform/early_otherwise_branch.rs +++ b/compiler/rustc_mir/src/transform/early_otherwise_branch.rs @@ -26,7 +26,12 @@ pub struct EarlyOtherwiseBranch; impl<'tcx> MirPass<'tcx> for EarlyOtherwiseBranch { fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { - if tcx.sess.opts.debugging_opts.mir_opt_level < 2 { + // FIXME(#78496) + if !tcx.sess.opts.debugging_opts.unsound_mir_opts { + return; + } + + if tcx.sess.mir_opt_level() < 3 { return; } trace!("running EarlyOtherwiseBranch on {:?}", body.source); @@ -91,8 +96,10 @@ impl<'tcx> MirPass<'tcx> for EarlyOtherwiseBranch { opt_to_apply.infos[0].first_switch_info.discr_used_in_switch; let not_equal_rvalue = Rvalue::BinaryOp( not_equal, - Operand::Copy(Place::from(second_discriminant_temp)), - Operand::Copy(first_descriminant_place), + box ( + Operand::Copy(Place::from(second_discriminant_temp)), + Operand::Copy(first_descriminant_place), + ), ); patch.add_statement( end_of_block_location, diff --git a/compiler/rustc_mir/src/transform/elaborate_drops.rs b/compiler/rustc_mir/src/transform/elaborate_drops.rs index 3d435f6d0e75a..c0fcfb620ff76 100644 --- a/compiler/rustc_mir/src/transform/elaborate_drops.rs +++ b/compiler/rustc_mir/src/transform/elaborate_drops.rs @@ -471,7 +471,7 @@ impl<'b, 'tcx> ElaborateDropsCtxt<'b, 'tcx> { Rvalue::Use(Operand::Constant(Box::new(Constant { span, user_ty: None, - literal: ty::Const::from_bool(self.tcx, val), + literal: ty::Const::from_bool(self.tcx, val).into(), }))) } diff --git a/compiler/rustc_mir/src/transform/generator.rs b/compiler/rustc_mir/src/transform/generator.rs index 7a1f3d44a5e97..003003a8abbea 100644 --- a/compiler/rustc_mir/src/transform/generator.rs +++ b/compiler/rustc_mir/src/transform/generator.rs @@ -751,9 +751,10 @@ fn sanitize_witness<'tcx>( span_bug!( body.span, "Broken MIR: generator contains type {} in MIR, \ - but typeck only knows about {}", - decl.ty, - witness, + but typeck only knows about {} and {:?}", + decl_ty, + allowed, + allowed_upvars ); } } @@ -989,7 +990,7 @@ fn insert_panic_block<'tcx>( cond: Operand::Constant(box Constant { span: body.span, user_ty: None, - literal: ty::Const::from_bool(tcx, false), + literal: ty::Const::from_bool(tcx, false).into(), }), expected: true, msg: message, @@ -1454,6 +1455,7 @@ impl Visitor<'tcx> for EnsureGeneratorFieldAssignmentsNeverAlias<'_> { | StatementKind::Retag(..) | StatementKind::AscribeUserType(..) | StatementKind::Coverage(..) + | StatementKind::CopyNonOverlapping(..) | StatementKind::Nop => {} } } diff --git a/compiler/rustc_mir/src/transform/inline.rs b/compiler/rustc_mir/src/transform/inline.rs index 0a899273efe6c..b6f80763bc8c4 100644 --- a/compiler/rustc_mir/src/transform/inline.rs +++ b/compiler/rustc_mir/src/transform/inline.rs @@ -39,20 +39,11 @@ struct CallSite<'tcx> { /// Returns true if MIR inlining is enabled in the current compilation session. crate fn is_enabled(tcx: TyCtxt<'_>) -> bool { - if tcx.sess.opts.debugging_opts.instrument_coverage { - // Since `Inline` happens after `InstrumentCoverage`, the function-specific coverage - // counters can be invalidated, such as by merging coverage counter statements from - // a pre-inlined function into a different function. This kind of change is invalid, - // so inlining must be skipped. Note: This check is performed here so inlining can - // be disabled without preventing other optimizations (regardless of `mir_opt_level`). - return false; - } - if let Some(enabled) = tcx.sess.opts.debugging_opts.inline_mir { return enabled; } - tcx.sess.opts.debugging_opts.mir_opt_level >= 2 + tcx.sess.mir_opt_level() >= 3 } impl<'tcx> MirPass<'tcx> for Inline { @@ -416,7 +407,7 @@ impl Inliner<'tcx> { TerminatorKind::Call { func: Operand::Constant(ref f), cleanup, .. } => { if let ty::FnDef(def_id, substs) = - *callsite.callee.subst_mir(self.tcx, &f.literal.ty).kind() + *callsite.callee.subst_mir(self.tcx, &f.literal.ty()).kind() { let substs = self.tcx.normalize_erasing_regions(self.param_env, substs); if let Ok(Some(instance)) = @@ -637,8 +628,11 @@ impl Inliner<'tcx> { // `required_consts`, here we may not only have `ConstKind::Unevaluated` // because we are calling `subst_and_normalize_erasing_regions`. caller_body.required_consts.extend( - callee_body.required_consts.iter().copied().filter(|&constant| { - matches!(constant.literal.val, ConstKind::Unevaluated(_, _, _)) + callee_body.required_consts.iter().copied().filter(|&ct| { + match ct.literal.const_for_ty() { + Some(ct) => matches!(ct.val, ConstKind::Unevaluated(_)), + None => true, + } }), ); } diff --git a/compiler/rustc_mir/src/transform/instcombine.rs b/compiler/rustc_mir/src/transform/instcombine.rs index 74dadb2572565..7aaf0224164c6 100644 --- a/compiler/rustc_mir/src/transform/instcombine.rs +++ b/compiler/rustc_mir/src/transform/instcombine.rs @@ -44,7 +44,7 @@ impl<'tcx, 'a> InstCombineContext<'tcx, 'a> { /// Transform boolean comparisons into logical operations. fn combine_bool_cmp(&self, source_info: &SourceInfo, rvalue: &mut Rvalue<'tcx>) { match rvalue { - Rvalue::BinaryOp(op @ (BinOp::Eq | BinOp::Ne), a, b) => { + Rvalue::BinaryOp(op @ (BinOp::Eq | BinOp::Ne), box (a, b)) => { let new = match (op, self.try_eval_bool(a), self.try_eval_bool(b)) { // Transform "Eq(a, true)" ==> "a" (BinOp::Eq, _, Some(true)) => Some(a.clone()), @@ -79,7 +79,7 @@ impl<'tcx, 'a> InstCombineContext<'tcx, 'a> { fn try_eval_bool(&self, a: &Operand<'_>) -> Option { let a = a.constant()?; - if a.literal.ty.is_bool() { a.literal.val.try_to_bool() } else { None } + if a.literal.ty().is_bool() { a.literal.try_to_bool() } else { None } } /// Transform "&(*a)" ==> "a". @@ -110,12 +110,13 @@ impl<'tcx, 'a> InstCombineContext<'tcx, 'a> { fn combine_len(&self, source_info: &SourceInfo, rvalue: &mut Rvalue<'tcx>) { if let Rvalue::Len(ref place) = *rvalue { let place_ty = place.ty(self.local_decls, self.tcx).ty; - if let ty::Array(_, len) = place_ty.kind() { + if let ty::Array(_, len) = *place_ty.kind() { if !self.should_combine(source_info, rvalue) { return; } - let constant = Constant { span: source_info.span, literal: len, user_ty: None }; + let constant = + Constant { span: source_info.span, literal: len.into(), user_ty: None }; *rvalue = Rvalue::Use(Operand::Constant(box constant)); } } diff --git a/compiler/rustc_mir/src/transform/lower_intrinsics.rs b/compiler/rustc_mir/src/transform/lower_intrinsics.rs index f5968532eb396..e6ee474285ec1 100644 --- a/compiler/rustc_mir/src/transform/lower_intrinsics.rs +++ b/compiler/rustc_mir/src/transform/lower_intrinsics.rs @@ -33,13 +33,34 @@ impl<'tcx> MirPass<'tcx> for LowerIntrinsics { Rvalue::Use(Operand::Constant(box Constant { span: terminator.source_info.span, user_ty: None, - literal: ty::Const::zero_sized(tcx, tcx.types.unit), + literal: ty::Const::zero_sized(tcx, tcx.types.unit).into(), })), )), }); terminator.kind = TerminatorKind::Goto { target }; } } + sym::copy_nonoverlapping => { + let target = destination.unwrap().1; + let mut args = args.drain(..); + block.statements.push(Statement { + source_info: terminator.source_info, + kind: StatementKind::CopyNonOverlapping( + box rustc_middle::mir::CopyNonOverlapping { + src: args.next().unwrap(), + dst: args.next().unwrap(), + count: args.next().unwrap(), + }, + ), + }); + assert_eq!( + args.next(), + None, + "Extra argument for copy_non_overlapping intrinsic" + ); + drop(args); + terminator.kind = TerminatorKind::Goto { target }; + } sym::wrapping_add | sym::wrapping_sub | sym::wrapping_mul => { if let Some((destination, target)) = *destination { let lhs; @@ -59,7 +80,7 @@ impl<'tcx> MirPass<'tcx> for LowerIntrinsics { source_info: terminator.source_info, kind: StatementKind::Assign(box ( destination, - Rvalue::BinaryOp(bin_op, lhs, rhs), + Rvalue::BinaryOp(bin_op, box (lhs, rhs)), )), }); terminator.kind = TerminatorKind::Goto { target }; diff --git a/compiler/rustc_mir/src/transform/match_branches.rs b/compiler/rustc_mir/src/transform/match_branches.rs index 92b4ae397ae5b..f7a9835353e5c 100644 --- a/compiler/rustc_mir/src/transform/match_branches.rs +++ b/compiler/rustc_mir/src/transform/match_branches.rs @@ -1,6 +1,7 @@ use crate::transform::MirPass; use rustc_middle::mir::*; use rustc_middle::ty::TyCtxt; +use std::iter; use super::simplify::simplify_cfg; @@ -40,7 +41,7 @@ pub struct MatchBranchSimplification; impl<'tcx> MirPass<'tcx> for MatchBranchSimplification { fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { - if tcx.sess.opts.debugging_opts.mir_opt_level <= 1 { + if tcx.sess.mir_opt_level() < 3 { return; } @@ -83,7 +84,7 @@ impl<'tcx> MirPass<'tcx> for MatchBranchSimplification { if first_stmts.len() != scnd_stmts.len() { continue; } - for (f, s) in first_stmts.iter().zip(scnd_stmts.iter()) { + for (f, s) in iter::zip(first_stmts, scnd_stmts) { match (&f.kind, &s.kind) { // If two statements are exactly the same, we can optimize. (f_s, s_s) if f_s == s_s => {} @@ -93,8 +94,8 @@ impl<'tcx> MirPass<'tcx> for MatchBranchSimplification { StatementKind::Assign(box (lhs_f, Rvalue::Use(Operand::Constant(f_c)))), StatementKind::Assign(box (lhs_s, Rvalue::Use(Operand::Constant(s_c)))), ) if lhs_f == lhs_s - && f_c.literal.ty.is_bool() - && s_c.literal.ty.is_bool() + && f_c.literal.ty().is_bool() + && s_c.literal.ty().is_bool() && f_c.literal.try_eval_bool(tcx, param_env).is_some() && s_c.literal.try_eval_bool(tcx, param_env).is_some() => {} @@ -113,7 +114,7 @@ impl<'tcx> MirPass<'tcx> for MatchBranchSimplification { // and bb_idx has a different terminator from both of them. let (from, first, second) = bbs.pick3_mut(bb_idx, first, second); - let new_stmts = first.statements.iter().zip(second.statements.iter()).map(|(f, s)| { + let new_stmts = iter::zip(&first.statements, &second.statements).map(|(f, s)| { match (&f.kind, &s.kind) { (f_s, s_s) if f_s == s_s => (*f).clone(), @@ -139,8 +140,7 @@ impl<'tcx> MirPass<'tcx> for MatchBranchSimplification { let op = if f_b { BinOp::Eq } else { BinOp::Ne }; let rhs = Rvalue::BinaryOp( op, - Operand::Copy(Place::from(discr_local)), - const_cmp, + box (Operand::Copy(Place::from(discr_local)), const_cmp), ); Statement { source_info: f.source_info, diff --git a/compiler/rustc_mir/src/transform/mod.rs b/compiler/rustc_mir/src/transform/mod.rs index 9cb8abf75c424..5c49ee69edc51 100644 --- a/compiler/rustc_mir/src/transform/mod.rs +++ b/compiler/rustc_mir/src/transform/mod.rs @@ -44,6 +44,7 @@ pub mod promote_consts; pub mod remove_noop_landing_pads; pub mod remove_storage_markers; pub mod remove_unneeded_drops; +pub mod remove_zsts; pub mod required_consts; pub mod rustc_peek; pub mod simplify; @@ -58,6 +59,7 @@ pub use rustc_middle::mir::MirSource; pub(crate) fn provide(providers: &mut Providers) { self::check_unsafety::provide(providers); + self::check_packed_ref::provide(providers); *providers = Providers { mir_keys, mir_const, @@ -313,11 +315,8 @@ fn mir_promoted( &simplify::SimplifyCfg::new("promote-consts"), ]; - let opt_coverage: &[&dyn MirPass<'tcx>] = if tcx.sess.opts.debugging_opts.instrument_coverage { - &[&coverage::InstrumentCoverage] - } else { - &[] - }; + let opt_coverage: &[&dyn MirPass<'tcx>] = + if tcx.sess.instrument_coverage() { &[&coverage::InstrumentCoverage] } else { &[] }; run_passes(tcx, &mut body, MirPhase::ConstPromotion, &[promote, opt_coverage]); @@ -475,7 +474,7 @@ fn run_post_borrowck_cleanup_passes<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tc } fn run_optimization_passes<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { - let mir_opt_level = tcx.sess.opts.debugging_opts.mir_opt_level; + let mir_opt_level = tcx.sess.mir_opt_level(); // Lowering generator control-flow and variables has to happen before we do anything else // to them. We run some optimizations before that, because they may be harder to do on the state @@ -494,6 +493,7 @@ fn run_optimization_passes<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { // The main optimizations that we do on MIR. let optimizations: &[&dyn MirPass<'tcx>] = &[ &remove_storage_markers::RemoveStorageMarkers, + &remove_zsts::RemoveZsts, &const_goto::ConstGoto, &remove_unneeded_drops::RemoveUnneededDrops, &match_branches::MatchBranchSimplification, diff --git a/compiler/rustc_mir/src/transform/multiple_return_terminators.rs b/compiler/rustc_mir/src/transform/multiple_return_terminators.rs index 617086622cc1a..4aaa0baa9f46a 100644 --- a/compiler/rustc_mir/src/transform/multiple_return_terminators.rs +++ b/compiler/rustc_mir/src/transform/multiple_return_terminators.rs @@ -10,7 +10,7 @@ pub struct MultipleReturnTerminators; impl<'tcx> MirPass<'tcx> for MultipleReturnTerminators { fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { - if tcx.sess.opts.debugging_opts.mir_opt_level < 3 { + if tcx.sess.mir_opt_level() < 4 { return; } diff --git a/compiler/rustc_mir/src/transform/nrvo.rs b/compiler/rustc_mir/src/transform/nrvo.rs index ce02fb261df6f..445dc12909c14 100644 --- a/compiler/rustc_mir/src/transform/nrvo.rs +++ b/compiler/rustc_mir/src/transform/nrvo.rs @@ -34,7 +34,7 @@ pub struct RenameReturnPlace; impl<'tcx> MirPass<'tcx> for RenameReturnPlace { fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut mir::Body<'tcx>) { - if tcx.sess.opts.debugging_opts.mir_opt_level == 0 { + if tcx.sess.mir_opt_level() == 0 { return; } diff --git a/compiler/rustc_mir/src/transform/promote_consts.rs b/compiler/rustc_mir/src/transform/promote_consts.rs index 47d976281a447..1bbaf833c4fd9 100644 --- a/compiler/rustc_mir/src/transform/promote_consts.rs +++ b/compiler/rustc_mir/src/transform/promote_consts.rs @@ -108,9 +108,6 @@ pub enum Candidate { /// the attribute currently provides the semantic requirement that arguments /// must be constant. Argument { bb: BasicBlock, index: usize }, - - /// `const` operand in asm!. - InlineAsm { bb: BasicBlock, index: usize }, } impl Candidate { @@ -118,16 +115,14 @@ impl Candidate { fn forces_explicit_promotion(&self) -> bool { match self { Candidate::Ref(_) => false, - Candidate::Argument { .. } | Candidate::InlineAsm { .. } => true, + Candidate::Argument { .. } => true, } } fn source_info(&self, body: &Body<'_>) -> SourceInfo { match self { Candidate::Ref(location) => *body.source_info(*location), - Candidate::Argument { bb, .. } | Candidate::InlineAsm { bb, .. } => { - *body.source_info(body.terminator_loc(*bb)) - } + Candidate::Argument { bb, .. } => *body.source_info(body.terminator_loc(*bb)), } } } @@ -217,36 +212,25 @@ impl<'tcx> Visitor<'tcx> for Collector<'_, 'tcx> { fn visit_terminator(&mut self, terminator: &Terminator<'tcx>, location: Location) { self.super_terminator(terminator, location); - match terminator.kind { - TerminatorKind::Call { ref func, .. } => { - if let ty::FnDef(def_id, _) = *func.ty(self.ccx.body, self.ccx.tcx).kind() { - let fn_sig = self.ccx.tcx.fn_sig(def_id); - if let Abi::RustIntrinsic | Abi::PlatformIntrinsic = fn_sig.abi() { - let name = self.ccx.tcx.item_name(def_id); - // FIXME(eddyb) use `#[rustc_args_required_const(2)]` for shuffles. - if name.as_str().starts_with("simd_shuffle") { - self.candidates - .push(Candidate::Argument { bb: location.block, index: 2 }); - - return; // Don't double count `simd_shuffle` candidates - } - } + if let TerminatorKind::Call { ref func, .. } = terminator.kind { + if let ty::FnDef(def_id, _) = *func.ty(self.ccx.body, self.ccx.tcx).kind() { + let fn_sig = self.ccx.tcx.fn_sig(def_id); + if let Abi::RustIntrinsic | Abi::PlatformIntrinsic = fn_sig.abi() { + let name = self.ccx.tcx.item_name(def_id); + // FIXME(eddyb) use `#[rustc_args_required_const(2)]` for shuffles. + if name.as_str().starts_with("simd_shuffle") { + self.candidates.push(Candidate::Argument { bb: location.block, index: 2 }); - if let Some(constant_args) = args_required_const(self.ccx.tcx, def_id) { - for index in constant_args { - self.candidates.push(Candidate::Argument { bb: location.block, index }); - } + return; // Don't double count `simd_shuffle` candidates } } - } - TerminatorKind::InlineAsm { ref operands, .. } => { - for (index, op) in operands.iter().enumerate() { - if let InlineAsmOperand::Const { .. } = op { - self.candidates.push(Candidate::InlineAsm { bb: location.block, index }) + + if let Some(constant_args) = args_required_const(self.ccx.tcx, def_id) { + for index in constant_args { + self.candidates.push(Candidate::Argument { bb: location.block, index }); } } } - _ => {} } } } @@ -335,18 +319,6 @@ impl<'tcx> Validator<'_, 'tcx> { _ => bug!(), } } - Candidate::InlineAsm { bb, index } => { - assert!(self.explicit); - - let terminator = self.body[bb].terminator(); - match &terminator.kind { - TerminatorKind::InlineAsm { operands, .. } => match &operands[index] { - InlineAsmOperand::Const { value } => self.validate_operand(value), - _ => bug!(), - }, - _ => bug!(), - } - } } } @@ -643,7 +615,7 @@ impl<'tcx> Validator<'_, 'tcx> { self.validate_operand(operand)?; } - Rvalue::BinaryOp(op, lhs, rhs) | Rvalue::CheckedBinaryOp(op, lhs, rhs) => { + Rvalue::BinaryOp(op, box (lhs, rhs)) | Rvalue::CheckedBinaryOp(op, box (lhs, rhs)) => { let op = *op; let lhs_ty = lhs.ty(self.body, self.tcx); @@ -818,9 +790,7 @@ pub fn validate_candidates( } match candidate { - Candidate::Argument { bb, index } | Candidate::InlineAsm { bb, index } - if !is_promotable => - { + Candidate::Argument { bb, index } if !is_promotable => { let span = ccx.body[bb].terminator().source_info.span; let msg = format!("argument {} is required to be a constant", index + 1); ccx.tcx.sess.span_err(span, &msg); @@ -921,7 +891,7 @@ impl<'a, 'tcx> Promoter<'a, 'tcx> { let unit = Rvalue::Use(Operand::Constant(box Constant { span: statement.source_info.span, user_ty: None, - literal: ty::Const::zero_sized(self.tcx, self.tcx.types.unit), + literal: ty::Const::zero_sized(self.tcx, self.tcx.types.unit).into(), })); mem::replace(rhs, unit) }, @@ -998,20 +968,22 @@ impl<'a, 'tcx> Promoter<'a, 'tcx> { Operand::Constant(Box::new(Constant { span, user_ty: None, - literal: tcx.mk_const(ty::Const { - ty, - val: ty::ConstKind::Unevaluated( - def, - InternalSubsts::for_item(tcx, def.did, |param, _| { - if let ty::GenericParamDefKind::Lifetime = param.kind { - tcx.lifetimes.re_erased.into() - } else { - tcx.mk_param_from_def(param) - } + literal: tcx + .mk_const(ty::Const { + ty, + val: ty::ConstKind::Unevaluated(ty::Unevaluated { + def, + substs: InternalSubsts::for_item(tcx, def.did, |param, _| { + if let ty::GenericParamDefKind::Lifetime = param.kind { + tcx.lifetimes.re_erased.into() + } else { + tcx.mk_param_from_def(param) + } + }), + promoted: Some(promoted_id), }), - Some(promoted_id), - ), - }), + }) + .into(), })) }; let (blocks, local_decls) = self.source.basic_blocks_and_local_decls_mut(); @@ -1087,24 +1059,6 @@ impl<'a, 'tcx> Promoter<'a, 'tcx> { _ => bug!(), } } - Candidate::InlineAsm { bb, index } => { - let terminator = blocks[bb].terminator_mut(); - match terminator.kind { - TerminatorKind::InlineAsm { ref mut operands, .. } => { - match &mut operands[index] { - InlineAsmOperand::Const { ref mut value } => { - let ty = value.ty(local_decls, self.tcx); - let span = terminator.source_info.span; - - Rvalue::Use(mem::replace(value, promoted_operand(ty, span))) - } - _ => bug!(), - } - } - - _ => bug!(), - } - } } }; @@ -1159,7 +1113,7 @@ pub fn promote_candidates<'tcx>( } } } - Candidate::Argument { .. } | Candidate::InlineAsm { .. } => {} + Candidate::Argument { .. } => {} } // Declare return place local so that `mir::Body::new` doesn't complain. @@ -1250,8 +1204,8 @@ crate fn is_const_fn_in_array_repeat_expression<'tcx>( if let Some(Terminator { kind: TerminatorKind::Call { func, destination, .. }, .. }) = &block.terminator { - if let Operand::Constant(box Constant { literal: ty::Const { ty, .. }, .. }) = func { - if let ty::FnDef(def_id, _) = *ty.kind() { + if let Operand::Constant(box Constant { literal, .. }) = func { + if let ty::FnDef(def_id, _) = *literal.ty().kind() { if let Some((destination_place, _)) = destination { if destination_place == place { if is_const_fn(ccx.tcx, def_id) { diff --git a/compiler/rustc_mir/src/transform/remove_noop_landing_pads.rs b/compiler/rustc_mir/src/transform/remove_noop_landing_pads.rs index 31e201c3a5bbe..5347846a4b334 100644 --- a/compiler/rustc_mir/src/transform/remove_noop_landing_pads.rs +++ b/compiler/rustc_mir/src/transform/remove_noop_landing_pads.rs @@ -55,6 +55,7 @@ impl RemoveNoopLandingPads { StatementKind::Assign { .. } | StatementKind::SetDiscriminant { .. } | StatementKind::LlvmInlineAsm { .. } + | StatementKind::CopyNonOverlapping(..) | StatementKind::Retag { .. } => { return false; } diff --git a/compiler/rustc_mir/src/transform/remove_zsts.rs b/compiler/rustc_mir/src/transform/remove_zsts.rs new file mode 100644 index 0000000000000..70f7538dd57a8 --- /dev/null +++ b/compiler/rustc_mir/src/transform/remove_zsts.rs @@ -0,0 +1,89 @@ +//! Removes assignments to ZST places. + +use crate::transform::MirPass; +use rustc_middle::mir::tcx::PlaceTy; +use rustc_middle::mir::{Body, LocalDecls, Place, StatementKind}; +use rustc_middle::ty::{self, Ty, TyCtxt}; + +pub struct RemoveZsts; + +impl<'tcx> MirPass<'tcx> for RemoveZsts { + fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { + if tcx.sess.mir_opt_level() < 3 { + return; + } + let param_env = tcx.param_env(body.source.def_id()); + let (basic_blocks, local_decls) = body.basic_blocks_and_local_decls_mut(); + for block in basic_blocks.iter_mut() { + for statement in block.statements.iter_mut() { + match statement.kind { + StatementKind::Assign(box (place, _)) => { + let place_ty = place.ty(local_decls, tcx).ty; + if !maybe_zst(place_ty) { + continue; + } + let layout = match tcx.layout_of(param_env.and(place_ty)) { + Ok(layout) => layout, + Err(_) => continue, + }; + if !layout.is_zst() { + continue; + } + if involves_a_union(place, local_decls, tcx) { + continue; + } + if tcx.consider_optimizing(|| { + format!( + "RemoveZsts - Place: {:?} SourceInfo: {:?}", + place, statement.source_info + ) + }) { + statement.make_nop(); + } + } + _ => {} + } + } + } + } +} + +/// A cheap, approximate check to avoid unnecessary `layout_of` calls. +fn maybe_zst(ty: Ty<'_>) -> bool { + match ty.kind() { + // maybe ZST (could be more precise) + ty::Adt(..) | ty::Array(..) | ty::Closure(..) | ty::Tuple(..) | ty::Opaque(..) => true, + // definitely ZST + ty::FnDef(..) | ty::Never => true, + // unreachable or can't be ZST + _ => false, + } +} + +/// Miri lazily allocates memory for locals on assignment, +/// so we must preserve writes to unions and union fields, +/// or it will ICE on reads of those fields. +fn involves_a_union<'tcx>( + place: Place<'tcx>, + local_decls: &LocalDecls<'tcx>, + tcx: TyCtxt<'tcx>, +) -> bool { + let mut place_ty = PlaceTy::from_ty(local_decls[place.local].ty); + if is_union(place_ty.ty) { + return true; + } + for elem in place.projection { + place_ty = place_ty.projection_ty(tcx, elem); + if is_union(place_ty.ty) { + return true; + } + } + return false; +} + +fn is_union(ty: Ty<'_>) -> bool { + match ty.kind() { + ty::Adt(def, _) if def.is_union() => true, + _ => false, + } +} diff --git a/compiler/rustc_mir/src/transform/required_consts.rs b/compiler/rustc_mir/src/transform/required_consts.rs index a63ab30a68fa2..8b64ad65ab35c 100644 --- a/compiler/rustc_mir/src/transform/required_consts.rs +++ b/compiler/rustc_mir/src/transform/required_consts.rs @@ -14,10 +14,10 @@ impl<'a, 'tcx> RequiredConstsVisitor<'a, 'tcx> { impl<'a, 'tcx> Visitor<'tcx> for RequiredConstsVisitor<'a, 'tcx> { fn visit_constant(&mut self, constant: &Constant<'tcx>, _: Location) { - let const_kind = constant.literal.val; - - if let ConstKind::Unevaluated(_, _, _) = const_kind { - self.required_consts.push(*constant); + if let Some(ct) = constant.literal.const_for_ty() { + if let ConstKind::Unevaluated(_) = ct.val { + self.required_consts.push(*constant); + } } } } diff --git a/compiler/rustc_mir/src/transform/rustc_peek.rs b/compiler/rustc_mir/src/transform/rustc_peek.rs index 7598be4e4a11d..a6b8f20f6d449 100644 --- a/compiler/rustc_mir/src/transform/rustc_peek.rs +++ b/compiler/rustc_mir/src/transform/rustc_peek.rs @@ -205,7 +205,7 @@ impl PeekCall { if let mir::TerminatorKind::Call { func: Operand::Constant(func), args, .. } = &terminator.kind { - if let ty::FnDef(def_id, substs) = *func.literal.ty.kind() { + if let ty::FnDef(def_id, substs) = *func.literal.ty().kind() { let sig = tcx.fn_sig(def_id); let name = tcx.item_name(def_id); if sig.abi() != Abi::RustIntrinsic || name != sym::rustc_peek { diff --git a/compiler/rustc_mir/src/transform/simplify.rs b/compiler/rustc_mir/src/transform/simplify.rs index 85f27428bbbf4..65e2d096b2094 100644 --- a/compiler/rustc_mir/src/transform/simplify.rs +++ b/compiler/rustc_mir/src/transform/simplify.rs @@ -414,7 +414,9 @@ impl UsedLocals { // A use, not a definition. self.visit_place(place, PlaceContext::MutatingUse(MutatingUseContext::Store), location); } else { - // A definition. Although, it still might use other locals for indexing. + // A definition. The base local itself is not visited, so this occurrence is not counted + // toward its use count. There might be other locals still, used in an indexing + // projection. self.super_projection( place.as_ref(), PlaceContext::MutatingUse(MutatingUseContext::Projection), @@ -428,6 +430,7 @@ impl Visitor<'_> for UsedLocals { fn visit_statement(&mut self, statement: &Statement<'tcx>, location: Location) { match statement.kind { StatementKind::LlvmInlineAsm(..) + | StatementKind::CopyNonOverlapping(..) | StatementKind::Retag(..) | StatementKind::Coverage(..) | StatementKind::FakeRead(..) diff --git a/compiler/rustc_mir/src/transform/simplify_comparison_integral.rs b/compiler/rustc_mir/src/transform/simplify_comparison_integral.rs index bd76e118fdf71..9f473f3bae534 100644 --- a/compiler/rustc_mir/src/transform/simplify_comparison_integral.rs +++ b/compiler/rustc_mir/src/transform/simplify_comparison_integral.rs @@ -84,10 +84,10 @@ impl<'tcx> MirPass<'tcx> for SimplifyComparisonIntegral { use Operand::*; match rhs { - Rvalue::BinaryOp(_, ref mut left @ Move(_), Constant(_)) => { + Rvalue::BinaryOp(_, box (ref mut left @ Move(_), Constant(_))) => { *left = Copy(opt.to_switch_on); } - Rvalue::BinaryOp(_, Constant(_), ref mut right @ Move(_)) => { + Rvalue::BinaryOp(_, box (Constant(_), ref mut right @ Move(_))) => { *right = Copy(opt.to_switch_on); } _ => (), @@ -166,7 +166,10 @@ impl<'a, 'tcx> OptimizationFinder<'a, 'tcx> { if *lhs == place_switched_on => { match rhs { - Rvalue::BinaryOp(op @ (BinOp::Eq | BinOp::Ne), left, right) => { + Rvalue::BinaryOp( + op @ (BinOp::Eq | BinOp::Ne), + box (left, right), + ) => { let (branch_value_scalar, branch_value_ty, to_switch_on) = find_branch_value_info(left, right)?; @@ -202,12 +205,12 @@ fn find_branch_value_info<'tcx>( match (left, right) { (Constant(branch_value), Copy(to_switch_on) | Move(to_switch_on)) | (Copy(to_switch_on) | Move(to_switch_on), Constant(branch_value)) => { - let branch_value_ty = branch_value.literal.ty; + let branch_value_ty = branch_value.literal.ty(); // we only want to apply this optimization if we are matching on integrals (and chars), as it is not possible to switch on floats if !branch_value_ty.is_integral() && !branch_value_ty.is_char() { return None; }; - let branch_value_scalar = branch_value.literal.val.try_to_scalar()?; + let branch_value_scalar = branch_value.literal.try_to_scalar()?; Some((branch_value_scalar, branch_value_ty, *to_switch_on)) } _ => None, diff --git a/compiler/rustc_mir/src/transform/unreachable_prop.rs b/compiler/rustc_mir/src/transform/unreachable_prop.rs index e39c8656021b2..658c6b6e9db20 100644 --- a/compiler/rustc_mir/src/transform/unreachable_prop.rs +++ b/compiler/rustc_mir/src/transform/unreachable_prop.rs @@ -12,8 +12,8 @@ pub struct UnreachablePropagation; impl MirPass<'_> for UnreachablePropagation { fn run_pass<'tcx>(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { - if tcx.sess.opts.debugging_opts.mir_opt_level < 3 { - // Enable only under -Zmir-opt-level=3 as in some cases (check the deeply-nested-opt + if tcx.sess.mir_opt_level() < 4 { + // Enable only under -Zmir-opt-level=4 as in some cases (check the deeply-nested-opt // perf benchmark) LLVM may spend quite a lot of time optimizing the generated code. return; } diff --git a/compiler/rustc_mir/src/transform/validate.rs b/compiler/rustc_mir/src/transform/validate.rs index 29b90bff210bd..d009b0b1b2384 100644 --- a/compiler/rustc_mir/src/transform/validate.rs +++ b/compiler/rustc_mir/src/transform/validate.rs @@ -294,7 +294,49 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { ); } } - _ => {} + StatementKind::CopyNonOverlapping(box rustc_middle::mir::CopyNonOverlapping { + ref src, + ref dst, + ref count, + }) => { + let src_ty = src.ty(&self.body.local_decls, self.tcx); + let op_src_ty = if let Some(src_deref) = src_ty.builtin_deref(true) { + src_deref.ty + } else { + self.fail( + location, + format!("Expected src to be ptr in copy_nonoverlapping, got: {}", src_ty), + ); + return; + }; + let dst_ty = dst.ty(&self.body.local_decls, self.tcx); + let op_dst_ty = if let Some(dst_deref) = dst_ty.builtin_deref(true) { + dst_deref.ty + } else { + self.fail( + location, + format!("Expected dst to be ptr in copy_nonoverlapping, got: {}", dst_ty), + ); + return; + }; + // since CopyNonOverlapping is parametrized by 1 type, + // we only need to check that they are equal and not keep an extra parameter. + if op_src_ty != op_dst_ty { + self.fail(location, format!("bad arg ({:?} != {:?})", op_src_ty, op_dst_ty)); + } + + let op_cnt_ty = count.ty(&self.body.local_decls, self.tcx); + if op_cnt_ty != self.tcx.types.usize { + self.fail(location, format!("bad arg ({:?} != usize)", op_cnt_ty)) + } + } + StatementKind::SetDiscriminant { .. } + | StatementKind::StorageLive(..) + | StatementKind::StorageDead(..) + | StatementKind::LlvmInlineAsm(..) + | StatementKind::Retag(_, _) + | StatementKind::Coverage(_) + | StatementKind::Nop => {} } self.super_statement(statement, location); diff --git a/compiler/rustc_mir/src/util/alignment.rs b/compiler/rustc_mir/src/util/alignment.rs index f567c9cfaab86..5d4ca871faa2e 100644 --- a/compiler/rustc_mir/src/util/alignment.rs +++ b/compiler/rustc_mir/src/util/alignment.rs @@ -1,5 +1,6 @@ use rustc_middle::mir::*; use rustc_middle::ty::{self, TyCtxt}; +use rustc_target::abi::Align; /// Returns `true` if this place is allowed to be less aligned /// than its containing struct (because it is within a packed @@ -14,17 +15,25 @@ where L: HasLocalDecls<'tcx>, { debug!("is_disaligned({:?})", place); - if !is_within_packed(tcx, local_decls, place) { - debug!("is_disaligned({:?}) - not within packed", place); - return false; - } + let pack = match is_within_packed(tcx, local_decls, place) { + None => { + debug!("is_disaligned({:?}) - not within packed", place); + return false; + } + Some(pack) => pack, + }; let ty = place.ty(local_decls, tcx).ty; match tcx.layout_raw(param_env.and(ty)) { - Ok(layout) if layout.align.abi.bytes() == 1 => { - // if the alignment is 1, the type can't be further - // disaligned. - debug!("is_disaligned({:?}) - align = 1", place); + Ok(layout) if layout.align.abi <= pack => { + // If the packed alignment is greater or equal to the field alignment, the type won't be + // further disaligned. + debug!( + "is_disaligned({:?}) - align = {}, packed = {}; not disaligned", + place, + layout.align.abi.bytes(), + pack.bytes() + ); false } _ => { @@ -34,7 +43,11 @@ where } } -fn is_within_packed<'tcx, L>(tcx: TyCtxt<'tcx>, local_decls: &L, place: Place<'tcx>) -> bool +fn is_within_packed<'tcx, L>( + tcx: TyCtxt<'tcx>, + local_decls: &L, + place: Place<'tcx>, +) -> Option where L: HasLocalDecls<'tcx>, { @@ -45,7 +58,7 @@ where ProjectionElem::Field(..) => { let ty = place_base.ty(local_decls, tcx).ty; match ty.kind() { - ty::Adt(def, _) if def.repr.packed() => return true, + ty::Adt(def, _) => return def.repr.pack, _ => {} } } @@ -53,5 +66,5 @@ where } } - false + None } diff --git a/compiler/rustc_mir/src/util/elaborate_drops.rs b/compiler/rustc_mir/src/util/elaborate_drops.rs index 0e2d8e5495b72..e9190d7ebef8b 100644 --- a/compiler/rustc_mir/src/util/elaborate_drops.rs +++ b/compiler/rustc_mir/src/util/elaborate_drops.rs @@ -678,11 +678,14 @@ where let one = self.constant_usize(1); let (ptr_next, cur_next) = if ptr_based { - (Rvalue::Use(copy(cur.into())), Rvalue::BinaryOp(BinOp::Offset, move_(cur.into()), one)) + ( + Rvalue::Use(copy(cur.into())), + Rvalue::BinaryOp(BinOp::Offset, box (move_(cur.into()), one)), + ) } else { ( Rvalue::AddressOf(Mutability::Mut, tcx.mk_place_index(self.place, cur)), - Rvalue::BinaryOp(BinOp::Add, move_(cur.into()), one), + Rvalue::BinaryOp(BinOp::Add, box (move_(cur.into()), one)), ) }; @@ -700,7 +703,7 @@ where let loop_block = BasicBlockData { statements: vec![self.assign( can_go, - Rvalue::BinaryOp(BinOp::Eq, copy(Place::from(cur)), copy(length_or_end)), + Rvalue::BinaryOp(BinOp::Eq, box (copy(Place::from(cur)), copy(length_or_end))), )], is_cleanup: unwind.is_cleanup(), terminator: Some(Terminator { @@ -816,7 +819,10 @@ where self.assign(cur, Rvalue::Cast(CastKind::Misc, Operand::Move(tmp), iter_ty)), self.assign( length_or_end, - Rvalue::BinaryOp(BinOp::Offset, Operand::Copy(cur), Operand::Move(length)), + Rvalue::BinaryOp( + BinOp::Offset, + box (Operand::Copy(cur), Operand::Move(length)), + ), ), ] } else { @@ -1029,7 +1035,7 @@ where Operand::Constant(box Constant { span: self.source_info.span, user_ty: None, - literal: ty::Const::from_usize(self.tcx(), val.into()), + literal: ty::Const::from_usize(self.tcx(), val.into()).into(), }) } diff --git a/compiler/rustc_mir/src/util/find_self_call.rs b/compiler/rustc_mir/src/util/find_self_call.rs index 5b146eeb87c04..33ad128eeeb75 100644 --- a/compiler/rustc_mir/src/util/find_self_call.rs +++ b/compiler/rustc_mir/src/util/find_self_call.rs @@ -3,7 +3,7 @@ use rustc_middle::ty::subst::SubstsRef; use rustc_middle::ty::{self, TyCtxt}; use rustc_span::def_id::DefId; -/// Checks if the specified `local` is used as the `self` prameter of a method call +/// Checks if the specified `local` is used as the `self` parameter of a method call /// in the provided `BasicBlock`. If it is, then the `DefId` of the called method is /// returned. pub fn find_self_call<'tcx>( @@ -17,8 +17,8 @@ pub fn find_self_call<'tcx>( &body[block].terminator { debug!("find_self_call: func={:?}", func); - if let Operand::Constant(box Constant { literal: ty::Const { ty, .. }, .. }) = func { - if let ty::FnDef(def_id, substs) = *ty.kind() { + if let Operand::Constant(box Constant { literal, .. }) = func { + if let ty::FnDef(def_id, substs) = *literal.ty().kind() { if let Some(ty::AssocItem { fn_has_self_parameter: true, .. }) = tcx.opt_associated_item(def_id) { diff --git a/compiler/rustc_mir/src/util/generic_graphviz.rs b/compiler/rustc_mir/src/util/generic_graphviz.rs index fd41e28226632..21c18b28e258b 100644 --- a/compiler/rustc_mir/src/util/generic_graphviz.rs +++ b/compiler/rustc_mir/src/util/generic_graphviz.rs @@ -40,22 +40,6 @@ impl< } } - pub fn new_subgraph( - graph: &'a G, - graphviz_name: &str, - node_content_fn: NodeContentFn, - edge_labels_fn: EdgeLabelsFn, - ) -> Self { - Self { - graph, - is_subgraph: true, - graphviz_name: graphviz_name.to_owned(), - graph_label: None, - node_content_fn, - edge_labels_fn, - } - } - pub fn set_graph_label(&mut self, graph_label: &str) { self.graph_label = Some(graph_label.to_owned()); } diff --git a/compiler/rustc_mir/src/util/patch.rs b/compiler/rustc_mir/src/util/patch.rs index 6566a996fe442..d09195f53aecc 100644 --- a/compiler/rustc_mir/src/util/patch.rs +++ b/compiler/rustc_mir/src/util/patch.rs @@ -13,7 +13,6 @@ pub struct MirPatch<'tcx> { new_locals: Vec>, resume_block: BasicBlock, next_local: usize, - make_nop: Vec, } impl<'tcx> MirPatch<'tcx> { @@ -25,7 +24,6 @@ impl<'tcx> MirPatch<'tcx> { new_locals: vec![], next_local: body.local_decls.len(), resume_block: START_BLOCK, - make_nop: vec![], }; // make sure the MIR we create has a resume block. It is @@ -117,15 +115,7 @@ impl<'tcx> MirPatch<'tcx> { self.add_statement(loc, StatementKind::Assign(box (place, rv))); } - pub fn make_nop(&mut self, loc: Location) { - self.make_nop.push(loc); - } - pub fn apply(self, body: &mut Body<'tcx>) { - debug!("MirPatch: make nops at: {:?}", self.make_nop); - for loc in self.make_nop { - body.make_statement_nop(loc); - } debug!( "MirPatch: {:?} new temps, starting from index {}: {:?}", self.new_locals.len(), diff --git a/compiler/rustc_mir/src/util/pretty.rs b/compiler/rustc_mir/src/util/pretty.rs index 247a0beccaf0f..3b88aec16b26a 100644 --- a/compiler/rustc_mir/src/util/pretty.rs +++ b/compiler/rustc_mir/src/util/pretty.rs @@ -439,7 +439,7 @@ impl Visitor<'tcx> for ExtraComments<'tcx> { fn visit_constant(&mut self, constant: &Constant<'tcx>, location: Location) { self.super_constant(constant, location); let Constant { span, user_ty, literal } = constant; - match literal.ty.kind() { + match literal.ty().kind() { ty::Int(_) | ty::Uint(_) | ty::Bool | ty::Char => {} // Unit type ty::Tuple(tys) if tys.is_empty() => {} @@ -449,7 +449,16 @@ impl Visitor<'tcx> for ExtraComments<'tcx> { if let Some(user_ty) = user_ty { self.push(&format!("+ user_ty: {:?}", user_ty)); } - self.push(&format!("+ literal: {:?}", literal)); + match literal { + ConstantKind::Ty(literal) => self.push(&format!("+ literal: {:?}", literal)), + ConstantKind::Val(val, ty) => { + // To keep the diffs small, we render this almost like we render ty::Const + self.push(&format!( + "+ literal: Const {{ ty: {}, val: Value({:?}) }}", + ty, val + )) + } + } } } } @@ -460,7 +469,21 @@ impl Visitor<'tcx> for ExtraComments<'tcx> { if use_verbose(ty) { self.push("ty::Const"); self.push(&format!("+ ty: {:?}", ty)); - self.push(&format!("+ val: {:?}", val)); + let val = match val { + ty::ConstKind::Param(p) => format!("Param({})", p), + ty::ConstKind::Infer(infer) => format!("Infer({:?})", infer), + ty::ConstKind::Bound(idx, var) => format!("Bound({:?}, {:?})", idx, var), + ty::ConstKind::Placeholder(ph) => format!("PlaceHolder({:?})", ph), + ty::ConstKind::Unevaluated(uv) => format!( + "Unevaluated({}, {:?}, {:?})", + self.tcx.def_path_str(uv.def.did), + uv.substs, + uv.promoted + ), + ty::ConstKind::Value(val) => format!("Value({:?})", val), + ty::ConstKind::Error(_) => format!("Error"), + }; + self.push(&format!("+ val: {}", val)); } } diff --git a/compiler/rustc_mir/src/util/spanview.rs b/compiler/rustc_mir/src/util/spanview.rs index d3ef8c64565c6..a9a30e407b4b0 100644 --- a/compiler/rustc_mir/src/util/spanview.rs +++ b/compiler/rustc_mir/src/util/spanview.rs @@ -245,6 +245,7 @@ pub fn statement_kind_name(statement: &Statement<'_>) -> &'static str { Retag(..) => "Retag", AscribeUserType(..) => "AscribeUserType", Coverage(..) => "Coverage", + CopyNonOverlapping(..) => "CopyNonOverlapping", Nop => "Nop", } } diff --git a/compiler/rustc_mir_build/src/build/block.rs b/compiler/rustc_mir_build/src/build/block.rs index d5f72e6f22dfa..808c6e3ff644b 100644 --- a/compiler/rustc_mir_build/src/build/block.rs +++ b/compiler/rustc_mir_build/src/build/block.rs @@ -2,7 +2,6 @@ use crate::build::matches::ArmHasGuard; use crate::build::ForGuard::OutsideGuard; use crate::build::{BlockAnd, BlockAndExtension, BlockFrame, Builder}; use crate::thir::*; -use rustc_hir as hir; use rustc_middle::mir::*; use rustc_session::lint::builtin::UNSAFE_OP_IN_UNSAFE_FN; use rustc_session::lint::Level; @@ -13,7 +12,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { &mut self, destination: Place<'tcx>, block: BasicBlock, - ast_block: &'tcx hir::Block<'tcx>, + ast_block: &Block<'_, 'tcx>, source_info: SourceInfo, ) -> BlockAnd<()> { let Block { @@ -24,7 +23,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { expr, targeted_by_break, safety_mode, - } = self.hir.mirror(ast_block); + } = *ast_block; self.in_opt_scope(opt_destruction_scope.map(|de| (de, source_info)), move |this| { this.in_scope((region_scope, source_info), LintLevel::Inherited, move |this| { if targeted_by_break { @@ -50,8 +49,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { destination: Place<'tcx>, mut block: BasicBlock, span: Span, - stmts: Vec>, - expr: Option>, + stmts: &[Stmt<'_, 'tcx>], + expr: Option<&Expr<'_, 'tcx>>, safety_mode: BlockSafety, ) -> BlockAnd<()> { let this = self; @@ -79,10 +78,9 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { this.update_source_scope_for_safety_mode(span, safety_mode); let source_info = this.source_info(span); - for stmt in stmts { - let Stmt { kind, opt_destruction_scope } = this.hir.mirror(stmt); + for Stmt { kind, opt_destruction_scope } in stmts { match kind { - StmtKind::Expr { scope, expr } => { + &StmtKind::Expr { scope, expr } => { this.block_context.push(BlockFrame::Statement { ignores_expr_result: true }); unpack!( block = this.in_opt_scope( @@ -90,7 +88,6 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { |this| { let si = (scope, source_info); this.in_scope(si, LintLevel::Inherited, |this| { - let expr = this.hir.mirror(expr); this.stmt_expr(block, expr, Some(scope)) }) } @@ -102,45 +99,44 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { this.block_context.push(BlockFrame::Statement { ignores_expr_result }); // Enter the remainder scope, i.e., the bindings' destruction scope. - this.push_scope((remainder_scope, source_info)); + this.push_scope((*remainder_scope, source_info)); let_scope_stack.push(remainder_scope); // Declare the bindings, which may create a source scope. - let remainder_span = - remainder_scope.span(this.hir.tcx(), &this.hir.region_scope_tree); + let remainder_span = remainder_scope.span(this.tcx, this.region_scope_tree); let visibility_scope = Some(this.new_source_scope(remainder_span, LintLevel::Inherited, None)); // Evaluate the initializer, if present. if let Some(init) = initializer { - let initializer_span = init.span(); + let initializer_span = init.span; unpack!( block = this.in_opt_scope( opt_destruction_scope.map(|de| (de, source_info)), |this| { - let scope = (init_scope, source_info); - this.in_scope(scope, lint_level, |this| { + let scope = (*init_scope, source_info); + this.in_scope(scope, *lint_level, |this| { this.declare_bindings( visibility_scope, remainder_span, - &pattern, + pattern, ArmHasGuard(false), Some((None, initializer_span)), ); - this.expr_into_pattern(block, pattern, init) + this.expr_into_pattern(block, pattern.clone(), init) }) } ) ); } else { - let scope = (init_scope, source_info); - unpack!(this.in_scope(scope, lint_level, |this| { + let scope = (*init_scope, source_info); + unpack!(this.in_scope(scope, *lint_level, |this| { this.declare_bindings( visibility_scope, remainder_span, - &pattern, + pattern, ArmHasGuard(false), None, ); @@ -171,18 +167,15 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { // Then, the block may have an optional trailing expression which is a “return” value // of the block, which is stored into `destination`. - let tcx = this.hir.tcx(); + let tcx = this.tcx; let destination_ty = destination.ty(&this.local_decls, tcx).ty; if let Some(expr) = expr { let tail_result_is_ignored = destination_ty.is_unit() || this.block_context.currently_ignores_tail_results(); - let span = match expr { - ExprRef::Thir(expr) => expr.span, - ExprRef::Mirror(ref expr) => expr.span, - }; - this.block_context.push(BlockFrame::TailExpr { tail_result_is_ignored, span }); + this.block_context + .push(BlockFrame::TailExpr { tail_result_is_ignored, span: expr.span }); - unpack!(block = this.into(destination, block, expr)); + unpack!(block = this.expr_into_dest(destination, block, expr)); let popped = this.block_context.pop(); assert!(popped.map_or(false, |bf| bf.is_tail_expr())); @@ -194,13 +187,13 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { if destination_ty.is_unit() { // We only want to assign an implicit `()` as the return value of the block if the // block does not diverge. (Otherwise, we may try to assign a unit to a `!`-type.) - this.cfg.push_assign_unit(block, source_info, destination, this.hir.tcx()); + this.cfg.push_assign_unit(block, source_info, destination, this.tcx); } } // Finally, we pop all the let scopes before exiting out from the scope of block // itself. for scope in let_scope_stack.into_iter().rev() { - unpack!(block = this.pop_scope((scope, source_info), block)); + unpack!(block = this.pop_scope((*scope, source_info), block)); } // Restore the original source scope. this.source_scope = outer_source_scope; @@ -220,7 +213,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { Safety::Safe => {} // no longer treat `unsafe fn`s as `unsafe` contexts (see RFC #2585) Safety::FnUnsafe - if self.hir.tcx().lint_level_at_node(UNSAFE_OP_IN_UNSAFE_FN, hir_id).0 + if self.tcx.lint_level_at_node(UNSAFE_OP_IN_UNSAFE_FN, hir_id).0 != Level::Allow => {} _ => return, } diff --git a/compiler/rustc_mir_build/src/build/cfg.rs b/compiler/rustc_mir_build/src/build/cfg.rs index 42e2b242d7726..fd4a783d12a00 100644 --- a/compiler/rustc_mir_build/src/build/cfg.rs +++ b/compiler/rustc_mir_build/src/build/cfg.rs @@ -68,7 +68,7 @@ impl<'tcx> CFG<'tcx> { Rvalue::Use(Operand::Constant(box Constant { span: source_info.span, user_ty: None, - literal: ty::Const::zero_sized(tcx, tcx.types.unit), + literal: ty::Const::zero_sized(tcx, tcx.types.unit).into(), })), ); } @@ -80,7 +80,7 @@ impl<'tcx> CFG<'tcx> { cause: FakeReadCause, place: Place<'tcx>, ) { - let kind = StatementKind::FakeRead(cause, box place); + let kind = StatementKind::FakeRead(box (cause, place)); let stmt = Statement { source_info, kind }; self.push(block, stmt); } diff --git a/compiler/rustc_mir_build/src/build/expr/as_constant.rs b/compiler/rustc_mir_build/src/build/expr/as_constant.rs index 3a36ad590c506..57f56e2d09227 100644 --- a/compiler/rustc_mir_build/src/build/expr/as_constant.rs +++ b/compiler/rustc_mir_build/src/build/expr/as_constant.rs @@ -8,18 +8,10 @@ use rustc_middle::ty::CanonicalUserTypeAnnotation; impl<'a, 'tcx> Builder<'a, 'tcx> { /// Compile `expr`, yielding a compile-time constant. Assumes that /// `expr` is a valid compile-time constant! - crate fn as_constant(&mut self, expr: M) -> Constant<'tcx> - where - M: Mirror<'tcx, Output = Expr<'tcx>>, - { - let expr = self.hir.mirror(expr); - self.expr_as_constant(expr) - } - - fn expr_as_constant(&mut self, expr: Expr<'tcx>) -> Constant<'tcx> { + crate fn as_constant(&mut self, expr: &Expr<'_, 'tcx>) -> Constant<'tcx> { let this = self; - let Expr { ty, temp_lifetime: _, span, kind } = expr; - match kind { + let Expr { ty, temp_lifetime: _, span, ref kind } = *expr; + match *kind { ExprKind::Scope { region_scope: _, lint_level: _, value } => this.as_constant(value), ExprKind::Literal { literal, user_ty, const_id: _ } => { let user_ty = user_ty.map(|user_ty| { @@ -30,10 +22,14 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { }) }); assert_eq!(literal.ty, ty); - Constant { span, user_ty, literal } + Constant { span, user_ty, literal: literal.into() } + } + ExprKind::StaticRef { literal, .. } => { + Constant { span, user_ty: None, literal: literal.into() } + } + ExprKind::ConstBlock { value } => { + Constant { span: span, user_ty: None, literal: value.into() } } - ExprKind::StaticRef { literal, .. } => Constant { span, user_ty: None, literal }, - ExprKind::ConstBlock { value } => Constant { span, user_ty: None, literal: value }, _ => span_bug!(span, "expression is not a valid constant {:?}", kind), } } diff --git a/compiler/rustc_mir_build/src/build/expr/as_operand.rs b/compiler/rustc_mir_build/src/build/expr/as_operand.rs index 60f8d8c8a9f36..c393878e0b995 100644 --- a/compiler/rustc_mir_build/src/build/expr/as_operand.rs +++ b/compiler/rustc_mir_build/src/build/expr/as_operand.rs @@ -14,10 +14,11 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { /// after the current enclosing `ExprKind::Scope` has ended, so /// please do *not* return it from functions to avoid bad /// miscompiles. - crate fn as_local_operand(&mut self, block: BasicBlock, expr: M) -> BlockAnd> - where - M: Mirror<'tcx, Output = Expr<'tcx>>, - { + crate fn as_local_operand( + &mut self, + block: BasicBlock, + expr: &Expr<'_, 'tcx>, + ) -> BlockAnd> { let local_scope = self.local_scope(); self.as_operand(block, Some(local_scope), expr) } @@ -70,14 +71,11 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { /// value to the stack. /// /// See #68034 for more details. - crate fn as_local_call_operand( + crate fn as_local_call_operand( &mut self, block: BasicBlock, - expr: M, - ) -> BlockAnd> - where - M: Mirror<'tcx, Output = Expr<'tcx>>, - { + expr: &Expr<'_, 'tcx>, + ) -> BlockAnd> { let local_scope = self.local_scope(); self.as_call_operand(block, Some(local_scope), expr) } @@ -88,41 +86,16 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { /// this time. /// /// The operand is known to be live until the end of `scope`. - crate fn as_operand( - &mut self, - block: BasicBlock, - scope: Option, - expr: M, - ) -> BlockAnd> - where - M: Mirror<'tcx, Output = Expr<'tcx>>, - { - let expr = self.hir.mirror(expr); - self.expr_as_operand(block, scope, expr) - } - + /// /// Like `as_local_call_operand`, except that the argument will /// not be valid once `scope` ends. - fn as_call_operand( - &mut self, - block: BasicBlock, - scope: Option, - expr: M, - ) -> BlockAnd> - where - M: Mirror<'tcx, Output = Expr<'tcx>>, - { - let expr = self.hir.mirror(expr); - self.expr_as_call_operand(block, scope, expr) - } - - fn expr_as_operand( + crate fn as_operand( &mut self, mut block: BasicBlock, scope: Option, - expr: Expr<'tcx>, + expr: &Expr<'_, 'tcx>, ) -> BlockAnd> { - debug!("expr_as_operand(block={:?}, expr={:?})", block, expr); + debug!("as_operand(block={:?}, expr={:?})", block, expr); let this = self; if let ExprKind::Scope { region_scope, lint_level, value } = expr.kind { @@ -133,7 +106,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { } let category = Category::of(&expr.kind).unwrap(); - debug!("expr_as_operand: category={:?} for={:?}", category, expr.kind); + debug!("as_operand: category={:?} for={:?}", category, expr.kind); match category { Category::Constant => { let constant = this.as_constant(expr); @@ -146,13 +119,13 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { } } - fn expr_as_call_operand( + crate fn as_call_operand( &mut self, mut block: BasicBlock, scope: Option, - expr: Expr<'tcx>, + expr: &Expr<'_, 'tcx>, ) -> BlockAnd> { - debug!("expr_as_call_operand(block={:?}, expr={:?})", block, expr); + debug!("as_call_operand(block={:?}, expr={:?})", block, expr); let this = self; if let ExprKind::Scope { region_scope, lint_level, value } = expr.kind { @@ -163,12 +136,12 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { }); } - let tcx = this.hir.tcx(); + let tcx = this.tcx; if tcx.features().unsized_fn_params { let ty = expr.ty; let span = expr.span; - let param_env = this.hir.param_env; + let param_env = this.param_env; if !ty.is_sized(tcx.at(span), param_env) { // !sized means !copy, so this is an unsized move @@ -176,9 +149,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { // As described above, detect the case where we are passing a value of unsized // type, and that value is coming from the deref of a box. - if let ExprKind::Deref { ref arg } = expr.kind { - let arg = this.hir.mirror(arg.clone()); - + if let ExprKind::Deref { arg } = expr.kind { // Generate let tmp0 = arg0 let operand = unpack!(block = this.as_temp(block, scope, arg, Mutability::Mut)); @@ -193,6 +164,6 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { } } - this.expr_as_operand(block, scope, expr) + this.as_operand(block, scope, expr) } } 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 3308a243a3afb..1053890e61837 100644 --- a/compiler/rustc_mir_build/src/build/expr/as_place.rs +++ b/compiler/rustc_mir_build/src/build/expr/as_place.rs @@ -10,14 +10,17 @@ use rustc_middle::hir::place::ProjectionKind as HirProjectionKind; use rustc_middle::middle::region; use rustc_middle::mir::AssertKind::BoundsCheck; use rustc_middle::mir::*; +use rustc_middle::ty::AdtDef; use rustc_middle::ty::{self, CanonicalUserTypeAnnotation, Ty, TyCtxt, Variance}; use rustc_span::Span; use rustc_target::abi::VariantIdx; use rustc_index::vec::Idx; +use std::iter; + /// The "outermost" place that holds this value. -#[derive(Copy, Clone)] +#[derive(Copy, Clone, Debug, PartialEq)] crate enum PlaceBase { /// Denotes the start of a `Place`. Local(Local), @@ -67,7 +70,7 @@ crate enum PlaceBase { /// /// This is used internally when building a place for an expression like `a.b.c`. The fields `b` /// and `c` can be progressively pushed onto the place builder that is created when converting `a`. -#[derive(Clone)] +#[derive(Clone, Debug, PartialEq)] crate struct PlaceBuilder<'tcx> { base: PlaceBase, projection: Vec>, @@ -77,26 +80,29 @@ crate struct PlaceBuilder<'tcx> { /// The projections are truncated to represent a path that might be captured by a /// closure/generator. This implies the vector returned from this function doesn't contain /// ProjectionElems `Downcast`, `ConstantIndex`, `Index`, or `Subslice` because those will never be -/// part of a path that is captued by a closure. We stop applying projections once we see the first +/// part of a path that is captured by a closure. We stop applying projections once we see the first /// projection that isn't captured by a closure. fn convert_to_hir_projections_and_truncate_for_capture<'tcx>( mir_projections: &[PlaceElem<'tcx>], ) -> Vec { let mut hir_projections = Vec::new(); + let mut variant = None; for mir_projection in mir_projections { let hir_projection = match mir_projection { ProjectionElem::Deref => HirProjectionKind::Deref, ProjectionElem::Field(field, _) => { - // We will never encouter this for multivariant enums, - // read the comment for `Downcast`. - HirProjectionKind::Field(field.index() as u32, VariantIdx::new(0)) + let variant = variant.unwrap_or(VariantIdx::new(0)); + HirProjectionKind::Field(field.index() as u32, variant) } - ProjectionElem::Downcast(..) => { - // This projections exist only for enums that have - // multiple variants. Since such enums that are captured - // completely, we can stop here. - break; + ProjectionElem::Downcast(.., idx) => { + // We don't expect to see multi-variant enums here, as earlier + // phases will have truncated them already. However, there can + // still be downcasts, thanks to single-variant enums. + // We keep track of VariantIdx so we can use this information + // if the next ProjectionElem is a Field. + variant = Some(*idx); + continue; } ProjectionElem::Index(..) | ProjectionElem::ConstantIndex { .. } @@ -106,7 +112,7 @@ fn convert_to_hir_projections_and_truncate_for_capture<'tcx>( break; } }; - + variant = None; hir_projections.push(hir_projection); } @@ -136,7 +142,7 @@ fn is_ancestor_or_same_capture( return false; } - proj_possible_ancestor.iter().zip(proj_capture).all(|(a, b)| a == b) + iter::zip(proj_possible_ancestor, proj_capture).all(|(a, b)| a == b) } /// Computes the index of a capture within the desugared closure provided the closure's @@ -194,12 +200,12 @@ fn find_capture_matching_projections<'a, 'tcx>( /// Takes a PlaceBuilder and resolves the upvar (if any) within it, so that the /// `PlaceBuilder` now starts from `PlaceBase::Local`. /// -/// Returns a Result with the error being the HirId of the Upvar that was not found. +/// Returns a Result with the error being the PlaceBuilder (`from_builder`) that was not found. fn to_upvars_resolved_place_builder<'a, 'tcx>( from_builder: PlaceBuilder<'tcx>, tcx: TyCtxt<'tcx>, typeck_results: &'a ty::TypeckResults<'tcx>, -) -> Result, HirId> { +) -> Result, PlaceBuilder<'tcx>> { match from_builder.base { PlaceBase::Local(_) => Ok(from_builder), PlaceBase::Upvar { var_hir_id, closure_def_id, closure_kind } => { @@ -230,13 +236,12 @@ fn to_upvars_resolved_place_builder<'a, 'tcx>( from_builder.projection ) } else { - // FIXME(project-rfc-2229#24): Handle this case properly debug!( "No associated capture found for {:?}[{:#?}]", var_hir_id, from_builder.projection, ); } - return Err(var_hir_id); + return Err(from_builder); }; let closure_ty = typeck_results @@ -300,6 +305,25 @@ impl<'tcx> PlaceBuilder<'tcx> { to_upvars_resolved_place_builder(self, tcx, typeck_results).unwrap() } + /// Attempts to resolve the `PlaceBuilder`. + /// On success, it will return the resolved `PlaceBuilder`. + /// On failure, it will return itself. + /// + /// Upvars resolve may fail for a `PlaceBuilder` when attempting to + /// resolve a disjoint field whose root variable is not captured + /// (destructured assignments) or when attempting to resolve a root + /// variable (discriminant matching with only wildcard arm) that is + /// not captured. This can happen because the final mir that will be + /// generated doesn't require a read for this place. Failures will only + /// happen inside closures. + crate fn try_upvars_resolved<'a>( + self, + tcx: TyCtxt<'tcx>, + typeck_results: &'a ty::TypeckResults<'tcx>, + ) -> Result, PlaceBuilder<'tcx>> { + to_upvars_resolved_place_builder(self, tcx, typeck_results) + } + crate fn base(&self) -> PlaceBase { self.base } @@ -308,15 +332,22 @@ impl<'tcx> PlaceBuilder<'tcx> { self.project(PlaceElem::Field(f, ty)) } - fn deref(self) -> Self { + crate fn deref(self) -> Self { self.project(PlaceElem::Deref) } + 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, + )) + } + fn index(self, index: Local) -> Self { self.project(PlaceElem::Index(index)) } - fn project(mut self, elem: PlaceElem<'tcx>) -> Self { + crate fn project(mut self, elem: PlaceElem<'tcx>) -> Self { self.projection.push(elem); self } @@ -347,25 +378,22 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { /// Extra care is needed if any user code is allowed to run between calling /// this method and using it, as is the case for `match` and index /// expressions. - crate fn as_place(&mut self, mut block: BasicBlock, expr: M) -> BlockAnd> - where - M: Mirror<'tcx, Output = Expr<'tcx>>, - { + crate fn as_place( + &mut self, + mut block: BasicBlock, + expr: &Expr<'_, 'tcx>, + ) -> BlockAnd> { let place_builder = unpack!(block = self.as_place_builder(block, expr)); - block.and(place_builder.into_place(self.hir.tcx(), self.hir.typeck_results())) + block.and(place_builder.into_place(self.tcx, self.typeck_results)) } /// This is used when constructing a compound `Place`, so that we can avoid creating /// intermediate `Place` values until we know the full set of projections. - crate fn as_place_builder( + crate fn as_place_builder( &mut self, block: BasicBlock, - expr: M, - ) -> BlockAnd> - where - M: Mirror<'tcx, Output = Expr<'tcx>>, - { - let expr = self.hir.mirror(expr); + expr: &Expr<'_, 'tcx>, + ) -> BlockAnd> { self.expr_as_place(block, expr, Mutability::Mut, None) } @@ -374,16 +402,13 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { /// place. The place itself may or may not be mutable: /// * If this expr is a place expr like a.b, then we will return that place. /// * Otherwise, a temporary is created: in that event, it will be an immutable temporary. - crate fn as_read_only_place( + crate fn as_read_only_place( &mut self, mut block: BasicBlock, - expr: M, - ) -> BlockAnd> - where - M: Mirror<'tcx, Output = Expr<'tcx>>, - { + expr: &Expr<'_, 'tcx>, + ) -> BlockAnd> { let place_builder = unpack!(block = self.as_read_only_place_builder(block, expr)); - block.and(place_builder.into_place(self.hir.tcx(), self.hir.typeck_results())) + block.and(place_builder.into_place(self.tcx, self.typeck_results)) } /// This is used when constructing a compound `Place`, so that we can avoid creating @@ -392,22 +417,18 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { /// place. The place itself may or may not be mutable: /// * If this expr is a place expr like a.b, then we will return that place. /// * Otherwise, a temporary is created: in that event, it will be an immutable temporary. - fn as_read_only_place_builder( + fn as_read_only_place_builder( &mut self, block: BasicBlock, - expr: M, - ) -> BlockAnd> - where - M: Mirror<'tcx, Output = Expr<'tcx>>, - { - let expr = self.hir.mirror(expr); + expr: &Expr<'_, 'tcx>, + ) -> BlockAnd> { self.expr_as_place(block, expr, Mutability::Not, None) } fn expr_as_place( &mut self, mut block: BasicBlock, - expr: Expr<'tcx>, + expr: &Expr<'_, 'tcx>, mutability: Mutability, fake_borrow_temps: Option<&mut Vec>, ) -> BlockAnd> { @@ -419,18 +440,15 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { match expr.kind { ExprKind::Scope { region_scope, lint_level, value } => { this.in_scope((region_scope, source_info), lint_level, |this| { - let value = this.hir.mirror(value); this.expr_as_place(block, value, mutability, fake_borrow_temps) }) } ExprKind::Field { lhs, name } => { - let lhs = this.hir.mirror(lhs); let place_builder = unpack!(block = this.expr_as_place(block, lhs, mutability, fake_borrow_temps,)); block.and(place_builder.field(name, expr.ty)) } ExprKind::Deref { arg } => { - let arg = this.hir.mirror(arg); let place_builder = unpack!(block = this.expr_as_place(block, arg, mutability, fake_borrow_temps,)); block.and(place_builder.deref()) @@ -462,7 +480,6 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { } ExprKind::PlaceTypeAscription { source, user_ty } => { - let source = this.hir.mirror(source); let place_builder = unpack!( block = this.expr_as_place(block, source, mutability, fake_borrow_temps,) ); @@ -474,8 +491,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { inferred_ty: expr.ty, }); - let place = - place_builder.clone().into_place(this.hir.tcx(), this.hir.typeck_results()); + let place = place_builder.clone().into_place(this.tcx, this.typeck_results); this.cfg.push( block, Statement { @@ -493,7 +509,6 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { block.and(place_builder) } ExprKind::ValueTypeAscription { source, user_ty } => { - let source = this.hir.mirror(source); let temp = unpack!(block = this.as_temp(block, source.temp_lifetime, source, mutability)); if let Some(user_ty) = user_ty { @@ -563,19 +578,18 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { /// Lower a captured upvar. Note we might not know the actual capture index, /// so we create a place starting from `PlaceBase::Upvar`, which will be resolved - /// once all projections that allow us to indentify a capture have been applied. + /// once all projections that allow us to identify a capture have been applied. fn lower_captured_upvar( &mut self, block: BasicBlock, upvar_id: ty::UpvarId, ) -> BlockAnd> { let closure_ty = self - .hir - .typeck_results() - .node_type(self.hir.tcx().hir().local_def_id_to_hir_id(upvar_id.closure_expr_id)); + .typeck_results + .node_type(self.tcx.hir().local_def_id_to_hir_id(upvar_id.closure_expr_id)); let closure_kind = if let ty::Closure(_, closure_substs) = closure_ty.kind() { - self.hir.infcx().closure_kind(closure_substs).unwrap() + self.infcx.closure_kind(closure_substs).unwrap() } else { // Generators are considered FnOnce. ty::ClosureKind::FnOnce @@ -599,41 +613,32 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { fn lower_index_expression( &mut self, mut block: BasicBlock, - base: ExprRef<'tcx>, - index: ExprRef<'tcx>, + base: &Expr<'_, 'tcx>, + index: &Expr<'_, 'tcx>, mutability: Mutability, fake_borrow_temps: Option<&mut Vec>, temp_lifetime: Option, expr_span: Span, source_info: SourceInfo, ) -> BlockAnd> { - let lhs = self.hir.mirror(base); - let base_fake_borrow_temps = &mut Vec::new(); let is_outermost_index = fake_borrow_temps.is_none(); let fake_borrow_temps = fake_borrow_temps.unwrap_or(base_fake_borrow_temps); let mut base_place = - unpack!(block = self.expr_as_place(block, lhs, mutability, Some(fake_borrow_temps),)); + unpack!(block = self.expr_as_place(block, base, mutability, Some(fake_borrow_temps),)); // Making this a *fresh* temporary means we do not have to worry about // the index changing later: Nothing will ever change this temporary. // The "retagging" transformation (for Stacked Borrows) relies on this. let idx = unpack!(block = self.as_temp(block, temp_lifetime, index, Mutability::Not,)); - block = self.bounds_check( - block, - base_place.clone().into_place(self.hir.tcx(), self.hir.typeck_results()), - idx, - expr_span, - source_info, - ); + block = self.bounds_check(block, base_place.clone(), idx, expr_span, source_info); if is_outermost_index { self.read_fake_borrows(block, fake_borrow_temps, source_info) } else { - base_place = - base_place.expect_upvars_resolved(self.hir.tcx(), self.hir.typeck_results()); + base_place = base_place.expect_upvars_resolved(self.tcx, self.typeck_results); self.add_fake_borrows_of_base( &base_place, block, @@ -649,25 +654,33 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { fn bounds_check( &mut self, block: BasicBlock, - slice: Place<'tcx>, + slice: PlaceBuilder<'tcx>, index: Local, expr_span: Span, source_info: SourceInfo, ) -> BasicBlock { - let usize_ty = self.hir.usize_ty(); - let bool_ty = self.hir.bool_ty(); + let usize_ty = self.tcx.types.usize; + let bool_ty = self.tcx.types.bool; // bounds check: let len = self.temp(usize_ty, expr_span); let lt = self.temp(bool_ty, expr_span); // len = len(slice) - self.cfg.push_assign(block, source_info, len, Rvalue::Len(slice)); + self.cfg.push_assign( + block, + source_info, + len, + Rvalue::Len(slice.into_place(self.tcx, self.typeck_results)), + ); // lt = idx < len self.cfg.push_assign( block, source_info, lt, - Rvalue::BinaryOp(BinOp::Lt, Operand::Copy(Place::from(index)), Operand::Copy(len)), + Rvalue::BinaryOp( + BinOp::Lt, + box (Operand::Copy(Place::from(index)), Operand::Copy(len)), + ), ); let msg = BoundsCheck { len: Operand::Move(len), index: Operand::Copy(Place::from(index)) }; // assert!(lt, "...") @@ -682,7 +695,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { expr_span: Span, source_info: SourceInfo, ) { - let tcx = self.hir.tcx(); + let tcx = self.tcx; let local = match base_place.base { PlaceBase::Local(local) => local, PlaceBase::Upvar { .. } => bug!("Expected PlacseBase::Local found Upvar"), diff --git a/compiler/rustc_mir_build/src/build/expr/as_rvalue.rs b/compiler/rustc_mir_build/src/build/expr/as_rvalue.rs index e602f4dd71d25..822fbd91c947e 100644 --- a/compiler/rustc_mir_build/src/build/expr/as_rvalue.rs +++ b/compiler/rustc_mir_build/src/build/expr/as_rvalue.rs @@ -8,6 +8,7 @@ use crate::build::{BlockAnd, BlockAndExtension, Builder}; use crate::thir::*; use rustc_middle::middle::region; use rustc_middle::mir::AssertKind; +use rustc_middle::mir::Place; use rustc_middle::mir::*; use rustc_middle::ty::{self, Ty, UpvarSubsts}; use rustc_span::Span; @@ -19,33 +20,21 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { /// The operand returned from this function will *not be valid* after /// an ExprKind::Scope is passed, so please do *not* return it from /// functions to avoid bad miscompiles. - crate fn as_local_rvalue(&mut self, block: BasicBlock, expr: M) -> BlockAnd> - where - M: Mirror<'tcx, Output = Expr<'tcx>>, - { + crate fn as_local_rvalue( + &mut self, + block: BasicBlock, + expr: &Expr<'_, 'tcx>, + ) -> BlockAnd> { let local_scope = self.local_scope(); self.as_rvalue(block, Some(local_scope), expr) } /// Compile `expr`, yielding an rvalue. - fn as_rvalue( - &mut self, - block: BasicBlock, - scope: Option, - expr: M, - ) -> BlockAnd> - where - M: Mirror<'tcx, Output = Expr<'tcx>>, - { - let expr = self.hir.mirror(expr); - self.expr_as_rvalue(block, scope, expr) - } - - fn expr_as_rvalue( + crate fn as_rvalue( &mut self, mut block: BasicBlock, scope: Option, - expr: Expr<'tcx>, + expr: &Expr<'_, 'tcx>, ) -> BlockAnd> { debug!("expr_as_rvalue(block={:?}, scope={:?}, expr={:?})", block, scope, expr); @@ -71,8 +60,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { ExprKind::Unary { op, arg } => { let arg = unpack!(block = this.as_operand(block, scope, arg)); // Check for -MIN on signed integers - if this.hir.check_overflow() && op == UnOp::Neg && expr.ty.is_signed() { - let bool_ty = this.hir.bool_ty(); + if this.check_overflow && op == UnOp::Neg && expr.ty.is_signed() { + let bool_ty = this.tcx.types.bool; let minval = this.minval_literal(expr_span, expr.ty); let is_min = this.temp(bool_ty, expr_span); @@ -81,7 +70,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { block, source_info, is_min, - Rvalue::BinaryOp(BinOp::Eq, arg.to_copy(), minval), + Rvalue::BinaryOp(BinOp::Eq, box (arg.to_copy(), minval)), ); block = this.assert( @@ -95,7 +84,6 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { block.and(Rvalue::UnaryOp(op, arg)) } ExprKind::Box { value } => { - let value = this.hir.mirror(value); // The `Box` temporary created here is not a part of the HIR, // and therefore is not considered during generator auto-trait // determination. See the comment about `box` at `yield_in_scope`. @@ -115,8 +103,11 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { // initialize the box contents: unpack!( - block = - this.into(this.hir.tcx().mk_place_deref(Place::from(result)), block, value) + block = this.expr_into_dest( + this.tcx.mk_place_deref(Place::from(result)), + block, + value + ) ); block.and(Rvalue::Use(Operand::Move(Place::from(result)))) } @@ -156,7 +147,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { // to the same MIR as `let x = ();`. // first process the set of fields - let el_ty = expr.ty.sequence_element_type(this.hir.tcx()); + let el_ty = expr.ty.sequence_element_type(this.tcx); let fields: Vec<_> = fields .into_iter() .map(|f| unpack!(block = this.as_operand(block, scope, f))) @@ -174,12 +165,41 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { block.and(Rvalue::Aggregate(box AggregateKind::Tuple, fields)) } - ExprKind::Closure { closure_id, substs, upvars, movability } => { + ExprKind::Closure { closure_id, substs, upvars, movability, ref fake_reads } => { + // Convert the closure fake reads, if any, from `ExprRef` to mir `Place` + // and push the fake reads. + // This must come before creating the operands. This is required in case + // there is a fake read and a borrow of the same path, since otherwise the + // fake read might interfere with the borrow. Consider an example like this + // one: + // ``` + // let mut x = 0; + // let c = || { + // &mut x; // mutable borrow of `x` + // match x { _ => () } // fake read of `x` + // }; + // ``` + for (thir_place, cause, hir_id) in fake_reads.into_iter() { + let place_builder = unpack!(block = this.as_place_builder(block, thir_place)); + + if let Ok(place_builder_resolved) = + place_builder.try_upvars_resolved(this.tcx, this.typeck_results) + { + let mir_place = + place_builder_resolved.into_place(this.tcx, this.typeck_results); + this.cfg.push_fake_read( + block, + this.source_info(this.tcx.hir().span(*hir_id)), + *cause, + mir_place, + ); + } + } + // see (*) above let operands: Vec<_> = upvars .into_iter() .map(|upvar| { - let upvar = this.hir.mirror(upvar); match Category::of(&upvar.kind) { // Use as_place to avoid creating a temporary when // moving a variable into a closure, so that @@ -214,6 +234,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { } }) .collect(); + let result = match substs { UpvarSubsts::Generator(substs) => { // We implicitly set the discriminant to 0. See @@ -230,7 +251,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { block.and(Rvalue::Use(Operand::Constant(box Constant { span: expr_span, user_ty: None, - literal: ty::Const::zero_sized(this.hir.tcx(), this.hir.tcx().types.unit), + literal: ty::Const::zero_sized(this.tcx, this.tcx.types.unit).into(), }))) } ExprKind::Yield { .. } @@ -282,21 +303,21 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { rhs: Operand<'tcx>, ) -> BlockAnd> { let source_info = self.source_info(span); - let bool_ty = self.hir.bool_ty(); - if self.hir.check_overflow() && op.is_checkable() && ty.is_integral() { - let result_tup = self.hir.tcx().intern_tup(&[ty, bool_ty]); + let bool_ty = self.tcx.types.bool; + if self.check_overflow && op.is_checkable() && ty.is_integral() { + let result_tup = self.tcx.intern_tup(&[ty, bool_ty]); let result_value = self.temp(result_tup, span); self.cfg.push_assign( block, source_info, result_value, - Rvalue::CheckedBinaryOp(op, lhs.to_copy(), rhs.to_copy()), + Rvalue::CheckedBinaryOp(op, box (lhs.to_copy(), rhs.to_copy())), ); let val_fld = Field::new(0); let of_fld = Field::new(1); - let tcx = self.hir.tcx(); + let tcx = self.tcx; let val = tcx.mk_place_field(result_value, val_fld, ty); let of = tcx.mk_place_field(result_value, of_fld, bool_ty); @@ -324,7 +345,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { block, source_info, is_zero, - Rvalue::BinaryOp(BinOp::Eq, rhs.to_copy(), zero), + Rvalue::BinaryOp(BinOp::Eq, box (rhs.to_copy(), zero)), ); block = self.assert(block, Operand::Move(is_zero), false, zero_err, span); @@ -345,13 +366,13 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { block, source_info, is_neg_1, - Rvalue::BinaryOp(BinOp::Eq, rhs.to_copy(), neg_1), + Rvalue::BinaryOp(BinOp::Eq, box (rhs.to_copy(), neg_1)), ); self.cfg.push_assign( block, source_info, is_min, - Rvalue::BinaryOp(BinOp::Eq, lhs.to_copy(), min), + Rvalue::BinaryOp(BinOp::Eq, box (lhs.to_copy(), min)), ); let is_neg_1 = Operand::Move(is_neg_1); @@ -360,14 +381,14 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { block, source_info, of, - Rvalue::BinaryOp(BinOp::BitAnd, is_neg_1, is_min), + Rvalue::BinaryOp(BinOp::BitAnd, box (is_neg_1, is_min)), ); block = self.assert(block, Operand::Move(of), false, overflow_err, span); } } - block.and(Rvalue::BinaryOp(op, lhs, rhs)) + block.and(Rvalue::BinaryOp(op, box (lhs, rhs))) } } @@ -377,7 +398,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { upvar_ty: Ty<'tcx>, temp_lifetime: Option, mut block: BasicBlock, - arg: ExprRef<'tcx>, + arg: &Expr<'_, 'tcx>, ) -> BlockAnd> { let this = self; @@ -398,7 +419,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { // is same as that of the capture in the parent closure. PlaceBase::Upvar { .. } => { let enclosing_upvars_resolved = - arg_place_builder.clone().into_place(this.hir.tcx(), this.hir.typeck_results()); + arg_place_builder.clone().into_place(this.tcx, this.typeck_results); match enclosing_upvars_resolved.as_ref() { PlaceRef { @@ -435,13 +456,13 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { Mutability::Mut => BorrowKind::Mut { allow_two_phase_borrow: false }, }; - let arg_place = arg_place_builder.into_place(this.hir.tcx(), this.hir.typeck_results()); + let arg_place = arg_place_builder.into_place(this.tcx, this.typeck_results); this.cfg.push_assign( block, source_info, Place::from(temp), - Rvalue::Ref(this.hir.tcx().lifetimes.re_erased, borrow_kind, arg_place), + Rvalue::Ref(this.tcx.lifetimes.re_erased, borrow_kind, arg_place), ); // See the comment in `expr_as_temp` and on the `rvalue_scopes` field for why @@ -456,9 +477,9 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { // Helper to get a `-1` value of the appropriate type fn neg_1_literal(&mut self, span: Span, ty: Ty<'tcx>) -> Operand<'tcx> { let param_ty = ty::ParamEnv::empty().and(ty); - let bits = self.hir.tcx().layout_of(param_ty).unwrap().size.bits(); + let bits = self.tcx.layout_of(param_ty).unwrap().size.bits(); let n = (!0u128) >> (128 - bits); - let literal = ty::Const::from_bits(self.hir.tcx(), n, param_ty); + let literal = ty::Const::from_bits(self.tcx, n, param_ty); self.literal_operand(span, literal) } @@ -467,9 +488,9 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { fn minval_literal(&mut self, span: Span, ty: Ty<'tcx>) -> Operand<'tcx> { assert!(ty.is_signed()); let param_ty = ty::ParamEnv::empty().and(ty); - let bits = self.hir.tcx().layout_of(param_ty).unwrap().size.bits(); + let bits = self.tcx.layout_of(param_ty).unwrap().size.bits(); let n = 1 << (bits - 1); - let literal = ty::Const::from_bits(self.hir.tcx(), n, param_ty); + let literal = ty::Const::from_bits(self.tcx, n, param_ty); self.literal_operand(span, literal) } diff --git a/compiler/rustc_mir_build/src/build/expr/as_temp.rs b/compiler/rustc_mir_build/src/build/expr/as_temp.rs index 9984b527ffdb4..98b910ab21c16 100644 --- a/compiler/rustc_mir_build/src/build/expr/as_temp.rs +++ b/compiler/rustc_mir_build/src/build/expr/as_temp.rs @@ -4,40 +4,34 @@ use crate::build::scope::DropKind; use crate::build::{BlockAnd, BlockAndExtension, Builder}; use crate::thir::*; use rustc_data_structures::stack::ensure_sufficient_stack; -use rustc_hir as hir; use rustc_middle::middle::region; use rustc_middle::mir::*; impl<'a, 'tcx> Builder<'a, 'tcx> { /// Compile `expr` into a fresh temporary. This is used when building /// up rvalues so as to freeze the value that will be consumed. - crate fn as_temp( + crate fn as_temp( &mut self, block: BasicBlock, temp_lifetime: Option, - expr: M, + expr: &Expr<'_, 'tcx>, mutability: Mutability, - ) -> BlockAnd - where - M: Mirror<'tcx, Output = Expr<'tcx>>, - { - let expr = self.hir.mirror(expr); - // + ) -> BlockAnd { // this is the only place in mir building that we need to truly need to worry about // infinite recursion. Everything else does recurse, too, but it always gets broken up // at some point by inserting an intermediate temporary - ensure_sufficient_stack(|| self.expr_as_temp(block, temp_lifetime, expr, mutability)) + ensure_sufficient_stack(|| self.as_temp_inner(block, temp_lifetime, expr, mutability)) } - fn expr_as_temp( + fn as_temp_inner( &mut self, mut block: BasicBlock, temp_lifetime: Option, - expr: Expr<'tcx>, + expr: &Expr<'_, 'tcx>, mutability: Mutability, ) -> BlockAnd { debug!( - "expr_as_temp(block={:?}, temp_lifetime={:?}, expr={:?}, mutability={:?})", + "as_temp(block={:?}, temp_lifetime={:?}, expr={:?}, mutability={:?})", block, temp_lifetime, expr, mutability ); let this = self; @@ -65,13 +59,13 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { } match expr.kind { ExprKind::StaticRef { def_id, .. } => { - assert!(!this.hir.tcx().is_thread_local_static(def_id)); + assert!(!this.tcx.is_thread_local_static(def_id)); local_decl.internal = true; local_decl.local_info = Some(box LocalInfo::StaticRef { def_id, is_thread_local: false }); } ExprKind::ThreadLocalRef(def_id) => { - assert!(this.hir.tcx().is_thread_local_static(def_id)); + assert!(this.tcx.is_thread_local_static(def_id)); local_decl.internal = true; local_decl.local_info = Some(box LocalInfo::StaticRef { def_id, is_thread_local: true }); @@ -89,7 +83,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { // Don't bother with StorageLive and Dead for these temporaries, // they are never assigned. ExprKind::Break { .. } | ExprKind::Continue { .. } | ExprKind::Return { .. } => (), - ExprKind::Block { body: hir::Block { expr: None, targeted_by_break: false, .. } } + ExprKind::Block { body: Block { expr: None, targeted_by_break: false, .. } } if expr_ty.is_never() => {} _ => { this.cfg @@ -114,7 +108,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { } } - unpack!(block = this.into(temp_place, block, expr)); + unpack!(block = this.expr_into_dest(temp_place, block, expr)); if let Some(temp_lifetime) = temp_lifetime { this.schedule_drop(expr_span, temp_lifetime, temp, DropKind::Value); diff --git a/compiler/rustc_mir_build/src/build/expr/category.rs b/compiler/rustc_mir_build/src/build/expr/category.rs index 9320b5810e396..0cadfa2f0a1af 100644 --- a/compiler/rustc_mir_build/src/build/expr/category.rs +++ b/compiler/rustc_mir_build/src/build/expr/category.rs @@ -31,7 +31,7 @@ crate enum RvalueFunc { /// Determines the category for a given expression. Note that scope /// and paren expressions have no category. impl Category { - crate fn of(ek: &ExprKind<'_>) -> Option { + crate fn of(ek: &ExprKind<'_, '_>) -> Option { match *ek { ExprKind::Scope { .. } => None, diff --git a/compiler/rustc_mir_build/src/build/expr/into.rs b/compiler/rustc_mir_build/src/build/expr/into.rs index 235fe14cbf9fd..1e7ed3d95d236 100644 --- a/compiler/rustc_mir_build/src/build/expr/into.rs +++ b/compiler/rustc_mir_build/src/build/expr/into.rs @@ -7,19 +7,21 @@ use rustc_ast::InlineAsmOptions; use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::stack::ensure_sufficient_stack; use rustc_hir as hir; +use rustc_index::vec::Idx; use rustc_middle::mir::*; -use rustc_middle::ty::CanonicalUserTypeAnnotation; +use rustc_middle::ty::{self, CanonicalUserTypeAnnotation}; +use std::iter; impl<'a, 'tcx> Builder<'a, 'tcx> { /// Compile `expr`, storing the result into `destination`, which /// is assumed to be uninitialized. - crate fn into_expr( + crate fn expr_into_dest( &mut self, destination: Place<'tcx>, mut block: BasicBlock, - expr: Expr<'tcx>, + expr: &Expr<'_, 'tcx>, ) -> BlockAnd<()> { - debug!("into_expr(destination={:?}, block={:?}, expr={:?})", destination, block, expr); + debug!("expr_into_dest(destination={:?}, block={:?}, expr={:?})", destination, block, expr); // since we frequently have to reference `self` from within a // closure, where `self` would be shadowed, it's easier to @@ -40,11 +42,11 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { let region_scope = (region_scope, source_info); ensure_sufficient_stack(|| { this.in_scope(region_scope, lint_level, |this| { - this.into(destination, block, value) + this.expr_into_dest(destination, block, value) }) }) } - ExprKind::Block { body: ast_block } => { + ExprKind::Block { body: ref ast_block } => { this.ast_block(destination, block, ast_block, source_info) } ExprKind::Match { scrutinee, arms } => { @@ -58,17 +60,17 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { let mut then_block = this.cfg.start_new_block(); let mut else_block = this.cfg.start_new_block(); - let term = TerminatorKind::if_(this.hir.tcx(), operand, then_block, else_block); + let term = TerminatorKind::if_(this.tcx, operand, then_block, else_block); this.cfg.terminate(block, source_info, term); - unpack!(then_block = this.into(destination, then_block, then)); + unpack!(then_block = this.expr_into_dest(destination, then_block, then)); else_block = if let Some(else_opt) = else_opt { - unpack!(this.into(destination, else_block, else_opt)) + unpack!(this.expr_into_dest(destination, else_block, else_opt)) } else { // Body of the `if` expression without an `else` clause must return `()`, thus // we implicitly generate a `else {}` if it is not specified. let correct_si = this.source_info(expr_span.shrink_to_hi()); - this.cfg.push_assign_unit(else_block, correct_si, destination, this.hir.tcx()); + this.cfg.push_assign_unit(else_block, correct_si, destination, this.tcx); else_block }; @@ -87,7 +89,6 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { join_block.unit() } ExprKind::NeverToAny { source } => { - let source = this.hir.mirror(source); let is_call = matches!(source.kind, ExprKind::Call { .. } | ExprKind::InlineAsm { .. }); @@ -110,18 +111,17 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { ExprKind::LogicalOp { op, lhs, rhs } => { // And: // - // [block: If(lhs)] -true-> [else_block: If(rhs)] -true-> [true_block] - // | | (false) - // +----------false-----------+------------------> [false_block] + // [block: If(lhs)] -true-> [else_block: dest = (rhs)] + // | (false) + // [shortcurcuit_block: dest = false] // // Or: // - // [block: If(lhs)] -false-> [else_block: If(rhs)] -true-> [true_block] - // | (true) | (false) - // [true_block] [false_block] + // [block: If(lhs)] -false-> [else_block: dest = (rhs)] + // | (true) + // [shortcurcuit_block: dest = true] - let (true_block, false_block, mut else_block, join_block) = ( - this.cfg.start_new_block(), + let (shortcircuit_block, mut else_block, join_block) = ( this.cfg.start_new_block(), this.cfg.start_new_block(), this.cfg.start_new_block(), @@ -129,33 +129,31 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { let lhs = unpack!(block = this.as_local_operand(block, lhs)); let blocks = match op { - LogicalOp::And => (else_block, false_block), - LogicalOp::Or => (true_block, else_block), + LogicalOp::And => (else_block, shortcircuit_block), + LogicalOp::Or => (shortcircuit_block, else_block), }; - let term = TerminatorKind::if_(this.hir.tcx(), lhs, blocks.0, blocks.1); + let term = TerminatorKind::if_(this.tcx, lhs, blocks.0, blocks.1); this.cfg.terminate(block, source_info, term); - let rhs = unpack!(else_block = this.as_local_operand(else_block, rhs)); - let term = TerminatorKind::if_(this.hir.tcx(), rhs, true_block, false_block); - this.cfg.terminate(else_block, source_info, term); - this.cfg.push_assign_constant( - true_block, + shortcircuit_block, source_info, destination, - Constant { span: expr_span, user_ty: None, literal: this.hir.true_literal() }, + Constant { + span: expr_span, + user_ty: None, + literal: match op { + LogicalOp::And => ty::Const::from_bool(this.tcx, false).into(), + LogicalOp::Or => ty::Const::from_bool(this.tcx, true).into(), + }, + }, ); + this.cfg.goto(shortcircuit_block, source_info, join_block); - this.cfg.push_assign_constant( - false_block, - source_info, - destination, - Constant { span: expr_span, user_ty: None, literal: this.hir.false_literal() }, - ); + let rhs = unpack!(else_block = this.as_local_operand(else_block, rhs)); + this.cfg.push_assign(else_block, source_info, destination, Rvalue::Use(rhs)); + this.cfg.goto(else_block, source_info, join_block); - // Link up both branches: - this.cfg.goto(true_block, source_info, join_block); - this.cfg.goto(false_block, source_info, join_block); join_block.unit() } ExprKind::Loop { body } => { @@ -188,7 +186,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { // introduce a unit temporary as the destination for the loop body. let tmp = this.get_unit_temp(); // Execute the body, branching back to the test. - let body_block_end = unpack!(this.into(tmp, body_block, body)); + let body_block_end = unpack!(this.expr_into_dest(tmp, body_block, body)); this.cfg.goto(body_block_end, source_info, loop_block); // Loops are only exited by `break` expressions. @@ -206,7 +204,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { this.record_operands_moved(&args); - debug!("into_expr: fn_span={:?}", fn_span); + debug!("expr_into_dest: fn_span={:?}", fn_span); this.cfg.terminate( block, @@ -230,7 +228,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { this.diverge_from(block); success.unit() } - ExprKind::Use { source } => this.into(destination, block, source), + ExprKind::Use { source } => this.expr_into_dest(destination, block, source), ExprKind::Borrow { arg, borrow_kind } => { // We don't do this in `as_rvalue` because we use `as_place` // for borrow expressions, so we cannot create an `RValue` that @@ -241,8 +239,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { BorrowKind::Shared => unpack!(block = this.as_read_only_place(block, arg)), _ => unpack!(block = this.as_place(block, arg)), }; - let borrow = - Rvalue::Ref(this.hir.tcx().lifetimes.re_erased, borrow_kind, arg_place); + let borrow = Rvalue::Ref(this.tcx.lifetimes.re_erased, borrow_kind, arg_place); this.cfg.push_assign(block, source_info, destination, borrow); block.unit() } @@ -255,7 +252,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { this.cfg.push_assign(block, source_info, destination, address_of); block.unit() } - ExprKind::Adt { adt_def, variant_index, substs, user_ty, fields, base } => { + ExprKind::Adt { adt_def, variant_index, substs, user_ty, fields, ref base } => { // See the notes for `ExprKind::Array` in `as_rvalue` and for // `ExprKind::Borrow` above. let is_union = adt_def.is_union(); @@ -270,7 +267,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { .map(|f| (f.name, unpack!(block = this.as_operand(block, Some(scope), f.expr)))) .collect(); - let field_names = this.hir.all_fields(adt_def, variant_index); + let field_names: Vec<_> = + (0..adt_def.variants[variant_index].fields.len()).map(Field::new).collect(); let fields: Vec<_> = if let Some(FruInfo { base, field_types }) = base { let place_builder = unpack!(block = this.as_place_builder(block, base)); @@ -278,9 +276,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { // MIR does not natively support FRU, so for each // base-supplied field, generate an operand that // reads it from the base. - field_names - .into_iter() - .zip(field_types.into_iter()) + iter::zip(field_names, *field_types) .map(|(n, ty)| match fields_map.get(&n) { Some(v) => v.clone(), None => { @@ -288,7 +284,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { this.consume_by_copy_or_move( place_builder .field(n, ty) - .into_place(this.hir.tcx(), this.hir.typeck_results()), + .into_place(this.tcx, this.typeck_results), ) } }) @@ -325,7 +321,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { use rustc_middle::mir; let operands = operands .into_iter() - .map(|op| match op { + .map(|op| match *op { thir::InlineAsmOperand::In { reg, expr } => mir::InlineAsmOperand::In { reg, value: unpack!(block = this.as_local_operand(block, expr)), @@ -334,7 +330,9 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { mir::InlineAsmOperand::Out { reg, late, - place: expr.map(|expr| unpack!(block = this.as_place(block, expr))), + place: expr + .as_ref() + .map(|expr| unpack!(block = this.as_place(block, expr))), } } thir::InlineAsmOperand::InOut { reg, late, expr } => { @@ -352,14 +350,16 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { reg, late, in_value: unpack!(block = this.as_local_operand(block, in_expr)), - out_place: out_expr.map(|out_expr| { + out_place: out_expr.as_ref().map(|out_expr| { unpack!(block = this.as_place(block, out_expr)) }), } } - thir::InlineAsmOperand::Const { expr } => mir::InlineAsmOperand::Const { - value: unpack!(block = this.as_local_operand(block, expr)), - }, + thir::InlineAsmOperand::Const { value, span } => { + mir::InlineAsmOperand::Const { + value: box Constant { span, user_ty: None, literal: value.into() }, + } + } thir::InlineAsmOperand::SymFn { expr } => { mir::InlineAsmOperand::SymFn { value: box this.as_constant(expr) } } @@ -394,7 +394,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { | ExprKind::AssignOp { .. } | ExprKind::LlvmInlineAsm { .. } => { unpack!(block = this.stmt_expr(block, expr, None)); - this.cfg.push_assign_unit(block, source_info, destination, this.hir.tcx()); + this.cfg.push_assign_unit(block, source_info, destination, this.tcx); block.unit() } @@ -417,7 +417,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { block.unit() } ExprKind::Index { .. } | ExprKind::Deref { .. } | ExprKind::Field { .. } => { - debug_assert!(Category::of(&expr.kind) == Some(Category::Place)); + debug_assert_eq!(Category::of(&expr.kind), Some(Category::Place)); // Create a "fake" temporary variable so that we check that the // value is Sized. Usually, this is caught in type checking, but @@ -426,8 +426,6 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { this.local_decls.push(LocalDecl::new(expr.ty, expr.span)); } - debug_assert!(Category::of(&expr.kind) == Some(Category::Place)); - let place = unpack!(block = this.as_place(block, expr)); let rvalue = Rvalue::Use(this.consume_by_copy_or_move(place)); this.cfg.push_assign(block, source_info, destination, rvalue); diff --git a/compiler/rustc_mir_build/src/build/expr/mod.rs b/compiler/rustc_mir_build/src/build/expr/mod.rs index ac8c7e725e1b4..539de80cab71c 100644 --- a/compiler/rustc_mir_build/src/build/expr/mod.rs +++ b/compiler/rustc_mir_build/src/build/expr/mod.rs @@ -9,7 +9,7 @@ //! a type that is not `Copy`, then using any of these functions will //! "move" the value out of its current home (if any). //! -//! - `into` -- writes the value into a specific location, which +//! - `expr_into_dest` -- writes the value into a specific location, which //! should be uninitialized //! - `as_operand` -- evaluates the value and yields an `Operand`, //! suitable for use as an argument to an `Rvalue` @@ -62,7 +62,7 @@ mod as_constant; mod as_operand; -mod as_place; +pub mod as_place; mod as_rvalue; mod as_temp; mod category; diff --git a/compiler/rustc_mir_build/src/build/expr/stmt.rs b/compiler/rustc_mir_build/src/build/expr/stmt.rs index f117689d940fd..f01315fc5db04 100644 --- a/compiler/rustc_mir_build/src/build/expr/stmt.rs +++ b/compiler/rustc_mir_build/src/build/expr/stmt.rs @@ -13,7 +13,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { crate fn stmt_expr( &mut self, mut block: BasicBlock, - expr: Expr<'tcx>, + expr: &Expr<'_, 'tcx>, statement_scope: Option, ) -> BlockAnd<()> { let this = self; @@ -21,29 +21,25 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { let source_info = this.source_info(expr.span); // Handle a number of expressions that don't need a destination at all. This // avoids needing a mountain of temporary `()` variables. - let expr2 = expr.clone(); match expr.kind { ExprKind::Scope { region_scope, lint_level, value } => { - let value = this.hir.mirror(value); this.in_scope((region_scope, source_info), lint_level, |this| { this.stmt_expr(block, value, statement_scope) }) } ExprKind::Assign { lhs, rhs } => { - let lhs = this.hir.mirror(lhs); - let rhs = this.hir.mirror(rhs); let lhs_span = lhs.span; // Note: we evaluate assignments right-to-left. This // is better for borrowck interaction with overloaded // operators like x[j] = x[i]. - debug!("stmt_expr Assign block_context.push(SubExpr) : {:?}", expr2); + debug!("stmt_expr Assign block_context.push(SubExpr) : {:?}", expr); this.block_context.push(BlockFrame::SubExpr); // Generate better code for things that don't need to be // dropped. - if this.hir.needs_drop(lhs.ty) { + if lhs.ty.needs_drop(this.tcx, this.param_env) { let rhs = unpack!(block = this.as_local_operand(block, rhs)); let lhs = unpack!(block = this.as_place(block, lhs)); unpack!(block = this.build_drop_and_replace(block, lhs_span, lhs, rhs)); @@ -65,10 +61,9 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { // only affects weird things like `x += {x += 1; x}` // -- is that equal to `x + (x + 1)` or `2*(x+1)`? - let lhs = this.hir.mirror(lhs); let lhs_ty = lhs.ty; - debug!("stmt_expr AssignOp block_context.push(SubExpr) : {:?}", expr2); + debug!("stmt_expr AssignOp block_context.push(SubExpr) : {:?}", expr); this.block_context.push(BlockFrame::SubExpr); // As above, RTL. @@ -90,24 +85,27 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { ExprKind::Continue { label } => { this.break_scope(block, None, BreakableTarget::Continue(label), source_info) } - ExprKind::Break { label, value } => { - this.break_scope(block, value, BreakableTarget::Break(label), source_info) - } + ExprKind::Break { label, value } => this.break_scope( + block, + value.as_deref(), + BreakableTarget::Break(label), + source_info, + ), ExprKind::Return { value } => { - this.break_scope(block, value, BreakableTarget::Return, source_info) + this.break_scope(block, value.as_deref(), BreakableTarget::Return, source_info) } ExprKind::LlvmInlineAsm { asm, outputs, inputs } => { - debug!("stmt_expr LlvmInlineAsm block_context.push(SubExpr) : {:?}", expr2); + debug!("stmt_expr LlvmInlineAsm block_context.push(SubExpr) : {:?}", expr); this.block_context.push(BlockFrame::SubExpr); let outputs = outputs .into_iter() - .map(|output| unpack!(block = this.as_place(block, output))) + .map(|output| unpack!(block = this.as_place(block, &output))) .collect::>() .into_boxed_slice(); let inputs = inputs .into_iter() .map(|input| { - (input.span(), unpack!(block = this.as_local_operand(block, input))) + (input.span, unpack!(block = this.as_local_operand(block, &input))) }) .collect::>() .into_boxed_slice(); @@ -140,15 +138,15 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { // it is usually better to focus on `the_value` rather // than the entirety of block(s) surrounding it. let adjusted_span = (|| { - if let ExprKind::Block { body } = expr.kind { + if let ExprKind::Block { body } = &expr.kind { if let Some(tail_expr) = &body.expr { - let mut expr = tail_expr; - while let rustc_hir::ExprKind::Block(subblock, _label) = &expr.kind { - if let Some(subtail_expr) = &subblock.expr { - expr = subtail_expr - } else { - break; - } + let mut expr = &*tail_expr; + while let ExprKind::Block { + body: Block { expr: Some(nested_expr), .. }, + } + | ExprKind::Scope { value: nested_expr, .. } = &expr.kind + { + expr = nested_expr; } this.block_context.push(BlockFrame::TailExpr { tail_result_is_ignored: true, diff --git a/compiler/rustc_mir_build/src/build/into.rs b/compiler/rustc_mir_build/src/build/into.rs deleted file mode 100644 index 7264e495b84fd..0000000000000 --- a/compiler/rustc_mir_build/src/build/into.rs +++ /dev/null @@ -1,55 +0,0 @@ -//! In general, there are a number of things for which it's convenient -//! to just call `builder.into` and have it emit its result into a -//! given location. This is basically for expressions or things that can be -//! wrapped up as expressions (e.g., blocks). To make this ergonomic, we use this -//! latter `EvalInto` trait. - -use crate::build::{BlockAnd, Builder}; -use crate::thir::*; -use rustc_middle::mir::*; - -pub(in crate::build) trait EvalInto<'tcx> { - fn eval_into( - self, - builder: &mut Builder<'_, 'tcx>, - destination: Place<'tcx>, - block: BasicBlock, - ) -> BlockAnd<()>; -} - -impl<'a, 'tcx> Builder<'a, 'tcx> { - crate fn into( - &mut self, - destination: Place<'tcx>, - block: BasicBlock, - expr: E, - ) -> BlockAnd<()> - where - E: EvalInto<'tcx>, - { - expr.eval_into(self, destination, block) - } -} - -impl<'tcx> EvalInto<'tcx> for ExprRef<'tcx> { - fn eval_into( - self, - builder: &mut Builder<'_, 'tcx>, - destination: Place<'tcx>, - block: BasicBlock, - ) -> BlockAnd<()> { - let expr = builder.hir.mirror(self); - builder.into_expr(destination, block, expr) - } -} - -impl<'tcx> EvalInto<'tcx> for Expr<'tcx> { - fn eval_into( - self, - builder: &mut Builder<'_, 'tcx>, - destination: Place<'tcx>, - block: BasicBlock, - ) -> BlockAnd<()> { - builder.into_expr(destination, block, self) - } -} diff --git a/compiler/rustc_mir_build/src/build/matches/mod.rs b/compiler/rustc_mir_build/src/build/matches/mod.rs index fde007ec011ba..0e422dc3c6378 100644 --- a/compiler/rustc_mir_build/src/build/matches/mod.rs +++ b/compiler/rustc_mir_build/src/build/matches/mod.rs @@ -5,6 +5,7 @@ //! This also includes code for pattern bindings in `let` statements and //! function parameters. +use crate::build::expr::as_place::PlaceBuilder; use crate::build::scope::DropKind; use crate::build::ForGuard::{self, OutsideGuard, RefWithinGuard}; use crate::build::{BlockAnd, BlockAndExtension, Builder}; @@ -89,14 +90,14 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { destination: Place<'tcx>, span: Span, mut block: BasicBlock, - scrutinee: ExprRef<'tcx>, - arms: Vec>, + scrutinee: &Expr<'_, 'tcx>, + arms: &[Arm<'_, 'tcx>], ) -> BlockAnd<()> { - let scrutinee_span = scrutinee.span(); + let scrutinee_span = scrutinee.span; let scrutinee_place = unpack!(block = self.lower_scrutinee(block, scrutinee, scrutinee_span,)); - let mut arm_candidates = self.create_match_candidates(scrutinee_place, &arms); + let mut arm_candidates = self.create_match_candidates(scrutinee_place.clone(), &arms); let match_has_guard = arms.iter().any(|arm| arm.guard.is_some()); let mut candidates = @@ -119,10 +120,10 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { fn lower_scrutinee( &mut self, mut block: BasicBlock, - scrutinee: ExprRef<'tcx>, + scrutinee: &Expr<'_, 'tcx>, scrutinee_span: Span, - ) -> BlockAnd> { - let scrutinee_place = unpack!(block = self.as_place(block, scrutinee)); + ) -> BlockAnd> { + let scrutinee_place_builder = unpack!(block = self.as_place_builder(block, scrutinee)); // Matching on a `scrutinee_place` with an uninhabited type doesn't // generate any memory reads by itself, and so if the place "expression" // contains unsafe operations like raw pointer dereferences or union @@ -138,25 +139,31 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { // uninhabited value. If we get never patterns, those will check that // the place is initialized, and so this read would only be used to // check safety. - let cause_matched_place = FakeReadCause::ForMatchedPlace; + let cause_matched_place = FakeReadCause::ForMatchedPlace(None); let source_info = self.source_info(scrutinee_span); - self.cfg.push_fake_read(block, source_info, cause_matched_place, scrutinee_place); - block.and(scrutinee_place) + if let Ok(scrutinee_builder) = + scrutinee_place_builder.clone().try_upvars_resolved(self.tcx, self.typeck_results) + { + let scrutinee_place = scrutinee_builder.into_place(self.tcx, self.typeck_results); + self.cfg.push_fake_read(block, source_info, cause_matched_place, scrutinee_place); + } + + block.and(scrutinee_place_builder) } /// Create the initial `Candidate`s for a `match` expression. fn create_match_candidates<'pat>( &mut self, - scrutinee: Place<'tcx>, - arms: &'pat [Arm<'tcx>], - ) -> Vec<(&'pat Arm<'tcx>, Candidate<'pat, 'tcx>)> { + scrutinee: PlaceBuilder<'tcx>, + arms: &'pat [Arm<'pat, 'tcx>], + ) -> Vec<(&'pat Arm<'pat, 'tcx>, Candidate<'pat, 'tcx>)> { // Assemble a list of candidates: there is one candidate per pattern, // which means there may be more than one candidate *per arm*. arms.iter() .map(|arm| { let arm_has_guard = arm.guard.is_some(); - let arm_candidate = Candidate::new(scrutinee, &arm.pattern, arm_has_guard); + let arm_candidate = Candidate::new(scrutinee.clone(), &arm.pattern, arm_has_guard); (arm, arm_candidate) }) .collect() @@ -222,9 +229,9 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { fn lower_match_arms( &mut self, destination: Place<'tcx>, - scrutinee_place: Place<'tcx>, + scrutinee_place_builder: PlaceBuilder<'tcx>, scrutinee_span: Span, - arm_candidates: Vec<(&'_ Arm<'tcx>, Candidate<'_, 'tcx>)>, + arm_candidates: Vec<(&'_ Arm<'_, 'tcx>, Candidate<'_, 'tcx>)>, outer_source_info: SourceInfo, fake_borrow_temps: Vec<(Place<'tcx>, Local)>, ) -> BlockAnd<()> { @@ -236,13 +243,33 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { let arm_source_info = self.source_info(arm.span); let arm_scope = (arm.scope, arm_source_info); self.in_scope(arm_scope, arm.lint_level, |this| { - let body = this.hir.mirror(arm.body.clone()); + // `try_upvars_resolved` may fail if it is unable to resolve the given + // `PlaceBuilder` inside a closure. In this case, we don't want to include + // a scrutinee place. `scrutinee_place_builder` will fail to be resolved + // if the only match arm is a wildcard (`_`). + // Example: + // ``` + // let foo = (0, 1); + // let c = || { + // match foo { _ => () }; + // }; + // ``` + let mut opt_scrutinee_place: Option<(Option<&Place<'tcx>>, Span)> = None; + let scrutinee_place: Place<'tcx>; + if let Ok(scrutinee_builder) = scrutinee_place_builder + .clone() + .try_upvars_resolved(this.tcx, this.typeck_results) + { + scrutinee_place = + scrutinee_builder.into_place(this.tcx, this.typeck_results); + opt_scrutinee_place = Some((Some(&scrutinee_place), scrutinee_span)); + } let scope = this.declare_bindings( None, arm.span, &arm.pattern, ArmHasGuard(arm.guard.is_some()), - Some((Some(&scrutinee_place), scrutinee_span)), + opt_scrutinee_place, ); let arm_block = this.bind_pattern( @@ -259,7 +286,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { this.source_scope = source_scope; } - this.into(destination, arm_block, body) + this.expr_into_dest(destination, arm_block, &arm.body) }) }) .collect(); @@ -286,7 +313,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { &mut self, outer_source_info: SourceInfo, candidate: Candidate<'_, 'tcx>, - guard: Option<&Guard<'tcx>>, + guard: Option<&Guard<'_, 'tcx>>, fake_borrow_temps: &Vec<(Place<'tcx>, Local)>, scrutinee_span: Span, arm_span: Option, @@ -362,18 +389,18 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { &mut self, mut block: BasicBlock, irrefutable_pat: Pat<'tcx>, - initializer: ExprRef<'tcx>, + initializer: &Expr<'_, 'tcx>, ) -> BlockAnd<()> { match *irrefutable_pat.kind { // Optimize the case of `let x = ...` to write directly into `x` PatKind::Binding { mode: BindingMode::ByValue, var, subpattern: None, .. } => { let place = self.storage_live_binding(block, var, irrefutable_pat.span, OutsideGuard, true); - unpack!(block = self.into(place, block, initializer)); + unpack!(block = self.expr_into_dest(place, block, initializer)); // Inject a fake read, see comments on `FakeReadCause::ForLet`. let source_info = self.source_info(irrefutable_pat.span); - self.cfg.push_fake_read(block, source_info, FakeReadCause::ForLet, place); + self.cfg.push_fake_read(block, source_info, FakeReadCause::ForLet(None), place); self.schedule_drop_for_binding(var, irrefutable_pat.span, OutsideGuard); block.unit() @@ -404,17 +431,17 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { } => { let place = self.storage_live_binding(block, var, irrefutable_pat.span, OutsideGuard, true); - unpack!(block = self.into(place, block, initializer)); + unpack!(block = self.expr_into_dest(place, block, initializer)); // Inject a fake read, see comments on `FakeReadCause::ForLet`. let pattern_source_info = self.source_info(irrefutable_pat.span); - let cause_let = FakeReadCause::ForLet; + let cause_let = FakeReadCause::ForLet(None); self.cfg.push_fake_read(block, pattern_source_info, cause_let, place); let ty_source_info = self.source_info(user_ty_span); let user_ty = pat_ascription_ty.user_ty( &mut self.canonical_user_type_annotations, - place.ty(&self.local_decls, self.hir.tcx()).ty, + place.ty(&self.local_decls, self.tcx).ty, ty_source_info.span, ); self.cfg.push( @@ -447,8 +474,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { } _ => { - let place = unpack!(block = self.as_place(block, initializer)); - self.place_into_pattern(block, irrefutable_pat, place, true) + let place_builder = unpack!(block = self.as_place_builder(block, initializer)); + self.place_into_pattern(block, irrefutable_pat, place_builder, true) } } } @@ -457,14 +484,12 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { &mut self, block: BasicBlock, irrefutable_pat: Pat<'tcx>, - initializer: Place<'tcx>, + initializer: PlaceBuilder<'tcx>, set_match_place: bool, ) -> BlockAnd<()> { - let mut candidate = Candidate::new(initializer, &irrefutable_pat, false); - + let mut candidate = Candidate::new(initializer.clone(), &irrefutable_pat, false); let fake_borrow_temps = self.lower_match_tree(block, irrefutable_pat.span, false, &mut [&mut candidate]); - // For matches and function arguments, the place that is being matched // can be set when creating the variables. But the place for // let PATTERN = ... might not even exist until we do the assignment. @@ -479,7 +504,27 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { VarBindingForm { opt_match_place: Some((ref mut match_place, _)), .. }, )))) = self.local_decls[local].local_info { - *match_place = Some(initializer); + // `try_upvars_resolved` may fail if it is unable to resolve the given + // `PlaceBuilder` inside a closure. In this case, we don't want to include + // a scrutinee place. `scrutinee_place_builder` will fail for destructured + // assignments. This is because a closure only captures the precise places + // that it will read and as a result a closure may not capture the entire + // tuple/struct and rather have individual places that will be read in the + // final MIR. + // Example: + // ``` + // let foo = (0, 1); + // let c = || { + // let (v1, v2) = foo; + // }; + // ``` + if let Ok(match_pair_resolved) = + initializer.clone().try_upvars_resolved(self.tcx, self.typeck_results) + { + let place = + match_pair_resolved.into_place(self.tcx, self.typeck_results); + *match_place = Some(place); + } } else { bug!("Let binding to non-user variable.") } @@ -556,7 +601,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { let local_id = self.var_local_id(var, for_guard); let source_info = self.source_info(span); self.cfg.push(block, Statement { source_info, kind: StatementKind::StorageLive(local_id) }); - let region_scope = self.hir.region_scope_tree.var_scope(var.local_id); + let region_scope = self.region_scope_tree.var_scope(var.local_id); if schedule_drop { self.schedule_drop(span, region_scope, local_id, DropKind::Storage); } @@ -565,7 +610,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { crate fn schedule_drop_for_binding(&mut self, var: HirId, span: Span, for_guard: ForGuard) { let local_id = self.var_local_id(var, for_guard); - let region_scope = self.hir.region_scope_tree.var_scope(var.local_id); + let region_scope = self.region_scope_tree.var_scope(var.local_id); self.schedule_drop(span, region_scope, local_id, DropKind::Value); } @@ -718,7 +763,7 @@ struct Candidate<'pat, 'tcx> { } impl<'tcx, 'pat> Candidate<'pat, 'tcx> { - fn new(place: Place<'tcx>, pattern: &'pat Pat<'tcx>, has_guard: bool) -> Self { + fn new(place: PlaceBuilder<'tcx>, pattern: &'pat Pat<'tcx>, has_guard: bool) -> Self { Candidate { span: pattern.span, has_guard, @@ -792,7 +837,7 @@ struct Ascription<'tcx> { #[derive(Clone, Debug)] crate struct MatchPair<'pat, 'tcx> { // this place... - place: Place<'tcx>, + place: PlaceBuilder<'tcx>, // ... must match this pattern. pattern: &'pat Pat<'tcx>, @@ -1071,7 +1116,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { fake_borrows.insert(Place { local: source.local, - projection: self.hir.tcx().intern_place_elems(proj_base), + projection: self.tcx.intern_place_elems(proj_base), }); } } @@ -1199,7 +1244,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { &mut otherwise, pats, or_span, - place, + place.clone(), fake_borrows, ); }); @@ -1225,12 +1270,14 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { otherwise: &mut Option, pats: &'pat [Pat<'tcx>], or_span: Span, - place: Place<'tcx>, + place: PlaceBuilder<'tcx>, fake_borrows: &mut Option>>, ) { debug!("test_or_pattern:\ncandidate={:#?}\npats={:#?}", candidate, pats); - let mut or_candidates: Vec<_> = - pats.iter().map(|pat| Candidate::new(place, pat, candidate.has_guard)).collect(); + let mut or_candidates: Vec<_> = pats + .iter() + .map(|pat| Candidate::new(place.clone(), pat, candidate.has_guard)) + .collect(); let mut or_candidate_refs: Vec<_> = or_candidates.iter_mut().collect(); let otherwise = if candidate.otherwise_block.is_some() { &mut candidate.otherwise_block @@ -1413,7 +1460,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { // extract the match-pair from the highest priority candidate let match_pair = &candidates.first().unwrap().match_pairs[0]; let mut test = self.test(match_pair); - let match_place = match_pair.place; + let match_place = match_pair.place.clone(); // most of the time, the test to perform is simply a function // of the main candidate; but for a test like SwitchInt, we @@ -1439,7 +1486,12 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { // Insert a Shallow borrow of any places that is switched on. if let Some(fb) = fake_borrows { - fb.insert(match_place); + if let Ok(match_place_resolved) = + match_place.clone().try_upvars_resolved(self.tcx, self.typeck_results) + { + let resolved_place = match_place_resolved.into_place(self.tcx, self.typeck_results); + fb.insert(resolved_place); + } } // perform the test, branching to one of N blocks. For each of @@ -1457,7 +1509,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { // encounter a candidate where the test is not relevant; at // that point, we stop sorting. while let Some(candidate) = candidates.first_mut() { - if let Some(idx) = self.sort_candidate(&match_place, &test, candidate) { + if let Some(idx) = self.sort_candidate(&match_place.clone(), &test, candidate) { let (candidate, rest) = candidates.split_first_mut().unwrap(); target_candidates[idx].push(candidate); candidates = rest; @@ -1550,7 +1602,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { fake_borrows: &'b FxHashSet>, temp_span: Span, ) -> Vec<(Place<'tcx>, Local)> { - let tcx = self.hir.tcx(); + let tcx = self.tcx; debug!("add_fake_borrows fake_borrows = {:?}", fake_borrows); @@ -1613,7 +1665,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { &mut self, candidate: Candidate<'pat, 'tcx>, parent_bindings: &[(Vec>, Vec>)], - guard: Option<&Guard<'tcx>>, + guard: Option<&Guard<'_, 'tcx>>, fake_borrows: &Vec<(Place<'tcx>, Local)>, scrutinee_span: Span, arm_span: Option, @@ -1727,7 +1779,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { // * So we eagerly create the reference for the arm and then take a // reference to that. if let Some(guard) = guard { - let tcx = self.hir.tcx(); + let tcx = self.tcx; let bindings = parent_bindings .iter() .flat_map(|(bindings, _)| bindings) @@ -1749,37 +1801,46 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { let (guard_span, (post_guard_block, otherwise_post_guard_block)) = match guard { Guard::If(e) => { - let e = self.hir.mirror(e.clone()); let source_info = self.source_info(e.span); (e.span, self.test_bool(block, e, source_info)) } Guard::IfLet(pat, scrutinee) => { - let scrutinee_span = scrutinee.span(); - let scrutinee_place = unpack!( - block = self.lower_scrutinee(block, scrutinee.clone(), scrutinee_span) - ); - let mut guard_candidate = Candidate::new(scrutinee_place, &pat, false); + let scrutinee_span = scrutinee.span; + let scrutinee_place_builder = + unpack!(block = self.lower_scrutinee(block, scrutinee, scrutinee_span)); + let mut guard_candidate = + Candidate::new(scrutinee_place_builder.clone(), &pat, false); let wildcard = Pat::wildcard_from_ty(pat.ty); - let mut otherwise_candidate = Candidate::new(scrutinee_place, &wildcard, false); + let mut otherwise_candidate = + Candidate::new(scrutinee_place_builder.clone(), &wildcard, false); let fake_borrow_temps = self.lower_match_tree( block, pat.span, false, &mut [&mut guard_candidate, &mut otherwise_candidate], ); + let mut opt_scrutinee_place: Option<(Option<&Place<'tcx>>, Span)> = None; + let scrutinee_place: Place<'tcx>; + if let Ok(scrutinee_builder) = + scrutinee_place_builder.try_upvars_resolved(self.tcx, self.typeck_results) + { + scrutinee_place = + scrutinee_builder.into_place(self.tcx, self.typeck_results); + opt_scrutinee_place = Some((Some(&scrutinee_place), scrutinee_span)); + } self.declare_bindings( None, pat.span.to(arm_span.unwrap()), pat, ArmHasGuard(false), - Some((Some(&scrutinee_place), scrutinee.span())), + opt_scrutinee_place, ); let post_guard_block = self.bind_pattern( self.source_info(pat.span), guard_candidate, None, &fake_borrow_temps, - scrutinee.span(), + scrutinee.span, None, None, ); @@ -1888,7 +1949,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { let user_ty = ascription.user_ty.clone().user_ty( &mut self.canonical_user_type_annotations, - ascription.source.ty(&self.local_decls, self.hir.tcx()).ty, + ascription.source.ty(&self.local_decls, self.tcx).ty, source_info.span, ); self.cfg.push( @@ -1917,7 +1978,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { // Assign each of the bindings. Since we are binding for a // guard expression, this will never trigger moves out of the // candidate. - let re_erased = self.hir.tcx().lifetimes.re_erased; + let re_erased = self.tcx.lifetimes.re_erased; for binding in bindings { debug!("bind_matched_candidate_for_guard(binding={:?})", binding); let source_info = self.source_info(binding.span); @@ -1966,7 +2027,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { { debug!("bind_matched_candidate_for_arm_body(block={:?})", block); - let re_erased = self.hir.tcx().lifetimes.re_erased; + let re_erased = self.tcx.lifetimes.re_erased; // Assign each of the bindings. This may trigger moves out of the candidate. for binding in bindings { let source_info = self.source_info(binding.span); @@ -2015,7 +2076,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { var_id, name, mode, var_ty, visibility_scope, source_info ); - let tcx = self.hir.tcx(); + let tcx = self.tcx; let debug_source_info = SourceInfo { span: source_info.span, scope: visibility_scope }; let binding_mode = match mode { BindingMode::ByValue => ty::BindingMode::BindByValue(mutability), diff --git a/compiler/rustc_mir_build/src/build/matches/simplify.rs b/compiler/rustc_mir_build/src/build/matches/simplify.rs index ddfaeafc07cf1..3ad143a57ff56 100644 --- a/compiler/rustc_mir_build/src/build/matches/simplify.rs +++ b/compiler/rustc_mir_build/src/build/matches/simplify.rs @@ -12,11 +12,11 @@ //! sort of test: for example, testing which variant an enum is, or //! testing a value against a constant. +use crate::build::expr::as_place::PlaceBuilder; use crate::build::matches::{Ascription, Binding, Candidate, MatchPair}; use crate::build::Builder; use crate::thir::{self, *}; use rustc_hir::RangeEnd; -use rustc_middle::mir::Place; use rustc_middle::ty; use rustc_middle::ty::layout::IntegerExt; use rustc_target::abi::{Integer, Size}; @@ -68,11 +68,12 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { let match_pairs = mem::take(&mut candidate.match_pairs); if let [MatchPair { pattern: Pat { kind: box PatKind::Or { pats }, .. }, place }] = - *match_pairs + &*match_pairs { existing_bindings.extend_from_slice(&new_bindings); mem::swap(&mut candidate.bindings, &mut existing_bindings); - candidate.subcandidates = self.create_or_subcandidates(candidate, place, pats); + candidate.subcandidates = + self.create_or_subcandidates(candidate, place.clone(), pats); return true; } @@ -125,12 +126,12 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { fn create_or_subcandidates<'pat>( &mut self, candidate: &Candidate<'pat, 'tcx>, - place: Place<'tcx>, + place: PlaceBuilder<'tcx>, pats: &'pat [Pat<'tcx>], ) -> Vec> { pats.iter() .map(|pat| { - let mut candidate = Candidate::new(place, pat, candidate.has_guard); + let mut candidate = Candidate::new(place.clone(), pat, candidate.has_guard); self.simplify_candidate(&mut candidate); candidate }) @@ -147,18 +148,17 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { match_pair: MatchPair<'pat, 'tcx>, candidate: &mut Candidate<'pat, 'tcx>, ) -> Result<(), MatchPair<'pat, 'tcx>> { - let tcx = self.hir.tcx(); + let tcx = self.tcx; match *match_pair.pattern.kind { PatKind::AscribeUserType { ref subpattern, ascription: thir::pattern::Ascription { variance, user_ty, user_ty_span }, } => { // Apply the type ascription to the value at `match_pair.place`, which is the - // value being matched, taking the variance field into account. candidate.ascriptions.push(Ascription { span: user_ty_span, user_ty, - source: match_pair.place, + source: match_pair.place.clone().into_place(self.tcx, self.typeck_results), variance, }); @@ -177,7 +177,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { name, mutability, span: match_pair.pattern.span, - source: match_pair.place, + source: match_pair.place.clone().into_place(self.tcx, self.typeck_results), var_id: var, var_ty: ty, binding_mode: mode, @@ -251,21 +251,23 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { PatKind::Variant { adt_def, substs, variant_index, ref subpatterns } => { let irrefutable = adt_def.variants.iter_enumerated().all(|(i, v)| { i == variant_index || { - self.hir.tcx().features().exhaustive_patterns + self.tcx.features().exhaustive_patterns && !v .uninhabited_from( - self.hir.tcx(), + self.tcx, substs, adt_def.adt_kind(), - self.hir.param_env, + self.param_env, ) .is_empty() } }) && (adt_def.did.is_local() || !adt_def.is_variant_list_non_exhaustive()); if irrefutable { - let place = tcx.mk_place_downcast(match_pair.place, adt_def, variant_index); - candidate.match_pairs.extend(self.field_match_pairs(place, subpatterns)); + let place_builder = match_pair.place.downcast(adt_def, variant_index); + candidate + .match_pairs + .extend(self.field_match_pairs(place_builder, subpatterns)); Ok(()) } else { Err(match_pair) @@ -290,8 +292,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { } PatKind::Deref { ref subpattern } => { - let place = tcx.mk_place_deref(match_pair.place); - candidate.match_pairs.push(MatchPair::new(place, subpattern)); + let place_builder = match_pair.place.deref(); + candidate.match_pairs.push(MatchPair::new(place_builder, subpattern)); Ok(()) } diff --git a/compiler/rustc_mir_build/src/build/matches/test.rs b/compiler/rustc_mir_build/src/build/matches/test.rs index 126fb957a6a99..b082169cd63c1 100644 --- a/compiler/rustc_mir_build/src/build/matches/test.rs +++ b/compiler/rustc_mir_build/src/build/matches/test.rs @@ -5,6 +5,7 @@ // identify what tests are needed, perform the tests, and then filter // the candidates based on the result. +use crate::build::expr::as_place::PlaceBuilder; use crate::build::matches::{Candidate, MatchPair, Test, TestKind}; use crate::build::Builder; use crate::thir::pattern::compare_const_vals; @@ -13,9 +14,11 @@ use rustc_data_structures::fx::FxIndexMap; use rustc_hir::{LangItem, RangeEnd}; use rustc_index::bit_set::BitSet; use rustc_middle::mir::*; +use rustc_middle::ty::subst::{GenericArg, Subst}; use rustc_middle::ty::util::IntTypeExt; -use rustc_middle::ty::{self, adjustment::PointerCast, Ty}; -use rustc_span::symbol::sym; +use rustc_middle::ty::{self, adjustment::PointerCast, Ty, TyCtxt}; +use rustc_span::def_id::DefId; +use rustc_span::symbol::{sym, Symbol}; use rustc_target::abi::VariantIdx; use std::cmp::Ordering; @@ -51,7 +54,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { PatKind::Constant { value } => Test { span: match_pair.pattern.span, - kind: TestKind::Eq { value, ty: match_pair.pattern.ty.clone() }, + kind: TestKind::Eq { value, ty: match_pair.pattern.ty }, }, PatKind::Range(range) => { @@ -79,7 +82,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { pub(super) fn add_cases_to_switch<'pat>( &mut self, - test_place: &Place<'tcx>, + test_place: &PlaceBuilder<'tcx>, candidate: &Candidate<'pat, 'tcx>, switch_ty: Ty<'tcx>, options: &mut FxIndexMap<&'tcx ty::Const<'tcx>, u128>, @@ -93,9 +96,9 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { match *match_pair.pattern.kind { PatKind::Constant { value } => { - options.entry(value).or_insert_with(|| { - value.eval_bits(self.hir.tcx(), self.hir.param_env, switch_ty) - }); + options + .entry(value) + .or_insert_with(|| value.eval_bits(self.tcx, self.param_env, switch_ty)); true } PatKind::Variant { .. } => { @@ -121,7 +124,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { pub(super) fn add_variants_to_switch<'pat>( &mut self, - test_place: &Place<'tcx>, + test_place: &PlaceBuilder<'tcx>, candidate: &Candidate<'pat, 'tcx>, variants: &mut BitSet, ) -> bool { @@ -149,15 +152,23 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { pub(super) fn perform_test( &mut self, block: BasicBlock, - place: Place<'tcx>, + place_builder: PlaceBuilder<'tcx>, test: &Test<'tcx>, make_target_blocks: impl FnOnce(&mut Self) -> Vec, ) { + let place: Place<'tcx>; + if let Ok(test_place_builder) = + place_builder.try_upvars_resolved(self.tcx, self.typeck_results) + { + place = test_place_builder.into_place(self.tcx, self.typeck_results); + } else { + return; + } debug!( "perform_test({:?}, {:?}: {:?}, {:?})", block, place, - place.ty(&self.local_decls, self.hir.tcx()), + place.ty(&self.local_decls, self.tcx), test ); @@ -169,7 +180,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { let num_enum_variants = adt_def.variants.len(); debug_assert_eq!(target_blocks.len(), num_enum_variants + 1); let otherwise_block = *target_blocks.last().unwrap(); - let tcx = self.hir.tcx(); + let tcx = self.tcx; let switch_targets = SwitchTargets::new( adt_def.discriminants(tcx).filter_map(|(idx, discr)| { if variants.contains(idx) { @@ -217,7 +228,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { 0 => (second_bb, first_bb), v => span_bug!(test.span, "expected boolean value but got {:?}", v), }; - TerminatorKind::if_(self.hir.tcx(), Operand::Copy(place), true_bb, false_bb) + TerminatorKind::if_(self.tcx, Operand::Copy(place), true_bb, false_bb) } else { bug!("`TestKind::SwitchInt` on `bool` should have two targets") } @@ -292,7 +303,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { TestKind::Len { len, op } => { let target_blocks = make_target_blocks(self); - let usize_ty = self.hir.usize_ty(); + let usize_ty = self.tcx.types.usize; let actual = self.temp(usize_ty, test.span); // actual = len(place) @@ -331,17 +342,17 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { left: Operand<'tcx>, right: Operand<'tcx>, ) { - let bool_ty = self.hir.bool_ty(); + let bool_ty = self.tcx.types.bool; let result = self.temp(bool_ty, source_info.span); // result = op(left, right) - self.cfg.push_assign(block, source_info, result, Rvalue::BinaryOp(op, left, right)); + self.cfg.push_assign(block, source_info, result, Rvalue::BinaryOp(op, box (left, right))); // branch based on result self.cfg.terminate( block, source_info, - TerminatorKind::if_(self.hir.tcx(), Operand::Move(result), success_block, fail_block), + TerminatorKind::if_(self.tcx, Operand::Move(result), success_block, fail_block), ); } @@ -377,7 +388,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { // nothing to do, neither is an array (None, None) => {} (Some((region, elem_ty, _)), _) | (None, Some((region, elem_ty, _))) => { - let tcx = self.hir.tcx(); + let tcx = self.tcx; // make both a slice ty = tcx.mk_imm_ref(region, tcx.mk_slice(elem_ty)); if opt_ref_ty.is_some() { @@ -408,10 +419,10 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { _ => bug!("non_scalar_compare called on non-reference type: {}", ty), }; - let eq_def_id = self.hir.tcx().require_lang_item(LangItem::PartialEq, None); - let method = self.hir.trait_method(eq_def_id, sym::eq, deref_ty, &[deref_ty.into()]); + let eq_def_id = self.tcx.require_lang_item(LangItem::PartialEq, None); + let method = trait_method(self.tcx, eq_def_id, sym::eq, deref_ty, &[deref_ty.into()]); - let bool_ty = self.hir.bool_ty(); + let bool_ty = self.tcx.types.bool; let eq_result = self.temp(bool_ty, source_info.span); let eq_block = self.cfg.start_new_block(); self.cfg.terminate( @@ -427,7 +438,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { // Need to experiment. user_ty: None, - literal: method, + literal: method.into(), }), args: vec![val, expect], destination: Some((eq_result, eq_block)), @@ -443,12 +454,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { self.cfg.terminate( eq_block, source_info, - TerminatorKind::if_( - self.hir.tcx(), - Operand::Move(eq_result), - success_block, - fail_block, - ), + TerminatorKind::if_(self.tcx, Operand::Move(eq_result), success_block, fail_block), ); } else { bug!("`TestKind::Eq` should have two target blocks") @@ -484,7 +490,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { /// tighter match code if we do something a bit different. pub(super) fn sort_candidate<'pat>( &mut self, - test_place: &Place<'tcx>, + test_place: &PlaceBuilder<'tcx>, test: &Test<'tcx>, candidate: &mut Candidate<'pat, 'tcx>, ) -> Option { @@ -632,11 +638,11 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { use rustc_hir::RangeEnd::*; use std::cmp::Ordering::*; - let tcx = self.hir.tcx(); + let tcx = self.tcx; let test_ty = test.lo.ty; - let lo = compare_const_vals(tcx, test.lo, pat.hi, self.hir.param_env, test_ty)?; - let hi = compare_const_vals(tcx, test.hi, pat.lo, self.hir.param_env, test_ty)?; + let lo = compare_const_vals(tcx, test.lo, pat.hi, self.param_env, test_ty)?; + let hi = compare_const_vals(tcx, test.hi, pat.lo, self.param_env, test_ty)?; match (test.end, pat.end, lo, hi) { // pat < test @@ -731,7 +737,6 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { candidate: &mut Candidate<'pat, 'tcx>, ) { let match_pair = candidate.match_pairs.remove(match_pair_index); - let tcx = self.hir.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 @@ -740,10 +745,10 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { Some(adt_def.variants[variant_index].ident.name), variant_index, ); - let downcast_place = tcx.mk_place_elem(match_pair.place, elem); // `(x as Variant)` + 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` - let place = tcx.mk_place_field(downcast_place, subpattern.field, subpattern.pattern.ty); + let place = downcast_place.clone().field(subpattern.field, subpattern.pattern.ty); // e.g., `(x as Variant).0 @ P1` MatchPair::new(place, &subpattern.pattern) }); @@ -762,10 +767,10 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { ) -> Option { use std::cmp::Ordering::*; - let tcx = self.hir.tcx(); + let tcx = self.tcx; - let a = compare_const_vals(tcx, range.lo, value, self.hir.param_env, range.lo.ty)?; - let b = compare_const_vals(tcx, value, range.hi, self.hir.param_env, range.lo.ty)?; + let a = compare_const_vals(tcx, range.lo, value, self.param_env, range.lo.ty)?; + let b = compare_const_vals(tcx, value, range.hi, self.param_env, range.lo.ty)?; match (b, range.end) { (Less, _) | (Equal, RangeEnd::Included) if a != Greater => Some(true), @@ -815,3 +820,25 @@ impl Test<'_> { fn is_switch_ty(ty: Ty<'_>) -> bool { ty.is_integral() || ty.is_char() || ty.is_bool() } + +fn trait_method<'tcx>( + tcx: TyCtxt<'tcx>, + trait_def_id: DefId, + method_name: Symbol, + self_ty: Ty<'tcx>, + params: &[GenericArg<'tcx>], +) -> &'tcx ty::Const<'tcx> { + let substs = tcx.mk_substs_trait(self_ty, params); + + // The unhygienic comparison here is acceptable because this is only + // used on known traits. + let item = tcx + .associated_items(trait_def_id) + .filter_by_name_unhygienic(method_name) + .find(|item| item.kind == ty::AssocKind::Fn) + .expect("trait method not found"); + + let method_ty = tcx.type_of(item.def_id); + let method_ty = method_ty.subst(tcx, substs); + ty::Const::zero_sized(tcx, method_ty) +} diff --git a/compiler/rustc_mir_build/src/build/matches/util.rs b/compiler/rustc_mir_build/src/build/matches/util.rs index db1f678a5c68d..d49a00a566053 100644 --- a/compiler/rustc_mir_build/src/build/matches/util.rs +++ b/compiler/rustc_mir_build/src/build/matches/util.rs @@ -1,3 +1,4 @@ +use crate::build::expr::as_place::PlaceBuilder; use crate::build::matches::MatchPair; use crate::build::Builder; use crate::thir::*; @@ -9,14 +10,13 @@ use std::convert::TryInto; impl<'a, 'tcx> Builder<'a, 'tcx> { crate fn field_match_pairs<'pat>( &mut self, - place: Place<'tcx>, + place: PlaceBuilder<'tcx>, subpatterns: &'pat [FieldPat<'tcx>], ) -> Vec> { subpatterns .iter() .map(|fieldpat| { - let place = - self.hir.tcx().mk_place_field(place, fieldpat.field, fieldpat.pattern.ty); + let place = place.clone().field(fieldpat.field, fieldpat.pattern.ty); MatchPair::new(place, &fieldpat.pattern) }) .collect() @@ -25,34 +25,37 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { crate fn prefix_slice_suffix<'pat>( &mut self, match_pairs: &mut SmallVec<[MatchPair<'pat, 'tcx>; 1]>, - place: &Place<'tcx>, + place: &PlaceBuilder<'tcx>, prefix: &'pat [Pat<'tcx>], opt_slice: Option<&'pat Pat<'tcx>>, suffix: &'pat [Pat<'tcx>], ) { - let tcx = self.hir.tcx(); - let (min_length, exact_size) = match place.ty(&self.local_decls, tcx).ty.kind() { - ty::Array(_, length) => (length.eval_usize(tcx, self.hir.param_env), true), + let tcx = self.tcx; + let (min_length, exact_size) = match place + .clone() + .into_place(tcx, self.typeck_results) + .ty(&self.local_decls, tcx) + .ty + .kind() + { + ty::Array(_, length) => (length.eval_usize(tcx, self.param_env), true), _ => ((prefix.len() + suffix.len()).try_into().unwrap(), false), }; match_pairs.extend(prefix.iter().enumerate().map(|(idx, subpattern)| { let elem = ProjectionElem::ConstantIndex { offset: idx as u64, min_length, from_end: false }; - let place = tcx.mk_place_elem(*place, elem); + let place = place.clone().project(elem); MatchPair::new(place, subpattern) })); if let Some(subslice_pat) = opt_slice { let suffix_len = suffix.len() as u64; - let subslice = tcx.mk_place_elem( - *place, - ProjectionElem::Subslice { - from: prefix.len() as u64, - to: if exact_size { min_length - suffix_len } else { suffix_len }, - from_end: !exact_size, - }, - ); + let subslice = place.clone().project(ProjectionElem::Subslice { + from: prefix.len() as u64, + to: if exact_size { min_length - suffix_len } else { suffix_len }, + from_end: !exact_size, + }); match_pairs.push(MatchPair::new(subslice, subslice_pat)); } @@ -63,7 +66,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { min_length, from_end: !exact_size, }; - let place = tcx.mk_place_elem(*place, elem); + let place = place.clone().project(elem); MatchPair::new(place, subpattern) })); } @@ -92,7 +95,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { } impl<'pat, 'tcx> MatchPair<'pat, 'tcx> { - crate fn new(place: Place<'tcx>, pattern: &'pat Pat<'tcx>) -> MatchPair<'pat, 'tcx> { + crate fn new(place: PlaceBuilder<'tcx>, pattern: &'pat Pat<'tcx>) -> MatchPair<'pat, 'tcx> { MatchPair { place, pattern } } } diff --git a/compiler/rustc_mir_build/src/build/misc.rs b/compiler/rustc_mir_build/src/build/misc.rs index 29651d9bc663a..a1126d1c3d529 100644 --- a/compiler/rustc_mir_build/src/build/misc.rs +++ b/compiler/rustc_mir_build/src/build/misc.rs @@ -3,10 +3,10 @@ use crate::build::Builder; -use rustc_middle::ty::{self, Ty}; - use rustc_middle::mir::*; +use rustc_middle::ty::{self, Ty}; use rustc_span::{Span, DUMMY_SP}; +use rustc_trait_selection::infer::InferCtxtExt; impl<'a, 'tcx> Builder<'a, 'tcx> { /// Adds a new temporary value of type `ty` storing the result of @@ -30,6 +30,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { span: Span, literal: &'tcx ty::Const<'tcx>, ) -> Operand<'tcx> { + let literal = literal.into(); let constant = box Constant { span, user_ty: None, literal }; Operand::Constant(constant) } @@ -37,7 +38,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { // Returns a zero literal operand for the appropriate type, works for // bool, char and integers. crate fn zero_literal(&mut self, span: Span, ty: Ty<'tcx>) -> Operand<'tcx> { - let literal = ty::Const::from_bits(self.hir.tcx(), 0, ty::ParamEnv::empty().and(ty)); + let literal = ty::Const::from_bits(self.tcx, 0, ty::ParamEnv::empty().and(ty)); self.literal_operand(span, literal) } @@ -48,7 +49,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { source_info: SourceInfo, value: u64, ) -> Place<'tcx> { - let usize_ty = self.hir.usize_ty(); + let usize_ty = self.tcx.types.usize; let temp = self.temp(usize_ty, source_info.span); self.cfg.push_assign_constant( block, @@ -57,16 +58,16 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { Constant { span: source_info.span, user_ty: None, - literal: self.hir.usize_literal(value), + literal: ty::Const::from_usize(self.tcx, value).into(), }, ); temp } crate fn consume_by_copy_or_move(&self, place: Place<'tcx>) -> Operand<'tcx> { - let tcx = self.hir.tcx(); + let tcx = self.tcx; let ty = place.ty(&self.local_decls, tcx).ty; - if !self.hir.type_is_copy_modulo_regions(ty, DUMMY_SP) { + if !self.infcx.type_is_copy_modulo_regions(self.param_env, ty, DUMMY_SP) { Operand::Move(place) } else { Operand::Copy(place) diff --git a/compiler/rustc_mir_build/src/build/mod.rs b/compiler/rustc_mir_build/src/build/mod.rs index b928458df8ee4..f944e5f8f0420 100644 --- a/compiler/rustc_mir_build/src/build/mod.rs +++ b/compiler/rustc_mir_build/src/build/mod.rs @@ -1,7 +1,7 @@ use crate::build; +use crate::build::expr::as_place::PlaceBuilder; use crate::build::scope::DropKind; -use crate::thir::cx::Cx; -use crate::thir::{BindingMode, LintLevel, PatKind}; +use crate::thir::{build_thir, Arena, BindingMode, Expr, LintLevel, Pat, PatKind}; use rustc_attr::{self as attr, UnwindAttr}; use rustc_errors::ErrorReported; use rustc_hir as hir; @@ -9,13 +9,13 @@ use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_hir::lang_items::LangItem; use rustc_hir::{GeneratorKind, HirIdMap, Node}; use rustc_index::vec::{Idx, IndexVec}; -use rustc_infer::infer::TyCtxtInferExt; +use rustc_infer::infer::{InferCtxt, TyCtxtInferExt}; use rustc_middle::hir::place::PlaceBase as HirPlaceBase; use rustc_middle::middle::region; use rustc_middle::mir::*; use rustc_middle::ty::subst::Subst; -use rustc_middle::ty::{self, Ty, TyCtxt, TypeFoldable}; -use rustc_span::symbol::kw; +use rustc_middle::ty::{self, Ty, TyCtxt, TypeFoldable, TypeckResults}; +use rustc_span::symbol::{kw, sym}; use rustc_span::Span; use rustc_target::spec::abi::Abi; use rustc_target::spec::PanicStrategy; @@ -42,6 +42,8 @@ crate fn mir_built<'tcx>( /// Construct the MIR for a given `DefId`. fn mir_build(tcx: TyCtxt<'_>, def: ty::WithOptConstParam) -> Body<'_> { let id = tcx.hir().local_def_id_to_hir_id(def.did); + let body_owner_kind = tcx.hir().body_owner_kind(id); + let typeck_results = tcx.typeck_opt_const_arg(def); // Figure out what primary body this item has. let (body_id, return_ty_span, span_with_body) = match tcx.hir().get(id) { @@ -86,15 +88,15 @@ fn mir_build(tcx: TyCtxt<'_>, def: ty::WithOptConstParam) -> Body<'_ // If we don't have a specialized span for the body, just use the // normal def span. let span_with_body = span_with_body.unwrap_or_else(|| tcx.hir().span(id)); + let arena = Arena::default(); tcx.infer_ctxt().enter(|infcx| { - let cx = Cx::new(&infcx, def, id); - let body = if let Some(ErrorReported) = cx.typeck_results().tainted_by_errors { - build::construct_error(cx, body_id) - } else if cx.body_owner_kind.is_fn_or_closure() { + let body = if let Some(ErrorReported) = typeck_results.tainted_by_errors { + build::construct_error(&infcx, def, id, body_id, body_owner_kind) + } else if body_owner_kind.is_fn_or_closure() { // fetch the fully liberated fn signature (that is, all bound // types/lifetimes replaced) - let fn_sig = cx.typeck_results().liberated_fn_sigs()[id]; + let fn_sig = typeck_results.liberated_fn_sigs()[id]; let fn_def_id = tcx.hir().local_def_id(id); let safety = match fn_sig.unsafety { @@ -103,6 +105,7 @@ fn mir_build(tcx: TyCtxt<'_>, def: ty::WithOptConstParam) -> Body<'_ }; let body = tcx.hir().body(body_id); + let thir = build_thir(tcx, def, &arena, &body.value); let ty = tcx.type_of(fn_def_id); let mut abi = fn_sig.abi; let implicit_argument = match ty.kind() { @@ -178,7 +181,8 @@ fn mir_build(tcx: TyCtxt<'_>, def: ty::WithOptConstParam) -> Body<'_ }; let mut mir = build::construct_fn( - cx, + &infcx, + def, id, arguments, safety, @@ -186,6 +190,7 @@ fn mir_build(tcx: TyCtxt<'_>, def: ty::WithOptConstParam) -> Body<'_ return_ty, return_ty_span, body, + thir, span_with_body, ); if yield_ty.is_some() { @@ -205,9 +210,12 @@ fn mir_build(tcx: TyCtxt<'_>, def: ty::WithOptConstParam) -> Body<'_ // place to be the type of the constant because NLL typeck will // equate them. - let return_ty = cx.typeck_results().node_type(id); + let return_ty = typeck_results.node_type(id); - build::construct_const(cx, body_id, return_ty, return_ty_span) + let ast_expr = &tcx.hir().body(body_id).value; + let thir = build_thir(tcx, def, &arena, ast_expr); + + build::construct_const(&infcx, thir, def, id, return_ty, return_ty_span) }; lints::check(tcx, &body); @@ -244,8 +252,13 @@ fn liberated_closure_env_ty( _ => bug!("closure expr does not have closure type: {:?}", closure_ty), }; - let closure_env_ty = tcx.closure_env_ty(closure_def_id, closure_substs).unwrap(); - tcx.erase_late_bound_regions(closure_env_ty) + let bound_vars = + tcx.mk_bound_variable_kinds(std::iter::once(ty::BoundVariableKind::Region(ty::BrEnv))); + let br = + ty::BoundRegion { var: ty::BoundVar::from_usize(bound_vars.len() - 1), kind: ty::BrEnv }; + let env_region = ty::ReLateBound(ty::INNERMOST, br); + let closure_env_ty = tcx.closure_env_ty(closure_def_id, closure_substs, env_region).unwrap(); + tcx.erase_late_bound_regions(ty::Binder::bind_with_vars(closure_env_ty, bound_vars)) } #[derive(Debug, PartialEq, Eq)] @@ -304,10 +317,17 @@ impl BlockFrame { struct BlockContext(Vec); struct Builder<'a, 'tcx> { - hir: Cx<'a, 'tcx>, + tcx: TyCtxt<'tcx>, + infcx: &'a InferCtxt<'a, 'tcx>, + typeck_results: &'tcx TypeckResults<'tcx>, + region_scope_tree: &'tcx region::ScopeTree, + param_env: ty::ParamEnv<'tcx>, + cfg: CFG<'tcx>, def_id: DefId, + hir_id: hir::HirId, + check_overflow: bool, fn_span: Span, arg_count: usize, generator_kind: Option, @@ -548,7 +568,7 @@ macro_rules! unpack { }}; } -fn should_abort_on_panic(tcx: TyCtxt<'_>, fn_def_id: LocalDefId, _abi: Abi) -> bool { +fn should_abort_on_panic(tcx: TyCtxt<'_>, fn_def_id: LocalDefId, abi: Abi) -> bool { // Validate `#[unwind]` syntax regardless of platform-specific panic strategy. let attrs = &tcx.get_attrs(fn_def_id.to_def_id()); let unwind_attr = attr::find_unwind_attr(&tcx.sess, attrs); @@ -558,12 +578,47 @@ fn should_abort_on_panic(tcx: TyCtxt<'_>, fn_def_id: LocalDefId, _abi: Abi) -> b return false; } - // This is a special case: some functions have a C abi but are meant to - // unwind anyway. Don't stop them. match unwind_attr { - None => false, // FIXME(#58794); should be `!(abi == Abi::Rust || abi == Abi::RustCall)` + // If an `#[unwind]` attribute was found, we should adhere to it. Some(UnwindAttr::Allowed) => false, Some(UnwindAttr::Aborts) => true, + // If no attribute was found and the panic strategy is `unwind`, then we should examine + // the function's ABI string to determine whether it should abort upon panic. + None if tcx.features().c_unwind => { + use Abi::*; + match abi { + // In the case of ABI's that have an `-unwind` equivalent, check whether the ABI + // permits unwinding. If so, we should not abort. Otherwise, we should. + C { unwind } | Stdcall { unwind } | System { unwind } | Thiscall { unwind } => { + !unwind + } + // Rust and `rust-call` functions are allowed to unwind, and should not abort. + Rust | RustCall => false, + // Other ABI's should abort. + Cdecl + | Fastcall + | Vectorcall + | Aapcs + | Win64 + | SysV64 + | PtxKernel + | Msp430Interrupt + | X86Interrupt + | AmdGpuKernel + | EfiApi + | AvrInterrupt + | AvrNonBlockingInterrupt + | CCmseNonSecureCall + | Wasm + | RustIntrinsic + | PlatformIntrinsic + | Unadjusted => true, + } + } + // If the `c_unwind` feature gate is not active, follow the behavior that was in place + // prior to #76570. This is a special case: some functions have a C ABI but are meant to + // unwind anyway. Don't stop them. + None => false, // FIXME(#58794); should be `!(abi == Abi::Rust || abi == Abi::RustCall)` } } @@ -577,8 +632,9 @@ struct ArgInfo<'tcx>( Option, ); -fn construct_fn<'a, 'tcx, A>( - hir: Cx<'a, 'tcx>, +fn construct_fn<'tcx, A>( + infcx: &InferCtxt<'_, 'tcx>, + fn_def: ty::WithOptConstParam, fn_id: hir::HirId, arguments: A, safety: Safety, @@ -586,6 +642,7 @@ fn construct_fn<'a, 'tcx, A>( return_ty: Ty<'tcx>, return_ty_span: Span, body: &'tcx hir::Body<'tcx>, + expr: &Expr<'_, 'tcx>, span_with_body: Span, ) -> Body<'tcx> where @@ -593,15 +650,13 @@ where { let arguments: Vec<_> = arguments.collect(); - let tcx = hir.tcx(); - let tcx_hir = tcx.hir(); - let span = tcx_hir.span(fn_id); - - let fn_def_id = tcx_hir.local_def_id(fn_id); + let tcx = infcx.tcx; + let span = tcx.hir().span(fn_id); let mut builder = Builder::new( - hir, - fn_def_id.to_def_id(), + infcx, + fn_def, + fn_id, span_with_body, arguments.len(), safety, @@ -625,16 +680,16 @@ where Some(builder.in_scope(arg_scope_s, LintLevel::Inherited, |builder| { builder.args_and_body( START_BLOCK, - fn_def_id.to_def_id(), + fn_def.did.to_def_id(), &arguments, arg_scope, - &body.value, + expr, ) })) })); let source_info = builder.source_info(fn_end); builder.cfg.terminate(return_block, source_info, TerminatorKind::Return); - let should_abort = should_abort_on_panic(tcx, fn_def_id, abi); + let should_abort = should_abort_on_panic(tcx, fn_def.did, abi); builder.build_drop_trees(should_abort); return_block.unit() })); @@ -645,7 +700,7 @@ where } else { None }; - debug!("fn_id {:?} has attrs {:?}", fn_def_id, tcx.get_attrs(fn_def_id.to_def_id())); + debug!("fn_id {:?} has attrs {:?}", fn_def, tcx.get_attrs(fn_def.did.to_def_id())); let mut body = builder.finish(); body.spread_arg = spread_arg; @@ -653,22 +708,20 @@ where } fn construct_const<'a, 'tcx>( - hir: Cx<'a, 'tcx>, - body_id: hir::BodyId, + infcx: &'a InferCtxt<'a, 'tcx>, + expr: &Expr<'_, 'tcx>, + def: ty::WithOptConstParam, + hir_id: hir::HirId, const_ty: Ty<'tcx>, const_ty_span: Span, ) -> Body<'tcx> { - let tcx = hir.tcx(); - let owner_id = tcx.hir().body_owner(body_id); - let def_id = tcx.hir().local_def_id(owner_id); - let span = tcx.hir().span(owner_id); + let tcx = infcx.tcx; + let span = tcx.hir().span(hir_id); let mut builder = - Builder::new(hir, def_id.to_def_id(), span, 0, Safety::Safe, const_ty, const_ty_span, None); + Builder::new(infcx, def, hir_id, span, 0, Safety::Safe, const_ty, const_ty_span, None); let mut block = START_BLOCK; - let ast_expr = &tcx.hir().body(body_id).value; - let expr = builder.hir.mirror(ast_expr); - unpack!(block = builder.into_expr(Place::return_place(), block, expr)); + unpack!(block = builder.expr_into_dest(Place::return_place(), block, &expr)); let source_info = builder.source_info(span); builder.cfg.terminate(block, source_info, TerminatorKind::Return); @@ -682,15 +735,19 @@ fn construct_const<'a, 'tcx>( /// /// This is required because we may still want to run MIR passes on an item /// with type errors, but normal MIR construction can't handle that in general. -fn construct_error<'a, 'tcx>(hir: Cx<'a, 'tcx>, body_id: hir::BodyId) -> Body<'tcx> { - let tcx = hir.tcx(); - let owner_id = tcx.hir().body_owner(body_id); - let def_id = tcx.hir().local_def_id(owner_id); - let span = tcx.hir().span(owner_id); +fn construct_error<'a, 'tcx>( + infcx: &'a InferCtxt<'a, 'tcx>, + def: ty::WithOptConstParam, + hir_id: hir::HirId, + body_id: hir::BodyId, + body_owner_kind: hir::BodyOwnerKind, +) -> Body<'tcx> { + let tcx = infcx.tcx; + let span = tcx.hir().span(hir_id); let ty = tcx.ty_error(); let generator_kind = tcx.hir().body(body_id).generator_kind; - let num_params = match hir.body_owner_kind { - hir::BodyOwnerKind::Fn => tcx.hir().fn_decl_by_hir_id(owner_id).unwrap().inputs.len(), + let num_params = match body_owner_kind { + hir::BodyOwnerKind::Fn => tcx.hir().fn_decl_by_hir_id(hir_id).unwrap().inputs.len(), hir::BodyOwnerKind::Closure => { if generator_kind.is_some() { // Generators have an implicit `self` parameter *and* a possibly @@ -698,22 +755,14 @@ fn construct_error<'a, 'tcx>(hir: Cx<'a, 'tcx>, body_id: hir::BodyId) -> Body<'t 2 } else { // The implicit self parameter adds another local in MIR. - 1 + tcx.hir().fn_decl_by_hir_id(owner_id).unwrap().inputs.len() + 1 + tcx.hir().fn_decl_by_hir_id(hir_id).unwrap().inputs.len() } } hir::BodyOwnerKind::Const => 0, hir::BodyOwnerKind::Static(_) => 0, }; - let mut builder = Builder::new( - hir, - def_id.to_def_id(), - span, - num_params, - Safety::Safe, - ty, - span, - generator_kind, - ); + let mut builder = + Builder::new(infcx, def, hir_id, span, num_params, Safety::Safe, ty, span, generator_kind); let source_info = builder.source_info(span); // Some MIR passes will expect the number of parameters to match the // function declaration. @@ -728,8 +777,9 @@ fn construct_error<'a, 'tcx>(hir: Cx<'a, 'tcx>, body_id: hir::BodyId) -> Body<'t impl<'a, 'tcx> Builder<'a, 'tcx> { fn new( - hir: Cx<'a, 'tcx>, - def_id: DefId, + infcx: &'a InferCtxt<'a, 'tcx>, + def: ty::WithOptConstParam, + hir_id: hir::HirId, span: Span, arg_count: usize, safety: Safety, @@ -737,10 +787,30 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { return_span: Span, generator_kind: Option, ) -> Builder<'a, 'tcx> { - let lint_level = LintLevel::Explicit(hir.root_lint_level); + let tcx = infcx.tcx; + let attrs = tcx.hir().attrs(hir_id); + // Some functions always have overflow checks enabled, + // however, they may not get codegen'd, depending on + // the settings for the crate they are codegened in. + let mut check_overflow = tcx.sess.contains_name(attrs, sym::rustc_inherit_overflow_checks); + // Respect -C overflow-checks. + check_overflow |= tcx.sess.overflow_checks(); + // Constants always need overflow checks. + check_overflow |= matches!( + tcx.hir().body_owner_kind(hir_id), + hir::BodyOwnerKind::Const | hir::BodyOwnerKind::Static(_) + ); + + let lint_level = LintLevel::Explicit(hir_id); let mut builder = Builder { - hir, - def_id, + tcx, + infcx, + typeck_results: tcx.typeck_opt_const_arg(def), + region_scope_tree: tcx.region_scope_tree(def.did), + param_env: tcx.param_env(def.did), + def_id: def.did.to_def_id(), + hir_id, + check_overflow, cfg: CFG { basic_blocks: IndexVec::new() }, fn_span: span, arg_count, @@ -796,7 +866,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { fn_def_id: DefId, arguments: &[ArgInfo<'tcx>], argument_scope: region::Scope, - ast_body: &'tcx hir::Expr<'tcx>, + expr: &Expr<'_, 'tcx>, ) -> BlockAnd<()> { // Allocate locals for the function arguments for &ArgInfo(ty, _, arg_opt, _) in arguments.iter() { @@ -816,12 +886,12 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { } } - let tcx = self.hir.tcx(); + let tcx = self.tcx; let tcx_hir = tcx.hir(); - let hir_typeck_results = self.hir.typeck_results(); + let hir_typeck_results = self.typeck_results; // In analyze_closure() in upvar.rs we gathered a list of upvars used by a - // indexed closure and we stored in a map called closure_captures in TypeckResults + // indexed closure and we stored in a map called closure_min_captures in TypeckResults // with the closure's DefId. Here, we run through that vec of UpvarIds for // the given closure and use the necessary information to create upvar // debuginfo and to fill `self.upvar_mutbls`. @@ -894,14 +964,18 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { // Make sure we drop (parts of) the argument even when not matched on. self.schedule_drop( - arg_opt.as_ref().map_or(ast_body.span, |arg| arg.pat.span), + arg_opt.as_ref().map_or(expr.span, |arg| arg.pat.span), argument_scope, local, DropKind::Value, ); if let Some(arg) = arg_opt { - let pattern = self.hir.pattern_from_hir(&arg.pat); + let pat = match tcx.hir().get(arg.pat.hir_id) { + Node::Pat(pat) | Node::Binding(pat) => pat, + node => bug!("pattern became {:?}", node), + }; + let pattern = Pat::from_hir(tcx, self.param_env, self.typeck_results, pat); let original_source_scope = self.source_scope; let span = pattern.span; self.set_correct_source_scope_for_arg(arg.hir_id, original_source_scope, span); @@ -936,12 +1010,15 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { _ => { scope = self.declare_bindings( scope, - ast_body.span, + expr.span, &pattern, matches::ArmHasGuard(false), Some((Some(&place), span)), ); - unpack!(block = self.place_into_pattern(block, pattern, place, false)); + let place_builder = PlaceBuilder::from(local); + unpack!( + block = self.place_into_pattern(block, pattern, place_builder, false) + ); } } self.source_scope = original_source_scope; @@ -953,8 +1030,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { self.source_scope = source_scope; } - let body = self.hir.mirror(ast_body); - self.into(Place::return_place(), block, body) + self.expr_into_dest(Place::return_place(), block, &expr) } fn set_correct_source_scope_for_arg( @@ -963,15 +1039,15 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { original_source_scope: SourceScope, pattern_span: Span, ) { - let tcx = self.hir.tcx(); - let current_root = tcx.maybe_lint_level_root_bounded(arg_hir_id, self.hir.root_lint_level); + let tcx = self.tcx; + let current_root = tcx.maybe_lint_level_root_bounded(arg_hir_id, self.hir_id); let parent_root = tcx.maybe_lint_level_root_bounded( self.source_scopes[original_source_scope] .local_data .as_ref() .assert_crate_local() .lint_root, - self.hir.root_lint_level, + self.hir_id, ); if current_root != parent_root { self.source_scope = @@ -983,7 +1059,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { match self.unit_temp { Some(tmp) => tmp, None => { - let ty = self.hir.unit_ty(); + let ty = self.tcx.mk_unit(); let fn_span = self.fn_span; let tmp = self.temp(ty, fn_span); self.unit_temp = Some(tmp); @@ -1001,7 +1077,6 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { mod block; mod cfg; mod expr; -mod into; mod matches; mod misc; mod scope; diff --git a/compiler/rustc_mir_build/src/build/scope.rs b/compiler/rustc_mir_build/src/build/scope.rs index 5e9d780d17977..41fc925c0494e 100644 --- a/compiler/rustc_mir_build/src/build/scope.rs +++ b/compiler/rustc_mir_build/src/build/scope.rs @@ -82,7 +82,7 @@ that contains only loops and breakable blocks. It tracks where a `break`, */ use crate::build::{BlockAnd, BlockAndExtension, BlockFrame, Builder, CFG}; -use crate::thir::{Expr, ExprRef, LintLevel}; +use crate::thir::{Expr, LintLevel}; use rustc_data_structures::fx::FxHashMap; use rustc_index::vec::IndexVec; use rustc_middle::middle::region; @@ -516,7 +516,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { { debug!("in_scope(region_scope={:?})", region_scope); let source_scope = self.source_scope; - let tcx = self.hir.tcx(); + let tcx = self.tcx; if let LintLevel::Explicit(current_hir_id) = lint_level { // Use `maybe_lint_level_root_bounded` with `root_lint_level` as a bound // to avoid adding Hir dependences on our parents. @@ -524,10 +524,9 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { let parent_root = tcx.maybe_lint_level_root_bounded( self.source_scopes[source_scope].local_data.as_ref().assert_crate_local().lint_root, - self.hir.root_lint_level, + self.hir_id, ); - let current_root = - tcx.maybe_lint_level_root_bounded(current_hir_id, self.hir.root_lint_level); + let current_root = tcx.maybe_lint_level_root_bounded(current_hir_id, self.hir_id); if parent_root != current_root { self.source_scope = self.new_source_scope( @@ -575,7 +574,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { crate fn break_scope( &mut self, mut block: BasicBlock, - value: Option>, + value: Option<&Expr<'_, 'tcx>>, target: BreakableTarget, source_info: SourceInfo, ) -> BlockAnd<()> { @@ -612,13 +611,20 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { if let Some(value) = value { debug!("stmt_expr Break val block_context.push(SubExpr)"); self.block_context.push(BlockFrame::SubExpr); - unpack!(block = self.into(destination, block, value)); + unpack!(block = self.expr_into_dest(destination, block, value)); self.block_context.pop(); } else { - self.cfg.push_assign_unit(block, source_info, destination, self.hir.tcx()) + self.cfg.push_assign_unit(block, source_info, destination, self.tcx) } } else { assert!(value.is_none(), "`return` and `break` should have a destination"); + if self.tcx.sess.instrument_coverage() { + // Unlike `break` and `return`, which push an `Assign` statement to MIR, from which + // a Coverage code region can be generated, `continue` needs no `Assign`; but + // without one, the `InstrumentCoverage` MIR pass cannot generate a code region for + // `continue`. Coverage will be missing unless we add a dummy `Assign` to MIR. + self.add_dummy_assignment(&span, block, source_info); + } } let region_scope = self.scopes.breakable_scopes[break_index].region_scope; @@ -644,6 +650,14 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { self.cfg.start_new_block().unit() } + // Add a dummy `Assign` statement to the CFG, with the span for the source code's `continue` + // statement. + fn add_dummy_assignment(&mut self, span: &Span, block: BasicBlock, source_info: SourceInfo) { + let local_decl = LocalDecl::new(self.tcx.mk_unit(), *span).internal(); + let temp_place = Place::from(self.local_decls.push(local_decl)); + self.cfg.push_assign_unit(block, source_info, temp_place, self.tcx); + } + crate fn exit_top_scope( &mut self, mut block: BasicBlock, @@ -763,7 +777,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { ) { let needs_drop = match drop_kind { DropKind::Value => { - if !self.hir.needs_drop(self.local_decls[local].ty) { + if !self.local_decls[local].ty.needs_drop(self.tcx, self.param_env) { return; } true @@ -834,10 +848,9 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { } if scope.region_scope == region_scope { - let region_scope_span = - region_scope.span(self.hir.tcx(), &self.hir.region_scope_tree); + let region_scope_span = region_scope.span(self.tcx, &self.region_scope_tree); // Attribute scope exit drops to scope's closing brace. - let scope_end = self.hir.tcx().sess.source_map().end_point(region_scope_span); + let scope_end = self.tcx.sess.source_map().end_point(region_scope_span); scope.drops.push(DropData { source_info: SourceInfo { span: scope_end, scope: scope.source_scope }, @@ -920,13 +933,13 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { crate fn test_bool( &mut self, mut block: BasicBlock, - condition: Expr<'tcx>, + condition: &Expr<'_, 'tcx>, source_info: SourceInfo, ) -> (BasicBlock, BasicBlock) { let cond = unpack!(block = self.as_local_operand(block, condition)); let true_block = self.cfg.start_new_block(); let false_block = self.cfg.start_new_block(); - let term = TerminatorKind::if_(self.hir.tcx(), cond.clone(), true_block, false_block); + let term = TerminatorKind::if_(self.tcx, cond.clone(), true_block, false_block); self.cfg.terminate(block, source_info, term); match cond { diff --git a/compiler/rustc_mir_build/src/lib.rs b/compiler/rustc_mir_build/src/lib.rs index 0866892265bd9..23bc1da09b555 100644 --- a/compiler/rustc_mir_build/src/lib.rs +++ b/compiler/rustc_mir_build/src/lib.rs @@ -9,8 +9,9 @@ #![feature(control_flow_enum)] #![feature(crate_visibility_modifier)] #![feature(bool_to_option)] +#![feature(iter_zip)] #![feature(once_cell)] -#![feature(or_patterns)] +#![cfg_attr(bootstrap, feature(or_patterns))] #![recursion_limit = "256"] #[macro_use] @@ -20,7 +21,7 @@ extern crate rustc_middle; mod build; mod lints; -mod thir; +pub mod thir; use rustc_middle::ty::query::Providers; diff --git a/compiler/rustc_mir_build/src/lints.rs b/compiler/rustc_mir_build/src/lints.rs index 576b537c01766..ef8bd20d51098 100644 --- a/compiler/rustc_mir_build/src/lints.rs +++ b/compiler/rustc_mir_build/src/lints.rs @@ -15,7 +15,7 @@ crate fn check<'tcx>(tcx: TyCtxt<'tcx>, body: &Body<'tcx>) { let hir_id = tcx.hir().local_def_id_to_hir_id(def_id); if let Some(fn_like_node) = FnLikeNode::from_node(tcx.hir().get(hir_id)) { - if let FnKind::Closure(_) = fn_like_node.kind() { + if let FnKind::Closure = fn_like_node.kind() { // closures can't recur, so they don't matter. return; } diff --git a/compiler/rustc_mir_build/src/thir/arena.rs b/compiler/rustc_mir_build/src/thir/arena.rs new file mode 100644 index 0000000000000..aacc7b12a42f9 --- /dev/null +++ b/compiler/rustc_mir_build/src/thir/arena.rs @@ -0,0 +1,98 @@ +use crate::thir::*; + +macro_rules! declare_arena { + ([], [$($a:tt $name:ident: $ty:ty,)*]) => { + #[derive(Default)] + pub struct Arena<'thir, 'tcx> { + pub dropless: rustc_arena::DroplessArena, + drop: rustc_arena::DropArena, + $($name: rustc_arena::arena_for_type!($a[$ty]),)* + } + + pub trait ArenaAllocatable<'thir, 'tcx, T = Self>: Sized { + fn allocate_on(self, arena: &'thir Arena<'thir, 'tcx>) -> &'thir mut Self; + fn allocate_from_iter( + arena: &'thir Arena<'thir, 'tcx>, + iter: impl ::std::iter::IntoIterator, + ) -> &'thir mut [Self]; + } + + impl<'thir, 'tcx, T: Copy> ArenaAllocatable<'thir, 'tcx, ()> for T { + #[inline] + fn allocate_on(self, arena: &'thir Arena<'thir, 'tcx>) -> &'thir mut Self { + arena.dropless.alloc(self) + } + #[inline] + fn allocate_from_iter( + arena: &'thir Arena<'thir, 'tcx>, + iter: impl ::std::iter::IntoIterator, + ) -> &'thir mut [Self] { + arena.dropless.alloc_from_iter(iter) + } + + } + $( + impl<'thir, 'tcx> ArenaAllocatable<'thir, 'tcx, $ty> for $ty { + #[inline] + fn allocate_on(self, arena: &'thir Arena<'thir, 'tcx>) -> &'thir mut Self { + if !::std::mem::needs_drop::() { + return arena.dropless.alloc(self); + } + match rustc_arena::which_arena_for_type!($a[&arena.$name]) { + ::std::option::Option::<&rustc_arena::TypedArena>::Some(ty_arena) => { + ty_arena.alloc(self) + } + ::std::option::Option::None => unsafe { arena.drop.alloc(self) }, + } + } + + #[inline] + fn allocate_from_iter( + arena: &'thir Arena<'thir, 'tcx>, + iter: impl ::std::iter::IntoIterator, + ) -> &'thir mut [Self] { + if !::std::mem::needs_drop::() { + return arena.dropless.alloc_from_iter(iter); + } + match rustc_arena::which_arena_for_type!($a[&arena.$name]) { + ::std::option::Option::<&rustc_arena::TypedArena>::Some(ty_arena) => { + ty_arena.alloc_from_iter(iter) + } + ::std::option::Option::None => unsafe { arena.drop.alloc_from_iter(iter) }, + } + } + } + )* + + impl<'thir, 'tcx> Arena<'thir, 'tcx> { + #[inline] + pub fn alloc, U>(&'thir self, value: T) -> &'thir mut T { + value.allocate_on(self) + } + + #[allow(dead_code)] // function is never used + #[inline] + pub fn alloc_slice(&'thir self, value: &[T]) -> &'thir mut [T] { + if value.is_empty() { + return &mut []; + } + self.dropless.alloc_slice(value) + } + + pub fn alloc_from_iter, U>( + &'thir self, + iter: impl ::std::iter::IntoIterator, + ) -> &'thir mut [T] { + T::allocate_from_iter(self, iter) + } + } + } +} + +declare_arena!([], [ + [] arm: Arm<'thir, 'tcx>, + [] expr: Expr<'thir, 'tcx>, + [] field_expr: FieldExpr<'thir, 'tcx>, + [few] inline_asm_operand: InlineAsmOperand<'thir, 'tcx>, + [] stmt: Stmt<'thir, 'tcx>, +]); diff --git a/compiler/rustc_mir_build/src/thir/constant.rs b/compiler/rustc_mir_build/src/thir/constant.rs index 969f7d1e3a458..ac93d042970f2 100644 --- a/compiler/rustc_mir_build/src/thir/constant.rs +++ b/compiler/rustc_mir_build/src/thir/constant.rs @@ -1,3 +1,4 @@ +use rustc_apfloat::Float; use rustc_ast as ast; use rustc_middle::mir::interpret::{ Allocation, ConstValue, LitToConstError, LitToConstInput, Scalar, @@ -61,20 +62,40 @@ fn parse_float<'tcx>(num: Symbol, fty: ty::FloatTy, neg: bool) -> Result { - num.parse::().map_err(|_| ())?; + let rust_f = num.parse::().map_err(|_| ())?; let mut f = num.parse::().unwrap_or_else(|e| { panic!("apfloat::ieee::Single failed to parse `{}`: {:?}", num, e) }); + assert!( + u128::from(rust_f.to_bits()) == f.to_bits(), + "apfloat::ieee::Single gave different result for `{}`: \ + {}({:#x}) vs Rust's {}({:#x})", + rust_f, + f, + f.to_bits(), + Single::from_bits(rust_f.to_bits().into()), + rust_f.to_bits() + ); if neg { f = -f; } Scalar::from_f32(f) } ty::FloatTy::F64 => { - num.parse::().map_err(|_| ())?; + let rust_f = num.parse::().map_err(|_| ())?; let mut f = num.parse::().unwrap_or_else(|e| { panic!("apfloat::ieee::Double failed to parse `{}`: {:?}", num, e) }); + assert!( + u128::from(rust_f.to_bits()) == f.to_bits(), + "apfloat::ieee::Double gave different result for `{}`: \ + {}({:#x}) vs Rust's {}({:#x})", + rust_f, + f, + f.to_bits(), + Double::from_bits(rust_f.to_bits().into()), + rust_f.to_bits() + ); if neg { f = -f; } diff --git a/compiler/rustc_mir_build/src/thir/cx/block.rs b/compiler/rustc_mir_build/src/thir/cx/block.rs index 980888df7fee4..d450f8a265d99 100644 --- a/compiler/rustc_mir_build/src/thir/cx/block.rs +++ b/compiler/rustc_mir_build/src/thir/cx/block.rs @@ -1,4 +1,3 @@ -use crate::thir::cx::to_ref::ToRef; use crate::thir::cx::Cx; use crate::thir::{self, *}; @@ -8,110 +7,95 @@ use rustc_middle::ty; use rustc_index::vec::Idx; -impl<'tcx> Mirror<'tcx> for &'tcx hir::Block<'tcx> { - type Output = Block<'tcx>; - - fn make_mirror(self, cx: &mut Cx<'_, 'tcx>) -> Block<'tcx> { +impl<'thir, 'tcx> Cx<'thir, 'tcx> { + crate fn mirror_block(&mut self, block: &'tcx hir::Block<'tcx>) -> Block<'thir, 'tcx> { // We have to eagerly lower the "spine" of the statements // in order to get the lexical scoping correctly. - let stmts = mirror_stmts(cx, self.hir_id.local_id, &*self.stmts); + let stmts = self.mirror_stmts(block.hir_id.local_id, block.stmts); let opt_destruction_scope = - cx.region_scope_tree.opt_destruction_scope(self.hir_id.local_id); + self.region_scope_tree.opt_destruction_scope(block.hir_id.local_id); Block { - targeted_by_break: self.targeted_by_break, - region_scope: region::Scope { id: self.hir_id.local_id, data: region::ScopeData::Node }, + targeted_by_break: block.targeted_by_break, + region_scope: region::Scope { + id: block.hir_id.local_id, + data: region::ScopeData::Node, + }, opt_destruction_scope, - span: self.span, + span: block.span, stmts, - expr: self.expr.to_ref(), - safety_mode: match self.rules { + expr: block.expr.map(|expr| self.mirror_expr(expr)), + safety_mode: match block.rules { hir::BlockCheckMode::DefaultBlock => BlockSafety::Safe, - hir::BlockCheckMode::UnsafeBlock(..) => BlockSafety::ExplicitUnsafe(self.hir_id), + hir::BlockCheckMode::UnsafeBlock(..) => BlockSafety::ExplicitUnsafe(block.hir_id), hir::BlockCheckMode::PushUnsafeBlock(..) => BlockSafety::PushUnsafe, hir::BlockCheckMode::PopUnsafeBlock(..) => BlockSafety::PopUnsafe, }, } } -} -fn mirror_stmts<'a, 'tcx>( - cx: &mut Cx<'a, 'tcx>, - block_id: hir::ItemLocalId, - stmts: &'tcx [hir::Stmt<'tcx>], -) -> Vec> { - let mut result = vec![]; - for (index, stmt) in stmts.iter().enumerate() { - let hir_id = stmt.hir_id; - let opt_dxn_ext = cx.region_scope_tree.opt_destruction_scope(hir_id.local_id); - match stmt.kind { - hir::StmtKind::Expr(ref expr) | hir::StmtKind::Semi(ref expr) => { - result.push(StmtRef::Mirror(Box::new(Stmt { + fn mirror_stmts( + &mut self, + block_id: hir::ItemLocalId, + stmts: &'tcx [hir::Stmt<'tcx>], + ) -> &'thir [Stmt<'thir, 'tcx>] { + self.arena.alloc_from_iter(stmts.iter().enumerate().filter_map(|(index, stmt)| { + let hir_id = stmt.hir_id; + let opt_dxn_ext = self.region_scope_tree.opt_destruction_scope(hir_id.local_id); + match stmt.kind { + hir::StmtKind::Expr(ref expr) | hir::StmtKind::Semi(ref expr) => Some(Stmt { kind: StmtKind::Expr { scope: region::Scope { id: hir_id.local_id, data: region::ScopeData::Node }, - expr: expr.to_ref(), + expr: self.mirror_expr(expr), }, opt_destruction_scope: opt_dxn_ext, - }))) - } - hir::StmtKind::Item(..) => { - // ignore for purposes of the MIR - } - hir::StmtKind::Local(ref local) => { - let remainder_scope = region::Scope { - id: block_id, - data: region::ScopeData::Remainder(region::FirstStatementIndex::new(index)), - }; + }), + hir::StmtKind::Item(..) => { + // ignore for purposes of the MIR + None + } + hir::StmtKind::Local(ref local) => { + let remainder_scope = region::Scope { + id: block_id, + data: region::ScopeData::Remainder(region::FirstStatementIndex::new(index)), + }; - let mut pattern = cx.pattern_from_hir(&local.pat); + let mut pattern = self.pattern_from_hir(local.pat); - if let Some(ty) = &local.ty { - if let Some(&user_ty) = cx.typeck_results.user_provided_types().get(ty.hir_id) { - debug!("mirror_stmts: user_ty={:?}", user_ty); - pattern = Pat { - ty: pattern.ty, - span: pattern.span, - kind: Box::new(PatKind::AscribeUserType { - ascription: thir::pattern::Ascription { - user_ty: PatTyProj::from_user_type(user_ty), - user_ty_span: ty.span, - variance: ty::Variance::Covariant, - }, - subpattern: pattern, - }), - }; + if let Some(ty) = &local.ty { + if let Some(&user_ty) = + self.typeck_results.user_provided_types().get(ty.hir_id) + { + debug!("mirror_stmts: user_ty={:?}", user_ty); + pattern = Pat { + ty: pattern.ty, + span: pattern.span, + kind: Box::new(PatKind::AscribeUserType { + ascription: thir::pattern::Ascription { + user_ty: PatTyProj::from_user_type(user_ty), + user_ty_span: ty.span, + variance: ty::Variance::Covariant, + }, + subpattern: pattern, + }), + }; + } } - } - result.push(StmtRef::Mirror(Box::new(Stmt { - kind: StmtKind::Let { - remainder_scope, - init_scope: region::Scope { - id: hir_id.local_id, - data: region::ScopeData::Node, + Some(Stmt { + kind: StmtKind::Let { + remainder_scope, + init_scope: region::Scope { + id: hir_id.local_id, + data: region::ScopeData::Node, + }, + pattern, + initializer: local.init.map(|init| self.mirror_expr(init)), + lint_level: LintLevel::Explicit(local.hir_id), }, - pattern, - initializer: local.init.to_ref(), - lint_level: LintLevel::Explicit(local.hir_id), - }, - opt_destruction_scope: opt_dxn_ext, - }))); + opt_destruction_scope: opt_dxn_ext, + }) + } } - } + })) } - result -} - -crate fn to_expr_ref<'a, 'tcx>( - cx: &mut Cx<'a, 'tcx>, - block: &'tcx hir::Block<'tcx>, -) -> ExprRef<'tcx> { - let block_ty = cx.typeck_results().node_type(block.hir_id); - let temp_lifetime = cx.region_scope_tree.temporary_scope(block.hir_id.local_id); - let expr = Expr { - ty: block_ty, - temp_lifetime, - span: block.span, - kind: ExprKind::Block { body: block }, - }; - expr.to_ref() } diff --git a/compiler/rustc_mir_build/src/thir/cx/expr.rs b/compiler/rustc_mir_build/src/thir/cx/expr.rs index 620ce360e7d91..924278e1a7fb3 100644 --- a/compiler/rustc_mir_build/src/thir/cx/expr.rs +++ b/compiler/rustc_mir_build/src/thir/cx/expr.rs @@ -1,11 +1,11 @@ -use crate::thir::cx::block; -use crate::thir::cx::to_ref::ToRef; use crate::thir::cx::Cx; use crate::thir::util::UserAnnotatedTyHelpers; use crate::thir::*; +use rustc_data_structures::stack::ensure_sufficient_stack; use rustc_hir as hir; use rustc_hir::def::{CtorKind, CtorOf, DefKind, Res}; use rustc_index::vec::Idx; +use rustc_middle::hir::place::Place as HirPlace; use rustc_middle::hir::place::PlaceBase as HirPlaceBase; use rustc_middle::hir::place::ProjectionKind as HirProjectionKind; use rustc_middle::mir::interpret::Scalar; @@ -17,45 +17,71 @@ use rustc_middle::ty::subst::{InternalSubsts, SubstsRef}; use rustc_middle::ty::{self, AdtKind, Ty}; use rustc_span::Span; -impl<'tcx> Mirror<'tcx> for &'tcx hir::Expr<'tcx> { - type Output = Expr<'tcx>; +use std::iter; - fn make_mirror(self, cx: &mut Cx<'_, 'tcx>) -> Expr<'tcx> { - let temp_lifetime = cx.region_scope_tree.temporary_scope(self.hir_id.local_id); - let expr_scope = region::Scope { id: self.hir_id.local_id, data: region::ScopeData::Node }; +impl<'thir, 'tcx> Cx<'thir, 'tcx> { + /// Mirrors and allocates a single [`hir::Expr`]. If you need to mirror a whole slice + /// of expressions, prefer using [`mirror_exprs`]. + /// + /// [`mirror_exprs`]: Self::mirror_exprs + crate fn mirror_expr(&mut self, expr: &'tcx hir::Expr<'tcx>) -> &'thir Expr<'thir, 'tcx> { + // `mirror_expr` is recursing very deep. Make sure the stack doesn't overflow. + ensure_sufficient_stack(|| self.arena.alloc(self.mirror_expr_inner(expr))) + } + + /// Mirrors and allocates a slice of [`hir::Expr`]s. They will be allocated as a + /// contiguous sequence in memory. + crate fn mirror_exprs(&mut self, exprs: &'tcx [hir::Expr<'tcx>]) -> &'thir [Expr<'thir, 'tcx>] { + self.arena.alloc_from_iter(exprs.iter().map(|expr| self.mirror_expr_inner(expr))) + } - debug!("Expr::make_mirror(): id={}, span={:?}", self.hir_id, self.span); + /// Mirrors a [`hir::Expr`] without allocating it into the arena. + /// This is a separate, private function so that [`mirror_expr`] and [`mirror_exprs`] can + /// decide how to allocate this expression (alone or within a slice). + /// + /// [`mirror_expr`]: Self::mirror_expr + /// [`mirror_exprs`]: Self::mirror_exprs + pub(super) fn mirror_expr_inner( + &mut self, + hir_expr: &'tcx hir::Expr<'tcx>, + ) -> Expr<'thir, 'tcx> { + let temp_lifetime = self.region_scope_tree.temporary_scope(hir_expr.hir_id.local_id); + let expr_scope = + region::Scope { id: hir_expr.hir_id.local_id, data: region::ScopeData::Node }; - let mut expr = make_mirror_unadjusted(cx, self); + debug!("Expr::make_mirror(): id={}, span={:?}", hir_expr.hir_id, hir_expr.span); + + let mut expr = self.make_mirror_unadjusted(hir_expr); // Now apply adjustments, if any. - for adjustment in cx.typeck_results().expr_adjustments(self) { + for adjustment in self.typeck_results.expr_adjustments(hir_expr) { debug!("make_mirror: expr={:?} applying adjustment={:?}", expr, adjustment); - expr = apply_adjustment(cx, self, expr, adjustment); + expr = self.apply_adjustment(hir_expr, expr, adjustment); } // Next, wrap this up in the expr's scope. expr = Expr { temp_lifetime, ty: expr.ty, - span: self.span, + span: hir_expr.span, kind: ExprKind::Scope { region_scope: expr_scope, - value: expr.to_ref(), - lint_level: LintLevel::Explicit(self.hir_id), + value: self.arena.alloc(expr), + lint_level: LintLevel::Explicit(hir_expr.hir_id), }, }; // Finally, create a destruction scope, if any. - if let Some(region_scope) = cx.region_scope_tree.opt_destruction_scope(self.hir_id.local_id) + if let Some(region_scope) = + self.region_scope_tree.opt_destruction_scope(hir_expr.hir_id.local_id) { expr = Expr { temp_lifetime, ty: expr.ty, - span: self.span, + span: hir_expr.span, kind: ExprKind::Scope { region_scope, - value: expr.to_ref(), + value: self.arena.alloc(expr), lint_level: LintLevel::Inherited, }, }; @@ -64,364 +90,407 @@ impl<'tcx> Mirror<'tcx> for &'tcx hir::Expr<'tcx> { // OK, all done! expr } -} -fn apply_adjustment<'a, 'tcx>( - cx: &mut Cx<'a, 'tcx>, - hir_expr: &'tcx hir::Expr<'tcx>, - mut expr: Expr<'tcx>, - adjustment: &Adjustment<'tcx>, -) -> Expr<'tcx> { - let Expr { temp_lifetime, mut span, .. } = expr; - - // Adjust the span from the block, to the last expression of the - // block. This is a better span when returning a mutable reference - // with too short a lifetime. The error message will use the span - // from the assignment to the return place, which should only point - // at the returned value, not the entire function body. - // - // fn return_short_lived<'a>(x: &'a mut i32) -> &'static mut i32 { - // x - // // ^ error message points at this expression. - // } - let mut adjust_span = |expr: &mut Expr<'tcx>| { - if let ExprKind::Block { body } = expr.kind { - if let Some(ref last_expr) = body.expr { - span = last_expr.span; - expr.span = span; + fn apply_adjustment( + &mut self, + hir_expr: &'tcx hir::Expr<'tcx>, + mut expr: Expr<'thir, 'tcx>, + adjustment: &Adjustment<'tcx>, + ) -> Expr<'thir, 'tcx> { + let Expr { temp_lifetime, mut span, .. } = expr; + + // Adjust the span from the block, to the last expression of the + // block. This is a better span when returning a mutable reference + // with too short a lifetime. The error message will use the span + // from the assignment to the return place, which should only point + // at the returned value, not the entire function body. + // + // fn return_short_lived<'a>(x: &'a mut i32) -> &'static mut i32 { + // x + // // ^ error message points at this expression. + // } + let mut adjust_span = |expr: &mut Expr<'thir, 'tcx>| { + if let ExprKind::Block { body } = &expr.kind { + if let Some(ref last_expr) = body.expr { + span = last_expr.span; + expr.span = span; + } } - } - }; + }; - let kind = match adjustment.kind { - Adjust::Pointer(PointerCast::Unsize) => { - adjust_span(&mut expr); - ExprKind::Pointer { cast: PointerCast::Unsize, source: expr.to_ref() } - } - Adjust::Pointer(cast) => ExprKind::Pointer { cast, source: expr.to_ref() }, - Adjust::NeverToAny => ExprKind::NeverToAny { source: expr.to_ref() }, - Adjust::Deref(None) => { - adjust_span(&mut expr); - ExprKind::Deref { arg: expr.to_ref() } - } - Adjust::Deref(Some(deref)) => { - // We don't need to do call adjust_span here since - // deref coercions always start with a built-in deref. - let call = deref.method_call(cx.tcx(), expr.ty); + let kind = match adjustment.kind { + Adjust::Pointer(PointerCast::Unsize) => { + adjust_span(&mut expr); + ExprKind::Pointer { cast: PointerCast::Unsize, source: self.arena.alloc(expr) } + } + Adjust::Pointer(cast) => ExprKind::Pointer { cast, source: self.arena.alloc(expr) }, + Adjust::NeverToAny => ExprKind::NeverToAny { source: self.arena.alloc(expr) }, + Adjust::Deref(None) => { + adjust_span(&mut expr); + ExprKind::Deref { arg: self.arena.alloc(expr) } + } + Adjust::Deref(Some(deref)) => { + // We don't need to do call adjust_span here since + // deref coercions always start with a built-in deref. + let call = deref.method_call(self.tcx(), expr.ty); - expr = Expr { - temp_lifetime, - ty: cx.tcx.mk_ref(deref.region, ty::TypeAndMut { ty: expr.ty, mutbl: deref.mutbl }), - span, - kind: ExprKind::Borrow { - borrow_kind: deref.mutbl.to_borrow_kind(), - arg: expr.to_ref(), - }, - }; + expr = Expr { + temp_lifetime, + ty: self + .tcx + .mk_ref(deref.region, ty::TypeAndMut { ty: expr.ty, mutbl: deref.mutbl }), + span, + kind: ExprKind::Borrow { + borrow_kind: deref.mutbl.to_borrow_kind(), + arg: self.arena.alloc(expr), + }, + }; - overloaded_place( - cx, - hir_expr, - adjustment.target, - Some(call), - vec![expr.to_ref()], - deref.span, - ) - } - Adjust::Borrow(AutoBorrow::Ref(_, m)) => { - ExprKind::Borrow { borrow_kind: m.to_borrow_kind(), arg: expr.to_ref() } - } - Adjust::Borrow(AutoBorrow::RawPtr(mutability)) => { - ExprKind::AddressOf { mutability, arg: expr.to_ref() } - } - }; + self.overloaded_place( + hir_expr, + adjustment.target, + Some(call), + self.arena.alloc_from_iter(iter::once(expr)), + deref.span, + ) + } + Adjust::Borrow(AutoBorrow::Ref(_, m)) => { + ExprKind::Borrow { borrow_kind: m.to_borrow_kind(), arg: self.arena.alloc(expr) } + } + Adjust::Borrow(AutoBorrow::RawPtr(mutability)) => { + ExprKind::AddressOf { mutability, arg: self.arena.alloc(expr) } + } + }; - Expr { temp_lifetime, ty: adjustment.target, span, kind } -} + Expr { temp_lifetime, ty: adjustment.target, span, kind } + } -fn make_mirror_unadjusted<'a, 'tcx>( - cx: &mut Cx<'a, 'tcx>, - expr: &'tcx hir::Expr<'tcx>, -) -> Expr<'tcx> { - let expr_ty = cx.typeck_results().expr_ty(expr); - let temp_lifetime = cx.region_scope_tree.temporary_scope(expr.hir_id.local_id); - - let kind = match expr.kind { - // Here comes the interesting stuff: - hir::ExprKind::MethodCall(_, method_span, ref args, fn_span) => { - // Rewrite a.b(c) into UFCS form like Trait::b(a, c) - let expr = method_callee(cx, expr, method_span, None); - let args = args.iter().map(|e| e.to_ref()).collect(); - ExprKind::Call { ty: expr.ty, fun: expr.to_ref(), args, from_hir_call: true, fn_span } - } + fn make_mirror_unadjusted(&mut self, expr: &'tcx hir::Expr<'tcx>) -> Expr<'thir, 'tcx> { + let expr_ty = self.typeck_results().expr_ty(expr); + let temp_lifetime = self.region_scope_tree.temporary_scope(expr.hir_id.local_id); - hir::ExprKind::Call(ref fun, ref args) => { - if cx.typeck_results().is_method_call(expr) { - // The callee is something implementing Fn, FnMut, or FnOnce. - // Find the actual method implementation being called and - // build the appropriate UFCS call expression with the - // callee-object as expr parameter. + let kind = match expr.kind { + // Here comes the interesting stuff: + hir::ExprKind::MethodCall(_, method_span, ref args, fn_span) => { + // Rewrite a.b(c) into UFCS form like Trait::b(a, c) + let expr = self.method_callee(expr, method_span, None); + let args = self.mirror_exprs(args); + ExprKind::Call { + ty: expr.ty, + fun: self.arena.alloc(expr), + args, + from_hir_call: true, + fn_span, + } + } - // rewrite f(u, v) into FnOnce::call_once(f, (u, v)) + hir::ExprKind::Call(ref fun, ref args) => { + if self.typeck_results().is_method_call(expr) { + // The callee is something implementing Fn, FnMut, or FnOnce. + // Find the actual method implementation being called and + // build the appropriate UFCS call expression with the + // callee-object as expr parameter. - let method = method_callee(cx, expr, fun.span, None); + // rewrite f(u, v) into FnOnce::call_once(f, (u, v)) - let arg_tys = args.iter().map(|e| cx.typeck_results().expr_ty_adjusted(e)); - let tupled_args = Expr { - ty: cx.tcx.mk_tup(arg_tys), - temp_lifetime, - span: expr.span, - kind: ExprKind::Tuple { fields: args.iter().map(ToRef::to_ref).collect() }, - }; + let method = self.method_callee(expr, fun.span, None); - ExprKind::Call { - ty: method.ty, - fun: method.to_ref(), - args: vec![fun.to_ref(), tupled_args.to_ref()], - from_hir_call: true, - fn_span: expr.span, - } - } else { - let adt_data = - if let hir::ExprKind::Path(hir::QPath::Resolved(_, ref path)) = fun.kind { - // Tuple-like ADTs are represented as ExprKind::Call. We convert them here. - expr_ty.ty_adt_def().and_then(|adt_def| match path.res { - Res::Def(DefKind::Ctor(_, CtorKind::Fn), ctor_id) => { - Some((adt_def, adt_def.variant_index_with_ctor_id(ctor_id))) - } - Res::SelfCtor(..) => Some((adt_def, VariantIdx::new(0))), - _ => None, - }) - } else { - None + let arg_tys = args.iter().map(|e| self.typeck_results().expr_ty_adjusted(e)); + let tupled_args = Expr { + ty: self.tcx.mk_tup(arg_tys), + temp_lifetime, + span: expr.span, + kind: ExprKind::Tuple { fields: self.mirror_exprs(args) }, }; - if let Some((adt_def, index)) = adt_data { - let substs = cx.typeck_results().node_substs(fun.hir_id); - let user_provided_types = cx.typeck_results().user_provided_types(); - let user_ty = user_provided_types.get(fun.hir_id).copied().map(|mut u_ty| { - if let UserType::TypeOf(ref mut did, _) = &mut u_ty.value { - *did = adt_def.did; - } - u_ty - }); - debug!("make_mirror_unadjusted: (call) user_ty={:?}", user_ty); - let field_refs = args - .iter() - .enumerate() - .map(|(idx, e)| FieldExprRef { name: Field::new(idx), expr: e.to_ref() }) - .collect(); - ExprKind::Adt { - adt_def, - substs, - variant_index: index, - fields: field_refs, - user_ty, - base: None, - } - } else { ExprKind::Call { - ty: cx.typeck_results().node_type(fun.hir_id), - fun: fun.to_ref(), - args: args.to_ref(), + ty: method.ty, + fun: self.arena.alloc(method), + args: self + .arena + .alloc_from_iter(vec![self.mirror_expr_inner(fun), tupled_args]), from_hir_call: true, fn_span: expr.span, } + } else { + let adt_data = + if let hir::ExprKind::Path(hir::QPath::Resolved(_, ref path)) = fun.kind { + // Tuple-like ADTs are represented as ExprKind::Call. We convert them here. + expr_ty.ty_adt_def().and_then(|adt_def| match path.res { + Res::Def(DefKind::Ctor(_, CtorKind::Fn), ctor_id) => { + Some((adt_def, adt_def.variant_index_with_ctor_id(ctor_id))) + } + Res::SelfCtor(..) => Some((adt_def, VariantIdx::new(0))), + _ => None, + }) + } else { + None + }; + if let Some((adt_def, index)) = adt_data { + let substs = self.typeck_results().node_substs(fun.hir_id); + let user_provided_types = self.typeck_results().user_provided_types(); + let user_ty = + user_provided_types.get(fun.hir_id).copied().map(|mut u_ty| { + if let UserType::TypeOf(ref mut did, _) = &mut u_ty.value { + *did = adt_def.did; + } + u_ty + }); + debug!("make_mirror_unadjusted: (call) user_ty={:?}", user_ty); + + let field_refs = + self.arena.alloc_from_iter(args.iter().enumerate().map(|(idx, e)| { + FieldExpr { name: Field::new(idx), expr: self.mirror_expr(e) } + })); + ExprKind::Adt { + adt_def, + substs, + variant_index: index, + fields: field_refs, + user_ty, + base: None, + } + } else { + ExprKind::Call { + ty: self.typeck_results().node_type(fun.hir_id), + fun: self.mirror_expr(fun), + args: self.mirror_exprs(args), + from_hir_call: true, + fn_span: expr.span, + } + } } } - } - hir::ExprKind::AddrOf(hir::BorrowKind::Ref, mutbl, ref arg) => { - ExprKind::Borrow { borrow_kind: mutbl.to_borrow_kind(), arg: arg.to_ref() } - } + hir::ExprKind::AddrOf(hir::BorrowKind::Ref, mutbl, ref arg) => { + ExprKind::Borrow { borrow_kind: mutbl.to_borrow_kind(), arg: self.mirror_expr(arg) } + } - hir::ExprKind::AddrOf(hir::BorrowKind::Raw, mutability, ref arg) => { - ExprKind::AddressOf { mutability, arg: arg.to_ref() } - } + hir::ExprKind::AddrOf(hir::BorrowKind::Raw, mutability, ref arg) => { + ExprKind::AddressOf { mutability, arg: self.mirror_expr(arg) } + } - hir::ExprKind::Block(ref blk, _) => ExprKind::Block { body: &blk }, + hir::ExprKind::Block(ref blk, _) => ExprKind::Block { body: self.mirror_block(blk) }, - hir::ExprKind::Assign(ref lhs, ref rhs, _) => { - ExprKind::Assign { lhs: lhs.to_ref(), rhs: rhs.to_ref() } - } + hir::ExprKind::Assign(ref lhs, ref rhs, _) => { + ExprKind::Assign { lhs: self.mirror_expr(lhs), rhs: self.mirror_expr(rhs) } + } - hir::ExprKind::AssignOp(op, ref lhs, ref rhs) => { - if cx.typeck_results().is_method_call(expr) { - overloaded_operator(cx, expr, vec![lhs.to_ref(), rhs.to_ref()]) - } else { - ExprKind::AssignOp { op: bin_op(op.node), lhs: lhs.to_ref(), rhs: rhs.to_ref() } + hir::ExprKind::AssignOp(op, ref lhs, ref rhs) => { + if self.typeck_results().is_method_call(expr) { + let lhs = self.mirror_expr_inner(lhs); + let rhs = self.mirror_expr_inner(rhs); + self.overloaded_operator(expr, self.arena.alloc_from_iter(vec![lhs, rhs])) + } else { + ExprKind::AssignOp { + op: bin_op(op.node), + lhs: self.mirror_expr(lhs), + rhs: self.mirror_expr(rhs), + } + } } - } - hir::ExprKind::Lit(ref lit) => ExprKind::Literal { - literal: cx.const_eval_literal(&lit.node, expr_ty, lit.span, false), - user_ty: None, - const_id: None, - }, - - hir::ExprKind::Binary(op, ref lhs, ref rhs) => { - if cx.typeck_results().is_method_call(expr) { - overloaded_operator(cx, expr, vec![lhs.to_ref(), rhs.to_ref()]) - } else { - // FIXME overflow - match (op.node, cx.constness) { - (hir::BinOpKind::And, _) => ExprKind::LogicalOp { - op: LogicalOp::And, - lhs: lhs.to_ref(), - rhs: rhs.to_ref(), - }, - (hir::BinOpKind::Or, _) => ExprKind::LogicalOp { - op: LogicalOp::Or, - lhs: lhs.to_ref(), - rhs: rhs.to_ref(), - }, + hir::ExprKind::Lit(ref lit) => ExprKind::Literal { + literal: self.const_eval_literal(&lit.node, expr_ty, lit.span, false), + user_ty: None, + const_id: None, + }, - _ => { - let op = bin_op(op.node); - ExprKind::Binary { op, lhs: lhs.to_ref(), rhs: rhs.to_ref() } + hir::ExprKind::Binary(op, ref lhs, ref rhs) => { + if self.typeck_results().is_method_call(expr) { + let lhs = self.mirror_expr_inner(lhs); + let rhs = self.mirror_expr_inner(rhs); + self.overloaded_operator(expr, self.arena.alloc_from_iter(vec![lhs, rhs])) + } else { + // FIXME overflow + match op.node { + hir::BinOpKind::And => ExprKind::LogicalOp { + op: LogicalOp::And, + lhs: self.mirror_expr(lhs), + rhs: self.mirror_expr(rhs), + }, + hir::BinOpKind::Or => ExprKind::LogicalOp { + op: LogicalOp::Or, + lhs: self.mirror_expr(lhs), + rhs: self.mirror_expr(rhs), + }, + + _ => { + let op = bin_op(op.node); + ExprKind::Binary { + op, + lhs: self.mirror_expr(lhs), + rhs: self.mirror_expr(rhs), + } + } } } } - } - hir::ExprKind::Index(ref lhs, ref index) => { - if cx.typeck_results().is_method_call(expr) { - overloaded_place( - cx, - expr, - expr_ty, - None, - vec![lhs.to_ref(), index.to_ref()], - expr.span, - ) - } else { - ExprKind::Index { lhs: lhs.to_ref(), index: index.to_ref() } + hir::ExprKind::Index(ref lhs, ref index) => { + if self.typeck_results().is_method_call(expr) { + let lhs = self.mirror_expr_inner(lhs); + let index = self.mirror_expr_inner(index); + self.overloaded_place( + expr, + expr_ty, + None, + self.arena.alloc_from_iter(vec![lhs, index]), + expr.span, + ) + } else { + ExprKind::Index { lhs: self.mirror_expr(lhs), index: self.mirror_expr(index) } + } } - } - hir::ExprKind::Unary(hir::UnOp::Deref, ref arg) => { - if cx.typeck_results().is_method_call(expr) { - overloaded_place(cx, expr, expr_ty, None, vec![arg.to_ref()], expr.span) - } else { - ExprKind::Deref { arg: arg.to_ref() } + hir::ExprKind::Unary(hir::UnOp::Deref, ref arg) => { + if self.typeck_results().is_method_call(expr) { + let arg = self.mirror_expr_inner(arg); + self.overloaded_place( + expr, + expr_ty, + None, + self.arena.alloc_from_iter(iter::once(arg)), + expr.span, + ) + } else { + ExprKind::Deref { arg: self.mirror_expr(arg) } + } } - } - hir::ExprKind::Unary(hir::UnOp::Not, ref arg) => { - if cx.typeck_results().is_method_call(expr) { - overloaded_operator(cx, expr, vec![arg.to_ref()]) - } else { - ExprKind::Unary { op: UnOp::Not, arg: arg.to_ref() } + hir::ExprKind::Unary(hir::UnOp::Not, ref arg) => { + if self.typeck_results().is_method_call(expr) { + let arg = self.mirror_expr_inner(arg); + self.overloaded_operator(expr, self.arena.alloc_from_iter(iter::once(arg))) + } else { + ExprKind::Unary { op: UnOp::Not, arg: self.mirror_expr(arg) } + } } - } - hir::ExprKind::Unary(hir::UnOp::Neg, ref arg) => { - if cx.typeck_results().is_method_call(expr) { - overloaded_operator(cx, expr, vec![arg.to_ref()]) - } else if let hir::ExprKind::Lit(ref lit) = arg.kind { - ExprKind::Literal { - literal: cx.const_eval_literal(&lit.node, expr_ty, lit.span, true), - user_ty: None, - const_id: None, + hir::ExprKind::Unary(hir::UnOp::Neg, ref arg) => { + if self.typeck_results().is_method_call(expr) { + let arg = self.mirror_expr_inner(arg); + self.overloaded_operator(expr, self.arena.alloc_from_iter(iter::once(arg))) + } else if let hir::ExprKind::Lit(ref lit) = arg.kind { + ExprKind::Literal { + literal: self.const_eval_literal(&lit.node, expr_ty, lit.span, true), + user_ty: None, + const_id: None, + } + } else { + ExprKind::Unary { op: UnOp::Neg, arg: self.mirror_expr(arg) } } - } else { - ExprKind::Unary { op: UnOp::Neg, arg: arg.to_ref() } } - } - hir::ExprKind::Struct(ref qpath, ref fields, ref base) => match expr_ty.kind() { - ty::Adt(adt, substs) => match adt.adt_kind() { - AdtKind::Struct | AdtKind::Union => { - let user_provided_types = cx.typeck_results().user_provided_types(); - let user_ty = user_provided_types.get(expr.hir_id).copied(); - debug!("make_mirror_unadjusted: (struct/union) user_ty={:?}", user_ty); - ExprKind::Adt { - adt_def: adt, - variant_index: VariantIdx::new(0), - substs, - user_ty, - fields: field_refs(cx, fields), - base: base.as_ref().map(|base| FruInfo { - base: base.to_ref(), - field_types: cx.typeck_results().fru_field_types()[expr.hir_id].clone(), - }), + hir::ExprKind::Struct(ref qpath, ref fields, ref base) => match expr_ty.kind() { + ty::Adt(adt, substs) => match adt.adt_kind() { + AdtKind::Struct | AdtKind::Union => { + let user_provided_types = self.typeck_results().user_provided_types(); + let user_ty = user_provided_types.get(expr.hir_id).copied(); + debug!("make_mirror_unadjusted: (struct/union) user_ty={:?}", user_ty); + ExprKind::Adt { + adt_def: adt, + variant_index: VariantIdx::new(0), + substs, + user_ty, + fields: self.field_refs(fields), + base: base.as_ref().map(|base| FruInfo { + base: self.mirror_expr(base), + field_types: self.arena.alloc_from_iter( + self.typeck_results().fru_field_types()[expr.hir_id] + .iter() + .cloned(), + ), + }), + } } - } - AdtKind::Enum => { - let res = cx.typeck_results().qpath_res(qpath, expr.hir_id); - match res { - Res::Def(DefKind::Variant, variant_id) => { - assert!(base.is_none()); - - let index = adt.variant_index_with_id(variant_id); - let user_provided_types = cx.typeck_results().user_provided_types(); - let user_ty = user_provided_types.get(expr.hir_id).copied(); - debug!("make_mirror_unadjusted: (variant) user_ty={:?}", user_ty); - ExprKind::Adt { - adt_def: adt, - variant_index: index, - substs, - user_ty, - fields: field_refs(cx, fields), - base: None, + AdtKind::Enum => { + let res = self.typeck_results().qpath_res(qpath, expr.hir_id); + match res { + Res::Def(DefKind::Variant, variant_id) => { + assert!(base.is_none()); + + let index = adt.variant_index_with_id(variant_id); + let user_provided_types = + self.typeck_results().user_provided_types(); + let user_ty = user_provided_types.get(expr.hir_id).copied(); + debug!("make_mirror_unadjusted: (variant) user_ty={:?}", user_ty); + ExprKind::Adt { + adt_def: adt, + variant_index: index, + substs, + user_ty, + fields: self.field_refs(fields), + base: None, + } + } + _ => { + span_bug!(expr.span, "unexpected res: {:?}", res); } - } - _ => { - span_bug!(expr.span, "unexpected res: {:?}", res); } } - } - }, - _ => { - span_bug!(expr.span, "unexpected type for struct literal: {:?}", expr_ty); - } - }, - - hir::ExprKind::Closure(..) => { - let closure_ty = cx.typeck_results().expr_ty(expr); - let (def_id, substs, movability) = match *closure_ty.kind() { - ty::Closure(def_id, substs) => (def_id, UpvarSubsts::Closure(substs), None), - ty::Generator(def_id, substs, movability) => { - (def_id, UpvarSubsts::Generator(substs), Some(movability)) - } + }, _ => { - span_bug!(expr.span, "closure expr w/o closure type: {:?}", closure_ty); + span_bug!(expr.span, "unexpected type for struct literal: {:?}", expr_ty); } - }; + }, - let upvars = cx - .typeck_results() - .closure_min_captures_flattened(def_id) - .zip(substs.upvar_tys()) - .map(|(captured_place, ty)| capture_upvar(cx, expr, captured_place, ty)) - .collect(); - ExprKind::Closure { closure_id: def_id, substs, upvars, movability } - } + hir::ExprKind::Closure(..) => { + let closure_ty = self.typeck_results().expr_ty(expr); + let (def_id, substs, movability) = match *closure_ty.kind() { + ty::Closure(def_id, substs) => (def_id, UpvarSubsts::Closure(substs), None), + ty::Generator(def_id, substs, movability) => { + (def_id, UpvarSubsts::Generator(substs), Some(movability)) + } + _ => { + span_bug!(expr.span, "closure expr w/o closure type: {:?}", closure_ty); + } + }; - hir::ExprKind::Path(ref qpath) => { - let res = cx.typeck_results().qpath_res(qpath, expr.hir_id); - convert_path_expr(cx, expr, res) - } + let upvars = self.arena.alloc_from_iter( + self.typeck_results + .closure_min_captures_flattened(def_id) + .zip(substs.upvar_tys()) + .map(|(captured_place, ty)| self.capture_upvar(expr, captured_place, ty)), + ); + + // Convert the closure fake reads, if any, from hir `Place` to ExprRef + let fake_reads = match self.typeck_results.closure_fake_reads.get(&def_id) { + Some(fake_reads) => fake_reads + .iter() + .map(|(place, cause, hir_id)| { + let expr = self.convert_captured_hir_place(expr, place.clone()); + let expr_ref: &'thir Expr<'thir, 'tcx> = self.arena.alloc(expr); + (expr_ref, *cause, *hir_id) + }) + .collect(), + None => Vec::new(), + }; + + ExprKind::Closure { closure_id: def_id, substs, upvars, movability, fake_reads } + } + + hir::ExprKind::Path(ref qpath) => { + let res = self.typeck_results().qpath_res(qpath, expr.hir_id); + self.convert_path_expr(expr, res) + } - hir::ExprKind::InlineAsm(ref asm) => ExprKind::InlineAsm { - template: asm.template, - operands: asm - .operands - .iter() - .map(|(op, _op_sp)| { + hir::ExprKind::InlineAsm(ref asm) => ExprKind::InlineAsm { + template: asm.template, + operands: self.arena.alloc_from_iter(asm.operands.iter().map(|(op, _op_sp)| { match *op { hir::InlineAsmOperand::In { reg, ref expr } => { - InlineAsmOperand::In { reg, expr: expr.to_ref() } + InlineAsmOperand::In { reg, expr: self.mirror_expr(expr) } } hir::InlineAsmOperand::Out { reg, late, ref expr } => { InlineAsmOperand::Out { reg, late, - expr: expr.as_ref().map(|expr| expr.to_ref()), + expr: expr.as_ref().map(|expr| self.mirror_expr(expr)), } } hir::InlineAsmOperand::InOut { reg, late, ref expr } => { - InlineAsmOperand::InOut { reg, late, expr: expr.to_ref() } + InlineAsmOperand::InOut { reg, late, expr: self.mirror_expr(expr) } } hir::InlineAsmOperand::SplitInOut { reg, @@ -431,11 +500,15 @@ fn make_mirror_unadjusted<'a, 'tcx>( } => InlineAsmOperand::SplitInOut { reg, late, - in_expr: in_expr.to_ref(), - out_expr: out_expr.as_ref().map(|expr| expr.to_ref()), + in_expr: self.mirror_expr(in_expr), + out_expr: out_expr.as_ref().map(|expr| self.mirror_expr(expr)), }, - hir::InlineAsmOperand::Const { ref expr } => { - InlineAsmOperand::Const { expr: expr.to_ref() } + hir::InlineAsmOperand::Const { ref anon_const } => { + let anon_const_def_id = self.tcx.hir().local_def_id(anon_const.hir_id); + let value = ty::Const::from_anon_const(self.tcx, anon_const_def_id); + let span = self.tcx.hir().span(anon_const.hir_id); + + InlineAsmOperand::Const { value, span } } hir::InlineAsmOperand::Sym { ref expr } => { let qpath = match expr.kind { @@ -447,25 +520,24 @@ fn make_mirror_unadjusted<'a, 'tcx>( ), }; let temp_lifetime = - cx.region_scope_tree.temporary_scope(expr.hir_id.local_id); - let res = cx.typeck_results().qpath_res(qpath, expr.hir_id); + self.region_scope_tree.temporary_scope(expr.hir_id.local_id); + let res = self.typeck_results().qpath_res(qpath, expr.hir_id); let ty; match res { Res::Def(DefKind::Fn, _) | Res::Def(DefKind::AssocFn, _) => { - ty = cx.typeck_results().node_type(expr.hir_id); - let user_ty = user_substs_applied_to_res(cx, expr.hir_id, res); + ty = self.typeck_results().node_type(expr.hir_id); + let user_ty = self.user_substs_applied_to_res(expr.hir_id, res); InlineAsmOperand::SymFn { - expr: Expr { + expr: self.arena.alloc(Expr { ty, temp_lifetime, span: expr.span, kind: ExprKind::Literal { - literal: ty::Const::zero_sized(cx.tcx, ty), + literal: ty::Const::zero_sized(self.tcx, ty), user_ty, const_id: None, }, - } - .to_ref(), + }), } } @@ -474,277 +546,583 @@ fn make_mirror_unadjusted<'a, 'tcx>( } _ => { - cx.tcx.sess.span_err( + self.tcx.sess.span_err( expr.span, "asm `sym` operand must point to a fn or static", ); // Not a real fn, but we're not reaching codegen anyways... - ty = cx.tcx.ty_error(); + ty = self.tcx.ty_error(); InlineAsmOperand::SymFn { - expr: Expr { + expr: self.arena.alloc(Expr { ty, temp_lifetime, span: expr.span, kind: ExprKind::Literal { - literal: ty::Const::zero_sized(cx.tcx, ty), + literal: ty::Const::zero_sized(self.tcx, ty), user_ty: None, const_id: None, }, - } - .to_ref(), + }), } } } } } - }) - .collect(), - options: asm.options, - line_spans: asm.line_spans, - }, - - hir::ExprKind::LlvmInlineAsm(ref asm) => ExprKind::LlvmInlineAsm { - asm: &asm.inner, - outputs: asm.outputs_exprs.to_ref(), - inputs: asm.inputs_exprs.to_ref(), - }, - - hir::ExprKind::ConstBlock(ref anon_const) => { - let anon_const_def_id = cx.tcx.hir().local_def_id(anon_const.hir_id); - let value = ty::Const::from_anon_const(cx.tcx, anon_const_def_id); - - ExprKind::ConstBlock { value } - } - // Now comes the rote stuff: - hir::ExprKind::Repeat(ref v, ref count) => { - let count_def_id = cx.tcx.hir().local_def_id(count.hir_id); - let count = ty::Const::from_anon_const(cx.tcx, count_def_id); + })), + options: asm.options, + line_spans: asm.line_spans, + }, - ExprKind::Repeat { value: v.to_ref(), count } - } - hir::ExprKind::Ret(ref v) => ExprKind::Return { value: v.to_ref() }, - hir::ExprKind::Break(dest, ref value) => match dest.target_id { - Ok(target_id) => ExprKind::Break { - label: region::Scope { id: target_id.local_id, data: region::ScopeData::Node }, - value: value.to_ref(), + hir::ExprKind::LlvmInlineAsm(ref asm) => ExprKind::LlvmInlineAsm { + asm: &asm.inner, + outputs: self.mirror_exprs(asm.outputs_exprs), + inputs: self.mirror_exprs(asm.inputs_exprs), + }, + + hir::ExprKind::ConstBlock(ref anon_const) => { + let anon_const_def_id = self.tcx.hir().local_def_id(anon_const.hir_id); + let value = ty::Const::from_anon_const(self.tcx, anon_const_def_id); + + ExprKind::ConstBlock { value } + } + // Now comes the rote stuff: + hir::ExprKind::Repeat(ref v, ref count) => { + let count_def_id = self.tcx.hir().local_def_id(count.hir_id); + let count = ty::Const::from_anon_const(self.tcx, count_def_id); + + ExprKind::Repeat { value: self.mirror_expr(v), count } + } + hir::ExprKind::Ret(ref v) => { + ExprKind::Return { value: v.as_ref().map(|v| self.mirror_expr(v)) } + } + hir::ExprKind::Break(dest, ref value) => match dest.target_id { + Ok(target_id) => ExprKind::Break { + label: region::Scope { id: target_id.local_id, data: region::ScopeData::Node }, + value: value.as_ref().map(|value| self.mirror_expr(value)), + }, + Err(err) => bug!("invalid loop id for break: {}", err), }, - Err(err) => bug!("invalid loop id for break: {}", err), - }, - hir::ExprKind::Continue(dest) => match dest.target_id { - Ok(loop_id) => ExprKind::Continue { - label: region::Scope { id: loop_id.local_id, data: region::ScopeData::Node }, + hir::ExprKind::Continue(dest) => match dest.target_id { + Ok(loop_id) => ExprKind::Continue { + label: region::Scope { id: loop_id.local_id, data: region::ScopeData::Node }, + }, + Err(err) => bug!("invalid loop id for continue: {}", err), }, - Err(err) => bug!("invalid loop id for continue: {}", err), - }, - hir::ExprKind::If(cond, then, else_opt) => ExprKind::If { - cond: cond.to_ref(), - then: then.to_ref(), - else_opt: else_opt.map(|el| el.to_ref()), - }, - hir::ExprKind::Match(ref discr, ref arms, _) => ExprKind::Match { - scrutinee: discr.to_ref(), - arms: arms.iter().map(|a| convert_arm(cx, a)).collect(), - }, - hir::ExprKind::Loop(ref body, ..) => ExprKind::Loop { body: block::to_expr_ref(cx, body) }, - hir::ExprKind::Field(ref source, ..) => ExprKind::Field { - lhs: source.to_ref(), - name: Field::new(cx.tcx.field_index(expr.hir_id, cx.typeck_results)), - }, - hir::ExprKind::Cast(ref source, ref cast_ty) => { - // Check for a user-given type annotation on this `cast` - let user_provided_types = cx.typeck_results.user_provided_types(); - let user_ty = user_provided_types.get(cast_ty.hir_id); - - debug!( - "cast({:?}) has ty w/ hir_id {:?} and user provided ty {:?}", - expr, cast_ty.hir_id, user_ty, - ); - - // Check to see if this cast is a "coercion cast", where the cast is actually done - // using a coercion (or is a no-op). - let cast = if cx.typeck_results().is_coercion_cast(source.hir_id) { - // Convert the lexpr to a vexpr. - ExprKind::Use { source: source.to_ref() } - } else if cx.typeck_results().expr_ty(source).is_region_ptr() { - // Special cased so that we can type check that the element - // type of the source matches the pointed to type of the - // destination. - ExprKind::Pointer { source: source.to_ref(), cast: PointerCast::ArrayToPointer } - } else { - // check whether this is casting an enum variant discriminant - // to prevent cycles, we refer to the discriminant initializer - // which is always an integer and thus doesn't need to know the - // enum's layout (or its tag type) to compute it during const eval - // Example: - // enum Foo { - // A, - // B = A as isize + 4, - // } - // The correct solution would be to add symbolic computations to miri, - // so we wouldn't have to compute and store the actual value - let var = if let hir::ExprKind::Path(ref qpath) = source.kind { - let res = cx.typeck_results().qpath_res(qpath, source.hir_id); - cx.typeck_results().node_type(source.hir_id).ty_adt_def().and_then(|adt_def| { - match res { - Res::Def( - DefKind::Ctor(CtorOf::Variant, CtorKind::Const), - variant_ctor_id, - ) => { - let idx = adt_def.variant_index_with_ctor_id(variant_ctor_id); - let (d, o) = adt_def.discriminant_def_for_variant(idx); - use rustc_middle::ty::util::IntTypeExt; - let ty = adt_def.repr.discr_type(); - let ty = ty.to_ty(cx.tcx()); - Some((d, o, ty)) - } - _ => None, - } - }) + hir::ExprKind::If(cond, then, else_opt) => ExprKind::If { + cond: self.mirror_expr(cond), + then: self.mirror_expr(then), + else_opt: else_opt.map(|el| self.mirror_expr(el)), + }, + hir::ExprKind::Match(ref discr, ref arms, _) => ExprKind::Match { + scrutinee: self.mirror_expr(discr), + arms: self.arena.alloc_from_iter(arms.iter().map(|a| self.convert_arm(a))), + }, + hir::ExprKind::Loop(ref body, ..) => { + let block_ty = self.typeck_results().node_type(body.hir_id); + let temp_lifetime = self.region_scope_tree.temporary_scope(body.hir_id.local_id); + let block = self.mirror_block(body); + let body = self.arena.alloc(Expr { + ty: block_ty, + temp_lifetime, + span: block.span, + kind: ExprKind::Block { body: block }, + }); + ExprKind::Loop { body } + } + hir::ExprKind::Field(ref source, ..) => ExprKind::Field { + lhs: self.mirror_expr(source), + name: Field::new(self.tcx.field_index(expr.hir_id, self.typeck_results)), + }, + hir::ExprKind::Cast(ref source, ref cast_ty) => { + // Check for a user-given type annotation on this `cast` + let user_provided_types = self.typeck_results.user_provided_types(); + let user_ty = user_provided_types.get(cast_ty.hir_id); + + debug!( + "cast({:?}) has ty w/ hir_id {:?} and user provided ty {:?}", + expr, cast_ty.hir_id, user_ty, + ); + + // Check to see if this cast is a "coercion cast", where the cast is actually done + // using a coercion (or is a no-op). + let cast = if self.typeck_results().is_coercion_cast(source.hir_id) { + // Convert the lexpr to a vexpr. + ExprKind::Use { source: self.mirror_expr(source) } + } else if self.typeck_results().expr_ty(source).is_region_ptr() { + // Special cased so that we can type check that the element + // type of the source matches the pointed to type of the + // destination. + ExprKind::Pointer { + source: self.mirror_expr(source), + cast: PointerCast::ArrayToPointer, + } } else { - None - }; - - let source = if let Some((did, offset, var_ty)) = var { - let mk_const = |literal| { - Expr { - temp_lifetime, - ty: var_ty, - span: expr.span, - kind: ExprKind::Literal { literal, user_ty: None, const_id: None }, - } - .to_ref() + // check whether this is casting an enum variant discriminant + // to prevent cycles, we refer to the discriminant initializer + // which is always an integer and thus doesn't need to know the + // enum's layout (or its tag type) to compute it during const eval + // Example: + // enum Foo { + // A, + // B = A as isize + 4, + // } + // The correct solution would be to add symbolic computations to miri, + // so we wouldn't have to compute and store the actual value + let var = if let hir::ExprKind::Path(ref qpath) = source.kind { + let res = self.typeck_results().qpath_res(qpath, source.hir_id); + self.typeck_results().node_type(source.hir_id).ty_adt_def().and_then( + |adt_def| match res { + Res::Def( + DefKind::Ctor(CtorOf::Variant, CtorKind::Const), + variant_ctor_id, + ) => { + let idx = adt_def.variant_index_with_ctor_id(variant_ctor_id); + let (d, o) = adt_def.discriminant_def_for_variant(idx); + use rustc_middle::ty::util::IntTypeExt; + let ty = adt_def.repr.discr_type(); + let ty = ty.to_ty(self.tcx()); + Some((d, o, ty)) + } + _ => None, + }, + ) + } else { + None }; - let offset = mk_const(ty::Const::from_bits( - cx.tcx, - offset as u128, - cx.param_env.and(var_ty), - )); - match did { - Some(did) => { - // in case we are offsetting from a computed discriminant - // and not the beginning of discriminants (which is always `0`) - let substs = InternalSubsts::identity_for_item(cx.tcx(), did); - let lhs = mk_const(cx.tcx().mk_const(ty::Const { - val: ty::ConstKind::Unevaluated( - ty::WithOptConstParam::unknown(did), - substs, - None, - ), + + let source = if let Some((did, offset, var_ty)) = var { + let mk_const = |literal| { + self.arena.alloc(Expr { + temp_lifetime, ty: var_ty, - })); - let bin = ExprKind::Binary { op: BinOp::Add, lhs, rhs: offset }; - Expr { temp_lifetime, ty: var_ty, span: expr.span, kind: bin }.to_ref() + span: expr.span, + kind: ExprKind::Literal { literal, user_ty: None, const_id: None }, + }) + }; + let offset = mk_const(ty::Const::from_bits( + self.tcx, + offset as u128, + self.param_env.and(var_ty), + )); + match did { + Some(did) => { + // in case we are offsetting from a computed discriminant + // and not the beginning of discriminants (which is always `0`) + let substs = InternalSubsts::identity_for_item(self.tcx(), did); + let lhs = mk_const(self.tcx().mk_const(ty::Const { + val: ty::ConstKind::Unevaluated(ty::Unevaluated { + def: ty::WithOptConstParam::unknown(did), + substs, + promoted: None, + }), + ty: var_ty, + })); + let bin = + ExprKind::Binary { op: BinOp::Add, lhs: lhs, rhs: offset }; + self.arena.alloc(Expr { + temp_lifetime, + ty: var_ty, + span: expr.span, + kind: bin, + }) + } + None => offset, } - None => offset, - } - } else { - source.to_ref() - }; + } else { + self.mirror_expr(source) + }; - ExprKind::Cast { source } - }; + ExprKind::Cast { source: source } + }; - if let Some(user_ty) = user_ty { - // NOTE: Creating a new Expr and wrapping a Cast inside of it may be - // inefficient, revisit this when performance becomes an issue. - let cast_expr = Expr { temp_lifetime, ty: expr_ty, span: expr.span, kind: cast }; - debug!("make_mirror_unadjusted: (cast) user_ty={:?}", user_ty); + if let Some(user_ty) = user_ty { + // NOTE: Creating a new Expr and wrapping a Cast inside of it may be + // inefficient, revisit this when performance becomes an issue. + let cast_expr = self.arena.alloc(Expr { + temp_lifetime, + ty: expr_ty, + span: expr.span, + kind: cast, + }); + debug!("make_mirror_unadjusted: (cast) user_ty={:?}", user_ty); - ExprKind::ValueTypeAscription { - source: cast_expr.to_ref(), - user_ty: Some(*user_ty), + ExprKind::ValueTypeAscription { source: cast_expr, user_ty: Some(*user_ty) } + } else { + cast } - } else { - cast } - } - hir::ExprKind::Type(ref source, ref ty) => { - let user_provided_types = cx.typeck_results.user_provided_types(); - let user_ty = user_provided_types.get(ty.hir_id).copied(); - debug!("make_mirror_unadjusted: (type) user_ty={:?}", user_ty); - if source.is_syntactic_place_expr() { - ExprKind::PlaceTypeAscription { source: source.to_ref(), user_ty } - } else { - ExprKind::ValueTypeAscription { source: source.to_ref(), user_ty } + hir::ExprKind::Type(ref source, ref ty) => { + let user_provided_types = self.typeck_results.user_provided_types(); + let user_ty = user_provided_types.get(ty.hir_id).copied(); + debug!("make_mirror_unadjusted: (type) user_ty={:?}", user_ty); + let mirrored = self.mirror_expr(source); + if source.is_syntactic_place_expr() { + ExprKind::PlaceTypeAscription { source: mirrored, user_ty } + } else { + ExprKind::ValueTypeAscription { source: mirrored, user_ty } + } + } + hir::ExprKind::DropTemps(ref source) => { + ExprKind::Use { source: self.mirror_expr(source) } } + hir::ExprKind::Box(ref value) => ExprKind::Box { value: self.mirror_expr(value) }, + hir::ExprKind::Array(ref fields) => { + ExprKind::Array { fields: self.mirror_exprs(fields) } + } + hir::ExprKind::Tup(ref fields) => ExprKind::Tuple { fields: self.mirror_exprs(fields) }, + + hir::ExprKind::Yield(ref v, _) => ExprKind::Yield { value: self.mirror_expr(v) }, + hir::ExprKind::Err => unreachable!(), + }; + + Expr { temp_lifetime, ty: expr_ty, span: expr.span, kind } + } + + fn user_substs_applied_to_res( + &mut self, + hir_id: hir::HirId, + res: Res, + ) -> Option> { + debug!("user_substs_applied_to_res: res={:?}", res); + let user_provided_type = match res { + // A reference to something callable -- e.g., a fn, method, or + // a tuple-struct or tuple-variant. This has the type of a + // `Fn` but with the user-given substitutions. + Res::Def(DefKind::Fn, _) + | Res::Def(DefKind::AssocFn, _) + | Res::Def(DefKind::Ctor(_, CtorKind::Fn), _) + | Res::Def(DefKind::Const, _) + | Res::Def(DefKind::AssocConst, _) => { + self.typeck_results().user_provided_types().get(hir_id).copied() + } + + // A unit struct/variant which is used as a value (e.g., + // `None`). This has the type of the enum/struct that defines + // this variant -- but with the substitutions given by the + // user. + Res::Def(DefKind::Ctor(_, CtorKind::Const), _) => { + self.user_substs_applied_to_ty_of_hir_id(hir_id) + } + + // `Self` is used in expression as a tuple struct constructor or an unit struct constructor + Res::SelfCtor(_) => self.user_substs_applied_to_ty_of_hir_id(hir_id), + + _ => bug!("user_substs_applied_to_res: unexpected res {:?} at {:?}", res, hir_id), + }; + debug!("user_substs_applied_to_res: user_provided_type={:?}", user_provided_type); + user_provided_type + } + + fn method_callee( + &mut self, + expr: &hir::Expr<'_>, + span: Span, + overloaded_callee: Option<(DefId, SubstsRef<'tcx>)>, + ) -> Expr<'thir, 'tcx> { + let temp_lifetime = self.region_scope_tree.temporary_scope(expr.hir_id.local_id); + let (def_id, substs, user_ty) = match overloaded_callee { + Some((def_id, substs)) => (def_id, substs, None), + None => { + let (kind, def_id) = + self.typeck_results().type_dependent_def(expr.hir_id).unwrap_or_else(|| { + span_bug!(expr.span, "no type-dependent def for method callee") + }); + let user_ty = self.user_substs_applied_to_res(expr.hir_id, Res::Def(kind, def_id)); + debug!("method_callee: user_ty={:?}", user_ty); + (def_id, self.typeck_results().node_substs(expr.hir_id), user_ty) + } + }; + let ty = self.tcx().mk_fn_def(def_id, substs); + Expr { + temp_lifetime, + ty, + span, + kind: ExprKind::Literal { + literal: ty::Const::zero_sized(self.tcx(), ty), + user_ty, + const_id: None, + }, } - hir::ExprKind::DropTemps(ref source) => ExprKind::Use { source: source.to_ref() }, - hir::ExprKind::Box(ref value) => ExprKind::Box { value: value.to_ref() }, - hir::ExprKind::Array(ref fields) => ExprKind::Array { fields: fields.to_ref() }, - hir::ExprKind::Tup(ref fields) => ExprKind::Tuple { fields: fields.to_ref() }, + } - hir::ExprKind::Yield(ref v, _) => ExprKind::Yield { value: v.to_ref() }, - hir::ExprKind::Err => unreachable!(), - }; + fn convert_arm(&mut self, arm: &'tcx hir::Arm<'tcx>) -> Arm<'thir, 'tcx> { + Arm { + pattern: self.pattern_from_hir(&arm.pat), + guard: arm.guard.as_ref().map(|g| match g { + hir::Guard::If(ref e) => Guard::If(self.mirror_expr(e)), + hir::Guard::IfLet(ref pat, ref e) => { + Guard::IfLet(self.pattern_from_hir(pat), self.mirror_expr(e)) + } + }), + body: self.mirror_expr(arm.body), + lint_level: LintLevel::Explicit(arm.hir_id), + scope: region::Scope { id: arm.hir_id.local_id, data: region::ScopeData::Node }, + span: arm.span, + } + } - Expr { temp_lifetime, ty: expr_ty, span: expr.span, kind } -} + fn convert_path_expr( + &mut self, + expr: &'tcx hir::Expr<'tcx>, + res: Res, + ) -> ExprKind<'thir, 'tcx> { + let substs = self.typeck_results().node_substs(expr.hir_id); + match res { + // A regular function, constructor function or a constant. + Res::Def(DefKind::Fn, _) + | Res::Def(DefKind::AssocFn, _) + | Res::Def(DefKind::Ctor(_, CtorKind::Fn), _) + | Res::SelfCtor(..) => { + let user_ty = self.user_substs_applied_to_res(expr.hir_id, res); + debug!("convert_path_expr: user_ty={:?}", user_ty); + ExprKind::Literal { + literal: ty::Const::zero_sized( + self.tcx, + self.typeck_results().node_type(expr.hir_id), + ), + user_ty, + const_id: None, + } + } + + Res::Def(DefKind::ConstParam, def_id) => { + let hir_id = self.tcx.hir().local_def_id_to_hir_id(def_id.expect_local()); + let item_id = self.tcx.hir().get_parent_node(hir_id); + let item_def_id = self.tcx.hir().local_def_id(item_id); + let generics = self.tcx.generics_of(item_def_id); + let index = generics.param_def_id_to_index[&def_id]; + let name = self.tcx.hir().name(hir_id); + let val = ty::ConstKind::Param(ty::ParamConst::new(index, name)); + ExprKind::Literal { + literal: self.tcx.mk_const(ty::Const { + val, + ty: self.typeck_results().node_type(expr.hir_id), + }), + user_ty: None, + const_id: Some(def_id), + } + } + + Res::Def(DefKind::Const, def_id) | Res::Def(DefKind::AssocConst, def_id) => { + let user_ty = self.user_substs_applied_to_res(expr.hir_id, res); + debug!("convert_path_expr: (const) user_ty={:?}", user_ty); + ExprKind::Literal { + literal: self.tcx.mk_const(ty::Const { + val: ty::ConstKind::Unevaluated(ty::Unevaluated { + def: ty::WithOptConstParam::unknown(def_id), + substs, + promoted: None, + }), + ty: self.typeck_results().node_type(expr.hir_id), + }), + user_ty, + const_id: Some(def_id), + } + } + + Res::Def(DefKind::Ctor(_, CtorKind::Const), def_id) => { + let user_provided_types = self.typeck_results.user_provided_types(); + let user_provided_type = user_provided_types.get(expr.hir_id).copied(); + debug!("convert_path_expr: user_provided_type={:?}", user_provided_type); + let ty = self.typeck_results().node_type(expr.hir_id); + match ty.kind() { + // A unit struct/variant which is used as a value. + // We return a completely different ExprKind here to account for this special case. + ty::Adt(adt_def, substs) => ExprKind::Adt { + adt_def, + variant_index: adt_def.variant_index_with_ctor_id(def_id), + substs, + user_ty: user_provided_type, + fields: self.arena.alloc_from_iter(iter::empty()), + base: None, + }, + _ => bug!("unexpected ty: {:?}", ty), + } + } + + // We encode uses of statics as a `*&STATIC` where the `&STATIC` part is + // a constant reference (or constant raw pointer for `static mut`) in MIR + Res::Def(DefKind::Static, id) => { + let ty = self.tcx.static_ptr_ty(id); + let temp_lifetime = self.region_scope_tree.temporary_scope(expr.hir_id.local_id); + let kind = if self.tcx.is_thread_local_static(id) { + ExprKind::ThreadLocalRef(id) + } else { + let ptr = self.tcx.create_static_alloc(id); + ExprKind::StaticRef { + literal: ty::Const::from_scalar(self.tcx, Scalar::Ptr(ptr.into()), ty), + def_id: id, + } + }; + ExprKind::Deref { + arg: self.arena.alloc(Expr { ty, temp_lifetime, span: expr.span, kind }), + } + } + + Res::Local(var_hir_id) => self.convert_var(var_hir_id), -fn user_substs_applied_to_res<'tcx>( - cx: &mut Cx<'_, 'tcx>, - hir_id: hir::HirId, - res: Res, -) -> Option> { - debug!("user_substs_applied_to_res: res={:?}", res); - let user_provided_type = match res { - // A reference to something callable -- e.g., a fn, method, or - // a tuple-struct or tuple-variant. This has the type of a - // `Fn` but with the user-given substitutions. - Res::Def(DefKind::Fn, _) - | Res::Def(DefKind::AssocFn, _) - | Res::Def(DefKind::Ctor(_, CtorKind::Fn), _) - | Res::Def(DefKind::Const, _) - | Res::Def(DefKind::AssocConst, _) => { - cx.typeck_results().user_provided_types().get(hir_id).copied() + _ => span_bug!(expr.span, "res `{:?}` not yet implemented", res), } + } - // A unit struct/variant which is used as a value (e.g., - // `None`). This has the type of the enum/struct that defines - // this variant -- but with the substitutions given by the - // user. - Res::Def(DefKind::Ctor(_, CtorKind::Const), _) => { - cx.user_substs_applied_to_ty_of_hir_id(hir_id) + fn convert_var(&mut self, var_hir_id: hir::HirId) -> ExprKind<'thir, 'tcx> { + // We want upvars here not captures. + // Captures will be handled in MIR. + let is_upvar = self + .tcx + .upvars_mentioned(self.body_owner) + .map_or(false, |upvars| upvars.contains_key(&var_hir_id)); + + debug!( + "convert_var({:?}): is_upvar={}, body_owner={:?}", + var_hir_id, is_upvar, self.body_owner + ); + + if is_upvar { + ExprKind::UpvarRef { closure_def_id: self.body_owner, var_hir_id } + } else { + ExprKind::VarRef { id: var_hir_id } } + } - // `Self` is used in expression as a tuple struct constructor or an unit struct constructor - Res::SelfCtor(_) => cx.user_substs_applied_to_ty_of_hir_id(hir_id), + fn overloaded_operator( + &mut self, + expr: &'tcx hir::Expr<'tcx>, + args: &'thir [Expr<'thir, 'tcx>], + ) -> ExprKind<'thir, 'tcx> { + let fun = self.arena.alloc(self.method_callee(expr, expr.span, None)); + ExprKind::Call { ty: fun.ty, fun, args, from_hir_call: false, fn_span: expr.span } + } - _ => bug!("user_substs_applied_to_res: unexpected res {:?} at {:?}", res, hir_id), - }; - debug!("user_substs_applied_to_res: user_provided_type={:?}", user_provided_type); - user_provided_type -} + fn overloaded_place( + &mut self, + expr: &'tcx hir::Expr<'tcx>, + place_ty: Ty<'tcx>, + overloaded_callee: Option<(DefId, SubstsRef<'tcx>)>, + args: &'thir [Expr<'thir, 'tcx>], + span: Span, + ) -> ExprKind<'thir, 'tcx> { + // For an overloaded *x or x[y] expression of type T, the method + // call returns an &T and we must add the deref so that the types + // line up (this is because `*x` and `x[y]` represent places): + + // Reconstruct the output assuming it's a reference with the + // same region and mutability as the receiver. This holds for + // `Deref(Mut)::Deref(_mut)` and `Index(Mut)::index(_mut)`. + let (region, mutbl) = match *args[0].ty.kind() { + ty::Ref(region, _, mutbl) => (region, mutbl), + _ => span_bug!(span, "overloaded_place: receiver is not a reference"), + }; + let ref_ty = self.tcx.mk_ref(region, ty::TypeAndMut { ty: place_ty, mutbl }); + + // construct the complete expression `foo()` for the overloaded call, + // which will yield the &T type + let temp_lifetime = self.region_scope_tree.temporary_scope(expr.hir_id.local_id); + let fun = self.arena.alloc(self.method_callee(expr, span, overloaded_callee)); + let ref_expr = self.arena.alloc(Expr { + temp_lifetime, + ty: ref_ty, + span, + kind: ExprKind::Call { ty: fun.ty, fun, args, from_hir_call: false, fn_span: span }, + }); -fn method_callee<'a, 'tcx>( - cx: &mut Cx<'a, 'tcx>, - expr: &hir::Expr<'_>, - span: Span, - overloaded_callee: Option<(DefId, SubstsRef<'tcx>)>, -) -> Expr<'tcx> { - let temp_lifetime = cx.region_scope_tree.temporary_scope(expr.hir_id.local_id); - let (def_id, substs, user_ty) = match overloaded_callee { - Some((def_id, substs)) => (def_id, substs, None), - None => { - let (kind, def_id) = cx - .typeck_results() - .type_dependent_def(expr.hir_id) - .unwrap_or_else(|| span_bug!(expr.span, "no type-dependent def for method callee")); - let user_ty = user_substs_applied_to_res(cx, expr.hir_id, Res::Def(kind, def_id)); - debug!("method_callee: user_ty={:?}", user_ty); - (def_id, cx.typeck_results().node_substs(expr.hir_id), user_ty) + // construct and return a deref wrapper `*foo()` + ExprKind::Deref { arg: ref_expr } + } + + fn convert_captured_hir_place( + &mut self, + closure_expr: &'tcx hir::Expr<'tcx>, + place: HirPlace<'tcx>, + ) -> Expr<'thir, 'tcx> { + let temp_lifetime = self.region_scope_tree.temporary_scope(closure_expr.hir_id.local_id); + let var_ty = place.base_ty; + + // The result of capture analysis in `rustc_typeck/check/upvar.rs`represents a captured path + // as it's seen for use within the closure and not at the time of closure creation. + // + // That is we see expect to see it start from a captured upvar and not something that is local + // to the closure's parent. + let var_hir_id = match place.base { + HirPlaceBase::Upvar(upvar_id) => upvar_id.var_path.hir_id, + base => bug!("Expected an upvar, found {:?}", base), + }; + + let mut captured_place_expr = Expr { + temp_lifetime, + ty: var_ty, + span: closure_expr.span, + kind: self.convert_var(var_hir_id), + }; + + for proj in place.projections.iter() { + let kind = match proj.kind { + HirProjectionKind::Deref => { + ExprKind::Deref { arg: self.arena.alloc(captured_place_expr) } + } + HirProjectionKind::Field(field, ..) => { + // Variant index will always be 0, because for multi-variant + // enums, we capture the enum entirely. + ExprKind::Field { + lhs: self.arena.alloc(captured_place_expr), + name: Field::new(field as usize), + } + } + HirProjectionKind::Index | HirProjectionKind::Subslice => { + // We don't capture these projections, so we can ignore them here + continue; + } + }; + + captured_place_expr = + Expr { temp_lifetime, ty: proj.ty, span: closure_expr.span, kind }; + } + + captured_place_expr + } + + fn capture_upvar( + &mut self, + closure_expr: &'tcx hir::Expr<'tcx>, + captured_place: &'tcx ty::CapturedPlace<'tcx>, + upvar_ty: Ty<'tcx>, + ) -> Expr<'thir, 'tcx> { + let upvar_capture = captured_place.info.capture_kind; + let captured_place_expr = + self.convert_captured_hir_place(closure_expr, captured_place.place.clone()); + 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::ByRef(upvar_borrow) => { + let borrow_kind = match upvar_borrow.kind { + ty::BorrowKind::ImmBorrow => BorrowKind::Shared, + ty::BorrowKind::UniqueImmBorrow => BorrowKind::Unique, + ty::BorrowKind::MutBorrow => BorrowKind::Mut { allow_two_phase_borrow: false }, + }; + Expr { + temp_lifetime, + ty: upvar_ty, + span: closure_expr.span, + kind: ExprKind::Borrow { + borrow_kind, + arg: self.arena.alloc(captured_place_expr), + }, + } + } } - }; - let ty = cx.tcx().mk_fn_def(def_id, substs); - Expr { - temp_lifetime, - ty, - span, - kind: ExprKind::Literal { - literal: ty::Const::zero_sized(cx.tcx(), ty), - user_ty, - const_id: None, - }, + } + + /// Converts a list of named fields (i.e., for struct-like struct/enum ADTs) into FieldExpr. + fn field_refs( + &mut self, + fields: &'tcx [hir::ExprField<'tcx>], + ) -> &'thir [FieldExpr<'thir, 'tcx>] { + self.arena.alloc_from_iter(fields.iter().map(|field| FieldExpr { + name: Field::new(self.tcx.field_index(field.hir_id, self.typeck_results)), + expr: self.mirror_expr(field.expr), + })) } } @@ -776,135 +1154,6 @@ impl ToBorrowKind for hir::Mutability { } } -fn convert_arm<'tcx>(cx: &mut Cx<'_, 'tcx>, arm: &'tcx hir::Arm<'tcx>) -> Arm<'tcx> { - Arm { - pattern: cx.pattern_from_hir(&arm.pat), - guard: arm.guard.as_ref().map(|g| match g { - hir::Guard::If(ref e) => Guard::If(e.to_ref()), - hir::Guard::IfLet(ref pat, ref e) => Guard::IfLet(cx.pattern_from_hir(pat), e.to_ref()), - }), - body: arm.body.to_ref(), - lint_level: LintLevel::Explicit(arm.hir_id), - scope: region::Scope { id: arm.hir_id.local_id, data: region::ScopeData::Node }, - span: arm.span, - } -} - -fn convert_path_expr<'a, 'tcx>( - cx: &mut Cx<'a, 'tcx>, - expr: &'tcx hir::Expr<'tcx>, - res: Res, -) -> ExprKind<'tcx> { - let substs = cx.typeck_results().node_substs(expr.hir_id); - match res { - // A regular function, constructor function or a constant. - Res::Def(DefKind::Fn, _) - | Res::Def(DefKind::AssocFn, _) - | Res::Def(DefKind::Ctor(_, CtorKind::Fn), _) - | Res::SelfCtor(..) => { - let user_ty = user_substs_applied_to_res(cx, expr.hir_id, res); - debug!("convert_path_expr: user_ty={:?}", user_ty); - ExprKind::Literal { - literal: ty::Const::zero_sized(cx.tcx, cx.typeck_results().node_type(expr.hir_id)), - user_ty, - const_id: None, - } - } - - Res::Def(DefKind::ConstParam, def_id) => { - let hir_id = cx.tcx.hir().local_def_id_to_hir_id(def_id.expect_local()); - let item_id = cx.tcx.hir().get_parent_node(hir_id); - let item_def_id = cx.tcx.hir().local_def_id(item_id); - let generics = cx.tcx.generics_of(item_def_id); - let index = generics.param_def_id_to_index[&def_id]; - let name = cx.tcx.hir().name(hir_id); - let val = ty::ConstKind::Param(ty::ParamConst::new(index, name)); - ExprKind::Literal { - literal: cx - .tcx - .mk_const(ty::Const { val, ty: cx.typeck_results().node_type(expr.hir_id) }), - user_ty: None, - const_id: Some(def_id), - } - } - - Res::Def(DefKind::Const, def_id) | Res::Def(DefKind::AssocConst, def_id) => { - let user_ty = user_substs_applied_to_res(cx, expr.hir_id, res); - debug!("convert_path_expr: (const) user_ty={:?}", user_ty); - ExprKind::Literal { - literal: cx.tcx.mk_const(ty::Const { - val: ty::ConstKind::Unevaluated( - ty::WithOptConstParam::unknown(def_id), - substs, - None, - ), - ty: cx.typeck_results().node_type(expr.hir_id), - }), - user_ty, - const_id: Some(def_id), - } - } - - Res::Def(DefKind::Ctor(_, CtorKind::Const), def_id) => { - let user_provided_types = cx.typeck_results.user_provided_types(); - let user_provided_type = user_provided_types.get(expr.hir_id).copied(); - debug!("convert_path_expr: user_provided_type={:?}", user_provided_type); - let ty = cx.typeck_results().node_type(expr.hir_id); - match ty.kind() { - // A unit struct/variant which is used as a value. - // We return a completely different ExprKind here to account for this special case. - ty::Adt(adt_def, substs) => ExprKind::Adt { - adt_def, - variant_index: adt_def.variant_index_with_ctor_id(def_id), - substs, - user_ty: user_provided_type, - fields: vec![], - base: None, - }, - _ => bug!("unexpected ty: {:?}", ty), - } - } - - // We encode uses of statics as a `*&STATIC` where the `&STATIC` part is - // a constant reference (or constant raw pointer for `static mut`) in MIR - Res::Def(DefKind::Static, id) => { - let ty = cx.tcx.static_ptr_ty(id); - let temp_lifetime = cx.region_scope_tree.temporary_scope(expr.hir_id.local_id); - let kind = if cx.tcx.is_thread_local_static(id) { - ExprKind::ThreadLocalRef(id) - } else { - let ptr = cx.tcx.create_static_alloc(id); - ExprKind::StaticRef { - literal: ty::Const::from_scalar(cx.tcx, Scalar::Ptr(ptr.into()), ty), - def_id: id, - } - }; - ExprKind::Deref { arg: Expr { ty, temp_lifetime, span: expr.span, kind }.to_ref() } - } - - Res::Local(var_hir_id) => convert_var(cx, var_hir_id), - - _ => span_bug!(expr.span, "res `{:?}` not yet implemented", res), - } -} - -fn convert_var<'tcx>(cx: &mut Cx<'_, 'tcx>, var_hir_id: hir::HirId) -> ExprKind<'tcx> { - // We want upvars here not captures. - // Captures will be handled in MIR. - let is_upvar = cx - .tcx - .upvars_mentioned(cx.body_owner) - .map_or(false, |upvars| upvars.contains_key(&var_hir_id)); - - debug!("convert_var({:?}): is_upvar={}, body_owner={:?}", var_hir_id, is_upvar, cx.body_owner); - - if is_upvar { - ExprKind::UpvarRef { closure_def_id: cx.body_owner, var_hir_id } - } else { - ExprKind::VarRef { id: var_hir_id } - } -} - fn bin_op(op: hir::BinOpKind) -> BinOp { match op { hir::BinOpKind::Add => BinOp::Add, @@ -926,139 +1175,3 @@ fn bin_op(op: hir::BinOpKind) -> BinOp { _ => bug!("no equivalent for ast binop {:?}", op), } } - -fn overloaded_operator<'a, 'tcx>( - cx: &mut Cx<'a, 'tcx>, - expr: &'tcx hir::Expr<'tcx>, - args: Vec>, -) -> ExprKind<'tcx> { - let fun = method_callee(cx, expr, expr.span, None); - ExprKind::Call { ty: fun.ty, fun: fun.to_ref(), args, from_hir_call: false, fn_span: expr.span } -} - -fn overloaded_place<'a, 'tcx>( - cx: &mut Cx<'a, 'tcx>, - expr: &'tcx hir::Expr<'tcx>, - place_ty: Ty<'tcx>, - overloaded_callee: Option<(DefId, SubstsRef<'tcx>)>, - args: Vec>, - span: Span, -) -> ExprKind<'tcx> { - // For an overloaded *x or x[y] expression of type T, the method - // call returns an &T and we must add the deref so that the types - // line up (this is because `*x` and `x[y]` represent places): - - let recv_ty = match args[0] { - ExprRef::Thir(e) => cx.typeck_results().expr_ty_adjusted(e), - ExprRef::Mirror(ref e) => e.ty, - }; - - // Reconstruct the output assuming it's a reference with the - // same region and mutability as the receiver. This holds for - // `Deref(Mut)::Deref(_mut)` and `Index(Mut)::index(_mut)`. - let (region, mutbl) = match *recv_ty.kind() { - ty::Ref(region, _, mutbl) => (region, mutbl), - _ => span_bug!(span, "overloaded_place: receiver is not a reference"), - }; - let ref_ty = cx.tcx.mk_ref(region, ty::TypeAndMut { ty: place_ty, mutbl }); - - // construct the complete expression `foo()` for the overloaded call, - // which will yield the &T type - let temp_lifetime = cx.region_scope_tree.temporary_scope(expr.hir_id.local_id); - let fun = method_callee(cx, expr, span, overloaded_callee); - let ref_expr = Expr { - temp_lifetime, - ty: ref_ty, - span, - kind: ExprKind::Call { - ty: fun.ty, - fun: fun.to_ref(), - args, - from_hir_call: false, - fn_span: span, - }, - }; - - // construct and return a deref wrapper `*foo()` - ExprKind::Deref { arg: ref_expr.to_ref() } -} - -fn capture_upvar<'a, 'tcx>( - cx: &mut Cx<'_, 'tcx>, - closure_expr: &'tcx hir::Expr<'tcx>, - captured_place: &'a ty::CapturedPlace<'tcx>, - upvar_ty: Ty<'tcx>, -) -> ExprRef<'tcx> { - let upvar_capture = captured_place.info.capture_kind; - let temp_lifetime = cx.region_scope_tree.temporary_scope(closure_expr.hir_id.local_id); - let var_ty = captured_place.place.base_ty; - - // The result of capture analysis in `rustc_typeck/check/upvar.rs`represents a captured path - // as it's seen for use within the closure and not at the time of closure creation. - // - // That is we see expect to see it start from a captured upvar and not something that is local - // to the closure's parent. - let var_hir_id = match captured_place.place.base { - HirPlaceBase::Upvar(upvar_id) => upvar_id.var_path.hir_id, - base => bug!("Expected an upvar, found {:?}", base), - }; - - let mut captured_place_expr = Expr { - temp_lifetime, - ty: var_ty, - span: closure_expr.span, - kind: convert_var(cx, var_hir_id), - }; - - for proj in captured_place.place.projections.iter() { - let kind = match proj.kind { - HirProjectionKind::Deref => ExprKind::Deref { arg: captured_place_expr.to_ref() }, - HirProjectionKind::Field(field, ..) => { - // Variant index will always be 0, because for multi-variant - // enums, we capture the enum entirely. - ExprKind::Field { - lhs: captured_place_expr.to_ref(), - name: Field::new(field as usize), - } - } - HirProjectionKind::Index | HirProjectionKind::Subslice => { - // We don't capture these projections, so we can ignore them here - continue; - } - }; - - captured_place_expr = Expr { temp_lifetime, ty: proj.ty, span: closure_expr.span, kind }; - } - - match upvar_capture { - ty::UpvarCapture::ByValue(_) => captured_place_expr.to_ref(), - ty::UpvarCapture::ByRef(upvar_borrow) => { - let borrow_kind = match upvar_borrow.kind { - ty::BorrowKind::ImmBorrow => BorrowKind::Shared, - ty::BorrowKind::UniqueImmBorrow => BorrowKind::Unique, - ty::BorrowKind::MutBorrow => BorrowKind::Mut { allow_two_phase_borrow: false }, - }; - Expr { - temp_lifetime, - ty: upvar_ty, - span: closure_expr.span, - kind: ExprKind::Borrow { borrow_kind, arg: captured_place_expr.to_ref() }, - } - .to_ref() - } - } -} - -/// Converts a list of named fields (i.e., for struct-like struct/enum ADTs) into FieldExprRef. -fn field_refs<'a, 'tcx>( - cx: &mut Cx<'a, 'tcx>, - fields: &'tcx [hir::Field<'tcx>], -) -> Vec> { - fields - .iter() - .map(|field| FieldExprRef { - name: Field::new(cx.tcx.field_index(field.hir_id, cx.typeck_results)), - expr: field.expr.to_ref(), - }) - .collect() -} diff --git a/compiler/rustc_mir_build/src/thir/cx/mod.rs b/compiler/rustc_mir_build/src/thir/cx/mod.rs index 465808cea9dd5..c0433604f8c68 100644 --- a/compiler/rustc_mir_build/src/thir/cx/mod.rs +++ b/compiler/rustc_mir_build/src/thir/cx/mod.rs @@ -2,6 +2,7 @@ //! structures into the THIR. The `builder` is generally ignorant of the tcx, //! etc., and instead goes through the `Cx` for most of its work. +use crate::thir::arena::Arena; use crate::thir::util::UserAnnotatedTyHelpers; use crate::thir::*; @@ -9,118 +10,48 @@ use rustc_ast as ast; use rustc_hir as hir; use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_hir::Node; -use rustc_index::vec::Idx; -use rustc_infer::infer::InferCtxt; use rustc_middle::middle::region; use rustc_middle::mir::interpret::{LitToConstError, LitToConstInput}; -use rustc_middle::ty::subst::Subst; -use rustc_middle::ty::subst::{GenericArg, InternalSubsts}; use rustc_middle::ty::{self, Ty, TyCtxt}; -use rustc_span::symbol::{sym, Symbol}; -use rustc_target::abi::VariantIdx; -use rustc_trait_selection::infer::InferCtxtExt; -#[derive(Clone)] -crate struct Cx<'a, 'tcx> { +pub fn build_thir<'thir, 'tcx>( tcx: TyCtxt<'tcx>, - infcx: &'a InferCtxt<'a, 'tcx>, + owner_def: ty::WithOptConstParam, + arena: &'thir Arena<'thir, 'tcx>, + expr: &'tcx hir::Expr<'tcx>, +) -> &'thir Expr<'thir, 'tcx> { + Cx::new(tcx, owner_def, &arena).mirror_expr(expr) +} - crate root_lint_level: hir::HirId, - crate param_env: ty::ParamEnv<'tcx>, +struct Cx<'thir, 'tcx> { + tcx: TyCtxt<'tcx>, + arena: &'thir Arena<'thir, 'tcx>, - /// Identity `InternalSubsts` for use with const-evaluation. - crate identity_substs: &'tcx InternalSubsts<'tcx>, + crate param_env: ty::ParamEnv<'tcx>, crate region_scope_tree: &'tcx region::ScopeTree, - crate typeck_results: &'a ty::TypeckResults<'tcx>, - - /// This is `Constness::Const` if we are compiling a `static`, - /// `const`, or the body of a `const fn`. - constness: hir::Constness, + crate typeck_results: &'tcx ty::TypeckResults<'tcx>, /// The `DefId` of the owner of this body. body_owner: DefId, - - /// What kind of body is being compiled. - crate body_owner_kind: hir::BodyOwnerKind, - - /// Whether this constant/function needs overflow checks. - check_overflow: bool, } -impl<'a, 'tcx> Cx<'a, 'tcx> { - crate fn new( - infcx: &'a InferCtxt<'a, 'tcx>, +impl<'thir, 'tcx> Cx<'thir, 'tcx> { + fn new( + tcx: TyCtxt<'tcx>, def: ty::WithOptConstParam, - src_id: hir::HirId, - ) -> Cx<'a, 'tcx> { - let tcx = infcx.tcx; + arena: &'thir Arena<'thir, 'tcx>, + ) -> Cx<'thir, 'tcx> { let typeck_results = tcx.typeck_opt_const_arg(def); - let body_owner_kind = tcx.hir().body_owner_kind(src_id); - - let constness = match body_owner_kind { - hir::BodyOwnerKind::Const | hir::BodyOwnerKind::Static(_) => hir::Constness::Const, - hir::BodyOwnerKind::Closure | hir::BodyOwnerKind::Fn => hir::Constness::NotConst, - }; - - let attrs = tcx.hir().attrs(src_id); - - // Some functions always have overflow checks enabled, - // however, they may not get codegen'd, depending on - // the settings for the crate they are codegened in. - let mut check_overflow = tcx.sess.contains_name(attrs, sym::rustc_inherit_overflow_checks); - - // Respect -C overflow-checks. - check_overflow |= tcx.sess.overflow_checks(); - - // Constants always need overflow checks. - check_overflow |= constness == hir::Constness::Const; - Cx { tcx, - infcx, - root_lint_level: src_id, + arena, param_env: tcx.param_env(def.did), - identity_substs: InternalSubsts::identity_for_item(tcx, def.did.to_def_id()), region_scope_tree: tcx.region_scope_tree(def.did), typeck_results, - constness, body_owner: def.did.to_def_id(), - body_owner_kind, - check_overflow, } } -} - -impl<'a, 'tcx> Cx<'a, 'tcx> { - /// Normalizes `ast` into the appropriate "mirror" type. - crate fn mirror>(&mut self, ast: M) -> M::Output { - ast.make_mirror(self) - } - - crate fn usize_ty(&mut self) -> Ty<'tcx> { - self.tcx.types.usize - } - - crate fn usize_literal(&mut self, value: u64) -> &'tcx ty::Const<'tcx> { - ty::Const::from_usize(self.tcx, value) - } - - crate fn bool_ty(&mut self) -> Ty<'tcx> { - self.tcx.types.bool - } - - crate fn unit_ty(&mut self) -> Ty<'tcx> { - self.tcx.mk_unit() - } - - crate fn true_literal(&mut self) -> &'tcx ty::Const<'tcx> { - ty::Const::from_bool(self.tcx, true) - } - - crate fn false_literal(&mut self) -> &'tcx ty::Const<'tcx> { - ty::Const::from_bool(self.tcx, false) - } crate fn const_eval_literal( &mut self, @@ -137,11 +68,11 @@ impl<'a, 'tcx> Cx<'a, 'tcx> { // FIXME(#31407) this is only necessary because float parsing is buggy self.tcx.sess.span_err(sp, "could not evaluate float literal (see issue #31407)"); // create a dummy value and continue compiling - Const::from_bits(self.tcx, 0, self.param_env.and(ty)) + self.tcx.const_error(ty) } Err(LitToConstError::Reported) => { // create a dummy value and continue compiling - Const::from_bits(self.tcx, 0, self.param_env.and(ty)) + self.tcx.const_error(ty) } Err(LitToConstError::TypeError) => bug!("const_eval_literal: had type error"), } @@ -154,69 +85,17 @@ impl<'a, 'tcx> Cx<'a, 'tcx> { }; Pat::from_hir(self.tcx, self.param_env, self.typeck_results(), p) } - - crate fn trait_method( - &mut self, - trait_def_id: DefId, - method_name: Symbol, - self_ty: Ty<'tcx>, - params: &[GenericArg<'tcx>], - ) -> &'tcx ty::Const<'tcx> { - let substs = self.tcx.mk_substs_trait(self_ty, params); - - // The unhygienic comparison here is acceptable because this is only - // used on known traits. - let item = self - .tcx - .associated_items(trait_def_id) - .filter_by_name_unhygienic(method_name) - .find(|item| item.kind == ty::AssocKind::Fn) - .expect("trait method not found"); - - let method_ty = self.tcx.type_of(item.def_id); - let method_ty = method_ty.subst(self.tcx, substs); - ty::Const::zero_sized(self.tcx, method_ty) - } - - crate fn all_fields(&mut self, adt_def: &ty::AdtDef, variant_index: VariantIdx) -> Vec { - (0..adt_def.variants[variant_index].fields.len()).map(Field::new).collect() - } - - crate fn needs_drop(&mut self, ty: Ty<'tcx>) -> bool { - ty.needs_drop(self.tcx, self.param_env) - } - - crate fn infcx(&self) -> &'a InferCtxt<'a, 'tcx> { - self.infcx - } - - crate fn tcx(&self) -> TyCtxt<'tcx> { - self.tcx - } - - crate fn typeck_results(&self) -> &'a ty::TypeckResults<'tcx> { - self.typeck_results - } - - crate fn check_overflow(&self) -> bool { - self.check_overflow - } - - crate fn type_is_copy_modulo_regions(&self, ty: Ty<'tcx>, span: Span) -> bool { - self.infcx.type_is_copy_modulo_regions(self.param_env, ty, span) - } } impl<'tcx> UserAnnotatedTyHelpers<'tcx> for Cx<'_, 'tcx> { fn tcx(&self) -> TyCtxt<'tcx> { - self.tcx() + self.tcx } fn typeck_results(&self) -> &ty::TypeckResults<'tcx> { - self.typeck_results() + self.typeck_results } } mod block; mod expr; -mod to_ref; diff --git a/compiler/rustc_mir_build/src/thir/cx/to_ref.rs b/compiler/rustc_mir_build/src/thir/cx/to_ref.rs deleted file mode 100644 index 53a988ebb79e2..0000000000000 --- a/compiler/rustc_mir_build/src/thir/cx/to_ref.rs +++ /dev/null @@ -1,65 +0,0 @@ -use crate::thir::*; - -use rustc_hir as hir; - -crate trait ToRef { - type Output; - fn to_ref(self) -> Self::Output; -} - -impl<'tcx> ToRef for &'tcx hir::Expr<'tcx> { - type Output = ExprRef<'tcx>; - - fn to_ref(self) -> ExprRef<'tcx> { - ExprRef::Thir(self) - } -} - -impl<'tcx> ToRef for &'tcx &'tcx hir::Expr<'tcx> { - type Output = ExprRef<'tcx>; - - fn to_ref(self) -> ExprRef<'tcx> { - ExprRef::Thir(&**self) - } -} - -impl<'tcx> ToRef for Expr<'tcx> { - type Output = ExprRef<'tcx>; - - fn to_ref(self) -> ExprRef<'tcx> { - ExprRef::Mirror(Box::new(self)) - } -} - -impl<'tcx, T, U> ToRef for &'tcx Option -where - &'tcx T: ToRef, -{ - type Output = Option; - - fn to_ref(self) -> Option { - self.as_ref().map(|expr| expr.to_ref()) - } -} - -impl<'tcx, T, U> ToRef for &'tcx Vec -where - &'tcx T: ToRef, -{ - type Output = Vec; - - fn to_ref(self) -> Vec { - self.iter().map(|expr| expr.to_ref()).collect() - } -} - -impl<'tcx, T, U> ToRef for &'tcx [T] -where - &'tcx T: ToRef, -{ - type Output = Vec; - - fn to_ref(self) -> Vec { - self.iter().map(|expr| expr.to_ref()).collect() - } -} diff --git a/compiler/rustc_mir_build/src/thir/mod.rs b/compiler/rustc_mir_build/src/thir/mod.rs index ed3d3927825af..f4596d523d07a 100644 --- a/compiler/rustc_mir_build/src/thir/mod.rs +++ b/compiler/rustc_mir_build/src/thir/mod.rs @@ -4,13 +4,12 @@ //! unit-tested and separated from the Rust source and compiler data //! structures. -use self::cx::Cx; use rustc_ast::{InlineAsmOptions, InlineAsmTemplatePiece}; use rustc_hir as hir; use rustc_hir::def_id::DefId; use rustc_middle::infer::canonical::Canonical; use rustc_middle::middle::region; -use rustc_middle::mir::{BinOp, BorrowKind, Field, UnOp}; +use rustc_middle::mir::{BinOp, BorrowKind, FakeReadCause, Field, UnOp}; use rustc_middle::ty::adjustment::PointerCast; use rustc_middle::ty::subst::SubstsRef; use rustc_middle::ty::{AdtDef, Const, Ty, UpvarSubsts, UserType}; @@ -19,58 +18,57 @@ use rustc_target::abi::VariantIdx; use rustc_target::asm::InlineAsmRegOrRegClass; crate mod constant; + crate mod cx; +pub use cx::build_thir; crate mod pattern; -crate use self::pattern::PatTyProj; -crate use self::pattern::{BindingMode, FieldPat, Pat, PatKind, PatRange}; +pub use self::pattern::{Ascription, BindingMode, FieldPat, Pat, PatKind, PatRange, PatTyProj}; + +mod arena; +pub use arena::Arena; mod util; #[derive(Copy, Clone, Debug)] -crate enum LintLevel { +pub enum LintLevel { Inherited, Explicit(hir::HirId), } -#[derive(Clone, Debug)] -crate struct Block<'tcx> { - crate targeted_by_break: bool, - crate region_scope: region::Scope, - crate opt_destruction_scope: Option, - crate span: Span, - crate stmts: Vec>, - crate expr: Option>, - crate safety_mode: BlockSafety, +#[derive(Debug)] +pub struct Block<'thir, 'tcx> { + pub targeted_by_break: bool, + pub region_scope: region::Scope, + pub opt_destruction_scope: Option, + pub span: Span, + pub stmts: &'thir [Stmt<'thir, 'tcx>], + pub expr: Option<&'thir Expr<'thir, 'tcx>>, + pub safety_mode: BlockSafety, } #[derive(Copy, Clone, Debug)] -crate enum BlockSafety { +pub enum BlockSafety { Safe, ExplicitUnsafe(hir::HirId), PushUnsafe, PopUnsafe, } -#[derive(Clone, Debug)] -crate enum StmtRef<'tcx> { - Mirror(Box>), -} - -#[derive(Clone, Debug)] -crate struct Stmt<'tcx> { - crate kind: StmtKind<'tcx>, - crate opt_destruction_scope: Option, +#[derive(Debug)] +pub struct Stmt<'thir, 'tcx> { + pub kind: StmtKind<'thir, 'tcx>, + pub opt_destruction_scope: Option, } -#[derive(Clone, Debug)] -crate enum StmtKind<'tcx> { +#[derive(Debug)] +pub enum StmtKind<'thir, 'tcx> { Expr { /// scope for this statement; may be used as lifetime of temporaries scope: region::Scope, /// expression being evaluated in this statement - expr: ExprRef<'tcx>, + expr: &'thir Expr<'thir, 'tcx>, }, Let { @@ -88,7 +86,7 @@ crate enum StmtKind<'tcx> { pattern: Pat<'tcx>, /// let pat: ty = ... - initializer: Option>, + initializer: Option<&'thir Expr<'thir, 'tcx>>, /// the lint level for this let-statement lint_level: LintLevel, @@ -96,13 +94,13 @@ crate enum StmtKind<'tcx> { } // `Expr` is used a lot. Make sure it doesn't unintentionally get bigger. -#[cfg(target_arch = "x86_64")] -rustc_data_structures::static_assert_size!(Expr<'_>, 168); +#[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))] +rustc_data_structures::static_assert_size!(Expr<'_, '_>, 144); /// The Thir trait implementor lowers their expressions (`&'tcx H::Expr`) /// into instances of this `Expr` enum. This lowering can be done /// basically as lazily or as eagerly as desired: every recursive -/// reference to an expression in this enum is an `ExprRef<'tcx>`, which +/// reference to an expression in this enum is an `&'thir Expr<'thir, 'tcx>`, which /// may in turn be another instance of this enum (boxed), or else an /// unlowered `&'tcx H::Expr`. Note that instances of `Expr` are very /// short-lived. They are created by `Thir::to_expr`, analyzed and @@ -113,105 +111,105 @@ rustc_data_structures::static_assert_size!(Expr<'_>, 168); /// MIR simplifications are already done in the impl of `Thir`. For /// example, method calls and overloaded operators are absent: they are /// expected to be converted into `Expr::Call` instances. -#[derive(Clone, Debug)] -crate struct Expr<'tcx> { +#[derive(Debug)] +pub struct Expr<'thir, 'tcx> { /// type of this expression - crate ty: Ty<'tcx>, + pub ty: Ty<'tcx>, /// lifetime of this expression if it should be spilled into a /// temporary; should be None only if in a constant context - crate temp_lifetime: Option, + pub temp_lifetime: Option, /// span of the expression in the source - crate span: Span, + pub span: Span, /// kind of expression - crate kind: ExprKind<'tcx>, + pub kind: ExprKind<'thir, 'tcx>, } -#[derive(Clone, Debug)] -crate enum ExprKind<'tcx> { +#[derive(Debug)] +pub enum ExprKind<'thir, 'tcx> { Scope { region_scope: region::Scope, lint_level: LintLevel, - value: ExprRef<'tcx>, + value: &'thir Expr<'thir, 'tcx>, }, Box { - value: ExprRef<'tcx>, + value: &'thir Expr<'thir, 'tcx>, }, If { - cond: ExprRef<'tcx>, - then: ExprRef<'tcx>, - else_opt: Option>, + cond: &'thir Expr<'thir, 'tcx>, + then: &'thir Expr<'thir, 'tcx>, + else_opt: Option<&'thir Expr<'thir, 'tcx>>, }, Call { ty: Ty<'tcx>, - fun: ExprRef<'tcx>, - args: Vec>, - // Whether this is from a call in HIR, rather than from an overloaded - // operator. True for overloaded function call. + fun: &'thir Expr<'thir, 'tcx>, + args: &'thir [Expr<'thir, 'tcx>], + /// Whether this is from a call in HIR, rather than from an overloaded + /// operator. `true` for overloaded function call. from_hir_call: bool, /// This `Span` is the span of the function, without the dot and receiver /// (e.g. `foo(a, b)` in `x.foo(a, b)` fn_span: Span, }, Deref { - arg: ExprRef<'tcx>, + arg: &'thir Expr<'thir, 'tcx>, }, // NOT overloaded! Binary { op: BinOp, - lhs: ExprRef<'tcx>, - rhs: ExprRef<'tcx>, + lhs: &'thir Expr<'thir, 'tcx>, + rhs: &'thir Expr<'thir, 'tcx>, }, // NOT overloaded! LogicalOp { op: LogicalOp, - lhs: ExprRef<'tcx>, - rhs: ExprRef<'tcx>, + lhs: &'thir Expr<'thir, 'tcx>, + rhs: &'thir Expr<'thir, 'tcx>, }, // NOT overloaded! // LogicalOp is distinct from BinaryOp because of lazy evaluation of the operands. Unary { op: UnOp, - arg: ExprRef<'tcx>, + arg: &'thir Expr<'thir, 'tcx>, }, // NOT overloaded! Cast { - source: ExprRef<'tcx>, + source: &'thir Expr<'thir, 'tcx>, }, Use { - source: ExprRef<'tcx>, + source: &'thir Expr<'thir, 'tcx>, }, // Use a lexpr to get a vexpr. NeverToAny { - source: ExprRef<'tcx>, + source: &'thir Expr<'thir, 'tcx>, }, Pointer { cast: PointerCast, - source: ExprRef<'tcx>, + source: &'thir Expr<'thir, 'tcx>, }, Loop { - body: ExprRef<'tcx>, + body: &'thir Expr<'thir, 'tcx>, }, Match { - scrutinee: ExprRef<'tcx>, - arms: Vec>, + scrutinee: &'thir Expr<'thir, 'tcx>, + arms: &'thir [Arm<'thir, 'tcx>], }, Block { - body: &'tcx hir::Block<'tcx>, + body: Block<'thir, 'tcx>, }, Assign { - lhs: ExprRef<'tcx>, - rhs: ExprRef<'tcx>, + lhs: &'thir Expr<'thir, 'tcx>, + rhs: &'thir Expr<'thir, 'tcx>, }, AssignOp { op: BinOp, - lhs: ExprRef<'tcx>, - rhs: ExprRef<'tcx>, + lhs: &'thir Expr<'thir, 'tcx>, + rhs: &'thir Expr<'thir, 'tcx>, }, Field { - lhs: ExprRef<'tcx>, + lhs: &'thir Expr<'thir, 'tcx>, name: Field, }, Index { - lhs: ExprRef<'tcx>, - index: ExprRef<'tcx>, + lhs: &'thir Expr<'thir, 'tcx>, + index: &'thir Expr<'thir, 'tcx>, }, VarRef { id: hir::HirId, @@ -226,35 +224,35 @@ crate enum ExprKind<'tcx> { }, Borrow { borrow_kind: BorrowKind, - arg: ExprRef<'tcx>, + arg: &'thir Expr<'thir, 'tcx>, }, /// A `&raw [const|mut] $place_expr` raw borrow resulting in type `*[const|mut] T`. AddressOf { mutability: hir::Mutability, - arg: ExprRef<'tcx>, + arg: &'thir Expr<'thir, 'tcx>, }, Break { label: region::Scope, - value: Option>, + value: Option<&'thir Expr<'thir, 'tcx>>, }, Continue { label: region::Scope, }, Return { - value: Option>, + value: Option<&'thir Expr<'thir, 'tcx>>, }, ConstBlock { value: &'tcx Const<'tcx>, }, Repeat { - value: ExprRef<'tcx>, + value: &'thir Expr<'thir, 'tcx>, count: &'tcx Const<'tcx>, }, Array { - fields: Vec>, + fields: &'thir [Expr<'thir, 'tcx>], }, Tuple { - fields: Vec>, + fields: &'thir [Expr<'thir, 'tcx>], }, Adt { adt_def: &'tcx AdtDef, @@ -265,24 +263,25 @@ crate enum ExprKind<'tcx> { /// Bar:: { ... }`. user_ty: Option>>, - fields: Vec>, - base: Option>, + fields: &'thir [FieldExpr<'thir, 'tcx>], + base: Option>, }, PlaceTypeAscription { - source: ExprRef<'tcx>, + source: &'thir Expr<'thir, 'tcx>, /// Type that the user gave to this expression user_ty: Option>>, }, ValueTypeAscription { - source: ExprRef<'tcx>, + source: &'thir Expr<'thir, 'tcx>, /// Type that the user gave to this expression user_ty: Option>>, }, Closure { closure_id: DefId, substs: UpvarSubsts<'tcx>, - upvars: Vec>, + upvars: &'thir [Expr<'thir, 'tcx>], movability: Option, + fake_reads: Vec<(&'thir Expr<'thir, 'tcx>, FakeReadCause, hir::HirId)>, }, Literal { literal: &'tcx Const<'tcx>, @@ -302,7 +301,7 @@ crate enum ExprKind<'tcx> { }, InlineAsm { template: &'tcx [InlineAsmTemplatePiece], - operands: Vec>, + operands: &'thir [InlineAsmOperand<'thir, 'tcx>], options: InlineAsmOptions, line_spans: &'tcx [Span], }, @@ -310,158 +309,78 @@ crate enum ExprKind<'tcx> { ThreadLocalRef(DefId), LlvmInlineAsm { asm: &'tcx hir::LlvmInlineAsmInner, - outputs: Vec>, - inputs: Vec>, + outputs: &'thir [Expr<'thir, 'tcx>], + inputs: &'thir [Expr<'thir, 'tcx>], }, Yield { - value: ExprRef<'tcx>, + value: &'thir Expr<'thir, 'tcx>, }, } -#[derive(Clone, Debug)] -crate enum ExprRef<'tcx> { - Thir(&'tcx hir::Expr<'tcx>), - Mirror(Box>), +#[derive(Debug)] +pub struct FieldExpr<'thir, 'tcx> { + pub name: Field, + pub expr: &'thir Expr<'thir, 'tcx>, } -#[derive(Clone, Debug)] -crate struct FieldExprRef<'tcx> { - crate name: Field, - crate expr: ExprRef<'tcx>, +#[derive(Debug)] +pub struct FruInfo<'thir, 'tcx> { + pub base: &'thir Expr<'thir, 'tcx>, + pub field_types: &'thir [Ty<'tcx>], } -#[derive(Clone, Debug)] -crate struct FruInfo<'tcx> { - crate base: ExprRef<'tcx>, - crate field_types: Vec>, +#[derive(Debug)] +pub struct Arm<'thir, 'tcx> { + pub pattern: Pat<'tcx>, + pub guard: Option>, + pub body: &'thir Expr<'thir, 'tcx>, + pub lint_level: LintLevel, + pub scope: region::Scope, + pub span: Span, } -#[derive(Clone, Debug)] -crate struct Arm<'tcx> { - crate pattern: Pat<'tcx>, - crate guard: Option>, - crate body: ExprRef<'tcx>, - crate lint_level: LintLevel, - crate scope: region::Scope, - crate span: Span, -} - -#[derive(Clone, Debug)] -crate enum Guard<'tcx> { - If(ExprRef<'tcx>), - IfLet(Pat<'tcx>, ExprRef<'tcx>), +#[derive(Debug)] +pub enum Guard<'thir, 'tcx> { + If(&'thir Expr<'thir, 'tcx>), + IfLet(Pat<'tcx>, &'thir Expr<'thir, 'tcx>), } #[derive(Copy, Clone, Debug)] -crate enum LogicalOp { +pub enum LogicalOp { And, Or, } -impl<'tcx> ExprRef<'tcx> { - crate fn span(&self) -> Span { - match self { - ExprRef::Thir(expr) => expr.span, - ExprRef::Mirror(expr) => expr.span, - } - } -} - -#[derive(Clone, Debug)] -crate enum InlineAsmOperand<'tcx> { +#[derive(Debug)] +pub enum InlineAsmOperand<'thir, 'tcx> { In { reg: InlineAsmRegOrRegClass, - expr: ExprRef<'tcx>, + expr: &'thir Expr<'thir, 'tcx>, }, Out { reg: InlineAsmRegOrRegClass, late: bool, - expr: Option>, + expr: Option<&'thir Expr<'thir, 'tcx>>, }, InOut { reg: InlineAsmRegOrRegClass, late: bool, - expr: ExprRef<'tcx>, + expr: &'thir Expr<'thir, 'tcx>, }, SplitInOut { reg: InlineAsmRegOrRegClass, late: bool, - in_expr: ExprRef<'tcx>, - out_expr: Option>, + in_expr: &'thir Expr<'thir, 'tcx>, + out_expr: Option<&'thir Expr<'thir, 'tcx>>, }, Const { - expr: ExprRef<'tcx>, + value: &'tcx Const<'tcx>, + span: Span, }, SymFn { - expr: ExprRef<'tcx>, + expr: &'thir Expr<'thir, 'tcx>, }, SymStatic { def_id: DefId, }, } - -/////////////////////////////////////////////////////////////////////////// -// The Mirror trait - -/// "Mirroring" is the process of converting from a HIR type into one -/// of the THIR types defined in this file. This is basically a "on -/// the fly" desugaring step that hides a lot of the messiness in the -/// tcx. For example, the mirror of a `&'tcx hir::Expr` is an -/// `Expr<'tcx>`. -/// -/// Mirroring is gradual: when you mirror an outer expression like `e1 -/// + e2`, the references to the inner expressions `e1` and `e2` are -/// `ExprRef<'tcx>` instances, and they may or may not be eagerly -/// mirrored. This allows a single AST node from the compiler to -/// expand into one or more Thir nodes, which lets the Thir nodes be -/// simpler. -crate trait Mirror<'tcx> { - type Output; - - fn make_mirror(self, cx: &mut Cx<'_, 'tcx>) -> Self::Output; -} - -impl<'tcx> Mirror<'tcx> for Expr<'tcx> { - type Output = Expr<'tcx>; - - fn make_mirror(self, _: &mut Cx<'_, 'tcx>) -> Expr<'tcx> { - self - } -} - -impl<'tcx> Mirror<'tcx> for ExprRef<'tcx> { - type Output = Expr<'tcx>; - - fn make_mirror(self, hir: &mut Cx<'_, 'tcx>) -> Expr<'tcx> { - match self { - ExprRef::Thir(h) => h.make_mirror(hir), - ExprRef::Mirror(m) => *m, - } - } -} - -impl<'tcx> Mirror<'tcx> for Stmt<'tcx> { - type Output = Stmt<'tcx>; - - fn make_mirror(self, _: &mut Cx<'_, 'tcx>) -> Stmt<'tcx> { - self - } -} - -impl<'tcx> Mirror<'tcx> for StmtRef<'tcx> { - type Output = Stmt<'tcx>; - - fn make_mirror(self, _: &mut Cx<'_, 'tcx>) -> Stmt<'tcx> { - match self { - StmtRef::Mirror(m) => *m, - } - } -} - -impl<'tcx> Mirror<'tcx> for Block<'tcx> { - type Output = Block<'tcx>; - - fn make_mirror(self, _: &mut Cx<'_, 'tcx>) -> Block<'tcx> { - self - } -} diff --git a/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs b/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs index ef1419b5b743c..c0624c805a685 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs @@ -246,6 +246,18 @@ impl<'a, 'tcx> ConstToPat<'a, 'tcx> { }) } + fn field_pats( + &self, + vals: impl Iterator>, + ) -> Result>, FallbackToConstRef> { + vals.enumerate() + .map(|(idx, val)| { + let field = Field::new(idx); + Ok(FieldPat { field, pattern: self.recur(val, false)? }) + }) + .collect() + } + // Recursive helper for `to_pat`; invoke that (instead of calling this directly). fn recur( &self, @@ -257,16 +269,6 @@ impl<'a, 'tcx> ConstToPat<'a, 'tcx> { let tcx = self.tcx(); let param_env = self.param_env; - let field_pats = |vals: &[&'tcx ty::Const<'tcx>]| -> Result<_, _> { - vals.iter() - .enumerate() - .map(|(idx, val)| { - let field = Field::new(idx); - Ok(FieldPat { field, pattern: self.recur(val, false)? }) - }) - .collect() - }; - let kind = match cv.ty.kind() { ty::Float(_) => { tcx.struct_span_lint_hir( @@ -361,12 +363,12 @@ impl<'a, 'tcx> ConstToPat<'a, 'tcx> { variant_index: destructured .variant .expect("destructed const of adt without variant id"), - subpatterns: field_pats(destructured.fields)?, + subpatterns: self.field_pats(destructured.fields.iter().copied())?, } } ty::Tuple(_) | ty::Adt(_, _) => { let destructured = tcx.destructure_const(param_env.and(cv)); - PatKind::Leaf { subpatterns: field_pats(destructured.fields)? } + PatKind::Leaf { subpatterns: self.field_pats(destructured.fields.iter().copied())? } } ty::Array(..) => PatKind::Array { prefix: tcx 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 3a67eeff92c31..8c740a7ec155a 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/deconstruct_pat.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/deconstruct_pat.rs @@ -1343,7 +1343,9 @@ impl<'p, 'tcx> Fields<'p, 'tcx> { match &mut fields { Fields::Vec(pats) => { for (i, pat) in new_pats { - pats[i] = pat + if let Some(p) = pats.get_mut(i) { + *p = pat; + } } } Fields::Filtered { fields, .. } => { diff --git a/compiler/rustc_mir_build/src/thir/pattern/mod.rs b/compiler/rustc_mir_build/src/thir/pattern/mod.rs index 6e29e60b3034d..9ac79a37ac690 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/mod.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/mod.rs @@ -40,22 +40,22 @@ crate enum PatternError { } #[derive(Copy, Clone, Debug, PartialEq)] -crate enum BindingMode { +pub enum BindingMode { ByValue, ByRef(BorrowKind), } #[derive(Clone, Debug, PartialEq)] -crate struct FieldPat<'tcx> { - crate field: Field, - crate pattern: Pat<'tcx>, +pub struct FieldPat<'tcx> { + pub field: Field, + pub pattern: Pat<'tcx>, } #[derive(Clone, Debug, PartialEq)] -crate struct Pat<'tcx> { - crate ty: Ty<'tcx>, - crate span: Span, - crate kind: Box>, +pub struct Pat<'tcx> { + pub ty: Ty<'tcx>, + pub span: Span, + pub kind: Box>, } impl<'tcx> Pat<'tcx> { @@ -65,8 +65,8 @@ impl<'tcx> Pat<'tcx> { } #[derive(Copy, Clone, Debug, PartialEq)] -crate struct PatTyProj<'tcx> { - crate user_ty: CanonicalUserType<'tcx>, +pub struct PatTyProj<'tcx> { + pub user_ty: CanonicalUserType<'tcx>, } impl<'tcx> PatTyProj<'tcx> { @@ -92,8 +92,8 @@ impl<'tcx> PatTyProj<'tcx> { } #[derive(Copy, Clone, Debug, PartialEq)] -crate struct Ascription<'tcx> { - crate user_ty: PatTyProj<'tcx>, +pub struct Ascription<'tcx> { + pub user_ty: PatTyProj<'tcx>, /// Variance to use when relating the type `user_ty` to the **type of the value being /// matched**. Typically, this is `Variance::Covariant`, since the value being matched must /// have a type that is some subtype of the ascribed type. @@ -112,12 +112,12 @@ crate struct Ascription<'tcx> { /// requires that `&'static str <: T_x`, where `T_x` is the type of `x`. Really, we should /// probably be checking for a `PartialEq` impl instead, but this preserves the behavior /// of the old type-check for now. See #57280 for details. - crate variance: ty::Variance, - crate user_ty_span: Span, + pub variance: ty::Variance, + pub user_ty_span: Span, } #[derive(Clone, Debug, PartialEq)] -crate enum PatKind<'tcx> { +pub enum PatKind<'tcx> { Wild, AscribeUserType { @@ -195,10 +195,10 @@ crate enum PatKind<'tcx> { } #[derive(Copy, Clone, Debug, PartialEq)] -crate struct PatRange<'tcx> { - crate lo: &'tcx ty::Const<'tcx>, - crate hi: &'tcx ty::Const<'tcx>, - crate end: RangeEnd, +pub struct PatRange<'tcx> { + pub lo: &'tcx ty::Const<'tcx>, + pub hi: &'tcx ty::Const<'tcx>, + pub end: RangeEnd, } impl<'tcx> fmt::Display for Pat<'tcx> { diff --git a/compiler/rustc_parse/src/lexer/mod.rs b/compiler/rustc_parse/src/lexer/mod.rs index 4bf870eb7ce7e..bd8dfd678a97b 100644 --- a/compiler/rustc_parse/src/lexer/mod.rs +++ b/compiler/rustc_parse/src/lexer/mod.rs @@ -128,7 +128,7 @@ impl<'a> StringReader<'a> { } /// Turns simple `rustc_lexer::TokenKind` enum into a rich - /// `librustc_ast::TokenKind`. This turns strings into interned + /// `rustc_ast::TokenKind`. This turns strings into interned /// symbols and runs additional validation. fn cook_lexer_token(&self, token: rustc_lexer::TokenKind, start: BytePos) -> Option { Some(match token { diff --git a/compiler/rustc_parse/src/lib.rs b/compiler/rustc_parse/src/lib.rs index cea4de72df549..905077a48e25a 100644 --- a/compiler/rustc_parse/src/lib.rs +++ b/compiler/rustc_parse/src/lib.rs @@ -1,16 +1,21 @@ //! The main parser interface. +#![feature(array_windows)] #![feature(crate_visibility_modifier)] #![feature(bindings_after_at)] #![feature(iter_order_by)] -#![feature(or_patterns)] +#![cfg_attr(bootstrap, feature(or_patterns))] #![feature(box_syntax)] #![feature(box_patterns)] +#![recursion_limit = "256"] use rustc_ast as ast; -use rustc_ast::token::{self, Nonterminal}; -use rustc_ast::tokenstream::{self, CanSynthesizeMissingTokens, LazyTokenStream, TokenStream}; +use rustc_ast::token::{self, Nonterminal, Token, TokenKind}; +use rustc_ast::tokenstream::{self, AttributesData, CanSynthesizeMissingTokens, LazyTokenStream}; +use rustc_ast::tokenstream::{AttrAnnotatedTokenStream, AttrAnnotatedTokenTree}; +use rustc_ast::tokenstream::{Spacing, TokenStream}; use rustc_ast::AstLike; +use rustc_ast::Attribute; use rustc_ast_pretty::pprust; use rustc_data_structures::sync::Lrc; use rustc_errors::{Diagnostic, FatalError, Level, PResult}; @@ -20,8 +25,6 @@ use rustc_span::{FileName, SourceFile, Span}; use std::path::Path; use std::str; -use tracing::debug; - pub const MACRO_ARGUMENTS: Option<&str> = Some("macro arguments"); #[macro_use] @@ -254,19 +257,23 @@ pub fn nt_to_tokenstream( // before we fall back to the stringification. let convert_tokens = - |tokens: Option<&LazyTokenStream>| tokens.as_ref().map(|t| t.create_token_stream()); + |tokens: Option<&LazyTokenStream>| Some(tokens?.create_token_stream().to_tokenstream()); let tokens = match *nt { - Nonterminal::NtItem(ref item) => prepend_attrs(sess, &item.attrs, nt, item.tokens.as_ref()), + Nonterminal::NtItem(ref item) => prepend_attrs(&item.attrs, item.tokens.as_ref()), Nonterminal::NtBlock(ref block) => convert_tokens(block.tokens.as_ref()), Nonterminal::NtStmt(ref stmt) => { - let do_prepend = |tokens| prepend_attrs(sess, stmt.attrs(), nt, tokens); if let ast::StmtKind::Empty = stmt.kind { - let tokens: TokenStream = - tokenstream::TokenTree::token(token::Semi, stmt.span).into(); - do_prepend(Some(&LazyTokenStream::new(tokens))) + let tokens = AttrAnnotatedTokenStream::new(vec![( + tokenstream::AttrAnnotatedTokenTree::Token(Token::new( + TokenKind::Semi, + stmt.span, + )), + Spacing::Alone, + )]); + prepend_attrs(&stmt.attrs(), Some(&LazyTokenStream::new(tokens))) } else { - do_prepend(stmt.tokens()) + prepend_attrs(&stmt.attrs(), stmt.tokens()) } } Nonterminal::NtPat(ref pat) => convert_tokens(pat.tokens.as_ref()), @@ -282,10 +289,7 @@ pub fn nt_to_tokenstream( Nonterminal::NtVis(ref vis) => convert_tokens(vis.tokens.as_ref()), Nonterminal::NtTT(ref tt) => Some(tt.clone().into()), Nonterminal::NtExpr(ref expr) | Nonterminal::NtLiteral(ref expr) => { - if expr.tokens.is_none() { - debug!("missing tokens for expr {:?}", expr); - } - prepend_attrs(sess, &expr.attrs, nt, expr.tokens.as_ref()) + prepend_attrs(&expr.attrs, expr.tokens.as_ref()) } }; @@ -294,34 +298,30 @@ pub fn nt_to_tokenstream( } else if matches!(synthesize_tokens, CanSynthesizeMissingTokens::Yes) { return fake_token_stream(sess, nt); } else { - panic!("Missing tokens for nt at {:?}: {:?}", nt.span(), pprust::nonterminal_to_string(nt)); + panic!( + "Missing tokens for nt {:?} at {:?}: {:?}", + nt, + nt.span(), + pprust::nonterminal_to_string(nt) + ); } } +fn prepend_attrs(attrs: &[Attribute], tokens: Option<&LazyTokenStream>) -> Option { + let tokens = tokens?; + if attrs.is_empty() { + return Some(tokens.create_token_stream().to_tokenstream()); + } + let attr_data = AttributesData { attrs: attrs.to_vec().into(), tokens: tokens.clone() }; + let wrapped = AttrAnnotatedTokenStream::new(vec![( + AttrAnnotatedTokenTree::Attributes(attr_data), + Spacing::Alone, + )]); + Some(wrapped.to_tokenstream()) +} + pub fn fake_token_stream(sess: &ParseSess, nt: &Nonterminal) -> TokenStream { let source = pprust::nonterminal_to_string(nt); let filename = FileName::macro_expansion_source_code(&source); parse_stream_from_source_str(filename, source, sess, Some(nt.span())) } - -fn prepend_attrs( - sess: &ParseSess, - attrs: &[ast::Attribute], - nt: &Nonterminal, - tokens: Option<&tokenstream::LazyTokenStream>, -) -> Option { - if attrs.is_empty() { - return Some(tokens?.create_token_stream()); - } - let mut builder = tokenstream::TokenStreamBuilder::new(); - for attr in attrs { - // FIXME: Correctly handle tokens for inner attributes. - // For now, we fall back to reparsing the original AST node - if attr.style == ast::AttrStyle::Inner { - return Some(fake_token_stream(sess, nt)); - } - builder.push(attr.tokens()); - } - builder.push(tokens?.create_token_stream()); - Some(builder.build()) -} diff --git a/compiler/rustc_parse/src/parser/attr.rs b/compiler/rustc_parse/src/parser/attr.rs index 95d4a48b845ef..ee6ff4dba396a 100644 --- a/compiler/rustc_parse/src/parser/attr.rs +++ b/compiler/rustc_parse/src/parser/attr.rs @@ -1,10 +1,11 @@ -use super::{AttrWrapper, Parser, PathStyle}; +use super::{AttrWrapper, Capturing, Parser, PathStyle}; use rustc_ast as ast; use rustc_ast::attr; use rustc_ast::token::{self, Nonterminal}; use rustc_ast_pretty::pprust; use rustc_errors::{error_code, PResult}; use rustc_span::{sym, Span}; +use std::convert::TryInto; use tracing::debug; @@ -29,6 +30,7 @@ impl<'a> Parser<'a> { pub(super) fn parse_outer_attributes(&mut self) -> PResult<'a, AttrWrapper> { let mut attrs: Vec = Vec::new(); let mut just_parsed_doc_comment = false; + let start_pos = self.token_cursor.num_next_calls; loop { debug!("parse_outer_attributes: self.token={:?}", self.token); let attr = if self.check(&token::Pound) { @@ -74,7 +76,7 @@ impl<'a> Parser<'a> { break; } } - Ok(AttrWrapper::new(attrs)) + Ok(AttrWrapper::new(attrs.into(), start_pos)) } /// Matches `attribute = # ! [ meta_item ]`. @@ -177,6 +179,7 @@ impl<'a> Parser<'a> { crate fn parse_inner_attributes(&mut self) -> PResult<'a, Vec> { let mut attrs: Vec = vec![]; loop { + let start_pos: u32 = self.token_cursor.num_next_calls.try_into().unwrap(); // Only try to parse if it is an inner attribute (has `!`). let attr = if self.check(&token::Pound) && self.look_ahead(1, |t| t == &token::Not) { Some(self.parse_attribute(InnerAttrPolicy::Permitted)?) @@ -191,6 +194,18 @@ impl<'a> Parser<'a> { None }; if let Some(attr) = attr { + let end_pos: u32 = self.token_cursor.num_next_calls.try_into().unwrap(); + // If we are currently capturing tokens, mark the location of this inner attribute. + // If capturing ends up creating a `LazyTokenStream`, we will include + // this replace range with it, removing the inner attribute from the final + // `AttrAnnotatedTokenStream`. Inner attributes are stored in the parsed AST note. + // During macro expansion, they are selectively inserted back into the + // token stream (the first inner attribute is remoevd each time we invoke the + // corresponding macro). + let range = start_pos..end_pos; + if let Capturing::Yes = self.capture_state.capturing { + self.capture_state.inner_attr_ranges.insert(attr.id, (range, vec![])); + } attrs.push(attr); } else { break; @@ -311,6 +326,9 @@ pub fn maybe_needs_tokens(attrs: &[ast::Attribute]) -> bool { // One of the attributes may either itself be a macro, // or expand to macro attributes (`cfg_attr`). attrs.iter().any(|attr| { + if attr.is_doc_comment() { + return false; + } attr.ident().map_or(true, |ident| { ident.name == sym::cfg_attr || !rustc_feature::is_builtin_attr_name(ident.name) }) diff --git a/compiler/rustc_parse/src/parser/attr_wrapper.rs b/compiler/rustc_parse/src/parser/attr_wrapper.rs index f45d8d6c7a00e..35759a396e87c 100644 --- a/compiler/rustc_parse/src/parser/attr_wrapper.rs +++ b/compiler/rustc_parse/src/parser/attr_wrapper.rs @@ -1,12 +1,14 @@ -use super::attr; -use super::{ForceCollect, Parser, TokenCursor, TrailingToken}; -use rustc_ast::token::{self, Token, TokenKind}; -use rustc_ast::tokenstream::{CreateTokenStream, TokenStream, TokenTree, TreeAndSpacing}; -use rustc_ast::tokenstream::{DelimSpan, LazyTokenStream, Spacing}; -use rustc_ast::AstLike; +use super::{Capturing, FlatToken, ForceCollect, Parser, ReplaceRange, TokenCursor, TrailingToken}; +use rustc_ast::token::{self, DelimToken, Token, TokenKind}; +use rustc_ast::tokenstream::{AttrAnnotatedTokenStream, AttributesData, CreateTokenStream}; +use rustc_ast::tokenstream::{AttrAnnotatedTokenTree, DelimSpan, LazyTokenStream, Spacing}; use rustc_ast::{self as ast}; +use rustc_ast::{AstLike, AttrVec, Attribute}; use rustc_errors::PResult; -use rustc_span::{Span, DUMMY_SP}; +use rustc_span::{sym, Span, DUMMY_SP}; + +use std::convert::TryInto; +use std::ops::Range; /// A wrapper type to ensure that the parser handles outer attributes correctly. /// When we parse outer attributes, we need to ensure that we capture tokens @@ -23,23 +25,158 @@ use rustc_span::{Span, DUMMY_SP}; /// cannot directly access the `attrs` field #[derive(Debug, Clone)] pub struct AttrWrapper { - attrs: Vec, + attrs: AttrVec, + // The start of the outer attributes in the token cursor. + // This allows us to create a `ReplaceRange` for the entire attribute + // target, including outer attributes. + start_pos: usize, } +// This struct is passed around very frequently, +// so make sure it doesn't accidentally get larger +#[cfg(target_arch = "x86_64")] +rustc_data_structures::static_assert_size!(AttrWrapper, 16); + impl AttrWrapper { - pub fn empty() -> AttrWrapper { - AttrWrapper { attrs: vec![] } + pub(super) fn new(attrs: AttrVec, start_pos: usize) -> AttrWrapper { + AttrWrapper { attrs, start_pos } } - pub fn new(attrs: Vec) -> AttrWrapper { - AttrWrapper { attrs } + pub fn empty() -> AttrWrapper { + AttrWrapper { attrs: AttrVec::new(), start_pos: usize::MAX } } // FIXME: Delay span bug here? - pub(crate) fn take_for_recovery(self) -> Vec { + pub(crate) fn take_for_recovery(self) -> AttrVec { self.attrs } + + // FIXME: require passing an NT to prevent misuse of this method + pub(crate) fn prepend_to_nt_inner(self, attrs: &mut Vec) { + let mut self_attrs: Vec<_> = self.attrs.into(); + std::mem::swap(attrs, &mut self_attrs); + attrs.extend(self_attrs); + } + pub fn is_empty(&self) -> bool { self.attrs.is_empty() } + + pub fn maybe_needs_tokens(&self) -> bool { + crate::parser::attr::maybe_needs_tokens(&self.attrs) + } +} + +/// Returns `true` if `attrs` contains a `cfg` or `cfg_attr` attribute +fn has_cfg_or_cfg_attr(attrs: &[Attribute]) -> bool { + // NOTE: Builtin attributes like `cfg` and `cfg_attr` cannot be renamed via imports. + // Therefore, the absence of a literal `cfg` or `cfg_attr` guarantees that + // we don't need to do any eager expansion. + attrs.iter().any(|attr| { + attr.ident().map_or(false, |ident| ident.name == sym::cfg || ident.name == sym::cfg_attr) + }) +} + +// Produces a `TokenStream` on-demand. Using `cursor_snapshot` +// and `num_calls`, we can reconstruct the `TokenStream` seen +// by the callback. This allows us to avoid producing a `TokenStream` +// if it is never needed - for example, a captured `macro_rules!` +// argument that is never passed to a proc macro. +// In practice token stream creation happens rarely compared to +// calls to `collect_tokens` (see some statistics in #78736), +// so we are doing as little up-front work as possible. +// +// This also makes `Parser` very cheap to clone, since +// there is no intermediate collection buffer to clone. +#[derive(Clone)] +struct LazyTokenStreamImpl { + start_token: (Token, Spacing), + cursor_snapshot: TokenCursor, + num_calls: usize, + break_last_token: bool, + replace_ranges: Box<[ReplaceRange]>, +} + +#[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))] +rustc_data_structures::static_assert_size!(LazyTokenStreamImpl, 144); + +impl CreateTokenStream for LazyTokenStreamImpl { + fn create_token_stream(&self) -> AttrAnnotatedTokenStream { + // The token produced by the final call to `next` or `next_desugared` + // was not actually consumed by the callback. The combination + // of chaining the initial token and using `take` produces the desired + // result - we produce an empty `TokenStream` if no calls were made, + // and omit the final token otherwise. + let mut cursor_snapshot = self.cursor_snapshot.clone(); + let tokens = + std::iter::once((FlatToken::Token(self.start_token.0.clone()), self.start_token.1)) + .chain((0..self.num_calls).map(|_| { + let token = if cursor_snapshot.desugar_doc_comments { + cursor_snapshot.next_desugared() + } else { + cursor_snapshot.next() + }; + (FlatToken::Token(token.0), token.1) + })) + .take(self.num_calls); + + if !self.replace_ranges.is_empty() { + let mut tokens: Vec<_> = tokens.collect(); + let mut replace_ranges = self.replace_ranges.clone(); + replace_ranges.sort_by_key(|(range, _)| range.start); + + #[cfg(debug_assertions)] + { + for [(range, tokens), (next_range, next_tokens)] in replace_ranges.array_windows() { + assert!( + range.end <= next_range.start || range.end >= next_range.end, + "Replace ranges should either be disjoint or nested: ({:?}, {:?}) ({:?}, {:?})", + range, + tokens, + next_range, + next_tokens, + ); + } + } + + // Process the replace ranges, starting from the highest start + // position and working our way back. If have tokens like: + // + // `#[cfg(FALSE)]` struct Foo { #[cfg(FALSE)] field: bool }` + // + // Then we will generate replace ranges for both + // the `#[cfg(FALSE)] field: bool` and the entire + // `#[cfg(FALSE)]` struct Foo { #[cfg(FALSE)] field: bool }` + // + // By starting processing from the replace range with the greatest + // start position, we ensure that any replace range which encloses + // another replace range will capture the *replaced* tokens for the inner + // range, not the original tokens. + for (range, new_tokens) in replace_ranges.iter().rev() { + assert!(!range.is_empty(), "Cannot replace an empty range: {:?}", range); + // Replace ranges are only allowed to decrease the number of tokens. + assert!( + range.len() >= new_tokens.len(), + "Range {:?} has greater len than {:?}", + range, + new_tokens + ); + + // Replace any removed tokens with `FlatToken::Empty`. + // This keeps the total length of `tokens` constant throughout the + // replacement process, allowing us to use all of the `ReplaceRanges` entries + // without adjusting indices. + let filler = std::iter::repeat((FlatToken::Empty, Spacing::Alone)) + .take(range.len() - new_tokens.len()); + + tokens.splice( + (range.start as usize)..(range.end as usize), + new_tokens.clone().into_iter().chain(filler), + ); + } + make_token_stream(tokens.into_iter(), self.break_last_token) + } else { + make_token_stream(tokens, self.break_last_token) + } + } } impl<'a> Parser<'a> { @@ -65,77 +202,195 @@ impl<'a> Parser<'a> { force_collect: ForceCollect, f: impl FnOnce(&mut Self, Vec) -> PResult<'a, (R, TrailingToken)>, ) -> PResult<'a, R> { - if matches!(force_collect, ForceCollect::No) && !attr::maybe_needs_tokens(&attrs.attrs) { - return Ok(f(self, attrs.attrs)?.0); + // We only bail out when nothing could possibly observe the collected tokens: + // 1. We cannot be force collecting tokens (since force-collecting requires tokens + // by definition + if matches!(force_collect, ForceCollect::No) + // None of our outer attributes can require tokens (e.g. a proc-macro) + && !attrs.maybe_needs_tokens() + // If our target supports custom inner attributes, then we cannot bail + // out early, since we may need to capture tokens for a custom inner attribute + // invocation. + && !R::SUPPORTS_CUSTOM_INNER_ATTRS + // Never bail out early in `capture_cfg` mode, since there might be `#[cfg]` + // or `#[cfg_attr]` attributes. + && !self.capture_cfg + { + return Ok(f(self, attrs.attrs.into())?.0); } + let start_token = (self.token.clone(), self.token_spacing); let cursor_snapshot = self.token_cursor.clone(); - let (mut ret, trailing_token) = f(self, attrs.attrs)?; - - // Produces a `TokenStream` on-demand. Using `cursor_snapshot` - // and `num_calls`, we can reconstruct the `TokenStream` seen - // by the callback. This allows us to avoid producing a `TokenStream` - // if it is never needed - for example, a captured `macro_rules!` - // argument that is never passed to a proc macro. - // In practice token stream creation happens rarely compared to - // calls to `collect_tokens` (see some statistics in #78736), - // so we are doing as little up-front work as possible. - // - // This also makes `Parser` very cheap to clone, since - // there is no intermediate collection buffer to clone. - #[derive(Clone)] - struct LazyTokenStreamImpl { - start_token: (Token, Spacing), - cursor_snapshot: TokenCursor, - num_calls: usize, - desugar_doc_comments: bool, - append_unglued_token: Option, + let has_outer_attrs = !attrs.attrs.is_empty(); + let prev_capturing = std::mem::replace(&mut self.capture_state.capturing, Capturing::Yes); + let replace_ranges_start = self.capture_state.replace_ranges.len(); + + let ret = f(self, attrs.attrs.into()); + + self.capture_state.capturing = prev_capturing; + + let (mut ret, trailing) = ret?; + + // When we're not in `capture-cfg` mode, then bail out early if: + // 1. Our target doesn't support tokens at all (e.g we're parsing an `NtIdent`) + // so there's nothing for us to do. + // 2. Our target already has tokens set (e.g. we've parsed something + // like `#[my_attr] $item`. The actual parsing code takes care of prepending + // any attributes to the nonterminal, so we don't need to modify the + // already captured tokens. + // Note that this check is independent of `force_collect`- if we already + // have tokens, or can't even store them, then there's never a need to + // force collection of new tokens. + if !self.capture_cfg && matches!(ret.tokens_mut(), None | Some(Some(_))) { + return Ok(ret); + } + + // This is very similar to the bail out check at the start of this function. + // Now that we've parsed an AST node, we have more information available. + if matches!(force_collect, ForceCollect::No) + // We now have inner attributes available, so this check is more precise + // than `attrs.maybe_needs_tokens()` at the start of the function. + // As a result, we don't need to check `R::SUPPORTS_CUSTOM_INNER_ATTRS` + && !crate::parser::attr::maybe_needs_tokens(ret.attrs()) + // Subtle: We call `has_cfg_or_cfg_attr` with the attrs from `ret`. + // This ensures that we consider inner attributes (e.g. `#![cfg]`), + // which require us to have tokens available + // We also call `has_cfg_or_cfg_attr` at the beginning of this function, + // but we only bail out if there's no possibility of inner attributes + // (!R::SUPPORTS_CUSTOM_INNER_ATTRS) + // We only catpure about `#[cfg]` or `#[cfg_attr]` in `capture_cfg` + // mode - during normal parsing, we don't need any special capturing + // for those attributes, since they're builtin. + && !(self.capture_cfg && has_cfg_or_cfg_attr(ret.attrs())) + { + return Ok(ret); } - impl CreateTokenStream for LazyTokenStreamImpl { - fn create_token_stream(&self) -> TokenStream { - // The token produced by the final call to `next` or `next_desugared` - // was not actually consumed by the callback. The combination - // of chaining the initial token and using `take` produces the desired - // result - we produce an empty `TokenStream` if no calls were made, - // and omit the final token otherwise. - let mut cursor_snapshot = self.cursor_snapshot.clone(); - let tokens = std::iter::once(self.start_token.clone()) - .chain((0..self.num_calls).map(|_| { - if self.desugar_doc_comments { - cursor_snapshot.next_desugared() - } else { - cursor_snapshot.next() - } - })) - .take(self.num_calls); - - make_token_stream(tokens, self.append_unglued_token.clone()) + + let mut inner_attr_replace_ranges = Vec::new(); + // Take the captured ranges for any inner attributes that we parsed. + for inner_attr in ret.attrs().iter().filter(|a| a.style == ast::AttrStyle::Inner) { + if let Some(attr_range) = self.capture_state.inner_attr_ranges.remove(&inner_attr.id) { + inner_attr_replace_ranges.push(attr_range); + } else { + self.sess + .span_diagnostic + .delay_span_bug(inner_attr.span, "Missing token range for attribute"); } } - let mut num_calls = self.token_cursor.num_next_calls - cursor_snapshot.num_next_calls; - match trailing_token { + let replace_ranges_end = self.capture_state.replace_ranges.len(); + + let cursor_snapshot_next_calls = cursor_snapshot.num_next_calls; + let mut end_pos = self.token_cursor.num_next_calls; + + // Capture a trailing token if requested by the callback 'f' + match trailing { TrailingToken::None => {} TrailingToken::Semi => { assert_eq!(self.token.kind, token::Semi); - num_calls += 1; + end_pos += 1; } TrailingToken::MaybeComma => { if self.token.kind == token::Comma { - num_calls += 1; + end_pos += 1; } } } - let lazy_impl = LazyTokenStreamImpl { + // If we 'broke' the last token (e.g. breaking a '>>' token to two '>' tokens), + // then extend the range of captured tokens to include it, since the parser + // was not actually bumped past it. When the `LazyTokenStream` gets converted + // into a `AttrAnnotatedTokenStream`, we will create the proper token. + if self.token_cursor.break_last_token { + assert_eq!( + trailing, + TrailingToken::None, + "Cannot set `break_last_token` and have trailing token" + ); + end_pos += 1; + } + + let num_calls = end_pos - cursor_snapshot_next_calls; + + // If we have no attributes, then we will never need to + // use any replace ranges. + let replace_ranges: Box<[ReplaceRange]> = if ret.attrs().is_empty() && !self.capture_cfg { + Box::new([]) + } else { + // Grab any replace ranges that occur *inside* the current AST node. + // We will perform the actual replacement when we convert the `LazyTokenStream` + // to a `AttrAnnotatedTokenStream` + let start_calls: u32 = cursor_snapshot_next_calls.try_into().unwrap(); + self.capture_state.replace_ranges[replace_ranges_start..replace_ranges_end] + .iter() + .cloned() + .chain(inner_attr_replace_ranges.clone().into_iter()) + .map(|(range, tokens)| { + ((range.start - start_calls)..(range.end - start_calls), tokens) + }) + .collect() + }; + + let tokens = LazyTokenStream::new(LazyTokenStreamImpl { start_token, num_calls, cursor_snapshot, - desugar_doc_comments: self.desugar_doc_comments, - append_unglued_token: self.token_cursor.append_unglued_token.clone(), - }; - ret.finalize_tokens(LazyTokenStream::new(lazy_impl)); + break_last_token: self.token_cursor.break_last_token, + replace_ranges, + }); + + // If we support tokens at all + if let Some(target_tokens) = ret.tokens_mut() { + if let Some(target_tokens) = target_tokens { + assert!( + !self.capture_cfg, + "Encountered existing tokens with capture_cfg set: {:?}", + target_tokens + ); + } else { + // Store se our newly captured tokens into the AST node + *target_tokens = Some(tokens.clone()); + }; + } + + let final_attrs = ret.attrs(); + + // If `capture_cfg` is set and we're inside a recursive call to + // `collect_tokens_trailing_token`, then we need to register a replace range + // if we have `#[cfg]` or `#[cfg_attr]`. This allows us to run eager cfg-expansion + // on the captured token stream. + if self.capture_cfg + && matches!(self.capture_state.capturing, Capturing::Yes) + && has_cfg_or_cfg_attr(&final_attrs) + { + let attr_data = AttributesData { attrs: final_attrs.to_vec().into(), tokens }; + + // Replace the entire AST node that we just parsed, including attributes, + // with a `FlatToken::AttrTarget`. If this AST node is inside an item + // that has `#[derive]`, then this will allow us to cfg-expand this + // AST node. + let start_pos = + if has_outer_attrs { attrs.start_pos } else { cursor_snapshot_next_calls }; + let new_tokens = vec![(FlatToken::AttrTarget(attr_data), Spacing::Alone)]; + + assert!( + !self.token_cursor.break_last_token, + "Should not have unglued last token with cfg attr" + ); + let range: Range = (start_pos.try_into().unwrap())..(end_pos.try_into().unwrap()); + self.capture_state.replace_ranges.push((range, new_tokens)); + self.capture_state.replace_ranges.extend(inner_attr_replace_ranges); + } + + // Only clear our `replace_ranges` when we're finished capturing entirely. + if matches!(self.capture_state.capturing, Capturing::No) { + self.capture_state.replace_ranges.clear(); + // We don't clear `inner_attr_ranges`, as doing so repeatedly + // had a measureable performance impact. Most inner attributes that + // we insert will get removed - when we drop the parser, we'll free + // up the memory used by any attributes that we didn't remove from the map. + } Ok(ret) } } @@ -143,43 +398,112 @@ impl<'a> Parser<'a> { /// Converts a flattened iterator of tokens (including open and close delimiter tokens) /// into a `TokenStream`, creating a `TokenTree::Delimited` for each matching pair /// of open and close delims. +// FIXME(#67062): Currently, we don't parse `None`-delimited groups correctly, +// which can cause us to end up with mismatched `None` delimiters in our +// captured tokens. This function contains several hacks to work around this - +// essentially, we throw away mismatched `None` delimiters when we encounter them. +// Once we properly parse `None` delimiters, they can be captured just like any +// other tokens, and these hacks can be removed. fn make_token_stream( - tokens: impl Iterator, - append_unglued_token: Option, -) -> TokenStream { + mut iter: impl Iterator, + break_last_token: bool, +) -> AttrAnnotatedTokenStream { #[derive(Debug)] struct FrameData { open: Span, - inner: Vec<(TokenTree, Spacing)>, + open_delim: DelimToken, + inner: Vec<(AttrAnnotatedTokenTree, Spacing)>, } - let mut stack = vec![FrameData { open: DUMMY_SP, inner: vec![] }]; - for (token, spacing) in tokens { + let mut stack = + vec![FrameData { open: DUMMY_SP, open_delim: DelimToken::NoDelim, inner: vec![] }]; + let mut token_and_spacing = iter.next(); + while let Some((token, spacing)) = token_and_spacing { match token { - Token { kind: TokenKind::OpenDelim(_), span } => { - stack.push(FrameData { open: span, inner: vec![] }); + FlatToken::Token(Token { kind: TokenKind::OpenDelim(delim), span }) => { + stack.push(FrameData { open: span, open_delim: delim, inner: vec![] }); } - Token { kind: TokenKind::CloseDelim(delim), span } => { - let frame_data = stack.pop().expect("Token stack was empty!"); + FlatToken::Token(Token { kind: TokenKind::CloseDelim(delim), span }) => { + // HACK: If we enconter a mismatched `None` delimiter at the top + // level, just ignore it. + if matches!(delim, DelimToken::NoDelim) + && (stack.len() == 1 + || !matches!(stack.last_mut().unwrap().open_delim, DelimToken::NoDelim)) + { + token_and_spacing = iter.next(); + continue; + } + let frame_data = stack + .pop() + .unwrap_or_else(|| panic!("Token stack was empty for token: {:?}", token)); + + // HACK: If our current frame has a mismatched opening `None` delimiter, + // merge our current frame with the one above it. That is, transform + // `[ { < first second } third ]` into `[ { first second } third ]` + if !matches!(delim, DelimToken::NoDelim) + && matches!(frame_data.open_delim, DelimToken::NoDelim) + { + stack.last_mut().unwrap().inner.extend(frame_data.inner); + // Process our closing delimiter again, this time at the previous + // frame in the stack + token_and_spacing = Some((token, spacing)); + continue; + } + + assert_eq!( + frame_data.open_delim, delim, + "Mismatched open/close delims: open={:?} close={:?}", + frame_data.open, span + ); let dspan = DelimSpan::from_pair(frame_data.open, span); - let stream = TokenStream::new(frame_data.inner); - let delimited = TokenTree::Delimited(dspan, delim, stream); + let stream = AttrAnnotatedTokenStream::new(frame_data.inner); + let delimited = AttrAnnotatedTokenTree::Delimited(dspan, delim, stream); stack .last_mut() - .unwrap_or_else(|| panic!("Bottom token frame is missing for tokens!")) + .unwrap_or_else(|| { + panic!("Bottom token frame is missing for token: {:?}", token) + }) .inner .push((delimited, Spacing::Alone)); } - token => { - stack - .last_mut() - .expect("Bottom token frame is missing!") - .inner - .push((TokenTree::Token(token), spacing)); - } + FlatToken::Token(token) => stack + .last_mut() + .expect("Bottom token frame is missing!") + .inner + .push((AttrAnnotatedTokenTree::Token(token), spacing)), + FlatToken::AttrTarget(data) => stack + .last_mut() + .expect("Bottom token frame is missing!") + .inner + .push((AttrAnnotatedTokenTree::Attributes(data), spacing)), + FlatToken::Empty => {} } + token_and_spacing = iter.next(); + } + // HACK: If we don't have a closing `None` delimiter for our last + // frame, merge the frame with the top-level frame. That is, + // turn `< first second` into `first second` + if stack.len() == 2 && stack[1].open_delim == DelimToken::NoDelim { + let temp_buf = stack.pop().unwrap(); + stack.last_mut().unwrap().inner.extend(temp_buf.inner); } let mut final_buf = stack.pop().expect("Missing final buf!"); - final_buf.inner.extend(append_unglued_token); + if break_last_token { + let (last_token, spacing) = final_buf.inner.pop().unwrap(); + if let AttrAnnotatedTokenTree::Token(last_token) = last_token { + let unglued_first = last_token.kind.break_two_token_op().unwrap().0; + + // A 'unglued' token is always two ASCII characters + let mut first_span = last_token.span.shrink_to_lo(); + first_span = first_span.with_hi(first_span.lo() + rustc_span::BytePos(1)); + + final_buf.inner.push(( + AttrAnnotatedTokenTree::Token(Token::new(unglued_first, first_span)), + spacing, + )); + } else { + panic!("Unexpected last token {:?}", last_token) + } + } assert!(stack.is_empty(), "Stack should be empty: final_buf={:?} stack={:?}", final_buf, stack); - TokenStream::new(final_buf.inner) + AttrAnnotatedTokenStream::new(final_buf.inner) } diff --git a/compiler/rustc_parse/src/parser/diagnostics.rs b/compiler/rustc_parse/src/parser/diagnostics.rs index f4ab3260d1a83..70a5ac6f15ec2 100644 --- a/compiler/rustc_parse/src/parser/diagnostics.rs +++ b/compiler/rustc_parse/src/parser/diagnostics.rs @@ -640,7 +640,7 @@ impl<'a> Parser<'a> { } } Err(mut err) => { - // We could't parse generic parameters, unlikely to be a turbofish. Rely on + // We couldn't parse generic parameters, unlikely to be a turbofish. Rely on // generic parse error instead. err.cancel(); *self = snapshot; @@ -666,21 +666,23 @@ impl<'a> Parser<'a> { ); match x { Ok((_, _, false)) => { - self.bump(); // `>` - match self.parse_expr() { - Ok(_) => { - e.span_suggestion_verbose( - binop.span.shrink_to_lo(), - TURBOFISH_SUGGESTION_STR, - "::".to_string(), - Applicability::MaybeIncorrect, - ); - e.emit(); - *expr = self.mk_expr_err(expr.span.to(self.prev_token.span)); - return Ok(()); - } - Err(mut err) => { - err.cancel(); + if self.eat(&token::Gt) { + match self.parse_expr() { + Ok(_) => { + e.span_suggestion_verbose( + binop.span.shrink_to_lo(), + TURBOFISH_SUGGESTION_STR, + "::".to_string(), + Applicability::MaybeIncorrect, + ); + e.emit(); + *expr = + self.mk_expr_err(expr.span.to(self.prev_token.span)); + return Ok(()); + } + Err(mut err) => { + err.cancel(); + } } } } @@ -1242,7 +1244,7 @@ impl<'a> Parser<'a> { let is_question = self.eat(&token::Question); // Handle `await? `. let expr = if self.token == token::OpenDelim(token::Brace) { // Handle `await { }`. - // This needs to be handled separatedly from the next arm to avoid + // This needs to be handled separately from the next arm to avoid // interpreting `await { }?` as `?.await`. self.parse_block_expr(None, self.token.span, BlockCheckMode::Default, AttrVec::new()) } else { @@ -1613,42 +1615,82 @@ impl<'a> Parser<'a> { Applicability::HasPlaceholders, ); return Some(ident); - } else if let PatKind::Ident(_, ident, _) = pat.kind { - if require_name - && (self.token == token::Comma - || self.token == token::Lt - || self.token == token::CloseDelim(token::Paren)) - { - // `fn foo(a, b) {}`, `fn foo(a, b) {}` or `fn foo(usize, usize) {}` - if first_param { - err.span_suggestion( - pat.span, - "if this is a `self` type, give it a parameter name", - format!("self: {}", ident), - Applicability::MaybeIncorrect, - ); + } else if require_name + && (self.token == token::Comma + || self.token == token::Lt + || self.token == token::CloseDelim(token::Paren)) + { + let rfc_note = "anonymous parameters are removed in the 2018 edition (see RFC 1685)"; + + let (ident, self_sugg, param_sugg, type_sugg) = match pat.kind { + PatKind::Ident(_, ident, _) => ( + ident, + format!("self: {}", ident), + format!("{}: TypeName", ident), + format!("_: {}", ident), + ), + // Also catches `fn foo(&a)`. + PatKind::Ref(ref pat, mutab) + if matches!(pat.clone().into_inner().kind, PatKind::Ident(..)) => + { + match pat.clone().into_inner().kind { + PatKind::Ident(_, ident, _) => { + let mutab = mutab.prefix_str(); + ( + ident, + format!("self: &{}{}", mutab, ident), + format!("{}: &{}TypeName", ident, mutab), + format!("_: &{}{}", mutab, ident), + ) + } + _ => unreachable!(), + } } - // Avoid suggesting that `fn foo(HashMap)` is fixed with a change to - // `fn foo(HashMap: TypeName)`. - if self.token != token::Lt { - err.span_suggestion( - pat.span, - "if this is a parameter name, give it a type", - format!("{}: TypeName", ident), - Applicability::HasPlaceholders, - ); + _ => { + // Otherwise, try to get a type and emit a suggestion. + if let Some(ty) = pat.to_ty() { + err.span_suggestion_verbose( + pat.span, + "explicitly ignore the parameter name", + format!("_: {}", pprust::ty_to_string(&ty)), + Applicability::MachineApplicable, + ); + err.note(rfc_note); + } + + return None; } + }; + + // `fn foo(a, b) {}`, `fn foo(a, b) {}` or `fn foo(usize, usize) {}` + if first_param { err.span_suggestion( pat.span, - "if this is a type, explicitly ignore the parameter name", - format!("_: {}", ident), - Applicability::MachineApplicable, + "if this is a `self` type, give it a parameter name", + self_sugg, + Applicability::MaybeIncorrect, + ); + } + // Avoid suggesting that `fn foo(HashMap)` is fixed with a change to + // `fn foo(HashMap: TypeName)`. + if self.token != token::Lt { + err.span_suggestion( + pat.span, + "if this is a parameter name, give it a type", + param_sugg, + Applicability::HasPlaceholders, ); - err.note("anonymous parameters are removed in the 2018 edition (see RFC 1685)"); - - // Don't attempt to recover by using the `X` in `X` as the parameter name. - return if self.token == token::Lt { None } else { Some(ident) }; } + err.span_suggestion( + pat.span, + "if this is a type, explicitly ignore the parameter name", + type_sugg, + Applicability::MachineApplicable, + ); + err.note(rfc_note); + + // Don't attempt to recover by using the `X` in `X` as the parameter name. + return if self.token == token::Lt { None } else { Some(ident) }; } None } diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs index 28bfaea4555b5..e155b3fa77391 100644 --- a/compiler/rustc_parse/src/parser/expr.rs +++ b/compiler/rustc_parse/src/parser/expr.rs @@ -1,4 +1,4 @@ -use super::pat::{GateOr, RecoverComma, PARAM_EXPECTED}; +use super::pat::{RecoverComma, PARAM_EXPECTED}; use super::ty::{AllowPlus, RecoverQPath, RecoverReturnSign}; use super::{AttrWrapper, BlockMode, ForceCollect, Parser, PathStyle, Restrictions, TokenType}; use super::{SemiColonMode, SeqSep, TokenExpectType, TrailingToken}; @@ -10,7 +10,7 @@ use rustc_ast::tokenstream::Spacing; use rustc_ast::util::classify; use rustc_ast::util::literal::LitError; use rustc_ast::util::parser::{prec_let_scrutinee_needs_par, AssocOp, Fixity}; -use rustc_ast::{self as ast, AttrStyle, AttrVec, CaptureBy, Field, Lit, UnOp, DUMMY_NODE_ID}; +use rustc_ast::{self as ast, AttrStyle, AttrVec, CaptureBy, ExprField, Lit, UnOp, DUMMY_NODE_ID}; use rustc_ast::{AnonConst, BinOp, BinOpKind, FnDecl, FnRetTy, MacCall, Param, Ty, TyKind}; use rustc_ast::{Arm, Async, BlockCheckMode, Expr, ExprKind, Label, Movability, RangeLimits}; use rustc_ast_pretty::pprust; @@ -92,7 +92,22 @@ impl<'a> Parser<'a> { self.parse_expr_res(Restrictions::empty(), None) } - pub(super) fn parse_anon_const_expr(&mut self) -> PResult<'a, AnonConst> { + /// Parses an expression, forcing tokens to be collected + pub fn parse_expr_force_collect(&mut self) -> PResult<'a, P> { + // If we have outer attributes, then the call to `collect_tokens_trailing_token` + // will be made for us. + if matches!(self.token.kind, TokenKind::Pound | TokenKind::DocComment(..)) { + self.parse_expr() + } else { + // If we don't have outer attributes, then we need to ensure + // that collection happens by using `collect_tokens_no_attrs`. + // Expression don't support custom inner attributes, so `parse_expr` + // will never try to collect tokens if we don't have outer attributes. + self.collect_tokens_no_attrs(|this| this.parse_expr()) + } + } + + pub fn parse_anon_const_expr(&mut self) -> PResult<'a, AnonConst> { self.parse_expr().map(|value| AnonConst { id: DUMMY_NODE_ID, value }) } @@ -1803,7 +1818,7 @@ impl<'a> Parser<'a> { /// The `let` token has already been eaten. fn parse_let_expr(&mut self, attrs: AttrVec) -> PResult<'a, P> { let lo = self.prev_token.span; - let pat = self.parse_pat_allow_top_alt(None, GateOr::No, RecoverComma::Yes)?; + let pat = self.parse_pat_allow_top_alt(None, RecoverComma::Yes)?; self.expect(&token::Eq)?; let expr = self.with_res(self.restrictions | Restrictions::NO_STRUCT_LITERAL, |this| { this.parse_assoc_expr_with(1 + prec_let_scrutinee_needs_par(), None.into()) @@ -1866,7 +1881,7 @@ impl<'a> Parser<'a> { _ => None, }; - let pat = self.parse_pat_allow_top_alt(None, GateOr::Yes, RecoverComma::Yes)?; + let pat = self.parse_pat_allow_top_alt(None, RecoverComma::Yes)?; if !self.eat_keyword(kw::In) { self.error_missing_in_for_loop(); } @@ -1973,11 +1988,107 @@ impl<'a> Parser<'a> { Ok(self.mk_expr(lo.to(hi), ExprKind::Match(scrutinee, arms), attrs)) } + /// Attempt to recover from match arm body with statements and no surrounding braces. + fn parse_arm_body_missing_braces( + &mut self, + first_expr: &P, + arrow_span: Span, + ) -> Option> { + if self.token.kind != token::Semi { + return None; + } + let start_snapshot = self.clone(); + let semi_sp = self.token.span; + self.bump(); // `;` + let mut stmts = + vec![self.mk_stmt(first_expr.span, ast::StmtKind::Expr(first_expr.clone()))]; + let err = |this: &mut Parser<'_>, stmts: Vec| { + let span = stmts[0].span.to(stmts[stmts.len() - 1].span); + let mut err = this.struct_span_err(span, "`match` arm body without braces"); + let (these, s, are) = + if stmts.len() > 1 { ("these", "s", "are") } else { ("this", "", "is") }; + err.span_label( + span, + &format!( + "{these} statement{s} {are} not surrounded by a body", + these = these, + s = s, + are = are + ), + ); + err.span_label(arrow_span, "while parsing the `match` arm starting here"); + if stmts.len() > 1 { + err.multipart_suggestion( + &format!("surround the statement{} with a body", s), + vec![ + (span.shrink_to_lo(), "{ ".to_string()), + (span.shrink_to_hi(), " }".to_string()), + ], + Applicability::MachineApplicable, + ); + } else { + err.span_suggestion( + semi_sp, + "use a comma to end a `match` arm expression", + ",".to_string(), + Applicability::MachineApplicable, + ); + } + err.emit(); + this.mk_expr_err(span) + }; + // We might have either a `,` -> `;` typo, or a block without braces. We need + // a more subtle parsing strategy. + loop { + if self.token.kind == token::CloseDelim(token::Brace) { + // We have reached the closing brace of the `match` expression. + return Some(err(self, stmts)); + } + if self.token.kind == token::Comma { + *self = start_snapshot; + return None; + } + let pre_pat_snapshot = self.clone(); + match self.parse_pat_no_top_alt(None) { + Ok(_pat) => { + if self.token.kind == token::FatArrow { + // Reached arm end. + *self = pre_pat_snapshot; + return Some(err(self, stmts)); + } + } + Err(mut err) => { + err.cancel(); + } + } + + *self = pre_pat_snapshot; + match self.parse_stmt_without_recovery(true, ForceCollect::No) { + // Consume statements for as long as possible. + Ok(Some(stmt)) => { + stmts.push(stmt); + } + Ok(None) => { + *self = start_snapshot; + break; + } + // We couldn't parse either yet another statement missing it's + // enclosing block nor the next arm's pattern or closing brace. + Err(mut stmt_err) => { + stmt_err.cancel(); + *self = start_snapshot; + break; + } + } + } + None + } + pub(super) fn parse_arm(&mut self) -> PResult<'a, Arm> { let attrs = self.parse_outer_attributes()?; self.collect_tokens_trailing_token(attrs, ForceCollect::No, |this, attrs| { let lo = this.token.span; - let pat = this.parse_pat_allow_top_alt(None, GateOr::No, RecoverComma::Yes)?; + let pat = this.parse_pat_allow_top_alt(None, RecoverComma::Yes)?; let guard = if this.eat_keyword(kw::If) { let if_span = this.prev_token.span; let cond = this.parse_expr()?; @@ -2007,6 +2118,21 @@ impl<'a> Parser<'a> { if require_comma { let sm = this.sess.source_map(); + if let Some(body) = this.parse_arm_body_missing_braces(&expr, arrow_span) { + let span = body.span; + return Ok(( + ast::Arm { + attrs, + pat, + guard, + body, + span, + id: DUMMY_NODE_ID, + is_placeholder: false, + }, + TrailingToken::None, + )); + } this.expect_one_of(&[token::Comma], &[token::CloseDelim(token::Brace)]).map_err( |mut err| { match (sm.span_to_lines(expr.span), sm.span_to_lines(arm_start_span)) { @@ -2205,7 +2331,7 @@ impl<'a> Parser<'a> { } let recovery_field = self.find_struct_error_after_field_looking_code(); - let parsed_field = match self.parse_field() { + let parsed_field = match self.parse_expr_field() { Ok(f) => Some(f), Err(mut e) => { if pth == kw::Async { @@ -2262,18 +2388,22 @@ impl<'a> Parser<'a> { let span = pth.span.to(self.token.span); self.expect(&token::CloseDelim(token::Brace))?; - let expr = if recover_async { ExprKind::Err } else { ExprKind::Struct(pth, fields, base) }; + let expr = if recover_async { + ExprKind::Err + } else { + ExprKind::Struct(P(ast::StructExpr { path: pth, fields, rest: base })) + }; Ok(self.mk_expr(span, expr, attrs)) } /// Use in case of error after field-looking code: `S { foo: () with a }`. - fn find_struct_error_after_field_looking_code(&self) -> Option { + fn find_struct_error_after_field_looking_code(&self) -> Option { match self.token.ident() { Some((ident, is_raw)) if (is_raw || !ident.is_reserved()) && self.look_ahead(1, |t| *t == token::Colon) => { - Some(ast::Field { + Some(ast::ExprField { ident, span: self.token.span, expr: self.mk_expr_err(self.token.span), @@ -2307,7 +2437,7 @@ impl<'a> Parser<'a> { } /// Parses `ident (COLON expr)?`. - fn parse_field(&mut self) -> PResult<'a, Field> { + fn parse_expr_field(&mut self) -> PResult<'a, ExprField> { let attrs = self.parse_outer_attributes()?; self.collect_tokens_trailing_token(attrs, ForceCollect::No, |this, attrs| { let lo = this.token.span; @@ -2327,7 +2457,7 @@ impl<'a> Parser<'a> { }; Ok(( - ast::Field { + ast::ExprField { ident, span: lo.to(expr.span), expr, @@ -2451,19 +2581,17 @@ impl<'a> Parser<'a> { attrs: AttrWrapper, f: impl FnOnce(&mut Self, Vec) -> PResult<'a, P>, ) -> PResult<'a, P> { - // FIXME - come up with a nice way to properly forward `ForceCollect`from - // the nonterminal parsing code. TThis approach iscorrect, but will cause - // us to unnecessarily capture tokens for exprs that have only builtin - // attributes. Revisit this before #![feature(stmt_expr_attributes)] is stabilized - let force_collect = if attrs.is_empty() { ForceCollect::No } else { ForceCollect::Yes }; - self.collect_tokens_trailing_token(attrs, force_collect, |this, attrs| { + self.collect_tokens_trailing_token(attrs, ForceCollect::No, |this, attrs| { let res = f(this, attrs)?; let trailing = if this.restrictions.contains(Restrictions::STMT_EXPR) && this.token.kind == token::Semi { TrailingToken::Semi } else { - TrailingToken::None + // FIXME - pass this through from the place where we know + // we need a comma, rather than assuming that `#[attr] expr,` + // always captures a trailing comma + TrailingToken::MaybeComma }; Ok((res, trailing)) }) diff --git a/compiler/rustc_parse/src/parser/item.rs b/compiler/rustc_parse/src/parser/item.rs index 9668a24bf8ab6..299b9a959c56f 100644 --- a/compiler/rustc_parse/src/parser/item.rs +++ b/compiler/rustc_parse/src/parser/item.rs @@ -9,7 +9,7 @@ use rustc_ast::tokenstream::{DelimSpan, TokenStream, TokenTree}; use rustc_ast::{self as ast, AttrVec, Attribute, DUMMY_NODE_ID}; use rustc_ast::{Async, Const, Defaultness, IsAuto, Mutability, Unsafe, UseTree, UseTreeKind}; use rustc_ast::{BindingMode, Block, FnDecl, FnSig, Param, SelfKind}; -use rustc_ast::{EnumDef, Generics, StructField, TraitRef, Ty, TyKind, Variant, VariantData}; +use rustc_ast::{EnumDef, FieldDef, Generics, TraitRef, Ty, TyKind, Variant, VariantData}; use rustc_ast::{FnHeader, ForeignItem, Path, PathSegment, Visibility, VisibilityKind}; use rustc_ast::{MacArgs, MacCall, MacDelimiter}; use rustc_ast_pretty::pprust; @@ -103,20 +103,11 @@ impl<'a> Parser<'a> { // over when we bump the parser if let token::Interpolated(nt) = &self.token.kind { if let token::NtItem(item) = &**nt { - let item = item.clone(); - - return self.collect_tokens_trailing_token( - attrs, - force_collect, - |this, mut attrs| { - let mut item = item; - mem::swap(&mut item.attrs, &mut attrs); - item.attrs.extend(attrs); - // Bump the parser so the we capture the token::Interpolated - this.bump(); - Ok((Some(item.into_inner()), TrailingToken::None)) - }, - ); + let mut item = item.clone(); + self.bump(); + + attrs.prepend_to_nt_inner(&mut item.attrs); + return Ok(Some(item.into_inner())); } }; @@ -204,6 +195,7 @@ impl<'a> Parser<'a> { def: &mut Defaultness, req_name: ReqName, ) -> PResult<'a, Option> { + let def_final = def == &Defaultness::Final; let mut def = || mem::replace(def, Defaultness::Final); let info = if self.eat_keyword(kw::Use) { @@ -226,7 +218,7 @@ impl<'a> Parser<'a> { } (Ident::invalid(), ItemKind::Use(tree)) - } else if self.check_fn_front_matter() { + } else if self.check_fn_front_matter(def_final) { // FUNCTION ITEM let (ident, sig, generics, body) = self.parse_fn(attrs, req_name, lo)?; (ident, ItemKind::Fn(box FnKind(def(), sig, generics, body))) @@ -529,7 +521,7 @@ impl<'a> Parser<'a> { generics.where_clause = self.parse_where_clause()?; - let impl_items = self.parse_item_list(attrs, |p| p.parse_impl_item())?; + let impl_items = self.parse_item_list(attrs, |p| p.parse_impl_item(ForceCollect::No))?; let item_kind = match ty_second { Some(ty_second) => { @@ -717,22 +709,32 @@ impl<'a> Parser<'a> { } else { // It's a normal trait. tps.where_clause = self.parse_where_clause()?; - let items = self.parse_item_list(attrs, |p| p.parse_trait_item())?; + let items = self.parse_item_list(attrs, |p| p.parse_trait_item(ForceCollect::No))?; Ok((ident, ItemKind::Trait(box TraitKind(is_auto, unsafety, tps, bounds, items)))) } } - pub fn parse_impl_item(&mut self) -> PResult<'a, Option>>> { - self.parse_assoc_item(|_| true) + pub fn parse_impl_item( + &mut self, + force_collect: ForceCollect, + ) -> PResult<'a, Option>>> { + self.parse_assoc_item(|_| true, force_collect) } - pub fn parse_trait_item(&mut self) -> PResult<'a, Option>>> { - self.parse_assoc_item(|edition| edition >= Edition::Edition2018) + pub fn parse_trait_item( + &mut self, + force_collect: ForceCollect, + ) -> PResult<'a, Option>>> { + self.parse_assoc_item(|edition| edition >= Edition::Edition2018, force_collect) } /// Parses associated items. - fn parse_assoc_item(&mut self, req_name: ReqName) -> PResult<'a, Option>>> { - Ok(self.parse_item_(req_name, ForceCollect::No)?.map( + fn parse_assoc_item( + &mut self, + req_name: ReqName, + force_collect: ForceCollect, + ) -> PResult<'a, Option>>> { + Ok(self.parse_item_(req_name, force_collect)?.map( |Item { attrs, id, span, vis, ident, kind, tokens }| { let kind = match AssocItemKind::try_from(kind) { Ok(kind) => kind, @@ -917,14 +919,17 @@ impl<'a> Parser<'a> { unsafety: Unsafe, ) -> PResult<'a, ItemInfo> { let abi = self.parse_abi(); // ABI? - let items = self.parse_item_list(attrs, |p| p.parse_foreign_item())?; + let items = self.parse_item_list(attrs, |p| p.parse_foreign_item(ForceCollect::No))?; let module = ast::ForeignMod { unsafety, abi, items }; Ok((Ident::invalid(), ItemKind::ForeignMod(module))) } /// Parses a foreign item (one in an `extern { ... }` block). - pub fn parse_foreign_item(&mut self) -> PResult<'a, Option>>> { - Ok(self.parse_item_(|_| true, ForceCollect::No)?.map( + pub fn parse_foreign_item( + &mut self, + force_collect: ForceCollect, + ) -> PResult<'a, Option>>> { + Ok(self.parse_item_(|_| true, force_collect)?.map( |Item { attrs, id, span, vis, ident, kind, tokens }| { let kind = match ForeignItemKind::try_from(kind) { Ok(kind) => kind, @@ -1231,14 +1236,12 @@ impl<'a> Parser<'a> { Ok((class_name, ItemKind::Union(vdata, generics))) } - fn parse_record_struct_body( - &mut self, - ) -> PResult<'a, (Vec, /* recovered */ bool)> { + fn parse_record_struct_body(&mut self) -> PResult<'a, (Vec, /* recovered */ bool)> { let mut fields = Vec::new(); let mut recovered = false; if self.eat(&token::OpenDelim(token::Brace)) { while self.token != token::CloseDelim(token::Brace) { - let field = self.parse_struct_decl_field().map_err(|e| { + let field = self.parse_field_def().map_err(|e| { self.consume_block(token::Brace, ConsumeClosingDelim::No); recovered = true; e @@ -1263,7 +1266,7 @@ impl<'a> Parser<'a> { Ok((fields, recovered)) } - fn parse_tuple_struct_body(&mut self) -> PResult<'a, Vec> { + fn parse_tuple_struct_body(&mut self) -> PResult<'a, Vec> { // This is the case where we find `struct Foo(T) where T: Copy;` // Unit like structs are handled in parse_item_struct function self.parse_paren_comma_seq(|p| { @@ -1274,7 +1277,7 @@ impl<'a> Parser<'a> { let ty = p.parse_ty()?; Ok(( - StructField { + FieldDef { span: lo.to(ty.span), vis, ident: None, @@ -1291,7 +1294,7 @@ impl<'a> Parser<'a> { } /// Parses an element of a struct declaration. - fn parse_struct_decl_field(&mut self) -> PResult<'a, StructField> { + fn parse_field_def(&mut self) -> PResult<'a, FieldDef> { let attrs = self.parse_outer_attributes()?; self.collect_tokens_trailing_token(attrs, ForceCollect::No, |this, attrs| { let lo = this.token.span; @@ -1306,7 +1309,7 @@ impl<'a> Parser<'a> { lo: Span, vis: Visibility, attrs: Vec, - ) -> PResult<'a, StructField> { + ) -> PResult<'a, FieldDef> { let mut seen_comma: bool = false; let a_var = self.parse_name_and_ty(lo, vis, attrs)?; if self.token == token::Comma { @@ -1398,11 +1401,11 @@ impl<'a> Parser<'a> { lo: Span, vis: Visibility, attrs: Vec, - ) -> PResult<'a, StructField> { + ) -> PResult<'a, FieldDef> { let name = self.parse_ident_common(false)?; self.expect(&token::Colon)?; let ty = self.parse_ty()?; - Ok(StructField { + Ok(FieldDef { span: lo.to(self.prev_token.span), ident: Some(name), vis, @@ -1445,7 +1448,7 @@ impl<'a> Parser<'a> { Ok((ident, ItemKind::MacroDef(ast::MacroDef { body, macro_rules: false }))) } - /// Is this unambiguously the start of a `macro_rules! foo` item defnition? + /// Is this unambiguously the start of a `macro_rules! foo` item definition? fn is_macro_rules_item(&mut self) -> bool { self.check_keyword(kw::MacroRules) && self.look_ahead(1, |t| *t == token::Not) @@ -1636,18 +1639,27 @@ impl<'a> Parser<'a> { } /// Is the current token the start of an `FnHeader` / not a valid parse? - pub(super) fn check_fn_front_matter(&mut self) -> bool { + /// + /// `check_pub` adds additional `pub` to the checks in case users place it + /// wrongly, can be used to ensure `pub` never comes after `default`. + pub(super) fn check_fn_front_matter(&mut self, check_pub: bool) -> bool { // We use an over-approximation here. // `const const`, `fn const` won't parse, but we're not stepping over other syntax either. - const QUALS: [Symbol; 4] = [kw::Const, kw::Async, kw::Unsafe, kw::Extern]; + // `pub` is added in case users got confused with the ordering like `async pub fn`, + // only if it wasn't preceeded by `default` as `default pub` is invalid. + let quals: &[Symbol] = if check_pub { + &[kw::Pub, kw::Const, kw::Async, kw::Unsafe, kw::Extern] + } else { + &[kw::Const, kw::Async, kw::Unsafe, kw::Extern] + }; self.check_keyword(kw::Fn) // Definitely an `fn`. // `$qual fn` or `$qual $qual`: - || QUALS.iter().any(|&kw| self.check_keyword(kw)) + || quals.iter().any(|&kw| self.check_keyword(kw)) && self.look_ahead(1, |t| { // `$qual fn`, e.g. `const fn` or `async fn`. t.is_keyword(kw::Fn) // Two qualifiers `$qual $qual` is enough, e.g. `async unsafe`. - || t.is_non_raw_ident_where(|i| QUALS.contains(&i.name) + || t.is_non_raw_ident_where(|i| quals.contains(&i.name) // Rule out 2015 `const async: T = val`. && i.is_reserved() // Rule out unsafe extern block. @@ -1668,6 +1680,7 @@ impl<'a> Parser<'a> { /// FnFrontMatter = FnQual "fn" ; /// ``` pub(super) fn parse_fn_front_matter(&mut self) -> PResult<'a, FnHeader> { + let sp_start = self.token.span; let constness = self.parse_constness(); let asyncness = self.parse_asyncness(); let unsafety = self.parse_unsafety(); @@ -1681,8 +1694,27 @@ impl<'a> Parser<'a> { // It is possible for `expect_one_of` to recover given the contents of // `self.expected_tokens`, therefore, do not use `self.unexpected()` which doesn't // account for this. - if !self.expect_one_of(&[], &[])? { - unreachable!() + match self.expect_one_of(&[], &[]) { + Ok(true) => {} + Ok(false) => unreachable!(), + Err(mut err) => { + // Recover incorrect visibility order such as `async pub`. + if self.check_keyword(kw::Pub) { + let sp = sp_start.to(self.prev_token.span); + if let Ok(snippet) = self.span_to_snippet(sp) { + let vis = self.parse_visibility(FollowedByType::No)?; + let vs = pprust::vis_to_string(&vis); + let vs = vs.trim_end(); + err.span_suggestion( + sp_start.to(self.prev_token.span), + &format!("visibility `{}` must come before `{}`", vs, snippet), + format!("{} {}", vs, snippet), + Applicability::MachineApplicable, + ); + } + } + return Err(err); + } } } @@ -1757,8 +1789,9 @@ impl<'a> Parser<'a> { let (pat, ty) = if is_name_required || this.is_named_param() { debug!("parse_param_general parse_pat (is_name_required:{})", is_name_required); - let pat = this.parse_fn_param_pat()?; - if let Err(mut err) = this.expect(&token::Colon) { + let (pat, colon) = this.parse_fn_param_pat_colon()?; + if !colon { + let mut err = this.unexpected::<()>().unwrap_err(); return if let Some(ident) = this.parameter_without_type(&mut err, pat, is_name_required, first_param) { diff --git a/compiler/rustc_parse/src/parser/mod.rs b/compiler/rustc_parse/src/parser/mod.rs index 4cc2224d27e42..ed95a5661b18e 100644 --- a/compiler/rustc_parse/src/parser/mod.rs +++ b/compiler/rustc_parse/src/parser/mod.rs @@ -14,18 +14,21 @@ use crate::lexer::UnmatchedBrace; pub use attr_wrapper::AttrWrapper; pub use diagnostics::AttemptLocalParseRecovery; use diagnostics::Error; -pub use pat::{GateOr, RecoverComma}; +pub use pat::RecoverComma; pub use path::PathStyle; use rustc_ast::ptr::P; use rustc_ast::token::{self, DelimToken, Token, TokenKind}; +use rustc_ast::tokenstream::AttributesData; use rustc_ast::tokenstream::{self, DelimSpan, Spacing}; -use rustc_ast::tokenstream::{TokenStream, TokenTree, TreeAndSpacing}; +use rustc_ast::tokenstream::{TokenStream, TokenTree}; +use rustc_ast::AttrId; use rustc_ast::DUMMY_NODE_ID; use rustc_ast::{self as ast, AnonConst, AstLike, AttrStyle, AttrVec, Const, CrateSugar, Extern}; use rustc_ast::{Async, Expr, ExprKind, MacArgs, MacDelimiter, Mutability, StrLit, Unsafe}; use rustc_ast::{Visibility, VisibilityKind}; use rustc_ast_pretty::pprust; +use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::sync::Lrc; use rustc_errors::PResult; use rustc_errors::{struct_span_err, Applicability, DiagnosticBuilder, FatalError}; @@ -34,6 +37,7 @@ use rustc_span::source_map::{Span, DUMMY_SP}; use rustc_span::symbol::{kw, sym, Ident, Symbol}; use tracing::debug; +use std::ops::Range; use std::{cmp, mem, slice}; bitflags::bitflags! { @@ -64,6 +68,7 @@ pub enum ForceCollect { No, } +#[derive(Debug, Eq, PartialEq)] pub enum TrailingToken { None, Semi, @@ -111,6 +116,7 @@ pub struct Parser<'a> { pub token_spacing: Spacing, /// The previous token. pub prev_token: Token, + pub capture_cfg: bool, restrictions: Restrictions, expected_tokens: Vec, // Important: This must only be advanced from `next_tok` @@ -134,6 +140,44 @@ pub struct Parser<'a> { pub last_type_ascription: Option<(Span, bool /* likely path typo */)>, /// If present, this `Parser` is not parsing Rust code but rather a macro call. subparser_name: Option<&'static str>, + capture_state: CaptureState, +} + +/// Indicates a range of tokens that should be replaced by +/// the tokens in the provided vector. This is used in two +/// places during token collection: +/// +/// 1. During the parsing of an AST node that may have a `#[derive]` +/// attribute, we parse a nested AST node that has `#[cfg]` or `#[cfg_attr]` +/// In this case, we use a `ReplaceRange` to replace the entire inner AST node +/// with `FlatToken::AttrTarget`, allowing us to perform eager cfg-expansion +/// on a `AttrAnnotatedTokenStream` +/// +/// 2. When we parse an inner attribute while collecting tokens. We +/// remove inner attributes from the token stream entirely, and +/// instead track them through the `attrs` field on the AST node. +/// This allows us to easily manipulate them (for example, removing +/// the first macro inner attribute to invoke a proc-macro). +/// When create a `TokenStream`, the inner attributes get inserted +/// into the proper place in the token stream. +pub type ReplaceRange = (Range, Vec<(FlatToken, Spacing)>); + +/// Controls how we capture tokens. Capturing can be expensive, +/// so we try to avoid performing capturing in cases where +/// we will never need a `AttrAnnotatedTokenStream` +#[derive(Copy, Clone)] +pub enum Capturing { + /// We aren't performing any capturing - this is the default mode. + No, + /// We are capturing tokens + Yes, +} + +#[derive(Clone)] +struct CaptureState { + capturing: Capturing, + replace_ranges: Vec, + inner_attr_ranges: FxHashMap, } impl<'a> Drop for Parser<'a> { @@ -167,11 +211,11 @@ struct TokenCursor { // want to capture just the first 'unglued' token. // For example, capturing the `Vec` // in `Option>` requires us to unglue - // the trailing `>>` token. The `append_unglued_token` + // the trailing `>>` token. The `break_last_token` // field is used to track this token - it gets // appended to the captured stream when // we evaluate a `LazyTokenStream` - append_unglued_token: Option, + break_last_token: bool, } #[derive(Clone)] @@ -188,9 +232,9 @@ impl TokenCursorFrame { TokenCursorFrame { delim, span, - open_delim: delim == token::NoDelim, + open_delim: false, tree_cursor: tts.into_trees(), - close_delim: delim == token::NoDelim, + close_delim: false, } } } @@ -364,19 +408,24 @@ impl<'a> Parser<'a> { desugar_doc_comments: bool, subparser_name: Option<&'static str>, ) -> Self { + let mut start_frame = TokenCursorFrame::new(DelimSpan::dummy(), token::NoDelim, tokens); + start_frame.open_delim = true; + start_frame.close_delim = true; + let mut parser = Parser { sess, token: Token::dummy(), token_spacing: Spacing::Alone, prev_token: Token::dummy(), + capture_cfg: false, restrictions: Restrictions::empty(), expected_tokens: Vec::new(), token_cursor: TokenCursor { - frame: TokenCursorFrame::new(DelimSpan::dummy(), token::NoDelim, tokens), + frame: start_frame, stack: Vec::new(), num_next_calls: 0, desugar_doc_comments, - append_unglued_token: None, + break_last_token: false, }, desugar_doc_comments, unmatched_angle_bracket_count: 0, @@ -385,6 +434,11 @@ impl<'a> Parser<'a> { last_unexpected_token_span: None, last_type_ascription: None, subparser_name, + capture_state: CaptureState { + capturing: Capturing::No, + replace_ranges: Vec::new(), + inner_attr_ranges: Default::default(), + }, }; // Make parser point to the first token. @@ -394,21 +448,29 @@ impl<'a> Parser<'a> { } fn next_tok(&mut self, fallback_span: Span) -> (Token, Spacing) { - let (mut next, spacing) = if self.desugar_doc_comments { - self.token_cursor.next_desugared() - } else { - self.token_cursor.next() - }; - self.token_cursor.num_next_calls += 1; - // We've retrieved an token from the underlying - // cursor, so we no longer need to worry about - // an unglued token. See `break_and_eat` for more details - self.token_cursor.append_unglued_token = None; - if next.span.is_dummy() { - // Tweak the location for better diagnostics, but keep syntactic context intact. - next.span = fallback_span.with_ctxt(next.span.ctxt()); + loop { + let (mut next, spacing) = if self.desugar_doc_comments { + self.token_cursor.next_desugared() + } else { + self.token_cursor.next() + }; + self.token_cursor.num_next_calls += 1; + // We've retrieved an token from the underlying + // cursor, so we no longer need to worry about + // an unglued token. See `break_and_eat` for more details + self.token_cursor.break_last_token = false; + if next.span.is_dummy() { + // Tweak the location for better diagnostics, but keep syntactic context intact. + next.span = fallback_span.with_ctxt(next.span.ctxt()); + } + if matches!( + next.kind, + token::OpenDelim(token::NoDelim) | token::CloseDelim(token::NoDelim) + ) { + continue; + } + return (next, spacing); } - (next, spacing) } pub fn unexpected(&mut self) -> PResult<'a, T> { @@ -606,8 +668,7 @@ impl<'a> Parser<'a> { // If we consume any additional tokens, then this token // is not needed (we'll capture the entire 'glued' token), // and `next_tok` will set this field to `None` - self.token_cursor.append_unglued_token = - Some((TokenTree::Token(self.token.clone()), Spacing::Alone)); + self.token_cursor.break_last_token = true; // Use the spacing of the glued token as the spacing // of the unglued second token. self.bump_with((Token::new(second, second_span), self.token_spacing)); @@ -688,6 +749,8 @@ impl<'a> Parser<'a> { let mut recovered = false; let mut trailing = false; let mut v = vec![]; + let unclosed_delims = !self.unclosed_delims.is_empty(); + while !self.expect_any_with_type(kets, expect) { if let token::CloseDelim(..) | token::Eof = self.token.kind { break; @@ -708,7 +771,7 @@ impl<'a> Parser<'a> { // Attempt to keep parsing if it was a similar separator. if let Some(ref tokens) = t.similar_tokens() { - if tokens.contains(&self.token.kind) { + if tokens.contains(&self.token.kind) && !unclosed_delims { self.bump(); } } @@ -867,15 +930,38 @@ impl<'a> Parser<'a> { } let frame = &self.token_cursor.frame; - match frame.tree_cursor.look_ahead(dist - 1) { - Some(tree) => match tree { - TokenTree::Token(token) => looker(token), - TokenTree::Delimited(dspan, delim, _) => { - looker(&Token::new(token::OpenDelim(*delim), dspan.open)) - } - }, - None => looker(&Token::new(token::CloseDelim(frame.delim), frame.span.close)), + if frame.delim != DelimToken::NoDelim { + let all_normal = (0..dist).all(|i| { + let token = frame.tree_cursor.look_ahead(i); + !matches!(token, Some(TokenTree::Delimited(_, DelimToken::NoDelim, _))) + }); + if all_normal { + return match frame.tree_cursor.look_ahead(dist - 1) { + Some(tree) => match tree { + TokenTree::Token(token) => looker(token), + TokenTree::Delimited(dspan, delim, _) => { + looker(&Token::new(token::OpenDelim(*delim), dspan.open)) + } + }, + None => looker(&Token::new(token::CloseDelim(frame.delim), frame.span.close)), + }; + } } + + let mut cursor = self.token_cursor.clone(); + let mut i = 0; + let mut token = Token::dummy(); + while i < dist { + token = cursor.next().0; + if matches!( + token.kind, + token::OpenDelim(token::NoDelim) | token::CloseDelim(token::NoDelim) + ) { + continue; + } + i += 1; + } + return looker(&token); } /// Returns whether any of the given keywords are `dist` tokens ahead of the current one. @@ -987,7 +1073,7 @@ impl<'a> Parser<'a> { } // Collect tokens because they are used during lowering to HIR. - let expr = self.collect_tokens_no_attrs(|this| this.parse_expr())?; + let expr = self.parse_expr_force_collect()?; let span = expr.span; match &expr.kind { @@ -1287,3 +1373,24 @@ pub fn emit_unclosed_delims(unclosed_delims: &mut Vec, sess: &Pa } } } + +/// A helper struct used when building a `AttrAnnotatedTokenStream` from +/// a `LazyTokenStream`. Both delimiter and non-delimited tokens +/// are stored as `FlatToken::Token`. A vector of `FlatToken`s +/// is then 'parsed' to build up a `AttrAnnotatedTokenStream` with nested +/// `AttrAnnotatedTokenTree::Delimited` tokens +#[derive(Debug, Clone)] +pub enum FlatToken { + /// A token - this holds both delimiter (e.g. '{' and '}') + /// and non-delimiter tokens + Token(Token), + /// Holds the `AttributesData` for an AST node. The + /// `AttributesData` is inserted directly into the + /// constructed `AttrAnnotatedTokenStream` as + /// a `AttrAnnotatedTokenTree::Attributes` + AttrTarget(AttributesData), + /// A special 'empty' token that is ignored during the conversion + /// to a `AttrAnnotatedTokenStream`. This is used to simplify the + /// handling of replace ranges. + Empty, +} diff --git a/compiler/rustc_parse/src/parser/nonterminal.rs b/compiler/rustc_parse/src/parser/nonterminal.rs index a84ae5151442d..5c4a2785d6e26 100644 --- a/compiler/rustc_parse/src/parser/nonterminal.rs +++ b/compiler/rustc_parse/src/parser/nonterminal.rs @@ -4,7 +4,7 @@ use rustc_ast_pretty::pprust; use rustc_errors::PResult; use rustc_span::symbol::{kw, Ident}; -use crate::parser::pat::{GateOr, RecoverComma}; +use crate::parser::pat::RecoverComma; use crate::parser::{FollowedByType, ForceCollect, Parser, PathStyle}; impl<'a> Parser<'a> { @@ -61,7 +61,7 @@ impl<'a> Parser<'a> { }, _ => false, }, - NonterminalKind::Pat2018 { .. } | NonterminalKind::Pat2021 { .. } => match token.kind { + NonterminalKind::Pat2015 { .. } | NonterminalKind::Pat2021 { .. } => match token.kind { token::Ident(..) | // box, ref, mut, and other identifiers (can stricten) token::OpenDelim(token::Paren) | // tuple pattern token::OpenDelim(token::Bracket) | // slice pattern @@ -118,32 +118,17 @@ impl<'a> Parser<'a> { return Err(self.struct_span_err(self.token.span, "expected a statement")); } }, - NonterminalKind::Pat2018 { .. } | NonterminalKind::Pat2021 { .. } => { + NonterminalKind::Pat2015 { .. } | NonterminalKind::Pat2021 { .. } => { token::NtPat(self.collect_tokens_no_attrs(|this| match kind { - NonterminalKind::Pat2018 { .. } => this.parse_pat_no_top_alt(None), + NonterminalKind::Pat2015 { .. } => this.parse_pat_no_top_alt(None), NonterminalKind::Pat2021 { .. } => { - this.parse_pat_allow_top_alt(None, GateOr::Yes, RecoverComma::No) + this.parse_pat_allow_top_alt(None, RecoverComma::No) } _ => unreachable!(), })?) } - // If there are attributes present, then `parse_expr` will end up collecting tokens, - // turning the outer `collect_tokens_no_attrs` into a no-op due to the already present - // tokens. If there are *not* attributes present, then the outer - // `collect_tokens_no_attrs` will ensure that we will end up collecting tokens for the - // expressions. - // - // This is less efficient than it could be, since the outer `collect_tokens_no_attrs` - // still needs to snapshot the `TokenCursor` before calling `parse_expr`, even when - // `parse_expr` will end up collecting tokens. Ideally, this would work more like - // `parse_item`, and take in a `ForceCollect` parameter. However, this would require - // adding a `ForceCollect` parameter in a bunch of places in expression parsing - // for little gain. If the perf impact from this turns out to be noticeable, we should - // revisit this apporach. - NonterminalKind::Expr => { - token::NtExpr(self.collect_tokens_no_attrs(|this| this.parse_expr())?) - } + NonterminalKind::Expr => token::NtExpr(self.parse_expr_force_collect()?), NonterminalKind::Literal => { // The `:literal` matcher does not support attributes token::NtLiteral( @@ -168,9 +153,7 @@ impl<'a> Parser<'a> { NonterminalKind::Path => token::NtPath( self.collect_tokens_no_attrs(|this| this.parse_path(PathStyle::Type))?, ), - NonterminalKind::Meta => { - token::NtMeta(P(self.collect_tokens_no_attrs(|this| this.parse_attr_item(false))?)) - } + NonterminalKind::Meta => token::NtMeta(P(self.parse_attr_item(true)?)), NonterminalKind::TT => token::NtTT(self.parse_token_tree()), NonterminalKind::Vis => token::NtVis( self.collect_tokens_no_attrs(|this| this.parse_visibility(FollowedByType::Yes))?, diff --git a/compiler/rustc_parse/src/parser/pat.rs b/compiler/rustc_parse/src/parser/pat.rs index 9e2e7359ca96e..0abefbd6a1219 100644 --- a/compiler/rustc_parse/src/parser/pat.rs +++ b/compiler/rustc_parse/src/parser/pat.rs @@ -3,7 +3,7 @@ use crate::{maybe_recover_from_interpolated_ty_qpath, maybe_whole}; use rustc_ast::mut_visit::{noop_visit_pat, MutVisitor}; use rustc_ast::ptr::P; use rustc_ast::token; -use rustc_ast::{self as ast, AttrVec, Attribute, FieldPat, MacCall, Pat, PatKind, RangeEnd}; +use rustc_ast::{self as ast, AttrVec, Attribute, MacCall, Pat, PatField, PatKind, RangeEnd}; use rustc_ast::{BindingMode, Expr, ExprKind, Mutability, Path, QSelf, RangeSyntax}; use rustc_ast_pretty::pprust; use rustc_errors::{struct_span_err, Applicability, DiagnosticBuilder, PResult}; @@ -17,13 +17,6 @@ pub(super) const PARAM_EXPECTED: Expected = Some("parameter name"); const WHILE_PARSING_OR_MSG: &str = "while parsing this or-pattern starting here"; -/// Whether or not an or-pattern should be gated when occurring in the current context. -#[derive(PartialEq, Clone, Copy)] -pub enum GateOr { - Yes, - No, -} - /// Whether or not to recover a `,` when parsing or-patterns. #[derive(PartialEq, Copy, Clone)] pub enum RecoverComma { @@ -31,6 +24,18 @@ pub enum RecoverComma { No, } +/// The result of `eat_or_separator`. We want to distinguish which case we are in to avoid +/// emitting duplicate diagnostics. +#[derive(Debug, Clone, Copy)] +enum EatOrResult { + /// We recovered from a trailing vert. + TrailingVert, + /// We ate an `|` (or `||` and recovered). + AteOr, + /// We did not eat anything (i.e. the current token is not `|` or `||`). + None, +} + impl<'a> Parser<'a> { /// Parses a pattern. /// @@ -52,16 +57,31 @@ impl<'a> Parser<'a> { pub fn parse_pat_allow_top_alt( &mut self, expected: Expected, - gate_or: GateOr, rc: RecoverComma, ) -> PResult<'a, P> { + self.parse_pat_allow_top_alt_inner(expected, rc).map(|(pat, _)| pat) + } + + /// Returns the pattern and a bool indicating whether we recovered from a trailing vert (true = + /// recovered). + fn parse_pat_allow_top_alt_inner( + &mut self, + expected: Expected, + rc: RecoverComma, + ) -> PResult<'a, (P, bool)> { + // Keep track of whether we recovered from a trailing vert so that we can avoid duplicated + // suggestions (which bothers rustfix). + // // Allow a '|' before the pats (RFCs 1925, 2530, and 2535). - let leading_vert_span = - if self.eat_or_separator(None) { Some(self.prev_token.span) } else { None }; + let (leading_vert_span, mut trailing_vert) = match self.eat_or_separator(None) { + EatOrResult::AteOr => (Some(self.prev_token.span), false), + EatOrResult::TrailingVert => (None, true), + EatOrResult::None => (None, false), + }; // Parse the first pattern (`p_0`). let first_pat = self.parse_pat_no_top_alt(expected)?; - self.maybe_recover_unexpected_comma(first_pat.span, rc, gate_or)?; + self.maybe_recover_unexpected_comma(first_pat.span, rc)?; // If the next token is not a `|`, // this is not an or-pattern and we should exit here. @@ -70,46 +90,92 @@ impl<'a> Parser<'a> { // then we should really gate the leading `|`. // This complicated procedure is done purely for diagnostics UX. if let Some(leading_vert_span) = leading_vert_span { - if gate_or == GateOr::Yes && self.sess.gated_spans.is_ungated(sym::or_patterns) { - self.sess.gated_spans.gate(sym::or_patterns, leading_vert_span); - } - // If there was a leading vert, treat this as an or-pattern. This improves // diagnostics. let span = leading_vert_span.to(self.prev_token.span); - return Ok(self.mk_pat(span, PatKind::Or(vec![first_pat]))); + return Ok((self.mk_pat(span, PatKind::Or(vec![first_pat])), trailing_vert)); } - return Ok(first_pat); + return Ok((first_pat, trailing_vert)); } // Parse the patterns `p_1 | ... | p_n` where `n > 0`. let lo = leading_vert_span.unwrap_or(first_pat.span); let mut pats = vec![first_pat]; - while self.eat_or_separator(Some(lo)) { + loop { + match self.eat_or_separator(Some(lo)) { + EatOrResult::AteOr => {} + EatOrResult::None => break, + EatOrResult::TrailingVert => { + trailing_vert = true; + break; + } + } let pat = self.parse_pat_no_top_alt(expected).map_err(|mut err| { err.span_label(lo, WHILE_PARSING_OR_MSG); err })?; - self.maybe_recover_unexpected_comma(pat.span, rc, gate_or)?; + self.maybe_recover_unexpected_comma(pat.span, rc)?; pats.push(pat); } let or_pattern_span = lo.to(self.prev_token.span); - // Feature gate the or-pattern if instructed: - if gate_or == GateOr::Yes { - self.sess.gated_spans.gate(sym::or_patterns, or_pattern_span); + Ok((self.mk_pat(or_pattern_span, PatKind::Or(pats)), trailing_vert)) + } + + /// Parse a pattern and (maybe) a `Colon` in positions where a pattern may be followed by a + /// type annotation (e.g. for `let` bindings or `fn` params). + /// + /// Generally, this corresponds to `pat_no_top_alt` followed by an optional `Colon`. It will + /// eat the `Colon` token if one is present. + /// + /// The return value represents the parsed pattern and `true` if a `Colon` was parsed (`false` + /// otherwise). + pub(super) fn parse_pat_before_ty( + &mut self, + expected: Expected, + rc: RecoverComma, + syntax_loc: &str, + ) -> PResult<'a, (P, bool)> { + // We use `parse_pat_allow_top_alt` regardless of whether we actually want top-level + // or-patterns so that we can detect when a user tries to use it. This allows us to print a + // better error message. + let (pat, trailing_vert) = self.parse_pat_allow_top_alt_inner(expected, rc)?; + let colon = self.eat(&token::Colon); + + if let PatKind::Or(pats) = &pat.kind { + let msg = format!("top-level or-patterns are not allowed in {}", syntax_loc); + let (help, fix) = if pats.len() == 1 { + // If all we have is a leading vert, then print a special message. This is the case + // if `parse_pat_allow_top_alt` returns an or-pattern with one variant. + let msg = "remove the `|`"; + let fix = pprust::pat_to_string(&pat); + (msg, fix) + } else { + let msg = "wrap the pattern in parentheses"; + let fix = format!("({})", pprust::pat_to_string(&pat)); + (msg, fix) + }; + + if trailing_vert { + // We already emitted an error and suggestion to remove the trailing vert. Don't + // emit again. + self.sess.span_diagnostic.delay_span_bug(pat.span, &msg); + } else { + self.struct_span_err(pat.span, &msg) + .span_suggestion(pat.span, help, fix, Applicability::MachineApplicable) + .emit(); + } } - Ok(self.mk_pat(or_pattern_span, PatKind::Or(pats))) + Ok((pat, colon)) } - /// Parse the pattern for a function or function pointer parameter. - pub(super) fn parse_fn_param_pat(&mut self) -> PResult<'a, P> { - // We actually do _not_ allow top-level or-patterns in function params, but we use - // `parse_pat_allow_top_alt` anyway so that we can detect when a user tries to use it. This - // allows us to print a better error message. - // + /// Parse the pattern for a function or function pointer parameter, followed by a colon. + /// + /// The return value represents the parsed pattern and `true` if a `Colon` was parsed (`false` + /// otherwise). + pub(super) fn parse_fn_param_pat_colon(&mut self) -> PResult<'a, (P, bool)> { // In order to get good UX, we first recover in the case of a leading vert for an illegal // top-level or-pat. Normally, this means recovering both `|` and `||`, but in this case, // a leading `||` probably doesn't indicate an or-pattern attempt, so we handle that @@ -128,53 +194,23 @@ impl<'a> Parser<'a> { self.bump(); } - let pat = self.parse_pat_allow_top_alt(PARAM_EXPECTED, GateOr::No, RecoverComma::No)?; - - if let PatKind::Or(..) = &pat.kind { - self.ban_illegal_fn_param_or_pat(&pat); - } - - Ok(pat) - } - - /// Ban `A | B` immediately in a parameter pattern and suggest wrapping in parens. - fn ban_illegal_fn_param_or_pat(&self, pat: &Pat) { - // If all we have a leading vert, then print a special message. This is the case if - // `parse_pat_allow_top_alt` returns an or-pattern with one variant. - let (msg, fix) = match &pat.kind { - PatKind::Or(pats) if pats.len() == 1 => { - let msg = "remove the leading `|`"; - let fix = pprust::pat_to_string(pat); - (msg, fix) - } - - _ => { - let msg = "wrap the pattern in parentheses"; - let fix = format!("({})", pprust::pat_to_string(pat)); - (msg, fix) - } - }; - - self.struct_span_err(pat.span, "an or-pattern parameter must be wrapped in parentheses") - .span_suggestion(pat.span, msg, fix, Applicability::MachineApplicable) - .emit(); + self.parse_pat_before_ty(PARAM_EXPECTED, RecoverComma::No, "function parameters") } /// Eat the or-pattern `|` separator. /// If instead a `||` token is encountered, recover and pretend we parsed `|`. - fn eat_or_separator(&mut self, lo: Option) -> bool { + fn eat_or_separator(&mut self, lo: Option) -> EatOrResult { if self.recover_trailing_vert(lo) { - return false; - } - - match self.token.kind { - token::OrOr => { - // Found `||`; Recover and pretend we parsed `|`. - self.ban_unexpected_or_or(lo); - self.bump(); - true - } - _ => self.eat(&token::BinOp(token::Or)), + EatOrResult::TrailingVert + } else if matches!(self.token.kind, token::OrOr) { + // Found `||`; Recover and pretend we parsed `|`. + self.ban_unexpected_or_or(lo); + self.bump(); + EatOrResult::AteOr + } else if self.eat(&token::BinOp(token::Or)) { + EatOrResult::AteOr + } else { + EatOrResult::None } } @@ -190,14 +226,14 @@ impl<'a> Parser<'a> { matches!( &token.uninterpolate().kind, token::FatArrow // e.g. `a | => 0,`. - | token::Ident(kw::If, false) // e.g. `a | if expr`. - | token::Eq // e.g. `let a | = 0`. - | token::Semi // e.g. `let a |;`. - | token::Colon // e.g. `let a | :`. - | token::Comma // e.g. `let (a |,)`. - | token::CloseDelim(token::Bracket) // e.g. `let [a | ]`. - | token::CloseDelim(token::Paren) // e.g. `let (a | )`. - | token::CloseDelim(token::Brace) // e.g. `let A { f: a | }`. + | token::Ident(kw::If, false) // e.g. `a | if expr`. + | token::Eq // e.g. `let a | = 0`. + | token::Semi // e.g. `let a |;`. + | token::Colon // e.g. `let a | :`. + | token::Comma // e.g. `let (a |,)`. + | token::CloseDelim(token::Bracket) // e.g. `let [a | ]`. + | token::CloseDelim(token::Paren) // e.g. `let (a | )`. + | token::CloseDelim(token::Brace) // e.g. `let A { f: a | }`. ) }); match (is_end_ahead, &self.token.kind) { @@ -227,12 +263,7 @@ impl<'a> Parser<'a> { /// Some special error handling for the "top-level" patterns in a match arm, /// `for` loop, `let`, &c. (in contrast to subpatterns within such). - fn maybe_recover_unexpected_comma( - &mut self, - lo: Span, - rc: RecoverComma, - gate_or: GateOr, - ) -> PResult<'a, ()> { + fn maybe_recover_unexpected_comma(&mut self, lo: Span, rc: RecoverComma) -> PResult<'a, ()> { if rc == RecoverComma::No || self.token != token::Comma { return Ok(()); } @@ -253,22 +284,18 @@ impl<'a> Parser<'a> { if let Ok(seq_snippet) = self.span_to_snippet(seq_span) { const MSG: &str = "try adding parentheses to match on a tuple..."; - let or_suggestion = - gate_or == GateOr::No || !self.sess.gated_spans.is_ungated(sym::or_patterns); err.span_suggestion( seq_span, - if or_suggestion { MSG } else { MSG.trim_end_matches('.') }, + MSG, format!("({})", seq_snippet), Applicability::MachineApplicable, ); - if or_suggestion { - err.span_suggestion( - seq_span, - "...or a vertical bar to match on multiple alternatives", - seq_snippet.replace(",", " |"), - Applicability::MachineApplicable, - ); - } + err.span_suggestion( + seq_span, + "...or a vertical bar to match on multiple alternatives", + seq_snippet.replace(",", " |"), + Applicability::MachineApplicable, + ); } Err(err) } @@ -323,7 +350,7 @@ impl<'a> Parser<'a> { } else if self.check(&token::OpenDelim(token::Bracket)) { // Parse `[pat, pat,...]` as a slice pattern. let (pats, _) = self.parse_delim_comma_seq(token::Bracket, |p| { - p.parse_pat_allow_top_alt(None, GateOr::Yes, RecoverComma::No) + p.parse_pat_allow_top_alt(None, RecoverComma::No) })?; PatKind::Slice(pats) } else if self.check(&token::DotDot) && !self.is_pat_range_end_start(1) { @@ -536,9 +563,8 @@ impl<'a> Parser<'a> { /// Parse a tuple or parenthesis pattern. fn parse_pat_tuple_or_parens(&mut self) -> PResult<'a, PatKind> { - let (fields, trailing_comma) = self.parse_paren_comma_seq(|p| { - p.parse_pat_allow_top_alt(None, GateOr::Yes, RecoverComma::No) - })?; + let (fields, trailing_comma) = + self.parse_paren_comma_seq(|p| p.parse_pat_allow_top_alt(None, RecoverComma::No))?; // Here, `(pat,)` is a tuple pattern. // For backward compatibility, `(..)` is a tuple pattern as well. @@ -851,9 +877,8 @@ impl<'a> Parser<'a> { if qself.is_some() { return self.error_qpath_before_pat(&path, "("); } - let (fields, _) = self.parse_paren_comma_seq(|p| { - p.parse_pat_allow_top_alt(None, GateOr::Yes, RecoverComma::No) - })?; + let (fields, _) = + self.parse_paren_comma_seq(|p| p.parse_pat_allow_top_alt(None, RecoverComma::No))?; Ok(PatKind::TupleStruct(path, fields)) } @@ -868,7 +893,7 @@ impl<'a> Parser<'a> { } /// Parses the fields of a struct-like pattern. - fn parse_pat_fields(&mut self) -> PResult<'a, (Vec, bool)> { + fn parse_pat_fields(&mut self) -> PResult<'a, (Vec, bool)> { let mut fields = Vec::new(); let mut etc = false; let mut ate_comma = true; @@ -1012,14 +1037,14 @@ impl<'a> Parser<'a> { .emit(); } - fn parse_pat_field(&mut self, lo: Span, attrs: Vec) -> PResult<'a, FieldPat> { + fn parse_pat_field(&mut self, lo: Span, attrs: Vec) -> PResult<'a, PatField> { // Check if a colon exists one ahead. This means we're parsing a fieldname. let hi; let (subpat, fieldname, is_shorthand) = if self.look_ahead(1, |t| t == &token::Colon) { // Parsing a pattern of the form `fieldname: pat`. let fieldname = self.parse_field_name()?; self.bump(); - let pat = self.parse_pat_allow_top_alt(None, GateOr::Yes, RecoverComma::No)?; + let pat = self.parse_pat_allow_top_alt(None, RecoverComma::No)?; hi = pat.span; (pat, fieldname, false) } else { @@ -1044,7 +1069,7 @@ impl<'a> Parser<'a> { (subpat, fieldname, true) }; - Ok(FieldPat { + Ok(PatField { ident: fieldname, pat: subpat, is_shorthand, diff --git a/compiler/rustc_parse/src/parser/stmt.rs b/compiler/rustc_parse/src/parser/stmt.rs index 07746f2390dd9..592f64f4a399f 100644 --- a/compiler/rustc_parse/src/parser/stmt.rs +++ b/compiler/rustc_parse/src/parser/stmt.rs @@ -1,7 +1,7 @@ use super::attr::DEFAULT_INNER_ATTR_FORBIDDEN; use super::diagnostics::{AttemptLocalParseRecovery, Error}; use super::expr::LhsExpr; -use super::pat::{GateOr, RecoverComma}; +use super::pat::RecoverComma; use super::path::PathStyle; use super::TrailingToken; use super::{AttrWrapper, BlockMode, ForceCollect, Parser, Restrictions, SemiColonMode}; @@ -13,7 +13,8 @@ use rustc_ast::token::{self, TokenKind}; use rustc_ast::util::classify; use rustc_ast::AstLike; use rustc_ast::{AttrStyle, AttrVec, Attribute, MacCall, MacCallStmt, MacStmtStyle}; -use rustc_ast::{Block, BlockCheckMode, Expr, ExprKind, Local, Stmt, StmtKind, DUMMY_NODE_ID}; +use rustc_ast::{Block, BlockCheckMode, Expr, ExprKind, Local, Stmt}; +use rustc_ast::{StmtKind, DUMMY_NODE_ID}; use rustc_errors::{Applicability, PResult}; use rustc_span::source_map::{BytePos, Span}; use rustc_span::symbol::{kw, sym}; @@ -34,7 +35,7 @@ impl<'a> Parser<'a> { /// If `force_capture` is true, forces collection of tokens regardless of whether /// or not we have attributes - fn parse_stmt_without_recovery( + crate fn parse_stmt_without_recovery( &mut self, capture_semi: bool, force_collect: ForceCollect, @@ -47,39 +48,26 @@ impl<'a> Parser<'a> { if let token::Interpolated(nt) = &self.token.kind { if let token::NtStmt(stmt) = &**nt { let mut stmt = stmt.clone(); - return self.collect_tokens_trailing_token( - attrs, - force_collect, - |this, mut attrs| { - stmt.visit_attrs(|stmt_attrs| { - mem::swap(stmt_attrs, &mut attrs); - stmt_attrs.extend(attrs); - }); - // Make sure we capture the token::Interpolated - this.bump(); - Ok((Some(stmt), TrailingToken::None)) - }, - ); + self.bump(); + stmt.visit_attrs(|stmt_attrs| { + attrs.prepend_to_nt_inner(stmt_attrs); + }); + return Ok(Some(stmt)); } } Ok(Some(if self.token.is_keyword(kw::Let) { self.parse_local_mk(lo, attrs, capture_semi, force_collect)? } else if self.is_kw_followed_by_ident(kw::Mut) { - self.recover_stmt_local( - lo, - attrs.take_for_recovery().into(), - "missing keyword", - "let mut", - )? + self.recover_stmt_local(lo, attrs, "missing keyword", "let mut")? } else if self.is_kw_followed_by_ident(kw::Auto) { self.bump(); // `auto` let msg = "write `let` instead of `auto` to introduce a new variable"; - self.recover_stmt_local(lo, attrs.take_for_recovery().into(), msg, "let")? + self.recover_stmt_local(lo, attrs, msg, "let")? } else if self.is_kw_followed_by_ident(sym::var) { self.bump(); // `var` let msg = "write `let` instead of `var` to introduce a new variable"; - self.recover_stmt_local(lo, attrs.take_for_recovery().into(), msg, "let")? + self.recover_stmt_local(lo, attrs, msg, "let")? } else if self.check_path() && !self.token.is_qpath_start() && !self.is_path_start_item() { // We have avoided contextual keywords like `union`, items with `crate` visibility, // or `auto trait` items. We aim to parse an arbitrary path `a::b` but not something @@ -111,7 +99,7 @@ impl<'a> Parser<'a> { attrs: AttrWrapper, force_collect: ForceCollect, ) -> PResult<'a, Stmt> { - self.collect_tokens_trailing_token(attrs, force_collect, |this, attrs| { + let stmt = self.collect_tokens_trailing_token(attrs, force_collect, |this, attrs| { let path = this.parse_path(PathStyle::Expr)?; if this.eat(&token::Not) { @@ -131,14 +119,22 @@ impl<'a> Parser<'a> { }; let expr = this.with_res(Restrictions::STMT_EXPR, |this| { - let expr = this.parse_dot_or_call_expr_with(expr, lo, attrs)?; + this.parse_dot_or_call_expr_with(expr, lo, attrs) + })?; + // `DUMMY_SP` will get overwritten later in this function + Ok((this.mk_stmt(rustc_span::DUMMY_SP, StmtKind::Expr(expr)), TrailingToken::None)) + })?; + + if let StmtKind::Expr(expr) = stmt.kind { + // Perform this outside of the `collect_tokens_trailing_token` closure, + // since our outer attributes do not apply to this part of the expression + let expr = self.with_res(Restrictions::STMT_EXPR, |this| { this.parse_assoc_expr_with(0, LhsExpr::AlreadyParsed(expr)) })?; - Ok(( - this.mk_stmt(lo.to(this.prev_token.span), StmtKind::Expr(expr)), - TrailingToken::None, - )) - }) + Ok(self.mk_stmt(lo.to(self.prev_token.span), StmtKind::Expr(expr))) + } else { + Ok(stmt) + } } /// Parses a statement macro `mac!(args)` provided a `path` representing `mac`. @@ -182,7 +178,7 @@ impl<'a> Parser<'a> { fn recover_stmt_local( &mut self, lo: Span, - attrs: AttrVec, + attrs: AttrWrapper, msg: &str, sugg: &str, ) -> PResult<'a, Stmt> { @@ -212,17 +208,23 @@ impl<'a> Parser<'a> { }) } - fn recover_local_after_let(&mut self, lo: Span, attrs: AttrVec) -> PResult<'a, Stmt> { - let local = self.parse_local(attrs)?; - Ok(self.mk_stmt(lo.to(self.prev_token.span), StmtKind::Local(local))) + fn recover_local_after_let(&mut self, lo: Span, attrs: AttrWrapper) -> PResult<'a, Stmt> { + self.collect_tokens_trailing_token(attrs, ForceCollect::No, |this, attrs| { + let local = this.parse_local(attrs.into())?; + // FIXME - maybe capture semicolon in recovery? + Ok(( + this.mk_stmt(lo.to(this.prev_token.span), StmtKind::Local(local)), + TrailingToken::None, + )) + }) } /// Parses a local variable declaration. fn parse_local(&mut self, attrs: AttrVec) -> PResult<'a, P> { let lo = self.prev_token.span; - let pat = self.parse_pat_allow_top_alt(None, GateOr::Yes, RecoverComma::Yes)?; + let (pat, colon) = self.parse_pat_before_ty(None, RecoverComma::Yes, "`let` bindings")?; - let (err, ty) = if self.eat(&token::Colon) { + let (err, ty) = if colon { // Save the state of the parser before parsing type normally, in case there is a `:` // instead of an `=` typo. let parser_snapshot_before_type = self.clone(); @@ -289,7 +291,7 @@ impl<'a> Parser<'a> { Ok(P(ast::Local { ty, pat, init, id: DUMMY_NODE_ID, span: lo.to(hi), attrs, tokens: None })) } - /// Parses the RHS of a local variable declaration (e.g., '= 14;'). + /// Parses the RHS of a local variable declaration (e.g., `= 14;`). fn parse_initializer(&mut self, eq_optional: bool) -> PResult<'a, Option>> { let eq_consumed = match self.token.kind { token::BinOpEq(..) => { @@ -304,6 +306,7 @@ impl<'a> Parser<'a> { "=".to_string(), Applicability::MaybeIncorrect, ) + .help("if you meant to overwrite, remove the `let` binding") .emit(); self.bump(); true diff --git a/compiler/rustc_parse/src/parser/ty.rs b/compiler/rustc_parse/src/parser/ty.rs index 8f03bfd4c3ab7..0f7b8ebd376b9 100644 --- a/compiler/rustc_parse/src/parser/ty.rs +++ b/compiler/rustc_parse/src/parser/ty.rs @@ -209,7 +209,7 @@ impl<'a> Parser<'a> { } else if self.eat_keyword(kw::Underscore) { // A type to be inferred `_` TyKind::Infer - } else if self.check_fn_front_matter() { + } else if self.check_fn_front_matter(false) { // Function pointer type self.parse_ty_bare_fn(lo, Vec::new(), recover_return_sign)? } else if self.check_keyword(kw::For) { @@ -217,7 +217,7 @@ impl<'a> Parser<'a> { // `for<'lt> [unsafe] [extern "ABI"] fn (&'lt S) -> T` // `for<'lt> Trait1<'lt> + Trait2 + 'a` let lifetime_defs = self.parse_late_bound_lifetime_defs()?; - if self.check_fn_front_matter() { + if self.check_fn_front_matter(false) { self.parse_ty_bare_fn(lo, lifetime_defs, recover_return_sign)? } else { let path = self.parse_path(PathStyle::Type)?; diff --git a/compiler/rustc_parse_format/src/lib.rs b/compiler/rustc_parse_format/src/lib.rs index 92d974690b514..50db69f420991 100644 --- a/compiler/rustc_parse_format/src/lib.rs +++ b/compiler/rustc_parse_format/src/lib.rs @@ -10,7 +10,7 @@ test(attr(deny(warnings))) )] #![feature(nll)] -#![feature(or_patterns)] +#![cfg_attr(bootstrap, feature(or_patterns))] #![feature(bool_to_option)] pub use Alignment::*; @@ -213,11 +213,13 @@ impl<'a> Iterator for Parser<'a> { Some(String(self.string(pos + 1))) } else { let arg = self.argument(); - if let Some(end) = self.must_consume('}') { - let start = self.to_span_index(pos); - let end = self.to_span_index(end + 1); + if let Some(rbrace_byte_idx) = self.must_consume('}') { + let lbrace_inner_offset = self.to_span_index(pos); + let rbrace_inner_offset = self.to_span_index(rbrace_byte_idx); if self.is_literal { - self.arg_places.push(start.to(end)); + self.arg_places.push( + lbrace_inner_offset.to(InnerOffset(rbrace_inner_offset.0 + 1)), + ); } } Some(NextArgument(arg)) @@ -735,25 +737,24 @@ fn find_skips_from_snippet( }; fn find_skips(snippet: &str, is_raw: bool) -> Vec { - let mut eat_ws = false; let mut s = snippet.char_indices().peekable(); let mut skips = vec![]; while let Some((pos, c)) = s.next() { match (c, s.peek()) { // skip whitespace and empty lines ending in '\\' ('\\', Some((next_pos, '\n'))) if !is_raw => { - eat_ws = true; skips.push(pos); skips.push(*next_pos); let _ = s.next(); - } - ('\\', Some((next_pos, '\n' | 'n' | 't'))) if eat_ws => { - skips.push(pos); - skips.push(*next_pos); - let _ = s.next(); - } - (' ' | '\n' | '\t', _) if eat_ws => { - skips.push(pos); + + while let Some((pos, c)) = s.peek() { + if matches!(c, ' ' | '\n' | '\t') { + skips.push(*pos); + let _ = s.next(); + } else { + break; + } + } } ('\\', Some((next_pos, 'n' | 't' | 'r' | '0' | '\\' | '\'' | '\"'))) => { skips.push(*next_pos); @@ -804,10 +805,6 @@ fn find_skips_from_snippet( } } } - _ if eat_ws => { - // `take_while(|c| c.is_whitespace())` - eat_ws = false; - } _ => {} } } diff --git a/compiler/rustc_passes/Cargo.toml b/compiler/rustc_passes/Cargo.toml index c87799f1c2acc..4069fb2127e02 100644 --- a/compiler/rustc_passes/Cargo.toml +++ b/compiler/rustc_passes/Cargo.toml @@ -19,3 +19,4 @@ rustc_serialize = { path = "../rustc_serialize" } rustc_span = { path = "../rustc_span" } rustc_trait_selection = { path = "../rustc_trait_selection" } rustc_lexer = { path = "../rustc_lexer" } +rustc_ast_pretty = { path = "../rustc_ast_pretty" } diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index 39245ea77e52c..d57cba6420f7b 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -8,8 +8,8 @@ use rustc_middle::hir::map::Map; use rustc_middle::ty::query::Providers; use rustc_middle::ty::TyCtxt; -use rustc_ast::{Attribute, LitKind, NestedMetaItem}; -use rustc_errors::{pluralize, struct_span_err}; +use rustc_ast::{Attribute, Lit, LitKind, NestedMetaItem}; +use rustc_errors::{pluralize, struct_span_err, Applicability}; use rustc_hir as hir; use rustc_hir::def_id::LocalDefId; use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor}; @@ -17,7 +17,9 @@ use rustc_hir::{ self, FnSig, ForeignItem, ForeignItemKind, HirId, Item, ItemKind, TraitItem, CRATE_HIR_ID, }; use rustc_hir::{MethodKind, Target}; -use rustc_session::lint::builtin::{CONFLICTING_REPR_HINTS, UNUSED_ATTRIBUTES}; +use rustc_session::lint::builtin::{ + CONFLICTING_REPR_HINTS, INVALID_DOC_ATTRIBUTES, UNUSED_ATTRIBUTES, +}; use rustc_session::parse::feature_err; use rustc_span::symbol::{sym, Symbol}; use rustc_span::{Span, DUMMY_SP}; @@ -60,52 +62,55 @@ impl CheckAttrVisitor<'tcx> { fn check_attributes( &self, hir_id: HirId, - attrs: &'hir [Attribute], span: &Span, target: Target, item: Option>, ) { let mut is_valid = true; + let attrs = self.tcx.hir().attrs(hir_id); for attr in attrs { - is_valid &= if self.tcx.sess.check_name(attr, sym::inline) { - self.check_inline(hir_id, attr, span, target) - } else if self.tcx.sess.check_name(attr, sym::non_exhaustive) { - self.check_non_exhaustive(hir_id, attr, span, target) - } else if self.tcx.sess.check_name(attr, sym::marker) { - self.check_marker(hir_id, attr, span, target) - } else if self.tcx.sess.check_name(attr, sym::target_feature) { - self.check_target_feature(hir_id, attr, span, target) - } else if self.tcx.sess.check_name(attr, sym::track_caller) { - self.check_track_caller(hir_id, &attr.span, attrs, span, target) - } else if self.tcx.sess.check_name(attr, sym::doc) { - self.check_doc_attrs(attr, hir_id, target) - } else if self.tcx.sess.check_name(attr, sym::no_link) { - self.check_no_link(hir_id, &attr, span, target) - } else if self.tcx.sess.check_name(attr, sym::export_name) { - self.check_export_name(hir_id, &attr, span, target) - } else if self.tcx.sess.check_name(attr, sym::rustc_args_required_const) { - self.check_rustc_args_required_const(&attr, span, target, item) - } else if self.tcx.sess.check_name(attr, sym::allow_internal_unstable) { - self.check_allow_internal_unstable(hir_id, &attr, span, target, &attrs) - } else if self.tcx.sess.check_name(attr, sym::rustc_allow_const_fn_unstable) { - self.check_rustc_allow_const_fn_unstable(hir_id, &attr, span, target) - } else if self.tcx.sess.check_name(attr, sym::naked) { - self.check_naked(hir_id, attr, span, target) - } else if self.tcx.sess.check_name(attr, sym::rustc_legacy_const_generics) { - self.check_rustc_legacy_const_generics(&attr, span, target, item) - } else { - // lint-only checks - if self.tcx.sess.check_name(attr, sym::cold) { - self.check_cold(hir_id, attr, span, target); - } else if self.tcx.sess.check_name(attr, sym::link_name) { - self.check_link_name(hir_id, attr, span, target); - } else if self.tcx.sess.check_name(attr, sym::link_section) { - self.check_link_section(hir_id, attr, span, target); - } else if self.tcx.sess.check_name(attr, sym::no_mangle) { - self.check_no_mangle(hir_id, attr, span, target); + is_valid &= match attr.name_or_empty() { + sym::inline => self.check_inline(hir_id, attr, span, target), + sym::non_exhaustive => self.check_non_exhaustive(hir_id, attr, span, target), + sym::marker => self.check_marker(hir_id, attr, span, target), + sym::target_feature => self.check_target_feature(hir_id, attr, span, target), + sym::track_caller => { + self.check_track_caller(hir_id, &attr.span, attrs, span, target) } - true + sym::doc => self.check_doc_attrs(attr, hir_id, target), + sym::no_link => self.check_no_link(hir_id, &attr, span, target), + sym::export_name => self.check_export_name(hir_id, &attr, span, target), + sym::rustc_args_required_const => { + self.check_rustc_args_required_const(&attr, span, target, item) + } + sym::rustc_layout_scalar_valid_range_start + | sym::rustc_layout_scalar_valid_range_end => { + self.check_rustc_layout_scalar_valid_range(&attr, span, target) + } + sym::allow_internal_unstable => { + self.check_allow_internal_unstable(hir_id, &attr, span, target, &attrs) + } + sym::rustc_allow_const_fn_unstable => { + self.check_rustc_allow_const_fn_unstable(hir_id, &attr, span, target) + } + sym::naked => self.check_naked(hir_id, attr, span, target), + sym::rustc_legacy_const_generics => { + self.check_rustc_legacy_const_generics(&attr, span, target, item) + } + sym::rustc_clean + | sym::rustc_dirty + | sym::rustc_if_this_changed + | sym::rustc_then_this_would_need => self.check_rustc_dirty_clean(&attr), + _ => true, }; + // lint-only checks + match attr.name_or_empty() { + sym::cold => self.check_cold(hir_id, attr, span, target), + sym::link_name => self.check_link_name(hir_id, attr, span, target), + sym::link_section => self.check_link_section(hir_id, attr, span, target), + sym::no_mangle => self.check_no_mangle(hir_id, attr, span, target), + _ => {} + } } if !is_valid { @@ -388,33 +393,50 @@ impl CheckAttrVisitor<'tcx> { .emit(); } - fn check_doc_alias(&self, meta: &NestedMetaItem, hir_id: HirId, target: Target) -> bool { - let doc_alias = meta.value_str().map(|s| s.to_string()).unwrap_or_else(String::new); + fn check_doc_alias_value( + &self, + meta: &NestedMetaItem, + doc_alias: &str, + hir_id: HirId, + target: Target, + is_list: bool, + ) -> bool { + let tcx = self.tcx; + let err_fn = move |span: Span, msg: &str| { + tcx.sess.span_err( + span, + &format!( + "`#[doc(alias{})]` {}", + if is_list { "(\"...\")" } else { " = \"...\"" }, + msg, + ), + ); + false + }; if doc_alias.is_empty() { - self.doc_attr_str_error(meta, "alias"); - return false; + return err_fn( + meta.name_value_literal_span().unwrap_or_else(|| meta.span()), + "attribute cannot have empty value", + ); } if let Some(c) = doc_alias.chars().find(|&c| c == '"' || c == '\'' || (c.is_whitespace() && c != ' ')) { - self.tcx - .sess - .struct_span_err( - meta.name_value_literal_span().unwrap_or_else(|| meta.span()), - &format!("{:?} character isn't allowed in `#[doc(alias = \"...\")]`", c), - ) - .emit(); + self.tcx.sess.span_err( + meta.name_value_literal_span().unwrap_or_else(|| meta.span()), + &format!( + "{:?} character isn't allowed in `#[doc(alias{})]`", + c, + if is_list { "(\"...\")" } else { " = \"...\"" }, + ), + ); return false; } if doc_alias.starts_with(' ') || doc_alias.ends_with(' ') { - self.tcx - .sess - .struct_span_err( - meta.name_value_literal_span().unwrap_or_else(|| meta.span()), - "`#[doc(alias = \"...\")]` cannot start or end with ' '", - ) - .emit(); - return false; + return err_fn( + meta.name_value_literal_span().unwrap_or_else(|| meta.span()), + "cannot start or end with ' '", + ); } if let Some(err) = match target { Target::Impl => Some("implementation block"), @@ -440,27 +462,63 @@ impl CheckAttrVisitor<'tcx> { } _ => None, } { - self.tcx - .sess - .struct_span_err( - meta.span(), - &format!("`#[doc(alias = \"...\")]` isn't allowed on {}", err), - ) - .emit(); - return false; + return err_fn(meta.span(), &format!("isn't allowed on {}", err)); } let item_name = self.tcx.hir().name(hir_id); if &*item_name.as_str() == doc_alias { + return err_fn(meta.span(), "is the same as the item's name"); + } + true + } + + fn check_doc_alias(&self, meta: &NestedMetaItem, hir_id: HirId, target: Target) -> bool { + if let Some(values) = meta.meta_item_list() { + let mut errors = 0; + for v in values { + match v.literal() { + Some(l) => match l.kind { + LitKind::Str(s, _) => { + if !self.check_doc_alias_value(v, &s.as_str(), hir_id, target, true) { + errors += 1; + } + } + _ => { + self.tcx + .sess + .struct_span_err( + v.span(), + "`#[doc(alias(\"a\"))]` expects string literals", + ) + .emit(); + errors += 1; + } + }, + None => { + self.tcx + .sess + .struct_span_err( + v.span(), + "`#[doc(alias(\"a\"))]` expects string literals", + ) + .emit(); + errors += 1; + } + } + } + errors == 0 + } else if let Some(doc_alias) = meta.value_str().map(|s| s.to_string()) { + self.check_doc_alias_value(meta, &doc_alias, hir_id, target, false) + } else { self.tcx .sess .struct_span_err( meta.span(), - &format!("`#[doc(alias = \"...\")]` is the same as the item's name"), + "doc alias attribute expects a string `#[doc(alias = \"a\")]` or a list of \ + strings `#[doc(alias(\"a\", \"b\"))]`", ) .emit(); - return false; + false } - true } fn check_doc_keyword(&self, meta: &NestedMetaItem, hir_id: HirId) -> bool { @@ -518,7 +576,7 @@ impl CheckAttrVisitor<'tcx> { .struct_span_err( meta.span(), &format!( - "`#![doc({} = \"...\")]` isn't allowed as a crate level attribute", + "`#![doc({} = \"...\")]` isn't allowed as a crate-level attribute", attr_name, ), ) @@ -529,68 +587,109 @@ impl CheckAttrVisitor<'tcx> { } fn check_doc_attrs(&self, attr: &Attribute, hir_id: HirId, target: Target) -> bool { - if let Some(mi) = attr.meta() { - if let Some(list) = mi.meta_item_list() { - for meta in list { - if meta.has_name(sym::alias) { - if !self.check_attr_crate_level(meta, hir_id, "alias") - || !self.check_doc_alias(meta, hir_id, target) + let mut is_valid = true; + + if let Some(list) = attr.meta().and_then(|mi| mi.meta_item_list().map(|l| l.to_vec())) { + for meta in list { + if let Some(i_meta) = meta.meta_item() { + match i_meta.name_or_empty() { + sym::alias + if !self.check_attr_crate_level(&meta, hir_id, "alias") + || !self.check_doc_alias(&meta, hir_id, target) => { - return false; + is_valid = false } - } else if meta.has_name(sym::keyword) { - if !self.check_attr_crate_level(meta, hir_id, "keyword") - || !self.check_doc_keyword(meta, hir_id) + + sym::keyword + if !self.check_attr_crate_level(&meta, hir_id, "keyword") + || !self.check_doc_keyword(&meta, hir_id) => { - return false; + is_valid = false } - } else if let Some(i_meta) = meta.meta_item() { - if ![ - sym::cfg, - sym::hidden, - sym::html_favicon_url, - sym::html_logo_url, - sym::html_no_source, - sym::html_playground_url, - sym::html_root_url, - sym::include, - sym::inline, - sym::issue_tracker_base_url, - sym::masked, - sym::no_default_passes, // deprecated - sym::no_inline, - sym::passes, // deprecated - sym::primitive, - sym::spotlight, - sym::test, - ] - .iter() - .any(|m| i_meta.has_name(*m)) - { + + sym::test if CRATE_HIR_ID != hir_id => { self.tcx.struct_span_lint_hir( - UNUSED_ATTRIBUTES, + INVALID_DOC_ATTRIBUTES, hir_id, - i_meta.span, + meta.span(), |lint| { - lint.build(&format!( - "unknown `doc` attribute `{}`", - i_meta.name_or_empty() - )) - .warn( - "this was previously accepted by the compiler but is \ - being phased out; it will become a hard error in \ - a future release!", + lint.build( + "`#![doc(test(...)]` is only allowed \ + as a crate-level attribute", ) .emit(); }, ); - return false; + is_valid = false; + } + + // no_default_passes: deprecated + // passes: deprecated + // plugins: removed, but rustdoc warns about it itself + sym::alias + | sym::cfg + | sym::hidden + | sym::html_favicon_url + | sym::html_logo_url + | sym::html_no_source + | sym::html_playground_url + | sym::html_root_url + | sym::include + | sym::inline + | sym::issue_tracker_base_url + | sym::keyword + | sym::masked + | sym::no_default_passes + | sym::no_inline + | sym::notable_trait + | sym::passes + | sym::plugins + | sym::primitive + | sym::test => {} + + _ => { + self.tcx.struct_span_lint_hir( + INVALID_DOC_ATTRIBUTES, + hir_id, + i_meta.span, + |lint| { + let mut diag = lint.build(&format!( + "unknown `doc` attribute `{}`", + rustc_ast_pretty::pprust::path_to_string(&i_meta.path), + )); + if i_meta.has_name(sym::spotlight) { + diag.note( + "`doc(spotlight)` was renamed to `doc(notable_trait)`", + ); + diag.span_suggestion_short( + i_meta.span, + "use `notable_trait` instead", + String::from("notable_trait"), + Applicability::MachineApplicable, + ); + diag.note("`doc(spotlight)` is now a no-op"); + } + diag.emit(); + }, + ); + is_valid = false; } } + } else { + self.tcx.struct_span_lint_hir( + INVALID_DOC_ATTRIBUTES, + hir_id, + meta.span(), + |lint| { + lint.build(&format!("invalid `doc` attribute")).emit(); + }, + ); + is_valid = false; } } } - true + + is_valid } /// Checks if `#[cold]` is applied to a non-function. Returns `true` if valid. @@ -794,6 +893,37 @@ impl CheckAttrVisitor<'tcx> { } } + fn check_rustc_layout_scalar_valid_range( + &self, + attr: &Attribute, + span: &Span, + target: Target, + ) -> bool { + if target != Target::Struct { + self.tcx + .sess + .struct_span_err(attr.span, "attribute should be applied to a struct") + .span_label(*span, "not a struct") + .emit(); + return false; + } + + let list = match attr.meta_item_list() { + None => return false, + Some(it) => it, + }; + + if matches!(&list[..], &[NestedMetaItem::Literal(Lit { kind: LitKind::Int(..), .. })]) { + true + } else { + self.tcx + .sess + .struct_span_err(attr.span, "expected exactly one integer literal argument") + .emit(); + false + } + } + /// Checks if `#[rustc_legacy_const_generics]` is applied to a function and has a valid argument. fn check_rustc_legacy_const_generics( &self, @@ -893,6 +1023,20 @@ impl CheckAttrVisitor<'tcx> { } } + /// Checks that the dep-graph debugging attributes are only present when the query-dep-graph + /// option is passed to the compiler. + fn check_rustc_dirty_clean(&self, attr: &Attribute) -> bool { + if self.tcx.sess.opts.debugging_opts.query_dep_graph { + true + } else { + self.tcx + .sess + .struct_span_err(attr.span, "attribute requires -Z query-dep-graph to be enabled") + .emit(); + false + } + } + /// Checks if `#[link_section]` is applied to a function or static. fn check_link_section(&self, hir_id: HirId, attr: &Attribute, span: &Span, target: Target) { match target { @@ -965,7 +1109,7 @@ impl CheckAttrVisitor<'tcx> { // ``` let hints: Vec<_> = attrs .iter() - .filter(|attr| self.tcx.sess.check_name(attr, sym::repr)) + .filter(|attr| attr.has_name(sym::repr)) .filter_map(|attr| attr.meta_item_list()) .flatten() .collect(); @@ -976,17 +1120,41 @@ impl CheckAttrVisitor<'tcx> { let mut is_transparent = false; for hint in &hints { + if !hint.is_meta_item() { + struct_span_err!( + self.tcx.sess, + hint.span(), + E0565, + "meta item in `repr` must be an identifier" + ) + .emit(); + continue; + } + let (article, allowed_targets) = match hint.name_or_empty() { - _ if !matches!(target, Target::Struct | Target::Enum | Target::Union) => { - ("a", "struct, enum, or union") - } - name @ sym::C | name @ sym::align => { - is_c |= name == sym::C; + sym::C => { + is_c = true; match target { Target::Struct | Target::Union | Target::Enum => continue, _ => ("a", "struct, enum, or union"), } } + sym::align => { + if let (Target::Fn, true) = (target, !self.tcx.features().fn_align) { + feature_err( + &self.tcx.sess.parse_sess, + sym::fn_align, + hint.span(), + "`repr(align)` attributes on functions are unstable", + ) + .emit(); + } + + match target { + Target::Struct | Target::Union | Target::Enum | Target::Fn => continue, + _ => ("a", "struct, enum, function, or union"), + } + } sym::packed => { if target != Target::Struct && target != Target::Union { ("a", "struct or union") @@ -1043,7 +1211,17 @@ impl CheckAttrVisitor<'tcx> { continue; } } - _ => continue, + _ => { + struct_span_err!( + self.tcx.sess, + hint.span(), + E0552, + "unrecognized representation hint" + ) + .emit(); + + continue; + } }; struct_span_err!( @@ -1102,7 +1280,7 @@ impl CheckAttrVisitor<'tcx> { fn check_used(&self, attrs: &'hir [Attribute], target: Target) { for attr in attrs { - if self.tcx.sess.check_name(attr, sym::used) && target != Target::Static { + if attr.has_name(sym::used) && target != Target::Static { self.tcx .sess .span_err(attr.span, "attribute must be applied to a `static` variable"); @@ -1200,53 +1378,29 @@ impl Visitor<'tcx> for CheckAttrVisitor<'tcx> { fn visit_item(&mut self, item: &'tcx Item<'tcx>) { let target = Target::from_item(item); - self.check_attributes( - item.hir_id(), - item.attrs, - &item.span, - target, - Some(ItemLike::Item(item)), - ); + self.check_attributes(item.hir_id(), &item.span, target, Some(ItemLike::Item(item))); intravisit::walk_item(self, item) } fn visit_generic_param(&mut self, generic_param: &'tcx hir::GenericParam<'tcx>) { let target = Target::from_generic_param(generic_param); - self.check_attributes( - generic_param.hir_id, - generic_param.attrs, - &generic_param.span, - target, - None, - ); + self.check_attributes(generic_param.hir_id, &generic_param.span, target, None); intravisit::walk_generic_param(self, generic_param) } fn visit_trait_item(&mut self, trait_item: &'tcx TraitItem<'tcx>) { let target = Target::from_trait_item(trait_item); - self.check_attributes( - trait_item.hir_id(), - &trait_item.attrs, - &trait_item.span, - target, - None, - ); + self.check_attributes(trait_item.hir_id(), &trait_item.span, target, None); intravisit::walk_trait_item(self, trait_item) } - fn visit_struct_field(&mut self, struct_field: &'tcx hir::StructField<'tcx>) { - self.check_attributes( - struct_field.hir_id, - &struct_field.attrs, - &struct_field.span, - Target::Field, - None, - ); - intravisit::walk_struct_field(self, struct_field); + fn visit_field_def(&mut self, struct_field: &'tcx hir::FieldDef<'tcx>) { + self.check_attributes(struct_field.hir_id, &struct_field.span, Target::Field, None); + intravisit::walk_field_def(self, struct_field); } fn visit_arm(&mut self, arm: &'tcx hir::Arm<'tcx>) { - self.check_attributes(arm.hir_id, &arm.attrs, &arm.span, Target::Arm, None); + self.check_attributes(arm.hir_id, &arm.span, Target::Arm, None); intravisit::walk_arm(self, arm); } @@ -1254,7 +1408,6 @@ impl Visitor<'tcx> for CheckAttrVisitor<'tcx> { let target = Target::from_foreign_item(f_item); self.check_attributes( f_item.hir_id(), - &f_item.attrs, &f_item.span, target, Some(ItemLike::ForeignItem(f_item)), @@ -1264,14 +1417,14 @@ impl Visitor<'tcx> for CheckAttrVisitor<'tcx> { fn visit_impl_item(&mut self, impl_item: &'tcx hir::ImplItem<'tcx>) { let target = target_from_impl_item(self.tcx, impl_item); - self.check_attributes(impl_item.hir_id(), &impl_item.attrs, &impl_item.span, target, None); + self.check_attributes(impl_item.hir_id(), &impl_item.span, target, None); intravisit::walk_impl_item(self, impl_item) } fn visit_stmt(&mut self, stmt: &'tcx hir::Stmt<'tcx>) { // When checking statements ignore expressions, they will be checked later. if let hir::StmtKind::Local(ref l) = stmt.kind { - self.check_attributes(l.hir_id, &l.attrs, &stmt.span, Target::Statement, None); + self.check_attributes(l.hir_id, &stmt.span, Target::Statement, None); } intravisit::walk_stmt(self, stmt) } @@ -1282,7 +1435,7 @@ impl Visitor<'tcx> for CheckAttrVisitor<'tcx> { _ => Target::Expression, }; - self.check_attributes(expr.hir_id, &expr.attrs, &expr.span, target, None); + self.check_attributes(expr.hir_id, &expr.span, target, None); intravisit::walk_expr(self, expr) } @@ -1292,23 +1445,17 @@ impl Visitor<'tcx> for CheckAttrVisitor<'tcx> { generics: &'tcx hir::Generics<'tcx>, item_id: HirId, ) { - self.check_attributes(variant.id, variant.attrs, &variant.span, Target::Variant, None); + self.check_attributes(variant.id, &variant.span, Target::Variant, None); intravisit::walk_variant(self, variant, generics, item_id) } fn visit_macro_def(&mut self, macro_def: &'tcx hir::MacroDef<'tcx>) { - self.check_attributes( - macro_def.hir_id(), - macro_def.attrs, - ¯o_def.span, - Target::MacroDef, - None, - ); + self.check_attributes(macro_def.hir_id(), ¯o_def.span, Target::MacroDef, None); intravisit::walk_macro_def(self, macro_def); } fn visit_param(&mut self, param: &'tcx hir::Param<'tcx>) { - self.check_attributes(param.hir_id, param.attrs, ¶m.span, Target::Param, None); + self.check_attributes(param.hir_id, ¶m.span, Target::Param, None); intravisit::walk_param(self, param); } @@ -1335,7 +1482,7 @@ fn check_invalid_crate_level_attr(tcx: TyCtxt<'_>, attrs: &[Attribute]) { sym::path, sym::automatically_derived, sym::start, - sym::main, + sym::rustc_main, ]; for attr in attrs { @@ -1376,13 +1523,7 @@ fn check_mod_attrs(tcx: TyCtxt<'_>, module_def_id: LocalDefId) { tcx.hir().visit_exported_macros_in_krate(check_attr_visitor); check_invalid_macro_level_attr(tcx, tcx.hir().krate().non_exported_macro_attrs); if module_def_id.is_top_level_module() { - check_attr_visitor.check_attributes( - CRATE_HIR_ID, - tcx.hir().krate_attrs(), - &DUMMY_SP, - Target::Mod, - None, - ); + check_attr_visitor.check_attributes(CRATE_HIR_ID, &DUMMY_SP, Target::Mod, None); check_invalid_crate_level_attr(tcx, tcx.hir().krate_attrs()); } } diff --git a/compiler/rustc_passes/src/dead.rs b/compiler/rustc_passes/src/dead.rs index 62a95aa57c29f..c63edf365a1aa 100644 --- a/compiler/rustc_passes/src/dead.rs +++ b/compiler/rustc_passes/src/dead.rs @@ -15,7 +15,6 @@ use rustc_middle::middle::privacy; use rustc_middle::ty::{self, DefIdTree, TyCtxt}; use rustc_session::lint; -use rustc_ast as ast; use rustc_span::symbol::{sym, Symbol}; // Any local node that may call something in its body block should be @@ -154,7 +153,7 @@ impl<'tcx> MarkSymbolVisitor<'tcx> { &mut self, lhs: &hir::Pat<'_>, res: Res, - pats: &[hir::FieldPat<'_>], + pats: &[hir::PatField<'_>], ) { let variant = match self.typeck_results().node_type(lhs.hir_id).kind() { ty::Adt(adt, _) => adt.variant_of_res(res), @@ -225,7 +224,7 @@ impl<'tcx> MarkSymbolVisitor<'tcx> { self.inherited_pub_visibility = had_inherited_pub_visibility; } - fn mark_as_used_if_union(&mut self, adt: &ty::AdtDef, fields: &[hir::Field<'_>]) { + fn mark_as_used_if_union(&mut self, adt: &ty::AdtDef, fields: &[hir::ExprField<'_>]) { if adt.is_union() && adt.non_enum_variant().fields.len() > 1 && adt.did.is_local() { for field in fields { let index = self.tcx.field_index(field.hir_id, self.typeck_results()); @@ -346,11 +345,8 @@ impl<'tcx> Visitor<'tcx> for MarkSymbolVisitor<'tcx> { } } -fn has_allow_dead_code_or_lang_attr( - tcx: TyCtxt<'_>, - id: hir::HirId, - attrs: &[ast::Attribute], -) -> bool { +fn has_allow_dead_code_or_lang_attr(tcx: TyCtxt<'_>, id: hir::HirId) -> bool { + let attrs = tcx.hir().attrs(id); if tcx.sess.contains_name(attrs, sym::lang) { return true; } @@ -400,8 +396,7 @@ struct LifeSeeder<'k, 'tcx> { impl<'v, 'k, 'tcx> ItemLikeVisitor<'v> for LifeSeeder<'k, 'tcx> { fn visit_item(&mut self, item: &hir::Item<'_>) { - let allow_dead_code = - has_allow_dead_code_or_lang_attr(self.tcx, item.hir_id(), &item.attrs); + let allow_dead_code = has_allow_dead_code_or_lang_attr(self.tcx, item.hir_id()); if allow_dead_code { self.worklist.push(item.hir_id()); } @@ -424,11 +419,7 @@ impl<'v, 'k, 'tcx> ItemLikeVisitor<'v> for LifeSeeder<'k, 'tcx> { for impl_item_ref in items { let impl_item = self.krate.impl_item(impl_item_ref.id); if of_trait.is_some() - || has_allow_dead_code_or_lang_attr( - self.tcx, - impl_item.hir_id(), - &impl_item.attrs, - ) + || has_allow_dead_code_or_lang_attr(self.tcx, impl_item.hir_id()) { self.worklist.push(impl_item_ref.id.hir_id()); } @@ -446,7 +437,7 @@ impl<'v, 'k, 'tcx> ItemLikeVisitor<'v> for LifeSeeder<'k, 'tcx> { fn visit_trait_item(&mut self, trait_item: &hir::TraitItem<'_>) { use hir::TraitItemKind::{Const, Fn}; if matches!(trait_item.kind, Const(_, Some(_)) | Fn(_, hir::TraitFn::Provided(_))) - && has_allow_dead_code_or_lang_attr(self.tcx, trait_item.hir_id(), &trait_item.attrs) + && has_allow_dead_code_or_lang_attr(self.tcx, trait_item.hir_id()) { self.worklist.push(trait_item.hir_id()); } @@ -459,11 +450,7 @@ impl<'v, 'k, 'tcx> ItemLikeVisitor<'v> for LifeSeeder<'k, 'tcx> { fn visit_foreign_item(&mut self, foreign_item: &hir::ForeignItem<'_>) { use hir::ForeignItemKind::{Fn, Static}; if matches!(foreign_item.kind, Static(..) | Fn(..)) - && has_allow_dead_code_or_lang_attr( - self.tcx, - foreign_item.hir_id(), - &foreign_item.attrs, - ) + && has_allow_dead_code_or_lang_attr(self.tcx, foreign_item.hir_id()) { self.worklist.push(foreign_item.hir_id()); } @@ -538,22 +525,21 @@ impl DeadVisitor<'tcx> { should_warn && !self.symbol_is_live(item.hir_id()) } - fn should_warn_about_field(&mut self, field: &hir::StructField<'_>) -> bool { + fn should_warn_about_field(&mut self, field: &hir::FieldDef<'_>) -> bool { let field_type = self.tcx.type_of(self.tcx.hir().local_def_id(field.hir_id)); !field.is_positional() && !self.symbol_is_live(field.hir_id) && !field_type.is_phantom_data() - && !has_allow_dead_code_or_lang_attr(self.tcx, field.hir_id, &field.attrs) + && !has_allow_dead_code_or_lang_attr(self.tcx, field.hir_id) } fn should_warn_about_variant(&mut self, variant: &hir::Variant<'_>) -> bool { - !self.symbol_is_live(variant.id) - && !has_allow_dead_code_or_lang_attr(self.tcx, variant.id, &variant.attrs) + !self.symbol_is_live(variant.id) && !has_allow_dead_code_or_lang_attr(self.tcx, variant.id) } fn should_warn_about_foreign_item(&mut self, fi: &hir::ForeignItem<'_>) -> bool { !self.symbol_is_live(fi.hir_id()) - && !has_allow_dead_code_or_lang_attr(self.tcx, fi.hir_id(), &fi.attrs) + && !has_allow_dead_code_or_lang_attr(self.tcx, fi.hir_id()) } // id := HIR id of an item's definition. @@ -664,11 +650,11 @@ impl Visitor<'tcx> for DeadVisitor<'tcx> { intravisit::walk_foreign_item(self, fi); } - fn visit_struct_field(&mut self, field: &'tcx hir::StructField<'tcx>) { + fn visit_field_def(&mut self, field: &'tcx hir::FieldDef<'tcx>) { if self.should_warn_about_field(&field) { self.warn_dead_code(field.hir_id, field.span, field.ident.name, "read"); } - intravisit::walk_struct_field(self, field); + intravisit::walk_field_def(self, field); } fn visit_impl_item(&mut self, impl_item: &'tcx hir::ImplItem<'tcx>) { diff --git a/compiler/rustc_passes/src/diagnostic_items.rs b/compiler/rustc_passes/src/diagnostic_items.rs index 3ec7ea39248bf..8dd3700e5b6af 100644 --- a/compiler/rustc_passes/src/diagnostic_items.rs +++ b/compiler/rustc_passes/src/diagnostic_items.rs @@ -27,19 +27,19 @@ struct DiagnosticItemCollector<'tcx> { impl<'v, 'tcx> ItemLikeVisitor<'v> for DiagnosticItemCollector<'tcx> { fn visit_item(&mut self, item: &hir::Item<'_>) { - self.observe_item(&item.attrs, item.def_id); + self.observe_item(item.def_id); } fn visit_trait_item(&mut self, trait_item: &hir::TraitItem<'_>) { - self.observe_item(&trait_item.attrs, trait_item.def_id); + self.observe_item(trait_item.def_id); } fn visit_impl_item(&mut self, impl_item: &hir::ImplItem<'_>) { - self.observe_item(&impl_item.attrs, impl_item.def_id); + self.observe_item(impl_item.def_id); } fn visit_foreign_item(&mut self, foreign_item: &hir::ForeignItem<'_>) { - self.observe_item(foreign_item.attrs, foreign_item.def_id); + self.observe_item(foreign_item.def_id); } } @@ -48,7 +48,9 @@ impl<'tcx> DiagnosticItemCollector<'tcx> { DiagnosticItemCollector { tcx, items: Default::default() } } - fn observe_item(&mut self, attrs: &[ast::Attribute], def_id: LocalDefId) { + fn observe_item(&mut self, def_id: LocalDefId) { + let hir_id = self.tcx.hir().local_def_id_to_hir_id(def_id); + let attrs = self.tcx.hir().attrs(hir_id); if let Some(name) = extract(&self.tcx.sess, attrs) { // insert into our table collect_item(self.tcx, &mut self.items, name, def_id.to_def_id()); @@ -105,7 +107,7 @@ fn collect<'tcx>(tcx: TyCtxt<'tcx>) -> FxHashMap { tcx.hir().krate().visit_all_item_likes(&mut collector); for m in tcx.hir().krate().exported_macros { - collector.observe_item(m.attrs, m.def_id); + collector.observe_item(m.def_id); } collector.items diff --git a/compiler/rustc_passes/src/entry.rs b/compiler/rustc_passes/src/entry.rs index 0d3a7ea3a8a00..e1b750df33c68 100644 --- a/compiler/rustc_passes/src/entry.rs +++ b/compiler/rustc_passes/src/entry.rs @@ -2,7 +2,7 @@ use rustc_ast::entry::EntryPointType; use rustc_errors::struct_span_err; use rustc_hir::def_id::{CrateNum, LocalDefId, CRATE_DEF_INDEX, LOCAL_CRATE}; use rustc_hir::itemlikevisit::ItemLikeVisitor; -use rustc_hir::{ForeignItem, HirId, ImplItem, Item, ItemKind, TraitItem}; +use rustc_hir::{ForeignItem, HirId, ImplItem, Item, ItemKind, TraitItem, CRATE_HIR_ID}; use rustc_middle::hir::map::Map; use rustc_middle::ty::query::Providers; use rustc_middle::ty::TyCtxt; @@ -60,7 +60,7 @@ fn entry_fn(tcx: TyCtxt<'_>, cnum: CrateNum) -> Option<(LocalDefId, EntryFnType) } // If the user wants no main function at all, then stop here. - if tcx.sess.contains_name(&tcx.hir().krate().item.attrs, sym::no_main) { + if tcx.sess.contains_name(&tcx.hir().attrs(CRATE_HIR_ID), sym::no_main) { return None; } @@ -80,10 +80,11 @@ fn entry_fn(tcx: TyCtxt<'_>, cnum: CrateNum) -> Option<(LocalDefId, EntryFnType) // Beware, this is duplicated in `librustc_builtin_macros/test_harness.rs` // (with `ast::Item`), so make sure to keep them in sync. -fn entry_point_type(sess: &Session, item: &Item<'_>, at_root: bool) -> EntryPointType { - if sess.contains_name(&item.attrs, sym::start) { +fn entry_point_type(ctxt: &EntryContext<'_, '_>, item: &Item<'_>, at_root: bool) -> EntryPointType { + let attrs = ctxt.map.attrs(item.hir_id()); + if ctxt.session.contains_name(attrs, sym::start) { EntryPointType::Start - } else if sess.contains_name(&item.attrs, sym::main) { + } else if ctxt.session.contains_name(attrs, sym::rustc_main) { EntryPointType::MainAttr } else if item.ident.name == sym::main { if at_root { @@ -103,14 +104,15 @@ fn throw_attr_err(sess: &Session, span: Span, attr: &str) { } fn find_item(item: &Item<'_>, ctxt: &mut EntryContext<'_, '_>, at_root: bool) { - match entry_point_type(&ctxt.session, item, at_root) { + match entry_point_type(ctxt, item, at_root) { EntryPointType::None => (), _ if !matches!(item.kind, ItemKind::Fn(..)) => { - if let Some(attr) = ctxt.session.find_by_name(item.attrs, sym::start) { + let attrs = ctxt.map.attrs(item.hir_id()); + if let Some(attr) = ctxt.session.find_by_name(attrs, sym::start) { throw_attr_err(&ctxt.session, attr.span, "start"); } - if let Some(attr) = ctxt.session.find_by_name(item.attrs, sym::main) { - throw_attr_err(&ctxt.session, attr.span, "main"); + if let Some(attr) = ctxt.session.find_by_name(attrs, sym::rustc_main) { + throw_attr_err(&ctxt.session, attr.span, "rustc_main"); } } EntryPointType::MainNamed => { @@ -169,7 +171,7 @@ fn configure_main( } fn no_main_err(tcx: TyCtxt<'_>, visitor: &EntryContext<'_, '_>) { - let sp = tcx.hir().krate().item.span; + let sp = tcx.hir().krate().item.inner; if *tcx.sess.parse_sess.reached_eof.borrow() { // There's an unclosed brace that made the parser reach `Eof`, we shouldn't complain about // the missing `fn main()` then as it might have been hidden inside an unclosed block. @@ -191,10 +193,7 @@ fn no_main_err(tcx: TyCtxt<'_>, visitor: &EntryContext<'_, '_>) { err.span_note(span, "here is a function named `main`"); } err.note("you have one or more functions named `main` not defined at the crate level"); - err.help( - "either move the `main` function definitions or attach the `#[main]` attribute \ - to one of them", - ); + err.help("consider moving the `main` function definitions"); // There were some functions named `main` though. Try to give the user a hint. format!( "the main function must be defined at the crate level{}", diff --git a/compiler/rustc_passes/src/hir_id_validator.rs b/compiler/rustc_passes/src/hir_id_validator.rs index 79e3b5952acaf..944a3097a61c3 100644 --- a/compiler/rustc_passes/src/hir_id_validator.rs +++ b/compiler/rustc_passes/src/hir_id_validator.rs @@ -172,17 +172,4 @@ impl<'a, 'hir> intravisit::Visitor<'hir> for HirIdValidator<'a, 'hir> { // we are currently in. So for those it's correct that they have a // different owner. } - - fn visit_generic_param(&mut self, param: &'hir hir::GenericParam<'hir>) { - if let hir::GenericParamKind::Type { - synthetic: Some(hir::SyntheticTyParamKind::ImplTrait), - .. - } = param.kind - { - // Synthetic impl trait parameters are owned by the node of the desugared type. - // This means it is correct for them to have a different owner. - } else { - intravisit::walk_generic_param(self, param); - } - } } diff --git a/compiler/rustc_passes/src/hir_stats.rs b/compiler/rustc_passes/src/hir_stats.rs index a2b6dd17ad9ba..2bed8cadeb95d 100644 --- a/compiler/rustc_passes/src/hir_stats.rs +++ b/compiler/rustc_passes/src/hir_stats.rs @@ -201,9 +201,9 @@ impl<'v> hir_visit::Visitor<'v> for StatCollector<'v> { hir_visit::walk_param_bound(self, bounds) } - fn visit_struct_field(&mut self, s: &'v hir::StructField<'v>) { - self.record("StructField", Id::Node(s.hir_id), s); - hir_visit::walk_struct_field(self, s) + fn visit_field_def(&mut self, s: &'v hir::FieldDef<'v>) { + self.record("FieldDef", Id::Node(s.hir_id), s); + hir_visit::walk_field_def(self, s) } fn visit_variant( @@ -241,7 +241,7 @@ impl<'v> hir_visit::Visitor<'v> for StatCollector<'v> { hir_visit::walk_assoc_type_binding(self, type_binding) } - fn visit_attribute(&mut self, attr: &'v ast::Attribute) { + fn visit_attribute(&mut self, _: hir::HirId, attr: &'v ast::Attribute) { self.record("Attribute", Id::Attr(attr.id), attr); } @@ -316,9 +316,9 @@ impl<'v> ast_visit::Visitor<'v> for StatCollector<'v> { ast_visit::walk_param_bound(self, bounds) } - fn visit_struct_field(&mut self, s: &'v ast::StructField) { - self.record("StructField", Id::None, s); - ast_visit::walk_struct_field(self, s) + fn visit_field_def(&mut self, s: &'v ast::FieldDef) { + self.record("FieldDef", Id::None, s); + ast_visit::walk_field_def(self, s) } fn visit_variant(&mut self, v: &'v ast::Variant) { diff --git a/compiler/rustc_passes/src/intrinsicck.rs b/compiler/rustc_passes/src/intrinsicck.rs index 0f4bb635eeefd..3f095d0e82427 100644 --- a/compiler/rustc_passes/src/intrinsicck.rs +++ b/compiler/rustc_passes/src/intrinsicck.rs @@ -347,7 +347,7 @@ impl ExprVisitor<'tcx> { } fn check_asm(&self, asm: &hir::InlineAsm<'tcx>) { - for (idx, (op, _op_sp)) in asm.operands.iter().enumerate() { + for (idx, (op, op_sp)) in asm.operands.iter().enumerate() { match *op { hir::InlineAsmOperand::In { reg, ref expr } => { self.check_asm_operand_type(idx, reg, expr, asm.template, None); @@ -372,14 +372,15 @@ impl ExprVisitor<'tcx> { ); } } - hir::InlineAsmOperand::Const { ref expr } => { - let ty = self.typeck_results.expr_ty_adjusted(expr); - match ty.kind() { + hir::InlineAsmOperand::Const { ref anon_const } => { + let anon_const_def_id = self.tcx.hir().local_def_id(anon_const.hir_id); + let value = ty::Const::from_anon_const(self.tcx, anon_const_def_id); + match value.ty.kind() { ty::Int(_) | ty::Uint(_) | ty::Float(_) => {} _ => { let msg = "asm `const` arguments must be integer or floating-point values"; - self.tcx.sess.span_err(expr.span, msg); + self.tcx.sess.span_err(*op_sp, msg); } } } diff --git a/compiler/rustc_passes/src/lang_items.rs b/compiler/rustc_passes/src/lang_items.rs index 8e2ad7f783e9b..7e6bb9779f077 100644 --- a/compiler/rustc_passes/src/lang_items.rs +++ b/compiler/rustc_passes/src/lang_items.rs @@ -13,7 +13,6 @@ use crate::weak_lang_items; use rustc_middle::middle::cstore::ExternCrate; use rustc_middle::ty::TyCtxt; -use rustc_ast::Attribute; use rustc_errors::struct_span_err; use rustc_hir as hir; use rustc_hir::def_id::{DefId, LOCAL_CRATE}; @@ -30,29 +29,21 @@ struct LanguageItemCollector<'tcx> { impl ItemLikeVisitor<'v> for LanguageItemCollector<'tcx> { fn visit_item(&mut self, item: &hir::Item<'_>) { - self.check_for_lang(Target::from_item(item), item.hir_id(), item.attrs); + self.check_for_lang(Target::from_item(item), item.hir_id()); if let hir::ItemKind::Enum(def, ..) = &item.kind { for variant in def.variants { - self.check_for_lang(Target::Variant, variant.id, variant.attrs); + self.check_for_lang(Target::Variant, variant.id); } } } fn visit_trait_item(&mut self, trait_item: &hir::TraitItem<'_>) { - self.check_for_lang( - Target::from_trait_item(trait_item), - trait_item.hir_id(), - trait_item.attrs, - ) + self.check_for_lang(Target::from_trait_item(trait_item), trait_item.hir_id()) } fn visit_impl_item(&mut self, impl_item: &hir::ImplItem<'_>) { - self.check_for_lang( - target_from_impl_item(self.tcx, impl_item), - impl_item.hir_id(), - impl_item.attrs, - ) + self.check_for_lang(target_from_impl_item(self.tcx, impl_item), impl_item.hir_id()) } fn visit_foreign_item(&mut self, _: &hir::ForeignItem<'_>) {} @@ -63,7 +54,8 @@ impl LanguageItemCollector<'tcx> { LanguageItemCollector { tcx, items: LanguageItems::new() } } - fn check_for_lang(&mut self, actual_target: Target, hir_id: HirId, attrs: &[Attribute]) { + fn check_for_lang(&mut self, actual_target: Target, hir_id: HirId) { + let attrs = self.tcx.hir().attrs(hir_id); let check_name = |attr, sym| self.tcx.sess.check_name(attr, sym); if let Some((value, span)) = extract(check_name, &attrs) { match ITEM_REFS.get(&value).cloned() { diff --git a/compiler/rustc_passes/src/lib.rs b/compiler/rustc_passes/src/lib.rs index 9759a500e0619..933e8ad1d727c 100644 --- a/compiler/rustc_passes/src/lib.rs +++ b/compiler/rustc_passes/src/lib.rs @@ -9,8 +9,9 @@ #![feature(const_panic)] #![feature(crate_visibility_modifier)] #![feature(in_band_lifetimes)] +#![feature(iter_zip)] #![feature(nll)] -#![feature(or_patterns)] +#![cfg_attr(bootstrap, feature(or_patterns))] #![recursion_limit = "256"] #[macro_use] diff --git a/compiler/rustc_passes/src/lib_features.rs b/compiler/rustc_passes/src/lib_features.rs index 02b20e45d000c..3dfe317a4bd17 100644 --- a/compiler/rustc_passes/src/lib_features.rs +++ b/compiler/rustc_passes/src/lib_features.rs @@ -120,7 +120,7 @@ impl Visitor<'tcx> for LibFeatureCollector<'tcx> { NestedVisitorMap::All(self.tcx.hir()) } - fn visit_attribute(&mut self, attr: &'tcx Attribute) { + fn visit_attribute(&mut self, _: rustc_hir::HirId, attr: &'tcx Attribute) { if let Some((feature, stable, span)) = self.extract(attr) { self.collect_feature(feature, stable, span); } @@ -131,7 +131,7 @@ fn collect(tcx: TyCtxt<'_>) -> LibFeatures { let mut collector = LibFeatureCollector::new(tcx); let krate = tcx.hir().krate(); for attr in krate.non_exported_macro_attrs { - collector.visit_attribute(attr); + collector.visit_attribute(rustc_hir::CRATE_HIR_ID, attr); } intravisit::walk_crate(&mut collector, krate); collector.lib_features diff --git a/compiler/rustc_passes/src/liveness.rs b/compiler/rustc_passes/src/liveness.rs index a96f332374430..e22a108aaf07e 100644 --- a/compiler/rustc_passes/src/liveness.rs +++ b/compiler/rustc_passes/src/liveness.rs @@ -95,7 +95,7 @@ use rustc_hir::{Expr, HirId, HirIdMap, HirIdSet}; use rustc_index::vec::IndexVec; use rustc_middle::hir::map::Map; use rustc_middle::ty::query::Providers; -use rustc_middle::ty::{self, DefIdTree, TyCtxt}; +use rustc_middle::ty::{self, DefIdTree, RootVariableMinCaptureList, TyCtxt}; use rustc_session::lint; use rustc_span::symbol::{kw, sym, Symbol}; use rustc_span::Span; @@ -103,6 +103,7 @@ use rustc_span::Span; use std::collections::VecDeque; use std::io; use std::io::prelude::*; +use std::iter; use std::rc::Rc; mod rwu_table; @@ -331,7 +332,7 @@ impl<'tcx> Visitor<'tcx> for IrMaps<'tcx> { } } - if let Some(captures) = maps.tcx.typeck(local_def_id).closure_captures.get(&def_id) { + if let Some(captures) = maps.tcx.typeck(local_def_id).closure_min_captures.get(&def_id) { for &var_hir_id in captures.keys() { let var_name = maps.tcx.hir().name(var_hir_id); maps.add_variable(Upvar(var_hir_id, var_name)); @@ -408,10 +409,10 @@ impl<'tcx> Visitor<'tcx> for IrMaps<'tcx> { if let Some(captures) = self .tcx .typeck(closure_def_id) - .closure_captures + .closure_min_captures .get(&closure_def_id.to_def_id()) { - // If closure captures is Some, upvars_mentioned must also be Some + // If closure_min_captures is Some, upvars_mentioned must also be Some let upvars = self.tcx.upvars_mentioned(closure_def_id).unwrap(); call_caps.extend(captures.keys().map(|var_id| { let upvar = upvars[var_id]; @@ -481,11 +482,10 @@ const ACC_USE: u32 = 4; struct Liveness<'a, 'tcx> { ir: &'a mut IrMaps<'tcx>, - body_owner: LocalDefId, typeck_results: &'a ty::TypeckResults<'tcx>, param_env: ty::ParamEnv<'tcx>, upvars: Option<&'tcx FxIndexMap>, - closure_captures: Option<&'tcx FxIndexMap>, + closure_min_captures: Option<&'tcx RootVariableMinCaptureList<'tcx>>, successors: IndexVec>, rwu_table: rwu_table::RWUTable, @@ -509,8 +509,7 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> { let typeck_results = ir.tcx.typeck(body_owner); let param_env = ir.tcx.param_env(body_owner); let upvars = ir.tcx.upvars_mentioned(body_owner); - let closure_captures = typeck_results.closure_captures.get(&body_owner.to_def_id()); - + let closure_min_captures = typeck_results.closure_min_captures.get(&body_owner.to_def_id()); let closure_ln = ir.add_live_node(ClosureNode); let exit_ln = ir.add_live_node(ExitNode); @@ -519,11 +518,10 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> { Liveness { ir, - body_owner, typeck_results, param_env, upvars, - closure_captures, + closure_min_captures, successors: IndexVec::from_elem_n(None, num_live_nodes), rwu_table: rwu_table::RWUTable::new(num_live_nodes, num_vars), closure_ln, @@ -707,25 +705,27 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> { // if they are live on the entry to the closure, since only the closure // itself can access them on subsequent calls. - if let Some(closure_captures) = self.closure_captures { + if let Some(closure_min_captures) = self.closure_min_captures { // Mark upvars captured by reference as used after closure exits. - // Since closure_captures is Some, upvars must exists too. - let upvars = self.upvars.unwrap(); - for (&var_hir_id, upvar_id) in closure_captures { - let upvar = upvars[&var_hir_id]; - match self.typeck_results.upvar_capture(*upvar_id) { - ty::UpvarCapture::ByRef(_) => { - let var = self.variable(var_hir_id, upvar.span); - self.acc(self.exit_ln, var, ACC_READ | ACC_USE); + 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::ByRef(_) => { + let var = self.variable( + var_hir_id, + captured_place.get_capture_kind_span(self.ir.tcx), + ); + self.acc(self.exit_ln, var, ACC_READ | ACC_USE); + } + ty::UpvarCapture::ByValue(_) => {} } - ty::UpvarCapture::ByValue(_) => {} } } } let succ = self.propagate_through_expr(&body.value, self.exit_ln); - if self.closure_captures.is_none() { + if self.closure_min_captures.is_none() { // Either not a closure, or closure without any captured variables. // No need to determine liveness of captured variables, since there // are none. @@ -1067,7 +1067,6 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> { for (op, _op_sp) in asm.operands.iter().rev() { match op { hir::InlineAsmOperand::In { expr, .. } - | hir::InlineAsmOperand::Const { expr, .. } | hir::InlineAsmOperand::Sym { expr, .. } => { succ = self.propagate_through_expr(expr, succ) } @@ -1085,6 +1084,7 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> { } succ = self.propagate_through_expr(in_expr, succ); } + hir::InlineAsmOperand::Const { .. } => {} } } succ @@ -1094,7 +1094,7 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> { let ia = &asm.inner; let outputs = asm.outputs_exprs; let inputs = asm.inputs_exprs; - let succ = ia.outputs.iter().zip(outputs).rev().fold(succ, |succ, (o, output)| { + let succ = iter::zip(&ia.outputs, outputs).rev().fold(succ, |succ, (o, output)| { // see comment on places // in propagate_through_place_components() if o.is_indirect { @@ -1221,7 +1221,7 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> { match path.res { Res::Local(hid) => { let in_upvars = self.upvars.map_or(false, |u| u.contains_key(&hid)); - let in_captures = self.closure_captures.map_or(false, |c| c.contains_key(&hid)); + let in_captures = self.closure_min_captures.map_or(false, |c| c.contains_key(&hid)); match (in_upvars, in_captures) { (false, _) | (true, true) => self.access_var(hir_id, hid, succ, acc, path.span), @@ -1345,7 +1345,7 @@ fn check_expr<'tcx>(this: &mut Liveness<'_, 'tcx>, expr: &'tcx Expr<'tcx>) { } // Output operands must be places - for (o, output) in asm.inner.outputs.iter().zip(asm.outputs_exprs) { + for (o, output) in iter::zip(&asm.inner.outputs, asm.outputs_exprs) { if !o.is_indirect { this.check_place(output); } @@ -1422,52 +1422,52 @@ impl<'tcx> Liveness<'_, 'tcx> { } fn warn_about_unused_upvars(&self, entry_ln: LiveNode) { - let closure_captures = match self.closure_captures { + let closure_min_captures = match self.closure_min_captures { None => return, - Some(closure_captures) => closure_captures, + Some(closure_min_captures) => closure_min_captures, }; - // If closure_captures is Some(), upvars must be Some() too. - let upvars = self.upvars.unwrap(); - for &var_hir_id in closure_captures.keys() { - let upvar = upvars[&var_hir_id]; - let var = self.variable(var_hir_id, upvar.span); - let upvar_id = ty::UpvarId { - var_path: ty::UpvarPath { hir_id: var_hir_id }, - closure_expr_id: self.body_owner, - }; - match self.typeck_results.upvar_capture(upvar_id) { - ty::UpvarCapture::ByValue(_) => {} - ty::UpvarCapture::ByRef(..) => continue, - }; - if self.used_on_entry(entry_ln, var) { - if !self.live_on_entry(entry_ln, var) { + // If closure_min_captures is Some(), upvars must be Some() too. + 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::ByRef(..) => continue, + }; + let span = captured_place.get_capture_kind_span(self.ir.tcx); + let var = self.variable(var_hir_id, span); + if self.used_on_entry(entry_ln, var) { + if !self.live_on_entry(entry_ln, var) { + if let Some(name) = self.should_warn(var) { + self.ir.tcx.struct_span_lint_hir( + lint::builtin::UNUSED_ASSIGNMENTS, + var_hir_id, + vec![span], + |lint| { + lint.build(&format!( + "value captured by `{}` is never read", + name + )) + .help("did you mean to capture by reference instead?") + .emit(); + }, + ); + } + } + } else { if let Some(name) = self.should_warn(var) { self.ir.tcx.struct_span_lint_hir( - lint::builtin::UNUSED_ASSIGNMENTS, + lint::builtin::UNUSED_VARIABLES, var_hir_id, - vec![upvar.span], + vec![span], |lint| { - lint.build(&format!("value captured by `{}` is never read", name)) + lint.build(&format!("unused variable: `{}`", name)) .help("did you mean to capture by reference instead?") .emit(); }, ); } } - } else { - if let Some(name) = self.should_warn(var) { - self.ir.tcx.struct_span_lint_hir( - lint::builtin::UNUSED_VARIABLES, - var_hir_id, - vec![upvar.span], - |lint| { - lint.build(&format!("unused variable: `{}`", name)) - .help("did you mean to capture by reference instead?") - .emit(); - }, - ); - } } } } diff --git a/compiler/rustc_passes/src/liveness/rwu_table.rs b/compiler/rustc_passes/src/liveness/rwu_table.rs index a1a6f27398ec8..6d5983f53dc39 100644 --- a/compiler/rustc_passes/src/liveness/rwu_table.rs +++ b/compiler/rustc_passes/src/liveness/rwu_table.rs @@ -1,4 +1,5 @@ use crate::liveness::{LiveNode, Variable}; +use std::iter; #[derive(Clone, Copy)] pub(super) struct RWU { @@ -91,7 +92,7 @@ impl RWUTable { let mut changed = false; let (dst_row, src_row) = self.pick2_rows_mut(dst, src); - for (dst_word, src_word) in dst_row.iter_mut().zip(src_row.iter()) { + for (dst_word, src_word) in iter::zip(dst_row, &*src_row) { let old = *dst_word; let new = *dst_word | src_word; *dst_word = new; diff --git a/compiler/rustc_passes/src/naked_functions.rs b/compiler/rustc_passes/src/naked_functions.rs index 93fb23c018bf0..89bc2e1a9870f 100644 --- a/compiler/rustc_passes/src/naked_functions.rs +++ b/compiler/rustc_passes/src/naked_functions.rs @@ -46,7 +46,7 @@ impl<'tcx> Visitor<'tcx> for CheckNakedFunctions<'tcx> { let fn_header; match fk { - FnKind::Closure(..) => { + FnKind::Closure => { // Closures with a naked attribute are rejected during attribute // check. Don't validate them any further. return; @@ -62,7 +62,8 @@ impl<'tcx> Visitor<'tcx> for CheckNakedFunctions<'tcx> { } } - let naked = fk.attrs().iter().any(|attr| attr.has_name(sym::naked)); + let attrs = self.tcx.hir().attrs(hir_id); + let naked = attrs.iter().any(|attr| attr.has_name(sym::naked)); if naked { let body = self.tcx.hir().body(body_id); check_abi(self.tcx, hir_id, fn_header.abi, ident_span); diff --git a/compiler/rustc_passes/src/region.rs b/compiler/rustc_passes/src/region.rs index b532021bed2e9..14a373c59423f 100644 --- a/compiler/rustc_passes/src/region.rs +++ b/compiler/rustc_passes/src/region.rs @@ -23,14 +23,6 @@ use std::mem; #[derive(Debug, Copy, Clone)] pub struct Context { - /// The root of the current region tree. This is typically the id - /// of the innermost fn body. Each fn forms its own disjoint tree - /// in the region hierarchy. These fn bodies are themselves - /// arranged into a tree. See the "Modeling closures" section of - /// the README in `rustc_trait_selection::infer::region_constraints` - /// for more details. - root_id: Option, - /// The scope that contains any new variables declared, plus its depth in /// the scope tree. var_parent: Option<(Scope, ScopeDepth)>, @@ -743,11 +735,6 @@ impl<'tcx> Visitor<'tcx> for RegionResolutionVisitor<'tcx> { let outer_pessimistic_yield = mem::replace(&mut self.pessimistic_yield, false); self.terminating_scopes.insert(body.value.hir_id.local_id); - if let Some(root_id) = self.cx.root_id { - self.scope_tree.record_closure_parent(body.value.hir_id.local_id, root_id); - } - self.cx.root_id = Some(body.value.hir_id.local_id); - self.enter_scope(Scope { id: body.value.hir_id.local_id, data: ScopeData::CallSite }); self.enter_scope(Scope { id: body.value.hir_id.local_id, data: ScopeData::Arguments }); @@ -824,7 +811,7 @@ fn region_scope_tree(tcx: TyCtxt<'_>, def_id: DefId) -> &ScopeTree { tcx, scope_tree: ScopeTree::default(), expr_and_pat_count: 0, - cx: Context { root_id: None, parent: None, var_parent: None }, + cx: Context { parent: None, var_parent: None }, terminating_scopes: Default::default(), pessimistic_yield: false, fixup_scopes: vec![], diff --git a/compiler/rustc_passes/src/stability.rs b/compiler/rustc_passes/src/stability.rs index c4c1034374360..9c4f9b1198cf2 100644 --- a/compiler/rustc_passes/src/stability.rs +++ b/compiler/rustc_passes/src/stability.rs @@ -9,7 +9,7 @@ use rustc_hir as hir; use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::{DefId, LocalDefId, CRATE_DEF_INDEX, LOCAL_CRATE}; use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor}; -use rustc_hir::{Generics, HirId, Item, StructField, TraitRef, Ty, TyKind, Variant}; +use rustc_hir::{FieldDef, Generics, HirId, Item, TraitRef, Ty, TyKind, Variant}; use rustc_middle::hir::map::Map; use rustc_middle::middle::privacy::AccessLevels; use rustc_middle::middle::stability::{DeprecationEntry, Index}; @@ -22,6 +22,7 @@ use rustc_span::symbol::{sym, Symbol}; use rustc_span::{Span, DUMMY_SP}; use std::cmp::Ordering; +use std::iter; use std::mem::replace; use std::num::NonZeroU32; @@ -97,7 +98,6 @@ impl<'a, 'tcx> Annotator<'a, 'tcx> { fn annotate( &mut self, hir_id: HirId, - attrs: &[Attribute], item_sp: Span, kind: AnnotationKind, inherit_deprecation: InheritDeprecation, @@ -107,6 +107,7 @@ impl<'a, 'tcx> Annotator<'a, 'tcx> { ) where F: FnOnce(&mut Self), { + let attrs = self.tcx.hir().attrs(hir_id); debug!("annotate(id = {:?}, attrs = {:?})", hir_id, attrs); let mut did_error = false; if !self.tcx.features().staged_api { @@ -214,7 +215,7 @@ impl<'a, 'tcx> Annotator<'a, 'tcx> { { // Explicit version of iter::order::lt to handle parse errors properly for (dep_v, stab_v) in - dep_since.as_str().split('.').zip(stab_since.as_str().split('.')) + iter::zip(dep_since.as_str().split('.'), stab_since.as_str().split('.')) { match stab_v.parse::() { Err(_) => { @@ -385,7 +386,6 @@ impl<'a, 'tcx> Visitor<'tcx> for Annotator<'a, 'tcx> { if let Some(ctor_hir_id) = sd.ctor_hir_id() { self.annotate( ctor_hir_id, - &i.attrs, i.span, AnnotationKind::Required, InheritDeprecation::Yes, @@ -400,7 +400,6 @@ impl<'a, 'tcx> Visitor<'tcx> for Annotator<'a, 'tcx> { self.annotate( i.hir_id(), - &i.attrs, i.span, kind, InheritDeprecation::Yes, @@ -414,7 +413,6 @@ impl<'a, 'tcx> Visitor<'tcx> for Annotator<'a, 'tcx> { fn visit_trait_item(&mut self, ti: &'tcx hir::TraitItem<'tcx>) { self.annotate( ti.hir_id(), - &ti.attrs, ti.span, AnnotationKind::Required, InheritDeprecation::Yes, @@ -431,7 +429,6 @@ impl<'a, 'tcx> Visitor<'tcx> for Annotator<'a, 'tcx> { if self.in_trait_impl { AnnotationKind::Prohibited } else { AnnotationKind::Required }; self.annotate( ii.hir_id(), - &ii.attrs, ii.span, kind, InheritDeprecation::Yes, @@ -446,7 +443,6 @@ impl<'a, 'tcx> Visitor<'tcx> for Annotator<'a, 'tcx> { fn visit_variant(&mut self, var: &'tcx Variant<'tcx>, g: &'tcx Generics<'tcx>, item_id: HirId) { self.annotate( var.id, - &var.attrs, var.span, AnnotationKind::Required, InheritDeprecation::Yes, @@ -456,7 +452,6 @@ impl<'a, 'tcx> Visitor<'tcx> for Annotator<'a, 'tcx> { if let Some(ctor_hir_id) = var.data.ctor_hir_id() { v.annotate( ctor_hir_id, - &var.attrs, var.span, AnnotationKind::Required, InheritDeprecation::Yes, @@ -471,17 +466,16 @@ impl<'a, 'tcx> Visitor<'tcx> for Annotator<'a, 'tcx> { ) } - fn visit_struct_field(&mut self, s: &'tcx StructField<'tcx>) { + fn visit_field_def(&mut self, s: &'tcx FieldDef<'tcx>) { self.annotate( s.hir_id, - &s.attrs, s.span, AnnotationKind::Required, InheritDeprecation::Yes, InheritConstStability::No, InheritStability::Yes, |v| { - intravisit::walk_struct_field(v, s); + intravisit::walk_field_def(v, s); }, ); } @@ -489,7 +483,6 @@ impl<'a, 'tcx> Visitor<'tcx> for Annotator<'a, 'tcx> { fn visit_foreign_item(&mut self, i: &'tcx hir::ForeignItem<'tcx>) { self.annotate( i.hir_id(), - &i.attrs, i.span, AnnotationKind::Required, InheritDeprecation::Yes, @@ -504,7 +497,6 @@ impl<'a, 'tcx> Visitor<'tcx> for Annotator<'a, 'tcx> { fn visit_macro_def(&mut self, md: &'tcx hir::MacroDef<'tcx>) { self.annotate( md.hir_id(), - &md.attrs, md.span, AnnotationKind::Required, InheritDeprecation::Yes, @@ -516,16 +508,14 @@ impl<'a, 'tcx> Visitor<'tcx> for Annotator<'a, 'tcx> { fn visit_generic_param(&mut self, p: &'tcx hir::GenericParam<'tcx>) { let kind = match &p.kind { - // FIXME(const_generics_defaults) - hir::GenericParamKind::Type { default, .. } if default.is_some() => { - AnnotationKind::Container - } + // Allow stability attributes on default generic arguments. + hir::GenericParamKind::Type { default: Some(_), .. } + | hir::GenericParamKind::Const { default: Some(_), .. } => AnnotationKind::Container, _ => AnnotationKind::Prohibited, }; self.annotate( p.hir_id, - &p.attrs, p.span, kind, InheritDeprecation::No, @@ -620,9 +610,9 @@ impl<'tcx> Visitor<'tcx> for MissingStabilityAnnotations<'tcx> { intravisit::walk_variant(self, var, g, item_id); } - fn visit_struct_field(&mut self, s: &'tcx StructField<'tcx>) { + fn visit_field_def(&mut self, s: &'tcx FieldDef<'tcx>) { self.check_missing_stability(s.hir_id, s.span); - intravisit::walk_struct_field(self, s); + intravisit::walk_field_def(self, s); } fn visit_foreign_item(&mut self, i: &'tcx hir::ForeignItem<'tcx>) { @@ -696,8 +686,7 @@ fn new_index(tcx: TyCtxt<'tcx>) -> Index<'tcx> { annotator.annotate( hir::CRATE_HIR_ID, - &krate.item.attrs, - krate.item.span, + krate.item.inner, AnnotationKind::Required, InheritDeprecation::Yes, InheritConstStability::No, @@ -762,8 +751,9 @@ impl Visitor<'tcx> for Checker<'tcx> { // error if all involved types and traits are stable, because // it will have no effect. // See: https://github.com/rust-lang/rust/issues/55436 + let attrs = self.tcx.hir().attrs(item.hir_id()); if let (Some((Stability { level: attr::Unstable { .. }, .. }, span)), _) = - attr::find_stability(&self.tcx.sess, &item.attrs, item.span) + attr::find_stability(&self.tcx.sess, attrs, item.span) { let mut c = CheckTraitImplStable { tcx: self.tcx, fully_stable: true }; c.visit_ty(self_ty); @@ -895,7 +885,7 @@ pub fn check_unused_or_stable_features(tcx: TyCtxt<'_>) { if tcx.stability().staged_api[&LOCAL_CRATE] { let krate = tcx.hir().krate(); let mut missing = MissingStabilityAnnotations { tcx, access_levels }; - missing.check_missing_stability(hir::CRATE_HIR_ID, krate.item.span); + missing.check_missing_stability(hir::CRATE_HIR_ID, krate.item.inner); intravisit::walk_crate(&mut missing, krate); krate.visit_all_item_likes(&mut missing.as_deep_visitor()); } diff --git a/compiler/rustc_passes/src/weak_lang_items.rs b/compiler/rustc_passes/src/weak_lang_items.rs index daff94cb6d355..de369ba9bbbe2 100644 --- a/compiler/rustc_passes/src/weak_lang_items.rs +++ b/compiler/rustc_passes/src/weak_lang_items.rs @@ -97,7 +97,8 @@ impl<'a, 'tcx, 'v> Visitor<'v> for Context<'a, 'tcx> { fn visit_foreign_item(&mut self, i: &hir::ForeignItem<'_>) { let check_name = |attr, sym| self.tcx.sess.check_name(attr, sym); - if let Some((lang_item, _)) = lang_items::extract(check_name, &i.attrs) { + let attrs = self.tcx.hir().attrs(i.hir_id()); + if let Some((lang_item, _)) = lang_items::extract(check_name, attrs) { self.register(lang_item, i.span); } intravisit::walk_foreign_item(self, i) diff --git a/compiler/rustc_plugin_impl/src/build.rs b/compiler/rustc_plugin_impl/src/build.rs index d5c287fb3bcb3..a49afa35e4624 100644 --- a/compiler/rustc_plugin_impl/src/build.rs +++ b/compiler/rustc_plugin_impl/src/build.rs @@ -16,7 +16,8 @@ struct RegistrarFinder<'tcx> { impl<'v, 'tcx> ItemLikeVisitor<'v> for RegistrarFinder<'tcx> { fn visit_item(&mut self, item: &hir::Item<'_>) { if let hir::ItemKind::Fn(..) = item.kind { - if self.tcx.sess.contains_name(&item.attrs, sym::plugin_registrar) { + let attrs = self.tcx.hir().attrs(item.hir_id()); + if self.tcx.sess.contains_name(attrs, sym::plugin_registrar) { self.registrars.push((item.def_id, item.span)); } } diff --git a/compiler/rustc_privacy/src/lib.rs b/compiler/rustc_privacy/src/lib.rs index 6e0e1c5eeef15..d37a5be2fe515 100644 --- a/compiler/rustc_privacy/src/lib.rs +++ b/compiler/rustc_privacy/src/lib.rs @@ -1,7 +1,7 @@ #![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")] #![feature(in_band_lifetimes)] #![feature(nll)] -#![feature(or_patterns)] +#![cfg_attr(bootstrap, feature(or_patterns))] #![feature(control_flow_enum)] #![feature(try_blocks)] #![feature(associated_type_defaults)] @@ -881,7 +881,8 @@ impl Visitor<'tcx> for EmbargoVisitor<'tcx> { fn visit_macro_def(&mut self, md: &'tcx hir::MacroDef<'tcx>) { // Non-opaque macros cannot make other items more accessible than they already are. - if attr::find_transparency(&self.tcx.sess, &md.attrs, md.ast.macro_rules).0 + let attrs = self.tcx.hir().attrs(md.hir_id()); + if attr::find_transparency(&self.tcx.sess, &attrs, md.ast.macro_rules).0 != Transparency::Opaque { // `#[macro_export]`-ed `macro_rules!` are `Public` since they @@ -927,8 +928,11 @@ impl ReachEverythingInTheInterfaceVisitor<'_, 'tcx> { self.visit(self.ev.tcx.type_of(param.def_id)); } } - GenericParamDefKind::Const => { + GenericParamDefKind::Const { has_default, .. } => { self.visit(self.ev.tcx.type_of(param.def_id)); + if has_default { + self.visit(self.ev.tcx.const_param_default(param.def_id)); + } } } } @@ -1110,7 +1114,7 @@ impl<'tcx> Visitor<'tcx> for NamePrivacyVisitor<'tcx> { //////////////////////////////////////////////////////////////////////////////////////////// /// Type privacy visitor, checks types for privacy and reports violations. -/// Both explicitly written types and inferred types of expressions and patters are checked. +/// Both explicitly written types and inferred types of expressions and patterns are checked. /// Checks are performed on "semantic" types regardless of names and their hygiene. //////////////////////////////////////////////////////////////////////////////////////////// @@ -1697,9 +1701,9 @@ impl<'a, 'tcx> Visitor<'tcx> for ObsoleteVisiblePrivateTypesVisitor<'a, 'tcx> { } } - fn visit_struct_field(&mut self, s: &'tcx hir::StructField<'tcx>) { + fn visit_field_def(&mut self, s: &'tcx hir::FieldDef<'tcx>) { if s.vis.node.is_pub() || self.in_variant { - intravisit::walk_struct_field(self, s); + intravisit::walk_field_def(self, s); } } @@ -1740,7 +1744,8 @@ impl SearchInterfaceForPrivateItemsVisitor<'tcx> { self.visit(self.tcx.type_of(param.def_id)); } } - GenericParamDefKind::Const => { + // FIXME(const_evaluatable_checked): May want to look inside const here + GenericParamDefKind::Const { .. } => { self.visit(self.tcx.type_of(param.def_id)); } } diff --git a/compiler/rustc_query_impl/Cargo.toml b/compiler/rustc_query_impl/Cargo.toml index c88b766a55a3e..383e30ca29fad 100644 --- a/compiler/rustc_query_impl/Cargo.toml +++ b/compiler/rustc_query_impl/Cargo.toml @@ -9,7 +9,7 @@ doctest = false [dependencies] measureme = "9.0.0" -rustc-rayon-core = "0.3.0" +rustc-rayon-core = "0.3.1" tracing = "0.1" rustc_ast = { path = "../rustc_ast" } rustc_attr = { path = "../rustc_attr" } diff --git a/compiler/rustc_query_impl/src/keys.rs b/compiler/rustc_query_impl/src/keys.rs index 1ae5bf12cabac..1fdb37398f996 100644 --- a/compiler/rustc_query_impl/src/keys.rs +++ b/compiler/rustc_query_impl/src/keys.rs @@ -228,6 +228,15 @@ impl<'tcx> Key for (&'tcx ty::Const<'tcx>, mir::Field) { } } +impl<'tcx> Key for mir::interpret::ConstAlloc<'tcx> { + fn query_crate(&self) -> CrateNum { + LOCAL_CRATE + } + fn default_span(&self, _: TyCtxt<'_>) -> Span { + DUMMY_SP + } +} + impl<'tcx> Key for ty::PolyTraitRef<'tcx> { fn query_crate(&self) -> CrateNum { self.def_id().krate @@ -246,6 +255,15 @@ impl<'tcx> Key for GenericArg<'tcx> { } } +impl<'tcx> Key for mir::ConstantKind<'tcx> { + fn query_crate(&self) -> CrateNum { + LOCAL_CRATE + } + fn default_span(&self, _: TyCtxt<'_>) -> Span { + DUMMY_SP + } +} + impl<'tcx> Key for &'tcx ty::Const<'tcx> { fn query_crate(&self) -> CrateNum { LOCAL_CRATE diff --git a/compiler/rustc_query_impl/src/lib.rs b/compiler/rustc_query_impl/src/lib.rs index e9314797fbdc5..00d886000faa2 100644 --- a/compiler/rustc_query_impl/src/lib.rs +++ b/compiler/rustc_query_impl/src/lib.rs @@ -19,8 +19,7 @@ extern crate tracing; use rustc_data_structures::fingerprint::Fingerprint; use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; use rustc_errors::{DiagnosticBuilder, Handler}; -use rustc_hir::def_id::CrateNum; -use rustc_index::vec::IndexVec; +use rustc_hir::def_id::LOCAL_CRATE; use rustc_middle::dep_graph; use rustc_middle::ich::StableHashingContext; use rustc_middle::ty::query::{query_keys, query_storage, query_stored, query_values}; diff --git a/compiler/rustc_query_impl/src/plumbing.rs b/compiler/rustc_query_impl/src/plumbing.rs index 37a176de94196..ee914fa1ba95c 100644 --- a/compiler/rustc_query_impl/src/plumbing.rs +++ b/compiler/rustc_query_impl/src/plumbing.rs @@ -390,13 +390,12 @@ macro_rules! define_queries { #[inline] fn compute(tcx: QueryCtxt<'tcx>, key: Self::Key) -> Self::Value { - let provider = tcx.queries.providers.get(key.query_crate()) - // HACK(eddyb) it's possible crates may be loaded after - // the query engine is created, and because crate loading - // is not yet integrated with the query engine, such crates - // would be missing appropriate entries in `providers`. - .unwrap_or(&tcx.queries.fallback_extern_providers) - .$name; + let is_local = key.query_crate() == LOCAL_CRATE; + let provider = if is_local { + tcx.queries.local_providers.$name + } else { + tcx.queries.extern_providers.$name + }; provider(*tcx, key) } @@ -439,6 +438,11 @@ macro_rules! define_queries { try_load_from_on_disk_cache: |_, _| {}, }; + pub const CompileMonoItem: QueryStruct = QueryStruct { + force_from_dep_node: |_, _| false, + try_load_from_on_disk_cache: |_, _| {}, + }; + $(pub const $name: QueryStruct = { const is_anon: bool = is_anon!([$($modifiers)*]); @@ -478,10 +482,7 @@ macro_rules! define_queries { return } - debug_assert!(tcx.dep_graph - .node_color(dep_node) - .map(|c| c.is_green()) - .unwrap_or(false)); + debug_assert!(tcx.dep_graph.is_green(dep_node)); let key = recover(*tcx, dep_node).unwrap_or_else(|| panic!("Failed to recover key for {:?} with hash {}", dep_node, dep_node.hash)); if queries::$name::cache_on_disk(tcx, &key, None) { @@ -507,8 +508,8 @@ macro_rules! define_queries_struct { (tcx: $tcx:tt, input: ($(([$($modifiers:tt)*] [$($attr:tt)*] [$name:ident]))*)) => { pub struct Queries<$tcx> { - providers: IndexVec, - fallback_extern_providers: Box, + local_providers: Box, + extern_providers: Box, $($(#[$attr])* $name: QueryState< crate::dep_graph::DepKind, @@ -518,12 +519,12 @@ macro_rules! define_queries_struct { impl<$tcx> Queries<$tcx> { pub fn new( - providers: IndexVec, - fallback_extern_providers: Providers, + local_providers: Providers, + extern_providers: Providers, ) -> Self { Queries { - providers, - fallback_extern_providers: Box::new(fallback_extern_providers), + local_providers: Box::new(local_providers), + extern_providers: Box::new(extern_providers), $($name: Default::default()),* } } diff --git a/compiler/rustc_query_system/Cargo.toml b/compiler/rustc_query_system/Cargo.toml index 7d3357f8fa2ef..19512dc1f646d 100644 --- a/compiler/rustc_query_system/Cargo.toml +++ b/compiler/rustc_query_system/Cargo.toml @@ -10,7 +10,7 @@ doctest = false [dependencies] rustc_arena = { path = "../rustc_arena" } tracing = "0.1" -rustc-rayon-core = "0.3.0" +rustc-rayon-core = "0.3.1" rustc_data_structures = { path = "../rustc_data_structures" } rustc_errors = { path = "../rustc_errors" } rustc_macros = { path = "../rustc_macros" } diff --git a/compiler/rustc_query_system/src/cache.rs b/compiler/rustc_query_system/src/cache.rs index c6dc7b4fe2851..d592812f79b6b 100644 --- a/compiler/rustc_query_system/src/cache.rs +++ b/compiler/rustc_query_system/src/cache.rs @@ -3,7 +3,6 @@ use crate::dep_graph::{DepContext, DepNodeIndex}; use rustc_data_structures::fx::FxHashMap; -use rustc_data_structures::sync::HashMapExt; use rustc_data_structures::sync::Lock; use std::hash::Hash; @@ -34,13 +33,6 @@ impl Cache { pub fn insert(&self, key: Key, dep_node: DepNodeIndex, value: Value) { self.hashmap.borrow_mut().insert(key, WithDepNode::new(dep_node, value)); } - - pub fn insert_same(&self, key: Key, dep_node: DepNodeIndex, value: Value) - where - Value: Eq, - { - self.hashmap.borrow_mut().insert_same(key, WithDepNode::new(dep_node, value)); - } } #[derive(Clone, Eq, PartialEq)] diff --git a/compiler/rustc_query_system/src/dep_graph/debug.rs b/compiler/rustc_query_system/src/dep_graph/debug.rs index 718a2f1039a4d..a544ac2c343ae 100644 --- a/compiler/rustc_query_system/src/dep_graph/debug.rs +++ b/compiler/rustc_query_system/src/dep_graph/debug.rs @@ -1,6 +1,8 @@ //! Code for debugging the dep-graph. -use super::{DepKind, DepNode}; +use super::{DepKind, DepNode, DepNodeIndex}; +use rustc_data_structures::fx::FxHashMap; +use rustc_data_structures::sync::Lock; use std::error::Error; /// A dep-node filter goes from a user-defined string to a query over @@ -34,13 +36,14 @@ impl DepNodeFilter { /// A filter like `F -> G` where `F` and `G` are valid dep-node /// filters. This can be used to test the source/target independently. -pub struct EdgeFilter { +pub struct EdgeFilter { pub source: DepNodeFilter, pub target: DepNodeFilter, + pub index_to_node: Lock>>, } -impl EdgeFilter { - pub fn new(test: &str) -> Result> { +impl EdgeFilter { + pub fn new(test: &str) -> Result, Box> { let parts: Vec<_> = test.split("->").collect(); if parts.len() != 2 { Err(format!("expected a filter like `a&b -> c&d`, not `{}`", test).into()) @@ -48,11 +51,13 @@ impl EdgeFilter { Ok(EdgeFilter { source: DepNodeFilter::new(parts[0]), target: DepNodeFilter::new(parts[1]), + index_to_node: Lock::new(FxHashMap::default()), }) } } - pub fn test(&self, source: &DepNode, target: &DepNode) -> bool { + #[cfg(debug_assertions)] + pub fn test(&self, source: &DepNode, target: &DepNode) -> bool { self.source.test(source) && self.target.test(target) } } diff --git a/compiler/rustc_query_system/src/dep_graph/dep_node.rs b/compiler/rustc_query_system/src/dep_graph/dep_node.rs index f55e2f777a26a..59ef6052a60c0 100644 --- a/compiler/rustc_query_system/src/dep_graph/dep_node.rs +++ b/compiler/rustc_query_system/src/dep_graph/dep_node.rs @@ -26,7 +26,7 @@ //! could not be instantiated because the current compilation session //! contained no `DefId` for thing that had been removed. //! -//! `DepNode` definition happens in `librustc_middle` with the `define_dep_nodes!()` macro. +//! `DepNode` definition happens in `rustc_middle` with the `define_dep_nodes!()` macro. //! This macro defines the `DepKind` enum and a corresponding `DepConstructor` enum. The //! `DepConstructor` enum links a `DepKind` to the parameters that are needed at runtime in order //! to construct a valid `DepNode` fingerprint. diff --git a/compiler/rustc_query_system/src/dep_graph/graph.rs b/compiler/rustc_query_system/src/dep_graph/graph.rs index 0f25572170f53..7a0fc320663f7 100644 --- a/compiler/rustc_query_system/src/dep_graph/graph.rs +++ b/compiler/rustc_query_system/src/dep_graph/graph.rs @@ -1,31 +1,33 @@ use rustc_data_structures::fingerprint::Fingerprint; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_data_structures::profiling::QueryInvocationId; +use rustc_data_structures::profiling::SelfProfilerRef; use rustc_data_structures::sharded::{self, Sharded}; use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; -use rustc_data_structures::sync::{AtomicU32, AtomicU64, Lock, LockGuard, Lrc, Ordering}; +use rustc_data_structures::steal::Steal; +use rustc_data_structures::sync::{AtomicU32, AtomicU64, Lock, Lrc, Ordering}; use rustc_data_structures::unlikely; use rustc_errors::Diagnostic; -use rustc_index::vec::{Idx, IndexVec}; -use rustc_serialize::{Encodable, Encoder}; +use rustc_index::vec::IndexVec; +use rustc_serialize::opaque::{FileEncodeResult, FileEncoder}; use parking_lot::{Condvar, Mutex}; use smallvec::{smallvec, SmallVec}; use std::collections::hash_map::Entry; -use std::env; use std::hash::Hash; use std::marker::PhantomData; use std::mem; -use std::ops::Range; use std::sync::atomic::Ordering::Relaxed; -use super::debug::EdgeFilter; use super::prev::PreviousDepGraph; use super::query::DepGraphQuery; -use super::serialized::SerializedDepNodeIndex; +use super::serialized::{GraphEncoder, SerializedDepNodeIndex}; use super::{DepContext, DepKind, DepNode, HasDepContext, WorkProductId}; use crate::query::QueryContext; +#[cfg(debug_assertions)] +use {super::debug::EdgeFilter, std::env}; + #[derive(Clone)] pub struct DepGraph { data: Option>>, @@ -109,6 +111,9 @@ impl DepGraph { pub fn new( prev_graph: PreviousDepGraph, prev_work_products: FxHashMap, + encoder: FileEncoder, + record_graph: bool, + record_stats: bool, ) -> DepGraph { let prev_graph_node_count = prev_graph.node_count(); @@ -116,7 +121,12 @@ impl DepGraph { data: Some(Lrc::new(DepGraphData { previous_work_products: prev_work_products, dep_node_debug: Default::default(), - current: CurrentDepGraph::new(prev_graph_node_count), + current: CurrentDepGraph::new( + prev_graph_node_count, + encoder, + record_graph, + record_stats, + ), emitting_diagnostics: Default::default(), emitting_diagnostics_cond_var: Condvar::new(), previous: prev_graph, @@ -136,62 +146,10 @@ impl DepGraph { self.data.is_some() } - pub fn query(&self) -> DepGraphQuery { - let data = self.data.as_ref().unwrap(); - let previous = &data.previous; - - // Note locking order: `prev_index_to_index`, then `data`. - let prev_index_to_index = data.current.prev_index_to_index.lock(); - let data = data.current.data.lock(); - let node_count = data.hybrid_indices.len(); - let edge_count = self.edge_count(&data); - - let mut nodes = Vec::with_capacity(node_count); - let mut edge_list_indices = Vec::with_capacity(node_count); - let mut edge_list_data = Vec::with_capacity(edge_count); - - // See `DepGraph`'s `Encodable` implementation for notes on the approach used here. - - edge_list_data.extend(data.unshared_edges.iter().map(|i| i.index())); - - for &hybrid_index in data.hybrid_indices.iter() { - match hybrid_index.into() { - HybridIndex::New(new_index) => { - nodes.push(data.new.nodes[new_index]); - let edges = &data.new.edges[new_index]; - edge_list_indices.push((edges.start.index(), edges.end.index())); - } - HybridIndex::Red(red_index) => { - nodes.push(previous.index_to_node(data.red.node_indices[red_index])); - let edges = &data.red.edges[red_index]; - edge_list_indices.push((edges.start.index(), edges.end.index())); - } - HybridIndex::LightGreen(lg_index) => { - nodes.push(previous.index_to_node(data.light_green.node_indices[lg_index])); - let edges = &data.light_green.edges[lg_index]; - edge_list_indices.push((edges.start.index(), edges.end.index())); - } - HybridIndex::DarkGreen(prev_index) => { - nodes.push(previous.index_to_node(prev_index)); - - let edges_iter = previous - .edge_targets_from(prev_index) - .iter() - .map(|&dst| prev_index_to_index[dst].unwrap().index()); - - let start = edge_list_data.len(); - edge_list_data.extend(edges_iter); - let end = edge_list_data.len(); - edge_list_indices.push((start, end)); - } - } + pub fn with_query(&self, f: impl Fn(&DepGraphQuery)) { + if let Some(data) = &self.data { + data.current.encoder.borrow().with_query(f) } - - debug_assert_eq!(nodes.len(), node_count); - debug_assert_eq!(edge_list_indices.len(), node_count); - debug_assert_eq!(edge_list_data.len(), edge_count); - - DepGraphQuery::new(&nodes[..], &edge_list_indices[..], &edge_list_data[..]) } pub fn assert_ignored(&self) { @@ -283,56 +241,16 @@ impl DepGraph { let print_status = cfg!(debug_assertions) && dcx.sess().opts.debugging_opts.dep_tasks; // Intern the new `DepNode`. - let dep_node_index = if let Some(prev_index) = data.previous.node_to_index_opt(&key) { - // Determine the color and index of the new `DepNode`. - let (color, dep_node_index) = if let Some(current_fingerprint) = current_fingerprint - { - if current_fingerprint == data.previous.fingerprint_by_index(prev_index) { - if print_status { - eprintln!("[task::green] {:?}", key); - } - - // This is a light green node: it existed in the previous compilation, - // its query was re-executed, and it has the same result as before. - let dep_node_index = - data.current.intern_light_green_node(&data.previous, prev_index, edges); - - (DepNodeColor::Green(dep_node_index), dep_node_index) - } else { - if print_status { - eprintln!("[task::red] {:?}", key); - } - - // This is a red node: it existed in the previous compilation, its query - // was re-executed, but it has a different result from before. - let dep_node_index = data.current.intern_red_node( - &data.previous, - prev_index, - edges, - current_fingerprint, - ); - - (DepNodeColor::Red, dep_node_index) - } - } else { - if print_status { - eprintln!("[task::unknown] {:?}", key); - } - - // This is a red node, effectively: it existed in the previous compilation - // session, its query was re-executed, but it doesn't compute a result hash - // (i.e. it represents a `no_hash` query), so we have no way of determining - // whether or not the result was the same as before. - let dep_node_index = data.current.intern_red_node( - &data.previous, - prev_index, - edges, - Fingerprint::ZERO, - ); - - (DepNodeColor::Red, dep_node_index) - }; + let (dep_node_index, prev_and_color) = data.current.intern_node( + dcx.profiler(), + &data.previous, + key, + edges, + current_fingerprint, + print_status, + ); + if let Some((prev_index, color)) = prev_and_color { debug_assert!( data.colors.get(prev_index).is_none(), "DepGraph::with_task() - Duplicate DepNodeColor \ @@ -341,20 +259,7 @@ impl DepGraph { ); data.colors.insert(prev_index, color); - dep_node_index - } else { - if print_status { - eprintln!("[task::new] {:?}", key); - } - - // This is a new node: it didn't exist in the previous compilation session. - data.current.intern_new_node( - &data.previous, - key, - edges, - current_fingerprint.unwrap_or(Fingerprint::ZERO), - ) - }; + } (result, dep_node_index) } else { @@ -368,7 +273,12 @@ impl DepGraph { /// Executes something within an "anonymous" task, that is, a task the /// `DepNode` of which is determined by the list of inputs it read from. - pub fn with_anon_task(&self, dep_kind: K, op: OP) -> (R, DepNodeIndex) + pub fn with_anon_task, OP, R>( + &self, + cx: Ctxt, + dep_kind: K, + op: OP, + ) -> (R, DepNodeIndex) where OP: FnOnce() -> R, { @@ -396,7 +306,7 @@ impl DepGraph { }; let dep_node_index = data.current.intern_new_node( - &data.previous, + cx.profiler(), target_dep_node, task_deps.reads, Fingerprint::ZERO, @@ -451,7 +361,7 @@ impl DepGraph { { if let Some(target) = task_deps.node { if let Some(ref forbidden_edge) = data.current.forbidden_edge { - let src = self.dep_node_of(dep_node_index); + let src = forbidden_edge.index_to_node.lock()[&dep_node_index]; if forbidden_edge.test(&src, &target) { panic!("forbidden edge {:?} -> {:?} created", src, target) } @@ -488,38 +398,6 @@ impl DepGraph { self.data.is_some() && self.dep_node_index_of_opt(dep_node).is_some() } - #[inline] - pub fn dep_node_of(&self, dep_node_index: DepNodeIndex) -> DepNode { - let data = self.data.as_ref().unwrap(); - let previous = &data.previous; - let data = data.current.data.lock(); - - match data.hybrid_indices[dep_node_index].into() { - HybridIndex::New(new_index) => data.new.nodes[new_index], - HybridIndex::Red(red_index) => previous.index_to_node(data.red.node_indices[red_index]), - HybridIndex::LightGreen(light_green_index) => { - previous.index_to_node(data.light_green.node_indices[light_green_index]) - } - HybridIndex::DarkGreen(prev_index) => previous.index_to_node(prev_index), - } - } - - #[inline] - pub fn fingerprint_of(&self, dep_node_index: DepNodeIndex) -> Fingerprint { - let data = self.data.as_ref().unwrap(); - let previous = &data.previous; - let data = data.current.data.lock(); - - match data.hybrid_indices[dep_node_index].into() { - HybridIndex::New(new_index) => data.new.fingerprints[new_index], - HybridIndex::Red(red_index) => data.red.fingerprints[red_index], - HybridIndex::LightGreen(light_green_index) => { - previous.fingerprint_by_index(data.light_green.node_indices[light_green_index]) - } - HybridIndex::DarkGreen(prev_index) => previous.fingerprint_by_index(prev_index), - } - } - pub fn prev_fingerprint_of(&self, dep_node: &DepNode) -> Option { self.data.as_ref().unwrap().previous.fingerprint_of(dep_node) } @@ -554,29 +432,13 @@ impl DepGraph { self.data.as_ref()?.dep_node_debug.borrow().get(&dep_node).cloned() } - fn edge_count(&self, node_data: &LockGuard<'_, DepNodeData>) -> usize { - let data = self.data.as_ref().unwrap(); - let previous = &data.previous; - - let mut edge_count = node_data.unshared_edges.len(); - - for &hybrid_index in node_data.hybrid_indices.iter() { - if let HybridIndex::DarkGreen(prev_index) = hybrid_index.into() { - edge_count += previous.edge_targets_from(prev_index).len() - } - } - - edge_count - } - - pub fn node_color(&self, dep_node: &DepNode) -> Option { + fn node_color(&self, dep_node: &DepNode) -> Option { if let Some(ref data) = self.data { if let Some(prev_index) = data.previous.node_to_index_opt(dep_node) { return data.colors.get(prev_index); } else { - // This is a node that did not exist in the previous compilation - // session, so we consider it to be red. - return Some(DepNodeColor::Red); + // This is a node that did not exist in the previous compilation session. + return None; } } @@ -775,11 +637,13 @@ impl DepGraph { // There may be multiple threads trying to mark the same dep node green concurrently - let dep_node_index = { - // We allocating an entry for the node in the current dependency graph and - // adding all the appropriate edges imported from the previous graph - data.current.intern_dark_green_node(&data.previous, prev_dep_node_index) - }; + // We allocating an entry for the node in the current dependency graph and + // adding all the appropriate edges imported from the previous graph + let dep_node_index = data.current.promote_node_and_deps_to_current( + tcx.dep_context().profiler(), + &data.previous, + prev_dep_node_index, + ); // ... emitting any stored diagnostic ... @@ -862,6 +726,12 @@ impl DepGraph { } } + // Returns true if the given node has been marked as red during the + // current compilation session. Used in various assertions + pub fn is_red(&self, dep_node: &DepNode) -> bool { + self.node_color(dep_node) == Some(DepNodeColor::Red) + } + // Returns true if the given node has been marked as green during the // current compilation session. Used in various assertions pub fn is_green(&self, dep_node: &DepNode) -> bool { @@ -911,106 +781,20 @@ impl DepGraph { } pub fn print_incremental_info(&self) { - #[derive(Clone)] - struct Stat { - kind: Kind, - node_counter: u64, - edge_counter: u64, + if let Some(data) = &self.data { + data.current.encoder.borrow().print_incremental_info( + data.current.total_read_count.load(Relaxed), + data.current.total_duplicate_read_count.load(Relaxed), + ) } + } - let data = self.data.as_ref().unwrap(); - let prev = &data.previous; - let current = &data.current; - let data = current.data.lock(); - - let mut stats: FxHashMap<_, Stat> = FxHashMap::with_hasher(Default::default()); - - for &hybrid_index in data.hybrid_indices.iter() { - let (kind, edge_count) = match hybrid_index.into() { - HybridIndex::New(new_index) => { - let kind = data.new.nodes[new_index].kind; - let edge_range = &data.new.edges[new_index]; - (kind, edge_range.end.as_usize() - edge_range.start.as_usize()) - } - HybridIndex::Red(red_index) => { - let kind = prev.index_to_node(data.red.node_indices[red_index]).kind; - let edge_range = &data.red.edges[red_index]; - (kind, edge_range.end.as_usize() - edge_range.start.as_usize()) - } - HybridIndex::LightGreen(lg_index) => { - let kind = prev.index_to_node(data.light_green.node_indices[lg_index]).kind; - let edge_range = &data.light_green.edges[lg_index]; - (kind, edge_range.end.as_usize() - edge_range.start.as_usize()) - } - HybridIndex::DarkGreen(prev_index) => { - let kind = prev.index_to_node(prev_index).kind; - let edge_count = prev.edge_targets_from(prev_index).len(); - (kind, edge_count) - } - }; - - let stat = stats.entry(kind).or_insert(Stat { kind, node_counter: 0, edge_counter: 0 }); - stat.node_counter += 1; - stat.edge_counter += edge_count as u64; - } - - let total_node_count = data.hybrid_indices.len(); - let total_edge_count = self.edge_count(&data); - - // Drop the lock guard. - std::mem::drop(data); - - let mut stats: Vec<_> = stats.values().cloned().collect(); - stats.sort_by_key(|s| -(s.node_counter as i64)); - - const SEPARATOR: &str = "[incremental] --------------------------------\ - ----------------------------------------------\ - ------------"; - - eprintln!("[incremental]"); - eprintln!("[incremental] DepGraph Statistics"); - eprintln!("{}", SEPARATOR); - eprintln!("[incremental]"); - eprintln!("[incremental] Total Node Count: {}", total_node_count); - eprintln!("[incremental] Total Edge Count: {}", total_edge_count); - - if cfg!(debug_assertions) { - let total_edge_reads = current.total_read_count.load(Relaxed); - let total_duplicate_edge_reads = current.total_duplicate_read_count.load(Relaxed); - - eprintln!("[incremental] Total Edge Reads: {}", total_edge_reads); - eprintln!("[incremental] Total Duplicate Edge Reads: {}", total_duplicate_edge_reads); - } - - eprintln!("[incremental]"); - - eprintln!( - "[incremental] {:<36}| {:<17}| {:<12}| {:<17}|", - "Node Kind", "Node Frequency", "Node Count", "Avg. Edge Count" - ); - - eprintln!( - "[incremental] -------------------------------------\ - |------------------\ - |-------------\ - |------------------|" - ); - - for stat in stats { - let node_kind_ratio = (100.0 * (stat.node_counter as f64)) / (total_node_count as f64); - let node_kind_avg_edges = (stat.edge_counter as f64) / (stat.node_counter as f64); - - eprintln!( - "[incremental] {:<36}|{:>16.1}% |{:>12} |{:>17.1} |", - format!("{:?}", stat.kind), - node_kind_ratio, - stat.node_counter, - node_kind_avg_edges, - ); + pub fn encode(&self, profiler: &SelfProfilerRef) -> FileEncodeResult { + if let Some(data) = &self.data { + data.current.encoder.steal().finish(profiler) + } else { + Ok(()) } - - eprintln!("{}", SEPARATOR); - eprintln!("[incremental]"); } fn next_virtual_depnode_index(&self) -> DepNodeIndex { @@ -1019,142 +803,6 @@ impl DepGraph { } } -impl> Encodable for DepGraph { - fn encode(&self, e: &mut E) -> Result<(), E::Error> { - // We used to serialize the dep graph by creating and serializing a `SerializedDepGraph` - // using data copied from the `DepGraph`. But copying created a large memory spike, so we - // now serialize directly from the `DepGraph` as if it's a `SerializedDepGraph`. Because we - // deserialize that data into a `SerializedDepGraph` in the next compilation session, we - // need `DepGraph`'s `Encodable` and `SerializedDepGraph`'s `Decodable` implementations to - // be in sync. If you update this encoding, be sure to update the decoding, and vice-versa. - - let data = self.data.as_ref().unwrap(); - let prev = &data.previous; - - // Note locking order: `prev_index_to_index`, then `data`. - let prev_index_to_index = data.current.prev_index_to_index.lock(); - let data = data.current.data.lock(); - let new = &data.new; - let red = &data.red; - let lg = &data.light_green; - - let node_count = data.hybrid_indices.len(); - let edge_count = self.edge_count(&data); - - // `rustc_middle::ty::query::OnDiskCache` expects nodes to be encoded in `DepNodeIndex` - // order. The edges in `edge_list_data` don't need to be in a particular order, as long as - // each node references its edges as a contiguous range within it. Therefore, we can encode - // `edge_list_data` directly from `unshared_edges`. It meets the above requirements, as - // each non-dark-green node already knows the range of edges to reference within it, which - // they'll encode in `edge_list_indices`. Dark green nodes, however, don't have their edges - // in `unshared_edges`, so need to add them to `edge_list_data`. - - use HybridIndex::*; - - // Encoded values (nodes, etc.) are explicitly typed below to avoid inadvertently - // serializing data in the wrong format (i.e. one incompatible with `SerializedDepGraph`). - e.emit_struct("SerializedDepGraph", 4, |e| { - e.emit_struct_field("nodes", 0, |e| { - // `SerializedDepGraph` expects this to be encoded as a sequence of `DepNode`s. - e.emit_seq(node_count, |e| { - for (seq_index, &hybrid_index) in data.hybrid_indices.iter().enumerate() { - let node: DepNode = match hybrid_index.into() { - New(i) => new.nodes[i], - Red(i) => prev.index_to_node(red.node_indices[i]), - LightGreen(i) => prev.index_to_node(lg.node_indices[i]), - DarkGreen(prev_index) => prev.index_to_node(prev_index), - }; - - e.emit_seq_elt(seq_index, |e| node.encode(e))?; - } - - Ok(()) - }) - })?; - - e.emit_struct_field("fingerprints", 1, |e| { - // `SerializedDepGraph` expects this to be encoded as a sequence of `Fingerprints`s. - e.emit_seq(node_count, |e| { - for (seq_index, &hybrid_index) in data.hybrid_indices.iter().enumerate() { - let fingerprint: Fingerprint = match hybrid_index.into() { - New(i) => new.fingerprints[i], - Red(i) => red.fingerprints[i], - LightGreen(i) => prev.fingerprint_by_index(lg.node_indices[i]), - DarkGreen(prev_index) => prev.fingerprint_by_index(prev_index), - }; - - e.emit_seq_elt(seq_index, |e| fingerprint.encode(e))?; - } - - Ok(()) - }) - })?; - - e.emit_struct_field("edge_list_indices", 2, |e| { - // `SerializedDepGraph` expects this to be encoded as a sequence of `(u32, u32)`s. - e.emit_seq(node_count, |e| { - // Dark green node edges start after the unshared (all other nodes') edges. - let mut dark_green_edge_index = data.unshared_edges.len(); - - for (seq_index, &hybrid_index) in data.hybrid_indices.iter().enumerate() { - let edge_indices: (u32, u32) = match hybrid_index.into() { - New(i) => (new.edges[i].start.as_u32(), new.edges[i].end.as_u32()), - Red(i) => (red.edges[i].start.as_u32(), red.edges[i].end.as_u32()), - LightGreen(i) => (lg.edges[i].start.as_u32(), lg.edges[i].end.as_u32()), - DarkGreen(prev_index) => { - let edge_count = prev.edge_targets_from(prev_index).len(); - let start = dark_green_edge_index as u32; - dark_green_edge_index += edge_count; - let end = dark_green_edge_index as u32; - (start, end) - } - }; - - e.emit_seq_elt(seq_index, |e| edge_indices.encode(e))?; - } - - assert_eq!(dark_green_edge_index, edge_count); - - Ok(()) - }) - })?; - - e.emit_struct_field("edge_list_data", 3, |e| { - // `SerializedDepGraph` expects this to be encoded as a sequence of - // `SerializedDepNodeIndex`. - e.emit_seq(edge_count, |e| { - for (seq_index, &edge) in data.unshared_edges.iter().enumerate() { - let serialized_edge = SerializedDepNodeIndex::new(edge.index()); - e.emit_seq_elt(seq_index, |e| serialized_edge.encode(e))?; - } - - let mut seq_index = data.unshared_edges.len(); - - for &hybrid_index in data.hybrid_indices.iter() { - if let DarkGreen(prev_index) = hybrid_index.into() { - for &edge in prev.edge_targets_from(prev_index) { - // Dark green node edges are stored in the previous graph - // and must be converted to edges in the current graph, - // and then serialized as `SerializedDepNodeIndex`. - let serialized_edge = SerializedDepNodeIndex::new( - prev_index_to_index[edge].as_ref().unwrap().index(), - ); - - e.emit_seq_elt(seq_index, |e| serialized_edge.encode(e))?; - seq_index += 1; - } - } - } - - assert_eq!(seq_index, edge_count); - - Ok(()) - }) - }) - }) - } -} - /// A "work product" is an intermediate result that we save into the /// incremental directory for later re-use. The primary example are /// the object files that we save for each partition at code @@ -1193,216 +841,20 @@ pub struct WorkProduct { pub saved_file: Option, } -// The maximum value of the follow index types leaves the upper two bits unused -// so that we can store multiple index types in `CompressedHybridIndex`, and use -// those bits to encode which index type it contains. - -// Index type for `NewDepNodeData`. -rustc_index::newtype_index! { - struct NewDepNodeIndex { - MAX = 0x7FFF_FFFF - } -} - -// Index type for `RedDepNodeData`. -rustc_index::newtype_index! { - struct RedDepNodeIndex { - MAX = 0x7FFF_FFFF - } -} - -// Index type for `LightGreenDepNodeData`. -rustc_index::newtype_index! { - struct LightGreenDepNodeIndex { - MAX = 0x7FFF_FFFF - } -} - -/// Compressed representation of `HybridIndex` enum. Bits unused by the -/// contained index types are used to encode which index type it contains. -#[derive(Copy, Clone)] -struct CompressedHybridIndex(u32); - -impl CompressedHybridIndex { - const NEW_TAG: u32 = 0b0000_0000_0000_0000_0000_0000_0000_0000; - const RED_TAG: u32 = 0b0100_0000_0000_0000_0000_0000_0000_0000; - const LIGHT_GREEN_TAG: u32 = 0b1000_0000_0000_0000_0000_0000_0000_0000; - const DARK_GREEN_TAG: u32 = 0b1100_0000_0000_0000_0000_0000_0000_0000; - - const TAG_MASK: u32 = 0b1100_0000_0000_0000_0000_0000_0000_0000; - const INDEX_MASK: u32 = !Self::TAG_MASK; -} - -impl From for CompressedHybridIndex { - #[inline] - fn from(index: NewDepNodeIndex) -> Self { - CompressedHybridIndex(Self::NEW_TAG | index.as_u32()) - } -} - -impl From for CompressedHybridIndex { - #[inline] - fn from(index: RedDepNodeIndex) -> Self { - CompressedHybridIndex(Self::RED_TAG | index.as_u32()) - } -} - -impl From for CompressedHybridIndex { - #[inline] - fn from(index: LightGreenDepNodeIndex) -> Self { - CompressedHybridIndex(Self::LIGHT_GREEN_TAG | index.as_u32()) - } -} - -impl From for CompressedHybridIndex { - #[inline] - fn from(index: SerializedDepNodeIndex) -> Self { - CompressedHybridIndex(Self::DARK_GREEN_TAG | index.as_u32()) - } -} - -/// Contains an index into one of several node data collections. Elsewhere, we -/// store `CompressedHyridIndex` instead of this to save space, but convert to -/// this type during processing to take advantage of the enum match ergonomics. -enum HybridIndex { - New(NewDepNodeIndex), - Red(RedDepNodeIndex), - LightGreen(LightGreenDepNodeIndex), - DarkGreen(SerializedDepNodeIndex), -} - -impl From for HybridIndex { - #[inline] - fn from(hybrid_index: CompressedHybridIndex) -> Self { - let index = hybrid_index.0 & CompressedHybridIndex::INDEX_MASK; - - match hybrid_index.0 & CompressedHybridIndex::TAG_MASK { - CompressedHybridIndex::NEW_TAG => HybridIndex::New(NewDepNodeIndex::from_u32(index)), - CompressedHybridIndex::RED_TAG => HybridIndex::Red(RedDepNodeIndex::from_u32(index)), - CompressedHybridIndex::LIGHT_GREEN_TAG => { - HybridIndex::LightGreen(LightGreenDepNodeIndex::from_u32(index)) - } - CompressedHybridIndex::DARK_GREEN_TAG => { - HybridIndex::DarkGreen(SerializedDepNodeIndex::from_u32(index)) - } - _ => unreachable!(), - } - } -} - // Index type for `DepNodeData`'s edges. rustc_index::newtype_index! { struct EdgeIndex { .. } } -/// Data for nodes in the current graph, divided into different collections -/// based on their presence in the previous graph, and if present, their color. -/// We divide nodes this way because different types of nodes are able to share -/// more or less data with the previous graph. -/// -/// To enable more sharing, we distinguish between two kinds of green nodes. -/// Light green nodes are nodes in the previous graph that have been marked -/// green because we re-executed their queries and the results were the same as -/// in the previous session. Dark green nodes are nodes in the previous graph -/// that have been marked green because we were able to mark all of their -/// dependencies green. -/// -/// Both light and dark green nodes can share the dep node and fingerprint with -/// the previous graph, but for light green nodes, we can't be sure that the -/// edges may be shared without comparing them against the previous edges, so we -/// store them directly (an approach in which we compare edges with the previous -/// edges to see if they can be shared was evaluated, but was not found to be -/// very profitable). -/// -/// For dark green nodes, we can share everything with the previous graph, which -/// is why the `HybridIndex::DarkGreen` enum variant contains the index of the -/// node in the previous graph, and why we don't have a separate collection for -/// dark green node data--the collection is the `PreviousDepGraph` itself. -/// -/// (Note that for dark green nodes, the edges in the previous graph -/// (`SerializedDepNodeIndex`s) must be converted to edges in the current graph -/// (`DepNodeIndex`s). `CurrentDepGraph` contains `prev_index_to_index`, which -/// can perform this conversion. It should always be possible, as by definition, -/// a dark green node is one whose dependencies from the previous session have -/// all been marked green--which means `prev_index_to_index` contains them.) -/// -/// Node data is stored in parallel vectors to eliminate the padding between -/// elements that would be needed to satisfy alignment requirements of the -/// structure that would contain all of a node's data. We could group tightly -/// packing subsets of node data together and use fewer vectors, but for -/// consistency's sake, we use separate vectors for each piece of data. -struct DepNodeData { - /// Data for nodes not in previous graph. - new: NewDepNodeData, - - /// Data for nodes in previous graph that have been marked red. - red: RedDepNodeData, - - /// Data for nodes in previous graph that have been marked light green. - light_green: LightGreenDepNodeData, - - // Edges for all nodes other than dark-green ones. Edges for each node - // occupy a contiguous region of this collection, which a node can reference - // using two indices. Storing edges this way rather than using an `EdgesVec` - // for each node reduces memory consumption by a not insignificant amount - // when compiling large crates. The downside is that we have to copy into - // this collection the edges from the `EdgesVec`s that are built up during - // query execution. But this is mostly balanced out by the more efficient - // implementation of `DepGraph::serialize` enabled by this representation. - unshared_edges: IndexVec, - - /// Mapping from `DepNodeIndex` to an index into a collection above. - /// Indicates which of the above collections contains a node's data. - /// - /// This collection is wasteful in time and space during incr-full builds, - /// because for those, all nodes are new. However, the waste is relatively - /// small, and the maintenance cost of avoiding using this for incr-full - /// builds is somewhat high and prone to bugginess. It does not seem worth - /// it at the time of this writing, but we may want to revisit the idea. - hybrid_indices: IndexVec, -} - -/// Data for nodes not in previous graph. Since we cannot share any data with -/// the previous graph, so we must store all of such a node's data here. -struct NewDepNodeData { - nodes: IndexVec>, - edges: IndexVec>, - fingerprints: IndexVec, -} - -/// Data for nodes in previous graph that have been marked red. We can share the -/// dep node with the previous graph, but the edges may be different, and the -/// fingerprint is known to be different, so we store the latter two directly. -struct RedDepNodeData { - node_indices: IndexVec, - edges: IndexVec>, - fingerprints: IndexVec, -} - -/// Data for nodes in previous graph that have been marked green because we -/// re-executed their queries and the results were the same as in the previous -/// session. We can share the dep node and the fingerprint with the previous -/// graph, but the edges may be different, so we store them directly. -struct LightGreenDepNodeData { - node_indices: IndexVec, - edges: IndexVec>, -} - /// `CurrentDepGraph` stores the dependency graph for the current session. It /// will be populated as we run queries or tasks. We never remove nodes from the /// graph: they are only added. /// -/// The nodes in it are identified by a `DepNodeIndex`. Internally, this maps to -/// a `HybridIndex`, which identifies which collection in the `data` field -/// contains a node's data. Which collection is used for a node depends on -/// whether the node was present in the `PreviousDepGraph`, and if so, the color -/// of the node. Each type of node can share more or less data with the previous -/// graph. When possible, we can store just the index of the node in the -/// previous graph, rather than duplicating its data in our own collections. -/// This is important, because these graph structures are some of the largest in -/// the compiler. +/// The nodes in it are identified by a `DepNodeIndex`. We avoid keeping the nodes +/// in memory. This is important, because these graph structures are some of the +/// largest in the compiler. /// -/// For the same reason, we also avoid storing `DepNode`s more than once as map +/// For this reason, we avoid storing `DepNode`s more than once as map /// keys. The `new_node_to_index` map only contains nodes not in the previous /// graph, and we map nodes in the previous graph to indices via a two-step /// mapping. `PreviousDepGraph` maps from `DepNode` to `SerializedDepNodeIndex`, @@ -1417,15 +869,15 @@ struct LightGreenDepNodeData { /// `new_node_to_index` and `data`, or `prev_index_to_index` and `data`. When /// manipulating both, we acquire `new_node_to_index` or `prev_index_to_index` /// first, and `data` second. -pub(super) struct CurrentDepGraph { - data: Lock>, +pub(super) struct CurrentDepGraph { + encoder: Steal>, new_node_to_index: Sharded, DepNodeIndex>>, prev_index_to_index: Lock>>, /// Used to trap when a specific edge is added to the graph. /// This is used for debug purposes and is only active with `debug_assertions`. - #[allow(dead_code)] - forbidden_edge: Option, + #[cfg(debug_assertions)] + forbidden_edge: Option>, /// Anonymous `DepNode`s are nodes whose IDs we compute from the list of /// their edges. This has the beneficial side-effect that multiple anonymous @@ -1447,7 +899,12 @@ pub(super) struct CurrentDepGraph { } impl CurrentDepGraph { - fn new(prev_graph_node_count: usize) -> CurrentDepGraph { + fn new( + prev_graph_node_count: usize, + encoder: FileEncoder, + record_graph: bool, + record_stats: bool, + ) -> CurrentDepGraph { use std::time::{SystemTime, UNIX_EPOCH}; let duration = SystemTime::now().duration_since(UNIX_EPOCH).unwrap(); @@ -1455,70 +912,29 @@ impl CurrentDepGraph { let mut stable_hasher = StableHasher::new(); nanos.hash(&mut stable_hasher); - let forbidden_edge = if cfg!(debug_assertions) { - match env::var("RUST_FORBID_DEP_GRAPH_EDGE") { - Ok(s) => match EdgeFilter::new(&s) { - Ok(f) => Some(f), - Err(err) => panic!("RUST_FORBID_DEP_GRAPH_EDGE invalid: {}", err), - }, - Err(_) => None, - } - } else { - None + #[cfg(debug_assertions)] + let forbidden_edge = match env::var("RUST_FORBID_DEP_GRAPH_EDGE") { + Ok(s) => match EdgeFilter::new(&s) { + Ok(f) => Some(f), + Err(err) => panic!("RUST_FORBID_DEP_GRAPH_EDGE invalid: {}", err), + }, + Err(_) => None, }; - // Pre-allocate the dep node structures. We over-allocate a little so - // that we hopefully don't have to re-allocate during this compilation - // session. The over-allocation for new nodes is 2% plus a small - // constant to account for the fact that in very small crates 2% might - // not be enough. The allocation for red and green node data doesn't - // include a constant, as we don't want to allocate anything for these - // structures during full incremental builds, where they aren't used. - // - // These estimates are based on the distribution of node and edge counts - // seen in rustc-perf benchmarks, adjusted somewhat to account for the - // fact that these benchmarks aren't perfectly representative. - // - // FIXME Use a collection type that doesn't copy node and edge data and - // grow multiplicatively on reallocation. Without such a collection or - // solution having the same effect, there is a performance hazard here - // in both time and space, as growing these collections means copying a - // large amount of data and doubling already large buffer capacities. A - // solution for this will also mean that it's less important to get - // these estimates right. - let new_node_count_estimate = (prev_graph_node_count * 2) / 100 + 200; - let red_node_count_estimate = (prev_graph_node_count * 3) / 100; - let light_green_node_count_estimate = (prev_graph_node_count * 25) / 100; - let total_node_count_estimate = prev_graph_node_count + new_node_count_estimate; - - let average_edges_per_node_estimate = 6; - let unshared_edge_count_estimate = average_edges_per_node_estimate - * (new_node_count_estimate + red_node_count_estimate + light_green_node_count_estimate); - // We store a large collection of these in `prev_index_to_index` during // non-full incremental builds, and want to ensure that the element size // doesn't inadvertently increase. static_assert_size!(Option, 4); + let new_node_count_estimate = 102 * prev_graph_node_count / 100 + 200; + CurrentDepGraph { - data: Lock::new(DepNodeData { - new: NewDepNodeData { - nodes: IndexVec::with_capacity(new_node_count_estimate), - edges: IndexVec::with_capacity(new_node_count_estimate), - fingerprints: IndexVec::with_capacity(new_node_count_estimate), - }, - red: RedDepNodeData { - node_indices: IndexVec::with_capacity(red_node_count_estimate), - edges: IndexVec::with_capacity(red_node_count_estimate), - fingerprints: IndexVec::with_capacity(red_node_count_estimate), - }, - light_green: LightGreenDepNodeData { - node_indices: IndexVec::with_capacity(light_green_node_count_estimate), - edges: IndexVec::with_capacity(light_green_node_count_estimate), - }, - unshared_edges: IndexVec::with_capacity(unshared_edge_count_estimate), - hybrid_indices: IndexVec::with_capacity(total_node_count_estimate), - }), + encoder: Steal::new(GraphEncoder::new( + encoder, + prev_graph_node_count, + record_graph, + record_stats, + )), new_node_to_index: Sharded::new(|| { FxHashMap::with_capacity_and_hasher( new_node_count_estimate / sharded::SHARDS, @@ -1527,89 +943,143 @@ impl CurrentDepGraph { }), prev_index_to_index: Lock::new(IndexVec::from_elem_n(None, prev_graph_node_count)), anon_id_seed: stable_hasher.finish(), + #[cfg(debug_assertions)] forbidden_edge, total_read_count: AtomicU64::new(0), total_duplicate_read_count: AtomicU64::new(0), } } + #[cfg(debug_assertions)] + fn record_edge(&self, dep_node_index: DepNodeIndex, key: DepNode) { + if let Some(forbidden_edge) = &self.forbidden_edge { + forbidden_edge.index_to_node.lock().insert(dep_node_index, key); + } + } + + /// Writes the node to the current dep-graph and allocates a `DepNodeIndex` for it. + /// Assumes that this is a node that has no equivalent in the previous dep-graph. fn intern_new_node( &self, - prev_graph: &PreviousDepGraph, - dep_node: DepNode, + profiler: &SelfProfilerRef, + key: DepNode, edges: EdgesVec, - fingerprint: Fingerprint, + current_fingerprint: Fingerprint, ) -> DepNodeIndex { - debug_assert!( - prev_graph.node_to_index_opt(&dep_node).is_none(), - "node in previous graph should be interned using one \ - of `intern_red_node`, `intern_light_green_node`, etc." - ); - - match self.new_node_to_index.get_shard_by_value(&dep_node).lock().entry(dep_node) { + match self.new_node_to_index.get_shard_by_value(&key).lock().entry(key) { Entry::Occupied(entry) => *entry.get(), Entry::Vacant(entry) => { - let data = &mut *self.data.lock(); - let new_index = data.new.nodes.push(dep_node); - add_edges(&mut data.unshared_edges, &mut data.new.edges, edges); - data.new.fingerprints.push(fingerprint); - let dep_node_index = data.hybrid_indices.push(new_index.into()); + let dep_node_index = + self.encoder.borrow().send(profiler, key, current_fingerprint, edges); entry.insert(dep_node_index); + #[cfg(debug_assertions)] + self.record_edge(dep_node_index, key); dep_node_index } } } - fn intern_red_node( + fn intern_node( &self, + profiler: &SelfProfilerRef, prev_graph: &PreviousDepGraph, - prev_index: SerializedDepNodeIndex, + key: DepNode, edges: EdgesVec, - fingerprint: Fingerprint, - ) -> DepNodeIndex { - self.debug_assert_not_in_new_nodes(prev_graph, prev_index); + fingerprint: Option, + print_status: bool, + ) -> (DepNodeIndex, Option<(SerializedDepNodeIndex, DepNodeColor)>) { + let print_status = cfg!(debug_assertions) && print_status; + + if let Some(prev_index) = prev_graph.node_to_index_opt(&key) { + // Determine the color and index of the new `DepNode`. + if let Some(fingerprint) = fingerprint { + if fingerprint == prev_graph.fingerprint_by_index(prev_index) { + if print_status { + eprintln!("[task::green] {:?}", key); + } - let mut prev_index_to_index = self.prev_index_to_index.lock(); + // This is a green node: it existed in the previous compilation, + // its query was re-executed, and it has the same result as before. + let mut prev_index_to_index = self.prev_index_to_index.lock(); + + let dep_node_index = match prev_index_to_index[prev_index] { + Some(dep_node_index) => dep_node_index, + None => { + let dep_node_index = + self.encoder.borrow().send(profiler, key, fingerprint, edges); + prev_index_to_index[prev_index] = Some(dep_node_index); + dep_node_index + } + }; - match prev_index_to_index[prev_index] { - Some(dep_node_index) => dep_node_index, - None => { - let data = &mut *self.data.lock(); - let red_index = data.red.node_indices.push(prev_index); - add_edges(&mut data.unshared_edges, &mut data.red.edges, edges); - data.red.fingerprints.push(fingerprint); - let dep_node_index = data.hybrid_indices.push(red_index.into()); - prev_index_to_index[prev_index] = Some(dep_node_index); - dep_node_index - } - } - } + #[cfg(debug_assertions)] + self.record_edge(dep_node_index, key); + (dep_node_index, Some((prev_index, DepNodeColor::Green(dep_node_index)))) + } else { + if print_status { + eprintln!("[task::red] {:?}", key); + } - fn intern_light_green_node( - &self, - prev_graph: &PreviousDepGraph, - prev_index: SerializedDepNodeIndex, - edges: EdgesVec, - ) -> DepNodeIndex { - self.debug_assert_not_in_new_nodes(prev_graph, prev_index); + // This is a red node: it existed in the previous compilation, its query + // was re-executed, but it has a different result from before. + let mut prev_index_to_index = self.prev_index_to_index.lock(); + + let dep_node_index = match prev_index_to_index[prev_index] { + Some(dep_node_index) => dep_node_index, + None => { + let dep_node_index = + self.encoder.borrow().send(profiler, key, fingerprint, edges); + prev_index_to_index[prev_index] = Some(dep_node_index); + dep_node_index + } + }; - let mut prev_index_to_index = self.prev_index_to_index.lock(); + #[cfg(debug_assertions)] + self.record_edge(dep_node_index, key); + (dep_node_index, Some((prev_index, DepNodeColor::Red))) + } + } else { + if print_status { + eprintln!("[task::unknown] {:?}", key); + } - match prev_index_to_index[prev_index] { - Some(dep_node_index) => dep_node_index, - None => { - let data = &mut *self.data.lock(); - let light_green_index = data.light_green.node_indices.push(prev_index); - add_edges(&mut data.unshared_edges, &mut data.light_green.edges, edges); - let dep_node_index = data.hybrid_indices.push(light_green_index.into()); - prev_index_to_index[prev_index] = Some(dep_node_index); - dep_node_index + // This is a red node, effectively: it existed in the previous compilation + // session, its query was re-executed, but it doesn't compute a result hash + // (i.e. it represents a `no_hash` query), so we have no way of determining + // whether or not the result was the same as before. + let mut prev_index_to_index = self.prev_index_to_index.lock(); + + let dep_node_index = match prev_index_to_index[prev_index] { + Some(dep_node_index) => dep_node_index, + None => { + let dep_node_index = + self.encoder.borrow().send(profiler, key, Fingerprint::ZERO, edges); + prev_index_to_index[prev_index] = Some(dep_node_index); + dep_node_index + } + }; + + #[cfg(debug_assertions)] + self.record_edge(dep_node_index, key); + (dep_node_index, Some((prev_index, DepNodeColor::Red))) + } + } else { + if print_status { + eprintln!("[task::new] {:?}", key); } + + let fingerprint = fingerprint.unwrap_or(Fingerprint::ZERO); + + // This is a new node: it didn't exist in the previous compilation session. + let dep_node_index = self.intern_new_node(profiler, key, edges, fingerprint); + + (dep_node_index, None) } } - fn intern_dark_green_node( + fn promote_node_and_deps_to_current( &self, + profiler: &SelfProfilerRef, prev_graph: &PreviousDepGraph, prev_index: SerializedDepNodeIndex, ) -> DepNodeIndex { @@ -1620,9 +1090,20 @@ impl CurrentDepGraph { match prev_index_to_index[prev_index] { Some(dep_node_index) => dep_node_index, None => { - let mut data = self.data.lock(); - let dep_node_index = data.hybrid_indices.push(prev_index.into()); + let key = prev_graph.index_to_node(prev_index); + let dep_node_index = self.encoder.borrow().send( + profiler, + key, + prev_graph.fingerprint_by_index(prev_index), + prev_graph + .edge_targets_from(prev_index) + .iter() + .map(|i| prev_index_to_index[*i].unwrap()) + .collect(), + ); prev_index_to_index[prev_index] = Some(dep_node_index); + #[cfg(debug_assertions)] + self.record_edge(dep_node_index, key); dep_node_index } } @@ -1642,18 +1123,6 @@ impl CurrentDepGraph { } } -#[inline] -fn add_edges( - edges: &mut IndexVec, - edge_indices: &mut IndexVec>, - new_edges: EdgesVec, -) { - let start = edges.next_index(); - edges.extend(new_edges); - let end = edges.next_index(); - edge_indices.push(start..end); -} - /// The capacity of the `reads` field `SmallVec` const TASK_DEPS_READS_CAP: usize = 8; type EdgesVec = SmallVec<[DepNodeIndex; TASK_DEPS_READS_CAP]>; diff --git a/compiler/rustc_query_system/src/dep_graph/mod.rs b/compiler/rustc_query_system/src/dep_graph/mod.rs index e8fb71be3e08f..1b6ecf3e637f3 100644 --- a/compiler/rustc_query_system/src/dep_graph/mod.rs +++ b/compiler/rustc_query_system/src/dep_graph/mod.rs @@ -13,6 +13,7 @@ pub use serialized::{SerializedDepGraph, SerializedDepNodeIndex}; use rustc_data_structures::profiling::SelfProfilerRef; use rustc_data_structures::sync::Lock; +use rustc_serialize::{opaque::FileEncoder, Encodable}; use rustc_session::Session; use std::fmt; @@ -59,7 +60,7 @@ impl HasDepContext for T { } /// Describe the different families of dependency nodes. -pub trait DepKind: Copy + fmt::Debug + Eq + Hash { +pub trait DepKind: Copy + fmt::Debug + Eq + Hash + Send + Encodable + 'static { const NULL: Self; /// Return whether this kind always require evaluation. diff --git a/compiler/rustc_query_system/src/dep_graph/prev.rs b/compiler/rustc_query_system/src/dep_graph/prev.rs index c3d0f79525572..6303bbf53b9c4 100644 --- a/compiler/rustc_query_system/src/dep_graph/prev.rs +++ b/compiler/rustc_query_system/src/dep_graph/prev.rs @@ -35,11 +35,6 @@ impl PreviousDepGraph { self.data.nodes[dep_node_index] } - #[inline] - pub fn node_to_index(&self, dep_node: &DepNode) -> SerializedDepNodeIndex { - self.index[dep_node] - } - #[inline] pub fn node_to_index_opt(&self, dep_node: &DepNode) -> Option { self.index.get(dep_node).cloned() diff --git a/compiler/rustc_query_system/src/dep_graph/query.rs b/compiler/rustc_query_system/src/dep_graph/query.rs index cc25d08cb54f4..27b3b5e13667e 100644 --- a/compiler/rustc_query_system/src/dep_graph/query.rs +++ b/compiler/rustc_query_system/src/dep_graph/query.rs @@ -1,38 +1,43 @@ use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::graph::implementation::{Direction, Graph, NodeIndex, INCOMING}; +use rustc_index::vec::IndexVec; -use super::{DepKind, DepNode}; +use super::{DepKind, DepNode, DepNodeIndex}; pub struct DepGraphQuery { pub graph: Graph, ()>, pub indices: FxHashMap, NodeIndex>, + pub dep_index_to_index: IndexVec>, } impl DepGraphQuery { - pub fn new( - nodes: &[DepNode], - edge_list_indices: &[(usize, usize)], - edge_list_data: &[usize], - ) -> DepGraphQuery { - let mut graph = Graph::with_capacity(nodes.len(), edge_list_data.len()); - let mut indices = FxHashMap::default(); - for node in nodes { - indices.insert(*node, graph.add_node(*node)); - } + pub fn new(prev_node_count: usize) -> DepGraphQuery { + let node_count = prev_node_count + prev_node_count / 4; + let edge_count = 6 * node_count; - for (source, &(start, end)) in edge_list_indices.iter().enumerate() { - for &target in &edge_list_data[start..end] { - let source = indices[&nodes[source]]; - let target = indices[&nodes[target]]; - graph.add_edge(source, target, ()); - } - } + let graph = Graph::with_capacity(node_count, edge_count); + let indices = FxHashMap::default(); + let dep_index_to_index = IndexVec::new(); - DepGraphQuery { graph, indices } + DepGraphQuery { graph, indices, dep_index_to_index } } - pub fn contains_node(&self, node: &DepNode) -> bool { - self.indices.contains_key(&node) + pub fn push(&mut self, index: DepNodeIndex, node: DepNode, edges: &[DepNodeIndex]) { + let source = self.graph.add_node(node); + if index.index() >= self.dep_index_to_index.len() { + self.dep_index_to_index.resize(index.index() + 1, None); + } + self.dep_index_to_index[index] = Some(source); + self.indices.insert(node, source); + + for &target in edges.iter() { + let target = self.dep_index_to_index[target]; + // We may miss the edges that are pushed while the `DepGraphQuery` is being accessed. + // Skip them to issues. + if let Some(target) = target { + self.graph.add_edge(source, target, ()); + } + } } pub fn nodes(&self) -> Vec<&DepNode> { diff --git a/compiler/rustc_query_system/src/dep_graph/serialized.rs b/compiler/rustc_query_system/src/dep_graph/serialized.rs index 9bb922b0a9008..6f3d1fb71994e 100644 --- a/compiler/rustc_query_system/src/dep_graph/serialized.rs +++ b/compiler/rustc_query_system/src/dep_graph/serialized.rs @@ -1,9 +1,28 @@ //! The data that we will serialize and deserialize. +//! +//! The dep-graph is serialized as a sequence of NodeInfo, with the dependencies +//! specified inline. The total number of nodes and edges are stored as the last +//! 16 bytes of the file, so we can find them easily at decoding time. +//! +//! The serialisation is performed on-demand when each node is emitted. Using this +//! scheme, we do not need to keep the current graph in memory. +//! +//! The deserisalisation is performed manually, in order to convert from the stored +//! sequence of NodeInfos to the different arrays in SerializedDepGraph. Since the +//! node and edge count are stored at the end of the file, all the arrays can be +//! pre-allocated with the right length. -use super::{DepKind, DepNode}; +use super::query::DepGraphQuery; +use super::{DepKind, DepNode, DepNodeIndex}; use rustc_data_structures::fingerprint::Fingerprint; -use rustc_index::vec::IndexVec; -use rustc_serialize::{Decodable, Decoder}; +use rustc_data_structures::fx::FxHashMap; +use rustc_data_structures::profiling::SelfProfilerRef; +use rustc_data_structures::sync::Lock; +use rustc_index::vec::{Idx, IndexVec}; +use rustc_serialize::opaque::{self, FileEncodeResult, FileEncoder, IntEncodedWithFixedSize}; +use rustc_serialize::{Decodable, Decoder, Encodable}; +use smallvec::SmallVec; +use std::convert::TryInto; // The maximum value of `SerializedDepNodeIndex` leaves the upper two bits // unused so that we can store multiple index types in `CompressedHybridIndex`, @@ -50,78 +69,239 @@ impl SerializedDepGraph { } } -impl> Decodable for SerializedDepGraph { - fn decode(d: &mut D) -> Result, D::Error> { - // We used to serialize the dep graph by creating and serializing a `SerializedDepGraph` - // using data copied from the `DepGraph`. But copying created a large memory spike, so we - // now serialize directly from the `DepGraph` as if it's a `SerializedDepGraph`. Because we - // deserialize that data into a `SerializedDepGraph` in the next compilation session, we - // need `DepGraph`'s `Encodable` and `SerializedDepGraph`'s `Decodable` implementations to - // be in sync. If you update this decoding, be sure to update the encoding, and vice-versa. - // - // We mimic the sequence of `Encode` and `Encodable` method calls used by the `DepGraph`'s - // `Encodable` implementation with the corresponding sequence of `Decode` and `Decodable` - // method calls. E.g. `Decode::read_struct` pairs with `Encode::emit_struct`, `DepNode`'s - // `decode` pairs with `DepNode`'s `encode`, and so on. Any decoding methods not associated - // with corresponding encoding methods called in `DepGraph`'s `Encodable` implementation - // are off limits, because we'd be relying on their implementation details. - // - // For example, because we know it happens to do the right thing, its tempting to just use - // `IndexVec`'s `Decodable` implementation to decode into some of the collections below, - // even though `DepGraph` doesn't use its `Encodable` implementation. But the `IndexVec` - // implementation could change, and we'd have a bug. - // - // Variables below are explicitly typed so that anyone who changes the `SerializedDepGraph` - // representation without updating this function will encounter a compilation error, and - // know to update this and possibly the `DepGraph` `Encodable` implementation accordingly - // (the latter should serialize data in a format compatible with our representation). - - d.read_struct("SerializedDepGraph", 4, |d| { - let nodes: IndexVec> = - d.read_struct_field("nodes", 0, |d| { - d.read_seq(|d, len| { - let mut v = IndexVec::with_capacity(len); - for i in 0..len { - v.push(d.read_seq_elt(i, |d| Decodable::decode(d))?); - } - Ok(v) - }) - })?; +impl<'a, K: DepKind + Decodable>> Decodable> + for SerializedDepGraph +{ + #[instrument(skip(d))] + fn decode(d: &mut opaque::Decoder<'a>) -> Result, String> { + let start_position = d.position(); - let fingerprints: IndexVec = - d.read_struct_field("fingerprints", 1, |d| { - d.read_seq(|d, len| { - let mut v = IndexVec::with_capacity(len); - for i in 0..len { - v.push(d.read_seq_elt(i, |d| Decodable::decode(d))?); - } - Ok(v) - }) - })?; + // The last 16 bytes are the node count and edge count. + debug!("position: {:?}", d.position()); + d.set_position(d.data.len() - 2 * IntEncodedWithFixedSize::ENCODED_SIZE); + debug!("position: {:?}", d.position()); - let edge_list_indices: IndexVec = d - .read_struct_field("edge_list_indices", 2, |d| { - d.read_seq(|d, len| { - let mut v = IndexVec::with_capacity(len); - for i in 0..len { - v.push(d.read_seq_elt(i, |d| Decodable::decode(d))?); - } - Ok(v) - }) - })?; + let node_count = IntEncodedWithFixedSize::decode(d)?.0 as usize; + let edge_count = IntEncodedWithFixedSize::decode(d)?.0 as usize; + debug!(?node_count, ?edge_count); + + debug!("position: {:?}", d.position()); + d.set_position(start_position); + debug!("position: {:?}", d.position()); + + let mut nodes = IndexVec::with_capacity(node_count); + let mut fingerprints = IndexVec::with_capacity(node_count); + let mut edge_list_indices = IndexVec::with_capacity(node_count); + let mut edge_list_data = Vec::with_capacity(edge_count); + + for _index in 0..node_count { + d.read_struct("NodeInfo", 3, |d| { + let dep_node: DepNode = d.read_struct_field("node", 0, Decodable::decode)?; + let _i: SerializedDepNodeIndex = nodes.push(dep_node); + debug_assert_eq!(_i.index(), _index); - let edge_list_data: Vec = - d.read_struct_field("edge_list_data", 3, |d| { + let fingerprint: Fingerprint = + d.read_struct_field("fingerprint", 1, Decodable::decode)?; + let _i: SerializedDepNodeIndex = fingerprints.push(fingerprint); + debug_assert_eq!(_i.index(), _index); + + d.read_struct_field("edges", 2, |d| { d.read_seq(|d, len| { - let mut v = Vec::with_capacity(len); - for i in 0..len { - v.push(d.read_seq_elt(i, |d| Decodable::decode(d))?); + let start = edge_list_data.len().try_into().unwrap(); + for e in 0..len { + let edge = d.read_seq_elt(e, Decodable::decode)?; + edge_list_data.push(edge); } - Ok(v) + let end = edge_list_data.len().try_into().unwrap(); + let _i: SerializedDepNodeIndex = edge_list_indices.push((start, end)); + debug_assert_eq!(_i.index(), _index); + Ok(()) }) - })?; + }) + })?; + } + + Ok(SerializedDepGraph { nodes, fingerprints, edge_list_indices, edge_list_data }) + } +} + +#[derive(Debug, Encodable, Decodable)] +pub struct NodeInfo { + node: DepNode, + fingerprint: Fingerprint, + edges: SmallVec<[DepNodeIndex; 8]>, +} + +struct Stat { + kind: K, + node_counter: u64, + edge_counter: u64, +} + +struct EncoderState { + encoder: FileEncoder, + total_node_count: usize, + total_edge_count: usize, + result: FileEncodeResult, + stats: Option>>, +} + +impl EncoderState { + fn new(encoder: FileEncoder, record_stats: bool) -> Self { + Self { + encoder, + total_edge_count: 0, + total_node_count: 0, + result: Ok(()), + stats: if record_stats { Some(FxHashMap::default()) } else { None }, + } + } + + #[instrument(skip(self, record_graph))] + fn encode_node( + &mut self, + node: &NodeInfo, + record_graph: &Option>>, + ) -> DepNodeIndex { + let index = DepNodeIndex::new(self.total_node_count); + self.total_node_count += 1; + + let edge_count = node.edges.len(); + self.total_edge_count += edge_count; + + if let Some(record_graph) = &record_graph { + // Do not ICE when a query is called from within `with_query`. + if let Some(record_graph) = &mut record_graph.try_lock() { + record_graph.push(index, node.node, &node.edges); + } + } + + if let Some(stats) = &mut self.stats { + let kind = node.node.kind; + + let stat = stats.entry(kind).or_insert(Stat { kind, node_counter: 0, edge_counter: 0 }); + stat.node_counter += 1; + stat.edge_counter += edge_count as u64; + } + + debug!(?index, ?node); + let encoder = &mut self.encoder; + if self.result.is_ok() { + self.result = node.encode(encoder); + } + index + } + + fn finish(self) -> FileEncodeResult { + let Self { mut encoder, total_node_count, total_edge_count, result, stats: _ } = self; + let () = result?; + + let node_count = total_node_count.try_into().unwrap(); + let edge_count = total_edge_count.try_into().unwrap(); + + debug!(?node_count, ?edge_count); + debug!("position: {:?}", encoder.position()); + IntEncodedWithFixedSize(node_count).encode(&mut encoder)?; + IntEncodedWithFixedSize(edge_count).encode(&mut encoder)?; + debug!("position: {:?}", encoder.position()); + // Drop the encoder so that nothing is written after the counts. + encoder.flush() + } +} + +pub struct GraphEncoder { + status: Lock>, + record_graph: Option>>, +} + +impl> GraphEncoder { + pub fn new( + encoder: FileEncoder, + prev_node_count: usize, + record_graph: bool, + record_stats: bool, + ) -> Self { + let record_graph = + if record_graph { Some(Lock::new(DepGraphQuery::new(prev_node_count))) } else { None }; + let status = Lock::new(EncoderState::new(encoder, record_stats)); + GraphEncoder { status, record_graph } + } + + pub(crate) fn with_query(&self, f: impl Fn(&DepGraphQuery)) { + if let Some(record_graph) = &self.record_graph { + f(&record_graph.lock()) + } + } + + pub(crate) fn print_incremental_info( + &self, + total_read_count: u64, + total_duplicate_read_count: u64, + ) { + let status = self.status.lock(); + if let Some(record_stats) = &status.stats { + let mut stats: Vec<_> = record_stats.values().collect(); + stats.sort_by_key(|s| -(s.node_counter as i64)); + + const SEPARATOR: &str = "[incremental] --------------------------------\ + ----------------------------------------------\ + ------------"; + + eprintln!("[incremental]"); + eprintln!("[incremental] DepGraph Statistics"); + eprintln!("{}", SEPARATOR); + eprintln!("[incremental]"); + eprintln!("[incremental] Total Node Count: {}", status.total_node_count); + eprintln!("[incremental] Total Edge Count: {}", status.total_edge_count); + + if cfg!(debug_assertions) { + eprintln!("[incremental] Total Edge Reads: {}", total_read_count); + eprintln!( + "[incremental] Total Duplicate Edge Reads: {}", + total_duplicate_read_count + ); + } + + eprintln!("[incremental]"); + eprintln!( + "[incremental] {:<36}| {:<17}| {:<12}| {:<17}|", + "Node Kind", "Node Frequency", "Node Count", "Avg. Edge Count" + ); + eprintln!("{}", SEPARATOR); + + for stat in stats { + let node_kind_ratio = + (100.0 * (stat.node_counter as f64)) / (status.total_node_count as f64); + let node_kind_avg_edges = (stat.edge_counter as f64) / (stat.node_counter as f64); + + eprintln!( + "[incremental] {:<36}|{:>16.1}% |{:>12} |{:>17.1} |", + format!("{:?}", stat.kind), + node_kind_ratio, + stat.node_counter, + node_kind_avg_edges, + ); + } + + eprintln!("{}", SEPARATOR); + eprintln!("[incremental]"); + } + } + + pub(crate) fn send( + &self, + profiler: &SelfProfilerRef, + node: DepNode, + fingerprint: Fingerprint, + edges: SmallVec<[DepNodeIndex; 8]>, + ) -> DepNodeIndex { + let _prof_timer = profiler.generic_activity("incr_comp_encode_dep_graph"); + let node = NodeInfo { node, fingerprint, edges }; + self.status.lock().encode_node(&node, &self.record_graph) + } - Ok(SerializedDepGraph { nodes, fingerprints, edge_list_indices, edge_list_data }) - }) + pub fn finish(self, profiler: &SelfProfilerRef) -> FileEncodeResult { + let _prof_timer = profiler.generic_activity("incr_comp_encode_dep_graph"); + self.status.into_inner().finish() } } diff --git a/compiler/rustc_query_system/src/lib.rs b/compiler/rustc_query_system/src/lib.rs index 26b76a9c006cd..071144f38e702 100644 --- a/compiler/rustc_query_system/src/lib.rs +++ b/compiler/rustc_query_system/src/lib.rs @@ -2,7 +2,9 @@ #![feature(const_fn)] #![feature(const_panic)] #![feature(core_intrinsics)] +#![feature(drain_filter)] #![feature(hash_raw_entry)] +#![feature(iter_zip)] #![feature(min_specialization)] #![feature(stmt_expr_attributes)] diff --git a/compiler/rustc_query_system/src/query/caches.rs b/compiler/rustc_query_system/src/query/caches.rs index ec71c8685804f..001bf3b216b1b 100644 --- a/compiler/rustc_query_system/src/query/caches.rs +++ b/compiler/rustc_query_system/src/query/caches.rs @@ -49,12 +49,11 @@ pub trait QueryCache: QueryStorage { index: DepNodeIndex, ) -> Self::Stored; - fn iter( + fn iter( &self, - shards: &Sharded, - get_shard: impl Fn(&mut L) -> &mut Self::Sharded, + shards: &Sharded, f: impl for<'a> FnOnce( - Box + 'a>, + &'a mut dyn Iterator, ) -> R, ) -> R; } @@ -125,16 +124,14 @@ where value } - fn iter( + fn iter( &self, - shards: &Sharded, - get_shard: impl Fn(&mut L) -> &mut Self::Sharded, - f: impl for<'a> FnOnce(Box + 'a>) -> R, + shards: &Sharded, + f: impl for<'a> FnOnce(&'a mut dyn Iterator) -> R, ) -> R { - let mut shards = shards.lock_shards(); - let mut shards: Vec<_> = shards.iter_mut().map(|shard| get_shard(shard)).collect(); - let results = shards.iter_mut().flat_map(|shard| shard.iter()).map(|(k, v)| (k, &v.0, v.1)); - f(Box::new(results)) + let shards = shards.lock_shards(); + let mut results = shards.iter().flat_map(|shard| shard.iter()).map(|(k, v)| (k, &v.0, v.1)); + f(&mut results) } } @@ -210,15 +207,13 @@ where &value.0 } - fn iter( + fn iter( &self, - shards: &Sharded, - get_shard: impl Fn(&mut L) -> &mut Self::Sharded, - f: impl for<'a> FnOnce(Box + 'a>) -> R, + shards: &Sharded, + f: impl for<'a> FnOnce(&'a mut dyn Iterator) -> R, ) -> R { - let mut shards = shards.lock_shards(); - let mut shards: Vec<_> = shards.iter_mut().map(|shard| get_shard(shard)).collect(); - let results = shards.iter_mut().flat_map(|shard| shard.iter()).map(|(k, v)| (k, &v.0, v.1)); - f(Box::new(results)) + let shards = shards.lock_shards(); + let mut results = shards.iter().flat_map(|shard| shard.iter()).map(|(k, v)| (k, &v.0, v.1)); + f(&mut results) } } diff --git a/compiler/rustc_query_system/src/query/job.rs b/compiler/rustc_query_system/src/query/job.rs index 35a2ac865f259..21f580db04f28 100644 --- a/compiler/rustc_query_system/src/query/job.rs +++ b/compiler/rustc_query_system/src/query/job.rs @@ -22,7 +22,7 @@ use { rustc_data_structures::{jobserver, OnDrop}, rustc_rayon_core as rayon_core, rustc_span::DUMMY_SP, - std::iter::FromIterator, + std::iter::{self, FromIterator}, std::{mem, process}, }; @@ -463,7 +463,7 @@ fn remove_cycle( spans.rotate_right(1); // Zip them back together - let mut stack: Vec<_> = spans.into_iter().zip(queries).collect(); + let mut stack: Vec<_> = iter::zip(spans, queries).collect(); // Remove the queries in our cycle from the list of jobs to look at for r in &stack { diff --git a/compiler/rustc_query_system/src/query/plumbing.rs b/compiler/rustc_query_system/src/query/plumbing.rs index 0050670b338ea..fb8a53048faba 100644 --- a/compiler/rustc_query_system/src/query/plumbing.rs +++ b/compiler/rustc_query_system/src/query/plumbing.rs @@ -76,10 +76,10 @@ impl QueryCacheStore { pub fn iter_results( &self, f: impl for<'a> FnOnce( - Box + 'a>, + &'a mut dyn Iterator, ) -> R, ) -> R { - self.cache.iter(&self.shards, |shard| &mut *shard, f) + self.cache.iter(&self.shards, f) } } @@ -449,9 +449,11 @@ where let ((result, dep_node_index), diagnostics) = with_diagnostics(|diagnostics| { tcx.start_query(job.id, diagnostics, || { - tcx.dep_context() - .dep_graph() - .with_anon_task(query.dep_kind, || query.compute(tcx, key)) + tcx.dep_context().dep_graph().with_anon_task( + *tcx.dep_context(), + query.dep_kind, + || query.compute(tcx, key), + ) }) }); @@ -533,7 +535,13 @@ where None }; - let result = if let Some(result) = result { + if let Some(result) = result { + // If `-Zincremental-verify-ich` is specified, re-hash results from + // the cache and make sure that they have the expected fingerprint. + if unlikely!(tcx.dep_context().sess().opts.debugging_opts.incremental_verify_ich) { + incremental_verify_ich(*tcx.dep_context(), &result, dep_node, query); + } + result } else { // We could not load a result from the on-disk cache, so @@ -545,32 +553,31 @@ where prof_timer.finish_with_query_invocation_id(dep_node_index.into()); - result - }; + // Verify that re-running the query produced a result with the expected hash + // This catches bugs in query implementations, turning them into ICEs. + // For example, a query might sort its result by `DefId` - since `DefId`s are + // not stable across compilation sessions, the result could get up getting sorted + // in a different order when the query is re-run, even though all of the inputs + // (e.g. `DefPathHash` values) were green. + // + // See issue #82920 for an example of a miscompilation that would get turned into + // an ICE by this check + incremental_verify_ich(*tcx.dep_context(), &result, dep_node, query); - // If `-Zincremental-verify-ich` is specified, re-hash results from - // the cache and make sure that they have the expected fingerprint. - if unlikely!(tcx.dep_context().sess().opts.debugging_opts.incremental_verify_ich) { - incremental_verify_ich(*tcx.dep_context(), &result, dep_node, dep_node_index, query); + result } - - result } -#[inline(never)] -#[cold] fn incremental_verify_ich( tcx: CTX::DepContext, result: &V, dep_node: &DepNode, - dep_node_index: DepNodeIndex, query: &QueryVtable, ) where CTX: QueryContext, { assert!( - Some(tcx.dep_graph().fingerprint_of(dep_node_index)) - == tcx.dep_graph().prev_fingerprint_of(dep_node), + tcx.dep_graph().is_green(dep_node), "fingerprint for green query instance not loaded from cache: {:?}", dep_node, ); @@ -581,9 +588,15 @@ fn incremental_verify_ich( let new_hash = query.hash_result(&mut hcx, result).unwrap_or(Fingerprint::ZERO); debug!("END verify_ich({:?})", dep_node); - let old_hash = tcx.dep_graph().fingerprint_of(dep_node_index); + let old_hash = tcx.dep_graph().prev_fingerprint_of(dep_node); - assert!(new_hash == old_hash, "found unstable fingerprints for {:?}", dep_node,); + assert_eq!( + Some(new_hash), + old_hash, + "found unstable fingerprints for {:?}: {:?}", + dep_node, + result + ); } fn force_query_with_job( diff --git a/compiler/rustc_resolve/src/build_reduced_graph.rs b/compiler/rustc_resolve/src/build_reduced_graph.rs index 65e5b0dddea30..d77022a65e439 100644 --- a/compiler/rustc_resolve/src/build_reduced_graph.rs +++ b/compiler/rustc_resolve/src/build_reduced_graph.rs @@ -1426,19 +1426,19 @@ impl<'a, 'b> Visitor<'b> for BuildReducedGraphVisitor<'a, 'b> { } } - fn visit_field(&mut self, f: &'b ast::Field) { + fn visit_expr_field(&mut self, f: &'b ast::ExprField) { if f.is_placeholder { self.visit_invoc(f.id); } else { - visit::walk_field(self, f); + visit::walk_expr_field(self, f); } } - fn visit_field_pattern(&mut self, fp: &'b ast::FieldPat) { + fn visit_pat_field(&mut self, fp: &'b ast::PatField) { if fp.is_placeholder { self.visit_invoc(fp.id); } else { - visit::walk_field_pattern(self, fp); + visit::walk_pat_field(self, fp); } } @@ -1458,13 +1458,13 @@ impl<'a, 'b> Visitor<'b> for BuildReducedGraphVisitor<'a, 'b> { } } - fn visit_struct_field(&mut self, sf: &'b ast::StructField) { + fn visit_field_def(&mut self, sf: &'b ast::FieldDef) { if sf.is_placeholder { self.visit_invoc(sf.id); } else { let vis = self.resolve_visibility(&sf.vis); self.r.visibilities.insert(self.r.local_def_id(sf.id), vis); - visit::walk_struct_field(self, sf); + visit::walk_field_def(self, sf); } } diff --git a/compiler/rustc_resolve/src/def_collector.rs b/compiler/rustc_resolve/src/def_collector.rs index 727d6ab53d869..17f0c39e39735 100644 --- a/compiler/rustc_resolve/src/def_collector.rs +++ b/compiler/rustc_resolve/src/def_collector.rs @@ -1,4 +1,4 @@ -use crate::Resolver; +use crate::{ImplTraitContext, Resolver}; use rustc_ast::visit::{self, FnKind}; use rustc_ast::walk_list; use rustc_ast::*; @@ -16,14 +16,15 @@ crate fn collect_definitions( fragment: &AstFragment, expansion: ExpnId, ) { - let parent_def = resolver.invocation_parents[&expansion]; - fragment.visit_with(&mut DefCollector { resolver, parent_def, expansion }); + let (parent_def, impl_trait_context) = resolver.invocation_parents[&expansion]; + fragment.visit_with(&mut DefCollector { resolver, parent_def, expansion, impl_trait_context }); } /// Creates `DefId`s for nodes in the AST. struct DefCollector<'a, 'b> { resolver: &'a mut Resolver<'b>, parent_def: LocalDefId, + impl_trait_context: ImplTraitContext, expansion: ExpnId, } @@ -40,7 +41,17 @@ impl<'a, 'b> DefCollector<'a, 'b> { self.parent_def = orig_parent_def; } - fn collect_field(&mut self, field: &'a StructField, index: Option) { + fn with_impl_trait( + &mut self, + impl_trait_context: ImplTraitContext, + f: F, + ) { + let orig_itc = std::mem::replace(&mut self.impl_trait_context, impl_trait_context); + f(self); + self.impl_trait_context = orig_itc; + } + + fn collect_field(&mut self, field: &'a FieldDef, index: Option) { let index = |this: &Self| { index.unwrap_or_else(|| { let node_id = NodeId::placeholder_from_expn_id(this.expansion); @@ -55,13 +66,14 @@ impl<'a, 'b> DefCollector<'a, 'b> { } else { let name = field.ident.map_or_else(|| sym::integer(index(self)), |ident| ident.name); let def = self.create_def(field.id, DefPathData::ValueNs(name), field.span); - self.with_parent(def, |this| visit::walk_struct_field(this, field)); + self.with_parent(def, |this| visit::walk_field_def(this, field)); } } fn visit_macro_invoc(&mut self, id: NodeId) { + let id = id.placeholder_to_expn_id(); let old_parent = - self.resolver.invocation_parents.insert(id.placeholder_to_expn_id(), self.parent_def); + self.resolver.invocation_parents.insert(id, (self.parent_def, self.impl_trait_context)); assert!(old_parent.is_none(), "parent `LocalDefId` is reset for an invocation"); } } @@ -103,29 +115,37 @@ impl<'a, 'b> visit::Visitor<'a> for DefCollector<'a, 'b> { let def = self.create_def(i.id, def_data, i.span); self.with_parent(def, |this| { - match i.kind { - ItemKind::Struct(ref struct_def, _) | ItemKind::Union(ref struct_def, _) => { - // If this is a unit or tuple-like struct, register the constructor. - if let Some(ctor_hir_id) = struct_def.ctor_id() { - this.create_def(ctor_hir_id, DefPathData::Ctor, i.span); + this.with_impl_trait(ImplTraitContext::Existential, |this| { + match i.kind { + ItemKind::Struct(ref struct_def, _) | ItemKind::Union(ref struct_def, _) => { + // If this is a unit or tuple-like struct, register the constructor. + if let Some(ctor_hir_id) = struct_def.ctor_id() { + this.create_def(ctor_hir_id, DefPathData::Ctor, i.span); + } } + _ => {} } - _ => {} - } - visit::walk_item(this, i); + visit::walk_item(this, i); + }) }); } fn visit_fn(&mut self, fn_kind: FnKind<'a>, span: Span, _: NodeId) { if let FnKind::Fn(_, _, sig, _, body) = fn_kind { if let Async::Yes { closure_id, return_impl_trait_id, .. } = sig.header.asyncness { - self.create_def(return_impl_trait_id, DefPathData::ImplTrait, span); + let return_impl_trait_id = + self.create_def(return_impl_trait_id, DefPathData::ImplTrait, span); // For async functions, we need to create their inner defs inside of a // closure to match their desugared representation. Besides that, // we must mirror everything that `visit::walk_fn` below does. self.visit_fn_header(&sig.header); - visit::walk_fn_decl(self, &sig.decl); + for param in &sig.decl.inputs { + self.visit_param(param); + } + self.with_parent(return_impl_trait_id, |this| { + this.visit_fn_ret_ty(&sig.decl.output) + }); let closure_def = self.create_def(closure_id, DefPathData::ClosureExpr, span); self.with_parent(closure_def, |this| walk_list!(this, visit_block, body)); return; @@ -137,6 +157,14 @@ impl<'a, 'b> visit::Visitor<'a> for DefCollector<'a, 'b> { fn visit_use_tree(&mut self, use_tree: &'a UseTree, id: NodeId, _nested: bool) { self.create_def(id, DefPathData::Misc, use_tree.span); + match use_tree.kind { + UseTreeKind::Simple(_, id1, id2) => { + self.create_def(id1, DefPathData::Misc, use_tree.prefix.span); + self.create_def(id2, DefPathData::Misc, use_tree.prefix.span); + } + UseTreeKind::Glob => (), + UseTreeKind::Nested(..) => {} + } visit::walk_use_tree(self, use_tree, id); } @@ -191,7 +219,15 @@ impl<'a, 'b> visit::Visitor<'a> for DefCollector<'a, 'b> { }; self.create_def(param.id, def_path_data, param.ident.span); - visit::walk_generic_param(self, param); + // impl-Trait can happen inside generic parameters, like + // ``` + // fn foo>() {} + // ``` + // + // In that case, the impl-trait is lowered as an additional generic parameter. + self.with_impl_trait(ImplTraitContext::Universal(self.parent_def), |this| { + visit::walk_generic_param(this, param) + }); } fn visit_assoc_item(&mut self, i: &'a AssocItem, ctxt: visit::AssocCtxt) { @@ -244,8 +280,19 @@ impl<'a, 'b> visit::Visitor<'a> for DefCollector<'a, 'b> { match ty.kind { TyKind::MacCall(..) => self.visit_macro_invoc(ty.id), TyKind::ImplTrait(node_id, _) => { - let parent_def = self.create_def(node_id, DefPathData::ImplTrait, ty.span); - self.with_parent(parent_def, |this| visit::walk_ty(this, ty)); + let parent_def = match self.impl_trait_context { + ImplTraitContext::Universal(item_def) => self.resolver.create_def( + item_def, + node_id, + DefPathData::ImplTrait, + self.expansion, + ty.span, + ), + ImplTraitContext::Existential => { + self.create_def(node_id, DefPathData::ImplTrait, ty.span) + } + }; + self.with_parent(parent_def, |this| visit::walk_ty(this, ty)) } _ => visit::walk_ty(self, ty), } @@ -262,25 +309,35 @@ impl<'a, 'b> visit::Visitor<'a> for DefCollector<'a, 'b> { if arm.is_placeholder { self.visit_macro_invoc(arm.id) } else { visit::walk_arm(self, arm) } } - fn visit_field(&mut self, f: &'a Field) { - if f.is_placeholder { self.visit_macro_invoc(f.id) } else { visit::walk_field(self, f) } + fn visit_expr_field(&mut self, f: &'a ExprField) { + if f.is_placeholder { + self.visit_macro_invoc(f.id) + } else { + visit::walk_expr_field(self, f) + } } - fn visit_field_pattern(&mut self, fp: &'a FieldPat) { + fn visit_pat_field(&mut self, fp: &'a PatField) { if fp.is_placeholder { self.visit_macro_invoc(fp.id) } else { - visit::walk_field_pattern(self, fp) + visit::walk_pat_field(self, fp) } } fn visit_param(&mut self, p: &'a Param) { - if p.is_placeholder { self.visit_macro_invoc(p.id) } else { visit::walk_param(self, p) } + if p.is_placeholder { + self.visit_macro_invoc(p.id) + } else { + self.with_impl_trait(ImplTraitContext::Universal(self.parent_def), |this| { + visit::walk_param(this, p) + }) + } } // This method is called only when we are visiting an individual field // after expanding an attribute on it. - fn visit_struct_field(&mut self, field: &'a StructField) { + fn visit_field_def(&mut self, field: &'a FieldDef) { self.collect_field(field, None); } } diff --git a/compiler/rustc_resolve/src/diagnostics.rs b/compiler/rustc_resolve/src/diagnostics.rs index 7493fd68505fe..87e28f7fcc592 100644 --- a/compiler/rustc_resolve/src/diagnostics.rs +++ b/compiler/rustc_resolve/src/diagnostics.rs @@ -450,12 +450,12 @@ impl<'a> Resolver<'a> { self.session, span, E0128, - "type parameters with a default cannot use \ + "generic parameters with a default cannot use \ forward declared identifiers" ); err.span_label( span, - "defaulted type parameters cannot be forward declared".to_string(), + "defaulted generic parameters cannot be forward declared".to_string(), ); err } @@ -606,7 +606,7 @@ impl<'a> Resolver<'a> { /// Lookup typo candidate in scope for a macro or import. fn early_lookup_typo_candidate( &mut self, - scope_set: ScopeSet, + scope_set: ScopeSet<'a>, parent_scope: &ParentScope<'a>, ident: Ident, filter_fn: &impl Fn(Res) -> bool, @@ -662,7 +662,7 @@ impl<'a> Resolver<'a> { let root_module = this.resolve_crate_root(root_ident); this.add_module_candidates(root_module, &mut suggestions, filter_fn); } - Scope::Module(module) => { + Scope::Module(module, _) => { this.add_module_candidates(module, &mut suggestions, filter_fn); } Scope::RegisteredAttrs => { @@ -758,17 +758,14 @@ impl<'a> Resolver<'a> { { let mut candidates = Vec::new(); let mut seen_modules = FxHashSet::default(); - let not_local_module = crate_name.name != kw::Crate; - let mut worklist = - vec![(start_module, Vec::::new(), true, not_local_module)]; + let mut worklist = vec![(start_module, Vec::::new(), true)]; let mut worklist_via_import = vec![]; - while let Some((in_module, path_segments, accessible, in_module_is_extern)) = - match worklist.pop() { - None => worklist_via_import.pop(), - Some(x) => Some(x), - } - { + while let Some((in_module, path_segments, accessible)) = match worklist.pop() { + None => worklist_via_import.pop(), + Some(x) => Some(x), + } { + let in_module_is_extern = !in_module.def_id().unwrap().is_local(); // We have to visit module children in deterministic order to avoid // instabilities in reported imports (#43552). in_module.for_each_child(self, |this, ident, ns, name_binding| { @@ -850,11 +847,10 @@ impl<'a> Resolver<'a> { name_binding.is_extern_crate() && lookup_ident.span.rust_2018(); if !is_extern_crate_that_also_appears_in_prelude { - let is_extern = in_module_is_extern || name_binding.is_extern_crate(); // add the module to the lookup if seen_modules.insert(module.def_id().unwrap()) { if via_import { &mut worklist_via_import } else { &mut worklist } - .push((module, path_segments, child_accessible, is_extern)); + .push((module, path_segments, child_accessible)); } } } diff --git a/compiler/rustc_resolve/src/imports.rs b/compiler/rustc_resolve/src/imports.rs index bd0296751a535..26858915f45a0 100644 --- a/compiler/rustc_resolve/src/imports.rs +++ b/compiler/rustc_resolve/src/imports.rs @@ -156,6 +156,21 @@ impl<'a> NameResolution<'a> { } } +// Reexports of the form `pub use foo as bar;` where `foo` is `extern crate foo;` +// are permitted for backward-compatibility under a deprecation lint. +fn pub_use_of_private_extern_crate_hack(import: &Import<'_>, binding: &NameBinding<'_>) -> bool { + match (&import.kind, &binding.kind) { + ( + ImportKind::Single { .. }, + NameBindingKind::Import { + import: Import { kind: ImportKind::ExternCrate { .. }, .. }, + .. + }, + ) => import.vis.get() == ty::Visibility::Public, + _ => false, + } +} + impl<'a> Resolver<'a> { crate fn resolve_ident_in_module_unadjusted( &mut self, @@ -263,10 +278,7 @@ impl<'a> Resolver<'a> { return Err((Determined, Weak::No)); } } - // `extern crate` are always usable for backwards compatibility, see issue #37020, - // remove this together with `PUB_USE_OF_PRIVATE_EXTERN_CRATE`. - let usable = this.is_accessible_from(binding.vis, parent_scope.module) - || binding.is_extern_crate(); + let usable = this.is_accessible_from(binding.vis, parent_scope.module); if usable { Ok(binding) } else { Err((Determined, Weak::No)) } }; @@ -309,10 +321,7 @@ impl<'a> Resolver<'a> { } } - if !(self.is_accessible_from(binding.vis, parent_scope.module) || - // Remove this together with `PUB_USE_OF_PRIVATE_EXTERN_CRATE` - (self.last_import_segment && binding.is_extern_crate())) - { + if !self.is_accessible_from(binding.vis, parent_scope.module) { self.privacy_errors.push(PrivacyError { ident, binding, @@ -455,9 +464,8 @@ impl<'a> Resolver<'a> { binding: &'a NameBinding<'a>, import: &'a Import<'a>, ) -> &'a NameBinding<'a> { - let vis = if binding.vis.is_at_least(import.vis.get(), self) || - // cf. `PUB_USE_OF_PRIVATE_EXTERN_CRATE` - !import.is_glob() && binding.is_extern_crate() + let vis = if binding.vis.is_at_least(import.vis.get(), self) + || pub_use_of_private_extern_crate_hack(import, binding) { import.vis.get() } else { @@ -947,14 +955,14 @@ impl<'a, 'b> ImportResolver<'a, 'b> { } return None; } - PathResult::NonModule(path_res) if path_res.base_res() == Res::Err => { + PathResult::NonModule(_) => { if no_ambiguity { assert!(import.imported_module.get().is_none()); } // The error was already reported earlier. return None; } - PathResult::Indeterminate | PathResult::NonModule(..) => unreachable!(), + PathResult::Indeterminate => unreachable!(), }; let (ident, target, source_bindings, target_bindings, type_ns_only) = match import.kind { @@ -1188,7 +1196,7 @@ impl<'a, 'b> ImportResolver<'a, 'b> { // All namespaces must be re-exported with extra visibility for an error to occur. if !any_successful_reexport { let (ns, binding) = reexport_error.unwrap(); - if ns == TypeNS && binding.is_extern_crate() { + if pub_use_of_private_extern_crate_hack(import, binding) { let msg = format!( "extern crate `{}` is private, and cannot be \ re-exported (error E0365), consider declaring with \ diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs index 6f24d7e941351..9321f11f65933 100644 --- a/compiler/rustc_resolve/src/late.rs +++ b/compiler/rustc_resolve/src/late.rs @@ -132,10 +132,10 @@ crate enum RibKind<'a> { /// We passed through a `macro_rules!` statement MacroDefinition(DefId), - /// All bindings in this rib are type parameters that can't be used - /// from the default of a type parameter because they're not declared - /// before said type parameter. Also see the `visit_generics` override. - ForwardTyParamBanRibKind, + /// All bindings in this rib are generic parameters that can't be used + /// from the default of a generic parameter because they're not declared + /// before said generic parameter. Also see the `visit_generics` override. + ForwardGenericParamBanRibKind, /// We are inside of the type of a const parameter. Can't refer to any /// parameters. @@ -154,7 +154,7 @@ impl RibKind<'_> { | ModuleRibKind(_) | MacroDefinition(_) | ConstParamTyRibKind => false, - AssocItemRibKind | ItemRibKind(_) | ForwardTyParamBanRibKind => true, + AssocItemRibKind | ItemRibKind(_) | ForwardGenericParamBanRibKind => true, } } } @@ -555,15 +555,16 @@ impl<'a: 'ast, 'ast> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast> { // provide previous type parameters as they're built. We // put all the parameters on the ban list and then remove // them one by one as they are processed and become available. - let mut default_ban_rib = Rib::new(ForwardTyParamBanRibKind); + let mut default_ban_rib = Rib::new(ForwardGenericParamBanRibKind); let mut found_default = false; default_ban_rib.bindings.extend(generics.params.iter().filter_map( |param| match param.kind { - GenericParamKind::Const { .. } | GenericParamKind::Lifetime { .. } => None, - GenericParamKind::Type { ref default, .. } => { - found_default |= default.is_some(); - found_default.then_some((Ident::with_dummy_span(param.ident.name), Res::Err)) + GenericParamKind::Type { default: Some(_), .. } + | GenericParamKind::Const { default: Some(_), .. } => { + found_default = true; + Some((Ident::with_dummy_span(param.ident.name), Res::Err)) } + _ => None, }, )); @@ -591,8 +592,8 @@ impl<'a: 'ast, 'ast> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast> { if let Some(ref ty) = default { self.ribs[TypeNS].push(default_ban_rib); - self.with_rib(ValueNS, ForwardTyParamBanRibKind, |this| { - // HACK: We use an empty `ForwardTyParamBanRibKind` here which + self.with_rib(ValueNS, ForwardGenericParamBanRibKind, |this| { + // HACK: We use an empty `ForwardGenericParamBanRibKind` here which // is only used to forbid the use of const parameters inside of // type defaults. // @@ -865,7 +866,7 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { | ItemRibKind(..) | ConstantItemRibKind(..) | ModuleRibKind(..) - | ForwardTyParamBanRibKind + | ForwardGenericParamBanRibKind | ConstParamTyRibKind => { return false; } @@ -1030,7 +1031,6 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { } ItemKind::Static(ref ty, _, ref expr) | ItemKind::Const(_, ref ty, ref expr) => { - debug!("resolve_item ItemKind::Const"); self.with_item_rib(HasGenericParams::No, |this| { this.visit_ty(ty); if let Some(expr) = expr { @@ -1596,6 +1596,7 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { .try_resolve_as_non_binding(pat_src, pat, bmode, ident, has_sub) .unwrap_or_else(|| self.fresh_binding(ident, pat.id, pat_src, bindings)); self.r.record_partial_res(pat.id, PartialRes::new(res)); + self.r.record_pat_span(pat.id, pat.span); } PatKind::TupleStruct(ref path, ref sub_patterns) => { self.smart_resolve_path( @@ -2251,8 +2252,8 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { visit::walk_expr(self, expr); } - ExprKind::Struct(ref path, ..) => { - self.smart_resolve_path(expr.id, None, path, PathSource::Struct); + ExprKind::Struct(ref se) => { + self.smart_resolve_path(expr.id, None, &se.path, PathSource::Struct); visit::walk_expr(self, expr); } @@ -2326,7 +2327,7 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { ExprKind::Call(ref callee, ref arguments) => { self.resolve_expr(callee, Some(expr)); - let const_args = self.r.legacy_const_generic_args(callee).unwrap_or(Vec::new()); + let const_args = self.r.legacy_const_generic_args(callee).unwrap_or_default(); for (idx, argument) in arguments.iter().enumerate() { // Constant arguments need to be treated as AnonConst since // that is how they will be later lowered to HIR. diff --git a/compiler/rustc_resolve/src/late/diagnostics.rs b/compiler/rustc_resolve/src/late/diagnostics.rs index 87bf79d722bde..e33c374f562e2 100644 --- a/compiler/rustc_resolve/src/late/diagnostics.rs +++ b/compiler/rustc_resolve/src/late/diagnostics.rs @@ -16,11 +16,14 @@ use rustc_hir::def::{self, CtorKind, CtorOf, DefKind}; use rustc_hir::def_id::{DefId, CRATE_DEF_INDEX, LOCAL_CRATE}; use rustc_hir::PrimTy; use rustc_session::parse::feature_err; +use rustc_span::edition::Edition; use rustc_span::hygiene::MacroKind; use rustc_span::lev_distance::find_best_match_for_name; use rustc_span::symbol::{kw, sym, Ident, Symbol}; use rustc_span::{BytePos, MultiSpan, Span, DUMMY_SP}; +use std::iter; + use tracing::debug; type Res = def::Res; @@ -133,7 +136,7 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> { let is_enum_variant = &|res| matches!(res, Res::Def(DefKind::Variant, _)); // Make the base error. - let expected = source.descr_expected(); + let mut expected = source.descr_expected(); let path_str = Segment::names_to_string(path); let item_str = path.last().unwrap().ident; let (base_msg, fallback_label, base_span, could_be_expr) = if let Some(res) = res { @@ -166,6 +169,15 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> { let (mod_prefix, mod_str) = if path.len() == 1 { (String::new(), "this scope".to_string()) } else if path.len() == 2 && path[0].ident.name == kw::PathRoot { + if self.r.session.edition() > Edition::Edition2015 { + // In edition 2018 onwards, the `::foo` syntax may only pull from the extern prelude + // which overrides all other expectations of item type + expected = "crate"; + (String::new(), "the list of imported crates".to_string()) + } else { + (String::new(), "the crate root".to_string()) + } + } else if path.len() == 2 && path[0].ident.name == kw::Crate { (String::new(), "the crate root".to_string()) } else { let mod_path = &path[..path.len() - 1]; @@ -174,7 +186,7 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> { PathResult::Module(ModuleOrUniformRoot::Module(module)) => module.res(), _ => None, } - .map_or(String::new(), |res| format!("{} ", res.descr())); + .map_or_else(String::new, |res| format!("{} ", res.descr())); (mod_prefix, format!("`{}`", Segment::names_to_string(mod_path))) }; ( @@ -324,7 +336,7 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> { .lookup_import_candidates(ident, ns, &self.parent_scope, is_enum_variant) .into_iter() .map(|suggestion| import_candidate_to_enum_paths(&suggestion)) - .filter(|(_, enum_ty_path)| enum_ty_path != "std::prelude::v1") + .filter(|(_, enum_ty_path)| !enum_ty_path.starts_with("std::prelude::")) .collect(); if !enum_candidates.is_empty() { if let (PathSource::Type, Some(span)) = @@ -444,12 +456,14 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> { } } + let is_macro = base_span.from_expansion() && base_span.desugaring_kind().is_none(); if !self.type_ascription_suggestion(&mut err, base_span) { let mut fallback = false; if let ( PathSource::Trait(AliasPossibility::Maybe), Some(Res::Def(DefKind::Struct | DefKind::Enum | DefKind::Union, _)), - ) = (source, res) + false, + ) = (source, res, is_macro) { if let Some(bounds @ [_, .., _]) = self.diagnostic_metadata.current_trait_object { fallback = true; @@ -553,6 +567,15 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> { } } } + } else if err_code == &rustc_errors::error_code!(E0412) { + if let Some(correct) = Self::likely_rust_type(path) { + err.span_suggestion( + span, + "perhaps you intended to use this type", + correct.to_string(), + Applicability::MaybeIncorrect, + ); + } } } @@ -907,7 +930,14 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> { let msg = "you might have meant to use `#![feature(trait_alias)]` instead of a \ `type` alias"; if let Some(span) = self.def_span(def_id) { - err.span_help(span, msg); + if let Ok(snip) = self.r.session.source_map().span_to_snippet(span) { + // The span contains a type alias so we should be able to + // replace `type` with `trait`. + let snip = snip.replacen("type", "trait", 1); + err.span_suggestion(span, msg, snip, Applicability::MaybeIncorrect); + } else { + err.span_help(span, msg); + } } else { err.help(msg); } @@ -985,9 +1015,7 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> { if let Some(spans) = field_spans.filter(|spans| spans.len() > 0 && fields.len() == spans.len()) { - let non_visible_spans: Vec = fields - .iter() - .zip(spans.iter()) + let non_visible_spans: Vec = iter::zip(&fields, &spans) .filter(|(vis, _)| { !self.r.is_accessible_from(**vis, self.parent_scope.module) }) @@ -1023,10 +1051,10 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> { if let Some(span) = self.def_span(def_id) { err.span_label(span, &format!("`{}` defined here", path_str)); } - let fields = - self.r.field_names.get(&def_id).map_or("/* fields */".to_string(), |fields| { - vec!["_"; fields.len()].join(", ") - }); + let fields = self.r.field_names.get(&def_id).map_or_else( + || "/* fields */".to_string(), + |fields| vec!["_"; fields.len()].join(", "), + ); err.span_suggestion( span, "use the tuple variant pattern syntax instead", @@ -1233,6 +1261,23 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> { } } + // Returns the name of the Rust type approximately corresponding to + // a type name in another programming language. + fn likely_rust_type(path: &[Segment]) -> Option { + let name = path[path.len() - 1].ident.as_str(); + // Common Java types + Some(match &*name { + "byte" => sym::u8, // In Java, bytes are signed, but in practice one almost always wants unsigned bytes. + "short" => sym::i16, + "boolean" => sym::bool, + "int" => sym::i32, + "long" => sym::i64, + "float" => sym::f32, + "double" => sym::f64, + _ => return None, + }) + } + /// Only used in a specific case of type ascription suggestions fn get_colon_suggestion_span(&self, start: Span) -> Span { let sm = self.r.session.source_map(); diff --git a/compiler/rustc_resolve/src/late/lifetimes.rs b/compiler/rustc_resolve/src/late/lifetimes.rs index 493f25f4992d1..174df09cbdbb2 100644 --- a/compiler/rustc_resolve/src/late/lifetimes.rs +++ b/compiler/rustc_resolve/src/late/lifetimes.rs @@ -1,3 +1,4 @@ +// ignore-tidy-filelength //! Name resolution for lifetimes. //! //! Name resolution for lifetimes follows *much* simpler rules than the @@ -11,7 +12,7 @@ use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_errors::{struct_span_err, Applicability, DiagnosticBuilder}; use rustc_hir as hir; use rustc_hir::def::{DefKind, Res}; -use rustc_hir::def_id::{CrateNum, DefIdMap, LOCAL_CRATE}; +use rustc_hir::def_id::DefIdMap; use rustc_hir::hir_id::ItemLocalId; use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor}; use rustc_hir::{GenericArg, GenericParam, LifetimeName, Node, ParamName, QPath}; @@ -26,9 +27,10 @@ use rustc_span::symbol::{kw, sym, Ident, Symbol}; use rustc_span::Span; use std::borrow::Cow; use std::cell::Cell; +use std::fmt; use std::mem::take; -use tracing::debug; +use tracing::{debug, span, Level}; // This counts the no of times a lifetime is used #[derive(Clone, Copy, Debug)] @@ -40,9 +42,9 @@ pub enum LifetimeUseSet<'tcx> { trait RegionExt { fn early(hir_map: &Map<'_>, index: &mut u32, param: &GenericParam<'_>) -> (ParamName, Region); - fn late(hir_map: &Map<'_>, param: &GenericParam<'_>) -> (ParamName, Region); + fn late(index: u32, hir_map: &Map<'_>, param: &GenericParam<'_>) -> (ParamName, Region); - fn late_anon(index: &Cell) -> Region; + fn late_anon(named_late_bound_vars: u32, index: &Cell) -> Region; fn id(&self) -> Option; @@ -65,29 +67,32 @@ impl RegionExt for Region { (param.name.normalize_to_macros_2_0(), Region::EarlyBound(i, def_id.to_def_id(), origin)) } - fn late(hir_map: &Map<'_>, param: &GenericParam<'_>) -> (ParamName, Region) { + fn late(idx: u32, hir_map: &Map<'_>, param: &GenericParam<'_>) -> (ParamName, Region) { let depth = ty::INNERMOST; let def_id = hir_map.local_def_id(param.hir_id); let origin = LifetimeDefOrigin::from_param(param); debug!( - "Region::late: param={:?} depth={:?} def_id={:?} origin={:?}", - param, depth, def_id, origin, + "Region::late: idx={:?}, param={:?} depth={:?} def_id={:?} origin={:?}", + idx, param, depth, def_id, origin, ); - (param.name.normalize_to_macros_2_0(), Region::LateBound(depth, def_id.to_def_id(), origin)) + ( + param.name.normalize_to_macros_2_0(), + Region::LateBound(depth, idx, def_id.to_def_id(), origin), + ) } - fn late_anon(index: &Cell) -> Region { + fn late_anon(named_late_bound_vars: u32, index: &Cell) -> Region { let i = index.get(); index.set(i + 1); let depth = ty::INNERMOST; - Region::LateBoundAnon(depth, i) + Region::LateBoundAnon(depth, named_late_bound_vars + i, i) } fn id(&self) -> Option { match *self { Region::Static | Region::LateBoundAnon(..) => None, - Region::EarlyBound(_, id, _) | Region::LateBound(_, id, _) | Region::Free(_, id) => { + Region::EarlyBound(_, id, _) | Region::LateBound(_, _, id, _) | Region::Free(_, id) => { Some(id) } } @@ -95,11 +100,11 @@ impl RegionExt for Region { fn shifted(self, amount: u32) -> Region { match self { - Region::LateBound(debruijn, id, origin) => { - Region::LateBound(debruijn.shifted_in(amount), id, origin) + Region::LateBound(debruijn, idx, id, origin) => { + Region::LateBound(debruijn.shifted_in(amount), idx, id, origin) } - Region::LateBoundAnon(debruijn, index) => { - Region::LateBoundAnon(debruijn.shifted_in(amount), index) + Region::LateBoundAnon(debruijn, index, anon_index) => { + Region::LateBoundAnon(debruijn.shifted_in(amount), index, anon_index) } _ => self, } @@ -107,11 +112,11 @@ impl RegionExt for Region { fn shifted_out_to_binder(self, binder: ty::DebruijnIndex) -> Region { match self { - Region::LateBound(debruijn, id, origin) => { - Region::LateBound(debruijn.shifted_out_to_binder(binder), id, origin) + Region::LateBound(debruijn, index, id, origin) => { + Region::LateBound(debruijn.shifted_out_to_binder(binder), index, id, origin) } - Region::LateBoundAnon(debruijn, index) => { - Region::LateBoundAnon(debruijn.shifted_out_to_binder(binder), index) + Region::LateBoundAnon(debruijn, index, anon_index) => { + Region::LateBoundAnon(debruijn.shifted_out_to_binder(binder), index, anon_index) } _ => self, } @@ -135,7 +140,7 @@ impl RegionExt for Region { /// FIXME. This struct gets converted to a `ResolveLifetimes` for /// actual use. It has the same data, but indexed by `LocalDefId`. This /// is silly. -#[derive(Default)] +#[derive(Debug, Default)] struct NamedRegionMap { // maps from every use of a named (not anonymous) lifetime to a // `Region` describing how that region is bound @@ -146,9 +151,13 @@ struct NamedRegionMap { // (b) it DOES appear in the arguments. late_bound: HirIdSet, - // For each type and trait definition, maps type parameters - // to the trait object lifetime defaults computed from them. - object_lifetime_defaults: HirIdMap>, + // Maps relevant hir items to the bound vars on them. These include: + // - function defs + // - function pointers + // - closures + // - trait refs + // - bound types (like `T` in `for<'a> T<'a>: Foo`) + late_bound_vars: HirIdMap>, } crate struct LifetimeContext<'a, 'tcx> { @@ -156,26 +165,16 @@ crate struct LifetimeContext<'a, 'tcx> { map: &'a mut NamedRegionMap, scope: ScopeRef<'a>, - /// This is slightly complicated. Our representation for poly-trait-refs contains a single - /// binder and thus we only allow a single level of quantification. However, - /// the syntax of Rust permits quantification in two places, e.g., `T: for <'a> Foo<'a>` - /// and `for <'a, 'b> &'b T: Foo<'a>`. In order to get the De Bruijn indices - /// correct when representing these constraints, we should only introduce one - /// scope. However, we want to support both locations for the quantifier and - /// during lifetime resolution we want precise information (so we can't - /// desugar in an earlier phase). - /// - /// So, if we encounter a quantifier at the outer scope, we set - /// `trait_ref_hack` to `true` (and introduce a scope), and then if we encounter - /// a quantifier at the inner scope, we error. If `trait_ref_hack` is `false`, - /// then we introduce the scope at the inner quantifier. - trait_ref_hack: bool, - /// Used to disallow the use of in-band lifetimes in `fn` or `Fn` syntax. is_in_fn_syntax: bool, is_in_const_generic: bool, + /// Indicates that we only care about the definition of a trait. This should + /// be false if the `Item` we are resolving lifetimes for is not a trait or + /// we eventually need lifetimes resolve for trait items. + trait_definition_only: bool, + /// List of labels in the function/method currently under analysis. labels_in_fn: Vec, @@ -222,6 +221,14 @@ enum Scope<'a> { /// of the resulting opaque type. opaque_type_parent: bool, + scope_type: BinderScopeType, + + /// The late bound vars for a given item are stored by `HirId` to be + /// queried later. However, if we enter an elision scope, we have to + /// later append the elided bound vars to the list and need to know what + /// to append to. + hir_id: hir::HirId, + s: ScopeRef<'a>, }, @@ -249,14 +256,90 @@ enum Scope<'a> { s: ScopeRef<'a>, }, + /// When we have nested trait refs, we concanetate late bound vars for inner + /// trait refs from outer ones. But we also need to include any HRTB + /// lifetimes encountered when identifying the trait that an associated type + /// is declared on. + Supertrait { + lifetimes: Vec, + s: ScopeRef<'a>, + }, + + TraitRefBoundary { + s: ScopeRef<'a>, + }, + Root, } +#[derive(Copy, Clone, Debug)] +enum BinderScopeType { + /// Any non-concatenating binder scopes. + Normal, + /// Within a syntactic trait ref, there may be multiple poly trait refs that + /// are nested (under the `associcated_type_bounds` feature). The binders of + /// the innner poly trait refs are extended from the outer poly trait refs + /// and don't increase the late bound depth. If you had + /// `T: for<'a> Foo Baz<'a, 'b>>`, then the `for<'b>` scope + /// would be `Concatenating`. This also used in trait refs in where clauses + /// where we have two binders `for<> T: for<> Foo` (I've intentionally left + /// out any lifetimes because they aren't needed to show the two scopes). + /// The inner `for<>` has a scope of `Concatenating`. + Concatenating, +} + +// A helper struct for debugging scopes without printing parent scopes +struct TruncatedScopeDebug<'a>(&'a Scope<'a>); + +impl<'a> fmt::Debug for TruncatedScopeDebug<'a> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self.0 { + Scope::Binder { + lifetimes, + next_early_index, + track_lifetime_uses, + opaque_type_parent, + scope_type, + hir_id, + s: _, + } => f + .debug_struct("Binder") + .field("lifetimes", lifetimes) + .field("next_early_index", next_early_index) + .field("track_lifetime_uses", track_lifetime_uses) + .field("opaque_type_parent", opaque_type_parent) + .field("scope_type", scope_type) + .field("hir_id", hir_id) + .field("s", &"..") + .finish(), + Scope::Body { id, s: _ } => { + f.debug_struct("Body").field("id", id).field("s", &"..").finish() + } + Scope::Elision { elide, s: _ } => { + f.debug_struct("Elision").field("elide", elide).field("s", &"..").finish() + } + Scope::ObjectLifetimeDefault { lifetime, s: _ } => f + .debug_struct("ObjectLifetimeDefault") + .field("lifetime", lifetime) + .field("s", &"..") + .finish(), + Scope::Supertrait { lifetimes, s: _ } => f + .debug_struct("Supertrait") + .field("lifetimes", lifetimes) + .field("s", &"..") + .finish(), + Scope::TraitRefBoundary { s: _ } => f.debug_struct("TraitRefBoundary").finish(), + Scope::Root => f.debug_struct("Root").finish(), + } + } +} + #[derive(Clone, Debug)] enum Elide { /// Use a fresh anonymous late-bound lifetime each time, by - /// incrementing the counter to generate sequential indices. - FreshLateAnon(Cell), + /// incrementing the counter to generate sequential indices. All + /// anonymous lifetimes must start *after* named bound vars. + FreshLateAnon(u32, Cell), /// Always use this one lifetime. Exact(Region), /// Less or more than one lifetime were found, error on unspecified. @@ -283,26 +366,94 @@ const ROOT_SCOPE: ScopeRef<'static> = &Scope::Root; pub fn provide(providers: &mut ty::query::Providers) { *providers = ty::query::Providers { + resolve_lifetimes_trait_definition, resolve_lifetimes, - named_region_map: |tcx, id| tcx.resolve_lifetimes(LOCAL_CRATE).defs.get(&id), + named_region_map: |tcx, id| resolve_lifetimes_for(tcx, id).defs.get(&id), is_late_bound_map, object_lifetime_defaults_map: |tcx, id| { - tcx.resolve_lifetimes(LOCAL_CRATE).object_lifetime_defaults.get(&id) + let hir_id = tcx.hir().local_def_id_to_hir_id(id); + match tcx.hir().find(hir_id) { + Some(Node::Item(item)) => compute_object_lifetime_defaults(tcx, item), + _ => None, + } }, + late_bound_vars_map: |tcx, id| resolve_lifetimes_for(tcx, id).late_bound_vars.get(&id), ..*providers }; } -/// Computes the `ResolveLifetimes` map that contains data for the -/// entire crate. You should not read the result of this query -/// directly, but rather use `named_region_map`, `is_late_bound_map`, -/// etc. -fn resolve_lifetimes(tcx: TyCtxt<'_>, for_krate: CrateNum) -> ResolveLifetimes { - assert_eq!(for_krate, LOCAL_CRATE); +/// Like `resolve_lifetimes`, but does not resolve lifetimes for trait items. +/// Also does not generate any diagnostics. +/// +/// This is ultimately a subset of the `resolve_lifetimes` work. It effectively +/// resolves lifetimes only within the trait "header" -- that is, the trait +/// and supertrait list. In contrast, `resolve_lifetimes` resolves all the +/// lifetimes within the trait and its items. There is room to refactor this, +/// for example to resolve lifetimes for each trait item in separate queries, +/// but it's convenient to do the entire trait at once because the lifetimes +/// from the trait definition are in scope within the trait items as well. +/// +/// The reason for this separate call is to resolve what would otherwise +/// be a cycle. Consider this example: +/// +/// ```rust +/// trait Base<'a> { +/// type BaseItem; +/// } +/// trait Sub<'b>: for<'a> Base<'a> { +/// type SubItem: Sub; +/// } +/// ``` +/// +/// When we resolve `Sub` and all its items, we also have to resolve `Sub`. +/// To figure out the index of `'b`, we have to know about the supertraits +/// of `Sub` so that we can determine that the `for<'a>` will be in scope. +/// (This is because we -- currently at least -- flatten all the late-bound +/// lifetimes into a single binder.) This requires us to resolve the +/// *trait definition* of `Sub`; basically just enough lifetime information +/// to look at the supertraits. +#[tracing::instrument(level = "debug", skip(tcx))] +fn resolve_lifetimes_trait_definition( + tcx: TyCtxt<'_>, + local_def_id: LocalDefId, +) -> ResolveLifetimes { + do_resolve(tcx, local_def_id, true) +} - let named_region_map = krate(tcx); +/// Computes the `ResolveLifetimes` map that contains data for an entire `Item`. +/// You should not read the result of this query directly, but rather use +/// `named_region_map`, `is_late_bound_map`, etc. +#[tracing::instrument(level = "debug", skip(tcx))] +fn resolve_lifetimes(tcx: TyCtxt<'_>, local_def_id: LocalDefId) -> ResolveLifetimes { + do_resolve(tcx, local_def_id, false) +} + +fn do_resolve( + tcx: TyCtxt<'_>, + local_def_id: LocalDefId, + trait_definition_only: bool, +) -> ResolveLifetimes { + let item = tcx.hir().expect_item(tcx.hir().local_def_id_to_hir_id(local_def_id)); + let mut named_region_map = NamedRegionMap { + defs: Default::default(), + late_bound: Default::default(), + late_bound_vars: Default::default(), + }; + let mut visitor = LifetimeContext { + tcx, + map: &mut named_region_map, + scope: ROOT_SCOPE, + is_in_fn_syntax: false, + is_in_const_generic: false, + trait_definition_only, + labels_in_fn: vec![], + xcrate_object_lifetime_defaults: Default::default(), + lifetime_uses: &mut Default::default(), + missing_named_lifetime_spots: vec![], + }; + visitor.visit_item(item); let mut rl = ResolveLifetimes::default(); @@ -314,14 +465,62 @@ fn resolve_lifetimes(tcx: TyCtxt<'_>, for_krate: CrateNum) -> ResolveLifetimes { let map = rl.late_bound.entry(hir_id.owner).or_default(); map.insert(hir_id.local_id); } - for (hir_id, v) in named_region_map.object_lifetime_defaults { - let map = rl.object_lifetime_defaults.entry(hir_id.owner).or_default(); + for (hir_id, v) in named_region_map.late_bound_vars { + let map = rl.late_bound_vars.entry(hir_id.owner).or_default(); map.insert(hir_id.local_id, v); } + debug!(?rl.defs); rl } +/// Given `any` owner (structs, traits, trait methods, etc.), does lifetime resolution. +/// There are two important things this does. +/// First, we have to resolve lifetimes for +/// the entire *`Item`* that contains this owner, because that's the largest "scope" +/// where we can have relevant lifetimes. +/// Second, if we are asking for lifetimes in a trait *definition*, we use `resolve_lifetimes_trait_definition` +/// instead of `resolve_lifetimes`, which does not descend into the trait items and does not emit diagnostics. +/// This allows us to avoid cycles. Importantly, if we ask for lifetimes for lifetimes that have an owner +/// other than the trait itself (like the trait methods or associated types), then we just use the regular +/// `resolve_lifetimes`. +fn resolve_lifetimes_for<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> &'tcx ResolveLifetimes { + let item_id = item_for(tcx, def_id); + if item_id == def_id { + let item = tcx.hir().item(hir::ItemId { def_id: item_id }); + match item.kind { + hir::ItemKind::Trait(..) => tcx.resolve_lifetimes_trait_definition(item_id), + _ => tcx.resolve_lifetimes(item_id), + } + } else { + tcx.resolve_lifetimes(item_id) + } +} + +/// Finds the `Item` that contains the given `LocalDefId` +fn item_for(tcx: TyCtxt<'_>, local_def_id: LocalDefId) -> LocalDefId { + let hir_id = tcx.hir().local_def_id_to_hir_id(local_def_id); + match tcx.hir().find(hir_id) { + Some(Node::Item(item)) => { + return item.def_id; + } + _ => {} + } + let item = { + let hir = tcx.hir(); + let mut parent_iter = hir.parent_iter(hir_id); + loop { + let node = parent_iter.next().map(|n| n.1); + match node { + Some(hir::Node::Item(item)) => break item.def_id, + Some(hir::Node::Crate(_)) | None => bug!("Called `item_for` on an Item."), + _ => {} + } + } + }; + item +} + fn is_late_bound_map<'tcx>( tcx: TyCtxt<'tcx>, def_id: LocalDefId, @@ -344,37 +543,10 @@ fn is_late_bound_map<'tcx>( tcx.is_late_bound_map(def_id.expect_local()) } - _ => tcx.resolve_lifetimes(LOCAL_CRATE).late_bound.get(&def_id).map(|lt| (def_id, lt)), + _ => resolve_lifetimes_for(tcx, def_id).late_bound.get(&def_id).map(|lt| (def_id, lt)), } } -fn krate(tcx: TyCtxt<'_>) -> NamedRegionMap { - let krate = tcx.hir().krate(); - let mut map = NamedRegionMap { - defs: Default::default(), - late_bound: Default::default(), - object_lifetime_defaults: compute_object_lifetime_defaults(tcx), - }; - { - let mut visitor = LifetimeContext { - tcx, - map: &mut map, - scope: ROOT_SCOPE, - trait_ref_hack: false, - is_in_fn_syntax: false, - is_in_const_generic: false, - labels_in_fn: vec![], - xcrate_object_lifetime_defaults: Default::default(), - lifetime_uses: &mut Default::default(), - missing_named_lifetime_spots: vec![], - }; - for item in krate.items.values() { - visitor.visit_item(item); - } - } - map -} - /// In traits, there is an implicit `Self` type parameter which comes before the generics. /// We have to account for this when computing the index of the other generic parameters. /// This function returns whether there is such an implicit parameter defined on the given item. @@ -382,6 +554,56 @@ fn sub_items_have_self_param(node: &hir::ItemKind<'_>) -> bool { matches!(*node, hir::ItemKind::Trait(..) | hir::ItemKind::TraitAlias(..)) } +fn late_region_as_bound_region<'tcx>(tcx: TyCtxt<'tcx>, region: &Region) -> ty::BoundVariableKind { + match region { + Region::LateBound(_, _, def_id, _) => { + let name = tcx.hir().name(tcx.hir().local_def_id_to_hir_id(def_id.expect_local())); + ty::BoundVariableKind::Region(ty::BrNamed(*def_id, name)) + } + Region::LateBoundAnon(_, _, anon_idx) => { + ty::BoundVariableKind::Region(ty::BrAnon(*anon_idx)) + } + _ => bug!("{:?} is not a late region", region), + } +} + +impl<'a, 'tcx> LifetimeContext<'a, 'tcx> { + /// Returns the binders in scope and the type of `Binder` that should be created for a poly trait ref. + fn poly_trait_ref_binder_info(&mut self) -> (Vec, BinderScopeType) { + let mut scope = self.scope; + let mut supertrait_lifetimes = vec![]; + loop { + match scope { + Scope::Body { .. } | Scope::Root => { + break (vec![], BinderScopeType::Normal); + } + + Scope::Elision { s, .. } | Scope::ObjectLifetimeDefault { s, .. } => { + scope = s; + } + + Scope::Supertrait { s, lifetimes } => { + supertrait_lifetimes = lifetimes.clone(); + scope = s; + } + + Scope::TraitRefBoundary { .. } => { + // We should only see super trait lifetimes if there is a `Binder` above + assert!(supertrait_lifetimes.is_empty()); + break (vec![], BinderScopeType::Normal); + } + + Scope::Binder { hir_id, .. } => { + // Nested poly trait refs have the binders concatenated + let mut full_binders = + self.map.late_bound_vars.entry(*hir_id).or_default().clone(); + full_binders.extend(supertrait_lifetimes.into_iter()); + break (full_binders, BinderScopeType::Concatenating); + } + } + } + } +} impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { type Map = Map<'tcx>; @@ -392,6 +614,12 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { // We want to nest trait/impl items in their parent, but nothing else. fn visit_nested_item(&mut self, _: hir::ItemId) {} + fn visit_trait_item_ref(&mut self, ii: &'tcx hir::TraitItemRef) { + if !self.trait_definition_only { + intravisit::walk_trait_item_ref(self, ii) + } + } + fn visit_nested_body(&mut self, body: hir::BodyId) { // Each body has their own set of labels, save labels. let saved = take(&mut self.labels_in_fn); @@ -403,11 +631,58 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { self.labels_in_fn = saved; } + fn visit_fn( + &mut self, + fk: intravisit::FnKind<'tcx>, + fd: &'tcx hir::FnDecl<'tcx>, + b: hir::BodyId, + s: rustc_span::Span, + hir_id: hir::HirId, + ) { + let name = match fk { + intravisit::FnKind::ItemFn(id, _, _, _) => id.as_str(), + intravisit::FnKind::Method(id, _, _) => id.as_str(), + intravisit::FnKind::Closure => Symbol::intern("closure").as_str(), + }; + let name: &str = &name; + let span = span!(Level::DEBUG, "visit_fn", name); + let _enter = span.enter(); + match fk { + // Any `Binders` are handled elsewhere + intravisit::FnKind::ItemFn(..) | intravisit::FnKind::Method(..) => { + intravisit::walk_fn(self, fk, fd, b, s, hir_id) + } + intravisit::FnKind::Closure => { + self.map.late_bound_vars.insert(hir_id, vec![]); + let scope = Scope::Binder { + hir_id, + lifetimes: FxHashMap::default(), + next_early_index: self.next_early_index(), + s: self.scope, + track_lifetime_uses: true, + opaque_type_parent: false, + scope_type: BinderScopeType::Normal, + }; + self.with(scope, move |_old_scope, this| { + intravisit::walk_fn(this, fk, fd, b, s, hir_id) + }); + } + } + } + fn visit_item(&mut self, item: &'tcx hir::Item<'tcx>) { + match &item.kind { + hir::ItemKind::Impl(hir::Impl { of_trait, .. }) => { + if let Some(of_trait) = of_trait { + self.map.late_bound_vars.insert(of_trait.hir_ref_id, Vec::default()); + } + } + _ => {} + } match item.kind { hir::ItemKind::Fn(ref sig, ref generics, _) => { self.missing_named_lifetime_spots.push(generics.into()); - self.visit_early_late(None, &sig.decl, generics, |this| { + self.visit_early_late(None, item.hir_id(), &sig.decl, generics, |this| { intravisit::walk_item(this, item); }); self.missing_named_lifetime_spots.pop(); @@ -430,6 +705,47 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { // Opaque types are visited when we visit the // `TyKind::OpaqueDef`, so that they have the lifetimes from // their parent opaque_ty in scope. + // + // The core idea here is that since OpaqueTys are generated with the impl Trait as + // their owner, we can keep going until we find the Item that owns that. We then + // conservatively add all resolved lifetimes. Otherwise we run into problems in + // cases like `type Foo<'a> = impl Bar`. + for (_hir_id, node) in + self.tcx.hir().parent_iter(self.tcx.hir().local_def_id_to_hir_id(item.def_id)) + { + match node { + hir::Node::Item(parent_item) => { + let resolved_lifetimes: &ResolveLifetimes = + self.tcx.resolve_lifetimes(item_for(self.tcx, parent_item.def_id)); + // We need to add *all* deps, since opaque tys may want them from *us* + for (&owner, defs) in resolved_lifetimes.defs.iter() { + defs.iter().for_each(|(&local_id, region)| { + self.map + .defs + .insert(hir::HirId { owner, local_id }, region.clone()); + }); + } + for (&owner, late_bound) in resolved_lifetimes.late_bound.iter() { + late_bound.iter().for_each(|&local_id| { + self.map.late_bound.insert(hir::HirId { owner, local_id }); + }); + } + for (&owner, late_bound_vars) in + resolved_lifetimes.late_bound_vars.iter() + { + late_bound_vars.iter().for_each(|(&local_id, late_bound_vars)| { + self.map.late_bound_vars.insert( + hir::HirId { owner, local_id }, + late_bound_vars.clone(), + ); + }); + } + break; + } + hir::Node::Crate(_) => bug!("No Item about an OpaqueTy"), + _ => {} + } + } } hir::ItemKind::TyAlias(_, ref generics) | hir::ItemKind::Enum(_, ref generics) @@ -463,16 +779,22 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { } }) .collect(); + self.map.late_bound_vars.insert(item.hir_id(), vec![]); let scope = Scope::Binder { + hir_id: item.hir_id(), lifetimes, next_early_index: index + non_lifetime_count, opaque_type_parent: true, track_lifetime_uses, + scope_type: BinderScopeType::Normal, s: ROOT_SCOPE, }; self.with(scope, |old_scope, this| { this.check_lifetime_params(old_scope, &generics.params); - intravisit::walk_item(this, item); + let scope = Scope::TraitRefBoundary { s: this.scope }; + this.with(scope, |_, this| { + intravisit::walk_item(this, item); + }); }); self.missing_named_lifetime_spots.pop(); } @@ -482,7 +804,7 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { fn visit_foreign_item(&mut self, item: &'tcx hir::ForeignItem<'tcx>) { match item.kind { hir::ForeignItemKind::Fn(ref decl, _, ref generics) => { - self.visit_early_late(None, decl, generics, |this| { + self.visit_early_late(None, item.hir_id(), decl, generics, |this| { intravisit::walk_foreign_item(this, item); }) } @@ -495,9 +817,8 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { } } + #[tracing::instrument(level = "debug", skip(self))] fn visit_ty(&mut self, ty: &'tcx hir::Ty<'tcx>) { - debug!("visit_ty: id={:?} ty={:?}", ty.hir_id, ty); - debug!("visit_ty: ty.kind={:?}", ty.kind); match ty.kind { hir::TyKind::BareFn(ref c) => { let next_early_index = self.next_early_index(); @@ -515,21 +836,29 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { }; self.missing_named_lifetime_spots .push(MissingLifetimeSpot::HigherRanked { span, span_type }); + let (lifetimes, binders): (FxHashMap, Vec<_>) = c + .generic_params + .iter() + .filter_map(|param| match param.kind { + GenericParamKind::Lifetime { .. } => Some(param), + _ => None, + }) + .enumerate() + .map(|(late_bound_idx, param)| { + let pair = Region::late(late_bound_idx as u32, &self.tcx.hir(), param); + let r = late_region_as_bound_region(self.tcx, &pair.1); + (pair, r) + }) + .unzip(); + self.map.late_bound_vars.insert(ty.hir_id, binders); let scope = Scope::Binder { - lifetimes: c - .generic_params - .iter() - .filter_map(|param| match param.kind { - GenericParamKind::Lifetime { .. } => { - Some(Region::late(&self.tcx.hir(), param)) - } - _ => None, - }) - .collect(), + hir_id: ty.hir_id, + lifetimes, s: self.scope, next_early_index, track_lifetime_uses: true, opaque_type_parent: false, + scope_type: BinderScopeType::Normal, }; self.with(scope, |old_scope, this| { // a bare fn has no bounds, so everything @@ -540,11 +869,14 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { self.missing_named_lifetime_spots.pop(); self.is_in_fn_syntax = was_in_fn_syntax; } - hir::TyKind::TraitObject(bounds, ref lifetime) => { - debug!("visit_ty: TraitObject(bounds={:?}, lifetime={:?})", bounds, lifetime); - for bound in bounds { - self.visit_poly_trait_ref(bound, hir::TraitBoundModifier::None); - } + hir::TyKind::TraitObject(bounds, ref lifetime, _) => { + debug!(?bounds, ?lifetime, "TraitObject"); + let scope = Scope::TraitRefBoundary { s: self.scope }; + self.with(scope, |_, this| { + for bound in bounds { + this.visit_poly_trait_ref(bound, hir::TraitBoundModifier::None); + } + }); match lifetime.name { LifetimeName::Implicit => { // For types like `dyn Foo`, we should @@ -564,7 +896,7 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { // resolved the same as the `'_` in `&'_ Foo`. // // cc #48468 - self.resolve_elided_lifetimes(vec![lifetime]) + self.resolve_elided_lifetimes(&[lifetime]) } LifetimeName::Param(_) | LifetimeName::Static => { // If the user wrote an explicit name, use that. @@ -596,9 +928,12 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { // Elided lifetimes are not allowed in non-return // position impl Trait - let scope = Scope::Elision { elide: Elide::Forbid, s: self.scope }; + let scope = Scope::TraitRefBoundary { s: self.scope }; self.with(scope, |_, this| { - intravisit::walk_item(this, opaque_ty); + let scope = Scope::Elision { elide: Elide::Forbid, s: this.scope }; + this.with(scope, |_, this| { + intravisit::walk_item(this, opaque_ty); + }) }); return; @@ -627,7 +962,7 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { // well-supported at the moment, so this doesn't work. // In the future, this should be fixed and this error should be removed. let def = self.map.defs.get(&lifetime.hir_id).cloned(); - if let Some(Region::LateBound(_, def_id, _)) = def { + if let Some(Region::LateBound(_, _, def_id, _)) = def { if let Some(def_id) = def_id.as_local() { let hir_id = self.tcx.hir().local_def_id_to_hir_id(def_id); // Ensure that the parent of the def is an item, not HRTB @@ -652,14 +987,16 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { }; if !parent_is_item { - struct_span_err!( - self.tcx.sess, - lifetime.span, - E0657, - "`impl Trait` can only capture lifetimes \ - bound at the fn or impl level" - ) - .emit(); + if !self.trait_definition_only { + struct_span_err!( + self.tcx.sess, + lifetime.span, + E0657, + "`impl Trait` can only capture lifetimes \ + bound at the fn or impl level" + ) + .emit(); + } self.uninsert_lifetime_on_error(lifetime, def.unwrap()); } } @@ -670,7 +1007,7 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { // We want to start our early-bound indices at the end of the parent scope, // not including any parent `impl Trait`s. let mut index = self.next_early_index_for_opaque_type(); - debug!("visit_ty: index = {}", index); + debug!(?index); let mut elision = None; let mut lifetimes = FxHashMap::default(); @@ -704,38 +1041,49 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { } } let next_early_index = index + non_lifetime_count; + self.map.late_bound_vars.insert(ty.hir_id, vec![]); if let Some(elision_region) = elision { let scope = Scope::Elision { elide: Elide::Exact(elision_region), s: self.scope }; self.with(scope, |_old_scope, this| { let scope = Scope::Binder { + hir_id: ty.hir_id, lifetimes, next_early_index, s: this.scope, track_lifetime_uses: true, opaque_type_parent: false, + scope_type: BinderScopeType::Normal, }; this.with(scope, |_old_scope, this| { this.visit_generics(generics); - for bound in bounds { - this.visit_param_bound(bound); - } + let scope = Scope::TraitRefBoundary { s: this.scope }; + this.with(scope, |_, this| { + for bound in bounds { + this.visit_param_bound(bound); + } + }) }); }); } else { let scope = Scope::Binder { + hir_id: ty.hir_id, lifetimes, next_early_index, s: self.scope, track_lifetime_uses: true, opaque_type_parent: false, + scope_type: BinderScopeType::Normal, }; self.with(scope, |_old_scope, this| { - this.visit_generics(generics); - for bound in bounds { - this.visit_param_bound(bound); - } + let scope = Scope::TraitRefBoundary { s: this.scope }; + this.with(scope, |_, this| { + this.visit_generics(generics); + for bound in bounds { + this.visit_param_bound(bound); + } + }) }); } } @@ -751,6 +1099,7 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { let tcx = self.tcx; self.visit_early_late( Some(tcx.hir().get_parent_item(trait_item.hir_id())), + trait_item.hir_id(), &sig.decl, &trait_item.generics, |this| intravisit::walk_trait_item(this, trait_item), @@ -776,22 +1125,28 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { } }) .collect(); + self.map.late_bound_vars.insert(trait_item.hir_id(), vec![]); let scope = Scope::Binder { + hir_id: trait_item.hir_id(), lifetimes, next_early_index: index + non_lifetime_count, s: self.scope, track_lifetime_uses: true, opaque_type_parent: true, + scope_type: BinderScopeType::Normal, }; self.with(scope, |old_scope, this| { this.check_lifetime_params(old_scope, &generics.params); - this.visit_generics(generics); - for bound in bounds { - this.visit_param_bound(bound); - } - if let Some(ty) = ty { - this.visit_ty(ty); - } + let scope = Scope::TraitRefBoundary { s: this.scope }; + this.with(scope, |_, this| { + this.visit_generics(generics); + for bound in bounds { + this.visit_param_bound(bound); + } + if let Some(ty) = ty { + this.visit_ty(ty); + } + }) }); self.missing_named_lifetime_spots.pop(); } @@ -813,6 +1168,7 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { let tcx = self.tcx; self.visit_early_late( Some(tcx.hir().get_parent_item(impl_item.hir_id())), + impl_item.hir_id(), &sig.decl, &impl_item.generics, |this| intravisit::walk_impl_item(this, impl_item), @@ -825,7 +1181,7 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { let mut index = self.next_early_index(); let mut non_lifetime_count = 0; debug!("visit_ty: index = {}", index); - let lifetimes = generics + let lifetimes: FxHashMap = generics .params .iter() .filter_map(|param| match param.kind { @@ -838,17 +1194,23 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { } }) .collect(); + self.map.late_bound_vars.insert(ty.hir_id, vec![]); let scope = Scope::Binder { + hir_id: ty.hir_id, lifetimes, next_early_index: index + non_lifetime_count, s: self.scope, track_lifetime_uses: true, opaque_type_parent: true, + scope_type: BinderScopeType::Normal, }; self.with(scope, |old_scope, this| { this.check_lifetime_params(old_scope, &generics.params); - this.visit_generics(generics); - this.visit_ty(ty); + let scope = Scope::TraitRefBoundary { s: this.scope }; + this.with(scope, |_, this| { + this.visit_generics(generics); + this.visit_ty(ty); + }) }); self.missing_named_lifetime_spots.pop(); } @@ -862,10 +1224,10 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { } } + #[tracing::instrument(level = "debug", skip(self))] fn visit_lifetime(&mut self, lifetime_ref: &'tcx hir::Lifetime) { - debug!("visit_lifetime(lifetime_ref={:?})", lifetime_ref); if lifetime_ref.is_elided() { - self.resolve_elided_lifetimes(vec![lifetime_ref]); + self.resolve_elided_lifetimes(&[lifetime_ref]); return; } if lifetime_ref.is_static() { @@ -897,93 +1259,113 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { } fn visit_generics(&mut self, generics: &'tcx hir::Generics<'tcx>) { - check_mixed_explicit_and_in_band_defs(self.tcx, &generics.params); - for param in generics.params { - match param.kind { - GenericParamKind::Lifetime { .. } => {} - GenericParamKind::Type { ref default, .. } => { - walk_list!(self, visit_param_bound, param.bounds); - if let Some(ref ty) = default { - self.visit_ty(&ty); + if !self.trait_definition_only { + check_mixed_explicit_and_in_band_defs(self.tcx, &generics.params); + } + let scope = Scope::TraitRefBoundary { s: self.scope }; + self.with(scope, |_, this| { + for param in generics.params { + match param.kind { + GenericParamKind::Lifetime { .. } => {} + GenericParamKind::Type { ref default, .. } => { + walk_list!(this, visit_param_bound, param.bounds); + if let Some(ref ty) = default { + this.visit_ty(&ty); + } + } + GenericParamKind::Const { ref ty, .. } => { + let was_in_const_generic = this.is_in_const_generic; + this.is_in_const_generic = true; + walk_list!(this, visit_param_bound, param.bounds); + this.visit_ty(&ty); + this.is_in_const_generic = was_in_const_generic; } } - GenericParamKind::Const { ref ty, .. } => { - let was_in_const_generic = self.is_in_const_generic; - self.is_in_const_generic = true; - walk_list!(self, visit_param_bound, param.bounds); - self.visit_ty(&ty); - self.is_in_const_generic = was_in_const_generic; - } - } - } - for predicate in generics.where_clause.predicates { - match predicate { - &hir::WherePredicate::BoundPredicate(hir::WhereBoundPredicate { - ref bounded_ty, - bounds, - ref bound_generic_params, - .. - }) => { - let lifetimes: FxHashMap<_, _> = bound_generic_params - .iter() - .filter_map(|param| match param.kind { - GenericParamKind::Lifetime { .. } => { - Some(Region::late(&self.tcx.hir(), param)) - } - _ => None, - }) - .collect(); - if !lifetimes.is_empty() { - let next_early_index = self.next_early_index(); + } + for predicate in generics.where_clause.predicates { + match predicate { + &hir::WherePredicate::BoundPredicate(hir::WhereBoundPredicate { + ref bounded_ty, + bounds, + ref bound_generic_params, + .. + }) => { + let (lifetimes, binders): (FxHashMap, Vec<_>) = + bound_generic_params + .iter() + .filter_map(|param| match param.kind { + GenericParamKind::Lifetime { .. } => Some(param), + _ => None, + }) + .enumerate() + .map(|(late_bound_idx, param)| { + let pair = + Region::late(late_bound_idx as u32, &this.tcx.hir(), param); + let r = late_region_as_bound_region(this.tcx, &pair.1); + (pair, r) + }) + .unzip(); + this.map.late_bound_vars.insert(bounded_ty.hir_id, binders.clone()); + let next_early_index = this.next_early_index(); + // Even if there are no lifetimes defined here, we still wrap it in a binder + // scope. If there happens to be a nested poly trait ref (an error), that + // will be `Concatenating` anyways, so we don't have to worry about the depth + // being wrong. let scope = Scope::Binder { + hir_id: bounded_ty.hir_id, lifetimes, - s: self.scope, + s: this.scope, next_early_index, track_lifetime_uses: true, opaque_type_parent: false, + scope_type: BinderScopeType::Normal, }; - let result = self.with(scope, |old_scope, this| { + this.with(scope, |old_scope, this| { this.check_lifetime_params(old_scope, &bound_generic_params); this.visit_ty(&bounded_ty); - this.trait_ref_hack = true; walk_list!(this, visit_param_bound, bounds); - this.trait_ref_hack = false; - }); - result - } else { - self.visit_ty(&bounded_ty); - walk_list!(self, visit_param_bound, bounds); + }) + } + &hir::WherePredicate::RegionPredicate(hir::WhereRegionPredicate { + ref lifetime, + bounds, + .. + }) => { + this.visit_lifetime(lifetime); + walk_list!(this, visit_param_bound, bounds); + } + &hir::WherePredicate::EqPredicate(hir::WhereEqPredicate { + ref lhs_ty, + ref rhs_ty, + .. + }) => { + this.visit_ty(lhs_ty); + this.visit_ty(rhs_ty); } - } - &hir::WherePredicate::RegionPredicate(hir::WhereRegionPredicate { - ref lifetime, - bounds, - .. - }) => { - self.visit_lifetime(lifetime); - walk_list!(self, visit_param_bound, bounds); - } - &hir::WherePredicate::EqPredicate(hir::WhereEqPredicate { - ref lhs_ty, - ref rhs_ty, - .. - }) => { - self.visit_ty(lhs_ty); - self.visit_ty(rhs_ty); } } - } + }) } fn visit_param_bound(&mut self, bound: &'tcx hir::GenericBound<'tcx>) { match bound { - hir::GenericBound::LangItemTrait { .. } if !self.trait_ref_hack => { + hir::GenericBound::LangItemTrait(_, _, hir_id, _) => { + // FIXME(jackh726): This is pretty weird. `LangItemTrait` doesn't go + // through the regular poly trait ref code, so we don't get another + // chance to introduce a binder. For now, I'm keeping the existing logic + // of "if there isn't a Binder scope above us, add one", but I + // imagine there's a better way to go about this. + let (binders, scope_type) = self.poly_trait_ref_binder_info(); + + self.map.late_bound_vars.insert(*hir_id, binders); let scope = Scope::Binder { + hir_id: *hir_id, lifetimes: FxHashMap::default(), s: self.scope, next_early_index: self.next_early_index(), track_lifetime_uses: true, opaque_type_parent: false, + scope_type, }; self.with(scope, |_, this| { intravisit::walk_param_bound(this, bound); @@ -1002,48 +1384,53 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { let should_pop_missing_lt = self.is_trait_ref_fn_scope(trait_ref); - let trait_ref_hack = take(&mut self.trait_ref_hack); - if !trait_ref_hack - || trait_ref - .bound_generic_params - .iter() - .any(|param| matches!(param.kind, GenericParamKind::Lifetime { .. })) - { - if trait_ref_hack { - struct_span_err!( - self.tcx.sess, - trait_ref.span, - E0316, - "nested quantification of lifetimes" - ) - .emit(); - } - let next_early_index = self.next_early_index(); - let scope = Scope::Binder { - lifetimes: trait_ref - .bound_generic_params - .iter() - .filter_map(|param| match param.kind { - GenericParamKind::Lifetime { .. } => { - Some(Region::late(&self.tcx.hir(), param)) - } - _ => None, - }) - .collect(), - s: self.scope, - next_early_index, - track_lifetime_uses: true, - opaque_type_parent: false, - }; - self.with(scope, |old_scope, this| { - this.check_lifetime_params(old_scope, &trait_ref.bound_generic_params); - walk_list!(this, visit_generic_param, trait_ref.bound_generic_params); - this.visit_trait_ref(&trait_ref.trait_ref); + let next_early_index = self.next_early_index(); + let (mut binders, scope_type) = self.poly_trait_ref_binder_info(); + + let initial_bound_vars = binders.len() as u32; + let mut lifetimes: FxHashMap = FxHashMap::default(); + let binders_iter = trait_ref + .bound_generic_params + .iter() + .filter_map(|param| match param.kind { + GenericParamKind::Lifetime { .. } => Some(param), + _ => None, + }) + .enumerate() + .map(|(late_bound_idx, param)| { + let pair = Region::late( + initial_bound_vars + late_bound_idx as u32, + &self.tcx.hir(), + param, + ); + let r = late_region_as_bound_region(self.tcx, &pair.1); + lifetimes.insert(pair.0, pair.1); + r }); - } else { - self.visit_trait_ref(&trait_ref.trait_ref); - } - self.trait_ref_hack = trait_ref_hack; + binders.extend(binders_iter); + + debug!(?binders); + self.map.late_bound_vars.insert(trait_ref.trait_ref.hir_ref_id, binders); + + // Always introduce a scope here, even if this is in a where clause and + // we introduced the binders around the bounded Ty. In that case, we + // just reuse the concatenation functionality also present in nested trait + // refs. + let scope = Scope::Binder { + hir_id: trait_ref.trait_ref.hir_ref_id, + lifetimes, + s: self.scope, + next_early_index, + track_lifetime_uses: true, + opaque_type_parent: false, + scope_type, + }; + self.with(scope, |old_scope, this| { + this.check_lifetime_params(old_scope, &trait_ref.bound_generic_params); + walk_list!(this, visit_generic_param, trait_ref.bound_generic_params); + this.visit_trait_ref(&trait_ref.trait_ref); + }); + if should_pop_missing_lt { self.missing_named_lifetime_spots.pop(); } @@ -1193,7 +1580,9 @@ fn extract_labels(ctxt: &mut LifetimeContext<'_, '_>, body: &hir::Body<'_>) { match *scope { Scope::Body { s, .. } | Scope::Elision { s, .. } - | Scope::ObjectLifetimeDefault { s, .. } => { + | Scope::ObjectLifetimeDefault { s, .. } + | Scope::Supertrait { s, .. } + | Scope::TraitRefBoundary { s, .. } => { scope = s; } @@ -1224,55 +1613,53 @@ fn extract_labels(ctxt: &mut LifetimeContext<'_, '_>, body: &hir::Body<'_>) { } } -fn compute_object_lifetime_defaults(tcx: TyCtxt<'_>) -> HirIdMap> { - let mut map = HirIdMap::default(); - for item in tcx.hir().krate().items.values() { - match item.kind { - hir::ItemKind::Struct(_, ref generics) - | hir::ItemKind::Union(_, ref generics) - | hir::ItemKind::Enum(_, ref generics) - | hir::ItemKind::OpaqueTy(hir::OpaqueTy { - ref generics, impl_trait_fn: None, .. - }) - | hir::ItemKind::TyAlias(_, ref generics) - | hir::ItemKind::Trait(_, _, ref generics, ..) => { - let result = object_lifetime_defaults_for_item(tcx, generics); - - // Debugging aid. - if tcx.sess.contains_name(&item.attrs, sym::rustc_object_lifetime_default) { - let object_lifetime_default_reprs: String = result - .iter() - .map(|set| match *set { - Set1::Empty => "BaseDefault".into(), - Set1::One(Region::Static) => "'static".into(), - Set1::One(Region::EarlyBound(mut i, _, _)) => generics - .params - .iter() - .find_map(|param| match param.kind { - GenericParamKind::Lifetime { .. } => { - if i == 0 { - return Some(param.name.ident().to_string().into()); - } - i -= 1; - None +fn compute_object_lifetime_defaults( + tcx: TyCtxt<'_>, + item: &hir::Item<'_>, +) -> Option> { + match item.kind { + hir::ItemKind::Struct(_, ref generics) + | hir::ItemKind::Union(_, ref generics) + | hir::ItemKind::Enum(_, ref generics) + | hir::ItemKind::OpaqueTy(hir::OpaqueTy { ref generics, impl_trait_fn: None, .. }) + | hir::ItemKind::TyAlias(_, ref generics) + | hir::ItemKind::Trait(_, _, ref generics, ..) => { + let result = object_lifetime_defaults_for_item(tcx, generics); + + // Debugging aid. + let attrs = tcx.hir().attrs(item.hir_id()); + if tcx.sess.contains_name(attrs, sym::rustc_object_lifetime_default) { + let object_lifetime_default_reprs: String = result + .iter() + .map(|set| match *set { + Set1::Empty => "BaseDefault".into(), + Set1::One(Region::Static) => "'static".into(), + Set1::One(Region::EarlyBound(mut i, _, _)) => generics + .params + .iter() + .find_map(|param| match param.kind { + GenericParamKind::Lifetime { .. } => { + if i == 0 { + return Some(param.name.ident().to_string().into()); } - _ => None, - }) - .unwrap(), - Set1::One(_) => bug!(), - Set1::Many => "Ambiguous".into(), - }) - .collect::>>() - .join(","); - tcx.sess.span_err(item.span, &object_lifetime_default_reprs); - } - - map.insert(item.hir_id(), result); + i -= 1; + None + } + _ => None, + }) + .unwrap(), + Set1::One(_) => bug!(), + Set1::Many => "Ambiguous".into(), + }) + .collect::>>() + .join(","); + tcx.sess.span_err(item.span, &object_lifetime_default_reprs); } - _ => {} + + Some(result) } + _ => None, } - map } /// Scan the bounds and where-clauses on parameters to extract bounds @@ -1388,18 +1775,22 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> { tcx: *tcx, map, scope: &wrap_scope, - trait_ref_hack: self.trait_ref_hack, is_in_fn_syntax: self.is_in_fn_syntax, is_in_const_generic: self.is_in_const_generic, + trait_definition_only: self.trait_definition_only, labels_in_fn, xcrate_object_lifetime_defaults, lifetime_uses, missing_named_lifetime_spots, }; - debug!("entering scope {:?}", this.scope); - f(self.scope, &mut this); - this.check_uses_for_lifetimes_defined_by_scope(); - debug!("exiting scope {:?}", this.scope); + let span = tracing::debug_span!("scope", scope = ?TruncatedScopeDebug(&this.scope)); + { + let _enter = span.enter(); + f(self.scope, &mut this); + if !self.trait_definition_only { + this.check_uses_for_lifetimes_defined_by_scope(); + } + } self.labels_in_fn = this.labels_in_fn; self.xcrate_object_lifetime_defaults = this.xcrate_object_lifetime_defaults; self.missing_named_lifetime_spots = this.missing_named_lifetime_spots; @@ -1551,7 +1942,7 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> { .values() .flat_map(|region| match region { Region::EarlyBound(_, def_id, _) - | Region::LateBound(_, def_id, _) + | Region::LateBound(_, _, def_id, _) | Region::Free(_, def_id) => Some(*def_id), Region::LateBoundAnon(..) | Region::Static => None, @@ -1697,6 +2088,7 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> { fn visit_early_late( &mut self, parent_id: Option, + hir_id: hir::HirId, decl: &'tcx hir::FnDecl<'tcx>, generics: &'tcx hir::Generics<'tcx>, walk: F, @@ -1706,31 +2098,34 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> { insert_late_bound_lifetimes(self.map, decl, generics); // Find the start of nested early scopes, e.g., in methods. - let mut index = 0; + let mut next_early_index = 0; if let Some(parent_id) = parent_id { let parent = self.tcx.hir().expect_item(parent_id); if sub_items_have_self_param(&parent.kind) { - index += 1; // Self comes before lifetimes + next_early_index += 1; // Self comes before lifetimes } match parent.kind { hir::ItemKind::Trait(_, _, ref generics, ..) | hir::ItemKind::Impl(hir::Impl { ref generics, .. }) => { - index += generics.params.len() as u32; + next_early_index += generics.params.len() as u32; } _ => {} } } let mut non_lifetime_count = 0; - let lifetimes = generics + let mut named_late_bound_vars = 0; + let lifetimes: FxHashMap = generics .params .iter() .filter_map(|param| match param.kind { GenericParamKind::Lifetime { .. } => { if self.map.late_bound.contains(¶m.hir_id) { - Some(Region::late(&self.tcx.hir(), param)) + let late_bound_idx = named_late_bound_vars; + named_late_bound_vars += 1; + Some(Region::late(late_bound_idx, &self.tcx.hir(), param)) } else { - Some(Region::early(&self.tcx.hir(), &mut index, param)) + Some(Region::early(&self.tcx.hir(), &mut next_early_index, param)) } } GenericParamKind::Type { .. } | GenericParamKind::Const { .. } => { @@ -1739,14 +2134,35 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> { } }) .collect(); - let next_early_index = index + non_lifetime_count; + let next_early_index = next_early_index + non_lifetime_count; + let binders: Vec<_> = generics + .params + .iter() + .filter_map(|param| match param.kind { + GenericParamKind::Lifetime { .. } + if self.map.late_bound.contains(¶m.hir_id) => + { + Some(param) + } + _ => None, + }) + .enumerate() + .map(|(late_bound_idx, param)| { + let pair = Region::late(late_bound_idx as u32, &self.tcx.hir(), param); + let r = late_region_as_bound_region(self.tcx, &pair.1); + r + }) + .collect(); + self.map.late_bound_vars.insert(hir_id, binders); let scope = Scope::Binder { + hir_id, lifetimes, next_early_index, s: self.scope, opaque_type_parent: true, track_lifetime_uses: false, + scope_type: BinderScopeType::Normal, }; self.with(scope, move |old_scope, this| { this.check_lifetime_params(old_scope, &generics.params); @@ -1769,7 +2185,9 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> { Scope::Binder { s, .. } | Scope::Body { s, .. } | Scope::Elision { s, .. } - | Scope::ObjectLifetimeDefault { s, .. } => scope = s, + | Scope::ObjectLifetimeDefault { s, .. } + | Scope::Supertrait { s, .. } + | Scope::TraitRefBoundary { s, .. } => scope = s, } } } @@ -1818,7 +2236,7 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> { break None; } - Scope::Binder { ref lifetimes, s, .. } => { + Scope::Binder { ref lifetimes, scope_type, s, .. } => { match lifetime_ref.name { LifetimeName::Param(param_name) => { if let Some(&def) = lifetimes.get(¶m_name.normalize_to_macros_2_0()) @@ -1828,12 +2246,17 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> { } _ => bug!("expected LifetimeName::Param"), } - - late_depth += 1; + match scope_type { + BinderScopeType::Normal => late_depth += 1, + BinderScopeType::Concatenating => {} + } scope = s; } - Scope::Elision { s, .. } | Scope::ObjectLifetimeDefault { s, .. } => { + Scope::Elision { s, .. } + | Scope::ObjectLifetimeDefault { s, .. } + | Scope::Supertrait { s, .. } + | Scope::TraitRefBoundary { s, .. } => { scope = s; } } @@ -1858,10 +2281,10 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> { } // Check for fn-syntax conflicts with in-band lifetime definitions - if self.is_in_fn_syntax { + if !self.trait_definition_only && self.is_in_fn_syntax { match def { Region::EarlyBound(_, _, LifetimeDefOrigin::InBand) - | Region::LateBound(_, _, LifetimeDefOrigin::InBand) => { + | Region::LateBound(_, _, _, LifetimeDefOrigin::InBand) => { struct_span_err!( self.tcx.sess, lifetime_ref.span, @@ -1880,6 +2303,7 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> { LifetimeDefOrigin::ExplicitOrElided | LifetimeDefOrigin::Error, ) | Region::LateBound( + _, _, _, LifetimeDefOrigin::ExplicitOrElided | LifetimeDefOrigin::Error, @@ -1915,7 +2339,7 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> { } let mut elide_lifetimes = true; - let lifetimes = generic_args + let lifetimes: Vec<_> = generic_args .args .iter() .filter_map(|arg| match arg { @@ -1928,8 +2352,10 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> { _ => None, }) .collect(); + // We short-circuit here if all are elided in order to pluralize + // possible errors if elide_lifetimes { - self.resolve_elided_lifetimes(lifetimes); + self.resolve_elided_lifetimes(&lifetimes); } else { lifetimes.iter().for_each(|lt| self.visit_lifetime(lt)); } @@ -1982,7 +2408,9 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> { Scope::Binder { s, .. } | Scope::Elision { s, .. } - | Scope::ObjectLifetimeDefault { s, .. } => { + | Scope::ObjectLifetimeDefault { s, .. } + | Scope::Supertrait { s, .. } + | Scope::TraitRefBoundary { s, .. } => { scope = s; } } @@ -1990,45 +2418,47 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> { }; let map = &self.map; - let unsubst = if let Some(def_id) = def_id.as_local() { + let set_to_region = |set: &ObjectLifetimeDefault| match *set { + Set1::Empty => { + if in_body { + None + } else { + Some(Region::Static) + } + } + Set1::One(r) => { + let lifetimes = generic_args.args.iter().filter_map(|arg| match arg { + GenericArg::Lifetime(lt) => Some(lt), + _ => None, + }); + r.subst(lifetimes, map) + } + Set1::Many => None, + }; + if let Some(def_id) = def_id.as_local() { let id = self.tcx.hir().local_def_id_to_hir_id(def_id); - &map.object_lifetime_defaults[&id] + self.tcx.object_lifetime_defaults(id).unwrap().iter().map(set_to_region).collect() } else { let tcx = self.tcx; - self.xcrate_object_lifetime_defaults.entry(def_id).or_insert_with(|| { - tcx.generics_of(def_id) - .params - .iter() - .filter_map(|param| match param.kind { - GenericParamDefKind::Type { object_lifetime_default, .. } => { - Some(object_lifetime_default) - } - GenericParamDefKind::Lifetime | GenericParamDefKind::Const => None, - }) - .collect() - }) - }; - debug!("visit_segment_args: unsubst={:?}", unsubst); - unsubst - .iter() - .map(|set| match *set { - Set1::Empty => { - if in_body { - None - } else { - Some(Region::Static) - } - } - Set1::One(r) => { - let lifetimes = generic_args.args.iter().filter_map(|arg| match arg { - GenericArg::Lifetime(lt) => Some(lt), - _ => None, - }); - r.subst(lifetimes, map) - } - Set1::Many => None, - }) - .collect() + self.xcrate_object_lifetime_defaults + .entry(def_id) + .or_insert_with(|| { + tcx.generics_of(def_id) + .params + .iter() + .filter_map(|param| match param.kind { + GenericParamDefKind::Type { object_lifetime_default, .. } => { + Some(object_lifetime_default) + } + GenericParamDefKind::Lifetime + | GenericParamDefKind::Const { .. } => None, + }) + .collect() + }) + .iter() + .map(set_to_region) + .collect() + } }); debug!("visit_segment_args: object_lifetime_defaults={:?}", object_lifetime_defaults); @@ -2079,23 +2509,139 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> { let has_lifetime_parameter = generic_args.args.iter().any(|arg| matches!(arg, GenericArg::Lifetime(_))); - // Resolve lifetimes found in the type `XX` from `Item = XX` bindings. - for b in generic_args.bindings { + // Resolve lifetimes found in the bindings, so either in the type `XX` in `Item = XX` or + // in the trait ref `YY<...>` in `Item: YY<...>`. + for binding in generic_args.bindings { let scope = Scope::ObjectLifetimeDefault { lifetime: if has_lifetime_parameter { None } else { Some(Region::Static) }, s: self.scope, }; - self.with(scope, |_, this| this.visit_assoc_type_binding(b)); + if let Some(type_def_id) = type_def_id { + let lifetimes = LifetimeContext::supertrait_hrtb_lifetimes( + self.tcx, + type_def_id, + binding.ident, + ); + self.with(scope, |_, this| { + let scope = + Scope::Supertrait { lifetimes: lifetimes.unwrap_or(vec![]), s: this.scope }; + this.with(scope, |_, this| this.visit_assoc_type_binding(binding)); + }); + } else { + self.with(scope, |_, this| this.visit_assoc_type_binding(binding)); + } + } + } + + /// Returns all the late-bound vars that come into scope from supertrait HRTBs, based on the + /// associated type name and starting trait. + /// For example, imagine we have + /// ```rust + /// trait Foo<'a, 'b> { + /// type As; + /// } + /// trait Bar<'b>: for<'a> Foo<'a, 'b> {} + /// trait Bar: for<'b> Bar<'b> {} + /// ``` + /// In this case, if we wanted to the supertrait HRTB lifetimes for `As` on + /// the starting trait `Bar`, we would return `Some(['b, 'a])`. + fn supertrait_hrtb_lifetimes( + tcx: TyCtxt<'tcx>, + def_id: DefId, + assoc_name: Ident, + ) -> Option> { + let trait_defines_associated_type_named = |trait_def_id: DefId| { + tcx.associated_items(trait_def_id) + .find_by_name_and_kind(tcx, assoc_name, ty::AssocKind::Type, trait_def_id) + .is_some() + }; + + use smallvec::{smallvec, SmallVec}; + let mut stack: SmallVec<[(DefId, SmallVec<[ty::BoundVariableKind; 8]>); 8]> = + smallvec![(def_id, smallvec![])]; + let mut visited: FxHashSet = FxHashSet::default(); + loop { + let (def_id, bound_vars) = match stack.pop() { + Some(next) => next, + None => break None, + }; + // See issue #83753. If someone writes an associated type on a non-trait, just treat it as + // there being no supertrait HRTBs. + match tcx.def_kind(def_id) { + DefKind::Trait | DefKind::TraitAlias | DefKind::Impl => {} + _ => break None, + } + + if trait_defines_associated_type_named(def_id) { + break Some(bound_vars.into_iter().collect()); + } + let predicates = + tcx.super_predicates_that_define_assoc_type((def_id, Some(assoc_name))); + let obligations = predicates.predicates.iter().filter_map(|&(pred, _)| { + let bound_predicate = pred.kind(); + match bound_predicate.skip_binder() { + ty::PredicateKind::Trait(data, _) => { + // The order here needs to match what we would get from `subst_supertrait` + let pred_bound_vars = bound_predicate.bound_vars(); + let mut all_bound_vars = bound_vars.clone(); + all_bound_vars.extend(pred_bound_vars.iter()); + let super_def_id = data.trait_ref.def_id; + Some((super_def_id, all_bound_vars)) + } + _ => None, + } + }); + + let obligations = obligations.filter(|o| visited.insert(o.0)); + stack.extend(obligations); } } + #[tracing::instrument(level = "debug", skip(self))] fn visit_fn_like_elision( &mut self, inputs: &'tcx [hir::Ty<'tcx>], output: Option<&'tcx hir::Ty<'tcx>>, ) { debug!("visit_fn_like_elision: enter"); - let arg_scope = Scope::Elision { elide: Elide::FreshLateAnon(Cell::new(0)), s: self.scope }; + let mut scope = &*self.scope; + let hir_id = loop { + match scope { + Scope::Binder { hir_id, .. } => { + break *hir_id; + } + Scope::Body { id, .. } => break id.hir_id, + Scope::ObjectLifetimeDefault { ref s, .. } + | Scope::Elision { ref s, .. } + | Scope::Supertrait { ref s, .. } + | Scope::TraitRefBoundary { ref s, .. } => { + scope = *s; + } + Scope::Root => { + // See issue #83907. Just bail out from looking inside. + self.tcx.sess.delay_span_bug( + rustc_span::DUMMY_SP, + "In fn_like_elision without appropriate scope above", + ); + return; + } + } + }; + // While not strictly necessary, we gather anon lifetimes *before* actually + // visiting the argument types. + let mut gather = GatherAnonLifetimes { anon_count: 0 }; + for input in inputs { + gather.visit_ty(input); + } + let late_bound_vars = self.map.late_bound_vars.entry(hir_id).or_default(); + let named_late_bound_vars = late_bound_vars.len() as u32; + late_bound_vars.extend( + (0..gather.anon_count).map(|var| ty::BoundVariableKind::Region(ty::BrAnon(var))), + ); + let arg_scope = Scope::Elision { + elide: Elide::FreshLateAnon(named_late_bound_vars, Cell::new(0)), + s: self.scope, + }; self.with(arg_scope, |_, this| { for input in inputs { this.visit_ty(input); @@ -2107,7 +2653,7 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> { None => return, }; - debug!("visit_fn_like_elision: determine output"); + debug!("determine output"); // Figure out if there's a body we can get argument names from, // and whether there's a `self` argument (treated specially). @@ -2273,11 +2819,10 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> { Elide::Error(arg_lifetimes) }; - debug!("visit_fn_like_elision: elide={:?}", elide); + debug!(?elide); let scope = Scope::Elision { elide, s: self.scope }; self.with(scope, |_, this| this.visit_ty(output)); - debug!("visit_fn_like_elision: exit"); struct GatherLifetimes<'a> { map: &'a NamedRegionMap, @@ -2298,7 +2843,7 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> { self.outer_index.shift_in(1); } match ty.kind { - hir::TyKind::TraitObject(bounds, ref lifetime) => { + hir::TyKind::TraitObject(bounds, ref lifetime, _) => { for bound in bounds { self.visit_poly_trait_ref(bound, hir::TraitBoundModifier::None); } @@ -2351,21 +2896,60 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> { fn visit_lifetime(&mut self, lifetime_ref: &hir::Lifetime) { if let Some(&lifetime) = self.map.defs.get(&lifetime_ref.hir_id) { match lifetime { - Region::LateBound(debruijn, _, _) | Region::LateBoundAnon(debruijn, _) + Region::LateBound(debruijn, _, _, _) + | Region::LateBoundAnon(debruijn, _, _) if debruijn < self.outer_index => { self.have_bound_regions = true; } _ => { + // FIXME(jackh726): nested trait refs? self.lifetimes.insert(lifetime.shifted_out_to_binder(self.outer_index)); } } } } } + + struct GatherAnonLifetimes { + anon_count: u32, + } + impl<'v> Visitor<'v> for GatherAnonLifetimes { + type Map = intravisit::ErasedMap<'v>; + + fn nested_visit_map(&mut self) -> NestedVisitorMap { + NestedVisitorMap::None + } + + fn visit_ty(&mut self, ty: &hir::Ty<'_>) { + // If we enter a `BareFn`, then we enter a *new* binding scope + if let hir::TyKind::BareFn(_) = ty.kind { + return; + } + intravisit::walk_ty(self, ty); + } + + fn visit_generic_args( + &mut self, + path_span: Span, + generic_args: &'v hir::GenericArgs<'v>, + ) { + // parenthesized args enter a new elison scope + if generic_args.parenthesized { + return; + } + intravisit::walk_generic_args(self, path_span, generic_args) + } + + fn visit_lifetime(&mut self, lifetime_ref: &hir::Lifetime) { + if lifetime_ref.is_elided() { + self.anon_count += 1; + } + } + } } - fn resolve_elided_lifetimes(&mut self, lifetime_refs: Vec<&'tcx hir::Lifetime>) { + fn resolve_elided_lifetimes(&mut self, lifetime_refs: &[&'tcx hir::Lifetime]) { debug!("resolve_elided_lifetimes(lifetime_refs={:?})", lifetime_refs); if lifetime_refs.is_empty() { @@ -2384,7 +2968,7 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> { Scope::Root => break None, - Scope::Binder { s, ref lifetimes, .. } => { + Scope::Binder { s, ref lifetimes, scope_type, .. } => { // collect named lifetimes for suggestions for name in lifetimes.keys() { if let hir::ParamName::Plain(name) = name { @@ -2392,15 +2976,20 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> { lifetime_spans.push(name.span); } } - late_depth += 1; + match scope_type { + BinderScopeType::Normal => late_depth += 1, + BinderScopeType::Concatenating => {} + } scope = s; } Scope::Elision { ref elide, ref s, .. } => { let lifetime = match *elide { - Elide::FreshLateAnon(ref counter) => { + Elide::FreshLateAnon(named_late_bound_vars, ref counter) => { for lifetime_ref in lifetime_refs { - let lifetime = Region::late_anon(counter).shifted(late_depth); + let lifetime = Region::late_anon(named_late_bound_vars, counter) + .shifted(late_depth); + self.insert_lifetime(lifetime_ref, lifetime); } return; @@ -2421,7 +3010,8 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> { scope = s; } Scope::ObjectLifetimeDefault { ref s, .. } - | Scope::Elision { ref s, .. } => { + | Scope::Elision { ref s, .. } + | Scope::TraitRefBoundary { ref s, .. } => { scope = s; } _ => break, @@ -2437,7 +3027,9 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> { return; } - Scope::ObjectLifetimeDefault { s, .. } => { + Scope::ObjectLifetimeDefault { s, .. } + | Scope::Supertrait { s, .. } + | Scope::TraitRefBoundary { s, .. } => { scope = s; } } @@ -2546,8 +3138,11 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> { let mut scope = self.scope; let lifetime = loop { match *scope { - Scope::Binder { s, .. } => { - late_depth += 1; + Scope::Binder { s, scope_type, .. } => { + match scope_type { + BinderScopeType::Normal => late_depth += 1, + BinderScopeType::Concatenating => {} + } scope = s; } @@ -2556,6 +3151,10 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> { Scope::Body { .. } | Scope::ObjectLifetimeDefault { lifetime: None, .. } => return, Scope::ObjectLifetimeDefault { lifetime: Some(l), .. } => break l, + + Scope::Supertrait { s, .. } | Scope::TraitRefBoundary { s, .. } => { + scope = s; + } } }; self.insert_lifetime(lifetime_ref, lifetime.shifted(late_depth)); @@ -2679,7 +3278,9 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> { match *old_scope { Scope::Body { s, .. } | Scope::Elision { s, .. } - | Scope::ObjectLifetimeDefault { s, .. } => { + | Scope::ObjectLifetimeDefault { s, .. } + | Scope::Supertrait { s, .. } + | Scope::TraitRefBoundary { s, .. } => { old_scope = s; } @@ -2725,7 +3326,7 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> { // A lifetime only used in a fn argument could as well // be replaced with `'_`, as that would generate a // fresh name, too. - Scope::Elision { elide: Elide::FreshLateAnon(_), .. } => break true, + Scope::Elision { elide: Elide::FreshLateAnon(..), .. } => break true, // In the return type or other such place, `'_` is not // going to make a fresh name, so we cannot @@ -2735,17 +3336,18 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> { elide: Elide::Exact(_) | Elide::Error(_) | Elide::Forbid, .. } => break false, - Scope::ObjectLifetimeDefault { s, .. } => scope = s, + Scope::ObjectLifetimeDefault { s, .. } + | Scope::Supertrait { s, .. } + | Scope::TraitRefBoundary { s, .. } => scope = s, } } } + #[tracing::instrument(level = "debug", skip(self))] fn insert_lifetime(&mut self, lifetime_ref: &'tcx hir::Lifetime, def: Region) { debug!( - "insert_lifetime: {} resolved to {:?} span={:?}", - self.tcx.hir().node_to_string(lifetime_ref.hir_id), - def, - self.tcx.sess.source_map().span_to_string(lifetime_ref.span) + node = ?self.tcx.hir().node_to_string(lifetime_ref.hir_id), + span = ?self.tcx.sess.source_map().span_to_string(lifetime_ref.span) ); self.map.defs.insert(lifetime_ref.hir_id, def); @@ -2755,16 +3357,16 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> { } Region::Free(_, def_id) - | Region::LateBound(_, def_id, _) + | Region::LateBound(_, _, def_id, _) | Region::EarlyBound(_, def_id, _) => { // A lifetime declared by the user. let track_lifetime_uses = self.track_lifetime_uses(); - debug!("insert_lifetime: track_lifetime_uses={}", track_lifetime_uses); + debug!(?track_lifetime_uses); if track_lifetime_uses && !self.lifetime_uses.contains_key(&def_id) { - debug!("insert_lifetime: first use of {:?}", def_id); + debug!("first use of {:?}", def_id); self.lifetime_uses.insert(def_id, LifetimeUseSet::One(lifetime_ref)); } else { - debug!("insert_lifetime: many uses of {:?}", def_id); + debug!("many uses of {:?}", def_id); self.lifetime_uses.insert(def_id, LifetimeUseSet::Many); } } @@ -2790,13 +3392,12 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> { /// "Constrained" basically means that it appears in any type but /// not amongst the inputs to a projection. In other words, `<&'a /// T as Trait<''b>>::Foo` does not constrain `'a` or `'b`. +#[tracing::instrument(level = "debug", skip(map))] fn insert_late_bound_lifetimes( map: &mut NamedRegionMap, decl: &hir::FnDecl<'_>, generics: &hir::Generics<'_>, ) { - debug!("insert_late_bound_lifetimes(decl={:?}, generics={:?})", decl, generics); - let mut constrained_by_input = ConstrainedCollector::default(); for arg_ty in decl.inputs { constrained_by_input.visit_ty(arg_ty); @@ -2805,7 +3406,7 @@ fn insert_late_bound_lifetimes( let mut appears_in_output = AllCollector::default(); intravisit::walk_fn_ret_ty(&mut appears_in_output, &decl.output); - debug!("insert_late_bound_lifetimes: constrained_by_input={:?}", constrained_by_input.regions); + debug!(?constrained_by_input.regions); // Walk the lifetimes that appear in where clauses. // @@ -2825,10 +3426,7 @@ fn insert_late_bound_lifetimes( } } - debug!( - "insert_late_bound_lifetimes: appears_in_where_clause={:?}", - appears_in_where_clause.regions - ); + debug!(?appears_in_where_clause.regions); // Late bound regions are those that: // - appear in the inputs @@ -2855,11 +3453,7 @@ fn insert_late_bound_lifetimes( continue; } - debug!( - "insert_late_bound_lifetimes: lifetime {:?} with id {:?} is late-bound", - param.name.ident(), - param.hir_id - ); + debug!("lifetime {:?} with id {:?} is late-bound", param.name.ident(), param.hir_id); let inserted = map.late_bound.insert(param.hir_id); assert!(inserted, "visited lifetime {:?} twice", param.hir_id); diff --git a/compiler/rustc_resolve/src/lib.rs b/compiler/rustc_resolve/src/lib.rs index 7aee718bfa962..129954381c9ba 100644 --- a/compiler/rustc_resolve/src/lib.rs +++ b/compiler/rustc_resolve/src/lib.rs @@ -6,7 +6,7 @@ //! Paths in macros, imports, expressions, types, patterns are resolved here. //! Label and lifetime names are resolved here as well. //! -//! Type-relative name resolution (methods, fields, associated items) happens in `librustc_typeck`. +//! Type-relative name resolution (methods, fields, associated items) happens in `rustc_typeck`. #![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")] #![feature(box_patterns)] @@ -14,9 +14,11 @@ #![feature(control_flow_enum)] #![feature(crate_visibility_modifier)] #![feature(format_args_capture)] +#![feature(iter_zip)] #![feature(nll)] -#![feature(or_patterns)] +#![cfg_attr(bootstrap, feature(or_patterns))] #![recursion_limit = "256"] +#![allow(rustdoc::private_intra_doc_links)] pub use rustc_hir::def::{Namespace, PerNS}; @@ -25,7 +27,6 @@ use Determinacy::*; use rustc_arena::{DroplessArena, TypedArena}; use rustc_ast::node_id::NodeMap; use rustc_ast::ptr::P; -use rustc_ast::unwrap_or; use rustc_ast::visit::{self, Visitor}; use rustc_ast::{self as ast, NodeId}; use rustc_ast::{Crate, CRATE_NODE_ID}; @@ -37,12 +38,12 @@ use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexMap}; use rustc_data_structures::ptr_key::PtrKey; use rustc_data_structures::sync::Lrc; use rustc_errors::{struct_span_err, Applicability, DiagnosticBuilder}; -use rustc_expand::base::{SyntaxExtension, SyntaxExtensionKind}; +use rustc_expand::base::{DeriveResolutions, SyntaxExtension, SyntaxExtensionKind}; use rustc_hir::def::Namespace::*; use rustc_hir::def::{self, CtorOf, DefKind, NonMacroAttrKind, PartialRes}; use rustc_hir::def_id::{CrateNum, DefId, DefIdMap, LocalDefId, CRATE_DEF_INDEX}; use rustc_hir::definitions::{DefKey, DefPathData, Definitions}; -use rustc_hir::{PrimTy, TraitCandidate}; +use rustc_hir::TraitCandidate; use rustc_index::vec::IndexVec; use rustc_metadata::creader::{CStore, CrateLoader}; use rustc_middle::hir::exports::ExportMap; @@ -108,7 +109,9 @@ enum Scope<'a> { DeriveHelpersCompat, MacroRules(MacroRulesScopeRef<'a>), CrateRoot, - Module(Module<'a>), + // The node ID is for reporting the `PROC_MACRO_DERIVE_RESOLUTION_FALLBACK` + // lint if it should be reported. + Module(Module<'a>, Option), RegisteredAttrs, MacroUsePrelude, BuiltinAttrs, @@ -122,13 +125,17 @@ enum Scope<'a> { /// with different restrictions when looking up the resolution. /// This enum is currently used only for early resolution (imports and macros), /// but not for late resolution yet. -enum ScopeSet { +#[derive(Clone, Copy)] +enum ScopeSet<'a> { /// All scopes with the given namespace. All(Namespace, /*is_import*/ bool), /// Crate root, then extern prelude (used for mixed 2015-2018 mode in macros). AbsolutePath(Namespace), /// All scopes with macro namespace and the given macro kind restriction. Macro(MacroKind), + /// All scopes with the given namespace, used for partially performing late resolution. + /// The node id enables lints and is used for reporting them. + Late(Namespace, Module<'a>, Option), } /// Everything you need to know about a name's location to resolve it. @@ -156,6 +163,12 @@ impl<'a> ParentScope<'a> { } } +#[derive(Copy, Debug, Clone)] +enum ImplTraitContext { + Existential, + Universal(LocalDefId), +} + #[derive(Eq)] struct BindingError { name: Symbol, @@ -222,7 +235,7 @@ enum ResolutionError<'a> { ), /// Error E0530: `X` bindings cannot shadow `Y`s. BindingShadowsSomethingUnacceptable(&'static str, Symbol, &'a NameBinding<'a>), - /// Error E0128: type parameters with a default cannot use forward-declared identifiers. + /// Error E0128: generic parameters with a default cannot use forward-declared identifiers. ForwardDeclaredTyParam, // FIXME(const_generics_defaults) /// ERROR E0770: the type of const parameters must not depend on other generic parameters. ParamInTyOfConstParam(Symbol), @@ -232,7 +245,7 @@ enum ResolutionError<'a> { /// /// This error is only emitted when using `min_const_generics`. ParamInNonTrivialAnonConst { name: Symbol, is_type: bool }, - /// Error E0735: type parameters with a default cannot use `Self` + /// Error E0735: generic parameters with a default cannot use `Self` SelfInTyParamDefault, /// Error E0767: use of unreachable label UnreachableLabel { name: Symbol, definition_span: Span, suggestion: Option }, @@ -839,6 +852,12 @@ enum BuiltinMacroState { AlreadySeen(Span), } +struct DeriveData { + resolutions: DeriveResolutions, + helper_attrs: Vec<(usize, Ident)>, + has_derive_copy: bool, +} + /// The main resolver class. /// /// This is the visitor that walks the whole crate. @@ -872,6 +891,10 @@ pub struct Resolver<'a> { /// "self-confirming" import resolutions during import validation. unusable_binding: Option<&'a NameBinding<'a>>, + // Spans for local variables found during pattern resolution. + // Used for suggestions during error reporting. + pat_span_map: NodeMap, + /// Resolutions for nodes that have a single resolution. partial_res_map: NodeMap, /// Resolutions for import nodes, which have multiple resolutions in different namespaces. @@ -961,8 +984,9 @@ pub struct Resolver<'a> { output_macro_rules_scopes: FxHashMap>, /// Helper attributes that are in scope for the given expansion. helper_attrs: FxHashMap>, - /// Resolutions for paths inside the `#[derive(...)]` attribute with the given `ExpnId`. - derive_resolutions: FxHashMap, ast::Path)>>, + /// Ready or in-progress results of resolving paths inside the `#[derive(...)]` attribute + /// with the given `ExpnId`. + derive_data: FxHashMap, /// Avoid duplicated errors for "name already defined". name_already_seen: FxHashMap, @@ -989,8 +1013,9 @@ pub struct Resolver<'a> { /// Indices of unnamed struct or variant fields with unresolved attributes. placeholder_field_indices: FxHashMap, /// When collecting definitions from an AST fragment produced by a macro invocation `ExpnId` - /// we know what parent node that fragment should be attached to thanks to this table. - invocation_parents: FxHashMap, + /// we know what parent node that fragment should be attached to thanks to this table, + /// and how the `impl Trait` fragments were introduced. + invocation_parents: FxHashMap, next_disambiguator: FxHashMap<(LocalDefId, DefPathData), u32>, /// Some way to know that we are in a *trait* impl in `visit_assoc_item`. @@ -1205,7 +1230,7 @@ impl<'a> Resolver<'a> { node_id_to_def_id.insert(CRATE_NODE_ID, root); let mut invocation_parents = FxHashMap::default(); - invocation_parents.insert(ExpnId::root(), root); + invocation_parents.insert(ExpnId::root(), (root, ImplTraitContext::Existential)); let mut extern_prelude: FxHashMap> = session .opts @@ -1249,6 +1274,7 @@ impl<'a> Resolver<'a> { last_import_segment: false, unusable_binding: None, + pat_span_map: Default::default(), partial_res_map: Default::default(), import_res_map: Default::default(), label_res_map: Default::default(), @@ -1297,7 +1323,7 @@ impl<'a> Resolver<'a> { invocation_parent_scopes: Default::default(), output_macro_rules_scopes: Default::default(), helper_attrs: Default::default(), - derive_resolutions: Default::default(), + derive_data: Default::default(), local_macro_def_scopes: FxHashMap::default(), name_already_seen: FxHashMap::default(), potentially_unused_imports: Vec::new(), @@ -1459,7 +1485,7 @@ impl<'a> Resolver<'a> { self.visit_scopes(ScopeSet::All(TypeNS, false), parent_scope, ctxt, |this, scope, _, _| { match scope { - Scope::Module(module) => { + Scope::Module(module, _) => { this.traits_in_module(module, assoc_item, &mut found_traits); } Scope::StdLibPrelude => { @@ -1623,7 +1649,7 @@ impl<'a> Resolver<'a> { /// If the callback returns `Some` result, we stop visiting scopes and return it. fn visit_scopes( &mut self, - scope_set: ScopeSet, + scope_set: ScopeSet<'a>, parent_scope: &ParentScope<'a>, ctxt: SyntaxContext, mut visitor: impl FnMut( @@ -1679,12 +1705,17 @@ impl<'a> Resolver<'a> { ScopeSet::All(ns, _) => (ns, None, false), ScopeSet::AbsolutePath(ns) => (ns, None, true), ScopeSet::Macro(macro_kind) => (MacroNS, Some(macro_kind), false), + ScopeSet::Late(ns, ..) => (ns, None, false), + }; + let module = match scope_set { + // Start with the specified module. + ScopeSet::Late(_, module, _) => module, + // Jump out of trait or enum modules, they do not act as scopes. + _ => parent_scope.module.nearest_item_scope(), }; - // Jump out of trait or enum modules, they do not act as scopes. - let module = parent_scope.module.nearest_item_scope(); let mut scope = match ns { _ if is_absolute_path => Scope::CrateRoot, - TypeNS | ValueNS => Scope::Module(module), + TypeNS | ValueNS => Scope::Module(module, None), MacroNS => Scope::DeriveHelpers(parent_scope.expansion), }; let mut ctxt = ctxt.normalize_to_macros_2_0(); @@ -1749,7 +1780,7 @@ impl<'a> Resolver<'a> { MacroRulesScope::Invocation(invoc_id) => { Scope::MacroRules(self.invocation_parent_scopes[&invoc_id].macro_rules) } - MacroRulesScope::Empty => Scope::Module(module), + MacroRulesScope::Empty => Scope::Module(module, None), }, Scope::CrateRoot => match ns { TypeNS => { @@ -1758,10 +1789,16 @@ impl<'a> Resolver<'a> { } ValueNS | MacroNS => break, }, - Scope::Module(module) => { + Scope::Module(module, prev_lint_id) => { use_prelude = !module.no_implicit_prelude; - match self.hygienic_lexical_parent(module, &mut ctxt) { - Some(parent_module) => Scope::Module(parent_module), + let derive_fallback_lint_id = match scope_set { + ScopeSet::Late(.., lint_id) => lint_id, + _ => None, + }; + match self.hygienic_lexical_parent(module, &mut ctxt, derive_fallback_lint_id) { + Some((parent_module, lint_id)) => { + Scope::Module(parent_module, lint_id.or(prev_lint_id)) + } None => { ctxt.adjust(ExpnId::root()); match ns { @@ -1817,6 +1854,7 @@ impl<'a> Resolver<'a> { ribs: &[Rib<'a>], ) -> Option> { assert!(ns == TypeNS || ns == ValueNS); + let orig_ident = ident; if ident.name == kw::Empty { return Some(LexicalScopeBinding::Res(Res::Err)); } @@ -1866,135 +1904,48 @@ impl<'a> Resolver<'a> { _ => continue, }; - let item = self.resolve_ident_in_module_unadjusted( - ModuleOrUniformRoot::Module(module), - ident, - ns, - parent_scope, - record_used, - path_span, - ); - if let Ok(binding) = item { - // The ident resolves to an item. - return Some(LexicalScopeBinding::Item(binding)); - } - match module.kind { ModuleKind::Block(..) => {} // We can see through blocks _ => break, } - } - ident = normalized_ident; - let mut poisoned = None; - loop { - let mut span_data = ident.span.data(); - let opt_module = if let Some(node_id) = record_used_id { - self.hygienic_lexical_parent_with_compatibility_fallback( - module, - &mut span_data.ctxt, - node_id, - &mut poisoned, - ) - } else { - self.hygienic_lexical_parent(module, &mut span_data.ctxt) - }; - ident.span = span_data.span(); - module = unwrap_or!(opt_module, break); - let adjusted_parent_scope = &ParentScope { module, ..*parent_scope }; - let result = self.resolve_ident_in_module_unadjusted( + let item = self.resolve_ident_in_module_unadjusted( ModuleOrUniformRoot::Module(module), ident, ns, - adjusted_parent_scope, + parent_scope, record_used, path_span, ); - - match result { - Ok(binding) => { - if let Some(node_id) = poisoned { - self.lint_buffer.buffer_lint_with_diagnostic( - lint::builtin::PROC_MACRO_DERIVE_RESOLUTION_FALLBACK, - node_id, - ident.span, - &format!("cannot find {} `{}` in this scope", ns.descr(), ident), - BuiltinLintDiagnostics::ProcMacroDeriveResolutionFallback(ident.span), - ); - } - return Some(LexicalScopeBinding::Item(binding)); - } - Err(Determined) => continue, - Err(Undetermined) => { - span_bug!(ident.span, "undetermined resolution during main resolution pass") - } - } - } - - if !module.no_implicit_prelude { - ident.span.adjust(ExpnId::root()); - if ns == TypeNS { - if let Some(binding) = self.extern_prelude_get(ident, !record_used) { - return Some(LexicalScopeBinding::Item(binding)); - } - if let Some(ident) = self.registered_tools.get(&ident) { - let binding = - (Res::ToolMod, ty::Visibility::Public, ident.span, ExpnId::root()) - .to_name_binding(self.arenas); - return Some(LexicalScopeBinding::Item(binding)); - } - } - if let Some(prelude) = self.prelude { - if let Ok(binding) = self.resolve_ident_in_module_unadjusted( - ModuleOrUniformRoot::Module(prelude), - ident, - ns, - parent_scope, - false, - path_span, - ) { - return Some(LexicalScopeBinding::Item(binding)); - } - } - } - - if ns == TypeNS { - if let Some(prim_ty) = PrimTy::from_name(ident.name) { - let binding = - (Res::PrimTy(prim_ty), ty::Visibility::Public, DUMMY_SP, ExpnId::root()) - .to_name_binding(self.arenas); + if let Ok(binding) = item { + // The ident resolves to an item. return Some(LexicalScopeBinding::Item(binding)); } } - - None + self.early_resolve_ident_in_lexical_scope( + orig_ident, + ScopeSet::Late(ns, module, record_used_id), + parent_scope, + record_used, + record_used, + path_span, + ) + .ok() + .map(LexicalScopeBinding::Item) } fn hygienic_lexical_parent( &mut self, module: Module<'a>, ctxt: &mut SyntaxContext, - ) -> Option> { + derive_fallback_lint_id: Option, + ) -> Option<(Module<'a>, Option)> { if !module.expansion.outer_expn_is_descendant_of(*ctxt) { - return Some(self.macro_def_scope(ctxt.remove_mark())); + return Some((self.macro_def_scope(ctxt.remove_mark()), None)); } if let ModuleKind::Block(..) = module.kind { - return Some(module.parent.unwrap().nearest_item_scope()); - } - - None - } - - fn hygienic_lexical_parent_with_compatibility_fallback( - &mut self, - module: Module<'a>, - ctxt: &mut SyntaxContext, - node_id: NodeId, - poisoned: &mut Option, - ) -> Option> { - if let module @ Some(..) = self.hygienic_lexical_parent(module, ctxt) { - return module; + return Some((module.parent.unwrap().nearest_item_scope(), None)); } // We need to support the next case under a deprecation warning @@ -2008,20 +1959,21 @@ impl<'a> Resolver<'a> { // ---- end // ``` // So we have to fall back to the module's parent during lexical resolution in this case. - if let Some(parent) = module.parent { - // Inner module is inside the macro, parent module is outside of the macro. - if module.expansion != parent.expansion - && module.expansion.is_descendant_of(parent.expansion) - { - // The macro is a proc macro derive - if let Some(def_id) = module.expansion.expn_data().macro_def_id { - let ext = self.get_macro_by_def_id(def_id); - if ext.builtin_name.is_none() - && ext.macro_kind() == MacroKind::Derive - && parent.expansion.outer_expn_is_descendant_of(*ctxt) - { - *poisoned = Some(node_id); - return module.parent; + if derive_fallback_lint_id.is_some() { + if let Some(parent) = module.parent { + // Inner module is inside the macro, parent module is outside of the macro. + if module.expansion != parent.expansion + && module.expansion.is_descendant_of(parent.expansion) + { + // The macro is a proc macro derive + if let Some(def_id) = module.expansion.expn_data().macro_def_id { + let ext = self.get_macro_by_def_id(def_id); + if ext.builtin_name.is_none() + && ext.macro_kind() == MacroKind::Derive + && parent.expansion.outer_expn_is_descendant_of(*ctxt) + { + return Some((parent, derive_fallback_lint_id)); + } } } } @@ -2433,8 +2385,10 @@ impl<'a> Resolver<'a> { Applicability::MaybeIncorrect, )), ) - } else { + } else if self.session.edition() == Edition::Edition2015 { (format!("maybe a missing crate `{}`?", ident), None) + } else { + (format!("could not find `{}` in the crate root", ident), None) } } else if i == 0 { if ident @@ -2444,16 +2398,74 @@ impl<'a> Resolver<'a> { .next() .map_or(false, |c| c.is_ascii_uppercase()) { - (format!("use of undeclared type `{}`", ident), None) + // Check whether the name refers to an item in the value namespace. + let suggestion = if ribs.is_some() { + let match_span = match self.resolve_ident_in_lexical_scope( + ident, + ValueNS, + parent_scope, + None, + path_span, + &ribs.unwrap()[ValueNS], + ) { + // Name matches a local variable. For example: + // ``` + // fn f() { + // let Foo: &str = ""; + // println!("{}", Foo::Bar); // Name refers to local + // // variable `Foo`. + // } + // ``` + Some(LexicalScopeBinding::Res(Res::Local(id))) => { + Some(*self.pat_span_map.get(&id).unwrap()) + } + + // Name matches item from a local name binding + // created by `use` declaration. For example: + // ``` + // pub Foo: &str = ""; + // + // mod submod { + // use super::Foo; + // println!("{}", Foo::Bar); // Name refers to local + // // binding `Foo`. + // } + // ``` + Some(LexicalScopeBinding::Item(name_binding)) => { + Some(name_binding.span) + } + _ => None, + }; + + if let Some(span) = match_span { + Some(( + vec![(span, String::from(""))], + format!("`{}` is defined here, but is not a type", ident), + Applicability::MaybeIncorrect, + )) + } else { + None + } + } else { + None + }; + + (format!("use of undeclared type `{}`", ident), suggestion) } else { (format!("use of undeclared crate or module `{}`", ident), None) } } else { let parent = path[i - 1].ident.name; - let parent = if parent == kw::PathRoot { - "crate root".to_owned() - } else { - format!("`{}`", parent) + let parent = match parent { + // ::foo is mounted at the crate root for 2015, and is the extern + // prelude for 2018+ + kw::PathRoot if self.session.edition() > Edition::Edition2015 => { + "the list of imported crates".to_owned() + } + kw::PathRoot | kw::Crate => "the crate root".to_owned(), + _ => { + format!("`{}`", parent) + } }; let mut msg = format!("could not find `{}` in {}", ident, parent); @@ -2577,8 +2589,8 @@ impl<'a> Resolver<'a> { debug!("validate_res_from_ribs({:?})", res); let ribs = &all_ribs[rib_index + 1..]; - // An invalid forward use of a type parameter from a previous default. - if let ForwardTyParamBanRibKind = all_ribs[rib_index].kind { + // An invalid forward use of a generic parameter from a previous default. + if let ForwardGenericParamBanRibKind = all_ribs[rib_index].kind { if record_used { let res_error = if rib_ident.name == kw::SelfUpper { ResolutionError::SelfInTyParamDefault @@ -2602,7 +2614,7 @@ impl<'a> Resolver<'a> { | ClosureOrAsyncRibKind | ModuleRibKind(..) | MacroDefinition(..) - | ForwardTyParamBanRibKind => { + | ForwardGenericParamBanRibKind => { // Nothing to do. Continue. } ItemRibKind(_) | FnItemRibKind | AssocItemRibKind => { @@ -2674,7 +2686,9 @@ impl<'a> Resolver<'a> { // We only forbid constant items if we are inside of type defaults, // for example `struct Foo()]>` - ForwardTyParamBanRibKind => { + ForwardGenericParamBanRibKind => { + // FIXME(const_generic_defaults): we may need to distinguish between + // being in type parameter defaults and const parameter defaults in_ty_param_default = true; continue; } @@ -2767,7 +2781,9 @@ impl<'a> Resolver<'a> { // We only forbid constant items if we are inside of type defaults, // for example `struct Foo()]>` - ForwardTyParamBanRibKind => { + ForwardGenericParamBanRibKind => { + // FIXME(const_generic_defaults): we may need to distinguish between + // being in type parameter defaults and const parameter defaults in_ty_param_default = true; continue; } @@ -2845,6 +2861,11 @@ impl<'a> Resolver<'a> { } } + fn record_pat_span(&mut self, node: NodeId, span: Span) { + debug!("(recording pat) recording {:?} for {:?}", node, span); + self.pat_span_map.insert(node, span); + } + fn is_accessible_from(&self, vis: ty::Visibility, module: Module<'a>) -> bool { vis.is_accessible_from(module.nearest_parent_mod, self) } diff --git a/compiler/rustc_resolve/src/macros.rs b/compiler/rustc_resolve/src/macros.rs index f7010ca94bd2c..10e27f33c2996 100644 --- a/compiler/rustc_resolve/src/macros.rs +++ b/compiler/rustc_resolve/src/macros.rs @@ -4,9 +4,9 @@ use crate::imports::ImportResolver; use crate::Namespace::*; use crate::{AmbiguityError, AmbiguityErrorMisc, AmbiguityKind, BuiltinMacroState, Determinacy}; -use crate::{CrateLint, ParentScope, ResolutionError, Resolver, Scope, ScopeSet, Weak}; +use crate::{CrateLint, DeriveData, ParentScope, ResolutionError, Resolver, Scope, ScopeSet, Weak}; use crate::{ModuleKind, ModuleOrUniformRoot, NameBinding, PathResult, Segment, ToNameBinding}; -use rustc_ast::{self as ast, NodeId}; +use rustc_ast::{self as ast, Inline, ItemKind, ModKind, NodeId}; use rustc_ast_lowering::ResolverAstLowering; use rustc_ast_pretty::pprust; use rustc_attr::StabilityLevel; @@ -14,16 +14,18 @@ use rustc_data_structures::fx::FxHashSet; use rustc_data_structures::ptr_key::PtrKey; use rustc_data_structures::sync::Lrc; use rustc_errors::struct_span_err; -use rustc_expand::base::{Indeterminate, ResolverExpand, SyntaxExtension, SyntaxExtensionKind}; +use rustc_expand::base::{Annotatable, DeriveResolutions, Indeterminate, ResolverExpand}; +use rustc_expand::base::{SyntaxExtension, SyntaxExtensionKind}; use rustc_expand::compile_declarative_macro; -use rustc_expand::expand::{AstFragment, Invocation, InvocationKind}; +use rustc_expand::expand::{AstFragment, Invocation, InvocationKind, SupportsMacroExpansion}; use rustc_feature::is_builtin_attr_name; use rustc_hir::def::{self, DefKind, NonMacroAttrKind}; use rustc_hir::def_id; use rustc_hir::PrimTy; use rustc_middle::middle::stability; use rustc_middle::ty; -use rustc_session::lint::builtin::{LEGACY_DERIVE_HELPERS, SOFT_UNSTABLE, UNUSED_MACROS}; +use rustc_session::lint::builtin::{LEGACY_DERIVE_HELPERS, PROC_MACRO_DERIVE_RESOLUTION_FALLBACK}; +use rustc_session::lint::builtin::{SOFT_UNSTABLE, UNUSED_MACROS}; use rustc_session::lint::BuiltinLintDiagnostics; use rustc_session::parse::feature_err; use rustc_session::Session; @@ -153,6 +155,26 @@ crate fn registered_attrs_and_tools( (registered_attrs, registered_tools) } +// Some feature gates for inner attributes are reported as lints for backward compatibility. +fn soft_custom_inner_attributes_gate(path: &ast::Path, invoc: &Invocation) -> bool { + match &path.segments[..] { + // `#![test]` + [seg] if seg.ident.name == sym::test => return true, + // `#![rustfmt::skip]` on out-of-line modules + [seg1, seg2] if seg1.ident.name == sym::rustfmt && seg2.ident.name == sym::skip => { + if let InvocationKind::Attr { item, .. } = &invoc.kind { + if let Annotatable::Item(item) = item { + if let ItemKind::Mod(_, ModKind::Loaded(_, Inline::No, _)) = item.kind { + return true; + } + } + } + } + _ => {} + } + false +} + impl<'a> ResolverExpand for Resolver<'a> { fn next_node_id(&mut self) -> NodeId { self.next_node_id() @@ -257,16 +279,17 @@ impl<'a> ResolverExpand for Resolver<'a> { // Derives are not included when `invocations` are collected, so we have to add them here. let parent_scope = &ParentScope { derives, ..parent_scope }; - let require_inert = !invoc.fragment_kind.supports_macro_expansion(); + let supports_macro_expansion = invoc.fragment_kind.supports_macro_expansion(); let node_id = self.lint_node_id(eager_expansion_root); let (ext, res) = self.smart_resolve_macro_path( path, kind, - require_inert, + supports_macro_expansion, inner_attr, parent_scope, node_id, force, + soft_custom_inner_attributes_gate(path, invoc), )?; let span = invoc.span(); @@ -326,7 +349,7 @@ impl<'a> ResolverExpand for Resolver<'a> { // nearest closing item - we should try to return the closest parent of the ExpnId self.invocation_parents .get(&expn_id) - .map_or(ast::CRATE_NODE_ID, |id| self.def_id_to_node_id[*id]) + .map_or(ast::CRATE_NODE_ID, |id| self.def_id_to_node_id[id.0]) } fn has_derive_copy(&self, expn_id: ExpnId) -> bool { @@ -336,8 +359,8 @@ impl<'a> ResolverExpand for Resolver<'a> { fn resolve_derives( &mut self, expn_id: ExpnId, - derives: Vec, force: bool, + derive_paths: &dyn Fn() -> DeriveResolutions, ) -> Result<(), Indeterminate> { // Block expansion of the container until we resolve all derives in it. // This is required for two reasons: @@ -345,49 +368,63 @@ impl<'a> ResolverExpand for Resolver<'a> { // is applied, so they have to be produced by the container's expansion rather // than by individual derives. // - Derives in the container need to know whether one of them is a built-in `Copy`. - // FIXME: Try to cache intermediate results to avoid resolving same derives multiple times. + // Temporarily take the data to avoid borrow checker conflicts. + let mut derive_data = mem::take(&mut self.derive_data); + let entry = derive_data.entry(expn_id).or_insert_with(|| DeriveData { + resolutions: derive_paths(), + helper_attrs: Vec::new(), + has_derive_copy: false, + }); let parent_scope = self.invocation_parent_scopes[&expn_id]; - let mut exts = Vec::new(); - let mut helper_attrs = Vec::new(); - let mut has_derive_copy = false; - for path in derives { - exts.push(( - match self.resolve_macro_path( - &path, - Some(MacroKind::Derive), - &parent_scope, - true, - force, - ) { - Ok((Some(ext), _)) => { - let span = - path.segments.last().unwrap().ident.span.normalize_to_macros_2_0(); - helper_attrs - .extend(ext.helper_attrs.iter().map(|name| Ident::new(*name, span))); - has_derive_copy |= ext.builtin_name == Some(sym::Copy); - ext - } - Ok(_) | Err(Determinacy::Determined) => self.dummy_ext(MacroKind::Derive), - Err(Determinacy::Undetermined) => return Err(Indeterminate), - }, - path, - )) + for (i, (path, opt_ext)) in entry.resolutions.iter_mut().enumerate() { + if opt_ext.is_none() { + *opt_ext = Some( + match self.resolve_macro_path( + &path, + Some(MacroKind::Derive), + &parent_scope, + true, + force, + ) { + Ok((Some(ext), _)) => { + if !ext.helper_attrs.is_empty() { + let last_seg = path.segments.last().unwrap(); + let span = last_seg.ident.span.normalize_to_macros_2_0(); + entry.helper_attrs.extend( + ext.helper_attrs + .iter() + .map(|name| (i, Ident::new(*name, span))), + ); + } + entry.has_derive_copy |= ext.builtin_name == Some(sym::Copy); + ext + } + Ok(_) | Err(Determinacy::Determined) => self.dummy_ext(MacroKind::Derive), + Err(Determinacy::Undetermined) => { + assert!(self.derive_data.is_empty()); + self.derive_data = derive_data; + return Err(Indeterminate); + } + }, + ); + } } - self.derive_resolutions.insert(expn_id, exts); - self.helper_attrs.insert(expn_id, helper_attrs); + // Sort helpers in a stable way independent from the derive resolution order. + entry.helper_attrs.sort_by_key(|(i, _)| *i); + self.helper_attrs + .insert(expn_id, entry.helper_attrs.iter().map(|(_, ident)| *ident).collect()); // Mark this derive as having `Copy` either if it has `Copy` itself or if its parent derive // has `Copy`, to support cases like `#[derive(Clone, Copy)] #[derive(Debug)]`. - if has_derive_copy || self.has_derive_copy(parent_scope.expansion) { + if entry.has_derive_copy || self.has_derive_copy(parent_scope.expansion) { self.containers_deriving_copy.insert(expn_id); } + assert!(self.derive_data.is_empty()); + self.derive_data = derive_data; Ok(()) } - fn take_derive_resolutions( - &mut self, - expn_id: ExpnId, - ) -> Option, ast::Path)>> { - self.derive_resolutions.remove(&expn_id) + fn take_derive_resolutions(&mut self, expn_id: ExpnId) -> Option { + self.derive_data.remove(&expn_id).map(|data| data.resolutions) } // The function that implements the resolution logic of `#[cfg_accessible(path)]`. @@ -435,11 +472,12 @@ impl<'a> Resolver<'a> { &mut self, path: &ast::Path, kind: MacroKind, - require_inert: bool, + supports_macro_expansion: SupportsMacroExpansion, inner_attr: bool, parent_scope: &ParentScope<'a>, node_id: NodeId, force: bool, + soft_custom_inner_attributes_gate: bool, ) -> Result<(Lrc, Res), Indeterminate> { let (ext, res) = match self.resolve_macro_path(path, Some(kind), parent_scope, true, force) { @@ -482,8 +520,17 @@ impl<'a> Resolver<'a> { let unexpected_res = if ext.macro_kind() != kind { Some((kind.article(), kind.descr_expected())) - } else if require_inert && matches!(res, Res::Def(..)) { - Some(("a", "non-macro attribute")) + } else if matches!(res, Res::Def(..)) { + match supports_macro_expansion { + SupportsMacroExpansion::No => Some(("a", "non-macro attribute")), + SupportsMacroExpansion::Yes { supports_inner_attrs } => { + if inner_attr && !supports_inner_attrs { + Some(("a", "non-macro inner attribute")) + } else { + None + } + } + } } else { None }; @@ -507,7 +554,7 @@ impl<'a> Resolver<'a> { Res::NonMacroAttr(..) => "custom inner attributes are unstable", _ => unreachable!(), }; - if path == &sym::test { + if soft_custom_inner_attributes_gate { self.session.parse_sess.buffer_lint(SOFT_UNSTABLE, path.span, node_id, msg); } else { feature_err(&self.session.parse_sess, sym::custom_inner_attributes, path.span, msg) @@ -610,7 +657,7 @@ impl<'a> Resolver<'a> { crate fn early_resolve_ident_in_lexical_scope( &mut self, orig_ident: Ident, - scope_set: ScopeSet, + scope_set: ScopeSet<'a>, parent_scope: &ParentScope<'a>, record_used: bool, force: bool, @@ -637,6 +684,7 @@ impl<'a> Resolver<'a> { ScopeSet::All(ns, is_import) => (ns, None, is_import), ScopeSet::AbsolutePath(ns) => (ns, None, false), ScopeSet::Macro(macro_kind) => (MacroNS, Some(macro_kind), false), + ScopeSet::Late(ns, ..) => (ns, None, false), }; // This is *the* result, resolution from the scope closest to the resolved identifier. @@ -745,19 +793,34 @@ impl<'a> Resolver<'a> { Err((Determinacy::Determined, _)) => Err(Determinacy::Determined), } } - Scope::Module(module) => { + Scope::Module(module, derive_fallback_lint_id) => { let adjusted_parent_scope = &ParentScope { module, ..*parent_scope }; let binding = this.resolve_ident_in_module_unadjusted_ext( ModuleOrUniformRoot::Module(module), ident, ns, adjusted_parent_scope, - true, + !matches!(scope_set, ScopeSet::Late(..)), record_used, path_span, ); match binding { Ok(binding) => { + if let Some(lint_id) = derive_fallback_lint_id { + this.lint_buffer.buffer_lint_with_diagnostic( + PROC_MACRO_DERIVE_RESOLUTION_FALLBACK, + lint_id, + orig_ident.span, + &format!( + "cannot find {} `{}` in this scope", + ns.descr(), + ident + ), + BuiltinLintDiagnostics::ProcMacroDeriveResolutionFallback( + orig_ident.span, + ), + ); + } let misc_flags = if ptr::eq(module, this.graph_root) { Flags::MISC_SUGGEST_CRATE } else if module.is_normal() { @@ -841,7 +904,7 @@ impl<'a> Resolver<'a> { Ok((binding, flags)) if sub_namespace_match(binding.macro_kind(), macro_kind) => { - if !record_used { + if !record_used || matches!(scope_set, ScopeSet::Late(..)) { return Some(Ok(binding)); } diff --git a/compiler/rustc_save_analysis/src/dump_visitor.rs b/compiler/rustc_save_analysis/src/dump_visitor.rs index 625d7c831202b..12c77e0c8a60d 100644 --- a/compiler/rustc_save_analysis/src/dump_visitor.rs +++ b/compiler/rustc_save_analysis/src/dump_visitor.rs @@ -151,7 +151,7 @@ impl<'tcx> DumpVisitor<'tcx> { }, crate_root: crate_root.unwrap_or_else(|| "".to_owned()), external_crates: self.save_ctxt.get_external_crates(), - span: self.span_from_span(krate.item.span), + span: self.span_from_span(krate.item.inner), }; self.dumper.crate_prelude(data); @@ -301,7 +301,7 @@ impl<'tcx> DumpVisitor<'tcx> { fn process_struct_field_def( &mut self, - field: &'tcx hir::StructField<'tcx>, + field: &'tcx hir::FieldDef<'tcx>, parent_id: hir::HirId, ) { let field_data = self.save_ctxt.get_field_data(field, parent_id); @@ -320,15 +320,6 @@ impl<'tcx> DumpVisitor<'tcx> { for param in generics.params { match param.kind { hir::GenericParamKind::Lifetime { .. } => {} - hir::GenericParamKind::Type { - synthetic: Some(hir::SyntheticTyParamKind::ImplTrait), - .. - } => { - return self - .nest_typeck_results(self.tcx.hir().local_def_id(param.hir_id), |this| { - this.visit_generics(generics) - }); - } hir::GenericParamKind::Type { .. } => { let param_ss = param.name.ident().span; let name = escape(self.span.snippet(param_ss)); @@ -496,6 +487,7 @@ impl<'tcx> DumpVisitor<'tcx> { if !self.span.filter_generated(item.ident.span) { let span = self.span_from_span(item.ident.span); + let attrs = self.tcx.hir().attrs(item.hir_id()); self.dumper.dump_def( &access_from!(self.save_ctxt, item, item.hir_id()), Def { @@ -508,9 +500,9 @@ impl<'tcx> DumpVisitor<'tcx> { parent: None, children: fields, decl_id: None, - docs: self.save_ctxt.docs_for_attrs(&item.attrs), + docs: self.save_ctxt.docs_for_attrs(attrs), sig: sig::item_signature(item, &self.save_ctxt), - attributes: lower_attributes(item.attrs.to_vec(), &self.save_ctxt), + attributes: lower_attributes(attrs.to_vec(), &self.save_ctxt), }, ); } @@ -554,6 +546,7 @@ impl<'tcx> DumpVisitor<'tcx> { let span = self.span_from_span(name_span); let id = id_from_hir_id(variant.id, &self.save_ctxt); let parent = Some(id_from_def_id(item.def_id.to_def_id())); + let attrs = self.tcx.hir().attrs(variant.id); self.dumper.dump_def( &access, @@ -567,12 +560,9 @@ impl<'tcx> DumpVisitor<'tcx> { parent, children: vec![], decl_id: None, - docs: self.save_ctxt.docs_for_attrs(&variant.attrs), + docs: self.save_ctxt.docs_for_attrs(attrs), sig: sig::variant_signature(variant, &self.save_ctxt), - attributes: lower_attributes( - variant.attrs.to_vec(), - &self.save_ctxt, - ), + attributes: lower_attributes(attrs.to_vec(), &self.save_ctxt), }, ); } @@ -594,6 +584,7 @@ impl<'tcx> DumpVisitor<'tcx> { let span = self.span_from_span(name_span); let id = id_from_hir_id(variant.id, &self.save_ctxt); let parent = Some(id_from_def_id(item.def_id.to_def_id())); + let attrs = self.tcx.hir().attrs(variant.id); self.dumper.dump_def( &access, @@ -607,12 +598,9 @@ impl<'tcx> DumpVisitor<'tcx> { parent, children: vec![], decl_id: None, - docs: self.save_ctxt.docs_for_attrs(&variant.attrs), + docs: self.save_ctxt.docs_for_attrs(attrs), sig: sig::variant_signature(variant, &self.save_ctxt), - attributes: lower_attributes( - variant.attrs.to_vec(), - &self.save_ctxt, - ), + attributes: lower_attributes(attrs.to_vec(), &self.save_ctxt), }, ); } @@ -675,6 +663,7 @@ impl<'tcx> DumpVisitor<'tcx> { let span = self.span_from_span(item.ident.span); let children = methods.iter().map(|i| id_from_def_id(i.id.def_id.to_def_id())).collect(); + let attrs = self.tcx.hir().attrs(item.hir_id()); self.dumper.dump_def( &access_from!(self.save_ctxt, item, item.hir_id()), Def { @@ -687,9 +676,9 @@ impl<'tcx> DumpVisitor<'tcx> { parent: None, children, decl_id: None, - docs: self.save_ctxt.docs_for_attrs(&item.attrs), + docs: self.save_ctxt.docs_for_attrs(attrs), sig: sig::item_signature(item, &self.save_ctxt), - attributes: lower_attributes(item.attrs.to_vec(), &self.save_ctxt), + attributes: lower_attributes(attrs.to_vec(), &self.save_ctxt), }, ); } @@ -795,7 +784,7 @@ impl<'tcx> DumpVisitor<'tcx> { &mut self, ex: &'tcx hir::Expr<'tcx>, path: &'tcx hir::QPath<'tcx>, - fields: &'tcx [hir::Field<'tcx>], + fields: &'tcx [hir::ExprField<'tcx>], variant: &'tcx ty::VariantDef, rest: Option<&'tcx hir::Expr<'tcx>>, ) { @@ -998,6 +987,7 @@ impl<'tcx> DumpVisitor<'tcx> { hir::TraitItemKind::Const(ref ty, body) => { let body = body.map(|b| &self.tcx.hir().body(b).value); let respan = respan(vis_span, hir::VisibilityKind::Public); + let attrs = self.tcx.hir().attrs(trait_item.hir_id()); self.process_assoc_const( trait_item.hir_id(), trait_item.ident, @@ -1005,7 +995,7 @@ impl<'tcx> DumpVisitor<'tcx> { body, trait_id, &respan, - &trait_item.attrs, + attrs, ); } hir::TraitItemKind::Fn(ref sig, ref trait_fn) => { @@ -1031,6 +1021,7 @@ impl<'tcx> DumpVisitor<'tcx> { if !self.span.filter_generated(trait_item.ident.span) { let span = self.span_from_span(trait_item.ident.span); let id = id_from_def_id(trait_item.def_id.to_def_id()); + let attrs = self.tcx.hir().attrs(trait_item.hir_id()); self.dumper.dump_def( &Access { public: true, reachable: true }, @@ -1044,7 +1035,7 @@ impl<'tcx> DumpVisitor<'tcx> { parent: Some(id_from_def_id(trait_id)), children: vec![], decl_id: None, - docs: self.save_ctxt.docs_for_attrs(&trait_item.attrs), + docs: self.save_ctxt.docs_for_attrs(attrs), sig: sig::assoc_type_signature( trait_item.hir_id(), trait_item.ident, @@ -1052,10 +1043,7 @@ impl<'tcx> DumpVisitor<'tcx> { default_ty.as_ref().map(|ty| &**ty), &self.save_ctxt, ), - attributes: lower_attributes( - trait_item.attrs.to_vec(), - &self.save_ctxt, - ), + attributes: lower_attributes(attrs.to_vec(), &self.save_ctxt), }, ); } @@ -1072,6 +1060,7 @@ impl<'tcx> DumpVisitor<'tcx> { match impl_item.kind { hir::ImplItemKind::Const(ref ty, body) => { let body = self.tcx.hir().body(body); + let attrs = self.tcx.hir().attrs(impl_item.hir_id()); self.process_assoc_const( impl_item.hir_id(), impl_item.ident, @@ -1079,7 +1068,7 @@ impl<'tcx> DumpVisitor<'tcx> { Some(&body.value), impl_id, &impl_item.vis, - &impl_item.attrs, + attrs, ); } hir::ImplItemKind::Fn(ref sig, body) => { @@ -1108,16 +1097,12 @@ impl<'tcx> DumpVisitor<'tcx> { format!("::{}", self.tcx.def_path_str(self.tcx.hir().local_def_id(id).to_def_id())); let sm = self.tcx.sess.source_map(); - let filename = sm.span_to_filename(krate.item.span); + let filename = sm.span_to_filename(krate.item.inner); let data_id = id_from_hir_id(id, &self.save_ctxt); - let children = krate - .item - .module - .item_ids - .iter() - .map(|i| id_from_def_id(i.def_id.to_def_id())) - .collect(); - let span = self.span_from_span(krate.item.span); + let children = + krate.item.item_ids.iter().map(|i| id_from_def_id(i.def_id.to_def_id())).collect(); + let span = self.span_from_span(krate.item.inner); + let attrs = self.tcx.hir().attrs(id); self.dumper.dump_def( &Access { public: true, reachable: true }, @@ -1131,9 +1116,9 @@ impl<'tcx> DumpVisitor<'tcx> { children, parent: None, decl_id: None, - docs: self.save_ctxt.docs_for_attrs(krate.item.attrs), + docs: self.save_ctxt.docs_for_attrs(attrs), sig: None, - attributes: lower_attributes(krate.item.attrs.to_owned(), &self.save_ctxt), + attributes: lower_attributes(attrs.to_owned(), &self.save_ctxt), }, ); intravisit::walk_crate(self, krate); @@ -1263,6 +1248,7 @@ impl<'tcx> Visitor<'tcx> for DumpVisitor<'tcx> { if !self.span.filter_generated(item.ident.span) { let span = self.span_from_span(item.ident.span); let id = id_from_def_id(item.def_id.to_def_id()); + let attrs = self.tcx.hir().attrs(item.hir_id()); self.dumper.dump_def( &access_from!(self.save_ctxt, item, item.hir_id()), @@ -1276,9 +1262,9 @@ impl<'tcx> Visitor<'tcx> for DumpVisitor<'tcx> { parent: None, children: vec![], decl_id: None, - docs: self.save_ctxt.docs_for_attrs(&item.attrs), + docs: self.save_ctxt.docs_for_attrs(attrs), sig: sig::item_signature(item, &self.save_ctxt), - attributes: lower_attributes(item.attrs.to_vec(), &self.save_ctxt), + attributes: lower_attributes(attrs.to_vec(), &self.save_ctxt), }, ); } diff --git a/compiler/rustc_save_analysis/src/lib.rs b/compiler/rustc_save_analysis/src/lib.rs index b3f281bcabcec..c19c16b88a7ab 100644 --- a/compiler/rustc_save_analysis/src/lib.rs +++ b/compiler/rustc_save_analysis/src/lib.rs @@ -1,6 +1,6 @@ #![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")] #![feature(nll)] -#![feature(or_patterns)] +#![cfg_attr(bootstrap, feature(or_patterns))] #![recursion_limit = "256"] mod dump_visitor; @@ -139,6 +139,7 @@ impl<'tcx> SaveContext<'tcx> { pub fn get_extern_item_data(&self, item: &hir::ForeignItem<'_>) -> Option { let def_id = item.def_id.to_def_id(); let qualname = format!("::{}", self.tcx.def_path_str(def_id)); + let attrs = self.tcx.hir().attrs(item.hir_id()); match item.kind { hir::ForeignItemKind::Fn(ref decl, arg_names, ref generics) => { filter!(self.span_utils, item.ident.span); @@ -169,9 +170,9 @@ impl<'tcx> SaveContext<'tcx> { parent: None, children: vec![], decl_id: None, - docs: self.docs_for_attrs(&item.attrs), + docs: self.docs_for_attrs(attrs), sig: sig::foreign_item_signature(item, self), - attributes: lower_attributes(item.attrs.to_vec(), self), + attributes: lower_attributes(attrs.to_vec(), self), })) } hir::ForeignItemKind::Static(ref ty, _) => { @@ -190,9 +191,9 @@ impl<'tcx> SaveContext<'tcx> { parent: None, children: vec![], decl_id: None, - docs: self.docs_for_attrs(&item.attrs), + docs: self.docs_for_attrs(attrs), sig: sig::foreign_item_signature(item, self), - attributes: lower_attributes(item.attrs.to_vec(), self), + attributes: lower_attributes(attrs.to_vec(), self), })) } // FIXME(plietar): needs a new DefKind in rls-data @@ -202,6 +203,7 @@ impl<'tcx> SaveContext<'tcx> { pub fn get_item_data(&self, item: &hir::Item<'_>) -> Option { let def_id = item.def_id.to_def_id(); + let attrs = self.tcx.hir().attrs(item.hir_id()); match item.kind { hir::ItemKind::Fn(ref sig, ref generics, _) => { let qualname = format!("::{}", self.tcx.def_path_str(def_id)); @@ -224,9 +226,9 @@ impl<'tcx> SaveContext<'tcx> { parent: None, children: vec![], decl_id: None, - docs: self.docs_for_attrs(&item.attrs), + docs: self.docs_for_attrs(attrs), sig: sig::item_signature(item, self), - attributes: lower_attributes(item.attrs.to_vec(), self), + attributes: lower_attributes(attrs.to_vec(), self), })) } hir::ItemKind::Static(ref typ, ..) => { @@ -247,9 +249,9 @@ impl<'tcx> SaveContext<'tcx> { parent: None, children: vec![], decl_id: None, - docs: self.docs_for_attrs(&item.attrs), + docs: self.docs_for_attrs(attrs), sig: sig::item_signature(item, self), - attributes: lower_attributes(item.attrs.to_vec(), self), + attributes: lower_attributes(attrs.to_vec(), self), })) } hir::ItemKind::Const(ref typ, _) => { @@ -269,9 +271,9 @@ impl<'tcx> SaveContext<'tcx> { parent: None, children: vec![], decl_id: None, - docs: self.docs_for_attrs(&item.attrs), + docs: self.docs_for_attrs(attrs), sig: sig::item_signature(item, self), - attributes: lower_attributes(item.attrs.to_vec(), self), + attributes: lower_attributes(attrs.to_vec(), self), })) } hir::ItemKind::Mod(ref m) => { @@ -296,9 +298,9 @@ impl<'tcx> SaveContext<'tcx> { .map(|i| id_from_def_id(i.def_id.to_def_id())) .collect(), decl_id: None, - docs: self.docs_for_attrs(&item.attrs), + docs: self.docs_for_attrs(attrs), sig: sig::item_signature(item, self), - attributes: lower_attributes(item.attrs.to_vec(), self), + attributes: lower_attributes(attrs.to_vec(), self), })) } hir::ItemKind::Enum(ref def, ref generics) => { @@ -317,9 +319,9 @@ impl<'tcx> SaveContext<'tcx> { parent: None, children: def.variants.iter().map(|v| id_from_hir_id(v.id, self)).collect(), decl_id: None, - docs: self.docs_for_attrs(&item.attrs), + docs: self.docs_for_attrs(attrs), sig: sig::item_signature(item, self), - attributes: lower_attributes(item.attrs.to_vec(), self), + attributes: lower_attributes(attrs.to_vec(), self), })) } hir::ItemKind::Impl(hir::Impl { ref of_trait, ref self_ty, ref items, .. }) => { @@ -377,7 +379,7 @@ impl<'tcx> SaveContext<'tcx> { } } - pub fn get_field_data(&self, field: &hir::StructField<'_>, scope: hir::HirId) -> Option { + pub fn get_field_data(&self, field: &hir::FieldDef<'_>, scope: hir::HirId) -> Option { let name = field.ident.to_string(); let scope_def_id = self.tcx.hir().local_def_id(scope).to_def_id(); let qualname = format!("::{}::{}", self.tcx.def_path_str(scope_def_id), field.ident); @@ -387,6 +389,7 @@ impl<'tcx> SaveContext<'tcx> { let id = id_from_def_id(field_def_id); let span = self.span_from_span(field.ident.span); + let attrs = self.tcx.hir().attrs(field.hir_id); Some(Def { kind: DefKind::Field, @@ -398,9 +401,9 @@ impl<'tcx> SaveContext<'tcx> { parent: Some(id_from_def_id(scope_def_id)), children: vec![], decl_id: None, - docs: self.docs_for_attrs(&field.attrs), + docs: self.docs_for_attrs(attrs), sig: sig::field_signature(field, self), - attributes: lower_attributes(field.attrs.to_vec(), self), + attributes: lower_attributes(attrs.to_vec(), self), }) } @@ -424,9 +427,9 @@ impl<'tcx> SaveContext<'tcx> { let trait_id = self.tcx.trait_id_of_impl(impl_id); let mut docs = String::new(); let mut attrs = vec![]; - if let Some(Node::ImplItem(item)) = hir.find(hir_id) { - docs = self.docs_for_attrs(&item.attrs); - attrs = item.attrs.to_vec(); + if let Some(Node::ImplItem(_)) = hir.find(hir_id) { + attrs = self.tcx.hir().attrs(hir_id).to_vec(); + docs = self.docs_for_attrs(&attrs); } let mut decl_id = None; @@ -470,9 +473,9 @@ impl<'tcx> SaveContext<'tcx> { let mut docs = String::new(); let mut attrs = vec![]; - if let Some(Node::TraitItem(item)) = self.tcx.hir().find(hir_id) { - docs = self.docs_for_attrs(&item.attrs); - attrs = item.attrs.to_vec(); + if let Some(Node::TraitItem(_)) = self.tcx.hir().find(hir_id) { + attrs = self.tcx.hir().attrs(hir_id).to_vec(); + docs = self.docs_for_attrs(&attrs); } ( @@ -513,19 +516,6 @@ impl<'tcx> SaveContext<'tcx> { }) } - pub fn get_trait_ref_data(&self, trait_ref: &hir::TraitRef<'_>) -> Option { - self.lookup_def_id(trait_ref.hir_ref_id).and_then(|def_id| { - let span = trait_ref.path.span; - if generated_code(span) { - return None; - } - let sub_span = trait_ref.path.segments.last().unwrap().ident.span; - filter!(self.span_utils, sub_span); - let span = self.span_from_span(sub_span); - Some(Ref { kind: RefKind::Type, span, ref_id: id_from_def_id(def_id) }) - }) - } - pub fn get_expr_data(&self, expr: &hir::Expr<'_>) -> Option { let ty = self.typeck_results().expr_ty_adjusted_opt(expr)?; if matches!(ty.kind(), ty::Error(_)) { @@ -766,7 +756,7 @@ impl<'tcx> SaveContext<'tcx> { pub fn get_field_ref_data( &self, - field_ref: &hir::Field<'_>, + field_ref: &hir::ExprField<'_>, variant: &ty::VariantDef, ) -> Option { filter!(self.span_utils, field_ref.ident.span); @@ -781,7 +771,10 @@ impl<'tcx> SaveContext<'tcx> { /// For a given piece of AST defined by the supplied Span and NodeId, /// returns `None` if the node is not macro-generated or the span is malformed, /// else uses the expansion callsite and callee to return some MacroRef. - pub fn get_macro_use_data(&self, span: Span) -> Option { + /// + /// FIXME: [`DumpVisitor::process_macro_use`] should actually dump this data + #[allow(dead_code)] + fn get_macro_use_data(&self, span: Span) -> Option { if !generated_code(span) { return None; } diff --git a/compiler/rustc_save_analysis/src/sig.rs b/compiler/rustc_save_analysis/src/sig.rs index 33db189af37a6..c3bc1c191ff09 100644 --- a/compiler/rustc_save_analysis/src/sig.rs +++ b/compiler/rustc_save_analysis/src/sig.rs @@ -55,7 +55,7 @@ pub fn foreign_item_signature( /// Signature for a struct or tuple field declaration. /// Does not include a trailing comma. -pub fn field_signature(field: &hir::StructField<'_>, scx: &SaveContext<'_>) -> Option { +pub fn field_signature(field: &hir::FieldDef<'_>, scx: &SaveContext<'_>) -> Option { if !scx.config.signatures { return None; } @@ -614,11 +614,12 @@ impl<'hir> Sig for hir::Generics<'hir> { start: offset + text.len(), end: offset + text.len() + param_text.as_str().len(), }); - if let hir::GenericParamKind::Const { ref ty, ref default } = param.kind { + if let hir::GenericParamKind::Const { ref ty, default } = param.kind { param_text.push_str(": "); param_text.push_str(&ty_to_string(&ty)); - if let Some(ref _default) = default { - // FIXME(const_generics_defaults): push the `default` value here + if let Some(default) = default { + param_text.push_str(" = "); + param_text.push_str(&id_to_string(&scx.tcx.hir(), default.hir_id)); } } if !param.bounds.is_empty() { @@ -655,7 +656,7 @@ impl<'hir> Sig for hir::Generics<'hir> { } } -impl<'hir> Sig for hir::StructField<'hir> { +impl<'hir> Sig for hir::FieldDef<'hir> { fn make(&self, offset: usize, _parent_id: Option, scx: &SaveContext<'_>) -> Result { let mut text = String::new(); diff --git a/compiler/rustc_serialize/src/json.rs b/compiler/rustc_serialize/src/json.rs index bbbe568f17a8f..78a102c5c2330 100644 --- a/compiler/rustc_serialize/src/json.rs +++ b/compiler/rustc_serialize/src/json.rs @@ -553,6 +553,12 @@ impl<'a> crate::Encoder for Encoder<'a> { fn emit_str(&mut self, v: &str) -> EncodeResult { escape_str(self.writer, v) } + fn emit_raw_bytes(&mut self, s: &[u8]) -> Result<(), Self::Error> { + for &c in s.iter() { + self.emit_u8(c)?; + } + Ok(()) + } fn emit_enum(&mut self, _name: &str, f: F) -> EncodeResult where @@ -879,6 +885,12 @@ impl<'a> crate::Encoder for PrettyEncoder<'a> { fn emit_str(&mut self, v: &str) -> EncodeResult { escape_str(self.writer, v) } + fn emit_raw_bytes(&mut self, s: &[u8]) -> Result<(), Self::Error> { + for &c in s.iter() { + self.emit_u8(c)?; + } + Ok(()) + } fn emit_enum(&mut self, _name: &str, f: F) -> EncodeResult where @@ -2354,6 +2366,13 @@ impl crate::Decoder for Decoder { expect!(self.pop(), String).map(Cow::Owned) } + fn read_raw_bytes_into(&mut self, s: &mut [u8]) -> Result<(), Self::Error> { + for c in s.iter_mut() { + *c = self.read_u8()?; + } + Ok(()) + } + fn read_enum(&mut self, _name: &str, f: F) -> DecodeResult where F: FnOnce(&mut Decoder) -> DecodeResult, diff --git a/compiler/rustc_serialize/src/lib.rs b/compiler/rustc_serialize/src/lib.rs index e439ddcdaa9df..cf5a9118275d9 100644 --- a/compiler/rustc_serialize/src/lib.rs +++ b/compiler/rustc_serialize/src/lib.rs @@ -16,7 +16,8 @@ Core encoding and decoding interfaces. #![feature(min_specialization)] #![feature(vec_spare_capacity)] #![feature(core_intrinsics)] -#![feature(int_bits_const)] +#![feature(maybe_uninit_array_assume_init)] +#![feature(maybe_uninit_uninit_array)] #![feature(maybe_uninit_slice)] #![feature(new_uninit)] #![cfg_attr(test, feature(test))] diff --git a/compiler/rustc_serialize/src/opaque.rs b/compiler/rustc_serialize/src/opaque.rs index 3e37fc87ce6f9..6e36184aff051 100644 --- a/compiler/rustc_serialize/src/opaque.rs +++ b/compiler/rustc_serialize/src/opaque.rs @@ -1,6 +1,7 @@ use crate::leb128::{self, max_leb128_len}; -use crate::serialize; +use crate::serialize::{self, Encoder as _}; use std::borrow::Cow; +use std::convert::TryInto; use std::fs::File; use std::io::{self, Write}; use std::mem::MaybeUninit; @@ -30,11 +31,6 @@ impl Encoder { pub fn position(&self) -> usize { self.data.len() } - - #[inline] - pub fn emit_raw_bytes(&mut self, s: &[u8]) { - self.data.extend_from_slice(s); - } } macro_rules! write_leb128 { @@ -154,7 +150,12 @@ impl serialize::Encoder for Encoder { #[inline] fn emit_str(&mut self, v: &str) -> EncodeResult { self.emit_usize(v.len())?; - self.emit_raw_bytes(v.as_bytes()); + self.emit_raw_bytes(v.as_bytes()) + } + + #[inline] + fn emit_raw_bytes(&mut self, s: &[u8]) -> EncodeResult { + self.data.extend_from_slice(s); Ok(()) } } @@ -208,11 +209,6 @@ impl FileEncoder { self.flushed + self.buffered } - #[inline] - pub fn emit_raw_bytes(&mut self, s: &[u8]) -> FileEncodeResult { - self.write_all(s) - } - pub fn flush(&mut self) -> FileEncodeResult { // This is basically a copy of `BufWriter::flush`. If `BufWriter` ever // offers a raw buffer access API, we can use it, and remove this. @@ -508,6 +504,11 @@ impl serialize::Encoder for FileEncoder { self.emit_usize(v.len())?; self.emit_raw_bytes(v.as_bytes()) } + + #[inline] + fn emit_raw_bytes(&mut self, s: &[u8]) -> FileEncodeResult { + self.write_all(s) + } } // ----------------------------------------------------------------------------- @@ -541,23 +542,10 @@ impl<'a> Decoder<'a> { } #[inline] - pub fn read_raw_bytes(&mut self, s: &mut [MaybeUninit]) -> Result<(), String> { + pub fn read_raw_bytes(&mut self, bytes: usize) -> &'a [u8] { let start = self.position; - let end = start + s.len(); - assert!(end <= self.data.len()); - - // SAFETY: Both `src` and `dst` point to at least `s.len()` elements: - // `src` points to at least `s.len()` elements by above assert, and - // `dst` points to `s.len()` elements by derivation from `s`. - unsafe { - let src = self.data.as_ptr().add(start); - let dst = s.as_mut_ptr() as *mut u8; - ptr::copy_nonoverlapping(src, dst, s.len()); - } - - self.position = end; - - Ok(()) + self.position += bytes; + &self.data[start..self.position] } } @@ -677,6 +665,14 @@ impl<'a> serialize::Decoder for Decoder<'a> { fn error(&mut self, err: &str) -> Self::Error { err.to_string() } + + #[inline] + fn read_raw_bytes_into(&mut self, s: &mut [u8]) -> Result<(), String> { + let start = self.position; + self.position += s.len(); + s.copy_from_slice(&self.data[start..self.position]); + Ok(()) + } } // Specializations for contiguous byte sequences follow. The default implementations for slices @@ -689,8 +685,7 @@ impl<'a> serialize::Decoder for Decoder<'a> { impl serialize::Encodable for [u8] { fn encode(&self, e: &mut Encoder) -> EncodeResult { serialize::Encoder::emit_usize(e, self.len())?; - e.emit_raw_bytes(self); - Ok(()) + e.emit_raw_bytes(self) } } @@ -706,15 +701,48 @@ impl serialize::Encodable for [u8] { impl<'a> serialize::Decodable> for Vec { fn decode(d: &mut Decoder<'a>) -> Result { let len = serialize::Decoder::read_usize(d)?; + Ok(d.read_raw_bytes(len).to_owned()) + } +} - let mut v = Vec::with_capacity(len); - let buf = &mut v.spare_capacity_mut()[..len]; - d.read_raw_bytes(buf)?; +// An integer that will always encode to 8 bytes. +pub struct IntEncodedWithFixedSize(pub u64); - unsafe { - v.set_len(len); - } +impl IntEncodedWithFixedSize { + pub const ENCODED_SIZE: usize = 8; +} + +impl serialize::Encodable for IntEncodedWithFixedSize { + #[inline] + fn encode(&self, e: &mut Encoder) -> EncodeResult { + let _start_pos = e.position(); + e.emit_raw_bytes(&self.0.to_le_bytes())?; + let _end_pos = e.position(); + debug_assert_eq!((_end_pos - _start_pos), IntEncodedWithFixedSize::ENCODED_SIZE); + Ok(()) + } +} + +impl serialize::Encodable for IntEncodedWithFixedSize { + #[inline] + fn encode(&self, e: &mut FileEncoder) -> FileEncodeResult { + let _start_pos = e.position(); + e.emit_raw_bytes(&self.0.to_le_bytes())?; + let _end_pos = e.position(); + debug_assert_eq!((_end_pos - _start_pos), IntEncodedWithFixedSize::ENCODED_SIZE); + Ok(()) + } +} + +impl<'a> serialize::Decodable> for IntEncodedWithFixedSize { + #[inline] + fn decode(decoder: &mut Decoder<'a>) -> Result { + let _start_pos = decoder.position(); + let bytes = decoder.read_raw_bytes(IntEncodedWithFixedSize::ENCODED_SIZE); + let _end_pos = decoder.position(); + debug_assert_eq!((_end_pos - _start_pos), IntEncodedWithFixedSize::ENCODED_SIZE); - Ok(v) + let value = u64::from_le_bytes(bytes.try_into().unwrap()); + Ok(IntEncodedWithFixedSize(value)) } } diff --git a/compiler/rustc_serialize/src/serialize.rs b/compiler/rustc_serialize/src/serialize.rs index 47aad5b88c622..d3e5f306970c5 100644 --- a/compiler/rustc_serialize/src/serialize.rs +++ b/compiler/rustc_serialize/src/serialize.rs @@ -33,6 +33,7 @@ pub trait Encoder { fn emit_f32(&mut self, v: f32) -> Result<(), Self::Error>; fn emit_char(&mut self, v: char) -> Result<(), Self::Error>; fn emit_str(&mut self, v: &str) -> Result<(), Self::Error>; + fn emit_raw_bytes(&mut self, s: &[u8]) -> Result<(), Self::Error>; // Compound types: #[inline] @@ -224,6 +225,7 @@ pub trait Decoder { fn read_f32(&mut self) -> Result; fn read_char(&mut self) -> Result; fn read_str(&mut self) -> Result, Self::Error>; + fn read_raw_bytes_into(&mut self, s: &mut [u8]) -> Result<(), Self::Error>; // Compound types: #[inline] diff --git a/compiler/rustc_serialize/tests/leb128.rs b/compiler/rustc_serialize/tests/leb128.rs index a2bcf2c251d7a..3e2aab5125ab7 100644 --- a/compiler/rustc_serialize/tests/leb128.rs +++ b/compiler/rustc_serialize/tests/leb128.rs @@ -1,4 +1,3 @@ -#![feature(int_bits_const)] #![feature(maybe_uninit_slice)] #![feature(maybe_uninit_uninit_array)] diff --git a/compiler/rustc_session/src/config.rs b/compiler/rustc_session/src/config.rs index 65be14e3510c7..1c9bd153f8de1 100644 --- a/compiler/rustc_session/src/config.rs +++ b/compiler/rustc_session/src/config.rs @@ -10,7 +10,6 @@ use crate::{early_error, early_warn, Session}; use rustc_data_structures::fx::FxHashSet; use rustc_data_structures::impl_stable_hash_via_hash; -use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; use rustc_target::abi::{Align, TargetDataLayout}; use rustc_target::spec::{SplitDebuginfo, Target, TargetTriple}; @@ -19,7 +18,7 @@ use rustc_serialize::json; use crate::parse::CrateConfig; use rustc_feature::UnstableFeatures; -use rustc_span::edition::{Edition, DEFAULT_EDITION, EDITION_NAME_LIST}; +use rustc_span::edition::{Edition, DEFAULT_EDITION, EDITION_NAME_LIST, LATEST_STABLE_EDITION}; use rustc_span::source_map::{FileName, FilePathMapping}; use rustc_span::symbol::{sym, Symbol}; use rustc_span::SourceFileHashAlgorithm; @@ -36,66 +35,6 @@ use std::iter::{self, FromIterator}; use std::path::{Path, PathBuf}; use std::str::{self, FromStr}; -bitflags! { - #[derive(Default, Encodable, Decodable)] - pub struct SanitizerSet: u8 { - const ADDRESS = 1 << 0; - const LEAK = 1 << 1; - const MEMORY = 1 << 2; - const THREAD = 1 << 3; - const HWADDRESS = 1 << 4; - } -} - -/// Formats a sanitizer set as a comma separated list of sanitizers' names. -impl fmt::Display for SanitizerSet { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let mut first = true; - for s in *self { - let name = match s { - SanitizerSet::ADDRESS => "address", - SanitizerSet::LEAK => "leak", - SanitizerSet::MEMORY => "memory", - SanitizerSet::THREAD => "thread", - SanitizerSet::HWADDRESS => "hwaddress", - _ => panic!("unrecognized sanitizer {:?}", s), - }; - if !first { - f.write_str(",")?; - } - f.write_str(name)?; - first = false; - } - Ok(()) - } -} - -impl IntoIterator for SanitizerSet { - type Item = SanitizerSet; - type IntoIter = std::vec::IntoIter; - - fn into_iter(self) -> Self::IntoIter { - [ - SanitizerSet::ADDRESS, - SanitizerSet::LEAK, - SanitizerSet::MEMORY, - SanitizerSet::THREAD, - SanitizerSet::HWADDRESS, - ] - .iter() - .copied() - .filter(|&s| self.contains(s)) - .collect::>() - .into_iter() - } -} - -impl HashStable for SanitizerSet { - fn hash_stable(&self, ctx: &mut CTX, hasher: &mut StableHasher) { - self.bits().hash_stable(ctx, hasher); - } -} - /// The different settings that the `-Z strip` flag can have. #[derive(Clone, Copy, PartialEq, Hash, Debug)] pub enum Strip { @@ -136,19 +75,21 @@ impl_stable_hash_via_hash!(OptLevel); /// This is what the `LtoCli` values get mapped to after resolving defaults and /// and taking other command line options into account. +/// +/// Note that linker plugin-based LTO is a different mechanism entirely. #[derive(Clone, PartialEq)] pub enum Lto { - /// Don't do any LTO whatsoever + /// Don't do any LTO whatsoever. No, - /// Do a full crate graph LTO with ThinLTO + /// Do a full-crate-graph (inter-crate) LTO with ThinLTO. Thin, - /// Do a local graph LTO with ThinLTO (only relevant for multiple codegen - /// units). + /// Do a local ThinLTO (intra-crate, over the CodeGen Units of the local crate only). This is + /// only relevant if multiple CGUs are used. ThinLocal, - /// Do a full crate graph LTO with "fat" LTO + /// Do a full-crate-graph (inter-crate) LTO with "fat" LTO. Fat, } @@ -184,6 +125,37 @@ pub enum MirSpanview { Block, } +/// The different settings that the `-Z instrument-coverage` flag can have. +/// +/// Coverage instrumentation now supports combining `-Z instrument-coverage` +/// with compiler and linker optimization (enabled with `-O` or `-C opt-level=1` +/// and higher). Nevertheless, there are many variables, depending on options +/// selected, code structure, and enabled attributes. If errors are encountered, +/// either while compiling or when generating `llvm-cov show` reports, consider +/// lowering the optimization level, including or excluding `-C link-dead-code`, +/// or using `-Z instrument-coverage=except-unused-functions` or `-Z +/// instrument-coverage=except-unused-generics`. +/// +/// Note that `ExceptUnusedFunctions` means: When `mapgen.rs` generates the +/// coverage map, it will not attempt to generate synthetic functions for unused +/// (and not code-generated) functions (whether they are generic or not). As a +/// result, non-codegenned functions will not be included in the coverage map, +/// and will not appear, as covered or uncovered, in coverage reports. +/// +/// `ExceptUnusedGenerics` will add synthetic functions to the coverage map, +/// unless the function has type parameters. +#[derive(Clone, Copy, PartialEq, Hash, Debug)] +pub enum InstrumentCoverage { + /// Default `-Z instrument-coverage` or `-Z instrument-coverage=statement` + All, + /// `-Z instrument-coverage=except-unused-generics` + ExceptUnusedGenerics, + /// `-Z instrument-coverage=except-unused-functions` + ExceptUnusedFunctions, + /// `-Z instrument-coverage=off` (or `no`, etc.) + Off, +} + #[derive(Clone, PartialEq, Hash)] pub enum LinkerPluginLto { LinkerPlugin(PathBuf), @@ -474,6 +446,7 @@ impl<'a> From<&'a ExternDepSpec> for rustc_lint_defs::ExternDepSpec { } impl Externs { + /// Used for testing. pub fn new(data: BTreeMap) -> Externs { Externs(data) } @@ -485,6 +458,10 @@ impl Externs { pub fn iter(&self) -> BTreeMapIter<'_, String, ExternEntry> { self.0.iter() } + + pub fn len(&self) -> usize { + self.0.len() + } } impl ExternEntry { @@ -573,13 +550,6 @@ impl Input { } } - pub fn get_input(&mut self) -> Option<&mut String> { - match *self { - Input::File(_) => None, - Input::Str { ref mut input, .. } => Some(input), - } - } - pub fn source_name(&self) -> FileName { match *self { Input::File(ref ifile) => ifile.clone().into(), @@ -734,6 +704,7 @@ impl Default for Options { remap_path_prefix: Vec::new(), edition: DEFAULT_EDITION, json_artifact_notifications: false, + json_unused_externs: false, pretty: None, } } @@ -747,12 +718,6 @@ impl Options { || self.debugging_opts.query_dep_graph } - #[inline(always)] - pub fn enable_dep_node_debug_strs(&self) -> bool { - cfg!(debug_assertions) - && (self.debugging_opts.query_dep_graph || self.debugging_opts.incremental_info) - } - pub fn file_path_mapping(&self) -> FilePathMapping { FilePathMapping::new(self.remap_path_prefix.clone()) } @@ -832,7 +797,7 @@ pub const fn default_lib_output() -> CrateType { CrateType::Rlib } -pub fn default_configuration(sess: &Session) -> CrateConfig { +fn default_configuration(sess: &Session) -> CrateConfig { let end = &sess.target.endian; let arch = &sess.target.arch; let wordsz = sess.target.pointer_width.to_string(); @@ -927,7 +892,7 @@ pub fn build_configuration(sess: &Session, mut user_cfg: CrateConfig) -> CrateCo user_cfg } -pub fn build_target_config( +pub(super) fn build_target_config( opts: &Options, target_override: Option, sysroot: &PathBuf, @@ -939,7 +904,7 @@ pub fn build_target_config( opts.error_format, &format!( "Error loading target specification: {}. \ - Use `--print target-list` for a list of built-in targets", + Run `rustc --print target-list` for a list of built-in targets", e ), ) @@ -1034,9 +999,6 @@ mod opt { pub fn flag_s(a: S, b: S, c: S) -> R { stable(longer(a, b), move |opts| opts.optflag(a, b, c)) } - pub fn flagopt_s(a: S, b: S, c: S, d: S) -> R { - stable(longer(a, b), move |opts| opts.optflagopt(a, b, c, d)) - } pub fn flagmulti_s(a: S, b: S, c: S) -> R { stable(longer(a, b), move |opts| opts.optflagmulti(a, b, c)) } @@ -1047,15 +1009,6 @@ mod opt { pub fn multi(a: S, b: S, c: S, d: S) -> R { unstable(longer(a, b), move |opts| opts.optmulti(a, b, c, d)) } - pub fn flag(a: S, b: S, c: S) -> R { - unstable(longer(a, b), move |opts| opts.optflag(a, b, c)) - } - pub fn flagopt(a: S, b: S, c: S, d: S) -> R { - unstable(longer(a, b), move |opts| opts.optflagopt(a, b, c, d)) - } - pub fn flagmulti(a: S, b: S, c: S) -> R { - unstable(longer(a, b), move |opts| opts.optflagmulti(a, b, c)) - } } /// Returns the "short" subset of the rustc command line options, @@ -1255,15 +1208,23 @@ pub fn parse_color(matches: &getopts::Matches) -> ColorConfig { } } +/// Possible json config files +pub struct JsonConfig { + pub json_rendered: HumanReadableErrorType, + pub json_artifact_notifications: bool, + pub json_unused_externs: bool, +} + /// Parse the `--json` flag. /// /// The first value returned is how to render JSON diagnostics, and the second /// is whether or not artifact notifications are enabled. -pub fn parse_json(matches: &getopts::Matches) -> (HumanReadableErrorType, bool) { +pub fn parse_json(matches: &getopts::Matches) -> JsonConfig { let mut json_rendered: fn(ColorConfig) -> HumanReadableErrorType = HumanReadableErrorType::Default; let mut json_color = ColorConfig::Never; let mut json_artifact_notifications = false; + let mut json_unused_externs = false; for option in matches.opt_strs("json") { // For now conservatively forbid `--color` with `--json` since `--json` // won't actually be emitting any colors and anything colorized is @@ -1280,6 +1241,7 @@ pub fn parse_json(matches: &getopts::Matches) -> (HumanReadableErrorType, bool) "diagnostic-short" => json_rendered = HumanReadableErrorType::Short, "diagnostic-rendered-ansi" => json_color = ColorConfig::Always, "artifacts" => json_artifact_notifications = true, + "unused-externs" => json_unused_externs = true, s => early_error( ErrorOutputType::default(), &format!("unknown `--json` option `{}`", s), @@ -1287,7 +1249,12 @@ pub fn parse_json(matches: &getopts::Matches) -> (HumanReadableErrorType, bool) } } } - (json_rendered(json_color), json_artifact_notifications) + + JsonConfig { + json_rendered: json_rendered(json_color), + json_artifact_notifications, + json_unused_externs, + } } /// Parses the `--error-format` flag. @@ -1360,13 +1327,16 @@ pub fn parse_crate_edition(matches: &getopts::Matches) -> Edition { }; if !edition.is_stable() && !nightly_options::is_unstable_enabled(matches) { - early_error( - ErrorOutputType::default(), - &format!( - "edition {} is unstable and only available with -Z unstable-options.", - edition, - ), - ) + let is_nightly = nightly_options::match_is_nightly_build(matches); + let msg = if !is_nightly { + format!( + "the crate requires edition {}, but the latest edition supported by this Rust version is {}", + edition, LATEST_STABLE_EDITION + ) + } else { + format!("edition {} is unstable and only available with -Z unstable-options", edition) + }; + early_error(ErrorOutputType::default(), &msg) } edition @@ -1865,7 +1835,8 @@ pub fn build_session_options(matches: &getopts::Matches) -> Options { let edition = parse_crate_edition(matches); - let (json_rendered, json_artifact_notifications) = parse_json(matches); + let JsonConfig { json_rendered, json_artifact_notifications, json_unused_externs } = + parse_json(matches); let error_format = parse_error_format(matches, color, json_rendered); @@ -1878,6 +1849,14 @@ pub fn build_session_options(matches: &getopts::Matches) -> Options { let mut debugging_opts = build_debugging_options(matches, error_format); check_debug_option_stability(&debugging_opts, error_format, json_rendered); + if !debugging_opts.unstable_options && json_unused_externs { + early_error( + error_format, + "the `-Z unstable-options` flag must also be passed to enable \ + the flag `--json=unused-externs`", + ); + } + let output_types = parse_output_types(&debugging_opts, matches, error_format); let mut cg = build_codegen_options(matches, error_format); @@ -1916,7 +1895,9 @@ pub fn build_session_options(matches: &getopts::Matches) -> Options { ); } - if debugging_opts.instrument_coverage { + if debugging_opts.instrument_coverage.is_some() + && debugging_opts.instrument_coverage != Some(InstrumentCoverage::Off) + { if cg.profile_generate.enabled() || cg.profile_use.is_some() { early_error( error_format, @@ -1942,23 +1923,6 @@ pub fn build_session_options(matches: &getopts::Matches) -> Options { } Some(SymbolManglingVersion::V0) => {} } - - if debugging_opts.mir_opt_level > 1 { - // Functions inlined during MIR transform can, at best, make it impossible to - // effectively cover inlined functions, and, at worst, break coverage map generation - // during LLVM codegen. For example, function counter IDs are only unique within a - // function. Inlining after these counters are injected can produce duplicate counters, - // resulting in an invalid coverage map (and ICE); so this option combination is not - // allowed. - early_warn( - error_format, - &format!( - "`-Z mir-opt-level={}` (or any level > 1) enables function inlining, which \ - is incompatible with `-Z instrument-coverage`. Inlining will be disabled.", - debugging_opts.mir_opt_level, - ), - ); - } } if let Ok(graphviz_font) = std::env::var("RUSTC_GRAPHVIZ_FONT") { @@ -2053,6 +2017,7 @@ pub fn build_session_options(matches: &getopts::Matches) -> Options { remap_path_prefix, edition, json_artifact_notifications, + json_unused_externs, pretty, } } @@ -2077,6 +2042,7 @@ fn parse_pretty( ("hir,identified", true) => Hir(PpHirMode::Identified), ("hir,typed", true) => Hir(PpHirMode::Typed), ("hir-tree", true) => HirTree, + ("thir-tree", true) => ThirTree, ("mir", true) => Mir, ("mir-cfg", true) => MirCFG, _ => { @@ -2268,6 +2234,8 @@ pub enum PpMode { Hir(PpHirMode), /// `-Zunpretty=hir-tree` HirTree, + /// `-Zunpretty=thir-tree` + ThirTree, /// `-Zunpretty=mir` Mir, /// `-Zunpretty=mir-cfg` @@ -2285,6 +2253,7 @@ impl PpMode { | AstTree(PpAstTreeMode::Expanded) | Hir(_) | HirTree + | ThirTree | Mir | MirCFG => true, } @@ -2292,7 +2261,7 @@ impl PpMode { pub fn needs_analysis(&self) -> bool { use PpMode::*; - matches!(*self, Mir | MirCFG) + matches!(*self, Mir | MirCFG | ThirTree) } } @@ -2316,8 +2285,8 @@ impl PpMode { /// how the hash should be calculated when adding a new command-line argument. crate mod dep_tracking { use super::{ - CFGuard, CrateType, DebugInfo, ErrorOutputType, LinkerPluginLto, LtoCli, OptLevel, - OutputTypes, Passes, SanitizerSet, SourceFileHashAlgorithm, SwitchWithOptPath, + CFGuard, CrateType, DebugInfo, ErrorOutputType, InstrumentCoverage, LinkerPluginLto, + LtoCli, OptLevel, OutputTypes, Passes, SourceFileHashAlgorithm, SwitchWithOptPath, SymbolManglingVersion, TrimmedDefPaths, }; use crate::lint; @@ -2326,7 +2295,7 @@ crate mod dep_tracking { use rustc_feature::UnstableFeatures; use rustc_span::edition::Edition; use rustc_target::spec::{CodeModel, MergeFunctions, PanicStrategy, RelocModel}; - use rustc_target::spec::{RelroLevel, SplitDebuginfo, TargetTriple, TlsModel}; + use rustc_target::spec::{RelroLevel, SanitizerSet, SplitDebuginfo, TargetTriple, TlsModel}; use std::collections::hash_map::DefaultHasher; use std::collections::BTreeMap; use std::hash::Hash; @@ -2370,6 +2339,7 @@ crate mod dep_tracking { impl_dep_tracking_hash_via_hash!(PathBuf); impl_dep_tracking_hash_via_hash!(lint::Level); impl_dep_tracking_hash_via_hash!(Option); + impl_dep_tracking_hash_via_hash!(Option); impl_dep_tracking_hash_via_hash!(Option); impl_dep_tracking_hash_via_hash!(Option); impl_dep_tracking_hash_via_hash!(Option); @@ -2382,6 +2352,7 @@ crate mod dep_tracking { impl_dep_tracking_hash_via_hash!(Option); impl_dep_tracking_hash_via_hash!(Option); impl_dep_tracking_hash_via_hash!(Option); + impl_dep_tracking_hash_via_hash!(Option); impl_dep_tracking_hash_via_hash!(Option); impl_dep_tracking_hash_via_hash!(Option); impl_dep_tracking_hash_via_hash!(CrateType); @@ -2443,7 +2414,7 @@ crate mod dep_tracking { } // This is a stable hash because BTreeMap is a sorted container - pub fn stable_hash( + crate fn stable_hash( sub_hashes: BTreeMap<&'static str, &dyn DepTrackingHash>, hasher: &mut DefaultHasher, error_format: ErrorOutputType, diff --git a/compiler/rustc_session/src/lib.rs b/compiler/rustc_session/src/lib.rs index d002f59739166..7971f7ef9efe2 100644 --- a/compiler/rustc_session/src/lib.rs +++ b/compiler/rustc_session/src/lib.rs @@ -1,9 +1,8 @@ #![feature(crate_visibility_modifier)] #![feature(once_cell)] -#![feature(or_patterns)] +#![cfg_attr(bootstrap, feature(or_patterns))] +#![recursion_limit = "256"] -#[macro_use] -extern crate bitflags; #[macro_use] extern crate rustc_macros; diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs index 6e7d39547a143..fd26f50da5a8e 100644 --- a/compiler/rustc_session/src/options.rs +++ b/compiler/rustc_session/src/options.rs @@ -5,7 +5,7 @@ use crate::lint; use crate::search_paths::SearchPath; use crate::utils::NativeLibKind; -use rustc_target::spec::{CodeModel, LinkerFlavor, MergeFunctions, PanicStrategy}; +use rustc_target::spec::{CodeModel, LinkerFlavor, MergeFunctions, PanicStrategy, SanitizerSet}; use rustc_target::spec::{RelocModel, RelroLevel, SplitDebuginfo, TargetTriple, TlsModel}; use rustc_feature::UnstableFeatures; @@ -147,6 +147,9 @@ top_level_options!( // by the compiler. json_artifact_notifications: bool [TRACKED], + // `true` if we're emitting a JSON blob containing the unused externs + json_unused_externs: bool [UNTRACKED], + pretty: Option [UNTRACKED], } ); @@ -248,9 +251,9 @@ macro_rules! options { pub const parse_list: &str = "a space-separated list of strings"; pub const parse_opt_list: &str = parse_list; pub const parse_opt_comma_list: &str = "a comma-separated list of strings"; - pub const parse_uint: &str = "a number"; - pub const parse_opt_uint: &str = parse_uint; - pub const parse_threads: &str = parse_uint; + pub const parse_number: &str = "a number"; + pub const parse_opt_number: &str = parse_number; + pub const parse_threads: &str = parse_number; pub const parse_passes: &str = "a space-separated list of passes, or `all`"; pub const parse_panic_strategy: &str = "either `unwind` or `abort`"; pub const parse_relro_level: &str = "one of: `full`, `partial`, or `off`"; @@ -262,6 +265,7 @@ macro_rules! options { pub const parse_linker_flavor: &str = ::rustc_target::spec::LinkerFlavor::one_of(); pub const parse_optimization_fuel: &str = "crate=integer"; pub const parse_mir_spanview: &str = "`statement` (default), `terminator`, or `block`"; + pub const parse_instrument_coverage: &str = "`all` (default), `except-unused-generics`, `except-unused-functions`, or `off`"; pub const parse_unpretty: &str = "`string` or `string=string`"; pub const parse_treat_err_as_bug: &str = "either no value or a number bigger than 0"; pub const parse_lto: &str = @@ -413,16 +417,16 @@ macro_rules! options { } } - /// Use this for any uint option that has a static default. - fn parse_uint(slot: &mut usize, v: Option<&str>) -> bool { + /// Use this for any numeric option that has a static default. + fn parse_number(slot: &mut T, v: Option<&str>) -> bool { match v.and_then(|s| s.parse().ok()) { Some(i) => { *slot = i; true }, None => false } } - /// Use this for any uint option that lacks a static default. - fn parse_opt_uint(slot: &mut Option, v: Option<&str>) -> bool { + /// Use this for any numeric option that lacks a static default. + fn parse_opt_number(slot: &mut Option, v: Option<&str>) -> bool { match v { Some(s) => { *slot = s.parse().ok(); slot.is_some() } None => false @@ -592,6 +596,41 @@ macro_rules! options { true } + fn parse_instrument_coverage(slot: &mut Option, v: Option<&str>) -> bool { + if v.is_some() { + let mut bool_arg = None; + if parse_opt_bool(&mut bool_arg, v) { + *slot = if bool_arg.unwrap() { + Some(InstrumentCoverage::All) + } else { + None + }; + return true + } + } + + let v = match v { + None => { + *slot = Some(InstrumentCoverage::All); + return true; + } + Some(v) => v, + }; + + *slot = Some(match v { + "all" => InstrumentCoverage::All, + "except-unused-generics" | "except_unused_generics" => { + InstrumentCoverage::ExceptUnusedGenerics + } + "except-unused-functions" | "except_unused_functions" => { + InstrumentCoverage::ExceptUnusedFunctions + } + "off" | "no" | "n" | "false" | "0" => InstrumentCoverage::Off, + _ => return false, + }); + true + } + fn parse_treat_err_as_bug(slot: &mut Option, v: Option<&str>) -> bool { match v { Some(s) => { *slot = s.parse().ok(); slot.is_some() } @@ -748,13 +787,13 @@ options! {CodegenOptions, CodegenSetter, basic_codegen_options, "this option is deprecated and does nothing"), code_model: Option = (None, parse_code_model, [TRACKED], "choose the code model to use (`rustc --print code-models` for details)"), - codegen_units: Option = (None, parse_opt_uint, [UNTRACKED], + codegen_units: Option = (None, parse_opt_number, [UNTRACKED], "divide crate into N units to optimize in parallel"), control_flow_guard: CFGuard = (CFGuard::Disabled, parse_cfguard, [TRACKED], "use Windows Control Flow Guard (default: no)"), debug_assertions: Option = (None, parse_opt_bool, [TRACKED], "explicitly enable the `cfg(debug_assertions)` directive"), - debuginfo: usize = (0, parse_uint, [TRACKED], + debuginfo: usize = (0, parse_number, [TRACKED], "debug info emission level (0 = no debug info, 1 = line tables only, \ 2 = full debug info with variable and type information; default: 0)"), default_linker_libraries: bool = (false, parse_bool, [UNTRACKED], @@ -769,13 +808,13 @@ options! {CodegenOptions, CodegenSetter, basic_codegen_options, "force use of unwind tables"), incremental: Option = (None, parse_opt_string, [UNTRACKED], "enable incremental compilation"), - inline_threshold: Option = (None, parse_opt_uint, [TRACKED], + inline_threshold: Option = (None, parse_opt_number, [TRACKED], "set the threshold for inlining a function"), link_arg: (/* redirected to link_args */) = ((), parse_string_push, [UNTRACKED], "a single extra argument to append to the linker invocation (can be used several times)"), link_args: Vec = (Vec::new(), parse_list, [UNTRACKED], "extra arguments to append to the linker invocation (space separated)"), - link_dead_code: Option = (None, parse_opt_bool, [UNTRACKED], + link_dead_code: Option = (None, parse_opt_bool, [TRACKED], "keep dead code at link time (useful for code coverage) (default: no)"), link_self_contained: Option = (None, parse_opt_bool, [UNTRACKED], "control whether to link Rust provided C objects/libraries or rely @@ -870,8 +909,6 @@ options! {DebuggingOptions, DebuggingSetter, basic_debugging_options, (default: no)"), borrowck: String = ("migrate".to_string(), parse_string, [UNTRACKED], "select which borrowck is used (`mir` or `migrate`) (default: `migrate`)"), - borrowck_stats: bool = (false, parse_bool, [UNTRACKED], - "gather borrowck statistics (default: no)"), cgu_partitioning_strategy: Option = (None, parse_opt_string, [TRACKED], "the codegen unit partitioning strategy to use"), chalk: bool = (false, parse_bool, [TRACKED], @@ -959,24 +996,22 @@ options! {DebuggingOptions, DebuggingSetter, basic_debugging_options, "verify incr. comp. hashes of green query instances (default: no)"), inline_mir: Option = (None, parse_opt_bool, [TRACKED], "enable MIR inlining (default: no)"), - inline_mir_threshold: Option = (None, parse_opt_uint, [TRACKED], + inline_mir_threshold: Option = (None, parse_opt_number, [TRACKED], "a default MIR inlining threshold (default: 50)"), - inline_mir_hint_threshold: Option = (None, parse_opt_uint, [TRACKED], + inline_mir_hint_threshold: Option = (None, parse_opt_number, [TRACKED], "inlining threshold for functions with inline hint (default: 100)"), inline_in_all_cgus: Option = (None, parse_opt_bool, [TRACKED], "control whether `#[inline]` functions are in all CGUs"), input_stats: bool = (false, parse_bool, [UNTRACKED], "gather statistics about the input (default: no)"), - insert_sideeffect: bool = (false, parse_bool, [TRACKED], - "fix undefined behavior when a thread doesn't eventually make progress \ - (such as entering an empty infinite loop) by inserting llvm.sideeffect \ - (default: no)"), - instrument_coverage: bool = (false, parse_bool, [TRACKED], + instrument_coverage: Option = (None, parse_instrument_coverage, [TRACKED], "instrument the generated code to support LLVM source-based code coverage \ reports (note, the compiler build config must include `profiler = true`, \ and is mutually exclusive with `-C profile-generate`/`-C profile-use`); \ implies `-Z symbol-mangling-version=v0`; disables/overrides some Rust \ - optimizations (default: no)"), + optimizations. Optional values are: `=all` (default coverage), \ + `=except-unused-generics`, `=except-unused-functions`, or `=off` \ + (default: instrument-coverage=off)"), instrument_mcount: bool = (false, parse_bool, [TRACKED], "insert function instrument code for mcount-based tracing (default: no)"), keep_hygiene_data: bool = (false, parse_bool, [UNTRACKED], @@ -999,10 +1034,10 @@ options! {DebuggingOptions, DebuggingSetter, basic_debugging_options, mir_emit_retag: bool = (false, parse_bool, [TRACKED], "emit Retagging MIR statements, interpreted e.g., by miri; implies -Zmir-opt-level=0 \ (default: no)"), - mir_opt_level: usize = (1, parse_uint, [TRACKED], - "MIR optimization level (0-3; default: 1)"), - mutable_noalias: bool = (false, parse_bool, [TRACKED], - "emit noalias metadata for mutable references (default: no)"), + mir_opt_level: Option = (None, parse_opt_number, [TRACKED], + "MIR optimization level (0-4; default: 1 in non optimized builds and 2 in optimized builds)"), + mutable_noalias: Option = (None, parse_opt_bool, [TRACKED], + "emit noalias metadata for mutable references (default: yes for LLVM >= 12, otherwise no)"), new_llvm_pass_manager: bool = (false, parse_bool, [TRACKED], "use new LLVM pass manager (default: no)"), nll_facts: bool = (false, parse_bool, [UNTRACKED], @@ -1120,7 +1155,7 @@ options! {DebuggingOptions, DebuggingSetter, basic_debugging_options, "which mangling version to use for symbol names ('legacy' (default) or 'v0')"), teach: bool = (false, parse_bool, [TRACKED], "show extended diagnostic help (default: no)"), - terminal_width: Option = (None, parse_opt_uint, [UNTRACKED], + terminal_width: Option = (None, parse_opt_number, [UNTRACKED], "set the current terminal width"), tune_cpu: Option = (None, parse_opt_string, [TRACKED], "select processor to schedule for (`rustc --print target-cpus` for details)"), diff --git a/compiler/rustc_session/src/parse.rs b/compiler/rustc_session/src/parse.rs index 81b38347414e8..65d5d96aba1de 100644 --- a/compiler/rustc_session/src/parse.rs +++ b/compiler/rustc_session/src/parse.rs @@ -13,7 +13,6 @@ use rustc_span::hygiene::ExpnId; use rustc_span::source_map::{FilePathMapping, SourceMap}; use rustc_span::{MultiSpan, Span, Symbol}; -use std::path::PathBuf; use std::str; /// The set of keys (and, optionally, values) that define the compilation @@ -122,8 +121,6 @@ pub struct ParseSess { pub missing_fragment_specifiers: Lock>, /// Places where raw identifiers were used. This is used for feature-gating raw identifiers. pub raw_identifier_spans: Lock>, - /// Used to determine and report recursive module inclusions. - pub included_mod_stack: Lock>, source_map: Lrc, pub buffered_lints: Lock>, /// Contains the spans of block expressions that could have been incomplete based on the @@ -143,6 +140,7 @@ pub struct ParseSess { } impl ParseSess { + /// Used for testing. pub fn new(file_path_mapping: FilePathMapping) -> Self { let sm = Lrc::new(SourceMap::new(file_path_mapping)); let handler = Handler::with_tty_emitter(ColorConfig::Auto, true, None, Some(sm.clone())); @@ -157,7 +155,6 @@ impl ParseSess { edition: ExpnId::root().expn_data().edition, missing_fragment_specifiers: Default::default(), raw_identifier_spans: Lock::new(Vec::new()), - included_mod_stack: Lock::new(vec![]), source_map, buffered_lints: Lock::new(vec![]), ambiguous_block_expr_parse: Lock::new(FxHashMap::default()), diff --git a/compiler/rustc_session/src/session.rs b/compiler/rustc_session/src/session.rs index 08a7447008ae7..e3ea3a11c4d40 100644 --- a/compiler/rustc_session/src/session.rs +++ b/compiler/rustc_session/src/session.rs @@ -1,14 +1,13 @@ use crate::cgu_reuse_tracker::CguReuseTracker; use crate::code_stats::CodeStats; pub use crate::code_stats::{DataTypeKind, FieldInfo, SizeKind, VariantInfo}; -use crate::config::{self, CrateType, OutputType, PrintRequest, SanitizerSet, SwitchWithOptPath}; +use crate::config::{self, CrateType, OutputType, PrintRequest, SwitchWithOptPath}; use crate::filesearch; use crate::lint::{self, LintId}; use crate::parse::ParseSess; use crate::search_paths::{PathKind, SearchPath}; pub use rustc_ast::attr::MarkedAttrs; -pub use rustc_ast::crate_disambiguator::CrateDisambiguator; pub use rustc_ast::Attribute; use rustc_data_structures::flock; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; @@ -21,14 +20,15 @@ use rustc_errors::annotate_snippet_emitter_writer::AnnotateSnippetEmitterWriter; use rustc_errors::emitter::{Emitter, EmitterWriter, HumanReadableErrorType}; use rustc_errors::json::JsonEmitter; use rustc_errors::registry::Registry; -use rustc_errors::{Applicability, Diagnostic, DiagnosticBuilder, DiagnosticId, ErrorReported}; +use rustc_errors::{Diagnostic, DiagnosticBuilder, DiagnosticId, ErrorReported}; use rustc_lint_defs::FutureBreakage; +pub use rustc_span::crate_disambiguator::CrateDisambiguator; use rustc_span::edition::Edition; use rustc_span::source_map::{FileLoader, MultiSpan, RealFileLoader, SourceMap, Span}; use rustc_span::{sym, SourceFileHashAlgorithm, Symbol}; use rustc_target::asm::InlineAsmArch; use rustc_target::spec::{CodeModel, PanicStrategy, RelocModel, RelroLevel}; -use rustc_target::spec::{SplitDebuginfo, Target, TargetTriple, TlsModel}; +use rustc_target::spec::{SanitizerSet, SplitDebuginfo, Target, TargetTriple, TlsModel}; use std::cell::{self, RefCell}; use std::env; @@ -241,8 +241,7 @@ pub struct PerfStats { enum DiagnosticBuilderMethod { Note, SpanNote, - SpanSuggestion(String), // suggestion - // Add more variants as needed to support one-time diagnostics. + // Add more variants as needed to support one-time diagnostics. } /// Trait implemented by error types. This should not be implemented manually. Instead, use @@ -551,15 +550,6 @@ impl Session { let span = span_maybe.expect("`span_note` needs a span"); diag_builder.span_note(span, message); } - DiagnosticBuilderMethod::SpanSuggestion(suggestion) => { - let span = span_maybe.expect("`span_suggestion_*` needs a span"); - diag_builder.span_suggestion( - span, - message, - suggestion, - Applicability::Unspecified, - ); - } } } } @@ -589,23 +579,6 @@ impl Session { self.diag_once(diag_builder, DiagnosticBuilderMethod::Note, msg_id, message, None); } - pub fn diag_span_suggestion_once<'a, 'b>( - &'a self, - diag_builder: &'b mut DiagnosticBuilder<'a>, - msg_id: DiagnosticMessageId, - span: Span, - message: &str, - suggestion: String, - ) { - self.diag_once( - diag_builder, - DiagnosticBuilderMethod::SpanSuggestion(suggestion), - msg_id, - message, - Some(span), - ); - } - #[inline] pub fn source_map(&self) -> &SourceMap { self.parse_sess.source_map() @@ -631,15 +604,18 @@ impl Session { pub fn verify_llvm_ir(&self) -> bool { self.opts.debugging_opts.verify_llvm_ir || option_env!("RUSTC_VERIFY_LLVM_IR").is_some() } - pub fn borrowck_stats(&self) -> bool { - self.opts.debugging_opts.borrowck_stats - } pub fn print_llvm_passes(&self) -> bool { self.opts.debugging_opts.print_llvm_passes } pub fn binary_dep_depinfo(&self) -> bool { self.opts.debugging_opts.binary_dep_depinfo } + pub fn mir_opt_level(&self) -> usize { + self.opts + .debugging_opts + .mir_opt_level + .unwrap_or_else(|| if self.opts.optimize != config::OptLevel::No { 2 } else { 1 }) + } /// Gets the features enabled for the current compilation session. /// DO NOT USE THIS METHOD if there is a TyCtxt available, as it circumvents @@ -831,8 +807,11 @@ impl Session { // This is used to control the emission of the `uwtable` attribute on // LLVM functions. // - // At the very least, unwind tables are needed when compiling with - // `-C panic=unwind`. + // Unwind tables are needed when compiling with `-C panic=unwind`, but + // LLVM won't omit unwind tables unless the function is also marked as + // `nounwind`, so users are allowed to disable `uwtable` emission. + // Historically rustc always emits `uwtable` attributes by default, so + // even they can be disabled, they're still emitted by default. // // On some targets (including windows), however, exceptions include // other events such as illegal instructions, segfaults, etc. This means @@ -845,13 +824,10 @@ impl Session { // If a target requires unwind tables, then they must be emitted. // Otherwise, we can defer to the `-C force-unwind-tables=` // value, if it is provided, or disable them, if not. - if self.panic_strategy() == PanicStrategy::Unwind { - true - } else if self.target.requires_uwtable { - true - } else { - self.opts.cg.force_unwind_tables.unwrap_or(false) - } + self.target.requires_uwtable + || self.opts.cg.force_unwind_tables.unwrap_or( + self.panic_strategy() == PanicStrategy::Unwind || self.target.default_uwtable, + ) } /// Returns the symbol name for the registrar function, @@ -884,22 +860,6 @@ impl Session { ) } - pub fn set_incr_session_load_dep_graph(&self, load: bool) { - let mut incr_comp_session = self.incr_comp_session.borrow_mut(); - - if let IncrCompSession::Active { ref mut load_dep_graph, .. } = *incr_comp_session { - *load_dep_graph = load; - } - } - - pub fn incr_session_load_dep_graph(&self) -> bool { - let incr_comp_session = self.incr_comp_session.borrow(); - match *incr_comp_session { - IncrCompSession::Active { load_dep_graph, .. } => load_dep_graph, - _ => false, - } - } - pub fn init_incr_comp_session( &self, session_dir: PathBuf, @@ -1137,6 +1097,21 @@ impl Session { self.opts.cg.link_dead_code.unwrap_or(false) } + pub fn instrument_coverage(&self) -> bool { + self.opts.debugging_opts.instrument_coverage.unwrap_or(config::InstrumentCoverage::Off) + != config::InstrumentCoverage::Off + } + + pub fn instrument_coverage_except_unused_generics(&self) -> bool { + self.opts.debugging_opts.instrument_coverage.unwrap_or(config::InstrumentCoverage::Off) + == config::InstrumentCoverage::ExceptUnusedGenerics + } + + pub fn instrument_coverage_except_unused_functions(&self) -> bool { + self.opts.debugging_opts.instrument_coverage.unwrap_or(config::InstrumentCoverage::Off) + == config::InstrumentCoverage::ExceptUnusedFunctions + } + pub fn mark_attr_known(&self, attr: &Attribute) { self.known_attrs.lock().mark(attr) } @@ -1509,13 +1484,6 @@ fn validate_commandline_args_with_session_available(sess: &Session) { // Unwind tables cannot be disabled if the target requires them. if let Some(include_uwtables) = sess.opts.cg.force_unwind_tables { - if sess.panic_strategy() == PanicStrategy::Unwind && !include_uwtables { - sess.err( - "panic=unwind requires unwind tables, they cannot be disabled \ - with `-C force-unwind-tables=no`.", - ); - } - if sess.target.requires_uwtable && !include_uwtables { sess.err( "target requires unwind tables, they cannot be disabled with \ @@ -1543,59 +1511,22 @@ fn validate_commandline_args_with_session_available(sess: &Session) { ); } - const ASAN_SUPPORTED_TARGETS: &[&str] = &[ - "aarch64-apple-darwin", - "aarch64-fuchsia", - "aarch64-unknown-linux-gnu", - "x86_64-apple-darwin", - "x86_64-fuchsia", - "x86_64-unknown-freebsd", - "x86_64-unknown-linux-gnu", - ]; - const LSAN_SUPPORTED_TARGETS: &[&str] = &[ - "aarch64-apple-darwin", - "aarch64-unknown-linux-gnu", - "x86_64-apple-darwin", - "x86_64-unknown-linux-gnu", - ]; - const MSAN_SUPPORTED_TARGETS: &[&str] = - &["aarch64-unknown-linux-gnu", "x86_64-unknown-freebsd", "x86_64-unknown-linux-gnu"]; - const TSAN_SUPPORTED_TARGETS: &[&str] = &[ - "aarch64-apple-darwin", - "aarch64-unknown-linux-gnu", - "x86_64-apple-darwin", - "x86_64-unknown-freebsd", - "x86_64-unknown-linux-gnu", - ]; - const HWASAN_SUPPORTED_TARGETS: &[&str] = - &["aarch64-linux-android", "aarch64-unknown-linux-gnu"]; - - // Sanitizers can only be used on some tested platforms. - for s in sess.opts.debugging_opts.sanitizer { - let supported_targets = match s { - SanitizerSet::ADDRESS => ASAN_SUPPORTED_TARGETS, - SanitizerSet::LEAK => LSAN_SUPPORTED_TARGETS, - SanitizerSet::MEMORY => MSAN_SUPPORTED_TARGETS, - SanitizerSet::THREAD => TSAN_SUPPORTED_TARGETS, - SanitizerSet::HWADDRESS => HWASAN_SUPPORTED_TARGETS, - _ => panic!("unrecognized sanitizer {}", s), - }; - if !supported_targets.contains(&&*sess.opts.target_triple.triple()) { - sess.err(&format!( - "`-Zsanitizer={}` only works with targets: {}", - s, - supported_targets.join(", ") - )); - } - let conflicting = sess.opts.debugging_opts.sanitizer - s; - if !conflicting.is_empty() { - sess.err(&format!( - "`-Zsanitizer={}` is incompatible with `-Zsanitizer={}`", - s, conflicting, - )); - // Don't report additional errors. - break; - } + // Sanitizers can only be used on platforms that we know have working sanitizer codegen. + let supported_sanitizers = sess.target.options.supported_sanitizers; + let unsupported_sanitizers = sess.opts.debugging_opts.sanitizer - supported_sanitizers; + match unsupported_sanitizers.into_iter().count() { + 0 => {} + 1 => sess + .err(&format!("{} sanitizer is not supported for this target", unsupported_sanitizers)), + _ => sess.err(&format!( + "{} sanitizers are not supported for this target", + unsupported_sanitizers + )), + } + // Cannot mix and match sanitizers. + let mut sanitizer_iter = sess.opts.debugging_opts.sanitizer.into_iter(); + if let (Some(first), Some(second)) = (sanitizer_iter.next(), sanitizer_iter.next()) { + sess.err(&format!("`-Zsanitizer={}` is incompatible with `-Zsanitizer={}`", first, second)); } } diff --git a/compiler/rustc_session/src/utils.rs b/compiler/rustc_session/src/utils.rs index f3d3330912464..e9d597d1ba65c 100644 --- a/compiler/rustc_session/src/utils.rs +++ b/compiler/rustc_session/src/utils.rs @@ -1,7 +1,13 @@ +use crate::parse::ParseSess; use crate::session::Session; +use rustc_ast::token::{self, DelimToken, Nonterminal, Token}; +use rustc_ast::tokenstream::CanSynthesizeMissingTokens; +use rustc_ast::tokenstream::{DelimSpan, TokenStream, TokenTree}; use rustc_data_structures::profiling::VerboseTimingGuard; use std::path::{Path, PathBuf}; +pub type NtToTokenstream = fn(&Nonterminal, &ParseSess, CanSynthesizeMissingTokens) -> TokenStream; + impl Session { pub fn timer<'a>(&'a self, what: &'static str) -> VerboseTimingGuard<'a> { self.prof.verbose_generic_activity(what) @@ -53,3 +59,52 @@ impl CanonicalizedPath { &self.original } } + +// FIXME: Find a better spot for this - it needs to be accessible from `rustc_ast_lowering`, +// and needs to access `ParseSess +pub struct FlattenNonterminals<'a> { + pub parse_sess: &'a ParseSess, + pub synthesize_tokens: CanSynthesizeMissingTokens, + pub nt_to_tokenstream: NtToTokenstream, +} + +impl<'a> FlattenNonterminals<'a> { + pub fn process_token_stream(&mut self, tokens: TokenStream) -> TokenStream { + fn can_skip(stream: &TokenStream) -> bool { + stream.trees().all(|tree| match tree { + TokenTree::Token(token) => !matches!(token.kind, token::Interpolated(_)), + TokenTree::Delimited(_, _, inner) => can_skip(&inner), + }) + } + + if can_skip(&tokens) { + return tokens; + } + + tokens.into_trees().flat_map(|tree| self.process_token_tree(tree).into_trees()).collect() + } + + pub fn process_token_tree(&mut self, tree: TokenTree) -> TokenStream { + match tree { + TokenTree::Token(token) => self.process_token(token), + TokenTree::Delimited(span, delim, tts) => { + TokenTree::Delimited(span, delim, self.process_token_stream(tts)).into() + } + } + } + + pub fn process_token(&mut self, token: Token) -> TokenStream { + match token.kind { + token::Interpolated(nt) => { + let tts = (self.nt_to_tokenstream)(&nt, self.parse_sess, self.synthesize_tokens); + TokenTree::Delimited( + DelimSpan::from_single(token.span), + DelimToken::NoDelim, + self.process_token_stream(tts), + ) + .into() + } + _ => TokenTree::Token(token).into(), + } + } +} diff --git a/compiler/rustc_ast/src/crate_disambiguator.rs b/compiler/rustc_span/src/crate_disambiguator.rs similarity index 100% rename from compiler/rustc_ast/src/crate_disambiguator.rs rename to compiler/rustc_span/src/crate_disambiguator.rs diff --git a/compiler/rustc_span/src/def_id.rs b/compiler/rustc_span/src/def_id.rs index 70e9526f626da..95bb0ad7ba2e1 100644 --- a/compiler/rustc_span/src/def_id.rs +++ b/compiler/rustc_span/src/def_id.rs @@ -1,3 +1,4 @@ +use crate::crate_disambiguator::CrateDisambiguator; use crate::HashStableContext; use rustc_data_structures::fingerprint::Fingerprint; use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; @@ -105,10 +106,74 @@ impl ::std::fmt::Debug for CrateNum { } } +/// A `DefPathHash` is a fixed-size representation of a `DefPath` that is +/// stable across crate and compilation session boundaries. It consists of two +/// separate 64-bit hashes. The first uniquely identifies the crate this +/// `DefPathHash` originates from (see [StableCrateId]), and the second +/// uniquely identifies the corresponding `DefPath` within that crate. Together +/// they form a unique identifier within an entire crate graph. +/// +/// There is a very small chance of hash collisions, which would mean that two +/// different `DefPath`s map to the same `DefPathHash`. Proceeding compilation +/// with such a hash collision would very probably lead to an ICE, and in the +/// worst case lead to a silent mis-compilation. The compiler therefore actively +/// and exhaustively checks for such hash collisions and aborts compilation if +/// it finds one. +/// +/// `DefPathHash` uses 64-bit hashes for both the crate-id part and the +/// crate-internal part, even though it is likely that there are many more +/// `LocalDefId`s in a single crate than there are individual crates in a crate +/// graph. Since we use the same number of bits in both cases, the collision +/// probability for the crate-local part will be quite a bit higher (though +/// still very small). +/// +/// This imbalance is not by accident: A hash collision in the +/// crate-local part of a `DefPathHash` will be detected and reported while +/// compiling the crate in question. Such a collision does not depend on +/// outside factors and can be easily fixed by the crate maintainer (e.g. by +/// renaming the item in question or by bumping the crate version in a harmless +/// way). +/// +/// A collision between crate-id hashes on the other hand is harder to fix +/// because it depends on the set of crates in the entire crate graph of a +/// compilation session. Again, using the same crate with a different version +/// number would fix the issue with a high probability -- but that might be +/// easier said then done if the crates in questions are dependencies of +/// third-party crates. +/// +/// That being said, given a high quality hash function, the collision +/// probabilities in question are very small. For example, for a big crate like +/// `rustc_middle` (with ~50000 `LocalDefId`s as of the time of writing) there +/// is a probability of roughly 1 in 14,750,000,000 of a crate-internal +/// collision occurring. For a big crate graph with 1000 crates in it, there is +/// a probability of 1 in 36,890,000,000,000 of a `StableCrateId` collision. #[derive(Copy, Clone, Hash, PartialEq, Eq, PartialOrd, Ord, Debug)] #[derive(HashStable_Generic, Encodable, Decodable)] pub struct DefPathHash(pub Fingerprint); +impl DefPathHash { + /// Returns the [StableCrateId] identifying the crate this [DefPathHash] + /// originates from. + #[inline] + pub fn stable_crate_id(&self) -> StableCrateId { + StableCrateId(self.0.as_value().0) + } + + /// Returns the crate-local part of the [DefPathHash]. + /// + /// Used for tests. + #[inline] + pub fn local_hash(&self) -> u64 { + self.0.as_value().1 + } + + /// Builds a new [DefPathHash] with the given [StableCrateId] and + /// `local_hash`, where `local_hash` must be unique within its crate. + pub fn new(stable_crate_id: StableCrateId, local_hash: u64) -> DefPathHash { + DefPathHash(Fingerprint::new(stable_crate_id.0, local_hash)) + } +} + impl Borrow for DefPathHash { #[inline] fn borrow(&self) -> &Fingerprint { @@ -116,6 +181,30 @@ impl Borrow for DefPathHash { } } +/// A [StableCrateId] is a 64 bit hash of `(crate-name, crate-disambiguator)`. It +/// is to [CrateNum] what [DefPathHash] is to [DefId]. It is stable across +/// compilation sessions. +/// +/// Since the ID is a hash value there is a (very small) chance that two crates +/// end up with the same [StableCrateId]. The compiler will check for such +/// collisions when loading crates and abort compilation in order to avoid +/// further trouble. +#[derive(Copy, Clone, Hash, PartialEq, Eq, PartialOrd, Ord, Debug, Encodable, Decodable)] +pub struct StableCrateId(u64); + +impl StableCrateId { + /// Computes the stable ID for a crate with the given name and + /// disambiguator. + pub fn new(crate_name: &str, crate_disambiguator: CrateDisambiguator) -> StableCrateId { + use std::hash::Hash; + + let mut hasher = StableHasher::new(); + crate_name.hash(&mut hasher); + crate_disambiguator.hash(&mut hasher); + StableCrateId(hasher.finish()) + } +} + rustc_index::newtype_index! { /// A DefIndex is an index into the hir-map for a crate, identifying a /// particular definition. It should really be considered an interned diff --git a/compiler/rustc_span/src/edition.rs b/compiler/rustc_span/src/edition.rs index a9200dd7dfd6e..8544acd6d05ee 100644 --- a/compiler/rustc_span/src/edition.rs +++ b/compiler/rustc_span/src/edition.rs @@ -20,7 +20,7 @@ pub enum Edition { Edition2015, /// The 2018 edition Edition2018, - /// The 2021 ediiton + /// The 2021 edition Edition2021, } diff --git a/compiler/rustc_span/src/hygiene.rs b/compiler/rustc_span/src/hygiene.rs index 4ccf657335fed..f75fe22767f47 100644 --- a/compiler/rustc_span/src/hygiene.rs +++ b/compiler/rustc_span/src/hygiene.rs @@ -118,7 +118,8 @@ impl ExpnId { HygieneData::with(|data| { let old_expn_data = &mut data.expn_data[self.0 as usize]; assert!(old_expn_data.is_none(), "expansion data is reset for an expansion ID"); - expn_data.orig_id.replace(self.as_u32()).expect_none("orig_id should be None"); + assert_eq!(expn_data.orig_id, None); + expn_data.orig_id = Some(self.as_u32()); *old_expn_data = Some(expn_data); }); update_disambiguator(self) @@ -202,7 +203,8 @@ impl HygieneData { fn fresh_expn(&mut self, mut expn_data: Option) -> ExpnId { let raw_id = self.expn_data.len() as u32; if let Some(data) = expn_data.as_mut() { - data.orig_id.replace(raw_id).expect_none("orig_id should be None"); + assert_eq!(data.orig_id, None); + data.orig_id = Some(raw_id); } self.expn_data.push(expn_data); ExpnId(raw_id) @@ -411,7 +413,7 @@ pub fn update_dollar_crate_names(mut get_name: impl FnMut(SyntaxContext) -> Symb let names: Vec<_> = range_to_update.clone().map(|idx| get_name(SyntaxContext::from_u32(idx as u32))).collect(); HygieneData::with(|data| { - range_to_update.zip(names.into_iter()).for_each(|(idx, name)| { + range_to_update.zip(names).for_each(|(idx, name)| { data.syntax_context_data[idx].dollar_crate_name = name; }) }) @@ -746,7 +748,7 @@ pub struct ExpnData { /// Used to force two `ExpnData`s to have different `Fingerprint`s. /// Due to macro expansion, it's possible to end up with two `ExpnId`s - /// that have identical `ExpnData`s. This violates the constract of `HashStable` + /// that have identical `ExpnData`s. This violates the contract of `HashStable` /// - the two `ExpnId`s are not equal, but their `Fingerprint`s are equal /// (since the numerical `ExpnId` value is not considered by the `HashStable` /// implementation). @@ -1174,11 +1176,7 @@ pub fn decode_syntax_context< Ok(new_ctxt) } -pub fn num_syntax_ctxts() -> usize { - HygieneData::with(|data| data.syntax_context_data.len()) -} - -pub fn for_all_ctxts_in Result<(), E>>( +fn for_all_ctxts_in Result<(), E>>( ctxts: impl Iterator, mut f: F, ) -> Result<(), E> { @@ -1191,7 +1189,7 @@ pub fn for_all_ctxts_in Ok(()) } -pub fn for_all_expns_in Result<(), E>>( +fn for_all_expns_in Result<(), E>>( expns: impl Iterator, mut f: F, ) -> Result<(), E> { @@ -1204,16 +1202,6 @@ pub fn for_all_expns_in Result<(), E>>( Ok(()) } -pub fn for_all_data Result<(), E>>( - mut f: F, -) -> Result<(), E> { - let all_data = HygieneData::with(|data| data.syntax_context_data.clone()); - for (i, data) in all_data.into_iter().enumerate() { - f((i as u32, SyntaxContext(i as u32), &data))?; - } - Ok(()) -} - impl Encodable for ExpnId { default fn encode(&self, _: &mut E) -> Result<(), E::Error> { panic!("cannot encode `ExpnId` with `{}`", std::any::type_name::()); @@ -1226,14 +1214,6 @@ impl Decodable for ExpnId { } } -pub fn for_all_expn_data Result<(), E>>(mut f: F) -> Result<(), E> { - let all_data = HygieneData::with(|data| data.expn_data.clone()); - for (i, data) in all_data.into_iter().enumerate() { - f(i as u32, &data.unwrap_or_else(|| panic!("Missing ExpnData!")))?; - } - Ok(()) -} - pub fn raw_encode_syntax_context( ctxt: SyntaxContext, context: &HygieneEncodeContext, @@ -1362,12 +1342,6 @@ fn update_disambiguator(expn_id: ExpnId) { fn hash_spans(&self) -> bool { true } - fn byte_pos_to_line_and_col( - &mut self, - byte: BytePos, - ) -> Option<(Lrc, usize, BytePos)> { - self.caching_source_map.byte_pos_to_line_and_col(byte) - } fn span_data_to_lines_and_cols( &mut self, span: &crate::SpanData, @@ -1416,9 +1390,11 @@ fn update_disambiguator(expn_id: ExpnId) { let new_hash: Fingerprint = hasher.finish(); HygieneData::with(|data| { - data.expn_data_disambiguators - .get(&new_hash) - .expect_none("Hash collision after disambiguator update!"); + assert_eq!( + data.expn_data_disambiguators.get(&new_hash), + None, + "Hash collision after disambiguator update!", + ); }); }; } diff --git a/compiler/rustc_span/src/lib.rs b/compiler/rustc_span/src/lib.rs index 4dce029e86b50..6f6ff37c525a2 100644 --- a/compiler/rustc_span/src/lib.rs +++ b/compiler/rustc_span/src/lib.rs @@ -21,7 +21,6 @@ #![feature(negative_impls)] #![feature(nll)] #![feature(min_specialization)] -#![feature(option_expect_none)] #[macro_use] extern crate rustc_macros; @@ -47,6 +46,8 @@ pub mod lev_distance; mod span_encoding; pub use span_encoding::{Span, DUMMY_SP}; +pub mod crate_disambiguator; + pub mod symbol; pub use symbol::{sym, Symbol}; @@ -1036,10 +1037,6 @@ pub enum ExternalSourceKind { } impl ExternalSource { - pub fn is_absent(&self) -> bool { - !matches!(self, ExternalSource::Foreign { kind: ExternalSourceKind::Present(_), .. }) - } - pub fn get_source(&self) -> Option<&Lrc> { match self { ExternalSource::Foreign { kind: ExternalSourceKind::Present(ref src), .. } => Some(src), @@ -1432,9 +1429,6 @@ impl SourceFile { self.src.is_none() } - pub fn byte_length(&self) -> u32 { - self.end_pos.0 - self.start_pos.0 - } pub fn count_lines(&self) -> usize { self.lines.len() } @@ -1872,10 +1866,6 @@ pub trait HashStableContext { fn expn_id_cache() -> &'static LocalKey; fn hash_crate_num(&mut self, _: CrateNum, hasher: &mut StableHasher); fn hash_spans(&self) -> bool; - fn byte_pos_to_line_and_col( - &mut self, - byte: BytePos, - ) -> Option<(Lrc, usize, BytePos)>; fn span_data_to_lines_and_cols( &mut self, span: &SpanData, @@ -1904,9 +1894,10 @@ where return; } + self.ctxt().hash_stable(ctx, hasher); + if self.is_dummy() { Hash::hash(&TAG_INVALID_SPAN, hasher); - self.ctxt().hash_stable(ctx, hasher); return; } @@ -1919,7 +1910,6 @@ where Some(pos) => pos, None => { Hash::hash(&TAG_INVALID_SPAN, hasher); - span.ctxt.hash_stable(ctx, hasher); return; } }; @@ -1946,7 +1936,6 @@ where let len = (span.hi - span.lo).0; Hash::hash(&col_line, hasher); Hash::hash(&len, hasher); - span.ctxt.hash_stable(ctx, hasher); } } @@ -1999,7 +1988,8 @@ impl HashStable for ExpnId { if cache.len() < new_len { cache.resize(new_len, None); } - cache[index].replace(sub_hash).expect_none("Cache slot was filled"); + let prev = cache[index].replace(sub_hash); + assert_eq!(prev, None, "Cache slot was filled"); }); sub_hash.hash_stable(ctx, hasher); } diff --git a/compiler/rustc_span/src/source_map.rs b/compiler/rustc_span/src/source_map.rs index b7eb6d5b3790b..f612d1425b932 100644 --- a/compiler/rustc_span/src/source_map.rs +++ b/compiler/rustc_span/src/source_map.rs @@ -453,41 +453,6 @@ impl SourceMap { } } - /// Returns `Some(span)`, a union of the LHS and RHS span. The LHS must precede the RHS. If - /// there are gaps between LHS and RHS, the resulting union will cross these gaps. - /// For this to work, - /// - /// * the syntax contexts of both spans much match, - /// * the LHS span needs to end on the same line the RHS span begins, - /// * the LHS span must start at or before the RHS span. - pub fn merge_spans(&self, sp_lhs: Span, sp_rhs: Span) -> Option { - // Ensure we're at the same expansion ID. - if sp_lhs.ctxt() != sp_rhs.ctxt() { - return None; - } - - let lhs_end = match self.lookup_line(sp_lhs.hi()) { - Ok(x) => x, - Err(_) => return None, - }; - let rhs_begin = match self.lookup_line(sp_rhs.lo()) { - Ok(x) => x, - Err(_) => return None, - }; - - // If we must cross lines to merge, don't merge. - if lhs_end.line != rhs_begin.line { - return None; - } - - // Ensure these follow the expected order and that we don't overlap. - if (sp_lhs.lo() <= sp_rhs.lo()) && (sp_lhs.hi() <= sp_rhs.lo()) { - Some(sp_lhs.to(sp_rhs)) - } else { - None - } - } - pub fn span_to_string(&self, sp: Span) -> String { if self.files.borrow().source_files.is_empty() && sp.is_dummy() { return "no-location".to_string(); @@ -931,13 +896,6 @@ impl SourceMap { SourceFileAndBytePos { sf, pos: offset } } - /// Converts an absolute `BytePos` to a `CharPos` relative to the `SourceFile`. - pub fn bytepos_to_file_charpos(&self, bpos: BytePos) -> CharPos { - let idx = self.lookup_source_file_idx(bpos); - let sf = &(*self.files.borrow().source_files)[idx]; - sf.bytepos_to_file_charpos(bpos) - } - // Returns the index of the `SourceFile` (in `self.files`) that contains `pos`. // This index is guaranteed to be valid for the lifetime of this `SourceMap`, // since `source_files` is a `MonotonicVec` diff --git a/compiler/rustc_span/src/source_map/tests.rs b/compiler/rustc_span/src/source_map/tests.rs index 0aca677248b72..7d814f1d82c11 100644 --- a/compiler/rustc_span/src/source_map/tests.rs +++ b/compiler/rustc_span/src/source_map/tests.rs @@ -10,6 +10,50 @@ fn init_source_map() -> SourceMap { sm } +impl SourceMap { + /// Returns `Some(span)`, a union of the LHS and RHS span. The LHS must precede the RHS. If + /// there are gaps between LHS and RHS, the resulting union will cross these gaps. + /// For this to work, + /// + /// * the syntax contexts of both spans much match, + /// * the LHS span needs to end on the same line the RHS span begins, + /// * the LHS span must start at or before the RHS span. + fn merge_spans(&self, sp_lhs: Span, sp_rhs: Span) -> Option { + // Ensure we're at the same expansion ID. + if sp_lhs.ctxt() != sp_rhs.ctxt() { + return None; + } + + let lhs_end = match self.lookup_line(sp_lhs.hi()) { + Ok(x) => x, + Err(_) => return None, + }; + let rhs_begin = match self.lookup_line(sp_rhs.lo()) { + Ok(x) => x, + Err(_) => return None, + }; + + // If we must cross lines to merge, don't merge. + if lhs_end.line != rhs_begin.line { + return None; + } + + // Ensure these follow the expected order and that we don't overlap. + if (sp_lhs.lo() <= sp_rhs.lo()) && (sp_lhs.hi() <= sp_rhs.lo()) { + Some(sp_lhs.to(sp_rhs)) + } else { + None + } + } + + /// Converts an absolute `BytePos` to a `CharPos` relative to the `SourceFile`. + fn bytepos_to_file_charpos(&self, bpos: BytePos) -> CharPos { + let idx = self.lookup_source_file_idx(bpos); + let sf = &(*self.files.borrow().source_files)[idx]; + sf.bytepos_to_file_charpos(bpos) + } +} + /// Tests `lookup_byte_offset`. #[test] fn t3() { diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index f9af0886a959a..1d1471fdeca04 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -18,7 +18,7 @@ use crate::{Edition, Span, DUMMY_SP, SESSION_GLOBALS}; #[cfg(test)] mod tests; -// The proc macro code for this is in `src/librustc_macros/src/symbols.rs`. +// The proc macro code for this is in `compiler/rustc_macros/src/symbols.rs`. symbols! { // After modifying this list adjust `is_special`, `is_used_keyword`/`is_unused_keyword`, // this should be rarely necessary though if the keywords are kept in alphabetic order. @@ -129,6 +129,7 @@ symbols! { BTreeMap, BTreeSet, BinaryHeap, + Borrow, C, CString, Center, @@ -141,6 +142,7 @@ symbols! { Decodable, Decoder, Default, + Deref, Encodable, Encoder, Eq, @@ -328,6 +330,7 @@ symbols! { bridge, bswap, c_str, + c_unwind, c_variadic, call, call_mut, @@ -342,6 +345,7 @@ symbols! { cfg_attr, cfg_attr_multi, cfg_doctest, + cfg_eval, cfg_panic, cfg_sanitize, cfg_target_feature, @@ -380,6 +384,7 @@ symbols! { const_fn_fn_ptr_basics, const_fn_transmute, const_fn_union, + const_generic_defaults, const_generics, const_generics_defaults, const_if_match, @@ -472,6 +477,7 @@ symbols! { doc_cfg, doc_keyword, doc_masked, + doc_notable_trait, doc_spotlight, doctest, document_private_items, @@ -555,6 +561,7 @@ symbols! { fmt, fmt_internals, fmul_fast, + fn_align, fn_must_use, fn_mut, fn_once, @@ -732,6 +739,7 @@ symbols! { min_const_generics, min_const_unsafe_fn, min_specialization, + min_type_alias_impl_trait, minnumf32, minnumf64, mips_target_feature, @@ -788,10 +796,13 @@ symbols! { non_modrs_mods, none_error, nontemporal_store, - nontrapping_dash_fptoint: "nontrapping-fptoint", + noop_method_borrow, + noop_method_clone, + noop_method_deref, noreturn, nostack, not, + notable_trait, note, object_safe_for_dispatch, of, @@ -837,7 +848,7 @@ symbols! { partial_ord, passes, pat, - pat2018, + pat2015, pat2021, path, pattern_parentheses, @@ -888,6 +899,8 @@ symbols! { profiler_runtime, ptr_guaranteed_eq, ptr_guaranteed_ne, + ptr_null, + ptr_null_mut, ptr_offset_from, pub_macro_rules, pub_restricted, @@ -952,8 +965,11 @@ symbols! { rt, rtm_target_feature, rust, + rust_2015, rust_2015_preview, + rust_2018, rust_2018_preview, + rust_2021, rust_2021_preview, rust_begin_unwind, rust_eh_catch_typeinfo, @@ -991,6 +1007,7 @@ symbols! { rustc_layout_scalar_valid_range_start, rustc_legacy_const_generics, rustc_macro_transparency, + rustc_main, rustc_mir, rustc_nonnull_optimization_guaranteed, rustc_object_lifetime_default, @@ -1070,6 +1087,7 @@ symbols! { simd_lt, simd_mul, simd_ne, + simd_neg, simd_or, simd_reduce_add_ordered, simd_reduce_add_unordered, @@ -1085,6 +1103,7 @@ symbols! { simd_reduce_or, simd_reduce_xor, simd_rem, + simd_round, simd_saturating_add, simd_saturating_sub, simd_scatter, @@ -1093,6 +1112,7 @@ symbols! { simd_shl, simd_shr, simd_sub, + simd_trunc, simd_xor, since, sinf32, @@ -1101,6 +1121,7 @@ symbols! { size_of, size_of_val, sized, + skip, slice, slice_alloc, slice_patterns, @@ -1278,6 +1299,7 @@ symbols! { vreg, vreg_low16, warn, + wasm_abi, wasm_import_module, wasm_target_feature, while_let, diff --git a/compiler/rustc_symbol_mangling/src/legacy.rs b/compiler/rustc_symbol_mangling/src/legacy.rs index b0d5f34090241..7d186c330ba3f 100644 --- a/compiler/rustc_symbol_mangling/src/legacy.rs +++ b/compiler/rustc_symbol_mangling/src/legacy.rs @@ -230,7 +230,7 @@ impl Printer<'tcx> for SymbolPrinter<'tcx> { fn print_dyn_existential( mut self, - predicates: &'tcx ty::List>>, + predicates: &'tcx ty::List>>, ) -> Result { let mut first = true; for p in predicates { diff --git a/compiler/rustc_symbol_mangling/src/lib.rs b/compiler/rustc_symbol_mangling/src/lib.rs index 7f8cded0ac0ec..c050bbc9b9df6 100644 --- a/compiler/rustc_symbol_mangling/src/lib.rs +++ b/compiler/rustc_symbol_mangling/src/lib.rs @@ -90,7 +90,7 @@ #![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")] #![feature(never_type)] #![feature(nll)] -#![feature(or_patterns)] +#![cfg_attr(bootstrap, feature(or_patterns))] #![feature(in_band_lifetimes)] #![recursion_limit = "256"] @@ -198,7 +198,7 @@ fn compute_symbol_name( // // [1]: https://bugs.llvm.org/show_bug.cgi?id=44316 if is_foreign - && (tcx.sess.target.arch != "wasm32" + && (!tcx.sess.target.is_like_wasm || !tcx.wasm_import_module_map(def_id.krate).contains_key(&def_id)) { if let Some(name) = attrs.link_name { diff --git a/compiler/rustc_symbol_mangling/src/v0.rs b/compiler/rustc_symbol_mangling/src/v0.rs index bbf7ecc39cfd9..37a834043f624 100644 --- a/compiler/rustc_symbol_mangling/src/v0.rs +++ b/compiler/rustc_symbol_mangling/src/v0.rs @@ -181,7 +181,7 @@ impl SymbolMangler<'tcx> { fn in_binder( mut self, - value: &ty::Binder, + value: &ty::Binder<'tcx, T>, print_value: impl FnOnce(Self, &T) -> Result, ) -> Result where @@ -318,7 +318,7 @@ impl Printer<'tcx> for SymbolMangler<'tcx> { // Late-bound lifetimes use indices starting at 1, // see `BinderLevel` for more details. - ty::ReLateBound(debruijn, ty::BoundRegion { kind: ty::BrAnon(i) }) => { + ty::ReLateBound(debruijn, ty::BoundRegion { kind: ty::BrAnon(i), .. }) => { let binder = &self.binders[self.binders.len() - 1 - debruijn.index()]; let depth = binder.lifetime_depths.start + i; @@ -440,7 +440,7 @@ impl Printer<'tcx> for SymbolMangler<'tcx> { } match sig.abi { Abi::Rust => {} - Abi::C => cx.push("KC"), + Abi::C { unwind: false } => cx.push("KC"), abi => { cx.push("K"); let name = abi.name(); @@ -483,7 +483,7 @@ impl Printer<'tcx> for SymbolMangler<'tcx> { fn print_dyn_existential( mut self, - predicates: &'tcx ty::List>>, + predicates: &'tcx ty::List>>, ) -> Result { for predicate in predicates { self = self.in_binder(&predicate, |mut cx, predicate| { diff --git a/compiler/rustc_target/README.md b/compiler/rustc_target/README.md index ac1e03385d181..ca72a89da5a3e 100644 --- a/compiler/rustc_target/README.md +++ b/compiler/rustc_target/README.md @@ -1,4 +1,4 @@ -`librustc_target` contains some very low-level details that are +`rustc_target` contains some very low-level details that are specific to different compilation targets and so forth. For more information about how rustc works, see the [rustc dev guide]. diff --git a/compiler/rustc_target/src/abi/call/mod.rs b/compiler/rustc_target/src/abi/call/mod.rs index 0deb1186b0fd6..0cf2441d84e04 100644 --- a/compiler/rustc_target/src/abi/call/mod.rs +++ b/compiler/rustc_target/src/abi/call/mod.rs @@ -18,8 +18,7 @@ mod riscv; mod s390x; mod sparc; mod sparc64; -mod wasm32; -mod wasm32_bindgen_compat; +mod wasm; mod x86; mod x86_64; mod x86_win64; @@ -65,14 +64,17 @@ mod attr_impl { const NoCapture = 1 << 2; const NonNull = 1 << 3; const ReadOnly = 1 << 4; - const InReg = 1 << 8; + const InReg = 1 << 5; + // NoAlias on &mut arguments can only be used with LLVM >= 12 due to miscompiles + // in earlier versions. FIXME: Remove this distinction once possible. + const NoAliasMutRef = 1 << 6; } } } /// Sometimes an ABI requires small integers to be extended to a full or partial register. This enum -/// defines if this extension should be zero-extension or sign-extension when necssary. When it is -/// not necesary to extend the argument, this enum is ignored. +/// defines if this extension should be zero-extension or sign-extension when necessary. When it is +/// not necessary to extend the argument, this enum is ignored. #[derive(Copy, Clone, PartialEq, Eq, Debug)] pub enum ArgExtension { None, @@ -644,11 +646,14 @@ impl<'a, Ty> FnAbi<'a, Ty> { "nvptx64" => nvptx64::compute_abi_info(self), "hexagon" => hexagon::compute_abi_info(self), "riscv32" | "riscv64" => riscv::compute_abi_info(cx, self), - "wasm32" => match cx.target_spec().os.as_str() { - "emscripten" | "wasi" => wasm32::compute_abi_info(cx, self), - _ => wasm32_bindgen_compat::compute_abi_info(self), - }, - "asmjs" => wasm32::compute_abi_info(cx, self), + "wasm32" | "wasm64" => { + if cx.target_spec().adjust_abi(abi) == spec::abi::Abi::Wasm { + wasm::compute_wasm_abi_info(self) + } else { + wasm::compute_c_abi_info(cx, self) + } + } + "asmjs" => wasm::compute_c_abi_info(cx, self), a => return Err(format!("unrecognized arch \"{}\" in target specification", a)), } diff --git a/compiler/rustc_target/src/abi/call/wasm.rs b/compiler/rustc_target/src/abi/call/wasm.rs new file mode 100644 index 0000000000000..bf2c08bb1662d --- /dev/null +++ b/compiler/rustc_target/src/abi/call/wasm.rs @@ -0,0 +1,83 @@ +use crate::abi::call::{ArgAbi, FnAbi, Uniform}; +use crate::abi::{HasDataLayout, LayoutOf, TyAndLayout, TyAndLayoutMethods}; + +fn unwrap_trivial_aggregate<'a, Ty, C>(cx: &C, val: &mut ArgAbi<'a, Ty>) -> bool +where + Ty: TyAndLayoutMethods<'a, C> + Copy, + C: LayoutOf> + HasDataLayout, +{ + if val.layout.is_aggregate() { + if let Some(unit) = val.layout.homogeneous_aggregate(cx).ok().and_then(|ha| ha.unit()) { + let size = val.layout.size; + if unit.size == size { + val.cast_to(Uniform { unit, total: size }); + return true; + } + } + } + false +} + +fn classify_ret<'a, Ty, C>(cx: &C, ret: &mut ArgAbi<'a, Ty>) +where + Ty: TyAndLayoutMethods<'a, C> + Copy, + C: LayoutOf> + HasDataLayout, +{ + ret.extend_integer_width_to(32); + if ret.layout.is_aggregate() && !unwrap_trivial_aggregate(cx, ret) { + ret.make_indirect(); + } +} + +fn classify_arg<'a, Ty, C>(cx: &C, arg: &mut ArgAbi<'a, Ty>) +where + Ty: TyAndLayoutMethods<'a, C> + Copy, + C: LayoutOf> + HasDataLayout, +{ + arg.extend_integer_width_to(32); + if arg.layout.is_aggregate() && !unwrap_trivial_aggregate(cx, arg) { + arg.make_indirect_byval(); + } +} + +/// The purpose of this ABI is to match the C ABI (aka clang) exactly. +pub fn compute_c_abi_info<'a, Ty, C>(cx: &C, fn_abi: &mut FnAbi<'a, Ty>) +where + Ty: TyAndLayoutMethods<'a, C> + Copy, + C: LayoutOf> + HasDataLayout, +{ + if !fn_abi.ret.is_ignore() { + classify_ret(cx, &mut fn_abi.ret); + } + + for arg in &mut fn_abi.args { + if arg.is_ignore() { + continue; + } + classify_arg(cx, arg); + } +} + +/// The purpose of this ABI is for matching the WebAssembly standard. This +/// intentionally diverges from the C ABI and is specifically crafted to take +/// advantage of LLVM's support of multiple returns in WebAssembly. +pub fn compute_wasm_abi_info(fn_abi: &mut FnAbi<'_, Ty>) { + if !fn_abi.ret.is_ignore() { + classify_ret(&mut fn_abi.ret); + } + + for arg in &mut fn_abi.args { + if arg.is_ignore() { + continue; + } + classify_arg(arg); + } + + fn classify_ret(ret: &mut ArgAbi<'_, Ty>) { + ret.extend_integer_width_to(32); + } + + fn classify_arg(arg: &mut ArgAbi<'_, Ty>) { + arg.extend_integer_width_to(32); + } +} diff --git a/compiler/rustc_target/src/abi/call/wasm32.rs b/compiler/rustc_target/src/abi/call/wasm32.rs deleted file mode 100644 index ff2c0e9bb6fcc..0000000000000 --- a/compiler/rustc_target/src/abi/call/wasm32.rs +++ /dev/null @@ -1,58 +0,0 @@ -use crate::abi::call::{ArgAbi, FnAbi, Uniform}; -use crate::abi::{HasDataLayout, LayoutOf, TyAndLayout, TyAndLayoutMethods}; - -fn unwrap_trivial_aggregate<'a, Ty, C>(cx: &C, val: &mut ArgAbi<'a, Ty>) -> bool -where - Ty: TyAndLayoutMethods<'a, C> + Copy, - C: LayoutOf> + HasDataLayout, -{ - if val.layout.is_aggregate() { - if let Some(unit) = val.layout.homogeneous_aggregate(cx).ok().and_then(|ha| ha.unit()) { - let size = val.layout.size; - if unit.size == size { - val.cast_to(Uniform { unit, total: size }); - return true; - } - } - } - false -} - -fn classify_ret<'a, Ty, C>(cx: &C, ret: &mut ArgAbi<'a, Ty>) -where - Ty: TyAndLayoutMethods<'a, C> + Copy, - C: LayoutOf> + HasDataLayout, -{ - ret.extend_integer_width_to(32); - if ret.layout.is_aggregate() && !unwrap_trivial_aggregate(cx, ret) { - ret.make_indirect(); - } -} - -fn classify_arg<'a, Ty, C>(cx: &C, arg: &mut ArgAbi<'a, Ty>) -where - Ty: TyAndLayoutMethods<'a, C> + Copy, - C: LayoutOf> + HasDataLayout, -{ - arg.extend_integer_width_to(32); - if arg.layout.is_aggregate() && !unwrap_trivial_aggregate(cx, arg) { - arg.make_indirect_byval(); - } -} - -pub fn compute_abi_info<'a, Ty, C>(cx: &C, fn_abi: &mut FnAbi<'a, Ty>) -where - Ty: TyAndLayoutMethods<'a, C> + Copy, - C: LayoutOf> + HasDataLayout, -{ - if !fn_abi.ret.is_ignore() { - classify_ret(cx, &mut fn_abi.ret); - } - - for arg in &mut fn_abi.args { - if arg.is_ignore() { - continue; - } - classify_arg(cx, arg); - } -} diff --git a/compiler/rustc_target/src/abi/call/wasm32_bindgen_compat.rs b/compiler/rustc_target/src/abi/call/wasm32_bindgen_compat.rs deleted file mode 100644 index 59571fd9d4821..0000000000000 --- a/compiler/rustc_target/src/abi/call/wasm32_bindgen_compat.rs +++ /dev/null @@ -1,29 +0,0 @@ -// This is not and has never been a correct C ABI for WebAssembly, but -// for a long time this was the C ABI that Rust used. wasm-bindgen -// depends on ABI details for this ABI and is incompatible with the -// correct C ABI, so this ABI is being kept around until wasm-bindgen -// can be fixed to work with the correct ABI. See #63649 for further -// discussion. - -use crate::abi::call::{ArgAbi, FnAbi}; - -fn classify_ret(ret: &mut ArgAbi<'_, Ty>) { - ret.extend_integer_width_to(32); -} - -fn classify_arg(arg: &mut ArgAbi<'_, Ty>) { - arg.extend_integer_width_to(32); -} - -pub fn compute_abi_info(fn_abi: &mut FnAbi<'_, Ty>) { - if !fn_abi.ret.is_ignore() { - classify_ret(&mut fn_abi.ret); - } - - for arg in &mut fn_abi.args { - if arg.is_ignore() { - continue; - } - classify_arg(arg); - } -} diff --git a/compiler/rustc_target/src/abi/mod.rs b/compiler/rustc_target/src/abi/mod.rs index b14b1ef00db91..e2618da749fb3 100644 --- a/compiler/rustc_target/src/abi/mod.rs +++ b/compiler/rustc_target/src/abi/mod.rs @@ -1112,7 +1112,7 @@ pub enum PointerKind { /// `&T` where `T` contains no `UnsafeCell`, is `noalias` and `readonly`. Frozen, - /// `&mut T`, when we know `noalias` is safe for LLVM. + /// `&mut T` which is `noalias` but not `readonly`. UniqueBorrowed, /// `Box`, unlike `UniqueBorrowed`, it also has `noalias` on returns. diff --git a/compiler/rustc_target/src/asm/arm.rs b/compiler/rustc_target/src/asm/arm.rs index 28000916e0c30..a7a708fe7dec3 100644 --- a/compiler/rustc_target/src/asm/arm.rs +++ b/compiler/rustc_target/src/asm/arm.rs @@ -68,7 +68,6 @@ fn frame_pointer_r11( _arch: InlineAsmArch, has_feature: impl FnMut(&str) -> bool, target: &Target, - _allocating: bool, ) -> Result<(), &'static str> { if !frame_pointer_is_r7(has_feature, target) { Err("the frame pointer (r11) cannot be used as an operand for inline asm") @@ -81,7 +80,6 @@ fn frame_pointer_r7( _arch: InlineAsmArch, has_feature: impl FnMut(&str) -> bool, target: &Target, - _allocating: bool, ) -> Result<(), &'static str> { if frame_pointer_is_r7(has_feature, target) { Err("the frame pointer (r7) cannot be used as an operand for inline asm") diff --git a/compiler/rustc_target/src/asm/mod.rs b/compiler/rustc_target/src/asm/mod.rs index dfc0989a9f8df..e2268a61a4257 100644 --- a/compiler/rustc_target/src/asm/mod.rs +++ b/compiler/rustc_target/src/asm/mod.rs @@ -90,7 +90,7 @@ macro_rules! def_regs { match name { $( $($alias)|* | $reg_name => { - $($filter(_arch, &mut _has_feature, _target, false)?;)? + $($filter(_arch, &mut _has_feature, _target)?;)? Ok(Self::$reg) } )* @@ -114,7 +114,7 @@ macro_rules! def_regs { #[allow(unused_imports)] use super::{InlineAsmReg, InlineAsmRegClass}; $( - if $($filter(_arch, &mut _has_feature, _target, true).is_ok() &&)? true { + if $($filter(_arch, &mut _has_feature, _target).is_ok() &&)? true { if let Some(set) = _map.get_mut(&InlineAsmRegClass::$arch($arch_regclass::$class)) { set.insert(InlineAsmReg::$arch($arch_reg::$reg)); } @@ -229,6 +229,8 @@ pub enum InlineAsmReg { Mips(MipsInlineAsmReg), SpirV(SpirVInlineAsmReg), Wasm(WasmInlineAsmReg), + // Placeholder for invalid register constraints for the current target + Err, } impl InlineAsmReg { @@ -240,6 +242,7 @@ impl InlineAsmReg { Self::RiscV(r) => r.name(), Self::Hexagon(r) => r.name(), Self::Mips(r) => r.name(), + Self::Err => "", } } @@ -251,6 +254,7 @@ impl InlineAsmReg { Self::RiscV(r) => InlineAsmRegClass::RiscV(r.reg_class()), Self::Hexagon(r) => InlineAsmRegClass::Hexagon(r.reg_class()), Self::Mips(r) => InlineAsmRegClass::Mips(r.reg_class()), + Self::Err => InlineAsmRegClass::Err, } } @@ -309,6 +313,7 @@ impl InlineAsmReg { Self::RiscV(r) => r.emit(out, arch, modifier), Self::Hexagon(r) => r.emit(out, arch, modifier), Self::Mips(r) => r.emit(out, arch, modifier), + Self::Err => unreachable!("Use of InlineAsmReg::Err"), } } @@ -320,6 +325,7 @@ impl InlineAsmReg { Self::RiscV(_) => cb(self), Self::Hexagon(r) => r.overlapping_regs(|r| cb(Self::Hexagon(r))), Self::Mips(_) => cb(self), + Self::Err => unreachable!("Use of InlineAsmReg::Err"), } } } @@ -346,6 +352,8 @@ pub enum InlineAsmRegClass { Mips(MipsInlineAsmRegClass), SpirV(SpirVInlineAsmRegClass), Wasm(WasmInlineAsmRegClass), + // Placeholder for invalid register constraints for the current target + Err, } impl InlineAsmRegClass { @@ -360,6 +368,7 @@ impl InlineAsmRegClass { Self::Mips(r) => r.name(), Self::SpirV(r) => r.name(), Self::Wasm(r) => r.name(), + Self::Err => rustc_span::symbol::sym::reg, } } @@ -377,6 +386,7 @@ impl InlineAsmRegClass { Self::Mips(r) => r.suggest_class(arch, ty).map(InlineAsmRegClass::Mips), Self::SpirV(r) => r.suggest_class(arch, ty).map(InlineAsmRegClass::SpirV), Self::Wasm(r) => r.suggest_class(arch, ty).map(InlineAsmRegClass::Wasm), + Self::Err => unreachable!("Use of InlineAsmRegClass::Err"), } } @@ -401,6 +411,7 @@ impl InlineAsmRegClass { Self::Mips(r) => r.suggest_modifier(arch, ty), Self::SpirV(r) => r.suggest_modifier(arch, ty), Self::Wasm(r) => r.suggest_modifier(arch, ty), + Self::Err => unreachable!("Use of InlineAsmRegClass::Err"), } } @@ -421,6 +432,7 @@ impl InlineAsmRegClass { Self::Mips(r) => r.default_modifier(arch), Self::SpirV(r) => r.default_modifier(arch), Self::Wasm(r) => r.default_modifier(arch), + Self::Err => unreachable!("Use of InlineAsmRegClass::Err"), } } @@ -440,6 +452,7 @@ impl InlineAsmRegClass { Self::Mips(r) => r.supported_types(arch), Self::SpirV(r) => r.supported_types(arch), Self::Wasm(r) => r.supported_types(arch), + Self::Err => unreachable!("Use of InlineAsmRegClass::Err"), } } @@ -476,6 +489,7 @@ impl InlineAsmRegClass { Self::Mips(r) => r.valid_modifiers(arch), Self::SpirV(r) => r.valid_modifiers(arch), Self::Wasm(r) => r.valid_modifiers(arch), + Self::Err => unreachable!("Use of InlineAsmRegClass::Err"), } } } diff --git a/compiler/rustc_target/src/asm/riscv.rs b/compiler/rustc_target/src/asm/riscv.rs index ced7483b00571..185d6ac8246c9 100644 --- a/compiler/rustc_target/src/asm/riscv.rs +++ b/compiler/rustc_target/src/asm/riscv.rs @@ -52,7 +52,6 @@ fn not_e( _arch: InlineAsmArch, mut has_feature: impl FnMut(&str) -> bool, _target: &Target, - _allocating: bool, ) -> Result<(), &'static str> { if has_feature("e") { Err("register can't be used with the `e` target feature") diff --git a/compiler/rustc_target/src/asm/x86.rs b/compiler/rustc_target/src/asm/x86.rs index 0f62c19e1a3cd..90660dad4c2a1 100644 --- a/compiler/rustc_target/src/asm/x86.rs +++ b/compiler/rustc_target/src/asm/x86.rs @@ -133,7 +133,6 @@ fn x86_64_only( arch: InlineAsmArch, _has_feature: impl FnMut(&str) -> bool, _target: &Target, - _allocating: bool, ) -> Result<(), &'static str> { match arch { InlineAsmArch::X86 => Err("register is only available on x86_64"), @@ -146,13 +145,9 @@ fn high_byte( arch: InlineAsmArch, _has_feature: impl FnMut(&str) -> bool, _target: &Target, - allocating: bool, ) -> Result<(), &'static str> { match arch { - InlineAsmArch::X86_64 if allocating => { - // The error message isn't actually used... - Err("high byte registers are not allocated by reg_byte") - } + InlineAsmArch::X86_64 => Err("high byte registers cannot be used as an operand on x86_64"), _ => Ok(()), } } diff --git a/compiler/rustc_target/src/lib.rs b/compiler/rustc_target/src/lib.rs index fb747dfcbd337..b54764b9e3239 100644 --- a/compiler/rustc_target/src/lib.rs +++ b/compiler/rustc_target/src/lib.rs @@ -28,5 +28,5 @@ pub mod spec; /// Requirements for a `StableHashingContext` to be used in this crate. /// This is a hack to allow using the `HashStable_Generic` derive macro -/// instead of implementing everything in librustc_middle. +/// instead of implementing everything in `rustc_middle`. pub trait HashStableContext {} diff --git a/compiler/rustc_target/src/spec/aarch64_apple_darwin.rs b/compiler/rustc_target/src/spec/aarch64_apple_darwin.rs index 7de809f76222d..feadd4e891cec 100644 --- a/compiler/rustc_target/src/spec/aarch64_apple_darwin.rs +++ b/compiler/rustc_target/src/spec/aarch64_apple_darwin.rs @@ -1,11 +1,12 @@ -use crate::spec::{LinkerFlavor, Target, TargetOptions}; +use crate::spec::{LinkerFlavor, SanitizerSet, Target, TargetOptions}; pub fn target() -> Target { let mut base = super::apple_base::opts("macos"); base.cpu = "apple-a12".to_string(); base.max_atomic_width = Some(128); - base.pre_link_args.insert(LinkerFlavor::Gcc, vec!["-arch".to_string(), "arm64".to_string()]); + base.supported_sanitizers = SanitizerSet::ADDRESS | SanitizerSet::LEAK | SanitizerSet::THREAD; + base.pre_link_args.insert(LinkerFlavor::Gcc, vec!["-arch".to_string(), "arm64".to_string()]); base.link_env_remove.extend(super::apple_base::macos_link_env_remove()); // Clang automatically chooses a more specific target based on diff --git a/compiler/rustc_target/src/spec/aarch64_fuchsia.rs b/compiler/rustc_target/src/spec/aarch64_fuchsia.rs index 1252741f9797d..c9cb21f1eb1e9 100644 --- a/compiler/rustc_target/src/spec/aarch64_fuchsia.rs +++ b/compiler/rustc_target/src/spec/aarch64_fuchsia.rs @@ -1,8 +1,9 @@ -use crate::spec::{Target, TargetOptions}; +use crate::spec::{SanitizerSet, Target, TargetOptions}; pub fn target() -> Target { let mut base = super::fuchsia_base::opts(); base.max_atomic_width = Some(128); + base.supported_sanitizers = SanitizerSet::ADDRESS; Target { llvm_target: "aarch64-fuchsia".to_string(), diff --git a/compiler/rustc_target/src/spec/aarch64_linux_android.rs b/compiler/rustc_target/src/spec/aarch64_linux_android.rs index fa6108df206b4..eaf3a2dbcf8c2 100644 --- a/compiler/rustc_target/src/spec/aarch64_linux_android.rs +++ b/compiler/rustc_target/src/spec/aarch64_linux_android.rs @@ -1,4 +1,4 @@ -use crate::spec::{Target, TargetOptions}; +use crate::spec::{SanitizerSet, Target, TargetOptions}; // See https://developer.android.com/ndk/guides/abis.html#arm64-v8a // for target ABI requirements. @@ -9,6 +9,7 @@ pub fn target() -> Target { // As documented in http://developer.android.com/ndk/guides/cpu-features.html // the neon (ASIMD) and FP must exist on all android aarch64 targets. base.features = "+neon,+fp-armv8".to_string(); + base.supported_sanitizers = SanitizerSet::HWADDRESS; Target { llvm_target: "aarch64-linux-android".to_string(), pointer_width: 64, diff --git a/compiler/rustc_target/src/spec/aarch64_unknown_linux_gnu.rs b/compiler/rustc_target/src/spec/aarch64_unknown_linux_gnu.rs index 58c72af4e769c..a07cd7db8897d 100644 --- a/compiler/rustc_target/src/spec/aarch64_unknown_linux_gnu.rs +++ b/compiler/rustc_target/src/spec/aarch64_unknown_linux_gnu.rs @@ -1,8 +1,13 @@ -use crate::spec::{Target, TargetOptions}; +use crate::spec::{SanitizerSet, Target, TargetOptions}; pub fn target() -> Target { let mut base = super::linux_gnu_base::opts(); base.max_atomic_width = Some(128); + base.supported_sanitizers = SanitizerSet::ADDRESS + | SanitizerSet::LEAK + | SanitizerSet::MEMORY + | SanitizerSet::THREAD + | SanitizerSet::HWADDRESS; Target { llvm_target: "aarch64-unknown-linux-gnu".to_string(), diff --git a/compiler/rustc_target/src/spec/abi.rs b/compiler/rustc_target/src/spec/abi.rs index 65e8a4e8db2ad..a026a623f7866 100644 --- a/compiler/rustc_target/src/spec/abi.rs +++ b/compiler/rustc_target/src/spec/abi.rs @@ -8,24 +8,21 @@ mod tests; #[derive(PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy, Debug)] #[derive(HashStable_Generic, Encodable, Decodable)] pub enum Abi { - // N.B., this ordering MUST match the AbiDatas array below. - // (This is ensured by the test indices_are_correct().) - // Multiplatform / generic ABIs // // These ABIs come first because every time we add a new ABI, we // have to re-bless all the hashing tests. These are used in many // places, so giving them stable values reduces test churn. The // specific values are meaningless. - Rust = 0, - C = 1, + Rust, + C { unwind: bool }, // Single platform ABIs Cdecl, - Stdcall, + Stdcall { unwind: bool }, Fastcall, Vectorcall, - Thiscall, + Thiscall { unwind: bool }, Aapcs, Win64, SysV64, @@ -37,9 +34,10 @@ pub enum Abi { AvrInterrupt, AvrNonBlockingInterrupt, CCmseNonSecureCall, + Wasm, // Multiplatform / generic ABIs - System, + System { unwind: bool }, RustIntrinsic, RustCall, PlatformIntrinsic, @@ -61,13 +59,16 @@ pub struct AbiData { const AbiDatas: &[AbiData] = &[ // Cross-platform ABIs AbiData { abi: Abi::Rust, name: "Rust", generic: true }, - AbiData { abi: Abi::C, name: "C", generic: true }, + AbiData { abi: Abi::C { unwind: false }, name: "C", generic: true }, + AbiData { abi: Abi::C { unwind: true }, name: "C-unwind", generic: true }, // Platform-specific ABIs AbiData { abi: Abi::Cdecl, name: "cdecl", generic: false }, - AbiData { abi: Abi::Stdcall, name: "stdcall", generic: false }, + AbiData { abi: Abi::Stdcall { unwind: false }, name: "stdcall", generic: false }, + AbiData { abi: Abi::Stdcall { unwind: true }, name: "stdcall-unwind", generic: false }, AbiData { abi: Abi::Fastcall, name: "fastcall", generic: false }, AbiData { abi: Abi::Vectorcall, name: "vectorcall", generic: false }, - AbiData { abi: Abi::Thiscall, name: "thiscall", generic: false }, + AbiData { abi: Abi::Thiscall { unwind: false }, name: "thiscall", generic: false }, + AbiData { abi: Abi::Thiscall { unwind: true }, name: "thiscall-unwind", generic: false }, AbiData { abi: Abi::Aapcs, name: "aapcs", generic: false }, AbiData { abi: Abi::Win64, name: "win64", generic: false }, AbiData { abi: Abi::SysV64, name: "sysv64", generic: false }, @@ -83,8 +84,10 @@ const AbiDatas: &[AbiData] = &[ generic: false, }, AbiData { abi: Abi::CCmseNonSecureCall, name: "C-cmse-nonsecure-call", generic: false }, + AbiData { abi: Abi::Wasm, name: "wasm", generic: false }, // Cross-platform ABIs - AbiData { abi: Abi::System, name: "system", generic: true }, + AbiData { abi: Abi::System { unwind: false }, name: "system", generic: true }, + AbiData { abi: Abi::System { unwind: true }, name: "system-unwind", generic: true }, AbiData { abi: Abi::RustIntrinsic, name: "rust-intrinsic", generic: true }, AbiData { abi: Abi::RustCall, name: "rust-call", generic: true }, AbiData { abi: Abi::PlatformIntrinsic, name: "platform-intrinsic", generic: true }, @@ -103,7 +106,53 @@ pub fn all_names() -> Vec<&'static str> { impl Abi { #[inline] pub fn index(self) -> usize { - self as usize + // N.B., this ordering MUST match the AbiDatas array above. + // (This is ensured by the test indices_are_correct().) + use Abi::*; + let i = match self { + // Cross-platform ABIs + Rust => 0, + C { unwind: false } => 1, + C { unwind: true } => 2, + // Platform-specific ABIs + Cdecl => 3, + Stdcall { unwind: false } => 4, + Stdcall { unwind: true } => 5, + Fastcall => 6, + Vectorcall => 7, + Thiscall { unwind: false } => 8, + Thiscall { unwind: true } => 9, + Aapcs => 10, + Win64 => 11, + SysV64 => 12, + PtxKernel => 13, + Msp430Interrupt => 14, + X86Interrupt => 15, + AmdGpuKernel => 16, + EfiApi => 17, + AvrInterrupt => 18, + AvrNonBlockingInterrupt => 19, + CCmseNonSecureCall => 20, + Wasm => 21, + // Cross-platform ABIs + System { unwind: false } => 22, + System { unwind: true } => 23, + RustIntrinsic => 24, + RustCall => 25, + PlatformIntrinsic => 26, + Unadjusted => 27, + }; + debug_assert!( + AbiDatas + .iter() + .enumerate() + .find(|(_, AbiData { abi, .. })| *abi == self) + .map(|(index, _)| index) + .expect("abi variant has associated data") + == i, + "Abi index did not match `AbiDatas` ordering" + ); + i } #[inline] @@ -122,6 +171,8 @@ impl Abi { impl fmt::Display for Abi { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "\"{}\"", self.name()) + match self { + abi => write!(f, "\"{}\"", abi.name()), + } } } diff --git a/compiler/rustc_target/src/spec/android_base.rs b/compiler/rustc_target/src/spec/android_base.rs index f6fbe7cd5f622..aaf81648c51b3 100644 --- a/compiler/rustc_target/src/spec/android_base.rs +++ b/compiler/rustc_target/src/spec/android_base.rs @@ -6,13 +6,16 @@ pub fn opts() -> TargetOptions { // Many of the symbols defined in compiler-rt are also defined in libgcc. // Android's linker doesn't like that by default. base.pre_link_args - .get_mut(&LinkerFlavor::Gcc) - .unwrap() + .entry(LinkerFlavor::Gcc) + .or_default() .push("-Wl,--allow-multiple-definition".to_string()); base.dwarf_version = Some(2); base.position_independent_executables = true; base.has_elf_tls = false; - base.requires_uwtable = true; + // This is for backward compatibility, see https://github.com/rust-lang/rust/issues/49867 + // for context. (At that time, there was no `-C force-unwind-tables`, so the only solution + // was to always emit `uwtable`). + base.default_uwtable = true; base.crt_static_respected = false; base } diff --git a/compiler/rustc_target/src/spec/apple_base.rs b/compiler/rustc_target/src/spec/apple_base.rs index 23f1357af163f..6fa0b34545097 100644 --- a/compiler/rustc_target/src/spec/apple_base.rs +++ b/compiler/rustc_target/src/spec/apple_base.rs @@ -1,6 +1,6 @@ use std::env; -use crate::spec::{LinkArgs, SplitDebuginfo, TargetOptions}; +use crate::spec::{SplitDebuginfo, TargetOptions}; pub fn opts(os: &str) -> TargetOptions { // ELF TLS is only available in macOS 10.7+. If you try to compile for 10.6 @@ -27,10 +27,8 @@ pub fn opts(os: &str) -> TargetOptions { is_like_osx: true, dwarf_version: Some(2), has_rpath: true, - dll_prefix: "lib".to_string(), dll_suffix: ".dylib".to_string(), archive_format: "darwin".to_string(), - pre_link_args: LinkArgs::new(), has_elf_tls: version >= (10, 7), abi_return_struct_as_int: true, emit_debug_gdb_scripts: false, diff --git a/compiler/rustc_target/src/spec/arm_base.rs b/compiler/rustc_target/src/spec/arm_base.rs index b74d80dc6bb2b..01f573313c97f 100644 --- a/compiler/rustc_target/src/spec/arm_base.rs +++ b/compiler/rustc_target/src/spec/arm_base.rs @@ -2,5 +2,14 @@ use crate::spec::abi::Abi; // All the calling conventions trigger an assertion(Unsupported calling convention) in llvm on arm pub fn unsupported_abis() -> Vec { - vec![Abi::Stdcall, Abi::Fastcall, Abi::Vectorcall, Abi::Thiscall, Abi::Win64, Abi::SysV64] + vec![ + Abi::Stdcall { unwind: false }, + Abi::Stdcall { unwind: true }, + Abi::Fastcall, + Abi::Vectorcall, + Abi::Thiscall { unwind: false }, + Abi::Thiscall { unwind: true }, + Abi::Win64, + Abi::SysV64, + ] } diff --git a/compiler/rustc_target/src/spec/armv7_linux_androideabi.rs b/compiler/rustc_target/src/spec/armv7_linux_androideabi.rs index 9aa378a8018fc..02a1191463e3c 100644 --- a/compiler/rustc_target/src/spec/armv7_linux_androideabi.rs +++ b/compiler/rustc_target/src/spec/armv7_linux_androideabi.rs @@ -12,7 +12,7 @@ pub fn target() -> Target { let mut base = super::android_base::opts(); base.features = "+v7,+thumb-mode,+thumb2,+vfp3,-d32,-neon".to_string(); base.max_atomic_width = Some(64); - base.pre_link_args.get_mut(&LinkerFlavor::Gcc).unwrap().push("-march=armv7-a".to_string()); + base.pre_link_args.entry(LinkerFlavor::Gcc).or_default().push("-march=armv7-a".to_string()); Target { llvm_target: "armv7-none-linux-android".to_string(), diff --git a/compiler/rustc_target/src/spec/armv7_unknown_linux_gnueabi.rs b/compiler/rustc_target/src/spec/armv7_unknown_linux_gnueabi.rs index ae6b8286f085f..f6fe88de37cf6 100644 --- a/compiler/rustc_target/src/spec/armv7_unknown_linux_gnueabi.rs +++ b/compiler/rustc_target/src/spec/armv7_unknown_linux_gnueabi.rs @@ -13,7 +13,6 @@ pub fn target() -> Target { options: TargetOptions { features: "+v7,+thumb2,+soft-float,-neon".to_string(), - cpu: "generic".to_string(), max_atomic_width: Some(64), unsupported_abis: super::arm_base::unsupported_abis(), mcount: "\u{1}__gnu_mcount_nc".to_string(), diff --git a/compiler/rustc_target/src/spec/armv7_unknown_linux_gnueabihf.rs b/compiler/rustc_target/src/spec/armv7_unknown_linux_gnueabihf.rs index 48c16b620fd69..5f0f47dd39776 100644 --- a/compiler/rustc_target/src/spec/armv7_unknown_linux_gnueabihf.rs +++ b/compiler/rustc_target/src/spec/armv7_unknown_linux_gnueabihf.rs @@ -14,7 +14,6 @@ pub fn target() -> Target { options: TargetOptions { // Info about features at https://wiki.debian.org/ArmHardFloatPort features: "+v7,+vfp3,-d32,+thumb2,-neon".to_string(), - cpu: "generic".to_string(), max_atomic_width: Some(64), unsupported_abis: super::arm_base::unsupported_abis(), mcount: "\u{1}__gnu_mcount_nc".to_string(), diff --git a/compiler/rustc_target/src/spec/armv7_unknown_linux_musleabi.rs b/compiler/rustc_target/src/spec/armv7_unknown_linux_musleabi.rs index 9f9f1bd79b0c1..c888fc2d4a389 100644 --- a/compiler/rustc_target/src/spec/armv7_unknown_linux_musleabi.rs +++ b/compiler/rustc_target/src/spec/armv7_unknown_linux_musleabi.rs @@ -18,7 +18,6 @@ pub fn target() -> Target { options: TargetOptions { features: "+v7,+thumb2,+soft-float,-neon".to_string(), - cpu: "generic".to_string(), max_atomic_width: Some(64), unsupported_abis: super::arm_base::unsupported_abis(), mcount: "\u{1}mcount".to_string(), diff --git a/compiler/rustc_target/src/spec/armv7_unknown_linux_musleabihf.rs b/compiler/rustc_target/src/spec/armv7_unknown_linux_musleabihf.rs index 59deee30ef260..2432ea519a8ec 100644 --- a/compiler/rustc_target/src/spec/armv7_unknown_linux_musleabihf.rs +++ b/compiler/rustc_target/src/spec/armv7_unknown_linux_musleabihf.rs @@ -17,7 +17,6 @@ pub fn target() -> Target { // target. options: TargetOptions { features: "+v7,+vfp3,-d32,+thumb2,-neon".to_string(), - cpu: "generic".to_string(), max_atomic_width: Some(64), unsupported_abis: super::arm_base::unsupported_abis(), mcount: "\u{1}mcount".to_string(), diff --git a/compiler/rustc_target/src/spec/armv7_unknown_netbsd_eabihf.rs b/compiler/rustc_target/src/spec/armv7_unknown_netbsd_eabihf.rs index 660525704c1b0..4fae3a8d0bf45 100644 --- a/compiler/rustc_target/src/spec/armv7_unknown_netbsd_eabihf.rs +++ b/compiler/rustc_target/src/spec/armv7_unknown_netbsd_eabihf.rs @@ -11,7 +11,6 @@ pub fn target() -> Target { options: TargetOptions { env: "eabihf".to_string(), features: "+v7,+vfp3,-d32,+thumb2,-neon".to_string(), - cpu: "generic".to_string(), max_atomic_width: Some(64), unsupported_abis: super::arm_base::unsupported_abis(), mcount: "__mcount".to_string(), diff --git a/compiler/rustc_target/src/spec/armv7_wrs_vxworks_eabihf.rs b/compiler/rustc_target/src/spec/armv7_wrs_vxworks_eabihf.rs index 6a43054067fe5..9fe7098a85f00 100644 --- a/compiler/rustc_target/src/spec/armv7_wrs_vxworks_eabihf.rs +++ b/compiler/rustc_target/src/spec/armv7_wrs_vxworks_eabihf.rs @@ -10,7 +10,6 @@ pub fn target() -> Target { options: TargetOptions { // Info about features at https://wiki.debian.org/ArmHardFloatPort features: "+v7,+vfp3,-d32,+thumb2,-neon".to_string(), - cpu: "generic".to_string(), max_atomic_width: Some(64), unsupported_abis: super::arm_base::unsupported_abis(), ..base diff --git a/compiler/rustc_target/src/spec/avr_gnu_base.rs b/compiler/rustc_target/src/spec/avr_gnu_base.rs index 67a7684da2c70..69ccce875ab0c 100644 --- a/compiler/rustc_target/src/spec/avr_gnu_base.rs +++ b/compiler/rustc_target/src/spec/avr_gnu_base.rs @@ -15,28 +15,12 @@ pub fn target(target_cpu: String) -> Target { exe_suffix: ".elf".to_string(), linker: Some("avr-gcc".to_owned()), - dynamic_linking: false, executables: true, linker_is_gnu: true, - has_rpath: false, - position_independent_executables: false, eh_frame_header: false, - pre_link_args: vec![( - LinkerFlavor::Gcc, - vec![ - format!("-mmcu={}", target_cpu), - // We want to be able to strip as much executable code as possible - // from the linker command line, and this flag indicates to the - // linker that it can avoid linking in dynamic libraries that don't - // actually satisfy any symbols up to that point (as with many other - // resolutions the linker does). This option only applies to all - // following libraries so we're sure to pass it as one of the first - // arguments. - "-Wl,--as-needed".to_string(), - ], - )] - .into_iter() - .collect(), + pre_link_args: vec![(LinkerFlavor::Gcc, vec![format!("-mmcu={}", target_cpu)])] + .into_iter() + .collect(), late_link_args: vec![(LinkerFlavor::Gcc, vec!["-lgcc".to_owned()])] .into_iter() .collect(), diff --git a/compiler/rustc_target/src/spec/crt_objects.rs b/compiler/rustc_target/src/spec/crt_objects.rs index 51a48147e6b62..2fc9ab29f92f4 100644 --- a/compiler/rustc_target/src/spec/crt_objects.rs +++ b/compiler/rustc_target/src/spec/crt_objects.rs @@ -108,11 +108,13 @@ pub(super) fn post_mingw() -> CrtObjects { } pub(super) fn pre_wasi_fallback() -> CrtObjects { + // Use crt1-command.o instead of crt1.o to enable support for new-style + // commands. See https://reviews.llvm.org/D81689 for more info. new(&[ - (LinkOutputKind::DynamicNoPicExe, &["crt1.o"]), - (LinkOutputKind::DynamicPicExe, &["crt1.o"]), - (LinkOutputKind::StaticNoPicExe, &["crt1.o"]), - (LinkOutputKind::StaticPicExe, &["crt1.o"]), + (LinkOutputKind::DynamicNoPicExe, &["crt1-command.o"]), + (LinkOutputKind::DynamicPicExe, &["crt1-command.o"]), + (LinkOutputKind::StaticNoPicExe, &["crt1-command.o"]), + (LinkOutputKind::StaticPicExe, &["crt1-command.o"]), (LinkOutputKind::WasiReactorExe, &["crt1-reactor.o"]), ]) } diff --git a/compiler/rustc_target/src/spec/dragonfly_base.rs b/compiler/rustc_target/src/spec/dragonfly_base.rs index b96de7ab1ed19..dd0170987824a 100644 --- a/compiler/rustc_target/src/spec/dragonfly_base.rs +++ b/compiler/rustc_target/src/spec/dragonfly_base.rs @@ -1,20 +1,6 @@ -use crate::spec::{LinkArgs, LinkerFlavor, RelroLevel, TargetOptions}; +use crate::spec::{RelroLevel, TargetOptions}; pub fn opts() -> TargetOptions { - let mut args = LinkArgs::new(); - args.insert( - LinkerFlavor::Gcc, - vec![ - // GNU-style linkers will use this to omit linking to libraries - // which don't actually fulfill any relocations, but only for - // libraries which follow this flag. Thus, use it before - // specifying libraries to link to. - "-Wl,--as-needed".to_string(), - // Always enable NX protection when it is available - "-Wl,-z,noexecstack".to_string(), - ], - ); - TargetOptions { os: "dragonfly".to_string(), dynamic_linking: true, @@ -22,7 +8,6 @@ pub fn opts() -> TargetOptions { os_family: Some("unix".to_string()), linker_is_gnu: true, has_rpath: true, - pre_link_args: args, position_independent_executables: true, relro_level: RelroLevel::Full, dwarf_version: Some(2), diff --git a/compiler/rustc_target/src/spec/freebsd_base.rs b/compiler/rustc_target/src/spec/freebsd_base.rs index c70c492716b30..ad3383cc5f261 100644 --- a/compiler/rustc_target/src/spec/freebsd_base.rs +++ b/compiler/rustc_target/src/spec/freebsd_base.rs @@ -1,20 +1,6 @@ -use crate::spec::{LinkArgs, LinkerFlavor, RelroLevel, TargetOptions}; +use crate::spec::{RelroLevel, TargetOptions}; pub fn opts() -> TargetOptions { - let mut args = LinkArgs::new(); - args.insert( - LinkerFlavor::Gcc, - vec![ - // GNU-style linkers will use this to omit linking to libraries - // which don't actually fulfill any relocations, but only for - // libraries which follow this flag. Thus, use it before - // specifying libraries to link to. - "-Wl,--as-needed".to_string(), - // Always enable NX protection when it is available - "-Wl,-z,noexecstack".to_string(), - ], - ); - TargetOptions { os: "freebsd".to_string(), dynamic_linking: true, @@ -22,7 +8,6 @@ pub fn opts() -> TargetOptions { os_family: Some("unix".to_string()), linker_is_gnu: true, has_rpath: true, - pre_link_args: args, position_independent_executables: true, eliminate_frame_pointer: false, // FIXME 43575 relro_level: RelroLevel::Full, diff --git a/compiler/rustc_target/src/spec/fuchsia_base.rs b/compiler/rustc_target/src/spec/fuchsia_base.rs index 5c39773cbe381..2b925f8b946c4 100644 --- a/compiler/rustc_target/src/spec/fuchsia_base.rs +++ b/compiler/rustc_target/src/spec/fuchsia_base.rs @@ -23,13 +23,11 @@ pub fn opts() -> TargetOptions { os: "fuchsia".to_string(), linker_flavor: LinkerFlavor::Lld(LldFlavor::Ld), linker: Some("rust-lld".to_owned()), - lld_flavor: LldFlavor::Ld, dynamic_linking: true, executables: true, os_family: Some("unix".to_string()), is_like_fuchsia: true, linker_is_gnu: true, - has_rpath: false, pre_link_args, pre_link_objects: crt_objects::new(&[ (LinkOutputKind::DynamicNoPicExe, &["Scrt1.o"]), diff --git a/compiler/rustc_target/src/spec/haiku_base.rs b/compiler/rustc_target/src/spec/haiku_base.rs index ec87645c4faaa..956e4ed4bf9c8 100644 --- a/compiler/rustc_target/src/spec/haiku_base.rs +++ b/compiler/rustc_target/src/spec/haiku_base.rs @@ -5,7 +5,6 @@ pub fn opts() -> TargetOptions { os: "haiku".to_string(), dynamic_linking: true, executables: true, - has_rpath: false, os_family: Some("unix".to_string()), relro_level: RelroLevel::Full, linker_is_gnu: true, diff --git a/compiler/rustc_target/src/spec/hermit_base.rs b/compiler/rustc_target/src/spec/hermit_base.rs index a75158a0ea0cb..ad013047e6a13 100644 --- a/compiler/rustc_target/src/spec/hermit_base.rs +++ b/compiler/rustc_target/src/spec/hermit_base.rs @@ -1,5 +1,4 @@ -use crate::spec::{LinkArgs, LinkerFlavor, LldFlavor, PanicStrategy}; -use crate::spec::{RelocModel, TargetOptions, TlsModel}; +use crate::spec::{LinkArgs, LinkerFlavor, LldFlavor, PanicStrategy, TargetOptions, TlsModel}; pub fn opts() -> TargetOptions { let mut pre_link_args = LinkArgs::new(); @@ -19,8 +18,6 @@ pub fn opts() -> TargetOptions { panic_strategy: PanicStrategy::Abort, position_independent_executables: true, static_position_independent_executables: true, - relocation_model: RelocModel::Pic, - os_family: None, tls_model: TlsModel::InitialExec, ..Default::default() } diff --git a/compiler/rustc_target/src/spec/hermit_kernel_base.rs b/compiler/rustc_target/src/spec/hermit_kernel_base.rs index 622f0d9a47198..6d18a14d6aec0 100644 --- a/compiler/rustc_target/src/spec/hermit_kernel_base.rs +++ b/compiler/rustc_target/src/spec/hermit_kernel_base.rs @@ -1,5 +1,4 @@ -use crate::spec::{LinkArgs, LinkerFlavor, LldFlavor, PanicStrategy}; -use crate::spec::{RelocModel, TargetOptions, TlsModel}; +use crate::spec::{LinkArgs, LinkerFlavor, LldFlavor, PanicStrategy, TargetOptions, TlsModel}; pub fn opts() -> TargetOptions { let mut pre_link_args = LinkArgs::new(); @@ -20,8 +19,6 @@ pub fn opts() -> TargetOptions { panic_strategy: PanicStrategy::Abort, position_independent_executables: true, static_position_independent_executables: true, - relocation_model: RelocModel::Pic, - os_family: None, tls_model: TlsModel::InitialExec, ..Default::default() } diff --git a/compiler/rustc_target/src/spec/hexagon_unknown_linux_musl.rs b/compiler/rustc_target/src/spec/hexagon_unknown_linux_musl.rs index 73d5e2057f955..e0097ee220a46 100644 --- a/compiler/rustc_target/src/spec/hexagon_unknown_linux_musl.rs +++ b/compiler/rustc_target/src/spec/hexagon_unknown_linux_musl.rs @@ -1,4 +1,4 @@ -use crate::spec::{LinkArgs, Target}; +use crate::spec::Target; pub fn target() -> Target { let mut base = super::linux_musl_base::opts(); @@ -8,15 +8,11 @@ pub fn target() -> Target { base.features = "-small-data,+hvx-length128b".to_string(); base.crt_static_default = false; - base.atomic_cas = true; base.has_rpath = true; base.linker_is_gnu = false; base.dynamic_linking = true; base.executables = true; - base.pre_link_args = LinkArgs::new(); - base.post_link_args = LinkArgs::new(); - Target { llvm_target: "hexagon-unknown-linux-musl".to_string(), pointer_width: 32, diff --git a/compiler/rustc_target/src/spec/i686_pc_windows_gnu.rs b/compiler/rustc_target/src/spec/i686_pc_windows_gnu.rs index 4979a5b3bc848..7002d88c512d7 100644 --- a/compiler/rustc_target/src/spec/i686_pc_windows_gnu.rs +++ b/compiler/rustc_target/src/spec/i686_pc_windows_gnu.rs @@ -12,8 +12,8 @@ pub fn target() -> Target { // Mark all dynamic libraries and executables as compatible with the larger 4GiB address // space available to x86 Windows binaries on x86_64. base.pre_link_args - .get_mut(&LinkerFlavor::Gcc) - .unwrap() + .entry(LinkerFlavor::Gcc) + .or_default() .push("-Wl,--large-address-aware".to_string()); Target { diff --git a/compiler/rustc_target/src/spec/i686_pc_windows_msvc.rs b/compiler/rustc_target/src/spec/i686_pc_windows_msvc.rs index e7a5643eaaa24..74074cfb5dda1 100644 --- a/compiler/rustc_target/src/spec/i686_pc_windows_msvc.rs +++ b/compiler/rustc_target/src/spec/i686_pc_windows_msvc.rs @@ -14,10 +14,10 @@ pub fn target() -> Target { // https://docs.microsoft.com/en-us/cpp/build/reference/safeseh-image-has-safe-exception-handlers "/SAFESEH".to_string(), ]; - base.pre_link_args.get_mut(&LinkerFlavor::Msvc).unwrap().extend(pre_link_args_msvc.clone()); + base.pre_link_args.entry(LinkerFlavor::Msvc).or_default().extend(pre_link_args_msvc.clone()); base.pre_link_args - .get_mut(&LinkerFlavor::Lld(LldFlavor::Link)) - .unwrap() + .entry(LinkerFlavor::Lld(LldFlavor::Link)) + .or_default() .extend(pre_link_args_msvc); Target { diff --git a/compiler/rustc_target/src/spec/i686_unknown_freebsd.rs b/compiler/rustc_target/src/spec/i686_unknown_freebsd.rs index fc425babb6948..a26cabdc90adc 100644 --- a/compiler/rustc_target/src/spec/i686_unknown_freebsd.rs +++ b/compiler/rustc_target/src/spec/i686_unknown_freebsd.rs @@ -4,7 +4,7 @@ pub fn target() -> Target { let mut base = super::freebsd_base::opts(); base.cpu = "pentium4".to_string(); base.max_atomic_width = Some(64); - let pre_link_args = base.pre_link_args.get_mut(&LinkerFlavor::Gcc).unwrap(); + let pre_link_args = base.pre_link_args.entry(LinkerFlavor::Gcc).or_default(); pre_link_args.push("-m32".to_string()); pre_link_args.push("-Wl,-znotext".to_string()); base.stack_probes = StackProbeType::InlineOrCall { min_llvm_version_for_inline: (11, 0, 1) }; diff --git a/compiler/rustc_target/src/spec/i686_unknown_linux_gnu.rs b/compiler/rustc_target/src/spec/i686_unknown_linux_gnu.rs index fe1e6a4299dab..633e8da0ccbc4 100644 --- a/compiler/rustc_target/src/spec/i686_unknown_linux_gnu.rs +++ b/compiler/rustc_target/src/spec/i686_unknown_linux_gnu.rs @@ -4,7 +4,7 @@ pub fn target() -> Target { let mut base = super::linux_gnu_base::opts(); base.cpu = "pentium4".to_string(); base.max_atomic_width = Some(64); - base.pre_link_args.get_mut(&LinkerFlavor::Gcc).unwrap().push("-m32".to_string()); + base.pre_link_args.entry(LinkerFlavor::Gcc).or_default().push("-m32".to_string()); base.stack_probes = StackProbeType::InlineOrCall { min_llvm_version_for_inline: (11, 0, 1) }; Target { diff --git a/compiler/rustc_target/src/spec/i686_unknown_linux_musl.rs b/compiler/rustc_target/src/spec/i686_unknown_linux_musl.rs index 623fd1b9ae801..8bcd261e4dfad 100644 --- a/compiler/rustc_target/src/spec/i686_unknown_linux_musl.rs +++ b/compiler/rustc_target/src/spec/i686_unknown_linux_musl.rs @@ -4,8 +4,8 @@ pub fn target() -> Target { let mut base = super::linux_musl_base::opts(); base.cpu = "pentium4".to_string(); base.max_atomic_width = Some(64); - base.pre_link_args.get_mut(&LinkerFlavor::Gcc).unwrap().push("-m32".to_string()); - base.pre_link_args.get_mut(&LinkerFlavor::Gcc).unwrap().push("-Wl,-melf_i386".to_string()); + base.pre_link_args.entry(LinkerFlavor::Gcc).or_default().push("-m32".to_string()); + base.pre_link_args.entry(LinkerFlavor::Gcc).or_default().push("-Wl,-melf_i386".to_string()); base.stack_probes = StackProbeType::InlineOrCall { min_llvm_version_for_inline: (11, 0, 1) }; // The unwinder used by i686-unknown-linux-musl, the LLVM libunwind diff --git a/compiler/rustc_target/src/spec/i686_unknown_netbsd.rs b/compiler/rustc_target/src/spec/i686_unknown_netbsd.rs index c4d11bfb13ece..e020264ad7af2 100644 --- a/compiler/rustc_target/src/spec/i686_unknown_netbsd.rs +++ b/compiler/rustc_target/src/spec/i686_unknown_netbsd.rs @@ -4,7 +4,7 @@ pub fn target() -> Target { let mut base = super::netbsd_base::opts(); base.cpu = "pentium4".to_string(); base.max_atomic_width = Some(64); - base.pre_link_args.get_mut(&LinkerFlavor::Gcc).unwrap().push("-m32".to_string()); + base.pre_link_args.entry(LinkerFlavor::Gcc).or_default().push("-m32".to_string()); base.stack_probes = StackProbeType::InlineOrCall { min_llvm_version_for_inline: (11, 0, 1) }; Target { diff --git a/compiler/rustc_target/src/spec/i686_unknown_openbsd.rs b/compiler/rustc_target/src/spec/i686_unknown_openbsd.rs index fdaaf6c741e89..86448cb911547 100644 --- a/compiler/rustc_target/src/spec/i686_unknown_openbsd.rs +++ b/compiler/rustc_target/src/spec/i686_unknown_openbsd.rs @@ -4,8 +4,8 @@ pub fn target() -> Target { let mut base = super::openbsd_base::opts(); base.cpu = "pentium4".to_string(); base.max_atomic_width = Some(64); - base.pre_link_args.get_mut(&LinkerFlavor::Gcc).unwrap().push("-m32".to_string()); - base.pre_link_args.get_mut(&LinkerFlavor::Gcc).unwrap().push("-fuse-ld=lld".to_string()); + base.pre_link_args.entry(LinkerFlavor::Gcc).or_default().push("-m32".to_string()); + base.pre_link_args.entry(LinkerFlavor::Gcc).or_default().push("-fuse-ld=lld".to_string()); base.stack_probes = StackProbeType::InlineOrCall { min_llvm_version_for_inline: (11, 0, 1) }; Target { diff --git a/compiler/rustc_target/src/spec/i686_uwp_windows_gnu.rs b/compiler/rustc_target/src/spec/i686_uwp_windows_gnu.rs index a3de93efb7820..426df59882d58 100644 --- a/compiler/rustc_target/src/spec/i686_uwp_windows_gnu.rs +++ b/compiler/rustc_target/src/spec/i686_uwp_windows_gnu.rs @@ -11,8 +11,8 @@ pub fn target() -> Target { // Mark all dynamic libraries and executables as compatible with the larger 4GiB address // space available to x86 Windows binaries on x86_64. base.pre_link_args - .get_mut(&LinkerFlavor::Gcc) - .unwrap() + .entry(LinkerFlavor::Gcc) + .or_default() .push("-Wl,--large-address-aware".to_string()); Target { diff --git a/compiler/rustc_target/src/spec/i686_wrs_vxworks.rs b/compiler/rustc_target/src/spec/i686_wrs_vxworks.rs index ec8a2493b4e42..e596eca86b0d7 100644 --- a/compiler/rustc_target/src/spec/i686_wrs_vxworks.rs +++ b/compiler/rustc_target/src/spec/i686_wrs_vxworks.rs @@ -4,7 +4,7 @@ pub fn target() -> Target { let mut base = super::vxworks_base::opts(); base.cpu = "pentium4".to_string(); base.max_atomic_width = Some(64); - base.pre_link_args.get_mut(&LinkerFlavor::Gcc).unwrap().push("-m32".to_string()); + base.pre_link_args.entry(LinkerFlavor::Gcc).or_default().push("-m32".to_string()); base.stack_probes = StackProbeType::InlineOrCall { min_llvm_version_for_inline: (11, 0, 1) }; Target { diff --git a/compiler/rustc_target/src/spec/l4re_base.rs b/compiler/rustc_target/src/spec/l4re_base.rs index 660fae5f5c7cd..db6b74eff6dbd 100644 --- a/compiler/rustc_target/src/spec/l4re_base.rs +++ b/compiler/rustc_target/src/spec/l4re_base.rs @@ -1,4 +1,4 @@ -use crate::spec::{LinkArgs, LinkerFlavor, PanicStrategy, TargetOptions}; +use crate::spec::{LinkerFlavor, PanicStrategy, TargetOptions}; //use std::process::Command; // Use GCC to locate code for crt* libraries from the host, not from L4Re. Note @@ -13,18 +13,13 @@ use crate::spec::{LinkArgs, LinkerFlavor, PanicStrategy, TargetOptions}; //} pub fn opts() -> TargetOptions { - let mut args = LinkArgs::new(); - args.insert(LinkerFlavor::Gcc, vec![]); - TargetOptions { os: "l4re".to_string(), env: "uclibc".to_string(), linker_flavor: LinkerFlavor::Ld, executables: true, - has_elf_tls: false, panic_strategy: PanicStrategy::Abort, linker: Some("ld".to_string()), - pre_link_args: args, os_family: Some("unix".to_string()), ..Default::default() } diff --git a/compiler/rustc_target/src/spec/linux_base.rs b/compiler/rustc_target/src/spec/linux_base.rs index 0631644ad6355..eeefd056e4b74 100644 --- a/compiler/rustc_target/src/spec/linux_base.rs +++ b/compiler/rustc_target/src/spec/linux_base.rs @@ -1,23 +1,6 @@ -use crate::spec::{LinkArgs, LinkerFlavor, RelroLevel, TargetOptions}; +use crate::spec::{RelroLevel, TargetOptions}; pub fn opts() -> TargetOptions { - let mut args = LinkArgs::new(); - args.insert( - LinkerFlavor::Gcc, - vec![ - // We want to be able to strip as much executable code as possible - // from the linker command line, and this flag indicates to the - // linker that it can avoid linking in dynamic libraries that don't - // actually satisfy any symbols up to that point (as with many other - // resolutions the linker does). This option only applies to all - // following libraries so we're sure to pass it as one of the first - // arguments. - "-Wl,--as-needed".to_string(), - // Always enable NX protection when it is available - "-Wl,-z,noexecstack".to_string(), - ], - ); - TargetOptions { os: "linux".to_string(), dynamic_linking: true, @@ -25,7 +8,6 @@ pub fn opts() -> TargetOptions { os_family: Some("unix".to_string()), linker_is_gnu: true, has_rpath: true, - pre_link_args: args, position_independent_executables: true, relro_level: RelroLevel::Full, has_elf_tls: true, diff --git a/compiler/rustc_target/src/spec/linux_kernel_base.rs b/compiler/rustc_target/src/spec/linux_kernel_base.rs index 5220156895367..d17d729c289eb 100644 --- a/compiler/rustc_target/src/spec/linux_kernel_base.rs +++ b/compiler/rustc_target/src/spec/linux_kernel_base.rs @@ -1,14 +1,6 @@ -use crate::spec::{ - LinkArgs, LinkerFlavor, PanicStrategy, RelocModel, RelroLevel, StackProbeType, TargetOptions, -}; +use crate::spec::{PanicStrategy, RelocModel, RelroLevel, StackProbeType, TargetOptions}; pub fn opts() -> TargetOptions { - let mut pre_link_args = LinkArgs::new(); - pre_link_args.insert( - LinkerFlavor::Gcc, - vec!["-Wl,--as-needed".to_string(), "-Wl,-z,noexecstack".to_string()], - ); - TargetOptions { env: "gnu".to_string(), disable_redzone: true, @@ -20,7 +12,6 @@ pub fn opts() -> TargetOptions { needs_plt: true, relro_level: RelroLevel::Full, relocation_model: RelocModel::Static, - pre_link_args, ..Default::default() } diff --git a/compiler/rustc_target/src/spec/mipsel_unknown_none.rs b/compiler/rustc_target/src/spec/mipsel_unknown_none.rs index 0f9d3c3de1543..110c8dd80ea77 100644 --- a/compiler/rustc_target/src/spec/mipsel_unknown_none.rs +++ b/compiler/rustc_target/src/spec/mipsel_unknown_none.rs @@ -23,10 +23,12 @@ pub fn target() -> Target { panic_strategy: PanicStrategy::Abort, relocation_model: RelocModel::Static, unsupported_abis: vec![ - Abi::Stdcall, + Abi::Stdcall { unwind: false }, + Abi::Stdcall { unwind: true }, Abi::Fastcall, Abi::Vectorcall, - Abi::Thiscall, + Abi::Thiscall { unwind: false }, + Abi::Thiscall { unwind: true }, Abi::Win64, Abi::SysV64, ], diff --git a/compiler/rustc_target/src/spec/mod.rs b/compiler/rustc_target/src/spec/mod.rs index 72b2d089ebc3a..490239ee6155c 100644 --- a/compiler/rustc_target/src/spec/mod.rs +++ b/compiler/rustc_target/src/spec/mod.rs @@ -37,6 +37,7 @@ use crate::abi::Endian; use crate::spec::abi::{lookup as lookup_abi, Abi}; use crate::spec::crt_objects::{CrtObjects, CrtObjectsFallback}; +use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; use rustc_serialize::json::{Json, ToJson}; use rustc_span::symbol::{sym, Symbol}; use std::collections::BTreeMap; @@ -78,7 +79,7 @@ mod solaris_base; mod thumb_base; mod uefi_msvc_base; mod vxworks_base; -mod wasm32_base; +mod wasm_base; mod windows_gnu_base; mod windows_msvc_base; mod windows_uwp_gnu_base; @@ -511,38 +512,6 @@ impl fmt::Display for SplitDebuginfo { } } -macro_rules! supported_targets { - ( $(($( $triple:literal, )+ $module:ident ),)+ ) => { - $(mod $module;)+ - - /// List of supported targets - pub const TARGETS: &[&str] = &[$($($triple),+),+]; - - fn load_builtin(target: &str) -> Option { - let mut t = match target { - $( $($triple)|+ => $module::target(), )+ - _ => return None, - }; - t.is_builtin = true; - debug!("got builtin target: {:?}", t); - Some(t) - } - - #[cfg(test)] - mod tests { - mod tests_impl; - - // Cannot put this into a separate file without duplication, make an exception. - $( - #[test] // `#[test]` - fn $module() { - tests_impl::test_target(super::$module::target()); - } - )+ - } - }; -} - #[derive(Clone, Debug, PartialEq, Eq)] pub enum StackProbeType { /// Don't emit any stack probes. @@ -620,6 +589,117 @@ impl ToJson for StackProbeType { } } +bitflags::bitflags! { + #[derive(Default, Encodable, Decodable)] + pub struct SanitizerSet: u8 { + const ADDRESS = 1 << 0; + const LEAK = 1 << 1; + const MEMORY = 1 << 2; + const THREAD = 1 << 3; + const HWADDRESS = 1 << 4; + } +} + +impl SanitizerSet { + /// Return sanitizer's name + /// + /// Returns none if the flags is a set of sanitizers numbering not exactly one. + fn as_str(self) -> Option<&'static str> { + Some(match self { + SanitizerSet::ADDRESS => "address", + SanitizerSet::LEAK => "leak", + SanitizerSet::MEMORY => "memory", + SanitizerSet::THREAD => "thread", + SanitizerSet::HWADDRESS => "hwaddress", + _ => return None, + }) + } +} + +/// Formats a sanitizer set as a comma separated list of sanitizers' names. +impl fmt::Display for SanitizerSet { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let mut first = true; + for s in *self { + let name = s.as_str().unwrap_or_else(|| panic!("unrecognized sanitizer {:?}", s)); + if !first { + f.write_str(", ")?; + } + f.write_str(name)?; + first = false; + } + Ok(()) + } +} + +impl IntoIterator for SanitizerSet { + type Item = SanitizerSet; + type IntoIter = std::vec::IntoIter; + + fn into_iter(self) -> Self::IntoIter { + [ + SanitizerSet::ADDRESS, + SanitizerSet::LEAK, + SanitizerSet::MEMORY, + SanitizerSet::THREAD, + SanitizerSet::HWADDRESS, + ] + .iter() + .copied() + .filter(|&s| self.contains(s)) + .collect::>() + .into_iter() + } +} + +impl HashStable for SanitizerSet { + fn hash_stable(&self, ctx: &mut CTX, hasher: &mut StableHasher) { + self.bits().hash_stable(ctx, hasher); + } +} + +impl ToJson for SanitizerSet { + fn to_json(&self) -> Json { + self.into_iter() + .map(|v| Some(v.as_str()?.to_json())) + .collect::>>() + .unwrap_or(Vec::new()) + .to_json() + } +} + +macro_rules! supported_targets { + ( $(($( $triple:literal, )+ $module:ident ),)+ ) => { + $(mod $module;)+ + + /// List of supported targets + pub const TARGETS: &[&str] = &[$($($triple),+),+]; + + fn load_builtin(target: &str) -> Option { + let mut t = match target { + $( $($triple)|+ => $module::target(), )+ + _ => return None, + }; + t.is_builtin = true; + debug!("got builtin target: {:?}", t); + Some(t) + } + + #[cfg(test)] + mod tests { + mod tests_impl; + + // Cannot put this into a separate file without duplication, make an exception. + $( + #[test] // `#[test]` + fn $module() { + tests_impl::test_target(super::$module::target()); + } + )+ + } + }; +} + supported_targets! { ("x86_64-unknown-linux-gnu", x86_64_unknown_linux_gnu), ("x86_64-unknown-linux-gnux32", x86_64_unknown_linux_gnux32), @@ -679,7 +759,7 @@ supported_targets! { ("thumbv7neon-linux-androideabi", thumbv7neon_linux_androideabi), ("aarch64-linux-android", aarch64_linux_android), - ("x86_64-linux-kernel", x86_64_linux_kernel), + ("x86_64-unknown-none-linuxkernel", x86_64_unknown_none_linuxkernel), ("aarch64-unknown-freebsd", aarch64_unknown_freebsd), ("armv6-unknown-freebsd", armv6_unknown_freebsd), @@ -694,6 +774,7 @@ supported_targets! { ("i686-unknown-openbsd", i686_unknown_openbsd), ("sparc64-unknown-openbsd", sparc64_unknown_openbsd), ("x86_64-unknown-openbsd", x86_64_unknown_openbsd), + ("powerpc-unknown-openbsd", powerpc_unknown_openbsd), ("aarch64-unknown-netbsd", aarch64_unknown_netbsd), ("armv6-unknown-netbsd-eabihf", armv6_unknown_netbsd_eabihf), @@ -761,6 +842,7 @@ supported_targets! { ("wasm32-unknown-emscripten", wasm32_unknown_emscripten), ("wasm32-unknown-unknown", wasm32_unknown_unknown), ("wasm32-wasi", wasm32_wasi), + ("wasm64-unknown-unknown", wasm64_unknown_unknown), ("thumbv6m-none-eabi", thumbv6m_none_eabi), ("thumbv7m-none-eabi", thumbv7m_none_eabi), @@ -777,7 +859,8 @@ supported_targets! { ("aarch64-unknown-hermit", aarch64_unknown_hermit), ("x86_64-unknown-hermit", x86_64_unknown_hermit), - ("x86_64-unknown-hermit-kernel", x86_64_unknown_hermit_kernel), + + ("x86_64-unknown-none-hermitkernel", x86_64_unknown_none_hermitkernel), ("riscv32i-unknown-none-elf", riscv32i_unknown_none_elf), ("riscv32imc-unknown-none-elf", riscv32imc_unknown_none_elf), @@ -994,6 +1077,8 @@ pub struct TargetOptions { pub is_like_emscripten: bool, /// Whether the target toolchain is like Fuchsia's. pub is_like_fuchsia: bool, + /// Whether a target toolchain is like WASM. + pub is_like_wasm: bool, /// Version of DWARF to use if not using the default. /// Useful because some platforms (osx, bsd) only want up to DWARF2. pub dwarf_version: Option, @@ -1109,6 +1194,10 @@ pub struct TargetOptions { /// unwinders. pub requires_uwtable: bool, + /// Whether or not to emit `uwtable` attributes on functions if `-C force-unwind-tables` + /// is not specified and `uwtable` is not required on this target. + pub default_uwtable: bool, + /// Whether or not SIMD types are passed by reference in the Rust ABI, /// typically required if a target can be compiled with a mixed set of /// target features. This is `true` by default, and `false` for targets like @@ -1158,6 +1247,16 @@ pub struct TargetOptions { /// How to handle split debug information, if at all. Specifying `None` has /// target-specific meaning. pub split_debuginfo: SplitDebuginfo, + + /// The sanitizers supported by this target + /// + /// Note that the support here is at a codegen level. If the machine code with sanitizer + /// enabled can generated on this target, but the necessary supporting libraries are not + /// distributed with the target, the sanitizer should still appear in this list for the target. + pub supported_sanitizers: SanitizerSet, + + /// If present it's a default value to use for adjusting the C ABI. + pub default_adjusted_cabi: Option, } impl Default for TargetOptions { @@ -1202,6 +1301,7 @@ impl Default for TargetOptions { is_like_emscripten: false, is_like_msvc: false, is_like_fuchsia: false, + is_like_wasm: false, dwarf_version: None, linker_is_gnu: false, allows_weak_linkage: true, @@ -1246,6 +1346,7 @@ impl Default for TargetOptions { default_hidden_visibility: false, emit_debug_gdb_scripts: true, requires_uwtable: false, + default_uwtable: false, simd_types_indirect: true, limit_rdylib_exports: true, override_export_symbols: None, @@ -1258,6 +1359,8 @@ impl Default for TargetOptions { eh_frame_header: true, has_thumb_interworking: false, split_debuginfo: SplitDebuginfo::Off, + supported_sanitizers: SanitizerSet::empty(), + default_adjusted_cabi: None, } } } @@ -1282,26 +1385,36 @@ impl Target { /// Given a function ABI, turn it into the correct ABI for this target. pub fn adjust_abi(&self, abi: Abi) -> Abi { match abi { - Abi::System => { + Abi::System { unwind } => { if self.is_like_windows && self.arch == "x86" { - Abi::Stdcall + Abi::Stdcall { unwind } } else { - Abi::C + Abi::C { unwind } } } // These ABI kinds are ignored on non-x86 Windows targets. // See https://docs.microsoft.com/en-us/cpp/cpp/argument-passing-and-naming-conventions // and the individual pages for __stdcall et al. - Abi::Stdcall | Abi::Fastcall | Abi::Vectorcall | Abi::Thiscall => { - if self.is_like_windows && self.arch != "x86" { Abi::C } else { abi } + Abi::Stdcall { unwind } | Abi::Thiscall { unwind } => { + if self.is_like_windows && self.arch != "x86" { Abi::C { unwind } } else { abi } + } + Abi::Fastcall | Abi::Vectorcall => { + if self.is_like_windows && self.arch != "x86" { + Abi::C { unwind: false } + } else { + abi + } } Abi::EfiApi => { if self.arch == "x86_64" { Abi::Win64 } else { - Abi::C + Abi::C { unwind: false } } } + + Abi::C { unwind } => self.default_adjusted_cabi.unwrap_or(Abi::C { unwind }), + abi => abi, } } @@ -1333,8 +1446,8 @@ impl Target { let get_req_field = |name: &str| { obj.find(name) - .map(|s| s.as_string()) - .and_then(|os| os.map(|s| s.to_string())) + .and_then(Json::as_string) + .map(str::to_string) .ok_or_else(|| format!("Field {} in target specification is required", name)) }; @@ -1537,6 +1650,24 @@ impl Target { )), }).unwrap_or(Ok(())) } ); + ($key_name:ident, SanitizerSet) => ( { + let name = (stringify!($key_name)).replace("_", "-"); + obj.find(&name[..]).and_then(|o| o.as_array()).and_then(|a| { + for s in a { + base.$key_name |= match s.as_string() { + Some("address") => SanitizerSet::ADDRESS, + Some("leak") => SanitizerSet::LEAK, + Some("memory") => SanitizerSet::MEMORY, + Some("thread") => SanitizerSet::THREAD, + Some("hwaddress") => SanitizerSet::HWADDRESS, + Some(s) => return Some(Err(format!("unknown sanitizer {}", s))), + _ => return Some(Err(format!("not a string: {:?}", s))), + }; + } + Some(Ok(())) + }).unwrap_or(Ok(())) + } ); + ($key_name:ident, crt_objects_fallback) => ( { let name = (stringify!($key_name)).replace("_", "-"); obj.find(&name[..]).and_then(|o| o.as_string().and_then(|s| { @@ -1618,6 +1749,16 @@ impl Target { } } } ); + ($key_name:ident, Option) => ( { + let name = (stringify!($key_name)).replace("_", "-"); + obj.find(&name[..]).and_then(|o| o.as_string().and_then(|s| { + match lookup_abi(s) { + Some(abi) => base.$key_name = Some(abi), + _ => return Some(Err(format!("'{}' is not a valid value for abi", s))), + } + Some(Ok(())) + })).unwrap_or(Ok(())) + } ); } if let Some(s) = obj.find("target-endian").and_then(Json::as_string) { @@ -1669,6 +1810,7 @@ impl Target { key!(is_like_msvc, bool); key!(is_like_emscripten, bool); key!(is_like_fuchsia, bool); + key!(is_like_wasm, bool); key!(dwarf_version, Option); key!(linker_is_gnu, bool); key!(allows_weak_linkage, bool); @@ -1702,6 +1844,7 @@ impl Target { key!(default_hidden_visibility, bool); key!(emit_debug_gdb_scripts, bool); key!(requires_uwtable, bool); + key!(default_uwtable, bool); key!(simd_types_indirect, bool); key!(limit_rdylib_exports, bool); key!(override_export_symbols, opt_list); @@ -1714,6 +1857,8 @@ impl Target { key!(eh_frame_header, bool); key!(has_thumb_interworking, bool); key!(split_debuginfo, SplitDebuginfo)?; + key!(supported_sanitizers, SanitizerSet)?; + key!(default_adjusted_cabi, Option)?; // NB: The old name is deprecated, but support for it is retained for // compatibility. @@ -1914,6 +2059,7 @@ impl ToJson for Target { target_option_val!(is_like_msvc); target_option_val!(is_like_emscripten); target_option_val!(is_like_fuchsia); + target_option_val!(is_like_wasm); target_option_val!(dwarf_version); target_option_val!(linker_is_gnu); target_option_val!(allows_weak_linkage); @@ -1947,6 +2093,7 @@ impl ToJson for Target { target_option_val!(default_hidden_visibility); target_option_val!(emit_debug_gdb_scripts); target_option_val!(requires_uwtable); + target_option_val!(default_uwtable); target_option_val!(simd_types_indirect); target_option_val!(limit_rdylib_exports); target_option_val!(override_export_symbols); @@ -1959,6 +2106,11 @@ impl ToJson for Target { target_option_val!(eh_frame_header); target_option_val!(has_thumb_interworking); target_option_val!(split_debuginfo); + target_option_val!(supported_sanitizers); + + if let Some(abi) = self.default_adjusted_cabi { + d.insert("default-adjusted-cabi".to_string(), Abi::name(abi).to_json()); + } if default.unsupported_abis != self.unsupported_abis { d.insert( diff --git a/compiler/rustc_target/src/spec/msvc_base.rs b/compiler/rustc_target/src/spec/msvc_base.rs index 39c0d5f0bb4ff..4ed7685ca0770 100644 --- a/compiler/rustc_target/src/spec/msvc_base.rs +++ b/compiler/rustc_target/src/spec/msvc_base.rs @@ -5,13 +5,6 @@ pub fn opts() -> TargetOptions { // Suppress the verbose logo and authorship debugging output, which would needlessly // clog any log files. "/NOLOGO".to_string(), - // Tell the compiler that non-code sections can be marked as non-executable, - // including stack pages. - // UEFI is fully compatible to non-executable data pages. - // In fact, firmware might enforce this, so we better let the linker know about this, - // so it will fail if the compiler ever tries placing code on the stack - // (e.g., trampoline constructs and alike). - "/NXCOMPAT".to_string(), ]; let mut pre_link_args = LinkArgs::new(); pre_link_args.insert(LinkerFlavor::Msvc, pre_link_args_msvc.clone()); diff --git a/compiler/rustc_target/src/spec/netbsd_base.rs b/compiler/rustc_target/src/spec/netbsd_base.rs index a77d60bd9d747..680cd60788bde 100644 --- a/compiler/rustc_target/src/spec/netbsd_base.rs +++ b/compiler/rustc_target/src/spec/netbsd_base.rs @@ -1,18 +1,6 @@ -use crate::spec::{LinkArgs, LinkerFlavor, RelroLevel, TargetOptions}; +use crate::spec::{RelroLevel, TargetOptions}; pub fn opts() -> TargetOptions { - let mut args = LinkArgs::new(); - args.insert( - LinkerFlavor::Gcc, - vec![ - // GNU-style linkers will use this to omit linking to libraries - // which don't actually fulfill any relocations, but only for - // libraries which follow this flag. Thus, use it before - // specifying libraries to link to. - "-Wl,--as-needed".to_string(), - ], - ); - TargetOptions { os: "netbsd".to_string(), dynamic_linking: true, @@ -21,7 +9,6 @@ pub fn opts() -> TargetOptions { linker_is_gnu: true, no_default_libraries: false, has_rpath: true, - pre_link_args: args, position_independent_executables: true, relro_level: RelroLevel::Full, use_ctors_section: true, diff --git a/compiler/rustc_target/src/spec/nvptx64_nvidia_cuda.rs b/compiler/rustc_target/src/spec/nvptx64_nvidia_cuda.rs index 3c9c7d578fbd4..15d8e4843f976 100644 --- a/compiler/rustc_target/src/spec/nvptx64_nvidia_cuda.rs +++ b/compiler/rustc_target/src/spec/nvptx64_nvidia_cuda.rs @@ -49,10 +49,12 @@ pub fn target() -> Target { // create the tests for this. unsupported_abis: vec![ Abi::Cdecl, - Abi::Stdcall, + Abi::Stdcall { unwind: false }, + Abi::Stdcall { unwind: true }, Abi::Fastcall, Abi::Vectorcall, - Abi::Thiscall, + Abi::Thiscall { unwind: false }, + Abi::Thiscall { unwind: true }, Abi::Aapcs, Abi::Win64, Abi::SysV64, diff --git a/compiler/rustc_target/src/spec/openbsd_base.rs b/compiler/rustc_target/src/spec/openbsd_base.rs index 2b40a1ed945cf..a6fd01ab110df 100644 --- a/compiler/rustc_target/src/spec/openbsd_base.rs +++ b/compiler/rustc_target/src/spec/openbsd_base.rs @@ -1,20 +1,6 @@ -use crate::spec::{LinkArgs, LinkerFlavor, RelroLevel, TargetOptions}; +use crate::spec::{RelroLevel, TargetOptions}; pub fn opts() -> TargetOptions { - let mut args = LinkArgs::new(); - args.insert( - LinkerFlavor::Gcc, - vec![ - // GNU-style linkers will use this to omit linking to libraries - // which don't actually fulfill any relocations, but only for - // libraries which follow this flag. Thus, use it before - // specifying libraries to link to. - "-Wl,--as-needed".to_string(), - // Always enable NX protection when it is available - "-Wl,-z,noexecstack".to_string(), - ], - ); - TargetOptions { os: "openbsd".to_string(), dynamic_linking: true, @@ -23,7 +9,6 @@ pub fn opts() -> TargetOptions { linker_is_gnu: true, has_rpath: true, abi_return_struct_as_int: true, - pre_link_args: args, position_independent_executables: true, eliminate_frame_pointer: false, // FIXME 43575 relro_level: RelroLevel::Full, diff --git a/compiler/rustc_target/src/spec/powerpc64_unknown_freebsd.rs b/compiler/rustc_target/src/spec/powerpc64_unknown_freebsd.rs index 3dddeb1129cbe..b3d6b7c610704 100644 --- a/compiler/rustc_target/src/spec/powerpc64_unknown_freebsd.rs +++ b/compiler/rustc_target/src/spec/powerpc64_unknown_freebsd.rs @@ -4,7 +4,7 @@ use crate::spec::{LinkerFlavor, Target, TargetOptions}; pub fn target() -> Target { let mut base = super::freebsd_base::opts(); base.cpu = "ppc64".to_string(); - base.pre_link_args.get_mut(&LinkerFlavor::Gcc).unwrap().push("-m64".to_string()); + base.pre_link_args.entry(LinkerFlavor::Gcc).or_default().push("-m64".to_string()); base.max_atomic_width = Some(64); Target { diff --git a/compiler/rustc_target/src/spec/powerpc64_unknown_linux_gnu.rs b/compiler/rustc_target/src/spec/powerpc64_unknown_linux_gnu.rs index 9db880b0e53ca..559a1a40868ff 100644 --- a/compiler/rustc_target/src/spec/powerpc64_unknown_linux_gnu.rs +++ b/compiler/rustc_target/src/spec/powerpc64_unknown_linux_gnu.rs @@ -4,7 +4,7 @@ use crate::spec::{LinkerFlavor, RelroLevel, Target, TargetOptions}; pub fn target() -> Target { let mut base = super::linux_gnu_base::opts(); base.cpu = "ppc64".to_string(); - base.pre_link_args.get_mut(&LinkerFlavor::Gcc).unwrap().push("-m64".to_string()); + base.pre_link_args.entry(LinkerFlavor::Gcc).or_default().push("-m64".to_string()); base.max_atomic_width = Some(64); // ld.so in at least RHEL6 on ppc64 has a bug related to BIND_NOW, so only enable partial RELRO diff --git a/compiler/rustc_target/src/spec/powerpc64_unknown_linux_musl.rs b/compiler/rustc_target/src/spec/powerpc64_unknown_linux_musl.rs index 8767f86b00bb4..f1190b159aba4 100644 --- a/compiler/rustc_target/src/spec/powerpc64_unknown_linux_musl.rs +++ b/compiler/rustc_target/src/spec/powerpc64_unknown_linux_musl.rs @@ -4,7 +4,7 @@ use crate::spec::{LinkerFlavor, Target, TargetOptions}; pub fn target() -> Target { let mut base = super::linux_musl_base::opts(); base.cpu = "ppc64".to_string(); - base.pre_link_args.get_mut(&LinkerFlavor::Gcc).unwrap().push("-m64".to_string()); + base.pre_link_args.entry(LinkerFlavor::Gcc).or_default().push("-m64".to_string()); base.max_atomic_width = Some(64); Target { diff --git a/compiler/rustc_target/src/spec/powerpc64_wrs_vxworks.rs b/compiler/rustc_target/src/spec/powerpc64_wrs_vxworks.rs index 2f28a8562472d..3ebc5469e0a8b 100644 --- a/compiler/rustc_target/src/spec/powerpc64_wrs_vxworks.rs +++ b/compiler/rustc_target/src/spec/powerpc64_wrs_vxworks.rs @@ -4,7 +4,7 @@ use crate::spec::{LinkerFlavor, Target, TargetOptions}; pub fn target() -> Target { let mut base = super::vxworks_base::opts(); base.cpu = "ppc64".to_string(); - base.pre_link_args.get_mut(&LinkerFlavor::Gcc).unwrap().push("-m64".to_string()); + base.pre_link_args.entry(LinkerFlavor::Gcc).or_default().push("-m64".to_string()); base.max_atomic_width = Some(64); Target { diff --git a/compiler/rustc_target/src/spec/powerpc64le_unknown_linux_gnu.rs b/compiler/rustc_target/src/spec/powerpc64le_unknown_linux_gnu.rs index 4cbd99765088f..76f70e474f073 100644 --- a/compiler/rustc_target/src/spec/powerpc64le_unknown_linux_gnu.rs +++ b/compiler/rustc_target/src/spec/powerpc64le_unknown_linux_gnu.rs @@ -3,7 +3,7 @@ use crate::spec::{LinkerFlavor, Target, TargetOptions}; pub fn target() -> Target { let mut base = super::linux_gnu_base::opts(); base.cpu = "ppc64le".to_string(); - base.pre_link_args.get_mut(&LinkerFlavor::Gcc).unwrap().push("-m64".to_string()); + base.pre_link_args.entry(LinkerFlavor::Gcc).or_default().push("-m64".to_string()); base.max_atomic_width = Some(64); Target { diff --git a/compiler/rustc_target/src/spec/powerpc64le_unknown_linux_musl.rs b/compiler/rustc_target/src/spec/powerpc64le_unknown_linux_musl.rs index efdc9ad7517d1..42c49103b3b4e 100644 --- a/compiler/rustc_target/src/spec/powerpc64le_unknown_linux_musl.rs +++ b/compiler/rustc_target/src/spec/powerpc64le_unknown_linux_musl.rs @@ -3,7 +3,7 @@ use crate::spec::{LinkerFlavor, Target, TargetOptions}; pub fn target() -> Target { let mut base = super::linux_musl_base::opts(); base.cpu = "ppc64le".to_string(); - base.pre_link_args.get_mut(&LinkerFlavor::Gcc).unwrap().push("-m64".to_string()); + base.pre_link_args.entry(LinkerFlavor::Gcc).or_default().push("-m64".to_string()); base.max_atomic_width = Some(64); Target { diff --git a/compiler/rustc_target/src/spec/powerpc_unknown_linux_gnu.rs b/compiler/rustc_target/src/spec/powerpc_unknown_linux_gnu.rs index 70dd0b2aee691..21ffdd2d160f3 100644 --- a/compiler/rustc_target/src/spec/powerpc_unknown_linux_gnu.rs +++ b/compiler/rustc_target/src/spec/powerpc_unknown_linux_gnu.rs @@ -3,7 +3,7 @@ use crate::spec::{LinkerFlavor, Target, TargetOptions}; pub fn target() -> Target { let mut base = super::linux_gnu_base::opts(); - base.pre_link_args.get_mut(&LinkerFlavor::Gcc).unwrap().push("-m32".to_string()); + base.pre_link_args.entry(LinkerFlavor::Gcc).or_default().push("-m32".to_string()); base.max_atomic_width = Some(32); Target { diff --git a/compiler/rustc_target/src/spec/powerpc_unknown_linux_gnuspe.rs b/compiler/rustc_target/src/spec/powerpc_unknown_linux_gnuspe.rs index 66118b74955ec..8d8f746f97f12 100644 --- a/compiler/rustc_target/src/spec/powerpc_unknown_linux_gnuspe.rs +++ b/compiler/rustc_target/src/spec/powerpc_unknown_linux_gnuspe.rs @@ -3,7 +3,7 @@ use crate::spec::{LinkerFlavor, Target, TargetOptions}; pub fn target() -> Target { let mut base = super::linux_gnu_base::opts(); - base.pre_link_args.get_mut(&LinkerFlavor::Gcc).unwrap().push("-mspe".to_string()); + base.pre_link_args.entry(LinkerFlavor::Gcc).or_default().push("-mspe".to_string()); base.max_atomic_width = Some(32); Target { diff --git a/compiler/rustc_target/src/spec/powerpc_unknown_linux_musl.rs b/compiler/rustc_target/src/spec/powerpc_unknown_linux_musl.rs index 679a3a2f6aacb..9633705db6d1f 100644 --- a/compiler/rustc_target/src/spec/powerpc_unknown_linux_musl.rs +++ b/compiler/rustc_target/src/spec/powerpc_unknown_linux_musl.rs @@ -3,7 +3,7 @@ use crate::spec::{LinkerFlavor, Target, TargetOptions}; pub fn target() -> Target { let mut base = super::linux_musl_base::opts(); - base.pre_link_args.get_mut(&LinkerFlavor::Gcc).unwrap().push("-m32".to_string()); + base.pre_link_args.entry(LinkerFlavor::Gcc).or_default().push("-m32".to_string()); base.max_atomic_width = Some(32); Target { diff --git a/compiler/rustc_target/src/spec/powerpc_unknown_netbsd.rs b/compiler/rustc_target/src/spec/powerpc_unknown_netbsd.rs index 1245098329aee..4cc5224fae30d 100644 --- a/compiler/rustc_target/src/spec/powerpc_unknown_netbsd.rs +++ b/compiler/rustc_target/src/spec/powerpc_unknown_netbsd.rs @@ -3,7 +3,7 @@ use crate::spec::{LinkerFlavor, Target, TargetOptions}; pub fn target() -> Target { let mut base = super::netbsd_base::opts(); - base.pre_link_args.get_mut(&LinkerFlavor::Gcc).unwrap().push("-m32".to_string()); + base.pre_link_args.entry(LinkerFlavor::Gcc).or_default().push("-m32".to_string()); base.max_atomic_width = Some(32); Target { diff --git a/compiler/rustc_target/src/spec/powerpc_unknown_openbsd.rs b/compiler/rustc_target/src/spec/powerpc_unknown_openbsd.rs new file mode 100644 index 0000000000000..c17183faa7ae0 --- /dev/null +++ b/compiler/rustc_target/src/spec/powerpc_unknown_openbsd.rs @@ -0,0 +1,16 @@ +use crate::abi::Endian; +use crate::spec::Target; + +pub fn target() -> Target { + let mut base = super::openbsd_base::opts(); + base.endian = Endian::Big; + base.max_atomic_width = Some(32); + + Target { + llvm_target: "powerpc-unknown-openbsd".to_string(), + pointer_width: 32, + data_layout: "E-m:e-p:32:32-i64:64-n32".to_string(), + arch: "powerpc".to_string(), + options: base, + } +} diff --git a/compiler/rustc_target/src/spec/powerpc_wrs_vxworks.rs b/compiler/rustc_target/src/spec/powerpc_wrs_vxworks.rs index bb943a8825c6c..2f0a6ca44a008 100644 --- a/compiler/rustc_target/src/spec/powerpc_wrs_vxworks.rs +++ b/compiler/rustc_target/src/spec/powerpc_wrs_vxworks.rs @@ -3,8 +3,8 @@ use crate::spec::{LinkerFlavor, Target, TargetOptions}; pub fn target() -> Target { let mut base = super::vxworks_base::opts(); - base.pre_link_args.get_mut(&LinkerFlavor::Gcc).unwrap().push("-m32".to_string()); - base.pre_link_args.get_mut(&LinkerFlavor::Gcc).unwrap().push("--secure-plt".to_string()); + base.pre_link_args.entry(LinkerFlavor::Gcc).or_default().push("-m32".to_string()); + base.pre_link_args.entry(LinkerFlavor::Gcc).or_default().push("--secure-plt".to_string()); base.max_atomic_width = Some(32); Target { diff --git a/compiler/rustc_target/src/spec/powerpc_wrs_vxworks_spe.rs b/compiler/rustc_target/src/spec/powerpc_wrs_vxworks_spe.rs index 4b4f118ba49bd..215f1a36227d7 100644 --- a/compiler/rustc_target/src/spec/powerpc_wrs_vxworks_spe.rs +++ b/compiler/rustc_target/src/spec/powerpc_wrs_vxworks_spe.rs @@ -3,8 +3,8 @@ use crate::spec::{LinkerFlavor, Target, TargetOptions}; pub fn target() -> Target { let mut base = super::vxworks_base::opts(); - base.pre_link_args.get_mut(&LinkerFlavor::Gcc).unwrap().push("-mspe".to_string()); - base.pre_link_args.get_mut(&LinkerFlavor::Gcc).unwrap().push("--secure-plt".to_string()); + base.pre_link_args.entry(LinkerFlavor::Gcc).or_default().push("-mspe".to_string()); + base.pre_link_args.entry(LinkerFlavor::Gcc).or_default().push("--secure-plt".to_string()); base.max_atomic_width = Some(32); Target { diff --git a/compiler/rustc_target/src/spec/redox_base.rs b/compiler/rustc_target/src/spec/redox_base.rs index 5ef705878a8ff..0afb4a72ac14f 100644 --- a/compiler/rustc_target/src/spec/redox_base.rs +++ b/compiler/rustc_target/src/spec/redox_base.rs @@ -1,23 +1,6 @@ -use crate::spec::{LinkArgs, LinkerFlavor, RelroLevel, TargetOptions}; +use crate::spec::{RelroLevel, TargetOptions}; pub fn opts() -> TargetOptions { - let mut args = LinkArgs::new(); - args.insert( - LinkerFlavor::Gcc, - vec![ - // We want to be able to strip as much executable code as possible - // from the linker command line, and this flag indicates to the - // linker that it can avoid linking in dynamic libraries that don't - // actually satisfy any symbols up to that point (as with many other - // resolutions the linker does). This option only applies to all - // following libraries so we're sure to pass it as one of the first - // arguments. - "-Wl,--as-needed".to_string(), - // Always enable NX protection when it is available - "-Wl,-z,noexecstack".to_string(), - ], - ); - TargetOptions { os: "redox".to_string(), env: "relibc".to_string(), @@ -26,7 +9,6 @@ pub fn opts() -> TargetOptions { os_family: Some("unix".to_string()), linker_is_gnu: true, has_rpath: true, - pre_link_args: args, position_independent_executables: true, relro_level: RelroLevel::Full, has_elf_tls: true, diff --git a/compiler/rustc_target/src/spec/riscv32i_unknown_none_elf.rs b/compiler/rustc_target/src/spec/riscv32i_unknown_none_elf.rs index a31a08a8cf93d..88a22f25ff47b 100644 --- a/compiler/rustc_target/src/spec/riscv32i_unknown_none_elf.rs +++ b/compiler/rustc_target/src/spec/riscv32i_unknown_none_elf.rs @@ -14,7 +14,6 @@ pub fn target() -> Target { cpu: "generic-rv32".to_string(), max_atomic_width: Some(0), atomic_cas: false, - features: String::new(), executables: true, panic_strategy: PanicStrategy::Abort, relocation_model: RelocModel::Static, diff --git a/compiler/rustc_target/src/spec/riscv32imac_unknown_none_elf.rs b/compiler/rustc_target/src/spec/riscv32imac_unknown_none_elf.rs index 2ee53fdc4016d..b406eec1e7502 100644 --- a/compiler/rustc_target/src/spec/riscv32imac_unknown_none_elf.rs +++ b/compiler/rustc_target/src/spec/riscv32imac_unknown_none_elf.rs @@ -13,7 +13,6 @@ pub fn target() -> Target { linker: Some("rust-lld".to_string()), cpu: "generic-rv32".to_string(), max_atomic_width: Some(32), - atomic_cas: true, features: "+m,+a,+c".to_string(), executables: true, panic_strategy: PanicStrategy::Abort, diff --git a/compiler/rustc_target/src/spec/riscv64gc_unknown_none_elf.rs b/compiler/rustc_target/src/spec/riscv64gc_unknown_none_elf.rs index 33a785fdfee48..481bce05a08e5 100644 --- a/compiler/rustc_target/src/spec/riscv64gc_unknown_none_elf.rs +++ b/compiler/rustc_target/src/spec/riscv64gc_unknown_none_elf.rs @@ -11,9 +11,9 @@ pub fn target() -> Target { options: TargetOptions { linker_flavor: LinkerFlavor::Lld(LldFlavor::Ld), linker: Some("rust-lld".to_string()), + llvm_abiname: "lp64d".to_string(), cpu: "generic-rv64".to_string(), max_atomic_width: Some(64), - atomic_cas: true, features: "+m,+a,+f,+d,+c".to_string(), executables: true, panic_strategy: PanicStrategy::Abort, diff --git a/compiler/rustc_target/src/spec/riscv64imac_unknown_none_elf.rs b/compiler/rustc_target/src/spec/riscv64imac_unknown_none_elf.rs index 908367ee2006e..3e4afd446dda9 100644 --- a/compiler/rustc_target/src/spec/riscv64imac_unknown_none_elf.rs +++ b/compiler/rustc_target/src/spec/riscv64imac_unknown_none_elf.rs @@ -13,7 +13,6 @@ pub fn target() -> Target { linker: Some("rust-lld".to_string()), cpu: "generic-rv64".to_string(), max_atomic_width: Some(64), - atomic_cas: true, features: "+m,+a,+c".to_string(), executables: true, panic_strategy: PanicStrategy::Abort, diff --git a/compiler/rustc_target/src/spec/riscv_base.rs b/compiler/rustc_target/src/spec/riscv_base.rs index 64cf890037e51..5bcbb2e621bd0 100644 --- a/compiler/rustc_target/src/spec/riscv_base.rs +++ b/compiler/rustc_target/src/spec/riscv_base.rs @@ -5,10 +5,12 @@ use crate::spec::abi::Abi; pub fn unsupported_abis() -> Vec { vec![ Abi::Cdecl, - Abi::Stdcall, + Abi::Stdcall { unwind: false }, + Abi::Stdcall { unwind: true }, Abi::Fastcall, Abi::Vectorcall, - Abi::Thiscall, + Abi::Thiscall { unwind: false }, + Abi::Thiscall { unwind: true }, Abi::Aapcs, Abi::Win64, Abi::SysV64, diff --git a/compiler/rustc_target/src/spec/sparc64_unknown_netbsd.rs b/compiler/rustc_target/src/spec/sparc64_unknown_netbsd.rs index 7d685c83100d3..b4286dfd88f11 100644 --- a/compiler/rustc_target/src/spec/sparc64_unknown_netbsd.rs +++ b/compiler/rustc_target/src/spec/sparc64_unknown_netbsd.rs @@ -4,7 +4,7 @@ use crate::spec::{LinkerFlavor, Target, TargetOptions}; pub fn target() -> Target { let mut base = super::netbsd_base::opts(); base.cpu = "v9".to_string(); - base.pre_link_args.get_mut(&LinkerFlavor::Gcc).unwrap().push("-m64".to_string()); + base.pre_link_args.entry(LinkerFlavor::Gcc).or_default().push("-m64".to_string()); base.max_atomic_width = Some(64); Target { diff --git a/compiler/rustc_target/src/spec/sparc64_unknown_openbsd.rs b/compiler/rustc_target/src/spec/sparc64_unknown_openbsd.rs index 63b13fad4f7f6..9732983161f29 100644 --- a/compiler/rustc_target/src/spec/sparc64_unknown_openbsd.rs +++ b/compiler/rustc_target/src/spec/sparc64_unknown_openbsd.rs @@ -5,7 +5,7 @@ pub fn target() -> Target { let mut base = super::openbsd_base::opts(); base.endian = Endian::Big; base.cpu = "v9".to_string(); - base.pre_link_args.get_mut(&LinkerFlavor::Gcc).unwrap().push("-m64".to_string()); + base.pre_link_args.entry(LinkerFlavor::Gcc).or_default().push("-m64".to_string()); base.max_atomic_width = Some(64); Target { diff --git a/compiler/rustc_target/src/spec/sparc_unknown_linux_gnu.rs b/compiler/rustc_target/src/spec/sparc_unknown_linux_gnu.rs index 9e8fbff81c538..1fd4cadfffc79 100644 --- a/compiler/rustc_target/src/spec/sparc_unknown_linux_gnu.rs +++ b/compiler/rustc_target/src/spec/sparc_unknown_linux_gnu.rs @@ -6,7 +6,7 @@ pub fn target() -> Target { base.endian = Endian::Big; base.cpu = "v9".to_string(); base.max_atomic_width = Some(64); - base.pre_link_args.get_mut(&LinkerFlavor::Gcc).unwrap().push("-mv8plus".to_string()); + base.pre_link_args.entry(LinkerFlavor::Gcc).or_default().push("-mv8plus".to_string()); Target { llvm_target: "sparc-unknown-linux-gnu".to_string(), diff --git a/compiler/rustc_target/src/spec/tests/tests_impl.rs b/compiler/rustc_target/src/spec/tests/tests_impl.rs index 9ec8467e0ac44..f4de8bc0a5803 100644 --- a/compiler/rustc_target/src/spec/tests/tests_impl.rs +++ b/compiler/rustc_target/src/spec/tests/tests_impl.rs @@ -50,6 +50,7 @@ impl Target { // and you certainly want "unknown" for the OS name. fn can_use_os_unknown(&self) -> bool { self.llvm_target == "wasm32-unknown-unknown" + || self.llvm_target == "wasm64-unknown-unknown" || (self.env == "sgx" && self.vendor == "fortanix") } } diff --git a/compiler/rustc_target/src/spec/thumbv4t_none_eabi.rs b/compiler/rustc_target/src/spec/thumbv4t_none_eabi.rs index d87c06d49cbc2..ef58824f38108 100644 --- a/compiler/rustc_target/src/spec/thumbv4t_none_eabi.rs +++ b/compiler/rustc_target/src/spec/thumbv4t_none_eabi.rs @@ -45,9 +45,6 @@ pub fn target() -> Target { main_needs_argc_argv: false, - // No thread-local storage (just use a static Cell) - has_elf_tls: false, - // don't have atomic compare-and-swap atomic_cas: false, has_thumb_interworking: true, diff --git a/compiler/rustc_target/src/spec/thumbv7a_pc_windows_msvc.rs b/compiler/rustc_target/src/spec/thumbv7a_pc_windows_msvc.rs index 8131a6e2ea444..1232daa577f23 100644 --- a/compiler/rustc_target/src/spec/thumbv7a_pc_windows_msvc.rs +++ b/compiler/rustc_target/src/spec/thumbv7a_pc_windows_msvc.rs @@ -11,10 +11,10 @@ pub fn target() -> Target { // where necessary, but this is not the observed behavior. // Disabling the LBR optimization works around the issue. let pre_link_args_msvc = "/OPT:NOLBR".to_string(); - base.pre_link_args.get_mut(&LinkerFlavor::Msvc).unwrap().push(pre_link_args_msvc.clone()); + base.pre_link_args.entry(LinkerFlavor::Msvc).or_default().push(pre_link_args_msvc.clone()); base.pre_link_args - .get_mut(&LinkerFlavor::Lld(LldFlavor::Link)) - .unwrap() + .entry(LinkerFlavor::Lld(LldFlavor::Link)) + .or_default() .push(pre_link_args_msvc); // FIXME(jordanrh): use PanicStrategy::Unwind when SEH is @@ -29,7 +29,6 @@ pub fn target() -> Target { options: TargetOptions { features: "+vfp3,+neon".to_string(), - cpu: "generic".to_string(), max_atomic_width: Some(64), unsupported_abis: super::arm_base::unsupported_abis(), ..base diff --git a/compiler/rustc_target/src/spec/thumbv7a_uwp_windows_msvc.rs b/compiler/rustc_target/src/spec/thumbv7a_uwp_windows_msvc.rs index a2c1b6bb90c97..e6a59f015c993 100644 --- a/compiler/rustc_target/src/spec/thumbv7a_uwp_windows_msvc.rs +++ b/compiler/rustc_target/src/spec/thumbv7a_uwp_windows_msvc.rs @@ -16,7 +16,6 @@ pub fn target() -> Target { arch: "arm".to_string(), options: TargetOptions { features: "+vfp3,+neon".to_string(), - cpu: "generic".to_string(), unsupported_abis: super::arm_base::unsupported_abis(), ..base }, diff --git a/compiler/rustc_target/src/spec/thumbv7neon_linux_androideabi.rs b/compiler/rustc_target/src/spec/thumbv7neon_linux_androideabi.rs index 41fdbc2f0a013..58b0a9d220279 100644 --- a/compiler/rustc_target/src/spec/thumbv7neon_linux_androideabi.rs +++ b/compiler/rustc_target/src/spec/thumbv7neon_linux_androideabi.rs @@ -12,7 +12,7 @@ pub fn target() -> Target { let mut base = super::android_base::opts(); base.features = "+v7,+thumb-mode,+thumb2,+vfp3,+neon".to_string(); base.max_atomic_width = Some(64); - base.pre_link_args.get_mut(&LinkerFlavor::Gcc).unwrap().push("-march=armv7-a".to_string()); + base.pre_link_args.entry(LinkerFlavor::Gcc).or_default().push("-march=armv7-a".to_string()); Target { llvm_target: "armv7-none-linux-android".to_string(), diff --git a/compiler/rustc_target/src/spec/thumbv7neon_unknown_linux_gnueabihf.rs b/compiler/rustc_target/src/spec/thumbv7neon_unknown_linux_gnueabihf.rs index 352d246874308..12d816d095b68 100644 --- a/compiler/rustc_target/src/spec/thumbv7neon_unknown_linux_gnueabihf.rs +++ b/compiler/rustc_target/src/spec/thumbv7neon_unknown_linux_gnueabihf.rs @@ -17,7 +17,6 @@ pub fn target() -> Target { options: TargetOptions { // Info about features at https://wiki.debian.org/ArmHardFloatPort features: "+v7,+thumb-mode,+thumb2,+vfp3,+neon".to_string(), - cpu: "generic".to_string(), max_atomic_width: Some(64), unsupported_abis: super::arm_base::unsupported_abis(), ..base diff --git a/compiler/rustc_target/src/spec/thumbv7neon_unknown_linux_musleabihf.rs b/compiler/rustc_target/src/spec/thumbv7neon_unknown_linux_musleabihf.rs index a788167aede07..020de87147cb6 100644 --- a/compiler/rustc_target/src/spec/thumbv7neon_unknown_linux_musleabihf.rs +++ b/compiler/rustc_target/src/spec/thumbv7neon_unknown_linux_musleabihf.rs @@ -21,7 +21,6 @@ pub fn target() -> Target { // target. options: TargetOptions { features: "+v7,+thumb-mode,+thumb2,+vfp3,+neon".to_string(), - cpu: "generic".to_string(), max_atomic_width: Some(64), unsupported_abis: super::arm_base::unsupported_abis(), mcount: "\u{1}mcount".to_string(), diff --git a/compiler/rustc_target/src/spec/uefi_msvc_base.rs b/compiler/rustc_target/src/spec/uefi_msvc_base.rs index b9ff16bd19ff4..6b6b60186010d 100644 --- a/compiler/rustc_target/src/spec/uefi_msvc_base.rs +++ b/compiler/rustc_target/src/spec/uefi_msvc_base.rs @@ -30,10 +30,10 @@ pub fn opts() -> TargetOptions { // exit (default for applications). "/subsystem:efi_application".to_string(), ]; - base.pre_link_args.get_mut(&LinkerFlavor::Msvc).unwrap().extend(pre_link_args_msvc.clone()); + base.pre_link_args.entry(LinkerFlavor::Msvc).or_default().extend(pre_link_args_msvc.clone()); base.pre_link_args - .get_mut(&LinkerFlavor::Lld(LldFlavor::Link)) - .unwrap() + .entry(LinkerFlavor::Lld(LldFlavor::Link)) + .or_default() .extend(pre_link_args_msvc); TargetOptions { diff --git a/compiler/rustc_target/src/spec/vxworks_base.rs b/compiler/rustc_target/src/spec/vxworks_base.rs index 70bc9ce3e0e2d..41c4d7625af2a 100644 --- a/compiler/rustc_target/src/spec/vxworks_base.rs +++ b/compiler/rustc_target/src/spec/vxworks_base.rs @@ -1,21 +1,6 @@ -use crate::spec::{LinkArgs, LinkerFlavor, TargetOptions}; +use crate::spec::TargetOptions; pub fn opts() -> TargetOptions { - let mut args = LinkArgs::new(); - args.insert( - LinkerFlavor::Gcc, - vec![ - // We want to be able to strip as much executable code as possible - // from the linker command line, and this flag indicates to the - // linker that it can avoid linking in dynamic libraries that don't - // actually satisfy any symbols up to that point (as with many other - // resolutions the linker does). This option only applies to all - // following libraries so we're sure to pass it as one of the first - // arguments. - "-Wl,--as-needed".to_string(), - ], - ); - TargetOptions { os: "vxworks".to_string(), env: "gnu".to_string(), @@ -27,8 +12,6 @@ pub fn opts() -> TargetOptions { os_family: Some("unix".to_string()), linker_is_gnu: true, has_rpath: true, - pre_link_args: args, - position_independent_executables: false, has_elf_tls: true, crt_static_default: true, crt_static_respected: true, diff --git a/compiler/rustc_target/src/spec/wasm32_base.rs b/compiler/rustc_target/src/spec/wasm32_base.rs deleted file mode 100644 index bfef3d37228f0..0000000000000 --- a/compiler/rustc_target/src/spec/wasm32_base.rs +++ /dev/null @@ -1,136 +0,0 @@ -use super::crt_objects::CrtObjectsFallback; -use super::{LinkerFlavor, LldFlavor, PanicStrategy, RelocModel, TargetOptions, TlsModel}; -use std::collections::BTreeMap; - -pub fn options() -> TargetOptions { - let mut lld_args = Vec::new(); - let mut clang_args = Vec::new(); - let mut arg = |arg: &str| { - lld_args.push(arg.to_string()); - clang_args.push(format!("-Wl,{}", arg)); - }; - - // By default LLD only gives us one page of stack (64k) which is a - // little small. Default to a larger stack closer to other PC platforms - // (1MB) and users can always inject their own link-args to override this. - arg("-z"); - arg("stack-size=1048576"); - - // By default LLD's memory layout is: - // - // 1. First, a blank page - // 2. Next, all static data - // 3. Finally, the main stack (which grows down) - // - // This has the unfortunate consequence that on stack overflows you - // corrupt static data and can cause some exceedingly weird bugs. To - // help detect this a little sooner we instead request that the stack is - // placed before static data. - // - // This means that we'll generate slightly larger binaries as references - // to static data will take more bytes in the ULEB128 encoding, but - // stack overflow will be guaranteed to trap as it underflows instead of - // corrupting static data. - arg("--stack-first"); - - // FIXME we probably shouldn't pass this but instead pass an explicit list - // of symbols we'll allow to be undefined. We don't currently have a - // mechanism of knowing, however, which symbols are intended to be imported - // from the environment and which are intended to be imported from other - // objects linked elsewhere. This is a coarse approximation but is sure to - // hide some bugs and frustrate someone at some point, so we should ideally - // work towards a world where we can explicitly list symbols that are - // supposed to be imported and have all other symbols generate errors if - // they remain undefined. - arg("--allow-undefined"); - - // Rust code should never have warnings, and warnings are often - // indicative of bugs, let's prevent them. - arg("--fatal-warnings"); - - // LLD only implements C++-like demangling, which doesn't match our own - // mangling scheme. Tell LLD to not demangle anything and leave it up to - // us to demangle these symbols later. Currently rustc does not perform - // further demangling, but tools like twiggy and wasm-bindgen are intended - // to do so. - arg("--no-demangle"); - - let mut pre_link_args = BTreeMap::new(); - pre_link_args.insert(LinkerFlavor::Lld(LldFlavor::Wasm), lld_args); - pre_link_args.insert(LinkerFlavor::Gcc, clang_args); - - TargetOptions { - // we allow dynamic linking, but only cdylibs. Basically we allow a - // final library artifact that exports some symbols (a wasm module) but - // we don't allow intermediate `dylib` crate types - dynamic_linking: true, - only_cdylib: true, - - // This means we'll just embed a `#[start]` function in the wasm module - executables: true, - - // relatively self-explanatory! - exe_suffix: ".wasm".to_string(), - dll_prefix: String::new(), - dll_suffix: ".wasm".to_string(), - linker_is_gnu: false, - eh_frame_header: false, - - max_atomic_width: Some(64), - - // Unwinding doesn't work right now, so the whole target unconditionally - // defaults to panic=abort. Note that this is guaranteed to change in - // the future once unwinding is implemented. Don't rely on this as we're - // basically guaranteed to change it once WebAssembly supports - // exceptions. - panic_strategy: PanicStrategy::Abort, - - // Wasm doesn't have atomics yet, so tell LLVM that we're in a single - // threaded model which will legalize atomics to normal operations. - singlethread: true, - - // no dynamic linking, no need for default visibility! - default_hidden_visibility: true, - - // Symbol visibility takes care of this for the WebAssembly. - // Additionally the only known linker, LLD, doesn't support the script - // arguments just yet - limit_rdylib_exports: false, - - // we use the LLD shipped with the Rust toolchain by default - linker: Some("rust-lld".to_owned()), - lld_flavor: LldFlavor::Wasm, - - // No need for indirection here, simd types can always be passed by - // value as the whole module either has simd or not, which is different - // from x86 (for example) where programs can have functions that don't - // enable simd features. - simd_types_indirect: false, - - pre_link_args, - - crt_objects_fallback: Some(CrtObjectsFallback::Wasm), - - // This has no effect in LLVM 8 or prior, but in LLVM 9 and later when - // PIC code is implemented this has quite a drastric effect if it stays - // at the default, `pic`. In an effort to keep wasm binaries as minimal - // as possible we're defaulting to `static` for now, but the hope is - // that eventually we can ship a `pic`-compatible standard library which - // works with `static` as well (or works with some method of generating - // non-relative calls and such later on). - relocation_model: RelocModel::Static, - - // When the atomics feature is activated then these two keys matter, - // otherwise they're basically ignored by the standard library. In this - // mode, however, the `#[thread_local]` attribute works (i.e. - // `has_elf_tls`) and we need to get it to work by specifying - // `local-exec` as that's all that's implemented in LLVM today for wasm. - has_elf_tls: true, - tls_model: TlsModel::LocalExec, - - // gdb scripts don't work on wasm blobs - emit_debug_gdb_scripts: false, - - ..Default::default() - } -} diff --git a/compiler/rustc_target/src/spec/wasm32_unknown_emscripten.rs b/compiler/rustc_target/src/spec/wasm32_unknown_emscripten.rs index 9f69ce16c215d..e028dbaa32520 100644 --- a/compiler/rustc_target/src/spec/wasm32_unknown_emscripten.rs +++ b/compiler/rustc_target/src/spec/wasm32_unknown_emscripten.rs @@ -1,10 +1,10 @@ -use super::wasm32_base; +use super::wasm_base; use super::{LinkArgs, LinkerFlavor, PanicStrategy, Target, TargetOptions}; pub fn target() -> Target { - let mut options = wasm32_base::options(); + let mut options = wasm_base::options(); - let clang_args = options.pre_link_args.get_mut(&LinkerFlavor::Gcc).unwrap(); + let clang_args = options.pre_link_args.entry(LinkerFlavor::Gcc).or_default(); // Rust really needs a way for users to specify exports and imports in // the source code. --export-dynamic isn't the right tool for this job, diff --git a/compiler/rustc_target/src/spec/wasm32_unknown_unknown.rs b/compiler/rustc_target/src/spec/wasm32_unknown_unknown.rs index 5e89ba2520bdd..834c4dbfc05f6 100644 --- a/compiler/rustc_target/src/spec/wasm32_unknown_unknown.rs +++ b/compiler/rustc_target/src/spec/wasm32_unknown_unknown.rs @@ -10,14 +10,26 @@ //! This target is more or less managed by the Rust and WebAssembly Working //! Group nowadays at . -use super::wasm32_base; +use super::wasm_base; use super::{LinkerFlavor, LldFlavor, Target}; +use crate::spec::abi::Abi; pub fn target() -> Target { - let mut options = wasm32_base::options(); + let mut options = wasm_base::options(); options.os = "unknown".to_string(); options.linker_flavor = LinkerFlavor::Lld(LldFlavor::Wasm); - let clang_args = options.pre_link_args.get_mut(&LinkerFlavor::Gcc).unwrap(); + + // This is a default for backwards-compatibility with the original + // definition of this target oh-so-long-ago. Once the "wasm" ABI is + // stable and the wasm-bindgen project has switched to using it then there's + // no need for this and it can be removed. + // + // Currently this is the reason that this target's ABI is mismatched with + // clang's ABI. This means that, in the limit, you can't merge C and Rust + // code on this target due to this ABI mismatch. + options.default_adjusted_cabi = Some(Abi::Wasm); + + let clang_args = options.pre_link_args.entry(LinkerFlavor::Gcc).or_default(); // Make sure clang uses LLD as its linker and is configured appropriately // otherwise @@ -35,7 +47,7 @@ pub fn target() -> Target { clang_args.push("-Wl,--export-dynamic".to_string()); // Add the flags to wasm-ld's args too. - let lld_args = options.pre_link_args.get_mut(&LinkerFlavor::Lld(LldFlavor::Wasm)).unwrap(); + let lld_args = options.pre_link_args.entry(LinkerFlavor::Lld(LldFlavor::Wasm)).or_default(); lld_args.push("--no-entry".to_string()); lld_args.push("--export-dynamic".to_string()); diff --git a/compiler/rustc_target/src/spec/wasm32_wasi.rs b/compiler/rustc_target/src/spec/wasm32_wasi.rs index 3f44acdc36b2d..a6b12d2ee8f65 100644 --- a/compiler/rustc_target/src/spec/wasm32_wasi.rs +++ b/compiler/rustc_target/src/spec/wasm32_wasi.rs @@ -72,11 +72,11 @@ //! best we can with this target. Don't start relying on too much here unless //! you know what you're getting in to! -use super::wasm32_base; +use super::wasm_base; use super::{crt_objects, LinkerFlavor, LldFlavor, Target}; pub fn target() -> Target { - let mut options = wasm32_base::options(); + let mut options = wasm_base::options(); options.os = "wasi".to_string(); options.linker_flavor = LinkerFlavor::Lld(LldFlavor::Wasm); diff --git a/compiler/rustc_target/src/spec/wasm64_unknown_unknown.rs b/compiler/rustc_target/src/spec/wasm64_unknown_unknown.rs new file mode 100644 index 0000000000000..8bfb229d77f67 --- /dev/null +++ b/compiler/rustc_target/src/spec/wasm64_unknown_unknown.rs @@ -0,0 +1,39 @@ +//! A "bare wasm" target representing a WebAssembly output that makes zero +//! assumptions about its environment. +//! +//! The `wasm64-unknown-unknown` target is intended to encapsulate use cases +//! that do not rely on any imported functionality. The binaries generated are +//! entirely self-contained by default when using the standard library. Although +//! the standard library is available, most of it returns an error immediately +//! (e.g. trying to create a TCP stream or something like that). + +use super::wasm_base; +use super::{LinkerFlavor, LldFlavor, Target}; + +pub fn target() -> Target { + let mut options = wasm_base::options(); + options.os = "unknown".to_string(); + options.linker_flavor = LinkerFlavor::Lld(LldFlavor::Wasm); + let clang_args = options.pre_link_args.get_mut(&LinkerFlavor::Gcc).unwrap(); + + // Make sure clang uses LLD as its linker and is configured appropriately + // otherwise + clang_args.push("--target=wasm64-unknown-unknown".to_string()); + + // For now this target just never has an entry symbol no matter the output + // type, so unconditionally pass this. + clang_args.push("-Wl,--no-entry".to_string()); + options + .pre_link_args + .get_mut(&LinkerFlavor::Lld(LldFlavor::Wasm)) + .unwrap() + .push("--no-entry".to_string()); + + Target { + llvm_target: "wasm64-unknown-unknown".to_string(), + pointer_width: 64, + data_layout: "e-m:e-p:64:64-i64:64-n32:64-S128".to_string(), + arch: "wasm64".to_string(), + options, + } +} diff --git a/compiler/rustc_target/src/spec/wasm_base.rs b/compiler/rustc_target/src/spec/wasm_base.rs new file mode 100644 index 0000000000000..b208eb92f8ff9 --- /dev/null +++ b/compiler/rustc_target/src/spec/wasm_base.rs @@ -0,0 +1,137 @@ +use super::crt_objects::CrtObjectsFallback; +use super::{LinkerFlavor, LldFlavor, PanicStrategy, RelocModel, TargetOptions, TlsModel}; +use std::collections::BTreeMap; + +pub fn options() -> TargetOptions { + let mut lld_args = Vec::new(); + let mut clang_args = Vec::new(); + let mut arg = |arg: &str| { + lld_args.push(arg.to_string()); + clang_args.push(format!("-Wl,{}", arg)); + }; + + // By default LLD only gives us one page of stack (64k) which is a + // little small. Default to a larger stack closer to other PC platforms + // (1MB) and users can always inject their own link-args to override this. + arg("-z"); + arg("stack-size=1048576"); + + // By default LLD's memory layout is: + // + // 1. First, a blank page + // 2. Next, all static data + // 3. Finally, the main stack (which grows down) + // + // This has the unfortunate consequence that on stack overflows you + // corrupt static data and can cause some exceedingly weird bugs. To + // help detect this a little sooner we instead request that the stack is + // placed before static data. + // + // This means that we'll generate slightly larger binaries as references + // to static data will take more bytes in the ULEB128 encoding, but + // stack overflow will be guaranteed to trap as it underflows instead of + // corrupting static data. + arg("--stack-first"); + + // FIXME we probably shouldn't pass this but instead pass an explicit list + // of symbols we'll allow to be undefined. We don't currently have a + // mechanism of knowing, however, which symbols are intended to be imported + // from the environment and which are intended to be imported from other + // objects linked elsewhere. This is a coarse approximation but is sure to + // hide some bugs and frustrate someone at some point, so we should ideally + // work towards a world where we can explicitly list symbols that are + // supposed to be imported and have all other symbols generate errors if + // they remain undefined. + arg("--allow-undefined"); + + // Rust code should never have warnings, and warnings are often + // indicative of bugs, let's prevent them. + arg("--fatal-warnings"); + + // LLD only implements C++-like demangling, which doesn't match our own + // mangling scheme. Tell LLD to not demangle anything and leave it up to + // us to demangle these symbols later. Currently rustc does not perform + // further demangling, but tools like twiggy and wasm-bindgen are intended + // to do so. + arg("--no-demangle"); + + let mut pre_link_args = BTreeMap::new(); + pre_link_args.insert(LinkerFlavor::Lld(LldFlavor::Wasm), lld_args); + pre_link_args.insert(LinkerFlavor::Gcc, clang_args); + + TargetOptions { + is_like_wasm: true, + + // we allow dynamic linking, but only cdylibs. Basically we allow a + // final library artifact that exports some symbols (a wasm module) but + // we don't allow intermediate `dylib` crate types + dynamic_linking: true, + only_cdylib: true, + + // This means we'll just embed a `#[start]` function in the wasm module + executables: true, + + // relatively self-explanatory! + exe_suffix: ".wasm".to_string(), + dll_prefix: String::new(), + dll_suffix: ".wasm".to_string(), + eh_frame_header: false, + + max_atomic_width: Some(64), + + // Unwinding doesn't work right now, so the whole target unconditionally + // defaults to panic=abort. Note that this is guaranteed to change in + // the future once unwinding is implemented. Don't rely on this as we're + // basically guaranteed to change it once WebAssembly supports + // exceptions. + panic_strategy: PanicStrategy::Abort, + + // Wasm doesn't have atomics yet, so tell LLVM that we're in a single + // threaded model which will legalize atomics to normal operations. + singlethread: true, + + // no dynamic linking, no need for default visibility! + default_hidden_visibility: true, + + // Symbol visibility takes care of this for the WebAssembly. + // Additionally the only known linker, LLD, doesn't support the script + // arguments just yet + limit_rdylib_exports: false, + + // we use the LLD shipped with the Rust toolchain by default + linker: Some("rust-lld".to_owned()), + lld_flavor: LldFlavor::Wasm, + + // No need for indirection here, simd types can always be passed by + // value as the whole module either has simd or not, which is different + // from x86 (for example) where programs can have functions that don't + // enable simd features. + simd_types_indirect: false, + + pre_link_args, + + crt_objects_fallback: Some(CrtObjectsFallback::Wasm), + + // This has no effect in LLVM 8 or prior, but in LLVM 9 and later when + // PIC code is implemented this has quite a drastric effect if it stays + // at the default, `pic`. In an effort to keep wasm binaries as minimal + // as possible we're defaulting to `static` for now, but the hope is + // that eventually we can ship a `pic`-compatible standard library which + // works with `static` as well (or works with some method of generating + // non-relative calls and such later on). + relocation_model: RelocModel::Static, + + // When the atomics feature is activated then these two keys matter, + // otherwise they're basically ignored by the standard library. In this + // mode, however, the `#[thread_local]` attribute works (i.e. + // `has_elf_tls`) and we need to get it to work by specifying + // `local-exec` as that's all that's implemented in LLVM today for wasm. + has_elf_tls: true, + tls_model: TlsModel::LocalExec, + + // gdb scripts don't work on wasm blobs + emit_debug_gdb_scripts: false, + + ..Default::default() + } +} diff --git a/compiler/rustc_target/src/spec/windows_gnu_base.rs b/compiler/rustc_target/src/spec/windows_gnu_base.rs index f556a13a5197f..478c567a93b25 100644 --- a/compiler/rustc_target/src/spec/windows_gnu_base.rs +++ b/compiler/rustc_target/src/spec/windows_gnu_base.rs @@ -9,8 +9,6 @@ pub fn opts() -> TargetOptions { // Tell GCC to avoid linker plugins, because we are not bundling // them with Windows installer, and Rust does its own LTO anyways. "-fno-use-linker-plugin".to_string(), - // Always enable DEP (NX bit) when it is available - "-Wl,--nxcompat".to_string(), // Enable ASLR "-Wl,--dynamicbase".to_string(), // ASLR will rebase it anyway so leaving that option enabled only leads to confusion @@ -73,8 +71,6 @@ pub fn opts() -> TargetOptions { dll_prefix: String::new(), dll_suffix: ".dll".to_string(), exe_suffix: ".exe".to_string(), - staticlib_prefix: "lib".to_string(), - staticlib_suffix: ".a".to_string(), os_family: Some("windows".to_string()), is_like_windows: true, allows_weak_linkage: false, diff --git a/compiler/rustc_target/src/spec/windows_uwp_msvc_base.rs b/compiler/rustc_target/src/spec/windows_uwp_msvc_base.rs index 700ee5ec646dd..b3fa5c22f9822 100644 --- a/compiler/rustc_target/src/spec/windows_uwp_msvc_base.rs +++ b/compiler/rustc_target/src/spec/windows_uwp_msvc_base.rs @@ -5,10 +5,10 @@ pub fn opts() -> TargetOptions { opts.vendor = "uwp".to_string(); let pre_link_args_msvc = vec!["/APPCONTAINER".to_string(), "mincore.lib".to_string()]; - opts.pre_link_args.get_mut(&LinkerFlavor::Msvc).unwrap().extend(pre_link_args_msvc.clone()); + opts.pre_link_args.entry(LinkerFlavor::Msvc).or_default().extend(pre_link_args_msvc.clone()); opts.pre_link_args - .get_mut(&LinkerFlavor::Lld(LldFlavor::Link)) - .unwrap() + .entry(LinkerFlavor::Lld(LldFlavor::Link)) + .or_default() .extend(pre_link_args_msvc); opts diff --git a/compiler/rustc_target/src/spec/x86_64_apple_darwin.rs b/compiler/rustc_target/src/spec/x86_64_apple_darwin.rs index 8c40baccda84a..c82359223da60 100644 --- a/compiler/rustc_target/src/spec/x86_64_apple_darwin.rs +++ b/compiler/rustc_target/src/spec/x86_64_apple_darwin.rs @@ -1,4 +1,4 @@ -use crate::spec::{LinkerFlavor, StackProbeType, Target, TargetOptions}; +use crate::spec::{LinkerFlavor, SanitizerSet, StackProbeType, Target, TargetOptions}; pub fn target() -> Target { let mut base = super::apple_base::opts("macos"); @@ -11,6 +11,7 @@ pub fn target() -> Target { ); base.link_env_remove.extend(super::apple_base::macos_link_env_remove()); base.stack_probes = StackProbeType::InlineOrCall { min_llvm_version_for_inline: (11, 0, 1) }; + base.supported_sanitizers = SanitizerSet::ADDRESS | SanitizerSet::LEAK | SanitizerSet::THREAD; // Clang automatically chooses a more specific target based on // MACOSX_DEPLOYMENT_TARGET. To enable cross-language LTO to work diff --git a/compiler/rustc_target/src/spec/x86_64_fortanix_unknown_sgx.rs b/compiler/rustc_target/src/spec/x86_64_fortanix_unknown_sgx.rs index 74fb6f0a8341c..90705c526f4b2 100644 --- a/compiler/rustc_target/src/spec/x86_64_fortanix_unknown_sgx.rs +++ b/compiler/rustc_target/src/spec/x86_64_fortanix_unknown_sgx.rs @@ -1,12 +1,9 @@ use std::iter; -use super::{LinkerFlavor, LldFlavor, PanicStrategy, Target, TargetOptions}; +use super::{LinkerFlavor, LldFlavor, Target, TargetOptions}; pub fn target() -> Target { const PRE_LINK_ARGS: &[&str] = &[ - "--as-needed", - "-z", - "noexecstack", "-e", "elf_entry", "-Bstatic", @@ -59,12 +56,10 @@ pub fn target() -> Target { env: "sgx".into(), vendor: "fortanix".into(), linker_flavor: LinkerFlavor::Lld(LldFlavor::Ld), - dynamic_linking: false, executables: true, linker_is_gnu: true, linker: Some("rust-lld".to_owned()), max_atomic_width: Some(64), - panic_strategy: PanicStrategy::Unwind, cpu: "x86-64".into(), features: "+rdrnd,+rdseed,+lvi-cfi,+lvi-load-hardening".into(), llvm_args: vec!["--x86-experimental-lvi-inline-asm-hardening".into()], diff --git a/compiler/rustc_target/src/spec/x86_64_fuchsia.rs b/compiler/rustc_target/src/spec/x86_64_fuchsia.rs index a39e7f8c34105..99acc7c207bc2 100644 --- a/compiler/rustc_target/src/spec/x86_64_fuchsia.rs +++ b/compiler/rustc_target/src/spec/x86_64_fuchsia.rs @@ -1,10 +1,11 @@ -use crate::spec::{StackProbeType, Target}; +use crate::spec::{SanitizerSet, StackProbeType, Target}; pub fn target() -> Target { let mut base = super::fuchsia_base::opts(); base.cpu = "x86-64".to_string(); base.max_atomic_width = Some(64); base.stack_probes = StackProbeType::InlineOrCall { min_llvm_version_for_inline: (11, 0, 1) }; + base.supported_sanitizers = SanitizerSet::ADDRESS; Target { llvm_target: "x86_64-fuchsia".to_string(), diff --git a/compiler/rustc_target/src/spec/x86_64_linux_android.rs b/compiler/rustc_target/src/spec/x86_64_linux_android.rs index d436242e62b30..0945a9f5c59a3 100644 --- a/compiler/rustc_target/src/spec/x86_64_linux_android.rs +++ b/compiler/rustc_target/src/spec/x86_64_linux_android.rs @@ -6,7 +6,7 @@ pub fn target() -> Target { // https://developer.android.com/ndk/guides/abis.html#86-64 base.features = "+mmx,+sse,+sse2,+sse3,+ssse3,+sse4.1,+sse4.2,+popcnt".to_string(); base.max_atomic_width = Some(64); - base.pre_link_args.get_mut(&LinkerFlavor::Gcc).unwrap().push("-m64".to_string()); + base.pre_link_args.entry(LinkerFlavor::Gcc).or_default().push("-m64".to_string()); base.stack_probes = StackProbeType::InlineOrCall { min_llvm_version_for_inline: (11, 0, 1) }; Target { diff --git a/compiler/rustc_target/src/spec/x86_64_linux_kernel.rs b/compiler/rustc_target/src/spec/x86_64_linux_kernel.rs deleted file mode 100644 index 43e683ddbcc84..0000000000000 --- a/compiler/rustc_target/src/spec/x86_64_linux_kernel.rs +++ /dev/null @@ -1,26 +0,0 @@ -// This defines the amd64 target for the Linux Kernel. See the linux-kernel-base module for -// generic Linux kernel options. - -use crate::spec::{CodeModel, LinkerFlavor, Target}; - -pub fn target() -> Target { - let mut base = super::linux_kernel_base::opts(); - base.cpu = "x86-64".to_string(); - base.max_atomic_width = Some(64); - base.features = - "-mmx,-sse,-sse2,-sse3,-ssse3,-sse4.1,-sse4.2,-3dnow,-3dnowa,-avx,-avx2,+soft-float" - .to_string(); - base.code_model = Some(CodeModel::Kernel); - base.pre_link_args.get_mut(&LinkerFlavor::Gcc).unwrap().push("-m64".to_string()); - - Target { - // FIXME: Some dispute, the linux-on-clang folks think this should use "Linux" - llvm_target: "x86_64-elf".to_string(), - pointer_width: 64, - data_layout: "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" - .to_string(), - arch: "x86_64".to_string(), - - options: base, - } -} diff --git a/compiler/rustc_target/src/spec/x86_64_pc_windows_gnu.rs b/compiler/rustc_target/src/spec/x86_64_pc_windows_gnu.rs index 36726ab4aedab..26a81a484b900 100644 --- a/compiler/rustc_target/src/spec/x86_64_pc_windows_gnu.rs +++ b/compiler/rustc_target/src/spec/x86_64_pc_windows_gnu.rs @@ -3,7 +3,7 @@ use crate::spec::{LinkerFlavor, LldFlavor, Target}; pub fn target() -> Target { let mut base = super::windows_gnu_base::opts(); base.cpu = "x86-64".to_string(); - let gcc_pre_link_args = base.pre_link_args.get_mut(&LinkerFlavor::Gcc).unwrap(); + let gcc_pre_link_args = base.pre_link_args.entry(LinkerFlavor::Gcc).or_default(); gcc_pre_link_args.push("-m64".to_string()); // Use high-entropy 64 bit address space for ASLR gcc_pre_link_args.push("-Wl,--high-entropy-va".to_string()); diff --git a/compiler/rustc_target/src/spec/x86_64_unknown_dragonfly.rs b/compiler/rustc_target/src/spec/x86_64_unknown_dragonfly.rs index d86b0d67acd15..295f9c837c325 100644 --- a/compiler/rustc_target/src/spec/x86_64_unknown_dragonfly.rs +++ b/compiler/rustc_target/src/spec/x86_64_unknown_dragonfly.rs @@ -4,7 +4,7 @@ pub fn target() -> Target { let mut base = super::dragonfly_base::opts(); base.cpu = "x86-64".to_string(); base.max_atomic_width = Some(64); - base.pre_link_args.get_mut(&LinkerFlavor::Gcc).unwrap().push("-m64".to_string()); + base.pre_link_args.entry(LinkerFlavor::Gcc).or_default().push("-m64".to_string()); base.stack_probes = StackProbeType::InlineOrCall { min_llvm_version_for_inline: (11, 0, 1) }; Target { diff --git a/compiler/rustc_target/src/spec/x86_64_unknown_freebsd.rs b/compiler/rustc_target/src/spec/x86_64_unknown_freebsd.rs index c7d3b3feed50e..ca3556fc48e50 100644 --- a/compiler/rustc_target/src/spec/x86_64_unknown_freebsd.rs +++ b/compiler/rustc_target/src/spec/x86_64_unknown_freebsd.rs @@ -1,11 +1,12 @@ -use crate::spec::{LinkerFlavor, StackProbeType, Target}; +use crate::spec::{LinkerFlavor, SanitizerSet, StackProbeType, Target}; pub fn target() -> Target { let mut base = super::freebsd_base::opts(); base.cpu = "x86-64".to_string(); base.max_atomic_width = Some(64); - base.pre_link_args.get_mut(&LinkerFlavor::Gcc).unwrap().push("-m64".to_string()); + base.pre_link_args.entry(LinkerFlavor::Gcc).or_default().push("-m64".to_string()); base.stack_probes = StackProbeType::InlineOrCall { min_llvm_version_for_inline: (11, 0, 1) }; + base.supported_sanitizers = SanitizerSet::ADDRESS | SanitizerSet::MEMORY | SanitizerSet::THREAD; Target { llvm_target: "x86_64-unknown-freebsd".to_string(), diff --git a/compiler/rustc_target/src/spec/x86_64_unknown_hermit_kernel.rs b/compiler/rustc_target/src/spec/x86_64_unknown_hermit_kernel.rs deleted file mode 100644 index 9fcfe4e6c14bf..0000000000000 --- a/compiler/rustc_target/src/spec/x86_64_unknown_hermit_kernel.rs +++ /dev/null @@ -1,20 +0,0 @@ -use crate::spec::{StackProbeType, Target}; - -pub fn target() -> Target { - let mut base = super::hermit_kernel_base::opts(); - base.cpu = "x86-64".to_string(); - base.max_atomic_width = Some(64); - base.features = - "-mmx,-sse,-sse2,-sse3,-ssse3,-sse4.1,-sse4.2,-3dnow,-3dnowa,-avx,-avx2,+soft-float" - .to_string(); - base.stack_probes = StackProbeType::InlineOrCall { min_llvm_version_for_inline: (11, 0, 1) }; - - Target { - llvm_target: "x86_64-unknown-hermit".to_string(), - pointer_width: 64, - data_layout: "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" - .to_string(), - arch: "x86_64".to_string(), - options: base, - } -} diff --git a/compiler/rustc_target/src/spec/x86_64_unknown_linux_gnu.rs b/compiler/rustc_target/src/spec/x86_64_unknown_linux_gnu.rs index 99906764dfc6e..9569e98ed59a8 100644 --- a/compiler/rustc_target/src/spec/x86_64_unknown_linux_gnu.rs +++ b/compiler/rustc_target/src/spec/x86_64_unknown_linux_gnu.rs @@ -1,11 +1,13 @@ -use crate::spec::{LinkerFlavor, StackProbeType, Target}; +use crate::spec::{LinkerFlavor, SanitizerSet, StackProbeType, Target}; pub fn target() -> Target { let mut base = super::linux_gnu_base::opts(); base.cpu = "x86-64".to_string(); base.max_atomic_width = Some(64); - base.pre_link_args.get_mut(&LinkerFlavor::Gcc).unwrap().push("-m64".to_string()); + base.pre_link_args.entry(LinkerFlavor::Gcc).or_default().push("-m64".to_string()); base.stack_probes = StackProbeType::InlineOrCall { min_llvm_version_for_inline: (11, 0, 1) }; + base.supported_sanitizers = + SanitizerSet::ADDRESS | SanitizerSet::LEAK | SanitizerSet::MEMORY | SanitizerSet::THREAD; Target { llvm_target: "x86_64-unknown-linux-gnu".to_string(), diff --git a/compiler/rustc_target/src/spec/x86_64_unknown_linux_gnux32.rs b/compiler/rustc_target/src/spec/x86_64_unknown_linux_gnux32.rs index 4b2bce37470cc..5f87534fe957e 100644 --- a/compiler/rustc_target/src/spec/x86_64_unknown_linux_gnux32.rs +++ b/compiler/rustc_target/src/spec/x86_64_unknown_linux_gnux32.rs @@ -4,7 +4,7 @@ pub fn target() -> Target { let mut base = super::linux_gnu_base::opts(); base.cpu = "x86-64".to_string(); base.max_atomic_width = Some(64); - base.pre_link_args.get_mut(&LinkerFlavor::Gcc).unwrap().push("-mx32".to_string()); + base.pre_link_args.entry(LinkerFlavor::Gcc).or_default().push("-mx32".to_string()); base.stack_probes = StackProbeType::InlineOrCall { min_llvm_version_for_inline: (11, 0, 1) }; base.has_elf_tls = false; // BUG(GabrielMajeri): disabling the PLT on x86_64 Linux with x32 ABI diff --git a/compiler/rustc_target/src/spec/x86_64_unknown_linux_musl.rs b/compiler/rustc_target/src/spec/x86_64_unknown_linux_musl.rs index fa9fdf5aa0992..1e2e5766a311b 100644 --- a/compiler/rustc_target/src/spec/x86_64_unknown_linux_musl.rs +++ b/compiler/rustc_target/src/spec/x86_64_unknown_linux_musl.rs @@ -1,12 +1,14 @@ -use crate::spec::{LinkerFlavor, StackProbeType, Target}; +use crate::spec::{LinkerFlavor, SanitizerSet, StackProbeType, Target}; pub fn target() -> Target { let mut base = super::linux_musl_base::opts(); base.cpu = "x86-64".to_string(); base.max_atomic_width = Some(64); - base.pre_link_args.get_mut(&LinkerFlavor::Gcc).unwrap().push("-m64".to_string()); + base.pre_link_args.entry(LinkerFlavor::Gcc).or_default().push("-m64".to_string()); base.stack_probes = StackProbeType::InlineOrCall { min_llvm_version_for_inline: (11, 0, 1) }; base.static_position_independent_executables = true; + base.supported_sanitizers = + SanitizerSet::ADDRESS | SanitizerSet::LEAK | SanitizerSet::MEMORY | SanitizerSet::THREAD; Target { llvm_target: "x86_64-unknown-linux-musl".to_string(), diff --git a/compiler/rustc_target/src/spec/x86_64_unknown_netbsd.rs b/compiler/rustc_target/src/spec/x86_64_unknown_netbsd.rs index 6d19dec00b411..54e7ceee82e8a 100644 --- a/compiler/rustc_target/src/spec/x86_64_unknown_netbsd.rs +++ b/compiler/rustc_target/src/spec/x86_64_unknown_netbsd.rs @@ -4,7 +4,7 @@ pub fn target() -> Target { let mut base = super::netbsd_base::opts(); base.cpu = "x86-64".to_string(); base.max_atomic_width = Some(64); - base.pre_link_args.get_mut(&LinkerFlavor::Gcc).unwrap().push("-m64".to_string()); + base.pre_link_args.entry(LinkerFlavor::Gcc).or_default().push("-m64".to_string()); base.stack_probes = StackProbeType::InlineOrCall { min_llvm_version_for_inline: (11, 0, 1) }; Target { diff --git a/compiler/rustc_target/src/spec/x86_64_unknown_none_hermitkernel.rs b/compiler/rustc_target/src/spec/x86_64_unknown_none_hermitkernel.rs new file mode 100644 index 0000000000000..a357def190b83 --- /dev/null +++ b/compiler/rustc_target/src/spec/x86_64_unknown_none_hermitkernel.rs @@ -0,0 +1,20 @@ +use crate::spec::{StackProbeType, Target}; + +pub fn target() -> Target { + let mut base = super::hermit_kernel_base::opts(); + base.cpu = "x86-64".to_string(); + base.max_atomic_width = Some(64); + base.features = + "-mmx,-sse,-sse2,-sse3,-ssse3,-sse4.1,-sse4.2,-3dnow,-3dnowa,-avx,-avx2,+soft-float" + .to_string(); + base.stack_probes = StackProbeType::InlineOrCall { min_llvm_version_for_inline: (11, 0, 1) }; + + Target { + llvm_target: "x86_64-unknown-none-elf".to_string(), + pointer_width: 64, + data_layout: "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" + .to_string(), + arch: "x86_64".to_string(), + options: base, + } +} diff --git a/compiler/rustc_target/src/spec/x86_64_unknown_none_linuxkernel.rs b/compiler/rustc_target/src/spec/x86_64_unknown_none_linuxkernel.rs new file mode 100644 index 0000000000000..fa6f255d4d9e5 --- /dev/null +++ b/compiler/rustc_target/src/spec/x86_64_unknown_none_linuxkernel.rs @@ -0,0 +1,29 @@ +// This defines the amd64 target for the Linux Kernel. See the linux-kernel-base module for +// generic Linux kernel options. + +use crate::spec::{CodeModel, LinkerFlavor, Target}; + +pub fn target() -> Target { + let mut base = super::linux_kernel_base::opts(); + base.cpu = "x86-64".to_string(); + base.max_atomic_width = Some(64); + base.features = + "-mmx,-sse,-sse2,-sse3,-ssse3,-sse4.1,-sse4.2,-3dnow,-3dnowa,-avx,-avx2,+soft-float" + .to_string(); + base.code_model = Some(CodeModel::Kernel); + base.pre_link_args.entry(LinkerFlavor::Gcc).or_default().push("-m64".to_string()); + + Target { + // FIXME: Some dispute, the linux-on-clang folks think this should use + // "Linux". We disagree because running *on* Linux is nothing like + // running *as" linux, and historically the "os" component as has always + // been used to mean the "on" part. + llvm_target: "x86_64-unknown-none-elf".to_string(), + pointer_width: 64, + data_layout: "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" + .to_string(), + arch: "x86_64".to_string(), + + options: base, + } +} diff --git a/compiler/rustc_target/src/spec/x86_64_unknown_openbsd.rs b/compiler/rustc_target/src/spec/x86_64_unknown_openbsd.rs index ac5939bcb3c43..530e63966aa1d 100644 --- a/compiler/rustc_target/src/spec/x86_64_unknown_openbsd.rs +++ b/compiler/rustc_target/src/spec/x86_64_unknown_openbsd.rs @@ -4,7 +4,7 @@ pub fn target() -> Target { let mut base = super::openbsd_base::opts(); base.cpu = "x86-64".to_string(); base.max_atomic_width = Some(64); - base.pre_link_args.get_mut(&LinkerFlavor::Gcc).unwrap().push("-m64".to_string()); + base.pre_link_args.entry(LinkerFlavor::Gcc).or_default().push("-m64".to_string()); base.stack_probes = StackProbeType::InlineOrCall { min_llvm_version_for_inline: (11, 0, 1) }; Target { diff --git a/compiler/rustc_target/src/spec/x86_64_unknown_redox.rs b/compiler/rustc_target/src/spec/x86_64_unknown_redox.rs index ddabe95ab8394..934f8de8ecc6b 100644 --- a/compiler/rustc_target/src/spec/x86_64_unknown_redox.rs +++ b/compiler/rustc_target/src/spec/x86_64_unknown_redox.rs @@ -4,7 +4,7 @@ pub fn target() -> Target { let mut base = super::redox_base::opts(); base.cpu = "x86-64".to_string(); base.max_atomic_width = Some(64); - base.pre_link_args.get_mut(&LinkerFlavor::Gcc).unwrap().push("-m64".to_string()); + base.pre_link_args.entry(LinkerFlavor::Gcc).or_default().push("-m64".to_string()); base.stack_probes = StackProbeType::InlineOrCall { min_llvm_version_for_inline: (11, 0, 1) }; Target { diff --git a/compiler/rustc_target/src/spec/x86_64_uwp_windows_gnu.rs b/compiler/rustc_target/src/spec/x86_64_uwp_windows_gnu.rs index 57913ba0dab6e..a5425e1c129f0 100644 --- a/compiler/rustc_target/src/spec/x86_64_uwp_windows_gnu.rs +++ b/compiler/rustc_target/src/spec/x86_64_uwp_windows_gnu.rs @@ -3,7 +3,7 @@ use crate::spec::{LinkerFlavor, LldFlavor, Target}; pub fn target() -> Target { let mut base = super::windows_uwp_gnu_base::opts(); base.cpu = "x86-64".to_string(); - let gcc_pre_link_args = base.pre_link_args.get_mut(&LinkerFlavor::Gcc).unwrap(); + let gcc_pre_link_args = base.pre_link_args.entry(LinkerFlavor::Gcc).or_default(); gcc_pre_link_args.push("-m64".to_string()); // Use high-entropy 64 bit address space for ASLR gcc_pre_link_args.push("-Wl,--high-entropy-va".to_string()); diff --git a/compiler/rustc_target/src/spec/x86_64_wrs_vxworks.rs b/compiler/rustc_target/src/spec/x86_64_wrs_vxworks.rs index 1b35e813fcd8f..f9fa9d9384304 100644 --- a/compiler/rustc_target/src/spec/x86_64_wrs_vxworks.rs +++ b/compiler/rustc_target/src/spec/x86_64_wrs_vxworks.rs @@ -4,7 +4,7 @@ pub fn target() -> Target { let mut base = super::vxworks_base::opts(); base.cpu = "x86-64".to_string(); base.max_atomic_width = Some(64); - base.pre_link_args.get_mut(&LinkerFlavor::Gcc).unwrap().push("-m64".to_string()); + base.pre_link_args.entry(LinkerFlavor::Gcc).or_default().push("-m64".to_string()); base.stack_probes = StackProbeType::InlineOrCall { min_llvm_version_for_inline: (11, 0, 1) }; base.disable_redzone = true; diff --git a/compiler/rustc_trait_selection/src/infer.rs b/compiler/rustc_trait_selection/src/infer.rs index da66fbc8587d7..a9ffb5542b6df 100644 --- a/compiler/rustc_trait_selection/src/infer.rs +++ b/compiler/rustc_trait_selection/src/infer.rs @@ -124,7 +124,7 @@ impl<'tcx> InferCtxtBuilderExt<'tcx> for InferCtxtBuilder<'tcx> { DUMMY_SP, canonical_key, |ref infcx, key, canonical_inference_vars| { - let mut fulfill_cx = TraitEngine::new(infcx.tcx); + let mut fulfill_cx = >::new(infcx.tcx); let value = operation(infcx, &mut *fulfill_cx, key)?; infcx.make_canonicalized_query_response( canonical_inference_vars, diff --git a/compiler/rustc_trait_selection/src/lib.rs b/compiler/rustc_trait_selection/src/lib.rs index e1f8d59991f21..4097e1577e112 100644 --- a/compiler/rustc_trait_selection/src/lib.rs +++ b/compiler/rustc_trait_selection/src/lib.rs @@ -15,15 +15,16 @@ #![feature(box_patterns)] #![feature(drain_filter)] #![feature(in_band_lifetimes)] +#![feature(iter_zip)] #![feature(never_type)] #![feature(crate_visibility_modifier)] -#![feature(or_patterns)] +#![cfg_attr(bootstrap, feature(or_patterns))] #![feature(control_flow_enum)] #![recursion_limit = "512"] // For rustdoc #[macro_use] extern crate rustc_macros; -#[cfg(target_arch = "x86_64")] +#[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))] #[macro_use] extern crate rustc_data_structures; #[macro_use] diff --git a/compiler/rustc_trait_selection/src/opaque_types.rs b/compiler/rustc_trait_selection/src/opaque_types.rs index 25ba489032bf1..fb4a8ce687c4d 100644 --- a/compiler/rustc_trait_selection/src/opaque_types.rs +++ b/compiler/rustc_trait_selection/src/opaque_types.rs @@ -422,7 +422,9 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { } // These opaque type inherit all lifetime parameters from their // parent, so we have to check them all. - hir::OpaqueTyOrigin::Binding | hir::OpaqueTyOrigin::Misc => 0, + hir::OpaqueTyOrigin::Binding + | hir::OpaqueTyOrigin::TyAlias + | hir::OpaqueTyOrigin::Misc => 0, }; let span = tcx.def_span(def_id); @@ -581,6 +583,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { // Otherwise, generate the label we'll use in the error message. hir::OpaqueTyOrigin::Binding | hir::OpaqueTyOrigin::FnReturn + | hir::OpaqueTyOrigin::TyAlias | hir::OpaqueTyOrigin::Misc => "impl Trait", }; let msg = format!("ambiguous lifetime bound in `{}`", context_name); @@ -694,7 +697,7 @@ where { fn visit_binder>( &mut self, - t: &ty::Binder, + t: &ty::Binder<'tcx, T>, ) -> ControlFlow { t.as_ref().skip_binder().visit_with(self); ControlFlow::CONTINUE @@ -1168,7 +1171,7 @@ impl<'a, 'tcx> Instantiator<'a, 'tcx> { // This also instantiates nested instances of `impl Trait`. let predicate = self.instantiate_opaque_types_in_map(predicate); - let cause = traits::ObligationCause::new(span, self.body_id, traits::MiscObligation); + let cause = traits::ObligationCause::new(span, self.body_id, traits::OpaqueType); // Require that the predicate holds for the concrete type. debug!("instantiate_opaque_types: predicate={:?}", predicate); diff --git a/compiler/rustc_trait_selection/src/traits/auto_trait.rs b/compiler/rustc_trait_selection/src/traits/auto_trait.rs index 6510c9464e145..f54eb0914a5a7 100644 --- a/compiler/rustc_trait_selection/src/traits/auto_trait.rs +++ b/compiler/rustc_trait_selection/src/traits/auto_trait.rs @@ -12,6 +12,7 @@ use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use std::collections::hash_map::Entry; use std::collections::VecDeque; +use std::iter; // FIXME(twk): this is obviously not nice to duplicate like that #[derive(Eq, PartialEq, Hash, Copy, Clone, Debug)] @@ -83,7 +84,7 @@ impl<'tcx> AutoTraitFinder<'tcx> { let trait_ref = ty::TraitRef { def_id: trait_did, substs: tcx.mk_substs_trait(ty, &[]) }; - let trait_pred = ty::Binder::bind(trait_ref); + let trait_pred = ty::Binder::dummy(trait_ref); let bail_out = tcx.infer_ctxt().enter(|infcx| { let mut selcx = SelectionContext::with_negative(&infcx, true); @@ -279,7 +280,7 @@ impl AutoTraitFinder<'tcx> { let mut already_visited = FxHashSet::default(); let mut predicates = VecDeque::new(); - predicates.push_back(ty::Binder::bind(ty::TraitPredicate { + predicates.push_back(ty::Binder::dummy(ty::TraitPredicate { trait_ref: ty::TraitRef { def_id: trait_did, substs: infcx.tcx.mk_substs_trait(ty, &[]), @@ -428,7 +429,9 @@ impl AutoTraitFinder<'tcx> { return true; } - for (new_region, old_region) in new_substs.regions().zip(old_substs.regions()) { + for (new_region, old_region) in + iter::zip(new_substs.regions(), old_substs.regions()) + { match (new_region, old_region) { // If both predicates have an `ReLateBound` (a HRTB) in the // same spot, we do nothing. @@ -803,12 +806,10 @@ impl AutoTraitFinder<'tcx> { } ty::PredicateKind::ConstEquate(c1, c2) => { let evaluate = |c: &'tcx ty::Const<'tcx>| { - if let ty::ConstKind::Unevaluated(def, substs, promoted) = c.val { + if let ty::ConstKind::Unevaluated(unevaluated) = c.val { match select.infcx().const_eval_resolve( obligation.param_env, - def, - substs, - promoted, + unevaluated, Some(obligation.cause.span), ) { Ok(val) => Ok(ty::Const::from_value(select.tcx(), val, c.ty)), diff --git a/compiler/rustc_trait_selection/src/traits/coherence.rs b/compiler/rustc_trait_selection/src/traits/coherence.rs index e8ae1f44a3671..9bb4af16a8f53 100644 --- a/compiler/rustc_trait_selection/src/traits/coherence.rs +++ b/compiler/rustc_trait_selection/src/traits/coherence.rs @@ -74,23 +74,22 @@ where let impl2_ref = tcx.impl_trait_ref(impl2_def_id); // Check if any of the input types definitely do not unify. - if impl1_ref - .iter() - .flat_map(|tref| tref.substs.types()) - .zip(impl2_ref.iter().flat_map(|tref| tref.substs.types())) - .any(|(ty1, ty2)| { - let t1 = fast_reject::simplify_type(tcx, ty1, false); - let t2 = fast_reject::simplify_type(tcx, ty2, false); - if let (Some(t1), Some(t2)) = (t1, t2) { - // Simplified successfully - // Types cannot unify if they differ in their reference mutability or simplify to different types - t1 != t2 || ty1.ref_mutability() != ty2.ref_mutability() - } else { - // Types might unify - false - } - }) - { + if iter::zip( + impl1_ref.iter().flat_map(|tref| tref.substs.types()), + impl2_ref.iter().flat_map(|tref| tref.substs.types()), + ) + .any(|(ty1, ty2)| { + let t1 = fast_reject::simplify_type(tcx, ty1, false); + let t2 = fast_reject::simplify_type(tcx, ty2, false); + if let (Some(t1), Some(t2)) = (t1, t2) { + // Simplified successfully + // Types cannot unify if they differ in their reference mutability or simplify to different types + t1 != t2 || ty1.ref_mutability() != ty2.ref_mutability() + } else { + // Types might unify + false + } + }) { // Some types involved are definitely different, so the impls couldn't possibly overlap. debug!("overlapping_impls: fast_reject early-exit"); return no_overlap(); @@ -587,6 +586,11 @@ fn ty_is_local_constructor(ty: Ty<'_>, in_crate: InCrate) -> bool { false } + ty::Closure(..) => { + // Similar to the `Opaque` case (#83613). + false + } + ty::Dynamic(ref tt, ..) => { if let Some(principal) = tt.principal() { def_id_is_local(principal.def_id(), in_crate) @@ -597,7 +601,7 @@ fn ty_is_local_constructor(ty: Ty<'_>, in_crate: InCrate) -> bool { ty::Error(_) => true, - ty::Closure(..) | ty::Generator(..) | ty::GeneratorWitness(..) => { + ty::Generator(..) | ty::GeneratorWitness(..) => { bug!("ty_is_local invoked on unexpected type: {:?}", ty) } } diff --git a/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs b/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs index 8a1be7ea1726d..8961cdaebf345 100644 --- a/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs +++ b/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs @@ -13,7 +13,7 @@ use rustc_hir::def::DefKind; use rustc_index::bit_set::BitSet; use rustc_index::vec::IndexVec; use rustc_infer::infer::InferCtxt; -use rustc_middle::mir::abstract_const::{Node, NodeId}; +use rustc_middle::mir::abstract_const::{Node, NodeId, NotConstEvaluatable}; use rustc_middle::mir::interpret::ErrorHandled; use rustc_middle::mir::{self, Rvalue, StatementKind, TerminatorKind}; use rustc_middle::ty::subst::{Subst, SubstsRef}; @@ -23,6 +23,7 @@ use rustc_span::def_id::{DefId, LocalDefId}; use rustc_span::Span; use std::cmp; +use std::iter; use std::ops::ControlFlow; /// Check if a given constant can be evaluated. @@ -32,7 +33,7 @@ pub fn is_const_evaluatable<'cx, 'tcx>( substs: SubstsRef<'tcx>, param_env: ty::ParamEnv<'tcx>, span: Span, -) -> Result<(), ErrorHandled> { +) -> Result<(), NotConstEvaluatable> { debug!("is_const_evaluatable({:?}, {:?})", def, substs); if infcx.tcx.features().const_evaluatable_checked { let tcx = infcx.tcx; @@ -103,29 +104,10 @@ pub fn is_const_evaluatable<'cx, 'tcx>( match failure_kind { FailureKind::MentionsInfer => { - return Err(ErrorHandled::TooGeneric); + return Err(NotConstEvaluatable::MentionsInfer); } FailureKind::MentionsParam => { - // FIXME(const_evaluatable_checked): Better error message. - let mut err = - infcx.tcx.sess.struct_span_err(span, "unconstrained generic constant"); - let const_span = tcx.def_span(def.did); - // FIXME(const_evaluatable_checked): Update this suggestion once - // explicit const evaluatable bounds are implemented. - if let Ok(snippet) = infcx.tcx.sess.source_map().span_to_snippet(const_span) - { - err.span_help( - tcx.def_span(def.did), - &format!("try adding a `where` bound using this expression: `where [u8; {}]: Sized`", snippet), - ); - } else { - err.span_help( - const_span, - "consider adding a `where` bound for this expression", - ); - } - err.emit(); - return Err(ErrorHandled::Reported(ErrorReported)); + return Err(NotConstEvaluatable::MentionsParam); } FailureKind::Concrete => { // Dealt with below by the same code which handles this @@ -163,7 +145,11 @@ pub fn is_const_evaluatable<'cx, 'tcx>( // and hopefully soon change this to an error. // // See #74595 for more details about this. - let concrete = infcx.const_eval_resolve(param_env, def, substs, None, Some(span)); + let concrete = infcx.const_eval_resolve( + param_env, + ty::Unevaluated { def, substs, promoted: None }, + Some(span), + ); if concrete.is_ok() && substs.has_param_types_or_consts() { match infcx.tcx.def_kind(def.did) { @@ -180,34 +166,16 @@ pub fn is_const_evaluatable<'cx, 'tcx>( debug!(?concrete, "is_const_evaluatable"); match concrete { - Err(ErrorHandled::TooGeneric) if !substs.has_infer_types_or_consts() => { - // FIXME(const_evaluatable_checked): We really should move - // emitting this error message to fulfill instead. For - // now this is easier. - // - // This is not a problem without `const_evaluatable_checked` as - // all `ConstEvaluatable` predicates have to be fulfilled for compilation - // to succeed. - // - // @lcnr: We already emit an error for things like - // `fn test() -> [0 - N]` eagerly here, - // so until we fix this I don't really care. - - let mut err = infcx - .tcx - .sess - .struct_span_err(span, "constant expression depends on a generic parameter"); - // FIXME(const_generics): we should suggest to the user how they can resolve this - // issue. However, this is currently not actually possible - // (see https://github.com/rust-lang/rust/issues/66962#issuecomment-575907083). - // - // Note that with `feature(const_evaluatable_checked)` this case should not - // be reachable. - err.note("this may fail depending on what value the parameter takes"); - err.emit(); - Err(ErrorHandled::Reported(ErrorReported)) + Err(ErrorHandled::TooGeneric) => Err(match substs.has_infer_types_or_consts() { + true => NotConstEvaluatable::MentionsInfer, + false => NotConstEvaluatable::MentionsParam, + }), + Err(ErrorHandled::Linted) => { + infcx.tcx.sess.delay_span_bug(span, "constant in type had error reported as lint"); + Err(NotConstEvaluatable::Error(ErrorReported)) } - c => c.map(drop), + Err(ErrorHandled::Reported(e)) => Err(NotConstEvaluatable::Error(e)), + Ok(_) => Ok(()), } } @@ -239,7 +207,9 @@ impl AbstractConst<'tcx> { ct: &ty::Const<'tcx>, ) -> Result>, ErrorReported> { match ct.val { - ty::ConstKind::Unevaluated(def, substs, None) => AbstractConst::new(tcx, def, substs), + ty::ConstKind::Unevaluated(ty::Unevaluated { def, substs, promoted: _ }) => { + AbstractConst::new(tcx, def, substs) + } ty::ConstKind::Error(_) => Err(ErrorReported), _ => Ok(None), } @@ -377,7 +347,10 @@ impl<'a, 'tcx> AbstractConstBuilder<'a, 'tcx> { let local = self.place_to_local(span, p)?; Ok(self.locals[local]) } - mir::Operand::Constant(ct) => Ok(self.add_node(Node::Leaf(ct.literal), span)), + mir::Operand::Constant(ct) => match ct.literal { + mir::ConstantKind::Ty(ct) => Ok(self.add_node(Node::Leaf(ct), span)), + mir::ConstantKind::Val(..) => self.error(Some(span), "unsupported constant")?, + }, } } @@ -411,7 +384,7 @@ impl<'a, 'tcx> AbstractConstBuilder<'a, 'tcx> { self.locals[local] = self.operand_to_node(span, operand)?; Ok(()) } - Rvalue::BinaryOp(op, ref lhs, ref rhs) if Self::check_binop(op) => { + Rvalue::BinaryOp(op, box (ref lhs, ref rhs)) if Self::check_binop(op) => { let lhs = self.operand_to_node(span, lhs)?; let rhs = self.operand_to_node(span, rhs)?; self.locals[local] = self.add_node(Node::Binop(op, lhs, rhs), span); @@ -421,7 +394,9 @@ impl<'a, 'tcx> AbstractConstBuilder<'a, 'tcx> { Ok(()) } } - Rvalue::CheckedBinaryOp(op, ref lhs, ref rhs) if Self::check_binop(op) => { + Rvalue::CheckedBinaryOp(op, box (ref lhs, ref rhs)) + if Self::check_binop(op) => + { let lhs = self.operand_to_node(span, lhs)?; let rhs = self.operand_to_node(span, rhs)?; self.locals[local] = self.add_node(Node::Binop(op, lhs, rhs), span); @@ -527,22 +502,25 @@ impl<'a, 'tcx> AbstractConstBuilder<'a, 'tcx> { if let Some(next) = self.build_terminator(block.terminator())? { block = &self.body.basic_blocks()[next]; } else { - assert_eq!(self.locals[mir::RETURN_PLACE], self.nodes.last().unwrap()); + break; + } + } + + assert_eq!(self.locals[mir::RETURN_PLACE], self.nodes.last().unwrap()); + for n in self.nodes.iter() { + if let Node::Leaf(ty::Const { val: ty::ConstKind::Unevaluated(ct), ty: _ }) = n.node { // `AbstractConst`s should not contain any promoteds as they require references which // are not allowed. - assert!(!self.nodes.iter().any(|n| matches!( - n.node, - Node::Leaf(ty::Const { val: ty::ConstKind::Unevaluated(_, _, Some(_)), ty: _ }) - ))); - - self.nodes[self.locals[mir::RETURN_PLACE]].used = true; - if let Some(&unused) = self.nodes.iter().find(|n| !n.used) { - self.error(Some(unused.span), "dead code")?; - } - - return Ok(self.tcx.arena.alloc_from_iter(self.nodes.into_iter().map(|n| n.node))); + assert_eq!(ct.promoted, None); } } + + self.nodes[self.locals[mir::RETURN_PLACE]].used = true; + if let Some(&unused) = self.nodes.iter().find(|n| !n.used) { + self.error(Some(unused.span), "dead code")?; + } + + Ok(self.tcx.arena.alloc_from_iter(self.nodes.into_iter().map(|n| n.node))) } } @@ -668,10 +646,16 @@ pub(super) fn try_unify<'tcx>( // we do not want to use `assert_eq!(a(), b())` to infer that `N` and `M` have to be `1`. This // means that we only allow inference variables if they are equal. (ty::ConstKind::Infer(a_val), ty::ConstKind::Infer(b_val)) => a_val == b_val, - ( - ty::ConstKind::Unevaluated(a_def, a_substs, None), - ty::ConstKind::Unevaluated(b_def, b_substs, None), - ) => a_def == b_def && a_substs == b_substs, + // We expand generic anonymous constants at the start of this function, so this + // branch should only be taking when dealing with associated constants, at + // which point directly comparing them seems like the desired behavior. + // + // FIXME(const_evaluatable_checked): This isn't actually the case. + // We also take this branch for concrete anonymous constants and + // expand generic anonymous constants with concrete substs. + (ty::ConstKind::Unevaluated(a_uv), ty::ConstKind::Unevaluated(b_uv)) => { + a_uv == b_uv + } // FIXME(const_evaluatable_checked): We may want to either actually try // to evaluate `a_ct` and `b_ct` if they are are fully concrete or something like // this, for now we just return false here. @@ -689,9 +673,7 @@ pub(super) fn try_unify<'tcx>( if a_args.len() == b_args.len() => { try_unify(tcx, a.subtree(a_f), b.subtree(b_f)) - && a_args - .iter() - .zip(b_args) + && iter::zip(a_args, b_args) .all(|(&an, &bn)| try_unify(tcx, a.subtree(an), b.subtree(bn))) } _ => false, 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 bfb5ebcea58b1..da5a1af7f770e 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs @@ -2,10 +2,10 @@ pub mod on_unimplemented; pub mod suggestions; use super::{ - ConstEvalFailure, EvaluationResult, FulfillmentError, FulfillmentErrorCode, - MismatchedProjectionTypes, Obligation, ObligationCause, ObligationCauseCode, - OnUnimplementedDirective, OnUnimplementedNote, OutputTypeParameterMismatch, Overflow, - PredicateObligation, SelectionContext, SelectionError, TraitNotObjectSafe, + EvaluationResult, FulfillmentError, FulfillmentErrorCode, MismatchedProjectionTypes, + Obligation, ObligationCause, ObligationCauseCode, OnUnimplementedDirective, + OnUnimplementedNote, OutputTypeParameterMismatch, Overflow, PredicateObligation, + SelectionContext, SelectionError, TraitNotObjectSafe, }; use crate::infer::error_reporting::{TyCategory, TypeAnnotationNeeded as ErrorCode}; @@ -17,7 +17,7 @@ use rustc_hir as hir; use rustc_hir::def_id::{DefId, LOCAL_CRATE}; use rustc_hir::intravisit::Visitor; use rustc_hir::Node; -use rustc_middle::mir::interpret::ErrorHandled; +use rustc_middle::mir::abstract_const::NotConstEvaluatable; use rustc_middle::ty::error::ExpectedFound; use rustc_middle::ty::fold::TypeFolder; use rustc_middle::ty::{ @@ -28,6 +28,7 @@ use rustc_session::DiagnosticMessageId; use rustc_span::symbol::{kw, sym}; use rustc_span::{ExpnKind, MultiSpan, Span, DUMMY_SP}; use std::fmt; +use std::iter; use crate::traits::query::evaluate_obligation::InferCtxtExt as _; use crate::traits::query::normalize::AtExt as _; @@ -161,7 +162,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { } } - for (error, suppressed) in errors.iter().zip(is_suppressed) { + for (error, suppressed) in iter::zip(errors, is_suppressed) { if !suppressed { self.report_fulfillment_error(error, body_id, fallback_has_occurred); } @@ -502,14 +503,14 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { let unit_obligation = obligation.with(predicate.without_const().to_predicate(tcx)); if self.predicate_may_hold(&unit_obligation) { + err.note("this trait is implemented for `()`."); err.note( - "the trait is implemented for `()`. \ - Possibly this error has been caused by changes to \ - Rust's type-inference algorithm (see issue #48950 \ - \ - for more information). Consider whether you meant to use \ - the type `()` here instead.", + "this error might have been caused by changes to \ + Rust's type-inference algorithm (see issue #48950 \ + \ + for more information).", ); + err.help("did you intend to use the type `()` here instead?"); } } @@ -738,24 +739,59 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { let violations = self.tcx.object_safety_violations(did); report_object_safety_error(self.tcx, span, did, violations) } - ConstEvalFailure(ErrorHandled::TooGeneric) => { - bug!("too generic should have been handled in `is_const_evaluatable`"); + + SelectionError::NotConstEvaluatable(NotConstEvaluatable::MentionsInfer) => { + bug!( + "MentionsInfer should have been handled in `traits/fulfill.rs` or `traits/select/mod.rs`" + ) + } + SelectionError::NotConstEvaluatable(NotConstEvaluatable::MentionsParam) => { + if !self.tcx.features().const_evaluatable_checked { + let mut err = self.tcx.sess.struct_span_err( + span, + "constant expression depends on a generic parameter", + ); + // FIXME(const_generics): we should suggest to the user how they can resolve this + // issue. However, this is currently not actually possible + // (see https://github.com/rust-lang/rust/issues/66962#issuecomment-575907083). + // + // Note that with `feature(const_evaluatable_checked)` this case should not + // be reachable. + err.note("this may fail depending on what value the parameter takes"); + err.emit(); + return; + } + + match obligation.predicate.kind().skip_binder() { + ty::PredicateKind::ConstEvaluatable(def, _) => { + let mut err = + self.tcx.sess.struct_span_err(span, "unconstrained generic constant"); + let const_span = self.tcx.def_span(def.did); + match self.tcx.sess.source_map().span_to_snippet(const_span) { + Ok(snippet) => err.help(&format!( + "try adding a `where` bound using this expression: `where [(); {}]:`", + snippet + )), + _ => err.help("consider adding a `where` bound using this expression"), + }; + err + } + _ => { + span_bug!( + span, + "unexpected non-ConstEvaluatable predicate, this should not be reachable" + ) + } + } } + // Already reported in the query. - ConstEvalFailure(ErrorHandled::Reported(ErrorReported)) => { + SelectionError::NotConstEvaluatable(NotConstEvaluatable::Error(ErrorReported)) => { // FIXME(eddyb) remove this once `ErrorReported` becomes a proof token. self.tcx.sess.delay_span_bug(span, "`ErrorReported` without an error"); return; } - // Already reported in the query, but only as a lint. - // This shouldn't actually happen for constants used in types, modulo - // bugs. The `delay_span_bug` here ensures it won't be ignored. - ConstEvalFailure(ErrorHandled::Linted) => { - self.tcx.sess.delay_span_bug(span, "constant in type had error reported as lint"); - return; - } - Overflow => { bug!("overflow should be handled before the `report_selection_error` path"); } @@ -819,7 +855,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { sig.decl .inputs .iter() - .map(|arg| match arg.clone().kind { + .map(|arg| match arg.kind { hir::TyKind::Tup(ref tys) => ArgKind::Tuple( Some(arg.span), vec![("_".to_owned(), "_".to_owned()); tys.len()], @@ -1190,10 +1226,11 @@ impl<'a, 'tcx> InferCtxtPrivExt<'tcx> for InferCtxt<'a, 'tcx> { ); let is_normalized_ty_expected = !matches!( - obligation.cause.code, + obligation.cause.code.peel_derives(), ObligationCauseCode::ItemObligation(_) | ObligationCauseCode::BindingObligation(_, _) | ObligationCauseCode::ObjectCastObligation(_) + | ObligationCauseCode::OpaqueType ); if let Err(error) = self.at(&obligation.cause, obligation.param_env).eq_exp( @@ -1917,7 +1954,7 @@ pub enum ArgKind { Arg(String, String), /// An argument of tuple type. For a "found" argument, the span is - /// the locationo in the source of the pattern. For a "expected" + /// the location in the source of the pattern. For a "expected" /// argument, it will be None. The vector is a list of (name, ty) /// strings for the components of the tuple. Tuple(Option, Vec<(String, String)>), diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/on_unimplemented.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/on_unimplemented.rs index e6a1cf58fe373..1ea34e5814e7c 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/on_unimplemented.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/on_unimplemented.rs @@ -7,6 +7,7 @@ use rustc_hir::def_id::DefId; use rustc_middle::ty::subst::Subst; use rustc_middle::ty::{self, GenericParamDefKind}; use rustc_span::symbol::sym; +use std::iter; use super::InferCtxtPrivExt; @@ -51,12 +52,11 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { if let Ok(..) = self.can_eq(param_env, trait_self_ty, impl_self_ty) { self_match_impls.push(def_id); - if trait_ref - .substs - .types() - .skip(1) - .zip(impl_trait_ref.substs.types().skip(1)) - .all(|(u, v)| self.fuzzy_match_tys(u, v)) + if iter::zip( + trait_ref.substs.types().skip(1), + impl_trait_ref.substs.types().skip(1), + ) + .all(|(u, v)| self.fuzzy_match_tys(u, v)) { fuzzy_match_impls.push(def_id); } @@ -163,61 +163,65 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { flags.push((sym::from_desugaring, None)); flags.push((sym::from_desugaring, Some(format!("{:?}", k)))); } - let generics = self.tcx.generics_of(def_id); - let self_ty = trait_ref.self_ty(); - // This is also included through the generics list as `Self`, - // but the parser won't allow you to use it - flags.push((sym::_Self, Some(self_ty.to_string()))); - if let Some(def) = self_ty.ty_adt_def() { - // We also want to be able to select self's original - // signature with no type arguments resolved - flags.push((sym::_Self, Some(self.tcx.type_of(def.did).to_string()))); - } - for param in generics.params.iter() { - let value = match param.kind { - GenericParamDefKind::Type { .. } | GenericParamDefKind::Const => { - trait_ref.substs[param.index as usize].to_string() - } - GenericParamDefKind::Lifetime => continue, - }; - let name = param.name; - flags.push((name, Some(value))); - } + // Add all types without trimmed paths. + ty::print::with_no_trimmed_paths(|| { + let generics = self.tcx.generics_of(def_id); + let self_ty = trait_ref.self_ty(); + // This is also included through the generics list as `Self`, + // but the parser won't allow you to use it + flags.push((sym::_Self, Some(self_ty.to_string()))); + if let Some(def) = self_ty.ty_adt_def() { + // We also want to be able to select self's original + // signature with no type arguments resolved + flags.push((sym::_Self, Some(self.tcx.type_of(def.did).to_string()))); + } - if let Some(true) = self_ty.ty_adt_def().map(|def| def.did.is_local()) { - flags.push((sym::crate_local, None)); - } + for param in generics.params.iter() { + let value = match param.kind { + GenericParamDefKind::Type { .. } | GenericParamDefKind::Const { .. } => { + trait_ref.substs[param.index as usize].to_string() + } + GenericParamDefKind::Lifetime => continue, + }; + let name = param.name; + flags.push((name, Some(value))); + } - // Allow targeting all integers using `{integral}`, even if the exact type was resolved - if self_ty.is_integral() { - flags.push((sym::_Self, Some("{integral}".to_owned()))); - } + if let Some(true) = self_ty.ty_adt_def().map(|def| def.did.is_local()) { + flags.push((sym::crate_local, None)); + } - if let ty::Array(aty, len) = self_ty.kind() { - flags.push((sym::_Self, Some("[]".to_owned()))); - flags.push((sym::_Self, Some(format!("[{}]", aty)))); - if let Some(def) = aty.ty_adt_def() { - // We also want to be able to select the array's type's original - // signature with no type arguments resolved - let type_string = self.tcx.type_of(def.did).to_string(); - flags.push((sym::_Self, Some(format!("[{}]", type_string)))); + // Allow targeting all integers using `{integral}`, even if the exact type was resolved + if self_ty.is_integral() { + flags.push((sym::_Self, Some("{integral}".to_owned()))); + } - let len = len.val.try_to_value().and_then(|v| v.try_to_machine_usize(self.tcx)); - let string = match len { - Some(n) => format!("[{}; {}]", type_string, n), - None => format!("[{}; _]", type_string), - }; - flags.push((sym::_Self, Some(string))); + if let ty::Array(aty, len) = self_ty.kind() { + flags.push((sym::_Self, Some("[]".to_owned()))); + flags.push((sym::_Self, Some(format!("[{}]", aty)))); + if let Some(def) = aty.ty_adt_def() { + // We also want to be able to select the array's type's original + // signature with no type arguments resolved + let type_string = self.tcx.type_of(def.did).to_string(); + flags.push((sym::_Self, Some(format!("[{}]", type_string)))); + + let len = len.val.try_to_value().and_then(|v| v.try_to_machine_usize(self.tcx)); + let string = match len { + Some(n) => format!("[{}; {}]", type_string, n), + None => format!("[{}; _]", type_string), + }; + flags.push((sym::_Self, Some(string))); + } } - } - if let ty::Dynamic(traits, _) = self_ty.kind() { - for t in traits.iter() { - if let ty::ExistentialPredicate::Trait(trait_ref) = t.skip_binder() { - flags.push((sym::_Self, Some(self.tcx.def_path_str(trait_ref.def_id)))) + if let ty::Dynamic(traits, _) = self_ty.kind() { + for t in traits.iter() { + if let ty::ExistentialPredicate::Trait(trait_ref) = t.skip_binder() { + flags.push((sym::_Self, Some(self.tcx.def_path_str(trait_ref.def_id)))) + } } } - } + }); if let Ok(Some(command)) = OnUnimplementedDirective::of_item(self.tcx, trait_ref.def_id, def_id) 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 5c97791530d99..6a4d41ffc1ac9 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs @@ -28,6 +28,7 @@ use std::fmt; use super::InferCtxtPrivExt; use crate::traits::query::evaluate_obligation::InferCtxtExt as _; +use rustc_middle::ty::print::with_no_trimmed_paths; #[derive(Debug)] pub enum GeneratorInteriorOrUpvar { @@ -65,7 +66,7 @@ pub trait InferCtxtExt<'tcx> { &self, obligation: &PredicateObligation<'tcx>, err: &mut DiagnosticBuilder<'_>, - trait_ref: ty::Binder>, + trait_ref: ty::Binder<'tcx, ty::TraitRef<'tcx>>, points_at_arg: bool, ); @@ -73,7 +74,7 @@ pub trait InferCtxtExt<'tcx> { &self, obligation: &PredicateObligation<'tcx>, err: &mut DiagnosticBuilder<'_>, - trait_ref: &ty::Binder>, + trait_ref: &ty::Binder<'tcx, ty::TraitRef<'tcx>>, points_at_arg: bool, has_custom_message: bool, ) -> bool; @@ -82,14 +83,14 @@ pub trait InferCtxtExt<'tcx> { &self, obligation: &PredicateObligation<'tcx>, err: &mut DiagnosticBuilder<'_>, - trait_ref: ty::Binder>, + trait_ref: ty::Binder<'tcx, ty::TraitRef<'tcx>>, ); fn suggest_change_mut( &self, obligation: &PredicateObligation<'tcx>, err: &mut DiagnosticBuilder<'_>, - trait_ref: ty::Binder>, + trait_ref: ty::Binder<'tcx, ty::TraitRef<'tcx>>, points_at_arg: bool, ); @@ -98,7 +99,7 @@ pub trait InferCtxtExt<'tcx> { obligation: &PredicateObligation<'tcx>, err: &mut DiagnosticBuilder<'_>, span: Span, - trait_ref: ty::Binder>, + trait_ref: ty::Binder<'tcx, ty::TraitRef<'tcx>>, ); fn return_type_span(&self, obligation: &PredicateObligation<'tcx>) -> Option; @@ -108,7 +109,7 @@ pub trait InferCtxtExt<'tcx> { err: &mut DiagnosticBuilder<'_>, span: Span, obligation: &PredicateObligation<'tcx>, - trait_ref: ty::Binder>, + trait_ref: ty::Binder<'tcx, ty::TraitRef<'tcx>>, ) -> bool; fn point_at_returns_when_relevant( @@ -170,7 +171,7 @@ pub trait InferCtxtExt<'tcx> { &self, err: &mut DiagnosticBuilder<'_>, obligation: &PredicateObligation<'tcx>, - trait_ref: ty::Binder>, + trait_ref: ty::Binder<'tcx, ty::TraitRef<'tcx>>, span: Span, ); } @@ -440,7 +441,8 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { { // Missing generic type parameter bound. let param_name = self_ty.to_string(); - let constraint = trait_ref.print_only_trait_path().to_string(); + let constraint = + with_no_trimmed_paths(|| trait_ref.print_only_trait_path().to_string()); if suggest_constraining_type_param( self.tcx, generics, @@ -583,7 +585,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { &self, obligation: &PredicateObligation<'tcx>, err: &mut DiagnosticBuilder<'_>, - trait_ref: ty::Binder>, + trait_ref: ty::Binder<'tcx, ty::TraitRef<'tcx>>, points_at_arg: bool, ) { let self_ty = match trait_ref.self_ty().no_bound_vars() { @@ -676,7 +678,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { &self, obligation: &PredicateObligation<'tcx>, err: &mut DiagnosticBuilder<'_>, - trait_ref: &ty::Binder>, + trait_ref: &ty::Binder<'tcx, ty::TraitRef<'tcx>>, points_at_arg: bool, has_custom_message: bool, ) -> bool { @@ -761,7 +763,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { &self, obligation: &PredicateObligation<'tcx>, err: &mut DiagnosticBuilder<'_>, - trait_ref: ty::Binder>, + trait_ref: ty::Binder<'tcx, ty::TraitRef<'tcx>>, ) { let span = obligation.cause.span; @@ -824,7 +826,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { &self, obligation: &PredicateObligation<'tcx>, err: &mut DiagnosticBuilder<'_>, - trait_ref: ty::Binder>, + trait_ref: ty::Binder<'tcx, ty::TraitRef<'tcx>>, points_at_arg: bool, ) { let span = obligation.cause.span; @@ -896,10 +898,10 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { obligation: &PredicateObligation<'tcx>, err: &mut DiagnosticBuilder<'_>, span: Span, - trait_ref: ty::Binder>, + trait_ref: ty::Binder<'tcx, ty::TraitRef<'tcx>>, ) { let is_empty_tuple = - |ty: ty::Binder>| *ty.skip_binder().kind() == ty::Tuple(ty::List::empty()); + |ty: ty::Binder<'tcx, Ty<'_>>| *ty.skip_binder().kind() == ty::Tuple(ty::List::empty()); let hir = self.tcx.hir(); let parent_node = hir.get_parent_node(obligation.cause.body_id); @@ -948,7 +950,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { err: &mut DiagnosticBuilder<'_>, span: Span, obligation: &PredicateObligation<'tcx>, - trait_ref: ty::Binder>, + trait_ref: ty::Binder<'tcx, ty::TraitRef<'tcx>>, ) -> bool { match obligation.cause.code.peel_derives() { // Only suggest `impl Trait` if the return type is unsized because it is `dyn Trait`. @@ -1840,6 +1842,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { | ObligationCauseCode::MethodReceiver | ObligationCauseCode::ReturnNoExpression | ObligationCauseCode::UnifyReceiver(..) + | ObligationCauseCode::OpaqueType | ObligationCauseCode::MiscObligation => {} ObligationCauseCode::SliceOrArrayElem => { err.note("slice and array elements must have `Sized` type"); @@ -1908,7 +1911,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { if is_const_fn { err.help( - "consider creating a new `const` item and initializing with the result \ + "consider creating a new `const` item and initializing it with the result \ of the function call to be used in the repeat position, like \ `const VAL: Type = const_fn();` and `let x = [VAL; 42];`", ); @@ -1916,7 +1919,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { if self.tcx.sess.is_nightly_build() && is_const_fn { err.help( - "create an inline `const` block, see PR \ + "create an inline `const` block, see RFC \ #2920 \ for more information", ); @@ -2067,7 +2070,14 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { // Don't print the tuple of capture types if !is_upvar_tys_infer_tuple { - err.note(&format!("required because it appears within the type `{}`", ty)); + let msg = format!("required because it appears within the type `{}`", ty); + match ty.kind() { + ty::Adt(def, _) => match self.tcx.opt_item_name(def.did) { + Some(ident) => err.span_note(ident.span, &msg), + None => err.note(&msg), + }, + _ => err.note(&msg), + }; } obligated_types.push(ty); @@ -2089,11 +2099,36 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { ObligationCauseCode::ImplDerivedObligation(ref data) => { let mut parent_trait_ref = self.resolve_vars_if_possible(data.parent_trait_ref); let parent_def_id = parent_trait_ref.def_id(); - err.note(&format!( + let msg = format!( "required because of the requirements on the impl of `{}` for `{}`", parent_trait_ref.print_only_trait_path(), parent_trait_ref.skip_binder().self_ty() - )); + ); + let mut candidates = vec![]; + self.tcx.for_each_relevant_impl( + parent_def_id, + parent_trait_ref.self_ty().skip_binder(), + |impl_def_id| { + candidates.push(impl_def_id); + }, + ); + match &candidates[..] { + [def_id] => match self.tcx.hir().get_if_local(*def_id) { + Some(Node::Item(hir::Item { + kind: hir::ItemKind::Impl(hir::Impl { of_trait, self_ty, .. }), + .. + })) => { + let mut spans = Vec::with_capacity(2); + if let Some(trait_ref) = of_trait { + spans.push(trait_ref.path.span); + } + spans.push(self_ty.span); + err.span_note(spans, &msg) + } + _ => err.note(&msg), + }, + _ => err.note(&msg), + }; let mut parent_predicate = parent_trait_ref.without_const().to_predicate(tcx); let mut data = data; @@ -2144,19 +2179,60 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { ) }); } - ObligationCauseCode::CompareImplMethodObligation { .. } => { - err.note(&format!( - "the requirement `{}` appears on the impl method but not on the corresponding \ - trait method", - predicate - )); + ObligationCauseCode::CompareImplMethodObligation { + item_name, + trait_item_def_id, + .. + } => { + let msg = format!( + "the requirement `{}` appears on the impl method `{}` but not on the \ + corresponding trait method", + predicate, item_name, + ); + let sp = self + .tcx + .opt_item_name(trait_item_def_id) + .map(|i| i.span) + .unwrap_or_else(|| self.tcx.def_span(trait_item_def_id)); + let mut assoc_span: MultiSpan = sp.into(); + assoc_span.push_span_label( + sp, + format!("this trait method doesn't have the requirement `{}`", predicate), + ); + if let Some(ident) = self + .tcx + .opt_associated_item(trait_item_def_id) + .and_then(|i| self.tcx.opt_item_name(i.container.id())) + { + assoc_span.push_span_label(ident.span, "in this trait".into()); + } + err.span_note(assoc_span, &msg); } - ObligationCauseCode::CompareImplTypeObligation { .. } => { - err.note(&format!( - "the requirement `{}` appears on the associated impl type but not on the \ + ObligationCauseCode::CompareImplTypeObligation { + item_name, trait_item_def_id, .. + } => { + let msg = format!( + "the requirement `{}` appears on the associated impl type `{}` but not on the \ corresponding associated trait type", - predicate - )); + predicate, item_name, + ); + let sp = self.tcx.def_span(trait_item_def_id); + let mut assoc_span: MultiSpan = sp.into(); + assoc_span.push_span_label( + sp, + format!( + "this trait associated type doesn't have the requirement `{}`", + predicate, + ), + ); + if let Some(ident) = self + .tcx + .opt_associated_item(trait_item_def_id) + .and_then(|i| self.tcx.opt_item_name(i.container.id())) + { + assoc_span.push_span_label(ident.span, "in this trait".into()); + } + err.span_note(assoc_span, &msg); } ObligationCauseCode::CompareImplConstObligation => { err.note(&format!( @@ -2190,7 +2266,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { &self, err: &mut DiagnosticBuilder<'_>, obligation: &PredicateObligation<'tcx>, - trait_ref: ty::Binder>, + trait_ref: ty::Binder<'tcx, ty::TraitRef<'tcx>>, span: Span, ) { debug!( diff --git a/compiler/rustc_trait_selection/src/traits/fulfill.rs b/compiler/rustc_trait_selection/src/traits/fulfill.rs index 3c447a7d1f9bb..fc9739f70d40d 100644 --- a/compiler/rustc_trait_selection/src/traits/fulfill.rs +++ b/compiler/rustc_trait_selection/src/traits/fulfill.rs @@ -3,7 +3,8 @@ use rustc_data_structures::obligation_forest::ProcessResult; use rustc_data_structures::obligation_forest::{Error, ForestObligation, Outcome}; use rustc_data_structures::obligation_forest::{ObligationForest, ObligationProcessor}; use rustc_errors::ErrorReported; -use rustc_infer::traits::{TraitEngine, TraitEngineExt as _, TraitObligation}; +use rustc_infer::traits::{SelectionError, TraitEngine, TraitEngineExt as _, TraitObligation}; +use rustc_middle::mir::abstract_const::NotConstEvaluatable; use rustc_middle::mir::interpret::ErrorHandled; use rustc_middle::ty::error::ExpectedFound; use rustc_middle::ty::subst::SubstsRef; @@ -18,7 +19,7 @@ use super::wf; use super::CodeAmbiguity; use super::CodeProjectionError; use super::CodeSelectionError; -use super::{ConstEvalFailure, Unimplemented}; +use super::Unimplemented; use super::{FulfillmentError, FulfillmentErrorCode}; use super::{ObligationCause, PredicateObligation}; @@ -87,7 +88,7 @@ pub struct PendingPredicateObligation<'tcx> { } // `PendingPredicateObligation` is used a lot. Make sure it doesn't unintentionally get bigger. -#[cfg(target_arch = "x86_64")] +#[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))] static_assert_size!(PendingPredicateObligation<'_>, 56); impl<'a, 'tcx> FulfillmentContext<'tcx> { @@ -498,14 +499,19 @@ impl<'a, 'b, 'tcx> FulfillProcessor<'a, 'b, 'tcx> { obligation.cause.span, ) { Ok(()) => ProcessResult::Changed(vec![]), - Err(ErrorHandled::TooGeneric) => { + Err(NotConstEvaluatable::MentionsInfer) => { pending_obligation.stalled_on.clear(); pending_obligation.stalled_on.extend( substs.iter().filter_map(TyOrConstInferVar::maybe_from_generic_arg), ); ProcessResult::Unchanged } - Err(e) => ProcessResult::Error(CodeSelectionError(ConstEvalFailure(e))), + Err( + e @ NotConstEvaluatable::MentionsParam + | e @ NotConstEvaluatable::Error(_), + ) => ProcessResult::Error(CodeSelectionError( + SelectionError::NotConstEvaluatable(e), + )), } } @@ -516,15 +522,13 @@ impl<'a, 'b, 'tcx> FulfillProcessor<'a, 'b, 'tcx> { // if the constants depend on generic parameters. // // Let's just see where this breaks :shrug: - if let ( - ty::ConstKind::Unevaluated(a_def, a_substs, None), - ty::ConstKind::Unevaluated(b_def, b_substs, None), - ) = (c1.val, c2.val) + if let (ty::ConstKind::Unevaluated(a), ty::ConstKind::Unevaluated(b)) = + (c1.val, c2.val) { if self .selcx .tcx() - .try_unify_abstract_consts(((a_def, a_substs), (b_def, b_substs))) + .try_unify_abstract_consts(((a.def, a.substs), (b.def, b.substs))) { return ProcessResult::Changed(vec![]); } @@ -534,18 +538,17 @@ impl<'a, 'b, 'tcx> FulfillProcessor<'a, 'b, 'tcx> { let stalled_on = &mut pending_obligation.stalled_on; let mut evaluate = |c: &'tcx Const<'tcx>| { - if let ty::ConstKind::Unevaluated(def, substs, promoted) = c.val { + if let ty::ConstKind::Unevaluated(unevaluated) = c.val { match self.selcx.infcx().const_eval_resolve( obligation.param_env, - def, - substs, - promoted, + unevaluated, Some(obligation.cause.span), ) { Ok(val) => Ok(Const::from_value(self.selcx.tcx(), val, c.ty)), Err(ErrorHandled::TooGeneric) => { stalled_on.extend( - substs + unevaluated + .substs .iter() .filter_map(TyOrConstInferVar::maybe_from_generic_arg), ); @@ -576,11 +579,11 @@ impl<'a, 'b, 'tcx> FulfillProcessor<'a, 'b, 'tcx> { } } (Err(ErrorHandled::Reported(ErrorReported)), _) - | (_, Err(ErrorHandled::Reported(ErrorReported))) => { - ProcessResult::Error(CodeSelectionError(ConstEvalFailure( - ErrorHandled::Reported(ErrorReported), - ))) - } + | (_, Err(ErrorHandled::Reported(ErrorReported))) => ProcessResult::Error( + CodeSelectionError(SelectionError::NotConstEvaluatable( + NotConstEvaluatable::Error(ErrorReported), + )), + ), (Err(ErrorHandled::Linted), _) | (_, Err(ErrorHandled::Linted)) => { span_bug!( obligation.cause.span(self.selcx.tcx()), @@ -681,7 +684,7 @@ impl<'a, 'b, 'tcx> FulfillProcessor<'a, 'b, 'tcx> { /// Returns the set of inference variables contained in `substs`. fn substs_infer_vars<'a, 'tcx>( selcx: &mut SelectionContext<'a, 'tcx>, - substs: ty::Binder>, + substs: ty::Binder<'tcx, SubstsRef<'tcx>>, ) -> impl Iterator> { selcx .infcx() diff --git a/compiler/rustc_trait_selection/src/traits/mod.rs b/compiler/rustc_trait_selection/src/traits/mod.rs index 5a8c53a0c4bd4..f26eb159105d7 100644 --- a/compiler/rustc_trait_selection/src/traits/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/mod.rs @@ -483,7 +483,7 @@ fn vtable_methods<'tcx>( let substs = trait_ref.map_bound(|trait_ref| { InternalSubsts::for_item(tcx, def_id, |param, _| match param.kind { GenericParamDefKind::Lifetime => tcx.lifetimes.re_erased.into(), - GenericParamDefKind::Type { .. } | GenericParamDefKind::Const => { + GenericParamDefKind::Type { .. } | GenericParamDefKind::Const { .. } => { trait_ref.substs[param.index as usize] } }) diff --git a/compiler/rustc_trait_selection/src/traits/object_safety.rs b/compiler/rustc_trait_selection/src/traits/object_safety.rs index 7de20e477fe04..ac5ec24eeeeb5 100644 --- a/compiler/rustc_trait_selection/src/traits/object_safety.rs +++ b/compiler/rustc_trait_selection/src/traits/object_safety.rs @@ -439,8 +439,7 @@ fn virtual_call_violation_for_method<'tcx>( return Some(MethodViolationCode::WhereClauseReferencesSelf); } - let receiver_ty = - tcx.liberate_late_bound_regions(method.def_id, sig.map_bound(|sig| sig.inputs()[0])); + let receiver_ty = tcx.liberate_late_bound_regions(method.def_id, sig.input(0)); // Until `unsized_locals` is fully implemented, `self: Self` can't be dispatched on. // However, this is already considered object-safe. We allow it as a special case here. @@ -757,7 +756,7 @@ fn contains_illegal_self_type_reference<'tcx, T: TypeFoldable<'tcx>>( struct IllegalSelfTypeVisitor<'tcx> { tcx: TyCtxt<'tcx>, trait_def_id: DefId, - supertraits: Option>>, + supertraits: Option>, } impl<'tcx> TypeVisitor<'tcx> for IllegalSelfTypeVisitor<'tcx> { @@ -778,8 +777,10 @@ fn contains_illegal_self_type_reference<'tcx, T: TypeFoldable<'tcx>>( // Compute supertraits of current trait lazily. if self.supertraits.is_none() { let trait_ref = - ty::Binder::bind(ty::TraitRef::identity(self.tcx, self.trait_def_id)); - self.supertraits = Some(traits::supertraits(self.tcx, trait_ref).collect()); + ty::Binder::dummy(ty::TraitRef::identity(self.tcx, self.trait_def_id)); + self.supertraits = Some( + traits::supertraits(self.tcx, trait_ref).map(|t| t.def_id()).collect(), + ); } // Determine whether the trait reference `Foo as @@ -790,9 +791,11 @@ fn contains_illegal_self_type_reference<'tcx, T: TypeFoldable<'tcx>>( // direct equality here because all of these types // are part of the formal parameter listing, and // hence there should be no inference variables. - let projection_trait_ref = ty::Binder::bind(data.trait_ref(self.tcx)); - let is_supertrait_of_current_trait = - self.supertraits.as_ref().unwrap().contains(&projection_trait_ref); + let is_supertrait_of_current_trait = self + .supertraits + .as_ref() + .unwrap() + .contains(&data.trait_ref(self.tcx).def_id); if is_supertrait_of_current_trait { ControlFlow::CONTINUE // do not walk contained types, do not report error, do collect $200 diff --git a/compiler/rustc_trait_selection/src/traits/on_unimplemented.rs b/compiler/rustc_trait_selection/src/traits/on_unimplemented.rs index 75822eadb2ab3..209fd83b3ab34 100644 --- a/compiler/rustc_trait_selection/src/traits/on_unimplemented.rs +++ b/compiler/rustc_trait_selection/src/traits/on_unimplemented.rs @@ -337,7 +337,7 @@ impl<'tcx> OnUnimplementedFormatString { .iter() .filter_map(|param| { let value = match param.kind { - GenericParamDefKind::Type { .. } | GenericParamDefKind::Const => { + GenericParamDefKind::Type { .. } | GenericParamDefKind::Const { .. } => { trait_ref.substs[param.index as usize].to_string() } GenericParamDefKind::Lifetime => return None, diff --git a/compiler/rustc_trait_selection/src/traits/project.rs b/compiler/rustc_trait_selection/src/traits/project.rs index 0af6d6459159e..b3e5df4da0a9f 100644 --- a/compiler/rustc_trait_selection/src/traits/project.rs +++ b/compiler/rustc_trait_selection/src/traits/project.rs @@ -1275,6 +1275,9 @@ fn confirm_discriminant_kind_candidate<'cx, 'tcx>( let tcx = selcx.tcx(); let self_ty = selcx.infcx().shallow_resolve(obligation.predicate.self_ty()); + // We get here from `poly_project_and_unify_type` which replaces bound vars + // with placeholders + debug_assert!(!self_ty.has_escaping_bound_vars()); let substs = tcx.mk_substs([self_ty.into()].iter()); let discriminant_def_id = tcx.require_lang_item(LangItem::Discriminant, None); @@ -1306,7 +1309,7 @@ fn confirm_pointee_candidate<'cx, 'tcx>( ty: self_ty.ptr_metadata_ty(tcx), }; - confirm_param_env_candidate(selcx, obligation, ty::Binder::bind(predicate), false) + confirm_param_env_candidate(selcx, obligation, ty::Binder::bind(predicate, tcx), false) } fn confirm_fn_pointer_candidate<'cx, 'tcx>( diff --git a/compiler/rustc_trait_selection/src/traits/query/mod.rs b/compiler/rustc_trait_selection/src/traits/query/mod.rs index 01f4f09e2381b..f6f42814d3f07 100644 --- a/compiler/rustc_trait_selection/src/traits/query/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/query/mod.rs @@ -3,7 +3,7 @@ //! which makes a canonical query by replacing unbound inference //! variables and regions, so that results can be reused more broadly. //! The providers for the queries defined here can be found in -//! `librustc_traits`. +//! `rustc_traits`. pub mod dropck_outlives; pub mod evaluate_obligation; diff --git a/compiler/rustc_trait_selection/src/traits/query/normalize.rs b/compiler/rustc_trait_selection/src/traits/query/normalize.rs index c908e1418c164..eb7ea8715c297 100644 --- a/compiler/rustc_trait_selection/src/traits/query/normalize.rs +++ b/compiler/rustc_trait_selection/src/traits/query/normalize.rs @@ -10,6 +10,7 @@ use crate::traits::{Obligation, ObligationCause, PredicateObligation, Reveal}; use rustc_data_structures::sso::SsoHashMap; use rustc_data_structures::stack::ensure_sufficient_stack; use rustc_infer::traits::Normalized; +use rustc_middle::mir; use rustc_middle::ty::fold::{TypeFoldable, TypeFolder}; use rustc_middle::ty::subst::Subst; use rustc_middle::ty::{self, Ty, TyCtxt}; @@ -214,4 +215,8 @@ impl<'cx, 'tcx> TypeFolder<'tcx> for QueryNormalizer<'cx, 'tcx> { let constant = constant.super_fold_with(self); constant.eval(self.infcx.tcx, self.param_env) } + + fn fold_mir_const(&mut self, constant: mir::ConstantKind<'tcx>) -> mir::ConstantKind<'tcx> { + constant.super_fold_with(self) + } } diff --git a/compiler/rustc_trait_selection/src/traits/query/type_op/custom.rs b/compiler/rustc_trait_selection/src/traits/query/type_op/custom.rs index 68356ce73aacd..4f5476ca5d0dc 100644 --- a/compiler/rustc_trait_selection/src/traits/query/type_op/custom.rs +++ b/compiler/rustc_trait_selection/src/traits/query/type_op/custom.rs @@ -62,7 +62,7 @@ fn scrape_region_constraints<'tcx, R>( infcx: &InferCtxt<'_, 'tcx>, op: impl FnOnce() -> Fallible>, ) -> Fallible<(R, Option>>)> { - let mut fulfill_cx = TraitEngine::new(infcx.tcx); + let mut fulfill_cx = >::new(infcx.tcx); let dummy_body_id = ObligationCause::dummy().body_id; // During NLL, we expect that nobody will register region diff --git a/compiler/rustc_trait_selection/src/traits/query/type_op/implied_outlives_bounds.rs b/compiler/rustc_trait_selection/src/traits/query/type_op/implied_outlives_bounds.rs index cf7f0a553c704..b351af44e942d 100644 --- a/compiler/rustc_trait_selection/src/traits/query/type_op/implied_outlives_bounds.rs +++ b/compiler/rustc_trait_selection/src/traits/query/type_op/implied_outlives_bounds.rs @@ -8,12 +8,6 @@ pub struct ImpliedOutlivesBounds<'tcx> { pub ty: Ty<'tcx>, } -impl<'tcx> ImpliedOutlivesBounds<'tcx> { - pub fn new(ty: Ty<'tcx>) -> Self { - ImpliedOutlivesBounds { ty } - } -} - impl<'tcx> super::QueryTypeOp<'tcx> for ImpliedOutlivesBounds<'tcx> { type QueryResponse = Vec>; diff --git a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs index 272930f6bb9ca..31685a012ca2f 100644 --- a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs +++ b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs @@ -272,7 +272,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { &mut self, obligation: &TraitObligation<'tcx>, trait_def_id: DefId, - nested: ty::Binder>>, + nested: ty::Binder<'tcx, Vec>>, ) -> ImplSourceAutoImplData> { debug!(?nested, "vtable_auto_impl"); ensure_sufficient_stack(|| { @@ -748,7 +748,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { cause, obligation.recursion_depth + 1, obligation.param_env, - ty::Binder::bind(outlives).to_predicate(tcx), + obligation.predicate.rebind(outlives).to_predicate(tcx), )); } diff --git a/compiler/rustc_trait_selection/src/traits/select/mod.rs b/compiler/rustc_trait_selection/src/traits/select/mod.rs index 45b5aff40a6d2..e4aabbdb7ede7 100644 --- a/compiler/rustc_trait_selection/src/traits/select/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/select/mod.rs @@ -34,6 +34,7 @@ use rustc_hir::def_id::DefId; use rustc_hir::Constness; use rustc_infer::infer::LateBoundRegionConversionTime; use rustc_middle::dep_graph::{DepKind, DepNodeIndex}; +use rustc_middle::mir::abstract_const::NotConstEvaluatable; use rustc_middle::mir::interpret::ErrorHandled; use rustc_middle::ty::fast_reject; use rustc_middle::ty::print::with_no_trimmed_paths; @@ -203,7 +204,7 @@ struct EvaluatedCandidate<'tcx> { /// When does the builtin impl for `T: Trait` apply? enum BuiltinImplConditions<'tcx> { /// The impl is conditional on `T1, T2, ...: Trait`. - Where(ty::Binder>>), + Where(ty::Binder<'tcx, Vec>>), /// There is no built-in impl. There may be some other /// candidate (a where-clause or user-defined impl). None, @@ -547,7 +548,8 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { obligation.cause.span, ) { Ok(()) => Ok(EvaluatedToOk), - Err(ErrorHandled::TooGeneric) => Ok(EvaluatedToAmbig), + Err(NotConstEvaluatable::MentionsInfer) => Ok(EvaluatedToAmbig), + Err(NotConstEvaluatable::MentionsParam) => Ok(EvaluatedToErr), Err(_) => Ok(EvaluatedToErr), } } @@ -556,13 +558,11 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { debug!(?c1, ?c2, "evaluate_predicate_recursively: equating consts"); let evaluate = |c: &'tcx ty::Const<'tcx>| { - if let ty::ConstKind::Unevaluated(def, substs, promoted) = c.val { + if let ty::ConstKind::Unevaluated(unevaluated) = c.val { self.infcx .const_eval_resolve( obligation.param_env, - def, - substs, - promoted, + unevaluated, Some(obligation.cause.span), ) .map(|val| ty::Const::from_value(self.tcx(), val, c.ty)) @@ -863,7 +863,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { stack: &TraitObligationStack<'o, 'tcx>, candidate: &SelectionCandidate<'tcx>, ) -> Result { - let result = self.evaluation_probe(|this| { + let mut result = self.evaluation_probe(|this| { let candidate = (*candidate).clone(); match this.confirm_candidate(stack.obligation, candidate) { Ok(selection) => { @@ -876,6 +876,19 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { Err(..) => Ok(EvaluatedToErr), } })?; + + // If we erased any lifetimes, then we want to use + // `EvaluatedToOkModuloRegions` instead of `EvaluatedToOk` + // as your final result. The result will be cached using + // the freshened trait predicate as a key, so we need + // our result to be correct by *any* choice of original lifetimes, + // not just the lifetime choice for this particular (non-erased) + // predicate. + // See issue #80691 + if stack.fresh_trait_ref.has_erased_regions() { + result = result.max(EvaluatedToOkModuloRegions); + } + debug!(?result); Ok(result) } @@ -931,7 +944,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { /// subobligations without taking in a 'parent' depth, causing the /// generated subobligations to have a `recursion_depth` of `0`. /// - /// To ensure that obligation_depth never decreasees, we force all subobligations + /// To ensure that obligation_depth never decreases, we force all subobligations /// to have at least the depth of the original obligation. fn add_depth>>( &self, @@ -968,7 +981,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { OP: FnOnce(&mut Self) -> R, { let (result, dep_node) = - self.tcx().dep_graph.with_anon_task(DepKind::TraitSelect, || op(self)); + self.tcx().dep_graph.with_anon_task(self.tcx(), DepKind::TraitSelect, || op(self)); self.tcx().dep_graph.read_index(dep_node); (result, dep_node) } @@ -1660,7 +1673,10 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { /// Bar where struct Bar { x: T, y: u32 } -> [i32, u32] /// Zed where enum Zed { A(T), B(u32) } -> [i32, u32] /// ``` - fn constituent_types_for_ty(&self, t: ty::Binder>) -> ty::Binder>> { + fn constituent_types_for_ty( + &self, + t: ty::Binder<'tcx, Ty<'tcx>>, + ) -> ty::Binder<'tcx, Vec>> { match *t.skip_binder().kind() { ty::Uint(_) | ty::Int(_) @@ -1733,7 +1749,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { cause: ObligationCause<'tcx>, recursion_depth: usize, trait_def_id: DefId, - types: ty::Binder>>, + types: ty::Binder<'tcx, Vec>>, ) -> Vec> { // Because the types were potentially derived from // higher-ranked obligations they may reference late-bound @@ -1754,7 +1770,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { .skip_binder() // binder moved -\ .iter() .flat_map(|ty| { - let ty: ty::Binder> = types.rebind(ty); // <----/ + let ty: ty::Binder<'tcx, Ty<'tcx>> = types.rebind(ty); // <----/ self.infcx.commit_unconditionally(|_| { let placeholder_ty = self.infcx.replace_bound_vars_with_placeholders(ty); @@ -1874,7 +1890,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { // substitution if we find that any of the input types, when // simplified, do not match. - obligation.predicate.skip_binder().trait_ref.substs.iter().zip(impl_trait_ref.substs).any( + iter::zip(obligation.predicate.skip_binder().trait_ref.substs, impl_trait_ref.substs).any( |(obligation_arg, impl_arg)| { match (obligation_arg.unpack(), impl_arg.unpack()) { (GenericArgKind::Type(obligation_ty), GenericArgKind::Type(impl_ty)) => { diff --git a/compiler/rustc_trait_selection/src/traits/specialize/mod.rs b/compiler/rustc_trait_selection/src/traits/specialize/mod.rs index 264cc4f248cec..4b563a87a1585 100644 --- a/compiler/rustc_trait_selection/src/traits/specialize/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/specialize/mod.rs @@ -395,14 +395,14 @@ fn report_conflicting_impls( // that's passed in. let decorate = |err: LintDiagnosticBuilder<'_>| { let msg = format!( - "conflicting implementations of trait `{}`{}:{}", + "conflicting implementations of trait `{}`{}{}", overlap.trait_desc, overlap .self_desc .clone() .map_or_else(String::new, |ty| { format!(" for type `{}`", ty) }), match used_to_be_allowed { - Some(FutureCompatOverlapErrorKind::Issue33140) => " (E0119)", + Some(FutureCompatOverlapErrorKind::Issue33140) => ": (E0119)", _ => "", } ); diff --git a/compiler/rustc_trait_selection/src/traits/util.rs b/compiler/rustc_trait_selection/src/traits/util.rs index 8888ea2c8490c..fd94f9f799847 100644 --- a/compiler/rustc_trait_selection/src/traits/util.rs +++ b/compiler/rustc_trait_selection/src/traits/util.rs @@ -328,7 +328,7 @@ pub fn closure_trait_ref_and_return_type( self_ty: Ty<'tcx>, sig: ty::PolyFnSig<'tcx>, tuple_arguments: TupleArgumentsFlag, -) -> ty::Binder<(ty::TraitRef<'tcx>, Ty<'tcx>)> { +) -> ty::Binder<'tcx, (ty::TraitRef<'tcx>, Ty<'tcx>)> { let arguments_tuple = match tuple_arguments { TupleArgumentsFlag::No => sig.skip_binder().inputs()[0], TupleArgumentsFlag::Yes => tcx.intern_tup(sig.skip_binder().inputs()), @@ -346,7 +346,7 @@ pub fn generator_trait_ref_and_outputs( fn_trait_def_id: DefId, self_ty: Ty<'tcx>, sig: ty::PolyGenSig<'tcx>, -) -> ty::Binder<(ty::TraitRef<'tcx>, Ty<'tcx>, Ty<'tcx>)> { +) -> ty::Binder<'tcx, (ty::TraitRef<'tcx>, Ty<'tcx>, Ty<'tcx>)> { debug_assert!(!self_ty.has_escaping_bound_vars()); let trait_ref = ty::TraitRef { def_id: fn_trait_def_id, diff --git a/compiler/rustc_trait_selection/src/traits/wf.rs b/compiler/rustc_trait_selection/src/traits/wf.rs index e6ef9b137d899..f592cf1cd249d 100644 --- a/compiler/rustc_trait_selection/src/traits/wf.rs +++ b/compiler/rustc_trait_selection/src/traits/wf.rs @@ -430,7 +430,7 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> { GenericArgKind::Const(constant) => { match constant.val { - ty::ConstKind::Unevaluated(def, substs, promoted) => { + ty::ConstKind::Unevaluated(ty::Unevaluated { def, substs, promoted }) => { assert!(promoted.is_none()); let obligations = self.nominal_obligations(def.did, substs); @@ -692,11 +692,7 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> { let predicates = predicates.instantiate(self.infcx.tcx, substs); debug_assert_eq!(predicates.predicates.len(), origins.len()); - predicates - .predicates - .into_iter() - .zip(predicates.spans.into_iter()) - .zip(origins.into_iter().rev()) + iter::zip(iter::zip(predicates.predicates, predicates.spans), origins.into_iter().rev()) .map(|((pred, span), origin_def_id)| { let cause = self.cause(traits::BindingObligation(origin_def_id, span)); traits::Obligation::with_depth(cause, self.recursion_depth, self.param_env, pred) @@ -708,7 +704,7 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> { fn from_object_ty( &mut self, ty: Ty<'tcx>, - data: &'tcx ty::List>>, + data: &'tcx ty::List>>, region: ty::Region<'tcx>, ) { // Imagine a type like this: @@ -771,7 +767,7 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> { /// `infer::required_region_bounds`, see that for more information. pub fn object_region_bounds<'tcx>( tcx: TyCtxt<'tcx>, - existential_predicates: &'tcx ty::List>>, + existential_predicates: &'tcx ty::List>>, ) -> Vec> { // Since we don't actually *know* the self type for an object, // this "open(err)" serves as a kind of dummy standin -- basically diff --git a/compiler/rustc_traits/src/chalk/db.rs b/compiler/rustc_traits/src/chalk/db.rs index 916186f4204e2..8c97e606c569f 100644 --- a/compiler/rustc_traits/src/chalk/db.rs +++ b/compiler/rustc_traits/src/chalk/db.rs @@ -735,11 +735,14 @@ fn bound_vars_for_item(tcx: TyCtxt<'tcx>, def_id: DefId) -> SubstsRef<'tcx> { .into(), ty::GenericParamDefKind::Lifetime => { - let br = ty::BoundRegion { kind: ty::BrAnon(substs.len() as u32) }; + let br = ty::BoundRegion { + var: ty::BoundVar::from_usize(substs.len()), + kind: ty::BrAnon(substs.len() as u32), + }; tcx.mk_region(ty::RegionKind::ReLateBound(ty::INNERMOST, br)).into() } - ty::GenericParamDefKind::Const => tcx + ty::GenericParamDefKind::Const { .. } => tcx .mk_const(ty::Const { val: ty::ConstKind::Bound(ty::INNERMOST, ty::BoundVar::from(param.index)), ty: tcx.type_of(param.def_id), diff --git a/compiler/rustc_traits/src/chalk/lowering.rs b/compiler/rustc_traits/src/chalk/lowering.rs index fdf5f697e6117..39890fd5b0574 100644 --- a/compiler/rustc_traits/src/chalk/lowering.rs +++ b/compiler/rustc_traits/src/chalk/lowering.rs @@ -434,17 +434,11 @@ impl<'tcx> LowerInto<'tcx, chalk_ir::Lifetime>> for Region<'t ReEarlyBound(_) => { panic!("Should have already been substituted."); } - ReLateBound(db, br) => match br.kind { - ty::BoundRegionKind::BrAnon(var) => { - chalk_ir::LifetimeData::BoundVar(chalk_ir::BoundVar::new( - chalk_ir::DebruijnIndex::new(db.as_u32()), - var as usize, - )) - .intern(interner) - } - ty::BoundRegionKind::BrNamed(_def_id, _name) => unimplemented!(), - ty::BrEnv => unimplemented!(), - }, + ReLateBound(db, br) => chalk_ir::LifetimeData::BoundVar(chalk_ir::BoundVar::new( + chalk_ir::DebruijnIndex::new(db.as_u32()), + br.var.as_usize(), + )) + .intern(interner), ReFree(_) => unimplemented!(), ReStatic => chalk_ir::LifetimeData::Static.intern(interner), ReVar(_) => unimplemented!(), @@ -467,7 +461,10 @@ impl<'tcx> LowerInto<'tcx, Region<'tcx>> for &chalk_ir::Lifetime ty::RegionKind::ReLateBound( ty::DebruijnIndex::from_u32(var.debruijn.depth()), - ty::BoundRegion { kind: ty::BrAnon(var.index as u32) }, + ty::BoundRegion { + var: ty::BoundVar::from_usize(var.index), + kind: ty::BrAnon(var.index as u32), + }, ), chalk_ir::LifetimeData::InferenceVar(_var) => unimplemented!(), chalk_ir::LifetimeData::Placeholder(p) => { @@ -606,7 +603,7 @@ impl<'tcx> LowerInto<'tcx, Option LowerInto<'tcx, chalk_ir::Binders>>> - for &'tcx ty::List>> + for &'tcx ty::List>> { fn lower_into( self, @@ -677,7 +674,9 @@ impl<'tcx> LowerInto<'tcx, chalk_ir::Binders LowerInto<'tcx, chalk_ir::FnSig>> for ty::Binder> { +impl<'tcx> LowerInto<'tcx, chalk_ir::FnSig>> + for ty::Binder<'tcx, ty::FnSig<'tcx>> +{ fn lower_into(self, _interner: &RustInterner<'_>) -> FnSig> { chalk_ir::FnSig { abi: self.abi(), @@ -801,7 +800,7 @@ impl<'tcx> LowerInto<'tcx, chalk_solve::rust_ir::AliasEqBound crate fn collect_bound_vars<'tcx, T: TypeFoldable<'tcx>>( interner: &RustInterner<'tcx>, tcx: TyCtxt<'tcx>, - ty: Binder, + ty: Binder<'tcx, T>, ) -> (T, chalk_ir::VariableKinds>, BTreeMap) { let mut bound_vars_collector = BoundVarsCollector::new(); ty.as_ref().skip_binder().visit_with(&mut bound_vars_collector); @@ -849,7 +848,10 @@ impl<'tcx> BoundVarsCollector<'tcx> { } impl<'tcx> TypeVisitor<'tcx> for BoundVarsCollector<'tcx> { - fn visit_binder>(&mut self, t: &Binder) -> ControlFlow { + fn visit_binder>( + &mut self, + t: &Binder<'tcx, T>, + ) -> ControlFlow { self.binder_index.shift_in(1); let result = t.super_visit_with(self); self.binder_index.shift_out(1); @@ -895,7 +897,7 @@ impl<'tcx> TypeVisitor<'tcx> for BoundVarsCollector<'tcx> { }, }, - ty::BrEnv => unimplemented!(), + ty::BoundRegionKind::BrEnv => unimplemented!(), }, ty::ReEarlyBound(_re) => { @@ -931,7 +933,7 @@ impl<'a, 'tcx> TypeFolder<'tcx> for NamedBoundVarSubstitutor<'a, 'tcx> { self.tcx } - fn fold_binder>(&mut self, t: Binder) -> Binder { + fn fold_binder>(&mut self, t: Binder<'tcx, T>) -> Binder<'tcx, T> { self.binder_index.shift_in(1); let result = t.super_fold_with(self); self.binder_index.shift_out(1); @@ -943,7 +945,7 @@ impl<'a, 'tcx> TypeFolder<'tcx> for NamedBoundVarSubstitutor<'a, 'tcx> { ty::ReLateBound(index, br) if *index == self.binder_index => match br.kind { ty::BrNamed(def_id, _name) => match self.named_parameters.get(&def_id) { Some(idx) => { - let new_br = ty::BoundRegion { kind: ty::BrAnon(*idx) }; + let new_br = ty::BoundRegion { var: br.var, kind: ty::BrAnon(*idx) }; return self.tcx.mk_region(RegionKind::ReLateBound(*index, new_br)); } None => panic!("Missing `BrNamed`."), @@ -987,7 +989,7 @@ impl<'tcx> TypeFolder<'tcx> for ParamsSubstitutor<'tcx> { self.tcx } - fn fold_binder>(&mut self, t: Binder) -> Binder { + fn fold_binder>(&mut self, t: Binder<'tcx, T>) -> Binder<'tcx, T> { self.binder_index.shift_in(1); let result = t.super_fold_with(self); self.binder_index.shift_out(1); @@ -1026,12 +1028,16 @@ impl<'tcx> TypeFolder<'tcx> for ParamsSubstitutor<'tcx> { // This covers any region variables in a goal, right? ty::ReEarlyBound(_re) => match self.named_regions.get(&_re.def_id) { Some(idx) => { - let br = ty::BoundRegion { kind: ty::BrAnon(*idx) }; + let br = ty::BoundRegion { + var: ty::BoundVar::from_u32(*idx), + kind: ty::BrAnon(*idx), + }; self.tcx.mk_region(RegionKind::ReLateBound(self.binder_index, br)) } None => { let idx = self.named_regions.len() as u32; - let br = ty::BoundRegion { kind: ty::BrAnon(idx) }; + let br = + ty::BoundRegion { var: ty::BoundVar::from_u32(idx), kind: ty::BrAnon(idx) }; self.named_regions.insert(_re.def_id, idx); self.tcx.mk_region(RegionKind::ReLateBound(self.binder_index, br)) } diff --git a/compiler/rustc_traits/src/chalk/mod.rs b/compiler/rustc_traits/src/chalk/mod.rs index d98f18182c843..b7275bac19048 100644 --- a/compiler/rustc_traits/src/chalk/mod.rs +++ b/compiler/rustc_traits/src/chalk/mod.rs @@ -165,7 +165,7 @@ crate fn evaluate_goal<'tcx>( // let's just ignore that let sol = Canonical { max_universe: ty::UniverseIndex::from_usize(0), - variables: obligation.variables.clone(), + variables: obligation.variables, value: QueryResponse { var_values: CanonicalVarValues { var_values: IndexVec::new() } .make_identity(tcx), diff --git a/compiler/rustc_traits/src/dropck_outlives.rs b/compiler/rustc_traits/src/dropck_outlives.rs index cfcbc77c172fd..9d5b9d7357fc4 100644 --- a/compiler/rustc_traits/src/dropck_outlives.rs +++ b/compiler/rustc_traits/src/dropck_outlives.rs @@ -75,7 +75,7 @@ fn dropck_outlives<'tcx>( // Set used to detect infinite recursion. let mut ty_set = FxHashSet::default(); - let mut fulfill_cx = TraitEngine::new(infcx.tcx); + let mut fulfill_cx = >::new(infcx.tcx); let cause = ObligationCause::dummy(); let mut constraints = DtorckConstraint::empty(); diff --git a/compiler/rustc_traits/src/normalize_erasing_regions.rs b/compiler/rustc_traits/src/normalize_erasing_regions.rs index 1213e5539084e..5ad0684fe6ee2 100644 --- a/compiler/rustc_traits/src/normalize_erasing_regions.rs +++ b/compiler/rustc_traits/src/normalize_erasing_regions.rs @@ -1,24 +1,35 @@ use rustc_infer::infer::TyCtxtInferExt; use rustc_middle::traits::query::NoSolution; use rustc_middle::ty::query::Providers; -use rustc_middle::ty::subst::GenericArg; use rustc_middle::ty::{self, ParamEnvAnd, TyCtxt, TypeFoldable}; use rustc_trait_selection::traits::query::normalize::AtExt; use rustc_trait_selection::traits::{Normalized, ObligationCause}; use std::sync::atomic::Ordering; crate fn provide(p: &mut Providers) { - *p = Providers { normalize_generic_arg_after_erasing_regions, ..*p }; + *p = Providers { + normalize_generic_arg_after_erasing_regions: |tcx, goal| { + debug!("normalize_generic_arg_after_erasing_regions(goal={:#?})", goal); + + tcx.sess + .perf_stats + .normalize_generic_arg_after_erasing_regions + .fetch_add(1, Ordering::Relaxed); + normalize_after_erasing_regions(tcx, goal) + }, + normalize_mir_const_after_erasing_regions: |tcx, goal| { + normalize_after_erasing_regions(tcx, goal) + }, + ..*p + }; } -fn normalize_generic_arg_after_erasing_regions<'tcx>( +#[instrument(level = "debug", skip(tcx))] +fn normalize_after_erasing_regions<'tcx, T: TypeFoldable<'tcx> + PartialEq + Copy>( tcx: TyCtxt<'tcx>, - goal: ParamEnvAnd<'tcx, GenericArg<'tcx>>, -) -> GenericArg<'tcx> { - debug!("normalize_generic_arg_after_erasing_regions(goal={:#?})", goal); - + goal: ParamEnvAnd<'tcx, T>, +) -> T { let ParamEnvAnd { param_env, value } = goal; - tcx.sess.perf_stats.normalize_generic_arg_after_erasing_regions.fetch_add(1, Ordering::Relaxed); tcx.infer_ctxt().enter(|infcx| { let cause = ObligationCause::dummy(); match infcx.at(&cause, param_env).normalize(value) { diff --git a/compiler/rustc_ty_utils/src/common_traits.rs b/compiler/rustc_ty_utils/src/common_traits.rs index 24ba071786607..cedc84d97c2d9 100644 --- a/compiler/rustc_ty_utils/src/common_traits.rs +++ b/compiler/rustc_ty_utils/src/common_traits.rs @@ -18,6 +18,10 @@ fn is_freeze_raw<'tcx>(tcx: TyCtxt<'tcx>, query: ty::ParamEnvAnd<'tcx, Ty<'tcx>> is_item_raw(tcx, query, LangItem::Freeze) } +fn is_unpin_raw<'tcx>(tcx: TyCtxt<'tcx>, query: ty::ParamEnvAnd<'tcx, Ty<'tcx>>) -> bool { + is_item_raw(tcx, query, LangItem::Unpin) +} + fn is_item_raw<'tcx>( tcx: TyCtxt<'tcx>, query: ty::ParamEnvAnd<'tcx, Ty<'tcx>>, @@ -37,5 +41,11 @@ fn is_item_raw<'tcx>( } pub(crate) fn provide(providers: &mut ty::query::Providers) { - *providers = ty::query::Providers { is_copy_raw, is_sized_raw, is_freeze_raw, ..*providers }; + *providers = ty::query::Providers { + is_copy_raw, + is_sized_raw, + is_freeze_raw, + is_unpin_raw, + ..*providers + }; } diff --git a/compiler/rustc_ty_utils/src/instance.rs b/compiler/rustc_ty_utils/src/instance.rs index 6b9d46ee0af80..874289d02938d 100644 --- a/compiler/rustc_ty_utils/src/instance.rs +++ b/compiler/rustc_ty_utils/src/instance.rs @@ -115,7 +115,7 @@ fn resolve_associated_item<'tcx>( ); let trait_ref = ty::TraitRef::from_method(tcx, trait_id, rcvr_substs); - let vtbl = tcx.codegen_fulfill_obligation((param_env, ty::Binder::bind(trait_ref)))?; + let vtbl = tcx.codegen_fulfill_obligation((param_env, ty::Binder::bind(trait_ref, tcx)))?; // Now that we know which impl is being used, we can dispatch to // the actual function: diff --git a/compiler/rustc_ty_utils/src/ty.rs b/compiler/rustc_ty_utils/src/ty.rs index 29f1761b84d2b..38e5ce6fd831c 100644 --- a/compiler/rustc_ty_utils/src/ty.rs +++ b/compiler/rustc_ty_utils/src/ty.rs @@ -210,9 +210,9 @@ fn associated_item_def_ids(tcx: TyCtxt<'_>, def_id: DefId) -> &[DefId] { } } -fn associated_items(tcx: TyCtxt<'_>, def_id: DefId) -> ty::AssociatedItems<'_> { +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::AssociatedItems::new(items) + ty::AssocItems::new(items) } fn def_ident_span(tcx: TyCtxt<'_>, def_id: DefId) -> Option { diff --git a/compiler/rustc_type_ir/src/lib.rs b/compiler/rustc_type_ir/src/lib.rs index fccd8b795ef56..8fcdf813b4137 100644 --- a/compiler/rustc_type_ir/src/lib.rs +++ b/compiler/rustc_type_ir/src/lib.rs @@ -1,7 +1,3 @@ -#![feature(never_type)] -#![feature(const_panic)] -#![feature(control_flow_enum)] - #[macro_use] extern crate bitflags; #[macro_use] diff --git a/compiler/rustc_typeck/src/astconv/errors.rs b/compiler/rustc_typeck/src/astconv/errors.rs index b5404c3a15ce0..695132281c6ba 100644 --- a/compiler/rustc_typeck/src/astconv/errors.rs +++ b/compiler/rustc_typeck/src/astconv/errors.rs @@ -10,6 +10,7 @@ use rustc_span::symbol::{sym, Ident}; use rustc_span::{Span, DUMMY_SP}; use std::collections::BTreeSet; +use std::iter; impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { /// On missing type parameters, emit an E0393 error and provide a structured suggestion using @@ -309,7 +310,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { // that the user forgot to give the associtated type's name. The canonical // example would be trying to use `Iterator` instead of // `Iterator`. - for (potential, item) in potential_assoc_types.iter().zip(assoc_items.iter()) { + for (potential, item) in iter::zip(&potential_assoc_types, assoc_items) { if let Ok(snippet) = tcx.sess.source_map().span_to_snippet(*potential) { suggestions.push((*potential, format!("{} = {}", item.ident, snippet))); } diff --git a/compiler/rustc_typeck/src/astconv/generics.rs b/compiler/rustc_typeck/src/astconv/generics.rs index 0ea0ccaceabd4..7a297f2c65f13 100644 --- a/compiler/rustc_typeck/src/astconv/generics.rs +++ b/compiler/rustc_typeck/src/astconv/generics.rs @@ -64,7 +64,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { kind: hir::TyKind::Path(rustc_hir::QPath::Resolved(_, path)), .. }), - GenericParamDefKind::Const, + GenericParamDefKind::Const { .. }, ) => match path.res { Res::Err => { add_braces_suggestion(arg, &mut err); @@ -82,7 +82,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { if param_type.is_suggestable() { err.span_suggestion( tcx.def_span(src_def_id), - "consider changing this type paramater to a `const`-generic", + "consider changing this type parameter to be a `const` generic", format!("const {}: {}", param_name, param_type), Applicability::MaybeIncorrect, ); @@ -93,7 +93,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { }, ( GenericArg::Type(hir::Ty { kind: hir::TyKind::Path(_), .. }), - GenericParamDefKind::Const, + GenericParamDefKind::Const { .. }, ) => add_braces_suggestion(arg, &mut err), ( GenericArg::Type(hir::Ty { kind: hir::TyKind::Array(_, len), .. }), @@ -166,7 +166,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { parent_substs: &[subst::GenericArg<'tcx>], has_self: bool, self_ty: Option>, - arg_count: GenericArgCountResult, + arg_count: &GenericArgCountResult, ctx: &mut impl CreateSubstsForGenericArgsCtxt<'a, 'tcx>, ) -> SubstsRef<'tcx> { // Collect the segments of the path; we need to substitute arguments @@ -236,7 +236,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { match (arg, ¶m.kind, arg_count.explicit_late_bound) { (GenericArg::Lifetime(_), GenericParamDefKind::Lifetime, _) | (GenericArg::Type(_), GenericParamDefKind::Type { .. }, _) - | (GenericArg::Const(_), GenericParamDefKind::Const, _) => { + | (GenericArg::Const(_), GenericParamDefKind::Const { .. }, _) => { substs.push(ctx.provided_kind(param, arg)); args.next(); params.next(); @@ -282,7 +282,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { GenericParamDefKind::Type { .. } => { ParamKindOrd::Type } - GenericParamDefKind::Const => { + GenericParamDefKind::Const { .. } => { ParamKindOrd::Const { unordered: tcx .features() @@ -499,7 +499,9 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { let expected_min = if infer_args { 0 } else { - param_counts.consts + named_type_param_count - default_counts.types + param_counts.consts + named_type_param_count + - default_counts.types + - default_counts.consts }; check_generics( diff --git a/compiler/rustc_typeck/src/astconv/mod.rs b/compiler/rustc_typeck/src/astconv/mod.rs index 947363fc3ed08..b6de491911ab7 100644 --- a/compiler/rustc_typeck/src/astconv/mod.rs +++ b/compiler/rustc_typeck/src/astconv/mod.rs @@ -198,6 +198,7 @@ pub trait CreateSubstsForGenericArgsCtxt<'a, 'tcx> { } impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { + #[tracing::instrument(level = "debug", skip(self))] pub fn ast_region_to_region( &self, lifetime: &hir::Lifetime, @@ -209,14 +210,20 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { let r = match tcx.named_region(lifetime.hir_id) { Some(rl::Region::Static) => tcx.lifetimes.re_static, - Some(rl::Region::LateBound(debruijn, id, _)) => { - let name = lifetime_name(id.expect_local()); - let br = ty::BoundRegion { kind: ty::BrNamed(id, name) }; + Some(rl::Region::LateBound(debruijn, index, def_id, _)) => { + let name = lifetime_name(def_id.expect_local()); + let br = ty::BoundRegion { + var: ty::BoundVar::from_u32(index), + kind: ty::BrNamed(def_id, name), + }; tcx.mk_region(ty::ReLateBound(debruijn, br)) } - Some(rl::Region::LateBoundAnon(debruijn, index)) => { - let br = ty::BoundRegion { kind: ty::BrAnon(index) }; + Some(rl::Region::LateBoundAnon(debruijn, index, anon_index)) => { + let br = ty::BoundRegion { + var: ty::BoundVar::from_u32(index), + kind: ty::BrAnon(anon_index), + }; tcx.mk_region(ty::ReLateBound(debruijn, br)) } @@ -237,6 +244,8 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { None => { self.re_infer(def, lifetime.span).unwrap_or_else(|| { + debug!(?lifetime, "unelided lifetime in signature"); + // This indicates an illegal lifetime // elision. `resolve_lifetime` should have // reported an error in this case -- but if @@ -263,7 +272,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { def_id: DefId, item_segment: &hir::PathSegment<'_>, ) -> SubstsRef<'tcx> { - let (substs, assoc_bindings, _) = self.create_substs_for_ast_path( + let (substs, _) = self.create_substs_for_ast_path( span, def_id, &[], @@ -272,6 +281,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { item_segment.infer_args, None, ); + let assoc_bindings = self.create_assoc_bindings_for_generic_args(item_segment.args()); if let Some(b) = assoc_bindings.first() { Self::prohibit_assoc_ty_binding(self.tcx(), b.span); @@ -311,6 +321,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { /// `[Vec, u8]` and `generic_args` are the arguments for the associated /// type itself: `['a]`. The returned `SubstsRef` concatenates these two /// lists: `[Vec, u8, 'a]`. + #[tracing::instrument(level = "debug", skip(self, span))] fn create_substs_for_ast_path<'a>( &self, span: Span, @@ -320,15 +331,10 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { generic_args: &'a hir::GenericArgs<'_>, infer_args: bool, self_ty: Option>, - ) -> (SubstsRef<'tcx>, Vec>, GenericArgCountResult) { + ) -> (SubstsRef<'tcx>, GenericArgCountResult) { // If the type is parameterized by this region, then replace this // region with the current anon region binding (in other words, // whatever & would get replaced with). - debug!( - "create_substs_for_ast_path(def_id={:?}, self_ty={:?}, \ - generic_args={:?})", - def_id, self_ty, generic_args - ); let tcx = self.tcx(); let generics = tcx.generics_of(def_id); @@ -364,7 +370,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { // here and so associated type bindings will be handled regardless of whether there are any // non-`Self` generic parameters. if generics.params.len() == 0 { - return (tcx.intern_substs(&[]), vec![], arg_count); + return (tcx.intern_substs(&[]), arg_count); } let is_object = self_ty.map_or(false, |ty| ty == self.tcx().types.trait_object_dummy_self); @@ -443,7 +449,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { self.astconv.ast_ty_to_ty(&ty).into() } } - (GenericParamDefKind::Const, GenericArg::Const(ct)) => { + (GenericParamDefKind::Const { .. }, GenericArg::Const(ct)) => { ty::Const::from_opt_const_arg_anon_const( tcx, ty::WithOptConstParam { @@ -504,15 +510,17 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { tcx.ty_error().into() } } - GenericParamDefKind::Const => { + GenericParamDefKind::Const { has_default } => { let ty = tcx.at(self.span).type_of(param.def_id); - // FIXME(const_generics_defaults) - if infer_args { - // No const parameters were provided, we can infer all. - self.astconv.ct_infer(ty, Some(param), self.span).into() + if !infer_args && has_default { + tcx.const_param_default(param.def_id).into() } else { - // We've already errored above about the mismatch. - tcx.const_error(ty).into() + if infer_args { + self.astconv.ct_infer(ty, Some(param), self.span).into() + } else { + // We've already errored above about the mismatch. + tcx.const_error(ty).into() + } } } } @@ -535,7 +543,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { parent_substs, self_ty.is_some(), self_ty, - arg_count.clone(), + &arg_count, &mut substs_ctx, ); @@ -546,6 +554,18 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { generic_args.args.is_empty(), ); + debug!( + "create_substs_for_ast_path(generic_params={:?}, self_ty={:?}) -> {:?}", + generics, self_ty, substs + ); + + (substs, arg_count) + } + + fn create_assoc_bindings_for_generic_args<'a>( + &self, + generic_args: &'a hir::GenericArgs<'_>, + ) -> Vec> { // Convert associated-type bindings or constraints into a separate vector. // Example: Given this: // @@ -576,12 +596,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { }) .collect(); - debug!( - "create_substs_for_ast_path(generic_params={:?}, self_ty={:?}) -> {:?}", - generics, self_ty, substs - ); - - (substs, assoc_bindings, arg_count) + assoc_bindings } crate fn create_substs_for_associated_item( @@ -631,8 +646,27 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { ) } - /// The given trait-ref must actually be a trait. - pub(super) fn instantiate_poly_trait_ref_inner( + /// Given a trait bound like `Debug`, applies that trait bound the given self-type to construct + /// a full trait reference. The resulting trait reference is returned. This may also generate + /// auxiliary bounds, which are added to `bounds`. + /// + /// Example: + /// + /// ``` + /// poly_trait_ref = Iterator + /// self_ty = Foo + /// ``` + /// + /// this would return `Foo: Iterator` and add `::Item = u32` into `bounds`. + /// + /// **A note on binders:** against our usual convention, there is an implied bounder around + /// the `self_ty` and `poly_trait_ref` parameters here. So they may reference bound regions. + /// If for example you had `for<'a> Foo<'a>: Bar<'a>`, then the `self_ty` would be `Foo<'a>` + /// where `'a` is a bound region at depth 0. Similarly, the `poly_trait_ref` would be + /// `Bar<'a>`. The returned poly-trait-ref will have this binder instantiated explicitly, + /// however. + #[tracing::instrument(level = "debug", skip(self, span, constness, bounds, speculative))] + pub fn instantiate_poly_trait_ref( &self, trait_ref: &hir::TraitRef<'_>, span: Span, @@ -643,18 +677,25 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { ) -> GenericArgCountResult { let trait_def_id = trait_ref.trait_def_id().unwrap_or_else(|| FatalError.raise()); - debug!("instantiate_poly_trait_ref({:?}, def_id={:?})", trait_ref, trait_def_id); - self.prohibit_generics(trait_ref.path.segments.split_last().unwrap().1); - let (substs, assoc_bindings, arg_count) = self.create_substs_for_ast_trait_ref( + let tcx = self.tcx(); + let bound_vars = tcx.late_bound_vars(trait_ref.hir_ref_id); + debug!(?bound_vars); + + let (substs, arg_count) = self.create_substs_for_ast_trait_ref( trait_ref.path.span, trait_def_id, self_ty, trait_ref.path.segments.last().unwrap(), ); - let poly_trait_ref = ty::Binder::bind(ty::TraitRef::new(trait_def_id, substs)); + let assoc_bindings = self + .create_assoc_bindings_for_generic_args(trait_ref.path.segments.last().unwrap().args()); + + let poly_trait_ref = + ty::Binder::bind_with_vars(ty::TraitRef::new(trait_def_id, substs), bound_vars); + debug!(?poly_trait_ref, ?assoc_bindings); bounds.trait_bounds.push((poly_trait_ref, span, constness)); let mut dup_bindings = FxHashMap::default(); @@ -672,50 +713,9 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { // Okay to ignore `Err` because of `ErrorReported` (see above). } - debug!( - "instantiate_poly_trait_ref({:?}, bounds={:?}) -> {:?}", - trait_ref, bounds, poly_trait_ref - ); - arg_count } - /// Given a trait bound like `Debug`, applies that trait bound the given self-type to construct - /// a full trait reference. The resulting trait reference is returned. This may also generate - /// auxiliary bounds, which are added to `bounds`. - /// - /// Example: - /// - /// ``` - /// poly_trait_ref = Iterator - /// self_ty = Foo - /// ``` - /// - /// this would return `Foo: Iterator` and add `::Item = u32` into `bounds`. - /// - /// **A note on binders:** against our usual convention, there is an implied bounder around - /// the `self_ty` and `poly_trait_ref` parameters here. So they may reference bound regions. - /// If for example you had `for<'a> Foo<'a>: Bar<'a>`, then the `self_ty` would be `Foo<'a>` - /// where `'a` is a bound region at depth 0. Similarly, the `poly_trait_ref` would be - /// `Bar<'a>`. The returned poly-trait-ref will have this binder instantiated explicitly, - /// however. - pub fn instantiate_poly_trait_ref( - &self, - poly_trait_ref: &hir::PolyTraitRef<'_>, - constness: Constness, - self_ty: Ty<'tcx>, - bounds: &mut Bounds<'tcx>, - ) -> GenericArgCountResult { - self.instantiate_poly_trait_ref_inner( - &poly_trait_ref.trait_ref, - poly_trait_ref.span, - constness, - self_ty, - bounds, - false, - ) - } - pub fn instantiate_lang_item_trait_ref( &self, lang_item: hir::LangItem, @@ -727,7 +727,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { ) { let trait_def_id = self.tcx().require_lang_item(lang_item, Some(span)); - let (substs, assoc_bindings, _) = self.create_substs_for_ast_path( + let (substs, _) = self.create_substs_for_ast_path( span, trait_def_id, &[], @@ -736,7 +736,11 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { false, Some(self_ty), ); - let poly_trait_ref = ty::Binder::bind(ty::TraitRef::new(trait_def_id, substs)); + let assoc_bindings = self.create_assoc_bindings_for_generic_args(args); + let tcx = self.tcx(); + let bound_vars = tcx.late_bound_vars(hir_id); + let poly_trait_ref = + ty::Binder::bind_with_vars(ty::TraitRef::new(trait_def_id, substs), bound_vars); bounds.trait_bounds.push((poly_trait_ref, span, Constness::NotConst)); let mut dup_bindings = FxHashMap::default(); @@ -760,23 +764,23 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { self_ty: Ty<'tcx>, trait_segment: &hir::PathSegment<'_>, ) -> ty::TraitRef<'tcx> { - let (substs, assoc_bindings, _) = + let (substs, _) = self.create_substs_for_ast_trait_ref(span, trait_def_id, self_ty, trait_segment); + let assoc_bindings = self.create_assoc_bindings_for_generic_args(trait_segment.args()); if let Some(b) = assoc_bindings.first() { Self::prohibit_assoc_ty_binding(self.tcx(), b.span); } ty::TraitRef::new(trait_def_id, substs) } + #[tracing::instrument(level = "debug", skip(self, span))] fn create_substs_for_ast_trait_ref<'a>( &self, span: Span, trait_def_id: DefId, self_ty: Ty<'tcx>, trait_segment: &'a hir::PathSegment<'a>, - ) -> (SubstsRef<'tcx>, Vec>, GenericArgCountResult) { - debug!("create_substs_for_ast_trait_ref(trait_segment={:?})", trait_segment); - + ) -> (SubstsRef<'tcx>, GenericArgCountResult) { self.complain_about_internal_fn_trait(span, trait_def_id, trait_segment); self.create_substs_for_ast_path( @@ -798,7 +802,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { } // Returns `true` if a bounds list includes `?Sized`. - pub fn is_unsized(&self, ast_bounds: &[&hir::GenericBound<'_>], span: Span) -> bool { + pub fn is_unsized(&self, ast_bounds: &[hir::GenericBound<'_>], span: Span) -> bool { let tcx = self.tcx(); // Try to find an unbound in bounds. @@ -853,29 +857,46 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { /// **A note on binders:** there is an implied binder around /// `param_ty` and `ast_bounds`. See `instantiate_poly_trait_ref` /// for more details. + #[tracing::instrument(level = "debug", skip(self, bounds))] fn add_bounds( &self, param_ty: Ty<'tcx>, - ast_bounds: &[&hir::GenericBound<'_>], + ast_bounds: &[hir::GenericBound<'_>], bounds: &mut Bounds<'tcx>, + bound_vars: &'tcx ty::List, ) { let constness = self.default_constness_for_trait_bounds(); for ast_bound in ast_bounds { match *ast_bound { hir::GenericBound::Trait(ref b, hir::TraitBoundModifier::None) => { - self.instantiate_poly_trait_ref(b, constness, param_ty, bounds); + self.instantiate_poly_trait_ref( + &b.trait_ref, + b.span, + constness, + param_ty, + bounds, + false, + ); } hir::GenericBound::Trait(ref b, hir::TraitBoundModifier::MaybeConst) => { - self.instantiate_poly_trait_ref(b, Constness::NotConst, param_ty, bounds); + self.instantiate_poly_trait_ref( + &b.trait_ref, + b.span, + Constness::NotConst, + param_ty, + bounds, + false, + ); } hir::GenericBound::Trait(_, hir::TraitBoundModifier::Maybe) => {} hir::GenericBound::LangItemTrait(lang_item, span, hir_id, args) => self .instantiate_lang_item_trait_ref( - *lang_item, *span, *hir_id, args, param_ty, bounds, + lang_item, span, hir_id, args, param_ty, bounds, ), - hir::GenericBound::Outlives(ref l) => bounds - .region_bounds - .push((ty::Binder::bind(self.ast_region_to_region(l, None)), l.span)), + hir::GenericBound::Outlives(ref l) => bounds.region_bounds.push(( + ty::Binder::bind_with_vars(self.ast_region_to_region(l, None), bound_vars), + l.span, + )), } } } @@ -903,7 +924,6 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { sized_by_default: SizedByDefault, span: Span, ) -> Bounds<'tcx> { - let ast_bounds: Vec<_> = ast_bounds.iter().collect(); self.compute_bounds_inner(param_ty, &ast_bounds, sized_by_default, span) } @@ -923,7 +943,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { if let Some(trait_ref) = ast_bound.trait_ref() { if let Some(trait_did) = trait_ref.trait_def_id() { if self.tcx().trait_may_define_assoc_type(trait_did, assoc_name) { - result.push(ast_bound); + result.push(ast_bound.clone()); } } } @@ -935,14 +955,13 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { fn compute_bounds_inner( &self, param_ty: Ty<'tcx>, - ast_bounds: &[&hir::GenericBound<'_>], + ast_bounds: &[hir::GenericBound<'_>], sized_by_default: SizedByDefault, span: Span, ) -> Bounds<'tcx> { let mut bounds = Bounds::default(); - self.add_bounds(param_ty, ast_bounds, &mut bounds); - bounds.trait_bounds.sort_by_key(|(t, _, _)| t.def_id()); + self.add_bounds(param_ty, ast_bounds, &mut bounds, ty::List::empty()); bounds.implicitly_sized = if let SizedByDefault::Yes = sized_by_default { if !self.is_unsized(ast_bounds, span) { Some(span) } else { None } @@ -959,6 +978,10 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { /// **A note on binders:** given something like `T: for<'a> Iterator`, the /// `trait_ref` here will be `for<'a> T: Iterator`. The `binding` data however is from *inside* /// the binder (e.g., `&'a u32`) and hence may reference bound regions. + #[tracing::instrument( + level = "debug", + skip(self, bounds, speculative, dup_bindings, path_span) + )] fn add_predicates_for_ast_type_binding( &self, hir_ref_id: hir::HirId, @@ -985,7 +1008,6 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { // // We want to produce `>::T == foo`. - debug!(?hir_ref_id, ?trait_ref, ?binding, ?bounds, "add_predicates_for_ast_type_binding",); let tcx = self.tcx(); let candidate = @@ -1086,7 +1108,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { let late_bound_in_trait_ref = tcx.collect_constrained_late_bound_regions(&projection_ty); let late_bound_in_ty = - tcx.collect_referenced_late_bound_regions(&ty::Binder::bind(ty)); + tcx.collect_referenced_late_bound_regions(&trait_ref.rebind(ty)); debug!("late_bound_in_trait_ref = {:?}", late_bound_in_trait_ref); debug!("late_bound_in_ty = {:?}", late_bound_in_ty); @@ -1135,10 +1157,8 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { // // Calling `skip_binder` is okay, because `add_bounds` expects the `param_ty` // parameter to have a skipped binder. - let param_ty = - tcx.mk_projection(assoc_ty.def_id, projection_ty.skip_binder().substs); - let ast_bounds: Vec<_> = ast_bounds.iter().collect(); - self.add_bounds(param_ty, &ast_bounds, bounds); + let param_ty = tcx.mk_ty(ty::Projection(projection_ty.skip_binder())); + self.add_bounds(param_ty, ast_bounds, bounds, candidate.bound_vars()); } } Ok(()) @@ -1172,10 +1192,12 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { Err(GenericArgCountMismatch { invalid_args: cur_potential_assoc_types, .. }), .. } = self.instantiate_poly_trait_ref( - trait_bound, + &trait_bound.trait_ref, + trait_bound.span, Constness::NotConst, dummy_self, &mut bounds, + false, ) { potential_assoc_types.extend(cur_potential_assoc_types); } @@ -1318,8 +1340,10 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { // De-duplicate auto traits so that, e.g., `dyn Trait + Send + Send` is the same as // `dyn Trait + Send`. - auto_traits.sort_by_key(|i| i.trait_ref().def_id()); - auto_traits.dedup_by_key(|i| i.trait_ref().def_id()); + // We remove duplicates by inserting into a `FxHashSet` to avoid re-ordering + // the bounds + let mut duplicates = FxHashSet::default(); + auto_traits.retain(|i| duplicates.insert(i.trait_ref().def_id())); debug!("regular_traits: {:?}", regular_traits); debug!("auto_traits: {:?}", auto_traits); @@ -1413,8 +1437,13 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { name: Symbol, ) { let mut err = struct_span_err!(self.tcx().sess, span, E0223, "ambiguous associated type"); - if let (Some(_), Ok(snippet)) = ( - self.tcx().sess.confused_type_with_std_module.borrow().get(&span), + if let (true, Ok(snippet)) = ( + self.tcx() + .sess + .confused_type_with_std_module + .borrow() + .keys() + .any(|full_span| full_span.contains(span)), self.tcx().sess.source_map().span_to_snippet(span), ) { err.span_suggestion( @@ -1602,6 +1631,8 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { // the whole path. // Will fail except for `T::A` and `Self::A`; i.e., if `qself_ty`/`qself_def` are not a type // parameter or `Self`. + // NOTE: When this function starts resolving `Trait::AssocTy` successfully + // it should also start reportint the `BARE_TRAIT_OBJECTS` lint. pub fn associated_path_to_ty( &self, hir_ref_id: hir::HirId, @@ -1651,7 +1682,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { }; self.one_bound_for_assoc_type( - || traits::supertraits(tcx, ty::Binder::bind(trait_ref)), + || traits::supertraits(tcx, ty::Binder::bind(trait_ref, tcx)), || "Self".to_string(), assoc_ident, span, @@ -2163,9 +2194,8 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { /// Turns a `hir::Ty` into a `Ty`. For diagnostics' purposes we keep track of whether trait /// objects are borrowed like `&dyn Trait` to avoid emitting redundant errors. + #[tracing::instrument(level = "debug", skip(self))] fn ast_ty_to_ty_inner(&self, ast_ty: &hir::Ty<'_>, borrowed: bool) -> Ty<'tcx> { - debug!("ast_ty_to_ty(id={:?}, ast_ty={:?} ty_ty={:?})", ast_ty.hir_id, ast_ty, ast_ty.kind); - let tcx = self.tcx(); let result_ty = match ast_ty.kind { @@ -2175,7 +2205,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { } hir::TyKind::Rptr(ref region, ref mt) => { let r = self.ast_region_to_region(region, None); - debug!("ast_ty_to_ty: r={:?}", r); + debug!(?r); let t = self.ast_ty_to_ty_inner(&mt.ty, true); tcx.mk_ref(r, ty::TypeAndMut { ty: t, mutbl: mt.mutbl }) } @@ -2187,6 +2217,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { require_c_abi_if_c_variadic(tcx, &bf.decl, bf.abi, ast_ty.span); tcx.mk_fn_ptr(self.ty_of_fn( + ast_ty.hir_id, bf.unsafety, bf.abi, &bf.decl, @@ -2195,11 +2226,11 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { Some(ast_ty), )) } - hir::TyKind::TraitObject(ref bounds, ref lifetime) => { + hir::TyKind::TraitObject(ref bounds, ref lifetime, _) => { self.conv_object_ty_poly_trait_ref(ast_ty.span, bounds, lifetime, borrowed) } hir::TyKind::Path(hir::QPath::Resolved(ref maybe_qself, ref path)) => { - debug!("ast_ty_to_ty: maybe_qself={:?} path={:?}", maybe_qself, path); + debug!(?maybe_qself, ?path); let opt_self_ty = maybe_qself.as_ref().map(|qself| self.ast_ty_to_ty(qself)); self.res_to_ty(opt_self_ty, path, false) } @@ -2215,7 +2246,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { } } hir::TyKind::Path(hir::QPath::TypeRelative(ref qself, ref segment)) => { - debug!("ast_ty_to_ty: qself={:?} segment={:?}", qself, segment); + debug!(?qself, ?segment); let ty = self.ast_ty_to_ty(qself); let res = if let hir::TyKind::Path(hir::QPath::Resolved(_, ref path)) = qself.kind { @@ -2229,7 +2260,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { } hir::TyKind::Path(hir::QPath::LangItem(lang_item, span)) => { let def_id = tcx.require_lang_item(lang_item, Some(span)); - let (substs, _, _) = self.create_substs_for_ast_path( + let (substs, _) = self.create_substs_for_ast_path( span, def_id, &[], @@ -2260,13 +2291,13 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { hir::TyKind::Err => tcx.ty_error(), }; - debug!("ast_ty_to_ty: result_ty={:?}", result_ty); + debug!(?result_ty); self.record_ty(ast_ty.hir_id, result_ty, ast_ty.span); result_ty } - pub fn impl_trait_ty_to_ty( + fn impl_trait_ty_to_ty( &self, def_id: DefId, lifetimes: &[hir::GenericArg<'_>], @@ -2327,6 +2358,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { pub fn ty_of_fn( &self, + hir_id: hir::HirId, unsafety: hir::Unsafety, abi: abi::Abi, decl: &hir::FnDecl<'_>, @@ -2337,6 +2369,8 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { debug!("ty_of_fn"); let tcx = self.tcx(); + let bound_vars = tcx.late_bound_vars(hir_id); + debug!(?bound_vars); // We proactively collect all the inferred type params to emit a single error per fn def. let mut visitor = PlaceholderHirTyCollector::default(); @@ -2356,8 +2390,8 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { debug!("ty_of_fn: output_ty={:?}", output_ty); - let bare_fn_ty = - ty::Binder::bind(tcx.mk_fn_sig(input_tys, output_ty, decl.c_variadic, unsafety, abi)); + let fn_ty = tcx.mk_fn_sig(input_tys, output_ty, decl.c_variadic, unsafety, abi); + let bare_fn_ty = ty::Binder::bind_with_vars(fn_ty, bound_vars); if !self.allow_ty_infer() { // We always collect the spans for placeholder types when evaluating `fn`s, but we @@ -2438,7 +2472,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { fn compute_object_lifetime_bound( &self, span: Span, - existential_predicates: &'tcx ty::List>>, + existential_predicates: &'tcx ty::List>>, ) -> Option> // if None, use the default { let tcx = self.tcx(); diff --git a/compiler/rustc_typeck/src/bounds.rs b/compiler/rustc_typeck/src/bounds.rs index 7ba90ad88193f..5d20064072263 100644 --- a/compiler/rustc_typeck/src/bounds.rs +++ b/compiler/rustc_typeck/src/bounds.rs @@ -26,7 +26,7 @@ pub struct Bounds<'tcx> { /// A list of region bounds on the (implicit) self type. So if you /// had `T: 'a + 'b` this might would be a list `['a, 'b]` (but /// the `T` is not explicitly included). - pub region_bounds: Vec<(ty::Binder>, Span)>, + pub region_bounds: Vec<(ty::Binder<'tcx, ty::Region<'tcx>>, Span)>, /// A list of trait bounds. So if you had `T: Debug` this would be /// `T: Debug`. Note that the self-type is explicit here. @@ -57,7 +57,7 @@ impl<'tcx> Bounds<'tcx> { // If it could be sized, and is, add the `Sized` predicate. let sized_predicate = self.implicitly_sized.and_then(|span| { tcx.lang_items().sized_trait().map(|sized| { - let trait_ref = ty::Binder::bind(ty::TraitRef { + let trait_ref = ty::Binder::dummy(ty::TraitRef { def_id: sized, substs: tcx.mk_substs_trait(param_ty, &[]), }); diff --git a/compiler/rustc_typeck/src/check/_match.rs b/compiler/rustc_typeck/src/check/_match.rs index cfd4e0830ef2a..d056f2c90f988 100644 --- a/compiler/rustc_typeck/src/check/_match.rs +++ b/compiler/rustc_typeck/src/check/_match.rs @@ -207,17 +207,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ), }; let cause = self.cause(span, code); - let can_coerce_to_return_ty = match self.ret_coercion.as_ref() { - Some(ret_coercion) if self.in_tail_expr => { - let ret_ty = ret_coercion.borrow().expected_ty(); - let ret_ty = self.inh.infcx.shallow_resolve(ret_ty); - self.can_coerce(arm_ty, ret_ty) - && prior_arm_ty.map_or(true, |t| self.can_coerce(t, ret_ty)) - // The match arms need to unify for the case of `impl Trait`. - && !matches!(ret_ty.kind(), ty::Opaque(..)) - } - _ => false, - }; // This is the moral equivalent of `coercion.coerce(self, cause, arm.body, arm_ty)`. // We use it this way to be able to expand on the potential error and detect when a @@ -229,6 +218,17 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { Some(&arm.body), arm_ty, Some(&mut |err: &mut DiagnosticBuilder<'_>| { + let can_coerce_to_return_ty = match self.ret_coercion.as_ref() { + Some(ret_coercion) if self.in_tail_expr => { + let ret_ty = ret_coercion.borrow().expected_ty(); + let ret_ty = self.inh.infcx.shallow_resolve(ret_ty); + self.can_coerce(arm_ty, ret_ty) + && prior_arm_ty.map_or(true, |t| self.can_coerce(t, ret_ty)) + // The match arms need to unify for the case of `impl Trait`. + && !matches!(ret_ty.kind(), ty::Opaque(..)) + } + _ => false, + }; if let (Expectation::IsLast(stmt), Some(ret), true) = (orig_expected, self.ret_type_span, can_coerce_to_return_ty) { diff --git a/compiler/rustc_typeck/src/check/callee.rs b/compiler/rustc_typeck/src/check/callee.rs index ca1e79fac73e9..b48102e0fc9b8 100644 --- a/compiler/rustc_typeck/src/check/callee.rs +++ b/compiler/rustc_typeck/src/check/callee.rs @@ -17,6 +17,7 @@ use rustc_span::symbol::{sym, Ident}; use rustc_span::Span; use rustc_target::spec::abi; use rustc_trait_selection::autoderef::Autoderef; +use std::iter; /// Checks that it is legal to call methods of the trait corresponding /// to `trait_id` (this only cares about the trait, not the specific @@ -77,11 +78,17 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let output = match result { None => { // this will report an error since original_callee_ty is not a fn - self.confirm_builtin_call(call_expr, original_callee_ty, arg_exprs, expected) + self.confirm_builtin_call( + call_expr, + callee_expr, + original_callee_ty, + arg_exprs, + expected, + ) } Some(CallStep::Builtin(callee_ty)) => { - self.confirm_builtin_call(call_expr, callee_ty, arg_exprs, expected) + self.confirm_builtin_call(call_expr, callee_expr, callee_ty, arg_exprs, expected) } Some(CallStep::DeferredClosure(fn_sig)) => { @@ -281,6 +288,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { fn confirm_builtin_call( &self, call_expr: &'tcx hir::Expr<'tcx>, + callee_expr: &'tcx hir::Expr<'tcx>, callee_ty: Ty<'tcx>, arg_exprs: &'tcx [hir::Expr<'tcx>], expected: Expectation<'tcx>, @@ -299,110 +307,104 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } - if let hir::ExprKind::Call(callee, _) = call_expr.kind { - let mut err = type_error_struct!( - self.tcx.sess, - callee.span, - callee_ty, - E0618, - "expected function, found {}", - match unit_variant { - Some(ref path) => format!("enum variant `{}`", path), - None => format!("`{}`", callee_ty), - } - ); + let mut err = type_error_struct!( + self.tcx.sess, + callee_expr.span, + callee_ty, + E0618, + "expected function, found {}", + match unit_variant { + Some(ref path) => format!("enum variant `{}`", path), + None => format!("`{}`", callee_ty), + } + ); - self.identify_bad_closure_def_and_call( - &mut err, - call_expr.hir_id, - &callee.kind, - callee.span, - ); + self.identify_bad_closure_def_and_call( + &mut err, + call_expr.hir_id, + &callee_expr.kind, + callee_expr.span, + ); - if let Some(ref path) = unit_variant { - err.span_suggestion( - call_expr.span, - &format!( - "`{}` is a unit variant, you need to write it \ + if let Some(ref path) = unit_variant { + err.span_suggestion( + call_expr.span, + &format!( + "`{}` is a unit variant, you need to write it \ without the parenthesis", - path - ), - path.to_string(), - Applicability::MachineApplicable, - ); - } + path + ), + path.to_string(), + Applicability::MachineApplicable, + ); + } - let mut inner_callee_path = None; - let def = match callee.kind { - hir::ExprKind::Path(ref qpath) => { - self.typeck_results.borrow().qpath_res(qpath, callee.hir_id) + let mut inner_callee_path = None; + let def = match callee_expr.kind { + hir::ExprKind::Path(ref qpath) => { + self.typeck_results.borrow().qpath_res(qpath, callee_expr.hir_id) + } + hir::ExprKind::Call(ref inner_callee, _) => { + // If the call spans more than one line and the callee kind is + // itself another `ExprCall`, that's a clue that we might just be + // missing a semicolon (Issue #51055) + let call_is_multiline = + self.tcx.sess.source_map().is_multiline(call_expr.span); + if call_is_multiline { + err.span_suggestion( + callee_expr.span.shrink_to_hi(), + "consider using a semicolon here", + ";".to_owned(), + Applicability::MaybeIncorrect, + ); } - hir::ExprKind::Call(ref inner_callee, _) => { - // If the call spans more than one line and the callee kind is - // itself another `ExprCall`, that's a clue that we might just be - // missing a semicolon (Issue #51055) - let call_is_multiline = - self.tcx.sess.source_map().is_multiline(call_expr.span); - if call_is_multiline { - err.span_suggestion( - callee.span.shrink_to_hi(), - "consider using a semicolon here", - ";".to_owned(), - Applicability::MaybeIncorrect, - ); - } - if let hir::ExprKind::Path(ref inner_qpath) = inner_callee.kind { - inner_callee_path = Some(inner_qpath); - self.typeck_results - .borrow() - .qpath_res(inner_qpath, inner_callee.hir_id) - } else { - Res::Err - } + if let hir::ExprKind::Path(ref inner_qpath) = inner_callee.kind { + inner_callee_path = Some(inner_qpath); + self.typeck_results.borrow().qpath_res(inner_qpath, inner_callee.hir_id) + } else { + Res::Err } - _ => Res::Err, - }; - - err.span_label(call_expr.span, "call expression requires function"); - - if let Some(span) = self.tcx.hir().res_span(def) { - let callee_ty = callee_ty.to_string(); - let label = match (unit_variant, inner_callee_path) { - (Some(path), _) => Some(format!("`{}` defined here", path)), - (_, Some(hir::QPath::Resolved(_, path))) => { - self.tcx.sess.source_map().span_to_snippet(path.span).ok().map( - |p| format!("`{}` defined here returns `{}`", p, callee_ty), - ) - } - _ => { - match def { - // Emit a different diagnostic for local variables, as they are not - // type definitions themselves, but rather variables *of* that type. - Res::Local(hir_id) => Some(format!( - "`{}` has type `{}`", - self.tcx.hir().name(hir_id), - callee_ty - )), - Res::Def(kind, def_id) - if kind.ns() == Some(Namespace::ValueNS) => - { - Some(format!( - "`{}` defined here", - self.tcx.def_path_str(def_id), - )) - } - _ => Some(format!("`{}` defined here", callee_ty)), + } + _ => Res::Err, + }; + + err.span_label(call_expr.span, "call expression requires function"); + + if let Some(span) = self.tcx.hir().res_span(def) { + let callee_ty = callee_ty.to_string(); + let label = match (unit_variant, inner_callee_path) { + (Some(path), _) => Some(format!("`{}` defined here", path)), + (_, Some(hir::QPath::Resolved(_, path))) => self + .tcx + .sess + .source_map() + .span_to_snippet(path.span) + .ok() + .map(|p| format!("`{}` defined here returns `{}`", p, callee_ty)), + _ => { + match def { + // Emit a different diagnostic for local variables, as they are not + // type definitions themselves, but rather variables *of* that type. + Res::Local(hir_id) => Some(format!( + "`{}` has type `{}`", + self.tcx.hir().name(hir_id), + callee_ty + )), + Res::Def(kind, def_id) if kind.ns() == Some(Namespace::ValueNS) => { + Some(format!( + "`{}` defined here", + self.tcx.def_path_str(def_id), + )) } + _ => Some(format!("`{}` defined here", callee_ty)), } - }; - if let Some(label) = label { - err.span_label(span, label); } + }; + if let Some(label) = label { + err.span_label(span, label); } - err.emit(); - } else { - bug!("call_expr.kind should be an ExprKind::Call, got {:?}", call_expr.kind); } + err.emit(); // This is the "default" function signature, used in case of error. // In that case, we check each argument against "error" in order to @@ -465,7 +467,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let expected_arg_tys = self.expected_inputs_for_expected_output( call_expr.span, expected, - fn_sig.output().clone(), + fn_sig.output(), fn_sig.inputs(), ); @@ -538,7 +540,7 @@ impl<'a, 'tcx> DeferredCallResolution<'tcx> { debug!("attempt_resolution: method_callee={:?}", method_callee); for (method_arg_ty, self_arg_ty) in - method_sig.inputs().iter().skip(1).zip(self.fn_sig.inputs()) + iter::zip(method_sig.inputs().iter().skip(1), self.fn_sig.inputs()) { fcx.demand_eqtype(self.call_expr.span, &self_arg_ty, &method_arg_ty); } diff --git a/compiler/rustc_typeck/src/check/cast.rs b/compiler/rustc_typeck/src/check/cast.rs index 16c344e8e2b9e..b760a54f08c76 100644 --- a/compiler/rustc_typeck/src/check/cast.rs +++ b/compiler/rustc_typeck/src/check/cast.rs @@ -359,6 +359,21 @@ impl<'a, 'tcx> CastCheck<'tcx> { { sugg = Some(format!("&{}", mutbl.prefix_str())); } + } else if let ty::RawPtr(TypeAndMut { mutbl, .. }) = *self.cast_ty.kind() { + if fcx + .try_coerce( + self.expr, + fcx.tcx.mk_ref( + &ty::RegionKind::ReErased, + TypeAndMut { ty: self.expr_ty, mutbl }, + ), + self.cast_ty, + AllowTwoPhase::No, + ) + .is_ok() + { + sugg = Some(format!("&{}", mutbl.prefix_str())); + } } if let Some(sugg) = sugg { err.span_label(self.span, "invalid cast"); diff --git a/compiler/rustc_typeck/src/check/check.rs b/compiler/rustc_typeck/src/check/check.rs index 0010d59f710cd..892abb5a34465 100644 --- a/compiler/rustc_typeck/src/check/check.rs +++ b/compiler/rustc_typeck/src/check/check.rs @@ -26,6 +26,7 @@ use rustc_trait_selection::opaque_types::InferCtxtExt as _; use rustc_trait_selection::traits::error_reporting::InferCtxtExt as _; use rustc_trait_selection::traits::{self, ObligationCauseCode}; +use std::iter; use std::ops::ControlFlow; pub fn check_wf_new(tcx: TyCtxt<'_>) { @@ -87,8 +88,69 @@ pub(super) fn check_fn<'a, 'tcx>( let declared_ret_ty = fn_sig.output(); - let revealed_ret_ty = - fcx.instantiate_opaque_types_from_value(fn_id, declared_ret_ty, decl.output.span()); + let feature = match tcx.hir().get(fn_id) { + // TAIT usage in function return position. + // Example: + // + // ```rust + // type Foo = impl Debug; + // fn bar() -> Foo { 42 } + // ``` + Node::Item(hir::Item { kind: ItemKind::Fn(..), .. }) | + // TAIT usage in associated function return position. + // + // Example with a free type alias: + // + // ```rust + // type Foo = impl Debug; + // impl SomeTrait for SomeType { + // fn bar() -> Foo { 42 } + // } + // ``` + // + // Example with an associated TAIT: + // + // ```rust + // impl SomeTrait for SomeType { + // type Foo = impl Debug; + // fn bar() -> Self::Foo { 42 } + // } + // ``` + Node::ImplItem(hir::ImplItem { + kind: hir::ImplItemKind::Fn(..), .. + }) => None, + // Forbid TAIT in trait declarations for now. + // Examples: + // + // ```rust + // type Foo = impl Debug; + // trait Bar { + // fn bar() -> Foo; + // } + // trait Bop { + // type Bop: PartialEq; + // } + // ``` + Node::TraitItem(hir::TraitItem { + kind: hir::TraitItemKind::Fn(..), + .. + }) | + // Forbid TAIT in closure return position for now. + // Example: + // + // ```rust + // type Foo = impl Debug; + // let x = |y| -> Foo { 42 + y }; + // ``` + Node::Expr(hir::Expr { kind: hir::ExprKind::Closure(..), .. }) => Some(sym::type_alias_impl_trait), + node => bug!("Item being checked wasn't a function/closure: {:?}", node), + }; + let revealed_ret_ty = fcx.instantiate_opaque_types_from_value( + fn_id, + declared_ret_ty, + decl.output.span(), + feature, + ); debug!("check_fn: declared_ret_ty: {}, revealed_ret_ty: {}", declared_ret_ty, revealed_ret_ty); fcx.ret_coercion = Some(RefCell::new(CoerceMany::new(revealed_ret_ty))); fcx.ret_type_span = Some(decl.output.span()); @@ -659,7 +721,8 @@ fn check_opaque_meets_bounds<'tcx>( // 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::Binding | hir::OpaqueTyOrigin::Misc => {} + hir::OpaqueTyOrigin::Binding | hir::OpaqueTyOrigin::Misc | hir::OpaqueTyOrigin::TyAlias => { + } } let hir_id = tcx.hir().local_def_id_to_hir_id(def_id); @@ -1410,7 +1473,7 @@ fn check_enum<'tcx>( } let mut disr_vals: Vec> = Vec::with_capacity(vs.len()); - for ((_, discr), v) in def.discriminants(tcx).zip(vs) { + for ((_, discr), v) in iter::zip(def.discriminants(tcx), vs) { // Check for duplicate discriminant values if let Some(i) = disr_vals.iter().position(|&x| x.val == discr.val) { let variant_did = def.variants[VariantIdx::new(i)].def_id; diff --git a/compiler/rustc_typeck/src/check/closure.rs b/compiler/rustc_typeck/src/check/closure.rs index 431e6d70ff35c..22d3dc6bdc0c0 100644 --- a/compiler/rustc_typeck/src/check/closure.rs +++ b/compiler/rustc_typeck/src/check/closure.rs @@ -69,7 +69,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let expr_def_id = self.tcx.hir().local_def_id(expr.hir_id); let ClosureSignatures { bound_sig, liberated_sig } = - self.sig_of_closure(expr_def_id.to_def_id(), decl, body, expected_sig); + self.sig_of_closure(expr.hir_id, expr_def_id.to_def_id(), decl, body, expected_sig); debug!("check_closure: ty_of_closure returns {:?}", liberated_sig); @@ -288,15 +288,16 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { fn sig_of_closure( &self, + hir_id: hir::HirId, expr_def_id: DefId, decl: &hir::FnDecl<'_>, body: &hir::Body<'_>, expected_sig: Option>, ) -> ClosureSignatures<'tcx> { if let Some(e) = expected_sig { - self.sig_of_closure_with_expectation(expr_def_id, decl, body, e) + self.sig_of_closure_with_expectation(hir_id, expr_def_id, decl, body, e) } else { - self.sig_of_closure_no_expectation(expr_def_id, decl, body) + self.sig_of_closure_no_expectation(hir_id, expr_def_id, decl, body) } } @@ -304,13 +305,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// types that the user gave into a signature. fn sig_of_closure_no_expectation( &self, + hir_id: hir::HirId, expr_def_id: DefId, decl: &hir::FnDecl<'_>, body: &hir::Body<'_>, ) -> ClosureSignatures<'tcx> { debug!("sig_of_closure_no_expectation()"); - let bound_sig = self.supplied_sig_of_closure(expr_def_id, decl, body); + let bound_sig = self.supplied_sig_of_closure(hir_id, expr_def_id, decl, body); self.closure_sigs(expr_def_id, body, bound_sig) } @@ -364,6 +366,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// regions with depth 1, which are bound then by the closure. fn sig_of_closure_with_expectation( &self, + hir_id: hir::HirId, expr_def_id: DefId, decl: &hir::FnDecl<'_>, body: &hir::Body<'_>, @@ -375,7 +378,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // expectation if things don't see to match up with what we // expect. if expected_sig.sig.c_variadic() != decl.c_variadic { - return self.sig_of_closure_no_expectation(expr_def_id, decl, body); + return self.sig_of_closure_no_expectation(hir_id, expr_def_id, decl, body); } else if expected_sig.sig.skip_binder().inputs_and_output.len() != decl.inputs.len() + 1 { return self.sig_of_closure_with_mismatched_number_of_arguments( expr_def_id, @@ -411,9 +414,15 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // Along the way, it also writes out entries for types that the user // wrote into our typeck results, which are then later used by the privacy // check. - match self.check_supplied_sig_against_expectation(expr_def_id, decl, body, &closure_sigs) { + match self.check_supplied_sig_against_expectation( + hir_id, + expr_def_id, + decl, + body, + &closure_sigs, + ) { Ok(infer_ok) => self.register_infer_ok_obligations(infer_ok), - Err(_) => return self.sig_of_closure_no_expectation(expr_def_id, decl, body), + Err(_) => return self.sig_of_closure_no_expectation(hir_id, expr_def_id, decl, body), } closure_sigs @@ -460,6 +469,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// strategy. fn check_supplied_sig_against_expectation( &self, + hir_id: hir::HirId, expr_def_id: DefId, decl: &hir::FnDecl<'_>, body: &hir::Body<'_>, @@ -469,7 +479,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // // (See comment on `sig_of_closure_with_expectation` for the // meaning of these letters.) - let supplied_sig = self.supplied_sig_of_closure(expr_def_id, decl, body); + let supplied_sig = self.supplied_sig_of_closure(hir_id, expr_def_id, decl, body); debug!("check_supplied_sig_against_expectation: supplied_sig={:?}", supplied_sig); @@ -492,13 +502,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // The liberated version of this signature should be a subtype // of the liberated form of the expectation. - for ((hir_ty, &supplied_ty), expected_ty) in decl - .inputs - .iter() - .zip(supplied_sig.inputs().skip_binder()) // binder moved to (*) below - .zip(expected_sigs.liberated_sig.inputs()) - // `liberated_sig` is E'. - { + for ((hir_ty, &supplied_ty), expected_ty) in iter::zip( + iter::zip( + decl.inputs, + supplied_sig.inputs().skip_binder(), // binder moved to (*) below + ), + expected_sigs.liberated_sig.inputs(), // `liberated_sig` is E'. + ) { // Instantiate (this part of..) S to S', i.e., with fresh variables. let (supplied_ty, _) = self.infcx.replace_bound_vars_with_fresh_vars( hir_ty.span, @@ -534,6 +544,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// Also, record this closure signature for later. fn supplied_sig_of_closure( &self, + hir_id: hir::HirId, expr_def_id: DefId, decl: &hir::FnDecl<'_>, body: &hir::Body<'_>, @@ -545,6 +556,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { decl, body.generator_kind, ); + let bound_vars = self.tcx.late_bound_vars(hir_id); + // First, convert the types that the user supplied (if any). let supplied_arguments = decl.inputs.iter().map(|a| astconv.ast_ty_to_ty(a)); let supplied_return = match decl.output { @@ -571,13 +584,16 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { }, }; - let result = ty::Binder::bind(self.tcx.mk_fn_sig( - supplied_arguments, - supplied_return, - decl.c_variadic, - hir::Unsafety::Normal, - Abi::RustCall, - )); + let result = ty::Binder::bind_with_vars( + self.tcx.mk_fn_sig( + supplied_arguments, + supplied_return, + decl.c_variadic, + hir::Unsafety::Normal, + Abi::RustCall, + ), + bound_vars, + ); debug!("supplied_sig_of_closure: result={:?}", result); diff --git a/compiler/rustc_typeck/src/check/coercion.rs b/compiler/rustc_typeck/src/check/coercion.rs index 32b3d0b059545..427f967a9b6bd 100644 --- a/compiler/rustc_typeck/src/check/coercion.rs +++ b/compiler/rustc_typeck/src/check/coercion.rs @@ -973,6 +973,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } }; if let (Some(a_sig), Some(b_sig)) = (a_sig, b_sig) { + // Intrinsics are not coercible to function pointers. + if a_sig.abi() == Abi::RustIntrinsic + || a_sig.abi() == Abi::PlatformIntrinsic + || b_sig.abi() == Abi::RustIntrinsic + || b_sig.abi() == Abi::PlatformIntrinsic + { + return Err(TypeError::IntrinsicCast); + } // The signature must match. let a_sig = self.normalize_associated_types_in(new.span, a_sig); let b_sig = self.normalize_associated_types_in(new.span, b_sig); @@ -1440,9 +1448,8 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> { // as prior return coercions would not be relevant (#57664). let parent_id = fcx.tcx.hir().get_parent_node(id); let fn_decl = if let Some((expr, blk_id)) = expression { - pointing_at_return_type = fcx.suggest_mismatched_types_on_tail( - &mut err, expr, expected, found, cause.span, blk_id, - ); + pointing_at_return_type = + fcx.suggest_mismatched_types_on_tail(&mut err, expr, expected, found, blk_id); let parent = fcx.tcx.hir().get(parent_id); if let (Some(cond_expr), true, false) = ( fcx.tcx.hir().get_if_cause(expr.hir_id), @@ -1487,34 +1494,12 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> { if let (Some((expr, _)), Some((fn_decl, _, _))) = (expression, fcx.get_node_fn_decl(parent_item)) { - fcx.suggest_missing_return_expr(&mut err, expr, fn_decl, expected, found); + fcx.suggest_missing_return_expr(&mut err, expr, fn_decl, expected, found, parent_id); } if let (Some(sp), Some(fn_output)) = (fcx.ret_coercion_span.get(), fn_output) { self.add_impl_trait_explanation(&mut err, cause, fcx, expected, sp, fn_output); } - - if let Some(sp) = fcx.ret_coercion_span.get() { - // If the closure has an explicit return type annotation, - // then a type error may occur at the first return expression we - // see in the closure (if it conflicts with the declared - // return type). Skip adding a note in this case, since it - // would be incorrect. - if !err.span.primary_spans().iter().any(|&span| span == sp) { - let hir = fcx.tcx.hir(); - let body_owner = hir.body_owned_by(hir.enclosing_body_owner(fcx.body_id)); - if fcx.tcx.is_closure(hir.body_owner_def_id(body_owner).to_def_id()) { - err.span_note( - sp, - &format!( - "return type inferred to be `{}` here", - fcx.resolve_vars_if_possible(expected) - ), - ); - } - } - } - err } @@ -1554,7 +1539,7 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> { if let hir::FnRetTy::Return(ty) = fn_output { // Get the return type. if let hir::TyKind::OpaqueDef(..) = ty.kind { - let ty = AstConv::ast_ty_to_ty(fcx, ty); + let ty = >::ast_ty_to_ty(fcx, ty); // Get the `impl Trait`'s `DefId`. if let ty::Opaque(def_id, _) = ty.kind() { let hir_id = fcx.tcx.hir().local_def_id_to_hir_id(def_id.expect_local()); @@ -1616,7 +1601,7 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> { fn is_return_ty_unsized(&self, fcx: &FnCtxt<'a, 'tcx>, blk_id: hir::HirId) -> bool { if let Some((fn_decl, _)) = fcx.get_fn_decl(blk_id) { if let hir::FnRetTy::Return(ty) = fn_decl.output { - let ty = AstConv::ast_ty_to_ty(fcx, ty); + let ty = >::ast_ty_to_ty(fcx, ty); if let ty::Dynamic(..) = ty.kind() { return true; } diff --git a/compiler/rustc_typeck/src/check/compare_method.rs b/compiler/rustc_typeck/src/check/compare_method.rs index a30a81079335d..60ca562f99200 100644 --- a/compiler/rustc_typeck/src/check/compare_method.rs +++ b/compiler/rustc_typeck/src/check/compare_method.rs @@ -14,6 +14,7 @@ use rustc_middle::ty::{GenericParamDefKind, ToPredicate, TyCtxt}; use rustc_span::Span; use rustc_trait_selection::traits::error_reporting::InferCtxtExt; use rustc_trait_selection::traits::{self, ObligationCause, ObligationCauseCode, Reveal}; +use std::iter; use super::{potentially_plural_count, FnCtxt, Inherited}; @@ -224,7 +225,7 @@ fn compare_predicate_entailment<'tcx>( let (impl_m_own_bounds, _) = infcx.replace_bound_vars_with_fresh_vars( impl_m_span, infer::HigherRankedType, - ty::Binder::bind(impl_m_own_bounds.predicates), + ty::Binder::bind(impl_m_own_bounds.predicates, tcx), ); for predicate in impl_m_own_bounds { let traits::Normalized { value: predicate, obligations } = @@ -257,14 +258,14 @@ fn compare_predicate_entailment<'tcx>( ); let impl_sig = inh.normalize_associated_types_in(impl_m_span, impl_m_hir_id, param_env, impl_sig); - let impl_fty = tcx.mk_fn_ptr(ty::Binder::bind(impl_sig)); + let impl_fty = tcx.mk_fn_ptr(ty::Binder::bind(impl_sig, tcx)); debug!("compare_impl_method: impl_fty={:?}", impl_fty); let trait_sig = tcx.liberate_late_bound_regions(impl_m.def_id, tcx.fn_sig(trait_m.def_id)); let trait_sig = trait_sig.subst(tcx, trait_to_placeholder_substs); let trait_sig = inh.normalize_associated_types_in(impl_m_span, impl_m_hir_id, param_env, trait_sig); - let trait_fty = tcx.mk_fn_ptr(ty::Binder::bind(trait_sig)); + let trait_fty = tcx.mk_fn_ptr(ty::Binder::bind(trait_sig, tcx)); debug!("compare_impl_method: trait_fty={:?}", trait_fty); @@ -277,9 +278,8 @@ fn compare_predicate_entailment<'tcx>( if let Err(terr) = sub_result { debug!("sub_types failed: impl ty {:?}, trait ty {:?}", impl_fty, trait_fty); - let (impl_err_span, trait_err_span) = extract_spans_for_error_reporting( - &infcx, param_env, &terr, &cause, impl_m, impl_sig, trait_m, trait_sig, - ); + let (impl_err_span, trait_err_span) = + extract_spans_for_error_reporting(&infcx, &terr, &cause, impl_m, trait_m); cause.make_mut().span = impl_err_span; @@ -290,18 +290,79 @@ fn compare_predicate_entailment<'tcx>( "method `{}` has an incompatible type for trait", trait_m.ident ); - if let TypeError::Mutability = terr { - if let Some(trait_err_span) = trait_err_span { - if let Ok(trait_err_str) = tcx.sess.source_map().span_to_snippet(trait_err_span) + match &terr { + TypeError::ArgumentMutability(0) | TypeError::ArgumentSorts(_, 0) + if trait_m.fn_has_self_parameter => + { + let ty = trait_sig.inputs()[0]; + let sugg = match ExplicitSelf::determine(ty, |_| ty == impl_trait_ref.self_ty()) { + ExplicitSelf::ByValue => "self".to_owned(), + ExplicitSelf::ByReference(_, hir::Mutability::Not) => "&self".to_owned(), + ExplicitSelf::ByReference(_, hir::Mutability::Mut) => { + "&mut self".to_owned() + } + _ => format!("self: {}", ty), + }; + + // When the `impl` receiver is an arbitrary self type, like `self: Box`, the + // span points only at the type `Box, but we want to cover the whole + // argument pattern and type. + let impl_m_hir_id = + tcx.hir().local_def_id_to_hir_id(impl_m.def_id.expect_local()); + let span = match tcx.hir().expect_impl_item(impl_m_hir_id).kind { + ImplItemKind::Fn(ref sig, body) => tcx + .hir() + .body_param_names(body) + .zip(sig.decl.inputs.iter()) + .map(|(param, ty)| param.span.to(ty.span)) + .next() + .unwrap_or(impl_err_span), + _ => bug!("{:?} is not a method", impl_m), + }; + + diag.span_suggestion( + span, + "change the self-receiver type to match the trait", + sugg, + Applicability::MachineApplicable, + ); + } + TypeError::ArgumentMutability(i) | TypeError::ArgumentSorts(_, i) => { + if trait_sig.inputs().len() == *i { + // Suggestion to change output type. We do not suggest in `async` functions + // to avoid complex logic or incorrect output. + let impl_m_hir_id = + tcx.hir().local_def_id_to_hir_id(impl_m.def_id.expect_local()); + match tcx.hir().expect_impl_item(impl_m_hir_id).kind { + ImplItemKind::Fn(ref sig, _) + if sig.header.asyncness == hir::IsAsync::NotAsync => + { + let msg = "change the output type to match the trait"; + let ap = Applicability::MachineApplicable; + match sig.decl.output { + hir::FnRetTy::DefaultReturn(sp) => { + let sugg = format!("-> {} ", trait_sig.output()); + diag.span_suggestion_verbose(sp, msg, sugg, ap); + } + hir::FnRetTy::Return(hir_ty) => { + let sugg = trait_sig.output().to_string(); + diag.span_suggestion(hir_ty.span, msg, sugg, ap); + } + }; + } + _ => {} + }; + } else if let Some(trait_ty) = trait_sig.inputs().get(*i) { diag.span_suggestion( impl_err_span, - "consider changing the mutability to match the trait", - trait_err_str, + "change the parameter type to match the trait", + trait_ty.to_string(), Applicability::MachineApplicable, ); } } + _ => {} } infcx.note_type_err( @@ -384,90 +445,35 @@ fn check_region_bounds_on_impl_item<'tcx>( fn extract_spans_for_error_reporting<'a, 'tcx>( infcx: &infer::InferCtxt<'a, 'tcx>, - param_env: ty::ParamEnv<'tcx>, terr: &TypeError<'_>, cause: &ObligationCause<'tcx>, impl_m: &ty::AssocItem, - impl_sig: ty::FnSig<'tcx>, trait_m: &ty::AssocItem, - trait_sig: ty::FnSig<'tcx>, ) -> (Span, Option) { let tcx = infcx.tcx; let impl_m_hir_id = tcx.hir().local_def_id_to_hir_id(impl_m.def_id.expect_local()); - let (impl_m_output, impl_m_iter) = match tcx.hir().expect_impl_item(impl_m_hir_id).kind { - ImplItemKind::Fn(ref impl_m_sig, _) => { - (&impl_m_sig.decl.output, impl_m_sig.decl.inputs.iter()) + let mut impl_args = match tcx.hir().expect_impl_item(impl_m_hir_id).kind { + ImplItemKind::Fn(ref sig, _) => { + sig.decl.inputs.iter().map(|t| t.span).chain(iter::once(sig.decl.output.span())) } _ => bug!("{:?} is not a method", impl_m), }; - - match *terr { - TypeError::Mutability => { - if let Some(def_id) = trait_m.def_id.as_local() { - let trait_m_hir_id = tcx.hir().local_def_id_to_hir_id(def_id); - let trait_m_iter = match tcx.hir().expect_trait_item(trait_m_hir_id).kind { - TraitItemKind::Fn(ref trait_m_sig, _) => trait_m_sig.decl.inputs.iter(), - _ => bug!("{:?} is not a TraitItemKind::Fn", trait_m), - }; - - impl_m_iter - .zip(trait_m_iter) - .find(|&(ref impl_arg, ref trait_arg)| { - match (&impl_arg.kind, &trait_arg.kind) { - ( - &hir::TyKind::Rptr(_, ref impl_mt), - &hir::TyKind::Rptr(_, ref trait_mt), - ) - | (&hir::TyKind::Ptr(ref impl_mt), &hir::TyKind::Ptr(ref trait_mt)) => { - impl_mt.mutbl != trait_mt.mutbl - } - _ => false, - } - }) - .map(|(ref impl_arg, ref trait_arg)| (impl_arg.span, Some(trait_arg.span))) - .unwrap_or_else(|| (cause.span(tcx), tcx.hir().span_if_local(trait_m.def_id))) - } else { - (cause.span(tcx), tcx.hir().span_if_local(trait_m.def_id)) + let trait_args = trait_m.def_id.as_local().map(|def_id| { + let trait_m_hir_id = tcx.hir().local_def_id_to_hir_id(def_id); + match tcx.hir().expect_trait_item(trait_m_hir_id).kind { + TraitItemKind::Fn(ref sig, _) => { + sig.decl.inputs.iter().map(|t| t.span).chain(iter::once(sig.decl.output.span())) } + _ => bug!("{:?} is not a TraitItemKind::Fn", trait_m), } - TypeError::Sorts(ExpectedFound { .. }) => { - if let Some(def_id) = trait_m.def_id.as_local() { - let trait_m_hir_id = tcx.hir().local_def_id_to_hir_id(def_id); - let (trait_m_output, trait_m_iter) = - match tcx.hir().expect_trait_item(trait_m_hir_id).kind { - TraitItemKind::Fn(ref trait_m_sig, _) => { - (&trait_m_sig.decl.output, trait_m_sig.decl.inputs.iter()) - } - _ => bug!("{:?} is not a TraitItemKind::Fn", trait_m), - }; + }); - let impl_iter = impl_sig.inputs().iter(); - let trait_iter = trait_sig.inputs().iter(); - impl_iter - .zip(trait_iter) - .zip(impl_m_iter) - .zip(trait_m_iter) - .find_map(|(((&impl_arg_ty, &trait_arg_ty), impl_arg), trait_arg)| match infcx - .at(&cause, param_env) - .sub(trait_arg_ty, impl_arg_ty) - { - Ok(_) => None, - Err(_) => Some((impl_arg.span, Some(trait_arg.span))), - }) - .unwrap_or_else(|| { - if infcx - .at(&cause, param_env) - .sup(trait_sig.output(), impl_sig.output()) - .is_err() - { - (impl_m_output.span(), Some(trait_m_output.span())) - } else { - (cause.span(tcx), tcx.hir().span_if_local(trait_m.def_id)) - } - }) - } else { - (cause.span(tcx), tcx.hir().span_if_local(trait_m.def_id)) - } + match *terr { + TypeError::ArgumentMutability(i) => { + (impl_args.nth(i).unwrap(), trait_args.and_then(|mut args| args.nth(i))) + } + TypeError::ArgumentSorts(ExpectedFound { .. }, i) => { + (impl_args.nth(i).unwrap(), trait_args.and_then(|mut args| args.nth(i))) } _ => (cause.span(tcx), tcx.hir().span_if_local(trait_m.def_id)), } @@ -517,8 +523,7 @@ fn compare_self_type<'tcx>( tcx.sess, impl_m_span, E0185, - "method `{}` has a `{}` declaration in the impl, but \ - not in the trait", + "method `{}` has a `{}` declaration in the impl, but not in the trait", trait_m.ident, self_descr ); @@ -538,8 +543,7 @@ fn compare_self_type<'tcx>( tcx.sess, impl_m_span, E0186, - "method `{}` has a `{}` declaration in the trait, but \ - not in the impl", + "method `{}` has a `{}` declaration in the trait, but not in the impl", trait_m.ident, self_descr ); @@ -792,14 +796,14 @@ fn compare_synthetic_generics<'tcx>( let trait_m_generics = tcx.generics_of(trait_m.def_id); let impl_m_type_params = impl_m_generics.params.iter().filter_map(|param| match param.kind { GenericParamDefKind::Type { synthetic, .. } => Some((param.def_id, synthetic)), - GenericParamDefKind::Lifetime | GenericParamDefKind::Const => None, + GenericParamDefKind::Lifetime | GenericParamDefKind::Const { .. } => None, }); let trait_m_type_params = trait_m_generics.params.iter().filter_map(|param| match param.kind { GenericParamDefKind::Type { synthetic, .. } => Some((param.def_id, synthetic)), - GenericParamDefKind::Lifetime | GenericParamDefKind::Const => None, + GenericParamDefKind::Lifetime | GenericParamDefKind::Const { .. } => None, }); for ((impl_def_id, impl_synthetic), (trait_def_id, trait_synthetic)) in - impl_m_type_params.zip(trait_m_type_params) + iter::zip(impl_m_type_params, trait_m_type_params) { if impl_synthetic != trait_synthetic { let impl_hir_id = tcx.hir().local_def_id_to_hir_id(impl_def_id.expect_local()); @@ -996,8 +1000,7 @@ crate fn compare_const_impl<'tcx>( tcx.sess, cause.span, E0326, - "implemented const `{}` has an incompatible type for \ - trait", + "implemented const `{}` has an incompatible type for trait", trait_c.ident ); diff --git a/compiler/rustc_typeck/src/check/demand.rs b/compiler/rustc_typeck/src/check/demand.rs index 39b973ed371ae..e5fcdcfa74315 100644 --- a/compiler/rustc_typeck/src/check/demand.rs +++ b/compiler/rustc_typeck/src/check/demand.rs @@ -37,6 +37,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.suggest_missing_parentheses(err, expr); self.note_need_for_fn_pointer(err, expected, expr_ty); self.note_internal_mutation_in_method(err, expr, expected, expr_ty); + self.report_closure_infered_return_type(err, expected) } // Requires that the two types unify, and prints an error message if @@ -200,7 +201,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if self.can_coerce(expr_ty, sole_field_ty) { let variant_path = self.tcx.def_path_str(variant.def_id); // FIXME #56861: DRYer prelude filtering - Some(variant_path.trim_start_matches("std::prelude::v1::").to_string()) + if let Some(path) = variant_path.strip_prefix("std::prelude::") { + if let Some((_, path)) = path.split_once("::") { + return Some(path.to_string()); + } + } + Some(variant_path) } else { None } @@ -361,6 +367,29 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { false } + /// If the given `HirId` corresponds to a block with a trailing expression, return that expression + crate fn maybe_get_block_expr(&self, hir_id: hir::HirId) -> Option<&'tcx hir::Expr<'tcx>> { + match self.tcx.hir().find(hir_id)? { + Node::Expr(hir::Expr { kind: hir::ExprKind::Block(block, ..), .. }) => block.expr, + _ => None, + } + } + + /// Returns whether the given expression is an `else if`. + crate fn is_else_if_block(&self, expr: &hir::Expr<'_>) -> bool { + if let hir::ExprKind::If(..) = expr.kind { + let parent_id = self.tcx.hir().get_parent_node(expr.hir_id); + if let Some(Node::Expr(hir::Expr { + kind: hir::ExprKind::If(_, _, Some(else_expr)), + .. + })) = self.tcx.hir().find(parent_id) + { + return else_expr.hir_id == expr.hir_id; + } + } + false + } + /// This function is used to determine potential "simple" improvements or users' errors and /// provide them useful help. For example: /// @@ -647,6 +676,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { }; let suggestion = if is_struct_pat_shorthand_field { format!("{}: *{}", code, code) + } else if self.is_else_if_block(expr) { + // Don't suggest nonsense like `else *if` + return None; + } else if let Some(expr) = self.maybe_get_block_expr(expr.hir_id) { + format!("*{}", sm.span_to_snippet(expr.span).unwrap_or(code)) } else { format!("*{}", code) }; @@ -1028,4 +1062,32 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { _ => false, } } + + // Report the type inferred by the return statement. + fn report_closure_infered_return_type( + &self, + err: &mut DiagnosticBuilder<'_>, + expected: Ty<'tcx>, + ) { + if let Some(sp) = self.ret_coercion_span.get() { + // If the closure has an explicit return type annotation, + // then a type error may occur at the first return expression we + // see in the closure (if it conflicts with the declared + // return type). Skip adding a note in this case, since it + // would be incorrect. + if !err.span.primary_spans().iter().any(|&span| span == sp) { + let hir = self.tcx.hir(); + let body_owner = hir.body_owned_by(hir.enclosing_body_owner(self.body_id)); + if self.tcx.is_closure(hir.body_owner_def_id(body_owner).to_def_id()) { + err.span_note( + sp, + &format!( + "return type inferred to be `{}` here", + self.resolve_vars_if_possible(expected) + ), + ); + } + } + } + } } diff --git a/compiler/rustc_typeck/src/check/dropck.rs b/compiler/rustc_typeck/src/check/dropck.rs index 4c3c4fd447058..de6336b254b3f 100644 --- a/compiler/rustc_typeck/src/check/dropck.rs +++ b/compiler/rustc_typeck/src/check/dropck.rs @@ -77,7 +77,7 @@ fn ensure_drop_params_and_item_params_correspond<'tcx>( tcx.infer_ctxt().enter(|ref infcx| { let impl_param_env = tcx.param_env(self_type_did); let tcx = infcx.tcx; - let mut fulfillment_cx = TraitEngine::new(tcx); + let mut fulfillment_cx = >::new(tcx); let named_type = tcx.type_of(self_type_did); @@ -354,9 +354,9 @@ impl TypeRelation<'tcx> for SimpleEqRelation<'tcx> { fn binders( &mut self, - a: ty::Binder, - b: ty::Binder, - ) -> RelateResult<'tcx, ty::Binder> + a: ty::Binder<'tcx, T>, + b: ty::Binder<'tcx, T>, + ) -> RelateResult<'tcx, ty::Binder<'tcx, T>> where T: Relate<'tcx>, { diff --git a/compiler/rustc_typeck/src/check/expectation.rs b/compiler/rustc_typeck/src/check/expectation.rs index 22be10a731f9f..e9e810344776b 100644 --- a/compiler/rustc_typeck/src/check/expectation.rs +++ b/compiler/rustc_typeck/src/check/expectation.rs @@ -104,8 +104,8 @@ impl<'a, 'tcx> Expectation<'tcx> { /// for the program to type-check). `only_has_type` will return /// such a constraint, if it exists. pub(super) fn only_has_type(self, fcx: &FnCtxt<'a, 'tcx>) -> Option> { - match self.resolve(fcx) { - ExpectHasType(ty) => Some(ty), + match self { + ExpectHasType(ty) => Some(fcx.resolve_vars_if_possible(ty)), NoExpectation | ExpectCastableToType(_) | ExpectRvalueLikeUnsized(_) | IsLast(_) => { None } diff --git a/compiler/rustc_typeck/src/check/expr.rs b/compiler/rustc_typeck/src/check/expr.rs index 2faf128c491fd..991c2ba693d69 100644 --- a/compiler/rustc_typeck/src/check/expr.rs +++ b/compiler/rustc_typeck/src/check/expr.rs @@ -6,7 +6,7 @@ use crate::astconv::AstConv as _; use crate::check::cast; use crate::check::coercion::CoerceMany; use crate::check::fatally_break_rust; -use crate::check::method::{probe, MethodError, SelfSource}; +use crate::check::method::SelfSource; use crate::check::report_unexpected_variant_res; use crate::check::BreakableCtxt; use crate::check::Diverges; @@ -30,7 +30,6 @@ use rustc_errors::{pluralize, struct_span_err, Applicability, DiagnosticBuilder, use rustc_hir as hir; use rustc_hir::def::{CtorKind, DefKind, Res}; use rustc_hir::def_id::DefId; -use rustc_hir::lang_items::LangItem; use rustc_hir::{ExprKind, QPath}; use rustc_infer::infer; use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; @@ -162,7 +161,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { expr: &'tcx hir::Expr<'tcx>, expected: Expectation<'tcx>, ) -> Ty<'tcx> { - debug!(">> type-checking: expr={:?} expected={:?}", expr, expected); + debug!(">> type-checking: expected={:?}, expr={:?} ", expected, expr); // True if `expr` is a `Try::from_ok(())` that is a result of desugaring a try block // without the final expr (e.g. `try { return; }`). We don't want to generate an @@ -225,7 +224,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { expr: &'tcx hir::Expr<'tcx>, expected: Expectation<'tcx>, ) -> Ty<'tcx> { - debug!("check_expr_kind(expr={:?}, expected={:?})", expr, expected); + debug!("check_expr_kind(expected={:?}, expr={:?})", expected, expr); let tcx = self.tcx; match expr.kind { @@ -461,7 +460,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.resolve_lang_item_path(lang_item, expr.span, expr.hir_id).1 } - fn check_expr_path(&self, qpath: &hir::QPath<'_>, expr: &'tcx hir::Expr<'tcx>) -> Ty<'tcx> { + fn check_expr_path( + &self, + qpath: &'tcx hir::QPath<'tcx>, + expr: &'tcx hir::Expr<'tcx>, + ) -> Ty<'tcx> { let tcx = self.tcx; let (res, opt_ty, segs) = self.resolve_ty_and_res_ufcs(qpath, expr.hir_id, expr.span); let ty = match res { @@ -600,7 +603,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { &cause, &mut |mut err| { self.suggest_mismatched_types_on_tail( - &mut err, expr, ty, e_ty, cause.span, target_id, + &mut err, expr, ty, e_ty, target_id, ); if let Some(val) = ty_kind_suggestion(ty) { let label = destination @@ -711,7 +714,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { }); let ret_ty = ret_coercion.borrow().expected_ty(); - let return_expr_ty = self.check_expr_with_hint(return_expr, ret_ty.clone()); + let return_expr_ty = self.check_expr_with_hint(return_expr, ret_ty); ret_coercion.borrow_mut().coerce( self, &self.cause(return_expr.span, ObligationCauseCode::ReturnValue(return_expr.hir_id)), @@ -947,7 +950,16 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } Err(error) => { if segment.ident.name != kw::Empty { - self.report_extended_method_error(segment, span, args, rcvr_t, error); + if let Some(mut err) = self.report_method_error( + span, + rcvr_t, + segment.ident, + SelfSource::MethodCall(&args[0]), + error, + Some(args), + ) { + err.emit(); + } } Err(()) } @@ -964,59 +976,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ) } - fn report_extended_method_error( - &self, - segment: &hir::PathSegment<'_>, - span: Span, - args: &'tcx [hir::Expr<'tcx>], - rcvr_t: Ty<'tcx>, - error: MethodError<'tcx>, - ) { - let rcvr = &args[0]; - let try_alt_rcvr = |err: &mut DiagnosticBuilder<'_>, new_rcvr_t| { - if let Some(new_rcvr_t) = new_rcvr_t { - if let Ok(pick) = self.lookup_probe( - span, - segment.ident, - new_rcvr_t, - rcvr, - probe::ProbeScope::AllTraits, - ) { - debug!("try_alt_rcvr: pick candidate {:?}", pick); - // Make sure the method is defined for the *actual* receiver: - // we don't want to treat `Box` as a receiver if - // it only works because of an autoderef to `&self` - if pick.autoderefs == 0 { - err.span_label( - pick.item.ident.span, - &format!("the method is available for `{}` here", new_rcvr_t), - ); - } - } - } - }; - - if let Some(mut err) = self.report_method_error( - span, - rcvr_t, - segment.ident, - SelfSource::MethodCall(rcvr), - error, - Some(args), - ) { - if let ty::Adt(..) = rcvr_t.kind() { - // Try alternative arbitrary self types that could fulfill this call. - // FIXME: probe for all types that *could* be arbitrary self-types, not - // just this list. - try_alt_rcvr(&mut err, self.tcx.mk_lang_item(rcvr_t, LangItem::OwnedBox)); - try_alt_rcvr(&mut err, self.tcx.mk_lang_item(rcvr_t, LangItem::Pin)); - try_alt_rcvr(&mut err, self.tcx.mk_diagnostic_item(rcvr_t, sym::Arc)); - try_alt_rcvr(&mut err, self.tcx.mk_diagnostic_item(rcvr_t, sym::Rc)); - } - err.emit(); - } - } - fn check_expr_cast( &self, e: &'tcx hir::Expr<'tcx>, @@ -1158,7 +1117,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { expr: &hir::Expr<'_>, expected: Expectation<'tcx>, qpath: &QPath<'_>, - fields: &'tcx [hir::Field<'tcx>], + fields: &'tcx [hir::ExprField<'tcx>], base_expr: &'tcx Option<&'tcx hir::Expr<'tcx>>, ) -> Ty<'tcx> { // Find the relevant variant @@ -1231,7 +1190,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { expr_id: hir::HirId, span: Span, variant: &'tcx ty::VariantDef, - ast_fields: &'tcx [hir::Field<'tcx>], + ast_fields: &'tcx [hir::ExprField<'tcx>], check_completeness: bool, ) -> bool { let tcx = self.tcx; @@ -1320,7 +1279,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { fn check_struct_fields_on_error( &self, - fields: &'tcx [hir::Field<'tcx>], + fields: &'tcx [hir::ExprField<'tcx>], base_expr: &'tcx Option<&'tcx hir::Expr<'tcx>>, ) { for field in fields { @@ -1411,8 +1370,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { &self, ty: Ty<'tcx>, variant: &'tcx ty::VariantDef, - field: &hir::Field<'_>, - skip_fields: &[hir::Field<'_>], + field: &hir::ExprField<'_>, + skip_fields: &[hir::ExprField<'_>], kind_name: &str, ty_span: Span, ) { @@ -2128,7 +2087,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { fn check_expr_asm(&self, asm: &'tcx hir::InlineAsm<'tcx>) -> Ty<'tcx> { for (op, _op_sp) in asm.operands { match op { - hir::InlineAsmOperand::In { expr, .. } | hir::InlineAsmOperand::Const { expr } => { + hir::InlineAsmOperand::In { expr, .. } => { self.check_expr_asm_operand(expr, true); } hir::InlineAsmOperand::Out { expr, .. } => { @@ -2145,6 +2104,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.check_expr_asm_operand(out_expr, false); } } + hir::InlineAsmOperand::Const { anon_const } => { + self.to_const(anon_const); + } hir::InlineAsmOperand::Sym { expr } => { self.check_expr(expr); } diff --git a/compiler/rustc_typeck/src/check/fn_ctxt/_impl.rs b/compiler/rustc_typeck/src/check/fn_ctxt/_impl.rs index f5e9cc1efcc45..9ace455042103 100644 --- a/compiler/rustc_typeck/src/check/fn_ctxt/_impl.rs +++ b/compiler/rustc_typeck/src/check/fn_ctxt/_impl.rs @@ -6,6 +6,7 @@ use crate::check::callee::{self, DeferredCallResolution}; use crate::check::method::{self, MethodCallee, SelfSource}; use crate::check::{BreakableCtxt, Diverges, Expectation, FallbackMode, FnCtxt, LocalTy}; +use rustc_ast::TraitObjectSyntax; use rustc_data_structures::captures::Captures; use rustc_data_structures::fx::FxHashSet; use rustc_errors::{Applicability, DiagnosticBuilder, ErrorReported}; @@ -13,7 +14,7 @@ use rustc_hir as hir; use rustc_hir::def::{CtorOf, DefKind, Res}; use rustc_hir::def_id::DefId; use rustc_hir::lang_items::LangItem; -use rustc_hir::{ExprKind, GenericArg, Node, QPath}; +use rustc_hir::{ExprKind, GenericArg, Node, QPath, TyKind}; use rustc_infer::infer::canonical::{Canonical, OriginalQueryValues, QueryResponse}; use rustc_infer::infer::error_reporting::TypeAnnotationNeeded::E0282; use rustc_infer::infer::{InferOk, InferResult}; @@ -27,10 +28,12 @@ use rustc_middle::ty::{ Ty, UserType, }; use rustc_session::lint; -use rustc_span::hygiene::DesugaringKind; +use rustc_session::lint::builtin::BARE_TRAIT_OBJECTS; +use rustc_session::parse::feature_err; use rustc_span::source_map::{original_sp, DUMMY_SP}; use rustc_span::symbol::{kw, sym, Ident}; use rustc_span::{self, BytePos, MultiSpan, Span}; +use rustc_span::{hygiene::DesugaringKind, Symbol}; use rustc_trait_selection::infer::InferCtxtExt as _; use rustc_trait_selection::opaque_types::InferCtxtExt as _; use rustc_trait_selection::traits::error_reporting::InferCtxtExt as _; @@ -39,6 +42,7 @@ use rustc_trait_selection::traits::{ }; use std::collections::hash_map::Entry; +use std::iter; use std::slice; impl<'a, 'tcx> FnCtxt<'a, 'tcx> { @@ -362,6 +366,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { parent_id: hir::HirId, value: T, value_span: Span, + feature: Option, ) -> T { let parent_def_id = self.tcx.hir().local_def_id(parent_id); debug!( @@ -380,7 +385,21 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let mut opaque_types = self.opaque_types.borrow_mut(); let mut opaque_types_vars = self.opaque_types_vars.borrow_mut(); + for (ty, decl) in opaque_type_map { + if let Some(feature) = feature { + if let hir::OpaqueTyOrigin::TyAlias = decl.origin { + if !self.tcx.features().enabled(feature) { + feature_err( + &self.tcx.sess.parse_sess, + feature, + value_span, + "type alias impl trait is not permitted here", + ) + .emit(); + } + } + } let _ = opaque_types.insert(ty, decl); let _ = opaque_types_vars.insert(decl.concrete_ty, decl.opaque_type); } @@ -457,7 +476,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } pub fn to_ty(&self, ast_t: &hir::Ty<'_>) -> Ty<'tcx> { - let t = AstConv::ast_ty_to_ty(self, ast_t); + let t = >::ast_ty_to_ty(self, ast_t); self.register_wf_obligation(t.into(), ast_t.span, traits::MiscObligation); t } @@ -839,7 +858,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // out unconstrained or ambiguous, as we're // just trying to get hints here. self.save_and_restore_in_snapshot_flag(|_| { - let mut fulfill = TraitEngine::new(self.tcx); + let mut fulfill = >::new(self.tcx); for obligation in ok.obligations { fulfill.register_predicate_obligation(self, obligation); } @@ -886,12 +905,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// Resolves an associated value path into a base type and associated constant, or method /// resolution. The newly resolved definition is written into `type_dependent_defs`. - pub fn resolve_ty_and_res_ufcs<'b>( + pub fn resolve_ty_and_res_ufcs( &self, - qpath: &'b QPath<'b>, + qpath: &'tcx QPath<'tcx>, hir_id: hir::HirId, span: Span, - ) -> (Res, Option>, &'b [hir::PathSegment<'b>]) { + ) -> (Res, Option>, &'tcx [hir::PathSegment<'tcx>]) { debug!("resolve_ty_and_res_ufcs: qpath={:?} hir_id={:?} span={:?}", qpath, hir_id, span); let (ty, qself, item_segment) = match *qpath { QPath::Resolved(ref opt_qself, ref path) => { @@ -932,6 +951,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { result }); + if result.is_ok() { + self.maybe_lint_bare_trait(qpath, hir_id); + } + // Write back the new resolution. self.write_resolution(hir_id, result); ( @@ -941,6 +964,29 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ) } + fn maybe_lint_bare_trait(&self, qpath: &QPath<'_>, hir_id: hir::HirId) { + if let QPath::TypeRelative(self_ty, _) = qpath { + if let TyKind::TraitObject([poly_trait_ref, ..], _, TraitObjectSyntax::None) = + self_ty.kind + { + self.tcx.struct_span_lint_hir(BARE_TRAIT_OBJECTS, hir_id, self_ty.span, |lint| { + let mut db = lint + .build(&format!("trait objects without an explicit `dyn` are deprecated")); + let (sugg, app) = match self.tcx.sess.source_map().span_to_snippet(self_ty.span) + { + Ok(s) if poly_trait_ref.trait_ref.path.is_global() => { + (format!("", s), Applicability::MachineApplicable) + } + Ok(s) => (format!("", s), Applicability::MachineApplicable), + Err(_) => (">".to_string(), Applicability::HasPlaceholders), + }; + db.span_suggestion(self_ty.span, "use `dyn`", sugg, app); + db.emit() + }); + } + } + } + /// Given a function `Node`, return its `FnDecl` if it exists, or `None` otherwise. pub(in super::super) fn get_node_fn_decl( &self, @@ -1101,7 +1147,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ( hir::ItemKind::OpaqueTy(hir::OpaqueTy { bounds: last_bounds, .. }), hir::ItemKind::OpaqueTy(hir::OpaqueTy { bounds: exp_bounds, .. }), - ) if last_bounds.iter().zip(exp_bounds.iter()).all(|(left, right)| { + ) if iter::zip(*last_bounds, *exp_bounds).all(|(left, right)| { match (left, right) { ( hir::GenericBound::Trait(tl, ml), @@ -1159,9 +1205,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let path_segs = match res { Res::Local(_) | Res::SelfCtor(_) => vec![], - Res::Def(kind, def_id) => { - AstConv::def_ids_for_value_path_segments(self, segments, self_ty, kind, def_id) - } + Res::Def(kind, def_id) => >::def_ids_for_value_path_segments( + self, segments, self_ty, kind, def_id, + ), _ => bug!("instantiate_value_path on {:?}", res), }; @@ -1204,7 +1250,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // errors if type parameters are provided in an inappropriate place. let generic_segs: FxHashSet<_> = path_segs.iter().map(|PathSeg(_, index)| index).collect(); - let generics_has_err = AstConv::prohibit_generics( + let generics_has_err = >::prohibit_generics( self, segments.iter().enumerate().filter_map(|(index, seg)| { if !generic_segs.contains(&index) || is_alias_variant_ctor { @@ -1247,7 +1293,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if let GenericArgCountResult { correct: Err(GenericArgCountMismatch { reported: Some(_), .. }), .. - } = AstConv::check_generic_arg_count_for_call( + } = >::check_generic_arg_count_for_call( tcx, span, def_id, @@ -1355,12 +1401,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ) -> subst::GenericArg<'tcx> { match (¶m.kind, arg) { (GenericParamDefKind::Lifetime, GenericArg::Lifetime(lt)) => { - AstConv::ast_region_to_region(self.fcx, lt, Some(param)).into() + >::ast_region_to_region(self.fcx, lt, Some(param)).into() } (GenericParamDefKind::Type { .. }, GenericArg::Type(ty)) => { self.fcx.to_ty(ty).into() } - (GenericParamDefKind::Const, GenericArg::Const(ct)) => { + (GenericParamDefKind::Const { .. }, GenericArg::Const(ct)) => { self.fcx.const_arg_to_const(&ct.value, param.def_id).into() } _ => unreachable!(), @@ -1398,23 +1444,25 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.fcx.var_for_def(self.span, param) } } - GenericParamDefKind::Const => { - // FIXME(const_generics_defaults) - // No const parameters were provided, we have to infer them. - self.fcx.var_for_def(self.span, param) + GenericParamDefKind::Const { has_default, .. } => { + if !infer_args && has_default { + tcx.const_param_default(param.def_id).into() + } else { + self.fcx.var_for_def(self.span, param) + } } } } } let substs = self_ctor_substs.unwrap_or_else(|| { - AstConv::create_substs_for_generic_args( + >::create_substs_for_generic_args( tcx, def_id, &[][..], has_self, self_ty, - arg_count, + &arg_count, &mut CreateCtorSubstsContext { fcx: self, span, diff --git a/compiler/rustc_typeck/src/check/fn_ctxt/checks.rs b/compiler/rustc_typeck/src/check/fn_ctxt/checks.rs index 5b8b25c210018..80b5a9d4e621a 100644 --- a/compiler/rustc_typeck/src/check/fn_ctxt/checks.rs +++ b/compiler/rustc_typeck/src/check/fn_ctxt/checks.rs @@ -23,6 +23,7 @@ use rustc_span::{self, MultiSpan, Span}; use rustc_trait_selection::traits::{self, ObligationCauseCode, StatementAsExpression}; use crate::structured_errors::StructuredDiagnostic; +use std::iter; use std::slice; impl<'a, 'tcx> FnCtxt<'a, 'tcx> { @@ -108,7 +109,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // All the input types from the fn signature must outlive the call // so as to validate implied bounds. - for (&fn_input_ty, arg_expr) in fn_inputs.iter().zip(args.iter()) { + for (&fn_input_ty, arg_expr) in iter::zip(fn_inputs, args) { self.register_wf_obligation(fn_input_ty.into(), arg_expr.span, traits::MiscObligation); } @@ -439,7 +440,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { qpath: &QPath<'_>, hir_id: hir::HirId, ) -> Option<(&'tcx ty::VariantDef, Ty<'tcx>)> { - let path_span = qpath.qself_span(); + let path_span = qpath.span(); let (def, ty) = self.finish_resolving_struct_path(qpath, path_span, hir_id); let variant = match def { Res::Err => { @@ -875,7 +876,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { match *qpath { QPath::Resolved(ref maybe_qself, ref path) => { let self_ty = maybe_qself.as_ref().map(|qself| self.to_ty(qself)); - let ty = AstConv::res_to_ty(self, self_ty, path, true); + let ty = >::res_to_ty(self, self_ty, path, true); (path.res, ty) } QPath::TypeRelative(ref qself, ref segment) => { @@ -886,8 +887,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } else { Res::Err }; - let result = - AstConv::associated_path_to_ty(self, hir_id, path_span, ty, res, segment, true); + let result = >::associated_path_to_ty( + self, hir_id, path_span, ty, res, segment, true, + ); let ty = result.map(|(ty, _, _)| ty).unwrap_or_else(|_| self.tcx().ty_error()); let result = result.map(|(_, kind, def_id)| (kind, def_id)); @@ -1000,7 +1002,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // would trigger in `is_send::();` // from `typeck-default-trait-impl-assoc-type.rs`. } else { - let ty = AstConv::ast_ty_to_ty(self, hir_ty); + let ty = >::ast_ty_to_ty(self, hir_ty); let ty = self.resolve_vars_if_possible(ty); if ty == predicate.self_ty() { error.obligation.cause.make_mut().span = hir_ty.span; diff --git a/compiler/rustc_typeck/src/check/fn_ctxt/suggestions.rs b/compiler/rustc_typeck/src/check/fn_ctxt/suggestions.rs index 9f15993e4718c..b758334484535 100644 --- a/compiler/rustc_typeck/src/check/fn_ctxt/suggestions.rs +++ b/compiler/rustc_typeck/src/check/fn_ctxt/suggestions.rs @@ -41,21 +41,21 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { expr: &'tcx hir::Expr<'tcx>, expected: Ty<'tcx>, found: Ty<'tcx>, - cause_span: Span, blk_id: hir::HirId, ) -> bool { let expr = expr.peel_drop_temps(); // If the expression is from an external macro, then do not suggest // adding a semicolon, because there's nowhere to put it. // See issue #81943. - if expr.can_have_side_effects() && !in_external_macro(self.tcx.sess, cause_span) { - self.suggest_missing_semicolon(err, expr, expected, cause_span); + if expr.can_have_side_effects() && !in_external_macro(self.tcx.sess, expr.span) { + self.suggest_missing_semicolon(err, expr, expected); } let mut pointing_at_return_type = false; if let Some((fn_decl, can_suggest)) = self.get_fn_decl(blk_id) { pointing_at_return_type = self.suggest_missing_return_type(err, &fn_decl, expected, found, can_suggest); - self.suggest_missing_return_expr(err, expr, &fn_decl, expected, found); + let fn_id = self.tcx.hir().get_return_block(blk_id).unwrap(); + self.suggest_missing_return_expr(err, expr, &fn_decl, expected, found, fn_id); } pointing_at_return_type } @@ -204,6 +204,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { found: Ty<'tcx>, expected_ty_expr: Option<&'tcx hir::Expr<'tcx>>, ) { + let expr = expr.peel_blocks(); if let Some((sp, msg, suggestion, applicability)) = self.check_ref(expr, found, expected) { err.span_suggestion(sp, msg, suggestion, applicability); } else if let (ty::FnDef(def_id, ..), true) = @@ -218,8 +219,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.is_hir_id_from_struct_pattern_shorthand_field(expr.hir_id, expr.span); let methods = self.get_conversion_methods(expr.span, expected, found, expr.hir_id); if let Ok(expr_text) = self.sess().source_map().span_to_snippet(expr.span) { - let mut suggestions = iter::repeat(&expr_text) - .zip(methods.iter()) + let mut suggestions = iter::zip(iter::repeat(&expr_text), &methods) .filter_map(|(receiver, method)| { let method_call = format!(".{}()", method.ident); if receiver.ends_with(&method_call) { @@ -388,7 +388,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { err: &mut DiagnosticBuilder<'_>, expression: &'tcx hir::Expr<'tcx>, expected: Ty<'tcx>, - cause_span: Span, ) { if expected.is_unit() { // `BlockTailExpression` only relevant if the tail expr would be @@ -403,7 +402,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if expression.can_have_side_effects() => { err.span_suggestion( - cause_span.shrink_to_hi(), + expression.span.shrink_to_hi(), "consider using a semicolon here", ";".to_string(), Applicability::MachineApplicable, @@ -461,7 +460,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // are not, the expectation must have been caused by something else. debug!("suggest_missing_return_type: return type {:?} node {:?}", ty, ty.kind); let sp = ty.span; - let ty = AstConv::ast_ty_to_ty(self, ty); + let ty = >::ast_ty_to_ty(self, ty); debug!("suggest_missing_return_type: return type {:?}", ty); debug!("suggest_missing_return_type: expected type {:?}", ty); if ty.kind() == expected.kind() { @@ -480,14 +479,16 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { fn_decl: &hir::FnDecl<'_>, expected: Ty<'tcx>, found: Ty<'tcx>, + id: hir::HirId, ) { if !expected.is_unit() { return; } let found = self.resolve_vars_with_obligations(found); if let hir::FnRetTy::Return(ty) = fn_decl.output { - let ty = AstConv::ast_ty_to_ty(self, ty); - let ty = self.tcx.erase_late_bound_regions(Binder::bind(ty)); + let ty = >::ast_ty_to_ty(self, ty); + let bound_vars = self.tcx.late_bound_vars(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); if self.can_coerce(found, ty) { err.multipart_suggestion( diff --git a/compiler/rustc_typeck/src/check/gather_locals.rs b/compiler/rustc_typeck/src/check/gather_locals.rs index 825ebc19fa6da..2683e886eeb0f 100644 --- a/compiler/rustc_typeck/src/check/gather_locals.rs +++ b/compiler/rustc_typeck/src/check/gather_locals.rs @@ -4,9 +4,8 @@ use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor}; use rustc_hir::PatKind; use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; use rustc_middle::ty::Ty; -use rustc_span::Span; +use rustc_span::{sym, Span}; use rustc_trait_selection::traits; -use std::mem; pub(super) struct GatherLocalsVisitor<'a, 'tcx> { fcx: &'a FnCtxt<'a, 'tcx>, @@ -14,12 +13,12 @@ pub(super) struct GatherLocalsVisitor<'a, 'tcx> { // parameters are special cases of patterns, but we want to handle them as // *distinct* cases. so track when we are hitting a pattern *within* an fn // parameter. - outermost_fn_param_pat: bool, + outermost_fn_param_pat: Option, } impl<'a, 'tcx> GatherLocalsVisitor<'a, 'tcx> { pub(super) fn new(fcx: &'a FnCtxt<'a, 'tcx>, parent_id: hir::HirId) -> Self { - Self { fcx, parent_id, outermost_fn_param_pat: false } + Self { fcx, parent_id, outermost_fn_param_pat: None } } fn assign(&mut self, span: Span, nid: hir::HirId, ty_opt: Option>) -> Ty<'tcx> { @@ -58,11 +57,12 @@ impl<'a, 'tcx> Visitor<'tcx> for GatherLocalsVisitor<'a, 'tcx> { Some(ref ty) => { let o_ty = self.fcx.to_ty(&ty); - let revealed_ty = if self.fcx.tcx.features().impl_trait_in_bindings { - self.fcx.instantiate_opaque_types_from_value(self.parent_id, o_ty, ty.span) - } else { - o_ty - }; + let revealed_ty = self.fcx.instantiate_opaque_types_from_value( + self.parent_id, + o_ty, + ty.span, + Some(sym::impl_trait_in_bindings), + ); let c_ty = self.fcx.inh.infcx.canonicalize_user_type_annotation(UserType::Ty(revealed_ty)); @@ -91,7 +91,7 @@ impl<'a, 'tcx> Visitor<'tcx> for GatherLocalsVisitor<'a, 'tcx> { } fn visit_param(&mut self, param: &'tcx hir::Param<'tcx>) { - let old_outermost_fn_param_pat = mem::replace(&mut self.outermost_fn_param_pat, true); + let old_outermost_fn_param_pat = self.outermost_fn_param_pat.replace(param.ty_span); intravisit::walk_param(self, param); self.outermost_fn_param_pat = old_outermost_fn_param_pat; } @@ -101,12 +101,12 @@ impl<'a, 'tcx> Visitor<'tcx> for GatherLocalsVisitor<'a, 'tcx> { if let PatKind::Binding(_, _, ident, _) = p.kind { let var_ty = self.assign(p.span, p.hir_id, None); - if self.outermost_fn_param_pat { + if let Some(ty_span) = self.outermost_fn_param_pat { if !self.fcx.tcx.features().unsized_fn_params { self.fcx.require_type_is_sized( var_ty, p.span, - traits::SizedArgumentType(Some(p.span)), + traits::SizedArgumentType(Some(ty_span)), ); } } else { @@ -122,7 +122,7 @@ impl<'a, 'tcx> Visitor<'tcx> for GatherLocalsVisitor<'a, 'tcx> { var_ty ); } - let old_outermost_fn_param_pat = mem::replace(&mut self.outermost_fn_param_pat, false); + let old_outermost_fn_param_pat = self.outermost_fn_param_pat.take(); intravisit::walk_pat(self, p); self.outermost_fn_param_pat = old_outermost_fn_param_pat; } diff --git a/compiler/rustc_typeck/src/check/generator_interior.rs b/compiler/rustc_typeck/src/check/generator_interior.rs index 91708465b3f60..e40aa91485858 100644 --- a/compiler/rustc_typeck/src/check/generator_interior.rs +++ b/compiler/rustc_typeck/src/check/generator_interior.rs @@ -186,7 +186,10 @@ pub fn resolve_interior<'a, 'tcx>( // which means that none of the regions inside relate to any other, even if // typeck had previously found constraints that would cause them to be related. let folded = fcx.tcx.fold_regions(erased, &mut false, |_, current_depth| { - let br = ty::BoundRegion { kind: ty::BrAnon(counter) }; + let br = ty::BoundRegion { + var: ty::BoundVar::from_u32(counter), + kind: ty::BrAnon(counter), + }; let r = fcx.tcx.mk_region(ty::ReLateBound(current_depth, br)); counter += 1; r @@ -202,11 +205,15 @@ pub fn resolve_interior<'a, 'tcx>( // Extract type components to build the witness type. let type_list = fcx.tcx.mk_type_list(type_causes.iter().map(|cause| cause.ty)); - let witness = fcx.tcx.mk_generator_witness(ty::Binder::bind(type_list)); + let bound_vars = fcx.tcx.mk_bound_variable_kinds( + (0..counter).map(|i| ty::BoundVariableKind::Region(ty::BrAnon(i))), + ); + let witness = + fcx.tcx.mk_generator_witness(ty::Binder::bind_with_vars(type_list, bound_vars.clone())); // Store the generator types and spans into the typeck results for this generator. visitor.fcx.inh.typeck_results.borrow_mut().generator_interior_types = - ty::Binder::bind(type_causes); + ty::Binder::bind_with_vars(type_causes, bound_vars); debug!( "types in generator after region replacement {:?}, span = {:?}", diff --git a/compiler/rustc_typeck/src/check/inherited.rs b/compiler/rustc_typeck/src/check/inherited.rs index 0011a3fc71b59..1dacbade1bd32 100644 --- a/compiler/rustc_typeck/src/check/inherited.rs +++ b/compiler/rustc_typeck/src/check/inherited.rs @@ -117,7 +117,7 @@ impl Inherited<'a, 'tcx> { maybe_typeck_results: infcx.in_progress_typeck_results, }, infcx, - fulfillment_cx: RefCell::new(TraitEngine::new(tcx)), + fulfillment_cx: RefCell::new(>::new(tcx)), locals: RefCell::new(Default::default()), deferred_sized_obligations: RefCell::new(Vec::new()), deferred_call_resolutions: RefCell::new(Default::default()), diff --git a/compiler/rustc_typeck/src/check/intrinsic.rs b/compiler/rustc_typeck/src/check/intrinsic.rs index dedf96863eaf6..5741b6824b5d7 100644 --- a/compiler/rustc_typeck/src/check/intrinsic.rs +++ b/compiler/rustc_typeck/src/check/intrinsic.rs @@ -101,12 +101,21 @@ pub fn check_intrinsic_type(tcx: TyCtxt<'_>, it: &hir::ForeignItem<'_>) { let intrinsic_name = tcx.item_name(it.def_id.to_def_id()); let name_str = intrinsic_name.as_str(); + let bound_vars = tcx.mk_bound_variable_kinds( + [ty::BoundVariableKind::Region(ty::BrAnon(0)), ty::BoundVariableKind::Region(ty::BrEnv)] + .iter() + .copied(), + ); let mk_va_list_ty = |mutbl| { tcx.lang_items().va_list().map(|did| { - let region = tcx - .mk_region(ty::ReLateBound(ty::INNERMOST, ty::BoundRegion { kind: ty::BrAnon(0) })); - let env_region = - tcx.mk_region(ty::ReLateBound(ty::INNERMOST, ty::BoundRegion { kind: ty::BrEnv })); + let region = tcx.mk_region(ty::ReLateBound( + ty::INNERMOST, + ty::BoundRegion { var: ty::BoundVar::from_u32(0), kind: ty::BrAnon(0) }, + )); + let env_region = tcx.mk_region(ty::ReLateBound( + ty::INNERMOST, + ty::BoundRegion { var: ty::BoundVar::from_u32(1), kind: ty::BrEnv }, + )); let va_list_ty = tcx.type_of(did).subst(tcx, &[region.into()]); (tcx.mk_ref(env_region, ty::TypeAndMut { ty: va_list_ty, mutbl }), va_list_ty) }) @@ -305,7 +314,7 @@ pub fn check_intrinsic_type(tcx: TyCtxt<'_>, it: &hir::ForeignItem<'_>) { tcx.associated_items(tcx.lang_items().discriminant_kind_trait().unwrap()); let discriminant_def_id = assoc_items.in_definition_order().next().unwrap().def_id; - let br = ty::BoundRegion { kind: ty::BrAnon(0) }; + let br = ty::BoundRegion { var: ty::BoundVar::from_u32(0), kind: ty::BrAnon(0) }; ( 1, vec![ @@ -366,7 +375,7 @@ pub fn check_intrinsic_type(tcx: TyCtxt<'_>, it: &hir::ForeignItem<'_>) { (n_tps, inputs, output, unsafety) }; let sig = tcx.mk_fn_sig(inputs.into_iter(), output, false, unsafety, Abi::RustIntrinsic); - let sig = ty::Binder::bind(sig); + let sig = ty::Binder::bind_with_vars(sig, bound_vars); equate_intrinsic_type(tcx, it, n_tps, sig) } @@ -398,7 +407,8 @@ pub fn check_platform_intrinsic_type(tcx: TyCtxt<'_>, it: &hir::ForeignItem<'_>) | sym::simd_fpow | sym::simd_saturating_add | sym::simd_saturating_sub => (1, vec![param(0), param(0)], param(0)), - sym::simd_fsqrt + sym::simd_neg + | sym::simd_fsqrt | sym::simd_fsin | sym::simd_fcos | sym::simd_fexp @@ -407,8 +417,10 @@ pub fn check_platform_intrinsic_type(tcx: TyCtxt<'_>, it: &hir::ForeignItem<'_>) | sym::simd_flog10 | sym::simd_flog | sym::simd_fabs + | sym::simd_ceil | sym::simd_floor - | sym::simd_ceil => (1, vec![param(0)], param(0)), + | sym::simd_round + | sym::simd_trunc => (1, vec![param(0)], param(0)), sym::simd_fpowi => (1, vec![param(0), tcx.types.i32], param(0)), sym::simd_fma => (1, vec![param(0), param(0), param(0)], param(0)), sym::simd_gather => (3, vec![param(0), param(1), param(2)], param(0)), diff --git a/compiler/rustc_typeck/src/check/method/confirm.rs b/compiler/rustc_typeck/src/check/method/confirm.rs index e5f19281b0773..f546a0d896354 100644 --- a/compiler/rustc_typeck/src/check/method/confirm.rs +++ b/compiler/rustc_typeck/src/check/method/confirm.rs @@ -15,6 +15,7 @@ use rustc_middle::ty::{self, GenericParamDefKind, Ty}; use rustc_span::Span; use rustc_trait_selection::traits; +use std::iter; use std::ops::Deref; struct ConfirmContext<'a, 'tcx> { @@ -118,7 +119,7 @@ impl<'a, 'tcx> ConfirmContext<'a, 'tcx> { // We won't add these if we encountered an illegal sized bound, so that we can use // a custom error in that case. if illegal_sized_bound.is_none() { - let method_ty = self.tcx.mk_fn_ptr(ty::Binder::bind(method_sig)); + let method_ty = self.tcx.mk_fn_ptr(ty::Binder::bind(method_sig, self.tcx)); self.add_obligations(method_ty, all_substs, method_predicates); } @@ -155,32 +156,46 @@ impl<'a, 'tcx> ConfirmContext<'a, 'tcx> { let mut target = self.structurally_resolved_type(autoderef.span(), autoderef.final_ty(false)); - if let Some(mutbl) = pick.autoref { - let region = self.next_region_var(infer::Autoref(self.span, pick.item)); - target = self.tcx.mk_ref(region, ty::TypeAndMut { mutbl, ty: target }); - let mutbl = match mutbl { - hir::Mutability::Not => AutoBorrowMutability::Not, - hir::Mutability::Mut => AutoBorrowMutability::Mut { - // Method call receivers are the primary use case - // for two-phase borrows. - allow_two_phase_borrow: AllowTwoPhase::Yes, - }, - }; - adjustments - .push(Adjustment { kind: Adjust::Borrow(AutoBorrow::Ref(region, mutbl)), target }); - - if let Some(unsize_target) = pick.unsize { - target = self - .tcx - .mk_ref(region, ty::TypeAndMut { mutbl: mutbl.into(), ty: unsize_target }); - adjustments.push(Adjustment { kind: Adjust::Pointer(PointerCast::Unsize), target }); + match &pick.autoref_or_ptr_adjustment { + Some(probe::AutorefOrPtrAdjustment::Autoref { mutbl, unsize }) => { + let region = self.next_region_var(infer::Autoref(self.span, pick.item)); + target = self.tcx.mk_ref(region, ty::TypeAndMut { mutbl: *mutbl, ty: target }); + let mutbl = match mutbl { + hir::Mutability::Not => AutoBorrowMutability::Not, + hir::Mutability::Mut => AutoBorrowMutability::Mut { + // Method call receivers are the primary use case + // for two-phase borrows. + allow_two_phase_borrow: AllowTwoPhase::Yes, + }, + }; + adjustments.push(Adjustment { + kind: Adjust::Borrow(AutoBorrow::Ref(region, mutbl)), + target, + }); + + if let Some(unsize_target) = unsize { + target = self + .tcx + .mk_ref(region, ty::TypeAndMut { mutbl: mutbl.into(), ty: unsize_target }); + adjustments + .push(Adjustment { kind: Adjust::Pointer(PointerCast::Unsize), target }); + } + } + Some(probe::AutorefOrPtrAdjustment::ToConstPtr) => { + target = match target.kind() { + ty::RawPtr(ty::TypeAndMut { ty, mutbl }) => { + assert_eq!(*mutbl, hir::Mutability::Mut); + self.tcx.mk_ptr(ty::TypeAndMut { mutbl: hir::Mutability::Not, ty }) + } + other => panic!("Cannot adjust receiver type {:?} to const ptr", other), + }; + + adjustments.push(Adjustment { + kind: Adjust::Pointer(PointerCast::MutToConstPointer), + target, + }); } - } else { - // No unsizing should be performed without autoref (at - // least during method dispach). This is because we - // currently only unsize `[T;N]` to `[T]`, and naturally - // that must occur being a reference. - assert!(pick.unsize.is_none()); + None => {} } self.register_predicates(autoderef.into_obligations()); @@ -300,7 +315,7 @@ impl<'a, 'tcx> ConfirmContext<'a, 'tcx> { // variables. let generics = self.tcx.generics_of(pick.item.def_id); - let arg_count_correct = AstConv::check_generic_arg_count_for_call( + let arg_count_correct = >::check_generic_arg_count_for_call( self.tcx, self.span, pick.item.def_id, @@ -338,12 +353,13 @@ impl<'a, 'tcx> ConfirmContext<'a, 'tcx> { ) -> subst::GenericArg<'tcx> { match (¶m.kind, arg) { (GenericParamDefKind::Lifetime, GenericArg::Lifetime(lt)) => { - AstConv::ast_region_to_region(self.cfcx.fcx, lt, Some(param)).into() + >::ast_region_to_region(self.cfcx.fcx, lt, Some(param)) + .into() } (GenericParamDefKind::Type { .. }, GenericArg::Type(ty)) => { self.cfcx.to_ty(ty).into() } - (GenericParamDefKind::Const, GenericArg::Const(ct)) => { + (GenericParamDefKind::Const { .. }, GenericArg::Const(ct)) => { self.cfcx.const_arg_to_const(&ct.value, param.def_id).into() } _ => unreachable!(), @@ -359,13 +375,13 @@ impl<'a, 'tcx> ConfirmContext<'a, 'tcx> { self.cfcx.var_for_def(self.cfcx.span, param) } } - AstConv::create_substs_for_generic_args( + >::create_substs_for_generic_args( self.tcx, pick.item.def_id, parent_substs, false, None, - arg_count_correct, + &arg_count_correct, &mut MethodSubstsCtxt { cfcx: self, pick, seg }, ) } @@ -481,10 +497,7 @@ impl<'a, 'tcx> ConfirmContext<'a, 'tcx> { // We don't care about regions here. .filter_map(|obligation| match obligation.predicate.kind().skip_binder() { ty::PredicateKind::Trait(trait_pred, _) if trait_pred.def_id() == sized_def_id => { - let span = predicates - .predicates - .iter() - .zip(predicates.spans.iter()) + let span = iter::zip(&predicates.predicates, &predicates.spans) .find_map( |(p, span)| { if *p == obligation.predicate { Some(*span) } else { None } @@ -537,7 +550,7 @@ impl<'a, 'tcx> ConfirmContext<'a, 'tcx> { upcast_trait_refs.into_iter().next().unwrap() } - fn replace_bound_vars_with_fresh_vars(&self, value: ty::Binder) -> T + fn replace_bound_vars_with_fresh_vars(&self, value: ty::Binder<'tcx, T>) -> T where T: TypeFoldable<'tcx>, { diff --git a/compiler/rustc_typeck/src/check/method/mod.rs b/compiler/rustc_typeck/src/check/method/mod.rs index 9a3d1e42b732a..bd7ffd057b466 100644 --- a/compiler/rustc_typeck/src/check/method/mod.rs +++ b/compiler/rustc_typeck/src/check/method/mod.rs @@ -45,6 +45,7 @@ pub struct MethodCallee<'tcx> { pub sig: ty::FnSig<'tcx>, } +#[derive(Debug)] pub enum MethodError<'tcx> { // Did not find an applicable method, but we did find various near-misses that may work. NoMatch(NoMatchData<'tcx>), @@ -66,6 +67,7 @@ pub enum MethodError<'tcx> { // Contains a list of static methods that may apply, a list of unsatisfied trait predicates which // could lead to matches if satisfied, and a list of not-in-scope traits which may work. +#[derive(Debug)] pub struct NoMatchData<'tcx> { pub static_candidates: Vec, pub unsatisfied_predicates: Vec<(ty::Predicate<'tcx>, Option>)>, @@ -308,7 +310,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // Construct a trait-reference `self_ty : Trait` let substs = InternalSubsts::for_item(self.tcx, trait_def_id, |param, _| { match param.kind { - GenericParamDefKind::Lifetime | GenericParamDefKind::Const => {} + GenericParamDefKind::Lifetime | GenericParamDefKind::Const { .. } => {} GenericParamDefKind::Type { .. } => { if param.index == 0 { return self_ty.into(); @@ -397,7 +399,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { obligations.extend(traits::predicates_for_generics(cause.clone(), self.param_env, bounds)); // Also add an obligation for the method type being well-formed. - let method_ty = tcx.mk_fn_ptr(ty::Binder::bind(fn_sig)); + let method_ty = tcx.mk_fn_ptr(ty::Binder::bind(fn_sig, tcx)); debug!( "lookup_in_trait_adjusted: matched method method_ty={:?} obligation={:?}", method_ty, obligation diff --git a/compiler/rustc_typeck/src/check/method/probe.rs b/compiler/rustc_typeck/src/check/method/probe.rs index 38ff88553e8d1..c2998c841312f 100644 --- a/compiler/rustc_typeck/src/check/method/probe.rs +++ b/compiler/rustc_typeck/src/check/method/probe.rs @@ -83,6 +83,8 @@ struct ProbeContext<'a, 'tcx> { unsatisfied_predicates: Vec<(ty::Predicate<'tcx>, Option>)>, is_suggestion: IsSuggestion, + + scope_expr_id: hir::HirId, } impl<'a, 'tcx> Deref for ProbeContext<'a, 'tcx> { @@ -154,6 +156,42 @@ enum ProbeResult { Match, } +/// When adjusting a receiver we often want to do one of +/// +/// - Add a `&` (or `&mut`), converting the receiver from `T` to `&T` (or `&mut T`) +/// - If the receiver has type `*mut T`, convert it to `*const T` +/// +/// This type tells us which one to do. +/// +/// Note that in principle we could do both at the same time. For example, when the receiver has +/// type `T`, we could autoref it to `&T`, then convert to `*const T`. Or, when it has type `*mut +/// T`, we could convert it to `*const T`, then autoref to `&*const T`. However, currently we do +/// (at most) one of these. Either the receiver has type `T` and we convert it to `&T` (or with +/// `mut`), or it has type `*mut T` and we convert it to `*const T`. +#[derive(Debug, PartialEq, Clone)] +pub enum AutorefOrPtrAdjustment<'tcx> { + /// Receiver has type `T`, add `&` or `&mut` (it `T` is `mut`), and maybe also "unsize" it. + /// Unsizing is used to convert a `[T; N]` to `[T]`, which only makes sense when autorefing. + Autoref { + mutbl: hir::Mutability, + + /// Indicates that the source expression should be "unsized" to a target type. This should + /// probably eventually go away in favor of just coercing method receivers. + unsize: Option>, + }, + /// Receiver has type `*mut T`, convert to `*const T` + ToConstPtr, +} + +impl<'tcx> AutorefOrPtrAdjustment<'tcx> { + fn get_unsize(&self) -> Option> { + match self { + AutorefOrPtrAdjustment::Autoref { mutbl: _, unsize } => unsize.clone(), + AutorefOrPtrAdjustment::ToConstPtr => None, + } + } +} + #[derive(Debug, PartialEq, Clone)] pub struct Pick<'tcx> { pub item: ty::AssocItem, @@ -165,17 +203,9 @@ pub struct Pick<'tcx> { /// A = expr | *expr | **expr | ... pub autoderefs: usize, - /// Indicates that an autoref is applied after the optional autoderefs - /// - /// B = A | &A | &mut A - pub autoref: Option, - - /// Indicates that the source expression should be "unsized" to a - /// target type. This should probably eventually go away in favor - /// of just coercing method receivers. - /// - /// C = B | unsize(B) - pub unsize: Option>, + /// Indicates that we want to add an autoref (and maybe also unsize it), or if the receiver is + /// `*mut T`, convert it to `*const T`. + pub autoref_or_ptr_adjustment: Option>, } #[derive(Clone, Debug, PartialEq, Eq)] @@ -420,6 +450,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { orig_values, steps.steps, is_suggestion, + scope_expr_id, ); probe_cx.assemble_inherent_candidates(); @@ -519,6 +550,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { orig_steps_var_values: OriginalQueryValues<'tcx>, steps: Lrc>>, is_suggestion: IsSuggestion, + scope_expr_id: hir::HirId, ) -> ProbeContext<'a, 'tcx> { ProbeContext { fcx, @@ -536,6 +568,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { private_candidate: None, unsatisfied_predicates: Vec::new(), is_suggestion, + scope_expr_id, } } @@ -1086,6 +1119,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { self.pick_by_value_method(step, self_ty).or_else(|| { self.pick_autorefd_method(step, self_ty, hir::Mutability::Not) .or_else(|| self.pick_autorefd_method(step, self_ty, hir::Mutability::Mut)) + .or_else(|| self.pick_const_ptr_method(step, self_ty)) }) }) .next() @@ -1113,7 +1147,10 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { // Insert a `&*` or `&mut *` if this is a reference type: if let ty::Ref(_, _, mutbl) = *step.self_ty.value.value.kind() { pick.autoderefs += 1; - pick.autoref = Some(mutbl); + pick.autoref_or_ptr_adjustment = Some(AutorefOrPtrAdjustment::Autoref { + mutbl, + unsize: pick.autoref_or_ptr_adjustment.and_then(|a| a.get_unsize()), + }) } pick @@ -1136,8 +1173,39 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { self.pick_method(autoref_ty).map(|r| { r.map(|mut pick| { pick.autoderefs = step.autoderefs; - pick.autoref = Some(mutbl); - pick.unsize = step.unsize.then_some(self_ty); + pick.autoref_or_ptr_adjustment = Some(AutorefOrPtrAdjustment::Autoref { + mutbl, + unsize: step.unsize.then_some(self_ty), + }); + pick + }) + }) + } + + /// If `self_ty` is `*mut T` then this picks `*const T` methods. The reason why we have a + /// special case for this is because going from `*mut T` to `*const T` with autoderefs and + /// autorefs would require dereferencing the pointer, which is not safe. + fn pick_const_ptr_method( + &mut self, + step: &CandidateStep<'tcx>, + self_ty: Ty<'tcx>, + ) -> Option> { + // Don't convert an unsized reference to ptr + if step.unsize { + return None; + } + + let ty = match self_ty.kind() { + ty::RawPtr(ty::TypeAndMut { ty, mutbl: hir::Mutability::Mut }) => ty, + _ => return None, + }; + + let const_self_ty = ty::TypeAndMut { ty, mutbl: hir::Mutability::Not }; + let const_ptr_ty = self.tcx.mk_ptr(const_self_ty); + self.pick_method(const_ptr_ty).map(|r| { + r.map(|mut pick| { + pick.autoderefs = step.autoderefs; + pick.autoref_or_ptr_adjustment = Some(AutorefOrPtrAdjustment::ToConstPtr); pick }) }) @@ -1249,7 +1317,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { ) { self.tcx.struct_span_lint_hir( lint::builtin::UNSTABLE_NAME_COLLISIONS, - self.fcx.body_id, + self.scope_expr_id, self.span, |lint| { let def_kind = stable_pick.item.kind.as_def_kind(); @@ -1510,8 +1578,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { kind: TraitPick, import_ids: probes[0].0.import_ids.clone(), autoderefs: 0, - autoref: None, - unsize: None, + autoref_or_ptr_adjustment: None, }) } @@ -1532,6 +1599,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { self.orig_steps_var_values.clone(), steps, IsSuggestion(true), + self.scope_expr_id, ); pcx.allow_similar_names = true; pcx.assemble_inherent_candidates(); @@ -1638,7 +1706,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { // In general, during probe we erase regions. self.tcx.lifetimes.re_erased.into() } - GenericParamDefKind::Type { .. } | GenericParamDefKind::Const => { + GenericParamDefKind::Type { .. } | GenericParamDefKind::Const { .. } => { self.var_for_def(self.span, param) } } @@ -1691,7 +1759,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { /// region got replaced with the same variable, which requires a bit more coordination /// and/or tracking the substitution and /// so forth. - fn erase_late_bound_regions(&self, value: ty::Binder) -> T + fn erase_late_bound_regions(&self, value: ty::Binder<'tcx, T>) -> T where T: TypeFoldable<'tcx>, { @@ -1700,7 +1768,9 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { /// Finds the method with the appropriate name (or return type, as the case may be). If /// `allow_similar_names` is set, find methods with close-matching names. - fn impl_or_trait_item(&self, def_id: DefId) -> Vec { + // The length of the returned iterator is nearly always 0 or 1 and this + // method is fairly hot. + fn impl_or_trait_item(&self, def_id: DefId) -> SmallVec<[ty::AssocItem; 1]> { if let Some(name) = self.method_name { if self.allow_similar_names { let max_dist = max(name.as_str().len(), 3) / 3; @@ -1716,7 +1786,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { } else { self.fcx .associated_item(def_id, name, Namespace::ValueNS) - .map_or_else(Vec::new, |x| vec![x]) + .map_or_else(SmallVec::new, |x| SmallVec::from_buf([x])) } } else { self.tcx.associated_items(def_id).in_definition_order().copied().collect() @@ -1748,8 +1818,7 @@ impl<'tcx> Candidate<'tcx> { }, import_ids: self.import_ids.clone(), autoderefs: 0, - autoref: None, - unsize: None, + autoref_or_ptr_adjustment: None, } } } diff --git a/compiler/rustc_typeck/src/check/method/suggest.rs b/compiler/rustc_typeck/src/check/method/suggest.rs index e6c551ff4d41b..02fe8312c4c1f 100644 --- a/compiler/rustc_typeck/src/check/method/suggest.rs +++ b/compiler/rustc_typeck/src/check/method/suggest.rs @@ -68,12 +68,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } - pub fn report_method_error<'b>( + pub fn report_method_error( &self, span: Span, rcvr_ty: Ty<'tcx>, item_name: Ident, - source: SelfSource<'b>, + source: SelfSource<'tcx>, error: MethodError<'tcx>, args: Option<&'tcx [hir::Expr<'tcx>]>, ) -> Option> { @@ -323,8 +323,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { err.span_suggestion( lit.span, &format!( - "you must specify a concrete type for \ - this numeric value, like `{}`", + "you must specify a concrete type for this numeric value, \ + like `{}`", concrete_type ), format!("{}_{}", snippet, concrete_type), @@ -975,17 +975,79 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } - fn suggest_traits_to_import<'b>( + fn suggest_traits_to_import( &self, err: &mut DiagnosticBuilder<'_>, span: Span, rcvr_ty: Ty<'tcx>, item_name: Ident, - source: SelfSource<'b>, + source: SelfSource<'tcx>, valid_out_of_scope_traits: Vec, unsatisfied_predicates: &[(ty::Predicate<'tcx>, Option>)], ) { - if self.suggest_valid_traits(err, valid_out_of_scope_traits) { + let mut alt_rcvr_sugg = false; + if let SelfSource::MethodCall(rcvr) = source { + debug!(?span, ?item_name, ?rcvr_ty, ?rcvr); + // Try alternative arbitrary self types that could fulfill this call. + // FIXME: probe for all types that *could* be arbitrary self-types, not + // just this list. + for (rcvr_ty, post) in &[ + (rcvr_ty, ""), + (self.tcx.mk_mut_ref(&ty::ReErased, rcvr_ty), "&mut "), + (self.tcx.mk_imm_ref(&ty::ReErased, rcvr_ty), "&"), + ] { + for (rcvr_ty, pre) in &[ + (self.tcx.mk_lang_item(rcvr_ty, LangItem::OwnedBox), "Box::new"), + (self.tcx.mk_lang_item(rcvr_ty, LangItem::Pin), "Pin::new"), + (self.tcx.mk_diagnostic_item(rcvr_ty, sym::Arc), "Arc::new"), + (self.tcx.mk_diagnostic_item(rcvr_ty, sym::Rc), "Rc::new"), + ] { + if let Some(new_rcvr_t) = *rcvr_ty { + if let Ok(pick) = self.lookup_probe( + span, + item_name, + new_rcvr_t, + rcvr, + crate::check::method::probe::ProbeScope::AllTraits, + ) { + debug!("try_alt_rcvr: pick candidate {:?}", pick); + let did = Some(pick.item.container.id()); + // We don't want to suggest a container type when the missing + // method is `.clone()` or `.deref()` otherwise we'd suggest + // `Arc::new(foo).clone()`, which is far from what the user wants. + let skip = [ + self.tcx.lang_items().clone_trait(), + self.tcx.lang_items().deref_trait(), + self.tcx.lang_items().deref_mut_trait(), + self.tcx.lang_items().drop_trait(), + ] + .contains(&did); + // Make sure the method is defined for the *actual* receiver: we don't + // want to treat `Box` as a receiver if it only works because of + // an autoderef to `&self` + if pick.autoderefs == 0 && !skip { + err.span_label( + pick.item.ident.span, + &format!("the method is available for `{}` here", new_rcvr_t), + ); + err.multipart_suggestion( + "consider wrapping the receiver expression with the \ + appropriate type", + vec![ + (rcvr.span.shrink_to_lo(), format!("{}({}", pre, post)), + (rcvr.span.shrink_to_hi(), ")".to_string()), + ], + Applicability::MaybeIncorrect, + ); + // We don't care about the other suggestions. + alt_rcvr_sugg = true; + } + } + } + } + } + } + if !alt_rcvr_sugg && self.suggest_valid_traits(err, valid_out_of_scope_traits) { return; } @@ -1075,6 +1137,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { "the method might not be found because of this arbitrary self type", ); } + if alt_rcvr_sugg { + return; + } if !candidates.is_empty() { // Sort from most relevant to least relevant. @@ -1284,7 +1349,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// Checks whether there is a local type somewhere in the chain of /// autoderefs of `rcvr_ty`. - fn type_derefs_to_local(&self, span: Span, rcvr_ty: Ty<'tcx>, source: SelfSource<'_>) -> bool { + fn type_derefs_to_local( + &self, + span: Span, + rcvr_ty: Ty<'tcx>, + source: SelfSource<'tcx>, + ) -> bool { fn is_local(ty: Ty<'_>) -> bool { match ty.kind() { ty::Adt(def, _) => def.did.is_local(), @@ -1310,7 +1380,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } -#[derive(Copy, Clone)] +#[derive(Copy, Clone, Debug)] pub enum SelfSource<'a> { QPath(&'a hir::Ty<'a>), MethodCall(&'a hir::Expr<'a> /* rcvr */), @@ -1466,11 +1536,12 @@ impl intravisit::Visitor<'tcx> for UsePlacementFinder<'tcx> { if self.span.map_or(true, |span| item.span < span) { if !item.span.from_expansion() { // Don't insert between attributes and an item. - if item.attrs.is_empty() { + let attrs = self.tcx.hir().attrs(item.hir_id()); + if attrs.is_empty() { self.span = Some(item.span.shrink_to_lo()); } else { // Find the first attribute on the item. - for attr in item.attrs { + for attr in attrs { if self.span.map_or(true, |span| attr.span < span) { self.span = Some(attr.span.shrink_to_lo()); } diff --git a/compiler/rustc_typeck/src/check/mod.rs b/compiler/rustc_typeck/src/check/mod.rs index bb85336d7fb2d..80e173de6b6e5 100644 --- a/compiler/rustc_typeck/src/check/mod.rs +++ b/compiler/rustc_typeck/src/check/mod.rs @@ -121,9 +121,9 @@ use rustc_middle::ty::{self, RegionKind, Ty, TyCtxt, UserType}; use rustc_session::config; use rustc_session::parse::feature_err; use rustc_session::Session; -use rustc_span::source_map::DUMMY_SP; use rustc_span::symbol::{kw, Ident}; use rustc_span::{self, BytePos, MultiSpan, Span}; +use rustc_span::{source_map::DUMMY_SP, sym}; use rustc_target::abi::VariantIdx; use rustc_target::spec::abi::Abi; use rustc_trait_selection::traits; @@ -495,8 +495,9 @@ fn typeck_with_fallback<'tcx>( let fcx = if let (Some(header), Some(decl)) = (fn_header, fn_decl) { let fn_sig = if crate::collect::get_infer_ret_ty(&decl.output).is_some() { let fcx = FnCtxt::new(&inh, param_env, body.value.hir_id); - AstConv::ty_of_fn( + >::ty_of_fn( &fcx, + id, header.unsafety, header.abi, decl, @@ -527,7 +528,7 @@ fn typeck_with_fallback<'tcx>( let fcx = FnCtxt::new(&inh, param_env, body.value.hir_id); let expected_type = body_ty .and_then(|ty| match ty.kind { - hir::TyKind::Infer => Some(AstConv::ast_ty_to_ty(&fcx, ty)), + hir::TyKind::Infer => Some(>::ast_ty_to_ty(&fcx, ty)), _ => None, }) .unwrap_or_else(|| match tcx.hir().get(id) { @@ -539,6 +540,19 @@ fn typeck_with_fallback<'tcx>( kind: TypeVariableOriginKind::TypeInference, span, }), + Node::Expr(&hir::Expr { kind: hir::ExprKind::InlineAsm(ia), .. }) + if ia.operands.iter().any(|(op, _op_sp)| match op { + hir::InlineAsmOperand::Const { anon_const } => { + anon_const.hir_id == id + } + _ => false, + }) => + { + fcx.next_ty_var(TypeVariableOrigin { + kind: TypeVariableOriginKind::MiscVariable, + span, + }) + } _ => fallback(), }, _ => fallback(), @@ -547,11 +561,12 @@ fn typeck_with_fallback<'tcx>( let expected_type = fcx.normalize_associated_types_in(body.value.span, expected_type); fcx.require_type_is_sized(expected_type, body.value.span, traits::ConstSized); - let revealed_ty = if tcx.features().impl_trait_in_bindings { - fcx.instantiate_opaque_types_from_value(id, expected_type, body.value.span) - } else { - expected_type - }; + let revealed_ty = fcx.instantiate_opaque_types_from_value( + id, + expected_type, + body.value.span, + Some(sym::impl_trait_in_bindings), + ); // Gather locals in statics (because of block expressions). GatherLocalsVisitor::new(&fcx, id).visit_body(body); diff --git a/compiler/rustc_typeck/src/check/pat.rs b/compiler/rustc_typeck/src/check/pat.rs index f8ca916caf127..53593b9bab4b8 100644 --- a/compiler/rustc_typeck/src/check/pat.rs +++ b/compiler/rustc_typeck/src/check/pat.rs @@ -680,7 +680,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { &self, pat: &'tcx Pat<'tcx>, qpath: &hir::QPath<'_>, - fields: &'tcx [hir::FieldPat<'tcx>], + fields: &'tcx [hir::PatField<'tcx>], etc: bool, expected: Ty<'tcx>, def_bm: BindingMode, @@ -861,7 +861,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { fn check_pat_tuple_struct( &self, pat: &'tcx Pat<'tcx>, - qpath: &hir::QPath<'_>, + qpath: &'tcx hir::QPath<'tcx>, subpats: &'tcx [&'tcx Pat<'tcx>], ddpos: Option, expected: Ty<'tcx>, @@ -1151,7 +1151,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { adt_ty: Ty<'tcx>, pat: &'tcx Pat<'tcx>, variant: &'tcx ty::VariantDef, - fields: &'tcx [hir::FieldPat<'tcx>], + fields: &'tcx [hir::PatField<'tcx>], etc: bool, def_bm: BindingMode, ti: TopInfo<'tcx>, @@ -1291,7 +1291,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { &self, variant: &VariantDef, pat: &'_ Pat<'_>, - fields: &[hir::FieldPat<'_>], + fields: &[hir::PatField<'_>], ) -> Option> { // if this is a tuple struct, then all field names will be numbers // so if any fields in a struct pattern use shorthand syntax, they will @@ -1446,7 +1446,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { fn error_tuple_variant_as_struct_pat( &self, pat: &Pat<'_>, - fields: &'tcx [hir::FieldPat<'tcx>], + fields: &'tcx [hir::PatField<'tcx>], variant: &ty::VariantDef, ) -> Option> { if let (CtorKind::Fn, PatKind::Struct(qpath, ..)) = (variant.ctor_kind, &pat.kind) { @@ -1484,7 +1484,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { fn get_suggested_tuple_struct_pattern( &self, - fields: &[hir::FieldPat<'_>], + fields: &[hir::PatField<'_>], variant: &VariantDef, ) -> String { let variant_field_idents = variant.fields.iter().map(|f| f.ident).collect::>(); @@ -1528,7 +1528,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { fn error_no_accessible_fields( &self, pat: &Pat<'_>, - fields: &'tcx [hir::FieldPat<'tcx>], + fields: &'tcx [hir::PatField<'tcx>], ) -> DiagnosticBuilder<'tcx> { let mut err = self .tcx @@ -1574,7 +1574,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { &self, pat: &Pat<'_>, unmentioned_fields: &[(&ty::FieldDef, Ident)], - fields: &'tcx [hir::FieldPat<'tcx>], + fields: &'tcx [hir::PatField<'tcx>], ) -> DiagnosticBuilder<'tcx> { let field_names = if unmentioned_fields.len() == 1 { format!("field `{}`", unmentioned_fields[0].1) diff --git a/compiler/rustc_typeck/src/check/place_op.rs b/compiler/rustc_typeck/src/check/place_op.rs index 254e41706f90b..5bd385107ca39 100644 --- a/compiler/rustc_typeck/src/check/place_op.rs +++ b/compiler/rustc_typeck/src/check/place_op.rs @@ -103,9 +103,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let method = self.try_overloaded_place_op(expr.span, self_ty, &[input_ty], PlaceOp::Index); - let result = method.map(|ok| { + if let Some(result) = method { debug!("try_index_step: success, using overloaded indexing"); - let method = self.register_infer_ok_obligations(ok); + let method = self.register_infer_ok_obligations(result); let mut adjustments = self.adjust_steps(autoderef); if let ty::Ref(region, _, hir::Mutability::Not) = method.sig.inputs()[0].kind() { @@ -128,10 +128,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.apply_adjustments(base_expr, adjustments); self.write_method_call(expr.hir_id, method); - (input_ty, self.make_overloaded_place_return_type(method).ty) - }); - if result.is_some() { - return result; + + return Some((input_ty, self.make_overloaded_place_return_type(method).ty)); } } diff --git a/compiler/rustc_typeck/src/check/regionck.rs b/compiler/rustc_typeck/src/check/regionck.rs index 88e8dd3cb129a..8f8514cadb745 100644 --- a/compiler/rustc_typeck/src/check/regionck.rs +++ b/compiler/rustc_typeck/src/check/regionck.rs @@ -354,7 +354,7 @@ impl<'a, 'tcx> Visitor<'tcx> for RegionCtxt<'a, 'tcx> { hir_id: hir::HirId, ) { assert!( - matches!(fk, intravisit::FnKind::Closure(..)), + matches!(fk, intravisit::FnKind::Closure), "visit_fn invoked for something other than a closure" ); @@ -771,21 +771,39 @@ impl<'a, 'tcx> RegionCtxt<'a, 'tcx> { debug!("link_upvar_region(borrorw_region={:?}, upvar_id={:?}", borrow_region, upvar_id); // A by-reference upvar can't be borrowed for longer than the // upvar is borrowed from the environment. - match self.typeck_results.borrow().upvar_capture(upvar_id) { - ty::UpvarCapture::ByRef(upvar_borrow) => { - self.sub_regions( - infer::ReborrowUpvar(span, upvar_id), - borrow_region, - upvar_borrow.region, - ); - if let ty::ImmBorrow = upvar_borrow.kind { - debug!("link_upvar_region: capture by shared ref"); - return; + let closure_local_def_id = upvar_id.closure_expr_id; + let mut all_captures_are_imm_borrow = true; + for captured_place in self + .typeck_results + .borrow() + .closure_min_captures + .get(&closure_local_def_id.to_def_id()) + .and_then(|root_var_min_cap| root_var_min_cap.get(&upvar_id.var_path.hir_id)) + .into_iter() + .flatten() + { + match captured_place.info.capture_kind { + ty::UpvarCapture::ByRef(upvar_borrow) => { + self.sub_regions( + infer::ReborrowUpvar(span, upvar_id), + borrow_region, + upvar_borrow.region, + ); + if let ty::ImmBorrow = upvar_borrow.kind { + debug!("link_upvar_region: capture by shared ref"); + } else { + all_captures_are_imm_borrow = false; + } + } + ty::UpvarCapture::ByValue(_) => { + all_captures_are_imm_borrow = false; } } - ty::UpvarCapture::ByValue(_) => {} } - let fn_hir_id = self.tcx.hir().local_def_id_to_hir_id(upvar_id.closure_expr_id); + if all_captures_are_imm_borrow { + return; + } + let fn_hir_id = self.tcx.hir().local_def_id_to_hir_id(closure_local_def_id); let ty = self.resolve_node_type(fn_hir_id); debug!("link_upvar_region: ty={:?}", ty); diff --git a/compiler/rustc_typeck/src/check/upvar.rs b/compiler/rustc_typeck/src/check/upvar.rs index 69c09528662d3..751eebb9f9564 100644 --- a/compiler/rustc_typeck/src/check/upvar.rs +++ b/compiler/rustc_typeck/src/check/upvar.rs @@ -30,18 +30,18 @@ //! then mean that all later passes would have to check for these figments //! and report an error, and it just seems like more mess in the end.) -use super::writeback::Resolver; 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; use rustc_hir::def_id::LocalDefId; use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor}; use rustc_infer::infer::UpvarRegion; use rustc_middle::hir::place::{Place, PlaceBase, PlaceWithHirId, Projection, ProjectionKind}; -use rustc_middle::ty::fold::TypeFoldable; +use rustc_middle::mir::FakeReadCause; use rustc_middle::ty::{self, Ty, TyCtxt, TypeckResults, UpvarSubsts}; use rustc_session::lint; use rustc_span::sym; @@ -50,6 +50,8 @@ use rustc_span::{MultiSpan, Span, Symbol}; use rustc_index::vec::Idx; use rustc_target::abi::VariantIdx; +use std::iter; + /// Describe the relationship between the paths of two places /// eg: /// - `foo` is ancestor of `foo.bar.baz` @@ -90,7 +92,7 @@ impl<'a, 'tcx> Visitor<'tcx> for InferBorrowKindVisitor<'a, 'tcx> { if let hir::ExprKind::Closure(cc, _, body_id, _, _) = expr.kind { let body = self.fcx.tcx.hir().body(body_id); self.visit_body(body); - self.fcx.analyze_closure(expr.hir_id, expr.span, body, cc); + self.fcx.analyze_closure(expr.hir_id, expr.span, body_id, body, cc); } intravisit::walk_expr(self, expr); @@ -103,6 +105,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { &self, closure_hir_id: hir::HirId, span: Span, + body_id: hir::BodyId, body: &'tcx hir::Body<'tcx>, capture_clause: hir::CaptureBy, ) { @@ -145,6 +148,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { current_closure_kind: ty::ClosureKind::LATTICE_BOTTOM, current_origin: None, capture_information: Default::default(), + fake_reads: Default::default(), }; euv::ExprUseVisitor::new( &mut delegate, @@ -165,7 +169,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let closure_hir_id = self.tcx.hir().local_def_id_to_hir_id(local_def_id); if should_do_migration_analysis(self.tcx, closure_hir_id) { - self.perform_2229_migration_anaysis(closure_def_id, capture_clause, span, body); + self.perform_2229_migration_anaysis(closure_def_id, body_id, capture_clause, span); } // We now fake capture information for all variables that are mentioned within the closure @@ -220,8 +224,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.log_closure_min_capture_info(closure_def_id, span); - self.min_captures_to_closure_captures_bridge(closure_def_id); - // Now that we've analyzed the closure, we know how each // variable is borrowed, and we know what traits the closure // implements (Fn vs FnMut etc). We now have some updates to do @@ -246,6 +248,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let final_tupled_upvars_type = self.tcx.mk_tup(final_upvar_tys.iter()); self.demand_suptype(span, substs.tupled_upvars_ty(), final_tupled_upvars_type); + let fake_reads = delegate + .fake_reads + .into_iter() + .map(|(place, cause, hir_id)| (place, cause, hir_id)) + .collect(); + self.typeck_results.borrow_mut().closure_fake_reads.insert(closure_def_id, fake_reads); + // If we are also inferred the closure kind here, // process any deferred resolutions. let deferred_call_resolutions = self.remove_deferred_call_resolutions(closure_def_id); @@ -284,80 +293,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .collect() } - /// Bridge for closure analysis - /// ---------------------------- - /// - /// For closure with DefId `c`, the bridge converts structures required for supporting RFC 2229, - /// to structures currently used in the compiler for handling closure captures. - /// - /// For example the following structure will be converted: - /// - /// closure_min_captures - /// foo -> [ {foo.x, ImmBorrow}, {foo.y, MutBorrow} ] - /// bar -> [ {bar.z, ByValue}, {bar.q, MutBorrow} ] - /// - /// to - /// - /// 1. closure_captures - /// foo -> UpvarId(foo, c), bar -> UpvarId(bar, c) - /// - /// 2. upvar_capture_map - /// UpvarId(foo,c) -> MutBorrow, UpvarId(bar, c) -> ByValue - fn min_captures_to_closure_captures_bridge(&self, closure_def_id: DefId) { - let mut closure_captures: FxIndexMap = Default::default(); - let mut upvar_capture_map = ty::UpvarCaptureMap::default(); - - if let Some(min_captures) = - self.typeck_results.borrow().closure_min_captures.get(&closure_def_id) - { - for (var_hir_id, min_list) in min_captures.iter() { - for captured_place in min_list { - let place = &captured_place.place; - let capture_info = captured_place.info; - - let upvar_id = match place.base { - PlaceBase::Upvar(upvar_id) => upvar_id, - base => bug!("Expected upvar, found={:?}", base), - }; - - assert_eq!(upvar_id.var_path.hir_id, *var_hir_id); - assert_eq!(upvar_id.closure_expr_id, closure_def_id.expect_local()); - - closure_captures.insert(*var_hir_id, upvar_id); - - let new_capture_kind = - if let Some(capture_kind) = upvar_capture_map.get(&upvar_id) { - // upvar_capture_map only stores the UpvarCapture (CaptureKind), - // so we create a fake capture info with no expression. - let fake_capture_info = ty::CaptureInfo { - capture_kind_expr_id: None, - path_expr_id: None, - capture_kind: *capture_kind, - }; - determine_capture_info(fake_capture_info, capture_info).capture_kind - } else { - capture_info.capture_kind - }; - upvar_capture_map.insert(upvar_id, new_capture_kind); - } - } - } - debug!("For closure_def_id={:?}, closure_captures={:#?}", closure_def_id, closure_captures); - debug!( - "For closure_def_id={:?}, upvar_capture_map={:#?}", - closure_def_id, upvar_capture_map - ); - - if !closure_captures.is_empty() { - self.typeck_results - .borrow_mut() - .closure_captures - .insert(closure_def_id, closure_captures); - - self.typeck_results.borrow_mut().upvar_capture_map.extend(upvar_capture_map); - } - } - /// Analyzes the information collected by `InferBorrowKind` to compute the min number of /// Places (and corresponding capture kind) that we need to keep track of to support all /// the required captured paths. @@ -532,20 +467,20 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { fn perform_2229_migration_anaysis( &self, closure_def_id: DefId, + body_id: hir::BodyId, capture_clause: hir::CaptureBy, span: Span, - body: &'tcx hir::Body<'tcx>, ) { let need_migrations = self.compute_2229_migrations( closure_def_id, span, capture_clause, - body, self.typeck_results.borrow().closure_min_captures.get(&closure_def_id), ); if !need_migrations.is_empty() { - let migrations_text = migration_suggestion_for_2229(self.tcx, &need_migrations); + let (migration_string, migrated_variables_concat) = + migration_suggestion_for_2229(self.tcx, &need_migrations); let local_def_id = closure_def_id.expect_local(); let closure_hir_id = self.tcx.hir().local_def_id_to_hir_id(local_def_id); @@ -557,7 +492,35 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let mut diagnostics_builder = lint.build( "drop order affected for closure because of `capture_disjoint_fields`", ); - diagnostics_builder.note(&migrations_text); + let closure_body_span = self.tcx.hir().span(body_id.hir_id); + let (sugg, app) = + match self.tcx.sess.source_map().span_to_snippet(closure_body_span) { + Ok(s) => { + let trimmed = s.trim_start(); + + // If the closure contains a block then replace the opening brace + // with "{ let _ = (..); " + let sugg = if let Some('{') = trimmed.chars().next() { + format!("{{ {}; {}", migration_string, &trimmed[1..]) + } else { + format!("{{ {}; {} }}", migration_string, s) + }; + (sugg, Applicability::MachineApplicable) + } + Err(_) => (migration_string.clone(), Applicability::HasPlaceholders), + }; + + let diagnostic_msg = format!( + "add a dummy let to cause {} to be fully captured", + migrated_variables_concat + ); + + diagnostics_builder.span_suggestion( + closure_body_span, + &diagnostic_msg, + sugg, + app, + ); diagnostics_builder.emit(); }, ); @@ -578,19 +541,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { closure_def_id: DefId, closure_span: Span, closure_clause: hir::CaptureBy, - body: &'tcx hir::Body<'tcx>, min_captures: Option<&ty::RootVariableMinCaptureList<'tcx>>, ) -> Vec { - fn resolve_ty>( - fcx: &FnCtxt<'_, 'tcx>, - span: Span, - body: &'tcx hir::Body<'tcx>, - ty: T, - ) -> T { - let mut resolver = Resolver::new(fcx, &span, body); - ty.fold_with(&mut resolver) - } - let upvars = if let Some(upvars) = self.tcx.upvars_mentioned(closure_def_id) { upvars } else { @@ -600,7 +552,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let mut need_migrations = Vec::new(); for (&var_hir_id, _) in upvars.iter() { - let ty = resolve_ty(self, closure_span, body, self.node_ty(var_hir_id)); + let ty = self.infcx.resolve_vars_if_possible(self.node_ty(var_hir_id)); if !ty.needs_drop(self.tcx, self.tcx.param_env(closure_def_id.expect_local())) { continue; @@ -701,7 +653,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// `w[c]`. /// Notation: /// - Ty(place): Type of place - /// - `(a, b)`: Represents the function parameters `base_path_ty` and `captured_projs` + /// - `(a, b)`: Represents the function parameters `base_path_ty` and `captured_by_move_projs` /// respectively. /// ``` /// (Ty(w), [ &[p, x], &[c] ]) @@ -762,7 +714,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { closure_def_id: DefId, closure_span: Span, base_path_ty: Ty<'tcx>, - captured_projs: Vec<&[Projection<'tcx>]>, + captured_by_move_projs: Vec<&[Projection<'tcx>]>, ) -> bool { let needs_drop = |ty: Ty<'tcx>| { ty.needs_drop(self.tcx, self.tcx.param_env(closure_def_id.expect_local())) @@ -787,9 +739,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // // eg. If `a.b` is captured and we are processing `a.b`, then we can't have the closure also // capture `a.b.c`, because that voilates min capture. - let is_completely_captured = captured_projs.iter().any(|projs| projs.is_empty()); + let is_completely_captured = captured_by_move_projs.iter().any(|projs| projs.is_empty()); - assert!(!is_completely_captured || (captured_projs.len() == 1)); + assert!(!is_completely_captured || (captured_by_move_projs.len() == 1)); if is_completely_captured { // The place is captured entirely, so doesn't matter if needs dtor, it will be drop @@ -797,23 +749,27 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { return false; } + if captured_by_move_projs.is_empty() { + return needs_drop(base_path_ty); + } + if is_drop_defined_for_ty { // If drop is implemented for this type then we need it to be fully captured, - // which we know it is not because of the previous check. Therefore we need to - // do migrate. - return true; - } + // and we know it is not completely captured because of the previous checks. - if captured_projs.is_empty() { - return needs_drop(base_path_ty); + // Note that this is a bug in the user code that will be reported by the + // borrow checker, since we can't move out of drop types. + + // The bug exists in the user's code pre-migration, and we don't migrate here. + return false; } match base_path_ty.kind() { // Observations: - // - `captured_projs` is not empty. Therefore we can call - // `captured_projs.first().unwrap()` safely. - // - All entries in `captured_projs` have atleast one projection. - // Therefore we can call `captured_projs.first().unwrap().first().unwrap()` safely. + // - `captured_by_move_projs` is not empty. Therefore we can call + // `captured_by_move_projs.first().unwrap()` safely. + // - All entries in `captured_by_move_projs` have atleast one projection. + // Therefore we can call `captured_by_move_projs.first().unwrap().first().unwrap()` safely. // We don't capture derefs in case of move captures, which would have be applied to // access any further paths. @@ -823,19 +779,19 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ty::Adt(def, substs) => { // Multi-varaint enums are captured in entirety, - // which would've been handled in the case of single empty slice in `captured_projs`. + // which would've been handled in the case of single empty slice in `captured_by_move_projs`. assert_eq!(def.variants.len(), 1); // Only Field projections can be applied to a non-box Adt. assert!( - captured_projs.iter().all(|projs| matches!( + captured_by_move_projs.iter().all(|projs| matches!( projs.first().unwrap().kind, ProjectionKind::Field(..) )) ); def.variants.get(VariantIdx::new(0)).unwrap().fields.iter().enumerate().any( |(i, field)| { - let paths_using_field = captured_projs + let paths_using_field = captured_by_move_projs .iter() .filter_map(|projs| { if let ProjectionKind::Field(field_idx, _) = @@ -862,14 +818,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ty::Tuple(..) => { // Only Field projections can be applied to a tuple. assert!( - captured_projs.iter().all(|projs| matches!( + captured_by_move_projs.iter().all(|projs| matches!( projs.first().unwrap().kind, ProjectionKind::Field(..) )) ); base_path_ty.tuple_fields().enumerate().any(|(i, element_ty)| { - let paths_using_field = captured_projs + let paths_using_field = captured_by_move_projs .iter() .filter_map(|projs| { if let ProjectionKind::Field(field_idx, _) = projs.first().unwrap().kind @@ -1053,6 +1009,52 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } +/// Truncate the capture so that the place being borrowed is in accordance with RFC 1240, +/// which states that it's unsafe to take a reference into a struct marked `repr(packed)`. +fn restrict_repr_packed_field_ref_capture<'tcx>( + tcx: TyCtxt<'tcx>, + param_env: ty::ParamEnv<'tcx>, + place: &Place<'tcx>, +) -> Place<'tcx> { + let pos = place.projections.iter().enumerate().position(|(i, p)| { + let ty = place.ty_before_projection(i); + + // Return true for fields of packed structs, unless those fields have alignment 1. + match p.kind { + ProjectionKind::Field(..) => match ty.kind() { + ty::Adt(def, _) if def.repr.packed() => { + match tcx.layout_raw(param_env.and(p.ty)) { + Ok(layout) if layout.align.abi.bytes() == 1 => { + // if the alignment is 1, the type can't be further + // disaligned. + debug!( + "restrict_repr_packed_field_ref_capture: ({:?}) - align = 1", + place + ); + false + } + _ => { + debug!("restrict_repr_packed_field_ref_capture: ({:?}) - true", place); + true + } + } + } + + _ => false, + }, + _ => false, + } + }); + + let mut place = place.clone(); + + if let Some(pos) = pos { + place.projections.truncate(pos); + } + + place +} + struct InferBorrowKind<'a, 'tcx> { fcx: &'a FnCtxt<'a, 'tcx>, @@ -1102,6 +1104,7 @@ struct InferBorrowKind<'a, 'tcx> { /// Place { V1, [ProjectionKind::Field(Index=1, Variant=0)] } : CaptureKind { E2, MutableBorrow } /// ``` capture_information: InferredCaptureInformation<'tcx>, + fake_reads: Vec<(Place<'tcx>, FakeReadCause, hir::HirId)>, } impl<'a, 'tcx> InferBorrowKind<'a, 'tcx> { @@ -1363,6 +1366,12 @@ impl<'a, 'tcx> InferBorrowKind<'a, 'tcx> { } 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 { + self.fake_reads.push((place, cause, diag_expr_id)); + } + } + fn consume( &mut self, place_with_id: &PlaceWithHirId<'tcx>, @@ -1391,8 +1400,15 @@ impl<'a, 'tcx> euv::Delegate<'tcx> for InferBorrowKind<'a, 'tcx> { place_with_id, diag_expr_id, bk ); + let place = restrict_repr_packed_field_ref_capture( + self.fcx.tcx, + self.fcx.param_env, + &place_with_id.place, + ); + 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); + self.init_capture_info_for_place(&place_with_id, diag_expr_id); } match bk { @@ -1409,11 +1425,7 @@ impl<'a, 'tcx> euv::Delegate<'tcx> for InferBorrowKind<'a, 'tcx> { fn mutate(&mut self, assignee_place: &PlaceWithHirId<'tcx>, diag_expr_id: hir::HirId) { debug!("mutate(assignee_place={:?}, diag_expr_id={:?})", assignee_place, diag_expr_id); - if !self.capture_information.contains_key(&assignee_place.place) { - self.init_capture_info_for_place(assignee_place, diag_expr_id); - } - - self.adjust_upvar_borrow_kind_for_mut(assignee_place, diag_expr_id); + self.borrow(assignee_place, diag_expr_id, ty::BorrowKind::MutBorrow); } } @@ -1539,12 +1551,29 @@ fn should_do_migration_analysis(tcx: TyCtxt<'_>, closure_id: hir::HirId) -> bool !matches!(level, lint::Level::Allow) } -fn migration_suggestion_for_2229(tcx: TyCtxt<'_>, need_migrations: &Vec) -> String { - let need_migrations_strings = - need_migrations.iter().map(|v| format!("{}", var_name(tcx, *v))).collect::>(); - let migrations_list_concat = need_migrations_strings.join(", "); +/// Return a two string tuple (s1, s2) +/// - s1: Line of code that is needed for the migration: eg: `let _ = (&x, ...)`. +/// - s2: Comma separated names of the variables being migrated. +fn migration_suggestion_for_2229( + tcx: TyCtxt<'_>, + need_migrations: &Vec, +) -> (String, String) { + let need_migrations_variables = + need_migrations.iter().map(|v| var_name(tcx, *v)).collect::>(); + + let migration_ref_concat = + need_migrations_variables.iter().map(|v| format!("&{}", v)).collect::>().join(", "); + + let migration_string = if 1 == need_migrations.len() { + format!("let _ = {}", migration_ref_concat) + } else { + format!("let _ = ({})", migration_ref_concat) + }; + + let migrated_variables_concat = + need_migrations_variables.iter().map(|v| format!("`{}`", v)).collect::>().join(", "); - format!("drop(&({}));", migrations_list_concat) + (migration_string, migrated_variables_concat) } /// Helper function to determine if we need to escalate CaptureKind from @@ -1559,7 +1588,7 @@ fn migration_suggestion_for_2229(tcx: TyCtxt<'_>, need_migrations: &Vec( let generics = tcx.generics_of(def_id); let is_our_default = |def: &ty::GenericParamDef| match def.kind { - GenericParamDefKind::Type { has_default, .. } => { + GenericParamDefKind::Type { has_default, .. } + | GenericParamDefKind::Const { has_default } => { has_default && def.index >= generics.parent_count as u32 } - _ => unreachable!(), + GenericParamDefKind::Lifetime => unreachable!(), }; // Check that concrete defaults are well-formed. See test `type-check-defaults.rs`. @@ -771,10 +773,15 @@ fn check_where_clauses<'tcx, 'fcx>( fcx.tcx.mk_param_from_def(param) } - - GenericParamDefKind::Const => { - // FIXME(const_generics_defaults) - fcx.tcx.mk_param_from_def(param) + GenericParamDefKind::Const { .. } => { + if is_our_default(param) { + let default_ct = tcx.const_param_default(param.def_id); + // Const params currently have to be concrete. + assert!(!default_ct.needs_subst()); + default_ct.into() + } else { + fcx.tcx.mk_param_from_def(param) + } } } }); @@ -857,7 +864,7 @@ fn check_where_clauses<'tcx, 'fcx>( debug!("check_where_clauses: predicates={:?}", predicates.predicates); assert_eq!(predicates.predicates.len(), predicates.spans.len()); let wf_obligations = - predicates.predicates.iter().zip(predicates.spans.iter()).flat_map(|(&p, &sp)| { + iter::zip(&predicates.predicates, &predicates.spans).flat_map(|(&p, &sp)| { traits::wf::predicate_obligations(fcx, fcx.param_env, fcx.body_id, p, sp) }); @@ -879,8 +886,8 @@ fn check_fn_or_method<'fcx, 'tcx>( let sig = fcx.normalize_associated_types_in(span, sig); let sig = fcx.tcx.liberate_late_bound_regions(def_id, sig); - for (&input_ty, span) in sig.inputs().iter().zip(hir_decl.inputs.iter().map(|t| t.span)) { - fcx.register_wf_obligation(input_ty.into(), span, ObligationCauseCode::MiscObligation); + for (&input_ty, ty) in iter::zip(sig.inputs(), hir_decl.inputs) { + fcx.register_wf_obligation(input_ty.into(), ty.span, ObligationCauseCode::MiscObligation); } implied_bounds.extend(sig.inputs()); @@ -1060,13 +1067,14 @@ fn check_method_receiver<'fcx, 'tcx>( debug!("check_method_receiver: sig={:?}", sig); let self_ty = fcx.normalize_associated_types_in(span, self_ty); - let self_ty = fcx.tcx.liberate_late_bound_regions(method.def_id, ty::Binder::bind(self_ty)); + let self_ty = + fcx.tcx.liberate_late_bound_regions(method.def_id, ty::Binder::bind(self_ty, fcx.tcx)); let receiver_ty = sig.inputs()[0]; let receiver_ty = fcx.normalize_associated_types_in(span, receiver_ty); let receiver_ty = - fcx.tcx.liberate_late_bound_regions(method.def_id, ty::Binder::bind(receiver_ty)); + fcx.tcx.liberate_late_bound_regions(method.def_id, ty::Binder::bind(receiver_ty, fcx.tcx)); if fcx.tcx.features().arbitrary_self_types { if !receiver_is_valid(fcx, span, receiver_ty, self_ty, true) { diff --git a/compiler/rustc_typeck/src/check/writeback.rs b/compiler/rustc_typeck/src/check/writeback.rs index af82a3bb4f59a..e472add6e80f3 100644 --- a/compiler/rustc_typeck/src/check/writeback.rs +++ b/compiler/rustc_typeck/src/check/writeback.rs @@ -4,11 +4,15 @@ use crate::check::FnCtxt; +use rustc_data_structures::stable_map::FxHashMap; use rustc_errors::ErrorReported; use rustc_hir as hir; +use rustc_hir::def_id::DefId; use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor}; use rustc_infer::infer::error_reporting::TypeAnnotationNeeded::E0282; use rustc_infer::infer::InferCtxt; +use rustc_middle::hir::place::Place as HirPlace; +use rustc_middle::mir::FakeReadCause; use rustc_middle::ty::adjustment::{Adjust, Adjustment, PointerCast}; use rustc_middle::ty::fold::{TypeFoldable, TypeFolder}; use rustc_middle::ty::{self, Ty, TyCtxt}; @@ -56,7 +60,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } wbcx.visit_body(body); wbcx.visit_min_capture_map(); - wbcx.visit_upvar_capture_map(); + wbcx.visit_fake_reads_map(); wbcx.visit_closures(); wbcx.visit_liberated_fn_sigs(); wbcx.visit_fru_field_types(); @@ -74,9 +78,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { wbcx.typeck_results.treat_byte_string_as_slice = mem::take(&mut self.typeck_results.borrow_mut().treat_byte_string_as_slice); - wbcx.typeck_results.closure_captures = - mem::take(&mut self.typeck_results.borrow_mut().closure_captures); - if self.is_tainted_by_errors() { // FIXME(eddyb) keep track of `ErrorReported` from where the error was emitted. wbcx.typeck_results.tainted_by_errors = Some(ErrorReported); @@ -363,20 +364,25 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> { self.typeck_results.closure_min_captures = min_captures_wb; } - fn visit_upvar_capture_map(&mut self) { - for (upvar_id, upvar_capture) in self.fcx.typeck_results.borrow().upvar_capture_map.iter() { - let new_upvar_capture = match *upvar_capture { - ty::UpvarCapture::ByValue(span) => ty::UpvarCapture::ByValue(span), - ty::UpvarCapture::ByRef(ref upvar_borrow) => { - ty::UpvarCapture::ByRef(ty::UpvarBorrow { - kind: upvar_borrow.kind, - region: self.tcx().lifetimes.re_erased, - }) - } - }; - debug!("Upvar capture for {:?} resolved to {:?}", upvar_id, new_upvar_capture); - self.typeck_results.upvar_capture_map.insert(*upvar_id, new_upvar_capture); + fn visit_fake_reads_map(&mut self) { + let mut resolved_closure_fake_reads: FxHashMap< + DefId, + Vec<(HirPlace<'tcx>, FakeReadCause, hir::HirId)>, + > = Default::default(); + for (closure_def_id, fake_reads) in + self.fcx.typeck_results.borrow().closure_fake_reads.iter() + { + let mut resolved_fake_reads = Vec::<(HirPlace<'tcx>, FakeReadCause, hir::HirId)>::new(); + for (place, cause, hir_id) in fake_reads.iter() { + let locatable = + self.tcx().hir().local_def_id_to_hir_id(closure_def_id.expect_local()); + + let resolved_fake_read = self.resolve(place.clone(), &locatable); + resolved_fake_reads.push((resolved_fake_read, *cause, *hir_id)); + } + resolved_closure_fake_reads.insert(*closure_def_id, resolved_fake_reads); } + self.typeck_results.closure_fake_reads = resolved_closure_fake_reads; } fn visit_closures(&mut self) { @@ -497,7 +503,8 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> { let mut skip_add = false; if let ty::Opaque(defin_ty_def_id, _substs) = *definition_ty.kind() { - if let hir::OpaqueTyOrigin::Misc = opaque_defn.origin { + if let hir::OpaqueTyOrigin::Misc | hir::OpaqueTyOrigin::TyAlias = opaque_defn.origin + { if def_id == defin_ty_def_id { debug!( "skipping adding concrete definition for opaque type {:?} {:?}", @@ -668,7 +675,7 @@ impl Locatable for hir::HirId { /// The Resolver. This is the type folding engine that detects /// unresolved types and so forth. -crate struct Resolver<'cx, 'tcx> { +struct Resolver<'cx, 'tcx> { tcx: TyCtxt<'tcx>, infcx: &'cx InferCtxt<'cx, 'tcx>, span: &'cx dyn Locatable, @@ -679,7 +686,7 @@ crate struct Resolver<'cx, 'tcx> { } impl<'cx, 'tcx> Resolver<'cx, 'tcx> { - crate fn new( + fn new( fcx: &'cx FnCtxt<'cx, 'tcx>, span: &'cx dyn Locatable, body: &'tcx hir::Body<'tcx>, diff --git a/compiler/rustc_typeck/src/coherence/builtin.rs b/compiler/rustc_typeck/src/coherence/builtin.rs index 5b44cb7eae516..8cae61e8c22f6 100644 --- a/compiler/rustc_typeck/src/coherence/builtin.rs +++ b/compiler/rustc_typeck/src/coherence/builtin.rs @@ -246,7 +246,7 @@ fn visit_implementation_of_dispatch_from_dyn(tcx: TyCtxt<'_>, impl_did: LocalDef )) .emit(); } else { - let mut fulfill_cx = TraitEngine::new(infcx.tcx); + let mut fulfill_cx = >::new(infcx.tcx); for field in coerced_fields { let predicate = predicate_for_trait_def( @@ -506,7 +506,7 @@ pub fn coerce_unsized_info(tcx: TyCtxt<'tcx>, impl_did: DefId) -> CoerceUnsizedI } }; - let mut fulfill_cx = TraitEngine::new(infcx.tcx); + let mut fulfill_cx = >::new(infcx.tcx); // Register an obligation for `A: Trait`. let cause = traits::ObligationCause::misc(span, impl_hir_id); diff --git a/compiler/rustc_typeck/src/coherence/inherent_impls_overlap.rs b/compiler/rustc_typeck/src/coherence/inherent_impls_overlap.rs index 2965409999202..c69389e7b432b 100644 --- a/compiler/rustc_typeck/src/coherence/inherent_impls_overlap.rs +++ b/compiler/rustc_typeck/src/coherence/inherent_impls_overlap.rs @@ -24,8 +24,8 @@ impl InherentOverlapChecker<'tcx> { /// namespace. fn impls_have_common_items( &self, - impl_items1: &ty::AssociatedItems<'_>, - impl_items2: &ty::AssociatedItems<'_>, + impl_items1: &ty::AssocItems<'_>, + impl_items2: &ty::AssocItems<'_>, ) -> bool { let mut impl_items1 = &impl_items1; let mut impl_items2 = &impl_items2; diff --git a/compiler/rustc_typeck/src/collect.rs b/compiler/rustc_typeck/src/collect.rs index 3881d55ef9169..1477418d5d8cf 100644 --- a/compiler/rustc_typeck/src/collect.rs +++ b/compiler/rustc_typeck/src/collect.rs @@ -15,6 +15,8 @@ //! At present, however, we do run collection across all items in the //! crate as a kind of pass. This should eventually be factored away. +// ignore-tidy-filelength + use crate::astconv::{AstConv, SizedByDefault}; use crate::bounds::Bounds; use crate::check::intrinsic::intrinsic_operation_unsafety; @@ -43,13 +45,13 @@ use rustc_middle::ty::util::Discr; use rustc_middle::ty::util::IntTypeExt; use rustc_middle::ty::{self, AdtKind, Const, DefIdTree, ToPolyTraitRef, Ty, TyCtxt}; use rustc_middle::ty::{ReprOptions, ToPredicate, WithConstness}; -use rustc_session::config::SanitizerSet; use rustc_session::lint; use rustc_session::parse::feature_err; use rustc_span::symbol::{kw, sym, Ident, Symbol}; use rustc_span::{Span, DUMMY_SP}; -use rustc_target::spec::abi; +use rustc_target::spec::{abi, SanitizerSet}; use rustc_trait_selection::traits::error_reporting::suggestions::NextTypeParamName; +use std::iter; mod item_bounds; mod type_of; @@ -254,10 +256,15 @@ impl Visitor<'tcx> for CollectItemTypesVisitor<'tcx> { self.tcx.ensure().type_of(def_id); } hir::GenericParamKind::Type { .. } => {} - hir::GenericParamKind::Const { .. } => { + hir::GenericParamKind::Const { default, .. } => { let def_id = self.tcx.hir().local_def_id(param.hir_id); self.tcx.ensure().type_of(def_id); - // FIXME(const_generics_defaults) + if let Some(default) = default { + let default_def_id = self.tcx.hir().local_def_id(default.hir_id); + // need to store default and type of default + self.tcx.ensure().type_of(default_def_id); + self.tcx.ensure().const_param_default(def_id); + } } } } @@ -310,7 +317,7 @@ impl ItemCtxt<'tcx> { } pub fn to_ty(&self, ast_ty: &hir::Ty<'_>) -> Ty<'tcx> { - AstConv::ast_ty_to_ty(self, ast_ty) + >::ast_ty_to_ty(self, ast_ty) } pub fn hir_id(&self) -> hir::HirId { @@ -729,8 +736,14 @@ fn convert_item(tcx: TyCtxt<'_>, item_id: hir::ItemId) { tcx.ensure().generics_of(item.def_id); tcx.ensure().type_of(item.def_id); tcx.ensure().predicates_of(item.def_id); - if let hir::ForeignItemKind::Fn(..) = item.kind { - tcx.ensure().fn_sig(item.def_id); + match item.kind { + hir::ForeignItemKind::Fn(..) => tcx.ensure().fn_sig(item.def_id), + hir::ForeignItemKind::Static(..) => { + let mut visitor = PlaceholderHirTyCollector::default(); + visitor.visit_foreign_item(item); + placeholder_type_error(tcx, None, &[], visitor.0, false, None); + } + _ => (), } } } @@ -1096,7 +1109,7 @@ fn super_predicates_that_define_assoc_type( // Convert the bounds that follow the colon, e.g., `Bar + Zed` in `trait Foo: Bar + Zed`. let self_param_ty = tcx.types.self_param; let superbounds1 = if let Some(assoc_name) = assoc_name { - AstConv::compute_bounds_that_match_assoc_type( + >::compute_bounds_that_match_assoc_type( &icx, self_param_ty, &bounds, @@ -1105,7 +1118,13 @@ fn super_predicates_that_define_assoc_type( assoc_name, ) } else { - AstConv::compute_bounds(&icx, self_param_ty, &bounds, SizedByDefault::No, item.span) + >::compute_bounds( + &icx, + self_param_ty, + &bounds, + SizedByDefault::No, + item.span, + ) }; let superbounds1 = superbounds1.predicates(tcx, self_param_ty); @@ -1232,7 +1251,8 @@ fn has_late_bound_regions<'tcx>(tcx: TyCtxt<'tcx>, node: Node<'tcx>) -> Option {} Some( - rl::Region::LateBound(debruijn, _, _) | rl::Region::LateBoundAnon(debruijn, _), + rl::Region::LateBound(debruijn, _, _, _) + | rl::Region::LateBoundAnon(debruijn, _, _), ) if debruijn < self.outer_index => {} Some( rl::Region::LateBound(..) @@ -1517,7 +1537,7 @@ fn generics_of(tcx: TyCtxt<'_>, def_id: DefId) -> ty::Generics { |lint| { lint.build( "defaults for type parameters are only allowed in \ - `struct`, `enum`, `type`, or `trait` definitions.", + `struct`, `enum`, `type`, or `trait` definitions", ) .emit(); }, @@ -1543,13 +1563,21 @@ fn generics_of(tcx: TyCtxt<'_>, def_id: DefId) -> ty::Generics { i += 1; Some(param_def) } - GenericParamKind::Const { .. } => { + GenericParamKind::Const { default, .. } => { + if !allow_defaults && default.is_some() { + tcx.sess.span_err( + param.span, + "defaults for const parameters are only allowed in \ + `struct`, `enum`, `type`, or `trait` definitions", + ); + } + let param_def = ty::GenericParamDef { index: type_start + i as u32, name: param.name.ident().name, def_id: tcx.hir().local_def_id(param.hir_id).to_def_id(), pure_wrt_drop: param.pure_wrt_drop, - kind: ty::GenericParamDefKind::Const, + kind: ty::GenericParamDefKind::Const { has_default: default.is_some() }, }; i += 1; Some(param_def) @@ -1687,10 +1715,11 @@ fn fn_sig(tcx: TyCtxt<'_>, def_id: DefId) -> ty::PolyFnSig<'_> { } diag.emit(); - ty::Binder::bind(fn_sig) + ty::Binder::bind(fn_sig, tcx) } - None => AstConv::ty_of_fn( + None => >::ty_of_fn( &icx, + hir_id, sig.header.unsafety, sig.header.abi, &sig.decl, @@ -1706,8 +1735,9 @@ fn fn_sig(tcx: TyCtxt<'_>, def_id: DefId) -> ty::PolyFnSig<'_> { ident, generics, .. - }) => AstConv::ty_of_fn( + }) => >::ty_of_fn( &icx, + hir_id, header.unsafety, header.abi, decl, @@ -1729,13 +1759,10 @@ fn fn_sig(tcx: TyCtxt<'_>, def_id: DefId) -> ty::PolyFnSig<'_> { let ty = tcx.type_of(tcx.hir().get_parent_did(hir_id).to_def_id()); let inputs = data.fields().iter().map(|f| tcx.type_of(tcx.hir().local_def_id(f.hir_id))); - ty::Binder::bind(tcx.mk_fn_sig( - inputs, - ty, - false, - hir::Unsafety::Normal, - abi::Abi::Rust, - )) + ty::Binder::bind( + tcx.mk_fn_sig(inputs, ty, false, hir::Unsafety::Normal, abi::Abi::Rust), + tcx, + ) } Expr(&hir::Expr { kind: hir::ExprKind::Closure(..), .. }) => { @@ -1767,7 +1794,7 @@ fn impl_trait_ref(tcx: TyCtxt<'_>, def_id: DefId) -> Option> { match tcx.hir().expect_item(hir_id).kind { hir::ItemKind::Impl(ref impl_) => impl_.of_trait.as_ref().map(|ast_trait_ref| { let selfty = tcx.type_of(def_id); - AstConv::instantiate_mono_trait_ref(&icx, ast_trait_ref, selfty) + >::instantiate_mono_trait_ref(&icx, ast_trait_ref, selfty) }), _ => bug!(), } @@ -2018,8 +2045,8 @@ fn gather_explicit_predicates_of(tcx: TyCtxt<'_>, def_id: DefId) -> ty::GenericP GenericParamKind::Lifetime { .. } => { param.bounds.iter().for_each(|bound| match bound { hir::GenericBound::Outlives(lt) => { - let bound = AstConv::ast_region_to_region(&icx, <, None); - let outlives = ty::Binder::bind(ty::OutlivesPredicate(region, bound)); + let bound = >::ast_region_to_region(&icx, <, None); + let outlives = ty::Binder::bind(ty::OutlivesPredicate(region, bound), tcx); predicates.insert((outlives.to_predicate(tcx), lt.span)); } _ => bug!(), @@ -2041,8 +2068,13 @@ fn gather_explicit_predicates_of(tcx: TyCtxt<'_>, def_id: DefId) -> ty::GenericP index += 1; let sized = SizedByDefault::Yes; - let bounds = - AstConv::compute_bounds(&icx, param_ty, ¶m.bounds, sized, param.span); + let bounds = >::compute_bounds( + &icx, + param_ty, + ¶m.bounds, + sized, + param.span, + ); predicates.extend(bounds.predicates(tcx, param_ty)); } GenericParamKind::Const { .. } => { @@ -2059,6 +2091,7 @@ fn gather_explicit_predicates_of(tcx: TyCtxt<'_>, def_id: DefId) -> ty::GenericP match predicate { hir::WherePredicate::BoundPredicate(bound_pred) => { let ty = icx.to_ty(&bound_pred.bounded_ty); + let bound_vars = icx.tcx.late_bound_vars(bound_pred.bounded_ty.hir_id); // Keep the type around in a dummy predicate, in case of no bounds. // That way, `where Ty:` is not a complete noop (see #53696) and `Ty` @@ -2074,9 +2107,13 @@ fn gather_explicit_predicates_of(tcx: TyCtxt<'_>, def_id: DefId) -> ty::GenericP } else { let span = bound_pred.bounded_ty.span; let re_root_empty = tcx.lifetimes.re_root_empty; - let predicate = ty::Binder::bind(ty::PredicateKind::TypeOutlives( - ty::OutlivesPredicate(ty, re_root_empty), - )); + let predicate = ty::Binder::bind_with_vars( + ty::PredicateKind::TypeOutlives(ty::OutlivesPredicate( + ty, + re_root_empty, + )), + bound_vars, + ); predicates.insert((predicate.to_predicate(tcx), span)); } } @@ -2091,19 +2128,21 @@ fn gather_explicit_predicates_of(tcx: TyCtxt<'_>, def_id: DefId) -> ty::GenericP }; let mut bounds = Bounds::default(); - let _ = AstConv::instantiate_poly_trait_ref( + let _ = >::instantiate_poly_trait_ref( &icx, - &poly_trait_ref, + &poly_trait_ref.trait_ref, + poly_trait_ref.span, constness, ty, &mut bounds, + false, ); predicates.extend(bounds.predicates(tcx, ty)); } &hir::GenericBound::LangItemTrait(lang_item, span, hir_id, args) => { let mut bounds = Bounds::default(); - AstConv::instantiate_lang_item_trait_ref( + >::instantiate_lang_item_trait_ref( &icx, lang_item, span, @@ -2116,11 +2155,15 @@ fn gather_explicit_predicates_of(tcx: TyCtxt<'_>, def_id: DefId) -> ty::GenericP } hir::GenericBound::Outlives(lifetime) => { - let region = AstConv::ast_region_to_region(&icx, lifetime, None); + let region = + >::ast_region_to_region(&icx, lifetime, None); predicates.insert(( - ty::Binder::bind(ty::PredicateKind::TypeOutlives( - ty::OutlivesPredicate(ty, region), - )) + ty::Binder::bind_with_vars( + ty::PredicateKind::TypeOutlives(ty::OutlivesPredicate( + ty, region, + )), + bound_vars, + ) .to_predicate(tcx), lifetime.span, )); @@ -2130,11 +2173,11 @@ fn gather_explicit_predicates_of(tcx: TyCtxt<'_>, def_id: DefId) -> ty::GenericP } hir::WherePredicate::RegionPredicate(region_pred) => { - let r1 = AstConv::ast_region_to_region(&icx, ®ion_pred.lifetime, None); + let r1 = >::ast_region_to_region(&icx, ®ion_pred.lifetime, None); predicates.extend(region_pred.bounds.iter().map(|bound| { let (r2, span) = match bound { hir::GenericBound::Outlives(lt) => { - (AstConv::ast_region_to_region(&icx, lt, None), lt.span) + (>::ast_region_to_region(&icx, lt, None), lt.span) } _ => bug!(), }; @@ -2200,10 +2243,11 @@ fn const_evaluatable_predicates_of<'tcx>( fn visit_anon_const(&mut self, c: &'tcx hir::AnonConst) { let def_id = self.tcx.hir().local_def_id(c.hir_id); let ct = ty::Const::from_anon_const(self.tcx, def_id); - if let ty::ConstKind::Unevaluated(def, substs, None) = ct.val { + if let ty::ConstKind::Unevaluated(uv) = ct.val { + assert_eq!(uv.promoted, None); let span = self.tcx.hir().span(c.hir_id); self.preds.insert(( - ty::PredicateKind::ConstEvaluatable(def, substs).to_predicate(self.tcx), + ty::PredicateKind::ConstEvaluatable(uv.def, uv.substs).to_predicate(self.tcx), span, )); } @@ -2341,7 +2385,14 @@ fn predicates_from_bound<'tcx>( }; let mut bounds = Bounds::default(); - let _ = astconv.instantiate_poly_trait_ref(tr, constness, param_ty, &mut bounds); + let _ = astconv.instantiate_poly_trait_ref( + &tr.trait_ref, + tr.span, + constness, + param_ty, + &mut bounds, + false, + ); bounds.predicates(astconv.tcx(), param_ty) } hir::GenericBound::LangItemTrait(lang_item, span, hir_id, args) => { @@ -2377,8 +2428,10 @@ fn compute_sig_of_foreign_fn_decl<'tcx>( } else { hir::Unsafety::Unsafe }; - let fty = AstConv::ty_of_fn( + let hir_id = tcx.hir().local_def_id_to_hir_id(def_id.expect_local()); + let fty = >::ty_of_fn( &ItemCtxt::new(tcx, def_id), + hir_id, unsafety, abi, decl, @@ -2413,7 +2466,7 @@ fn compute_sig_of_foreign_fn_decl<'tcx>( .emit(); } }; - for (input, ty) in decl.inputs.iter().zip(fty.inputs().skip_binder()) { + for (input, ty) in iter::zip(decl.inputs, fty.inputs().skip_binder()) { check(&input, ty) } if let hir::FnRetTy::Return(ref ty) = decl.output { @@ -2666,7 +2719,7 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, id: DefId) -> CodegenFnAttrs { } else if tcx.sess.check_name(attr, sym::used) { codegen_fn_attrs.flags |= CodegenFnAttrFlags::USED; } else if tcx.sess.check_name(attr, sym::cmse_nonsecure_entry) { - if tcx.fn_sig(id).abi() != abi::Abi::C { + if !matches!(tcx.fn_sig(id).abi(), abi::Abi::C { .. }) { struct_span_err!( tcx.sess, attr.span, @@ -2838,6 +2891,36 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, id: DefId) -> CodegenFnAttrs { None } }; + } else if tcx.sess.check_name(attr, sym::repr) { + codegen_fn_attrs.alignment = match attr.meta_item_list() { + Some(items) => match items.as_slice() { + [item] => match item.name_value_literal() { + Some((sym::align, literal)) => { + let alignment = rustc_attr::parse_alignment(&literal.kind); + + match alignment { + Ok(align) => Some(align), + Err(msg) => { + struct_span_err!( + tcx.sess.diagnostic(), + attr.span, + E0589, + "invalid `repr(align)` attribute: {}", + msg + ) + .emit(); + + None + } + } + } + _ => None, + }, + [] => None, + _ => None, + }, + None => None, + }; } } diff --git a/compiler/rustc_typeck/src/collect/item_bounds.rs b/compiler/rustc_typeck/src/collect/item_bounds.rs index fe18dc5ed0c69..a5b36445aae2e 100644 --- a/compiler/rustc_typeck/src/collect/item_bounds.rs +++ b/compiler/rustc_typeck/src/collect/item_bounds.rs @@ -25,7 +25,7 @@ fn associated_type_bounds<'tcx>( InternalSubsts::identity_for_item(tcx, assoc_item_def_id), ); - let bounds = AstConv::compute_bounds( + let bounds = >::compute_bounds( &ItemCtxt::new(tcx, assoc_item_def_id), item_ty, &bounds, @@ -66,7 +66,7 @@ fn opaque_type_bounds<'tcx>( let item_ty = tcx.mk_opaque(opaque_def_id, InternalSubsts::identity_for_item(tcx, opaque_def_id)); - let bounds = AstConv::compute_bounds( + let bounds = >::compute_bounds( &ItemCtxt::new(tcx, opaque_def_id), item_ty, &bounds, diff --git a/compiler/rustc_typeck/src/collect/type_of.rs b/compiler/rustc_typeck/src/collect/type_of.rs index e6f771cfd5372..d8eea1ad80b0b 100644 --- a/compiler/rustc_typeck/src/collect/type_of.rs +++ b/compiler/rustc_typeck/src/collect/type_of.rs @@ -83,7 +83,7 @@ pub(super) fn opt_const_param_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Option< return generics .params .iter() - .filter(|param| matches!(param.kind, ty::GenericParamDefKind::Const)) + .filter(|param| matches!(param.kind, ty::GenericParamDefKind::Const { .. })) .nth(arg_index) .map(|param| param.def_id); } @@ -121,7 +121,7 @@ pub(super) fn opt_const_param_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Option< tcx.generics_of(type_dependent_def) .params .iter() - .filter(|param| matches!(param.kind, ty::GenericParamDefKind::Const)) + .filter(|param| matches!(param.kind, ty::GenericParamDefKind::Const { .. })) .nth(idx) .map(|param| param.def_id) } @@ -211,7 +211,7 @@ pub(super) fn opt_const_param_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Option< generics .params .iter() - .filter(|param| matches!(param.kind, ty::GenericParamDefKind::Const)) + .filter(|param| matches!(param.kind, ty::GenericParamDefKind::Const { .. })) .nth(arg_index) .map(|param| param.def_id) } @@ -430,12 +430,27 @@ pub(super) fn type_of(tcx: TyCtxt<'_>, def_id: DefId) -> Ty<'_> { tcx.typeck(def_id).node_type(anon_const.hir_id) } + Node::Expr(&Expr { kind: ExprKind::InlineAsm(ia), .. }) + if ia.operands.iter().any(|(op, _op_sp)| match op { + hir::InlineAsmOperand::Const { anon_const } => anon_const.hir_id == hir_id, + _ => false, + }) => + { + tcx.typeck(def_id).node_type(hir_id) + } + Node::Variant(Variant { disr_expr: Some(ref e), .. }) if e.hir_id == hir_id => tcx .adt_def(tcx.hir().get_parent_did(hir_id).to_def_id()) .repr .discr_type() .to_ty(tcx), + Node::GenericParam(&GenericParam { + hir_id: param_hir_id, + kind: GenericParamKind::Const { default: Some(ct), .. }, + .. + }) if ct.hir_id == hir_id => tcx.type_of(tcx.hir().local_def_id(param_hir_id)), + x => tcx.ty_error_with_message( DUMMY_SP, &format!("unexpected const parent in type_of_def_id(): {:?}", x), @@ -722,11 +737,12 @@ fn infer_placeholder_type( format!("{}: {}", item_ident, ty), Applicability::MachineApplicable, ) - .emit(); + .emit_unless(ty.references_error()); } None => { let mut diag = bad_placeholder_type(tcx, vec![span]); - if !matches!(ty.kind(), ty::Error(_)) { + + if !ty.references_error() { diag.span_suggestion( span, "replace `_` with the correct type", @@ -734,6 +750,7 @@ fn infer_placeholder_type( Applicability::MaybeIncorrect, ); } + diag.emit(); } } diff --git a/compiler/rustc_typeck/src/expr_use_visitor.rs b/compiler/rustc_typeck/src/expr_use_visitor.rs index 52110af47929e..532ee00daf8a0 100644 --- a/compiler/rustc_typeck/src/expr_use_visitor.rs +++ b/compiler/rustc_typeck/src/expr_use_visitor.rs @@ -5,8 +5,9 @@ pub use self::ConsumeMode::*; // Export these here so that Clippy can use them. -pub use rustc_middle::hir::place::{PlaceBase, PlaceWithHirId, Projection}; +pub use rustc_middle::hir::place::{Place, PlaceBase, PlaceWithHirId, Projection}; +use rustc_data_structures::fx::FxIndexMap; use rustc_hir as hir; use rustc_hir::def::Res; use rustc_hir::def_id::LocalDefId; @@ -14,8 +15,10 @@ use rustc_hir::PatKind; use rustc_index::vec::Idx; use rustc_infer::infer::InferCtxt; use rustc_middle::hir::place::ProjectionKind; +use rustc_middle::mir::FakeReadCause; use rustc_middle::ty::{self, adjustment, TyCtxt}; use rustc_target::abi::VariantIdx; +use std::iter; use crate::mem_categorization as mc; @@ -51,6 +54,9 @@ pub trait Delegate<'tcx> { // The path at `assignee_place` is being assigned to. // `diag_expr_id` is the id used for diagnostics (see `consume` for more details). fn mutate(&mut self, assignee_place: &PlaceWithHirId<'tcx>, diag_expr_id: hir::HirId); + + // The `place` should be a fake read because of specified `cause`. + fn fake_read(&mut self, place: Place<'tcx>, cause: FakeReadCause, diag_expr_id: hir::HirId); } #[derive(Copy, Clone, PartialEq, Debug)] @@ -229,7 +235,66 @@ impl<'a, 'tcx> ExprUseVisitor<'a, 'tcx> { hir::ExprKind::Match(ref discr, arms, _) => { let discr_place = return_if_err!(self.mc.cat_expr(&discr)); - self.borrow_expr(&discr, ty::ImmBorrow); + + // Matching should not always be considered a use of the place, hence + // discr does not necessarily need to be borrowed. + // We only want to borrow discr if the pattern contain something other + // than wildcards. + let ExprUseVisitor { ref mc, body_owner: _, delegate: _ } = *self; + let mut needs_to_be_read = false; + for arm in arms.iter() { + return_if_err!(mc.cat_pattern(discr_place.clone(), &arm.pat, |place, pat| { + match &pat.kind { + PatKind::Binding(.., opt_sub_pat) => { + // If the opt_sub_pat is None, than the binding does not count as + // a wildcard for the purpose of borrowing discr. + if opt_sub_pat.is_none() { + needs_to_be_read = true; + } + } + PatKind::TupleStruct(..) + | PatKind::Path(..) + | PatKind::Struct(..) + | PatKind::Tuple(..) => { + // If the PatKind is a TupleStruct, Struct or Tuple then we want to check + // whether the Variant is a MultiVariant or a SingleVariant. We only want + // to borrow discr if it is a MultiVariant. + // If it is a SingleVariant and creates a binding we will handle that when + // this callback gets called again. + if let ty::Adt(def, _) = place.place.base_ty.kind() { + if def.variants.len() > 1 { + needs_to_be_read = true; + } + } + } + PatKind::Lit(_) => { + // If the PatKind is a Lit then we want + // to borrow discr. + needs_to_be_read = true; + } + _ => {} + } + })); + } + + if needs_to_be_read { + self.borrow_expr(&discr, ty::ImmBorrow); + } else { + let closure_def_id = match discr_place.place.base { + PlaceBase::Upvar(upvar_id) => Some(upvar_id.closure_expr_id.to_def_id()), + _ => None, + }; + + self.delegate.fake_read( + discr_place.place.clone(), + FakeReadCause::ForMatchedPlace(closure_def_id), + discr_place.hir_id, + ); + + // We always want to walk the discriminant. We want to make sure, for instance, + // that the discriminant has been initialized. + self.walk_expr(&discr); + } // treatment of the discriminant is handled while walking the arms. for arm in arms { @@ -253,7 +318,6 @@ impl<'a, 'tcx> ExprUseVisitor<'a, 'tcx> { for (op, _op_sp) in asm.operands { match op { hir::InlineAsmOperand::In { expr, .. } - | hir::InlineAsmOperand::Const { expr, .. } | hir::InlineAsmOperand::Sym { expr, .. } => self.consume_expr(expr), hir::InlineAsmOperand::Out { expr, .. } => { if let Some(expr) = expr { @@ -269,12 +333,13 @@ impl<'a, 'tcx> ExprUseVisitor<'a, 'tcx> { self.mutate_expr(out_expr); } } + hir::InlineAsmOperand::Const { .. } => {} } } } hir::ExprKind::LlvmInlineAsm(ref ia) => { - for (o, output) in ia.inner.outputs.iter().zip(ia.outputs_exprs) { + for (o, output) in iter::zip(&ia.inner.outputs, ia.outputs_exprs) { if o.is_indirect { self.consume_expr(output); } else { @@ -397,7 +462,7 @@ impl<'a, 'tcx> ExprUseVisitor<'a, 'tcx> { fn walk_struct_expr( &mut self, - fields: &[hir::Field<'_>], + fields: &[hir::ExprField<'_>], opt_with: &Option<&'hir hir::Expr<'_>>, ) { // Consume the expressions supplying values for each field. @@ -518,6 +583,16 @@ impl<'a, 'tcx> ExprUseVisitor<'a, 'tcx> { } fn walk_arm(&mut self, discr_place: &PlaceWithHirId<'tcx>, arm: &hir::Arm<'_>) { + let closure_def_id = match discr_place.place.base { + PlaceBase::Upvar(upvar_id) => Some(upvar_id.closure_expr_id.to_def_id()), + _ => None, + }; + + self.delegate.fake_read( + discr_place.place.clone(), + FakeReadCause::ForMatchedPlace(closure_def_id), + discr_place.hir_id, + ); self.walk_pat(discr_place, &arm.pat); if let Some(hir::Guard::If(ref e)) = arm.guard { @@ -530,6 +605,16 @@ impl<'a, 'tcx> ExprUseVisitor<'a, 'tcx> { /// Walks a pat that occurs in isolation (i.e., top-level of fn argument or /// let binding, and *not* a match arm or nested pat.) fn walk_irrefutable_pat(&mut self, discr_place: &PlaceWithHirId<'tcx>, pat: &hir::Pat<'_>) { + let closure_def_id = match discr_place.place.base { + PlaceBase::Upvar(upvar_id) => Some(upvar_id.closure_expr_id.to_def_id()), + _ => None, + }; + + self.delegate.fake_read( + discr_place.place.clone(), + FakeReadCause::ForLet(closure_def_id), + discr_place.hir_id, + ); self.walk_pat(discr_place, pat); } @@ -586,7 +671,7 @@ impl<'a, 'tcx> ExprUseVisitor<'a, 'tcx> { /// In the following example the closures `c` only captures `p.x`` even though `incr` /// is a capture of the nested closure /// - /// ```rust,ignore(cannot-test-this-because-pseduo-code) + /// ```rust,ignore(cannot-test-this-because-pseudo-code) /// let p = ..; /// let c = || { /// let incr = 10; @@ -597,6 +682,14 @@ impl<'a, 'tcx> ExprUseVisitor<'a, 'tcx> { /// - When reporting the Place back to the Delegate, ensure that the UpvarId uses the enclosing /// closure as the DefId. fn walk_captures(&mut self, closure_expr: &hir::Expr<'_>) { + fn upvar_is_local_variable( + upvars: Option<&'tcx FxIndexMap>, + upvar_id: &hir::HirId, + body_owner_is_closure: bool, + ) -> bool { + upvars.map(|upvars| !upvars.contains_key(upvar_id)).unwrap_or(body_owner_is_closure) + } + debug!("walk_captures({:?})", closure_expr); let closure_def_id = self.tcx().hir().local_def_id(closure_expr.hir_id).to_def_id(); @@ -608,6 +701,46 @@ impl<'a, 'tcx> ExprUseVisitor<'a, 'tcx> { ty::Closure(..) | ty::Generator(..) ); + // If we have a nested closure, we want to include the fake reads present in the nested closure. + if let Some(fake_reads) = self.mc.typeck_results.closure_fake_reads.get(&closure_def_id) { + for (fake_read, cause, hir_id) in fake_reads.iter() { + match fake_read.base { + PlaceBase::Upvar(upvar_id) => { + if upvar_is_local_variable( + upvars, + &upvar_id.var_path.hir_id, + body_owner_is_closure, + ) { + // The nested closure might be fake reading the current (enclosing) closure's local variables. + // The only places we want to fake read before creating the parent closure are the ones that + // are not local to it/ defined by it. + // + // ```rust,ignore(cannot-test-this-because-pseudo-code) + // let v1 = (0, 1); + // let c = || { // fake reads: v1 + // let v2 = (0, 1); + // let e = || { // fake reads: v1, v2 + // let (_, t1) = v1; + // let (_, t2) = v2; + // } + // } + // ``` + // This check is performed when visiting the body of the outermost closure (`c`) and ensures + // that we don't add a fake read of v2 in c. + continue; + } + } + _ => { + bug!( + "Do not know how to get HirId out of Rvalue and StaticItem {:?}", + fake_read.base + ); + } + }; + self.delegate.fake_read(fake_read.clone(), *cause, *hir_id); + } + } + if let Some(min_captures) = self.mc.typeck_results.closure_min_captures.get(&closure_def_id) { for (var_hir_id, min_list) in min_captures.iter() { diff --git a/compiler/rustc_typeck/src/impl_wf_check.rs b/compiler/rustc_typeck/src/impl_wf_check.rs index 7713381e62e46..1240946860573 100644 --- a/compiler/rustc_typeck/src/impl_wf_check.rs +++ b/compiler/rustc_typeck/src/impl_wf_check.rs @@ -173,7 +173,7 @@ fn enforce_impl_params_are_constrained( ); } } - ty::GenericParamDefKind::Const => { + ty::GenericParamDefKind::Const { .. } => { let param_ct = ty::ParamConst::for_def(param); if !input_parameters.contains(&cgp::Parameter::from(param_ct)) { report_unused_parameter( diff --git a/compiler/rustc_typeck/src/lib.rs b/compiler/rustc_typeck/src/lib.rs index 6ddc26efeae35..190744fe6f1d8 100644 --- a/compiler/rustc_typeck/src/lib.rs +++ b/compiler/rustc_typeck/src/lib.rs @@ -63,8 +63,9 @@ This API is completely unstable and subject to change. #![feature(format_args_capture)] #![feature(in_band_lifetimes)] #![feature(is_sorted)] +#![feature(iter_zip)] #![feature(nll)] -#![feature(or_patterns)] +#![cfg_attr(bootstrap, feature(or_patterns))] #![feature(try_blocks)] #![feature(never_type)] #![feature(slice_partition_dedup)] @@ -118,14 +119,19 @@ use astconv::AstConv; use bounds::Bounds; fn require_c_abi_if_c_variadic(tcx: TyCtxt<'_>, decl: &hir::FnDecl<'_>, abi: Abi, span: Span) { - if decl.c_variadic && !(abi == Abi::C || abi == Abi::Cdecl) { - let mut err = struct_span_err!( - tcx.sess, - span, - E0045, - "C-variadic function must have C or cdecl calling convention" - ); - err.span_label(span, "C-variadics require C or cdecl calling convention").emit(); + match (decl.c_variadic, abi) { + // The function has the correct calling convention, or isn't a "C-variadic" function. + (false, _) | (true, Abi::C { .. }) | (true, Abi::Cdecl) => {} + // The function is a "C-variadic" function with an incorrect calling convention. + (true, _) => { + let mut err = struct_span_err!( + tcx.sess, + span, + E0045, + "C-variadic function must have C or cdecl calling convention" + ); + err.span_label(span, "C-variadics require C or cdecl calling convention").emit(); + } } } @@ -137,7 +143,7 @@ fn require_same_types<'tcx>( ) -> bool { tcx.infer_ctxt().enter(|ref infcx| { let param_env = ty::ParamEnv::empty(); - let mut fulfill_cx = TraitEngine::new(infcx.tcx); + let mut fulfill_cx = >::new(infcx.tcx); match infcx.at(&cause, param_env).eq(expected, actual) { Ok(InferOk { obligations, .. }) => { fulfill_cx.register_predicate_obligations(infcx, obligations); @@ -201,7 +207,8 @@ fn check_main_fn_ty(tcx: TyCtxt<'_>, main_def_id: LocalDefId) { error = true; } - for attr in it.attrs { + let attrs = tcx.hir().attrs(main_id); + for attr in attrs { if tcx.sess.check_name(attr, sym::track_caller) { tcx.sess .struct_span_err( @@ -300,7 +307,8 @@ fn check_start_fn_ty(tcx: TyCtxt<'_>, start_def_id: LocalDefId) { error = true; } - for attr in it.attrs { + let attrs = tcx.hir().attrs(start_id); + for attr in attrs { if tcx.sess.check_name(attr, sym::track_caller) { tcx.sess .struct_span_err( @@ -422,7 +430,7 @@ pub fn hir_ty_to_ty<'tcx>(tcx: TyCtxt<'tcx>, hir_ty: &hir::Ty<'_>) -> Ty<'tcx> { let env_node_id = tcx.hir().get_parent_item(hir_ty.hir_id); let env_def_id = tcx.hir().local_def_id(env_node_id); let item_cx = self::collect::ItemCtxt::new(tcx, env_def_id.to_def_id()); - item_cx.to_ty(hir_ty) + >::ast_ty_to_ty(&item_cx, hir_ty) } pub fn hir_trait_to_predicates<'tcx>( @@ -437,7 +445,7 @@ pub fn hir_trait_to_predicates<'tcx>( let env_def_id = tcx.hir().local_def_id(env_hir_id); let item_cx = self::collect::ItemCtxt::new(tcx, env_def_id.to_def_id()); let mut bounds = Bounds::default(); - let _ = AstConv::instantiate_poly_trait_ref_inner( + let _ = >::instantiate_poly_trait_ref( &item_cx, hir_trait, DUMMY_SP, diff --git a/compiler/rustc_typeck/src/variance/solve.rs b/compiler/rustc_typeck/src/variance/solve.rs index 2d3369cba7a1e..1a4d88ced0e45 100644 --- a/compiler/rustc_typeck/src/variance/solve.rs +++ b/compiler/rustc_typeck/src/variance/solve.rs @@ -78,7 +78,7 @@ impl<'a, 'tcx> SolveContext<'a, 'tcx> { // Make all const parameters invariant. for param in generics.params.iter() { - if let ty::GenericParamDefKind::Const = param.kind { + if let ty::GenericParamDefKind::Const { .. } = param.kind { variances[param.index as usize] = ty::Invariant; } } diff --git a/config.toml.example b/config.toml.example index 5c4392acca595..6e5584797b559 100644 --- a/config.toml.example +++ b/config.toml.example @@ -112,6 +112,7 @@ changelog-seen = 2 # When invoking `llvm-config` this configures whether the `--shared` argument is # passed to prefer linking to shared libraries. +# NOTE: `thin-lto = true` requires this to be `true` and will give an error otherwise. #link-shared = false # When building llvm, this configures what is being appended to the version. @@ -120,22 +121,23 @@ changelog-seen = 2 #version-suffix = "-rust-dev" # On MSVC you can compile LLVM with clang-cl, but the test suite doesn't pass -# with clang-cl, so this is special in that it only compiles LLVM with clang-cl -#clang-cl = '/path/to/clang-cl.exe' +# with clang-cl, so this is special in that it only compiles LLVM with clang-cl. +# Note that this takes a /path/to/clang-cl, not a boolean. +#clang-cl = cc # Pass extra compiler and linker flags to the LLVM CMake build. -#cflags = "-fextra-flag" -#cxxflags = "-fextra-flag" -#ldflags = "-Wl,extra-flag" +#cflags = "" +#cxxflags = "" +#ldflags = "" # Use libc++ when building LLVM instead of libstdc++. This is the default on # platforms already use libc++ as the default C++ library, but this option # allows you to use libc++ even on platforms when it's not. You need to ensure # that your host compiler ships with libc++. -#use-libcxx = true +#use-libcxx = false # The value specified here will be passed as `-DLLVM_USE_LINKER` to CMake. -#use-linker = "lld" +#use-linker = (path) # Whether or not to specify `-DLLVM_TEMPORARILY_ALLOW_OLD_TOOLCHAIN=YES` #allow-old-toolchain = false @@ -147,6 +149,9 @@ changelog-seen = 2 # General build configuration options # ============================================================================= [build] +# The default stage to use for the `check` subcommand +#check-stage = 0 + # The default stage to use for the `doc` subcommand #doc-stage = 0 @@ -170,14 +175,14 @@ changelog-seen = 2 # binaries of this build triple and the nightly will be used to bootstrap the # first compiler. # -# Defaults to host platform -#build = "x86_64-unknown-linux-gnu" +# Defaults to platform where `x.py` is run. +#build = "x86_64-unknown-linux-gnu" (as an example) # Which triples to produce a compiler toolchain for. Each of these triples will # be bootstrapped from the build triple themselves. # -# Defaults to just the build triple -#host = ["x86_64-unknown-linux-gnu"] +# Defaults to just the build triple. +#host = ["x86_64-unknown-linux-gnu"] (as an example) # Which triples to build libraries (core/alloc/std/test/proc_macro) for. Each of # these triples will be bootstrapped from the build triple themselves. @@ -185,7 +190,7 @@ changelog-seen = 2 # Defaults to `host`. If you set this explicitly, you likely want to add all # host triples to this list as well in order for those host toolchains to be # able to compile programs for their native target. -#target = ["x86_64-unknown-linux-gnu"] +#target = ["x86_64-unknown-linux-gnu"] (as an example) # Use this directory to store build artifacts. # You can use "$ROOT" to indicate the root of the git repository. @@ -193,21 +198,26 @@ changelog-seen = 2 # Instead of downloading the src/stage0.txt version of Cargo specified, use # this Cargo binary instead to build all Rust code -#cargo = "/path/to/bin/cargo" +#cargo = "/path/to/cargo" # Instead of downloading the src/stage0.txt version of the compiler # specified, use this rustc binary instead as the stage0 snapshot compiler. -#rustc = "/path/to/bin/rustc" +#rustc = "/path/to/rustc" # Instead of download the src/stage0.txt version of rustfmt specified, # use this rustfmt binary instead as the stage0 snapshot rustfmt. -#rustfmt = "/path/to/bin/rustfmt" +#rustfmt = "/path/to/rustfmt" # Flag to specify whether any documentation is built. If false, rustdoc and # friends will still be compiled but they will not be used to generate any # documentation. #docs = true +# Flag to specify whether CSS, JavaScript, and HTML are minified when +# docs are generated. JSON is always minified, because it's enormous, +# and generated in already-minified form from the beginning. +#docs-minification = true + # Indicate whether the compiler should be documented in addition to the standard # library and facade crates. #compiler-docs = false @@ -254,10 +264,11 @@ changelog-seen = 2 # be built if `extended = true`. #extended = false -# Installs chosen set of extended tools if `extended = true`. By default builds all. -# If chosen tool failed to build the installation fails. If `extended = false`, this -# option is ignored. -#tools = ["cargo", "rls", "clippy", "rustfmt", "analysis", "src"] +# Installs chosen set of extended tools if `extended = true`. By default builds +# all extended tools except `rust-demangler`, unless the target is also being +# built with `profiler = true`. If chosen tool failed to build the installation +# fails. If `extended = false`, this option is ignored. +#tools = ["cargo", "rls", "clippy", "rustfmt", "analysis", "src"] # + "rust-demangler" if `profiler` # Verbosity level: 0 == not verbose, 1 == verbose, 2 == very verbose #verbose = 0 @@ -320,16 +331,9 @@ changelog-seen = 2 # Where to install man pages in `prefix` above #mandir = "share/man" -# Where to install data in `prefix` above (currently unused) +# Where to install data in `prefix` above #datadir = "share" -# Where to install additional info in `prefix` above (currently unused) -#infodir = "share/info" - -# Where to install local state (currently unused) -# If this is a relative path, it will get installed in `prefix` above -#localstatedir = "/var/lib" - # ============================================================================= # Options for compiling Rust code itself # ============================================================================= @@ -367,7 +371,9 @@ changelog-seen = 2 # Whether to download the stage 1 and 2 compilers from CI. # This is mostly useful for tools; if you have changes to `compiler/` they will be ignored. # -# FIXME: currently, this also uses the downloaded compiler for stage0, but that causes unnecessary rebuilds. +# You can set this to "if-unchanged" to only download if `compiler/` has not been modified. +# +# FIXME(#82739): currently, this also uses the downloaded compiler for stage0, but that causes unnecessary rebuilds. #download-rustc = false # Number of codegen units to use for each compiler invocation. A value of 0 @@ -379,7 +385,9 @@ changelog-seen = 2 # Sets the number of codegen units to build the standard library with, # regardless of what the codegen-unit setting for the rest of the compiler is. -#codegen-units-std = 1 +# NOTE: building with anything other than 1 is known to occasionally have bugs. +# See https://github.com/rust-lang/rust/issues/83600. +#codegen-units-std = codegen-units # Whether or not debug assertions are enabled for the compiler and standard # library. Debug assertions control the maximum log level used by rustc. When @@ -422,19 +430,13 @@ changelog-seen = 2 #debuginfo-level = 0 # Debuginfo level for the compiler. -# -# Defaults to rust.debuginfo-level value -#debuginfo-level-rustc = 0 +#debuginfo-level-rustc = debuginfo-level # Debuginfo level for the standard library. -# -# Defaults to rust.debuginfo-level value -#debuginfo-level-std = 0 +#debuginfo-level-std = debuginfo-level # Debuginfo level for the tools. -# -# Defaults to rust.debuginfo-level value -#debuginfo-level-tools = 0 +#debuginfo-level-tools = debuginfo-level # Debuginfo level for the test suites run with compiletest. # FIXME(#61117): Some tests fail when this option is enabled. @@ -461,7 +463,9 @@ changelog-seen = 2 # The default linker that will be hard-coded into the generated compiler for # targets that don't specify linker explicitly in their target specifications. # Note that this is not the linker used to link said compiler. -#default-linker = "cc" +# +# See https://doc.rust-lang.org/rustc/codegen-options/index.html#linker for more information. +#default-linker = (path) # The "channel" for the Rust build to produce. The stable/beta channels only # allow using stable features, whereas the nightly and dev channels allow using @@ -471,10 +475,15 @@ changelog-seen = 2 # A descriptive string to be appended to `rustc --version` output, which is # also used in places like debuginfo `DW_AT_producer`. This may be useful for # supplementary build information, like distro-specific package versions. -#description = "" +#description = (string) -# The root location of the musl installation directory. -#musl-root = "..." +# The root location of the musl installation directory. The library directory +# will also need to contain libunwind.a for an unwinding implementation. Note +# that this option only makes sense for musl targets that produce statically +# linked binaries. +# +# Defaults to /usr on musl hosts. Has no default otherwise. +#musl-root = (path) # By default the `rustc` executable is built with `-Wl,-rpath` flags on Unix # platforms to ensure that the compiler is usable by default from the build @@ -497,14 +506,14 @@ changelog-seen = 2 # Having the git information can cause a lot of rebuilds during development. # Note: If this attribute is not explicitly set (e.g. if left commented out) it # will default to true if channel = "dev", but will default to false otherwise. -#ignore-git = true +#ignore-git = if channel == "dev" { true } else { false } # When creating source tarballs whether or not to create a source tarball. -#dist-src = false +#dist-src = true # After building or testing extended tools (e.g. clippy and rustfmt), append the # result (broken, compiling, testing) into this JSON file. -#save-toolstates = "/path/to/toolstates.json" +#save-toolstates = (path) # This is an array of the codegen backends that will be compiled for the rustc # that's being compiled. The default is to only build the LLVM codegen backend, @@ -540,9 +549,7 @@ changelog-seen = 2 # Compile the compiler with a non-default ThinLTO import limit. This import # limit controls the maximum size of functions imported by ThinLTO. Decreasing # will make code compile faster at the expense of lower runtime performance. -# If `incremental` is set to true above, the import limit will default to 10 -# instead of LLVM's default of 100. -#thin-lto-import-instr-limit = 100 +#thin-lto-import-instr-limit = if incremental { 10 } else { LLVM default (currently 100) } # Map debuginfo paths to `/rust/$sha/...`, generally only set for releases #remap-debuginfo = false @@ -576,63 +583,66 @@ changelog-seen = 2 # ============================================================================= [target.x86_64-unknown-linux-gnu] -# C compiler to be used to compiler C code. Note that the +# C compiler to be used to compile C code. Note that the # default value is platform specific, and if not specified it may also depend on # what platform is crossing to what platform. -#cc = "cc" +# See `src/bootstrap/cc_detect.rs` for details. +#cc = "cc" (path) -# C++ compiler to be used to compiler C++ code (e.g. LLVM and our LLVM shims). +# C++ compiler to be used to compile C++ code (e.g. LLVM and our LLVM shims). # This is only used for host targets. -#cxx = "c++" +# See `src/bootstrap/cc_detect.rs` for details. +#cxx = "c++" (path) # Archiver to be used to assemble static libraries compiled from C/C++ code. # Note: an absolute path should be used, otherwise LLVM build will break. -#ar = "ar" +#ar = "ar" (path) # Ranlib to be used to assemble static libraries compiled from C/C++ code. # Note: an absolute path should be used, otherwise LLVM build will break. -#ranlib = "ranlib" +#ranlib = "ranlib" (path) -# Linker to be used to link Rust code. Note that the +# Linker to be used to bootstrap Rust code. Note that the # default value is platform specific, and if not specified it may also depend on # what platform is crossing to what platform. # Setting this will override the `use-lld` option for Rust code when targeting MSVC. -#linker = "cc" +#linker = "cc" (path) # Path to the `llvm-config` binary of the installation of a custom LLVM to link # against. Note that if this is specified we don't compile LLVM at all for this # target. -#llvm-config = "../path/to/llvm/root/bin/llvm-config" +#llvm-config = (path) # Normally the build system can find LLVM's FileCheck utility, but if # not, you can specify an explicit file name for it. -#llvm-filecheck = "/path/to/FileCheck" +#llvm-filecheck = "/path/to/llvm-version/bin/FileCheck" # If this target is for Android, this option will be required to specify where # the NDK for the target lives. This is used to find the C compiler to link and # build native code. -#android-ndk = "/path/to/ndk" +# See `src/bootstrap/cc_detect.rs` for details. +#android-ndk = (path) # Build the sanitizer runtimes for this target. # This option will override the same option under [build] section. -#sanitizers = false +#sanitizers = build.sanitizers (bool) # Build the profiler runtime for this target(required when compiling with options that depend # on this runtime, such as `-C profile-generate` or `-Z instrument-coverage`). # This option will override the same option under [build] section. -#profiler = false +#profiler = build.profiler (bool) # Force static or dynamic linkage of the standard library for this target. If # this target is a host for rustc, this will also affect the linkage of the # compiler itself. This is useful for building rustc on targets that normally # only use static libraries. If unset, the target's default linkage is used. -#crt-static = false +#crt-static = (bool) # The root location of the musl installation directory. The library directory # will also need to contain libunwind.a for an unwinding implementation. Note # that this option only makes sense for musl targets that produce statically -# linked binaries -#musl-root = "..." +# linked binaries. +#musl-root = build.musl-root (path) # The full path to the musl libdir. #musl-libdir = musl-root/lib @@ -640,11 +650,11 @@ changelog-seen = 2 # The root location of the `wasm32-wasi` sysroot. Only used for the # `wasm32-wasi` target. If you are building wasm32-wasi target, make sure to # create a `[target.wasm32-wasi]` section and move this field there. -#wasi-root = "..." +#wasi-root = (path) # Used in testing for configuring where the QEMU images are located, you # probably don't want to use this. -#qemu-rootfs = "..." +#qemu-rootfs = (path) # ============================================================================= # Distribution options @@ -661,12 +671,7 @@ changelog-seen = 2 # # This folder should be populated ahead of time before the build system is # invoked. -#sign-folder = "path/to/folder/to/sign" - -# This is a file which contains the password of the default gpg key. This will -# be passed to `gpg` down the road when signing all files in `sign-folder` -# above. This should be stored in plaintext. -#gpg-password-file = "path/to/gpg/password" +#sign-folder = (path) # The remote address that all artifacts will eventually be uploaded to. The # build system generates manifests which will point to these urls, and for the @@ -674,18 +679,19 @@ changelog-seen = 2 # # Note that this address should not contain a trailing slash as file names will # be appended to it. -#upload-addr = "https://example.com/folder" +#upload-addr = (URL) # Whether to build a plain source tarball to upload # We disable that on Windows not to override the one already uploaded on S3 # as the one built on Windows will contain backslashes in paths causing problems # on linux #src-tarball = true -# # Whether to allow failures when building tools #missing-tools = false # List of compression formats to use when generating dist tarballs. The list of # formats is provided to rust-installer, which must support all of them. +# +# This list must be non-empty. #compression-formats = ["gz", "xz"] diff --git a/library/alloc/Cargo.toml b/library/alloc/Cargo.toml index d95b5b7f17f42..4f97c95bcb9ea 100644 --- a/library/alloc/Cargo.toml +++ b/library/alloc/Cargo.toml @@ -2,6 +2,9 @@ authors = ["The Rust Project Developers"] name = "alloc" version = "0.0.0" +license = "MIT OR Apache-2.0" +repository = "https://github.com/rust-lang/rust.git" +description = "The Rust core allocation and collections library" autotests = false autobenches = false edition = "2018" diff --git a/library/alloc/benches/lib.rs b/library/alloc/benches/lib.rs index 32edb86d10119..38a8f65f1695a 100644 --- a/library/alloc/benches/lib.rs +++ b/library/alloc/benches/lib.rs @@ -4,6 +4,7 @@ #![feature(btree_drain_filter)] #![feature(map_first_last)] #![feature(repr_simd)] +#![feature(slice_partition_dedup)] #![feature(test)] extern crate test; diff --git a/library/alloc/benches/vec.rs b/library/alloc/benches/vec.rs index 89893b6209c0a..48709e89823d8 100644 --- a/library/alloc/benches/vec.rs +++ b/library/alloc/benches/vec.rs @@ -4,23 +4,13 @@ use test::{black_box, Bencher}; #[bench] fn bench_new(b: &mut Bencher) { - b.iter(|| { - let v: Vec = Vec::new(); - assert_eq!(v.len(), 0); - assert_eq!(v.capacity(), 0); - v - }) + b.iter(|| Vec::::new()) } fn do_bench_with_capacity(b: &mut Bencher, src_len: usize) { b.bytes = src_len as u64; - b.iter(|| { - let v: Vec = Vec::with_capacity(src_len); - assert_eq!(v.len(), 0); - assert_eq!(v.capacity(), src_len); - v - }) + b.iter(|| Vec::::with_capacity(src_len)) } #[bench] @@ -46,12 +36,7 @@ fn bench_with_capacity_1000(b: &mut Bencher) { fn do_bench_from_fn(b: &mut Bencher, src_len: usize) { b.bytes = src_len as u64; - b.iter(|| { - let dst = (0..src_len).collect::>(); - assert_eq!(dst.len(), src_len); - assert!(dst.iter().enumerate().all(|(i, x)| i == *x)); - dst - }) + b.iter(|| (0..src_len).collect::>()) } #[bench] @@ -77,12 +62,7 @@ fn bench_from_fn_1000(b: &mut Bencher) { fn do_bench_from_elem(b: &mut Bencher, src_len: usize) { b.bytes = src_len as u64; - b.iter(|| { - let dst: Vec = repeat(5).take(src_len).collect(); - assert_eq!(dst.len(), src_len); - assert!(dst.iter().all(|x| *x == 5)); - dst - }) + b.iter(|| repeat(5).take(src_len).collect::>()) } #[bench] @@ -110,12 +90,7 @@ fn do_bench_from_slice(b: &mut Bencher, src_len: usize) { b.bytes = src_len as u64; - b.iter(|| { - let dst = src.clone()[..].to_vec(); - assert_eq!(dst.len(), src_len); - assert!(dst.iter().enumerate().all(|(i, x)| i == *x)); - dst - }); + b.iter(|| src.as_slice().to_vec()); } #[bench] @@ -144,9 +119,7 @@ fn do_bench_from_iter(b: &mut Bencher, src_len: usize) { b.bytes = src_len as u64; b.iter(|| { - let dst: Vec<_> = FromIterator::from_iter(src.clone()); - assert_eq!(dst.len(), src_len); - assert!(dst.iter().enumerate().all(|(i, x)| i == *x)); + let dst: Vec<_> = FromIterator::from_iter(src.iter().cloned()); dst }); } @@ -180,8 +153,6 @@ fn do_bench_extend(b: &mut Bencher, dst_len: usize, src_len: usize) { b.iter(|| { let mut dst = dst.clone(); dst.extend(src.clone()); - assert_eq!(dst.len(), dst_len + src_len); - assert!(dst.iter().enumerate().all(|(i, x)| i == *x)); dst }); } @@ -230,8 +201,6 @@ fn do_bench_extend_from_slice(b: &mut Bencher, dst_len: usize, src_len: usize) { b.iter(|| { let mut dst = dst.clone(); dst.extend_from_slice(&src); - assert_eq!(dst.len(), dst_len + src_len); - assert!(dst.iter().enumerate().all(|(i, x)| i == *x)); dst }); } @@ -290,12 +259,7 @@ fn do_bench_clone(b: &mut Bencher, src_len: usize) { b.bytes = src_len as u64; - b.iter(|| { - let dst = src.clone(); - assert_eq!(dst.len(), src_len); - assert!(dst.iter().enumerate().all(|(i, x)| i == *x)); - dst - }); + b.iter(|| src.clone()); } #[bench] @@ -329,8 +293,7 @@ fn do_bench_clone_from(b: &mut Bencher, times: usize, dst_len: usize, src_len: u for _ in 0..times { dst.clone_from(&src); - assert_eq!(dst.len(), src_len); - assert!(dst.iter().enumerate().all(|(i, x)| dst_len + i == *x)); + dst = black_box(dst); } dst }); @@ -463,11 +426,10 @@ macro_rules! bench_in_place { fn $fname(b: &mut Bencher) { b.iter(|| { let src: Vec<$type> = black_box(vec![$init; $count]); - let mut sink = src.into_iter() + src.into_iter() .enumerate() .map(|(idx, e)| idx as $type ^ e) - .collect::>(); - black_box(sink.as_mut_ptr()) + .collect::>() }); } )+ @@ -527,7 +489,6 @@ fn bench_in_place_zip_recycle(b: &mut Bencher) { .enumerate() .map(|(i, (d, s))| d.wrapping_add(i as u8) ^ s) .collect::>(); - assert_eq!(mangled.len(), 1000); data = black_box(mangled); }); } @@ -548,6 +509,22 @@ fn bench_in_place_zip_iter_mut(b: &mut Bencher) { black_box(data); } +pub fn vec_cast(input: Vec) -> Vec { + input.into_iter().map(|e| unsafe { std::mem::transmute_copy(&e) }).collect() +} + +#[bench] +fn bench_transmute(b: &mut Bencher) { + let mut vec = vec![10u32; 100]; + b.bytes = 800; // 2 casts x 4 bytes x 100 + b.iter(|| { + let v = std::mem::take(&mut vec); + let v = black_box(vec_cast::(v)); + let v = black_box(vec_cast::(v)); + vec = v; + }); +} + #[derive(Clone)] struct Droppable(usize); @@ -598,23 +575,6 @@ fn bench_nest_chain_chain_collect(b: &mut Bencher) { }); } -pub fn example_plain_slow(l: &[u32]) -> Vec { - let mut result = Vec::with_capacity(l.len()); - result.extend(l.iter().rev()); - result -} - -pub fn map_fast(l: &[(u32, u32)]) -> Vec { - let mut result = Vec::with_capacity(l.len()); - for i in 0..l.len() { - unsafe { - *result.get_unchecked_mut(i) = l[i].0; - result.set_len(i); - } - } - result -} - #[bench] fn bench_range_map_collect(b: &mut Bencher) { b.iter(|| (0..LEN).map(|_| u32::default()).collect::>()); @@ -653,7 +613,11 @@ fn bench_rev_1(b: &mut Bencher) { #[bench] fn bench_rev_2(b: &mut Bencher) { let data = black_box([0; LEN]); - b.iter(|| example_plain_slow(&data)); + b.iter(|| { + let mut v = Vec::::with_capacity(data.len()); + v.extend(data.iter().rev()); + v + }); } #[bench] @@ -669,5 +633,103 @@ fn bench_map_regular(b: &mut Bencher) { #[bench] fn bench_map_fast(b: &mut Bencher) { let data = black_box([(0, 0); LEN]); - b.iter(|| map_fast(&data)); + b.iter(|| { + let mut result = Vec::with_capacity(data.len()); + for i in 0..data.len() { + unsafe { + *result.get_unchecked_mut(i) = data[i].0; + result.set_len(i); + } + } + result + }); +} + +fn random_sorted_fill(mut seed: u32, buf: &mut [u32]) { + let mask = if buf.len() < 8192 { + 0xFF + } else if buf.len() < 200_000 { + 0xFFFF + } else { + 0xFFFF_FFFF + }; + + for item in buf.iter_mut() { + seed ^= seed << 13; + seed ^= seed >> 17; + seed ^= seed << 5; + + *item = seed & mask; + } + + buf.sort(); +} + +fn bench_vec_dedup_old(b: &mut Bencher, sz: usize) { + let mut template = vec![0u32; sz]; + b.bytes = std::mem::size_of_val(template.as_slice()) as u64; + random_sorted_fill(0x43, &mut template); + + let mut vec = template.clone(); + b.iter(|| { + let len = { + let (dedup, _) = vec.partition_dedup(); + dedup.len() + }; + vec.truncate(len); + + black_box(vec.first()); + vec.clear(); + vec.extend_from_slice(&template); + }); +} + +fn bench_vec_dedup_new(b: &mut Bencher, sz: usize) { + let mut template = vec![0u32; sz]; + b.bytes = std::mem::size_of_val(template.as_slice()) as u64; + random_sorted_fill(0x43, &mut template); + + let mut vec = template.clone(); + b.iter(|| { + vec.dedup(); + black_box(vec.first()); + vec.clear(); + vec.extend_from_slice(&template); + }); +} + +#[bench] +fn bench_dedup_old_100(b: &mut Bencher) { + bench_vec_dedup_old(b, 100); +} +#[bench] +fn bench_dedup_new_100(b: &mut Bencher) { + bench_vec_dedup_new(b, 100); +} + +#[bench] +fn bench_dedup_old_1000(b: &mut Bencher) { + bench_vec_dedup_old(b, 1000); +} +#[bench] +fn bench_dedup_new_1000(b: &mut Bencher) { + bench_vec_dedup_new(b, 1000); +} + +#[bench] +fn bench_dedup_old_10000(b: &mut Bencher) { + bench_vec_dedup_old(b, 10000); +} +#[bench] +fn bench_dedup_new_10000(b: &mut Bencher) { + bench_vec_dedup_new(b, 10000); +} + +#[bench] +fn bench_dedup_old_100000(b: &mut Bencher) { + bench_vec_dedup_old(b, 100000); +} +#[bench] +fn bench_dedup_new_100000(b: &mut Bencher) { + bench_vec_dedup_new(b, 100000); } diff --git a/library/alloc/src/boxed.rs b/library/alloc/src/boxed.rs index 04033905728ab..ef37fef045526 100644 --- a/library/alloc/src/boxed.rs +++ b/library/alloc/src/boxed.rs @@ -793,7 +793,7 @@ impl Box { /// use std::alloc::{Allocator, Layout, System}; /// /// unsafe { - /// let ptr = System.allocate(Layout::new::())?.as_mut_ptr(); + /// let ptr = System.allocate(Layout::new::())?.as_mut_ptr() as *mut i32; /// // In general .write is required to avoid attempting to destruct /// // the (uninitialized) previous contents of `ptr`, though for this /// // simple example `*ptr = 5` would have worked as well. diff --git a/library/alloc/src/collections/binary_heap.rs b/library/alloc/src/collections/binary_heap.rs index 4377780e15f45..a201af0103070 100644 --- a/library/alloc/src/collections/binary_heap.rs +++ b/library/alloc/src/collections/binary_heap.rs @@ -566,7 +566,7 @@ impl BinaryHeap { let mut child = 2 * hole.pos() + 1; // Loop invariant: child == 2 * hole.pos() + 1. - while child < end - 1 { + while child <= end.saturating_sub(2) { // compare with the greater of the two children // SAFETY: child < end - 1 < self.len() and // child + 1 < end <= self.len(), so they're valid indexes. @@ -625,7 +625,7 @@ impl BinaryHeap { let mut child = 2 * hole.pos() + 1; // Loop invariant: child == 2 * hole.pos() + 1. - while child < end - 1 { + while child <= end.saturating_sub(2) { // SAFETY: child < end - 1 < self.len() and // child + 1 < end <= self.len(), so they're valid indexes. // child == 2 * hole.pos() + 1 != hole.pos() and @@ -652,6 +652,43 @@ impl BinaryHeap { unsafe { self.sift_up(start, pos) }; } + /// Rebuild assuming data[0..start] is still a proper heap. + fn rebuild_tail(&mut self, start: usize) { + if start == self.len() { + return; + } + + let tail_len = self.len() - start; + + #[inline(always)] + fn log2_fast(x: usize) -> usize { + (usize::BITS - x.leading_zeros() - 1) as usize + } + + // `rebuild` takes O(self.len()) operations + // and about 2 * self.len() comparisons in the worst case + // while repeating `sift_up` takes O(tail_len * log(start)) operations + // and about 1 * tail_len * log_2(start) comparisons in the worst case, + // assuming start >= tail_len. For larger heaps, the crossover point + // no longer follows this reasoning and was determined empirically. + let better_to_rebuild = if start < tail_len { + true + } else if self.len() <= 2048 { + 2 * self.len() < tail_len * log2_fast(start) + } else { + 2 * self.len() < tail_len * 11 + }; + + if better_to_rebuild { + self.rebuild(); + } else { + for i in start..self.len() { + // SAFETY: The index `i` is always less than self.len(). + unsafe { self.sift_up(0, i) }; + } + } + } + fn rebuild(&mut self) { let mut n = self.len() / 2; while n > 0 { @@ -689,37 +726,11 @@ impl BinaryHeap { swap(self, other); } - if other.is_empty() { - return; - } + let start = self.data.len(); - #[inline(always)] - fn log2_fast(x: usize) -> usize { - (usize::BITS - x.leading_zeros() - 1) as usize - } - - // `rebuild` takes O(len1 + len2) operations - // and about 2 * (len1 + len2) comparisons in the worst case - // while `extend` takes O(len2 * log(len1)) operations - // and about 1 * len2 * log_2(len1) comparisons in the worst case, - // assuming len1 >= len2. For larger heaps, the crossover point - // no longer follows this reasoning and was determined empirically. - #[inline] - fn better_to_rebuild(len1: usize, len2: usize) -> bool { - let tot_len = len1 + len2; - if tot_len <= 2048 { - 2 * tot_len < len2 * log2_fast(len1) - } else { - 2 * tot_len < len2 * 11 - } - } + self.data.append(&mut other.data); - if better_to_rebuild(self.len(), other.len()) { - self.data.append(&mut other.data); - self.rebuild(); - } else { - self.extend(other.drain()); - } + self.rebuild_tail(start); } /// Returns an iterator which retrieves elements in heap order. @@ -770,12 +781,22 @@ impl BinaryHeap { /// assert_eq!(heap.into_sorted_vec(), [-10, 2, 4]) /// ``` #[unstable(feature = "binary_heap_retain", issue = "71503")] - pub fn retain(&mut self, f: F) + pub fn retain(&mut self, mut f: F) where F: FnMut(&T) -> bool, { - self.data.retain(f); - self.rebuild(); + let mut first_removed = self.len(); + let mut i = 0; + self.data.retain(|e| { + let keep = f(e); + if !keep && i < first_removed { + first_removed = i; + } + i += 1; + keep + }); + // data[0..first_removed] is untouched, so we only need to rebuild the tail: + self.rebuild_tail(first_removed); } } @@ -958,6 +979,27 @@ impl BinaryHeap { self.data.shrink_to(min_capacity) } + /// Returns a slice of all values in the underlying vector, in arbitrary + /// order. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// #![feature(binary_heap_as_slice)] + /// use std::collections::BinaryHeap; + /// use std::io::{self, Write}; + /// + /// let heap = BinaryHeap::from(vec![1, 2, 3, 4, 5, 6, 7]); + /// + /// io::sink().write(heap.as_slice()).unwrap(); + /// ``` + #[unstable(feature = "binary_heap_as_slice", issue = "83659")] + pub fn as_slice(&self) -> &[T] { + self.data.as_slice() + } + /// Consumes the `BinaryHeap` and returns the underlying vector /// in arbitrary order. /// diff --git a/library/alloc/src/collections/btree/map.rs b/library/alloc/src/collections/btree/map.rs index 783f88f026b8f..971244718b4ad 100644 --- a/library/alloc/src/collections/btree/map.rs +++ b/library/alloc/src/collections/btree/map.rs @@ -14,22 +14,22 @@ use super::node::{self, marker, ForceResult::*, Handle, NodeRef, Root}; use super::search::SearchResult::*; mod entry; -pub use entry::{Entry, OccupiedEntry, VacantEntry}; +pub use entry::{Entry, OccupiedEntry, OccupiedError, VacantEntry}; use Entry::*; /// Minimum number of elements in nodes that are not a root. /// We might temporarily have fewer elements during methods. pub(super) const MIN_LEN: usize = node::MIN_LEN_AFTER_SPLIT; -// A tree in a `BTreeMap` is a tree in the `node` module with addtional invariants: +// A tree in a `BTreeMap` is a tree in the `node` module with additional invariants: // - Keys must appear in ascending order (according to the key's type). // - If the root node is internal, it must contain at least 1 element. // - Every non-root node contains at least MIN_LEN elements. // -// An empty map may be represented both by the absense of a root node or by a +// An empty map may be represented both by the absence of a root node or by a // root node that is an empty leaf. -/// A map based on a B-Tree. +/// A map based on a [B-Tree]. /// /// B-Trees represent a fundamental compromise between cache-efficiency and actually minimizing /// the amount of work performed in a search. In theory, a binary search tree (BST) is the optimal @@ -63,6 +63,7 @@ pub(super) const MIN_LEN: usize = node::MIN_LEN_AFTER_SPLIT; /// undefined behavior. This could include panics, incorrect results, aborts, memory leaks, and /// non-termination. /// +/// [B-Tree]: https://en.wikipedia.org/wiki/B-tree /// [`Cell`]: core::cell::Cell /// [`RefCell`]: core::cell::RefCell /// @@ -836,6 +837,40 @@ impl BTreeMap { } } + /// Tries to insert a key-value pair into the map, and returns + /// a mutable reference to the value in the entry. + /// + /// If the map already had this key present, nothing is updated, and + /// an error containing the occupied entry and the value is returned. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// #![feature(map_try_insert)] + /// + /// use std::collections::BTreeMap; + /// + /// let mut map = BTreeMap::new(); + /// assert_eq!(map.try_insert(37, "a").unwrap(), &"a"); + /// + /// let err = map.try_insert(37, "b").unwrap_err(); + /// assert_eq!(err.entry.key(), &37); + /// assert_eq!(err.entry.get(), &"a"); + /// assert_eq!(err.value, "b"); + /// ``` + #[unstable(feature = "map_try_insert", issue = "82766")] + pub fn try_insert(&mut self, key: K, value: V) -> Result<&mut V, OccupiedError<'_, K, V>> + where + K: Ord, + { + match self.entry(key) { + Occupied(entry) => Err(OccupiedError { entry, value }), + Vacant(entry) => Ok(entry.insert(value)), + } + } + /// Removes a key from the map, returning the value at the key if the key /// was previously in the map. /// @@ -905,7 +940,6 @@ impl BTreeMap { /// # Examples /// /// ``` - /// #![feature(btree_retain)] /// use std::collections::BTreeMap; /// /// let mut map: BTreeMap = (0..8).map(|x| (x, x*10)).collect(); @@ -914,7 +948,7 @@ impl BTreeMap { /// assert!(map.into_iter().eq(vec![(0, 0), (2, 20), (4, 40), (6, 60)])); /// ``` #[inline] - #[unstable(feature = "btree_retain", issue = "79025")] + #[stable(feature = "btree_retain", since = "1.53.0")] pub fn retain(&mut self, mut f: F) where K: Ord, diff --git a/library/alloc/src/collections/btree/map/entry.rs b/library/alloc/src/collections/btree/map/entry.rs index 941f82a8070a0..6b30d95977395 100644 --- a/library/alloc/src/collections/btree/map/entry.rs +++ b/library/alloc/src/collections/btree/map/entry.rs @@ -71,6 +71,41 @@ impl Debug for OccupiedEntry<'_, K, V> { } } +/// The error returned by [`try_insert`](BTreeMap::try_insert) when the key already exists. +/// +/// Contains the occupied entry, and the value that was not inserted. +#[unstable(feature = "map_try_insert", issue = "82766")] +pub struct OccupiedError<'a, K: 'a, V: 'a> { + /// The entry in the map that was already occupied. + pub entry: OccupiedEntry<'a, K, V>, + /// The value which was not inserted, because the entry was already occupied. + pub value: V, +} + +#[unstable(feature = "map_try_insert", issue = "82766")] +impl Debug for OccupiedError<'_, K, V> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("OccupiedError") + .field("key", self.entry.key()) + .field("old_value", self.entry.get()) + .field("new_value", &self.value) + .finish() + } +} + +#[unstable(feature = "map_try_insert", issue = "82766")] +impl<'a, K: Debug + Ord, V: Debug> fmt::Display for OccupiedError<'a, K, V> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!( + f, + "failed to insert {:?}, key {:?} already exists with value {:?}", + self.value, + self.entry.key(), + self.entry.get(), + ) + } +} + impl<'a, K: Ord, V> Entry<'a, K, V> { /// Ensures a value is in the entry by inserting the default if empty, and returns /// a mutable reference to the value in the entry. diff --git a/library/alloc/src/collections/btree/map/tests.rs b/library/alloc/src/collections/btree/map/tests.rs index 4e48db7f49305..3a74b6a6fa85c 100644 --- a/library/alloc/src/collections/btree/map/tests.rs +++ b/library/alloc/src/collections/btree/map/tests.rs @@ -776,7 +776,6 @@ fn test_range_backwards_4() { } #[test] -#[should_panic] fn test_range_finding_ill_order_in_map() { let mut map = BTreeMap::new(); map.insert(Cyclic3::B, ()); @@ -789,7 +788,6 @@ fn test_range_finding_ill_order_in_map() { } #[test] -#[should_panic] fn test_range_finding_ill_order_in_range_ord() { // Has proper order the first time asked, then flips around. struct EvilTwin(i32); @@ -1801,11 +1799,11 @@ fn test_occupied_entry_key() { let key = "hello there"; let value = "value goes here"; assert!(a.is_empty()); - a.insert(key.clone(), value.clone()); + a.insert(key, value); assert_eq!(a.len(), 1); assert_eq!(a[key], value); - match a.entry(key.clone()) { + match a.entry(key) { Vacant(_) => panic!(), Occupied(e) => assert_eq!(key, *e.key()), } @@ -1821,11 +1819,11 @@ fn test_vacant_entry_key() { let value = "value goes here"; assert!(a.is_empty()); - match a.entry(key.clone()) { + match a.entry(key) { Occupied(_) => panic!(), Vacant(e) => { assert_eq!(key, *e.key()); - e.insert(value.clone()); + e.insert(value); } } assert_eq!(a.len(), 1); diff --git a/library/alloc/src/collections/btree/navigate.rs b/library/alloc/src/collections/btree/navigate.rs index c2c99a9360cf1..4399feaccc9b7 100644 --- a/library/alloc/src/collections/btree/navigate.rs +++ b/library/alloc/src/collections/btree/navigate.rs @@ -29,11 +29,18 @@ impl LeafRange { impl NodeRef { /// Finds the distinct leaf edges delimiting a specified range in a tree. - /// Returns either a pair of different handles into the same tree or a pair - /// of empty options. + /// + /// If such distinct edges exist, returns them in ascending order, meaning + /// that a non-zero number of calls to `next_unchecked` on the `front` of + /// the result and/or calls to `next_back_unchecked` on the `back` of the + /// result will eventually reach the same edge. + /// + /// If there are no such edges, i.e., if the tree contains no key within + /// the range, returns a pair of empty options. + /// /// # Safety - /// Unless `BorrowType` is `Immut`, do not use the duplicate handles to - /// visit the same KV twice. + /// Unless `BorrowType` is `Immut`, do not use the handles to visit the same + /// KV twice. unsafe fn find_leaf_edges_spanning_range( self, range: R, diff --git a/library/alloc/src/collections/btree/node.rs b/library/alloc/src/collections/btree/node.rs index 9a7119470f370..f330a1bb3dcfe 100644 --- a/library/alloc/src/collections/btree/node.rs +++ b/library/alloc/src/collections/btree/node.rs @@ -128,106 +128,6 @@ impl InternalNode { /// is not a separate type and has no destructor. type BoxedNode = NonNull>; -/// The root node of an owned tree. -/// -/// Note that this does not have a destructor, and must be cleaned up manually. -pub type Root = NodeRef; - -impl Root { - /// Returns a new owned tree, with its own root node that is initially empty. - pub fn new() -> Self { - NodeRef::new_leaf().forget_type() - } -} - -impl NodeRef { - fn new_leaf() -> Self { - Self::from_new_leaf(LeafNode::new()) - } - - fn from_new_leaf(leaf: Box>) -> Self { - NodeRef { height: 0, node: NonNull::from(Box::leak(leaf)), _marker: PhantomData } - } -} - -impl NodeRef { - fn new_internal(child: Root) -> Self { - let mut new_node = unsafe { InternalNode::new() }; - new_node.edges[0].write(child.node); - unsafe { NodeRef::from_new_internal(new_node, child.height + 1) } - } - - /// # Safety - /// `height` must not be zero. - unsafe fn from_new_internal(internal: Box>, height: usize) -> Self { - debug_assert!(height > 0); - let node = NonNull::from(Box::leak(internal)).cast(); - let mut this = NodeRef { height, node, _marker: PhantomData }; - this.borrow_mut().correct_all_childrens_parent_links(); - this - } -} - -impl NodeRef { - /// Mutably borrows the owned root node. Unlike `reborrow_mut`, this is safe - /// because the return value cannot be used to destroy the root, and there - /// cannot be other references to the tree. - pub fn borrow_mut(&mut self) -> NodeRef, K, V, Type> { - NodeRef { height: self.height, node: self.node, _marker: PhantomData } - } - - /// Slightly mutably borrows the owned root node. - pub fn borrow_valmut(&mut self) -> NodeRef, K, V, Type> { - NodeRef { height: self.height, node: self.node, _marker: PhantomData } - } - - /// Irreversibly transitions to a reference that permits traversal and offers - /// destructive methods and little else. - pub fn into_dying(self) -> NodeRef { - NodeRef { height: self.height, node: self.node, _marker: PhantomData } - } -} - -impl NodeRef { - /// Adds a new internal node with a single edge pointing to the previous root node, - /// make that new node the root node, and return it. This increases the height by 1 - /// and is the opposite of `pop_internal_level`. - pub fn push_internal_level(&mut self) -> NodeRef, K, V, marker::Internal> { - super::mem::take_mut(self, |old_root| NodeRef::new_internal(old_root).forget_type()); - - // `self.borrow_mut()`, except that we just forgot we're internal now: - NodeRef { height: self.height, node: self.node, _marker: PhantomData } - } - - /// Removes the internal root node, using its first child as the new root node. - /// As it is intended only to be called when the root node has only one child, - /// no cleanup is done on any of the keys, values and other children. - /// This decreases the height by 1 and is the opposite of `push_internal_level`. - /// - /// Requires exclusive access to the `Root` object but not to the root node; - /// it will not invalidate other handles or references to the root node. - /// - /// Panics if there is no internal level, i.e., if the root node is a leaf. - pub fn pop_internal_level(&mut self) { - assert!(self.height > 0); - - let top = self.node; - - // SAFETY: we asserted to be internal. - let internal_self = unsafe { self.borrow_mut().cast_to_internal_unchecked() }; - // SAFETY: we borrowed `self` exclusively and its borrow type is exclusive. - let internal_node = unsafe { &mut *NodeRef::as_internal_ptr(&internal_self) }; - // SAFETY: the first edge is always initialized. - self.node = unsafe { internal_node.edges[0].assume_init_read() }; - self.height -= 1; - self.clear_parent_link(); - - unsafe { - Global.deallocate(top.cast(), Layout::new::>()); - } - } -} - // N.B. `NodeRef` is always covariant in `K` and `V`, even when the `BorrowType` // is `Mut`. This is technically wrong, but cannot result in any unsafety due to // internal use of `NodeRef` because we stay completely generic over `K` and `V`. @@ -292,6 +192,11 @@ pub struct NodeRef { _marker: PhantomData<(BorrowType, Type)>, } +/// The root node of an owned tree. +/// +/// Note that this does not have a destructor, and must be cleaned up manually. +pub type Root = NodeRef; + impl<'a, K: 'a, V: 'a, Type> Copy for NodeRef, K, V, Type> {} impl<'a, K: 'a, V: 'a, Type> Clone for NodeRef, K, V, Type> { fn clone(&self) -> Self { @@ -307,6 +212,34 @@ unsafe impl<'a, K: Send + 'a, V: Send + 'a, Type> Send for NodeRef Send for NodeRef {} unsafe impl Send for NodeRef {} +impl NodeRef { + fn new_leaf() -> Self { + Self::from_new_leaf(LeafNode::new()) + } + + fn from_new_leaf(leaf: Box>) -> Self { + NodeRef { height: 0, node: NonNull::from(Box::leak(leaf)), _marker: PhantomData } + } +} + +impl NodeRef { + fn new_internal(child: Root) -> Self { + let mut new_node = unsafe { InternalNode::new() }; + new_node.edges[0].write(child.node); + unsafe { NodeRef::from_new_internal(new_node, child.height + 1) } + } + + /// # Safety + /// `height` must not be zero. + unsafe fn from_new_internal(internal: Box>, height: usize) -> Self { + debug_assert!(height > 0); + let node = NonNull::from(Box::leak(internal)).cast(); + let mut this = NodeRef { height, node, _marker: PhantomData }; + this.borrow_mut().correct_all_childrens_parent_links(); + this + } +} + impl NodeRef { /// Unpack a node reference that was packed as `NodeRef::parent`. fn from_internal(node: NonNull>, height: usize) -> Self { @@ -420,6 +353,19 @@ impl NodeRef } } +impl NodeRef { + /// Could be a public implementation of PartialEq, but only used in this module. + fn eq(&self, other: &Self) -> bool { + let Self { node, height, _marker } = self; + if node.eq(&other.node) { + debug_assert_eq!(*height, other.height); + true + } else { + false + } + } +} + impl<'a, K: 'a, V: 'a, Type> NodeRef, K, V, Type> { /// Exposes the leaf portion of any leaf or internal node in an immutable tree. fn into_leaf(self) -> &'a LeafNode { @@ -461,20 +407,6 @@ impl NodeRef { } } -impl<'a, K, V> NodeRef, K, V, marker::LeafOrInternal> { - /// Unsafely asserts to the compiler the static information that this node is a `Leaf`. - unsafe fn cast_to_leaf_unchecked(self) -> NodeRef, K, V, marker::Leaf> { - debug_assert!(self.height == 0); - NodeRef { height: self.height, node: self.node, _marker: PhantomData } - } - - /// Unsafely asserts to the compiler the static information that this node is an `Internal`. - unsafe fn cast_to_internal_unchecked(self) -> NodeRef, K, V, marker::Internal> { - debug_assert!(self.height > 0); - NodeRef { height: self.height, node: self.node, _marker: PhantomData } - } -} - impl<'a, K, V, Type> NodeRef, K, V, Type> { /// Temporarily takes out another, mutable reference to the same node. Beware, as /// this method is very dangerous, doubly so since it may not immediately appear @@ -577,6 +509,22 @@ impl<'a, K: 'a, V: 'a, Type> NodeRef, K, V, Type> { } } +impl<'a, K, V> NodeRef, K, V, marker::Internal> { + /// # Safety + /// Every item returned by `range` is a valid edge index for the node. + unsafe fn correct_childrens_parent_links>(&mut self, range: R) { + for i in range { + debug_assert!(i <= self.len()); + unsafe { Handle::new_edge(self.reborrow_mut(), i) }.correct_parent_link(); + } + } + + fn correct_all_childrens_parent_links(&mut self) { + let len = self.len(); + unsafe { self.correct_childrens_parent_links(0..=len) }; + } +} + impl<'a, K: 'a, V: 'a> NodeRef, K, V, marker::LeafOrInternal> { /// Sets the node's link to its parent edge, /// without invalidating other references to the node. @@ -596,6 +544,71 @@ impl NodeRef { } } +impl NodeRef { + /// Returns a new owned tree, with its own root node that is initially empty. + pub fn new() -> Self { + NodeRef::new_leaf().forget_type() + } + + /// Adds a new internal node with a single edge pointing to the previous root node, + /// make that new node the root node, and return it. This increases the height by 1 + /// and is the opposite of `pop_internal_level`. + pub fn push_internal_level(&mut self) -> NodeRef, K, V, marker::Internal> { + super::mem::take_mut(self, |old_root| NodeRef::new_internal(old_root).forget_type()); + + // `self.borrow_mut()`, except that we just forgot we're internal now: + NodeRef { height: self.height, node: self.node, _marker: PhantomData } + } + + /// Removes the internal root node, using its first child as the new root node. + /// As it is intended only to be called when the root node has only one child, + /// no cleanup is done on any of the keys, values and other children. + /// This decreases the height by 1 and is the opposite of `push_internal_level`. + /// + /// Requires exclusive access to the `Root` object but not to the root node; + /// it will not invalidate other handles or references to the root node. + /// + /// Panics if there is no internal level, i.e., if the root node is a leaf. + pub fn pop_internal_level(&mut self) { + assert!(self.height > 0); + + let top = self.node; + + // SAFETY: we asserted to be internal. + let internal_self = unsafe { self.borrow_mut().cast_to_internal_unchecked() }; + // SAFETY: we borrowed `self` exclusively and its borrow type is exclusive. + let internal_node = unsafe { &mut *NodeRef::as_internal_ptr(&internal_self) }; + // SAFETY: the first edge is always initialized. + self.node = unsafe { internal_node.edges[0].assume_init_read() }; + self.height -= 1; + self.clear_parent_link(); + + unsafe { + Global.deallocate(top.cast(), Layout::new::>()); + } + } +} + +impl NodeRef { + /// Mutably borrows the owned root node. Unlike `reborrow_mut`, this is safe + /// because the return value cannot be used to destroy the root, and there + /// cannot be other references to the tree. + pub fn borrow_mut(&mut self) -> NodeRef, K, V, Type> { + NodeRef { height: self.height, node: self.node, _marker: PhantomData } + } + + /// Slightly mutably borrows the owned root node. + pub fn borrow_valmut(&mut self) -> NodeRef, K, V, Type> { + NodeRef { height: self.height, node: self.node, _marker: PhantomData } + } + + /// Irreversibly transitions to a reference that permits traversal and offers + /// destructive methods and little else. + pub fn into_dying(self) -> NodeRef { + NodeRef { height: self.height, node: self.node, _marker: PhantomData } + } +} + impl<'a, K: 'a, V: 'a> NodeRef, K, V, marker::Leaf> { /// Adds a key-value pair to the end of the node. pub fn push(&mut self, key: K, val: V) { @@ -610,22 +623,6 @@ impl<'a, K: 'a, V: 'a> NodeRef, K, V, marker::Leaf> { } } -impl<'a, K, V> NodeRef, K, V, marker::Internal> { - /// # Safety - /// Every item returned by `range` is a valid edge index for the node. - unsafe fn correct_childrens_parent_links>(&mut self, range: R) { - for i in range { - debug_assert!(i <= self.len()); - unsafe { Handle::new_edge(self.reborrow_mut(), i) }.correct_parent_link(); - } - } - - fn correct_all_childrens_parent_links(&mut self) { - let len = self.len(); - unsafe { self.correct_childrens_parent_links(0..=len) }; - } -} - impl<'a, K: 'a, V: 'a> NodeRef, K, V, marker::Internal> { /// Adds a key-value pair, and an edge to go to the right of that pair, /// to the end of the node. @@ -645,6 +642,20 @@ impl<'a, K: 'a, V: 'a> NodeRef, K, V, marker::Internal> { } } +impl NodeRef { + /// Removes any static information asserting that this node is a `Leaf` node. + pub fn forget_type(self) -> NodeRef { + NodeRef { height: self.height, node: self.node, _marker: PhantomData } + } +} + +impl NodeRef { + /// Removes any static information asserting that this node is an `Internal` node. + pub fn forget_type(self) -> NodeRef { + NodeRef { height: self.height, node: self.node, _marker: PhantomData } + } +} + impl NodeRef { /// Checks whether a node is an `Internal` node or a `Leaf` node. pub fn force( @@ -669,6 +680,20 @@ impl NodeRef { } } +impl<'a, K, V> NodeRef, K, V, marker::LeafOrInternal> { + /// Unsafely asserts to the compiler the static information that this node is a `Leaf`. + unsafe fn cast_to_leaf_unchecked(self) -> NodeRef, K, V, marker::Leaf> { + debug_assert!(self.height == 0); + NodeRef { height: self.height, node: self.node, _marker: PhantomData } + } + + /// Unsafely asserts to the compiler the static information that this node is an `Internal`. + unsafe fn cast_to_internal_unchecked(self) -> NodeRef, K, V, marker::Internal> { + debug_assert!(self.height > 0); + NodeRef { height: self.height, node: self.node, _marker: PhantomData } + } +} + /// A reference to a specific key-value pair or edge within a node. The `Node` parameter /// must be a `NodeRef`, while the `Type` can either be `KV` (signifying a handle on a key-value /// pair) or `Edge` (signifying a handle on an edge). @@ -722,19 +747,6 @@ impl Handle, mar } } -impl NodeRef { - /// Could be a public implementation of PartialEq, but only used in this module. - fn eq(&self, other: &Self) -> bool { - let Self { node, height, _marker } = self; - if node.eq(&other.node) { - debug_assert_eq!(*height, other.height); - true - } else { - false - } - } -} - impl PartialEq for Handle, HandleType> { @@ -754,16 +766,6 @@ impl } } -impl<'a, K, V, Type> Handle, K, V, marker::LeafOrInternal>, Type> { - /// Unsafely asserts to the compiler the static information that the handle's node is a `Leaf`. - pub unsafe fn cast_to_leaf_unchecked( - self, - ) -> Handle, K, V, marker::Leaf>, Type> { - let node = unsafe { self.node.cast_to_leaf_unchecked() }; - Handle { node, idx: self.idx, _marker: PhantomData } - } -} - impl<'a, K, V, NodeType, HandleType> Handle, K, V, NodeType>, HandleType> { /// Temporarily takes out another, mutable handle on the same location. Beware, as /// this method is very dangerous, doubly so since it may not immediately appear @@ -1466,20 +1468,6 @@ impl<'a, K: 'a, V: 'a> BalancingContext<'a, K, V> { } } -impl NodeRef { - /// Removes any static information asserting that this node is a `Leaf` node. - pub fn forget_type(self) -> NodeRef { - NodeRef { height: self.height, node: self.node, _marker: PhantomData } - } -} - -impl NodeRef { - /// Removes any static information asserting that this node is an `Internal` node. - pub fn forget_type(self) -> NodeRef { - NodeRef { height: self.height, node: self.node, _marker: PhantomData } - } -} - impl Handle, marker::Edge> { pub fn forget_node_type( self, @@ -1531,6 +1519,16 @@ impl Handle Handle, K, V, marker::LeafOrInternal>, Type> { + /// Unsafely asserts to the compiler the static information that the handle's node is a `Leaf`. + pub unsafe fn cast_to_leaf_unchecked( + self, + ) -> Handle, K, V, marker::Leaf>, Type> { + let node = unsafe { self.node.cast_to_leaf_unchecked() }; + Handle { node, idx: self.idx, _marker: PhantomData } + } +} + impl<'a, K, V> Handle, K, V, marker::LeafOrInternal>, marker::Edge> { /// Move the suffix after `self` from one node to another one. `right` must be empty. /// The first edge of `right` remains unchanged. diff --git a/library/alloc/src/collections/btree/search.rs b/library/alloc/src/collections/btree/search.rs index f376b3cde02e5..5651a03c47a43 100644 --- a/library/alloc/src/collections/btree/search.rs +++ b/library/alloc/src/collections/btree/search.rs @@ -68,13 +68,16 @@ impl NodeRef( mut self, @@ -94,9 +97,8 @@ impl NodeRef, R: RangeBounds, { - // WARNING: Inlining these variables would be unsound (#81138) - // We assume the bounds reported by `range` remain the same, but - // an adversarial implementation could change between calls + // Inlining these variables should be avoided. We assume the bounds reported by `range` + // remain the same, but an adversarial implementation could change between calls (#81138). let (start, end) = (range.start_bound(), range.end_bound()); match (start, end) { (Bound::Excluded(s), Bound::Excluded(e)) if s == e => { @@ -113,10 +115,8 @@ impl NodeRef upper_edge_idx { - panic!("Ord is ill-defined in BTreeMap range") - } + let (upper_edge_idx, upper_child_bound) = + unsafe { self.find_upper_bound_index(upper_bound, lower_edge_idx) }; if lower_edge_idx < upper_edge_idx { return Ok(( self, @@ -126,6 +126,7 @@ impl NodeRef return Err(common_edge), @@ -165,7 +166,7 @@ impl NodeRef, { - let (edge_idx, bound) = self.find_upper_bound_index(bound); + let (edge_idx, bound) = unsafe { self.find_upper_bound_index(bound, 0) }; let edge = unsafe { Handle::new_edge(self, edge_idx) }; (edge, bound) } @@ -184,29 +185,33 @@ impl NodeRef { Q: Ord, K: Borrow, { - match self.find_key_index(key) { + match unsafe { self.find_key_index(key, 0) } { IndexResult::KV(idx) => Found(unsafe { Handle::new_kv(self, idx) }), IndexResult::Edge(idx) => GoDown(unsafe { Handle::new_edge(self, idx) }), } } /// Returns either the KV index in the node at which the key (or an equivalent) - /// exists, or the edge index where the key belongs. + /// exists, or the edge index where the key belongs, starting from a particular index. /// /// The result is meaningful only if the tree is ordered by key, like the tree /// in a `BTreeMap` is. - fn find_key_index(&self, key: &Q) -> IndexResult + /// + /// # Safety + /// `start_index` must be a valid edge index for the node. + unsafe fn find_key_index(&self, key: &Q, start_index: usize) -> IndexResult where Q: Ord, K: Borrow, { let node = self.reborrow(); let keys = node.keys(); - for (i, k) in keys.iter().enumerate() { + debug_assert!(start_index <= keys.len()); + for (offset, k) in unsafe { keys.get_unchecked(start_index..) }.iter().enumerate() { match key.cmp(k.borrow()) { Ordering::Greater => {} - Ordering::Equal => return IndexResult::KV(i), - Ordering::Less => return IndexResult::Edge(i), + Ordering::Equal => return IndexResult::KV(start_index + offset), + Ordering::Less => return IndexResult::Edge(start_index + offset), } } IndexResult::Edge(keys.len()) @@ -226,11 +231,11 @@ impl NodeRef { K: Borrow, { match bound { - Included(key) => match self.find_key_index(key) { + Included(key) => match unsafe { self.find_key_index(key, 0) } { IndexResult::KV(idx) => (idx, AllExcluded), IndexResult::Edge(idx) => (idx, bound), }, - Excluded(key) => match self.find_key_index(key) { + Excluded(key) => match unsafe { self.find_key_index(key, 0) } { IndexResult::KV(idx) => (idx + 1, AllIncluded), IndexResult::Edge(idx) => (idx, bound), }, @@ -239,26 +244,31 @@ impl NodeRef { } } - /// Clone of `find_lower_bound_index` for the upper bound. - fn find_upper_bound_index<'r, Q>( + /// Mirror image of `find_lower_bound_index` for the upper bound, + /// with an additional parameter to skip part of the key array. + /// + /// # Safety + /// `start_index` must be a valid edge index for the node. + unsafe fn find_upper_bound_index<'r, Q>( &self, bound: SearchBound<&'r Q>, + start_index: usize, ) -> (usize, SearchBound<&'r Q>) where Q: ?Sized + Ord, K: Borrow, { match bound { - Included(key) => match self.find_key_index(key) { + Included(key) => match unsafe { self.find_key_index(key, start_index) } { IndexResult::KV(idx) => (idx + 1, AllExcluded), IndexResult::Edge(idx) => (idx, bound), }, - Excluded(key) => match self.find_key_index(key) { + Excluded(key) => match unsafe { self.find_key_index(key, start_index) } { IndexResult::KV(idx) => (idx, AllIncluded), IndexResult::Edge(idx) => (idx, bound), }, AllIncluded => (self.len(), AllIncluded), - AllExcluded => (0, AllExcluded), + AllExcluded => (start_index, AllExcluded), } } } diff --git a/library/alloc/src/collections/btree/set.rs b/library/alloc/src/collections/btree/set.rs index a331b8d8e4bbb..737932d931c02 100644 --- a/library/alloc/src/collections/btree/set.rs +++ b/library/alloc/src/collections/btree/set.rs @@ -851,7 +851,6 @@ impl BTreeSet { /// # Examples /// /// ``` - /// #![feature(btree_retain)] /// use std::collections::BTreeSet; /// /// let xs = [1, 2, 3, 4, 5, 6]; @@ -860,7 +859,7 @@ impl BTreeSet { /// set.retain(|&k| k % 2 == 0); /// assert!(set.iter().eq([2, 4, 6].iter())); /// ``` - #[unstable(feature = "btree_retain", issue = "79025")] + #[stable(feature = "btree_retain", since = "1.53.0")] pub fn retain(&mut self, mut f: F) where T: Ord, diff --git a/library/alloc/src/collections/vec_deque/into_iter.rs b/library/alloc/src/collections/vec_deque/into_iter.rs index 465b058cd98e9..1c635dd4f27fa 100644 --- a/library/alloc/src/collections/vec_deque/into_iter.rs +++ b/library/alloc/src/collections/vec_deque/into_iter.rs @@ -1,5 +1,5 @@ use core::fmt; -use core::iter::FusedIterator; +use core::iter::{FusedIterator, TrustedLen, TrustedRandomAccess}; use super::VecDeque; @@ -36,6 +36,22 @@ impl Iterator for IntoIter { let len = self.inner.len(); (len, Some(len)) } + + #[inline] + unsafe fn __iterator_get_unchecked(&mut self, idx: usize) -> Self::Item + where + Self: TrustedRandomAccess, + { + // Safety: The TrustedRandomAccess contract requires that callers only pass an index + // that is in bounds. + // Additionally Self: TrustedRandomAccess is only implemented for T: Copy which means even + // multiple repeated reads of the same index would be safe and the + // values are !Drop, thus won't suffer from double drops. + unsafe { + let idx = self.inner.wrap_add(self.inner.tail, idx); + self.inner.buffer_read(idx) + } + } } #[stable(feature = "rust1", since = "1.0.0")] @@ -55,3 +71,17 @@ impl ExactSizeIterator for IntoIter { #[stable(feature = "fused", since = "1.26.0")] impl FusedIterator for IntoIter {} + +#[unstable(feature = "trusted_len", issue = "37572")] +unsafe impl TrustedLen for IntoIter {} + +#[doc(hidden)] +#[unstable(feature = "trusted_random_access", issue = "none")] +// T: Copy as approximation for !Drop since get_unchecked does not update the pointers +// and thus we can't implement drop-handling +unsafe impl TrustedRandomAccess for IntoIter +where + T: Copy, +{ + const MAY_HAVE_SIDE_EFFECT: bool = false; +} diff --git a/library/alloc/src/collections/vec_deque/iter.rs b/library/alloc/src/collections/vec_deque/iter.rs index ad31b991cb6c3..e4cfb3acdfd5c 100644 --- a/library/alloc/src/collections/vec_deque/iter.rs +++ b/library/alloc/src/collections/vec_deque/iter.rs @@ -1,5 +1,5 @@ use core::fmt; -use core::iter::FusedIterator; +use core::iter::{FusedIterator, TrustedLen, TrustedRandomAccess}; use core::ops::Try; use super::{count, wrap_index, RingSlices}; @@ -101,6 +101,19 @@ impl<'a, T> Iterator for Iter<'a, T> { fn last(mut self) -> Option<&'a T> { self.next_back() } + + #[inline] + unsafe fn __iterator_get_unchecked(&mut self, idx: usize) -> Self::Item + where + Self: TrustedRandomAccess, + { + // Safety: The TrustedRandomAccess contract requires that callers only pass an index + // that is in bounds. + unsafe { + let idx = wrap_index(self.tail.wrapping_add(idx), self.ring.len()); + self.ring.get_unchecked(idx) + } + } } #[stable(feature = "rust1", since = "1.0.0")] @@ -157,3 +170,12 @@ impl ExactSizeIterator for Iter<'_, T> { #[stable(feature = "fused", since = "1.26.0")] impl FusedIterator for Iter<'_, T> {} + +#[unstable(feature = "trusted_len", issue = "37572")] +unsafe impl TrustedLen for Iter<'_, T> {} + +#[doc(hidden)] +#[unstable(feature = "trusted_random_access", issue = "none")] +unsafe impl TrustedRandomAccess for Iter<'_, T> { + const MAY_HAVE_SIDE_EFFECT: bool = false; +} diff --git a/library/alloc/src/collections/vec_deque/iter_mut.rs b/library/alloc/src/collections/vec_deque/iter_mut.rs index 3d0c3094e26cd..9493676e66bc8 100644 --- a/library/alloc/src/collections/vec_deque/iter_mut.rs +++ b/library/alloc/src/collections/vec_deque/iter_mut.rs @@ -1,5 +1,5 @@ use core::fmt; -use core::iter::FusedIterator; +use core::iter::{FusedIterator, TrustedLen, TrustedRandomAccess}; use core::marker::PhantomData; use super::{count, wrap_index, RingSlices}; @@ -87,6 +87,19 @@ impl<'a, T> Iterator for IterMut<'a, T> { fn last(mut self) -> Option<&'a mut T> { self.next_back() } + + #[inline] + unsafe fn __iterator_get_unchecked(&mut self, idx: usize) -> Self::Item + where + Self: TrustedRandomAccess, + { + // Safety: The TrustedRandomAccess contract requires that callers only pass an index + // that is in bounds. + unsafe { + let idx = wrap_index(self.tail.wrapping_add(idx), self.ring.len()); + &mut *self.ring.get_unchecked_mut(idx) + } + } } #[stable(feature = "rust1", since = "1.0.0")] @@ -126,3 +139,12 @@ impl ExactSizeIterator for IterMut<'_, T> { #[stable(feature = "fused", since = "1.26.0")] impl FusedIterator for IterMut<'_, T> {} + +#[unstable(feature = "trusted_len", issue = "37572")] +unsafe impl TrustedLen for IterMut<'_, T> {} + +#[doc(hidden)] +#[unstable(feature = "trusted_random_access", issue = "none")] +unsafe impl TrustedRandomAccess for IterMut<'_, T> { + const MAY_HAVE_SIDE_EFFECT: bool = false; +} diff --git a/library/alloc/src/collections/vec_deque/mod.rs b/library/alloc/src/collections/vec_deque/mod.rs index f7cefdce27856..7d6fbf1c438bf 100644 --- a/library/alloc/src/collections/vec_deque/mod.rs +++ b/library/alloc/src/collections/vec_deque/mod.rs @@ -58,7 +58,7 @@ mod tests; const INITIAL_CAPACITY: usize = 7; // 2^3 - 1 const MINIMUM_CAPACITY: usize = 1; // 2 - 1 -const MAXIMUM_ZST_CAPACITY: usize = 1 << (core::mem::size_of::() * 8 - 1); // Largest possible power of two +const MAXIMUM_ZST_CAPACITY: usize = 1 << (usize::BITS - 1); // Largest possible power of two /// A double-ended queue implemented with a growable ring buffer. /// @@ -2403,6 +2403,12 @@ impl VecDeque { /// [`Result::Err`] is returned, containing the index where a matching /// element could be inserted while maintaining sorted order. /// + /// See also [`binary_search_by`], [`binary_search_by_key`], and [`partition_point`]. + /// + /// [`binary_search_by`]: VecDeque::binary_search_by + /// [`binary_search_by_key`]: VecDeque::binary_search_by_key + /// [`partition_point`]: VecDeque::partition_point + /// /// # Examples /// /// Looks up a series of four elements. The first is found, with a @@ -2457,6 +2463,12 @@ impl VecDeque { /// [`Result::Err`] is returned, containing the index where a matching /// element could be inserted while maintaining sorted order. /// + /// See also [`binary_search`], [`binary_search_by_key`], and [`partition_point`]. + /// + /// [`binary_search`]: VecDeque::binary_search + /// [`binary_search_by_key`]: VecDeque::binary_search_by_key + /// [`partition_point`]: VecDeque::partition_point + /// /// # Examples /// /// Looks up a series of four elements. The first is found, with a @@ -2481,8 +2493,11 @@ impl VecDeque { F: FnMut(&'a T) -> Ordering, { let (front, back) = self.as_slices(); + let cmp_back = back.first().map(|elem| f(elem)); - if let Some(Ordering::Less | Ordering::Equal) = back.first().map(|elem| f(elem)) { + if let Some(Ordering::Equal) = cmp_back { + Ok(front.len()) + } else if let Some(Ordering::Less) = cmp_back { back.binary_search_by(f).map(|idx| idx + front.len()).map_err(|idx| idx + front.len()) } else { front.binary_search_by(f) @@ -2492,8 +2507,7 @@ impl VecDeque { /// Binary searches this sorted `VecDeque` with a key extraction function. /// /// Assumes that the `VecDeque` is sorted by the key, for instance with - /// [`make_contiguous().sort_by_key()`](#method.make_contiguous) using the same - /// key extraction function. + /// [`make_contiguous().sort_by_key()`] using the same key extraction function. /// /// If the value is found then [`Result::Ok`] is returned, containing the /// index of the matching element. If there are multiple matches, then any @@ -2501,6 +2515,13 @@ impl VecDeque { /// [`Result::Err`] is returned, containing the index where a matching /// element could be inserted while maintaining sorted order. /// + /// See also [`binary_search`], [`binary_search_by`], and [`partition_point`]. + /// + /// [`make_contiguous().sort_by_key()`]: VecDeque::make_contiguous + /// [`binary_search`]: VecDeque::binary_search + /// [`binary_search_by`]: VecDeque::binary_search_by + /// [`partition_point`]: VecDeque::partition_point + /// /// # Examples /// /// Looks up a series of four elements in a slice of pairs sorted by @@ -2531,6 +2552,51 @@ impl VecDeque { { self.binary_search_by(|k| f(k).cmp(b)) } + + /// Returns the index of the partition point according to the given predicate + /// (the index of the first element of the second partition). + /// + /// The deque is assumed to be partitioned according to the given predicate. + /// This means that all elements for which the predicate returns true are at the start of the deque + /// and all elements for which the predicate returns false are at the end. + /// For example, [7, 15, 3, 5, 4, 12, 6] is a partitioned under the predicate x % 2 != 0 + /// (all odd numbers are at the start, all even at the end). + /// + /// If this deque is not partitioned, the returned result is unspecified and meaningless, + /// as this method performs a kind of binary search. + /// + /// See also [`binary_search`], [`binary_search_by`], and [`binary_search_by_key`]. + /// + /// [`binary_search`]: VecDeque::binary_search + /// [`binary_search_by`]: VecDeque::binary_search_by + /// [`binary_search_by_key`]: VecDeque::binary_search_by_key + /// + /// # Examples + /// + /// ``` + /// #![feature(vecdeque_binary_search)] + /// use std::collections::VecDeque; + /// + /// let deque: VecDeque<_> = vec![1, 2, 3, 3, 5, 6, 7].into(); + /// let i = deque.partition_point(|&x| x < 5); + /// + /// assert_eq!(i, 4); + /// assert!(deque.iter().take(i).all(|&x| x < 5)); + /// assert!(deque.iter().skip(i).all(|&x| !(x < 5))); + /// ``` + #[unstable(feature = "vecdeque_binary_search", issue = "78021")] + pub fn partition_point

`] ensures that the pointee of any pointer type +//! At a high level, a [Pin]\

ensures that the pointee of any pointer type //! `P` has a stable location in memory, meaning it cannot be moved elsewhere //! and its memory cannot be deallocated until it gets dropped. We say that the //! pointee is "pinned". Things get more subtle when discussing types that @@ -14,13 +14,15 @@ //! for more details. //! //! By default, all types in Rust are movable. Rust allows passing all types by-value, -//! and common smart-pointer types such as [`Box`] and `&mut T` allow replacing and -//! moving the values they contain: you can move out of a [`Box`], or you can use [`mem::swap`]. -//! [`Pin

`] wraps a pointer type `P`, so [`Pin`]`<`[`Box`]`>` functions much like a regular -//! [`Box`]: when a [`Pin`]`<`[`Box`]`>` gets dropped, so do its contents, and the memory gets -//! deallocated. Similarly, [`Pin`]`<&mut T>` is a lot like `&mut T`. However, [`Pin

`] does -//! not let clients actually obtain a [`Box`] or `&mut T` to pinned data, which implies that you -//! cannot use operations such as [`mem::swap`]: +//! and common smart-pointer types such as [Box]\ and [&mut] T allow +//! replacing and moving the values they contain: you can move out of a [Box]\, +//! or you can use [`mem::swap`]. [Pin]\

wraps a pointer type `P`, so +//! [Pin]<[Box]\> functions much like a regular [Box]\: +//! when a [Pin]<[Box]\> gets dropped, so do its contents, and the memory gets +//! deallocated. Similarly, [Pin]<[&mut] T> is a lot like [&mut] T. +//! However, [Pin]\

does not let clients actually obtain a [Box]\ +//! or [&mut] T to pinned data, which implies that you cannot use operations such +//! as [`mem::swap`]: //! //! ``` //! use std::pin::Pin; @@ -32,18 +34,18 @@ //! } //! ``` //! -//! It is worth reiterating that [`Pin

`] does *not* change the fact that a Rust compiler -//! considers all types movable. [`mem::swap`] remains callable for any `T`. Instead, [`Pin

`] -//! prevents certain *values* (pointed to by pointers wrapped in [`Pin

`]) from being -//! moved by making it impossible to call methods that require `&mut T` on them -//! (like [`mem::swap`]). -//! -//! [`Pin

`] can be used to wrap any pointer type `P`, and as such it interacts with -//! [`Deref`] and [`DerefMut`]. A [`Pin

`] where `P: Deref` should be considered -//! as a "`P`-style pointer" to a pinned `P::Target` -- so, a [`Pin`]`<`[`Box`]`>` is -//! an owned pointer to a pinned `T`, and a [`Pin`]`<`[`Rc`]`>` is a reference-counted -//! pointer to a pinned `T`. -//! For correctness, [`Pin

`] relies on the implementations of [`Deref`] and +//! It is worth reiterating that [Pin]\

does *not* change the fact that a Rust +//! compiler considers all types movable. [`mem::swap`] remains callable for any `T`. Instead, +//! [Pin]\

prevents certain *values* (pointed to by pointers wrapped in +//! [Pin]\

) from being moved by making it impossible to call methods that require +//! [&mut] T on them (like [`mem::swap`]). +//! +//! [Pin]\

can be used to wrap any pointer type `P`, and as such it interacts with +//! [`Deref`] and [`DerefMut`]. A [Pin]\

where P: [Deref] should be +//! considered as a "`P`-style pointer" to a pinned P::[Target] – so, a +//! [Pin]<[Box]\> is an owned pointer to a pinned `T`, and a +//! [Pin]<[Rc]\> is a reference-counted pointer to a pinned `T`. +//! For correctness, [Pin]\

relies on the implementations of [`Deref`] and //! [`DerefMut`] not to move out of their `self` parameter, and only ever to //! return a pointer to pinned data when they are called on a pinned pointer. //! @@ -53,19 +55,19 @@ //! rely on having a stable address. This includes all the basic types (like //! [`bool`], [`i32`], and references) as well as types consisting solely of these //! types. Types that do not care about pinning implement the [`Unpin`] -//! auto-trait, which cancels the effect of [`Pin

`]. For `T: Unpin`, -//! [`Pin`]`<`[`Box`]`>` and [`Box`] function identically, as do [`Pin`]`<&mut T>` and -//! `&mut T`. +//! auto-trait, which cancels the effect of [Pin]\

. For T: [Unpin], +//! [Pin]<[Box]\> and [Box]\ function identically, as do +//! [Pin]<[&mut] T> and [&mut] T. //! -//! Note that pinning and [`Unpin`] only affect the pointed-to type `P::Target`, not the pointer -//! type `P` itself that got wrapped in [`Pin

`]. For example, whether or not [`Box`] is -//! [`Unpin`] has no effect on the behavior of [`Pin`]`<`[`Box`]`>` (here, `T` is the -//! pointed-to type). +//! Note that pinning and [`Unpin`] only affect the pointed-to type P::[Target], +//! not the pointer type `P` itself that got wrapped in [Pin]\

. For example, +//! whether or not [Box]\ is [`Unpin`] has no effect on the behavior of +//! [Pin]<[Box]\> (here, `T` is the pointed-to type). //! //! # Example: self-referential struct //! //! Before we go into more details to explain the guarantees and choices -//! associated with `Pin`, we discuss some examples for how it might be used. +//! associated with [Pin]\

, we discuss some examples for how it might be used. //! Feel free to [skip to where the theoretical discussion continues](#drop-guarantee). //! //! ```rust @@ -129,7 +131,7 @@ //! //! To make this work, every element has pointers to its predecessor and successor in //! the list. Elements can only be added when they are pinned, because moving the elements -//! around would invalidate the pointers. Moreover, the [`Drop`] implementation of a linked +//! around would invalidate the pointers. Moreover, the [`Drop`][Drop] implementation of a linked //! list element will patch the pointers of its predecessor and successor to remove itself //! from the list. //! @@ -149,8 +151,8 @@ //! when [`drop`] is called*. Only once [`drop`] returns or panics, the memory may be reused. //! //! Memory can be "invalidated" by deallocation, but also by -//! replacing a [`Some(v)`] by [`None`], or calling [`Vec::set_len`] to "kill" some elements -//! off of a vector. It can be repurposed by using [`ptr::write`] to overwrite it without +//! replacing a [Some]\(v) by [`None`], or calling [`Vec::set_len`] to "kill" some +//! elements off of a vector. It can be repurposed by using [`ptr::write`] to overwrite it without //! calling the destructor first. None of this is allowed for pinned data without calling [`drop`]. //! //! This is exactly the kind of guarantee that the intrusive linked list from the previous @@ -158,25 +160,25 @@ //! //! Notice that this guarantee does *not* mean that memory does not leak! It is still //! completely okay not ever to call [`drop`] on a pinned element (e.g., you can still -//! call [`mem::forget`] on a [`Pin`]`<`[`Box`]`>`). In the example of the doubly-linked +//! call [`mem::forget`] on a [Pin]<[Box]\>). In the example of the doubly-linked //! list, that element would just stay in the list. However you may not free or reuse the storage //! *without calling [`drop`]*. //! //! # `Drop` implementation //! //! If your type uses pinning (such as the two examples above), you have to be careful -//! when implementing [`Drop`]. The [`drop`] function takes `&mut self`, but this +//! when implementing [`Drop`][Drop]. The [`drop`] function takes [&mut] self, but this //! is called *even if your type was previously pinned*! It is as if the //! compiler automatically called [`Pin::get_unchecked_mut`]. //! //! This can never cause a problem in safe code because implementing a type that //! relies on pinning requires unsafe code, but be aware that deciding to make //! use of pinning in your type (for example by implementing some operation on -//! [`Pin`]`<&Self>` or [`Pin`]`<&mut Self>`) has consequences for your [`Drop`] -//! implementation as well: if an element of your type could have been pinned, -//! you must treat [`Drop`] as implicitly taking [`Pin`]`<&mut Self>`. +//! [Pin]<[&]Self> or [Pin]<[&mut] Self>) has consequences for your +//! [`Drop`][Drop]implementation as well: if an element of your type could have been pinned, +//! you must treat [`Drop`][Drop] as implicitly taking [Pin]<[&mut] Self>. //! -//! For example, you could implement `Drop` as follows: +//! For example, you could implement [`Drop`][Drop] as follows: //! //! ```rust,no_run //! # use std::pin::Pin; @@ -204,18 +206,18 @@ //! # Projections and Structural Pinning //! //! When working with pinned structs, the question arises how one can access the -//! fields of that struct in a method that takes just [`Pin`]`<&mut Struct>`. +//! fields of that struct in a method that takes just [Pin]<[&mut] Struct>. //! The usual approach is to write helper methods (so called *projections*) -//! that turn [`Pin`]`<&mut Struct>` into a reference to the field, but what -//! type should that reference have? Is it [`Pin`]`<&mut Field>` or `&mut Field`? +//! that turn [Pin]<[&mut] Struct> into a reference to the field, but what type should +//! that reference have? Is it [Pin]<[&mut] Field> or [&mut] Field? //! The same question arises with the fields of an `enum`, and also when considering -//! container/wrapper types such as [`Vec`], [`Box`], or [`RefCell`]. -//! (This question applies to both mutable and shared references, we just -//! use the more common case of mutable references here for illustration.) +//! container/wrapper types such as [Vec]\, [Box]\, +//! or [RefCell]\. (This question applies to both mutable and shared references, +//! we just use the more common case of mutable references here for illustration.) //! -//! It turns out that it is actually up to the author of the data structure -//! to decide whether the pinned projection for a particular field turns -//! [`Pin`]`<&mut Struct>` into [`Pin`]`<&mut Field>` or `&mut Field`. There are some +//! It turns out that it is actually up to the author of the data structure to decide whether +//! the pinned projection for a particular field turns [Pin]<[&mut] Struct> +//! into [Pin]<[&mut] Field> or [&mut] Field. There are some //! constraints though, and the most important constraint is *consistency*: //! every field can be *either* projected to a pinned reference, *or* have //! pinning removed as part of the projection. If both are done for the same field, @@ -230,12 +232,12 @@ //! ## Pinning *is not* structural for `field` //! //! It may seem counter-intuitive that the field of a pinned struct might not be pinned, -//! but that is actually the easiest choice: if a [`Pin`]`<&mut Field>` is never created, +//! but that is actually the easiest choice: if a [Pin]<[&mut] Field> is never created, //! nothing can go wrong! So, if you decide that some field does not have structural pinning, //! all you have to ensure is that you never create a pinned reference to that field. //! //! Fields without structural pinning may have a projection method that turns -//! [`Pin`]`<&mut Struct>` into `&mut Field`: +//! [Pin]<[&mut] Struct> into [&mut] Field: //! //! ```rust,no_run //! # use std::pin::Pin; @@ -249,16 +251,16 @@ //! } //! ``` //! -//! You may also `impl Unpin for Struct` *even if* the type of `field` +//! You may also impl [Unpin] for Struct *even if* the type of `field` //! is not [`Unpin`]. What that type thinks about pinning is not relevant -//! when no [`Pin`]`<&mut Field>` is ever created. +//! when no [Pin]<[&mut] Field> is ever created. //! //! ## Pinning *is* structural for `field` //! //! The other option is to decide that pinning is "structural" for `field`, //! meaning that if the struct is pinned then so is the field. //! -//! This allows writing a projection that creates a [`Pin`]`<&mut Field>`, thus +//! This allows writing a projection that creates a [Pin]<[&mut] Field>, thus //! witnessing that the field is pinned: //! //! ```rust,no_run @@ -278,34 +280,36 @@ //! 1. The struct must only be [`Unpin`] if all the structural fields are //! [`Unpin`]. This is the default, but [`Unpin`] is a safe trait, so as the author of //! the struct it is your responsibility *not* to add something like -//! `impl Unpin for Struct`. (Notice that adding a projection operation +//! impl\ [Unpin] for Struct\. (Notice that adding a projection operation //! requires unsafe code, so the fact that [`Unpin`] is a safe trait does not break -//! the principle that you only have to worry about any of this if you use `unsafe`.) +//! the principle that you only have to worry about any of this if you use [`unsafe`].) //! 2. The destructor of the struct must not move structural fields out of its argument. This -//! is the exact point that was raised in the [previous section][drop-impl]: `drop` takes -//! `&mut self`, but the struct (and hence its fields) might have been pinned before. -//! You have to guarantee that you do not move a field inside your [`Drop`] implementation. -//! In particular, as explained previously, this means that your struct must *not* -//! be `#[repr(packed)]`. +//! is the exact point that was raised in the [previous section][drop-impl]: [`drop`] takes +//! [&mut] self, but the struct (and hence its fields) might have been pinned +//! before. You have to guarantee that you do not move a field inside your [`Drop`][Drop] +//! implementation. In particular, as explained previously, this means that your struct +//! must *not* be `#[repr(packed)]`. //! See that section for how to write [`drop`] in a way that the compiler can help you //! not accidentally break pinning. //! 3. You must make sure that you uphold the [`Drop` guarantee][drop-guarantee]: //! once your struct is pinned, the memory that contains the //! content is not overwritten or deallocated without calling the content's destructors. -//! This can be tricky, as witnessed by [`VecDeque`]: the destructor of [`VecDeque`] -//! can fail to call [`drop`] on all elements if one of the destructors panics. This violates -//! the [`Drop`] guarantee, because it can lead to elements being deallocated without -//! their destructor being called. ([`VecDeque`] has no pinning projections, so this +//! This can be tricky, as witnessed by [VecDeque]\: the destructor of +//! [VecDeque]\ can fail to call [`drop`] on all elements if one of the +//! destructors panics. This violates the [`Drop`][Drop] guarantee, because it can lead to +//! elements being deallocated without their destructor being called. +//! ([VecDeque]\ has no pinning projections, so this //! does not cause unsoundness.) //! 4. You must not offer any other operations that could lead to data being moved out of //! the structural fields when your type is pinned. For example, if the struct contains an -//! [`Option`] and there is a `take`-like operation with type -//! `fn(Pin<&mut Struct>) -> Option`, -//! that operation can be used to move a `T` out of a pinned `Struct` -- which means +//! [Option]\ and there is a [`take`][Option::take]-like operation with type +//! fn([Pin]<[&mut] Struct\>) -> [Option]\, +//! that operation can be used to move a `T` out of a pinned `Struct` – which means //! pinning cannot be structural for the field holding this data. //! -//! For a more complex example of moving data out of a pinned type, imagine if [`RefCell`] -//! had a method `fn get_pin_mut(self: Pin<&mut Self>) -> Pin<&mut T>`. +//! For a more complex example of moving data out of a pinned type, +//! imagine if [RefCell]\ had a method +//! fn get_pin_mut(self: [Pin]<[&mut] Self>) -> [Pin]<[&mut] T>. //! Then we could do the following: //! ```compile_fail //! fn exploit_ref_cell(rc: Pin<&mut RefCell>) { @@ -315,60 +319,65 @@ //! let content = &mut *b; // And here we have `&mut T` to the same data. //! } //! ``` -//! This is catastrophic, it means we can first pin the content of the [`RefCell`] -//! (using `RefCell::get_pin_mut`) and then move that content using the mutable -//! reference we got later. +//! This is catastrophic, it means we can first pin the content of the +//! [RefCell]\ (using [RefCell]::get_pin_mut) and then move that +//! content using the mutable reference we got later. //! //! ## Examples //! -//! For a type like [`Vec`], both possibilities (structural pinning or not) make sense. -//! A [`Vec`] with structural pinning could have `get_pin`/`get_pin_mut` methods to get -//! pinned references to elements. However, it could *not* allow calling -//! [`pop`][Vec::pop] on a pinned [`Vec`] because that would move the (structurally pinned) -//! contents! Nor could it allow [`push`][Vec::push], which might reallocate and thus also move the -//! contents. +//! For a type like [Vec]\, both possibilities (structural pinning or not) make +//! sense. A [Vec]\ with structural pinning could have `get_pin`/`get_pin_mut` +//! methods to get pinned references to elements. However, it could *not* allow calling +//! [`pop`][Vec::pop] on a pinned [Vec]\ because that would move the (structurally +//! pinned) contents! Nor could it allow [`push`][Vec::push], which might reallocate and thus also +//! move the contents. //! -//! A [`Vec`] without structural pinning could `impl Unpin for Vec`, because the contents -//! are never pinned and the [`Vec`] itself is fine with being moved as well. +//! A [Vec]\ without structural pinning could +//! impl\ [Unpin] for [Vec]\, because the contents are never pinned +//! and the [Vec]\ itself is fine with being moved as well. //! At that point pinning just has no effect on the vector at all. //! //! In the standard library, pointer types generally do not have structural pinning, -//! and thus they do not offer pinning projections. This is why `Box: Unpin` holds for all `T`. -//! It makes sense to do this for pointer types, because moving the `Box` -//! does not actually move the `T`: the [`Box`] can be freely movable (aka `Unpin`) even if -//! the `T` is not. In fact, even [`Pin`]`<`[`Box`]`>` and [`Pin`]`<&mut T>` are always -//! [`Unpin`] themselves, for the same reason: their contents (the `T`) are pinned, but the -//! pointers themselves can be moved without moving the pinned data. For both [`Box`] and -//! [`Pin`]`<`[`Box`]`>`, whether the content is pinned is entirely independent of whether the +//! and thus they do not offer pinning projections. This is why [Box]\: [Unpin] +//! holds for all `T`. It makes sense to do this for pointer types, because moving the +//! [Box]\ does not actually move the `T`: the [Box]\ can be freely +//! movable (aka [`Unpin`]) even if the `T` is not. In fact, even [Pin]<[Box]\> and +//! [Pin]<[&mut] T> are always [`Unpin`] themselves, for the same reason: +//! their contents (the `T`) are pinned, but the pointers themselves can be moved without moving +//! the pinned data. For both [Box]\ and [Pin]<[Box]\>, +//! whether the content is pinned is entirely independent of whether the //! pointer is pinned, meaning pinning is *not* structural. //! //! When implementing a [`Future`] combinator, you will usually need structural pinning //! for the nested futures, as you need to get pinned references to them to call [`poll`]. //! But if your combinator contains any other data that does not need to be pinned, //! you can make those fields not structural and hence freely access them with a -//! mutable reference even when you just have [`Pin`]`<&mut Self>` (such as in your own +//! mutable reference even when you just have [Pin]<[&mut] Self> (such as in your own //! [`poll`] implementation). //! -//! [`Deref`]: crate::ops::Deref -//! [`DerefMut`]: crate::ops::DerefMut -//! [`mem::swap`]: crate::mem::swap -//! [`mem::forget`]: crate::mem::forget -//! [`Box`]: ../../std/boxed/struct.Box.html -//! [`Vec`]: ../../std/vec/struct.Vec.html -//! [`Vec::set_len`]: ../../std/vec/struct.Vec.html#method.set_len -//! [`Box`]: ../../std/boxed/struct.Box.html -//! [Vec::pop]: ../../std/vec/struct.Vec.html#method.pop -//! [Vec::push]: ../../std/vec/struct.Vec.html#method.push -//! [`Rc`]: ../../std/rc/struct.Rc.html -//! [`RefCell`]: crate::cell::RefCell -//! [`drop`]: Drop::drop -//! [`VecDeque`]: ../../std/collections/struct.VecDeque.html -//! [`Some(v)`]: Some -//! [`ptr::write`]: crate::ptr::write -//! [`Future`]: crate::future::Future +//! [Deref]: crate::ops::Deref "ops::Deref" +//! [`Deref`]: crate::ops::Deref "ops::Deref" +//! [Target]: crate::ops::Deref::Target "ops::Deref::Target" +//! [`DerefMut`]: crate::ops::DerefMut "ops::DerefMut" +//! [`mem::swap`]: crate::mem::swap "mem::swap" +//! [`mem::forget`]: crate::mem::forget "mem::forget" +//! [Vec]: ../../std/vec/struct.Vec.html "Vec" +//! [`Vec::set_len`]: ../../std/vec/struct.Vec.html#method.set_len "Vec::set_len" +//! [Box]: ../../std/boxed/struct.Box.html "Box" +//! [Vec::pop]: ../../std/vec/struct.Vec.html#method.pop "Vec::pop" +//! [Vec::push]: ../../std/vec/struct.Vec.html#method.push "Vec::push" +//! [Rc]: ../../std/rc/struct.Rc.html "rc::Rc" +//! [RefCell]: crate::cell::RefCell "cell::RefCell" +//! [`drop`]: Drop::drop "Drop::drop" +//! [VecDeque]: ../../std/collections/struct.VecDeque.html "collections::VecDeque" +//! [`ptr::write`]: crate::ptr::write "ptr::write" +//! [`Future`]: crate::future::Future "future::Future" //! [drop-impl]: #drop-implementation //! [drop-guarantee]: #drop-guarantee -//! [`poll`]: crate::future::Future::poll +//! [`poll`]: crate::future::Future::poll "future::Future::poll" +//! [&]: ../../std/primitive.reference.html "shared reference" +//! [&mut]: ../../std/primitive.reference.html "mutable reference" +//! [`unsafe`]: ../../std/keyword.unsafe.html "keyword unsafe" #![stable(feature = "pin", since = "1.33.0")] diff --git a/library/core/src/prelude/mod.rs b/library/core/src/prelude/mod.rs index 51f4acf068580..8f57db49496c3 100644 --- a/library/core/src/prelude/mod.rs +++ b/library/core/src/prelude/mod.rs @@ -1,5 +1,41 @@ //! The libcore prelude +//! +//! This module is intended for users of libcore which do not link to libstd as +//! well. This module is imported by default when `#![no_std]` is used in the +//! same manner as the standard library's prelude. #![stable(feature = "core_prelude", since = "1.4.0")] pub mod v1; + +/// The 2015 version of the core prelude. +/// +/// See the [module-level documentation](self) for more. +#[unstable(feature = "prelude_2015", issue = "none")] +pub mod rust_2015 { + #[unstable(feature = "prelude_2015", issue = "none")] + #[doc(no_inline)] + pub use super::v1::*; +} + +/// The 2018 version of the core prelude. +/// +/// See the [module-level documentation](self) for more. +#[unstable(feature = "prelude_2018", issue = "none")] +pub mod rust_2018 { + #[unstable(feature = "prelude_2018", issue = "none")] + #[doc(no_inline)] + pub use super::v1::*; +} + +/// The 2021 version of the core prelude. +/// +/// See the [module-level documentation](self) for more. +#[unstable(feature = "prelude_2021", issue = "none")] +pub mod rust_2021 { + #[unstable(feature = "prelude_2021", issue = "none")] + #[doc(no_inline)] + pub use super::v1::*; + + // FIXME: Add more things. +} diff --git a/library/core/src/prelude/v1.rs b/library/core/src/prelude/v1.rs index a1fbd8dec7505..c89fe57cb05ce 100644 --- a/library/core/src/prelude/v1.rs +++ b/library/core/src/prelude/v1.rs @@ -1,8 +1,6 @@ -//! The core prelude +//! The first version of the core prelude. //! -//! This module is intended for users of libcore which do not link to libstd as -//! well. This module is imported by default when `#![no_std]` is used in the -//! same manner as the standard library's prelude. +//! See the [module-level documentation](super) for more. #![stable(feature = "core_prelude", since = "1.4.0")] @@ -63,13 +61,12 @@ pub use crate::{ }; #[stable(feature = "builtin_macro_prelude", since = "1.38.0")] -#[allow(deprecated)] +#[allow(deprecated, deprecated_in_future)] #[doc(no_inline)] pub use crate::macros::builtin::{ bench, global_allocator, test, test_case, RustcDecodable, RustcEncodable, }; -#[cfg(not(bootstrap))] #[stable(feature = "builtin_macro_prelude", since = "1.38.0")] #[doc(no_inline)] pub use crate::macros::builtin::derive; @@ -81,3 +78,11 @@ pub use crate::macros::builtin::derive; )] #[doc(no_inline)] pub use crate::macros::builtin::cfg_accessible; + +#[unstable( + feature = "cfg_eval", + issue = "82679", + reason = "`cfg_eval` is a recently implemented feature" +)] +#[doc(no_inline)] +pub use crate::macros::builtin::cfg_eval; diff --git a/library/core/src/ptr/const_ptr.rs b/library/core/src/ptr/const_ptr.rs index cfc1bfd54be2d..b9b2ba9ae61e9 100644 --- a/library/core/src/ptr/const_ptr.rs +++ b/library/core/src/ptr/const_ptr.rs @@ -51,7 +51,6 @@ impl *const T { /// Decompose a (possibly wide) pointer into is address and metadata components. /// /// The pointer can be later reconstructed with [`from_raw_parts`]. - #[cfg(not(bootstrap))] #[unstable(feature = "ptr_metadata", issue = "81513")] #[rustc_const_unstable(feature = "ptr_metadata", issue = "81513")] #[inline] @@ -184,8 +183,7 @@ impl *const T { /// Behavior: /// /// * Both the starting and resulting pointer must be either in bounds or one - /// byte past the end of the same allocated object. Note that in Rust, - /// every (stack-allocated) variable is considered a separate allocated object. + /// byte past the end of the same [allocated object]. /// /// * The computed offset, **in bytes**, cannot overflow an `isize`. /// @@ -210,6 +208,7 @@ impl *const T { /// enables more aggressive compiler optimizations. /// /// [`wrapping_offset`]: #method.wrapping_offset + /// [allocated object]: crate::ptr#allocated-object /// /// # Examples /// @@ -227,7 +226,7 @@ impl *const T { #[stable(feature = "rust1", since = "1.0.0")] #[must_use = "returns a new pointer rather than modifying its argument"] #[rustc_const_unstable(feature = "const_ptr_offset", issue = "71499")] - #[inline] + #[inline(always)] pub const unsafe fn offset(self, count: isize) -> *const T where T: Sized, @@ -245,9 +244,8 @@ impl *const T { /// /// This operation itself is always safe, but using the resulting pointer is not. /// - /// The resulting pointer remains attached to the same allocated object that `self` points to. - /// It may *not* be used to access a different allocated object. Note that in Rust, every - /// (stack-allocated) variable is considered a separate allocated object. + /// The resulting pointer "remembers" the [allocated object] that `self` points to; it may not + /// be used to read or write other allocated objects. /// /// In other words, `let z = x.wrapping_offset((y as isize) - (x as isize))` does *not* make `z` /// the same as `y` even if we assume `T` has size `1` and there is no overflow: `z` is still @@ -265,10 +263,8 @@ impl *const T { /// `x.wrapping_offset(o).wrapping_offset(o.wrapping_neg())` is always the same as `x`. In other /// words, leaving the allocated object and then re-entering it later is permitted. /// - /// If you need to cross object boundaries, cast the pointer to an integer and - /// do the arithmetic there. - /// /// [`offset`]: #method.offset + /// [allocated object]: crate::ptr#allocated-object /// /// # Examples /// @@ -292,7 +288,7 @@ impl *const T { #[stable(feature = "ptr_wrapping_offset", since = "1.16.0")] #[must_use = "returns a new pointer rather than modifying its argument"] #[rustc_const_unstable(feature = "const_ptr_offset", issue = "71499")] - #[inline] + #[inline(always)] pub const fn wrapping_offset(self, count: isize) -> *const T where T: Sized, @@ -314,31 +310,37 @@ impl *const T { /// Behavior: /// /// * Both the starting and other pointer must be either in bounds or one - /// byte past the end of the same allocated object. Note that in Rust, - /// every (stack-allocated) variable is considered a separate allocated object. + /// byte past the end of the same [allocated object]. /// /// * Both pointers must be *derived from* a pointer to the same object. /// (See below for an example.) /// - /// * The distance between the pointers, **in bytes**, cannot overflow an `isize`. - /// /// * The distance between the pointers, in bytes, must be an exact multiple /// of the size of `T`. /// + /// * The distance between the pointers, **in bytes**, cannot overflow an `isize`. + /// /// * The distance being in bounds cannot rely on "wrapping around" the address space. /// - /// The compiler and standard library generally try to ensure allocations - /// never reach a size where an offset is a concern. For instance, `Vec` - /// and `Box` ensure they never allocate more than `isize::MAX` bytes, so - /// `ptr_into_vec.offset_from(vec.as_ptr())` is always safe. + /// Rust types are never larger than `isize::MAX` and Rust allocations never wrap around the + /// address space, so two pointers within some value of any Rust type `T` will always satisfy + /// the last two conditions. The standard library also generally ensures that allocations + /// never reach a size where an offset is a concern. For instance, `Vec` and `Box` ensure they + /// never allocate more than `isize::MAX` bytes, so `ptr_into_vec.offset_from(vec.as_ptr())` + /// always satisfies the last two conditions. /// - /// Most platforms fundamentally can't even construct such an allocation. + /// Most platforms fundamentally can't even construct such a large allocation. /// For instance, no known 64-bit platform can ever serve a request /// for 263 bytes due to page-table limitations or splitting the address space. /// However, some 32-bit and 16-bit platforms may successfully serve a request for /// more than `isize::MAX` bytes with things like Physical Address /// Extension. As such, memory acquired directly from allocators or memory /// mapped files *may* be too large to handle with this function. + /// (Note that [`offset`] and [`add`] also have a similar limitation and hence cannot be used on + /// such large allocations either.) + /// + /// [`add`]: #method.add + /// [allocated object]: crate::ptr#allocated-object /// /// # Panics /// @@ -462,8 +464,7 @@ impl *const T { /// Behavior: /// /// * Both the starting and resulting pointer must be either in bounds or one - /// byte past the end of the same allocated object. Note that in Rust, - /// every (stack-allocated) variable is considered a separate allocated object. + /// byte past the end of the same [allocated object]. /// /// * The computed offset, **in bytes**, cannot overflow an `isize`. /// @@ -488,6 +489,7 @@ impl *const T { /// enables more aggressive compiler optimizations. /// /// [`wrapping_add`]: #method.wrapping_add + /// [allocated object]: crate::ptr#allocated-object /// /// # Examples /// @@ -505,7 +507,7 @@ impl *const T { #[stable(feature = "pointer_methods", since = "1.26.0")] #[must_use = "returns a new pointer rather than modifying its argument"] #[rustc_const_unstable(feature = "const_ptr_offset", issue = "71499")] - #[inline] + #[inline(always)] pub const unsafe fn add(self, count: usize) -> Self where T: Sized, @@ -526,8 +528,7 @@ impl *const T { /// Behavior: /// /// * Both the starting and resulting pointer must be either in bounds or one - /// byte past the end of the same allocated object. Note that in Rust, - /// every (stack-allocated) variable is considered a separate allocated object. + /// byte past the end of the same [allocated object]. /// /// * The computed offset cannot exceed `isize::MAX` **bytes**. /// @@ -552,6 +553,7 @@ impl *const T { /// enables more aggressive compiler optimizations. /// /// [`wrapping_sub`]: #method.wrapping_sub + /// [allocated object]: crate::ptr#allocated-object /// /// # Examples /// @@ -588,9 +590,8 @@ impl *const T { /// /// This operation itself is always safe, but using the resulting pointer is not. /// - /// The resulting pointer remains attached to the same allocated object that `self` points to. - /// It may *not* be used to access a different allocated object. Note that in Rust, every - /// (stack-allocated) variable is considered a separate allocated object. + /// The resulting pointer "remembers" the [allocated object] that `self` points to; it may not + /// be used to read or write other allocated objects. /// /// In other words, `let z = x.wrapping_add((y as usize) - (x as usize))` does *not* make `z` /// the same as `y` even if we assume `T` has size `1` and there is no overflow: `z` is still @@ -608,10 +609,8 @@ impl *const T { /// `x.wrapping_add(o).wrapping_sub(o)` is always the same as `x`. In other words, leaving the /// allocated object and then re-entering it later is permitted. /// - /// If you need to cross object boundaries, cast the pointer to an integer and - /// do the arithmetic there. - /// /// [`add`]: #method.add + /// [allocated object]: crate::ptr#allocated-object /// /// # Examples /// @@ -635,7 +634,7 @@ impl *const T { #[stable(feature = "pointer_methods", since = "1.26.0")] #[must_use = "returns a new pointer rather than modifying its argument"] #[rustc_const_unstable(feature = "const_ptr_offset", issue = "71499")] - #[inline] + #[inline(always)] pub const fn wrapping_add(self, count: usize) -> Self where T: Sized, @@ -653,9 +652,8 @@ impl *const T { /// /// This operation itself is always safe, but using the resulting pointer is not. /// - /// The resulting pointer remains attached to the same allocated object that `self` points to. - /// It may *not* be used to access a different allocated object. Note that in Rust, every - /// (stack-allocated) variable is considered a separate allocated object. + /// The resulting pointer "remembers" the [allocated object] that `self` points to; it may not + /// be used to read or write other allocated objects. /// /// In other words, `let z = x.wrapping_sub((x as usize) - (y as usize))` does *not* make `z` /// the same as `y` even if we assume `T` has size `1` and there is no overflow: `z` is still @@ -673,10 +671,8 @@ impl *const T { /// `x.wrapping_add(o).wrapping_sub(o)` is always the same as `x`. In other words, leaving the /// allocated object and then re-entering it later is permitted. /// - /// If you need to cross object boundaries, cast the pointer to an integer and - /// do the arithmetic there. - /// /// [`sub`]: #method.sub + /// [allocated object]: crate::ptr#allocated-object /// /// # Examples /// @@ -728,7 +724,7 @@ impl *const T { /// #![feature(set_ptr_value)] /// # use core::fmt::Debug; /// let arr: [i32; 3] = [1, 2, 3]; - /// let mut ptr = &arr[0] as *const dyn Debug; + /// let mut ptr = arr.as_ptr() as *const dyn Debug; /// let thin = ptr as *const u8; /// unsafe { /// ptr = ptr.set_ptr_value(thin.add(8)); @@ -813,9 +809,10 @@ impl *const T { /// See [`ptr::copy`] for safety concerns and examples. /// /// [`ptr::copy`]: crate::ptr::copy() + #[rustc_const_unstable(feature = "const_intrinsic_copy", issue = "80697")] #[stable(feature = "pointer_methods", since = "1.26.0")] #[inline] - pub unsafe fn copy_to(self, dest: *mut T, count: usize) + pub const unsafe fn copy_to(self, dest: *mut T, count: usize) where T: Sized, { @@ -831,9 +828,10 @@ impl *const T { /// See [`ptr::copy_nonoverlapping`] for safety concerns and examples. /// /// [`ptr::copy_nonoverlapping`]: crate::ptr::copy_nonoverlapping() + #[rustc_const_unstable(feature = "const_intrinsic_copy", issue = "80697")] #[stable(feature = "pointer_methods", since = "1.26.0")] #[inline] - pub unsafe fn copy_to_nonoverlapping(self, dest: *mut T, count: usize) + pub const unsafe fn copy_to_nonoverlapping(self, dest: *mut T, count: usize) where T: Sized, { @@ -916,13 +914,6 @@ impl *const [T] { #[unstable(feature = "slice_ptr_len", issue = "71146")] #[rustc_const_unstable(feature = "const_slice_ptr_len", issue = "71146")] pub const fn len(self) -> usize { - #[cfg(bootstrap)] - { - // SAFETY: this is safe because `*const [T]` and `FatPtr` have the same layout. - // Only `std` can make this guarantee. - unsafe { Repr { rust: self }.raw }.len - } - #[cfg(not(bootstrap))] metadata(self) } @@ -989,7 +980,7 @@ impl *const [T] { /// * The pointer must be [valid] for reads for `ptr.len() * mem::size_of::()` many bytes, /// and it must be properly aligned. This means in particular: /// - /// * The entire memory range of this slice must be contained within a single allocated object! + /// * The entire memory range of this slice must be contained within a single [allocated object]! /// Slices can never span across multiple allocated objects. /// /// * The pointer must be aligned even for zero-length slices. One @@ -1011,6 +1002,7 @@ impl *const [T] { /// See also [`slice::from_raw_parts`][]. /// /// [valid]: crate::ptr#safety + /// [allocated object]: crate::ptr#allocated-object #[inline] #[unstable(feature = "ptr_as_uninit", issue = "75402")] pub unsafe fn as_uninit_slice<'a>(self) -> Option<&'a [MaybeUninit]> { diff --git a/library/core/src/ptr/mod.rs b/library/core/src/ptr/mod.rs index 5026c48bdf4b4..ad8696ab9272d 100644 --- a/library/core/src/ptr/mod.rs +++ b/library/core/src/ptr/mod.rs @@ -55,6 +55,14 @@ //! has size 0, i.e., even if memory is not actually touched. Consider using //! [`NonNull::dangling`] in such cases. //! +//! ## Allocated object +//! +//! For several operations, such as [`offset`] or field projections (`expr.field`), the notion of an +//! "allocated object" becomes relevant. An allocated object is a contiguous region of memory. +//! Common examples of allocated objects include stack-allocated variables (each variable is a +//! separate allocated object), heap allocations (each allocation created by the global allocator is +//! a separate allocated object), and `static` variables. +//! //! [aliasing]: ../../nomicon/aliasing.html //! [book]: ../../book/ch19-01-unsafe-rust.html#dereferencing-a-raw-pointer //! [ub]: ../../reference/behavior-considered-undefined.html @@ -67,7 +75,7 @@ use crate::cmp::Ordering; use crate::fmt; use crate::hash; -use crate::intrinsics::{self, abort, is_aligned_and_not_null, is_nonoverlapping}; +use crate::intrinsics::{self, abort, is_aligned_and_not_null}; use crate::mem::{self, MaybeUninit}; #[stable(feature = "rust1", since = "1.0.0")] @@ -82,11 +90,8 @@ pub use crate::intrinsics::copy; #[doc(inline)] pub use crate::intrinsics::write_bytes; -#[cfg(not(bootstrap))] mod metadata; -#[cfg(not(bootstrap))] pub(crate) use metadata::PtrRepr; -#[cfg(not(bootstrap))] #[unstable(feature = "ptr_metadata", issue = "81513")] pub use metadata::{from_raw_parts, from_raw_parts_mut, metadata, DynMetadata, Pointee, Thin}; @@ -205,7 +210,8 @@ pub unsafe fn drop_in_place(to_drop: *mut T) { #[inline(always)] #[stable(feature = "rust1", since = "1.0.0")] #[rustc_promotable] -#[rustc_const_stable(feature = "const_ptr_null", since = "1.32.0")] +#[rustc_const_stable(feature = "const_ptr_null", since = "1.24.0")] +#[rustc_diagnostic_item = "ptr_null"] pub const fn null() -> *const T { 0 as *const T } @@ -223,38 +229,12 @@ pub const fn null() -> *const T { #[inline(always)] #[stable(feature = "rust1", since = "1.0.0")] #[rustc_promotable] -#[rustc_const_stable(feature = "const_ptr_null", since = "1.32.0")] +#[rustc_const_stable(feature = "const_ptr_null", since = "1.24.0")] +#[rustc_diagnostic_item = "ptr_null_mut"] pub const fn null_mut() -> *mut T { 0 as *mut T } -#[cfg(bootstrap)] -#[repr(C)] -pub(crate) union Repr { - pub(crate) rust: *const [T], - rust_mut: *mut [T], - pub(crate) raw: FatPtr, -} - -#[cfg(bootstrap)] -#[repr(C)] -pub(crate) struct FatPtr { - data: *const T, - pub(crate) len: usize, -} - -#[cfg(bootstrap)] -// Manual impl needed to avoid `T: Clone` bound. -impl Clone for FatPtr { - fn clone(&self) -> Self { - *self - } -} - -#[cfg(bootstrap)] -// Manual impl needed to avoid `T: Copy` bound. -impl Copy for FatPtr {} - /// Forms a raw slice from a pointer and a length. /// /// The `len` argument is the number of **elements**, not the number of bytes. @@ -279,14 +259,6 @@ impl Copy for FatPtr {} #[stable(feature = "slice_from_raw_parts", since = "1.42.0")] #[rustc_const_unstable(feature = "const_slice_from_raw_parts", issue = "67456")] pub const fn slice_from_raw_parts(data: *const T, len: usize) -> *const [T] { - #[cfg(bootstrap)] - { - // SAFETY: Accessing the value from the `Repr` union is safe since *const [T] - // and FatPtr have the same memory layouts. Only std can make this - // guarantee. - unsafe { Repr { raw: FatPtr { data, len } }.rust } - } - #[cfg(not(bootstrap))] from_raw_parts(data.cast(), len) } @@ -319,13 +291,6 @@ pub const fn slice_from_raw_parts(data: *const T, len: usize) -> *const [T] { #[stable(feature = "slice_from_raw_parts", since = "1.42.0")] #[rustc_const_unstable(feature = "const_slice_from_raw_parts", issue = "67456")] pub const fn slice_from_raw_parts_mut(data: *mut T, len: usize) -> *mut [T] { - #[cfg(bootstrap)] - { - // SAFETY: Accessing the value from the `Repr` union is safe since *mut [T] - // and FatPtr have the same memory layouts - unsafe { Repr { raw: FatPtr { data, len } }.rust_mut } - } - #[cfg(not(bootstrap))] from_raw_parts_mut(data.cast(), len) } @@ -394,7 +359,8 @@ pub const fn slice_from_raw_parts_mut(data: *mut T, len: usize) -> *mut [T] { /// ``` #[inline] #[stable(feature = "rust1", since = "1.0.0")] -pub unsafe fn swap(x: *mut T, y: *mut T) { +#[rustc_const_unstable(feature = "const_swap", issue = "83163")] +pub const unsafe fn swap(x: *mut T, y: *mut T) { // Give ourselves some scratch space to work with. // We do not have to worry about drops: `MaybeUninit` does nothing when dropped. let mut tmp = MaybeUninit::::uninit(); @@ -451,16 +417,8 @@ pub unsafe fn swap(x: *mut T, y: *mut T) { /// ``` #[inline] #[stable(feature = "swap_nonoverlapping", since = "1.27.0")] -pub unsafe fn swap_nonoverlapping(x: *mut T, y: *mut T, count: usize) { - if cfg!(debug_assertions) - && !(is_aligned_and_not_null(x) - && is_aligned_and_not_null(y) - && is_nonoverlapping(x, y, count)) - { - // Not panicking to keep codegen impact smaller. - abort(); - } - +#[rustc_const_unstable(feature = "const_swap", issue = "83163")] +pub const unsafe fn swap_nonoverlapping(x: *mut T, y: *mut T, count: usize) { let x = x as *mut u8; let y = y as *mut u8; let len = mem::size_of::() * count; @@ -470,25 +428,40 @@ pub unsafe fn swap_nonoverlapping(x: *mut T, y: *mut T, count: usize) { } #[inline] -pub(crate) unsafe fn swap_nonoverlapping_one(x: *mut T, y: *mut T) { - // For types smaller than the block optimization below, - // just swap directly to avoid pessimizing codegen. - if mem::size_of::() < 32 { - // SAFETY: the caller must guarantee that `x` and `y` are valid - // for writes, properly aligned, and non-overlapping. - unsafe { - let z = read(x); - copy_nonoverlapping(y, x, 1); - write(y, z); +#[rustc_const_unstable(feature = "const_swap", issue = "83163")] +pub(crate) const unsafe fn swap_nonoverlapping_one(x: *mut T, y: *mut T) { + // NOTE(eddyb) SPIR-V's Logical addressing model doesn't allow for arbitrary + // reinterpretation of values as (chunkable) byte arrays, and the loop in the + // block optimization in `swap_nonoverlapping_bytes` is hard to rewrite back + // into the (unoptimized) direct swapping implementation, so we disable it. + // FIXME(eddyb) the block optimization also prevents MIR optimizations from + // understanding `mem::replace`, `Option::take`, etc. - a better overall + // solution might be to make `swap_nonoverlapping` into an intrinsic, which + // a backend can choose to implement using the block optimization, or not. + #[cfg(not(target_arch = "spirv"))] + { + // Only apply the block optimization in `swap_nonoverlapping_bytes` for types + // at least as large as the block size, to avoid pessimizing codegen. + if mem::size_of::() >= 32 { + // SAFETY: the caller must uphold the safety contract for `swap_nonoverlapping`. + unsafe { swap_nonoverlapping(x, y, 1) }; + return; } - } else { - // SAFETY: the caller must uphold the safety contract for `swap_nonoverlapping`. - unsafe { swap_nonoverlapping(x, y, 1) }; + } + + // Direct swapping, for the cases not going through the block optimization. + // SAFETY: the caller must guarantee that `x` and `y` are valid + // for writes, properly aligned, and non-overlapping. + unsafe { + let z = read(x); + copy_nonoverlapping(y, x, 1); + write(y, z); } } #[inline] -unsafe fn swap_nonoverlapping_bytes(x: *mut u8, y: *mut u8, len: usize) { +#[rustc_const_unstable(feature = "const_swap", issue = "83163")] +const unsafe fn swap_nonoverlapping_bytes(x: *mut u8, y: *mut u8, len: usize) { // The approach here is to utilize simd to swap x & y efficiently. Testing reveals // that swapping either 32 bytes or 64 bytes at a time is most efficient for Intel // Haswell E processors. LLVM is more able to optimize if we give a struct a @@ -512,7 +485,7 @@ unsafe fn swap_nonoverlapping_bytes(x: *mut u8, y: *mut u8, len: usize) { let t = t.as_mut_ptr() as *mut u8; // SAFETY: As `i < len`, and as the caller must guarantee that `x` and `y` are valid - // for `len` bytes, `x + i` and `y + i` must be valid adresses, which fulfills the + // for `len` bytes, `x + i` and `y + i` must be valid addresses, which fulfills the // safety contract for `add`. // // Also, the caller must guarantee that `x` and `y` are valid for writes, properly aligned, @@ -589,7 +562,8 @@ unsafe fn swap_nonoverlapping_bytes(x: *mut u8, y: *mut u8, len: usize) { /// ``` #[inline] #[stable(feature = "rust1", since = "1.0.0")] -pub unsafe fn replace(dst: *mut T, mut src: T) -> T { +#[rustc_const_unstable(feature = "const_replace", issue = "83164")] +pub const unsafe fn replace(dst: *mut T, mut src: T) -> T { // SAFETY: the caller must guarantee that `dst` is valid to be // cast to a mutable reference (valid for writes, aligned, initialized), // and cannot overlap `src` since `dst` must point to a distinct @@ -771,6 +745,7 @@ pub const unsafe fn read(src: *const T) -> T { /// unaligned: 0x01020304, /// }; /// +/// #[allow(unaligned_references)] /// let v = unsafe { /// // Here we attempt to take the address of a 32-bit integer which is not aligned. /// let unaligned = @@ -964,6 +939,7 @@ pub const unsafe fn write(dst: *mut T, src: T) { /// let v = 0x01020304; /// let mut packed: Packed = unsafe { std::mem::zeroed() }; /// +/// #[allow(unaligned_references)] /// let v = unsafe { /// // Here we attempt to take the address of a 32-bit integer which is not aligned. /// let unaligned = @@ -1518,6 +1494,10 @@ fnptr_impls_args! { A, B, C, D, E, F, G, H, I, J, K, L } /// as all other references. This macro can create a raw pointer *without* creating /// a reference first. /// +/// Note, however, that the `expr` in `addr_of!(expr)` is still subject to all +/// the usual rules. In particular, `addr_of!(*ptr::null())` is Undefined +/// Behavior because it dereferences a NULL pointer. +/// /// # Example /// /// ``` @@ -1534,6 +1514,10 @@ fnptr_impls_args! { A, B, C, D, E, F, G, H, I, J, K, L } /// let raw_f2 = ptr::addr_of!(packed.f2); /// assert_eq!(unsafe { raw_f2.read_unaligned() }, 2); /// ``` +/// +/// See [`addr_of_mut`] for how to create a pointer to unininitialized data. +/// Doing that with `addr_of` would not make much sense since one could only +/// read the data, and that would be Undefined Behavior. #[stable(feature = "raw_ref_macros", since = "1.51.0")] #[rustc_macro_transparency = "semitransparent"] #[allow_internal_unstable(raw_ref_op)] @@ -1550,7 +1534,13 @@ pub macro addr_of($place:expr) { /// as all other references. This macro can create a raw pointer *without* creating /// a reference first. /// -/// # Example +/// Note, however, that the `expr` in `addr_of_mut!(expr)` is still subject to all +/// the usual rules. In particular, `addr_of_mut!(*ptr::null_mut())` is Undefined +/// Behavior because it dereferences a NULL pointer. +/// +/// # Examples +/// +/// **Creating a pointer to unaligned data:** /// /// ``` /// use std::ptr; @@ -1567,6 +1557,23 @@ pub macro addr_of($place:expr) { /// unsafe { raw_f2.write_unaligned(42); } /// assert_eq!({packed.f2}, 42); // `{...}` forces copying the field instead of creating a reference. /// ``` +/// +/// **Creating a pointer to uninitialized data:** +/// +/// ```rust +/// use std::{ptr, mem::MaybeUninit}; +/// +/// struct Demo { +/// field: bool, +/// } +/// +/// let mut uninit = MaybeUninit::::uninit(); +/// // `&uninit.as_mut().field` would create a reference to an uninitialized `bool`, +/// // and thus be Undefined Behavior! +/// let f1_ptr = unsafe { ptr::addr_of_mut!((*uninit.as_mut_ptr()).field) }; +/// unsafe { f1_ptr.write(true); } +/// let init = unsafe { uninit.assume_init() }; +/// ``` #[stable(feature = "raw_ref_macros", since = "1.51.0")] #[rustc_macro_transparency = "semitransparent"] #[allow_internal_unstable(raw_ref_op)] diff --git a/library/core/src/ptr/mut_ptr.rs b/library/core/src/ptr/mut_ptr.rs index 06bacc8635147..55c019c51d51b 100644 --- a/library/core/src/ptr/mut_ptr.rs +++ b/library/core/src/ptr/mut_ptr.rs @@ -50,7 +50,6 @@ impl *mut T { /// Decompose a (possibly wide) pointer into is address and metadata components. /// /// The pointer can be later reconstructed with [`from_raw_parts_mut`]. - #[cfg(not(bootstrap))] #[unstable(feature = "ptr_metadata", issue = "81513")] #[rustc_const_unstable(feature = "ptr_metadata", issue = "81513")] #[inline] @@ -189,8 +188,7 @@ impl *mut T { /// Behavior: /// /// * Both the starting and resulting pointer must be either in bounds or one - /// byte past the end of the same allocated object. Note that in Rust, - /// every (stack-allocated) variable is considered a separate allocated object. + /// byte past the end of the same [allocated object]. /// /// * The computed offset, **in bytes**, cannot overflow an `isize`. /// @@ -215,6 +213,7 @@ impl *mut T { /// enables more aggressive compiler optimizations. /// /// [`wrapping_offset`]: #method.wrapping_offset + /// [allocated object]: crate::ptr#allocated-object /// /// # Examples /// @@ -232,7 +231,7 @@ impl *mut T { #[stable(feature = "rust1", since = "1.0.0")] #[must_use = "returns a new pointer rather than modifying its argument"] #[rustc_const_unstable(feature = "const_ptr_offset", issue = "71499")] - #[inline] + #[inline(always)] pub const unsafe fn offset(self, count: isize) -> *mut T where T: Sized, @@ -251,9 +250,8 @@ impl *mut T { /// /// This operation itself is always safe, but using the resulting pointer is not. /// - /// The resulting pointer remains attached to the same allocated object that `self` points to. - /// It may *not* be used to access a different allocated object. Note that in Rust, every - /// (stack-allocated) variable is considered a separate allocated object. + /// The resulting pointer "remembers" the [allocated object] that `self` points to; it may not + /// be used to read or write other allocated objects. /// /// In other words, `let z = x.wrapping_offset((y as isize) - (x as isize))` does *not* make `z` /// the same as `y` even if we assume `T` has size `1` and there is no overflow: `z` is still @@ -271,10 +269,8 @@ impl *mut T { /// `x.wrapping_offset(o).wrapping_offset(o.wrapping_neg())` is always the same as `x`. In other /// words, leaving the allocated object and then re-entering it later is permitted. /// - /// If you need to cross object boundaries, cast the pointer to an integer and - /// do the arithmetic there. - /// /// [`offset`]: #method.offset + /// [allocated object]: crate::ptr#allocated-object /// /// # Examples /// @@ -298,7 +294,7 @@ impl *mut T { #[stable(feature = "ptr_wrapping_offset", since = "1.16.0")] #[must_use = "returns a new pointer rather than modifying its argument"] #[rustc_const_unstable(feature = "const_ptr_offset", issue = "71499")] - #[inline] + #[inline(always)] pub const fn wrapping_offset(self, count: isize) -> *mut T where T: Sized, @@ -485,31 +481,37 @@ impl *mut T { /// Behavior: /// /// * Both the starting and other pointer must be either in bounds or one - /// byte past the end of the same allocated object. Note that in Rust, - /// every (stack-allocated) variable is considered a separate allocated object. + /// byte past the end of the same [allocated object]. /// /// * Both pointers must be *derived from* a pointer to the same object. /// (See below for an example.) /// - /// * The distance between the pointers, **in bytes**, cannot overflow an `isize`. - /// /// * The distance between the pointers, in bytes, must be an exact multiple /// of the size of `T`. /// + /// * The distance between the pointers, **in bytes**, cannot overflow an `isize`. + /// /// * The distance being in bounds cannot rely on "wrapping around" the address space. /// - /// The compiler and standard library generally try to ensure allocations - /// never reach a size where an offset is a concern. For instance, `Vec` - /// and `Box` ensure they never allocate more than `isize::MAX` bytes, so - /// `ptr_into_vec.offset_from(vec.as_ptr())` is always safe. + /// Rust types are never larger than `isize::MAX` and Rust allocations never wrap around the + /// address space, so two pointers within some value of any Rust type `T` will always satisfy + /// the last two conditions. The standard library also generally ensures that allocations + /// never reach a size where an offset is a concern. For instance, `Vec` and `Box` ensure they + /// never allocate more than `isize::MAX` bytes, so `ptr_into_vec.offset_from(vec.as_ptr())` + /// always satisfies the last two conditions. /// - /// Most platforms fundamentally can't even construct such an allocation. + /// Most platforms fundamentally can't even construct such a large allocation. /// For instance, no known 64-bit platform can ever serve a request /// for 263 bytes due to page-table limitations or splitting the address space. /// However, some 32-bit and 16-bit platforms may successfully serve a request for /// more than `isize::MAX` bytes with things like Physical Address /// Extension. As such, memory acquired directly from allocators or memory /// mapped files *may* be too large to handle with this function. + /// (Note that [`offset`] and [`add`] also have a similar limitation and hence cannot be used on + /// such large allocations either.) + /// + /// [`add`]: #method.add + /// [allocated object]: crate::ptr#allocated-object /// /// # Panics /// @@ -569,8 +571,7 @@ impl *mut T { /// Behavior: /// /// * Both the starting and resulting pointer must be either in bounds or one - /// byte past the end of the same allocated object. Note that in Rust, - /// every (stack-allocated) variable is considered a separate allocated object. + /// byte past the end of the same [allocated object]. /// /// * The computed offset, **in bytes**, cannot overflow an `isize`. /// @@ -612,7 +613,7 @@ impl *mut T { #[stable(feature = "pointer_methods", since = "1.26.0")] #[must_use = "returns a new pointer rather than modifying its argument"] #[rustc_const_unstable(feature = "const_ptr_offset", issue = "71499")] - #[inline] + #[inline(always)] pub const unsafe fn add(self, count: usize) -> Self where T: Sized, @@ -633,8 +634,7 @@ impl *mut T { /// Behavior: /// /// * Both the starting and resulting pointer must be either in bounds or one - /// byte past the end of the same allocated object. Note that in Rust, - /// every (stack-allocated) variable is considered a separate allocated object. + /// byte past the end of the same [allocated object]. /// /// * The computed offset cannot exceed `isize::MAX` **bytes**. /// @@ -659,6 +659,7 @@ impl *mut T { /// enables more aggressive compiler optimizations. /// /// [`wrapping_sub`]: #method.wrapping_sub + /// [allocated object]: crate::ptr#allocated-object /// /// # Examples /// @@ -695,9 +696,8 @@ impl *mut T { /// /// This operation itself is always safe, but using the resulting pointer is not. /// - /// The resulting pointer remains attached to the same allocated object that `self` points to. - /// It may *not* be used to access a different allocated object. Note that in Rust, every - /// (stack-allocated) variable is considered a separate allocated object. + /// The resulting pointer "remembers" the [allocated object] that `self` points to; it may not + /// be used to read or write other allocated objects. /// /// In other words, `let z = x.wrapping_add((y as usize) - (x as usize))` does *not* make `z` /// the same as `y` even if we assume `T` has size `1` and there is no overflow: `z` is still @@ -715,10 +715,8 @@ impl *mut T { /// `x.wrapping_add(o).wrapping_sub(o)` is always the same as `x`. In other words, leaving the /// allocated object and then re-entering it later is permitted. /// - /// If you need to cross object boundaries, cast the pointer to an integer and - /// do the arithmetic there. - /// /// [`add`]: #method.add + /// [allocated object]: crate::ptr#allocated-object /// /// # Examples /// @@ -742,7 +740,7 @@ impl *mut T { #[stable(feature = "pointer_methods", since = "1.26.0")] #[must_use = "returns a new pointer rather than modifying its argument"] #[rustc_const_unstable(feature = "const_ptr_offset", issue = "71499")] - #[inline] + #[inline(always)] pub const fn wrapping_add(self, count: usize) -> Self where T: Sized, @@ -760,9 +758,8 @@ impl *mut T { /// /// This operation itself is always safe, but using the resulting pointer is not. /// - /// The resulting pointer remains attached to the same allocated object that `self` points to. - /// It may *not* be used to access a different allocated object. Note that in Rust, every - /// (stack-allocated) variable is considered a separate allocated object. + /// The resulting pointer "remembers" the [allocated object] that `self` points to; it may not + /// be used to read or write other allocated objects. /// /// In other words, `let z = x.wrapping_sub((x as usize) - (y as usize))` does *not* make `z` /// the same as `y` even if we assume `T` has size `1` and there is no overflow: `z` is still @@ -780,10 +777,8 @@ impl *mut T { /// `x.wrapping_add(o).wrapping_sub(o)` is always the same as `x`. In other words, leaving the /// allocated object and then re-entering it later is permitted. /// - /// If you need to cross object boundaries, cast the pointer to an integer and - /// do the arithmetic there. - /// /// [`sub`]: #method.sub + /// [allocated object]: crate::ptr#allocated-object /// /// # Examples /// @@ -835,7 +830,7 @@ impl *mut T { /// #![feature(set_ptr_value)] /// # use core::fmt::Debug; /// let mut arr: [i32; 3] = [1, 2, 3]; - /// let mut ptr = &mut arr[0] as *mut dyn Debug; + /// let mut ptr = arr.as_mut_ptr() as *mut dyn Debug; /// let thin = ptr as *mut u8; /// unsafe { /// ptr = ptr.set_ptr_value(thin.add(8)); @@ -920,9 +915,10 @@ impl *mut T { /// See [`ptr::copy`] for safety concerns and examples. /// /// [`ptr::copy`]: crate::ptr::copy() + #[rustc_const_unstable(feature = "const_intrinsic_copy", issue = "80697")] #[stable(feature = "pointer_methods", since = "1.26.0")] #[inline] - pub unsafe fn copy_to(self, dest: *mut T, count: usize) + pub const unsafe fn copy_to(self, dest: *mut T, count: usize) where T: Sized, { @@ -938,9 +934,10 @@ impl *mut T { /// See [`ptr::copy_nonoverlapping`] for safety concerns and examples. /// /// [`ptr::copy_nonoverlapping`]: crate::ptr::copy_nonoverlapping() + #[rustc_const_unstable(feature = "const_intrinsic_copy", issue = "80697")] #[stable(feature = "pointer_methods", since = "1.26.0")] #[inline] - pub unsafe fn copy_to_nonoverlapping(self, dest: *mut T, count: usize) + pub const unsafe fn copy_to_nonoverlapping(self, dest: *mut T, count: usize) where T: Sized, { @@ -956,9 +953,10 @@ impl *mut T { /// See [`ptr::copy`] for safety concerns and examples. /// /// [`ptr::copy`]: crate::ptr::copy() + #[rustc_const_unstable(feature = "const_intrinsic_copy", issue = "80697")] #[stable(feature = "pointer_methods", since = "1.26.0")] #[inline] - pub unsafe fn copy_from(self, src: *const T, count: usize) + pub const unsafe fn copy_from(self, src: *const T, count: usize) where T: Sized, { @@ -974,9 +972,10 @@ impl *mut T { /// See [`ptr::copy_nonoverlapping`] for safety concerns and examples. /// /// [`ptr::copy_nonoverlapping`]: crate::ptr::copy_nonoverlapping() + #[rustc_const_unstable(feature = "const_intrinsic_copy", issue = "80697")] #[stable(feature = "pointer_methods", since = "1.26.0")] #[inline] - pub unsafe fn copy_from_nonoverlapping(self, src: *const T, count: usize) + pub const unsafe fn copy_from_nonoverlapping(self, src: *const T, count: usize) where T: Sized, { @@ -1175,13 +1174,6 @@ impl *mut [T] { #[unstable(feature = "slice_ptr_len", issue = "71146")] #[rustc_const_unstable(feature = "const_slice_ptr_len", issue = "71146")] pub const fn len(self) -> usize { - #[cfg(bootstrap)] - { - // SAFETY: this is safe because `*const [T]` and `FatPtr` have the same layout. - // Only `std` can make this guarantee. - unsafe { Repr { rust_mut: self }.raw }.len - } - #[cfg(not(bootstrap))] metadata(self) } @@ -1251,7 +1243,7 @@ impl *mut [T] { /// * The pointer must be [valid] for reads for `ptr.len() * mem::size_of::()` many bytes, /// and it must be properly aligned. This means in particular: /// - /// * The entire memory range of this slice must be contained within a single allocated object! + /// * The entire memory range of this slice must be contained within a single [allocated object]! /// Slices can never span across multiple allocated objects. /// /// * The pointer must be aligned even for zero-length slices. One @@ -1273,6 +1265,7 @@ impl *mut [T] { /// See also [`slice::from_raw_parts`][]. /// /// [valid]: crate::ptr#safety + /// [allocated object]: crate::ptr#allocated-object #[inline] #[unstable(feature = "ptr_as_uninit", issue = "75402")] pub unsafe fn as_uninit_slice<'a>(self) -> Option<&'a [MaybeUninit]> { @@ -1301,7 +1294,7 @@ impl *mut [T] { /// * The pointer must be [valid] for reads and writes for `ptr.len() * mem::size_of::()` /// many bytes, and it must be properly aligned. This means in particular: /// - /// * The entire memory range of this slice must be contained within a single allocated object! + /// * The entire memory range of this slice must be contained within a single [allocated object]! /// Slices can never span across multiple allocated objects. /// /// * The pointer must be aligned even for zero-length slices. One @@ -1323,6 +1316,7 @@ impl *mut [T] { /// See also [`slice::from_raw_parts_mut`][]. /// /// [valid]: crate::ptr#safety + /// [allocated object]: crate::ptr#allocated-object #[inline] #[unstable(feature = "ptr_as_uninit", issue = "75402")] pub unsafe fn as_uninit_slice_mut<'a>(self) -> Option<&'a mut [MaybeUninit]> { diff --git a/library/core/src/ptr/non_null.rs b/library/core/src/ptr/non_null.rs index 8d533cd6be117..e525f61604385 100644 --- a/library/core/src/ptr/non_null.rs +++ b/library/core/src/ptr/non_null.rs @@ -72,7 +72,7 @@ impl NonNull { /// sentinel value. Types that lazily allocate must track initialization by /// some other means. #[stable(feature = "nonnull", since = "1.25.0")] - #[rustc_const_stable(feature = "const_nonnull_dangling", since = "1.32.0")] + #[rustc_const_stable(feature = "const_nonnull_dangling", since = "1.36.0")] #[inline] pub const fn dangling() -> Self { // SAFETY: mem::align_of() returns a non-zero usize which is then casted @@ -110,7 +110,7 @@ impl NonNull { /// [the module documentation]: crate::ptr#safety #[inline] #[unstable(feature = "ptr_as_uninit", issue = "75402")] - pub unsafe fn as_uninit_ref(&self) -> &MaybeUninit { + pub unsafe fn as_uninit_ref<'a>(&self) -> &'a MaybeUninit { // SAFETY: the caller must guarantee that `self` meets all the // requirements for a reference. unsafe { &*self.cast().as_ptr() } @@ -142,7 +142,7 @@ impl NonNull { /// [the module documentation]: crate::ptr#safety #[inline] #[unstable(feature = "ptr_as_uninit", issue = "75402")] - pub unsafe fn as_uninit_mut(&mut self) -> &mut MaybeUninit { + pub unsafe fn as_uninit_mut<'a>(&mut self) -> &'a mut MaybeUninit { // SAFETY: the caller must guarantee that `self` meets all the // requirements for a reference. unsafe { &mut *self.cast().as_ptr() } @@ -156,7 +156,7 @@ impl NonNull { /// /// `ptr` must be non-null. #[stable(feature = "nonnull", since = "1.25.0")] - #[rustc_const_stable(feature = "const_nonnull_new_unchecked", since = "1.32.0")] + #[rustc_const_stable(feature = "const_nonnull_new_unchecked", since = "1.25.0")] #[inline] pub const unsafe fn new_unchecked(ptr: *mut T) -> Self { // SAFETY: the caller must guarantee that `ptr` is non-null. @@ -181,7 +181,6 @@ impl NonNull { /// See the documentation of [`std::ptr::from_raw_parts`] for more details. /// /// [`std::ptr::from_raw_parts`]: crate::ptr::from_raw_parts - #[cfg(not(bootstrap))] #[unstable(feature = "ptr_metadata", issue = "81513")] #[rustc_const_unstable(feature = "ptr_metadata", issue = "81513")] #[inline] @@ -198,7 +197,6 @@ impl NonNull { /// Decompose a (possibly wide) pointer into is address and metadata components. /// /// The pointer can be later reconstructed with [`NonNull::from_raw_parts`]. - #[cfg(not(bootstrap))] #[unstable(feature = "ptr_metadata", issue = "81513")] #[rustc_const_unstable(feature = "ptr_metadata", issue = "81513")] #[inline] @@ -244,7 +242,7 @@ impl NonNull { /// [the module documentation]: crate::ptr#safety #[stable(feature = "nonnull", since = "1.25.0")] #[inline] - pub unsafe fn as_ref(&self) -> &T { + pub unsafe fn as_ref<'a>(&self) -> &'a T { // SAFETY: the caller must guarantee that `self` meets all the // requirements for a reference. unsafe { &*self.as_ptr() } @@ -280,7 +278,7 @@ impl NonNull { /// [the module documentation]: crate::ptr#safety #[stable(feature = "nonnull", since = "1.25.0")] #[inline] - pub unsafe fn as_mut(&mut self) -> &mut T { + pub unsafe fn as_mut<'a>(&mut self) -> &'a mut T { // SAFETY: the caller must guarantee that `self` meets all the // requirements for a mutable reference. unsafe { &mut *self.as_ptr() } @@ -288,7 +286,7 @@ impl NonNull { /// Casts to a pointer of another type. #[stable(feature = "nonnull_cast", since = "1.27.0")] - #[rustc_const_stable(feature = "const_nonnull_cast", since = "1.32.0")] + #[rustc_const_stable(feature = "const_nonnull_cast", since = "1.36.0")] #[inline] pub const fn cast(self) -> NonNull { // SAFETY: `self` is a `NonNull` pointer which is necessarily non-null @@ -427,7 +425,7 @@ impl NonNull<[T]> { /// [valid]: crate::ptr#safety #[inline] #[unstable(feature = "ptr_as_uninit", issue = "75402")] - pub unsafe fn as_uninit_slice(&self) -> &[MaybeUninit] { + pub unsafe fn as_uninit_slice<'a>(&self) -> &'a [MaybeUninit] { // SAFETY: the caller must uphold the safety contract for `as_uninit_slice`. unsafe { slice::from_raw_parts(self.cast().as_ptr(), self.len()) } } @@ -488,7 +486,7 @@ impl NonNull<[T]> { /// ``` #[inline] #[unstable(feature = "ptr_as_uninit", issue = "75402")] - pub unsafe fn as_uninit_slice_mut(&self) -> &mut [MaybeUninit] { + pub unsafe fn as_uninit_slice_mut<'a>(&self) -> &'a mut [MaybeUninit] { // SAFETY: the caller must uphold the safety contract for `as_uninit_slice_mut`. unsafe { slice::from_raw_parts_mut(self.cast().as_ptr(), self.len()) } } diff --git a/library/core/src/raw.rs b/library/core/src/raw.rs index 1227d9b01f011..6d1e28f4cd7d7 100644 --- a/library/core/src/raw.rs +++ b/library/core/src/raw.rs @@ -1,5 +1,9 @@ #![allow(missing_docs)] #![unstable(feature = "raw", issue = "27751")] +#![rustc_deprecated( + since = "1.53.0", + reason = "use pointer metadata APIs instead https://github.com/rust-lang/rust/issues/81513" +)] //! Contains struct definitions for the layout of compiler built-in types. //! diff --git a/library/core/src/result.rs b/library/core/src/result.rs index 7c48b8e41c621..20f8095b7d1ce 100644 --- a/library/core/src/result.rs +++ b/library/core/src/result.rs @@ -1167,6 +1167,42 @@ impl> Result { } } +#[unstable(feature = "unwrap_infallible", reason = "newly added", issue = "61695")] +impl, E> Result { + /// Returns the contained [`Err`] value, but never panics. + /// + /// Unlike [`unwrap_err`], this method is known to never panic on the + /// result types it is implemented for. Therefore, it can be used + /// instead of `unwrap_err` as a maintainability safeguard that will fail + /// to compile if the ok type of the `Result` is later changed + /// to a type that can actually occur. + /// + /// [`unwrap_err`]: Result::unwrap_err + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// # #![feature(never_type)] + /// # #![feature(unwrap_infallible)] + /// + /// fn only_bad_news() -> Result { + /// Err("Oops, it failed".into()) + /// } + /// + /// let error: String = only_bad_news().into_err(); + /// println!("{}", error); + /// ``` + #[inline] + pub fn into_err(self) -> E { + match self { + Ok(x) => x.into(), + Err(e) => e, + } + } +} + impl Result { /// Converts from `Result` (or `&Result`) to `Result<&::Target, &E>`. /// @@ -1233,7 +1269,8 @@ impl Result, E> { /// ``` #[inline] #[stable(feature = "transpose_result", since = "1.33.0")] - pub fn transpose(self) -> Option> { + #[rustc_const_unstable(feature = "const_result", issue = "82814")] + pub const fn transpose(self) -> Option> { match self { Ok(Some(x)) => Some(Ok(x)), Ok(None) => None, diff --git a/library/core/src/slice/ascii.rs b/library/core/src/slice/ascii.rs index 42032bc9035bc..906dcb1e8bcee 100644 --- a/library/core/src/slice/ascii.rs +++ b/library/core/src/slice/ascii.rs @@ -1,6 +1,10 @@ //! Operations on ASCII `[u8]`. +use crate::ascii; +use crate::fmt::{self, Write}; +use crate::iter; use crate::mem; +use crate::ops; #[lang = "slice_u8"] #[cfg(not(test))] @@ -19,7 +23,7 @@ impl [u8] { #[stable(feature = "ascii_methods_on_intrinsics", since = "1.23.0")] #[inline] pub fn eq_ignore_ascii_case(&self, other: &[u8]) -> bool { - self.len() == other.len() && self.iter().zip(other).all(|(a, b)| a.eq_ignore_ascii_case(b)) + self.len() == other.len() && iter::zip(self, other).all(|(a, b)| a.eq_ignore_ascii_case(b)) } /// Converts this slice to its ASCII upper case equivalent in-place. @@ -55,6 +59,95 @@ impl [u8] { byte.make_ascii_lowercase(); } } + + /// Returns an iterator that produces an escaped version of this slice, + /// treating it as an ASCII string. + /// + /// # Examples + /// + /// ``` + /// #![feature(inherent_ascii_escape)] + /// + /// let s = b"0\t\r\n'\"\\\x9d"; + /// let escaped = s.escape_ascii().to_string(); + /// assert_eq!(escaped, "0\\t\\r\\n\\'\\\"\\\\\\x9d"); + /// ``` + #[unstable(feature = "inherent_ascii_escape", issue = "77174")] + pub fn escape_ascii(&self) -> EscapeAscii<'_> { + EscapeAscii { inner: self.iter().flat_map(EscapeByte) } + } +} + +impl_fn_for_zst! { + #[derive(Clone)] + struct EscapeByte impl Fn = |byte: &u8| -> ascii::EscapeDefault { + ascii::escape_default(*byte) + }; +} + +/// An iterator over the escaped version of a byte slice. +/// +/// This `struct` is created by the [`slice::escape_ascii`] method. See its +/// documentation for more information. +#[unstable(feature = "inherent_ascii_escape", issue = "77174")] +#[derive(Clone)] +pub struct EscapeAscii<'a> { + inner: iter::FlatMap, ascii::EscapeDefault, EscapeByte>, +} + +#[unstable(feature = "inherent_ascii_escape", issue = "77174")] +impl<'a> iter::Iterator for EscapeAscii<'a> { + type Item = u8; + #[inline] + fn next(&mut self) -> Option { + self.inner.next() + } + #[inline] + fn size_hint(&self) -> (usize, Option) { + self.inner.size_hint() + } + #[inline] + fn try_fold(&mut self, init: Acc, fold: Fold) -> R + where + Fold: FnMut(Acc, Self::Item) -> R, + R: ops::Try, + { + self.inner.try_fold(init, fold) + } + #[inline] + fn fold(self, init: Acc, fold: Fold) -> Acc + where + Fold: FnMut(Acc, Self::Item) -> Acc, + { + self.inner.fold(init, fold) + } + #[inline] + fn last(mut self) -> Option { + self.next_back() + } +} + +#[unstable(feature = "inherent_ascii_escape", issue = "77174")] +impl<'a> iter::DoubleEndedIterator for EscapeAscii<'a> { + fn next_back(&mut self) -> Option { + self.inner.next_back() + } +} +#[unstable(feature = "inherent_ascii_escape", issue = "77174")] +impl<'a> iter::ExactSizeIterator for EscapeAscii<'a> {} +#[unstable(feature = "inherent_ascii_escape", issue = "77174")] +impl<'a> iter::FusedIterator for EscapeAscii<'a> {} +#[unstable(feature = "inherent_ascii_escape", issue = "77174")] +impl<'a> fmt::Display for EscapeAscii<'a> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.clone().try_for_each(|b| f.write_char(b as char)) + } +} +#[unstable(feature = "inherent_ascii_escape", issue = "77174")] +impl<'a> fmt::Debug for EscapeAscii<'a> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("EscapeAscii").finish_non_exhaustive() + } } /// Returns `true` if any byte in the word `v` is nonascii (>= 128). Snarfed diff --git a/library/core/src/slice/index.rs b/library/core/src/slice/index.rs index c92b37b14be4f..f722430354991 100644 --- a/library/core/src/slice/index.rs +++ b/library/core/src/slice/index.rs @@ -81,6 +81,8 @@ mod private_slice_index { impl Sealed for ops::RangeInclusive {} #[stable(feature = "slice_get_slice", since = "1.28.0")] impl Sealed for ops::RangeToInclusive {} + #[stable(feature = "slice_index_with_ops_bound_pair", since = "1.53.0")] + impl Sealed for (ops::Bound, ops::Bound) {} } /// A helper trait used for indexing operations. @@ -546,3 +548,113 @@ where ops::Range { start, end } } + +/// Convert pair of `ops::Bound`s into `ops::Range` without performing any bounds checking and (in debug) overflow checking +fn into_range_unchecked( + len: usize, + (start, end): (ops::Bound, ops::Bound), +) -> ops::Range { + use ops::Bound; + let start = match start { + Bound::Included(i) => i, + Bound::Excluded(i) => i + 1, + Bound::Unbounded => 0, + }; + let end = match end { + Bound::Included(i) => i + 1, + Bound::Excluded(i) => i, + Bound::Unbounded => len, + }; + start..end +} + +/// Convert pair of `ops::Bound`s into `ops::Range`. +/// Returns `None` on overflowing indices. +fn into_range( + len: usize, + (start, end): (ops::Bound, ops::Bound), +) -> Option> { + use ops::Bound; + let start = match start { + Bound::Included(start) => start, + Bound::Excluded(start) => start.checked_add(1)?, + Bound::Unbounded => 0, + }; + + let end = match end { + Bound::Included(end) => end.checked_add(1)?, + Bound::Excluded(end) => end, + Bound::Unbounded => len, + }; + + // Don't bother with checking `start < end` and `end <= len` + // since these checks are handled by `Range` impls + + Some(start..end) +} + +/// Convert pair of `ops::Bound`s into `ops::Range`. +/// Panics on overflowing indices. +fn into_slice_range( + len: usize, + (start, end): (ops::Bound, ops::Bound), +) -> ops::Range { + use ops::Bound; + let start = match start { + Bound::Included(start) => start, + Bound::Excluded(start) => { + start.checked_add(1).unwrap_or_else(|| slice_start_index_overflow_fail()) + } + Bound::Unbounded => 0, + }; + + let end = match end { + Bound::Included(end) => { + end.checked_add(1).unwrap_or_else(|| slice_end_index_overflow_fail()) + } + Bound::Excluded(end) => end, + Bound::Unbounded => len, + }; + + // Don't bother with checking `start < end` and `end <= len` + // since these checks are handled by `Range` impls + + start..end +} + +#[stable(feature = "slice_index_with_ops_bound_pair", since = "1.53.0")] +unsafe impl SliceIndex<[T]> for (ops::Bound, ops::Bound) { + type Output = [T]; + + #[inline] + fn get(self, slice: &[T]) -> Option<&Self::Output> { + into_range(slice.len(), self)?.get(slice) + } + + #[inline] + fn get_mut(self, slice: &mut [T]) -> Option<&mut Self::Output> { + into_range(slice.len(), self)?.get_mut(slice) + } + + #[inline] + unsafe fn get_unchecked(self, slice: *const [T]) -> *const Self::Output { + // SAFETY: the caller has to uphold the safety contract for `get_unchecked`. + unsafe { into_range_unchecked(slice.len(), self).get_unchecked(slice) } + } + + #[inline] + unsafe fn get_unchecked_mut(self, slice: *mut [T]) -> *mut Self::Output { + // SAFETY: the caller has to uphold the safety contract for `get_unchecked_mut`. + unsafe { into_range_unchecked(slice.len(), self).get_unchecked_mut(slice) } + } + + #[inline] + fn index(self, slice: &[T]) -> &Self::Output { + into_slice_range(slice.len(), self).index(slice) + } + + #[inline] + fn index_mut(self, slice: &mut [T]) -> &mut Self::Output { + into_slice_range(slice.len(), self).index_mut(slice) + } +} diff --git a/library/core/src/slice/iter.rs b/library/core/src/slice/iter.rs index 796301c76b608..1ee662c6c8e3c 100644 --- a/library/core/src/slice/iter.rs +++ b/library/core/src/slice/iter.rs @@ -286,7 +286,6 @@ impl<'a, T> IterMut<'a, T> { /// Basic usage: /// /// ``` - /// # #![feature(slice_iter_mut_as_slice)] /// let mut slice: &mut [usize] = &mut [1, 2, 3]; /// /// // First, we get the iterator: @@ -299,12 +298,19 @@ impl<'a, T> IterMut<'a, T> { /// // Now `as_slice` returns "[2, 3]": /// assert_eq!(iter.as_slice(), &[2, 3]); /// ``` - #[unstable(feature = "slice_iter_mut_as_slice", reason = "recently added", issue = "58957")] + #[stable(feature = "slice_iter_mut_as_slice", since = "1.53.0")] pub fn as_slice(&self) -> &[T] { self.make_slice() } } +#[stable(feature = "slice_iter_mut_as_slice", since = "1.53.0")] +impl AsRef<[T]> for IterMut<'_, T> { + fn as_ref(&self) -> &[T] { + self.as_slice() + } +} + iterator! {struct IterMut -> *mut T, &'a mut T, mut, {mut}, {}} /// An internal abstraction over the splitting iterators, so that @@ -335,9 +341,11 @@ pub struct Split<'a, T: 'a, P> where P: FnMut(&T) -> bool, { - v: &'a [T], + // Used for `SplitWhitespace` and `SplitAsciiWhitespace` `as_str` methods + pub(crate) v: &'a [T], pred: P, - finished: bool, + // Used for `SplitAsciiWhitespace` `as_str` method + pub(crate) finished: bool, } impl<'a, T: 'a, P: FnMut(&T) -> bool> Split<'a, T, P> { diff --git a/library/core/src/slice/mod.rs b/library/core/src/slice/mod.rs index b2c5c292f450e..ec28cdd1ba0d9 100644 --- a/library/core/src/slice/mod.rs +++ b/library/core/src/slice/mod.rs @@ -8,7 +8,7 @@ #![stable(feature = "rust1", since = "1.0.0")] -use crate::cmp::Ordering::{self, Equal, Greater, Less}; +use crate::cmp::Ordering::{self, Greater, Less}; use crate::marker::Copy; use crate::mem; use crate::num::NonZeroUsize; @@ -81,6 +81,9 @@ pub use index::SliceIndex; #[unstable(feature = "slice_range", issue = "76393")] pub use index::range; +#[unstable(feature = "inherent_ascii_escape", issue = "77174")] +pub use ascii::EscapeAscii; + #[lang = "slice"] #[cfg(not(test))] impl [T] { @@ -94,28 +97,19 @@ impl [T] { /// ``` #[doc(alias = "length")] #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_stable(feature = "const_slice_len", since = "1.32.0")] + #[rustc_const_stable(feature = "const_slice_len", since = "1.39.0")] #[inline] // SAFETY: const sound because we transmute out the length field as a usize (which it must be) #[rustc_allow_const_fn_unstable(const_fn_union)] pub const fn len(&self) -> usize { - #[cfg(bootstrap)] - { - // SAFETY: this is safe because `&[T]` and `FatPtr` have the same layout. - // Only `std` can make this guarantee. - unsafe { crate::ptr::Repr { rust: self }.raw.len } - } - #[cfg(not(bootstrap))] - { - // FIXME: Replace with `crate::ptr::metadata(self)` when that is const-stable. - // As of this writing this causes a "Const-stable functions can only call other - // const-stable functions" error. - - // SAFETY: Accessing the value from the `PtrRepr` union is safe since *const T - // and PtrComponents have the same memory layouts. Only std can make this - // guarantee. - unsafe { crate::ptr::PtrRepr { const_ptr: self }.components.metadata } - } + // FIXME: Replace with `crate::ptr::metadata(self)` when that is const-stable. + // As of this writing this causes a "Const-stable functions can only call other + // const-stable functions" error. + + // SAFETY: Accessing the value from the `PtrRepr` union is safe since *const T + // and PtrComponents have the same memory layouts. Only std can make this + // guarantee. + unsafe { crate::ptr::PtrRepr { const_ptr: self }.components.metadata } } /// Returns `true` if the slice has a length of 0. @@ -127,7 +121,7 @@ impl [T] { /// assert!(!a.is_empty()); /// ``` #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_stable(feature = "const_slice_is_empty", since = "1.32.0")] + #[rustc_const_stable(feature = "const_slice_is_empty", since = "1.39.0")] #[inline] pub const fn is_empty(&self) -> bool { self.len() == 0 @@ -145,8 +139,9 @@ impl [T] { /// assert_eq!(None, w.first()); /// ``` #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_const_unstable(feature = "const_slice_first_last", issue = "83570")] #[inline] - pub fn first(&self) -> Option<&T> { + pub const fn first(&self) -> Option<&T> { if let [first, ..] = self { Some(first) } else { None } } @@ -163,8 +158,9 @@ impl [T] { /// assert_eq!(x, &[5, 1, 2]); /// ``` #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_const_unstable(feature = "const_slice_first_last", issue = "83570")] #[inline] - pub fn first_mut(&mut self) -> Option<&mut T> { + pub const fn first_mut(&mut self) -> Option<&mut T> { if let [first, ..] = self { Some(first) } else { None } } @@ -181,8 +177,9 @@ impl [T] { /// } /// ``` #[stable(feature = "slice_splits", since = "1.5.0")] + #[rustc_const_unstable(feature = "const_slice_first_last", issue = "83570")] #[inline] - pub fn split_first(&self) -> Option<(&T, &[T])> { + pub const fn split_first(&self) -> Option<(&T, &[T])> { if let [first, tail @ ..] = self { Some((first, tail)) } else { None } } @@ -201,8 +198,9 @@ impl [T] { /// assert_eq!(x, &[3, 4, 5]); /// ``` #[stable(feature = "slice_splits", since = "1.5.0")] + #[rustc_const_unstable(feature = "const_slice_first_last", issue = "83570")] #[inline] - pub fn split_first_mut(&mut self) -> Option<(&mut T, &mut [T])> { + pub const fn split_first_mut(&mut self) -> Option<(&mut T, &mut [T])> { if let [first, tail @ ..] = self { Some((first, tail)) } else { None } } @@ -219,8 +217,9 @@ impl [T] { /// } /// ``` #[stable(feature = "slice_splits", since = "1.5.0")] + #[rustc_const_unstable(feature = "const_slice_first_last", issue = "83570")] #[inline] - pub fn split_last(&self) -> Option<(&T, &[T])> { + pub const fn split_last(&self) -> Option<(&T, &[T])> { if let [init @ .., last] = self { Some((last, init)) } else { None } } @@ -239,8 +238,9 @@ impl [T] { /// assert_eq!(x, &[4, 5, 3]); /// ``` #[stable(feature = "slice_splits", since = "1.5.0")] + #[rustc_const_unstable(feature = "const_slice_first_last", issue = "83570")] #[inline] - pub fn split_last_mut(&mut self) -> Option<(&mut T, &mut [T])> { + pub const fn split_last_mut(&mut self) -> Option<(&mut T, &mut [T])> { if let [init @ .., last] = self { Some((last, init)) } else { None } } @@ -256,8 +256,9 @@ impl [T] { /// assert_eq!(None, w.last()); /// ``` #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_const_unstable(feature = "const_slice_first_last", issue = "83570")] #[inline] - pub fn last(&self) -> Option<&T> { + pub const fn last(&self) -> Option<&T> { if let [.., last] = self { Some(last) } else { None } } @@ -274,8 +275,9 @@ impl [T] { /// assert_eq!(x, &[0, 1, 10]); /// ``` #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_const_unstable(feature = "const_slice_first_last", issue = "83570")] #[inline] - pub fn last_mut(&mut self) -> Option<&mut T> { + pub const fn last_mut(&mut self) -> Option<&mut T> { if let [.., last] = self { Some(last) } else { None } } @@ -308,7 +310,7 @@ impl [T] { /// Returns a mutable reference to an element or subslice depending on the /// type of index (see [`get`]) or `None` if the index is out of bounds. /// - /// [`get`]: #method.get + /// [`get`]: slice::get /// /// # Examples /// @@ -339,7 +341,7 @@ impl [T] { /// Calling this method with an out-of-bounds index is *[undefined behavior]* /// even if the resulting reference is not used. /// - /// [`get`]: #method.get + /// [`get`]: slice::get /// [undefined behavior]: https://doc.rust-lang.org/reference/behavior-considered-undefined.html /// /// # Examples @@ -373,7 +375,7 @@ impl [T] { /// Calling this method with an out-of-bounds index is *[undefined behavior]* /// even if the resulting reference is not used. /// - /// [`get_mut`]: #method.get_mut + /// [`get_mut`]: slice::get_mut /// [undefined behavior]: https://doc.rust-lang.org/reference/behavior-considered-undefined.html /// /// # Examples @@ -424,7 +426,7 @@ impl [T] { /// } /// ``` /// - /// [`as_mut_ptr`]: #method.as_mut_ptr + /// [`as_mut_ptr`]: slice::as_mut_ptr #[stable(feature = "rust1", since = "1.0.0")] #[rustc_const_stable(feature = "const_slice_as_ptr", since = "1.32.0")] #[inline] @@ -487,7 +489,7 @@ impl [T] { /// assert!(!a.as_ptr_range().contains(&y)); /// ``` /// - /// [`as_ptr`]: #method.as_ptr + /// [`as_ptr`]: slice::as_ptr #[stable(feature = "slice_ptr_range", since = "1.48.0")] #[rustc_const_unstable(feature = "const_ptr_offset", issue = "71499")] #[inline] @@ -529,7 +531,7 @@ impl [T] { /// use two pointers to refer to a range of elements in memory, as is /// common in C++. /// - /// [`as_mut_ptr`]: #method.as_mut_ptr + /// [`as_mut_ptr`]: slice::as_mut_ptr #[stable(feature = "slice_ptr_range", since = "1.48.0")] #[rustc_const_unstable(feature = "const_ptr_offset", issue = "71499")] #[inline] @@ -780,8 +782,8 @@ impl [T] { /// assert!(iter.next().is_none()); /// ``` /// - /// [`chunks_exact`]: #method.chunks_exact - /// [`rchunks`]: #method.rchunks + /// [`chunks_exact`]: slice::chunks_exact + /// [`rchunks`]: slice::rchunks #[stable(feature = "rust1", since = "1.0.0")] #[inline] pub fn chunks(&self, chunk_size: usize) -> Chunks<'_, T> { @@ -818,8 +820,8 @@ impl [T] { /// assert_eq!(v, &[1, 1, 2, 2, 3]); /// ``` /// - /// [`chunks_exact_mut`]: #method.chunks_exact_mut - /// [`rchunks_mut`]: #method.rchunks_mut + /// [`chunks_exact_mut`]: slice::chunks_exact_mut + /// [`rchunks_mut`]: slice::rchunks_mut #[stable(feature = "rust1", since = "1.0.0")] #[inline] pub fn chunks_mut(&mut self, chunk_size: usize) -> ChunksMut<'_, T> { @@ -855,8 +857,8 @@ impl [T] { /// assert_eq!(iter.remainder(), &['m']); /// ``` /// - /// [`chunks`]: #method.chunks - /// [`rchunks_exact`]: #method.rchunks_exact + /// [`chunks`]: slice::chunks + /// [`rchunks_exact`]: slice::rchunks_exact #[stable(feature = "chunks_exact", since = "1.31.0")] #[inline] pub fn chunks_exact(&self, chunk_size: usize) -> ChunksExact<'_, T> { @@ -897,8 +899,8 @@ impl [T] { /// assert_eq!(v, &[1, 1, 2, 2, 0]); /// ``` /// - /// [`chunks_mut`]: #method.chunks_mut - /// [`rchunks_exact_mut`]: #method.rchunks_exact_mut + /// [`chunks_mut`]: slice::chunks_mut + /// [`rchunks_exact_mut`]: slice::rchunks_exact_mut #[stable(feature = "chunks_exact", since = "1.31.0")] #[inline] pub fn chunks_exact_mut(&mut self, chunk_size: usize) -> ChunksExactMut<'_, T> { @@ -1032,7 +1034,7 @@ impl [T] { /// assert_eq!(iter.remainder(), &['m']); /// ``` /// - /// [`chunks_exact`]: #method.chunks_exact + /// [`chunks_exact`]: slice::chunks_exact #[unstable(feature = "array_chunks", issue = "74985")] #[inline] pub fn array_chunks(&self) -> ArrayChunks<'_, T, N> { @@ -1182,7 +1184,7 @@ impl [T] { /// assert_eq!(v, &[1, 1, 2, 2, 0]); /// ``` /// - /// [`chunks_exact_mut`]: #method.chunks_exact_mut + /// [`chunks_exact_mut`]: slice::chunks_exact_mut #[unstable(feature = "array_chunks", issue = "74985")] #[inline] pub fn array_chunks_mut(&mut self) -> ArrayChunksMut<'_, T, N> { @@ -1214,7 +1216,7 @@ impl [T] { /// assert!(iter.next().is_none()); /// ``` /// - /// [`windows`]: #method.windows + /// [`windows`]: slice::windows #[unstable(feature = "array_windows", issue = "75027")] #[inline] pub fn array_windows(&self) -> ArrayWindows<'_, T, N> { @@ -1247,8 +1249,8 @@ impl [T] { /// assert!(iter.next().is_none()); /// ``` /// - /// [`rchunks_exact`]: #method.rchunks_exact - /// [`chunks`]: #method.chunks + /// [`rchunks_exact`]: slice::rchunks_exact + /// [`chunks`]: slice::chunks #[stable(feature = "rchunks", since = "1.31.0")] #[inline] pub fn rchunks(&self, chunk_size: usize) -> RChunks<'_, T> { @@ -1285,8 +1287,8 @@ impl [T] { /// assert_eq!(v, &[3, 2, 2, 1, 1]); /// ``` /// - /// [`rchunks_exact_mut`]: #method.rchunks_exact_mut - /// [`chunks_mut`]: #method.chunks_mut + /// [`rchunks_exact_mut`]: slice::rchunks_exact_mut + /// [`chunks_mut`]: slice::chunks_mut #[stable(feature = "rchunks", since = "1.31.0")] #[inline] pub fn rchunks_mut(&mut self, chunk_size: usize) -> RChunksMut<'_, T> { @@ -1323,9 +1325,9 @@ impl [T] { /// assert_eq!(iter.remainder(), &['l']); /// ``` /// - /// [`chunks`]: #method.chunks - /// [`rchunks`]: #method.rchunks - /// [`chunks_exact`]: #method.chunks_exact + /// [`chunks`]: slice::chunks + /// [`rchunks`]: slice::rchunks + /// [`chunks_exact`]: slice::chunks_exact #[stable(feature = "rchunks", since = "1.31.0")] #[inline] pub fn rchunks_exact(&self, chunk_size: usize) -> RChunksExact<'_, T> { @@ -1366,9 +1368,9 @@ impl [T] { /// assert_eq!(v, &[0, 2, 2, 1, 1]); /// ``` /// - /// [`chunks_mut`]: #method.chunks_mut - /// [`rchunks_mut`]: #method.rchunks_mut - /// [`chunks_exact_mut`]: #method.chunks_exact_mut + /// [`chunks_mut`]: slice::chunks_mut + /// [`rchunks_mut`]: slice::rchunks_mut + /// [`chunks_exact_mut`]: slice::chunks_exact_mut #[stable(feature = "rchunks", since = "1.31.0")] #[inline] pub fn rchunks_exact_mut(&mut self, chunk_size: usize) -> RChunksExactMut<'_, T> { @@ -1552,7 +1554,7 @@ impl [T] { /// even if the resulting reference is not used. The caller has to ensure that /// `0 <= mid <= self.len()`. /// - /// [`split_at`]: #method.split_at + /// [`split_at`]: slice::split_at /// [undefined behavior]: https://doc.rust-lang.org/reference/behavior-considered-undefined.html /// /// # Examples @@ -1601,7 +1603,7 @@ impl [T] { /// even if the resulting reference is not used. The caller has to ensure that /// `0 <= mid <= self.len()`. /// - /// [`split_at_mut`]: #method.split_at_mut + /// [`split_at_mut`]: slice::split_at_mut /// [undefined behavior]: https://doc.rust-lang.org/reference/behavior-considered-undefined.html /// /// # Examples @@ -2103,9 +2105,9 @@ impl [T] { /// /// See also [`binary_search_by`], [`binary_search_by_key`], and [`partition_point`]. /// - /// [`binary_search_by`]: #method.binary_search_by - /// [`binary_search_by_key`]: #method.binary_search_by_key - /// [`partition_point`]: #method.partition_point + /// [`binary_search_by`]: slice::binary_search_by + /// [`binary_search_by_key`]: slice::binary_search_by_key + /// [`partition_point`]: slice::partition_point /// /// # Examples /// @@ -2156,9 +2158,9 @@ impl [T] { /// /// See also [`binary_search`], [`binary_search_by_key`], and [`partition_point`]. /// - /// [`binary_search`]: #method.binary_search - /// [`binary_search_by_key`]: #method.binary_search_by_key - /// [`partition_point`]: #method.partition_point + /// [`binary_search`]: slice::binary_search + /// [`binary_search_by_key`]: slice::binary_search_by_key + /// [`partition_point`]: slice::partition_point /// /// # Examples /// @@ -2185,25 +2187,33 @@ impl [T] { where F: FnMut(&'a T) -> Ordering, { - let s = self; - let mut size = s.len(); - if size == 0 { - return Err(0); - } - let mut base = 0usize; - while size > 1 { - let half = size / 2; - let mid = base + half; - // SAFETY: the call is made safe by the following inconstants: - // - `mid >= 0`: by definition - // - `mid < size`: `mid = size / 2 + size / 4 + size / 8 ...` - let cmp = f(unsafe { s.get_unchecked(mid) }); - base = if cmp == Greater { base } else { mid }; - size -= half; + let mut size = self.len(); + let mut left = 0; + let mut right = size; + while left < right { + let mid = left + size / 2; + + // SAFETY: the call is made safe by the following invariants: + // - `mid >= 0` + // - `mid < size`: `mid` is limited by `[left; right)` bound. + let cmp = f(unsafe { self.get_unchecked(mid) }); + + // The reason why we use if/else control flow rather than match + // is because match reorders comparison operations, which is perf sensitive. + // This is x86 asm for u8: https://rust.godbolt.org/z/8Y8Pra. + if cmp == Less { + left = mid + 1; + } else if cmp == Greater { + right = mid; + } else { + // SAFETY: same as the `get_unchecked` above + unsafe { crate::intrinsics::assume(mid < self.len()) }; + return Ok(mid); + } + + size = right - left; } - // SAFETY: base is always in [0, size) because base <= mid. - let cmp = f(unsafe { s.get_unchecked(base) }); - if cmp == Equal { Ok(base) } else { Err(base + (cmp == Less) as usize) } + Err(left) } /// Binary searches this sorted slice with a key extraction function. @@ -2219,10 +2229,10 @@ impl [T] { /// /// See also [`binary_search`], [`binary_search_by`], and [`partition_point`]. /// - /// [`sort_by_key`]: #method.sort_by_key - /// [`binary_search`]: #method.binary_search - /// [`binary_search_by`]: #method.binary_search_by - /// [`partition_point`]: #method.partition_point + /// [`sort_by_key`]: slice::sort_by_key + /// [`binary_search`]: slice::binary_search + /// [`binary_search_by`]: slice::binary_search_by + /// [`partition_point`]: slice::partition_point /// /// # Examples /// @@ -2242,6 +2252,11 @@ impl [T] { /// let r = s.binary_search_by_key(&1, |&(a, b)| b); /// assert!(match r { Ok(1..=4) => true, _ => false, }); /// ``` + // Lint rustdoc::broken_intra_doc_links is allowed as `slice::sort_by_key` is + // in crate `alloc`, and as such doesn't exists yet when building `core`. + // links to downstream crate: #74481. Since primitives are only documented in + // libstd (#73423), this never leads to broken links in practice. + #[allow(rustdoc::broken_intra_doc_links)] #[stable(feature = "slice_binary_search_by_key", since = "1.10.0")] #[inline] pub fn binary_search_by_key<'a, B, F>(&'a self, b: &B, mut f: F) -> Result @@ -2440,7 +2455,7 @@ impl [T] { /// The current algorithm is based on the quickselect portion of the same quicksort algorithm /// used for [`sort_unstable`]. /// - /// [`sort_unstable`]: #method.sort_unstable + /// [`sort_unstable`]: slice::sort_unstable /// /// # Panics /// @@ -2488,7 +2503,7 @@ impl [T] { /// The current algorithm is based on the quickselect portion of the same quicksort algorithm /// used for [`sort_unstable`]. /// - /// [`sort_unstable`]: #method.sort_unstable + /// [`sort_unstable`]: slice::sort_unstable /// /// # Panics /// @@ -2540,7 +2555,7 @@ impl [T] { /// The current algorithm is based on the quickselect portion of the same quicksort algorithm /// used for [`sort_unstable`]. /// - /// [`sort_unstable`]: #method.sort_unstable + /// [`sort_unstable`]: slice::sort_unstable /// /// # Panics /// @@ -2877,7 +2892,7 @@ impl [T] { /// trait to generate values, you can pass [`Default::default`] as the /// argument. /// - /// [`fill`]: #method.fill + /// [`fill`]: slice::fill /// /// # Examples /// @@ -2950,8 +2965,8 @@ impl [T] { /// assert_eq!(slice, [4, 5, 3, 4, 5]); /// ``` /// - /// [`copy_from_slice`]: #method.copy_from_slice - /// [`split_at_mut`]: #method.split_at_mut + /// [`copy_from_slice`]: slice::copy_from_slice + /// [`split_at_mut`]: slice::split_at_mut #[stable(feature = "clone_from_slice", since = "1.7.0")] pub fn clone_from_slice(&mut self, src: &[T]) where @@ -3012,8 +3027,8 @@ impl [T] { /// assert_eq!(slice, [4, 5, 3, 4, 5]); /// ``` /// - /// [`clone_from_slice`]: #method.clone_from_slice - /// [`split_at_mut`]: #method.split_at_mut + /// [`clone_from_slice`]: slice::clone_from_slice + /// [`split_at_mut`]: slice::split_at_mut #[doc(alias = "memcpy")] #[stable(feature = "copy_from_slice", since = "1.9.0")] pub fn copy_from_slice(&mut self, src: &[T]) @@ -3130,7 +3145,7 @@ impl [T] { /// assert_eq!(slice, [4, 5, 3, 1, 2]); /// ``` /// - /// [`split_at_mut`]: #method.split_at_mut + /// [`split_at_mut`]: slice::split_at_mut #[stable(feature = "swap_with_slice", since = "1.27.0")] pub fn swap_with_slice(&mut self, other: &mut [T]) { assert!(self.len() == other.len(), "destination and source slices have different lengths"); @@ -3374,7 +3389,7 @@ impl [T] { /// function to determine the ordering of two elements. Apart from that, it's equivalent to /// [`is_sorted`]; see its documentation for more information. /// - /// [`is_sorted`]: #method.is_sorted + /// [`is_sorted`]: slice::is_sorted #[unstable(feature = "is_sorted", reason = "new API", issue = "53485")] pub fn is_sorted_by(&self, mut compare: F) -> bool where @@ -3389,7 +3404,7 @@ impl [T] { /// elements, as determined by `f`. Apart from that, it's equivalent to [`is_sorted`]; see its /// documentation for more information. /// - /// [`is_sorted`]: #method.is_sorted + /// [`is_sorted`]: slice::is_sorted /// /// # Examples /// @@ -3423,9 +3438,9 @@ impl [T] { /// /// See also [`binary_search`], [`binary_search_by`], and [`binary_search_by_key`]. /// - /// [`binary_search`]: #method.binary_search - /// [`binary_search_by`]: #method.binary_search_by - /// [`binary_search_by_key`]: #method.binary_search_by_key + /// [`binary_search`]: slice::binary_search + /// [`binary_search_by`]: slice::binary_search_by + /// [`binary_search_by_key`]: slice::binary_search_by_key /// /// # Examples /// diff --git a/library/core/src/slice/specialize.rs b/library/core/src/slice/specialize.rs index 16a9588989c85..425cf71626f40 100644 --- a/library/core/src/slice/specialize.rs +++ b/library/core/src/slice/specialize.rs @@ -1,3 +1,4 @@ +use crate::mem::{size_of, transmute_copy}; use crate::ptr::write_bytes; pub(super) trait SpecFill { @@ -17,42 +18,18 @@ impl SpecFill for [T] { } impl SpecFill for [T] { - default fn spec_fill(&mut self, value: T) { - for item in self.iter_mut() { - *item = value; - } - } -} - -impl SpecFill for [u8] { - fn spec_fill(&mut self, value: u8) { - // SAFETY: this is slice of u8 - unsafe { - let ptr = self.as_mut_ptr(); - let len = self.len(); - write_bytes(ptr, value, len); - } - } -} - -impl SpecFill for [i8] { - fn spec_fill(&mut self, value: i8) { - // SAFETY: this is slice of i8 - unsafe { - let ptr = self.as_mut_ptr(); - let len = self.len(); - write_bytes(ptr, value as u8, len); - } - } -} - -impl SpecFill for [bool] { - fn spec_fill(&mut self, value: bool) { - // SAFETY: this is slice of bool - unsafe { - let ptr = self.as_mut_ptr(); - let len = self.len(); - write_bytes(ptr, value as u8, len); + fn spec_fill(&mut self, value: T) { + if size_of::() == 1 { + // SAFETY: The size_of check above ensures that values are 1 byte wide, as required + // for the transmute and write_bytes + unsafe { + let value: u8 = transmute_copy(&value); + write_bytes(self.as_mut_ptr(), value, self.len()); + } + } else { + for item in self.iter_mut() { + *item = value; + } } } } diff --git a/library/core/src/str/iter.rs b/library/core/src/str/iter.rs index 0c9307a6d2f15..724137aba9f3c 100644 --- a/library/core/src/str/iter.rs +++ b/library/core/src/str/iter.rs @@ -189,6 +189,30 @@ impl<'a> CharIndices<'a> { pub fn as_str(&self) -> &'a str { self.iter.as_str() } + + /// Returns the byte position of the next character, or the length + /// of the underlying string if there are no more characters. + /// + /// # Examples + /// + /// ``` + /// #![feature(char_indices_offset)] + /// let mut chars = "a楽".char_indices(); + /// + /// assert_eq!(chars.offset(), 0); + /// assert_eq!(chars.next(), Some((0, 'a'))); + /// + /// assert_eq!(chars.offset(), 1); + /// assert_eq!(chars.next(), Some((1, '楽'))); + /// + /// assert_eq!(chars.offset(), 4); + /// assert_eq!(chars.next(), None); + /// ``` + #[inline] + #[unstable(feature = "char_indices_offset", issue = "83871")] + pub fn offset(&self) -> usize { + self.front_offset + } } /// An iterator over the bytes of a string slice. @@ -1200,6 +1224,30 @@ impl<'a> DoubleEndedIterator for SplitWhitespace<'a> { #[stable(feature = "fused", since = "1.26.0")] impl FusedIterator for SplitWhitespace<'_> {} +impl<'a> SplitWhitespace<'a> { + /// Returns remainder of the splitted string + /// + /// # Examples + /// + /// ``` + /// #![feature(str_split_whitespace_as_str)] + /// + /// let mut split = "Mary had a little lamb".split_whitespace(); + /// assert_eq!(split.as_str(), "Mary had a little lamb"); + /// + /// split.next(); + /// assert_eq!(split.as_str(), "had a little lamb"); + /// + /// split.by_ref().for_each(drop); + /// assert_eq!(split.as_str(), ""); + /// ``` + #[inline] + #[unstable(feature = "str_split_whitespace_as_str", issue = "77998")] + pub fn as_str(&self) -> &'a str { + self.inner.iter.as_str() + } +} + #[stable(feature = "split_ascii_whitespace", since = "1.34.0")] impl<'a> Iterator for SplitAsciiWhitespace<'a> { type Item = &'a str; @@ -1231,6 +1279,35 @@ impl<'a> DoubleEndedIterator for SplitAsciiWhitespace<'a> { #[stable(feature = "split_ascii_whitespace", since = "1.34.0")] impl FusedIterator for SplitAsciiWhitespace<'_> {} +impl<'a> SplitAsciiWhitespace<'a> { + /// Returns remainder of the splitted string + /// + /// # Examples + /// + /// ``` + /// #![feature(str_split_whitespace_as_str)] + /// + /// let mut split = "Mary had a little lamb".split_ascii_whitespace(); + /// assert_eq!(split.as_str(), "Mary had a little lamb"); + /// + /// split.next(); + /// assert_eq!(split.as_str(), "had a little lamb"); + /// + /// split.by_ref().for_each(drop); + /// assert_eq!(split.as_str(), ""); + /// ``` + #[inline] + #[unstable(feature = "str_split_whitespace_as_str", issue = "77998")] + pub fn as_str(&self) -> &'a str { + if self.inner.iter.iter.finished { + return ""; + } + + // SAFETY: Slice is created from str. + unsafe { crate::str::from_utf8_unchecked(&self.inner.iter.iter.v) } + } +} + #[stable(feature = "split_inclusive", since = "1.51.0")] impl<'a, P: Pattern<'a>> Iterator for SplitInclusive<'a, P> { type Item = &'a str; @@ -1306,7 +1383,7 @@ pub struct EncodeUtf16<'a> { #[stable(feature = "collection_debug", since = "1.17.0")] impl fmt::Debug for EncodeUtf16<'_> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.pad("EncodeUtf16 { .. }") + f.debug_struct("EncodeUtf16").finish_non_exhaustive() } } diff --git a/library/core/src/str/mod.rs b/library/core/src/str/mod.rs index 50ccc2a2eaba1..95dd54976b2c0 100644 --- a/library/core/src/str/mod.rs +++ b/library/core/src/str/mod.rs @@ -15,7 +15,7 @@ mod validations; use self::pattern::Pattern; use self::pattern::{DoubleEndedSearcher, ReverseSearcher, Searcher}; -use crate::char; +use crate::char::{self, EscapeDebugExtArgs}; use crate::mem; use crate::slice::{self, SliceIndex}; @@ -66,7 +66,7 @@ pub use iter::{EscapeDebug, EscapeDefault, EscapeUnicode}; pub use iter::SplitAsciiWhitespace; #[stable(feature = "split_inclusive", since = "1.51.0")] -use iter::SplitInclusive; +pub use iter::SplitInclusive; #[unstable(feature = "str_internals", issue = "none")] pub use validations::next_code_point; @@ -140,7 +140,7 @@ impl str { /// ``` #[doc(alias = "length")] #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_stable(feature = "const_str_len", since = "1.32.0")] + #[rustc_const_stable(feature = "const_str_len", since = "1.39.0")] #[inline] pub const fn len(&self) -> usize { self.as_bytes().len() @@ -161,7 +161,7 @@ impl str { /// ``` #[inline] #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_stable(feature = "const_str_is_empty", since = "1.32.0")] + #[rustc_const_stable(feature = "const_str_is_empty", since = "1.39.0")] pub const fn is_empty(&self) -> bool { self.len() == 0 } @@ -217,7 +217,7 @@ impl str { /// assert_eq!(b"bors", bytes); /// ``` #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_stable(feature = "str_as_bytes", since = "1.32.0")] + #[rustc_const_stable(feature = "str_as_bytes", since = "1.39.0")] #[inline(always)] #[allow(unused_attributes)] #[rustc_allow_const_fn_unstable(const_fn_transmute)] @@ -2342,7 +2342,7 @@ impl str { EscapeDebug { inner: chars .next() - .map(|first| first.escape_debug_ext(true)) + .map(|first| first.escape_debug_ext(EscapeDebugExtArgs::ESCAPE_ALL)) .into_iter() .flatten() .chain(chars.flat_map(CharEscapeDebugContinue)), @@ -2460,7 +2460,11 @@ impl_fn_for_zst! { #[derive(Clone)] struct CharEscapeDebugContinue impl Fn = |c: char| -> char::EscapeDebug { - c.escape_debug_ext(false) + c.escape_debug_ext(EscapeDebugExtArgs { + escape_grapheme_extended: false, + escape_single_quote: true, + escape_double_quote: true + }) }; #[derive(Clone)] diff --git a/library/core/src/sync/atomic.rs b/library/core/src/sync/atomic.rs index ca39224602e69..f1a115563fd3e 100644 --- a/library/core/src/sync/atomic.rs +++ b/library/core/src/sync/atomic.rs @@ -78,7 +78,7 @@ //! ``` //! use std::sync::Arc; //! use std::sync::atomic::{AtomicUsize, Ordering}; -//! use std::thread; +//! use std::{hint, thread}; //! //! fn main() { //! let spinlock = Arc::new(AtomicUsize::new(1)); @@ -89,7 +89,9 @@ //! }); //! //! // Wait for the other thread to release the lock -//! while spinlock.load(Ordering::SeqCst) != 0 {} +//! while spinlock.load(Ordering::SeqCst) != 0 { +//! hint::spin_loop(); +//! } //! //! if let Err(panic) = thread.join() { //! println!("Thread had an error: {:?}", panic); @@ -283,7 +285,7 @@ impl AtomicBool { /// ``` #[inline] #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_stable(feature = "const_atomic_new", since = "1.32.0")] + #[rustc_const_stable(feature = "const_atomic_new", since = "1.24.0")] pub const fn new(v: bool) -> AtomicBool { AtomicBool { v: UnsafeCell::new(v as u8) } } @@ -837,7 +839,6 @@ impl AtomicBool { /// # Examples /// /// ```rust - /// #![feature(atomic_fetch_update)] /// use std::sync::atomic::{AtomicBool, Ordering}; /// /// let x = AtomicBool::new(false); @@ -847,7 +848,7 @@ impl AtomicBool { /// assert_eq!(x.load(Ordering::SeqCst), false); /// ``` #[inline] - #[unstable(feature = "atomic_fetch_update", reason = "recently added", issue = "78639")] + #[stable(feature = "atomic_fetch_update", since = "1.53.0")] #[cfg(target_has_atomic = "8")] pub fn fetch_update( &self, @@ -883,7 +884,7 @@ impl AtomicPtr { /// ``` #[inline] #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_stable(feature = "const_atomic_new", since = "1.32.0")] + #[rustc_const_stable(feature = "const_atomic_new", since = "1.24.0")] pub const fn new(p: *mut T) -> AtomicPtr { AtomicPtr { p: UnsafeCell::new(p) } } @@ -898,8 +899,10 @@ impl AtomicPtr { /// ``` /// use std::sync::atomic::{AtomicPtr, Ordering}; /// - /// let mut atomic_ptr = AtomicPtr::new(&mut 10); - /// *atomic_ptr.get_mut() = &mut 5; + /// let mut data = 10; + /// let mut atomic_ptr = AtomicPtr::new(&mut data); + /// let mut other_data = 5; + /// *atomic_ptr.get_mut() = &mut other_data; /// assert_eq!(unsafe { *atomic_ptr.load(Ordering::SeqCst) }, 5); /// ``` #[inline] @@ -916,9 +919,11 @@ impl AtomicPtr { /// #![feature(atomic_from_mut)] /// use std::sync::atomic::{AtomicPtr, Ordering}; /// - /// let mut some_ptr = &mut 123 as *mut i32; + /// let mut data = 123; + /// let mut some_ptr = &mut data as *mut i32; /// let a = AtomicPtr::from_mut(&mut some_ptr); - /// a.store(&mut 456, Ordering::Relaxed); + /// let mut other_data = 456; + /// a.store(&mut other_data, Ordering::Relaxed); /// assert_eq!(unsafe { *some_ptr }, 456); /// ``` #[inline] @@ -944,7 +949,8 @@ impl AtomicPtr { /// ``` /// use std::sync::atomic::AtomicPtr; /// - /// let atomic_ptr = AtomicPtr::new(&mut 5); + /// let mut data = 5; + /// let atomic_ptr = AtomicPtr::new(&mut data); /// assert_eq!(unsafe { *atomic_ptr.into_inner() }, 5); /// ``` #[inline] @@ -1220,7 +1226,6 @@ impl AtomicPtr { /// # Examples /// /// ```rust - /// #![feature(atomic_fetch_update)] /// use std::sync::atomic::{AtomicPtr, Ordering}; /// /// let ptr: *mut _ = &mut 5; @@ -1239,7 +1244,7 @@ impl AtomicPtr { /// assert_eq!(some_ptr.load(Ordering::SeqCst), new); /// ``` #[inline] - #[unstable(feature = "atomic_fetch_update", reason = "recently added", issue = "78639")] + #[stable(feature = "atomic_fetch_update", since = "1.53.0")] #[cfg(target_has_atomic = "ptr")] pub fn fetch_update( &self, @@ -2276,7 +2281,7 @@ macro_rules! atomic_int_ptr_sized { stable(feature = "atomic_access", since = "1.15.0"), stable(feature = "atomic_from", since = "1.23.0"), stable(feature = "atomic_nand", since = "1.27.0"), - rustc_const_stable(feature = "const_integer_atomics", since = "1.34.0"), + rustc_const_stable(feature = "const_integer_atomics", since = "1.24.0"), stable(feature = "rust1", since = "1.0.0"), "isize", "", @@ -2296,7 +2301,7 @@ macro_rules! atomic_int_ptr_sized { stable(feature = "atomic_access", since = "1.15.0"), stable(feature = "atomic_from", since = "1.23.0"), stable(feature = "atomic_nand", since = "1.27.0"), - rustc_const_stable(feature = "const_integer_atomics", since = "1.34.0"), + rustc_const_stable(feature = "const_integer_atomics", since = "1.24.0"), stable(feature = "rust1", since = "1.0.0"), "usize", "", diff --git a/library/core/src/time.rs b/library/core/src/time.rs index b1443bc33d2ff..fa6a6c2cccc01 100644 --- a/library/core/src/time.rs +++ b/library/core/src/time.rs @@ -48,6 +48,17 @@ const MICROS_PER_SEC: u64 = 1_000_000; /// /// let ten_millis = Duration::from_millis(10); /// ``` +/// +/// # Formatting `Duration` values +/// +/// `Duration` intentionally does not have a `Display` impl, as there are a +/// variety of ways to format spans of time for human readability. `Duration` +/// provides a `Debug` impl that shows the full precision of the value. +/// +/// The `Debug` output uses the non-ASCII "µs" suffix for microseconds. If your +/// program output may appear in contexts that cannot rely on full Unicode +/// compatibility, you may wish to format `Duration` objects yourself or use a +/// crate to do so. #[stable(feature = "duration", since = "1.3.0")] #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)] pub struct Duration { @@ -113,14 +124,13 @@ impl Duration { /// # Examples /// /// ``` - /// #![feature(duration_zero)] /// use std::time::Duration; /// /// let duration = Duration::ZERO; /// assert!(duration.is_zero()); /// assert_eq!(duration.as_nanos(), 0); /// ``` - #[unstable(feature = "duration_zero", issue = "73544")] + #[stable(feature = "duration_zero", since = "1.53.0")] pub const ZERO: Duration = Duration::from_nanos(0); /// The maximum duration. @@ -258,7 +268,6 @@ impl Duration { /// # Examples /// /// ``` - /// #![feature(duration_zero)] /// use std::time::Duration; /// /// assert!(Duration::ZERO.is_zero()); @@ -270,7 +279,8 @@ impl Duration { /// assert!(!Duration::from_nanos(1).is_zero()); /// assert!(!Duration::from_secs(1).is_zero()); /// ``` - #[unstable(feature = "duration_zero", issue = "73544")] + #[stable(feature = "duration_zero", since = "1.53.0")] + #[rustc_const_stable(feature = "duration_zero", since = "1.53.0")] #[inline] pub const fn is_zero(&self) -> bool { self.secs == 0 && self.nanos == 0 @@ -468,14 +478,13 @@ impl Duration { /// # Examples /// /// ``` - /// #![feature(duration_saturating_ops)] /// #![feature(duration_constants)] /// use std::time::Duration; /// /// assert_eq!(Duration::new(0, 0).saturating_add(Duration::new(0, 1)), Duration::new(0, 1)); /// assert_eq!(Duration::new(1, 0).saturating_add(Duration::new(u64::MAX, 0)), Duration::MAX); /// ``` - #[unstable(feature = "duration_saturating_ops", issue = "76416")] + #[stable(feature = "duration_saturating_ops", since = "1.53.0")] #[inline] #[rustc_const_unstable(feature = "duration_consts_2", issue = "72440")] pub const fn saturating_add(self, rhs: Duration) -> Duration { @@ -526,14 +535,12 @@ impl Duration { /// # Examples /// /// ``` - /// #![feature(duration_saturating_ops)] - /// #![feature(duration_zero)] /// use std::time::Duration; /// /// assert_eq!(Duration::new(0, 1).saturating_sub(Duration::new(0, 0)), Duration::new(0, 1)); /// assert_eq!(Duration::new(0, 0).saturating_sub(Duration::new(0, 1)), Duration::ZERO); /// ``` - #[unstable(feature = "duration_saturating_ops", issue = "76416")] + #[stable(feature = "duration_saturating_ops", since = "1.53.0")] #[inline] #[rustc_const_unstable(feature = "duration_consts_2", issue = "72440")] pub const fn saturating_sub(self, rhs: Duration) -> Duration { @@ -579,14 +586,13 @@ impl Duration { /// # Examples /// /// ``` - /// #![feature(duration_saturating_ops)] /// #![feature(duration_constants)] /// use std::time::Duration; /// /// assert_eq!(Duration::new(0, 500_000_001).saturating_mul(2), Duration::new(1, 2)); /// assert_eq!(Duration::new(u64::MAX - 1, 0).saturating_mul(2), Duration::MAX); /// ``` - #[unstable(feature = "duration_saturating_ops", issue = "76416")] + #[stable(feature = "duration_saturating_ops", since = "1.53.0")] #[inline] #[rustc_const_unstable(feature = "duration_consts_2", issue = "72440")] pub const fn saturating_mul(self, rhs: u32) -> Duration { diff --git a/library/core/src/unicode/unicode_data.rs b/library/core/src/unicode/unicode_data.rs index 9c92a8ba28ae4..16803bf2e83b9 100644 --- a/library/core/src/unicode/unicode_data.rs +++ b/library/core/src/unicode/unicode_data.rs @@ -549,16 +549,24 @@ pub mod white_space { #[rustfmt::skip] pub mod conversions { pub fn to_lower(c: char) -> [char; 3] { - match bsearch_case_table(c, LOWERCASE_TABLE) { - None => [c, '\0', '\0'], - Some(index) => LOWERCASE_TABLE[index].1, + if c.is_ascii() { + [(c as u8).to_ascii_lowercase() as char, '\0', '\0'] + } else { + match bsearch_case_table(c, LOWERCASE_TABLE) { + None => [c, '\0', '\0'], + Some(index) => LOWERCASE_TABLE[index].1, + } } } pub fn to_upper(c: char) -> [char; 3] { - match bsearch_case_table(c, UPPERCASE_TABLE) { - None => [c, '\0', '\0'], - Some(index) => UPPERCASE_TABLE[index].1, + if c.is_ascii() { + [(c as u8).to_ascii_uppercase() as char, '\0', '\0'] + } else { + match bsearch_case_table(c, UPPERCASE_TABLE) { + None => [c, '\0', '\0'], + Some(index) => UPPERCASE_TABLE[index].1, + } } } diff --git a/library/core/tests/array.rs b/library/core/tests/array.rs index 89c2a969c28bb..ce7480ce2ee89 100644 --- a/library/core/tests/array.rs +++ b/library/core/tests/array.rs @@ -1,24 +1,6 @@ -use core::array::{self, FixedSizeArray, IntoIter}; +use core::array::{self, IntoIter}; use core::convert::TryFrom; -#[test] -fn fixed_size_array() { - let mut array = [0; 64]; - let mut zero_sized = [(); 64]; - let mut empty_array = [0; 0]; - let mut empty_zero_sized = [(); 0]; - - assert_eq!(FixedSizeArray::as_slice(&array).len(), 64); - assert_eq!(FixedSizeArray::as_slice(&zero_sized).len(), 64); - assert_eq!(FixedSizeArray::as_slice(&empty_array).len(), 0); - assert_eq!(FixedSizeArray::as_slice(&empty_zero_sized).len(), 0); - - assert_eq!(FixedSizeArray::as_mut_slice(&mut array).len(), 64); - assert_eq!(FixedSizeArray::as_mut_slice(&mut zero_sized).len(), 64); - assert_eq!(FixedSizeArray::as_mut_slice(&mut empty_array).len(), 0); - assert_eq!(FixedSizeArray::as_mut_slice(&mut empty_zero_sized).len(), 0); -} - #[test] fn array_from_ref() { let value: String = "Hello World!".into(); diff --git a/library/core/tests/fmt/builders.rs b/library/core/tests/fmt/builders.rs index 129c121e8ceac..7580010a28b4a 100644 --- a/library/core/tests/fmt/builders.rs +++ b/library/core/tests/fmt/builders.rs @@ -105,12 +105,7 @@ mod debug_struct { } assert_eq!("Foo { .. }", format!("{:?}", Foo)); - assert_eq!( - "Foo { - .. -}", - format!("{:#?}", Foo) - ); + assert_eq!("Foo { .. }", format!("{:#?}", Foo)); } #[test] diff --git a/library/core/tests/iter/adapters/chain.rs b/library/core/tests/iter/adapters/chain.rs index ca5ae12ae263f..4cd79687b53d4 100644 --- a/library/core/tests/iter/adapters/chain.rs +++ b/library/core/tests/iter/adapters/chain.rs @@ -174,14 +174,14 @@ fn test_iterator_chain_size_hint() { fn test_iterator_chain_unfused() { // Chain shouldn't be fused in its second iterator, depending on direction let mut iter = NonFused::new(empty()).chain(Toggle { is_empty: true }); - iter.next().unwrap_none(); - iter.next().unwrap(); - iter.next().unwrap_none(); + assert!(iter.next().is_none()); + assert!(iter.next().is_some()); + assert!(iter.next().is_none()); let mut iter = Toggle { is_empty: true }.chain(NonFused::new(empty())); - iter.next_back().unwrap_none(); - iter.next_back().unwrap(); - iter.next_back().unwrap_none(); + assert!(iter.next_back().is_none()); + assert!(iter.next_back().is_some()); + assert!(iter.next_back().is_none()); } #[test] diff --git a/library/core/tests/iter/adapters/intersperse.rs b/library/core/tests/iter/adapters/intersperse.rs index 9dbe232e4eec8..b336c03b5adbe 100644 --- a/library/core/tests/iter/adapters/intersperse.rs +++ b/library/core/tests/iter/adapters/intersperse.rs @@ -9,7 +9,7 @@ fn test_intersperse() { assert_eq!(v, vec![1]); let xs = ["a", "", "b", "c"]; - let v: Vec<&str> = xs.iter().map(|x| x.clone()).intersperse(", ").collect(); + let v: Vec<&str> = xs.iter().map(|x| *x).intersperse(", ").collect(); let text: String = v.concat(); assert_eq!(text, "a, , b, c".to_string()); @@ -24,7 +24,7 @@ fn test_intersperse_size_hint() { assert_eq!(iter.size_hint(), (0, Some(0))); let xs = ["a", "", "b", "c"]; - let mut iter = xs.iter().map(|x| x.clone()).intersperse(", "); + let mut iter = xs.iter().map(|x| *x).intersperse(", "); assert_eq!(iter.size_hint(), (7, Some(7))); assert_eq!(iter.next(), Some("a")); diff --git a/library/core/tests/iter/adapters/zip.rs b/library/core/tests/iter/adapters/zip.rs index 1fce0951e365e..000c15f72c886 100644 --- a/library/core/tests/iter/adapters/zip.rs +++ b/library/core/tests/iter/adapters/zip.rs @@ -245,3 +245,46 @@ fn test_double_ended_zip() { assert_eq!(it.next_back(), Some((3, 3))); assert_eq!(it.next(), None); } + +#[test] +fn test_issue_82282() { + fn overflowed_zip(arr: &[i32]) -> impl Iterator { + static UNIT_EMPTY_ARR: [(); 0] = []; + + let mapped = arr.into_iter().map(|i| *i); + let mut zipped = mapped.zip(UNIT_EMPTY_ARR.iter()); + zipped.next(); + zipped + } + + let arr = [1, 2, 3]; + let zip = overflowed_zip(&arr).zip(overflowed_zip(&arr)); + + assert_eq!(zip.size_hint(), (0, Some(0))); + for _ in zip { + panic!(); + } +} + +#[test] +fn test_issue_82291() { + use std::cell::Cell; + + let mut v1 = [()]; + let v2 = [()]; + + let called = Cell::new(0); + + let mut zip = v1 + .iter_mut() + .map(|r| { + called.set(called.get() + 1); + r + }) + .zip(&v2); + + zip.next_back(); + assert_eq!(called.get(), 1); + zip.next(); + assert_eq!(called.get(), 1); +} diff --git a/library/core/tests/lib.rs b/library/core/tests/lib.rs index d6d3111e2ffa7..f6bfe67e1b12c 100644 --- a/library/core/tests/lib.rs +++ b/library/core/tests/lib.rs @@ -20,16 +20,12 @@ #![feature(core_intrinsics)] #![feature(core_private_bignum)] #![feature(core_private_diy_float)] -#![feature(debug_non_exhaustive)] #![feature(dec2flt)] #![feature(div_duration)] #![feature(duration_consts_2)] #![feature(duration_constants)] -#![feature(duration_saturating_ops)] -#![feature(duration_zero)] #![feature(exact_size_is_empty)] #![feature(extern_types)] -#![feature(fixed_size_array)] #![feature(flt2dec)] #![feature(fmt_internals)] #![feature(hashmap_internals)] @@ -58,7 +54,6 @@ #![feature(iter_intersperse)] #![feature(iter_is_partitioned)] #![feature(iter_order_by)] -#![feature(cmp_min_max_by)] #![feature(iter_map_while)] #![feature(const_mut_refs)] #![feature(const_pin)] @@ -68,20 +63,15 @@ #![feature(unwrap_infallible)] #![feature(option_result_unwrap_unchecked)] #![feature(result_into_ok_or_err)] -#![feature(option_unwrap_none)] -#![feature(peekable_peek_mut)] -#![cfg_attr(not(bootstrap), feature(ptr_metadata))] +#![feature(ptr_metadata)] #![feature(once_cell)] -#![feature(unsafe_block_in_unsafe_fn)] #![feature(unsized_tuple_coercion)] -#![feature(int_bits_const)] -#![feature(nonzero_leading_trailing_zeros)] #![feature(const_option)] #![feature(integer_atomics)] #![feature(slice_group_by)] #![feature(trusted_random_access)] +#![feature(unsize)] #![deny(unsafe_op_in_unsafe_fn)] -#![cfg_attr(not(bootstrap), feature(unsize))] extern crate test; diff --git a/library/core/tests/mem.rs b/library/core/tests/mem.rs index c0b75036f4f3e..dfdbc9305d2a3 100644 --- a/library/core/tests/mem.rs +++ b/library/core/tests/mem.rs @@ -97,6 +97,9 @@ fn test_transmute_copy() { assert_eq!(1, unsafe { transmute_copy(&1) }); } +// Remove this test when `std::raw` is removed. +// The replacement pointer metadata APIs are tested in library/core/tests/ptr.rs +#[allow(deprecated)] #[test] fn test_transmute() { trait Foo { diff --git a/library/core/tests/num/flt2dec/mod.rs b/library/core/tests/num/flt2dec/mod.rs index 8e95249a79d19..960a7ca5ff508 100644 --- a/library/core/tests/num/flt2dec/mod.rs +++ b/library/core/tests/num/flt2dec/mod.rs @@ -514,51 +514,38 @@ where let f = &mut f_; assert_eq!(to_string(f, 0.0, Minus, 0), "0"); - assert_eq!(to_string(f, 0.0, MinusRaw, 0), "0"); + assert_eq!(to_string(f, 0.0, Minus, 0), "0"); assert_eq!(to_string(f, 0.0, MinusPlus, 0), "+0"); - assert_eq!(to_string(f, 0.0, MinusPlusRaw, 0), "+0"); - assert_eq!(to_string(f, -0.0, Minus, 0), "0"); - assert_eq!(to_string(f, -0.0, MinusRaw, 0), "-0"); - assert_eq!(to_string(f, -0.0, MinusPlus, 0), "+0"); - assert_eq!(to_string(f, -0.0, MinusPlusRaw, 0), "-0"); + assert_eq!(to_string(f, -0.0, Minus, 0), "-0"); + assert_eq!(to_string(f, -0.0, MinusPlus, 0), "-0"); + assert_eq!(to_string(f, 0.0, Minus, 1), "0.0"); assert_eq!(to_string(f, 0.0, Minus, 1), "0.0"); - assert_eq!(to_string(f, 0.0, MinusRaw, 1), "0.0"); assert_eq!(to_string(f, 0.0, MinusPlus, 1), "+0.0"); - assert_eq!(to_string(f, 0.0, MinusPlusRaw, 1), "+0.0"); - assert_eq!(to_string(f, -0.0, Minus, 8), "0.00000000"); - assert_eq!(to_string(f, -0.0, MinusRaw, 8), "-0.00000000"); - assert_eq!(to_string(f, -0.0, MinusPlus, 8), "+0.00000000"); - assert_eq!(to_string(f, -0.0, MinusPlusRaw, 8), "-0.00000000"); + assert_eq!(to_string(f, -0.0, Minus, 8), "-0.00000000"); + assert_eq!(to_string(f, -0.0, MinusPlus, 8), "-0.00000000"); assert_eq!(to_string(f, 1.0 / 0.0, Minus, 0), "inf"); - assert_eq!(to_string(f, 1.0 / 0.0, MinusRaw, 0), "inf"); + assert_eq!(to_string(f, 1.0 / 0.0, Minus, 0), "inf"); assert_eq!(to_string(f, 1.0 / 0.0, MinusPlus, 0), "+inf"); - assert_eq!(to_string(f, 1.0 / 0.0, MinusPlusRaw, 0), "+inf"); assert_eq!(to_string(f, 0.0 / 0.0, Minus, 0), "NaN"); - assert_eq!(to_string(f, 0.0 / 0.0, MinusRaw, 1), "NaN"); - assert_eq!(to_string(f, 0.0 / 0.0, MinusPlus, 8), "NaN"); - assert_eq!(to_string(f, 0.0 / 0.0, MinusPlusRaw, 64), "NaN"); + assert_eq!(to_string(f, 0.0 / 0.0, Minus, 1), "NaN"); + assert_eq!(to_string(f, 0.0 / 0.0, MinusPlus, 64), "NaN"); assert_eq!(to_string(f, -1.0 / 0.0, Minus, 0), "-inf"); - assert_eq!(to_string(f, -1.0 / 0.0, MinusRaw, 1), "-inf"); - assert_eq!(to_string(f, -1.0 / 0.0, MinusPlus, 8), "-inf"); - assert_eq!(to_string(f, -1.0 / 0.0, MinusPlusRaw, 64), "-inf"); + assert_eq!(to_string(f, -1.0 / 0.0, Minus, 1), "-inf"); + assert_eq!(to_string(f, -1.0 / 0.0, MinusPlus, 64), "-inf"); assert_eq!(to_string(f, 3.14, Minus, 0), "3.14"); - assert_eq!(to_string(f, 3.14, MinusRaw, 0), "3.14"); + assert_eq!(to_string(f, 3.14, Minus, 0), "3.14"); assert_eq!(to_string(f, 3.14, MinusPlus, 0), "+3.14"); - assert_eq!(to_string(f, 3.14, MinusPlusRaw, 0), "+3.14"); assert_eq!(to_string(f, -3.14, Minus, 0), "-3.14"); - assert_eq!(to_string(f, -3.14, MinusRaw, 0), "-3.14"); + assert_eq!(to_string(f, -3.14, Minus, 0), "-3.14"); assert_eq!(to_string(f, -3.14, MinusPlus, 0), "-3.14"); - assert_eq!(to_string(f, -3.14, MinusPlusRaw, 0), "-3.14"); assert_eq!(to_string(f, 3.14, Minus, 1), "3.14"); - assert_eq!(to_string(f, 3.14, MinusRaw, 2), "3.14"); - assert_eq!(to_string(f, 3.14, MinusPlus, 3), "+3.140"); - assert_eq!(to_string(f, 3.14, MinusPlusRaw, 4), "+3.1400"); + assert_eq!(to_string(f, 3.14, Minus, 2), "3.14"); + assert_eq!(to_string(f, 3.14, MinusPlus, 4), "+3.1400"); + assert_eq!(to_string(f, -3.14, Minus, 8), "-3.14000000"); assert_eq!(to_string(f, -3.14, Minus, 8), "-3.14000000"); - assert_eq!(to_string(f, -3.14, MinusRaw, 8), "-3.14000000"); assert_eq!(to_string(f, -3.14, MinusPlus, 8), "-3.14000000"); - assert_eq!(to_string(f, -3.14, MinusPlusRaw, 8), "-3.14000000"); assert_eq!(to_string(f, 7.5e-11, Minus, 0), "0.000000000075"); assert_eq!(to_string(f, 7.5e-11, Minus, 3), "0.000000000075"); @@ -615,68 +602,48 @@ where let f = &mut f_; assert_eq!(to_string(f, 0.0, Minus, (-4, 16), false), "0"); - assert_eq!(to_string(f, 0.0, MinusRaw, (-4, 16), false), "0"); + assert_eq!(to_string(f, 0.0, Minus, (-4, 16), false), "0"); assert_eq!(to_string(f, 0.0, MinusPlus, (-4, 16), false), "+0"); - assert_eq!(to_string(f, 0.0, MinusPlusRaw, (-4, 16), false), "+0"); - assert_eq!(to_string(f, -0.0, Minus, (-4, 16), false), "0"); - assert_eq!(to_string(f, -0.0, MinusRaw, (-4, 16), false), "-0"); - assert_eq!(to_string(f, -0.0, MinusPlus, (-4, 16), false), "+0"); - assert_eq!(to_string(f, -0.0, MinusPlusRaw, (-4, 16), false), "-0"); + assert_eq!(to_string(f, -0.0, Minus, (-4, 16), false), "-0"); + assert_eq!(to_string(f, -0.0, MinusPlus, (-4, 16), false), "-0"); assert_eq!(to_string(f, 0.0, Minus, (0, 0), true), "0E0"); - assert_eq!(to_string(f, 0.0, MinusRaw, (0, 0), false), "0e0"); - assert_eq!(to_string(f, 0.0, MinusPlus, (-9, -5), true), "+0E0"); - assert_eq!(to_string(f, 0.0, MinusPlusRaw, (5, 9), false), "+0e0"); - assert_eq!(to_string(f, -0.0, Minus, (0, 0), true), "0E0"); - assert_eq!(to_string(f, -0.0, MinusRaw, (0, 0), false), "-0e0"); - assert_eq!(to_string(f, -0.0, MinusPlus, (-9, -5), true), "+0E0"); - assert_eq!(to_string(f, -0.0, MinusPlusRaw, (5, 9), false), "-0e0"); + assert_eq!(to_string(f, 0.0, Minus, (0, 0), false), "0e0"); + assert_eq!(to_string(f, 0.0, MinusPlus, (5, 9), false), "+0e0"); + assert_eq!(to_string(f, -0.0, Minus, (0, 0), true), "-0E0"); + assert_eq!(to_string(f, -0.0, MinusPlus, (5, 9), false), "-0e0"); assert_eq!(to_string(f, 1.0 / 0.0, Minus, (-4, 16), false), "inf"); - assert_eq!(to_string(f, 1.0 / 0.0, MinusRaw, (-4, 16), true), "inf"); - assert_eq!(to_string(f, 1.0 / 0.0, MinusPlus, (-4, 16), false), "+inf"); - assert_eq!(to_string(f, 1.0 / 0.0, MinusPlusRaw, (-4, 16), true), "+inf"); + assert_eq!(to_string(f, 1.0 / 0.0, Minus, (-4, 16), true), "inf"); + assert_eq!(to_string(f, 1.0 / 0.0, MinusPlus, (-4, 16), true), "+inf"); assert_eq!(to_string(f, 0.0 / 0.0, Minus, (0, 0), false), "NaN"); - assert_eq!(to_string(f, 0.0 / 0.0, MinusRaw, (0, 0), true), "NaN"); - assert_eq!(to_string(f, 0.0 / 0.0, MinusPlus, (-9, -5), false), "NaN"); - assert_eq!(to_string(f, 0.0 / 0.0, MinusPlusRaw, (5, 9), true), "NaN"); + assert_eq!(to_string(f, 0.0 / 0.0, Minus, (0, 0), true), "NaN"); + assert_eq!(to_string(f, 0.0 / 0.0, MinusPlus, (5, 9), true), "NaN"); assert_eq!(to_string(f, -1.0 / 0.0, Minus, (0, 0), false), "-inf"); - assert_eq!(to_string(f, -1.0 / 0.0, MinusRaw, (0, 0), true), "-inf"); - assert_eq!(to_string(f, -1.0 / 0.0, MinusPlus, (-9, -5), false), "-inf"); - assert_eq!(to_string(f, -1.0 / 0.0, MinusPlusRaw, (5, 9), true), "-inf"); + assert_eq!(to_string(f, -1.0 / 0.0, Minus, (0, 0), true), "-inf"); + assert_eq!(to_string(f, -1.0 / 0.0, MinusPlus, (5, 9), true), "-inf"); assert_eq!(to_string(f, 3.14, Minus, (-4, 16), false), "3.14"); - assert_eq!(to_string(f, 3.14, MinusRaw, (-4, 16), false), "3.14"); assert_eq!(to_string(f, 3.14, MinusPlus, (-4, 16), false), "+3.14"); - assert_eq!(to_string(f, 3.14, MinusPlusRaw, (-4, 16), false), "+3.14"); assert_eq!(to_string(f, -3.14, Minus, (-4, 16), false), "-3.14"); - assert_eq!(to_string(f, -3.14, MinusRaw, (-4, 16), false), "-3.14"); assert_eq!(to_string(f, -3.14, MinusPlus, (-4, 16), false), "-3.14"); - assert_eq!(to_string(f, -3.14, MinusPlusRaw, (-4, 16), false), "-3.14"); assert_eq!(to_string(f, 3.14, Minus, (0, 0), true), "3.14E0"); - assert_eq!(to_string(f, 3.14, MinusRaw, (0, 0), false), "3.14e0"); - assert_eq!(to_string(f, 3.14, MinusPlus, (-9, -5), true), "+3.14E0"); - assert_eq!(to_string(f, 3.14, MinusPlusRaw, (5, 9), false), "+3.14e0"); + assert_eq!(to_string(f, 3.14, Minus, (0, 0), false), "3.14e0"); + assert_eq!(to_string(f, 3.14, MinusPlus, (5, 9), false), "+3.14e0"); assert_eq!(to_string(f, -3.14, Minus, (0, 0), true), "-3.14E0"); - assert_eq!(to_string(f, -3.14, MinusRaw, (0, 0), false), "-3.14e0"); - assert_eq!(to_string(f, -3.14, MinusPlus, (-9, -5), true), "-3.14E0"); - assert_eq!(to_string(f, -3.14, MinusPlusRaw, (5, 9), false), "-3.14e0"); + assert_eq!(to_string(f, -3.14, Minus, (0, 0), false), "-3.14e0"); + assert_eq!(to_string(f, -3.14, MinusPlus, (5, 9), false), "-3.14e0"); assert_eq!(to_string(f, 0.1, Minus, (-4, 16), false), "0.1"); - assert_eq!(to_string(f, 0.1, MinusRaw, (-4, 16), false), "0.1"); + assert_eq!(to_string(f, 0.1, Minus, (-4, 16), false), "0.1"); assert_eq!(to_string(f, 0.1, MinusPlus, (-4, 16), false), "+0.1"); - assert_eq!(to_string(f, 0.1, MinusPlusRaw, (-4, 16), false), "+0.1"); assert_eq!(to_string(f, -0.1, Minus, (-4, 16), false), "-0.1"); - assert_eq!(to_string(f, -0.1, MinusRaw, (-4, 16), false), "-0.1"); assert_eq!(to_string(f, -0.1, MinusPlus, (-4, 16), false), "-0.1"); - assert_eq!(to_string(f, -0.1, MinusPlusRaw, (-4, 16), false), "-0.1"); assert_eq!(to_string(f, 0.1, Minus, (0, 0), true), "1E-1"); - assert_eq!(to_string(f, 0.1, MinusRaw, (0, 0), false), "1e-1"); - assert_eq!(to_string(f, 0.1, MinusPlus, (-9, -5), true), "+1E-1"); - assert_eq!(to_string(f, 0.1, MinusPlusRaw, (5, 9), false), "+1e-1"); + assert_eq!(to_string(f, 0.1, Minus, (0, 0), false), "1e-1"); + assert_eq!(to_string(f, 0.1, MinusPlus, (5, 9), false), "+1e-1"); assert_eq!(to_string(f, -0.1, Minus, (0, 0), true), "-1E-1"); - assert_eq!(to_string(f, -0.1, MinusRaw, (0, 0), false), "-1e-1"); - assert_eq!(to_string(f, -0.1, MinusPlus, (-9, -5), true), "-1E-1"); - assert_eq!(to_string(f, -0.1, MinusPlusRaw, (5, 9), false), "-1e-1"); + assert_eq!(to_string(f, -0.1, Minus, (0, 0), false), "-1e-1"); + assert_eq!(to_string(f, -0.1, MinusPlus, (5, 9), false), "-1e-1"); assert_eq!(to_string(f, 7.5e-11, Minus, (-4, 16), false), "7.5e-11"); assert_eq!(to_string(f, 7.5e-11, Minus, (-11, 10), false), "0.000000000075"); @@ -734,68 +701,51 @@ where let f = &mut f_; assert_eq!(to_string(f, 0.0, Minus, 1, true), "0E0"); - assert_eq!(to_string(f, 0.0, MinusRaw, 1, false), "0e0"); - assert_eq!(to_string(f, 0.0, MinusPlus, 1, true), "+0E0"); - assert_eq!(to_string(f, 0.0, MinusPlusRaw, 1, false), "+0e0"); - assert_eq!(to_string(f, -0.0, Minus, 1, true), "0E0"); - assert_eq!(to_string(f, -0.0, MinusRaw, 1, false), "-0e0"); - assert_eq!(to_string(f, -0.0, MinusPlus, 1, true), "+0E0"); - assert_eq!(to_string(f, -0.0, MinusPlusRaw, 1, false), "-0e0"); + assert_eq!(to_string(f, 0.0, Minus, 1, false), "0e0"); + assert_eq!(to_string(f, 0.0, MinusPlus, 1, false), "+0e0"); + assert_eq!(to_string(f, -0.0, Minus, 1, true), "-0E0"); + assert_eq!(to_string(f, -0.0, MinusPlus, 1, false), "-0e0"); assert_eq!(to_string(f, 0.0, Minus, 2, true), "0.0E0"); - assert_eq!(to_string(f, 0.0, MinusRaw, 2, false), "0.0e0"); - assert_eq!(to_string(f, 0.0, MinusPlus, 2, true), "+0.0E0"); - assert_eq!(to_string(f, 0.0, MinusPlusRaw, 2, false), "+0.0e0"); - assert_eq!(to_string(f, -0.0, Minus, 8, true), "0.0000000E0"); - assert_eq!(to_string(f, -0.0, MinusRaw, 8, false), "-0.0000000e0"); - assert_eq!(to_string(f, -0.0, MinusPlus, 8, true), "+0.0000000E0"); - assert_eq!(to_string(f, -0.0, MinusPlusRaw, 8, false), "-0.0000000e0"); + assert_eq!(to_string(f, 0.0, Minus, 2, false), "0.0e0"); + assert_eq!(to_string(f, 0.0, MinusPlus, 2, false), "+0.0e0"); + assert_eq!(to_string(f, -0.0, Minus, 8, false), "-0.0000000e0"); + assert_eq!(to_string(f, -0.0, MinusPlus, 8, false), "-0.0000000e0"); assert_eq!(to_string(f, 1.0 / 0.0, Minus, 1, false), "inf"); - assert_eq!(to_string(f, 1.0 / 0.0, MinusRaw, 1, true), "inf"); - assert_eq!(to_string(f, 1.0 / 0.0, MinusPlus, 1, false), "+inf"); - assert_eq!(to_string(f, 1.0 / 0.0, MinusPlusRaw, 1, true), "+inf"); + assert_eq!(to_string(f, 1.0 / 0.0, Minus, 1, true), "inf"); + assert_eq!(to_string(f, 1.0 / 0.0, MinusPlus, 1, true), "+inf"); assert_eq!(to_string(f, 0.0 / 0.0, Minus, 8, false), "NaN"); - assert_eq!(to_string(f, 0.0 / 0.0, MinusRaw, 8, true), "NaN"); - assert_eq!(to_string(f, 0.0 / 0.0, MinusPlus, 8, false), "NaN"); - assert_eq!(to_string(f, 0.0 / 0.0, MinusPlusRaw, 8, true), "NaN"); + assert_eq!(to_string(f, 0.0 / 0.0, Minus, 8, true), "NaN"); + assert_eq!(to_string(f, 0.0 / 0.0, MinusPlus, 8, true), "NaN"); assert_eq!(to_string(f, -1.0 / 0.0, Minus, 64, false), "-inf"); - assert_eq!(to_string(f, -1.0 / 0.0, MinusRaw, 64, true), "-inf"); - assert_eq!(to_string(f, -1.0 / 0.0, MinusPlus, 64, false), "-inf"); - assert_eq!(to_string(f, -1.0 / 0.0, MinusPlusRaw, 64, true), "-inf"); + assert_eq!(to_string(f, -1.0 / 0.0, Minus, 64, true), "-inf"); + assert_eq!(to_string(f, -1.0 / 0.0, MinusPlus, 64, true), "-inf"); assert_eq!(to_string(f, 3.14, Minus, 1, true), "3E0"); - assert_eq!(to_string(f, 3.14, MinusRaw, 1, false), "3e0"); - assert_eq!(to_string(f, 3.14, MinusPlus, 1, true), "+3E0"); - assert_eq!(to_string(f, 3.14, MinusPlusRaw, 1, false), "+3e0"); + assert_eq!(to_string(f, 3.14, Minus, 1, false), "3e0"); + assert_eq!(to_string(f, 3.14, MinusPlus, 1, false), "+3e0"); assert_eq!(to_string(f, -3.14, Minus, 2, true), "-3.1E0"); - assert_eq!(to_string(f, -3.14, MinusRaw, 2, false), "-3.1e0"); - assert_eq!(to_string(f, -3.14, MinusPlus, 2, true), "-3.1E0"); - assert_eq!(to_string(f, -3.14, MinusPlusRaw, 2, false), "-3.1e0"); + assert_eq!(to_string(f, -3.14, Minus, 2, false), "-3.1e0"); + assert_eq!(to_string(f, -3.14, MinusPlus, 2, false), "-3.1e0"); assert_eq!(to_string(f, 3.14, Minus, 3, true), "3.14E0"); - assert_eq!(to_string(f, 3.14, MinusRaw, 3, false), "3.14e0"); - assert_eq!(to_string(f, 3.14, MinusPlus, 3, true), "+3.14E0"); - assert_eq!(to_string(f, 3.14, MinusPlusRaw, 3, false), "+3.14e0"); + assert_eq!(to_string(f, 3.14, Minus, 3, false), "3.14e0"); + assert_eq!(to_string(f, 3.14, MinusPlus, 3, false), "+3.14e0"); assert_eq!(to_string(f, -3.14, Minus, 4, true), "-3.140E0"); - assert_eq!(to_string(f, -3.14, MinusRaw, 4, false), "-3.140e0"); - assert_eq!(to_string(f, -3.14, MinusPlus, 4, true), "-3.140E0"); - assert_eq!(to_string(f, -3.14, MinusPlusRaw, 4, false), "-3.140e0"); + assert_eq!(to_string(f, -3.14, Minus, 4, false), "-3.140e0"); + assert_eq!(to_string(f, -3.14, MinusPlus, 4, false), "-3.140e0"); assert_eq!(to_string(f, 0.195, Minus, 1, false), "2e-1"); - assert_eq!(to_string(f, 0.195, MinusRaw, 1, true), "2E-1"); - assert_eq!(to_string(f, 0.195, MinusPlus, 1, false), "+2e-1"); - assert_eq!(to_string(f, 0.195, MinusPlusRaw, 1, true), "+2E-1"); + assert_eq!(to_string(f, 0.195, Minus, 1, true), "2E-1"); + assert_eq!(to_string(f, 0.195, MinusPlus, 1, true), "+2E-1"); assert_eq!(to_string(f, -0.195, Minus, 2, false), "-2.0e-1"); - assert_eq!(to_string(f, -0.195, MinusRaw, 2, true), "-2.0E-1"); - assert_eq!(to_string(f, -0.195, MinusPlus, 2, false), "-2.0e-1"); - assert_eq!(to_string(f, -0.195, MinusPlusRaw, 2, true), "-2.0E-1"); + assert_eq!(to_string(f, -0.195, Minus, 2, true), "-2.0E-1"); + assert_eq!(to_string(f, -0.195, MinusPlus, 2, true), "-2.0E-1"); assert_eq!(to_string(f, 0.195, Minus, 3, false), "1.95e-1"); - assert_eq!(to_string(f, 0.195, MinusRaw, 3, true), "1.95E-1"); - assert_eq!(to_string(f, 0.195, MinusPlus, 3, false), "+1.95e-1"); - assert_eq!(to_string(f, 0.195, MinusPlusRaw, 3, true), "+1.95E-1"); + assert_eq!(to_string(f, 0.195, Minus, 3, true), "1.95E-1"); + assert_eq!(to_string(f, 0.195, MinusPlus, 3, true), "+1.95E-1"); assert_eq!(to_string(f, -0.195, Minus, 4, false), "-1.950e-1"); - assert_eq!(to_string(f, -0.195, MinusRaw, 4, true), "-1.950E-1"); - assert_eq!(to_string(f, -0.195, MinusPlus, 4, false), "-1.950e-1"); - assert_eq!(to_string(f, -0.195, MinusPlusRaw, 4, true), "-1.950E-1"); + assert_eq!(to_string(f, -0.195, Minus, 4, true), "-1.950E-1"); + assert_eq!(to_string(f, -0.195, MinusPlus, 4, true), "-1.950E-1"); assert_eq!(to_string(f, 9.5, Minus, 1, false), "1e1"); assert_eq!(to_string(f, 9.5, Minus, 2, false), "9.5e0"); @@ -1007,68 +957,48 @@ where let f = &mut f_; assert_eq!(to_string(f, 0.0, Minus, 0), "0"); - assert_eq!(to_string(f, 0.0, MinusRaw, 0), "0"); assert_eq!(to_string(f, 0.0, MinusPlus, 0), "+0"); - assert_eq!(to_string(f, 0.0, MinusPlusRaw, 0), "+0"); - assert_eq!(to_string(f, -0.0, Minus, 0), "0"); - assert_eq!(to_string(f, -0.0, MinusRaw, 0), "-0"); - assert_eq!(to_string(f, -0.0, MinusPlus, 0), "+0"); - assert_eq!(to_string(f, -0.0, MinusPlusRaw, 0), "-0"); + assert_eq!(to_string(f, -0.0, Minus, 0), "-0"); + assert_eq!(to_string(f, -0.0, MinusPlus, 0), "-0"); assert_eq!(to_string(f, 0.0, Minus, 1), "0.0"); - assert_eq!(to_string(f, 0.0, MinusRaw, 1), "0.0"); assert_eq!(to_string(f, 0.0, MinusPlus, 1), "+0.0"); - assert_eq!(to_string(f, 0.0, MinusPlusRaw, 1), "+0.0"); - assert_eq!(to_string(f, -0.0, Minus, 8), "0.00000000"); - assert_eq!(to_string(f, -0.0, MinusRaw, 8), "-0.00000000"); - assert_eq!(to_string(f, -0.0, MinusPlus, 8), "+0.00000000"); - assert_eq!(to_string(f, -0.0, MinusPlusRaw, 8), "-0.00000000"); + assert_eq!(to_string(f, -0.0, Minus, 8), "-0.00000000"); + assert_eq!(to_string(f, -0.0, MinusPlus, 8), "-0.00000000"); assert_eq!(to_string(f, 1.0 / 0.0, Minus, 0), "inf"); - assert_eq!(to_string(f, 1.0 / 0.0, MinusRaw, 1), "inf"); - assert_eq!(to_string(f, 1.0 / 0.0, MinusPlus, 8), "+inf"); - assert_eq!(to_string(f, 1.0 / 0.0, MinusPlusRaw, 64), "+inf"); + assert_eq!(to_string(f, 1.0 / 0.0, Minus, 1), "inf"); + assert_eq!(to_string(f, 1.0 / 0.0, MinusPlus, 64), "+inf"); assert_eq!(to_string(f, 0.0 / 0.0, Minus, 0), "NaN"); - assert_eq!(to_string(f, 0.0 / 0.0, MinusRaw, 1), "NaN"); - assert_eq!(to_string(f, 0.0 / 0.0, MinusPlus, 8), "NaN"); - assert_eq!(to_string(f, 0.0 / 0.0, MinusPlusRaw, 64), "NaN"); + assert_eq!(to_string(f, 0.0 / 0.0, Minus, 1), "NaN"); + assert_eq!(to_string(f, 0.0 / 0.0, MinusPlus, 64), "NaN"); assert_eq!(to_string(f, -1.0 / 0.0, Minus, 0), "-inf"); - assert_eq!(to_string(f, -1.0 / 0.0, MinusRaw, 1), "-inf"); - assert_eq!(to_string(f, -1.0 / 0.0, MinusPlus, 8), "-inf"); - assert_eq!(to_string(f, -1.0 / 0.0, MinusPlusRaw, 64), "-inf"); + assert_eq!(to_string(f, -1.0 / 0.0, Minus, 1), "-inf"); + assert_eq!(to_string(f, -1.0 / 0.0, MinusPlus, 64), "-inf"); assert_eq!(to_string(f, 3.14, Minus, 0), "3"); - assert_eq!(to_string(f, 3.14, MinusRaw, 0), "3"); + assert_eq!(to_string(f, 3.14, Minus, 0), "3"); assert_eq!(to_string(f, 3.14, MinusPlus, 0), "+3"); - assert_eq!(to_string(f, 3.14, MinusPlusRaw, 0), "+3"); assert_eq!(to_string(f, -3.14, Minus, 0), "-3"); - assert_eq!(to_string(f, -3.14, MinusRaw, 0), "-3"); + assert_eq!(to_string(f, -3.14, Minus, 0), "-3"); assert_eq!(to_string(f, -3.14, MinusPlus, 0), "-3"); - assert_eq!(to_string(f, -3.14, MinusPlusRaw, 0), "-3"); assert_eq!(to_string(f, 3.14, Minus, 1), "3.1"); - assert_eq!(to_string(f, 3.14, MinusRaw, 2), "3.14"); - assert_eq!(to_string(f, 3.14, MinusPlus, 3), "+3.140"); - assert_eq!(to_string(f, 3.14, MinusPlusRaw, 4), "+3.1400"); + assert_eq!(to_string(f, 3.14, Minus, 2), "3.14"); + assert_eq!(to_string(f, 3.14, MinusPlus, 4), "+3.1400"); + assert_eq!(to_string(f, -3.14, Minus, 8), "-3.14000000"); assert_eq!(to_string(f, -3.14, Minus, 8), "-3.14000000"); - assert_eq!(to_string(f, -3.14, MinusRaw, 8), "-3.14000000"); assert_eq!(to_string(f, -3.14, MinusPlus, 8), "-3.14000000"); - assert_eq!(to_string(f, -3.14, MinusPlusRaw, 8), "-3.14000000"); assert_eq!(to_string(f, 0.195, Minus, 0), "0"); - assert_eq!(to_string(f, 0.195, MinusRaw, 0), "0"); assert_eq!(to_string(f, 0.195, MinusPlus, 0), "+0"); - assert_eq!(to_string(f, 0.195, MinusPlusRaw, 0), "+0"); assert_eq!(to_string(f, -0.195, Minus, 0), "-0"); - assert_eq!(to_string(f, -0.195, MinusRaw, 0), "-0"); + assert_eq!(to_string(f, -0.195, Minus, 0), "-0"); assert_eq!(to_string(f, -0.195, MinusPlus, 0), "-0"); - assert_eq!(to_string(f, -0.195, MinusPlusRaw, 0), "-0"); assert_eq!(to_string(f, 0.195, Minus, 1), "0.2"); - assert_eq!(to_string(f, 0.195, MinusRaw, 2), "0.20"); - assert_eq!(to_string(f, 0.195, MinusPlus, 3), "+0.195"); - assert_eq!(to_string(f, 0.195, MinusPlusRaw, 4), "+0.1950"); + assert_eq!(to_string(f, 0.195, Minus, 2), "0.20"); + assert_eq!(to_string(f, 0.195, MinusPlus, 4), "+0.1950"); assert_eq!(to_string(f, -0.195, Minus, 5), "-0.19500"); - assert_eq!(to_string(f, -0.195, MinusRaw, 6), "-0.195000"); - assert_eq!(to_string(f, -0.195, MinusPlus, 7), "-0.1950000"); - assert_eq!(to_string(f, -0.195, MinusPlusRaw, 8), "-0.19500000"); + assert_eq!(to_string(f, -0.195, Minus, 6), "-0.195000"); + assert_eq!(to_string(f, -0.195, MinusPlus, 8), "-0.19500000"); assert_eq!(to_string(f, 999.5, Minus, 0), "1000"); assert_eq!(to_string(f, 999.5, Minus, 1), "999.5"); diff --git a/library/core/tests/num/ieee754.rs b/library/core/tests/num/ieee754.rs new file mode 100644 index 0000000000000..f6e5dfc98c793 --- /dev/null +++ b/library/core/tests/num/ieee754.rs @@ -0,0 +1,158 @@ +//! IEEE 754 floating point compliance tests +//! +//! To understand IEEE 754's requirements on a programming language, one must understand that the +//! requirements of IEEE 754 rest on the total programming environment, and not entirely on any +//! one component. That means the hardware, language, and even libraries are considered part of +//! conforming floating point support in a programming environment. +//! +//! A programming language's duty, accordingly, is: +//! 1. offer access to the hardware where the hardware offers support +//! 2. provide operations that fulfill the remaining requirements of the standard +//! 3. provide the ability to write additional software that can fulfill those requirements +//! +//! This may be fulfilled in any combination that the language sees fit. However, to claim that +//! a language supports IEEE 754 is to suggest that it has fulfilled requirements 1 and 2, without +//! deferring minimum requirements to libraries. This is because support for IEEE 754 is defined +//! as complete support for at least one specified floating point type as an "arithmetic" and +//! "interchange" format, plus specified type conversions to "external character sequences" and +//! integer types. +//! +//! For our purposes, +//! "interchange format" => f32, f64 +//! "arithmetic format" => f32, f64, and any "soft floats" +//! "external character sequence" => str from any float +//! "integer format" => {i,u}{8,16,32,64,128} +//! +//! None of these tests are against Rust's own implementation. They are only tests against the +//! standard. That is why they accept wildly diverse inputs or may seem to duplicate other tests. +//! Please consider this carefully when adding, removing, or reorganizing these tests. They are +//! here so that it is clear what tests are required by the standard and what can be changed. +use ::core::str::FromStr; + +// IEEE 754 for many tests is applied to specific bit patterns. +// These generally are not applicable to NaN, however. +macro_rules! assert_biteq { + ($lhs:expr, $rhs:expr) => { + assert_eq!($lhs.to_bits(), $rhs.to_bits()) + }; +} + +// ToString uses the default fmt::Display impl without special concerns, and bypasses other parts +// of the formatting infrastructure, which makes it ideal for testing here. +#[allow(unused_macros)] +macro_rules! roundtrip { + ($f:expr => $t:ty) => { + ($f).to_string().parse::<$t>().unwrap() + }; +} + +macro_rules! assert_floats_roundtrip { + ($f:ident) => { + assert_biteq!(f32::$f, roundtrip!(f32::$f => f32)); + assert_biteq!(f64::$f, roundtrip!(f64::$f => f64)); + }; + ($f:expr) => { + assert_biteq!($f as f32, roundtrip!($f => f32)); + assert_biteq!($f as f64, roundtrip!($f => f64)); + } +} + +macro_rules! assert_floats_bitne { + ($lhs:ident, $rhs:ident) => { + assert_ne!(f32::$lhs.to_bits(), f32::$rhs.to_bits()); + assert_ne!(f64::$lhs.to_bits(), f64::$rhs.to_bits()); + }; + ($lhs:expr, $rhs:expr) => { + assert_ne!(f32::to_bits($lhs), f32::to_bits($rhs)); + assert_ne!(f64::to_bits($lhs), f64::to_bits($rhs)); + }; +} + +// We must preserve signs on all numbers. That includes zero. +// -0 and 0 are == normally, so test bit equality. +#[test] +fn preserve_signed_zero() { + assert_floats_roundtrip!(-0.0); + assert_floats_roundtrip!(0.0); + assert_floats_bitne!(0.0, -0.0); +} + +#[test] +fn preserve_signed_infinity() { + assert_floats_roundtrip!(INFINITY); + assert_floats_roundtrip!(NEG_INFINITY); + assert_floats_bitne!(INFINITY, NEG_INFINITY); +} + +#[test] +fn infinity_to_str() { + assert!(match f32::INFINITY.to_string().to_lowercase().as_str() { + "+infinity" | "infinity" => true, + "+inf" | "inf" => true, + _ => false, + }); + assert!( + match f64::INFINITY.to_string().to_lowercase().as_str() { + "+infinity" | "infinity" => true, + "+inf" | "inf" => true, + _ => false, + }, + "Infinity must write to a string as some casing of inf or infinity, with an optional +." + ); +} + +#[test] +fn neg_infinity_to_str() { + assert!(match f32::NEG_INFINITY.to_string().to_lowercase().as_str() { + "-infinity" | "-inf" => true, + _ => false, + }); + assert!( + match f64::NEG_INFINITY.to_string().to_lowercase().as_str() { + "-infinity" | "-inf" => true, + _ => false, + }, + "Negative Infinity must write to a string as some casing of -inf or -infinity" + ) +} + +#[test] +fn nan_to_str() { + assert!( + match f32::NAN.to_string().to_lowercase().as_str() { + "nan" | "+nan" | "-nan" => true, + _ => false, + }, + "NaNs must write to a string as some casing of nan." + ) +} + +// "+"?("inf"|"infinity") in any case => Infinity +#[test] +fn infinity_from_str() { + assert_biteq!(f32::INFINITY, f32::from_str("infinity").unwrap()); + assert_biteq!(f32::INFINITY, f32::from_str("inf").unwrap()); + assert_biteq!(f32::INFINITY, f32::from_str("+infinity").unwrap()); + assert_biteq!(f32::INFINITY, f32::from_str("+inf").unwrap()); + // yes! this means you are weLcOmE tO mY iNfInItElY tWiStEd MiNd + assert_biteq!(f32::INFINITY, f32::from_str("+iNfInItY").unwrap()); +} + +// "-inf"|"-infinity" in any case => Negative Infinity +#[test] +fn neg_infinity_from_str() { + assert_biteq!(f32::NEG_INFINITY, f32::from_str("-infinity").unwrap()); + assert_biteq!(f32::NEG_INFINITY, f32::from_str("-inf").unwrap()); + assert_biteq!(f32::NEG_INFINITY, f32::from_str("-INF").unwrap()); + assert_biteq!(f32::NEG_INFINITY, f32::from_str("-INFinity").unwrap()); +} + +// ("+"|"-"")?"s"?"nan" in any case => qNaN +#[test] +fn qnan_from_str() { + assert!("nan".parse::().unwrap().is_nan()); + assert!("-nan".parse::().unwrap().is_nan()); + assert!("+nan".parse::().unwrap().is_nan()); + assert!("+NAN".parse::().unwrap().is_nan()); + assert!("-NaN".parse::().unwrap().is_nan()); +} diff --git a/library/core/tests/num/mod.rs b/library/core/tests/num/mod.rs index e66a73ac1289b..bbb67667dfc05 100644 --- a/library/core/tests/num/mod.rs +++ b/library/core/tests/num/mod.rs @@ -32,6 +32,7 @@ mod flt2dec; mod ops; mod wrapping; +mod ieee754; mod nan; /// Adds the attribute to all items in the block. diff --git a/library/core/tests/ptr.rs b/library/core/tests/ptr.rs index 224a58e3ccdb7..11af8090c3a4f 100644 --- a/library/core/tests/ptr.rs +++ b/library/core/tests/ptr.rs @@ -1,8 +1,6 @@ use core::cell::RefCell; -#[cfg(not(bootstrap))] use core::ptr; use core::ptr::*; -#[cfg(not(bootstrap))] use std::fmt::{Debug, Display}; #[test] @@ -419,7 +417,6 @@ fn offset_from() { } #[test] -#[cfg(not(bootstrap))] fn ptr_metadata() { struct Unit; struct Pair(A, B); @@ -478,7 +475,6 @@ fn ptr_metadata() { } #[test] -#[cfg(not(bootstrap))] fn ptr_metadata_bounds() { fn metadata_eq_method_address() -> usize { // The `Metadata` associated type has an `Ord` bound, so this is valid: @@ -510,7 +506,6 @@ fn ptr_metadata_bounds() { } #[test] -#[cfg(not(bootstrap))] fn dyn_metadata() { #[derive(Debug)] #[repr(align(32))] @@ -530,7 +525,6 @@ fn dyn_metadata() { } #[test] -#[cfg(not(bootstrap))] fn from_raw_parts() { let mut value = 5_u32; let address = &mut value as *mut _ as *mut (); @@ -557,7 +551,6 @@ fn from_raw_parts() { } #[test] -#[cfg(not(bootstrap))] fn thin_box() { let foo = ThinBox::::new(4); assert_eq!(foo.to_string(), "4"); diff --git a/library/core/tests/result.rs b/library/core/tests/result.rs index 5fcd7b4d3a327..c461ab380ad3d 100644 --- a/library/core/tests/result.rs +++ b/library/core/tests/result.rs @@ -225,6 +225,28 @@ pub fn test_into_ok() { assert_eq!(infallible_op2().into_ok(), 667); } +#[test] +pub fn test_into_err() { + fn until_error_op() -> Result { + Err(666) + } + + assert_eq!(until_error_op().into_err(), 666); + + enum MyNeverToken {} + impl From for ! { + fn from(never: MyNeverToken) -> ! { + match never {} + } + } + + fn until_error_op2() -> Result { + Err(667) + } + + assert_eq!(until_error_op2().into_err(), 667); +} + #[test] fn test_try() { fn try_result_some() -> Option { diff --git a/library/core/tests/slice.rs b/library/core/tests/slice.rs index 9ccc5a08dcbea..3a98cd9d2ee91 100644 --- a/library/core/tests/slice.rs +++ b/library/core/tests/slice.rs @@ -1,4 +1,5 @@ use core::cell::Cell; +use core::cmp::Ordering; use core::result::Result::{Err, Ok}; #[test] @@ -64,6 +65,17 @@ fn test_binary_search() { assert_eq!(b.binary_search(&6), Err(4)); assert_eq!(b.binary_search(&7), Ok(4)); assert_eq!(b.binary_search(&8), Err(5)); + + let b = [(); usize::MAX]; + assert_eq!(b.binary_search(&()), Ok(usize::MAX / 2)); +} + +#[test] +fn test_binary_search_by_overflow() { + let b = [(); usize::MAX]; + assert_eq!(b.binary_search_by(|_| Ordering::Equal), Ok(usize::MAX / 2)); + assert_eq!(b.binary_search_by(|_| Ordering::Greater), Err(0)); + assert_eq!(b.binary_search_by(|_| Ordering::Less), Err(usize::MAX)); } #[test] @@ -73,13 +85,13 @@ fn test_binary_search_implementation_details() { let b = [1, 1, 2, 2, 3, 3, 3]; assert_eq!(b.binary_search(&1), Ok(1)); assert_eq!(b.binary_search(&2), Ok(3)); - assert_eq!(b.binary_search(&3), Ok(6)); + assert_eq!(b.binary_search(&3), Ok(5)); let b = [1, 1, 1, 1, 1, 3, 3, 3, 3]; assert_eq!(b.binary_search(&1), Ok(4)); - assert_eq!(b.binary_search(&3), Ok(8)); + assert_eq!(b.binary_search(&3), Ok(7)); let b = [1, 1, 1, 1, 3, 3, 3, 3, 3]; - assert_eq!(b.binary_search(&1), Ok(3)); - assert_eq!(b.binary_search(&3), Ok(8)); + assert_eq!(b.binary_search(&1), Ok(2)); + assert_eq!(b.binary_search(&3), Ok(4)); } #[test] @@ -1268,6 +1280,9 @@ mod slice_index { } )*) => {$( mod $case_name { + #[allow(unused_imports)] + use core::ops::Bound; + #[test] fn pass() { let mut v = $data; @@ -1364,6 +1379,24 @@ mod slice_index { bad: data[7..=6]; message: "out of range"; } + + in mod boundpair_len { + data: [0, 1, 2, 3, 4, 5]; + + good: data[(Bound::Included(6), Bound::Unbounded)] == []; + good: data[(Bound::Unbounded, Bound::Included(5))] == [0, 1, 2, 3, 4, 5]; + good: data[(Bound::Unbounded, Bound::Excluded(6))] == [0, 1, 2, 3, 4, 5]; + good: data[(Bound::Included(0), Bound::Included(5))] == [0, 1, 2, 3, 4, 5]; + good: data[(Bound::Included(0), Bound::Excluded(6))] == [0, 1, 2, 3, 4, 5]; + good: data[(Bound::Included(2), Bound::Excluded(4))] == [2, 3]; + good: data[(Bound::Excluded(1), Bound::Included(4))] == [2, 3, 4]; + good: data[(Bound::Excluded(5), Bound::Excluded(6))] == []; + good: data[(Bound::Included(6), Bound::Excluded(6))] == []; + good: data[(Bound::Excluded(5), Bound::Included(5))] == []; + good: data[(Bound::Included(6), Bound::Included(5))] == []; + bad: data[(Bound::Unbounded, Bound::Included(6))]; + message: "out of range"; + } } panic_cases! { @@ -1404,6 +1437,14 @@ mod slice_index { bad: data[4..=2]; message: "but ends at"; } + + in mod boundpair_neg_width { + data: [0, 1, 2, 3, 4, 5]; + + good: data[(Bound::Included(4), Bound::Excluded(4))] == []; + bad: data[(Bound::Included(4), Bound::Excluded(3))]; + message: "but ends at"; + } } panic_cases! { @@ -1422,6 +1463,20 @@ mod slice_index { bad: data[..= usize::MAX]; message: "maximum usize"; } + + in mod boundpair_overflow_end { + data: [0; 1]; + + bad: data[(Bound::Unbounded, Bound::Included(usize::MAX))]; + message: "maximum usize"; + } + + in mod boundpair_overflow_start { + data: [0; 1]; + + bad: data[(Bound::Excluded(usize::MAX), Bound::Unbounded)]; + message: "maximum usize"; + } } // panic_cases! } @@ -1982,6 +2037,7 @@ fn test_copy_within_panics_dest_too_long() { // The length is only 13, so a slice of length 4 starting at index 10 is out of bounds. bytes.copy_within(0..4, 10); } + #[test] #[should_panic(expected = "slice index starts at 2 but ends at 1")] fn test_copy_within_panics_src_inverted() { diff --git a/library/panic_abort/Cargo.toml b/library/panic_abort/Cargo.toml index b15919fad75e7..bdab664cd64fc 100644 --- a/library/panic_abort/Cargo.toml +++ b/library/panic_abort/Cargo.toml @@ -2,6 +2,9 @@ authors = ["The Rust Project Developers"] name = "panic_abort" 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" [lib] @@ -10,6 +13,7 @@ bench = false doc = false [dependencies] +alloc = { path = "../alloc" } cfg-if = { version = "0.1.8", features = ['rustc-dep-of-std'] } core = { path = "../core" } libc = { version = "0.2", default-features = false } diff --git a/library/panic_abort/src/android.rs b/library/panic_abort/src/android.rs new file mode 100644 index 0000000000000..34d77502eabe5 --- /dev/null +++ b/library/panic_abort/src/android.rs @@ -0,0 +1,49 @@ +use alloc::string::String; +use core::mem::transmute; +use core::panic::BoxMeUp; +use core::ptr::copy_nonoverlapping; + +const ANDROID_SET_ABORT_MESSAGE: &[u8] = b"android_set_abort_message\0"; +type SetAbortMessageType = unsafe extern "C" fn(*const libc::c_char) -> (); + +// Forward the abort message to libc's android_set_abort_message. We try our best to populate the +// message but as this function may already be called as part of a failed allocation, it may not be +// possible to do so. +// +// Some methods of core are on purpose avoided (such as try_reserve) as these rely on the correct +// resolution of rust_eh_personality which is loosely defined in panic_abort. +// +// Weakly resolve the symbol for android_set_abort_message. This function is only available +// for API >= 21. +pub(crate) unsafe fn android_set_abort_message(payload: *mut &mut dyn BoxMeUp) { + let func_addr = + libc::dlsym(libc::RTLD_DEFAULT, ANDROID_SET_ABORT_MESSAGE.as_ptr() as *const libc::c_char) + as usize; + if func_addr == 0 { + return; + } + + let payload = (*payload).get(); + let msg = match payload.downcast_ref::<&'static str>() { + Some(msg) => msg.as_bytes(), + None => match payload.downcast_ref::() { + Some(msg) => msg.as_bytes(), + None => &[], + }, + }; + if msg.is_empty() { + return; + } + + // Allocate a new buffer to append the null byte. + let size = msg.len() + 1usize; + let buf = libc::malloc(size) as *mut libc::c_char; + if buf.is_null() { + return; // allocation failure + } + copy_nonoverlapping(msg.as_ptr(), buf as *mut u8, msg.len()); + buf.offset(msg.len() as isize).write(0); + + let func = transmute::(func_addr); + func(buf); +} diff --git a/library/panic_abort/src/lib.rs b/library/panic_abort/src/lib.rs index eb2277d8baacd..5dcd1e6af3659 100644 --- a/library/panic_abort/src/lib.rs +++ b/library/panic_abort/src/lib.rs @@ -19,6 +19,9 @@ #![feature(rustc_attrs)] #![feature(asm)] +#[cfg(target_os = "android")] +mod android; + use core::any::Any; use core::panic::BoxMeUp; @@ -31,6 +34,10 @@ pub unsafe extern "C" fn __rust_panic_cleanup(_: *mut u8) -> *mut (dyn Any + Sen // "Leak" the payload and shim to the relevant abort on the platform in question. #[rustc_std_internal_symbol] pub unsafe extern "C" fn __rust_start_panic(_payload: *mut &mut dyn BoxMeUp) -> u32 { + // Android has the ability to attach a message as part of the abort. + #[cfg(target_os = "android")] + android::android_set_abort_message(_payload); + abort(); cfg_if::cfg_if! { diff --git a/library/panic_unwind/Cargo.toml b/library/panic_unwind/Cargo.toml index d27ba9876416d..533f059a85e45 100644 --- a/library/panic_unwind/Cargo.toml +++ b/library/panic_unwind/Cargo.toml @@ -2,6 +2,9 @@ authors = ["The Rust Project Developers"] name = "panic_unwind" 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" [lib] diff --git a/library/panic_unwind/src/lib.rs b/library/panic_unwind/src/lib.rs index 9ce9c477ec0f0..99a0c67fc11b9 100644 --- a/library/panic_unwind/src/lib.rs +++ b/library/panic_unwind/src/lib.rs @@ -18,7 +18,6 @@ issue_tracker_base_url = "https://github.com/rust-lang/rust/issues/" )] #![feature(core_intrinsics)] -#![feature(int_bits_const)] #![feature(lang_items)] #![feature(nll)] #![feature(panic_unwind)] diff --git a/library/profiler_builtins/build.rs b/library/profiler_builtins/build.rs index 2de6db762b2fb..1b1f11798d74d 100644 --- a/library/profiler_builtins/build.rs +++ b/library/profiler_builtins/build.rs @@ -24,13 +24,11 @@ fn main() { "InstrProfilingPlatformLinux.c", "InstrProfilingPlatformOther.c", "InstrProfilingPlatformWindows.c", + "InstrProfilingRuntime.cpp", "InstrProfilingUtil.c", "InstrProfilingValue.c", "InstrProfilingVersionVar.c", "InstrProfilingWriter.c", - // This file was renamed in LLVM 10. - "InstrProfilingRuntime.cc", - "InstrProfilingRuntime.cpp", // These files were added in LLVM 11. "InstrProfilingInternal.c", "InstrProfilingBiasVar.c", diff --git a/library/std/Cargo.toml b/library/std/Cargo.toml index 275fcc4c29299..ab7b142ef3db0 100644 --- a/library/std/Cargo.toml +++ b/library/std/Cargo.toml @@ -16,11 +16,12 @@ cfg-if = { version = "0.1.8", features = ['rustc-dep-of-std'] } panic_unwind = { path = "../panic_unwind", optional = true } panic_abort = { path = "../panic_abort" } core = { path = "../core" } -libc = { version = "0.2.85", default-features = false, features = ['rustc-dep-of-std'] } +libc = { version = "0.2.93", default-features = false, features = ['rustc-dep-of-std'] } compiler_builtins = { version = "0.1.39" } profiler_builtins = { path = "../profiler_builtins", optional = true } unwind = { path = "../unwind" } -hashbrown = { version = "0.9.0", default-features = false, features = ['rustc-dep-of-std'] } +hashbrown = { version = "0.11", default-features = false, features = ['rustc-dep-of-std'] } +std_detect = { path = "../stdarch/crates/std_detect", default-features = false, features = ['rustc-dep-of-std'] } # Dependencies of the `backtrace` crate addr2line = { version = "0.14.0", optional = true, default-features = false } @@ -70,8 +71,8 @@ panic_immediate_abort = ["core/panic_immediate_abort"] # Enable std_detect default features for stdarch/crates/std_detect: # https://github.com/rust-lang/stdarch/blob/master/crates/std_detect/Cargo.toml -std_detect_file_io = [] -std_detect_dlsym_getauxval = [] +std_detect_file_io = ["std_detect/std_detect_file_io"] +std_detect_dlsym_getauxval = ["std_detect/std_detect_dlsym_getauxval"] [package.metadata.fortanix-sgx] # Maximum possible number of threads when testing diff --git a/library/std/src/collections/hash/map.rs b/library/std/src/collections/hash/map.rs index 27f7191831d41..bdf559847cc85 100644 --- a/library/std/src/collections/hash/map.rs +++ b/library/std/src/collections/hash/map.rs @@ -1,3 +1,5 @@ +// ignore-tidy-filelength + #[cfg(test)] mod tests; @@ -15,7 +17,7 @@ use crate::iter::{FromIterator, FusedIterator}; use crate::ops::Index; use crate::sys; -/// A hash map implemented with quadratic probing and SIMD lookup. +/// A [hash map] implemented with quadratic probing and SIMD lookup. /// /// By default, `HashMap` uses a hashing algorithm selected to provide /// resistance against HashDoS attacks. The algorithm is randomly seeded, and a @@ -60,6 +62,7 @@ use crate::sys; /// The original C++ version of SwissTable can be found [here], and this /// [CppCon talk] gives an overview of how the algorithm works. /// +/// [hash map]: crate::collections#use-a-hashmap-when /// [hashing algorithms available on crates.io]: https://crates.io/keywords/hasher /// [SwissTable]: https://abseil.io/blog/20180927-swisstables /// [here]: https://github.com/abseil/abseil-cpp/blob/master/absl/container/internal/raw_hash_set.h @@ -842,6 +845,37 @@ where self.base.insert(k, v) } + /// Tries to insert a key-value pair into the map, and returns + /// a mutable reference to the value in the entry. + /// + /// If the map already had this key present, nothing is updated, and + /// an error containing the occupied entry and the value is returned. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// #![feature(map_try_insert)] + /// + /// use std::collections::HashMap; + /// + /// let mut map = HashMap::new(); + /// assert_eq!(map.try_insert(37, "a").unwrap(), &"a"); + /// + /// let err = map.try_insert(37, "b").unwrap_err(); + /// assert_eq!(err.entry.key(), &37); + /// assert_eq!(err.entry.get(), &"a"); + /// assert_eq!(err.value, "b"); + /// ``` + #[unstable(feature = "map_try_insert", issue = "82766")] + pub fn try_insert(&mut self, key: K, value: V) -> Result<&mut V, OccupiedError<'_, K, V>> { + match self.entry(key) { + Occupied(entry) => Err(OccupiedError { entry, value }), + Vacant(entry) => Ok(entry.insert(value)), + } + } + /// Removes a key from the map, returning the value at the key if the key /// was previously in the map. /// @@ -1759,7 +1793,7 @@ impl<'a, K, V, S> RawVacantEntryMut<'a, K, V, S> { #[unstable(feature = "hash_raw_entry", issue = "56167")] impl Debug for RawEntryBuilderMut<'_, K, V, S> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("RawEntryBuilder").finish() + f.debug_struct("RawEntryBuilder").finish_non_exhaustive() } } @@ -1779,21 +1813,21 @@ impl Debug for RawOccupiedEntryMut<'_, K, V, S> { f.debug_struct("RawOccupiedEntryMut") .field("key", self.key()) .field("value", self.get()) - .finish() + .finish_non_exhaustive() } } #[unstable(feature = "hash_raw_entry", issue = "56167")] impl Debug for RawVacantEntryMut<'_, K, V, S> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("RawVacantEntryMut").finish() + f.debug_struct("RawVacantEntryMut").finish_non_exhaustive() } } #[unstable(feature = "hash_raw_entry", issue = "56167")] impl Debug for RawEntryBuilder<'_, K, V, S> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("RawEntryBuilder").finish() + f.debug_struct("RawEntryBuilder").finish_non_exhaustive() } } @@ -1833,7 +1867,10 @@ pub struct OccupiedEntry<'a, K: 'a, V: 'a> { #[stable(feature = "debug_hash_map", since = "1.12.0")] impl Debug for OccupiedEntry<'_, K, V> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("OccupiedEntry").field("key", self.key()).field("value", self.get()).finish() + f.debug_struct("OccupiedEntry") + .field("key", self.key()) + .field("value", self.get()) + .finish_non_exhaustive() } } @@ -1851,6 +1888,41 @@ impl Debug for VacantEntry<'_, K, V> { } } +/// The error returned by [`try_insert`](HashMap::try_insert) when the key already exists. +/// +/// Contains the occupied entry, and the value that was not inserted. +#[unstable(feature = "map_try_insert", issue = "82766")] +pub struct OccupiedError<'a, K: 'a, V: 'a> { + /// The entry in the map that was already occupied. + pub entry: OccupiedEntry<'a, K, V>, + /// The value which was not inserted, because the entry was already occupied. + pub value: V, +} + +#[unstable(feature = "map_try_insert", issue = "82766")] +impl Debug for OccupiedError<'_, K, V> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("OccupiedError") + .field("key", self.entry.key()) + .field("old_value", self.entry.get()) + .field("new_value", &self.value) + .finish_non_exhaustive() + } +} + +#[unstable(feature = "map_try_insert", issue = "82766")] +impl<'a, K: Debug, V: Debug> fmt::Display for OccupiedError<'a, K, V> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!( + f, + "failed to insert {:?}, key {:?} already exists with value {:?}", + self.value, + self.entry.key(), + self.entry.get(), + ) + } +} + #[stable(feature = "rust1", since = "1.0.0")] impl<'a, K, V, S> IntoIterator for &'a HashMap { type Item = (&'a K, &'a V); @@ -2185,7 +2257,7 @@ where F: FnMut(&K, &mut V) -> bool, { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.pad("DrainFilter { .. }") + f.debug_struct("DrainFilter").finish_non_exhaustive() } } @@ -2885,7 +2957,7 @@ impl Default for RandomState { #[stable(feature = "std_debug", since = "1.16.0")] impl fmt::Debug for RandomState { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.pad("RandomState { .. }") + f.debug_struct("RandomState").finish_non_exhaustive() } } diff --git a/library/std/src/collections/hash/map/tests.rs b/library/std/src/collections/hash/map/tests.rs index 467968354e25d..819be14222752 100644 --- a/library/std/src/collections/hash/map/tests.rs +++ b/library/std/src/collections/hash/map/tests.rs @@ -774,11 +774,11 @@ fn test_occupied_entry_key() { let key = "hello there"; let value = "value goes here"; assert!(a.is_empty()); - a.insert(key.clone(), value.clone()); + a.insert(key, value); assert_eq!(a.len(), 1); assert_eq!(a[key], value); - match a.entry(key.clone()) { + match a.entry(key) { Vacant(_) => panic!(), Occupied(e) => assert_eq!(key, *e.key()), } @@ -793,11 +793,11 @@ fn test_vacant_entry_key() { let value = "value goes here"; assert!(a.is_empty()); - match a.entry(key.clone()) { + match a.entry(key) { Occupied(_) => panic!(), Vacant(e) => { assert_eq!(key, *e.key()); - e.insert(value.clone()); + e.insert(value); } } assert_eq!(a.len(), 1); diff --git a/library/std/src/collections/hash/set.rs b/library/std/src/collections/hash/set.rs index 912e975aa0a4c..5220c8ad70977 100644 --- a/library/std/src/collections/hash/set.rs +++ b/library/std/src/collections/hash/set.rs @@ -19,7 +19,7 @@ use super::map::{map_try_reserve_error, RandomState}; // for `bucket.val` in the case of HashSet. I suppose we would need HKT // to get rid of it properly. -/// A hash set implemented as a `HashMap` where the value is `()`. +/// A [hash set] implemented as a `HashMap` where the value is `()`. /// /// As with the [`HashMap`] type, a `HashSet` requires that the elements /// implement the [`Eq`] and [`Hash`] traits. This can frequently be achieved by @@ -105,6 +105,7 @@ use super::map::{map_try_reserve_error, RandomState}; /// // use the values stored in the set /// ``` /// +/// [hash set]: crate::collections#use-the-set-variant-of-any-of-these-maps-when /// [`HashMap`]: crate::collections::HashMap /// [`RefCell`]: crate::cell::RefCell /// [`Cell`]: crate::cell::Cell @@ -1532,7 +1533,7 @@ where F: FnMut(&K) -> bool, { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.pad("DrainFilter { .. }") + f.debug_struct("DrainFilter").finish_non_exhaustive() } } diff --git a/library/std/src/collections/mod.rs b/library/std/src/collections/mod.rs index 80a13d52a2a27..8cda601edd141 100644 --- a/library/std/src/collections/mod.rs +++ b/library/std/src/collections/mod.rs @@ -401,9 +401,11 @@ #![stable(feature = "rust1", since = "1.0.0")] #[stable(feature = "rust1", since = "1.0.0")] +// FIXME(#82080) The deprecation here is only theoretical, and does not actually produce a warning. #[rustc_deprecated(reason = "moved to `std::ops::Bound`", since = "1.26.0")] #[doc(hidden)] pub use crate::ops::Bound; + #[stable(feature = "rust1", since = "1.0.0")] pub use alloc_crate::collections::{binary_heap, btree_map, btree_set}; #[stable(feature = "rust1", since = "1.0.0")] diff --git a/library/std/src/env.rs b/library/std/src/env.rs index 9763a2da34151..5fa092af1da02 100644 --- a/library/std/src/env.rs +++ b/library/std/src/env.rs @@ -154,7 +154,7 @@ impl Iterator for Vars { #[stable(feature = "std_debug", since = "1.16.0")] impl fmt::Debug for Vars { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.pad("Vars { .. }") + f.debug_struct("Vars").finish_non_exhaustive() } } @@ -172,7 +172,7 @@ impl Iterator for VarsOs { #[stable(feature = "std_debug", since = "1.16.0")] impl fmt::Debug for VarsOs { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.pad("VarsOs { .. }") + f.debug_struct("VarOs").finish_non_exhaustive() } } @@ -419,7 +419,7 @@ impl<'a> Iterator for SplitPaths<'a> { #[stable(feature = "std_debug", since = "1.16.0")] impl fmt::Debug for SplitPaths<'_> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.pad("SplitPaths { .. }") + f.debug_struct("SplitPaths").finish_non_exhaustive() } } @@ -799,7 +799,7 @@ impl DoubleEndedIterator for Args { #[stable(feature = "std_debug", since = "1.16.0")] impl fmt::Debug for Args { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("Args").field("inner", &self.inner.inner.inner_debug()).finish() + f.debug_struct("Args").field("inner", &self.inner.inner).finish() } } @@ -840,7 +840,7 @@ impl DoubleEndedIterator for ArgsOs { #[stable(feature = "std_debug", since = "1.16.0")] impl fmt::Debug for ArgsOs { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("ArgsOs").field("inner", &self.inner.inner_debug()).finish() + f.debug_struct("ArgsOs").field("inner", &self.inner).finish() } } diff --git a/library/std/src/error.rs b/library/std/src/error.rs index 94338c7b04d06..14c2f961d3261 100644 --- a/library/std/src/error.rs +++ b/library/std/src/error.rs @@ -33,15 +33,22 @@ use crate::string; use crate::sync::Arc; /// `Error` is a trait representing the basic expectations for error values, -/// i.e., values of type `E` in [`Result`]. Errors must describe -/// themselves through the [`Display`] and [`Debug`] traits, and may provide -/// cause chain information: +/// i.e., values of type `E` in [`Result`]. /// -/// [`Error::source()`] is generally used when errors cross -/// "abstraction boundaries". If one module must report an error that is caused -/// by an error from a lower-level module, it can allow accessing that error -/// via [`Error::source()`]. This makes it possible for the high-level -/// module to provide its own errors while also revealing some of the +/// Errors must describe themselves through the [`Display`] and [`Debug`] +/// traits. Error messages are typically concise lowercase sentences without +/// trailing punctuation: +/// +/// ``` +/// let err = "NaN".parse::().unwrap_err(); +/// assert_eq!(err.to_string(), "invalid digit found in string"); +/// ``` +/// +/// Errors may provide cause chain information. [`Error::source()`] is generally +/// used when errors cross "abstraction boundaries". If one module must report +/// an error that is caused by an error from a lower-level module, it can allow +/// accessing that error via [`Error::source()`]. This makes it possible for the +/// high-level module to provide its own errors while also revealing some of the /// implementation for debugging via `source` chains. #[stable(feature = "rust1", since = "1.0.0")] pub trait Error: Debug + Display { @@ -470,6 +477,24 @@ impl Error for char::DecodeUtf16Error { } } +#[unstable(feature = "map_try_insert", issue = "82766")] +impl<'a, K: Debug + Ord, V: Debug> Error + for crate::collections::btree_map::OccupiedError<'a, K, V> +{ + #[allow(deprecated)] + fn description(&self) -> &str { + "key already exists" + } +} + +#[unstable(feature = "map_try_insert", issue = "82766")] +impl<'a, K: Debug, V: Debug> Error for crate::collections::hash_map::OccupiedError<'a, K, V> { + #[allow(deprecated)] + fn description(&self) -> &str { + "key already exists" + } +} + #[stable(feature = "box_error", since = "1.8.0")] impl Error for Box { #[allow(deprecated, deprecated_in_future)] diff --git a/library/std/src/ffi/c_str.rs b/library/std/src/ffi/c_str.rs index 945bfda1b7874..ed4950c57a627 100644 --- a/library/std/src/ffi/c_str.rs +++ b/library/std/src/ffi/c_str.rs @@ -613,7 +613,8 @@ impl CString { #[inline] #[stable(feature = "rust1", since = "1.0.0")] pub fn as_bytes(&self) -> &[u8] { - &self.inner[..self.inner.len() - 1] + // SAFETY: CString has a length at least 1 + unsafe { self.inner.get_unchecked(..self.inner.len() - 1) } } /// Equivalent to [`CString::as_bytes()`] except that the @@ -1036,7 +1037,7 @@ impl fmt::Display for NulError { impl From for io::Error { /// Converts a [`NulError`] into a [`io::Error`]. fn from(_: NulError) -> io::Error { - io::Error::new(io::ErrorKind::InvalidInput, "data provided contains a nul byte") + io::Error::new_const(io::ErrorKind::InvalidInput, &"data provided contains a nul byte") } } @@ -1322,7 +1323,8 @@ impl CStr { #[stable(feature = "rust1", since = "1.0.0")] pub fn to_bytes(&self) -> &[u8] { let bytes = self.to_bytes_with_nul(); - &bytes[..bytes.len() - 1] + // SAFETY: to_bytes_with_nul returns slice with length at least 1 + unsafe { bytes.get_unchecked(..bytes.len() - 1) } } /// Converts this C string to a byte slice containing the trailing 0 byte. diff --git a/library/std/src/ffi/c_str/tests.rs b/library/std/src/ffi/c_str/tests.rs index 4dff3df63a8b6..4f7ba9ad43756 100644 --- a/library/std/src/ffi/c_str/tests.rs +++ b/library/std/src/ffi/c_str/tests.rs @@ -193,3 +193,19 @@ fn cstr_index_from_empty() { let cstr = CStr::from_bytes_with_nul(original).unwrap(); let _ = &cstr[original.len()..]; } + +#[test] +fn c_string_from_empty_string() { + let original = ""; + let cstring = CString::new(original).unwrap(); + assert_eq!(original.as_bytes(), cstring.as_bytes()); + assert_eq!([b'\0'], cstring.as_bytes_with_nul()); +} + +#[test] +fn c_str_from_empty_string() { + let original = b"\0"; + let cstr = CStr::from_bytes_with_nul(original).unwrap(); + assert_eq!([] as [u8; 0], cstr.to_bytes()); + assert_eq!([b'\0'], cstr.to_bytes_with_nul()); +} diff --git a/library/std/src/ffi/os_str.rs b/library/std/src/ffi/os_str.rs index 272eccda89471..ecaab67034908 100644 --- a/library/std/src/ffi/os_str.rs +++ b/library/std/src/ffi/os_str.rs @@ -5,6 +5,7 @@ use crate::borrow::{Borrow, Cow}; use crate::cmp; use crate::fmt; use crate::hash::{Hash, Hasher}; +use crate::iter::{Extend, FromIterator}; use crate::ops; use crate::rc::Rc; use crate::str::FromStr; @@ -360,7 +361,7 @@ impl OsString { impl From for OsString { /// Converts a [`String`] into a [`OsString`]. /// - /// The conversion copies the data, and includes an allocation on the heap. + /// This conversion does not allocate or copy memory. #[inline] fn from(s: String) -> OsString { OsString { inner: Buf::from_string(s) } @@ -715,7 +716,6 @@ impl OsStr { /// # Examples /// /// ``` - /// #![feature(osstring_ascii)] /// use std::ffi::OsString; /// /// let mut s = OsString::from("GRÜßE, JÜRGEN ❤"); @@ -724,7 +724,7 @@ impl OsStr { /// /// assert_eq!("grÜße, jÜrgen ❤", s); /// ``` - #[unstable(feature = "osstring_ascii", issue = "70516")] + #[stable(feature = "osstring_ascii", since = "1.53.0")] #[inline] pub fn make_ascii_lowercase(&mut self) { self.inner.make_ascii_lowercase() @@ -741,7 +741,6 @@ impl OsStr { /// # Examples /// /// ``` - /// #![feature(osstring_ascii)] /// use std::ffi::OsString; /// /// let mut s = OsString::from("Grüße, Jürgen ❤"); @@ -750,7 +749,7 @@ impl OsStr { /// /// assert_eq!("GRüßE, JüRGEN ❤", s); /// ``` - #[unstable(feature = "osstring_ascii", issue = "70516")] + #[stable(feature = "osstring_ascii", since = "1.53.0")] #[inline] pub fn make_ascii_uppercase(&mut self) { self.inner.make_ascii_uppercase() @@ -767,13 +766,12 @@ impl OsStr { /// # Examples /// /// ``` - /// #![feature(osstring_ascii)] /// use std::ffi::OsString; /// let s = OsString::from("Grüße, Jürgen ❤"); /// /// assert_eq!("grüße, jürgen ❤", s.to_ascii_lowercase()); /// ``` - #[unstable(feature = "osstring_ascii", issue = "70516")] + #[stable(feature = "osstring_ascii", since = "1.53.0")] pub fn to_ascii_lowercase(&self) -> OsString { OsString::from_inner(self.inner.to_ascii_lowercase()) } @@ -789,13 +787,12 @@ impl OsStr { /// # Examples /// /// ``` - /// #![feature(osstring_ascii)] /// use std::ffi::OsString; /// let s = OsString::from("Grüße, Jürgen ❤"); /// /// assert_eq!("GRüßE, JüRGEN ❤", s.to_ascii_uppercase()); /// ``` - #[unstable(feature = "osstring_ascii", issue = "70516")] + #[stable(feature = "osstring_ascii", since = "1.53.0")] pub fn to_ascii_uppercase(&self) -> OsString { OsString::from_inner(self.inner.to_ascii_uppercase()) } @@ -805,7 +802,6 @@ impl OsStr { /// # Examples /// /// ``` - /// #![feature(osstring_ascii)] /// use std::ffi::OsString; /// /// let ascii = OsString::from("hello!\n"); @@ -814,7 +810,7 @@ impl OsStr { /// assert!(ascii.is_ascii()); /// assert!(!non_ascii.is_ascii()); /// ``` - #[unstable(feature = "osstring_ascii", issue = "70516")] + #[stable(feature = "osstring_ascii", since = "1.53.0")] #[inline] pub fn is_ascii(&self) -> bool { self.inner.is_ascii() @@ -828,14 +824,13 @@ impl OsStr { /// # Examples /// /// ``` - /// #![feature(osstring_ascii)] /// use std::ffi::OsString; /// /// assert!(OsString::from("Ferris").eq_ignore_ascii_case("FERRIS")); /// assert!(OsString::from("Ferrös").eq_ignore_ascii_case("FERRöS")); /// assert!(!OsString::from("Ferrös").eq_ignore_ascii_case("FERRÖS")); /// ``` - #[unstable(feature = "osstring_ascii", issue = "70516")] + #[stable(feature = "osstring_ascii", since = "1.53.0")] pub fn eq_ignore_ascii_case>(&self, other: S) -> bool { self.inner.eq_ignore_ascii_case(&other.as_ref().inner) } @@ -863,7 +858,7 @@ impl From> for Box { #[stable(feature = "os_string_from_box", since = "1.18.0")] impl From> for OsString { - /// Converts a [`Box`]`<`[`OsStr`]`>` into a `OsString` without copying or + /// Converts a [`Box`]`<`[`OsStr`]`>` into an [`OsString`] without copying or /// allocating. #[inline] fn from(boxed: Box) -> OsString { @@ -1192,3 +1187,88 @@ impl FromStr for OsString { Ok(OsString::from(s)) } } + +#[stable(feature = "osstring_extend", since = "1.52.0")] +impl Extend for OsString { + #[inline] + fn extend>(&mut self, iter: T) { + for s in iter { + self.push(&s); + } + } +} + +#[stable(feature = "osstring_extend", since = "1.52.0")] +impl<'a> Extend<&'a OsStr> for OsString { + #[inline] + fn extend>(&mut self, iter: T) { + for s in iter { + self.push(s); + } + } +} + +#[stable(feature = "osstring_extend", since = "1.52.0")] +impl<'a> Extend> for OsString { + #[inline] + fn extend>>(&mut self, iter: T) { + for s in iter { + self.push(&s); + } + } +} + +#[stable(feature = "osstring_extend", since = "1.52.0")] +impl FromIterator for OsString { + #[inline] + fn from_iter>(iter: I) -> Self { + let mut iterator = iter.into_iter(); + + // Because we're iterating over `OsString`s, we can avoid at least + // one allocation by getting the first string from the iterator + // and appending to it all the subsequent strings. + match iterator.next() { + None => OsString::new(), + Some(mut buf) => { + buf.extend(iterator); + buf + } + } + } +} + +#[stable(feature = "osstring_extend", since = "1.52.0")] +impl<'a> FromIterator<&'a OsStr> for OsString { + #[inline] + fn from_iter>(iter: I) -> Self { + let mut buf = Self::new(); + for s in iter { + buf.push(s); + } + buf + } +} + +#[stable(feature = "osstring_extend", since = "1.52.0")] +impl<'a> FromIterator> for OsString { + #[inline] + fn from_iter>>(iter: I) -> Self { + let mut iterator = iter.into_iter(); + + // Because we're iterating over `OsString`s, we can avoid at least + // one allocation by getting the first owned string from the iterator + // and appending to it all the subsequent strings. + match iterator.next() { + None => OsString::new(), + Some(Cow::Owned(mut buf)) => { + buf.extend(iterator); + buf + } + Some(Cow::Borrowed(buf)) => { + let mut buf = OsString::from(buf); + buf.extend(iterator); + buf + } + } + } +} diff --git a/library/std/src/fs.rs b/library/std/src/fs.rs index 43119c36cfec7..e6120b8ee31c2 100644 --- a/library/std/src/fs.rs +++ b/library/std/src/fs.rs @@ -265,8 +265,9 @@ pub fn read>(path: P) -> io::Result> { /// ```no_run /// use std::fs; /// use std::net::SocketAddr; +/// use std::error::Error; /// -/// fn main() -> Result<(), Box> { +/// fn main() -> Result<(), Box> { /// let foo: SocketAddr = fs::read_to_string("address.txt")?.parse()?; /// Ok(()) /// } @@ -1154,7 +1155,7 @@ impl fmt::Debug for Metadata { .field("modified", &self.modified()) .field("accessed", &self.accessed()) .field("created", &self.created()) - .finish() + .finish_non_exhaustive() } } @@ -1677,9 +1678,9 @@ pub fn rename, Q: AsRef>(from: P, to: Q) -> io::Result<()> /// This function will return an error in the following situations, but is not /// limited to just these cases: /// -/// * The `from` path is not a file. -/// * The `from` file does not exist. -/// * The current process does not have the permission rights to access +/// * `from` is neither a regular file nor a symlink to a regular file. +/// * `from` does not exist. +/// * The current process does not have the permission rights to read /// `from` or write `to`. /// /// # Examples @@ -2188,7 +2189,10 @@ impl DirBuilder { match path.parent() { Some(p) => self.create_dir_all(p)?, None => { - return Err(io::Error::new(io::ErrorKind::Other, "failed to create whole tree")); + return Err(io::Error::new_const( + io::ErrorKind::Other, + &"failed to create whole tree", + )); } } match self.inner.mkdir(path) { diff --git a/library/std/src/fs/tests.rs b/library/std/src/fs/tests.rs index 5c969741592e6..ce8d3a56f7af4 100644 --- a/library/std/src/fs/tests.rs +++ b/library/std/src/fs/tests.rs @@ -1329,7 +1329,9 @@ fn metadata_access_times() { match (a.created(), b.created()) { (Ok(t1), Ok(t2)) => assert!(t1 <= t2), (Err(e1), Err(e2)) - if e1.kind() == ErrorKind::Other && e2.kind() == ErrorKind::Other => {} + if e1.kind() == ErrorKind::Other && e2.kind() == ErrorKind::Other + || e1.kind() == ErrorKind::Unsupported + && e2.kind() == ErrorKind::Unsupported => {} (a, b) => { panic!("creation time must be always supported or not supported: {:?} {:?}", a, b,) } diff --git a/library/std/src/io/buffered/bufreader.rs b/library/std/src/io/buffered/bufreader.rs index 987371f50ec22..d8021d3e99a70 100644 --- a/library/std/src/io/buffered/bufreader.rs +++ b/library/std/src/io/buffered/bufreader.rs @@ -1,6 +1,8 @@ use crate::cmp; use crate::fmt; -use crate::io::{self, BufRead, Initializer, IoSliceMut, Read, Seek, SeekFrom, DEFAULT_BUF_SIZE}; +use crate::io::{ + self, BufRead, Initializer, IoSliceMut, Read, Seek, SeekFrom, SizeHint, DEFAULT_BUF_SIZE, +}; /// The `BufReader` struct adds buffering to any reader. /// @@ -90,10 +92,9 @@ impl BufReader { #[stable(feature = "rust1", since = "1.0.0")] pub fn with_capacity(capacity: usize, inner: R) -> BufReader { unsafe { - let mut buffer = Vec::with_capacity(capacity); - buffer.set_len(capacity); - inner.initializer().initialize(&mut buffer); - BufReader { inner, buf: buffer.into_boxed_slice(), pos: 0, cap: 0 } + let mut buf = Box::new_uninit_slice(capacity).assume_init(); + inner.initializer().initialize(&mut buf); + BufReader { inner, buf, pos: 0, cap: 0 } } } } @@ -233,7 +234,7 @@ impl BufReader { /// the buffer will not be flushed, allowing for more efficient seeks. /// This method does not return the location of the underlying reader, so the caller /// must track this information themselves if it is required. - #[unstable(feature = "bufreader_seek_relative", issue = "31100")] + #[stable(feature = "bufreader_seek_relative", since = "1.53.0")] pub fn seek_relative(&mut self, offset: i64) -> io::Result<()> { let pos = self.pos as u64; if offset < 0 { @@ -435,3 +436,9 @@ impl Seek for BufReader { }) } } + +impl SizeHint for BufReader { + fn lower_bound(&self) -> usize { + self.buffer().len() + } +} diff --git a/library/std/src/io/buffered/bufwriter.rs b/library/std/src/io/buffered/bufwriter.rs index 65bc2fcf00ae0..80f98bbbad366 100644 --- a/library/std/src/io/buffered/bufwriter.rs +++ b/library/std/src/io/buffered/bufwriter.rs @@ -164,9 +164,9 @@ impl BufWriter { match r { Ok(0) => { - return Err(Error::new( + return Err(Error::new_const( ErrorKind::WriteZero, - "failed to write the buffered data", + &"failed to write the buffered data", )); } Ok(n) => guard.consume(n), diff --git a/library/std/src/io/buffered/linewriter.rs b/library/std/src/io/buffered/linewriter.rs index 502c6e3c6c0b9..d7b620d6f9177 100644 --- a/library/std/src/io/buffered/linewriter.rs +++ b/library/std/src/io/buffered/linewriter.rs @@ -227,6 +227,6 @@ where "buffer", &format_args!("{}/{}", self.inner.buffer().len(), self.inner.capacity()), ) - .finish() + .finish_non_exhaustive() } } diff --git a/library/std/src/io/cursor.rs b/library/std/src/io/cursor.rs index bbee2cc98425e..9021b47006538 100644 --- a/library/std/src/io/cursor.rs +++ b/library/std/src/io/cursor.rs @@ -229,9 +229,9 @@ where self.pos = n; Ok(self.pos) } - None => Err(Error::new( + None => Err(Error::new_const( ErrorKind::InvalidInput, - "invalid seek to a negative or overflowing position", + &"invalid seek to a negative or overflowing position", )), } } @@ -328,9 +328,9 @@ fn slice_write_vectored( // Resizing write implementation fn vec_write(pos_mut: &mut u64, vec: &mut Vec, buf: &[u8]) -> io::Result { let pos: usize = (*pos_mut).try_into().map_err(|_| { - Error::new( + Error::new_const( ErrorKind::InvalidInput, - "cursor position exceeds maximum possible vector length", + &"cursor position exceeds maximum possible vector length", ) })?; // Make sure the internal buffer is as least as big as where we diff --git a/library/std/src/io/error.rs b/library/std/src/io/error.rs index ba0f0a0cd714a..9bed12bf2ae2b 100644 --- a/library/std/src/io/error.rs +++ b/library/std/src/io/error.rs @@ -69,6 +69,8 @@ impl fmt::Debug for Error { enum Repr { Os(i32), Simple(ErrorKind), + // &str is a fat pointer, but &&str is a thin pointer. + SimpleMessage(ErrorKind, &'static &'static str), Custom(Box), } @@ -178,6 +180,12 @@ pub enum ErrorKind { /// read. #[stable(feature = "read_exact", since = "1.6.0")] UnexpectedEof, + + /// This operation is unsupported on this platform. + /// + /// This means that the operation can never succeed. + #[stable(feature = "unsupported_error", since = "1.53.0")] + Unsupported, } impl ErrorKind { @@ -201,6 +209,7 @@ impl ErrorKind { ErrorKind::Interrupted => "operation interrupted", ErrorKind::Other => "other os error", ErrorKind::UnexpectedEof => "unexpected end of file", + ErrorKind::Unsupported => "unsupported", } } } @@ -259,6 +268,19 @@ impl Error { Error { repr: Repr::Custom(Box::new(Custom { kind, error })) } } + /// Creates a new I/O error from a known kind of error as well as a + /// constant message. + /// + /// This function does not allocate. + /// + /// This function should maybe change to + /// `new_const(kind: ErrorKind)` + /// in the future, when const generics allow that. + #[inline] + pub(crate) const fn new_const(kind: ErrorKind, message: &'static &'static str) -> Error { + Self { repr: Repr::SimpleMessage(kind, message) } + } + /// Returns an error representing the last OS error which occurred. /// /// This function reads the value of `errno` for the target platform (e.g. @@ -273,6 +295,7 @@ impl Error { /// println!("last OS error: {:?}", Error::last_os_error()); /// ``` #[stable(feature = "rust1", since = "1.0.0")] + #[inline] pub fn last_os_error() -> Error { Error::from_raw_os_error(sys::os::errno() as i32) } @@ -303,6 +326,7 @@ impl Error { /// # } /// ``` #[stable(feature = "rust1", since = "1.0.0")] + #[inline] pub fn from_raw_os_error(code: i32) -> Error { Error { repr: Repr::Os(code) } } @@ -337,11 +361,13 @@ impl Error { /// } /// ``` #[stable(feature = "rust1", since = "1.0.0")] + #[inline] pub fn raw_os_error(&self) -> Option { match self.repr { Repr::Os(i) => Some(i), Repr::Custom(..) => None, Repr::Simple(..) => None, + Repr::SimpleMessage(..) => None, } } @@ -373,10 +399,12 @@ impl Error { /// } /// ``` #[stable(feature = "io_error_inner", since = "1.3.0")] + #[inline] pub fn get_ref(&self) -> Option<&(dyn error::Error + Send + Sync + 'static)> { match self.repr { Repr::Os(..) => None, Repr::Simple(..) => None, + Repr::SimpleMessage(..) => None, Repr::Custom(ref c) => Some(&*c.error), } } @@ -444,10 +472,12 @@ impl Error { /// } /// ``` #[stable(feature = "io_error_inner", since = "1.3.0")] + #[inline] pub fn get_mut(&mut self) -> Option<&mut (dyn error::Error + Send + Sync + 'static)> { match self.repr { Repr::Os(..) => None, Repr::Simple(..) => None, + Repr::SimpleMessage(..) => None, Repr::Custom(ref mut c) => Some(&mut *c.error), } } @@ -480,10 +510,12 @@ impl Error { /// } /// ``` #[stable(feature = "io_error_inner", since = "1.3.0")] + #[inline] pub fn into_inner(self) -> Option> { match self.repr { Repr::Os(..) => None, Repr::Simple(..) => None, + Repr::SimpleMessage(..) => None, Repr::Custom(c) => Some(c.error), } } @@ -507,11 +539,13 @@ impl Error { /// } /// ``` #[stable(feature = "rust1", since = "1.0.0")] + #[inline] pub fn kind(&self) -> ErrorKind { match self.repr { Repr::Os(code) => sys::decode_error_kind(code), Repr::Custom(ref c) => c.kind, Repr::Simple(kind) => kind, + Repr::SimpleMessage(kind, _) => kind, } } } @@ -527,6 +561,9 @@ impl fmt::Debug for Repr { .finish(), Repr::Custom(ref c) => fmt::Debug::fmt(&c, fmt), Repr::Simple(kind) => fmt.debug_tuple("Kind").field(&kind).finish(), + Repr::SimpleMessage(kind, &message) => { + fmt.debug_struct("Error").field("kind", &kind).field("message", &message).finish() + } } } } @@ -541,6 +578,7 @@ impl fmt::Display for Error { } Repr::Custom(ref c) => c.error.fmt(fmt), Repr::Simple(kind) => write!(fmt, "{}", kind.as_str()), + Repr::SimpleMessage(_, &msg) => msg.fmt(fmt), } } } @@ -551,6 +589,7 @@ impl error::Error for Error { fn description(&self) -> &str { match self.repr { Repr::Os(..) | Repr::Simple(..) => self.kind().as_str(), + Repr::SimpleMessage(_, &msg) => msg, Repr::Custom(ref c) => c.error.description(), } } @@ -560,6 +599,7 @@ impl error::Error for Error { match self.repr { Repr::Os(..) => None, Repr::Simple(..) => None, + Repr::SimpleMessage(..) => None, Repr::Custom(ref c) => c.error.cause(), } } @@ -568,6 +608,7 @@ impl error::Error for Error { match self.repr { Repr::Os(..) => None, Repr::Simple(..) => None, + Repr::SimpleMessage(..) => None, Repr::Custom(ref c) => c.error.source(), } } diff --git a/library/std/src/io/error/tests.rs b/library/std/src/io/error/tests.rs index 0cce9368c8089..5098a46313de3 100644 --- a/library/std/src/io/error/tests.rs +++ b/library/std/src/io/error/tests.rs @@ -1,9 +1,15 @@ use super::{Custom, Error, ErrorKind, Repr}; use crate::error; use crate::fmt; +use crate::mem::size_of; use crate::sys::decode_error_kind; use crate::sys::os::error_string; +#[test] +fn test_size() { + assert!(size_of::() <= size_of::<[usize; 2]>()); +} + #[test] fn test_debug_error() { let code = 6; @@ -51,3 +57,13 @@ fn test_downcasting() { let extracted = err.into_inner().unwrap(); extracted.downcast::().unwrap(); } + +#[test] +fn test_const() { + const E: Error = Error::new_const(ErrorKind::NotFound, &"hello"); + + assert_eq!(E.kind(), ErrorKind::NotFound); + assert_eq!(E.to_string(), "hello"); + assert!(format!("{:?}", E).contains("\"hello\"")); + assert!(format!("{:?}", E).contains("NotFound")); +} diff --git a/library/std/src/io/impls.rs b/library/std/src/io/impls.rs index 00bf8b9af7384..6891bd8a66437 100644 --- a/library/std/src/io/impls.rs +++ b/library/std/src/io/impls.rs @@ -1,6 +1,7 @@ #[cfg(test)] mod tests; +use crate::alloc::Allocator; use crate::cmp; use crate::fmt; use crate::io::{ @@ -262,7 +263,7 @@ impl Read for &[u8] { #[inline] fn read_exact(&mut self, buf: &mut [u8]) -> io::Result<()> { if buf.len() > self.len() { - return Err(Error::new(ErrorKind::UnexpectedEof, "failed to fill whole buffer")); + return Err(Error::new_const(ErrorKind::UnexpectedEof, &"failed to fill whole buffer")); } let (a, b) = self.split_at(buf.len()); @@ -344,7 +345,7 @@ impl Write for &mut [u8] { if self.write(data)? == data.len() { Ok(()) } else { - Err(Error::new(ErrorKind::WriteZero, "failed to write whole buffer")) + Err(Error::new_const(ErrorKind::WriteZero, &"failed to write whole buffer")) } } @@ -357,7 +358,7 @@ impl Write for &mut [u8] { /// Write is implemented for `Vec` by appending to the vector. /// The vector will grow as needed. #[stable(feature = "rust1", since = "1.0.0")] -impl Write for Vec { +impl Write for Vec { #[inline] fn write(&mut self, buf: &[u8]) -> io::Result { self.extend_from_slice(buf); diff --git a/library/std/src/io/mod.rs b/library/std/src/io/mod.rs index 2291498740510..9625984195bd2 100644 --- a/library/std/src/io/mod.rs +++ b/library/std/src/io/mod.rs @@ -333,7 +333,7 @@ where let ret = f(g.buf); if str::from_utf8(&g.buf[g.len..]).is_err() { ret.and_then(|_| { - Err(Error::new(ErrorKind::InvalidData, "stream did not contain valid UTF-8")) + Err(Error::new_const(ErrorKind::InvalidData, &"stream did not contain valid UTF-8")) }) } else { g.len = g.buf.len(); @@ -429,7 +429,7 @@ pub(crate) fn default_read_exact(this: &mut R, mut buf: &mut [ } } if !buf.is_empty() { - Err(Error::new(ErrorKind::UnexpectedEof, "failed to fill whole buffer")) + Err(Error::new_const(ErrorKind::UnexpectedEof, &"failed to fill whole buffer")) } else { Ok(()) } @@ -505,7 +505,8 @@ pub(crate) fn default_read_exact(this: &mut R, mut buf: &mut [ /// [`std::io`]: self /// [`File`]: crate::fs::File #[stable(feature = "rust1", since = "1.0.0")] -#[doc(spotlight)] +#[cfg_attr(bootstrap, doc(spotlight))] +#[cfg_attr(not(bootstrap), doc(notable_trait))] pub trait Read { /// Pull some bytes from this source into the specified buffer, returning /// how many bytes were read. @@ -514,8 +515,8 @@ pub trait Read { /// waiting for data, but if an object needs to block for a read and cannot, /// it will typically signal this via an [`Err`] return value. /// - /// If the return value of this method is [`Ok(n)`], then it must be - /// guaranteed that `0 <= n <= buf.len()`. A nonzero `n` value indicates + /// If the return value of this method is [`Ok(n)`], then implementations must + /// guarantee that `0 <= n <= buf.len()`. A nonzero `n` value indicates /// that the buffer `buf` has been filled in with `n` bytes of data from this /// source. If `n` is `0`, then it can indicate one of two scenarios: /// @@ -529,6 +530,11 @@ pub trait Read { /// This may happen for example because fewer bytes are actually available right now /// (e. g. being close to end-of-file) or because read() was interrupted by a signal. /// + /// As this trait is safe to implement, callers cannot rely on `n <= buf.len()` for safety. + /// Extra care needs to be taken when `unsafe` functions are used to access the read bytes. + /// Callers have to ensure that no unchecked out-of-bounds accesses are possible even if + /// `n > buf.len()`. + /// /// No guarantees are provided about the contents of `buf` when this /// function is called, implementations cannot rely on any property of the /// contents of `buf` being true. It is recommended that *implementations* @@ -1291,7 +1297,8 @@ impl Initializer { /// /// [`write_all`]: Write::write_all #[stable(feature = "rust1", since = "1.0.0")] -#[doc(spotlight)] +#[cfg_attr(bootstrap, doc(spotlight))] +#[cfg_attr(not(bootstrap), doc(notable_trait))] pub trait Write { /// Write a buffer into this writer, returning how many bytes were written. /// @@ -1432,7 +1439,10 @@ pub trait Write { while !buf.is_empty() { match self.write(buf) { Ok(0) => { - return Err(Error::new(ErrorKind::WriteZero, "failed to write whole buffer")); + return Err(Error::new_const( + ErrorKind::WriteZero, + &"failed to write whole buffer", + )); } Ok(n) => buf = &buf[n..], Err(ref e) if e.kind() == ErrorKind::Interrupted => {} @@ -1497,7 +1507,10 @@ pub trait Write { while !bufs.is_empty() { match self.write_vectored(bufs) { Ok(0) => { - return Err(Error::new(ErrorKind::WriteZero, "failed to write whole buffer")); + return Err(Error::new_const( + ErrorKind::WriteZero, + &"failed to write whole buffer", + )); } Ok(n) => bufs = IoSlice::advance(bufs, n), Err(ref e) if e.kind() == ErrorKind::Interrupted => {} @@ -1571,7 +1584,7 @@ pub trait Write { if output.error.is_err() { output.error } else { - Err(Error::new(ErrorKind::Other, "formatter error")) + Err(Error::new_const(ErrorKind::Other, &"formatter error")) } } } @@ -2103,6 +2116,7 @@ pub trait BufRead: Read { /// /// [`chain`]: Read::chain #[stable(feature = "rust1", since = "1.0.0")] +#[derive(Debug)] pub struct Chain { first: T, second: U, @@ -2184,13 +2198,6 @@ impl Chain { } } -#[stable(feature = "std_debug", since = "1.16.0")] -impl fmt::Debug for Chain { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("Chain").field("t", &self.first).field("u", &self.second).finish() - } -} - #[stable(feature = "rust1", since = "1.0.0")] impl Read for Chain { fn read(&mut self, buf: &mut [u8]) -> Result { @@ -2238,6 +2245,19 @@ impl BufRead for Chain { } } +impl SizeHint for Chain { + fn lower_bound(&self) -> usize { + SizeHint::lower_bound(&self.first) + SizeHint::lower_bound(&self.second) + } + + fn upper_bound(&self) -> Option { + match (SizeHint::upper_bound(&self.first), SizeHint::upper_bound(&self.second)) { + (Some(first), Some(second)) => Some(first + second), + _ => None, + } + } +} + /// Reader adaptor which limits the bytes read from an underlying reader. /// /// This struct is generally created by calling [`take`] on a reader. @@ -2464,6 +2484,30 @@ impl Iterator for Bytes { }; } } + + fn size_hint(&self) -> (usize, Option) { + SizeHint::size_hint(&self.inner) + } +} + +trait SizeHint { + fn lower_bound(&self) -> usize; + + fn upper_bound(&self) -> Option; + + fn size_hint(&self) -> (usize, Option) { + (self.lower_bound(), self.upper_bound()) + } +} + +impl SizeHint for T { + default fn lower_bound(&self) -> usize { + 0 + } + + default fn upper_bound(&self) -> Option { + None + } } /// An iterator over the contents of an instance of `BufRead` split on a diff --git a/library/std/src/io/stdio.rs b/library/std/src/io/stdio.rs index 1e72c9e0611ea..c2e0b24ba8327 100644 --- a/library/std/src/io/stdio.rs +++ b/library/std/src/io/stdio.rs @@ -373,7 +373,7 @@ impl Stdin { #[stable(feature = "std_debug", since = "1.16.0")] impl fmt::Debug for Stdin { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.pad("Stdin { .. }") + f.debug_struct("Stdin").finish_non_exhaustive() } } @@ -467,7 +467,7 @@ impl BufRead for StdinLock<'_> { #[stable(feature = "std_debug", since = "1.16.0")] impl fmt::Debug for StdinLock<'_> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.pad("StdinLock { .. }") + f.debug_struct("StdinLock").finish_non_exhaustive() } } @@ -607,7 +607,7 @@ impl Stdout { #[stable(feature = "std_debug", since = "1.16.0")] impl fmt::Debug for Stdout { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.pad("Stdout { .. }") + f.debug_struct("Stdout").finish_non_exhaustive() } } @@ -689,7 +689,7 @@ impl Write for StdoutLock<'_> { #[stable(feature = "std_debug", since = "1.16.0")] impl fmt::Debug for StdoutLock<'_> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.pad("StdoutLock { .. }") + f.debug_struct("StdoutLock").finish_non_exhaustive() } } @@ -804,7 +804,7 @@ impl Stderr { #[stable(feature = "std_debug", since = "1.16.0")] impl fmt::Debug for Stderr { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.pad("Stderr { .. }") + f.debug_struct("Stderr").finish_non_exhaustive() } } @@ -886,7 +886,7 @@ impl Write for StderrLock<'_> { #[stable(feature = "std_debug", since = "1.16.0")] impl fmt::Debug for StderrLock<'_> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.pad("StderrLock { .. }") + f.debug_struct("StderrLock").finish_non_exhaustive() } } diff --git a/library/std/src/io/tests.rs b/library/std/src/io/tests.rs index f176c2f088cb3..2b14e16150317 100644 --- a/library/std/src/io/tests.rs +++ b/library/std/src/io/tests.rs @@ -1,7 +1,7 @@ use super::{repeat, Cursor, SeekFrom}; use crate::cmp::{self, min}; use crate::io::{self, IoSlice, IoSliceMut}; -use crate::io::{BufRead, Read, Seek, Write}; +use crate::io::{BufRead, BufReader, Read, Seek, Write}; use crate::ops::Deref; #[test] @@ -152,12 +152,12 @@ fn take_eof() { impl Read for R { fn read(&mut self, _: &mut [u8]) -> io::Result { - Err(io::Error::new(io::ErrorKind::Other, "")) + Err(io::Error::new_const(io::ErrorKind::Other, &"")) } } impl BufRead for R { fn fill_buf(&mut self) -> io::Result<&[u8]> { - Err(io::Error::new(io::ErrorKind::Other, "")) + Err(io::Error::new_const(io::ErrorKind::Other, &"")) } fn consume(&mut self, _amt: usize) {} } @@ -198,6 +198,53 @@ fn chain_bufread() { cmp_bufread(chain1, chain2, &testdata[..]); } +#[test] +fn bufreader_size_hint() { + let testdata = b"ABCDEFGHIJKL"; + let mut buf_reader = BufReader::new(&testdata[..]); + assert_eq!(buf_reader.buffer().len(), 0); + + let buffer_length = testdata.len(); + buf_reader.fill_buf().unwrap(); + + // Check that size hint matches buffer contents + let mut buffered_bytes = buf_reader.bytes(); + let (lower_bound, _upper_bound) = buffered_bytes.size_hint(); + assert_eq!(lower_bound, buffer_length); + + // Check that size hint matches buffer contents after advancing + buffered_bytes.next().unwrap().unwrap(); + let (lower_bound, _upper_bound) = buffered_bytes.size_hint(); + assert_eq!(lower_bound, buffer_length - 1); +} + +#[test] +fn empty_size_hint() { + let size_hint = io::empty().bytes().size_hint(); + assert_eq!(size_hint, (0, Some(0))); +} + +#[test] +fn chain_empty_size_hint() { + let chain = io::empty().chain(io::empty()); + let size_hint = chain.bytes().size_hint(); + assert_eq!(size_hint, (0, Some(0))); +} + +#[test] +fn chain_size_hint() { + let testdata = b"ABCDEFGHIJKL"; + let mut buf_reader_1 = BufReader::new(&testdata[..6]); + let mut buf_reader_2 = BufReader::new(&testdata[6..]); + + buf_reader_1.fill_buf().unwrap(); + buf_reader_2.fill_buf().unwrap(); + + let chain = buf_reader_1.chain(buf_reader_2); + let size_hint = chain.bytes().size_hint(); + assert_eq!(size_hint, (testdata.len(), None)); +} + #[test] fn chain_zero_length_read_is_not_eof() { let a = b"A"; diff --git a/library/std/src/io/util.rs b/library/std/src/io/util.rs index e43ce4cdb4b8e..73f2f3eb3f5dc 100644 --- a/library/std/src/io/util.rs +++ b/library/std/src/io/util.rs @@ -4,7 +4,9 @@ mod tests; use crate::fmt; -use crate::io::{self, BufRead, Initializer, IoSlice, IoSliceMut, Read, Seek, SeekFrom, Write}; +use crate::io::{ + self, BufRead, Initializer, IoSlice, IoSliceMut, Read, Seek, SeekFrom, SizeHint, Write, +}; /// A reader which is always at EOF. /// @@ -76,7 +78,13 @@ impl Seek for Empty { #[stable(feature = "std_debug", since = "1.16.0")] impl fmt::Debug for Empty { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.pad("Empty { .. }") + f.debug_struct("Empty").finish_non_exhaustive() + } +} + +impl SizeHint for Empty { + fn upper_bound(&self) -> Option { + Some(0) } } @@ -142,7 +150,7 @@ impl Read for Repeat { #[stable(feature = "std_debug", since = "1.16.0")] impl fmt::Debug for Repeat { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.pad("Repeat { .. }") + f.debug_struct("Repeat").finish_non_exhaustive() } } @@ -228,6 +236,6 @@ impl Write for &Sink { #[stable(feature = "std_debug", since = "1.16.0")] impl fmt::Debug for Sink { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.pad("Sink { .. }") + f.debug_struct("Sink").finish_non_exhaustive() } } diff --git a/library/std/src/keyword_docs.rs b/library/std/src/keyword_docs.rs index 383eaf2e3a206..39ed62425cecf 100644 --- a/library/std/src/keyword_docs.rs +++ b/library/std/src/keyword_docs.rs @@ -547,15 +547,18 @@ mod fn_keyword {} /// # fn code() { } /// # let iterator = 0..2; /// { -/// let mut _iter = std::iter::IntoIterator::into_iter(iterator); -/// loop { -/// match _iter.next() { -/// Some(loop_variable) => { -/// code() -/// }, -/// None => break, -/// } -/// } +/// let result = match IntoIterator::into_iter(iterator) { +/// mut iter => loop { +/// let next; +/// match iter.next() { +/// Some(val) => next = val, +/// None => break, +/// }; +/// let loop_variable = next; +/// let () = { code(); }; +/// }, +/// }; +/// result /// } /// ``` /// @@ -1031,8 +1034,8 @@ mod mod_keyword {} /// }; /// ``` /// -/// For more information on the `move` keyword, see the [closure]'s section -/// of the Rust book or the [threads] section +/// For more information on the `move` keyword, see the [closures][closure] section +/// of the Rust book or the [threads] section. /// /// [closure]: ../book/ch13-01-closures.html /// [threads]: ../book/ch16-01-threads.html#using-move-closures-with-threads @@ -1310,7 +1313,11 @@ mod return_keyword {} /// [Reference]: ../reference/items/associated-items.html#methods mod self_keyword {} -#[doc(keyword = "Self")] +// FIXME: Once rustdoc can handle URL conflicts on case insensitive file systems, we can remove the +// three next lines and put back: `#[doc(keyword = "Self")]`. +#[doc(alias = "Self")] +#[allow(rustc::existing_doc_keyword)] +#[doc(keyword = "SelfTy")] // /// The implementing type within a [`trait`] or [`impl`] block, or the current type within a type /// definition. @@ -1761,6 +1768,7 @@ mod super_keyword {} /// In the 2015 edition the parameters pattern was not needed for traits: /// /// ```rust,edition2015 +/// # #![allow(anonymous_parameters)] /// trait Tr { /// fn f(i32); /// } diff --git a/library/std/src/lazy.rs b/library/std/src/lazy.rs index 974851a8bd634..ca86e569bc127 100644 --- a/library/std/src/lazy.rs +++ b/library/std/src/lazy.rs @@ -515,7 +515,7 @@ pub struct SyncLazy T> { #[unstable(feature = "once_cell", issue = "74465")] impl fmt::Debug for SyncLazy { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("Lazy").field("cell", &self.cell).field("init", &"..").finish() + f.debug_struct("Lazy").field("cell", &self.cell).finish_non_exhaustive() } } diff --git a/library/std/src/lib.rs b/library/std/src/lib.rs index ba49dee38e642..089d43483fcb3 100644 --- a/library/std/src/lib.rs +++ b/library/std/src/lib.rs @@ -208,13 +208,15 @@ // std may use features in a platform-specific way #![allow(unused_features)] #![feature(rustc_allow_const_fn_unstable)] -#![cfg_attr(test, feature(internal_output_capture, print_internals, update_panic_count))] +#![cfg_attr( + test, + feature(internal_output_capture, print_internals, update_panic_count, thread_local_const_init) +)] #![cfg_attr( all(target_vendor = "fortanix", target_env = "sgx"), feature(slice_index_methods, coerce_unsized, sgx_platform) )] #![deny(rustc::existing_doc_keyword)] -#![cfg_attr(all(test, target_vendor = "fortanix", target_env = "sgx"), feature(fixed_size_array))] // std is implemented with unstable features, many of which are internal // compiler details that will never be stable // NB: the following list is sorted to minimize merge conflicts. @@ -228,11 +230,13 @@ #![feature(arbitrary_self_types)] #![feature(array_error_internals)] #![feature(asm)] +#![feature(assert_matches)] #![feature(associated_type_bounds)] #![feature(atomic_mut_ptr)] #![feature(box_syntax)] #![feature(c_variadic)] #![feature(cfg_accessible)] +#![feature(cfg_eval)] #![feature(cfg_target_has_atomic)] #![feature(cfg_target_thread_local)] #![feature(char_error_internals)] @@ -247,6 +251,7 @@ #![feature(const_ip)] #![feature(const_ipv6)] #![feature(const_raw_ptr_deref)] +#![feature(const_socketaddr)] #![feature(const_ipv4)] #![feature(container_error_extra)] #![feature(core_intrinsics)] @@ -255,10 +260,10 @@ #![feature(doc_cfg)] #![feature(doc_keyword)] #![feature(doc_masked)] -#![feature(doc_spotlight)] +#![cfg_attr(bootstrap, feature(doc_spotlight))] +#![cfg_attr(not(bootstrap), feature(doc_notable_trait))] #![feature(dropck_eyepatch)] #![feature(duration_constants)] -#![feature(duration_zero)] #![feature(edition_panic)] #![feature(exact_size_is_empty)] #![feature(exhaustive_patterns)] @@ -276,11 +281,12 @@ #![feature(integer_atomics)] #![feature(into_future)] #![feature(intra_doc_pointers)] +#![feature(iter_zip)] #![feature(lang_items)] -#![feature(link_args)] #![feature(linkage)] #![feature(llvm_asm)] #![feature(log_syntax)] +#![feature(map_try_insert)] #![feature(maybe_uninit_extra)] #![feature(maybe_uninit_ref)] #![feature(maybe_uninit_slice)] @@ -289,15 +295,17 @@ #![feature(needs_panic_runtime)] #![feature(negative_impls)] #![feature(never_type)] +#![feature(new_uninit)] #![feature(nll)] #![feature(nonnull_slice_from_raw_parts)] #![feature(once_cell)] #![feature(auto_traits)] -#![feature(or_patterns)] +#![cfg_attr(bootstrap, feature(or_patterns))] #![feature(panic_info_message)] #![feature(panic_internals)] #![feature(panic_unwind)] #![feature(pin_static_ref)] +#![feature(prelude_2021)] #![feature(prelude_import)] #![feature(ptr_internals)] #![feature(raw)] @@ -323,7 +331,6 @@ #![feature(try_blocks)] #![feature(try_reserve)] #![feature(unboxed_closures)] -#![feature(unsafe_block_in_unsafe_fn)] #![feature(unsafe_cell_raw_get)] #![feature(unwind_attributes)] #![feature(vec_into_raw_parts)] @@ -391,7 +398,11 @@ pub use alloc_crate::vec; #[stable(feature = "rust1", since = "1.0.0")] pub use core::any; #[stable(feature = "simd_arch", since = "1.27.0")] -#[doc(no_inline)] +// The `no_inline`-attribute is required to make the documentation of all +// targets available. +// See https://github.com/rust-lang/rust/pull/57808#issuecomment-457390549 for +// more information. +#[doc(no_inline)] // Note (#82861): required for correct documentation pub use core::arch; #[stable(feature = "core_array", since = "1.36.0")] pub use core::array; @@ -448,6 +459,7 @@ pub use core::pin; #[stable(feature = "rust1", since = "1.0.0")] pub use core::ptr; #[stable(feature = "rust1", since = "1.0.0")] +#[allow(deprecated, deprecated_in_future)] pub use core::raw; #[stable(feature = "rust1", since = "1.0.0")] pub use core::result; @@ -529,29 +541,23 @@ pub mod rt; #[allow(dead_code, unused_attributes)] mod backtrace_rs; -// Pull in the `std_detect` crate directly into libstd. The contents of -// `std_detect` are in a different repository: rust-lang/stdarch. -// -// `std_detect` depends on libstd, but the contents of this module are -// set up in such a way that directly pulling it here works such that the -// crate uses the this crate as its libstd. -#[path = "../../stdarch/crates/std_detect/src/mod.rs"] -#[allow(missing_debug_implementations, missing_docs, dead_code)] -#[unstable(feature = "stdsimd", issue = "48556")] -#[cfg(not(test))] -mod std_detect; - +#[stable(feature = "simd_x86", since = "1.27.0")] +pub use std_detect::is_x86_feature_detected; #[doc(hidden)] #[unstable(feature = "stdsimd", issue = "48556")] -#[cfg(not(test))] -pub use std_detect::detect; +pub use std_detect::*; +#[unstable(feature = "stdsimd", issue = "48556")] +pub use std_detect::{ + is_aarch64_feature_detected, is_arm_feature_detected, is_mips64_feature_detected, + is_mips_feature_detected, is_powerpc64_feature_detected, is_powerpc_feature_detected, +}; // Re-export macros defined in libcore. #[stable(feature = "rust1", since = "1.0.0")] #[allow(deprecated, deprecated_in_future)] pub use core::{ - assert_eq, assert_ne, debug_assert, debug_assert_eq, debug_assert_ne, matches, r#try, todo, - unimplemented, unreachable, write, writeln, + assert_eq, assert_matches, assert_ne, debug_assert, debug_assert_eq, debug_assert_matches, + debug_assert_ne, matches, r#try, todo, unimplemented, unreachable, write, writeln, }; // Re-export built-in macros defined through libcore. diff --git a/library/std/src/net/addr.rs b/library/std/src/net/addr.rs index 63de87128340f..70376d5e06575 100644 --- a/library/std/src/net/addr.rs +++ b/library/std/src/net/addr.rs @@ -57,7 +57,8 @@ pub enum SocketAddr { /// See [`SocketAddr`] for a type encompassing both IPv4 and IPv6 socket addresses. /// /// The size of a `SocketAddrV4` struct may vary depending on the target operating -/// system. +/// system. Do not assume that this type has the same memory layout as the underlying +/// system representation. /// /// [IETF RFC 793]: https://tools.ietf.org/html/rfc793 /// [`IPv4` address]: Ipv4Addr @@ -76,6 +77,8 @@ pub enum SocketAddr { #[derive(Copy)] #[stable(feature = "rust1", since = "1.0.0")] pub struct SocketAddrV4 { + // Do not assume that this struct is implemented as the underlying system representation. + // The memory layout is not part of the stable interface that std exposes. inner: c::sockaddr_in, } @@ -88,7 +91,8 @@ pub struct SocketAddrV4 { /// See [`SocketAddr`] for a type encompassing both IPv4 and IPv6 socket addresses. /// /// The size of a `SocketAddrV6` struct may vary depending on the target operating -/// system. +/// system. Do not assume that this type has the same memory layout as the underlying +/// system representation. /// /// [IETF RFC 2553, Section 3.3]: https://tools.ietf.org/html/rfc2553#section-3.3 /// [`IPv6` address]: Ipv6Addr @@ -107,6 +111,8 @@ pub struct SocketAddrV4 { #[derive(Copy)] #[stable(feature = "rust1", since = "1.0.0")] pub struct SocketAddrV6 { + // Do not assume that this struct is implemented as the underlying system representation. + // The memory layout is not part of the stable interface that std exposes. inner: c::sockaddr_in6, } @@ -143,7 +149,8 @@ impl SocketAddr { /// assert_eq!(socket.ip(), IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1))); /// ``` #[stable(feature = "ip_addr", since = "1.7.0")] - pub fn ip(&self) -> IpAddr { + #[rustc_const_unstable(feature = "const_socketaddr", issue = "82485")] + pub const fn ip(&self) -> IpAddr { match *self { SocketAddr::V4(ref a) => IpAddr::V4(*a.ip()), SocketAddr::V6(ref a) => IpAddr::V6(*a.ip()), @@ -182,7 +189,8 @@ impl SocketAddr { /// assert_eq!(socket.port(), 8080); /// ``` #[stable(feature = "rust1", since = "1.0.0")] - pub fn port(&self) -> u16 { + #[rustc_const_unstable(feature = "const_socketaddr", issue = "82485")] + pub const fn port(&self) -> u16 { match *self { SocketAddr::V4(ref a) => a.port(), SocketAddr::V6(ref a) => a.port(), @@ -224,7 +232,8 @@ impl SocketAddr { /// assert_eq!(socket.is_ipv6(), false); /// ``` #[stable(feature = "sockaddr_checker", since = "1.16.0")] - pub fn is_ipv4(&self) -> bool { + #[rustc_const_unstable(feature = "const_socketaddr", issue = "82485")] + pub const fn is_ipv4(&self) -> bool { matches!(*self, SocketAddr::V4(_)) } @@ -244,7 +253,8 @@ impl SocketAddr { /// assert_eq!(socket.is_ipv6(), true); /// ``` #[stable(feature = "sockaddr_checker", since = "1.16.0")] - pub fn is_ipv6(&self) -> bool { + #[rustc_const_unstable(feature = "const_socketaddr", issue = "82485")] + pub const fn is_ipv6(&self) -> bool { matches!(*self, SocketAddr::V6(_)) } } @@ -284,7 +294,8 @@ impl SocketAddrV4 { /// assert_eq!(socket.ip(), &Ipv4Addr::new(127, 0, 0, 1)); /// ``` #[stable(feature = "rust1", since = "1.0.0")] - pub fn ip(&self) -> &Ipv4Addr { + #[rustc_const_unstable(feature = "const_socketaddr", issue = "82485")] + pub const fn ip(&self) -> &Ipv4Addr { // SAFETY: `Ipv4Addr` is `#[repr(C)] struct { _: in_addr; }`. // It is safe to cast from `&in_addr` to `&Ipv4Addr`. unsafe { &*(&self.inner.sin_addr as *const c::in_addr as *const Ipv4Addr) } @@ -317,7 +328,8 @@ impl SocketAddrV4 { /// assert_eq!(socket.port(), 8080); /// ``` #[stable(feature = "rust1", since = "1.0.0")] - pub fn port(&self) -> u16 { + #[rustc_const_unstable(feature = "const_socketaddr", issue = "82485")] + pub const fn port(&self) -> u16 { ntohs(self.inner.sin_port) } @@ -380,7 +392,8 @@ impl SocketAddrV6 { /// assert_eq!(socket.ip(), &Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1)); /// ``` #[stable(feature = "rust1", since = "1.0.0")] - pub fn ip(&self) -> &Ipv6Addr { + #[rustc_const_unstable(feature = "const_socketaddr", issue = "82485")] + pub const fn ip(&self) -> &Ipv6Addr { unsafe { &*(&self.inner.sin6_addr as *const c::in6_addr as *const Ipv6Addr) } } @@ -411,7 +424,8 @@ impl SocketAddrV6 { /// assert_eq!(socket.port(), 8080); /// ``` #[stable(feature = "rust1", since = "1.0.0")] - pub fn port(&self) -> u16 { + #[rustc_const_unstable(feature = "const_socketaddr", issue = "82485")] + pub const fn port(&self) -> u16 { ntohs(self.inner.sin6_port) } @@ -452,7 +466,8 @@ impl SocketAddrV6 { /// assert_eq!(socket.flowinfo(), 10); /// ``` #[stable(feature = "rust1", since = "1.0.0")] - pub fn flowinfo(&self) -> u32 { + #[rustc_const_unstable(feature = "const_socketaddr", issue = "82485")] + pub const fn flowinfo(&self) -> u32 { self.inner.sin6_flowinfo } @@ -490,7 +505,8 @@ impl SocketAddrV6 { /// assert_eq!(socket.scope_id(), 78); /// ``` #[stable(feature = "rust1", since = "1.0.0")] - pub fn scope_id(&self) -> u32 { + #[rustc_const_unstable(feature = "const_socketaddr", issue = "82485")] + pub const fn scope_id(&self) -> u32 { self.inner.sin6_scope_id } diff --git a/library/std/src/net/ip.rs b/library/std/src/net/ip.rs index 2aa305d7f831e..9b629e19be53d 100644 --- a/library/std/src/net/ip.rs +++ b/library/std/src/net/ip.rs @@ -67,7 +67,9 @@ pub enum IpAddr { /// /// `Ipv4Addr` provides a [`FromStr`] implementation. The four octets are in decimal /// notation, divided by `.` (this is called "dot-decimal notation"). +/// Notably, octal numbers and hexadecimal numbers are not allowed per [IETF RFC 6943]. /// +/// [IETF RFC 6943]: https://tools.ietf.org/html/rfc6943#section-3.1.1 /// [`FromStr`]: crate::str::FromStr /// /// # Examples @@ -332,6 +334,8 @@ impl Ipv4Addr { /// An IPv4 address representing an unspecified address: 0.0.0.0 /// + /// This corresponds to the constant `INADDR_ANY` in other languages. + /// /// # Examples /// /// ``` @@ -340,6 +344,7 @@ impl Ipv4Addr { /// let addr = Ipv4Addr::UNSPECIFIED; /// assert_eq!(addr, Ipv4Addr::new(0, 0, 0, 0)); /// ``` + #[doc(alias = "INADDR_ANY")] #[stable(feature = "ip_constructors", since = "1.30.0")] pub const UNSPECIFIED: Self = Ipv4Addr::new(0, 0, 0, 0); @@ -991,6 +996,7 @@ impl Ord for Ipv4Addr { } impl IntoInner for Ipv4Addr { + #[inline] fn into_inner(self) -> c::in_addr { self.inner } @@ -1798,11 +1804,13 @@ impl Ord for Ipv6Addr { } impl AsInner for Ipv6Addr { + #[inline] fn as_inner(&self) -> &c::in6_addr { &self.inner } } impl FromInner for Ipv6Addr { + #[inline] fn from_inner(addr: c::in6_addr) -> Ipv6Addr { Ipv6Addr { inner: addr } } diff --git a/library/std/src/net/mod.rs b/library/std/src/net/mod.rs index d4b1552fec5ff..d814e9b25ba9a 100644 --- a/library/std/src/net/mod.rs +++ b/library/std/src/net/mod.rs @@ -88,6 +88,6 @@ where } } Err(last_err.unwrap_or_else(|| { - Error::new(ErrorKind::InvalidInput, "could not resolve to any addresses") + Error::new_const(ErrorKind::InvalidInput, &"could not resolve to any addresses") })) } diff --git a/library/std/src/net/parser.rs b/library/std/src/net/parser.rs index e8b89626fbde1..88a8cb76befbf 100644 --- a/library/std/src/net/parser.rs +++ b/library/std/src/net/parser.rs @@ -35,7 +35,7 @@ macro_rules! impl_helper { impl_helper! { u8 u16 u32 } struct Parser<'a> { - // parsing as ASCII, so can use byte array + // Parsing as ASCII, so can use byte array. state: &'a [u8], } @@ -44,7 +44,7 @@ impl<'a> Parser<'a> { Parser { state: input.as_bytes() } } - /// Run a parser, and restore the pre-parse state if it fails + /// Run a parser, and restore the pre-parse state if it fails. fn read_atomically(&mut self, inner: F) -> Option where F: FnOnce(&mut Parser<'_>) -> Option, @@ -67,6 +67,11 @@ impl<'a> Parser<'a> { if self.state.is_empty() { result } else { None }.ok_or(AddrParseError(())) } + /// Peek the next character from the input + fn peek_char(&self) -> Option { + self.state.first().map(|&b| char::from(b)) + } + /// Read the next character from the input fn read_char(&mut self) -> Option { self.state.split_first().map(|(&b, tail)| { @@ -126,31 +131,38 @@ impl<'a> Parser<'a> { }) } - /// Read an IPv4 address + /// Read an IPv4 address. fn read_ipv4_addr(&mut self) -> Option { self.read_atomically(|p| { let mut groups = [0; 4]; for (i, slot) in groups.iter_mut().enumerate() { - *slot = p.read_separator('.', i, |p| p.read_number(10, None))?; + *slot = p.read_separator('.', i, |p| { + // Disallow octal number in IP string. + // https://tools.ietf.org/html/rfc6943#section-3.1.1 + match (p.peek_char(), p.read_number(10, None)) { + (Some('0'), Some(number)) if number != 0 => None, + (_, number) => number, + } + })?; } Some(groups.into()) }) } - /// Read an IPV6 Address + /// Read an IPv6 Address. fn read_ipv6_addr(&mut self) -> Option { - /// Read a chunk of an ipv6 address into `groups`. Returns the number + /// Read a chunk of an IPv6 address into `groups`. Returns the number /// of groups read, along with a bool indicating if an embedded - /// trailing ipv4 address was read. Specifically, read a series of - /// colon-separated ipv6 groups (0x0000 - 0xFFFF), with an optional - /// trailing embedded ipv4 address. + /// trailing IPv4 address was read. Specifically, read a series of + /// colon-separated IPv6 groups (0x0000 - 0xFFFF), with an optional + /// trailing embedded IPv4 address. fn read_groups(p: &mut Parser<'_>, groups: &mut [u16]) -> (usize, bool) { let limit = groups.len(); for (i, slot) in groups.iter_mut().enumerate() { - // Try to read a trailing embedded ipv4 address. There must be + // Try to read a trailing embedded IPv4 address. There must be // at least two groups left. if i < limit - 1 { let ipv4 = p.read_separator(':', i, |p| p.read_ipv4_addr()); @@ -188,8 +200,8 @@ impl<'a> Parser<'a> { return None; } - // read `::` if previous code parsed less than 8 groups - // `::` indicates one or more groups of 16 bits of zeros + // Read `::` if previous code parsed less than 8 groups. + // `::` indicates one or more groups of 16 bits of zeros. p.read_given_char(':')?; p.read_given_char(':')?; @@ -206,12 +218,12 @@ impl<'a> Parser<'a> { }) } - /// Read an IP Address, either IPV4 or IPV6. + /// Read an IP Address, either IPv4 or IPv6. fn read_ip_addr(&mut self) -> Option { self.read_ipv4_addr().map(IpAddr::V4).or_else(move || self.read_ipv6_addr().map(IpAddr::V6)) } - /// Read a : followed by a port in base 10. + /// Read a `:` followed by a port in base 10. fn read_port(&mut self) -> Option { self.read_atomically(|p| { p.read_given_char(':')?; @@ -219,7 +231,7 @@ impl<'a> Parser<'a> { }) } - /// Read a % followed by a scope id in base 10. + /// Read a `%` followed by a scope ID in base 10. fn read_scope_id(&mut self) -> Option { self.read_atomically(|p| { p.read_given_char('%')?; @@ -227,7 +239,7 @@ impl<'a> Parser<'a> { }) } - /// Read an IPV4 address with a port + /// Read an IPv4 address with a port. fn read_socket_addr_v4(&mut self) -> Option { self.read_atomically(|p| { let ip = p.read_ipv4_addr()?; @@ -236,7 +248,7 @@ impl<'a> Parser<'a> { }) } - /// Read an IPV6 address with a port + /// Read an IPv6 address with a port. fn read_socket_addr_v6(&mut self) -> Option { self.read_atomically(|p| { p.read_given_char('[')?; diff --git a/library/std/src/net/parser/tests.rs b/library/std/src/net/parser/tests.rs index 8d8889cd19d36..6d2d48ecad02f 100644 --- a/library/std/src/net/parser/tests.rs +++ b/library/std/src/net/parser/tests.rs @@ -8,11 +8,15 @@ const SCOPE_ID: u32 = 1337; const IPV4: Ipv4Addr = Ipv4Addr::new(192, 168, 0, 1); const IPV4_STR: &str = "192.168.0.1"; const IPV4_STR_PORT: &str = "192.168.0.1:8080"; +const IPV4_STR_WITH_OCTAL: &str = "0127.0.0.1"; +const IPV4_STR_WITH_HEX: &str = "0x10.0.0.1"; const IPV6: Ipv6Addr = Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0xc0a8, 0x1); const IPV6_STR_FULL: &str = "2001:db8:0:0:0:0:c0a8:1"; const IPV6_STR_COMPRESS: &str = "2001:db8::c0a8:1"; const IPV6_STR_V4: &str = "2001:db8::192.168.0.1"; +const IPV6_STR_V4_WITH_OCTAL: &str = "2001:db8::0127.0.0.1"; +const IPV6_STR_V4_WITH_HEX: &str = "2001:db8::0x10.0.0.1"; const IPV6_STR_PORT: &str = "[2001:db8::c0a8:1]:8080"; const IPV6_STR_PORT_SCOPE_ID: &str = "[2001:db8::c0a8:1%1337]:8080"; @@ -22,6 +26,8 @@ fn parse_ipv4() { assert_eq!(result, IPV4); assert!(Ipv4Addr::from_str(IPV4_STR_PORT).is_err()); + assert!(Ipv4Addr::from_str(IPV4_STR_WITH_OCTAL).is_err()); + assert!(Ipv4Addr::from_str(IPV4_STR_WITH_HEX).is_err()); assert!(Ipv4Addr::from_str(IPV6_STR_FULL).is_err()); assert!(Ipv4Addr::from_str(IPV6_STR_COMPRESS).is_err()); assert!(Ipv4Addr::from_str(IPV6_STR_V4).is_err()); @@ -39,6 +45,8 @@ fn parse_ipv6() { let result: Ipv6Addr = IPV6_STR_V4.parse().unwrap(); assert_eq!(result, IPV6); + assert!(Ipv6Addr::from_str(IPV6_STR_V4_WITH_OCTAL).is_err()); + assert!(Ipv6Addr::from_str(IPV6_STR_V4_WITH_HEX).is_err()); assert!(Ipv6Addr::from_str(IPV4_STR).is_err()); assert!(Ipv6Addr::from_str(IPV4_STR_PORT).is_err()); assert!(Ipv6Addr::from_str(IPV6_STR_PORT).is_err()); diff --git a/library/std/src/net/udp.rs b/library/std/src/net/udp.rs index 2377a76a33de9..b9af5992dffdf 100644 --- a/library/std/src/net/udp.rs +++ b/library/std/src/net/udp.rs @@ -173,7 +173,7 @@ impl UdpSocket { pub fn send_to(&self, buf: &[u8], addr: A) -> io::Result { match addr.to_socket_addrs()?.next() { Some(addr) => self.0.send_to(buf, &addr), - None => Err(Error::new(ErrorKind::InvalidInput, "no addresses to send data to")), + None => Err(Error::new_const(ErrorKind::InvalidInput, &"no addresses to send data to")), } } diff --git a/library/std/src/os/haiku/raw.rs b/library/std/src/os/haiku/raw.rs index 0d7e70b6b351d..48117d288ae47 100644 --- a/library/std/src/os/haiku/raw.rs +++ b/library/std/src/os/haiku/raw.rs @@ -1,6 +1,13 @@ //! Haiku-specific raw type definitions #![stable(feature = "raw_ext", since = "1.1.0")] +#![rustc_deprecated( + since = "1.53.0", + reason = "these type aliases are no longer supported by \ + the standard library, the `libc` crate on \ + crates.io should be used instead for the correct \ + definitions" +)] #![allow(deprecated)] use crate::os::raw::c_long; diff --git a/library/std/src/path.rs b/library/std/src/path.rs index de3b57df44e1e..f4020a428791e 100644 --- a/library/std/src/path.rs +++ b/library/std/src/path.rs @@ -1467,7 +1467,7 @@ impl> From<&T> for PathBuf { #[stable(feature = "rust1", since = "1.0.0")] impl From for PathBuf { - /// Converts a `OsString` into a `PathBuf` + /// Converts an [`OsString`] into a [`PathBuf`] /// /// This conversion does not allocate or copy memory. #[inline] @@ -1478,7 +1478,7 @@ impl From for PathBuf { #[stable(feature = "from_path_buf_for_os_string", since = "1.14.0")] impl From for OsString { - /// Converts a `PathBuf` into a `OsString` + /// Converts a [`PathBuf`] into an [`OsString`] /// /// This conversion does not allocate or copy memory. #[inline] @@ -1489,7 +1489,7 @@ impl From for OsString { #[stable(feature = "rust1", since = "1.0.0")] impl From for PathBuf { - /// Converts a `String` into a `PathBuf` + /// Converts a [`String`] into a [`PathBuf`] /// /// This conversion does not allocate or copy memory. #[inline] @@ -1595,7 +1595,7 @@ impl<'a> From> for PathBuf { #[stable(feature = "shared_from_slice2", since = "1.24.0")] impl From for Arc { - /// Converts a `PathBuf` into an `Arc` by moving the `PathBuf` data into a new `Arc` buffer. + /// Converts a [`PathBuf`] into an [`Arc`] by moving the [`PathBuf`] data into a new [`Arc`] buffer. #[inline] fn from(s: PathBuf) -> Arc { let arc: Arc = Arc::from(s.into_os_string()); @@ -1605,7 +1605,7 @@ impl From for Arc { #[stable(feature = "shared_from_slice2", since = "1.24.0")] impl From<&Path> for Arc { - /// Converts a `Path` into an `Arc` by copying the `Path` data into a new `Arc` buffer. + /// Converts a [`Path`] into an [`Arc`] by copying the [`Path`] data into a new [`Arc`] buffer. #[inline] fn from(s: &Path) -> Arc { let arc: Arc = Arc::from(s.as_os_str()); @@ -1615,7 +1615,7 @@ impl From<&Path> for Arc { #[stable(feature = "shared_from_slice2", since = "1.24.0")] impl From for Rc { - /// Converts a `PathBuf` into an `Rc` by moving the `PathBuf` data into a new `Rc` buffer. + /// Converts a [`PathBuf`] into an [`Rc`] by moving the [`PathBuf`] data into a new `Rc` buffer. #[inline] fn from(s: PathBuf) -> Rc { let rc: Rc = Rc::from(s.into_os_string()); @@ -1625,7 +1625,7 @@ impl From for Rc { #[stable(feature = "shared_from_slice2", since = "1.24.0")] impl From<&Path> for Rc { - /// Converts a `Path` into an `Rc` by copying the `Path` data into a new `Rc` buffer. + /// Converts a [`Path`] into an [`Rc`] by copying the [`Path`] data into a new `Rc` buffer. #[inline] fn from(s: &Path) -> Rc { let rc: Rc = Rc::from(s.as_os_str()); @@ -2472,6 +2472,36 @@ impl Path { fs::metadata(self).is_ok() } + /// Returns `Ok(true)` if the path points at an existing entity. + /// + /// This function will traverse symbolic links to query information about the + /// destination file. In case of broken symbolic links this will return `Ok(false)`. + /// + /// As opposed to the `exists()` method, this one doesn't silently ignore errors + /// unrelated to the path not existing. (E.g. it will return `Err(_)` in case of permission + /// denied on some of the parent directories.) + /// + /// # Examples + /// + /// ```no_run + /// #![feature(path_try_exists)] + /// + /// use std::path::Path; + /// assert!(!Path::new("does_not_exist.txt").try_exists().expect("Can't check existence of file does_not_exist.txt")); + /// assert!(Path::new("/root/secret_file.txt").try_exists().is_err()); + /// ``` + // FIXME: stabilization should modify documentation of `exists()` to recommend this method + // instead. + #[unstable(feature = "path_try_exists", issue = "83186")] + #[inline] + pub fn try_exists(&self) -> io::Result { + match fs::metadata(self) { + Ok(_) => Ok(true), + Err(error) if error.kind() == io::ErrorKind::NotFound => Ok(false), + Err(error) => Err(error), + } + } + /// Returns `true` if the path exists on disk and is pointing at a regular file. /// /// This function will traverse symbolic links to query information about the diff --git a/library/std/src/prelude/mod.rs b/library/std/src/prelude/mod.rs index eb2095b819657..1b4facdd049bb 100644 --- a/library/std/src/prelude/mod.rs +++ b/library/std/src/prelude/mod.rs @@ -28,53 +28,53 @@ //! The current version of the prelude (version 1) lives in //! [`std::prelude::v1`], and re-exports the following: //! -//! * [`std::marker`]::{[`Copy`], [`Send`], [`Sized`], [`Sync`], [`Unpin`]}: +//! * [std::marker]::{[Copy], [Send], [Sized], [Sync], [Unpin]}, //! marker traits that indicate fundamental properties of types. -//! * [`std::ops`]::{[`Drop`], [`Fn`], [`FnMut`], [`FnOnce`]}: various +//! * [std::ops]::{[Drop], [Fn], [FnMut], [FnOnce]}, various //! operations for both destructors and overloading `()`. -//! * [`std::mem`]::[`drop`][`mem::drop`]: a convenience function for explicitly +//! * [std::mem]::[drop][mem::drop], a convenience function for explicitly //! dropping a value. -//! * [`std::boxed`]::[`Box`]: a way to allocate values on the heap. -//! * [`std::borrow`]::[`ToOwned`]: the conversion trait that defines +//! * [std::boxed]::[Box], a way to allocate values on the heap. +//! * [std::borrow]::[ToOwned], the conversion trait that defines //! [`to_owned`], the generic method for creating an owned type from a //! borrowed type. -//! * [`std::clone`]::[`Clone`]: the ubiquitous trait that defines -//! [`clone`][`Clone::clone`], the method for producing a copy of a value. -//! * [`std::cmp`]::{[`PartialEq`], [`PartialOrd`], [`Eq`], [`Ord`]}: the +//! * [std::clone]::[Clone], the ubiquitous trait that defines +//! [`clone`][Clone::clone], the method for producing a copy of a value. +//! * [std::cmp]::{[PartialEq], [PartialOrd], [Eq], [Ord]}, the //! comparison traits, which implement the comparison operators and are often //! seen in trait bounds. -//! * [`std::convert`]::{[`AsRef`], [`AsMut`], [`Into`], [`From`]}: generic +//! * [std::convert]::{[AsRef], [AsMut], [Into], [From]}, generic //! conversions, used by savvy API authors to create overloaded methods. -//! * [`std::default`]::[`Default`], types that have default values. -//! * [`std::iter`]::{[`Iterator`], [`Extend`], [`IntoIterator`], -//! [`DoubleEndedIterator`], [`ExactSizeIterator`]}: iterators of various +//! * [std::default]::[Default], types that have default values. +//! * [std::iter]::{[Iterator], [Extend], [IntoIterator], [DoubleEndedIterator], [ExactSizeIterator]}, +//! iterators of various //! kinds. -//! * [`std::option`]::[`Option`]::{[`self`][`Option`], [`Some`], [`None`]}, a +//! * [std::option]::[Option]::{[self][Option], [Some], [None]}, a //! type which expresses the presence or absence of a value. This type is so //! commonly used, its variants are also exported. -//! * [`std::result`]::[`Result`]::{[`self`][`Result`], [`Ok`], [`Err`]}: a type +//! * [std::result]::[Result]::{[self][Result], [Ok], [Err]}, a type //! for functions that may succeed or fail. Like [`Option`], its variants are //! exported as well. -//! * [`std::string`]::{[`String`], [`ToString`]}: heap-allocated strings. -//! * [`std::vec`]::[`Vec`]: a growable, heap-allocated vector. +//! * [std::string]::{[String], [ToString]}, heap-allocated strings. +//! * [std::vec]::[Vec], a growable, heap-allocated vector. //! -//! [`mem::drop`]: crate::mem::drop -//! [`std::borrow`]: crate::borrow -//! [`std::boxed`]: crate::boxed -//! [`std::clone`]: crate::clone -//! [`std::cmp`]: crate::cmp -//! [`std::convert`]: crate::convert -//! [`std::default`]: crate::default -//! [`std::iter`]: crate::iter -//! [`std::marker`]: crate::marker -//! [`std::mem`]: crate::mem -//! [`std::ops`]: crate::ops -//! [`std::option`]: crate::option +//! [mem::drop]: crate::mem::drop +//! [std::borrow]: crate::borrow +//! [std::boxed]: crate::boxed +//! [std::clone]: crate::clone +//! [std::cmp]: crate::cmp +//! [std::convert]: crate::convert +//! [std::default]: crate::default +//! [std::iter]: crate::iter +//! [std::marker]: crate::marker +//! [std::mem]: crate::mem +//! [std::ops]: crate::ops +//! [std::option]: crate::option //! [`std::prelude::v1`]: v1 -//! [`std::result`]: crate::result -//! [`std::slice`]: crate::slice -//! [`std::string`]: crate::string -//! [`std::vec`]: mod@crate::vec +//! [std::result]: crate::result +//! [std::slice]: crate::slice +//! [std::string]: crate::string +//! [std::vec]: mod@crate::vec //! [`to_owned`]: crate::borrow::ToOwned::to_owned //! [book-closures]: ../../book/ch13-01-closures.html //! [book-dtor]: ../../book/ch15-03-drop.html @@ -84,3 +84,37 @@ #![stable(feature = "rust1", since = "1.0.0")] pub mod v1; + +/// The 2015 version of the prelude of The Rust Standard Library. +/// +/// See the [module-level documentation](self) for more. +#[unstable(feature = "prelude_2015", issue = "none")] +pub mod rust_2015 { + #[unstable(feature = "prelude_2015", issue = "none")] + #[doc(no_inline)] + pub use super::v1::*; +} + +/// The 2018 version of the prelude of The Rust Standard Library. +/// +/// See the [module-level documentation](self) for more. +#[unstable(feature = "prelude_2018", issue = "none")] +pub mod rust_2018 { + #[unstable(feature = "prelude_2018", issue = "none")] + #[doc(no_inline)] + pub use super::v1::*; +} + +/// The 2021 version of the prelude of The Rust Standard Library. +/// +/// See the [module-level documentation](self) for more. +#[unstable(feature = "prelude_2021", issue = "none")] +pub mod rust_2021 { + #[unstable(feature = "prelude_2021", issue = "none")] + #[doc(no_inline)] + pub use super::v1::*; + + #[unstable(feature = "prelude_2021", issue = "none")] + #[doc(no_inline)] + pub use core::prelude::rust_2021::*; +} diff --git a/library/std/src/prelude/v1.rs b/library/std/src/prelude/v1.rs index ef9aec54a4ca2..4a3c3ba163598 100644 --- a/library/std/src/prelude/v1.rs +++ b/library/std/src/prelude/v1.rs @@ -1,6 +1,6 @@ //! The first version of the prelude of The Rust Standard Library. //! -//! See the [module-level documentation](../index.html) for more. +//! See the [module-level documentation](super) for more. #![stable(feature = "rust1", since = "1.0.0")] @@ -48,13 +48,12 @@ pub use core::prelude::v1::{ // FIXME: Attribute and internal derive macros are not documented because for them rustdoc generates // dead links which fail link checker testing. #[stable(feature = "builtin_macro_prelude", since = "1.38.0")] -#[allow(deprecated)] +#[allow(deprecated, deprecated_in_future)] #[doc(hidden)] pub use core::prelude::v1::{ bench, global_allocator, test, test_case, RustcDecodable, RustcEncodable, }; -#[cfg(not(bootstrap))] #[stable(feature = "builtin_macro_prelude", since = "1.38.0")] #[doc(hidden)] pub use core::prelude::v1::derive; @@ -67,6 +66,14 @@ pub use core::prelude::v1::derive; #[doc(hidden)] pub use core::prelude::v1::cfg_accessible; +#[unstable( + feature = "cfg_eval", + issue = "82679", + reason = "`cfg_eval` is a recently implemented feature" +)] +#[doc(hidden)] +pub use core::prelude::v1::cfg_eval; + // The file so far is equivalent to src/libcore/prelude/v1.rs, // and below to src/liballoc/prelude.rs. // Those files are duplicated rather than using glob imports diff --git a/library/std/src/primitive_docs.rs b/library/std/src/primitive_docs.rs index d4bb2083d00d8..84d072b9860e8 100644 --- a/library/std/src/primitive_docs.rs +++ b/library/std/src/primitive_docs.rs @@ -805,10 +805,12 @@ mod prim_tuple {} /// often discard insignificant digits: `println!("{}", 1.0f32 / 5.0f32)` will /// print `0.2`. /// -/// Additionally, `f32` can represent a couple of special values: +/// Additionally, `f32` can represent some special values: /// -/// - `-0`: this is just due to how floats are encoded. It is semantically -/// equivalent to `0` and `-0.0 == 0.0` results in `true`. +/// - −0.0: IEEE 754 floating point numbers have a bit that indicates their sign, so −0.0 is a +/// possible value. For comparison −0.0 = +0.0, but floating point operations can carry +/// the sign bit through arithmetic operations. This means −0.0 × +0.0 produces −0.0 and +/// a negative number rounded to a value smaller than a float can represent also produces −0.0. /// - [∞](#associatedconstant.INFINITY) and /// [−∞](#associatedconstant.NEG_INFINITY): these result from calculations /// like `1.0 / 0.0`. diff --git a/library/std/src/process.rs b/library/std/src/process.rs index 6480e654c55f0..931b3b600a302 100644 --- a/library/std/src/process.rs +++ b/library/std/src/process.rs @@ -71,11 +71,15 @@ //! .spawn() //! .expect("failed to execute child"); //! -//! { -//! // limited borrow of stdin -//! let stdin = child.stdin.as_mut().expect("failed to get stdin"); +//! // If the child process fills its stdout buffer, it may end up +//! // waiting until the parent reads the stdout, and not be able to +//! // read stdin in the meantime, causing a deadlock. +//! // Writing from another thread ensures that stdout is being read +//! // at the same time, avoiding the problem. +//! let mut stdin = child.stdin.take().expect("failed to get stdin"); +//! std::thread::spawn(move || { //! stdin.write_all(b"test").expect("failed to write to stdin"); -//! } +//! }); //! //! let output = child //! .wait_with_output() @@ -230,7 +234,7 @@ impl fmt::Debug for Child { .field("stdin", &self.stdin) .field("stdout", &self.stdout) .field("stderr", &self.stderr) - .finish() + .finish_non_exhaustive() } } @@ -308,7 +312,7 @@ impl FromInner for ChildStdin { #[stable(feature = "std_debug", since = "1.16.0")] impl fmt::Debug for ChildStdin { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.pad("ChildStdin { .. }") + f.debug_struct("ChildStdin").finish_non_exhaustive() } } @@ -369,7 +373,7 @@ impl FromInner for ChildStdout { #[stable(feature = "std_debug", since = "1.16.0")] impl fmt::Debug for ChildStdout { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.pad("ChildStdout { .. }") + f.debug_struct("ChildStdout").finish_non_exhaustive() } } @@ -430,7 +434,7 @@ impl FromInner for ChildStderr { #[stable(feature = "std_debug", since = "1.16.0")] impl fmt::Debug for ChildStderr { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.pad("ChildStderr { .. }") + f.debug_struct("ChildStderr").finish_non_exhaustive() } } @@ -885,7 +889,7 @@ impl Command { } /// Executes a command as a child process, waiting for it to finish and - /// collecting its exit status. + /// collecting its status. /// /// By default, stdin, stdout and stderr are inherited from the parent. /// @@ -899,7 +903,7 @@ impl Command { /// .status() /// .expect("failed to execute process"); /// - /// println!("process exited with: {}", status); + /// println!("process finished with: {}", status); /// /// assert!(status.success()); /// ``` @@ -1145,14 +1149,21 @@ impl Stdio { /// .spawn() /// .expect("Failed to spawn child process"); /// - /// { - /// let stdin = child.stdin.as_mut().expect("Failed to open stdin"); + /// let mut stdin = child.stdin.take().expect("Failed to open stdin"); + /// std::thread::spawn(move || { /// stdin.write_all("Hello, world!".as_bytes()).expect("Failed to write to stdin"); - /// } + /// }); /// /// let output = child.wait_with_output().expect("Failed to read stdout"); /// assert_eq!(String::from_utf8_lossy(&output.stdout), "!dlrow ,olleH"); /// ``` + /// + /// Writing more than a pipe buffer's worth of input to stdin without also reading + /// stdout and stderr at the same time may cause a deadlock. + /// This is an issue when running any program that doesn't guarantee that it reads + /// its entire stdin before writing more than a pipe buffer's worth of output. + /// The size of a pipe buffer varies on different targets. + /// #[stable(feature = "process", since = "1.0.0")] pub fn piped() -> Stdio { Stdio(imp::Stdio::MakePipe) @@ -1246,7 +1257,7 @@ impl FromInner for Stdio { #[stable(feature = "std_debug", since = "1.16.0")] impl fmt::Debug for Stdio { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.pad("Stdio { .. }") + f.debug_struct("Stdio").finish_non_exhaustive() } } @@ -1368,11 +1379,17 @@ impl From for Stdio { /// Describes the result of a process after it has terminated. /// -/// This `struct` is used to represent the exit status of a child process. +/// This `struct` is used to represent the exit status or other termination of a child process. /// Child processes are created via the [`Command`] struct and their exit /// status is exposed through the [`status`] method, or the [`wait`] method /// of a [`Child`] process. /// +/// An `ExitStatus` represents every possible disposition of a process. On Unix this +/// is the **wait status**. It is *not* simply an *exit status* (a value passed to `exit`). +/// +/// For proper error reporting of failed processes, print the value of `ExitStatus` using its +/// implementation of [`Display`](crate::fmt::Display). +/// /// [`status`]: Command::status /// [`wait`]: Child::wait #[derive(PartialEq, Eq, Clone, Copy, Debug)] @@ -1400,7 +1417,7 @@ impl ExitStatus { /// if status.success() { /// println!("'projects/' directory created"); /// } else { - /// println!("failed to create 'projects/' directory"); + /// println!("failed to create 'projects/' directory: {}", status); /// } /// ``` #[stable(feature = "process", since = "1.0.0")] @@ -1410,9 +1427,14 @@ impl ExitStatus { /// Returns the exit code of the process, if any. /// - /// On Unix, this will return `None` if the process was terminated - /// by a signal; `std::os::unix` provides an extension trait for - /// extracting the signal and other details from the `ExitStatus`. + /// In Unix terms the return value is the **exit status**: the value passed to `exit`, if the + /// process finished by calling `exit`. Note that on Unix the exit status is truncated to 8 + /// bits, and that values that didn't come from a program's call to `exit` may be invented the + /// runtime system (often, for example, 255, 254, 127 or 126). + /// + /// On Unix, this will return `None` if the process was terminated by a signal. + /// [`ExitStatusExt`](crate::os::unix::process::ExitStatusExt) is an + /// extension trait for extracting any such signal, and other details, from the `ExitStatus`. /// /// # Examples /// diff --git a/library/std/src/sync/barrier.rs b/library/std/src/sync/barrier.rs index eab26b6c7150c..a17b82f82e8c2 100644 --- a/library/std/src/sync/barrier.rs +++ b/library/std/src/sync/barrier.rs @@ -60,7 +60,7 @@ pub struct BarrierWaitResult(bool); #[stable(feature = "std_debug", since = "1.16.0")] impl fmt::Debug for Barrier { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.pad("Barrier { .. }") + f.debug_struct("Barrier").finish_non_exhaustive() } } diff --git a/library/std/src/sync/condvar.rs b/library/std/src/sync/condvar.rs index ffc1e57f4e03f..2f0b32c90d0ee 100644 --- a/library/std/src/sync/condvar.rs +++ b/library/std/src/sync/condvar.rs @@ -2,9 +2,8 @@ mod tests; use crate::fmt; -use crate::sync::{mutex, MutexGuard, PoisonError}; +use crate::sync::{mutex, poison, LockResult, MutexGuard, PoisonError}; use crate::sys_common::condvar as sys; -use crate::sys_common::poison::{self, LockResult}; use crate::time::{Duration, Instant}; /// A type indicating whether a timed wait on a condition variable returned @@ -548,7 +547,7 @@ impl Condvar { #[stable(feature = "std_debug", since = "1.16.0")] impl fmt::Debug for Condvar { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.pad("Condvar { .. }") + f.debug_struct("Condvar").finish_non_exhaustive() } } diff --git a/library/std/src/sync/mod.rs b/library/std/src/sync/mod.rs index b6699910b07cf..ee35598bab5fe 100644 --- a/library/std/src/sync/mod.rs +++ b/library/std/src/sync/mod.rs @@ -166,9 +166,9 @@ pub use self::mutex::{Mutex, MutexGuard}; #[allow(deprecated)] pub use self::once::{Once, OnceState, ONCE_INIT}; #[stable(feature = "rust1", since = "1.0.0")] -pub use self::rwlock::{RwLock, RwLockReadGuard, RwLockWriteGuard}; +pub use self::poison::{LockResult, PoisonError, TryLockError, TryLockResult}; #[stable(feature = "rust1", since = "1.0.0")] -pub use crate::sys_common::poison::{LockResult, PoisonError, TryLockError, TryLockResult}; +pub use self::rwlock::{RwLock, RwLockReadGuard, RwLockWriteGuard}; pub mod mpsc; @@ -176,4 +176,5 @@ mod barrier; mod condvar; mod mutex; mod once; +mod poison; mod rwlock; diff --git a/library/std/src/sync/mpsc/mod.rs b/library/std/src/sync/mpsc/mod.rs index b12e7eeb13814..ea1d598d26461 100644 --- a/library/std/src/sync/mpsc/mod.rs +++ b/library/std/src/sync/mpsc/mod.rs @@ -864,7 +864,7 @@ impl Drop for Sender { #[stable(feature = "mpsc_debug", since = "1.8.0")] impl fmt::Debug for Sender { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("Sender").finish() + f.debug_struct("Sender").finish_non_exhaustive() } } @@ -991,7 +991,7 @@ impl Drop for SyncSender { #[stable(feature = "mpsc_debug", since = "1.8.0")] impl fmt::Debug for SyncSender { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("SyncSender").finish() + f.debug_struct("SyncSender").finish_non_exhaustive() } } @@ -1470,14 +1470,14 @@ impl Drop for Receiver { #[stable(feature = "mpsc_debug", since = "1.8.0")] impl fmt::Debug for Receiver { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("Receiver").finish() + f.debug_struct("Receiver").finish_non_exhaustive() } } #[stable(feature = "rust1", since = "1.0.0")] impl fmt::Debug for SendError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - "SendError(..)".fmt(f) + f.debug_struct("SendError").finish_non_exhaustive() } } diff --git a/library/std/src/sync/mutex.rs b/library/std/src/sync/mutex.rs index ab61618dc7d7b..2615bea659253 100644 --- a/library/std/src/sync/mutex.rs +++ b/library/std/src/sync/mutex.rs @@ -6,8 +6,8 @@ use crate::fmt; use crate::mem; use crate::ops::{Deref, DerefMut}; use crate::ptr; +use crate::sync::{poison, LockResult, TryLockError, TryLockResult}; use crate::sys_common::mutex as sys; -use crate::sys_common::poison::{self, LockResult, TryLockError, TryLockResult}; /// A mutual exclusion primitive useful for protecting shared data /// @@ -441,10 +441,13 @@ impl Default for Mutex { #[stable(feature = "rust1", since = "1.0.0")] impl fmt::Debug for Mutex { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let mut d = f.debug_struct("Mutex"); match self.try_lock() { - Ok(guard) => f.debug_struct("Mutex").field("data", &&*guard).finish(), + Ok(guard) => { + d.field("data", &&*guard); + } Err(TryLockError::Poisoned(err)) => { - f.debug_struct("Mutex").field("data", &&**err.get_ref()).finish() + d.field("data", &&**err.get_ref()); } Err(TryLockError::WouldBlock) => { struct LockedPlaceholder; @@ -453,10 +456,11 @@ impl fmt::Debug for Mutex { f.write_str("") } } - - f.debug_struct("Mutex").field("data", &LockedPlaceholder).finish() + d.field("data", &LockedPlaceholder); } } + d.field("poisoned", &self.poison.get()); + d.finish_non_exhaustive() } } diff --git a/library/std/src/sync/once.rs b/library/std/src/sync/once.rs index 2e5f843fc43cb..6da6c18e47799 100644 --- a/library/std/src/sync/once.rs +++ b/library/std/src/sync/once.rs @@ -471,7 +471,7 @@ fn wait(state_and_queue: &AtomicUsize, mut current_state: usize) { // If the managing thread happens to signal and unpark us before we // can park ourselves, the result could be this thread never gets // unparked. Luckily `park` comes with the guarantee that if it got - // an `unpark` just before on an unparked thread is does not park. + // an `unpark` just before on an unparked thread it does not park. thread::park(); } break; @@ -481,7 +481,7 @@ fn wait(state_and_queue: &AtomicUsize, mut current_state: usize) { #[stable(feature = "std_debug", since = "1.16.0")] impl fmt::Debug for Once { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.pad("Once { .. }") + f.debug_struct("Once").finish_non_exhaustive() } } diff --git a/library/std/src/sys_common/poison.rs b/library/std/src/sync/poison.rs similarity index 94% rename from library/std/src/sys_common/poison.rs rename to library/std/src/sync/poison.rs index 2ab2c700a1bf1..05e1833c3e567 100644 --- a/library/std/src/sys_common/poison.rs +++ b/library/std/src/sync/poison.rs @@ -3,9 +3,6 @@ use crate::fmt; use crate::sync::atomic::{AtomicBool, Ordering}; use crate::thread; -#[allow(unused_imports)] // for intra-doc links -use crate::sync::{Mutex, RwLock}; - pub struct Flag { failed: AtomicBool, } @@ -80,6 +77,8 @@ pub struct Guard { /// } /// }; /// ``` +/// [`Mutex`]: crate::sync::Mutex +/// [`RwLock`]: crate::sync::RwLock #[stable(feature = "rust1", since = "1.0.0")] pub struct PoisonError { guard: T, @@ -89,9 +88,11 @@ pub struct PoisonError { /// can occur while trying to acquire a lock, from the [`try_lock`] method on a /// [`Mutex`] or the [`try_read`] and [`try_write`] methods on an [`RwLock`]. /// -/// [`try_lock`]: Mutex::try_lock -/// [`try_read`]: RwLock::try_read -/// [`try_write`]: RwLock::try_write +/// [`try_lock`]: crate::sync::Mutex::try_lock +/// [`try_read`]: crate::sync::RwLock::try_read +/// [`try_write`]: crate::sync::RwLock::try_write +/// [`Mutex`]: crate::sync::Mutex +/// [`RwLock`]: crate::sync::RwLock #[stable(feature = "rust1", since = "1.0.0")] pub enum TryLockError { /// The lock could not be acquired because another thread failed while holding @@ -127,7 +128,7 @@ pub type TryLockResult = Result>; #[stable(feature = "rust1", since = "1.0.0")] impl fmt::Debug for PoisonError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - "PoisonError { inner: .. }".fmt(f) + f.debug_struct("PoisonError").finish_non_exhaustive() } } @@ -149,7 +150,8 @@ impl Error for PoisonError { impl PoisonError { /// Creates a `PoisonError`. /// - /// This is generally created by methods like [`Mutex::lock`] or [`RwLock::read`]. + /// This is generally created by methods like [`Mutex::lock`](crate::sync::Mutex::lock) + /// or [`RwLock::read`](crate::sync::RwLock::read). #[stable(feature = "sync_poison", since = "1.2.0")] pub fn new(guard: T) -> PoisonError { PoisonError { guard } diff --git a/library/std/src/sync/rwlock.rs b/library/std/src/sync/rwlock.rs index 0298f59228cbe..b01bcec1361d7 100644 --- a/library/std/src/sync/rwlock.rs +++ b/library/std/src/sync/rwlock.rs @@ -6,7 +6,7 @@ use crate::fmt; use crate::mem; use crate::ops::{Deref, DerefMut}; use crate::ptr; -use crate::sys_common::poison::{self, LockResult, TryLockError, TryLockResult}; +use crate::sync::{poison, LockResult, TryLockError, TryLockResult}; use crate::sys_common::rwlock as sys; /// A reader-writer lock @@ -422,10 +422,13 @@ unsafe impl<#[may_dangle] T: ?Sized> Drop for RwLock { #[stable(feature = "rust1", since = "1.0.0")] impl fmt::Debug for RwLock { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let mut d = f.debug_struct("RwLock"); match self.try_read() { - Ok(guard) => f.debug_struct("RwLock").field("data", &&*guard).finish(), + Ok(guard) => { + d.field("data", &&*guard); + } Err(TryLockError::Poisoned(err)) => { - f.debug_struct("RwLock").field("data", &&**err.get_ref()).finish() + d.field("data", &&**err.get_ref()); } Err(TryLockError::WouldBlock) => { struct LockedPlaceholder; @@ -434,10 +437,11 @@ impl fmt::Debug for RwLock { f.write_str("") } } - - f.debug_struct("RwLock").field("data", &LockedPlaceholder).finish() + d.field("data", &LockedPlaceholder); } } + d.field("poisoned", &self.poison.get()); + d.finish_non_exhaustive() } } @@ -473,7 +477,7 @@ impl<'rwlock, T: ?Sized> RwLockWriteGuard<'rwlock, T> { #[stable(feature = "std_debug", since = "1.16.0")] impl fmt::Debug for RwLockReadGuard<'_, T> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("RwLockReadGuard").field("lock", &self.lock).finish() + (**self).fmt(f) } } @@ -487,7 +491,7 @@ impl fmt::Display for RwLockReadGuard<'_, T> { #[stable(feature = "std_debug", since = "1.16.0")] impl fmt::Debug for RwLockWriteGuard<'_, T> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("RwLockWriteGuard").field("lock", &self.lock).finish() + (**self).fmt(f) } } diff --git a/library/std/src/sys_common/alloc.rs b/library/std/src/sys/common/alloc.rs similarity index 98% rename from library/std/src/sys_common/alloc.rs rename to library/std/src/sys/common/alloc.rs index 6c1bc0d839ad3..2a54e99020e38 100644 --- a/library/std/src/sys_common/alloc.rs +++ b/library/std/src/sys/common/alloc.rs @@ -1,5 +1,3 @@ -#![allow(dead_code)] - use crate::alloc::{GlobalAlloc, Layout, System}; use crate::cmp; use crate::ptr; diff --git a/library/std/src/sys/common/mod.rs b/library/std/src/sys/common/mod.rs new file mode 100644 index 0000000000000..ff64d2aa82515 --- /dev/null +++ b/library/std/src/sys/common/mod.rs @@ -0,0 +1,13 @@ +// This module contains code that is shared between all platforms, mostly utility or fallback code. +// This explicitly does not include code that is shared between only a few platforms, +// such as when reusing an implementation from `unix` or `unsupported`. +// In those cases the desired code should be included directly using the #[path] attribute, +// not moved to this module. +// +// Currently `sys_common` contains a lot of code that should live in this module, +// ideally `sys_common` would only contain platform-independent abstractions on top of `sys`. +// Progress on this is tracked in #84187. + +#![allow(dead_code)] + +pub mod alloc; diff --git a/library/std/src/sys/hermit/args.rs b/library/std/src/sys/hermit/args.rs index 7727293927282..4794f89a5aee3 100644 --- a/library/std/src/sys/hermit/args.rs +++ b/library/std/src/sys/hermit/args.rs @@ -1,4 +1,5 @@ use crate::ffi::OsString; +use crate::fmt; use crate::marker::PhantomData; use crate::vec; @@ -22,9 +23,9 @@ pub struct Args { _dont_send_or_sync_me: PhantomData<*mut ()>, } -impl Args { - pub fn inner_debug(&self) -> &[OsString] { - self.iter.as_slice() +impl fmt::Debug for Args { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.iter.as_slice().fmt(f) } } diff --git a/library/std/src/sys/hermit/fd.rs b/library/std/src/sys/hermit/fd.rs index 97d1a38b41ab1..c400f5f2c2e84 100644 --- a/library/std/src/sys/hermit/fd.rs +++ b/library/std/src/sys/hermit/fd.rs @@ -1,9 +1,10 @@ #![unstable(reason = "not public", issue = "none", feature = "fd")] -use crate::io::{self, ErrorKind, Read}; +use crate::io::{self, Read}; use crate::mem; use crate::sys::cvt; use crate::sys::hermit::abi; +use crate::sys::unsupported; use crate::sys_common::AsInner; #[derive(Debug)] @@ -46,7 +47,7 @@ impl FileDesc { self.duplicate_path(&[]) } pub fn duplicate_path(&self, _path: &[u8]) -> io::Result { - Err(io::Error::new(ErrorKind::Other, "duplicate isn't supported")) + unsupported() } pub fn nonblocking(&self) -> io::Result { @@ -54,11 +55,11 @@ impl FileDesc { } pub fn set_cloexec(&self) -> io::Result<()> { - Err(io::Error::new(ErrorKind::Other, "cloexec isn't supported")) + unsupported() } pub fn set_nonblocking(&self, _nonblocking: bool) -> io::Result<()> { - Err(io::Error::new(ErrorKind::Other, "nonblocking isn't supported")) + unsupported() } } diff --git a/library/std/src/sys/hermit/fs.rs b/library/std/src/sys/hermit/fs.rs index 1807655e97170..5b3f2fa4e8275 100644 --- a/library/std/src/sys/hermit/fs.rs +++ b/library/std/src/sys/hermit/fs.rs @@ -9,7 +9,7 @@ use crate::sys::hermit::abi; use crate::sys::hermit::abi::{O_APPEND, O_CREAT, O_EXCL, O_RDONLY, O_RDWR, O_TRUNC, O_WRONLY}; use crate::sys::hermit::fd::FileDesc; use crate::sys::time::SystemTime; -use crate::sys::{unsupported, Void}; +use crate::sys::unsupported; use crate::sys_common::os_str_bytes::OsStrExt; pub use crate::sys_common::fs::copy; @@ -22,11 +22,11 @@ fn cstr(path: &Path) -> io::Result { #[derive(Debug)] pub struct File(FileDesc); -pub struct FileAttr(Void); +pub struct FileAttr(!); -pub struct ReadDir(Void); +pub struct ReadDir(!); -pub struct DirEntry(Void); +pub struct DirEntry(!); #[derive(Clone, Debug)] pub struct OpenOptions { @@ -41,64 +41,64 @@ pub struct OpenOptions { mode: i32, } -pub struct FilePermissions(Void); +pub struct FilePermissions(!); -pub struct FileType(Void); +pub struct FileType(!); #[derive(Debug)] pub struct DirBuilder {} impl FileAttr { pub fn size(&self) -> u64 { - match self.0 {} + self.0 } pub fn perm(&self) -> FilePermissions { - match self.0 {} + self.0 } pub fn file_type(&self) -> FileType { - match self.0 {} + self.0 } pub fn modified(&self) -> io::Result { - match self.0 {} + self.0 } pub fn accessed(&self) -> io::Result { - match self.0 {} + self.0 } pub fn created(&self) -> io::Result { - match self.0 {} + self.0 } } impl Clone for FileAttr { fn clone(&self) -> FileAttr { - match self.0 {} + self.0 } } impl FilePermissions { pub fn readonly(&self) -> bool { - match self.0 {} + self.0 } pub fn set_readonly(&mut self, _readonly: bool) { - match self.0 {} + self.0 } } impl Clone for FilePermissions { fn clone(&self) -> FilePermissions { - match self.0 {} + self.0 } } impl PartialEq for FilePermissions { fn eq(&self, _other: &FilePermissions) -> bool { - match self.0 {} + self.0 } } @@ -106,27 +106,27 @@ impl Eq for FilePermissions {} impl fmt::Debug for FilePermissions { fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self.0 {} + self.0 } } impl FileType { pub fn is_dir(&self) -> bool { - match self.0 {} + self.0 } pub fn is_file(&self) -> bool { - match self.0 {} + self.0 } pub fn is_symlink(&self) -> bool { - match self.0 {} + self.0 } } impl Clone for FileType { fn clone(&self) -> FileType { - match self.0 {} + self.0 } } @@ -134,7 +134,7 @@ impl Copy for FileType {} impl PartialEq for FileType { fn eq(&self, _other: &FileType) -> bool { - match self.0 {} + self.0 } } @@ -142,19 +142,19 @@ impl Eq for FileType {} impl Hash for FileType { fn hash(&self, _h: &mut H) { - match self.0 {} + self.0 } } impl fmt::Debug for FileType { fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self.0 {} + self.0 } } impl fmt::Debug for ReadDir { fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self.0 {} + self.0 } } @@ -162,25 +162,25 @@ impl Iterator for ReadDir { type Item = io::Result; fn next(&mut self) -> Option> { - match self.0 {} + self.0 } } impl DirEntry { pub fn path(&self) -> PathBuf { - match self.0 {} + self.0 } pub fn file_name(&self) -> OsString { - match self.0 {} + self.0 } pub fn metadata(&self) -> io::Result { - match self.0 {} + self.0 } pub fn file_type(&self) -> io::Result { - match self.0 {} + self.0 } } @@ -226,7 +226,7 @@ impl OpenOptions { (false, _, true) => Ok(O_WRONLY | O_APPEND), (true, _, true) => Ok(O_RDWR | O_APPEND), (false, false, false) => { - Err(io::Error::new(ErrorKind::InvalidInput, "invalid access mode")) + Err(io::Error::new_const(ErrorKind::InvalidInput, &"invalid access mode")) } } } @@ -236,12 +236,18 @@ impl OpenOptions { (true, false) => {} (false, false) => { if self.truncate || self.create || self.create_new { - return Err(io::Error::new(ErrorKind::InvalidInput, "invalid creation mode")); + return Err(io::Error::new_const( + ErrorKind::InvalidInput, + &"invalid creation mode", + )); } } (_, true) => { if self.truncate && !self.create_new { - return Err(io::Error::new(ErrorKind::InvalidInput, "invalid creation mode")); + return Err(io::Error::new_const( + ErrorKind::InvalidInput, + &"invalid creation mode", + )); } } } diff --git a/library/std/src/sys/hermit/mod.rs b/library/std/src/sys/hermit/mod.rs index f185635b7a0a6..56497162c0333 100644 --- a/library/std/src/sys/hermit/mod.rs +++ b/library/std/src/sys/hermit/mod.rs @@ -32,6 +32,7 @@ pub mod mutex; pub mod net; pub mod os; pub mod path; +#[path = "../unsupported/pipe.rs"] pub mod pipe; #[path = "../unsupported/process.rs"] pub mod process; @@ -54,14 +55,12 @@ pub fn unsupported() -> crate::io::Result { } pub fn unsupported_err() -> crate::io::Error { - crate::io::Error::new(crate::io::ErrorKind::Other, "operation not supported on HermitCore yet") + crate::io::Error::new_const( + crate::io::ErrorKind::Unsupported, + &"operation not supported on HermitCore yet", + ) } -// This enum is used as the storage for a bunch of types which can't actually -// exist. -#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)] -pub enum Void {} - pub unsafe fn strlen(start: *const c_char) -> usize { let mut str = start; diff --git a/library/std/src/sys/hermit/net.rs b/library/std/src/sys/hermit/net.rs index 8a788a9265f63..5f8839157eafc 100644 --- a/library/std/src/sys/hermit/net.rs +++ b/library/std/src/sys/hermit/net.rs @@ -6,7 +6,7 @@ use crate::str; use crate::sync::Arc; use crate::sys::hermit::abi; use crate::sys::hermit::abi::IpAddress::{Ipv4, Ipv6}; -use crate::sys::{unsupported, Void}; +use crate::sys::unsupported; use crate::sys_common::AsInner; use crate::time::Duration; @@ -14,7 +14,10 @@ use crate::time::Duration; /// if not, starts it. pub fn init() -> io::Result<()> { if abi::network_init() < 0 { - return Err(io::Error::new(ErrorKind::Other, "Unable to initialize network interface")); + return Err(io::Error::new_const( + ErrorKind::Other, + &"Unable to initialize network interface", + )); } Ok(()) @@ -47,9 +50,10 @@ impl TcpStream { match abi::tcpstream::connect(addr.ip().to_string().as_bytes(), addr.port(), None) { Ok(handle) => Ok(TcpStream(Arc::new(Socket(handle)))), - _ => { - Err(io::Error::new(ErrorKind::Other, "Unable to initiate a connection on a socket")) - } + _ => Err(io::Error::new_const( + ErrorKind::Other, + &"Unable to initiate a connection on a socket", + )), } } @@ -60,15 +64,16 @@ impl TcpStream { Some(duration.as_millis() as u64), ) { Ok(handle) => Ok(TcpStream(Arc::new(Socket(handle)))), - _ => { - Err(io::Error::new(ErrorKind::Other, "Unable to initiate a connection on a socket")) - } + _ => Err(io::Error::new_const( + ErrorKind::Other, + &"Unable to initiate a connection on a socket", + )), } } pub fn set_read_timeout(&self, duration: Option) -> io::Result<()> { abi::tcpstream::set_read_timeout(*self.0.as_inner(), duration.map(|d| d.as_millis() as u64)) - .map_err(|_| io::Error::new(ErrorKind::Other, "Unable to set timeout value")) + .map_err(|_| io::Error::new_const(ErrorKind::Other, &"Unable to set timeout value")) } pub fn set_write_timeout(&self, duration: Option) -> io::Result<()> { @@ -76,26 +81,28 @@ impl TcpStream { *self.0.as_inner(), duration.map(|d| d.as_millis() as u64), ) - .map_err(|_| io::Error::new(ErrorKind::Other, "Unable to set timeout value")) + .map_err(|_| io::Error::new_const(ErrorKind::Other, &"Unable to set timeout value")) } pub fn read_timeout(&self) -> io::Result> { - let duration = abi::tcpstream::get_read_timeout(*self.0.as_inner()) - .map_err(|_| io::Error::new(ErrorKind::Other, "Unable to determine timeout value"))?; + let duration = abi::tcpstream::get_read_timeout(*self.0.as_inner()).map_err(|_| { + io::Error::new_const(ErrorKind::Other, &"Unable to determine timeout value") + })?; Ok(duration.map(|d| Duration::from_millis(d))) } pub fn write_timeout(&self) -> io::Result> { - let duration = abi::tcpstream::get_write_timeout(*self.0.as_inner()) - .map_err(|_| io::Error::new(ErrorKind::Other, "Unable to determine timeout value"))?; + let duration = abi::tcpstream::get_write_timeout(*self.0.as_inner()).map_err(|_| { + io::Error::new_const(ErrorKind::Other, &"Unable to determine timeout value") + })?; Ok(duration.map(|d| Duration::from_millis(d))) } pub fn peek(&self, buf: &mut [u8]) -> io::Result { abi::tcpstream::peek(*self.0.as_inner(), buf) - .map_err(|_| io::Error::new(ErrorKind::Other, "set_nodelay failed")) + .map_err(|_| io::Error::new_const(ErrorKind::Other, &"set_nodelay failed")) } pub fn read(&self, buffer: &mut [u8]) -> io::Result { @@ -107,7 +114,7 @@ impl TcpStream { for i in ioslice.iter_mut() { let ret = abi::tcpstream::read(*self.0.as_inner(), &mut i[0..]) - .map_err(|_| io::Error::new(ErrorKind::Other, "Unable to read on socket"))?; + .map_err(|_| io::Error::new_const(ErrorKind::Other, &"Unable to read on socket"))?; if ret != 0 { size += ret; @@ -130,8 +137,9 @@ impl TcpStream { let mut size: usize = 0; for i in ioslice.iter() { - size += abi::tcpstream::write(*self.0.as_inner(), i) - .map_err(|_| io::Error::new(ErrorKind::Other, "Unable to write on socket"))?; + size += abi::tcpstream::write(*self.0.as_inner(), i).map_err(|_| { + io::Error::new_const(ErrorKind::Other, &"Unable to write on socket") + })?; } Ok(size) @@ -144,13 +152,13 @@ impl TcpStream { pub fn peer_addr(&self) -> io::Result { let (ipaddr, port) = abi::tcpstream::peer_addr(*self.0.as_inner()) - .map_err(|_| io::Error::new(ErrorKind::Other, "peer_addr failed"))?; + .map_err(|_| io::Error::new_const(ErrorKind::Other, &"peer_addr failed"))?; let saddr = match ipaddr { Ipv4(ref addr) => SocketAddr::new(IpAddr::V4(Ipv4Addr::from(addr.0)), port), Ipv6(ref addr) => SocketAddr::new(IpAddr::V6(Ipv6Addr::from(addr.0)), port), _ => { - return Err(io::Error::new(ErrorKind::Other, "peer_addr failed")); + return Err(io::Error::new_const(ErrorKind::Other, &"peer_addr failed")); } }; @@ -158,12 +166,12 @@ impl TcpStream { } pub fn socket_addr(&self) -> io::Result { - Err(io::Error::new(ErrorKind::Other, "socket_addr isn't supported")) + unsupported() } pub fn shutdown(&self, how: Shutdown) -> io::Result<()> { abi::tcpstream::shutdown(*self.0.as_inner(), how as i32) - .map_err(|_| io::Error::new(ErrorKind::Other, "unable to shutdown socket")) + .map_err(|_| io::Error::new_const(ErrorKind::Other, &"unable to shutdown socket")) } pub fn duplicate(&self) -> io::Result { @@ -172,31 +180,31 @@ impl TcpStream { pub fn set_nodelay(&self, mode: bool) -> io::Result<()> { abi::tcpstream::set_nodelay(*self.0.as_inner(), mode) - .map_err(|_| io::Error::new(ErrorKind::Other, "set_nodelay failed")) + .map_err(|_| io::Error::new_const(ErrorKind::Other, &"set_nodelay failed")) } pub fn nodelay(&self) -> io::Result { abi::tcpstream::nodelay(*self.0.as_inner()) - .map_err(|_| io::Error::new(ErrorKind::Other, "nodelay failed")) + .map_err(|_| io::Error::new_const(ErrorKind::Other, &"nodelay failed")) } pub fn set_ttl(&self, tll: u32) -> io::Result<()> { abi::tcpstream::set_tll(*self.0.as_inner(), tll) - .map_err(|_| io::Error::new(ErrorKind::Other, "unable to set TTL")) + .map_err(|_| io::Error::new_const(ErrorKind::Other, &"unable to set TTL")) } pub fn ttl(&self) -> io::Result { abi::tcpstream::get_tll(*self.0.as_inner()) - .map_err(|_| io::Error::new(ErrorKind::Other, "unable to get TTL")) + .map_err(|_| io::Error::new_const(ErrorKind::Other, &"unable to get TTL")) } pub fn take_error(&self) -> io::Result> { - Err(io::Error::new(ErrorKind::Other, "take_error isn't supported")) + unsupported() } pub fn set_nonblocking(&self, mode: bool) -> io::Result<()> { abi::tcpstream::set_nonblocking(*self.0.as_inner(), mode) - .map_err(|_| io::Error::new(ErrorKind::Other, "unable to set blocking mode")) + .map_err(|_| io::Error::new_const(ErrorKind::Other, &"unable to set blocking mode")) } } @@ -222,12 +230,12 @@ impl TcpListener { pub fn accept(&self) -> io::Result<(TcpStream, SocketAddr)> { let (handle, ipaddr, port) = abi::tcplistener::accept(self.0.port()) - .map_err(|_| io::Error::new(ErrorKind::Other, "accept failed"))?; + .map_err(|_| io::Error::new_const(ErrorKind::Other, &"accept failed"))?; let saddr = match ipaddr { Ipv4(ref addr) => SocketAddr::new(IpAddr::V4(Ipv4Addr::from(addr.0)), port), Ipv6(ref addr) => SocketAddr::new(IpAddr::V6(Ipv6Addr::from(addr.0)), port), _ => { - return Err(io::Error::new(ErrorKind::Other, "accept failed")); + return Err(io::Error::new_const(ErrorKind::Other, &"accept failed")); } }; @@ -239,27 +247,27 @@ impl TcpListener { } pub fn set_ttl(&self, _: u32) -> io::Result<()> { - Err(io::Error::new(ErrorKind::Other, "not supported")) + unsupported() } pub fn ttl(&self) -> io::Result { - Err(io::Error::new(ErrorKind::Other, "not supported")) + unsupported() } pub fn set_only_v6(&self, _: bool) -> io::Result<()> { - Err(io::Error::new(ErrorKind::Other, "not supported")) + unsupported() } pub fn only_v6(&self) -> io::Result { - Err(io::Error::new(ErrorKind::Other, "not supported")) + unsupported() } pub fn take_error(&self) -> io::Result> { - Err(io::Error::new(ErrorKind::Other, "not supported")) + unsupported() } pub fn set_nonblocking(&self, _: bool) -> io::Result<()> { - Err(io::Error::new(ErrorKind::Other, "not supported")) + unsupported() } } @@ -273,127 +281,127 @@ pub struct UdpSocket(abi::Handle); impl UdpSocket { pub fn bind(_: io::Result<&SocketAddr>) -> io::Result { - Err(io::Error::new(ErrorKind::Other, "not supported")) + unsupported() } pub fn peer_addr(&self) -> io::Result { - Err(io::Error::new(ErrorKind::Other, "not supported")) + unsupported() } pub fn socket_addr(&self) -> io::Result { - Err(io::Error::new(ErrorKind::Other, "not supported")) + unsupported() } pub fn recv_from(&self, _: &mut [u8]) -> io::Result<(usize, SocketAddr)> { - Err(io::Error::new(ErrorKind::Other, "not supported")) + unsupported() } pub fn peek_from(&self, _: &mut [u8]) -> io::Result<(usize, SocketAddr)> { - Err(io::Error::new(ErrorKind::Other, "not supported")) + unsupported() } pub fn send_to(&self, _: &[u8], _: &SocketAddr) -> io::Result { - Err(io::Error::new(ErrorKind::Other, "not supported")) + unsupported() } pub fn duplicate(&self) -> io::Result { - Err(io::Error::new(ErrorKind::Other, "not supported")) + unsupported() } pub fn set_read_timeout(&self, _: Option) -> io::Result<()> { - Err(io::Error::new(ErrorKind::Other, "not supported")) + unsupported() } pub fn set_write_timeout(&self, _: Option) -> io::Result<()> { - Err(io::Error::new(ErrorKind::Other, "not supported")) + unsupported() } pub fn read_timeout(&self) -> io::Result> { - Err(io::Error::new(ErrorKind::Other, "not supported")) + unsupported() } pub fn write_timeout(&self) -> io::Result> { - Err(io::Error::new(ErrorKind::Other, "not supported")) + unsupported() } pub fn set_broadcast(&self, _: bool) -> io::Result<()> { - Err(io::Error::new(ErrorKind::Other, "not supported")) + unsupported() } pub fn broadcast(&self) -> io::Result { - Err(io::Error::new(ErrorKind::Other, "not supported")) + unsupported() } pub fn set_multicast_loop_v4(&self, _: bool) -> io::Result<()> { - Err(io::Error::new(ErrorKind::Other, "not supported")) + unsupported() } pub fn multicast_loop_v4(&self) -> io::Result { - Err(io::Error::new(ErrorKind::Other, "not supported")) + unsupported() } pub fn set_multicast_ttl_v4(&self, _: u32) -> io::Result<()> { - Err(io::Error::new(ErrorKind::Other, "not supported")) + unsupported() } pub fn multicast_ttl_v4(&self) -> io::Result { - Err(io::Error::new(ErrorKind::Other, "not supported")) + unsupported() } pub fn set_multicast_loop_v6(&self, _: bool) -> io::Result<()> { - Err(io::Error::new(ErrorKind::Other, "not supported")) + unsupported() } pub fn multicast_loop_v6(&self) -> io::Result { - Err(io::Error::new(ErrorKind::Other, "not supported")) + unsupported() } pub fn join_multicast_v4(&self, _: &Ipv4Addr, _: &Ipv4Addr) -> io::Result<()> { - Err(io::Error::new(ErrorKind::Other, "not supported")) + unsupported() } pub fn join_multicast_v6(&self, _: &Ipv6Addr, _: u32) -> io::Result<()> { - Err(io::Error::new(ErrorKind::Other, "not supported")) + unsupported() } pub fn leave_multicast_v4(&self, _: &Ipv4Addr, _: &Ipv4Addr) -> io::Result<()> { - Err(io::Error::new(ErrorKind::Other, "not supported")) + unsupported() } pub fn leave_multicast_v6(&self, _: &Ipv6Addr, _: u32) -> io::Result<()> { - Err(io::Error::new(ErrorKind::Other, "not supported")) + unsupported() } pub fn set_ttl(&self, _: u32) -> io::Result<()> { - Err(io::Error::new(ErrorKind::Other, "not supported")) + unsupported() } pub fn ttl(&self) -> io::Result { - Err(io::Error::new(ErrorKind::Other, "not supported")) + unsupported() } pub fn take_error(&self) -> io::Result> { - Err(io::Error::new(ErrorKind::Other, "not supported")) + unsupported() } pub fn set_nonblocking(&self, _: bool) -> io::Result<()> { - Err(io::Error::new(ErrorKind::Other, "not supported")) + unsupported() } pub fn recv(&self, _: &mut [u8]) -> io::Result { - Err(io::Error::new(ErrorKind::Other, "not supported")) + unsupported() } pub fn peek(&self, _: &mut [u8]) -> io::Result { - Err(io::Error::new(ErrorKind::Other, "not supported")) + unsupported() } pub fn send(&self, _: &[u8]) -> io::Result { - Err(io::Error::new(ErrorKind::Other, "not supported")) + unsupported() } pub fn connect(&self, _: io::Result<&SocketAddr>) -> io::Result<()> { - Err(io::Error::new(ErrorKind::Other, "not supported")) + unsupported() } } @@ -403,18 +411,18 @@ impl fmt::Debug for UdpSocket { } } -pub struct LookupHost(Void); +pub struct LookupHost(!); impl LookupHost { pub fn port(&self) -> u16 { - match self.0 {} + self.0 } } impl Iterator for LookupHost { type Item = SocketAddr; fn next(&mut self) -> Option { - match self.0 {} + self.0 } } diff --git a/library/std/src/sys/hermit/os.rs b/library/std/src/sys/hermit/os.rs index 78eabf8f81e98..4487e9d636cb0 100644 --- a/library/std/src/sys/hermit/os.rs +++ b/library/std/src/sys/hermit/os.rs @@ -9,7 +9,7 @@ use crate::path::{self, PathBuf}; use crate::str; use crate::sync::Mutex; use crate::sys::hermit::abi; -use crate::sys::{unsupported, Void}; +use crate::sys::unsupported; use crate::sys_common::os_str_bytes::*; use crate::vec; @@ -29,7 +29,7 @@ pub fn chdir(_: &path::Path) -> io::Result<()> { unsupported() } -pub struct SplitPaths<'a>(&'a Void); +pub struct SplitPaths<'a>(!, PhantomData<&'a ()>); pub fn split_paths(_unparsed: &OsStr) -> SplitPaths<'_> { panic!("unsupported") @@ -38,7 +38,7 @@ pub fn split_paths(_unparsed: &OsStr) -> SplitPaths<'_> { impl<'a> Iterator for SplitPaths<'a> { type Item = PathBuf; fn next(&mut self) -> Option { - match *self.0 {} + self.0 } } diff --git a/library/std/src/sys/hermit/pipe.rs b/library/std/src/sys/hermit/pipe.rs deleted file mode 100644 index 10d0925823eb9..0000000000000 --- a/library/std/src/sys/hermit/pipe.rs +++ /dev/null @@ -1,38 +0,0 @@ -use crate::io::{self, IoSlice, IoSliceMut}; -use crate::sys::Void; - -pub struct AnonPipe(Void); - -impl AnonPipe { - pub fn read(&self, _buf: &mut [u8]) -> io::Result { - match self.0 {} - } - - pub fn read_vectored(&self, _bufs: &mut [IoSliceMut<'_>]) -> io::Result { - match self.0 {} - } - - pub fn is_read_vectored(&self) -> bool { - match self.0 {} - } - - pub fn write(&self, _buf: &[u8]) -> io::Result { - match self.0 {} - } - - pub fn write_vectored(&self, _bufs: &[IoSlice<'_>]) -> io::Result { - match self.0 {} - } - - pub fn is_write_vectored(&self) -> bool { - match self.0 {} - } - - pub fn diverge(&self) -> ! { - match self.0 {} - } -} - -pub fn read2(p1: AnonPipe, _v1: &mut Vec, _p2: AnonPipe, _v2: &mut Vec) -> io::Result<()> { - match p1.0 {} -} diff --git a/library/std/src/sys/hermit/stdio.rs b/library/std/src/sys/hermit/stdio.rs index 82304dd6dc293..6bff13ca92cca 100644 --- a/library/std/src/sys/hermit/stdio.rs +++ b/library/std/src/sys/hermit/stdio.rs @@ -40,7 +40,7 @@ impl io::Write for Stdout { unsafe { len = abi::write(1, data.as_ptr() as *const u8, data.len()) } if len < 0 { - Err(io::Error::new(io::ErrorKind::Other, "Stdout is not able to print")) + Err(io::Error::new_const(io::ErrorKind::Other, &"Stdout is not able to print")) } else { Ok(len as usize) } @@ -52,7 +52,7 @@ impl io::Write for Stdout { unsafe { len = abi::write(1, data.as_ptr() as *const u8, data.len()) } if len < 0 { - Err(io::Error::new(io::ErrorKind::Other, "Stdout is not able to print")) + Err(io::Error::new_const(io::ErrorKind::Other, &"Stdout is not able to print")) } else { Ok(len as usize) } @@ -81,7 +81,7 @@ impl io::Write for Stderr { unsafe { len = abi::write(2, data.as_ptr() as *const u8, data.len()) } if len < 0 { - Err(io::Error::new(io::ErrorKind::Other, "Stderr is not able to print")) + Err(io::Error::new_const(io::ErrorKind::Other, &"Stderr is not able to print")) } else { Ok(len as usize) } @@ -93,7 +93,7 @@ impl io::Write for Stderr { unsafe { len = abi::write(2, data.as_ptr() as *const u8, data.len()) } if len < 0 { - Err(io::Error::new(io::ErrorKind::Other, "Stderr is not able to print")) + Err(io::Error::new_const(io::ErrorKind::Other, &"Stderr is not able to print")) } else { Ok(len as usize) } diff --git a/library/std/src/sys/hermit/thread.rs b/library/std/src/sys/hermit/thread.rs index 7bd71e120de40..f35a3a8a80fc5 100644 --- a/library/std/src/sys/hermit/thread.rs +++ b/library/std/src/sys/hermit/thread.rs @@ -37,7 +37,7 @@ impl Thread { // The thread failed to start and as a result p was not consumed. Therefore, it is // safe to reconstruct the box so that it gets deallocated. drop(Box::from_raw(p)); - Err(io::Error::new(io::ErrorKind::Other, "Unable to create thread!")) + Err(io::Error::new_const(io::ErrorKind::Other, &"Unable to create thread!")) } else { Ok(Thread { tid: tid }) }; diff --git a/library/std/src/sys/mod.rs b/library/std/src/sys/mod.rs index 1e79a5c3f9bd5..2450a7aac5ede 100644 --- a/library/std/src/sys/mod.rs +++ b/library/std/src/sys/mod.rs @@ -22,11 +22,10 @@ #![allow(missing_debug_implementations)] +mod common; + cfg_if::cfg_if! { - if #[cfg(target_os = "vxworks")] { - mod vxworks; - pub use self::vxworks::*; - } else if #[cfg(unix)] { + if #[cfg(unix)] { mod unix; pub use self::unix::*; } else if #[cfg(windows)] { @@ -70,8 +69,6 @@ cfg_if::cfg_if! { #[allow(missing_docs)] pub mod unix_ext {} } else { - // On other platforms like Windows document the bare bones of unix - use crate::os::linux as platform; #[path = "unix/ext/mod.rs"] pub mod unix_ext; } diff --git a/library/std/src/sys/sgx/abi/mem.rs b/library/std/src/sys/sgx/abi/mem.rs index 3753fdd353617..1e743894a9fea 100644 --- a/library/std/src/sys/sgx/abi/mem.rs +++ b/library/std/src/sys/sgx/abi/mem.rs @@ -36,10 +36,9 @@ pub fn image_base() -> u64 { let base: u64; unsafe { asm!( - "lea IMAGE_BASE(%rip), {}", + "lea {}, qword ptr [rip + IMAGE_BASE]", lateout(reg) base, - // NOTE(#76738): ATT syntax is used to support LLVM 8 and 9. - options(att_syntax, nostack, preserves_flags, nomem, pure), + options(nostack, preserves_flags, nomem, pure), ) }; base diff --git a/library/std/src/sys/sgx/args.rs b/library/std/src/sys/sgx/args.rs index 2d2e692ec7d35..463188ad7c0c3 100644 --- a/library/std/src/sys/sgx/args.rs +++ b/library/std/src/sys/sgx/args.rs @@ -1,5 +1,6 @@ use super::abi::usercalls::{alloc, raw::ByteBuffer}; use crate::ffi::OsString; +use crate::fmt; use crate::slice; use crate::sync::atomic::{AtomicUsize, Ordering}; use crate::sys::os_str::Buf; @@ -31,9 +32,9 @@ pub fn args() -> Args { pub struct Args(slice::Iter<'static, OsString>); -impl Args { - pub fn inner_debug(&self) -> &[OsString] { - self.0.as_slice() +impl fmt::Debug for Args { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.0.as_slice().fmt(f) } } diff --git a/library/std/src/sys/sgx/ext/arch.rs b/library/std/src/sys/sgx/ext/arch.rs index 7488e7e5dc9ec..730db34e73328 100644 --- a/library/std/src/sys/sgx/ext/arch.rs +++ b/library/std/src/sys/sgx/ext/arch.rs @@ -36,8 +36,7 @@ pub fn egetkey(request: &Align512<[u8; 512]>) -> Result, u32> inlateout("eax") ENCLU_EGETKEY => error, in("rbx") request, in("rcx") out.as_mut_ptr(), - // NOTE(#76738): ATT syntax is used to support LLVM 8 and 9. - options(att_syntax, nostack), + options(nostack), ); match error { @@ -66,8 +65,7 @@ pub fn ereport( in("rbx") targetinfo, in("rcx") reportdata, in("rdx") report.as_mut_ptr(), - // NOTE(#76738): ATT syntax is used to support LLVM 8 and 9. - options(att_syntax, preserves_flags, nostack), + options(preserves_flags, nostack), ); report.assume_init() diff --git a/library/std/src/sys/sgx/mod.rs b/library/std/src/sys/sgx/mod.rs index b10bed621dbad..d6a5683073309 100644 --- a/library/std/src/sys/sgx/mod.rs +++ b/library/std/src/sys/sgx/mod.rs @@ -50,7 +50,7 @@ pub fn unsupported() -> crate::io::Result { } pub fn unsupported_err() -> crate::io::Error { - crate::io::Error::new(ErrorKind::Other, "operation not supported on SGX yet") + crate::io::Error::new_const(ErrorKind::Unsupported, &"operation not supported on SGX yet") } /// This function is used to implement various functions that doesn't exist, @@ -61,9 +61,9 @@ pub fn unsupported_err() -> crate::io::Error { pub fn sgx_ineffective(v: T) -> crate::io::Result { static SGX_INEFFECTIVE_ERROR: AtomicBool = AtomicBool::new(false); if SGX_INEFFECTIVE_ERROR.load(Ordering::Relaxed) { - Err(crate::io::Error::new( + Err(crate::io::Error::new_const( ErrorKind::Other, - "operation can't be trusted to have any effect on SGX", + &"operation can't be trusted to have any effect on SGX", )) } else { Ok(v) @@ -115,11 +115,6 @@ pub fn decode_error_kind(code: i32) -> ErrorKind { } } -// This enum is used as the storage for a bunch of types which can't actually -// exist. -#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)] -pub enum Void {} - pub unsafe fn strlen(mut s: *const c_char) -> usize { let mut n = 0; while unsafe { *s } != 0 { diff --git a/library/std/src/sys/sgx/net.rs b/library/std/src/sys/sgx/net.rs index 3dd8267921e5e..5ccedece0f84b 100644 --- a/library/std/src/sys/sgx/net.rs +++ b/library/std/src/sys/sgx/net.rs @@ -5,7 +5,7 @@ use crate::io::{self, IoSlice, IoSliceMut}; use crate::net::{Ipv4Addr, Ipv6Addr, Shutdown, SocketAddr, ToSocketAddrs}; use crate::sync::Arc; use crate::sys::fd::FileDesc; -use crate::sys::{sgx_ineffective, unsupported, AsInner, FromInner, IntoInner, TryIntoInner, Void}; +use crate::sys::{sgx_ineffective, unsupported, AsInner, FromInner, IntoInner, TryIntoInner}; use crate::time::Duration; use super::abi::usercalls; @@ -97,9 +97,9 @@ impl TcpStream { pub fn connect_timeout(addr: &SocketAddr, dur: Duration) -> io::Result { if dur == Duration::default() { - return Err(io::Error::new( + return Err(io::Error::new_const( io::ErrorKind::InvalidInput, - "cannot set a 0 duration timeout", + &"cannot set a 0 duration timeout", )); } Self::connect(Ok(addr)) // FIXME: ignoring timeout @@ -108,9 +108,9 @@ impl TcpStream { pub fn set_read_timeout(&self, dur: Option) -> io::Result<()> { match dur { Some(dur) if dur == Duration::default() => { - return Err(io::Error::new( + return Err(io::Error::new_const( io::ErrorKind::InvalidInput, - "cannot set a 0 duration timeout", + &"cannot set a 0 duration timeout", )); } _ => sgx_ineffective(()), @@ -120,9 +120,9 @@ impl TcpStream { pub fn set_write_timeout(&self, dur: Option) -> io::Result<()> { match dur { Some(dur) if dur == Duration::default() => { - return Err(io::Error::new( + return Err(io::Error::new_const( io::ErrorKind::InvalidInput, - "cannot set a 0 duration timeout", + &"cannot set a 0 duration timeout", )); } _ => sgx_ineffective(()), @@ -310,7 +310,7 @@ impl FromInner for TcpListener { } } -pub struct UdpSocket(Void); +pub struct UdpSocket(!); impl UdpSocket { pub fn bind(_: io::Result<&SocketAddr>) -> io::Result { @@ -318,129 +318,129 @@ impl UdpSocket { } pub fn peer_addr(&self) -> io::Result { - match self.0 {} + self.0 } pub fn socket_addr(&self) -> io::Result { - match self.0 {} + self.0 } pub fn recv_from(&self, _: &mut [u8]) -> io::Result<(usize, SocketAddr)> { - match self.0 {} + self.0 } pub fn peek_from(&self, _: &mut [u8]) -> io::Result<(usize, SocketAddr)> { - match self.0 {} + self.0 } pub fn send_to(&self, _: &[u8], _: &SocketAddr) -> io::Result { - match self.0 {} + self.0 } pub fn duplicate(&self) -> io::Result { - match self.0 {} + self.0 } pub fn set_read_timeout(&self, _: Option) -> io::Result<()> { - match self.0 {} + self.0 } pub fn set_write_timeout(&self, _: Option) -> io::Result<()> { - match self.0 {} + self.0 } pub fn read_timeout(&self) -> io::Result> { - match self.0 {} + self.0 } pub fn write_timeout(&self) -> io::Result> { - match self.0 {} + self.0 } pub fn set_broadcast(&self, _: bool) -> io::Result<()> { - match self.0 {} + self.0 } pub fn broadcast(&self) -> io::Result { - match self.0 {} + self.0 } pub fn set_multicast_loop_v4(&self, _: bool) -> io::Result<()> { - match self.0 {} + self.0 } pub fn multicast_loop_v4(&self) -> io::Result { - match self.0 {} + self.0 } pub fn set_multicast_ttl_v4(&self, _: u32) -> io::Result<()> { - match self.0 {} + self.0 } pub fn multicast_ttl_v4(&self) -> io::Result { - match self.0 {} + self.0 } pub fn set_multicast_loop_v6(&self, _: bool) -> io::Result<()> { - match self.0 {} + self.0 } pub fn multicast_loop_v6(&self) -> io::Result { - match self.0 {} + self.0 } pub fn join_multicast_v4(&self, _: &Ipv4Addr, _: &Ipv4Addr) -> io::Result<()> { - match self.0 {} + self.0 } pub fn join_multicast_v6(&self, _: &Ipv6Addr, _: u32) -> io::Result<()> { - match self.0 {} + self.0 } pub fn leave_multicast_v4(&self, _: &Ipv4Addr, _: &Ipv4Addr) -> io::Result<()> { - match self.0 {} + self.0 } pub fn leave_multicast_v6(&self, _: &Ipv6Addr, _: u32) -> io::Result<()> { - match self.0 {} + self.0 } pub fn set_ttl(&self, _: u32) -> io::Result<()> { - match self.0 {} + self.0 } pub fn ttl(&self) -> io::Result { - match self.0 {} + self.0 } pub fn take_error(&self) -> io::Result> { - match self.0 {} + self.0 } pub fn set_nonblocking(&self, _: bool) -> io::Result<()> { - match self.0 {} + self.0 } pub fn recv(&self, _: &mut [u8]) -> io::Result { - match self.0 {} + self.0 } pub fn peek(&self, _: &mut [u8]) -> io::Result { - match self.0 {} + self.0 } pub fn send(&self, _: &[u8]) -> io::Result { - match self.0 {} + self.0 } pub fn connect(&self, _: io::Result<&SocketAddr>) -> io::Result<()> { - match self.0 {} + self.0 } } impl fmt::Debug for UdpSocket { fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self.0 {} + self.0 } } @@ -462,7 +462,7 @@ impl fmt::Display for NonIpSockAddr { } } -pub struct LookupHost(Void); +pub struct LookupHost(!); impl LookupHost { fn new(host: String) -> io::Result { @@ -470,14 +470,14 @@ impl LookupHost { } pub fn port(&self) -> u16 { - match self.0 {} + self.0 } } impl Iterator for LookupHost { type Item = SocketAddr; fn next(&mut self) -> Option { - match self.0 {} + self.0 } } diff --git a/library/std/src/sys/sgx/os.rs b/library/std/src/sys/sgx/os.rs index 56fc84b4a3fca..144248d60c9cf 100644 --- a/library/std/src/sys/sgx/os.rs +++ b/library/std/src/sys/sgx/os.rs @@ -5,12 +5,13 @@ use crate::error::Error as StdError; use crate::ffi::{OsStr, OsString}; use crate::fmt; use crate::io; +use crate::marker::PhantomData; use crate::path::{self, PathBuf}; use crate::str; use crate::sync::atomic::{AtomicUsize, Ordering}; use crate::sync::Mutex; use crate::sync::Once; -use crate::sys::{decode_error_kind, sgx_ineffective, unsupported, Void}; +use crate::sys::{decode_error_kind, sgx_ineffective, unsupported}; use crate::vec; pub fn errno() -> i32 { @@ -35,7 +36,7 @@ pub fn chdir(_: &path::Path) -> io::Result<()> { sgx_ineffective(()) } -pub struct SplitPaths<'a>(&'a Void); +pub struct SplitPaths<'a>(!, PhantomData<&'a ()>); pub fn split_paths(_unparsed: &OsStr) -> SplitPaths<'_> { panic!("unsupported") @@ -44,7 +45,7 @@ pub fn split_paths(_unparsed: &OsStr) -> SplitPaths<'_> { impl<'a> Iterator for SplitPaths<'a> { type Item = PathBuf; fn next(&mut self) -> Option { - match *self.0 {} + self.0 } } diff --git a/library/std/src/sys/unix/alloc.rs b/library/std/src/sys/unix/alloc.rs index 964abe8b8c9ea..1b71905aa09b7 100644 --- a/library/std/src/sys/unix/alloc.rs +++ b/library/std/src/sys/unix/alloc.rs @@ -1,6 +1,6 @@ use crate::alloc::{GlobalAlloc, Layout, System}; use crate::ptr; -use crate::sys_common::alloc::{realloc_fallback, MIN_ALIGN}; +use crate::sys::common::alloc::{realloc_fallback, MIN_ALIGN}; #[stable(feature = "alloc_system_type", since = "1.28.0")] unsafe impl GlobalAlloc for System { diff --git a/library/std/src/sys/unix/android.rs b/library/std/src/sys/unix/android.rs index ea05ee3d7cedf..cf6aa31b7cfe3 100644 --- a/library/std/src/sys/unix/android.rs +++ b/library/std/src/sys/unix/android.rs @@ -96,7 +96,7 @@ pub fn ftruncate64(fd: c_int, size: u64) -> io::Result<()> { Some(f) => cvt_r(|| f(fd, size as i64)).map(drop), None => { if size > i32::MAX as u64 { - Err(io::Error::new(io::ErrorKind::InvalidInput, "cannot truncate >2GB")) + Err(io::Error::new_const(io::ErrorKind::InvalidInput, &"cannot truncate >2GB")) } else { cvt_r(|| ftruncate(fd, size as i32)).map(drop) } @@ -123,7 +123,7 @@ pub unsafe fn cvt_pread64( if let Ok(o) = offset.try_into() { cvt(pread(fd, buf, count, o)) } else { - Err(io::Error::new(io::ErrorKind::InvalidInput, "cannot pread >2GB")) + Err(io::Error::new_const(io::ErrorKind::InvalidInput, &"cannot pread >2GB")) } }) } @@ -141,7 +141,7 @@ pub unsafe fn cvt_pwrite64( if let Ok(o) = offset.try_into() { cvt(pwrite(fd, buf, count, o)) } else { - Err(io::Error::new(io::ErrorKind::InvalidInput, "cannot pwrite >2GB")) + Err(io::Error::new_const(io::ErrorKind::InvalidInput, &"cannot pwrite >2GB")) } }) } diff --git a/library/std/src/sys/unix/args.rs b/library/std/src/sys/unix/args.rs index 6967647249390..cba0627e93a85 100644 --- a/library/std/src/sys/unix/args.rs +++ b/library/std/src/sys/unix/args.rs @@ -6,6 +6,7 @@ #![allow(dead_code)] // runtime init functions not used during testing use crate::ffi::OsString; +use crate::fmt; use crate::marker::PhantomData; use crate::vec; @@ -29,9 +30,9 @@ pub struct Args { _dont_send_or_sync_me: PhantomData<*mut ()>, } -impl Args { - pub fn inner_debug(&self) -> &[OsString] { - self.iter.as_slice() +impl fmt::Debug for Args { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.iter.as_slice().fmt(f) } } diff --git a/library/std/src/sys/unix/env.rs b/library/std/src/sys/unix/env.rs index 7f5e9b04dba4b..3a88dc083b06c 100644 --- a/library/std/src/sys/unix/env.rs +++ b/library/std/src/sys/unix/env.rs @@ -173,3 +173,14 @@ pub mod os { pub const EXE_SUFFIX: &str = ""; pub const EXE_EXTENSION: &str = ""; } + +#[cfg(target_os = "vxworks")] +pub mod os { + pub const FAMILY: &str = "unix"; + pub const OS: &str = "vxworks"; + pub const DLL_PREFIX: &str = "lib"; + pub const DLL_SUFFIX: &str = ".so"; + pub const DLL_EXTENSION: &str = "so"; + pub const EXE_SUFFIX: &str = ""; + pub const EXE_EXTENSION: &str = ""; +} diff --git a/library/std/src/sys/unix/ext/fs.rs b/library/std/src/sys/unix/ext/fs.rs index ba75b9bac8035..9a982a4acd9e2 100644 --- a/library/std/src/sys/unix/ext/fs.rs +++ b/library/std/src/sys/unix/ext/fs.rs @@ -2,11 +2,11 @@ #![stable(feature = "rust1", since = "1.0.0")] +use super::platform::fs::MetadataExt as _; use crate::fs::{self, OpenOptions, Permissions}; use crate::io; use crate::path::Path; use crate::sys; -use crate::sys::platform::fs::MetadataExt as UnixMetadataExt; use crate::sys_common::{AsInner, AsInnerMut, FromInner}; // Used for `File::read` on intra-doc links #[allow(unused_imports)] @@ -109,7 +109,7 @@ pub trait FileExt { } } if !buf.is_empty() { - Err(io::Error::new(io::ErrorKind::UnexpectedEof, "failed to fill whole buffer")) + Err(io::Error::new_const(io::ErrorKind::UnexpectedEof, &"failed to fill whole buffer")) } else { Ok(()) } @@ -191,9 +191,9 @@ pub trait FileExt { while !buf.is_empty() { match self.write_at(buf, offset) { Ok(0) => { - return Err(io::Error::new( + return Err(io::Error::new_const( io::ErrorKind::WriteZero, - "failed to write whole buffer", + &"failed to write whole buffer", )); } Ok(n) => { diff --git a/library/std/src/sys/unix/ext/mod.rs b/library/std/src/sys/unix/ext/mod.rs index f43546880983a..735bf35a3ced6 100644 --- a/library/std/src/sys/unix/ext/mod.rs +++ b/library/std/src/sys/unix/ext/mod.rs @@ -29,6 +29,44 @@ #![doc(cfg(unix))] #![allow(missing_docs)] +cfg_if::cfg_if! { + if #[cfg(doc)] { + // Use linux as the default platform when documenting on other platforms like Windows + use crate::os::linux as platform; + } else { + #[cfg(target_os = "android")] + use crate::os::android as platform; + #[cfg(target_os = "dragonfly")] + use crate::os::dragonfly as platform; + #[cfg(target_os = "emscripten")] + use crate::os::emscripten as platform; + #[cfg(target_os = "freebsd")] + use crate::os::freebsd as platform; + #[cfg(target_os = "fuchsia")] + use crate::os::fuchsia as platform; + #[cfg(target_os = "haiku")] + use crate::os::haiku as platform; + #[cfg(target_os = "illumos")] + use crate::os::illumos as platform; + #[cfg(target_os = "ios")] + use crate::os::ios as platform; + #[cfg(any(target_os = "linux", target_os = "l4re"))] + use crate::os::linux as platform; + #[cfg(target_os = "macos")] + use crate::os::macos as platform; + #[cfg(target_os = "netbsd")] + use crate::os::netbsd as platform; + #[cfg(target_os = "openbsd")] + use crate::os::openbsd as platform; + #[cfg(target_os = "redox")] + use crate::os::redox as platform; + #[cfg(target_os = "solaris")] + use crate::os::solaris as platform; + #[cfg(target_os = "vxworks")] + use crate::os::vxworks as platform; + } +} + pub mod ffi; pub mod fs; pub mod io; diff --git a/library/std/src/sys/unix/ext/net/addr.rs b/library/std/src/sys/unix/ext/net/addr.rs index 1f9036242eb59..459f3590e6470 100644 --- a/library/std/src/sys/unix/ext/net/addr.rs +++ b/library/std/src/sys/unix/ext/net/addr.rs @@ -2,7 +2,7 @@ use crate::ffi::OsStr; use crate::os::unix::ffi::OsStrExt; use crate::path::Path; use crate::sys::cvt; -use crate::{ascii, fmt, io, mem}; +use crate::{ascii, fmt, io, iter, mem}; // FIXME(#43348): Make libc adapt #[doc(cfg(...))] so we don't need these fake definitions here? #[cfg(not(unix))] @@ -29,19 +29,19 @@ pub(super) unsafe fn sockaddr_un(path: &Path) -> io::Result<(libc::sockaddr_un, let bytes = path.as_os_str().as_bytes(); if bytes.contains(&0) { - return Err(io::Error::new( + return Err(io::Error::new_const( io::ErrorKind::InvalidInput, - "paths may not contain interior null bytes", + &"paths may not contain interior null bytes", )); } if bytes.len() >= addr.sun_path.len() { - return Err(io::Error::new( + return Err(io::Error::new_const( io::ErrorKind::InvalidInput, - "path must be shorter than SUN_LEN", + &"path must be shorter than SUN_LEN", )); } - for (dst, src) in addr.sun_path.iter_mut().zip(bytes.iter()) { + for (dst, src) in iter::zip(&mut addr.sun_path, bytes) { *dst = *src as libc::c_char; } // null byte for pathname addresses is already there because we zeroed the @@ -118,9 +118,9 @@ impl SocketAddr { // linux returns zero bytes of address len = sun_path_offset(&addr) as libc::socklen_t; // i.e., zero-length address } else if addr.sun_family != libc::AF_UNIX as libc::sa_family_t { - return Err(io::Error::new( + return Err(io::Error::new_const( io::ErrorKind::InvalidInput, - "file descriptor did not correspond to a Unix socket", + &"file descriptor did not correspond to a Unix socket", )); } diff --git a/library/std/src/sys/unix/ext/net/ancillary.rs b/library/std/src/sys/unix/ext/net/ancillary.rs index 33d6a39af07f6..011ae643f8712 100644 --- a/library/std/src/sys/unix/ext/net/ancillary.rs +++ b/library/std/src/sys/unix/ext/net/ancillary.rs @@ -5,9 +5,7 @@ use crate::marker::PhantomData; use crate::mem::{size_of, zeroed}; use crate::os::unix::io::RawFd; use crate::path::Path; -#[cfg(target_os = "android")] -use crate::ptr::eq; -use crate::ptr::read_unaligned; +use crate::ptr::{eq, read_unaligned}; use crate::slice::from_raw_parts; use crate::sys::net::Socket; @@ -30,12 +28,10 @@ pub(super) fn recv_vectored_with_ancillary_from( ) -> io::Result<(usize, bool, io::Result)> { unsafe { let mut msg_name: libc::sockaddr_un = zeroed(); - let mut msg: libc::msghdr = zeroed(); msg.msg_name = &mut msg_name as *mut _ as *mut _; msg.msg_namelen = size_of::() as libc::socklen_t; msg.msg_iov = bufs.as_mut_ptr().cast(); - msg.msg_control = ancillary.buffer.as_mut_ptr().cast(); cfg_if::cfg_if! { if #[cfg(any(target_os = "android", all(target_os = "linux", target_env = "gnu")))] { msg.msg_iovlen = bufs.len() as libc::size_t; @@ -45,6 +41,7 @@ pub(super) fn recv_vectored_with_ancillary_from( target_os = "emscripten", target_os = "freebsd", all(target_os = "linux", target_env = "musl",), + target_os = "macos", target_os = "netbsd", target_os = "openbsd", ))] { @@ -52,6 +49,10 @@ pub(super) fn recv_vectored_with_ancillary_from( msg.msg_controllen = ancillary.buffer.len() as libc::socklen_t; } } + // macos requires that the control pointer is NULL when the len is 0. + if msg.msg_controllen > 0 { + msg.msg_control = ancillary.buffer.as_mut_ptr().cast(); + } let count = socket.recv_msg(&mut msg)?; @@ -79,7 +80,6 @@ pub(super) fn send_vectored_with_ancillary_to( msg.msg_name = &mut msg_name as *mut _ as *mut _; msg.msg_namelen = msg_namelen; msg.msg_iov = bufs.as_ptr() as *mut _; - msg.msg_control = ancillary.buffer.as_mut_ptr().cast(); cfg_if::cfg_if! { if #[cfg(any(target_os = "android", all(target_os = "linux", target_env = "gnu")))] { msg.msg_iovlen = bufs.len() as libc::size_t; @@ -89,6 +89,7 @@ pub(super) fn send_vectored_with_ancillary_to( target_os = "emscripten", target_os = "freebsd", all(target_os = "linux", target_env = "musl",), + target_os = "macos", target_os = "netbsd", target_os = "openbsd", ))] { @@ -96,6 +97,10 @@ pub(super) fn send_vectored_with_ancillary_to( msg.msg_controllen = ancillary.length as libc::socklen_t; } } + // macos requires that the control pointer is NULL when the len is 0. + if msg.msg_controllen > 0 { + msg.msg_control = ancillary.buffer.as_mut_ptr().cast(); + } ancillary.truncated = false; @@ -147,6 +152,7 @@ fn add_to_ancillary_data( target_os = "emscripten", target_os = "freebsd", all(target_os = "linux", target_env = "musl",), + target_os = "macos", target_os = "netbsd", target_os = "openbsd", ))] { @@ -159,14 +165,12 @@ fn add_to_ancillary_data( while !cmsg.is_null() { previous_cmsg = cmsg; cmsg = libc::CMSG_NXTHDR(&msg, cmsg); - cfg_if::cfg_if! { - // Android return the same pointer if it is the last cmsg. - // Therefore, check it if the previous pointer is the same as the current one. - if #[cfg(target_os = "android")] { - if cmsg == previous_cmsg { - break; - } - } + + // Most operating systems, but not Linux or emscripten, return the previous pointer + // when its length is zero. Therefore, check if the previous pointer is the same as + // the current one. + if eq(cmsg, previous_cmsg) { + break; } } @@ -184,6 +188,7 @@ fn add_to_ancillary_data( target_os = "emscripten", target_os = "freebsd", all(target_os = "linux", target_env = "musl",), + target_os = "macos", target_os = "netbsd", target_os = "openbsd", ))] { @@ -371,6 +376,7 @@ impl<'a> AncillaryData<'a> { target_os = "emscripten", target_os = "freebsd", all(target_os = "linux", target_env = "musl",), + target_os = "macos", target_os = "netbsd", target_os = "openbsd", ))] { @@ -421,6 +427,7 @@ impl<'a> Iterator for Messages<'a> { target_os = "emscripten", target_os = "freebsd", all(target_os = "linux", target_env = "musl",), + target_os = "macos", target_os = "netbsd", target_os = "openbsd", ))] { @@ -435,15 +442,13 @@ impl<'a> Iterator for Messages<'a> { }; let cmsg = cmsg.as_ref()?; - cfg_if::cfg_if! { - // Android return the same pointer if it is the last cmsg. - // Therefore, check it if the previous pointer is the same as the current one. - if #[cfg(target_os = "android")] { - if let Some(current) = self.current { - if eq(current, cmsg) { - return None; - } - } + + // Most operating systems, but not Linux or emscripten, return the previous pointer + // when its length is zero. Therefore, check if the previous pointer is the same as + // the current one. + if let Some(current) = self.current { + if eq(current, cmsg) { + return None; } } @@ -514,6 +519,12 @@ impl<'a> SocketAncillary<'a> { self.buffer.len() } + /// Returns `true` if the ancillary data is empty. + #[unstable(feature = "unix_socket_ancillary_data", issue = "76915")] + pub fn is_empty(&self) -> bool { + self.length == 0 + } + /// Returns the number of used bytes. #[unstable(feature = "unix_socket_ancillary_data", issue = "76915")] pub fn len(&self) -> usize { diff --git a/library/std/src/sys/unix/ext/process.rs b/library/std/src/sys/unix/ext/process.rs index 88a27f27f6628..bc3bc0dcb0ce3 100644 --- a/library/std/src/sys/unix/ext/process.rs +++ b/library/std/src/sys/unix/ext/process.rs @@ -62,9 +62,14 @@ pub trait CommandExt: Sealed { /// `fork`. This primarily means that any modifications made to memory on /// behalf of this closure will **not** be visible to the parent process. /// This is often a very constrained environment where normal operations - /// like `malloc` or acquiring a mutex are not guaranteed to work (due to + /// like `malloc`, accessing environment variables through [`std::env`] + /// or acquiring a mutex are not guaranteed to work (due to /// other threads perhaps still running when the `fork` was run). /// + /// For further details refer to the [POSIX fork() specification] + /// and the equivalent documentation for any targeted + /// platform, especially the requirements around *async-signal-safety*. + /// /// This also means that all resources such as file descriptors and /// memory-mapped regions got duplicated. It is your responsibility to make /// sure that the closure does not violate library invariants by making @@ -73,6 +78,10 @@ pub trait CommandExt: Sealed { /// When this closure is run, aspects such as the stdio file descriptors and /// working directory have successfully been changed, so output to these /// locations may not appear where intended. + /// + /// [POSIX fork() specification]: + /// https://pubs.opengroup.org/onlinepubs/9699919799/functions/fork.html + /// [`std::env`]: mod@crate::env #[stable(feature = "process_pre_exec", since = "1.34.0")] unsafe fn pre_exec(&mut self, f: F) -> &mut process::Command where @@ -188,12 +197,20 @@ impl CommandExt for process::Command { /// Unix-specific extensions to [`process::ExitStatus`]. /// +/// On Unix, `ExitStatus` **does not necessarily represent an exit status**, as passed to the +/// `exit` system call or returned by [`ExitStatus::code()`](crate::process::ExitStatus::code). +/// It represents **any wait status**, as returned by one of the `wait` family of system calls. +/// +/// This is because a Unix wait status (a Rust `ExitStatus`) can represent a Unix exit status, but +/// can also represent other kinds of process event. +/// /// This trait is sealed: it cannot be implemented outside the standard library. /// This is so that future additional methods are not breaking changes. #[stable(feature = "rust1", since = "1.0.0")] pub trait ExitStatusExt: Sealed { - /// Creates a new `ExitStatus` from the raw underlying `i32` return value of - /// a process. + /// Creates a new `ExitStatus` from the raw underlying integer status value from `wait` + /// + /// The value should be a **wait status, not an exit status**. #[stable(feature = "exit_status_from", since = "1.12.0")] fn from_raw(raw: i32) -> Self; @@ -210,18 +227,20 @@ pub trait ExitStatusExt: Sealed { /// If the process was stopped by a signal, returns that signal. /// /// In other words, if `WIFSTOPPED`, this returns `WSTOPSIG`. This is only possible if the status came from - /// a `wait` system call which was passed `WUNTRACED`, was then converted into an `ExitStatus`. + /// a `wait` system call which was passed `WUNTRACED`, and was then converted into an `ExitStatus`. #[unstable(feature = "unix_process_wait_more", issue = "80695")] fn stopped_signal(&self) -> Option; /// Whether the process was continued from a stopped status. /// /// Ie, `WIFCONTINUED`. This is only possible if the status came from a `wait` system call - /// which was passed `WCONTINUED`, was then converted into an `ExitStatus`. + /// which was passed `WCONTINUED`, and was then converted into an `ExitStatus`. #[unstable(feature = "unix_process_wait_more", issue = "80695")] fn continued(&self) -> bool; /// Returns the underlying raw `wait` status. + /// + /// The returned integer is a **wait status, not an exit status**. #[unstable(feature = "unix_process_wait_more", issue = "80695")] fn into_raw(self) -> i32; } diff --git a/library/std/src/sys/unix/ext/raw.rs b/library/std/src/sys/unix/ext/raw.rs index 3199a0bff0bcc..c292955cb4eea 100644 --- a/library/std/src/sys/unix/ext/raw.rs +++ b/library/std/src/sys/unix/ext/raw.rs @@ -24,10 +24,10 @@ pub type pid_t = i32; #[doc(inline)] #[stable(feature = "pthread_t", since = "1.8.0")] -pub use crate::sys::platform::raw::pthread_t; +pub use super::platform::raw::pthread_t; #[doc(inline)] #[stable(feature = "raw_ext", since = "1.1.0")] -pub use crate::sys::platform::raw::{blkcnt_t, time_t}; +pub use super::platform::raw::{blkcnt_t, time_t}; #[doc(inline)] #[stable(feature = "raw_ext", since = "1.1.0")] -pub use crate::sys::platform::raw::{blksize_t, dev_t, ino_t, mode_t, nlink_t, off_t}; +pub use super::platform::raw::{blksize_t, dev_t, ino_t, mode_t, nlink_t, off_t}; diff --git a/library/std/src/sys/unix/fs.rs b/library/std/src/sys/unix/fs.rs index d1b0ad9e5f847..16a7f727696ec 100644 --- a/library/std/src/sys/unix/fs.rs +++ b/library/std/src/sys/unix/fs.rs @@ -2,7 +2,7 @@ use crate::os::unix::prelude::*; use crate::ffi::{CStr, CString, OsStr, OsString}; use crate::fmt; -use crate::io::{self, Error, ErrorKind, IoSlice, IoSliceMut, SeekFrom}; +use crate::io::{self, Error, IoSlice, IoSliceMut, SeekFrom}; use crate::mem; use crate::path::{Path, PathBuf}; use crate::ptr; @@ -357,17 +357,17 @@ impl FileAttr { tv_nsec: ext.stx_btime.tv_nsec as _, })) } else { - Err(io::Error::new( + Err(io::Error::new_const( io::ErrorKind::Other, - "creation time is not available for the filesystem", + &"creation time is not available for the filesystem", )) }; } } - Err(io::Error::new( - io::ErrorKind::Other, - "creation time is not available on this platform \ + Err(io::Error::new_const( + io::ErrorKind::Unsupported, + &"creation time is not available on this platform \ currently", )) } @@ -1152,14 +1152,12 @@ pub fn canonicalize(p: &Path) -> io::Result { fn open_from(from: &Path) -> io::Result<(crate::fs::File, crate::fs::Metadata)> { use crate::fs::File; + use crate::sys_common::fs::NOT_FILE_ERROR; let reader = File::open(from)?; let metadata = reader.metadata()?; if !metadata.is_file() { - return Err(Error::new( - ErrorKind::InvalidInput, - "the source path is not an existing regular file", - )); + return Err(NOT_FILE_ERROR); } Ok((reader, metadata)) } diff --git a/library/std/src/sys/unix/kernel_copy.rs b/library/std/src/sys/unix/kernel_copy.rs index 200dbf06ff8a6..9687576bb6aeb 100644 --- a/library/std/src/sys/unix/kernel_copy.rs +++ b/library/std/src/sys/unix/kernel_copy.rs @@ -61,6 +61,7 @@ use crate::process::{ChildStderr, ChildStdin, ChildStdout}; use crate::ptr; use crate::sync::atomic::{AtomicBool, AtomicU8, Ordering}; use crate::sys::cvt; +use libc::{EBADF, EINVAL, ENOSYS, EOPNOTSUPP, EOVERFLOW, EPERM, EXDEV}; #[cfg(test)] mod tests; @@ -535,7 +536,7 @@ pub(super) fn copy_regular_files(reader: RawFd, writer: RawFd, max_len: u64) -> cvt(copy_file_range(INVALID_FD, ptr::null_mut(), INVALID_FD, ptr::null_mut(), 1, 0)) }; - if matches!(result.map_err(|e| e.raw_os_error()), Err(Some(libc::EBADF))) { + if matches!(result.map_err(|e| e.raw_os_error()), Err(Some(EBADF))) { HAS_COPY_FILE_RANGE.store(AVAILABLE, Ordering::Relaxed); } else { HAS_COPY_FILE_RANGE.store(UNAVAILABLE, Ordering::Relaxed); @@ -573,19 +574,20 @@ pub(super) fn copy_regular_files(reader: RawFd, writer: RawFd, max_len: u64) -> Err(err) => { return match err.raw_os_error() { // when file offset + max_length > u64::MAX - Some(libc::EOVERFLOW) => CopyResult::Fallback(written), - Some( - libc::ENOSYS | libc::EXDEV | libc::EINVAL | libc::EPERM | libc::EOPNOTSUPP, - ) => { + Some(EOVERFLOW) => CopyResult::Fallback(written), + Some(ENOSYS | EXDEV | EINVAL | EPERM | EOPNOTSUPP | EBADF) => { // Try fallback io::copy if either: // - Kernel version is < 4.5 (ENOSYS¹) // - Files are mounted on different fs (EXDEV) // - copy_file_range is broken in various ways on RHEL/CentOS 7 (EOPNOTSUPP) // - copy_file_range file is immutable or syscall is blocked by seccomp¹ (EPERM) // - copy_file_range cannot be used with pipes or device nodes (EINVAL) + // - the writer fd was opened with O_APPEND (EBADF²) // // ¹ these cases should be detected by the initial probe but we handle them here // anyway in case syscall interception changes during runtime + // ² actually invalid file descriptors would cause this too, but in that case + // the fallback code path is expected to encounter the same error again assert_eq!(written, 0); CopyResult::Fallback(0) } @@ -649,7 +651,7 @@ fn sendfile_splice(mode: SpliceMode, reader: RawFd, writer: RawFd, len: u64) -> Ok(ret) => written += ret as u64, Err(err) => { return match err.raw_os_error() { - Some(libc::ENOSYS | libc::EPERM) => { + Some(ENOSYS | EPERM) => { // syscall not supported (ENOSYS) // syscall is disallowed, e.g. by seccomp (EPERM) match mode { @@ -659,12 +661,12 @@ fn sendfile_splice(mode: SpliceMode, reader: RawFd, writer: RawFd, len: u64) -> assert_eq!(written, 0); CopyResult::Fallback(0) } - Some(libc::EINVAL) => { + Some(EINVAL) => { // splice/sendfile do not support this particular file descriptor (EINVAL) assert_eq!(written, 0); CopyResult::Fallback(0) } - Some(os_err) if mode == SpliceMode::Sendfile && os_err == libc::EOVERFLOW => { + Some(os_err) if mode == SpliceMode::Sendfile && os_err == EOVERFLOW => { CopyResult::Fallback(written) } _ => CopyResult::Error(err, written), diff --git a/library/std/src/sys/unix/kernel_copy/tests.rs b/library/std/src/sys/unix/kernel_copy/tests.rs index 77369cdd35fb6..3fe849e23e2e6 100644 --- a/library/std/src/sys/unix/kernel_copy/tests.rs +++ b/library/std/src/sys/unix/kernel_copy/tests.rs @@ -65,6 +65,24 @@ fn copy_specialization() -> Result<()> { result.and(rm1).and(rm2) } +#[test] +fn copies_append_mode_sink() -> Result<()> { + let tmp_path = tmpdir(); + let source_path = tmp_path.join("copies_append_mode.source"); + let sink_path = tmp_path.join("copies_append_mode.sink"); + let mut source = + OpenOptions::new().create(true).truncate(true).write(true).read(true).open(&source_path)?; + write!(source, "not empty")?; + source.seek(SeekFrom::Start(0))?; + let mut sink = OpenOptions::new().create(true).append(true).open(&sink_path)?; + + let copied = crate::io::copy(&mut source, &mut sink)?; + + assert_eq!(copied, 9); + + Ok(()) +} + #[bench] fn bench_file_to_file_copy(b: &mut test::Bencher) { const BYTES: usize = 128 * 1024; diff --git a/library/std/src/sys/unix/l4re.rs b/library/std/src/sys/unix/l4re.rs index a2912387108e1..3cf637c82285a 100644 --- a/library/std/src/sys/unix/l4re.rs +++ b/library/std/src/sys/unix/l4re.rs @@ -1,6 +1,9 @@ macro_rules! unimpl { () => { - return Err(io::Error::new(io::ErrorKind::Other, "No networking available on L4Re.")); + return Err(io::Error::new_const( + io::ErrorKind::Unsupported, + &"No networking available on L4Re.", + )); }; } diff --git a/library/std/src/sys/unix/mod.rs b/library/std/src/sys/unix/mod.rs index f8a5ee8996911..1316835a89d12 100644 --- a/library/std/src/sys/unix/mod.rs +++ b/library/std/src/sys/unix/mod.rs @@ -2,38 +2,6 @@ use crate::io::ErrorKind; -#[cfg(any(doc, target_os = "linux"))] -pub use crate::os::linux as platform; - -#[cfg(all(not(doc), target_os = "android"))] -pub use crate::os::android as platform; -#[cfg(all(not(doc), target_os = "dragonfly"))] -pub use crate::os::dragonfly as platform; -#[cfg(all(not(doc), target_os = "emscripten"))] -pub use crate::os::emscripten as platform; -#[cfg(all(not(doc), target_os = "freebsd"))] -pub use crate::os::freebsd as platform; -#[cfg(all(not(doc), target_os = "fuchsia"))] -pub use crate::os::fuchsia as platform; -#[cfg(all(not(doc), target_os = "haiku"))] -pub use crate::os::haiku as platform; -#[cfg(all(not(doc), target_os = "illumos"))] -pub use crate::os::illumos as platform; -#[cfg(all(not(doc), target_os = "ios"))] -pub use crate::os::ios as platform; -#[cfg(all(not(doc), target_os = "l4re"))] -pub use crate::os::linux as platform; -#[cfg(all(not(doc), target_os = "macos"))] -pub use crate::os::macos as platform; -#[cfg(all(not(doc), target_os = "netbsd"))] -pub use crate::os::netbsd as platform; -#[cfg(all(not(doc), target_os = "openbsd"))] -pub use crate::os::openbsd as platform; -#[cfg(all(not(doc), target_os = "redox"))] -pub use crate::os::redox as platform; -#[cfg(all(not(doc), target_os = "solaris"))] -pub use crate::os::solaris as platform; - pub use self::rand::hashmap_random_keys; pub use libc::strlen; @@ -103,6 +71,7 @@ pub fn init() { } else if #[cfg(not(any( target_os = "emscripten", target_os = "fuchsia", + target_os = "vxworks", // The poll on Darwin doesn't set POLLNVAL for closed fds. target_os = "macos", target_os = "ios", @@ -180,6 +149,7 @@ pub fn decode_error_kind(errno: i32) -> ErrorKind { libc::EINVAL => ErrorKind::InvalidInput, libc::ETIMEDOUT => ErrorKind::TimedOut, libc::EEXIST => ErrorKind::AlreadyExists, + libc::ENOSYS => ErrorKind::Unsupported, // These two constants can have the same value on some systems, // but different values on others, so we can't use a match diff --git a/library/std/src/sys/unix/net.rs b/library/std/src/sys/unix/net.rs index 587ffe15981d5..e6b61062d15ff 100644 --- a/library/std/src/sys/unix/net.rs +++ b/library/std/src/sys/unix/net.rs @@ -139,9 +139,9 @@ impl Socket { let mut pollfd = libc::pollfd { fd: self.0.raw(), events: libc::POLLOUT, revents: 0 }; if timeout.as_secs() == 0 && timeout.subsec_nanos() == 0 { - return Err(io::Error::new( + return Err(io::Error::new_const( io::ErrorKind::InvalidInput, - "cannot set a 0 duration timeout", + &"cannot set a 0 duration timeout", )); } @@ -150,7 +150,7 @@ impl Socket { loop { let elapsed = start.elapsed(); if elapsed >= timeout { - return Err(io::Error::new(io::ErrorKind::TimedOut, "connection timed out")); + return Err(io::Error::new_const(io::ErrorKind::TimedOut, &"connection timed out")); } let timeout = timeout - elapsed; @@ -177,7 +177,10 @@ impl Socket { // for POLLHUP rather than read readiness if pollfd.revents & libc::POLLHUP != 0 { let e = self.take_error()?.unwrap_or_else(|| { - io::Error::new(io::ErrorKind::Other, "no error set after POLLHUP") + io::Error::new_const( + io::ErrorKind::Other, + &"no error set after POLLHUP", + ) }); return Err(e); } @@ -318,9 +321,9 @@ impl Socket { let timeout = match dur { Some(dur) => { if dur.as_secs() == 0 && dur.subsec_nanos() == 0 { - return Err(io::Error::new( + return Err(io::Error::new_const( io::ErrorKind::InvalidInput, - "cannot set a 0 duration timeout", + &"cannot set a 0 duration timeout", )); } diff --git a/library/std/src/sys/unix/os.rs b/library/std/src/sys/unix/os.rs index 1d1118aa69434..503ba410097c8 100644 --- a/library/std/src/sys/unix/os.rs +++ b/library/std/src/sys/unix/os.rs @@ -21,8 +21,8 @@ use crate::slice; use crate::str; use crate::sys::cvt; use crate::sys::fd; +use crate::sys::rwlock::{RWLockReadGuard, StaticRWLock}; use crate::sys_common::mutex::{StaticMutex, StaticMutexGuard}; -use crate::sys_common::rwlock::{RWLockReadGuard, StaticRWLock}; use crate::vec; use libc::{c_char, c_int, c_void}; @@ -85,11 +85,6 @@ pub fn errno() -> i32 { unsafe { libc::errnoGet() } } -#[cfg(target_os = "vxworks")] -pub fn set_errno(e: i32) { - unsafe { libc::errnoSet(e as c_int) }; -} - #[cfg(target_os = "dragonfly")] pub fn errno() -> i32 { extern "C" { @@ -223,7 +218,7 @@ where impl fmt::Display for JoinPathsError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "path segment contains separator `{}`", PATH_SEPARATOR) + write!(f, "path segment contains separator `{}`", char::from(PATH_SEPARATOR)) } } @@ -287,9 +282,9 @@ pub fn current_exe() -> io::Result { 0, ))?; if path_len <= 1 { - return Err(io::Error::new( + return Err(io::Error::new_const( io::ErrorKind::Other, - "KERN_PROC_PATHNAME sysctl returned zero-length string", + &"KERN_PROC_PATHNAME sysctl returned zero-length string", )); } let mut path: Vec = Vec::with_capacity(path_len); @@ -310,9 +305,9 @@ pub fn current_exe() -> io::Result { if curproc_exe.is_file() { return crate::fs::read_link(curproc_exe); } - Err(io::Error::new( + Err(io::Error::new_const( io::ErrorKind::Other, - "/proc/curproc/exe doesn't point to regular file.", + &"/proc/curproc/exe doesn't point to regular file.", )) } sysctl().or_else(|_| procfs()) @@ -329,7 +324,7 @@ pub fn current_exe() -> io::Result { cvt(libc::sysctl(mib, 4, argv.as_mut_ptr() as *mut _, &mut argv_len, ptr::null_mut(), 0))?; argv.set_len(argv_len as usize); if argv[0].is_null() { - return Err(io::Error::new(io::ErrorKind::Other, "no current exe available")); + return Err(io::Error::new_const(io::ErrorKind::Other, &"no current exe available")); } let argv0 = CStr::from_ptr(argv[0]).to_bytes(); if argv0[0] == b'.' || argv0.iter().any(|b| *b == b'/') { @@ -343,9 +338,9 @@ pub fn current_exe() -> io::Result { #[cfg(any(target_os = "linux", target_os = "android", target_os = "emscripten"))] pub fn current_exe() -> io::Result { match crate::fs::read_link("/proc/self/exe") { - Err(ref e) if e.kind() == io::ErrorKind::NotFound => Err(io::Error::new( + Err(ref e) if e.kind() == io::ErrorKind::NotFound => Err(io::Error::new_const( io::ErrorKind::Other, - "no /proc/self/exe available. Is /proc mounted?", + &"no /proc/self/exe available. Is /proc mounted?", )), other => other, } @@ -431,7 +426,7 @@ pub fn current_exe() -> io::Result { _get_next_image_info(0, &mut cookie, &mut info, mem::size_of::() as i32); if result != 0 { use crate::io::ErrorKind; - Err(io::Error::new(ErrorKind::Other, "Error getting executable path")) + Err(io::Error::new_const(ErrorKind::Other, &"Error getting executable path")) } else { let name = CStr::from_ptr(info.name.as_ptr()).to_bytes(); Ok(PathBuf::from(OsStr::from_bytes(name))) @@ -447,7 +442,7 @@ pub fn current_exe() -> io::Result { #[cfg(any(target_os = "fuchsia", target_os = "l4re"))] pub fn current_exe() -> io::Result { use crate::io::ErrorKind; - Err(io::Error::new(ErrorKind::Other, "Not yet implemented!")) + Err(io::Error::new_const(ErrorKind::Unsupported, &"Not yet implemented!")) } #[cfg(target_os = "vxworks")] @@ -642,7 +637,7 @@ pub fn getppid() -> u32 { unsafe { libc::getppid() as u32 } } -#[cfg(target_env = "gnu")] +#[cfg(all(target_env = "gnu", not(target_os = "vxworks")))] pub fn glibc_version() -> Option<(usize, usize)> { if let Some(Ok(version_str)) = glibc_version_cstr().map(CStr::to_str) { parse_glibc_version(version_str) @@ -651,7 +646,7 @@ pub fn glibc_version() -> Option<(usize, usize)> { } } -#[cfg(target_env = "gnu")] +#[cfg(all(target_env = "gnu", not(target_os = "vxworks")))] fn glibc_version_cstr() -> Option<&'static CStr> { weak! { fn gnu_get_libc_version() -> *const libc::c_char @@ -665,7 +660,7 @@ fn glibc_version_cstr() -> Option<&'static CStr> { // Returns Some((major, minor)) if the string is a valid "x.y" version, // ignoring any extra dot-separated parts. Otherwise return None. -#[cfg(target_env = "gnu")] +#[cfg(all(target_env = "gnu", not(target_os = "vxworks")))] fn parse_glibc_version(version: &str) -> Option<(usize, usize)> { let mut parsed_ints = version.split('.').map(str::parse::).fuse(); match (parsed_ints.next(), parsed_ints.next()) { diff --git a/library/std/src/sys/unix/process/mod.rs b/library/std/src/sys/unix/process/mod.rs index 1b7b93f9d4a5f..f67c70c01772f 100644 --- a/library/std/src/sys/unix/process/mod.rs +++ b/library/std/src/sys/unix/process/mod.rs @@ -4,11 +4,17 @@ pub use crate::ffi::OsString as EnvKey; pub use crate::sys_common::process::CommandEnvs; mod process_common; -#[cfg(not(target_os = "fuchsia"))] -#[path = "process_unix.rs"] -mod process_inner; -#[cfg(target_os = "fuchsia")] -#[path = "process_fuchsia.rs"] -mod process_inner; -#[cfg(target_os = "fuchsia")] -mod zircon; + +cfg_if::cfg_if! { + if #[cfg(target_os = "fuchsia")] { + #[path = "process_fuchsia.rs"] + mod process_inner; + mod zircon; + } else if #[cfg(target_os = "vxworks")] { + #[path = "process_vxworks.rs"] + mod process_inner; + } else { + #[path = "process_unix.rs"] + mod process_inner; + } +} diff --git a/library/std/src/sys/unix/process/process_fuchsia.rs b/library/std/src/sys/unix/process/process_fuchsia.rs index 0d4703d7f503a..b19ad4ccdc777 100644 --- a/library/std/src/sys/unix/process/process_fuchsia.rs +++ b/library/std/src/sys/unix/process/process_fuchsia.rs @@ -22,9 +22,9 @@ impl Command { let envp = self.capture_env(); if self.saw_nul() { - return Err(io::Error::new( + return Err(io::Error::new_const( io::ErrorKind::InvalidInput, - "nul byte found in provided data", + &"nul byte found in provided data", )); } @@ -37,7 +37,10 @@ impl Command { pub fn exec(&mut self, default: Stdio) -> io::Error { if self.saw_nul() { - return io::Error::new(io::ErrorKind::InvalidInput, "nul byte found in provided data"); + return io::Error::new_const( + io::ErrorKind::InvalidInput, + &"nul byte found in provided data", + ); } match self.setup_io(default, true) { @@ -182,9 +185,9 @@ impl Process { ))?; } if actual != 1 { - return Err(io::Error::new( + return Err(io::Error::new_const( io::ErrorKind::InvalidData, - "Failed to get exit status of process", + &"Failed to get exit status of process", )); } Ok(ExitStatus(proc_info.return_code)) @@ -220,9 +223,9 @@ impl Process { ))?; } if actual != 1 { - return Err(io::Error::new( + return Err(io::Error::new_const( io::ErrorKind::InvalidData, - "Failed to get exit status of process", + &"Failed to get exit status of process", )); } Ok(Some(ExitStatus(proc_info.return_code))) diff --git a/library/std/src/sys/unix/process/process_unix.rs b/library/std/src/sys/unix/process/process_unix.rs index 9e82df7755e89..ed9044382a898 100644 --- a/library/std/src/sys/unix/process/process_unix.rs +++ b/library/std/src/sys/unix/process/process_unix.rs @@ -1,6 +1,7 @@ use crate::convert::TryInto; use crate::fmt; use crate::io::{self, Error, ErrorKind}; +use crate::mem; use crate::ptr; use crate::sys; use crate::sys::cvt; @@ -27,7 +28,10 @@ impl Command { let envp = self.capture_env(); if self.saw_nul() { - return Err(io::Error::new(ErrorKind::InvalidInput, "nul byte found in provided data")); + return Err(io::Error::new_const( + ErrorKind::InvalidInput, + &"nul byte found in provided data", + )); } let (ours, theirs) = self.setup_io(default, needs_stdin)?; @@ -45,41 +49,37 @@ impl Command { // // Note that as soon as we're done with the fork there's no need to hold // a lock any more because the parent won't do anything and the child is - // in its own process. - let result = unsafe { - let _env_lock = sys::os::env_read_lock(); - cvt(libc::fork())? - }; + // in its own process. Thus the parent drops the lock guard while the child + // forgets it to avoid unlocking it on a new thread, which would be invalid. + let (env_lock, pid) = unsafe { (sys::os::env_read_lock(), cvt(libc::fork())?) }; - let pid = unsafe { - match result { - 0 => { - drop(input); - let Err(err) = self.do_exec(theirs, envp.as_ref()); - let errno = err.raw_os_error().unwrap_or(libc::EINVAL) as u32; - let errno = errno.to_be_bytes(); - let bytes = [ - errno[0], - errno[1], - errno[2], - errno[3], - CLOEXEC_MSG_FOOTER[0], - CLOEXEC_MSG_FOOTER[1], - CLOEXEC_MSG_FOOTER[2], - CLOEXEC_MSG_FOOTER[3], - ]; - // pipe I/O up to PIPE_BUF bytes should be atomic, and then - // we want to be sure we *don't* run at_exit destructors as - // we're being torn down regardless - rtassert!(output.write(&bytes).is_ok()); - libc::_exit(1) - } - n => n, - } - }; + if pid == 0 { + mem::forget(env_lock); + drop(input); + let Err(err) = unsafe { self.do_exec(theirs, envp.as_ref()) }; + let errno = err.raw_os_error().unwrap_or(libc::EINVAL) as u32; + let errno = errno.to_be_bytes(); + let bytes = [ + errno[0], + errno[1], + errno[2], + errno[3], + CLOEXEC_MSG_FOOTER[0], + CLOEXEC_MSG_FOOTER[1], + CLOEXEC_MSG_FOOTER[2], + CLOEXEC_MSG_FOOTER[3], + ]; + // pipe I/O up to PIPE_BUF bytes should be atomic, and then + // we want to be sure we *don't* run at_exit destructors as + // we're being torn down regardless + rtassert!(output.write(&bytes).is_ok()); + unsafe { libc::_exit(1) } + } - let mut p = Process { pid, status: None }; + drop(env_lock); drop(output); + + let mut p = Process { pid, status: None }; let mut bytes = [0; 8]; // loop to handle EINTR @@ -115,7 +115,10 @@ impl Command { let envp = self.capture_env(); if self.saw_nul() { - return io::Error::new(ErrorKind::InvalidInput, "nul byte found in provided data"); + return io::Error::new_const( + ErrorKind::InvalidInput, + &"nul byte found in provided data", + ); } match self.setup_io(default, true) { @@ -439,9 +442,9 @@ impl Process { // and used for another process, and we probably shouldn't be killing // random processes, so just return an error. if self.status.is_some() { - Err(Error::new( + Err(Error::new_const( ErrorKind::InvalidInput, - "invalid argument: can't kill an exited process", + &"invalid argument: can't kill an exited process", )) } else { cvt(unsafe { libc::kill(self.pid, libc::SIGKILL) }).map(drop) @@ -526,10 +529,23 @@ impl From for ExitStatus { impl fmt::Display for ExitStatus { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { if let Some(code) = self.code() { - write!(f, "exit code: {}", code) + write!(f, "exit status: {}", code) + } else if let Some(signal) = self.signal() { + if self.core_dumped() { + write!(f, "signal: {} (core dumped)", signal) + } else { + write!(f, "signal: {}", signal) + } + } else if let Some(signal) = self.stopped_signal() { + write!(f, "stopped (not terminated) by signal: {}", signal) + } else if self.continued() { + write!(f, "continued (WIFCONTINUED)") } else { - let signal = self.signal().unwrap(); - write!(f, "signal: {}", signal) + write!(f, "unrecognised wait status: {} {:#x}", self.0, self.0) } } } + +#[cfg(test)] +#[path = "process_unix/tests.rs"] +mod tests; diff --git a/library/std/src/sys/unix/process/process_unix/tests.rs b/library/std/src/sys/unix/process/process_unix/tests.rs new file mode 100644 index 0000000000000..02c469fbcdfd8 --- /dev/null +++ b/library/std/src/sys/unix/process/process_unix/tests.rs @@ -0,0 +1,30 @@ +#[test] +fn exitstatus_display_tests() { + // In practice this is the same on every Unix. + // If some weird platform turns out to be different, and this test fails, use #[cfg]. + use crate::os::unix::process::ExitStatusExt; + use crate::process::ExitStatus; + + let t = |v, s| assert_eq!(s, format!("{}", ::from_raw(v))); + + t(0x0000f, "signal: 15"); + t(0x0008b, "signal: 11 (core dumped)"); + t(0x00000, "exit status: 0"); + t(0x0ff00, "exit status: 255"); + + // On MacOS, 0x0137f is WIFCONTINUED, not WIFSTOPPED. Probably *BSD is similar. + // https://github.com/rust-lang/rust/pull/82749#issuecomment-790525956 + // The purpose of this test is to test our string formatting, not our understanding of the wait + // status magic numbers. So restrict these to Linux. + if cfg!(target_os = "linux") { + t(0x0137f, "stopped (not terminated) by signal: 19"); + t(0x0ffff, "continued (WIFCONTINUED)"); + } + + // Testing "unrecognised wait status" is hard because the wait.h macros typically + // assume that the value came from wait and isn't mad. With the glibc I have here + // this works: + if cfg!(all(target_os = "linux", target_env = "gnu")) { + t(0x000ff, "unrecognised wait status: 255 0xff"); + } +} diff --git a/library/std/src/sys/vxworks/process/process_vxworks.rs b/library/std/src/sys/unix/process/process_vxworks.rs similarity index 88% rename from library/std/src/sys/vxworks/process/process_vxworks.rs rename to library/std/src/sys/unix/process/process_vxworks.rs index 69adbcdddc971..eecdb624b9cfa 100644 --- a/library/std/src/sys/vxworks/process/process_vxworks.rs +++ b/library/std/src/sys/unix/process/process_vxworks.rs @@ -18,11 +18,13 @@ impl Command { needs_stdin: bool, ) -> io::Result<(Process, StdioPipes)> { use crate::sys::cvt_r; - const CLOEXEC_MSG_FOOTER: &'static [u8] = b"NOEX"; let envp = self.capture_env(); if self.saw_nul() { - return Err(io::Error::new(ErrorKind::InvalidInput, "nul byte found in provided data")); + return Err(io::Error::new_const( + ErrorKind::InvalidInput, + &"nul byte found in provided data", + )); } let (ours, theirs) = self.setup_io(default, needs_stdin)?; let mut p = Process { pid: 0, status: None }; @@ -58,6 +60,9 @@ impl Command { t!(cvt(libc::chdir(cwd.as_ptr()))); } + // pre_exec closures are ignored on VxWorks + let _ = self.get_closures(); + let c_envp = envp .as_ref() .map(|c| c.as_ptr()) @@ -65,7 +70,7 @@ impl Command { let stack_size = thread::min_stack(); // ensure that access to the environment is synchronized - let _lock = sys::os::env_lock(); + let _lock = sys::os::env_read_lock(); let ret = libc::rtpSpawn( self.get_program_cstr().as_ptr(), @@ -134,9 +139,9 @@ impl Process { // and used for another process, and we probably shouldn't be killing // random processes, so just return an error. if self.status.is_some() { - Err(Error::new( + Err(Error::new_const( ErrorKind::InvalidInput, - "invalid argument: can't kill an exited process", + &"invalid argument: can't kill an exited process", )) } else { cvt(unsafe { libc::kill(self.pid, libc::SIGKILL) }).map(drop) @@ -193,6 +198,24 @@ impl ExitStatus { pub fn signal(&self) -> Option { if !self.exited() { Some(libc::WTERMSIG(self.0)) } else { None } } + + pub fn core_dumped(&self) -> bool { + // This method is not yet properly implemented on VxWorks + false + } + + pub fn stopped_signal(&self) -> Option { + if libc::WIFSTOPPED(self.0) { Some(libc::WSTOPSIG(self.0)) } else { None } + } + + pub fn continued(&self) -> bool { + // This method is not yet properly implemented on VxWorks + false + } + + pub fn into_raw(&self) -> c_int { + self.0 + } } /// Converts a raw `c_int` to a type-safe `ExitStatus` by wrapping it without copying. diff --git a/library/std/src/sys/unix/rand.rs b/library/std/src/sys/unix/rand.rs index 38ddb41700c4b..44f9eabc319a0 100644 --- a/library/std/src/sys/unix/rand.rs +++ b/library/std/src/sys/unix/rand.rs @@ -18,7 +18,8 @@ pub fn hashmap_random_keys() -> (u64, u64) { not(target_os = "freebsd"), not(target_os = "netbsd"), not(target_os = "fuchsia"), - not(target_os = "redox") + not(target_os = "redox"), + not(target_os = "vxworks") ))] mod imp { use crate::fs::File; @@ -237,3 +238,29 @@ mod imp { file.read_exact(v).expect("failed to read rand:") } } + +#[cfg(target_os = "vxworks")] +mod imp { + use crate::io; + use core::sync::atomic::{AtomicBool, Ordering::Relaxed}; + + pub fn fill_bytes(v: &mut [u8]) { + static RNG_INIT: AtomicBool = AtomicBool::new(false); + while !RNG_INIT.load(Relaxed) { + let ret = unsafe { libc::randSecure() }; + if ret < 0 { + panic!("couldn't generate random bytes: {}", io::Error::last_os_error()); + } else if ret > 0 { + RNG_INIT.store(true, Relaxed); + break; + } + unsafe { libc::usleep(10) }; + } + let ret = unsafe { + libc::randABytes(v.as_mut_ptr() as *mut libc::c_uchar, v.len() as libc::c_int) + }; + if ret < 0 { + panic!("couldn't generate random bytes: {}", io::Error::last_os_error()); + } + } +} diff --git a/library/std/src/sys/unix/rwlock.rs b/library/std/src/sys/unix/rwlock.rs index 2b5067a34f648..d97d9d712fc93 100644 --- a/library/std/src/sys/unix/rwlock.rs +++ b/library/std/src/sys/unix/rwlock.rs @@ -139,3 +139,55 @@ impl RWLock { } } } + +pub struct StaticRWLock(RWLock); + +impl StaticRWLock { + pub const fn new() -> StaticRWLock { + StaticRWLock(RWLock::new()) + } + + /// Acquires shared access to the underlying lock, blocking the current + /// thread to do so. + /// + /// The lock is automatically unlocked when the returned guard is dropped. + #[inline] + pub fn read_with_guard(&'static self) -> RWLockReadGuard { + // SAFETY: All methods require static references, therefore self + // cannot be moved between invocations. + unsafe { + self.0.read(); + } + RWLockReadGuard(&self.0) + } + + /// Acquires write access to the underlying lock, blocking the current thread + /// to do so. + /// + /// The lock is automatically unlocked when the returned guard is dropped. + #[inline] + pub fn write_with_guard(&'static self) -> RWLockWriteGuard { + // SAFETY: All methods require static references, therefore self + // cannot be moved between invocations. + unsafe { + self.0.write(); + } + RWLockWriteGuard(&self.0) + } +} + +pub struct RWLockReadGuard(&'static RWLock); + +impl Drop for RWLockReadGuard { + fn drop(&mut self) { + unsafe { self.0.read_unlock() } + } +} + +pub struct RWLockWriteGuard(&'static RWLock); + +impl Drop for RWLockWriteGuard { + fn drop(&mut self) { + unsafe { self.0.write_unlock() } + } +} diff --git a/library/std/src/sys/unix/stack_overflow.rs b/library/std/src/sys/unix/stack_overflow.rs index d7bba50c76ab8..2a487fff54ae7 100644 --- a/library/std/src/sys/unix/stack_overflow.rs +++ b/library/std/src/sys/unix/stack_overflow.rs @@ -39,6 +39,7 @@ impl Drop for Handler { ))] mod imp { use super::Handler; + use crate::io; use crate::mem; use crate::ptr; @@ -149,11 +150,11 @@ mod imp { 0, ); if stackp == MAP_FAILED { - panic!("failed to allocate an alternative stack"); + panic!("failed to allocate an alternative stack: {}", io::Error::last_os_error()); } let guard_result = libc::mprotect(stackp, page_size(), PROT_NONE); if guard_result != 0 { - panic!("failed to set up alternative stack guard page"); + panic!("failed to set up alternative stack guard page: {}", io::Error::last_os_error()); } stackp.add(page_size()) } diff --git a/library/std/src/sys/unix/thread.rs b/library/std/src/sys/unix/thread.rs index 40c9630751416..b8f43caec32a3 100644 --- a/library/std/src/sys/unix/thread.rs +++ b/library/std/src/sys/unix/thread.rs @@ -231,6 +231,7 @@ pub mod guard { use libc::{mmap, mprotect}; use libc::{MAP_ANON, MAP_FAILED, MAP_FIXED, MAP_PRIVATE, PROT_NONE, PROT_READ, PROT_WRITE}; + use crate::io; use crate::ops::Range; use crate::sync::atomic::{AtomicUsize, Ordering}; use crate::sys::os; @@ -342,6 +343,20 @@ pub mod guard { // it can eventually grow to. It cannot be used to determine // the position of kernel's stack guard. None + } else if cfg!(target_os = "freebsd") { + // FreeBSD's stack autogrows, and optionally includes a guard page + // at the bottom. If we try to remap the bottom of the stack + // ourselves, FreeBSD's guard page moves upwards. So we'll just use + // the builtin guard page. + let stackaddr = get_stack_start_aligned()?; + let guardaddr = stackaddr as usize; + // Technically the number of guard pages is tunable and controlled + // by the security.bsd.stack_guard_page sysctl, but there are + // few reasons to change it from the default. The default value has + // been 1 ever since FreeBSD 11.1 and 10.4. + const GUARD_PAGES: usize = 1; + let guard = guardaddr..guardaddr + GUARD_PAGES * page_size; + Some(guard) } else { // Reallocate the last page of the stack. // This ensures SIGBUS will be raised on @@ -361,18 +376,17 @@ pub mod guard { 0, ); if result != stackaddr || result == MAP_FAILED { - panic!("failed to allocate a guard page"); + panic!("failed to allocate a guard page: {}", io::Error::last_os_error()); } let result = mprotect(stackaddr, page_size, PROT_NONE); if result != 0 { - panic!("failed to protect the guard page"); + panic!("failed to protect the guard page: {}", io::Error::last_os_error()); } let guardaddr = stackaddr as usize; - let offset = if cfg!(target_os = "freebsd") { 2 } else { 1 }; - Some(guardaddr..guardaddr + offset * page_size) + Some(guardaddr..guardaddr + page_size) } } @@ -416,11 +430,7 @@ pub mod guard { assert_eq!(libc::pthread_attr_getstack(&attr, &mut stackaddr, &mut size), 0); let stackaddr = stackaddr as usize; - ret = if cfg!(target_os = "freebsd") { - // FIXME does freebsd really fault *below* the guard addr? - let guardaddr = stackaddr - guardsize; - Some(guardaddr - PAGE_SIZE.load(Ordering::Relaxed)..guardaddr) - } else if cfg!(target_os = "netbsd") { + ret = if cfg!(any(target_os = "freebsd", target_os = "netbsd")) { Some(stackaddr - guardsize..stackaddr) } else if cfg!(all(target_os = "linux", target_env = "musl")) { Some(stackaddr - guardsize..stackaddr) diff --git a/library/std/src/sys/unix/thread_local_dtor.rs b/library/std/src/sys/unix/thread_local_dtor.rs index c3275eb6f0e50..c3f410353b915 100644 --- a/library/std/src/sys/unix/thread_local_dtor.rs +++ b/library/std/src/sys/unix/thread_local_dtor.rs @@ -92,3 +92,9 @@ pub unsafe fn register_dtor(t: *mut u8, dtor: unsafe extern "C" fn(*mut u8)) { } } } + +#[cfg(target_os = "vxworks")] +pub unsafe fn register_dtor(t: *mut u8, dtor: unsafe extern "C" fn(*mut u8)) { + use crate::sys_common::thread_local_dtor::register_dtor_fallback; + register_dtor_fallback(t, dtor); +} diff --git a/library/std/src/sys/unsupported/args.rs b/library/std/src/sys/unsupported/args.rs index 71d0c5fa13e18..360bb65af6953 100644 --- a/library/std/src/sys/unsupported/args.rs +++ b/library/std/src/sys/unsupported/args.rs @@ -9,9 +9,9 @@ pub fn args() -> Args { Args {} } -impl Args { - pub fn inner_debug(&self) -> &[OsString] { - &[] +impl fmt::Debug for Args { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_list().finish() } } diff --git a/library/std/src/sys/unsupported/common.rs b/library/std/src/sys/unsupported/common.rs index 2cdd9c4d19e6e..0ef84c84ee877 100644 --- a/library/std/src/sys/unsupported/common.rs +++ b/library/std/src/sys/unsupported/common.rs @@ -18,7 +18,10 @@ pub fn unsupported() -> std_io::Result { } pub fn unsupported_err() -> std_io::Error { - std_io::Error::new(std_io::ErrorKind::Other, "operation not supported on this platform") + std_io::Error::new_const( + std_io::ErrorKind::Unsupported, + &"operation not supported on this platform", + ) } pub fn decode_error_kind(_code: i32) -> crate::io::ErrorKind { @@ -33,11 +36,6 @@ pub fn hashmap_random_keys() -> (u64, u64) { (1, 2) } -// This enum is used as the storage for a bunch of types which can't actually -// exist. -#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)] -pub enum Void {} - pub unsafe fn strlen(mut s: *const c_char) -> usize { // SAFETY: The caller must guarantee `s` points to a valid 0-terminated string. unsafe { diff --git a/library/std/src/sys/unsupported/fs.rs b/library/std/src/sys/unsupported/fs.rs index 4271d9b334588..cd533761e3732 100644 --- a/library/std/src/sys/unsupported/fs.rs +++ b/library/std/src/sys/unsupported/fs.rs @@ -4,77 +4,77 @@ use crate::hash::{Hash, Hasher}; use crate::io::{self, IoSlice, IoSliceMut, SeekFrom}; use crate::path::{Path, PathBuf}; use crate::sys::time::SystemTime; -use crate::sys::{unsupported, Void}; +use crate::sys::unsupported; -pub struct File(Void); +pub struct File(!); -pub struct FileAttr(Void); +pub struct FileAttr(!); -pub struct ReadDir(Void); +pub struct ReadDir(!); -pub struct DirEntry(Void); +pub struct DirEntry(!); #[derive(Clone, Debug)] pub struct OpenOptions {} -pub struct FilePermissions(Void); +pub struct FilePermissions(!); -pub struct FileType(Void); +pub struct FileType(!); #[derive(Debug)] pub struct DirBuilder {} impl FileAttr { pub fn size(&self) -> u64 { - match self.0 {} + self.0 } pub fn perm(&self) -> FilePermissions { - match self.0 {} + self.0 } pub fn file_type(&self) -> FileType { - match self.0 {} + self.0 } pub fn modified(&self) -> io::Result { - match self.0 {} + self.0 } pub fn accessed(&self) -> io::Result { - match self.0 {} + self.0 } pub fn created(&self) -> io::Result { - match self.0 {} + self.0 } } impl Clone for FileAttr { fn clone(&self) -> FileAttr { - match self.0 {} + self.0 } } impl FilePermissions { pub fn readonly(&self) -> bool { - match self.0 {} + self.0 } pub fn set_readonly(&mut self, _readonly: bool) { - match self.0 {} + self.0 } } impl Clone for FilePermissions { fn clone(&self) -> FilePermissions { - match self.0 {} + self.0 } } impl PartialEq for FilePermissions { fn eq(&self, _other: &FilePermissions) -> bool { - match self.0 {} + self.0 } } @@ -82,27 +82,27 @@ impl Eq for FilePermissions {} impl fmt::Debug for FilePermissions { fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self.0 {} + self.0 } } impl FileType { pub fn is_dir(&self) -> bool { - match self.0 {} + self.0 } pub fn is_file(&self) -> bool { - match self.0 {} + self.0 } pub fn is_symlink(&self) -> bool { - match self.0 {} + self.0 } } impl Clone for FileType { fn clone(&self) -> FileType { - match self.0 {} + self.0 } } @@ -110,7 +110,7 @@ impl Copy for FileType {} impl PartialEq for FileType { fn eq(&self, _other: &FileType) -> bool { - match self.0 {} + self.0 } } @@ -118,19 +118,19 @@ impl Eq for FileType {} impl Hash for FileType { fn hash(&self, _h: &mut H) { - match self.0 {} + self.0 } } impl fmt::Debug for FileType { fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self.0 {} + self.0 } } impl fmt::Debug for ReadDir { fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self.0 {} + self.0 } } @@ -138,25 +138,25 @@ impl Iterator for ReadDir { type Item = io::Result; fn next(&mut self) -> Option> { - match self.0 {} + self.0 } } impl DirEntry { pub fn path(&self) -> PathBuf { - match self.0 {} + self.0 } pub fn file_name(&self) -> OsString { - match self.0 {} + self.0 } pub fn metadata(&self) -> io::Result { - match self.0 {} + self.0 } pub fn file_type(&self) -> io::Result { - match self.0 {} + self.0 } } @@ -179,59 +179,59 @@ impl File { } pub fn file_attr(&self) -> io::Result { - match self.0 {} + self.0 } pub fn fsync(&self) -> io::Result<()> { - match self.0 {} + self.0 } pub fn datasync(&self) -> io::Result<()> { - match self.0 {} + self.0 } pub fn truncate(&self, _size: u64) -> io::Result<()> { - match self.0 {} + self.0 } pub fn read(&self, _buf: &mut [u8]) -> io::Result { - match self.0 {} + self.0 } pub fn read_vectored(&self, _bufs: &mut [IoSliceMut<'_>]) -> io::Result { - match self.0 {} + self.0 } pub fn is_read_vectored(&self) -> bool { - match self.0 {} + self.0 } pub fn write(&self, _buf: &[u8]) -> io::Result { - match self.0 {} + self.0 } pub fn write_vectored(&self, _bufs: &[IoSlice<'_>]) -> io::Result { - match self.0 {} + self.0 } pub fn is_write_vectored(&self) -> bool { - match self.0 {} + self.0 } pub fn flush(&self) -> io::Result<()> { - match self.0 {} + self.0 } pub fn seek(&self, _pos: SeekFrom) -> io::Result { - match self.0 {} + self.0 } pub fn duplicate(&self) -> io::Result { - match self.0 {} + self.0 } pub fn set_permissions(&self, _perm: FilePermissions) -> io::Result<()> { - match self.0 {} + self.0 } } @@ -247,7 +247,7 @@ impl DirBuilder { impl fmt::Debug for File { fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self.0 {} + self.0 } } diff --git a/library/std/src/sys/unsupported/net.rs b/library/std/src/sys/unsupported/net.rs index 5c9f1098f9b7f..96203c74b576c 100644 --- a/library/std/src/sys/unsupported/net.rs +++ b/library/std/src/sys/unsupported/net.rs @@ -2,10 +2,10 @@ use crate::convert::TryFrom; use crate::fmt; use crate::io::{self, IoSlice, IoSliceMut}; use crate::net::{Ipv4Addr, Ipv6Addr, Shutdown, SocketAddr}; -use crate::sys::{unsupported, Void}; +use crate::sys::unsupported; use crate::time::Duration; -pub struct TcpStream(Void); +pub struct TcpStream(!); impl TcpStream { pub fn connect(_: io::Result<&SocketAddr>) -> io::Result { @@ -17,97 +17,97 @@ impl TcpStream { } pub fn set_read_timeout(&self, _: Option) -> io::Result<()> { - match self.0 {} + self.0 } pub fn set_write_timeout(&self, _: Option) -> io::Result<()> { - match self.0 {} + self.0 } pub fn read_timeout(&self) -> io::Result> { - match self.0 {} + self.0 } pub fn write_timeout(&self) -> io::Result> { - match self.0 {} + self.0 } pub fn peek(&self, _: &mut [u8]) -> io::Result { - match self.0 {} + self.0 } pub fn read(&self, _: &mut [u8]) -> io::Result { - match self.0 {} + self.0 } pub fn read_vectored(&self, _: &mut [IoSliceMut<'_>]) -> io::Result { - match self.0 {} + self.0 } pub fn is_read_vectored(&self) -> bool { - match self.0 {} + self.0 } pub fn write(&self, _: &[u8]) -> io::Result { - match self.0 {} + self.0 } pub fn write_vectored(&self, _: &[IoSlice<'_>]) -> io::Result { - match self.0 {} + self.0 } pub fn is_write_vectored(&self) -> bool { - match self.0 {} + self.0 } pub fn peer_addr(&self) -> io::Result { - match self.0 {} + self.0 } pub fn socket_addr(&self) -> io::Result { - match self.0 {} + self.0 } pub fn shutdown(&self, _: Shutdown) -> io::Result<()> { - match self.0 {} + self.0 } pub fn duplicate(&self) -> io::Result { - match self.0 {} + self.0 } pub fn set_nodelay(&self, _: bool) -> io::Result<()> { - match self.0 {} + self.0 } pub fn nodelay(&self) -> io::Result { - match self.0 {} + self.0 } pub fn set_ttl(&self, _: u32) -> io::Result<()> { - match self.0 {} + self.0 } pub fn ttl(&self) -> io::Result { - match self.0 {} + self.0 } pub fn take_error(&self) -> io::Result> { - match self.0 {} + self.0 } pub fn set_nonblocking(&self, _: bool) -> io::Result<()> { - match self.0 {} + self.0 } } impl fmt::Debug for TcpStream { fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self.0 {} + self.0 } } -pub struct TcpListener(Void); +pub struct TcpListener(!); impl TcpListener { pub fn bind(_: io::Result<&SocketAddr>) -> io::Result { @@ -115,49 +115,49 @@ impl TcpListener { } pub fn socket_addr(&self) -> io::Result { - match self.0 {} + self.0 } pub fn accept(&self) -> io::Result<(TcpStream, SocketAddr)> { - match self.0 {} + self.0 } pub fn duplicate(&self) -> io::Result { - match self.0 {} + self.0 } pub fn set_ttl(&self, _: u32) -> io::Result<()> { - match self.0 {} + self.0 } pub fn ttl(&self) -> io::Result { - match self.0 {} + self.0 } pub fn set_only_v6(&self, _: bool) -> io::Result<()> { - match self.0 {} + self.0 } pub fn only_v6(&self) -> io::Result { - match self.0 {} + self.0 } pub fn take_error(&self) -> io::Result> { - match self.0 {} + self.0 } pub fn set_nonblocking(&self, _: bool) -> io::Result<()> { - match self.0 {} + self.0 } } impl fmt::Debug for TcpListener { fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self.0 {} + self.0 } } -pub struct UdpSocket(Void); +pub struct UdpSocket(!); impl UdpSocket { pub fn bind(_: io::Result<&SocketAddr>) -> io::Result { @@ -165,144 +165,144 @@ impl UdpSocket { } pub fn peer_addr(&self) -> io::Result { - match self.0 {} + self.0 } pub fn socket_addr(&self) -> io::Result { - match self.0 {} + self.0 } pub fn recv_from(&self, _: &mut [u8]) -> io::Result<(usize, SocketAddr)> { - match self.0 {} + self.0 } pub fn peek_from(&self, _: &mut [u8]) -> io::Result<(usize, SocketAddr)> { - match self.0 {} + self.0 } pub fn send_to(&self, _: &[u8], _: &SocketAddr) -> io::Result { - match self.0 {} + self.0 } pub fn duplicate(&self) -> io::Result { - match self.0 {} + self.0 } pub fn set_read_timeout(&self, _: Option) -> io::Result<()> { - match self.0 {} + self.0 } pub fn set_write_timeout(&self, _: Option) -> io::Result<()> { - match self.0 {} + self.0 } pub fn read_timeout(&self) -> io::Result> { - match self.0 {} + self.0 } pub fn write_timeout(&self) -> io::Result> { - match self.0 {} + self.0 } pub fn set_broadcast(&self, _: bool) -> io::Result<()> { - match self.0 {} + self.0 } pub fn broadcast(&self) -> io::Result { - match self.0 {} + self.0 } pub fn set_multicast_loop_v4(&self, _: bool) -> io::Result<()> { - match self.0 {} + self.0 } pub fn multicast_loop_v4(&self) -> io::Result { - match self.0 {} + self.0 } pub fn set_multicast_ttl_v4(&self, _: u32) -> io::Result<()> { - match self.0 {} + self.0 } pub fn multicast_ttl_v4(&self) -> io::Result { - match self.0 {} + self.0 } pub fn set_multicast_loop_v6(&self, _: bool) -> io::Result<()> { - match self.0 {} + self.0 } pub fn multicast_loop_v6(&self) -> io::Result { - match self.0 {} + self.0 } pub fn join_multicast_v4(&self, _: &Ipv4Addr, _: &Ipv4Addr) -> io::Result<()> { - match self.0 {} + self.0 } pub fn join_multicast_v6(&self, _: &Ipv6Addr, _: u32) -> io::Result<()> { - match self.0 {} + self.0 } pub fn leave_multicast_v4(&self, _: &Ipv4Addr, _: &Ipv4Addr) -> io::Result<()> { - match self.0 {} + self.0 } pub fn leave_multicast_v6(&self, _: &Ipv6Addr, _: u32) -> io::Result<()> { - match self.0 {} + self.0 } pub fn set_ttl(&self, _: u32) -> io::Result<()> { - match self.0 {} + self.0 } pub fn ttl(&self) -> io::Result { - match self.0 {} + self.0 } pub fn take_error(&self) -> io::Result> { - match self.0 {} + self.0 } pub fn set_nonblocking(&self, _: bool) -> io::Result<()> { - match self.0 {} + self.0 } pub fn recv(&self, _: &mut [u8]) -> io::Result { - match self.0 {} + self.0 } pub fn peek(&self, _: &mut [u8]) -> io::Result { - match self.0 {} + self.0 } pub fn send(&self, _: &[u8]) -> io::Result { - match self.0 {} + self.0 } pub fn connect(&self, _: io::Result<&SocketAddr>) -> io::Result<()> { - match self.0 {} + self.0 } } impl fmt::Debug for UdpSocket { fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self.0 {} + self.0 } } -pub struct LookupHost(Void); +pub struct LookupHost(!); impl LookupHost { pub fn port(&self) -> u16 { - match self.0 {} + self.0 } } impl Iterator for LookupHost { type Item = SocketAddr; fn next(&mut self) -> Option { - match self.0 {} + self.0 } } diff --git a/library/std/src/sys/unsupported/os.rs b/library/std/src/sys/unsupported/os.rs index 0615780c24212..e30395a0b1d92 100644 --- a/library/std/src/sys/unsupported/os.rs +++ b/library/std/src/sys/unsupported/os.rs @@ -1,8 +1,9 @@ -use super::{unsupported, Void}; +use super::unsupported; use crate::error::Error as StdError; use crate::ffi::{OsStr, OsString}; use crate::fmt; use crate::io; +use crate::marker::PhantomData; use crate::path::{self, PathBuf}; pub fn errno() -> i32 { @@ -21,7 +22,7 @@ pub fn chdir(_: &path::Path) -> io::Result<()> { unsupported() } -pub struct SplitPaths<'a>(&'a Void); +pub struct SplitPaths<'a>(!, PhantomData<&'a ()>); pub fn split_paths(_unparsed: &OsStr) -> SplitPaths<'_> { panic!("unsupported") @@ -30,7 +31,7 @@ pub fn split_paths(_unparsed: &OsStr) -> SplitPaths<'_> { impl<'a> Iterator for SplitPaths<'a> { type Item = PathBuf; fn next(&mut self) -> Option { - match *self.0 {} + self.0 } } @@ -62,12 +63,12 @@ pub fn current_exe() -> io::Result { unsupported() } -pub struct Env(Void); +pub struct Env(!); impl Iterator for Env { type Item = (OsString, OsString); fn next(&mut self) -> Option<(OsString, OsString)> { - match self.0 {} + self.0 } } @@ -80,11 +81,11 @@ pub fn getenv(_: &OsStr) -> io::Result> { } pub fn setenv(_: &OsStr, _: &OsStr) -> io::Result<()> { - Err(io::Error::new(io::ErrorKind::Other, "cannot set env vars on this platform")) + Err(io::Error::new_const(io::ErrorKind::Unsupported, &"cannot set env vars on this platform")) } pub fn unsetenv(_: &OsStr) -> io::Result<()> { - Err(io::Error::new(io::ErrorKind::Other, "cannot unset env vars on this platform")) + Err(io::Error::new_const(io::ErrorKind::Unsupported, &"cannot unset env vars on this platform")) } pub fn temp_dir() -> PathBuf { diff --git a/library/std/src/sys/unsupported/pipe.rs b/library/std/src/sys/unsupported/pipe.rs index 10d0925823eb9..25514c2322fa4 100644 --- a/library/std/src/sys/unsupported/pipe.rs +++ b/library/std/src/sys/unsupported/pipe.rs @@ -1,35 +1,34 @@ use crate::io::{self, IoSlice, IoSliceMut}; -use crate::sys::Void; -pub struct AnonPipe(Void); +pub struct AnonPipe(!); impl AnonPipe { pub fn read(&self, _buf: &mut [u8]) -> io::Result { - match self.0 {} + self.0 } pub fn read_vectored(&self, _bufs: &mut [IoSliceMut<'_>]) -> io::Result { - match self.0 {} + self.0 } pub fn is_read_vectored(&self) -> bool { - match self.0 {} + self.0 } pub fn write(&self, _buf: &[u8]) -> io::Result { - match self.0 {} + self.0 } pub fn write_vectored(&self, _bufs: &[IoSlice<'_>]) -> io::Result { - match self.0 {} + self.0 } pub fn is_write_vectored(&self) -> bool { - match self.0 {} + self.0 } pub fn diverge(&self) -> ! { - match self.0 {} + self.0 } } diff --git a/library/std/src/sys/unsupported/process.rs b/library/std/src/sys/unsupported/process.rs index 3ede2291d5a91..38ac0a1ddd5f9 100644 --- a/library/std/src/sys/unsupported/process.rs +++ b/library/std/src/sys/unsupported/process.rs @@ -5,7 +5,7 @@ use crate::marker::PhantomData; use crate::path::Path; use crate::sys::fs::File; use crate::sys::pipe::AnonPipe; -use crate::sys::{unsupported, Void}; +use crate::sys::unsupported; use crate::sys_common::process::{CommandEnv, CommandEnvs}; pub use crate::ffi::OsString as EnvKey; @@ -94,21 +94,21 @@ impl fmt::Debug for Command { } } -pub struct ExitStatus(Void); +pub struct ExitStatus(!); impl ExitStatus { pub fn success(&self) -> bool { - match self.0 {} + self.0 } pub fn code(&self) -> Option { - match self.0 {} + self.0 } } impl Clone for ExitStatus { fn clone(&self) -> ExitStatus { - match self.0 {} + self.0 } } @@ -116,7 +116,7 @@ impl Copy for ExitStatus {} impl PartialEq for ExitStatus { fn eq(&self, _other: &ExitStatus) -> bool { - match self.0 {} + self.0 } } @@ -124,13 +124,13 @@ impl Eq for ExitStatus {} impl fmt::Debug for ExitStatus { fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self.0 {} + self.0 } } impl fmt::Display for ExitStatus { fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self.0 {} + self.0 } } @@ -146,23 +146,23 @@ impl ExitCode { } } -pub struct Process(Void); +pub struct Process(!); impl Process { pub fn id(&self) -> u32 { - match self.0 {} + self.0 } pub fn kill(&mut self) -> io::Result<()> { - match self.0 {} + self.0 } pub fn wait(&mut self) -> io::Result { - match self.0 {} + self.0 } pub fn try_wait(&mut self) -> io::Result> { - match self.0 {} + self.0 } } diff --git a/library/std/src/sys/unsupported/thread.rs b/library/std/src/sys/unsupported/thread.rs index 20ae309db30d7..cda8510e1baeb 100644 --- a/library/std/src/sys/unsupported/thread.rs +++ b/library/std/src/sys/unsupported/thread.rs @@ -1,9 +1,9 @@ -use super::{unsupported, Void}; +use super::unsupported; use crate::ffi::CStr; use crate::io; use crate::time::Duration; -pub struct Thread(Void); +pub struct Thread(!); pub const DEFAULT_MIN_STACK_SIZE: usize = 4096; @@ -26,7 +26,7 @@ impl Thread { } pub fn join(self) { - match self.0 {} + self.0 } } diff --git a/library/std/src/sys/vxworks/env.rs b/library/std/src/sys/vxworks/env.rs deleted file mode 100644 index fe1aedd58590b..0000000000000 --- a/library/std/src/sys/vxworks/env.rs +++ /dev/null @@ -1,9 +0,0 @@ -pub mod os { - pub const FAMILY: &str = "vxworks"; - pub const OS: &str = "vxworks"; - pub const DLL_PREFIX: &str = "lib"; - pub const DLL_SUFFIX: &str = ".so"; - pub const DLL_EXTENSION: &str = "so"; - pub const EXE_SUFFIX: &str = ""; - pub const EXE_EXTENSION: &str = ""; -} diff --git a/library/std/src/sys/vxworks/mod.rs b/library/std/src/sys/vxworks/mod.rs deleted file mode 100644 index c20edaa1a4778..0000000000000 --- a/library/std/src/sys/vxworks/mod.rs +++ /dev/null @@ -1,137 +0,0 @@ -#![allow(dead_code)] -#![allow(missing_docs, nonstandard_style)] - -use crate::io::ErrorKind; - -pub use self::rand::hashmap_random_keys; -pub use crate::os::vxworks as platform; -pub use libc::strlen; - -#[macro_use] -#[path = "../unix/weak.rs"] -pub mod weak; - -#[path = "../unix/alloc.rs"] -pub mod alloc; -#[path = "../unix/args.rs"] -pub mod args; -#[path = "../unix/cmath.rs"] -pub mod cmath; -#[path = "../unix/condvar.rs"] -pub mod condvar; -pub mod env; -#[path = "../unix/ext/mod.rs"] -pub mod ext; -#[path = "../unix/fd.rs"] -pub mod fd; -#[path = "../unix/fs.rs"] -pub mod fs; -#[path = "../unix/io.rs"] -pub mod io; -#[path = "../unix/memchr.rs"] -pub mod memchr; -#[path = "../unix/mutex.rs"] -pub mod mutex; -#[path = "../unix/net.rs"] -pub mod net; -#[path = "../unix/os.rs"] -pub mod os; -#[path = "../unix/path.rs"] -pub mod path; -#[path = "../unix/pipe.rs"] -pub mod pipe; -pub mod process; -pub mod rand; -#[path = "../unix/rwlock.rs"] -pub mod rwlock; -#[path = "../unix/stack_overflow.rs"] -pub mod stack_overflow; -#[path = "../unix/stdio.rs"] -pub mod stdio; -#[path = "../unix/thread.rs"] -pub mod thread; -pub mod thread_local_dtor; -#[path = "../unix/thread_local_key.rs"] -pub mod thread_local_key; -#[path = "../unix/time.rs"] -pub mod time; - -pub use crate::sys_common::os_str_bytes as os_str; - -#[cfg(not(test))] -pub fn init() { - // ignore SIGPIPE - unsafe { - assert!(signal(libc::SIGPIPE, libc::SIG_IGN) != libc::SIG_ERR); - } -} - -pub use libc::signal; - -pub fn decode_error_kind(errno: i32) -> ErrorKind { - match errno as libc::c_int { - libc::ECONNREFUSED => ErrorKind::ConnectionRefused, - libc::ECONNRESET => ErrorKind::ConnectionReset, - libc::EPERM | libc::EACCES => ErrorKind::PermissionDenied, - libc::EPIPE => ErrorKind::BrokenPipe, - libc::ENOTCONN => ErrorKind::NotConnected, - libc::ECONNABORTED => ErrorKind::ConnectionAborted, - libc::EADDRNOTAVAIL => ErrorKind::AddrNotAvailable, - libc::EADDRINUSE => ErrorKind::AddrInUse, - libc::ENOENT => ErrorKind::NotFound, - libc::EINTR => ErrorKind::Interrupted, - libc::EINVAL => ErrorKind::InvalidInput, - libc::ETIMEDOUT => ErrorKind::TimedOut, - libc::EEXIST => ErrorKind::AlreadyExists, - - // These two constants can have the same value on some systems, - // but different values on others, so we can't use a match - // clause - x if x == libc::EAGAIN || x == libc::EWOULDBLOCK => ErrorKind::WouldBlock, - - _ => ErrorKind::Other, - } -} - -#[doc(hidden)] -pub trait IsMinusOne { - fn is_minus_one(&self) -> bool; -} - -macro_rules! impl_is_minus_one { - ($($t:ident)*) => ($(impl IsMinusOne for $t { - fn is_minus_one(&self) -> bool { - *self == -1 - } - })*) -} - -impl_is_minus_one! { i8 i16 i32 i64 isize } - -pub fn cvt(t: T) -> crate::io::Result { - if t.is_minus_one() { Err(crate::io::Error::last_os_error()) } else { Ok(t) } -} - -pub fn cvt_r(mut f: F) -> crate::io::Result -where - T: IsMinusOne, - F: FnMut() -> T, -{ - loop { - match cvt(f()) { - Err(ref e) if e.kind() == ErrorKind::Interrupted => {} - other => return other, - } - } -} - -// On Unix-like platforms, libc::abort will unregister signal handlers -// including the SIGABRT handler, preventing the abort from being blocked, and -// fclose streams, with the side effect of flushing them so libc buffered -// output will be printed. Additionally the shell will generally print a more -// understandable error message like "Abort trap" rather than "Illegal -// instruction" that intrinsics::abort would cause, as intrinsics::abort is -// implemented as an illegal instruction. -pub fn abort_internal() -> ! { - unsafe { libc::abort() } -} diff --git a/library/std/src/sys/vxworks/process/mod.rs b/library/std/src/sys/vxworks/process/mod.rs deleted file mode 100644 index dc6130eaa24a8..0000000000000 --- a/library/std/src/sys/vxworks/process/mod.rs +++ /dev/null @@ -1,9 +0,0 @@ -pub use self::process_common::{Command, CommandArgs, ExitCode, Stdio, StdioPipes}; -pub use self::process_inner::{ExitStatus, Process}; -pub use crate::ffi::OsString as EnvKey; -pub use crate::sys_common::process::CommandEnvs; - -#[path = "../../unix/process/process_common.rs"] -mod process_common; -#[path = "process_vxworks.rs"] -mod process_inner; diff --git a/library/std/src/sys/vxworks/rand.rs b/library/std/src/sys/vxworks/rand.rs deleted file mode 100644 index 3a1ff5fd3b9c6..0000000000000 --- a/library/std/src/sys/vxworks/rand.rs +++ /dev/null @@ -1,36 +0,0 @@ -use crate::mem; -use crate::slice; - -pub fn hashmap_random_keys() -> (u64, u64) { - let mut v = (0, 0); - unsafe { - let view = slice::from_raw_parts_mut(&mut v as *mut _ as *mut u8, mem::size_of_val(&v)); - imp::fill_bytes(view); - } - return v; -} - -mod imp { - use crate::io; - use core::sync::atomic::{AtomicBool, Ordering::Relaxed}; - - pub fn fill_bytes(v: &mut [u8]) { - static RNG_INIT: AtomicBool = AtomicBool::new(false); - while !RNG_INIT.load(Relaxed) { - let ret = unsafe { libc::randSecure() }; - if ret < 0 { - panic!("couldn't generate random bytes: {}", io::Error::last_os_error()); - } else if ret > 0 { - RNG_INIT.store(true, Relaxed); - break; - } - unsafe { libc::usleep(10) }; - } - let ret = unsafe { - libc::randABytes(v.as_mut_ptr() as *mut libc::c_uchar, v.len() as libc::c_int) - }; - if ret < 0 { - panic!("couldn't generate random bytes: {}", io::Error::last_os_error()); - } - } -} diff --git a/library/std/src/sys/vxworks/thread_local_dtor.rs b/library/std/src/sys/vxworks/thread_local_dtor.rs deleted file mode 100644 index 5391ed83ebc36..0000000000000 --- a/library/std/src/sys/vxworks/thread_local_dtor.rs +++ /dev/null @@ -1,7 +0,0 @@ -#![cfg(target_thread_local)] -#![unstable(feature = "thread_local_internals", issue = "none")] - -pub unsafe fn register_dtor(t: *mut u8, dtor: unsafe extern "C" fn(*mut u8)) { - use crate::sys_common::thread_local_dtor::register_dtor_fallback; - register_dtor_fallback(t, dtor); -} diff --git a/library/std/src/sys/wasi/args.rs b/library/std/src/sys/wasi/args.rs index 9a27218e1fb70..86405dede4277 100644 --- a/library/std/src/sys/wasi/args.rs +++ b/library/std/src/sys/wasi/args.rs @@ -1,6 +1,7 @@ #![deny(unsafe_op_in_unsafe_fn)] use crate::ffi::{CStr, OsStr, OsString}; +use crate::fmt; use crate::marker::PhantomData; use crate::os::wasi::ffi::OsStrExt; use crate::vec; @@ -38,9 +39,9 @@ fn maybe_args() -> Option> { } } -impl Args { - pub fn inner_debug(&self) -> &[OsString] { - self.iter.as_slice() +impl fmt::Debug for Args { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.iter.as_slice().fmt(f) } } diff --git a/library/std/src/sys/wasi/ext/fs.rs b/library/std/src/sys/wasi/ext/fs.rs index 6472642f03499..ba4057bd34ca2 100644 --- a/library/std/src/sys/wasi/ext/fs.rs +++ b/library/std/src/sys/wasi/ext/fs.rs @@ -85,7 +85,7 @@ pub trait FileExt { } } if !buf.is_empty() { - Err(io::Error::new(io::ErrorKind::UnexpectedEof, "failed to fill whole buffer")) + Err(io::Error::new_const(io::ErrorKind::UnexpectedEof, &"failed to fill whole buffer")) } else { Ok(()) } @@ -151,9 +151,9 @@ pub trait FileExt { while !buf.is_empty() { match self.write_at(buf, offset) { Ok(0) => { - return Err(io::Error::new( + return Err(io::Error::new_const( io::ErrorKind::WriteZero, - "failed to write whole buffer", + &"failed to write whole buffer", )); } Ok(n) => { @@ -532,5 +532,5 @@ pub fn symlink_path, U: AsRef>(old_path: P, new_path: U) -> } fn osstr2str(f: &OsStr) -> io::Result<&str> { - f.to_str().ok_or_else(|| io::Error::new(io::ErrorKind::Other, "input must be utf-8")) + f.to_str().ok_or_else(|| io::Error::new_const(io::ErrorKind::Other, &"input must be utf-8")) } diff --git a/library/std/src/sys/wasi/fs.rs b/library/std/src/sys/wasi/fs.rs index bcf7da46b4b07..ed0f03e4b710b 100644 --- a/library/std/src/sys/wasi/fs.rs +++ b/library/std/src/sys/wasi/fs.rs @@ -130,7 +130,7 @@ impl FileType { impl fmt::Debug for ReadDir { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("ReadDir").finish() + f.debug_struct("ReadDir").finish_non_exhaustive() } } @@ -650,13 +650,11 @@ fn open_parent(p: &Path) -> io::Result<(ManuallyDrop, PathBuf)> { ); return Err(io::Error::new(io::ErrorKind::Other, msg)); } - let len = CStr::from_ptr(buf.as_ptr().cast()).to_bytes().len(); - buf.set_len(len); - buf.shrink_to_fit(); + let relative = CStr::from_ptr(relative_path).to_bytes().to_vec(); return Ok(( ManuallyDrop::new(WasiFd::from_raw(fd as u32)), - PathBuf::from(OsString::from_vec(buf)), + PathBuf::from(OsString::from_vec(relative)), )); } } @@ -672,7 +670,7 @@ fn open_parent(p: &Path) -> io::Result<(ManuallyDrop, PathBuf)> { } pub fn osstr2str(f: &OsStr) -> io::Result<&str> { - f.to_str().ok_or_else(|| io::Error::new(io::ErrorKind::Other, "input must be utf-8")) + f.to_str().ok_or_else(|| io::Error::new_const(io::ErrorKind::Other, &"input must be utf-8")) } pub fn copy(from: &Path, to: &Path) -> io::Result { diff --git a/library/std/src/sys/wasi/mod.rs b/library/std/src/sys/wasi/mod.rs index a0a37ef8316a8..b7b640b174fa9 100644 --- a/library/std/src/sys/wasi/mod.rs +++ b/library/std/src/sys/wasi/mod.rs @@ -78,6 +78,7 @@ pub fn decode_error_kind(errno: i32) -> std_io::ErrorKind { wasi::ERRNO_TIMEDOUT => TimedOut, wasi::ERRNO_EXIST => AlreadyExists, wasi::ERRNO_AGAIN => WouldBlock, + wasi::ERRNO_NOSYS => Unsupported, _ => Other, } } diff --git a/library/std/src/sys/wasi/net.rs b/library/std/src/sys/wasi/net.rs index 3f294e7df418e..06860673d90e0 100644 --- a/library/std/src/sys/wasi/net.rs +++ b/library/std/src/sys/wasi/net.rs @@ -5,7 +5,7 @@ use crate::convert::TryFrom; use crate::fmt; use crate::io::{self, IoSlice, IoSliceMut}; use crate::net::{Ipv4Addr, Ipv6Addr, Shutdown, SocketAddr}; -use crate::sys::{unsupported, Void}; +use crate::sys::unsupported; use crate::sys_common::FromInner; use crate::time::Duration; @@ -343,18 +343,18 @@ impl fmt::Debug for UdpSocket { } } -pub struct LookupHost(Void); +pub struct LookupHost(!); impl LookupHost { pub fn port(&self) -> u16 { - match self.0 {} + self.0 } } impl Iterator for LookupHost { type Item = SocketAddr; fn next(&mut self) -> Option { - match self.0 {} + self.0 } } diff --git a/library/std/src/sys/wasi/os.rs b/library/std/src/sys/wasi/os.rs index 185d6109cb93e..cf17ac0ba5f2b 100644 --- a/library/std/src/sys/wasi/os.rs +++ b/library/std/src/sys/wasi/os.rs @@ -10,7 +10,7 @@ use crate::os::wasi::prelude::*; use crate::path::{self, PathBuf}; use crate::str; use crate::sys::memchr; -use crate::sys::{unsupported, Void}; +use crate::sys::unsupported; use crate::vec; // Add a few symbols not in upstream `libc` just yet. @@ -87,7 +87,7 @@ pub fn chdir(p: &path::Path) -> io::Result<()> { } } -pub struct SplitPaths<'a>(&'a Void); +pub struct SplitPaths<'a>(!, PhantomData<&'a ()>); pub fn split_paths(_unparsed: &OsStr) -> SplitPaths<'_> { panic!("unsupported") @@ -96,7 +96,7 @@ pub fn split_paths(_unparsed: &OsStr) -> SplitPaths<'_> { impl<'a> Iterator for SplitPaths<'a> { type Item = PathBuf; fn next(&mut self) -> Option { - match *self.0 {} + self.0 } } diff --git a/library/std/src/sys/wasi/thread.rs b/library/std/src/sys/wasi/thread.rs index 8eaa5f09cb656..74515553a8218 100644 --- a/library/std/src/sys/wasi/thread.rs +++ b/library/std/src/sys/wasi/thread.rs @@ -3,10 +3,10 @@ use crate::ffi::CStr; use crate::io; use crate::mem; -use crate::sys::{unsupported, Void}; +use crate::sys::unsupported; use crate::time::Duration; -pub struct Thread(Void); +pub struct Thread(!); pub const DEFAULT_MIN_STACK_SIZE: usize = 4096; @@ -59,7 +59,7 @@ impl Thread { } pub fn join(self) { - match self.0 {} + self.0 } } diff --git a/library/std/src/sys/wasm/args.rs b/library/std/src/sys/wasm/args.rs index 3b6557ae3257f..99d300b53b3b9 100644 --- a/library/std/src/sys/wasm/args.rs +++ b/library/std/src/sys/wasm/args.rs @@ -1,4 +1,5 @@ use crate::ffi::OsString; +use crate::fmt; use crate::marker::PhantomData; use crate::vec; @@ -17,9 +18,9 @@ pub struct Args { _dont_send_or_sync_me: PhantomData<*mut ()>, } -impl Args { - pub fn inner_debug(&self) -> &[OsString] { - self.iter.as_slice() +impl fmt::Debug for Args { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.iter.as_slice().fmt(f) } } diff --git a/library/std/src/sys/wasm/thread.rs b/library/std/src/sys/wasm/thread.rs index 5eafb77da1dcd..b7bf95c89b482 100644 --- a/library/std/src/sys/wasm/thread.rs +++ b/library/std/src/sys/wasm/thread.rs @@ -1,9 +1,9 @@ use crate::ffi::CStr; use crate::io; -use crate::sys::{unsupported, Void}; +use crate::sys::unsupported; use crate::time::Duration; -pub struct Thread(Void); +pub struct Thread(!); pub const DEFAULT_MIN_STACK_SIZE: usize = 4096; @@ -47,7 +47,7 @@ impl Thread { } pub fn join(self) { - match self.0 {} + self.0 } } diff --git a/library/std/src/sys/windows/alloc.rs b/library/std/src/sys/windows/alloc.rs index 99b4d6c72a0e3..2fe71f9f28d5c 100644 --- a/library/std/src/sys/windows/alloc.rs +++ b/library/std/src/sys/windows/alloc.rs @@ -1,61 +1,246 @@ +#![deny(unsafe_op_in_unsafe_fn)] + use crate::alloc::{GlobalAlloc, Layout, System}; +use crate::ffi::c_void; +use crate::ptr; +use crate::sync::atomic::{AtomicPtr, Ordering}; use crate::sys::c; -use crate::sys_common::alloc::{realloc_fallback, MIN_ALIGN}; +use crate::sys::common::alloc::{realloc_fallback, MIN_ALIGN}; -#[repr(C)] -struct Header(*mut u8); +#[cfg(test)] +mod tests; + +// Heap memory management on Windows is done by using the system Heap API (heapapi.h) +// See https://docs.microsoft.com/windows/win32/api/heapapi/ + +// Flag to indicate that the memory returned by `HeapAlloc` should be zeroed. +const HEAP_ZERO_MEMORY: c::DWORD = 0x00000008; -unsafe fn get_header<'a>(ptr: *mut u8) -> &'a mut Header { - &mut *(ptr as *mut Header).offset(-1) +extern "system" { + // Get a handle to the default heap of the current process, or null if the operation fails. + // + // SAFETY: Successful calls to this function within the same process are assumed to + // always return the same handle, which remains valid for the entire lifetime of the process. + // + // See https://docs.microsoft.com/windows/win32/api/heapapi/nf-heapapi-getprocessheap + fn GetProcessHeap() -> c::HANDLE; + + // Allocate a block of `dwBytes` bytes of memory from a given heap `hHeap`. + // The allocated memory may be uninitialized, or zeroed if `dwFlags` is + // set to `HEAP_ZERO_MEMORY`. + // + // Returns a pointer to the newly-allocated memory or null if the operation fails. + // The returned pointer will be aligned to at least `MIN_ALIGN`. + // + // SAFETY: + // - `hHeap` must be a non-null handle returned by `GetProcessHeap`. + // - `dwFlags` must be set to either zero or `HEAP_ZERO_MEMORY`. + // + // Note that `dwBytes` is allowed to be zero, contrary to some other allocators. + // + // See https://docs.microsoft.com/windows/win32/api/heapapi/nf-heapapi-heapalloc + fn HeapAlloc(hHeap: c::HANDLE, dwFlags: c::DWORD, dwBytes: c::SIZE_T) -> c::LPVOID; + + // Reallocate a block of memory behind a given pointer `lpMem` from a given heap `hHeap`, + // to a block of at least `dwBytes` bytes, either shrinking the block in place, + // or allocating at a new location, copying memory, and freeing the original location. + // + // Returns a pointer to the reallocated memory or null if the operation fails. + // The returned pointer will be aligned to at least `MIN_ALIGN`. + // If the operation fails the given block will never have been freed. + // + // SAFETY: + // - `hHeap` must be a non-null handle returned by `GetProcessHeap`. + // - `dwFlags` must be set to zero. + // - `lpMem` must be a non-null pointer to an allocated block returned by `HeapAlloc` or + // `HeapReAlloc`, that has not already been freed. + // If the block was successfully reallocated at a new location, pointers pointing to + // the freed memory, such as `lpMem`, must not be dereferenced ever again. + // + // Note that `dwBytes` is allowed to be zero, contrary to some other allocators. + // + // See https://docs.microsoft.com/windows/win32/api/heapapi/nf-heapapi-heaprealloc + fn HeapReAlloc( + hHeap: c::HANDLE, + dwFlags: c::DWORD, + lpMem: c::LPVOID, + dwBytes: c::SIZE_T, + ) -> c::LPVOID; + + // Free a block of memory behind a given pointer `lpMem` from a given heap `hHeap`. + // Returns a nonzero value if the operation is successful, and zero if the operation fails. + // + // SAFETY: + // - `hHeap` must be a non-null handle returned by `GetProcessHeap`. + // - `dwFlags` must be set to zero. + // - `lpMem` must be a pointer to an allocated block returned by `HeapAlloc` or `HeapReAlloc`, + // that has not already been freed. + // If the block was successfully freed, pointers pointing to the freed memory, such as `lpMem`, + // must not be dereferenced ever again. + // + // Note that `lpMem` is allowed to be null, which will not cause the operation to fail. + // + // See https://docs.microsoft.com/windows/win32/api/heapapi/nf-heapapi-heapfree + fn HeapFree(hHeap: c::HANDLE, dwFlags: c::DWORD, lpMem: c::LPVOID) -> c::BOOL; } -unsafe fn align_ptr(ptr: *mut u8, align: usize) -> *mut u8 { - let aligned = ptr.add(align - (ptr as usize & (align - 1))); - *get_header(aligned) = Header(ptr); - aligned +// Cached handle to the default heap of the current process. +// Either a non-null handle returned by `GetProcessHeap`, or null when not yet initialized or `GetProcessHeap` failed. +static HEAP: AtomicPtr = AtomicPtr::new(ptr::null_mut()); + +// Get a handle to the default heap of the current process, or null if the operation fails. +// If this operation is successful, `HEAP` will be successfully initialized and contain +// a non-null handle returned by `GetProcessHeap`. +#[inline] +fn init_or_get_process_heap() -> c::HANDLE { + let heap = HEAP.load(Ordering::Relaxed); + if heap.is_null() { + // `HEAP` has not yet been successfully initialized + let heap = unsafe { GetProcessHeap() }; + if !heap.is_null() { + // SAFETY: No locking is needed because within the same process, + // successful calls to `GetProcessHeap` will always return the same value, even on different threads. + HEAP.store(heap, Ordering::Release); + + // SAFETY: `HEAP` contains a non-null handle returned by `GetProcessHeap` + heap + } else { + // Could not get the current process heap. + ptr::null_mut() + } + } else { + // SAFETY: `HEAP` contains a non-null handle returned by `GetProcessHeap` + heap + } } +// Get a non-null handle to the default heap of the current process. +// SAFETY: `HEAP` must have been successfully initialized. #[inline] -unsafe fn allocate_with_flags(layout: Layout, flags: c::DWORD) -> *mut u8 { - if layout.align() <= MIN_ALIGN { - return c::HeapAlloc(c::GetProcessHeap(), flags, layout.size()) as *mut u8; +unsafe fn get_process_heap() -> c::HANDLE { + HEAP.load(Ordering::Acquire) +} + +// Header containing a pointer to the start of an allocated block. +// SAFETY: Size and alignment must be <= `MIN_ALIGN`. +#[repr(C)] +struct Header(*mut u8); + +// Allocate a block of optionally zeroed memory for a given `layout`. +// SAFETY: Returns a pointer satisfying the guarantees of `System` about allocated pointers, +// or null if the operation fails. If this returns non-null `HEAP` will have been successfully +// initialized. +#[inline] +unsafe fn allocate(layout: Layout, zeroed: bool) -> *mut u8 { + let heap = init_or_get_process_heap(); + if heap.is_null() { + // Allocation has failed, could not get the current process heap. + return ptr::null_mut(); } - let size = layout.size() + layout.align(); - let ptr = c::HeapAlloc(c::GetProcessHeap(), flags, size); - if ptr.is_null() { ptr as *mut u8 } else { align_ptr(ptr as *mut u8, layout.align()) } + // Allocated memory will be either zeroed or uninitialized. + let flags = if zeroed { HEAP_ZERO_MEMORY } else { 0 }; + + if layout.align() <= MIN_ALIGN { + // SAFETY: `heap` is a non-null handle returned by `GetProcessHeap`. + // The returned pointer points to the start of an allocated block. + unsafe { HeapAlloc(heap, flags, layout.size()) as *mut u8 } + } else { + // Allocate extra padding in order to be able to satisfy the alignment. + let total = layout.align() + layout.size(); + + // SAFETY: `heap` is a non-null handle returned by `GetProcessHeap`. + let ptr = unsafe { HeapAlloc(heap, flags, total) as *mut u8 }; + if ptr.is_null() { + // Allocation has failed. + return ptr::null_mut(); + } + + // Create a correctly aligned pointer offset from the start of the allocated block, + // and write a header before it. + + let offset = layout.align() - (ptr as usize & (layout.align() - 1)); + // SAFETY: `MIN_ALIGN` <= `offset` <= `layout.align()` and the size of the allocated + // block is `layout.align() + layout.size()`. `aligned` will thus be a correctly aligned + // pointer inside the allocated block with at least `layout.size()` bytes after it and at + // least `MIN_ALIGN` bytes of padding before it. + let aligned = unsafe { ptr.add(offset) }; + // SAFETY: Because the size and alignment of a header is <= `MIN_ALIGN` and `aligned` + // is aligned to at least `MIN_ALIGN` and has at least `MIN_ALIGN` bytes of padding before + // it, it is safe to write a header directly before it. + unsafe { ptr::write((aligned as *mut Header).offset(-1), Header(ptr)) }; + + // SAFETY: The returned pointer does not point to the to the start of an allocated block, + // but there is a header readable directly before it containing the location of the start + // of the block. + aligned + } } +// All pointers returned by this allocator have, in addition to the guarantees of `GlobalAlloc`, the +// following properties: +// +// If the pointer was allocated or reallocated with a `layout` specifying an alignment <= `MIN_ALIGN` +// the pointer will be aligned to at least `MIN_ALIGN` and point to the start of the allocated block. +// +// If the pointer was allocated or reallocated with a `layout` specifying an alignment > `MIN_ALIGN` +// the pointer will be aligned to the specified alignment and not point to the start of the allocated block. +// Instead there will be a header readable directly before the returned pointer, containing the actual +// location of the start of the block. #[stable(feature = "alloc_system_type", since = "1.28.0")] unsafe impl GlobalAlloc for System { #[inline] unsafe fn alloc(&self, layout: Layout) -> *mut u8 { - allocate_with_flags(layout, 0) + // SAFETY: Pointers returned by `allocate` satisfy the guarantees of `System` + let zeroed = false; + unsafe { allocate(layout, zeroed) } } #[inline] unsafe fn alloc_zeroed(&self, layout: Layout) -> *mut u8 { - allocate_with_flags(layout, c::HEAP_ZERO_MEMORY) + // SAFETY: Pointers returned by `allocate` satisfy the guarantees of `System` + let zeroed = true; + unsafe { allocate(layout, zeroed) } } #[inline] unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) { - if layout.align() <= MIN_ALIGN { - let err = c::HeapFree(c::GetProcessHeap(), 0, ptr as c::LPVOID); - debug_assert!(err != 0, "Failed to free heap memory: {}", c::GetLastError()); - } else { - let header = get_header(ptr); - let err = c::HeapFree(c::GetProcessHeap(), 0, header.0 as c::LPVOID); - debug_assert!(err != 0, "Failed to free heap memory: {}", c::GetLastError()); - } + let block = { + if layout.align() <= MIN_ALIGN { + ptr + } else { + // The location of the start of the block is stored in the padding before `ptr`. + + // SAFETY: Because of the contract of `System`, `ptr` is guaranteed to be non-null + // and have a header readable directly before it. + unsafe { ptr::read((ptr as *mut Header).offset(-1)).0 } + } + }; + + // SAFETY: because `ptr` has been successfully allocated with this allocator, + // `HEAP` must have been successfully initialized. + let heap = unsafe { get_process_heap() }; + + // SAFETY: `heap` is a non-null handle returned by `GetProcessHeap`, + // `block` is a pointer to the start of an allocated block. + unsafe { HeapFree(heap, 0, block as c::LPVOID) }; } #[inline] unsafe fn realloc(&self, ptr: *mut u8, layout: Layout, new_size: usize) -> *mut u8 { if layout.align() <= MIN_ALIGN { - c::HeapReAlloc(c::GetProcessHeap(), 0, ptr as c::LPVOID, new_size) as *mut u8 + // SAFETY: because `ptr` has been successfully allocated with this allocator, + // `HEAP` must have been successfully initialized. + let heap = unsafe { get_process_heap() }; + + // SAFETY: `heap` is a non-null handle returned by `GetProcessHeap`, + // `ptr` is a pointer to the start of an allocated block. + // The returned pointer points to the start of an allocated block. + unsafe { HeapReAlloc(heap, 0, ptr as c::LPVOID, new_size) as *mut u8 } } else { - realloc_fallback(self, ptr, layout, new_size) + // SAFETY: `realloc_fallback` is implemented using `dealloc` and `alloc`, which will + // correctly handle `ptr` and return a pointer satisfying the guarantees of `System` + unsafe { realloc_fallback(self, ptr, layout, new_size) } } } } diff --git a/library/std/src/sys/windows/alloc/tests.rs b/library/std/src/sys/windows/alloc/tests.rs new file mode 100644 index 0000000000000..674a3e1d92d17 --- /dev/null +++ b/library/std/src/sys/windows/alloc/tests.rs @@ -0,0 +1,9 @@ +use super::{Header, MIN_ALIGN}; +use crate::mem; + +#[test] +fn alloc_header() { + // Header must fit in the padding before an aligned pointer + assert!(mem::size_of::

() <= MIN_ALIGN); + assert!(mem::align_of::
() <= MIN_ALIGN); +} diff --git a/library/std/src/sys/windows/args.rs b/library/std/src/sys/windows/args.rs index bcc2ea9ae00f0..31197e4accc6d 100644 --- a/library/std/src/sys/windows/args.rs +++ b/library/std/src/sys/windows/args.rs @@ -164,19 +164,9 @@ pub struct Args { parsed_args_list: vec::IntoIter, } -pub struct ArgsInnerDebug<'a> { - args: &'a Args, -} - -impl<'a> fmt::Debug for ArgsInnerDebug<'a> { +impl fmt::Debug for Args { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - self.args.parsed_args_list.as_slice().fmt(f) - } -} - -impl Args { - pub fn inner_debug(&self) -> ArgsInnerDebug<'_> { - ArgsInnerDebug { args: self } + self.parsed_args_list.as_slice().fmt(f) } } diff --git a/library/std/src/sys/windows/c.rs b/library/std/src/sys/windows/c.rs index 9789ed085e29d..3e4176ef7f8fe 100644 --- a/library/std/src/sys/windows/c.rs +++ b/library/std/src/sys/windows/c.rs @@ -285,8 +285,6 @@ pub const FD_SETSIZE: usize = 64; pub const STACK_SIZE_PARAM_IS_A_RESERVATION: DWORD = 0x00010000; -pub const HEAP_ZERO_MEMORY: DWORD = 0x00000008; - pub const STATUS_SUCCESS: NTSTATUS = 0x00000000; #[repr(C)] @@ -1017,11 +1015,6 @@ extern "system" { timeout: *const timeval, ) -> c_int; - pub fn GetProcessHeap() -> HANDLE; - pub fn HeapAlloc(hHeap: HANDLE, dwFlags: DWORD, dwBytes: SIZE_T) -> LPVOID; - pub fn HeapReAlloc(hHeap: HANDLE, dwFlags: DWORD, lpMem: LPVOID, dwBytes: SIZE_T) -> LPVOID; - pub fn HeapFree(hHeap: HANDLE, dwFlags: DWORD, lpMem: LPVOID) -> BOOL; - // >= Vista / Server 2008 // https://docs.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-createsymboliclinkw pub fn CreateSymbolicLinkW( diff --git a/library/std/src/sys/windows/fs.rs b/library/std/src/sys/windows/fs.rs index 307a47678c6a2..8e6bd76f85f06 100644 --- a/library/std/src/sys/windows/fs.rs +++ b/library/std/src/sys/windows/fs.rs @@ -513,9 +513,9 @@ impl File { ) } _ => { - return Err(io::Error::new( + return Err(io::Error::new_const( io::ErrorKind::Other, - "Unsupported reparse point type", + &"Unsupported reparse point type", )); } }; @@ -802,7 +802,10 @@ pub fn link(original: &Path, link: &Path) -> io::Result<()> { #[cfg(target_vendor = "uwp")] pub fn link(_original: &Path, _link: &Path) -> io::Result<()> { - return Err(io::Error::new(io::ErrorKind::Other, "hard link are not supported on UWP")); + return Err(io::Error::new_const( + io::ErrorKind::Unsupported, + &"hard link are not supported on UWP", + )); } pub fn stat(path: &Path) -> io::Result { diff --git a/library/std/src/sys/windows/mod.rs b/library/std/src/sys/windows/mod.rs index fcbff59dec007..973301af2d992 100644 --- a/library/std/src/sys/windows/mod.rs +++ b/library/std/src/sys/windows/mod.rs @@ -78,6 +78,7 @@ pub fn decode_error_kind(errno: i32) -> ErrorKind { | c::ERROR_IPSEC_IKE_TIMED_OUT | c::ERROR_RUNLEVEL_SWITCH_TIMEOUT | c::ERROR_RUNLEVEL_SWITCH_AGENT_TIMEOUT => return ErrorKind::TimedOut, + c::ERROR_CALL_NOT_IMPLEMENTED => return ErrorKind::Unsupported, _ => {} } @@ -130,9 +131,9 @@ pub fn to_u16s>(s: S) -> crate::io::Result> { fn inner(s: &OsStr) -> crate::io::Result> { let mut maybe_result: Vec = s.encode_wide().collect(); if unrolled_find_u16s(0, &maybe_result).is_some() { - return Err(crate::io::Error::new( + return Err(crate::io::Error::new_const( ErrorKind::InvalidInput, - "strings passed to WinAPI cannot contain NULs", + &"strings passed to WinAPI cannot contain NULs", )); } maybe_result.push(0); diff --git a/library/std/src/sys/windows/net.rs b/library/std/src/sys/windows/net.rs index 9e74454bc2335..ad04afc0b6d8a 100644 --- a/library/std/src/sys/windows/net.rs +++ b/library/std/src/sys/windows/net.rs @@ -136,9 +136,9 @@ impl Socket { } if timeout.as_secs() == 0 && timeout.subsec_nanos() == 0 { - return Err(io::Error::new( + return Err(io::Error::new_const( io::ErrorKind::InvalidInput, - "cannot set a 0 duration timeout", + &"cannot set a 0 duration timeout", )); } @@ -164,7 +164,7 @@ impl Socket { unsafe { cvt(c::select(1, ptr::null_mut(), &mut writefds, &mut errorfds, &timeout))? }; match n { - 0 => Err(io::Error::new(io::ErrorKind::TimedOut, "connection timed out")), + 0 => Err(io::Error::new_const(io::ErrorKind::TimedOut, &"connection timed out")), _ => { if writefds.fd_count != 1 { if let Some(e) = self.take_error()? { @@ -339,9 +339,9 @@ impl Socket { Some(dur) => { let timeout = sys::dur2timeout(dur); if timeout == 0 { - return Err(io::Error::new( + return Err(io::Error::new_const( io::ErrorKind::InvalidInput, - "cannot set a 0 duration timeout", + &"cannot set a 0 duration timeout", )); } timeout @@ -370,7 +370,7 @@ impl Socket { #[cfg(target_vendor = "uwp")] fn set_no_inherit(&self) -> io::Result<()> { - Err(io::Error::new(io::ErrorKind::Other, "Unavailable on UWP")) + Err(io::Error::new_const(io::ErrorKind::Unsupported, &"Unavailable on UWP")) } pub fn shutdown(&self, how: Shutdown) -> io::Result<()> { diff --git a/library/std/src/sys/windows/process.rs b/library/std/src/sys/windows/process.rs index 83d37795ee5c1..30bbfdd0dd1e2 100644 --- a/library/std/src/sys/windows/process.rs +++ b/library/std/src/sys/windows/process.rs @@ -62,7 +62,7 @@ impl AsRef for EnvKey { fn ensure_no_nuls>(str: T) -> io::Result { if str.as_ref().encode_wide().any(|b| b == 0) { - Err(io::Error::new(ErrorKind::InvalidInput, "nul byte found in provided data")) + Err(io::Error::new_const(ErrorKind::InvalidInput, &"nul byte found in provided data")) } else { Ok(str) } diff --git a/library/std/src/sys/windows/stdio.rs b/library/std/src/sys/windows/stdio.rs index ff214497166be..be3141e46a1cc 100644 --- a/library/std/src/sys/windows/stdio.rs +++ b/library/std/src/sys/windows/stdio.rs @@ -68,9 +68,9 @@ fn write(handle_id: c::DWORD, data: &[u8]) -> io::Result { let utf8 = match str::from_utf8(&data[..len]) { Ok(s) => s, Err(ref e) if e.valid_up_to() == 0 => { - return Err(io::Error::new( + return Err(io::Error::new_const( io::ErrorKind::InvalidData, - "Windows stdio in console mode does not support writing non-UTF-8 byte sequences", + &"Windows stdio in console mode does not support writing non-UTF-8 byte sequences", )); } Err(e) => str::from_utf8(&data[..e.valid_up_to()]).unwrap(), @@ -149,9 +149,9 @@ impl io::Read for Stdin { if buf.len() == 0 { return Ok(0); } else if buf.len() < 4 { - return Err(io::Error::new( + return Err(io::Error::new_const( io::ErrorKind::InvalidInput, - "Windows stdin in console mode does not support a buffer too small to \ + &"Windows stdin in console mode does not support a buffer too small to \ guarantee holding one arbitrary UTF-8 character (4 bytes)", )); } @@ -243,9 +243,9 @@ fn utf16_to_utf8(utf16: &[u16], utf8: &mut [u8]) -> io::Result { } Err(_) => { // We can't really do any better than forget all data and return an error. - return Err(io::Error::new( + return Err(io::Error::new_const( io::ErrorKind::InvalidData, - "Windows stdin in console mode does not support non-UTF-16 input; \ + &"Windows stdin in console mode does not support non-UTF-16 input; \ encountered unpaired surrogate", )); } diff --git a/library/std/src/sys_common/fs.rs b/library/std/src/sys_common/fs.rs index 6bdb26cd078ed..30908824dd663 100644 --- a/library/std/src/sys_common/fs.rs +++ b/library/std/src/sys_common/fs.rs @@ -4,15 +4,17 @@ use crate::fs; use crate::io::{self, Error, ErrorKind}; use crate::path::Path; +pub(crate) const NOT_FILE_ERROR: Error = Error::new_const( + ErrorKind::InvalidInput, + &"the source path is neither a regular file nor a symlink to a regular file", +); + pub fn copy(from: &Path, to: &Path) -> io::Result { let mut reader = fs::File::open(from)?; let metadata = reader.metadata()?; if !metadata.is_file() { - return Err(Error::new( - ErrorKind::InvalidInput, - "the source path is not an existing regular file", - )); + return Err(NOT_FILE_ERROR); } let mut writer = fs::File::create(to)?; diff --git a/library/std/src/sys_common/mod.rs b/library/std/src/sys_common/mod.rs index 660f0e0df9732..4979bc0b5af77 100644 --- a/library/std/src/sys_common/mod.rs +++ b/library/std/src/sys_common/mod.rs @@ -8,9 +8,11 @@ //! rest of `std` is complex, with dependencies going in all //! directions: `std` depending on `sys_common`, `sys_common` //! depending on `sys`, and `sys` depending on `sys_common` and `std`. -//! Ideally `sys_common` would be split into two and the dependencies -//! between them all would form a dag, facilitating the extraction of -//! `std::sys` from the standard library. +//! This is because `sys_common` not only contains platform-independent code, +//! but also code that is shared between the different platforms in `sys`. +//! Ideally all that shared code should be moved to `sys::common`, +//! and the dependencies between `std`, `sys_common` and `sys` all would form a dag. +//! Progress on this is tracked in #84187. #![allow(missing_docs)] #![allow(missing_debug_implementations)] @@ -46,7 +48,6 @@ macro_rules! rtunwrap { }; } -pub mod alloc; pub mod at_exit_imp; pub mod backtrace; pub mod bytestring; @@ -58,7 +59,6 @@ pub mod mutex; // when generating documentation. #[cfg(any(doc, not(windows)))] pub mod os_str_bytes; -pub mod poison; pub mod process; pub mod remutex; pub mod rwlock; diff --git a/library/std/src/sys_common/net.rs b/library/std/src/sys_common/net.rs index 38ba0d2fbdb6b..d5f29c4a43970 100644 --- a/library/std/src/sys_common/net.rs +++ b/library/std/src/sys_common/net.rs @@ -108,7 +108,7 @@ pub fn sockaddr_to_addr(storage: &c::sockaddr_storage, len: usize) -> io::Result *(storage as *const _ as *const c::sockaddr_in6) }))) } - _ => Err(Error::new(ErrorKind::InvalidInput, "invalid argument")), + _ => Err(Error::new_const(ErrorKind::InvalidInput, &"invalid argument")), } } @@ -171,7 +171,7 @@ impl TryFrom<&str> for LookupHost { ($e:expr, $msg:expr) => { match $e { Some(r) => r, - None => return Err(io::Error::new(io::ErrorKind::InvalidInput, $msg)), + None => return Err(io::Error::new_const(io::ErrorKind::InvalidInput, &$msg)), } }; } diff --git a/library/std/src/sys_common/rwlock.rs b/library/std/src/sys_common/rwlock.rs index 70b31b19f824c..3705d641a1be6 100644 --- a/library/std/src/sys_common/rwlock.rs +++ b/library/std/src/sys_common/rwlock.rs @@ -86,62 +86,3 @@ impl RWLock { self.0.destroy() } } - -// the cfg annotations only exist due to dead code warnings. the code itself is portable -#[cfg(unix)] -pub struct StaticRWLock(RWLock); - -#[cfg(unix)] -impl StaticRWLock { - pub const fn new() -> StaticRWLock { - StaticRWLock(RWLock::new()) - } - - /// Acquires shared access to the underlying lock, blocking the current - /// thread to do so. - /// - /// The lock is automatically unlocked when the returned guard is dropped. - #[inline] - pub fn read_with_guard(&'static self) -> RWLockReadGuard { - // SAFETY: All methods require static references, therefore self - // cannot be moved between invocations. - unsafe { - self.0.read(); - } - RWLockReadGuard(&self.0) - } - - /// Acquires write access to the underlying lock, blocking the current thread - /// to do so. - /// - /// The lock is automatically unlocked when the returned guard is dropped. - #[inline] - pub fn write_with_guard(&'static self) -> RWLockWriteGuard { - // SAFETY: All methods require static references, therefore self - // cannot be moved between invocations. - unsafe { - self.0.write(); - } - RWLockWriteGuard(&self.0) - } -} - -#[cfg(unix)] -pub struct RWLockReadGuard(&'static RWLock); - -#[cfg(unix)] -impl Drop for RWLockReadGuard { - fn drop(&mut self) { - unsafe { self.0.read_unlock() } - } -} - -#[cfg(unix)] -pub struct RWLockWriteGuard(&'static RWLock); - -#[cfg(unix)] -impl Drop for RWLockWriteGuard { - fn drop(&mut self) { - unsafe { self.0.write_unlock() } - } -} diff --git a/library/std/src/sys_common/thread_local_dtor.rs b/library/std/src/sys_common/thread_local_dtor.rs index 6f5ebf4a27158..f9971fb6f21ef 100644 --- a/library/std/src/sys_common/thread_local_dtor.rs +++ b/library/std/src/sys_common/thread_local_dtor.rs @@ -1,6 +1,6 @@ //! Thread-local destructor //! -//! Besides thread-local "keys" (pointer-sized non-adressable thread-local store +//! Besides thread-local "keys" (pointer-sized non-addressable thread-local store //! with an associated destructor), many platforms also provide thread-local //! destructors that are not associated with any particular data. These are //! often more efficient. diff --git a/library/std/src/thread/available_concurrency.rs b/library/std/src/thread/available_concurrency.rs index 64a5f89890a31..e8cdde8801459 100644 --- a/library/std/src/thread/available_concurrency.rs +++ b/library/std/src/thread/available_concurrency.rs @@ -64,7 +64,7 @@ cfg_if::cfg_if! { sysinfo.dwNumberOfProcessors as usize }; match res { - 0 => Err(io::Error::new(io::ErrorKind::NotFound, "The number of hardware threads is not known for the target platform")), + 0 => Err(io::Error::new_const(io::ErrorKind::NotFound, &"The number of hardware threads is not known for the target platform")), cpus => Ok(unsafe { NonZeroUsize::new_unchecked(cpus) }), } } @@ -81,7 +81,7 @@ cfg_if::cfg_if! { fn available_concurrency_internal() -> io::Result { match unsafe { libc::sysconf(libc::_SC_NPROCESSORS_ONLN) } { -1 => Err(io::Error::last_os_error()), - 0 => Err(io::Error::new(io::ErrorKind::NotFound, "The number of hardware threads is not known for the target platform")), + 0 => Err(io::Error::new_const(io::ErrorKind::NotFound, &"The number of hardware threads is not known for the target platform")), cpus => Ok(unsafe { NonZeroUsize::new_unchecked(cpus as usize) }), } } @@ -114,7 +114,7 @@ cfg_if::cfg_if! { if res == -1 { return Err(io::Error::last_os_error()); } else if cpus == 0 { - return Err(io::Error::new(io::ErrorKind::NotFound, "The number of hardware threads is not known for the target platform")); + return Err(io::Error::new_const(io::ErrorKind::NotFound, &"The number of hardware threads is not known for the target platform")); } } Ok(unsafe { NonZeroUsize::new_unchecked(cpus as usize) }) @@ -142,7 +142,7 @@ cfg_if::cfg_if! { if res == -1 { return Err(io::Error::last_os_error()); } else if cpus == 0 { - return Err(io::Error::new(io::ErrorKind::NotFound, "The number of hardware threads is not known for the target platform")); + return Err(io::Error::new_const(io::ErrorKind::NotFound, &"The number of hardware threads is not known for the target platform")); } Ok(unsafe { NonZeroUsize::new_unchecked(cpus as usize) }) @@ -150,7 +150,7 @@ cfg_if::cfg_if! { } else { // FIXME: implement on vxWorks, Redox, HermitCore, Haiku, l4re fn available_concurrency_internal() -> io::Result { - Err(io::Error::new(io::ErrorKind::NotFound, "The number of hardware threads is not known for the target platform")) + Err(io::Error::new_const(io::ErrorKind::NotFound, &"The number of hardware threads is not known for the target platform")) } } } diff --git a/library/std/src/thread/local.rs b/library/std/src/thread/local.rs index dd438858c37cd..abd5b7784834c 100644 --- a/library/std/src/thread/local.rs +++ b/library/std/src/thread/local.rs @@ -100,7 +100,7 @@ pub struct LocalKey { #[stable(feature = "std_debug", since = "1.16.0")] impl fmt::Debug for LocalKey { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.pad("LocalKey { .. }") + f.debug_struct("LocalKey").finish_non_exhaustive() } } @@ -133,6 +133,15 @@ macro_rules! thread_local { // empty (base case for the recursion) () => {}; + ($(#[$attr:meta])* $vis:vis static $name:ident: $t:ty = const { $init:expr }; $($rest:tt)*) => ( + $crate::__thread_local_inner!($(#[$attr])* $vis $name, $t, const $init); + $crate::thread_local!($($rest)*); + ); + + ($(#[$attr:meta])* $vis:vis static $name:ident: $t:ty = const { $init:expr }) => ( + $crate::__thread_local_inner!($(#[$attr])* $vis $name, $t, const $init); + ); + // process multiple declarations ($(#[$attr:meta])* $vis:vis static $name:ident: $t:ty = $init:expr; $($rest:tt)*) => ( $crate::__thread_local_inner!($(#[$attr])* $vis $name, $t, $init); @@ -151,6 +160,101 @@ macro_rules! thread_local { #[allow_internal_unstable(thread_local_internals, cfg_target_thread_local, thread_local)] #[allow_internal_unsafe] macro_rules! __thread_local_inner { + // used to generate the `LocalKey` value for const-initialized thread locals + (@key $t:ty, const $init:expr) => {{ + unsafe fn __getit() -> $crate::option::Option<&'static $t> { + const _REQUIRE_UNSTABLE: () = $crate::thread::require_unstable_const_init_thread_local(); + + // wasm without atomics maps directly to `static mut`, and dtors + // aren't implemented because thread dtors aren't really a thing + // on wasm right now + // + // FIXME(#84224) this should come after the `target_thread_local` + // block. + #[cfg(all(target_arch = "wasm32", not(target_feature = "atomics")))] + { + static mut VAL: $t = $init; + Some(&VAL) + } + + // If the platform has support for `#[thread_local]`, use it. + #[cfg(all( + target_thread_local, + not(all(target_arch = "wasm32", not(target_feature = "atomics"))), + ))] + { + // If a dtor isn't needed we can do something "very raw" and + // just get going. + if !$crate::mem::needs_drop::<$t>() { + #[thread_local] + static mut VAL: $t = $init; + unsafe { + return Some(&VAL) + } + } + + #[thread_local] + static mut VAL: $t = $init; + // 0 == dtor not registered + // 1 == dtor registered, dtor not run + // 2 == dtor registered and is running or has run + #[thread_local] + static mut STATE: u8 = 0; + + unsafe extern "C" fn destroy(ptr: *mut u8) { + let ptr = ptr as *mut $t; + + unsafe { + debug_assert_eq!(STATE, 1); + STATE = 2; + $crate::ptr::drop_in_place(ptr); + } + } + + unsafe { + match STATE { + // 0 == we haven't registered a destructor, so do + // so now. + 0 => { + $crate::thread::__FastLocalKeyInner::<$t>::register_dtor( + $crate::ptr::addr_of_mut!(VAL) as *mut u8, + destroy, + ); + STATE = 1; + Some(&VAL) + } + // 1 == the destructor is registered and the value + // is valid, so return the pointer. + 1 => Some(&VAL), + // otherwise the destructor has already run, so we + // can't give access. + _ => None, + } + } + } + + // On platforms without `#[thread_local]` we fall back to the + // same implementation as below for os thread locals. + #[cfg(all( + not(target_thread_local), + not(all(target_arch = "wasm32", not(target_feature = "atomics"))), + ))] + { + #[inline] + const fn __init() -> $t { $init } + static __KEY: $crate::thread::__OsLocalKeyInner<$t> = + $crate::thread::__OsLocalKeyInner::new(); + #[allow(unused_unsafe)] + unsafe { __KEY.get(__init) } + } + } + + unsafe { + $crate::thread::LocalKey::new(__getit) + } + }}; + + // used to generate the `LocalKey` value for `thread_local!` (@key $t:ty, $init:expr) => { { #[inline] @@ -188,9 +292,9 @@ macro_rules! __thread_local_inner { } } }; - ($(#[$attr:meta])* $vis:vis $name:ident, $t:ty, $init:expr) => { + ($(#[$attr:meta])* $vis:vis $name:ident, $t:ty, $($init:tt)*) => { $(#[$attr])* $vis const $name: $crate::thread::LocalKey<$t> = - $crate::__thread_local_inner!(@key $t, $init); + $crate::__thread_local_inner!(@key $t, $($init)*); } } @@ -368,7 +472,7 @@ pub mod statik { impl fmt::Debug for Key { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.pad("Key { .. }") + f.debug_struct("Key").finish_non_exhaustive() } } @@ -433,7 +537,7 @@ pub mod fast { impl fmt::Debug for Key { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.pad("Key { .. }") + f.debug_struct("Key").finish_non_exhaustive() } } @@ -442,6 +546,15 @@ pub mod fast { Key { inner: LazyKeyInner::new(), dtor_state: Cell::new(DtorState::Unregistered) } } + // note that this is just a publically-callable function only for the + // const-initialized form of thread locals, basically a way to call the + // free `register_dtor` function defined elsewhere in libstd. + pub unsafe fn register_dtor(a: *mut u8, dtor: unsafe extern "C" fn(*mut u8)) { + unsafe { + register_dtor(a, dtor); + } + } + pub unsafe fn get T>(&self, init: F) -> Option<&'static T> { // SAFETY: See the definitions of `LazyKeyInner::get` and // `try_initialize` for more informations. @@ -538,7 +651,7 @@ pub mod os { impl fmt::Debug for Key { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.pad("Key { .. }") + f.debug_struct("Key").finish_non_exhaustive() } } diff --git a/library/std/src/thread/local/tests.rs b/library/std/src/thread/local/tests.rs index 4fb0a0890826e..80e6798d847b1 100644 --- a/library/std/src/thread/local/tests.rs +++ b/library/std/src/thread/local/tests.rs @@ -1,6 +1,6 @@ use crate::cell::{Cell, UnsafeCell}; use crate::sync::mpsc::{channel, Sender}; -use crate::thread; +use crate::thread::{self, LocalKey}; use crate::thread_local; struct Foo(Sender<()>); @@ -15,74 +15,90 @@ impl Drop for Foo { #[test] fn smoke_no_dtor() { thread_local!(static FOO: Cell = Cell::new(1)); + run(&FOO); + thread_local!(static FOO2: Cell = const { Cell::new(1) }); + run(&FOO2); - FOO.with(|f| { - assert_eq!(f.get(), 1); - f.set(2); - }); - let (tx, rx) = channel(); - let _t = thread::spawn(move || { - FOO.with(|f| { + fn run(key: &'static LocalKey>) { + key.with(|f| { assert_eq!(f.get(), 1); + f.set(2); }); - tx.send(()).unwrap(); - }); - rx.recv().unwrap(); + let t = thread::spawn(move || { + key.with(|f| { + assert_eq!(f.get(), 1); + }); + }); + t.join().unwrap(); - FOO.with(|f| { - assert_eq!(f.get(), 2); - }); + key.with(|f| { + assert_eq!(f.get(), 2); + }); + } } #[test] fn states() { - struct Foo; + struct Foo(&'static LocalKey); impl Drop for Foo { fn drop(&mut self) { - assert!(FOO.try_with(|_| ()).is_err()); + assert!(self.0.try_with(|_| ()).is_err()); } } - thread_local!(static FOO: Foo = Foo); - thread::spawn(|| { - assert!(FOO.try_with(|_| ()).is_ok()); - }) - .join() - .ok() - .expect("thread panicked"); + thread_local!(static FOO: Foo = Foo(&FOO)); + run(&FOO); + thread_local!(static FOO2: Foo = const { Foo(&FOO2) }); + run(&FOO2); + + fn run(foo: &'static LocalKey) { + thread::spawn(move || { + assert!(foo.try_with(|_| ()).is_ok()); + }) + .join() + .unwrap(); + } } #[test] fn smoke_dtor() { thread_local!(static FOO: UnsafeCell> = UnsafeCell::new(None)); - - let (tx, rx) = channel(); - let _t = thread::spawn(move || unsafe { - let mut tx = Some(tx); - FOO.with(|f| { - *f.get() = Some(Foo(tx.take().unwrap())); + run(&FOO); + thread_local!(static FOO2: UnsafeCell> = const { UnsafeCell::new(None) }); + run(&FOO2); + + fn run(key: &'static LocalKey>>) { + let (tx, rx) = channel(); + let t = thread::spawn(move || unsafe { + let mut tx = Some(tx); + key.with(|f| { + *f.get() = Some(Foo(tx.take().unwrap())); + }); }); - }); - rx.recv().unwrap(); + rx.recv().unwrap(); + t.join().unwrap(); + } } #[test] fn circular() { - struct S1; - struct S2; + struct S1(&'static LocalKey>>, &'static LocalKey>>); + struct S2(&'static LocalKey>>, &'static LocalKey>>); thread_local!(static K1: UnsafeCell> = UnsafeCell::new(None)); thread_local!(static K2: UnsafeCell> = UnsafeCell::new(None)); - static mut HITS: u32 = 0; + thread_local!(static K3: UnsafeCell> = const { UnsafeCell::new(None) }); + thread_local!(static K4: UnsafeCell> = const { UnsafeCell::new(None) }); + static mut HITS: usize = 0; impl Drop for S1 { fn drop(&mut self) { unsafe { HITS += 1; - if K2.try_with(|_| ()).is_err() { + if self.1.try_with(|_| ()).is_err() { assert_eq!(HITS, 3); } else { if HITS == 1 { - K2.with(|s| *s.get() = Some(S2)); + self.1.with(|s| *s.get() = Some(S2(self.0, self.1))); } else { assert_eq!(HITS, 3); } @@ -94,38 +110,54 @@ fn circular() { fn drop(&mut self) { unsafe { HITS += 1; - assert!(K1.try_with(|_| ()).is_ok()); + assert!(self.0.try_with(|_| ()).is_ok()); assert_eq!(HITS, 2); - K1.with(|s| *s.get() = Some(S1)); + self.0.with(|s| *s.get() = Some(S1(self.0, self.1))); } } } thread::spawn(move || { - drop(S1); + drop(S1(&K1, &K2)); + }) + .join() + .unwrap(); + + unsafe { + HITS = 0; + } + + thread::spawn(move || { + drop(S1(&K3, &K4)); }) .join() - .ok() - .expect("thread panicked"); + .unwrap(); } #[test] fn self_referential() { - struct S1; + struct S1(&'static LocalKey>>); + thread_local!(static K1: UnsafeCell> = UnsafeCell::new(None)); + thread_local!(static K2: UnsafeCell> = const { UnsafeCell::new(None) }); impl Drop for S1 { fn drop(&mut self) { - assert!(K1.try_with(|_| ()).is_err()); + assert!(self.0.try_with(|_| ()).is_err()); } } thread::spawn(move || unsafe { - K1.with(|s| *s.get() = Some(S1)); + K1.with(|s| *s.get() = Some(S1(&K1))); }) .join() - .ok() - .expect("thread panicked"); + .unwrap(); + + thread::spawn(move || unsafe { + K2.with(|s| *s.get() = Some(S1(&K2))); + }) + .join() + .unwrap(); } // Note that this test will deadlock if TLS destructors aren't run (this @@ -152,3 +184,26 @@ fn dtors_in_dtors_in_dtors() { }); rx.recv().unwrap(); } + +#[test] +fn dtors_in_dtors_in_dtors_const_init() { + struct S1(Sender<()>); + thread_local!(static K1: UnsafeCell> = const { UnsafeCell::new(None) }); + thread_local!(static K2: UnsafeCell> = const { UnsafeCell::new(None) }); + + impl Drop for S1 { + fn drop(&mut self) { + let S1(ref tx) = *self; + unsafe { + let _ = K2.try_with(|s| *s.get() = Some(Foo(tx.clone()))); + } + } + } + + let (tx, rx) = channel(); + let _t = thread::spawn(move || unsafe { + let mut tx = Some(tx); + K1.with(|s| *s.get() = Some(S1(tx.take().unwrap()))); + }); + rx.recv().unwrap(); +} diff --git a/library/std/src/thread/mod.rs b/library/std/src/thread/mod.rs index 0ef848ff0c4c1..30d8c2a1b6fa0 100644 --- a/library/std/src/thread/mod.rs +++ b/library/std/src/thread/mod.rs @@ -204,6 +204,13 @@ pub use self::local::os::Key as __OsLocalKeyInner; #[doc(hidden)] pub use self::local::statik::Key as __StaticLocalKeyInner; +// This is only used to make thread locals with `const { .. }` initialization +// expressions unstable. If and/or when that syntax is stabilized with thread +// locals this will simply be removed. +#[doc(hidden)] +#[unstable(feature = "thread_local_const_init", issue = "84223")] +pub const fn require_unstable_const_init_thread_local() {} + //////////////////////////////////////////////////////////////////////////////// // Builder //////////////////////////////////////////////////////////////////////////////// @@ -1176,7 +1183,10 @@ impl Thread { #[stable(feature = "rust1", since = "1.0.0")] impl fmt::Debug for Thread { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("Thread").field("id", &self.id()).field("name", &self.name()).finish() + f.debug_struct("Thread") + .field("id", &self.id()) + .field("name", &self.name()) + .finish_non_exhaustive() } } @@ -1403,7 +1413,7 @@ impl IntoInner for JoinHandle { #[stable(feature = "std_debug", since = "1.16.0")] impl fmt::Debug for JoinHandle { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.pad("JoinHandle { .. }") + f.debug_struct("JoinHandle").finish_non_exhaustive() } } diff --git a/library/stdarch b/library/stdarch index 9c732a56f67f5..19f5459dd0f89 160000 --- a/library/stdarch +++ b/library/stdarch @@ -1 +1 @@ -Subproject commit 9c732a56f67f54d12a0b4fd99993154906c95ea6 +Subproject commit 19f5459dd0f89e466b7bcaa0f69ecca90f21a4d1 diff --git a/library/test/src/bench.rs b/library/test/src/bench.rs index d4b37284ea774..169154187f250 100644 --- a/library/test/src/bench.rs +++ b/library/test/src/bench.rs @@ -2,7 +2,11 @@ pub use std::hint::black_box; use super::{ - event::CompletedTest, options::BenchMode, test_result::TestResult, types::TestDesc, Sender, + event::CompletedTest, + options::BenchMode, + test_result::TestResult, + types::{TestDesc, TestId}, + Sender, }; use crate::stats; @@ -177,8 +181,13 @@ where } } -pub fn benchmark(desc: TestDesc, monitor_ch: Sender, nocapture: bool, f: F) -where +pub fn benchmark( + id: TestId, + desc: TestDesc, + monitor_ch: Sender, + nocapture: bool, + f: F, +) where F: FnMut(&mut Bencher), { let mut bs = Bencher { mode: BenchMode::Auto, summary: None, bytes: 0 }; @@ -213,7 +222,7 @@ where }; let stdout = data.lock().unwrap().to_vec(); - let message = CompletedTest::new(desc, test_result, None, stdout); + let message = CompletedTest::new(id, desc, test_result, None, stdout); monitor_ch.send(message).unwrap(); } diff --git a/library/test/src/event.rs b/library/test/src/event.rs index 2103a0d10f4c6..206f3e10e847d 100644 --- a/library/test/src/event.rs +++ b/library/test/src/event.rs @@ -3,10 +3,11 @@ use super::test_result::TestResult; use super::time::TestExecTime; -use super::types::TestDesc; +use super::types::{TestDesc, TestId}; #[derive(Debug, Clone)] pub struct CompletedTest { + pub id: TestId, pub desc: TestDesc, pub result: TestResult, pub exec_time: Option, @@ -15,12 +16,13 @@ pub struct CompletedTest { impl CompletedTest { pub fn new( + id: TestId, desc: TestDesc, result: TestResult, exec_time: Option, stdout: Vec, ) -> Self { - Self { desc, result, exec_time, stdout } + Self { id, desc, result, exec_time, stdout } } } diff --git a/library/test/src/helpers/exit_code.rs b/library/test/src/helpers/exit_code.rs index 31e234d981800..50bb260762a70 100644 --- a/library/test/src/helpers/exit_code.rs +++ b/library/test/src/helpers/exit_code.rs @@ -4,7 +4,7 @@ use std::process::ExitStatus; #[cfg(not(unix))] pub fn get_exit_code(status: ExitStatus) -> Result { - status.code().ok_or("received no exit code from child process".into()) + status.code().ok_or_else(|| "received no exit code from child process".into()) } #[cfg(unix)] diff --git a/library/test/src/lib.rs b/library/test/src/lib.rs index 3415eaa9fb345..2e0864f303cc9 100644 --- a/library/test/src/lib.rs +++ b/library/test/src/lib.rs @@ -25,7 +25,6 @@ #![feature(nll)] #![feature(available_concurrency)] #![feature(internal_output_capture)] -#![feature(option_unwrap_none)] #![feature(panic_unwind)] #![feature(staged_api)] #![feature(termination_trait_lib)] @@ -55,7 +54,7 @@ pub mod test { time::{TestExecTime, TestTimeOptions}, types::{ DynTestFn, DynTestName, StaticBenchFn, StaticTestFn, StaticTestName, TestDesc, - TestDescAndFn, TestName, TestType, + TestDescAndFn, TestId, TestName, TestType, }, }; } @@ -216,9 +215,10 @@ where // Use a deterministic hasher type TestMap = - HashMap>; + HashMap>; struct TimeoutEntry { + id: TestId, desc: TestDesc, timeout: Instant, } @@ -250,7 +250,9 @@ where let (filtered_tests, filtered_benchs): (Vec<_>, _) = filtered_tests .into_iter() - .partition(|e| matches!(e.testfn, StaticTestFn(_) | DynTestFn(_))); + .enumerate() + .map(|(i, e)| (TestId(i), e)) + .partition(|(_, e)| matches!(e.testfn, StaticTestFn(_) | DynTestFn(_))); let concurrency = opts.test_threads.unwrap_or_else(get_concurrency); @@ -279,7 +281,7 @@ where break; } let timeout_entry = timeout_queue.pop_front().unwrap(); - if running_tests.contains_key(&timeout_entry.desc) { + if running_tests.contains_key(&timeout_entry.id) { timed_out.push(timeout_entry.desc); } } @@ -295,11 +297,12 @@ where if concurrency == 1 { while !remaining.is_empty() { - let test = remaining.pop().unwrap(); + let (id, test) = remaining.pop().unwrap(); let event = TestEvent::TeWait(test.desc.clone()); notify_about_test_event(event)?; - run_test(opts, !opts.run_tests, test, run_strategy, tx.clone(), Concurrent::No) - .unwrap_none(); + let join_handle = + run_test(opts, !opts.run_tests, id, test, run_strategy, tx.clone(), Concurrent::No); + assert!(join_handle.is_none()); let completed_test = rx.recv().unwrap(); let event = TestEvent::TeResult(completed_test); @@ -308,7 +311,7 @@ where } else { while pending > 0 || !remaining.is_empty() { while pending < concurrency && !remaining.is_empty() { - let test = remaining.pop().unwrap(); + let (id, test) = remaining.pop().unwrap(); let timeout = time::get_default_test_timeout(); let desc = test.desc.clone(); @@ -317,13 +320,14 @@ where let join_handle = run_test( opts, !opts.run_tests, + id, test, run_strategy, tx.clone(), Concurrent::Yes, ); - running_tests.insert(desc.clone(), RunningTest { join_handle }); - timeout_queue.push_back(TimeoutEntry { desc, timeout }); + running_tests.insert(id, RunningTest { join_handle }); + timeout_queue.push_back(TimeoutEntry { id, desc, timeout }); pending += 1; } @@ -352,13 +356,12 @@ where } let mut completed_test = res.unwrap(); - if let Some(running_test) = running_tests.remove(&completed_test.desc) { - if let Some(join_handle) = running_test.join_handle { - if let Err(_) = join_handle.join() { - if let TrOk = completed_test.result { - completed_test.result = - TrFailedMsg("panicked after reporting success".to_string()); - } + let running_test = running_tests.remove(&completed_test.id).unwrap(); + if let Some(join_handle) = running_test.join_handle { + if let Err(_) = join_handle.join() { + if let TrOk = completed_test.result { + completed_test.result = + TrFailedMsg("panicked after reporting success".to_string()); } } } @@ -371,10 +374,10 @@ where if opts.bench_benchmarks { // All benchmarks run at the end, in serial. - for b in filtered_benchs { + for (id, b) in filtered_benchs { let event = TestEvent::TeWait(b.desc.clone()); notify_about_test_event(event)?; - run_test(opts, false, b, run_strategy, tx.clone(), Concurrent::No); + run_test(opts, false, id, b, run_strategy, tx.clone(), Concurrent::No); let completed_test = rx.recv().unwrap(); let event = TestEvent::TeResult(completed_test); @@ -448,6 +451,7 @@ pub fn convert_benchmarks_to_tests(tests: Vec) -> Vec, @@ -461,7 +465,7 @@ pub fn run_test( && !cfg!(target_os = "emscripten"); if force_ignore || desc.ignore || ignore_because_no_process_support { - let message = CompletedTest::new(desc, TrIgnored, None, Vec::new()); + let message = CompletedTest::new(id, desc, TrIgnored, None, Vec::new()); monitor_ch.send(message).unwrap(); return None; } @@ -474,6 +478,7 @@ pub fn run_test( } fn run_test_inner( + id: TestId, desc: TestDesc, monitor_ch: Sender, testfn: Box, @@ -484,6 +489,7 @@ pub fn run_test( let runtest = move || match opts.strategy { RunStrategy::InProcess => run_test_in_process( + id, desc, opts.nocapture, opts.time.is_some(), @@ -492,6 +498,7 @@ pub fn run_test( opts.time, ), RunStrategy::SpawnPrimary => spawn_test_subprocess( + id, desc, opts.nocapture, opts.time.is_some(), @@ -530,14 +537,14 @@ pub fn run_test( match testfn { DynBenchFn(bencher) => { // Benchmarks aren't expected to panic, so we run them all in-process. - crate::bench::benchmark(desc, monitor_ch, opts.nocapture, |harness| { + crate::bench::benchmark(id, desc, monitor_ch, opts.nocapture, |harness| { bencher.run(harness) }); None } StaticBenchFn(benchfn) => { // Benchmarks aren't expected to panic, so we run them all in-process. - crate::bench::benchmark(desc, monitor_ch, opts.nocapture, benchfn); + crate::bench::benchmark(id, desc, monitor_ch, opts.nocapture, benchfn); None } DynTestFn(f) => { @@ -546,6 +553,7 @@ pub fn run_test( _ => panic!("Cannot run dynamic test fn out-of-process"), }; run_test_inner( + id, desc, monitor_ch, Box::new(move || __rust_begin_short_backtrace(f)), @@ -553,6 +561,7 @@ pub fn run_test( ) } StaticTestFn(f) => run_test_inner( + id, desc, monitor_ch, Box::new(move || __rust_begin_short_backtrace(f)), @@ -571,6 +580,7 @@ fn __rust_begin_short_backtrace(f: F) { } fn run_test_in_process( + id: TestId, desc: TestDesc, nocapture: bool, report_time: bool, @@ -599,11 +609,12 @@ fn run_test_in_process( Err(e) => calc_result(&desc, Err(e.as_ref()), &time_opts, &exec_time), }; let stdout = data.lock().unwrap_or_else(|e| e.into_inner()).to_vec(); - let message = CompletedTest::new(desc, test_result, exec_time, stdout); + let message = CompletedTest::new(id, desc, test_result, exec_time, stdout); monitor_ch.send(message).unwrap(); } fn spawn_test_subprocess( + id: TestId, desc: TestDesc, nocapture: bool, report_time: bool, @@ -653,7 +664,7 @@ fn spawn_test_subprocess( (result, test_output, exec_time) })(); - let message = CompletedTest::new(desc, result, exec_time, test_output); + let message = CompletedTest::new(id, desc, result, exec_time, test_output); monitor_ch.send(message).unwrap(); } diff --git a/library/test/src/tests.rs b/library/test/src/tests.rs index e3c9b3869154a..6a3f31b74ea59 100644 --- a/library/test/src/tests.rs +++ b/library/test/src/tests.rs @@ -94,7 +94,7 @@ pub fn do_not_run_ignored_tests() { testfn: DynTestFn(Box::new(f)), }; let (tx, rx) = channel(); - run_test(&TestOpts::new(), false, desc, RunStrategy::InProcess, tx, Concurrent::No); + run_test(&TestOpts::new(), false, TestId(0), desc, RunStrategy::InProcess, tx, Concurrent::No); let result = rx.recv().unwrap().result; assert_ne!(result, TrOk); } @@ -113,7 +113,7 @@ pub fn ignored_tests_result_in_ignored() { testfn: DynTestFn(Box::new(f)), }; let (tx, rx) = channel(); - run_test(&TestOpts::new(), false, desc, RunStrategy::InProcess, tx, Concurrent::No); + run_test(&TestOpts::new(), false, TestId(0), desc, RunStrategy::InProcess, tx, Concurrent::No); let result = rx.recv().unwrap().result; assert_eq!(result, TrIgnored); } @@ -136,7 +136,7 @@ fn test_should_panic() { testfn: DynTestFn(Box::new(f)), }; let (tx, rx) = channel(); - run_test(&TestOpts::new(), false, desc, RunStrategy::InProcess, tx, Concurrent::No); + run_test(&TestOpts::new(), false, TestId(0), desc, RunStrategy::InProcess, tx, Concurrent::No); let result = rx.recv().unwrap().result; assert_eq!(result, TrOk); } @@ -159,7 +159,7 @@ fn test_should_panic_good_message() { testfn: DynTestFn(Box::new(f)), }; let (tx, rx) = channel(); - run_test(&TestOpts::new(), false, desc, RunStrategy::InProcess, tx, Concurrent::No); + run_test(&TestOpts::new(), false, TestId(0), desc, RunStrategy::InProcess, tx, Concurrent::No); let result = rx.recv().unwrap().result; assert_eq!(result, TrOk); } @@ -187,7 +187,7 @@ fn test_should_panic_bad_message() { testfn: DynTestFn(Box::new(f)), }; let (tx, rx) = channel(); - run_test(&TestOpts::new(), false, desc, RunStrategy::InProcess, tx, Concurrent::No); + run_test(&TestOpts::new(), false, TestId(0), desc, RunStrategy::InProcess, tx, Concurrent::No); let result = rx.recv().unwrap().result; assert_eq!(result, TrFailedMsg(failed_msg.to_string())); } @@ -219,7 +219,7 @@ fn test_should_panic_non_string_message_type() { testfn: DynTestFn(Box::new(f)), }; let (tx, rx) = channel(); - run_test(&TestOpts::new(), false, desc, RunStrategy::InProcess, tx, Concurrent::No); + run_test(&TestOpts::new(), false, TestId(0), desc, RunStrategy::InProcess, tx, Concurrent::No); let result = rx.recv().unwrap().result; assert_eq!(result, TrFailedMsg(failed_msg)); } @@ -243,7 +243,15 @@ fn test_should_panic_but_succeeds() { testfn: DynTestFn(Box::new(f)), }; let (tx, rx) = channel(); - run_test(&TestOpts::new(), false, desc, RunStrategy::InProcess, tx, Concurrent::No); + run_test( + &TestOpts::new(), + false, + TestId(0), + desc, + RunStrategy::InProcess, + tx, + Concurrent::No, + ); let result = rx.recv().unwrap().result; assert_eq!( result, @@ -270,7 +278,7 @@ fn report_time_test_template(report_time: bool) -> Option { let test_opts = TestOpts { time_options, ..TestOpts::new() }; let (tx, rx) = channel(); - run_test(&test_opts, false, desc, RunStrategy::InProcess, tx, Concurrent::No); + run_test(&test_opts, false, TestId(0), desc, RunStrategy::InProcess, tx, Concurrent::No); let exec_time = rx.recv().unwrap().exec_time; exec_time } @@ -305,7 +313,7 @@ fn time_test_failure_template(test_type: TestType) -> TestResult { let test_opts = TestOpts { time_options: Some(time_options), ..TestOpts::new() }; let (tx, rx) = channel(); - run_test(&test_opts, false, desc, RunStrategy::InProcess, tx, Concurrent::No); + run_test(&test_opts, false, TestId(0), desc, RunStrategy::InProcess, tx, Concurrent::No); let result = rx.recv().unwrap().result; result @@ -637,7 +645,7 @@ pub fn test_bench_no_iter() { test_type: TestType::Unknown, }; - crate::bench::benchmark(desc, tx, true, f); + crate::bench::benchmark(TestId(0), desc, tx, true, f); rx.recv().unwrap(); } @@ -657,7 +665,7 @@ pub fn test_bench_iter() { test_type: TestType::Unknown, }; - crate::bench::benchmark(desc, tx, true, f); + crate::bench::benchmark(TestId(0), desc, tx, true, f); rx.recv().unwrap(); } diff --git a/library/test/src/types.rs b/library/test/src/types.rs index 5b75d2f367fff..c5d91f653b356 100644 --- a/library/test/src/types.rs +++ b/library/test/src/types.rs @@ -112,9 +112,13 @@ impl fmt::Debug for TestFn { } } +// A unique integer associated with each test. +#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] +pub struct TestId(pub usize); + // The definition of a single test. A test runner will run a list of // these. -#[derive(Clone, Debug, PartialEq, Eq, Hash)] +#[derive(Clone, Debug)] pub struct TestDesc { pub name: TestName, pub ignore: bool, diff --git a/library/unwind/Cargo.toml b/library/unwind/Cargo.toml index 4f7a304a59f68..69128591e0672 100644 --- a/library/unwind/Cargo.toml +++ b/library/unwind/Cargo.toml @@ -2,6 +2,8 @@ authors = ["The Rust Project Developers"] name = "unwind" version = "0.0.0" +license = "MIT OR Apache-2.0" +repository = "https://github.com/rust-lang/rust.git" edition = "2018" include = [ '/libunwind/*', diff --git a/library/unwind/src/lib.rs b/library/unwind/src/lib.rs index d145c6767ec26..be5e56c71e36f 100644 --- a/library/unwind/src/lib.rs +++ b/library/unwind/src/lib.rs @@ -15,7 +15,7 @@ cfg_if::cfg_if! { target_os = "none", ))] { // These "unix" family members do not have unwinder. - // Note this also matches x86_64-linux-kernel. + // Note this also matches x86_64-unknown-none-linuxkernel. } else if #[cfg(any( unix, windows, diff --git a/src/bootstrap/CHANGELOG.md b/src/bootstrap/CHANGELOG.md index f899f21080ebe..8437a10426b71 100644 --- a/src/bootstrap/CHANGELOG.md +++ b/src/bootstrap/CHANGELOG.md @@ -8,6 +8,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). ## [Changes since the last major version] - `llvm-libunwind` now accepts `in-tree` (formerly true), `system` or `no` (formerly false) [#77703](https://github.com/rust-lang/rust/pull/77703) +- The options `infodir`, `localstatedir`, and `gpg-password-file` are no longer allowed in config.toml. Previously, they were ignored without warning. Note that `infodir` and `localstatedir` are still accepted by `./configure`, with a warning. [#82451](https://github.com/rust-lang/rust/pull/82451) ### Non-breaking changes diff --git a/src/bootstrap/Cargo.toml b/src/bootstrap/Cargo.toml index e04128d1b0b25..c14ad6fa5fff4 100644 --- a/src/bootstrap/Cargo.toml +++ b/src/bootstrap/Cargo.toml @@ -53,7 +53,7 @@ merge = "0.1.0" [target.'cfg(windows)'.dependencies.winapi] version = "0.3" -features = ["fileapi", "ioapiset", "jobapi2", "handleapi", "winioctl"] +features = ["fileapi", "ioapiset", "jobapi2", "handleapi", "winioctl", "psapi", "impl-default"] [dev-dependencies] pretty_assertions = "0.6" diff --git a/src/bootstrap/bin/rustc.rs b/src/bootstrap/bin/rustc.rs index 6b1be0ca09d0d..d462dc4d1165c 100644 --- a/src/bootstrap/bin/rustc.rs +++ b/src/bootstrap/bin/rustc.rs @@ -17,7 +17,7 @@ use std::env; use std::path::PathBuf; -use std::process::Command; +use std::process::{Child, Command}; use std::str::FromStr; use std::time::Instant; @@ -138,27 +138,36 @@ fn main() { cmd.arg("-Z").arg("force-unstable-if-unmarked"); } + let is_test = args.iter().any(|a| a == "--test"); if verbose > 1 { let rust_env_vars = env::vars().filter(|(k, _)| k.starts_with("RUST") || k.starts_with("CARGO")); + let prefix = if is_test { "[RUSTC-SHIM] rustc --test" } else { "[RUSTC-SHIM] rustc" }; + let prefix = match crate_name { + Some(crate_name) => format!("{} {}", prefix, crate_name), + None => prefix.to_string(), + }; for (i, (k, v)) in rust_env_vars.enumerate() { - eprintln!("rustc env[{}]: {:?}={:?}", i, k, v); + eprintln!("{} env[{}]: {:?}={:?}", prefix, i, k, v); } - eprintln!("rustc working directory: {}", env::current_dir().unwrap().display()); + eprintln!("{} working directory: {}", prefix, env::current_dir().unwrap().display()); eprintln!( - "rustc command: {:?}={:?} {:?}", + "{} command: {:?}={:?} {:?}", + prefix, bootstrap::util::dylib_path_var(), env::join_paths(&dylib_path).unwrap(), cmd, ); - eprintln!("sysroot: {:?}", sysroot); - eprintln!("libdir: {:?}", libdir); + eprintln!("{} sysroot: {:?}", prefix, sysroot); + eprintln!("{} libdir: {:?}", prefix, libdir); } let start = Instant::now(); - let status = { + let (child, status) = { let errmsg = format!("\nFailed to run:\n{:?}\n-------------", cmd); - cmd.status().expect(&errmsg) + let mut child = cmd.spawn().expect(&errmsg); + let status = child.wait().expect(&errmsg); + (child, status) }; if env::var_os("RUSTC_PRINT_STEP_TIMINGS").is_some() @@ -166,11 +175,10 @@ fn main() { { if let Some(crate_name) = crate_name { let dur = start.elapsed(); - let is_test = args.iter().any(|a| a == "--test"); // If the user requested resource usage data, then // include that in addition to the timing output. let rusage_data = - env::var_os("RUSTC_PRINT_STEP_RUSAGE").and_then(|_| format_rusage_data()); + env::var_os("RUSTC_PRINT_STEP_RUSAGE").and_then(|_| format_rusage_data(child)); eprintln!( "[RUSTC-TIMING] {} test:{} {}.{:03}{}{}", crate_name, @@ -207,19 +215,83 @@ fn main() { } } -#[cfg(not(unix))] -/// getrusage is not available on non-unix platforms. So for now, we do not -/// bother trying to make a shim for it. -fn format_rusage_data() -> Option { +#[cfg(all(not(unix), not(windows)))] +// In the future we can add this for more platforms +fn format_rusage_data(_child: Child) -> Option { None } +#[cfg(windows)] +fn format_rusage_data(child: Child) -> Option { + use std::os::windows::io::AsRawHandle; + use winapi::um::{processthreadsapi, psapi, timezoneapi}; + let handle = child.as_raw_handle(); + macro_rules! try_bool { + ($e:expr) => { + if $e != 1 { + return None; + } + }; + } + + let mut user_filetime = Default::default(); + let mut user_time = Default::default(); + let mut kernel_filetime = Default::default(); + let mut kernel_time = Default::default(); + let mut memory_counters = psapi::PROCESS_MEMORY_COUNTERS::default(); + + unsafe { + try_bool!(processthreadsapi::GetProcessTimes( + handle, + &mut Default::default(), + &mut Default::default(), + &mut kernel_filetime, + &mut user_filetime, + )); + try_bool!(timezoneapi::FileTimeToSystemTime(&user_filetime, &mut user_time)); + try_bool!(timezoneapi::FileTimeToSystemTime(&kernel_filetime, &mut kernel_time)); + + // Unlike on Linux with RUSAGE_CHILDREN, this will only return memory information for the process + // with the given handle and none of that process's children. + try_bool!(psapi::GetProcessMemoryInfo( + handle as _, + &mut memory_counters as *mut _ as _, + std::mem::size_of::() as u32, + )); + } + + // Guide on interpreting these numbers: + // https://docs.microsoft.com/en-us/windows/win32/psapi/process-memory-usage-information + let peak_working_set = memory_counters.PeakWorkingSetSize / 1024; + let peak_page_file = memory_counters.PeakPagefileUsage / 1024; + let peak_paged_pool = memory_counters.QuotaPeakPagedPoolUsage / 1024; + let peak_nonpaged_pool = memory_counters.QuotaPeakNonPagedPoolUsage / 1024; + Some(format!( + "user: {USER_SEC}.{USER_USEC:03} \ + sys: {SYS_SEC}.{SYS_USEC:03} \ + peak working set (kb): {PEAK_WORKING_SET} \ + peak page file usage (kb): {PEAK_PAGE_FILE} \ + peak paged pool usage (kb): {PEAK_PAGED_POOL} \ + peak non-paged pool usage (kb): {PEAK_NONPAGED_POOL} \ + page faults: {PAGE_FAULTS}", + USER_SEC = user_time.wSecond + (user_time.wMinute * 60), + USER_USEC = user_time.wMilliseconds, + SYS_SEC = kernel_time.wSecond + (kernel_time.wMinute * 60), + SYS_USEC = kernel_time.wMilliseconds, + PEAK_WORKING_SET = peak_working_set, + PEAK_PAGE_FILE = peak_page_file, + PEAK_PAGED_POOL = peak_paged_pool, + PEAK_NONPAGED_POOL = peak_nonpaged_pool, + PAGE_FAULTS = memory_counters.PageFaultCount, + )) +} + #[cfg(unix)] /// Tries to build a string with human readable data for several of the rusage /// fields. Note that we are focusing mainly on data that we believe to be /// supplied on Linux (the `rusage` struct has other fields in it but they are /// currently unsupported by Linux). -fn format_rusage_data() -> Option { +fn format_rusage_data(_child: Child) -> Option { let rusage: libc::rusage = unsafe { let mut recv = std::mem::zeroed(); // -1 is RUSAGE_CHILDREN, which means to get the rusage for all children diff --git a/src/bootstrap/bootstrap.py b/src/bootstrap/bootstrap.py index 3e7d1d54f1284..4111420e4745a 100644 --- a/src/bootstrap/bootstrap.py +++ b/src/bootstrap/bootstrap.py @@ -383,7 +383,7 @@ def __init__(self): self.nix_deps_dir = None self.rustc_commit = None - def download_stage0(self): + def download_toolchain(self, stage0=True, rustc_channel=None): """Fetch the build system for Rust, written in Rust This method will build a cache directory, then it will fetch the @@ -393,43 +393,47 @@ def download_stage0(self): Each downloaded tarball is extracted, after that, the script will move all the content to the right place. """ - rustc_channel = self.rustc_channel + if rustc_channel is None: + rustc_channel = self.rustc_channel rustfmt_channel = self.rustfmt_channel - - if self.rustc().startswith(self.bin_root()) and \ - (not os.path.exists(self.rustc()) or - self.program_out_of_date(self.rustc_stamp(), self.date + str(self.rustc_commit))): - if os.path.exists(self.bin_root()): - shutil.rmtree(self.bin_root()) - download_rustc = self.rustc_commit is not None + bin_root = self.bin_root(stage0) + + key = self.date + if not stage0: + key += str(self.rustc_commit) + if self.rustc(stage0).startswith(bin_root) and \ + (not os.path.exists(self.rustc(stage0)) or + self.program_out_of_date(self.rustc_stamp(stage0), key)): + if os.path.exists(bin_root): + shutil.rmtree(bin_root) tarball_suffix = '.tar.xz' if support_xz() else '.tar.gz' filename = "rust-std-{}-{}{}".format( rustc_channel, self.build, tarball_suffix) pattern = "rust-std-{}".format(self.build) - self._download_component_helper(filename, pattern, tarball_suffix, download_rustc) + self._download_component_helper(filename, pattern, tarball_suffix, stage0) filename = "rustc-{}-{}{}".format(rustc_channel, self.build, tarball_suffix) - self._download_component_helper(filename, "rustc", tarball_suffix, download_rustc) + self._download_component_helper(filename, "rustc", tarball_suffix, stage0) filename = "cargo-{}-{}{}".format(rustc_channel, self.build, tarball_suffix) self._download_component_helper(filename, "cargo", tarball_suffix) - if self.rustc_commit is not None: + if not stage0: filename = "rustc-dev-{}-{}{}".format(rustc_channel, self.build, tarball_suffix) self._download_component_helper( - filename, "rustc-dev", tarball_suffix, download_rustc + filename, "rustc-dev", tarball_suffix, stage0 ) - self.fix_bin_or_dylib("{}/bin/rustc".format(self.bin_root())) - self.fix_bin_or_dylib("{}/bin/rustdoc".format(self.bin_root())) - self.fix_bin_or_dylib("{}/bin/cargo".format(self.bin_root())) - lib_dir = "{}/lib".format(self.bin_root()) + self.fix_bin_or_dylib("{}/bin/rustc".format(bin_root)) + self.fix_bin_or_dylib("{}/bin/rustdoc".format(bin_root)) + self.fix_bin_or_dylib("{}/bin/cargo".format(bin_root)) + lib_dir = "{}/lib".format(bin_root) for lib in os.listdir(lib_dir): if lib.endswith(".so"): - self.fix_bin_or_dylib(os.path.join(lib_dir, lib), rpath_libz=True) - with output(self.rustc_stamp()) as rust_stamp: - rust_stamp.write(self.date + str(self.rustc_commit)) + self.fix_bin_or_dylib(os.path.join(lib_dir, lib)) + with output(self.rustc_stamp(stage0)) as rust_stamp: + rust_stamp.write(key) - if self.rustfmt() and self.rustfmt().startswith(self.bin_root()) and ( + if self.rustfmt() and self.rustfmt().startswith(bin_root) and ( not os.path.exists(self.rustfmt()) or self.program_out_of_date(self.rustfmt_stamp(), self.rustfmt_channel) ): @@ -440,12 +444,13 @@ def download_stage0(self): self._download_component_helper( filename, "rustfmt-preview", tarball_suffix, key=date ) - self.fix_bin_or_dylib("{}/bin/rustfmt".format(self.bin_root())) - self.fix_bin_or_dylib("{}/bin/cargo-fmt".format(self.bin_root())) + self.fix_bin_or_dylib("{}/bin/rustfmt".format(bin_root)) + self.fix_bin_or_dylib("{}/bin/cargo-fmt".format(bin_root)) with output(self.rustfmt_stamp()) as rustfmt_stamp: rustfmt_stamp.write(self.rustfmt_channel) - if self.downloading_llvm(): + # Avoid downloading LLVM twice (once for stage0 and once for the master rustc) + if self.downloading_llvm() and stage0: # We want the most recent LLVM submodule update to avoid downloading # LLVM more often than necessary. # @@ -463,6 +468,8 @@ def download_stage0(self): "--", "{}/src/llvm-project".format(top_level), "{}/src/bootstrap/download-ci-llvm-stamp".format(top_level), + # the LLVM shared object file is named `LLVM-12-rust-{version}-nightly` + "{}/src/version".format(top_level) ]).decode(sys.getdefaultencoding()).strip() llvm_assertions = self.get_toml('assertions', 'llvm') == 'true' llvm_root = self.llvm_root() @@ -470,10 +477,10 @@ def download_stage0(self): if self.program_out_of_date(self.llvm_stamp(), llvm_sha + str(llvm_assertions)): self._download_ci_llvm(llvm_sha, llvm_assertions) for binary in ["llvm-config", "FileCheck"]: - self.fix_bin_or_dylib(os.path.join(llvm_root, "bin", binary), rpath_libz=True) + self.fix_bin_or_dylib(os.path.join(llvm_root, "bin", binary)) for lib in os.listdir(llvm_lib): if lib.endswith(".so"): - self.fix_bin_or_dylib(os.path.join(llvm_lib, lib), rpath_libz=True) + self.fix_bin_or_dylib(os.path.join(llvm_lib, lib)) with output(self.llvm_stamp()) as llvm_stamp: llvm_stamp.write(llvm_sha + str(llvm_assertions)) @@ -496,27 +503,26 @@ def downloading_llvm(self): or (opt == "if-available" and self.build in supported_platforms) def _download_component_helper( - self, filename, pattern, tarball_suffix, download_rustc=False, key=None + self, filename, pattern, tarball_suffix, stage0=True, key=None ): if key is None: - if download_rustc: - key = self.rustc_commit - else: + if stage0: key = self.date + else: + key = self.rustc_commit cache_dst = os.path.join(self.build_dir, "cache") rustc_cache = os.path.join(cache_dst, key) if not os.path.exists(rustc_cache): os.makedirs(rustc_cache) - if download_rustc: - url = "https://ci-artifacts.rust-lang.org/rustc-builds/{}".format(self.rustc_commit) - else: + if stage0: url = "{}/dist/{}".format(self._download_url, key) + else: + url = "https://ci-artifacts.rust-lang.org/rustc-builds/{}".format(self.rustc_commit) tarball = os.path.join(rustc_cache, filename) if not os.path.exists(tarball): - do_verify = not download_rustc - get("{}/{}".format(url, filename), tarball, verbose=self.verbose, do_verify=do_verify) - unpack(tarball, tarball_suffix, self.bin_root(), match=pattern, verbose=self.verbose) + get("{}/{}".format(url, filename), tarball, verbose=self.verbose, do_verify=stage0) + unpack(tarball, tarball_suffix, self.bin_root(stage0), match=pattern, verbose=self.verbose) def _download_ci_llvm(self, llvm_sha, llvm_assertions): cache_prefix = "llvm-{}-{}".format(llvm_sha, llvm_assertions) @@ -542,7 +548,7 @@ def _download_ci_llvm(self, llvm_sha, llvm_assertions): match="rust-dev", verbose=self.verbose) - def fix_bin_or_dylib(self, fname, rpath_libz=False): + def fix_bin_or_dylib(self, fname): """Modifies the interpreter section of 'fname' to fix the dynamic linker, or the RPATH section, to fix the dynamic library search path @@ -574,59 +580,52 @@ def fix_bin_or_dylib(self, fname, rpath_libz=False): nix_os_msg = "info: you seem to be running NixOS. Attempting to patch" print(nix_os_msg, fname) - # Only build `stage0/.nix-deps` once. + # Only build `.nix-deps` once. nix_deps_dir = self.nix_deps_dir if not nix_deps_dir: - nix_deps_dir = "{}/.nix-deps".format(self.bin_root()) - if not os.path.exists(nix_deps_dir): - os.makedirs(nix_deps_dir) - - nix_deps = [ - # Needed for the path of `ld-linux.so` (via `nix-support/dynamic-linker`). - "stdenv.cc.bintools", - - # Needed as a system dependency of `libLLVM-*.so`. - "zlib", - - # Needed for patching ELF binaries (see doc comment above). - "patchelf", - ] - # Run `nix-build` to "build" each dependency (which will likely reuse # the existing `/nix/store` copy, or at most download a pre-built copy). - # Importantly, we don't rely on `nix-build` printing the `/nix/store` - # path on stdout, but use `-o` to symlink it into `stage0/.nix-deps/$dep`, - # ensuring garbage collection will never remove the `/nix/store` path - # (which would break our patched binaries that hardcode those paths). - for dep in nix_deps: - try: - subprocess.check_output([ - "nix-build", "", - "-A", dep, - "-o", "{}/{}".format(nix_deps_dir, dep), - ]) - except subprocess.CalledProcessError as reason: - print("warning: failed to call nix-build:", reason) - return - + # + # Importantly, we create a gc-root called `.nix-deps` in the `build/` + # directory, but still reference the actual `/nix/store` path in the rpath + # as it makes it significantly more robust against changes to the location of + # the `.nix-deps` location. + # + # bintools: Needed for the path of `ld-linux.so` (via `nix-support/dynamic-linker`). + # zlib: Needed as a system dependency of `libLLVM-*.so`. + # patchelf: Needed for patching ELF binaries (see doc comment above). + nix_deps_dir = "{}/{}".format(self.build_dir, ".nix-deps") + nix_expr = ''' + with (import {}); + symlinkJoin { + name = "rust-stage0-dependencies"; + paths = [ + zlib + patchelf + stdenv.cc.bintools + ]; + } + ''' + try: + subprocess.check_output([ + "nix-build", "-E", nix_expr, "-o", nix_deps_dir, + ]) + except subprocess.CalledProcessError as reason: + print("warning: failed to call nix-build:", reason) + return self.nix_deps_dir = nix_deps_dir - patchelf = "{}/patchelf/bin/patchelf".format(nix_deps_dir) - patchelf_args = [] - - if rpath_libz: - # Patch RPATH to add `zlib` dependency that stems from LLVM - dylib_deps = ["zlib"] - rpath_entries = [ - # Relative default, all binary and dynamic libraries we ship - # appear to have this (even when `../lib` is redundant). - "$ORIGIN/../lib", - ] + ["{}/{}/lib".format(nix_deps_dir, dep) for dep in dylib_deps] - patchelf_args += ["--set-rpath", ":".join(rpath_entries)] + patchelf = "{}/bin/patchelf".format(nix_deps_dir) + rpath_entries = [ + # Relative default, all binary and dynamic libraries we ship + # appear to have this (even when `../lib` is redundant). + "$ORIGIN/../lib", + os.path.join(os.path.realpath(nix_deps_dir), "lib") + ] + patchelf_args = ["--set-rpath", ":".join(rpath_entries)] if not fname.endswith(".so"): # Finally, set the corret .interp for binaries - bintools_dir = "{}/stdenv.cc.bintools".format(nix_deps_dir) - with open("{}/nix-support/dynamic-linker".format(bintools_dir)) as dynamic_linker: + with open("{}/nix-support/dynamic-linker".format(nix_deps_dir)) as dynamic_linker: patchelf_args += ["--set-interpreter", dynamic_linker.read().rstrip()] try: @@ -635,11 +634,13 @@ def fix_bin_or_dylib(self, fname, rpath_libz=False): print("warning: failed to call patchelf:", reason) return - # Return the stage1 compiler to download, if any. - def maybe_download_rustc(self): + # If `download-rustc` is set, download the most recent commit with CI artifacts + def maybe_download_ci_toolchain(self): # If `download-rustc` is not set, default to rebuilding. - if self.get_toml("download-rustc", section="rust") != "true": + download_rustc = self.get_toml("download-rustc", section="rust") + if download_rustc is None or download_rustc == "false": return None + assert download_rustc == "true" or download_rustc == "if-unchanged", download_rustc # Handle running from a directory other than the top level rev_parse = ["git", "rev-parse", "--show-toplevel"] @@ -654,19 +655,27 @@ def maybe_download_rustc(self): # Warn if there were changes to the compiler since the ancestor commit. status = subprocess.call(["git", "diff-index", "--quiet", commit, "--", compiler]) if status != 0: + if download_rustc == "if-unchanged": + return None print("warning: `download-rustc` is enabled, but there are changes to compiler/") - return commit + if self.verbose: + print("using downloaded stage1 artifacts from CI (commit {})".format(commit)) + self.rustc_commit = commit + # FIXME: support downloading artifacts from the beta channel + self.download_toolchain(False, "nightly") - def rustc_stamp(self): - """Return the path for .rustc-stamp + def rustc_stamp(self, stage0): + """Return the path for .rustc-stamp at the given stage >>> rb = RustBuild() >>> rb.build_dir = "build" - >>> rb.rustc_stamp() == os.path.join("build", "stage0", ".rustc-stamp") + >>> rb.rustc_stamp(True) == os.path.join("build", "stage0", ".rustc-stamp") + True + >>> rb.rustc_stamp(False) == os.path.join("build", "ci-rustc", ".rustc-stamp") True """ - return os.path.join(self.bin_root(), '.rustc-stamp') + return os.path.join(self.bin_root(stage0), '.rustc-stamp') def rustfmt_stamp(self): """Return the path for .rustfmt-stamp @@ -676,7 +685,7 @@ def rustfmt_stamp(self): >>> rb.rustfmt_stamp() == os.path.join("build", "stage0", ".rustfmt-stamp") True """ - return os.path.join(self.bin_root(), '.rustfmt-stamp') + return os.path.join(self.bin_root(True), '.rustfmt-stamp') def llvm_stamp(self): """Return the path for .rustfmt-stamp @@ -696,21 +705,27 @@ def program_out_of_date(self, stamp_path, key): with open(stamp_path, 'r') as stamp: return key != stamp.read() - def bin_root(self): - """Return the binary root directory + def bin_root(self, stage0): + """Return the binary root directory for the given stage >>> rb = RustBuild() >>> rb.build_dir = "build" - >>> rb.bin_root() == os.path.join("build", "stage0") + >>> rb.bin_root(True) == os.path.join("build", "stage0") + True + >>> rb.bin_root(False) == os.path.join("build", "ci-rustc") True When the 'build' property is given should be a nested directory: >>> rb.build = "devel" - >>> rb.bin_root() == os.path.join("build", "devel", "stage0") + >>> rb.bin_root(True) == os.path.join("build", "devel", "stage0") True """ - return os.path.join(self.build_dir, self.build, "stage0") + if stage0: + subdir = "stage0" + else: + subdir = "ci-rustc" + return os.path.join(self.build_dir, self.build, subdir) def llvm_root(self): """Return the CI LLVM root directory @@ -773,9 +788,9 @@ def cargo(self): """Return config path for cargo""" return self.program_config('cargo') - def rustc(self): + def rustc(self, stage0): """Return config path for rustc""" - return self.program_config('rustc') + return self.program_config('rustc', stage0) def rustfmt(self): """Return config path for rustfmt""" @@ -783,23 +798,27 @@ def rustfmt(self): return None return self.program_config('rustfmt') - def program_config(self, program): - """Return config path for the given program + def program_config(self, program, stage0=True): + """Return config path for the given program at the given stage >>> rb = RustBuild() >>> rb.config_toml = 'rustc = "rustc"\\n' >>> rb.program_config('rustc') 'rustc' >>> rb.config_toml = '' - >>> cargo_path = rb.program_config('cargo') - >>> cargo_path.rstrip(".exe") == os.path.join(rb.bin_root(), + >>> cargo_path = rb.program_config('cargo', True) + >>> cargo_path.rstrip(".exe") == os.path.join(rb.bin_root(True), + ... "bin", "cargo") + True + >>> cargo_path = rb.program_config('cargo', False) + >>> cargo_path.rstrip(".exe") == os.path.join(rb.bin_root(False), ... "bin", "cargo") True """ config = self.get_toml(program) if config: return os.path.expanduser(config) - return os.path.join(self.bin_root(), "bin", "{}{}".format( + return os.path.join(self.bin_root(stage0), "bin", "{}{}".format( program, self.exe_suffix())) @staticmethod @@ -854,14 +873,14 @@ def build_bootstrap(self): if "CARGO_BUILD_TARGET" in env: del env["CARGO_BUILD_TARGET"] env["CARGO_TARGET_DIR"] = build_dir - env["RUSTC"] = self.rustc() - env["LD_LIBRARY_PATH"] = os.path.join(self.bin_root(), "lib") + \ + env["RUSTC"] = self.rustc(True) + env["LD_LIBRARY_PATH"] = os.path.join(self.bin_root(True), "lib") + \ (os.pathsep + env["LD_LIBRARY_PATH"]) \ if "LD_LIBRARY_PATH" in env else "" - env["DYLD_LIBRARY_PATH"] = os.path.join(self.bin_root(), "lib") + \ + env["DYLD_LIBRARY_PATH"] = os.path.join(self.bin_root(True), "lib") + \ (os.pathsep + env["DYLD_LIBRARY_PATH"]) \ if "DYLD_LIBRARY_PATH" in env else "" - env["LIBRARY_PATH"] = os.path.join(self.bin_root(), "lib") + \ + env["LIBRARY_PATH"] = os.path.join(self.bin_root(True), "lib") + \ (os.pathsep + env["LIBRARY_PATH"]) \ if "LIBRARY_PATH" in env else "" # preserve existing RUSTFLAGS @@ -884,7 +903,7 @@ def build_bootstrap(self): if self.get_toml("deny-warnings", "rust") != "false": env["RUSTFLAGS"] += " -Dwarnings" - env["PATH"] = os.path.join(self.bin_root(), "bin") + \ + env["PATH"] = os.path.join(self.bin_root(True), "bin") + \ os.pathsep + env["PATH"] if not os.path.isfile(self.cargo()): raise Exception("no cargo executable found at `{}`".format( @@ -1135,14 +1154,9 @@ def bootstrap(help_triggered): build.update_submodules() # Fetch/build the bootstrap - build.rustc_commit = build.maybe_download_rustc() - if build.rustc_commit is not None: - if build.verbose: - commit = build.rustc_commit - print("using downloaded stage1 artifacts from CI (commit {})".format(commit)) - # FIXME: support downloading artifacts from the beta channel - build.rustc_channel = "nightly" - build.download_stage0() + build.download_toolchain() + # Download the master compiler if `download-rustc` is set + build.maybe_download_ci_toolchain() sys.stdout.flush() build.ensure_vendored() build.build_bootstrap() @@ -1158,6 +1172,8 @@ def bootstrap(help_triggered): env["RUSTC_BOOTSTRAP"] = '1' if toml_path: env["BOOTSTRAP_CONFIG"] = toml_path + if build.rustc_commit is not None: + env["BOOTSTRAP_DOWNLOAD_RUSTC"] = '1' run(args, env=env, verbose=build.verbose) diff --git a/src/bootstrap/builder.rs b/src/bootstrap/builder.rs index 9317c89625d06..38901a35296e1 100644 --- a/src/bootstrap/builder.rs +++ b/src/bootstrap/builder.rs @@ -397,6 +397,7 @@ impl<'a> Builder<'a> { test::Crate, test::CrateLibrustc, test::CrateRustdoc, + test::CrateRustdocJsonTypes, test::Linkcheck, test::TierCheck, test::Cargotest, @@ -419,6 +420,7 @@ impl<'a> Builder<'a> { test::Rustfmt, test::Miri, test::Clippy, + test::RustDemangler, test::CompiletestTest, test::RustdocJSStd, test::RustdocJSNotStd, @@ -465,6 +467,7 @@ impl<'a> Builder<'a> { dist::Rls, dist::RustAnalyzer, dist::Rustfmt, + dist::RustDemangler, dist::Clippy, dist::Miri, dist::LlvmTools, @@ -480,6 +483,7 @@ impl<'a> Builder<'a> { install::Rls, install::RustAnalyzer, install::Rustfmt, + install::RustDemangler, install::Clippy, install::Miri, install::Analysis, @@ -737,12 +741,7 @@ impl<'a> Builder<'a> { .env("RUSTDOC_REAL", self.rustdoc(compiler)) .env("RUSTC_BOOTSTRAP", "1"); - // cfg(bootstrap), can be removed on the next beta bump - if compiler.stage == 0 { - cmd.arg("-Winvalid_codeblock_attributes"); - } else { - cmd.arg("-Wrustdoc::invalid_codeblock_attributes"); - } + cmd.arg("-Wrustdoc::invalid_codeblock_attributes"); if self.config.deny_warnings { cmd.arg("-Dwarnings"); @@ -1299,12 +1298,7 @@ impl<'a> Builder<'a> { // fixed via better support from Cargo. cargo.env("RUSTC_LINT_FLAGS", lint_flags.join(" ")); - // cfg(bootstrap), can be removed on the next beta bump - if compiler.stage == 0 { - rustdocflags.arg("-Winvalid_codeblock_attributes"); - } else { - rustdocflags.arg("-Wrustdoc::invalid_codeblock_attributes"); - } + rustdocflags.arg("-Wrustdoc::invalid_codeblock_attributes"); } if mode == Mode::Rustc { diff --git a/src/bootstrap/builder/tests.rs b/src/bootstrap/builder/tests.rs index 885fcfff030a2..a881512e988ed 100644 --- a/src/bootstrap/builder/tests.rs +++ b/src/bootstrap/builder/tests.rs @@ -585,8 +585,11 @@ mod dist { rustfix_coverage: false, pass: None, }; + // Make sure rustfmt binary not being found isn't an error. + config.channel = "beta".to_string(); let build = Build::new(config); let mut builder = Builder::new(&build); + builder.run_step_descriptions(&Builder::get_step_descriptions(Kind::Test), &[]); let a = TargetSelection::from_user("A"); diff --git a/src/bootstrap/compile.rs b/src/bootstrap/compile.rs index 24800b7886d94..66a88e85fea39 100644 --- a/src/bootstrap/compile.rs +++ b/src/bootstrap/compile.rs @@ -65,7 +65,9 @@ impl Step for Std { // These artifacts were already copied (in `impl Step for Sysroot`). // Don't recompile them. - if builder.config.download_rustc { + // NOTE: the ABI of the beta compiler is different from the ABI of the downloaded compiler, + // so its artifacts can't be reused. + if builder.config.download_rustc && compiler.stage != 0 { return; } @@ -210,7 +212,7 @@ fn copy_self_contained_objects( panic!("Target {:?} does not have a \"wasi-root\" key", target.triple) }) .join("lib/wasm32-wasi"); - for &obj in &["crt1.o", "crt1-reactor.o"] { + for &obj in &["crt1-command.o", "crt1-reactor.o"] { copy_and_stamp( builder, &libdir_self_contained, @@ -460,11 +462,13 @@ impl Step for StartupObjects { let dst_file = &dst_dir.join(file.to_string() + ".o"); if !up_to_date(src_file, dst_file) { let mut cmd = Command::new(&builder.initial_rustc); + cmd.env("RUSTC_BOOTSTRAP", "1"); + if !builder.local_rebuild { + // a local_rebuild compiler already has stage1 features + cmd.arg("--cfg").arg("bootstrap"); + } builder.run( - cmd.env("RUSTC_BOOTSTRAP", "1") - .arg("--cfg") - .arg("bootstrap") - .arg("--target") + cmd.arg("--target") .arg(target.rustc_target_arg()) .arg("--emit=obj") .arg("-o") @@ -513,7 +517,9 @@ impl Step for Rustc { let compiler = self.compiler; let target = self.target; - if builder.config.download_rustc { + // NOTE: the ABI of the beta compiler is different from the ABI of the downloaded compiler, + // so its artifacts can't be reused. + if builder.config.download_rustc && compiler.stage != 0 { // Copy the existing artifacts instead of rebuilding them. // NOTE: this path is only taken for tools linking to rustc-dev. builder.ensure(Sysroot { compiler }); @@ -934,14 +940,15 @@ impl Step for Sysroot { t!(fs::create_dir_all(&sysroot)); // If we're downloading a compiler from CI, we can use the same compiler for all stages other than 0. - if builder.config.download_rustc { + if builder.config.download_rustc && compiler.stage != 0 { assert_eq!( builder.config.build, compiler.host, "Cross-compiling is not yet supported with `download-rustc`", ); // Copy the compiler into the correct sysroot. - let stage0_dir = builder.config.out.join(&*builder.config.build.triple).join("stage0"); - builder.cp_r(&stage0_dir, &sysroot); + let ci_rustc_dir = + builder.config.out.join(&*builder.config.build.triple).join("ci-rustc"); + builder.cp_r(&ci_rustc_dir, &sysroot); return INTERNER.intern_path(sysroot); } diff --git a/src/bootstrap/config.rs b/src/bootstrap/config.rs index b2af8e59b7c9f..483816b98d689 100644 --- a/src/bootstrap/config.rs +++ b/src/bootstrap/config.rs @@ -51,6 +51,7 @@ pub struct Config { pub submodules: bool, pub fast_submodules: bool, pub compiler_docs: bool, + pub docs_minification: bool, pub docs: bool, pub locked_deps: bool, pub vendor: bool, @@ -66,7 +67,7 @@ pub struct Config { pub rustc_error_format: Option, pub json_output: bool, pub test_compare_mode: bool, - pub llvm_libunwind: Option, + pub llvm_libunwind: LlvmLibunwind, pub color: Color, pub on_fail: Option, @@ -100,8 +101,8 @@ pub struct Config { pub llvm_link_jobs: Option, pub llvm_version_suffix: Option, pub llvm_use_linker: Option, - pub llvm_allow_old_toolchain: Option, - pub llvm_polly: Option, + pub llvm_allow_old_toolchain: bool, + pub llvm_polly: bool, pub llvm_from_ci: bool, pub use_lld: bool, @@ -148,7 +149,6 @@ pub struct Config { // dist misc pub dist_sign_folder: Option, pub dist_upload_addr: Option, - pub dist_gpg_password_file: Option, pub dist_compression_formats: Option>, // libstd features @@ -362,6 +362,7 @@ struct Build { rustfmt: Option, docs: Option, compiler_docs: Option, + docs_minification: Option, submodules: Option, fast_submodules: Option, gdb: Option, @@ -402,10 +403,6 @@ struct Install { libdir: Option, mandir: Option, datadir: Option, - - // standard paths, currently unused - infodir: Option, - localstatedir: Option, } /// TOML representation of how the LLVM build is configured. @@ -508,7 +505,8 @@ struct Rust { new_symbol_mangling: Option, profile_generate: Option, profile_use: Option, - download_rustc: Option, + // ignored; this is set from an env var set by bootstrap.py + download_rustc: Option, } /// TOML representation of how each build target is configured. @@ -557,14 +555,14 @@ impl Config { config.submodules = true; config.fast_submodules = true; config.docs = true; + config.docs_minification = true; config.rust_rpath = true; config.channel = "dev".to_string(); config.codegen_tests = true; - config.ignore_git = false; config.rust_dist_src = true; config.rust_codegen_backends = vec![INTERNER.intern_str("llvm")]; config.deny_warnings = true; - config.missing_tools = false; + config.bindir = "bin".into(); // set by build.rs config.build = TargetSelection::from_user(&env!("BUILD_TRIPLE")); @@ -594,7 +592,6 @@ impl Config { config.dry_run = flags.dry_run; config.keep_stage = flags.keep_stage; config.keep_stage_std = flags.keep_stage_std; - config.bindir = "bin".into(); // default config.color = flags.color; if let Some(value) = flags.deny_warnings { config.deny_warnings = value; @@ -663,6 +660,7 @@ impl Config { config.python = build.python.map(PathBuf::from); set(&mut config.low_priority, build.low_priority); set(&mut config.compiler_docs, build.compiler_docs); + set(&mut config.docs_minification, build.docs_minification); set(&mut config.docs, build.docs); set(&mut config.submodules, build.submodules); set(&mut config.fast_submodules, build.fast_submodules); @@ -683,51 +681,6 @@ impl Config { set(&mut config.print_step_timings, build.print_step_timings); set(&mut config.print_step_rusage, build.print_step_rusage); - // See https://github.com/rust-lang/compiler-team/issues/326 - config.stage = match config.cmd { - Subcommand::Check { .. } => flags.stage.or(build.check_stage).unwrap_or(0), - Subcommand::Doc { .. } => flags.stage.or(build.doc_stage).unwrap_or(0), - Subcommand::Build { .. } => flags.stage.or(build.build_stage).unwrap_or(1), - Subcommand::Test { .. } => flags.stage.or(build.test_stage).unwrap_or(1), - Subcommand::Bench { .. } => flags.stage.or(build.bench_stage).unwrap_or(2), - Subcommand::Dist { .. } => flags.stage.or(build.dist_stage).unwrap_or(2), - Subcommand::Install { .. } => flags.stage.or(build.install_stage).unwrap_or(2), - // These are all bootstrap tools, which don't depend on the compiler. - // The stage we pass shouldn't matter, but use 0 just in case. - Subcommand::Clean { .. } - | Subcommand::Clippy { .. } - | Subcommand::Fix { .. } - | Subcommand::Run { .. } - | Subcommand::Setup { .. } - | Subcommand::Format { .. } => flags.stage.unwrap_or(0), - }; - - // CI should always run stage 2 builds, unless it specifically states otherwise - #[cfg(not(test))] - if flags.stage.is_none() && crate::CiEnv::current() != crate::CiEnv::None { - match config.cmd { - Subcommand::Test { .. } - | Subcommand::Doc { .. } - | Subcommand::Build { .. } - | Subcommand::Bench { .. } - | Subcommand::Dist { .. } - | Subcommand::Install { .. } => { - assert_eq!( - config.stage, 2, - "x.py should be run with `--stage 2` on CI, but was run with `--stage {}`", - config.stage, - ); - } - Subcommand::Clean { .. } - | Subcommand::Check { .. } - | Subcommand::Clippy { .. } - | Subcommand::Fix { .. } - | Subcommand::Run { .. } - | Subcommand::Setup { .. } - | Subcommand::Format { .. } => {} - } - } - config.verbose = cmp::max(config.verbose, flags.verbose); if let Some(install) = toml.install { @@ -788,12 +741,25 @@ impl Config { config.llvm_ldflags = llvm.ldflags.clone(); set(&mut config.llvm_use_libcxx, llvm.use_libcxx); config.llvm_use_linker = llvm.use_linker.clone(); - config.llvm_allow_old_toolchain = llvm.allow_old_toolchain; - config.llvm_polly = llvm.polly; + config.llvm_allow_old_toolchain = llvm.allow_old_toolchain.unwrap_or(false); + config.llvm_polly = llvm.polly.unwrap_or(false); config.llvm_from_ci = match llvm.download_ci_llvm { Some(StringOrBool::String(s)) => { assert!(s == "if-available", "unknown option `{}` for download-ci-llvm", s); - config.build.triple == "x86_64-unknown-linux-gnu" + // This is currently all tier 1 targets (since others may not have CI artifacts) + // https://doc.rust-lang.org/rustc/platform-support.html#tier-1 + // FIXME: this is duplicated in bootstrap.py + let supported_platforms = [ + "aarch64-unknown-linux-gnu", + "i686-pc-windows-gnu", + "i686-pc-windows-msvc", + "i686-unknown-linux-gnu", + "x86_64-unknown-linux-gnu", + "x86_64-apple-darwin", + "x86_64-pc-windows-gnu", + "x86_64-pc-windows-msvc", + ]; + supported_platforms.contains(&&*config.build.triple) } Some(StringOrBool::Bool(b)) => b, None => false, @@ -814,7 +780,6 @@ impl Config { check_ci_llvm!(llvm.targets); check_ci_llvm!(llvm.experimental_targets); check_ci_llvm!(llvm.link_jobs); - check_ci_llvm!(llvm.link_shared); check_ci_llvm!(llvm.clang_cl); check_ci_llvm!(llvm.version_suffix); check_ci_llvm!(llvm.cflags); @@ -835,6 +800,11 @@ impl Config { // If we're building with ThinLTO on, we want to link to LLVM // shared, to avoid re-doing ThinLTO (which happens in the link // step) with each stage. + assert_ne!( + llvm.link_shared, + Some(false), + "setting link-shared=false is incompatible with thin-lto=true" + ); config.llvm_link_shared = true; } } @@ -860,7 +830,8 @@ impl Config { set(&mut config.test_compare_mode, rust.test_compare_mode); config.llvm_libunwind = rust .llvm_libunwind - .map(|v| v.parse().expect("failed to parse rust.llvm-libunwind")); + .map(|v| v.parse().expect("failed to parse rust.llvm-libunwind")) + .unwrap_or_default(); set(&mut config.backtrace, rust.backtrace); set(&mut config.channel, rust.channel); config.description = rust.description; @@ -893,7 +864,7 @@ impl Config { config.rust_codegen_units_std = rust.codegen_units_std.map(threads_from_config); config.rust_profile_use = flags.rust_profile_use.or(rust.profile_use); config.rust_profile_generate = flags.rust_profile_generate.or(rust.profile_generate); - config.download_rustc = rust.download_rustc.unwrap_or(false); + config.download_rustc = env::var("BOOTSTRAP_DOWNLOAD_RUSTC").as_deref() == Ok("1"); } else { config.rust_profile_use = flags.rust_profile_use; config.rust_profile_generate = flags.rust_profile_generate; @@ -948,7 +919,6 @@ impl Config { if let Some(t) = toml.dist { config.dist_sign_folder = t.sign_folder.map(PathBuf::from); - config.dist_gpg_password_file = t.gpg_password_file.map(PathBuf::from); config.dist_upload_addr = t.upload_addr; config.dist_compression_formats = t.compression_formats; set(&mut config.rust_dist_src, t.src_tarball); @@ -972,12 +942,8 @@ impl Config { // default values for all options that we haven't otherwise stored yet. config.llvm_skip_rebuild = llvm_skip_rebuild.unwrap_or(false); - - let default = false; - config.llvm_assertions = llvm_assertions.unwrap_or(default); - - let default = true; - config.rust_optimize = optimize.unwrap_or(default); + config.llvm_assertions = llvm_assertions.unwrap_or(false); + config.rust_optimize = optimize.unwrap_or(true); let default = debug == Some(true); config.rust_debug_assertions = debug_assertions.unwrap_or(default); @@ -1001,6 +967,59 @@ impl Config { let default = config.channel == "dev"; config.ignore_git = ignore_git.unwrap_or(default); + let download_rustc = config.download_rustc; + // See https://github.com/rust-lang/compiler-team/issues/326 + config.stage = match config.cmd { + Subcommand::Check { .. } => flags.stage.or(build.check_stage).unwrap_or(0), + // `download-rustc` only has a speed-up for stage2 builds. Default to stage2 unless explicitly overridden. + Subcommand::Doc { .. } => { + flags.stage.or(build.doc_stage).unwrap_or(if download_rustc { 2 } else { 0 }) + } + Subcommand::Build { .. } => { + flags.stage.or(build.build_stage).unwrap_or(if download_rustc { 2 } else { 1 }) + } + Subcommand::Test { .. } => { + flags.stage.or(build.test_stage).unwrap_or(if download_rustc { 2 } else { 1 }) + } + Subcommand::Bench { .. } => flags.stage.or(build.bench_stage).unwrap_or(2), + Subcommand::Dist { .. } => flags.stage.or(build.dist_stage).unwrap_or(2), + Subcommand::Install { .. } => flags.stage.or(build.install_stage).unwrap_or(2), + // These are all bootstrap tools, which don't depend on the compiler. + // The stage we pass shouldn't matter, but use 0 just in case. + Subcommand::Clean { .. } + | Subcommand::Clippy { .. } + | Subcommand::Fix { .. } + | Subcommand::Run { .. } + | Subcommand::Setup { .. } + | Subcommand::Format { .. } => flags.stage.unwrap_or(0), + }; + + // CI should always run stage 2 builds, unless it specifically states otherwise + #[cfg(not(test))] + if flags.stage.is_none() && crate::CiEnv::current() != crate::CiEnv::None { + match config.cmd { + Subcommand::Test { .. } + | Subcommand::Doc { .. } + | Subcommand::Build { .. } + | Subcommand::Bench { .. } + | Subcommand::Dist { .. } + | Subcommand::Install { .. } => { + assert_eq!( + config.stage, 2, + "x.py should be run with `--stage 2` on CI, but was run with `--stage {}`", + config.stage, + ); + } + Subcommand::Clean { .. } + | Subcommand::Check { .. } + | Subcommand::Clippy { .. } + | Subcommand::Fix { .. } + | Subcommand::Run { .. } + | Subcommand::Setup { .. } + | Subcommand::Format { .. } => {} + } + } + config } diff --git a/src/bootstrap/configure.py b/src/bootstrap/configure.py index 2e6e9142afe6c..999882a1c04b0 100755 --- a/src/bootstrap/configure.py +++ b/src/bootstrap/configure.py @@ -439,7 +439,12 @@ def configure_section(lines, config): lines[i] = "{} = {}".format(key, to_toml(value)) break if not found: - raise RuntimeError("failed to find config line for {}".format(key)) + # These are used by rpm, but aren't accepted by x.py. + # Give a warning that they're ignored, but not a hard error. + if key in ["infodir", "localstatedir"]: + print("warning: {} will be ignored".format(key)) + else: + raise RuntimeError("failed to find config line for {}".format(key)) for section_key in config: diff --git a/src/bootstrap/defaults/config.compiler.toml b/src/bootstrap/defaults/config.compiler.toml index 0ca928843d589..883bfead64e4a 100644 --- a/src/bootstrap/defaults/config.compiler.toml +++ b/src/bootstrap/defaults/config.compiler.toml @@ -8,6 +8,5 @@ debug-logging = true incremental = true [llvm] -# Will download LLVM from CI if available on your platform (Linux only for now) -# https://github.com/rust-lang/rust/issues/77084 tracks support for more platforms +# Will download LLVM from CI if available on your platform. download-ci-llvm = "if-available" diff --git a/src/bootstrap/defaults/config.tools.toml b/src/bootstrap/defaults/config.tools.toml new file mode 100644 index 0000000000000..182fb0fb0675c --- /dev/null +++ b/src/bootstrap/defaults/config.tools.toml @@ -0,0 +1,16 @@ +# These defaults are meant for contributors to tools which build on the +# compiler, but do not modify it directly. +[rust] +# This enables `RUSTC_LOG=debug`, avoiding confusing situations +# where adding `debug!()` appears to do nothing. +# However, it makes running the compiler slightly slower. +debug-logging = true +# This greatly increases the speed of rebuilds, especially when there are only minor changes. However, it makes the initial build slightly slower. +incremental = true +# Download rustc from CI instead of building it from source. +# This cuts compile times by almost 60x, but means you can't modify the compiler. +download-rustc = "if-unchanged" + +[llvm] +# Will download LLVM from CI if available on your platform. +download-ci-llvm = "if-available" diff --git a/src/bootstrap/dist.rs b/src/bootstrap/dist.rs index 802b5c99500cc..aee3c8324bc11 100644 --- a/src/bootstrap/dist.rs +++ b/src/bootstrap/dist.rs @@ -74,7 +74,7 @@ impl Step for Docs { let mut tarball = Tarball::new(builder, "rust-docs", &host.triple); tarball.set_product_name("Rust Documentation"); - tarball.add_dir(&builder.doc_out(host), dest); + tarball.add_bulk_dir(&builder.doc_out(host), dest); tarball.add_file(&builder.src.join("src/doc/robots.txt"), dest, 0o644); Some(tarball.generate()) } @@ -107,7 +107,7 @@ impl Step for RustcDocs { let mut tarball = Tarball::new(builder, "rustc-docs", &host.triple); tarball.set_product_name("Rustc Documentation"); - tarball.add_dir(&builder.compiler_doc_out(host), "share/doc/rust/html/rustc"); + tarball.add_bulk_dir(&builder.compiler_doc_out(host), "share/doc/rust/html/rustc"); Some(tarball.generate()) } } @@ -1246,6 +1246,56 @@ impl Step for Rustfmt { } } +#[derive(Debug, PartialOrd, Ord, Copy, Clone, Hash, PartialEq, Eq)] +pub struct RustDemangler { + pub compiler: Compiler, + pub target: TargetSelection, +} + +impl Step for RustDemangler { + type Output = Option; + const ONLY_HOSTS: bool = true; + + fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { + run.path("rust-demangler") + } + + fn make_run(run: RunConfig<'_>) { + run.builder.ensure(RustDemangler { + compiler: run.builder.compiler_for( + run.builder.top_stage, + run.builder.config.build, + run.target, + ), + target: run.target, + }); + } + + fn run(self, builder: &Builder<'_>) -> Option { + let compiler = self.compiler; + let target = self.target; + assert!(builder.config.extended); + + // Only build this extended tool if explicitly included in `tools`, or if `profiler = true` + let profiler = builder.config.profiler_enabled(target); + if !builder.config.tools.as_ref().map_or(profiler, |t| t.contains("rust-demangler")) { + return None; + } + + let rust_demangler = builder + .ensure(tool::RustDemangler { compiler, target, extra_features: Vec::new() }) + .expect("rust-demangler expected to build - in-tree tool"); + + // Prepare the image directory + let mut tarball = Tarball::new(builder, "rust-demangler", &target.triple); + tarball.set_overlay(OverlayKind::RustDemangler); + tarball.is_preview(true); + tarball.add_file(&rust_demangler, "bin", 0o755); + tarball.add_legal_and_readme_to("share/doc/rust-demangler"); + Some(tarball.generate()) + } +} + #[derive(Debug, PartialOrd, Ord, Copy, Clone, Hash, PartialEq, Eq)] pub struct Extended { stage: u32, @@ -1282,6 +1332,7 @@ impl Step for Extended { let rustc_installer = builder.ensure(Rustc { compiler: builder.compiler(stage, target) }); let cargo_installer = builder.ensure(Cargo { compiler, target }); let rustfmt_installer = builder.ensure(Rustfmt { compiler, target }); + let rust_demangler_installer = builder.ensure(RustDemangler { compiler, target }); let rls_installer = builder.ensure(Rls { compiler, target }); let rust_analyzer_installer = builder.ensure(RustAnalyzer { compiler, target }); let llvm_tools_installer = builder.ensure(LlvmTools { target }); @@ -1307,9 +1358,10 @@ impl Step for Extended { let mut tarballs = Vec::new(); tarballs.push(rustc_installer); tarballs.push(cargo_installer); + tarballs.push(clippy_installer); + tarballs.extend(rust_demangler_installer.clone()); tarballs.extend(rls_installer.clone()); tarballs.extend(rust_analyzer_installer.clone()); - tarballs.push(clippy_installer); tarballs.extend(miri_installer.clone()); tarballs.extend(rustfmt_installer.clone()); tarballs.extend(llvm_tools_installer); @@ -1366,6 +1418,9 @@ impl Step for Extended { let xform = |p: &Path| { let mut contents = t!(fs::read_to_string(p)); + if rust_demangler_installer.is_none() { + contents = filter(&contents, "rust-demangler"); + } if rls_installer.is_none() { contents = filter(&contents, "rls"); } @@ -1414,7 +1469,9 @@ impl Step for Extended { prepare("rust-std"); prepare("rust-analysis"); prepare("clippy"); - + if rust_demangler_installer.is_some() { + prepare("rust-demangler"); + } if rls_installer.is_some() { prepare("rls"); } @@ -1462,6 +1519,8 @@ impl Step for Extended { "rust-analyzer-preview".to_string() } else if name == "clippy" { "clippy-preview".to_string() + } else if name == "rust-demangler" { + "rust-demangler-preview".to_string() } else if name == "miri" { "miri-preview".to_string() } else { @@ -1479,6 +1538,9 @@ impl Step for Extended { prepare("rust-docs"); prepare("rust-std"); prepare("clippy"); + if rust_demangler_installer.is_some() { + prepare("rust-demangler"); + } if rls_installer.is_some() { prepare("rls"); } @@ -1620,6 +1682,25 @@ impl Step for Extended { .arg("-t") .arg(etc.join("msi/remove-duplicates.xsl")), ); + if rust_demangler_installer.is_some() { + builder.run( + Command::new(&heat) + .current_dir(&exe) + .arg("dir") + .arg("rust-demangler") + .args(&heat_flags) + .arg("-cg") + .arg("RustDemanglerGroup") + .arg("-dr") + .arg("RustDemangler") + .arg("-var") + .arg("var.RustDemanglerDir") + .arg("-out") + .arg(exe.join("RustDemanglerGroup.wxs")) + .arg("-t") + .arg(etc.join("msi/remove-duplicates.xsl")), + ); + } if miri_installer.is_some() { builder.run( Command::new(&heat) @@ -1693,6 +1774,9 @@ impl Step for Extended { .arg(&input); add_env(builder, &mut cmd, target); + if rust_demangler_installer.is_some() { + cmd.arg("-dRustDemanglerDir=rust-demangler"); + } if rls_installer.is_some() { cmd.arg("-dRlsDir=rls"); } @@ -1715,6 +1799,9 @@ impl Step for Extended { candle("CargoGroup.wxs".as_ref()); candle("StdGroup.wxs".as_ref()); candle("ClippyGroup.wxs".as_ref()); + if rust_demangler_installer.is_some() { + candle("RustDemanglerGroup.wxs".as_ref()); + } if rls_installer.is_some() { candle("RlsGroup.wxs".as_ref()); } @@ -1761,6 +1848,9 @@ impl Step for Extended { if rust_analyzer_installer.is_some() { cmd.arg("RustAnalyzerGroup.wixobj"); } + if rust_demangler_installer.is_some() { + cmd.arg("RustDemanglerGroup.wixobj"); + } if miri_installer.is_some() { cmd.arg("MiriGroup.wixobj"); } diff --git a/src/bootstrap/doc.rs b/src/bootstrap/doc.rs index 30d690c970507..a32b92ef1af83 100644 --- a/src/bootstrap/doc.rs +++ b/src/bootstrap/doc.rs @@ -270,6 +270,10 @@ fn invoke_rustdoc( .arg("--markdown-css") .arg("../rust.css"); + if !builder.config.docs_minification { + cmd.arg("-Z").arg("unstable-options").arg("--disable-minification"); + } + builder.run(&mut cmd); } @@ -365,6 +369,10 @@ impl Step for Standalone { .arg(&out) .arg(&path); + if !builder.config.docs_minification { + cmd.arg("--disable-minification"); + } + if filename == "not_found.md" { cmd.arg("--markdown-css").arg("https://doc.rust-lang.org/rust.css"); } else { @@ -437,6 +445,10 @@ impl Step for Std { .arg("--index-page") .arg(&builder.src.join("src/doc/index.md")); + if !builder.config.docs_minification { + cargo.arg("--disable-minification"); + } + builder.run(&mut cargo.into()); }; // Only build the following crates. While we could just iterate over the @@ -458,12 +470,16 @@ impl Step for Std { // Look for library/std, library/core etc in the `x.py doc` arguments and // open the corresponding rendered docs. for path in builder.paths.iter().map(components_simplified) { - if path.get(0) == Some(&"library") { - let requested_crate = &path[1]; - if krates.contains(&requested_crate) { - let index = out.join(requested_crate).join("index.html"); - open(builder, &index); - } + let requested_crate = if path.get(0) == Some(&"library") { + &path[1] + } else if !path.is_empty() { + &path[0] + } else { + continue; + }; + if krates.contains(&requested_crate) { + let index = out.join(requested_crate).join("index.html"); + open(builder, &index); } } } @@ -528,6 +544,8 @@ impl Step for Rustc { // Build cargo command. let mut cargo = builder.cargo(compiler, Mode::Rustc, SourceType::InTree, target, "doc"); cargo.rustdocflag("--document-private-items"); + // Since we always pass --document-private-items, there's no need to warn about linking to private items. + cargo.rustdocflag("-Arustdoc::private-intra-doc-links"); cargo.rustdocflag("--enable-index-page"); cargo.rustdocflag("-Zunstable-options"); cargo.rustdocflag("-Znormalize-docs"); diff --git a/src/bootstrap/install.rs b/src/bootstrap/install.rs index b427420d57795..13ee909afd5e4 100644 --- a/src/bootstrap/install.rs +++ b/src/bootstrap/install.rs @@ -17,6 +17,11 @@ use crate::Compiler; use crate::builder::{Builder, RunConfig, ShouldRun, Step}; use crate::config::{Config, TargetSelection}; +#[cfg(target_os = "illumos")] +const SHELL: &str = "bash"; +#[cfg(not(target_os = "illumos"))] +const SHELL: &str = "sh"; + fn install_sh( builder: &Builder<'_>, package: &str, @@ -37,7 +42,7 @@ fn install_sh( let empty_dir = builder.out.join("tmp/empty_dir"); t!(fs::create_dir_all(&empty_dir)); - let mut cmd = Command::new("sh"); + let mut cmd = Command::new(SHELL); cmd.current_dir(&empty_dir) .arg(sanitize_sh(&tarball.decompressed_output().join("install.sh"))) .arg(format!("--prefix={}", prepare_dir(prefix))) @@ -190,6 +195,22 @@ install!((self, builder, _config), ); } }; + RustDemangler, "rust-demangler", Self::should_build(_config), only_hosts: true, { + // Note: Even though `should_build` may return true for `extended` default tools, + // dist::RustDemangler may still return None, unless the target-dependent `profiler` config + // is also true, or the `tools` array explicitly includes "rust-demangler". + if let Some(tarball) = builder.ensure(dist::RustDemangler { + compiler: self.compiler, + target: self.target + }) { + install_sh(builder, "rust-demangler", self.compiler.stage, Some(self.target), &tarball); + } else { + builder.info( + &format!("skipping Install RustDemangler stage{} ({})", + self.compiler.stage, self.target), + ); + } + }; Analysis, "analysis", Self::should_build(_config), only_hosts: false, { let tarball = builder.ensure(dist::Analysis { // Find the actual compiler (handling the full bootstrap option) which diff --git a/src/bootstrap/lib.rs b/src/bootstrap/lib.rs index 5d708d3b25c12..24da44b933abc 100644 --- a/src/bootstrap/lib.rs +++ b/src/bootstrap/lib.rs @@ -547,7 +547,7 @@ impl Build { fn std_features(&self, target: TargetSelection) -> String { let mut features = "panic-unwind".to_string(); - match self.config.llvm_libunwind.unwrap_or_default() { + match self.config.llvm_libunwind { LlvmLibunwind::InTree => features.push_str(" llvm-libunwind"), LlvmLibunwind::System => features.push_str(" system-llvm-libunwind"), LlvmLibunwind::No => {} diff --git a/src/bootstrap/native.rs b/src/bootstrap/native.rs index fb5127bac54df..bde0a96f03013 100644 --- a/src/bootstrap/native.rs +++ b/src/bootstrap/native.rs @@ -256,7 +256,7 @@ impl Step for Llvm { enabled_llvm_projects.push("compiler-rt"); } - if let Some(true) = builder.config.llvm_polly { + if builder.config.llvm_polly { enabled_llvm_projects.push("polly"); } @@ -311,7 +311,7 @@ impl Step for Llvm { cfg.define("LLVM_USE_LINKER", linker); } - if let Some(true) = builder.config.llvm_allow_old_toolchain { + if builder.config.llvm_allow_old_toolchain { cfg.define("LLVM_TEMPORARILY_ALLOW_OLD_TOOLCHAIN", "YES"); } @@ -347,11 +347,11 @@ fn check_llvm_version(builder: &Builder<'_>, llvm_config: &Path) { let version = output(cmd.arg("--version")); let mut parts = version.split('.').take(2).filter_map(|s| s.parse::().ok()); if let (Some(major), Some(_minor)) = (parts.next(), parts.next()) { - if major >= 9 { + if major >= 10 { return; } } - panic!("\n\nbad LLVM version: {}, need >=9.0\n\n", version) + panic!("\n\nbad LLVM version: {}, need >=10.0\n\n", version) } fn configure_cmake( @@ -814,6 +814,9 @@ fn supported_sanitizers( "x86_64-unknown-linux-gnu" => { common_libs("linux", "x86_64", &["asan", "lsan", "msan", "tsan"]) } + "x86_64-unknown-linux-musl" => { + common_libs("linux", "x86_64", &["asan", "lsan", "msan", "tsan"]) + } _ => Vec::new(), } } diff --git a/src/bootstrap/setup.rs b/src/bootstrap/setup.rs index 725147767dbd1..a5829dfa9d879 100644 --- a/src/bootstrap/setup.rs +++ b/src/bootstrap/setup.rs @@ -13,6 +13,7 @@ pub enum Profile { Compiler, Codegen, Library, + Tools, User, } @@ -24,15 +25,16 @@ impl Profile { pub fn all() -> impl Iterator { use Profile::*; // N.B. these are ordered by how they are displayed, not alphabetically - [Library, Compiler, Codegen, User].iter().copied() + [Library, Compiler, Codegen, Tools, User].iter().copied() } pub fn purpose(&self) -> String { use Profile::*; match self { Library => "Contribute to the standard library", - Compiler => "Contribute to the compiler or rustdoc", + Compiler => "Contribute to the compiler itself", Codegen => "Contribute to the compiler, and also modify LLVM or codegen", + Tools => "Contribute to tools which depend on the compiler, but do not modify it directly (e.g. rustdoc, clippy, miri)", User => "Install Rust from source", } .to_string() @@ -53,9 +55,12 @@ impl FromStr for Profile { fn from_str(s: &str) -> Result { match s { "lib" | "library" => Ok(Profile::Library), - "compiler" | "rustdoc" => Ok(Profile::Compiler), + "compiler" => Ok(Profile::Compiler), "llvm" | "codegen" => Ok(Profile::Codegen), "maintainer" | "user" => Ok(Profile::User), + "tools" | "tool" | "rustdoc" | "clippy" | "miri" | "rustfmt" | "rls" => { + Ok(Profile::Tools) + } _ => Err(format!("unknown profile: '{}'", s)), } } @@ -68,6 +73,7 @@ impl fmt::Display for Profile { Profile::Codegen => write!(f, "codegen"), Profile::Library => write!(f, "library"), Profile::User => write!(f, "user"), + Profile::Tools => write!(f, "tools"), } } } @@ -103,6 +109,14 @@ pub fn setup(src_path: &Path, profile: Profile) { let suggestions = match profile { Profile::Codegen | Profile::Compiler => &["check", "build", "test"][..], + Profile::Tools => &[ + "check", + "build", + "test src/test/rustdoc*", + "test src/tools/clippy", + "test src/tools/miri", + "test src/tools/rustfmt", + ], Profile::Library => &["check", "build", "test library/std", "doc"], Profile::User => &["dist", "build"], }; diff --git a/src/bootstrap/tarball.rs b/src/bootstrap/tarball.rs index 7fb03056f1bd3..9ff5c2327e0f7 100644 --- a/src/bootstrap/tarball.rs +++ b/src/bootstrap/tarball.rs @@ -15,6 +15,7 @@ pub(crate) enum OverlayKind { Clippy, Miri, Rustfmt, + RustDemangler, RLS, RustAnalyzer, } @@ -47,6 +48,9 @@ impl OverlayKind { "src/tools/rustfmt/LICENSE-APACHE", "src/tools/rustfmt/LICENSE-MIT", ], + OverlayKind::RustDemangler => { + &["src/tools/rust-demangler/README.md", "LICENSE-APACHE", "LICENSE-MIT"] + } OverlayKind::RLS => &[ "src/tools/rls/README.md", "src/tools/rls/LICENSE-APACHE", @@ -64,6 +68,7 @@ impl OverlayKind { match self { OverlayKind::Rust => builder.rust_version(), OverlayKind::LLVM => builder.rust_version(), + OverlayKind::RustDemangler => builder.release_num("rust-demangler"), OverlayKind::Cargo => { builder.cargo_info.version(builder, &builder.release_num("cargo")) } @@ -94,6 +99,7 @@ pub(crate) struct Tarball<'a> { temp_dir: PathBuf, image_dir: PathBuf, overlay_dir: PathBuf, + bulk_dirs: Vec, include_target_in_component_name: bool, is_preview: bool, @@ -132,6 +138,7 @@ impl<'a> Tarball<'a> { temp_dir, image_dir, overlay_dir, + bulk_dirs: Vec::new(), include_target_in_component_name: false, is_preview: false, @@ -196,6 +203,11 @@ impl<'a> Tarball<'a> { self.builder.cp_r(src.as_ref(), &dest); } + pub(crate) fn add_bulk_dir(&mut self, src: impl AsRef, dest: impl AsRef) { + self.bulk_dirs.push(dest.as_ref().to_path_buf()); + self.add_dir(src, dest); + } + pub(crate) fn generate(self) -> GeneratedTarball { let mut component_name = self.component.clone(); if self.is_preview { @@ -216,6 +228,16 @@ impl<'a> Tarball<'a> { .arg("--image-dir") .arg(&this.image_dir) .arg(format!("--component-name={}", &component_name)); + + if let Some((dir, dirs)) = this.bulk_dirs.split_first() { + let mut arg = dir.as_os_str().to_os_string(); + for dir in dirs { + arg.push(","); + arg.push(dir); + } + cmd.arg("--bulk-dirs").arg(&arg); + } + this.non_bare_args(cmd); }) } diff --git a/src/bootstrap/test.rs b/src/bootstrap/test.rs index c0cd24dd81f01..b9d7ecf8c0eb1 100644 --- a/src/bootstrap/test.rs +++ b/src/bootstrap/test.rs @@ -108,6 +108,19 @@ impl Step for Linkcheck { /// documentation to ensure we don't have a bunch of dead ones. fn run(self, builder: &Builder<'_>) { let host = self.host; + let hosts = &builder.hosts; + let targets = &builder.targets; + + // if we have different hosts and targets, some things may be built for + // the host (e.g. rustc) and others for the target (e.g. std). The + // documentation built for each will contain broken links to + // docs built for the other platform (e.g. rustc linking to cargo) + if (hosts != targets) && !hosts.is_empty() && !targets.is_empty() { + panic!( + "Linkcheck currently does not support builds with different hosts and targets. +You can skip linkcheck with --exclude src/tools/linkchecker" + ); + } builder.info(&format!("Linkcheck ({})", host)); @@ -122,7 +135,8 @@ impl Step for Linkcheck { fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { let builder = run.builder; - run.path("src/tools/linkchecker").default_condition(builder.config.docs) + let run = run.path("src/tools/linkchecker"); + run.default_condition(builder.config.docs) } fn make_run(run: RunConfig<'_>) { @@ -337,6 +351,57 @@ impl Step for Rustfmt { } } +#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] +pub struct RustDemangler { + stage: u32, + host: TargetSelection, +} + +impl Step for RustDemangler { + type Output = (); + const ONLY_HOSTS: bool = true; + + fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { + run.path("src/tools/rust-demangler") + } + + fn make_run(run: RunConfig<'_>) { + run.builder.ensure(RustDemangler { stage: run.builder.top_stage, host: run.target }); + } + + /// Runs `cargo test` for rust-demangler. + fn run(self, builder: &Builder<'_>) { + let stage = self.stage; + let host = self.host; + let compiler = builder.compiler(stage, host); + + let rust_demangler = builder + .ensure(tool::RustDemangler { compiler, target: self.host, extra_features: Vec::new() }) + .expect("in-tree tool"); + let mut cargo = tool::prepare_tool_cargo( + builder, + compiler, + Mode::ToolRustc, + host, + "test", + "src/tools/rust-demangler", + SourceType::InTree, + &[], + ); + + let dir = testdir(builder, compiler.host); + t!(fs::create_dir_all(&dir)); + + cargo.env("RUST_DEMANGLER_DRIVER_PATH", rust_demangler); + + cargo.arg("--").args(builder.config.cmd.test_args()); + + cargo.add_rustc_lib_path(builder, compiler); + + builder.run(&mut cargo.into()); + } +} + #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] pub struct Miri { stage: u32, @@ -452,7 +517,14 @@ impl Step for Miri { cargo.add_rustc_lib_path(builder, compiler); - if !try_run(builder, &mut cargo.into()) { + let mut cargo = Command::from(cargo); + if !try_run(builder, &mut cargo) { + return; + } + + // # Run `cargo test` with `-Zmir-opt-level=4`. + cargo.env("MIRIFLAGS", "-O -Zmir-opt-level=4"); + if !try_run(builder, &mut cargo) { return; } @@ -782,6 +854,7 @@ impl Step for Tidy { cmd.arg(&builder.src); cmd.arg(&builder.initial_cargo); cmd.arg(&builder.out); + cmd.arg(builder.jobs().to_string()); if builder.is_verbose() { cmd.arg("--verbose"); } @@ -791,6 +864,19 @@ impl Step for Tidy { if builder.config.channel == "dev" || builder.config.channel == "nightly" { builder.info("fmt check"); + if builder.config.initial_rustfmt.is_none() { + let inferred_rustfmt_dir = builder.config.initial_rustc.parent().unwrap(); + eprintln!( + "\ +error: no `rustfmt` binary found in {PATH} +info: `rust.channel` is currently set to \"{CHAN}\" +help: if you are testing a beta branch, set `rust.channel` to \"beta\" in the `config.toml` file +help: to skip test's attempt to check tidiness, pass `--exclude src/tools/tidy` to `x.py test`", + PATH = inferred_rustfmt_dir.display(), + CHAN = builder.config.channel, + ); + std::process::exit(1); + } crate::format::format(&builder.build, !builder.config.cmd.bless()); } } @@ -1076,7 +1162,7 @@ note: if you're sure you want to do this, please open an issue as to why. In the // Avoid depending on rustdoc when we don't need it. if mode == "rustdoc" - || (mode == "run-make" && suite.ends_with("fulldeps")) + || mode == "run-make" || (mode == "ui" && is_rustdoc) || mode == "js-doc-test" || mode == "rustdoc-json" @@ -1092,7 +1178,10 @@ note: if you're sure you want to do this, please open an issue as to why. In the } if mode == "run-make" && suite.ends_with("fulldeps") { - cmd.arg("--rust-demangler-path").arg(builder.tool_exe(Tool::RustDemangler)); + let rust_demangler = builder + .ensure(tool::RustDemangler { compiler, target, extra_features: Vec::new() }) + .expect("in-tree tool"); + cmd.arg("--rust-demangler-path").arg(rust_demangler); } cmd.arg("--src-base").arg(builder.src.join("src/test").join(suite)); @@ -1143,6 +1232,7 @@ note: if you're sure you want to do this, please open an issue as to why. In the hostflags.push(format!("-Lnative={}", builder.test_helpers_out(compiler.host).display())); if builder.is_fuse_ld_lld(compiler.host) { hostflags.push("-Clink-args=-fuse-ld=lld".to_string()); + hostflags.push("-Clink-arg=-Wl,--threads=1".to_string()); } cmd.arg("--host-rustcflags").arg(hostflags.join(" ")); @@ -1150,6 +1240,7 @@ note: if you're sure you want to do this, please open an issue as to why. In the targetflags.push(format!("-Lnative={}", builder.test_helpers_out(target).display())); if builder.is_fuse_ld_lld(target) { targetflags.push("-Clink-args=-fuse-ld=lld".to_string()); + targetflags.push("-Clink-arg=-Wl,--threads=1".to_string()); } cmd.arg("--target-rustcflags").arg(targetflags.join(" ")); @@ -1922,6 +2013,77 @@ impl Step for CrateRustdoc { } } +#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] +pub struct CrateRustdocJsonTypes { + host: TargetSelection, + test_kind: TestKind, +} + +impl Step for CrateRustdocJsonTypes { + type Output = (); + const DEFAULT: bool = true; + const ONLY_HOSTS: bool = true; + + fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { + run.path("src/rustdoc-json-types") + } + + fn make_run(run: RunConfig<'_>) { + let builder = run.builder; + + let test_kind = builder.kind.into(); + + builder.ensure(CrateRustdocJsonTypes { host: run.target, test_kind }); + } + + fn run(self, builder: &Builder<'_>) { + let test_kind = self.test_kind; + let target = self.host; + + // Use the previous stage compiler to reuse the artifacts that are + // created when running compiletest for src/test/rustdoc. If this used + // `compiler`, then it would cause rustdoc to be built *again*, which + // isn't really necessary. + let compiler = builder.compiler_for(builder.top_stage, target, target); + builder.ensure(compile::Rustc { compiler, target }); + + let mut cargo = tool::prepare_tool_cargo( + builder, + compiler, + Mode::ToolRustc, + target, + test_kind.subcommand(), + "src/rustdoc-json-types", + SourceType::InTree, + &[], + ); + if test_kind.subcommand() == "test" && !builder.fail_fast { + cargo.arg("--no-fail-fast"); + } + + cargo.arg("-p").arg("rustdoc-json-types"); + + cargo.arg("--"); + cargo.args(&builder.config.cmd.test_args()); + + if self.host.contains("musl") { + cargo.arg("'-Ctarget-feature=-crt-static'"); + } + + if !builder.config.verbose_tests { + cargo.arg("--quiet"); + } + + builder.info(&format!( + "{} rustdoc-json-types stage{} ({} -> {})", + test_kind, compiler.stage, &compiler.host, target + )); + let _time = util::timeit(&builder); + + try_run(builder, &mut cargo.into()); + } +} + /// Some test suites are run inside emulators or on remote devices, and most /// of our test binaries are linked dynamically which means we need to ship /// the standard library and such to the emulator ahead of time. This step diff --git a/src/bootstrap/tool.rs b/src/bootstrap/tool.rs index 3fc3b68fd8681..e85f4628fb03a 100644 --- a/src/bootstrap/tool.rs +++ b/src/bootstrap/tool.rs @@ -368,7 +368,6 @@ bootstrap_tool!( Compiletest, "src/tools/compiletest", "compiletest", is_unstable_tool = true; BuildManifest, "src/tools/build-manifest", "build-manifest"; RemoteTestClient, "src/tools/remote-test-client", "remote-test-client"; - RustDemangler, "src/tools/rust-demangler", "rust-demangler"; RustInstaller, "src/tools/rust-installer", "fabricate", is_external_tool = true; RustdocTheme, "src/tools/rustdoc-themes", "rustdoc-themes"; ExpandYamlAnchors, "src/tools/expand-yaml-anchors", "expand-yaml-anchors"; @@ -514,12 +513,30 @@ impl Step for Rustdoc { // rustc compiler it's paired with, so it must be built with the previous stage compiler. let build_compiler = builder.compiler(target_compiler.stage - 1, builder.config.build); + // When using `download-rustc` and a stage0 build_compiler, copying rustc doesn't actually + // build stage0 libstd (because the libstd in sysroot has the wrong ABI). Explicitly build + // it. + builder.ensure(compile::Std { compiler: build_compiler, target: target_compiler.host }); + builder.ensure(compile::Rustc { compiler: build_compiler, target: target_compiler.host }); + // NOTE: this implies that `download-rustc` is pretty useless when compiling with the stage0 + // compiler, since you do just as much work. + if !builder.config.dry_run && builder.config.download_rustc && build_compiler.stage == 0 { + println!( + "warning: `download-rustc` does nothing when building stage1 tools; consider using `--stage 2` instead" + ); + } + // The presence of `target_compiler` ensures that the necessary libraries (codegen backends, // compiler libraries, ...) are built. Rustdoc does not require the presence of any // libraries within sysroot_libdir (i.e., rustlib), though doctests may want it (since // they'll be linked to those libraries). As such, don't explicitly `ensure` any additional // libraries here. The intuition here is that If we've built a compiler, we should be able // to build rustdoc. + // + let mut features = Vec::new(); + if builder.config.jemalloc { + features.push("jemalloc".to_string()); + } let cargo = prepare_tool_cargo( builder, @@ -529,7 +546,7 @@ impl Step for Rustdoc { "build", "src/tools/rustdoc", SourceType::InTree, - &[], + features.as_slice(), ); builder.info(&format!( @@ -719,6 +736,7 @@ tool_extended!((self, builder), }); self.extra_features.push("clippy".to_owned()); }; + RustDemangler, rust_demangler, "src/tools/rust-demangler", "rust-demangler", stable=false, in_tree=true, {}; Rustfmt, rustfmt, "src/tools/rustfmt", "rustfmt", stable=true, {}; RustAnalyzer, rust_analyzer, "src/tools/rust-analyzer/crates/rust-analyzer", "rust-analyzer", stable=false, {}; ); diff --git a/src/ci/docker/host-x86_64/disabled/riscv64gc-linux/Dockerfile b/src/ci/docker/host-x86_64/disabled/riscv64gc-linux/Dockerfile index f3f52ed61d133..4377608700b0c 100644 --- a/src/ci/docker/host-x86_64/disabled/riscv64gc-linux/Dockerfile +++ b/src/ci/docker/host-x86_64/disabled/riscv64gc-linux/Dockerfile @@ -98,6 +98,6 @@ COPY scripts/sccache.sh /scripts/ RUN sh /scripts/sccache.sh ENV RUST_CONFIGURE_ARGS --qemu-riscv64-rootfs=/tmp/rootfs -ENV SCRIPT python3 ../x.py --stage 2 test --target riscv64gc-unknown-linux-gnu +ENV SCRIPT python3 ../x.py --stage 2 test --host='' --target riscv64gc-unknown-linux-gnu ENV NO_CHANGE_USER=1 diff --git a/src/ci/docker/host-x86_64/dist-android/Dockerfile b/src/ci/docker/host-x86_64/dist-android/Dockerfile index ee727359f3961..2f0496d7dd4ba 100644 --- a/src/ci/docker/host-x86_64/dist-android/Dockerfile +++ b/src/ci/docker/host-x86_64/dist-android/Dockerfile @@ -9,6 +9,8 @@ RUN . /scripts/android-ndk.sh && \ download_ndk android-ndk-r15c-linux-x86_64.zip && \ make_standalone_toolchain arm 14 && \ make_standalone_toolchain x86 14 && \ + make_standalone_toolchain arm 21 && \ + make_standalone_toolchain x86 21 && \ make_standalone_toolchain arm64 21 && \ make_standalone_toolchain x86_64 21 && \ remove_ndk diff --git a/src/ci/docker/host-x86_64/dist-riscv64-linux/riscv64-unknown-linux-gnu.config b/src/ci/docker/host-x86_64/dist-riscv64-linux/riscv64-unknown-linux-gnu.config index dbb4be550dd70..7124ee3aeb53b 100644 --- a/src/ci/docker/host-x86_64/dist-riscv64-linux/riscv64-unknown-linux-gnu.config +++ b/src/ci/docker/host-x86_64/dist-riscv64-linux/riscv64-unknown-linux-gnu.config @@ -644,7 +644,7 @@ CT_EXPAT_PATCH_GLOBAL=y CT_EXPAT_PATCH_ORDER="global" CT_EXPAT_V_2_2=y # CT_EXPAT_NO_VERSIONS is not set -CT_EXPAT_VERSION="2.2.6" +CT_EXPAT_VERSION="2.3.0" CT_EXPAT_MIRRORS="http://downloads.sourceforge.net/project/expat/expat/${CT_EXPAT_VERSION}" CT_EXPAT_ARCHIVE_FILENAME="@{pkg_name}-@{version}" CT_EXPAT_ARCHIVE_DIRNAME="@{pkg_name}-@{version}" diff --git a/src/ci/docker/host-x86_64/dist-various-2/Dockerfile b/src/ci/docker/host-x86_64/dist-various-2/Dockerfile index 7bb3cb1451688..de3a99f34fcfa 100644 --- a/src/ci/docker/host-x86_64/dist-various-2/Dockerfile +++ b/src/ci/docker/host-x86_64/dist-various-2/Dockerfile @@ -45,10 +45,10 @@ ENV \ CC_armv7_unknown_linux_gnueabi=arm-linux-gnueabi-gcc-8 \ CXX_armv7_unknown_linux_gnueabi=arm-linux-gnueabi-g++-8 \ AR_x86_64_fortanix_unknown_sgx=ar \ - CC_x86_64_fortanix_unknown_sgx=x86_64-fortanix-unknown-sgx-clang-11 \ - CFLAGS_x86_64_fortanix_unknown_sgx="-mlvi-hardening -mllvm -x86-experimental-lvi-inline-asm-hardening" \ - CXX_x86_64_fortanix_unknown_sgx=x86_64-fortanix-unknown-sgx-clang++-11 \ - CXXFLAGS_x86_64_fortanix_unknown_sgx="-mlvi-hardening -mllvm -x86-experimental-lvi-inline-asm-hardening" \ + CC_x86_64_fortanix_unknown_sgx=clang-11 \ + CFLAGS_x86_64_fortanix_unknown_sgx="-D__ELF__ -isystem/usr/include/x86_64-linux-gnu -mlvi-hardening -mllvm -x86-experimental-lvi-inline-asm-hardening" \ + CXX_x86_64_fortanix_unknown_sgx=clang++-11 \ + CXXFLAGS_x86_64_fortanix_unknown_sgx="-D__ELF__ -isystem/usr/include/x86_64-linux-gnu -mlvi-hardening -mllvm -x86-experimental-lvi-inline-asm-hardening" \ AR_i686_unknown_freebsd=i686-unknown-freebsd11-ar \ CC_i686_unknown_freebsd=i686-unknown-freebsd11-clang \ CXX_i686_unknown_freebsd=i686-unknown-freebsd11-clang++ \ @@ -71,8 +71,6 @@ COPY host-x86_64/dist-various-2/build-solaris-toolchain.sh /tmp/ RUN /tmp/build-solaris-toolchain.sh x86_64 amd64 solaris-i386 RUN /tmp/build-solaris-toolchain.sh sparcv9 sparcv9 solaris-sparc COPY host-x86_64/dist-various-2/build-x86_64-fortanix-unknown-sgx-toolchain.sh /tmp/ -COPY host-x86_64/dist-various-2/x86_64-fortanix-unknown-sgx-clang-wrap.sh /usr/bin/x86_64-fortanix-unknown-sgx-clang-11 -RUN ln -s /usr/bin/x86_64-fortanix-unknown-sgx-clang-11 /usr/bin/x86_64-fortanix-unknown-sgx-clang++-11 RUN /tmp/build-x86_64-fortanix-unknown-sgx-toolchain.sh COPY host-x86_64/dist-various-2/build-wasi-toolchain.sh /tmp/ diff --git a/src/ci/docker/host-x86_64/dist-various-2/x86_64-fortanix-unknown-sgx-clang-wrap.sh b/src/ci/docker/host-x86_64/dist-various-2/x86_64-fortanix-unknown-sgx-clang-wrap.sh deleted file mode 100755 index c4ff44c37b1e3..0000000000000 --- a/src/ci/docker/host-x86_64/dist-various-2/x86_64-fortanix-unknown-sgx-clang-wrap.sh +++ /dev/null @@ -1,14 +0,0 @@ -#!/bin/bash - -args=("$@") - -for i in "${!args[@]}"; do - # x86_64-fortanix-unknown-sgx doesn't have a C sysroot for things like - # stdint.h and the C++ STL. Unlike GCC, clang will not use the host's - # sysroot instead. Force it. - if [ "${args[$i]}" = "--target=x86_64-fortanix-unknown-sgx" ]; then - args[$i]="--target=x86_64-unknown-linux-gnu" - fi -done - -exec "${0/x86_64-fortanix-unknown-sgx-clang/clang}" "${args[@]}" diff --git a/src/ci/docker/host-x86_64/test-various/Dockerfile b/src/ci/docker/host-x86_64/test-various/Dockerfile index f5fe546cdc6b5..ad0e8e9f92808 100644 --- a/src/ci/docker/host-x86_64/test-various/Dockerfile +++ b/src/ci/docker/host-x86_64/test-various/Dockerfile @@ -18,7 +18,7 @@ RUN apt-get update && DEBIAN_FRONTEND=noninteractive apt-get install -y --no-ins wget \ patch -RUN curl -sL https://nodejs.org/dist/v14.4.0/node-v14.4.0-linux-x64.tar.xz | \ +RUN curl -sL https://nodejs.org/dist/v15.14.0/node-v15.14.0-linux-x64.tar.xz | \ tar -xJ WORKDIR /build/ @@ -31,7 +31,7 @@ RUN sh /scripts/sccache.sh ENV RUST_CONFIGURE_ARGS \ --musl-root-x86_64=/usr/local/x86_64-linux-musl \ - --set build.nodejs=/node-v14.4.0-linux-x64/bin/node \ + --set build.nodejs=/node-v15.14.0-linux-x64/bin/node \ --set rust.lld # Some run-make tests have assertions about code size, and enabling debug diff --git a/src/ci/docker/host-x86_64/x86_64-gnu-llvm-9/Dockerfile b/src/ci/docker/host-x86_64/x86_64-gnu-llvm-10/Dockerfile similarity index 91% rename from src/ci/docker/host-x86_64/x86_64-gnu-llvm-9/Dockerfile rename to src/ci/docker/host-x86_64/x86_64-gnu-llvm-10/Dockerfile index 38eac6588b02c..c34198708c462 100644 --- a/src/ci/docker/host-x86_64/x86_64-gnu-llvm-9/Dockerfile +++ b/src/ci/docker/host-x86_64/x86_64-gnu-llvm-10/Dockerfile @@ -13,8 +13,8 @@ RUN apt-get update && apt-get install -y --no-install-recommends \ cmake \ sudo \ gdb \ - llvm-9-tools \ - llvm-9-dev \ + llvm-10-tools \ + llvm-10-dev \ libedit-dev \ libssl-dev \ pkg-config \ @@ -28,7 +28,7 @@ RUN sh /scripts/sccache.sh # using llvm-link-shared due to libffi issues -- see #34486 ENV RUST_CONFIGURE_ARGS \ --build=x86_64-unknown-linux-gnu \ - --llvm-root=/usr/lib/llvm-9 \ + --llvm-root=/usr/lib/llvm-10 \ --enable-llvm-link-shared \ --set rust.thin-lto-import-instr-limit=10 @@ -46,6 +46,7 @@ ENV SCRIPT python2.7 ../x.py --stage 2 test --exclude src/tools/tidy && \ # This is intended to make sure that both `--pass=check` continues to # work. # - python2.7 ../x.py --stage 2 test src/test/ui --pass=check --target=i686-unknown-linux-gnu && \ + python2.7 ../x.py --stage 2 test src/test/ui --pass=check \ + --host='' --target=i686-unknown-linux-gnu && \ # Run tidy at the very end, after all the other tests. python2.7 ../x.py --stage 2 test src/tools/tidy diff --git a/src/ci/github-actions/ci.yml b/src/ci/github-actions/ci.yml index 7125b33699a0c..a59a90b86bcaf 100644 --- a/src/ci/github-actions/ci.yml +++ b/src/ci/github-actions/ci.yml @@ -280,7 +280,7 @@ jobs: - name: mingw-check <<: *job-linux-xl - - name: x86_64-gnu-llvm-9 + - name: x86_64-gnu-llvm-10 <<: *job-linux-xl - name: x86_64-gnu-tools @@ -412,7 +412,7 @@ jobs: - name: x86_64-gnu-distcheck <<: *job-linux-xl - - name: x86_64-gnu-llvm-9 + - name: x86_64-gnu-llvm-10 env: RUST_BACKTRACE: 1 <<: *job-linux-xl @@ -655,7 +655,7 @@ jobs: if: github.event_name == 'push' && github.ref == 'refs/heads/master' && github.repository == 'rust-lang-ci/rust' steps: - name: checkout the source code - uses: actions/checkout@v1 + uses: actions/checkout@v2 with: fetch-depth: 2 diff --git a/src/doc/book b/src/doc/book index 0f87daf683ae3..b54090a99ec7c 160000 --- a/src/doc/book +++ b/src/doc/book @@ -1 +1 @@ -Subproject commit 0f87daf683ae3de3cb725faecb11b7e7e89f0e5a +Subproject commit b54090a99ec7c4b46a5203a9c927fdbc311bb1f5 diff --git a/src/doc/embedded-book b/src/doc/embedded-book index a96d096cffe5f..569c3391f5c0c 160000 --- a/src/doc/embedded-book +++ b/src/doc/embedded-book @@ -1 +1 @@ -Subproject commit a96d096cffe5fa2c84af1b4b61e1492f839bb2e1 +Subproject commit 569c3391f5c0cc43433bc77831d17f8ff4d76602 diff --git a/src/doc/nomicon b/src/doc/nomicon index adca786547d08..8551afbb2ca6f 160000 --- a/src/doc/nomicon +++ b/src/doc/nomicon @@ -1 +1 @@ -Subproject commit adca786547d08fe676b2fc7a6f08c2ed5280ca38 +Subproject commit 8551afbb2ca6f5ea37fe58380318b209785e4e02 diff --git a/src/doc/reference b/src/doc/reference index 3b6fe80c205d2..e1abb17cd94cd 160000 --- a/src/doc/reference +++ b/src/doc/reference @@ -1 +1 @@ -Subproject commit 3b6fe80c205d2a2b5dc8a276192bbce9eeb9e9cf +Subproject commit e1abb17cd94cd5a8a374b48e1bc8134a2208ed48 diff --git a/src/doc/rust-by-example b/src/doc/rust-by-example index 3e0d98790c912..c80f0b09fc15b 160000 --- a/src/doc/rust-by-example +++ b/src/doc/rust-by-example @@ -1 +1 @@ -Subproject commit 3e0d98790c9126517fa1c604dc3678f396e92a27 +Subproject commit c80f0b09fc15b9251825343be910c08531938ab2 diff --git a/src/doc/rust.css b/src/doc/rust.css index ff18e1a8f51cc..06f4df9a9b6da 100644 --- a/src/doc/rust.css +++ b/src/doc/rust.css @@ -332,6 +332,7 @@ table th { /* Code snippets */ +.example-wrap { position: relative; } pre.rust { position: relative; } a.test-arrow { background-color: rgba(78, 139, 202, 0.2); diff --git a/src/doc/rustc-dev-guide b/src/doc/rustc-dev-guide index c431f8c29a414..a9bd2bbf31e4f 160000 --- a/src/doc/rustc-dev-guide +++ b/src/doc/rustc-dev-guide @@ -1 +1 @@ -Subproject commit c431f8c29a41413dddcb3bfa0d71c9cabe366317 +Subproject commit a9bd2bbf31e4f92b5d3d8e80b22839d0cc7a2022 diff --git a/src/doc/rustc/src/codegen-options/index.md b/src/doc/rustc/src/codegen-options/index.md index 1883346430be0..077c322bd7736 100644 --- a/src/doc/rustc/src/codegen-options/index.md +++ b/src/doc/rustc/src/codegen-options/index.md @@ -497,19 +497,19 @@ point instructions in software. It takes one of the following values: This option controls the emission of "split debuginfo" for debug information that `rustc` generates. The default behavior of this option is platform-specific, and not all possible values for this option work on all -platform. Possible values are: +platforms. Possible values are: * `off` - This is the default for platforms with ELF binaries and windows-gnu - (not Windows MSVC and not macOS). This typically means that dwarf debug + (not Windows MSVC and not macOS). This typically means that DWARF debug information can be found in the final artifact in sections of the executable. This option is not supported on Windows MSVC. On macOS this options prevents the final execution of `dsymutil` to generate debuginfo. -* `packed` - This is the default for Windows MSVC and macOS platforms. The term +* `packed` - This is the default for Windows MSVC and macOS. The term "packed" here means that all the debug information is packed into a separate file from the main executable. On Windows MSVC this is a `*.pdb` file, on macOS this is a `*.dSYM` folder, and on other platforms this is a `*.dwp` - files. + file. * `unpacked` - This means that debug information will be found in separate files for each compilation unit (object file). This is not supported on @@ -517,7 +517,7 @@ platform. Possible values are: debug information. On other Unix platforms this means that `*.dwo` files will contain debug information. -Note that `packed` and `unpacked` gated behind `-Zunstable-options` on +Note that `packed` and `unpacked` are gated behind `-Z unstable-options` on non-macOS platforms at this time. ## target-cpu diff --git a/src/doc/rustc/src/platform-support.md b/src/doc/rustc/src/platform-support.md index d3b88c019f019..25b56b96ed20f 100644 --- a/src/doc/rustc/src/platform-support.md +++ b/src/doc/rustc/src/platform-support.md @@ -179,7 +179,7 @@ target | std | host | notes `i386-apple-ios` | ✓ | | 32-bit x86 iOS `i686-apple-darwin` | ✓ | ✓ | 32-bit macOS (10.7+, Lion+) `i686-pc-windows-msvc` | ✓ | | 32-bit Windows XP support -`i686-unknown-uefi` | ? | | 32-bit UEFI +`i686-unknown-uefi` | * | | 32-bit UEFI `i686-unknown-haiku` | ✓ | ✓ | 32-bit Haiku `i686-unknown-netbsd` | ✓ | ✓ | NetBSD/i386 with SSE2 `i686-unknown-openbsd` | ✓ | ✓ | 32-bit OpenBSD @@ -198,6 +198,7 @@ target | std | host | notes `powerpc-unknown-linux-gnuspe` | ✓ | | PowerPC SPE Linux `powerpc-unknown-linux-musl` | ? | | `powerpc-unknown-netbsd` | ✓ | ✓ | +`powerpc-unknown-openbsd` | ? | | `powerpc-wrs-vxworks` | ? | | `powerpc-wrs-vxworks-spe` | ? | | `powerpc64-unknown-freebsd` | ✓ | ✓ | PPC64 FreeBSD (ELFv1 and ELFv2) @@ -215,18 +216,19 @@ target | std | host | notes `thumbv7a-uwp-windows-msvc` | ✓ | | `thumbv7neon-unknown-linux-musleabihf` | ? | | Thumb2-mode ARMv7a Linux with NEON, MUSL `thumbv4t-none-eabi` | * | | ARMv4T T32 +`wasm64-unknown-unknown` | * | | WebAssembly `x86_64-apple-ios-macabi` | ✓ | | Apple Catalyst on x86_64 `x86_64-apple-tvos` | * | | x86 64-bit tvOS -`x86_64-linux-kernel` | * | | Linux kernel modules +`x86_64-unknown-none-linuxkernel` | * | | Linux kernel modules `x86_64-sun-solaris` | ? | | Deprecated target for 64-bit Solaris 10/11, illumos `x86_64-pc-windows-msvc` | ✓ | | 64-bit Windows XP support `x86_64-unknown-dragonfly` | ✓ | ✓ | 64-bit DragonFlyBSD `x86_64-unknown-haiku` | ✓ | ✓ | 64-bit Haiku `x86_64-unknown-hermit` | ? | | -`x86_64-unknown-hermit-kernel` | ? | | HermitCore kernel +`x86_64-unknown-none-hermitkernel` | ? | | HermitCore kernel `x86_64-unknown-l4re-uclibc` | ? | | `x86_64-unknown-openbsd` | ✓ | ✓ | 64-bit OpenBSD -`x86_64-unknown-uefi` | ? | | +`x86_64-unknown-uefi` | * | | 64-bit UEFI `x86_64-uwp-windows-gnu` | ✓ | | `x86_64-uwp-windows-msvc` | ✓ | | `x86_64-wrs-vxworks` | ? | | diff --git a/src/doc/rustc/src/targets/built-in.md b/src/doc/rustc/src/targets/built-in.md index c33b506cdaefb..344048ee4a1cd 100644 --- a/src/doc/rustc/src/targets/built-in.md +++ b/src/doc/rustc/src/targets/built-in.md @@ -12,4 +12,4 @@ library built by the official Rust distributions. Most targets will need a system linker, and possibly other things. [rustup]: https://github.com/rust-lang/rustup -[rustup-cross]: https://github.com/rust-lang/rustup#cross-compilation +[rustup-cross]: https://rust-lang.github.io/rustup/cross-compilation.html diff --git a/src/doc/rustdoc/src/SUMMARY.md b/src/doc/rustdoc/src/SUMMARY.md index 943aa09f678d7..ae94527e2b4ca 100644 --- a/src/doc/rustdoc/src/SUMMARY.md +++ b/src/doc/rustdoc/src/SUMMARY.md @@ -8,7 +8,7 @@ - [Documentation tests](documentation-tests.md) - [Linking to items by name](linking-to-items-by-name.md) - [Lints](lints.md) -- [Passes](passes.md) - [Advanced features](advanced-features.md) - [Unstable features](unstable-features.md) +- [Passes](passes.md) - [References](references.md) diff --git a/src/doc/rustdoc/src/advanced-features.md b/src/doc/rustdoc/src/advanced-features.md index abdc2e4025d0e..6147bd0a97a96 100644 --- a/src/doc/rustdoc/src/advanced-features.md +++ b/src/doc/rustdoc/src/advanced-features.md @@ -81,3 +81,10 @@ Then, when looking for it through the `rustdoc` search, if you enter "x" or "big", search will show the `BigX` struct first. There are some limitations on the doc alias names though: you can't use `"` or whitespace. + +You can add multiple aliases at the same time by using a list: + +```rust,no_run +#[doc(alias("x", "big"))] +pub struct BigX; +``` diff --git a/src/doc/rustdoc/src/command-line-arguments.md b/src/doc/rustdoc/src/command-line-arguments.md index 0302fbecb6ed0..2e4016e24bc3f 100644 --- a/src/doc/rustdoc/src/command-line-arguments.md +++ b/src/doc/rustdoc/src/command-line-arguments.md @@ -57,23 +57,6 @@ release: 1.17.0 LLVM version: 3.9 ``` -## `-r`/`--input-format`: input format - -This flag is currently ignored; the idea is that `rustdoc` would support various -input formats, and you could specify them via this flag. - -Rustdoc only supports Rust source code and Markdown input formats. If the -file ends in `.md` or `.markdown`, `rustdoc` treats it as a Markdown file. -Otherwise, it assumes that the input file is Rust. - - -## `-w`/`--output-format`: output format - -This flag is currently ignored; the idea is that `rustdoc` would support -various output formats, and you could specify them via this flag. - -Rustdoc only supports HTML output, and so this flag is redundant today. - ## `-o`/`--output`: output path Using this flag looks like this: @@ -100,6 +83,25 @@ By default, `rustdoc` assumes that the name of your crate is the same name as the `.rs` file. `--crate-name` lets you override this assumption with whatever name you choose. +## `--document-private-items`: Show items that are not public + +Using this flag looks like this: + +```bash +$ rustdoc src/lib.rs --document-private-items +``` + +By default, `rustdoc` only documents items that are publicly reachable. + +```rust +pub fn public() {} // this item is public and will documented +mod private { // this item is private and will not be documented + pub fn unreachable() {} // this item is public, but unreachable, so it will not be documented +} +``` + +`--document-private-items` documents all items, even if they're not public. + ## `-L`/`--library-path`: where to look for dependencies Using this flag looks like this: @@ -166,38 +168,6 @@ affect that. The arguments to this flag are the same as those for the `-C` flag on rustc. Run `rustc -C help` to get the full list. -## `--passes`: add more rustdoc passes - -Using this flag looks like this: - -```bash -$ rustdoc --passes list -$ rustdoc src/lib.rs --passes strip-priv-imports -``` - -An argument of "list" will print a list of possible "rustdoc passes", and other -arguments will be the name of which passes to run in addition to the defaults. - -For more details on passes, see [the chapter on them](passes.md). - -See also `--no-defaults`. - -## `--no-defaults`: don't run default passes - -Using this flag looks like this: - -```bash -$ rustdoc src/lib.rs --no-defaults -``` - -By default, `rustdoc` will run several passes over your code. This -removes those defaults, allowing you to use `--passes` to specify -exactly which passes you want. - -For more details on passes, see [the chapter on them](passes.md). - -See also `--passes`. - ## `--test`: run code examples as tests Using this flag looks like this: @@ -429,3 +399,21 @@ If you specify `@path` on the command-line, then it will open `path` and read command line options from it. These options are one per line; a blank line indicates an empty option. The file can use Unix or Windows style line endings, and must be encoded as UTF-8. + +## `--passes`: add more rustdoc passes + +This flag is **deprecated**. +For more details on passes, see [the chapter on them](passes.md). + +## `--no-defaults`: don't run default passes + +This flag is **deprecated**. +For more details on passes, see [the chapter on them](passes.md). + +## `-r`/`--input-format`: input format + +This flag is **deprecated** and **has no effect**. + +Rustdoc only supports Rust source code and Markdown input formats. If the +file ends in `.md` or `.markdown`, `rustdoc` treats it as a Markdown file. +Otherwise, it assumes that the input file is Rust. diff --git a/src/doc/rustdoc/src/lints.md b/src/doc/rustdoc/src/lints.md index 174db711bcee7..a6626679a7d60 100644 --- a/src/doc/rustdoc/src/lints.md +++ b/src/doc/rustdoc/src/lints.md @@ -294,17 +294,14 @@ warning: unclosed HTML tag `h1` warning: 2 warnings emitted ``` -## non_autolinks +## bare_urls -This lint is **nightly-only** and **warns by default**. It detects links which -could use the "automatic" link syntax. For example: +This lint is **warn-by-default**. It detects URLs which are not links. +For example: ```rust /// http://example.org -/// [http://example.com](http://example.com) /// [http://example.net] -/// -/// [http://example.com]: http://example.com pub fn foo() {} ``` @@ -312,22 +309,18 @@ Which will give: ```text warning: this URL is not a hyperlink - --> foo.rs:1:5 + --> links.rs:1:5 | 1 | /// http://example.org | ^^^^^^^^^^^^^^^^^^ help: use an automatic link instead: `` | - = note: `#[warn(rustdoc::non_autolinks)]` on by default - -warning: unneeded long form for URL - --> foo.rs:2:5 - | -2 | /// [http://example.com](http://example.com) - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use an automatic link instead: `` + = note: `#[warn(rustdoc::bare_urls)]` on by default warning: this URL is not a hyperlink - --> foo.rs:3:6 + --> links.rs:3:6 | 3 | /// [http://example.net] | ^^^^^^^^^^^^^^^^^^ help: use an automatic link instead: `` + +warning: 2 warnings emitted ``` diff --git a/src/doc/rustdoc/src/passes.md b/src/doc/rustdoc/src/passes.md index 140b832f19a54..c3c3fd3068ec4 100644 --- a/src/doc/rustdoc/src/passes.md +++ b/src/doc/rustdoc/src/passes.md @@ -3,86 +3,9 @@ Rustdoc has a concept called "passes". These are transformations that `rustdoc` runs on your documentation before producing its final output. -In addition to the passes below, check out the docs for these flags: +Customizing passes is **deprecated**. The available passes are not considered stable and may +change in any release. -* [`--passes`](command-line-arguments.md#--passes-add-more-rustdoc-passes) -* [`--no-defaults`](command-line-arguments.md#--no-defaults-dont-run-default-passes) - -## Default passes - -By default, rustdoc will run some passes, namely: - -* `strip-hidden` -* `strip-private` -* `collapse-docs` -* `unindent-comments` - -However, `strip-private` implies `strip-priv-imports`, and so effectively, -all passes are run by default. - -## `strip-hidden` - -This pass implements the `#[doc(hidden)]` attribute. When this pass runs, it -checks each item, and if it is annotated with this attribute, it removes it -from `rustdoc`'s output. - -Without this pass, these items will remain in the output. - -## `unindent-comments` - -When you write a doc comment like this: - -```rust,no_run -/// This is a documentation comment. -# fn f() {} -``` - -There's a space between the `///` and that `T`. That spacing isn't intended -to be a part of the output; it's there for humans, to help separate the doc -comment syntax from the text of the comment. This pass is what removes that -space. - -The exact rules are left under-specified so that we can fix issues that we find. - -Without this pass, the exact number of spaces is preserved. - -## `collapse-docs` - -With this pass, multiple `#[doc]` attributes are converted into one single -documentation string. - -For example: - -```rust,no_run -#[doc = "This is the first line."] -#[doc = "This is the second line."] -# fn f() {} -``` - -Gets collapsed into a single doc string of - -```text -This is the first line. -This is the second line. -``` - -## `strip-private` - -This removes documentation for any non-public items, so for example: - -```rust,no_run -/// These are private docs. -struct Private; - -/// These are public docs. -pub struct Public; -``` - -This pass removes the docs for `Private`, since they're not public. - -This pass implies `strip-priv-imports`. - -## `strip-priv-imports` - -This is the same as `strip-private`, but for `extern crate` and `use` -statements instead of items. +In the past the most common use case for customizing passes was to omit the `strip-private` pass. +You can do this more easily, and without risk of the pass being changed, by passing +[`--document-private-items`](./unstable-features.md#--document-private-items). diff --git a/src/doc/rustdoc/src/unstable-features.md b/src/doc/rustdoc/src/unstable-features.md index b43070510413a..28b81a40265d3 100644 --- a/src/doc/rustdoc/src/unstable-features.md +++ b/src/doc/rustdoc/src/unstable-features.md @@ -88,26 +88,28 @@ Book][unstable-doc-cfg] and [its tracking issue][issue-doc-cfg]. [unstable-doc-cfg]: ../unstable-book/language-features/doc-cfg.html [issue-doc-cfg]: https://github.com/rust-lang/rust/issues/43781 -### Adding your trait to the "Important Traits" dialog +### Adding your trait to the "Notable traits" dialog -Rustdoc keeps a list of a few traits that are believed to be "fundamental" to a given type when -implemented on it. These traits are intended to be the primary interface for their types, and are -often the only thing available to be documented on their types. For this reason, Rustdoc will track -when a given type implements one of these traits and call special attention to it when a function -returns one of these types. This is the "Important Traits" dialog, visible as a circle-i button next -to the function, which, when clicked, shows the dialog. +Rustdoc keeps a list of a few traits that are believed to be "fundamental" to +types that implement them. These traits are intended to be the primary interface +for their implementers, and are often most of the API available to be documented +on their types. For this reason, Rustdoc will track when a given type implements +one of these traits and call special attention to it when a function returns one +of these types. This is the "Notable traits" dialog, accessible as a circled `i` +button next to the function, which, when clicked, shows the dialog. -In the standard library, the traits that qualify for inclusion are `Iterator`, `io::Read`, and -`io::Write`. However, rather than being implemented as a hard-coded list, these traits have a -special marker attribute on them: `#[doc(spotlight)]`. This means that you could apply this -attribute to your own trait to include it in the "Important Traits" dialog in documentation. +In the standard library, some of the traits that are part of this list are +`Iterator`, `Future`, `io::Read`, and `io::Write`. However, rather than being +implemented as a hard-coded list, these traits have a special marker attribute +on them: `#[doc(notable_trait)]`. This means that you can apply this attribute +to your own trait to include it in the "Notable traits" dialog in documentation. -The `#[doc(spotlight)]` attribute currently requires the `#![feature(doc_spotlight)]` feature gate. -For more information, see [its chapter in the Unstable Book][unstable-spotlight] and [its tracking -issue][issue-spotlight]. +The `#[doc(notable_trait)]` attribute currently requires the `#![feature(doc_notable_trait)]` +feature gate. For more information, see [its chapter in the Unstable Book][unstable-notable_trait] +and [its tracking issue][issue-notable_trait]. -[unstable-spotlight]: ../unstable-book/language-features/doc-spotlight.html -[issue-spotlight]: https://github.com/rust-lang/rust/issues/45040 +[unstable-notable_trait]: ../unstable-book/language-features/doc-notable-trait.html +[issue-notable_trait]: https://github.com/rust-lang/rust/issues/45040 ### Exclude certain dependencies from documentation @@ -340,6 +342,30 @@ Some methodology notes about what rustdoc counts in this metric: Public items that are not documented can be seen with the built-in `missing_docs` lint. Private items that are not documented can be seen with Clippy's `missing_docs_in_private_items` lint. +## `-w`/`--output-format`: output format + +When using +[`--show-coverage`](https://doc.rust-lang.org/nightly/rustdoc/unstable-features.html#--show-coverage-get-statistics-about-code-documentation-coverage), +passing `--output-format json` will display the coverage information in JSON format. For example, +here is the JSON for a file with one documented item and one undocumented item: + +```rust +/// This item has documentation +pub fn foo() {} + +pub fn no_documentation() {} +``` + +```json +{"no_std.rs":{"total":3,"with_docs":1,"total_examples":3,"with_examples":0}} +``` + +Note that the third item is the crate root, which in this case is undocumented. + +When not using `--show-coverage`, `--output-format json` emits documentation in the experimental +[JSON format](https://github.com/rust-lang/rfcs/pull/2963). `--output-format html` has no effect, +and is also accepted on stable toolchains. + ### `--enable-per-target-ignores`: allow `ignore-foo` style filters for doctests Using this flag looks like this: diff --git a/src/doc/unstable-book/src/compiler-flags/source-based-code-coverage.md b/src/doc/unstable-book/src/compiler-flags/source-based-code-coverage.md index 8aca005214724..0d05b80e211a2 100644 --- a/src/doc/unstable-book/src/compiler-flags/source-based-code-coverage.md +++ b/src/doc/unstable-book/src/compiler-flags/source-based-code-coverage.md @@ -128,7 +128,7 @@ $ cargo profdata -- --help # note the additional "--" preceding the tool-specif ## Creating coverage reports -Raw profiles have to be indexed before they can be used to generate coverage reports. This is done using [`llvm-profdata merge`] (or `cargo cov -- merge`), which can combine multiple raw profiles and index them at the same time: +Raw profiles have to be indexed before they can be used to generate coverage reports. This is done using [`llvm-profdata merge`] (or `cargo profdata -- merge`), which can combine multiple raw profiles and index them at the same time: ```shell $ llvm-profdata merge -sparse formatjson5.profraw -o formatjson5.profdata diff --git a/src/doc/unstable-book/src/language-features/c-unwind.md b/src/doc/unstable-book/src/language-features/c-unwind.md new file mode 100644 index 0000000000000..2801d9b5e7778 --- /dev/null +++ b/src/doc/unstable-book/src/language-features/c-unwind.md @@ -0,0 +1,15 @@ +# `c_unwind` + +The tracking issue for this feature is: [#74990] + +[#74990]: https://github.com/rust-lang/rust/issues/74990 + +------------------------ + +Introduces four new ABI strings: "C-unwind", "stdcall-unwind", +"thiscall-unwind", and "system-unwind". These enable unwinding from other +languages (such as C++) into Rust frames and from Rust into other languages. + +See [RFC 2945] for more information. + +[RFC 2945]: https://github.com/rust-lang/rfcs/blob/master/text/2945-c-unwind-abi.md diff --git a/src/doc/unstable-book/src/language-features/cfg-version.md b/src/doc/unstable-book/src/language-features/cfg-version.md index 2b1e50835b767..a6ec42cecba8a 100644 --- a/src/doc/unstable-book/src/language-features/cfg-version.md +++ b/src/doc/unstable-book/src/language-features/cfg-version.md @@ -7,19 +7,20 @@ The tracking issue for this feature is: [#64796] ------------------------ The `cfg_version` feature makes it possible to execute different code -depending on the compiler version. +depending on the compiler version. It will return true if the compiler +version is greater than or equal to the specified version. ## Examples ```rust #![feature(cfg_version)] -#[cfg(version("1.42"))] +#[cfg(version("1.42"))] // 1.42 and above fn a() { // ... } -#[cfg(not(version("1.42")))] +#[cfg(not(version("1.42")))] // 1.41 and below fn a() { // ... } diff --git a/src/doc/unstable-book/src/language-features/doc-notable-trait.md b/src/doc/unstable-book/src/language-features/doc-notable-trait.md new file mode 100644 index 0000000000000..dc402ed4253ac --- /dev/null +++ b/src/doc/unstable-book/src/language-features/doc-notable-trait.md @@ -0,0 +1,33 @@ +# `doc_notable_trait` + +The tracking issue for this feature is: [#45040] + +The `doc_notable_trait` feature allows the use of the `#[doc(notable_trait)]` +attribute, which will display the trait in a "Notable traits" dialog for +functions returning types that implement the trait. For example, this attribute +is applied to the `Iterator`, `Future`, `io::Read`, and `io::Write` traits in +the standard library. + +You can do this on your own traits like so: + +``` +#![feature(doc_notable_trait)] + +#[doc(notable_trait)] +pub trait MyTrait {} + +pub struct MyStruct; +impl MyTrait for MyStruct {} + +/// The docs for this function will have a button that displays a dialog about +/// `MyStruct` implementing `MyTrait`. +pub fn my_fn() -> MyStruct { MyStruct } +``` + +This feature was originally implemented in PR [#45039]. + +See also its documentation in [the rustdoc book][rustdoc-book-notable_trait]. + +[#45040]: https://github.com/rust-lang/rust/issues/45040 +[#45039]: https://github.com/rust-lang/rust/pull/45039 +[rustdoc-book-notable_trait]: ../../rustdoc/unstable-features.html#adding-your-trait-to-the-notable-traits-dialog diff --git a/src/doc/unstable-book/src/language-features/doc-spotlight.md b/src/doc/unstable-book/src/language-features/doc-spotlight.md deleted file mode 100644 index 75eff16331898..0000000000000 --- a/src/doc/unstable-book/src/language-features/doc-spotlight.md +++ /dev/null @@ -1,30 +0,0 @@ -# `doc_spotlight` - -The tracking issue for this feature is: [#45040] - -The `doc_spotlight` feature allows the use of the `spotlight` parameter to the `#[doc]` attribute, -to "spotlight" a specific trait on the return values of functions. Adding a `#[doc(spotlight)]` -attribute to a trait definition will make rustdoc print extra information for functions which return -a type that implements that trait. For example, this attribute is applied to the `Iterator`, -`io::Read`, `io::Write`, and `Future` traits in the standard library. - -You can do this on your own traits, like this: - -``` -#![feature(doc_spotlight)] - -#[doc(spotlight)] -pub trait MyTrait {} - -pub struct MyStruct; -impl MyTrait for MyStruct {} - -/// The docs for this function will have an extra line about `MyStruct` implementing `MyTrait`, -/// without having to write that yourself! -pub fn my_fn() -> MyStruct { MyStruct } -``` - -This feature was originally implemented in PR [#45039]. - -[#45040]: https://github.com/rust-lang/rust/issues/45040 -[#45039]: https://github.com/rust-lang/rust/pull/45039 diff --git a/src/doc/unstable-book/src/language-features/lang-items.md b/src/doc/unstable-book/src/language-features/lang-items.md index 22780804610b6..86bedb51538b5 100644 --- a/src/doc/unstable-book/src/language-features/lang-items.md +++ b/src/doc/unstable-book/src/language-features/lang-items.md @@ -191,7 +191,7 @@ mechanisms of the compiler. This is often mapped to GCC's personality function which do not trigger a panic can be assured that this function is never called. The language item's name is `eh_personality`. -[unwind]: https://github.com/rust-lang/rust/blob/master/src/libpanic_unwind/gcc.rs +[unwind]: https://github.com/rust-lang/rust/blob/master/library/panic_unwind/src/gcc.rs The second function, `rust_begin_panic`, is also used by the failure mechanisms of the compiler. When a panic happens, this controls the message that's displayed on diff --git a/src/doc/unstable-book/src/language-features/link-args.md b/src/doc/unstable-book/src/language-features/link-args.md deleted file mode 100644 index da36e1580012b..0000000000000 --- a/src/doc/unstable-book/src/language-features/link-args.md +++ /dev/null @@ -1,32 +0,0 @@ -# `link_args` - -The tracking issue for this feature is: [#29596] - -[#29596]: https://github.com/rust-lang/rust/issues/29596 - ------------------------- - -You can tell `rustc` how to customize linking, and that is via the `link_args` -attribute. This attribute is applied to `extern` blocks and specifies raw flags -which need to get passed to the linker when producing an artifact. An example -usage would be: - -```rust,no_run -#![feature(link_args)] - -#[link_args = "-foo -bar -baz"] -extern "C" {} -# fn main() {} -``` - -Note that this feature is currently hidden behind the `feature(link_args)` gate -because this is not a sanctioned way of performing linking. Right now `rustc` -shells out to the system linker (`gcc` on most systems, `link.exe` on MSVC), so -it makes sense to provide extra command line arguments, but this will not -always be the case. In the future `rustc` may use LLVM directly to link native -libraries, in which case `link_args` will have no meaning. You can achieve the -same effect as the `link_args` attribute with the `-C link-args` argument to -`rustc`. - -It is highly recommended to *not* use this attribute, and rather use the more -formal `#[link(...)]` attribute on `extern` blocks instead. diff --git a/src/doc/unstable-book/src/language-features/non-ascii-idents.md b/src/doc/unstable-book/src/language-features/non-ascii-idents.md deleted file mode 100644 index 847f25ecab132..0000000000000 --- a/src/doc/unstable-book/src/language-features/non-ascii-idents.md +++ /dev/null @@ -1,48 +0,0 @@ -# `non_ascii_idents` - -The tracking issue for this feature is: [#55467] - -[#55467]: https://github.com/rust-lang/rust/issues/55467 - ------------------------- - -The `non_ascii_idents` feature adds support for non-ASCII identifiers. - -## Examples - -```rust -#![feature(non_ascii_idents)] - -const ε: f64 = 0.00001f64; -const Π: f64 = 3.14f64; -``` - -## Changes to the language reference - -> **Lexer:**\ -> IDENTIFIER :\ ->       XID_start XID_continue\*\ ->    | `_` XID_continue+ - -An identifier is any nonempty Unicode string of the following form: - -Either - - * The first character has property [`XID_start`] - * The remaining characters have property [`XID_continue`] - -Or - - * The first character is `_` - * The identifier is more than one character, `_` alone is not an identifier - * The remaining characters have property [`XID_continue`] - -that does _not_ occur in the set of [strict keywords]. - -> **Note**: [`XID_start`] and [`XID_continue`] as character properties cover the -> character ranges used to form the more familiar C and Java language-family -> identifiers. - -[`XID_start`]: http://unicode.org/cldr/utility/list-unicodeset.jsp?a=%5B%3AXID_Start%3A%5D&abb=on&g=&i= -[`XID_continue`]: http://unicode.org/cldr/utility/list-unicodeset.jsp?a=%5B%3AXID_Continue%3A%5D&abb=on&g=&i= -[strict keywords]: ../../reference/keywords.md#strict-keywords diff --git a/src/doc/unstable-book/src/language-features/or-patterns.md b/src/doc/unstable-book/src/language-features/or-patterns.md deleted file mode 100644 index 55c31add26d77..0000000000000 --- a/src/doc/unstable-book/src/language-features/or-patterns.md +++ /dev/null @@ -1,36 +0,0 @@ -# `or_patterns` - -The tracking issue for this feature is: [#54883] - -[#54883]: https://github.com/rust-lang/rust/issues/54883 - ------------------------- - -The `or_pattern` language feature allows `|` to be arbitrarily nested within -a pattern, for example, `Some(A(0) | B(1 | 2))` becomes a valid pattern. - -## Examples - -```rust,no_run -#![feature(or_patterns)] - -pub enum Foo { - Bar, - Baz, - Quux, -} - -pub fn example(maybe_foo: Option) { - match maybe_foo { - Some(Foo::Bar | Foo::Baz) => { - println!("The value contained `Bar` or `Baz`"); - } - Some(_) => { - println!("The value did not contain `Bar` or `Baz`"); - } - None => { - println!("The value was `None`"); - } - } -} -``` diff --git a/src/doc/unstable-book/src/library-features/asm.md b/src/doc/unstable-book/src/library-features/asm.md index c0e23b834d150..4f9033cedc3ff 100644 --- a/src/doc/unstable-book/src/library-features/asm.md +++ b/src/doc/unstable-book/src/library-features/asm.md @@ -35,7 +35,7 @@ Inline assembly is currently supported on the following architectures: Let us start with the simplest possible example: ```rust,allow_fail -# #![feature(asm)] +#![feature(asm)] unsafe { asm!("nop"); } @@ -52,7 +52,7 @@ Now inserting an instruction that does nothing is rather boring. Let us do somet actually acts on data: ```rust,allow_fail -# #![feature(asm)] +#![feature(asm)] let x: u64; unsafe { asm!("mov {}, 5", out(reg) x); @@ -74,7 +74,7 @@ the template and will read the variable from there after the inline assembly fin Let us see another example that also uses an input: ```rust,allow_fail -# #![feature(asm)] +#![feature(asm)] let i: u64 = 3; let o: u64; unsafe { @@ -114,7 +114,7 @@ readability, and allows reordering instructions without changing the argument or We can further refine the above example to avoid the `mov` instruction: ```rust,allow_fail -# #![feature(asm)] +#![feature(asm)] let mut x: u64 = 3; unsafe { asm!("add {0}, {number}", inout(reg) x, number = const 5); @@ -128,7 +128,7 @@ This is different from specifying an input and output separately in that it is g It is also possible to specify different variables for the input and output parts of an `inout` operand: ```rust,allow_fail -# #![feature(asm)] +#![feature(asm)] let x: u64 = 3; let y: u64; unsafe { @@ -150,7 +150,7 @@ There is also a `inlateout` variant of this specifier. Here is an example where `inlateout` *cannot* be used: ```rust,allow_fail -# #![feature(asm)] +#![feature(asm)] let mut a: u64 = 4; let b: u64 = 4; let c: u64 = 4; @@ -171,7 +171,7 @@ Here the compiler is free to allocate the same register for inputs `b` and `c` s However the following example can use `inlateout` since the output is only modified after all input registers have been read: ```rust,allow_fail -# #![feature(asm)] +#![feature(asm)] let mut a: u64 = 4; let b: u64 = 4; unsafe { @@ -190,7 +190,7 @@ While `reg` is generally available on any architecture, these are highly archite among others can be addressed by their name. ```rust,allow_fail,no_run -# #![feature(asm)] +#![feature(asm)] let cmd = 0xd1; unsafe { asm!("out 0x64, eax", in("eax") cmd); @@ -206,7 +206,7 @@ Note that unlike other operand types, explicit register operands cannot be used Consider this example which uses the x86 `mul` instruction: ```rust,allow_fail -# #![feature(asm)] +#![feature(asm)] fn mul(a: u64, b: u64) -> u128 { let lo: u64; let hi: u64; @@ -242,7 +242,7 @@ We need to tell the compiler about this since it may need to save and restore th around the inline assembly block. ```rust,allow_fail -# #![feature(asm)] +#![feature(asm)] let ebx: u32; let ecx: u32; @@ -272,7 +272,7 @@ However we still need to tell the compiler that `eax` and `edx` have been modifi This can also be used with a general register class (e.g. `reg`) to obtain a scratch register for use inside the asm code: ```rust,allow_fail -# #![feature(asm)] +#![feature(asm)] // Multiply x by 6 using shifts and adds let mut x: u64 = 4; unsafe { @@ -294,7 +294,7 @@ A special operand type, `sym`, allows you to use the symbol name of a `fn` or `s This allows you to call a function or access a global variable without needing to keep its address in a register. ```rust,allow_fail -# #![feature(asm)] +#![feature(asm)] extern "C" fn foo(arg: i32) { println!("arg = {}", arg); } @@ -306,13 +306,19 @@ fn call_foo(arg: i32) { sym foo, // 1st argument in rdi, which is caller-saved inout("rdi") arg => _, - // All caller-saved registers must be marked as clobberred + // All caller-saved registers must be marked as clobbered out("rax") _, out("rcx") _, out("rdx") _, out("rsi") _, out("r8") _, out("r9") _, out("r10") _, out("r11") _, out("xmm0") _, out("xmm1") _, out("xmm2") _, out("xmm3") _, out("xmm4") _, out("xmm5") _, out("xmm6") _, out("xmm7") _, out("xmm8") _, out("xmm9") _, out("xmm10") _, out("xmm11") _, out("xmm12") _, out("xmm13") _, out("xmm14") _, out("xmm15") _, + // Also mark AVX-512 registers as clobbered. This is accepted by the + // compiler even if AVX-512 is not enabled on the current target. + out("xmm16") _, out("xmm17") _, out("xmm18") _, out("xmm19") _, + out("xmm20") _, out("xmm21") _, out("xmm22") _, out("xmm23") _, + out("xmm24") _, out("xmm25") _, out("xmm26") _, out("xmm27") _, + out("xmm28") _, out("xmm29") _, out("xmm30") _, out("xmm31") _, ) } } @@ -330,7 +336,7 @@ By default the compiler will always choose the name that refers to the full regi This default can be overriden by using modifiers on the template string operands, just like you would with format strings: ```rust,allow_fail -# #![feature(asm)] +#![feature(asm)] let mut x: u16 = 0xab; unsafe { @@ -355,7 +361,7 @@ For example, in x86/x86_64 and intel assembly syntax, you should wrap inputs/out to indicate they are memory operands: ```rust,allow_fail -# #![feature(asm, llvm_asm)] +#![feature(asm, llvm_asm)] # fn load_fpu_control_word(control: u16) { unsafe { asm!("fldcw [{}]", in(reg) &control, options(nostack)); @@ -366,6 +372,43 @@ unsafe { # } ``` +## Labels + +The compiler is allowed to instantiate multiple copies an `asm!` block, for example when the function containing it is inlined in multiple places. As a consequence, you should only use GNU assembler [local labels] inside inline assembly code. Defining symbols in assembly code may lead to assembler and/or linker errors due to duplicate symbol definitions. + +Moreover, due to [an llvm bug], you shouldn't use labels exclusively made of `0` and `1` digits, e.g. `0`, `11` or `101010`, as they may end up being interpreted as binary values. + +```rust,allow_fail +#![feature(asm)] + +let mut a = 0; +unsafe { + asm!( + "mov {0}, 10", + "2:", + "sub {0}, 1", + "cmp {0}, 3", + "jle 2f", + "jmp 2b", + "2:", + "add {0}, 2", + out(reg) a + ); +} +assert_eq!(a, 5); +``` + +This will decrement the `{0}` register value from 10 to 3, then add 2 and store it in `a`. + +This example show a few thing: + +First that the same number can be used as a label multiple times in the same inline block. + +Second, that when a numeric label is used as a reference (as an instruction operand, for example), the suffixes b (“backward”) or f (“forward”) should be added to the numeric label. It will then refer to the nearest label defined by this number in this direction. + +[local labels]: https://sourceware.org/binutils/docs/as/Symbol-Names.html#Local-Labels +[an llvm bug]: https://bugs.llvm.org/show_bug.cgi?id=36144 + ## Options By default, an inline assembly block is treated the same way as an external FFI function call with a custom calling convention: it may read/write memory, have observable side effects, etc. However in many cases, it is desirable to give the compiler more information about what the assembly code is actually doing so that it can optimize better. @@ -373,7 +416,7 @@ By default, an inline assembly block is treated the same way as an external FFI Let's take our previous example of an `add` instruction: ```rust,allow_fail -# #![feature(asm)] +#![feature(asm)] let mut a: u64 = 4; let b: u64 = 4; unsafe { @@ -495,7 +538,7 @@ Here is the list of currently supported register classes: | x86 | `reg` | `ax`, `bx`, `cx`, `dx`, `si`, `di`, `r[8-15]` (x86-64 only) | `r` | | x86 | `reg_abcd` | `ax`, `bx`, `cx`, `dx` | `Q` | | x86-32 | `reg_byte` | `al`, `bl`, `cl`, `dl`, `ah`, `bh`, `ch`, `dh` | `q` | -| x86-64 | `reg_byte` | `al`, `bl`, `cl`, `dl`, `sil`, `dil`, `r[8-15]b`, `ah`\*, `bh`\*, `ch`\*, `dh`\* | `q` | +| x86-64 | `reg_byte`\* | `al`, `bl`, `cl`, `dl`, `sil`, `dil`, `r[8-15]b` | `q` | | x86 | `xmm_reg` | `xmm[0-7]` (x86) `xmm[0-15]` (x86-64) | `x` | | x86 | `ymm_reg` | `ymm[0-7]` (x86) `ymm[0-15]` (x86-64) | `x` | | x86 | `zmm_reg` | `zmm[0-7]` (x86) `zmm[0-31]` (x86-64) | `v` | @@ -526,7 +569,7 @@ Here is the list of currently supported register classes: > **Note**: On x86 we treat `reg_byte` differently from `reg` because the compiler can allocate `al` and `ah` separately whereas `reg` reserves the whole register. > -> Note #2: On x86-64 the high byte registers (e.g. `ah`) are only available when used as an explicit register. Specifying the `reg_byte` register class for an operand will always allocate a low byte register. +> Note #2: On x86-64 the high byte registers (e.g. `ah`) are not available in the `reg_byte` register class. > > Note #3: NVPTX doesn't have a fixed register set, so named registers are not supported. > @@ -781,8 +824,5 @@ The compiler performs some additional checks on options: - You are responsible for switching any target-specific state (e.g. thread-local storage, stack bounds). - The set of memory locations that you may access is the intersection of those allowed by the `asm!` blocks you entered and exited. - You cannot assume that an `asm!` block will appear exactly once in the output binary. The compiler is allowed to instantiate multiple copies of the `asm!` block, for example when the function containing it is inlined in multiple places. - - As a consequence, you should only use [local labels] inside inline assembly code. Defining symbols in assembly code may lead to assembler and/or linker errors due to duplicate symbol definitions. > **Note**: As a general rule, the flags covered by `preserves_flags` are those which are *not* preserved when performing a function call. - -[local labels]: https://sourceware.org/binutils/docs/as/Symbol-Names.html#Local-Labels diff --git a/src/etc/gdb_providers.py b/src/etc/gdb_providers.py index 2d902a9b6e08e..f0ce13b269c59 100644 --- a/src/etc/gdb_providers.py +++ b/src/etc/gdb_providers.py @@ -349,17 +349,18 @@ def __init__(self, valobj, show_values=True): self.show_values = show_values table = self.table() - capacity = int(table["bucket_mask"]) + 1 - ctrl = table["ctrl"]["pointer"] + table_inner = table["table"] + capacity = int(table_inner["bucket_mask"]) + 1 + ctrl = table_inner["ctrl"]["pointer"] - self.size = int(table["items"]) + self.size = int(table_inner["items"]) self.pair_type = table.type.template_argument(0).strip_typedefs() - self.new_layout = not table.type.has_key("data") + self.new_layout = not table_inner.type.has_key("data") if self.new_layout: self.data_ptr = ctrl.cast(self.pair_type.pointer()) else: - self.data_ptr = table["data"]["pointer"] + self.data_ptr = table_inner["data"]["pointer"] self.valid_indices = [] for idx in range(capacity): diff --git a/src/etc/lldb_providers.py b/src/etc/lldb_providers.py index ca2685ca31ffd..86dcc335e3cbf 100644 --- a/src/etc/lldb_providers.py +++ b/src/etc/lldb_providers.py @@ -527,20 +527,22 @@ def get_child_at_index(self, index): def update(self): # type: () -> None table = self.table() - capacity = table.GetChildMemberWithName("bucket_mask").GetValueAsUnsigned() + 1 - ctrl = table.GetChildMemberWithName("ctrl").GetChildAtIndex(0) + inner_table = table.GetChildMemberWithName("table") - self.size = table.GetChildMemberWithName("items").GetValueAsUnsigned() + capacity = inner_table.GetChildMemberWithName("bucket_mask").GetValueAsUnsigned() + 1 + ctrl = inner_table.GetChildMemberWithName("ctrl").GetChildAtIndex(0) + + self.size = inner_table.GetChildMemberWithName("items").GetValueAsUnsigned() self.pair_type = table.type.template_args[0] if self.pair_type.IsTypedefType(): self.pair_type = self.pair_type.GetTypedefedType() self.pair_type_size = self.pair_type.GetByteSize() - self.new_layout = not table.GetChildMemberWithName("data").IsValid() + self.new_layout = not inner_table.GetChildMemberWithName("data").IsValid() if self.new_layout: self.data_ptr = ctrl.Cast(self.pair_type.GetPointerType()) else: - self.data_ptr = table.GetChildMemberWithName("data").GetChildAtIndex(0) + self.data_ptr = inner_table.GetChildMemberWithName("data").GetChildAtIndex(0) u8_type = self.valobj.GetTarget().GetBasicType(eBasicTypeUnsignedChar) u8_type_size = self.valobj.GetTarget().GetBasicType(eBasicTypeUnsignedChar).GetByteSize() diff --git a/src/etc/natvis/liballoc.natvis b/src/etc/natvis/liballoc.natvis index cfaafc5734bce..9cc60fc7b47e5 100644 --- a/src/etc/natvis/liballoc.natvis +++ b/src/etc/natvis/liballoc.natvis @@ -75,4 +75,11 @@ ptr.pointer->data + + Borrowed({__0}) + Owned({__0}) + + __0 + + diff --git a/src/etc/natvis/libcore.natvis b/src/etc/natvis/libcore.natvis index 984a8bfb13c7c..9c3c26f597838 100644 --- a/src/etc/natvis/libcore.natvis +++ b/src/etc/natvis/libcore.natvis @@ -30,4 +30,19 @@ + + Ok({__0}) + Err({(*($T2*) &__0)}) + + __0 + (*($T2*) &__0) + + + + + {(void*) pointer} + + *pointer + + \ No newline at end of file diff --git a/src/etc/natvis/libstd.natvis b/src/etc/natvis/libstd.natvis index 7e5ee7b13daf1..2c05f9d7c12e5 100644 --- a/src/etc/natvis/libstd.natvis +++ b/src/etc/natvis/libstd.natvis @@ -26,22 +26,22 @@ --> - {{ len={base.table.items} }} + {{ len={base.table.table.items} }} - base.table.items - base.table.items + base.table.growth_left + base.table.table.items + base.table.table.items + base.table.table.growth_left base.hash_builder - - base.table.items + + base.table.table.items - + n-- - ((tuple<$T1, $T2>*)base.table.ctrl.pointer)[-(i + 1)].__1 + ((tuple<$T1, $T2>*)base.table.table.ctrl.pointer)[-(i + 1)].__1 i++ @@ -50,26 +50,55 @@ - {{ len={base.map.table.items} }} + {{ len={base.map.table.table.items} }} - base.map.table.items - base.map.table.items + base.map.table.growth_left + base.map.table.table.items + base.map.table.table.items + base.map.table.table.growth_left base.map.hash_builder - - base.map.table.items + + base.map.table.table.items - + n-- - (($T1*)base.map.table.ctrl.pointer)[-(i + 1)] + (($T1*)base.map.table.table.ctrl.pointer)[-(i + 1)] i++ + + + {inner.data_ptr,s} + + + + + inner.length + (char*)inner.data_ptr + + + + + + + + {(char*) inner} + + + {(char*) inner} + + + strlen((char *) inner) + 1 + (char*)inner + + + + + diff --git a/src/librustdoc/Cargo.toml b/src/librustdoc/Cargo.toml index 44c2c3b17860b..b816b9d718876 100644 --- a/src/librustdoc/Cargo.toml +++ b/src/librustdoc/Cargo.toml @@ -8,9 +8,9 @@ edition = "2018" path = "lib.rs" [dependencies] -arrayvec = { version = "0.5.1", default-features = false } +arrayvec = { version = "0.7", default-features = false } pulldown-cmark = { version = "0.8", default-features = false } -minifier = "0.0.33" +minifier = "0.0.39" rayon = { version = "0.3.0", package = "rustc-rayon" } serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" @@ -20,7 +20,7 @@ itertools = "0.9" regex = "1" rustdoc-json-types = { path = "../rustdoc-json-types" } tracing = "0.1" -tracing-tree = "0.1.6" +tracing-tree = "0.1.9" [dependencies.tracing-subscriber] version = "0.2.13" @@ -29,3 +29,9 @@ features = ["fmt", "env-filter", "smallvec", "parking_lot", "ansi"] [dev-dependencies] expect-test = "1.0" + +[features] +jemalloc = [] + +[package.metadata.rust-analyzer] +rustc_private = true diff --git a/src/librustdoc/clean/auto_trait.rs b/src/librustdoc/clean/auto_trait.rs index d0e058c7dda75..9052e5954200f 100644 --- a/src/librustdoc/clean/auto_trait.rs +++ b/src/librustdoc/clean/auto_trait.rs @@ -110,7 +110,7 @@ impl<'a, 'tcx> AutoTraitFinder<'a, 'tcx> { }; Some(Item { - source: Span::dummy(), + span: Span::dummy(), name: None, attrs: Default::default(), visibility: Inherited, diff --git a/src/librustdoc/clean/blanket_impl.rs b/src/librustdoc/clean/blanket_impl.rs index 4e4e1e5cbce2f..f7e08d1040188 100644 --- a/src/librustdoc/clean/blanket_impl.rs +++ b/src/librustdoc/clean/blanket_impl.rs @@ -26,18 +26,16 @@ impl<'a, 'tcx> BlanketImplFinder<'a, 'tcx> { { continue; } - self.cx.tcx.for_each_relevant_impl(trait_def_id, ty, |impl_def_id| { + // 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() { debug!( "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 may_apply = self.cx.tcx.infer_ctxt().enter(|infcx| { - match trait_ref.self_ty().kind() { - ty::Param(_) => {} - _ => return false, - } - + 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); @@ -90,7 +88,7 @@ impl<'a, 'tcx> BlanketImplFinder<'a, 'tcx> { may_apply, trait_ref, ty ); if !may_apply { - return; + continue; } self.cx.generated_synthetics.insert((ty, trait_def_id)); @@ -102,7 +100,7 @@ impl<'a, 'tcx> BlanketImplFinder<'a, 'tcx> { .collect(); impls.push(Item { - source: self.cx.tcx.def_span(impl_def_id).clean(self.cx), + span: self.cx.tcx.def_span(impl_def_id).clean(self.cx), name: None, attrs: Default::default(), visibility: Inherited, @@ -131,7 +129,7 @@ impl<'a, 'tcx> BlanketImplFinder<'a, 'tcx> { blanket_impl: Some(trait_ref.self_ty().clean(self.cx)), }), }); - }); + } } impls } diff --git a/src/librustdoc/clean/cfg.rs b/src/librustdoc/clean/cfg.rs index 02adccef594ef..592aefb6a4b01 100644 --- a/src/librustdoc/clean/cfg.rs +++ b/src/librustdoc/clean/cfg.rs @@ -498,7 +498,7 @@ impl<'a> fmt::Display for Display<'a> { "powerpc64" => "PowerPC-64", "s390x" => "s390x", "sparc64" => "SPARC64", - "wasm32" => "WebAssembly", + "wasm32" | "wasm64" => "WebAssembly", "x86" => "x86", "x86_64" => "x86-64", _ => "", diff --git a/src/librustdoc/clean/inline.rs b/src/librustdoc/clean/inline.rs index 47a74238a7a3d..277ec91f15ed7 100644 --- a/src/librustdoc/clean/inline.rs +++ b/src/librustdoc/clean/inline.rs @@ -9,7 +9,7 @@ use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::{DefId, CRATE_DEF_INDEX}; use rustc_hir::Mutability; use rustc_metadata::creader::LoadedMacro; -use rustc_middle::ty; +use rustc_middle::ty::{self, TyCtxt}; use rustc_mir::const_eval::is_min_const_fn; use rustc_span::hygiene::MacroKind; use rustc_span::symbol::{kw, sym, Symbol}; @@ -459,7 +459,7 @@ fn build_module( items.push(clean::Item { name: None, attrs: box clean::Attributes::default(), - source: clean::Span::dummy(), + span: clean::Span::dummy(), def_id: DefId::local(CRATE_DEF_INDEX), visibility: clean::Public, kind: box clean::ImportItem(clean::Import::new_simple( @@ -490,23 +490,19 @@ fn build_module( clean::Module { items, is_crate: false } } -crate fn print_inlined_const(cx: &DocContext<'_>, did: DefId) -> String { +crate fn print_inlined_const(tcx: TyCtxt<'_>, did: DefId) -> String { if let Some(did) = did.as_local() { - let hir_id = cx.tcx.hir().local_def_id_to_hir_id(did); - rustc_hir_pretty::id_to_string(&cx.tcx.hir(), hir_id) + let hir_id = tcx.hir().local_def_id_to_hir_id(did); + rustc_hir_pretty::id_to_string(&tcx.hir(), hir_id) } else { - cx.tcx.rendered_const(did) + tcx.rendered_const(did) } } -fn build_const(cx: &mut DocContext<'_>, did: DefId) -> clean::Constant { +fn build_const(cx: &mut DocContext<'_>, def_id: DefId) -> clean::Constant { clean::Constant { - type_: cx.tcx.type_of(did).clean(cx), - expr: print_inlined_const(cx, did), - value: clean::utils::print_evaluated_const(cx, did), - is_literal: did.as_local().map_or(false, |did| { - clean::utils::is_literal_expr(cx, cx.tcx.hir().local_def_id_to_hir_id(did)) - }), + type_: cx.tcx.type_of(def_id).clean(cx), + kind: clean::ConstantKind::Extern { def_id }, } } @@ -628,7 +624,7 @@ crate fn record_extern_trait(cx: &mut DocContext<'_>, did: DefId) { let trait_ = clean::TraitWithExtraInfo { trait_, - is_spotlight: clean::utils::has_doc_flag(cx.tcx.get_attrs(did), sym::spotlight), + is_notable: clean::utils::has_doc_flag(cx.tcx.get_attrs(did), sym::notable_trait), }; cx.external_traits.borrow_mut().insert(did, trait_); cx.active_extern_traits.remove(&did); diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index 5d81498f8d210..72046645e3a97 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -84,125 +84,8 @@ impl, U> Clean> for Option { impl Clean for CrateNum { fn clean(&self, cx: &mut DocContext<'_>) -> ExternalCrate { - let tcx = cx.tcx; let root = DefId { krate: *self, index: CRATE_DEF_INDEX }; - let krate_span = tcx.def_span(root); - let krate_src = cx.sess().source_map().span_to_filename(krate_span); - - // Collect all inner modules which are tagged as implementations of - // primitives. - // - // Note that this loop only searches the top-level items of the crate, - // and this is intentional. If we were to search the entire crate for an - // item tagged with `#[doc(primitive)]` then we would also have to - // search the entirety of external modules for items tagged - // `#[doc(primitive)]`, which is a pretty inefficient process (decoding - // all that metadata unconditionally). - // - // In order to keep the metadata load under control, the - // `#[doc(primitive)]` feature is explicitly designed to only allow the - // primitive tags to show up as the top level items in a crate. - // - // Also note that this does not attempt to deal with modules tagged - // duplicately for the same primitive. This is handled later on when - // rendering by delegating everything to a hash map. - let mut as_primitive = |res: Res| { - if let Res::Def(DefKind::Mod, def_id) = res { - let attrs = cx.tcx.get_attrs(def_id).clean(cx); - let mut prim = None; - for attr in attrs.lists(sym::doc) { - if let Some(v) = attr.value_str() { - if attr.has_name(sym::primitive) { - prim = PrimitiveType::from_symbol(v); - if prim.is_some() { - break; - } - // FIXME: should warn on unknown primitives? - } - } - } - return prim.map(|p| (def_id, p)); - } - None - }; - let primitives = if root.is_local() { - tcx.hir() - .krate() - .item - .module - .item_ids - .iter() - .filter_map(|&id| { - let item = tcx.hir().item(id); - match item.kind { - hir::ItemKind::Mod(_) => { - as_primitive(Res::Def(DefKind::Mod, id.def_id.to_def_id())) - } - hir::ItemKind::Use(ref path, hir::UseKind::Single) - if item.vis.node.is_pub() => - { - as_primitive(path.res).map(|(_, prim)| { - // Pretend the primitive is local. - (id.def_id.to_def_id(), prim) - }) - } - _ => None, - } - }) - .collect() - } else { - tcx.item_children(root).iter().map(|item| item.res).filter_map(as_primitive).collect() - }; - - let mut as_keyword = |res: Res| { - if let Res::Def(DefKind::Mod, def_id) = res { - let attrs = tcx.get_attrs(def_id).clean(cx); - let mut keyword = None; - for attr in attrs.lists(sym::doc) { - if attr.has_name(sym::keyword) { - if let Some(v) = attr.value_str() { - keyword = Some(v); - break; - } - } - } - return keyword.map(|p| (def_id, p)); - } - None - }; - let keywords = if root.is_local() { - tcx.hir() - .krate() - .item - .module - .item_ids - .iter() - .filter_map(|&id| { - let item = tcx.hir().item(id); - match item.kind { - hir::ItemKind::Mod(_) => { - as_keyword(Res::Def(DefKind::Mod, id.def_id.to_def_id())) - } - hir::ItemKind::Use(ref path, hir::UseKind::Single) - if item.vis.node.is_pub() => - { - as_keyword(path.res).map(|(_, prim)| (id.def_id.to_def_id(), prim)) - } - _ => None, - } - }) - .collect() - } else { - tcx.item_children(root).iter().map(|item| item.res).filter_map(as_keyword).collect() - }; - - ExternalCrate { - name: tcx.crate_name(*self), - src: krate_src, - attrs: tcx.get_attrs(root).clean(cx), - primitives, - keywords, - } + ExternalCrate { crate_num: *self, attrs: cx.tcx.get_attrs(root).clean(cx) } } } @@ -231,11 +114,11 @@ impl Clean for doctree::Module<'_> { let what_rustc_thinks = Item::from_hir_id_and_parts( self.id, - self.name, + Some(self.name), ModuleItem(Module { is_crate: self.is_crate, items }), cx, ); - Item { source: span.clean(cx), ..what_rustc_thinks } + Item { span: span.clean(cx), ..what_rustc_thinks } } } @@ -354,7 +237,7 @@ impl Clean for hir::Lifetime { match def { Some( rl::Region::EarlyBound(_, node_id, _) - | rl::Region::LateBound(_, node_id, _) + | rl::Region::LateBound(_, _, node_id, _) | rl::Region::Free(_, node_id), ) => { if let Some(lt) = cx.lt_substs.get(&node_id).cloned() { @@ -398,9 +281,7 @@ impl Clean for hir::ConstArg { .tcx .type_of(cx.tcx.hir().body_owner_def_id(self.value.body).to_def_id()) .clean(cx), - expr: print_const_expr(cx.tcx, self.value.body), - value: None, - is_literal: is_literal_expr(cx, self.value.body.hir_id), + kind: ConstantKind::Anonymous { body: self.value.body }, } } } @@ -415,7 +296,7 @@ impl Clean> for ty::RegionKind { fn clean(&self, _cx: &mut DocContext<'_>) -> Option { match *self { ty::ReStatic => Some(Lifetime::statik()), - ty::ReLateBound(_, ty::BoundRegion { kind: ty::BrNamed(_, name) }) => { + ty::ReLateBound(_, ty::BoundRegion { kind: ty::BrNamed(_, name), .. }) => { Some(Lifetime(name)) } ty::ReEarlyBound(ref data) => Some(Lifetime(data.name)), @@ -863,7 +744,8 @@ fn clean_fn_or_proc_macro( name: &mut Symbol, cx: &mut DocContext<'_>, ) -> ItemKind { - let macro_kind = item.attrs.iter().find_map(|a| { + let attrs = cx.tcx.hir().attrs(item.hir_id()); + let macro_kind = attrs.iter().find_map(|a| { if a.has_name(sym::proc_macro) { Some(MacroKind::Bang) } else if a.has_name(sym::proc_macro_derive) { @@ -877,8 +759,7 @@ fn clean_fn_or_proc_macro( match macro_kind { Some(kind) => { if kind == MacroKind::Derive { - *name = item - .attrs + *name = attrs .lists(sym::proc_macro_derive) .find_map(|mi| mi.ident()) .expect("proc-macro derives require a name") @@ -886,7 +767,7 @@ fn clean_fn_or_proc_macro( } let mut helpers = Vec::new(); - for mi in item.attrs.lists(sym::proc_macro_derive) { + for mi in attrs.lists(sym::proc_macro_derive) { if !mi.has_name(sym::attributes) { continue; } @@ -1135,7 +1016,7 @@ impl Clean for ty::AssocItem { ty::AssocKind::Const => { let ty = tcx.type_of(self.def_id); let default = if self.defaultness.has_value() { - Some(inline::print_inlined_const(cx, self.def_id)) + Some(inline::print_inlined_const(tcx, self.def_id)) } else { None }; @@ -1477,7 +1358,7 @@ impl Clean for hir::Ty<'_> { } } TyKind::Path(_) => clean_qpath(&self, cx), - TyKind::TraitObject(ref bounds, ref lifetime) => { + TyKind::TraitObject(ref bounds, ref lifetime, _) => { match bounds[0].clean(cx).trait_ { ResolvedPath { path, param_names: None, did, is_generic } => { let mut bounds: Vec = bounds[1..] @@ -1745,16 +1626,15 @@ impl<'tcx> Clean for Ty<'tcx> { impl<'tcx> Clean for ty::Const<'tcx> { fn clean(&self, cx: &mut DocContext<'_>) -> Constant { + // FIXME: instead of storing the stringified expression, store `self` directly instead. Constant { type_: self.ty.clean(cx), - expr: format!("{}", self), - value: None, - is_literal: false, + kind: ConstantKind::TyConst { expr: self.to_string() }, } } } -impl Clean for hir::StructField<'_> { +impl Clean for hir::FieldDef<'_> { fn clean(&self, cx: &mut DocContext<'_>) -> Item { let what_rustc_thinks = Item::from_hir_id_and_parts( self.hir_id, @@ -1953,9 +1833,7 @@ impl Clean> for (&hir::Item<'_>, Option) { } ItemKind::Const(ty, body_id) => ConstantItem(Constant { type_: ty.clean(cx), - expr: print_const_expr(cx.tcx, body_id), - value: print_evaluated_const(cx, def_id), - is_literal: is_literal_expr(cx, body_id.hir_id), + kind: ConstantKind::Local { body: body_id, def_id }, }), ItemKind::OpaqueTy(ref ty) => OpaqueTyItem(OpaqueTy { bounds: ty.bounds.clean(cx), @@ -2102,8 +1980,9 @@ fn clean_extern_crate( let cnum = cx.tcx.extern_mod_stmt_cnum(krate.def_id).unwrap_or(LOCAL_CRATE); // this is the ID of the crate itself let crate_def_id = DefId { krate: cnum, index: CRATE_DEF_INDEX }; + let attrs = cx.tcx.hir().attrs(krate.hir_id()); let please_inline = krate.vis.node.is_pub() - && krate.attrs.iter().any(|a| { + && attrs.iter().any(|a| { a.has_name(sym::doc) && match a.meta_item_list() { Some(l) => attr::list_contains_name(&l, sym::inline), @@ -2121,7 +2000,7 @@ fn clean_extern_crate( cx.tcx.parent_module(krate.hir_id()).to_def_id(), res, name, - Some(krate.attrs), + Some(attrs), &mut visited, ) { return items; @@ -2129,12 +2008,12 @@ fn clean_extern_crate( } // FIXME: using `from_def_id_and_kind` breaks `rustdoc/masked` for some reason vec![Item { - name: None, - attrs: box krate.attrs.clean(cx), - source: krate.span.clean(cx), + name: Some(name), + attrs: box attrs.clean(cx), + span: krate.span.clean(cx), def_id: crate_def_id, visibility: krate.vis.clean(cx), - kind: box ExternCrateItem(name, orig_name), + kind: box ExternCrateItem { src: orig_name }, }] } @@ -2152,7 +2031,8 @@ fn clean_use_statement( return Vec::new(); } - let inline_attr = import.attrs.lists(sym::doc).get_word_attr(sym::inline); + let attrs = cx.tcx.hir().attrs(import.hir_id()); + let inline_attr = attrs.lists(sym::doc).get_word_attr(sym::inline); let pub_underscore = import.vis.node.is_pub() && name == kw::Underscore; if pub_underscore { @@ -2174,7 +2054,7 @@ fn clean_use_statement( // Don't inline doc(hidden) imports so they can be stripped at a later stage. let mut denied = !import.vis.node.is_pub() || pub_underscore - || import.attrs.iter().any(|a| { + || attrs.iter().any(|a| { a.has_name(sym::doc) && match a.meta_item_list() { Some(l) => { @@ -2214,7 +2094,7 @@ fn clean_use_statement( cx.tcx.parent_module(import.hir_id()).to_def_id(), path.res, name, - Some(import.attrs), + Some(attrs), &mut visited, ) { items.push(Item::from_def_id_and_parts( @@ -2292,14 +2172,14 @@ impl Clean for (&hir::MacroDef<'_>, Option) { if matchers.len() <= 1 { format!( "{}macro {}{} {{\n ...\n}}", - vis.print_with_space(cx.tcx, def_id, &cx.cache), + vis.to_src_with_space(cx.tcx, def_id), name, matchers.iter().map(|span| span.to_src(cx)).collect::(), ) } else { format!( "{}macro {} {{\n{}}}", - vis.print_with_space(cx.tcx, def_id, &cx.cache), + vis.to_src_with_space(cx.tcx, def_id), name, matchers .iter() diff --git a/src/librustdoc/clean/types.rs b/src/librustdoc/clean/types.rs index e3f47a85d5154..1acde8401b21e 100644 --- a/src/librustdoc/clean/types.rs +++ b/src/librustdoc/clean/types.rs @@ -9,15 +9,16 @@ use std::sync::Arc; use std::{slice, vec}; use arrayvec::ArrayVec; + use rustc_ast::attr; use rustc_ast::util::comments::beautify_doc_string; use rustc_ast::{self as ast, AttrStyle}; use rustc_attr::{ConstStability, Deprecation, Stability, StabilityLevel}; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; -use rustc_feature::UnstableFeatures; +use rustc_data_structures::thin_vec::ThinVec; use rustc_hir as hir; -use rustc_hir::def::{CtorKind, Res}; -use rustc_hir::def_id::{CrateNum, DefId, DefIndex}; +use rustc_hir::def::{CtorKind, DefKind, Res}; +use rustc_hir::def_id::{CrateNum, DefId, DefIndex, CRATE_DEF_INDEX}; use rustc_hir::lang_items::LangItem; use rustc_hir::{BodyId, Mutability}; use rustc_index::vec::IndexVec; @@ -32,13 +33,15 @@ use rustc_target::spec::abi::Abi; use crate::clean::cfg::Cfg; use crate::clean::external_path; -use crate::clean::inline; +use crate::clean::inline::{self, print_inlined_const}; use crate::clean::types::Type::{QPath, ResolvedPath}; +use crate::clean::utils::{is_literal_expr, print_const_expr, print_evaluated_const}; use crate::clean::Clean; use crate::core::DocContext; use crate::formats::cache::Cache; use crate::formats::item_type::ItemType; use crate::html::render::cache::ExternalLocation; +use crate::html::render::Context; use self::FnRetTy::*; use self::ItemKind::*; @@ -51,13 +54,12 @@ thread_local!(crate static MAX_DEF_IDX: RefCell> = crate struct Crate { crate name: Symbol, crate src: FileName, - crate module: Option, + crate module: Item, crate externs: Vec<(CrateNum, ExternalCrate)>, - crate primitives: Vec<(DefId, PrimitiveType)>, + crate primitives: ThinVec<(DefId, PrimitiveType)>, // These are later on moved into `CACHEKEY`, leaving the map empty. // Only here so that they can be filtered through the rustdoc passes. crate external_traits: Rc>>, - crate masked_crates: FxHashSet, crate collapsed: bool, } @@ -65,16 +67,143 @@ crate struct Crate { #[derive(Clone, Debug)] crate struct TraitWithExtraInfo { crate trait_: Trait, - crate is_spotlight: bool, + crate is_notable: bool, } #[derive(Clone, Debug)] crate struct ExternalCrate { - crate name: Symbol, - crate src: FileName, + crate crate_num: CrateNum, crate attrs: Attributes, - crate primitives: Vec<(DefId, PrimitiveType)>, - crate keywords: Vec<(DefId, Symbol)>, +} + +impl ExternalCrate { + #[inline] + fn def_id(&self) -> DefId { + DefId { krate: self.crate_num, index: CRATE_DEF_INDEX } + } + + crate fn src(&self, tcx: TyCtxt<'_>) -> FileName { + let krate_span = tcx.def_span(self.def_id()); + tcx.sess.source_map().span_to_filename(krate_span) + } + + crate fn name(&self, tcx: TyCtxt<'_>) -> Symbol { + tcx.crate_name(self.crate_num) + } + + crate fn keywords(&self, tcx: TyCtxt<'_>) -> ThinVec<(DefId, Symbol)> { + let root = self.def_id(); + + let as_keyword = |res: Res| { + if let Res::Def(DefKind::Mod, def_id) = res { + let attrs = tcx.get_attrs(def_id); + let mut keyword = None; + for attr in attrs.lists(sym::doc) { + if attr.has_name(sym::keyword) { + if let Some(v) = attr.value_str() { + keyword = Some(v); + break; + } + } + } + return keyword.map(|p| (def_id, p)); + } + None + }; + if root.is_local() { + tcx.hir() + .krate() + .item + .item_ids + .iter() + .filter_map(|&id| { + let item = tcx.hir().item(id); + match item.kind { + hir::ItemKind::Mod(_) => { + as_keyword(Res::Def(DefKind::Mod, id.def_id.to_def_id())) + } + hir::ItemKind::Use(ref path, hir::UseKind::Single) + if item.vis.node.is_pub() => + { + as_keyword(path.res).map(|(_, prim)| (id.def_id.to_def_id(), prim)) + } + _ => None, + } + }) + .collect() + } else { + tcx.item_children(root).iter().map(|item| item.res).filter_map(as_keyword).collect() + } + } + + crate fn primitives(&self, tcx: TyCtxt<'_>) -> ThinVec<(DefId, PrimitiveType)> { + let root = self.def_id(); + + // Collect all inner modules which are tagged as implementations of + // primitives. + // + // Note that this loop only searches the top-level items of the crate, + // and this is intentional. If we were to search the entire crate for an + // item tagged with `#[doc(primitive)]` then we would also have to + // search the entirety of external modules for items tagged + // `#[doc(primitive)]`, which is a pretty inefficient process (decoding + // all that metadata unconditionally). + // + // In order to keep the metadata load under control, the + // `#[doc(primitive)]` feature is explicitly designed to only allow the + // primitive tags to show up as the top level items in a crate. + // + // Also note that this does not attempt to deal with modules tagged + // duplicately for the same primitive. This is handled later on when + // rendering by delegating everything to a hash map. + let as_primitive = |res: Res| { + if let Res::Def(DefKind::Mod, def_id) = res { + let attrs = tcx.get_attrs(def_id); + let mut prim = None; + for attr in attrs.lists(sym::doc) { + if let Some(v) = attr.value_str() { + if attr.has_name(sym::primitive) { + prim = PrimitiveType::from_symbol(v); + if prim.is_some() { + break; + } + // FIXME: should warn on unknown primitives? + } + } + } + return prim.map(|p| (def_id, p)); + } + None + }; + + if root.is_local() { + tcx.hir() + .krate() + .item + .item_ids + .iter() + .filter_map(|&id| { + let item = tcx.hir().item(id); + match item.kind { + hir::ItemKind::Mod(_) => { + as_primitive(Res::Def(DefKind::Mod, id.def_id.to_def_id())) + } + hir::ItemKind::Use(ref path, hir::UseKind::Single) + if item.vis.node.is_pub() => + { + as_primitive(path.res).map(|(_, prim)| { + // Pretend the primitive is local. + (id.def_id.to_def_id(), prim) + }) + } + _ => None, + } + }) + .collect() + } else { + tcx.item_children(root).iter().map(|item| item.res).filter_map(as_primitive).collect() + } + } } /// Anything with a source location and set of attributes and, optionally, a @@ -82,18 +211,20 @@ crate struct ExternalCrate { /// directly to the AST's concept of an item; it's a strict superset. #[derive(Clone)] crate struct Item { - /// Stringified span - crate source: Span, - /// Not everything has a name. E.g., impls + crate span: Span, + /// The name of this item. + /// Optional because not every item has a name, e.g. impls. crate name: Option, crate attrs: Box, crate visibility: Visibility, + /// Information about this item that is specific to what kind of item it is. + /// E.g., struct vs enum vs function. crate kind: Box, crate def_id: DefId, } // `Item` is used a lot. Make sure it doesn't unintentionally get bigger. -#[cfg(target_arch = "x86_64")] +#[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))] rustc_data_structures::static_assert_size!(Item, 48); impl fmt::Debug for Item { @@ -101,7 +232,7 @@ impl fmt::Debug for Item { let def_id: &dyn fmt::Debug = if self.is_fake() { &"**FAKE**" } else { &self.def_id }; fmt.debug_struct("Item") - .field("source", &self.source) + .field("source", &self.span) .field("name", &self.name) .field("attrs", &self.attrs) .field("kind", &self.kind) @@ -166,7 +297,7 @@ impl Item { debug!("name={:?}, def_id={:?}", name, def_id); // `span_if_local()` lies about functions and only gives the span of the function signature - let source = def_id.as_local().map_or_else( + let span = def_id.as_local().map_or_else( || cx.tcx.def_span(def_id), |local| { let hir = cx.tcx.hir(); @@ -178,7 +309,7 @@ impl Item { def_id, kind: box kind, name, - source: source.clean(cx), + span: span.clean(cx), attrs, visibility: cx.tcx.visibility(def_id).clean(cx), } @@ -190,8 +321,73 @@ impl Item { self.attrs.collapsed_doc_value() } - crate fn links(&self, cache: &Cache) -> Vec { - self.attrs.links(self.def_id.krate, cache) + crate fn links(&self, cx: &Context<'_>) -> Vec { + use crate::html::format::href; + + cx.cache() + .intra_doc_links + .get(&self.def_id) + .map_or(&[][..], |v| v.as_slice()) + .iter() + .filter_map(|ItemLink { link: s, link_text, did, ref fragment }| { + match *did { + Some(did) => { + if let Some((mut href, ..)) = href(did, cx) { + if let Some(ref fragment) = *fragment { + href.push('#'); + href.push_str(fragment); + } + Some(RenderedLink { + original_text: s.clone(), + new_text: link_text.clone(), + href, + }) + } else { + None + } + } + // FIXME(83083): using fragments as a side-channel for + // primitive names is very unfortunate + None => { + let relative_to = &cx.current; + if let Some(ref fragment) = *fragment { + let url = match cx.cache().extern_locations.get(&self.def_id.krate) { + Some(&(_, _, ExternalLocation::Local)) => { + if relative_to[0] == "std" { + let depth = relative_to.len() - 1; + "../".repeat(depth) + } else { + let depth = relative_to.len(); + format!("{}std/", "../".repeat(depth)) + } + } + Some(&(_, _, ExternalLocation::Remote(ref s))) => { + format!("{}/std/", s.trim_end_matches('/')) + } + Some(&(_, _, ExternalLocation::Unknown)) | None => format!( + "https://doc.rust-lang.org/{}/std/", + crate::doc_rust_lang_org_channel(), + ), + }; + // This is a primitive so the url is done "by hand". + let tail = fragment.find('#').unwrap_or_else(|| fragment.len()); + Some(RenderedLink { + original_text: s.clone(), + new_text: link_text.clone(), + href: format!( + "{}primitive.{}.html{}", + url, + &fragment[..tail], + &fragment[tail..] + ), + }) + } else { + panic!("This isn't a primitive?!"); + } + } + } + }) + .collect() } crate fn is_crate(&self) -> bool { @@ -323,7 +519,10 @@ impl Item { #[derive(Clone, Debug)] crate enum ItemKind { - ExternCrateItem(Symbol, Option), + ExternCrateItem { + /// The crate's name, *not* the name it's imported as. + src: Option, + }, ImportItem(Import), StructItem(Struct), UnionItem(Union), @@ -376,7 +575,7 @@ impl ItemKind { TraitItem(t) => t.items.iter(), ImplItem(i) => i.items.iter(), ModuleItem(m) => m.items.iter(), - ExternCrateItem(_, _) + ExternCrateItem { .. } | ImportItem(_) | FunctionItem(_) | TypedefItem(_, _) @@ -557,21 +756,21 @@ impl<'a> FromIterator<&'a DocFragment> for String { } } +/// The attributes on an [`Item`], including attributes like `#[derive(...)]` and `#[inline]`, +/// as well as doc comments. #[derive(Clone, Debug, Default)] crate struct Attributes { crate doc_strings: Vec, crate other_attrs: Vec, crate cfg: Option>, crate span: Option, - /// map from Rust paths to resolved defs and potential URL fragments - crate links: Vec, crate inner_docs: bool, } #[derive(Clone, Debug, Default, PartialEq, Eq, Hash)] /// A link that has not yet been rendered. /// -/// This link will be turned into a rendered link by [`Attributes::links`] +/// This link will be turned into a rendered link by [`Item::links`]. crate struct ItemLink { /// The original link written in the markdown pub(crate) link: String, @@ -797,7 +996,6 @@ impl Attributes { other_attrs, cfg: if cfg == Cfg::True { None } else { Some(Arc::new(cfg)) }, span: sp, - links: vec![], inner_docs, } } @@ -841,79 +1039,24 @@ impl Attributes { if self.doc_strings.is_empty() { None } else { Some(self.doc_strings.iter().collect()) } } - /// Gets links as a vector - /// - /// Cache must be populated before call - crate fn links(&self, krate: CrateNum, cache: &Cache) -> Vec { - use crate::html::format::href; - use crate::html::render::CURRENT_DEPTH; + crate fn get_doc_aliases(&self) -> Box<[String]> { + let mut aliases = FxHashSet::default(); - self.links - .iter() - .filter_map(|ItemLink { link: s, link_text, did, fragment }| { - match *did { - Some(did) => { - if let Some((mut href, ..)) = href(did, cache) { - if let Some(ref fragment) = *fragment { - href.push('#'); - href.push_str(fragment); - } - Some(RenderedLink { - original_text: s.clone(), - new_text: link_text.clone(), - href, - }) - } else { - None - } - } - None => { - if let Some(ref fragment) = *fragment { - let url = match cache.extern_locations.get(&krate) { - Some(&(_, _, ExternalLocation::Local)) => { - let depth = CURRENT_DEPTH.with(|l| l.get()); - "../".repeat(depth) - } - Some(&(_, _, ExternalLocation::Remote(ref s))) => s.to_string(), - Some(&(_, _, ExternalLocation::Unknown)) | None => String::from( - // NOTE: intentionally doesn't pass crate name to avoid having - // different primitive links between crates - if UnstableFeatures::from_environment(None).is_nightly_build() { - "https://doc.rust-lang.org/nightly" - } else { - "https://doc.rust-lang.org" - }, - ), - }; - // This is a primitive so the url is done "by hand". - let tail = fragment.find('#').unwrap_or_else(|| fragment.len()); - Some(RenderedLink { - original_text: s.clone(), - new_text: link_text.clone(), - href: format!( - "{}{}std/primitive.{}.html{}", - url, - if !url.ends_with('/') { "/" } else { "" }, - &fragment[..tail], - &fragment[tail..] - ), - }) - } else { - panic!("This isn't a primitive?!"); + for attr in self.other_attrs.lists(sym::doc).filter(|a| a.has_name(sym::alias)) { + if let Some(values) = attr.meta_item_list() { + for l in values { + match l.literal().unwrap().kind { + ast::LitKind::Str(s, _) => { + aliases.insert(s.as_str().to_string()); } + _ => unreachable!(), } } - }) - .collect() - } - - crate fn get_doc_aliases(&self) -> FxHashSet { - self.other_attrs - .lists(sym::doc) - .filter(|a| a.has_name(sym::alias)) - .filter_map(|a| a.value_str().map(|s| s.to_string())) - .filter(|v| !v.is_empty()) - .collect::>() + } else { + aliases.insert(attr.value_str().map(|s| s.to_string()).unwrap()); + } + } + aliases.into_iter().collect::>().into() } } @@ -922,7 +1065,6 @@ impl PartialEq for Attributes { self.doc_strings == rhs.doc_strings && self.cfg == rhs.cfg && self.span == rhs.span - && self.links == rhs.links && self .other_attrs .iter() @@ -938,7 +1080,6 @@ impl Hash for Attributes { self.doc_strings.hash(hasher); self.cfg.hash(hasher); self.span.hash(hasher); - self.links.hash(hasher); for attr in &self.other_attrs { attr.id.hash(hasher); } @@ -1602,36 +1743,18 @@ impl PrimitiveType { } } - crate fn impls(&self, tcx: TyCtxt<'_>) -> &'static ArrayVec<[DefId; 4]> { + crate fn impls(&self, tcx: TyCtxt<'_>) -> &'static ArrayVec { Self::all_impls(tcx).get(self).expect("missing impl for primitive type") } - crate fn all_impls(tcx: TyCtxt<'_>) -> &'static FxHashMap> { - static CELL: OnceCell>> = OnceCell::new(); + crate fn all_impls(tcx: TyCtxt<'_>) -> &'static FxHashMap> { + static CELL: OnceCell>> = OnceCell::new(); CELL.get_or_init(move || { use self::PrimitiveType::*; - /// A macro to create a FxHashMap. - /// - /// Example: - /// - /// ``` - /// let letters = map!{"a" => "b", "c" => "d"}; - /// ``` - /// - /// Trailing commas are allowed. - /// Commas between elements are required (even if the expression is a block). - macro_rules! map { - ($( $key: expr => $val: expr ),* $(,)*) => {{ - let mut map = ::rustc_data_structures::fx::FxHashMap::default(); - $( map.insert($key, $val); )* - map - }} - } - let single = |a: Option| a.into_iter().collect(); - let both = |a: Option, b: Option| -> ArrayVec<_> { + let both = |a: Option, b: Option| -> ArrayVec<_, 4> { a.into_iter().chain(b).collect() }; @@ -1803,8 +1926,13 @@ impl From for PrimitiveType { #[derive(Copy, Clone, Debug)] crate enum Visibility { + /// `pub` Public, + /// Visibility inherited from parent. + /// + /// For example, this is the visibility of private items and of enum variants. Inherited, + /// `pub(crate)`, `pub(super)`, or `pub(in path::to::somewhere)` Restricted(DefId), } @@ -1853,7 +1981,8 @@ crate enum Variant { Struct(VariantStruct), } -/// Small wrapper around `rustc_span::Span` that adds helper methods and enforces calling `source_callsite`. +/// Small wrapper around [`rustc_span::Span]` that adds helper methods +/// and enforces calling [`rustc_span::Span::source_callsite()`]. #[derive(Clone, Debug)] crate struct Span(rustc_span::Span); @@ -1865,12 +1994,12 @@ impl Span { Self(sp.source_callsite()) } - crate fn dummy() -> Self { - Self(rustc_span::DUMMY_SP) + crate fn inner(&self) -> rustc_span::Span { + self.0 } - crate fn span(&self) -> rustc_span::Span { - self.0 + crate fn dummy() -> Self { + Self(rustc_span::DUMMY_SP) } crate fn is_dummy(&self) -> bool { @@ -1983,9 +2112,58 @@ crate struct Static { #[derive(Clone, PartialEq, Eq, Hash, Debug)] crate struct Constant { crate type_: Type, - crate expr: String, - crate value: Option, - crate is_literal: bool, + crate kind: ConstantKind, +} + +#[derive(Clone, PartialEq, Eq, Hash, Debug)] +crate enum ConstantKind { + /// This is the wrapper around `ty::Const` for a non-local constant. Because it doesn't have a + /// `BodyId`, we need to handle it on its own. + /// + /// Note that `ty::Const` includes generic parameters, and may not always be uniquely identified + /// by a DefId. So this field must be different from `Extern`. + TyConst { expr: String }, + /// A constant (expression) that's not an item or associated item. These are usually found + /// nested inside types (e.g., array lengths) or expressions (e.g., repeat counts), and also + /// used to define explicit discriminant values for enum variants. + Anonymous { body: BodyId }, + /// A constant from a different crate. + Extern { def_id: DefId }, + /// `const FOO: u32 = ...;` + Local { def_id: DefId, body: BodyId }, +} + +impl Constant { + crate fn expr(&self, tcx: TyCtxt<'_>) -> String { + match self.kind { + ConstantKind::TyConst { ref expr } => expr.clone(), + ConstantKind::Extern { def_id } => print_inlined_const(tcx, def_id), + ConstantKind::Local { body, .. } | ConstantKind::Anonymous { body } => { + print_const_expr(tcx, body) + } + } + } + + crate fn value(&self, tcx: TyCtxt<'_>) -> Option { + match self.kind { + ConstantKind::TyConst { .. } | ConstantKind::Anonymous { .. } => None, + ConstantKind::Extern { def_id } | ConstantKind::Local { def_id, .. } => { + print_evaluated_const(tcx, def_id) + } + } + } + + crate fn is_literal(&self, tcx: TyCtxt<'_>) -> bool { + match self.kind { + ConstantKind::TyConst { .. } => false, + ConstantKind::Extern { def_id } => def_id.as_local().map_or(false, |def_id| { + is_literal_expr(tcx, tcx.hir().local_def_id_to_hir_id(def_id)) + }), + ConstantKind::Local { body, .. } | ConstantKind::Anonymous { body } => { + is_literal_expr(tcx, body.hir_id) + } + } + } } #[derive(Clone, Debug)] diff --git a/src/librustdoc/clean/utils.rs b/src/librustdoc/clean/utils.rs index a64e8c21c462e..55a0cb42a2081 100644 --- a/src/librustdoc/clean/utils.rs +++ b/src/librustdoc/clean/utils.rs @@ -1,13 +1,12 @@ use crate::clean::auto_trait::AutoTraitFinder; use crate::clean::blanket_impl::BlanketImplFinder; use crate::clean::{ - inline, Clean, Crate, ExternalCrate, Generic, GenericArg, GenericArgs, ImportSource, Item, - ItemKind, Lifetime, MacroKind, Path, PathSegment, Primitive, PrimitiveType, ResolvedPath, Type, - TypeBinding, TypeKind, + inline, Clean, Crate, Generic, GenericArg, GenericArgs, ImportSource, Item, ItemKind, Lifetime, + MacroKind, Path, PathSegment, Primitive, PrimitiveType, ResolvedPath, Type, TypeBinding, + TypeKind, }; use crate::core::DocContext; -use rustc_data_structures::fx::FxHashSet; use rustc_hir as hir; use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::{DefId, LOCAL_CRATE}; @@ -38,7 +37,6 @@ crate fn krate(cx: &mut DocContext<'_>) -> Crate { // Clean the crate, translating the entire librustc_ast AST to one that is // understood by rustdoc. let mut module = module.clean(cx); - let mut masked_crates = FxHashSet::default(); match *module.kind { ItemKind::ModuleItem(ref module) => { @@ -49,14 +47,18 @@ crate fn krate(cx: &mut DocContext<'_>) -> Crate { && (it.attrs.has_doc_flag(sym::masked) || cx.tcx.is_compiler_builtins(it.def_id.krate)) { - masked_crates.insert(it.def_id.krate); + cx.cache.masked_crates.insert(it.def_id.krate); } } } _ => unreachable!(), } - let ExternalCrate { name, src, primitives, keywords, .. } = LOCAL_CRATE.clean(cx); + let local_crate = LOCAL_CRATE.clean(cx); + let src = local_crate.src(cx.tcx); + let name = local_crate.name(cx.tcx); + let primitives = local_crate.primitives(cx.tcx); + let keywords = local_crate.keywords(cx.tcx); { let m = match *module.kind { ItemKind::ModuleItem(ref mut m) => m, @@ -78,11 +80,10 @@ crate fn krate(cx: &mut DocContext<'_>) -> Crate { Crate { name, src, - module: Some(module), + module, externs, primitives, external_traits: cx.external_traits.clone(), - masked_crates, collapsed: false, } } @@ -100,7 +101,7 @@ fn external_generic_args( .iter() .filter_map(|kind| match kind.unpack() { GenericArgKind::Lifetime(lt) => match lt { - ty::ReLateBound(_, ty::BoundRegion { kind: ty::BrAnon(_) }) => { + ty::ReLateBound(_, ty::BoundRegion { kind: ty::BrAnon(_), .. }) => { Some(GenericArg::Lifetime(Lifetime::elided())) } _ => lt.clean(cx).map(GenericArg::Lifetime), @@ -254,19 +255,9 @@ crate fn name_from_pat(p: &hir::Pat<'_>) -> Symbol { debug!("trying to get a name from pattern: {:?}", p); Symbol::intern(&match p.kind { - PatKind::Wild => return kw::Underscore, + PatKind::Wild | PatKind::Struct(..) => return kw::Underscore, PatKind::Binding(_, _, ident, _) => return ident.name, PatKind::TupleStruct(ref p, ..) | PatKind::Path(ref p) => qpath_to_string(p), - PatKind::Struct(ref name, ref fields, etc) => format!( - "{} {{ {}{} }}", - qpath_to_string(name), - fields - .iter() - .map(|fp| format!("{}: {}", fp.ident, name_from_pat(&fp.pat))) - .collect::>() - .join(", "), - if etc { ", .." } else { "" } - ), PatKind::Or(ref pats) => pats .iter() .map(|p| name_from_pat(&**p).to_string()) @@ -299,12 +290,12 @@ crate fn name_from_pat(p: &hir::Pat<'_>) -> Symbol { crate fn print_const(cx: &DocContext<'_>, n: &'tcx ty::Const<'_>) -> String { match n.val { - ty::ConstKind::Unevaluated(def, _, promoted) => { + ty::ConstKind::Unevaluated(ty::Unevaluated { def, substs: _, promoted }) => { let mut s = if let Some(def) = def.as_local() { let hir_id = cx.tcx.hir().local_def_id_to_hir_id(def.did); print_const_expr(cx.tcx, cx.tcx.hir().body_owned_by(hir_id)) } else { - inline::print_inlined_const(cx, def.did) + inline::print_inlined_const(cx.tcx, def.did) }; if let Some(promoted) = promoted { s.push_str(&format!("::{:?}", promoted)) @@ -327,15 +318,15 @@ crate fn print_const(cx: &DocContext<'_>, n: &'tcx ty::Const<'_>) -> String { } } -crate fn print_evaluated_const(cx: &DocContext<'_>, def_id: DefId) -> Option { - cx.tcx.const_eval_poly(def_id).ok().and_then(|val| { - let ty = cx.tcx.type_of(def_id); +crate fn print_evaluated_const(tcx: TyCtxt<'_>, def_id: DefId) -> Option { + tcx.const_eval_poly(def_id).ok().and_then(|val| { + let ty = tcx.type_of(def_id); match (val, ty.kind()) { (_, &ty::Ref(..)) => None, (ConstValue::Scalar(_), &ty::Adt(_, _)) => None, (ConstValue::Scalar(_), _) => { - let const_ = ty::Const::from_value(cx.tcx, val, ty); - Some(print_const_with_custom_print_scalar(cx, const_)) + let const_ = ty::Const::from_value(tcx, val, ty); + Some(print_const_with_custom_print_scalar(tcx, const_)) } _ => None, } @@ -352,7 +343,7 @@ fn format_integer_with_underscore_sep(num: &str) -> String { .collect() } -fn print_const_with_custom_print_scalar(cx: &DocContext<'_>, ct: &'tcx ty::Const<'tcx>) -> String { +fn print_const_with_custom_print_scalar(tcx: TyCtxt<'_>, ct: &'tcx ty::Const<'tcx>) -> String { // Use a slightly different format for integer types which always shows the actual value. // For all other types, fallback to the original `pretty_print_const`. match (ct.val, ct.ty.kind()) { @@ -360,8 +351,8 @@ fn print_const_with_custom_print_scalar(cx: &DocContext<'_>, ct: &'tcx ty::Const format!("{}{}", format_integer_with_underscore_sep(&int.to_string()), ui.name_str()) } (ty::ConstKind::Value(ConstValue::Scalar(int)), ty::Int(i)) => { - let ty = cx.tcx.lift(ct.ty).unwrap(); - let size = cx.tcx.layout_of(ty::ParamEnv::empty().and(ty)).unwrap().size; + let ty = tcx.lift(ct.ty).unwrap(); + let size = tcx.layout_of(ty::ParamEnv::empty().and(ty)).unwrap().size; let data = int.assert_bits(size); let sign_extended_data = size.sign_extend(data) as i128; @@ -375,8 +366,8 @@ fn print_const_with_custom_print_scalar(cx: &DocContext<'_>, ct: &'tcx ty::Const } } -crate fn is_literal_expr(cx: &DocContext<'_>, hir_id: hir::HirId) -> bool { - if let hir::Node::Expr(expr) = cx.tcx.hir().get(hir_id) { +crate fn is_literal_expr(tcx: TyCtxt<'_>, hir_id: hir::HirId) -> bool { + if let hir::Node::Expr(expr) = tcx.hir().get(hir_id) { if let hir::ExprKind::Lit(_) = &expr.kind { return true; } @@ -414,7 +405,7 @@ crate fn resolve_type(cx: &mut DocContext<'_>, path: Path, id: hir::HirId) -> Ty return Generic(kw::SelfUpper); } Res::Def(DefKind::TyParam, _) if path.segments.len() == 1 => { - return Generic(Symbol::intern(&format!("{:#}", path.print(&cx.cache)))); + return Generic(Symbol::intern(&path.whole_name())); } Res::SelfTy(..) | Res::Def(DefKind::TyParam | DefKind::AssocTy, _) => true, _ => false, @@ -534,3 +525,14 @@ crate fn has_doc_flag(attrs: ty::Attributes<'_>, flag: Symbol) -> bool { && attr.meta_item_list().map_or(false, |l| rustc_attr::list_contains_name(&l, flag)) }) } + +/// Return a channel suitable for using in a `doc.rust-lang.org/{channel}` format string. +crate fn doc_rust_lang_org_channel() -> &'static str { + match env!("CFG_RELEASE_CHANNEL") { + "stable" => env!("CFG_RELEASE_NUM"), + "beta" => "beta", + "nightly" | "dev" => "nightly", + // custom build of rustdoc maybe? link to the stable docs just in case + _ => "", + } +} diff --git a/src/librustdoc/config.rs b/src/librustdoc/config.rs index de6942968ea0d..8f10ab2d3aca7 100644 --- a/src/librustdoc/config.rs +++ b/src/librustdoc/config.rs @@ -3,6 +3,7 @@ use std::convert::TryFrom; use std::ffi::OsStr; use std::fmt; use std::path::PathBuf; +use std::str::FromStr; use rustc_data_structures::fx::FxHashMap; use rustc_session::config::{self, parse_crate_types_from_list, parse_externs, CrateType}; @@ -96,8 +97,7 @@ crate struct Options { crate maybe_sysroot: Option, /// Lint information passed over the command-line. crate lint_opts: Vec<(String, Level)>, - /// Whether to ask rustc to describe the lints it knows. Practically speaking, this will not be - /// used, since we abort if we have no input file, but it's included for completeness. + /// Whether to ask rustc to describe the lints it knows. crate describe_lints: bool, /// What level to cap lints at. crate lint_cap: Option, @@ -153,6 +153,8 @@ crate struct Options { /// If this option is set to `true`, rustdoc will only run checks and not generate /// documentation. crate run_check: bool, + /// Whether doctests should emit unused externs + crate json_unused_externs: bool, } impl fmt::Debug for Options { @@ -266,6 +268,34 @@ crate struct RenderOptions { /// If `true`, generate a JSON file in the crate folder instead of HTML redirection files. crate generate_redirect_map: bool, crate unstable_features: rustc_feature::UnstableFeatures, + crate emit: Vec, +} + +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +crate enum EmitType { + Unversioned, + Toolchain, + InvocationSpecific, +} + +impl FromStr for EmitType { + type Err = (); + + fn from_str(s: &str) -> Result { + use EmitType::*; + match s { + "unversioned-shared-resources" => Ok(Unversioned), + "toolchain-shared-resources" => Ok(Toolchain), + "invocation-specific" => Ok(InvocationSpecific), + _ => Err(()), + } + } +} + +impl RenderOptions { + crate fn should_emit_crate(&self) -> bool { + self.emit.is_empty() || self.emit.contains(&EmitType::InvocationSpecific) + } } impl Options { @@ -315,8 +345,16 @@ impl Options { return Err(0); } + if matches.opt_strs("print").iter().any(|opt| opt == "unversioned-files") { + for file in crate::html::render::FILES_UNVERSIONED.keys() { + println!("{}", file); + } + return Err(0); + } + let color = config::parse_color(&matches); - let (json_rendered, _artifacts) = config::parse_json(&matches); + let config::JsonConfig { json_rendered, json_unused_externs, .. } = + config::parse_json(&matches); let error_format = config::parse_error_format(&matches, color, json_rendered); let codegen_options = build_codegen_options(matches, error_format); @@ -327,6 +365,30 @@ impl Options { // check for deprecated options check_deprecated_options(&matches, &diag); + let mut emit = Vec::new(); + for list in matches.opt_strs("emit") { + for kind in list.split(',') { + match kind.parse() { + Ok(kind) => emit.push(kind), + Err(()) => { + diag.err(&format!("unrecognized emission type: {}", kind)); + return Err(1); + } + } + } + } + + // check for `--output-format=json` + if !matches!(matches.opt_str("output-format").as_deref(), None | Some("html")) + && !matches.opt_present("show-coverage") + && !nightly_options::is_unstable_enabled(matches) + { + rustc_session::early_error( + error_format, + "the -Z unstable-options flag must be passed to enable --output-format for documentation generation (see https://github.com/rust-lang/rust/issues/76578)", + ); + } + let to_check = matches.opt_strs("check-theme"); if !to_check.is_empty() { let paths = theme::load_css_paths(static_files::themes::LIGHT.as_bytes()); @@ -432,7 +494,9 @@ impl Options { return Err(1); } if theme_file.extension() != Some(OsStr::new("css")) { - diag.struct_err(&format!("invalid argument: \"{}\"", theme_s)).emit(); + diag.struct_err(&format!("invalid argument: \"{}\"", theme_s)) + .help("arguments to --theme must have a .css extension") + .emit(); return Err(1); } let (success, ret) = theme::test_theme_against(&theme_file, &paths, &diag); @@ -458,7 +522,6 @@ impl Options { let edition = config::parse_crate_edition(&matches); let mut id_map = html::markdown::IdMap::new(); - id_map.populate(&html::render::INITIAL_IDS); let external_html = match ExternalHtml::load( &matches.opt_strs("html-in-header"), &matches.opt_strs("html-before-content"), @@ -522,13 +585,7 @@ impl Options { let output_format = match matches.opt_str("output-format") { Some(s) => match OutputFormat::try_from(s.as_str()) { Ok(out_fmt) => { - if out_fmt.is_json() - && !(show_coverage || nightly_options::match_is_nightly_build(matches)) - { - diag.struct_err("json output format isn't supported for doc generation") - .emit(); - return Err(1); - } else if !out_fmt.is_json() && show_coverage { + if !out_fmt.is_json() && show_coverage { diag.struct_err( "html output format isn't supported for the --show-coverage option", ) @@ -634,9 +691,11 @@ impl Options { unstable_features: rustc_feature::UnstableFeatures::from_environment( crate_name.as_deref(), ), + emit, }, crate_name, output_format, + json_unused_externs, }) } @@ -648,19 +707,12 @@ impl Options { /// Prints deprecation warnings for deprecated options fn check_deprecated_options(matches: &getopts::Matches, diag: &rustc_errors::Handler) { - let deprecated_flags = ["input-format", "output-format", "no-defaults", "passes"]; + let deprecated_flags = ["input-format", "no-defaults", "passes"]; for flag in deprecated_flags.iter() { if matches.opt_present(flag) { - if *flag == "output-format" - && (matches.opt_present("show-coverage") - || nightly_options::match_is_nightly_build(matches)) - { - continue; - } - let mut err = - diag.struct_warn(&format!("the '{}' flag is considered deprecated", flag)); - err.warn( + let mut err = diag.struct_warn(&format!("the `{}` flag is deprecated", flag)); + err.note( "see issue #44136 \ for more information", ); diff --git a/src/librustdoc/core.rs b/src/librustdoc/core.rs index 9cb0649b314f5..be7bff1a29c2b 100644 --- a/src/librustdoc/core.rs +++ b/src/librustdoc/core.rs @@ -1,11 +1,12 @@ +use rustc_ast as ast; 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::{Namespace::TypeNS, Res}; -use rustc_hir::def_id::{CrateNum, DefId, DefIndex, LocalDefId, CRATE_DEF_INDEX, LOCAL_CRATE}; +use rustc_hir::def::Res; +use rustc_hir::def_id::{CrateNum, DefId, DefIndex, LocalDefId, LOCAL_CRATE}; use rustc_hir::HirId; use rustc_hir::{ intravisit::{self, NestedVisitorMap, Visitor}, @@ -22,7 +23,7 @@ use rustc_session::DiagnosticOutput; use rustc_session::Session; use rustc_span::source_map; use rustc_span::symbol::sym; -use rustc_span::DUMMY_SP; +use rustc_span::Span; use std::cell::RefCell; use std::collections::hash_map::Entry; @@ -76,7 +77,7 @@ crate struct DocContext<'tcx> { /// /// See `collect_intra_doc_links::traits_implemented_by` for more details. /// `map>` - crate module_trait_cache: RefCell>>, + crate module_trait_cache: FxHashMap>, /// This same cache is used throughout rustdoc, including in [`crate::html::render`]. crate cache: Cache, /// Used by [`clean::inline`] to tell if an item has already been inlined. @@ -169,13 +170,13 @@ impl<'tcx> DocContext<'tcx> { /// Like `hir().local_def_id_to_hir_id()`, but skips calling it on fake DefIds. /// (This avoids a slice-index-out-of-bounds panic.) - crate fn as_local_hir_id(&self, def_id: DefId) -> Option { + crate fn as_local_hir_id(tcx: TyCtxt<'_>, def_id: DefId) -> Option { if MAX_DEF_IDX.with(|m| { m.borrow().get(&def_id.krate).map(|&idx| idx <= def_id.index).unwrap_or(false) }) { None } else { - def_id.as_local().map(|def_id| self.tcx.hir().local_def_id_to_hir_id(def_id)) + def_id.as_local().map(|def_id| tcx.hir().local_def_id_to_hir_id(def_id)) } } } @@ -311,6 +312,7 @@ crate fn create_config( diagnostic_output: DiagnosticOutput::Default, stderr: None, lint_caps, + parse_sess_created: None, register_lints: Some(box crate::lint::register_lints), override_queries: Some(|_sess, providers, _external_providers| { // Most lints will require typechecking, so just don't run them. @@ -347,49 +349,24 @@ crate fn create_config( } crate fn create_resolver<'a>( - externs: config::Externs, queries: &Queries<'a>, sess: &Session, ) -> Rc> { - let extern_names: Vec = externs - .iter() - .filter(|(_, entry)| entry.add_prelude) - .map(|(name, _)| name) - .cloned() - .collect(); - let parts = abort_on_err(queries.expansion(), sess).peek(); - let resolver = parts.1.borrow(); - - // Before we actually clone it, let's force all the extern'd crates to - // actually be loaded, just in case they're only referred to inside - // intra-doc links - 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) - } - } - }); - }); + let (krate, resolver, _) = &*parts; + let resolver = resolver.borrow().clone(); - // Now we're good to clone the resolver because everything should be loaded - resolver.clone() + let mut loader = crate::passes::collect_intra_doc_links::IntraLinkCrateLoader::new(resolver); + ast::visit::walk_crate(&mut loader, krate); + + loader.resolver } crate fn run_global_ctxt( tcx: TyCtxt<'_>, resolver: Rc>, mut default_passes: passes::DefaultPassOption, - mut manual_passes: Vec, + manual_passes: Vec, render_options: RenderOptions, output_format: OutputFormat, ) -> (clean::Crate, RenderOptions, Cache) { @@ -450,7 +427,7 @@ crate fn run_global_ctxt( .cloned() .filter(|trait_def_id| tcx.trait_is_auto(*trait_def_id)) .collect(), - module_trait_cache: RefCell::new(FxHashMap::default()), + module_trait_cache: FxHashMap::default(), cache: Cache::new(access_levels, render_options.document_private), inlined: FxHashSet::default(), output_format, @@ -463,79 +440,98 @@ crate fn run_global_ctxt( if let Some(sized_trait_did) = ctxt.tcx.lang_items().sized_trait() { let mut sized_trait = build_external_trait(&mut ctxt, sized_trait_did); sized_trait.is_auto = true; - ctxt.external_traits.borrow_mut().insert( - sized_trait_did, - TraitWithExtraInfo { trait_: sized_trait, is_spotlight: false }, - ); + ctxt.external_traits + .borrow_mut() + .insert(sized_trait_did, TraitWithExtraInfo { trait_: sized_trait, is_notable: false }); } debug!("crate: {:?}", tcx.hir().krate()); let mut krate = tcx.sess.time("clean_crate", || clean::krate(&mut ctxt)); - if let Some(ref m) = krate.module { - if m.doc_value().map(|d| d.is_empty()).unwrap_or(true) { - let help = "The following guide may be of use:\n\ - https://doc.rust-lang.org/nightly/rustdoc/how-to-write-documentation.html"; - tcx.struct_lint_node( - crate::lint::MISSING_CRATE_LEVEL_DOCS, - ctxt.as_local_hir_id(m.def_id).unwrap(), - |lint| { - let mut diag = - lint.build("no documentation found for this crate's top-level module"); - diag.help(help); - diag.emit(); - }, - ); - } + if krate.module.doc_value().map(|d| d.is_empty()).unwrap_or(true) { + let help = format!( + "The following guide may be of use:\n\ + https://doc.rust-lang.org/{}/rustdoc/how-to-write-documentation.html", + crate::doc_rust_lang_org_channel(), + ); + tcx.struct_lint_node( + crate::lint::MISSING_CRATE_LEVEL_DOCS, + DocContext::as_local_hir_id(tcx, krate.module.def_id).unwrap(), + |lint| { + let mut diag = + lint.build("no documentation found for this crate's top-level module"); + diag.help(&help); + diag.emit(); + }, + ); } - fn report_deprecated_attr(name: &str, diag: &rustc_errors::Handler) { - let mut msg = diag - .struct_warn(&format!("the `#![doc({})]` attribute is considered deprecated", name)); - msg.warn( + fn report_deprecated_attr(name: &str, diag: &rustc_errors::Handler, sp: Span) { + let mut msg = + diag.struct_span_warn(sp, &format!("the `#![doc({})]` attribute is deprecated", name)); + msg.note( "see issue #44136 \ for more information", ); if name == "no_default_passes" { msg.help("you may want to use `#![doc(document_private_items)]`"); + } else if name.starts_with("plugins") { + msg.warn("`#![doc(plugins = \"...\")]` no longer functions; see CVE-2018-1000622 "); } msg.emit(); } + let parse_pass = |name: &str, sp: Option| { + if let Some(pass) = passes::find_pass(name) { + Some(ConditionalPass::always(pass)) + } else { + let msg = &format!("ignoring unknown pass `{}`", name); + let mut warning = if let Some(sp) = sp { + tcx.sess.struct_span_warn(sp, msg) + } else { + tcx.sess.struct_warn(msg) + }; + if name == "collapse-docs" { + warning.note("the `collapse-docs` pass was removed in #80261 "); + } + warning.emit(); + None + } + }; + + let mut manual_passes: Vec<_> = + manual_passes.into_iter().flat_map(|name| parse_pass(&name, None)).collect(); + // Process all of the crate attributes, extracting plugin metadata along // with the passes which we are supposed to run. - for attr in krate.module.as_ref().unwrap().attrs.lists(sym::doc) { + for attr in krate.module.attrs.lists(sym::doc) { let diag = ctxt.sess().diagnostic(); let name = attr.name_or_empty(); if attr.is_word() { if name == sym::no_default_passes { - report_deprecated_attr("no_default_passes", diag); + report_deprecated_attr("no_default_passes", diag, attr.span()); if default_passes == passes::DefaultPassOption::Default { default_passes = passes::DefaultPassOption::None; } } } else if let Some(value) = attr.value_str() { - let sink = match name { + match name { sym::passes => { - report_deprecated_attr("passes = \"...\"", diag); - &mut manual_passes + report_deprecated_attr("passes = \"...\"", diag, attr.span()); } sym::plugins => { - report_deprecated_attr("plugins = \"...\"", diag); - eprintln!( - "WARNING: `#![doc(plugins = \"...\")]` \ - no longer functions; see CVE-2018-1000622" - ); + report_deprecated_attr("plugins = \"...\"", diag, attr.span()); continue; } _ => continue, }; for name in value.as_str().split_whitespace() { - sink.push(name.to_string()); + let span = attr.name_value_literal_span().unwrap_or(attr.span()); + manual_passes.extend(parse_pass(name, Some(span))); } } @@ -544,17 +540,7 @@ crate fn run_global_ctxt( } } - let passes = passes::defaults(default_passes).iter().copied().chain( - manual_passes.into_iter().flat_map(|name| { - if let Some(pass) = passes::find_pass(&name) { - Some(ConditionalPass::always(pass)) - } else { - error!("unknown pass {}, skipping", name); - None - } - }), - ); - + let passes = passes::defaults(default_passes).iter().copied().chain(manual_passes); info!("Executing passes"); for p in passes { diff --git a/src/librustdoc/doctest.rs b/src/librustdoc/doctest.rs index 27ce064669d2c..6f6ed0eb68413 100644 --- a/src/librustdoc/doctest.rs +++ b/src/librustdoc/doctest.rs @@ -1,5 +1,5 @@ use rustc_ast as ast; -use rustc_data_structures::fx::FxHashMap; +use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_data_structures::sync::Lrc; use rustc_errors::{ColorConfig, ErrorReported}; use rustc_hir as hir; @@ -23,6 +23,8 @@ use std::panic; use std::path::PathBuf; use std::process::{self, Command, Stdio}; use std::str; +use std::sync::atomic::{AtomicUsize, Ordering}; +use std::sync::{Arc, Mutex}; use crate::clean::Attributes; use crate::config::Options; @@ -95,6 +97,7 @@ crate fn run(options: Options) -> Result<(), ErrorReported> { diagnostic_output: DiagnosticOutput::Default, stderr: None, lint_caps, + parse_sess_created: None, register_lints: Some(box crate::lint::register_lints), override_queries: None, make_codegen_backend: None, @@ -103,28 +106,32 @@ crate fn run(options: Options) -> Result<(), ErrorReported> { let mut test_args = options.test_args.clone(); let display_warnings = options.display_warnings; + let externs = options.externs.clone(); + let json_unused_externs = options.json_unused_externs; - let tests = interface::run_compiler(config, |compiler| { + let res = interface::run_compiler(config, |compiler| { compiler.enter(|queries| { - let lower_to_hir = queries.lower_to_hir()?; - - let mut opts = scrape_test_config(lower_to_hir.peek().0); - opts.display_warnings |= options.display_warnings; - let enable_per_target_ignores = options.enable_per_target_ignores; - let mut collector = Collector::new( - queries.crate_name()?.peek().to_string(), - options, - false, - opts, - Some(compiler.session().parse_sess.clone_source_map()), - None, - enable_per_target_ignores, - ); + let _lower_to_hir = queries.lower_to_hir()?; + let crate_name = queries.crate_name()?.peek().to_string(); let mut global_ctxt = queries.global_ctxt()?.take(); - global_ctxt.enter(|tcx| { + let collector = global_ctxt.enter(|tcx| { let krate = tcx.hir().krate(); + let crate_attrs = tcx.hir().attrs(CRATE_HIR_ID); + + let mut opts = scrape_test_config(crate_attrs); + opts.display_warnings |= options.display_warnings; + let enable_per_target_ignores = options.enable_per_target_ignores; + let mut collector = Collector::new( + crate_name, + options, + false, + opts, + Some(compiler.session().parse_sess.clone_source_map()), + None, + enable_per_target_ignores, + ); let mut hir_collector = HirCollector { sess: compiler.session(), @@ -137,22 +144,26 @@ crate fn run(options: Options) -> Result<(), ErrorReported> { }; hir_collector.visit_testable( "".to_string(), - &krate.item.attrs, CRATE_HIR_ID, - krate.item.span, + krate.item.inner, |this| { intravisit::walk_crate(this, krate); }, ); + + collector }); compiler.session().abort_if_errors(); - let ret: Result<_, ErrorReported> = Ok(collector.tests); + let unused_extern_reports = collector.unused_extern_reports.clone(); + let compiling_test_count = collector.compiling_test_count.load(Ordering::SeqCst); + let ret: Result<_, ErrorReported> = + Ok((collector.tests, unused_extern_reports, compiling_test_count)); ret }) }); - let tests = match tests { - Ok(tests) => tests, + let (tests, unused_extern_reports, compiling_test_count) = match res { + Ok(res) => res, Err(ErrorReported) => return Err(ErrorReported), }; @@ -164,19 +175,55 @@ crate fn run(options: Options) -> Result<(), ErrorReported> { Some(testing::Options::new().display_output(display_warnings)), ); + // Collect and warn about unused externs, but only if we've gotten + // reports for each doctest + if json_unused_externs { + let unused_extern_reports: Vec<_> = + std::mem::take(&mut unused_extern_reports.lock().unwrap()); + if unused_extern_reports.len() == compiling_test_count { + let extern_names = externs.iter().map(|(name, _)| name).collect::>(); + let mut unused_extern_names = unused_extern_reports + .iter() + .map(|uexts| uexts.unused_extern_names.iter().collect::>()) + .fold(extern_names, |uextsa, uextsb| { + uextsa.intersection(&uextsb).map(|v| *v).collect::>() + }) + .iter() + .map(|v| (*v).clone()) + .collect::>(); + unused_extern_names.sort(); + // Take the most severe lint level + let lint_level = unused_extern_reports + .iter() + .map(|uexts| uexts.lint_level.as_str()) + .max_by_key(|v| match *v { + "warn" => 1, + "deny" => 2, + "forbid" => 3, + // The allow lint level is not expected, + // as if allow is specified, no message + // is to be emitted. + v => unreachable!("Invalid lint level '{}'", v), + }) + .unwrap_or("warn") + .to_string(); + let uext = UnusedExterns { lint_level, unused_extern_names }; + let unused_extern_json = serde_json::to_string(&uext).unwrap(); + eprintln!("{}", unused_extern_json); + } + } + Ok(()) } // Look for `#![doc(test(no_crate_inject))]`, used by crates in the std facade. -fn scrape_test_config(krate: &::rustc_hir::Crate<'_>) -> TestOptions { +fn scrape_test_config(attrs: &[ast::Attribute]) -> TestOptions { use rustc_ast_pretty::pprust; let mut opts = TestOptions { no_crate_inject: false, display_warnings: false, attrs: Vec::new() }; - let test_attrs: Vec<_> = krate - .item - .attrs + let test_attrs: Vec<_> = attrs .iter() .filter(|a| a.has_name(sym::doc)) .flat_map(|a| a.meta_item_list().unwrap_or_else(Vec::new)) @@ -233,6 +280,18 @@ impl DirState { } } +// NOTE: Keep this in sync with the equivalent structs in rustc +// and cargo. +// We could unify this struct the one in rustc but they have different +// ownership semantics, so doing so would create wasteful allocations. +#[derive(serde::Serialize, serde::Deserialize)] +struct UnusedExterns { + /// Lint level of the unused_crate_dependencies lint + lint_level: String, + /// List of unused externs by their names. + unused_extern_names: Vec, +} + fn run_test( test: &str, cratename: &str, @@ -251,6 +310,7 @@ fn run_test( outdir: DirState, path: PathBuf, test_id: &str, + report_unused_externs: impl Fn(UnusedExterns), ) -> Result<(), TestFailure> { let (test, line_offset, supports_color) = make_test(test, Some(cratename), as_test_harness, opts, edition, Some(test_id)); @@ -276,6 +336,12 @@ fn run_test( if as_test_harness { compiler.arg("--test"); } + if options.json_unused_externs && !compile_fail { + compiler.arg("--error-format=json"); + compiler.arg("--json").arg("unused-externs"); + compiler.arg("-Z").arg("unstable-options"); + compiler.arg("-W").arg("unused_crate_dependencies"); + } for lib_str in &options.lib_strs { compiler.arg("-L").arg(&lib_str); } @@ -335,7 +401,26 @@ fn run_test( eprint!("{}", self.0); } } - let out = str::from_utf8(&output.stderr).unwrap(); + let mut out_lines = str::from_utf8(&output.stderr) + .unwrap() + .lines() + .filter(|l| { + if let Ok(uext) = serde_json::from_str::(l) { + report_unused_externs(uext); + false + } else { + true + } + }) + .collect::>(); + + // Add a \n to the end to properly terminate the last line, + // but only if there was output to be printed + if out_lines.len() > 0 { + out_lines.push(""); + } + + let out = out_lines.join("\n"); let _bomb = Bomb(&out); match (output.status.success(), compile_fail) { (true, true) => { @@ -719,6 +804,8 @@ crate struct Collector { source_map: Option>, filename: Option, visited_tests: FxHashMap<(String, usize), usize>, + unused_extern_reports: Arc>>, + compiling_test_count: AtomicUsize, } impl Collector { @@ -743,6 +830,8 @@ impl Collector { source_map, filename, visited_tests: FxHashMap::default(), + unused_extern_reports: Default::default(), + compiling_test_count: AtomicUsize::new(0), } } @@ -789,6 +878,10 @@ impl Tester for Collector { let runtool_args = self.options.runtool_args.clone(); let target = self.options.target.clone(); let target_str = target.to_string(); + let unused_externs = self.unused_extern_reports.clone(); + if !config.compile_fail { + self.compiling_test_count.fetch_add(1, Ordering::SeqCst); + } // FIXME(#44940): if doctests ever support path remapping, then this filename // needs to be the result of `SourceMap::span_to_unmapped_path`. @@ -844,6 +937,9 @@ impl Tester for Collector { test_type: testing::TestType::DocTest, }, testfn: testing::DynTestFn(box move || { + let report_unused_externs = |uext| { + unused_externs.lock().unwrap().push(uext); + }; let res = run_test( &test, &cratename, @@ -862,6 +958,7 @@ impl Tester for Collector { outdir, path, &test_id, + report_unused_externs, ); if let Err(err) = res { @@ -991,11 +1088,11 @@ impl<'a, 'hir, 'tcx> HirCollector<'a, 'hir, 'tcx> { fn visit_testable( &mut self, name: String, - attrs: &[ast::Attribute], hir_id: HirId, sp: Span, nested: F, ) { + let attrs = self.tcx.hir().attrs(hir_id); let mut attrs = Attributes::from_ast(self.sess.diagnostic(), attrs, None); if let Some(ref cfg) = attrs.cfg { if !cfg.matches(&self.sess.parse_sess, Some(&self.sess.features_untracked())) { @@ -1053,45 +1150,27 @@ impl<'a, 'hir, 'tcx> intravisit::Visitor<'hir> for HirCollector<'a, 'hir, 'tcx> item.ident.to_string() }; - self.visit_testable(name, &item.attrs, item.hir_id(), item.span, |this| { + self.visit_testable(name, item.hir_id(), item.span, |this| { intravisit::walk_item(this, item); }); } fn visit_trait_item(&mut self, item: &'hir hir::TraitItem<'_>) { - self.visit_testable( - item.ident.to_string(), - &item.attrs, - item.hir_id(), - item.span, - |this| { - intravisit::walk_trait_item(this, item); - }, - ); + self.visit_testable(item.ident.to_string(), item.hir_id(), item.span, |this| { + intravisit::walk_trait_item(this, item); + }); } fn visit_impl_item(&mut self, item: &'hir hir::ImplItem<'_>) { - self.visit_testable( - item.ident.to_string(), - &item.attrs, - item.hir_id(), - item.span, - |this| { - intravisit::walk_impl_item(this, item); - }, - ); + self.visit_testable(item.ident.to_string(), item.hir_id(), item.span, |this| { + intravisit::walk_impl_item(this, item); + }); } fn visit_foreign_item(&mut self, item: &'hir hir::ForeignItem<'_>) { - self.visit_testable( - item.ident.to_string(), - &item.attrs, - item.hir_id(), - item.span, - |this| { - intravisit::walk_foreign_item(this, item); - }, - ); + self.visit_testable(item.ident.to_string(), item.hir_id(), item.span, |this| { + intravisit::walk_foreign_item(this, item); + }); } fn visit_variant( @@ -1100,21 +1179,20 @@ impl<'a, 'hir, 'tcx> intravisit::Visitor<'hir> for HirCollector<'a, 'hir, 'tcx> g: &'hir hir::Generics<'_>, item_id: hir::HirId, ) { - self.visit_testable(v.ident.to_string(), &v.attrs, v.id, v.span, |this| { + self.visit_testable(v.ident.to_string(), v.id, v.span, |this| { intravisit::walk_variant(this, v, g, item_id); }); } - fn visit_struct_field(&mut self, f: &'hir hir::StructField<'_>) { - self.visit_testable(f.ident.to_string(), &f.attrs, f.hir_id, f.span, |this| { - intravisit::walk_struct_field(this, f); + fn visit_field_def(&mut self, f: &'hir hir::FieldDef<'_>) { + self.visit_testable(f.ident.to_string(), f.hir_id, f.span, |this| { + intravisit::walk_field_def(this, f); }); } fn visit_macro_def(&mut self, macro_def: &'hir hir::MacroDef<'_>) { self.visit_testable( macro_def.ident.to_string(), - ¯o_def.attrs, macro_def.hir_id(), macro_def.span, |_| (), diff --git a/src/librustdoc/doctree.rs b/src/librustdoc/doctree.rs index 645b2bb193ec2..189624c0d809c 100644 --- a/src/librustdoc/doctree.rs +++ b/src/librustdoc/doctree.rs @@ -5,7 +5,7 @@ use rustc_span::{self, Span, Symbol}; use rustc_hir as hir; crate struct Module<'hir> { - crate name: Option, + crate name: Symbol, crate where_outer: Span, crate where_inner: Span, crate mods: Vec>, @@ -18,7 +18,7 @@ crate struct Module<'hir> { } impl Module<'hir> { - crate fn new(name: Option) -> Module<'hir> { + crate fn new(name: Symbol) -> Module<'hir> { Module { name, id: hir::CRATE_HIR_ID, diff --git a/src/librustdoc/fold.rs b/src/librustdoc/fold.rs index 2b980ebe5926a..376fef6568af7 100644 --- a/src/librustdoc/fold.rs +++ b/src/librustdoc/fold.rs @@ -87,7 +87,7 @@ crate trait DocFolder: Sized { } fn fold_crate(&mut self, mut c: Crate) -> Crate { - c.module = c.module.take().and_then(|module| self.fold_item(module)); + c.module = self.fold_item(c.module).unwrap(); { let external_traits = { std::mem::take(&mut *c.external_traits.borrow_mut()) }; diff --git a/src/librustdoc/formats/cache.rs b/src/librustdoc/formats/cache.rs index f20296f4fe148..9a61f963a3ec3 100644 --- a/src/librustdoc/formats/cache.rs +++ b/src/librustdoc/formats/cache.rs @@ -89,12 +89,16 @@ crate struct Cache { /// This is stored in `Cache` so it doesn't need to be passed through all rustdoc functions. crate document_private: bool, + /// Crates marked with [`#[doc(masked)]`][doc_masked]. + /// + /// [doc_masked]: https://doc.rust-lang.org/nightly/unstable-book/language-features/doc-masked.html + crate masked_crates: FxHashSet, + // Private fields only used when initially crawling a crate to build a cache stack: Vec, parent_stack: Vec, parent_is_trait_impl: bool, stripped_mod: bool, - masked_crates: FxHashSet, crate search_index: Vec, crate deref_trait_did: Option, @@ -112,14 +116,15 @@ crate struct Cache { // even though the trait itself is not exported. This can happen if a trait // was defined in function/expression scope, since the impl will be picked // up by `collect-trait-impls` but the trait won't be scraped out in the HIR - // crawl. In order to prevent crashes when looking for spotlight traits or + // crawl. In order to prevent crashes when looking for notable traits or // when gathering trait documentation on a type, hold impls here while // folding and add them to the cache later on if we find the trait. orphan_trait_impls: Vec<(DefId, FxHashSet, Impl)>, - /// Aliases added through `#[doc(alias = "...")]`. Since a few items can have the same alias, - /// we need the alias element to have an array of items. - crate aliases: BTreeMap>, + /// All intra-doc links resolved so far. + /// + /// Links are indexed by the DefId of the item they document. + crate intra_doc_links: BTreeMap>, } /// This struct is used to wrap the `cache` and `tcx` in order to run `DocFolder`. @@ -146,24 +151,24 @@ impl Cache { // Crawl the crate to build various caches used for the output debug!(?self.crate_version); self.traits = krate.external_traits.take(); - self.masked_crates = mem::take(&mut krate.masked_crates); // Cache where all our extern crates are located // FIXME: this part is specific to HTML so it'd be nice to remove it from the common code for &(n, ref e) in &krate.externs { - let src_root = match e.src { + let src_root = match e.src(tcx) { FileName::Real(ref p) => match p.local_path().parent() { Some(p) => p.to_path_buf(), None => PathBuf::new(), }, _ => PathBuf::new(), }; - let extern_url = extern_html_root_urls.get(&*e.name.as_str()).map(|u| &**u); + let name = e.name(tcx); + let extern_url = extern_html_root_urls.get(&*name.as_str()).map(|u| &**u); self.extern_locations - .insert(n, (e.name, src_root, extern_location(e, extern_url, &dst))); + .insert(n, (name, src_root, extern_location(e, extern_url, &dst, tcx))); let did = DefId { krate: n, index: CRATE_DEF_INDEX }; - self.external_paths.insert(did, (vec![e.name.to_string()], ItemType::Module)); + self.external_paths.insert(did, (vec![name.to_string()], ItemType::Module)); } // Cache where all known primitives have their documentation located. @@ -171,7 +176,7 @@ impl Cache { // Favor linking to as local extern as possible, so iterate all crates in // reverse topological order. for &(_, ref e) in krate.externs.iter().rev() { - for &(def_id, prim) in &e.primitives { + for &(def_id, prim) in &e.primitives(tcx) { self.primitive_locations.insert(prim, def_id); } } @@ -179,8 +184,6 @@ impl Cache { self.primitive_locations.insert(prim, def_id); } - self.stack.push(krate.name.to_string()); - krate = CacheBuilder { tcx, cache: self, empty_cache: Cache::default() }.fold_crate(krate); for (trait_did, dids, impl_) in self.orphan_trait_impls.drain(..) { @@ -226,7 +229,7 @@ impl<'a, 'tcx> DocFolder for CacheBuilder<'a, 'tcx> { if let clean::TraitItem(ref t) = *item.kind { self.cache.traits.entry(item.def_id).or_insert_with(|| clean::TraitWithExtraInfo { trait_: t.clone(), - is_spotlight: item.attrs.has_doc_flag(sym::spotlight), + is_notable: item.attrs.has_doc_flag(sym::notable_trait), }); } @@ -308,15 +311,8 @@ impl<'a, 'tcx> DocFolder for CacheBuilder<'a, 'tcx> { parent, parent_idx: None, search_type: get_index_search_type(&item, &self.empty_cache, self.tcx), + aliases: item.attrs.get_doc_aliases(), }); - - for alias in item.attrs.get_doc_aliases() { - self.cache - .aliases - .entry(alias.to_lowercase()) - .or_insert(Vec::new()) - .push(self.cache.search_index.len() - 1); - } } } (Some(parent), None) if is_inherent_impl_item => { diff --git a/src/librustdoc/formats/item_type.rs b/src/librustdoc/formats/item_type.rs index 7922a1ffa062e..460d4b907c09a 100644 --- a/src/librustdoc/formats/item_type.rs +++ b/src/librustdoc/formats/item_type.rs @@ -67,7 +67,7 @@ impl<'a> From<&'a clean::Item> for ItemType { match *kind { clean::ModuleItem(..) => ItemType::Module, - clean::ExternCrateItem(..) => ItemType::ExternCrate, + clean::ExternCrateItem { .. } => ItemType::ExternCrate, clean::ImportItem(..) => ItemType::Import, clean::StructItem(..) => ItemType::Struct, clean::UnionItem(..) => ItemType::Union, diff --git a/src/librustdoc/formats/renderer.rs b/src/librustdoc/formats/renderer.rs index b779363e5c701..b8ef3384c5908 100644 --- a/src/librustdoc/formats/renderer.rs +++ b/src/librustdoc/formats/renderer.rs @@ -1,5 +1,5 @@ use rustc_middle::ty::TyCtxt; -use rustc_span::edition::Edition; +use rustc_span::Symbol; use crate::clean; use crate::config::RenderOptions; @@ -9,37 +9,40 @@ use crate::formats::cache::Cache; /// Allows for different backends to rustdoc to be used with the `run_format()` function. Each /// backend renderer has hooks for initialization, documenting an item, entering and exiting a /// module, and cleanup/finalizing output. -crate trait FormatRenderer<'tcx>: Clone { +crate trait FormatRenderer<'tcx>: Sized { /// Gives a description of the renderer. Used for performance profiling. fn descr() -> &'static str; + /// Whether to call `item` recursivly for modules + /// + /// This is true for html, and false for json. See #80664 + const RUN_ON_MODULE: bool; + /// Sets up any state required for the renderer. When this is called the cache has already been /// populated. fn init( krate: clean::Crate, options: RenderOptions, - edition: Edition, cache: Cache, tcx: TyCtxt<'tcx>, ) -> Result<(Self, clean::Crate), Error>; + /// Make a new renderer to render a child of the item currently being rendered. + fn make_child_renderer(&self) -> Self; + /// Renders a single non-module item. This means no recursive sub-item rendering is required. fn item(&mut self, item: clean::Item) -> Result<(), Error>; /// Renders a module (should not handle recursing into children). - fn mod_item_in(&mut self, item: &clean::Item, item_name: &str) -> Result<(), Error>; + fn mod_item_in(&mut self, item: &clean::Item) -> Result<(), Error>; /// Runs after recursively rendering all sub-items of a module. - fn mod_item_out(&mut self, item_name: &str) -> Result<(), Error>; + fn mod_item_out(&mut self) -> Result<(), Error> { + Ok(()) + } /// Post processing hook for cleanup and dumping output to files. - /// - /// A handler is available if the renderer wants to report errors. - fn after_krate( - &mut self, - krate: &clean::Crate, - diag: &rustc_errors::Handler, - ) -> Result<(), Error>; + fn after_krate(&mut self) -> Result<(), Error>; fn cache(&self) -> &Cache; } @@ -49,53 +52,48 @@ crate fn run_format<'tcx, T: FormatRenderer<'tcx>>( krate: clean::Crate, options: RenderOptions, cache: Cache, - diag: &rustc_errors::Handler, - edition: Edition, tcx: TyCtxt<'tcx>, ) -> Result<(), Error> { let prof = &tcx.sess.prof; - let (mut format_renderer, mut krate) = prof + let emit_crate = options.should_emit_crate(); + let (mut format_renderer, krate) = prof .extra_verbose_generic_activity("create_renderer", T::descr()) - .run(|| T::init(krate, options, edition, cache, tcx))?; - - let mut item = match krate.module.take() { - Some(i) => i, - None => return Ok(()), - }; + .run(|| T::init(krate, options, cache, tcx))?; - item.name = Some(krate.name); + if !emit_crate { + return Ok(()); + } // Render the crate documentation - let mut work = vec![(format_renderer.clone(), item)]; + let mut work = vec![(format_renderer.make_child_renderer(), krate.module)]; - let unknown = rustc_span::Symbol::intern(""); + let unknown = Symbol::intern(""); while let Some((mut cx, item)) = work.pop() { - if item.is_mod() { + if item.is_mod() && T::RUN_ON_MODULE { // modules are special because they add a namespace. We also need to // recurse into the items of the module as well. - let name = item.name.as_ref().unwrap().to_string(); - if name.is_empty() { - panic!("Unexpected module with empty name"); - } - let _timer = prof.generic_activity_with_arg("render_mod_item", name.as_str()); + let _timer = + prof.generic_activity_with_arg("render_mod_item", item.name.unwrap().to_string()); - cx.mod_item_in(&item, &name)?; + cx.mod_item_in(&item)?; let module = match *item.kind { clean::StrippedItem(box clean::ModuleItem(m)) | clean::ModuleItem(m) => m, _ => unreachable!(), }; for it in module.items { debug!("Adding {:?} to worklist", it.name); - work.push((cx.clone(), it)); + work.push((cx.make_child_renderer(), it)); } - cx.mod_item_out(&name)?; - } else if item.name.is_some() { + cx.mod_item_out()?; + // FIXME: checking `item.name.is_some()` is very implicit and leads to lots of special + // cases. Use an explicit match instead. + } else if item.name.is_some() && !item.is_extern_crate() { prof.generic_activity_with_arg("render_item", &*item.name.unwrap_or(unknown).as_str()) .run(|| cx.item(item))?; } } prof.extra_verbose_generic_activity("renderer_after_krate", T::descr()) - .run(|| format_renderer.after_krate(&krate, diag)) + .run(|| format_renderer.after_krate()) } diff --git a/src/librustdoc/html/format.rs b/src/librustdoc/html/format.rs index bea0e75832c33..e39652c6dd5de 100644 --- a/src/librustdoc/html/format.rs +++ b/src/librustdoc/html/format.rs @@ -5,10 +5,11 @@ //! assume that HTML output is desired, although it may be possible to redesign //! them in the future to instead emit any format desired. -use std::borrow::Cow; use std::cell::Cell; use std::fmt; +use std::iter; +use rustc_data_structures::captures::Captures; use rustc_data_structures::fx::FxHashSet; use rustc_hir as hir; use rustc_middle::ty::TyCtxt; @@ -16,11 +17,10 @@ use rustc_span::def_id::{DefId, CRATE_DEF_INDEX}; use rustc_target::spec::abi::Abi; use crate::clean::{self, utils::find_nearest_parent_module, PrimitiveType}; -use crate::formats::cache::Cache; use crate::formats::item_type::ItemType; use crate::html::escape::Escape; use crate::html::render::cache::ExternalLocation; -use crate::html::render::CURRENT_DEPTH; +use crate::html::render::Context; crate trait Print { fn print(self, buffer: &mut Buffer); @@ -105,31 +105,10 @@ impl Buffer { crate fn is_for_html(&self) -> bool { self.for_html } -} - -/// Wrapper struct for properly emitting a function or method declaration. -crate struct Function<'a> { - /// The declaration to emit. - crate decl: &'a clean::FnDecl, - /// The length of the function header and name. In other words, the number of characters in the - /// function declaration up to but not including the parentheses. - /// - /// Used to determine line-wrapping. - crate header_len: usize, - /// The number of spaces to indent each successive line with, if line-wrapping is necessary. - crate indent: usize, - /// Whether the function is async or not. - crate asyncness: hir::IsAsync, -} -/// Wrapper struct for emitting a where-clause from Generics. -crate struct WhereClause<'a> { - /// The Generics from which to emit a where-clause. - crate gens: &'a clean::Generics, - /// The number of spaces to indent each line with. - crate indent: usize, - /// Whether the where-clause needs to add a comma and newline after the last bound. - crate end_newline: bool, + crate fn reserve(&mut self, additional: usize) { + self.buffer.reserve(additional) + } } fn comma_sep(items: impl Iterator) -> impl fmt::Display { @@ -144,27 +123,30 @@ fn comma_sep(items: impl Iterator) -> impl fmt::Displ }) } -crate fn print_generic_bounds<'a>( +crate fn print_generic_bounds<'a, 'tcx: 'a>( bounds: &'a [clean::GenericBound], - cache: &'a Cache, -) -> impl fmt::Display + 'a { + cx: &'a Context<'tcx>, +) -> impl fmt::Display + 'a + Captures<'tcx> { display_fn(move |f| { let mut bounds_dup = FxHashSet::default(); for (i, bound) in - bounds.iter().filter(|b| bounds_dup.insert(b.print(cache).to_string())).enumerate() + bounds.iter().filter(|b| bounds_dup.insert(b.print(cx).to_string())).enumerate() { if i > 0 { f.write_str(" + ")?; } - fmt::Display::fmt(&bound.print(cache), f)?; + fmt::Display::fmt(&bound.print(cx), f)?; } Ok(()) }) } impl clean::GenericParamDef { - crate fn print<'a>(&'a self, cache: &'a Cache) -> impl fmt::Display + 'a { + crate fn print<'a, 'tcx: 'a>( + &'a self, + cx: &'a Context<'tcx>, + ) -> impl fmt::Display + 'a + Captures<'tcx> { display_fn(move |f| match self.kind { clean::GenericParamDefKind::Lifetime => write!(f, "{}", self.name), clean::GenericParamDefKind::Type { ref bounds, ref default, .. } => { @@ -172,17 +154,17 @@ impl clean::GenericParamDef { if !bounds.is_empty() { if f.alternate() { - write!(f, ": {:#}", print_generic_bounds(bounds, cache))?; + write!(f, ": {:#}", print_generic_bounds(bounds, cx))?; } else { - write!(f, ": {}", print_generic_bounds(bounds, cache))?; + write!(f, ": {}", print_generic_bounds(bounds, cx))?; } } if let Some(ref ty) = default { if f.alternate() { - write!(f, " = {:#}", ty.print(cache))?; + write!(f, " = {:#}", ty.print(cx))?; } else { - write!(f, " = {}", ty.print(cache))?; + write!(f, " = {}", ty.print(cx))?; } } @@ -190,9 +172,9 @@ impl clean::GenericParamDef { } clean::GenericParamDefKind::Const { ref ty, .. } => { if f.alternate() { - write!(f, "const {}: {:#}", self.name, ty.print(cache)) + write!(f, "const {}: {:#}", self.name, ty.print(cx)) } else { - write!(f, "const {}: {}", self.name, ty.print(cache)) + write!(f, "const {}: {}", self.name, ty.print(cx)) } } }) @@ -200,7 +182,10 @@ impl clean::GenericParamDef { } impl clean::Generics { - crate fn print<'a>(&'a self, cache: &'a Cache) -> impl fmt::Display + 'a { + crate fn print<'a, 'tcx: 'a>( + &'a self, + cx: &'a Context<'tcx>, + ) -> impl fmt::Display + 'a + Captures<'tcx> { display_fn(move |f| { let real_params = self.params.iter().filter(|p| !p.is_synthetic_type_param()).collect::>(); @@ -208,109 +193,106 @@ impl clean::Generics { return Ok(()); } if f.alternate() { - write!(f, "<{:#}>", comma_sep(real_params.iter().map(|g| g.print(cache)))) + write!(f, "<{:#}>", comma_sep(real_params.iter().map(|g| g.print(cx)))) } else { - write!(f, "<{}>", comma_sep(real_params.iter().map(|g| g.print(cache)))) + write!(f, "<{}>", comma_sep(real_params.iter().map(|g| g.print(cx)))) } }) } } -impl<'a> WhereClause<'a> { - crate fn print<'b>(&'b self, cache: &'b Cache) -> impl fmt::Display + 'b { - display_fn(move |f| { - let &WhereClause { gens, indent, end_newline } = self; - if gens.where_predicates.is_empty() { - return Ok(()); +/// * The Generics from which to emit a where-clause. +/// * The number of spaces to indent each line with. +/// * Whether the where-clause needs to add a comma and newline after the last bound. +crate fn print_where_clause<'a, 'tcx: 'a>( + gens: &'a clean::Generics, + cx: &'a Context<'tcx>, + indent: usize, + end_newline: bool, +) -> impl fmt::Display + 'a + Captures<'tcx> { + display_fn(move |f| { + if gens.where_predicates.is_empty() { + return Ok(()); + } + let mut clause = String::new(); + if f.alternate() { + clause.push_str(" where"); + } else { + if end_newline { + clause.push_str(" where"); + } else { + clause.push_str(" where"); } - let mut clause = String::new(); + } + for (i, pred) in gens.where_predicates.iter().enumerate() { if f.alternate() { - clause.push_str(" where"); + clause.push(' '); } else { - if end_newline { - clause.push_str(" where"); - } else { - clause.push_str(" where"); - } + clause.push_str("
"); } - for (i, pred) in gens.where_predicates.iter().enumerate() { - if f.alternate() { - clause.push(' '); - } else { - clause.push_str("
"); - } - match pred { - clean::WherePredicate::BoundPredicate { ty, bounds } => { - let bounds = bounds; - if f.alternate() { - clause.push_str(&format!( - "{:#}: {:#}", - ty.print(cache), - print_generic_bounds(bounds, cache) - )); - } else { - clause.push_str(&format!( - "{}: {}", - ty.print(cache), - print_generic_bounds(bounds, cache) - )); - } - } - clean::WherePredicate::RegionPredicate { lifetime, bounds } => { + match pred { + clean::WherePredicate::BoundPredicate { ty, bounds } => { + let bounds = bounds; + if f.alternate() { + clause.push_str(&format!( + "{:#}: {:#}", + ty.print(cx), + print_generic_bounds(bounds, cx) + )); + } else { clause.push_str(&format!( "{}: {}", - lifetime.print(), - bounds - .iter() - .map(|b| b.print(cache).to_string()) - .collect::>() - .join(" + ") + ty.print(cx), + print_generic_bounds(bounds, cx) )); } - clean::WherePredicate::EqPredicate { lhs, rhs } => { - if f.alternate() { - clause.push_str(&format!( - "{:#} == {:#}", - lhs.print(cache), - rhs.print(cache) - )); - } else { - clause.push_str(&format!( - "{} == {}", - lhs.print(cache), - rhs.print(cache) - )); - } + } + clean::WherePredicate::RegionPredicate { lifetime, bounds } => { + clause.push_str(&format!( + "{}: {}", + lifetime.print(), + bounds + .iter() + .map(|b| b.print(cx).to_string()) + .collect::>() + .join(" + ") + )); + } + clean::WherePredicate::EqPredicate { lhs, rhs } => { + if f.alternate() { + clause.push_str(&format!("{:#} == {:#}", lhs.print(cx), rhs.print(cx),)); + } else { + clause.push_str(&format!("{} == {}", lhs.print(cx), rhs.print(cx),)); } } + } - if i < gens.where_predicates.len() - 1 || end_newline { - clause.push(','); - } + if i < gens.where_predicates.len() - 1 || end_newline { + clause.push(','); } + } - if end_newline { - // add a space so stripping
tags and breaking spaces still renders properly - if f.alternate() { - clause.push(' '); - } else { - clause.push_str(" "); - } + if end_newline { + // add a space so stripping
tags and breaking spaces still renders properly + if f.alternate() { + clause.push(' '); + } else { + clause.push_str(" "); } + } - if !f.alternate() { - clause.push_str("
"); - let padding = " ".repeat(indent + 4); - clause = clause.replace("
", &format!("
{}", padding)); - clause.insert_str(0, &" ".repeat(indent.saturating_sub(1))); - if !end_newline { - clause.insert_str(0, "
"); - } + if !f.alternate() { + clause.push_str("
"); + let padding = " ".repeat(indent + 4); + clause = clause.replace("
", &format!("
{}", padding)); + clause.insert_str(0, &" ".repeat(indent.saturating_sub(1))); + if !end_newline { + clause.insert_str(0, "
"); } - write!(f, "{}", clause) - }) - } + } + write!(f, "{}", clause) + }) } impl clean::Lifetime { @@ -320,46 +302,51 @@ impl clean::Lifetime { } impl clean::Constant { - crate fn print(&self) -> impl fmt::Display + '_ { - display_fn(move |f| { - if f.alternate() { - f.write_str(&self.expr) - } else { - write!(f, "{}", Escape(&self.expr)) - } - }) + crate fn print(&self, tcx: TyCtxt<'_>) -> impl fmt::Display + '_ { + let expr = self.expr(tcx); + display_fn( + move |f| { + if f.alternate() { f.write_str(&expr) } else { write!(f, "{}", Escape(&expr)) } + }, + ) } } impl clean::PolyTrait { - fn print<'a>(&'a self, cache: &'a Cache) -> impl fmt::Display + 'a { + fn print<'a, 'tcx: 'a>( + &'a self, + cx: &'a Context<'tcx>, + ) -> impl fmt::Display + 'a + Captures<'tcx> { display_fn(move |f| { if !self.generic_params.is_empty() { if f.alternate() { write!( f, "for<{:#}> ", - comma_sep(self.generic_params.iter().map(|g| g.print(cache))) + comma_sep(self.generic_params.iter().map(|g| g.print(cx))) )?; } else { write!( f, "for<{}> ", - comma_sep(self.generic_params.iter().map(|g| g.print(cache))) + comma_sep(self.generic_params.iter().map(|g| g.print(cx))) )?; } } if f.alternate() { - write!(f, "{:#}", self.trait_.print(cache)) + write!(f, "{:#}", self.trait_.print(cx)) } else { - write!(f, "{}", self.trait_.print(cache)) + write!(f, "{}", self.trait_.print(cx)) } }) } } impl clean::GenericBound { - crate fn print<'a>(&'a self, cache: &'a Cache) -> impl fmt::Display + 'a { + crate fn print<'a, 'tcx: 'a>( + &'a self, + cx: &'a Context<'tcx>, + ) -> impl fmt::Display + 'a + Captures<'tcx> { display_fn(move |f| match self { clean::GenericBound::Outlives(lt) => write!(f, "{}", lt.print()), clean::GenericBound::TraitBound(ty, modifier) => { @@ -369,9 +356,9 @@ impl clean::GenericBound { hir::TraitBoundModifier::MaybeConst => "?const", }; if f.alternate() { - write!(f, "{}{:#}", modifier_str, ty.print(cache)) + write!(f, "{}{:#}", modifier_str, ty.print(cx)) } else { - write!(f, "{}{}", modifier_str, ty.print(cache)) + write!(f, "{}{}", modifier_str, ty.print(cx)) } } }) @@ -379,7 +366,10 @@ impl clean::GenericBound { } impl clean::GenericArgs { - fn print<'a>(&'a self, cache: &'a Cache) -> impl fmt::Display + 'a { + fn print<'a, 'tcx: 'a>( + &'a self, + cx: &'a Context<'tcx>, + ) -> impl fmt::Display + 'a + Captures<'tcx> { display_fn(move |f| { match self { clean::GenericArgs::AngleBracketed { args, bindings } => { @@ -396,9 +386,9 @@ impl clean::GenericArgs { } comma = true; if f.alternate() { - write!(f, "{:#}", arg.print(cache))?; + write!(f, "{:#}", arg.print(cx))?; } else { - write!(f, "{}", arg.print(cache))?; + write!(f, "{}", arg.print(cx))?; } } for binding in bindings { @@ -407,9 +397,9 @@ impl clean::GenericArgs { } comma = true; if f.alternate() { - write!(f, "{:#}", binding.print(cache))?; + write!(f, "{:#}", binding.print(cx))?; } else { - write!(f, "{}", binding.print(cache))?; + write!(f, "{}", binding.print(cx))?; } } if f.alternate() { @@ -428,17 +418,17 @@ impl clean::GenericArgs { } comma = true; if f.alternate() { - write!(f, "{:#}", ty.print(cache))?; + write!(f, "{:#}", ty.print(cx))?; } else { - write!(f, "{}", ty.print(cache))?; + write!(f, "{}", ty.print(cx))?; } } f.write_str(")")?; if let Some(ref ty) = *output { if f.alternate() { - write!(f, " -> {:#}", ty.print(cache))?; + write!(f, " -> {:#}", ty.print(cx))?; } else { - write!(f, " -> {}", ty.print(cache))?; + write!(f, " -> {}", ty.print(cx))?; } } } @@ -448,89 +438,89 @@ impl clean::GenericArgs { } } -impl clean::PathSegment { - crate fn print<'a>(&'a self, cache: &'a Cache) -> impl fmt::Display + 'a { - display_fn(move |f| { - if f.alternate() { - write!(f, "{}{:#}", self.name, self.args.print(cache)) - } else { - write!(f, "{}{}", self.name, self.args.print(cache)) - } - }) +crate fn href(did: DefId, cx: &Context<'_>) -> Option<(String, ItemType, Vec)> { + let cache = &cx.cache(); + let relative_to = &cx.current; + fn to_module_fqp(shortty: ItemType, fqp: &[String]) -> &[String] { + if shortty == ItemType::Module { &fqp[..] } else { &fqp[..fqp.len() - 1] } } -} - -impl clean::Path { - crate fn print<'a>(&'a self, cache: &'a Cache) -> impl fmt::Display + 'a { - display_fn(move |f| { - if self.global { - f.write_str("::")? - } - for (i, seg) in self.segments.iter().enumerate() { - if i > 0 { - f.write_str("::")? - } - if f.alternate() { - write!(f, "{:#}", seg.print(cache))?; - } else { - write!(f, "{}", seg.print(cache))?; - } - } - Ok(()) - }) - } -} - -crate fn href(did: DefId, cache: &Cache) -> Option<(String, ItemType, Vec)> { if !did.is_local() && !cache.access_levels.is_public(did) && !cache.document_private { return None; } - let depth = CURRENT_DEPTH.with(|l| l.get()); - let (fqp, shortty, mut url) = match cache.paths.get(&did) { - Some(&(ref fqp, shortty)) => (fqp, shortty, "../".repeat(depth)), + 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); + href_relative_parts(module_fqp, relative_to) + }), None => { let &(ref fqp, shortty) = cache.external_paths.get(&did)?; + let module_fqp = to_module_fqp(shortty, fqp); ( fqp, shortty, match cache.extern_locations[&did.krate] { - (.., ExternalLocation::Remote(ref s)) => s.to_string(), - (.., ExternalLocation::Local) => "../".repeat(depth), + (.., ExternalLocation::Remote(ref s)) => { + let s = s.trim_end_matches('/'); + let mut s = vec![&s[..]]; + s.extend(module_fqp[..].iter().map(String::as_str)); + s + } + (.., ExternalLocation::Local) => href_relative_parts(module_fqp, relative_to), (.., ExternalLocation::Unknown) => return None, }, ) } }; - for component in &fqp[..fqp.len() - 1] { - url.push_str(component); - url.push('/'); - } + let last = &fqp.last().unwrap()[..]; + let filename; match shortty { ItemType::Module => { - url.push_str(fqp.last().unwrap()); - url.push_str("/index.html"); + url_parts.push("index.html"); } _ => { - url.push_str(shortty.as_str()); - url.push('.'); - url.push_str(fqp.last().unwrap()); - url.push_str(".html"); + filename = format!("{}.{}.html", shortty.as_str(), last); + url_parts.push(&filename); + } + } + Some((url_parts.join("/"), shortty, fqp.to_vec())) +} + +/// 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<'a>(fqp: &'a [String], relative_to_fqp: &'a [String]) -> Vec<&'a str> { + 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(); } } - Some((url, shortty, fqp.to_vec())) + // 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() + // 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() + // linking to the same module + } else { + Vec::new() + } } /// Used when rendering a `ResolvedPath` structure. This invokes the `path` /// rendering function with the necessary arguments for linking to a local path. -fn resolved_path( +fn resolved_path<'a, 'cx: 'a>( w: &mut fmt::Formatter<'_>, did: DefId, path: &clean::Path, print_all: bool, use_absolute: bool, - cache: &Cache, + cx: &'cx Context<'_>, ) -> fmt::Result { let last = path.segments.last().unwrap(); @@ -540,22 +530,22 @@ fn resolved_path( } } if w.alternate() { - write!(w, "{}{:#}", &last.name, last.args.print(cache))?; + write!(w, "{}{:#}", &last.name, last.args.print(cx))?; } else { let path = if use_absolute { - if let Some((_, _, fqp)) = href(did, cache) { + if let Some((_, _, fqp)) = href(did, cx) { format!( "{}::{}", fqp[..fqp.len() - 1].join("::"), - anchor(did, fqp.last().unwrap(), cache) + anchor(did, fqp.last().unwrap(), cx) ) } else { last.name.to_string() } } else { - anchor(did, &*last.name.as_str(), cache).to_string() + anchor(did, &*last.name.as_str(), cx).to_string() }; - write!(w, "{}{}", path, last.args.print(cache))?; + write!(w, "{}{}", path, last.args.print(cx))?; } Ok(()) } @@ -564,13 +554,14 @@ fn primitive_link( f: &mut fmt::Formatter<'_>, prim: clean::PrimitiveType, name: &str, - m: &Cache, + cx: &Context<'_>, ) -> fmt::Result { + let m = &cx.cache(); let mut needs_termination = false; if !f.alternate() { match m.primitive_locations.get(&prim) { Some(&def_id) if def_id.is_local() => { - let len = CURRENT_DEPTH.with(|s| s.get()); + let len = cx.current.len(); let len = if len == 0 { 0 } else { len - 1 }; write!( f, @@ -581,20 +572,28 @@ fn primitive_link( needs_termination = true; } Some(&def_id) => { + let cname_str; let loc = match m.extern_locations[&def_id.krate] { - (ref cname, _, ExternalLocation::Remote(ref s)) => Some((cname, s.to_string())), + (ref cname, _, ExternalLocation::Remote(ref s)) => { + cname_str = cname.as_str(); + Some(vec![s.trim_end_matches('/'), &cname_str[..]]) + } (ref cname, _, ExternalLocation::Local) => { - let len = CURRENT_DEPTH.with(|s| s.get()); - Some((cname, "../".repeat(len))) + cname_str = cname.as_str(); + Some(if cx.current.first().map(|x| &x[..]) == Some(&cname_str[..]) { + iter::repeat("..").take(cx.current.len() - 1).collect() + } else { + let cname = iter::once(&cname_str[..]); + iter::repeat("..").take(cx.current.len()).chain(cname).collect() + }) } (.., ExternalLocation::Unknown) => None, }; - if let Some((cname, root)) = loc { + if let Some(loc) = loc { write!( f, - "
", - root, - cname, + "", + loc.join("/"), prim.to_url_str() )?; needs_termination = true; @@ -611,15 +610,15 @@ fn primitive_link( } /// Helper to render type parameters -fn tybounds<'a>( +fn tybounds<'a, 'tcx: 'a>( param_names: &'a Option>, - cache: &'a Cache, -) -> impl fmt::Display + 'a { + cx: &'a Context<'tcx>, +) -> impl fmt::Display + 'a + Captures<'tcx> { display_fn(move |f| match *param_names { Some(ref params) => { for param in params { write!(f, " + ")?; - fmt::Display::fmt(¶m.print(cache), f)?; + fmt::Display::fmt(¶m.print(cx), f)?; } Ok(()) } @@ -627,9 +626,14 @@ fn tybounds<'a>( }) } -crate fn anchor<'a>(did: DefId, text: &'a str, cache: &'a Cache) -> impl fmt::Display + 'a { +crate fn anchor<'a, 'cx: 'a>( + did: DefId, + text: &'a str, + cx: &'cx Context<'_>, +) -> impl fmt::Display + 'a { + let parts = href(did, cx); display_fn(move |f| { - if let Some((url, short_ty, fqp)) = href(did, cache) { + if let Some((url, short_ty, fqp)) = parts { write!( f, r#"{}"#, @@ -645,11 +649,11 @@ crate fn anchor<'a>(did: DefId, text: &'a str, cache: &'a Cache) -> impl fmt::Di }) } -fn fmt_type( +fn fmt_type<'cx>( t: &clean::Type, f: &mut fmt::Formatter<'_>, use_absolute: bool, - cache: &Cache, + cx: &'cx Context<'_>, ) -> fmt::Result { debug!("fmt_type(t = {:?})", t); @@ -660,69 +664,69 @@ fn fmt_type( f.write_str("dyn ")?; } // Paths like `T::Output` and `Self::Output` should be rendered with all segments. - resolved_path(f, did, path, is_generic, use_absolute, cache)?; - fmt::Display::fmt(&tybounds(param_names, cache), f) + resolved_path(f, did, path, is_generic, use_absolute, cx)?; + fmt::Display::fmt(&tybounds(param_names, cx), f) } clean::Infer => write!(f, "_"), - clean::Primitive(prim) => primitive_link(f, prim, prim.as_str(), cache), + clean::Primitive(prim) => primitive_link(f, prim, prim.as_str(), cx), clean::BareFunction(ref decl) => { if f.alternate() { write!( f, "{:#}{}{:#}fn{:#}", - decl.print_hrtb_with_space(cache), + decl.print_hrtb_with_space(cx), decl.unsafety.print_with_space(), print_abi_with_space(decl.abi), - decl.decl.print(cache) + decl.decl.print(cx), ) } else { write!( f, "{}{}{}", - decl.print_hrtb_with_space(cache), + decl.print_hrtb_with_space(cx), decl.unsafety.print_with_space(), print_abi_with_space(decl.abi) )?; - primitive_link(f, PrimitiveType::Fn, "fn", cache)?; - write!(f, "{}", decl.decl.print(cache)) + primitive_link(f, PrimitiveType::Fn, "fn", cx)?; + write!(f, "{}", decl.decl.print(cx)) } } clean::Tuple(ref typs) => { match &typs[..] { - &[] => primitive_link(f, PrimitiveType::Unit, "()", cache), + &[] => primitive_link(f, PrimitiveType::Unit, "()", cx), &[ref one] => { - primitive_link(f, PrimitiveType::Tuple, "(", cache)?; + primitive_link(f, PrimitiveType::Tuple, "(", cx)?; // Carry `f.alternate()` into this display w/o branching manually. - fmt::Display::fmt(&one.print(cache), f)?; - primitive_link(f, PrimitiveType::Tuple, ",)", cache) + fmt::Display::fmt(&one.print(cx), f)?; + primitive_link(f, PrimitiveType::Tuple, ",)", cx) } many => { - primitive_link(f, PrimitiveType::Tuple, "(", cache)?; + primitive_link(f, PrimitiveType::Tuple, "(", cx)?; for (i, item) in many.iter().enumerate() { if i != 0 { write!(f, ", ")?; } - fmt::Display::fmt(&item.print(cache), f)?; + fmt::Display::fmt(&item.print(cx), f)?; } - primitive_link(f, PrimitiveType::Tuple, ")", cache) + primitive_link(f, PrimitiveType::Tuple, ")", cx) } } } clean::Slice(ref t) => { - primitive_link(f, PrimitiveType::Slice, "[", cache)?; - fmt::Display::fmt(&t.print(cache), f)?; - primitive_link(f, PrimitiveType::Slice, "]", cache) + primitive_link(f, PrimitiveType::Slice, "[", cx)?; + fmt::Display::fmt(&t.print(cx), f)?; + primitive_link(f, PrimitiveType::Slice, "]", cx) } clean::Array(ref t, ref n) => { - primitive_link(f, PrimitiveType::Array, "[", cache)?; - fmt::Display::fmt(&t.print(cache), f)?; + primitive_link(f, PrimitiveType::Array, "[", cx)?; + fmt::Display::fmt(&t.print(cx), f)?; if f.alternate() { - primitive_link(f, PrimitiveType::Array, &format!("; {}]", n), cache) + primitive_link(f, PrimitiveType::Array, &format!("; {}]", n), cx) } else { - primitive_link(f, PrimitiveType::Array, &format!("; {}]", Escape(n)), cache) + primitive_link(f, PrimitiveType::Array, &format!("; {}]", Escape(n)), cx) } } - clean::Never => primitive_link(f, PrimitiveType::Never, "!", cache), + clean::Never => primitive_link(f, PrimitiveType::Never, "!", cx), clean::RawPointer(m, ref t) => { let m = match m { hir::Mutability::Mut => "mut", @@ -734,26 +738,21 @@ fn fmt_type( primitive_link( f, clean::PrimitiveType::RawPointer, - &format!("*{} {:#}", m, t.print(cache)), - cache, + &format!("*{} {:#}", m, t.print(cx)), + cx, ) } else { primitive_link( f, clean::PrimitiveType::RawPointer, - &format!("*{} {}", m, t.print(cache)), - cache, + &format!("*{} {}", m, t.print(cx)), + cx, ) } } _ => { - primitive_link( - f, - clean::PrimitiveType::RawPointer, - &format!("*{} ", m), - cache, - )?; - fmt::Display::fmt(&t.print(cache), f) + primitive_link(f, clean::PrimitiveType::RawPointer, &format!("*{} ", m), cx)?; + fmt::Display::fmt(&t.print(cx), f) } } } @@ -773,15 +772,15 @@ fn fmt_type( primitive_link( f, PrimitiveType::Slice, - &format!("{}{}{}[{:#}]", amp, lt, m, bt.print(cache)), - cache, + &format!("{}{}{}[{:#}]", amp, lt, m, bt.print(cx)), + cx, ) } else { primitive_link( f, PrimitiveType::Slice, - &format!("{}{}{}[{}]", amp, lt, m, bt.print(cache)), - cache, + &format!("{}{}{}[{}]", amp, lt, m, bt.print(cx)), + cx, ) } } @@ -790,20 +789,20 @@ fn fmt_type( f, PrimitiveType::Slice, &format!("{}{}{}[", amp, lt, m), - cache, + cx, )?; if f.alternate() { - write!(f, "{:#}", bt.print(cache))?; + write!(f, "{:#}", bt.print(cx))?; } else { - write!(f, "{}", bt.print(cache))?; + write!(f, "{}", bt.print(cx))?; } - primitive_link(f, PrimitiveType::Slice, "]", cache) + primitive_link(f, PrimitiveType::Slice, "]", cx) } } } clean::ResolvedPath { param_names: Some(ref v), .. } if !v.is_empty() => { write!(f, "{}{}{}(", amp, lt, m)?; - fmt_type(&ty, f, use_absolute, cache)?; + fmt_type(&ty, f, use_absolute, cx)?; write!(f, ")") } clean::Generic(..) => { @@ -811,21 +810,21 @@ fn fmt_type( f, PrimitiveType::Reference, &format!("{}{}{}", amp, lt, m), - cache, + cx, )?; - fmt_type(&ty, f, use_absolute, cache) + fmt_type(&ty, f, use_absolute, cx) } _ => { write!(f, "{}{}{}", amp, lt, m)?; - fmt_type(&ty, f, use_absolute, cache) + fmt_type(&ty, f, use_absolute, cx) } } } clean::ImplTrait(ref bounds) => { if f.alternate() { - write!(f, "impl {:#}", print_generic_bounds(bounds, cache)) + write!(f, "impl {:#}", print_generic_bounds(bounds, cx)) } else { - write!(f, "impl {}", print_generic_bounds(bounds, cache)) + write!(f, "impl {}", print_generic_bounds(bounds, cx)) } } clean::QPath { ref name, ref self_type, ref trait_ } => { @@ -837,15 +836,15 @@ fn fmt_type( }; if f.alternate() { if should_show_cast { - write!(f, "<{:#} as {:#}>::", self_type.print(cache), trait_.print(cache))? + write!(f, "<{:#} as {:#}>::", self_type.print(cx), trait_.print(cx))? } else { - write!(f, "{:#}::", self_type.print(cache))? + write!(f, "{:#}::", self_type.print(cx))? } } else { if should_show_cast { - write!(f, "<{} as {}>::", self_type.print(cache), trait_.print(cache))? + write!(f, "<{} as {}>::", self_type.print(cx), trait_.print(cx))? } else { - write!(f, "{}::", self_type.print(cache))? + write!(f, "{}::", self_type.print(cx))? } }; match *trait_ { @@ -860,7 +859,7 @@ fn fmt_type( // everything comes in as a fully resolved QPath (hard to // look at). box clean::ResolvedPath { did, ref param_names, .. } => { - match href(did, cache) { + match href(did, cx) { Some((ref url, _, ref path)) if !f.alternate() => { write!( f, @@ -886,52 +885,61 @@ fn fmt_type( } impl clean::Type { - crate fn print<'b, 'a: 'b>(&'a self, cache: &'b Cache) -> impl fmt::Display + 'b { - display_fn(move |f| fmt_type(self, f, false, cache)) + crate fn print<'b, 'a: 'b, 'tcx: 'a>( + &'a self, + cx: &'a Context<'tcx>, + ) -> impl fmt::Display + 'b + Captures<'tcx> { + display_fn(move |f| fmt_type(self, f, false, cx)) } } impl clean::Impl { - crate fn print<'a>(&'a self, cache: &'a Cache, use_absolute: bool) -> impl fmt::Display + 'a { + crate fn print<'a, 'tcx: 'a>( + &'a self, + use_absolute: bool, + cx: &'a Context<'tcx>, + ) -> impl fmt::Display + 'a + Captures<'tcx> { display_fn(move |f| { if f.alternate() { - write!(f, "impl{:#} ", self.generics.print(cache))?; + write!(f, "impl{:#} ", self.generics.print(cx))?; } else { - write!(f, "impl{} ", self.generics.print(cache))?; + write!(f, "impl{} ", self.generics.print(cx))?; } if let Some(ref ty) = self.trait_ { if self.negative_polarity { write!(f, "!")?; } - fmt::Display::fmt(&ty.print(cache), f)?; + fmt::Display::fmt(&ty.print(cx), f)?; write!(f, " for ")?; } if let Some(ref ty) = self.blanket_impl { - fmt_type(ty, f, use_absolute, cache)?; + fmt_type(ty, f, use_absolute, cx)?; } else { - fmt_type(&self.for_, f, use_absolute, cache)?; + fmt_type(&self.for_, f, use_absolute, cx)?; } - let where_clause = WhereClause { gens: &self.generics, indent: 0, end_newline: true }; - fmt::Display::fmt(&where_clause.print(cache), f)?; + fmt::Display::fmt(&print_where_clause(&self.generics, cx, 0, true), f)?; Ok(()) }) } } impl clean::Arguments { - crate fn print<'a>(&'a self, cache: &'a Cache) -> impl fmt::Display + 'a { + crate fn print<'a, 'tcx: 'a>( + &'a self, + cx: &'a Context<'tcx>, + ) -> impl fmt::Display + 'a + Captures<'tcx> { display_fn(move |f| { for (i, input) in self.values.iter().enumerate() { if !input.name.is_empty() { write!(f, "{}: ", input.name)?; } if f.alternate() { - write!(f, "{:#}", input.type_.print(cache))?; + write!(f, "{:#}", input.type_.print(cx))?; } else { - write!(f, "{}", input.type_.print(cache))?; + write!(f, "{}", input.type_.print(cx))?; } if i + 1 < self.values.len() { write!(f, ", ")?; @@ -943,21 +951,29 @@ impl clean::Arguments { } impl clean::FnRetTy { - crate fn print<'a>(&'a self, cache: &'a Cache) -> impl fmt::Display + 'a { + crate fn print<'a, 'tcx: 'a>( + &'a self, + cx: &'a Context<'tcx>, + ) -> impl fmt::Display + 'a + Captures<'tcx> { display_fn(move |f| match self { clean::Return(clean::Tuple(tys)) if tys.is_empty() => Ok(()), - clean::Return(ty) if f.alternate() => write!(f, " -> {:#}", ty.print(cache)), - clean::Return(ty) => write!(f, " -> {}", ty.print(cache)), + clean::Return(ty) if f.alternate() => { + write!(f, " -> {:#}", ty.print(cx)) + } + clean::Return(ty) => write!(f, " -> {}", ty.print(cx)), clean::DefaultReturn => Ok(()), }) } } impl clean::BareFunctionDecl { - fn print_hrtb_with_space<'a>(&'a self, cache: &'a Cache) -> impl fmt::Display + 'a { + fn print_hrtb_with_space<'a, 'tcx: 'a>( + &'a self, + cx: &'a Context<'tcx>, + ) -> impl fmt::Display + 'a + Captures<'tcx> { display_fn(move |f| { if !self.generic_params.is_empty() { - write!(f, "for<{}> ", comma_sep(self.generic_params.iter().map(|g| g.print(cache)))) + write!(f, "for<{}> ", comma_sep(self.generic_params.iter().map(|g| g.print(cx)))) } else { Ok(()) } @@ -966,149 +982,164 @@ impl clean::BareFunctionDecl { } impl clean::FnDecl { - crate fn print<'a>(&'a self, cache: &'a Cache) -> impl fmt::Display + 'a { + crate fn print<'b, 'a: 'b, 'tcx: 'a>( + &'a self, + cx: &'a Context<'tcx>, + ) -> impl fmt::Display + 'b + Captures<'tcx> { display_fn(move |f| { let ellipsis = if self.c_variadic { ", ..." } else { "" }; if f.alternate() { write!( f, "({args:#}{ellipsis}){arrow:#}", - args = self.inputs.print(cache), + args = self.inputs.print(cx), ellipsis = ellipsis, - arrow = self.output.print(cache) + arrow = self.output.print(cx) ) } else { write!( f, "({args}{ellipsis}){arrow}", - args = self.inputs.print(cache), + args = self.inputs.print(cx), ellipsis = ellipsis, - arrow = self.output.print(cache) + arrow = self.output.print(cx) ) } }) } -} -impl Function<'_> { - crate fn print<'b, 'a: 'b>(&'a self, cache: &'b Cache) -> impl fmt::Display + 'b { - display_fn(move |f| { - let &Function { decl, header_len, indent, asyncness } = self; - let amp = if f.alternate() { "&" } else { "&" }; - let mut args = String::new(); - let mut args_plain = String::new(); - for (i, input) in decl.inputs.values.iter().enumerate() { - if i == 0 { - args.push_str("
"); - } + /// * `header_len`: The length of the function header and name. In other words, the number of + /// characters in the function declaration up to but not including the parentheses. + ///
Used to determine line-wrapping. + /// * `indent`: The number of spaces to indent each successive line with, if line-wrapping is + /// necessary. + /// * `asyncness`: Whether the function is async or not. + crate fn full_print<'a, 'tcx: 'a>( + &'a self, + header_len: usize, + indent: usize, + asyncness: hir::IsAsync, + cx: &'a Context<'tcx>, + ) -> impl fmt::Display + 'a + Captures<'tcx> { + display_fn(move |f| self.inner_full_print(header_len, indent, asyncness, f, cx)) + } - if let Some(selfty) = input.to_self() { - match selfty { - clean::SelfValue => { - args.push_str("self"); - args_plain.push_str("self"); - } - clean::SelfBorrowed(Some(ref lt), mtbl) => { - args.push_str(&format!( - "{}{} {}self", - amp, - lt.print(), - mtbl.print_with_space() - )); - args_plain.push_str(&format!( - "&{} {}self", - lt.print(), - mtbl.print_with_space() - )); - } - clean::SelfBorrowed(None, mtbl) => { - args.push_str(&format!("{}{}self", amp, mtbl.print_with_space())); - args_plain.push_str(&format!("&{}self", mtbl.print_with_space())); - } - clean::SelfExplicit(ref typ) => { - if f.alternate() { - args.push_str(&format!("self: {:#}", typ.print(cache))); - } else { - args.push_str(&format!("self: {}", typ.print(cache))); - } - args_plain.push_str(&format!("self: {:#}", typ.print(cache))); - } + fn inner_full_print( + &self, + header_len: usize, + indent: usize, + asyncness: hir::IsAsync, + f: &mut fmt::Formatter<'_>, + cx: &Context<'_>, + ) -> fmt::Result { + let amp = if f.alternate() { "&" } else { "&" }; + let mut args = String::new(); + let mut args_plain = String::new(); + for (i, input) in self.inputs.values.iter().enumerate() { + if i == 0 { + args.push_str("
"); + } + + if let Some(selfty) = input.to_self() { + match selfty { + clean::SelfValue => { + args.push_str("self"); + args_plain.push_str("self"); } - } else { - if i > 0 { - args.push_str("
"); - args_plain.push(' '); + clean::SelfBorrowed(Some(ref lt), mtbl) => { + args.push_str(&format!( + "{}{} {}self", + amp, + lt.print(), + mtbl.print_with_space() + )); + args_plain.push_str(&format!( + "&{} {}self", + lt.print(), + mtbl.print_with_space() + )); } - if !input.name.is_empty() { - args.push_str(&format!("{}: ", input.name)); - args_plain.push_str(&format!("{}: ", input.name)); + clean::SelfBorrowed(None, mtbl) => { + args.push_str(&format!("{}{}self", amp, mtbl.print_with_space())); + args_plain.push_str(&format!("&{}self", mtbl.print_with_space())); } - - if f.alternate() { - args.push_str(&format!("{:#}", input.type_.print(cache))); - } else { - args.push_str(&input.type_.print(cache).to_string()); + clean::SelfExplicit(ref typ) => { + if f.alternate() { + args.push_str(&format!("self: {:#}", typ.print(cx))); + } else { + args.push_str(&format!("self: {}", typ.print(cx))); + } + args_plain.push_str(&format!("self: {:#}", typ.print(cx))); } - args_plain.push_str(&format!("{:#}", input.type_.print(cache))); } - if i + 1 < decl.inputs.values.len() { - args.push(','); - args_plain.push(','); + } else { + if i > 0 { + args.push_str("
"); + args_plain.push(' '); + } + if !input.name.is_empty() { + args.push_str(&format!("{}: ", input.name)); + args_plain.push_str(&format!("{}: ", input.name)); } - } - - let mut args_plain = format!("({})", args_plain); - if decl.c_variadic { - args.push_str(",
..."); - args_plain.push_str(", ..."); + if f.alternate() { + args.push_str(&format!("{:#}", input.type_.print(cx))); + } else { + args.push_str(&input.type_.print(cx).to_string()); + } + args_plain.push_str(&format!("{:#}", input.type_.print(cx))); + } + if i + 1 < self.inputs.values.len() { + args.push(','); + args_plain.push(','); } + } - let output = if let hir::IsAsync::Async = asyncness { - Cow::Owned(decl.sugared_async_return_type()) - } else { - Cow::Borrowed(&decl.output) - }; + let mut args_plain = format!("({})", args_plain); - let arrow_plain = format!("{:#}", &output.print(cache)); - let arrow = if f.alternate() { - format!("{:#}", &output.print(cache)) - } else { - output.print(cache).to_string() - }; + if self.c_variadic { + args.push_str(",
..."); + args_plain.push_str(", ..."); + } - let declaration_len = header_len + args_plain.len() + arrow_plain.len(); - let output = if declaration_len > 80 { - let full_pad = format!("
{}", " ".repeat(indent + 4)); - let close_pad = format!("
{}", " ".repeat(indent)); - format!( - "({args}{close}){arrow}", - args = args.replace("
", &full_pad), - close = close_pad, - arrow = arrow - ) - } else { - format!("({args}){arrow}", args = args.replace("
", ""), arrow = arrow) - }; + let arrow_plain; + let arrow = if let hir::IsAsync::Async = asyncness { + let output = self.sugared_async_return_type(); + arrow_plain = format!("{:#}", output.print(cx)); + if f.alternate() { arrow_plain.clone() } else { format!("{}", output.print(cx)) } + } else { + arrow_plain = format!("{:#}", self.output.print(cx)); + if f.alternate() { arrow_plain.clone() } else { format!("{}", self.output.print(cx)) } + }; - if f.alternate() { - write!(f, "{}", output.replace("
", "\n")) - } else { - write!(f, "{}", output) - } - }) + let declaration_len = header_len + args_plain.len() + arrow_plain.len(); + let output = if declaration_len > 80 { + let full_pad = format!("
{}", " ".repeat(indent + 4)); + let close_pad = format!("
{}", " ".repeat(indent)); + format!( + "({args}{close}){arrow}", + args = args.replace("
", &full_pad), + close = close_pad, + arrow = arrow + ) + } else { + format!("({args}){arrow}", args = args.replace("
", ""), arrow = arrow) + }; + + if f.alternate() { + write!(f, "{}", output.replace("
", "\n")) + } else { + write!(f, "{}", output) + } } } impl clean::Visibility { - crate fn print_with_space<'tcx>( + crate fn print_with_space<'a, 'tcx: 'a>( self, - tcx: TyCtxt<'tcx>, item_did: DefId, - cache: &Cache, - ) -> impl fmt::Display + 'tcx { - use rustc_span::symbol::kw; - + cx: &'a Context<'tcx>, + ) -> impl fmt::Display + 'a + Captures<'tcx> { let to_print = match self { clean::Public => "pub ".to_owned(), clean::Inherited => String::new(), @@ -1116,7 +1147,7 @@ impl clean::Visibility { // FIXME(camelid): This may not work correctly if `item_did` is a module. // However, rustdoc currently never displays a module's // visibility, so it shouldn't matter. - let parent_module = find_nearest_parent_module(tcx, item_did); + let parent_module = find_nearest_parent_module(cx.tcx(), item_did); if vis_did.index == CRATE_DEF_INDEX { "pub(crate) ".to_owned() @@ -1125,26 +1156,19 @@ impl clean::Visibility { // is the same as no visibility modifier String::new() } else if parent_module - .map(|parent| find_nearest_parent_module(tcx, parent)) + .map(|parent| find_nearest_parent_module(cx.tcx(), parent)) .flatten() == Some(vis_did) { "pub(super) ".to_owned() } else { - let path = tcx.def_path(vis_did); + let path = cx.tcx().def_path(vis_did); debug!("path={:?}", path); - let first_name = - path.data[0].data.get_opt_name().expect("modules are always named"); // 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(), cache).to_string(); + let anchor = anchor(vis_did, &last_name.as_str(), cx).to_string(); - let mut s = "pub(".to_owned(); - if path.data.len() != 1 - || (first_name != kw::SelfLower && first_name != kw::Super) - { - s.push_str("in "); - } + let mut s = "pub(in ".to_owned(); for seg in &path.data[..path.data.len() - 1] { s.push_str(&format!("{}::", seg.data.get_opt_name().unwrap())); } @@ -1155,6 +1179,43 @@ impl clean::Visibility { }; display_fn(move |f| f.write_str(&to_print)) } + + /// This function is the same as print_with_space, except that it renders no links. + /// It's used for macros' rendered source view, which is syntax highlighted and cannot have + /// any HTML in it. + crate fn to_src_with_space<'a, 'tcx: 'a>( + self, + tcx: TyCtxt<'tcx>, + item_did: DefId, + ) -> impl fmt::Display + 'a + Captures<'tcx> { + let to_print = match self { + clean::Public => "pub ".to_owned(), + clean::Inherited => String::new(), + clean::Visibility::Restricted(vis_did) => { + // FIXME(camelid): This may not work correctly if `item_did` is a module. + // However, rustdoc currently never displays a module's + // visibility, so it shouldn't matter. + let parent_module = find_nearest_parent_module(tcx, item_did); + + if vis_did.index == CRATE_DEF_INDEX { + "pub(crate) ".to_owned() + } else if parent_module == Some(vis_did) { + // `pub(in foo)` where `foo` is the parent module + // is the same as no visibility modifier + String::new() + } else if parent_module + .map(|parent| find_nearest_parent_module(tcx, parent)) + .flatten() + == Some(vis_did) + { + "pub(super) ".to_owned() + } else { + format!("pub(in {}) ", tcx.def_path_str(vis_did)) + } + } + }; + display_fn(move |f| f.write_str(&to_print)) + } } crate trait PrintWithSpace { @@ -1198,20 +1259,23 @@ impl PrintWithSpace for hir::Mutability { } impl clean::Import { - crate fn print<'b, 'a: 'b>(&'a self, cache: &'b Cache) -> impl fmt::Display + 'b { + crate fn print<'a, 'tcx: 'a>( + &'a self, + cx: &'a Context<'tcx>, + ) -> impl fmt::Display + 'a + Captures<'tcx> { display_fn(move |f| match self.kind { clean::ImportKind::Simple(name) => { if name == self.source.path.last() { - write!(f, "use {};", self.source.print(cache)) + write!(f, "use {};", self.source.print(cx)) } else { - write!(f, "use {} as {};", self.source.print(cache), name) + write!(f, "use {} as {};", self.source.print(cx), name) } } clean::ImportKind::Glob => { if self.source.path.segments.is_empty() { write!(f, "use *;") } else { - write!(f, "use {}::*;", self.source.print(cache)) + write!(f, "use {}::*;", self.source.print(cx)) } } }) @@ -1219,16 +1283,19 @@ impl clean::Import { } impl clean::ImportSource { - crate fn print<'b, 'a: 'b>(&'a self, cache: &'b Cache) -> impl fmt::Display + 'b { + crate fn print<'a, 'tcx: 'a>( + &'a self, + cx: &'a Context<'tcx>, + ) -> impl fmt::Display + 'a + Captures<'tcx> { display_fn(move |f| match self.did { - Some(did) => resolved_path(f, did, &self.path, true, false, cache), + Some(did) => resolved_path(f, did, &self.path, true, false, cx), _ => { for seg in &self.path.segments[..self.path.segments.len() - 1] { write!(f, "{}::", seg.name)?; } let name = self.path.last_name(); if let hir::def::Res::PrimTy(p) = self.path.res { - primitive_link(f, PrimitiveType::from(p), &*name, cache)?; + primitive_link(f, PrimitiveType::from(p), &*name, cx)?; } else { write!(f, "{}", name)?; } @@ -1239,23 +1306,26 @@ impl clean::ImportSource { } impl clean::TypeBinding { - crate fn print<'b, 'a: 'b>(&'a self, cache: &'b Cache) -> impl fmt::Display + 'b { + crate fn print<'a, 'tcx: 'a>( + &'a self, + cx: &'a Context<'tcx>, + ) -> impl fmt::Display + 'a + Captures<'tcx> { display_fn(move |f| { f.write_str(&*self.name.as_str())?; match self.kind { clean::TypeBindingKind::Equality { ref ty } => { if f.alternate() { - write!(f, " = {:#}", ty.print(cache))?; + write!(f, " = {:#}", ty.print(cx))?; } else { - write!(f, " = {}", ty.print(cache))?; + write!(f, " = {}", ty.print(cx))?; } } clean::TypeBindingKind::Constraint { ref bounds } => { if !bounds.is_empty() { if f.alternate() { - write!(f, ": {:#}", print_generic_bounds(bounds, cache))?; + write!(f, ": {:#}", print_generic_bounds(bounds, cx))?; } else { - write!(f, ": {}", print_generic_bounds(bounds, cache))?; + write!(f, ": {}", print_generic_bounds(bounds, cx))?; } } } @@ -1280,11 +1350,14 @@ crate fn print_default_space<'a>(v: bool) -> &'a str { } impl clean::GenericArg { - crate fn print<'b, 'a: 'b>(&'a self, cache: &'b Cache) -> impl fmt::Display + 'b { + crate fn print<'a, 'tcx: 'a>( + &'a self, + cx: &'a Context<'tcx>, + ) -> impl fmt::Display + 'a + Captures<'tcx> { display_fn(move |f| match self { clean::GenericArg::Lifetime(lt) => fmt::Display::fmt(<.print(), f), - clean::GenericArg::Type(ty) => fmt::Display::fmt(&ty.print(cache), f), - clean::GenericArg::Const(ct) => fmt::Display::fmt(&ct.print(), f), + clean::GenericArg::Type(ty) => fmt::Display::fmt(&ty.print(cx), f), + clean::GenericArg::Const(ct) => fmt::Display::fmt(&ct.print(cx.tcx()), f), }) } } diff --git a/src/librustdoc/html/highlight.rs b/src/librustdoc/html/highlight.rs index 7e50d72e60f7d..0030ef67c075d 100644 --- a/src/librustdoc/html/highlight.rs +++ b/src/librustdoc/html/highlight.rs @@ -136,6 +136,16 @@ impl Iterator for TokenIter<'a> { } } +fn get_real_ident_class(text: &str, edition: Edition) -> Class { + match text { + "ref" | "mut" => Class::RefKeyWord, + "self" | "Self" => Class::Self_, + "false" | "true" => Class::Bool, + _ if Symbol::intern(text).is_reserved(|| edition) => Class::KeyWord, + _ => Class::Ident, + } +} + /// Processes program tokens, classifying strings of text by highlighting /// category (`Class`). struct Classifier<'a> { @@ -144,6 +154,8 @@ struct Classifier<'a> { in_macro: bool, in_macro_nonterminal: bool, edition: Edition, + byte_pos: u32, + src: &'a str, } impl<'a> Classifier<'a> { @@ -155,6 +167,68 @@ impl<'a> Classifier<'a> { in_macro: false, in_macro_nonterminal: false, edition, + byte_pos: 0, + src, + } + } + + /// Concatenate colons and idents as one when possible. + fn get_full_ident_path(&mut self) -> Vec<(TokenKind, usize, usize)> { + let start = self.byte_pos as usize; + let mut pos = start; + let mut has_ident = false; + let edition = self.edition; + + loop { + let mut nb = 0; + while let Some((TokenKind::Colon, _)) = self.tokens.peek() { + self.tokens.next(); + nb += 1; + } + // Ident path can start with "::" but if we already have content in the ident path, + // the "::" is mandatory. + if has_ident && nb == 0 { + return vec![(TokenKind::Ident, start, pos)]; + } else if nb != 0 && nb != 2 { + if has_ident { + return vec![(TokenKind::Ident, start, pos), (TokenKind::Colon, pos, pos + nb)]; + } else { + return vec![(TokenKind::Colon, pos, pos + nb)]; + } + } + + if let Some((Class::Ident, text)) = self.tokens.peek().map(|(token, text)| { + if *token == TokenKind::Ident { + let class = get_real_ident_class(text, edition); + (class, text) + } else { + // Doesn't matter which Class we put in here... + (Class::Comment, text) + } + }) { + // We only "add" the colon if there is an ident behind. + pos += text.len() + nb; + has_ident = true; + self.tokens.next(); + } else if nb > 0 && has_ident { + return vec![(TokenKind::Ident, start, pos), (TokenKind::Colon, pos, pos + nb)]; + } else if nb > 0 { + return vec![(TokenKind::Colon, pos, pos + nb)]; + } else if has_ident { + return vec![(TokenKind::Ident, start, pos)]; + } else { + return Vec::new(); + } + } + } + + /// Wraps the tokens iteration to ensure that the byte_pos is always correct. + fn next(&mut self) -> Option<(TokenKind, &'a str)> { + if let Some((kind, text)) = self.tokens.next() { + self.byte_pos += text.len() as u32; + Some((kind, text)) + } else { + None } } @@ -165,8 +239,25 @@ impl<'a> Classifier<'a> { /// token is used. fn highlight(mut self, sink: &mut dyn FnMut(Highlight<'a>)) { with_default_session_globals(|| { - while let Some((token, text)) = self.tokens.next() { - self.advance(token, text, sink); + loop { + if self + .tokens + .peek() + .map(|t| matches!(t.0, TokenKind::Colon | TokenKind::Ident)) + .unwrap_or(false) + { + let tokens = self.get_full_ident_path(); + for (token, start, end) in tokens { + let text = &self.src[start..end]; + self.advance(token, text, sink); + self.byte_pos += text.len() as u32; + } + } + if let Some((token, text)) = self.next() { + self.advance(token, text, sink); + } else { + break; + } } }) } @@ -189,7 +280,9 @@ impl<'a> Classifier<'a> { // leading identifier. TokenKind::Bang if self.in_macro => { self.in_macro = false; - Class::Macro + sink(Highlight::Token { text, class: None }); + sink(Highlight::ExitSpan); + return; } // Assume that '&' or '*' is the reference or dereference operator @@ -201,12 +294,12 @@ impl<'a> Classifier<'a> { }, TokenKind::And => match lookahead { Some(TokenKind::And) => { - let _and = self.tokens.next(); + self.next(); sink(Highlight::Token { text: "&&", class: Some(Class::Op) }); return; } Some(TokenKind::Eq) => { - let _eq = self.tokens.next(); + self.next(); sink(Highlight::Token { text: "&=", class: Some(Class::Op) }); return; } @@ -258,7 +351,7 @@ impl<'a> Classifier<'a> { match lookahead { // Case 1: #![inner_attribute] Some(TokenKind::Bang) => { - let _not = self.tokens.next().unwrap(); + self.next(); if let Some(TokenKind::OpenBracket) = self.peek() { self.in_attribute = true; sink(Highlight::EnterSpan { class: Class::Attribute }); @@ -298,21 +391,21 @@ impl<'a> Classifier<'a> { }, TokenKind::Ident | TokenKind::RawIdent if lookahead == Some(TokenKind::Bang) => { self.in_macro = true; - Class::Macro + sink(Highlight::EnterSpan { class: Class::Macro }); + sink(Highlight::Token { text, class: None }); + return; } - TokenKind::Ident => match text { - "ref" | "mut" => Class::RefKeyWord, - "self" | "Self" => Class::Self_, - "false" | "true" => Class::Bool, - "Option" | "Result" => Class::PreludeTy, - "Some" | "None" | "Ok" | "Err" => Class::PreludeVal, - // Keywords are also included in the identifier set. - _ if Symbol::intern(text).is_reserved(|| self.edition) => Class::KeyWord, - _ if self.in_macro_nonterminal => { - self.in_macro_nonterminal = false; - Class::MacroNonTerminal - } - _ => Class::Ident, + TokenKind::Ident => match get_real_ident_class(text, self.edition) { + Class::Ident => match text { + "Option" | "Result" => Class::PreludeTy, + "Some" | "None" | "Ok" | "Err" => Class::PreludeVal, + _ if self.in_macro_nonterminal => { + self.in_macro_nonterminal = false; + Class::MacroNonTerminal + } + _ => Class::Ident, + }, + c => c, }, TokenKind::RawIdent => Class::Ident, TokenKind::Lifetime { .. } => Class::Lifetime, diff --git a/src/librustdoc/html/highlight/fixtures/dos_line.html b/src/librustdoc/html/highlight/fixtures/dos_line.html index 4400f85681d8a..1c8dbffe78c22 100644 --- a/src/librustdoc/html/highlight/fixtures/dos_line.html +++ b/src/librustdoc/html/highlight/fixtures/dos_line.html @@ -1,3 +1,3 @@ pub fn foo() { -println!("foo"); +println!("foo"); } diff --git a/src/librustdoc/html/highlight/fixtures/sample.html b/src/librustdoc/html/highlight/fixtures/sample.html index d937246f4665a..8d23477bbcb8c 100644 --- a/src/librustdoc/html/highlight/fixtures/sample.html +++ b/src/librustdoc/html/highlight/fixtures/sample.html @@ -10,6 +10,8 @@
#![crate_type = "lib"]
 
+use std::path::{Path, PathBuf};
+
 #[cfg(target_os = "linux")]
 fn main() {
     let foo = true && false || true;
@@ -17,11 +19,19 @@
     let _ = &foo;
     let _ = &&foo;
     let _ = *foo;
-    mac!(foo, &mut bar);
-    assert!(self.length < N && index <= self.length);
+    mac!(foo, &mut bar);
+    assert!(self.length < N && index <= self.length);
+    ::std::env::var("gateau").is_ok();
+    #[rustfmt::skip]
+    let s:std::path::PathBuf = std::path::PathBuf::new();
+    let mut s = String::new();
+
+    match &s {
+        ref mut x => {}
+    }
 }
 
-macro_rules! bar {
+macro_rules! bar {
     ($foo:tt) => {};
 }
 
diff --git a/src/librustdoc/html/highlight/fixtures/sample.rs b/src/librustdoc/html/highlight/fixtures/sample.rs index 956fdbe090bae..b027203655d92 100644 --- a/src/librustdoc/html/highlight/fixtures/sample.rs +++ b/src/librustdoc/html/highlight/fixtures/sample.rs @@ -1,5 +1,7 @@ #![crate_type = "lib"] +use std::path::{Path, PathBuf}; + #[cfg(target_os = "linux")] fn main() { let foo = true && false || true; @@ -9,6 +11,14 @@ fn main() { let _ = *foo; mac!(foo, &mut bar); assert!(self.length < N && index <= self.length); + ::std::env::var("gateau").is_ok(); + #[rustfmt::skip] + let s:std::path::PathBuf = std::path::PathBuf::new(); + let mut s = String::new(); + + match &s { + ref mut x => {} + } } macro_rules! bar { diff --git a/src/librustdoc/html/layout.rs b/src/librustdoc/html/layout.rs index ab65c71d7d1af..dc29add933314 100644 --- a/src/librustdoc/html/layout.rs +++ b/src/librustdoc/html/layout.rs @@ -58,7 +58,7 @@ crate fn render( {style_files}\ \ \ - \ + \ \ {css_extension}\ {favicon}\ @@ -68,7 +68,7 @@ crate fn render( \ \ \ - -endef -export SPANVIEW_HEADER - -all: $(patsubst $(SOURCEDIR)/lib/%.rs,%,$(wildcard $(SOURCEDIR)/lib/*.rs)) $(patsubst $(SOURCEDIR)/%.rs,%,$(wildcard $(SOURCEDIR)/*.rs)) - -# Ensure there are no `expected` results for tests that may have been removed or renamed -.PHONY: clear_expected_if_blessed -clear_expected_if_blessed: -ifdef RUSTC_BLESS_TEST - rm -rf expected_mir_dump.*/ -endif - --include clear_expected_if_blessed - -# FIXME(richkadel): The actions for these two types of targets (libraries and binaries) should be -# combined. - -%: $(SOURCEDIR)/lib/%.rs - # Compile the test library with coverage instrumentation - $(RUSTC) $(SOURCEDIR)/lib/$@.rs \ - $$( grep -q '^\/\/ require-rust-edition-2018' $(SOURCEDIR)/lib/$@.rs && \ - echo "--edition=2018" \ - ) \ - --crate-type rlib \ - -Ztrim-diagnostic-paths=no \ - -Zinstrument-coverage \ - -Zdump-mir=InstrumentCoverage \ - -Zdump-mir-spanview \ - -Zdump-mir-dir="$(TMPDIR)"/mir_dump.$@ - - for path in "$(TMPDIR)"/mir_dump.$@/*; do \ - file="$$(basename "$$path")"; \ - urlescaped="$$("$(PYTHON)" $(BASEDIR)/escape_url.py $$file)" || exit $$?; \ - printf "$$SPANVIEW_HEADER\n" "$@" "$$urlescaped" > "$$path".modified; \ - tail -n +2 "$$path" >> "$$path".modified; \ - mv "$$path".modified "$$path"; \ - done && true # for/done ends in non-zero status - -ifdef RUSTC_BLESS_TEST - mkdir -p expected_mir_dump.$@ - cp "$(TMPDIR)"/mir_dump.$@/*InstrumentCoverage.0.html expected_mir_dump.$@/ -else - # Check that the selected `mir_dump` files match what we expect (`--bless` refreshes `expected`) - mkdir -p "$(TMPDIR)"/actual_mir_dump.$@ - rm -f "$(TMPDIR)"/actual_mir_dump.$@/* - cp "$(TMPDIR)"/mir_dump.$@/*InstrumentCoverage.0.html "$(TMPDIR)"/actual_mir_dump.$@/ - $(DIFF) -r expected_mir_dump.$@/ "$(TMPDIR)"/actual_mir_dump.$@/ -endif - -%: $(SOURCEDIR)/%.rs - # Compile the test program with coverage instrumentation - $(RUSTC) $(SOURCEDIR)/$@.rs \ - $$( grep -q '^\/\/ require-rust-edition-2018' $(SOURCEDIR)/$@.rs && \ - echo "--edition=2018" \ - ) \ - -L "$(TMPDIR)" \ - -Ztrim-diagnostic-paths=no \ - -Zinstrument-coverage \ - -Zdump-mir=InstrumentCoverage \ - -Zdump-mir-spanview \ - -Zdump-mir-dir="$(TMPDIR)"/mir_dump.$@ - - for path in "$(TMPDIR)"/mir_dump.$@/*; do \ - file="$$(basename "$$path")"; \ - urlescaped="$$("$(PYTHON)" $(BASEDIR)/escape_url.py $$file)" || exit $$?; \ - printf "$$SPANVIEW_HEADER\n" "$@" "$$urlescaped" > "$$path".modified; \ - tail -n +2 "$$path" >> "$$path".modified; \ - mv "$$path".modified "$$path"; \ - done && true # for/done ends in non-zero status - -ifdef RUSTC_BLESS_TEST - mkdir -p expected_mir_dump.$@ - cp "$(TMPDIR)"/mir_dump.$@/*InstrumentCoverage.0.html expected_mir_dump.$@/ -else - # Check that the selected `mir_dump` files match what we expect (`--bless` refreshes `expected`) - mkdir -p "$(TMPDIR)"/actual_mir_dump.$@ - rm -f "$(TMPDIR)"/actual_mir_dump.$@/* - cp "$(TMPDIR)"/mir_dump.$@/*InstrumentCoverage.0.html "$(TMPDIR)"/actual_mir_dump.$@/ - $(DIFF) -r expected_mir_dump.$@/ "$(TMPDIR)"/actual_mir_dump.$@/ -endif diff --git a/src/test/run-make-fulldeps/coverage-spanview/escape_url.py b/src/test/run-make-fulldeps/coverage-spanview/escape_url.py deleted file mode 100644 index b725ed4630302..0000000000000 --- a/src/test/run-make-fulldeps/coverage-spanview/escape_url.py +++ /dev/null @@ -1,12 +0,0 @@ -#!/usr/bin/env python - -import sys - -# Support python 2 or 3 -try: - from urllib.parse import quote -except ImportError: - from urllib import quote - -# Converts the input string into a valid URL parameter string. -print (quote(' '.join(sys.argv[1:]))) diff --git a/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.abort/abort.main.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.abort/abort.main.-------.InstrumentCoverage.0.html deleted file mode 100644 index 176587af25be0..0000000000000 --- a/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.abort/abort.main.-------.InstrumentCoverage.0.html +++ /dev/null @@ -1,106 +0,0 @@ - - - - -abort.main - Coverage Spans - - - -
@0⦊fn main() -> Result<(), u8> { - let mut countdown = 10⦉@0; - while @1,2⦊countdown > 0⦉@1,2 { - if @3,5⦊countdown < 5⦉@3,5 @6,8⦊{ - might_abort(false); - }⦉@6,8@7⦊⦉@7 - // See discussion (below the `Notes` section) on coverage results for the closing brace. - if @9⦊countdown < 5⦉@9 @10,12⦊{ might_abort(false); }⦉@10,12@11⦊⦉@11 // Counts for different regions on one line. - // For the following example, the closing brace is the last character on the line. - // This shows the character after the closing brace is highlighted, even if that next - // character is a newline. - if @13⦊countdown < 5⦉@13 @14,16⦊{ might_abort(false); }⦉@14,16@15⦊⦉@15 - @17,18⦊countdown -= 1⦉@17,18; - } - @4⦊Ok(()) -}⦉@4
- - diff --git a/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.abort/abort.might_abort.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.abort/abort.might_abort.-------.InstrumentCoverage.0.html deleted file mode 100644 index a302b974ae1d2..0000000000000 --- a/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.abort/abort.might_abort.-------.InstrumentCoverage.0.html +++ /dev/null @@ -1,163 +0,0 @@ - - - - -abort.might_abort - Coverage Spans - - - -
@0⦊fn might_abort(should_abort: bool) ⦉@0{ - if @0⦊should_abort⦉@0 { - @1,3,4⦊println!("aborting..."); - panic!("panics and aborts");⦉@1,3,4 - } else @2,5,6⦊{ - println!("Don't Panic"); - } -}⦉@2,5,6
- - diff --git a/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.assert/assert.main.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.assert/assert.main.-------.InstrumentCoverage.0.html deleted file mode 100644 index 365e94cd31e50..0000000000000 --- a/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.assert/assert.main.-------.InstrumentCoverage.0.html +++ /dev/null @@ -1,102 +0,0 @@ - - - - -assert.main - Coverage Spans - - - -
@0⦊fn main() -> Result<(),u8> { - let mut countdown = 10⦉@0; - while @1,2⦊countdown > 0⦉@1,2 { - if @3,5⦊countdown == 1⦉@3,5 @6,8⦊{ - might_fail_assert(3); - }⦉@6,8 else if @7⦊countdown < 5⦉@7 @9,11⦊{ - might_fail_assert(2); - }⦉@9,11@10⦊⦉@10 - @13,14⦊countdown -= 1⦉@13,14; - } - @4⦊Ok(()) -}⦉@4
- - diff --git a/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.assert/assert.might_fail_assert.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.assert/assert.might_fail_assert.-------.InstrumentCoverage.0.html deleted file mode 100644 index db72a5306ff70..0000000000000 --- a/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.assert/assert.might_fail_assert.-------.InstrumentCoverage.0.html +++ /dev/null @@ -1,97 +0,0 @@ - - - - -assert.might_fail_assert - Coverage Spans - - - -
@0,1,2,3,4⦊fn might_fail_assert(one_plus_one: u32) ⦉@0,1,2,3,4{ - @0,1,2,3,4⦊println!("does 1 + 1 = {}?", one_plus_one);⦉@0,1,2,3,4 - assert_eq!(@0,1,2,3,4⦊1 + 1⦉@0,1,2,3,4, one_plus_one, @5,7⦊"the argument was wrong"⦉@5,7); -}@6⦊⦉@6
- - diff --git a/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.async/async.c-{closure#0}.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.async/async.c-{closure#0}.-------.InstrumentCoverage.0.html deleted file mode 100644 index 21bfce701fe6e..0000000000000 --- a/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.async/async.c-{closure#0}.-------.InstrumentCoverage.0.html +++ /dev/null @@ -1,82 +0,0 @@ - - - - -async.c-{closure#0} - Coverage Spans - - - -
@0⦊{ - if x == 8⦉@0 { - @1⦊1⦉@1 - } else { - @2⦊0⦉@2 - } -}@3⦊⦉@3
- - diff --git a/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.async/async.c.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.async/async.c.-------.InstrumentCoverage.0.html deleted file mode 100644 index 3eee0dd4100ed..0000000000000 --- a/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.async/async.c.-------.InstrumentCoverage.0.html +++ /dev/null @@ -1,80 +0,0 @@ - - - - -async.c - Coverage Spans - - - -
@0,1⦊async fn c(x: u8) -> u8 ⦉@0,1{ - if x == 8 { - 1 - } else { - 0 - } -}
- - diff --git a/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.async/async.d-{closure#0}.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.async/async.d-{closure#0}.-------.InstrumentCoverage.0.html deleted file mode 100644 index 7cf34f077954f..0000000000000 --- a/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.async/async.d-{closure#0}.-------.InstrumentCoverage.0.html +++ /dev/null @@ -1,75 +0,0 @@ - - - - -async.d-{closure#0} - Coverage Spans - - - -
@0⦊⦉@0{ 1 }
- - diff --git a/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.async/async.d.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.async/async.d.-------.InstrumentCoverage.0.html deleted file mode 100644 index 5c7f6e00224a0..0000000000000 --- a/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.async/async.d.-------.InstrumentCoverage.0.html +++ /dev/null @@ -1,74 +0,0 @@ - - - - -async.d - Coverage Spans - - - -
@0,1⦊async fn d() -> u8 ⦉@0,1{ 1 }
- - diff --git a/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.async/async.e-{closure#0}.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.async/async.e-{closure#0}.-------.InstrumentCoverage.0.html deleted file mode 100644 index 1f95a7d35af88..0000000000000 --- a/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.async/async.e-{closure#0}.-------.InstrumentCoverage.0.html +++ /dev/null @@ -1,75 +0,0 @@ - - - - -async.e-{closure#0} - Coverage Spans - - - -
@0⦊⦉@0{ 1 }
- - diff --git a/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.async/async.e.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.async/async.e.-------.InstrumentCoverage.0.html deleted file mode 100644 index ee3b7b1d7ffc7..0000000000000 --- a/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.async/async.e.-------.InstrumentCoverage.0.html +++ /dev/null @@ -1,74 +0,0 @@ - - - - -async.e - Coverage Spans - - - -
@0,1⦊async fn e() -> u8 ⦉@0,1{ 1 }
- - diff --git a/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.async/async.executor-block_on-VTABLE-{closure#0}.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.async/async.executor-block_on-VTABLE-{closure#0}.-------.InstrumentCoverage.0.html deleted file mode 100644 index 64fc1568b0085..0000000000000 --- a/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.async/async.executor-block_on-VTABLE-{closure#0}.-------.InstrumentCoverage.0.html +++ /dev/null @@ -1,84 +0,0 @@ - - - - -async.executor-block_on-VTABLE-{closure#0} - Coverage Spans - - - -
@0,1,2,3⦊⦉@0,1,2,3$crate::panicking::panic_fmt($crate::format_args!($fmt, $($arg)+))
- - diff --git a/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.async/async.executor-block_on-VTABLE-{closure#1}.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.async/async.executor-block_on-VTABLE-{closure#1}.-------.InstrumentCoverage.0.html deleted file mode 100644 index 1bbcfa5744b6b..0000000000000 --- a/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.async/async.executor-block_on-VTABLE-{closure#1}.-------.InstrumentCoverage.0.html +++ /dev/null @@ -1,84 +0,0 @@ - - - - -async.executor-block_on-VTABLE-{closure#1} - Coverage Spans - - - -
@0,1,2,3⦊⦉@0,1,2,3$crate::panicking::panic_fmt($crate::format_args!($fmt, $($arg)+))
- - diff --git a/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.async/async.executor-block_on-VTABLE-{closure#2}.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.async/async.executor-block_on-VTABLE-{closure#2}.-------.InstrumentCoverage.0.html deleted file mode 100644 index 14cb98d20c988..0000000000000 --- a/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.async/async.executor-block_on-VTABLE-{closure#2}.-------.InstrumentCoverage.0.html +++ /dev/null @@ -1,84 +0,0 @@ - - - - -async.executor-block_on-VTABLE-{closure#2} - Coverage Spans - - - -
@0,1,2,3⦊⦉@0,1,2,3$crate::panicking::panic_fmt($crate::format_args!($fmt, $($arg)+))
- - diff --git a/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.async/async.executor-block_on-VTABLE-{closure#3}.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.async/async.executor-block_on-VTABLE-{closure#3}.-------.InstrumentCoverage.0.html deleted file mode 100644 index ef2fe7d06825d..0000000000000 --- a/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.async/async.executor-block_on-VTABLE-{closure#3}.-------.InstrumentCoverage.0.html +++ /dev/null @@ -1,75 +0,0 @@ - - - - -async.executor-block_on-VTABLE-{closure#3} - Coverage Spans - - - -
|_| @0⦊()⦉@0
- - diff --git a/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.async/async.executor-block_on.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.async/async.executor-block_on.-------.InstrumentCoverage.0.html deleted file mode 100644 index 9a5bd6e42cd37..0000000000000 --- a/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.async/async.executor-block_on.-------.InstrumentCoverage.0.html +++ /dev/null @@ -1,239 +0,0 @@ - - - - -async.executor-block_on - Coverage Spans - - - -
@0,1,2,3,4,5⦊pub fn block_on<F: Future>(mut future: F) -> F::Output { - let mut future = unsafe { Pin::new_unchecked(&mut future) }; - - static VTABLE: RawWakerVTable = RawWakerVTable::new( - |_| unimplemented!("clone"), - |_| unimplemented!("wake"), - |_| unimplemented!("wake_by_ref"), - |_| (), - ); - let waker = unsafe { Waker::from_raw(RawWaker::new(core::ptr::null(), &VTABLE)) }; - let mut context = Context::from_waker(&waker)⦉@0,1,2,3,4,5; - - loop { - if let Poll::Ready(@10,12,14,15,16,17⦊val⦉@10,12,14,15,16,17) = @6,7,8,9⦊future.as_mut().poll(&mut context)⦉@6,7,8,9 { - break @10,12,14,15,16,17⦊val⦉@10,12,14,15,16,17; - }@11,13⦊⦉@11,13 - } - }@10,12,14,15,16,17⦊⦉@10,12,14,15,16,17
- - diff --git a/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.async/async.f-{closure#0}.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.async/async.f-{closure#0}.-------.InstrumentCoverage.0.html deleted file mode 100644 index 74b62673ac94b..0000000000000 --- a/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.async/async.f-{closure#0}.-------.InstrumentCoverage.0.html +++ /dev/null @@ -1,75 +0,0 @@ - - - - -async.f-{closure#0} - Coverage Spans - - - -
@0⦊⦉@0{ 1 }
- - diff --git a/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.async/async.f.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.async/async.f.-------.InstrumentCoverage.0.html deleted file mode 100644 index a31bca54df2e5..0000000000000 --- a/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.async/async.f.-------.InstrumentCoverage.0.html +++ /dev/null @@ -1,74 +0,0 @@ - - - - -async.f - Coverage Spans - - - -
@0,1⦊async fn f() -> u8 ⦉@0,1{ 1 }
- - diff --git a/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.async/async.foo-{closure#0}.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.async/async.foo-{closure#0}.-------.InstrumentCoverage.0.html deleted file mode 100644 index b8c53cccabda1..0000000000000 --- a/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.async/async.foo-{closure#0}.-------.InstrumentCoverage.0.html +++ /dev/null @@ -1,75 +0,0 @@ - - - - -async.foo-{closure#0} - Coverage Spans - - - -
@0⦊⦉@0{ [false; 10] }
- - diff --git a/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.async/async.foo.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.async/async.foo.-------.InstrumentCoverage.0.html deleted file mode 100644 index cf72a9d532c79..0000000000000 --- a/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.async/async.foo.-------.InstrumentCoverage.0.html +++ /dev/null @@ -1,74 +0,0 @@ - - - - -async.foo - Coverage Spans - - - -
@0,1⦊async fn foo() -> [bool; 10] ⦉@0,1{ [false; 10] }
- - diff --git a/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.async/async.g-{closure#0}.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.async/async.g-{closure#0}.-------.InstrumentCoverage.0.html deleted file mode 100644 index b10012621b7dd..0000000000000 --- a/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.async/async.g-{closure#0}.-------.InstrumentCoverage.0.html +++ /dev/null @@ -1,82 +0,0 @@ - - - - -async.g-{closure#0} - Coverage Spans - - - -
@0,3,4⦊{ - match x⦉@0,3,4 { - @17⦊y⦉@17 if @0,3,4⦊e()⦉@0,3,4.await == @10,13,15,16⦊y⦉@10,13,15,16 => @17⦊()⦉@17, - @33⦊y⦉@33 if @1,19,20⦊f()⦉@1,19,20.await == @26,29,31,32⦊y⦉@26,29,31,32 => @33⦊()⦉@33, - _ => @2⦊()⦉@2, - } -}@35,36⦊⦉@35,36
- - diff --git a/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.async/async.g.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.async/async.g.-------.InstrumentCoverage.0.html deleted file mode 100644 index 973995477b9aa..0000000000000 --- a/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.async/async.g.-------.InstrumentCoverage.0.html +++ /dev/null @@ -1,80 +0,0 @@ - - - - -async.g - Coverage Spans - - - -
@0,1⦊pub async fn g(x: u8) ⦉@0,1{ - match x { - y if e().await == y => (), - y if f().await == y => (), - _ => (), - } -}
- - diff --git a/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.async/async.h-{closure#0}.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.async/async.h-{closure#0}.-------.InstrumentCoverage.0.html deleted file mode 100644 index 6b4b43f836580..0000000000000 --- a/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.async/async.h-{closure#0}.-------.InstrumentCoverage.0.html +++ /dev/null @@ -1,82 +0,0 @@ - - - - -async.h-{closure#0} - Coverage Spans - - - -
@0,2,3⦊{ // The function signature is counted when called, but the body is not - // executed (not awaited) so the open brace has a `0` count (at least when - // displayed with `llvm-cov show` in color-mode). - match x⦉@0,2,3 { - @17⦊y⦉@17 if @0,2,3⦊foo()⦉@0,2,3.await[@9,12,14,15,16⦊y⦉@9,12,14,15,16] => @17⦊()⦉@17, - _ => @1⦊()⦉@1, - } -}@19,20⦊⦉@19,20
- - diff --git a/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.async/async.h.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.async/async.h.-------.InstrumentCoverage.0.html deleted file mode 100644 index f2ea01281fe1e..0000000000000 --- a/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.async/async.h.-------.InstrumentCoverage.0.html +++ /dev/null @@ -1,81 +0,0 @@ - - - - -async.h - Coverage Spans - - - -
@0,1⦊async fn h(x: usize) ⦉@0,1{ // The function signature is counted when called, but the body is not - // executed (not awaited) so the open brace has a `0` count (at least when - // displayed with `llvm-cov show` in color-mode). - match x { - y if foo().await[y] => (), - _ => (), - } -}
- - diff --git a/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.async/async.i-{closure#0}.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.async/async.i-{closure#0}.-------.InstrumentCoverage.0.html deleted file mode 100644 index 49297870fa0a7..0000000000000 --- a/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.async/async.i-{closure#0}.-------.InstrumentCoverage.0.html +++ /dev/null @@ -1,91 +0,0 @@ - - - - -async.i-{closure#0} - Coverage Spans - - - -
@0,3,4⦊{ // line coverage is 1, but there are 2 regions: - // (a) the function signature, counted when the function is called; and - // (b) the open brace for the function body, counted once when the body is - // executed asynchronously. - match x⦉@0,3,4 { - @18,20⦊y⦉@18,20 if @0,3,4⦊c(x)⦉@0,3,4.await == @10,13,15,16,17⦊y + 1⦉@10,13,15,16,17 => { @18,20⦊d()⦉@18,20.await; } - @48⦊y⦉@48 if @1,33,34⦊f()⦉@1,33,34.await == @40,43,45,46,47⦊y + 1⦉@40,43,45,46,47 => @48⦊()⦉@48, - _ => @2⦊()⦉@2, - } -}@50,51⦊⦉@50,51
- - diff --git a/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.async/async.i.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.async/async.i.-------.InstrumentCoverage.0.html deleted file mode 100644 index e5dc6ecd4eb63..0000000000000 --- a/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.async/async.i.-------.InstrumentCoverage.0.html +++ /dev/null @@ -1,83 +0,0 @@ - - - - -async.i - Coverage Spans - - - -
@0,1⦊async fn i(x: u8) ⦉@0,1{ // line coverage is 1, but there are 2 regions: - // (a) the function signature, counted when the function is called; and - // (b) the open brace for the function body, counted once when the body is - // executed asynchronously. - match x { - y if c(x).await == y + 1 => { d().await; } - y if f().await == y + 1 => (), - _ => (), - } -}
- - diff --git a/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.async/async.j-c.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.async/async.j-c.-------.InstrumentCoverage.0.html deleted file mode 100644 index e6384b7598fc2..0000000000000 --- a/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.async/async.j-c.-------.InstrumentCoverage.0.html +++ /dev/null @@ -1,90 +0,0 @@ - - - - -async.j-c - Coverage Spans - - - -
@0⦊fn c(x: u8) -> u8 { - if x == 8⦉@0 { - @1⦊1⦉@1 // This line appears covered, but the 1-character expression span covering the `1` - // is not executed. (`llvm-cov show` displays a `^0` below the `1` ). This is because - // `fn j()` executes the open brace for the funciton body, followed by the function's - // first executable statement, `match x`. Inner function declarations are not - // "visible" to the MIR for `j()`, so the code region counts all lines between the - // open brace and the first statement as executed, which is, in a sense, true. - // `llvm-cov show` overcomes this kind of situation by showing the actual counts - // of the enclosed coverages, (that is, the `1` expression was not executed, and - // accurately displays a `0`). - } else { - @2⦊0⦉@2 - } - }@3⦊⦉@3
- - diff --git a/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.async/async.j-d.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.async/async.j-d.-------.InstrumentCoverage.0.html deleted file mode 100644 index 4eed8ee60dd64..0000000000000 --- a/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.async/async.j-d.-------.InstrumentCoverage.0.html +++ /dev/null @@ -1,75 +0,0 @@ - - - - -async.j-d - Coverage Spans - - - -
@0⦊fn d() -> u8 { 1 }⦉@0
- - diff --git a/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.async/async.j-f.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.async/async.j-f.-------.InstrumentCoverage.0.html deleted file mode 100644 index 6e80c8c786ecb..0000000000000 --- a/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.async/async.j-f.-------.InstrumentCoverage.0.html +++ /dev/null @@ -1,75 +0,0 @@ - - - - -async.j-f - Coverage Spans - - - -
@0⦊fn f() -> u8 { 1 }⦉@0
- - diff --git a/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.async/async.j.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.async/async.j.-------.InstrumentCoverage.0.html deleted file mode 100644 index 7cc751074a077..0000000000000 --- a/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.async/async.j.-------.InstrumentCoverage.0.html +++ /dev/null @@ -1,108 +0,0 @@ - - - - -async.j - Coverage Spans - - - -
@0,3,4,5⦊fn j(x: u8) { - // non-async versions of `c()`, `d()`, and `f()` to make it similar to async `i()`. - fn c(x: u8) -> u8 { - if x == 8 { - 1 // This line appears covered, but the 1-character expression span covering the `1` - // is not executed. (`llvm-cov show` displays a `^0` below the `1` ). This is because - // `fn j()` executes the open brace for the funciton body, followed by the function's - // first executable statement, `match x`. Inner function declarations are not - // "visible" to the MIR for `j()`, so the code region counts all lines between the - // open brace and the first statement as executed, which is, in a sense, true. - // `llvm-cov show` overcomes this kind of situation by showing the actual counts - // of the enclosed coverages, (that is, the `1` expression was not executed, and - // accurately displays a `0`). - } else { - 0 - } - } - fn d() -> u8 { 1 } - fn f() -> u8 { 1 } - match x⦉@0,3,4,5 { - @6,8⦊y⦉@6,8 if @0,3,4,5⦊c(x) == y + 1⦉@0,3,4,5 => @6,8⦊{ d(); }⦉@6,8 - @12⦊y⦉@12 if @1,9,10,11⦊f() == y + 1⦉@1,9,10,11 => @12⦊()⦉@12, - _ => @2⦊()⦉@2, - } -}@14⦊⦉@14
- - diff --git a/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.async/async.k.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.async/async.k.-------.InstrumentCoverage.0.html deleted file mode 100644 index 5792521bb2c57..0000000000000 --- a/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.async/async.k.-------.InstrumentCoverage.0.html +++ /dev/null @@ -1,80 +0,0 @@ - - - - -async.k - Coverage Spans - - - -
@0⦊fn k(x: u8) { // unused function - match x⦉@0 { - 1 => @1,4⦊()⦉@1,4, - 2 => @2,5⦊()⦉@2,5, - _ => @3⦊()⦉@3, - } -}@6⦊⦉@6
- - diff --git a/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.async/async.l.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.async/async.l.-------.InstrumentCoverage.0.html deleted file mode 100644 index cd92b88c24cbb..0000000000000 --- a/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.async/async.l.-------.InstrumentCoverage.0.html +++ /dev/null @@ -1,80 +0,0 @@ - - - - -async.l - Coverage Spans - - - -
@0⦊fn l(x: u8) { - match x⦉@0 { - 1 => @1,4⦊()⦉@1,4, - 2 => @2,5⦊()⦉@2,5, - _ => @3⦊()⦉@3, - } -}@6⦊⦉@6
- - diff --git a/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.async/async.m-{closure#0}.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.async/async.m-{closure#0}.-------.InstrumentCoverage.0.html deleted file mode 100644 index 9cf86ce34a04d..0000000000000 --- a/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.async/async.m-{closure#0}.-------.InstrumentCoverage.0.html +++ /dev/null @@ -1,79 +0,0 @@ - - - - -async.m-{closure#0} - Coverage Spans - - - -
@0,1⦊{ x - 1 }⦉@0,1
- - diff --git a/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.async/async.m.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.async/async.m.-------.InstrumentCoverage.0.html deleted file mode 100644 index 04412c1d99427..0000000000000 --- a/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.async/async.m.-------.InstrumentCoverage.0.html +++ /dev/null @@ -1,74 +0,0 @@ - - - - -async.m - Coverage Spans - - - -
@0,1⦊async fn m(x: u8) -> u8 ⦉@0,1{ x - 1 }
- - diff --git a/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.async/async.main.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.async/async.main.-------.InstrumentCoverage.0.html deleted file mode 100644 index b892af0ed37d5..0000000000000 --- a/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.async/async.main.-------.InstrumentCoverage.0.html +++ /dev/null @@ -1,190 +0,0 @@ - - - - -async.main - Coverage Spans - - - -
@0,1,2,3,4,5,6,7,8,9,10,11,12,13⦊fn main() { - let _ = g(10); - let _ = h(9); - let mut future = Box::pin(i(8)); - j(7); - l(6); - let _ = m(5); - executor::block_on(future.as_mut()); -}⦉@0,1,2,3,4,5,6,7,8,9,10,11,12,13
- - diff --git a/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.closure/closure.main-{closure#0}.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.closure/closure.main-{closure#0}.-------.InstrumentCoverage.0.html deleted file mode 100644 index 3998295a0525e..0000000000000 --- a/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.closure/closure.main-{closure#0}.-------.InstrumentCoverage.0.html +++ /dev/null @@ -1,96 +0,0 @@ - - - - -closure.main-{closure#0} - Coverage Spans - - - -
|| - @0⦊{ - let mut countdown = 0; - if is_false⦉@0 @1⦊{ - countdown = 10; - }⦉@1@2⦊⦉@2 - @3,4⦊"alt string 2".to_owned() - }⦉@3,4
- - diff --git a/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.closure/closure.main-{closure#10}.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.closure/closure.main-{closure#10}.-------.InstrumentCoverage.0.html deleted file mode 100644 index 3bdfe71b48c27..0000000000000 --- a/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.closure/closure.main-{closure#10}.-------.InstrumentCoverage.0.html +++ /dev/null @@ -1,96 +0,0 @@ - - - - -closure.main-{closure#10} - Coverage Spans - - - -
|| - @0⦊{ - let mut countdown = 0; - if is_false⦉@0 @1⦊{ - countdown = 10; - }⦉@1@2⦊⦉@2 - @3,4⦊"alt string 1".to_owned() - }⦉@3,4
- - diff --git a/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.closure/closure.main-{closure#11}.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.closure/closure.main-{closure#11}.-------.InstrumentCoverage.0.html deleted file mode 100644 index 4b3f04b5a0c7e..0000000000000 --- a/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.closure/closure.main-{closure#11}.-------.InstrumentCoverage.0.html +++ /dev/null @@ -1,96 +0,0 @@ - - - - -closure.main-{closure#11} - Coverage Spans - - - -
|| - @0⦊{ - let mut countdown = 0; - if is_false⦉@0 @1⦊{ - countdown = 10; - }⦉@1@2⦊⦉@2 - @3,4⦊"alt string 3".to_owned() - }⦉@3,4
- - diff --git a/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.closure/closure.main-{closure#1}.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.closure/closure.main-{closure#1}.-------.InstrumentCoverage.0.html deleted file mode 100644 index 8ae494178f70b..0000000000000 --- a/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.closure/closure.main-{closure#1}.-------.InstrumentCoverage.0.html +++ /dev/null @@ -1,96 +0,0 @@ - - - - -closure.main-{closure#1} - Coverage Spans - - - -
|| - @0⦊{ - let mut countdown = 0; - if is_false⦉@0 @1⦊{ - countdown = 10; - }⦉@1@2⦊⦉@2 - @3,4⦊"alt string 4".to_owned() - }⦉@3,4
- - diff --git a/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.closure/closure.main-{closure#2}.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.closure/closure.main-{closure#2}.-------.InstrumentCoverage.0.html deleted file mode 100644 index ad40ba57d69be..0000000000000 --- a/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.closure/closure.main-{closure#2}.-------.InstrumentCoverage.0.html +++ /dev/null @@ -1,128 +0,0 @@ - - - - -closure.main-{closure#2} - Coverage Spans - - - -
|val| - @0⦊{ - let mut countdown = 0; - if is_false⦉@0 @1⦊{ - countdown = 10; - }⦉@1@2⦊⦉@2 - @3,4,5,6,7⦊format!("'{}'", val) - }⦉@3,4,5,6,7
- - diff --git a/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.closure/closure.main-{closure#3}.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.closure/closure.main-{closure#3}.-------.InstrumentCoverage.0.html deleted file mode 100644 index 23101d76a8ef4..0000000000000 --- a/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.closure/closure.main-{closure#3}.-------.InstrumentCoverage.0.html +++ /dev/null @@ -1,91 +0,0 @@ - - - - -closure.main-{closure#3} - Coverage Spans - - - -
| - mut countdown - | - @0⦊{ - if is_false⦉@0 @1⦊{ - countdown = 10; - }⦉@1@2⦊⦉@2 - @3,4⦊"closure should be unused".to_owned() - }⦉@3,4
- - diff --git a/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.closure/closure.main-{closure#4}.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.closure/closure.main-{closure#4}.-------.InstrumentCoverage.0.html deleted file mode 100644 index df0172bdf7067..0000000000000 --- a/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.closure/closure.main-{closure#4}.-------.InstrumentCoverage.0.html +++ /dev/null @@ -1,77 +0,0 @@ - - - - -closure.main-{closure#4} - Coverage Spans - - - -
| _unused_arg: u8 | @0,1⦊countdown += 1⦉@0,1
- - diff --git a/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.closure/closure.main-{closure#5}.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.closure/closure.main-{closure#5}.-------.InstrumentCoverage.0.html deleted file mode 100644 index 1c19aa8eeefaa..0000000000000 --- a/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.closure/closure.main-{closure#5}.-------.InstrumentCoverage.0.html +++ /dev/null @@ -1,115 +0,0 @@ - - - - -closure.main-{closure#5} - Coverage Spans - - - -
@0,1,2⦊{ - $crate::io::_print($crate::format_args_nl!($($arg)*)); - }⦉@0,1,2
- - diff --git a/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.closure/closure.main-{closure#6}.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.closure/closure.main-{closure#6}.-------.InstrumentCoverage.0.html deleted file mode 100644 index 74c75c6c46ca2..0000000000000 --- a/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.closure/closure.main-{closure#6}.-------.InstrumentCoverage.0.html +++ /dev/null @@ -1,87 +0,0 @@ - - - - -closure.main-{closure#6} - Coverage Spans - - - -
| _unused_arg: u8 | @0,1,2⦊{ println!("not called") }⦉@0,1,2
- - diff --git a/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.closure/closure.main-{closure#7}.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.closure/closure.main-{closure#7}.-------.InstrumentCoverage.0.html deleted file mode 100644 index 386fb1b9e6f95..0000000000000 --- a/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.closure/closure.main-{closure#7}.-------.InstrumentCoverage.0.html +++ /dev/null @@ -1,115 +0,0 @@ - - - - -closure.main-{closure#7} - Coverage Spans - - - -
| _unused_arg: u8 | @0,1,2⦊{ - println!("not called") - }⦉@0,1,2
- - diff --git a/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.closure/closure.main-{closure#8}.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.closure/closure.main-{closure#8}.-------.InstrumentCoverage.0.html deleted file mode 100644 index f9da6ac9dfc34..0000000000000 --- a/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.closure/closure.main-{closure#8}.-------.InstrumentCoverage.0.html +++ /dev/null @@ -1,89 +0,0 @@ - - - - -closure.main-{closure#8} - Coverage Spans - - - -
| - _unused_arg: u8 - | @0,1,2⦊{ println!("not called") }⦉@0,1,2
- - diff --git a/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.closure/closure.main-{closure#9}.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.closure/closure.main-{closure#9}.-------.InstrumentCoverage.0.html deleted file mode 100644 index e259fc9bb67e7..0000000000000 --- a/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.closure/closure.main-{closure#9}.-------.InstrumentCoverage.0.html +++ /dev/null @@ -1,89 +0,0 @@ - - - - -closure.main-{closure#9} - Coverage Spans - - - -
| - _unused_arg: u8 - | @0,1,2⦊{ println!("not called") }⦉@0,1,2
- - diff --git a/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.closure/closure.main.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.closure/closure.main.-------.InstrumentCoverage.0.html deleted file mode 100644 index a7d1728114ec9..0000000000000 --- a/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.closure/closure.main.-------.InstrumentCoverage.0.html +++ /dev/null @@ -1,10921 +0,0 @@ - - - - -closure.main - Coverage Spans - - - -
@0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42⦊fn main() { - // Initialize test constants in a way that cannot be determined at compile time, to ensure - // rustc and LLVM cannot optimize out statements (or coverage counters) downstream from - // dependent conditions. - let is_true = std::env::args().len() == 1; - let is_false = ! is_true; - - let mut some_string = Some(String::from("the string content")); - println!( - "The string or alt: {}" - , - some_string - . - unwrap_or_else - ( - ⦉@0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42|| - { - let mut countdown = 0; - if is_false { - countdown = 10; - } - "alt string 1".to_owned() - }@0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42⦊ - ) - ); - - some_string = Some(String::from("the string content")); - let - a - = - ⦉@0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42|| - { - let mut countdown = 0; - if is_false { - countdown = 10; - } - "alt string 2".to_owned() - }@0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42⦊; - println!( - "The string or alt: {}" - , - some_string - . - unwrap_or_else - ( - a - ) - ); - - some_string = None; - println!( - "The string or alt: {}" - , - some_string - . - unwrap_or_else - ( - ⦉@0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42|| - { - let mut countdown = 0; - if is_false { - countdown = 10; - } - "alt string 3".to_owned() - }@0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42⦊ - ) - ); - - some_string = None; - let - a - = - ⦉@0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42|| - { - let mut countdown = 0; - if is_false { - countdown = 10; - } - "alt string 4".to_owned() - }@0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42⦊; - println!( - "The string or alt: {}" - , - some_string - . - unwrap_or_else - ( - a - ) - ); - - let - quote_closure - = - ⦉@0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42|val| - { - let mut countdown = 0; - if is_false { - countdown = 10; - } - format!("'{}'", val) - }@0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42⦊; - println!( - "Repeated, quoted string: {:?}" - , - std::iter::repeat("repeat me") - .take(5) - .map - ( - quote_closure - ) - .collect::<Vec<_>>() - ); - - let - _unused_closure - = - ⦉@0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42| - mut countdown - | - { - if is_false { - countdown = 10; - } - "closure should be unused".to_owned() - }@0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42⦊; - - let mut countdown = 10; - let _short_unused_closure = ⦉@0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42| _unused_arg: u8 | countdown += 1@0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42⦊; - - // Macros can sometimes confuse the coverage results. Compare this next assignment, with an - // unused closure that invokes the `println!()` macro, with the closure assignment above, that - // does not use a macro. The closure above correctly shows `0` executions. - let _short_unused_closure = ⦉@0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42| _unused_arg: u8 | println!("not called")@0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42⦊; - // The closure assignment above is executed, with a line count of `1`, but the `println!()` - // could not have been called, and yet, there is no indication that it wasn't... - - // ...but adding block braces gives the expected result, showing the block was not executed. - let _short_unused_closure_block = ⦉@0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42| _unused_arg: u8 | { println!("not called") }@0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42⦊; - - let _shortish_unused_closure = ⦉@0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42| _unused_arg: u8 | { - println!("not called") - }@0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42⦊; - - let _as_short_unused_closure = ⦉@0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42| - _unused_arg: u8 - | { println!("not called") }@0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42⦊; - - let _almost_as_short_unused_closure = ⦉@0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42| - _unused_arg: u8 - | { println!("not called") }@0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42⦊ - ; -}⦉@0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42
- - diff --git a/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.conditions/conditions.main.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.conditions/conditions.main.-------.InstrumentCoverage.0.html deleted file mode 100644 index 0aa6fe65686cf..0000000000000 --- a/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.conditions/conditions.main.-------.InstrumentCoverage.0.html +++ /dev/null @@ -1,307 +0,0 @@ - - - - -conditions.main - Coverage Spans - - - -
@0⦊fn main() ⦉@0{ - let @0⦊mut countdown = 0; - if true⦉@0 @1⦊{ - countdown = 10; - }⦉@1@2⦊⦉@2 - - const B: u32 = 100; - let @21⦊x⦉@21 = if @3⦊countdown > 7⦉@3 { - @4,6⦊countdown -= 4; - B⦉@4,6 - } else if @5⦊countdown > 2⦉@5 { - if @7⦊countdown < 1⦉@7 || @15⦊countdown > 5⦉@15 || @11⦊countdown != 9⦉@11 @17⦊{ - countdown = 0; - }⦉@17@18⦊⦉@18 - @19,20⦊countdown -= 5; - countdown⦉@19,20 - } else { - @8⦊return⦉@8; - }; - - let @21⦊mut countdown = 0; - if true⦉@21 @22⦊{ - countdown = 10; - }⦉@22@23⦊⦉@23 - - if @24⦊countdown > 7⦉@24 @25,27⦊{ - countdown -= 4; - }⦉@25,27 else if @26⦊countdown > 2⦉@26 { - if @28⦊countdown < 1⦉@28 || @36⦊countdown > 5⦉@36 || @32⦊countdown != 9⦉@32 @38⦊{ - countdown = 0; - }⦉@38@39⦊⦉@39 - @40,41⦊countdown -= 5⦉@40,41; - } else { - @29⦊return⦉@29; - } - - if @42⦊true⦉@42 { - let @43⦊mut countdown = 0; - if true⦉@43 @45⦊{ - countdown = 10; - }⦉@45@46⦊⦉@46 - - if @47⦊countdown > 7⦉@47 @48,50⦊{ - countdown -= 4; - }⦉@48,50 - else if @49⦊countdown > 2⦉@49 { - if @51⦊countdown < 1⦉@51 || @59⦊countdown > 5⦉@59 || @55⦊countdown != 9⦉@55 @61⦊{ - countdown = 0; - }⦉@61@62⦊⦉@62 - @63,64⦊countdown -= 5⦉@63,64; - } else { - @52⦊return⦉@52; - } - }@44⦊⦉@44 // Note: closing brace shows uncovered (vs. `0` for implicit else) because condition literal - // `true` was const-evaluated. The compiler knows the `if` block will be executed. - - let @66⦊mut countdown = 0; - if true⦉@66 @67⦊{ - countdown = 1; - }⦉@67@68⦊⦉@68 - - let @89⦊z⦉@89 = if @69⦊countdown > 7⦉@69 @70,72⦊{ - countdown -= 4; - }⦉@70,72 else if @71⦊countdown > 2⦉@71 { - if @73⦊countdown < 1⦉@73 || @81⦊countdown > 5⦉@81 || @77⦊countdown != 9⦉@77 @83⦊{ - countdown = 0; - }⦉@83@84⦊⦉@84 - @85,86⦊countdown -= 5⦉@85,86; - } else { - let @74,87,88⦊should_be_reachable = countdown; - println!("reached"); - return⦉@74,87,88; - }; - - let @107⦊w⦉@107 = if @89⦊countdown > 7⦉@89 @90,92⦊{ - countdown -= 4; - }⦉@90,92 else if @91⦊countdown > 2⦉@91 { - if @93⦊countdown < 1⦉@93 || @101⦊countdown > 5⦉@101 || @97⦊countdown != 9⦉@97 @103⦊{ - countdown = 0; - }⦉@103@104⦊⦉@104 - @105,106⦊countdown -= 5⦉@105,106; - } else { - @94⦊return⦉@94; - }; -}@111⦊⦉@111
- - diff --git a/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.dead_code/dead_code.main.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.dead_code/dead_code.main.-------.InstrumentCoverage.0.html deleted file mode 100644 index be06ddd126da9..0000000000000 --- a/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.dead_code/dead_code.main.-------.InstrumentCoverage.0.html +++ /dev/null @@ -1,143 +0,0 @@ - - - - -dead_code.main - Coverage Spans - - - -
@0,1,2,3⦊fn main() { - // Initialize test constants in a way that cannot be determined at compile time, to ensure - // rustc and LLVM cannot optimize out statements (or coverage counters) downstream from - // dependent conditions. - let is_true = std::env::args().len() == 1; - - let mut countdown = 0; - if is_true⦉@0,1,2,3 @4⦊{ - countdown = 10; - }⦉@4@5⦊⦉@5 -}@6⦊⦉@6
- - diff --git a/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.dead_code/dead_code.unused_fn.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.dead_code/dead_code.unused_fn.-------.InstrumentCoverage.0.html deleted file mode 100644 index 7f2d8d3c8ec1e..0000000000000 --- a/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.dead_code/dead_code.unused_fn.-------.InstrumentCoverage.0.html +++ /dev/null @@ -1,143 +0,0 @@ - - - - -dead_code.unused_fn - Coverage Spans - - - -
@0,1,2,3⦊fn unused_fn() { - // Initialize test constants in a way that cannot be determined at compile time, to ensure - // rustc and LLVM cannot optimize out statements (or coverage counters) downstream from - // dependent conditions. - let is_true = std::env::args().len() == 1; - - let mut countdown = 0; - if is_true⦉@0,1,2,3 @4⦊{ - countdown = 10; - }⦉@4@5⦊⦉@5 -}@6⦊⦉@6
- - diff --git a/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.dead_code/dead_code.unused_pub_fn_not_in_library.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.dead_code/dead_code.unused_pub_fn_not_in_library.-------.InstrumentCoverage.0.html deleted file mode 100644 index be44f71348c17..0000000000000 --- a/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.dead_code/dead_code.unused_pub_fn_not_in_library.-------.InstrumentCoverage.0.html +++ /dev/null @@ -1,143 +0,0 @@ - - - - -dead_code.unused_pub_fn_not_in_library - Coverage Spans - - - -
@0,1,2,3⦊pub fn unused_pub_fn_not_in_library() { - // Initialize test constants in a way that cannot be determined at compile time, to ensure - // rustc and LLVM cannot optimize out statements (or coverage counters) downstream from - // dependent conditions. - let is_true = std::env::args().len() == 1; - - let mut countdown = 0; - if is_true⦉@0,1,2,3 @4⦊{ - countdown = 10; - }⦉@4@5⦊⦉@5 -}@6⦊⦉@6
- - diff --git a/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.doctest/doctest.main.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.doctest/doctest.main.-------.InstrumentCoverage.0.html deleted file mode 100644 index 95813decf9f6a..0000000000000 --- a/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.doctest/doctest.main.-------.InstrumentCoverage.0.html +++ /dev/null @@ -1,96 +0,0 @@ - - - - -doctest.main - Coverage Spans - - - -
@0⦊fn main() ⦉@0{ - if @0⦊true⦉@0 { - @4⦊@3⦊assert_eq!(1, 1);⦉@3⦉@4 - } else { - @6⦊@5⦊assert_eq!(1, 2);⦉@5⦉@6 - } -}@7⦊⦉@7
- - diff --git a/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.doctest_crate/doctest_crate.fn_run_in_doctests.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.doctest_crate/doctest_crate.fn_run_in_doctests.-------.InstrumentCoverage.0.html deleted file mode 100644 index 3a41d3482b0b9..0000000000000 --- a/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.doctest_crate/doctest_crate.fn_run_in_doctests.-------.InstrumentCoverage.0.html +++ /dev/null @@ -1,113 +0,0 @@ - - - - -doctest_crate.fn_run_in_doctests - Coverage Spans - - - -
@0⦊pub fn fn_run_in_doctests(conditional: usize) ⦉@0{ - match @0⦊conditional⦉@0 { - 1 => @7⦊@6⦊assert_eq!(1, 1)⦉@6⦉@7, // this is run, - 2 => @10⦊@9⦊assert_eq!(1, 1)⦉@9⦉@10, // this, - 3 => @13⦊@12⦊assert_eq!(1, 1)⦉@12⦉@13, // and this too - _ => @15⦊@14⦊assert_eq!(1, 2)⦉@14⦉@15, // however this is not - } -}@16⦊⦉@16
- - diff --git a/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.drop_trait/drop_trait.main.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.drop_trait/drop_trait.main.-------.InstrumentCoverage.0.html deleted file mode 100644 index 66a6e776a0622..0000000000000 --- a/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.drop_trait/drop_trait.main.-------.InstrumentCoverage.0.html +++ /dev/null @@ -1,152 +0,0 @@ - - - - -drop_trait.main - Coverage Spans - - - -
@0⦊fn main() -> Result<(),u8> { - let _firecracker = Firework { strength: 1 }; - - let _tnt = Firework { strength: 100 }; - - if true⦉@0 { - @1,3,4,8,9⦊println!("Exiting with error..."); - return Err(1)⦉@1,3,4,8,9; - }@2,5,6,7⦊ - - let _ = Firework { strength: 1000 }; - - Ok(())⦉@2,5,6,7 -}@10⦊⦉@10
- - diff --git a/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.drop_trait/drop_trait.{impl#0}-drop.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.drop_trait/drop_trait.{impl#0}-drop.-------.InstrumentCoverage.0.html deleted file mode 100644 index 57d56c5cf7369..0000000000000 --- a/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.drop_trait/drop_trait.{impl#0}-drop.-------.InstrumentCoverage.0.html +++ /dev/null @@ -1,133 +0,0 @@ - - - - -drop_trait.{impl#0}-drop - Coverage Spans - - - -
@0,1,2,3⦊fn drop(&mut self) { - println!("BOOM times {}!!!", self.strength); - }⦉@0,1,2,3
- - diff --git a/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.generics/generics.main.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.generics/generics.main.-------.InstrumentCoverage.0.html deleted file mode 100644 index 098c140425160..0000000000000 --- a/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.generics/generics.main.-------.InstrumentCoverage.0.html +++ /dev/null @@ -1,237 +0,0 @@ - - - - -generics.main - Coverage Spans - - - -
@0,1,2,3⦊fn main() -> Result<(),u8> { - let mut firecracker = Firework { strength: 1 }; - firecracker.set_strength(2); - - let mut tnt = Firework { strength: 100.1 }; - tnt.set_strength(200.1); - tnt.set_strength(300.3); - - if true⦉@0,1,2,3 { - @4,6,7,11,12⦊println!("Exiting with error..."); - return Err(1)⦉@4,6,7,11,12; - }@5,8,9,10⦊ // The remaining lines below have no coverage because `if true` (with the constant literal - // `true`) is guaranteed to execute the `then` block, which is also guaranteed to `return`. - // Thankfully, in the normal case, conditions are not guaranteed ahead of time, and as shown - // in other tests, the lines below would have coverage (which would show they had `0` - // executions, assuming the condition still evaluated to `true`). - - let _ = Firework { strength: 1000 }; - - Ok(())⦉@5,8,9,10 -}@13⦊⦉@13
- - diff --git a/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.generics/generics.{impl#0}-set_strength.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.generics/generics.{impl#0}-set_strength.-------.InstrumentCoverage.0.html deleted file mode 100644 index 329641d47bdbc..0000000000000 --- a/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.generics/generics.{impl#0}-set_strength.-------.InstrumentCoverage.0.html +++ /dev/null @@ -1,85 +0,0 @@ - - - - -generics.{impl#0}-set_strength - Coverage Spans - - - -
@0⦊fn set_strength(&mut self, new_strength: T) { - self.strength = new_strength; - }⦉@0
- - diff --git a/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.generics/generics.{impl#1}-drop.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.generics/generics.{impl#1}-drop.-------.InstrumentCoverage.0.html deleted file mode 100644 index 4908bc7b4a772..0000000000000 --- a/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.generics/generics.{impl#1}-drop.-------.InstrumentCoverage.0.html +++ /dev/null @@ -1,133 +0,0 @@ - - - - -generics.{impl#1}-drop - Coverage Spans - - - -
@0,1,2,3⦊fn drop(&mut self) { - println!("BOOM times {}!!!", self.strength); - }⦉@0,1,2,3
- - diff --git a/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.if/if.main.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.if/if.main.-------.InstrumentCoverage.0.html deleted file mode 100644 index d6eccf57846ef..0000000000000 --- a/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.if/if.main.-------.InstrumentCoverage.0.html +++ /dev/null @@ -1,238 +0,0 @@ - - - - -if.main - Coverage Spans - - - -
@0,1,2,3⦊fn main() { - // Initialize test constants in a way that cannot be determined at compile time, to ensure - // rustc and LLVM cannot optimize out statements (or coverage counters) downstream from - // dependent conditions. - let - is_true - = - std::env::args().len() - == - 1 - ; - let - mut - countdown - = - 0 - ; - if - is_true⦉@0,1,2,3 - @4⦊{ - countdown - = - 10 - ; - }⦉@4@5⦊⦉@5 -}@6⦊⦉@6
- - diff --git a/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.if_else/if_else.main.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.if_else/if_else.main.-------.InstrumentCoverage.0.html deleted file mode 100644 index e0f0ac4020594..0000000000000 --- a/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.if_else/if_else.main.-------.InstrumentCoverage.0.html +++ /dev/null @@ -1,195 +0,0 @@ - - - - -if_else.main - Coverage Spans - - - -
@0,1,2,3⦊fn main() { - // Initialize test constants in a way that cannot be determined at compile time, to ensure - // rustc and LLVM cannot optimize out statements (or coverage counters) downstream from - // dependent conditions. - let is_true = std::env::args().len() == 1; - - let mut countdown = 0; - if - is_true⦉@0,1,2,3 - @4⦊{ - countdown - = - 10 - ; - }⦉@4 - else // Note coverage region difference without semicolon - { - @5⦊countdown - = - 100⦉@5 - } - - if - @6⦊is_true⦉@6 - @7⦊{ - countdown - = - 10 - ; - }⦉@7 - else - @8⦊{ - countdown - = - 100 - ; - }⦉@8 -}@9⦊⦉@9
- - diff --git a/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.inner_items/inner_items.main-InTrait-default_trait_func.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.inner_items/inner_items.main-InTrait-default_trait_func.-------.InstrumentCoverage.0.html deleted file mode 100644 index 1dc5bb64e0bc3..0000000000000 --- a/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.inner_items/inner_items.main-InTrait-default_trait_func.-------.InstrumentCoverage.0.html +++ /dev/null @@ -1,93 +0,0 @@ - - - - -inner_items.main-InTrait-default_trait_func - Coverage Spans - - - -
@0,1,2⦊fn default_trait_func(&mut self) { - in_func(IN_CONST); - self.trait_func(IN_CONST); - }⦉@0,1,2
- - diff --git a/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.inner_items/inner_items.main-in_func.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.inner_items/inner_items.main-in_func.-------.InstrumentCoverage.0.html deleted file mode 100644 index 82724e5e86517..0000000000000 --- a/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.inner_items/inner_items.main-in_func.-------.InstrumentCoverage.0.html +++ /dev/null @@ -1,203 +0,0 @@ - - - - -inner_items.main-in_func - Coverage Spans - - - -
@0,1,2,3,4⦊fn in_func(a: u32) { - let b = 1; - let c = a + b; - println!("c = {}", c) - }⦉@0,1,2,3,4
- - diff --git a/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.inner_items/inner_items.main-{impl#0}-trait_func.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.inner_items/inner_items.main-{impl#0}-trait_func.-------.InstrumentCoverage.0.html deleted file mode 100644 index b00a781a0a74c..0000000000000 --- a/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.inner_items/inner_items.main-{impl#0}-trait_func.-------.InstrumentCoverage.0.html +++ /dev/null @@ -1,101 +0,0 @@ - - - - -inner_items.main-{impl#0}-trait_func - Coverage Spans - - - -
@0,1,2⦊fn trait_func(&mut self, incr: u32) { - self.in_struct_field += incr; - in_func(self.in_struct_field); - }⦉@0,1,2
- - diff --git a/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.inner_items/inner_items.main.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.inner_items/inner_items.main.-------.InstrumentCoverage.0.html deleted file mode 100644 index 4a1003dfbed32..0000000000000 --- a/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.inner_items/inner_items.main.-------.InstrumentCoverage.0.html +++ /dev/null @@ -1,189 +0,0 @@ - - - - -inner_items.main - Coverage Spans - - - -
@0,1,2,3⦊fn main() ⦉@0,1,2,3{ - // Initialize test constants in a way that cannot be determined at compile time, to ensure - // rustc and LLVM cannot optimize out statements (or coverage counters) downstream from - // dependent conditions. - let @0,1,2,3⦊is_true = std::env::args().len() == 1; - - let mut countdown = 0; - if is_true⦉@0,1,2,3 @4⦊{ - countdown = 10; - }⦉@4@5⦊⦉@5 - - mod in_mod { - const IN_MOD_CONST: u32 = 1000; - } - - fn in_func(a: u32) { - let b = 1; - let c = a + b; - println!("c = {}", c) - } - - struct InStruct { - in_struct_field: u32, - } - - const IN_CONST: u32 = 1234; - - trait InTrait { - fn trait_func(&mut self, incr: u32); - - fn default_trait_func(&mut self) { - in_func(IN_CONST); - self.trait_func(IN_CONST); - } - } - - impl InTrait for InStruct { - fn trait_func(&mut self, incr: u32) { - self.in_struct_field += incr; - in_func(self.in_struct_field); - } - } - - type InType = String; - - if @6⦊is_true⦉@6 @7,9⦊{ - in_func(countdown); - }⦉@7,9@8⦊⦉@8 - - let @10,11⦊mut val = InStruct { - in_struct_field: 101, - }; - - val.default_trait_func(); -}⦉@10,11
- - diff --git a/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.lazy_boolean/lazy_boolean.main.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.lazy_boolean/lazy_boolean.main.-------.InstrumentCoverage.0.html deleted file mode 100644 index 358e2e2bbba3c..0000000000000 --- a/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.lazy_boolean/lazy_boolean.main.-------.InstrumentCoverage.0.html +++ /dev/null @@ -1,259 +0,0 @@ - - - - -lazy_boolean.main - Coverage Spans - - - -
@0,1,2,3⦊fn main() { - // Initialize test constants in a way that cannot be determined at compile time, to ensure - // rustc and LLVM cannot optimize out statements (or coverage counters) downstream from - // dependent conditions. - let is_true = std::env::args().len() == 1; - - let (mut a, mut b, mut c) = (0, 0, 0); - if is_true⦉@0,1,2,3 @4⦊{ - a = 1; - b = 10; - c = 100; - }⦉@4@5⦊⦉@5 - let - @10⦊somebool⦉@10 - = - @6⦊a < b⦉@6 - || - @9⦊b < c⦉@9 - ; - let - @14⦊somebool⦉@14 - = - @10⦊b < a⦉@10 - || - @13⦊b < c⦉@13 - ; - let @18⦊somebool⦉@18 = @14⦊a < b⦉@14 && @17⦊b < c⦉@17; - let @22⦊somebool⦉@22 = @18⦊b < a⦉@18 && @21⦊b < c⦉@21; - - if - @22⦊! - is_true⦉@22 - @23⦊{ - a = 2 - ; - }⦉@23@24⦊⦉@24 - - if - @25⦊is_true⦉@25 - @26⦊{ - b = 30 - ; - }⦉@26 - else - @27⦊{ - c = 400 - ; - }⦉@27 - - if @28⦊!is_true⦉@28 @29⦊{ - a = 2; - }⦉@29@30⦊⦉@30 - - if @31⦊is_true⦉@31 @32⦊{ - b = 30; - }⦉@32 else @33⦊{ - c = 400; - }⦉@33 -}@34⦊⦉@34
- - diff --git a/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.loop_break_value/loop_break_value.main.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.loop_break_value/loop_break_value.main.-------.InstrumentCoverage.0.html deleted file mode 100644 index 076d036c2aff7..0000000000000 --- a/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.loop_break_value/loop_break_value.main.-------.InstrumentCoverage.0.html +++ /dev/null @@ -1,128 +0,0 @@ - - - - -loop_break_value.main - Coverage Spans - - - -
@0,1⦊fn main() { - let result - = - loop - { - break - 10 - ; - } - ; -}⦉@0,1
- - diff --git a/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.loops_branches/loops_branches.main.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.loops_branches/loops_branches.main.-------.InstrumentCoverage.0.html deleted file mode 100644 index 95e8f0b71eab8..0000000000000 --- a/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.loops_branches/loops_branches.main.-------.InstrumentCoverage.0.html +++ /dev/null @@ -1,161 +0,0 @@ - - - - -loops_branches.main - Coverage Spans - - - -
@0,1,2,3⦊fn main() { - let debug_test = DebugTest; - println!("{:?}", debug_test); -}⦉@0,1,2,3
- - diff --git a/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.loops_branches/loops_branches.{impl#0}-fmt.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.loops_branches/loops_branches.{impl#0}-fmt.-------.InstrumentCoverage.0.html deleted file mode 100644 index f6f08b6a7704b..0000000000000 --- a/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.loops_branches/loops_branches.{impl#0}-fmt.-------.InstrumentCoverage.0.html +++ /dev/null @@ -1,100 +0,0 @@ - - - - -loops_branches.{impl#0}-fmt - Coverage Spans - - - -
@0⦊fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { - if true⦉@0 { - if @1⦊false⦉@1 { - while @4,5⦊true⦉@4,5 @6,8⦊{ - }⦉@6,8 - }@3⦊⦉@3 - @9,10,11,12⦊write!(f, "error")⦉@9,10,11,12@14,16,17,18⦊?⦉@14,16,17,18; - } else @2⦊{ - }⦉@2 - @19⦊Ok(())⦉@19 - }@20⦊⦉@20
- - diff --git a/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.match_or_pattern/match_or_pattern.main.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.match_or_pattern/match_or_pattern.main.-------.InstrumentCoverage.0.html deleted file mode 100644 index 013c292ce9a43..0000000000000 --- a/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.match_or_pattern/match_or_pattern.main.-------.InstrumentCoverage.0.html +++ /dev/null @@ -1,259 +0,0 @@ - - - - -match_or_pattern.main - Coverage Spans - - - -
@0,1,2,3⦊fn main() { - // Initialize test constants in a way that cannot be determined at compile time, to ensure - // rustc and LLVM cannot optimize out statements (or coverage counters) downstream from - // dependent conditions. - let is_true = std::env::args().len() == 1; - - let mut a: u8 = 0; - let mut b: u8 = 0; - if is_true⦉@0,1,2,3 @4⦊{ - a = 2; - b = 0; - }⦉@4@5⦊⦉@5 - match @6⦊(a, b)⦉@6 { - // Or patterns generate MIR `SwitchInt` with multiple targets to the same `BasicBlock`. - // This test confirms a fix for Issue #79569. - (0 | 1, 2 | 3) => @9,10⦊{}⦉@9,10 - _ => @7⦊{}⦉@7 - } - if @11⦊is_true⦉@11 @12⦊{ - a = 0; - b = 0; - }⦉@12@13⦊⦉@13 - match @14⦊(a, b)⦉@14 { - (0 | 1, 2 | 3) => @17,18⦊{}⦉@17,18 - _ => @15⦊{}⦉@15 - } - if @19⦊is_true⦉@19 @20⦊{ - a = 2; - b = 2; - }⦉@20@21⦊⦉@21 - match @22⦊(a, b)⦉@22 { - (0 | 1, 2 | 3) => @25,26⦊{}⦉@25,26 - _ => @23⦊{}⦉@23 - } - if @27⦊is_true⦉@27 @28⦊{ - a = 0; - b = 2; - }⦉@28@29⦊⦉@29 - match @30⦊(a, b)⦉@30 { - (0 | 1, 2 | 3) => @33,34⦊{}⦉@33,34 - _ => @31⦊{}⦉@31 - } -}@35⦊⦉@35
- - diff --git a/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.nested_loops/nested_loops.main.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.nested_loops/nested_loops.main.-------.InstrumentCoverage.0.html deleted file mode 100644 index 1abc24156d9c3..0000000000000 --- a/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.nested_loops/nested_loops.main.-------.InstrumentCoverage.0.html +++ /dev/null @@ -1,166 +0,0 @@ - - - - -nested_loops.main - Coverage Spans - - - -
@0,1,2,3⦊fn main() { - let is_true = std::env::args().len() == 1; - let mut countdown = 10⦉@0,1,2,3; - - 'outer: while @4,5⦊countdown > 0⦉@4,5 { - let @6,8,9⦊mut a = 100; - let mut b = 100⦉@6,8,9; - for @14,16⦊_⦉@14,16 in @10,11,12⦊0..50⦉@10,11,12 { - if @14,16⦊a < 30⦉@14,16 { - @17⦊break⦉@17; - }@18,19,20⦊ - a -= 5; - b -= 5; - if b < 90⦉@18,19,20 { - @21,23⦊a -= 10; - if is_true⦉@21,23 { - @24⦊break 'outer⦉@24; - } else @25,26⦊{ - a -= 2; - }⦉@25,26 - }@22⦊⦉@22 - } - @28,29⦊countdown -= 1⦉@28,29; - } -}@30⦊⦉@30
- - diff --git a/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.overflow/overflow.main.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.overflow/overflow.main.-------.InstrumentCoverage.0.html deleted file mode 100644 index 2a9b1b10efcd2..0000000000000 --- a/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.overflow/overflow.main.-------.InstrumentCoverage.0.html +++ /dev/null @@ -1,258 +0,0 @@ - - - - -overflow.main - Coverage Spans - - - -
@0⦊fn main() -> Result<(),u8> { - let mut countdown = 10⦉@0; - while @1,2⦊countdown > 0⦉@1,2 { - if @3,5⦊countdown == 1⦉@3,5 @6,8,9,10,11⦊{ - let result = might_overflow(10); - println!("Result: {}", result); - }⦉@6,8,9,10,11 else if @7⦊countdown < 5⦉@7 @12,14,15,16,17⦊{ - let result = might_overflow(1); - println!("Result: {}", result); - }⦉@12,14,15,16,17@13⦊⦉@13 - @19,20⦊countdown -= 1⦉@19,20; - } - @4⦊Ok(()) -}⦉@4
- - diff --git a/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.overflow/overflow.might_overflow.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.overflow/overflow.might_overflow.-------.InstrumentCoverage.0.html deleted file mode 100644 index c6043f0bd07fe..0000000000000 --- a/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.overflow/overflow.might_overflow.-------.InstrumentCoverage.0.html +++ /dev/null @@ -1,394 +0,0 @@ - - - - -overflow.might_overflow - Coverage Spans - - - -
@0⦊fn might_overflow(to_add: u32) -> u32 { - if to_add > 5⦉@0 @1,3,4⦊{ - println!("this will probably overflow"); - }⦉@1,3,4@2⦊⦉@2 - let @5,6,7,8,9,10,11,12,13⦊add_to = u32::MAX - 5; - println!("does {} + {} overflow?", add_to, to_add); - let result = to_add + add_to; - println!("continuing after overflow check"); - result -}⦉@5,6,7,8,9,10,11,12,13
- - diff --git a/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.panic_unwind/panic_unwind.main.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.panic_unwind/panic_unwind.main.-------.InstrumentCoverage.0.html deleted file mode 100644 index 5b097f118e3a8..0000000000000 --- a/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.panic_unwind/panic_unwind.main.-------.InstrumentCoverage.0.html +++ /dev/null @@ -1,102 +0,0 @@ - - - - -panic_unwind.main - Coverage Spans - - - -
@0⦊fn main() -> Result<(), u8> { - let mut countdown = 10⦉@0; - while @1,2⦊countdown > 0⦉@1,2 { - if @3,5⦊countdown == 1⦉@3,5 @6,8⦊{ - might_panic(true); - }⦉@6,8 else if @7⦊countdown < 5⦉@7 @9,11⦊{ - might_panic(false); - }⦉@9,11@10⦊⦉@10 - @13,14⦊countdown -= 1⦉@13,14; - } - @4⦊Ok(()) -}⦉@4
- - diff --git a/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.panic_unwind/panic_unwind.might_panic.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.panic_unwind/panic_unwind.might_panic.-------.InstrumentCoverage.0.html deleted file mode 100644 index 32988629ba0eb..0000000000000 --- a/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.panic_unwind/panic_unwind.might_panic.-------.InstrumentCoverage.0.html +++ /dev/null @@ -1,163 +0,0 @@ - - - - -panic_unwind.might_panic - Coverage Spans - - - -
@0⦊fn might_panic(should_panic: bool) ⦉@0{ - if @0⦊should_panic⦉@0 { - @1,3,4⦊println!("panicking..."); - panic!("panics");⦉@1,3,4 - } else @2,5,6⦊{ - println!("Don't Panic"); - } -}⦉@2,5,6
- - diff --git a/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.partial_eq/partial_eq.main.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.partial_eq/partial_eq.main.-------.InstrumentCoverage.0.html deleted file mode 100644 index 3e307c4f460d5..0000000000000 --- a/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.partial_eq/partial_eq.main.-------.InstrumentCoverage.0.html +++ /dev/null @@ -1,295 +0,0 @@ - - - - -partial_eq.main - Coverage Spans - - - -
@0,1,2,3,4,5,6,7,8⦊fn main() { - let version_3_2_1 = Version::new(3, 2, 1); - let version_3_3_0 = Version::new(3, 3, 0); - - println!("{:?} < {:?} = {}", version_3_2_1, version_3_3_0, version_3_2_1 < version_3_3_0); -}⦉@0,1,2,3,4,5,6,7,8
- - diff --git a/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.partial_eq/partial_eq.{impl#0}-new.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.partial_eq/partial_eq.{impl#0}-new.-------.InstrumentCoverage.0.html deleted file mode 100644 index 820ccf74d1b9e..0000000000000 --- a/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.partial_eq/partial_eq.{impl#0}-new.-------.InstrumentCoverage.0.html +++ /dev/null @@ -1,108 +0,0 @@ - - - - -partial_eq.{impl#0}-new - Coverage Spans - - - -
@0⦊pub fn new(major: usize, minor: usize, patch: usize) -> Self { - Self { - major, - minor, - patch, - } - }⦉@0
- - diff --git a/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.partial_eq/partial_eq.{impl#1}-cmp.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.partial_eq/partial_eq.{impl#1}-cmp.-------.InstrumentCoverage.0.html deleted file mode 100644 index ee4c10afd8b89..0000000000000 --- a/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.partial_eq/partial_eq.{impl#1}-cmp.-------.InstrumentCoverage.0.html +++ /dev/null @@ -1,74 +0,0 @@ - - - - -partial_eq.{impl#1}-cmp - Coverage Spans - - - -
@0,1⦊⦉@0,1Ord@15⦊⦉@15
- - diff --git a/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.partial_eq/partial_eq.{impl#2}-partial_cmp.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.partial_eq/partial_eq.{impl#2}-partial_cmp.-------.InstrumentCoverage.0.html deleted file mode 100644 index 2f5092bd51f0a..0000000000000 --- a/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.partial_eq/partial_eq.{impl#2}-partial_cmp.-------.InstrumentCoverage.0.html +++ /dev/null @@ -1,74 +0,0 @@ - - - - -partial_eq.{impl#2}-partial_cmp - Coverage Spans - - - -
@0,1⦊⦉@0,1PartialOrd@18⦊⦉@18
- - diff --git a/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.partial_eq/partial_eq.{impl#4}-assert_receiver_is_total_eq.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.partial_eq/partial_eq.{impl#4}-assert_receiver_is_total_eq.-------.InstrumentCoverage.0.html deleted file mode 100644 index ebb8b1c15ce08..0000000000000 --- a/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.partial_eq/partial_eq.{impl#4}-assert_receiver_is_total_eq.-------.InstrumentCoverage.0.html +++ /dev/null @@ -1,75 +0,0 @@ - - - - -partial_eq.{impl#4}-assert_receiver_is_total_eq - Coverage Spans - - - -
@0⦊⦉@0Eq
- - diff --git a/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.partial_eq/partial_eq.{impl#6}-eq.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.partial_eq/partial_eq.{impl#6}-eq.-------.InstrumentCoverage.0.html deleted file mode 100644 index 2e128181c5e42..0000000000000 --- a/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.partial_eq/partial_eq.{impl#6}-eq.-------.InstrumentCoverage.0.html +++ /dev/null @@ -1,74 +0,0 @@ - - - - -partial_eq.{impl#6}-eq - Coverage Spans - - - -
@0⦊⦉@0PartialEq@4⦊⦉@4
- - diff --git a/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.partial_eq/partial_eq.{impl#6}-ne.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.partial_eq/partial_eq.{impl#6}-ne.-------.InstrumentCoverage.0.html deleted file mode 100644 index 637b1c62086c5..0000000000000 --- a/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.partial_eq/partial_eq.{impl#6}-ne.-------.InstrumentCoverage.0.html +++ /dev/null @@ -1,74 +0,0 @@ - - - - -partial_eq.{impl#6}-ne - Coverage Spans - - - -
@0⦊⦉@0PartialEq@4⦊⦉@4
- - diff --git a/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.partial_eq/partial_eq.{impl#7}-fmt.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.partial_eq/partial_eq.{impl#7}-fmt.-------.InstrumentCoverage.0.html deleted file mode 100644 index 930492f24841f..0000000000000 --- a/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.partial_eq/partial_eq.{impl#7}-fmt.-------.InstrumentCoverage.0.html +++ /dev/null @@ -1,110 +0,0 @@ - - - - -partial_eq.{impl#7}-fmt - Coverage Spans - - - -
@0,1,2,3,4,5⦊Debug⦉@0,1,2,3,4,5
- - diff --git a/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.partial_eq/partial_eq.{impl#8}-clone.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.partial_eq/partial_eq.{impl#8}-clone.-------.InstrumentCoverage.0.html deleted file mode 100644 index f1c983933432c..0000000000000 --- a/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.partial_eq/partial_eq.{impl#8}-clone.-------.InstrumentCoverage.0.html +++ /dev/null @@ -1,88 +0,0 @@ - - - - -partial_eq.{impl#8}-clone - Coverage Spans - - - -
@0,1,2,3⦊Clone⦉@0,1,2,3
- - diff --git a/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.simple_loop/simple_loop.main.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.simple_loop/simple_loop.main.-------.InstrumentCoverage.0.html deleted file mode 100644 index 6b911eea34121..0000000000000 --- a/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.simple_loop/simple_loop.main.-------.InstrumentCoverage.0.html +++ /dev/null @@ -1,193 +0,0 @@ - - - - -simple_loop.main - Coverage Spans - - - -
@0,1,2,3⦊fn main() { - // Initialize test constants in a way that cannot be determined at compile time, to ensure - // rustc and LLVM cannot optimize out statements (or coverage counters) downstream from - // dependent conditions. - let is_true = std::env::args().len() == 1; - - let mut countdown = 0; - - if - is_true⦉@0,1,2,3 - @4⦊{ - countdown - = - 10 - ; - }⦉@4@5⦊⦉@5 - - loop - { - if - @7,8⦊countdown - == - 0⦉@7,8 - { - @9⦊break⦉@9 - ; - }@10,11⦊ - countdown - -= - 1⦉@10,11 - ; - } -}@9⦊⦉@9
- - diff --git a/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.simple_match/simple_match.main.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.simple_match/simple_match.main.-------.InstrumentCoverage.0.html deleted file mode 100644 index a56692d9c2a48..0000000000000 --- a/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.simple_match/simple_match.main.-------.InstrumentCoverage.0.html +++ /dev/null @@ -1,222 +0,0 @@ - - - - -simple_match.main - Coverage Spans - - - -
@0,1,2,3⦊fn main() { - // Initialize test constants in a way that cannot be determined at compile time, to ensure - // rustc and LLVM cannot optimize out statements (or coverage counters) downstream from - // dependent conditions. - let is_true = std::env::args().len() == 1; - - let mut countdown = 1; - if is_true⦉@0,1,2,3 @4⦊{ - countdown = 0; - }⦉@4@5⦊⦉@5 - - for - @12,14,16⦊_⦉@12,14,16 - in - @8,9,10⦊0..2⦉@8,9,10 - { - let z - ; - match - @12,14,16⦊countdown⦉@12,14,16 - { - @17⦊x⦉@17 - if - @12,14,16⦊x - < - 1⦉@12,14,16 - => - @17⦊{ - z = countdown - ; - let y = countdown - ; - countdown = 10 - ; - }⦉@17 - _ - => - @15⦊{}⦉@15 - } - } -}@11⦊⦉@11
- - diff --git a/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.tight_inf_loop/tight_inf_loop.main.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.tight_inf_loop/tight_inf_loop.main.-------.InstrumentCoverage.0.html deleted file mode 100644 index 8cbd265c6a01c..0000000000000 --- a/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.tight_inf_loop/tight_inf_loop.main.-------.InstrumentCoverage.0.html +++ /dev/null @@ -1,81 +0,0 @@ - - - - -tight_inf_loop.main - Coverage Spans - - - -
@0⦊fn main() { - if false⦉@0 { - @3,4⦊loop {}⦉@3,4 - }@2⦊ -}⦉@2
- - diff --git a/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.try_error_result/try_error_result.call.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.try_error_result/try_error_result.call.-------.InstrumentCoverage.0.html deleted file mode 100644 index a8a3139334c96..0000000000000 --- a/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.try_error_result/try_error_result.call.-------.InstrumentCoverage.0.html +++ /dev/null @@ -1,82 +0,0 @@ - - - - -try_error_result.call - Coverage Spans - - - -
@0⦊fn call(return_error: bool) -> Result<(),()> { - if return_error⦉@0 { - @1⦊Err(())⦉@1 - } else { - @2⦊Ok(())⦉@2 - } -}@3⦊⦉@3
- - diff --git a/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.try_error_result/try_error_result.main.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.try_error_result/try_error_result.main.-------.InstrumentCoverage.0.html deleted file mode 100644 index 5b0c5cb072f04..0000000000000 --- a/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.try_error_result/try_error_result.main.-------.InstrumentCoverage.0.html +++ /dev/null @@ -1,129 +0,0 @@ - - - - -try_error_result.main - Coverage Spans - - - -
@0,1⦊fn main() -> Result<(),()> { - let mut - countdown = 10⦉@0,1 - ; - for - @6,8,9⦊_⦉@6,8,9 - in - @2,3,4⦊0..10⦉@2,3,4 - { - @6,8,9⦊countdown - -= 1 - ; - if - countdown < 5⦉@6,8,9 - { - @10,12,13⦊call(/*return_error=*/ true)⦉@10,12,13@15,17,18,19⦊?⦉@15,17,18,19; - @14,20,21⦊call(/*return_error=*/ false)⦉@14,20,21@23,25,26,27⦊?⦉@23,25,26,27; - } - else - { - @11,28,29⦊call(/*return_error=*/ false)⦉@11,28,29@31,33,34,35⦊?⦉@31,33,34,35; - } - } - @5⦊Ok(())⦉@5 -}@38⦊⦉@38
- - diff --git a/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.used_crate/used_crate.unused_function.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.used_crate/used_crate.unused_function.-------.InstrumentCoverage.0.html deleted file mode 100644 index 65e21ecef13bd..0000000000000 --- a/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.used_crate/used_crate.unused_function.-------.InstrumentCoverage.0.html +++ /dev/null @@ -1,115 +0,0 @@ - - - - -used_crate.unused_function - Coverage Spans - - - -
@0,1,2,3⦊pub fn unused_function() { - let is_true = std::env::args().len() == 1; - let mut countdown = 2; - if !is_true⦉@0,1,2,3 @4⦊{ - countdown = 20; - }⦉@4@5⦊⦉@5 -}@6⦊⦉@6
- - diff --git a/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.used_crate/used_crate.unused_generic_function.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.used_crate/used_crate.unused_generic_function.-------.InstrumentCoverage.0.html deleted file mode 100644 index 02154a2268b75..0000000000000 --- a/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.used_crate/used_crate.unused_generic_function.-------.InstrumentCoverage.0.html +++ /dev/null @@ -1,133 +0,0 @@ - - - - -used_crate.unused_generic_function - Coverage Spans - - - -
@0,1,2,3,4⦊pub fn unused_generic_function<T: Debug>(arg: T) { - println!("unused_generic_function with {:?}", arg); -}⦉@0,1,2,3,4
- - diff --git a/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.used_crate/used_crate.unused_private_function.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.used_crate/used_crate.unused_private_function.-------.InstrumentCoverage.0.html deleted file mode 100644 index 78228594e3753..0000000000000 --- a/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.used_crate/used_crate.unused_private_function.-------.InstrumentCoverage.0.html +++ /dev/null @@ -1,115 +0,0 @@ - - - - -used_crate.unused_private_function - Coverage Spans - - - -
@0,1,2,3⦊fn unused_private_function() { - let is_true = std::env::args().len() == 1; - let mut countdown = 2; - if !is_true⦉@0,1,2,3 @4⦊{ - countdown = 20; - }⦉@4@5⦊⦉@5 -}@6⦊⦉@6
- - diff --git a/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.used_crate/used_crate.use_this_lib_crate.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.used_crate/used_crate.use_this_lib_crate.-------.InstrumentCoverage.0.html deleted file mode 100644 index 8f618d2e24954..0000000000000 --- a/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.used_crate/used_crate.use_this_lib_crate.-------.InstrumentCoverage.0.html +++ /dev/null @@ -1,190 +0,0 @@ - - - - -used_crate.use_this_lib_crate - Coverage Spans - - - -
@0,1,2,3,4,5,6,7,8⦊fn use_this_lib_crate() { - used_from_bin_crate_and_lib_crate_generic_function("used from library used_crate.rs"); - used_with_same_type_from_bin_crate_and_lib_crate_generic_function( - "used from library used_crate.rs", - ); - let some_vec = vec![5, 6, 7, 8]; - used_only_from_this_lib_crate_generic_function(some_vec); - used_only_from_this_lib_crate_generic_function("used ONLY from library used_crate.rs"); -}⦉@0,1,2,3,4,5,6,7,8
- - diff --git a/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.used_crate/used_crate.used_from_bin_crate_and_lib_crate_generic_function.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.used_crate/used_crate.used_from_bin_crate_and_lib_crate_generic_function.-------.InstrumentCoverage.0.html deleted file mode 100644 index 61a709c4729f2..0000000000000 --- a/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.used_crate/used_crate.used_from_bin_crate_and_lib_crate_generic_function.-------.InstrumentCoverage.0.html +++ /dev/null @@ -1,133 +0,0 @@ - - - - -used_crate.used_from_bin_crate_and_lib_crate_generic_function - Coverage Spans - - - -
@0,1,2,3,4⦊pub fn used_from_bin_crate_and_lib_crate_generic_function<T: Debug>(arg: T) { - println!("used_from_bin_crate_and_lib_crate_generic_function with {:?}", arg); -}⦉@0,1,2,3,4
- - diff --git a/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.used_crate/used_crate.used_function.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.used_crate/used_crate.used_function.-------.InstrumentCoverage.0.html deleted file mode 100644 index 974a24b2c6d44..0000000000000 --- a/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.used_crate/used_crate.used_function.-------.InstrumentCoverage.0.html +++ /dev/null @@ -1,110 +0,0 @@ - - - - -used_crate.used_function - Coverage Spans - - - -
@0,1,2,3⦊pub fn used_function() ⦉@0,1,2,3{ - // Initialize test constants in a way that cannot be determined at compile time, to ensure - // rustc and LLVM cannot optimize out statements (or coverage counters) downstream from - // dependent conditions. - let @0,1,2,3⦊is_true = std::env::args().len() == 1; - let mut countdown = 0; - if is_true⦉@0,1,2,3 @4⦊{ - countdown = 10; - }⦉@4@5⦊⦉@5 - @6,7⦊use_this_lib_crate(); -}⦉@6,7
- - diff --git a/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.used_crate/used_crate.used_only_from_bin_crate_generic_function.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.used_crate/used_crate.used_only_from_bin_crate_generic_function.-------.InstrumentCoverage.0.html deleted file mode 100644 index 68035339fe4a8..0000000000000 --- a/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.used_crate/used_crate.used_only_from_bin_crate_generic_function.-------.InstrumentCoverage.0.html +++ /dev/null @@ -1,133 +0,0 @@ - - - - -used_crate.used_only_from_bin_crate_generic_function - Coverage Spans - - - -
@0,1,2,3,4⦊pub fn used_only_from_bin_crate_generic_function<T: Debug>(arg: T) { - println!("used_only_from_bin_crate_generic_function with {:?}", arg); -}⦉@0,1,2,3,4
- - diff --git a/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.used_crate/used_crate.used_only_from_this_lib_crate_generic_function.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.used_crate/used_crate.used_only_from_this_lib_crate_generic_function.-------.InstrumentCoverage.0.html deleted file mode 100644 index 63944eb9ab38e..0000000000000 --- a/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.used_crate/used_crate.used_only_from_this_lib_crate_generic_function.-------.InstrumentCoverage.0.html +++ /dev/null @@ -1,133 +0,0 @@ - - - - -used_crate.used_only_from_this_lib_crate_generic_function - Coverage Spans - - - -
@0,1,2,3,4⦊pub fn used_only_from_this_lib_crate_generic_function<T: Debug>(arg: T) { - println!("used_only_from_this_lib_crate_generic_function with {:?}", arg); -}⦉@0,1,2,3,4
- - diff --git a/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.used_crate/used_crate.used_with_same_type_from_bin_crate_and_lib_crate_generic_function.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.used_crate/used_crate.used_with_same_type_from_bin_crate_and_lib_crate_generic_function.-------.InstrumentCoverage.0.html deleted file mode 100644 index b146180fbd155..0000000000000 --- a/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.used_crate/used_crate.used_with_same_type_from_bin_crate_and_lib_crate_generic_function.-------.InstrumentCoverage.0.html +++ /dev/null @@ -1,133 +0,0 @@ - - - - -used_crate.used_with_same_type_from_bin_crate_and_lib_crate_generic_function - Coverage Spans - - - -
@0,1,2,3,4⦊pub fn used_with_same_type_from_bin_crate_and_lib_crate_generic_function<T: Debug>(arg: T) { - println!("used_with_same_type_from_bin_crate_and_lib_crate_generic_function with {:?}", arg); -}⦉@0,1,2,3,4
- - diff --git a/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.uses_crate/uses_crate.main.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.uses_crate/uses_crate.main.-------.InstrumentCoverage.0.html deleted file mode 100644 index 28cf051ecf873..0000000000000 --- a/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.uses_crate/uses_crate.main.-------.InstrumentCoverage.0.html +++ /dev/null @@ -1,193 +0,0 @@ - - - - -uses_crate.main - Coverage Spans - - - -
@0,1,2,3,4,5,6,7,8,9⦊fn main() { - used_crate::used_function(); - let some_vec = vec![1, 2, 3, 4]; - used_crate::used_only_from_bin_crate_generic_function(&some_vec); - used_crate::used_only_from_bin_crate_generic_function("used from bin uses_crate.rs"); - used_crate::used_from_bin_crate_and_lib_crate_generic_function(some_vec); - used_crate::used_with_same_type_from_bin_crate_and_lib_crate_generic_function("interesting?"); -}⦉@0,1,2,3,4,5,6,7,8,9
- - diff --git a/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.while/while.main.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.while/while.main.-------.InstrumentCoverage.0.html deleted file mode 100644 index f037a8ee5c52f..0000000000000 --- a/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.while/while.main.-------.InstrumentCoverage.0.html +++ /dev/null @@ -1,82 +0,0 @@ - - - - -while.main - Coverage Spans - - - -
@0⦊fn main() { - let num = 9⦉@0; - while @1,2⦊num >= 10⦉@1,2 @3,5⦊{ - }⦉@3,5 -}@4⦊⦉@4
- - diff --git a/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.while_early_ret/while_early_ret.main.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.while_early_ret/while_early_ret.main.-------.InstrumentCoverage.0.html deleted file mode 100644 index fcb5418e1d0cf..0000000000000 --- a/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.while_early_ret/while_early_ret.main.-------.InstrumentCoverage.0.html +++ /dev/null @@ -1,130 +0,0 @@ - - - - -while_early_ret.main - Coverage Spans - - - -
@0⦊fn main() -> Result<(),u8> { - let mut countdown = 10⦉@0; - while - @1,2⦊countdown - > - 0⦉@1,2 - { - if - @3,5⦊countdown - < - 5⦉@3,5 - { - return - if - @6⦊countdown - > - 8⦉@6 - { - @8⦊Ok(())⦉@8 - } - else - { - @9⦊Err(1)⦉@9 - } - ; - }@7,11⦊ - countdown - -= - 1⦉@7,11 - ; - } - @4⦊Ok(())⦉@4 -}@12⦊⦉@12
- - diff --git a/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.yield/yield.main-{closure#0}.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.yield/yield.main-{closure#0}.-------.InstrumentCoverage.0.html deleted file mode 100644 index 1e68c345f849b..0000000000000 --- a/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.yield/yield.main-{closure#0}.-------.InstrumentCoverage.0.html +++ /dev/null @@ -1,79 +0,0 @@ - - - - -yield.main-{closure#0} - Coverage Spans - - - -
|| @0⦊{ - yield 1⦉@0; - return @1⦊"foo" - }⦉@1
- - diff --git a/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.yield/yield.main-{closure#1}.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.yield/yield.main-{closure#1}.-------.InstrumentCoverage.0.html deleted file mode 100644 index 842d7823bfd12..0000000000000 --- a/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.yield/yield.main-{closure#1}.-------.InstrumentCoverage.0.html +++ /dev/null @@ -1,81 +0,0 @@ - - - - -yield.main-{closure#1} - Coverage Spans - - - -
|| @0⦊{ - yield 1⦉@0; - @1⦊yield 2⦉@1; - @2⦊yield 3⦉@2; - return @3⦊"foo" - }⦉@3
- - diff --git a/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.yield/yield.main.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.yield/yield.main.-------.InstrumentCoverage.0.html deleted file mode 100644 index 4c0c0d562b840..0000000000000 --- a/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.yield/yield.main.-------.InstrumentCoverage.0.html +++ /dev/null @@ -1,138 +0,0 @@ - - - - -yield.main - Coverage Spans - - - -
@0,1,2⦊fn main() ⦉@0,1,2{ - let @0,1,2⦊mut generator⦉@0,1,2 = || { - yield 1; - return "foo" - }; - - match @0,1,2⦊Pin::new(&mut generator).resume(()) { - GeneratorState::Yielded(1)⦉@0,1,2 => @4,6,7,8⦊{}⦉@4,6,7,8 - _ => @5⦊panic!("unexpected value from resume")⦉@5, - } - match @4,6,7,8⦊Pin::new(&mut generator).resume(())⦉@4,6,7,8 { - GeneratorState::Complete(@10,11⦊"foo"⦉@10,11) => @12,13,14,15⦊{}⦉@12,13,14,15 - _ => @9⦊panic!("unexpected value from resume")⦉@9, - } - - let @12,13,14,15⦊mut generator⦉@12,13,14,15 = || { - yield 1; - yield 2; - yield 3; - return "foo" - }; - - match @12,13,14,15⦊Pin::new(&mut generator).resume(()) { - GeneratorState::Yielded(1)⦉@12,13,14,15 => @17,19,20,21⦊{}⦉@17,19,20,21 - _ => @18⦊panic!("unexpected value from resume")⦉@18, - } - match @17,19,20,21⦊Pin::new(&mut generator).resume(()) { - GeneratorState::Yielded(2)⦉@17,19,20,21 => @23,25⦊{}⦉@23,25 - _ => @24⦊panic!("unexpected value from resume")⦉@24, - } -}@23,25⦊⦉@23,25
- - diff --git a/src/test/run-make-fulldeps/coverage/async.rs b/src/test/run-make-fulldeps/coverage/async.rs index 5553af92465ca..a6e387747068a 100644 --- a/src/test/run-make-fulldeps/coverage/async.rs +++ b/src/test/run-make-fulldeps/coverage/async.rs @@ -1,6 +1,6 @@ #![allow(unused_assignments, dead_code)] -// require-rust-edition-2018 +// compile-flags: --edition=2018 -C opt-level=1 async fn c(x: u8) -> u8 { if x == 8 { @@ -63,7 +63,7 @@ fn j(x: u8) { 0 } } - fn d() -> u8 { 1 } + fn d() -> u8 { 1 } // inner function is defined in-line, but the function is not executed fn f() -> u8 { 1 } match x { y if c(x) == y + 1 => { d(); } @@ -109,11 +109,11 @@ mod executor { pub fn block_on(mut future: F) -> F::Output { let mut future = unsafe { Pin::new_unchecked(&mut future) }; - + use std::hint::unreachable_unchecked; static VTABLE: RawWakerVTable = RawWakerVTable::new( - |_| unimplemented!("clone"), - |_| unimplemented!("wake"), - |_| unimplemented!("wake_by_ref"), + |_| unsafe { unreachable_unchecked() }, // clone + |_| unsafe { unreachable_unchecked() }, // wake + |_| unsafe { unreachable_unchecked() }, // wake_by_ref |_| (), ); let waker = unsafe { Waker::from_raw(RawWaker::new(core::ptr::null(), &VTABLE)) }; diff --git a/src/test/run-make-fulldeps/coverage/async2.rs b/src/test/run-make-fulldeps/coverage/async2.rs new file mode 100644 index 0000000000000..6171d95ff5543 --- /dev/null +++ b/src/test/run-make-fulldeps/coverage/async2.rs @@ -0,0 +1,69 @@ +// compile-flags: --edition=2018 + +use core::{ + future::Future, + marker::Send, + pin::Pin, +}; + +fn non_async_func() { + println!("non_async_func was covered"); + let b = true; + if b { + println!("non_async_func println in block"); + } +} + +// FIXME(#83985): The auto-generated closure in an async function is failing to include +// the println!() and `let` assignment lines in the coverage code region(s), as it does in the +// non-async function above, unless the `println!()` is inside a covered block. +async fn async_func() { + println!("async_func was covered"); + let b = true; + if b { + println!("async_func println in block"); + } +} + +// FIXME(#83985): As above, this async function only has the `println!()` macro call, which is not +// showing coverage, so the entire async closure _appears_ uncovered; but this is not exactly true. +// It's only certain kinds of lines and/or their context that results in missing coverage. +async fn async_func_just_println() { + println!("async_func_just_println was covered"); +} + +fn main() { + println!("codecovsample::main"); + + non_async_func(); + + executor::block_on(async_func()); + executor::block_on(async_func_just_println()); +} + +mod executor { + use core::{ + future::Future, + pin::Pin, + task::{Context, Poll, RawWaker, RawWakerVTable, Waker}, + }; + + pub fn block_on(mut future: F) -> F::Output { + let mut future = unsafe { Pin::new_unchecked(&mut future) }; + use std::hint::unreachable_unchecked; + static VTABLE: RawWakerVTable = RawWakerVTable::new( + |_| unsafe { unreachable_unchecked() }, // clone + |_| unsafe { unreachable_unchecked() }, // wake + |_| unsafe { unreachable_unchecked() }, // wake_by_ref + |_| (), + ); + let waker = unsafe { Waker::from_raw(RawWaker::new(core::ptr::null(), &VTABLE)) }; + let mut context = Context::from_waker(&waker); + + loop { + if let Poll::Ready(val) = future.as_mut().poll(&mut context) { + break val; + } + } + } +} diff --git a/src/test/run-make-fulldeps/coverage/closure.rs b/src/test/run-make-fulldeps/coverage/closure.rs index 914cb0fe051c7..796512f0c71ed 100644 --- a/src/test/run-make-fulldeps/coverage/closure.rs +++ b/src/test/run-make-fulldeps/coverage/closure.rs @@ -1,5 +1,5 @@ #![allow(unused_assignments, unused_variables)] - +// compile-flags: -C opt-level=2 # fix described in rustc_middle/mir/mono.rs fn main() { // Initialize test constants in a way that cannot be determined at compile time, to ensure // rustc and LLVM cannot optimize out statements (or coverage counters) downstream from diff --git a/src/test/run-make-fulldeps/coverage/continue.rs b/src/test/run-make-fulldeps/coverage/continue.rs new file mode 100644 index 0000000000000..624aa98341b80 --- /dev/null +++ b/src/test/run-make-fulldeps/coverage/continue.rs @@ -0,0 +1,69 @@ +#![allow(unused_assignments, unused_variables)] + +fn main() { + let is_true = std::env::args().len() == 1; + + let mut x = 0; + for _ in 0..10 { + match is_true { + true => { + continue; + } + _ => { + x = 1; + } + } + x = 3; + } + for _ in 0..10 { + match is_true { + false => { + x = 1; + } + _ => { + continue; + } + } + x = 3; + } + for _ in 0..10 { + match is_true { + true => { + x = 1; + } + _ => { + continue; + } + } + x = 3; + } + for _ in 0..10 { + if is_true { + continue; + } + x = 3; + } + for _ in 0..10 { + match is_true { + false => { + x = 1; + } + _ => { + let _ = x; + } + } + x = 3; + } + for _ in 0..10 { + match is_true { + false => { + x = 1; + } + _ => { + break; + } + } + x = 3; + } + let _ = x; +} diff --git a/src/test/run-make-fulldeps/coverage/coverage_tools.mk b/src/test/run-make-fulldeps/coverage/coverage_tools.mk index 38643aaf9021e..aa1dc7b91ce94 100644 --- a/src/test/run-make-fulldeps/coverage/coverage_tools.mk +++ b/src/test/run-make-fulldeps/coverage/coverage_tools.mk @@ -4,11 +4,3 @@ # -include ../coverage/coverage_tools.mk -include ../tools.mk - -# ISSUE(76038): When targeting MSVC, Rust binaries built with both `-Z instrument-coverage` and -# `-C link-dead-code` typically crash (with a seg-fault) or at best generate an empty `*.profraw` -# file, required for coverage reports. -# -# Enabling `-C link-dead-code` is not necessary when compiling with `-Z instrument-coverage`, -# due to improvements in the coverage map generation, to add unreachable functions known to Rust. -# Therefore, `-C link-dead-code` is no longer automatically enabled. diff --git a/src/test/run-make-fulldeps/coverage/inline.rs b/src/test/run-make-fulldeps/coverage/inline.rs new file mode 100644 index 0000000000000..9cfab9ddbadf2 --- /dev/null +++ b/src/test/run-make-fulldeps/coverage/inline.rs @@ -0,0 +1,51 @@ +// compile-flags: -Zinline-mir + +use std::fmt::Display; + +fn main() { + permutations(&['a', 'b', 'c']); +} + +#[inline(always)] +fn permutations(xs: &[T]) { + let mut ys = xs.to_owned(); + permutate(&mut ys, 0); +} + +fn permutate(xs: &mut [T], k: usize) { + let n = length(xs); + if k == n { + display(xs); + } else if k < n { + for i in k..n { + swap(xs, i, k); + permutate(xs, k + 1); + swap(xs, i, k); + } + } else { + error(); + } +} + +fn length(xs: &[T]) -> usize { + xs.len() +} + +#[inline] +fn swap(xs: &mut [T], i: usize, j: usize) { + let t = xs[i]; + xs[i] = xs[j]; + xs[j] = t; +} + +fn display(xs: &[T]) { + for x in xs { + print!("{}", x); + } + println!(); +} + +#[inline(always)] +fn error() { + panic!("error"); +} diff --git a/src/test/run-make-fulldeps/coverage/lib/used_crate.rs b/src/test/run-make-fulldeps/coverage/lib/used_crate.rs index e5555f9193576..eaa93115ae8d2 100644 --- a/src/test/run-make-fulldeps/coverage/lib/used_crate.rs +++ b/src/test/run-make-fulldeps/coverage/lib/used_crate.rs @@ -1,5 +1,5 @@ #![allow(unused_assignments, unused_variables)] - +// compile-flags: -C opt-level=3 # validates coverage now works with optimizations use std::fmt::Debug; pub fn used_function() { @@ -17,7 +17,7 @@ pub fn used_function() { pub fn used_only_from_bin_crate_generic_function(arg: T) { println!("used_only_from_bin_crate_generic_function with {:?}", arg); } - +// Expect for above function: `Unexecuted instantiation` (see below) pub fn used_only_from_this_lib_crate_generic_function(arg: T) { println!("used_only_from_this_lib_crate_generic_function with {:?}", arg); } @@ -60,45 +60,41 @@ fn use_this_lib_crate() { used_only_from_this_lib_crate_generic_function("used ONLY from library used_crate.rs"); } -// FIXME(#79651): `used_from_bin_crate_and_lib_crate_generic_function()` is covered and executed -// `2` times, but the coverage output also shows (at the bottom of the coverage report): -// ------------------ -// | Unexecuted instantiation: -// ------------------ +// FIXME(#79651): "Unexecuted instantiation" errors appear in coverage results, +// for example: // -// Note, the function name shown in the error seems to change depending on the structure of the -// code, for some reason, including: +// | Unexecuted instantiation: used_crate::used_only_from_bin_crate_generic_function::<_> // -// * used_crate::used_from_bin_crate_and_lib_crate_generic_function::<&str> -// * used_crate::use_this_lib_crate +// These notices appear when `llvm-cov` shows instantiations. This may be a +// default option, but it can be suppressed with: // -// The `Unexecuted instantiation` error may be related to more than one generic function. Since the -// reporting is not consistent, it may not be obvious if there are multiple problems here; however, -// `used_crate::used_from_bin_crate_and_lib_crate_generic_function::<&str>` (which I have seen -// with this error) is the only generic function missing instantiaion coverage counts. +// ```shell +// $ `llvm-cov show --show-instantiations=0 ...` +// ``` // -// The `&str` variant was called from within this `lib` crate, and the `bin` crate also calls this -// function, but with `T` type `&Vec`. +// The notice is triggered because the function is unused by the library itself, +// and when the library is compiled, a synthetic function is generated, so +// unused function coverage can be reported. Coverage can be skipped for unused +// generic functions with: // -// I believe the reason is that one or both crates are generating `Zero` counters for what it -// believes are "Unreachable" instantiations, but those instantiations are counted from the -// coverage map in the other crate. +// ```shell +// $ `rustc -Z instrument-coverage=except-unused-generics ...` +// ``` // -// See `add_unreachable_coverage()` in `mapgen.rs` for more on how these `Zero` counters are added -// for what the funciton believes are `DefId`s that did not get codegenned. I suspect the issue -// may be related to this process, but this needs to be confirmed. It may not be possible to know -// for sure if a function is truly unused and should be reported with `Zero` coverage if it may -// still get used from an external crate. (Something to look at: If the `DefId` in MIR corresponds -// _only_ to the generic function without type parameters, is the `DefId` in the codegenned set, -// instantiated with one of the type parameters (in either or both crates) a *different* `DefId`? -// If so, `add_unreachable_coverage()` would assume the MIR `DefId` was uncovered, and would add -// unreachable coverage. +// Even though this function is used by `uses_crate.rs` (and +// counted), with substitutions for `T`, those instantiations are only generated +// when the generic function is actually used (from the binary, not from this +// library crate). So the test result shows coverage for all instantiated +// versions and their generic type substitutions, plus the `Unexecuted +// instantiation` message for the non-substituted version. This is valid, but +// unfortunately a little confusing. // -// I didn't think they could be different, but if they can, we would need to find the `DefId` for -// the generic function MIR and include it in the set of "codegenned" DefIds if any instantiation -// of that generic function does exist. +// The library crate has its own coverage map, and the only way to show unused +// coverage of a generic function is to include the generic function in the +// coverage map, marked as an "unused function". If the library were used by +// another binary that never used this generic function, then it would be valid +// to show the unused generic, with unknown substitution (`_`). // -// Note, however, for `used_with_same_type_from_bin_crate_and_lib_crate_generic_function()` both -// crates use this function with the same type variant. The function does not have multiple -// instantiations, so the coverage analysis is not confused. No "Unexecuted instantiations" errors -// are reported. +// The alternative is to exclude all generics from being included in the "unused +// functions" list, which would then omit coverage results for +// `unused_generic_function()`, below. diff --git a/src/test/run-make-fulldeps/coverage/lib/used_inline_crate.rs b/src/test/run-make-fulldeps/coverage/lib/used_inline_crate.rs new file mode 100644 index 0000000000000..4a052756d4e27 --- /dev/null +++ b/src/test/run-make-fulldeps/coverage/lib/used_inline_crate.rs @@ -0,0 +1,90 @@ +#![allow(unused_assignments, unused_variables)] + +// compile-flags: -C opt-level=3 # validates coverage now works with optimizations + +use std::fmt::Debug; + +pub fn used_function() { + // Initialize test constants in a way that cannot be determined at compile time, to ensure + // rustc and LLVM cannot optimize out statements (or coverage counters) downstream from + // dependent conditions. + let is_true = std::env::args().len() == 1; + let mut countdown = 0; + if is_true { + countdown = 10; + } + use_this_lib_crate(); +} + +#[inline(always)] +pub fn used_inline_function() { + // Initialize test constants in a way that cannot be determined at compile time, to ensure + // rustc and LLVM cannot optimize out statements (or coverage counters) downstream from + // dependent conditions. + let is_true = std::env::args().len() == 1; + let mut countdown = 0; + if is_true { + countdown = 10; + } + use_this_lib_crate(); +} + + + + + + + +#[inline(always)] +pub fn used_only_from_bin_crate_generic_function(arg: T) { + println!("used_only_from_bin_crate_generic_function with {:?}", arg); +} +// Expect for above function: `Unexecuted instantiation` (see notes in `used_crate.rs`) + +#[inline(always)] +pub fn used_only_from_this_lib_crate_generic_function(arg: T) { + println!("used_only_from_this_lib_crate_generic_function with {:?}", arg); +} + +#[inline(always)] +pub fn used_from_bin_crate_and_lib_crate_generic_function(arg: T) { + println!("used_from_bin_crate_and_lib_crate_generic_function with {:?}", arg); +} + +#[inline(always)] +pub fn used_with_same_type_from_bin_crate_and_lib_crate_generic_function(arg: T) { + println!("used_with_same_type_from_bin_crate_and_lib_crate_generic_function with {:?}", arg); +} + +#[inline(always)] +pub fn unused_generic_function(arg: T) { + println!("unused_generic_function with {:?}", arg); +} + +#[inline(always)] +pub fn unused_function() { + let is_true = std::env::args().len() == 1; + let mut countdown = 2; + if !is_true { + countdown = 20; + } +} + +#[inline(always)] +fn unused_private_function() { + let is_true = std::env::args().len() == 1; + let mut countdown = 2; + if !is_true { + countdown = 20; + } +} + +fn use_this_lib_crate() { + used_from_bin_crate_and_lib_crate_generic_function("used from library used_crate.rs"); + used_with_same_type_from_bin_crate_and_lib_crate_generic_function( + "used from library used_crate.rs", + ); + let some_vec = vec![5, 6, 7, 8]; + used_only_from_this_lib_crate_generic_function(some_vec); + used_only_from_this_lib_crate_generic_function("used ONLY from library used_crate.rs"); +} diff --git a/src/test/run-make-fulldeps/coverage/loops_branches.rs b/src/test/run-make-fulldeps/coverage/loops_branches.rs index 938421d32e7a5..4d9bbad3367f6 100644 --- a/src/test/run-make-fulldeps/coverage/loops_branches.rs +++ b/src/test/run-make-fulldeps/coverage/loops_branches.rs @@ -1,7 +1,7 @@ #![allow(unused_assignments, unused_variables, while_true)] -// This test confirms an earlier problem was resolved, supporting the MIR graph generated by the -// structure of this `fmt` function. +// This test confirms that (1) unexecuted infinite loops are handled correctly by the +// InstrumentCoverage MIR pass; and (2) Counter Expressions that subtract from zero can be dropped. struct DebugTest; @@ -15,6 +15,40 @@ impl std::fmt::Debug for DebugTest { write!(f, "error")?; } else { } + + for i in 0..10 { + if true { + if false { + while true {} + } + write!(f, "error")?; + } else { + } + } + Ok(()) + } +} + +struct DisplayTest; + +impl std::fmt::Display for DisplayTest { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + if false { + } else { + if false { + while true {} + } + write!(f, "error")?; + } + for i in 0..10 { + if false { + } else { + if false { + while true {} + } + write!(f, "error")?; + } + } Ok(()) } } @@ -22,15 +56,6 @@ impl std::fmt::Debug for DebugTest { fn main() { let debug_test = DebugTest; println!("{:?}", debug_test); + let display_test = DisplayTest; + println!("{}", display_test); } - -/* - -This is the error message generated, before the issue was fixed: - -error: internal compiler error: compiler/rustc_mir/src/transform/coverage/mod.rs:374:42: -Error processing: DefId(0:6 ~ bug_incomplete_cov_graph_traversal_simplified[317d]::{impl#0}::fmt): -Error { message: "`TraverseCoverageGraphWithLoops` missed some `BasicCoverageBlock`s: -[bcb6, bcb7, bcb9]" } - -*/ diff --git a/src/test/run-make-fulldeps/coverage/unused.rs b/src/test/run-make-fulldeps/coverage/unused.rs new file mode 100644 index 0000000000000..fb6113eb01c2d --- /dev/null +++ b/src/test/run-make-fulldeps/coverage/unused.rs @@ -0,0 +1,39 @@ +fn foo(x: T) { + let mut i = 0; + while i < 10 { + i != 0 || i != 0; + i += 1; + } +} + +fn unused_template_func(x: T) { + let mut i = 0; + while i < 10 { + i != 0 || i != 0; + i += 1; + } +} + +fn unused_func(mut a: u32) { + if a != 0 { + a += 1; + } +} + +fn unused_func2(mut a: u32) { + if a != 0 { + a += 1; + } +} + +fn unused_func3(mut a: u32) { + if a != 0 { + a += 1; + } +} + +fn main() -> Result<(), u8> { + foo::(0); + foo::(0.0); + Ok(()) +} diff --git a/src/test/run-make-fulldeps/coverage/uses_crate.rs b/src/test/run-make-fulldeps/coverage/uses_crate.rs index 8d24b1ca3e67b..20cb05fe5b0ad 100644 --- a/src/test/run-make-fulldeps/coverage/uses_crate.rs +++ b/src/test/run-make-fulldeps/coverage/uses_crate.rs @@ -1,5 +1,5 @@ #![allow(unused_assignments, unused_variables)] - +// compile-flags: -C opt-level=3 # validates coverage now works with optimizations extern crate used_crate; fn main() { diff --git a/src/test/run-make-fulldeps/coverage/uses_inline_crate.rs b/src/test/run-make-fulldeps/coverage/uses_inline_crate.rs new file mode 100644 index 0000000000000..a7fe8532be31b --- /dev/null +++ b/src/test/run-make-fulldeps/coverage/uses_inline_crate.rs @@ -0,0 +1,17 @@ +#![allow(unused_assignments, unused_variables)] + +// compile-flags: -C opt-level=3 # validates coverage now works with optimizations + +extern crate used_inline_crate; + +fn main() { + used_inline_crate::used_function(); + used_inline_crate::used_inline_function(); + let some_vec = vec![1, 2, 3, 4]; + used_inline_crate::used_only_from_bin_crate_generic_function(&some_vec); + used_inline_crate::used_only_from_bin_crate_generic_function("used from bin uses_crate.rs"); + used_inline_crate::used_from_bin_crate_and_lib_crate_generic_function(some_vec); + used_inline_crate::used_with_same_type_from_bin_crate_and_lib_crate_generic_function( + "interesting?", + ); +} diff --git a/src/test/run-make-fulldeps/issue-19371/foo.rs b/src/test/run-make-fulldeps/issue-19371/foo.rs index fdd7e8b24c521..4acabbb70ede2 100644 --- a/src/test/run-make-fulldeps/issue-19371/foo.rs +++ b/src/test/run-make-fulldeps/issue-19371/foo.rs @@ -57,6 +57,7 @@ fn compile(code: String, output: PathBuf, sysroot: PathBuf) { diagnostic_output: DiagnosticOutput::Default, stderr: None, lint_caps: Default::default(), + parse_sess_created: None, register_lints: None, override_queries: None, make_codegen_backend: None, diff --git a/src/test/run-make-fulldeps/issue-83045/Makefile b/src/test/run-make-fulldeps/issue-83045/Makefile new file mode 100644 index 0000000000000..34853cb1d31e5 --- /dev/null +++ b/src/test/run-make-fulldeps/issue-83045/Makefile @@ -0,0 +1,33 @@ +include ../../run-make-fulldeps/tools.mk + +# This test case creates a situation where the crate loader would run +# into an ICE when confronted with an invalid setup where it cannot +# find the dependency of a direct dependency. +# +# The test case makes sure that the compiler produces the expected +# error message but does not ICE immediately after. +# +# See https://github.com/rust-lang/rust/issues/83045 + +# This is a platform-independent issue, no need to waste time testing +# everywhere. +# only-x86_64 +# only-linux + +# NOTE: We use BARE_RUSTC below so that the compiler can't find liba.rlib +# If we used RUSTC the additional '-L TMPDIR' option would allow rustc to +# actually find the crate. +# +# We check that we get the expected error message +# But that we do not get an ICE + +all: + $(RUSTC) --crate-name=a --crate-type=rlib a.rs --verbose + $(RUSTC) --crate-name=b --crate-type=rlib --extern a=$(TMPDIR)/liba.rlib b.rs --verbose + $(BARE_RUSTC) --out-dir $(TMPDIR) \ + --extern b=$(TMPDIR)/libb.rlib \ + --crate-type=rlib \ + --edition=2018 \ + c.rs 2>&1 | tee $(TMPDIR)/output.txt || exit 0 + $(CGREP) E0463 < $(TMPDIR)/output.txt + $(CGREP) -v "internal compiler error" < $(TMPDIR)/output.txt diff --git a/src/test/run-make-fulldeps/issue-83045/a.rs b/src/test/run-make-fulldeps/issue-83045/a.rs new file mode 100644 index 0000000000000..66d9f758e9dde --- /dev/null +++ b/src/test/run-make-fulldeps/issue-83045/a.rs @@ -0,0 +1 @@ +// empty on purpose diff --git a/src/test/run-make-fulldeps/issue-83045/b.rs b/src/test/run-make-fulldeps/issue-83045/b.rs new file mode 100644 index 0000000000000..f4876cfa45736 --- /dev/null +++ b/src/test/run-make-fulldeps/issue-83045/b.rs @@ -0,0 +1 @@ +extern crate a; diff --git a/src/test/run-make-fulldeps/issue-83045/c.rs b/src/test/run-make-fulldeps/issue-83045/c.rs new file mode 100644 index 0000000000000..e0c4525499ed7 --- /dev/null +++ b/src/test/run-make-fulldeps/issue-83045/c.rs @@ -0,0 +1 @@ +use b as _; diff --git a/src/test/run-make-fulldeps/link-args-order/Makefile b/src/test/run-make-fulldeps/link-args-order/Makefile index 98c1e0eac3b0e..f94e882ccb68d 100644 --- a/src/test/run-make-fulldeps/link-args-order/Makefile +++ b/src/test/run-make-fulldeps/link-args-order/Makefile @@ -6,5 +6,5 @@ RUSTC_FLAGS = -C linker-flavor=ld -C link-arg=a -C link-args="b c" -C link-args= RUSTC_FLAGS_PRE = -C linker-flavor=ld -Z pre-link-arg=a -Z pre-link-args="b c" -Z pre-link-args="d e" -Z pre-link-arg=f all: - $(RUSTC) $(RUSTC_FLAGS) empty.rs 2>&1 | $(CGREP) '"a" "b" "c" "d" "e" "f" "g"' + $(RUSTC) $(RUSTC_FLAGS) empty.rs 2>&1 | $(CGREP) '"a" "b" "c" "d" "e" "f"' $(RUSTC) $(RUSTC_FLAGS_PRE) empty.rs 2>&1 | $(CGREP) '"a" "b" "c" "d" "e" "f"' diff --git a/src/test/run-make-fulldeps/link-args-order/empty.rs b/src/test/run-make-fulldeps/link-args-order/empty.rs index 2439171004b5f..f328e4d9d04c3 100644 --- a/src/test/run-make-fulldeps/link-args-order/empty.rs +++ b/src/test/run-make-fulldeps/link-args-order/empty.rs @@ -1,6 +1 @@ -#![feature(link_args)] - -#[link_args = "g"] -extern "C" {} - fn main() {} diff --git a/src/test/run-make-fulldeps/print-unversioned-files/Makefile b/src/test/run-make-fulldeps/print-unversioned-files/Makefile new file mode 100644 index 0000000000000..e368f61cddfc3 --- /dev/null +++ b/src/test/run-make-fulldeps/print-unversioned-files/Makefile @@ -0,0 +1,4 @@ +-include ../tools.mk + +all: + $(RUSTDOC) -Z unstable-options --print unversioned-files | sort | diff - unversioned-files.txt diff --git a/src/test/run-make-fulldeps/print-unversioned-files/unversioned-files.txt b/src/test/run-make-fulldeps/print-unversioned-files/unversioned-files.txt new file mode 100644 index 0000000000000..4b20cd5d7453e --- /dev/null +++ b/src/test/run-make-fulldeps/print-unversioned-files/unversioned-files.txt @@ -0,0 +1,16 @@ +COPYRIGHT.txt +FiraSans-LICENSE.txt +FiraSans-Medium.woff +FiraSans-Medium.woff2 +FiraSans-Regular.woff +FiraSans-Regular.woff2 +LICENSE-APACHE.txt +LICENSE-MIT.txt +SourceCodePro-It.ttf.woff +SourceCodePro-LICENSE.txt +SourceCodePro-Regular.ttf.woff +SourceCodePro-Semibold.ttf.woff +SourceSerif4-Bold.ttf.woff +SourceSerif4-It.ttf.woff +SourceSerif4-LICENSE.md +SourceSerif4-Regular.ttf.woff diff --git a/src/test/run-make/emit-shared-files/Makefile b/src/test/run-make/emit-shared-files/Makefile new file mode 100644 index 0000000000000..d89b526d4303f --- /dev/null +++ b/src/test/run-make/emit-shared-files/Makefile @@ -0,0 +1,46 @@ +-include ../../run-make-fulldeps/tools.mk + +INVOCATION_ONLY = $(TMPDIR)/invocation-only +TOOLCHAIN_ONLY = $(TMPDIR)/toolchain-only +ALL_SHARED = $(TMPDIR)/all-shared + +all: invocation-only toolchain-only all-shared + +invocation-only: + $(RUSTDOC) -Z unstable-options --emit=invocation-specific --output $(INVOCATION_ONLY) --resource-suffix=-xxx --theme y.css --extend-css z.css x.rs + [ -e $(INVOCATION_ONLY)/search-index-xxx.js ] + [ -e $(INVOCATION_ONLY)/settings.html ] + [ -e $(INVOCATION_ONLY)/x/all.html ] + [ -e $(INVOCATION_ONLY)/x/index.html ] + [ -e $(INVOCATION_ONLY)/theme-xxx.css ] # generated from z.css + ! [ -e $(INVOCATION_ONLY)/storage-xxx.js ] + ! [ -e $(INVOCATION_ONLY)/SourceSerif4-It.ttf.woff ] + + # FIXME: this probably shouldn't have a suffix + [ -e $(INVOCATION_ONLY)/y-xxx.css ] + # FIXME: this is technically incorrect (see `write_shared`) + ! [ -e $(INVOCATION_ONLY)/main-xxx.js ] + +toolchain-only: + $(RUSTDOC) -Z unstable-options --emit=toolchain-shared-resources --output $(TOOLCHAIN_ONLY) --resource-suffix=-xxx --extend-css z.css x.rs + [ -e $(TOOLCHAIN_ONLY)/storage-xxx.js ] + ! [ -e $(TOOLCHAIN_ONLY)/SourceSerif4-It.ttf.woff ] + ! [ -e $(TOOLCHAIN_ONLY)/search-index-xxx.js ] + ! [ -e $(TOOLCHAIN_ONLY)/x/index.html ] + ! [ -e $(TOOLCHAIN_ONLY)/theme.css ] + + [ -e $(TOOLCHAIN_ONLY)/main-xxx.js ] + ! [ -e $(TOOLCHAIN_ONLY)/y-xxx.css ] + +all-shared: + $(RUSTDOC) -Z unstable-options --emit=toolchain-shared-resources,unversioned-shared-resources --output $(ALL_SHARED) --resource-suffix=-xxx --extend-css z.css x.rs + [ -e $(ALL_SHARED)/storage-xxx.js ] + [ -e $(ALL_SHARED)/SourceSerif4-It.ttf.woff ] + ! [ -e $(ALL_SHARED)/search-index-xxx.js ] + ! [ -e $(ALL_SHARED)/settings.html ] + ! [ -e $(ALL_SHARED)/x ] + ! [ -e $(ALL_SHARED)/src ] + ! [ -e $(ALL_SHARED)/theme.css ] + + [ -e $(ALL_SHARED)/main-xxx.js ] + ! [ -e $(ALL_SHARED)/y-xxx.css ] diff --git a/src/test/run-make/emit-shared-files/x.rs b/src/test/run-make/emit-shared-files/x.rs new file mode 100644 index 0000000000000..5df7576133a68 --- /dev/null +++ b/src/test/run-make/emit-shared-files/x.rs @@ -0,0 +1 @@ +// nothing to see here diff --git a/src/test/run-make/emit-shared-files/y.css b/src/test/run-make/emit-shared-files/y.css new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/src/test/run-make/emit-shared-files/z.css b/src/test/run-make/emit-shared-files/z.css new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/src/test/run-make/incr-prev-body-beyond-eof/Makefile b/src/test/run-make/incr-prev-body-beyond-eof/Makefile index 49a7ee5f900fd..24eea3acaeacd 100644 --- a/src/test/run-make/incr-prev-body-beyond-eof/Makefile +++ b/src/test/run-make/incr-prev-body-beyond-eof/Makefile @@ -1,7 +1,7 @@ -include ../../run-make-fulldeps/tools.mk +# ignore-none no-std is not supported +# ignore-nvptx64-nvidia-cuda FIXME: can't find crate for `std` -# FIXME https://github.com/rust-lang/rust/issues/78911 -# ignore-32bit wrong/no cross compiler and sometimes we pass wrong gcc args (-m64) +include ../../run-make-fulldeps/tools.mk # Tests that we don't ICE during incremental compilation after modifying a # function span such that its previous end line exceeds the number of lines @@ -14,6 +14,6 @@ all: mkdir $(SRC) mkdir $(INCR) cp a.rs $(SRC)/main.rs - $(RUSTC) -C incremental=$(INCR) $(SRC)/main.rs + $(RUSTC) -C incremental=$(INCR) $(SRC)/main.rs --target $(TARGET) cp b.rs $(SRC)/main.rs - $(RUSTC) -C incremental=$(INCR) $(SRC)/main.rs + $(RUSTC) -C incremental=$(INCR) $(SRC)/main.rs --target $(TARGET) diff --git a/src/test/run-make/issue-36710/Makefile b/src/test/run-make/issue-36710/Makefile index b0e8451ff5d01..b5270ad2ba9d4 100644 --- a/src/test/run-make/issue-36710/Makefile +++ b/src/test/run-make/issue-36710/Makefile @@ -1,13 +1,20 @@ -include ../../run-make-fulldeps/tools.mk +# ignore-riscv64 $(call RUN,foo) expects to run the target executable natively +# so it won't work with remote-test-server +# ignore-arm Another build using remote-test-server +# ignore-none no-std is not supported +# ignore-wasm32 FIXME: don't attempt to compile C++ to WASM +# ignore-wasm64 FIXME: don't attempt to compile C++ to WASM +# ignore-nvptx64-nvidia-cuda FIXME: can't find crate for `std` +# ignore-musl FIXME: this makefile needs teaching how to use a musl toolchain +# (see dist-i586-gnu-i586-i686-musl Dockerfile) -# FIXME https://github.com/rust-lang/rust/issues/78911 -# ignore-32bit wrong/no cross compiler and sometimes we pass wrong gcc args (-m64) +include ../../run-make-fulldeps/tools.mk all: foo $(call RUN,foo) foo: foo.rs $(call NATIVE_STATICLIB,foo) - $(RUSTC) $< -lfoo $(EXTRARSCXXFLAGS) + $(RUSTC) $< -lfoo $(EXTRARSCXXFLAGS) --target $(TARGET) $(TMPDIR)/libfoo.o: foo.cpp $(call COMPILE_OBJ_CXX,$@,$<) diff --git a/src/test/run-make/issue-83112-incr-test-moved-file/Makefile b/src/test/run-make/issue-83112-incr-test-moved-file/Makefile new file mode 100644 index 0000000000000..2f796e5b2fc07 --- /dev/null +++ b/src/test/run-make/issue-83112-incr-test-moved-file/Makefile @@ -0,0 +1,25 @@ +include ../../run-make-fulldeps/tools.mk + +# ignore-none no-std is not supported +# ignore-nvptx64-nvidia-cuda FIXME: can't find crate for 'std' + +# Regression test for issue #83112 +# The generated test harness code contains spans with a dummy location, +# but a non-dummy SyntaxContext. Previously, the incremental cache was encoding +# these spans as a full span (with a source file index), instead of skipping +# the encoding of the location information. If the file gest moved, the hash +# of the span will be unchanged (since it has a dummy location), so the incr +# cache would end up try to load a non-existent file using the previously +# enccoded source file id. + +SRC=$(TMPDIR)/src +INCR=$(TMPDIR)/incr + +all: + mkdir $(SRC) + mkdir $(SRC)/mydir + mkdir $(INCR) + cp main.rs $(SRC)/main.rs + $(RUSTC) --test -C incremental=$(INCR) $(SRC)/main.rs --target $(TARGET) + mv $(SRC)/main.rs $(SRC)/mydir/main.rs + $(RUSTC) --test -C incremental=$(INCR) $(SRC)/mydir/main.rs --target $(TARGET) diff --git a/src/test/run-make/issue-83112-incr-test-moved-file/main.rs b/src/test/run-make/issue-83112-incr-test-moved-file/main.rs new file mode 100644 index 0000000000000..f328e4d9d04c3 --- /dev/null +++ b/src/test/run-make/issue-83112-incr-test-moved-file/main.rs @@ -0,0 +1 @@ +fn main() {} diff --git a/src/test/run-make/unstable-flag-required/Makefile b/src/test/run-make/unstable-flag-required/Makefile new file mode 100644 index 0000000000000..b8769d5f69051 --- /dev/null +++ b/src/test/run-make/unstable-flag-required/Makefile @@ -0,0 +1,4 @@ +-include ../../run-make-fulldeps/tools.mk + +all: + $(RUSTDOC) --output-format=json x.html 2>&1 | diff - output-format-json.stderr diff --git a/src/test/run-make/unstable-flag-required/README.md b/src/test/run-make/unstable-flag-required/README.md new file mode 100644 index 0000000000000..e5251fdf9b5d9 --- /dev/null +++ b/src/test/run-make/unstable-flag-required/README.md @@ -0,0 +1,3 @@ +This is a collection of tests that verify `--unstable-options` is required. +It should eventually be removed in favor of UI tests once compiletest stops +passing --unstable-options by default (#82639). diff --git a/src/test/run-make/unstable-flag-required/output-format-json.stderr b/src/test/run-make/unstable-flag-required/output-format-json.stderr new file mode 100644 index 0000000000000..fb4079beb2799 --- /dev/null +++ b/src/test/run-make/unstable-flag-required/output-format-json.stderr @@ -0,0 +1,2 @@ +error: the -Z unstable-options flag must be passed to enable --output-format for documentation generation (see https://github.com/rust-lang/rust/issues/76578) + diff --git a/src/test/run-make/unstable-flag-required/x.rs b/src/test/run-make/unstable-flag-required/x.rs new file mode 100644 index 0000000000000..5df7576133a68 --- /dev/null +++ b/src/test/run-make/unstable-flag-required/x.rs @@ -0,0 +1 @@ +// nothing to see here diff --git a/src/test/run-make/wasm-abi/Makefile b/src/test/run-make/wasm-abi/Makefile new file mode 100644 index 0000000000000..61fc4e8f57fb8 --- /dev/null +++ b/src/test/run-make/wasm-abi/Makefile @@ -0,0 +1,7 @@ +-include ../../run-make-fulldeps/tools.mk + +# only-wasm32-bare + +all: + $(RUSTC) foo.rs --target wasm32-unknown-unknown + $(NODE) foo.js $(TMPDIR)/foo.wasm diff --git a/src/test/run-make/wasm-abi/foo.js b/src/test/run-make/wasm-abi/foo.js new file mode 100644 index 0000000000000..9e9a65401af81 --- /dev/null +++ b/src/test/run-make/wasm-abi/foo.js @@ -0,0 +1,22 @@ +const fs = require('fs'); +const process = require('process'); +const assert = require('assert'); +const buffer = fs.readFileSync(process.argv[2]); + +const m = new WebAssembly.Module(buffer); +const i = new WebAssembly.Instance(m, { + host: { + two_i32: () => [100, 101], + two_i64: () => [102n, 103n], + two_f32: () => [104, 105], + two_f64: () => [106, 107], + mishmash: () => [108, 109, 110, 111n, 112, 113], + } +}); + +assert.deepEqual(i.exports.return_two_i32(), [1, 2]) +assert.deepEqual(i.exports.return_two_i64(), [3, 4]) +assert.deepEqual(i.exports.return_two_f32(), [5, 6]) +assert.deepEqual(i.exports.return_two_f64(), [7, 8]) +assert.deepEqual(i.exports.return_mishmash(), [9, 10, 11, 12, 13, 14]) +i.exports.call_imports(); diff --git a/src/test/run-make/wasm-abi/foo.rs b/src/test/run-make/wasm-abi/foo.rs new file mode 100644 index 0000000000000..0678eb3ff51ae --- /dev/null +++ b/src/test/run-make/wasm-abi/foo.rs @@ -0,0 +1,87 @@ +#![crate_type = "cdylib"] +#![deny(warnings)] +#![feature(wasm_abi)] + +#[repr(C)] +#[derive(PartialEq, Debug)] +pub struct TwoI32 { + pub a: i32, + pub b: i32, +} + +#[no_mangle] +pub extern "wasm" fn return_two_i32() -> TwoI32 { + TwoI32 { a: 1, b: 2 } +} + +#[repr(C)] +#[derive(PartialEq, Debug)] +pub struct TwoI64 { + pub a: i64, + pub b: i64, +} + +#[no_mangle] +pub extern "wasm" fn return_two_i64() -> TwoI64 { + TwoI64 { a: 3, b: 4 } +} + +#[repr(C)] +#[derive(PartialEq, Debug)] +pub struct TwoF32 { + pub a: f32, + pub b: f32, +} + +#[no_mangle] +pub extern "wasm" fn return_two_f32() -> TwoF32 { + TwoF32 { a: 5., b: 6. } +} + +#[repr(C)] +#[derive(PartialEq, Debug)] +pub struct TwoF64 { + pub a: f64, + pub b: f64, +} + +#[no_mangle] +pub extern "wasm" fn return_two_f64() -> TwoF64 { + TwoF64 { a: 7., b: 8. } +} + +#[repr(C)] +#[derive(PartialEq, Debug)] +pub struct Mishmash { + pub a: f64, + pub b: f32, + pub c: i32, + pub d: i64, + pub e: TwoI32, +} + +#[no_mangle] +pub extern "wasm" fn return_mishmash() -> Mishmash { + Mishmash { a: 9., b: 10., c: 11, d: 12, e: TwoI32 { a: 13, b: 14 } } +} + +#[link(wasm_import_module = "host")] +extern "wasm" { + fn two_i32() -> TwoI32; + fn two_i64() -> TwoI64; + fn two_f32() -> TwoF32; + fn two_f64() -> TwoF64; + fn mishmash() -> Mishmash; +} + +#[no_mangle] +pub unsafe extern "C" fn call_imports() { + assert_eq!(two_i32(), TwoI32 { a: 100, b: 101 }); + assert_eq!(two_i64(), TwoI64 { a: 102, b: 103 }); + assert_eq!(two_f32(), TwoF32 { a: 104., b: 105. }); + assert_eq!(two_f64(), TwoF64 { a: 106., b: 107. }); + assert_eq!( + mishmash(), + Mishmash { a: 108., b: 109., c: 110, d: 111, e: TwoI32 { a: 112, b: 113 } } + ); +} diff --git a/src/test/rustdoc-gui/README.md b/src/test/rustdoc-gui/README.md new file mode 100644 index 0000000000000..499a98a3d2237 --- /dev/null +++ b/src/test/rustdoc-gui/README.md @@ -0,0 +1,12 @@ +The tests present here are used to test the generated HTML from rustdoc. The +goal is to prevent unsound/unexpected GUI changes. + +This is using the [browser-ui-test] framework to do so. It works as follows: + +It wraps [puppeteer] to send commands to a web browser in order to navigate and +test what's being currently displayed in the web page. + +You can find more information and its documentation in its [repository][browser-ui-test]. + +[browser-ui-test]: https://github.com/GuillaumeGomez/browser-UI-test/ +[puppeteer]: https://pptr.dev/ diff --git a/src/test/rustdoc-gui/check_info_sign_position.goml b/src/test/rustdoc-gui/check_info_sign_position.goml new file mode 100644 index 0000000000000..9aa72a3ad53e7 --- /dev/null +++ b/src/test/rustdoc-gui/check_info_sign_position.goml @@ -0,0 +1,9 @@ +goto: file://|DOC_PATH|/index.html +goto: ./fn.check_list_code_block.html +// If the codeblock is the first element of the docblock, the information tooltip must have +// have some top margin to avoid going over the toggle (the "[+]"). +assert: (".docblock > .information > .compile_fail", { "margin-top": "16px" }) +// Checks that the other codeblocks don't have this top margin. +assert: ("ol > li > .information > .compile_fail", { "margin-top": "0px" }) +assert: ("ol > li > .information > .ignore", { "margin-top": "0px" }) +assert: (".docblock > .information > .ignore", { "margin-top": "0px" }) diff --git a/src/test/rustdoc-gui/lib.rs b/src/test/rustdoc-gui/lib.rs index 3c996f5b65c92..eeba3e3f9070d 100644 --- a/src/test/rustdoc-gui/lib.rs +++ b/src/test/rustdoc-gui/lib.rs @@ -27,6 +27,13 @@ pub fn foo() {} /// Just a normal struct. pub struct Foo; +impl Foo { + #[must_use] + pub fn must_use(&self) -> bool { + true + } +} + /// Just a normal enum. pub enum WhoLetTheDogOut { /// Woof! @@ -42,26 +49,45 @@ pub fn some_more_function(t: &T) -> String { /// Woohoo! A trait! pub trait AnotherOne { + /// Some func 3. + fn func3(); + /// Some func 1. fn func1(); + fn another(); + fn why_not(); + /// Some func 2. fn func2(); - /// Some func 3. - fn func3(); + fn hello(); } +/// ```compile_fail +/// whatever +/// ``` +/// /// Check for "i" signs in lists! /// /// 1. elem 1 -/// 2.test 1 -/// ```compile_fail -/// fn foo() {} -/// ``` +/// 2. test 1 +/// ```compile_fail +/// fn foo() {} +/// ``` /// 3. elem 3 /// 4. ```ignore (it's a test) /// fn foo() {} /// ``` /// 5. elem 5 +/// +/// Final one: +/// +/// ```ignore (still a test) +/// let x = 12; +/// ``` pub fn check_list_code_block() {} + +pub enum AnEnum { + WithVariants { and: usize, sub: usize, variants: usize }, +} diff --git a/src/test/rustdoc-gui/nojs-attr-pos.goml b/src/test/rustdoc-gui/nojs-attr-pos.goml new file mode 100644 index 0000000000000..35daa4cb9e322 --- /dev/null +++ b/src/test/rustdoc-gui/nojs-attr-pos.goml @@ -0,0 +1,5 @@ +// Check that the attributes are well positioned when javascript is disabled (since +// there is no toggle to display) +javascript: false +goto: file://|DOC_PATH|/struct.Foo.html +assert: (".attributes", {"margin-left": "0px"}) diff --git a/src/test/rustdoc-gui/search-tab-selection-if-current-is-empty.goml b/src/test/rustdoc-gui/search-tab-selection-if-current-is-empty.goml new file mode 100644 index 0000000000000..a4df102d245be --- /dev/null +++ b/src/test/rustdoc-gui/search-tab-selection-if-current-is-empty.goml @@ -0,0 +1,21 @@ +goto: file://|DOC_PATH|/index.html +write: (".search-input", "Foo") +// Waiting for the search results to appear... +wait-for: "#titles" +assert: ("#titles > button:nth-of-type(1)", "class", "selected") + +// To go back to the original "state" +goto: file://|DOC_PATH|/index.html +write: (".search-input", "-> String") +// Waiting for the search results to appear... +wait-for: "#titles" +// With this search, only the last tab shouldn't be empty so it should be selected. +assert: ("#titles > button:nth-of-type(3)", "class", "selected") + +// To go back to the original "state" +goto: file://|DOC_PATH|/index.html +write: (".search-input", "-> Something") +// Waiting for the search results to appear... +wait-for: "#titles" +// With this search, all the tabs are empty so the first one should remain selected. +assert: ("#titles > button:nth-of-type(1)", "class", "selected") diff --git a/src/test/rustdoc-gui/trait-sidebar-item-order.goml b/src/test/rustdoc-gui/trait-sidebar-item-order.goml new file mode 100644 index 0000000000000..914486e1c281d --- /dev/null +++ b/src/test/rustdoc-gui/trait-sidebar-item-order.goml @@ -0,0 +1,7 @@ +goto: file://|DOC_PATH|/trait.AnotherOne.html +assert: (".sidebar-links a:nth-of-type(1)", "another") +assert: (".sidebar-links a:nth-of-type(2)", "func1") +assert: (".sidebar-links a:nth-of-type(3)", "func2") +assert: (".sidebar-links a:nth-of-type(4)", "func3") +assert: (".sidebar-links a:nth-of-type(5)", "hello") +assert: (".sidebar-links a:nth-of-type(6)", "why_not") diff --git a/src/test/rustdoc-json/nested.rs b/src/test/rustdoc-json/nested.rs index 5bb66f7f6fd7c..b0e717d8a86ba 100644 --- a/src/test/rustdoc-json/nested.rs +++ b/src/test/rustdoc-json/nested.rs @@ -24,7 +24,7 @@ pub mod l1 { // @has - "$.index[*][?(@.name=='l3')].inner.items[*]" $l4_id pub struct L4; } - // @is nested.json "$.index[*][?(@.inner.span=='l3::L4')].kind" \"import\" - // @is - "$.index[*][?(@.inner.span=='l3::L4')].inner.glob" false + // @is nested.json "$.index[*][?(@.inner.source=='l3::L4')].kind" \"import\" + // @is - "$.index[*][?(@.inner.source=='l3::L4')].inner.glob" false pub use l3::L4; } diff --git a/src/test/rustdoc-json/reexport/glob_extern.rs b/src/test/rustdoc-json/reexport/glob_extern.rs new file mode 100644 index 0000000000000..831c185f6b136 --- /dev/null +++ b/src/test/rustdoc-json/reexport/glob_extern.rs @@ -0,0 +1,17 @@ +// edition:2018 + +#![no_core] +#![feature(no_core)] + +// @!has glob_extern.json "$.index[*][?(@.name=='mod1')]" +mod mod1 { + extern "C" { + // @set public_fn_id = - "$.index[*][?(@.name=='public_fn')].id" + pub fn public_fn(); + // @!has - "$.index[*][?(@.name=='private_fn')]" + fn private_fn(); + } +} + +// @has - "$.index[*][?(@.name=='glob_extern')].inner.items[*]" $public_fn_id +pub use mod1::*; diff --git a/src/test/rustdoc-json/reexport/glob_private.rs b/src/test/rustdoc-json/reexport/glob_private.rs new file mode 100644 index 0000000000000..e907de9236776 --- /dev/null +++ b/src/test/rustdoc-json/reexport/glob_private.rs @@ -0,0 +1,27 @@ +// edition:2018 + +#![no_core] +#![feature(no_core)] + +// @!has glob_private.json "$.index[*][?(@.name=='mod1')]" +mod mod1 { + // @!has - "$.index[*][?(@.name=='mod2')]" + mod mod2 { + // @set m2pub_id = - "$.index[*][?(@.name=='Mod2Public')].id" + pub struct Mod2Public; + + // @!has - "$.index[*][?(@.name=='Mod2Private')]" + struct Mod2Private; + } + pub use self::mod2::*; + + // @set m1pub_id = - "$.index[*][?(@.name=='Mod1Public')].id" + pub struct Mod1Public; + + // @!has - "$.index[*][?(@.name=='Mod1Private')]" + struct Mod1Private; +} +pub use mod1::*; + +// @has - "$.index[*][?(@.name=='glob_private')].inner.items[*]" $m2pub_id +// @has - "$.index[*][?(@.name=='glob_private')].inner.items[*]" $m1pub_id diff --git a/src/test/rustdoc-json/reexport/in_root_and_mod.rs b/src/test/rustdoc-json/reexport/in_root_and_mod.rs new file mode 100644 index 0000000000000..e3cecbdd7ff2f --- /dev/null +++ b/src/test/rustdoc-json/reexport/in_root_and_mod.rs @@ -0,0 +1,15 @@ +#![feature(no_core)] +#![no_core] + +mod foo { + // @set foo_id = in_root_and_mod.json "$.index[*][?(@.name=='Foo')].id" + pub struct Foo; +} + +// @has - "$.index[*][?(@.name=='in_root_and_mod')].inner.items[*]" $foo_id +pub use foo::Foo; + +pub mod bar { + // @has - "$.index[*][?(@.name=='bar')].inner.items[*]" $foo_id + pub use crate::foo::Foo; +} diff --git a/src/test/rustdoc-json/reexport/in_root_and_mod_pub.rs b/src/test/rustdoc-json/reexport/in_root_and_mod_pub.rs new file mode 100644 index 0000000000000..2daadf7620ca0 --- /dev/null +++ b/src/test/rustdoc-json/reexport/in_root_and_mod_pub.rs @@ -0,0 +1,20 @@ +#![feature(no_core)] +#![no_core] + +pub mod foo { + // @set bar_id = in_root_and_mod_pub.json "$.index[*][?(@.name=='Bar')].id" + // @has - "$.index[*][?(@.name=='foo')].inner.items[*]" $bar_id + pub struct Bar; +} + +// @set root_import_id = - "$.index[*][?(@.inner.source=='foo::Bar')].id" +// @is - "$.index[*][?(@.inner.source=='foo::Bar')].inner.id" $bar_id +// @has - "$.index[*][?(@.name=='in_root_and_mod_pub')].inner.items[*]" $root_import_id +pub use foo::Bar; + +pub mod baz { + // @set baz_import_id = - "$.index[*][?(@.inner.source=='crate::foo::Bar')].id" + // @is - "$.index[*][?(@.inner.source=='crate::foo::Bar')].inner.id" $bar_id + // @has - "$.index[*][?(@.name=='baz')].inner.items[*]" $baz_import_id + pub use crate::foo::Bar; +} diff --git a/src/test/rustdoc-json/reexport/rename_private.rs b/src/test/rustdoc-json/reexport/rename_private.rs new file mode 100644 index 0000000000000..fb8296f23374a --- /dev/null +++ b/src/test/rustdoc-json/reexport/rename_private.rs @@ -0,0 +1,14 @@ +// edition:2018 + +#![no_core] +#![feature(no_core)] +// @!has rename_private.json "$.index[*][?(@.name=='inner')]" +mod inner { + // @!has - "$.index[*][?(@.name=='Public')]" + pub struct Public; +} + +// @set newname_id = - "$.index[*][?(@.name=='NewName')].id" +// @is - "$.index[*][?(@.name=='NewName')].kind" \"struct\" +// @has - "$.index[*][?(@.name=='rename_private')].inner.items[*]" $newname_id +pub use inner::Public as NewName; diff --git a/src/test/rustdoc-json/reexport/rename_public.rs b/src/test/rustdoc-json/reexport/rename_public.rs new file mode 100644 index 0000000000000..2dd438d223058 --- /dev/null +++ b/src/test/rustdoc-json/reexport/rename_public.rs @@ -0,0 +1,17 @@ +// edition:2018 + +#![no_core] +#![feature(no_core)] + +// @set inner_id = rename_public.json "$.index[*][?(@.name=='inner')].id" +// @has - "$.index[*][?(@.name=='rename_public')].inner.items[*]" $inner_id +pub mod inner { + // @set public_id = - "$.index[*][?(@.name=='Public')].id" + // @has - "$.index[*][?(@.name=='inner')].inner.items[*]" $public_id + pub struct Public; +} +// @set import_id = - "$.index[*][?(@.inner.name=='NewName')].id" +// @!has - "$.index[*][?(@.inner.name=='Public')]" +// @has - "$.index[*][?(@.name=='rename_public')].inner.items[*]" $import_id +// @is - "$.index[*][?(@.inner.name=='NewName')].inner.source" \"inner::Public\" +pub use inner::Public as NewName; diff --git a/src/test/rustdoc-json/reexport/simple_private.rs b/src/test/rustdoc-json/reexport/simple_private.rs new file mode 100644 index 0000000000000..658b121e6ce97 --- /dev/null +++ b/src/test/rustdoc-json/reexport/simple_private.rs @@ -0,0 +1,13 @@ +// edition:2018 + +#![no_core] +#![feature(no_core)] + +// @!has simple_private.json "$.index[*][?(@.name=='inner')]" +mod inner { + // @set pub_id = - "$.index[*][?(@.name=='Public')].id" + pub struct Public; +} + +// @has - "$.index[*][?(@.name=='simple_private')].inner.items[*]" $pub_id +pub use inner::Public; diff --git a/src/test/rustdoc-json/reexport/simple_public.rs b/src/test/rustdoc-json/reexport/simple_public.rs new file mode 100644 index 0000000000000..2e4de301f6f11 --- /dev/null +++ b/src/test/rustdoc-json/reexport/simple_public.rs @@ -0,0 +1,18 @@ +// edition:2018 + +#![no_core] +#![feature(no_core)] + +// @set inner_id = simple_public.json "$.index[*][?(@.name=='inner')].id" +// @has - "$.index[*][?(@.name=='simple_public')].inner.items[*]" $inner_id +pub mod inner { + + // @set public_id = - "$.index[*][?(@.name=='Public')].id" + // @has - "$.index[*][?(@.name=='inner')].inner.items[*]" $public_id + pub struct Public; +} + +// @set import_id = - "$.index[*][?(@.inner.name=='Public')].id" +// @has - "$.index[*][?(@.name=='simple_public')].inner.items[*]" $import_id +// @is - "$.index[*][?(@.inner.name=='Public')].inner.source" \"inner::Public\" +pub use inner::Public; diff --git a/src/test/rustdoc-ui/auxiliary/panic-item.rs b/src/test/rustdoc-ui/auxiliary/panic-item.rs new file mode 100644 index 0000000000000..17b26850d4d25 --- /dev/null +++ b/src/test/rustdoc-ui/auxiliary/panic-item.rs @@ -0,0 +1,17 @@ +// no-prefer-dynamic +#![crate_type = "lib"] +#![no_std] +#![feature(lang_items)] + +use core::panic::PanicInfo; +use core::sync::atomic::{self, Ordering}; + +#[panic_handler] +fn panic(_info: &PanicInfo) -> ! { + loop { + atomic::compiler_fence(Ordering::SeqCst); + } +} + +#[lang = "eh_personality"] +fn foo() {} diff --git a/src/test/rustdoc-ui/bare-urls.fixed b/src/test/rustdoc-ui/bare-urls.fixed new file mode 100644 index 0000000000000..23aa5c44c21fd --- /dev/null +++ b/src/test/rustdoc-ui/bare-urls.fixed @@ -0,0 +1,60 @@ +// run-rustfix + +#![deny(rustdoc::bare_urls)] + +/// +//~^ ERROR this URL is not a hyperlink +/// +//~^ ERROR this URL is not a hyperlink +/// +//~^ ERROR this URL is not a hyperlink +/// +//~^ ERROR this URL is not a hyperlink +/// +//~^ ERROR not a hyperlink +/// +//~^ ERROR this URL is not a hyperlink +/// +//~^ ERROR this URL is not a hyperlink +/// +//~^ ERROR this URL is not a hyperlink +/// +//~^ ERROR this URL is not a hyperlink +/// +//~^ ERROR this URL is not a hyperlink +/// +//~^ ERROR this URL is not a hyperlink +/// +//~^ ERROR this URL is not a hyperlink +/// +//~^ ERROR this URL is not a hyperlink +/// +//~^ ERROR this URL is not a hyperlink +/// +//~^ ERROR this URL is not a hyperlink +/// +//~^ ERROR this URL is not a hyperlink +/// hey! +//~^ ERROR this URL is not a hyperlink +pub fn c() {} + +/// +/// [a](http://a.com) +/// [b] +/// +/// [b]: http://b.com +/// +/// ``` +/// This link should not be linted: http://example.com +/// +/// Nor this one: or this one: [x](http://example.com) +/// ``` +/// +/// [should_not.lint](should_not.lint) +pub fn everything_is_fine_here() {} + +#[allow(rustdoc::bare_urls)] +pub mod foo { + /// https://somewhere.com/a?hello=12&bye=11#xyz + pub fn bar() {} +} diff --git a/src/test/rustdoc-ui/bare-urls.rs b/src/test/rustdoc-ui/bare-urls.rs new file mode 100644 index 0000000000000..592f57343bc92 --- /dev/null +++ b/src/test/rustdoc-ui/bare-urls.rs @@ -0,0 +1,60 @@ +// run-rustfix + +#![deny(rustdoc::bare_urls)] + +/// https://somewhere.com +//~^ ERROR this URL is not a hyperlink +/// https://somewhere.com/a +//~^ ERROR this URL is not a hyperlink +/// https://www.somewhere.com +//~^ ERROR this URL is not a hyperlink +/// https://www.somewhere.com/a +//~^ ERROR this URL is not a hyperlink +/// https://subdomain.example.com +//~^ ERROR not a hyperlink +/// https://somewhere.com? +//~^ ERROR this URL is not a hyperlink +/// https://somewhere.com/a? +//~^ ERROR this URL is not a hyperlink +/// https://somewhere.com?hello=12 +//~^ ERROR this URL is not a hyperlink +/// https://somewhere.com/a?hello=12 +//~^ ERROR this URL is not a hyperlink +/// https://example.com?hello=12#xyz +//~^ ERROR this URL is not a hyperlink +/// https://example.com/a?hello=12#xyz +//~^ ERROR this URL is not a hyperlink +/// https://example.com#xyz +//~^ ERROR this URL is not a hyperlink +/// https://example.com/a#xyz +//~^ ERROR this URL is not a hyperlink +/// https://somewhere.com?hello=12&bye=11 +//~^ ERROR this URL is not a hyperlink +/// https://somewhere.com/a?hello=12&bye=11 +//~^ ERROR this URL is not a hyperlink +/// https://somewhere.com?hello=12&bye=11#xyz +//~^ ERROR this URL is not a hyperlink +/// hey! https://somewhere.com/a?hello=12&bye=11#xyz +//~^ ERROR this URL is not a hyperlink +pub fn c() {} + +/// +/// [a](http://a.com) +/// [b] +/// +/// [b]: http://b.com +/// +/// ``` +/// This link should not be linted: http://example.com +/// +/// Nor this one: or this one: [x](http://example.com) +/// ``` +/// +/// [should_not.lint](should_not.lint) +pub fn everything_is_fine_here() {} + +#[allow(rustdoc::bare_urls)] +pub mod foo { + /// https://somewhere.com/a?hello=12&bye=11#xyz + pub fn bar() {} +} diff --git a/src/test/rustdoc-ui/bare-urls.stderr b/src/test/rustdoc-ui/bare-urls.stderr new file mode 100644 index 0000000000000..7097a8ddf3a79 --- /dev/null +++ b/src/test/rustdoc-ui/bare-urls.stderr @@ -0,0 +1,143 @@ +error: this URL is not a hyperlink + --> $DIR/bare-urls.rs:5:5 + | +LL | /// https://somewhere.com + | ^^^^^^^^^^^^^^^^^^^^^ help: use an automatic link instead: `` + | +note: the lint level is defined here + --> $DIR/bare-urls.rs:3:9 + | +LL | #![deny(rustdoc::bare_urls)] + | ^^^^^^^^^^^^^^^^^^ + = note: bare URLs are not automatically turned into clickable links + +error: this URL is not a hyperlink + --> $DIR/bare-urls.rs:7:5 + | +LL | /// https://somewhere.com/a + | ^^^^^^^^^^^^^^^^^^^^^^^ help: use an automatic link instead: `` + | + = note: bare URLs are not automatically turned into clickable links + +error: this URL is not a hyperlink + --> $DIR/bare-urls.rs:9:5 + | +LL | /// https://www.somewhere.com + | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: use an automatic link instead: `` + | + = note: bare URLs are not automatically turned into clickable links + +error: this URL is not a hyperlink + --> $DIR/bare-urls.rs:11:5 + | +LL | /// https://www.somewhere.com/a + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use an automatic link instead: `` + | + = note: bare URLs are not automatically turned into clickable links + +error: this URL is not a hyperlink + --> $DIR/bare-urls.rs:13:5 + | +LL | /// https://subdomain.example.com + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use an automatic link instead: `` + | + = note: bare URLs are not automatically turned into clickable links + +error: this URL is not a hyperlink + --> $DIR/bare-urls.rs:15:5 + | +LL | /// https://somewhere.com? + | ^^^^^^^^^^^^^^^^^^^^^^ help: use an automatic link instead: `` + | + = note: bare URLs are not automatically turned into clickable links + +error: this URL is not a hyperlink + --> $DIR/bare-urls.rs:17:5 + | +LL | /// https://somewhere.com/a? + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: use an automatic link instead: `` + | + = note: bare URLs are not automatically turned into clickable links + +error: this URL is not a hyperlink + --> $DIR/bare-urls.rs:19:5 + | +LL | /// https://somewhere.com?hello=12 + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use an automatic link instead: `` + | + = note: bare URLs are not automatically turned into clickable links + +error: this URL is not a hyperlink + --> $DIR/bare-urls.rs:21:5 + | +LL | /// https://somewhere.com/a?hello=12 + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use an automatic link instead: `` + | + = note: bare URLs are not automatically turned into clickable links + +error: this URL is not a hyperlink + --> $DIR/bare-urls.rs:23:5 + | +LL | /// https://example.com?hello=12#xyz + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use an automatic link instead: `` + | + = note: bare URLs are not automatically turned into clickable links + +error: this URL is not a hyperlink + --> $DIR/bare-urls.rs:25:5 + | +LL | /// https://example.com/a?hello=12#xyz + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use an automatic link instead: `` + | + = note: bare URLs are not automatically turned into clickable links + +error: this URL is not a hyperlink + --> $DIR/bare-urls.rs:27:5 + | +LL | /// https://example.com#xyz + | ^^^^^^^^^^^^^^^^^^^^^^^ help: use an automatic link instead: `` + | + = note: bare URLs are not automatically turned into clickable links + +error: this URL is not a hyperlink + --> $DIR/bare-urls.rs:29:5 + | +LL | /// https://example.com/a#xyz + | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: use an automatic link instead: `` + | + = note: bare URLs are not automatically turned into clickable links + +error: this URL is not a hyperlink + --> $DIR/bare-urls.rs:31:5 + | +LL | /// https://somewhere.com?hello=12&bye=11 + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use an automatic link instead: `` + | + = note: bare URLs are not automatically turned into clickable links + +error: this URL is not a hyperlink + --> $DIR/bare-urls.rs:33:5 + | +LL | /// https://somewhere.com/a?hello=12&bye=11 + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use an automatic link instead: `` + | + = note: bare URLs are not automatically turned into clickable links + +error: this URL is not a hyperlink + --> $DIR/bare-urls.rs:35:5 + | +LL | /// https://somewhere.com?hello=12&bye=11#xyz + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use an automatic link instead: `` + | + = note: bare URLs are not automatically turned into clickable links + +error: this URL is not a hyperlink + --> $DIR/bare-urls.rs:37:10 + | +LL | /// hey! https://somewhere.com/a?hello=12&bye=11#xyz + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use an automatic link instead: `` + | + = note: bare URLs are not automatically turned into clickable links + +error: aborting due to 17 previous errors + diff --git a/src/test/rustdoc-ui/check-doc-alias-attr.rs b/src/test/rustdoc-ui/check-doc-alias-attr.rs index 912e35f916545..719b98604c497 100644 --- a/src/test/rustdoc-ui/check-doc-alias-attr.rs +++ b/src/test/rustdoc-ui/check-doc-alias-attr.rs @@ -1,11 +1,11 @@ #![crate_type = "lib"] #[doc(alias = "foo")] // ok! +#[doc(alias("bar", "baz"))] // ok! pub struct Bar; #[doc(alias)] //~ ERROR #[doc(alias = 0)] //~ ERROR -#[doc(alias("bar"))] //~ ERROR #[doc(alias = "\"")] //~ ERROR #[doc(alias = "\n")] //~ ERROR #[doc(alias = " @@ -13,4 +13,16 @@ pub struct Bar; #[doc(alias = "\t")] //~ ERROR #[doc(alias = " hello")] //~ ERROR #[doc(alias = "hello ")] //~ ERROR +#[doc(alias = "")] //~ ERROR pub struct Foo; + +#[doc(alias(0))] //~ ERROR +#[doc(alias("\""))] //~ ERROR +#[doc(alias("\n"))] //~ ERROR +#[doc(alias(" +"))] //~^ ERROR +#[doc(alias("\t"))] //~ ERROR +#[doc(alias(" hello"))] //~ ERROR +#[doc(alias("hello "))] //~ ERROR +#[doc(alias(""))] //~ ERROR +pub struct Foo2; diff --git a/src/test/rustdoc-ui/check-doc-alias-attr.stderr b/src/test/rustdoc-ui/check-doc-alias-attr.stderr index 1c7fc83bb8dea..250568be3333f 100644 --- a/src/test/rustdoc-ui/check-doc-alias-attr.stderr +++ b/src/test/rustdoc-ui/check-doc-alias-attr.stderr @@ -1,22 +1,16 @@ -error: doc alias attribute expects a string: #[doc(alias = "a")] - --> $DIR/check-doc-alias-attr.rs:6:7 +error: doc alias attribute expects a string `#[doc(alias = "a")]` or a list of strings `#[doc(alias("a", "b"))]` + --> $DIR/check-doc-alias-attr.rs:7:7 | LL | #[doc(alias)] | ^^^^^ -error: doc alias attribute expects a string: #[doc(alias = "a")] - --> $DIR/check-doc-alias-attr.rs:7:7 +error: doc alias attribute expects a string `#[doc(alias = "a")]` or a list of strings `#[doc(alias("a", "b"))]` + --> $DIR/check-doc-alias-attr.rs:8:7 | LL | #[doc(alias = 0)] | ^^^^^^^^^ -error: doc alias attribute expects a string: #[doc(alias = "a")] - --> $DIR/check-doc-alias-attr.rs:8:7 - | -LL | #[doc(alias("bar"))] - | ^^^^^^^^^^^^ - -error: '\"' character isn't allowed in `#[doc(alias = "...")]` +error: '"' character isn't allowed in `#[doc(alias = "...")]` --> $DIR/check-doc-alias-attr.rs:9:15 | LL | #[doc(alias = "\"")] @@ -54,5 +48,61 @@ error: `#[doc(alias = "...")]` cannot start or end with ' ' LL | #[doc(alias = "hello ")] | ^^^^^^^^ -error: aborting due to 9 previous errors +error: `#[doc(alias = "...")]` attribute cannot have empty value + --> $DIR/check-doc-alias-attr.rs:16:15 + | +LL | #[doc(alias = "")] + | ^^ + +error: `#[doc(alias("a"))]` expects string literals + --> $DIR/check-doc-alias-attr.rs:19:13 + | +LL | #[doc(alias(0))] + | ^ + +error: '"' character isn't allowed in `#[doc(alias("..."))]` + --> $DIR/check-doc-alias-attr.rs:20:13 + | +LL | #[doc(alias("\""))] + | ^^^^ + +error: '\n' character isn't allowed in `#[doc(alias("..."))]` + --> $DIR/check-doc-alias-attr.rs:21:13 + | +LL | #[doc(alias("\n"))] + | ^^^^ + +error: '\n' character isn't allowed in `#[doc(alias("..."))]` + --> $DIR/check-doc-alias-attr.rs:22:13 + | +LL | #[doc(alias(" + | _____________^ +LL | | "))] + | |_^ + +error: '\t' character isn't allowed in `#[doc(alias("..."))]` + --> $DIR/check-doc-alias-attr.rs:24:13 + | +LL | #[doc(alias("\t"))] + | ^^^^ + +error: `#[doc(alias("..."))]` cannot start or end with ' ' + --> $DIR/check-doc-alias-attr.rs:25:13 + | +LL | #[doc(alias(" hello"))] + | ^^^^^^^^ + +error: `#[doc(alias("..."))]` cannot start or end with ' ' + --> $DIR/check-doc-alias-attr.rs:26:13 + | +LL | #[doc(alias("hello "))] + | ^^^^^^^^ + +error: `#[doc(alias("..."))]` attribute cannot have empty value + --> $DIR/check-doc-alias-attr.rs:27:13 + | +LL | #[doc(alias(""))] + | ^^ + +error: aborting due to 17 previous errors diff --git a/src/test/rustdoc-ui/check-fail.rs b/src/test/rustdoc-ui/check-fail.rs index 291fc112c3407..2355d6a3d6cbc 100644 --- a/src/test/rustdoc-ui/check-fail.rs +++ b/src/test/rustdoc-ui/check-fail.rs @@ -1,7 +1,7 @@ // compile-flags: -Z unstable-options --check #![deny(missing_docs)] -#![deny(rustdoc)] +#![deny(rustdoc::all)] //! ```rust,testharness //~^ ERROR diff --git a/src/test/rustdoc-ui/check-fail.stderr b/src/test/rustdoc-ui/check-fail.stderr index 9f5ccbc6687c2..2758c5490a3d8 100644 --- a/src/test/rustdoc-ui/check-fail.stderr +++ b/src/test/rustdoc-ui/check-fail.stderr @@ -19,9 +19,9 @@ LL | pub fn foo() {} note: the lint level is defined here --> $DIR/check-fail.rs:4:9 | -LL | #![deny(rustdoc)] - | ^^^^^^^ - = note: `#[deny(rustdoc::missing_doc_code_examples)]` implied by `#[deny(rustdoc)]` +LL | #![deny(rustdoc::all)] + | ^^^^^^^^^^^^ + = note: `#[deny(rustdoc::missing_doc_code_examples)]` implied by `#[deny(rustdoc::all)]` error: unknown attribute `testharness`. Did you mean `test_harness`? --> $DIR/check-fail.rs:6:1 @@ -35,9 +35,9 @@ LL | | //! ``` note: the lint level is defined here --> $DIR/check-fail.rs:4:9 | -LL | #![deny(rustdoc)] - | ^^^^^^^ - = note: `#[deny(rustdoc::invalid_codeblock_attributes)]` implied by `#[deny(rustdoc)]` +LL | #![deny(rustdoc::all)] + | ^^^^^^^^^^^^ + = note: `#[deny(rustdoc::invalid_codeblock_attributes)]` implied by `#[deny(rustdoc::all)]` = help: the code block will either not be tested if not marked as a rust one or the code will be wrapped inside a main function error: unknown attribute `testharness`. Did you mean `test_harness`? diff --git a/src/test/rustdoc-ui/check.rs b/src/test/rustdoc-ui/check.rs index 022c56214d451..65a56e03d9dfc 100644 --- a/src/test/rustdoc-ui/check.rs +++ b/src/test/rustdoc-ui/check.rs @@ -4,7 +4,7 @@ #![warn(missing_docs)] //~^ WARN //~^^ WARN -#![warn(rustdoc)] +#![warn(rustdoc::all)] pub fn foo() {} //~^ WARN diff --git a/src/test/rustdoc-ui/check.stderr b/src/test/rustdoc-ui/check.stderr index e6ba9df9b0555..2e1fc1eca4d6e 100644 --- a/src/test/rustdoc-ui/check.stderr +++ b/src/test/rustdoc-ui/check.stderr @@ -4,7 +4,7 @@ warning: missing documentation for the crate LL | / #![warn(missing_docs)] LL | | LL | | -LL | | #![warn(rustdoc)] +LL | | #![warn(rustdoc::all)] LL | | LL | | pub fn foo() {} | |_______________^ @@ -26,9 +26,9 @@ warning: no documentation found for this crate's top-level module note: the lint level is defined here --> $DIR/check.rs:7:9 | -LL | #![warn(rustdoc)] - | ^^^^^^^ - = note: `#[warn(rustdoc::missing_crate_level_docs)]` implied by `#[warn(rustdoc)]` +LL | #![warn(rustdoc::all)] + | ^^^^^^^^^^^^ + = note: `#[warn(rustdoc::missing_crate_level_docs)]` implied by `#[warn(rustdoc::all)]` = help: The following guide may be of use: https://doc.rust-lang.org/nightly/rustdoc/how-to-write-documentation.html @@ -38,7 +38,7 @@ warning: missing code example in this documentation LL | / #![warn(missing_docs)] LL | | LL | | -LL | | #![warn(rustdoc)] +LL | | #![warn(rustdoc::all)] LL | | LL | | pub fn foo() {} | |_______________^ @@ -46,9 +46,9 @@ LL | | pub fn foo() {} note: the lint level is defined here --> $DIR/check.rs:7:9 | -LL | #![warn(rustdoc)] - | ^^^^^^^ - = note: `#[warn(rustdoc::missing_doc_code_examples)]` implied by `#[warn(rustdoc)]` +LL | #![warn(rustdoc::all)] + | ^^^^^^^^^^^^ + = note: `#[warn(rustdoc::missing_doc_code_examples)]` implied by `#[warn(rustdoc::all)]` warning: missing code example in this documentation --> $DIR/check.rs:9:1 diff --git a/src/test/rustdoc-ui/commandline-argfile-missing.rs b/src/test/rustdoc-ui/commandline-argfile-missing.rs index 020c3ff3c7e63..5a6465bd06469 100644 --- a/src/test/rustdoc-ui/commandline-argfile-missing.rs +++ b/src/test/rustdoc-ui/commandline-argfile-missing.rs @@ -1,6 +1,5 @@ // Check to see if we can get parameters from an @argsfile file // -// ignore-tidy-linelength // normalize-stderr-test: "os error \d+" -> "os error $$ERR" // normalize-stderr-test: "commandline-argfile-missing.args:[^(]*" -> "commandline-argfile-missing.args: $$FILE_MISSING " // compile-flags: --cfg cmdline_set @{{src-base}}/commandline-argfile-missing.args diff --git a/src/test/rustdoc-ui/coverage/traits.rs b/src/test/rustdoc-ui/coverage/traits.rs index 7d5cf049e7fdd..2a108dac2a5db 100644 --- a/src/test/rustdoc-ui/coverage/traits.rs +++ b/src/test/rustdoc-ui/coverage/traits.rs @@ -2,6 +2,7 @@ // check-pass #![feature(trait_alias)] +#![feature(min_type_alias_impl_trait)] /// look at this trait right here pub trait ThisTrait { @@ -16,6 +17,7 @@ pub trait ThisTrait { } /// so what happens if we take some struct... +#[derive(Clone)] pub struct SomeStruct; /// ...and slap this trait on it? @@ -29,10 +31,8 @@ impl ThisTrait for SomeStruct { /// but what about those aliases? i hear they're pretty exotic pub trait MyAlias = ThisTrait + Send + Sync; -// FIXME(58624): once rustdoc can process opaque `impl Trait` types, -// we need to make sure they're counted -// /// woah, getting all opaque in here -// pub type ThisExists = impl ThisTrait; -// -// /// why don't we get a little more concrete -// pub fn defines() -> ThisExists { SomeStruct {} } +/// woah, getting all opaque in here +pub type ThisExists = impl ThisTrait; + +/// why don't we get a little more concrete +pub fn defines() -> ThisExists { SomeStruct {} } diff --git a/src/test/rustdoc-ui/coverage/traits.stdout b/src/test/rustdoc-ui/coverage/traits.stdout index e04d48b4980c4..5053d02090c44 100644 --- a/src/test/rustdoc-ui/coverage/traits.stdout +++ b/src/test/rustdoc-ui/coverage/traits.stdout @@ -1,7 +1,7 @@ +-------------------------------------+------------+------------+------------+------------+ | File | Documented | Percentage | Examples | Percentage | +-------------------------------------+------------+------------+------------+------------+ -| ...st/rustdoc-ui/coverage/traits.rs | 6 | 85.7% | 0 | 0.0% | +| ...st/rustdoc-ui/coverage/traits.rs | 8 | 88.9% | 0 | 0.0% | +-------------------------------------+------------+------------+------------+------------+ -| Total | 6 | 85.7% | 0 | 0.0% | +| Total | 8 | 88.9% | 0 | 0.0% | +-------------------------------------+------------+------------+------------+------------+ diff --git a/src/test/rustdoc-ui/deprecated-attrs.rs b/src/test/rustdoc-ui/deprecated-attrs.rs index ca626afbe5359..5febc5eb9cd54 100644 --- a/src/test/rustdoc-ui/deprecated-attrs.rs +++ b/src/test/rustdoc-ui/deprecated-attrs.rs @@ -1,7 +1,17 @@ // check-pass +// compile-flags: --passes unknown-pass +// error-pattern: ignoring unknown pass `unknown-pass` -#![doc(no_default_passes, passes = "unindent-comments")] - -struct SomeStruct; - -pub struct OtherStruct; +#![doc(no_default_passes)] +//~^ WARNING attribute is deprecated +//~| NOTE see issue #44136 +//~| HELP use `#![doc(document_private_items)]` +#![doc(passes = "collapse-docs unindent-comments")] +//~^ WARNING attribute is deprecated +//~| NOTE see issue #44136 +//~| WARNING ignoring unknown pass +//~| NOTE `collapse-docs` pass was removed +#![doc(plugins = "xxx")] +//~^ WARNING attribute is deprecated +//~| NOTE see issue #44136 +//~| WARNING no longer functions; see CVE diff --git a/src/test/rustdoc-ui/deprecated-attrs.stderr b/src/test/rustdoc-ui/deprecated-attrs.stderr index f68fb46744805..b855cedf5223e 100644 --- a/src/test/rustdoc-ui/deprecated-attrs.stderr +++ b/src/test/rustdoc-ui/deprecated-attrs.stderr @@ -1,11 +1,42 @@ -warning: the `#![doc(no_default_passes)]` attribute is considered deprecated +warning: the `passes` flag is deprecated | - = warning: see issue #44136 for more information + = note: see issue #44136 for more information + +warning: ignoring unknown pass `unknown-pass` + +warning: the `#![doc(no_default_passes)]` attribute is deprecated + --> $DIR/deprecated-attrs.rs:5:8 + | +LL | #![doc(no_default_passes)] + | ^^^^^^^^^^^^^^^^^ + | + = note: see issue #44136 for more information = help: you may want to use `#![doc(document_private_items)]` -warning: the `#![doc(passes = "...")]` attribute is considered deprecated +warning: the `#![doc(passes = "...")]` attribute is deprecated + --> $DIR/deprecated-attrs.rs:9:8 + | +LL | #![doc(passes = "collapse-docs unindent-comments")] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #44136 for more information + +warning: ignoring unknown pass `collapse-docs` + --> $DIR/deprecated-attrs.rs:9:17 + | +LL | #![doc(passes = "collapse-docs unindent-comments")] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: the `collapse-docs` pass was removed in #80261 + +warning: the `#![doc(plugins = "...")]` attribute is deprecated + --> $DIR/deprecated-attrs.rs:14:8 + | +LL | #![doc(plugins = "xxx")] + | ^^^^^^^^^^^^^^^ | - = warning: see issue #44136 for more information + = note: see issue #44136 for more information + = warning: `#![doc(plugins = "...")]` no longer functions; see CVE-2018-1000622 -warning: 2 warnings emitted +warning: 5 warnings emitted diff --git a/src/test/rustdoc-ui/doc-alias-crate-level.stderr b/src/test/rustdoc-ui/doc-alias-crate-level.stderr index 9e746cba05f63..fc8095e03ca93 100644 --- a/src/test/rustdoc-ui/doc-alias-crate-level.stderr +++ b/src/test/rustdoc-ui/doc-alias-crate-level.stderr @@ -4,7 +4,7 @@ error: '\'' character isn't allowed in `#[doc(alias = "...")]` LL | #[doc(alias = "shouldn't work!")] | ^^^^^^^^^^^^^^^^^ -error: `#![doc(alias = "...")]` isn't allowed as a crate level attribute +error: `#![doc(alias = "...")]` isn't allowed as a crate-level attribute --> $DIR/doc-alias-crate-level.rs:1:8 | LL | #![doc(alias = "crate-level-not-working")] diff --git a/src/test/rustdoc-ui/doc-attr.rs b/src/test/rustdoc-ui/doc-attr.rs index 3519b5707b375..980d1c0e2077d 100644 --- a/src/test/rustdoc-ui/doc-attr.rs +++ b/src/test/rustdoc-ui/doc-attr.rs @@ -1,11 +1,25 @@ #![crate_type = "lib"] -#![deny(unused_attributes)] -//~^ NOTE lint level is defined here +#![deny(warnings)] #![doc(as_ptr)] //~^ ERROR unknown `doc` attribute -//~| WARNING will become a hard error in a future release +//~^^ WARN #[doc(as_ptr)] //~^ ERROR unknown `doc` attribute -//~| WARNING will become a hard error in a future release +//~^^ WARN pub fn foo() {} + +#[doc(123)] +//~^ ERROR invalid `doc` attribute +//~| WARN +#[doc("hello", "bar")] +//~^ ERROR invalid `doc` attribute +//~| WARN +//~| ERROR invalid `doc` attribute +//~| WARN +#[doc(foo::bar, crate::bar::baz = "bye")] +//~^ ERROR unknown `doc` attribute +//~| WARN +//~| ERROR unknown `doc` attribute +//~| WARN +fn bar() {} diff --git a/src/test/rustdoc-ui/doc-attr.stderr b/src/test/rustdoc-ui/doc-attr.stderr index 9666db2b10e01..cc2494c92e649 100644 --- a/src/test/rustdoc-ui/doc-attr.stderr +++ b/src/test/rustdoc-ui/doc-attr.stderr @@ -1,5 +1,5 @@ error: unknown `doc` attribute `as_ptr` - --> $DIR/doc-attr.rs:8:7 + --> $DIR/doc-attr.rs:7:7 | LL | #[doc(as_ptr)] | ^^^^^^ @@ -7,17 +7,65 @@ LL | #[doc(as_ptr)] note: the lint level is defined here --> $DIR/doc-attr.rs:2:9 | -LL | #![deny(unused_attributes)] - | ^^^^^^^^^^^^^^^^^ +LL | #![deny(warnings)] + | ^^^^^^^^ + = note: `#[deny(invalid_doc_attributes)]` implied by `#[deny(warnings)]` = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #82730 + +error: invalid `doc` attribute + --> $DIR/doc-attr.rs:12:7 + | +LL | #[doc(123)] + | ^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #82730 + +error: invalid `doc` attribute + --> $DIR/doc-attr.rs:15:7 + | +LL | #[doc("hello", "bar")] + | ^^^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #82730 + +error: invalid `doc` attribute + --> $DIR/doc-attr.rs:15:16 + | +LL | #[doc("hello", "bar")] + | ^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #82730 + +error: unknown `doc` attribute `foo::bar` + --> $DIR/doc-attr.rs:20:7 + | +LL | #[doc(foo::bar, crate::bar::baz = "bye")] + | ^^^^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #82730 + +error: unknown `doc` attribute `crate::bar::baz` + --> $DIR/doc-attr.rs:20:17 + | +LL | #[doc(foo::bar, crate::bar::baz = "bye")] + | ^^^^^^^^^^^^^^^^^^^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #82730 error: unknown `doc` attribute `as_ptr` - --> $DIR/doc-attr.rs:4:8 + --> $DIR/doc-attr.rs:3:8 | LL | #![doc(as_ptr)] | ^^^^^^ | = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #82730 -error: aborting due to 2 previous errors +error: aborting due to 7 previous errors diff --git a/src/test/rustdoc-ui/doc-attr2.rs b/src/test/rustdoc-ui/doc-attr2.rs new file mode 100644 index 0000000000000..3fb484644d7fa --- /dev/null +++ b/src/test/rustdoc-ui/doc-attr2.rs @@ -0,0 +1,11 @@ +#![crate_type = "lib"] +#![deny(warnings)] + +#[doc(test(no_crate_inject))] //~ ERROR +//~^ WARN +pub fn foo() {} + +pub mod bar { + #![doc(test(no_crate_inject))] //~ ERROR + //~^ WARN +} diff --git a/src/test/rustdoc-ui/doc-attr2.stderr b/src/test/rustdoc-ui/doc-attr2.stderr new file mode 100644 index 0000000000000..643107318b979 --- /dev/null +++ b/src/test/rustdoc-ui/doc-attr2.stderr @@ -0,0 +1,26 @@ +error: `#![doc(test(...)]` is only allowed as a crate-level attribute + --> $DIR/doc-attr2.rs:4:7 + | +LL | #[doc(test(no_crate_inject))] + | ^^^^^^^^^^^^^^^^^^^^^ + | +note: the lint level is defined here + --> $DIR/doc-attr2.rs:2:9 + | +LL | #![deny(warnings)] + | ^^^^^^^^ + = note: `#[deny(invalid_doc_attributes)]` implied by `#[deny(warnings)]` + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #82730 + +error: `#![doc(test(...)]` is only allowed as a crate-level attribute + --> $DIR/doc-attr2.rs:9:12 + | +LL | #![doc(test(no_crate_inject))] + | ^^^^^^^^^^^^^^^^^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #82730 + +error: aborting due to 2 previous errors + diff --git a/src/test/rustdoc-ui/doc-spotlight.fixed b/src/test/rustdoc-ui/doc-spotlight.fixed new file mode 100644 index 0000000000000..94b69a99879c0 --- /dev/null +++ b/src/test/rustdoc-ui/doc-spotlight.fixed @@ -0,0 +1,9 @@ +// check-pass +// run-rustfix + +#![feature(doc_notable_trait)] + +#[doc(notable_trait)] +//~^ WARN unknown `doc` attribute `spotlight` +//~| WARN this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! +trait MyTrait {} diff --git a/src/test/rustdoc-ui/doc-spotlight.rs b/src/test/rustdoc-ui/doc-spotlight.rs new file mode 100644 index 0000000000000..cc5f159a8093e --- /dev/null +++ b/src/test/rustdoc-ui/doc-spotlight.rs @@ -0,0 +1,9 @@ +// check-pass +// run-rustfix + +#![feature(doc_notable_trait)] + +#[doc(spotlight)] +//~^ WARN unknown `doc` attribute `spotlight` +//~| WARN this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! +trait MyTrait {} diff --git a/src/test/rustdoc-ui/doc-spotlight.stderr b/src/test/rustdoc-ui/doc-spotlight.stderr new file mode 100644 index 0000000000000..e5fa6293f3d8d --- /dev/null +++ b/src/test/rustdoc-ui/doc-spotlight.stderr @@ -0,0 +1,14 @@ +warning: unknown `doc` attribute `spotlight` + --> $DIR/doc-spotlight.rs:6:7 + | +LL | #[doc(spotlight)] + | ^^^^^^^^^ help: use `notable_trait` instead + | + = note: `#[warn(invalid_doc_attributes)]` on by default + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #82730 + = note: `doc(spotlight)` was renamed to `doc(notable_trait)` + = note: `doc(spotlight)` is now a no-op + +warning: 1 warning emitted + diff --git a/src/test/rustdoc-ui/error-in-impl-trait/trait-alias-closure.rs b/src/test/rustdoc-ui/error-in-impl-trait/trait-alias-closure.rs index 31dd786cbbf89..3e7b428023487 100644 --- a/src/test/rustdoc-ui/error-in-impl-trait/trait-alias-closure.rs +++ b/src/test/rustdoc-ui/error-in-impl-trait/trait-alias-closure.rs @@ -1,5 +1,5 @@ // check-pass -#![feature(type_alias_impl_trait)] +#![feature(min_type_alias_impl_trait)] pub trait ValidTrait {} type ImplTrait = impl ValidTrait; diff --git a/src/test/rustdoc-ui/error-in-impl-trait/trait-alias.rs b/src/test/rustdoc-ui/error-in-impl-trait/trait-alias.rs index c18a024af4bbc..7ebf4d544de1e 100644 --- a/src/test/rustdoc-ui/error-in-impl-trait/trait-alias.rs +++ b/src/test/rustdoc-ui/error-in-impl-trait/trait-alias.rs @@ -1,5 +1,5 @@ // check-pass -#![feature(type_alias_impl_trait)] +#![feature(min_type_alias_impl_trait)] pub trait ValidTrait {} type ImplTrait = impl ValidTrait; diff --git a/src/test/rustdoc-ui/intra-doc/anchors.rs b/src/test/rustdoc-ui/intra-doc/anchors.rs index 009b291be1f08..6785cb7abeaee 100644 --- a/src/test/rustdoc-ui/intra-doc/anchors.rs +++ b/src/test/rustdoc-ui/intra-doc/anchors.rs @@ -43,3 +43,7 @@ pub fn enum_link() {} /// [u32#hello] //~^ ERROR `u32#hello` contains an anchor pub fn x() {} + +/// [prim@usize#x] +//~^ ERROR `prim@usize#x` contains an anchor +pub mod usize {} diff --git a/src/test/rustdoc-ui/intra-doc/anchors.stderr b/src/test/rustdoc-ui/intra-doc/anchors.stderr index 97b0cea0c1e4d..d63e1ee60b3c5 100644 --- a/src/test/rustdoc-ui/intra-doc/anchors.stderr +++ b/src/test/rustdoc-ui/intra-doc/anchors.stderr @@ -1,32 +1,53 @@ -error: `Foo::f#hola` contains an anchor, but links to fields are already anchored - --> $DIR/anchors.rs:25:15 +error: `prim@usize#x` contains an anchor, but links to builtin types are already anchored + --> $DIR/anchors.rs:47:6 | -LL | /// Or maybe [Foo::f#hola]. - | ^^^^^^^^^^^ contains invalid anchor +LL | /// [prim@usize#x] + | ^^^^^^^^^^-- + | | + | invalid anchor | note: the lint level is defined here --> $DIR/anchors.rs:1:9 | LL | #![deny(rustdoc::broken_intra_doc_links)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + = note: this restriction may be lifted in a future release + = note: see https://github.com/rust-lang/rust/issues/83083 for more information + +error: `Foo::f#hola` contains an anchor, but links to fields are already anchored + --> $DIR/anchors.rs:25:15 + | +LL | /// Or maybe [Foo::f#hola]. + | ^^^^^^----- + | | + | invalid anchor error: `hello#people#!` contains multiple anchors --> $DIR/anchors.rs:31:28 | LL | /// Another anchor error: [hello#people#!]. - | ^^^^^^^^^^^^^^ contains invalid anchor + | ^^^^^^^^^^^^-- + | | + | invalid anchor error: `Enum::A#whatever` contains an anchor, but links to variants are already anchored --> $DIR/anchors.rs:37:28 | LL | /// Damn enum's variants: [Enum::A#whatever]. - | ^^^^^^^^^^^^^^^^ contains invalid anchor + | ^^^^^^^--------- + | | + | invalid anchor error: `u32#hello` contains an anchor, but links to builtin types are already anchored --> $DIR/anchors.rs:43:6 | LL | /// [u32#hello] - | ^^^^^^^^^ contains invalid anchor + | ^^^------ + | | + | invalid anchor + | + = note: this restriction may be lifted in a future release + = note: see https://github.com/rust-lang/rust/issues/83083 for more information -error: aborting due to 4 previous errors +error: aborting due to 5 previous errors diff --git a/src/test/rustdoc-ui/intra-doc/double-anchor.stderr b/src/test/rustdoc-ui/intra-doc/double-anchor.stderr index c0241b98b78c1..6addb010e078f 100644 --- a/src/test/rustdoc-ui/intra-doc/double-anchor.stderr +++ b/src/test/rustdoc-ui/intra-doc/double-anchor.stderr @@ -2,7 +2,9 @@ warning: `with#anchor#error` contains multiple anchors --> $DIR/double-anchor.rs:5:18 | LL | /// docs [label][with#anchor#error] - | ^^^^^^^^^^^^^^^^^ contains invalid anchor + | ^^^^^^^^^^^------ + | | + | invalid anchor | = note: `#[warn(rustdoc::broken_intra_doc_links)]` on by default diff --git a/src/test/rustdoc-ui/intra-doc/email-address-localhost.rs b/src/test/rustdoc-ui/intra-doc/email-address-localhost.rs new file mode 100644 index 0000000000000..417618c74582c --- /dev/null +++ b/src/test/rustdoc-ui/intra-doc/email-address-localhost.rs @@ -0,0 +1,6 @@ +#![deny(warnings)] + +//! Email me at . +//~^ ERROR unknown disambiguator `hello` + +//! This should *not* warn: . diff --git a/src/test/rustdoc-ui/intra-doc/email-address-localhost.stderr b/src/test/rustdoc-ui/intra-doc/email-address-localhost.stderr new file mode 100644 index 0000000000000..f287f87408c48 --- /dev/null +++ b/src/test/rustdoc-ui/intra-doc/email-address-localhost.stderr @@ -0,0 +1,16 @@ +error: unknown disambiguator `hello` + --> $DIR/email-address-localhost.rs:3:18 + | +LL | //! Email me at . + | ^^^^^ + | +note: the lint level is defined here + --> $DIR/email-address-localhost.rs:1:9 + | +LL | #![deny(warnings)] + | ^^^^^^^^ + = note: `#[deny(rustdoc::broken_intra_doc_links)]` implied by `#[deny(warnings)]` + = note: see https://doc.rust-lang.org/nightly/rustdoc/linking-to-items-by-name.html#namespaces-and-disambiguators for more info about disambiguators + +error: aborting due to previous error + diff --git a/src/test/rustdoc-ui/intra-doc/private-from-crate-level.rs b/src/test/rustdoc-ui/intra-doc/private-from-crate-level.rs new file mode 100644 index 0000000000000..e429e75b2148f --- /dev/null +++ b/src/test/rustdoc-ui/intra-doc/private-from-crate-level.rs @@ -0,0 +1,6 @@ +// check-pass + +//! [my_module] +//~^ WARN public documentation for `private_from_crate_level` links to private item `my_module` + +mod my_module {} diff --git a/src/test/rustdoc-ui/intra-doc/private-from-crate-level.stderr b/src/test/rustdoc-ui/intra-doc/private-from-crate-level.stderr new file mode 100644 index 0000000000000..6172cd2e316a2 --- /dev/null +++ b/src/test/rustdoc-ui/intra-doc/private-from-crate-level.stderr @@ -0,0 +1,11 @@ +warning: public documentation for `private_from_crate_level` links to private item `my_module` + --> $DIR/private-from-crate-level.rs:3:6 + | +LL | //! [my_module] + | ^^^^^^^^^ this item is private + | + = note: `#[warn(rustdoc::private_intra_doc_links)]` on by default + = note: this link will resolve properly if you pass `--document-private-items` + +warning: 1 warning emitted + diff --git a/src/test/rustdoc-ui/intra-doc/private.private.stderr b/src/test/rustdoc-ui/intra-doc/private.private.stderr index cae5b1f20e6c3..392321f9c60db 100644 --- a/src/test/rustdoc-ui/intra-doc/private.private.stderr +++ b/src/test/rustdoc-ui/intra-doc/private.private.stderr @@ -1,19 +1,27 @@ warning: public documentation for `DocMe` links to private item `DontDocMe` - --> $DIR/private.rs:5:11 + --> $DIR/private.rs:7:11 | -LL | /// docs [DontDocMe] [DontDocMe::f] +LL | /// docs [DontDocMe] [DontDocMe::f] [DontDocMe::x] | ^^^^^^^^^ this item is private | = note: `#[warn(rustdoc::private_intra_doc_links)]` on by default = note: this link resolves only because you passed `--document-private-items`, but will break without warning: public documentation for `DocMe` links to private item `DontDocMe::f` - --> $DIR/private.rs:5:23 + --> $DIR/private.rs:7:23 | -LL | /// docs [DontDocMe] [DontDocMe::f] +LL | /// docs [DontDocMe] [DontDocMe::f] [DontDocMe::x] | ^^^^^^^^^^^^ this item is private | = note: this link resolves only because you passed `--document-private-items`, but will break without -warning: 2 warnings emitted +warning: public documentation for `DocMe` links to private item `DontDocMe::x` + --> $DIR/private.rs:7:38 + | +LL | /// docs [DontDocMe] [DontDocMe::f] [DontDocMe::x] + | ^^^^^^^^^^^^ this item is private + | + = note: this link resolves only because you passed `--document-private-items`, but will break without + +warning: 3 warnings emitted diff --git a/src/test/rustdoc-ui/intra-doc/private.public.stderr b/src/test/rustdoc-ui/intra-doc/private.public.stderr index 05b202e37fbcb..5d1c34b9168d9 100644 --- a/src/test/rustdoc-ui/intra-doc/private.public.stderr +++ b/src/test/rustdoc-ui/intra-doc/private.public.stderr @@ -1,19 +1,27 @@ warning: public documentation for `DocMe` links to private item `DontDocMe` - --> $DIR/private.rs:5:11 + --> $DIR/private.rs:7:11 | -LL | /// docs [DontDocMe] [DontDocMe::f] +LL | /// docs [DontDocMe] [DontDocMe::f] [DontDocMe::x] | ^^^^^^^^^ this item is private | = note: `#[warn(rustdoc::private_intra_doc_links)]` on by default = note: this link will resolve properly if you pass `--document-private-items` warning: public documentation for `DocMe` links to private item `DontDocMe::f` - --> $DIR/private.rs:5:23 + --> $DIR/private.rs:7:23 | -LL | /// docs [DontDocMe] [DontDocMe::f] +LL | /// docs [DontDocMe] [DontDocMe::f] [DontDocMe::x] | ^^^^^^^^^^^^ this item is private | = note: this link will resolve properly if you pass `--document-private-items` -warning: 2 warnings emitted +warning: public documentation for `DocMe` links to private item `DontDocMe::x` + --> $DIR/private.rs:7:38 + | +LL | /// docs [DontDocMe] [DontDocMe::f] [DontDocMe::x] + | ^^^^^^^^^^^^ this item is private + | + = note: this link will resolve properly if you pass `--document-private-items` + +warning: 3 warnings emitted diff --git a/src/test/rustdoc-ui/intra-doc/private.rs b/src/test/rustdoc-ui/intra-doc/private.rs index 3782864305f1f..525332ddaac3b 100644 --- a/src/test/rustdoc-ui/intra-doc/private.rs +++ b/src/test/rustdoc-ui/intra-doc/private.rs @@ -2,12 +2,16 @@ // revisions: public private // [private]compile-flags: --document-private-items -/// docs [DontDocMe] [DontDocMe::f] +// make sure to update `rustdoc/intra-doc/private.rs` if you update this file + +/// docs [DontDocMe] [DontDocMe::f] [DontDocMe::x] //~^ WARNING public documentation for `DocMe` links to private item `DontDocMe` +//~| WARNING public documentation for `DocMe` links to private item `DontDocMe::x` //~| WARNING public documentation for `DocMe` links to private item `DontDocMe::f` -// FIXME: for [private] we should also make sure the link was actually generated pub struct DocMe; -struct DontDocMe; +struct DontDocMe { + x: usize, +} impl DontDocMe { fn f() {} diff --git a/src/test/rustdoc-ui/intra-doc/unknown-disambiguator.rs b/src/test/rustdoc-ui/intra-doc/unknown-disambiguator.rs new file mode 100644 index 0000000000000..925fc515a3e65 --- /dev/null +++ b/src/test/rustdoc-ui/intra-doc/unknown-disambiguator.rs @@ -0,0 +1,13 @@ +#![deny(warnings)] + +//! Linking to [foo@banana] and [`bar@banana!()`]. +//~^ ERROR unknown disambiguator `foo` +//~| ERROR unknown disambiguator `bar` +//! And to [no disambiguator](@nectarine) and [another](@apricot!()). +//~^ ERROR unknown disambiguator `` +//~| ERROR unknown disambiguator `` +//! And with weird backticks: [``foo@hello``] [foo`@`hello]. +//~^ ERROR unknown disambiguator `foo` +//~| ERROR unknown disambiguator `foo` + +fn main() {} diff --git a/src/test/rustdoc-ui/intra-doc/unknown-disambiguator.stderr b/src/test/rustdoc-ui/intra-doc/unknown-disambiguator.stderr new file mode 100644 index 0000000000000..94d6d4616518e --- /dev/null +++ b/src/test/rustdoc-ui/intra-doc/unknown-disambiguator.stderr @@ -0,0 +1,56 @@ +error: unknown disambiguator `foo` + --> $DIR/unknown-disambiguator.rs:3:17 + | +LL | //! Linking to [foo@banana] and [`bar@banana!()`]. + | ^^^ + | +note: the lint level is defined here + --> $DIR/unknown-disambiguator.rs:1:9 + | +LL | #![deny(warnings)] + | ^^^^^^^^ + = note: `#[deny(rustdoc::broken_intra_doc_links)]` implied by `#[deny(warnings)]` + = note: see https://doc.rust-lang.org/nightly/rustdoc/linking-to-items-by-name.html#namespaces-and-disambiguators for more info about disambiguators + +error: unknown disambiguator `bar` + --> $DIR/unknown-disambiguator.rs:3:35 + | +LL | //! Linking to [foo@banana] and [`bar@banana!()`]. + | ^^^ + | + = note: see https://doc.rust-lang.org/nightly/rustdoc/linking-to-items-by-name.html#namespaces-and-disambiguators for more info about disambiguators + +error: unknown disambiguator `foo` + --> $DIR/unknown-disambiguator.rs:9:34 + | +LL | //! And with weird backticks: [``foo@hello``] [foo`@`hello]. + | ^^^ + | + = note: see https://doc.rust-lang.org/nightly/rustdoc/linking-to-items-by-name.html#namespaces-and-disambiguators for more info about disambiguators + +error: unknown disambiguator `foo` + --> $DIR/unknown-disambiguator.rs:9:48 + | +LL | //! And with weird backticks: [``foo@hello``] [foo`@`hello]. + | ^^^ + | + = note: see https://doc.rust-lang.org/nightly/rustdoc/linking-to-items-by-name.html#namespaces-and-disambiguators for more info about disambiguators + +error: unknown disambiguator `` + --> $DIR/unknown-disambiguator.rs:6:31 + | +LL | //! And to [no disambiguator](@nectarine) and [another](@apricot!()). + | ^ + | + = note: see https://doc.rust-lang.org/nightly/rustdoc/linking-to-items-by-name.html#namespaces-and-disambiguators for more info about disambiguators + +error: unknown disambiguator `` + --> $DIR/unknown-disambiguator.rs:6:57 + | +LL | //! And to [no disambiguator](@nectarine) and [another](@apricot!()). + | ^ + | + = note: see https://doc.rust-lang.org/nightly/rustdoc/linking-to-items-by-name.html#namespaces-and-disambiguators for more info about disambiguators + +error: aborting due to 6 previous errors + diff --git a/src/test/rustdoc-ui/invalid-theme-name.rs b/src/test/rustdoc-ui/invalid-theme-name.rs new file mode 100644 index 0000000000000..c22ebf02718e6 --- /dev/null +++ b/src/test/rustdoc-ui/invalid-theme-name.rs @@ -0,0 +1,3 @@ +// compile-flags:--theme {{src-base}}/invalid-theme-name.rs +// error-pattern: invalid argument +// error-pattern: must have a .css extension diff --git a/src/test/rustdoc-ui/invalid-theme-name.stderr b/src/test/rustdoc-ui/invalid-theme-name.stderr new file mode 100644 index 0000000000000..80204442dbec3 --- /dev/null +++ b/src/test/rustdoc-ui/invalid-theme-name.stderr @@ -0,0 +1,4 @@ +error: invalid argument: "$DIR/invalid-theme-name.rs" + | + = help: arguments to --theme must have a .css extension + diff --git a/src/test/rustdoc-ui/issue-83883-describe-lints.rs b/src/test/rustdoc-ui/issue-83883-describe-lints.rs new file mode 100644 index 0000000000000..a261b782d4859 --- /dev/null +++ b/src/test/rustdoc-ui/issue-83883-describe-lints.rs @@ -0,0 +1,8 @@ +// compile-flags: -W help +// check-pass +// +// ignore-tidy-linelength +// +// normalize-stdout-test: "( +name default meaning\n +---- ------- -------\n)?( *[[:word:]:-]+ (allow |warn |deny |forbid ) [^\n]+\n)+" -> " $$NAMES $$LEVELS $$MEANINGS" +// normalize-stdout-test: " +name sub-lints\n +---- ---------\n( *[[:word:]:-]+ [^\n]+\n)+" -> " $$NAMES $$SUB_LINTS" +// normalize-stdout-test: " +rustdoc::all( (rustdoc::[[:word:]-]+, )*rustdoc::[[:word:]-]+)?" -> " rustdoc::all $$GROUPS$4" diff --git a/src/test/rustdoc-ui/issue-83883-describe-lints.stdout b/src/test/rustdoc-ui/issue-83883-describe-lints.stdout new file mode 100644 index 0000000000000..651faf5761f18 --- /dev/null +++ b/src/test/rustdoc-ui/issue-83883-describe-lints.stdout @@ -0,0 +1,25 @@ + +Available lint options: + -W Warn about + -A Allow + -D Deny + -F Forbid (deny and all attempts to override) + + +Lint checks provided by rustc: + + $NAMES $LEVELS $MEANINGS + +Lint groups provided by rustc: + + $NAMES $SUB_LINTS + +Lint checks provided by plugins loaded by this crate: + + $NAMES $LEVELS $MEANINGS + +Lint groups provided by plugins loaded by this crate: + + rustdoc::all $GROUPS + + diff --git a/src/test/rustdoc-ui/lint-group.rs b/src/test/rustdoc-ui/lint-group.rs index 1446f7f1c1f31..61555a6e68617 100644 --- a/src/test/rustdoc-ui/lint-group.rs +++ b/src/test/rustdoc-ui/lint-group.rs @@ -4,7 +4,7 @@ //! println!("sup"); //! ``` -#![deny(rustdoc)] +#![deny(rustdoc::all)] /// what up, let's make an [error] /// diff --git a/src/test/rustdoc-ui/lint-group.stderr b/src/test/rustdoc-ui/lint-group.stderr index 6f8a20f7d53f0..cc6f03b03ff61 100644 --- a/src/test/rustdoc-ui/lint-group.stderr +++ b/src/test/rustdoc-ui/lint-group.stderr @@ -7,9 +7,9 @@ LL | /// wait, this doesn't have a doctest? note: the lint level is defined here --> $DIR/lint-group.rs:7:9 | -LL | #![deny(rustdoc)] - | ^^^^^^^ - = note: `#[deny(rustdoc::missing_doc_code_examples)]` implied by `#[deny(rustdoc)]` +LL | #![deny(rustdoc::all)] + | ^^^^^^^^^^^^ + = note: `#[deny(rustdoc::missing_doc_code_examples)]` implied by `#[deny(rustdoc::all)]` error: documentation test in private item --> $DIR/lint-group.rs:19:1 @@ -24,9 +24,9 @@ LL | | /// ``` note: the lint level is defined here --> $DIR/lint-group.rs:7:9 | -LL | #![deny(rustdoc)] - | ^^^^^^^ - = note: `#[deny(rustdoc::private_doc_tests)]` implied by `#[deny(rustdoc)]` +LL | #![deny(rustdoc::all)] + | ^^^^^^^^^^^^ + = note: `#[deny(rustdoc::private_doc_tests)]` implied by `#[deny(rustdoc::all)]` error: missing code example in this documentation --> $DIR/lint-group.rs:26:1 @@ -43,9 +43,9 @@ LL | /// what up, let's make an [error] note: the lint level is defined here --> $DIR/lint-group.rs:7:9 | -LL | #![deny(rustdoc)] - | ^^^^^^^ - = note: `#[deny(rustdoc::broken_intra_doc_links)]` implied by `#[deny(rustdoc)]` +LL | #![deny(rustdoc::all)] + | ^^^^^^^^^^^^ + = note: `#[deny(rustdoc::broken_intra_doc_links)]` implied by `#[deny(rustdoc::all)]` = help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]` error: unclosed HTML tag `unknown` @@ -57,9 +57,9 @@ LL | /// note: the lint level is defined here --> $DIR/lint-group.rs:7:9 | -LL | #![deny(rustdoc)] - | ^^^^^^^ - = note: `#[deny(rustdoc::invalid_html_tags)]` implied by `#[deny(rustdoc)]` +LL | #![deny(rustdoc::all)] + | ^^^^^^^^^^^^ + = note: `#[deny(rustdoc::invalid_html_tags)]` implied by `#[deny(rustdoc::all)]` error: aborting due to 5 previous errors diff --git a/src/test/rustdoc-ui/lint-missing-doc-code-example.rs b/src/test/rustdoc-ui/lint-missing-doc-code-example.rs index 8d727b0d0b550..41e8847792694 100644 --- a/src/test/rustdoc-ui/lint-missing-doc-code-example.rs +++ b/src/test/rustdoc-ui/lint-missing-doc-code-example.rs @@ -12,16 +12,16 @@ /// ``` /// println!("hello"); /// ``` -fn test() { +pub fn test() { } #[allow(missing_docs)] -mod module1 { //~ ERROR +pub mod module1 { //~ ERROR } #[allow(rustdoc::missing_doc_code_examples)] /// doc -mod module2 { +pub mod module2 { /// doc pub fn test() {} @@ -63,9 +63,22 @@ pub enum Enum { /// Doc //~^ ERROR #[repr(C)] -union Union { +pub union Union { /// Doc, but no code example and it's fine! a: i32, /// Doc, but no code example and it's fine! b: f32, } + + +#[doc(hidden)] +pub mod foo { + pub fn bar() {} +} + +fn babar() {} + + +mod fofoo { + pub fn tadam() {} +} diff --git a/src/test/rustdoc-ui/lint-missing-doc-code-example.stderr b/src/test/rustdoc-ui/lint-missing-doc-code-example.stderr index 370c577f85d8f..3715797854281 100644 --- a/src/test/rustdoc-ui/lint-missing-doc-code-example.stderr +++ b/src/test/rustdoc-ui/lint-missing-doc-code-example.stderr @@ -1,7 +1,7 @@ error: missing code example in this documentation --> $DIR/lint-missing-doc-code-example.rs:19:1 | -LL | / mod module1 { +LL | / pub mod module1 { LL | | } | |_^ | diff --git a/src/test/rustdoc-ui/output-format-html-stable.rs b/src/test/rustdoc-ui/output-format-html-stable.rs new file mode 100644 index 0000000000000..fa0362640bb8f --- /dev/null +++ b/src/test/rustdoc-ui/output-format-html-stable.rs @@ -0,0 +1,4 @@ +// compile-flags: --output-format html +// check-pass +// This tests that `--output-format html` is accepted without `-Z unstable-options`, +// since it has been stable since 1.0. diff --git a/src/test/rustdoc-ui/renamed-lint-still-applies.rs b/src/test/rustdoc-ui/renamed-lint-still-applies.rs index 6d4bad16aadc2..05a32d3cc3109 100644 --- a/src/test/rustdoc-ui/renamed-lint-still-applies.rs +++ b/src/test/rustdoc-ui/renamed-lint-still-applies.rs @@ -1,5 +1,11 @@ // compile-args: --crate-type lib #![deny(broken_intra_doc_links)] -//~^ WARNING renamed +// FIXME: the old names for rustdoc lints should warn by default once `rustdoc::` makes it to the +// stable channel. //! [x] //~^ ERROR unresolved link + +#![deny(rustdoc::non_autolinks)] +//~^ WARNING renamed to `rustdoc::bare_urls` +//! http://example.com +//~^ ERROR not a hyperlink diff --git a/src/test/rustdoc-ui/renamed-lint-still-applies.stderr b/src/test/rustdoc-ui/renamed-lint-still-applies.stderr index e14cbfa1726c3..3040cad532c82 100644 --- a/src/test/rustdoc-ui/renamed-lint-still-applies.stderr +++ b/src/test/rustdoc-ui/renamed-lint-still-applies.stderr @@ -1,13 +1,13 @@ -warning: lint `broken_intra_doc_links` has been renamed to `rustdoc::broken_intra_doc_links` - --> $DIR/renamed-lint-still-applies.rs:2:9 +warning: lint `rustdoc::non_autolinks` has been renamed to `rustdoc::bare_urls` + --> $DIR/renamed-lint-still-applies.rs:8:9 | -LL | #![deny(broken_intra_doc_links)] - | ^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `rustdoc::broken_intra_doc_links` +LL | #![deny(rustdoc::non_autolinks)] + | ^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `rustdoc::bare_urls` | = note: `#[warn(renamed_and_removed_lints)]` on by default error: unresolved link to `x` - --> $DIR/renamed-lint-still-applies.rs:4:6 + --> $DIR/renamed-lint-still-applies.rs:5:6 | LL | //! [x] | ^ no item named `x` in scope @@ -17,7 +17,21 @@ note: the lint level is defined here | LL | #![deny(broken_intra_doc_links)] | ^^^^^^^^^^^^^^^^^^^^^^ + = note: `#[deny(rustdoc::broken_intra_doc_links)]` implied by `#[deny(broken_intra_doc_links)]` = help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]` -error: aborting due to previous error; 1 warning emitted +error: this URL is not a hyperlink + --> $DIR/renamed-lint-still-applies.rs:10:5 + | +LL | //! http://example.com + | ^^^^^^^^^^^^^^^^^^ help: use an automatic link instead: `` + | +note: the lint level is defined here + --> $DIR/renamed-lint-still-applies.rs:8:9 + | +LL | #![deny(rustdoc::non_autolinks)] + | ^^^^^^^^^^^^^^^^^^^^^^ + = note: bare URLs are not automatically turned into clickable links + +error: aborting due to 2 previous errors; 1 warning emitted diff --git a/src/test/rustdoc-ui/test-hidden.rs b/src/test/rustdoc-ui/test-hidden.rs new file mode 100644 index 0000000000000..9e068d4db4b72 --- /dev/null +++ b/src/test/rustdoc-ui/test-hidden.rs @@ -0,0 +1,25 @@ +// check-pass +// compile-flags:--test +// normalize-stdout-test "finished in \d+\.\d+s" -> "finished in $$TIME" +// normalize-stdout-test: "src/test/rustdoc-ui" -> "$$DIR" + +/// ``` +// If `const_err` becomes a hard error in the future, please replace this with another +// deny-by-default lint instead of removing it altogether +/// # ! [allow(const_err)] +/// const C: usize = 1/0; +/// +/// # use std::path::PathBuf; +/// #use std::path::Path; +/// let x = Path::new("y.rs"); +/// let x = PathBuf::from("y.rs"); +/// +/// #[cfg(FALSE)] +/// assert!(false); +/// +/// # [cfg(FALSE)] +/// assert!(false); +/// ``` +fn main() { + panic!(); +} diff --git a/src/test/rustdoc-ui/test-hidden.stdout b/src/test/rustdoc-ui/test-hidden.stdout new file mode 100644 index 0000000000000..89d98d6d688f3 --- /dev/null +++ b/src/test/rustdoc-ui/test-hidden.stdout @@ -0,0 +1,6 @@ + +running 1 test +test $DIR/test-hidden.rs - main (line 6) ... ok + +test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME + diff --git a/src/test/rustdoc-ui/unknown-renamed-lints.rs b/src/test/rustdoc-ui/unknown-renamed-lints.rs index d2c78bc477410..9096cce127658 100644 --- a/src/test/rustdoc-ui/unknown-renamed-lints.rs +++ b/src/test/rustdoc-ui/unknown-renamed-lints.rs @@ -8,9 +8,17 @@ //~^ ERROR unknown lint: `rustdoc::x` #![deny(intra_doc_link_resolution_failure)] //~^ ERROR renamed to `rustdoc::broken_intra_doc_links` - #![deny(non_autolinks)] -//~^ ERROR renamed to `rustdoc::non_autolinks` +//~^ ERROR renamed to `rustdoc::bare_urls` +#![deny(rustdoc::non_autolinks)] +//~^ ERROR renamed to `rustdoc::bare_urls` + +#![deny(private_doc_tests)] +// FIXME: the old names for rustdoc lints should warn by default once `rustdoc::` makes it to the +// stable channel. + +#![deny(rustdoc)] +//~^ ERROR removed: use `rustdoc::all` instead // Explicitly don't try to handle this case, it was never valid #![deny(rustdoc::intra_doc_link_resolution_failure)] diff --git a/src/test/rustdoc-ui/unknown-renamed-lints.stderr b/src/test/rustdoc-ui/unknown-renamed-lints.stderr index 0f31673fb47f2..51e06821cf7ab 100644 --- a/src/test/rustdoc-ui/unknown-renamed-lints.stderr +++ b/src/test/rustdoc-ui/unknown-renamed-lints.stderr @@ -28,19 +28,31 @@ note: the lint level is defined here LL | #![deny(renamed_and_removed_lints)] | ^^^^^^^^^^^^^^^^^^^^^^^^^ -error: lint `non_autolinks` has been renamed to `rustdoc::non_autolinks` - --> $DIR/unknown-renamed-lints.rs:12:9 +error: lint `non_autolinks` has been renamed to `rustdoc::bare_urls` + --> $DIR/unknown-renamed-lints.rs:11:9 | LL | #![deny(non_autolinks)] - | ^^^^^^^^^^^^^ help: use the new name: `rustdoc::non_autolinks` + | ^^^^^^^^^^^^^ help: use the new name: `rustdoc::bare_urls` + +error: lint `rustdoc::non_autolinks` has been renamed to `rustdoc::bare_urls` + --> $DIR/unknown-renamed-lints.rs:13:9 + | +LL | #![deny(rustdoc::non_autolinks)] + | ^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `rustdoc::bare_urls` + +error: lint `rustdoc` has been removed: use `rustdoc::all` instead + --> $DIR/unknown-renamed-lints.rs:20:9 + | +LL | #![deny(rustdoc)] + | ^^^^^^^ error: unknown lint: `rustdoc::intra_doc_link_resolution_failure` - --> $DIR/unknown-renamed-lints.rs:16:9 + --> $DIR/unknown-renamed-lints.rs:24:9 | LL | #![deny(rustdoc::intra_doc_link_resolution_failure)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: Compilation failed, aborting rustdoc -error: aborting due to 6 previous errors +error: aborting due to 8 previous errors diff --git a/src/test/rustdoc-ui/unused-extern-crate.rs b/src/test/rustdoc-ui/unused-extern-crate.rs new file mode 100644 index 0000000000000..f703a18379074 --- /dev/null +++ b/src/test/rustdoc-ui/unused-extern-crate.rs @@ -0,0 +1,3 @@ +// check-pass +// aux-crate:panic_item=panic-item.rs +// @has unused_extern_crate/index.html diff --git a/src/test/rustdoc-ui/url-improvements.rs b/src/test/rustdoc-ui/url-improvements.rs deleted file mode 100644 index d0b43de2f0e0e..0000000000000 --- a/src/test/rustdoc-ui/url-improvements.rs +++ /dev/null @@ -1,66 +0,0 @@ -#![deny(rustdoc::non_autolinks)] - -/// [http://aa.com](http://aa.com) -//~^ ERROR unneeded long form for URL -/// [http://bb.com] -//~^ ERROR unneeded long form for URL -/// -/// [http://bb.com]: http://bb.com -/// -/// [http://c.com][http://c.com] -pub fn a() {} - -/// https://somewhere.com -//~^ ERROR this URL is not a hyperlink -/// https://somewhere.com/a -//~^ ERROR this URL is not a hyperlink -/// https://www.somewhere.com -//~^ ERROR this URL is not a hyperlink -/// https://www.somewhere.com/a -//~^ ERROR this URL is not a hyperlink -/// https://subdomain.example.com -//~^ ERROR not a hyperlink -/// https://somewhere.com? -//~^ ERROR this URL is not a hyperlink -/// https://somewhere.com/a? -//~^ ERROR this URL is not a hyperlink -/// https://somewhere.com?hello=12 -//~^ ERROR this URL is not a hyperlink -/// https://somewhere.com/a?hello=12 -//~^ ERROR this URL is not a hyperlink -/// https://example.com?hello=12#xyz -//~^ ERROR this URL is not a hyperlink -/// https://example.com/a?hello=12#xyz -//~^ ERROR this URL is not a hyperlink -/// https://example.com#xyz -//~^ ERROR this URL is not a hyperlink -/// https://example.com/a#xyz -//~^ ERROR this URL is not a hyperlink -/// https://somewhere.com?hello=12&bye=11 -//~^ ERROR this URL is not a hyperlink -/// https://somewhere.com/a?hello=12&bye=11 -//~^ ERROR this URL is not a hyperlink -/// https://somewhere.com?hello=12&bye=11#xyz -//~^ ERROR this URL is not a hyperlink -/// hey! https://somewhere.com/a?hello=12&bye=11#xyz -//~^ ERROR this URL is not a hyperlink -pub fn c() {} - -/// -/// [a](http://a.com) -/// [b] -/// -/// [b]: http://b.com -/// -/// ``` -/// This link should not be linted: http://example.com -/// ``` -/// -/// [should_not.lint](should_not.lint) -pub fn everything_is_fine_here() {} - -#[allow(rustdoc::non_autolinks)] -pub mod foo { - /// https://somewhere.com/a?hello=12&bye=11#xyz - pub fn bar() {} -} diff --git a/src/test/rustdoc-ui/url-improvements.stderr b/src/test/rustdoc-ui/url-improvements.stderr deleted file mode 100644 index f377973656a83..0000000000000 --- a/src/test/rustdoc-ui/url-improvements.stderr +++ /dev/null @@ -1,122 +0,0 @@ -error: unneeded long form for URL - --> $DIR/url-improvements.rs:3:5 - | -LL | /// [http://aa.com](http://aa.com) - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use an automatic link instead: `` - | -note: the lint level is defined here - --> $DIR/url-improvements.rs:1:9 - | -LL | #![deny(rustdoc::non_autolinks)] - | ^^^^^^^^^^^^^^^^^^^^^^ - -error: unneeded long form for URL - --> $DIR/url-improvements.rs:5:5 - | -LL | /// [http://bb.com] - | ^^^^^^^^^^^^^^^ help: use an automatic link instead: `` - -error: this URL is not a hyperlink - --> $DIR/url-improvements.rs:13:5 - | -LL | /// https://somewhere.com - | ^^^^^^^^^^^^^^^^^^^^^ help: use an automatic link instead: `` - -error: this URL is not a hyperlink - --> $DIR/url-improvements.rs:15:5 - | -LL | /// https://somewhere.com/a - | ^^^^^^^^^^^^^^^^^^^^^^^ help: use an automatic link instead: `` - -error: this URL is not a hyperlink - --> $DIR/url-improvements.rs:17:5 - | -LL | /// https://www.somewhere.com - | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: use an automatic link instead: `` - -error: this URL is not a hyperlink - --> $DIR/url-improvements.rs:19:5 - | -LL | /// https://www.somewhere.com/a - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use an automatic link instead: `` - -error: this URL is not a hyperlink - --> $DIR/url-improvements.rs:21:5 - | -LL | /// https://subdomain.example.com - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use an automatic link instead: `` - -error: this URL is not a hyperlink - --> $DIR/url-improvements.rs:23:5 - | -LL | /// https://somewhere.com? - | ^^^^^^^^^^^^^^^^^^^^^^ help: use an automatic link instead: `` - -error: this URL is not a hyperlink - --> $DIR/url-improvements.rs:25:5 - | -LL | /// https://somewhere.com/a? - | ^^^^^^^^^^^^^^^^^^^^^^^^ help: use an automatic link instead: `` - -error: this URL is not a hyperlink - --> $DIR/url-improvements.rs:27:5 - | -LL | /// https://somewhere.com?hello=12 - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use an automatic link instead: `` - -error: this URL is not a hyperlink - --> $DIR/url-improvements.rs:29:5 - | -LL | /// https://somewhere.com/a?hello=12 - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use an automatic link instead: `` - -error: this URL is not a hyperlink - --> $DIR/url-improvements.rs:31:5 - | -LL | /// https://example.com?hello=12#xyz - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use an automatic link instead: `` - -error: this URL is not a hyperlink - --> $DIR/url-improvements.rs:33:5 - | -LL | /// https://example.com/a?hello=12#xyz - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use an automatic link instead: `` - -error: this URL is not a hyperlink - --> $DIR/url-improvements.rs:35:5 - | -LL | /// https://example.com#xyz - | ^^^^^^^^^^^^^^^^^^^^^^^ help: use an automatic link instead: `` - -error: this URL is not a hyperlink - --> $DIR/url-improvements.rs:37:5 - | -LL | /// https://example.com/a#xyz - | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: use an automatic link instead: `` - -error: this URL is not a hyperlink - --> $DIR/url-improvements.rs:39:5 - | -LL | /// https://somewhere.com?hello=12&bye=11 - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use an automatic link instead: `` - -error: this URL is not a hyperlink - --> $DIR/url-improvements.rs:41:5 - | -LL | /// https://somewhere.com/a?hello=12&bye=11 - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use an automatic link instead: `` - -error: this URL is not a hyperlink - --> $DIR/url-improvements.rs:43:5 - | -LL | /// https://somewhere.com?hello=12&bye=11#xyz - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use an automatic link instead: `` - -error: this URL is not a hyperlink - --> $DIR/url-improvements.rs:45:10 - | -LL | /// hey! https://somewhere.com/a?hello=12&bye=11#xyz - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use an automatic link instead: `` - -error: aborting due to 19 previous errors - diff --git a/src/test/rustdoc/asm-foreign.rs b/src/test/rustdoc/asm-foreign.rs new file mode 100644 index 0000000000000..570ed043dd9e8 --- /dev/null +++ b/src/test/rustdoc/asm-foreign.rs @@ -0,0 +1,20 @@ +// Make sure rustdoc accepts asm! for a foreign architecture. + +#![feature(asm)] + +// @has asm_foreign/fn.aarch64.html +pub unsafe fn aarch64(a: f64, b: f64) -> f64 { + let c; + asm!("add {:d}, {:d}, d0", out(vreg) c, in(vreg) a, in("d0") { + || {}; + b + }); + c +} + +// @has asm_foreign/fn.x86.html +pub unsafe fn x86(a: f64, b: f64) -> f64 { + let c; + asm!("addsd {}, {}, xmm0", out(xmm_reg) c, in(xmm_reg) a, in("xmm0") b); + c +} diff --git a/src/test/rustdoc/asm-foreign2.rs b/src/test/rustdoc/asm-foreign2.rs new file mode 100644 index 0000000000000..34e313e7eaceb --- /dev/null +++ b/src/test/rustdoc/asm-foreign2.rs @@ -0,0 +1,11 @@ +// only-aarch64 +// Make sure rustdoc accepts options(att_syntax) asm! on non-x86 targets. + +#![feature(asm)] + +// @has asm_foreign2/fn.x86.html +pub unsafe fn x86(x: i64) -> i64 { + let y; + asm!("movq {}, {}", in(reg) x, out(reg) y, options(att_syntax)); + y +} diff --git a/src/test/rustdoc/assoc-item-cast.rs b/src/test/rustdoc/assoc-item-cast.rs index dc62fac6a957d..273fc62aa1793 100644 --- a/src/test/rustdoc/assoc-item-cast.rs +++ b/src/test/rustdoc/assoc-item-cast.rs @@ -1,6 +1,5 @@ #![crate_name = "foo"] -// ignore-tidy-linelength pub trait Expression { type SqlType; diff --git a/src/test/rustdoc/assoc-types.rs b/src/test/rustdoc/assoc-types.rs index 5f0fdbb322cad..8fda171002ba0 100644 --- a/src/test/rustdoc/assoc-types.rs +++ b/src/test/rustdoc/assoc-types.rs @@ -1,5 +1,3 @@ -// ignore-tidy-linelength - #![crate_type="lib"] // @has assoc_types/trait.Index.html @@ -8,14 +6,14 @@ pub trait Index { type Output: ?Sized; // @has - '//*[@id="tymethod.index"]//code' \ // "fn index<'a>(&'a self, index: I) -> &'a Self::Output" - // @has - '//*[@id="tymethod.index"]//code//a[@href="../assoc_types/trait.Index.html#associatedtype.Output"]' \ + // @has - '//*[@id="tymethod.index"]//code//a[@href="trait.Index.html#associatedtype.Output"]' \ // "Output" fn index<'a>(&'a self, index: I) -> &'a Self::Output; } // @has assoc_types/fn.use_output.html // @has - '//*[@class="rust fn"]' '-> &T::Output' -// @has - '//*[@class="rust fn"]//a[@href="../assoc_types/trait.Index.html#associatedtype.Output"]' 'Output' +// @has - '//*[@class="rust fn"]//a[@href="trait.Index.html#associatedtype.Output"]' 'Output' pub fn use_output>(obj: &T, index: usize) -> &T::Output { obj.index(index) } @@ -26,12 +24,12 @@ pub trait Feed { // @has assoc_types/fn.use_input.html // @has - '//*[@class="rust fn"]' 'T::Input' -// @has - '//*[@class="rust fn"]//a[@href="../assoc_types/trait.Feed.html#associatedtype.Input"]' 'Input' +// @has - '//*[@class="rust fn"]//a[@href="trait.Feed.html#associatedtype.Input"]' 'Input' pub fn use_input(_feed: &T, _element: T::Input) { } // @has assoc_types/fn.cmp_input.html // @has - '//*[@class="rust fn"]' 'where T::Input: PartialEq' -// @has - '//*[@class="rust fn"]//a[@href="../assoc_types/trait.Feed.html#associatedtype.Input"]' 'Input' +// @has - '//*[@class="rust fn"]//a[@href="trait.Feed.html#associatedtype.Input"]' 'Input' pub fn cmp_input(a: &T::Input, b: &U::Input) -> bool where T::Input: PartialEq { diff --git a/src/test/rustdoc/async-fn.rs b/src/test/rustdoc/async-fn.rs index aa4ad261c80ff..4b66b5271c5ba 100644 --- a/src/test/rustdoc/async-fn.rs +++ b/src/test/rustdoc/async-fn.rs @@ -1,4 +1,3 @@ -// ignore-tidy-linelength // edition:2018 // @has async_fn/fn.foo.html '//pre[@class="rust fn"]' 'pub async fn foo() -> Option' pub async fn foo() -> Option { diff --git a/src/test/rustdoc/attributes.rs b/src/test/rustdoc/attributes.rs index 54c5939f908d4..51cd4a6cbfd12 100644 --- a/src/test/rustdoc/attributes.rs +++ b/src/test/rustdoc/attributes.rs @@ -1,21 +1,21 @@ #![crate_name = "foo"] -// @has foo/fn.f.html '//*[@class="docblock attributes"]' '#[no_mangle]' +// @has foo/fn.f.html '//*[@class="rust fn"]' '#[no_mangle]' #[no_mangle] pub extern "C" fn f() {} -// @has foo/fn.g.html '//*[@class="docblock attributes"]' '#[export_name = "bar"]' +// @has foo/fn.g.html '//*[@class="rust fn"]' '#[export_name = "bar"]' #[export_name = "bar"] pub extern "C" fn g() {} -// @matches foo/enum.Foo.html '//*[@class="docblock attributes top-attr"]' \ -// '(?m)\A#\[repr\(i64\)\]\n#\[must_use\]\Z' +// @matches foo/enum.Foo.html '//*[@class="rust enum"]' \ +// '#\[repr\(i64\)\]\n#\[must_use\]' #[repr(i64)] #[must_use] pub enum Foo { Bar, } -// @has foo/struct.Repr.html '//*[@class="docblock attributes top-attr"]' '#[repr(C, align(8))]' +// @has foo/struct.Repr.html '//*[@class="docblock type-decl"]' '#[repr(C, align(8))]' #[repr(C, align(8))] pub struct Repr; diff --git a/src/test/rustdoc/auxiliary/issue-73061.rs b/src/test/rustdoc/auxiliary/issue-73061.rs index e05a3bc6d9180..9b4129e771f87 100644 --- a/src/test/rustdoc/auxiliary/issue-73061.rs +++ b/src/test/rustdoc/auxiliary/issue-73061.rs @@ -1,6 +1,6 @@ //edition:2018 -#![feature(type_alias_impl_trait)] +#![feature(min_type_alias_impl_trait)] pub trait Foo { type X: std::future::Future; diff --git a/src/test/rustdoc/auxiliary/primitive-doc.rs b/src/test/rustdoc/auxiliary/primitive-doc.rs new file mode 100644 index 0000000000000..a5b69740dd447 --- /dev/null +++ b/src/test/rustdoc/auxiliary/primitive-doc.rs @@ -0,0 +1,6 @@ +// compile-flags: --crate-type lib --edition 2018 + +#[doc(primitive = "usize")] +/// This is the built-in type `usize`. +mod usize { +} diff --git a/src/test/rustdoc/check-styled-link.rs b/src/test/rustdoc/check-styled-link.rs index b479820dfb2ec..ed4a5ea21374b 100644 --- a/src/test/rustdoc/check-styled-link.rs +++ b/src/test/rustdoc/check-styled-link.rs @@ -2,7 +2,7 @@ pub struct Foo; -// @has foo/struct.Bar.html '//a[@href="../foo/struct.Foo.html"]' 'Foo' +// @has foo/struct.Bar.html '//a[@href="struct.Foo.html"]' 'Foo' /// Code-styled reference to [`Foo`]. pub struct Bar; diff --git a/src/test/rustdoc/const-display.rs b/src/test/rustdoc/const-display.rs index 11ba68a388fba..fb5c8517f6cac 100644 --- a/src/test/rustdoc/const-display.rs +++ b/src/test/rustdoc/const-display.rs @@ -1,5 +1,3 @@ -// ignore-tidy-linelength - #![crate_name = "foo"] #![unstable(feature = "humans", diff --git a/src/test/rustdoc/const-generics/add-impl.rs b/src/test/rustdoc/const-generics/add-impl.rs index 85be89719ec39..77432ba153955 100644 --- a/src/test/rustdoc/const-generics/add-impl.rs +++ b/src/test/rustdoc/const-generics/add-impl.rs @@ -1,5 +1,3 @@ -// ignore-tidy-linelength - #![feature(const_generics)] #![crate_name = "foo"] @@ -10,7 +8,7 @@ pub struct Simd { inner: T, } -// @has foo/struct.Simd.html '//div[@id="trait-implementations-list"]/h3/code' 'impl Add> for Simd' +// @has foo/struct.Simd.html '//div[@id="trait-implementations-list"]//h3/code' 'impl Add> for Simd' impl Add for Simd { type Output = Self; diff --git a/src/test/rustdoc/const-generics/const-impl.rs b/src/test/rustdoc/const-generics/const-impl.rs index 03f5bb2ca4374..04fb339533339 100644 --- a/src/test/rustdoc/const-generics/const-impl.rs +++ b/src/test/rustdoc/const-generics/const-impl.rs @@ -1,5 +1,3 @@ -// ignore-tidy-linelength - #![feature(const_generics)] #![crate_name = "foo"] diff --git a/src/test/rustdoc/const-generics/type-alias.rs b/src/test/rustdoc/const-generics/type-alias.rs index 85160dc07a7aa..ebda5b1940455 100644 --- a/src/test/rustdoc/const-generics/type-alias.rs +++ b/src/test/rustdoc/const-generics/type-alias.rs @@ -1,4 +1,3 @@ -// ignore-tidy-linelength #![crate_name = "foo"] // @has foo/type.CellIndex.html '//pre[@class="rust typedef"]' 'type CellIndex = [i64; D];' diff --git a/src/test/rustdoc/cross-crate-primitive-doc.rs b/src/test/rustdoc/cross-crate-primitive-doc.rs new file mode 100644 index 0000000000000..05376e4680ec4 --- /dev/null +++ b/src/test/rustdoc/cross-crate-primitive-doc.rs @@ -0,0 +1,9 @@ +// aux-build:primitive-doc.rs +// compile-flags: --extern-html-root-url=primitive_doc=../ -Z unstable-options + +#![no_std] + +extern crate primitive_doc; + +// @has 'cross_crate_primitive_doc/fn.foo.html' '//a[@href="../primitive_doc/primitive.usize.html"]' 'usize' +pub fn foo() -> usize { 0 } diff --git a/src/test/rustdoc/decl_macro.rs b/src/test/rustdoc/decl_macro.rs index e48a56f906c95..ede3f455a2044 100644 --- a/src/test/rustdoc/decl_macro.rs +++ b/src/test/rustdoc/decl_macro.rs @@ -1,3 +1,5 @@ +// compile-flags: --document-private-items + #![feature(decl_macro)] // @has decl_macro/macro.my_macro.html //pre 'pub macro my_macro() {' @@ -37,3 +39,18 @@ pub macro my_macro_multi { pub macro by_example_single { ($foo:expr) => {} } + +mod a { + mod b { + // @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) {' + pub(in a) macro by_example_vis_named { + ($foo:expr) => {} + } + } + } +} diff --git a/src/test/rustdoc/default-trait-method-link.rs b/src/test/rustdoc/default-trait-method-link.rs index e4f0bdab16292..7bcd2a3c149e9 100644 --- a/src/test/rustdoc/default-trait-method-link.rs +++ b/src/test/rustdoc/default-trait-method-link.rs @@ -1,7 +1,7 @@ #![crate_name = "foo"] -// @has foo/trait.Foo.html '//a[@href="../foo/trait.Foo.html#tymethod.req"]' 'req' -// @has foo/trait.Foo.html '//a[@href="../foo/trait.Foo.html#method.prov"]' 'prov' +// @has foo/trait.Foo.html '//a[@href="trait.Foo.html#tymethod.req"]' 'req' +// @has foo/trait.Foo.html '//a[@href="trait.Foo.html#method.prov"]' 'prov' /// Always make sure to implement [`req`], but you don't have to implement [`prov`]. /// diff --git a/src/test/rustdoc/deref-recursive-pathbuf.rs b/src/test/rustdoc/deref-recursive-pathbuf.rs index 759e881aab415..459a30060c623 100644 --- a/src/test/rustdoc/deref-recursive-pathbuf.rs +++ b/src/test/rustdoc/deref-recursive-pathbuf.rs @@ -1,5 +1,3 @@ -// ignore-tidy-linelength - // #26207: Show all methods reachable via Deref impls, recursing through multiple dereferencing // levels and across multiple crates. diff --git a/src/test/rustdoc/deref-recursive.rs b/src/test/rustdoc/deref-recursive.rs index 5aef87c38cd27..b96b5397ad78b 100644 --- a/src/test/rustdoc/deref-recursive.rs +++ b/src/test/rustdoc/deref-recursive.rs @@ -1,5 +1,3 @@ -// ignore-tidy-linelength - // #26207: Show all methods reachable via Deref impls, recursing through multiple dereferencing // levels if needed. diff --git a/src/test/rustdoc/deref-typedef.rs b/src/test/rustdoc/deref-typedef.rs index 589f133b975a5..47009559e6f74 100644 --- a/src/test/rustdoc/deref-typedef.rs +++ b/src/test/rustdoc/deref-typedef.rs @@ -1,5 +1,3 @@ -// ignore-tidy-linelength - #![crate_name = "foo"] // @has 'foo/struct.Bar.html' diff --git a/src/test/rustdoc/doc-notable_trait.rs b/src/test/rustdoc/doc-notable_trait.rs new file mode 100644 index 0000000000000..58a24b855d6e2 --- /dev/null +++ b/src/test/rustdoc/doc-notable_trait.rs @@ -0,0 +1,36 @@ +#![feature(doc_notable_trait)] + +pub struct Wrapper { + inner: T, +} + +impl SomeTrait for Wrapper {} + +#[doc(notable_trait)] +pub trait SomeTrait { + // @has doc_notable_trait/trait.SomeTrait.html + // @has - '//code[@class="content"]' 'impl SomeTrait for Wrapper' + fn wrap_me(self) -> Wrapper where Self: Sized { + Wrapper { + inner: self, + } + } +} + +pub struct SomeStruct; +impl SomeTrait for SomeStruct {} + +impl SomeStruct { + // @has doc_notable_trait/struct.SomeStruct.html + // @has - '//code[@class="content"]' 'impl SomeTrait for SomeStruct' + // @has - '//code[@class="content"]' 'impl SomeTrait for Wrapper' + pub fn new() -> SomeStruct { + SomeStruct + } +} + +// @has doc_notable_trait/fn.bare_fn.html +// @has - '//code[@class="content"]' 'impl SomeTrait for SomeStruct' +pub fn bare_fn() -> SomeStruct { + SomeStruct +} diff --git a/src/test/rustdoc/doc-spotlight.rs b/src/test/rustdoc/doc-spotlight.rs deleted file mode 100644 index ddd46c3c2155f..0000000000000 --- a/src/test/rustdoc/doc-spotlight.rs +++ /dev/null @@ -1,36 +0,0 @@ -#![feature(doc_spotlight)] - -pub struct Wrapper { - inner: T, -} - -impl SomeTrait for Wrapper {} - -#[doc(spotlight)] -pub trait SomeTrait { - // @has doc_spotlight/trait.SomeTrait.html - // @has - '//code[@class="content"]' 'impl SomeTrait for Wrapper' - fn wrap_me(self) -> Wrapper where Self: Sized { - Wrapper { - inner: self, - } - } -} - -pub struct SomeStruct; -impl SomeTrait for SomeStruct {} - -impl SomeStruct { - // @has doc_spotlight/struct.SomeStruct.html - // @has - '//code[@class="content"]' 'impl SomeTrait for SomeStruct' - // @has - '//code[@class="content"]' 'impl SomeTrait for Wrapper' - pub fn new() -> SomeStruct { - SomeStruct - } -} - -// @has doc_spotlight/fn.bare_fn.html -// @has - '//code[@class="content"]' 'impl SomeTrait for SomeStruct' -pub fn bare_fn() -> SomeStruct { - SomeStruct -} diff --git a/src/test/rustdoc/double-quote-escape.rs b/src/test/rustdoc/double-quote-escape.rs index 243d8ad7965b8..546af2c121adb 100644 --- a/src/test/rustdoc/double-quote-escape.rs +++ b/src/test/rustdoc/double-quote-escape.rs @@ -1,6 +1,5 @@ #![crate_name = "foo"] -// ignore-tidy-linelength pub trait Foo { fn foo() {} diff --git a/src/test/rustdoc/duplicate-cfg.rs b/src/test/rustdoc/duplicate-cfg.rs index 7b938af3c7d50..cec504ea1517a 100644 --- a/src/test/rustdoc/duplicate-cfg.rs +++ b/src/test/rustdoc/duplicate-cfg.rs @@ -1,5 +1,3 @@ -// ignore-tidy-linelength - #![crate_name = "foo"] #![feature(doc_cfg)] diff --git a/src/test/rustdoc/duplicate_impls/issue-33054.rs b/src/test/rustdoc/duplicate_impls/issue-33054.rs index 112d632971a5f..1e644bb973987 100644 --- a/src/test/rustdoc/duplicate_impls/issue-33054.rs +++ b/src/test/rustdoc/duplicate_impls/issue-33054.rs @@ -1,8 +1,8 @@ // @has issue_33054/impls/struct.Foo.html // @has - '//code' 'impl Foo' // @has - '//code' 'impl Bar for Foo' -// @count - '//*[@id="trait-implementations-list"]/*[@class="impl"]' 1 -// @count - '//*[@id="main"]/*[@class="impl"]' 1 +// @count - '//*[@id="trait-implementations-list"]//*[@class="impl"]' 1 +// @count - '//*[@id="main"]/details/summary/*[@class="impl"]' 1 // @has issue_33054/impls/bar/trait.Bar.html // @has - '//code' 'impl Bar for Foo' // @count - '//*[@class="struct"]' 1 diff --git a/src/test/rustdoc/extern-links.rs b/src/test/rustdoc/extern-links.rs index 991f869138d93..0383ccf7db666 100644 --- a/src/test/rustdoc/extern-links.rs +++ b/src/test/rustdoc/extern-links.rs @@ -3,7 +3,7 @@ #![crate_name = "foo"] -extern crate extern_links; +pub extern crate extern_links; // @!has foo/index.html '//a' 'extern_links' #[doc(no_inline)] diff --git a/src/test/rustdoc/fn-type.rs b/src/test/rustdoc/fn-type.rs index f5e123aed9c47..3959aeb6cfb7f 100644 --- a/src/test/rustdoc/fn-type.rs +++ b/src/test/rustdoc/fn-type.rs @@ -1,5 +1,3 @@ -// ignore-tidy-linelength - #![crate_name = "foo"] #![crate_type = "lib"] diff --git a/src/test/rustdoc/for-lifetime.rs b/src/test/rustdoc/for-lifetime.rs index 299794b63b2ea..34a7eae31c790 100644 --- a/src/test/rustdoc/for-lifetime.rs +++ b/src/test/rustdoc/for-lifetime.rs @@ -1,5 +1,3 @@ -// ignore-tidy-linelength - #![crate_name = "foo"] #![crate_type = "lib"] diff --git a/src/test/rustdoc/impl-trait-alias.rs b/src/test/rustdoc/impl-trait-alias.rs index 54c3f856ddb3c..bf73a833580c5 100644 --- a/src/test/rustdoc/impl-trait-alias.rs +++ b/src/test/rustdoc/impl-trait-alias.rs @@ -1,4 +1,4 @@ -#![feature(type_alias_impl_trait)] +#![feature(min_type_alias_impl_trait)] trait MyTrait {} impl MyTrait for i32 {} diff --git a/src/test/rustdoc/inline_cross/impl_trait.rs b/src/test/rustdoc/inline_cross/impl_trait.rs index 0ab2fa99f877f..a2adc0e63c9c0 100644 --- a/src/test/rustdoc/inline_cross/impl_trait.rs +++ b/src/test/rustdoc/inline_cross/impl_trait.rs @@ -1,6 +1,5 @@ // aux-build:impl_trait_aux.rs // edition:2018 -// ignore-tidy-linelength extern crate impl_trait_aux; @@ -18,7 +17,7 @@ pub use impl_trait_aux::func2; // @has impl_trait/fn.func3.html // @has - '//pre[@class="rust fn"]' "func3(" -// @has - '//pre[@class="rust fn"]' "_x: impl Clone + Iterator>)" +// @has - '//pre[@class="rust fn"]' "_x: impl Iterator> + Clone)" // @!has - '//pre[@class="rust fn"]' 'where' pub use impl_trait_aux::func3; diff --git a/src/test/rustdoc/intra-doc-crate/self.rs b/src/test/rustdoc/intra-doc-crate/self.rs index 4db63b12b6bb7..8c36a7fa002d8 100644 --- a/src/test/rustdoc/intra-doc-crate/self.rs +++ b/src/test/rustdoc/intra-doc-crate/self.rs @@ -3,7 +3,7 @@ extern crate cross_crate_self; -// @has self/struct.S.html '//a[@href="../self/struct.S.html#method.f"]' "Self::f" -// @has self/struct.S.html '//a[@href="../self/struct.S.html"]' "Self" +// @has self/struct.S.html '//a[@href="struct.S.html#method.f"]' "Self::f" +// @has self/struct.S.html '//a[@href="struct.S.html"]' "Self" // @has self/struct.S.html '//a[@href="../cross_crate_self/index.html"]' "crate" pub use cross_crate_self::S; diff --git a/src/test/rustdoc/intra-doc/anchors.rs b/src/test/rustdoc/intra-doc/anchors.rs index e4f0c737bdd33..8ec1a7b4f9056 100644 --- a/src/test/rustdoc/intra-doc/anchors.rs +++ b/src/test/rustdoc/intra-doc/anchors.rs @@ -4,7 +4,7 @@ pub struct Something; // @has anchors/struct.SomeOtherType.html -// @has - '//a/@href' '../anchors/struct.Something.html#Anchor!' +// @has - '//a/@href' 'struct.Something.html#Anchor!' /// I want... /// diff --git a/src/test/rustdoc/intra-doc/associated-defaults.rs b/src/test/rustdoc/intra-doc/associated-defaults.rs index f99d9b5baea44..68647127fe880 100644 --- a/src/test/rustdoc/intra-doc/associated-defaults.rs +++ b/src/test/rustdoc/intra-doc/associated-defaults.rs @@ -1,4 +1,3 @@ -// ignore-tidy-linelength #![deny(intra_doc_link_resolution_failure)] #![feature(associated_type_defaults)] @@ -10,14 +9,14 @@ pub trait TraitWithDefault { } /// Link to [UsesDefaults::T] and [UsesDefaults::f] -// @has 'associated_defaults/struct.UsesDefaults.html' '//a[@href="../associated_defaults/struct.UsesDefaults.html#associatedtype.T"]' 'UsesDefaults::T' -// @has 'associated_defaults/struct.UsesDefaults.html' '//a[@href="../associated_defaults/struct.UsesDefaults.html#method.f"]' 'UsesDefaults::f' +// @has 'associated_defaults/struct.UsesDefaults.html' '//a[@href="struct.UsesDefaults.html#associatedtype.T"]' 'UsesDefaults::T' +// @has 'associated_defaults/struct.UsesDefaults.html' '//a[@href="struct.UsesDefaults.html#method.f"]' 'UsesDefaults::f' pub struct UsesDefaults; impl TraitWithDefault for UsesDefaults {} /// Link to [OverridesDefaults::T] and [OverridesDefaults::f] -// @has 'associated_defaults/struct.OverridesDefaults.html' '//a[@href="../associated_defaults/struct.OverridesDefaults.html#associatedtype.T"]' 'OverridesDefaults::T' -// @has 'associated_defaults/struct.OverridesDefaults.html' '//a[@href="../associated_defaults/struct.OverridesDefaults.html#method.f"]' 'OverridesDefaults::f' +// @has 'associated_defaults/struct.OverridesDefaults.html' '//a[@href="struct.OverridesDefaults.html#associatedtype.T"]' 'OverridesDefaults::T' +// @has 'associated_defaults/struct.OverridesDefaults.html' '//a[@href="struct.OverridesDefaults.html#method.f"]' 'OverridesDefaults::f' pub struct OverridesDefaults; impl TraitWithDefault for OverridesDefaults { type T = bool; diff --git a/src/test/rustdoc/intra-doc/associated-items.rs b/src/test/rustdoc/intra-doc/associated-items.rs index 09dfbfcf68a32..2757418bc64e5 100644 --- a/src/test/rustdoc/intra-doc/associated-items.rs +++ b/src/test/rustdoc/intra-doc/associated-items.rs @@ -1,4 +1,3 @@ -// ignore-tidy-linelength #![deny(intra_doc_link_resolution_failure)] /// [`std::collections::BTreeMap::into_iter`] @@ -10,10 +9,10 @@ pub fn foo() {} /// Link to [MyStruct], [link from struct][MyStruct::method], [MyStruct::clone], [MyStruct::Input] -// @has 'associated_items/struct.MyStruct.html' '//a[@href="../associated_items/struct.MyStruct.html"]' 'MyStruct' -// @has 'associated_items/struct.MyStruct.html' '//a[@href="../associated_items/struct.MyStruct.html#method.method"]' 'link from struct' -// @has 'associated_items/struct.MyStruct.html' '//a[@href="../associated_items/struct.MyStruct.html#method.clone"]' 'MyStruct::clone' -// @has 'associated_items/struct.MyStruct.html' '//a[@href="../associated_items/struct.MyStruct.html#associatedtype.Input"]' 'MyStruct::Input' +// @has 'associated_items/struct.MyStruct.html' '//a[@href="struct.MyStruct.html"]' 'MyStruct' +// @has 'associated_items/struct.MyStruct.html' '//a[@href="struct.MyStruct.html#method.method"]' 'link from struct' +// @has 'associated_items/struct.MyStruct.html' '//a[@href="struct.MyStruct.html#method.clone"]' 'MyStruct::clone' +// @has 'associated_items/struct.MyStruct.html' '//a[@href="struct.MyStruct.html#associatedtype.Input"]' 'MyStruct::Input' pub struct MyStruct { foo: () } impl Clone for MyStruct { @@ -31,7 +30,7 @@ impl T for MyStruct { type Input = usize; /// [link from method][MyStruct::method] on method - // @has 'associated_items/struct.MyStruct.html' '//a[@href="../associated_items/struct.MyStruct.html#method.method"]' 'link from method' + // @has 'associated_items/struct.MyStruct.html' '//a[@href="struct.MyStruct.html#method.method"]' 'link from method' fn method(i: usize) { } } diff --git a/src/test/rustdoc/intra-doc/auxiliary/empty.rs b/src/test/rustdoc/intra-doc/auxiliary/empty.rs new file mode 100644 index 0000000000000..d11c69f812a8d --- /dev/null +++ b/src/test/rustdoc/intra-doc/auxiliary/empty.rs @@ -0,0 +1 @@ +// intentionally empty diff --git a/src/test/rustdoc/intra-doc/auxiliary/empty2.rs b/src/test/rustdoc/intra-doc/auxiliary/empty2.rs new file mode 100644 index 0000000000000..d11c69f812a8d --- /dev/null +++ b/src/test/rustdoc/intra-doc/auxiliary/empty2.rs @@ -0,0 +1 @@ +// intentionally empty diff --git a/src/test/rustdoc/auxiliary/issue-66159-1.rs b/src/test/rustdoc/intra-doc/auxiliary/issue-66159-1.rs similarity index 100% rename from src/test/rustdoc/auxiliary/issue-66159-1.rs rename to src/test/rustdoc/intra-doc/auxiliary/issue-66159-1.rs diff --git a/src/test/rustdoc/intra-doc/basic.rs b/src/test/rustdoc/intra-doc/basic.rs index 7760546e1fa1c..39f5c298bc4a1 100644 --- a/src/test/rustdoc/intra-doc/basic.rs +++ b/src/test/rustdoc/intra-doc/basic.rs @@ -1,21 +1,21 @@ // @has basic/index.html -// @has - '//a/@href' '../basic/struct.ThisType.html' -// @has - '//a/@href' '../basic/struct.ThisType.html#method.this_method' -// @has - '//a/@href' '../basic/enum.ThisEnum.html' -// @has - '//a/@href' '../basic/enum.ThisEnum.html#variant.ThisVariant' -// @has - '//a/@href' '../basic/trait.ThisTrait.html' -// @has - '//a/@href' '../basic/trait.ThisTrait.html#tymethod.this_associated_method' -// @has - '//a/@href' '../basic/trait.ThisTrait.html#associatedtype.ThisAssociatedType' -// @has - '//a/@href' '../basic/trait.ThisTrait.html#associatedconstant.THIS_ASSOCIATED_CONST' -// @has - '//a/@href' '../basic/trait.ThisTrait.html' -// @has - '//a/@href' '../basic/type.ThisAlias.html' -// @has - '//a/@href' '../basic/union.ThisUnion.html' -// @has - '//a/@href' '../basic/fn.this_function.html' -// @has - '//a/@href' '../basic/constant.THIS_CONST.html' -// @has - '//a/@href' '../basic/static.THIS_STATIC.html' -// @has - '//a/@href' '../basic/macro.this_macro.html' -// @has - '//a/@href' '../basic/trait.SoAmbiguous.html' -// @has - '//a/@href' '../basic/fn.SoAmbiguous.html' +// @has - '//a/@href' 'struct.ThisType.html' +// @has - '//a/@href' 'struct.ThisType.html#method.this_method' +// @has - '//a/@href' 'enum.ThisEnum.html' +// @has - '//a/@href' 'enum.ThisEnum.html#variant.ThisVariant' +// @has - '//a/@href' 'trait.ThisTrait.html' +// @has - '//a/@href' 'trait.ThisTrait.html#tymethod.this_associated_method' +// @has - '//a/@href' 'trait.ThisTrait.html#associatedtype.ThisAssociatedType' +// @has - '//a/@href' 'trait.ThisTrait.html#associatedconstant.THIS_ASSOCIATED_CONST' +// @has - '//a/@href' 'trait.ThisTrait.html' +// @has - '//a/@href' 'type.ThisAlias.html' +// @has - '//a/@href' 'union.ThisUnion.html' +// @has - '//a/@href' 'fn.this_function.html' +// @has - '//a/@href' 'constant.THIS_CONST.html' +// @has - '//a/@href' 'static.THIS_STATIC.html' +// @has - '//a/@href' 'macro.this_macro.html' +// @has - '//a/@href' 'trait.SoAmbiguous.html' +// @has - '//a/@href' 'fn.SoAmbiguous.html' //! In this crate we would like to link to: //! //! * [`ThisType`](ThisType) @@ -46,7 +46,7 @@ macro_rules! this_macro { () => {}; } -// @has basic/struct.ThisType.html '//a/@href' '../basic/macro.this_macro.html' +// @has basic/struct.ThisType.html '//a/@href' 'macro.this_macro.html' /// another link to [`this_macro!()`] pub struct ThisType; @@ -72,10 +72,10 @@ pub trait SoAmbiguous {} pub fn SoAmbiguous() {} -// @has basic/struct.SomeOtherType.html '//a/@href' '../basic/struct.ThisType.html' -// @has - '//a/@href' '../basic/struct.ThisType.html#method.this_method' -// @has - '//a/@href' '../basic/enum.ThisEnum.html' -// @has - '//a/@href' '../basic/enum.ThisEnum.html#variant.ThisVariant' +// @has basic/struct.SomeOtherType.html '//a/@href' 'struct.ThisType.html' +// @has - '//a/@href' 'struct.ThisType.html#method.this_method' +// @has - '//a/@href' 'enum.ThisEnum.html' +// @has - '//a/@href' 'enum.ThisEnum.html#variant.ThisVariant' /// Shortcut links for: /// * [`ThisType`] /// * [`ThisType::this_method`] diff --git a/src/test/rustdoc/intra-doc/cross-crate/additional_doc.rs b/src/test/rustdoc/intra-doc/cross-crate/additional_doc.rs index 837390b3c7161..85c5866ca7ecf 100644 --- a/src/test/rustdoc/intra-doc/cross-crate/additional_doc.rs +++ b/src/test/rustdoc/intra-doc/cross-crate/additional_doc.rs @@ -4,7 +4,7 @@ extern crate my_rand; -// @has 'additional_doc/trait.Rng.html' '//a[@href="../additional_doc/trait.Rng.html"]' 'Rng' +// @has 'additional_doc/trait.Rng.html' '//a[@href="trait.Rng.html"]' 'Rng' // @has 'additional_doc/trait.Rng.html' '//a[@href="../my_rand/trait.RngCore.html"]' 'RngCore' /// This is an [`Rng`]. pub use my_rand::Rng; diff --git a/src/test/rustdoc/intra-doc/cross-crate/hidden.rs b/src/test/rustdoc/intra-doc/cross-crate/hidden.rs index 9c9d4c649455e..31337f20f18dc 100644 --- a/src/test/rustdoc/intra-doc/cross-crate/hidden.rs +++ b/src/test/rustdoc/intra-doc/cross-crate/hidden.rs @@ -6,5 +6,5 @@ extern crate hidden_dep; -// @has 'hidden/struct.Ready.html' '//a/@href' '../hidden/fn.ready.html' +// @has 'hidden/struct.Ready.html' '//a/@href' 'fn.ready.html' pub use hidden_dep::future::{ready, Ready}; diff --git a/src/test/rustdoc/intra-doc/cross-crate/macro.rs b/src/test/rustdoc/intra-doc/cross-crate/macro.rs index 311b16dff13bc..62659ce689a71 100644 --- a/src/test/rustdoc/intra-doc/cross-crate/macro.rs +++ b/src/test/rustdoc/intra-doc/cross-crate/macro.rs @@ -1,4 +1,3 @@ -// ignore-tidy-linelength // aux-build:macro_inner.rs // aux-build:proc_macro.rs // build-aux-docs diff --git a/src/test/rustdoc/intra-doc/cross-crate/submodule-outer.rs b/src/test/rustdoc/intra-doc/cross-crate/submodule-outer.rs index 45f561328f279..db7952b5aced0 100644 --- a/src/test/rustdoc/intra-doc/cross-crate/submodule-outer.rs +++ b/src/test/rustdoc/intra-doc/cross-crate/submodule-outer.rs @@ -11,6 +11,6 @@ pub mod bar { // NOTE: we re-exported both `Foo` and `Bar` here, // NOTE: so they are inlined and therefore we link to the current module. -// @has 'submodule_outer/trait.Foo.html' '//a[@href="../submodule_outer/bar/trait.Bar.html"]' 'Bar' -// @has 'submodule_outer/trait.Foo.html' '//a[@href="../submodule_outer/trait.Baz.html"]' 'Baz' +// @has 'submodule_outer/trait.Foo.html' '//a[@href="bar/trait.Bar.html"]' 'Bar' +// @has 'submodule_outer/trait.Foo.html' '//a[@href="trait.Baz.html"]' 'Baz' pub use ::bar_::{Foo, Baz}; diff --git a/src/test/rustdoc/intra-doc/cross-crate/traits.rs b/src/test/rustdoc/intra-doc/cross-crate/traits.rs index 07decb48019da..68f5cb3a092e4 100644 --- a/src/test/rustdoc/intra-doc/cross-crate/traits.rs +++ b/src/test/rustdoc/intra-doc/cross-crate/traits.rs @@ -1,6 +1,5 @@ // aux-build:traits.rs // build-aux-docs -// ignore-tidy-line-length #![deny(broken_intra_doc_links)] extern crate inner; diff --git a/src/test/rustdoc/intra-doc/disambiguators-removed.rs b/src/test/rustdoc/intra-doc/disambiguators-removed.rs index aa0ced62aaf30..d782c5cf5dc84 100644 --- a/src/test/rustdoc/intra-doc/disambiguators-removed.rs +++ b/src/test/rustdoc/intra-doc/disambiguators-removed.rs @@ -1,28 +1,27 @@ -// ignore-tidy-linelength #![deny(intra_doc_link_resolution_failure)] // first try backticks /// Trait: [`trait@Name`], fn: [`fn@Name`], [`Name`][`macro@Name`] // @has disambiguators_removed/struct.AtDisambiguator.html -// @has - '//a[@href="../disambiguators_removed/trait.Name.html"][code]' "Name" -// @has - '//a[@href="../disambiguators_removed/fn.Name.html"][code]' "Name" -// @has - '//a[@href="../disambiguators_removed/macro.Name.html"][code]' "Name" +// @has - '//a[@href="trait.Name.html"][code]' "Name" +// @has - '//a[@href="fn.Name.html"][code]' "Name" +// @has - '//a[@href="macro.Name.html"][code]' "Name" pub struct AtDisambiguator; /// fn: [`Name()`], macro: [`Name!`] // @has disambiguators_removed/struct.SymbolDisambiguator.html -// @has - '//a[@href="../disambiguators_removed/fn.Name.html"][code]' "Name()" -// @has - '//a[@href="../disambiguators_removed/macro.Name.html"][code]' "Name!" +// @has - '//a[@href="fn.Name.html"][code]' "Name()" +// @has - '//a[@href="macro.Name.html"][code]' "Name!" pub struct SymbolDisambiguator; // Now make sure that backticks aren't added if they weren't already there /// [fn@Name] // @has disambiguators_removed/trait.Name.html -// @has - '//a[@href="../disambiguators_removed/fn.Name.html"]' "Name" -// @!has - '//a[@href="../disambiguators_removed/fn.Name.html"][code]' "Name" +// @has - '//a[@href="fn.Name.html"]' "Name" +// @!has - '//a[@href="fn.Name.html"][code]' "Name" // FIXME: this will turn !() into ! alone /// [Name!()] -// @has - '//a[@href="../disambiguators_removed/macro.Name.html"]' "Name!" +// @has - '//a[@href="macro.Name.html"]' "Name!" pub trait Name {} #[allow(non_snake_case)] @@ -30,22 +29,22 @@ pub trait Name {} // Try collapsed reference links /// [macro@Name][] // @has disambiguators_removed/fn.Name.html -// @has - '//a[@href="../disambiguators_removed/macro.Name.html"]' "Name" +// @has - '//a[@href="macro.Name.html"]' "Name" // Try links that have the same text as a generated URL -/// Weird URL aligned [../disambiguators_removed/macro.Name.html][trait@Name] -// @has - '//a[@href="../disambiguators_removed/trait.Name.html"]' "../disambiguators_removed/macro.Name.html" +/// Weird URL aligned [macro.Name.html][trait@Name] +// @has - '//a[@href="trait.Name.html"]' "macro.Name.html" pub fn Name() {} #[macro_export] // Rustdoc doesn't currently handle links that have weird interspersing of inline code blocks. /// [fn@Na`m`e] // @has disambiguators_removed/macro.Name.html -// @has - '//a[@href="../disambiguators_removed/fn.Name.html"]' "fn@Name" +// @has - '//a[@href="fn.Name.html"]' "fn@Name" // It also doesn't handle any case where the code block isn't the whole link text: /// [trait@`Name`] -// @has - '//a[@href="../disambiguators_removed/trait.Name.html"]' "trait@Name" +// @has - '//a[@href="trait.Name.html"]' "trait@Name" macro_rules! Name { () => () } diff --git a/src/test/rustdoc/intra-doc/email-address.rs b/src/test/rustdoc/intra-doc/email-address.rs new file mode 100644 index 0000000000000..c407eb80da225 --- /dev/null +++ b/src/test/rustdoc/intra-doc/email-address.rs @@ -0,0 +1,6 @@ +//! Email me at . +//! Email me at . +//! Email me at (this warns but will still become a link). +// @has email_address/index.html '//a[@href="mailto:hello@example.com"]' 'hello@example.com' +// @has email_address/index.html '//a[@href="mailto:hello-world@example.com"]' 'hello-world@example.com' +// @has email_address/index.html '//a[@href="mailto:hello@localhost"]' 'hello@localhost' diff --git a/src/test/rustdoc/intra-doc/enum-struct-field.rs b/src/test/rustdoc/intra-doc/enum-struct-field.rs index 70bf343a9a5e5..2270a1fafa1cb 100644 --- a/src/test/rustdoc/intra-doc/enum-struct-field.rs +++ b/src/test/rustdoc/intra-doc/enum-struct-field.rs @@ -11,4 +11,4 @@ pub enum Foo { /// I want [Foo::X::y]. pub fn foo() {} -// @has foo/fn.foo.html '//a/@href' '../foo/enum.Foo.html#variant.X.field.y' +// @has foo/fn.foo.html '//a/@href' 'enum.Foo.html#variant.X.field.y' diff --git a/src/test/rustdoc/intra-doc/extern-crate-only-used-in-link.rs b/src/test/rustdoc/intra-doc/extern-crate-only-used-in-link.rs new file mode 100644 index 0000000000000..5d8dcf8bc1d16 --- /dev/null +++ b/src/test/rustdoc/intra-doc/extern-crate-only-used-in-link.rs @@ -0,0 +1,19 @@ +// This test is just a little cursed. +// aux-build:issue-66159-1.rs +// aux-crate:priv:issue_66159_1=issue-66159-1.rs +// aux-build:empty.rs +// aux-crate:priv:empty=empty.rs +// aux-build:empty2.rs +// aux-crate:priv:empty2=empty2.rs +// build-aux-docs +// compile-flags:-Z unstable-options --edition 2018 + +// @has extern_crate_only_used_in_link/index.html +// @has - '//a[@href="../issue_66159_1/struct.Something.html"]' 'issue_66159_1::Something' +//! [issue_66159_1::Something] + +// @has - '//a[@href="../empty/index.html"]' 'empty' +//! [`empty`] + +// @has - '//a[@href="../empty2/index.html"]' 'empty2' +//! [empty2] diff --git a/src/test/rustdoc/intra-doc/extern-type.rs b/src/test/rustdoc/intra-doc/extern-type.rs index e1934698d1fad..f37ae62dde1aa 100644 --- a/src/test/rustdoc/intra-doc/extern-type.rs +++ b/src/test/rustdoc/intra-doc/extern-type.rs @@ -12,6 +12,6 @@ impl ExternType { // @has 'extern_type/foreigntype.ExternType.html' // @has 'extern_type/fn.links_to_extern_type.html' \ -// 'href="../extern_type/foreigntype.ExternType.html#method.f"' +// 'href="foreigntype.ExternType.html#method.f"' /// See also [ExternType::f] pub fn links_to_extern_type() {} diff --git a/src/test/rustdoc/intra-doc/issue-82209.rs b/src/test/rustdoc/intra-doc/issue-82209.rs index 76618cdce4cd2..68a5672a8d209 100644 --- a/src/test/rustdoc/intra-doc/issue-82209.rs +++ b/src/test/rustdoc/intra-doc/issue-82209.rs @@ -8,4 +8,4 @@ pub enum Foo { }, } -// @has foo/enum.Foo.html '//a/@href' '../foo/enum.Foo.html#variant.Bar.field.abc' +// @has foo/enum.Foo.html '//a/@href' 'enum.Foo.html#variant.Bar.field.abc' diff --git a/src/test/rustdoc/intra-doc/mod-ambiguity.rs b/src/test/rustdoc/intra-doc/mod-ambiguity.rs index feb013b22be65..24b9dc30a9e49 100644 --- a/src/test/rustdoc/intra-doc/mod-ambiguity.rs +++ b/src/test/rustdoc/intra-doc/mod-ambiguity.rs @@ -6,11 +6,11 @@ pub fn foo() { } pub mod foo {} -// @has mod_ambiguity/struct.A.html '//a/@href' '../mod_ambiguity/foo/index.html' +// @has mod_ambiguity/struct.A.html '//a/@href' 'foo/index.html' /// Module is [`module@foo`] pub struct A; -// @has mod_ambiguity/struct.B.html '//a/@href' '../mod_ambiguity/fn.foo.html' +// @has mod_ambiguity/struct.B.html '//a/@href' 'fn.foo.html' /// Function is [`fn@foo`] pub struct B; diff --git a/src/test/rustdoc/intra-doc/non-path-primitives.rs b/src/test/rustdoc/intra-doc/non-path-primitives.rs index ffa02b0c635b3..ee71537d15531 100644 --- a/src/test/rustdoc/intra-doc/non-path-primitives.rs +++ b/src/test/rustdoc/intra-doc/non-path-primitives.rs @@ -1,4 +1,3 @@ -// ignore-tidy-linelength #![crate_name = "foo"] #![feature(intra_doc_pointers)] #![deny(rustdoc::broken_intra_doc_links)] diff --git a/src/test/rustdoc/intra-doc/prim-assoc.rs b/src/test/rustdoc/intra-doc/prim-assoc.rs index d687cbd69bb18..4099ececfaf7c 100644 --- a/src/test/rustdoc/intra-doc/prim-assoc.rs +++ b/src/test/rustdoc/intra-doc/prim-assoc.rs @@ -1,4 +1,3 @@ -// ignore-tidy-linelength #![deny(broken_intra_doc_links)] //! [i32::MAX] diff --git a/src/test/rustdoc/intra-doc/prim-methods-external-core.rs b/src/test/rustdoc/intra-doc/prim-methods-external-core.rs index 434e03389835f..695a7fbfb5348 100644 --- a/src/test/rustdoc/intra-doc/prim-methods-external-core.rs +++ b/src/test/rustdoc/intra-doc/prim-methods-external-core.rs @@ -2,7 +2,6 @@ // build-aux-docs // ignore-cross-compile // ignore-windows -// ignore-tidy-linelength #![deny(broken_intra_doc_links)] #![feature(no_core, lang_items)] diff --git a/src/test/rustdoc/intra-doc/prim-methods-local.rs b/src/test/rustdoc/intra-doc/prim-methods-local.rs index 9888f29db5bac..f0b939a468c03 100644 --- a/src/test/rustdoc/intra-doc/prim-methods-local.rs +++ b/src/test/rustdoc/intra-doc/prim-methods-local.rs @@ -3,7 +3,6 @@ #![no_core] #![crate_type = "rlib"] -// ignore-tidy-linelength // @has prim_methods_local/index.html // @has - '//*[@id="main"]//a[@href="https://doc.rust-lang.org/nightly/std/primitive.char.html"]' 'char' diff --git a/src/test/rustdoc/intra-doc/prim-methods.rs b/src/test/rustdoc/intra-doc/prim-methods.rs index f19cff7d34afa..6de15e76d15cf 100644 --- a/src/test/rustdoc/intra-doc/prim-methods.rs +++ b/src/test/rustdoc/intra-doc/prim-methods.rs @@ -1,6 +1,5 @@ #![deny(broken_intra_doc_links)] -// ignore-tidy-linelength // @has prim_methods/index.html // @has - '//*[@id="main"]//a[@href="https://doc.rust-lang.org/nightly/std/primitive.char.html"]' 'char' diff --git a/src/test/rustdoc/intra-doc/prim-precedence.rs b/src/test/rustdoc/intra-doc/prim-precedence.rs index ed2c2cda7184d..478b40b0b516f 100644 --- a/src/test/rustdoc/intra-doc/prim-precedence.rs +++ b/src/test/rustdoc/intra-doc/prim-precedence.rs @@ -1,4 +1,3 @@ -// ignore-tidy-linelength #![deny(broken_intra_doc_links)] pub mod char { @@ -12,6 +11,6 @@ pub mod char { pub struct MyString; /// See also [crate::char] and [mod@char] -// @has prim_precedence/struct.MyString2.html '//*[@href="../prim_precedence/char/index.html"]' 'crate::char' -// @has - '//*[@href="../prim_precedence/char/index.html"]' 'mod@char' +// @has prim_precedence/struct.MyString2.html '//*[@href="char/index.html"]' 'crate::char' +// @has - '//*[@href="char/index.html"]' 'mod@char' pub struct MyString2; diff --git a/src/test/rustdoc/intra-doc/primitive-non-default-impl.rs b/src/test/rustdoc/intra-doc/primitive-non-default-impl.rs index 548eb090a3276..cf83ead4db72c 100644 --- a/src/test/rustdoc/intra-doc/primitive-non-default-impl.rs +++ b/src/test/rustdoc/intra-doc/primitive-non-default-impl.rs @@ -1,6 +1,5 @@ #![deny(broken_intra_doc_links)] -// ignore-tidy-linelength // @has primitive_non_default_impl/fn.str_methods.html /// [`str::trim`] diff --git a/src/test/rustdoc/intra-doc/private.rs b/src/test/rustdoc/intra-doc/private.rs index f86ca44403d93..2756a7998e8ea 100644 --- a/src/test/rustdoc/intra-doc/private.rs +++ b/src/test/rustdoc/intra-doc/private.rs @@ -1,6 +1,17 @@ #![crate_name = "private"] // compile-flags: --document-private-items -/// docs [DontDocMe] -// @has private/struct.DocMe.html '//*a[@href="../private/struct.DontDocMe.html"]' 'DontDocMe' + +// make sure to update `rustdoc-ui/intra-doc/private.rs` if you update this file + +/// docs [DontDocMe] [DontDocMe::f] [DontDocMe::x] +// @has private/struct.DocMe.html '//*a[@href="struct.DontDocMe.html"]' 'DontDocMe' +// @has private/struct.DocMe.html '//*a[@href="struct.DontDocMe.html#method.f"]' 'DontDocMe::f' +// @has private/struct.DocMe.html '//*a[@href="struct.DontDocMe.html#structfield.x"]' 'DontDocMe::x' pub struct DocMe; -struct DontDocMe; +struct DontDocMe { + x: usize, +} + +impl DontDocMe { + fn f() {} +} diff --git a/src/test/rustdoc/intra-doc/proc-macro.rs b/src/test/rustdoc/intra-doc/proc-macro.rs index ab4626ccfc3ab..fce10a130be65 100644 --- a/src/test/rustdoc/intra-doc/proc-macro.rs +++ b/src/test/rustdoc/intra-doc/proc-macro.rs @@ -9,17 +9,17 @@ pub use proc_macro_macro::{DeriveA, attr_a}; use proc_macro_macro::{DeriveB, attr_b}; // @has proc_macro/struct.Foo.html -// @has - '//a/@href' '../proc_macro/derive.DeriveA.html' -// @has - '//a/@href' '../proc_macro/attr.attr_a.html' -// @has - '//a/@href' '../proc_macro/trait.DeriveTrait.html' +// @has - '//a/@href' 'derive.DeriveA.html' +// @has - '//a/@href' 'attr.attr_a.html' +// @has - '//a/@href' 'trait.DeriveTrait.html' // @has - '//a/@href' '../proc_macro_macro/derive.DeriveB.html' // @has - '//a/@href' '../proc_macro_macro/attr.attr_b.html' /// Link to [DeriveA], [attr_a], [DeriveB], [attr_b], [DeriveTrait] pub struct Foo; // @has proc_macro/struct.Bar.html -// @has - '//a/@href' '../proc_macro/derive.DeriveA.html' -// @has - '//a/@href' '../proc_macro/attr.attr_a.html' +// @has - '//a/@href' 'derive.DeriveA.html' +// @has - '//a/@href' 'attr.attr_a.html' /// Link to [deriveA](derive@DeriveA) [attr](macro@attr_a) pub struct Bar; diff --git a/src/test/rustdoc/intra-doc/pub-use.rs b/src/test/rustdoc/intra-doc/pub-use.rs index dd52249abc6d0..579fa68cee8be 100644 --- a/src/test/rustdoc/intra-doc/pub-use.rs +++ b/src/test/rustdoc/intra-doc/pub-use.rs @@ -13,7 +13,7 @@ extern crate inner; // @has outer/index.html // @ has - '//a[@href="https://doc.rust-lang.org/nightly/std/env/fn.var.html"]' "std::env" -// @ has - '//a[@href="../outer/fn.f.html"]' "g" +// @ has - '//a[@href="fn.f.html"]' "g" pub use f as g; // FIXME: same as above diff --git a/src/test/rustdoc/intra-doc/raw-ident-self.rs b/src/test/rustdoc/intra-doc/raw-ident-self.rs index d289797f14652..177c3016fb151 100644 --- a/src/test/rustdoc/intra-doc/raw-ident-self.rs +++ b/src/test/rustdoc/intra-doc/raw-ident-self.rs @@ -5,7 +5,7 @@ pub mod r#impl { impl S { /// See [Self::b]. // @has raw_ident_self/impl/struct.S.html - // @has - '//a[@href="../../raw_ident_self/impl/struct.S.html#method.b"]' 'Self::b' + // @has - '//a[@href="struct.S.html#method.b"]' 'Self::b' pub fn a() {} pub fn b() {} diff --git a/src/test/rustdoc/intra-doc/reexport-additional-docs.rs b/src/test/rustdoc/intra-doc/reexport-additional-docs.rs index 96f3580f3055e..64683bacd6513 100644 --- a/src/test/rustdoc/intra-doc/reexport-additional-docs.rs +++ b/src/test/rustdoc/intra-doc/reexport-additional-docs.rs @@ -3,13 +3,13 @@ #![crate_name = "foo"] extern crate inner; -// @has foo/struct.Inner.html '//a[@href="../foo/fn.with_code.html"]' 'crate::with_code' +// @has foo/struct.Inner.html '//a[@href="fn.with_code.html"]' 'crate::with_code' /// [crate::with_code] -// @has - '//a[@href="../foo/fn.with_code.html"]' 'different text' +// @has - '//a[@href="fn.with_code.html"]' 'different text' /// [different text][with_code] -// @has - '//a[@href="../foo/fn.me_too.html"]' 'me_too' +// @has - '//a[@href="fn.me_too.html"]' 'me_too' #[doc = "[me_too]"] -// @has - '//a[@href="../foo/fn.me_three.html"]' 'reference link' +// @has - '//a[@href="fn.me_three.html"]' 'reference link' /// This [reference link] #[doc = "has an attr in the way"] /// diff --git a/src/test/rustdoc/intra-doc/self.rs b/src/test/rustdoc/intra-doc/self.rs index 81545fec7411b..0ba7df8a78ad7 100644 --- a/src/test/rustdoc/intra-doc/self.rs +++ b/src/test/rustdoc/intra-doc/self.rs @@ -1,9 +1,8 @@ #![crate_name = "foo"] -// ignore-tidy-linelength -// @has foo/index.html '//a/@href' '../foo/struct.Foo.html#method.new' -// @has foo/struct.Foo.html '//a/@href' '../foo/struct.Foo.html#method.new' +// @has foo/index.html '//a/@href' 'struct.Foo.html#method.new' +// @has foo/struct.Foo.html '//a/@href' 'struct.Foo.html#method.new' /// Use [`new`] to create a new instance. /// @@ -16,8 +15,8 @@ impl Foo { } } -// @has foo/index.html '//a/@href' '../foo/struct.Bar.html#method.new2' -// @has foo/struct.Bar.html '//a/@href' '../foo/struct.Bar.html#method.new2' +// @has foo/index.html '//a/@href' 'struct.Bar.html#method.new2' +// @has foo/struct.Bar.html '//a/@href' 'struct.Bar.html#method.new2' /// Use [`new2`] to create a new instance. /// @@ -31,7 +30,7 @@ impl Bar { } pub struct MyStruct { - // @has foo/struct.MyStruct.html '//a/@href' '../foo/struct.MyStruct.html#structfield.struct_field' + // @has foo/struct.MyStruct.html '//a/@href' 'struct.MyStruct.html#structfield.struct_field' /// [`struct_field`] /// @@ -40,7 +39,7 @@ pub struct MyStruct { } pub enum MyEnum { - // @has foo/enum.MyEnum.html '//a/@href' '../foo/enum.MyEnum.html#variant.EnumVariant' + // @has foo/enum.MyEnum.html '//a/@href' 'enum.MyEnum.html#variant.EnumVariant' /// [`EnumVariant`] /// @@ -49,7 +48,7 @@ pub enum MyEnum { } pub union MyUnion { - // @has foo/union.MyUnion.html '//a/@href' '../foo/union.MyUnion.html#structfield.union_field' + // @has foo/union.MyUnion.html '//a/@href' 'union.MyUnion.html#structfield.union_field' /// [`union_field`] /// @@ -58,21 +57,21 @@ pub union MyUnion { } pub trait MyTrait { - // @has foo/trait.MyTrait.html '//a/@href' '../foo/trait.MyTrait.html#associatedtype.AssoType' + // @has foo/trait.MyTrait.html '//a/@href' 'trait.MyTrait.html#associatedtype.AssoType' /// [`AssoType`] /// /// [`AssoType`]: Self::AssoType type AssoType; - // @has foo/trait.MyTrait.html '//a/@href' '../foo/trait.MyTrait.html#associatedconstant.ASSO_CONST' + // @has foo/trait.MyTrait.html '//a/@href' 'trait.MyTrait.html#associatedconstant.ASSO_CONST' /// [`ASSO_CONST`] /// /// [`ASSO_CONST`]: Self::ASSO_CONST const ASSO_CONST: i32 = 1; - // @has foo/trait.MyTrait.html '//a/@href' '../foo/trait.MyTrait.html#method.asso_fn' + // @has foo/trait.MyTrait.html '//a/@href' 'trait.MyTrait.html#method.asso_fn' /// [`asso_fn`] /// @@ -81,7 +80,7 @@ pub trait MyTrait { } impl MyStruct { - // @has foo/struct.MyStruct.html '//a/@href' '../foo/struct.MyStruct.html#method.for_impl' + // @has foo/struct.MyStruct.html '//a/@href' 'struct.MyStruct.html#method.for_impl' /// [`for_impl`] /// @@ -92,21 +91,21 @@ impl MyStruct { } impl MyTrait for MyStruct { - // @has foo/struct.MyStruct.html '//a/@href' '../foo/struct.MyStruct.html#associatedtype.AssoType' + // @has foo/struct.MyStruct.html '//a/@href' 'struct.MyStruct.html#associatedtype.AssoType' /// [`AssoType`] /// /// [`AssoType`]: Self::AssoType type AssoType = u32; - // @has foo/struct.MyStruct.html '//a/@href' '../foo/struct.MyStruct.html#associatedconstant.ASSO_CONST' + // @has foo/struct.MyStruct.html '//a/@href' 'struct.MyStruct.html#associatedconstant.ASSO_CONST' /// [`ASSO_CONST`] /// /// [`ASSO_CONST`]: Self::ASSO_CONST const ASSO_CONST: i32 = 10; - // @has foo/struct.MyStruct.html '//a/@href' '../foo/struct.MyStruct.html#method.asso_fn' + // @has foo/struct.MyStruct.html '//a/@href' 'struct.MyStruct.html#method.asso_fn' /// [`asso_fn`] /// diff --git a/src/test/rustdoc/intra-doc/trait-impl.rs b/src/test/rustdoc/intra-doc/trait-impl.rs index fab8406d525e5..cf60dc1dbd50e 100644 --- a/src/test/rustdoc/intra-doc/trait-impl.rs +++ b/src/test/rustdoc/intra-doc/trait-impl.rs @@ -1,26 +1,25 @@ #![crate_name = "foo"] -// ignore-tidy-linelength pub struct MyStruct; impl MyTrait for MyStruct { -// @has foo/struct.MyStruct.html '//a/@href' '../foo/struct.MyStruct.html#associatedtype.AssoType' +// @has foo/struct.MyStruct.html '//a/@href' 'struct.MyStruct.html#associatedtype.AssoType' /// [`AssoType`] /// /// [`AssoType`]: MyStruct::AssoType type AssoType = u32; -// @has foo/struct.MyStruct.html '//a/@href' '../foo/struct.MyStruct.html#associatedconstant.ASSO_CONST' +// @has foo/struct.MyStruct.html '//a/@href' 'struct.MyStruct.html#associatedconstant.ASSO_CONST' /// [`ASSO_CONST`] /// /// [`ASSO_CONST`]: MyStruct::ASSO_CONST const ASSO_CONST: i32 = 10; -// @has foo/struct.MyStruct.html '//a/@href' '../foo/struct.MyStruct.html#method.trait_fn' +// @has foo/struct.MyStruct.html '//a/@href' 'struct.MyStruct.html#method.trait_fn' /// [`trait_fn`] /// diff --git a/src/test/rustdoc/intra-doc/trait-item.rs b/src/test/rustdoc/intra-doc/trait-item.rs index de8585f4c9a78..7602aced56416 100644 --- a/src/test/rustdoc/intra-doc/trait-item.rs +++ b/src/test/rustdoc/intra-doc/trait-item.rs @@ -1,9 +1,8 @@ -// ignore-tidy-linelength #![deny(broken_intra_doc_links)] /// Link to [S::assoc_fn()] /// Link to [Default::default()] -// @has trait_item/struct.S.html '//*[@href="../trait_item/struct.S.html#method.assoc_fn"]' 'S::assoc_fn()' +// @has trait_item/struct.S.html '//*[@href="struct.S.html#method.assoc_fn"]' 'S::assoc_fn()' // @has - '//*[@href="https://doc.rust-lang.org/nightly/core/default/trait.Default.html#tymethod.default"]' 'Default::default()' pub struct S; diff --git a/src/test/rustdoc/intra-doc/true-false.rs b/src/test/rustdoc/intra-doc/true-false.rs index 7b21e93414740..db637ece36995 100644 --- a/src/test/rustdoc/intra-doc/true-false.rs +++ b/src/test/rustdoc/intra-doc/true-false.rs @@ -1,7 +1,6 @@ #![deny(broken_intra_doc_links)] #![crate_name = "foo"] -// ignore-tidy-linelength // @has foo/index.html // @has - '//*[@id="main"]//a[@href="https://doc.rust-lang.org/nightly/std/primitive.bool.html"]' 'true' diff --git a/src/test/rustdoc/intra-link-self-cache.rs b/src/test/rustdoc/intra-link-self-cache.rs index add1530a5a675..63bf7fa5768a7 100644 --- a/src/test/rustdoc/intra-link-self-cache.rs +++ b/src/test/rustdoc/intra-link-self-cache.rs @@ -1,12 +1,12 @@ #![crate_name = "foo"] -// @has foo/enum.E1.html '//a/@href' '../foo/enum.E1.html#variant.A' +// @has foo/enum.E1.html '//a/@href' 'enum.E1.html#variant.A' /// [Self::A::b] pub enum E1 { A { b: usize } } -// @has foo/enum.E2.html '//a/@href' '../foo/enum.E2.html#variant.A' +// @has foo/enum.E2.html '//a/@href' 'enum.E2.html#variant.A' /// [Self::A::b] pub enum E2 { diff --git a/src/test/rustdoc/issue-21474.rs b/src/test/rustdoc/issue-21474.rs index 896fc1a78f13f..5de26abace6fa 100644 --- a/src/test/rustdoc/issue-21474.rs +++ b/src/test/rustdoc/issue-21474.rs @@ -7,5 +7,5 @@ mod inner { pub trait Blah { } // @count issue_21474/struct.What.html \ -// '//*[@id="trait-implementations-list"]/*[@class="impl"]' 1 +// '//*[@id="trait-implementations-list"]//*[@class="impl"]' 1 pub struct What; diff --git a/src/test/rustdoc/issue-28478.rs b/src/test/rustdoc/issue-28478.rs index 4cc4056025463..497276e6826a1 100644 --- a/src/test/rustdoc/issue-28478.rs +++ b/src/test/rustdoc/issue-28478.rs @@ -23,9 +23,9 @@ impl Foo { } impl Bar for Foo { - // @has - '//*[@href="../issue_28478/trait.Bar.html#associatedtype.Bar"]' 'Bar' - // @has - '//*[@href="../issue_28478/trait.Bar.html#associatedconstant.Baz"]' 'Baz' - // @has - '//*[@href="../issue_28478/trait.Bar.html#tymethod.bar"]' 'bar' + // @has - '//*[@href="trait.Bar.html#associatedtype.Bar"]' 'Bar' + // @has - '//*[@href="trait.Bar.html#associatedconstant.Baz"]' 'Baz' + // @has - '//*[@href="trait.Bar.html#tymethod.bar"]' 'bar' fn bar() {} - // @has - '//*[@href="../issue_28478/trait.Bar.html#method.baz"]' 'baz' + // @has - '//*[@href="trait.Bar.html#method.baz"]' 'baz' } diff --git a/src/test/rustdoc/issue-28927.rs b/src/test/rustdoc/issue-28927.rs index 7b535f33bf7e3..38a520850b6dd 100644 --- a/src/test/rustdoc/issue-28927.rs +++ b/src/test/rustdoc/issue-28927.rs @@ -2,5 +2,5 @@ // aux-build:issue-28927-1.rs // ignore-cross-compile -extern crate issue_28927_1 as inner1; +pub extern crate issue_28927_1 as inner1; pub use inner1 as foo; diff --git a/src/test/rustdoc/issue-29503.rs b/src/test/rustdoc/issue-29503.rs index d7e0f37b2866f..2b25da77d7e7b 100644 --- a/src/test/rustdoc/issue-29503.rs +++ b/src/test/rustdoc/issue-29503.rs @@ -1,5 +1,3 @@ -// ignore-tidy-linelength - use std::fmt; // @has issue_29503/trait.MyTrait.html @@ -7,7 +5,7 @@ pub trait MyTrait { fn my_string(&self) -> String; } -// @has - "//div[@id='implementors-list']/h3[@id='impl-MyTrait']//code" "impl MyTrait for T where T: Debug" +// @has - "//div[@id='implementors-list']//h3[@id='impl-MyTrait']//code" "impl MyTrait for T where T: Debug" impl MyTrait for T where T: fmt::Debug { fn my_string(&self) -> String { format!("{:?}", self) diff --git a/src/test/rustdoc/issue-45584.rs b/src/test/rustdoc/issue-45584.rs index 0225c0c5c2fa7..8a5f0413826a9 100644 --- a/src/test/rustdoc/issue-45584.rs +++ b/src/test/rustdoc/issue-45584.rs @@ -4,12 +4,12 @@ pub trait Bar {} // @has 'foo/struct.Foo1.html' pub struct Foo1; -// @count - '//*[@id="trait-implementations-list"]/*[@class="impl"]' 1 +// @count - '//*[@id="trait-implementations-list"]//*[@class="impl"]' 1 // @has - '//*[@class="impl"]' "impl Bar for Foo1" impl Bar for Foo1 {} // @has 'foo/struct.Foo2.html' pub struct Foo2; -// @count - '//*[@id="trait-implementations-list"]/*[@class="impl"]' 1 +// @count - '//*[@id="trait-implementations-list"]//*[@class="impl"]' 1 // @has - '//*[@class="impl"]' "impl Bar<&'static Foo2, Foo2> for u8" impl Bar<&'static Foo2, Foo2> for u8 {} diff --git a/src/test/rustdoc/issue-50159.rs b/src/test/rustdoc/issue-50159.rs index 74502be622a4f..0820512e52140 100644 --- a/src/test/rustdoc/issue-50159.rs +++ b/src/test/rustdoc/issue-50159.rs @@ -13,8 +13,8 @@ impl Signal2 for B where B: Signal { // @has issue_50159/struct.Switch.html // @has - '//code' 'impl Send for Switch where ::Item: Send' // @has - '//code' 'impl Sync for Switch where ::Item: Sync' -// @count - '//*[@id="implementations-list"]/*[@class="impl"]' 0 -// @count - '//*[@id="synthetic-implementations-list"]/*[@class="impl"]' 5 +// @count - '//*[@id="implementations-list"]//*[@class="impl"]' 0 +// @count - '//*[@id="synthetic-implementations-list"]//*[@class="impl"]' 5 pub struct Switch { pub inner: ::Item2, } diff --git a/src/test/rustdoc/issue-51236.rs b/src/test/rustdoc/issue-51236.rs index d9accf9c5998b..d018c948162d9 100644 --- a/src/test/rustdoc/issue-51236.rs +++ b/src/test/rustdoc/issue-51236.rs @@ -7,7 +7,7 @@ pub mod traits { } // @has issue_51236/struct.Owned.html -// @has - '//*[@id="synthetic-implementations-list"]/*[@class="impl"]//code' "impl Send for \ +// @has - '//*[@id="synthetic-implementations-list"]//*[@class="impl"]//code' "impl Send for \ // Owned where >::Reader: Send" pub struct Owned where T: for<'a> ::traits::Owned<'a> { marker: PhantomData<>::Reader>, diff --git a/src/test/rustdoc/issue-53812.rs b/src/test/rustdoc/issue-53812.rs index 3ebf154077f49..daebe059f8ef6 100644 --- a/src/test/rustdoc/issue-53812.rs +++ b/src/test/rustdoc/issue-53812.rs @@ -12,9 +12,9 @@ macro_rules! array_impls { } } -// @has issue_53812/trait.MyIterator.html '//*[@id="implementors-list"]//h3[1]' 'MyStruct<[T; 0]>' -// @has - '//*[@id="implementors-list"]//h3[2]' 'MyStruct<[T; 1]>' -// @has - '//*[@id="implementors-list"]//h3[3]' 'MyStruct<[T; 2]>' -// @has - '//*[@id="implementors-list"]//h3[4]' 'MyStruct<[T; 3]>' -// @has - '//*[@id="implementors-list"]//h3[5]' 'MyStruct<[T; 10]>' +// @has issue_53812/trait.MyIterator.html '//*[@id="implementors-list"]/details[1]/summary/h3' 'MyStruct<[T; 0]>' +// @has - '//*[@id="implementors-list"]/details[2]/summary/h3' 'MyStruct<[T; 1]>' +// @has - '//*[@id="implementors-list"]/details[3]/summary/h3' 'MyStruct<[T; 2]>' +// @has - '//*[@id="implementors-list"]/details[4]/summary/h3' 'MyStruct<[T; 3]>' +// @has - '//*[@id="implementors-list"]/details[5]/summary/h3' 'MyStruct<[T; 10]>' array_impls! { 10 3 2 1 0 } diff --git a/src/test/rustdoc/issue-54705.rs b/src/test/rustdoc/issue-54705.rs index 263b1eb0bd65a..47da94a4ccf4c 100644 --- a/src/test/rustdoc/issue-54705.rs +++ b/src/test/rustdoc/issue-54705.rs @@ -3,10 +3,10 @@ pub trait ScopeHandle<'scope> {} // @has issue_54705/struct.ScopeFutureContents.html -// @has - '//*[@id="synthetic-implementations-list"]/*[@class="impl"]//code' "impl<'scope, S> \ +// @has - '//*[@id="synthetic-implementations-list"]//*[@class="impl"]//code' "impl<'scope, S> \ // Send for ScopeFutureContents<'scope, S> where S: Sync" // -// @has - '//*[@id="synthetic-implementations-list"]/*[@class="impl"]//code' "impl<'scope, S> \ +// @has - '//*[@id="synthetic-implementations-list"]//*[@class="impl"]//code' "impl<'scope, S> \ // Sync for ScopeFutureContents<'scope, S> where S: Sync" pub struct ScopeFutureContents<'scope, S> where S: ScopeHandle<'scope>, diff --git a/src/test/rustdoc/issue-55321.rs b/src/test/rustdoc/issue-55321.rs index d312a5114595a..d1877f39ba761 100644 --- a/src/test/rustdoc/issue-55321.rs +++ b/src/test/rustdoc/issue-55321.rs @@ -1,16 +1,16 @@ #![feature(negative_impls)] // @has issue_55321/struct.A.html -// @has - '//*[@id="trait-implementations-list"]/*[@class="impl"]//code' "impl !Send for A" -// @has - '//*[@id="trait-implementations-list"]/*[@class="impl"]//code' "impl !Sync for A" +// @has - '//*[@id="trait-implementations-list"]//*[@class="impl"]//code' "impl !Send for A" +// @has - '//*[@id="trait-implementations-list"]//*[@class="impl"]//code' "impl !Sync for A" pub struct A(); impl !Send for A {} impl !Sync for A {} // @has issue_55321/struct.B.html -// @has - '//*[@id="synthetic-implementations-list"]/*[@class="impl"]//code' "impl !Send for \ +// @has - '//*[@id="synthetic-implementations-list"]//*[@class="impl"]//code' "impl !Send for \ // B" -// @has - '//*[@id="synthetic-implementations-list"]/*[@class="impl"]//code' "impl !Sync for \ +// @has - '//*[@id="synthetic-implementations-list"]//*[@class="impl"]//code' "impl !Sync for \ // B" pub struct B(A, Box); diff --git a/src/test/rustdoc/issue-55364.rs b/src/test/rustdoc/issue-55364.rs index 200a29fc7eea3..f156d225bd79b 100644 --- a/src/test/rustdoc/issue-55364.rs +++ b/src/test/rustdoc/issue-55364.rs @@ -1,22 +1,20 @@ -// ignore-tidy-linelength - // First a module with inner documentation // @has issue_55364/subone/index.html // These foo/bar links in the module's documentation should refer inside `subone` -// @has - '//section[@id="main"]/div[@class="docblock"]//a[@href="../../issue_55364/subone/fn.foo.html"]' 'foo' -// @has - '//section[@id="main"]/div[@class="docblock"]//a[@href="../../issue_55364/subone/fn.bar.html"]' 'bar' +// @has - '//section[@id="main"]/div[@class="docblock"]//a[@href="fn.foo.html"]' 'foo' +// @has - '//section[@id="main"]/div[@class="docblock"]//a[@href="fn.bar.html"]' 'bar' pub mod subone { //! See either [foo] or [bar]. // This should refer to subone's `bar` // @has issue_55364/subone/fn.foo.html - // @has - '//section[@id="main"]/div[@class="docblock"]//a[@href="../../issue_55364/subone/fn.bar.html"]' 'bar' + // @has - '//section[@id="main"]/div[@class="docblock"]//a[@href="fn.bar.html"]' 'bar' /// See [bar] pub fn foo() {} // This should refer to subone's `foo` // @has issue_55364/subone/fn.bar.html - // @has - '//section[@id="main"]/div[@class="docblock"]//a[@href="../../issue_55364/subone/fn.foo.html"]' 'foo' + // @has - '//section[@id="main"]/div[@class="docblock"]//a[@href="fn.foo.html"]' 'foo' /// See [foo] pub fn bar() {} } @@ -25,11 +23,11 @@ pub mod subone { // @has issue_55364/subtwo/index.html // These foo/bar links in the module's documentation should not reference inside `subtwo` -// @!has - '//section[@id="main"]/div[@class="docblock"]//a[@href="../../issue_55364/subtwo/fn.foo.html"]' 'foo' -// @!has - '//section[@id="main"]/div[@class="docblock"]//a[@href="../../issue_55364/subtwo/fn.bar.html"]' 'bar' +// @!has - '//section[@id="main"]/div[@class="docblock"]//a[@href="fn.foo.html"]' 'foo' +// @!has - '//section[@id="main"]/div[@class="docblock"]//a[@href="fn.bar.html"]' 'bar' // Instead it should be referencing the top level functions -// @has - '//section[@id="main"]/div[@class="docblock"]//a[@href="../../issue_55364/fn.foo.html"]' 'foo' -// @has - '//section[@id="main"]/div[@class="docblock"]//a[@href="../../issue_55364/fn.bar.html"]' 'bar' +// @has - '//section[@id="main"]/div[@class="docblock"]//a[@href="../fn.foo.html"]' 'foo' +// @has - '//section[@id="main"]/div[@class="docblock"]//a[@href="../fn.bar.html"]' 'bar' // Though there should be such links later // @has - '//section[@id="main"]/table//tr[@class="module-item"]/td/a[@class="fn"][@href="fn.foo.html"]' 'foo' // @has - '//section[@id="main"]/table//tr[@class="module-item"]/td/a[@class="fn"][@href="fn.bar.html"]' 'bar' @@ -39,13 +37,13 @@ pub mod subtwo { // Despite the module's docs referring to the top level foo/bar, // this should refer to subtwo's `bar` // @has issue_55364/subtwo/fn.foo.html - // @has - '//section[@id="main"]/div[@class="docblock"]//a[@href="../../issue_55364/subtwo/fn.bar.html"]' 'bar' + // @has - '//section[@id="main"]/div[@class="docblock"]//a[@href="fn.bar.html"]' 'bar' /// See [bar] pub fn foo() {} // Despite the module's docs referring to the top level foo/bar, // this should refer to subtwo's `foo` // @has issue_55364/subtwo/fn.bar.html - // @has - '//section[@id="main"]/div[@class="docblock"]//a[@href="../../issue_55364/subtwo/fn.foo.html"]' 'foo' + // @has - '//section[@id="main"]/div[@class="docblock"]//a[@href="fn.foo.html"]' 'foo' /// See [foo] pub fn bar() {} } @@ -61,8 +59,8 @@ pub fn bar() {} // @has issue_55364/subthree/index.html // This module should also refer to the top level foo/bar -// @has - '//section[@id="main"]/div[@class="docblock"]//a[@href="../../issue_55364/fn.foo.html"]' 'foo' -// @has - '//section[@id="main"]/div[@class="docblock"]//a[@href="../../issue_55364/fn.bar.html"]' 'bar' +// @has - '//section[@id="main"]/div[@class="docblock"]//a[@href="../fn.foo.html"]' 'foo' +// @has - '//section[@id="main"]/div[@class="docblock"]//a[@href="../fn.bar.html"]' 'bar' pub mod subthree { //! See either [foo][super::foo] or [bar][super::bar] } @@ -70,8 +68,8 @@ pub mod subthree { // Next we go *deeper* - In order to ensure it's not just "this or parent" // we test `crate::` and a `super::super::...` chain // @has issue_55364/subfour/subfive/subsix/subseven/subeight/index.html -// @has - '//section[@id="main"]/table//tr[@class="module-item"]/td[@class="docblock-short"]//a[@href="../../../../../../issue_55364/subone/fn.foo.html"]' 'other foo' -// @has - '//section[@id="main"]/table//tr[@class="module-item"]/td[@class="docblock-short"]//a[@href="../../../../../../issue_55364/subtwo/fn.bar.html"]' 'other bar' +// @has - '//section[@id="main"]/table//tr[@class="module-item"]/td[@class="docblock-short"]//a[@href="../../../../../subone/fn.foo.html"]' 'other foo' +// @has - '//section[@id="main"]/table//tr[@class="module-item"]/td[@class="docblock-short"]//a[@href="../../../../../subtwo/fn.bar.html"]' 'other bar' pub mod subfour { pub mod subfive { pub mod subsix { diff --git a/src/test/rustdoc/issue-56822.rs b/src/test/rustdoc/issue-56822.rs index 5b67817fa4caa..b932a3d34749c 100644 --- a/src/test/rustdoc/issue-56822.rs +++ b/src/test/rustdoc/issue-56822.rs @@ -17,7 +17,7 @@ impl<'a, T> MyTrait for Inner<'a, T> { } // @has issue_56822/struct.Parser.html -// @has - '//*[@id="synthetic-implementations-list"]/*[@class="impl"]//code' "impl<'a> Send for \ +// @has - '//*[@id="synthetic-implementations-list"]//*[@class="impl"]//code' "impl<'a> Send for \ // Parser<'a>" pub struct Parser<'a> { field: > as MyTrait>::Output diff --git a/src/test/rustdoc/issue-60726.rs b/src/test/rustdoc/issue-60726.rs index 6acc86277385d..79b8b70c54525 100644 --- a/src/test/rustdoc/issue-60726.rs +++ b/src/test/rustdoc/issue-60726.rs @@ -26,9 +26,9 @@ where {} // @has issue_60726/struct.IntoIter.html -// @has - '//*[@id="synthetic-implementations-list"]/*[@class="impl"]//code' "impl !Send for \ +// @has - '//*[@id="synthetic-implementations-list"]//*[@class="impl"]//code' "impl !Send for \ // IntoIter" -// @has - '//*[@id="synthetic-implementations-list"]/*[@class="impl"]//code' "impl !Sync for \ +// @has - '//*[@id="synthetic-implementations-list"]//*[@class="impl"]//code' "impl !Sync for \ // IntoIter" pub struct IntoIter{ hello:DynTrait>, diff --git a/src/test/rustdoc/issue-66159.rs b/src/test/rustdoc/issue-66159.rs deleted file mode 100644 index 003d079a470c0..0000000000000 --- a/src/test/rustdoc/issue-66159.rs +++ /dev/null @@ -1,10 +0,0 @@ -// aux-crate:priv:issue_66159_1=issue-66159-1.rs -// compile-flags:-Z unstable-options - -// The issue was an ICE which meant that we never actually generated the docs -// so if we have generated the docs, we're okay. -// Since we don't generate the docs for the auxiliary files, we can't actually -// verify that the struct is linked correctly. - -// @has issue_66159/index.html -//! [issue_66159_1::Something] diff --git a/src/test/rustdoc/issue-72340.rs b/src/test/rustdoc/issue-72340.rs index 6ed3bfbe3e54b..64044cfe94720 100644 --- a/src/test/rustdoc/issue-72340.rs +++ b/src/test/rustdoc/issue-72340.rs @@ -10,7 +10,7 @@ impl Body { } impl Default for Body { - // @has foo/struct.Body.html '//a/@href' '../foo/struct.Body.html#method.empty' + // @has foo/struct.Body.html '//a/@href' 'struct.Body.html#method.empty' /// Returns [`Body::empty()`](Body::empty). fn default() -> Body { diff --git a/src/test/rustdoc/issue-75588.rs b/src/test/rustdoc/issue-75588.rs index 835ed02ac00db..aebffeff5f05e 100644 --- a/src/test/rustdoc/issue-75588.rs +++ b/src/test/rustdoc/issue-75588.rs @@ -1,4 +1,3 @@ -// ignore-tidy-linelength // aux-build:realcore.rs // aux-build:real_gimli.rs diff --git a/src/test/rustdoc/item-hide-threshold.rs b/src/test/rustdoc/item-hide-threshold.rs new file mode 100644 index 0000000000000..8986f72636a40 --- /dev/null +++ b/src/test/rustdoc/item-hide-threshold.rs @@ -0,0 +1,156 @@ +#![allow(unused)] + +// @has 'item_hide_threshold/struct.PubStruct.html' +// @count - '//details[@class="rustdoc-toggle type-contents-toggle"]' 0 +pub struct PubStruct { + pub a: usize, + pub b: usize, +} + +// @has 'item_hide_threshold/struct.BigPubStruct.html' +// @count - '//details[@class="rustdoc-toggle type-contents-toggle"]' 1 +// @has - '//details[@class="rustdoc-toggle type-contents-toggle"]' 'Show fields' +pub struct BigPubStruct { + pub a: usize, + pub b: usize, + pub c: usize, + pub d: usize, + pub e: usize, + pub f: usize, + pub g: usize, + pub h: usize, + pub i: usize, + pub j: usize, + pub k: usize, + pub l: usize, + pub m: usize, +} + +// @has 'item_hide_threshold/union.BigUnion.html' +// @count - '//details[@class="rustdoc-toggle type-contents-toggle"]' 1 +// @has - '//details[@class="rustdoc-toggle type-contents-toggle"]' 'Show fields' +pub union BigUnion { + pub a: usize, + pub b: usize, + pub c: usize, + pub d: usize, + pub e: usize, + pub f: usize, + pub g: usize, + pub h: usize, + pub i: usize, + pub j: usize, + pub k: usize, + pub l: usize, + pub m: usize, +} + +// @has 'item_hide_threshold/union.Union.html' +// @count - '//details[@class="rustdoc-toggle type-contents-toggle"]' 0 +pub union Union { + pub a: usize, + pub b: usize, + pub c: usize, +} + +// @has 'item_hide_threshold/struct.PrivStruct.html' +// @count - '//details[@class="rustdoc-toggle type-contents-toggle"]' 0 +// @has - '//div[@class="docblock type-decl"]' 'fields omitted' +pub struct PrivStruct { + a: usize, + b: usize, +} + +// @has 'item_hide_threshold/enum.Enum.html' +// @count - '//details[@class="rustdoc-toggle type-contents-toggle"]' 1 +// @has - '//details[@class="rustdoc-toggle type-contents-toggle"]' 'Show fields' +pub enum Enum { + A, B, C, + D { + a: u8, + b: u8 + } +} + +// @has 'item_hide_threshold/enum.LargeEnum.html' +// @count - '//details[@class="rustdoc-toggle type-contents-toggle"]' 1 +// @has - '//details[@class="rustdoc-toggle type-contents-toggle"]' 'Show variants' +pub enum LargeEnum { + A, B, C, D, E, F(u8), G, H, I, J, K, L, M +} + +// @has 'item_hide_threshold/trait.Trait.html' +// @count - '//details[@class="rustdoc-toggle type-contents-toggle"]' 0 +pub trait Trait { + type A; + #[must_use] + fn foo(); + fn bar(); +} + +// @has 'item_hide_threshold/trait.GinormousTrait.html' +// @count - '//details[@class="rustdoc-toggle type-contents-toggle"]' 1 +// @has - '//details[@class="rustdoc-toggle type-contents-toggle"]' 'Show associated items' +pub trait GinormousTrait { + type A; + type B; + type C; + type D; + type E; + type F; + type G; + type H; + type I; + type J; + type K; + type L; + type M; + const N: usize = 1; + #[must_use] + fn foo(); + fn bar(); +} + +// @has 'item_hide_threshold/trait.HugeTrait.html' +// @count - '//details[@class="rustdoc-toggle type-contents-toggle"]' 1 +// @has - '//details[@class="rustdoc-toggle type-contents-toggle"]' 'Show associated constants and methods' +pub trait HugeTrait { + type A; + const M: usize = 1; + const N: usize = 1; + const O: usize = 1; + const P: usize = 1; + const Q: usize = 1; + const R: usize = 1; + const S: usize = 1; + const T: usize = 1; + const U: usize = 1; + const V: usize = 1; + const W: usize = 1; + const X: usize = 1; + #[must_use] + fn foo(); + fn bar(); +} + +// @has 'item_hide_threshold/trait.BigTrait.html' +// @count - '//details[@class="rustdoc-toggle type-contents-toggle"]' 1 +// @has - '//details[@class="rustdoc-toggle type-contents-toggle"]' 'Show methods' +pub trait BigTrait { + type A; + #[must_use] + fn foo(); + fn bar(); + fn baz(); + fn quux(); + fn frob(); + fn greeble(); + fn blap(); + fn whoop(); + fn pow(); + fn bang(); + fn oomph(); + fn argh(); + fn wap(); + fn ouch(); +} diff --git a/src/test/rustdoc/link-assoc-const.rs b/src/test/rustdoc/link-assoc-const.rs index f9eb2b722d6e6..75a2531a308f7 100644 --- a/src/test/rustdoc/link-assoc-const.rs +++ b/src/test/rustdoc/link-assoc-const.rs @@ -1,7 +1,7 @@ #![crate_name = "foo"] -// @has foo/index.html '//a[@href="../foo/foo/constant.FIRSTCONST.html"]' 'foo::FIRSTCONST' -// @has foo/index.html '//a[@href="../foo/struct.Bar.html#associatedconstant.CONST"]' 'Bar::CONST' +// @has foo/index.html '//a[@href="foo/constant.FIRSTCONST.html"]' 'foo::FIRSTCONST' +// @has foo/index.html '//a[@href="struct.Bar.html#associatedconstant.CONST"]' 'Bar::CONST' //! We have here [`foo::FIRSTCONST`] and [`Bar::CONST`]. diff --git a/src/test/rustdoc/playground-arg.rs b/src/test/rustdoc/playground-arg.rs index dbe2297f81887..69c8962653931 100644 --- a/src/test/rustdoc/playground-arg.rs +++ b/src/test/rustdoc/playground-arg.rs @@ -1,5 +1,4 @@ // compile-flags: --playground-url=https://example.com/ -Z unstable-options -// ignore-tidy-linelength #![crate_name = "foo"] diff --git a/src/test/rustdoc/playground.rs b/src/test/rustdoc/playground.rs index 9971c7b4297a2..877ea1cfba15a 100644 --- a/src/test/rustdoc/playground.rs +++ b/src/test/rustdoc/playground.rs @@ -1,5 +1,3 @@ -// ignore-tidy-linelength - #![crate_name = "foo"] #![doc(html_playground_url = "https://www.example.com/")] diff --git a/src/test/rustdoc/prim-title.rs b/src/test/rustdoc/prim-title.rs deleted file mode 100644 index fa3fd512fada6..0000000000000 --- a/src/test/rustdoc/prim-title.rs +++ /dev/null @@ -1,7 +0,0 @@ -#![crate_name = "foo"] - -// @has foo/primitive.u8.html '//head/title' 'u8 - Rust' -// @!has - '//head/title' 'foo' -#[doc(primitive = "u8")] -/// `u8` docs -mod u8 {} diff --git a/src/test/rustdoc/primitive-link.rs b/src/test/rustdoc/primitive-link.rs index 3041ff77684ca..dd455e45bfc2a 100644 --- a/src/test/rustdoc/primitive-link.rs +++ b/src/test/rustdoc/primitive-link.rs @@ -1,6 +1,5 @@ #![crate_name = "foo"] -// ignore-tidy-linelength // @has foo/struct.Foo.html '//*[@class="docblock"]/p/a[@href="https://doc.rust-lang.org/nightly/std/primitive.u32.html"]' 'u32' // @has foo/struct.Foo.html '//*[@class="docblock"]/p/a[@href="https://doc.rust-lang.org/nightly/std/primitive.i64.html"]' 'i64' diff --git a/src/test/rustdoc/proc-macro.rs b/src/test/rustdoc/proc-macro.rs index 82196e413e94b..f6d1f2cf91b5f 100644 --- a/src/test/rustdoc/proc-macro.rs +++ b/src/test/rustdoc/proc-macro.rs @@ -61,12 +61,12 @@ pub fn some_derive(_item: TokenStream) -> TokenStream { // @has some_macros/foo/index.html mod foo { // @has - '//code' 'pub use some_proc_macro;' - // @has - '//a/@href' '../../some_macros/macro.some_proc_macro.html' + // @has - '//a/@href' '../macro.some_proc_macro.html' pub use some_proc_macro; // @has - '//code' 'pub use some_proc_attr;' - // @has - '//a/@href' '../../some_macros/attr.some_proc_attr.html' + // @has - '//a/@href' '../attr.some_proc_attr.html' pub use some_proc_attr; // @has - '//code' 'pub use some_derive;' - // @has - '//a/@href' '../../some_macros/derive.SomeDerive.html' + // @has - '//a/@href' '../derive.SomeDerive.html' pub use some_derive; } diff --git a/src/test/rustdoc/raw-ident-eliminate-r-hashtag.rs b/src/test/rustdoc/raw-ident-eliminate-r-hashtag.rs index f895a4c2104ba..ad19036126760 100644 --- a/src/test/rustdoc/raw-ident-eliminate-r-hashtag.rs +++ b/src/test/rustdoc/raw-ident-eliminate-r-hashtag.rs @@ -1,5 +1,3 @@ -// ignore-tidy-linelength - #![crate_type="lib"] pub mod internal { @@ -10,13 +8,13 @@ pub mod internal { /// /// [name]: mod /// [other name]: crate::internal::mod - // @has 'raw_ident_eliminate_r_hashtag/internal/struct.B.html' '//*a[@href="../../raw_ident_eliminate_r_hashtag/internal/struct.mod.html"]' 'name' - // @has 'raw_ident_eliminate_r_hashtag/internal/struct.B.html' '//*a[@href="../../raw_ident_eliminate_r_hashtag/internal/struct.mod.html"]' 'other name' + // @has 'raw_ident_eliminate_r_hashtag/internal/struct.B.html' '//*a[@href="struct.mod.html"]' 'name' + // @has 'raw_ident_eliminate_r_hashtag/internal/struct.B.html' '//*a[@href="struct.mod.html"]' 'other name' pub struct B; } /// See [name]. /// /// [name]: internal::mod -// @has 'raw_ident_eliminate_r_hashtag/struct.A.html' '//*a[@href="../raw_ident_eliminate_r_hashtag/internal/struct.mod.html"]' 'name' +// @has 'raw_ident_eliminate_r_hashtag/struct.A.html' '//*a[@href="internal/struct.mod.html"]' 'name' pub struct A; diff --git a/src/test/rustdoc/reexport-stability-tags-deprecated-and-portability.rs b/src/test/rustdoc/reexport-stability-tags-deprecated-and-portability.rs new file mode 100644 index 0000000000000..a79d05904e31c --- /dev/null +++ b/src/test/rustdoc/reexport-stability-tags-deprecated-and-portability.rs @@ -0,0 +1,48 @@ +#![crate_name = "foo"] +#![feature(doc_cfg)] + +pub mod tag { + #[deprecated(since = "0.1.8", note = "Use bar() instead")] + pub trait Deprecated {} + + #[doc(cfg(feature = "sync"))] + pub trait Portability {} + + #[deprecated(since = "0.1.8", note = "Use bar() instead")] + #[doc(cfg(feature = "sync"))] + pub trait Both {} + + pub trait None {} +} + +// @has foo/mod1/index.html +pub mod mod1 { + // @has - '//code' 'pub use tag::Deprecated;' + // @has - '//span' 'Deprecated' + // @!has - '//span' 'sync' + pub use tag::Deprecated; +} + +// @has foo/mod2/index.html +pub mod mod2 { + // @has - '//code' 'pub use tag::Portability;' + // @!has - '//span' 'Deprecated' + // @has - '//span' 'sync' + pub use tag::Portability; +} + +// @has foo/mod3/index.html +pub mod mod3 { + // @has - '//code' 'pub use tag::Both;' + // @has - '//span' 'Deprecated' + // @has - '//span' 'sync' + pub use tag::Both; +} + +// @has foo/mod4/index.html +pub mod mod4 { + // @has - '//code' 'pub use tag::None;' + // @!has - '//span' 'Deprecated' + // @!has - '//span' 'sync' + pub use tag::None; +} diff --git a/src/test/rustdoc/reexport-stability-tags-unstable-and-portability.rs b/src/test/rustdoc/reexport-stability-tags-unstable-and-portability.rs new file mode 100644 index 0000000000000..ff8a910f59f98 --- /dev/null +++ b/src/test/rustdoc/reexport-stability-tags-unstable-and-portability.rs @@ -0,0 +1,61 @@ +#![crate_name = "foo"] +#![feature(doc_cfg)] +#![feature(staged_api)] +#![stable(feature = "rust1", since = "1.0.0")] + +#[stable(feature = "rust1", since = "1.0.0")] +pub mod tag { + #[unstable(feature = "humans", issue = "none")] + pub trait Unstable {} + + #[stable(feature = "rust1", since = "1.0.0")] + #[doc(cfg(feature = "sync"))] + pub trait Portability {} + + #[unstable(feature = "humans", issue = "none")] + #[doc(cfg(feature = "sync"))] + pub trait Both {} + + #[stable(feature = "rust1", since = "1.0.0")] + pub trait None {} +} + +// @has foo/mod1/index.html +#[stable(feature = "rust1", since = "1.0.0")] +pub mod mod1 { + // @has - '//code' 'pub use tag::Unstable;' + // @has - '//span' 'Experimental' + // @!has - '//span' 'sync' + #[stable(feature = "rust1", since = "1.0.0")] + pub use tag::Unstable; +} + +// @has foo/mod2/index.html +#[stable(feature = "rust1", since = "1.0.0")] +pub mod mod2 { + // @has - '//code' 'pub use tag::Portability;' + // @!has - '//span' 'Experimental' + // @has - '//span' 'sync' + #[stable(feature = "rust1", since = "1.0.0")] + pub use tag::Portability; +} + +// @has foo/mod3/index.html +#[stable(feature = "rust1", since = "1.0.0")] +pub mod mod3 { + // @has - '//code' 'pub use tag::Both;' + // @has - '//span' 'Experimental' + // @has - '//span' 'sync' + #[stable(feature = "rust1", since = "1.0.0")] + pub use tag::Both; +} + +// @has foo/mod4/index.html +#[stable(feature = "rust1", since = "1.0.0")] +pub mod mod4 { + // @has - '//code' 'pub use tag::None;' + // @!has - '//span' 'Experimental' + // @!has - '//span' 'sync' + #[stable(feature = "rust1", since = "1.0.0")] + pub use tag::None; +} diff --git a/src/test/rustdoc/return-impl-trait.rs b/src/test/rustdoc/return-impl-trait.rs index 1ccf5ac46119a..da4945aadf7f9 100644 --- a/src/test/rustdoc/return-impl-trait.rs +++ b/src/test/rustdoc/return-impl-trait.rs @@ -1,4 +1,4 @@ -#![feature(type_alias_impl_trait)] +#![feature(min_type_alias_impl_trait)] pub trait Backend {} diff --git a/src/test/rustdoc/smart-punct.rs b/src/test/rustdoc/smart-punct.rs index a1ca2699554e5..5319892c99c23 100644 --- a/src/test/rustdoc/smart-punct.rs +++ b/src/test/rustdoc/smart-punct.rs @@ -1,5 +1,3 @@ -// ignore-tidy-linelength - #![crate_name = "foo"] //! This is the "start" of the 'document'! How'd you know that "it's" the start? diff --git a/src/test/rustdoc/src-links-external.rs b/src/test/rustdoc/src-links-external.rs index 0469e4ad62477..8012e442213b8 100644 --- a/src/test/rustdoc/src-links-external.rs +++ b/src/test/rustdoc/src-links-external.rs @@ -1,7 +1,6 @@ // aux-build:src-links-external.rs // build-aux-docs // ignore-cross-compile -// ignore-tidy-linelength #![crate_name = "foo"] diff --git a/src/test/rustdoc/struct-arg-pattern.rs b/src/test/rustdoc/struct-arg-pattern.rs new file mode 100644 index 0000000000000..3c0369e3d3413 --- /dev/null +++ b/src/test/rustdoc/struct-arg-pattern.rs @@ -0,0 +1,10 @@ +#![crate_name = "foo"] + +struct BodyId { + hir_id: usize, +} + +// @has 'foo/fn.body_owner.html' '//*[@class="rust fn"]' 'pub fn body_owner(_: BodyId)' +pub fn body_owner(BodyId { hir_id }: BodyId) { + // ... +} diff --git a/src/test/rustdoc/struct-field.rs b/src/test/rustdoc/struct-field.rs index 532e29bc691c4..998683bdde7f2 100644 --- a/src/test/rustdoc/struct-field.rs +++ b/src/test/rustdoc/struct-field.rs @@ -1,10 +1,9 @@ #![crate_name = "foo"] -// ignore-tidy-linelength -// @has foo/index.html '//*[@class="docblock"]/p/a[@href="../foo/struct.Foo.html#structfield.bar"]' 'Foo::bar' -// @has foo/index.html '//*[@class="docblock"]/p/a[@href="../foo/union.Bar.html#structfield.foo"]' 'Bar::foo' -// @has foo/index.html '//*[@class="docblock"]/p/a[@href="../foo/enum.Uniooon.html#variant.X"]' 'Uniooon::X' +// @has foo/index.html '//*[@class="docblock"]/p/a[@href="struct.Foo.html#structfield.bar"]' 'Foo::bar' +// @has foo/index.html '//*[@class="docblock"]/p/a[@href="union.Bar.html#structfield.foo"]' 'Bar::foo' +// @has foo/index.html '//*[@class="docblock"]/p/a[@href="enum.Uniooon.html#variant.X"]' 'Uniooon::X' //! Test with [Foo::bar], [Bar::foo], [Uniooon::X] diff --git a/src/test/rustdoc/synthetic_auto/basic.rs b/src/test/rustdoc/synthetic_auto/basic.rs index 38de5316b6cf5..0dd3a3f7a86c5 100644 --- a/src/test/rustdoc/synthetic_auto/basic.rs +++ b/src/test/rustdoc/synthetic_auto/basic.rs @@ -1,8 +1,8 @@ // @has basic/struct.Foo.html // @has - '//code' 'impl Send for Foo where T: Send' // @has - '//code' 'impl Sync for Foo where T: Sync' -// @count - '//*[@id="implementations-list"]/*[@class="impl"]' 0 -// @count - '//*[@id="synthetic-implementations-list"]/*[@class="impl"]' 5 +// @count - '//*[@id="implementations-list"]//*[@class="impl"]' 0 +// @count - '//*[@id="synthetic-implementations-list"]//*[@class="impl"]' 5 pub struct Foo { field: T, } diff --git a/src/test/rustdoc/synthetic_auto/complex.rs b/src/test/rustdoc/synthetic_auto/complex.rs index 80a717718c22b..d951a20e2dec0 100644 --- a/src/test/rustdoc/synthetic_auto/complex.rs +++ b/src/test/rustdoc/synthetic_auto/complex.rs @@ -20,7 +20,7 @@ mod foo { } // @has complex/struct.NotOuter.html -// @has - '//*[@id="synthetic-implementations-list"]/*[@class="impl"]//code' "impl<'a, T, K: \ +// @has - '//*[@id="synthetic-implementations-list"]//*[@class="impl"]//code' "impl<'a, T, K: \ // ?Sized> Send for Outer<'a, T, K> where K: for<'b> Fn((&'b bool, &'a u8)) \ // -> &'b i8, T: MyTrait<'a>, >::MyItem: Copy, 'a: 'static" diff --git a/src/test/rustdoc/synthetic_auto/lifetimes.rs b/src/test/rustdoc/synthetic_auto/lifetimes.rs index 6d0a68f9b0734..05c88f10822ca 100644 --- a/src/test/rustdoc/synthetic_auto/lifetimes.rs +++ b/src/test/rustdoc/synthetic_auto/lifetimes.rs @@ -9,10 +9,10 @@ where {} // @has lifetimes/struct.Foo.html -// @has - '//*[@id="synthetic-implementations-list"]/*[@class="impl"]//code' "impl<'c, K> Send \ +// @has - '//*[@id="synthetic-implementations-list"]//*[@class="impl"]//code' "impl<'c, K> Send \ // for Foo<'c, K> where K: for<'b> Fn(&'b bool) -> &'c u8, 'c: 'static" // -// @has - '//*[@id="synthetic-implementations-list"]/*[@class="impl"]//code' "impl<'c, K> Sync \ +// @has - '//*[@id="synthetic-implementations-list"]//*[@class="impl"]//code' "impl<'c, K> Sync \ // for Foo<'c, K> where K: Sync" pub struct Foo<'c, K: 'c> { inner_field: Inner<'c, K>, diff --git a/src/test/rustdoc/synthetic_auto/manual.rs b/src/test/rustdoc/synthetic_auto/manual.rs index d20b4744af15b..88ddd57349a29 100644 --- a/src/test/rustdoc/synthetic_auto/manual.rs +++ b/src/test/rustdoc/synthetic_auto/manual.rs @@ -1,12 +1,12 @@ // @has manual/struct.Foo.html -// @has - '//*[@id="synthetic-implementations-list"]/*[@class="impl"]//code' 'impl Sync for \ +// @has - '//*[@id="synthetic-implementations-list"]//*[@class="impl"]//code' 'impl Sync for \ // Foo where T: Sync' // -// @has - '//*[@id="trait-implementations-list"]/*[@class="impl"]//code' \ +// @has - '//*[@id="trait-implementations-list"]//*[@class="impl"]//code' \ // 'impl Send for Foo' // -// @count - '//*[@id="trait-implementations-list"]/*[@class="impl"]' 1 -// @count - '//*[@id="synthetic-implementations-list"]/*[@class="impl"]' 4 +// @count - '//*[@id="trait-implementations-list"]//*[@class="impl"]' 1 +// @count - '//*[@id="synthetic-implementations-list"]//*[@class="impl"]' 4 pub struct Foo { field: T, } diff --git a/src/test/rustdoc/synthetic_auto/negative.rs b/src/test/rustdoc/synthetic_auto/negative.rs index 30713849da221..53801542c9520 100644 --- a/src/test/rustdoc/synthetic_auto/negative.rs +++ b/src/test/rustdoc/synthetic_auto/negative.rs @@ -3,10 +3,10 @@ pub struct Inner { } // @has negative/struct.Outer.html -// @has - '//*[@id="synthetic-implementations-list"]/*[@class="impl"]//code' "impl !Send for \ +// @has - '//*[@id="synthetic-implementations-list"]//*[@class="impl"]//code' "impl !Send for \ // Outer" // -// @has - '//*[@id="synthetic-implementations-list"]/*[@class="impl"]//code' "impl \ +// @has - '//*[@id="synthetic-implementations-list"]//*[@class="impl"]//code' "impl \ // !Sync for Outer" pub struct Outer { inner_field: Inner, diff --git a/src/test/rustdoc/synthetic_auto/nested.rs b/src/test/rustdoc/synthetic_auto/nested.rs index e710ce1c2ed95..d4d93a87ffc9b 100644 --- a/src/test/rustdoc/synthetic_auto/nested.rs +++ b/src/test/rustdoc/synthetic_auto/nested.rs @@ -9,10 +9,10 @@ where } // @has nested/struct.Foo.html -// @has - '//*[@id="synthetic-implementations-list"]/*[@class="impl"]//code' 'impl Send for \ +// @has - '//*[@id="synthetic-implementations-list"]//*[@class="impl"]//code' 'impl Send for \ // Foo where T: Copy' // -// @has - '//*[@id="synthetic-implementations-list"]/*[@class="impl"]//code' \ +// @has - '//*[@id="synthetic-implementations-list"]//*[@class="impl"]//code' \ // 'impl Sync for Foo where T: Sync' pub struct Foo { inner_field: Inner, diff --git a/src/test/rustdoc/synthetic_auto/no-redundancy.rs b/src/test/rustdoc/synthetic_auto/no-redundancy.rs index cf173111ec1e2..3a23dc2cf9576 100644 --- a/src/test/rustdoc/synthetic_auto/no-redundancy.rs +++ b/src/test/rustdoc/synthetic_auto/no-redundancy.rs @@ -9,7 +9,7 @@ where } // @has no_redundancy/struct.Outer.html -// @has - '//*[@id="synthetic-implementations-list"]/*[@class="impl"]//code' "impl Send for \ +// @has - '//*[@id="synthetic-implementations-list"]//*[@class="impl"]//code' "impl Send for \ // Outer where T: Copy + Send" pub struct Outer { inner_field: Inner, diff --git a/src/test/rustdoc/synthetic_auto/project.rs b/src/test/rustdoc/synthetic_auto/project.rs index 5346521f8d2e3..060491e3cf10f 100644 --- a/src/test/rustdoc/synthetic_auto/project.rs +++ b/src/test/rustdoc/synthetic_auto/project.rs @@ -23,10 +23,10 @@ where } // @has project/struct.Foo.html -// @has - '//*[@id="synthetic-implementations-list"]/*[@class="impl"]//code' "impl<'c, K> Send \ +// @has - '//*[@id="synthetic-implementations-list"]//*[@class="impl"]//code' "impl<'c, K> Send \ // for Foo<'c, K> where K: MyTrait, 'c: 'static" // -// @has - '//*[@id="synthetic-implementations-list"]/*[@class="impl"]//code' "impl<'c, K> Sync \ +// @has - '//*[@id="synthetic-implementations-list"]//*[@class="impl"]//code' "impl<'c, K> Sync \ // for Foo<'c, K> where K: MyTrait, ::MyItem: OtherTrait, 'c: 'static," pub struct Foo<'c, K: 'c> { inner_field: Inner<'c, K>, diff --git a/src/test/rustdoc/synthetic_auto/self-referential.rs b/src/test/rustdoc/synthetic_auto/self-referential.rs index 905aa20918bef..ecdbdf41b2025 100644 --- a/src/test/rustdoc/synthetic_auto/self-referential.rs +++ b/src/test/rustdoc/synthetic_auto/self-referential.rs @@ -23,7 +23,7 @@ impl Pattern for Wrapper { // @has self_referential/struct.WriteAndThen.html -// @has - '//*[@id="synthetic-implementations-list"]/*[@class="impl"]//code' "impl Send for \ +// @has - '//*[@id="synthetic-implementations-list"]//*[@class="impl"]//code' "impl Send for \ // WriteAndThen where ::Value: Send" pub struct WriteAndThen(pub P1::Value,pub > as Pattern>::Value) where P1: Pattern; diff --git a/src/test/rustdoc/synthetic_auto/static-region.rs b/src/test/rustdoc/synthetic_auto/static-region.rs index 59493744b623d..a10e694c1b281 100644 --- a/src/test/rustdoc/synthetic_auto/static-region.rs +++ b/src/test/rustdoc/synthetic_auto/static-region.rs @@ -3,7 +3,7 @@ pub trait OwnedTrait<'a> { } // @has static_region/struct.Owned.html -// @has - '//*[@id="synthetic-implementations-list"]/*[@class="impl"]//code' "impl Send for \ +// @has - '//*[@id="synthetic-implementations-list"]//*[@class="impl"]//code' "impl Send for \ // Owned where >::Reader: Send" pub struct Owned where T: OwnedTrait<'static> { marker: >::Reader, diff --git a/src/test/rustdoc/tab_title.rs b/src/test/rustdoc/tab_title.rs new file mode 100644 index 0000000000000..7dce6092deaed --- /dev/null +++ b/src/test/rustdoc/tab_title.rs @@ -0,0 +1,44 @@ +#![crate_name = "foo"] +#![feature(doc_keyword)] + +// tests for the html element + +// @has foo/index.html '//head/title' 'foo - Rust' + +// @has foo/fn.widget_count.html '//head/title' 'widget_count in foo - Rust' +/// blah +pub fn widget_count() {} + +// @has foo/struct.Widget.html '//head/title' 'Widget in foo - Rust' +pub struct Widget; + +// @has foo/constant.ANSWER.html '//head/title' 'ANSWER in foo - Rust' +pub const ANSWER: u8 = 42; + +// @has foo/blah/index.html '//head/title' 'foo::blah - Rust' +pub mod blah { + // @has foo/blah/struct.Widget.html '//head/title' 'Widget in foo::blah - Rust' + pub struct Widget; + + // @has foo/blah/trait.Awesome.html '//head/title' 'Awesome in foo::blah - Rust' + pub trait Awesome {} + + // @has foo/blah/fn.make_widget.html '//head/title' 'make_widget in foo::blah - Rust' + pub fn make_widget() {} + + // @has foo/macro.cool_macro.html '//head/title' 'cool_macro in foo - Rust' + #[macro_export] + macro_rules! cool_macro { + ($t:tt) => { $t } + } +} + +// @has foo/keyword.continue.html '//head/title' 'continue - Rust' +#[doc(keyword = "continue")] +mod continue_keyword {} + +// @has foo/primitive.u8.html '//head/title' 'u8 - Rust' +// @!has - '//head/title' 'foo' +#[doc(primitive = "u8")] +/// `u8` docs +mod u8 {} diff --git a/src/test/rustdoc/trait-attributes.rs b/src/test/rustdoc/trait-attributes.rs index a6ee046edec8b..2bb24a82193fe 100644 --- a/src/test/rustdoc/trait-attributes.rs +++ b/src/test/rustdoc/trait-attributes.rs @@ -1,9 +1,8 @@ #![crate_name = "foo"] -// ignore-tidy-linelength pub trait Foo { - // @has foo/trait.Foo.html '//h3[@id="tymethod.foo"]//span[@class="docblock attributes"]' '#[must_use]' + // @has foo/trait.Foo.html '//h3[@id="tymethod.foo"]//div[@class="code-attribute"]' '#[must_use]' #[must_use] fn foo(); } @@ -12,11 +11,11 @@ pub trait Foo { pub struct Bar; impl Bar { - // @has foo/struct.Bar.html '//h4[@id="method.bar"]//span[@class="docblock attributes"]' '#[must_use]' + // @has foo/struct.Bar.html '//h4[@id="method.bar"]//div[@class="code-attribute"]' '#[must_use]' #[must_use] pub fn bar() {} - // @has foo/struct.Bar.html '//h4[@id="method.bar2"]//span[@class="docblock attributes"]' '#[must_use]' + // @has foo/struct.Bar.html '//h4[@id="method.bar2"]//div[@class="code-attribute"]' '#[must_use]' #[must_use] pub fn bar2() {} } diff --git a/src/test/rustdoc/trait-impl-items-links-and-anchors.rs b/src/test/rustdoc/trait-impl-items-links-and-anchors.rs new file mode 100644 index 0000000000000..c6a9313e821c9 --- /dev/null +++ b/src/test/rustdoc/trait-impl-items-links-and-anchors.rs @@ -0,0 +1,65 @@ +pub trait MyTrait { + type Assoc; + const VALUE: u32; + fn trait_function(&self); + fn defaulted(&self) {} + fn defaulted_override(&self) {} +} + + +impl MyTrait for String { + // @has trait_impl_items_links_and_anchors/trait.MyTrait.html '//h4[@id="associatedtype.Assoc-1"]//a[@class="type"]/@href' #associatedtype.Assoc + // @has trait_impl_items_links_and_anchors/trait.MyTrait.html '//h4[@id="associatedtype.Assoc-1"]//a[@class="anchor"]/@href' #associatedtype.Assoc-1 + type Assoc = (); + // @has trait_impl_items_links_and_anchors/trait.MyTrait.html '//h4[@id="associatedconstant.VALUE-1"]//a[@class="constant"]/@href' #associatedconstant.VALUE + // @has trait_impl_items_links_and_anchors/trait.MyTrait.html '//h4[@id="associatedconstant.VALUE-1"]//a[@class="anchor"]/@href' #associatedconstant.VALUE-1 + const VALUE: u32 = 5; + // @has trait_impl_items_links_and_anchors/trait.MyTrait.html '//h4[@id="method.trait_function"]//a[@class="fnname"]/@href' #tymethod.trait_function + // @has trait_impl_items_links_and_anchors/trait.MyTrait.html '//h4[@id="method.trait_function"]//a[@class="anchor"]/@href' #method.trait_function + fn trait_function(&self) {} + // @has trait_impl_items_links_and_anchors/trait.MyTrait.html '//h4[@id="method.defaulted_override-1"]//a[@class="fnname"]/@href' #method.defaulted_override + // @has trait_impl_items_links_and_anchors/trait.MyTrait.html '//h4[@id="method.defaulted_override-1"]//a[@class="anchor"]/@href' #method.defaulted_override-1 + fn defaulted_override(&self) {} +} + +impl MyTrait for Vec<u8> { + // @has trait_impl_items_links_and_anchors/trait.MyTrait.html '//h4[@id="associatedtype.Assoc-2"]//a[@class="type"]/@href' #associatedtype.Assoc + // @has trait_impl_items_links_and_anchors/trait.MyTrait.html '//h4[@id="associatedtype.Assoc-2"]//a[@class="anchor"]/@href' #associatedtype.Assoc-2 + type Assoc = (); + // @has trait_impl_items_links_and_anchors/trait.MyTrait.html '//h4[@id="associatedconstant.VALUE-2"]//a[@class="constant"]/@href' #associatedconstant.VALUE + // @has trait_impl_items_links_and_anchors/trait.MyTrait.html '//h4[@id="associatedconstant.VALUE-2"]//a[@class="anchor"]/@href' #associatedconstant.VALUE-2 + const VALUE: u32 = 5; + // @has trait_impl_items_links_and_anchors/trait.MyTrait.html '//h4[@id="method.trait_function"]//a[@class="fnname"]/@href' #tymethod.trait_function + // @has trait_impl_items_links_and_anchors/trait.MyTrait.html '//h4[@id="method.trait_function-1"]//a[@class="anchor"]/@href' #method.trait_function-1 + fn trait_function(&self) {} + // @has trait_impl_items_links_and_anchors/trait.MyTrait.html '//h4[@id="method.defaulted_override-2"]//a[@class="fnname"]/@href' #method.defaulted_override + // @has trait_impl_items_links_and_anchors/trait.MyTrait.html '//h4[@id="method.defaulted_override-2"]//a[@class="anchor"]/@href' #method.defaulted_override-2 + fn defaulted_override(&self) {} +} + +impl MyTrait for MyStruct { + // @has trait_impl_items_links_and_anchors/trait.MyTrait.html '//h4[@id="associatedtype.Assoc-3"]//a[@class="type"]/@href' #associatedtype.Assoc + // @has trait_impl_items_links_and_anchors/trait.MyTrait.html '//h4[@id="associatedtype.Assoc-3"]//a[@class="anchor"]/@href' #associatedtype.Assoc-3 + // @has trait_impl_items_links_and_anchors/struct.MyStruct.html '//h4[@id="associatedtype.Assoc"]//a[@class="type"]/@href' trait.MyTrait.html#associatedtype.Assoc + // @has trait_impl_items_links_and_anchors/struct.MyStruct.html '//h4[@id="associatedtype.Assoc"]//a[@class="anchor"]/@href' #associatedtype.Assoc + type Assoc = bool; + // @has trait_impl_items_links_and_anchors/trait.MyTrait.html '//h4[@id="associatedconstant.VALUE-3"]//a[@class="constant"]/@href' #associatedconstant.VALUE + // @has trait_impl_items_links_and_anchors/trait.MyTrait.html '//h4[@id="associatedconstant.VALUE-3"]//a[@class="anchor"]/@href' #associatedconstant.VALUE-3 + // @has trait_impl_items_links_and_anchors/struct.MyStruct.html '//h4[@id="associatedconstant.VALUE"]//a[@class="constant"]/@href' trait.MyTrait.html#associatedconstant.VALUE + // @has trait_impl_items_links_and_anchors/struct.MyStruct.html '//h4[@id="associatedconstant.VALUE"]//a[@class="anchor"]/@href' #associatedconstant.VALUE + const VALUE: u32 = 20; + // @has trait_impl_items_links_and_anchors/trait.MyTrait.html '//h4[@id="method.trait_function-2"]//a[@class="fnname"]/@href' #tymethod.trait_function + // @has trait_impl_items_links_and_anchors/trait.MyTrait.html '//h4[@id="method.trait_function-2"]//a[@class="anchor"]/@href' #method.trait_function-2 + // @has trait_impl_items_links_and_anchors/struct.MyStruct.html '//h4[@id="method.trait_function"]//a[@class="fnname"]/@href' trait.MyTrait.html#tymethod.trait_function + // @has trait_impl_items_links_and_anchors/struct.MyStruct.html '//h4[@id="method.trait_function"]//a[@class="anchor"]/@href' #method.trait_function + fn trait_function(&self) {} + // @has trait_impl_items_links_and_anchors/trait.MyTrait.html '//h4[@id="method.defaulted_override-3"]//a[@class="fnname"]/@href' #method.defaulted_override + // @has trait_impl_items_links_and_anchors/trait.MyTrait.html '//h4[@id="method.defaulted_override-3"]//a[@class="anchor"]/@href' #method.defaulted_override-3 + // @has trait_impl_items_links_and_anchors/struct.MyStruct.html '//h4[@id="method.defaulted_override"]//a[@class="fnname"]/@href' trait.MyTrait.html#method.defaulted_override + // @has trait_impl_items_links_and_anchors/struct.MyStruct.html '//h4[@id="method.defaulted_override"]//a[@class="anchor"]/@href' #method.defaulted_override + fn defaulted_override(&self) {} + // @has trait_impl_items_links_and_anchors/struct.MyStruct.html '//h4[@id="method.defaulted"]//a[@class="fnname"]/@href' trait.MyTrait.html#method.defaulted + // @has trait_impl_items_links_and_anchors/struct.MyStruct.html '//h4[@id="method.defaulted"]//a[@class="anchor"]/@href' #method.defaulted +} + +pub struct MyStruct; diff --git a/src/test/rustdoc/trait-self-link.rs b/src/test/rustdoc/trait-self-link.rs index bac28b44012cc..e311dadff0e63 100644 --- a/src/test/rustdoc/trait-self-link.rs +++ b/src/test/rustdoc/trait-self-link.rs @@ -1,4 +1,4 @@ -// @has trait_self_link/trait.Foo.html //a/@href ../trait_self_link/trait.Foo.html +// @has trait_self_link/trait.Foo.html //a/@href trait.Foo.html pub trait Foo {} pub struct Bar; diff --git a/src/test/rustdoc/unit-return.rs b/src/test/rustdoc/unit-return.rs index b1f251dae6eed..ae3a6031519fb 100644 --- a/src/test/rustdoc/unit-return.rs +++ b/src/test/rustdoc/unit-return.rs @@ -10,8 +10,8 @@ pub fn f0<F: FnMut(u8) + Clone>(f: F) {} // @has 'foo/fn.f1.html' '//*[@class="rust fn"]' 'F: FnMut(u16) + Clone' pub fn f1<F: FnMut(u16) -> () + Clone>(f: F) {} -// @has 'foo/fn.f2.html' '//*[@class="rust fn"]' 'F: Clone + FnMut(u32)' +// @has 'foo/fn.f2.html' '//*[@class="rust fn"]' 'F: FnMut(u32) + Clone' pub use unit_return::f2; -// @has 'foo/fn.f3.html' '//*[@class="rust fn"]' 'F: Clone + FnMut(u64)' +// @has 'foo/fn.f3.html' '//*[@class="rust fn"]' 'F: FnMut(u64) + Clone' pub use unit_return::f3; diff --git a/src/test/ui-fulldeps/auxiliary/issue-40001-plugin.rs b/src/test/ui-fulldeps/auxiliary/issue-40001-plugin.rs index 4e9d4d342734c..124dd670d0931 100644 --- a/src/test/ui-fulldeps/auxiliary/issue-40001-plugin.rs +++ b/src/test/ui-fulldeps/auxiliary/issue-40001-plugin.rs @@ -49,7 +49,7 @@ impl<'tcx> LateLintPass<'tcx> for MissingAllowedAttrPass { }; let allowed = |attr| pprust::attribute_to_string(attr).contains("allowed_attr"); - if !item.attrs.iter().any(allowed) { + if !cx.tcx.hir().attrs(item.hir_id()).iter().any(allowed) { cx.lint(MISSING_ALLOWED_ATTR, |lint| { lint.build("Missing 'allowed_attr' attribute").set_span(span).emit() }); diff --git a/src/test/ui-fulldeps/auxiliary/lint-for-crate-rpass.rs b/src/test/ui-fulldeps/auxiliary/lint-for-crate-rpass.rs index 736a8633dac53..51cea4f6ba91b 100644 --- a/src/test/ui-fulldeps/auxiliary/lint-for-crate-rpass.rs +++ b/src/test/ui-fulldeps/auxiliary/lint-for-crate-rpass.rs @@ -28,11 +28,12 @@ macro_rules! fake_lint_pass { impl LateLintPass<'_> for $struct { fn check_crate(&mut self, cx: &LateContext, krate: &rustc_hir::Crate) { + let attrs = cx.tcx.hir().attrs(rustc_hir::CRATE_HIR_ID); $( - if !cx.sess().contains_name(&krate.item.attrs, $attr) { + if !cx.sess().contains_name(attrs, $attr) { cx.lint(CRATE_NOT_OKAY, |lint| { let msg = format!("crate is not marked with #![{}]", $attr); - lint.build(&msg).set_span(krate.item.span).emit() + lint.build(&msg).set_span(krate.item.inner).emit() }); } )* diff --git a/src/test/ui-fulldeps/auxiliary/lint-for-crate.rs b/src/test/ui-fulldeps/auxiliary/lint-for-crate.rs index bd477b793fc70..ef5353e6d8cf1 100644 --- a/src/test/ui-fulldeps/auxiliary/lint-for-crate.rs +++ b/src/test/ui-fulldeps/auxiliary/lint-for-crate.rs @@ -27,10 +27,11 @@ declare_lint_pass!(Pass => [CRATE_NOT_OKAY]); impl<'tcx> LateLintPass<'tcx> for Pass { fn check_crate(&mut self, cx: &LateContext, krate: &rustc_hir::Crate) { - if !cx.sess().contains_name(&krate.item.attrs, Symbol::intern("crate_okay")) { + let attrs = cx.tcx.hir().attrs(rustc_hir::CRATE_HIR_ID); + if !cx.sess().contains_name(attrs, Symbol::intern("crate_okay")) { cx.lint(CRATE_NOT_OKAY, |lint| { lint.build("crate is not marked with #![crate_okay]") - .set_span(krate.item.span) + .set_span(krate.item.inner) .emit() }); } diff --git a/src/test/ui-fulldeps/pprust-expr-roundtrip.rs b/src/test/ui-fulldeps/pprust-expr-roundtrip.rs index bff92d8607ece..ac2d29c9caf92 100644 --- a/src/test/ui-fulldeps/pprust-expr-roundtrip.rs +++ b/src/test/ui-fulldeps/pprust-expr-roundtrip.rs @@ -155,7 +155,9 @@ fn iter_exprs(depth: usize, f: &mut dyn FnMut(P<Expr>)) { }, 17 => { let path = Path::from_ident(Ident::from_str("S")); - g(ExprKind::Struct(path, vec![], StructRest::Base(make_x()))); + g(ExprKind::Struct(P(StructExpr { + path, fields: vec![], rest: StructRest::Base(make_x()) + }))); }, 18 => { iter_exprs(depth - 1, &mut |e| g(ExprKind::Try(e))); diff --git a/src/test/ui/nullable-pointer-ffi-compat.rs b/src/test/ui/abi/nullable-pointer-ffi-compat.rs similarity index 100% rename from src/test/ui/nullable-pointer-ffi-compat.rs rename to src/test/ui/abi/nullable-pointer-ffi-compat.rs diff --git a/src/test/ui/allocator/no_std-alloc-error-handler-custom.rs b/src/test/ui/allocator/no_std-alloc-error-handler-custom.rs index 4d40c7d0d2237..c9b4abbfd3fd3 100644 --- a/src/test/ui/allocator/no_std-alloc-error-handler-custom.rs +++ b/src/test/ui/allocator/no_std-alloc-error-handler-custom.rs @@ -7,7 +7,7 @@ // compile-flags:-C panic=abort // aux-build:helper.rs -#![feature(start, rustc_private, new_uninit, panic_info_message)] +#![feature(start, rustc_private, new_uninit, panic_info_message, lang_items)] #![feature(alloc_error_handler)] #![no_std] @@ -84,6 +84,13 @@ fn panic(panic_info: &core::panic::PanicInfo) -> ! { } } +// Because we are compiling this code with `-C panic=abort`, this wouldn't normally be needed. +// However, `core` and `alloc` are both compiled with `-C panic=unwind`, which means that functions +// in these libaries will refer to `rust_eh_personality` if LLVM can not *prove* the contents won't +// unwind. So, for this test case we will define the symbol. +#[lang = "eh_personality"] +extern fn rust_eh_personality() {} + #[derive(Debug)] struct Page([[u64; 32]; 16]); diff --git a/src/test/ui/allocator/no_std-alloc-error-handler-default.rs b/src/test/ui/allocator/no_std-alloc-error-handler-default.rs index 4f8c44f1763c8..d6cd4a6af855f 100644 --- a/src/test/ui/allocator/no_std-alloc-error-handler-default.rs +++ b/src/test/ui/allocator/no_std-alloc-error-handler-default.rs @@ -8,7 +8,7 @@ // aux-build:helper.rs // gate-test-default_alloc_error_handler -#![feature(start, rustc_private, new_uninit, panic_info_message)] +#![feature(start, rustc_private, new_uninit, panic_info_message, lang_items)] #![feature(default_alloc_error_handler)] #![no_std] @@ -71,6 +71,13 @@ fn panic(panic_info: &core::panic::PanicInfo) -> ! { } } +// Because we are compiling this code with `-C panic=abort`, this wouldn't normally be needed. +// However, `core` and `alloc` are both compiled with `-C panic=unwind`, which means that functions +// in these libaries will refer to `rust_eh_personality` if LLVM can not *prove* the contents won't +// unwind. So, for this test case we will define the symbol. +#[lang = "eh_personality"] +extern fn rust_eh_personality() {} + #[derive(Debug)] struct Page([[u64; 32]; 16]); diff --git a/src/test/ui/anon-params/anon-params-denied-2018.rs b/src/test/ui/anon-params/anon-params-denied-2018.rs index 5721f5d235783..95533cf3dfbf1 100644 --- a/src/test/ui/anon-params/anon-params-denied-2018.rs +++ b/src/test/ui/anon-params/anon-params-denied-2018.rs @@ -5,6 +5,20 @@ trait T { fn foo(i32); //~ expected one of `:`, `@`, or `|`, found `)` + // Also checks with `&` + fn foo_with_ref(&mut i32); + //~^ ERROR expected one of `:`, `@`, or `|`, found `)` + + fn foo_with_qualified_path(<Bar as T>::Baz); + //~^ ERROR expected one of `(`, `...`, `..=`, `..`, `::`, `:`, `{`, or `|`, found `)` + + fn foo_with_qualified_path_and_ref(&<Bar as T>::Baz); + //~^ ERROR expected one of `(`, `...`, `..=`, `..`, `::`, `:`, `{`, or `|`, found `)` + + fn foo_with_multiple_qualified_paths(<Bar as T>::Baz, <Bar as T>::Baz); + //~^ ERROR expected one of `(`, `...`, `..=`, `..`, `::`, `:`, `{`, or `|`, found `,` + //~| ERROR expected one of `(`, `...`, `..=`, `..`, `::`, `:`, `{`, or `|`, found `)` + fn bar_with_default_impl(String, String) {} //~^ ERROR expected one of `:` //~| ERROR expected one of `:` diff --git a/src/test/ui/anon-params/anon-params-denied-2018.stderr b/src/test/ui/anon-params/anon-params-denied-2018.stderr index 840294db0830a..b53640cd65ba9 100644 --- a/src/test/ui/anon-params/anon-params-denied-2018.stderr +++ b/src/test/ui/anon-params/anon-params-denied-2018.stderr @@ -18,8 +18,76 @@ help: if this is a type, explicitly ignore the parameter name LL | fn foo(_: i32); | ^^^^^^ +error: expected one of `:`, `@`, or `|`, found `)` + --> $DIR/anon-params-denied-2018.rs:9:29 + | +LL | fn foo_with_ref(&mut i32); + | ^ expected one of `:`, `@`, or `|` + | + = note: anonymous parameters are removed in the 2018 edition (see RFC 1685) +help: if this is a `self` type, give it a parameter name + | +LL | fn foo_with_ref(self: &mut i32); + | ^^^^^^^^^^^^^^ +help: if this is a parameter name, give it a type + | +LL | fn foo_with_ref(i32: &mut TypeName); + | ^^^^^^^^^^^^^^^^^^ +help: if this is a type, explicitly ignore the parameter name + | +LL | fn foo_with_ref(_: &mut i32); + | ^^^^^^^^^^^ + +error: expected one of `(`, `...`, `..=`, `..`, `::`, `:`, `{`, or `|`, found `)` + --> $DIR/anon-params-denied-2018.rs:12:47 + | +LL | fn foo_with_qualified_path(<Bar as T>::Baz); + | ^ expected one of 8 possible tokens + | + = note: anonymous parameters are removed in the 2018 edition (see RFC 1685) +help: explicitly ignore the parameter name + | +LL | fn foo_with_qualified_path(_: <Bar as T>::Baz); + | ^^^^^^^^^^^^^^^^^^ + +error: expected one of `(`, `...`, `..=`, `..`, `::`, `:`, `{`, or `|`, found `)` + --> $DIR/anon-params-denied-2018.rs:15:56 + | +LL | fn foo_with_qualified_path_and_ref(&<Bar as T>::Baz); + | ^ expected one of 8 possible tokens + | + = note: anonymous parameters are removed in the 2018 edition (see RFC 1685) +help: explicitly ignore the parameter name + | +LL | fn foo_with_qualified_path_and_ref(_: &<Bar as T>::Baz); + | ^^^^^^^^^^^^^^^^^^^ + +error: expected one of `(`, `...`, `..=`, `..`, `::`, `:`, `{`, or `|`, found `,` + --> $DIR/anon-params-denied-2018.rs:18:57 + | +LL | fn foo_with_multiple_qualified_paths(<Bar as T>::Baz, <Bar as T>::Baz); + | ^ expected one of 8 possible tokens + | + = note: anonymous parameters are removed in the 2018 edition (see RFC 1685) +help: explicitly ignore the parameter name + | +LL | fn foo_with_multiple_qualified_paths(_: <Bar as T>::Baz, <Bar as T>::Baz); + | ^^^^^^^^^^^^^^^^^^ + +error: expected one of `(`, `...`, `..=`, `..`, `::`, `:`, `{`, or `|`, found `)` + --> $DIR/anon-params-denied-2018.rs:18:74 + | +LL | fn foo_with_multiple_qualified_paths(<Bar as T>::Baz, <Bar as T>::Baz); + | ^ expected one of 8 possible tokens + | + = note: anonymous parameters are removed in the 2018 edition (see RFC 1685) +help: explicitly ignore the parameter name + | +LL | fn foo_with_multiple_qualified_paths(<Bar as T>::Baz, _: <Bar as T>::Baz); + | ^^^^^^^^^^^^^^^^^^ + error: expected one of `:`, `@`, or `|`, found `,` - --> $DIR/anon-params-denied-2018.rs:8:36 + --> $DIR/anon-params-denied-2018.rs:22:36 | LL | fn bar_with_default_impl(String, String) {} | ^ expected one of `:`, `@`, or `|` @@ -39,7 +107,7 @@ LL | fn bar_with_default_impl(_: String, String) {} | ^^^^^^^^^ error: expected one of `:`, `@`, or `|`, found `)` - --> $DIR/anon-params-denied-2018.rs:8:44 + --> $DIR/anon-params-denied-2018.rs:22:44 | LL | fn bar_with_default_impl(String, String) {} | ^ expected one of `:`, `@`, or `|` @@ -55,7 +123,7 @@ LL | fn bar_with_default_impl(String, _: String) {} | ^^^^^^^^^ error: expected one of `:`, `@`, or `|`, found `,` - --> $DIR/anon-params-denied-2018.rs:13:22 + --> $DIR/anon-params-denied-2018.rs:27:22 | LL | fn baz(a:usize, b, c: usize) -> usize { | ^ expected one of `:`, `@`, or `|` @@ -70,5 +138,5 @@ help: if this is a type, explicitly ignore the parameter name LL | fn baz(a:usize, _: b, c: usize) -> usize { | ^^^^ -error: aborting due to 4 previous errors +error: aborting due to 9 previous errors diff --git a/src/test/ui/anon-params/anon-params-edition-hygiene.rs b/src/test/ui/anon-params/anon-params-edition-hygiene.rs index 14e11c5696f4a..6936205f8b96d 100644 --- a/src/test/ui/anon-params/anon-params-edition-hygiene.rs +++ b/src/test/ui/anon-params/anon-params-edition-hygiene.rs @@ -2,6 +2,9 @@ // edition:2018 // aux-build:anon-params-edition-hygiene.rs +// This warning is still surfaced +#![allow(anonymous_parameters)] + #[macro_use] extern crate anon_params_edition_hygiene; diff --git a/src/test/ui/asm/bad-reg.rs b/src/test/ui/asm/bad-reg.rs index 016ea9329c4d0..da302b248760f 100644 --- a/src/test/ui/asm/bad-reg.rs +++ b/src/test/ui/asm/bad-reg.rs @@ -37,6 +37,8 @@ fn main() { //~^ ERROR invalid register `mm0`: MMX registers are not currently supported as operands asm!("", in("k0") foo); //~^ ERROR invalid register `k0`: the k0 AVX mask register cannot be used as an operand + asm!("", in("ah") foo); + //~^ ERROR invalid register `ah`: high byte registers cannot be used as an operand // Explicit register conflicts // (except in/lateout which don't conflict) diff --git a/src/test/ui/asm/bad-reg.stderr b/src/test/ui/asm/bad-reg.stderr index c6b7d310dfa6c..2bfb4854c3442 100644 --- a/src/test/ui/asm/bad-reg.stderr +++ b/src/test/ui/asm/bad-reg.stderr @@ -94,8 +94,14 @@ error: invalid register `k0`: the k0 AVX mask register cannot be used as an oper LL | asm!("", in("k0") foo); | ^^^^^^^^^^^^ +error: invalid register `ah`: high byte registers cannot be used as an operand on x86_64 + --> $DIR/bad-reg.rs:40:18 + | +LL | asm!("", in("ah") foo); + | ^^^^^^^^^^^^ + error: register `al` conflicts with register `ax` - --> $DIR/bad-reg.rs:44:33 + --> $DIR/bad-reg.rs:46:33 | LL | asm!("", in("eax") foo, in("al") bar); | ------------- ^^^^^^^^^^^^ register `al` @@ -103,7 +109,7 @@ LL | asm!("", in("eax") foo, in("al") bar); | register `ax` error: register `ax` conflicts with register `ax` - --> $DIR/bad-reg.rs:46:33 + --> $DIR/bad-reg.rs:48:33 | LL | asm!("", in("rax") foo, out("rax") bar); | ------------- ^^^^^^^^^^^^^^ register `ax` @@ -111,13 +117,13 @@ LL | asm!("", in("rax") foo, out("rax") bar); | register `ax` | help: use `lateout` instead of `out` to avoid conflict - --> $DIR/bad-reg.rs:46:18 + --> $DIR/bad-reg.rs:48:18 | LL | asm!("", in("rax") foo, out("rax") bar); | ^^^^^^^^^^^^^ error: register `ymm0` conflicts with register `xmm0` - --> $DIR/bad-reg.rs:49:34 + --> $DIR/bad-reg.rs:51:34 | LL | asm!("", in("xmm0") foo, in("ymm0") bar); | -------------- ^^^^^^^^^^^^^^ register `ymm0` @@ -125,7 +131,7 @@ LL | asm!("", in("xmm0") foo, in("ymm0") bar); | register `xmm0` error: register `ymm0` conflicts with register `xmm0` - --> $DIR/bad-reg.rs:51:34 + --> $DIR/bad-reg.rs:53:34 | LL | asm!("", in("xmm0") foo, out("ymm0") bar); | -------------- ^^^^^^^^^^^^^^^ register `ymm0` @@ -133,10 +139,10 @@ LL | asm!("", in("xmm0") foo, out("ymm0") bar); | register `xmm0` | help: use `lateout` instead of `out` to avoid conflict - --> $DIR/bad-reg.rs:51:18 + --> $DIR/bad-reg.rs:53:18 | LL | asm!("", in("xmm0") foo, out("ymm0") bar); | ^^^^^^^^^^^^^^ -error: aborting due to 18 previous errors +error: aborting due to 19 previous errors diff --git a/src/test/ui/asm/const.rs b/src/test/ui/asm/const.rs index e08da24f44a22..0f6a7cd44749e 100644 --- a/src/test/ui/asm/const.rs +++ b/src/test/ui/asm/const.rs @@ -1,41 +1,21 @@ -// no-system-llvm +// min-llvm-version: 10.0.1 // only-x86_64 // run-pass #![feature(asm)] -use std::mem::size_of; - -trait Proj { - const C: usize; -} -impl Proj for i8 { - const C: usize = 8; -} -impl Proj for i16 { - const C: usize = 16; +fn const_generic<const X: usize>() -> usize { + unsafe { + let a: usize; + asm!("mov {}, {}", out(reg) a, const X); + a + } } const fn constfn(x: usize) -> usize { x } -fn generic<T: Proj>() { - unsafe { - let a: usize; - asm!("mov {}, {}", out(reg) a, const size_of::<T>()); - assert_eq!(a, size_of::<T>()); - - let b: usize; - asm!("mov {}, {}", out(reg) b, const size_of::<T>() + constfn(5)); - assert_eq!(b, size_of::<T>() + 5); - - let c: usize; - asm!("mov {}, {}", out(reg) c, const T::C); - assert_eq!(c, T::C); - } -} - fn main() { unsafe { let a: usize; @@ -51,6 +31,6 @@ fn main() { assert_eq!(c, 10); } - generic::<i8>(); - generic::<i16>(); + let d = const_generic::<5>(); + assert_eq!(d, 5); } diff --git a/src/test/ui/asm/inline-syntax.arm.stderr b/src/test/ui/asm/inline-syntax.arm.stderr new file mode 100644 index 0000000000000..bcae1d565f136 --- /dev/null +++ b/src/test/ui/asm/inline-syntax.arm.stderr @@ -0,0 +1,74 @@ +error: unknown directive + --> $DIR/inline-syntax.rs:25:15 + | +LL | asm!(".intel_syntax noprefix", "nop"); + | ^ + | +note: instantiated into assembly here + --> <inline asm>:1:2 + | +LL | .intel_syntax noprefix + | ^ + +error: unknown directive + --> $DIR/inline-syntax.rs:28:15 + | +LL | asm!(".intel_syntax aaa noprefix", "nop"); + | ^ + | +note: instantiated into assembly here + --> <inline asm>:1:2 + | +LL | .intel_syntax aaa noprefix + | ^ + +error: unknown directive + --> $DIR/inline-syntax.rs:31:15 + | +LL | asm!(".att_syntax noprefix", "nop"); + | ^ + | +note: instantiated into assembly here + --> <inline asm>:1:2 + | +LL | .att_syntax noprefix + | ^ + +error: unknown directive + --> $DIR/inline-syntax.rs:34:15 + | +LL | asm!(".att_syntax bbb noprefix", "nop"); + | ^ + | +note: instantiated into assembly here + --> <inline asm>:1:2 + | +LL | .att_syntax bbb noprefix + | ^ + +error: unknown directive + --> $DIR/inline-syntax.rs:37:15 + | +LL | asm!(".intel_syntax noprefix; nop"); + | ^ + | +note: instantiated into assembly here + --> <inline asm>:1:2 + | +LL | .intel_syntax noprefix; nop + | ^ + +error: unknown directive + --> $DIR/inline-syntax.rs:43:13 + | +LL | .intel_syntax noprefix + | ^ + | +note: instantiated into assembly here + --> <inline asm>:2:13 + | +LL | .intel_syntax noprefix + | ^ + +error: aborting due to 6 previous errors + diff --git a/src/test/ui/asm/inline-syntax.rs b/src/test/ui/asm/inline-syntax.rs new file mode 100644 index 0000000000000..13ded19cfea91 --- /dev/null +++ b/src/test/ui/asm/inline-syntax.rs @@ -0,0 +1,49 @@ +// needs-llvm-components: arm +// revisions: x86_64 arm +//[x86_64] compile-flags: --target x86_64-unknown-linux-gnu +//[x86_64] check-pass +//[x86_64_allowed] compile-flags: --target x86_64-unknown-linux-gnu +//[x86_64_allowed] check-pass +//[arm] compile-flags: --target armv7-unknown-linux-gnueabihf +//[arm] build-fail + +#![feature(no_core, lang_items, rustc_attrs)] +#![crate_type = "rlib"] +#![no_core] +#![cfg_attr(x86_64_allowed, allow(bad_asm_style))] + +#[rustc_builtin_macro] +macro_rules! asm { + () => {}; +} + +#[lang = "sized"] +trait Sized {} + +pub fn main() { + unsafe { + asm!(".intel_syntax noprefix", "nop"); + //[x86_64]~^ WARN avoid using `.intel_syntax` + //[arm]~^^ ERROR unknown directive + asm!(".intel_syntax aaa noprefix", "nop"); + //[x86_64]~^ WARN avoid using `.intel_syntax` + //[arm]~^^ ERROR unknown directive + asm!(".att_syntax noprefix", "nop"); + //[x86_64]~^ WARN avoid using `.att_syntax` + //[arm]~^^ ERROR unknown directive + asm!(".att_syntax bbb noprefix", "nop"); + //[x86_64]~^ WARN avoid using `.att_syntax` + //[arm]~^^ ERROR unknown directive + asm!(".intel_syntax noprefix; nop"); + //[x86_64]~^ WARN avoid using `.intel_syntax` + //[arm]~^^ ERROR unknown directive + + asm!( + r" + .intel_syntax noprefix + nop" + ); + //[x86_64]~^^^ WARN avoid using `.intel_syntax` + //[arm]~^^^^ ERROR unknown directive + } +} diff --git a/src/test/ui/asm/inline-syntax.x86_64.stderr b/src/test/ui/asm/inline-syntax.x86_64.stderr new file mode 100644 index 0000000000000..02b29b09013dd --- /dev/null +++ b/src/test/ui/asm/inline-syntax.x86_64.stderr @@ -0,0 +1,40 @@ +warning: avoid using `.intel_syntax`, Intel syntax is the default + --> $DIR/inline-syntax.rs:25:15 + | +LL | asm!(".intel_syntax noprefix", "nop"); + | ^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `#[warn(bad_asm_style)]` on by default + +warning: avoid using `.intel_syntax`, Intel syntax is the default + --> $DIR/inline-syntax.rs:28:15 + | +LL | asm!(".intel_syntax aaa noprefix", "nop"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + +warning: avoid using `.att_syntax`, prefer using `options(att_syntax)` instead + --> $DIR/inline-syntax.rs:31:15 + | +LL | asm!(".att_syntax noprefix", "nop"); + | ^^^^^^^^^^^^^^^^^^^^ + +warning: avoid using `.att_syntax`, prefer using `options(att_syntax)` instead + --> $DIR/inline-syntax.rs:34:15 + | +LL | asm!(".att_syntax bbb noprefix", "nop"); + | ^^^^^^^^^^^^^^^^^^^^^^^^ + +warning: avoid using `.intel_syntax`, Intel syntax is the default + --> $DIR/inline-syntax.rs:37:15 + | +LL | asm!(".intel_syntax noprefix; nop"); + | ^^^^^^^^^^^^^^^^^^^^^^ + +warning: avoid using `.intel_syntax`, Intel syntax is the default + --> $DIR/inline-syntax.rs:43:13 + | +LL | .intel_syntax noprefix + | ^^^^^^^^^^^^^^^^^^^^^^ + +warning: 6 warnings emitted + diff --git a/src/test/ui/asm/naked-invalid-attr.rs b/src/test/ui/asm/naked-invalid-attr.rs index cdb6c17454b73..2576d1124c85c 100644 --- a/src/test/ui/asm/naked-invalid-attr.rs +++ b/src/test/ui/asm/naked-invalid-attr.rs @@ -1,6 +1,6 @@ // Checks that #[naked] attribute can be placed on function definitions only. // -// ignore-wasm32 asm unsupported +// needs-asm-support #![feature(asm)] #![feature(naked_functions)] #![naked] //~ ERROR should be applied to a function definition diff --git a/src/test/ui/asm/parse-error.rs b/src/test/ui/asm/parse-error.rs index 538a3fde8fdeb..f2e9d9ca08b24 100644 --- a/src/test/ui/asm/parse-error.rs +++ b/src/test/ui/asm/parse-error.rs @@ -36,17 +36,23 @@ fn main() { //~^ ERROR expected one of asm!("{}", options(), const foo); //~^ ERROR arguments are not allowed after options + //~^^ ERROR attempt to use a non-constant value in a constant asm!("{a}", a = const foo, a = const bar); //~^ ERROR duplicate argument named `a` //~^^ ERROR argument never used + //~^^^ ERROR attempt to use a non-constant value in a constant + //~^^^^ ERROR attempt to use a non-constant value in a constant asm!("", a = in("eax") foo); //~^ ERROR explicit register arguments cannot have names asm!("{a}", in("eax") foo, a = const bar); //~^ ERROR named arguments cannot follow explicit register arguments + //~^^ ERROR attempt to use a non-constant value in a constant asm!("{a}", in("eax") foo, a = const bar); //~^ ERROR named arguments cannot follow explicit register arguments + //~^^ ERROR attempt to use a non-constant value in a constant asm!("{1}", in("eax") foo, const bar); //~^ ERROR positional arguments cannot follow named arguments or explicit register arguments + //~^^ ERROR attempt to use a non-constant value in a constant asm!("", options(), ""); //~^ ERROR expected one of asm!("{}", in(reg) foo, "{}", out(reg) foo); diff --git a/src/test/ui/asm/parse-error.stderr b/src/test/ui/asm/parse-error.stderr index dfbfc0abe3472..4ab9d86948de9 100644 --- a/src/test/ui/asm/parse-error.stderr +++ b/src/test/ui/asm/parse-error.stderr @@ -91,7 +91,7 @@ LL | asm!("{}", options(), const foo); | previous options error: duplicate argument named `a` - --> $DIR/parse-error.rs:39:36 + --> $DIR/parse-error.rs:40:36 | LL | asm!("{a}", a = const foo, a = const bar); | ------------- ^^^^^^^^^^^^^ duplicate argument @@ -99,7 +99,7 @@ LL | asm!("{a}", a = const foo, a = const bar); | previously here error: argument never used - --> $DIR/parse-error.rs:39:36 + --> $DIR/parse-error.rs:40:36 | LL | asm!("{a}", a = const foo, a = const bar); | ^^^^^^^^^^^^^ argument never used @@ -107,13 +107,13 @@ LL | asm!("{a}", a = const foo, a = const bar); = help: if this argument is intentionally unused, consider using it in an asm comment: `"/* {1} */"` error: explicit register arguments cannot have names - --> $DIR/parse-error.rs:42:18 + --> $DIR/parse-error.rs:45:18 | LL | asm!("", a = in("eax") foo); | ^^^^^^^^^^^^^^^^^ error: named arguments cannot follow explicit register arguments - --> $DIR/parse-error.rs:44:36 + --> $DIR/parse-error.rs:47:36 | LL | asm!("{a}", in("eax") foo, a = const bar); | ------------- ^^^^^^^^^^^^^ named argument @@ -121,7 +121,7 @@ LL | asm!("{a}", in("eax") foo, a = const bar); | explicit register argument error: named arguments cannot follow explicit register arguments - --> $DIR/parse-error.rs:46:36 + --> $DIR/parse-error.rs:50:36 | LL | asm!("{a}", in("eax") foo, a = const bar); | ------------- ^^^^^^^^^^^^^ named argument @@ -129,7 +129,7 @@ LL | asm!("{a}", in("eax") foo, a = const bar); | explicit register argument error: positional arguments cannot follow named arguments or explicit register arguments - --> $DIR/parse-error.rs:48:36 + --> $DIR/parse-error.rs:53:36 | LL | asm!("{1}", in("eax") foo, const bar); | ------------- ^^^^^^^^^ positional argument @@ -137,19 +137,19 @@ LL | asm!("{1}", in("eax") foo, const bar); | explicit register argument error: expected one of `const`, `in`, `inlateout`, `inout`, `lateout`, `options`, `out`, or `sym`, found `""` - --> $DIR/parse-error.rs:50:29 + --> $DIR/parse-error.rs:56:29 | LL | asm!("", options(), ""); | ^^ expected one of 8 possible tokens error: expected one of `const`, `in`, `inlateout`, `inout`, `lateout`, `options`, `out`, or `sym`, found `"{}"` - --> $DIR/parse-error.rs:52:33 + --> $DIR/parse-error.rs:58:33 | LL | asm!("{}", in(reg) foo, "{}", out(reg) foo); | ^^^^ expected one of 8 possible tokens error: asm template must be a string literal - --> $DIR/parse-error.rs:54:14 + --> $DIR/parse-error.rs:60:14 | LL | asm!(format!("{{{}}}", 0), in(reg) foo); | ^^^^^^^^^^^^^^^^^^^^ @@ -157,12 +157,67 @@ LL | asm!(format!("{{{}}}", 0), in(reg) foo); = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) error: asm template must be a string literal - --> $DIR/parse-error.rs:56:21 + --> $DIR/parse-error.rs:62:21 | LL | asm!("{1}", format!("{{{}}}", 0), in(reg) foo, out(reg) bar); | ^^^^^^^^^^^^^^^^^^^^ | = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) -error: aborting due to 25 previous errors +error[E0435]: attempt to use a non-constant value in a constant + --> $DIR/parse-error.rs:37:37 + | +LL | let mut foo = 0; + | ---------- help: consider using `const` instead of `let`: `const foo` +... +LL | asm!("{}", options(), const foo); + | ^^^ non-constant value + +error[E0435]: attempt to use a non-constant value in a constant + --> $DIR/parse-error.rs:40:31 + | +LL | let mut foo = 0; + | ---------- help: consider using `const` instead of `let`: `const foo` +... +LL | asm!("{a}", a = const foo, a = const bar); + | ^^^ non-constant value + +error[E0435]: attempt to use a non-constant value in a constant + --> $DIR/parse-error.rs:40:46 + | +LL | let mut bar = 0; + | ---------- help: consider using `const` instead of `let`: `const bar` +... +LL | asm!("{a}", a = const foo, a = const bar); + | ^^^ non-constant value + +error[E0435]: attempt to use a non-constant value in a constant + --> $DIR/parse-error.rs:47:46 + | +LL | let mut bar = 0; + | ---------- help: consider using `const` instead of `let`: `const bar` +... +LL | asm!("{a}", in("eax") foo, a = const bar); + | ^^^ non-constant value + +error[E0435]: attempt to use a non-constant value in a constant + --> $DIR/parse-error.rs:50:46 + | +LL | let mut bar = 0; + | ---------- help: consider using `const` instead of `let`: `const bar` +... +LL | asm!("{a}", in("eax") foo, a = const bar); + | ^^^ non-constant value + +error[E0435]: attempt to use a non-constant value in a constant + --> $DIR/parse-error.rs:53:42 + | +LL | let mut bar = 0; + | ---------- help: consider using `const` instead of `let`: `const bar` +... +LL | asm!("{1}", in("eax") foo, const bar); + | ^^^ non-constant value + +error: aborting due to 31 previous errors +For more information about this error, try `rustc --explain E0435`. diff --git a/src/test/ui/asm/srcloc.rs b/src/test/ui/asm/srcloc.rs index 1477e3dd5665c..ed8cefc58b727 100644 --- a/src/test/ui/asm/srcloc.rs +++ b/src/test/ui/asm/srcloc.rs @@ -1,7 +1,7 @@ -// no-system-llvm +// min-llvm-version: 10.0.1 // only-x86_64 // build-fail - +// compile-flags: -Ccodegen-units=1 #![feature(asm)] // Checks that inline asm errors are mapped to the correct line in the source code. diff --git a/src/test/ui/asm/sym.rs b/src/test/ui/asm/sym.rs index 9931697e4129c..18be201d640c4 100644 --- a/src/test/ui/asm/sym.rs +++ b/src/test/ui/asm/sym.rs @@ -1,4 +1,6 @@ -// no-system-llvm +// min-llvm-version: 10.0.1 +// FIXME(#84025): codegen-units=1 leads to linkage errors +// compile-flags: -C codegen-units=2 // only-x86_64 // only-linux // run-pass @@ -76,5 +78,5 @@ fn main() { std::thread::spawn(|| { assert_eq!(static_addr!(S1), &S1 as *const u32); assert_eq!(static_tls_addr!(S2), &S2 as *const u32); - }); + }).join().unwrap(); } diff --git a/src/test/ui/asm/type-check-1.rs b/src/test/ui/asm/type-check-1.rs index 7880382c3b74f..57a91aaa934e9 100644 --- a/src/test/ui/asm/type-check-1.rs +++ b/src/test/ui/asm/type-check-1.rs @@ -21,5 +21,23 @@ fn main() { //~^ ERROR the size for values of type `[u64]` cannot be known at compilation time asm!("{}", inout(reg) v[..]); //~^ ERROR the size for values of type `[u64]` cannot be known at compilation time + + // Constants must be... constant + + let x = 0; + const fn const_foo(x: i32) -> i32 { + x + } + const fn const_bar<T>(x: T) -> T { + x + } + asm!("{}", const x); + //~^ ERROR attempt to use a non-constant value in a constant + asm!("{}", const const_foo(0)); + asm!("{}", const const_foo(x)); + //~^ ERROR attempt to use a non-constant value in a constant + asm!("{}", const const_bar(0)); + asm!("{}", const const_bar(x)); + //~^ ERROR attempt to use a non-constant value in a constant } } diff --git a/src/test/ui/asm/type-check-1.stderr b/src/test/ui/asm/type-check-1.stderr index 556e83fdb0d42..eefab6d397788 100644 --- a/src/test/ui/asm/type-check-1.stderr +++ b/src/test/ui/asm/type-check-1.stderr @@ -1,3 +1,30 @@ +error[E0435]: attempt to use a non-constant value in a constant + --> $DIR/type-check-1.rs:34:26 + | +LL | let x = 0; + | ----- help: consider using `const` instead of `let`: `const x` +... +LL | asm!("{}", const x); + | ^ non-constant value + +error[E0435]: attempt to use a non-constant value in a constant + --> $DIR/type-check-1.rs:37:36 + | +LL | let x = 0; + | ----- help: consider using `const` instead of `let`: `const x` +... +LL | asm!("{}", const const_foo(x)); + | ^ non-constant value + +error[E0435]: attempt to use a non-constant value in a constant + --> $DIR/type-check-1.rs:40:36 + | +LL | let x = 0; + | ----- help: consider using `const` instead of `let`: `const x` +... +LL | asm!("{}", const const_bar(x)); + | ^ non-constant value + error: invalid asm output --> $DIR/type-check-1.rs:10:29 | @@ -37,6 +64,7 @@ LL | asm!("{}", inout(reg) v[..]); = help: the trait `Sized` is not implemented for `[u64]` = note: all inline asm arguments must have a statically known size -error: aborting due to 5 previous errors +error: aborting due to 8 previous errors -For more information about this error, try `rustc --explain E0277`. +Some errors have detailed explanations: E0277, E0435. +For more information about an error, try `rustc --explain E0277`. diff --git a/src/test/ui/asm/type-check-2.rs b/src/test/ui/asm/type-check-2.rs index 01c8b4eb6540a..0677167ccfe73 100644 --- a/src/test/ui/asm/type-check-2.rs +++ b/src/test/ui/asm/type-check-2.rs @@ -28,31 +28,19 @@ fn main() { // Const operands must be integer or floats, and must be constants. - let x = 0; - const C: i32 = 0; - const fn const_foo(x: i32) -> i32 { - x - } - const fn const_bar<T>(x: T) -> T { - x - } + asm!("{}", const 0); asm!("{}", const 0i32); asm!("{}", const 0f32); asm!("{}", const 0 as *mut u8); //~^ ERROR asm `const` arguments must be integer or floating-point values - asm!("{}", const &0); - //~^ ERROR asm `const` arguments must be integer or floating-point values - asm!("{}", const x); - //~^ ERROR argument 1 is required to be a constant - asm!("{}", const const_foo(0)); - asm!("{}", const const_foo(x)); - //~^ ERROR argument 1 is required to be a constant - asm!("{}", const const_bar(0)); - asm!("{}", const const_bar(x)); - //~^ ERROR argument 1 is required to be a constant + + // This currently causes an ICE: https://github.com/rust-lang/rust/issues/81857 + // asm!("{}", const &0); + // ERROR asm `const` arguments must be integer or floating-point values // Sym operands must point to a function or static + const C: i32 = 0; static S: i32 = 0; asm!("{}", sym S); asm!("{}", sym main); diff --git a/src/test/ui/asm/type-check-2.stderr b/src/test/ui/asm/type-check-2.stderr index a520bea8f1da2..830ca7b55509d 100644 --- a/src/test/ui/asm/type-check-2.stderr +++ b/src/test/ui/asm/type-check-2.stderr @@ -1,25 +1,19 @@ error: asm `const` arguments must be integer or floating-point values - --> $DIR/type-check-2.rs:41:26 + --> $DIR/type-check-2.rs:34:20 | LL | asm!("{}", const 0 as *mut u8); - | ^^^^^^^^^^^^ - -error: asm `const` arguments must be integer or floating-point values - --> $DIR/type-check-2.rs:43:26 - | -LL | asm!("{}", const &0); - | ^^ + | ^^^^^^^^^^^^^^^^^^ error: arguments for inline assembly must be copyable - --> $DIR/type-check-2.rs:66:32 + --> $DIR/type-check-2.rs:54:32 | LL | asm!("{}", in(xmm_reg) SimdNonCopy(0.0, 0.0, 0.0, 0.0)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: `SimdNonCopy` does not implement the Copy trait -error: cannot use value of type `[closure@$DIR/type-check-2.rs:78:28: 78:38]` for inline assembly - --> $DIR/type-check-2.rs:78:28 +error: cannot use value of type `[closure@$DIR/type-check-2.rs:66:28: 66:38]` for inline assembly + --> $DIR/type-check-2.rs:66:28 | LL | asm!("{}", in(reg) |x: i32| x); | ^^^^^^^^^^ @@ -27,7 +21,7 @@ LL | asm!("{}", in(reg) |x: i32| x); = note: only integers, floats, SIMD vectors, pointers and function pointers can be used as arguments for inline assembly error: cannot use value of type `Vec<i32>` for inline assembly - --> $DIR/type-check-2.rs:80:28 + --> $DIR/type-check-2.rs:68:28 | LL | asm!("{}", in(reg) vec![0]); | ^^^^^^^ @@ -36,7 +30,7 @@ LL | asm!("{}", in(reg) vec![0]); = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) error: cannot use value of type `(i32, i32, i32)` for inline assembly - --> $DIR/type-check-2.rs:82:28 + --> $DIR/type-check-2.rs:70:28 | LL | asm!("{}", in(reg) (1, 2, 3)); | ^^^^^^^^^ @@ -44,7 +38,7 @@ LL | asm!("{}", in(reg) (1, 2, 3)); = note: only integers, floats, SIMD vectors, pointers and function pointers can be used as arguments for inline assembly error: cannot use value of type `[i32; 3]` for inline assembly - --> $DIR/type-check-2.rs:84:28 + --> $DIR/type-check-2.rs:72:28 | LL | asm!("{}", in(reg) [1, 2, 3]); | ^^^^^^^^^ @@ -52,7 +46,7 @@ LL | asm!("{}", in(reg) [1, 2, 3]); = note: only integers, floats, SIMD vectors, pointers and function pointers can be used as arguments for inline assembly error: cannot use value of type `fn() {main}` for inline assembly - --> $DIR/type-check-2.rs:92:31 + --> $DIR/type-check-2.rs:80:31 | LL | asm!("{}", inout(reg) f); | ^ @@ -60,7 +54,7 @@ LL | asm!("{}", inout(reg) f); = note: only integers, floats, SIMD vectors, pointers and function pointers can be used as arguments for inline assembly error: cannot use value of type `&mut i32` for inline assembly - --> $DIR/type-check-2.rs:95:31 + --> $DIR/type-check-2.rs:83:31 | LL | asm!("{}", inout(reg) r); | ^ @@ -68,35 +62,17 @@ LL | asm!("{}", inout(reg) r); = note: only integers, floats, SIMD vectors, pointers and function pointers can be used as arguments for inline assembly error: asm `sym` operand must point to a fn or static - --> $DIR/type-check-2.rs:59:24 + --> $DIR/type-check-2.rs:47:24 | LL | asm!("{}", sym C); | ^ error: asm `sym` operand must point to a fn or static - --> $DIR/type-check-2.rs:61:24 + --> $DIR/type-check-2.rs:49:24 | LL | asm!("{}", sym x); | ^ -error: argument 1 is required to be a constant - --> $DIR/type-check-2.rs:45:9 - | -LL | asm!("{}", const x); - | ^^^^^^^^^^^^^^^^^^^^ - -error: argument 1 is required to be a constant - --> $DIR/type-check-2.rs:48:9 - | -LL | asm!("{}", const const_foo(x)); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: argument 1 is required to be a constant - --> $DIR/type-check-2.rs:51:9 - | -LL | asm!("{}", const const_bar(x)); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - error[E0381]: use of possibly-uninitialized variable: `x` --> $DIR/type-check-2.rs:13:28 | @@ -127,7 +103,7 @@ LL | let v: Vec<u64> = vec![0, 1, 2]; LL | asm!("{}", inout(reg) v[0]); | ^ cannot borrow as mutable -error: aborting due to 18 previous errors +error: aborting due to 14 previous errors Some errors have detailed explanations: E0381, E0596. For more information about an error, try `rustc --explain E0381`. diff --git a/src/test/ui/assign-imm-local-twice.rs b/src/test/ui/assign-imm-local-twice.rs index c1c9bf62819af..b50f6ab5deb15 100644 --- a/src/test/ui/assign-imm-local-twice.rs +++ b/src/test/ui/assign-imm-local-twice.rs @@ -1,6 +1,6 @@ fn test() { let v: isize; - //~^ HELP make this binding mutable + //~^ HELP consider making this binding mutable //~| SUGGESTION mut v v = 1; //~ NOTE first assignment println!("v={}", v); diff --git a/src/test/ui/assign-imm-local-twice.stderr b/src/test/ui/assign-imm-local-twice.stderr index df0f4c4d80608..bba5d8dffe4bd 100644 --- a/src/test/ui/assign-imm-local-twice.stderr +++ b/src/test/ui/assign-imm-local-twice.stderr @@ -2,7 +2,7 @@ error[E0384]: cannot assign twice to immutable variable `v` --> $DIR/assign-imm-local-twice.rs:7:5 | LL | let v: isize; - | - help: make this binding mutable: `mut v` + | - help: consider making this binding mutable: `mut v` ... LL | v = 1; | ----- first assignment to `v` diff --git a/src/test/ui/associated-type-bounds/ambiguous-associated-type2.rs b/src/test/ui/associated-type-bounds/ambiguous-associated-type2.rs index 1b6d6d0ff599f..48de593342fa6 100644 --- a/src/test/ui/associated-type-bounds/ambiguous-associated-type2.rs +++ b/src/test/ui/associated-type-bounds/ambiguous-associated-type2.rs @@ -1,5 +1,3 @@ -// ignore-tidy-linelength - trait Foo { type Item; } diff --git a/src/test/ui/associated-type-bounds/ambiguous-associated-type2.stderr b/src/test/ui/associated-type-bounds/ambiguous-associated-type2.stderr index bda1debeac0d6..e72ef0e4b332e 100644 --- a/src/test/ui/associated-type-bounds/ambiguous-associated-type2.stderr +++ b/src/test/ui/associated-type-bounds/ambiguous-associated-type2.stderr @@ -1,12 +1,12 @@ error[E0391]: cycle detected when computing the super traits of `Baz` with associated type name `Item` - --> $DIR/ambiguous-associated-type2.rs:9:1 + --> $DIR/ambiguous-associated-type2.rs:7:1 | LL | trait Baz: Foo + Bar<Self::Item> {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: ...which again requires computing the super traits of `Baz` with associated type name `Item`, completing the cycle note: cycle used when computing the super traits of `Baz` - --> $DIR/ambiguous-associated-type2.rs:9:1 + --> $DIR/ambiguous-associated-type2.rs:7:1 | LL | trait Baz: Foo + Bar<Self::Item> {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/src/test/ui/associated-type-bounds/bad-bounds-on-assoc-in-trait.stderr b/src/test/ui/associated-type-bounds/bad-bounds-on-assoc-in-trait.stderr index 1c493581bc953..a5ebb80c83620 100644 --- a/src/test/ui/associated-type-bounds/bad-bounds-on-assoc-in-trait.stderr +++ b/src/test/ui/associated-type-bounds/bad-bounds-on-assoc-in-trait.stderr @@ -1,15 +1,3 @@ -error[E0277]: `<<Self as Case1>::C as Iterator>::Item` is not an iterator - --> $DIR/bad-bounds-on-assoc-in-trait.rs:27:5 - | -LL | type C: Clone + Iterator<Item: Send + Iterator<Item: for<'a> Lam<&'a u8, App: Debug>> + Sync>; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `<<Self as Case1>::C as Iterator>::Item` is not an iterator - | - = help: the trait `Iterator` is not implemented for `<<Self as Case1>::C as Iterator>::Item` -help: consider further restricting the associated type - | -LL | trait Case1 where <<Self as Case1>::C as Iterator>::Item: Iterator { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - error[E0277]: `<<Self as Case1>::C as Iterator>::Item` cannot be sent between threads safely --> $DIR/bad-bounds-on-assoc-in-trait.rs:27:36 | @@ -27,6 +15,23 @@ help: consider further restricting the associated type LL | trait Case1 where <<Self as Case1>::C as Iterator>::Item: Send { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +error[E0277]: `<<Self as Case1>::C as Iterator>::Item` is not an iterator + --> $DIR/bad-bounds-on-assoc-in-trait.rs:27:43 + | +LL | type C: Clone + Iterator<Item: Send + Iterator<Item: for<'a> Lam<&'a u8, App: Debug>> + Sync>; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `<<Self as Case1>::C as Iterator>::Item` is not an iterator + | + ::: $SRC_DIR/core/src/iter/traits/iterator.rs:LL:COL + | +LL | pub trait Iterator { + | ------------------ required by this bound in `Iterator` + | + = help: the trait `Iterator` is not implemented for `<<Self as Case1>::C as Iterator>::Item` +help: consider further restricting the associated type + | +LL | trait Case1 where <<Self as Case1>::C as Iterator>::Item: Iterator { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + error[E0277]: `<<Self as Case1>::C as Iterator>::Item` cannot be shared between threads safely --> $DIR/bad-bounds-on-assoc-in-trait.rs:27:93 | diff --git a/src/test/ui/associated-type-bounds/duplicate.full_tait.stderr b/src/test/ui/associated-type-bounds/duplicate.full_tait.stderr new file mode 100644 index 0000000000000..bd3cac1f887bf --- /dev/null +++ b/src/test/ui/associated-type-bounds/duplicate.full_tait.stderr @@ -0,0 +1,572 @@ +warning: the feature `type_alias_impl_trait` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/duplicate.rs:4:32 + | +LL | #![cfg_attr(full_tait, feature(type_alias_impl_trait))] + | ^^^^^^^^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #63063 <https://github.com/rust-lang/rust/issues/63063> for more information + +warning: the feature `impl_trait_in_bindings` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/duplicate.rs:6:12 + | +LL | #![feature(impl_trait_in_bindings)] + | ^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #63065 <https://github.com/rust-lang/rust/issues/63065> for more information + +error[E0719]: the value of the associated type `Item` (from trait `Iterator`) is already specified + --> $DIR/duplicate.rs:11:36 + | +LL | struct SI1<T: Iterator<Item: Copy, Item: Send>> { f: T } + | ---------- ^^^^^^^^^^ re-bound here + | | + | `Item` bound here first + +error[E0719]: the value of the associated type `Item` (from trait `Iterator`) is already specified + --> $DIR/duplicate.rs:13:36 + | +LL | struct SI2<T: Iterator<Item: Copy, Item: Copy>> { f: T } + | ---------- ^^^^^^^^^^ re-bound here + | | + | `Item` bound here first + +error[E0719]: the value of the associated type `Item` (from trait `Iterator`) is already specified + --> $DIR/duplicate.rs:15:39 + | +LL | struct SI3<T: Iterator<Item: 'static, Item: 'static>> { f: T } + | ------------- ^^^^^^^^^^^^^ re-bound here + | | + | `Item` bound here first + +error[E0719]: the value of the associated type `Item` (from trait `Iterator`) is already specified + --> $DIR/duplicate.rs:17:45 + | +LL | struct SW1<T> where T: Iterator<Item: Copy, Item: Send> { f: T } + | ---------- ^^^^^^^^^^ re-bound here + | | + | `Item` bound here first + +error[E0719]: the value of the associated type `Item` (from trait `Iterator`) is already specified + --> $DIR/duplicate.rs:19:45 + | +LL | struct SW2<T> where T: Iterator<Item: Copy, Item: Copy> { f: T } + | ---------- ^^^^^^^^^^ re-bound here + | | + | `Item` bound here first + +error[E0719]: the value of the associated type `Item` (from trait `Iterator`) is already specified + --> $DIR/duplicate.rs:21:48 + | +LL | struct SW3<T> where T: Iterator<Item: 'static, Item: 'static> { f: T } + | ------------- ^^^^^^^^^^^^^ re-bound here + | | + | `Item` bound here first + +error[E0719]: the value of the associated type `Item` (from trait `Iterator`) is already specified + --> $DIR/duplicate.rs:24:34 + | +LL | enum EI1<T: Iterator<Item: Copy, Item: Send>> { V(T) } + | ---------- ^^^^^^^^^^ re-bound here + | | + | `Item` bound here first + +error[E0719]: the value of the associated type `Item` (from trait `Iterator`) is already specified + --> $DIR/duplicate.rs:26:34 + | +LL | enum EI2<T: Iterator<Item: Copy, Item: Copy>> { V(T) } + | ---------- ^^^^^^^^^^ re-bound here + | | + | `Item` bound here first + +error[E0719]: the value of the associated type `Item` (from trait `Iterator`) is already specified + --> $DIR/duplicate.rs:28:37 + | +LL | enum EI3<T: Iterator<Item: 'static, Item: 'static>> { V(T) } + | ------------- ^^^^^^^^^^^^^ re-bound here + | | + | `Item` bound here first + +error[E0719]: the value of the associated type `Item` (from trait `Iterator`) is already specified + --> $DIR/duplicate.rs:30:43 + | +LL | enum EW1<T> where T: Iterator<Item: Copy, Item: Send> { V(T) } + | ---------- ^^^^^^^^^^ re-bound here + | | + | `Item` bound here first + +error[E0719]: the value of the associated type `Item` (from trait `Iterator`) is already specified + --> $DIR/duplicate.rs:32:43 + | +LL | enum EW2<T> where T: Iterator<Item: Copy, Item: Copy> { V(T) } + | ---------- ^^^^^^^^^^ re-bound here + | | + | `Item` bound here first + +error[E0719]: the value of the associated type `Item` (from trait `Iterator`) is already specified + --> $DIR/duplicate.rs:34:46 + | +LL | enum EW3<T> where T: Iterator<Item: 'static, Item: 'static> { V(T) } + | ------------- ^^^^^^^^^^^^^ re-bound here + | | + | `Item` bound here first + +error[E0719]: the value of the associated type `Item` (from trait `Iterator`) is already specified + --> $DIR/duplicate.rs:37:35 + | +LL | union UI1<T: Iterator<Item: Copy, Item: Send>> { f: T } + | ---------- ^^^^^^^^^^ re-bound here + | | + | `Item` bound here first + +error[E0719]: the value of the associated type `Item` (from trait `Iterator`) is already specified + --> $DIR/duplicate.rs:39:35 + | +LL | union UI2<T: Iterator<Item: Copy, Item: Copy>> { f: T } + | ---------- ^^^^^^^^^^ re-bound here + | | + | `Item` bound here first + +error[E0719]: the value of the associated type `Item` (from trait `Iterator`) is already specified + --> $DIR/duplicate.rs:41:38 + | +LL | union UI3<T: Iterator<Item: 'static, Item: 'static>> { f: T } + | ------------- ^^^^^^^^^^^^^ re-bound here + | | + | `Item` bound here first + +error[E0719]: the value of the associated type `Item` (from trait `Iterator`) is already specified + --> $DIR/duplicate.rs:43:44 + | +LL | union UW1<T> where T: Iterator<Item: Copy, Item: Send> { f: T } + | ---------- ^^^^^^^^^^ re-bound here + | | + | `Item` bound here first + +error[E0719]: the value of the associated type `Item` (from trait `Iterator`) is already specified + --> $DIR/duplicate.rs:45:44 + | +LL | union UW2<T> where T: Iterator<Item: Copy, Item: Copy> { f: T } + | ---------- ^^^^^^^^^^ re-bound here + | | + | `Item` bound here first + +error[E0719]: the value of the associated type `Item` (from trait `Iterator`) is already specified + --> $DIR/duplicate.rs:47:47 + | +LL | union UW3<T> where T: Iterator<Item: 'static, Item: 'static> { f: T } + | ------------- ^^^^^^^^^^^^^ re-bound here + | | + | `Item` bound here first + +error[E0719]: the value of the associated type `Item` (from trait `Iterator`) is already specified + --> $DIR/duplicate.rs:50:32 + | +LL | fn FI1<T: Iterator<Item: Copy, Item: Send>>() {} + | ---------- ^^^^^^^^^^ re-bound here + | | + | `Item` bound here first + +error[E0719]: the value of the associated type `Item` (from trait `Iterator`) is already specified + --> $DIR/duplicate.rs:52:32 + | +LL | fn FI2<T: Iterator<Item: Copy, Item: Copy>>() {} + | ---------- ^^^^^^^^^^ re-bound here + | | + | `Item` bound here first + +error[E0719]: the value of the associated type `Item` (from trait `Iterator`) is already specified + --> $DIR/duplicate.rs:54:35 + | +LL | fn FI3<T: Iterator<Item: 'static, Item: 'static>>() {} + | ------------- ^^^^^^^^^^^^^ re-bound here + | | + | `Item` bound here first + +error[E0719]: the value of the associated type `Item` (from trait `Iterator`) is already specified + --> $DIR/duplicate.rs:56:43 + | +LL | fn FW1<T>() where T: Iterator<Item: Copy, Item: Send> {} + | ---------- ^^^^^^^^^^ re-bound here + | | + | `Item` bound here first + +error[E0719]: the value of the associated type `Item` (from trait `Iterator`) is already specified + --> $DIR/duplicate.rs:58:43 + | +LL | fn FW2<T>() where T: Iterator<Item: Copy, Item: Copy> {} + | ---------- ^^^^^^^^^^ re-bound here + | | + | `Item` bound here first + +error[E0719]: the value of the associated type `Item` (from trait `Iterator`) is already specified + --> $DIR/duplicate.rs:60:46 + | +LL | fn FW3<T>() where T: Iterator<Item: 'static, Item: 'static> {} + | ------------- ^^^^^^^^^^^^^ re-bound here + | | + | `Item` bound here first + +error[E0719]: the value of the associated type `Item` (from trait `Iterator`) is already specified + --> $DIR/duplicate.rs:66:40 + | +LL | fn FAPIT1(_: impl Iterator<Item: Copy, Item: Send>) {} + | ---------- ^^^^^^^^^^ re-bound here + | | + | `Item` bound here first + +error[E0719]: the value of the associated type `Item` (from trait `Iterator`) is already specified + --> $DIR/duplicate.rs:68:40 + | +LL | fn FAPIT2(_: impl Iterator<Item: Copy, Item: Copy>) {} + | ---------- ^^^^^^^^^^ re-bound here + | | + | `Item` bound here first + +error[E0719]: the value of the associated type `Item` (from trait `Iterator`) is already specified + --> $DIR/duplicate.rs:70:43 + | +LL | fn FAPIT3(_: impl Iterator<Item: 'static, Item: 'static>) {} + | ------------- ^^^^^^^^^^^^^ re-bound here + | | + | `Item` bound here first + +error[E0719]: the value of the associated type `Item` (from trait `Iterator`) is already specified + --> $DIR/duplicate.rs:73:39 + | +LL | const CIT1: impl Iterator<Item: Copy, Item: Send> = iter::empty(); + | ---------- ^^^^^^^^^^ re-bound here + | | + | `Item` bound here first + +error[E0719]: the value of the associated type `Item` (from trait `Iterator`) is already specified + --> $DIR/duplicate.rs:75:39 + | +LL | const CIT2: impl Iterator<Item: Copy, Item: Copy> = iter::empty(); + | ---------- ^^^^^^^^^^ re-bound here + | | + | `Item` bound here first + +error[E0719]: the value of the associated type `Item` (from trait `Iterator`) is already specified + --> $DIR/duplicate.rs:77:42 + | +LL | const CIT3: impl Iterator<Item: 'static, Item: 'static> = iter::empty(); + | ------------- ^^^^^^^^^^^^^ re-bound here + | | + | `Item` bound here first + +error[E0719]: the value of the associated type `Item` (from trait `Iterator`) is already specified + --> $DIR/duplicate.rs:79:40 + | +LL | static SIT1: impl Iterator<Item: Copy, Item: Send> = iter::empty(); + | ---------- ^^^^^^^^^^ re-bound here + | | + | `Item` bound here first + +error[E0719]: the value of the associated type `Item` (from trait `Iterator`) is already specified + --> $DIR/duplicate.rs:81:40 + | +LL | static SIT2: impl Iterator<Item: Copy, Item: Copy> = iter::empty(); + | ---------- ^^^^^^^^^^ re-bound here + | | + | `Item` bound here first + +error[E0719]: the value of the associated type `Item` (from trait `Iterator`) is already specified + --> $DIR/duplicate.rs:83:43 + | +LL | static SIT3: impl Iterator<Item: 'static, Item: 'static> = iter::empty(); + | ------------- ^^^^^^^^^^^^^ re-bound here + | | + | `Item` bound here first + +error[E0719]: the value of the associated type `Item` (from trait `Iterator`) is already specified + --> $DIR/duplicate.rs:86:46 + | +LL | fn lit1() { let _: impl Iterator<Item: Copy, Item: Send> = iter::empty(); } + | ---------- ^^^^^^^^^^ re-bound here + | | + | `Item` bound here first + +error[E0719]: the value of the associated type `Item` (from trait `Iterator`) is already specified + --> $DIR/duplicate.rs:88:46 + | +LL | fn lit2() { let _: impl Iterator<Item: Copy, Item: Copy> = iter::empty(); } + | ---------- ^^^^^^^^^^ re-bound here + | | + | `Item` bound here first + +error[E0719]: the value of the associated type `Item` (from trait `Iterator`) is already specified + --> $DIR/duplicate.rs:90:49 + | +LL | fn lit3() { let _: impl Iterator<Item: 'static, Item: 'static> = iter::empty(); } + | ------------- ^^^^^^^^^^^^^ re-bound here + | | + | `Item` bound here first + +error[E0719]: the value of the associated type `Item` (from trait `Iterator`) is already specified + --> $DIR/duplicate.rs:93:35 + | +LL | type TAI1<T: Iterator<Item: Copy, Item: Send>> = T; + | ---------- ^^^^^^^^^^ re-bound here + | | + | `Item` bound here first + +error[E0719]: the value of the associated type `Item` (from trait `Iterator`) is already specified + --> $DIR/duplicate.rs:95:35 + | +LL | type TAI2<T: Iterator<Item: Copy, Item: Copy>> = T; + | ---------- ^^^^^^^^^^ re-bound here + | | + | `Item` bound here first + +error[E0719]: the value of the associated type `Item` (from trait `Iterator`) is already specified + --> $DIR/duplicate.rs:97:38 + | +LL | type TAI3<T: Iterator<Item: 'static, Item: 'static>> = T; + | ------------- ^^^^^^^^^^^^^ re-bound here + | | + | `Item` bound here first + +error[E0719]: the value of the associated type `Item` (from trait `Iterator`) is already specified + --> $DIR/duplicate.rs:99:44 + | +LL | type TAW1<T> where T: Iterator<Item: Copy, Item: Send> = T; + | ---------- ^^^^^^^^^^ re-bound here + | | + | `Item` bound here first + +error[E0719]: the value of the associated type `Item` (from trait `Iterator`) is already specified + --> $DIR/duplicate.rs:101:44 + | +LL | type TAW2<T> where T: Iterator<Item: Copy, Item: Copy> = T; + | ---------- ^^^^^^^^^^ re-bound here + | | + | `Item` bound here first + +error[E0719]: the value of the associated type `Item` (from trait `Iterator`) is already specified + --> $DIR/duplicate.rs:103:47 + | +LL | type TAW3<T> where T: Iterator<Item: 'static, Item: 'static> = T; + | ------------- ^^^^^^^^^^^^^ re-bound here + | | + | `Item` bound here first + +error[E0719]: the value of the associated type `Item` (from trait `Iterator`) is already specified + --> $DIR/duplicate.rs:106:36 + | +LL | type ETAI1<T: Iterator<Item: Copy, Item: Send>> = impl Copy; + | ---------- ^^^^^^^^^^ re-bound here + | | + | `Item` bound here first + +error[E0719]: the value of the associated type `Item` (from trait `Iterator`) is already specified + --> $DIR/duplicate.rs:108:36 + | +LL | type ETAI2<T: Iterator<Item: Copy, Item: Copy>> = impl Copy; + | ---------- ^^^^^^^^^^ re-bound here + | | + | `Item` bound here first + +error[E0719]: the value of the associated type `Item` (from trait `Iterator`) is already specified + --> $DIR/duplicate.rs:110:39 + | +LL | type ETAI3<T: Iterator<Item: 'static, Item: 'static>> = impl Copy; + | ------------- ^^^^^^^^^^^^^ re-bound here + | | + | `Item` bound here first + +error[E0719]: the value of the associated type `Item` (from trait `Iterator`) is already specified + --> $DIR/duplicate.rs:112:40 + | +LL | type ETAI4 = impl Iterator<Item: Copy, Item: Send>; + | ---------- ^^^^^^^^^^ re-bound here + | | + | `Item` bound here first + +error[E0719]: the value of the associated type `Item` (from trait `Iterator`) is already specified + --> $DIR/duplicate.rs:114:40 + | +LL | type ETAI5 = impl Iterator<Item: Copy, Item: Copy>; + | ---------- ^^^^^^^^^^ re-bound here + | | + | `Item` bound here first + +error[E0719]: the value of the associated type `Item` (from trait `Iterator`) is already specified + --> $DIR/duplicate.rs:116:43 + | +LL | type ETAI6 = impl Iterator<Item: 'static, Item: 'static>; + | ------------- ^^^^^^^^^^^^^ re-bound here + | | + | `Item` bound here first + +error[E0719]: the value of the associated type `Item` (from trait `Iterator`) is already specified + --> $DIR/duplicate.rs:119:36 + | +LL | trait TRI1<T: Iterator<Item: Copy, Item: Send>> {} + | ---------- ^^^^^^^^^^ re-bound here + | | + | `Item` bound here first + +error[E0719]: the value of the associated type `Item` (from trait `Iterator`) is already specified + --> $DIR/duplicate.rs:121:36 + | +LL | trait TRI2<T: Iterator<Item: Copy, Item: Copy>> {} + | ---------- ^^^^^^^^^^ re-bound here + | | + | `Item` bound here first + +error[E0719]: the value of the associated type `Item` (from trait `Iterator`) is already specified + --> $DIR/duplicate.rs:123:39 + | +LL | trait TRI3<T: Iterator<Item: 'static, Item: 'static>> {} + | ------------- ^^^^^^^^^^^^^ re-bound here + | | + | `Item` bound here first + +error[E0719]: the value of the associated type `Item` (from trait `Iterator`) is already specified + --> $DIR/duplicate.rs:125:34 + | +LL | trait TRS1: Iterator<Item: Copy, Item: Send> {} + | ---------- ^^^^^^^^^^ re-bound here + | | + | `Item` bound here first + +error[E0719]: the value of the associated type `Item` (from trait `Iterator`) is already specified + --> $DIR/duplicate.rs:127:34 + | +LL | trait TRS2: Iterator<Item: Copy, Item: Copy> {} + | ---------- ^^^^^^^^^^ re-bound here + | | + | `Item` bound here first + +error[E0719]: the value of the associated type `Item` (from trait `Iterator`) is already specified + --> $DIR/duplicate.rs:129:37 + | +LL | trait TRS3: Iterator<Item: 'static, Item: 'static> {} + | ------------- ^^^^^^^^^^^^^ re-bound here + | | + | `Item` bound here first + +error[E0719]: the value of the associated type `Item` (from trait `Iterator`) is already specified + --> $DIR/duplicate.rs:131:45 + | +LL | trait TRW1<T> where T: Iterator<Item: Copy, Item: Send> {} + | ---------- ^^^^^^^^^^ re-bound here + | | + | `Item` bound here first + +error[E0719]: the value of the associated type `Item` (from trait `Iterator`) is already specified + --> $DIR/duplicate.rs:133:45 + | +LL | trait TRW2<T> where T: Iterator<Item: Copy, Item: Copy> {} + | ---------- ^^^^^^^^^^ re-bound here + | | + | `Item` bound here first + +error[E0719]: the value of the associated type `Item` (from trait `Iterator`) is already specified + --> $DIR/duplicate.rs:135:48 + | +LL | trait TRW3<T> where T: Iterator<Item: 'static, Item: 'static> {} + | ------------- ^^^^^^^^^^^^^ re-bound here + | | + | `Item` bound here first + +error[E0719]: the value of the associated type `Item` (from trait `Iterator`) is already specified + --> $DIR/duplicate.rs:137:46 + | +LL | trait TRSW1 where Self: Iterator<Item: Copy, Item: Send> {} + | ---------- ^^^^^^^^^^ re-bound here + | | + | `Item` bound here first + +error[E0719]: the value of the associated type `Item` (from trait `Iterator`) is already specified + --> $DIR/duplicate.rs:137:46 + | +LL | trait TRSW1 where Self: Iterator<Item: Copy, Item: Send> {} + | ---------- ^^^^^^^^^^ re-bound here + | | + | `Item` bound here first + +error[E0719]: the value of the associated type `Item` (from trait `Iterator`) is already specified + --> $DIR/duplicate.rs:140:46 + | +LL | trait TRSW2 where Self: Iterator<Item: Copy, Item: Copy> {} + | ---------- ^^^^^^^^^^ re-bound here + | | + | `Item` bound here first + +error[E0719]: the value of the associated type `Item` (from trait `Iterator`) is already specified + --> $DIR/duplicate.rs:140:46 + | +LL | trait TRSW2 where Self: Iterator<Item: Copy, Item: Copy> {} + | ---------- ^^^^^^^^^^ re-bound here + | | + | `Item` bound here first + +error[E0719]: the value of the associated type `Item` (from trait `Iterator`) is already specified + --> $DIR/duplicate.rs:143:49 + | +LL | trait TRSW3 where Self: Iterator<Item: 'static, Item: 'static> {} + | ------------- ^^^^^^^^^^^^^ re-bound here + | | + | `Item` bound here first + +error[E0719]: the value of the associated type `Item` (from trait `Iterator`) is already specified + --> $DIR/duplicate.rs:143:49 + | +LL | trait TRSW3 where Self: Iterator<Item: 'static, Item: 'static> {} + | ------------- ^^^^^^^^^^^^^ re-bound here + | | + | `Item` bound here first + +error[E0719]: the value of the associated type `Item` (from trait `Iterator`) is already specified + --> $DIR/duplicate.rs:153:40 + | +LL | type TADyn1 = dyn Iterator<Item: Copy, Item: Send>; + | ---------- ^^^^^^^^^^ re-bound here + | | + | `Item` bound here first + +error[E0719]: the value of the associated type `Item` (from trait `Iterator`) is already specified + --> $DIR/duplicate.rs:155:44 + | +LL | type TADyn2 = Box<dyn Iterator<Item: Copy, Item: Copy>>; + | ---------- ^^^^^^^^^^ re-bound here + | | + | `Item` bound here first + +error[E0719]: the value of the associated type `Item` (from trait `Iterator`) is already specified + --> $DIR/duplicate.rs:157:43 + | +LL | type TADyn3 = dyn Iterator<Item: 'static, Item: 'static>; + | ------------- ^^^^^^^^^^^^^ re-bound here + | | + | `Item` bound here first + +error[E0719]: the value of the associated type `Item` (from trait `Iterator`) is already specified + --> $DIR/duplicate.rs:146:43 + | +LL | trait TRA1 { type A: Iterator<Item: Copy, Item: Send>; } + | ---------- ^^^^^^^^^^ re-bound here + | | + | `Item` bound here first + +error[E0719]: the value of the associated type `Item` (from trait `Iterator`) is already specified + --> $DIR/duplicate.rs:148:43 + | +LL | trait TRA2 { type A: Iterator<Item: Copy, Item: Copy>; } + | ---------- ^^^^^^^^^^ re-bound here + | | + | `Item` bound here first + +error[E0719]: the value of the associated type `Item` (from trait `Iterator`) is already specified + --> $DIR/duplicate.rs:150:46 + | +LL | trait TRA3 { type A: Iterator<Item: 'static, Item: 'static>; } + | ------------- ^^^^^^^^^^^^^ re-bound here + | | + | `Item` bound here first + +error: aborting due to 69 previous errors; 2 warnings emitted + +For more information about this error, try `rustc --explain E0719`. diff --git a/src/test/ui/associated-type-bounds/duplicate.min_tait.stderr b/src/test/ui/associated-type-bounds/duplicate.min_tait.stderr new file mode 100644 index 0000000000000..500e527a0188a --- /dev/null +++ b/src/test/ui/associated-type-bounds/duplicate.min_tait.stderr @@ -0,0 +1,564 @@ +warning: the feature `impl_trait_in_bindings` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/duplicate.rs:6:12 + | +LL | #![feature(impl_trait_in_bindings)] + | ^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #63065 <https://github.com/rust-lang/rust/issues/63065> for more information + +error[E0719]: the value of the associated type `Item` (from trait `Iterator`) is already specified + --> $DIR/duplicate.rs:11:36 + | +LL | struct SI1<T: Iterator<Item: Copy, Item: Send>> { f: T } + | ---------- ^^^^^^^^^^ re-bound here + | | + | `Item` bound here first + +error[E0719]: the value of the associated type `Item` (from trait `Iterator`) is already specified + --> $DIR/duplicate.rs:13:36 + | +LL | struct SI2<T: Iterator<Item: Copy, Item: Copy>> { f: T } + | ---------- ^^^^^^^^^^ re-bound here + | | + | `Item` bound here first + +error[E0719]: the value of the associated type `Item` (from trait `Iterator`) is already specified + --> $DIR/duplicate.rs:15:39 + | +LL | struct SI3<T: Iterator<Item: 'static, Item: 'static>> { f: T } + | ------------- ^^^^^^^^^^^^^ re-bound here + | | + | `Item` bound here first + +error[E0719]: the value of the associated type `Item` (from trait `Iterator`) is already specified + --> $DIR/duplicate.rs:17:45 + | +LL | struct SW1<T> where T: Iterator<Item: Copy, Item: Send> { f: T } + | ---------- ^^^^^^^^^^ re-bound here + | | + | `Item` bound here first + +error[E0719]: the value of the associated type `Item` (from trait `Iterator`) is already specified + --> $DIR/duplicate.rs:19:45 + | +LL | struct SW2<T> where T: Iterator<Item: Copy, Item: Copy> { f: T } + | ---------- ^^^^^^^^^^ re-bound here + | | + | `Item` bound here first + +error[E0719]: the value of the associated type `Item` (from trait `Iterator`) is already specified + --> $DIR/duplicate.rs:21:48 + | +LL | struct SW3<T> where T: Iterator<Item: 'static, Item: 'static> { f: T } + | ------------- ^^^^^^^^^^^^^ re-bound here + | | + | `Item` bound here first + +error[E0719]: the value of the associated type `Item` (from trait `Iterator`) is already specified + --> $DIR/duplicate.rs:24:34 + | +LL | enum EI1<T: Iterator<Item: Copy, Item: Send>> { V(T) } + | ---------- ^^^^^^^^^^ re-bound here + | | + | `Item` bound here first + +error[E0719]: the value of the associated type `Item` (from trait `Iterator`) is already specified + --> $DIR/duplicate.rs:26:34 + | +LL | enum EI2<T: Iterator<Item: Copy, Item: Copy>> { V(T) } + | ---------- ^^^^^^^^^^ re-bound here + | | + | `Item` bound here first + +error[E0719]: the value of the associated type `Item` (from trait `Iterator`) is already specified + --> $DIR/duplicate.rs:28:37 + | +LL | enum EI3<T: Iterator<Item: 'static, Item: 'static>> { V(T) } + | ------------- ^^^^^^^^^^^^^ re-bound here + | | + | `Item` bound here first + +error[E0719]: the value of the associated type `Item` (from trait `Iterator`) is already specified + --> $DIR/duplicate.rs:30:43 + | +LL | enum EW1<T> where T: Iterator<Item: Copy, Item: Send> { V(T) } + | ---------- ^^^^^^^^^^ re-bound here + | | + | `Item` bound here first + +error[E0719]: the value of the associated type `Item` (from trait `Iterator`) is already specified + --> $DIR/duplicate.rs:32:43 + | +LL | enum EW2<T> where T: Iterator<Item: Copy, Item: Copy> { V(T) } + | ---------- ^^^^^^^^^^ re-bound here + | | + | `Item` bound here first + +error[E0719]: the value of the associated type `Item` (from trait `Iterator`) is already specified + --> $DIR/duplicate.rs:34:46 + | +LL | enum EW3<T> where T: Iterator<Item: 'static, Item: 'static> { V(T) } + | ------------- ^^^^^^^^^^^^^ re-bound here + | | + | `Item` bound here first + +error[E0719]: the value of the associated type `Item` (from trait `Iterator`) is already specified + --> $DIR/duplicate.rs:37:35 + | +LL | union UI1<T: Iterator<Item: Copy, Item: Send>> { f: T } + | ---------- ^^^^^^^^^^ re-bound here + | | + | `Item` bound here first + +error[E0719]: the value of the associated type `Item` (from trait `Iterator`) is already specified + --> $DIR/duplicate.rs:39:35 + | +LL | union UI2<T: Iterator<Item: Copy, Item: Copy>> { f: T } + | ---------- ^^^^^^^^^^ re-bound here + | | + | `Item` bound here first + +error[E0719]: the value of the associated type `Item` (from trait `Iterator`) is already specified + --> $DIR/duplicate.rs:41:38 + | +LL | union UI3<T: Iterator<Item: 'static, Item: 'static>> { f: T } + | ------------- ^^^^^^^^^^^^^ re-bound here + | | + | `Item` bound here first + +error[E0719]: the value of the associated type `Item` (from trait `Iterator`) is already specified + --> $DIR/duplicate.rs:43:44 + | +LL | union UW1<T> where T: Iterator<Item: Copy, Item: Send> { f: T } + | ---------- ^^^^^^^^^^ re-bound here + | | + | `Item` bound here first + +error[E0719]: the value of the associated type `Item` (from trait `Iterator`) is already specified + --> $DIR/duplicate.rs:45:44 + | +LL | union UW2<T> where T: Iterator<Item: Copy, Item: Copy> { f: T } + | ---------- ^^^^^^^^^^ re-bound here + | | + | `Item` bound here first + +error[E0719]: the value of the associated type `Item` (from trait `Iterator`) is already specified + --> $DIR/duplicate.rs:47:47 + | +LL | union UW3<T> where T: Iterator<Item: 'static, Item: 'static> { f: T } + | ------------- ^^^^^^^^^^^^^ re-bound here + | | + | `Item` bound here first + +error[E0719]: the value of the associated type `Item` (from trait `Iterator`) is already specified + --> $DIR/duplicate.rs:50:32 + | +LL | fn FI1<T: Iterator<Item: Copy, Item: Send>>() {} + | ---------- ^^^^^^^^^^ re-bound here + | | + | `Item` bound here first + +error[E0719]: the value of the associated type `Item` (from trait `Iterator`) is already specified + --> $DIR/duplicate.rs:52:32 + | +LL | fn FI2<T: Iterator<Item: Copy, Item: Copy>>() {} + | ---------- ^^^^^^^^^^ re-bound here + | | + | `Item` bound here first + +error[E0719]: the value of the associated type `Item` (from trait `Iterator`) is already specified + --> $DIR/duplicate.rs:54:35 + | +LL | fn FI3<T: Iterator<Item: 'static, Item: 'static>>() {} + | ------------- ^^^^^^^^^^^^^ re-bound here + | | + | `Item` bound here first + +error[E0719]: the value of the associated type `Item` (from trait `Iterator`) is already specified + --> $DIR/duplicate.rs:56:43 + | +LL | fn FW1<T>() where T: Iterator<Item: Copy, Item: Send> {} + | ---------- ^^^^^^^^^^ re-bound here + | | + | `Item` bound here first + +error[E0719]: the value of the associated type `Item` (from trait `Iterator`) is already specified + --> $DIR/duplicate.rs:58:43 + | +LL | fn FW2<T>() where T: Iterator<Item: Copy, Item: Copy> {} + | ---------- ^^^^^^^^^^ re-bound here + | | + | `Item` bound here first + +error[E0719]: the value of the associated type `Item` (from trait `Iterator`) is already specified + --> $DIR/duplicate.rs:60:46 + | +LL | fn FW3<T>() where T: Iterator<Item: 'static, Item: 'static> {} + | ------------- ^^^^^^^^^^^^^ re-bound here + | | + | `Item` bound here first + +error[E0719]: the value of the associated type `Item` (from trait `Iterator`) is already specified + --> $DIR/duplicate.rs:66:40 + | +LL | fn FAPIT1(_: impl Iterator<Item: Copy, Item: Send>) {} + | ---------- ^^^^^^^^^^ re-bound here + | | + | `Item` bound here first + +error[E0719]: the value of the associated type `Item` (from trait `Iterator`) is already specified + --> $DIR/duplicate.rs:68:40 + | +LL | fn FAPIT2(_: impl Iterator<Item: Copy, Item: Copy>) {} + | ---------- ^^^^^^^^^^ re-bound here + | | + | `Item` bound here first + +error[E0719]: the value of the associated type `Item` (from trait `Iterator`) is already specified + --> $DIR/duplicate.rs:70:43 + | +LL | fn FAPIT3(_: impl Iterator<Item: 'static, Item: 'static>) {} + | ------------- ^^^^^^^^^^^^^ re-bound here + | | + | `Item` bound here first + +error[E0719]: the value of the associated type `Item` (from trait `Iterator`) is already specified + --> $DIR/duplicate.rs:73:39 + | +LL | const CIT1: impl Iterator<Item: Copy, Item: Send> = iter::empty(); + | ---------- ^^^^^^^^^^ re-bound here + | | + | `Item` bound here first + +error[E0719]: the value of the associated type `Item` (from trait `Iterator`) is already specified + --> $DIR/duplicate.rs:75:39 + | +LL | const CIT2: impl Iterator<Item: Copy, Item: Copy> = iter::empty(); + | ---------- ^^^^^^^^^^ re-bound here + | | + | `Item` bound here first + +error[E0719]: the value of the associated type `Item` (from trait `Iterator`) is already specified + --> $DIR/duplicate.rs:77:42 + | +LL | const CIT3: impl Iterator<Item: 'static, Item: 'static> = iter::empty(); + | ------------- ^^^^^^^^^^^^^ re-bound here + | | + | `Item` bound here first + +error[E0719]: the value of the associated type `Item` (from trait `Iterator`) is already specified + --> $DIR/duplicate.rs:79:40 + | +LL | static SIT1: impl Iterator<Item: Copy, Item: Send> = iter::empty(); + | ---------- ^^^^^^^^^^ re-bound here + | | + | `Item` bound here first + +error[E0719]: the value of the associated type `Item` (from trait `Iterator`) is already specified + --> $DIR/duplicate.rs:81:40 + | +LL | static SIT2: impl Iterator<Item: Copy, Item: Copy> = iter::empty(); + | ---------- ^^^^^^^^^^ re-bound here + | | + | `Item` bound here first + +error[E0719]: the value of the associated type `Item` (from trait `Iterator`) is already specified + --> $DIR/duplicate.rs:83:43 + | +LL | static SIT3: impl Iterator<Item: 'static, Item: 'static> = iter::empty(); + | ------------- ^^^^^^^^^^^^^ re-bound here + | | + | `Item` bound here first + +error[E0719]: the value of the associated type `Item` (from trait `Iterator`) is already specified + --> $DIR/duplicate.rs:86:46 + | +LL | fn lit1() { let _: impl Iterator<Item: Copy, Item: Send> = iter::empty(); } + | ---------- ^^^^^^^^^^ re-bound here + | | + | `Item` bound here first + +error[E0719]: the value of the associated type `Item` (from trait `Iterator`) is already specified + --> $DIR/duplicate.rs:88:46 + | +LL | fn lit2() { let _: impl Iterator<Item: Copy, Item: Copy> = iter::empty(); } + | ---------- ^^^^^^^^^^ re-bound here + | | + | `Item` bound here first + +error[E0719]: the value of the associated type `Item` (from trait `Iterator`) is already specified + --> $DIR/duplicate.rs:90:49 + | +LL | fn lit3() { let _: impl Iterator<Item: 'static, Item: 'static> = iter::empty(); } + | ------------- ^^^^^^^^^^^^^ re-bound here + | | + | `Item` bound here first + +error[E0719]: the value of the associated type `Item` (from trait `Iterator`) is already specified + --> $DIR/duplicate.rs:93:35 + | +LL | type TAI1<T: Iterator<Item: Copy, Item: Send>> = T; + | ---------- ^^^^^^^^^^ re-bound here + | | + | `Item` bound here first + +error[E0719]: the value of the associated type `Item` (from trait `Iterator`) is already specified + --> $DIR/duplicate.rs:95:35 + | +LL | type TAI2<T: Iterator<Item: Copy, Item: Copy>> = T; + | ---------- ^^^^^^^^^^ re-bound here + | | + | `Item` bound here first + +error[E0719]: the value of the associated type `Item` (from trait `Iterator`) is already specified + --> $DIR/duplicate.rs:97:38 + | +LL | type TAI3<T: Iterator<Item: 'static, Item: 'static>> = T; + | ------------- ^^^^^^^^^^^^^ re-bound here + | | + | `Item` bound here first + +error[E0719]: the value of the associated type `Item` (from trait `Iterator`) is already specified + --> $DIR/duplicate.rs:99:44 + | +LL | type TAW1<T> where T: Iterator<Item: Copy, Item: Send> = T; + | ---------- ^^^^^^^^^^ re-bound here + | | + | `Item` bound here first + +error[E0719]: the value of the associated type `Item` (from trait `Iterator`) is already specified + --> $DIR/duplicate.rs:101:44 + | +LL | type TAW2<T> where T: Iterator<Item: Copy, Item: Copy> = T; + | ---------- ^^^^^^^^^^ re-bound here + | | + | `Item` bound here first + +error[E0719]: the value of the associated type `Item` (from trait `Iterator`) is already specified + --> $DIR/duplicate.rs:103:47 + | +LL | type TAW3<T> where T: Iterator<Item: 'static, Item: 'static> = T; + | ------------- ^^^^^^^^^^^^^ re-bound here + | | + | `Item` bound here first + +error[E0719]: the value of the associated type `Item` (from trait `Iterator`) is already specified + --> $DIR/duplicate.rs:106:36 + | +LL | type ETAI1<T: Iterator<Item: Copy, Item: Send>> = impl Copy; + | ---------- ^^^^^^^^^^ re-bound here + | | + | `Item` bound here first + +error[E0719]: the value of the associated type `Item` (from trait `Iterator`) is already specified + --> $DIR/duplicate.rs:108:36 + | +LL | type ETAI2<T: Iterator<Item: Copy, Item: Copy>> = impl Copy; + | ---------- ^^^^^^^^^^ re-bound here + | | + | `Item` bound here first + +error[E0719]: the value of the associated type `Item` (from trait `Iterator`) is already specified + --> $DIR/duplicate.rs:110:39 + | +LL | type ETAI3<T: Iterator<Item: 'static, Item: 'static>> = impl Copy; + | ------------- ^^^^^^^^^^^^^ re-bound here + | | + | `Item` bound here first + +error[E0719]: the value of the associated type `Item` (from trait `Iterator`) is already specified + --> $DIR/duplicate.rs:112:40 + | +LL | type ETAI4 = impl Iterator<Item: Copy, Item: Send>; + | ---------- ^^^^^^^^^^ re-bound here + | | + | `Item` bound here first + +error[E0719]: the value of the associated type `Item` (from trait `Iterator`) is already specified + --> $DIR/duplicate.rs:114:40 + | +LL | type ETAI5 = impl Iterator<Item: Copy, Item: Copy>; + | ---------- ^^^^^^^^^^ re-bound here + | | + | `Item` bound here first + +error[E0719]: the value of the associated type `Item` (from trait `Iterator`) is already specified + --> $DIR/duplicate.rs:116:43 + | +LL | type ETAI6 = impl Iterator<Item: 'static, Item: 'static>; + | ------------- ^^^^^^^^^^^^^ re-bound here + | | + | `Item` bound here first + +error[E0719]: the value of the associated type `Item` (from trait `Iterator`) is already specified + --> $DIR/duplicate.rs:119:36 + | +LL | trait TRI1<T: Iterator<Item: Copy, Item: Send>> {} + | ---------- ^^^^^^^^^^ re-bound here + | | + | `Item` bound here first + +error[E0719]: the value of the associated type `Item` (from trait `Iterator`) is already specified + --> $DIR/duplicate.rs:121:36 + | +LL | trait TRI2<T: Iterator<Item: Copy, Item: Copy>> {} + | ---------- ^^^^^^^^^^ re-bound here + | | + | `Item` bound here first + +error[E0719]: the value of the associated type `Item` (from trait `Iterator`) is already specified + --> $DIR/duplicate.rs:123:39 + | +LL | trait TRI3<T: Iterator<Item: 'static, Item: 'static>> {} + | ------------- ^^^^^^^^^^^^^ re-bound here + | | + | `Item` bound here first + +error[E0719]: the value of the associated type `Item` (from trait `Iterator`) is already specified + --> $DIR/duplicate.rs:125:34 + | +LL | trait TRS1: Iterator<Item: Copy, Item: Send> {} + | ---------- ^^^^^^^^^^ re-bound here + | | + | `Item` bound here first + +error[E0719]: the value of the associated type `Item` (from trait `Iterator`) is already specified + --> $DIR/duplicate.rs:127:34 + | +LL | trait TRS2: Iterator<Item: Copy, Item: Copy> {} + | ---------- ^^^^^^^^^^ re-bound here + | | + | `Item` bound here first + +error[E0719]: the value of the associated type `Item` (from trait `Iterator`) is already specified + --> $DIR/duplicate.rs:129:37 + | +LL | trait TRS3: Iterator<Item: 'static, Item: 'static> {} + | ------------- ^^^^^^^^^^^^^ re-bound here + | | + | `Item` bound here first + +error[E0719]: the value of the associated type `Item` (from trait `Iterator`) is already specified + --> $DIR/duplicate.rs:131:45 + | +LL | trait TRW1<T> where T: Iterator<Item: Copy, Item: Send> {} + | ---------- ^^^^^^^^^^ re-bound here + | | + | `Item` bound here first + +error[E0719]: the value of the associated type `Item` (from trait `Iterator`) is already specified + --> $DIR/duplicate.rs:133:45 + | +LL | trait TRW2<T> where T: Iterator<Item: Copy, Item: Copy> {} + | ---------- ^^^^^^^^^^ re-bound here + | | + | `Item` bound here first + +error[E0719]: the value of the associated type `Item` (from trait `Iterator`) is already specified + --> $DIR/duplicate.rs:135:48 + | +LL | trait TRW3<T> where T: Iterator<Item: 'static, Item: 'static> {} + | ------------- ^^^^^^^^^^^^^ re-bound here + | | + | `Item` bound here first + +error[E0719]: the value of the associated type `Item` (from trait `Iterator`) is already specified + --> $DIR/duplicate.rs:137:46 + | +LL | trait TRSW1 where Self: Iterator<Item: Copy, Item: Send> {} + | ---------- ^^^^^^^^^^ re-bound here + | | + | `Item` bound here first + +error[E0719]: the value of the associated type `Item` (from trait `Iterator`) is already specified + --> $DIR/duplicate.rs:137:46 + | +LL | trait TRSW1 where Self: Iterator<Item: Copy, Item: Send> {} + | ---------- ^^^^^^^^^^ re-bound here + | | + | `Item` bound here first + +error[E0719]: the value of the associated type `Item` (from trait `Iterator`) is already specified + --> $DIR/duplicate.rs:140:46 + | +LL | trait TRSW2 where Self: Iterator<Item: Copy, Item: Copy> {} + | ---------- ^^^^^^^^^^ re-bound here + | | + | `Item` bound here first + +error[E0719]: the value of the associated type `Item` (from trait `Iterator`) is already specified + --> $DIR/duplicate.rs:140:46 + | +LL | trait TRSW2 where Self: Iterator<Item: Copy, Item: Copy> {} + | ---------- ^^^^^^^^^^ re-bound here + | | + | `Item` bound here first + +error[E0719]: the value of the associated type `Item` (from trait `Iterator`) is already specified + --> $DIR/duplicate.rs:143:49 + | +LL | trait TRSW3 where Self: Iterator<Item: 'static, Item: 'static> {} + | ------------- ^^^^^^^^^^^^^ re-bound here + | | + | `Item` bound here first + +error[E0719]: the value of the associated type `Item` (from trait `Iterator`) is already specified + --> $DIR/duplicate.rs:143:49 + | +LL | trait TRSW3 where Self: Iterator<Item: 'static, Item: 'static> {} + | ------------- ^^^^^^^^^^^^^ re-bound here + | | + | `Item` bound here first + +error[E0719]: the value of the associated type `Item` (from trait `Iterator`) is already specified + --> $DIR/duplicate.rs:153:40 + | +LL | type TADyn1 = dyn Iterator<Item: Copy, Item: Send>; + | ---------- ^^^^^^^^^^ re-bound here + | | + | `Item` bound here first + +error[E0719]: the value of the associated type `Item` (from trait `Iterator`) is already specified + --> $DIR/duplicate.rs:155:44 + | +LL | type TADyn2 = Box<dyn Iterator<Item: Copy, Item: Copy>>; + | ---------- ^^^^^^^^^^ re-bound here + | | + | `Item` bound here first + +error[E0719]: the value of the associated type `Item` (from trait `Iterator`) is already specified + --> $DIR/duplicate.rs:157:43 + | +LL | type TADyn3 = dyn Iterator<Item: 'static, Item: 'static>; + | ------------- ^^^^^^^^^^^^^ re-bound here + | | + | `Item` bound here first + +error[E0719]: the value of the associated type `Item` (from trait `Iterator`) is already specified + --> $DIR/duplicate.rs:146:43 + | +LL | trait TRA1 { type A: Iterator<Item: Copy, Item: Send>; } + | ---------- ^^^^^^^^^^ re-bound here + | | + | `Item` bound here first + +error[E0719]: the value of the associated type `Item` (from trait `Iterator`) is already specified + --> $DIR/duplicate.rs:148:43 + | +LL | trait TRA2 { type A: Iterator<Item: Copy, Item: Copy>; } + | ---------- ^^^^^^^^^^ re-bound here + | | + | `Item` bound here first + +error[E0719]: the value of the associated type `Item` (from trait `Iterator`) is already specified + --> $DIR/duplicate.rs:150:46 + | +LL | trait TRA3 { type A: Iterator<Item: 'static, Item: 'static>; } + | ------------- ^^^^^^^^^^^^^ re-bound here + | | + | `Item` bound here first + +error: aborting due to 69 previous errors; 1 warning emitted + +For more information about this error, try `rustc --explain E0719`. diff --git a/src/test/ui/associated-type-bounds/duplicate.rs b/src/test/ui/associated-type-bounds/duplicate.rs index 39df9ba02fdf5..c3319a7050d5d 100644 --- a/src/test/ui/associated-type-bounds/duplicate.rs +++ b/src/test/ui/associated-type-bounds/duplicate.rs @@ -1,7 +1,8 @@ -// ignore-tidy-linelength - #![feature(associated_type_bounds)] -#![feature(type_alias_impl_trait)] +// revisions: min_tait full_tait +#![feature(min_type_alias_impl_trait)] +#![cfg_attr(full_tait, feature(type_alias_impl_trait))] +//[full_tait]~^ WARN incomplete #![feature(impl_trait_in_bindings)] //~ WARN the feature `impl_trait_in_bindings` is incomplete #![feature(untagged_unions)] diff --git a/src/test/ui/associated-type-bounds/duplicate.stderr b/src/test/ui/associated-type-bounds/duplicate.stderr deleted file mode 100644 index de2876d6b60b8..0000000000000 --- a/src/test/ui/associated-type-bounds/duplicate.stderr +++ /dev/null @@ -1,564 +0,0 @@ -warning: the feature `impl_trait_in_bindings` is incomplete and may not be safe to use and/or cause compiler crashes - --> $DIR/duplicate.rs:5:12 - | -LL | #![feature(impl_trait_in_bindings)] - | ^^^^^^^^^^^^^^^^^^^^^^ - | - = note: `#[warn(incomplete_features)]` on by default - = note: see issue #63065 <https://github.com/rust-lang/rust/issues/63065> for more information - -error[E0719]: the value of the associated type `Item` (from trait `Iterator`) is already specified - --> $DIR/duplicate.rs:10:36 - | -LL | struct SI1<T: Iterator<Item: Copy, Item: Send>> { f: T } - | ---------- ^^^^^^^^^^ re-bound here - | | - | `Item` bound here first - -error[E0719]: the value of the associated type `Item` (from trait `Iterator`) is already specified - --> $DIR/duplicate.rs:12:36 - | -LL | struct SI2<T: Iterator<Item: Copy, Item: Copy>> { f: T } - | ---------- ^^^^^^^^^^ re-bound here - | | - | `Item` bound here first - -error[E0719]: the value of the associated type `Item` (from trait `Iterator`) is already specified - --> $DIR/duplicate.rs:14:39 - | -LL | struct SI3<T: Iterator<Item: 'static, Item: 'static>> { f: T } - | ------------- ^^^^^^^^^^^^^ re-bound here - | | - | `Item` bound here first - -error[E0719]: the value of the associated type `Item` (from trait `Iterator`) is already specified - --> $DIR/duplicate.rs:16:45 - | -LL | struct SW1<T> where T: Iterator<Item: Copy, Item: Send> { f: T } - | ---------- ^^^^^^^^^^ re-bound here - | | - | `Item` bound here first - -error[E0719]: the value of the associated type `Item` (from trait `Iterator`) is already specified - --> $DIR/duplicate.rs:18:45 - | -LL | struct SW2<T> where T: Iterator<Item: Copy, Item: Copy> { f: T } - | ---------- ^^^^^^^^^^ re-bound here - | | - | `Item` bound here first - -error[E0719]: the value of the associated type `Item` (from trait `Iterator`) is already specified - --> $DIR/duplicate.rs:20:48 - | -LL | struct SW3<T> where T: Iterator<Item: 'static, Item: 'static> { f: T } - | ------------- ^^^^^^^^^^^^^ re-bound here - | | - | `Item` bound here first - -error[E0719]: the value of the associated type `Item` (from trait `Iterator`) is already specified - --> $DIR/duplicate.rs:23:34 - | -LL | enum EI1<T: Iterator<Item: Copy, Item: Send>> { V(T) } - | ---------- ^^^^^^^^^^ re-bound here - | | - | `Item` bound here first - -error[E0719]: the value of the associated type `Item` (from trait `Iterator`) is already specified - --> $DIR/duplicate.rs:25:34 - | -LL | enum EI2<T: Iterator<Item: Copy, Item: Copy>> { V(T) } - | ---------- ^^^^^^^^^^ re-bound here - | | - | `Item` bound here first - -error[E0719]: the value of the associated type `Item` (from trait `Iterator`) is already specified - --> $DIR/duplicate.rs:27:37 - | -LL | enum EI3<T: Iterator<Item: 'static, Item: 'static>> { V(T) } - | ------------- ^^^^^^^^^^^^^ re-bound here - | | - | `Item` bound here first - -error[E0719]: the value of the associated type `Item` (from trait `Iterator`) is already specified - --> $DIR/duplicate.rs:29:43 - | -LL | enum EW1<T> where T: Iterator<Item: Copy, Item: Send> { V(T) } - | ---------- ^^^^^^^^^^ re-bound here - | | - | `Item` bound here first - -error[E0719]: the value of the associated type `Item` (from trait `Iterator`) is already specified - --> $DIR/duplicate.rs:31:43 - | -LL | enum EW2<T> where T: Iterator<Item: Copy, Item: Copy> { V(T) } - | ---------- ^^^^^^^^^^ re-bound here - | | - | `Item` bound here first - -error[E0719]: the value of the associated type `Item` (from trait `Iterator`) is already specified - --> $DIR/duplicate.rs:33:46 - | -LL | enum EW3<T> where T: Iterator<Item: 'static, Item: 'static> { V(T) } - | ------------- ^^^^^^^^^^^^^ re-bound here - | | - | `Item` bound here first - -error[E0719]: the value of the associated type `Item` (from trait `Iterator`) is already specified - --> $DIR/duplicate.rs:36:35 - | -LL | union UI1<T: Iterator<Item: Copy, Item: Send>> { f: T } - | ---------- ^^^^^^^^^^ re-bound here - | | - | `Item` bound here first - -error[E0719]: the value of the associated type `Item` (from trait `Iterator`) is already specified - --> $DIR/duplicate.rs:38:35 - | -LL | union UI2<T: Iterator<Item: Copy, Item: Copy>> { f: T } - | ---------- ^^^^^^^^^^ re-bound here - | | - | `Item` bound here first - -error[E0719]: the value of the associated type `Item` (from trait `Iterator`) is already specified - --> $DIR/duplicate.rs:40:38 - | -LL | union UI3<T: Iterator<Item: 'static, Item: 'static>> { f: T } - | ------------- ^^^^^^^^^^^^^ re-bound here - | | - | `Item` bound here first - -error[E0719]: the value of the associated type `Item` (from trait `Iterator`) is already specified - --> $DIR/duplicate.rs:42:44 - | -LL | union UW1<T> where T: Iterator<Item: Copy, Item: Send> { f: T } - | ---------- ^^^^^^^^^^ re-bound here - | | - | `Item` bound here first - -error[E0719]: the value of the associated type `Item` (from trait `Iterator`) is already specified - --> $DIR/duplicate.rs:44:44 - | -LL | union UW2<T> where T: Iterator<Item: Copy, Item: Copy> { f: T } - | ---------- ^^^^^^^^^^ re-bound here - | | - | `Item` bound here first - -error[E0719]: the value of the associated type `Item` (from trait `Iterator`) is already specified - --> $DIR/duplicate.rs:46:47 - | -LL | union UW3<T> where T: Iterator<Item: 'static, Item: 'static> { f: T } - | ------------- ^^^^^^^^^^^^^ re-bound here - | | - | `Item` bound here first - -error[E0719]: the value of the associated type `Item` (from trait `Iterator`) is already specified - --> $DIR/duplicate.rs:49:32 - | -LL | fn FI1<T: Iterator<Item: Copy, Item: Send>>() {} - | ---------- ^^^^^^^^^^ re-bound here - | | - | `Item` bound here first - -error[E0719]: the value of the associated type `Item` (from trait `Iterator`) is already specified - --> $DIR/duplicate.rs:51:32 - | -LL | fn FI2<T: Iterator<Item: Copy, Item: Copy>>() {} - | ---------- ^^^^^^^^^^ re-bound here - | | - | `Item` bound here first - -error[E0719]: the value of the associated type `Item` (from trait `Iterator`) is already specified - --> $DIR/duplicate.rs:53:35 - | -LL | fn FI3<T: Iterator<Item: 'static, Item: 'static>>() {} - | ------------- ^^^^^^^^^^^^^ re-bound here - | | - | `Item` bound here first - -error[E0719]: the value of the associated type `Item` (from trait `Iterator`) is already specified - --> $DIR/duplicate.rs:55:43 - | -LL | fn FW1<T>() where T: Iterator<Item: Copy, Item: Send> {} - | ---------- ^^^^^^^^^^ re-bound here - | | - | `Item` bound here first - -error[E0719]: the value of the associated type `Item` (from trait `Iterator`) is already specified - --> $DIR/duplicate.rs:57:43 - | -LL | fn FW2<T>() where T: Iterator<Item: Copy, Item: Copy> {} - | ---------- ^^^^^^^^^^ re-bound here - | | - | `Item` bound here first - -error[E0719]: the value of the associated type `Item` (from trait `Iterator`) is already specified - --> $DIR/duplicate.rs:59:46 - | -LL | fn FW3<T>() where T: Iterator<Item: 'static, Item: 'static> {} - | ------------- ^^^^^^^^^^^^^ re-bound here - | | - | `Item` bound here first - -error[E0719]: the value of the associated type `Item` (from trait `Iterator`) is already specified - --> $DIR/duplicate.rs:65:40 - | -LL | fn FAPIT1(_: impl Iterator<Item: Copy, Item: Send>) {} - | ---------- ^^^^^^^^^^ re-bound here - | | - | `Item` bound here first - -error[E0719]: the value of the associated type `Item` (from trait `Iterator`) is already specified - --> $DIR/duplicate.rs:67:40 - | -LL | fn FAPIT2(_: impl Iterator<Item: Copy, Item: Copy>) {} - | ---------- ^^^^^^^^^^ re-bound here - | | - | `Item` bound here first - -error[E0719]: the value of the associated type `Item` (from trait `Iterator`) is already specified - --> $DIR/duplicate.rs:69:43 - | -LL | fn FAPIT3(_: impl Iterator<Item: 'static, Item: 'static>) {} - | ------------- ^^^^^^^^^^^^^ re-bound here - | | - | `Item` bound here first - -error[E0719]: the value of the associated type `Item` (from trait `Iterator`) is already specified - --> $DIR/duplicate.rs:72:39 - | -LL | const CIT1: impl Iterator<Item: Copy, Item: Send> = iter::empty(); - | ---------- ^^^^^^^^^^ re-bound here - | | - | `Item` bound here first - -error[E0719]: the value of the associated type `Item` (from trait `Iterator`) is already specified - --> $DIR/duplicate.rs:74:39 - | -LL | const CIT2: impl Iterator<Item: Copy, Item: Copy> = iter::empty(); - | ---------- ^^^^^^^^^^ re-bound here - | | - | `Item` bound here first - -error[E0719]: the value of the associated type `Item` (from trait `Iterator`) is already specified - --> $DIR/duplicate.rs:76:42 - | -LL | const CIT3: impl Iterator<Item: 'static, Item: 'static> = iter::empty(); - | ------------- ^^^^^^^^^^^^^ re-bound here - | | - | `Item` bound here first - -error[E0719]: the value of the associated type `Item` (from trait `Iterator`) is already specified - --> $DIR/duplicate.rs:78:40 - | -LL | static SIT1: impl Iterator<Item: Copy, Item: Send> = iter::empty(); - | ---------- ^^^^^^^^^^ re-bound here - | | - | `Item` bound here first - -error[E0719]: the value of the associated type `Item` (from trait `Iterator`) is already specified - --> $DIR/duplicate.rs:80:40 - | -LL | static SIT2: impl Iterator<Item: Copy, Item: Copy> = iter::empty(); - | ---------- ^^^^^^^^^^ re-bound here - | | - | `Item` bound here first - -error[E0719]: the value of the associated type `Item` (from trait `Iterator`) is already specified - --> $DIR/duplicate.rs:82:43 - | -LL | static SIT3: impl Iterator<Item: 'static, Item: 'static> = iter::empty(); - | ------------- ^^^^^^^^^^^^^ re-bound here - | | - | `Item` bound here first - -error[E0719]: the value of the associated type `Item` (from trait `Iterator`) is already specified - --> $DIR/duplicate.rs:85:46 - | -LL | fn lit1() { let _: impl Iterator<Item: Copy, Item: Send> = iter::empty(); } - | ---------- ^^^^^^^^^^ re-bound here - | | - | `Item` bound here first - -error[E0719]: the value of the associated type `Item` (from trait `Iterator`) is already specified - --> $DIR/duplicate.rs:87:46 - | -LL | fn lit2() { let _: impl Iterator<Item: Copy, Item: Copy> = iter::empty(); } - | ---------- ^^^^^^^^^^ re-bound here - | | - | `Item` bound here first - -error[E0719]: the value of the associated type `Item` (from trait `Iterator`) is already specified - --> $DIR/duplicate.rs:89:49 - | -LL | fn lit3() { let _: impl Iterator<Item: 'static, Item: 'static> = iter::empty(); } - | ------------- ^^^^^^^^^^^^^ re-bound here - | | - | `Item` bound here first - -error[E0719]: the value of the associated type `Item` (from trait `Iterator`) is already specified - --> $DIR/duplicate.rs:92:35 - | -LL | type TAI1<T: Iterator<Item: Copy, Item: Send>> = T; - | ---------- ^^^^^^^^^^ re-bound here - | | - | `Item` bound here first - -error[E0719]: the value of the associated type `Item` (from trait `Iterator`) is already specified - --> $DIR/duplicate.rs:94:35 - | -LL | type TAI2<T: Iterator<Item: Copy, Item: Copy>> = T; - | ---------- ^^^^^^^^^^ re-bound here - | | - | `Item` bound here first - -error[E0719]: the value of the associated type `Item` (from trait `Iterator`) is already specified - --> $DIR/duplicate.rs:96:38 - | -LL | type TAI3<T: Iterator<Item: 'static, Item: 'static>> = T; - | ------------- ^^^^^^^^^^^^^ re-bound here - | | - | `Item` bound here first - -error[E0719]: the value of the associated type `Item` (from trait `Iterator`) is already specified - --> $DIR/duplicate.rs:98:44 - | -LL | type TAW1<T> where T: Iterator<Item: Copy, Item: Send> = T; - | ---------- ^^^^^^^^^^ re-bound here - | | - | `Item` bound here first - -error[E0719]: the value of the associated type `Item` (from trait `Iterator`) is already specified - --> $DIR/duplicate.rs:100:44 - | -LL | type TAW2<T> where T: Iterator<Item: Copy, Item: Copy> = T; - | ---------- ^^^^^^^^^^ re-bound here - | | - | `Item` bound here first - -error[E0719]: the value of the associated type `Item` (from trait `Iterator`) is already specified - --> $DIR/duplicate.rs:102:47 - | -LL | type TAW3<T> where T: Iterator<Item: 'static, Item: 'static> = T; - | ------------- ^^^^^^^^^^^^^ re-bound here - | | - | `Item` bound here first - -error[E0719]: the value of the associated type `Item` (from trait `Iterator`) is already specified - --> $DIR/duplicate.rs:105:36 - | -LL | type ETAI1<T: Iterator<Item: Copy, Item: Send>> = impl Copy; - | ---------- ^^^^^^^^^^ re-bound here - | | - | `Item` bound here first - -error[E0719]: the value of the associated type `Item` (from trait `Iterator`) is already specified - --> $DIR/duplicate.rs:107:36 - | -LL | type ETAI2<T: Iterator<Item: Copy, Item: Copy>> = impl Copy; - | ---------- ^^^^^^^^^^ re-bound here - | | - | `Item` bound here first - -error[E0719]: the value of the associated type `Item` (from trait `Iterator`) is already specified - --> $DIR/duplicate.rs:109:39 - | -LL | type ETAI3<T: Iterator<Item: 'static, Item: 'static>> = impl Copy; - | ------------- ^^^^^^^^^^^^^ re-bound here - | | - | `Item` bound here first - -error[E0719]: the value of the associated type `Item` (from trait `Iterator`) is already specified - --> $DIR/duplicate.rs:111:40 - | -LL | type ETAI4 = impl Iterator<Item: Copy, Item: Send>; - | ---------- ^^^^^^^^^^ re-bound here - | | - | `Item` bound here first - -error[E0719]: the value of the associated type `Item` (from trait `Iterator`) is already specified - --> $DIR/duplicate.rs:113:40 - | -LL | type ETAI5 = impl Iterator<Item: Copy, Item: Copy>; - | ---------- ^^^^^^^^^^ re-bound here - | | - | `Item` bound here first - -error[E0719]: the value of the associated type `Item` (from trait `Iterator`) is already specified - --> $DIR/duplicate.rs:115:43 - | -LL | type ETAI6 = impl Iterator<Item: 'static, Item: 'static>; - | ------------- ^^^^^^^^^^^^^ re-bound here - | | - | `Item` bound here first - -error[E0719]: the value of the associated type `Item` (from trait `Iterator`) is already specified - --> $DIR/duplicate.rs:118:36 - | -LL | trait TRI1<T: Iterator<Item: Copy, Item: Send>> {} - | ---------- ^^^^^^^^^^ re-bound here - | | - | `Item` bound here first - -error[E0719]: the value of the associated type `Item` (from trait `Iterator`) is already specified - --> $DIR/duplicate.rs:120:36 - | -LL | trait TRI2<T: Iterator<Item: Copy, Item: Copy>> {} - | ---------- ^^^^^^^^^^ re-bound here - | | - | `Item` bound here first - -error[E0719]: the value of the associated type `Item` (from trait `Iterator`) is already specified - --> $DIR/duplicate.rs:122:39 - | -LL | trait TRI3<T: Iterator<Item: 'static, Item: 'static>> {} - | ------------- ^^^^^^^^^^^^^ re-bound here - | | - | `Item` bound here first - -error[E0719]: the value of the associated type `Item` (from trait `Iterator`) is already specified - --> $DIR/duplicate.rs:124:34 - | -LL | trait TRS1: Iterator<Item: Copy, Item: Send> {} - | ---------- ^^^^^^^^^^ re-bound here - | | - | `Item` bound here first - -error[E0719]: the value of the associated type `Item` (from trait `Iterator`) is already specified - --> $DIR/duplicate.rs:126:34 - | -LL | trait TRS2: Iterator<Item: Copy, Item: Copy> {} - | ---------- ^^^^^^^^^^ re-bound here - | | - | `Item` bound here first - -error[E0719]: the value of the associated type `Item` (from trait `Iterator`) is already specified - --> $DIR/duplicate.rs:128:37 - | -LL | trait TRS3: Iterator<Item: 'static, Item: 'static> {} - | ------------- ^^^^^^^^^^^^^ re-bound here - | | - | `Item` bound here first - -error[E0719]: the value of the associated type `Item` (from trait `Iterator`) is already specified - --> $DIR/duplicate.rs:130:45 - | -LL | trait TRW1<T> where T: Iterator<Item: Copy, Item: Send> {} - | ---------- ^^^^^^^^^^ re-bound here - | | - | `Item` bound here first - -error[E0719]: the value of the associated type `Item` (from trait `Iterator`) is already specified - --> $DIR/duplicate.rs:132:45 - | -LL | trait TRW2<T> where T: Iterator<Item: Copy, Item: Copy> {} - | ---------- ^^^^^^^^^^ re-bound here - | | - | `Item` bound here first - -error[E0719]: the value of the associated type `Item` (from trait `Iterator`) is already specified - --> $DIR/duplicate.rs:134:48 - | -LL | trait TRW3<T> where T: Iterator<Item: 'static, Item: 'static> {} - | ------------- ^^^^^^^^^^^^^ re-bound here - | | - | `Item` bound here first - -error[E0719]: the value of the associated type `Item` (from trait `Iterator`) is already specified - --> $DIR/duplicate.rs:136:46 - | -LL | trait TRSW1 where Self: Iterator<Item: Copy, Item: Send> {} - | ---------- ^^^^^^^^^^ re-bound here - | | - | `Item` bound here first - -error[E0719]: the value of the associated type `Item` (from trait `Iterator`) is already specified - --> $DIR/duplicate.rs:136:46 - | -LL | trait TRSW1 where Self: Iterator<Item: Copy, Item: Send> {} - | ---------- ^^^^^^^^^^ re-bound here - | | - | `Item` bound here first - -error[E0719]: the value of the associated type `Item` (from trait `Iterator`) is already specified - --> $DIR/duplicate.rs:139:46 - | -LL | trait TRSW2 where Self: Iterator<Item: Copy, Item: Copy> {} - | ---------- ^^^^^^^^^^ re-bound here - | | - | `Item` bound here first - -error[E0719]: the value of the associated type `Item` (from trait `Iterator`) is already specified - --> $DIR/duplicate.rs:139:46 - | -LL | trait TRSW2 where Self: Iterator<Item: Copy, Item: Copy> {} - | ---------- ^^^^^^^^^^ re-bound here - | | - | `Item` bound here first - -error[E0719]: the value of the associated type `Item` (from trait `Iterator`) is already specified - --> $DIR/duplicate.rs:142:49 - | -LL | trait TRSW3 where Self: Iterator<Item: 'static, Item: 'static> {} - | ------------- ^^^^^^^^^^^^^ re-bound here - | | - | `Item` bound here first - -error[E0719]: the value of the associated type `Item` (from trait `Iterator`) is already specified - --> $DIR/duplicate.rs:142:49 - | -LL | trait TRSW3 where Self: Iterator<Item: 'static, Item: 'static> {} - | ------------- ^^^^^^^^^^^^^ re-bound here - | | - | `Item` bound here first - -error[E0719]: the value of the associated type `Item` (from trait `Iterator`) is already specified - --> $DIR/duplicate.rs:152:40 - | -LL | type TADyn1 = dyn Iterator<Item: Copy, Item: Send>; - | ---------- ^^^^^^^^^^ re-bound here - | | - | `Item` bound here first - -error[E0719]: the value of the associated type `Item` (from trait `Iterator`) is already specified - --> $DIR/duplicate.rs:154:44 - | -LL | type TADyn2 = Box<dyn Iterator<Item: Copy, Item: Copy>>; - | ---------- ^^^^^^^^^^ re-bound here - | | - | `Item` bound here first - -error[E0719]: the value of the associated type `Item` (from trait `Iterator`) is already specified - --> $DIR/duplicate.rs:156:43 - | -LL | type TADyn3 = dyn Iterator<Item: 'static, Item: 'static>; - | ------------- ^^^^^^^^^^^^^ re-bound here - | | - | `Item` bound here first - -error[E0719]: the value of the associated type `Item` (from trait `Iterator`) is already specified - --> $DIR/duplicate.rs:145:43 - | -LL | trait TRA1 { type A: Iterator<Item: Copy, Item: Send>; } - | ---------- ^^^^^^^^^^ re-bound here - | | - | `Item` bound here first - -error[E0719]: the value of the associated type `Item` (from trait `Iterator`) is already specified - --> $DIR/duplicate.rs:147:43 - | -LL | trait TRA2 { type A: Iterator<Item: Copy, Item: Copy>; } - | ---------- ^^^^^^^^^^ re-bound here - | | - | `Item` bound here first - -error[E0719]: the value of the associated type `Item` (from trait `Iterator`) is already specified - --> $DIR/duplicate.rs:149:46 - | -LL | trait TRA3 { type A: Iterator<Item: 'static, Item: 'static>; } - | ------------- ^^^^^^^^^^^^^ re-bound here - | | - | `Item` bound here first - -error: aborting due to 69 previous errors; 1 warning emitted - -For more information about this error, try `rustc --explain E0719`. diff --git a/src/test/ui/associated-type-bounds/hrtb.rs b/src/test/ui/associated-type-bounds/hrtb.rs new file mode 100644 index 0000000000000..7ab3836493b01 --- /dev/null +++ b/src/test/ui/associated-type-bounds/hrtb.rs @@ -0,0 +1,65 @@ +// check-pass + +#![feature(associated_type_bounds)] + +trait A<'a> {} +trait B<'b> {} +fn foo<T>() +where + for<'a> T: A<'a> + 'a, +{ +} +trait C<'c>: for<'a> A<'a> + for<'b> B<'b> { + type As; +} +struct D<T> +where + T: for<'c> C<'c, As: A<'c>>, +{ + t: std::marker::PhantomData<T>, +} + +trait E<'e> { + type As; +} +trait F<'f>: for<'a> A<'a> + for<'e> E<'e> {} +struct G<T> +where + for<'f> T: F<'f, As: E<'f>> + 'f, +{ + t: std::marker::PhantomData<T>, +} + +trait I<'a, 'b, 'c> { + type As; +} +trait H<'d, 'e>: for<'f> I<'d, 'f, 'e> + 'd {} +fn foo2<T>() +where + T: for<'g> H<'g, 'g, As: for<'h> H<'h, 'g> + 'g>, +{ +} + +fn foo3<T>() +where + T: for<'i> H<'i, 'i, As: for<'j> H<'j, 'i, As: for<'k> I<'i, 'k, 'j> + 'j> + 'i>, +{ +} +fn foo4<T>() +where + T: for<'l, 'i> H<'l, 'i, As: for<'j> H<'j, 'i, As: for<'k> I<'l, 'k, 'j> + 'j> + 'i>, +{ +} + +struct X<'x, 'y> { + x: std::marker::PhantomData<&'x ()>, + y: std::marker::PhantomData<&'y ()>, +} + +fn foo5<T>() +where + T: for<'l, 'i> H<'l, 'i, As: for<'j> H<'j, 'i, As: for<'k> H<'j, 'k, As = X<'j, 'k>> + 'j> + 'i> +{ +} + +fn main() {} diff --git a/src/test/ui/associated-type-bounds/issue-79949.rs b/src/test/ui/associated-type-bounds/issue-79949.rs new file mode 100644 index 0000000000000..9f924f1fd81d8 --- /dev/null +++ b/src/test/ui/associated-type-bounds/issue-79949.rs @@ -0,0 +1,26 @@ +// check-pass + +#![allow(incomplete_features)] +#![feature(associated_type_bounds)] +#![feature(generic_associated_types)] + +trait MP { + type T<'a>; +} +struct S(String); +impl MP for S { + type T<'a> = &'a str; +} + +trait SR: MP { + fn sr<IM>(&self) -> i32 + where + for<'a> IM: T<T: U<<Self as MP>::T<'a>>>; +} + +trait T { + type T; +} +trait U<X> {} + +fn main() {} diff --git a/src/test/ui/associated-type-bounds/issue-81193.rs b/src/test/ui/associated-type-bounds/issue-81193.rs new file mode 100644 index 0000000000000..d2aa54ab9512e --- /dev/null +++ b/src/test/ui/associated-type-bounds/issue-81193.rs @@ -0,0 +1,15 @@ +// check-pass + +#![feature(associated_type_bounds)] + +trait A<'a, 'b> {} + +trait B<'a, 'b, 'c> {} + +fn err<'u, 'a, F>() +where + for<'b> F: Iterator<Item: for<'c> B<'a, 'b, 'c> + for<'c> A<'a, 'c>>, +{ +} + +fn main() {} diff --git a/src/test/ui/associated-type-bounds/issue-83017.rs b/src/test/ui/associated-type-bounds/issue-83017.rs new file mode 100644 index 0000000000000..8f0a9ea3566fb --- /dev/null +++ b/src/test/ui/associated-type-bounds/issue-83017.rs @@ -0,0 +1,39 @@ +#![feature(associated_type_bounds)] + +trait TraitA<'a> { + type AsA; +} + +trait TraitB<'a, 'b> { + type AsB; +} + +trait TraitC<'a, 'b, 'c> {} + +struct X; + +impl<'a, 'b, 'c> TraitC<'a, 'b, 'c> for X {} + +struct Y; + +impl<'a, 'b> TraitB<'a, 'b> for Y { + type AsB = X; +} + +struct Z; + +impl<'a> TraitA<'a> for Z { + type AsA = Y; +} + +fn foo<T>() +where + for<'a> T: TraitA<'a, AsA: for<'b> TraitB<'a, 'b, AsB: for<'c> TraitC<'a, 'b, 'c>>>, +{ +} + +fn main() { + foo::<Z>(); + //~^ ERROR: the trait bound `for<'a, 'b> <Z as TraitA<'a>>::AsA: TraitB<'a, 'b>` is not satisfied + //~| ERROR: the trait bound `for<'a, 'b, 'c> <<Z as TraitA<'a>>::AsA as TraitB<'a, 'b>>::AsB: TraitC<'a, 'b, 'c>` is not satisfied +} diff --git a/src/test/ui/associated-type-bounds/issue-83017.stderr b/src/test/ui/associated-type-bounds/issue-83017.stderr new file mode 100644 index 0000000000000..4eb71fd0287e0 --- /dev/null +++ b/src/test/ui/associated-type-bounds/issue-83017.stderr @@ -0,0 +1,27 @@ +error[E0277]: the trait bound `for<'a, 'b> <Z as TraitA<'a>>::AsA: TraitB<'a, 'b>` is not satisfied + --> $DIR/issue-83017.rs:36:5 + | +LL | fn foo<T>() + | --- required by a bound in this +LL | where +LL | for<'a> T: TraitA<'a, AsA: for<'b> TraitB<'a, 'b, AsB: for<'c> TraitC<'a, 'b, 'c>>>, + | ------------------------------------------------------- required by this bound in `foo` +... +LL | foo::<Z>(); + | ^^^^^^^^ the trait `for<'a, 'b> TraitB<'a, 'b>` is not implemented for `<Z as TraitA<'a>>::AsA` + +error[E0277]: the trait bound `for<'a, 'b, 'c> <<Z as TraitA<'a>>::AsA as TraitB<'a, 'b>>::AsB: TraitC<'a, 'b, 'c>` is not satisfied + --> $DIR/issue-83017.rs:36:5 + | +LL | fn foo<T>() + | --- required by a bound in this +LL | where +LL | for<'a> T: TraitA<'a, AsA: for<'b> TraitB<'a, 'b, AsB: for<'c> TraitC<'a, 'b, 'c>>>, + | -------------------------- required by this bound in `foo` +... +LL | foo::<Z>(); + | ^^^^^^^^ the trait `for<'a, 'b, 'c> TraitC<'a, 'b, 'c>` is not implemented for `<<Z as TraitA<'a>>::AsA as TraitB<'a, 'b>>::AsB` + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0277`. diff --git a/src/test/ui/associated-type-bounds/trait-alias-impl-trait.full_tait.stderr b/src/test/ui/associated-type-bounds/trait-alias-impl-trait.full_tait.stderr new file mode 100644 index 0000000000000..d4da4babf4e11 --- /dev/null +++ b/src/test/ui/associated-type-bounds/trait-alias-impl-trait.full_tait.stderr @@ -0,0 +1,11 @@ +warning: the feature `type_alias_impl_trait` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/trait-alias-impl-trait.rs:6:32 + | +LL | #![cfg_attr(full_tait, feature(type_alias_impl_trait))] + | ^^^^^^^^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #63063 <https://github.com/rust-lang/rust/issues/63063> for more information + +warning: 1 warning emitted + diff --git a/src/test/ui/associated-type-bounds/trait-alias-impl-trait.rs b/src/test/ui/associated-type-bounds/trait-alias-impl-trait.rs index 025540ce20070..cf20631af5bca 100644 --- a/src/test/ui/associated-type-bounds/trait-alias-impl-trait.rs +++ b/src/test/ui/associated-type-bounds/trait-alias-impl-trait.rs @@ -1,7 +1,10 @@ // run-pass #![feature(associated_type_bounds)] -#![feature(type_alias_impl_trait)] +// revisions: min_tait full_tait +#![feature(min_type_alias_impl_trait)] +#![cfg_attr(full_tait, feature(type_alias_impl_trait))] +//[full_tait]~^ WARN incomplete use std::ops::Add; diff --git a/src/test/ui/associated-types/associated-type-projection-from-multiple-supertraits.stderr b/src/test/ui/associated-types/associated-type-projection-from-multiple-supertraits.stderr index b6a88179c1f63..cd7c0dc4a44d0 100644 --- a/src/test/ui/associated-types/associated-type-projection-from-multiple-supertraits.stderr +++ b/src/test/ui/associated-types/associated-type-projection-from-multiple-supertraits.stderr @@ -20,12 +20,12 @@ LL | fn dent<C:BoxCar>(c: C, color: C::Color) { | help: use fully qualified syntax to disambiguate | -LL | fn dent<C:BoxCar>(c: C, color: <C as Box>::Color) { - | ^^^^^^^^^^^^^^^^^ -help: use fully qualified syntax to disambiguate - | LL | fn dent<C:BoxCar>(c: C, color: <C as Vehicle>::Color) { | ^^^^^^^^^^^^^^^^^^^^^ +help: use fully qualified syntax to disambiguate + | +LL | fn dent<C:BoxCar>(c: C, color: <C as Box>::Color) { + | ^^^^^^^^^^^^^^^^^ error[E0222]: ambiguous associated type `Color` in bounds of `BoxCar` --> $DIR/associated-type-projection-from-multiple-supertraits.rs:23:37 @@ -42,8 +42,8 @@ LL | fn dent_object<COLOR>(c: dyn BoxCar<Color=COLOR>) { = help: consider introducing a new type parameter `T` and adding `where` constraints: where T: BoxCar, - T: Box::Color = COLOR, - T: Vehicle::Color = COLOR + T: Vehicle::Color = COLOR, + T: Box::Color = COLOR error[E0191]: the value of the associated types `Color` (from trait `Box`), `Color` (from trait `Vehicle`) must be specified --> $DIR/associated-type-projection-from-multiple-supertraits.rs:23:30 @@ -73,12 +73,12 @@ LL | fn paint<C:BoxCar>(c: C, d: C::Color) { | help: use fully qualified syntax to disambiguate | -LL | fn paint<C:BoxCar>(c: C, d: <C as Box>::Color) { - | ^^^^^^^^^^^^^^^^^ -help: use fully qualified syntax to disambiguate - | LL | fn paint<C:BoxCar>(c: C, d: <C as Vehicle>::Color) { | ^^^^^^^^^^^^^^^^^^^^^ +help: use fully qualified syntax to disambiguate + | +LL | fn paint<C:BoxCar>(c: C, d: <C as Box>::Color) { + | ^^^^^^^^^^^^^^^^^ error[E0191]: the value of the associated types `Color` (from trait `Box`), `Color` (from trait `Vehicle`) must be specified --> $DIR/associated-type-projection-from-multiple-supertraits.rs:32:32 diff --git a/src/test/ui/associated-types/associated-types-coherence-failure.stderr b/src/test/ui/associated-types/associated-types-coherence-failure.stderr index 2c53b0a2248fa..211613b371492 100644 --- a/src/test/ui/associated-types/associated-types-coherence-failure.stderr +++ b/src/test/ui/associated-types/associated-types-coherence-failure.stderr @@ -1,4 +1,4 @@ -error[E0119]: conflicting implementations of trait `IntoCow<'_, _>` for type `Cow<'_, _>`: +error[E0119]: conflicting implementations of trait `IntoCow<'_, _>` for type `Cow<'_, _>` --> $DIR/associated-types-coherence-failure.rs:21:1 | LL | impl<'a, B: ?Sized> IntoCow<'a, B> for <B as ToOwned>::Owned where B: ToOwned { @@ -7,7 +7,7 @@ LL | impl<'a, B: ?Sized> IntoCow<'a, B> for <B as ToOwned>::Owned where B: ToOwn LL | impl<'a, B: ?Sized> IntoCow<'a, B> for Cow<'a, B> where B: ToOwned { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `Cow<'_, _>` -error[E0119]: conflicting implementations of trait `IntoCow<'_, _>` for type `&_`: +error[E0119]: conflicting implementations of trait `IntoCow<'_, _>` for type `&_` --> $DIR/associated-types-coherence-failure.rs:28:1 | LL | impl<'a, B: ?Sized> IntoCow<'a, B> for <B as ToOwned>::Owned where B: ToOwned { diff --git a/src/test/ui/associated-types/defaults-specialization.stderr b/src/test/ui/associated-types/defaults-specialization.stderr index 920f8322813fe..3c7dc1fc3c9b2 100644 --- a/src/test/ui/associated-types/defaults-specialization.stderr +++ b/src/test/ui/associated-types/defaults-specialization.stderr @@ -15,7 +15,10 @@ LL | fn make() -> Self::Ty { | -------- type in trait ... LL | fn make() -> u8 { 0 } - | ^^ expected associated type, found `u8` + | ^^ + | | + | expected associated type, found `u8` + | help: change the output type to match the trait: `<A<T> as Tr>::Ty` | = note: expected fn pointer `fn() -> <A<T> as Tr>::Ty` found fn pointer `fn() -> u8` @@ -30,7 +33,10 @@ LL | default type Ty = bool; | ----------------------- expected this associated type LL | LL | fn make() -> bool { true } - | ^^^^ expected associated type, found `bool` + | ^^^^ + | | + | expected associated type, found `bool` + | help: change the output type to match the trait: `<B<T> as Tr>::Ty` | = note: expected fn pointer `fn() -> <B<T> as Tr>::Ty` found fn pointer `fn() -> bool` diff --git a/src/test/ui/associated-types/defaults-suitability.stderr b/src/test/ui/associated-types/defaults-suitability.stderr index 274d09fd09c86..af4e6f0a5c161 100644 --- a/src/test/ui/associated-types/defaults-suitability.stderr +++ b/src/test/ui/associated-types/defaults-suitability.stderr @@ -31,8 +31,8 @@ LL | type Bar: Clone = Vec<T>; = note: required because of the requirements on the impl of `Clone` for `Vec<T>` help: consider restricting type parameter `T` | -LL | trait Foo<T: Clone> { - | ^^^^^^^ +LL | trait Foo<T: std::clone::Clone> { + | ^^^^^^^^^^^^^^^^^^^ error[E0277]: the trait bound `(): Foo<Self>` is not satisfied --> $DIR/defaults-suitability.rs:34:5 @@ -99,8 +99,8 @@ LL | type Baz = T; | help: consider further restricting type parameter `T` | -LL | Self::Baz: Clone, T: Clone - | ^^^^^^^^^^ +LL | Self::Baz: Clone, T: std::clone::Clone + | ^^^^^^^^^^^^^^^^^^^^^^ error: aborting due to 8 previous errors diff --git a/src/test/ui/associated-types/defaults-unsound-62211-1.stderr b/src/test/ui/associated-types/defaults-unsound-62211-1.stderr index 8e446cf051f47..bcdb50aa312cb 100644 --- a/src/test/ui/associated-types/defaults-unsound-62211-1.stderr +++ b/src/test/ui/associated-types/defaults-unsound-62211-1.stderr @@ -13,20 +13,6 @@ help: consider further restricting `Self` LL | trait UncheckedCopy: Sized + std::fmt::Display { | ^^^^^^^^^^^^^^^^^^^ -error[E0277]: the trait bound `Self: Deref` is not satisfied - --> $DIR/defaults-unsound-62211-1.rs:20:5 - | -LL | type Output: Copy + Deref<Target = str> + AddAssign<&'static str> + From<Self> + Display = Self; - | ^^^^^^^^^^^^^^^^^^^^-------------------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | | | - | | required by this bound in `UncheckedCopy::Output` - | the trait `Deref` is not implemented for `Self` - | -help: consider further restricting `Self` - | -LL | trait UncheckedCopy: Sized + Deref { - | ^^^^^^^ - error[E0277]: cannot add-assign `&'static str` to `Self` --> $DIR/defaults-unsound-62211-1.rs:20:5 | @@ -41,6 +27,20 @@ help: consider further restricting `Self` LL | trait UncheckedCopy: Sized + AddAssign<&'static str> { | ^^^^^^^^^^^^^^^^^^^^^^^^^ +error[E0277]: the trait bound `Self: Deref` is not satisfied + --> $DIR/defaults-unsound-62211-1.rs:20:5 + | +LL | type Output: Copy + Deref<Target = str> + AddAssign<&'static str> + From<Self> + Display = Self; + | ^^^^^^^^^^^^^^^^^^^^-------------------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | | + | | required by this bound in `UncheckedCopy::Output` + | the trait `Deref` is not implemented for `Self` + | +help: consider further restricting `Self` + | +LL | trait UncheckedCopy: Sized + Deref { + | ^^^^^^^ + error[E0277]: the trait bound `Self: Copy` is not satisfied --> $DIR/defaults-unsound-62211-1.rs:20:5 | diff --git a/src/test/ui/associated-types/defaults-unsound-62211-2.stderr b/src/test/ui/associated-types/defaults-unsound-62211-2.stderr index 93f4f497b38a2..fa5cf9196edbd 100644 --- a/src/test/ui/associated-types/defaults-unsound-62211-2.stderr +++ b/src/test/ui/associated-types/defaults-unsound-62211-2.stderr @@ -13,20 +13,6 @@ help: consider further restricting `Self` LL | trait UncheckedCopy: Sized + std::fmt::Display { | ^^^^^^^^^^^^^^^^^^^ -error[E0277]: the trait bound `Self: Deref` is not satisfied - --> $DIR/defaults-unsound-62211-2.rs:20:5 - | -LL | type Output: Copy + Deref<Target = str> + AddAssign<&'static str> + From<Self> + Display = Self; - | ^^^^^^^^^^^^^^^^^^^^-------------------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | | | - | | required by this bound in `UncheckedCopy::Output` - | the trait `Deref` is not implemented for `Self` - | -help: consider further restricting `Self` - | -LL | trait UncheckedCopy: Sized + Deref { - | ^^^^^^^ - error[E0277]: cannot add-assign `&'static str` to `Self` --> $DIR/defaults-unsound-62211-2.rs:20:5 | @@ -41,6 +27,20 @@ help: consider further restricting `Self` LL | trait UncheckedCopy: Sized + AddAssign<&'static str> { | ^^^^^^^^^^^^^^^^^^^^^^^^^ +error[E0277]: the trait bound `Self: Deref` is not satisfied + --> $DIR/defaults-unsound-62211-2.rs:20:5 + | +LL | type Output: Copy + Deref<Target = str> + AddAssign<&'static str> + From<Self> + Display = Self; + | ^^^^^^^^^^^^^^^^^^^^-------------------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | | + | | required by this bound in `UncheckedCopy::Output` + | the trait `Deref` is not implemented for `Self` + | +help: consider further restricting `Self` + | +LL | trait UncheckedCopy: Sized + Deref { + | ^^^^^^^ + error[E0277]: the trait bound `Self: Copy` is not satisfied --> $DIR/defaults-unsound-62211-2.rs:20:5 | diff --git a/src/test/ui/associated-types/impl-trait-return-missing-constraint.stderr b/src/test/ui/associated-types/impl-trait-return-missing-constraint.stderr index b0e9e33a6c38e..89e05b61fc9d8 100644 --- a/src/test/ui/associated-types/impl-trait-return-missing-constraint.stderr +++ b/src/test/ui/associated-types/impl-trait-return-missing-constraint.stderr @@ -2,13 +2,13 @@ error[E0271]: type mismatch resolving `<impl Bar as Foo>::Item == i32` --> $DIR/impl-trait-return-missing-constraint.rs:25:13 | LL | fn bar() -> impl Bar { - | -------- the expected opaque type + | -------- the found opaque type ... LL | fn baz() -> impl Bar<Item = i32> { - | ^^^^^^^^^^^^^^^^^^^^ expected associated type, found `i32` + | ^^^^^^^^^^^^^^^^^^^^ expected `i32`, found associated type | - = note: expected associated type `<impl Bar as Foo>::Item` - found type `i32` + = note: expected type `i32` + found associated type `<impl Bar as Foo>::Item` help: consider constraining the associated type `<impl Bar as Foo>::Item` to `i32` | LL | fn bar() -> impl Bar<Item = i32> { diff --git a/src/test/ui/associated-types/impl-wf-cycle-1.stderr b/src/test/ui/associated-types/impl-wf-cycle-1.stderr index 82328048c99a3..1d8d2b0149d21 100644 --- a/src/test/ui/associated-types/impl-wf-cycle-1.stderr +++ b/src/test/ui/associated-types/impl-wf-cycle-1.stderr @@ -10,7 +10,11 @@ LL | | LL | | } | |_^ | - = note: required because of the requirements on the impl of `Grault` for `(T,)` +note: required because of the requirements on the impl of `Grault` for `(T,)` + --> $DIR/impl-wf-cycle-1.rs:15:17 + | +LL | impl<T: Grault> Grault for (T,) + | ^^^^^^ ^^^^ = note: 1 redundant requirements hidden = note: required because of the requirements on the impl of `Grault` for `(T,)` @@ -20,7 +24,11 @@ error[E0275]: overflow evaluating the requirement `<(T,) as Grault>::A == _` LL | type A = (); | ^^^^^^^^^^^^ | - = note: required because of the requirements on the impl of `Grault` for `(T,)` +note: required because of the requirements on the impl of `Grault` for `(T,)` + --> $DIR/impl-wf-cycle-1.rs:15:17 + | +LL | impl<T: Grault> Grault for (T,) + | ^^^^^^ ^^^^ = note: 1 redundant requirements hidden = note: required because of the requirements on the impl of `Grault` for `(T,)` @@ -30,7 +38,11 @@ error[E0275]: overflow evaluating the requirement `<(T,) as Grault>::A == _` LL | type B = bool; | ^^^^^^^^^^^^^^ | - = note: required because of the requirements on the impl of `Grault` for `(T,)` +note: required because of the requirements on the impl of `Grault` for `(T,)` + --> $DIR/impl-wf-cycle-1.rs:15:17 + | +LL | impl<T: Grault> Grault for (T,) + | ^^^^^^ ^^^^ = note: 1 redundant requirements hidden = note: required because of the requirements on the impl of `Grault` for `(T,)` diff --git a/src/test/ui/associated-types/impl-wf-cycle-2.stderr b/src/test/ui/associated-types/impl-wf-cycle-2.stderr index 5cd18a33adf37..a17e63f28fe91 100644 --- a/src/test/ui/associated-types/impl-wf-cycle-2.stderr +++ b/src/test/ui/associated-types/impl-wf-cycle-2.stderr @@ -10,7 +10,11 @@ LL | | LL | | } | |_^ | - = note: required because of the requirements on the impl of `Grault` for `(T,)` +note: required because of the requirements on the impl of `Grault` for `(T,)` + --> $DIR/impl-wf-cycle-2.rs:7:17 + | +LL | impl<T: Grault> Grault for (T,) + | ^^^^^^ ^^^^ error[E0275]: overflow evaluating the requirement `<(T,) as Grault>::A == _` --> $DIR/impl-wf-cycle-2.rs:11:5 @@ -18,7 +22,11 @@ error[E0275]: overflow evaluating the requirement `<(T,) as Grault>::A == _` LL | type A = (); | ^^^^^^^^^^^^ | - = note: required because of the requirements on the impl of `Grault` for `(T,)` +note: required because of the requirements on the impl of `Grault` for `(T,)` + --> $DIR/impl-wf-cycle-2.rs:7:17 + | +LL | impl<T: Grault> Grault for (T,) + | ^^^^^^ ^^^^ error: aborting due to 2 previous errors diff --git a/src/test/ui/issues/issue-23208.rs b/src/test/ui/associated-types/issue-23208.rs similarity index 100% rename from src/test/ui/issues/issue-23208.rs rename to src/test/ui/associated-types/issue-23208.rs diff --git a/src/test/ui/associated-types/issue-27675-unchecked-bounds.stderr b/src/test/ui/associated-types/issue-27675-unchecked-bounds.stderr index 02396bd00a59d..b6122bd54d162 100644 --- a/src/test/ui/associated-types/issue-27675-unchecked-bounds.stderr +++ b/src/test/ui/associated-types/issue-27675-unchecked-bounds.stderr @@ -9,8 +9,8 @@ LL | copy::<dyn Setup<From=T>>(t) | help: consider restricting type parameter `T` | -LL | pub fn copy_any<T: Copy>(t: &T) -> T { - | ^^^^^^ +LL | pub fn copy_any<T: std::marker::Copy>(t: &T) -> T { + | ^^^^^^^^^^^^^^^^^^^ error: aborting due to previous error diff --git a/src/test/ui/issues/issue-31597.rs b/src/test/ui/associated-types/issue-31597.rs similarity index 100% rename from src/test/ui/issues/issue-31597.rs rename to src/test/ui/associated-types/issue-31597.rs diff --git a/src/test/ui/associated-types/issue-43784-associated-type.stderr b/src/test/ui/associated-types/issue-43784-associated-type.stderr index d5105ae3b5815..0e653a7d3b22f 100644 --- a/src/test/ui/associated-types/issue-43784-associated-type.stderr +++ b/src/test/ui/associated-types/issue-43784-associated-type.stderr @@ -9,8 +9,8 @@ LL | type Assoc = T; | help: consider restricting type parameter `T` | -LL | impl<T: Copy> Complete for T { - | ^^^^^^ +LL | impl<T: std::marker::Copy> Complete for T { + | ^^^^^^^^^^^^^^^^^^^ error: aborting due to previous error diff --git a/src/test/ui/associated-types/issue-44153.stderr b/src/test/ui/associated-types/issue-44153.stderr index 8ef71087958ca..b7db5d385829c 100644 --- a/src/test/ui/associated-types/issue-44153.stderr +++ b/src/test/ui/associated-types/issue-44153.stderr @@ -5,9 +5,13 @@ LL | fn visit() {} | ---------- required by `Visit::visit` ... LL | <() as Visit>::visit(); - | ^^^^^^^^^^^^^^^^^^^^ expected `()`, found `&()` + | ^^^^^^^^^^^^^^^^^^^^ expected `&()`, found `()` | - = note: required because of the requirements on the impl of `Visit` for `()` +note: required because of the requirements on the impl of `Visit` for `()` + --> $DIR/issue-44153.rs:13:10 + | +LL | impl<'a> Visit for () where + | ^^^^^ ^^ error: aborting due to previous error diff --git a/src/test/ui/associated-types/issue-63591.full_tait.stderr b/src/test/ui/associated-types/issue-63591.full_tait.stderr new file mode 100644 index 0000000000000..9857f7e2677a8 --- /dev/null +++ b/src/test/ui/associated-types/issue-63591.full_tait.stderr @@ -0,0 +1,11 @@ +warning: the feature `type_alias_impl_trait` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/issue-63591.rs:6:32 + | +LL | #![cfg_attr(full_tait, feature(type_alias_impl_trait))] + | ^^^^^^^^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #63063 <https://github.com/rust-lang/rust/issues/63063> for more information + +warning: 1 warning emitted + diff --git a/src/test/ui/associated-types/issue-63591.rs b/src/test/ui/associated-types/issue-63591.rs index 4d2e39f4da60c..bba9ae2d9e70f 100644 --- a/src/test/ui/associated-types/issue-63591.rs +++ b/src/test/ui/associated-types/issue-63591.rs @@ -1,7 +1,10 @@ // check-pass #![feature(associated_type_bounds)] -#![feature(type_alias_impl_trait)] +// revisions: min_tait full_tait +#![feature(min_type_alias_impl_trait)] +#![cfg_attr(full_tait, feature(type_alias_impl_trait))] +//[full_tait]~^ WARN incomplete fn main() {} diff --git a/src/test/ui/associated-types/issue-65774-1.stderr b/src/test/ui/associated-types/issue-65774-1.stderr index f644eb5a1f478..fc020e40b5ad2 100644 --- a/src/test/ui/associated-types/issue-65774-1.stderr +++ b/src/test/ui/associated-types/issue-65774-1.stderr @@ -13,7 +13,11 @@ error[E0277]: the trait bound `T: MyDisplay` is not satisfied LL | let closure = |config: &mut <S as MPU>::MpuConfig| writer.my_write(&config); | ^^^^^^^ the trait `MyDisplay` is not implemented for `T` | - = note: required because of the requirements on the impl of `MyDisplay` for `&mut T` +note: required because of the requirements on the impl of `MyDisplay` for `&mut T` + --> $DIR/issue-65774-1.rs:5:24 + | +LL | impl<'a, T: MyDisplay> MyDisplay for &'a mut T { } + | ^^^^^^^^^ ^^^^^^^^^ = note: required for the cast to the object type `dyn MyDisplay` error: aborting due to 2 previous errors diff --git a/src/test/ui/issues/issue-76179.rs b/src/test/ui/associated-types/issue-76179.rs similarity index 100% rename from src/test/ui/issues/issue-76179.rs rename to src/test/ui/associated-types/issue-76179.rs diff --git a/src/test/ui/ast-json/ast-json-output.stdout b/src/test/ui/ast-json/ast-json-output.stdout index d26530efe3ecd..535f57bf6af85 100644 --- a/src/test/ui/ast-json/ast-json-output.stdout +++ b/src/test/ui/ast-json/ast-json-output.stdout @@ -1 +1 @@ -{"attrs":[{"kind":{"variant":"Normal","fields":[{"path":{"span":{"lo":0,"hi":0},"segments":[{"ident":{"name":"crate_type","span":{"lo":0,"hi":0}},"id":0,"args":null}],"tokens":null},"args":{"variant":"Eq","fields":[{"lo":0,"hi":0},{"kind":{"variant":"Interpolated","fields":[{"variant":"NtExpr","fields":[{"id":0,"kind":{"variant":"Lit","fields":[{"token":{"kind":"Str","symbol":"lib","suffix":null},"kind":{"variant":"Str","fields":["lib","Cooked"]},"span":{"lo":0,"hi":0}}]},"span":{"lo":0,"hi":0},"attrs":{"0":null},"tokens":{"0":[[{"variant":"Token","fields":[{"kind":{"variant":"Literal","fields":[{"kind":"Str","symbol":"lib","suffix":null}]},"span":{"lo":0,"hi":0}}]},"Alone"]]}}]}]},"span":{"lo":0,"hi":0}}]},"tokens":null},{"0":[[{"variant":"Token","fields":[{"kind":"Pound","span":{"lo":0,"hi":0}}]},"Joint"],[{"variant":"Token","fields":[{"kind":"Not","span":{"lo":0,"hi":0}}]},"Alone"],[{"variant":"Delimited","fields":[{"open":{"lo":0,"hi":0},"close":{"lo":0,"hi":0}},"Bracket",{"0":[[{"variant":"Token","fields":[{"kind":{"variant":"Ident","fields":["crate_type",false]},"span":{"lo":0,"hi":0}}]},"Alone"],[{"variant":"Token","fields":[{"kind":"Eq","span":{"lo":0,"hi":0}}]},"Alone"],[{"variant":"Token","fields":[{"kind":{"variant":"Literal","fields":[{"kind":"Str","symbol":"lib","suffix":null}]},"span":{"lo":0,"hi":0}}]},"Alone"]]}]},"Alone"]]}]},"id":null,"style":"Inner","span":{"lo":0,"hi":0}}],"items":[{"attrs":[{"kind":{"variant":"Normal","fields":[{"path":{"span":{"lo":0,"hi":0},"segments":[{"ident":{"name":"prelude_import","span":{"lo":0,"hi":0}},"id":0,"args":null}],"tokens":null},"args":"Empty","tokens":null},null]},"id":null,"style":"Outer","span":{"lo":0,"hi":0}}],"id":0,"span":{"lo":0,"hi":0},"vis":{"kind":"Inherited","span":{"lo":0,"hi":0},"tokens":null},"ident":{"name":"","span":{"lo":0,"hi":0}},"kind":{"variant":"Use","fields":[{"prefix":{"span":{"lo":0,"hi":0},"segments":[{"ident":{"name":"{{root}}","span":{"lo":0,"hi":0}},"id":0,"args":null},{"ident":{"name":"std","span":{"lo":0,"hi":0}},"id":0,"args":null},{"ident":{"name":"prelude","span":{"lo":0,"hi":0}},"id":0,"args":null},{"ident":{"name":"v1","span":{"lo":0,"hi":0}},"id":0,"args":null}],"tokens":null},"kind":"Glob","span":{"lo":0,"hi":0}}]},"tokens":null},{"attrs":[{"kind":{"variant":"Normal","fields":[{"path":{"span":{"lo":0,"hi":0},"segments":[{"ident":{"name":"macro_use","span":{"lo":0,"hi":0}},"id":0,"args":null}],"tokens":null},"args":"Empty","tokens":null},null]},"id":null,"style":"Outer","span":{"lo":0,"hi":0}}],"id":0,"span":{"lo":0,"hi":0},"vis":{"kind":"Inherited","span":{"lo":0,"hi":0},"tokens":null},"ident":{"name":"std","span":{"lo":0,"hi":0}},"kind":{"variant":"ExternCrate","fields":[null]},"tokens":null},{"attrs":[],"id":0,"span":{"lo":0,"hi":0},"vis":{"kind":"Inherited","span":{"lo":0,"hi":0},"tokens":null},"ident":{"name":"core","span":{"lo":0,"hi":0}},"kind":{"variant":"ExternCrate","fields":[null]},"tokens":null}],"span":{"lo":0,"hi":0},"proc_macros":[]} +{"attrs":[{"kind":{"variant":"Normal","fields":[{"path":{"span":{"lo":0,"hi":0},"segments":[{"ident":{"name":"crate_type","span":{"lo":0,"hi":0}},"id":0,"args":null}],"tokens":null},"args":{"variant":"Eq","fields":[{"lo":0,"hi":0},{"kind":{"variant":"Interpolated","fields":[{"variant":"NtExpr","fields":[{"id":0,"kind":{"variant":"Lit","fields":[{"token":{"kind":"Str","symbol":"lib","suffix":null},"kind":{"variant":"Str","fields":["lib","Cooked"]},"span":{"lo":0,"hi":0}}]},"span":{"lo":0,"hi":0},"attrs":{"0":null},"tokens":{"0":[[{"variant":"Token","fields":[{"kind":{"variant":"Literal","fields":[{"kind":"Str","symbol":"lib","suffix":null}]},"span":{"lo":0,"hi":0}}]},"Alone"]]}}]}]},"span":{"lo":0,"hi":0}}]},"tokens":null},{"0":[[{"variant":"Token","fields":[{"kind":"Pound","span":{"lo":0,"hi":0}}]},"Joint"],[{"variant":"Token","fields":[{"kind":"Not","span":{"lo":0,"hi":0}}]},"Alone"],[{"variant":"Delimited","fields":[{"open":{"lo":0,"hi":0},"close":{"lo":0,"hi":0}},"Bracket",{"0":[[{"variant":"Token","fields":[{"kind":{"variant":"Ident","fields":["crate_type",false]},"span":{"lo":0,"hi":0}}]},"Alone"],[{"variant":"Token","fields":[{"kind":"Eq","span":{"lo":0,"hi":0}}]},"Alone"],[{"variant":"Token","fields":[{"kind":{"variant":"Literal","fields":[{"kind":"Str","symbol":"lib","suffix":null}]},"span":{"lo":0,"hi":0}}]},"Alone"]]}]},"Alone"]]}]},"id":null,"style":"Inner","span":{"lo":0,"hi":0}}],"items":[{"attrs":[{"kind":{"variant":"Normal","fields":[{"path":{"span":{"lo":0,"hi":0},"segments":[{"ident":{"name":"prelude_import","span":{"lo":0,"hi":0}},"id":0,"args":null}],"tokens":null},"args":"Empty","tokens":null},null]},"id":null,"style":"Outer","span":{"lo":0,"hi":0}}],"id":0,"span":{"lo":0,"hi":0},"vis":{"kind":"Inherited","span":{"lo":0,"hi":0},"tokens":null},"ident":{"name":"","span":{"lo":0,"hi":0}},"kind":{"variant":"Use","fields":[{"prefix":{"span":{"lo":0,"hi":0},"segments":[{"ident":{"name":"{{root}}","span":{"lo":0,"hi":0}},"id":0,"args":null},{"ident":{"name":"std","span":{"lo":0,"hi":0}},"id":0,"args":null},{"ident":{"name":"prelude","span":{"lo":0,"hi":0}},"id":0,"args":null},{"ident":{"name":"rust_2015","span":{"lo":0,"hi":0}},"id":0,"args":null}],"tokens":null},"kind":"Glob","span":{"lo":0,"hi":0}}]},"tokens":null},{"attrs":[{"kind":{"variant":"Normal","fields":[{"path":{"span":{"lo":0,"hi":0},"segments":[{"ident":{"name":"macro_use","span":{"lo":0,"hi":0}},"id":0,"args":null}],"tokens":null},"args":"Empty","tokens":null},null]},"id":null,"style":"Outer","span":{"lo":0,"hi":0}}],"id":0,"span":{"lo":0,"hi":0},"vis":{"kind":"Inherited","span":{"lo":0,"hi":0},"tokens":null},"ident":{"name":"std","span":{"lo":0,"hi":0}},"kind":{"variant":"ExternCrate","fields":[null]},"tokens":null},{"attrs":[],"id":0,"span":{"lo":0,"hi":0},"vis":{"kind":"Inherited","span":{"lo":0,"hi":0},"tokens":null},"ident":{"name":"core","span":{"lo":0,"hi":0}},"kind":{"variant":"ExternCrate","fields":[null]},"tokens":null}],"span":{"lo":0,"hi":0},"proc_macros":[]} diff --git a/src/test/ui/async-await/async-trait-fn.rs b/src/test/ui/async-await/async-trait-fn.rs index f0403b76620c1..e2062e82725c0 100644 --- a/src/test/ui/async-await/async-trait-fn.rs +++ b/src/test/ui/async-await/async-trait-fn.rs @@ -2,6 +2,10 @@ trait T { async fn foo() {} //~ ERROR functions in traits cannot be declared `async` async fn bar(&self) {} //~ ERROR functions in traits cannot be declared `async` + async fn baz() { //~ ERROR functions in traits cannot be declared `async` + // Nested item must not ICE. + fn a() {} + } } fn main() {} diff --git a/src/test/ui/async-await/async-trait-fn.stderr b/src/test/ui/async-await/async-trait-fn.stderr index 6080b2815eeb8..1eb8969a80d20 100644 --- a/src/test/ui/async-await/async-trait-fn.stderr +++ b/src/test/ui/async-await/async-trait-fn.stderr @@ -20,6 +20,22 @@ LL | async fn bar(&self) {} = note: `async` trait functions are not currently supported = note: consider using the `async-trait` crate: https://crates.io/crates/async-trait -error: aborting due to 2 previous errors +error[E0706]: functions in traits cannot be declared `async` + --> $DIR/async-trait-fn.rs:5:5 + | +LL | async fn baz() { + | ^---- + | | + | _____`async` because of this + | | +LL | | // Nested item must not ICE. +LL | | fn a() {} +LL | | } + | |_____^ + | + = note: `async` trait functions are not currently supported + = note: consider using the `async-trait` crate: https://crates.io/crates/async-trait + +error: aborting due to 3 previous errors For more information about this error, try `rustc --explain E0706`. diff --git a/src/test/ui/async-await/issue-61452.stderr b/src/test/ui/async-await/issue-61452.stderr index 5eb4b54871737..f2dec87baf08b 100644 --- a/src/test/ui/async-await/issue-61452.stderr +++ b/src/test/ui/async-await/issue-61452.stderr @@ -13,7 +13,7 @@ LL | pub async fn g(x: usize) { | - | | | first assignment to `x` - | help: make this binding mutable: `mut x` + | help: consider making this binding mutable: `mut x` LL | x += 1; | ^^^^^^ cannot assign twice to immutable variable diff --git a/src/test/ui/async-await/issue-61949-self-return-type.rs b/src/test/ui/async-await/issue-61949-self-return-type.rs index 6a28c69193da0..43429ba2329f9 100644 --- a/src/test/ui/async-await/issue-61949-self-return-type.rs +++ b/src/test/ui/async-await/issue-61949-self-return-type.rs @@ -1,4 +1,3 @@ -// ignore-tidy-linelength // edition:2018 // This test checks that `Self` is prohibited as a return type. See #61949 for context. diff --git a/src/test/ui/async-await/issue-61949-self-return-type.stderr b/src/test/ui/async-await/issue-61949-self-return-type.stderr index 4eeef871c5bfc..52b726e186e3a 100644 --- a/src/test/ui/async-await/issue-61949-self-return-type.stderr +++ b/src/test/ui/async-await/issue-61949-self-return-type.stderr @@ -1,5 +1,5 @@ error[E0760]: `async fn` return type cannot contain a projection or `Self` that references lifetimes from a parent scope - --> $DIR/issue-61949-self-return-type.rs:11:40 + --> $DIR/issue-61949-self-return-type.rs:10:40 | LL | pub async fn new(_bar: &'a i32) -> Self { | ^^^^ help: consider spelling out the type instead: `Foo<'a>` diff --git a/src/test/ui/async-await/issue-67651.stderr b/src/test/ui/async-await/issue-67651.stderr index 99857c215eb8f..89017f6cc3ec6 100644 --- a/src/test/ui/async-await/issue-67651.stderr +++ b/src/test/ui/async-await/issue-67651.stderr @@ -1,4 +1,4 @@ -error[E0119]: conflicting implementations of trait `From` for type `()`: +error[E0119]: conflicting implementations of trait `From` for type `()` --> $DIR/issue-67651.rs:11:1 | LL | impl From for () { diff --git a/src/test/ui/async-await/issue-70818.stderr b/src/test/ui/async-await/issue-70818.stderr index 11fca2dd8ef4c..5f39cf6dde125 100644 --- a/src/test/ui/async-await/issue-70818.stderr +++ b/src/test/ui/async-await/issue-70818.stderr @@ -11,8 +11,8 @@ LL | async { (ty, ty1) } | ^^^ has type `U` which is not `Send` help: consider restricting type parameter `U` | -LL | fn foo<T: Send, U: Send>(ty: T, ty1: U) -> impl Future<Output = (T, U)> + Send { - | ^^^^^^ +LL | fn foo<T: Send, U: std::marker::Send>(ty: T, ty1: U) -> impl Future<Output = (T, U)> + Send { + | ^^^^^^^^^^^^^^^^^^^ error: aborting due to previous error diff --git a/src/test/ui/async-await/issue-72590-type-error-sized.stderr b/src/test/ui/async-await/issue-72590-type-error-sized.stderr index 0f90a4c336c57..50dfeffde7cf0 100644 --- a/src/test/ui/async-await/issue-72590-type-error-sized.stderr +++ b/src/test/ui/async-await/issue-72590-type-error-sized.stderr @@ -17,7 +17,11 @@ LL | async fn frob(self) {} | ^^^^ doesn't have a size known at compile-time | = help: within `Foo`, the trait `Sized` is not implemented for `str` - = note: required because it appears within the type `Foo` +note: required because it appears within the type `Foo` + --> $DIR/issue-72590-type-error-sized.rs:5:8 + | +LL | struct Foo { + | ^^^ = help: unsized fn params are gated as an unstable feature help: function arguments must have a statically known size, borrowed types always have a known size | diff --git a/src/test/ui/issue-74047.rs b/src/test/ui/async-await/issue-74047.rs similarity index 100% rename from src/test/ui/issue-74047.rs rename to src/test/ui/async-await/issue-74047.rs diff --git a/src/test/ui/issue-74047.stderr b/src/test/ui/async-await/issue-74047.stderr similarity index 100% rename from src/test/ui/issue-74047.stderr rename to src/test/ui/async-await/issue-74047.stderr diff --git a/src/test/ui/async-await/issues/issue-60655-latebound-regions.full_tait.stderr b/src/test/ui/async-await/issues/issue-60655-latebound-regions.full_tait.stderr new file mode 100644 index 0000000000000..cc61becd71b1f --- /dev/null +++ b/src/test/ui/async-await/issues/issue-60655-latebound-regions.full_tait.stderr @@ -0,0 +1,11 @@ +warning: the feature `type_alias_impl_trait` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/issue-60655-latebound-regions.rs:8:32 + | +LL | #![cfg_attr(full_tait, feature(type_alias_impl_trait))] + | ^^^^^^^^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #63063 <https://github.com/rust-lang/rust/issues/63063> for more information + +warning: 1 warning emitted + diff --git a/src/test/ui/async-await/issues/issue-60655-latebound-regions.rs b/src/test/ui/async-await/issues/issue-60655-latebound-regions.rs index 66a3b07c3bd96..f8b6a43896166 100644 --- a/src/test/ui/async-await/issues/issue-60655-latebound-regions.rs +++ b/src/test/ui/async-await/issues/issue-60655-latebound-regions.rs @@ -3,7 +3,10 @@ // check-pass // edition:2018 -#![feature(type_alias_impl_trait)] +// revisions: min_tait full_tait +#![feature(min_type_alias_impl_trait)] +#![cfg_attr(full_tait, feature(type_alias_impl_trait))] +//[full_tait]~^ WARN incomplete use std::future::Future; diff --git a/src/test/ui/async-await/issues/issue-64964.rs b/src/test/ui/async-await/issues/issue-64964.rs index 11f6cb6af9cc6..5313d1715c483 100644 --- a/src/test/ui/async-await/issues/issue-64964.rs +++ b/src/test/ui/async-await/issues/issue-64964.rs @@ -1,5 +1,5 @@ // check-pass -// compile-flags: -Z query-dep-graph +// compile-flags: -Z query-dep-graph -C incremental=tmp/issue-64964 // edition:2018 // Regression test for ICE related to `await`ing in a method + incr. comp. (#64964) diff --git a/src/test/ui/async-await/pin-needed-to-poll-2.rs b/src/test/ui/async-await/pin-needed-to-poll-2.rs new file mode 100644 index 0000000000000..6ce70336d0c63 --- /dev/null +++ b/src/test/ui/async-await/pin-needed-to-poll-2.rs @@ -0,0 +1,48 @@ +use std::{ + future::Future, + pin::Pin, + marker::Unpin, + task::{Context, Poll}, +}; + +struct Sleep(std::marker::PhantomPinned); + +impl Future for Sleep { + type Output = (); + + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> { + Poll::Ready(()) + } +} + +impl Drop for Sleep { + fn drop(&mut self) {} +} + +fn sleep() -> Sleep { + Sleep(std::marker::PhantomPinned) +} + + +struct MyFuture { + sleep: Sleep, +} + +impl MyFuture { + fn new() -> Self { + Self { + sleep: sleep(), + } + } +} + +impl Future for MyFuture { + type Output = (); + + fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> { + Pin::new(&mut self.sleep).poll(cx) + //~^ ERROR `PhantomPinned` cannot be unpinned + } +} + +fn main() {} diff --git a/src/test/ui/async-await/pin-needed-to-poll-2.stderr b/src/test/ui/async-await/pin-needed-to-poll-2.stderr new file mode 100644 index 0000000000000..c4d21de8aaf75 --- /dev/null +++ b/src/test/ui/async-await/pin-needed-to-poll-2.stderr @@ -0,0 +1,17 @@ +error[E0277]: `PhantomPinned` cannot be unpinned + --> $DIR/pin-needed-to-poll-2.rs:43:9 + | +LL | Pin::new(&mut self.sleep).poll(cx) + | ^^^^^^^^ within `Sleep`, the trait `Unpin` is not implemented for `PhantomPinned` + | + = note: consider using `Box::pin` +note: required because it appears within the type `Sleep` + --> $DIR/pin-needed-to-poll-2.rs:8:8 + | +LL | struct Sleep(std::marker::PhantomPinned); + | ^^^^^ + = note: required by `Pin::<P>::new` + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0277`. diff --git a/src/test/ui/async-await/pin-needed-to-poll.rs b/src/test/ui/async-await/pin-needed-to-poll.rs new file mode 100644 index 0000000000000..0d1fe684f60e2 --- /dev/null +++ b/src/test/ui/async-await/pin-needed-to-poll.rs @@ -0,0 +1,47 @@ +use std::{ + future::Future, + pin::Pin, + task::{Context, Poll}, +}; + +struct Sleep; + +impl Future for Sleep { + type Output = (); + + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> { + Poll::Ready(()) + } +} + +impl Drop for Sleep { + fn drop(&mut self) {} +} + +fn sleep() -> Sleep { + Sleep +} + + +struct MyFuture { + sleep: Sleep, +} + +impl MyFuture { + fn new() -> Self { + Self { + sleep: sleep(), + } + } +} + +impl Future for MyFuture { + type Output = (); + + fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> { + self.sleep.poll(cx) + //~^ ERROR no method named `poll` found for struct `Sleep` in the current scope + } +} + +fn main() {} diff --git a/src/test/ui/async-await/pin-needed-to-poll.stderr b/src/test/ui/async-await/pin-needed-to-poll.stderr new file mode 100644 index 0000000000000..0756a4d59c19b --- /dev/null +++ b/src/test/ui/async-await/pin-needed-to-poll.stderr @@ -0,0 +1,22 @@ +error[E0599]: no method named `poll` found for struct `Sleep` in the current scope + --> $DIR/pin-needed-to-poll.rs:42:20 + | +LL | struct Sleep; + | ------------- method `poll` not found for this +... +LL | self.sleep.poll(cx) + | ^^^^ method not found in `Sleep` + | + ::: $SRC_DIR/core/src/future/future.rs:LL:COL + | +LL | fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output>; + | ---- the method is available for `Pin<&mut Sleep>` here + | +help: consider wrapping the receiver expression with the appropriate type + | +LL | Pin::new(&mut self.sleep).poll(cx) + | ^^^^^^^^^^^^^ ^ + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0599`. diff --git a/src/test/ui/attr-main-2.rs b/src/test/ui/attr-main-2.rs deleted file mode 100644 index 3a51f83ba3b18..0000000000000 --- a/src/test/ui/attr-main-2.rs +++ /dev/null @@ -1,11 +0,0 @@ -// run-pass - -#![feature(main)] - -pub fn main() { - panic!() -} - -#[main] -fn foo() { -} diff --git a/src/test/ui/attr-main.rs b/src/test/ui/attr-main.rs deleted file mode 100644 index 9c4caaa4a42eb..0000000000000 --- a/src/test/ui/attr-main.rs +++ /dev/null @@ -1,8 +0,0 @@ -// run-pass -// pretty-expanded FIXME #23616 - -#![feature(main)] - -#[main] -fn foo() { -} diff --git a/src/test/ui/attr.rs b/src/test/ui/attr.rs deleted file mode 100644 index 9c4caaa4a42eb..0000000000000 --- a/src/test/ui/attr.rs +++ /dev/null @@ -1,8 +0,0 @@ -// run-pass -// pretty-expanded FIXME #23616 - -#![feature(main)] - -#[main] -fn foo() { -} diff --git a/src/test/ui/attributes/doc-attr.rs b/src/test/ui/attributes/doc-attr.rs index 3519b5707b375..980d1c0e2077d 100644 --- a/src/test/ui/attributes/doc-attr.rs +++ b/src/test/ui/attributes/doc-attr.rs @@ -1,11 +1,25 @@ #![crate_type = "lib"] -#![deny(unused_attributes)] -//~^ NOTE lint level is defined here +#![deny(warnings)] #![doc(as_ptr)] //~^ ERROR unknown `doc` attribute -//~| WARNING will become a hard error in a future release +//~^^ WARN #[doc(as_ptr)] //~^ ERROR unknown `doc` attribute -//~| WARNING will become a hard error in a future release +//~^^ WARN pub fn foo() {} + +#[doc(123)] +//~^ ERROR invalid `doc` attribute +//~| WARN +#[doc("hello", "bar")] +//~^ ERROR invalid `doc` attribute +//~| WARN +//~| ERROR invalid `doc` attribute +//~| WARN +#[doc(foo::bar, crate::bar::baz = "bye")] +//~^ ERROR unknown `doc` attribute +//~| WARN +//~| ERROR unknown `doc` attribute +//~| WARN +fn bar() {} diff --git a/src/test/ui/attributes/doc-attr.stderr b/src/test/ui/attributes/doc-attr.stderr index 9666db2b10e01..cc2494c92e649 100644 --- a/src/test/ui/attributes/doc-attr.stderr +++ b/src/test/ui/attributes/doc-attr.stderr @@ -1,5 +1,5 @@ error: unknown `doc` attribute `as_ptr` - --> $DIR/doc-attr.rs:8:7 + --> $DIR/doc-attr.rs:7:7 | LL | #[doc(as_ptr)] | ^^^^^^ @@ -7,17 +7,65 @@ LL | #[doc(as_ptr)] note: the lint level is defined here --> $DIR/doc-attr.rs:2:9 | -LL | #![deny(unused_attributes)] - | ^^^^^^^^^^^^^^^^^ +LL | #![deny(warnings)] + | ^^^^^^^^ + = note: `#[deny(invalid_doc_attributes)]` implied by `#[deny(warnings)]` = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #82730 <https://github.com/rust-lang/rust/issues/82730> + +error: invalid `doc` attribute + --> $DIR/doc-attr.rs:12:7 + | +LL | #[doc(123)] + | ^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #82730 <https://github.com/rust-lang/rust/issues/82730> + +error: invalid `doc` attribute + --> $DIR/doc-attr.rs:15:7 + | +LL | #[doc("hello", "bar")] + | ^^^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #82730 <https://github.com/rust-lang/rust/issues/82730> + +error: invalid `doc` attribute + --> $DIR/doc-attr.rs:15:16 + | +LL | #[doc("hello", "bar")] + | ^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #82730 <https://github.com/rust-lang/rust/issues/82730> + +error: unknown `doc` attribute `foo::bar` + --> $DIR/doc-attr.rs:20:7 + | +LL | #[doc(foo::bar, crate::bar::baz = "bye")] + | ^^^^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #82730 <https://github.com/rust-lang/rust/issues/82730> + +error: unknown `doc` attribute `crate::bar::baz` + --> $DIR/doc-attr.rs:20:17 + | +LL | #[doc(foo::bar, crate::bar::baz = "bye")] + | ^^^^^^^^^^^^^^^^^^^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #82730 <https://github.com/rust-lang/rust/issues/82730> error: unknown `doc` attribute `as_ptr` - --> $DIR/doc-attr.rs:4:8 + --> $DIR/doc-attr.rs:3:8 | LL | #![doc(as_ptr)] | ^^^^^^ | = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #82730 <https://github.com/rust-lang/rust/issues/82730> -error: aborting due to 2 previous errors +error: aborting due to 7 previous errors diff --git a/src/test/ui/attributes/doc-attr2.rs b/src/test/ui/attributes/doc-attr2.rs new file mode 100644 index 0000000000000..3fb484644d7fa --- /dev/null +++ b/src/test/ui/attributes/doc-attr2.rs @@ -0,0 +1,11 @@ +#![crate_type = "lib"] +#![deny(warnings)] + +#[doc(test(no_crate_inject))] //~ ERROR +//~^ WARN +pub fn foo() {} + +pub mod bar { + #![doc(test(no_crate_inject))] //~ ERROR + //~^ WARN +} diff --git a/src/test/ui/attributes/doc-attr2.stderr b/src/test/ui/attributes/doc-attr2.stderr new file mode 100644 index 0000000000000..643107318b979 --- /dev/null +++ b/src/test/ui/attributes/doc-attr2.stderr @@ -0,0 +1,26 @@ +error: `#![doc(test(...)]` is only allowed as a crate-level attribute + --> $DIR/doc-attr2.rs:4:7 + | +LL | #[doc(test(no_crate_inject))] + | ^^^^^^^^^^^^^^^^^^^^^ + | +note: the lint level is defined here + --> $DIR/doc-attr2.rs:2:9 + | +LL | #![deny(warnings)] + | ^^^^^^^^ + = note: `#[deny(invalid_doc_attributes)]` implied by `#[deny(warnings)]` + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #82730 <https://github.com/rust-lang/rust/issues/82730> + +error: `#![doc(test(...)]` is only allowed as a crate-level attribute + --> $DIR/doc-attr2.rs:9:12 + | +LL | #![doc(test(no_crate_inject))] + | ^^^^^^^^^^^^^^^^^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #82730 <https://github.com/rust-lang/rust/issues/82730> + +error: aborting due to 2 previous errors + diff --git a/src/test/ui/attributes/nonterminal-expansion.rs b/src/test/ui/attributes/nonterminal-expansion.rs index b4f523936a0b0..d663431d6836f 100644 --- a/src/test/ui/attributes/nonterminal-expansion.rs +++ b/src/test/ui/attributes/nonterminal-expansion.rs @@ -3,7 +3,6 @@ macro_rules! pass_nonterminal { ($n:expr) => { #[repr(align($n))] //~ ERROR expected unsuffixed literal or identifier, found `n!()` - //~| ERROR unrecognized representation hint struct S; }; } diff --git a/src/test/ui/attributes/nonterminal-expansion.stderr b/src/test/ui/attributes/nonterminal-expansion.stderr index 9f7f185f94752..75663a666a56d 100644 --- a/src/test/ui/attributes/nonterminal-expansion.stderr +++ b/src/test/ui/attributes/nonterminal-expansion.stderr @@ -9,17 +9,5 @@ LL | pass_nonterminal!(n!()); | = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) -error[E0552]: unrecognized representation hint - --> $DIR/nonterminal-expansion.rs:5:16 - | -LL | #[repr(align($n))] - | ^^^^^^^^^ -... -LL | pass_nonterminal!(n!()); - | ------------------------ in this macro invocation - | - = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) - -error: aborting due to 2 previous errors +error: aborting due to previous error -For more information about this error, try `rustc --explain E0552`. diff --git a/src/test/ui/bad/bad-sized.stderr b/src/test/ui/bad/bad-sized.stderr deleted file mode 100644 index 260d78b543a4c..0000000000000 --- a/src/test/ui/bad/bad-sized.stderr +++ /dev/null @@ -1,37 +0,0 @@ -error[E0225]: only auto traits can be used as additional traits in a trait object - --> $DIR/bad-sized.rs:4:28 - | -LL | let x: Vec<dyn Trait + Sized> = Vec::new(); - | ----- ^^^^^ additional non-auto trait - | | - | first non-auto trait - | - = help: consider creating a new trait with all of these as super-traits and using that trait here instead: `trait NewTrait: Trait + Sized {}` - = note: auto-traits like `Send` and `Sync` are traits that have special properties; for more information on them, visit <https://doc.rust-lang.org/reference/special-types-and-traits.html#auto-traits> - -error[E0277]: the size for values of type `dyn Trait` cannot be known at compilation time - --> $DIR/bad-sized.rs:4:12 - | -LL | let x: Vec<dyn Trait + Sized> = Vec::new(); - | ^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time - | - ::: $SRC_DIR/alloc/src/vec/mod.rs:LL:COL - | -LL | pub struct Vec<T, #[unstable(feature = "allocator_api", issue = "32838")] A: Allocator = Global> { - | - required by this bound in `Vec` - | - = help: the trait `Sized` is not implemented for `dyn Trait` - -error[E0277]: the size for values of type `dyn Trait` cannot be known at compilation time - --> $DIR/bad-sized.rs:4:37 - | -LL | let x: Vec<dyn Trait + Sized> = Vec::new(); - | ^^^^^^^^ doesn't have a size known at compile-time - | - = help: the trait `Sized` is not implemented for `dyn Trait` - = note: required by `Vec::<T>::new` - -error: aborting due to 3 previous errors - -Some errors have detailed explanations: E0225, E0277. -For more information about an error, try `rustc --explain E0225`. diff --git a/src/test/ui/binding/issue-53114-safety-checks.rs b/src/test/ui/binding/issue-53114-safety-checks.rs index 28adb7571a98b..ca4f0efd2392b 100644 --- a/src/test/ui/binding/issue-53114-safety-checks.rs +++ b/src/test/ui/binding/issue-53114-safety-checks.rs @@ -20,13 +20,13 @@ fn let_wild_gets_unsafe_field() { let u1 = U { a: I(0) }; let u2 = U { a: I(1) }; let p = P { a: &2, b: &3 }; - let _ = &p.b; //~ WARN E0133 + let _ = &p.b; //~ WARN reference to packed field //~^ WARN will become a hard error let _ = u1.a; // #53114: should eventually signal error as well let _ = &u2.a; //~ ERROR [E0133] // variation on above with `_` in substructure - let (_,) = (&p.b,); //~ WARN E0133 + let (_,) = (&p.b,); //~ WARN reference to packed field //~^ WARN will become a hard error let (_,) = (u1.a,); //~ ERROR [E0133] let (_,) = (&u2.a,); //~ ERROR [E0133] @@ -36,13 +36,13 @@ fn match_unsafe_field_to_wild() { let u1 = U { a: I(0) }; let u2 = U { a: I(1) }; let p = P { a: &2, b: &3 }; - match &p.b { _ => { } } //~ WARN E0133 + match &p.b { _ => { } } //~ WARN reference to packed field //~^ WARN will become a hard error match u1.a { _ => { } } //~ ERROR [E0133] match &u2.a { _ => { } } //~ ERROR [E0133] // variation on above with `_` in substructure - match (&p.b,) { (_,) => { } } //~ WARN E0133 + match (&p.b,) { (_,) => { } } //~ WARN reference to packed field //~^ WARN will become a hard error match (u1.a,) { (_,) => { } } //~ ERROR [E0133] match (&u2.a,) { (_,) => { } } //~ ERROR [E0133] diff --git a/src/test/ui/binding/issue-53114-safety-checks.stderr b/src/test/ui/binding/issue-53114-safety-checks.stderr index d4b8dfbade5d6..9e7deea4524cb 100644 --- a/src/test/ui/binding/issue-53114-safety-checks.stderr +++ b/src/test/ui/binding/issue-53114-safety-checks.stderr @@ -1,13 +1,43 @@ -warning: borrow of packed field is unsafe and requires unsafe function or block (error E0133) +warning: reference to packed field is unaligned --> $DIR/issue-53114-safety-checks.rs:23:13 | LL | let _ = &p.b; | ^^^^ | - = note: `#[warn(safe_packed_borrows)]` on by default + = note: `#[warn(unaligned_references)]` on by default = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = note: for more information, see issue #46043 <https://github.com/rust-lang/rust/issues/46043> - = note: fields of packed structs might be misaligned: dereferencing a misaligned pointer or even just creating a misaligned reference is undefined behavior + = note: for more information, see issue #82523 <https://github.com/rust-lang/rust/issues/82523> + = note: fields of packed structs are not properly aligned, and creating a misaligned reference is undefined behavior (even if that reference is never dereferenced) + +warning: reference to packed field is unaligned + --> $DIR/issue-53114-safety-checks.rs:29:17 + | +LL | let (_,) = (&p.b,); + | ^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #82523 <https://github.com/rust-lang/rust/issues/82523> + = note: fields of packed structs are not properly aligned, and creating a misaligned reference is undefined behavior (even if that reference is never dereferenced) + +warning: reference to packed field is unaligned + --> $DIR/issue-53114-safety-checks.rs:39:11 + | +LL | match &p.b { _ => { } } + | ^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #82523 <https://github.com/rust-lang/rust/issues/82523> + = note: fields of packed structs are not properly aligned, and creating a misaligned reference is undefined behavior (even if that reference is never dereferenced) + +warning: reference to packed field is unaligned + --> $DIR/issue-53114-safety-checks.rs:45:12 + | +LL | match (&p.b,) { (_,) => { } } + | ^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #82523 <https://github.com/rust-lang/rust/issues/82523> + = note: fields of packed structs are not properly aligned, and creating a misaligned reference is undefined behavior (even if that reference is never dereferenced) error[E0133]: access to union field is unsafe and requires unsafe function or block --> $DIR/issue-53114-safety-checks.rs:26:13 @@ -17,16 +47,6 @@ LL | let _ = &u2.a; | = note: the field may not be properly initialized: using uninitialized data will cause undefined behavior -warning: borrow of packed field is unsafe and requires unsafe function or block (error E0133) - --> $DIR/issue-53114-safety-checks.rs:29:17 - | -LL | let (_,) = (&p.b,); - | ^^^^ - | - = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = note: for more information, see issue #46043 <https://github.com/rust-lang/rust/issues/46043> - = note: fields of packed structs might be misaligned: dereferencing a misaligned pointer or even just creating a misaligned reference is undefined behavior - error[E0133]: access to union field is unsafe and requires unsafe function or block --> $DIR/issue-53114-safety-checks.rs:31:17 | @@ -43,16 +63,6 @@ LL | let (_,) = (&u2.a,); | = note: the field may not be properly initialized: using uninitialized data will cause undefined behavior -warning: borrow of packed field is unsafe and requires unsafe function or block (error E0133) - --> $DIR/issue-53114-safety-checks.rs:39:11 - | -LL | match &p.b { _ => { } } - | ^^^^ - | - = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = note: for more information, see issue #46043 <https://github.com/rust-lang/rust/issues/46043> - = note: fields of packed structs might be misaligned: dereferencing a misaligned pointer or even just creating a misaligned reference is undefined behavior - error[E0133]: access to union field is unsafe and requires unsafe function or block --> $DIR/issue-53114-safety-checks.rs:41:11 | @@ -69,16 +79,6 @@ LL | match &u2.a { _ => { } } | = note: the field may not be properly initialized: using uninitialized data will cause undefined behavior -warning: borrow of packed field is unsafe and requires unsafe function or block (error E0133) - --> $DIR/issue-53114-safety-checks.rs:45:12 - | -LL | match (&p.b,) { (_,) => { } } - | ^^^^ - | - = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = note: for more information, see issue #46043 <https://github.com/rust-lang/rust/issues/46043> - = note: fields of packed structs might be misaligned: dereferencing a misaligned pointer or even just creating a misaligned reference is undefined behavior - error[E0133]: access to union field is unsafe and requires unsafe function or block --> $DIR/issue-53114-safety-checks.rs:47:12 | diff --git a/src/test/ui/shadow.rs b/src/test/ui/binding/shadow.rs similarity index 100% rename from src/test/ui/shadow.rs rename to src/test/ui/binding/shadow.rs diff --git a/src/test/ui/block-result/issue-22645.stderr b/src/test/ui/block-result/issue-22645.stderr index 6649e67a5093c..397bdac60513e 100644 --- a/src/test/ui/block-result/issue-22645.stderr +++ b/src/test/ui/block-result/issue-22645.stderr @@ -6,7 +6,11 @@ LL | b + 3 | = help: the following implementations were found: <f64 as Scalar> - = note: required because of the requirements on the impl of `Add<{integer}>` for `Bob` +note: required because of the requirements on the impl of `Add<{integer}>` for `Bob` + --> $DIR/issue-22645.rs:8:19 + | +LL | impl<RHS: Scalar> Add <RHS> for Bob { + | ^^^^^^^^^ ^^^ error[E0308]: mismatched types --> $DIR/issue-22645.rs:15:3 diff --git a/src/test/ui/borrowck/bindings-after-at-or-patterns-slice-patterns-box-patterns.rs b/src/test/ui/borrowck/bindings-after-at-or-patterns-slice-patterns-box-patterns.rs index 97f96ab69295f..2839a65deaa4b 100644 --- a/src/test/ui/borrowck/bindings-after-at-or-patterns-slice-patterns-box-patterns.rs +++ b/src/test/ui/borrowck/bindings-after-at-or-patterns-slice-patterns-box-patterns.rs @@ -1,6 +1,5 @@ // Tests using a combination of pattern features has the expected borrow checking behavior #![feature(bindings_after_at)] -#![feature(or_patterns)] #![feature(box_patterns)] enum Test { diff --git a/src/test/ui/borrowck/bindings-after-at-or-patterns-slice-patterns-box-patterns.stderr b/src/test/ui/borrowck/bindings-after-at-or-patterns-slice-patterns-box-patterns.stderr index 96e313b39ed1e..2204dcabc9e47 100644 --- a/src/test/ui/borrowck/bindings-after-at-or-patterns-slice-patterns-box-patterns.stderr +++ b/src/test/ui/borrowck/bindings-after-at-or-patterns-slice-patterns-box-patterns.stderr @@ -1,5 +1,5 @@ error: cannot borrow value as mutable because it is also borrowed as immutable - --> $DIR/bindings-after-at-or-patterns-slice-patterns-box-patterns.rs:38:9 + --> $DIR/bindings-after-at-or-patterns-slice-patterns-box-patterns.rs:37:9 | LL | ref foo @ [.., ref mut bar] => (), | -------^^^^^^^^-----------^ @@ -8,7 +8,7 @@ LL | ref foo @ [.., ref mut bar] => (), | immutable borrow, by `foo`, occurs here error: cannot borrow value as mutable because it is also borrowed as immutable - --> $DIR/bindings-after-at-or-patterns-slice-patterns-box-patterns.rs:122:9 + --> $DIR/bindings-after-at-or-patterns-slice-patterns-box-patterns.rs:121:9 | LL | ref foo @ Some(box ref mut s) => (), | -------^^^^^^^^^^^^---------^ @@ -17,7 +17,7 @@ LL | ref foo @ Some(box ref mut s) => (), | immutable borrow, by `foo`, occurs here error[E0382]: borrow of moved value: `x` - --> $DIR/bindings-after-at-or-patterns-slice-patterns-box-patterns.rs:20:5 + --> $DIR/bindings-after-at-or-patterns-slice-patterns-box-patterns.rs:19:5 | LL | fn bindings_after_at_slice_patterns_move_binding(x: [String; 4]) { | - move occurs because `x` has type `[String; 4]`, which does not implement the `Copy` trait @@ -29,7 +29,7 @@ LL | &x; | ^^ value borrowed here after move error[E0502]: cannot borrow `x` as immutable because it is also borrowed as mutable - --> $DIR/bindings-after-at-or-patterns-slice-patterns-box-patterns.rs:30:5 + --> $DIR/bindings-after-at-or-patterns-slice-patterns-box-patterns.rs:29:5 | LL | ref mut foo @ [.., _] => Some(foo), | --------------------- mutable borrow occurs here @@ -41,7 +41,7 @@ LL | drop(r); | - mutable borrow later used here error[E0502]: cannot borrow `x` as mutable because it is also borrowed as immutable - --> $DIR/bindings-after-at-or-patterns-slice-patterns-box-patterns.rs:52:5 + --> $DIR/bindings-after-at-or-patterns-slice-patterns-box-patterns.rs:51:5 | LL | [ref foo @ .., ref bar] => Some(foo), | ------------ immutable borrow occurs here @@ -53,7 +53,7 @@ LL | drop(r); | - immutable borrow later used here error[E0502]: cannot borrow `x` as mutable because it is also borrowed as immutable - --> $DIR/bindings-after-at-or-patterns-slice-patterns-box-patterns.rs:64:5 + --> $DIR/bindings-after-at-or-patterns-slice-patterns-box-patterns.rs:63:5 | LL | ref foo @ [.., ref bar] => Some(foo), | ----------------------- immutable borrow occurs here @@ -65,7 +65,7 @@ LL | drop(r); | - immutable borrow later used here error[E0382]: borrow of moved value: `x` - --> $DIR/bindings-after-at-or-patterns-slice-patterns-box-patterns.rs:78:5 + --> $DIR/bindings-after-at-or-patterns-slice-patterns-box-patterns.rs:77:5 | LL | fn bindings_after_at_or_patterns_move(x: Option<Test>) { | - move occurs because `x` has type `Option<Test>`, which does not implement the `Copy` trait @@ -80,7 +80,7 @@ LL | &x; | ^^ value borrowed here after move error[E0502]: cannot borrow `x` as mutable because it is also borrowed as immutable - --> $DIR/bindings-after-at-or-patterns-slice-patterns-box-patterns.rs:88:5 + --> $DIR/bindings-after-at-or-patterns-slice-patterns-box-patterns.rs:87:5 | LL | ref foo @ Some(Test::Foo | Test::Bar) => Some(foo), | ------------------------------------- immutable borrow occurs here @@ -92,7 +92,7 @@ LL | drop(r); | - immutable borrow later used here error[E0502]: cannot borrow `x` as immutable because it is also borrowed as mutable - --> $DIR/bindings-after-at-or-patterns-slice-patterns-box-patterns.rs:100:5 + --> $DIR/bindings-after-at-or-patterns-slice-patterns-box-patterns.rs:99:5 | LL | ref mut foo @ Some(Test::Foo | Test::Bar) => Some(foo), | ----------------------------------------- mutable borrow occurs here @@ -104,7 +104,7 @@ LL | drop(r); | - mutable borrow later used here error[E0502]: cannot borrow `x` as mutable because it is also borrowed as immutable - --> $DIR/bindings-after-at-or-patterns-slice-patterns-box-patterns.rs:114:5 + --> $DIR/bindings-after-at-or-patterns-slice-patterns-box-patterns.rs:113:5 | LL | ref foo @ Some(box ref s) => Some(foo), | ------------------------- immutable borrow occurs here @@ -116,7 +116,7 @@ LL | drop(r); | - immutable borrow later used here error[E0382]: borrow of moved value: `x` - --> $DIR/bindings-after-at-or-patterns-slice-patterns-box-patterns.rs:136:5 + --> $DIR/bindings-after-at-or-patterns-slice-patterns-box-patterns.rs:135:5 | LL | fn bindings_after_at_slice_patterns_or_patterns_moves(x: [Option<Test>; 4]) { | - move occurs because `x` has type `[Option<Test>; 4]`, which does not implement the `Copy` trait @@ -131,7 +131,7 @@ LL | &x; | ^^ value borrowed here after move error[E0502]: cannot borrow `x` as mutable because it is also borrowed as immutable - --> $DIR/bindings-after-at-or-patterns-slice-patterns-box-patterns.rs:146:5 + --> $DIR/bindings-after-at-or-patterns-slice-patterns-box-patterns.rs:145:5 | LL | ref a @ [ref b @ .., Some(Test::Foo | Test::Bar)] => Some(a), | ------------------------------------------------- immutable borrow occurs here @@ -143,7 +143,7 @@ LL | drop(r); | - immutable borrow later used here error[E0502]: cannot borrow `x` as mutable because it is also borrowed as immutable - --> $DIR/bindings-after-at-or-patterns-slice-patterns-box-patterns.rs:158:5 + --> $DIR/bindings-after-at-or-patterns-slice-patterns-box-patterns.rs:157:5 | LL | ref a @ [ref b @ .., Some(Test::Foo | Test::Bar)] => Some(b), | ---------- immutable borrow occurs here @@ -155,7 +155,7 @@ LL | drop(r); | - immutable borrow later used here error[E0502]: cannot borrow `x` as mutable because it is also borrowed as immutable - --> $DIR/bindings-after-at-or-patterns-slice-patterns-box-patterns.rs:172:5 + --> $DIR/bindings-after-at-or-patterns-slice-patterns-box-patterns.rs:171:5 | LL | [_, ref a @ Some(box ref b), ..] => Some(a), | ----------------------- immutable borrow occurs here @@ -167,7 +167,7 @@ LL | drop(r); | - immutable borrow later used here error[E0502]: cannot borrow `x` as mutable because it is also borrowed as immutable - --> $DIR/bindings-after-at-or-patterns-slice-patterns-box-patterns.rs:188:5 + --> $DIR/bindings-after-at-or-patterns-slice-patterns-box-patterns.rs:187:5 | LL | [_, ref a @ Some(box Test::Foo | box Test::Bar), ..] => Some(a), | ------------------------------------------- immutable borrow occurs here @@ -179,7 +179,7 @@ LL | drop(r); | - immutable borrow later used here error[E0502]: cannot borrow `x` as immutable because it is also borrowed as mutable - --> $DIR/bindings-after-at-or-patterns-slice-patterns-box-patterns.rs:202:5 + --> $DIR/bindings-after-at-or-patterns-slice-patterns-box-patterns.rs:201:5 | LL | [_, ref mut a @ Some(box Test::Foo | box Test::Bar), ..] => Some(a), | ----------------------------------------------- mutable borrow occurs here @@ -191,7 +191,7 @@ LL | drop(r); | - mutable borrow later used here error[E0502]: cannot borrow `x` as mutable because it is also borrowed as immutable - --> $DIR/bindings-after-at-or-patterns-slice-patterns-box-patterns.rs:216:5 + --> $DIR/bindings-after-at-or-patterns-slice-patterns-box-patterns.rs:215:5 | LL | ref a @ [_, ref b @ Some(box Test::Foo | box Test::Bar), ..] => Some(a), | ------------------------------------------------------------ immutable borrow occurs here diff --git a/src/test/ui/borrowck/borrowck-asm.stderr b/src/test/ui/borrowck/borrowck-asm.stderr index 3dccca784151e..74cf5a55b70ab 100644 --- a/src/test/ui/borrowck/borrowck-asm.stderr +++ b/src/test/ui/borrowck/borrowck-asm.stderr @@ -29,7 +29,7 @@ LL | let x = 3; | - | | | first assignment to `x` - | help: make this binding mutable: `mut x` + | help: consider making this binding mutable: `mut x` LL | unsafe { LL | llvm_asm!("nop" : "=r"(x)); | ^ cannot assign twice to immutable variable @@ -41,7 +41,7 @@ LL | let x = 3; | - | | | first assignment to `x` - | help: make this binding mutable: `mut x` + | help: consider making this binding mutable: `mut x` LL | unsafe { LL | llvm_asm!("nop" : "+r"(x)); | ^ cannot assign twice to immutable variable diff --git a/src/test/ui/borrowck/borrowck-describe-lvalue.rs b/src/test/ui/borrowck/borrowck-describe-lvalue.rs index c8bfbe0729c59..0e6c0635adb45 100644 --- a/src/test/ui/borrowck/borrowck-describe-lvalue.rs +++ b/src/test/ui/borrowck/borrowck-describe-lvalue.rs @@ -1,5 +1,3 @@ -// ignore-tidy-linelength - pub struct Foo { x: u32 } diff --git a/src/test/ui/borrowck/borrowck-describe-lvalue.stderr b/src/test/ui/borrowck/borrowck-describe-lvalue.stderr index e386aa1f1f4d4..0f2ebbcbf3cdb 100644 --- a/src/test/ui/borrowck/borrowck-describe-lvalue.stderr +++ b/src/test/ui/borrowck/borrowck-describe-lvalue.stderr @@ -1,5 +1,5 @@ error[E0499]: cannot borrow `x` as mutable more than once at a time - --> $DIR/borrowck-describe-lvalue.rs:256:13 + --> $DIR/borrowck-describe-lvalue.rs:254:13 | LL | let y = &mut x; | ------ first mutable borrow occurs here @@ -9,7 +9,7 @@ LL | *y = 1; | ------ first borrow later used here error[E0499]: cannot borrow `x` as mutable more than once at a time - --> $DIR/borrowck-describe-lvalue.rs:266:20 + --> $DIR/borrowck-describe-lvalue.rs:264:20 | LL | let y = &mut x; | ------ first mutable borrow occurs here @@ -19,7 +19,7 @@ LL | *y = 1; | ------ first borrow later used here error: captured variable cannot escape `FnMut` closure body - --> $DIR/borrowck-describe-lvalue.rs:264:16 + --> $DIR/borrowck-describe-lvalue.rs:262:16 | LL | let mut x = 0; | ----- variable defined here @@ -38,7 +38,7 @@ LL | | } = note: ...therefore, they cannot allow references to captured variables to escape error[E0503]: cannot use `f.x` because it was mutably borrowed - --> $DIR/borrowck-describe-lvalue.rs:39:9 + --> $DIR/borrowck-describe-lvalue.rs:37:9 | LL | let x = f.x(); | - borrow of `f` occurs here @@ -48,7 +48,7 @@ LL | drop(x); | - borrow later used here error[E0503]: cannot use `g.0` because it was mutably borrowed - --> $DIR/borrowck-describe-lvalue.rs:46:9 + --> $DIR/borrowck-describe-lvalue.rs:44:9 | LL | let x = g.x(); | - borrow of `g` occurs here @@ -58,7 +58,7 @@ LL | drop(x); | - borrow later used here error[E0503]: cannot use `h.0` because it was mutably borrowed - --> $DIR/borrowck-describe-lvalue.rs:53:9 + --> $DIR/borrowck-describe-lvalue.rs:51:9 | LL | let x = &mut h.0; | -------- borrow of `h.0` occurs here @@ -68,7 +68,7 @@ LL | drop(x); | - borrow later used here error[E0503]: cannot use `e.0` because it was mutably borrowed - --> $DIR/borrowck-describe-lvalue.rs:61:20 + --> $DIR/borrowck-describe-lvalue.rs:59:20 | LL | let x = e.x(); | - borrow of `e` occurs here @@ -80,7 +80,7 @@ LL | drop(x); | - borrow later used here error[E0503]: cannot use `u.a` because it was mutably borrowed - --> $DIR/borrowck-describe-lvalue.rs:69:9 + --> $DIR/borrowck-describe-lvalue.rs:67:9 | LL | let x = &mut u.a; | -------- borrow of `u.a` occurs here @@ -90,7 +90,7 @@ LL | drop(x); | - borrow later used here error[E0503]: cannot use `f.x` because it was mutably borrowed - --> $DIR/borrowck-describe-lvalue.rs:76:9 + --> $DIR/borrowck-describe-lvalue.rs:74:9 | LL | let x = f.x(); | - borrow of `*f` occurs here @@ -100,7 +100,7 @@ LL | drop(x); | - borrow later used here error[E0503]: cannot use `g.0` because it was mutably borrowed - --> $DIR/borrowck-describe-lvalue.rs:83:9 + --> $DIR/borrowck-describe-lvalue.rs:81:9 | LL | let x = g.x(); | - borrow of `*g` occurs here @@ -110,7 +110,7 @@ LL | drop(x); | - borrow later used here error[E0503]: cannot use `h.0` because it was mutably borrowed - --> $DIR/borrowck-describe-lvalue.rs:90:9 + --> $DIR/borrowck-describe-lvalue.rs:88:9 | LL | let x = &mut h.0; | -------- borrow of `h.0` occurs here @@ -120,7 +120,7 @@ LL | drop(x); | - borrow later used here error[E0503]: cannot use `e.0` because it was mutably borrowed - --> $DIR/borrowck-describe-lvalue.rs:98:20 + --> $DIR/borrowck-describe-lvalue.rs:96:20 | LL | let x = e.x(); | - borrow of `*e` occurs here @@ -132,7 +132,7 @@ LL | drop(x); | - borrow later used here error[E0503]: cannot use `u.a` because it was mutably borrowed - --> $DIR/borrowck-describe-lvalue.rs:107:9 + --> $DIR/borrowck-describe-lvalue.rs:105:9 | LL | let x = &mut u.a; | -------- borrow of `u.a` occurs here @@ -142,7 +142,7 @@ LL | drop(x); | - borrow later used here error[E0503]: cannot use `v[..]` because it was mutably borrowed - --> $DIR/borrowck-describe-lvalue.rs:115:15 + --> $DIR/borrowck-describe-lvalue.rs:113:15 | LL | let x = &mut v; | ------ borrow of `v` occurs here @@ -154,7 +154,7 @@ LL | drop(x); | - borrow later used here error[E0503]: cannot use `v[..]` because it was mutably borrowed - --> $DIR/borrowck-describe-lvalue.rs:120:18 + --> $DIR/borrowck-describe-lvalue.rs:118:18 | LL | let x = &mut v; | ------ borrow of `v` occurs here @@ -166,7 +166,7 @@ LL | drop(x); | - borrow later used here error[E0503]: cannot use `v[..]` because it was mutably borrowed - --> $DIR/borrowck-describe-lvalue.rs:125:25 + --> $DIR/borrowck-describe-lvalue.rs:123:25 | LL | let x = &mut v; | ------ borrow of `v` occurs here @@ -178,7 +178,7 @@ LL | drop(x); | - borrow later used here error[E0503]: cannot use `v[..]` because it was mutably borrowed - --> $DIR/borrowck-describe-lvalue.rs:130:28 + --> $DIR/borrowck-describe-lvalue.rs:128:28 | LL | let x = &mut v; | ------ borrow of `v` occurs here @@ -190,7 +190,7 @@ LL | drop(x); | - borrow later used here error[E0503]: cannot use `v[..]` because it was mutably borrowed - --> $DIR/borrowck-describe-lvalue.rs:141:15 + --> $DIR/borrowck-describe-lvalue.rs:139:15 | LL | let x = &mut v; | ------ borrow of `v` occurs here @@ -202,7 +202,7 @@ LL | drop(x); | - borrow later used here error[E0503]: cannot use `v[..]` because it was mutably borrowed - --> $DIR/borrowck-describe-lvalue.rs:146:18 + --> $DIR/borrowck-describe-lvalue.rs:144:18 | LL | let x = &mut v; | ------ borrow of `v` occurs here @@ -214,7 +214,7 @@ LL | drop(x); | - borrow later used here error[E0503]: cannot use `v[..]` because it was mutably borrowed - --> $DIR/borrowck-describe-lvalue.rs:151:15 + --> $DIR/borrowck-describe-lvalue.rs:149:15 | LL | let x = &mut v; | ------ borrow of `v` occurs here @@ -226,7 +226,7 @@ LL | drop(x); | - borrow later used here error[E0503]: cannot use `v[..]` because it was mutably borrowed - --> $DIR/borrowck-describe-lvalue.rs:156:18 + --> $DIR/borrowck-describe-lvalue.rs:154:18 | LL | let x = &mut v; | ------ borrow of `v` occurs here @@ -238,7 +238,7 @@ LL | drop(x); | - borrow later used here error[E0503]: cannot use `e` because it was mutably borrowed - --> $DIR/borrowck-describe-lvalue.rs:169:13 + --> $DIR/borrowck-describe-lvalue.rs:167:13 | LL | let x = &mut e; | ------ borrow of `e` occurs here @@ -250,7 +250,7 @@ LL | drop(x); | - borrow later used here error[E0502]: cannot borrow `e.0` as immutable because it is also borrowed as mutable - --> $DIR/borrowck-describe-lvalue.rs:169:18 + --> $DIR/borrowck-describe-lvalue.rs:167:18 | LL | let x = &mut e; | ------ mutable borrow occurs here @@ -262,7 +262,7 @@ LL | drop(x); | - mutable borrow later used here error[E0502]: cannot borrow `e.x` as immutable because it is also borrowed as mutable - --> $DIR/borrowck-describe-lvalue.rs:173:23 + --> $DIR/borrowck-describe-lvalue.rs:171:23 | LL | let x = &mut e; | ------ mutable borrow occurs here @@ -274,7 +274,7 @@ LL | drop(x); | - mutable borrow later used here error[E0502]: cannot borrow `s.y.0` as immutable because it is also borrowed as mutable - --> $DIR/borrowck-describe-lvalue.rs:186:22 + --> $DIR/borrowck-describe-lvalue.rs:184:22 | LL | let x = &mut s; | ------ mutable borrow occurs here @@ -286,7 +286,7 @@ LL | drop(x); | - mutable borrow later used here error[E0502]: cannot borrow `s.x.y` as immutable because it is also borrowed as mutable - --> $DIR/borrowck-describe-lvalue.rs:192:28 + --> $DIR/borrowck-describe-lvalue.rs:190:28 | LL | let x = &mut s; | ------ mutable borrow occurs here @@ -298,7 +298,7 @@ LL | drop(x); | - mutable borrow later used here error[E0503]: cannot use `*v` because it was mutably borrowed - --> $DIR/borrowck-describe-lvalue.rs:234:9 + --> $DIR/borrowck-describe-lvalue.rs:232:9 | LL | let x = &mut v; | ------ borrow of `v` occurs here @@ -309,7 +309,7 @@ LL | drop(x); | - borrow later used here error[E0503]: cannot use `v[_].y` because it was mutably borrowed - --> $DIR/borrowck-describe-lvalue.rs:234:9 + --> $DIR/borrowck-describe-lvalue.rs:232:9 | LL | let x = &mut v; | ------ borrow of `v` occurs here @@ -320,7 +320,7 @@ LL | drop(x); | - borrow later used here error[E0502]: cannot borrow `v[..].x` as immutable because it is also borrowed as mutable - --> $DIR/borrowck-describe-lvalue.rs:245:24 + --> $DIR/borrowck-describe-lvalue.rs:243:24 | LL | let x = &mut v; | ------ mutable borrow occurs here @@ -332,7 +332,7 @@ LL | drop(x); | - mutable borrow later used here error[E0502]: cannot borrow `*block.current` as immutable because it is also borrowed as mutable - --> $DIR/borrowck-describe-lvalue.rs:208:29 + --> $DIR/borrowck-describe-lvalue.rs:206:29 | LL | let x = &mut block; | ---------- mutable borrow occurs here @@ -343,7 +343,7 @@ LL | drop(x); | - mutable borrow later used here error[E0502]: cannot borrow `*block.current` as immutable because it is also borrowed as mutable - --> $DIR/borrowck-describe-lvalue.rs:223:33 + --> $DIR/borrowck-describe-lvalue.rs:221:33 | LL | let x = &mut block; | ---------- mutable borrow occurs here @@ -354,7 +354,7 @@ LL | drop(x); | - mutable borrow later used here error[E0382]: use of moved value: `x` - --> $DIR/borrowck-describe-lvalue.rs:276:22 + --> $DIR/borrowck-describe-lvalue.rs:274:22 | LL | drop(x); | - value moved here diff --git a/src/test/ui/borrowck/borrowck-match-binding-is-assignment.stderr b/src/test/ui/borrowck/borrowck-match-binding-is-assignment.stderr index 5661ca52cbabc..dd22d7e2e2ed2 100644 --- a/src/test/ui/borrowck/borrowck-match-binding-is-assignment.stderr +++ b/src/test/ui/borrowck/borrowck-match-binding-is-assignment.stderr @@ -5,7 +5,7 @@ LL | x => { | - | | | first assignment to `x` - | help: make this binding mutable: `mut x` + | help: consider making this binding mutable: `mut x` LL | x += 1; | ^^^^^^ cannot assign twice to immutable variable @@ -16,7 +16,7 @@ LL | E::Foo(x) => { | - | | | first assignment to `x` - | help: make this binding mutable: `mut x` + | help: consider making this binding mutable: `mut x` LL | x += 1; | ^^^^^^ cannot assign twice to immutable variable @@ -27,7 +27,7 @@ LL | S { bar: x } => { | - | | | first assignment to `x` - | help: make this binding mutable: `mut x` + | help: consider making this binding mutable: `mut x` LL | x += 1; | ^^^^^^ cannot assign twice to immutable variable @@ -38,7 +38,7 @@ LL | (x,) => { | - | | | first assignment to `x` - | help: make this binding mutable: `mut x` + | help: consider making this binding mutable: `mut x` LL | x += 1; | ^^^^^^ cannot assign twice to immutable variable @@ -49,7 +49,7 @@ LL | [x,_,_] => { | - | | | first assignment to `x` - | help: make this binding mutable: `mut x` + | help: consider making this binding mutable: `mut x` LL | x += 1; | ^^^^^^ cannot assign twice to immutable variable diff --git a/src/test/ui/borrowck/borrowck-union-borrow.rs b/src/test/ui/borrowck/borrowck-union-borrow.rs index 63901680bd157..f01915398a412 100644 --- a/src/test/ui/borrowck/borrowck-union-borrow.rs +++ b/src/test/ui/borrowck/borrowck-union-borrow.rs @@ -1,5 +1,3 @@ -// ignore-tidy-linelength - #[derive(Clone, Copy)] union U { a: u8, diff --git a/src/test/ui/borrowck/borrowck-union-borrow.stderr b/src/test/ui/borrowck/borrowck-union-borrow.stderr index ca10e299c5857..395cd0b4855ab 100644 --- a/src/test/ui/borrowck/borrowck-union-borrow.stderr +++ b/src/test/ui/borrowck/borrowck-union-borrow.stderr @@ -1,5 +1,5 @@ error[E0502]: cannot borrow `u.a` as mutable because it is also borrowed as immutable - --> $DIR/borrowck-union-borrow.rs:25:23 + --> $DIR/borrowck-union-borrow.rs:23:23 | LL | let ra = &u.a; | ---- immutable borrow occurs here @@ -9,7 +9,7 @@ LL | drop(ra); | -- immutable borrow later used here error[E0506]: cannot assign to `u.a` because it is borrowed - --> $DIR/borrowck-union-borrow.rs:30:13 + --> $DIR/borrowck-union-borrow.rs:28:13 | LL | let ra = &u.a; | ---- borrow of `u.a` occurs here @@ -19,7 +19,7 @@ LL | drop(ra); | -- borrow later used here error[E0502]: cannot borrow `u` (via `u.b`) as mutable because it is also borrowed as immutable (via `u.a`) - --> $DIR/borrowck-union-borrow.rs:46:23 + --> $DIR/borrowck-union-borrow.rs:44:23 | LL | let ra = &u.a; | ---- immutable borrow occurs here (via `u.a`) @@ -31,7 +31,7 @@ LL | drop(ra); = note: `u.b` is a field of the union `U`, so it overlaps the field `u.a` error[E0506]: cannot assign to `u.b` because it is borrowed - --> $DIR/borrowck-union-borrow.rs:51:13 + --> $DIR/borrowck-union-borrow.rs:49:13 | LL | let ra = &u.a; | ---- borrow of `u.b` occurs here @@ -41,7 +41,7 @@ LL | drop(ra); | -- borrow later used here error[E0502]: cannot borrow `u.a` as immutable because it is also borrowed as mutable - --> $DIR/borrowck-union-borrow.rs:57:22 + --> $DIR/borrowck-union-borrow.rs:55:22 | LL | let rma = &mut u.a; | -------- mutable borrow occurs here @@ -51,7 +51,7 @@ LL | drop(rma); | --- mutable borrow later used here error[E0503]: cannot use `u.a` because it was mutably borrowed - --> $DIR/borrowck-union-borrow.rs:62:21 + --> $DIR/borrowck-union-borrow.rs:60:21 | LL | let ra = &mut u.a; | -------- borrow of `u.a` occurs here @@ -61,7 +61,7 @@ LL | drop(ra); | -- borrow later used here error[E0499]: cannot borrow `u.a` as mutable more than once at a time - --> $DIR/borrowck-union-borrow.rs:67:24 + --> $DIR/borrowck-union-borrow.rs:65:24 | LL | let rma = &mut u.a; | -------- first mutable borrow occurs here @@ -71,7 +71,7 @@ LL | drop(rma); | --- first borrow later used here error[E0506]: cannot assign to `u.a` because it is borrowed - --> $DIR/borrowck-union-borrow.rs:72:13 + --> $DIR/borrowck-union-borrow.rs:70:13 | LL | let rma = &mut u.a; | -------- borrow of `u.a` occurs here @@ -81,7 +81,7 @@ LL | drop(rma); | --- borrow later used here error[E0502]: cannot borrow `u` (via `u.b`) as immutable because it is also borrowed as mutable (via `u.a`) - --> $DIR/borrowck-union-borrow.rs:78:22 + --> $DIR/borrowck-union-borrow.rs:76:22 | LL | let rma = &mut u.a; | -------- mutable borrow occurs here (via `u.a`) @@ -93,7 +93,7 @@ LL | drop(rma); = note: `u.b` is a field of the union `U`, so it overlaps the field `u.a` error[E0503]: cannot use `u.b` because it was mutably borrowed - --> $DIR/borrowck-union-borrow.rs:83:21 + --> $DIR/borrowck-union-borrow.rs:81:21 | LL | let ra = &mut u.a; | -------- borrow of `u.a` occurs here @@ -104,7 +104,7 @@ LL | drop(ra); | -- borrow later used here error[E0499]: cannot borrow `u` (via `u.b`) as mutable more than once at a time - --> $DIR/borrowck-union-borrow.rs:89:24 + --> $DIR/borrowck-union-borrow.rs:87:24 | LL | let rma = &mut u.a; | -------- first mutable borrow occurs here (via `u.a`) @@ -116,7 +116,7 @@ LL | drop(rma); = note: `u.b` is a field of the union `U`, so it overlaps the field `u.a` error[E0506]: cannot assign to `u.b` because it is borrowed - --> $DIR/borrowck-union-borrow.rs:94:13 + --> $DIR/borrowck-union-borrow.rs:92:13 | LL | let rma = &mut u.a; | -------- borrow of `u.b` occurs here diff --git a/src/test/ui/borrowck/immutable-arg.stderr b/src/test/ui/borrowck/immutable-arg.stderr index 7255ca327e753..bddb0633a0b86 100644 --- a/src/test/ui/borrowck/immutable-arg.stderr +++ b/src/test/ui/borrowck/immutable-arg.stderr @@ -2,7 +2,7 @@ error[E0384]: cannot assign to immutable argument `_x` --> $DIR/immutable-arg.rs:2:5 | LL | fn foo(_x: u32) { - | -- help: make this binding mutable: `mut _x` + | -- help: consider making this binding mutable: `mut _x` LL | _x = 4; | ^^^^^^ cannot assign to immutable argument diff --git a/src/test/ui/issues/issue-42344.rs b/src/test/ui/borrowck/issue-42344.rs similarity index 100% rename from src/test/ui/issues/issue-42344.rs rename to src/test/ui/borrowck/issue-42344.rs diff --git a/src/test/ui/issues/issue-42344.stderr b/src/test/ui/borrowck/issue-42344.stderr similarity index 100% rename from src/test/ui/issues/issue-42344.stderr rename to src/test/ui/borrowck/issue-42344.stderr diff --git a/src/test/ui/borrowck/issue-45199.rs b/src/test/ui/borrowck/issue-45199.rs index cbd45cbb61990..ded46e56e3451 100644 --- a/src/test/ui/borrowck/issue-45199.rs +++ b/src/test/ui/borrowck/issue-45199.rs @@ -1,6 +1,6 @@ fn test_drop_replace() { let b: Box<isize>; - //~^ HELP make this binding mutable + //~^ HELP consider making this binding mutable //~| SUGGESTION mut b b = Box::new(1); //~ NOTE first assignment b = Box::new(2); //~ ERROR cannot assign twice to immutable variable `b` @@ -9,13 +9,13 @@ fn test_drop_replace() { fn test_call() { let b = Box::new(1); //~ NOTE first assignment - //~| HELP make this binding mutable + //~| HELP consider making this binding mutable //~| SUGGESTION mut b b = Box::new(2); //~ ERROR cannot assign twice to immutable variable `b` //~| NOTE cannot assign twice to immutable } -fn test_args(b: Box<i32>) { //~ HELP make this binding mutable +fn test_args(b: Box<i32>) { //~ HELP consider making this binding mutable //~| SUGGESTION mut b b = Box::new(2); //~ ERROR cannot assign to immutable argument `b` //~| NOTE cannot assign to immutable argument diff --git a/src/test/ui/borrowck/issue-45199.stderr b/src/test/ui/borrowck/issue-45199.stderr index 83b634051bb00..47aa30908270d 100644 --- a/src/test/ui/borrowck/issue-45199.stderr +++ b/src/test/ui/borrowck/issue-45199.stderr @@ -2,7 +2,7 @@ error[E0384]: cannot assign twice to immutable variable `b` --> $DIR/issue-45199.rs:6:5 | LL | let b: Box<isize>; - | - help: make this binding mutable: `mut b` + | - help: consider making this binding mutable: `mut b` ... LL | b = Box::new(1); | - first assignment to `b` @@ -16,7 +16,7 @@ LL | let b = Box::new(1); | - | | | first assignment to `b` - | help: make this binding mutable: `mut b` + | help: consider making this binding mutable: `mut b` ... LL | b = Box::new(2); | ^ cannot assign twice to immutable variable @@ -25,7 +25,7 @@ error[E0384]: cannot assign to immutable argument `b` --> $DIR/issue-45199.rs:20:5 | LL | fn test_args(b: Box<i32>) { - | - help: make this binding mutable: `mut b` + | - help: consider making this binding mutable: `mut b` LL | LL | b = Box::new(2); | ^ cannot assign to immutable argument diff --git a/src/test/ui/borrowck/or-patterns.rs b/src/test/ui/borrowck/or-patterns.rs index 5b31e2d76a05b..aeab3b92e9840 100644 --- a/src/test/ui/borrowck/or-patterns.rs +++ b/src/test/ui/borrowck/or-patterns.rs @@ -1,8 +1,6 @@ // Test that borrow check considers all choices in an or pattern, even the // unreachable ones. -#![feature(or_patterns)] - fn or_pattern_moves_all(x: ((String, String),)) { match x { ((y, _) | (_, y),) => (), diff --git a/src/test/ui/borrowck/or-patterns.stderr b/src/test/ui/borrowck/or-patterns.stderr index 9593b94537aea..dd5797c3f79cc 100644 --- a/src/test/ui/borrowck/or-patterns.stderr +++ b/src/test/ui/borrowck/or-patterns.stderr @@ -1,5 +1,5 @@ error[E0382]: borrow of moved value: `x.0.0` - --> $DIR/or-patterns.rs:10:5 + --> $DIR/or-patterns.rs:8:5 | LL | ((y, _) | (_, y),) => (), | - value moved here @@ -10,7 +10,7 @@ LL | &x.0 .0; = note: move occurs because `x.0.0` has type `String`, which does not implement the `Copy` trait error[E0382]: borrow of moved value: `x.0.1` - --> $DIR/or-patterns.rs:12:5 + --> $DIR/or-patterns.rs:10:5 | LL | ((y, _) | (_, y),) => (), | - value moved here @@ -21,7 +21,7 @@ LL | &x.0 .1; = note: move occurs because `x.0.1` has type `String`, which does not implement the `Copy` trait error[E0502]: cannot borrow `x.0.0` as mutable because it is also borrowed as immutable - --> $DIR/or-patterns.rs:20:5 + --> $DIR/or-patterns.rs:18:5 | LL | ((ref y, _) | (_, ref y),) => y, | ----- immutable borrow occurs here @@ -33,7 +33,7 @@ LL | drop(r); | - immutable borrow later used here error[E0502]: cannot borrow `x.0.1` as mutable because it is also borrowed as immutable - --> $DIR/or-patterns.rs:22:5 + --> $DIR/or-patterns.rs:20:5 | LL | ((ref y, _) | (_, ref y),) => y, | ----- immutable borrow occurs here @@ -45,7 +45,7 @@ LL | drop(r); | - immutable borrow later used here error[E0502]: cannot borrow `x.0.0` as immutable because it is also borrowed as mutable - --> $DIR/or-patterns.rs:31:5 + --> $DIR/or-patterns.rs:29:5 | LL | ((ref mut y, _) | (_, ref mut y),) => y, | --------- mutable borrow occurs here @@ -57,7 +57,7 @@ LL | drop(r); | - mutable borrow later used here error[E0502]: cannot borrow `x.0.1` as immutable because it is also borrowed as mutable - --> $DIR/or-patterns.rs:33:5 + --> $DIR/or-patterns.rs:31:5 | LL | ((ref mut y, _) | (_, ref mut y),) => y, | --------- mutable borrow occurs here @@ -69,7 +69,7 @@ LL | drop(r); | - mutable borrow later used here error[E0382]: borrow of moved value: `x.0.0` - --> $DIR/or-patterns.rs:40:5 + --> $DIR/or-patterns.rs:38:5 | LL | let ((y, _) | (_, y),) = x; | - value moved here @@ -79,7 +79,7 @@ LL | &x.0 .0; = note: move occurs because `x.0.0` has type `String`, which does not implement the `Copy` trait error[E0382]: borrow of moved value: `x.0.1` - --> $DIR/or-patterns.rs:42:5 + --> $DIR/or-patterns.rs:40:5 | LL | let ((y, _) | (_, y),) = x; | - value moved here @@ -90,7 +90,7 @@ LL | &x.0 .1; = note: move occurs because `x.0.1` has type `String`, which does not implement the `Copy` trait error[E0502]: cannot borrow `x.0.0` as mutable because it is also borrowed as immutable - --> $DIR/or-patterns.rs:48:5 + --> $DIR/or-patterns.rs:46:5 | LL | let ((ref r, _) | (_, ref r),) = x; | ----- immutable borrow occurs here @@ -101,7 +101,7 @@ LL | drop(r); | - immutable borrow later used here error[E0502]: cannot borrow `x.0.1` as mutable because it is also borrowed as immutable - --> $DIR/or-patterns.rs:50:5 + --> $DIR/or-patterns.rs:48:5 | LL | let ((ref r, _) | (_, ref r),) = x; | ----- immutable borrow occurs here @@ -113,7 +113,7 @@ LL | drop(r); | - immutable borrow later used here error[E0502]: cannot borrow `x.0.0` as immutable because it is also borrowed as mutable - --> $DIR/or-patterns.rs:57:5 + --> $DIR/or-patterns.rs:55:5 | LL | let ((ref mut r, _) | (_, ref mut r),) = x; | --------- mutable borrow occurs here @@ -124,7 +124,7 @@ LL | drop(r); | - mutable borrow later used here error[E0502]: cannot borrow `x.0.1` as immutable because it is also borrowed as mutable - --> $DIR/or-patterns.rs:59:5 + --> $DIR/or-patterns.rs:57:5 | LL | let ((ref mut r, _) | (_, ref mut r),) = x; | --------- mutable borrow occurs here diff --git a/src/test/ui/borrowck/two-phase-allow-access-during-reservation.nll_target.stderr b/src/test/ui/borrowck/two-phase-allow-access-during-reservation.nll_target.stderr index 9f31c3f87c37d..bba3393fc1405 100644 --- a/src/test/ui/borrowck/two-phase-allow-access-during-reservation.nll_target.stderr +++ b/src/test/ui/borrowck/two-phase-allow-access-during-reservation.nll_target.stderr @@ -1,5 +1,5 @@ error[E0503]: cannot use `i` because it was mutably borrowed - --> $DIR/two-phase-allow-access-during-reservation.rs:30:19 + --> $DIR/two-phase-allow-access-during-reservation.rs:28:19 | LL | /*1*/ let p = &mut i; // (reservation of `i` starts here) | ------ borrow of `i` occurs here @@ -11,7 +11,7 @@ LL | /*3*/ *p += 1; // (mutable borrow of `i` starts here, since `p` | ------- borrow later used here error[E0503]: cannot use `i` because it was mutably borrowed - --> $DIR/two-phase-allow-access-during-reservation.rs:35:19 + --> $DIR/two-phase-allow-access-during-reservation.rs:33:19 | LL | /*1*/ let p = &mut i; // (reservation of `i` starts here) | ------ borrow of `i` occurs here diff --git a/src/test/ui/borrowck/two-phase-allow-access-during-reservation.rs b/src/test/ui/borrowck/two-phase-allow-access-during-reservation.rs index 07169afefc988..3afa679ce390a 100644 --- a/src/test/ui/borrowck/two-phase-allow-access-during-reservation.rs +++ b/src/test/ui/borrowck/two-phase-allow-access-during-reservation.rs @@ -1,5 +1,3 @@ -// ignore-tidy-linelength - // revisions: nll_target // The following revisions are disabled due to missing support for two_phase_beyond_autoref diff --git a/src/test/ui/borrowck/two-phase-reservation-sharing-interference.nll_target.stderr b/src/test/ui/borrowck/two-phase-reservation-sharing-interference.nll_target.stderr index 7b64d9d400286..2cbdc0901bc50 100644 --- a/src/test/ui/borrowck/two-phase-reservation-sharing-interference.nll_target.stderr +++ b/src/test/ui/borrowck/two-phase-reservation-sharing-interference.nll_target.stderr @@ -1,5 +1,5 @@ error[E0502]: cannot borrow `vec` as mutable because it is also borrowed as immutable - --> $DIR/two-phase-reservation-sharing-interference.rs:36:17 + --> $DIR/two-phase-reservation-sharing-interference.rs:34:17 | LL | let shared = &vec; | ---- immutable borrow occurs here diff --git a/src/test/ui/borrowck/two-phase-reservation-sharing-interference.rs b/src/test/ui/borrowck/two-phase-reservation-sharing-interference.rs index de6f66c1c3f9b..f7392bfeaab36 100644 --- a/src/test/ui/borrowck/two-phase-reservation-sharing-interference.rs +++ b/src/test/ui/borrowck/two-phase-reservation-sharing-interference.rs @@ -1,5 +1,3 @@ -// ignore-tidy-linelength - // revisions: nll_target // The following revisions are disabled due to missing support from two-phase beyond autorefs diff --git a/src/test/ui/bound-suggestions.fixed b/src/test/ui/bound-suggestions.fixed index be61b7dda256a..31fdd2b67e294 100644 --- a/src/test/ui/bound-suggestions.fixed +++ b/src/test/ui/bound-suggestions.fixed @@ -5,37 +5,37 @@ use std::fmt::Debug; // Rustfix should add this, or use `std::fmt::Debug` instead. #[allow(dead_code)] -fn test_impl(t: impl Sized + Debug) { +fn test_impl(t: impl Sized + std::fmt::Debug) { println!("{:?}", t); //~^ ERROR doesn't implement } #[allow(dead_code)] -fn test_no_bounds<T: Debug>(t: T) { +fn test_no_bounds<T: std::fmt::Debug>(t: T) { println!("{:?}", t); //~^ ERROR doesn't implement } #[allow(dead_code)] -fn test_one_bound<T: Sized + Debug>(t: T) { +fn test_one_bound<T: Sized + std::fmt::Debug>(t: T) { println!("{:?}", t); //~^ ERROR doesn't implement } #[allow(dead_code)] -fn test_no_bounds_where<X, Y>(x: X, y: Y) where X: std::fmt::Debug, Y: Debug { +fn test_no_bounds_where<X, Y>(x: X, y: Y) where X: std::fmt::Debug, Y: std::fmt::Debug { println!("{:?} {:?}", x, y); //~^ ERROR doesn't implement } #[allow(dead_code)] -fn test_one_bound_where<X>(x: X) where X: Sized + Debug { +fn test_one_bound_where<X>(x: X) where X: Sized + std::fmt::Debug { println!("{:?}", x); //~^ ERROR doesn't implement } #[allow(dead_code)] -fn test_many_bounds_where<X>(x: X) where X: Sized, X: Sized, X: Debug { +fn test_many_bounds_where<X>(x: X) where X: Sized, X: Sized, X: std::fmt::Debug { println!("{:?}", x); //~^ ERROR doesn't implement } diff --git a/src/test/ui/bound-suggestions.stderr b/src/test/ui/bound-suggestions.stderr index 12e67e90265ab..ebf43bdb2717c 100644 --- a/src/test/ui/bound-suggestions.stderr +++ b/src/test/ui/bound-suggestions.stderr @@ -8,8 +8,8 @@ LL | println!("{:?}", t); = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) help: consider further restricting this bound | -LL | fn test_impl(t: impl Sized + Debug) { - | ^^^^^^^ +LL | fn test_impl(t: impl Sized + std::fmt::Debug) { + | ^^^^^^^^^^^^^^^^^ error[E0277]: `T` doesn't implement `Debug` --> $DIR/bound-suggestions.rs:15:22 @@ -21,8 +21,8 @@ LL | println!("{:?}", t); = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) help: consider restricting type parameter `T` | -LL | fn test_no_bounds<T: Debug>(t: T) { - | ^^^^^^^ +LL | fn test_no_bounds<T: std::fmt::Debug>(t: T) { + | ^^^^^^^^^^^^^^^^^ error[E0277]: `T` doesn't implement `Debug` --> $DIR/bound-suggestions.rs:21:22 @@ -34,8 +34,8 @@ LL | println!("{:?}", t); = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) help: consider further restricting this bound | -LL | fn test_one_bound<T: Sized + Debug>(t: T) { - | ^^^^^^^ +LL | fn test_one_bound<T: Sized + std::fmt::Debug>(t: T) { + | ^^^^^^^^^^^^^^^^^ error[E0277]: `Y` doesn't implement `Debug` --> $DIR/bound-suggestions.rs:27:30 @@ -47,8 +47,8 @@ LL | println!("{:?} {:?}", x, y); = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) help: consider further restricting type parameter `Y` | -LL | fn test_no_bounds_where<X, Y>(x: X, y: Y) where X: std::fmt::Debug, Y: Debug { - | ^^^^^^^^^^ +LL | fn test_no_bounds_where<X, Y>(x: X, y: Y) where X: std::fmt::Debug, Y: std::fmt::Debug { + | ^^^^^^^^^^^^^^^^^^^^ error[E0277]: `X` doesn't implement `Debug` --> $DIR/bound-suggestions.rs:33:22 @@ -60,8 +60,8 @@ LL | println!("{:?}", x); = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) help: consider further restricting this bound | -LL | fn test_one_bound_where<X>(x: X) where X: Sized + Debug { - | ^^^^^^^ +LL | fn test_one_bound_where<X>(x: X) where X: Sized + std::fmt::Debug { + | ^^^^^^^^^^^^^^^^^ error[E0277]: `X` doesn't implement `Debug` --> $DIR/bound-suggestions.rs:39:22 @@ -73,8 +73,8 @@ LL | println!("{:?}", x); = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) help: consider further restricting type parameter `X` | -LL | fn test_many_bounds_where<X>(x: X) where X: Sized, X: Sized, X: Debug { - | ^^^^^^^^^^ +LL | fn test_many_bounds_where<X>(x: X) where X: Sized, X: Sized, X: std::fmt::Debug { + | ^^^^^^^^^^^^^^^^^^^^ error[E0277]: the size for values of type `Self` cannot be known at compilation time --> $DIR/bound-suggestions.rs:44:46 diff --git a/src/test/ui/builtin-superkinds/builtin-superkinds-double-superkind.stderr b/src/test/ui/builtin-superkinds/builtin-superkinds-double-superkind.stderr index 7e8ac113b4871..7ff986ec38109 100644 --- a/src/test/ui/builtin-superkinds/builtin-superkinds-double-superkind.stderr +++ b/src/test/ui/builtin-superkinds/builtin-superkinds-double-superkind.stderr @@ -10,8 +10,8 @@ LL | impl <T: Sync+'static> Foo for (T,) { } = note: required because it appears within the type `(T,)` help: consider further restricting this bound | -LL | impl <T: Sync+'static + Send> Foo for (T,) { } - | ^^^^^^ +LL | impl <T: Sync+'static + std::marker::Send> Foo for (T,) { } + | ^^^^^^^^^^^^^^^^^^^ error[E0277]: `T` cannot be shared between threads safely --> $DIR/builtin-superkinds-double-superkind.rs:9:16 @@ -25,8 +25,8 @@ LL | impl <T: Send> Foo for (T,T) { } = note: required because it appears within the type `(T, T)` help: consider further restricting this bound | -LL | impl <T: Send + Sync> Foo for (T,T) { } - | ^^^^^^ +LL | impl <T: Send + std::marker::Sync> Foo for (T,T) { } + | ^^^^^^^^^^^^^^^^^^^ error: aborting due to 2 previous errors diff --git a/src/test/ui/builtin-superkinds/builtin-superkinds-in-metadata.stderr b/src/test/ui/builtin-superkinds/builtin-superkinds-in-metadata.stderr index 2b4b6e548b881..0d1d747272d18 100644 --- a/src/test/ui/builtin-superkinds/builtin-superkinds-in-metadata.stderr +++ b/src/test/ui/builtin-superkinds/builtin-superkinds-in-metadata.stderr @@ -9,11 +9,15 @@ LL | impl <T:Sync+'static> RequiresRequiresShareAndSend for X<T> { } LL | pub trait RequiresRequiresShareAndSend : RequiresShare + Send { } | ---- required by this bound in `RequiresRequiresShareAndSend` | - = note: required because it appears within the type `X<T>` +note: required because it appears within the type `X<T>` + --> $DIR/builtin-superkinds-in-metadata.rs:9:8 + | +LL | struct X<T>(T); + | ^ help: consider further restricting this bound | -LL | impl <T:Sync+'static + Send> RequiresRequiresShareAndSend for X<T> { } - | ^^^^^^ +LL | impl <T:Sync+'static + std::marker::Send> RequiresRequiresShareAndSend for X<T> { } + | ^^^^^^^^^^^^^^^^^^^ error: aborting due to previous error diff --git a/src/test/ui/builtin-superkinds/builtin-superkinds-typaram-not-send.stderr b/src/test/ui/builtin-superkinds/builtin-superkinds-typaram-not-send.stderr index ff2cd1c4c8c75..ad80b3fa8d11f 100644 --- a/src/test/ui/builtin-superkinds/builtin-superkinds-typaram-not-send.stderr +++ b/src/test/ui/builtin-superkinds/builtin-superkinds-typaram-not-send.stderr @@ -9,8 +9,8 @@ LL | impl <T: Sync+'static> Foo for T { } | help: consider further restricting this bound | -LL | impl <T: Sync+'static + Send> Foo for T { } - | ^^^^^^ +LL | impl <T: Sync+'static + std::marker::Send> Foo for T { } + | ^^^^^^^^^^^^^^^^^^^ error: aborting due to previous error diff --git a/src/test/ui/cast/fat-ptr-cast-rpass.rs b/src/test/ui/cast/fat-ptr-cast-rpass.rs index 5f5e621d762df..9fa2255e1b348 100644 --- a/src/test/ui/cast/fat-ptr-cast-rpass.rs +++ b/src/test/ui/cast/fat-ptr-cast-rpass.rs @@ -1,5 +1,8 @@ // run-pass +// Remove this file when `std::raw` is removed. +// The replacement pointer metadata APIs are tested in library/core/tests/ptr.rs +#![allow(deprecated)] #![feature(raw)] use std::mem; @@ -37,5 +40,4 @@ fn main() { assert_eq!(b, d); assert_eq!(c, d as usize); - } diff --git a/src/test/ui/cast/issue-84213.fixed b/src/test/ui/cast/issue-84213.fixed new file mode 100644 index 0000000000000..e1a60557a20c7 --- /dev/null +++ b/src/test/ui/cast/issue-84213.fixed @@ -0,0 +1,15 @@ +// run-rustfix + +struct Something { + pub field: u32, +} + +fn main() { + let mut something = Something { field: 1337 }; + + let _pointer_to_something = &something as *const Something; + //~^ ERROR: non-primitive cast + + let _mut_pointer_to_something = &mut something as *mut Something; + //~^ ERROR: non-primitive cast +} diff --git a/src/test/ui/cast/issue-84213.rs b/src/test/ui/cast/issue-84213.rs new file mode 100644 index 0000000000000..3df264bdffb6a --- /dev/null +++ b/src/test/ui/cast/issue-84213.rs @@ -0,0 +1,15 @@ +// run-rustfix + +struct Something { + pub field: u32, +} + +fn main() { + let mut something = Something { field: 1337 }; + + let _pointer_to_something = something as *const Something; + //~^ ERROR: non-primitive cast + + let _mut_pointer_to_something = something as *mut Something; + //~^ ERROR: non-primitive cast +} diff --git a/src/test/ui/cast/issue-84213.stderr b/src/test/ui/cast/issue-84213.stderr new file mode 100644 index 0000000000000..a76aac580131e --- /dev/null +++ b/src/test/ui/cast/issue-84213.stderr @@ -0,0 +1,25 @@ +error[E0605]: non-primitive cast: `Something` as `*const Something` + --> $DIR/issue-84213.rs:10:33 + | +LL | let _pointer_to_something = something as *const Something; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ invalid cast + | +help: borrow the value for the cast to be valid + | +LL | let _pointer_to_something = &something as *const Something; + | ^ + +error[E0605]: non-primitive cast: `Something` as `*mut Something` + --> $DIR/issue-84213.rs:13:37 + | +LL | let _mut_pointer_to_something = something as *mut Something; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ invalid cast + | +help: borrow the value for the cast to be valid + | +LL | let _mut_pointer_to_something = &mut something as *mut Something; + | ^^^^ + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0605`. diff --git a/src/test/ui/check-doc-alias-attr.rs b/src/test/ui/check-doc-alias-attr.rs deleted file mode 100644 index 912e35f916545..0000000000000 --- a/src/test/ui/check-doc-alias-attr.rs +++ /dev/null @@ -1,16 +0,0 @@ -#![crate_type = "lib"] - -#[doc(alias = "foo")] // ok! -pub struct Bar; - -#[doc(alias)] //~ ERROR -#[doc(alias = 0)] //~ ERROR -#[doc(alias("bar"))] //~ ERROR -#[doc(alias = "\"")] //~ ERROR -#[doc(alias = "\n")] //~ ERROR -#[doc(alias = " -")] //~^ ERROR -#[doc(alias = "\t")] //~ ERROR -#[doc(alias = " hello")] //~ ERROR -#[doc(alias = "hello ")] //~ ERROR -pub struct Foo; diff --git a/src/test/ui/check-doc-alias-attr.stderr b/src/test/ui/check-doc-alias-attr.stderr deleted file mode 100644 index 1c7fc83bb8dea..0000000000000 --- a/src/test/ui/check-doc-alias-attr.stderr +++ /dev/null @@ -1,58 +0,0 @@ -error: doc alias attribute expects a string: #[doc(alias = "a")] - --> $DIR/check-doc-alias-attr.rs:6:7 - | -LL | #[doc(alias)] - | ^^^^^ - -error: doc alias attribute expects a string: #[doc(alias = "a")] - --> $DIR/check-doc-alias-attr.rs:7:7 - | -LL | #[doc(alias = 0)] - | ^^^^^^^^^ - -error: doc alias attribute expects a string: #[doc(alias = "a")] - --> $DIR/check-doc-alias-attr.rs:8:7 - | -LL | #[doc(alias("bar"))] - | ^^^^^^^^^^^^ - -error: '\"' character isn't allowed in `#[doc(alias = "...")]` - --> $DIR/check-doc-alias-attr.rs:9:15 - | -LL | #[doc(alias = "\"")] - | ^^^^ - -error: '\n' character isn't allowed in `#[doc(alias = "...")]` - --> $DIR/check-doc-alias-attr.rs:10:15 - | -LL | #[doc(alias = "\n")] - | ^^^^ - -error: '\n' character isn't allowed in `#[doc(alias = "...")]` - --> $DIR/check-doc-alias-attr.rs:11:15 - | -LL | #[doc(alias = " - | _______________^ -LL | | ")] - | |_^ - -error: '\t' character isn't allowed in `#[doc(alias = "...")]` - --> $DIR/check-doc-alias-attr.rs:13:15 - | -LL | #[doc(alias = "\t")] - | ^^^^ - -error: `#[doc(alias = "...")]` cannot start or end with ' ' - --> $DIR/check-doc-alias-attr.rs:14:15 - | -LL | #[doc(alias = " hello")] - | ^^^^^^^^ - -error: `#[doc(alias = "...")]` cannot start or end with ' ' - --> $DIR/check-doc-alias-attr.rs:15:15 - | -LL | #[doc(alias = "hello ")] - | ^^^^^^^^ - -error: aborting due to 9 previous errors - diff --git a/src/test/ui/cleanup-shortcircuit.rs b/src/test/ui/cleanup-shortcircuit.rs index 4f5197a5ba9b8..fe867ce1fbd57 100644 --- a/src/test/ui/cleanup-shortcircuit.rs +++ b/src/test/ui/cleanup-shortcircuit.rs @@ -3,6 +3,9 @@ // pretty-expanded FIXME #23616 +#![allow(deref_nullptr)] + + use std::env; pub fn main() { diff --git a/src/test/ui/closures/2229_closure_analysis/diagnostics/closure-origin-single-variant-diagnostics.rs b/src/test/ui/closures/2229_closure_analysis/diagnostics/closure-origin-single-variant-diagnostics.rs index 6107a082237c6..2ed0149b9db7d 100644 --- a/src/test/ui/closures/2229_closure_analysis/diagnostics/closure-origin-single-variant-diagnostics.rs +++ b/src/test/ui/closures/2229_closure_analysis/diagnostics/closure-origin-single-variant-diagnostics.rs @@ -13,12 +13,8 @@ fn main() { let mut point = SingleVariant::Point(10, -10); let c = || { - // FIXME(project-rfc-2229#24): Change this to be a destructure pattern - // once this is fixed, to remove the warning. - if let SingleVariant::Point(ref mut x, _) = point { - //~^ WARNING: irrefutable `if let` pattern - *x += 1; - } + let SingleVariant::Point(ref mut x, _) = point; + *x += 1; }; let b = c; diff --git a/src/test/ui/closures/2229_closure_analysis/diagnostics/closure-origin-single-variant-diagnostics.stderr b/src/test/ui/closures/2229_closure_analysis/diagnostics/closure-origin-single-variant-diagnostics.stderr index 8586dfd91863c..402f5e4f33e6f 100644 --- a/src/test/ui/closures/2229_closure_analysis/diagnostics/closure-origin-single-variant-diagnostics.stderr +++ b/src/test/ui/closures/2229_closure_analysis/diagnostics/closure-origin-single-variant-diagnostics.stderr @@ -7,21 +7,8 @@ LL | #![feature(capture_disjoint_fields)] = note: `#[warn(incomplete_features)]` on by default = note: see issue #53488 <https://github.com/rust-lang/rust/issues/53488> for more information -warning: irrefutable `if let` pattern - --> $DIR/closure-origin-single-variant-diagnostics.rs:18:9 - | -LL | / if let SingleVariant::Point(ref mut x, _) = point { -LL | | -LL | | *x += 1; -LL | | } - | |_________^ - | - = note: `#[warn(irrefutable_let_patterns)]` on by default - = note: this pattern will always match, so the `if let` is useless - = help: consider replacing the `if let` with a `let` - error[E0382]: use of moved value: `c` - --> $DIR/closure-origin-single-variant-diagnostics.rs:25:13 + --> $DIR/closure-origin-single-variant-diagnostics.rs:21:13 | LL | let b = c; | - value moved here @@ -29,11 +16,11 @@ LL | let a = c; | ^ value used here after move | note: closure cannot be moved more than once as it is not `Copy` due to moving the variable `point.0` out of its environment - --> $DIR/closure-origin-single-variant-diagnostics.rs:18:53 + --> $DIR/closure-origin-single-variant-diagnostics.rs:16:50 | -LL | if let SingleVariant::Point(ref mut x, _) = point { - | ^^^^^ +LL | let SingleVariant::Point(ref mut x, _) = point; + | ^^^^^ -error: aborting due to previous error; 2 warnings emitted +error: aborting due to previous error; 1 warning emitted For more information about this error, try `rustc --explain E0382`. diff --git a/src/test/ui/closures/2229_closure_analysis/diagnostics/liveness.rs b/src/test/ui/closures/2229_closure_analysis/diagnostics/liveness.rs new file mode 100644 index 0000000000000..09491f296f667 --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/diagnostics/liveness.rs @@ -0,0 +1,91 @@ +// check-pass +#![feature(capture_disjoint_fields)] +//~^ WARNING: the feature `capture_disjoint_fields` is incomplete +#![allow(unreachable_code)] +#![warn(unused)] + +#[derive(Debug)] +struct Point { + x: i32, + y: i32, +} + +pub fn f() { + let mut a = 1; + let mut c = Point{ x:1, y:0 }; + + // Captured by value, but variable is dead on entry. + (move || { + // This will not trigger a warning for unused variable as + // c.x will be treated as a Non-tracked place + c.x = 1; + println!("{}", c.x); + a = 1; //~ WARN value captured by `a` is never read + println!("{}", a); + })(); + + // Read and written to, but never actually used. + (move || { + // This will not trigger a warning for unused variable as + // c.x will be treated as a Non-tracked place + c.x += 1; + a += 1; //~ WARN unused variable: `a` + })(); + + (move || { + println!("{}", c.x); + // Value is read by closure itself on later invocations. + // This will not trigger a warning for unused variable as + // c.x will be treated as a Non-tracked place + c.x += 1; + println!("{}", a); + a += 1; + })(); + let b = Box::new(42); + (move || { + println!("{}", c.x); + // Never read because this is FnOnce closure. + // This will not trigger a warning for unused variable as + // c.x will be treated as a Non-tracked place + c.x += 1; + println!("{}", a); + a += 1; //~ WARN value assigned to `a` is never read + drop(b); + })(); +} + +#[derive(Debug)] +struct MyStruct<'a> { + x: Option<& 'a str>, + y: i32, +} + +pub fn nested() { + let mut a : Option<& str>; + a = None; + let mut b : Option<& str>; + b = None; + let mut d = MyStruct{ x: None, y: 1}; + let mut e = MyStruct{ x: None, y: 1}; + (|| { + (|| { + // This will not trigger a warning for unused variable as + // d.x will be treated as a Non-tracked place + d.x = Some("d1"); + d.x = Some("d2"); + a = Some("d1"); //~ WARN value assigned to `a` is never read + a = Some("d2"); + })(); + (move || { + // This will not trigger a warning for unused variable as + //e.x will be treated as a Non-tracked place + e.x = Some("e1"); + e.x = Some("e2"); + b = Some("e1"); //~ WARN value assigned to `b` is never read + //~| WARN unused variable: `b` + b = Some("e2"); //~ WARN value assigned to `b` is never read + })(); + })(); +} + +fn main() {} diff --git a/src/test/ui/closures/2229_closure_analysis/diagnostics/liveness.stderr b/src/test/ui/closures/2229_closure_analysis/diagnostics/liveness.stderr new file mode 100644 index 0000000000000..81bbc4e1dc0c2 --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/diagnostics/liveness.stderr @@ -0,0 +1,79 @@ +warning: the feature `capture_disjoint_fields` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/liveness.rs:2:12 + | +LL | #![feature(capture_disjoint_fields)] + | ^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #53488 <https://github.com/rust-lang/rust/issues/53488> for more information + +warning: value captured by `a` is never read + --> $DIR/liveness.rs:23:9 + | +LL | a = 1; + | ^ + | +note: the lint level is defined here + --> $DIR/liveness.rs:5:9 + | +LL | #![warn(unused)] + | ^^^^^^ + = note: `#[warn(unused_assignments)]` implied by `#[warn(unused)]` + = help: did you mean to capture by reference instead? + +warning: unused variable: `a` + --> $DIR/liveness.rs:32:9 + | +LL | a += 1; + | ^ + | +note: the lint level is defined here + --> $DIR/liveness.rs:5:9 + | +LL | #![warn(unused)] + | ^^^^^^ + = note: `#[warn(unused_variables)]` implied by `#[warn(unused)]` + = help: did you mean to capture by reference instead? + +warning: value assigned to `a` is never read + --> $DIR/liveness.rs:52:9 + | +LL | a += 1; + | ^ + | + = help: maybe it is overwritten before being read? + +warning: value assigned to `a` is never read + --> $DIR/liveness.rs:76:13 + | +LL | a = Some("d1"); + | ^ + | + = help: maybe it is overwritten before being read? + +warning: value assigned to `b` is never read + --> $DIR/liveness.rs:84:13 + | +LL | b = Some("e1"); + | ^ + | + = help: maybe it is overwritten before being read? + +warning: value assigned to `b` is never read + --> $DIR/liveness.rs:86:13 + | +LL | b = Some("e2"); + | ^ + | + = help: maybe it is overwritten before being read? + +warning: unused variable: `b` + --> $DIR/liveness.rs:84:13 + | +LL | b = Some("e1"); + | ^ + | + = help: did you mean to capture by reference instead? + +warning: 8 warnings emitted + diff --git a/src/test/ui/closures/2229_closure_analysis/diagnostics/liveness_unintentional_copy.rs b/src/test/ui/closures/2229_closure_analysis/diagnostics/liveness_unintentional_copy.rs new file mode 100644 index 0000000000000..e2035464dfa8a --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/diagnostics/liveness_unintentional_copy.rs @@ -0,0 +1,42 @@ +// check-pass +#![feature(capture_disjoint_fields)] +//~^ WARNING: the feature `capture_disjoint_fields` is incomplete +#![warn(unused)] + +#[derive(Debug)] +struct MyStruct { + a: i32, + b: i32, +} + +pub fn unintentional_copy_one() { + let mut a = 1; + let mut last = MyStruct{ a: 1, b: 1}; + let mut f = move |s| { + // This will not trigger a warning for unused variable + // as last.a will be treated as a Non-tracked place + last.a = s; + a = s; + //~^ WARN value assigned to `a` is never read + //~| WARN unused variable: `a` + }; + f(2); + f(3); + f(4); +} + +pub fn unintentional_copy_two() { + let mut a = 1; + let mut sum = MyStruct{ a: 1, b: 0}; + (1..10).for_each(move |x| { + // This will not trigger a warning for unused variable + // as sum.b will be treated as a Non-tracked place + sum.b += x; + a += x; //~ WARN unused variable: `a` + }); +} + +fn main() { + unintentional_copy_one(); + unintentional_copy_two(); +} diff --git a/src/test/ui/closures/2229_closure_analysis/diagnostics/liveness_unintentional_copy.stderr b/src/test/ui/closures/2229_closure_analysis/diagnostics/liveness_unintentional_copy.stderr new file mode 100644 index 0000000000000..35b0c22fc4fb9 --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/diagnostics/liveness_unintentional_copy.stderr @@ -0,0 +1,47 @@ +warning: the feature `capture_disjoint_fields` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/liveness_unintentional_copy.rs:2:12 + | +LL | #![feature(capture_disjoint_fields)] + | ^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #53488 <https://github.com/rust-lang/rust/issues/53488> for more information + +warning: value assigned to `a` is never read + --> $DIR/liveness_unintentional_copy.rs:19:9 + | +LL | a = s; + | ^ + | +note: the lint level is defined here + --> $DIR/liveness_unintentional_copy.rs:4:9 + | +LL | #![warn(unused)] + | ^^^^^^ + = note: `#[warn(unused_assignments)]` implied by `#[warn(unused)]` + = help: maybe it is overwritten before being read? + +warning: unused variable: `a` + --> $DIR/liveness_unintentional_copy.rs:19:9 + | +LL | a = s; + | ^ + | +note: the lint level is defined here + --> $DIR/liveness_unintentional_copy.rs:4:9 + | +LL | #![warn(unused)] + | ^^^^^^ + = note: `#[warn(unused_variables)]` implied by `#[warn(unused)]` + = help: did you mean to capture by reference instead? + +warning: unused variable: `a` + --> $DIR/liveness_unintentional_copy.rs:35:9 + | +LL | a += x; + | ^ + | + = help: did you mean to capture by reference instead? + +warning: 4 warnings emitted + diff --git a/src/test/ui/closures/2229_closure_analysis/diagnostics/repr_packed.rs b/src/test/ui/closures/2229_closure_analysis/diagnostics/repr_packed.rs new file mode 100644 index 0000000000000..82ec60a2e790a --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/diagnostics/repr_packed.rs @@ -0,0 +1,36 @@ +// check-pass + +#![feature(capture_disjoint_fields)] +//~^ WARNING: the feature `capture_disjoint_fields` is incomplete + +// Given how the closure desugaring is implemented (at least at the time of writing this test), +// we don't need to truncate the captured path to a reference into a packed-struct if the field +// being referenced will be moved into the closure, since it's safe to move out a field from a +// packed-struct. +// +// However to avoid surprises for the user, or issues when the closure is +// inlined we will truncate the capture to access just the struct regardless of if the field +// might get moved into the closure. +// +// It is possible for someone to try writing the code that relies on the desugaring to access a ref +// into a packed-struct without explicity using unsafe. Here we test that the compiler warns the +// user that such an access is still unsafe. +fn test_missing_unsafe_warning_on_repr_packed() { + #[repr(packed)] + struct Foo { x: String } + + let foo = Foo { x: String::new() }; + + let c = || { + println!("{}", foo.x); + //~^ WARNING: reference to packed field is unaligned + //~| WARNING: this was previously accepted by the compiler but is being phased out + let _z = foo.x; + }; + + c(); +} + +fn main() { + test_missing_unsafe_warning_on_repr_packed(); +} diff --git a/src/test/ui/closures/2229_closure_analysis/diagnostics/repr_packed.stderr b/src/test/ui/closures/2229_closure_analysis/diagnostics/repr_packed.stderr new file mode 100644 index 0000000000000..e8cc164be8754 --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/diagnostics/repr_packed.stderr @@ -0,0 +1,22 @@ +warning: the feature `capture_disjoint_fields` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/repr_packed.rs:3:12 + | +LL | #![feature(capture_disjoint_fields)] + | ^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #53488 <https://github.com/rust-lang/rust/issues/53488> for more information + +warning: reference to packed field is unaligned + --> $DIR/repr_packed.rs:25:24 + | +LL | println!("{}", foo.x); + | ^^^^^ + | + = note: `#[warn(unaligned_references)]` on by default + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #82523 <https://github.com/rust-lang/rust/issues/82523> + = note: fields of packed structs are not properly aligned, and creating a misaligned reference is undefined behavior (even if that reference is never dereferenced) + +warning: 2 warnings emitted + diff --git a/src/test/ui/closures/2229_closure_analysis/migrations/insignificant_drop.fixed b/src/test/ui/closures/2229_closure_analysis/migrations/insignificant_drop.fixed new file mode 100644 index 0000000000000..300f67e8b1e81 --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/migrations/insignificant_drop.fixed @@ -0,0 +1,133 @@ +// run-rustfix + +#![deny(disjoint_capture_drop_reorder)] +//~^ NOTE: the lint level is defined here + +// Test cases for types that implement a insignificant drop (stlib defined) + +// `t` needs Drop because one of its elements needs drop, +// therefore precise capture might affect drop ordering +fn test1_all_need_migration() { + let t = (String::new(), String::new()); + let t1 = (String::new(), String::new()); + let t2 = (String::new(), String::new()); + + let c = || { let _ = (&t, &t1, &t2); + //~^ ERROR: drop order affected for closure because of `capture_disjoint_fields` + //~| HELP: add a dummy let to cause `t`, `t1`, `t2` to be fully captured + + let _t = t.0; + let _t1 = t1.0; + let _t2 = t2.0; + }; + + c(); +} + +// String implements drop and therefore should be migrated. +// But in this test cases, `t2` is completely captured and when it is dropped won't be affected +fn test2_only_precise_paths_need_migration() { + let t = (String::new(), String::new()); + let t1 = (String::new(), String::new()); + let t2 = (String::new(), String::new()); + + let c = || { let _ = (&t, &t1); + //~^ ERROR: drop order affected for closure because of `capture_disjoint_fields` + //~| HELP: add a dummy let to cause `t`, `t1` to be fully captured + let _t = t.0; + let _t1 = t1.0; + let _t2 = t2; + }; + + c(); +} + +// If a variable would've not been captured by value then it would've not been +// dropped with the closure and therefore doesn't need migration. +fn test3_only_by_value_need_migration() { + let t = (String::new(), String::new()); + let t1 = (String::new(), String::new()); + let c = || { let _ = &t; + //~^ ERROR: drop order affected for closure because of `capture_disjoint_fields` + //~| HELP: add a dummy let to cause `t` to be fully captured + let _t = t.0; + println!("{}", t1.1); + }; + + c(); +} + +// Copy types get copied into the closure instead of move. Therefore we don't need to +// migrate then as their drop order isn't tied to the closure. +fn test4_only_non_copy_types_need_migration() { + let t = (String::new(), String::new()); + + // `t1` is Copy because all of its elements are Copy + let t1 = (0i32, 0i32); + + let c = || { let _ = &t; + //~^ ERROR: drop order affected for closure because of `capture_disjoint_fields` + //~| HELP: add a dummy let to cause `t` to be fully captured + let _t = t.0; + let _t1 = t1.0; + }; + + c(); +} + +fn test5_only_drop_types_need_migration() { + struct S(i32, i32); + + let t = (String::new(), String::new()); + + // `s` doesn't implement Drop or any elements within it, and doesn't need migration + let s = S(0i32, 0i32); + + let c = || { let _ = &t; + //~^ ERROR: drop order affected for closure because of `capture_disjoint_fields` + //~| HELP: add a dummy let to cause `t` to be fully captured + let _t = t.0; + let _s = s.0; + }; + + c(); +} + +// Since we are using a move closure here, both `t` and `t1` get moved +// even though they are being used by ref inside the closure. +fn test6_move_closures_non_copy_types_might_need_migration() { + let t = (String::new(), String::new()); + let t1 = (String::new(), String::new()); + let c = move || { let _ = (&t1, &t); + //~^ ERROR: drop order affected for closure because of `capture_disjoint_fields` + //~| HELP: add a dummy let to cause `t1`, `t` to be fully captured + println!("{} {}", t1.1, t.1); + }; + + c(); +} + +// Test migration analysis in case of Drop + Non Drop aggregates. +// Note we need migration here only because the non-copy (because Drop type) is captured, +// otherwise we won't need to, since we can get away with just by ref capture in that case. +fn test7_drop_non_drop_aggregate_need_migration() { + let t = (String::new(), String::new(), 0i32); + + let c = || { let _ = &t; + //~^ ERROR: drop order affected for closure because of `capture_disjoint_fields` + //~| HELP: add a dummy let to cause `t` to be fully captured + let _t = t.0; + }; + + c(); +} + +fn main() { + test1_all_need_migration(); + test2_only_precise_paths_need_migration(); + test3_only_by_value_need_migration(); + test4_only_non_copy_types_need_migration(); + test5_only_drop_types_need_migration(); + test6_move_closures_non_copy_types_might_need_migration(); + test7_drop_non_drop_aggregate_need_migration(); +} diff --git a/src/test/ui/closures/2229_closure_analysis/migrations/insignificant_drop.rs b/src/test/ui/closures/2229_closure_analysis/migrations/insignificant_drop.rs index 02b373620966e..a17c70d3e2877 100644 --- a/src/test/ui/closures/2229_closure_analysis/migrations/insignificant_drop.rs +++ b/src/test/ui/closures/2229_closure_analysis/migrations/insignificant_drop.rs @@ -1,3 +1,5 @@ +// run-rustfix + #![deny(disjoint_capture_drop_reorder)] //~^ NOTE: the lint level is defined here @@ -11,8 +13,9 @@ fn test1_all_need_migration() { let t2 = (String::new(), String::new()); let c = || { - //~^ERROR: drop order affected for closure because of `capture_disjoint_fields` - //~| NOTE: drop(&(t, t1, t2)); + //~^ ERROR: drop order affected for closure because of `capture_disjoint_fields` + //~| HELP: add a dummy let to cause `t`, `t1`, `t2` to be fully captured + let _t = t.0; let _t1 = t1.0; let _t2 = t2.0; @@ -29,8 +32,8 @@ fn test2_only_precise_paths_need_migration() { let t2 = (String::new(), String::new()); let c = || { - //~^ERROR: drop order affected for closure because of `capture_disjoint_fields` - //~| NOTE: drop(&(t, t1)); + //~^ ERROR: drop order affected for closure because of `capture_disjoint_fields` + //~| HELP: add a dummy let to cause `t`, `t1` to be fully captured let _t = t.0; let _t1 = t1.0; let _t2 = t2; @@ -45,8 +48,8 @@ fn test3_only_by_value_need_migration() { let t = (String::new(), String::new()); let t1 = (String::new(), String::new()); let c = || { - //~^ERROR: drop order affected for closure because of `capture_disjoint_fields` - //~| NOTE: drop(&(t)); + //~^ ERROR: drop order affected for closure because of `capture_disjoint_fields` + //~| HELP: add a dummy let to cause `t` to be fully captured let _t = t.0; println!("{}", t1.1); }; @@ -63,8 +66,8 @@ fn test4_only_non_copy_types_need_migration() { let t1 = (0i32, 0i32); let c = || { - //~^ERROR: drop order affected for closure because of `capture_disjoint_fields` - //~| NOTE: drop(&(t)); + //~^ ERROR: drop order affected for closure because of `capture_disjoint_fields` + //~| HELP: add a dummy let to cause `t` to be fully captured let _t = t.0; let _t1 = t1.0; }; @@ -81,8 +84,8 @@ fn test5_only_drop_types_need_migration() { let s = S(0i32, 0i32); let c = || { - //~^ERROR: drop order affected for closure because of `capture_disjoint_fields` - //~| NOTE: drop(&(t)); + //~^ ERROR: drop order affected for closure because of `capture_disjoint_fields` + //~| HELP: add a dummy let to cause `t` to be fully captured let _t = t.0; let _s = s.0; }; @@ -96,8 +99,8 @@ fn test6_move_closures_non_copy_types_might_need_migration() { let t = (String::new(), String::new()); let t1 = (String::new(), String::new()); let c = move || { - //~^ERROR: drop order affected for closure because of `capture_disjoint_fields` - //~| NOTE: drop(&(t1, t)); + //~^ ERROR: drop order affected for closure because of `capture_disjoint_fields` + //~| HELP: add a dummy let to cause `t1`, `t` to be fully captured println!("{} {}", t1.1, t.1); }; @@ -111,8 +114,8 @@ fn test7_drop_non_drop_aggregate_need_migration() { let t = (String::new(), String::new(), 0i32); let c = || { - //~^ERROR: drop order affected for closure because of `capture_disjoint_fields` - //~| NOTE: drop(&(t)); + //~^ ERROR: drop order affected for closure because of `capture_disjoint_fields` + //~| HELP: add a dummy let to cause `t` to be fully captured let _t = t.0; }; diff --git a/src/test/ui/closures/2229_closure_analysis/migrations/insignificant_drop.stderr b/src/test/ui/closures/2229_closure_analysis/migrations/insignificant_drop.stderr index 656c132c12dee..69c12d2bb56c0 100644 --- a/src/test/ui/closures/2229_closure_analysis/migrations/insignificant_drop.stderr +++ b/src/test/ui/closures/2229_closure_analysis/migrations/insignificant_drop.stderr @@ -1,25 +1,33 @@ error: drop order affected for closure because of `capture_disjoint_fields` - --> $DIR/insignificant_drop.rs:13:13 + --> $DIR/insignificant_drop.rs:15:13 | LL | let c = || { | _____________^ LL | | LL | | -LL | | let _t = t.0; -LL | | let _t1 = t1.0; +LL | | +... | LL | | let _t2 = t2.0; LL | | }; | |_____^ | note: the lint level is defined here - --> $DIR/insignificant_drop.rs:1:9 + --> $DIR/insignificant_drop.rs:3:9 | LL | #![deny(disjoint_capture_drop_reorder)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - = note: drop(&(t, t1, t2)); +help: add a dummy let to cause `t`, `t1`, `t2` to be fully captured + | +LL | let c = || { let _ = (&t, &t1, &t2); +LL | +LL | +LL | +LL | let _t = t.0; +LL | let _t1 = t1.0; + ... error: drop order affected for closure because of `capture_disjoint_fields` - --> $DIR/insignificant_drop.rs:31:13 + --> $DIR/insignificant_drop.rs:34:13 | LL | let c = || { | _____________^ @@ -31,10 +39,18 @@ LL | | let _t2 = t2; LL | | }; | |_____^ | - = note: drop(&(t, t1)); +help: add a dummy let to cause `t`, `t1` to be fully captured + | +LL | let c = || { let _ = (&t, &t1); +LL | +LL | +LL | let _t = t.0; +LL | let _t1 = t1.0; +LL | let _t2 = t2; + ... error: drop order affected for closure because of `capture_disjoint_fields` - --> $DIR/insignificant_drop.rs:47:13 + --> $DIR/insignificant_drop.rs:50:13 | LL | let c = || { | _____________^ @@ -45,10 +61,18 @@ LL | | println!("{}", t1.1); LL | | }; | |_____^ | - = note: drop(&(t)); +help: add a dummy let to cause `t` to be fully captured + | +LL | let c = || { let _ = &t; +LL | +LL | +LL | let _t = t.0; +LL | println!("{}", t1.1); +LL | }; + | error: drop order affected for closure because of `capture_disjoint_fields` - --> $DIR/insignificant_drop.rs:65:13 + --> $DIR/insignificant_drop.rs:68:13 | LL | let c = || { | _____________^ @@ -59,10 +83,18 @@ LL | | let _t1 = t1.0; LL | | }; | |_____^ | - = note: drop(&(t)); +help: add a dummy let to cause `t` to be fully captured + | +LL | let c = || { let _ = &t; +LL | +LL | +LL | let _t = t.0; +LL | let _t1 = t1.0; +LL | }; + | error: drop order affected for closure because of `capture_disjoint_fields` - --> $DIR/insignificant_drop.rs:83:13 + --> $DIR/insignificant_drop.rs:86:13 | LL | let c = || { | _____________^ @@ -73,10 +105,18 @@ LL | | let _s = s.0; LL | | }; | |_____^ | - = note: drop(&(t)); +help: add a dummy let to cause `t` to be fully captured + | +LL | let c = || { let _ = &t; +LL | +LL | +LL | let _t = t.0; +LL | let _s = s.0; +LL | }; + | error: drop order affected for closure because of `capture_disjoint_fields` - --> $DIR/insignificant_drop.rs:98:13 + --> $DIR/insignificant_drop.rs:101:13 | LL | let c = move || { | _____________^ @@ -86,10 +126,17 @@ LL | | println!("{} {}", t1.1, t.1); LL | | }; | |_____^ | - = note: drop(&(t1, t)); +help: add a dummy let to cause `t1`, `t` to be fully captured + | +LL | let c = move || { let _ = (&t1, &t); +LL | +LL | +LL | println!("{} {}", t1.1, t.1); +LL | }; + | error: drop order affected for closure because of `capture_disjoint_fields` - --> $DIR/insignificant_drop.rs:113:13 + --> $DIR/insignificant_drop.rs:116:13 | LL | let c = || { | _____________^ @@ -99,7 +146,14 @@ LL | | let _t = t.0; LL | | }; | |_____^ | - = note: drop(&(t)); +help: add a dummy let to cause `t` to be fully captured + | +LL | let c = || { let _ = &t; +LL | +LL | +LL | let _t = t.0; +LL | }; + | error: aborting due to 7 previous errors diff --git a/src/test/ui/closures/2229_closure_analysis/migrations/issue-78720.rs b/src/test/ui/closures/2229_closure_analysis/migrations/issue-78720.rs new file mode 100644 index 0000000000000..3a6af00254c5a --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/migrations/issue-78720.rs @@ -0,0 +1,10 @@ +// run-pass + +#![warn(disjoint_capture_drop_reorder)] + +fn main() { + if let a = "" { + //~^ WARNING: irrefutable `if let` pattern + drop(|_: ()| drop(a)); + } +} diff --git a/src/test/ui/closures/2229_closure_analysis/migrations/issue-78720.stderr b/src/test/ui/closures/2229_closure_analysis/migrations/issue-78720.stderr new file mode 100644 index 0000000000000..7e5da949cb299 --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/migrations/issue-78720.stderr @@ -0,0 +1,15 @@ +warning: irrefutable `if let` pattern + --> $DIR/issue-78720.rs:6:5 + | +LL | / if let a = "" { +LL | | +LL | | drop(|_: ()| drop(a)); +LL | | } + | |_____^ + | + = note: `#[warn(irrefutable_let_patterns)]` on by default + = note: this pattern will always match, so the `if let` is useless + = help: consider replacing the `if let` with a `let` + +warning: 1 warning emitted + diff --git a/src/test/ui/closures/2229_closure_analysis/migrations/migrations_rustfix.fixed b/src/test/ui/closures/2229_closure_analysis/migrations/migrations_rustfix.fixed new file mode 100644 index 0000000000000..a3e51a2b8e91a --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/migrations/migrations_rustfix.fixed @@ -0,0 +1,40 @@ +// run-rustfix +#![deny(disjoint_capture_drop_reorder)] +//~^ NOTE: the lint level is defined here + +// Test the two possible cases for automated migartion using rustfix +// - Closure contains a block i.e. `|| { .. };` +// - Closure contains just an expr `|| ..;` + +#[derive(Debug)] +struct Foo(i32); +impl Drop for Foo { + fn drop(&mut self) { + println!("{:?} dropped", self.0); + } +} + +fn closure_contains_block() { + let t = (Foo(0), Foo(0)); + let c = || { let _ = &t; + //~^ ERROR: drop order affected for closure because of `capture_disjoint_fields` + //~| HELP: add a dummy let to cause `t` to be fully captured + let _t = t.0; + }; + + c(); +} + +fn closure_doesnt_contain_block() { + let t = (Foo(0), Foo(0)); + let c = || { let _ = &t; t.0 }; + //~^ ERROR: drop order affected for closure because of `capture_disjoint_fields` + //~| HELP: add a dummy let to cause `t` to be fully captured + + c(); +} + +fn main() { + closure_contains_block(); + closure_doesnt_contain_block(); +} diff --git a/src/test/ui/closures/2229_closure_analysis/migrations/migrations_rustfix.rs b/src/test/ui/closures/2229_closure_analysis/migrations/migrations_rustfix.rs new file mode 100644 index 0000000000000..0eb837b688835 --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/migrations/migrations_rustfix.rs @@ -0,0 +1,40 @@ +// run-rustfix +#![deny(disjoint_capture_drop_reorder)] +//~^ NOTE: the lint level is defined here + +// Test the two possible cases for automated migartion using rustfix +// - Closure contains a block i.e. `|| { .. };` +// - Closure contains just an expr `|| ..;` + +#[derive(Debug)] +struct Foo(i32); +impl Drop for Foo { + fn drop(&mut self) { + println!("{:?} dropped", self.0); + } +} + +fn closure_contains_block() { + let t = (Foo(0), Foo(0)); + let c = || { + //~^ ERROR: drop order affected for closure because of `capture_disjoint_fields` + //~| HELP: add a dummy let to cause `t` to be fully captured + let _t = t.0; + }; + + c(); +} + +fn closure_doesnt_contain_block() { + let t = (Foo(0), Foo(0)); + let c = || t.0; + //~^ ERROR: drop order affected for closure because of `capture_disjoint_fields` + //~| HELP: add a dummy let to cause `t` to be fully captured + + c(); +} + +fn main() { + closure_contains_block(); + closure_doesnt_contain_block(); +} diff --git a/src/test/ui/closures/2229_closure_analysis/migrations/migrations_rustfix.stderr b/src/test/ui/closures/2229_closure_analysis/migrations/migrations_rustfix.stderr new file mode 100644 index 0000000000000..e6173217edc2f --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/migrations/migrations_rustfix.stderr @@ -0,0 +1,38 @@ +error: drop order affected for closure because of `capture_disjoint_fields` + --> $DIR/migrations_rustfix.rs:19:13 + | +LL | let c = || { + | _____________^ +LL | | +LL | | +LL | | let _t = t.0; +LL | | }; + | |_____^ + | +note: the lint level is defined here + --> $DIR/migrations_rustfix.rs:2:9 + | +LL | #![deny(disjoint_capture_drop_reorder)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +help: add a dummy let to cause `t` to be fully captured + | +LL | let c = || { let _ = &t; +LL | +LL | +LL | let _t = t.0; +LL | }; + | + +error: drop order affected for closure because of `capture_disjoint_fields` + --> $DIR/migrations_rustfix.rs:30:13 + | +LL | let c = || t.0; + | ^^^^^^ + | +help: add a dummy let to cause `t` to be fully captured + | +LL | let c = || { let _ = &t; t.0 }; + | ^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 2 previous errors + diff --git a/src/test/ui/closures/2229_closure_analysis/migrations/precise.fixed b/src/test/ui/closures/2229_closure_analysis/migrations/precise.fixed new file mode 100644 index 0000000000000..b739035c78422 --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/migrations/precise.fixed @@ -0,0 +1,56 @@ +// run-rustfix + +#![deny(disjoint_capture_drop_reorder)] + +#[derive(Debug)] +struct Foo(i32); +impl Drop for Foo { + fn drop(&mut self) { + println!("{:?} dropped", self.0); + } +} + +struct ConstainsDropField(Foo, Foo); + +// Test that lint is triggered if a path that implements Drop is not captured by move +fn test_precise_analysis_drop_paths_not_captured_by_move() { + let t = ConstainsDropField(Foo(10), Foo(20)); + + let c = || { let _ = &t; + //~^ ERROR: drop order affected for closure because of `capture_disjoint_fields` + //~| HELP: add a dummy let to cause `t` to be fully captured + let _t = t.0; + let _t = &t.1; + }; + + c(); +} + +struct S; +impl Drop for S { + fn drop(&mut self) { + } +} + +struct T(S, S); +struct U(T, T); + +// Test precise analysis for the lint works with paths longer than one. +fn test_precise_analysis_long_path_missing() { + let u = U(T(S, S), T(S, S)); + + let c = || { let _ = &u; + //~^ ERROR: drop order affected for closure because of `capture_disjoint_fields` + //~| HELP: add a dummy let to cause `u` to be fully captured + let _x = u.0.0; + let _x = u.0.1; + let _x = u.1.0; + }; + + c(); +} + +fn main() { + test_precise_analysis_drop_paths_not_captured_by_move(); + test_precise_analysis_long_path_missing(); +} diff --git a/src/test/ui/closures/2229_closure_analysis/migrations/precise.rs b/src/test/ui/closures/2229_closure_analysis/migrations/precise.rs index 79702cc6b56f3..e1f29c9d0e9d8 100644 --- a/src/test/ui/closures/2229_closure_analysis/migrations/precise.rs +++ b/src/test/ui/closures/2229_closure_analysis/migrations/precise.rs @@ -1,5 +1,6 @@ +// run-rustfix + #![deny(disjoint_capture_drop_reorder)] -//~^ NOTE: the lint level is defined here #[derive(Debug)] struct Foo(i32); @@ -11,35 +12,13 @@ impl Drop for Foo { struct ConstainsDropField(Foo, Foo); -#[derive(Debug)] -struct ContainsAndImplsDrop(Foo); -impl Drop for ContainsAndImplsDrop { - fn drop(&mut self) { - println!("{:?} dropped", self.0); - } -} - -// Test that even if all paths starting at root variable that implement Drop are captured, -// the lint is triggered if the root variable implements drop and isn't captured. -fn test_precise_analysis_parent_root_impl_drop_not_captured() { - let t = ContainsAndImplsDrop(Foo(10)); - - let c = || { - //~^ERROR: drop order affected for closure because of `capture_disjoint_fields` - //~| NOTE: drop(&(t)); - let _t = t.0; - }; - - c(); -} - // Test that lint is triggered if a path that implements Drop is not captured by move fn test_precise_analysis_drop_paths_not_captured_by_move() { let t = ConstainsDropField(Foo(10), Foo(20)); let c = || { - //~^ERROR: drop order affected for closure because of `capture_disjoint_fields` - //~| NOTE: drop(&(t)); + //~^ ERROR: drop order affected for closure because of `capture_disjoint_fields` + //~| HELP: add a dummy let to cause `t` to be fully captured let _t = t.0; let _t = &t.1; }; @@ -61,8 +40,8 @@ fn test_precise_analysis_long_path_missing() { let u = U(T(S, S), T(S, S)); let c = || { - //~^ERROR: drop order affected for closure because of `capture_disjoint_fields` - //~| NOTE: drop(&(u)); + //~^ ERROR: drop order affected for closure because of `capture_disjoint_fields` + //~| HELP: add a dummy let to cause `u` to be fully captured let _x = u.0.0; let _x = u.0.1; let _x = u.1.0; @@ -72,7 +51,6 @@ fn test_precise_analysis_long_path_missing() { } fn main() { - test_precise_analysis_parent_root_impl_drop_not_captured(); test_precise_analysis_drop_paths_not_captured_by_move(); test_precise_analysis_long_path_missing(); } diff --git a/src/test/ui/closures/2229_closure_analysis/migrations/precise.stderr b/src/test/ui/closures/2229_closure_analysis/migrations/precise.stderr index 968ca395f946e..7135ded13c256 100644 --- a/src/test/ui/closures/2229_closure_analysis/migrations/precise.stderr +++ b/src/test/ui/closures/2229_closure_analysis/migrations/precise.stderr @@ -1,37 +1,32 @@ error: drop order affected for closure because of `capture_disjoint_fields` - --> $DIR/precise.rs:27:13 + --> $DIR/precise.rs:19:13 | LL | let c = || { | _____________^ LL | | LL | | LL | | let _t = t.0; +LL | | let _t = &t.1; LL | | }; | |_____^ | note: the lint level is defined here - --> $DIR/precise.rs:1:9 + --> $DIR/precise.rs:3:9 | LL | #![deny(disjoint_capture_drop_reorder)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - = note: drop(&(t)); - -error: drop order affected for closure because of `capture_disjoint_fields` - --> $DIR/precise.rs:40:13 +help: add a dummy let to cause `t` to be fully captured | -LL | let c = || { - | _____________^ -LL | | -LL | | -LL | | let _t = t.0; -LL | | let _t = &t.1; -LL | | }; - | |_____^ +LL | let c = || { let _ = &t; +LL | +LL | +LL | let _t = t.0; +LL | let _t = &t.1; +LL | }; | - = note: drop(&(t)); error: drop order affected for closure because of `capture_disjoint_fields` - --> $DIR/precise.rs:63:13 + --> $DIR/precise.rs:42:13 | LL | let c = || { | _____________^ @@ -43,7 +38,15 @@ LL | | let _x = u.1.0; LL | | }; | |_____^ | - = note: drop(&(u)); +help: add a dummy let to cause `u` to be fully captured + | +LL | let c = || { let _ = &u; +LL | +LL | +LL | let _x = u.0.0; +LL | let _x = u.0.1; +LL | let _x = u.1.0; + ... -error: aborting due to 3 previous errors +error: aborting due to 2 previous errors diff --git a/src/test/ui/closures/2229_closure_analysis/migrations/significant_drop.fixed b/src/test/ui/closures/2229_closure_analysis/migrations/significant_drop.fixed new file mode 100644 index 0000000000000..e1b212153f431 --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/migrations/significant_drop.fixed @@ -0,0 +1,136 @@ +// run-rustfix +#![deny(disjoint_capture_drop_reorder)] +//~^ NOTE: the lint level is defined here + +// Test cases for types that implement a significant drop (user defined) + +#[derive(Debug)] +struct Foo(i32); +impl Drop for Foo { + fn drop(&mut self) { + println!("{:?} dropped", self.0); + } +} + +#[derive(Debug)] +struct ConstainsDropField(Foo, Foo); + +// `t` needs Drop because one of its elements needs drop, +// therefore precise capture might affect drop ordering +fn test1_all_need_migration() { + let t = (Foo(0), Foo(0)); + let t1 = (Foo(0), Foo(0)); + let t2 = (Foo(0), Foo(0)); + + let c = || { let _ = (&t, &t1, &t2); + //~^ ERROR: drop order affected for closure because of `capture_disjoint_fields` + //~| HELP: add a dummy let to cause `t`, `t1`, `t2` to be fully captured + let _t = t.0; + let _t1 = t1.0; + let _t2 = t2.0; + }; + + c(); +} + +// String implements drop and therefore should be migrated. +// But in this test cases, `t2` is completely captured and when it is dropped won't be affected +fn test2_only_precise_paths_need_migration() { + let t = (Foo(0), Foo(0)); + let t1 = (Foo(0), Foo(0)); + let t2 = (Foo(0), Foo(0)); + + let c = || { let _ = (&t, &t1); + //~^ ERROR: drop order affected for closure because of `capture_disjoint_fields` + //~| HELP: add a dummy let to cause `t`, `t1` to be fully captured + let _t = t.0; + let _t1 = t1.0; + let _t2 = t2; + }; + + c(); +} + +// If a variable would've not been captured by value then it would've not been +// dropped with the closure and therefore doesn't need migration. +fn test3_only_by_value_need_migration() { + let t = (Foo(0), Foo(0)); + let t1 = (Foo(0), Foo(0)); + let c = || { let _ = &t; + //~^ ERROR: drop order affected for closure because of `capture_disjoint_fields` + //~| HELP: add a dummy let to cause `t` to be fully captured + let _t = t.0; + println!("{:?}", t1.1); + }; + + c(); +} + +// The root variable might not implement drop themselves but some path starting +// at the root variable might implement Drop. +// +// If this path isn't captured we need to migrate for the root variable. +fn test4_type_contains_drop_need_migration() { + let t = ConstainsDropField(Foo(0), Foo(0)); + + let c = || { let _ = &t; + //~^ ERROR: drop order affected for closure because of `capture_disjoint_fields` + //~| HELP: add a dummy let to cause `t` to be fully captured + let _t = t.0; + }; + + c(); +} + +// Test migration analysis in case of Drop + Non Drop aggregates. +// Note we need migration here only because the non-copy (because Drop type) is captured, +// otherwise we won't need to, since we can get away with just by ref capture in that case. +fn test5_drop_non_drop_aggregate_need_migration() { + let t = (Foo(0), Foo(0), 0i32); + + let c = || { let _ = &t; + //~^ ERROR: drop order affected for closure because of `capture_disjoint_fields` + //~| HELP: add a dummy let to cause `t` to be fully captured + let _t = t.0; + }; + + c(); +} + +// Test migration analysis in case of Significant and Insignificant Drop aggregates. +fn test6_significant_insignificant_drop_aggregate_need_migration() { + let t = (Foo(0), String::new()); + + let c = || { let _ = &t; + //~^ ERROR: drop order affected for closure because of `capture_disjoint_fields` + //~| HELP: add a dummy let to cause `t` to be fully captured + let _t = t.1; + }; + + c(); +} + +// Since we are using a move closure here, both `t` and `t1` get moved +// even though they are being used by ref inside the closure. +fn test7_move_closures_non_copy_types_might_need_migration() { + let t = (Foo(0), Foo(0)); + let t1 = (Foo(0), Foo(0), Foo(0)); + + let c = move || { let _ = (&t1, &t); + //~^ ERROR: drop order affected for closure because of `capture_disjoint_fields` + //~| HELP: add a dummy let to cause `t1`, `t` to be fully captured + println!("{:?} {:?}", t1.1, t.1); + }; + + c(); +} + +fn main() { + test1_all_need_migration(); + test2_only_precise_paths_need_migration(); + test3_only_by_value_need_migration(); + test4_type_contains_drop_need_migration(); + test5_drop_non_drop_aggregate_need_migration(); + test6_significant_insignificant_drop_aggregate_need_migration(); + test7_move_closures_non_copy_types_might_need_migration(); +} diff --git a/src/test/ui/closures/2229_closure_analysis/migrations/significant_drop.rs b/src/test/ui/closures/2229_closure_analysis/migrations/significant_drop.rs index ed5e4ea8be011..106b293351515 100644 --- a/src/test/ui/closures/2229_closure_analysis/migrations/significant_drop.rs +++ b/src/test/ui/closures/2229_closure_analysis/migrations/significant_drop.rs @@ -1,3 +1,4 @@ +// run-rustfix #![deny(disjoint_capture_drop_reorder)] //~^ NOTE: the lint level is defined here @@ -22,8 +23,8 @@ fn test1_all_need_migration() { let t2 = (Foo(0), Foo(0)); let c = || { - //~^ERROR: drop order affected for closure because of `capture_disjoint_fields` - //~| NOTE: drop(&(t, t1, t2)); + //~^ ERROR: drop order affected for closure because of `capture_disjoint_fields` + //~| HELP: add a dummy let to cause `t`, `t1`, `t2` to be fully captured let _t = t.0; let _t1 = t1.0; let _t2 = t2.0; @@ -40,8 +41,8 @@ fn test2_only_precise_paths_need_migration() { let t2 = (Foo(0), Foo(0)); let c = || { - //~^ERROR: drop order affected for closure because of `capture_disjoint_fields` - //~| NOTE: drop(&(t, t1)); + //~^ ERROR: drop order affected for closure because of `capture_disjoint_fields` + //~| HELP: add a dummy let to cause `t`, `t1` to be fully captured let _t = t.0; let _t1 = t1.0; let _t2 = t2; @@ -56,8 +57,8 @@ fn test3_only_by_value_need_migration() { let t = (Foo(0), Foo(0)); let t1 = (Foo(0), Foo(0)); let c = || { - //~^ERROR: drop order affected for closure because of `capture_disjoint_fields` - //~| NOTE: drop(&(t)); + //~^ ERROR: drop order affected for closure because of `capture_disjoint_fields` + //~| HELP: add a dummy let to cause `t` to be fully captured let _t = t.0; println!("{:?}", t1.1); }; @@ -73,8 +74,8 @@ fn test4_type_contains_drop_need_migration() { let t = ConstainsDropField(Foo(0), Foo(0)); let c = || { - //~^ERROR: drop order affected for closure because of `capture_disjoint_fields` - //~| NOTE: drop(&(t)); + //~^ ERROR: drop order affected for closure because of `capture_disjoint_fields` + //~| HELP: add a dummy let to cause `t` to be fully captured let _t = t.0; }; @@ -88,8 +89,8 @@ fn test5_drop_non_drop_aggregate_need_migration() { let t = (Foo(0), Foo(0), 0i32); let c = || { - //~^ERROR: drop order affected for closure because of `capture_disjoint_fields` - //~| NOTE: drop(&(t)); + //~^ ERROR: drop order affected for closure because of `capture_disjoint_fields` + //~| HELP: add a dummy let to cause `t` to be fully captured let _t = t.0; }; @@ -98,13 +99,11 @@ fn test5_drop_non_drop_aggregate_need_migration() { // Test migration analysis in case of Significant and Insignificant Drop aggregates. fn test6_significant_insignificant_drop_aggregate_need_migration() { - struct S(i32, i32); - let t = (Foo(0), String::new()); let c = || { - //~^ERROR: drop order affected for closure because of `capture_disjoint_fields` - //~| NOTE: drop(&(t)); + //~^ ERROR: drop order affected for closure because of `capture_disjoint_fields` + //~| HELP: add a dummy let to cause `t` to be fully captured let _t = t.1; }; @@ -118,8 +117,8 @@ fn test7_move_closures_non_copy_types_might_need_migration() { let t1 = (Foo(0), Foo(0), Foo(0)); let c = move || { - //~^ERROR: drop order affected for closure because of `capture_disjoint_fields` - //~| NOTE: drop(&(t1, t)); + //~^ ERROR: drop order affected for closure because of `capture_disjoint_fields` + //~| HELP: add a dummy let to cause `t1`, `t` to be fully captured println!("{:?} {:?}", t1.1, t.1); }; diff --git a/src/test/ui/closures/2229_closure_analysis/migrations/significant_drop.stderr b/src/test/ui/closures/2229_closure_analysis/migrations/significant_drop.stderr index 6c21b27b493ba..ee29fe1306059 100644 --- a/src/test/ui/closures/2229_closure_analysis/migrations/significant_drop.stderr +++ b/src/test/ui/closures/2229_closure_analysis/migrations/significant_drop.stderr @@ -1,5 +1,5 @@ error: drop order affected for closure because of `capture_disjoint_fields` - --> $DIR/significant_drop.rs:24:13 + --> $DIR/significant_drop.rs:25:13 | LL | let c = || { | _____________^ @@ -12,14 +12,22 @@ LL | | }; | |_____^ | note: the lint level is defined here - --> $DIR/significant_drop.rs:1:9 + --> $DIR/significant_drop.rs:2:9 | LL | #![deny(disjoint_capture_drop_reorder)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - = note: drop(&(t, t1, t2)); +help: add a dummy let to cause `t`, `t1`, `t2` to be fully captured + | +LL | let c = || { let _ = (&t, &t1, &t2); +LL | +LL | +LL | let _t = t.0; +LL | let _t1 = t1.0; +LL | let _t2 = t2.0; + ... error: drop order affected for closure because of `capture_disjoint_fields` - --> $DIR/significant_drop.rs:42:13 + --> $DIR/significant_drop.rs:43:13 | LL | let c = || { | _____________^ @@ -31,10 +39,18 @@ LL | | let _t2 = t2; LL | | }; | |_____^ | - = note: drop(&(t, t1)); +help: add a dummy let to cause `t`, `t1` to be fully captured + | +LL | let c = || { let _ = (&t, &t1); +LL | +LL | +LL | let _t = t.0; +LL | let _t1 = t1.0; +LL | let _t2 = t2; + ... error: drop order affected for closure because of `capture_disjoint_fields` - --> $DIR/significant_drop.rs:58:13 + --> $DIR/significant_drop.rs:59:13 | LL | let c = || { | _____________^ @@ -45,10 +61,18 @@ LL | | println!("{:?}", t1.1); LL | | }; | |_____^ | - = note: drop(&(t)); +help: add a dummy let to cause `t` to be fully captured + | +LL | let c = || { let _ = &t; +LL | +LL | +LL | let _t = t.0; +LL | println!("{:?}", t1.1); +LL | }; + | error: drop order affected for closure because of `capture_disjoint_fields` - --> $DIR/significant_drop.rs:75:13 + --> $DIR/significant_drop.rs:76:13 | LL | let c = || { | _____________^ @@ -58,10 +82,17 @@ LL | | let _t = t.0; LL | | }; | |_____^ | - = note: drop(&(t)); +help: add a dummy let to cause `t` to be fully captured + | +LL | let c = || { let _ = &t; +LL | +LL | +LL | let _t = t.0; +LL | }; + | error: drop order affected for closure because of `capture_disjoint_fields` - --> $DIR/significant_drop.rs:90:13 + --> $DIR/significant_drop.rs:91:13 | LL | let c = || { | _____________^ @@ -71,10 +102,17 @@ LL | | let _t = t.0; LL | | }; | |_____^ | - = note: drop(&(t)); +help: add a dummy let to cause `t` to be fully captured + | +LL | let c = || { let _ = &t; +LL | +LL | +LL | let _t = t.0; +LL | }; + | error: drop order affected for closure because of `capture_disjoint_fields` - --> $DIR/significant_drop.rs:105:13 + --> $DIR/significant_drop.rs:104:13 | LL | let c = || { | _____________^ @@ -84,10 +122,17 @@ LL | | let _t = t.1; LL | | }; | |_____^ | - = note: drop(&(t)); +help: add a dummy let to cause `t` to be fully captured + | +LL | let c = || { let _ = &t; +LL | +LL | +LL | let _t = t.1; +LL | }; + | error: drop order affected for closure because of `capture_disjoint_fields` - --> $DIR/significant_drop.rs:120:13 + --> $DIR/significant_drop.rs:119:13 | LL | let c = move || { | _____________^ @@ -97,7 +142,14 @@ LL | | println!("{:?} {:?}", t1.1, t.1); LL | | }; | |_____^ | - = note: drop(&(t1, t)); +help: add a dummy let to cause `t1`, `t` to be fully captured + | +LL | let c = move || { let _ = (&t1, &t); +LL | +LL | +LL | println!("{:?} {:?}", t1.1, t.1); +LL | }; + | error: aborting due to 7 previous errors diff --git a/src/test/ui/closures/2229_closure_analysis/pattern-matching-should-fail.rs b/src/test/ui/closures/2229_closure_analysis/pattern-matching-should-fail.rs new file mode 100644 index 0000000000000..609a11a578ae8 --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/pattern-matching-should-fail.rs @@ -0,0 +1,84 @@ +#![feature(capture_disjoint_fields)] +//~^ WARNING: the feature `capture_disjoint_fields` is incomplete +//~| `#[warn(incomplete_features)]` on by default +//~| see issue #53488 <https://github.com/rust-lang/rust/issues/53488> +#![feature(never_type)] + +// Should fake read the discriminant and throw an error +fn test1() { + let x: !; + let c1 = || match x { }; + //~^ ERROR: use of possibly-uninitialized variable: `x` +} + +// Should fake read the discriminant and throw an error +fn test2() { + let x: !; + let c2 = || match x { _ => () }; + //~^ ERROR: borrow of possibly-uninitialized variable: `x` +} + +// Testing single variant patterns +enum SingleVariant { + Points(u32) +} + +// Should fake read the discriminant and throw an error +fn test3() { + let variant: !; + let c = || { + //~^ ERROR: borrow of possibly-uninitialized variable: `variant` + match variant { + SingleVariant::Points(_) => {} + } + }; + c(); +} + +// Should fake read the discriminant and throw an error +fn test4() { + let variant: !; + let c = || { + //~^ ERROR: borrow of possibly-uninitialized variable: `variant` + match variant { + SingleVariant::Points(a) => { + println!("{:?}", a); + } + } + }; + c(); +} + +fn test5() { + let t: !; + let g: !; + + let a = || { + match g { }; + //~^ ERROR: use of possibly-uninitialized variable: `g` + let c = || { + match t { }; + //~^ ERROR: use of possibly-uninitialized variable: `t` + }; + + c(); + }; + +} + +// Should fake read the discriminant and throw an error +fn test6() { + let x: u8; + let c1 = || match x { }; + //~^ ERROR: use of possibly-uninitialized variable: `x` + //~| ERROR: non-exhaustive patterns: type `u8` is non-empty +} + +fn main() { + test1(); + test2(); + test3(); + test4(); + test5(); + test6(); +} diff --git a/src/test/ui/closures/2229_closure_analysis/pattern-matching-should-fail.stderr b/src/test/ui/closures/2229_closure_analysis/pattern-matching-should-fail.stderr new file mode 100644 index 0000000000000..c225abb58b731 --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/pattern-matching-should-fail.stderr @@ -0,0 +1,72 @@ +warning: the feature `capture_disjoint_fields` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/pattern-matching-should-fail.rs:1:12 + | +LL | #![feature(capture_disjoint_fields)] + | ^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #53488 <https://github.com/rust-lang/rust/issues/53488> for more information + +error[E0004]: non-exhaustive patterns: type `u8` is non-empty + --> $DIR/pattern-matching-should-fail.rs:72:23 + | +LL | let c1 = || match x { }; + | ^ + | + = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + = note: the matched value is of type `u8` + +error[E0381]: use of possibly-uninitialized variable: `x` + --> $DIR/pattern-matching-should-fail.rs:10:23 + | +LL | let c1 = || match x { }; + | ^ use of possibly-uninitialized `x` + +error[E0381]: borrow of possibly-uninitialized variable: `x` + --> $DIR/pattern-matching-should-fail.rs:17:14 + | +LL | let c2 = || match x { _ => () }; + | ^^ - borrow occurs due to use in closure + | | + | use of possibly-uninitialized `x` + +error[E0381]: borrow of possibly-uninitialized variable: `variant` + --> $DIR/pattern-matching-should-fail.rs:29:13 + | +LL | let c = || { + | ^^ use of possibly-uninitialized `variant` +LL | +LL | match variant { + | ------- borrow occurs due to use in closure + +error[E0381]: borrow of possibly-uninitialized variable: `variant` + --> $DIR/pattern-matching-should-fail.rs:41:13 + | +LL | let c = || { + | ^^ use of possibly-uninitialized `variant` +LL | +LL | match variant { + | ------- borrow occurs due to use in closure + +error[E0381]: use of possibly-uninitialized variable: `g` + --> $DIR/pattern-matching-should-fail.rs:57:15 + | +LL | match g { }; + | ^ use of possibly-uninitialized `g` + +error[E0381]: use of possibly-uninitialized variable: `t` + --> $DIR/pattern-matching-should-fail.rs:60:19 + | +LL | match t { }; + | ^ use of possibly-uninitialized `t` + +error[E0381]: use of possibly-uninitialized variable: `x` + --> $DIR/pattern-matching-should-fail.rs:72:23 + | +LL | let c1 = || match x { }; + | ^ use of possibly-uninitialized `x` + +error: aborting due to 8 previous errors; 1 warning emitted + +Some errors have detailed explanations: E0004, E0381. +For more information about an error, try `rustc --explain E0004`. diff --git a/src/test/ui/closures/2229_closure_analysis/patterns-capture-analysis.rs b/src/test/ui/closures/2229_closure_analysis/patterns-capture-analysis.rs new file mode 100644 index 0000000000000..0a877dd366c7f --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/patterns-capture-analysis.rs @@ -0,0 +1,139 @@ +#![feature(capture_disjoint_fields)] +//~^ WARNING: the feature `capture_disjoint_fields` is incomplete +//~| NOTE: `#[warn(incomplete_features)]` on by default +//~| NOTE: see issue #53488 <https://github.com/rust-lang/rust/issues/53488> +#![feature(rustc_attrs)] + +// Should capture the discriminant since a variant of a multivariant enum is +// mentioned in the match arm; the discriminant is captured by the closure regardless +// of if it creates a binding +fn test_1_should_capture() { + let variant = Some(2229); + let c = #[rustc_capture_analysis] + //~^ ERROR: attributes on expressions are experimental + //~| NOTE: see issue #15701 <https://github.com/rust-lang/rust/issues/15701> + + || { + //~^ First Pass analysis includes: + //~| Min Capture analysis includes: + match variant { + //~^ NOTE: Capturing variant[] -> ImmBorrow + //~| NOTE: Min Capture variant[] -> ImmBorrow + Some(_) => {} + _ => {} + } + }; + c(); +} + +// Should not capture the discriminant since only a wildcard is mentioned in the +// match arm +fn test_2_should_not_capture() { + let variant = Some(2229); + let c = #[rustc_capture_analysis] + //~^ ERROR: attributes on expressions are experimental + //~| NOTE: see issue #15701 <https://github.com/rust-lang/rust/issues/15701> + || { + //~^ First Pass analysis includes: + match variant { + _ => {} + } + }; + c(); +} + +// Testing single variant patterns +enum SingleVariant { + Points(u32) +} + +// Should not capture the discriminant since the single variant mentioned +// in the match arm does not trigger a binding +fn test_3_should_not_capture_single_variant() { + let variant = SingleVariant::Points(1); + let c = #[rustc_capture_analysis] + //~^ ERROR: attributes on expressions are experimental + //~| NOTE: see issue #15701 <https://github.com/rust-lang/rust/issues/15701> + || { + //~^ First Pass analysis includes: + match variant { + SingleVariant::Points(_) => {} + } + }; + c(); +} + +// Should not capture the discriminant since the single variant mentioned +// in the match arm does not trigger a binding +fn test_6_should_capture_single_variant() { + let variant = SingleVariant::Points(1); + let c = #[rustc_capture_analysis] + //~^ ERROR: attributes on expressions are experimental + //~| NOTE: see issue #15701 <https://github.com/rust-lang/rust/issues/15701> + || { + //~^ First Pass analysis includes: + //~| Min Capture analysis includes: + match variant { + //~^ NOTE: Capturing variant[] -> ImmBorrow + //~| NOTE: Capturing variant[(0, 0)] -> ImmBorrow + //~| NOTE: Min Capture variant[] -> ImmBorrow + SingleVariant::Points(a) => { + println!("{:?}", a); + } + } + }; + c(); +} + +// Should not capture the discriminant since only wildcards are mentioned in the +// match arm +fn test_4_should_not_capture_array() { + let array: [i32; 3] = [0; 3]; + let c = #[rustc_capture_analysis] + //~^ ERROR: attributes on expressions are experimental + //~| NOTE: see issue #15701 <https://github.com/rust-lang/rust/issues/15701> + || { + //~^ First Pass analysis includes: + match array { + [_,_,_] => {} + } + }; + c(); +} + +// Testing MultiVariant patterns +enum MVariant { + A, + B, + C, +} + +// Should capture the discriminant since a variant of the multi variant enum is +// mentioned in the match arm; the discriminant is captured by the closure +// regardless of if it creates a binding +fn test_5_should_capture_multi_variant() { + let variant = MVariant::A; + let c = #[rustc_capture_analysis] + //~^ ERROR: attributes on expressions are experimental + //~| NOTE: see issue #15701 <https://github.com/rust-lang/rust/issues/15701> + || { + //~^ First Pass analysis includes: + //~| Min Capture analysis includes: + match variant { + //~^ NOTE: Capturing variant[] -> ImmBorrow + //~| NOTE: Min Capture variant[] -> ImmBorrow + MVariant::A => {} + _ => {} + } + }; + c(); +} + +fn main() { + test_1_should_capture(); + test_2_should_not_capture(); + test_3_should_not_capture_single_variant(); + test_6_should_capture_single_variant(); + test_4_should_not_capture_array(); + test_5_should_capture_multi_variant(); +} diff --git a/src/test/ui/closures/2229_closure_analysis/patterns-capture-analysis.stderr b/src/test/ui/closures/2229_closure_analysis/patterns-capture-analysis.stderr new file mode 100644 index 0000000000000..ad3e96a5753e3 --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/patterns-capture-analysis.stderr @@ -0,0 +1,212 @@ +error[E0658]: attributes on expressions are experimental + --> $DIR/patterns-capture-analysis.rs:12:14 + | +LL | let c = #[rustc_capture_analysis] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #15701 <https://github.com/rust-lang/rust/issues/15701> for more information + = help: add `#![feature(stmt_expr_attributes)]` to the crate attributes to enable + +error[E0658]: attributes on expressions are experimental + --> $DIR/patterns-capture-analysis.rs:33:14 + | +LL | let c = #[rustc_capture_analysis] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #15701 <https://github.com/rust-lang/rust/issues/15701> for more information + = help: add `#![feature(stmt_expr_attributes)]` to the crate attributes to enable + +error[E0658]: attributes on expressions are experimental + --> $DIR/patterns-capture-analysis.rs:54:14 + | +LL | let c = #[rustc_capture_analysis] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #15701 <https://github.com/rust-lang/rust/issues/15701> for more information + = help: add `#![feature(stmt_expr_attributes)]` to the crate attributes to enable + +error[E0658]: attributes on expressions are experimental + --> $DIR/patterns-capture-analysis.rs:70:14 + | +LL | let c = #[rustc_capture_analysis] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #15701 <https://github.com/rust-lang/rust/issues/15701> for more information + = help: add `#![feature(stmt_expr_attributes)]` to the crate attributes to enable + +error[E0658]: attributes on expressions are experimental + --> $DIR/patterns-capture-analysis.rs:92:14 + | +LL | let c = #[rustc_capture_analysis] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #15701 <https://github.com/rust-lang/rust/issues/15701> for more information + = help: add `#![feature(stmt_expr_attributes)]` to the crate attributes to enable + +error[E0658]: attributes on expressions are experimental + --> $DIR/patterns-capture-analysis.rs:116:14 + | +LL | let c = #[rustc_capture_analysis] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #15701 <https://github.com/rust-lang/rust/issues/15701> for more information + = help: add `#![feature(stmt_expr_attributes)]` to the crate attributes to enable + +warning: the feature `capture_disjoint_fields` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/patterns-capture-analysis.rs:1:12 + | +LL | #![feature(capture_disjoint_fields)] + | ^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #53488 <https://github.com/rust-lang/rust/issues/53488> for more information + +error: First Pass analysis includes: + --> $DIR/patterns-capture-analysis.rs:16:5 + | +LL | / || { +LL | | +LL | | +LL | | match variant { +... | +LL | | } +LL | | }; + | |_____^ + | +note: Capturing variant[] -> ImmBorrow + --> $DIR/patterns-capture-analysis.rs:19:15 + | +LL | match variant { + | ^^^^^^^ + +error: Min Capture analysis includes: + --> $DIR/patterns-capture-analysis.rs:16:5 + | +LL | / || { +LL | | +LL | | +LL | | match variant { +... | +LL | | } +LL | | }; + | |_____^ + | +note: Min Capture variant[] -> ImmBorrow + --> $DIR/patterns-capture-analysis.rs:19:15 + | +LL | match variant { + | ^^^^^^^ + +error: First Pass analysis includes: + --> $DIR/patterns-capture-analysis.rs:36:5 + | +LL | / || { +LL | | +LL | | match variant { +LL | | _ => {} +LL | | } +LL | | }; + | |_____^ + +error: First Pass analysis includes: + --> $DIR/patterns-capture-analysis.rs:57:5 + | +LL | / || { +LL | | +LL | | match variant { +LL | | SingleVariant::Points(_) => {} +LL | | } +LL | | }; + | |_____^ + +error: First Pass analysis includes: + --> $DIR/patterns-capture-analysis.rs:73:5 + | +LL | / || { +LL | | +LL | | +LL | | match variant { +... | +LL | | } +LL | | }; + | |_____^ + | +note: Capturing variant[] -> ImmBorrow + --> $DIR/patterns-capture-analysis.rs:76:15 + | +LL | match variant { + | ^^^^^^^ +note: Capturing variant[(0, 0)] -> ImmBorrow + --> $DIR/patterns-capture-analysis.rs:76:15 + | +LL | match variant { + | ^^^^^^^ + +error: Min Capture analysis includes: + --> $DIR/patterns-capture-analysis.rs:73:5 + | +LL | / || { +LL | | +LL | | +LL | | match variant { +... | +LL | | } +LL | | }; + | |_____^ + | +note: Min Capture variant[] -> ImmBorrow + --> $DIR/patterns-capture-analysis.rs:76:15 + | +LL | match variant { + | ^^^^^^^ + +error: First Pass analysis includes: + --> $DIR/patterns-capture-analysis.rs:95:5 + | +LL | / || { +LL | | +LL | | match array { +LL | | [_,_,_] => {} +LL | | } +LL | | }; + | |_____^ + +error: First Pass analysis includes: + --> $DIR/patterns-capture-analysis.rs:119:5 + | +LL | / || { +LL | | +LL | | +LL | | match variant { +... | +LL | | } +LL | | }; + | |_____^ + | +note: Capturing variant[] -> ImmBorrow + --> $DIR/patterns-capture-analysis.rs:122:15 + | +LL | match variant { + | ^^^^^^^ + +error: Min Capture analysis includes: + --> $DIR/patterns-capture-analysis.rs:119:5 + | +LL | / || { +LL | | +LL | | +LL | | match variant { +... | +LL | | } +LL | | }; + | |_____^ + | +note: Min Capture variant[] -> ImmBorrow + --> $DIR/patterns-capture-analysis.rs:122:15 + | +LL | match variant { + | ^^^^^^^ + +error: aborting due to 15 previous errors; 1 warning emitted + +For more information about this error, try `rustc --explain E0658`. diff --git a/src/test/ui/closures/2229_closure_analysis/repr_packed.rs b/src/test/ui/closures/2229_closure_analysis/repr_packed.rs new file mode 100644 index 0000000000000..2b9ef2a76bba7 --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/repr_packed.rs @@ -0,0 +1,103 @@ +#![feature(capture_disjoint_fields)] +//~^ WARNING: the feature `capture_disjoint_fields` is incomplete +//~| `#[warn(incomplete_features)]` on by default +//~| see issue #53488 <https://github.com/rust-lang/rust/issues/53488> + +#![feature(rustc_attrs)] + +// `u8` aligned at a byte and are unaffected by repr(packed). +// Therefore we can precisely (and safely) capture references to both the fields. +fn test_alignment_not_affected() { + #[repr(packed)] + struct Foo { x: u8, y: u8 } + + let mut foo = Foo { x: 0, y: 0 }; + + let mut c = #[rustc_capture_analysis] + //~^ ERROR: attributes on expressions are experimental + //~| NOTE: see issue #15701 <https://github.com/rust-lang/rust/issues/15701> + || { + //~^ ERROR: First Pass analysis includes: + //~| ERROR: Min Capture analysis includes: + let z1: &u8 = &foo.x; + //~^ NOTE: Capturing foo[(0, 0)] -> ImmBorrow + //~| NOTE: Min Capture foo[(0, 0)] -> ImmBorrow + let z2: &mut u8 = &mut foo.y; + //~^ NOTE: Capturing foo[(1, 0)] -> MutBorrow + //~| NOTE: Min Capture foo[(1, 0)] -> MutBorrow + + *z2 = 42; + + println!("({}, {})", z1, z2); + }; + + c(); +} + +// `String`, `u16` are not aligned at a one byte boundry and are thus affected by repr(packed). +// +// Here we test that the closure doesn't capture a reference point to `foo.x` but +// rather capture `foo` entirely. +fn test_alignment_affected() { + #[repr(packed)] + struct Foo { x: String, y: u16 } + + let mut foo = Foo { x: String::new(), y: 0 }; + + let mut c = #[rustc_capture_analysis] + //~^ ERROR: attributes on expressions are experimental + //~| NOTE: see issue #15701 <https://github.com/rust-lang/rust/issues/15701> + || { + //~^ ERROR: First Pass analysis includes: + //~| ERROR: Min Capture analysis includes: + let z1: &String = &foo.x; + let z2: &mut u16 = &mut foo.y; + //~^ NOTE: Capturing foo[] -> MutBorrow + //~| NOTE: Min Capture foo[] -> MutBorrow + + + *z2 = 42; + + println!("({}, {})", z1, z2); + }; + + c(); +} + +// Given how the closure desugaring is implemented (at least at the time of writing this test), +// we don't need to truncate the captured path to a reference into a packed-struct if the field +// being referenced will be moved into the closure, since it's safe to move out a field from a +// packed-struct. +// +// However to avoid surprises for the user, or issues when the closure is +// inlined we will truncate the capture to access just the struct regardless of if the field +// might get moved into the closure. +fn test_truncation_when_ref_and_move() { + #[repr(packed)] + struct Foo { x: String } + + let mut foo = Foo { x: String::new() }; + + let c = #[rustc_capture_analysis] + //~^ ERROR: attributes on expressions are experimental + //~| NOTE: see issue #15701 <https://github.com/rust-lang/rust/issues/15701> + || { + //~^ ERROR: First Pass analysis includes: + //~| ERROR: Min Capture analysis includes: + println!("{}", foo.x); + //~^ NOTE: Capturing foo[] -> ImmBorrow + //~| NOTE: Min Capture foo[] -> ByValue + //~| NOTE: foo[] used here + let _z = foo.x; + //~^ NOTE: Capturing foo[(0, 0)] -> ByValue + //~| NOTE: foo[] captured as ByValue here + }; + + c(); +} + +fn main() { + test_truncation_when_ref_and_move(); + test_alignment_affected(); + test_alignment_not_affected(); +} diff --git a/src/test/ui/closures/2229_closure_analysis/repr_packed.stderr b/src/test/ui/closures/2229_closure_analysis/repr_packed.stderr new file mode 100644 index 0000000000000..0517dd04b6f44 --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/repr_packed.stderr @@ -0,0 +1,165 @@ +error[E0658]: attributes on expressions are experimental + --> $DIR/repr_packed.rs:16:17 + | +LL | let mut c = #[rustc_capture_analysis] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #15701 <https://github.com/rust-lang/rust/issues/15701> for more information + = help: add `#![feature(stmt_expr_attributes)]` to the crate attributes to enable + +error[E0658]: attributes on expressions are experimental + --> $DIR/repr_packed.rs:47:17 + | +LL | let mut c = #[rustc_capture_analysis] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #15701 <https://github.com/rust-lang/rust/issues/15701> for more information + = help: add `#![feature(stmt_expr_attributes)]` to the crate attributes to enable + +error[E0658]: attributes on expressions are experimental + --> $DIR/repr_packed.rs:81:13 + | +LL | let c = #[rustc_capture_analysis] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #15701 <https://github.com/rust-lang/rust/issues/15701> for more information + = help: add `#![feature(stmt_expr_attributes)]` to the crate attributes to enable + +warning: the feature `capture_disjoint_fields` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/repr_packed.rs:1:12 + | +LL | #![feature(capture_disjoint_fields)] + | ^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #53488 <https://github.com/rust-lang/rust/issues/53488> for more information + +error: First Pass analysis includes: + --> $DIR/repr_packed.rs:19:5 + | +LL | / || { +LL | | +LL | | +LL | | let z1: &u8 = &foo.x; +... | +LL | | println!("({}, {})", z1, z2); +LL | | }; + | |_____^ + | +note: Capturing foo[(0, 0)] -> ImmBorrow + --> $DIR/repr_packed.rs:22:24 + | +LL | let z1: &u8 = &foo.x; + | ^^^^^ +note: Capturing foo[(1, 0)] -> MutBorrow + --> $DIR/repr_packed.rs:25:32 + | +LL | let z2: &mut u8 = &mut foo.y; + | ^^^^^ + +error: Min Capture analysis includes: + --> $DIR/repr_packed.rs:19:5 + | +LL | / || { +LL | | +LL | | +LL | | let z1: &u8 = &foo.x; +... | +LL | | println!("({}, {})", z1, z2); +LL | | }; + | |_____^ + | +note: Min Capture foo[(0, 0)] -> ImmBorrow + --> $DIR/repr_packed.rs:22:24 + | +LL | let z1: &u8 = &foo.x; + | ^^^^^ +note: Min Capture foo[(1, 0)] -> MutBorrow + --> $DIR/repr_packed.rs:25:32 + | +LL | let z2: &mut u8 = &mut foo.y; + | ^^^^^ + +error: First Pass analysis includes: + --> $DIR/repr_packed.rs:50:5 + | +LL | / || { +LL | | +LL | | +LL | | let z1: &String = &foo.x; +... | +LL | | println!("({}, {})", z1, z2); +LL | | }; + | |_____^ + | +note: Capturing foo[] -> MutBorrow + --> $DIR/repr_packed.rs:54:33 + | +LL | let z2: &mut u16 = &mut foo.y; + | ^^^^^ + +error: Min Capture analysis includes: + --> $DIR/repr_packed.rs:50:5 + | +LL | / || { +LL | | +LL | | +LL | | let z1: &String = &foo.x; +... | +LL | | println!("({}, {})", z1, z2); +LL | | }; + | |_____^ + | +note: Min Capture foo[] -> MutBorrow + --> $DIR/repr_packed.rs:54:33 + | +LL | let z2: &mut u16 = &mut foo.y; + | ^^^^^ + +error: First Pass analysis includes: + --> $DIR/repr_packed.rs:84:5 + | +LL | / || { +LL | | +LL | | +LL | | println!("{}", foo.x); +... | +LL | | +LL | | }; + | |_____^ + | +note: Capturing foo[] -> ImmBorrow + --> $DIR/repr_packed.rs:87:24 + | +LL | println!("{}", foo.x); + | ^^^^^ +note: Capturing foo[(0, 0)] -> ByValue + --> $DIR/repr_packed.rs:91:18 + | +LL | let _z = foo.x; + | ^^^^^ + +error: Min Capture analysis includes: + --> $DIR/repr_packed.rs:84:5 + | +LL | / || { +LL | | +LL | | +LL | | println!("{}", foo.x); +... | +LL | | +LL | | }; + | |_____^ + | +note: Min Capture foo[] -> ByValue + --> $DIR/repr_packed.rs:87:24 + | +LL | println!("{}", foo.x); + | ^^^^^ foo[] used here +... +LL | let _z = foo.x; + | ^^^^^ foo[] captured as ByValue here + +error: aborting due to 9 previous errors; 1 warning emitted + +For more information about this error, try `rustc --explain E0658`. diff --git a/src/test/ui/closures/2229_closure_analysis/run_pass/capture_with_wildcard_match.rs b/src/test/ui/closures/2229_closure_analysis/run_pass/capture_with_wildcard_match.rs new file mode 100644 index 0000000000000..eaea0dbfb5e8c --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/run_pass/capture_with_wildcard_match.rs @@ -0,0 +1,28 @@ +//check-pass +#![feature(capture_disjoint_fields)] +//~^ WARNING: the feature `capture_disjoint_fields` is incomplete + +fn test1() { + let foo : [Vec<u8>; 3] = ["String".into(), "String".into(), "String".into()]; + let c = || { + match foo { _ => () }; + }; + drop(foo); + c(); +} + +fn test2() { + let foo : Option<[Vec<u8>; 3]> = Some(["String".into(), "String".into(), "String".into()]); + let c = || { + match foo { + Some(_) => 1, + _ => 2 + }; + }; + c(); +} + +fn main() { + test1(); + test2(); +} diff --git a/src/test/ui/closures/2229_closure_analysis/run_pass/capture_with_wildcard_match.stderr b/src/test/ui/closures/2229_closure_analysis/run_pass/capture_with_wildcard_match.stderr new file mode 100644 index 0000000000000..2c17a189afbbc --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/run_pass/capture_with_wildcard_match.stderr @@ -0,0 +1,11 @@ +warning: the feature `capture_disjoint_fields` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/capture_with_wildcard_match.rs:2:12 + | +LL | #![feature(capture_disjoint_fields)] + | ^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #53488 <https://github.com/rust-lang/rust/issues/53488> for more information + +warning: 1 warning emitted + diff --git a/src/test/ui/closures/2229_closure_analysis/run_pass/destructure-pattern-closure-within-closure.rs b/src/test/ui/closures/2229_closure_analysis/run_pass/destructure-pattern-closure-within-closure.rs new file mode 100644 index 0000000000000..3ad083a92d569 --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/run_pass/destructure-pattern-closure-within-closure.rs @@ -0,0 +1,22 @@ +//check-pass +#![feature(capture_disjoint_fields)] +//~^ WARNING: the feature `capture_disjoint_fields` is incomplete +#![warn(unused)] + +fn main() { + let t = (String::from("Hello"), String::from("World")); + let g = (String::from("Mr"), String::from("Goose")); + + let a = || { + let (_, g2) = g; + //~^ WARN unused variable: `g2` + let c = || { + let (_, t2) = t; + //~^ WARN unused variable: `t2` + }; + + c(); + }; + + a(); +} diff --git a/src/test/ui/closures/2229_closure_analysis/run_pass/destructure-pattern-closure-within-closure.stderr b/src/test/ui/closures/2229_closure_analysis/run_pass/destructure-pattern-closure-within-closure.stderr new file mode 100644 index 0000000000000..c4abf934123b3 --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/run_pass/destructure-pattern-closure-within-closure.stderr @@ -0,0 +1,30 @@ +warning: the feature `capture_disjoint_fields` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/destructure-pattern-closure-within-closure.rs:2:12 + | +LL | #![feature(capture_disjoint_fields)] + | ^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #53488 <https://github.com/rust-lang/rust/issues/53488> for more information + +warning: unused variable: `t2` + --> $DIR/destructure-pattern-closure-within-closure.rs:14:21 + | +LL | let (_, t2) = t; + | ^^ help: if this is intentional, prefix it with an underscore: `_t2` + | +note: the lint level is defined here + --> $DIR/destructure-pattern-closure-within-closure.rs:4:9 + | +LL | #![warn(unused)] + | ^^^^^^ + = note: `#[warn(unused_variables)]` implied by `#[warn(unused)]` + +warning: unused variable: `g2` + --> $DIR/destructure-pattern-closure-within-closure.rs:11:17 + | +LL | let (_, g2) = g; + | ^^ help: if this is intentional, prefix it with an underscore: `_g2` + +warning: 3 warnings emitted + diff --git a/src/test/ui/closures/2229_closure_analysis/run_pass/destructure_patterns.rs b/src/test/ui/closures/2229_closure_analysis/run_pass/destructure_patterns.rs new file mode 100644 index 0000000000000..65527648b2c99 --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/run_pass/destructure_patterns.rs @@ -0,0 +1,123 @@ +//check-pass +#![feature(capture_disjoint_fields)] +//~^ WARNING: the feature `capture_disjoint_fields` is incomplete +#![warn(unused)] + +struct Point { + x: u32, + y: u32, +} + +fn test1() { + let t = (String::from("Hello"), String::from("World")); + + let c = || { + let (t1, t2) = t; + //~^ WARN unused variable: `t1` + //~| WARN unused variable: `t2` + }; + + c(); +} + +fn test2() { + let t = (String::from("Hello"), String::from("World")); + + let c = || { + let (t1, _) = t; + //~^ WARN unused variable: `t1` + }; + + c(); +} + +fn test3() { + let t = (String::from("Hello"), String::from("World")); + + let c = || { + let (_, t2) = t; + //~^ WARN unused variable: `t2` + }; + + c(); +} + +fn test4() { + let t = (String::from("Hello"), String::from("World")); + //~^ WARN unused variable: `t` + + let c = || { + let (_, _) = t; + }; + + c(); +} + +fn test5() { + let t = (String::new(), String::new()); + let _c = || { + let _a = match t { + (t1, _) => t1, + }; + }; +} + +fn test6() { + let t = (String::new(), String::new()); + let _c = || { + let _a = match t { + (_, t2) => t2, + }; + }; +} + +fn test7() { + let t = (String::new(), String::new()); + let _c = || { + let _a = match t { + (t1, t2) => (t1, t2), + }; + }; +} + +fn test8() { + let x = 0; + //~^ WARN unused variable: `x` + let tup = (1, 2); + //~^ WARN unused variable: `tup` + let p = Point { x: 10, y: 20 }; + + let c = || { + let _ = x; + let Point { x, y } = p; + //~^ WARN unused variable: `x` + println!("{}", y); + let (_, _) = tup; + }; + + c(); +} + +fn test9() { + let _z = 9; + let t = (String::from("Hello"), String::from("World")); + + let c = || { + let (_, t) = t; + println!("{}", t); + }; + + c(); +} + +fn main() { + test1(); + test2(); + test3(); + test4(); + test5(); + test6(); + test7(); + test8(); + test9(); +} diff --git a/src/test/ui/closures/2229_closure_analysis/run_pass/destructure_patterns.stderr b/src/test/ui/closures/2229_closure_analysis/run_pass/destructure_patterns.stderr new file mode 100644 index 0000000000000..fcfe9ee95f185 --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/run_pass/destructure_patterns.stderr @@ -0,0 +1,66 @@ +warning: the feature `capture_disjoint_fields` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/destructure_patterns.rs:2:12 + | +LL | #![feature(capture_disjoint_fields)] + | ^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #53488 <https://github.com/rust-lang/rust/issues/53488> for more information + +warning: unused variable: `t1` + --> $DIR/destructure_patterns.rs:15:14 + | +LL | let (t1, t2) = t; + | ^^ help: if this is intentional, prefix it with an underscore: `_t1` + | +note: the lint level is defined here + --> $DIR/destructure_patterns.rs:4:9 + | +LL | #![warn(unused)] + | ^^^^^^ + = note: `#[warn(unused_variables)]` implied by `#[warn(unused)]` + +warning: unused variable: `t2` + --> $DIR/destructure_patterns.rs:15:18 + | +LL | let (t1, t2) = t; + | ^^ help: if this is intentional, prefix it with an underscore: `_t2` + +warning: unused variable: `t1` + --> $DIR/destructure_patterns.rs:27:14 + | +LL | let (t1, _) = t; + | ^^ help: if this is intentional, prefix it with an underscore: `_t1` + +warning: unused variable: `t2` + --> $DIR/destructure_patterns.rs:38:17 + | +LL | let (_, t2) = t; + | ^^ help: if this is intentional, prefix it with an underscore: `_t2` + +warning: unused variable: `t` + --> $DIR/destructure_patterns.rs:46:9 + | +LL | let t = (String::from("Hello"), String::from("World")); + | ^ help: if this is intentional, prefix it with an underscore: `_t` + +warning: unused variable: `x` + --> $DIR/destructure_patterns.rs:92:21 + | +LL | let Point { x, y } = p; + | ^ help: try ignoring the field: `x: _` + +warning: unused variable: `x` + --> $DIR/destructure_patterns.rs:84:9 + | +LL | let x = 0; + | ^ help: if this is intentional, prefix it with an underscore: `_x` + +warning: unused variable: `tup` + --> $DIR/destructure_patterns.rs:86:9 + | +LL | let tup = (1, 2); + | ^^^ help: if this is intentional, prefix it with an underscore: `_tup` + +warning: 9 warnings emitted + diff --git a/src/test/ui/closures/2229_closure_analysis/run_pass/drop_then_use_fake_reads.rs b/src/test/ui/closures/2229_closure_analysis/run_pass/drop_then_use_fake_reads.rs new file mode 100644 index 0000000000000..dae50854d82fb --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/run_pass/drop_then_use_fake_reads.rs @@ -0,0 +1,12 @@ +//check-pass +#![feature(capture_disjoint_fields)] +//~^ WARNING: the feature `capture_disjoint_fields` is incomplete +#![feature(rustc_attrs)] + +fn main() { + let mut x = 1; + let c = || { + drop(&mut x); + match x { _ => () } + }; +} diff --git a/src/test/ui/closures/2229_closure_analysis/run_pass/drop_then_use_fake_reads.stderr b/src/test/ui/closures/2229_closure_analysis/run_pass/drop_then_use_fake_reads.stderr new file mode 100644 index 0000000000000..7f811875d1363 --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/run_pass/drop_then_use_fake_reads.stderr @@ -0,0 +1,11 @@ +warning: the feature `capture_disjoint_fields` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/drop_then_use_fake_reads.rs:2:12 + | +LL | #![feature(capture_disjoint_fields)] + | ^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #53488 <https://github.com/rust-lang/rust/issues/53488> for more information + +warning: 1 warning emitted + diff --git a/src/test/ui/closures/2229_closure_analysis/run_pass/lit-pattern-matching-with-methods.rs b/src/test/ui/closures/2229_closure_analysis/run_pass/lit-pattern-matching-with-methods.rs new file mode 100644 index 0000000000000..9c086fe4bdfe7 --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/run_pass/lit-pattern-matching-with-methods.rs @@ -0,0 +1,31 @@ +//check-pass +#![feature(capture_disjoint_fields)] +//~^ WARNING: the feature `capture_disjoint_fields` is incomplete +#![warn(unused)] +#![feature(rustc_attrs)] +#![feature(btree_drain_filter)] + +use std::collections::BTreeMap; +use std::panic::{catch_unwind, AssertUnwindSafe}; + +fn main() { + let mut map = BTreeMap::new(); + map.insert("a", ()); + map.insert("b", ()); + map.insert("c", ()); + + { + let mut it = map.drain_filter(|_, _| true); + catch_unwind(AssertUnwindSafe(|| while it.next().is_some() {})).unwrap_err(); + let result = catch_unwind(AssertUnwindSafe(|| it.next())); + assert!(matches!(result, Ok(None))); + } + + { + let mut it = map.drain_filter(|_, _| true); + catch_unwind(AssertUnwindSafe(|| while let Some(_) = it.next() {})).unwrap_err(); + let result = catch_unwind(AssertUnwindSafe(|| it.next())); + assert!(matches!(result, Ok(None))); + } + +} diff --git a/src/test/ui/closures/2229_closure_analysis/run_pass/lit-pattern-matching-with-methods.stderr b/src/test/ui/closures/2229_closure_analysis/run_pass/lit-pattern-matching-with-methods.stderr new file mode 100644 index 0000000000000..bc046ecad6867 --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/run_pass/lit-pattern-matching-with-methods.stderr @@ -0,0 +1,11 @@ +warning: the feature `capture_disjoint_fields` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/lit-pattern-matching-with-methods.rs:2:12 + | +LL | #![feature(capture_disjoint_fields)] + | ^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #53488 <https://github.com/rust-lang/rust/issues/53488> for more information + +warning: 1 warning emitted + diff --git a/src/test/ui/closures/2229_closure_analysis/run_pass/struct-pattern-matching-with-methods.rs b/src/test/ui/closures/2229_closure_analysis/run_pass/struct-pattern-matching-with-methods.rs new file mode 100644 index 0000000000000..d260a448926d6 --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/run_pass/struct-pattern-matching-with-methods.rs @@ -0,0 +1,49 @@ +//check-pass +#![feature(capture_disjoint_fields)] +//~^ WARNING: the feature `capture_disjoint_fields` is incomplete +#![warn(unused)] +#![feature(rustc_attrs)] + +#[derive(Debug, Clone, Copy)] +enum PointType { + TwoD { x: u32, y: u32 }, + + ThreeD{ x: u32, y: u32, z: u32 } +} + +// Testing struct patterns +struct Points { + points: Vec<PointType>, +} + +impl Points { + pub fn test1(&mut self) -> Vec<usize> { + (0..self.points.len()) + .filter_map(|i| { + let idx = i as usize; + match self.test2(idx) { + PointType::TwoD { .. } => Some(i), + PointType::ThreeD { .. } => None, + } + }) + .collect() + } + + pub fn test2(&mut self, i: usize) -> PointType { + self.points[i] + } +} + +fn main() { + let mut points = Points { + points: Vec::<PointType>::new() + }; + + points.points.push(PointType::ThreeD { x:0, y:0, z:0 }); + points.points.push(PointType::TwoD{ x:0, y:0 }); + points.points.push(PointType::ThreeD{ x:0, y:0, z:0 }); + points.points.push(PointType::TwoD{ x:0, y:0 }); + + println!("{:?}", points.test1()); + println!("{:?}", points.points); +} diff --git a/src/test/ui/closures/2229_closure_analysis/run_pass/struct-pattern-matching-with-methods.stderr b/src/test/ui/closures/2229_closure_analysis/run_pass/struct-pattern-matching-with-methods.stderr new file mode 100644 index 0000000000000..3e4303a3710df --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/run_pass/struct-pattern-matching-with-methods.stderr @@ -0,0 +1,11 @@ +warning: the feature `capture_disjoint_fields` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/struct-pattern-matching-with-methods.rs:2:12 + | +LL | #![feature(capture_disjoint_fields)] + | ^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #53488 <https://github.com/rust-lang/rust/issues/53488> for more information + +warning: 1 warning emitted + diff --git a/src/test/ui/closures/2229_closure_analysis/run_pass/tuple-struct-pattern-matching-with-methods.rs b/src/test/ui/closures/2229_closure_analysis/run_pass/tuple-struct-pattern-matching-with-methods.rs new file mode 100644 index 0000000000000..b3bee79254ec4 --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/run_pass/tuple-struct-pattern-matching-with-methods.rs @@ -0,0 +1,44 @@ +//check-pass +#![feature(capture_disjoint_fields)] +//~^ WARNING: the feature `capture_disjoint_fields` is incomplete + +#[derive(Copy, Clone)] +enum PointType { + TwoD(u32, u32), + ThreeD(u32, u32, u32) +} + +// Testing tuple struct patterns +struct Points { + points: Vec<PointType>, +} + +impl Points { + pub fn test1(&mut self) -> Vec<usize> { + (0..self.points.len()) + .filter_map(|i| { + match self.test2(i) { + PointType::TwoD (..) => Some(i), + PointType::ThreeD (..) => None, + } + }) + .collect() + } + + pub fn test2(&mut self, i: usize) -> PointType { + self.points[i] + } +} + +fn main() { + let mut points = Points { + points: Vec::<PointType>::new() + }; + + points.points.push(PointType::ThreeD(0,0,0)); + points.points.push(PointType::TwoD(0,0)); + points.points.push(PointType::ThreeD(0,0,1)); + points.points.push(PointType::TwoD(0,1)); + + println!("{:?}", points.test1()); +} diff --git a/src/test/ui/closures/2229_closure_analysis/run_pass/tuple-struct-pattern-matching-with-methods.stderr b/src/test/ui/closures/2229_closure_analysis/run_pass/tuple-struct-pattern-matching-with-methods.stderr new file mode 100644 index 0000000000000..ded0e37b0f366 --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/run_pass/tuple-struct-pattern-matching-with-methods.stderr @@ -0,0 +1,11 @@ +warning: the feature `capture_disjoint_fields` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/tuple-struct-pattern-matching-with-methods.rs:2:12 + | +LL | #![feature(capture_disjoint_fields)] + | ^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #53488 <https://github.com/rust-lang/rust/issues/53488> for more information + +warning: 1 warning emitted + diff --git a/src/test/ui/closures/2229_closure_analysis/run_pass/use_of_mutable_borrow_and_fake_reads.rs b/src/test/ui/closures/2229_closure_analysis/run_pass/use_of_mutable_borrow_and_fake_reads.rs new file mode 100644 index 0000000000000..0e6da8f4f1889 --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/run_pass/use_of_mutable_borrow_and_fake_reads.rs @@ -0,0 +1,12 @@ +//check-pass +#![feature(capture_disjoint_fields)] +//~^ WARNING: the feature `capture_disjoint_fields` is incomplete +#![feature(rustc_attrs)] + +fn main() { + let mut x = 0; + let c = || { + &mut x; // mutable borrow of `x` + match x { _ => () } // fake read of `x` + }; +} diff --git a/src/test/ui/closures/2229_closure_analysis/run_pass/use_of_mutable_borrow_and_fake_reads.stderr b/src/test/ui/closures/2229_closure_analysis/run_pass/use_of_mutable_borrow_and_fake_reads.stderr new file mode 100644 index 0000000000000..7d16d77bf737e --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/run_pass/use_of_mutable_borrow_and_fake_reads.stderr @@ -0,0 +1,11 @@ +warning: the feature `capture_disjoint_fields` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/use_of_mutable_borrow_and_fake_reads.rs:2:12 + | +LL | #![feature(capture_disjoint_fields)] + | ^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #53488 <https://github.com/rust-lang/rust/issues/53488> for more information + +warning: 1 warning emitted + diff --git a/src/test/ui/closures/closure-bounds-cant-promote-superkind-in-struct.stderr b/src/test/ui/closures/closure-bounds-cant-promote-superkind-in-struct.stderr index 48f18b1ebe957..273eae995538a 100644 --- a/src/test/ui/closures/closure-bounds-cant-promote-superkind-in-struct.stderr +++ b/src/test/ui/closures/closure-bounds-cant-promote-superkind-in-struct.stderr @@ -9,8 +9,8 @@ LL | fn foo<F>(blk: F) -> X<F> where F: FnOnce() + 'static { | help: consider further restricting this bound | -LL | fn foo<F>(blk: F) -> X<F> where F: FnOnce() + 'static + Send { - | ^^^^^^ +LL | fn foo<F>(blk: F) -> X<F> where F: FnOnce() + 'static + std::marker::Send { + | ^^^^^^^^^^^^^^^^^^^ error: aborting due to previous error diff --git a/src/test/ui/closures/closure-bounds-subtype.stderr b/src/test/ui/closures/closure-bounds-subtype.stderr index d649eeccb8cc9..7df29d5a098a0 100644 --- a/src/test/ui/closures/closure-bounds-subtype.stderr +++ b/src/test/ui/closures/closure-bounds-subtype.stderr @@ -9,8 +9,8 @@ LL | take_const_owned(f); | help: consider further restricting this bound | -LL | fn give_owned<F>(f: F) where F: FnOnce() + Send + Sync { - | ^^^^^^ +LL | fn give_owned<F>(f: F) where F: FnOnce() + Send + std::marker::Sync { + | ^^^^^^^^^^^^^^^^^^^ error: aborting due to previous error diff --git a/src/test/ui/closures/issue-84128.rs b/src/test/ui/closures/issue-84128.rs new file mode 100644 index 0000000000000..f81d7cfaa654b --- /dev/null +++ b/src/test/ui/closures/issue-84128.rs @@ -0,0 +1,16 @@ +// test for issue 84128 +// missing suggestion for similar ADT type with diffetent generic paramenter +// on closure ReturnNoExpression + +struct Foo<T>(T); + +fn main() { + || { + if false { + return Foo(0); + } + + Foo(()) + //~^ ERROR mismatched types [E0308] + }; +} diff --git a/src/test/ui/closures/issue-84128.stderr b/src/test/ui/closures/issue-84128.stderr new file mode 100644 index 0000000000000..70d9273ddf7ce --- /dev/null +++ b/src/test/ui/closures/issue-84128.stderr @@ -0,0 +1,15 @@ +error[E0308]: mismatched types + --> $DIR/issue-84128.rs:13:13 + | +LL | Foo(()) + | ^^ expected integer, found `()` + | +note: return type inferred to be `{integer}` here + --> $DIR/issue-84128.rs:10:20 + | +LL | return Foo(0); + | ^^^^^^ + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0308`. diff --git a/src/test/ui/codemap_tests/two_files.stderr b/src/test/ui/codemap_tests/two_files.stderr index de2ffc2e5dc1d..aff51ee9e2f54 100644 --- a/src/test/ui/codemap_tests/two_files.stderr +++ b/src/test/ui/codemap_tests/two_files.stderr @@ -5,10 +5,9 @@ LL | impl Bar for Baz { } | ^^^ type aliases cannot be used as traits | help: you might have meant to use `#![feature(trait_alias)]` instead of a `type` alias - --> $DIR/two_files_data.rs:5:1 | -LL | type Bar = dyn Foo; - | ^^^^^^^^^^^^^^^^^^^ +LL | trait Bar = dyn Foo; + | error: aborting due to previous error diff --git a/src/test/ui/codemap_tests/unicode.stderr b/src/test/ui/codemap_tests/unicode.stderr index 61c3f4f1c9847..357dd25389ecb 100644 --- a/src/test/ui/codemap_tests/unicode.stderr +++ b/src/test/ui/codemap_tests/unicode.stderr @@ -4,7 +4,7 @@ error[E0703]: invalid ABI: found `路濫狼á́́` LL | extern "路濫狼á́́" fn foo() {} | ^^^^^^^^^ invalid ABI | - = help: valid ABIs: Rust, C, cdecl, stdcall, fastcall, vectorcall, thiscall, aapcs, win64, sysv64, ptx-kernel, msp430-interrupt, x86-interrupt, amdgpu-kernel, efiapi, avr-interrupt, avr-non-blocking-interrupt, C-cmse-nonsecure-call, system, rust-intrinsic, rust-call, platform-intrinsic, unadjusted + = help: valid ABIs: Rust, C, C-unwind, cdecl, stdcall, stdcall-unwind, fastcall, vectorcall, thiscall, thiscall-unwind, aapcs, win64, sysv64, ptx-kernel, msp430-interrupt, x86-interrupt, amdgpu-kernel, efiapi, avr-interrupt, avr-non-blocking-interrupt, C-cmse-nonsecure-call, wasm, system, system-unwind, rust-intrinsic, rust-call, platform-intrinsic, unadjusted error: aborting due to previous error diff --git a/src/test/ui/codemap_tests/unicode_2.rs b/src/test/ui/codemap_tests/unicode_2.rs index fa69115b2daaf..3301e7a18d11a 100644 --- a/src/test/ui/codemap_tests/unicode_2.rs +++ b/src/test/ui/codemap_tests/unicode_2.rs @@ -1,5 +1,3 @@ -#![feature(non_ascii_idents)] - fn main() { let _ = ("a̐éö̲", 0u7); //~ ERROR invalid width let _ = ("아あ", 1i42); //~ ERROR invalid width diff --git a/src/test/ui/codemap_tests/unicode_2.stderr b/src/test/ui/codemap_tests/unicode_2.stderr index c01942712d4f3..a776a4a1e7e13 100644 --- a/src/test/ui/codemap_tests/unicode_2.stderr +++ b/src/test/ui/codemap_tests/unicode_2.stderr @@ -1,5 +1,5 @@ error: invalid width `7` for integer literal - --> $DIR/unicode_2.rs:4:25 + --> $DIR/unicode_2.rs:2:25 | LL | let _ = ("a̐éö̲", 0u7); | ^^^ @@ -7,7 +7,7 @@ LL | let _ = ("a̐éö̲", 0u7); = help: valid widths are 8, 16, 32, 64 and 128 error: invalid width `42` for integer literal - --> $DIR/unicode_2.rs:5:20 + --> $DIR/unicode_2.rs:3:20 | LL | let _ = ("아あ", 1i42); | ^^^^ @@ -15,7 +15,7 @@ LL | let _ = ("아あ", 1i42); = help: valid widths are 8, 16, 32, 64 and 128 error[E0425]: cannot find value `a̐é` in this scope - --> $DIR/unicode_2.rs:6:13 + --> $DIR/unicode_2.rs:4:13 | LL | let _ = a̐é; | ^^ not found in this scope diff --git a/src/test/ui/coherence/coherence-blanket-conflicts-with-blanket-implemented.stderr b/src/test/ui/coherence/coherence-blanket-conflicts-with-blanket-implemented.stderr index e95826ed4d53d..9156972a1df99 100644 --- a/src/test/ui/coherence/coherence-blanket-conflicts-with-blanket-implemented.stderr +++ b/src/test/ui/coherence/coherence-blanket-conflicts-with-blanket-implemented.stderr @@ -1,4 +1,4 @@ -error[E0119]: conflicting implementations of trait `MyTrait`: +error[E0119]: conflicting implementations of trait `MyTrait` --> $DIR/coherence-blanket-conflicts-with-blanket-implemented.rs:24:1 | LL | impl<T:Even> MyTrait for T { diff --git a/src/test/ui/coherence/coherence-blanket-conflicts-with-blanket-unimplemented.stderr b/src/test/ui/coherence/coherence-blanket-conflicts-with-blanket-unimplemented.stderr index ea9838b4520e0..8400968e1227d 100644 --- a/src/test/ui/coherence/coherence-blanket-conflicts-with-blanket-unimplemented.stderr +++ b/src/test/ui/coherence/coherence-blanket-conflicts-with-blanket-unimplemented.stderr @@ -1,4 +1,4 @@ -error[E0119]: conflicting implementations of trait `MyTrait`: +error[E0119]: conflicting implementations of trait `MyTrait` --> $DIR/coherence-blanket-conflicts-with-blanket-unimplemented.rs:20:1 | LL | impl<T:Even> MyTrait for T { diff --git a/src/test/ui/coherence/coherence-blanket-conflicts-with-specific-cross-crate.stderr b/src/test/ui/coherence/coherence-blanket-conflicts-with-specific-cross-crate.stderr index a2008f04265dc..c25c43692928a 100644 --- a/src/test/ui/coherence/coherence-blanket-conflicts-with-specific-cross-crate.stderr +++ b/src/test/ui/coherence/coherence-blanket-conflicts-with-specific-cross-crate.stderr @@ -1,4 +1,4 @@ -error[E0119]: conflicting implementations of trait `go_trait::GoMut` for type `MyThingy`: +error[E0119]: conflicting implementations of trait `go_trait::GoMut` for type `MyThingy` --> $DIR/coherence-blanket-conflicts-with-specific-cross-crate.rs:15:1 | LL | impl GoMut for MyThingy { diff --git a/src/test/ui/coherence/coherence-blanket-conflicts-with-specific-multidispatch.stderr b/src/test/ui/coherence/coherence-blanket-conflicts-with-specific-multidispatch.stderr index af4f4d09d7abd..c2a925213da2d 100644 --- a/src/test/ui/coherence/coherence-blanket-conflicts-with-specific-multidispatch.stderr +++ b/src/test/ui/coherence/coherence-blanket-conflicts-with-specific-multidispatch.stderr @@ -1,4 +1,4 @@ -error[E0119]: conflicting implementations of trait `MyTrait<MyType>` for type `MyType`: +error[E0119]: conflicting implementations of trait `MyTrait<MyType>` for type `MyType` --> $DIR/coherence-blanket-conflicts-with-specific-multidispatch.rs:22:1 | LL | impl<T> MyTrait<T> for T { diff --git a/src/test/ui/coherence/coherence-blanket-conflicts-with-specific-trait.stderr b/src/test/ui/coherence/coherence-blanket-conflicts-with-specific-trait.stderr index 6922f3ebffaff..e1a5dffebdaad 100644 --- a/src/test/ui/coherence/coherence-blanket-conflicts-with-specific-trait.stderr +++ b/src/test/ui/coherence/coherence-blanket-conflicts-with-specific-trait.stderr @@ -1,4 +1,4 @@ -error[E0119]: conflicting implementations of trait `MyTrait` for type `MyType`: +error[E0119]: conflicting implementations of trait `MyTrait` for type `MyType` --> $DIR/coherence-blanket-conflicts-with-specific-trait.rs:20:1 | LL | impl<T:OtherTrait> MyTrait for T { diff --git a/src/test/ui/coherence/coherence-blanket-conflicts-with-specific.stderr b/src/test/ui/coherence/coherence-blanket-conflicts-with-specific.stderr index 4bc282764796b..ba60a2ea92947 100644 --- a/src/test/ui/coherence/coherence-blanket-conflicts-with-specific.stderr +++ b/src/test/ui/coherence/coherence-blanket-conflicts-with-specific.stderr @@ -1,4 +1,4 @@ -error[E0119]: conflicting implementations of trait `MyTrait` for type `MyType`: +error[E0119]: conflicting implementations of trait `MyTrait` for type `MyType` --> $DIR/coherence-blanket-conflicts-with-specific.rs:19:1 | LL | impl<T> MyTrait for T { diff --git a/src/test/ui/coherence/coherence-conflicting-negative-trait-impl.stderr b/src/test/ui/coherence/coherence-conflicting-negative-trait-impl.stderr index 4d9f815c79581..5295170cd8bf3 100644 --- a/src/test/ui/coherence/coherence-conflicting-negative-trait-impl.stderr +++ b/src/test/ui/coherence/coherence-conflicting-negative-trait-impl.stderr @@ -7,7 +7,7 @@ LL | LL | impl<T: MyTrait> !Send for TestType<T> {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ negative implementation here -error[E0119]: conflicting implementations of trait `std::marker::Send` for type `TestType<_>`: +error[E0119]: conflicting implementations of trait `std::marker::Send` for type `TestType<_>` --> $DIR/coherence-conflicting-negative-trait-impl.rs:13:1 | LL | unsafe impl<T: MyTrait + 'static> Send for TestType<T> {} diff --git a/src/test/ui/coherence/coherence-cross-crate-conflict.stderr b/src/test/ui/coherence/coherence-cross-crate-conflict.stderr index 827d26ab43443..d0d86c72ffc68 100644 --- a/src/test/ui/coherence/coherence-cross-crate-conflict.stderr +++ b/src/test/ui/coherence/coherence-cross-crate-conflict.stderr @@ -1,4 +1,4 @@ -error[E0119]: conflicting implementations of trait `trait_impl_conflict::Foo` for type `isize`: +error[E0119]: conflicting implementations of trait `trait_impl_conflict::Foo` for type `isize` --> $DIR/coherence-cross-crate-conflict.rs:9:1 | LL | impl<A> Foo for A { diff --git a/src/test/ui/coherence/coherence-fn-covariant-bound-vs-static.stderr b/src/test/ui/coherence/coherence-fn-covariant-bound-vs-static.stderr index 49271edf8e5e4..7dabd97b94e82 100644 --- a/src/test/ui/coherence/coherence-fn-covariant-bound-vs-static.stderr +++ b/src/test/ui/coherence/coherence-fn-covariant-bound-vs-static.stderr @@ -1,4 +1,4 @@ -error[E0119]: conflicting implementations of trait `Trait` for type `for<'r> fn(fn(&'r ()))`: +error[E0119]: conflicting implementations of trait `Trait` for type `for<'r> fn(fn(&'r ()))` --> $DIR/coherence-fn-covariant-bound-vs-static.rs:17:1 | LL | impl Trait for for<'r> fn(fn(&'r ())) {} diff --git a/src/test/ui/coherence/coherence-fn-implied-bounds.stderr b/src/test/ui/coherence/coherence-fn-implied-bounds.stderr index a3e7f0bcde376..c8accc9974791 100644 --- a/src/test/ui/coherence/coherence-fn-implied-bounds.stderr +++ b/src/test/ui/coherence/coherence-fn-implied-bounds.stderr @@ -1,4 +1,4 @@ -error: conflicting implementations of trait `Trait` for type `for<'a, 'b> fn(&'a &'b u32, &'b &'a u32) -> &'b u32`: +error: conflicting implementations of trait `Trait` for type `for<'a, 'b> fn(&'a &'b u32, &'b &'a u32) -> &'b u32` --> $DIR/coherence-fn-implied-bounds.rs:21:1 | LL | impl Trait for for<'a, 'b> fn(&'a &'b u32, &'b &'a u32) -> &'b u32 {} diff --git a/src/test/ui/coherence/coherence-fn-inputs.stderr b/src/test/ui/coherence/coherence-fn-inputs.stderr index 56ab873a39320..82bd8a35f4575 100644 --- a/src/test/ui/coherence/coherence-fn-inputs.stderr +++ b/src/test/ui/coherence/coherence-fn-inputs.stderr @@ -1,4 +1,4 @@ -error[E0119]: conflicting implementations of trait `Trait` for type `for<'a, 'b> fn(&'a u32, &'b u32)`: +error[E0119]: conflicting implementations of trait `Trait` for type `for<'a, 'b> fn(&'a u32, &'b u32)` --> $DIR/coherence-fn-inputs.rs:15:1 | LL | impl Trait for for<'a, 'b> fn(&'a u32, &'b u32) {} diff --git a/src/test/ui/coherence/coherence-free-vs-bound-region.stderr b/src/test/ui/coherence/coherence-free-vs-bound-region.stderr index 97aa491272143..c249fa43c3b60 100644 --- a/src/test/ui/coherence/coherence-free-vs-bound-region.stderr +++ b/src/test/ui/coherence/coherence-free-vs-bound-region.stderr @@ -1,4 +1,4 @@ -error: conflicting implementations of trait `TheTrait` for type `fn(&u8)`: +error: conflicting implementations of trait `TheTrait` for type `fn(&u8)` --> $DIR/coherence-free-vs-bound-region.rs:16:1 | LL | impl<'a> TheTrait for fn(&'a u8) {} diff --git a/src/test/ui/coherence/coherence-impls-copy.stderr b/src/test/ui/coherence/coherence-impls-copy.stderr index 8cc24f099e382..2ac0706d72e57 100644 --- a/src/test/ui/coherence/coherence-impls-copy.stderr +++ b/src/test/ui/coherence/coherence-impls-copy.stderr @@ -1,4 +1,4 @@ -error[E0119]: conflicting implementations of trait `std::marker::Copy` for type `i32`: +error[E0119]: conflicting implementations of trait `std::marker::Copy` for type `i32` --> $DIR/coherence-impls-copy.rs:5:1 | LL | impl Copy for i32 {} @@ -7,7 +7,7 @@ LL | impl Copy for i32 {} = note: conflicting implementation in crate `core`: - impl Copy for i32; -error[E0119]: conflicting implementations of trait `std::marker::Copy` for type `&NotSync`: +error[E0119]: conflicting implementations of trait `std::marker::Copy` for type `&NotSync` --> $DIR/coherence-impls-copy.rs:29:1 | LL | impl Copy for &'static NotSync {} @@ -17,7 +17,7 @@ LL | impl Copy for &'static NotSync {} - impl<T> Copy for &T where T: ?Sized; -error[E0119]: conflicting implementations of trait `std::marker::Copy` for type `&[NotSync]`: +error[E0119]: conflicting implementations of trait `std::marker::Copy` for type `&[NotSync]` --> $DIR/coherence-impls-copy.rs:34:1 | LL | impl Copy for &'static [NotSync] {} diff --git a/src/test/ui/coherence/coherence-impls-send.stderr b/src/test/ui/coherence/coherence-impls-send.stderr index edca31b5daee9..46e9e7e986c34 100644 --- a/src/test/ui/coherence/coherence-impls-send.stderr +++ b/src/test/ui/coherence/coherence-impls-send.stderr @@ -1,4 +1,4 @@ -error[E0119]: conflicting implementations of trait `std::marker::Send` for type `&[NotSync]`: +error[E0119]: conflicting implementations of trait `std::marker::Send` for type `&[NotSync]` --> $DIR/coherence-impls-send.rs:25:1 | LL | unsafe impl Send for &'static [NotSync] {} diff --git a/src/test/ui/coherence/coherence-no-direct-lifetime-dispatch.stderr b/src/test/ui/coherence/coherence-no-direct-lifetime-dispatch.stderr index a74ffbb3afd96..8a43ad7b7f0e8 100644 --- a/src/test/ui/coherence/coherence-no-direct-lifetime-dispatch.stderr +++ b/src/test/ui/coherence/coherence-no-direct-lifetime-dispatch.stderr @@ -1,4 +1,4 @@ -error[E0119]: conflicting implementations of trait `MyTrait`: +error[E0119]: conflicting implementations of trait `MyTrait` --> $DIR/coherence-no-direct-lifetime-dispatch.rs:6:1 | LL | impl<T> MyTrait for T {} diff --git a/src/test/ui/coherence/coherence-overlap-all-t-and-tuple.stderr b/src/test/ui/coherence/coherence-overlap-all-t-and-tuple.stderr index 5e8bfbcc3cafa..6a0880334b6e0 100644 --- a/src/test/ui/coherence/coherence-overlap-all-t-and-tuple.stderr +++ b/src/test/ui/coherence/coherence-overlap-all-t-and-tuple.stderr @@ -1,4 +1,4 @@ -error[E0119]: conflicting implementations of trait `From<(_,)>` for type `(_,)`: +error[E0119]: conflicting implementations of trait `From<(_,)>` for type `(_,)` --> $DIR/coherence-overlap-all-t-and-tuple.rs:16:1 | LL | impl <T> From<T> for T { diff --git a/src/test/ui/coherence/coherence-overlap-downstream.stderr b/src/test/ui/coherence/coherence-overlap-downstream.stderr index 6fb398562d6be..9ab099489d9e5 100644 --- a/src/test/ui/coherence/coherence-overlap-downstream.stderr +++ b/src/test/ui/coherence/coherence-overlap-downstream.stderr @@ -1,4 +1,4 @@ -error[E0119]: conflicting implementations of trait `Sweet`: +error[E0119]: conflicting implementations of trait `Sweet` --> $DIR/coherence-overlap-downstream.rs:8:1 | LL | impl<T:Sugar> Sweet for T { } @@ -6,7 +6,7 @@ LL | impl<T:Sugar> Sweet for T { } LL | impl<T:Fruit> Sweet for T { } | ^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation -error[E0119]: conflicting implementations of trait `Foo<_>` for type `i32`: +error[E0119]: conflicting implementations of trait `Foo<_>` for type `i32` --> $DIR/coherence-overlap-downstream.rs:14:1 | LL | impl<X, T> Foo<X> for T where T: Bar<X> {} diff --git a/src/test/ui/coherence/coherence-overlap-issue-23516.stderr b/src/test/ui/coherence/coherence-overlap-issue-23516.stderr index fe4c5cf3490dd..85eb189e10eee 100644 --- a/src/test/ui/coherence/coherence-overlap-issue-23516.stderr +++ b/src/test/ui/coherence/coherence-overlap-issue-23516.stderr @@ -1,4 +1,4 @@ -error[E0119]: conflicting implementations of trait `Sweet` for type `std::boxed::Box<_>`: +error[E0119]: conflicting implementations of trait `Sweet` for type `std::boxed::Box<_>` --> $DIR/coherence-overlap-issue-23516.rs:8:1 | LL | impl<T:Sugar> Sweet for T { } diff --git a/src/test/ui/coherence/coherence-overlap-messages.stderr b/src/test/ui/coherence/coherence-overlap-messages.stderr index 28147f52fa614..5a97296eebd9b 100644 --- a/src/test/ui/coherence/coherence-overlap-messages.stderr +++ b/src/test/ui/coherence/coherence-overlap-messages.stderr @@ -1,4 +1,4 @@ -error[E0119]: conflicting implementations of trait `Foo`: +error[E0119]: conflicting implementations of trait `Foo` --> $DIR/coherence-overlap-messages.rs:4:1 | LL | impl<T> Foo for T {} @@ -6,7 +6,7 @@ LL | impl<T> Foo for T {} LL | impl<U> Foo for U {} | ^^^^^^^^^^^^^^^^^ conflicting implementation -error[E0119]: conflicting implementations of trait `Bar` for type `(u8, u8)`: +error[E0119]: conflicting implementations of trait `Bar` for type `(u8, u8)` --> $DIR/coherence-overlap-messages.rs:11:1 | LL | impl<T> Bar for (T, u8) {} @@ -14,7 +14,7 @@ LL | impl<T> Bar for (T, u8) {} LL | impl<T> Bar for (u8, T) {} | ^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `(u8, u8)` -error[E0119]: conflicting implementations of trait `Baz<u8>` for type `u8`: +error[E0119]: conflicting implementations of trait `Baz<u8>` for type `u8` --> $DIR/coherence-overlap-messages.rs:17:1 | LL | impl<T> Baz<u8> for T {} @@ -22,7 +22,7 @@ LL | impl<T> Baz<u8> for T {} LL | impl<T> Baz<T> for u8 {} | ^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `u8` -error[E0119]: conflicting implementations of trait `Quux<_, _>`: +error[E0119]: conflicting implementations of trait `Quux<_, _>` --> $DIR/coherence-overlap-messages.rs:23:1 | LL | impl<T, U, V> Quux<U, V> for T {} @@ -30,7 +30,7 @@ LL | impl<T, U, V> Quux<U, V> for T {} LL | impl<T, U> Quux<U, U> for T {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation -error[E0119]: conflicting implementations of trait `Quux<_, _>`: +error[E0119]: conflicting implementations of trait `Quux<_, _>` --> $DIR/coherence-overlap-messages.rs:25:1 | LL | impl<T, U, V> Quux<U, V> for T {} diff --git a/src/test/ui/coherence/coherence-overlap-upstream.stderr b/src/test/ui/coherence/coherence-overlap-upstream.stderr index 8d3de9a243e8e..8272c88758607 100644 --- a/src/test/ui/coherence/coherence-overlap-upstream.stderr +++ b/src/test/ui/coherence/coherence-overlap-upstream.stderr @@ -1,4 +1,4 @@ -error[E0119]: conflicting implementations of trait `Foo` for type `i16`: +error[E0119]: conflicting implementations of trait `Foo` for type `i16` --> $DIR/coherence-overlap-upstream.rs:13:1 | LL | impl<T> Foo for T where T: Remote {} diff --git a/src/test/ui/coherence/coherence-projection-conflict-orphan.stderr b/src/test/ui/coherence/coherence-projection-conflict-orphan.stderr index 06a840255bd9a..51f6faab3c7e4 100644 --- a/src/test/ui/coherence/coherence-projection-conflict-orphan.stderr +++ b/src/test/ui/coherence/coherence-projection-conflict-orphan.stderr @@ -1,4 +1,4 @@ -error[E0119]: conflicting implementations of trait `Foo<i32>` for type `i32`: +error[E0119]: conflicting implementations of trait `Foo<i32>` for type `i32` --> $DIR/coherence-projection-conflict-orphan.rs:16:1 | LL | impl Foo<i32> for i32 { } diff --git a/src/test/ui/coherence/coherence-projection-conflict-ty-param.stderr b/src/test/ui/coherence/coherence-projection-conflict-ty-param.stderr index c5c9b0ac33c2a..85d3d358f83cf 100644 --- a/src/test/ui/coherence/coherence-projection-conflict-ty-param.stderr +++ b/src/test/ui/coherence/coherence-projection-conflict-ty-param.stderr @@ -1,4 +1,4 @@ -error[E0119]: conflicting implementations of trait `Foo<_>` for type `std::option::Option<_>`: +error[E0119]: conflicting implementations of trait `Foo<_>` for type `std::option::Option<_>` --> $DIR/coherence-projection-conflict-ty-param.rs:10:1 | LL | impl <P, T: Foo<P>> Foo<P> for Option<T> {} diff --git a/src/test/ui/coherence/coherence-projection-conflict.stderr b/src/test/ui/coherence/coherence-projection-conflict.stderr index aed6910b52909..e7d1fb2934661 100644 --- a/src/test/ui/coherence/coherence-projection-conflict.stderr +++ b/src/test/ui/coherence/coherence-projection-conflict.stderr @@ -1,4 +1,4 @@ -error[E0119]: conflicting implementations of trait `Foo<i32>` for type `i32`: +error[E0119]: conflicting implementations of trait `Foo<i32>` for type `i32` --> $DIR/coherence-projection-conflict.rs:11:1 | LL | impl Foo<i32> for i32 { } diff --git a/src/test/ui/coherence/coherence-subtyping.stderr b/src/test/ui/coherence/coherence-subtyping.stderr index 7f751a24c75c9..6f95f0a06b5fa 100644 --- a/src/test/ui/coherence/coherence-subtyping.stderr +++ b/src/test/ui/coherence/coherence-subtyping.stderr @@ -1,4 +1,4 @@ -warning: conflicting implementations of trait `TheTrait` for type `for<'a, 'b> fn(&'a u8, &'b u8) -> &'a u8`: +warning: conflicting implementations of trait `TheTrait` for type `for<'a, 'b> fn(&'a u8, &'b u8) -> &'a u8` --> $DIR/coherence-subtyping.rs:15:1 | LL | impl TheTrait for for<'a, 'b> fn(&'a u8, &'b u8) -> &'a u8 {} diff --git a/src/test/ui/coherence/coherence-tuple-conflict.stderr b/src/test/ui/coherence/coherence-tuple-conflict.stderr index f6c2bc32aa870..09ad5e5b22435 100644 --- a/src/test/ui/coherence/coherence-tuple-conflict.stderr +++ b/src/test/ui/coherence/coherence-tuple-conflict.stderr @@ -1,4 +1,4 @@ -error[E0119]: conflicting implementations of trait `MyTrait` for type `(_, _)`: +error[E0119]: conflicting implementations of trait `MyTrait` for type `(_, _)` --> $DIR/coherence-tuple-conflict.rs:15:1 | LL | impl<T> MyTrait for (T,T) { diff --git a/src/test/ui/coherence/coherence-wasm-bindgen.stderr b/src/test/ui/coherence/coherence-wasm-bindgen.stderr index c77483bb847f5..432646e5321eb 100644 --- a/src/test/ui/coherence/coherence-wasm-bindgen.stderr +++ b/src/test/ui/coherence/coherence-wasm-bindgen.stderr @@ -1,4 +1,4 @@ -error: conflicting implementations of trait `IntoWasmAbi` for type `&dyn std::ops::Fn(&_) -> _`: +error: conflicting implementations of trait `IntoWasmAbi` for type `&dyn std::ops::Fn(&_) -> _` --> $DIR/coherence-wasm-bindgen.rs:28:1 | LL | / impl<'a, 'b, A, R> IntoWasmAbi for &'a (dyn Fn(A) -> R + 'b) diff --git a/src/test/ui/coherence/coherence_copy_like_err_fundamental_struct_tuple.stderr b/src/test/ui/coherence/coherence_copy_like_err_fundamental_struct_tuple.stderr index cf6c6fb8c7a9d..db730650185e3 100644 --- a/src/test/ui/coherence/coherence_copy_like_err_fundamental_struct_tuple.stderr +++ b/src/test/ui/coherence/coherence_copy_like_err_fundamental_struct_tuple.stderr @@ -1,4 +1,4 @@ -error[E0119]: conflicting implementations of trait `MyTrait` for type `lib::MyFundamentalStruct<(MyType,)>`: +error[E0119]: conflicting implementations of trait `MyTrait` for type `lib::MyFundamentalStruct<(MyType,)>` --> $DIR/coherence_copy_like_err_fundamental_struct_tuple.rs:16:1 | LL | impl<T: lib::MyCopy> MyTrait for T { } diff --git a/src/test/ui/coherence/coherence_copy_like_err_struct.stderr b/src/test/ui/coherence/coherence_copy_like_err_struct.stderr index cf79e851bf4ab..3bc3dffda5d1b 100644 --- a/src/test/ui/coherence/coherence_copy_like_err_struct.stderr +++ b/src/test/ui/coherence/coherence_copy_like_err_struct.stderr @@ -1,4 +1,4 @@ -error[E0119]: conflicting implementations of trait `MyTrait` for type `lib::MyStruct<MyType>`: +error[E0119]: conflicting implementations of trait `MyTrait` for type `lib::MyStruct<MyType>` --> $DIR/coherence_copy_like_err_struct.rs:19:1 | LL | impl<T: lib::MyCopy> MyTrait for T { } diff --git a/src/test/ui/coherence/coherence_copy_like_err_tuple.stderr b/src/test/ui/coherence/coherence_copy_like_err_tuple.stderr index 52f66427dfae4..090497ec18977 100644 --- a/src/test/ui/coherence/coherence_copy_like_err_tuple.stderr +++ b/src/test/ui/coherence/coherence_copy_like_err_tuple.stderr @@ -1,4 +1,4 @@ -error[E0119]: conflicting implementations of trait `MyTrait` for type `(MyType,)`: +error[E0119]: conflicting implementations of trait `MyTrait` for type `(MyType,)` --> $DIR/coherence_copy_like_err_tuple.rs:18:1 | LL | impl<T: lib::MyCopy> MyTrait for T { } diff --git a/src/test/ui/command-line-diagnostics.stderr b/src/test/ui/command-line-diagnostics.stderr index b3f8d8a643fb5..6223ad880d695 100644 --- a/src/test/ui/command-line-diagnostics.stderr +++ b/src/test/ui/command-line-diagnostics.stderr @@ -5,7 +5,7 @@ LL | let x = 42; | - | | | first assignment to `x` - | help: make this binding mutable: `mut x` + | help: consider making this binding mutable: `mut x` LL | x = 43; | ^^^^^^ cannot assign twice to immutable variable diff --git a/src/test/ui/command/command-pre-exec.rs b/src/test/ui/command/command-pre-exec.rs index 8fc6a220331f0..819ed0b2ddef7 100644 --- a/src/test/ui/command/command-pre-exec.rs +++ b/src/test/ui/command/command-pre-exec.rs @@ -43,20 +43,6 @@ fn main() { assert!(output.stderr.is_empty()); assert_eq!(output.stdout, b"hello\nhello2\n"); - let output = unsafe { - Command::new(&me) - .arg("test2") - .pre_exec(|| { - env::set_var("FOO", "BAR"); - Ok(()) - }) - .output() - .unwrap() - }; - assert!(output.status.success()); - assert!(output.stderr.is_empty()); - assert!(output.stdout.is_empty()); - let output = unsafe { Command::new(&me) .arg("test3") diff --git a/src/test/ui/command/command-setgroups.rs b/src/test/ui/command/command-setgroups.rs index 2067314f740d1..aff67f91bba59 100644 --- a/src/test/ui/command/command-setgroups.rs +++ b/src/test/ui/command/command-setgroups.rs @@ -1,6 +1,5 @@ // run-pass // ignore-windows - this is a unix-specific test -// ignore-cloudabi // ignore-emscripten // ignore-sgx // ignore-musl - returns dummy result for _SC_NGROUPS_MAX diff --git a/src/test/ui/commandline-argfile-missing.rs b/src/test/ui/commandline-argfile-missing.rs index 020c3ff3c7e63..5a6465bd06469 100644 --- a/src/test/ui/commandline-argfile-missing.rs +++ b/src/test/ui/commandline-argfile-missing.rs @@ -1,6 +1,5 @@ // Check to see if we can get parameters from an @argsfile file // -// ignore-tidy-linelength // normalize-stderr-test: "os error \d+" -> "os error $$ERR" // normalize-stderr-test: "commandline-argfile-missing.args:[^(]*" -> "commandline-argfile-missing.args: $$FILE_MISSING " // compile-flags: --cfg cmdline_set @{{src-base}}/commandline-argfile-missing.args diff --git a/src/test/ui/compare-method/bad-self-type.rs b/src/test/ui/compare-method/bad-self-type.rs new file mode 100644 index 0000000000000..f42a9e49abdff --- /dev/null +++ b/src/test/ui/compare-method/bad-self-type.rs @@ -0,0 +1,26 @@ +use std::future::Future; +use std::task::{Context, Poll}; + +fn main() {} + +struct MyFuture {} + +impl Future for MyFuture { + type Output = (); + fn poll(self, _: &mut Context<'_>) -> Poll<()> { + //~^ ERROR method `poll` has an incompatible type for trait + todo!() + } +} + +trait T { + fn foo(self); + fn bar(self) -> Option<()>; +} + +impl T for MyFuture { + fn foo(self: Box<Self>) {} + //~^ ERROR method `foo` has an incompatible type for trait + fn bar(self) {} + //~^ ERROR method `bar` has an incompatible type for trait +} diff --git a/src/test/ui/compare-method/bad-self-type.stderr b/src/test/ui/compare-method/bad-self-type.stderr new file mode 100644 index 0000000000000..76f91fbf241d0 --- /dev/null +++ b/src/test/ui/compare-method/bad-self-type.stderr @@ -0,0 +1,46 @@ +error[E0053]: method `poll` has an incompatible type for trait + --> $DIR/bad-self-type.rs:10:13 + | +LL | fn poll(self, _: &mut Context<'_>) -> Poll<()> { + | ^^^^ + | | + | expected struct `Pin`, found struct `MyFuture` + | help: change the self-receiver type to match the trait: `self: Pin<&mut MyFuture>` + | + = note: expected fn pointer `fn(Pin<&mut MyFuture>, &mut Context<'_>) -> Poll<_>` + found fn pointer `fn(MyFuture, &mut Context<'_>) -> Poll<_>` + +error[E0053]: method `foo` has an incompatible type for trait + --> $DIR/bad-self-type.rs:22:18 + | +LL | fn foo(self); + | ---- type in trait +... +LL | fn foo(self: Box<Self>) {} + | ------^^^^^^^^^ + | | | + | | expected struct `MyFuture`, found struct `Box` + | help: change the self-receiver type to match the trait: `self` + | + = note: expected fn pointer `fn(MyFuture)` + found fn pointer `fn(Box<MyFuture>)` + +error[E0053]: method `bar` has an incompatible type for trait + --> $DIR/bad-self-type.rs:24:18 + | +LL | fn bar(self) -> Option<()>; + | ---------- type in trait +... +LL | fn bar(self) {} + | ^ expected enum `Option`, found `()` + | + = note: expected fn pointer `fn(MyFuture) -> Option<()>` + found fn pointer `fn(MyFuture)` +help: change the output type to match the trait + | +LL | fn bar(self) -> Option<()> {} + | ^^^^^^^^^^^^^ + +error: aborting due to 3 previous errors + +For more information about this error, try `rustc --explain E0053`. diff --git a/src/test/ui/compare-method/reordered-type-param.stderr b/src/test/ui/compare-method/reordered-type-param.stderr index f1f8a663f2120..d581628ea48ad 100644 --- a/src/test/ui/compare-method/reordered-type-param.stderr +++ b/src/test/ui/compare-method/reordered-type-param.stderr @@ -5,8 +5,10 @@ LL | fn b<C:Clone,D>(&self, x: C) -> C; | - type in trait ... LL | fn b<F:Clone,G>(&self, _x: G) -> G { panic!() } - | - - ^ expected type parameter `F`, found type parameter `G` - | | | + | - - ^ + | | | | + | | | expected type parameter `F`, found type parameter `G` + | | | help: change the parameter type to match the trait: `F` | | found type parameter | expected type parameter | diff --git a/src/test/ui/conditional-compilation/cfg-attr-multi-true.stderr b/src/test/ui/conditional-compilation/cfg-attr-multi-true.stderr index 21b3a6f1f33b6..5f278f94b93bd 100644 --- a/src/test/ui/conditional-compilation/cfg-attr-multi-true.stderr +++ b/src/test/ui/conditional-compilation/cfg-attr-multi-true.stderr @@ -10,7 +10,7 @@ warning: use of deprecated struct `MustUseDeprecated` --> $DIR/cfg-attr-multi-true.rs:19:5 | LL | MustUseDeprecated::new(); - | ^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^ warning: use of deprecated struct `MustUseDeprecated` --> $DIR/cfg-attr-multi-true.rs:13:17 diff --git a/src/test/ui/conditional-compilation/inner-cfg-non-inline-mod.rs b/src/test/ui/conditional-compilation/inner-cfg-non-inline-mod.rs new file mode 100644 index 0000000000000..af5a6462e8a75 --- /dev/null +++ b/src/test/ui/conditional-compilation/inner-cfg-non-inline-mod.rs @@ -0,0 +1,7 @@ +// check-pass + +mod module_with_cfg; + +mod module_with_cfg {} // Ok, the module above is configured away by an inner attribute. + +fn main() {} diff --git a/src/test/ui/conditional-compilation/module_with_cfg.rs b/src/test/ui/conditional-compilation/module_with_cfg.rs new file mode 100644 index 0000000000000..56c4baadf22b4 --- /dev/null +++ b/src/test/ui/conditional-compilation/module_with_cfg.rs @@ -0,0 +1,3 @@ +// ignore-test + +#![cfg_attr(all(), cfg(FALSE))] diff --git a/src/test/ui/const-generics/const-argument-cross-crate-mismatch.full.stderr b/src/test/ui/const-generics/const-argument-cross-crate-mismatch.full.stderr index a35c3abc113b9..6ef698bd6a04f 100644 --- a/src/test/ui/const-generics/const-argument-cross-crate-mismatch.full.stderr +++ b/src/test/ui/const-generics/const-argument-cross-crate-mismatch.full.stderr @@ -1,11 +1,11 @@ error[E0308]: mismatched types - --> $DIR/const-argument-cross-crate-mismatch.rs:7:67 + --> $DIR/const-argument-cross-crate-mismatch.rs:9:67 | LL | let _ = const_generic_lib::function(const_generic_lib::Struct([0u8, 1u8])); | ^^^^^^^^^^ expected an array with a fixed size of 3 elements, found one with 2 elements error[E0308]: mismatched types - --> $DIR/const-argument-cross-crate-mismatch.rs:9:65 + --> $DIR/const-argument-cross-crate-mismatch.rs:11:65 | LL | let _: const_generic_lib::Alias = const_generic_lib::Struct([0u8, 1u8, 2u8]); | ^^^^^^^^^^^^^^^ expected an array with a fixed size of 2 elements, found one with 3 elements diff --git a/src/test/ui/const-generics/const-argument-cross-crate-mismatch.min.stderr b/src/test/ui/const-generics/const-argument-cross-crate-mismatch.min.stderr index a35c3abc113b9..6ef698bd6a04f 100644 --- a/src/test/ui/const-generics/const-argument-cross-crate-mismatch.min.stderr +++ b/src/test/ui/const-generics/const-argument-cross-crate-mismatch.min.stderr @@ -1,11 +1,11 @@ error[E0308]: mismatched types - --> $DIR/const-argument-cross-crate-mismatch.rs:7:67 + --> $DIR/const-argument-cross-crate-mismatch.rs:9:67 | LL | let _ = const_generic_lib::function(const_generic_lib::Struct([0u8, 1u8])); | ^^^^^^^^^^ expected an array with a fixed size of 3 elements, found one with 2 elements error[E0308]: mismatched types - --> $DIR/const-argument-cross-crate-mismatch.rs:9:65 + --> $DIR/const-argument-cross-crate-mismatch.rs:11:65 | LL | let _: const_generic_lib::Alias = const_generic_lib::Struct([0u8, 1u8, 2u8]); | ^^^^^^^^^^^^^^^ expected an array with a fixed size of 2 elements, found one with 3 elements diff --git a/src/test/ui/const-generics/const-argument-cross-crate-mismatch.rs b/src/test/ui/const-generics/const-argument-cross-crate-mismatch.rs index 9ae2ae50ba0ab..a8f533eceaa6d 100644 --- a/src/test/ui/const-generics/const-argument-cross-crate-mismatch.rs +++ b/src/test/ui/const-generics/const-argument-cross-crate-mismatch.rs @@ -1,5 +1,7 @@ // aux-build:const_generic_lib.rs // revisions: full min +#![cfg_attr(full, feature(const_generics))] +#![cfg_attr(full, allow(incomplete_features))] extern crate const_generic_lib; diff --git a/src/test/ui/const-generics/const-argument-if-length.full.stderr b/src/test/ui/const-generics/const-argument-if-length.full.stderr index 5dca01f0dc078..c6088e665a232 100644 --- a/src/test/ui/const-generics/const-argument-if-length.full.stderr +++ b/src/test/ui/const-generics/const-argument-if-length.full.stderr @@ -2,7 +2,7 @@ error[E0277]: the size for values of type `T` cannot be known at compilation tim --> $DIR/const-argument-if-length.rs:7:28 | LL | pub const fn is_zst<T: ?Sized>() -> usize { - | - this type parameter needs to be `Sized` + | - this type parameter needs to be `std::marker::Sized` LL | if std::mem::size_of::<T>() == 0 { | ^ doesn't have a size known at compile-time | @@ -15,7 +15,7 @@ error[E0277]: the size for values of type `T` cannot be known at compilation tim --> $DIR/const-argument-if-length.rs:16:12 | LL | pub struct AtLeastByte<T: ?Sized> { - | - this type parameter needs to be `Sized` + | - this type parameter needs to be `std::marker::Sized` LL | value: T, | ^ doesn't have a size known at compile-time | diff --git a/src/test/ui/const-generics/const-argument-if-length.min.stderr b/src/test/ui/const-generics/const-argument-if-length.min.stderr index ea177c1974619..bc06e8d7fb123 100644 --- a/src/test/ui/const-generics/const-argument-if-length.min.stderr +++ b/src/test/ui/const-generics/const-argument-if-length.min.stderr @@ -11,7 +11,7 @@ error[E0277]: the size for values of type `T` cannot be known at compilation tim --> $DIR/const-argument-if-length.rs:16:12 | LL | pub struct AtLeastByte<T: ?Sized> { - | - this type parameter needs to be `Sized` + | - this type parameter needs to be `std::marker::Sized` LL | value: T, | ^ doesn't have a size known at compile-time | diff --git a/src/test/ui/const-generics/const_evaluatable_checked/cross_crate_predicate.stderr b/src/test/ui/const-generics/const_evaluatable_checked/cross_crate_predicate.stderr index 1beb5315d1055..84b2665d5bfed 100644 --- a/src/test/ui/const-generics/const_evaluatable_checked/cross_crate_predicate.stderr +++ b/src/test/ui/const-generics/const_evaluatable_checked/cross_crate_predicate.stderr @@ -3,48 +3,52 @@ error: unconstrained generic constant | LL | let _ = const_evaluatable_lib::test1::<T>(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | -help: try adding a `where` bound using this expression: `where [u8; std::mem::size_of::<T>() - 1]: Sized` - --> $DIR/auxiliary/const_evaluatable_lib.rs:6:10 + | + ::: $DIR/auxiliary/const_evaluatable_lib.rs:6:10 | LL | [u8; std::mem::size_of::<T>() - 1]: Sized, - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ---------------------------- required by this bound in `test1` + | + = help: try adding a `where` bound using this expression: `where [(); std::mem::size_of::<T>() - 1]:` error: unconstrained generic constant --> $DIR/cross_crate_predicate.rs:7:13 | LL | let _ = const_evaluatable_lib::test1::<T>(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | -help: try adding a `where` bound using this expression: `where [u8; std::mem::size_of::<T>() - 1]: Sized` - --> $DIR/auxiliary/const_evaluatable_lib.rs:4:27 + | + ::: $DIR/auxiliary/const_evaluatable_lib.rs:4:27 | LL | pub fn test1<T>() -> [u8; std::mem::size_of::<T>() - 1] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ---------------------------- required by this bound in `test1` + | + = help: try adding a `where` bound using this expression: `where [(); std::mem::size_of::<T>() - 1]:` error: unconstrained generic constant --> $DIR/cross_crate_predicate.rs:7:13 | LL | let _ = const_evaluatable_lib::test1::<T>(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | -help: try adding a `where` bound using this expression: `where [u8; std::mem::size_of::<T>() - 1]: Sized` - --> $DIR/auxiliary/const_evaluatable_lib.rs:6:10 + | + ::: $DIR/auxiliary/const_evaluatable_lib.rs:6:10 | LL | [u8; std::mem::size_of::<T>() - 1]: Sized, - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ---------------------------- required by this bound in `test1` + | + = help: try adding a `where` bound using this expression: `where [(); std::mem::size_of::<T>() - 1]:` error: unconstrained generic constant --> $DIR/cross_crate_predicate.rs:7:13 | LL | let _ = const_evaluatable_lib::test1::<T>(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | -help: try adding a `where` bound using this expression: `where [u8; std::mem::size_of::<T>() - 1]: Sized` - --> $DIR/auxiliary/const_evaluatable_lib.rs:4:27 + | + ::: $DIR/auxiliary/const_evaluatable_lib.rs:4:27 | LL | pub fn test1<T>() -> [u8; std::mem::size_of::<T>() - 1] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ---------------------------- required by this bound in `test1` + | + = help: try adding a `where` bound using this expression: `where [(); std::mem::size_of::<T>() - 1]:` error: aborting due to 4 previous errors diff --git a/src/test/ui/const-generics/const_evaluatable_checked/different-fn.stderr b/src/test/ui/const-generics/const_evaluatable_checked/different-fn.stderr index 8cdc9b57750ee..7c11a47b2f0f9 100644 --- a/src/test/ui/const-generics/const_evaluatable_checked/different-fn.stderr +++ b/src/test/ui/const-generics/const_evaluatable_checked/different-fn.stderr @@ -4,11 +4,7 @@ error: unconstrained generic constant LL | [0; size_of::<Foo<T>>()] | ^^^^^^^^^^^^^^^^^^^ | -help: try adding a `where` bound using this expression: `where [u8; size_of::<Foo<T>>()]: Sized` - --> $DIR/different-fn.rs:10:9 - | -LL | [0; size_of::<Foo<T>>()] - | ^^^^^^^^^^^^^^^^^^^ + = help: try adding a `where` bound using this expression: `where [(); size_of::<Foo<T>>()]:` error: aborting due to previous error diff --git a/src/test/ui/const-generics/const_evaluatable_checked/dont-eagerly-error-in-is-const-evaluatable.rs b/src/test/ui/const-generics/const_evaluatable_checked/dont-eagerly-error-in-is-const-evaluatable.rs new file mode 100644 index 0000000000000..92a410afcb1a9 --- /dev/null +++ b/src/test/ui/const-generics/const_evaluatable_checked/dont-eagerly-error-in-is-const-evaluatable.rs @@ -0,0 +1,17 @@ +// run-pass +#![feature(const_generics)] +#![feature(const_evaluatable_checked)] +#![allow(incomplete_features)] + +// This test is a repro for #82279. It checks that we don't error +// when calling is_const_evaluatable on `std::mem::size_of::<T>()` +// when looking for candidates that may prove `T: Foo` in `foo` + +trait Foo {} + +#[allow(dead_code)] +fn foo<T: Foo>() {} + +impl<T> Foo for T where [(); std::mem::size_of::<T>()]: {} + +fn main() {} diff --git a/src/test/ui/const-generics/defaults/auxiliary/const_defaulty.rs b/src/test/ui/const-generics/defaults/auxiliary/const_defaulty.rs new file mode 100644 index 0000000000000..769b6e952dc9c --- /dev/null +++ b/src/test/ui/const-generics/defaults/auxiliary/const_defaulty.rs @@ -0,0 +1,15 @@ +#![feature(const_generics)] +#![feature(const_generics_defaults)] +#![allow(incomplete_features)] + +pub struct Defaulted<const N: usize=3>; +impl Defaulted { + pub fn new() -> Self { + Defaulted + } +} +impl<const N: usize> Defaulted<N> { + pub fn value(&self) -> usize { + N + } +} diff --git a/src/test/ui/const-generics/defaults/complex-unord-param.rs b/src/test/ui/const-generics/defaults/complex-unord-param.rs index 82b3627d22ff8..d24e403e017ef 100644 --- a/src/test/ui/const-generics/defaults/complex-unord-param.rs +++ b/src/test/ui/const-generics/defaults/complex-unord-param.rs @@ -6,16 +6,16 @@ #![allow(dead_code)] struct NestedArrays<'a, const N: usize, A: 'a, const M: usize, T:'a =u32> { - //[min]~^ ERROR type parameters must be declared prior to const parameters - args: &'a [&'a [T; M]; N], - specifier: A, + //[min]~^ ERROR type parameters must be declared prior to const parameters + args: &'a [&'a [T; M]; N], + specifier: A, } fn main() { - let array = [1, 2, 3]; - let nest = [&array]; - let _ = NestedArrays { - args: &nest, - specifier: true, - }; + let array = [1, 2, 3]; + let nest = [&array]; + let _ = NestedArrays { + args: &nest, + specifier: true, + }; } diff --git a/src/test/ui/const-generics/defaults/const-default.rs b/src/test/ui/const-generics/defaults/const-default.rs new file mode 100644 index 0000000000000..150c70770ae51 --- /dev/null +++ b/src/test/ui/const-generics/defaults/const-default.rs @@ -0,0 +1,30 @@ +// run-pass + +#![feature(const_generics)] +#![feature(const_generics_defaults)] +#![allow(incomplete_features)] + + +pub struct ConstDefault<const N: usize = 3>; + +impl<const N: usize> ConstDefault<N> { + fn foo(self) -> usize { + N + } +} + +impl ConstDefault { + fn new() -> Self { + ConstDefault + } + + fn bar(self) {} +} + +pub fn main() { + let s = ConstDefault::new(); + assert_eq!(s.foo(), 3); + + let w = ConstDefault::<3>; + w.bar(); +} diff --git a/src/test/ui/const-generics/defaults/default-annotation.rs b/src/test/ui/const-generics/defaults/default-annotation.rs new file mode 100644 index 0000000000000..3febb7cffbf15 --- /dev/null +++ b/src/test/ui/const-generics/defaults/default-annotation.rs @@ -0,0 +1,20 @@ +// run-pass +#![feature(staged_api)] + +#![feature(const_generics)] +#![feature(const_generics_defaults)] +#![allow(incomplete_features)] + +#![stable(feature = "const_default_test", since="none")] + + +#[unstable(feature = "const_default_stable", issue="none")] +pub struct ConstDefaultUnstable<const N: usize = 3>; + +#[stable(feature = "const_default_unstable", since="none")] +pub struct ConstDefaultStable<const N: usize = { + #[stable(feature = "const_default_unstable_val", since="none")] + 3 +}>; + +fn main() {} diff --git a/src/test/ui/const-generics/defaults/external.rs b/src/test/ui/const-generics/defaults/external.rs new file mode 100644 index 0000000000000..b39e69ab10b66 --- /dev/null +++ b/src/test/ui/const-generics/defaults/external.rs @@ -0,0 +1,27 @@ +// aux-build:const_defaulty.rs +// check-pass +#![feature(const_generics_defaults)] +#![allow(incomplete_features)] + +extern crate const_defaulty; +use const_defaulty::Defaulted; + +struct Local<const N: usize=4>; +impl Local { + fn new() -> Self { + Local + } +} +impl<const N: usize>Local<N> { + fn value(&self) -> usize { + N + } +} + +fn main() { + let v = Defaulted::new(); + assert_eq!(v.value(), 3); + + let l = Local::new(); + assert_eq!(l.value(), 4); +} diff --git a/src/test/ui/const-generics/defaults/mismatch.rs b/src/test/ui/const-generics/defaults/mismatch.rs new file mode 100644 index 0000000000000..d85b756f538dc --- /dev/null +++ b/src/test/ui/const-generics/defaults/mismatch.rs @@ -0,0 +1,23 @@ +#![feature(const_generics)] +#![feature(const_generics_defaults)] +#![allow(incomplete_features)] + +pub struct Example<const N: usize=13>; +pub struct Example2<T=u32, const N: usize=13>(T); +pub struct Example3<const N: usize=13, T=u32>(T); +pub struct Example4<const N: usize=13, const M: usize=4>; + +fn main() { + let e: Example::<13> = (); + //~^ Error: mismatched types + let e: Example2::<u32, 13> = (); + //~^ Error: mismatched types + let e: Example3::<13, u32> = (); + //~^ Error: mismatched types + let e: Example3::<7> = (); + //~^ Error: mismatched types + // FIXME(const_generics_defaults): There should be a note for the error below, but it is + // missing. + let e: Example4::<7> = (); + //~^ Error: mismatched types +} diff --git a/src/test/ui/const-generics/defaults/mismatch.stderr b/src/test/ui/const-generics/defaults/mismatch.stderr new file mode 100644 index 0000000000000..ff72c71c40f0f --- /dev/null +++ b/src/test/ui/const-generics/defaults/mismatch.stderr @@ -0,0 +1,52 @@ +error[E0308]: mismatched types + --> $DIR/mismatch.rs:11:28 + | +LL | let e: Example::<13> = (); + | ------------- ^^ expected struct `Example`, found `()` + | | + | expected due to this + +error[E0308]: mismatched types + --> $DIR/mismatch.rs:13:34 + | +LL | let e: Example2::<u32, 13> = (); + | ------------------- ^^ expected struct `Example2`, found `()` + | | + | expected due to this + | + = note: expected struct `Example2` + found unit type `()` + +error[E0308]: mismatched types + --> $DIR/mismatch.rs:15:34 + | +LL | let e: Example3::<13, u32> = (); + | ------------------- ^^ expected struct `Example3`, found `()` + | | + | expected due to this + | + = note: expected struct `Example3` + found unit type `()` + +error[E0308]: mismatched types + --> $DIR/mismatch.rs:17:28 + | +LL | let e: Example3::<7> = (); + | ------------- ^^ expected struct `Example3`, found `()` + | | + | expected due to this + | + = note: expected struct `Example3<7_usize>` + found unit type `()` + +error[E0308]: mismatched types + --> $DIR/mismatch.rs:21:28 + | +LL | let e: Example4::<7> = (); + | ------------- ^^ expected struct `Example4`, found `()` + | | + | expected due to this + +error: aborting due to 5 previous errors + +For more information about this error, try `rustc --explain E0308`. diff --git a/src/test/ui/const-generics/defaults/needs-feature.rs b/src/test/ui/const-generics/defaults/needs-feature.rs index 7eb7764a64449..b58dee0712a76 100644 --- a/src/test/ui/const-generics/defaults/needs-feature.rs +++ b/src/test/ui/const-generics/defaults/needs-feature.rs @@ -10,5 +10,5 @@ struct A<const N: usize, T=u32>(T); //[min]~^ ERROR type parameters must be declared prior fn main() { - let _: A<3> = A(0); + let _: A<3> = A(0); } diff --git a/src/test/ui/const-generics/defaults/pretty-printing-ast.rs b/src/test/ui/const-generics/defaults/pretty-printing-ast.rs new file mode 100644 index 0000000000000..a25d4baca1a97 --- /dev/null +++ b/src/test/ui/const-generics/defaults/pretty-printing-ast.rs @@ -0,0 +1,13 @@ +// Test the AST pretty printer correctly handles default values for const generics +// check-pass +// compile-flags: -Z unpretty=expanded + +#![crate_type = "lib"] +#![feature(const_generics_defaults)] +#![allow(incomplete_features)] + +trait Foo<const KIND: bool = true> {} + +fn foo<const SIZE: usize = 5>() {} + +struct Range<const FROM: usize = 0, const LEN: usize = 0, const TO: usize = {FROM + LEN}>; diff --git a/src/test/ui/const-generics/defaults/pretty-printing-ast.stdout b/src/test/ui/const-generics/defaults/pretty-printing-ast.stdout new file mode 100644 index 0000000000000..f7a1d2ca4b2ef --- /dev/null +++ b/src/test/ui/const-generics/defaults/pretty-printing-ast.stdout @@ -0,0 +1,20 @@ +#![feature(prelude_import)] +#![no_std] +// Test the AST pretty printer correctly handles default values for const generics +// check-pass +// compile-flags: -Z unpretty=expanded + +#![crate_type = "lib"] +#![feature(const_generics_defaults)] +#![allow(incomplete_features)] +#[prelude_import] +use ::std::prelude::rust_2015::*; +#[macro_use] +extern crate std; + +trait Foo<const KIND : bool = true> { } + +fn foo<const SIZE : usize = 5>() { } + +struct Range<const FROM : usize = 0, const LEN : usize = 0, const TO : usize = + { FROM + LEN }>; diff --git a/src/test/ui/const-generics/defaults/repr-c-issue-82792.rs b/src/test/ui/const-generics/defaults/repr-c-issue-82792.rs new file mode 100644 index 0000000000000..18ecf46729977 --- /dev/null +++ b/src/test/ui/const-generics/defaults/repr-c-issue-82792.rs @@ -0,0 +1,14 @@ +// Regression test for #82792. + +// run-pass + +#![feature(const_generics_defaults)] +#![allow(incomplete_features)] + +#[repr(C)] +pub struct Loaf<T: Sized, const N: usize = 1usize> { + head: [T; N], + slice: [T], +} + +fn main() {} diff --git a/src/test/ui/const-generics/defaults/simple-defaults.rs b/src/test/ui/const-generics/defaults/simple-defaults.rs index 1f1b6c2260db6..cb66c7769bb23 100644 --- a/src/test/ui/const-generics/defaults/simple-defaults.rs +++ b/src/test/ui/const-generics/defaults/simple-defaults.rs @@ -6,12 +6,12 @@ #![allow(dead_code)] struct FixedOutput<'a, const N: usize, T=u32> { - //[min]~^ ERROR type parameters must be declared prior to const parameters - out: &'a [T; N], + //[min]~^ ERROR type parameters must be declared prior to const parameters + out: &'a [T; N], } trait FixedOutputter { - fn out(&self) -> FixedOutput<'_, 10>; + fn out(&self) -> FixedOutput<'_, 10>; } fn main() {} diff --git a/src/test/ui/const-generics/defaults/wrong-order.full.stderr b/src/test/ui/const-generics/defaults/wrong-order.full.stderr index 96deb4a8b5abb..accc73134d899 100644 --- a/src/test/ui/const-generics/defaults/wrong-order.full.stderr +++ b/src/test/ui/const-generics/defaults/wrong-order.full.stderr @@ -1,4 +1,4 @@ -error: type parameters with a default must be trailing +error: generic parameters with a default must be trailing --> $DIR/wrong-order.rs:4:10 | LL | struct A<T = u32, const N: usize> { diff --git a/src/test/ui/const-generics/defaults/wrong-order.min.stderr b/src/test/ui/const-generics/defaults/wrong-order.min.stderr index b19da76f415d7..c8f1d471b244b 100644 --- a/src/test/ui/const-generics/defaults/wrong-order.min.stderr +++ b/src/test/ui/const-generics/defaults/wrong-order.min.stderr @@ -1,4 +1,4 @@ -error: type parameters with a default must be trailing +error: generic parameters with a default must be trailing --> $DIR/wrong-order.rs:4:10 | LL | struct A<T = u32, const N: usize> { diff --git a/src/test/ui/const-generics/defaults/wrong-order.rs b/src/test/ui/const-generics/defaults/wrong-order.rs index 4f1c05011b0bf..5c2d9b8ad4751 100644 --- a/src/test/ui/const-generics/defaults/wrong-order.rs +++ b/src/test/ui/const-generics/defaults/wrong-order.rs @@ -2,7 +2,7 @@ #![cfg_attr(full, feature(const_generics))] //[full]~WARN the feature `const_generics` is incomplete struct A<T = u32, const N: usize> { - //~^ ERROR type parameters with a default must be trailing + //~^ ERROR generic parameters with a default must be trailing arg: T, } diff --git a/src/test/ui/const-generics/diagnostics.stderr b/src/test/ui/const-generics/diagnostics.stderr index 7d038ff955d6c..c8ee6ad61ec73 100644 --- a/src/test/ui/const-generics/diagnostics.stderr +++ b/src/test/ui/const-generics/diagnostics.stderr @@ -31,9 +31,12 @@ error[E0747]: type provided when a constant was expected --> $DIR/diagnostics.rs:12:19 | LL | impl<N> Foo for B<N> {} - | - ^ - | | - | help: consider changing this type paramater to a `const`-generic: `const N: u8` + | ^ + | +help: consider changing this type parameter to be a `const` generic + | +LL | impl<const N: u8> Foo for B<N> {} + | ^^^^^^^^^^^ error[E0747]: unresolved item provided when a constant was expected --> $DIR/diagnostics.rs:16:32 diff --git a/src/test/ui/const-generics/issues/issue-56445-1.full.stderr b/src/test/ui/const-generics/issues/issue-56445-1.full.stderr new file mode 100644 index 0000000000000..8416d64e1c2de --- /dev/null +++ b/src/test/ui/const-generics/issues/issue-56445-1.full.stderr @@ -0,0 +1,20 @@ +warning: the feature `const_generics` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/issue-56445-1.rs:3:27 + | +LL | #![cfg_attr(full, feature(const_generics))] + | ^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #44580 <https://github.com/rust-lang/rust/issues/44580> for more information + +error[E0771]: use of non-static lifetime `'a` in const generic + --> $DIR/issue-56445-1.rs:8:26 + | +LL | struct Bug<'a, const S: &'a str>(PhantomData<&'a ()>); + | ^^ + | + = note: for more information, see issue #74052 <https://github.com/rust-lang/rust/issues/74052> + +error: aborting due to previous error; 1 warning emitted + +For more information about this error, try `rustc --explain E0771`. diff --git a/src/test/ui/const-generics/issues/issue-56445-1.min.stderr b/src/test/ui/const-generics/issues/issue-56445-1.min.stderr new file mode 100644 index 0000000000000..f7056f27cb37b --- /dev/null +++ b/src/test/ui/const-generics/issues/issue-56445-1.min.stderr @@ -0,0 +1,11 @@ +error[E0771]: use of non-static lifetime `'a` in const generic + --> $DIR/issue-56445-1.rs:8:26 + | +LL | struct Bug<'a, const S: &'a str>(PhantomData<&'a ()>); + | ^^ + | + = note: for more information, see issue #74052 <https://github.com/rust-lang/rust/issues/74052> + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0771`. diff --git a/src/test/ui/const-generics/issues/issue-56445.rs b/src/test/ui/const-generics/issues/issue-56445-1.rs similarity index 100% rename from src/test/ui/const-generics/issues/issue-56445.rs rename to src/test/ui/const-generics/issues/issue-56445-1.rs diff --git a/src/test/ui/const-generics/issues/issue-56445-2.rs b/src/test/ui/const-generics/issues/issue-56445-2.rs new file mode 100644 index 0000000000000..e078c8487c727 --- /dev/null +++ b/src/test/ui/const-generics/issues/issue-56445-2.rs @@ -0,0 +1,11 @@ +// Regression test for https://github.com/rust-lang/rust/issues/56445#issuecomment-502095133 +struct OnDiskDirEntry<'a> { _s: &'a usize } + +impl<'a> OnDiskDirEntry<'a> { + const LFN_FRAGMENT_LEN: usize = 2; + + fn lfn_contents(&self) -> [char; Self::LFN_FRAGMENT_LEN] { loop { } } + //~^ ERROR: generic `Self` types are currently not permitted in anonymous constants +} + +fn main() {} diff --git a/src/test/ui/const-generics/issues/issue-56445-2.stderr b/src/test/ui/const-generics/issues/issue-56445-2.stderr new file mode 100644 index 0000000000000..770c80cbbd305 --- /dev/null +++ b/src/test/ui/const-generics/issues/issue-56445-2.stderr @@ -0,0 +1,14 @@ +error: generic `Self` types are currently not permitted in anonymous constants + --> $DIR/issue-56445-2.rs:7:38 + | +LL | fn lfn_contents(&self) -> [char; Self::LFN_FRAGMENT_LEN] { loop { } } + | ^^^^ + | +note: not a concrete type + --> $DIR/issue-56445-2.rs:4:10 + | +LL | impl<'a> OnDiskDirEntry<'a> { + | ^^^^^^^^^^^^^^^^^^ + +error: aborting due to previous error + diff --git a/src/test/ui/const-generics/issues/issue-56445-3.rs b/src/test/ui/const-generics/issues/issue-56445-3.rs new file mode 100644 index 0000000000000..c29df14586e21 --- /dev/null +++ b/src/test/ui/const-generics/issues/issue-56445-3.rs @@ -0,0 +1,12 @@ +// Regression test for https://github.com/rust-lang/rust/issues/56445#issuecomment-524494170 +pub struct Memory<'rom> { + rom: &'rom [u8], + ram: [u8; Self::SIZE], + //~^ ERROR: generic `Self` types are currently not permitted in anonymous constants +} + +impl<'rom> Memory<'rom> { + pub const SIZE: usize = 0x8000; +} + +fn main() {} diff --git a/src/test/ui/const-generics/issues/issue-56445-3.stderr b/src/test/ui/const-generics/issues/issue-56445-3.stderr new file mode 100644 index 0000000000000..f1c49eecfb547 --- /dev/null +++ b/src/test/ui/const-generics/issues/issue-56445-3.stderr @@ -0,0 +1,8 @@ +error: generic `Self` types are currently not permitted in anonymous constants + --> $DIR/issue-56445-3.rs:4:15 + | +LL | ram: [u8; Self::SIZE], + | ^^^^ + +error: aborting due to previous error + diff --git a/src/test/ui/const-generics/issues/issue-56445.full.stderr b/src/test/ui/const-generics/issues/issue-56445.full.stderr deleted file mode 100644 index 61fba92c1962e..0000000000000 --- a/src/test/ui/const-generics/issues/issue-56445.full.stderr +++ /dev/null @@ -1,20 +0,0 @@ -warning: the feature `const_generics` is incomplete and may not be safe to use and/or cause compiler crashes - --> $DIR/issue-56445.rs:3:27 - | -LL | #![cfg_attr(full, feature(const_generics))] - | ^^^^^^^^^^^^^^ - | - = note: `#[warn(incomplete_features)]` on by default - = note: see issue #44580 <https://github.com/rust-lang/rust/issues/44580> for more information - -error[E0771]: use of non-static lifetime `'a` in const generic - --> $DIR/issue-56445.rs:8:26 - | -LL | struct Bug<'a, const S: &'a str>(PhantomData<&'a ()>); - | ^^ - | - = note: for more information, see issue #74052 <https://github.com/rust-lang/rust/issues/74052> - -error: aborting due to previous error; 1 warning emitted - -For more information about this error, try `rustc --explain E0771`. diff --git a/src/test/ui/const-generics/issues/issue-56445.min.stderr b/src/test/ui/const-generics/issues/issue-56445.min.stderr deleted file mode 100644 index 80702dd4bc33c..0000000000000 --- a/src/test/ui/const-generics/issues/issue-56445.min.stderr +++ /dev/null @@ -1,11 +0,0 @@ -error[E0771]: use of non-static lifetime `'a` in const generic - --> $DIR/issue-56445.rs:8:26 - | -LL | struct Bug<'a, const S: &'a str>(PhantomData<&'a ()>); - | ^^ - | - = note: for more information, see issue #74052 <https://github.com/rust-lang/rust/issues/74052> - -error: aborting due to previous error - -For more information about this error, try `rustc --explain E0771`. diff --git a/src/test/ui/const-generics/issues/issue-61336-2.full.stderr b/src/test/ui/const-generics/issues/issue-61336-2.full.stderr index 9f8e68d211dec..ef0cafcb9bb7c 100644 --- a/src/test/ui/const-generics/issues/issue-61336-2.full.stderr +++ b/src/test/ui/const-generics/issues/issue-61336-2.full.stderr @@ -16,8 +16,8 @@ LL | [x; { N }] = note: the `Copy` trait is required because the repeated element will be copied help: consider restricting type parameter `T` | -LL | fn g<T: Copy, const N: usize>(x: T) -> [T; N] { - | ^^^^^^ +LL | fn g<T: std::marker::Copy, const N: usize>(x: T) -> [T; N] { + | ^^^^^^^^^^^^^^^^^^^ error: aborting due to previous error; 1 warning emitted diff --git a/src/test/ui/const-generics/issues/issue-61336-2.min.stderr b/src/test/ui/const-generics/issues/issue-61336-2.min.stderr index 82d17a87e0af8..e5fe50513aaf7 100644 --- a/src/test/ui/const-generics/issues/issue-61336-2.min.stderr +++ b/src/test/ui/const-generics/issues/issue-61336-2.min.stderr @@ -7,8 +7,8 @@ LL | [x; { N }] = note: the `Copy` trait is required because the repeated element will be copied help: consider restricting type parameter `T` | -LL | fn g<T: Copy, const N: usize>(x: T) -> [T; N] { - | ^^^^^^ +LL | fn g<T: std::marker::Copy, const N: usize>(x: T) -> [T; N] { + | ^^^^^^^^^^^^^^^^^^^ error: aborting due to previous error diff --git a/src/test/ui/const-generics/issues/issue-61336.full.stderr b/src/test/ui/const-generics/issues/issue-61336.full.stderr index 974e2af6fd2cf..fcfd39387c293 100644 --- a/src/test/ui/const-generics/issues/issue-61336.full.stderr +++ b/src/test/ui/const-generics/issues/issue-61336.full.stderr @@ -16,8 +16,8 @@ LL | [x; N] = note: the `Copy` trait is required because the repeated element will be copied help: consider restricting type parameter `T` | -LL | fn g<T: Copy, const N: usize>(x: T) -> [T; N] { - | ^^^^^^ +LL | fn g<T: std::marker::Copy, const N: usize>(x: T) -> [T; N] { + | ^^^^^^^^^^^^^^^^^^^ error: aborting due to previous error; 1 warning emitted diff --git a/src/test/ui/const-generics/issues/issue-61336.min.stderr b/src/test/ui/const-generics/issues/issue-61336.min.stderr index 19c7153582cb9..91580313e1e83 100644 --- a/src/test/ui/const-generics/issues/issue-61336.min.stderr +++ b/src/test/ui/const-generics/issues/issue-61336.min.stderr @@ -7,8 +7,8 @@ LL | [x; N] = note: the `Copy` trait is required because the repeated element will be copied help: consider restricting type parameter `T` | -LL | fn g<T: Copy, const N: usize>(x: T) -> [T; N] { - | ^^^^^^ +LL | fn g<T: std::marker::Copy, const N: usize>(x: T) -> [T; N] { + | ^^^^^^^^^^^^^^^^^^^ error: aborting due to previous error diff --git a/src/test/ui/const-generics/issues/issue-64494.min.stderr b/src/test/ui/const-generics/issues/issue-64494.min.stderr index 936ab7f6e7e33..846db0c91b6e7 100644 --- a/src/test/ui/const-generics/issues/issue-64494.min.stderr +++ b/src/test/ui/const-generics/issues/issue-64494.min.stderr @@ -16,7 +16,7 @@ LL | impl<T: Foo> MyTrait for T where Is<{T::VAL == 6}>: True {} = note: type parameters may not be used in const expressions = help: use `#![feature(const_generics)]` and `#![feature(const_evaluatable_checked)]` to allow generic const expressions -error[E0119]: conflicting implementations of trait `MyTrait`: +error[E0119]: conflicting implementations of trait `MyTrait` --> $DIR/issue-64494.rs:18:1 | LL | impl<T: Foo> MyTrait for T where Is<{T::VAL == 5}>: True {} diff --git a/src/test/ui/const-generics/issues/issue-69654-run-pass.rs b/src/test/ui/const-generics/issues/issue-69654-run-pass.rs index bbfd2183b06e3..45318ca68fcc5 100644 --- a/src/test/ui/const-generics/issues/issue-69654-run-pass.rs +++ b/src/test/ui/const-generics/issues/issue-69654-run-pass.rs @@ -1,4 +1,3 @@ -// run-pass #![feature(const_generics)] #![allow(incomplete_features, unused_braces)] @@ -15,4 +14,5 @@ where fn main() { Foo::foo(); + //~^ ERROR the function or associated item } diff --git a/src/test/ui/const-generics/issues/issue-69654-run-pass.stderr b/src/test/ui/const-generics/issues/issue-69654-run-pass.stderr new file mode 100644 index 0000000000000..a82a60696b36b --- /dev/null +++ b/src/test/ui/const-generics/issues/issue-69654-run-pass.stderr @@ -0,0 +1,15 @@ +error[E0599]: the function or associated item `foo` exists for struct `Foo<{_: usize}>`, but its trait bounds were not satisfied + --> $DIR/issue-69654-run-pass.rs:16:10 + | +LL | struct Foo<const N: usize> {} + | -------------------------- function or associated item `foo` not found for this +... +LL | Foo::foo(); + | ^^^ function or associated item cannot be called on `Foo<{_: usize}>` due to unsatisfied trait bounds + | + = note: the following trait bounds were not satisfied: + `[u8; _]: Bar<[(); _]>` + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0599`. diff --git a/src/test/ui/const-generics/issues/issue-69654.rs b/src/test/ui/const-generics/issues/issue-69654.rs index 7e775999ebd10..b1214b12a1440 100644 --- a/src/test/ui/const-generics/issues/issue-69654.rs +++ b/src/test/ui/const-generics/issues/issue-69654.rs @@ -15,4 +15,5 @@ where fn main() { Foo::foo(); + //~^ ERROR the function or associated item } diff --git a/src/test/ui/const-generics/issues/issue-69654.stderr b/src/test/ui/const-generics/issues/issue-69654.stderr index 70af7bf25d849..0ce7640f68577 100644 --- a/src/test/ui/const-generics/issues/issue-69654.stderr +++ b/src/test/ui/const-generics/issues/issue-69654.stderr @@ -4,6 +4,19 @@ error[E0423]: expected value, found type parameter `T` LL | impl<T> Bar<T> for [u8; T] {} | ^ not a value -error: aborting due to previous error +error[E0599]: the function or associated item `foo` exists for struct `Foo<{_: usize}>`, but its trait bounds were not satisfied + --> $DIR/issue-69654.rs:17:10 + | +LL | struct Foo<const N: usize> {} + | -------------------------- function or associated item `foo` not found for this +... +LL | Foo::foo(); + | ^^^ function or associated item cannot be called on `Foo<{_: usize}>` due to unsatisfied trait bounds + | + = note: the following trait bounds were not satisfied: + `[u8; _]: Bar<[(); _]>` + +error: aborting due to 2 previous errors -For more information about this error, try `rustc --explain E0423`. +Some errors have detailed explanations: E0423, E0599. +For more information about an error, try `rustc --explain E0423`. diff --git a/src/test/ui/const-generics/issues/issue-75299.rs b/src/test/ui/const-generics/issues/issue-75299.rs index 23f30a1eea073..9d3f25b3b47f8 100644 --- a/src/test/ui/const-generics/issues/issue-75299.rs +++ b/src/test/ui/const-generics/issues/issue-75299.rs @@ -1,4 +1,4 @@ -// compile-flags: -Zmir-opt-level=3 +// compile-flags: -Zmir-opt-level=4 // run-pass #![feature(const_generics)] diff --git a/src/test/ui/const-generics/min_const_generics/const_default_first.rs b/src/test/ui/const-generics/min_const_generics/const_default_first.rs new file mode 100644 index 0000000000000..ae82c85eb7e4b --- /dev/null +++ b/src/test/ui/const-generics/min_const_generics/const_default_first.rs @@ -0,0 +1,9 @@ +#![crate_type = "lib"] +#![feature(const_generics)] +#![feature(const_generics_defaults)] +#![allow(incomplete_features, dead_code)] + +struct Both<const N: usize=3, T> { +//~^ ERROR: generic parameters with a default must be + v: T +} diff --git a/src/test/ui/const-generics/min_const_generics/const_default_first.stderr b/src/test/ui/const-generics/min_const_generics/const_default_first.stderr new file mode 100644 index 0000000000000..f7a2e484fc61b --- /dev/null +++ b/src/test/ui/const-generics/min_const_generics/const_default_first.stderr @@ -0,0 +1,8 @@ +error: generic parameters with a default must be trailing + --> $DIR/const_default_first.rs:6:19 + | +LL | struct Both<const N: usize=3, T> { + | ^ + +error: aborting due to previous error + diff --git a/src/test/ui/const-generics/min_const_generics/default_function_param.rs b/src/test/ui/const-generics/min_const_generics/default_function_param.rs index 5b0a42a45565c..ebdb5a65bc351 100644 --- a/src/test/ui/const-generics/min_const_generics/default_function_param.rs +++ b/src/test/ui/const-generics/min_const_generics/default_function_param.rs @@ -1,4 +1,6 @@ -fn foo<const SIZE: usize = 5>() {} -//~^ ERROR default values for const generic parameters are experimental +#![crate_type = "lib"] +#![feature(const_generics_defaults)] +#![allow(incomplete_features)] -fn main() {} +fn foo<const SIZE: usize = 5usize>() {} +//~^ ERROR defaults for const parameters are diff --git a/src/test/ui/const-generics/min_const_generics/default_function_param.stderr b/src/test/ui/const-generics/min_const_generics/default_function_param.stderr index 31b5ad5123ed2..243ac0db5f55f 100644 --- a/src/test/ui/const-generics/min_const_generics/default_function_param.stderr +++ b/src/test/ui/const-generics/min_const_generics/default_function_param.stderr @@ -1,12 +1,8 @@ -error[E0658]: default values for const generic parameters are experimental - --> $DIR/default_function_param.rs:1:26 +error: defaults for const parameters are only allowed in `struct`, `enum`, `type`, or `trait` definitions + --> $DIR/default_function_param.rs:5:14 | -LL | fn foo<const SIZE: usize = 5>() {} - | ^^^ - | - = note: see issue #44580 <https://github.com/rust-lang/rust/issues/44580> for more information - = help: add `#![feature(const_generics_defaults)]` to the crate attributes to enable +LL | fn foo<const SIZE: usize = 5usize>() {} + | ^^^^ error: aborting due to previous error -For more information about this error, try `rustc --explain E0658`. diff --git a/src/test/ui/const-generics/min_const_generics/invalid-patterns.32bit.stderr b/src/test/ui/const-generics/min_const_generics/invalid-patterns.32bit.stderr new file mode 100644 index 0000000000000..415a53a56274a --- /dev/null +++ b/src/test/ui/const-generics/min_const_generics/invalid-patterns.32bit.stderr @@ -0,0 +1,72 @@ +error[E0308]: mismatched types + --> $DIR/invalid-patterns.rs:29:21 + | +LL | get_flag::<false, 0xFF>(); + | ^^^^ expected `char`, found `u8` + +error[E0308]: mismatched types + --> $DIR/invalid-patterns.rs:31:14 + | +LL | get_flag::<7, 'c'>(); + | ^ expected `bool`, found integer + +error[E0308]: mismatched types + --> $DIR/invalid-patterns.rs:33:14 + | +LL | get_flag::<42, 0x5ad>(); + | ^^ expected `bool`, found integer + +error[E0308]: mismatched types + --> $DIR/invalid-patterns.rs:33:18 + | +LL | get_flag::<42, 0x5ad>(); + | ^^^^^ expected `char`, found `u8` + +error[E0080]: it is undefined behavior to use this value + --> $DIR/invalid-patterns.rs:38:21 + | +LL | get_flag::<false, { unsafe { char_raw.character } }>(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered uninitialized bytes, but expected a valid unicode scalar value (in `0..=0x10FFFF` but not in `0xD800..=0xDFFF`) + | + = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the raw bytes of the constant (size: 4, align: 4) { + __ __ __ __ │ ░░░░ + } + +error[E0080]: it is undefined behavior to use this value + --> $DIR/invalid-patterns.rs:40:14 + | +LL | get_flag::<{ unsafe { bool_raw.boolean } }, 'z'>(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered 0x42, but expected a boolean + | + = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the raw bytes of the constant (size: 1, align: 1) { + 42 │ B + } + +error[E0080]: it is undefined behavior to use this value + --> $DIR/invalid-patterns.rs:42:14 + | +LL | get_flag::<{ unsafe { bool_raw.boolean } }, { unsafe { char_raw.character } }>(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered 0x42, but expected a boolean + | + = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the raw bytes of the constant (size: 1, align: 1) { + 42 │ B + } + +error[E0080]: it is undefined behavior to use this value + --> $DIR/invalid-patterns.rs:42:47 + | +LL | get_flag::<{ unsafe { bool_raw.boolean } }, { unsafe { char_raw.character } }>(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered uninitialized bytes, but expected a valid unicode scalar value (in `0..=0x10FFFF` but not in `0xD800..=0xDFFF`) + | + = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the raw bytes of the constant (size: 4, align: 4) { + __ __ __ __ │ ░░░░ + } + +error: aborting due to 8 previous errors + +Some errors have detailed explanations: E0080, E0308. +For more information about an error, try `rustc --explain E0080`. diff --git a/src/test/ui/const-generics/min_const_generics/invalid-patterns.64bit.stderr b/src/test/ui/const-generics/min_const_generics/invalid-patterns.64bit.stderr new file mode 100644 index 0000000000000..415a53a56274a --- /dev/null +++ b/src/test/ui/const-generics/min_const_generics/invalid-patterns.64bit.stderr @@ -0,0 +1,72 @@ +error[E0308]: mismatched types + --> $DIR/invalid-patterns.rs:29:21 + | +LL | get_flag::<false, 0xFF>(); + | ^^^^ expected `char`, found `u8` + +error[E0308]: mismatched types + --> $DIR/invalid-patterns.rs:31:14 + | +LL | get_flag::<7, 'c'>(); + | ^ expected `bool`, found integer + +error[E0308]: mismatched types + --> $DIR/invalid-patterns.rs:33:14 + | +LL | get_flag::<42, 0x5ad>(); + | ^^ expected `bool`, found integer + +error[E0308]: mismatched types + --> $DIR/invalid-patterns.rs:33:18 + | +LL | get_flag::<42, 0x5ad>(); + | ^^^^^ expected `char`, found `u8` + +error[E0080]: it is undefined behavior to use this value + --> $DIR/invalid-patterns.rs:38:21 + | +LL | get_flag::<false, { unsafe { char_raw.character } }>(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered uninitialized bytes, but expected a valid unicode scalar value (in `0..=0x10FFFF` but not in `0xD800..=0xDFFF`) + | + = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the raw bytes of the constant (size: 4, align: 4) { + __ __ __ __ │ ░░░░ + } + +error[E0080]: it is undefined behavior to use this value + --> $DIR/invalid-patterns.rs:40:14 + | +LL | get_flag::<{ unsafe { bool_raw.boolean } }, 'z'>(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered 0x42, but expected a boolean + | + = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the raw bytes of the constant (size: 1, align: 1) { + 42 │ B + } + +error[E0080]: it is undefined behavior to use this value + --> $DIR/invalid-patterns.rs:42:14 + | +LL | get_flag::<{ unsafe { bool_raw.boolean } }, { unsafe { char_raw.character } }>(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered 0x42, but expected a boolean + | + = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the raw bytes of the constant (size: 1, align: 1) { + 42 │ B + } + +error[E0080]: it is undefined behavior to use this value + --> $DIR/invalid-patterns.rs:42:47 + | +LL | get_flag::<{ unsafe { bool_raw.boolean } }, { unsafe { char_raw.character } }>(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered uninitialized bytes, but expected a valid unicode scalar value (in `0..=0x10FFFF` but not in `0xD800..=0xDFFF`) + | + = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the raw bytes of the constant (size: 4, align: 4) { + __ __ __ __ │ ░░░░ + } + +error: aborting due to 8 previous errors + +Some errors have detailed explanations: E0080, E0308. +For more information about an error, try `rustc --explain E0080`. diff --git a/src/test/ui/const-generics/min_const_generics/invalid-patterns.rs b/src/test/ui/const-generics/min_const_generics/invalid-patterns.rs index a120eee67ee40..682e0eced9dff 100644 --- a/src/test/ui/const-generics/min_const_generics/invalid-patterns.rs +++ b/src/test/ui/const-generics/min_const_generics/invalid-patterns.rs @@ -1,3 +1,4 @@ +// stderr-per-bitwidth use std::mem::transmute; fn get_flag<const FlagSet: bool, const ShortName: char>() -> Option<char> { diff --git a/src/test/ui/const-generics/min_const_generics/invalid-patterns.stderr b/src/test/ui/const-generics/min_const_generics/invalid-patterns.stderr deleted file mode 100644 index 04f716fa73351..0000000000000 --- a/src/test/ui/const-generics/min_const_generics/invalid-patterns.stderr +++ /dev/null @@ -1,60 +0,0 @@ -error[E0308]: mismatched types - --> $DIR/invalid-patterns.rs:28:21 - | -LL | get_flag::<false, 0xFF>(); - | ^^^^ expected `char`, found `u8` - -error[E0308]: mismatched types - --> $DIR/invalid-patterns.rs:30:14 - | -LL | get_flag::<7, 'c'>(); - | ^ expected `bool`, found integer - -error[E0308]: mismatched types - --> $DIR/invalid-patterns.rs:32:14 - | -LL | get_flag::<42, 0x5ad>(); - | ^^ expected `bool`, found integer - -error[E0308]: mismatched types - --> $DIR/invalid-patterns.rs:32:18 - | -LL | get_flag::<42, 0x5ad>(); - | ^^^^^ expected `char`, found `u8` - -error[E0080]: it is undefined behavior to use this value - --> $DIR/invalid-patterns.rs:37:21 - | -LL | get_flag::<false, { unsafe { char_raw.character } }>(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered uninitialized bytes, but expected a valid unicode scalar value (in `0..=0x10FFFF` but not in `0xD800..=0xDFFF`) - | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. - -error[E0080]: it is undefined behavior to use this value - --> $DIR/invalid-patterns.rs:39:14 - | -LL | get_flag::<{ unsafe { bool_raw.boolean } }, 'z'>(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered 0x42, but expected a boolean - | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. - -error[E0080]: it is undefined behavior to use this value - --> $DIR/invalid-patterns.rs:41:14 - | -LL | get_flag::<{ unsafe { bool_raw.boolean } }, { unsafe { char_raw.character } }>(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered 0x42, but expected a boolean - | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. - -error[E0080]: it is undefined behavior to use this value - --> $DIR/invalid-patterns.rs:41:47 - | -LL | get_flag::<{ unsafe { bool_raw.boolean } }, { unsafe { char_raw.character } }>(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered uninitialized bytes, but expected a valid unicode scalar value (in `0..=0x10FFFF` but not in `0xD800..=0xDFFF`) - | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. - -error: aborting due to 8 previous errors - -Some errors have detailed explanations: E0080, E0308. -For more information about an error, try `rustc --explain E0080`. diff --git a/src/test/ui/const-generics/min_const_generics/type_and_const_defaults.rs b/src/test/ui/const-generics/min_const_generics/type_and_const_defaults.rs new file mode 100644 index 0000000000000..435a63a528345 --- /dev/null +++ b/src/test/ui/const-generics/min_const_generics/type_and_const_defaults.rs @@ -0,0 +1,27 @@ +// run-pass +#![feature(const_generics)] +#![feature(const_generics_defaults)] +#![allow(incomplete_features, dead_code)] + +struct Both<T=u32, const N: usize=3> { + arr: [T; N] +} + +trait BothTrait<T=u32, const N: usize=3> {} + +enum BothEnum<T=u32, const N: usize=3> { + Dummy([T; N]) +} + +struct OppOrder<const N: usize=3, T=u32> { + arr: [T; N] +} + +fn main() { + let _ = OppOrder::<3, u32> { + arr: [0,0,0], + }; + let _ = Both::<u8, 1> { + arr: [0], + }; +} diff --git a/src/test/ui/const-generics/occurs-check/unused-substs-1.rs b/src/test/ui/const-generics/occurs-check/unused-substs-1.rs index f56687ecd9329..6ded9f13bc4fa 100644 --- a/src/test/ui/const-generics/occurs-check/unused-substs-1.rs +++ b/src/test/ui/const-generics/occurs-check/unused-substs-1.rs @@ -1,4 +1,3 @@ -// build-pass #![feature(const_generics)] #![allow(incomplete_features)] @@ -10,5 +9,5 @@ where A<N>: Bar<N>; fn main() { - let _ = A; + let _ = A; //~ERROR the trait bound } diff --git a/src/test/ui/const-generics/occurs-check/unused-substs-1.stderr b/src/test/ui/const-generics/occurs-check/unused-substs-1.stderr new file mode 100644 index 0000000000000..6830288acc0ad --- /dev/null +++ b/src/test/ui/const-generics/occurs-check/unused-substs-1.stderr @@ -0,0 +1,17 @@ +error[E0277]: the trait bound `A<{_: usize}>: Bar<{_: usize}>` is not satisfied + --> $DIR/unused-substs-1.rs:12:13 + | +LL | / struct A<const N: usize> +LL | | where +LL | | A<N>: Bar<N>; + | |_________________- required by `A` +... +LL | let _ = A; + | ^ the trait `Bar<{_: usize}>` is not implemented for `A<{_: usize}>` + | + = help: the following implementations were found: + <A<7_usize> as Bar<N>> + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0277`. diff --git a/src/test/ui/const-generics/occurs-check/unused-substs-2.rs b/src/test/ui/const-generics/occurs-check/unused-substs-2.rs index 12444ec5312d9..2d00141fbf70b 100644 --- a/src/test/ui/const-generics/occurs-check/unused-substs-2.rs +++ b/src/test/ui/const-generics/occurs-check/unused-substs-2.rs @@ -1,4 +1,3 @@ -// check-pass #![feature(const_generics)] #![allow(incomplete_features)] @@ -24,4 +23,6 @@ fn main() { // `t` is `ty::Infer(TyVar(_#1t))` // `foo` contains `ty::Infer(TyVar(_#1t))` in its substs t = foo; + //~^ ERROR mismatched types + //~| NOTE cyclic type } diff --git a/src/test/ui/const-generics/occurs-check/unused-substs-2.stderr b/src/test/ui/const-generics/occurs-check/unused-substs-2.stderr new file mode 100644 index 0000000000000..9532fc21a31b7 --- /dev/null +++ b/src/test/ui/const-generics/occurs-check/unused-substs-2.stderr @@ -0,0 +1,9 @@ +error[E0308]: mismatched types + --> $DIR/unused-substs-2.rs:25:9 + | +LL | t = foo; + | ^^^ cyclic type of infinite size + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0308`. diff --git a/src/test/ui/const-generics/occurs-check/unused-substs-3.rs b/src/test/ui/const-generics/occurs-check/unused-substs-3.rs index 187e27382fcf2..2e306f8c4c88f 100644 --- a/src/test/ui/const-generics/occurs-check/unused-substs-3.rs +++ b/src/test/ui/const-generics/occurs-check/unused-substs-3.rs @@ -1,4 +1,3 @@ -// check-pass #![feature(const_generics)] #![allow(incomplete_features)] @@ -15,4 +14,6 @@ fn main() { // `t` is `ty::Infer(TyVar(_#1t))` // `foo` contains `ty::Infer(TyVar(_#1t))` in its substs t = foo; + //~^ ERROR mismatched types + //~| NOTE cyclic type } diff --git a/src/test/ui/const-generics/occurs-check/unused-substs-3.stderr b/src/test/ui/const-generics/occurs-check/unused-substs-3.stderr new file mode 100644 index 0000000000000..2551d68f97474 --- /dev/null +++ b/src/test/ui/const-generics/occurs-check/unused-substs-3.stderr @@ -0,0 +1,12 @@ +error[E0308]: mismatched types + --> $DIR/unused-substs-3.rs:16:9 + | +LL | t = foo; + | ^^^ + | | + | cyclic type of infinite size + | help: try using a conversion method: `foo.to_vec()` + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0308`. diff --git a/src/test/ui/const-generics/occurs-check/unused-substs-4.rs b/src/test/ui/const-generics/occurs-check/unused-substs-4.rs index 8e42ceb6d70e9..9c7f5ab91edb1 100644 --- a/src/test/ui/const-generics/occurs-check/unused-substs-4.rs +++ b/src/test/ui/const-generics/occurs-check/unused-substs-4.rs @@ -1,4 +1,3 @@ -// build-pass #![feature(const_generics)] #![allow(incomplete_features)] @@ -8,5 +7,5 @@ fn bind<const N: usize>(value: [u8; N]) -> [u8; 3 + 4] { fn main() { let mut arr = Default::default(); - arr = bind(arr); + arr = bind(arr); //~ ERROR mismatched type } diff --git a/src/test/ui/const-generics/occurs-check/unused-substs-4.stderr b/src/test/ui/const-generics/occurs-check/unused-substs-4.stderr new file mode 100644 index 0000000000000..5685eedbdeca8 --- /dev/null +++ b/src/test/ui/const-generics/occurs-check/unused-substs-4.stderr @@ -0,0 +1,9 @@ +error[E0308]: mismatched types + --> $DIR/unused-substs-4.rs:10:11 + | +LL | arr = bind(arr); + | ^^^^^^^^^ encountered a self-referencing constant + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0308`. diff --git a/src/test/ui/const-generics/params-in-ct-in-ty-param-lazy-norm.full.stderr b/src/test/ui/const-generics/params-in-ct-in-ty-param-lazy-norm.full.stderr index debb272da3608..c2b7b206653a6 100644 --- a/src/test/ui/const-generics/params-in-ct-in-ty-param-lazy-norm.full.stderr +++ b/src/test/ui/const-generics/params-in-ct-in-ty-param-lazy-norm.full.stderr @@ -1,4 +1,4 @@ -error: type parameters with a default must be trailing +error: generic parameters with a default must be trailing --> $DIR/params-in-ct-in-ty-param-lazy-norm.rs:11:12 | LL | struct Bar<T = [u8; N], const N: usize>(T); diff --git a/src/test/ui/const-generics/params-in-ct-in-ty-param-lazy-norm.min.stderr b/src/test/ui/const-generics/params-in-ct-in-ty-param-lazy-norm.min.stderr index 171efca193831..4a462c328bf64 100644 --- a/src/test/ui/const-generics/params-in-ct-in-ty-param-lazy-norm.min.stderr +++ b/src/test/ui/const-generics/params-in-ct-in-ty-param-lazy-norm.min.stderr @@ -1,4 +1,4 @@ -error: type parameters with a default must be trailing +error: generic parameters with a default must be trailing --> $DIR/params-in-ct-in-ty-param-lazy-norm.rs:11:12 | LL | struct Bar<T = [u8; N], const N: usize>(T); diff --git a/src/test/ui/const-generics/params-in-ct-in-ty-param-lazy-norm.rs b/src/test/ui/const-generics/params-in-ct-in-ty-param-lazy-norm.rs index a85e2a2f2c4f1..c7be8bdaf9c3d 100644 --- a/src/test/ui/const-generics/params-in-ct-in-ty-param-lazy-norm.rs +++ b/src/test/ui/const-generics/params-in-ct-in-ty-param-lazy-norm.rs @@ -10,6 +10,6 @@ struct Foo<T, U = [u8; std::mem::size_of::<T>()]>(T, U); // FIXME(const_generics_defaults): We still don't know how to deal with type defaults. struct Bar<T = [u8; N], const N: usize>(T); //~^ ERROR constant values inside of type parameter defaults -//~| ERROR type parameters with a default +//~| ERROR generic parameters with a default fn main() {} diff --git a/src/test/ui/const-generics/type_mismatch.rs b/src/test/ui/const-generics/type_mismatch.rs new file mode 100644 index 0000000000000..4a7534e371381 --- /dev/null +++ b/src/test/ui/const-generics/type_mismatch.rs @@ -0,0 +1,9 @@ +fn foo<const N: usize>() -> [u8; N] { + bar::<N>() //~ ERROR mismatched types +} + +fn bar<const N: u8>() -> [u8; N] {} +//~^ ERROR mismatched types +//~| ERROR mismatched types + +fn main() {} diff --git a/src/test/ui/const-generics/type_mismatch.stderr b/src/test/ui/const-generics/type_mismatch.stderr new file mode 100644 index 0000000000000..f5053e4c8c8d3 --- /dev/null +++ b/src/test/ui/const-generics/type_mismatch.stderr @@ -0,0 +1,23 @@ +error[E0308]: mismatched types + --> $DIR/type_mismatch.rs:2:11 + | +LL | bar::<N>() + | ^ expected `u8`, found `usize` + +error[E0308]: mismatched types + --> $DIR/type_mismatch.rs:5:31 + | +LL | fn bar<const N: u8>() -> [u8; N] {} + | ^ expected `usize`, found `u8` + +error[E0308]: mismatched types + --> $DIR/type_mismatch.rs:5:26 + | +LL | fn bar<const N: u8>() -> [u8; N] {} + | --- ^^^^^^^ expected array `[u8; N]`, found `()` + | | + | implicitly returns `()` as its body has no tail or `return` expression + +error: aborting due to 3 previous errors + +For more information about this error, try `rustc --explain E0308`. diff --git a/src/test/ui/const-generics/type_not_in_scope.rs b/src/test/ui/const-generics/type_not_in_scope.rs new file mode 100644 index 0000000000000..5933701808b3e --- /dev/null +++ b/src/test/ui/const-generics/type_not_in_scope.rs @@ -0,0 +1,11 @@ +impl X { + //~^ ERROR cannot find type + fn getn<const N: usize>() -> [u8; N] { + getn::<N>() + } +} +fn getn<const N: cfg_attr>() -> [u8; N] {} +//~^ ERROR expected type, found built-in attribute `cfg_attr` +//~| ERROR mismatched types + +fn main() {} diff --git a/src/test/ui/const-generics/type_not_in_scope.stderr b/src/test/ui/const-generics/type_not_in_scope.stderr new file mode 100644 index 0000000000000..16796acb3d285 --- /dev/null +++ b/src/test/ui/const-generics/type_not_in_scope.stderr @@ -0,0 +1,24 @@ +error[E0412]: cannot find type `X` in this scope + --> $DIR/type_not_in_scope.rs:1:6 + | +LL | impl X { + | ^ not found in this scope + +error[E0573]: expected type, found built-in attribute `cfg_attr` + --> $DIR/type_not_in_scope.rs:7:18 + | +LL | fn getn<const N: cfg_attr>() -> [u8; N] {} + | ^^^^^^^^ not a type + +error[E0308]: mismatched types + --> $DIR/type_not_in_scope.rs:7:33 + | +LL | fn getn<const N: cfg_attr>() -> [u8; N] {} + | ---- ^^^^^^^ expected array `[u8; N]`, found `()` + | | + | implicitly returns `()` as its body has no tail or `return` expression + +error: aborting due to 3 previous errors + +Some errors have detailed explanations: E0308, E0412, E0573. +For more information about an error, try `rustc --explain E0308`. diff --git a/src/test/ui/const_evaluatable/needs_where_clause.stderr b/src/test/ui/const_evaluatable/needs_where_clause.stderr index 945105d1a2dc6..7b41e39b7d742 100644 --- a/src/test/ui/const_evaluatable/needs_where_clause.stderr +++ b/src/test/ui/const_evaluatable/needs_where_clause.stderr @@ -4,11 +4,7 @@ error: unconstrained generic constant LL | b: [f32; complex_maths::<T>(N)], | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | -help: try adding a `where` bound using this expression: `where [u8; complex_maths::<T>(N)]: Sized` - --> $DIR/needs_where_clause.rs:11:12 - | -LL | b: [f32; complex_maths::<T>(N)], - | ^^^^^^^^^^^^^^^^^^^^^ + = help: try adding a `where` bound using this expression: `where [(); complex_maths::<T>(N)]:` error: aborting due to previous error diff --git a/src/test/ui/const_evaluatable/no_where_clause.stderr b/src/test/ui/const_evaluatable/no_where_clause.stderr index 84a65f0d1d29b..3e5c2f5cad1df 100644 --- a/src/test/ui/const_evaluatable/no_where_clause.stderr +++ b/src/test/ui/const_evaluatable/no_where_clause.stderr @@ -4,11 +4,7 @@ error: unconstrained generic constant LL | b: [f32; complex_maths(N)], | ^^^^^^^^^^^^^^^^^^^^^^^ | -help: try adding a `where` bound using this expression: `where [u8; complex_maths(N)]: Sized` - --> $DIR/no_where_clause.rs:10:12 - | -LL | b: [f32; complex_maths(N)], - | ^^^^^^^^^^^^^^^^ + = help: try adding a `where` bound using this expression: `where [(); complex_maths(N)]:` error: aborting due to previous error diff --git a/src/test/ui/const_prop/inline_spans.rs b/src/test/ui/const_prop/inline_spans.rs index 32902b49d0284..adc7874104ae6 100644 --- a/src/test/ui/const_prop/inline_spans.rs +++ b/src/test/ui/const_prop/inline_spans.rs @@ -1,5 +1,5 @@ // build-fail -// compile-flags: -Zmir-opt-level=2 +// compile-flags: -Zmir-opt-level=3 #![deny(warnings)] diff --git a/src/test/ui/const_prop/inline_spans_lint_attribute.rs b/src/test/ui/const_prop/inline_spans_lint_attribute.rs index 656ff02dc67ef..1db53d77193f2 100644 --- a/src/test/ui/const_prop/inline_spans_lint_attribute.rs +++ b/src/test/ui/const_prop/inline_spans_lint_attribute.rs @@ -1,6 +1,6 @@ // Must be build-pass, because check-pass will not run const prop and thus not emit the lint anyway. // build-pass -// compile-flags: -Zmir-opt-level=2 +// compile-flags: -Zmir-opt-level=3 #![deny(warnings)] diff --git a/src/test/ui/issues/auxiliary/issue-17718-aux.rs b/src/test/ui/consts/auxiliary/issue-17718-aux.rs similarity index 100% rename from src/test/ui/issues/auxiliary/issue-17718-aux.rs rename to src/test/ui/consts/auxiliary/issue-17718-aux.rs diff --git a/src/test/ui/consts/const-blocks/fn-call-in-non-const.stderr b/src/test/ui/consts/const-blocks/fn-call-in-non-const.stderr index 303de078013ac..329c6cb2b1285 100644 --- a/src/test/ui/consts/const-blocks/fn-call-in-non-const.stderr +++ b/src/test/ui/consts/const-blocks/fn-call-in-non-const.stderr @@ -7,8 +7,8 @@ LL | let _: [Option<Bar>; 2] = [no_copy(); 2]; = help: the following implementations were found: <Option<T> as Copy> = note: the `Copy` trait is required because the repeated element will be copied - = help: consider creating a new `const` item and initializing with the result of the function call to be used in the repeat position, like `const VAL: Type = const_fn();` and `let x = [VAL; 42];` - = help: create an inline `const` block, see PR #2920 <https://github.com/rust-lang/rfcs/pull/2920> for more information + = help: consider creating a new `const` item and initializing it with the result of the function call to be used in the repeat position, like `const VAL: Type = const_fn();` and `let x = [VAL; 42];` + = help: create an inline `const` block, see RFC #2920 <https://github.com/rust-lang/rfcs/pull/2920> for more information error: aborting due to previous error diff --git a/src/test/ui/consts/const-err4.32bit.stderr b/src/test/ui/consts/const-err4.32bit.stderr new file mode 100644 index 0000000000000..1dbda8cbcd08d --- /dev/null +++ b/src/test/ui/consts/const-err4.32bit.stderr @@ -0,0 +1,14 @@ +error[E0080]: it is undefined behavior to use this value + --> $DIR/const-err4.rs:9:11 + | +LL | Boo = [unsafe { Foo { b: () }.a }; 4][3], + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered uninitialized bytes, but expected initialized plain (non-pointer) bytes + | + = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the raw bytes of the constant (size: 4, align: 4) { + __ __ __ __ │ ░░░░ + } + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0080`. diff --git a/src/test/ui/consts/const-err4.64bit.stderr b/src/test/ui/consts/const-err4.64bit.stderr new file mode 100644 index 0000000000000..2eea3ea709638 --- /dev/null +++ b/src/test/ui/consts/const-err4.64bit.stderr @@ -0,0 +1,14 @@ +error[E0080]: it is undefined behavior to use this value + --> $DIR/const-err4.rs:9:11 + | +LL | Boo = [unsafe { Foo { b: () }.a }; 4][3], + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered uninitialized bytes, but expected initialized plain (non-pointer) bytes + | + = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the raw bytes of the constant (size: 8, align: 8) { + __ __ __ __ __ __ __ __ │ ░░░░░░░░ + } + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0080`. diff --git a/src/test/ui/consts/const-err4.rs b/src/test/ui/consts/const-err4.rs index 70d9bc149d8d8..f0625faa80179 100644 --- a/src/test/ui/consts/const-err4.rs +++ b/src/test/ui/consts/const-err4.rs @@ -1,3 +1,4 @@ +// stderr-per-bitwidth #[derive(Copy, Clone)] union Foo { a: isize, diff --git a/src/test/ui/consts/const-err4.stderr b/src/test/ui/consts/const-err4.stderr deleted file mode 100644 index 081b09e33006f..0000000000000 --- a/src/test/ui/consts/const-err4.stderr +++ /dev/null @@ -1,11 +0,0 @@ -error[E0080]: it is undefined behavior to use this value - --> $DIR/const-err4.rs:8:11 - | -LL | Boo = [unsafe { Foo { b: () }.a }; 4][3], - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered uninitialized bytes, but expected initialized plain (non-pointer) bytes - | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. - -error: aborting due to previous error - -For more information about this error, try `rustc --explain E0080`. diff --git a/src/test/ui/consts/const-eval/conditional_array_execution.rs b/src/test/ui/consts/const-eval/conditional_array_execution.rs index 9b99a685b6370..bd517e568eae9 100644 --- a/src/test/ui/consts/const-eval/conditional_array_execution.rs +++ b/src/test/ui/consts/const-eval/conditional_array_execution.rs @@ -10,7 +10,7 @@ const FOO: u32 = [X - Y, Y - X][(X < Y) as usize]; fn main() { println!("{}", FOO); - //~^ ERROR + //~^ ERROR evaluation of constant value failed //~| WARN erroneous constant used [const_err] //~| WARN this was previously accepted by the compiler but is being phased out } diff --git a/src/test/ui/consts/const-eval/const-eval-query-stack.stderr b/src/test/ui/consts/const-eval/const-eval-query-stack.stderr index 6a205ce9787f9..e6fecef9fb3b2 100644 --- a/src/test/ui/consts/const-eval/const-eval-query-stack.stderr +++ b/src/test/ui/consts/const-eval/const-eval-query-stack.stderr @@ -20,7 +20,7 @@ error[E0080]: evaluation of constant value failed LL | let x: &'static i32 = &X; | ^ referenced constant has errors query stack during panic: -#0 [normalize_generic_arg_after_erasing_regions] normalizing `main::promoted[1]` +#0 [normalize_mir_const_after_erasing_regions] normalizing `main::promoted[1]` #1 [optimized_mir] optimizing MIR for `main` #2 [collect_and_partition_mono_items] collect_and_partition_mono_items end of query stack diff --git a/src/test/ui/consts/const-eval/const-pointer-values-in-various-types.64bit.stderr b/src/test/ui/consts/const-eval/const-pointer-values-in-various-types.64bit.stderr new file mode 100644 index 0000000000000..187e2760ad2ca --- /dev/null +++ b/src/test/ui/consts/const-eval/const-pointer-values-in-various-types.64bit.stderr @@ -0,0 +1,323 @@ +error[E0080]: it is undefined behavior to use this value + --> $DIR/const-pointer-values-in-various-types.rs:26:5 + | +LL | const I32_REF_USIZE_UNION: usize = unsafe { Nonsense { int_32_ref: &3 }.u }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered pointer to alloc2, but expected initialized plain (non-pointer) bytes + | + = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the raw bytes of the constant (size: 8, align: 8) { + ╾───────alloc2────────╼ │ ╾──────╼ + } + +error: any use of this value will cause an error + --> $DIR/const-pointer-values-in-various-types.rs:29:43 + | +LL | const I32_REF_U8_UNION: u8 = unsafe { Nonsense { int_32_ref: &3 }.uint_8 }; + | --------------------------------------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^--- + | | + | unable to turn pointer into raw bytes + | + = note: `#[deny(const_err)]` on by default + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #71800 <https://github.com/rust-lang/rust/issues/71800> + +error: any use of this value will cause an error + --> $DIR/const-pointer-values-in-various-types.rs:33:45 + | +LL | const I32_REF_U16_UNION: u16 = unsafe { Nonsense { int_32_ref: &3 }.uint_16 }; + | ----------------------------------------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^--- + | | + | unable to turn pointer into raw bytes + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #71800 <https://github.com/rust-lang/rust/issues/71800> + +error: any use of this value will cause an error + --> $DIR/const-pointer-values-in-various-types.rs:37:45 + | +LL | const I32_REF_U32_UNION: u32 = unsafe { Nonsense { int_32_ref: &3 }.uint_32 }; + | ----------------------------------------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^--- + | | + | unable to turn pointer into raw bytes + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #71800 <https://github.com/rust-lang/rust/issues/71800> + +error[E0080]: it is undefined behavior to use this value + --> $DIR/const-pointer-values-in-various-types.rs:41:5 + | +LL | const I32_REF_U64_UNION: u64 = unsafe { Nonsense { int_32_ref: &3 }.uint_64 }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered pointer to alloc18, but expected initialized plain (non-pointer) bytes + | + = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the raw bytes of the constant (size: 8, align: 8) { + ╾───────alloc18───────╼ │ ╾──────╼ + } + +error[E0080]: it is undefined behavior to use this value + --> $DIR/const-pointer-values-in-various-types.rs:44:5 + | +LL | const I32_REF_U128_UNION: u128 = unsafe { Nonsense { int_32_ref: &3 }.uint_128 }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered uninitialized bytes, but expected initialized plain (non-pointer) bytes + | + = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the raw bytes of the constant (size: 16, align: 8) { + __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ │ ░░░░░░░░░░░░░░░░ + } + +error: any use of this value will cause an error + --> $DIR/const-pointer-values-in-various-types.rs:47:43 + | +LL | const I32_REF_I8_UNION: i8 = unsafe { Nonsense { int_32_ref: &3 }.int_8 }; + | --------------------------------------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^--- + | | + | unable to turn pointer into raw bytes + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #71800 <https://github.com/rust-lang/rust/issues/71800> + +error: any use of this value will cause an error + --> $DIR/const-pointer-values-in-various-types.rs:51:45 + | +LL | const I32_REF_I16_UNION: i16 = unsafe { Nonsense { int_32_ref: &3 }.int_16 }; + | ----------------------------------------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^--- + | | + | unable to turn pointer into raw bytes + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #71800 <https://github.com/rust-lang/rust/issues/71800> + +error: any use of this value will cause an error + --> $DIR/const-pointer-values-in-various-types.rs:55:45 + | +LL | const I32_REF_I32_UNION: i32 = unsafe { Nonsense { int_32_ref: &3 }.int_32 }; + | ----------------------------------------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^--- + | | + | unable to turn pointer into raw bytes + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #71800 <https://github.com/rust-lang/rust/issues/71800> + +error[E0080]: it is undefined behavior to use this value + --> $DIR/const-pointer-values-in-various-types.rs:59:5 + | +LL | const I32_REF_I64_UNION: i64 = unsafe { Nonsense { int_32_ref: &3 }.int_64 }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered pointer to alloc38, but expected initialized plain (non-pointer) bytes + | + = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the raw bytes of the constant (size: 8, align: 8) { + ╾───────alloc38───────╼ │ ╾──────╼ + } + +error[E0080]: it is undefined behavior to use this value + --> $DIR/const-pointer-values-in-various-types.rs:62:5 + | +LL | const I32_REF_I128_UNION: i128 = unsafe { Nonsense { int_32_ref: &3 }.int_128 }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered uninitialized bytes, but expected initialized plain (non-pointer) bytes + | + = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the raw bytes of the constant (size: 16, align: 8) { + __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ │ ░░░░░░░░░░░░░░░░ + } + +error: any use of this value will cause an error + --> $DIR/const-pointer-values-in-various-types.rs:65:45 + | +LL | const I32_REF_F32_UNION: f32 = unsafe { Nonsense { int_32_ref: &3 }.float_32 }; + | ----------------------------------------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^--- + | | + | unable to turn pointer into raw bytes + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #71800 <https://github.com/rust-lang/rust/issues/71800> + +error[E0080]: it is undefined behavior to use this value + --> $DIR/const-pointer-values-in-various-types.rs:69:5 + | +LL | const I32_REF_F64_UNION: f64 = unsafe { Nonsense { int_32_ref: &3 }.float_64 }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered pointer to alloc50, but expected initialized plain (non-pointer) bytes + | + = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the raw bytes of the constant (size: 8, align: 8) { + ╾───────alloc50───────╼ │ ╾──────╼ + } + +error: any use of this value will cause an error + --> $DIR/const-pointer-values-in-various-types.rs:72:47 + | +LL | const I32_REF_BOOL_UNION: bool = unsafe { Nonsense { int_32_ref: &3 }.truthy_falsey }; + | ------------------------------------------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^--- + | | + | unable to turn pointer into raw bytes + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #71800 <https://github.com/rust-lang/rust/issues/71800> + +error: any use of this value will cause an error + --> $DIR/const-pointer-values-in-various-types.rs:76:47 + | +LL | const I32_REF_CHAR_UNION: char = unsafe { Nonsense { int_32_ref: &3 }.character }; + | ------------------------------------------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^--- + | | + | unable to turn pointer into raw bytes + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #71800 <https://github.com/rust-lang/rust/issues/71800> + +error: any use of this value will cause an error + --> $DIR/const-pointer-values-in-various-types.rs:80:39 + | +LL | const STR_U8_UNION: u8 = unsafe { Nonsense { stringy: "3" }.uint_8 }; + | ----------------------------------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^--- + | | + | unable to turn pointer into raw bytes + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #71800 <https://github.com/rust-lang/rust/issues/71800> + +error: any use of this value will cause an error + --> $DIR/const-pointer-values-in-various-types.rs:84:41 + | +LL | const STR_U16_UNION: u16 = unsafe { Nonsense { stringy: "3" }.uint_16 }; + | ------------------------------------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^--- + | | + | unable to turn pointer into raw bytes + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #71800 <https://github.com/rust-lang/rust/issues/71800> + +error: any use of this value will cause an error + --> $DIR/const-pointer-values-in-various-types.rs:88:41 + | +LL | const STR_U32_UNION: u32 = unsafe { Nonsense { stringy: "3" }.uint_32 }; + | ------------------------------------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^--- + | | + | unable to turn pointer into raw bytes + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #71800 <https://github.com/rust-lang/rust/issues/71800> + +error[E0080]: it is undefined behavior to use this value + --> $DIR/const-pointer-values-in-various-types.rs:92:5 + | +LL | const STR_U64_UNION: u64 = unsafe { Nonsense { stringy: "3" }.uint_64 }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered pointer to alloc71, but expected initialized plain (non-pointer) bytes + | + = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the raw bytes of the constant (size: 8, align: 8) { + ╾───────alloc71───────╼ │ ╾──────╼ + } + +error: any use of this value will cause an error + --> $DIR/const-pointer-values-in-various-types.rs:95:43 + | +LL | const STR_U128_UNION: u128 = unsafe { Nonsense { stringy: "3" }.uint_128 }; + | --------------------------------------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^--- + | | + | unable to turn pointer into raw bytes + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #71800 <https://github.com/rust-lang/rust/issues/71800> + +error: any use of this value will cause an error + --> $DIR/const-pointer-values-in-various-types.rs:99:39 + | +LL | const STR_I8_UNION: i8 = unsafe { Nonsense { stringy: "3" }.int_8 }; + | ----------------------------------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^--- + | | + | unable to turn pointer into raw bytes + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #71800 <https://github.com/rust-lang/rust/issues/71800> + +error: any use of this value will cause an error + --> $DIR/const-pointer-values-in-various-types.rs:103:41 + | +LL | const STR_I16_UNION: i16 = unsafe { Nonsense { stringy: "3" }.int_16 }; + | ------------------------------------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^--- + | | + | unable to turn pointer into raw bytes + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #71800 <https://github.com/rust-lang/rust/issues/71800> + +error: any use of this value will cause an error + --> $DIR/const-pointer-values-in-various-types.rs:107:41 + | +LL | const STR_I32_UNION: i32 = unsafe { Nonsense { stringy: "3" }.int_32 }; + | ------------------------------------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^--- + | | + | unable to turn pointer into raw bytes + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #71800 <https://github.com/rust-lang/rust/issues/71800> + +error[E0080]: it is undefined behavior to use this value + --> $DIR/const-pointer-values-in-various-types.rs:111:5 + | +LL | const STR_I64_UNION: i64 = unsafe { Nonsense { stringy: "3" }.int_64 }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered pointer to alloc86, but expected initialized plain (non-pointer) bytes + | + = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the raw bytes of the constant (size: 8, align: 8) { + ╾───────alloc86───────╼ │ ╾──────╼ + } + +error: any use of this value will cause an error + --> $DIR/const-pointer-values-in-various-types.rs:114:43 + | +LL | const STR_I128_UNION: i128 = unsafe { Nonsense { stringy: "3" }.int_128 }; + | --------------------------------------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^--- + | | + | unable to turn pointer into raw bytes + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #71800 <https://github.com/rust-lang/rust/issues/71800> + +error: any use of this value will cause an error + --> $DIR/const-pointer-values-in-various-types.rs:118:41 + | +LL | const STR_F32_UNION: f32 = unsafe { Nonsense { stringy: "3" }.float_32 }; + | ------------------------------------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^--- + | | + | unable to turn pointer into raw bytes + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #71800 <https://github.com/rust-lang/rust/issues/71800> + +error[E0080]: it is undefined behavior to use this value + --> $DIR/const-pointer-values-in-various-types.rs:122:5 + | +LL | const STR_F64_UNION: f64 = unsafe { Nonsense { stringy: "3" }.float_64 }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered pointer to alloc95, but expected initialized plain (non-pointer) bytes + | + = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the raw bytes of the constant (size: 8, align: 8) { + ╾───────alloc95───────╼ │ ╾──────╼ + } + +error: any use of this value will cause an error + --> $DIR/const-pointer-values-in-various-types.rs:125:43 + | +LL | const STR_BOOL_UNION: bool = unsafe { Nonsense { stringy: "3" }.truthy_falsey }; + | --------------------------------------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^--- + | | + | unable to turn pointer into raw bytes + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #71800 <https://github.com/rust-lang/rust/issues/71800> + +error: any use of this value will cause an error + --> $DIR/const-pointer-values-in-various-types.rs:129:43 + | +LL | const STR_CHAR_UNION: char = unsafe { Nonsense { stringy: "3" }.character }; + | --------------------------------------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^--- + | | + | unable to turn pointer into raw bytes + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #71800 <https://github.com/rust-lang/rust/issues/71800> + +error: aborting due to 29 previous errors + +For more information about this error, try `rustc --explain E0080`. diff --git a/src/test/ui/consts/const-eval/const-pointer-values-in-various-types.rs b/src/test/ui/consts/const-eval/const-pointer-values-in-various-types.rs index 90bc191020e26..a1a932639db6e 100644 --- a/src/test/ui/consts/const-eval/const-pointer-values-in-various-types.rs +++ b/src/test/ui/consts/const-eval/const-pointer-values-in-various-types.rs @@ -1,4 +1,5 @@ // only-x86_64 +// stderr-per-bitwidth #[repr(C)] union Nonsense { diff --git a/src/test/ui/consts/const-eval/const-pointer-values-in-various-types.stderr b/src/test/ui/consts/const-eval/const-pointer-values-in-various-types.stderr deleted file mode 100644 index ccd13784784e1..0000000000000 --- a/src/test/ui/consts/const-eval/const-pointer-values-in-various-types.stderr +++ /dev/null @@ -1,296 +0,0 @@ -error[E0080]: it is undefined behavior to use this value - --> $DIR/const-pointer-values-in-various-types.rs:25:5 - | -LL | const I32_REF_USIZE_UNION: usize = unsafe { Nonsense { int_32_ref: &3 }.u }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered pointer to alloc2, but expected initialized plain (non-pointer) bytes - | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. - -error: any use of this value will cause an error - --> $DIR/const-pointer-values-in-various-types.rs:28:43 - | -LL | const I32_REF_U8_UNION: u8 = unsafe { Nonsense { int_32_ref: &3 }.uint_8 }; - | --------------------------------------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^--- - | | - | unable to turn pointer into raw bytes - | - = note: `#[deny(const_err)]` on by default - = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = note: for more information, see issue #71800 <https://github.com/rust-lang/rust/issues/71800> - -error: any use of this value will cause an error - --> $DIR/const-pointer-values-in-various-types.rs:32:45 - | -LL | const I32_REF_U16_UNION: u16 = unsafe { Nonsense { int_32_ref: &3 }.uint_16 }; - | ----------------------------------------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^--- - | | - | unable to turn pointer into raw bytes - | - = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = note: for more information, see issue #71800 <https://github.com/rust-lang/rust/issues/71800> - -error: any use of this value will cause an error - --> $DIR/const-pointer-values-in-various-types.rs:36:45 - | -LL | const I32_REF_U32_UNION: u32 = unsafe { Nonsense { int_32_ref: &3 }.uint_32 }; - | ----------------------------------------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^--- - | | - | unable to turn pointer into raw bytes - | - = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = note: for more information, see issue #71800 <https://github.com/rust-lang/rust/issues/71800> - -error[E0080]: it is undefined behavior to use this value - --> $DIR/const-pointer-values-in-various-types.rs:40:5 - | -LL | const I32_REF_U64_UNION: u64 = unsafe { Nonsense { int_32_ref: &3 }.uint_64 }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered pointer to alloc18, but expected initialized plain (non-pointer) bytes - | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. - -error[E0080]: it is undefined behavior to use this value - --> $DIR/const-pointer-values-in-various-types.rs:43:5 - | -LL | const I32_REF_U128_UNION: u128 = unsafe { Nonsense { int_32_ref: &3 }.uint_128 }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered uninitialized bytes, but expected initialized plain (non-pointer) bytes - | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. - -error: any use of this value will cause an error - --> $DIR/const-pointer-values-in-various-types.rs:46:43 - | -LL | const I32_REF_I8_UNION: i8 = unsafe { Nonsense { int_32_ref: &3 }.int_8 }; - | --------------------------------------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^--- - | | - | unable to turn pointer into raw bytes - | - = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = note: for more information, see issue #71800 <https://github.com/rust-lang/rust/issues/71800> - -error: any use of this value will cause an error - --> $DIR/const-pointer-values-in-various-types.rs:50:45 - | -LL | const I32_REF_I16_UNION: i16 = unsafe { Nonsense { int_32_ref: &3 }.int_16 }; - | ----------------------------------------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^--- - | | - | unable to turn pointer into raw bytes - | - = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = note: for more information, see issue #71800 <https://github.com/rust-lang/rust/issues/71800> - -error: any use of this value will cause an error - --> $DIR/const-pointer-values-in-various-types.rs:54:45 - | -LL | const I32_REF_I32_UNION: i32 = unsafe { Nonsense { int_32_ref: &3 }.int_32 }; - | ----------------------------------------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^--- - | | - | unable to turn pointer into raw bytes - | - = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = note: for more information, see issue #71800 <https://github.com/rust-lang/rust/issues/71800> - -error[E0080]: it is undefined behavior to use this value - --> $DIR/const-pointer-values-in-various-types.rs:58:5 - | -LL | const I32_REF_I64_UNION: i64 = unsafe { Nonsense { int_32_ref: &3 }.int_64 }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered pointer to alloc38, but expected initialized plain (non-pointer) bytes - | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. - -error[E0080]: it is undefined behavior to use this value - --> $DIR/const-pointer-values-in-various-types.rs:61:5 - | -LL | const I32_REF_I128_UNION: i128 = unsafe { Nonsense { int_32_ref: &3 }.int_128 }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered uninitialized bytes, but expected initialized plain (non-pointer) bytes - | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. - -error: any use of this value will cause an error - --> $DIR/const-pointer-values-in-various-types.rs:64:45 - | -LL | const I32_REF_F32_UNION: f32 = unsafe { Nonsense { int_32_ref: &3 }.float_32 }; - | ----------------------------------------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^--- - | | - | unable to turn pointer into raw bytes - | - = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = note: for more information, see issue #71800 <https://github.com/rust-lang/rust/issues/71800> - -error[E0080]: it is undefined behavior to use this value - --> $DIR/const-pointer-values-in-various-types.rs:68:5 - | -LL | const I32_REF_F64_UNION: f64 = unsafe { Nonsense { int_32_ref: &3 }.float_64 }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered pointer to alloc50, but expected initialized plain (non-pointer) bytes - | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. - -error: any use of this value will cause an error - --> $DIR/const-pointer-values-in-various-types.rs:71:47 - | -LL | const I32_REF_BOOL_UNION: bool = unsafe { Nonsense { int_32_ref: &3 }.truthy_falsey }; - | ------------------------------------------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^--- - | | - | unable to turn pointer into raw bytes - | - = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = note: for more information, see issue #71800 <https://github.com/rust-lang/rust/issues/71800> - -error: any use of this value will cause an error - --> $DIR/const-pointer-values-in-various-types.rs:75:47 - | -LL | const I32_REF_CHAR_UNION: char = unsafe { Nonsense { int_32_ref: &3 }.character }; - | ------------------------------------------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^--- - | | - | unable to turn pointer into raw bytes - | - = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = note: for more information, see issue #71800 <https://github.com/rust-lang/rust/issues/71800> - -error: any use of this value will cause an error - --> $DIR/const-pointer-values-in-various-types.rs:79:39 - | -LL | const STR_U8_UNION: u8 = unsafe { Nonsense { stringy: "3" }.uint_8 }; - | ----------------------------------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^--- - | | - | unable to turn pointer into raw bytes - | - = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = note: for more information, see issue #71800 <https://github.com/rust-lang/rust/issues/71800> - -error: any use of this value will cause an error - --> $DIR/const-pointer-values-in-various-types.rs:83:41 - | -LL | const STR_U16_UNION: u16 = unsafe { Nonsense { stringy: "3" }.uint_16 }; - | ------------------------------------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^--- - | | - | unable to turn pointer into raw bytes - | - = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = note: for more information, see issue #71800 <https://github.com/rust-lang/rust/issues/71800> - -error: any use of this value will cause an error - --> $DIR/const-pointer-values-in-various-types.rs:87:41 - | -LL | const STR_U32_UNION: u32 = unsafe { Nonsense { stringy: "3" }.uint_32 }; - | ------------------------------------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^--- - | | - | unable to turn pointer into raw bytes - | - = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = note: for more information, see issue #71800 <https://github.com/rust-lang/rust/issues/71800> - -error[E0080]: it is undefined behavior to use this value - --> $DIR/const-pointer-values-in-various-types.rs:91:5 - | -LL | const STR_U64_UNION: u64 = unsafe { Nonsense { stringy: "3" }.uint_64 }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered pointer to alloc71, but expected initialized plain (non-pointer) bytes - | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. - -error: any use of this value will cause an error - --> $DIR/const-pointer-values-in-various-types.rs:94:43 - | -LL | const STR_U128_UNION: u128 = unsafe { Nonsense { stringy: "3" }.uint_128 }; - | --------------------------------------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^--- - | | - | unable to turn pointer into raw bytes - | - = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = note: for more information, see issue #71800 <https://github.com/rust-lang/rust/issues/71800> - -error: any use of this value will cause an error - --> $DIR/const-pointer-values-in-various-types.rs:98:39 - | -LL | const STR_I8_UNION: i8 = unsafe { Nonsense { stringy: "3" }.int_8 }; - | ----------------------------------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^--- - | | - | unable to turn pointer into raw bytes - | - = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = note: for more information, see issue #71800 <https://github.com/rust-lang/rust/issues/71800> - -error: any use of this value will cause an error - --> $DIR/const-pointer-values-in-various-types.rs:102:41 - | -LL | const STR_I16_UNION: i16 = unsafe { Nonsense { stringy: "3" }.int_16 }; - | ------------------------------------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^--- - | | - | unable to turn pointer into raw bytes - | - = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = note: for more information, see issue #71800 <https://github.com/rust-lang/rust/issues/71800> - -error: any use of this value will cause an error - --> $DIR/const-pointer-values-in-various-types.rs:106:41 - | -LL | const STR_I32_UNION: i32 = unsafe { Nonsense { stringy: "3" }.int_32 }; - | ------------------------------------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^--- - | | - | unable to turn pointer into raw bytes - | - = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = note: for more information, see issue #71800 <https://github.com/rust-lang/rust/issues/71800> - -error[E0080]: it is undefined behavior to use this value - --> $DIR/const-pointer-values-in-various-types.rs:110:5 - | -LL | const STR_I64_UNION: i64 = unsafe { Nonsense { stringy: "3" }.int_64 }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered pointer to alloc86, but expected initialized plain (non-pointer) bytes - | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. - -error: any use of this value will cause an error - --> $DIR/const-pointer-values-in-various-types.rs:113:43 - | -LL | const STR_I128_UNION: i128 = unsafe { Nonsense { stringy: "3" }.int_128 }; - | --------------------------------------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^--- - | | - | unable to turn pointer into raw bytes - | - = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = note: for more information, see issue #71800 <https://github.com/rust-lang/rust/issues/71800> - -error: any use of this value will cause an error - --> $DIR/const-pointer-values-in-various-types.rs:117:41 - | -LL | const STR_F32_UNION: f32 = unsafe { Nonsense { stringy: "3" }.float_32 }; - | ------------------------------------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^--- - | | - | unable to turn pointer into raw bytes - | - = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = note: for more information, see issue #71800 <https://github.com/rust-lang/rust/issues/71800> - -error[E0080]: it is undefined behavior to use this value - --> $DIR/const-pointer-values-in-various-types.rs:121:5 - | -LL | const STR_F64_UNION: f64 = unsafe { Nonsense { stringy: "3" }.float_64 }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered pointer to alloc95, but expected initialized plain (non-pointer) bytes - | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. - -error: any use of this value will cause an error - --> $DIR/const-pointer-values-in-various-types.rs:124:43 - | -LL | const STR_BOOL_UNION: bool = unsafe { Nonsense { stringy: "3" }.truthy_falsey }; - | --------------------------------------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^--- - | | - | unable to turn pointer into raw bytes - | - = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = note: for more information, see issue #71800 <https://github.com/rust-lang/rust/issues/71800> - -error: any use of this value will cause an error - --> $DIR/const-pointer-values-in-various-types.rs:128:43 - | -LL | const STR_CHAR_UNION: char = unsafe { Nonsense { stringy: "3" }.character }; - | --------------------------------------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^--- - | | - | unable to turn pointer into raw bytes - | - = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = note: for more information, see issue #71800 <https://github.com/rust-lang/rust/issues/71800> - -error: aborting due to 29 previous errors - -For more information about this error, try `rustc --explain E0080`. diff --git a/src/test/ui/consts/const-eval/heap/alloc_intrinsic_uninit.32bit.stderr b/src/test/ui/consts/const-eval/heap/alloc_intrinsic_uninit.32bit.stderr new file mode 100644 index 0000000000000..92d990f1498e2 --- /dev/null +++ b/src/test/ui/consts/const-eval/heap/alloc_intrinsic_uninit.32bit.stderr @@ -0,0 +1,14 @@ +error[E0080]: it is undefined behavior to use this value + --> $DIR/alloc_intrinsic_uninit.rs:9:1 + | +LL | const BAR: &i32 = unsafe { &*(intrinsics::const_allocate(4, 4) as *mut i32) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered uninitialized bytes at .<deref>, but expected initialized plain (non-pointer) bytes + | + = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the raw bytes of the constant (size: 4, align: 4) { + ╾─alloc1──╼ │ ╾──╼ + } + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0080`. diff --git a/src/test/ui/consts/const-eval/heap/alloc_intrinsic_uninit.64bit.stderr b/src/test/ui/consts/const-eval/heap/alloc_intrinsic_uninit.64bit.stderr new file mode 100644 index 0000000000000..6d63233997da4 --- /dev/null +++ b/src/test/ui/consts/const-eval/heap/alloc_intrinsic_uninit.64bit.stderr @@ -0,0 +1,14 @@ +error[E0080]: it is undefined behavior to use this value + --> $DIR/alloc_intrinsic_uninit.rs:9:1 + | +LL | const BAR: &i32 = unsafe { &*(intrinsics::const_allocate(4, 4) as *mut i32) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered uninitialized bytes at .<deref>, but expected initialized plain (non-pointer) bytes + | + = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the raw bytes of the constant (size: 8, align: 8) { + ╾───────alloc1────────╼ │ ╾──────╼ + } + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0080`. diff --git a/src/test/ui/consts/const-eval/heap/alloc_intrinsic_uninit.rs b/src/test/ui/consts/const-eval/heap/alloc_intrinsic_uninit.rs index 998b6cef84a72..63a3fd4e090ba 100644 --- a/src/test/ui/consts/const-eval/heap/alloc_intrinsic_uninit.rs +++ b/src/test/ui/consts/const-eval/heap/alloc_intrinsic_uninit.rs @@ -1,3 +1,4 @@ +// stderr-per-bitwidth // compile-test #![feature(core_intrinsics)] #![feature(const_heap)] diff --git a/src/test/ui/consts/const-eval/heap/alloc_intrinsic_uninit.stderr b/src/test/ui/consts/const-eval/heap/alloc_intrinsic_uninit.stderr deleted file mode 100644 index 866f877f54d43..0000000000000 --- a/src/test/ui/consts/const-eval/heap/alloc_intrinsic_uninit.stderr +++ /dev/null @@ -1,11 +0,0 @@ -error[E0080]: it is undefined behavior to use this value - --> $DIR/alloc_intrinsic_uninit.rs:8:1 - | -LL | const BAR: &i32 = unsafe { &*(intrinsics::const_allocate(4, 4) as *mut i32) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered uninitialized bytes at .<deref>, but expected initialized plain (non-pointer) bytes - | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. - -error: aborting due to previous error - -For more information about this error, try `rustc --explain E0080`. diff --git a/src/test/ui/consts/const-eval/issue-44578.rs b/src/test/ui/consts/const-eval/issue-44578.rs index 79f1301a2f944..a88e2197048d5 100644 --- a/src/test/ui/consts/const-eval/issue-44578.rs +++ b/src/test/ui/consts/const-eval/issue-44578.rs @@ -25,5 +25,5 @@ impl Foo for u16 { fn main() { println!("{}", <Bar<u16, u8> as Foo>::AMT); - //~^ ERROR evaluation of constant value failed [E0080] + //~^ ERROR evaluation of constant value failed } diff --git a/src/test/ui/consts/const-eval/ref_to_int_match.32bit.stderr b/src/test/ui/consts/const-eval/ref_to_int_match.32bit.stderr new file mode 100644 index 0000000000000..c14457490ac45 --- /dev/null +++ b/src/test/ui/consts/const-eval/ref_to_int_match.32bit.stderr @@ -0,0 +1,26 @@ +error[E0080]: it is undefined behavior to use this value + --> $DIR/ref_to_int_match.rs:26:1 + | +LL | const BAR: Int = unsafe { Foo { r: &42 }.f }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered pointer to alloc2, but expected initialized plain (non-pointer) bytes + | + = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the raw bytes of the constant (size: 4, align: 4) { + ╾─alloc2──╼ │ ╾──╼ + } + +error: could not evaluate constant pattern + --> $DIR/ref_to_int_match.rs:8:14 + | +LL | 10..=BAR => {}, + | ^^^ + +error: could not evaluate constant pattern + --> $DIR/ref_to_int_match.rs:8:14 + | +LL | 10..=BAR => {}, + | ^^^ + +error: aborting due to 3 previous errors + +For more information about this error, try `rustc --explain E0080`. diff --git a/src/test/ui/consts/const-eval/ref_to_int_match.64bit.stderr b/src/test/ui/consts/const-eval/ref_to_int_match.64bit.stderr new file mode 100644 index 0000000000000..6ea1cf145a26c --- /dev/null +++ b/src/test/ui/consts/const-eval/ref_to_int_match.64bit.stderr @@ -0,0 +1,26 @@ +error[E0080]: it is undefined behavior to use this value + --> $DIR/ref_to_int_match.rs:26:1 + | +LL | const BAR: Int = unsafe { Foo { r: &42 }.f }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered pointer to alloc2, but expected initialized plain (non-pointer) bytes + | + = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the raw bytes of the constant (size: 8, align: 8) { + ╾───────alloc2────────╼ │ ╾──────╼ + } + +error: could not evaluate constant pattern + --> $DIR/ref_to_int_match.rs:8:14 + | +LL | 10..=BAR => {}, + | ^^^ + +error: could not evaluate constant pattern + --> $DIR/ref_to_int_match.rs:8:14 + | +LL | 10..=BAR => {}, + | ^^^ + +error: aborting due to 3 previous errors + +For more information about this error, try `rustc --explain E0080`. diff --git a/src/test/ui/consts/const-eval/ref_to_int_match.rs b/src/test/ui/consts/const-eval/ref_to_int_match.rs index 87136a109db35..0741e425d9b7a 100644 --- a/src/test/ui/consts/const-eval/ref_to_int_match.rs +++ b/src/test/ui/consts/const-eval/ref_to_int_match.rs @@ -1,3 +1,4 @@ +// stderr-per-bitwidth #![feature(const_fn_union)] fn main() { diff --git a/src/test/ui/consts/const-eval/ref_to_int_match.stderr b/src/test/ui/consts/const-eval/ref_to_int_match.stderr deleted file mode 100644 index cb0ba5d9929b9..0000000000000 --- a/src/test/ui/consts/const-eval/ref_to_int_match.stderr +++ /dev/null @@ -1,23 +0,0 @@ -error[E0080]: it is undefined behavior to use this value - --> $DIR/ref_to_int_match.rs:25:1 - | -LL | const BAR: Int = unsafe { Foo { r: &42 }.f }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered pointer to alloc2, but expected initialized plain (non-pointer) bytes - | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. - -error: could not evaluate constant pattern - --> $DIR/ref_to_int_match.rs:7:14 - | -LL | 10..=BAR => {}, - | ^^^ - -error: could not evaluate constant pattern - --> $DIR/ref_to_int_match.rs:7:14 - | -LL | 10..=BAR => {}, - | ^^^ - -error: aborting due to 3 previous errors - -For more information about this error, try `rustc --explain E0080`. diff --git a/src/test/ui/consts/const-eval/transmute-const.32bit.stderr b/src/test/ui/consts/const-eval/transmute-const.32bit.stderr new file mode 100644 index 0000000000000..89c7220788324 --- /dev/null +++ b/src/test/ui/consts/const-eval/transmute-const.32bit.stderr @@ -0,0 +1,14 @@ +error[E0080]: it is undefined behavior to use this value + --> $DIR/transmute-const.rs:4:1 + | +LL | static FOO: bool = unsafe { mem::transmute(3u8) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered 0x03, but expected a boolean + | + = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the raw bytes of the constant (size: 1, align: 1) { + 03 │ . + } + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0080`. diff --git a/src/test/ui/consts/const-eval/transmute-const.64bit.stderr b/src/test/ui/consts/const-eval/transmute-const.64bit.stderr new file mode 100644 index 0000000000000..89c7220788324 --- /dev/null +++ b/src/test/ui/consts/const-eval/transmute-const.64bit.stderr @@ -0,0 +1,14 @@ +error[E0080]: it is undefined behavior to use this value + --> $DIR/transmute-const.rs:4:1 + | +LL | static FOO: bool = unsafe { mem::transmute(3u8) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered 0x03, but expected a boolean + | + = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the raw bytes of the constant (size: 1, align: 1) { + 03 │ . + } + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0080`. diff --git a/src/test/ui/consts/const-eval/transmute-const.rs b/src/test/ui/consts/const-eval/transmute-const.rs index 1cfad00ca76df..d9d0a3aea07be 100644 --- a/src/test/ui/consts/const-eval/transmute-const.rs +++ b/src/test/ui/consts/const-eval/transmute-const.rs @@ -1,3 +1,4 @@ +// stderr-per-bitwidth use std::mem; static FOO: bool = unsafe { mem::transmute(3u8) }; diff --git a/src/test/ui/consts/const-eval/transmute-const.stderr b/src/test/ui/consts/const-eval/transmute-const.stderr deleted file mode 100644 index 46a404982778d..0000000000000 --- a/src/test/ui/consts/const-eval/transmute-const.stderr +++ /dev/null @@ -1,11 +0,0 @@ -error[E0080]: it is undefined behavior to use this value - --> $DIR/transmute-const.rs:3:1 - | -LL | static FOO: bool = unsafe { mem::transmute(3u8) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered 0x03, but expected a boolean - | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. - -error: aborting due to previous error - -For more information about this error, try `rustc --explain E0080`. diff --git a/src/test/ui/consts/const-eval/ub-enum.32bit.stderr b/src/test/ui/consts/const-eval/ub-enum.32bit.stderr new file mode 100644 index 0000000000000..2274366fa216b --- /dev/null +++ b/src/test/ui/consts/const-eval/ub-enum.32bit.stderr @@ -0,0 +1,146 @@ +error[E0080]: it is undefined behavior to use this value + --> $DIR/ub-enum.rs:24:1 + | +LL | const BAD_ENUM: Enum = unsafe { mem::transmute(1usize) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered 0x00000001 at .<enum-tag>, but expected a valid enum tag + | + = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the raw bytes of the constant (size: 4, align: 4) { + 01 00 00 00 │ .... + } + +error[E0080]: it is undefined behavior to use this value + --> $DIR/ub-enum.rs:27:1 + | +LL | const BAD_ENUM_PTR: Enum = unsafe { mem::transmute(&1) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered pointer to alloc8 at .<enum-tag>, but expected initialized plain (non-pointer) bytes + | + = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the raw bytes of the constant (size: 4, align: 4) { + ╾─alloc8──╼ │ ╾──╼ + } + +error[E0080]: it is undefined behavior to use this value + --> $DIR/ub-enum.rs:30:1 + | +LL | const BAD_ENUM_WRAPPED: Wrap<Enum> = unsafe { mem::transmute(&1) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered pointer to alloc12 at .0.<enum-tag>, but expected initialized plain (non-pointer) bytes + | + = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the raw bytes of the constant (size: 4, align: 4) { + ╾─alloc12─╼ │ ╾──╼ + } + +error[E0080]: it is undefined behavior to use this value + --> $DIR/ub-enum.rs:42:1 + | +LL | const BAD_ENUM2: Enum2 = unsafe { mem::transmute(0usize) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered 0x00000000 at .<enum-tag>, but expected a valid enum tag + | + = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the raw bytes of the constant (size: 4, align: 4) { + 00 00 00 00 │ .... + } + +error[E0080]: it is undefined behavior to use this value + --> $DIR/ub-enum.rs:44:1 + | +LL | const BAD_ENUM2_PTR: Enum2 = unsafe { mem::transmute(&0) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered pointer to alloc18 at .<enum-tag>, but expected initialized plain (non-pointer) bytes + | + = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the raw bytes of the constant (size: 4, align: 4) { + ╾─alloc18─╼ │ ╾──╼ + } + +error[E0080]: it is undefined behavior to use this value + --> $DIR/ub-enum.rs:47:1 + | +LL | const BAD_ENUM2_WRAPPED: Wrap<Enum2> = unsafe { mem::transmute(&0) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered pointer to alloc22 at .0.<enum-tag>, but expected initialized plain (non-pointer) bytes + | + = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the raw bytes of the constant (size: 4, align: 4) { + ╾─alloc22─╼ │ ╾──╼ + } + +error[E0080]: it is undefined behavior to use this value + --> $DIR/ub-enum.rs:56:1 + | +LL | const BAD_ENUM2_UNDEF : Enum2 = unsafe { MaybeUninit { uninit: () }.init }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered uninitialized bytes at .<enum-tag>, but expected initialized plain (non-pointer) bytes + | + = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the raw bytes of the constant (size: 4, align: 4) { + __ __ __ __ │ ░░░░ + } + +error[E0080]: it is undefined behavior to use this value + --> $DIR/ub-enum.rs:60:1 + | +LL | const BAD_ENUM2_OPTION_PTR: Option<Enum2> = unsafe { mem::transmute(&0) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered pointer to alloc28 at .<enum-tag>, but expected initialized plain (non-pointer) bytes + | + = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the raw bytes of the constant (size: 4, align: 4) { + ╾─alloc28─╼ │ ╾──╼ + } + +error[E0080]: it is undefined behavior to use this value + --> $DIR/ub-enum.rs:77:1 + | +LL | const BAD_UNINHABITED_VARIANT1: UninhDiscriminant = unsafe { mem::transmute(1u8) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered a value of the never type `!` at .<enum-variant(B)>.0 + | + = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the raw bytes of the constant (size: 1, align: 1) { + 01 │ . + } + +error[E0080]: it is undefined behavior to use this value + --> $DIR/ub-enum.rs:79:1 + | +LL | const BAD_UNINHABITED_VARIANT2: UninhDiscriminant = unsafe { mem::transmute(3u8) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered a value of uninhabited type Never at .<enum-variant(D)>.0 + | + = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the raw bytes of the constant (size: 1, align: 1) { + 03 │ . + } + +error[E0080]: it is undefined behavior to use this value + --> $DIR/ub-enum.rs:87:1 + | +LL | const BAD_OPTION_CHAR: Option<(char, char)> = Some(('x', unsafe { mem::transmute(!0u32) })); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered 0xffffffff at .<enum-variant(Some)>.0.1, but expected a valid unicode scalar value (in `0..=0x10FFFF` but not in `0xD800..=0xDFFF`) + | + = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the raw bytes of the constant (size: 8, align: 4) { + 78 00 00 00 ff ff ff ff │ x....... + } + +error[E0080]: it is undefined behavior to use this value + --> $DIR/ub-enum.rs:92:1 + | +LL | const BAD_UNINHABITED_WITH_DATA1: Result<(i32, Never), (i32, !)> = unsafe { mem::transmute(0u64) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered a value of uninhabited type Never at .<enum-variant(Ok)>.0.1 + | + = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the raw bytes of the constant (size: 8, align: 4) { + 00 00 00 00 00 00 00 00 │ ........ + } + +error[E0080]: it is undefined behavior to use this value + --> $DIR/ub-enum.rs:94:1 + | +LL | const BAD_UNINHABITED_WITH_DATA2: Result<(i32, !), (i32, Never)> = unsafe { mem::transmute(0u64) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered a value of the never type `!` at .<enum-variant(Ok)>.0.1 + | + = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the raw bytes of the constant (size: 8, align: 4) { + 00 00 00 00 00 00 00 00 │ ........ + } + +error: aborting due to 13 previous errors + +For more information about this error, try `rustc --explain E0080`. diff --git a/src/test/ui/consts/const-eval/ub-enum.64bit.stderr b/src/test/ui/consts/const-eval/ub-enum.64bit.stderr new file mode 100644 index 0000000000000..29d97962f32d7 --- /dev/null +++ b/src/test/ui/consts/const-eval/ub-enum.64bit.stderr @@ -0,0 +1,146 @@ +error[E0080]: it is undefined behavior to use this value + --> $DIR/ub-enum.rs:24:1 + | +LL | const BAD_ENUM: Enum = unsafe { mem::transmute(1usize) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered 0x0000000000000001 at .<enum-tag>, but expected a valid enum tag + | + = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the raw bytes of the constant (size: 8, align: 8) { + 01 00 00 00 00 00 00 00 │ ........ + } + +error[E0080]: it is undefined behavior to use this value + --> $DIR/ub-enum.rs:27:1 + | +LL | const BAD_ENUM_PTR: Enum = unsafe { mem::transmute(&1) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered pointer to alloc8 at .<enum-tag>, but expected initialized plain (non-pointer) bytes + | + = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the raw bytes of the constant (size: 8, align: 8) { + ╾───────alloc8────────╼ │ ╾──────╼ + } + +error[E0080]: it is undefined behavior to use this value + --> $DIR/ub-enum.rs:30:1 + | +LL | const BAD_ENUM_WRAPPED: Wrap<Enum> = unsafe { mem::transmute(&1) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered pointer to alloc12 at .0.<enum-tag>, but expected initialized plain (non-pointer) bytes + | + = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the raw bytes of the constant (size: 8, align: 8) { + ╾───────alloc12───────╼ │ ╾──────╼ + } + +error[E0080]: it is undefined behavior to use this value + --> $DIR/ub-enum.rs:42:1 + | +LL | const BAD_ENUM2: Enum2 = unsafe { mem::transmute(0usize) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered 0x0000000000000000 at .<enum-tag>, but expected a valid enum tag + | + = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the raw bytes of the constant (size: 8, align: 8) { + 00 00 00 00 00 00 00 00 │ ........ + } + +error[E0080]: it is undefined behavior to use this value + --> $DIR/ub-enum.rs:44:1 + | +LL | const BAD_ENUM2_PTR: Enum2 = unsafe { mem::transmute(&0) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered pointer to alloc18 at .<enum-tag>, but expected initialized plain (non-pointer) bytes + | + = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the raw bytes of the constant (size: 8, align: 8) { + ╾───────alloc18───────╼ │ ╾──────╼ + } + +error[E0080]: it is undefined behavior to use this value + --> $DIR/ub-enum.rs:47:1 + | +LL | const BAD_ENUM2_WRAPPED: Wrap<Enum2> = unsafe { mem::transmute(&0) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered pointer to alloc22 at .0.<enum-tag>, but expected initialized plain (non-pointer) bytes + | + = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the raw bytes of the constant (size: 8, align: 8) { + ╾───────alloc22───────╼ │ ╾──────╼ + } + +error[E0080]: it is undefined behavior to use this value + --> $DIR/ub-enum.rs:56:1 + | +LL | const BAD_ENUM2_UNDEF : Enum2 = unsafe { MaybeUninit { uninit: () }.init }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered uninitialized bytes at .<enum-tag>, but expected initialized plain (non-pointer) bytes + | + = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the raw bytes of the constant (size: 8, align: 8) { + __ __ __ __ __ __ __ __ │ ░░░░░░░░ + } + +error[E0080]: it is undefined behavior to use this value + --> $DIR/ub-enum.rs:60:1 + | +LL | const BAD_ENUM2_OPTION_PTR: Option<Enum2> = unsafe { mem::transmute(&0) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered pointer to alloc28 at .<enum-tag>, but expected initialized plain (non-pointer) bytes + | + = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the raw bytes of the constant (size: 8, align: 8) { + ╾───────alloc28───────╼ │ ╾──────╼ + } + +error[E0080]: it is undefined behavior to use this value + --> $DIR/ub-enum.rs:77:1 + | +LL | const BAD_UNINHABITED_VARIANT1: UninhDiscriminant = unsafe { mem::transmute(1u8) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered a value of the never type `!` at .<enum-variant(B)>.0 + | + = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the raw bytes of the constant (size: 1, align: 1) { + 01 │ . + } + +error[E0080]: it is undefined behavior to use this value + --> $DIR/ub-enum.rs:79:1 + | +LL | const BAD_UNINHABITED_VARIANT2: UninhDiscriminant = unsafe { mem::transmute(3u8) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered a value of uninhabited type Never at .<enum-variant(D)>.0 + | + = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the raw bytes of the constant (size: 1, align: 1) { + 03 │ . + } + +error[E0080]: it is undefined behavior to use this value + --> $DIR/ub-enum.rs:87:1 + | +LL | const BAD_OPTION_CHAR: Option<(char, char)> = Some(('x', unsafe { mem::transmute(!0u32) })); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered 0xffffffff at .<enum-variant(Some)>.0.1, but expected a valid unicode scalar value (in `0..=0x10FFFF` but not in `0xD800..=0xDFFF`) + | + = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the raw bytes of the constant (size: 8, align: 4) { + 78 00 00 00 ff ff ff ff │ x....... + } + +error[E0080]: it is undefined behavior to use this value + --> $DIR/ub-enum.rs:92:1 + | +LL | const BAD_UNINHABITED_WITH_DATA1: Result<(i32, Never), (i32, !)> = unsafe { mem::transmute(0u64) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered a value of uninhabited type Never at .<enum-variant(Ok)>.0.1 + | + = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the raw bytes of the constant (size: 8, align: 4) { + 00 00 00 00 00 00 00 00 │ ........ + } + +error[E0080]: it is undefined behavior to use this value + --> $DIR/ub-enum.rs:94:1 + | +LL | const BAD_UNINHABITED_WITH_DATA2: Result<(i32, !), (i32, Never)> = unsafe { mem::transmute(0u64) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered a value of the never type `!` at .<enum-variant(Ok)>.0.1 + | + = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the raw bytes of the constant (size: 8, align: 4) { + 00 00 00 00 00 00 00 00 │ ........ + } + +error: aborting due to 13 previous errors + +For more information about this error, try `rustc --explain E0080`. diff --git a/src/test/ui/consts/const-eval/ub-enum.rs b/src/test/ui/consts/const-eval/ub-enum.rs index dc94f2368c9b6..e408d8ec072e3 100644 --- a/src/test/ui/consts/const-eval/ub-enum.rs +++ b/src/test/ui/consts/const-eval/ub-enum.rs @@ -1,4 +1,4 @@ -// normalize-stderr-64bit "0x0000000000" -> "0x00" +// stderr-per-bitwidth #![feature(never_type)] #![allow(const_err)] // make sure we cannot allow away the errors tested here diff --git a/src/test/ui/consts/const-eval/ub-enum.stderr b/src/test/ui/consts/const-eval/ub-enum.stderr deleted file mode 100644 index db95b996c18c9..0000000000000 --- a/src/test/ui/consts/const-eval/ub-enum.stderr +++ /dev/null @@ -1,107 +0,0 @@ -error[E0080]: it is undefined behavior to use this value - --> $DIR/ub-enum.rs:24:1 - | -LL | const BAD_ENUM: Enum = unsafe { mem::transmute(1usize) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered 0x00000001 at .<enum-tag>, but expected a valid enum tag - | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. - -error[E0080]: it is undefined behavior to use this value - --> $DIR/ub-enum.rs:27:1 - | -LL | const BAD_ENUM_PTR: Enum = unsafe { mem::transmute(&1) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered pointer to alloc8 at .<enum-tag>, but expected initialized plain (non-pointer) bytes - | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. - -error[E0080]: it is undefined behavior to use this value - --> $DIR/ub-enum.rs:30:1 - | -LL | const BAD_ENUM_WRAPPED: Wrap<Enum> = unsafe { mem::transmute(&1) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered pointer to alloc12 at .0.<enum-tag>, but expected initialized plain (non-pointer) bytes - | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. - -error[E0080]: it is undefined behavior to use this value - --> $DIR/ub-enum.rs:42:1 - | -LL | const BAD_ENUM2: Enum2 = unsafe { mem::transmute(0usize) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered 0x00000000 at .<enum-tag>, but expected a valid enum tag - | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. - -error[E0080]: it is undefined behavior to use this value - --> $DIR/ub-enum.rs:44:1 - | -LL | const BAD_ENUM2_PTR: Enum2 = unsafe { mem::transmute(&0) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered pointer to alloc18 at .<enum-tag>, but expected initialized plain (non-pointer) bytes - | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. - -error[E0080]: it is undefined behavior to use this value - --> $DIR/ub-enum.rs:47:1 - | -LL | const BAD_ENUM2_WRAPPED: Wrap<Enum2> = unsafe { mem::transmute(&0) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered pointer to alloc22 at .0.<enum-tag>, but expected initialized plain (non-pointer) bytes - | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. - -error[E0080]: it is undefined behavior to use this value - --> $DIR/ub-enum.rs:56:1 - | -LL | const BAD_ENUM2_UNDEF : Enum2 = unsafe { MaybeUninit { uninit: () }.init }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered uninitialized bytes at .<enum-tag>, but expected initialized plain (non-pointer) bytes - | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. - -error[E0080]: it is undefined behavior to use this value - --> $DIR/ub-enum.rs:60:1 - | -LL | const BAD_ENUM2_OPTION_PTR: Option<Enum2> = unsafe { mem::transmute(&0) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered pointer to alloc28 at .<enum-tag>, but expected initialized plain (non-pointer) bytes - | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. - -error[E0080]: it is undefined behavior to use this value - --> $DIR/ub-enum.rs:77:1 - | -LL | const BAD_UNINHABITED_VARIANT1: UninhDiscriminant = unsafe { mem::transmute(1u8) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered a value of the never type `!` at .<enum-variant(B)>.0 - | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. - -error[E0080]: it is undefined behavior to use this value - --> $DIR/ub-enum.rs:79:1 - | -LL | const BAD_UNINHABITED_VARIANT2: UninhDiscriminant = unsafe { mem::transmute(3u8) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered a value of uninhabited type Never at .<enum-variant(D)>.0 - | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. - -error[E0080]: it is undefined behavior to use this value - --> $DIR/ub-enum.rs:87:1 - | -LL | const BAD_OPTION_CHAR: Option<(char, char)> = Some(('x', unsafe { mem::transmute(!0u32) })); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered 0xffffffff at .<enum-variant(Some)>.0.1, but expected a valid unicode scalar value (in `0..=0x10FFFF` but not in `0xD800..=0xDFFF`) - | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. - -error[E0080]: it is undefined behavior to use this value - --> $DIR/ub-enum.rs:92:1 - | -LL | const BAD_UNINHABITED_WITH_DATA1: Result<(i32, Never), (i32, !)> = unsafe { mem::transmute(0u64) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered a value of uninhabited type Never at .<enum-variant(Ok)>.0.1 - | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. - -error[E0080]: it is undefined behavior to use this value - --> $DIR/ub-enum.rs:94:1 - | -LL | const BAD_UNINHABITED_WITH_DATA2: Result<(i32, !), (i32, Never)> = unsafe { mem::transmute(0u64) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered a value of the never type `!` at .<enum-variant(Ok)>.0.1 - | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. - -error: aborting due to 13 previous errors - -For more information about this error, try `rustc --explain E0080`. diff --git a/src/test/ui/consts/const-eval/ub-int-array.32bit.stderr b/src/test/ui/consts/const-eval/ub-int-array.32bit.stderr new file mode 100644 index 0000000000000..c13271a1e5eca --- /dev/null +++ b/src/test/ui/consts/const-eval/ub-int-array.32bit.stderr @@ -0,0 +1,54 @@ +error[E0080]: it is undefined behavior to use this value + --> $DIR/ub-int-array.rs:14:1 + | +LL | / const UNINIT_INT_0: [u32; 3] = unsafe { +LL | | +LL | | +LL | | [ +... | +LL | | ] +LL | | }; + | |__^ type validation failed: encountered uninitialized bytes at [0] + | + = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the raw bytes of the constant (size: 12, align: 4) { + __ __ __ __ 01 00 00 00 02 00 00 00 │ ░░░░........ + } + +error[E0080]: it is undefined behavior to use this value + --> $DIR/ub-int-array.rs:23:1 + | +LL | / const UNINIT_INT_1: [u32; 3] = unsafe { +LL | | +LL | | +LL | | mem::transmute( +... | +LL | | ) +LL | | }; + | |__^ type validation failed: encountered uninitialized bytes at [1] + | + = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the raw bytes of the constant (size: 12, align: 4) { + 00 00 00 00 01 __ 01 01 02 02 __ 02 │ .....░....░. + } + +error[E0080]: it is undefined behavior to use this value + --> $DIR/ub-int-array.rs:43:1 + | +LL | / const UNINIT_INT_2: [u32; 3] = unsafe { +LL | | +LL | | +LL | | mem::transmute( +... | +LL | | ) +LL | | }; + | |__^ type validation failed: encountered uninitialized bytes at [2] + | + = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the raw bytes of the constant (size: 12, align: 4) { + 00 00 00 00 01 01 01 01 02 02 02 __ │ ...........░ + } + +error: aborting due to 3 previous errors + +For more information about this error, try `rustc --explain E0080`. diff --git a/src/test/ui/consts/const-eval/ub-int-array.64bit.stderr b/src/test/ui/consts/const-eval/ub-int-array.64bit.stderr new file mode 100644 index 0000000000000..c13271a1e5eca --- /dev/null +++ b/src/test/ui/consts/const-eval/ub-int-array.64bit.stderr @@ -0,0 +1,54 @@ +error[E0080]: it is undefined behavior to use this value + --> $DIR/ub-int-array.rs:14:1 + | +LL | / const UNINIT_INT_0: [u32; 3] = unsafe { +LL | | +LL | | +LL | | [ +... | +LL | | ] +LL | | }; + | |__^ type validation failed: encountered uninitialized bytes at [0] + | + = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the raw bytes of the constant (size: 12, align: 4) { + __ __ __ __ 01 00 00 00 02 00 00 00 │ ░░░░........ + } + +error[E0080]: it is undefined behavior to use this value + --> $DIR/ub-int-array.rs:23:1 + | +LL | / const UNINIT_INT_1: [u32; 3] = unsafe { +LL | | +LL | | +LL | | mem::transmute( +... | +LL | | ) +LL | | }; + | |__^ type validation failed: encountered uninitialized bytes at [1] + | + = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the raw bytes of the constant (size: 12, align: 4) { + 00 00 00 00 01 __ 01 01 02 02 __ 02 │ .....░....░. + } + +error[E0080]: it is undefined behavior to use this value + --> $DIR/ub-int-array.rs:43:1 + | +LL | / const UNINIT_INT_2: [u32; 3] = unsafe { +LL | | +LL | | +LL | | mem::transmute( +... | +LL | | ) +LL | | }; + | |__^ type validation failed: encountered uninitialized bytes at [2] + | + = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the raw bytes of the constant (size: 12, align: 4) { + 00 00 00 00 01 01 01 01 02 02 02 __ │ ...........░ + } + +error: aborting due to 3 previous errors + +For more information about this error, try `rustc --explain E0080`. diff --git a/src/test/ui/consts/const-eval/ub-int-array.rs b/src/test/ui/consts/const-eval/ub-int-array.rs index 6801c7fa3ff08..635cbb8cef66c 100644 --- a/src/test/ui/consts/const-eval/ub-int-array.rs +++ b/src/test/ui/consts/const-eval/ub-int-array.rs @@ -1,5 +1,5 @@ #![allow(const_err)] // make sure we cannot allow away the errors tested here - +// stderr-per-bitwidth //! Test the "array of int" fast path in validity checking, and in particular whether it //! points at the right array element. diff --git a/src/test/ui/consts/const-eval/ub-int-array.stderr b/src/test/ui/consts/const-eval/ub-int-array.stderr deleted file mode 100644 index 92f654847df32..0000000000000 --- a/src/test/ui/consts/const-eval/ub-int-array.stderr +++ /dev/null @@ -1,45 +0,0 @@ -error[E0080]: it is undefined behavior to use this value - --> $DIR/ub-int-array.rs:14:1 - | -LL | / const UNINIT_INT_0: [u32; 3] = unsafe { -LL | | -LL | | -LL | | [ -... | -LL | | ] -LL | | }; - | |__^ type validation failed: encountered uninitialized bytes at [0] - | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. - -error[E0080]: it is undefined behavior to use this value - --> $DIR/ub-int-array.rs:23:1 - | -LL | / const UNINIT_INT_1: [u32; 3] = unsafe { -LL | | -LL | | -LL | | mem::transmute( -... | -LL | | ) -LL | | }; - | |__^ type validation failed: encountered uninitialized bytes at [1] - | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. - -error[E0080]: it is undefined behavior to use this value - --> $DIR/ub-int-array.rs:43:1 - | -LL | / const UNINIT_INT_2: [u32; 3] = unsafe { -LL | | -LL | | -LL | | mem::transmute( -... | -LL | | ) -LL | | }; - | |__^ type validation failed: encountered uninitialized bytes at [2] - | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. - -error: aborting due to 3 previous errors - -For more information about this error, try `rustc --explain E0080`. diff --git a/src/test/ui/consts/const-eval/ub-nonnull.32bit.stderr b/src/test/ui/consts/const-eval/ub-nonnull.32bit.stderr new file mode 100644 index 0000000000000..3affde739afe7 --- /dev/null +++ b/src/test/ui/consts/const-eval/ub-nonnull.32bit.stderr @@ -0,0 +1,90 @@ +error[E0080]: it is undefined behavior to use this value + --> $DIR/ub-nonnull.rs:12:1 + | +LL | const NULL_PTR: NonNull<u8> = unsafe { mem::transmute(0usize) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered 0, but expected something greater or equal to 1 + | + = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the raw bytes of the constant (size: 4, align: 4) { + 00 00 00 00 │ .... + } + +error: any use of this value will cause an error + --> $DIR/ub-nonnull.rs:19:30 + | +LL | / const OUT_OF_BOUNDS_PTR: NonNull<u8> = { unsafe { +LL | | let ptr: &[u8; 256] = mem::transmute(&0u8); // &0 gets promoted so it does not dangle +LL | | // Use address-of-element for pointer arithmetic. This could wrap around to NULL! +LL | | let out_of_bounds_ptr = &ptr[255]; + | | ^^^^^^^^ memory access failed: pointer must be in-bounds at offset 256, but is outside bounds of alloc10 which has size 1 +LL | | +LL | | mem::transmute(out_of_bounds_ptr) +LL | | } }; + | |____- + | +note: the lint level is defined here + --> $DIR/ub-nonnull.rs:15:8 + | +LL | #[deny(const_err)] // this triggers a `const_err` so validation does not even happen + | ^^^^^^^^^ + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #71800 <https://github.com/rust-lang/rust/issues/71800> + +error[E0080]: it is undefined behavior to use this value + --> $DIR/ub-nonnull.rs:24:1 + | +LL | const NULL_U8: NonZeroU8 = unsafe { mem::transmute(0u8) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered 0, but expected something greater or equal to 1 + | + = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the raw bytes of the constant (size: 1, align: 1) { + 00 │ . + } + +error[E0080]: it is undefined behavior to use this value + --> $DIR/ub-nonnull.rs:26:1 + | +LL | const NULL_USIZE: NonZeroUsize = unsafe { mem::transmute(0usize) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered 0, but expected something greater or equal to 1 + | + = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the raw bytes of the constant (size: 4, align: 4) { + 00 00 00 00 │ .... + } + +error[E0080]: it is undefined behavior to use this value + --> $DIR/ub-nonnull.rs:34:1 + | +LL | const UNINIT: NonZeroU8 = unsafe { MaybeUninit { uninit: () }.init }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered uninitialized bytes at .0, but expected initialized plain (non-pointer) bytes + | + = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the raw bytes of the constant (size: 1, align: 1) { + __ │ ░ + } + +error[E0080]: it is undefined behavior to use this value + --> $DIR/ub-nonnull.rs:42:1 + | +LL | const BAD_RANGE1: RestrictedRange1 = unsafe { RestrictedRange1(42) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered 42, but expected something in the range 10..=30 + | + = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the raw bytes of the constant (size: 4, align: 4) { + 2a 00 00 00 │ *... + } + +error[E0080]: it is undefined behavior to use this value + --> $DIR/ub-nonnull.rs:48:1 + | +LL | const BAD_RANGE2: RestrictedRange2 = unsafe { RestrictedRange2(20) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered 20, but expected something less or equal to 10, or greater or equal to 30 + | + = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the raw bytes of the constant (size: 4, align: 4) { + 14 00 00 00 │ .... + } + +error: aborting due to 7 previous errors + +For more information about this error, try `rustc --explain E0080`. diff --git a/src/test/ui/consts/const-eval/ub-nonnull.64bit.stderr b/src/test/ui/consts/const-eval/ub-nonnull.64bit.stderr new file mode 100644 index 0000000000000..63815c46efe14 --- /dev/null +++ b/src/test/ui/consts/const-eval/ub-nonnull.64bit.stderr @@ -0,0 +1,90 @@ +error[E0080]: it is undefined behavior to use this value + --> $DIR/ub-nonnull.rs:12:1 + | +LL | const NULL_PTR: NonNull<u8> = unsafe { mem::transmute(0usize) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered 0, but expected something greater or equal to 1 + | + = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the raw bytes of the constant (size: 8, align: 8) { + 00 00 00 00 00 00 00 00 │ ........ + } + +error: any use of this value will cause an error + --> $DIR/ub-nonnull.rs:19:30 + | +LL | / const OUT_OF_BOUNDS_PTR: NonNull<u8> = { unsafe { +LL | | let ptr: &[u8; 256] = mem::transmute(&0u8); // &0 gets promoted so it does not dangle +LL | | // Use address-of-element for pointer arithmetic. This could wrap around to NULL! +LL | | let out_of_bounds_ptr = &ptr[255]; + | | ^^^^^^^^ memory access failed: pointer must be in-bounds at offset 256, but is outside bounds of alloc10 which has size 1 +LL | | +LL | | mem::transmute(out_of_bounds_ptr) +LL | | } }; + | |____- + | +note: the lint level is defined here + --> $DIR/ub-nonnull.rs:15:8 + | +LL | #[deny(const_err)] // this triggers a `const_err` so validation does not even happen + | ^^^^^^^^^ + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #71800 <https://github.com/rust-lang/rust/issues/71800> + +error[E0080]: it is undefined behavior to use this value + --> $DIR/ub-nonnull.rs:24:1 + | +LL | const NULL_U8: NonZeroU8 = unsafe { mem::transmute(0u8) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered 0, but expected something greater or equal to 1 + | + = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the raw bytes of the constant (size: 1, align: 1) { + 00 │ . + } + +error[E0080]: it is undefined behavior to use this value + --> $DIR/ub-nonnull.rs:26:1 + | +LL | const NULL_USIZE: NonZeroUsize = unsafe { mem::transmute(0usize) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered 0, but expected something greater or equal to 1 + | + = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the raw bytes of the constant (size: 8, align: 8) { + 00 00 00 00 00 00 00 00 │ ........ + } + +error[E0080]: it is undefined behavior to use this value + --> $DIR/ub-nonnull.rs:34:1 + | +LL | const UNINIT: NonZeroU8 = unsafe { MaybeUninit { uninit: () }.init }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered uninitialized bytes at .0, but expected initialized plain (non-pointer) bytes + | + = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the raw bytes of the constant (size: 1, align: 1) { + __ │ ░ + } + +error[E0080]: it is undefined behavior to use this value + --> $DIR/ub-nonnull.rs:42:1 + | +LL | const BAD_RANGE1: RestrictedRange1 = unsafe { RestrictedRange1(42) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered 42, but expected something in the range 10..=30 + | + = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the raw bytes of the constant (size: 4, align: 4) { + 2a 00 00 00 │ *... + } + +error[E0080]: it is undefined behavior to use this value + --> $DIR/ub-nonnull.rs:48:1 + | +LL | const BAD_RANGE2: RestrictedRange2 = unsafe { RestrictedRange2(20) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered 20, but expected something less or equal to 10, or greater or equal to 30 + | + = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the raw bytes of the constant (size: 4, align: 4) { + 14 00 00 00 │ .... + } + +error: aborting due to 7 previous errors + +For more information about this error, try `rustc --explain E0080`. diff --git a/src/test/ui/consts/const-eval/ub-nonnull.rs b/src/test/ui/consts/const-eval/ub-nonnull.rs index e4ced600b4cc4..0bc406e01a0ce 100644 --- a/src/test/ui/consts/const-eval/ub-nonnull.rs +++ b/src/test/ui/consts/const-eval/ub-nonnull.rs @@ -1,3 +1,4 @@ +// stderr-per-bitwidth #![feature(rustc_attrs)] #![allow(const_err, invalid_value)] // make sure we cannot allow away the errors tested here diff --git a/src/test/ui/consts/const-eval/ub-nonnull.stderr b/src/test/ui/consts/const-eval/ub-nonnull.stderr deleted file mode 100644 index 94496b77fe773..0000000000000 --- a/src/test/ui/consts/const-eval/ub-nonnull.stderr +++ /dev/null @@ -1,72 +0,0 @@ -error[E0080]: it is undefined behavior to use this value - --> $DIR/ub-nonnull.rs:11:1 - | -LL | const NULL_PTR: NonNull<u8> = unsafe { mem::transmute(0usize) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered 0, but expected something greater or equal to 1 - | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. - -error: any use of this value will cause an error - --> $DIR/ub-nonnull.rs:18:30 - | -LL | / const OUT_OF_BOUNDS_PTR: NonNull<u8> = { unsafe { -LL | | let ptr: &[u8; 256] = mem::transmute(&0u8); // &0 gets promoted so it does not dangle -LL | | // Use address-of-element for pointer arithmetic. This could wrap around to NULL! -LL | | let out_of_bounds_ptr = &ptr[255]; - | | ^^^^^^^^ memory access failed: pointer must be in-bounds at offset 256, but is outside bounds of alloc10 which has size 1 -LL | | -LL | | mem::transmute(out_of_bounds_ptr) -LL | | } }; - | |____- - | -note: the lint level is defined here - --> $DIR/ub-nonnull.rs:14:8 - | -LL | #[deny(const_err)] // this triggers a `const_err` so validation does not even happen - | ^^^^^^^^^ - = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = note: for more information, see issue #71800 <https://github.com/rust-lang/rust/issues/71800> - -error[E0080]: it is undefined behavior to use this value - --> $DIR/ub-nonnull.rs:23:1 - | -LL | const NULL_U8: NonZeroU8 = unsafe { mem::transmute(0u8) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered 0, but expected something greater or equal to 1 - | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. - -error[E0080]: it is undefined behavior to use this value - --> $DIR/ub-nonnull.rs:25:1 - | -LL | const NULL_USIZE: NonZeroUsize = unsafe { mem::transmute(0usize) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered 0, but expected something greater or equal to 1 - | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. - -error[E0080]: it is undefined behavior to use this value - --> $DIR/ub-nonnull.rs:33:1 - | -LL | const UNINIT: NonZeroU8 = unsafe { MaybeUninit { uninit: () }.init }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered uninitialized bytes at .0, but expected initialized plain (non-pointer) bytes - | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. - -error[E0080]: it is undefined behavior to use this value - --> $DIR/ub-nonnull.rs:41:1 - | -LL | const BAD_RANGE1: RestrictedRange1 = unsafe { RestrictedRange1(42) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered 42, but expected something in the range 10..=30 - | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. - -error[E0080]: it is undefined behavior to use this value - --> $DIR/ub-nonnull.rs:47:1 - | -LL | const BAD_RANGE2: RestrictedRange2 = unsafe { RestrictedRange2(20) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered 20, but expected something less or equal to 10, or greater or equal to 30 - | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. - -error: aborting due to 7 previous errors - -For more information about this error, try `rustc --explain E0080`. diff --git a/src/test/ui/consts/const-eval/ub-ref-ptr.32bit.stderr b/src/test/ui/consts/const-eval/ub-ref-ptr.32bit.stderr new file mode 100644 index 0000000000000..32e13baa3f53e --- /dev/null +++ b/src/test/ui/consts/const-eval/ub-ref-ptr.32bit.stderr @@ -0,0 +1,124 @@ +error[E0080]: it is undefined behavior to use this value + --> $DIR/ub-ref-ptr.rs:13:1 + | +LL | const UNALIGNED: &u16 = unsafe { mem::transmute(&[0u8; 4]) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered an unaligned reference (required 2 byte alignment but found 1) + | + = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the raw bytes of the constant (size: 4, align: 4) { + ╾─alloc2──╼ │ ╾──╼ + } + +error[E0080]: it is undefined behavior to use this value + --> $DIR/ub-ref-ptr.rs:17:1 + | +LL | const UNALIGNED_BOX: Box<u16> = unsafe { mem::transmute(&[0u8; 4]) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered an unaligned box (required 2 byte alignment but found 1) + | + = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the raw bytes of the constant (size: 4, align: 4) { + ╾─alloc6──╼ │ ╾──╼ + } + +error[E0080]: it is undefined behavior to use this value + --> $DIR/ub-ref-ptr.rs:21:1 + | +LL | const NULL: &u16 = unsafe { mem::transmute(0usize) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered a NULL reference + | + = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the raw bytes of the constant (size: 4, align: 4) { + 00 00 00 00 │ .... + } + +error[E0080]: it is undefined behavior to use this value + --> $DIR/ub-ref-ptr.rs:24:1 + | +LL | const NULL_BOX: Box<u16> = unsafe { mem::transmute(0usize) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered a NULL box + | + = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the raw bytes of the constant (size: 4, align: 4) { + 00 00 00 00 │ .... + } + +error[E0080]: it is undefined behavior to use this value + --> $DIR/ub-ref-ptr.rs:30:1 + | +LL | const REF_AS_USIZE: usize = unsafe { mem::transmute(&0) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered pointer to alloc14, but expected initialized plain (non-pointer) bytes + | + = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the raw bytes of the constant (size: 4, align: 4) { + ╾─alloc14─╼ │ ╾──╼ + } + +error[E0080]: it is undefined behavior to use this value + --> $DIR/ub-ref-ptr.rs:33:1 + | +LL | const REF_AS_USIZE_SLICE: &[usize] = &[unsafe { mem::transmute(&0) }]; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered a pointer at .<deref>, but expected plain (non-pointer) bytes + | + = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the raw bytes of the constant (size: 4, align: 4) { + ╾─alloc20─╼ │ ╾──╼ + } + +error[E0080]: it is undefined behavior to use this value + --> $DIR/ub-ref-ptr.rs:36:1 + | +LL | const REF_AS_USIZE_BOX_SLICE: Box<[usize]> = unsafe { mem::transmute::<&[usize], _>(&[mem::transmute(&0)]) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered a pointer at .<deref>, but expected plain (non-pointer) bytes + | + = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the raw bytes of the constant (size: 4, align: 4) { + ╾─alloc25─╼ │ ╾──╼ + } + +error[E0080]: it is undefined behavior to use this value + --> $DIR/ub-ref-ptr.rs:39:1 + | +LL | const USIZE_AS_REF: &'static u8 = unsafe { mem::transmute(1337usize) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered a dangling reference (created from integer) + | + = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the raw bytes of the constant (size: 4, align: 4) { + 39 05 00 00 │ 9... + } + +error[E0080]: it is undefined behavior to use this value + --> $DIR/ub-ref-ptr.rs:42:1 + | +LL | const USIZE_AS_BOX: Box<u8> = unsafe { mem::transmute(1337usize) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered a dangling box (created from integer) + | + = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the raw bytes of the constant (size: 4, align: 4) { + 39 05 00 00 │ 9... + } + +error[E0080]: it is undefined behavior to use this value + --> $DIR/ub-ref-ptr.rs:45:1 + | +LL | const UNINIT_PTR: *const i32 = unsafe { MaybeUninit { uninit: () }.init }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered uninitialized raw pointer + | + = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the raw bytes of the constant (size: 4, align: 4) { + __ __ __ __ │ ░░░░ + } + +error[E0080]: it is undefined behavior to use this value + --> $DIR/ub-ref-ptr.rs:47:1 + | +LL | const UNINIT_FN_PTR: fn() = unsafe { MaybeUninit { uninit: () }.init }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered uninitialized bytes, but expected a function pointer + | + = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the raw bytes of the constant (size: 4, align: 4) { + __ __ __ __ │ ░░░░ + } + +error: aborting due to 11 previous errors + +For more information about this error, try `rustc --explain E0080`. diff --git a/src/test/ui/consts/const-eval/ub-ref-ptr.64bit.stderr b/src/test/ui/consts/const-eval/ub-ref-ptr.64bit.stderr new file mode 100644 index 0000000000000..8bd4637a80be4 --- /dev/null +++ b/src/test/ui/consts/const-eval/ub-ref-ptr.64bit.stderr @@ -0,0 +1,124 @@ +error[E0080]: it is undefined behavior to use this value + --> $DIR/ub-ref-ptr.rs:13:1 + | +LL | const UNALIGNED: &u16 = unsafe { mem::transmute(&[0u8; 4]) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered an unaligned reference (required 2 byte alignment but found 1) + | + = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the raw bytes of the constant (size: 8, align: 8) { + ╾───────alloc2────────╼ │ ╾──────╼ + } + +error[E0080]: it is undefined behavior to use this value + --> $DIR/ub-ref-ptr.rs:17:1 + | +LL | const UNALIGNED_BOX: Box<u16> = unsafe { mem::transmute(&[0u8; 4]) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered an unaligned box (required 2 byte alignment but found 1) + | + = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the raw bytes of the constant (size: 8, align: 8) { + ╾───────alloc6────────╼ │ ╾──────╼ + } + +error[E0080]: it is undefined behavior to use this value + --> $DIR/ub-ref-ptr.rs:21:1 + | +LL | const NULL: &u16 = unsafe { mem::transmute(0usize) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered a NULL reference + | + = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the raw bytes of the constant (size: 8, align: 8) { + 00 00 00 00 00 00 00 00 │ ........ + } + +error[E0080]: it is undefined behavior to use this value + --> $DIR/ub-ref-ptr.rs:24:1 + | +LL | const NULL_BOX: Box<u16> = unsafe { mem::transmute(0usize) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered a NULL box + | + = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the raw bytes of the constant (size: 8, align: 8) { + 00 00 00 00 00 00 00 00 │ ........ + } + +error[E0080]: it is undefined behavior to use this value + --> $DIR/ub-ref-ptr.rs:30:1 + | +LL | const REF_AS_USIZE: usize = unsafe { mem::transmute(&0) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered pointer to alloc14, but expected initialized plain (non-pointer) bytes + | + = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the raw bytes of the constant (size: 8, align: 8) { + ╾───────alloc14───────╼ │ ╾──────╼ + } + +error[E0080]: it is undefined behavior to use this value + --> $DIR/ub-ref-ptr.rs:33:1 + | +LL | const REF_AS_USIZE_SLICE: &[usize] = &[unsafe { mem::transmute(&0) }]; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered a pointer at .<deref>, but expected plain (non-pointer) bytes + | + = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the raw bytes of the constant (size: 8, align: 8) { + ╾───────alloc20───────╼ │ ╾──────╼ + } + +error[E0080]: it is undefined behavior to use this value + --> $DIR/ub-ref-ptr.rs:36:1 + | +LL | const REF_AS_USIZE_BOX_SLICE: Box<[usize]> = unsafe { mem::transmute::<&[usize], _>(&[mem::transmute(&0)]) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered a pointer at .<deref>, but expected plain (non-pointer) bytes + | + = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the raw bytes of the constant (size: 8, align: 8) { + ╾───────alloc25───────╼ │ ╾──────╼ + } + +error[E0080]: it is undefined behavior to use this value + --> $DIR/ub-ref-ptr.rs:39:1 + | +LL | const USIZE_AS_REF: &'static u8 = unsafe { mem::transmute(1337usize) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered a dangling reference (created from integer) + | + = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the raw bytes of the constant (size: 8, align: 8) { + 39 05 00 00 00 00 00 00 │ 9....... + } + +error[E0080]: it is undefined behavior to use this value + --> $DIR/ub-ref-ptr.rs:42:1 + | +LL | const USIZE_AS_BOX: Box<u8> = unsafe { mem::transmute(1337usize) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered a dangling box (created from integer) + | + = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the raw bytes of the constant (size: 8, align: 8) { + 39 05 00 00 00 00 00 00 │ 9....... + } + +error[E0080]: it is undefined behavior to use this value + --> $DIR/ub-ref-ptr.rs:45:1 + | +LL | const UNINIT_PTR: *const i32 = unsafe { MaybeUninit { uninit: () }.init }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered uninitialized raw pointer + | + = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the raw bytes of the constant (size: 8, align: 8) { + __ __ __ __ __ __ __ __ │ ░░░░░░░░ + } + +error[E0080]: it is undefined behavior to use this value + --> $DIR/ub-ref-ptr.rs:47:1 + | +LL | const UNINIT_FN_PTR: fn() = unsafe { MaybeUninit { uninit: () }.init }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered uninitialized bytes, but expected a function pointer + | + = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the raw bytes of the constant (size: 8, align: 8) { + __ __ __ __ __ __ __ __ │ ░░░░░░░░ + } + +error: aborting due to 11 previous errors + +For more information about this error, try `rustc --explain E0080`. diff --git a/src/test/ui/consts/const-eval/ub-ref-ptr.rs b/src/test/ui/consts/const-eval/ub-ref-ptr.rs index f6075987d178d..8857ae4caac83 100644 --- a/src/test/ui/consts/const-eval/ub-ref-ptr.rs +++ b/src/test/ui/consts/const-eval/ub-ref-ptr.rs @@ -1,4 +1,5 @@ // ignore-tidy-linelength +// stderr-per-bitwidth #![allow(const_err, invalid_value)] // make sure we cannot allow away the errors tested here use std::mem; diff --git a/src/test/ui/consts/const-eval/ub-ref-ptr.stderr b/src/test/ui/consts/const-eval/ub-ref-ptr.stderr deleted file mode 100644 index 7d76c5cb43fe0..0000000000000 --- a/src/test/ui/consts/const-eval/ub-ref-ptr.stderr +++ /dev/null @@ -1,91 +0,0 @@ -error[E0080]: it is undefined behavior to use this value - --> $DIR/ub-ref-ptr.rs:12:1 - | -LL | const UNALIGNED: &u16 = unsafe { mem::transmute(&[0u8; 4]) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered an unaligned reference (required 2 byte alignment but found 1) - | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. - -error[E0080]: it is undefined behavior to use this value - --> $DIR/ub-ref-ptr.rs:16:1 - | -LL | const UNALIGNED_BOX: Box<u16> = unsafe { mem::transmute(&[0u8; 4]) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered an unaligned box (required 2 byte alignment but found 1) - | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. - -error[E0080]: it is undefined behavior to use this value - --> $DIR/ub-ref-ptr.rs:20:1 - | -LL | const NULL: &u16 = unsafe { mem::transmute(0usize) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered a NULL reference - | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. - -error[E0080]: it is undefined behavior to use this value - --> $DIR/ub-ref-ptr.rs:23:1 - | -LL | const NULL_BOX: Box<u16> = unsafe { mem::transmute(0usize) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered a NULL box - | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. - -error[E0080]: it is undefined behavior to use this value - --> $DIR/ub-ref-ptr.rs:29:1 - | -LL | const REF_AS_USIZE: usize = unsafe { mem::transmute(&0) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered pointer to alloc14, but expected initialized plain (non-pointer) bytes - | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. - -error[E0080]: it is undefined behavior to use this value - --> $DIR/ub-ref-ptr.rs:32:1 - | -LL | const REF_AS_USIZE_SLICE: &[usize] = &[unsafe { mem::transmute(&0) }]; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered a pointer at .<deref>, but expected plain (non-pointer) bytes - | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. - -error[E0080]: it is undefined behavior to use this value - --> $DIR/ub-ref-ptr.rs:35:1 - | -LL | const REF_AS_USIZE_BOX_SLICE: Box<[usize]> = unsafe { mem::transmute::<&[usize], _>(&[mem::transmute(&0)]) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered a pointer at .<deref>, but expected plain (non-pointer) bytes - | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. - -error[E0080]: it is undefined behavior to use this value - --> $DIR/ub-ref-ptr.rs:38:1 - | -LL | const USIZE_AS_REF: &'static u8 = unsafe { mem::transmute(1337usize) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered a dangling reference (created from integer) - | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. - -error[E0080]: it is undefined behavior to use this value - --> $DIR/ub-ref-ptr.rs:41:1 - | -LL | const USIZE_AS_BOX: Box<u8> = unsafe { mem::transmute(1337usize) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered a dangling box (created from integer) - | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. - -error[E0080]: it is undefined behavior to use this value - --> $DIR/ub-ref-ptr.rs:44:1 - | -LL | const UNINIT_PTR: *const i32 = unsafe { MaybeUninit { uninit: () }.init }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered uninitialized raw pointer - | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. - -error[E0080]: it is undefined behavior to use this value - --> $DIR/ub-ref-ptr.rs:46:1 - | -LL | const UNINIT_FN_PTR: fn() = unsafe { MaybeUninit { uninit: () }.init }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered uninitialized bytes, but expected a function pointer - | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. - -error: aborting due to 11 previous errors - -For more information about this error, try `rustc --explain E0080`. diff --git a/src/test/ui/consts/const-eval/ub-uninhabit.32bit.stderr b/src/test/ui/consts/const-eval/ub-uninhabit.32bit.stderr new file mode 100644 index 0000000000000..4155a8a2ef9f3 --- /dev/null +++ b/src/test/ui/consts/const-eval/ub-uninhabit.32bit.stderr @@ -0,0 +1,32 @@ +error[E0080]: it is undefined behavior to use this value + --> $DIR/ub-uninhabit.rs:15:1 + | +LL | const BAD_BAD_BAD: Bar = unsafe { MaybeUninit { uninit: () }.init }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered a value of uninhabited type Bar + | + = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the raw bytes of the constant (size: 0, align: 1) {} + +error[E0080]: it is undefined behavior to use this value + --> $DIR/ub-uninhabit.rs:18:1 + | +LL | const BAD_BAD_REF: &Bar = unsafe { mem::transmute(1usize) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered a value of uninhabited type Bar at .<deref> + | + = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the raw bytes of the constant (size: 4, align: 4) { + 01 00 00 00 │ .... + } + +error[E0080]: it is undefined behavior to use this value + --> $DIR/ub-uninhabit.rs:21:1 + | +LL | const BAD_BAD_ARRAY: [Bar; 1] = unsafe { MaybeUninit { uninit: () }.init }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered a value of uninhabited type Bar at [0] + | + = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the raw bytes of the constant (size: 0, align: 1) {} + +error: aborting due to 3 previous errors + +For more information about this error, try `rustc --explain E0080`. diff --git a/src/test/ui/consts/const-eval/ub-uninhabit.64bit.stderr b/src/test/ui/consts/const-eval/ub-uninhabit.64bit.stderr new file mode 100644 index 0000000000000..def795c7f563c --- /dev/null +++ b/src/test/ui/consts/const-eval/ub-uninhabit.64bit.stderr @@ -0,0 +1,32 @@ +error[E0080]: it is undefined behavior to use this value + --> $DIR/ub-uninhabit.rs:15:1 + | +LL | const BAD_BAD_BAD: Bar = unsafe { MaybeUninit { uninit: () }.init }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered a value of uninhabited type Bar + | + = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the raw bytes of the constant (size: 0, align: 1) {} + +error[E0080]: it is undefined behavior to use this value + --> $DIR/ub-uninhabit.rs:18:1 + | +LL | const BAD_BAD_REF: &Bar = unsafe { mem::transmute(1usize) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered a value of uninhabited type Bar at .<deref> + | + = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the raw bytes of the constant (size: 8, align: 8) { + 01 00 00 00 00 00 00 00 │ ........ + } + +error[E0080]: it is undefined behavior to use this value + --> $DIR/ub-uninhabit.rs:21:1 + | +LL | const BAD_BAD_ARRAY: [Bar; 1] = unsafe { MaybeUninit { uninit: () }.init }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered a value of uninhabited type Bar at [0] + | + = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the raw bytes of the constant (size: 0, align: 1) {} + +error: aborting due to 3 previous errors + +For more information about this error, try `rustc --explain E0080`. diff --git a/src/test/ui/consts/const-eval/ub-uninhabit.rs b/src/test/ui/consts/const-eval/ub-uninhabit.rs index b81bca3849431..33fbd14c4726e 100644 --- a/src/test/ui/consts/const-eval/ub-uninhabit.rs +++ b/src/test/ui/consts/const-eval/ub-uninhabit.rs @@ -1,3 +1,4 @@ +// stderr-per-bitwidth #![allow(const_err)] // make sure we cannot allow away the errors tested here use std::mem; diff --git a/src/test/ui/consts/const-eval/ub-uninhabit.stderr b/src/test/ui/consts/const-eval/ub-uninhabit.stderr deleted file mode 100644 index 16f5316a44230..0000000000000 --- a/src/test/ui/consts/const-eval/ub-uninhabit.stderr +++ /dev/null @@ -1,27 +0,0 @@ -error[E0080]: it is undefined behavior to use this value - --> $DIR/ub-uninhabit.rs:14:1 - | -LL | const BAD_BAD_BAD: Bar = unsafe { MaybeUninit { uninit: () }.init }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered a value of uninhabited type Bar - | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. - -error[E0080]: it is undefined behavior to use this value - --> $DIR/ub-uninhabit.rs:17:1 - | -LL | const BAD_BAD_REF: &Bar = unsafe { mem::transmute(1usize) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered a value of uninhabited type Bar at .<deref> - | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. - -error[E0080]: it is undefined behavior to use this value - --> $DIR/ub-uninhabit.rs:20:1 - | -LL | const BAD_BAD_ARRAY: [Bar; 1] = unsafe { MaybeUninit { uninit: () }.init }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered a value of uninhabited type Bar at [0] - | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. - -error: aborting due to 3 previous errors - -For more information about this error, try `rustc --explain E0080`. diff --git a/src/test/ui/consts/const-eval/ub-upvars.32bit.stderr b/src/test/ui/consts/const-eval/ub-upvars.32bit.stderr new file mode 100644 index 0000000000000..62756518a00e7 --- /dev/null +++ b/src/test/ui/consts/const-eval/ub-upvars.32bit.stderr @@ -0,0 +1,18 @@ +error[E0080]: it is undefined behavior to use this value + --> $DIR/ub-upvars.rs:6:1 + | +LL | / const BAD_UPVAR: &dyn FnOnce() = &{ +LL | | let bad_ref: &'static u16 = unsafe { mem::transmute(0usize) }; +LL | | let another_var = 13; +LL | | move || { let _ = bad_ref; let _ = another_var; } +LL | | }; + | |__^ type validation failed: encountered a NULL reference at .<deref>.<dyn-downcast>.<captured-var(bad_ref)> + | + = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the raw bytes of the constant (size: 8, align: 4) { + ╾─alloc2──╼ ╾─alloc3──╼ │ ╾──╼╾──╼ + } + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0080`. diff --git a/src/test/ui/consts/const-eval/ub-upvars.64bit.stderr b/src/test/ui/consts/const-eval/ub-upvars.64bit.stderr new file mode 100644 index 0000000000000..e9fabd9a3bcc4 --- /dev/null +++ b/src/test/ui/consts/const-eval/ub-upvars.64bit.stderr @@ -0,0 +1,18 @@ +error[E0080]: it is undefined behavior to use this value + --> $DIR/ub-upvars.rs:6:1 + | +LL | / const BAD_UPVAR: &dyn FnOnce() = &{ +LL | | let bad_ref: &'static u16 = unsafe { mem::transmute(0usize) }; +LL | | let another_var = 13; +LL | | move || { let _ = bad_ref; let _ = another_var; } +LL | | }; + | |__^ type validation failed: encountered a NULL reference at .<deref>.<dyn-downcast>.<captured-var(bad_ref)> + | + = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the raw bytes of the constant (size: 16, align: 8) { + ╾───────alloc2────────╼ ╾───────alloc3────────╼ │ ╾──────╼╾──────╼ + } + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0080`. diff --git a/src/test/ui/consts/const-eval/ub-upvars.rs b/src/test/ui/consts/const-eval/ub-upvars.rs index 5d19276557eb6..57dd7b9e58132 100644 --- a/src/test/ui/consts/const-eval/ub-upvars.rs +++ b/src/test/ui/consts/const-eval/ub-upvars.rs @@ -1,3 +1,4 @@ +// stderr-per-bitwidth #![allow(const_err, invalid_value)] // make sure we cannot allow away the errors tested here use std::mem; diff --git a/src/test/ui/consts/const-eval/ub-upvars.stderr b/src/test/ui/consts/const-eval/ub-upvars.stderr deleted file mode 100644 index afd6c9035cabf..0000000000000 --- a/src/test/ui/consts/const-eval/ub-upvars.stderr +++ /dev/null @@ -1,15 +0,0 @@ -error[E0080]: it is undefined behavior to use this value - --> $DIR/ub-upvars.rs:5:1 - | -LL | / const BAD_UPVAR: &dyn FnOnce() = &{ -LL | | let bad_ref: &'static u16 = unsafe { mem::transmute(0usize) }; -LL | | let another_var = 13; -LL | | move || { let _ = bad_ref; let _ = another_var; } -LL | | }; - | |__^ type validation failed: encountered a NULL reference at .<deref>.<dyn-downcast>.<captured-var(bad_ref)> - | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. - -error: aborting due to previous error - -For more information about this error, try `rustc --explain E0080`. diff --git a/src/test/ui/consts/const-eval/ub-wide-ptr.32bit.stderr b/src/test/ui/consts/const-eval/ub-wide-ptr.32bit.stderr new file mode 100644 index 0000000000000..7ca5c647d8868 --- /dev/null +++ b/src/test/ui/consts/const-eval/ub-wide-ptr.32bit.stderr @@ -0,0 +1,309 @@ +error[E0080]: it is undefined behavior to use this value + --> $DIR/ub-wide-ptr.rs:38:1 + | +LL | const STR_TOO_LONG: &str = unsafe { mem::transmute((&42u8, 999usize)) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered a dangling reference (going beyond the bounds of its allocation) + | + = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the raw bytes of the constant (size: 8, align: 4) { + ╾─allocN──╼ e7 03 00 00 │ ╾──╼.... + } + +error[E0080]: it is undefined behavior to use this value + --> $DIR/ub-wide-ptr.rs:40:1 + | +LL | const NESTED_STR_MUCH_TOO_LONG: (&str,) = (unsafe { mem::transmute((&42, usize::MAX)) },); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered invalid reference metadata: slice is bigger than largest supported object at .0 + | + = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the raw bytes of the constant (size: 8, align: 4) { + ╾─allocN─╼ ff ff ff ff │ ╾──╼.... + } + +error[E0080]: it is undefined behavior to use this value + --> $DIR/ub-wide-ptr.rs:43:1 + | +LL | const STR_LENGTH_PTR: &str = unsafe { mem::transmute((&42u8, &3)) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered non-integer slice length in wide pointer + | + = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the raw bytes of the constant (size: 8, align: 4) { + ╾─allocN─╼ ╾─allocN─╼ │ ╾──╼╾──╼ + } + +error[E0080]: it is undefined behavior to use this value + --> $DIR/ub-wide-ptr.rs:46:1 + | +LL | const MY_STR_LENGTH_PTR: &MyStr = unsafe { mem::transmute((&42u8, &3)) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered non-integer slice length in wide pointer + | + = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the raw bytes of the constant (size: 8, align: 4) { + ╾─allocN─╼ ╾─allocN─╼ │ ╾──╼╾──╼ + } + +error[E0080]: it is undefined behavior to use this value + --> $DIR/ub-wide-ptr.rs:48:1 + | +LL | const MY_STR_MUCH_TOO_LONG: &MyStr = unsafe { mem::transmute((&42u8, usize::MAX)) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered invalid reference metadata: slice is bigger than largest supported object + | + = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the raw bytes of the constant (size: 8, align: 4) { + ╾─allocN─╼ ff ff ff ff │ ╾──╼.... + } + +error[E0080]: it is undefined behavior to use this value + --> $DIR/ub-wide-ptr.rs:52:1 + | +LL | const STR_NO_INIT: &str = unsafe { mem::transmute::<&[_], _>(&[MaybeUninit::<u8> { uninit: () }]) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered uninitialized data in `str` at .<deref> + | + = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the raw bytes of the constant (size: 8, align: 4) { + ╾─allocN─╼ 01 00 00 00 │ ╾──╼.... + } + +error[E0080]: it is undefined behavior to use this value + --> $DIR/ub-wide-ptr.rs:55:1 + | +LL | const MYSTR_NO_INIT: &MyStr = unsafe { mem::transmute::<&[_], _>(&[MaybeUninit::<u8> { uninit: () }]) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered uninitialized data in `str` at .<deref>.0 + | + = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the raw bytes of the constant (size: 8, align: 4) { + ╾─allocN─╼ 01 00 00 00 │ ╾──╼.... + } + +error[E0080]: it is undefined behavior to use this value + --> $DIR/ub-wide-ptr.rs:62:1 + | +LL | / const SLICE_LENGTH_UNINIT: &[u8] = unsafe { +LL | | +LL | | let uninit_len = MaybeUninit::<usize> { uninit: () }; +LL | | mem::transmute((42, uninit_len)) +LL | | }; + | |__^ type validation failed: encountered uninitialized reference + | + = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the raw bytes of the constant (size: 8, align: 4) { + 2a 00 00 00 __ __ __ __ │ *...░░░░ + } + +error[E0080]: it is undefined behavior to use this value + --> $DIR/ub-wide-ptr.rs:68:1 + | +LL | const SLICE_TOO_LONG: &[u8] = unsafe { mem::transmute((&42u8, 999usize)) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered a dangling reference (going beyond the bounds of its allocation) + | + = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the raw bytes of the constant (size: 8, align: 4) { + ╾─allocN─╼ e7 03 00 00 │ ╾──╼.... + } + +error[E0080]: it is undefined behavior to use this value + --> $DIR/ub-wide-ptr.rs:71:1 + | +LL | const SLICE_LENGTH_PTR: &[u8] = unsafe { mem::transmute((&42u8, &3)) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered non-integer slice length in wide pointer + | + = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the raw bytes of the constant (size: 8, align: 4) { + ╾─allocN─╼ ╾─allocN─╼ │ ╾──╼╾──╼ + } + +error[E0080]: it is undefined behavior to use this value + --> $DIR/ub-wide-ptr.rs:74:1 + | +LL | const SLICE_TOO_LONG_BOX: Box<[u8]> = unsafe { mem::transmute((&42u8, 999usize)) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered a dangling box (going beyond the bounds of its allocation) + | + = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the raw bytes of the constant (size: 8, align: 4) { + ╾─allocN─╼ e7 03 00 00 │ ╾──╼.... + } + +error[E0080]: it is undefined behavior to use this value + --> $DIR/ub-wide-ptr.rs:77:1 + | +LL | const SLICE_LENGTH_PTR_BOX: Box<[u8]> = unsafe { mem::transmute((&42u8, &3)) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered non-integer slice length in wide pointer + | + = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the raw bytes of the constant (size: 8, align: 4) { + ╾─allocN─╼ ╾─allocN─╼ │ ╾──╼╾──╼ + } + +error[E0080]: it is undefined behavior to use this value + --> $DIR/ub-wide-ptr.rs:81:1 + | +LL | const SLICE_CONTENT_INVALID: &[bool] = &[unsafe { mem::transmute(3u8) }]; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered 0x03 at .<deref>[0], but expected a boolean + | + = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the raw bytes of the constant (size: 4, align: 4) { + ╾─allocN─╼ │ ╾──╼ + } + +error[E0080]: it is undefined behavior to use this value + --> $DIR/ub-wide-ptr.rs:87:1 + | +LL | const MYSLICE_PREFIX_BAD: &MySliceBool = &MySlice(unsafe { mem::transmute(3u8) }, [false]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered 0x03 at .<deref>.0, but expected a boolean + | + = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the raw bytes of the constant (size: 4, align: 4) { + ╾─allocN─╼ │ ╾──╼ + } + +error[E0080]: it is undefined behavior to use this value + --> $DIR/ub-wide-ptr.rs:90:1 + | +LL | const MYSLICE_SUFFIX_BAD: &MySliceBool = &MySlice(true, [unsafe { mem::transmute(3u8) }]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered 0x03 at .<deref>.1[0], but expected a boolean + | + = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the raw bytes of the constant (size: 4, align: 4) { + ╾─allocN─╼ │ ╾──╼ + } + +error[E0080]: it is undefined behavior to use this value + --> $DIR/ub-wide-ptr.rs:97:1 + | +LL | / const RAW_SLICE_LENGTH_UNINIT: *const [u8] = unsafe { +LL | | +LL | | let uninit_len = MaybeUninit::<usize> { uninit: () }; +LL | | mem::transmute((42, uninit_len)) +LL | | }; + | |__^ type validation failed: encountered uninitialized raw pointer + | + = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the raw bytes of the constant (size: 8, align: 4) { + 2a 00 00 00 __ __ __ __ │ *...░░░░ + } + +error[E0080]: it is undefined behavior to use this value + --> $DIR/ub-wide-ptr.rs:105:1 + | +LL | const TRAIT_OBJ_SHORT_VTABLE_1: W<&dyn Trait> = unsafe { mem::transmute(W((&92u8, &3u8))) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered too small vtable at .0 + | + = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the raw bytes of the constant (size: 8, align: 4) { + ╾allocN─╼ ╾allocN─╼ │ ╾──╼╾──╼ + } + +error[E0080]: it is undefined behavior to use this value + --> $DIR/ub-wide-ptr.rs:108:1 + | +LL | const TRAIT_OBJ_SHORT_VTABLE_2: W<&dyn Trait> = unsafe { mem::transmute(W((&92u8, &3u64))) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered too small vtable at .0 + | + = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the raw bytes of the constant (size: 8, align: 4) { + ╾allocN─╼ ╾allocN─╼ │ ╾──╼╾──╼ + } + +error[E0080]: it is undefined behavior to use this value + --> $DIR/ub-wide-ptr.rs:111:1 + | +LL | const TRAIT_OBJ_INT_VTABLE: W<&dyn Trait> = unsafe { mem::transmute(W((&92u8, 4usize))) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered dangling vtable pointer in wide pointer at .0 + | + = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the raw bytes of the constant (size: 8, align: 4) { + ╾allocN─╼ 04 00 00 00 │ ╾──╼.... + } + +error[E0080]: it is undefined behavior to use this value + --> $DIR/ub-wide-ptr.rs:113:1 + | +LL | const TRAIT_OBJ_UNALIGNED_VTABLE: &dyn Trait = unsafe { mem::transmute((&92u8, &[0u8; 128])) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered unaligned vtable pointer in wide pointer + | + = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the raw bytes of the constant (size: 8, align: 4) { + ╾allocN─╼ ╾allocN─╼ │ ╾──╼╾──╼ + } + +error[E0080]: it is undefined behavior to use this value + --> $DIR/ub-wide-ptr.rs:115:1 + | +LL | const TRAIT_OBJ_BAD_DROP_FN_NULL: &dyn Trait = unsafe { mem::transmute((&92u8, &[0usize; 8])) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered invalid drop function pointer in vtable (not pointing to a function) + | + = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the raw bytes of the constant (size: 8, align: 4) { + ╾allocN─╼ ╾allocN─╼ │ ╾──╼╾──╼ + } + +error[E0080]: it is undefined behavior to use this value + --> $DIR/ub-wide-ptr.rs:117:1 + | +LL | const TRAIT_OBJ_BAD_DROP_FN_INT: &dyn Trait = unsafe { mem::transmute((&92u8, &[1usize; 8])) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered invalid drop function pointer in vtable (not pointing to a function) + | + = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the raw bytes of the constant (size: 8, align: 4) { + ╾allocN─╼ ╾allocN─╼ │ ╾──╼╾──╼ + } + +error[E0080]: it is undefined behavior to use this value + --> $DIR/ub-wide-ptr.rs:119:1 + | +LL | const TRAIT_OBJ_BAD_DROP_FN_NOT_FN_PTR: W<&dyn Trait> = unsafe { mem::transmute(W((&92u8, &[&42u8; 8]))) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered invalid drop function pointer in vtable (not pointing to a function) at .0 + | + = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the raw bytes of the constant (size: 8, align: 4) { + ╾allocN─╼ ╾allocN─╼ │ ╾──╼╾──╼ + } + +error[E0080]: it is undefined behavior to use this value + --> $DIR/ub-wide-ptr.rs:123:1 + | +LL | const TRAIT_OBJ_CONTENT_INVALID: &dyn Trait = unsafe { mem::transmute::<_, &bool>(&3u8) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered 0x03 at .<deref>.<dyn-downcast>, but expected a boolean + | + = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the raw bytes of the constant (size: 8, align: 4) { + ╾allocN─╼ ╾allocN─╼ │ ╾──╼╾──╼ + } + +error[E0080]: it is undefined behavior to use this value + --> $DIR/ub-wide-ptr.rs:127:1 + | +LL | const RAW_TRAIT_OBJ_VTABLE_NULL: *const dyn Trait = unsafe { mem::transmute((&92u8, 0usize)) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered dangling vtable pointer in wide pointer + | + = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the raw bytes of the constant (size: 8, align: 4) { + ╾allocN─╼ 00 00 00 00 │ ╾──╼.... + } + +error[E0080]: it is undefined behavior to use this value + --> $DIR/ub-wide-ptr.rs:129:1 + | +LL | const RAW_TRAIT_OBJ_VTABLE_INVALID: *const dyn Trait = unsafe { mem::transmute((&92u8, &3u64)) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered too small vtable + | + = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the raw bytes of the constant (size: 8, align: 4) { + ╾allocN─╼ ╾allocN─╼ │ ╾──╼╾──╼ + } + +error[E0080]: could not evaluate static initializer + --> $DIR/ub-wide-ptr.rs:135:5 + | +LL | mem::transmute::<_, &dyn Trait>((&92u8, 0usize)) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ inbounds test failed: 0x0 is not a valid pointer + +error[E0080]: could not evaluate static initializer + --> $DIR/ub-wide-ptr.rs:139:5 + | +LL | mem::transmute::<_, &dyn Trait>((&92u8, &3u64)) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ memory access failed: pointer must be in-bounds at offset N, but is outside bounds of allocN which has size N + +error: aborting due to 28 previous errors + +For more information about this error, try `rustc --explain E0080`. diff --git a/src/test/ui/consts/const-eval/ub-wide-ptr.64bit.stderr b/src/test/ui/consts/const-eval/ub-wide-ptr.64bit.stderr new file mode 100644 index 0000000000000..e42c65a1517d2 --- /dev/null +++ b/src/test/ui/consts/const-eval/ub-wide-ptr.64bit.stderr @@ -0,0 +1,309 @@ +error[E0080]: it is undefined behavior to use this value + --> $DIR/ub-wide-ptr.rs:38:1 + | +LL | const STR_TOO_LONG: &str = unsafe { mem::transmute((&42u8, 999usize)) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered a dangling reference (going beyond the bounds of its allocation) + | + = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the raw bytes of the constant (size: 16, align: 8) { + ╾───────allocN────────╼ e7 03 00 00 00 00 00 00 │ ╾──────╼........ + } + +error[E0080]: it is undefined behavior to use this value + --> $DIR/ub-wide-ptr.rs:40:1 + | +LL | const NESTED_STR_MUCH_TOO_LONG: (&str,) = (unsafe { mem::transmute((&42, usize::MAX)) },); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered invalid reference metadata: slice is bigger than largest supported object at .0 + | + = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the raw bytes of the constant (size: 16, align: 8) { + ╾───────allocN───────╼ ff ff ff ff ff ff ff ff │ ╾──────╼........ + } + +error[E0080]: it is undefined behavior to use this value + --> $DIR/ub-wide-ptr.rs:43:1 + | +LL | const STR_LENGTH_PTR: &str = unsafe { mem::transmute((&42u8, &3)) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered non-integer slice length in wide pointer + | + = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the raw bytes of the constant (size: 16, align: 8) { + ╾───────allocN───────╼ ╾───────allocN───────╼ │ ╾──────╼╾──────╼ + } + +error[E0080]: it is undefined behavior to use this value + --> $DIR/ub-wide-ptr.rs:46:1 + | +LL | const MY_STR_LENGTH_PTR: &MyStr = unsafe { mem::transmute((&42u8, &3)) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered non-integer slice length in wide pointer + | + = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the raw bytes of the constant (size: 16, align: 8) { + ╾───────allocN───────╼ ╾───────allocN───────╼ │ ╾──────╼╾──────╼ + } + +error[E0080]: it is undefined behavior to use this value + --> $DIR/ub-wide-ptr.rs:48:1 + | +LL | const MY_STR_MUCH_TOO_LONG: &MyStr = unsafe { mem::transmute((&42u8, usize::MAX)) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered invalid reference metadata: slice is bigger than largest supported object + | + = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the raw bytes of the constant (size: 16, align: 8) { + ╾───────allocN───────╼ ff ff ff ff ff ff ff ff │ ╾──────╼........ + } + +error[E0080]: it is undefined behavior to use this value + --> $DIR/ub-wide-ptr.rs:52:1 + | +LL | const STR_NO_INIT: &str = unsafe { mem::transmute::<&[_], _>(&[MaybeUninit::<u8> { uninit: () }]) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered uninitialized data in `str` at .<deref> + | + = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the raw bytes of the constant (size: 16, align: 8) { + ╾───────allocN───────╼ 01 00 00 00 00 00 00 00 │ ╾──────╼........ + } + +error[E0080]: it is undefined behavior to use this value + --> $DIR/ub-wide-ptr.rs:55:1 + | +LL | const MYSTR_NO_INIT: &MyStr = unsafe { mem::transmute::<&[_], _>(&[MaybeUninit::<u8> { uninit: () }]) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered uninitialized data in `str` at .<deref>.0 + | + = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the raw bytes of the constant (size: 16, align: 8) { + ╾───────allocN───────╼ 01 00 00 00 00 00 00 00 │ ╾──────╼........ + } + +error[E0080]: it is undefined behavior to use this value + --> $DIR/ub-wide-ptr.rs:62:1 + | +LL | / const SLICE_LENGTH_UNINIT: &[u8] = unsafe { +LL | | +LL | | let uninit_len = MaybeUninit::<usize> { uninit: () }; +LL | | mem::transmute((42, uninit_len)) +LL | | }; + | |__^ type validation failed: encountered uninitialized reference + | + = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the raw bytes of the constant (size: 16, align: 8) { + __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ │ ░░░░░░░░░░░░░░░░ + } + +error[E0080]: it is undefined behavior to use this value + --> $DIR/ub-wide-ptr.rs:68:1 + | +LL | const SLICE_TOO_LONG: &[u8] = unsafe { mem::transmute((&42u8, 999usize)) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered a dangling reference (going beyond the bounds of its allocation) + | + = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the raw bytes of the constant (size: 16, align: 8) { + ╾───────allocN───────╼ e7 03 00 00 00 00 00 00 │ ╾──────╼........ + } + +error[E0080]: it is undefined behavior to use this value + --> $DIR/ub-wide-ptr.rs:71:1 + | +LL | const SLICE_LENGTH_PTR: &[u8] = unsafe { mem::transmute((&42u8, &3)) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered non-integer slice length in wide pointer + | + = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the raw bytes of the constant (size: 16, align: 8) { + ╾───────allocN───────╼ ╾───────allocN───────╼ │ ╾──────╼╾──────╼ + } + +error[E0080]: it is undefined behavior to use this value + --> $DIR/ub-wide-ptr.rs:74:1 + | +LL | const SLICE_TOO_LONG_BOX: Box<[u8]> = unsafe { mem::transmute((&42u8, 999usize)) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered a dangling box (going beyond the bounds of its allocation) + | + = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the raw bytes of the constant (size: 16, align: 8) { + ╾───────allocN───────╼ e7 03 00 00 00 00 00 00 │ ╾──────╼........ + } + +error[E0080]: it is undefined behavior to use this value + --> $DIR/ub-wide-ptr.rs:77:1 + | +LL | const SLICE_LENGTH_PTR_BOX: Box<[u8]> = unsafe { mem::transmute((&42u8, &3)) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered non-integer slice length in wide pointer + | + = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the raw bytes of the constant (size: 16, align: 8) { + ╾───────allocN───────╼ ╾───────allocN───────╼ │ ╾──────╼╾──────╼ + } + +error[E0080]: it is undefined behavior to use this value + --> $DIR/ub-wide-ptr.rs:81:1 + | +LL | const SLICE_CONTENT_INVALID: &[bool] = &[unsafe { mem::transmute(3u8) }]; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered 0x03 at .<deref>[0], but expected a boolean + | + = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the raw bytes of the constant (size: 8, align: 8) { + ╾───────allocN───────╼ │ ╾──────╼ + } + +error[E0080]: it is undefined behavior to use this value + --> $DIR/ub-wide-ptr.rs:87:1 + | +LL | const MYSLICE_PREFIX_BAD: &MySliceBool = &MySlice(unsafe { mem::transmute(3u8) }, [false]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered 0x03 at .<deref>.0, but expected a boolean + | + = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the raw bytes of the constant (size: 8, align: 8) { + ╾───────allocN───────╼ │ ╾──────╼ + } + +error[E0080]: it is undefined behavior to use this value + --> $DIR/ub-wide-ptr.rs:90:1 + | +LL | const MYSLICE_SUFFIX_BAD: &MySliceBool = &MySlice(true, [unsafe { mem::transmute(3u8) }]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered 0x03 at .<deref>.1[0], but expected a boolean + | + = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the raw bytes of the constant (size: 8, align: 8) { + ╾───────allocN───────╼ │ ╾──────╼ + } + +error[E0080]: it is undefined behavior to use this value + --> $DIR/ub-wide-ptr.rs:97:1 + | +LL | / const RAW_SLICE_LENGTH_UNINIT: *const [u8] = unsafe { +LL | | +LL | | let uninit_len = MaybeUninit::<usize> { uninit: () }; +LL | | mem::transmute((42, uninit_len)) +LL | | }; + | |__^ type validation failed: encountered uninitialized raw pointer + | + = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the raw bytes of the constant (size: 16, align: 8) { + __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ │ ░░░░░░░░░░░░░░░░ + } + +error[E0080]: it is undefined behavior to use this value + --> $DIR/ub-wide-ptr.rs:105:1 + | +LL | const TRAIT_OBJ_SHORT_VTABLE_1: W<&dyn Trait> = unsafe { mem::transmute(W((&92u8, &3u8))) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered too small vtable at .0 + | + = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the raw bytes of the constant (size: 16, align: 8) { + ╾──────allocN───────╼ ╾──────allocN───────╼ │ ╾──────╼╾──────╼ + } + +error[E0080]: it is undefined behavior to use this value + --> $DIR/ub-wide-ptr.rs:108:1 + | +LL | const TRAIT_OBJ_SHORT_VTABLE_2: W<&dyn Trait> = unsafe { mem::transmute(W((&92u8, &3u64))) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered too small vtable at .0 + | + = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the raw bytes of the constant (size: 16, align: 8) { + ╾──────allocN───────╼ ╾──────allocN───────╼ │ ╾──────╼╾──────╼ + } + +error[E0080]: it is undefined behavior to use this value + --> $DIR/ub-wide-ptr.rs:111:1 + | +LL | const TRAIT_OBJ_INT_VTABLE: W<&dyn Trait> = unsafe { mem::transmute(W((&92u8, 4usize))) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered dangling vtable pointer in wide pointer at .0 + | + = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the raw bytes of the constant (size: 16, align: 8) { + ╾──────allocN───────╼ 04 00 00 00 00 00 00 00 │ ╾──────╼........ + } + +error[E0080]: it is undefined behavior to use this value + --> $DIR/ub-wide-ptr.rs:113:1 + | +LL | const TRAIT_OBJ_UNALIGNED_VTABLE: &dyn Trait = unsafe { mem::transmute((&92u8, &[0u8; 128])) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered unaligned vtable pointer in wide pointer + | + = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the raw bytes of the constant (size: 16, align: 8) { + ╾──────allocN───────╼ ╾──────allocN───────╼ │ ╾──────╼╾──────╼ + } + +error[E0080]: it is undefined behavior to use this value + --> $DIR/ub-wide-ptr.rs:115:1 + | +LL | const TRAIT_OBJ_BAD_DROP_FN_NULL: &dyn Trait = unsafe { mem::transmute((&92u8, &[0usize; 8])) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered invalid drop function pointer in vtable (not pointing to a function) + | + = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the raw bytes of the constant (size: 16, align: 8) { + ╾──────allocN───────╼ ╾──────allocN───────╼ │ ╾──────╼╾──────╼ + } + +error[E0080]: it is undefined behavior to use this value + --> $DIR/ub-wide-ptr.rs:117:1 + | +LL | const TRAIT_OBJ_BAD_DROP_FN_INT: &dyn Trait = unsafe { mem::transmute((&92u8, &[1usize; 8])) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered invalid drop function pointer in vtable (not pointing to a function) + | + = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the raw bytes of the constant (size: 16, align: 8) { + ╾──────allocN───────╼ ╾──────allocN───────╼ │ ╾──────╼╾──────╼ + } + +error[E0080]: it is undefined behavior to use this value + --> $DIR/ub-wide-ptr.rs:119:1 + | +LL | const TRAIT_OBJ_BAD_DROP_FN_NOT_FN_PTR: W<&dyn Trait> = unsafe { mem::transmute(W((&92u8, &[&42u8; 8]))) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered invalid drop function pointer in vtable (not pointing to a function) at .0 + | + = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the raw bytes of the constant (size: 16, align: 8) { + ╾──────allocN───────╼ ╾──────allocN───────╼ │ ╾──────╼╾──────╼ + } + +error[E0080]: it is undefined behavior to use this value + --> $DIR/ub-wide-ptr.rs:123:1 + | +LL | const TRAIT_OBJ_CONTENT_INVALID: &dyn Trait = unsafe { mem::transmute::<_, &bool>(&3u8) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered 0x03 at .<deref>.<dyn-downcast>, but expected a boolean + | + = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the raw bytes of the constant (size: 16, align: 8) { + ╾──────allocN───────╼ ╾──────allocN───────╼ │ ╾──────╼╾──────╼ + } + +error[E0080]: it is undefined behavior to use this value + --> $DIR/ub-wide-ptr.rs:127:1 + | +LL | const RAW_TRAIT_OBJ_VTABLE_NULL: *const dyn Trait = unsafe { mem::transmute((&92u8, 0usize)) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered dangling vtable pointer in wide pointer + | + = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the raw bytes of the constant (size: 16, align: 8) { + ╾──────allocN───────╼ 00 00 00 00 00 00 00 00 │ ╾──────╼........ + } + +error[E0080]: it is undefined behavior to use this value + --> $DIR/ub-wide-ptr.rs:129:1 + | +LL | const RAW_TRAIT_OBJ_VTABLE_INVALID: *const dyn Trait = unsafe { mem::transmute((&92u8, &3u64)) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered too small vtable + | + = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the raw bytes of the constant (size: 16, align: 8) { + ╾──────allocN───────╼ ╾──────allocN───────╼ │ ╾──────╼╾──────╼ + } + +error[E0080]: could not evaluate static initializer + --> $DIR/ub-wide-ptr.rs:135:5 + | +LL | mem::transmute::<_, &dyn Trait>((&92u8, 0usize)) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ inbounds test failed: 0x0 is not a valid pointer + +error[E0080]: could not evaluate static initializer + --> $DIR/ub-wide-ptr.rs:139:5 + | +LL | mem::transmute::<_, &dyn Trait>((&92u8, &3u64)) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ memory access failed: pointer must be in-bounds at offset N, but is outside bounds of allocN which has size N + +error: aborting due to 28 previous errors + +For more information about this error, try `rustc --explain E0080`. diff --git a/src/test/ui/consts/const-eval/ub-wide-ptr.rs b/src/test/ui/consts/const-eval/ub-wide-ptr.rs index 2975118cdb7fb..0fb9f7960ce1a 100644 --- a/src/test/ui/consts/const-eval/ub-wide-ptr.rs +++ b/src/test/ui/consts/const-eval/ub-wide-ptr.rs @@ -1,3 +1,4 @@ +// stderr-per-bitwidth // ignore-tidy-linelength #![allow(unused)] #![allow(const_err)] // make sure we cannot allow away the errors tested here diff --git a/src/test/ui/consts/const-eval/ub-wide-ptr.stderr b/src/test/ui/consts/const-eval/ub-wide-ptr.stderr deleted file mode 100644 index be9ec16a06fe3..0000000000000 --- a/src/test/ui/consts/const-eval/ub-wide-ptr.stderr +++ /dev/null @@ -1,231 +0,0 @@ -error[E0080]: it is undefined behavior to use this value - --> $DIR/ub-wide-ptr.rs:37:1 - | -LL | const STR_TOO_LONG: &str = unsafe { mem::transmute((&42u8, 999usize)) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered a dangling reference (going beyond the bounds of its allocation) - | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. - -error[E0080]: it is undefined behavior to use this value - --> $DIR/ub-wide-ptr.rs:39:1 - | -LL | const NESTED_STR_MUCH_TOO_LONG: (&str,) = (unsafe { mem::transmute((&42, usize::MAX)) },); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered invalid reference metadata: slice is bigger than largest supported object at .0 - | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. - -error[E0080]: it is undefined behavior to use this value - --> $DIR/ub-wide-ptr.rs:42:1 - | -LL | const STR_LENGTH_PTR: &str = unsafe { mem::transmute((&42u8, &3)) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered non-integer slice length in wide pointer - | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. - -error[E0080]: it is undefined behavior to use this value - --> $DIR/ub-wide-ptr.rs:45:1 - | -LL | const MY_STR_LENGTH_PTR: &MyStr = unsafe { mem::transmute((&42u8, &3)) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered non-integer slice length in wide pointer - | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. - -error[E0080]: it is undefined behavior to use this value - --> $DIR/ub-wide-ptr.rs:47:1 - | -LL | const MY_STR_MUCH_TOO_LONG: &MyStr = unsafe { mem::transmute((&42u8, usize::MAX)) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered invalid reference metadata: slice is bigger than largest supported object - | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. - -error[E0080]: it is undefined behavior to use this value - --> $DIR/ub-wide-ptr.rs:51:1 - | -LL | const STR_NO_INIT: &str = unsafe { mem::transmute::<&[_], _>(&[MaybeUninit::<u8> { uninit: () }]) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered uninitialized data in `str` at .<deref> - | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. - -error[E0080]: it is undefined behavior to use this value - --> $DIR/ub-wide-ptr.rs:54:1 - | -LL | const MYSTR_NO_INIT: &MyStr = unsafe { mem::transmute::<&[_], _>(&[MaybeUninit::<u8> { uninit: () }]) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered uninitialized data in `str` at .<deref>.0 - | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. - -error[E0080]: it is undefined behavior to use this value - --> $DIR/ub-wide-ptr.rs:61:1 - | -LL | / const SLICE_LENGTH_UNINIT: &[u8] = unsafe { -LL | | -LL | | let uninit_len = MaybeUninit::<usize> { uninit: () }; -LL | | mem::transmute((42, uninit_len)) -LL | | }; - | |__^ type validation failed: encountered uninitialized reference - | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. - -error[E0080]: it is undefined behavior to use this value - --> $DIR/ub-wide-ptr.rs:67:1 - | -LL | const SLICE_TOO_LONG: &[u8] = unsafe { mem::transmute((&42u8, 999usize)) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered a dangling reference (going beyond the bounds of its allocation) - | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. - -error[E0080]: it is undefined behavior to use this value - --> $DIR/ub-wide-ptr.rs:70:1 - | -LL | const SLICE_LENGTH_PTR: &[u8] = unsafe { mem::transmute((&42u8, &3)) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered non-integer slice length in wide pointer - | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. - -error[E0080]: it is undefined behavior to use this value - --> $DIR/ub-wide-ptr.rs:73:1 - | -LL | const SLICE_TOO_LONG_BOX: Box<[u8]> = unsafe { mem::transmute((&42u8, 999usize)) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered a dangling box (going beyond the bounds of its allocation) - | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. - -error[E0080]: it is undefined behavior to use this value - --> $DIR/ub-wide-ptr.rs:76:1 - | -LL | const SLICE_LENGTH_PTR_BOX: Box<[u8]> = unsafe { mem::transmute((&42u8, &3)) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered non-integer slice length in wide pointer - | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. - -error[E0080]: it is undefined behavior to use this value - --> $DIR/ub-wide-ptr.rs:80:1 - | -LL | const SLICE_CONTENT_INVALID: &[bool] = &[unsafe { mem::transmute(3u8) }]; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered 0x03 at .<deref>[0], but expected a boolean - | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. - -error[E0080]: it is undefined behavior to use this value - --> $DIR/ub-wide-ptr.rs:86:1 - | -LL | const MYSLICE_PREFIX_BAD: &MySliceBool = &MySlice(unsafe { mem::transmute(3u8) }, [false]); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered 0x03 at .<deref>.0, but expected a boolean - | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. - -error[E0080]: it is undefined behavior to use this value - --> $DIR/ub-wide-ptr.rs:89:1 - | -LL | const MYSLICE_SUFFIX_BAD: &MySliceBool = &MySlice(true, [unsafe { mem::transmute(3u8) }]); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered 0x03 at .<deref>.1[0], but expected a boolean - | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. - -error[E0080]: it is undefined behavior to use this value - --> $DIR/ub-wide-ptr.rs:96:1 - | -LL | / const RAW_SLICE_LENGTH_UNINIT: *const [u8] = unsafe { -LL | | -LL | | let uninit_len = MaybeUninit::<usize> { uninit: () }; -LL | | mem::transmute((42, uninit_len)) -LL | | }; - | |__^ type validation failed: encountered uninitialized raw pointer - | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. - -error[E0080]: it is undefined behavior to use this value - --> $DIR/ub-wide-ptr.rs:104:1 - | -LL | const TRAIT_OBJ_SHORT_VTABLE_1: W<&dyn Trait> = unsafe { mem::transmute(W((&92u8, &3u8))) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered too small vtable at .0 - | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. - -error[E0080]: it is undefined behavior to use this value - --> $DIR/ub-wide-ptr.rs:107:1 - | -LL | const TRAIT_OBJ_SHORT_VTABLE_2: W<&dyn Trait> = unsafe { mem::transmute(W((&92u8, &3u64))) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered too small vtable at .0 - | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. - -error[E0080]: it is undefined behavior to use this value - --> $DIR/ub-wide-ptr.rs:110:1 - | -LL | const TRAIT_OBJ_INT_VTABLE: W<&dyn Trait> = unsafe { mem::transmute(W((&92u8, 4usize))) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered dangling vtable pointer in wide pointer at .0 - | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. - -error[E0080]: it is undefined behavior to use this value - --> $DIR/ub-wide-ptr.rs:112:1 - | -LL | const TRAIT_OBJ_UNALIGNED_VTABLE: &dyn Trait = unsafe { mem::transmute((&92u8, &[0u8; 128])) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered unaligned vtable pointer in wide pointer - | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. - -error[E0080]: it is undefined behavior to use this value - --> $DIR/ub-wide-ptr.rs:114:1 - | -LL | const TRAIT_OBJ_BAD_DROP_FN_NULL: &dyn Trait = unsafe { mem::transmute((&92u8, &[0usize; 8])) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered invalid drop function pointer in vtable (not pointing to a function) - | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. - -error[E0080]: it is undefined behavior to use this value - --> $DIR/ub-wide-ptr.rs:116:1 - | -LL | const TRAIT_OBJ_BAD_DROP_FN_INT: &dyn Trait = unsafe { mem::transmute((&92u8, &[1usize; 8])) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered invalid drop function pointer in vtable (not pointing to a function) - | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. - -error[E0080]: it is undefined behavior to use this value - --> $DIR/ub-wide-ptr.rs:118:1 - | -LL | const TRAIT_OBJ_BAD_DROP_FN_NOT_FN_PTR: W<&dyn Trait> = unsafe { mem::transmute(W((&92u8, &[&42u8; 8]))) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered invalid drop function pointer in vtable (not pointing to a function) at .0 - | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. - -error[E0080]: it is undefined behavior to use this value - --> $DIR/ub-wide-ptr.rs:122:1 - | -LL | const TRAIT_OBJ_CONTENT_INVALID: &dyn Trait = unsafe { mem::transmute::<_, &bool>(&3u8) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered 0x03 at .<deref>.<dyn-downcast>, but expected a boolean - | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. - -error[E0080]: it is undefined behavior to use this value - --> $DIR/ub-wide-ptr.rs:126:1 - | -LL | const RAW_TRAIT_OBJ_VTABLE_NULL: *const dyn Trait = unsafe { mem::transmute((&92u8, 0usize)) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered dangling vtable pointer in wide pointer - | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. - -error[E0080]: it is undefined behavior to use this value - --> $DIR/ub-wide-ptr.rs:128:1 - | -LL | const RAW_TRAIT_OBJ_VTABLE_INVALID: *const dyn Trait = unsafe { mem::transmute((&92u8, &3u64)) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered too small vtable - | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. - -error[E0080]: could not evaluate static initializer - --> $DIR/ub-wide-ptr.rs:134:5 - | -LL | mem::transmute::<_, &dyn Trait>((&92u8, 0usize)) - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ inbounds test failed: 0x0 is not a valid pointer - -error[E0080]: could not evaluate static initializer - --> $DIR/ub-wide-ptr.rs:138:5 - | -LL | mem::transmute::<_, &dyn Trait>((&92u8, &3u64)) - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ memory access failed: pointer must be in-bounds at offset N, but is outside bounds of allocN which has size N - -error: aborting due to 28 previous errors - -For more information about this error, try `rustc --explain E0080`. diff --git a/src/test/ui/consts/const-eval/union-const-eval-field.rs b/src/test/ui/consts/const-eval/union-const-eval-field.rs index 7f29a5bc24e4e..f8e1d6d569dd9 100644 --- a/src/test/ui/consts/const-eval/union-const-eval-field.rs +++ b/src/test/ui/consts/const-eval/union-const-eval-field.rs @@ -1,3 +1,4 @@ +// only-x86_64 #![feature(const_fn)] type Field1 = i32; diff --git a/src/test/ui/consts/const-eval/union-const-eval-field.stderr b/src/test/ui/consts/const-eval/union-const-eval-field.stderr index 9193bd9dea189..c1c2dcb22695d 100644 --- a/src/test/ui/consts/const-eval/union-const-eval-field.stderr +++ b/src/test/ui/consts/const-eval/union-const-eval-field.stderr @@ -1,10 +1,13 @@ error[E0080]: it is undefined behavior to use this value - --> $DIR/union-const-eval-field.rs:28:5 + --> $DIR/union-const-eval-field.rs:29:5 | LL | const FIELD3: Field3 = unsafe { UNION.field3 }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered uninitialized bytes, but expected initialized plain (non-pointer) bytes | = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the raw bytes of the constant (size: 8, align: 8) { + __ __ __ __ __ __ __ __ │ ░░░░░░░░ + } error: aborting due to previous error diff --git a/src/test/ui/consts/const-eval/union-ice.rs b/src/test/ui/consts/const-eval/union-ice.rs index 5a14c7fd99345..40e5a005ba4af 100644 --- a/src/test/ui/consts/const-eval/union-ice.rs +++ b/src/test/ui/consts/const-eval/union-ice.rs @@ -1,3 +1,4 @@ +// only-x86_64 #![feature(const_fn)] type Field1 = i32; diff --git a/src/test/ui/consts/const-eval/union-ice.stderr b/src/test/ui/consts/const-eval/union-ice.stderr index 2545167aa02f7..f8b9478ad1a60 100644 --- a/src/test/ui/consts/const-eval/union-ice.stderr +++ b/src/test/ui/consts/const-eval/union-ice.stderr @@ -1,13 +1,16 @@ error[E0080]: it is undefined behavior to use this value - --> $DIR/union-ice.rs:14:1 + --> $DIR/union-ice.rs:15:1 | LL | const FIELD3: Field3 = unsafe { UNION.field3 }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered uninitialized bytes, but expected initialized plain (non-pointer) bytes | = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the raw bytes of the constant (size: 8, align: 8) { + __ __ __ __ __ __ __ __ │ ░░░░░░░░ + } error[E0080]: it is undefined behavior to use this value - --> $DIR/union-ice.rs:16:1 + --> $DIR/union-ice.rs:17:1 | LL | / const FIELD_PATH: Struct = Struct { LL | | a: 42, @@ -16,9 +19,12 @@ LL | | }; | |__^ type validation failed: encountered uninitialized bytes at .b, but expected initialized plain (non-pointer) bytes | = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the raw bytes of the constant (size: 16, align: 8) { + __ __ __ __ __ __ __ __ 2a __ __ __ __ __ __ __ │ ░░░░░░░░*░░░░░░░ + } error[E0080]: it is undefined behavior to use this value - --> $DIR/union-ice.rs:26:1 + --> $DIR/union-ice.rs:27:1 | LL | / const FIELD_PATH2: Struct2 = Struct2 { LL | | b: [ @@ -30,6 +36,11 @@ LL | | }; | |__^ type validation failed: encountered uninitialized bytes at .b[1] | = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the raw bytes of the constant (size: 40, align: 8) { + 0x00 │ 15 00 00 00 00 00 00 00 __ __ __ __ __ __ __ __ │ ........░░░░░░░░ + 0x10 │ 17 00 00 00 00 00 00 00 18 00 00 00 00 00 00 00 │ ................ + 0x20 │ 2a __ __ __ __ __ __ __ │ *░░░░░░░ + } error: aborting due to 3 previous errors diff --git a/src/test/ui/consts/const-eval/union-ub.32bit.stderr b/src/test/ui/consts/const-eval/union-ub.32bit.stderr new file mode 100644 index 0000000000000..d3e4bad968bd0 --- /dev/null +++ b/src/test/ui/consts/const-eval/union-ub.32bit.stderr @@ -0,0 +1,25 @@ +error[E0080]: it is undefined behavior to use this value + --> $DIR/union-ub.rs:33:1 + | +LL | const BAD_BOOL: bool = unsafe { DummyUnion { u8: 42 }.bool}; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered 0x2a, but expected a boolean + | + = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the raw bytes of the constant (size: 1, align: 1) { + 2a │ * + } + +error[E0080]: it is undefined behavior to use this value + --> $DIR/union-ub.rs:35:1 + | +LL | const UNINIT_BOOL: bool = unsafe { DummyUnion { unit: () }.bool}; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered uninitialized bytes, but expected a boolean + | + = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the raw bytes of the constant (size: 1, align: 1) { + __ │ ░ + } + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0080`. diff --git a/src/test/ui/consts/const-eval/union-ub.64bit.stderr b/src/test/ui/consts/const-eval/union-ub.64bit.stderr new file mode 100644 index 0000000000000..d3e4bad968bd0 --- /dev/null +++ b/src/test/ui/consts/const-eval/union-ub.64bit.stderr @@ -0,0 +1,25 @@ +error[E0080]: it is undefined behavior to use this value + --> $DIR/union-ub.rs:33:1 + | +LL | const BAD_BOOL: bool = unsafe { DummyUnion { u8: 42 }.bool}; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered 0x2a, but expected a boolean + | + = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the raw bytes of the constant (size: 1, align: 1) { + 2a │ * + } + +error[E0080]: it is undefined behavior to use this value + --> $DIR/union-ub.rs:35:1 + | +LL | const UNINIT_BOOL: bool = unsafe { DummyUnion { unit: () }.bool}; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered uninitialized bytes, but expected a boolean + | + = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the raw bytes of the constant (size: 1, align: 1) { + __ │ ░ + } + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0080`. diff --git a/src/test/ui/consts/const-eval/union-ub.rs b/src/test/ui/consts/const-eval/union-ub.rs index 512359f5b1c38..c1bfe69a706e4 100644 --- a/src/test/ui/consts/const-eval/union-ub.rs +++ b/src/test/ui/consts/const-eval/union-ub.rs @@ -1,3 +1,4 @@ +// stderr-per-bitwidth #![allow(const_err)] // make sure we cannot allow away the errors tested here #[repr(C)] diff --git a/src/test/ui/consts/const-eval/union-ub.stderr b/src/test/ui/consts/const-eval/union-ub.stderr deleted file mode 100644 index e8869d0d76c1c..0000000000000 --- a/src/test/ui/consts/const-eval/union-ub.stderr +++ /dev/null @@ -1,19 +0,0 @@ -error[E0080]: it is undefined behavior to use this value - --> $DIR/union-ub.rs:32:1 - | -LL | const BAD_BOOL: bool = unsafe { DummyUnion { u8: 42 }.bool}; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered 0x2a, but expected a boolean - | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. - -error[E0080]: it is undefined behavior to use this value - --> $DIR/union-ub.rs:34:1 - | -LL | const UNINIT_BOOL: bool = unsafe { DummyUnion { unit: () }.bool}; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered uninitialized bytes, but expected a boolean - | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. - -error: aborting due to 2 previous errors - -For more information about this error, try `rustc --explain E0080`. diff --git a/src/test/ui/consts/const-eval/validate_uninhabited_zsts.32bit.stderr b/src/test/ui/consts/const-eval/validate_uninhabited_zsts.32bit.stderr new file mode 100644 index 0000000000000..bb91b43e20b4b --- /dev/null +++ b/src/test/ui/consts/const-eval/validate_uninhabited_zsts.32bit.stderr @@ -0,0 +1,56 @@ +warning: any use of this value will cause an error + --> $DIR/validate_uninhabited_zsts.rs:6:14 + | +LL | unsafe { std::mem::transmute(()) } + | ^^^^^^^^^^^^^^^^^^^^^^^ + | | + | transmuting to uninhabited type + | inside `foo` at $DIR/validate_uninhabited_zsts.rs:6:14 + | inside `FOO` at $DIR/validate_uninhabited_zsts.rs:16:26 +... +LL | const FOO: [Empty; 3] = [foo(); 3]; + | ----------------------------------- + | +note: the lint level is defined here + --> $DIR/validate_uninhabited_zsts.rs:15:8 + | +LL | #[warn(const_err)] + | ^^^^^^^^^ + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #71800 <https://github.com/rust-lang/rust/issues/71800> + +error[E0080]: it is undefined behavior to use this value + --> $DIR/validate_uninhabited_zsts.rs:19:1 + | +LL | const BAR: [Empty; 3] = [unsafe { std::mem::transmute(()) }; 3]; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered a value of uninhabited type Empty at [0] + | + = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the raw bytes of the constant (size: 0, align: 1) {} + +warning: the type `!` does not permit zero-initialization + --> $DIR/validate_uninhabited_zsts.rs:6:14 + | +LL | unsafe { std::mem::transmute(()) } + | ^^^^^^^^^^^^^^^^^^^^^^^ + | | + | this code causes undefined behavior when executed + | help: use `MaybeUninit<T>` instead, and only call `assume_init` after initialization is done + | + = note: `#[warn(invalid_value)]` on by default + = note: the `!` type has no valid value + +warning: the type `Empty` does not permit zero-initialization + --> $DIR/validate_uninhabited_zsts.rs:19:35 + | +LL | const BAR: [Empty; 3] = [unsafe { std::mem::transmute(()) }; 3]; + | ^^^^^^^^^^^^^^^^^^^^^^^ + | | + | this code causes undefined behavior when executed + | help: use `MaybeUninit<T>` instead, and only call `assume_init` after initialization is done + | + = note: enums with no variants have no valid value + +error: aborting due to previous error; 3 warnings emitted + +For more information about this error, try `rustc --explain E0080`. diff --git a/src/test/ui/consts/const-eval/validate_uninhabited_zsts.64bit.stderr b/src/test/ui/consts/const-eval/validate_uninhabited_zsts.64bit.stderr new file mode 100644 index 0000000000000..bb91b43e20b4b --- /dev/null +++ b/src/test/ui/consts/const-eval/validate_uninhabited_zsts.64bit.stderr @@ -0,0 +1,56 @@ +warning: any use of this value will cause an error + --> $DIR/validate_uninhabited_zsts.rs:6:14 + | +LL | unsafe { std::mem::transmute(()) } + | ^^^^^^^^^^^^^^^^^^^^^^^ + | | + | transmuting to uninhabited type + | inside `foo` at $DIR/validate_uninhabited_zsts.rs:6:14 + | inside `FOO` at $DIR/validate_uninhabited_zsts.rs:16:26 +... +LL | const FOO: [Empty; 3] = [foo(); 3]; + | ----------------------------------- + | +note: the lint level is defined here + --> $DIR/validate_uninhabited_zsts.rs:15:8 + | +LL | #[warn(const_err)] + | ^^^^^^^^^ + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #71800 <https://github.com/rust-lang/rust/issues/71800> + +error[E0080]: it is undefined behavior to use this value + --> $DIR/validate_uninhabited_zsts.rs:19:1 + | +LL | const BAR: [Empty; 3] = [unsafe { std::mem::transmute(()) }; 3]; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered a value of uninhabited type Empty at [0] + | + = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the raw bytes of the constant (size: 0, align: 1) {} + +warning: the type `!` does not permit zero-initialization + --> $DIR/validate_uninhabited_zsts.rs:6:14 + | +LL | unsafe { std::mem::transmute(()) } + | ^^^^^^^^^^^^^^^^^^^^^^^ + | | + | this code causes undefined behavior when executed + | help: use `MaybeUninit<T>` instead, and only call `assume_init` after initialization is done + | + = note: `#[warn(invalid_value)]` on by default + = note: the `!` type has no valid value + +warning: the type `Empty` does not permit zero-initialization + --> $DIR/validate_uninhabited_zsts.rs:19:35 + | +LL | const BAR: [Empty; 3] = [unsafe { std::mem::transmute(()) }; 3]; + | ^^^^^^^^^^^^^^^^^^^^^^^ + | | + | this code causes undefined behavior when executed + | help: use `MaybeUninit<T>` instead, and only call `assume_init` after initialization is done + | + = note: enums with no variants have no valid value + +error: aborting due to previous error; 3 warnings emitted + +For more information about this error, try `rustc --explain E0080`. diff --git a/src/test/ui/consts/const-eval/validate_uninhabited_zsts.rs b/src/test/ui/consts/const-eval/validate_uninhabited_zsts.rs index 4e1c71cd60013..a32dfa2918ba7 100644 --- a/src/test/ui/consts/const-eval/validate_uninhabited_zsts.rs +++ b/src/test/ui/consts/const-eval/validate_uninhabited_zsts.rs @@ -1,3 +1,4 @@ +// stderr-per-bitwidth #![feature(const_fn)] #![feature(const_fn_transmute)] diff --git a/src/test/ui/consts/const-eval/validate_uninhabited_zsts.stderr b/src/test/ui/consts/const-eval/validate_uninhabited_zsts.stderr deleted file mode 100644 index 3f22fac11f65d..0000000000000 --- a/src/test/ui/consts/const-eval/validate_uninhabited_zsts.stderr +++ /dev/null @@ -1,55 +0,0 @@ -warning: any use of this value will cause an error - --> $DIR/validate_uninhabited_zsts.rs:5:14 - | -LL | unsafe { std::mem::transmute(()) } - | ^^^^^^^^^^^^^^^^^^^^^^^ - | | - | transmuting to uninhabited type - | inside `foo` at $DIR/validate_uninhabited_zsts.rs:5:14 - | inside `FOO` at $DIR/validate_uninhabited_zsts.rs:15:26 -... -LL | const FOO: [Empty; 3] = [foo(); 3]; - | ----------------------------------- - | -note: the lint level is defined here - --> $DIR/validate_uninhabited_zsts.rs:14:8 - | -LL | #[warn(const_err)] - | ^^^^^^^^^ - = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = note: for more information, see issue #71800 <https://github.com/rust-lang/rust/issues/71800> - -error[E0080]: it is undefined behavior to use this value - --> $DIR/validate_uninhabited_zsts.rs:18:1 - | -LL | const BAR: [Empty; 3] = [unsafe { std::mem::transmute(()) }; 3]; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered a value of uninhabited type Empty at [0] - | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. - -warning: the type `!` does not permit zero-initialization - --> $DIR/validate_uninhabited_zsts.rs:5:14 - | -LL | unsafe { std::mem::transmute(()) } - | ^^^^^^^^^^^^^^^^^^^^^^^ - | | - | this code causes undefined behavior when executed - | help: use `MaybeUninit<T>` instead, and only call `assume_init` after initialization is done - | - = note: `#[warn(invalid_value)]` on by default - = note: the `!` type has no valid value - -warning: the type `Empty` does not permit zero-initialization - --> $DIR/validate_uninhabited_zsts.rs:18:35 - | -LL | const BAR: [Empty; 3] = [unsafe { std::mem::transmute(()) }; 3]; - | ^^^^^^^^^^^^^^^^^^^^^^^ - | | - | this code causes undefined behavior when executed - | help: use `MaybeUninit<T>` instead, and only call `assume_init` after initialization is done - | - = note: enums with no variants have no valid value - -error: aborting due to previous error; 3 warnings emitted - -For more information about this error, try `rustc --explain E0080`. diff --git a/src/test/ui/consts/const-fn-in-vec.stderr b/src/test/ui/consts/const-fn-in-vec.stderr index f9f184dfc06b6..f02cb4f1ff193 100644 --- a/src/test/ui/consts/const-fn-in-vec.stderr +++ b/src/test/ui/consts/const-fn-in-vec.stderr @@ -5,8 +5,8 @@ LL | let strings: [String; 5] = [String::new(); 5]; | ^^^^^^^^^^^^^^^^^^ the trait `Copy` is not implemented for `String` | = note: the `Copy` trait is required because the repeated element will be copied - = help: consider creating a new `const` item and initializing with the result of the function call to be used in the repeat position, like `const VAL: Type = const_fn();` and `let x = [VAL; 42];` - = help: create an inline `const` block, see PR #2920 <https://github.com/rust-lang/rfcs/pull/2920> for more information + = help: consider creating a new `const` item and initializing it with the result of the function call to be used in the repeat position, like `const VAL: Type = const_fn();` and `let x = [VAL; 42];` + = help: create an inline `const` block, see RFC #2920 <https://github.com/rust-lang/rfcs/pull/2920> for more information error: aborting due to previous error diff --git a/src/test/ui/consts/const-int-unchecked.rs b/src/test/ui/consts/const-int-unchecked.rs index 41d8f7a0972bc..2ccc5d27bbb78 100644 --- a/src/test/ui/consts/const-int-unchecked.rs +++ b/src/test/ui/consts/const-int-unchecked.rs @@ -186,4 +186,13 @@ const _: i32 = unsafe { std::intrinsics::unchecked_rem(i32::MIN, -1) }; //~^ ERROR any use of this value will cause an error //~| WARN this was previously accepted by the compiler but is being phased out +// capture fault with zero value + +const _: u32 = unsafe { std::intrinsics::ctlz_nonzero(0) }; +//~^ ERROR any use of this value will cause an error +//~| WARN this was previously accepted by the compiler but is being phased out +const _: u32 = unsafe { std::intrinsics::cttz_nonzero(0) }; +//~^ ERROR any use of this value will cause an error +//~| WARN this was previously accepted by the compiler but is being phased out + fn main() {} diff --git a/src/test/ui/consts/const-int-unchecked.stderr b/src/test/ui/consts/const-int-unchecked.stderr index e5ecbbc71a7f6..999b26543e2d5 100644 --- a/src/test/ui/consts/const-int-unchecked.stderr +++ b/src/test/ui/consts/const-int-unchecked.stderr @@ -516,5 +516,27 @@ LL | const _: i32 = unsafe { std::intrinsics::unchecked_rem(i32::MIN, -1) }; = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! = note: for more information, see issue #71800 <https://github.com/rust-lang/rust/issues/71800> -error: aborting due to 47 previous errors +error: any use of this value will cause an error + --> $DIR/const-int-unchecked.rs:191:25 + | +LL | const _: u32 = unsafe { std::intrinsics::ctlz_nonzero(0) }; + | ------------------------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^--- + | | + | `ctlz_nonzero` called on 0 + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #71800 <https://github.com/rust-lang/rust/issues/71800> + +error: any use of this value will cause an error + --> $DIR/const-int-unchecked.rs:194:25 + | +LL | const _: u32 = unsafe { std::intrinsics::cttz_nonzero(0) }; + | ------------------------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^--- + | | + | `cttz_nonzero` called on 0 + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #71800 <https://github.com/rust-lang/rust/issues/71800> + +error: aborting due to 49 previous errors diff --git a/src/test/ui/consts/const-points-to-static.32bit.stderr b/src/test/ui/consts/const-points-to-static.32bit.stderr new file mode 100644 index 0000000000000..c582678e2d65b --- /dev/null +++ b/src/test/ui/consts/const-points-to-static.32bit.stderr @@ -0,0 +1,22 @@ +error[E0080]: it is undefined behavior to use this value + --> $DIR/const-points-to-static.rs:6:1 + | +LL | const TEST: &u8 = &MY_STATIC; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered a reference pointing to a static variable + | + = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the raw bytes of the constant (size: 4, align: 4) { + ╾─alloc0──╼ │ ╾──╼ + } + +warning: skipping const checks + | +help: skipping check that does not even have a feature gate + --> $DIR/const-points-to-static.rs:6:20 + | +LL | const TEST: &u8 = &MY_STATIC; + | ^^^^^^^^^ + +error: aborting due to previous error; 1 warning emitted + +For more information about this error, try `rustc --explain E0080`. diff --git a/src/test/ui/consts/const-points-to-static.64bit.stderr b/src/test/ui/consts/const-points-to-static.64bit.stderr new file mode 100644 index 0000000000000..1112499a3ee64 --- /dev/null +++ b/src/test/ui/consts/const-points-to-static.64bit.stderr @@ -0,0 +1,22 @@ +error[E0080]: it is undefined behavior to use this value + --> $DIR/const-points-to-static.rs:6:1 + | +LL | const TEST: &u8 = &MY_STATIC; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered a reference pointing to a static variable + | + = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the raw bytes of the constant (size: 8, align: 8) { + ╾───────alloc0────────╼ │ ╾──────╼ + } + +warning: skipping const checks + | +help: skipping check that does not even have a feature gate + --> $DIR/const-points-to-static.rs:6:20 + | +LL | const TEST: &u8 = &MY_STATIC; + | ^^^^^^^^^ + +error: aborting due to previous error; 1 warning emitted + +For more information about this error, try `rustc --explain E0080`. diff --git a/src/test/ui/consts/const-points-to-static.rs b/src/test/ui/consts/const-points-to-static.rs index 7087b6e6a6764..4359230685f35 100644 --- a/src/test/ui/consts/const-points-to-static.rs +++ b/src/test/ui/consts/const-points-to-static.rs @@ -1,11 +1,13 @@ // compile-flags: -Zunleash-the-miri-inside-of-you +// stderr-per-bitwidth #![allow(dead_code)] const TEST: &u8 = &MY_STATIC; //~^ ERROR it is undefined behavior to use this value //~| NOTE encountered a reference pointing to a static variable -//~| NOTE +//~| NOTE undefined behavior +//~| NOTE the raw bytes of the constant static MY_STATIC: u8 = 4; diff --git a/src/test/ui/consts/const-points-to-static.stderr b/src/test/ui/consts/const-points-to-static.stderr deleted file mode 100644 index 465537fb3d5ea..0000000000000 --- a/src/test/ui/consts/const-points-to-static.stderr +++ /dev/null @@ -1,19 +0,0 @@ -error[E0080]: it is undefined behavior to use this value - --> $DIR/const-points-to-static.rs:5:1 - | -LL | const TEST: &u8 = &MY_STATIC; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered a reference pointing to a static variable - | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. - -warning: skipping const checks - | -help: skipping check that does not even have a feature gate - --> $DIR/const-points-to-static.rs:5:20 - | -LL | const TEST: &u8 = &MY_STATIC; - | ^^^^^^^^^ - -error: aborting due to previous error; 1 warning emitted - -For more information about this error, try `rustc --explain E0080`. diff --git a/src/test/ui/consts/copy-intrinsic.rs b/src/test/ui/consts/copy-intrinsic.rs new file mode 100644 index 0000000000000..9dc595f37faae --- /dev/null +++ b/src/test/ui/consts/copy-intrinsic.rs @@ -0,0 +1,45 @@ +// ignore-tidy-linelength +#![feature(const_mut_refs, const_intrinsic_copy, const_ptr_offset)] +use std::{ptr, mem}; + +const COPY_ZERO: () = unsafe { + // Since we are not copying anything, this should be allowed. + let src = (); + let mut dst = (); + ptr::copy_nonoverlapping(&src as *const _ as *const i32, &mut dst as *mut _ as *mut i32, 0); +}; + +const COPY_OOB_1: () = unsafe { + let mut x = 0i32; + let dangle = (&mut x as *mut i32).wrapping_add(10); + // Even if the first ptr is an int ptr and this is a ZST copy, we should detect dangling 2nd ptrs. + ptr::copy_nonoverlapping(0x100 as *const i32, dangle, 0); //~ ERROR any use of this value will cause an error + //~| memory access failed: pointer must be in-bounds + //~| previously accepted +}; +const COPY_OOB_2: () = unsafe { + let x = 0i32; + let dangle = (&x as *const i32).wrapping_add(10); + // Even if the second ptr is an int ptr and this is a ZST copy, we should detect dangling 1st ptrs. + ptr::copy_nonoverlapping(dangle, 0x100 as *mut i32, 0); //~ ERROR any use of this value will cause an error + //~| memory access failed: pointer must be in-bounds + //~| previously accepted +}; + +const COPY_SIZE_OVERFLOW: () = unsafe { + let x = 0; + let mut y = 0; + ptr::copy(&x, &mut y, 1usize << (mem::size_of::<usize>() * 8 - 1)); //~ ERROR any use of this value will cause an error + //~| overflow computing total size of `copy` + //~| previously accepted +}; +const COPY_NONOVERLAPPING_SIZE_OVERFLOW: () = unsafe { + let x = 0; + let mut y = 0; + ptr::copy_nonoverlapping(&x, &mut y, 1usize << (mem::size_of::<usize>() * 8 - 1)); //~ ERROR any use of this value will cause an error + //~| overflow computing total size of `copy_nonoverlapping` + //~| previously accepted +}; + +fn main() { +} diff --git a/src/test/ui/consts/copy-intrinsic.stderr b/src/test/ui/consts/copy-intrinsic.stderr new file mode 100644 index 0000000000000..2736cdeac690e --- /dev/null +++ b/src/test/ui/consts/copy-intrinsic.stderr @@ -0,0 +1,69 @@ +error: any use of this value will cause an error + --> $DIR/copy-intrinsic.rs:16:5 + | +LL | / const COPY_OOB_1: () = unsafe { +LL | | let mut x = 0i32; +LL | | let dangle = (&mut x as *mut i32).wrapping_add(10); +LL | | // Even if the first ptr is an int ptr and this is a ZST copy, we should detect dangling 2nd ptrs. +LL | | ptr::copy_nonoverlapping(0x100 as *const i32, dangle, 0); + | | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ memory access failed: pointer must be in-bounds at offset 40, but is outside bounds of alloc4 which has size 4 +LL | | +LL | | +LL | | }; + | |__- + | + = note: `#[deny(const_err)]` on by default + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #71800 <https://github.com/rust-lang/rust/issues/71800> + +error: any use of this value will cause an error + --> $DIR/copy-intrinsic.rs:24:5 + | +LL | / const COPY_OOB_2: () = unsafe { +LL | | let x = 0i32; +LL | | let dangle = (&x as *const i32).wrapping_add(10); +LL | | // Even if the second ptr is an int ptr and this is a ZST copy, we should detect dangling 1st ptrs. +LL | | ptr::copy_nonoverlapping(dangle, 0x100 as *mut i32, 0); + | | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ memory access failed: pointer must be in-bounds at offset 40, but is outside bounds of alloc6 which has size 4 +LL | | +LL | | +LL | | }; + | |__- + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #71800 <https://github.com/rust-lang/rust/issues/71800> + +error: any use of this value will cause an error + --> $DIR/copy-intrinsic.rs:32:5 + | +LL | / const COPY_SIZE_OVERFLOW: () = unsafe { +LL | | let x = 0; +LL | | let mut y = 0; +LL | | ptr::copy(&x, &mut y, 1usize << (mem::size_of::<usize>() * 8 - 1)); + | | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ overflow computing total size of `copy` +LL | | +LL | | +LL | | }; + | |__- + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #71800 <https://github.com/rust-lang/rust/issues/71800> + +error: any use of this value will cause an error + --> $DIR/copy-intrinsic.rs:39:5 + | +LL | / const COPY_NONOVERLAPPING_SIZE_OVERFLOW: () = unsafe { +LL | | let x = 0; +LL | | let mut y = 0; +LL | | ptr::copy_nonoverlapping(&x, &mut y, 1usize << (mem::size_of::<usize>() * 8 - 1)); + | | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ overflow computing total size of `copy_nonoverlapping` +LL | | +LL | | +LL | | }; + | |__- + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #71800 <https://github.com/rust-lang/rust/issues/71800> + +error: aborting due to 4 previous errors + diff --git a/src/test/ui/issues/issue-17718.rs b/src/test/ui/consts/issue-17718.rs similarity index 100% rename from src/test/ui/issues/issue-17718.rs rename to src/test/ui/consts/issue-17718.rs diff --git a/src/test/ui/consts/issue-32829-2.rs b/src/test/ui/consts/issue-32829-2.rs index c93c84b5fb773..e0fcf27833096 100644 --- a/src/test/ui/consts/issue-32829-2.rs +++ b/src/test/ui/consts/issue-32829-2.rs @@ -1,5 +1,3 @@ -// ignore-tidy-linelength - const bad : u32 = { { 5; diff --git a/src/test/ui/consts/issue-32829-2.stderr b/src/test/ui/consts/issue-32829-2.stderr index 8d7423f29ae97..1d265875c5c97 100644 --- a/src/test/ui/consts/issue-32829-2.stderr +++ b/src/test/ui/consts/issue-32829-2.stderr @@ -1,17 +1,17 @@ error[E0015]: calls in constants are limited to constant functions, tuple structs and tuple variants - --> $DIR/issue-32829-2.rs:12:9 + --> $DIR/issue-32829-2.rs:10:9 | LL | invalid(); | ^^^^^^^^^ error[E0015]: calls in statics are limited to constant functions, tuple structs and tuple variants - --> $DIR/issue-32829-2.rs:34:9 + --> $DIR/issue-32829-2.rs:32:9 | LL | invalid(); | ^^^^^^^^^ error[E0015]: calls in statics are limited to constant functions, tuple structs and tuple variants - --> $DIR/issue-32829-2.rs:56:9 + --> $DIR/issue-32829-2.rs:54:9 | LL | invalid(); | ^^^^^^^^^ diff --git a/src/test/ui/issues/issue-3521.fixed b/src/test/ui/consts/issue-3521.fixed similarity index 100% rename from src/test/ui/issues/issue-3521.fixed rename to src/test/ui/consts/issue-3521.fixed diff --git a/src/test/ui/issues/issue-3521.rs b/src/test/ui/consts/issue-3521.rs similarity index 100% rename from src/test/ui/issues/issue-3521.rs rename to src/test/ui/consts/issue-3521.rs diff --git a/src/test/ui/issues/issue-3521.stderr b/src/test/ui/consts/issue-3521.stderr similarity index 100% rename from src/test/ui/issues/issue-3521.stderr rename to src/test/ui/consts/issue-3521.stderr diff --git a/src/test/ui/issues/issue-52060.rs b/src/test/ui/consts/issue-52060.rs similarity index 100% rename from src/test/ui/issues/issue-52060.rs rename to src/test/ui/consts/issue-52060.rs diff --git a/src/test/ui/issues/issue-52060.stderr b/src/test/ui/consts/issue-52060.stderr similarity index 100% rename from src/test/ui/issues/issue-52060.stderr rename to src/test/ui/consts/issue-52060.stderr diff --git a/src/test/ui/consts/issue-63952.32bit.stderr b/src/test/ui/consts/issue-63952.32bit.stderr new file mode 100644 index 0000000000000..6d52ed065bff2 --- /dev/null +++ b/src/test/ui/consts/issue-63952.32bit.stderr @@ -0,0 +1,20 @@ +error[E0080]: it is undefined behavior to use this value + --> $DIR/issue-63952.rs:17:1 + | +LL | / const SLICE_WAY_TOO_LONG: &[u8] = unsafe { +LL | | SliceTransmute { +LL | | repr: SliceRepr { +LL | | ptr: &42, +... | +LL | | .slice +LL | | }; + | |__^ type validation failed: encountered invalid reference metadata: slice is bigger than largest supported object + | + = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the raw bytes of the constant (size: 8, align: 4) { + ╾─alloc3──╼ ff ff ff ff │ ╾──╼.... + } + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0080`. diff --git a/src/test/ui/consts/issue-63952.64bit.stderr b/src/test/ui/consts/issue-63952.64bit.stderr new file mode 100644 index 0000000000000..3335c5bf72ef5 --- /dev/null +++ b/src/test/ui/consts/issue-63952.64bit.stderr @@ -0,0 +1,20 @@ +error[E0080]: it is undefined behavior to use this value + --> $DIR/issue-63952.rs:17:1 + | +LL | / const SLICE_WAY_TOO_LONG: &[u8] = unsafe { +LL | | SliceTransmute { +LL | | repr: SliceRepr { +LL | | ptr: &42, +... | +LL | | .slice +LL | | }; + | |__^ type validation failed: encountered invalid reference metadata: slice is bigger than largest supported object + | + = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the raw bytes of the constant (size: 16, align: 8) { + ╾───────alloc3────────╼ ff ff ff ff ff ff ff ff │ ╾──────╼........ + } + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0080`. diff --git a/src/test/ui/consts/issue-63952.rs b/src/test/ui/consts/issue-63952.rs index f50e1e51f612f..5c83e6f45c9ba 100644 --- a/src/test/ui/consts/issue-63952.rs +++ b/src/test/ui/consts/issue-63952.rs @@ -1,4 +1,5 @@ // Regression test for #63952, shouldn't hang. +// stderr-per-bitwidth #[repr(C)] #[derive(Copy, Clone)] diff --git a/src/test/ui/consts/issue-63952.stderr b/src/test/ui/consts/issue-63952.stderr deleted file mode 100644 index 503c5706fa24e..0000000000000 --- a/src/test/ui/consts/issue-63952.stderr +++ /dev/null @@ -1,17 +0,0 @@ -error[E0080]: it is undefined behavior to use this value - --> $DIR/issue-63952.rs:16:1 - | -LL | / const SLICE_WAY_TOO_LONG: &[u8] = unsafe { -LL | | SliceTransmute { -LL | | repr: SliceRepr { -LL | | ptr: &42, -... | -LL | | .slice -LL | | }; - | |__^ type validation failed: encountered invalid reference metadata: slice is bigger than largest supported object - | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. - -error: aborting due to previous error - -For more information about this error, try `rustc --explain E0080`. diff --git a/src/test/ui/consts/issue-66345.rs b/src/test/ui/consts/issue-66345.rs index 7d0de73007c8c..4971d96476f43 100644 --- a/src/test/ui/consts/issue-66345.rs +++ b/src/test/ui/consts/issue-66345.rs @@ -1,8 +1,8 @@ // run-pass -// compile-flags: -Z mir-opt-level=3 +// compile-flags: -Z mir-opt-level=4 // Checks that the compiler does not ICE when passing references to field of by-value struct -// with -Z mir-opt-level=3 +// with -Z mir-opt-level=4 fn do_nothing(_: &()) {} diff --git a/src/test/ui/consts/issue-67529.rs b/src/test/ui/consts/issue-67529.rs index df4bc668bee17..dd24c2d27e27a 100644 --- a/src/test/ui/consts/issue-67529.rs +++ b/src/test/ui/consts/issue-67529.rs @@ -1,4 +1,4 @@ -// compile-flags: -Z mir-opt-level=2 +// compile-flags: -Z mir-opt-level=3 // run-pass struct Baz<T: ?Sized> { diff --git a/src/test/ui/consts/issue-67640.rs b/src/test/ui/consts/issue-67640.rs index bc0ee8d386f0a..4c71a2e022446 100644 --- a/src/test/ui/consts/issue-67640.rs +++ b/src/test/ui/consts/issue-67640.rs @@ -1,4 +1,4 @@ -// compile-flags: -Z mir-opt-level=3 +// compile-flags: -Z mir-opt-level=4 // run-pass struct X { diff --git a/src/test/ui/consts/issue-67641.rs b/src/test/ui/consts/issue-67641.rs index f50fba287a231..e5a74f15654c4 100644 --- a/src/test/ui/consts/issue-67641.rs +++ b/src/test/ui/consts/issue-67641.rs @@ -1,4 +1,4 @@ -// compile-flags: -Z mir-opt-level=2 +// compile-flags: -Z mir-opt-level=3 // run-pass use std::cell::Cell; diff --git a/src/test/ui/consts/issue-67862.rs b/src/test/ui/consts/issue-67862.rs index 84f72154d262f..b9e96a87f1472 100644 --- a/src/test/ui/consts/issue-67862.rs +++ b/src/test/ui/consts/issue-67862.rs @@ -1,4 +1,4 @@ -// compile-flags: -Z mir-opt-level=2 +// compile-flags: -Z mir-opt-level=3 // run-pass fn e220() -> (i64, i64) { diff --git a/src/test/ui/consts/issue-79690.64bit.stderr b/src/test/ui/consts/issue-79690.64bit.stderr new file mode 100644 index 0000000000000..2639bc4812cbb --- /dev/null +++ b/src/test/ui/consts/issue-79690.64bit.stderr @@ -0,0 +1,14 @@ +error[E0080]: it is undefined behavior to use this value + --> $DIR/issue-79690.rs:30:1 + | +LL | const G: Fat = unsafe { Transmute { t: FOO }.u }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered (potentially part of) a pointer at .1.<deref>.size.foo, but expected plain (non-pointer) bytes + | + = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the raw bytes of the constant (size: 16, align: 8) { + ╾───────alloc2────────╼ ╾───────alloc3────────╼ │ ╾──────╼╾──────╼ + } + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0080`. diff --git a/src/test/ui/consts/issue-79690.rs b/src/test/ui/consts/issue-79690.rs index a2e7b97b3187d..56747bf5a1107 100644 --- a/src/test/ui/consts/issue-79690.rs +++ b/src/test/ui/consts/issue-79690.rs @@ -1,5 +1,6 @@ // ignore-32bit // This test gives a different error on 32-bit architectures. +// stderr-per-bitwidth union Transmute<T: Copy, U: Copy> { t: T, diff --git a/src/test/ui/consts/issue-79690.stderr b/src/test/ui/consts/issue-79690.stderr deleted file mode 100644 index ca56ff220564a..0000000000000 --- a/src/test/ui/consts/issue-79690.stderr +++ /dev/null @@ -1,11 +0,0 @@ -error[E0080]: it is undefined behavior to use this value - --> $DIR/issue-79690.rs:29:1 - | -LL | const G: Fat = unsafe { Transmute { t: FOO }.u }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered (potentially part of) a pointer at .1.<deref>.size.foo, but expected plain (non-pointer) bytes - | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. - -error: aborting due to previous error - -For more information about this error, try `rustc --explain E0080`. diff --git a/src/test/ui/mir_check_nonconst.rs b/src/test/ui/consts/mir_check_nonconst.rs similarity index 100% rename from src/test/ui/mir_check_nonconst.rs rename to src/test/ui/consts/mir_check_nonconst.rs diff --git a/src/test/ui/mir_check_nonconst.stderr b/src/test/ui/consts/mir_check_nonconst.stderr similarity index 100% rename from src/test/ui/mir_check_nonconst.stderr rename to src/test/ui/consts/mir_check_nonconst.stderr diff --git a/src/test/ui/consts/miri_unleashed/const_refers_to_static2.32bit.stderr b/src/test/ui/consts/miri_unleashed/const_refers_to_static2.32bit.stderr new file mode 100644 index 0000000000000..6ca18290b43da --- /dev/null +++ b/src/test/ui/consts/miri_unleashed/const_refers_to_static2.32bit.stderr @@ -0,0 +1,55 @@ +error[E0080]: it is undefined behavior to use this value + --> $DIR/const_refers_to_static2.rs:11:1 + | +LL | / const REF_INTERIOR_MUT: &usize = { +LL | | +LL | | +LL | | +LL | | static FOO: AtomicUsize = AtomicUsize::new(0); +LL | | unsafe { &*(&FOO as *const _ as *const usize) } +LL | | }; + | |__^ type validation failed: encountered a reference pointing to a static variable + | + = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the raw bytes of the constant (size: 4, align: 4) { + ╾─alloc0──╼ │ ╾──╼ + } + +error[E0080]: it is undefined behavior to use this value + --> $DIR/const_refers_to_static2.rs:20:1 + | +LL | / const READ_IMMUT: &usize = { +LL | | +LL | | +LL | | +LL | | static FOO: usize = 0; +LL | | &FOO +LL | | }; + | |__^ type validation failed: encountered a reference pointing to a static variable + | + = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the raw bytes of the constant (size: 4, align: 4) { + ╾─alloc1──╼ │ ╾──╼ + } + +warning: skipping const checks + | +help: skipping check that does not even have a feature gate + --> $DIR/const_refers_to_static2.rs:16:18 + | +LL | unsafe { &*(&FOO as *const _ as *const usize) } + | ^^^ +help: skipping check for `const_raw_ptr_deref` feature + --> $DIR/const_refers_to_static2.rs:16:14 + | +LL | unsafe { &*(&FOO as *const _ as *const usize) } + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +help: skipping check that does not even have a feature gate + --> $DIR/const_refers_to_static2.rs:25:6 + | +LL | &FOO + | ^^^ + +error: aborting due to 2 previous errors; 1 warning emitted + +For more information about this error, try `rustc --explain E0080`. diff --git a/src/test/ui/consts/miri_unleashed/const_refers_to_static2.64bit.stderr b/src/test/ui/consts/miri_unleashed/const_refers_to_static2.64bit.stderr new file mode 100644 index 0000000000000..5521cd34aadcc --- /dev/null +++ b/src/test/ui/consts/miri_unleashed/const_refers_to_static2.64bit.stderr @@ -0,0 +1,55 @@ +error[E0080]: it is undefined behavior to use this value + --> $DIR/const_refers_to_static2.rs:11:1 + | +LL | / const REF_INTERIOR_MUT: &usize = { +LL | | +LL | | +LL | | +LL | | static FOO: AtomicUsize = AtomicUsize::new(0); +LL | | unsafe { &*(&FOO as *const _ as *const usize) } +LL | | }; + | |__^ type validation failed: encountered a reference pointing to a static variable + | + = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the raw bytes of the constant (size: 8, align: 8) { + ╾───────alloc0────────╼ │ ╾──────╼ + } + +error[E0080]: it is undefined behavior to use this value + --> $DIR/const_refers_to_static2.rs:20:1 + | +LL | / const READ_IMMUT: &usize = { +LL | | +LL | | +LL | | +LL | | static FOO: usize = 0; +LL | | &FOO +LL | | }; + | |__^ type validation failed: encountered a reference pointing to a static variable + | + = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the raw bytes of the constant (size: 8, align: 8) { + ╾───────alloc1────────╼ │ ╾──────╼ + } + +warning: skipping const checks + | +help: skipping check that does not even have a feature gate + --> $DIR/const_refers_to_static2.rs:16:18 + | +LL | unsafe { &*(&FOO as *const _ as *const usize) } + | ^^^ +help: skipping check for `const_raw_ptr_deref` feature + --> $DIR/const_refers_to_static2.rs:16:14 + | +LL | unsafe { &*(&FOO as *const _ as *const usize) } + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +help: skipping check that does not even have a feature gate + --> $DIR/const_refers_to_static2.rs:25:6 + | +LL | &FOO + | ^^^ + +error: aborting due to 2 previous errors; 1 warning emitted + +For more information about this error, try `rustc --explain E0080`. diff --git a/src/test/ui/consts/miri_unleashed/const_refers_to_static2.rs b/src/test/ui/consts/miri_unleashed/const_refers_to_static2.rs index b5db685ef2c06..2548474d4fd1c 100644 --- a/src/test/ui/consts/miri_unleashed/const_refers_to_static2.rs +++ b/src/test/ui/consts/miri_unleashed/const_refers_to_static2.rs @@ -1,4 +1,5 @@ // compile-flags: -Zunleash-the-miri-inside-of-you +// stderr-per-bitwidth #![allow(const_err)] use std::sync::atomic::AtomicUsize; @@ -9,7 +10,8 @@ use std::sync::atomic::Ordering; const REF_INTERIOR_MUT: &usize = { //~ ERROR undefined behavior to use this value //~| NOTE encountered a reference pointing to a static variable -//~| NOTE +//~| NOTE undefined behavior +//~| NOTE the raw bytes of the constant static FOO: AtomicUsize = AtomicUsize::new(0); unsafe { &*(&FOO as *const _ as *const usize) } }; @@ -17,7 +19,8 @@ const REF_INTERIOR_MUT: &usize = { //~ ERROR undefined behavior to use this valu // ok some day perhaps const READ_IMMUT: &usize = { //~ ERROR it is undefined behavior to use this value //~| NOTE encountered a reference pointing to a static variable -//~| NOTE +//~| NOTE undefined behavior +//~| NOTE the raw bytes of the constant static FOO: usize = 0; &FOO }; diff --git a/src/test/ui/consts/miri_unleashed/const_refers_to_static2.stderr b/src/test/ui/consts/miri_unleashed/const_refers_to_static2.stderr deleted file mode 100644 index 2e40b38dac768..0000000000000 --- a/src/test/ui/consts/miri_unleashed/const_refers_to_static2.stderr +++ /dev/null @@ -1,47 +0,0 @@ -error[E0080]: it is undefined behavior to use this value - --> $DIR/const_refers_to_static2.rs:10:1 - | -LL | / const REF_INTERIOR_MUT: &usize = { -LL | | -LL | | -LL | | static FOO: AtomicUsize = AtomicUsize::new(0); -LL | | unsafe { &*(&FOO as *const _ as *const usize) } -LL | | }; - | |__^ type validation failed: encountered a reference pointing to a static variable - | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. - -error[E0080]: it is undefined behavior to use this value - --> $DIR/const_refers_to_static2.rs:18:1 - | -LL | / const READ_IMMUT: &usize = { -LL | | -LL | | -LL | | static FOO: usize = 0; -LL | | &FOO -LL | | }; - | |__^ type validation failed: encountered a reference pointing to a static variable - | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. - -warning: skipping const checks - | -help: skipping check that does not even have a feature gate - --> $DIR/const_refers_to_static2.rs:14:18 - | -LL | unsafe { &*(&FOO as *const _ as *const usize) } - | ^^^ -help: skipping check for `const_raw_ptr_deref` feature - --> $DIR/const_refers_to_static2.rs:14:14 - | -LL | unsafe { &*(&FOO as *const _ as *const usize) } - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -help: skipping check that does not even have a feature gate - --> $DIR/const_refers_to_static2.rs:22:6 - | -LL | &FOO - | ^^^ - -error: aborting due to 2 previous errors; 1 warning emitted - -For more information about this error, try `rustc --explain E0080`. diff --git a/src/test/ui/consts/miri_unleashed/const_refers_to_static_cross_crate.32bit.stderr b/src/test/ui/consts/miri_unleashed/const_refers_to_static_cross_crate.32bit.stderr new file mode 100644 index 0000000000000..73d1d7c5b9507 --- /dev/null +++ b/src/test/ui/consts/miri_unleashed/const_refers_to_static_cross_crate.32bit.stderr @@ -0,0 +1,184 @@ +error[E0080]: it is undefined behavior to use this value + --> $DIR/const_refers_to_static_cross_crate.rs:12:1 + | +LL | / const SLICE_MUT: &[u8; 1] = { +LL | | +LL | | +LL | | +LL | | unsafe { &static_cross_crate::ZERO } +LL | | }; + | |__^ type validation failed: encountered a reference pointing to a static variable + | + = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the raw bytes of the constant (size: 4, align: 4) { + ╾─alloc0──╼ │ ╾──╼ + } + +error: could not evaluate constant pattern + --> $DIR/const_refers_to_static_cross_crate.rs:47:9 + | +LL | SLICE_MUT => true, + | ^^^^^^^^^ + +error[E0080]: it is undefined behavior to use this value + --> $DIR/const_refers_to_static_cross_crate.rs:19:1 + | +LL | / const U8_MUT: &u8 = { +LL | | +LL | | +LL | | +LL | | unsafe { &static_cross_crate::ZERO[0] } +LL | | }; + | |__^ type validation failed: encountered a reference pointing to a static variable + | + = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the raw bytes of the constant (size: 4, align: 4) { + ╾─alloc0──╼ │ ╾──╼ + } + +error: could not evaluate constant pattern + --> $DIR/const_refers_to_static_cross_crate.rs:56:9 + | +LL | U8_MUT => true, + | ^^^^^^ + +warning: any use of this value will cause an error + --> $DIR/const_refers_to_static_cross_crate.rs:29:15 + | +LL | / const U8_MUT2: &u8 = { +LL | | unsafe { &(*static_cross_crate::ZERO_REF)[0] } + | | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ constant accesses static +LL | | +LL | | +LL | | +LL | | +LL | | }; + | |__- + | +note: the lint level is defined here + --> $DIR/const_refers_to_static_cross_crate.rs:27:8 + | +LL | #[warn(const_err)] + | ^^^^^^^^^ + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #71800 <https://github.com/rust-lang/rust/issues/71800> + +error: could not evaluate constant pattern + --> $DIR/const_refers_to_static_cross_crate.rs:67:9 + | +LL | U8_MUT2 => true, + | ^^^^^^^ + +warning: any use of this value will cause an error + --> $DIR/const_refers_to_static_cross_crate.rs:37:51 + | +LL | / const U8_MUT3: &u8 = { +LL | | unsafe { match static_cross_crate::OPT_ZERO { Some(ref u) => u, None => panic!() } } + | | ^^^^^^^^^^^ constant accesses static +LL | | +LL | | +... | +LL | | +LL | | }; + | |__- + | +note: the lint level is defined here + --> $DIR/const_refers_to_static_cross_crate.rs:35:8 + | +LL | #[warn(const_err)] + | ^^^^^^^^^ + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #71800 <https://github.com/rust-lang/rust/issues/71800> + +error: could not evaluate constant pattern + --> $DIR/const_refers_to_static_cross_crate.rs:75:9 + | +LL | U8_MUT3 => true, + | ^^^^^^^ + +error: could not evaluate constant pattern + --> $DIR/const_refers_to_static_cross_crate.rs:47:9 + | +LL | SLICE_MUT => true, + | ^^^^^^^^^ + +error: could not evaluate constant pattern + --> $DIR/const_refers_to_static_cross_crate.rs:56:9 + | +LL | U8_MUT => true, + | ^^^^^^ + +error: could not evaluate constant pattern + --> $DIR/const_refers_to_static_cross_crate.rs:67:9 + | +LL | U8_MUT2 => true, + | ^^^^^^^ + +error: could not evaluate constant pattern + --> $DIR/const_refers_to_static_cross_crate.rs:75:9 + | +LL | U8_MUT3 => true, + | ^^^^^^^ + +warning: skipping const checks + | +help: skipping check that does not even have a feature gate + --> $DIR/const_refers_to_static_cross_crate.rs:16:15 + | +LL | unsafe { &static_cross_crate::ZERO } + | ^^^^^^^^^^^^^^^^^^^^^^^^ +help: skipping check that does not even have a feature gate + --> $DIR/const_refers_to_static_cross_crate.rs:16:15 + | +LL | unsafe { &static_cross_crate::ZERO } + | ^^^^^^^^^^^^^^^^^^^^^^^^ +help: skipping check that does not even have a feature gate + --> $DIR/const_refers_to_static_cross_crate.rs:23:15 + | +LL | unsafe { &static_cross_crate::ZERO[0] } + | ^^^^^^^^^^^^^^^^^^^^^^^^ +help: skipping check that does not even have a feature gate + --> $DIR/const_refers_to_static_cross_crate.rs:23:15 + | +LL | unsafe { &static_cross_crate::ZERO[0] } + | ^^^^^^^^^^^^^^^^^^^^^^^^ +help: skipping check that does not even have a feature gate + --> $DIR/const_refers_to_static_cross_crate.rs:23:15 + | +LL | unsafe { &static_cross_crate::ZERO[0] } + | ^^^^^^^^^^^^^^^^^^^^^^^^ +help: skipping check that does not even have a feature gate + --> $DIR/const_refers_to_static_cross_crate.rs:29:17 + | +LL | unsafe { &(*static_cross_crate::ZERO_REF)[0] } + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +help: skipping check that does not even have a feature gate + --> $DIR/const_refers_to_static_cross_crate.rs:37:20 + | +LL | unsafe { match static_cross_crate::OPT_ZERO { Some(ref u) => u, None => panic!() } } + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +help: skipping check that does not even have a feature gate + --> $DIR/const_refers_to_static_cross_crate.rs:37:20 + | +LL | unsafe { match static_cross_crate::OPT_ZERO { Some(ref u) => u, None => panic!() } } + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +help: skipping check that does not even have a feature gate + --> $DIR/const_refers_to_static_cross_crate.rs:37:20 + | +LL | unsafe { match static_cross_crate::OPT_ZERO { Some(ref u) => u, None => panic!() } } + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +help: skipping check for `const_panic` feature + --> $DIR/const_refers_to_static_cross_crate.rs:37:77 + | +LL | unsafe { match static_cross_crate::OPT_ZERO { Some(ref u) => u, None => panic!() } } + | ^^^^^^^^ +help: skipping check that does not even have a feature gate + --> $DIR/const_refers_to_static_cross_crate.rs:37:20 + | +LL | unsafe { match static_cross_crate::OPT_ZERO { Some(ref u) => u, None => panic!() } } + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + = note: this warning originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: aborting due to 10 previous errors; 3 warnings emitted + +For more information about this error, try `rustc --explain E0080`. diff --git a/src/test/ui/consts/miri_unleashed/const_refers_to_static_cross_crate.64bit.stderr b/src/test/ui/consts/miri_unleashed/const_refers_to_static_cross_crate.64bit.stderr new file mode 100644 index 0000000000000..7228f7178ff87 --- /dev/null +++ b/src/test/ui/consts/miri_unleashed/const_refers_to_static_cross_crate.64bit.stderr @@ -0,0 +1,184 @@ +error[E0080]: it is undefined behavior to use this value + --> $DIR/const_refers_to_static_cross_crate.rs:12:1 + | +LL | / const SLICE_MUT: &[u8; 1] = { +LL | | +LL | | +LL | | +LL | | unsafe { &static_cross_crate::ZERO } +LL | | }; + | |__^ type validation failed: encountered a reference pointing to a static variable + | + = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the raw bytes of the constant (size: 8, align: 8) { + ╾───────alloc0────────╼ │ ╾──────╼ + } + +error: could not evaluate constant pattern + --> $DIR/const_refers_to_static_cross_crate.rs:47:9 + | +LL | SLICE_MUT => true, + | ^^^^^^^^^ + +error[E0080]: it is undefined behavior to use this value + --> $DIR/const_refers_to_static_cross_crate.rs:19:1 + | +LL | / const U8_MUT: &u8 = { +LL | | +LL | | +LL | | +LL | | unsafe { &static_cross_crate::ZERO[0] } +LL | | }; + | |__^ type validation failed: encountered a reference pointing to a static variable + | + = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the raw bytes of the constant (size: 8, align: 8) { + ╾───────alloc0────────╼ │ ╾──────╼ + } + +error: could not evaluate constant pattern + --> $DIR/const_refers_to_static_cross_crate.rs:56:9 + | +LL | U8_MUT => true, + | ^^^^^^ + +warning: any use of this value will cause an error + --> $DIR/const_refers_to_static_cross_crate.rs:29:15 + | +LL | / const U8_MUT2: &u8 = { +LL | | unsafe { &(*static_cross_crate::ZERO_REF)[0] } + | | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ constant accesses static +LL | | +LL | | +LL | | +LL | | +LL | | }; + | |__- + | +note: the lint level is defined here + --> $DIR/const_refers_to_static_cross_crate.rs:27:8 + | +LL | #[warn(const_err)] + | ^^^^^^^^^ + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #71800 <https://github.com/rust-lang/rust/issues/71800> + +error: could not evaluate constant pattern + --> $DIR/const_refers_to_static_cross_crate.rs:67:9 + | +LL | U8_MUT2 => true, + | ^^^^^^^ + +warning: any use of this value will cause an error + --> $DIR/const_refers_to_static_cross_crate.rs:37:51 + | +LL | / const U8_MUT3: &u8 = { +LL | | unsafe { match static_cross_crate::OPT_ZERO { Some(ref u) => u, None => panic!() } } + | | ^^^^^^^^^^^ constant accesses static +LL | | +LL | | +... | +LL | | +LL | | }; + | |__- + | +note: the lint level is defined here + --> $DIR/const_refers_to_static_cross_crate.rs:35:8 + | +LL | #[warn(const_err)] + | ^^^^^^^^^ + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #71800 <https://github.com/rust-lang/rust/issues/71800> + +error: could not evaluate constant pattern + --> $DIR/const_refers_to_static_cross_crate.rs:75:9 + | +LL | U8_MUT3 => true, + | ^^^^^^^ + +error: could not evaluate constant pattern + --> $DIR/const_refers_to_static_cross_crate.rs:47:9 + | +LL | SLICE_MUT => true, + | ^^^^^^^^^ + +error: could not evaluate constant pattern + --> $DIR/const_refers_to_static_cross_crate.rs:56:9 + | +LL | U8_MUT => true, + | ^^^^^^ + +error: could not evaluate constant pattern + --> $DIR/const_refers_to_static_cross_crate.rs:67:9 + | +LL | U8_MUT2 => true, + | ^^^^^^^ + +error: could not evaluate constant pattern + --> $DIR/const_refers_to_static_cross_crate.rs:75:9 + | +LL | U8_MUT3 => true, + | ^^^^^^^ + +warning: skipping const checks + | +help: skipping check that does not even have a feature gate + --> $DIR/const_refers_to_static_cross_crate.rs:16:15 + | +LL | unsafe { &static_cross_crate::ZERO } + | ^^^^^^^^^^^^^^^^^^^^^^^^ +help: skipping check that does not even have a feature gate + --> $DIR/const_refers_to_static_cross_crate.rs:16:15 + | +LL | unsafe { &static_cross_crate::ZERO } + | ^^^^^^^^^^^^^^^^^^^^^^^^ +help: skipping check that does not even have a feature gate + --> $DIR/const_refers_to_static_cross_crate.rs:23:15 + | +LL | unsafe { &static_cross_crate::ZERO[0] } + | ^^^^^^^^^^^^^^^^^^^^^^^^ +help: skipping check that does not even have a feature gate + --> $DIR/const_refers_to_static_cross_crate.rs:23:15 + | +LL | unsafe { &static_cross_crate::ZERO[0] } + | ^^^^^^^^^^^^^^^^^^^^^^^^ +help: skipping check that does not even have a feature gate + --> $DIR/const_refers_to_static_cross_crate.rs:23:15 + | +LL | unsafe { &static_cross_crate::ZERO[0] } + | ^^^^^^^^^^^^^^^^^^^^^^^^ +help: skipping check that does not even have a feature gate + --> $DIR/const_refers_to_static_cross_crate.rs:29:17 + | +LL | unsafe { &(*static_cross_crate::ZERO_REF)[0] } + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +help: skipping check that does not even have a feature gate + --> $DIR/const_refers_to_static_cross_crate.rs:37:20 + | +LL | unsafe { match static_cross_crate::OPT_ZERO { Some(ref u) => u, None => panic!() } } + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +help: skipping check that does not even have a feature gate + --> $DIR/const_refers_to_static_cross_crate.rs:37:20 + | +LL | unsafe { match static_cross_crate::OPT_ZERO { Some(ref u) => u, None => panic!() } } + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +help: skipping check that does not even have a feature gate + --> $DIR/const_refers_to_static_cross_crate.rs:37:20 + | +LL | unsafe { match static_cross_crate::OPT_ZERO { Some(ref u) => u, None => panic!() } } + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +help: skipping check for `const_panic` feature + --> $DIR/const_refers_to_static_cross_crate.rs:37:77 + | +LL | unsafe { match static_cross_crate::OPT_ZERO { Some(ref u) => u, None => panic!() } } + | ^^^^^^^^ +help: skipping check that does not even have a feature gate + --> $DIR/const_refers_to_static_cross_crate.rs:37:20 + | +LL | unsafe { match static_cross_crate::OPT_ZERO { Some(ref u) => u, None => panic!() } } + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + = note: this warning originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: aborting due to 10 previous errors; 3 warnings emitted + +For more information about this error, try `rustc --explain E0080`. diff --git a/src/test/ui/consts/miri_unleashed/const_refers_to_static_cross_crate.rs b/src/test/ui/consts/miri_unleashed/const_refers_to_static_cross_crate.rs index 7bbe9c87705a3..53f70198f4ced 100644 --- a/src/test/ui/consts/miri_unleashed/const_refers_to_static_cross_crate.rs +++ b/src/test/ui/consts/miri_unleashed/const_refers_to_static_cross_crate.rs @@ -1,5 +1,6 @@ // compile-flags: -Zunleash-the-miri-inside-of-you // aux-build:static_cross_crate.rs +// stderr-per-bitwidth #![allow(const_err)] #![feature(exclusive_range_pattern, half_open_range_patterns)] @@ -10,13 +11,15 @@ extern crate static_cross_crate; // Allowing this would be a disaster for pattern matching, we could violate exhaustiveness checking! const SLICE_MUT: &[u8; 1] = { //~ ERROR undefined behavior to use this value //~| NOTE encountered a reference pointing to a static variable -//~| NOTE +//~| NOTE undefined behavior +//~| NOTE the raw bytes of the constant unsafe { &static_cross_crate::ZERO } }; const U8_MUT: &u8 = { //~ ERROR undefined behavior to use this value //~| NOTE encountered a reference pointing to a static variable -//~| NOTE +//~| NOTE undefined behavior +//~| NOTE the raw bytes of the constant unsafe { &static_cross_crate::ZERO[0] } }; diff --git a/src/test/ui/consts/miri_unleashed/const_refers_to_static_cross_crate.stderr b/src/test/ui/consts/miri_unleashed/const_refers_to_static_cross_crate.stderr deleted file mode 100644 index a9d6fde6c05be..0000000000000 --- a/src/test/ui/consts/miri_unleashed/const_refers_to_static_cross_crate.stderr +++ /dev/null @@ -1,176 +0,0 @@ -error[E0080]: it is undefined behavior to use this value - --> $DIR/const_refers_to_static_cross_crate.rs:11:1 - | -LL | / const SLICE_MUT: &[u8; 1] = { -LL | | -LL | | -LL | | unsafe { &static_cross_crate::ZERO } -LL | | }; - | |__^ type validation failed: encountered a reference pointing to a static variable - | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. - -error: could not evaluate constant pattern - --> $DIR/const_refers_to_static_cross_crate.rs:44:9 - | -LL | SLICE_MUT => true, - | ^^^^^^^^^ - -error[E0080]: it is undefined behavior to use this value - --> $DIR/const_refers_to_static_cross_crate.rs:17:1 - | -LL | / const U8_MUT: &u8 = { -LL | | -LL | | -LL | | unsafe { &static_cross_crate::ZERO[0] } -LL | | }; - | |__^ type validation failed: encountered a reference pointing to a static variable - | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. - -error: could not evaluate constant pattern - --> $DIR/const_refers_to_static_cross_crate.rs:53:9 - | -LL | U8_MUT => true, - | ^^^^^^ - -warning: any use of this value will cause an error - --> $DIR/const_refers_to_static_cross_crate.rs:26:15 - | -LL | / const U8_MUT2: &u8 = { -LL | | unsafe { &(*static_cross_crate::ZERO_REF)[0] } - | | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ constant accesses static -LL | | -LL | | -LL | | -LL | | -LL | | }; - | |__- - | -note: the lint level is defined here - --> $DIR/const_refers_to_static_cross_crate.rs:24:8 - | -LL | #[warn(const_err)] - | ^^^^^^^^^ - = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = note: for more information, see issue #71800 <https://github.com/rust-lang/rust/issues/71800> - -error: could not evaluate constant pattern - --> $DIR/const_refers_to_static_cross_crate.rs:64:9 - | -LL | U8_MUT2 => true, - | ^^^^^^^ - -warning: any use of this value will cause an error - --> $DIR/const_refers_to_static_cross_crate.rs:34:51 - | -LL | / const U8_MUT3: &u8 = { -LL | | unsafe { match static_cross_crate::OPT_ZERO { Some(ref u) => u, None => panic!() } } - | | ^^^^^^^^^^^ constant accesses static -LL | | -LL | | -... | -LL | | -LL | | }; - | |__- - | -note: the lint level is defined here - --> $DIR/const_refers_to_static_cross_crate.rs:32:8 - | -LL | #[warn(const_err)] - | ^^^^^^^^^ - = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = note: for more information, see issue #71800 <https://github.com/rust-lang/rust/issues/71800> - -error: could not evaluate constant pattern - --> $DIR/const_refers_to_static_cross_crate.rs:72:9 - | -LL | U8_MUT3 => true, - | ^^^^^^^ - -error: could not evaluate constant pattern - --> $DIR/const_refers_to_static_cross_crate.rs:44:9 - | -LL | SLICE_MUT => true, - | ^^^^^^^^^ - -error: could not evaluate constant pattern - --> $DIR/const_refers_to_static_cross_crate.rs:53:9 - | -LL | U8_MUT => true, - | ^^^^^^ - -error: could not evaluate constant pattern - --> $DIR/const_refers_to_static_cross_crate.rs:64:9 - | -LL | U8_MUT2 => true, - | ^^^^^^^ - -error: could not evaluate constant pattern - --> $DIR/const_refers_to_static_cross_crate.rs:72:9 - | -LL | U8_MUT3 => true, - | ^^^^^^^ - -warning: skipping const checks - | -help: skipping check that does not even have a feature gate - --> $DIR/const_refers_to_static_cross_crate.rs:14:15 - | -LL | unsafe { &static_cross_crate::ZERO } - | ^^^^^^^^^^^^^^^^^^^^^^^^ -help: skipping check that does not even have a feature gate - --> $DIR/const_refers_to_static_cross_crate.rs:14:15 - | -LL | unsafe { &static_cross_crate::ZERO } - | ^^^^^^^^^^^^^^^^^^^^^^^^ -help: skipping check that does not even have a feature gate - --> $DIR/const_refers_to_static_cross_crate.rs:20:15 - | -LL | unsafe { &static_cross_crate::ZERO[0] } - | ^^^^^^^^^^^^^^^^^^^^^^^^ -help: skipping check that does not even have a feature gate - --> $DIR/const_refers_to_static_cross_crate.rs:20:15 - | -LL | unsafe { &static_cross_crate::ZERO[0] } - | ^^^^^^^^^^^^^^^^^^^^^^^^ -help: skipping check that does not even have a feature gate - --> $DIR/const_refers_to_static_cross_crate.rs:20:15 - | -LL | unsafe { &static_cross_crate::ZERO[0] } - | ^^^^^^^^^^^^^^^^^^^^^^^^ -help: skipping check that does not even have a feature gate - --> $DIR/const_refers_to_static_cross_crate.rs:26:17 - | -LL | unsafe { &(*static_cross_crate::ZERO_REF)[0] } - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -help: skipping check that does not even have a feature gate - --> $DIR/const_refers_to_static_cross_crate.rs:34:20 - | -LL | unsafe { match static_cross_crate::OPT_ZERO { Some(ref u) => u, None => panic!() } } - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -help: skipping check that does not even have a feature gate - --> $DIR/const_refers_to_static_cross_crate.rs:34:20 - | -LL | unsafe { match static_cross_crate::OPT_ZERO { Some(ref u) => u, None => panic!() } } - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -help: skipping check that does not even have a feature gate - --> $DIR/const_refers_to_static_cross_crate.rs:34:20 - | -LL | unsafe { match static_cross_crate::OPT_ZERO { Some(ref u) => u, None => panic!() } } - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -help: skipping check for `const_panic` feature - --> $DIR/const_refers_to_static_cross_crate.rs:34:77 - | -LL | unsafe { match static_cross_crate::OPT_ZERO { Some(ref u) => u, None => panic!() } } - | ^^^^^^^^ -help: skipping check that does not even have a feature gate - --> $DIR/const_refers_to_static_cross_crate.rs:34:20 - | -LL | unsafe { match static_cross_crate::OPT_ZERO { Some(ref u) => u, None => panic!() } } - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - = note: this warning originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) - -error: aborting due to 10 previous errors; 3 warnings emitted - -For more information about this error, try `rustc --explain E0080`. diff --git a/src/test/ui/consts/miri_unleashed/drop.stderr b/src/test/ui/consts/miri_unleashed/drop.stderr index eb1b42c57bc51..f79dc454a83a0 100644 --- a/src/test/ui/consts/miri_unleashed/drop.stderr +++ b/src/test/ui/consts/miri_unleashed/drop.stderr @@ -5,7 +5,7 @@ LL | pub unsafe fn drop_in_place<T: ?Sized>(to_drop: *mut T) { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | | | calling non-const function `<Vec<i32> as Drop>::drop` - | inside `drop_in_place::<Vec<i32>> - shim(Some(Vec<i32>))` at $SRC_DIR/core/src/ptr/mod.rs:LL:COL + | inside `std::ptr::drop_in_place::<Vec<i32>> - shim(Some(Vec<i32>))` at $SRC_DIR/core/src/ptr/mod.rs:LL:COL | ::: $DIR/drop.rs:18:1 | diff --git a/src/test/ui/consts/miri_unleashed/mutable_references_err.32bit.stderr b/src/test/ui/consts/miri_unleashed/mutable_references_err.32bit.stderr new file mode 100644 index 0000000000000..62126ef2babd3 --- /dev/null +++ b/src/test/ui/consts/miri_unleashed/mutable_references_err.32bit.stderr @@ -0,0 +1,56 @@ +error[E0080]: it is undefined behavior to use this value + --> $DIR/mutable_references_err.rs:17:1 + | +LL | / const MUH: Meh = Meh { +LL | | x: &UnsafeCell::new(42), +LL | | }; + | |__^ type validation failed: encountered `UnsafeCell` in a `const` at .x.<deref> + | + = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the raw bytes of the constant (size: 4, align: 4) { + ╾─alloc2──╼ │ ╾──╼ + } + +error[E0080]: it is undefined behavior to use this value + --> $DIR/mutable_references_err.rs:27:1 + | +LL | const SNEAKY: &dyn Sync = &Synced { x: UnsafeCell::new(42) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered `UnsafeCell` in a `const` at .<deref>.<dyn-downcast>.x + | + = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the raw bytes of the constant (size: 8, align: 4) { + ╾─alloc6──╼ ╾─alloc7──╼ │ ╾──╼╾──╼ + } + +error[E0080]: it is undefined behavior to use this value + --> $DIR/mutable_references_err.rs:31:1 + | +LL | const BLUNT: &mut i32 = &mut 42; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered mutable reference in a `const` + | + = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the raw bytes of the constant (size: 4, align: 4) { + ╾─alloc10─╼ │ ╾──╼ + } + +warning: skipping const checks + | +help: skipping check that does not even have a feature gate + --> $DIR/mutable_references_err.rs:18:8 + | +LL | x: &UnsafeCell::new(42), + | ^^^^^^^^^^^^^^^^^^^^ +help: skipping check that does not even have a feature gate + --> $DIR/mutable_references_err.rs:27:27 + | +LL | const SNEAKY: &dyn Sync = &Synced { x: UnsafeCell::new(42) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +help: skipping check that does not even have a feature gate + --> $DIR/mutable_references_err.rs:31:25 + | +LL | const BLUNT: &mut i32 = &mut 42; + | ^^^^^^^ + +error: aborting due to 3 previous errors; 1 warning emitted + +For more information about this error, try `rustc --explain E0080`. diff --git a/src/test/ui/consts/miri_unleashed/mutable_references_err.64bit.stderr b/src/test/ui/consts/miri_unleashed/mutable_references_err.64bit.stderr new file mode 100644 index 0000000000000..606184e573255 --- /dev/null +++ b/src/test/ui/consts/miri_unleashed/mutable_references_err.64bit.stderr @@ -0,0 +1,56 @@ +error[E0080]: it is undefined behavior to use this value + --> $DIR/mutable_references_err.rs:17:1 + | +LL | / const MUH: Meh = Meh { +LL | | x: &UnsafeCell::new(42), +LL | | }; + | |__^ type validation failed: encountered `UnsafeCell` in a `const` at .x.<deref> + | + = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the raw bytes of the constant (size: 8, align: 8) { + ╾───────alloc2────────╼ │ ╾──────╼ + } + +error[E0080]: it is undefined behavior to use this value + --> $DIR/mutable_references_err.rs:27:1 + | +LL | const SNEAKY: &dyn Sync = &Synced { x: UnsafeCell::new(42) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered `UnsafeCell` in a `const` at .<deref>.<dyn-downcast>.x + | + = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the raw bytes of the constant (size: 16, align: 8) { + ╾───────alloc6────────╼ ╾───────alloc7────────╼ │ ╾──────╼╾──────╼ + } + +error[E0080]: it is undefined behavior to use this value + --> $DIR/mutable_references_err.rs:31:1 + | +LL | const BLUNT: &mut i32 = &mut 42; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered mutable reference in a `const` + | + = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the raw bytes of the constant (size: 8, align: 8) { + ╾───────alloc10───────╼ │ ╾──────╼ + } + +warning: skipping const checks + | +help: skipping check that does not even have a feature gate + --> $DIR/mutable_references_err.rs:18:8 + | +LL | x: &UnsafeCell::new(42), + | ^^^^^^^^^^^^^^^^^^^^ +help: skipping check that does not even have a feature gate + --> $DIR/mutable_references_err.rs:27:27 + | +LL | const SNEAKY: &dyn Sync = &Synced { x: UnsafeCell::new(42) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +help: skipping check that does not even have a feature gate + --> $DIR/mutable_references_err.rs:31:25 + | +LL | const BLUNT: &mut i32 = &mut 42; + | ^^^^^^^ + +error: aborting due to 3 previous errors; 1 warning emitted + +For more information about this error, try `rustc --explain E0080`. diff --git a/src/test/ui/consts/miri_unleashed/mutable_references_err.rs b/src/test/ui/consts/miri_unleashed/mutable_references_err.rs index 195414dbad9a2..722b9cf94e859 100644 --- a/src/test/ui/consts/miri_unleashed/mutable_references_err.rs +++ b/src/test/ui/consts/miri_unleashed/mutable_references_err.rs @@ -1,3 +1,4 @@ +// stderr-per-bitwidth // compile-flags: -Zunleash-the-miri-inside-of-you #![allow(const_err)] diff --git a/src/test/ui/consts/miri_unleashed/mutable_references_err.stderr b/src/test/ui/consts/miri_unleashed/mutable_references_err.stderr deleted file mode 100644 index 0c206dd51aaab..0000000000000 --- a/src/test/ui/consts/miri_unleashed/mutable_references_err.stderr +++ /dev/null @@ -1,47 +0,0 @@ -error[E0080]: it is undefined behavior to use this value - --> $DIR/mutable_references_err.rs:16:1 - | -LL | / const MUH: Meh = Meh { -LL | | x: &UnsafeCell::new(42), -LL | | }; - | |__^ type validation failed: encountered `UnsafeCell` in a `const` at .x.<deref> - | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. - -error[E0080]: it is undefined behavior to use this value - --> $DIR/mutable_references_err.rs:26:1 - | -LL | const SNEAKY: &dyn Sync = &Synced { x: UnsafeCell::new(42) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered `UnsafeCell` in a `const` at .<deref>.<dyn-downcast>.x - | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. - -error[E0080]: it is undefined behavior to use this value - --> $DIR/mutable_references_err.rs:30:1 - | -LL | const BLUNT: &mut i32 = &mut 42; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered mutable reference in a `const` - | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. - -warning: skipping const checks - | -help: skipping check that does not even have a feature gate - --> $DIR/mutable_references_err.rs:17:8 - | -LL | x: &UnsafeCell::new(42), - | ^^^^^^^^^^^^^^^^^^^^ -help: skipping check that does not even have a feature gate - --> $DIR/mutable_references_err.rs:26:27 - | -LL | const SNEAKY: &dyn Sync = &Synced { x: UnsafeCell::new(42) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -help: skipping check that does not even have a feature gate - --> $DIR/mutable_references_err.rs:30:25 - | -LL | const BLUNT: &mut i32 = &mut 42; - | ^^^^^^^ - -error: aborting due to 3 previous errors; 1 warning emitted - -For more information about this error, try `rustc --explain E0080`. diff --git a/src/test/ui/consts/miri_unleashed/tls.stderr b/src/test/ui/consts/miri_unleashed/tls.stderr index 5cef636e0a817..62e2dc7121097 100644 --- a/src/test/ui/consts/miri_unleashed/tls.stderr +++ b/src/test/ui/consts/miri_unleashed/tls.stderr @@ -2,13 +2,13 @@ error[E0080]: could not evaluate static initializer --> $DIR/tls.rs:12:25 | LL | unsafe { let _val = A; } - | ^ cannot access thread local static (DefId(0:4 ~ tls[317d]::A)) + | ^ cannot access thread local static (DefId(0:6 ~ tls[317d]::A)) error[E0080]: could not evaluate static initializer --> $DIR/tls.rs:19:26 | LL | unsafe { let _val = &A; } - | ^ cannot access thread local static (DefId(0:4 ~ tls[317d]::A)) + | ^ cannot access thread local static (DefId(0:6 ~ tls[317d]::A)) warning: skipping const checks | diff --git a/src/test/ui/consts/offset_ub.rs b/src/test/ui/consts/offset_ub.rs index 4f943ed9ad194..7ce45ba9c4b2b 100644 --- a/src/test/ui/consts/offset_ub.rs +++ b/src/test/ui/consts/offset_ub.rs @@ -1,4 +1,3 @@ -// ignore-tidy-linelength #![feature(const_ptr_offset)] use std::ptr; diff --git a/src/test/ui/consts/offset_ub.stderr b/src/test/ui/consts/offset_ub.stderr index 5e8b7a8e0b698..082142fbbb77c 100644 --- a/src/test/ui/consts/offset_ub.stderr +++ b/src/test/ui/consts/offset_ub.stderr @@ -6,9 +6,9 @@ LL | unsafe { intrinsics::offset(self, count) } | | | overflowing in-bounds pointer arithmetic | inside `ptr::const_ptr::<impl *const u8>::offset` at $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL - | inside `BEFORE_START` at $DIR/offset_ub.rs:7:46 + | inside `BEFORE_START` at $DIR/offset_ub.rs:6:46 | - ::: $DIR/offset_ub.rs:7:1 + ::: $DIR/offset_ub.rs:6:1 | LL | pub const BEFORE_START: *const u8 = unsafe { (&0u8 as *const u8).offset(-1) }; | ------------------------------------------------------------------------------ @@ -25,9 +25,9 @@ LL | unsafe { intrinsics::offset(self, count) } | | | inbounds test failed: pointer must be in-bounds at offset 2, but is outside bounds of allocN which has size 1 | inside `ptr::const_ptr::<impl *const u8>::offset` at $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL - | inside `AFTER_END` at $DIR/offset_ub.rs:8:43 + | inside `AFTER_END` at $DIR/offset_ub.rs:7:43 | - ::: $DIR/offset_ub.rs:8:1 + ::: $DIR/offset_ub.rs:7:1 | LL | pub const AFTER_END: *const u8 = unsafe { (&0u8 as *const u8).offset(2) }; | -------------------------------------------------------------------------- @@ -43,9 +43,9 @@ LL | unsafe { intrinsics::offset(self, count) } | | | inbounds test failed: pointer must be in-bounds at offset 101, but is outside bounds of allocN which has size 100 | inside `ptr::const_ptr::<impl *const u8>::offset` at $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL - | inside `AFTER_ARRAY` at $DIR/offset_ub.rs:9:45 + | inside `AFTER_ARRAY` at $DIR/offset_ub.rs:8:45 | - ::: $DIR/offset_ub.rs:9:1 + ::: $DIR/offset_ub.rs:8:1 | LL | pub const AFTER_ARRAY: *const u8 = unsafe { [0u8; 100].as_ptr().offset(101) }; | ------------------------------------------------------------------------------ @@ -61,9 +61,9 @@ LL | unsafe { intrinsics::offset(self, count) } | | | overflowing in-bounds pointer arithmetic | inside `ptr::const_ptr::<impl *const u16>::offset` at $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL - | inside `OVERFLOW` at $DIR/offset_ub.rs:11:43 + | inside `OVERFLOW` at $DIR/offset_ub.rs:10:43 | - ::: $DIR/offset_ub.rs:11:1 + ::: $DIR/offset_ub.rs:10:1 | LL | pub const OVERFLOW: *const u16 = unsafe { [0u16; 1].as_ptr().offset(isize::MAX) }; | ---------------------------------------------------------------------------------- @@ -79,9 +79,9 @@ LL | unsafe { intrinsics::offset(self, count) } | | | overflowing in-bounds pointer arithmetic | inside `ptr::const_ptr::<impl *const u16>::offset` at $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL - | inside `UNDERFLOW` at $DIR/offset_ub.rs:12:44 + | inside `UNDERFLOW` at $DIR/offset_ub.rs:11:44 | - ::: $DIR/offset_ub.rs:12:1 + ::: $DIR/offset_ub.rs:11:1 | LL | pub const UNDERFLOW: *const u16 = unsafe { [0u16; 1].as_ptr().offset(isize::MIN) }; | ----------------------------------------------------------------------------------- @@ -97,9 +97,9 @@ LL | unsafe { intrinsics::offset(self, count) } | | | overflowing in-bounds pointer arithmetic | inside `ptr::const_ptr::<impl *const u8>::offset` at $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL - | inside `OVERFLOW_ADDRESS_SPACE` at $DIR/offset_ub.rs:13:56 + | inside `OVERFLOW_ADDRESS_SPACE` at $DIR/offset_ub.rs:12:56 | - ::: $DIR/offset_ub.rs:13:1 + ::: $DIR/offset_ub.rs:12:1 | LL | pub const OVERFLOW_ADDRESS_SPACE: *const u8 = unsafe { (usize::MAX as *const u8).offset(2) }; | --------------------------------------------------------------------------------------------- @@ -115,9 +115,9 @@ LL | unsafe { intrinsics::offset(self, count) } | | | overflowing in-bounds pointer arithmetic | inside `ptr::const_ptr::<impl *const u8>::offset` at $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL - | inside `UNDERFLOW_ADDRESS_SPACE` at $DIR/offset_ub.rs:14:57 + | inside `UNDERFLOW_ADDRESS_SPACE` at $DIR/offset_ub.rs:13:57 | - ::: $DIR/offset_ub.rs:14:1 + ::: $DIR/offset_ub.rs:13:1 | LL | pub const UNDERFLOW_ADDRESS_SPACE: *const u8 = unsafe { (1 as *const u8).offset(-2) }; | -------------------------------------------------------------------------------------- @@ -133,9 +133,9 @@ LL | unsafe { intrinsics::offset(self, count) } | | | inbounds test failed: pointer must be in-bounds at offset 1, but is outside bounds of allocN which has size 0 | inside `ptr::const_ptr::<impl *const u8>::offset` at $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL - | inside `ZERO_SIZED_ALLOC` at $DIR/offset_ub.rs:16:50 + | inside `ZERO_SIZED_ALLOC` at $DIR/offset_ub.rs:15:50 | - ::: $DIR/offset_ub.rs:16:1 + ::: $DIR/offset_ub.rs:15:1 | LL | pub const ZERO_SIZED_ALLOC: *const u8 = unsafe { [0u8; 0].as_ptr().offset(1) }; | ------------------------------------------------------------------------------- @@ -151,9 +151,9 @@ LL | unsafe { intrinsics::offset(self, count) as *mut T } | | | unable to turn bytes into a pointer | inside `ptr::mut_ptr::<impl *mut u8>::offset` at $SRC_DIR/core/src/ptr/mut_ptr.rs:LL:COL - | inside `DANGLING` at $DIR/offset_ub.rs:17:42 + | inside `DANGLING` at $DIR/offset_ub.rs:16:42 | - ::: $DIR/offset_ub.rs:17:1 + ::: $DIR/offset_ub.rs:16:1 | LL | pub const DANGLING: *const u8 = unsafe { ptr::NonNull::<u8>::dangling().as_ptr().offset(4) }; | --------------------------------------------------------------------------------------------- @@ -169,9 +169,9 @@ LL | unsafe { intrinsics::offset(self, count) } | | | inbounds test failed: 0x0 is not a valid pointer | inside `ptr::const_ptr::<impl *const u8>::offset` at $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL - | inside `NULL_OFFSET_ZERO` at $DIR/offset_ub.rs:20:50 + | inside `NULL_OFFSET_ZERO` at $DIR/offset_ub.rs:19:50 | - ::: $DIR/offset_ub.rs:20:1 + ::: $DIR/offset_ub.rs:19:1 | LL | pub const NULL_OFFSET_ZERO: *const u8 = unsafe { ptr::null::<u8>().offset(0) }; | ------------------------------------------------------------------------------- @@ -187,9 +187,9 @@ LL | unsafe { intrinsics::offset(self, count) } | | | unable to turn bytes into a pointer | inside `ptr::const_ptr::<impl *const u8>::offset` at $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL - | inside `UNDERFLOW_ABS` at $DIR/offset_ub.rs:23:47 + | inside `UNDERFLOW_ABS` at $DIR/offset_ub.rs:22:47 | - ::: $DIR/offset_ub.rs:23:1 + ::: $DIR/offset_ub.rs:22:1 | LL | pub const UNDERFLOW_ABS: *const u8 = unsafe { (usize::MAX as *const u8).offset(isize::MIN) }; | --------------------------------------------------------------------------------------------- diff --git a/src/test/ui/consts/std/alloc.32bit.stderr b/src/test/ui/consts/std/alloc.32bit.stderr new file mode 100644 index 0000000000000..bb9ccbba37692 --- /dev/null +++ b/src/test/ui/consts/std/alloc.32bit.stderr @@ -0,0 +1,14 @@ +error[E0080]: it is undefined behavior to use this value + --> $DIR/alloc.rs:8:1 + | +LL | const LAYOUT_INVALID: Layout = unsafe { Layout::from_size_align_unchecked(0x1000, 0x00) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered 0 at .align_, but expected something greater or equal to 1 + | + = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the raw bytes of the constant (size: 8, align: 4) { + 00 10 00 00 00 00 00 00 │ ........ + } + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0080`. diff --git a/src/test/ui/consts/std/alloc.64bit.stderr b/src/test/ui/consts/std/alloc.64bit.stderr new file mode 100644 index 0000000000000..2c891b1d79c13 --- /dev/null +++ b/src/test/ui/consts/std/alloc.64bit.stderr @@ -0,0 +1,14 @@ +error[E0080]: it is undefined behavior to use this value + --> $DIR/alloc.rs:8:1 + | +LL | const LAYOUT_INVALID: Layout = unsafe { Layout::from_size_align_unchecked(0x1000, 0x00) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered 0 at .align_, but expected something greater or equal to 1 + | + = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the raw bytes of the constant (size: 16, align: 8) { + 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 │ ................ + } + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0080`. diff --git a/src/test/ui/consts/std/alloc.rs b/src/test/ui/consts/std/alloc.rs index 65ac7e44926d0..14eadc4487f9e 100644 --- a/src/test/ui/consts/std/alloc.rs +++ b/src/test/ui/consts/std/alloc.rs @@ -1,3 +1,4 @@ +// stderr-per-bitwidth use std::alloc::Layout; // ok diff --git a/src/test/ui/consts/std/alloc.stderr b/src/test/ui/consts/std/alloc.stderr deleted file mode 100644 index 26b7a24ebfa6f..0000000000000 --- a/src/test/ui/consts/std/alloc.stderr +++ /dev/null @@ -1,11 +0,0 @@ -error[E0080]: it is undefined behavior to use this value - --> $DIR/alloc.rs:7:1 - | -LL | const LAYOUT_INVALID: Layout = unsafe { Layout::from_size_align_unchecked(0x1000, 0x00) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered 0 at .align_, but expected something greater or equal to 1 - | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. - -error: aborting due to previous error - -For more information about this error, try `rustc --explain E0080`. diff --git a/src/test/ui/consts/trait_specialization.rs b/src/test/ui/consts/trait_specialization.rs index 3adbbb5304634..c581ef6b0f7bb 100644 --- a/src/test/ui/consts/trait_specialization.rs +++ b/src/test/ui/consts/trait_specialization.rs @@ -1,5 +1,5 @@ // ignore-wasm32-bare which doesn't support `std::process:exit()` -// compile-flags: -Zmir-opt-level=2 +// compile-flags: -Zmir-opt-level=3 // run-pass // Tests that specialization does not cause optimizations running on polymorphic MIR to resolve diff --git a/src/test/ui/consts/validate_never_arrays.32bit.stderr b/src/test/ui/consts/validate_never_arrays.32bit.stderr new file mode 100644 index 0000000000000..2d1bba391e820 --- /dev/null +++ b/src/test/ui/consts/validate_never_arrays.32bit.stderr @@ -0,0 +1,36 @@ +error[E0080]: it is undefined behavior to use this value + --> $DIR/validate_never_arrays.rs:4:1 + | +LL | const _: &[!; 1] = unsafe { &*(1_usize as *const [!; 1]) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered a value of the never type `!` at .<deref>[0] + | + = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the raw bytes of the constant (size: 4, align: 4) { + 01 00 00 00 │ .... + } + +error[E0080]: it is undefined behavior to use this value + --> $DIR/validate_never_arrays.rs:7:1 + | +LL | const _: &[!] = unsafe { &*(1_usize as *const [!; 1]) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered a value of the never type `!` at .<deref>[0] + | + = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the raw bytes of the constant (size: 8, align: 4) { + 01 00 00 00 01 00 00 00 │ ........ + } + +error[E0080]: it is undefined behavior to use this value + --> $DIR/validate_never_arrays.rs:8:1 + | +LL | const _: &[!] = unsafe { &*(1_usize as *const [!; 42]) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered a value of the never type `!` at .<deref>[0] + | + = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the raw bytes of the constant (size: 8, align: 4) { + 01 00 00 00 2a 00 00 00 │ ....*... + } + +error: aborting due to 3 previous errors + +For more information about this error, try `rustc --explain E0080`. diff --git a/src/test/ui/consts/validate_never_arrays.64bit.stderr b/src/test/ui/consts/validate_never_arrays.64bit.stderr new file mode 100644 index 0000000000000..dd677f1b21edd --- /dev/null +++ b/src/test/ui/consts/validate_never_arrays.64bit.stderr @@ -0,0 +1,36 @@ +error[E0080]: it is undefined behavior to use this value + --> $DIR/validate_never_arrays.rs:4:1 + | +LL | const _: &[!; 1] = unsafe { &*(1_usize as *const [!; 1]) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered a value of the never type `!` at .<deref>[0] + | + = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the raw bytes of the constant (size: 8, align: 8) { + 01 00 00 00 00 00 00 00 │ ........ + } + +error[E0080]: it is undefined behavior to use this value + --> $DIR/validate_never_arrays.rs:7:1 + | +LL | const _: &[!] = unsafe { &*(1_usize as *const [!; 1]) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered a value of the never type `!` at .<deref>[0] + | + = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the raw bytes of the constant (size: 16, align: 8) { + 01 00 00 00 00 00 00 00 01 00 00 00 00 00 00 00 │ ................ + } + +error[E0080]: it is undefined behavior to use this value + --> $DIR/validate_never_arrays.rs:8:1 + | +LL | const _: &[!] = unsafe { &*(1_usize as *const [!; 42]) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered a value of the never type `!` at .<deref>[0] + | + = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the raw bytes of the constant (size: 16, align: 8) { + 01 00 00 00 00 00 00 00 2a 00 00 00 00 00 00 00 │ ........*....... + } + +error: aborting due to 3 previous errors + +For more information about this error, try `rustc --explain E0080`. diff --git a/src/test/ui/consts/validate_never_arrays.rs b/src/test/ui/consts/validate_never_arrays.rs index c7144f05ec7a4..1990fb073970e 100644 --- a/src/test/ui/consts/validate_never_arrays.rs +++ b/src/test/ui/consts/validate_never_arrays.rs @@ -1,3 +1,4 @@ +// stderr-per-bitwidth #![feature(const_raw_ptr_deref, never_type)] const _: &[!; 1] = unsafe { &*(1_usize as *const [!; 1]) }; //~ ERROR undefined behavior diff --git a/src/test/ui/consts/validate_never_arrays.stderr b/src/test/ui/consts/validate_never_arrays.stderr deleted file mode 100644 index 77f0a2ebd4021..0000000000000 --- a/src/test/ui/consts/validate_never_arrays.stderr +++ /dev/null @@ -1,27 +0,0 @@ -error[E0080]: it is undefined behavior to use this value - --> $DIR/validate_never_arrays.rs:3:1 - | -LL | const _: &[!; 1] = unsafe { &*(1_usize as *const [!; 1]) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered a value of the never type `!` at .<deref>[0] - | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. - -error[E0080]: it is undefined behavior to use this value - --> $DIR/validate_never_arrays.rs:6:1 - | -LL | const _: &[!] = unsafe { &*(1_usize as *const [!; 1]) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered a value of the never type `!` at .<deref>[0] - | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. - -error[E0080]: it is undefined behavior to use this value - --> $DIR/validate_never_arrays.rs:7:1 - | -LL | const _: &[!] = unsafe { &*(1_usize as *const [!; 42]) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered a value of the never type `!` at .<deref>[0] - | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. - -error: aborting due to 3 previous errors - -For more information about this error, try `rustc --explain E0080`. diff --git a/src/test/ui/copy-a-resource.stderr b/src/test/ui/copy-a-resource.stderr index 36cf57bd3c560..79095452f9d02 100644 --- a/src/test/ui/copy-a-resource.stderr +++ b/src/test/ui/copy-a-resource.stderr @@ -6,14 +6,6 @@ LL | struct Foo { ... LL | let _y = x.clone(); | ^^^^^ method not found in `Foo` - | - ::: $SRC_DIR/core/src/clone.rs:LL:COL - | -LL | fn clone(&self) -> Self; - | ----- - | | - | the method is available for `Arc<Foo>` here - | the method is available for `Rc<Foo>` here | = help: items from traits can only be used if the trait is implemented and in scope = note: the following trait defines an item `clone`, perhaps you need to implement it: diff --git a/src/test/ui/dep-graph/dep-graph-assoc-type-codegen.rs b/src/test/ui/dep-graph/dep-graph-assoc-type-codegen.rs index 0d11d933af04e..a0ee3ad31e697 100644 --- a/src/test/ui/dep-graph/dep-graph-assoc-type-codegen.rs +++ b/src/test/ui/dep-graph/dep-graph-assoc-type-codegen.rs @@ -1,7 +1,7 @@ // Test that when a trait impl changes, fns whose body uses that trait // must also be recompiled. -// compile-flags: -Z query-dep-graph +// compile-flags: -Z query-dep-graph -C incremental=tmp/dep-graph-assoc-type-codegen #![feature(rustc_attrs)] #![allow(warnings)] diff --git a/src/test/ui/dep-graph/dep-graph-caller-callee.rs b/src/test/ui/dep-graph/dep-graph-caller-callee.rs index b12c635d2e733..c95ea53650b47 100644 --- a/src/test/ui/dep-graph/dep-graph-caller-callee.rs +++ b/src/test/ui/dep-graph/dep-graph-caller-callee.rs @@ -1,7 +1,7 @@ // Test that immediate callers have to change when callee changes, but // not callers' callers. -// compile-flags: -Z query-dep-graph +// compile-flags: -Z query-dep-graph -C incremental=tmp/dep-graph-caller-callee #![feature(rustc_attrs)] #![allow(dead_code)] diff --git a/src/test/ui/dep-graph/dep-graph-check-attr.rs b/src/test/ui/dep-graph/dep-graph-check-attr.rs new file mode 100644 index 0000000000000..1026efc1b1dbe --- /dev/null +++ b/src/test/ui/dep-graph/dep-graph-check-attr.rs @@ -0,0 +1,20 @@ +// Test that using rustc_clean/dirty/if_this_changed/then_this_would_need +// are forbidden when `-Z query-dep-graph` is not enabled. + +#![feature(rustc_attrs)] +#![allow(dead_code)] +#![allow(unused_variables)] + +#[rustc_dirty(hir_owner)] //~ ERROR attribute requires -Z query-dep-graph +fn main() {} + +#[rustc_if_this_changed(hir_owner)] //~ ERROR attribute requires -Z query-dep-graph +struct Foo<T> { + f: T, +} + +#[rustc_clean(hir_owner)] //~ ERROR attribute requires -Z query-dep-graph +type TypeAlias<T> = Foo<T>; + +#[rustc_then_this_would_need(variances_of)] //~ ERROR attribute requires -Z query-dep-graph +trait Use<T> {} diff --git a/src/test/ui/dep-graph/dep-graph-check-attr.stderr b/src/test/ui/dep-graph/dep-graph-check-attr.stderr new file mode 100644 index 0000000000000..945a4237c1298 --- /dev/null +++ b/src/test/ui/dep-graph/dep-graph-check-attr.stderr @@ -0,0 +1,26 @@ +error: attribute requires -Z query-dep-graph to be enabled + --> $DIR/dep-graph-check-attr.rs:8:1 + | +LL | #[rustc_dirty(hir_owner)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: attribute requires -Z query-dep-graph to be enabled + --> $DIR/dep-graph-check-attr.rs:11:1 + | +LL | #[rustc_if_this_changed(hir_owner)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: attribute requires -Z query-dep-graph to be enabled + --> $DIR/dep-graph-check-attr.rs:16:1 + | +LL | #[rustc_clean(hir_owner)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: attribute requires -Z query-dep-graph to be enabled + --> $DIR/dep-graph-check-attr.rs:19:1 + | +LL | #[rustc_then_this_would_need(variances_of)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 4 previous errors + diff --git a/src/test/ui/dep-graph/dep-graph-struct-signature.rs b/src/test/ui/dep-graph/dep-graph-struct-signature.rs index 7ef6fac48c3a6..50a670b877238 100644 --- a/src/test/ui/dep-graph/dep-graph-struct-signature.rs +++ b/src/test/ui/dep-graph/dep-graph-struct-signature.rs @@ -1,7 +1,7 @@ // Test cases where a changing struct appears in the signature of fns // and methods. -// compile-flags: -Z query-dep-graph +// compile-flags: -Z query-dep-graph -C incremental=tmp/dep-graph-struct-signature #![feature(rustc_attrs)] #![allow(dead_code)] diff --git a/src/test/ui/dep-graph/dep-graph-trait-impl-two-traits-same-method.rs b/src/test/ui/dep-graph/dep-graph-trait-impl-two-traits-same-method.rs index 1b3bf5a3933fe..c0a6617316b8d 100644 --- a/src/test/ui/dep-graph/dep-graph-trait-impl-two-traits-same-method.rs +++ b/src/test/ui/dep-graph/dep-graph-trait-impl-two-traits-same-method.rs @@ -1,7 +1,7 @@ // Test that adding an impl to a trait `Foo` DOES affect functions // that only use `Bar` if they have methods in common. -// compile-flags: -Z query-dep-graph +// compile-flags: -Z query-dep-graph -C incremental=tmp/dep-graph-trait-impl-two-traits-same-method #![feature(rustc_attrs)] #![allow(dead_code)] diff --git a/src/test/ui/dep-graph/dep-graph-trait-impl-two-traits.rs b/src/test/ui/dep-graph/dep-graph-trait-impl-two-traits.rs index ebfe8ccc3dfaf..56e9762ddb26c 100644 --- a/src/test/ui/dep-graph/dep-graph-trait-impl-two-traits.rs +++ b/src/test/ui/dep-graph/dep-graph-trait-impl-two-traits.rs @@ -1,7 +1,7 @@ // Test that adding an impl to a trait `Foo` does not affect functions // that only use `Bar`, so long as they do not have methods in common. -// compile-flags: -Z query-dep-graph +// compile-flags: -Z query-dep-graph -C incremental=tmp/dep-graph-trait-impl-two-traits #![feature(rustc_attrs)] #![allow(warnings)] diff --git a/src/test/ui/dep-graph/dep-graph-trait-impl.rs b/src/test/ui/dep-graph/dep-graph-trait-impl.rs index 9dd201e2a1fbc..3bbe3e745ca69 100644 --- a/src/test/ui/dep-graph/dep-graph-trait-impl.rs +++ b/src/test/ui/dep-graph/dep-graph-trait-impl.rs @@ -1,7 +1,7 @@ // Test that when a trait impl changes, fns whose body uses that trait // must also be recompiled. -// compile-flags: -Z query-dep-graph +// compile-flags: -Z query-dep-graph -C incremental=tmp/dep-graph-trait-impl #![feature(rustc_attrs)] #![allow(warnings)] diff --git a/src/test/ui/dep-graph/dep-graph-type-alias.rs b/src/test/ui/dep-graph/dep-graph-type-alias.rs index c9151ce79c5f6..5c5e24693a4f5 100644 --- a/src/test/ui/dep-graph/dep-graph-type-alias.rs +++ b/src/test/ui/dep-graph/dep-graph-type-alias.rs @@ -1,6 +1,6 @@ // Test that changing what a `type` points to does not go unnoticed. -// compile-flags: -Z query-dep-graph +// compile-flags: -Z query-dep-graph -C incremental=tmp/dep-graph-type-alias #![feature(rustc_attrs)] #![allow(dead_code)] diff --git a/src/test/ui/dep-graph/dep-graph-variance-alias.rs b/src/test/ui/dep-graph/dep-graph-variance-alias.rs index 927ea5597783a..6cc1f44104a09 100644 --- a/src/test/ui/dep-graph/dep-graph-variance-alias.rs +++ b/src/test/ui/dep-graph/dep-graph-variance-alias.rs @@ -1,7 +1,7 @@ // Test that changing what a `type` points to does not go unnoticed // by the variance analysis. -// compile-flags: -Z query-dep-graph +// compile-flags: -Z query-dep-graph -C incremental=tmp/dep-graph-variance-alias #![feature(rustc_attrs)] #![allow(dead_code)] diff --git a/src/test/ui/deprecation/deprecation-lint.rs b/src/test/ui/deprecation/deprecation-lint.rs index 560e29688864a..b6c791c15fd20 100644 --- a/src/test/ui/deprecation/deprecation-lint.rs +++ b/src/test/ui/deprecation/deprecation-lint.rs @@ -1,5 +1,4 @@ // aux-build:deprecation-lint.rs -// ignore-tidy-linelength #![deny(deprecated)] #![allow(warnings)] diff --git a/src/test/ui/deprecation/deprecation-lint.stderr b/src/test/ui/deprecation/deprecation-lint.stderr index 12c76f0f4a5d7..959cf93bac053 100644 --- a/src/test/ui/deprecation/deprecation-lint.stderr +++ b/src/test/ui/deprecation/deprecation-lint.stderr @@ -1,737 +1,737 @@ error: use of deprecated function `deprecation_lint::deprecated`: text - --> $DIR/deprecation-lint.rs:17:9 + --> $DIR/deprecation-lint.rs:16:9 | LL | deprecated(); | ^^^^^^^^^^ | note: the lint level is defined here - --> $DIR/deprecation-lint.rs:4:9 + --> $DIR/deprecation-lint.rs:3:9 | LL | #![deny(deprecated)] | ^^^^^^^^^^ error: use of deprecated associated function `deprecation_lint::Trait::trait_deprecated`: text - --> $DIR/deprecation-lint.rs:22:9 + --> $DIR/deprecation-lint.rs:21:9 | LL | Trait::trait_deprecated(&foo); | ^^^^^^^^^^^^^^^^^^^^^^^ error: use of deprecated associated function `deprecation_lint::Trait::trait_deprecated`: text - --> $DIR/deprecation-lint.rs:24:9 + --> $DIR/deprecation-lint.rs:23:9 | LL | <Foo as Trait>::trait_deprecated(&foo); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: use of deprecated function `deprecation_lint::deprecated_text`: text - --> $DIR/deprecation-lint.rs:26:9 + --> $DIR/deprecation-lint.rs:25:9 | LL | deprecated_text(); | ^^^^^^^^^^^^^^^ error: use of deprecated associated function `deprecation_lint::Trait::trait_deprecated_text`: text - --> $DIR/deprecation-lint.rs:31:9 + --> $DIR/deprecation-lint.rs:30:9 | LL | ... Trait::trait_deprecated_text(&foo); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: use of deprecated associated function `deprecation_lint::Trait::trait_deprecated_text`: text - --> $DIR/deprecation-lint.rs:33:9 + --> $DIR/deprecation-lint.rs:32:9 | LL | ... <Foo as Trait>::trait_deprecated_text(&foo); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: use of deprecated struct `deprecation_lint::DeprecatedStruct`: text - --> $DIR/deprecation-lint.rs:35:17 + --> $DIR/deprecation-lint.rs:34:17 | LL | let _ = DeprecatedStruct { | ^^^^^^^^^^^^^^^^ error: use of deprecated struct `deprecation_lint::DeprecatedUnitStruct`: text - --> $DIR/deprecation-lint.rs:39:17 + --> $DIR/deprecation-lint.rs:38:17 | LL | let _ = DeprecatedUnitStruct; | ^^^^^^^^^^^^^^^^^^^^ error: use of deprecated variant `deprecation_lint::Enum::DeprecatedVariant`: text - --> $DIR/deprecation-lint.rs:41:17 + --> $DIR/deprecation-lint.rs:40:17 | LL | let _ = Enum::DeprecatedVariant; | ^^^^^^^^^^^^^^^^^^^^^^^ error: use of deprecated struct `deprecation_lint::DeprecatedTupleStruct`: text - --> $DIR/deprecation-lint.rs:43:17 + --> $DIR/deprecation-lint.rs:42:17 | LL | let _ = DeprecatedTupleStruct (1); | ^^^^^^^^^^^^^^^^^^^^^ error: use of deprecated struct `deprecation_lint::nested::DeprecatedStruct`: text - --> $DIR/deprecation-lint.rs:45:17 + --> $DIR/deprecation-lint.rs:44:17 | LL | let _ = nested::DeprecatedStruct { | ^^^^^^^^^^^^^^^^^^^^^^^^ error: use of deprecated struct `deprecation_lint::nested::DeprecatedUnitStruct`: text - --> $DIR/deprecation-lint.rs:49:17 + --> $DIR/deprecation-lint.rs:48:17 | LL | let _ = nested::DeprecatedUnitStruct; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: use of deprecated variant `deprecation_lint::nested::Enum::DeprecatedVariant`: text - --> $DIR/deprecation-lint.rs:51:17 + --> $DIR/deprecation-lint.rs:50:17 | LL | ... let _ = nested::Enum::DeprecatedVariant; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: use of deprecated struct `deprecation_lint::nested::DeprecatedTupleStruct`: text - --> $DIR/deprecation-lint.rs:53:17 + --> $DIR/deprecation-lint.rs:52:17 | LL | ... let _ = nested::DeprecatedTupleStruct (1); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: use of deprecated function `deprecation_lint::deprecated_text`: text - --> $DIR/deprecation-lint.rs:60:25 + --> $DIR/deprecation-lint.rs:59:25 | LL | macro_test_arg!(deprecated_text()); | ^^^^^^^^^^^^^^^ error: use of deprecated function `deprecation_lint::deprecated_text`: text - --> $DIR/deprecation-lint.rs:61:41 + --> $DIR/deprecation-lint.rs:60:41 | LL | macro_test_arg!(macro_test_arg!(deprecated_text())); | ^^^^^^^^^^^^^^^ error: use of deprecated associated function `deprecation_lint::Trait::trait_deprecated`: text - --> $DIR/deprecation-lint.rs:66:9 + --> $DIR/deprecation-lint.rs:65:9 | LL | Trait::trait_deprecated(&foo); | ^^^^^^^^^^^^^^^^^^^^^^^ error: use of deprecated associated function `deprecation_lint::Trait::trait_deprecated`: text - --> $DIR/deprecation-lint.rs:68:9 + --> $DIR/deprecation-lint.rs:67:9 | LL | <Foo as Trait>::trait_deprecated(&foo); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: use of deprecated associated function `deprecation_lint::Trait::trait_deprecated_text`: text - --> $DIR/deprecation-lint.rs:70:9 + --> $DIR/deprecation-lint.rs:69:9 | LL | ... Trait::trait_deprecated_text(&foo); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: use of deprecated associated function `deprecation_lint::Trait::trait_deprecated_text`: text - --> $DIR/deprecation-lint.rs:72:9 + --> $DIR/deprecation-lint.rs:71:9 | LL | ... <Foo as Trait>::trait_deprecated_text(&foo); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: use of deprecated trait `deprecation_lint::DeprecatedTrait`: text - --> $DIR/deprecation-lint.rs:82:10 + --> $DIR/deprecation-lint.rs:81:10 | LL | impl DeprecatedTrait for S {} | ^^^^^^^^^^^^^^^ error: use of deprecated trait `deprecation_lint::DeprecatedTrait`: text - --> $DIR/deprecation-lint.rs:83:24 + --> $DIR/deprecation-lint.rs:82:24 | LL | trait LocalTrait : DeprecatedTrait { } | ^^^^^^^^^^^^^^^ error: use of deprecated struct `deprecation_lint::Deprecated`: text - --> $DIR/deprecation-lint.rs:114:17 + --> $DIR/deprecation-lint.rs:113:17 | LL | let x = Deprecated { | ^^^^^^^^^^ error: use of deprecated struct `deprecation_lint::Deprecated`: text - --> $DIR/deprecation-lint.rs:123:13 + --> $DIR/deprecation-lint.rs:122:13 | LL | let Deprecated { | ^^^^^^^^^^ error: use of deprecated struct `deprecation_lint::Deprecated`: text - --> $DIR/deprecation-lint.rs:129:13 + --> $DIR/deprecation-lint.rs:128:13 | LL | let Deprecated | ^^^^^^^^^^ error: use of deprecated struct `deprecation_lint::Deprecated2`: text - --> $DIR/deprecation-lint.rs:133:17 + --> $DIR/deprecation-lint.rs:132:17 | LL | let x = Deprecated2(1, 2, 3); | ^^^^^^^^^^^ error: use of deprecated struct `deprecation_lint::Deprecated2`: text - --> $DIR/deprecation-lint.rs:143:13 + --> $DIR/deprecation-lint.rs:142:13 | LL | let Deprecated2 | ^^^^^^^^^^^ error: use of deprecated struct `deprecation_lint::Deprecated2`: text - --> $DIR/deprecation-lint.rs:152:13 + --> $DIR/deprecation-lint.rs:151:13 | LL | let Deprecated2 | ^^^^^^^^^^^ error: use of deprecated function `deprecation_lint::deprecated_mod::deprecated`: text - --> $DIR/deprecation-lint.rs:163:9 + --> $DIR/deprecation-lint.rs:162:9 | LL | deprecated_mod::deprecated(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ error: use of deprecated function `this_crate::deprecated`: text - --> $DIR/deprecation-lint.rs:246:9 + --> $DIR/deprecation-lint.rs:245:9 | LL | deprecated(); | ^^^^^^^^^^ error: use of deprecated associated function `this_crate::Trait::trait_deprecated`: text - --> $DIR/deprecation-lint.rs:251:9 + --> $DIR/deprecation-lint.rs:250:9 | LL | Trait::trait_deprecated(&foo); | ^^^^^^^^^^^^^^^^^^^^^^^ error: use of deprecated associated function `this_crate::Trait::trait_deprecated`: text - --> $DIR/deprecation-lint.rs:253:9 + --> $DIR/deprecation-lint.rs:252:9 | LL | <Foo as Trait>::trait_deprecated(&foo); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: use of deprecated function `this_crate::deprecated_text`: text - --> $DIR/deprecation-lint.rs:255:9 + --> $DIR/deprecation-lint.rs:254:9 | LL | deprecated_text(); | ^^^^^^^^^^^^^^^ error: use of deprecated associated function `this_crate::Trait::trait_deprecated_text`: text - --> $DIR/deprecation-lint.rs:260:9 + --> $DIR/deprecation-lint.rs:259:9 | LL | Trait::trait_deprecated_text(&foo); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: use of deprecated associated function `this_crate::Trait::trait_deprecated_text`: text - --> $DIR/deprecation-lint.rs:262:9 + --> $DIR/deprecation-lint.rs:261:9 | LL | ... <Foo as Trait>::trait_deprecated_text(&foo); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: use of deprecated function `this_crate::deprecated_future`: text - --> $DIR/deprecation-lint.rs:265:9 + --> $DIR/deprecation-lint.rs:264:9 | LL | deprecated_future(); | ^^^^^^^^^^^^^^^^^ error: use of deprecated function `this_crate::deprecated_future_text`: text - --> $DIR/deprecation-lint.rs:266:9 + --> $DIR/deprecation-lint.rs:265:9 | LL | deprecated_future_text(); | ^^^^^^^^^^^^^^^^^^^^^^ error: use of deprecated struct `this_crate::DeprecatedStruct`: text - --> $DIR/deprecation-lint.rs:268:17 + --> $DIR/deprecation-lint.rs:267:17 | LL | let _ = DeprecatedStruct { | ^^^^^^^^^^^^^^^^ error: use of deprecated unit struct `this_crate::DeprecatedUnitStruct`: text - --> $DIR/deprecation-lint.rs:273:17 + --> $DIR/deprecation-lint.rs:272:17 | LL | let _ = DeprecatedUnitStruct; | ^^^^^^^^^^^^^^^^^^^^ error: use of deprecated unit variant `this_crate::Enum::DeprecatedVariant`: text - --> $DIR/deprecation-lint.rs:275:17 + --> $DIR/deprecation-lint.rs:274:17 | LL | let _ = Enum::DeprecatedVariant; | ^^^^^^^^^^^^^^^^^^^^^^^ error: use of deprecated tuple struct `this_crate::DeprecatedTupleStruct`: text - --> $DIR/deprecation-lint.rs:277:17 + --> $DIR/deprecation-lint.rs:276:17 | LL | let _ = DeprecatedTupleStruct (1); | ^^^^^^^^^^^^^^^^^^^^^ error: use of deprecated struct `this_crate::nested::DeprecatedStruct`: text - --> $DIR/deprecation-lint.rs:279:17 + --> $DIR/deprecation-lint.rs:278:17 | LL | let _ = nested::DeprecatedStruct { | ^^^^^^^^^^^^^^^^^^^^^^^^ error: use of deprecated unit struct `this_crate::nested::DeprecatedUnitStruct`: text - --> $DIR/deprecation-lint.rs:284:17 + --> $DIR/deprecation-lint.rs:283:17 | LL | let _ = nested::DeprecatedUnitStruct; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: use of deprecated unit variant `this_crate::nested::Enum::DeprecatedVariant`: text - --> $DIR/deprecation-lint.rs:286:17 + --> $DIR/deprecation-lint.rs:285:17 | LL | ... let _ = nested::Enum::DeprecatedVariant; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: use of deprecated tuple struct `this_crate::nested::DeprecatedTupleStruct`: text - --> $DIR/deprecation-lint.rs:288:17 + --> $DIR/deprecation-lint.rs:287:17 | LL | ... let _ = nested::DeprecatedTupleStruct (1); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: use of deprecated associated function `this_crate::Trait::trait_deprecated`: text - --> $DIR/deprecation-lint.rs:293:9 + --> $DIR/deprecation-lint.rs:292:9 | LL | Trait::trait_deprecated(&foo); | ^^^^^^^^^^^^^^^^^^^^^^^ error: use of deprecated associated function `this_crate::Trait::trait_deprecated`: text - --> $DIR/deprecation-lint.rs:295:9 + --> $DIR/deprecation-lint.rs:294:9 | LL | <Foo as Trait>::trait_deprecated(&foo); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: use of deprecated associated function `this_crate::Trait::trait_deprecated_text`: text - --> $DIR/deprecation-lint.rs:297:9 + --> $DIR/deprecation-lint.rs:296:9 | LL | Trait::trait_deprecated_text(&foo); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: use of deprecated associated function `this_crate::Trait::trait_deprecated_text`: text - --> $DIR/deprecation-lint.rs:299:9 + --> $DIR/deprecation-lint.rs:298:9 | LL | ... <Foo as Trait>::trait_deprecated_text(&foo); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: use of deprecated function `this_crate::test_fn_closure_body::{closure#0}::bar` - --> $DIR/deprecation-lint.rs:317:13 + --> $DIR/deprecation-lint.rs:316:13 | LL | bar(); | ^^^ error: use of deprecated trait `this_crate::DeprecatedTrait`: text - --> $DIR/deprecation-lint.rs:336:10 + --> $DIR/deprecation-lint.rs:335:10 | LL | impl DeprecatedTrait for S { } | ^^^^^^^^^^^^^^^ error: use of deprecated trait `this_crate::DeprecatedTrait`: text - --> $DIR/deprecation-lint.rs:338:24 + --> $DIR/deprecation-lint.rs:337:24 | LL | trait LocalTrait : DeprecatedTrait { } | ^^^^^^^^^^^^^^^ error: use of deprecated struct `this_crate2::Deprecated`: text - --> $DIR/deprecation-lint.rs:390:17 + --> $DIR/deprecation-lint.rs:389:17 | LL | let x = Deprecated { | ^^^^^^^^^^ error: use of deprecated struct `this_crate2::Deprecated`: text - --> $DIR/deprecation-lint.rs:399:13 + --> $DIR/deprecation-lint.rs:398:13 | LL | let Deprecated { | ^^^^^^^^^^ error: use of deprecated struct `this_crate2::Deprecated`: text - --> $DIR/deprecation-lint.rs:405:13 + --> $DIR/deprecation-lint.rs:404:13 | LL | let Deprecated | ^^^^^^^^^^ error: use of deprecated tuple struct `this_crate2::Deprecated2`: text - --> $DIR/deprecation-lint.rs:410:17 + --> $DIR/deprecation-lint.rs:409:17 | LL | let x = Deprecated2(1, 2, 3); | ^^^^^^^^^^^ error: use of deprecated tuple struct `this_crate2::Deprecated2`: text - --> $DIR/deprecation-lint.rs:420:13 + --> $DIR/deprecation-lint.rs:419:13 | LL | let Deprecated2 | ^^^^^^^^^^^ error: use of deprecated tuple struct `this_crate2::Deprecated2`: text - --> $DIR/deprecation-lint.rs:429:13 + --> $DIR/deprecation-lint.rs:428:13 | LL | let Deprecated2 | ^^^^^^^^^^^ error: use of deprecated associated function `deprecation_lint::MethodTester::method_deprecated`: text - --> $DIR/deprecation-lint.rs:18:13 + --> $DIR/deprecation-lint.rs:17:13 | LL | foo.method_deprecated(); | ^^^^^^^^^^^^^^^^^ error: use of deprecated associated function `deprecation_lint::MethodTester::method_deprecated`: text - --> $DIR/deprecation-lint.rs:19:9 + --> $DIR/deprecation-lint.rs:18:9 | LL | Foo::method_deprecated(&foo); | ^^^^^^^^^^^^^^^^^^^^^^ error: use of deprecated associated function `deprecation_lint::MethodTester::method_deprecated`: text - --> $DIR/deprecation-lint.rs:20:9 + --> $DIR/deprecation-lint.rs:19:9 | LL | <Foo>::method_deprecated(&foo); | ^^^^^^^^^^^^^^^^^^^^^^^^ error: use of deprecated associated function `deprecation_lint::Trait::trait_deprecated`: text - --> $DIR/deprecation-lint.rs:21:13 + --> $DIR/deprecation-lint.rs:20:13 | LL | foo.trait_deprecated(); | ^^^^^^^^^^^^^^^^ error: use of deprecated associated function `deprecation_lint::Trait::trait_deprecated`: text - --> $DIR/deprecation-lint.rs:23:9 + --> $DIR/deprecation-lint.rs:22:9 | LL | <Foo>::trait_deprecated(&foo); | ^^^^^^^^^^^^^^^^^^^^^^^ error: use of deprecated associated function `deprecation_lint::MethodTester::method_deprecated_text`: text - --> $DIR/deprecation-lint.rs:27:13 + --> $DIR/deprecation-lint.rs:26:13 | LL | ... foo.method_deprecated_text(); | ^^^^^^^^^^^^^^^^^^^^^^ error: use of deprecated associated function `deprecation_lint::MethodTester::method_deprecated_text`: text - --> $DIR/deprecation-lint.rs:28:9 + --> $DIR/deprecation-lint.rs:27:9 | LL | ... Foo::method_deprecated_text(&foo); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: use of deprecated associated function `deprecation_lint::MethodTester::method_deprecated_text`: text - --> $DIR/deprecation-lint.rs:29:9 + --> $DIR/deprecation-lint.rs:28:9 | LL | ... <Foo>::method_deprecated_text(&foo); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: use of deprecated associated function `deprecation_lint::Trait::trait_deprecated_text`: text - --> $DIR/deprecation-lint.rs:30:13 + --> $DIR/deprecation-lint.rs:29:13 | LL | foo.trait_deprecated_text(); | ^^^^^^^^^^^^^^^^^^^^^ error: use of deprecated associated function `deprecation_lint::Trait::trait_deprecated_text`: text - --> $DIR/deprecation-lint.rs:32:9 + --> $DIR/deprecation-lint.rs:31:9 | LL | ... <Foo>::trait_deprecated_text(&foo); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: use of deprecated field `deprecation_lint::DeprecatedStruct::i`: text - --> $DIR/deprecation-lint.rs:36:13 + --> $DIR/deprecation-lint.rs:35:13 | LL | i: 0 | ^^^^ error: use of deprecated field `deprecation_lint::nested::DeprecatedStruct::i`: text - --> $DIR/deprecation-lint.rs:46:13 + --> $DIR/deprecation-lint.rs:45:13 | LL | i: 0 | ^^^^ error: use of deprecated associated function `deprecation_lint::Trait::trait_deprecated`: text - --> $DIR/deprecation-lint.rs:65:13 + --> $DIR/deprecation-lint.rs:64:13 | LL | foo.trait_deprecated(); | ^^^^^^^^^^^^^^^^ error: use of deprecated associated function `deprecation_lint::Trait::trait_deprecated`: text - --> $DIR/deprecation-lint.rs:67:9 + --> $DIR/deprecation-lint.rs:66:9 | LL | <Foo>::trait_deprecated(&foo); | ^^^^^^^^^^^^^^^^^^^^^^^ error: use of deprecated associated function `deprecation_lint::Trait::trait_deprecated_text`: text - --> $DIR/deprecation-lint.rs:69:13 + --> $DIR/deprecation-lint.rs:68:13 | LL | foo.trait_deprecated_text(); | ^^^^^^^^^^^^^^^^^^^^^ error: use of deprecated associated function `deprecation_lint::Trait::trait_deprecated_text`: text - --> $DIR/deprecation-lint.rs:71:9 + --> $DIR/deprecation-lint.rs:70:9 | LL | ... <Foo>::trait_deprecated_text(&foo); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: use of deprecated associated function `deprecation_lint::Trait::trait_deprecated`: text - --> $DIR/deprecation-lint.rs:76:13 + --> $DIR/deprecation-lint.rs:75:13 | LL | foo.trait_deprecated(); | ^^^^^^^^^^^^^^^^ error: use of deprecated associated function `deprecation_lint::Trait::trait_deprecated_text`: text - --> $DIR/deprecation-lint.rs:77:13 + --> $DIR/deprecation-lint.rs:76:13 | LL | foo.trait_deprecated_text(); | ^^^^^^^^^^^^^^^^^^^^^ error: use of deprecated field `deprecation_lint::Stable::override2`: text - --> $DIR/deprecation-lint.rs:87:13 + --> $DIR/deprecation-lint.rs:86:13 | LL | override2: 3, | ^^^^^^^^^^^^ error: use of deprecated field `deprecation_lint::Stable::override2`: text - --> $DIR/deprecation-lint.rs:91:17 + --> $DIR/deprecation-lint.rs:90:17 | LL | let _ = x.override2; | ^^^^^^^^^^^ error: use of deprecated field `deprecation_lint::Stable::override2`: text - --> $DIR/deprecation-lint.rs:95:13 + --> $DIR/deprecation-lint.rs:94:13 | LL | override2: _ | ^^^^^^^^^^^^ error: use of deprecated field `deprecation_lint::Stable2::2`: text - --> $DIR/deprecation-lint.rs:103:17 + --> $DIR/deprecation-lint.rs:102:17 | LL | let _ = x.2; | ^^^ error: use of deprecated field `deprecation_lint::Stable2::2`: text - --> $DIR/deprecation-lint.rs:108:20 + --> $DIR/deprecation-lint.rs:107:20 | LL | _) | ^ error: use of deprecated field `deprecation_lint::Deprecated::inherit`: text - --> $DIR/deprecation-lint.rs:116:13 + --> $DIR/deprecation-lint.rs:115:13 | LL | inherit: 1, | ^^^^^^^^^^ error: use of deprecated field `deprecation_lint::Deprecated::inherit`: text - --> $DIR/deprecation-lint.rs:120:17 + --> $DIR/deprecation-lint.rs:119:17 | LL | let _ = x.inherit; | ^^^^^^^^^ error: use of deprecated field `deprecation_lint::Deprecated::inherit`: text - --> $DIR/deprecation-lint.rs:125:13 + --> $DIR/deprecation-lint.rs:124:13 | LL | inherit: _, | ^^^^^^^^^^ error: use of deprecated field `deprecation_lint::Deprecated2::0`: text - --> $DIR/deprecation-lint.rs:136:17 + --> $DIR/deprecation-lint.rs:135:17 | LL | let _ = x.0; | ^^^ error: use of deprecated field `deprecation_lint::Deprecated2::1`: text - --> $DIR/deprecation-lint.rs:138:17 + --> $DIR/deprecation-lint.rs:137:17 | LL | let _ = x.1; | ^^^ error: use of deprecated field `deprecation_lint::Deprecated2::2`: text - --> $DIR/deprecation-lint.rs:140:17 + --> $DIR/deprecation-lint.rs:139:17 | LL | let _ = x.2; | ^^^ error: use of deprecated field `deprecation_lint::Deprecated2::0`: text - --> $DIR/deprecation-lint.rs:145:14 + --> $DIR/deprecation-lint.rs:144:14 | LL | (_, | ^ error: use of deprecated field `deprecation_lint::Deprecated2::1`: text - --> $DIR/deprecation-lint.rs:147:14 + --> $DIR/deprecation-lint.rs:146:14 | LL | _, | ^ error: use of deprecated field `deprecation_lint::Deprecated2::2`: text - --> $DIR/deprecation-lint.rs:149:14 + --> $DIR/deprecation-lint.rs:148:14 | LL | _) | ^ error: use of deprecated associated function `this_crate::MethodTester::method_deprecated`: text - --> $DIR/deprecation-lint.rs:247:13 + --> $DIR/deprecation-lint.rs:246:13 | LL | foo.method_deprecated(); | ^^^^^^^^^^^^^^^^^ error: use of deprecated associated function `this_crate::MethodTester::method_deprecated`: text - --> $DIR/deprecation-lint.rs:248:9 + --> $DIR/deprecation-lint.rs:247:9 | LL | Foo::method_deprecated(&foo); | ^^^^^^^^^^^^^^^^^^^^^^ error: use of deprecated associated function `this_crate::MethodTester::method_deprecated`: text - --> $DIR/deprecation-lint.rs:249:9 + --> $DIR/deprecation-lint.rs:248:9 | LL | <Foo>::method_deprecated(&foo); | ^^^^^^^^^^^^^^^^^^^^^^^^ error: use of deprecated associated function `this_crate::Trait::trait_deprecated`: text - --> $DIR/deprecation-lint.rs:250:13 + --> $DIR/deprecation-lint.rs:249:13 | LL | foo.trait_deprecated(); | ^^^^^^^^^^^^^^^^ error: use of deprecated associated function `this_crate::Trait::trait_deprecated`: text - --> $DIR/deprecation-lint.rs:252:9 + --> $DIR/deprecation-lint.rs:251:9 | LL | <Foo>::trait_deprecated(&foo); | ^^^^^^^^^^^^^^^^^^^^^^^ error: use of deprecated associated function `this_crate::MethodTester::method_deprecated_text`: text - --> $DIR/deprecation-lint.rs:256:13 + --> $DIR/deprecation-lint.rs:255:13 | LL | ... foo.method_deprecated_text(); | ^^^^^^^^^^^^^^^^^^^^^^ error: use of deprecated associated function `this_crate::MethodTester::method_deprecated_text`: text - --> $DIR/deprecation-lint.rs:257:9 + --> $DIR/deprecation-lint.rs:256:9 | LL | ... Foo::method_deprecated_text(&foo); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: use of deprecated associated function `this_crate::MethodTester::method_deprecated_text`: text - --> $DIR/deprecation-lint.rs:258:9 + --> $DIR/deprecation-lint.rs:257:9 | LL | ... <Foo>::method_deprecated_text(&foo); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: use of deprecated associated function `this_crate::Trait::trait_deprecated_text`: text - --> $DIR/deprecation-lint.rs:259:13 + --> $DIR/deprecation-lint.rs:258:13 | LL | foo.trait_deprecated_text(); | ^^^^^^^^^^^^^^^^^^^^^ error: use of deprecated associated function `this_crate::Trait::trait_deprecated_text`: text - --> $DIR/deprecation-lint.rs:261:9 + --> $DIR/deprecation-lint.rs:260:9 | LL | <Foo>::trait_deprecated_text(&foo); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: use of deprecated field `this_crate::DeprecatedStruct::i`: text - --> $DIR/deprecation-lint.rs:270:13 + --> $DIR/deprecation-lint.rs:269:13 | LL | i: 0 | ^^^^ error: use of deprecated field `this_crate::nested::DeprecatedStruct::i`: text - --> $DIR/deprecation-lint.rs:281:13 + --> $DIR/deprecation-lint.rs:280:13 | LL | i: 0 | ^^^^ error: use of deprecated associated function `this_crate::Trait::trait_deprecated`: text - --> $DIR/deprecation-lint.rs:292:13 + --> $DIR/deprecation-lint.rs:291:13 | LL | foo.trait_deprecated(); | ^^^^^^^^^^^^^^^^ error: use of deprecated associated function `this_crate::Trait::trait_deprecated`: text - --> $DIR/deprecation-lint.rs:294:9 + --> $DIR/deprecation-lint.rs:293:9 | LL | <Foo>::trait_deprecated(&foo); | ^^^^^^^^^^^^^^^^^^^^^^^ error: use of deprecated associated function `this_crate::Trait::trait_deprecated_text`: text - --> $DIR/deprecation-lint.rs:296:13 + --> $DIR/deprecation-lint.rs:295:13 | LL | foo.trait_deprecated_text(); | ^^^^^^^^^^^^^^^^^^^^^ error: use of deprecated associated function `this_crate::Trait::trait_deprecated_text`: text - --> $DIR/deprecation-lint.rs:298:9 + --> $DIR/deprecation-lint.rs:297:9 | LL | <Foo>::trait_deprecated_text(&foo); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: use of deprecated associated function `this_crate::Trait::trait_deprecated`: text - --> $DIR/deprecation-lint.rs:303:13 + --> $DIR/deprecation-lint.rs:302:13 | LL | foo.trait_deprecated(); | ^^^^^^^^^^^^^^^^ error: use of deprecated associated function `this_crate::Trait::trait_deprecated_text`: text - --> $DIR/deprecation-lint.rs:304:13 + --> $DIR/deprecation-lint.rs:303:13 | LL | foo.trait_deprecated_text(); | ^^^^^^^^^^^^^^^^^^^^^ error: use of deprecated field `this_crate2::Stable::override2`: text - --> $DIR/deprecation-lint.rs:363:13 + --> $DIR/deprecation-lint.rs:362:13 | LL | override2: 3, | ^^^^^^^^^^^^ error: use of deprecated field `this_crate2::Stable::override2`: text - --> $DIR/deprecation-lint.rs:367:17 + --> $DIR/deprecation-lint.rs:366:17 | LL | let _ = x.override2; | ^^^^^^^^^^^ error: use of deprecated field `this_crate2::Stable::override2`: text - --> $DIR/deprecation-lint.rs:371:13 + --> $DIR/deprecation-lint.rs:370:13 | LL | override2: _ | ^^^^^^^^^^^^ error: use of deprecated field `this_crate2::Stable2::2`: text - --> $DIR/deprecation-lint.rs:379:17 + --> $DIR/deprecation-lint.rs:378:17 | LL | let _ = x.2; | ^^^ error: use of deprecated field `this_crate2::Stable2::2`: text - --> $DIR/deprecation-lint.rs:384:20 + --> $DIR/deprecation-lint.rs:383:20 | LL | _) | ^ error: use of deprecated field `this_crate2::Deprecated::inherit`: text - --> $DIR/deprecation-lint.rs:392:13 + --> $DIR/deprecation-lint.rs:391:13 | LL | inherit: 1, | ^^^^^^^^^^ error: use of deprecated field `this_crate2::Deprecated::inherit`: text - --> $DIR/deprecation-lint.rs:396:17 + --> $DIR/deprecation-lint.rs:395:17 | LL | let _ = x.inherit; | ^^^^^^^^^ error: use of deprecated field `this_crate2::Deprecated::inherit`: text - --> $DIR/deprecation-lint.rs:401:13 + --> $DIR/deprecation-lint.rs:400:13 | LL | inherit: _, | ^^^^^^^^^^ error: use of deprecated field `this_crate2::Deprecated2::0`: text - --> $DIR/deprecation-lint.rs:413:17 + --> $DIR/deprecation-lint.rs:412:17 | LL | let _ = x.0; | ^^^ error: use of deprecated field `this_crate2::Deprecated2::1`: text - --> $DIR/deprecation-lint.rs:415:17 + --> $DIR/deprecation-lint.rs:414:17 | LL | let _ = x.1; | ^^^ error: use of deprecated field `this_crate2::Deprecated2::2`: text - --> $DIR/deprecation-lint.rs:417:17 + --> $DIR/deprecation-lint.rs:416:17 | LL | let _ = x.2; | ^^^ error: use of deprecated field `this_crate2::Deprecated2::0`: text - --> $DIR/deprecation-lint.rs:422:14 + --> $DIR/deprecation-lint.rs:421:14 | LL | (_, | ^ error: use of deprecated field `this_crate2::Deprecated2::1`: text - --> $DIR/deprecation-lint.rs:424:14 + --> $DIR/deprecation-lint.rs:423:14 | LL | _, | ^ error: use of deprecated field `this_crate2::Deprecated2::2`: text - --> $DIR/deprecation-lint.rs:426:14 + --> $DIR/deprecation-lint.rs:425:14 | LL | _) | ^ diff --git a/src/test/ui/deprecation/rustc_deprecation-in-future.rs b/src/test/ui/deprecation/rustc_deprecation-in-future.rs index 11f7960b75786..3715f8eb2252d 100644 --- a/src/test/ui/deprecation/rustc_deprecation-in-future.rs +++ b/src/test/ui/deprecation/rustc_deprecation-in-future.rs @@ -1,5 +1,3 @@ -// ignore-tidy-linelength - #![deny(deprecated_in_future)] #![feature(staged_api)] diff --git a/src/test/ui/deprecation/rustc_deprecation-in-future.stderr b/src/test/ui/deprecation/rustc_deprecation-in-future.stderr index b5a7dd3c28daf..1c3339a8a9dc0 100644 --- a/src/test/ui/deprecation/rustc_deprecation-in-future.stderr +++ b/src/test/ui/deprecation/rustc_deprecation-in-future.stderr @@ -1,17 +1,17 @@ error: use of unit struct `S1` that will be deprecated in future version 99.99.99: effectively never - --> $DIR/rustc_deprecation-in-future.rs:18:13 + --> $DIR/rustc_deprecation-in-future.rs:16:13 | LL | let _ = S1; | ^^ | note: the lint level is defined here - --> $DIR/rustc_deprecation-in-future.rs:3:9 + --> $DIR/rustc_deprecation-in-future.rs:1:9 | LL | #![deny(deprecated_in_future)] | ^^^^^^^^^^^^^^^^^^^^ error: use of unit struct `S2` that will be deprecated in a future Rust version: literally never - --> $DIR/rustc_deprecation-in-future.rs:19:13 + --> $DIR/rustc_deprecation-in-future.rs:17:13 | LL | let _ = S2; | ^^ diff --git a/src/test/ui/deref-suggestion.rs b/src/test/ui/deref-suggestion.rs index 580410aecf4f8..4fd695585ba06 100644 --- a/src/test/ui/deref-suggestion.rs +++ b/src/test/ui/deref-suggestion.rs @@ -45,4 +45,30 @@ fn main() { //~^ ERROR mismatched types let r = R { i: i }; //~^ ERROR mismatched types + + + let a = &1; + let b = &2; + let val: i32 = if true { + a + 1 + } else { + b + //~^ ERROR mismatched types + }; + let val: i32 = if true { + let _ = 2; + a + 1 + } else { + let _ = 2; + b + //~^ ERROR mismatched types + }; + let val = if true { + *a + } else if true { + //~^ ERROR incompatible types + b + } else { + &0 + }; } diff --git a/src/test/ui/deref-suggestion.stderr b/src/test/ui/deref-suggestion.stderr index f59f05db9c047..632a279d79623 100644 --- a/src/test/ui/deref-suggestion.stderr +++ b/src/test/ui/deref-suggestion.stderr @@ -89,6 +89,43 @@ LL | let r = R { i: i }; | expected `u32`, found `&{integer}` | help: consider dereferencing the borrow: `*i` -error: aborting due to 10 previous errors +error[E0308]: mismatched types + --> $DIR/deref-suggestion.rs:55:9 + | +LL | b + | ^ + | | + | expected `i32`, found `&{integer}` + | help: consider dereferencing the borrow: `*b` + +error[E0308]: mismatched types + --> $DIR/deref-suggestion.rs:63:9 + | +LL | b + | ^ + | | + | expected `i32`, found `&{integer}` + | help: consider dereferencing the borrow: `*b` + +error[E0308]: `if` and `else` have incompatible types + --> $DIR/deref-suggestion.rs:68:12 + | +LL | let val = if true { + | _______________- +LL | | *a + | | -- expected because of this +LL | | } else if true { + | |____________^ +LL | || +LL | || b +LL | || } else { +LL | || &0 +LL | || }; + | || ^ + | ||_____| + | |______`if` and `else` have incompatible types + | expected `i32`, found `&{integer}` + +error: aborting due to 13 previous errors For more information about this error, try `rustc --explain E0308`. diff --git a/src/test/ui/derives/derive-assoc-type-not-impl.stderr b/src/test/ui/derives/derive-assoc-type-not-impl.stderr index ffee7004f8f8e..fd993d0f9d885 100644 --- a/src/test/ui/derives/derive-assoc-type-not-impl.stderr +++ b/src/test/ui/derives/derive-assoc-type-not-impl.stderr @@ -12,14 +12,6 @@ LL | struct NotClone; ... LL | Bar::<NotClone> { x: 1 }.clone(); | ^^^^^ method cannot be called on `Bar<NotClone>` due to unsatisfied trait bounds - | - ::: $SRC_DIR/core/src/clone.rs:LL:COL - | -LL | fn clone(&self) -> Self; - | ----- - | | - | the method is available for `Arc<Bar<NotClone>>` here - | the method is available for `Rc<Bar<NotClone>>` here | = note: the following trait bounds were not satisfied: `NotClone: Clone` diff --git a/src/test/ui/derives/derive-macro-const-default.rs b/src/test/ui/derives/derive-macro-const-default.rs new file mode 100644 index 0000000000000..a844f2d20237b --- /dev/null +++ b/src/test/ui/derives/derive-macro-const-default.rs @@ -0,0 +1,14 @@ +// check-pass +#![allow(incomplete_features)] +#![feature(const_generics_defaults)] + +#[derive(Clone, PartialEq, Debug)] +struct Example<T, const N: usize = 1usize>([T; N]); + +fn main() { + let a = Example([(); 16]); + let b = a.clone(); + if a != b { + let _c = format!("{:?}", a); + } +} diff --git a/src/test/ui/derives/deriving-with-repr-packed.rs b/src/test/ui/derives/deriving-with-repr-packed.rs index d354cef66bc06..b78eeaa90551b 100644 --- a/src/test/ui/derives/deriving-with-repr-packed.rs +++ b/src/test/ui/derives/deriving-with-repr-packed.rs @@ -1,4 +1,4 @@ -#![deny(safe_packed_borrows)] +#![deny(unaligned_references)] // check that derive on a packed struct with non-Copy fields // correctly. This can't be made to work perfectly because diff --git a/src/test/ui/derives/deriving-with-repr-packed.stderr b/src/test/ui/derives/deriving-with-repr-packed.stderr index d739257c8de63..3caa563a08560 100644 --- a/src/test/ui/derives/deriving-with-repr-packed.stderr +++ b/src/test/ui/derives/deriving-with-repr-packed.stderr @@ -7,10 +7,10 @@ LL | #[derive(Copy, Clone, PartialEq, Eq)] note: the lint level is defined here --> $DIR/deriving-with-repr-packed.rs:1:9 | -LL | #![deny(safe_packed_borrows)] - | ^^^^^^^^^^^^^^^^^^^ +LL | #![deny(unaligned_references)] + | ^^^^^^^^^^^^^^^^^^^^ = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = note: for more information, see issue #46043 <https://github.com/rust-lang/rust/issues/46043> + = note: for more information, see issue #82523 <https://github.com/rust-lang/rust/issues/82523> = note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info) error: `#[derive]` can't be used on a `#[repr(packed)]` struct with type or const parameters (error E0133) @@ -20,7 +20,7 @@ LL | #[derive(Copy, Clone, PartialEq, Eq)] | ^^^^^^^^^ | = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = note: for more information, see issue #46043 <https://github.com/rust-lang/rust/issues/46043> + = note: for more information, see issue #82523 <https://github.com/rust-lang/rust/issues/82523> = note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info) error: `#[derive]` can't be used on a `#[repr(packed)]` struct that does not derive Copy (error E0133) @@ -30,7 +30,7 @@ LL | #[derive(PartialEq, Eq)] | ^^^^^^^^^ | = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = note: for more information, see issue #46043 <https://github.com/rust-lang/rust/issues/46043> + = note: for more information, see issue #82523 <https://github.com/rust-lang/rust/issues/82523> = note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info) error: `#[derive]` can't be used on a `#[repr(packed)]` struct that does not derive Copy (error E0133) @@ -40,7 +40,7 @@ LL | #[derive(PartialEq)] | ^^^^^^^^^ | = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = note: for more information, see issue #46043 <https://github.com/rust-lang/rust/issues/46043> + = note: for more information, see issue #82523 <https://github.com/rust-lang/rust/issues/82523> = note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info) error: aborting due to 4 previous errors diff --git a/src/test/ui/dest-prop/skeptic-miscompile.rs b/src/test/ui/dest-prop/skeptic-miscompile.rs index c27a1f04532e7..4bb61dbc7f400 100644 --- a/src/test/ui/dest-prop/skeptic-miscompile.rs +++ b/src/test/ui/dest-prop/skeptic-miscompile.rs @@ -1,6 +1,6 @@ // run-pass -// compile-flags: -Zmir-opt-level=2 +// compile-flags: -Zmir-opt-level=3 trait IterExt: Iterator { fn fold_ex<B, F>(mut self, init: B, mut f: F) -> B diff --git a/src/test/ui/bad/bad-expr-lhs.rs b/src/test/ui/destructuring-assignment/bad-expr-lhs.rs similarity index 100% rename from src/test/ui/bad/bad-expr-lhs.rs rename to src/test/ui/destructuring-assignment/bad-expr-lhs.rs diff --git a/src/test/ui/bad/bad-expr-lhs.stderr b/src/test/ui/destructuring-assignment/bad-expr-lhs.stderr similarity index 100% rename from src/test/ui/bad/bad-expr-lhs.stderr rename to src/test/ui/destructuring-assignment/bad-expr-lhs.stderr diff --git a/src/test/ui/did_you_mean/issue-48492-tuple-destructure-missing-parens.stderr b/src/test/ui/did_you_mean/issue-48492-tuple-destructure-missing-parens.stderr index 26c24d7432c48..d05d6d120b084 100644 --- a/src/test/ui/did_you_mean/issue-48492-tuple-destructure-missing-parens.stderr +++ b/src/test/ui/did_you_mean/issue-48492-tuple-destructure-missing-parens.stderr @@ -47,19 +47,46 @@ error: unexpected `,` in pattern --> $DIR/issue-48492-tuple-destructure-missing-parens.rs:67:10 | LL | for x, _barr_body in women.iter().map(|woman| woman.allosomes.clone()) { - | -^----------- help: try adding parentheses to match on a tuple: `(x, _barr_body)` + | ^ + | +help: try adding parentheses to match on a tuple... + | +LL | for (x, _barr_body) in women.iter().map(|woman| woman.allosomes.clone()) { + | ^^^^^^^^^^^^^^^ +help: ...or a vertical bar to match on multiple alternatives + | +LL | for x | _barr_body in women.iter().map(|woman| woman.allosomes.clone()) { + | ^^^^^^^^^^^^^^ error: unexpected `,` in pattern --> $DIR/issue-48492-tuple-destructure-missing-parens.rs:75:10 | LL | for x, y @ Allosome::Y(_) in men.iter().map(|man| man.allosomes.clone()) { - | -^------------------- help: try adding parentheses to match on a tuple: `(x, y @ Allosome::Y(_))` + | ^ + | +help: try adding parentheses to match on a tuple... + | +LL | for (x, y @ Allosome::Y(_)) in men.iter().map(|man| man.allosomes.clone()) { + | ^^^^^^^^^^^^^^^^^^^^^^^ +help: ...or a vertical bar to match on multiple alternatives + | +LL | for x | y @ Allosome::Y(_) in men.iter().map(|man| man.allosomes.clone()) { + | ^^^^^^^^^^^^^^^^^^^^^^ error: unexpected `,` in pattern --> $DIR/issue-48492-tuple-destructure-missing-parens.rs:84:14 | LL | let women, men: (Vec<Genome>, Vec<Genome>) = genomes.iter().cloned() - | -----^---- help: try adding parentheses to match on a tuple: `(women, men)` + | ^ + | +help: try adding parentheses to match on a tuple... + | +LL | let (women, men): (Vec<Genome>, Vec<Genome>) = genomes.iter().cloned() + | ^^^^^^^^^^^^ +help: ...or a vertical bar to match on multiple alternatives + | +LL | let women | men: (Vec<Genome>, Vec<Genome>) = genomes.iter().cloned() + | ^^^^^^^^^^^ error: aborting due to 6 previous errors diff --git a/src/test/ui/did_you_mean/recursion_limit.stderr b/src/test/ui/did_you_mean/recursion_limit.stderr index c5b42416eac4a..2d52172a6fa53 100644 --- a/src/test/ui/did_you_mean/recursion_limit.stderr +++ b/src/test/ui/did_you_mean/recursion_limit.stderr @@ -8,16 +8,56 @@ LL | is_send::<A>(); | ^^^^^^^^^^^^ | = help: consider adding a `#![recursion_limit="20"]` attribute to your crate (`recursion_limit`) - = note: required because it appears within the type `J` - = note: required because it appears within the type `I` - = note: required because it appears within the type `H` - = note: required because it appears within the type `G` - = note: required because it appears within the type `F` - = note: required because it appears within the type `E` - = note: required because it appears within the type `D` - = note: required because it appears within the type `C` - = note: required because it appears within the type `B` - = note: required because it appears within the type `A` +note: required because it appears within the type `J` + --> $DIR/recursion_limit.rs:24:9 + | +LL | link! { J, K } + | ^ +note: required because it appears within the type `I` + --> $DIR/recursion_limit.rs:23:9 + | +LL | link! { I, J } + | ^ +note: required because it appears within the type `H` + --> $DIR/recursion_limit.rs:22:9 + | +LL | link! { H, I } + | ^ +note: required because it appears within the type `G` + --> $DIR/recursion_limit.rs:21:9 + | +LL | link! { G, H } + | ^ +note: required because it appears within the type `F` + --> $DIR/recursion_limit.rs:20:9 + | +LL | link! { F, G } + | ^ +note: required because it appears within the type `E` + --> $DIR/recursion_limit.rs:19:9 + | +LL | link! { E, F } + | ^ +note: required because it appears within the type `D` + --> $DIR/recursion_limit.rs:18:9 + | +LL | link! { D, E } + | ^ +note: required because it appears within the type `C` + --> $DIR/recursion_limit.rs:17:9 + | +LL | link! { C, D } + | ^ +note: required because it appears within the type `B` + --> $DIR/recursion_limit.rs:16:9 + | +LL | link! { B, C } + | ^ +note: required because it appears within the type `A` + --> $DIR/recursion_limit.rs:15:9 + | +LL | link! { A, B } + | ^ error: aborting due to previous error diff --git a/src/test/ui/directory_ownership/macro-expanded-mod.rs b/src/test/ui/directory_ownership/macro-expanded-mod.rs index 9cb159603a8c5..fa81769e5a800 100644 --- a/src/test/ui/directory_ownership/macro-expanded-mod.rs +++ b/src/test/ui/directory_ownership/macro-expanded-mod.rs @@ -2,7 +2,7 @@ macro_rules! mod_decl { ($i:ident) => { - mod $i; //~ ERROR Cannot declare a non-inline module inside a block + mod $i; //~ ERROR cannot declare a non-inline module inside a block }; } diff --git a/src/test/ui/directory_ownership/macro-expanded-mod.stderr b/src/test/ui/directory_ownership/macro-expanded-mod.stderr index f90419247c92b..4039728e18ac3 100644 --- a/src/test/ui/directory_ownership/macro-expanded-mod.stderr +++ b/src/test/ui/directory_ownership/macro-expanded-mod.stderr @@ -1,4 +1,4 @@ -error: Cannot declare a non-inline module inside a block unless it has a path attribute +error: cannot declare a non-inline module inside a block unless it has a path attribute --> $DIR/macro-expanded-mod.rs:5:9 | LL | mod $i; diff --git a/src/test/ui/directory_ownership/non-inline-mod-restriction.rs b/src/test/ui/directory_ownership/non-inline-mod-restriction.rs index af31b8a49281f..de4f816656cc4 100644 --- a/src/test/ui/directory_ownership/non-inline-mod-restriction.rs +++ b/src/test/ui/directory_ownership/non-inline-mod-restriction.rs @@ -1,5 +1,5 @@ // Test that non-inline modules are not allowed inside blocks. fn main() { - mod foo; //~ ERROR Cannot declare a non-inline module inside a block + mod foo; //~ ERROR cannot declare a non-inline module inside a block } diff --git a/src/test/ui/directory_ownership/non-inline-mod-restriction.stderr b/src/test/ui/directory_ownership/non-inline-mod-restriction.stderr index d034942ca5d4c..64189bee43f6e 100644 --- a/src/test/ui/directory_ownership/non-inline-mod-restriction.stderr +++ b/src/test/ui/directory_ownership/non-inline-mod-restriction.stderr @@ -1,4 +1,4 @@ -error: Cannot declare a non-inline module inside a block unless it has a path attribute +error: cannot declare a non-inline module inside a block unless it has a path attribute --> $DIR/non-inline-mod-restriction.rs:4:5 | LL | mod foo; diff --git a/src/test/ui/dst/dst-bad-deep.stderr b/src/test/ui/dst/dst-bad-deep.stderr index ea6b2390478ba..71e57b3e062b4 100644 --- a/src/test/ui/dst/dst-bad-deep.stderr +++ b/src/test/ui/dst/dst-bad-deep.stderr @@ -5,8 +5,16 @@ LL | let h: &Fat<Fat<[isize]>> = &Fat { ptr: *g }; | ^^^^^^^^^^^^^^^ doesn't have a size known at compile-time | = help: within `Fat<Fat<[isize]>>`, the trait `Sized` is not implemented for `[isize]` - = note: required because it appears within the type `Fat<[isize]>` - = note: required because it appears within the type `Fat<Fat<[isize]>>` +note: required because it appears within the type `Fat<[isize]>` + --> $DIR/dst-bad-deep.rs:6:8 + | +LL | struct Fat<T: ?Sized> { + | ^^^ +note: required because it appears within the type `Fat<Fat<[isize]>>` + --> $DIR/dst-bad-deep.rs:6:8 + | +LL | struct Fat<T: ?Sized> { + | ^^^ = note: structs must have a statically known size to be initialized error: aborting due to previous error diff --git a/src/test/ui/dst/dst-object-from-unsized-type.stderr b/src/test/ui/dst/dst-object-from-unsized-type.stderr index 49940112a9f63..2d12265df98ed 100644 --- a/src/test/ui/dst/dst-object-from-unsized-type.stderr +++ b/src/test/ui/dst/dst-object-from-unsized-type.stderr @@ -2,7 +2,7 @@ error[E0277]: the size for values of type `T` cannot be known at compilation tim --> $DIR/dst-object-from-unsized-type.rs:8:23 | LL | fn test1<T: ?Sized + Foo>(t: &T) { - | - this type parameter needs to be `Sized` + | - this type parameter needs to be `std::marker::Sized` LL | let u: &dyn Foo = t; | ^ doesn't have a size known at compile-time | @@ -12,7 +12,7 @@ error[E0277]: the size for values of type `T` cannot be known at compilation tim --> $DIR/dst-object-from-unsized-type.rs:13:23 | LL | fn test2<T: ?Sized + Foo>(t: &T) { - | - this type parameter needs to be `Sized` + | - this type parameter needs to be `std::marker::Sized` LL | let v: &dyn Foo = t as &dyn Foo; | ^ doesn't have a size known at compile-time | diff --git a/src/test/ui/editions-crate-root-2015.rs b/src/test/ui/editions-crate-root-2015.rs new file mode 100644 index 0000000000000..4c890e3ae6994 --- /dev/null +++ b/src/test/ui/editions-crate-root-2015.rs @@ -0,0 +1,21 @@ +// edition:2015 + +mod inner { + fn global_inner(_: ::nonexistant::Foo) { + //~^ ERROR failed to resolve: maybe a missing crate `nonexistant`? + } + fn crate_inner(_: crate::nonexistant::Foo) { + //~^ ERROR failed to resolve: maybe a missing crate `nonexistant`? + } + + fn bare_global(_: ::nonexistant) { + //~^ ERROR cannot find type `nonexistant` in the crate root + } + fn bare_crate(_: crate::nonexistant) { + //~^ ERROR cannot find type `nonexistant` in the crate root + } +} + +fn main() { + +} diff --git a/src/test/ui/editions-crate-root-2015.stderr b/src/test/ui/editions-crate-root-2015.stderr new file mode 100644 index 0000000000000..f8d65fec3d12d --- /dev/null +++ b/src/test/ui/editions-crate-root-2015.stderr @@ -0,0 +1,28 @@ +error[E0433]: failed to resolve: maybe a missing crate `nonexistant`? + --> $DIR/editions-crate-root-2015.rs:4:26 + | +LL | fn global_inner(_: ::nonexistant::Foo) { + | ^^^^^^^^^^^ maybe a missing crate `nonexistant`? + +error[E0433]: failed to resolve: maybe a missing crate `nonexistant`? + --> $DIR/editions-crate-root-2015.rs:7:30 + | +LL | fn crate_inner(_: crate::nonexistant::Foo) { + | ^^^^^^^^^^^ maybe a missing crate `nonexistant`? + +error[E0412]: cannot find type `nonexistant` in the crate root + --> $DIR/editions-crate-root-2015.rs:11:25 + | +LL | fn bare_global(_: ::nonexistant) { + | ^^^^^^^^^^^ not found in the crate root + +error[E0412]: cannot find type `nonexistant` in the crate root + --> $DIR/editions-crate-root-2015.rs:14:29 + | +LL | fn bare_crate(_: crate::nonexistant) { + | ^^^^^^^^^^^ not found in the crate root + +error: aborting due to 4 previous errors + +Some errors have detailed explanations: E0412, E0433. +For more information about an error, try `rustc --explain E0412`. diff --git a/src/test/ui/editions-crate-root-2018.rs b/src/test/ui/editions-crate-root-2018.rs new file mode 100644 index 0000000000000..61e4329bbb347 --- /dev/null +++ b/src/test/ui/editions-crate-root-2018.rs @@ -0,0 +1,21 @@ +// edition:2018 + +mod inner { + fn global_inner(_: ::nonexistant::Foo) { + //~^ ERROR failed to resolve: could not find `nonexistant` in the list of imported crates + } + fn crate_inner(_: crate::nonexistant::Foo) { + //~^ ERROR failed to resolve: could not find `nonexistant` in the crate root + } + + fn bare_global(_: ::nonexistant) { + //~^ ERROR cannot find crate `nonexistant` in the list of imported crates + } + fn bare_crate(_: crate::nonexistant) { + //~^ ERROR cannot find type `nonexistant` in the crate root + } +} + +fn main() { + +} diff --git a/src/test/ui/editions-crate-root-2018.stderr b/src/test/ui/editions-crate-root-2018.stderr new file mode 100644 index 0000000000000..967a5a2fca155 --- /dev/null +++ b/src/test/ui/editions-crate-root-2018.stderr @@ -0,0 +1,28 @@ +error[E0433]: failed to resolve: could not find `nonexistant` in the list of imported crates + --> $DIR/editions-crate-root-2018.rs:4:26 + | +LL | fn global_inner(_: ::nonexistant::Foo) { + | ^^^^^^^^^^^ could not find `nonexistant` in the list of imported crates + +error[E0433]: failed to resolve: could not find `nonexistant` in the crate root + --> $DIR/editions-crate-root-2018.rs:7:30 + | +LL | fn crate_inner(_: crate::nonexistant::Foo) { + | ^^^^^^^^^^^ could not find `nonexistant` in the crate root + +error[E0412]: cannot find crate `nonexistant` in the list of imported crates + --> $DIR/editions-crate-root-2018.rs:11:25 + | +LL | fn bare_global(_: ::nonexistant) { + | ^^^^^^^^^^^ not found in the list of imported crates + +error[E0412]: cannot find type `nonexistant` in the crate root + --> $DIR/editions-crate-root-2018.rs:14:29 + | +LL | fn bare_crate(_: crate::nonexistant) { + | ^^^^^^^^^^^ not found in the crate root + +error: aborting due to 4 previous errors + +Some errors have detailed explanations: E0412, E0433. +For more information about an error, try `rustc --explain E0412`. diff --git a/src/test/ui/editions/edition-imports-virtual-2015-gated.stderr b/src/test/ui/editions/edition-imports-virtual-2015-gated.stderr index 06605c6f2082e..1cde0f72140d1 100644 --- a/src/test/ui/editions/edition-imports-virtual-2015-gated.stderr +++ b/src/test/ui/editions/edition-imports-virtual-2015-gated.stderr @@ -2,7 +2,7 @@ error[E0432]: unresolved import `E` --> $DIR/edition-imports-virtual-2015-gated.rs:8:5 | LL | gen_gated!(); - | ^^^^^^^^^^^^^ could not find `E` in crate root + | ^^^^^^^^^^^^^ could not find `E` in the list of imported crates | = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/src/test/ui/error-codes/E0063.rs b/src/test/ui/error-codes/E0063.rs index 58527cc0c5dd7..48c9c13f018f7 100644 --- a/src/test/ui/error-codes/E0063.rs +++ b/src/test/ui/error-codes/E0063.rs @@ -1,5 +1,3 @@ -// ignore-tidy-linelength - struct SingleFoo { x: i32 } diff --git a/src/test/ui/error-codes/E0063.stderr b/src/test/ui/error-codes/E0063.stderr index 5dc4927071b6b..235e204025ae2 100644 --- a/src/test/ui/error-codes/E0063.stderr +++ b/src/test/ui/error-codes/E0063.stderr @@ -1,23 +1,23 @@ error[E0063]: missing field `x` in initializer of `SingleFoo` - --> $DIR/E0063.rs:32:13 + --> $DIR/E0063.rs:30:13 | LL | let w = SingleFoo { }; | ^^^^^^^^^ missing `x` error[E0063]: missing fields `y` and `z` in initializer of `PluralFoo` - --> $DIR/E0063.rs:34:13 + --> $DIR/E0063.rs:32:13 | LL | let x = PluralFoo {x: 1}; | ^^^^^^^^^ missing `y` and `z` error[E0063]: missing fields `a`, `b`, `y` and 1 other field in initializer of `TruncatedFoo` - --> $DIR/E0063.rs:36:13 + --> $DIR/E0063.rs:34:13 | LL | let y = TruncatedFoo{x:1}; | ^^^^^^^^^^^^ missing `a`, `b`, `y` and 1 other field error[E0063]: missing fields `a`, `b`, `c` and 2 other fields in initializer of `TruncatedPluralFoo` - --> $DIR/E0063.rs:38:13 + --> $DIR/E0063.rs:36:13 | LL | let z = TruncatedPluralFoo{x:1}; | ^^^^^^^^^^^^^^^^^^ missing `a`, `b`, `c` and 2 other fields diff --git a/src/test/ui/error-codes/E0106.stderr b/src/test/ui/error-codes/E0106.stderr index ac70e887626ab..b961fc8aeeb53 100644 --- a/src/test/ui/error-codes/E0106.stderr +++ b/src/test/ui/error-codes/E0106.stderr @@ -23,17 +23,6 @@ LL | A(u8), LL | B(&'a bool), | -error[E0106]: missing lifetime specifier - --> $DIR/E0106.rs:10:14 - | -LL | type MyStr = &str; - | ^ expected named lifetime parameter - | -help: consider introducing a named lifetime parameter - | -LL | type MyStr<'a> = &'a str; - | ^^^^ ^^^ - error[E0106]: missing lifetime specifier --> $DIR/E0106.rs:17:10 | @@ -61,6 +50,17 @@ LL | LL | buzz: Buzz<'a, 'a>, | +error[E0106]: missing lifetime specifier + --> $DIR/E0106.rs:10:14 + | +LL | type MyStr = &str; + | ^ expected named lifetime parameter + | +help: consider introducing a named lifetime parameter + | +LL | type MyStr<'a> = &'a str; + | ^^^^ ^^^ + error: aborting due to 5 previous errors For more information about this error, try `rustc --explain E0106`. diff --git a/src/test/ui/error-codes/E0119.stderr b/src/test/ui/error-codes/E0119.stderr index e7690aa30bd20..e08a2c7fcbbd0 100644 --- a/src/test/ui/error-codes/E0119.stderr +++ b/src/test/ui/error-codes/E0119.stderr @@ -1,4 +1,4 @@ -error[E0119]: conflicting implementations of trait `MyTrait` for type `Foo`: +error[E0119]: conflicting implementations of trait `MyTrait` for type `Foo` --> $DIR/E0119.rs:13:1 | LL | impl<T> MyTrait for T { diff --git a/src/test/ui/error-codes/E0128.stderr b/src/test/ui/error-codes/E0128.stderr index 253aa166bd371..eb66d46936a85 100644 --- a/src/test/ui/error-codes/E0128.stderr +++ b/src/test/ui/error-codes/E0128.stderr @@ -1,8 +1,8 @@ -error[E0128]: type parameters with a default cannot use forward declared identifiers +error[E0128]: generic parameters with a default cannot use forward declared identifiers --> $DIR/E0128.rs:1:14 | LL | struct Foo<T=U, U=()> { - | ^ defaulted type parameters cannot be forward declared + | ^ defaulted generic parameters cannot be forward declared error: aborting due to previous error diff --git a/src/test/ui/error-codes/E0137.rs b/src/test/ui/error-codes/E0137.rs deleted file mode 100644 index b8299c71bdf6d..0000000000000 --- a/src/test/ui/error-codes/E0137.rs +++ /dev/null @@ -1,8 +0,0 @@ -#![feature(main)] - -#[main] -fn foo() {} - -#[main] -fn f() {} -//~^ ERROR E0137 diff --git a/src/test/ui/error-codes/E0137.stderr b/src/test/ui/error-codes/E0137.stderr deleted file mode 100644 index f4d5e10369ac7..0000000000000 --- a/src/test/ui/error-codes/E0137.stderr +++ /dev/null @@ -1,12 +0,0 @@ -error[E0137]: multiple functions with a `#[main]` attribute - --> $DIR/E0137.rs:7:1 - | -LL | fn foo() {} - | ----------- first `#[main]` function -... -LL | fn f() {} - | ^^^^^^^^^ additional `#[main]` function - -error: aborting due to previous error - -For more information about this error, try `rustc --explain E0137`. diff --git a/src/test/ui/error-codes/E0275.stderr b/src/test/ui/error-codes/E0275.stderr index 46966f22b6d1e..390c1e3e8ea4a 100644 --- a/src/test/ui/error-codes/E0275.stderr +++ b/src/test/ui/error-codes/E0275.stderr @@ -8,7 +8,11 @@ LL | impl<T> Foo for T where Bar<T>: Foo {} | ^^^ | = help: consider adding a `#![recursion_limit="256"]` attribute to your crate (`E0275`) - = note: required because of the requirements on the impl of `Foo` for `Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<T>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>` +note: required because of the requirements on the impl of `Foo` for `Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<T>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>` + --> $DIR/E0275.rs:5:9 + | +LL | impl<T> Foo for T where Bar<T>: Foo {} + | ^^^ ^ = note: 127 redundant requirements hidden = note: required because of the requirements on the impl of `Foo` for `Bar<T>` diff --git a/src/test/ui/error-codes/E0277-2.stderr b/src/test/ui/error-codes/E0277-2.stderr index a0ab1641ca7c6..afd0e032dc3b3 100644 --- a/src/test/ui/error-codes/E0277-2.stderr +++ b/src/test/ui/error-codes/E0277-2.stderr @@ -8,9 +8,21 @@ LL | is_send::<Foo>(); | ^^^^^^^^^^^^^^ `*const u8` cannot be sent between threads safely | = help: within `Foo`, the trait `Send` is not implemented for `*const u8` - = note: required because it appears within the type `Baz` - = note: required because it appears within the type `Bar` - = note: required because it appears within the type `Foo` +note: required because it appears within the type `Baz` + --> $DIR/E0277-2.rs:9:8 + | +LL | struct Baz { + | ^^^ +note: required because it appears within the type `Bar` + --> $DIR/E0277-2.rs:5:8 + | +LL | struct Bar { + | ^^^ +note: required because it appears within the type `Foo` + --> $DIR/E0277-2.rs:1:8 + | +LL | struct Foo { + | ^^^ error: aborting due to previous error diff --git a/src/test/ui/error-codes/E0277.stderr b/src/test/ui/error-codes/E0277.stderr index 1f54af4d154e4..dea0bb259f506 100644 --- a/src/test/ui/error-codes/E0277.stderr +++ b/src/test/ui/error-codes/E0277.stderr @@ -9,8 +9,8 @@ LL | fn f(p: Path) { } = help: unsized fn params are gated as an unstable feature help: function arguments must have a statically known size, borrowed types always have a known size | -LL | fn f(&p: Path) { } - | ^ +LL | fn f(p: &Path) { } + | ^ error[E0277]: the trait bound `i32: Foo` is not satisfied --> $DIR/E0277.rs:15:15 diff --git a/src/test/ui/error-codes/E0565.rs b/src/test/ui/error-codes/E0565.rs index 3bf428676105d..df76f6b13af9b 100644 --- a/src/test/ui/error-codes/E0565.rs +++ b/src/test/ui/error-codes/E0565.rs @@ -1,6 +1,5 @@ // repr currently doesn't support literals #[repr("C")] //~ ERROR E0565 - //~| ERROR E0565 -struct A { } +struct A {} -fn main() { } +fn main() {} diff --git a/src/test/ui/error-codes/E0565.stderr b/src/test/ui/error-codes/E0565.stderr index aa0951528e1ed..6ed90c0ae4ffe 100644 --- a/src/test/ui/error-codes/E0565.stderr +++ b/src/test/ui/error-codes/E0565.stderr @@ -4,12 +4,6 @@ error[E0565]: meta item in `repr` must be an identifier LL | #[repr("C")] | ^^^ -error[E0565]: meta item in `repr` must be an identifier - --> $DIR/E0565.rs:2:8 - | -LL | #[repr("C")] - | ^^^ - -error: aborting due to 2 previous errors +error: aborting due to previous error For more information about this error, try `rustc --explain E0565`. diff --git a/src/test/ui/error-codes/e0119/complex-impl.stderr b/src/test/ui/error-codes/e0119/complex-impl.stderr index d617d8129248a..04babb0644718 100644 --- a/src/test/ui/error-codes/e0119/complex-impl.stderr +++ b/src/test/ui/error-codes/e0119/complex-impl.stderr @@ -1,4 +1,4 @@ -error[E0119]: conflicting implementations of trait `complex_impl_support::External` for type `(Q, complex_impl_support::M<'_, '_, '_, std::boxed::Box<_>, _, _>)`: +error[E0119]: conflicting implementations of trait `complex_impl_support::External` for type `(Q, complex_impl_support::M<'_, '_, '_, std::boxed::Box<_>, _, _>)` --> $DIR/complex-impl.rs:9:1 | LL | impl<R> External for (Q, R) {} diff --git a/src/test/ui/error-codes/e0119/conflict-with-std.stderr b/src/test/ui/error-codes/e0119/conflict-with-std.stderr index 68551f4377591..3ff96a6a4d65d 100644 --- a/src/test/ui/error-codes/e0119/conflict-with-std.stderr +++ b/src/test/ui/error-codes/e0119/conflict-with-std.stderr @@ -1,4 +1,4 @@ -error[E0119]: conflicting implementations of trait `std::convert::AsRef<Q>` for type `std::boxed::Box<Q>`: +error[E0119]: conflicting implementations of trait `std::convert::AsRef<Q>` for type `std::boxed::Box<Q>` --> $DIR/conflict-with-std.rs:5:1 | LL | impl AsRef<Q> for Box<Q> { @@ -8,7 +8,7 @@ LL | impl AsRef<Q> for Box<Q> { - impl<T, A> AsRef<T> for Box<T, A> where A: Allocator, T: ?Sized; -error[E0119]: conflicting implementations of trait `std::convert::From<S>` for type `S`: +error[E0119]: conflicting implementations of trait `std::convert::From<S>` for type `S` --> $DIR/conflict-with-std.rs:12:1 | LL | impl From<S> for S { @@ -17,7 +17,7 @@ LL | impl From<S> for S { = note: conflicting implementation in crate `core`: - impl<T> From<T> for T; -error[E0119]: conflicting implementations of trait `std::convert::TryFrom<X>` for type `X`: +error[E0119]: conflicting implementations of trait `std::convert::TryFrom<X>` for type `X` --> $DIR/conflict-with-std.rs:19:1 | LL | impl TryFrom<X> for X { diff --git a/src/test/ui/error-codes/e0119/issue-23563.stderr b/src/test/ui/error-codes/e0119/issue-23563.stderr index 912a80fec7551..f149cef587f25 100644 --- a/src/test/ui/error-codes/e0119/issue-23563.stderr +++ b/src/test/ui/error-codes/e0119/issue-23563.stderr @@ -1,4 +1,4 @@ -error[E0119]: conflicting implementations of trait `a::LolFrom<&[_]>` for type `LocalType<_>`: +error[E0119]: conflicting implementations of trait `a::LolFrom<&[_]>` for type `LocalType<_>` --> $DIR/issue-23563.rs:13:1 | LL | impl<'a, T> LolFrom<&'a [T]> for LocalType<T> { diff --git a/src/test/ui/error-codes/e0119/issue-27403.stderr b/src/test/ui/error-codes/e0119/issue-27403.stderr index ea74c9b21bd4a..c11a50487479e 100644 --- a/src/test/ui/error-codes/e0119/issue-27403.stderr +++ b/src/test/ui/error-codes/e0119/issue-27403.stderr @@ -1,4 +1,4 @@ -error[E0119]: conflicting implementations of trait `std::convert::Into<_>` for type `GenX<_>`: +error[E0119]: conflicting implementations of trait `std::convert::Into<_>` for type `GenX<_>` --> $DIR/issue-27403.rs:5:1 | LL | impl<S> Into<S> for GenX<S> { diff --git a/src/test/ui/error-codes/e0119/issue-28981.stderr b/src/test/ui/error-codes/e0119/issue-28981.stderr index e12dd7623310d..56e8e1eb54002 100644 --- a/src/test/ui/error-codes/e0119/issue-28981.stderr +++ b/src/test/ui/error-codes/e0119/issue-28981.stderr @@ -1,4 +1,4 @@ -error[E0119]: conflicting implementations of trait `std::ops::Deref` for type `&_`: +error[E0119]: conflicting implementations of trait `std::ops::Deref` for type `&_` --> $DIR/issue-28981.rs:5:1 | LL | impl<Foo> Deref for Foo { } diff --git a/src/test/ui/error-codes/e0119/so-37347311.stderr b/src/test/ui/error-codes/e0119/so-37347311.stderr index a9fbd0fee4981..f1c2b0d29742e 100644 --- a/src/test/ui/error-codes/e0119/so-37347311.stderr +++ b/src/test/ui/error-codes/e0119/so-37347311.stderr @@ -1,4 +1,4 @@ -error[E0119]: conflicting implementations of trait `std::convert::From<MyError<_>>` for type `MyError<_>`: +error[E0119]: conflicting implementations of trait `std::convert::From<MyError<_>>` for type `MyError<_>` --> $DIR/so-37347311.rs:11:1 | LL | impl<S: Storage> From<S::Error> for MyError<S> { diff --git a/src/test/ui/export-fully-qualified.rs b/src/test/ui/export-fully-qualified.rs index 40f26c7095f13..4e73a2c548884 100644 --- a/src/test/ui/export-fully-qualified.rs +++ b/src/test/ui/export-fully-qualified.rs @@ -1,5 +1,3 @@ -// ignore-tidy-linelength - // In this test baz isn't resolved when called as foo.baz even though // it's called from inside foo. This is somewhat surprising and may // want to change eventually. diff --git a/src/test/ui/export-fully-qualified.stderr b/src/test/ui/export-fully-qualified.stderr index a8af0c7c9b823..7ee352e1232a7 100644 --- a/src/test/ui/export-fully-qualified.stderr +++ b/src/test/ui/export-fully-qualified.stderr @@ -1,5 +1,5 @@ error[E0433]: failed to resolve: use of undeclared crate or module `foo` - --> $DIR/export-fully-qualified.rs:8:20 + --> $DIR/export-fully-qualified.rs:6:20 | LL | pub fn bar() { foo::baz(); } | ^^^ use of undeclared crate or module `foo` diff --git a/src/test/ui/extern/extern-types-unsized.stderr b/src/test/ui/extern/extern-types-unsized.stderr index 278db45655720..72e4be51822e2 100644 --- a/src/test/ui/extern/extern-types-unsized.stderr +++ b/src/test/ui/extern/extern-types-unsized.stderr @@ -23,7 +23,11 @@ LL | assert_sized::<Foo>(); | ^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time | = help: within `Foo`, the trait `Sized` is not implemented for `A` - = note: required because it appears within the type `Foo` +note: required because it appears within the type `Foo` + --> $DIR/extern-types-unsized.rs:9:8 + | +LL | struct Foo { + | ^^^ help: consider relaxing the implicit `Sized` restriction | LL | fn assert_sized<T: ?Sized>() {} @@ -39,7 +43,11 @@ LL | assert_sized::<Bar<A>>(); | ^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time | = help: within `Bar<A>`, the trait `Sized` is not implemented for `A` - = note: required because it appears within the type `Bar<A>` +note: required because it appears within the type `Bar<A>` + --> $DIR/extern-types-unsized.rs:14:8 + | +LL | struct Bar<T: ?Sized> { + | ^^^ help: consider relaxing the implicit `Sized` restriction | LL | fn assert_sized<T: ?Sized>() {} @@ -55,8 +63,16 @@ LL | assert_sized::<Bar<Bar<A>>>(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time | = help: within `Bar<Bar<A>>`, the trait `Sized` is not implemented for `A` - = note: required because it appears within the type `Bar<A>` - = note: required because it appears within the type `Bar<Bar<A>>` +note: required because it appears within the type `Bar<A>` + --> $DIR/extern-types-unsized.rs:14:8 + | +LL | struct Bar<T: ?Sized> { + | ^^^ +note: required because it appears within the type `Bar<Bar<A>>` + --> $DIR/extern-types-unsized.rs:14:8 + | +LL | struct Bar<T: ?Sized> { + | ^^^ help: consider relaxing the implicit `Sized` restriction | LL | fn assert_sized<T: ?Sized>() {} diff --git a/src/test/ui/extern/issue-64655-allow-unwind-when-calling-panic-directly.rs b/src/test/ui/extern/issue-64655-allow-unwind-when-calling-panic-directly.rs index 74c6e501c9136..7b731a1d71da3 100644 --- a/src/test/ui/extern/issue-64655-allow-unwind-when-calling-panic-directly.rs +++ b/src/test/ui/extern/issue-64655-allow-unwind-when-calling-panic-directly.rs @@ -19,7 +19,9 @@ // revisions: no thin fat //[no]compile-flags: -C lto=no -//[thin]compile-flags: -C lto=thin +// FIXME(#83854) running this revision with 1 CGU triggers llvm assert in register allocator +// when executed in i686-gnu-nopt runner. +//[thin]compile-flags: -C lto=thin -Ccodegen-units=2 //[fat]compile-flags: -C lto=fat #![feature(core_panic)] diff --git a/src/test/ui/extern/issue-64655-extern-rust-must-allow-unwind.rs b/src/test/ui/extern/issue-64655-extern-rust-must-allow-unwind.rs index bc15fcb0e3996..32e6d0c90b255 100644 --- a/src/test/ui/extern/issue-64655-extern-rust-must-allow-unwind.rs +++ b/src/test/ui/extern/issue-64655-extern-rust-must-allow-unwind.rs @@ -40,7 +40,9 @@ //[no1]compile-flags: -C opt-level=1 -C lto=no //[no2]compile-flags: -C opt-level=2 -C lto=no //[no3]compile-flags: -C opt-level=3 -C lto=no -//[thin0]compile-flags: -C opt-level=0 -C lto=thin +// FIXME(#83854) running this revision with 1 CGU triggers llvm assert in register allocator +// when executed in dist-i586-gnu-i586-i686-musl runner. +//[thin0]compile-flags: -C opt-level=0 -C lto=thin -Ccodegen-units=2 //[thin1]compile-flags: -C opt-level=1 -C lto=thin //[thin2]compile-flags: -C opt-level=2 -C lto=thin //[thin3]compile-flags: -C opt-level=3 -C lto=thin diff --git a/src/test/ui/feature-gates/feature-gate-abi.rs b/src/test/ui/feature-gates/feature-gate-abi.rs index 31f09cc61f9ac..49cf2e158edfc 100644 --- a/src/test/ui/feature-gates/feature-gate-abi.rs +++ b/src/test/ui/feature-gates/feature-gate-abi.rs @@ -1,5 +1,4 @@ // only-x86_64 -// ignore-tidy-linelength // gate-test-intrinsics // gate-test-platform_intrinsics // gate-test-abi_vectorcall @@ -11,9 +10,9 @@ // Functions extern "rust-intrinsic" fn f1() {} //~ ERROR intrinsics are subject to change -//~^ ERROR intrinsic must be in + //~^ ERROR intrinsic must be in extern "platform-intrinsic" fn f2() {} //~ ERROR platform intrinsics are experimental -//~^ ERROR intrinsic must be in + //~^ ERROR intrinsic must be in extern "vectorcall" fn f3() {} //~ ERROR vectorcall is experimental and subject to change extern "rust-call" fn f4(_: ()) {} //~ ERROR rust-call ABI is subject to change extern "msp430-interrupt" fn f5() {} //~ ERROR msp430-interrupt ABI is experimental @@ -22,13 +21,14 @@ extern "x86-interrupt" fn f7() {} //~ ERROR x86-interrupt ABI is experimental extern "thiscall" fn f8() {} //~ ERROR thiscall is experimental and subject to change extern "amdgpu-kernel" fn f9() {} //~ ERROR amdgpu-kernel ABI is experimental and subject to change extern "efiapi" fn f10() {} //~ ERROR efiapi ABI is experimental and subject to change +extern "wasm" fn f11() {} //~ ERROR wasm ABI is experimental and subject to change // Methods in trait definition trait Tr { extern "rust-intrinsic" fn m1(); //~ ERROR intrinsics are subject to change - //~^ ERROR intrinsic must be in + //~^ ERROR intrinsic must be in extern "platform-intrinsic" fn m2(); //~ ERROR platform intrinsics are experimental - //~^ ERROR intrinsic must be in + //~^ ERROR intrinsic must be in extern "vectorcall" fn m3(); //~ ERROR vectorcall is experimental and subject to change extern "rust-call" fn m4(_: ()); //~ ERROR rust-call ABI is subject to change extern "msp430-interrupt" fn m5(); //~ ERROR msp430-interrupt ABI is experimental @@ -37,6 +37,7 @@ trait Tr { extern "thiscall" fn m8(); //~ ERROR thiscall is experimental and subject to change extern "amdgpu-kernel" fn m9(); //~ ERROR amdgpu-kernel ABI is experimental and subject to change extern "efiapi" fn m10(); //~ ERROR efiapi ABI is experimental and subject to change + extern "wasm" fn m11() {} //~ ERROR wasm ABI is experimental and subject to change extern "vectorcall" fn dm3() {} //~ ERROR vectorcall is experimental and subject to change extern "rust-call" fn dm4(_: ()) {} //~ ERROR rust-call ABI is subject to change @@ -46,6 +47,7 @@ trait Tr { extern "thiscall" fn dm8() {} //~ ERROR thiscall is experimental and subject to change extern "amdgpu-kernel" fn dm9() {} //~ ERROR amdgpu-kernel ABI is experimental and subject to change extern "efiapi" fn dm10() {} //~ ERROR efiapi ABI is experimental and subject to change + extern "wasm" fn dm11() {} //~ ERROR wasm ABI is experimental and subject to change } struct S; @@ -53,9 +55,9 @@ struct S; // Methods in trait impl impl Tr for S { extern "rust-intrinsic" fn m1() {} //~ ERROR intrinsics are subject to change - //~^ ERROR intrinsic must be in + //~^ ERROR intrinsic must be in extern "platform-intrinsic" fn m2() {} //~ ERROR platform intrinsics are experimental - //~^ ERROR intrinsic must be in + //~^ ERROR intrinsic must be in extern "vectorcall" fn m3() {} //~ ERROR vectorcall is experimental and subject to change extern "rust-call" fn m4(_: ()) {} //~ ERROR rust-call ABI is subject to change extern "msp430-interrupt" fn m5() {} //~ ERROR msp430-interrupt ABI is experimental @@ -64,14 +66,15 @@ impl Tr for S { extern "thiscall" fn m8() {} //~ ERROR thiscall is experimental and subject to change extern "amdgpu-kernel" fn m9() {} //~ ERROR amdgpu-kernel ABI is experimental and subject to change extern "efiapi" fn m10() {} //~ ERROR efiapi ABI is experimental and subject to change + extern "wasm" fn m11() {} //~ ERROR wasm ABI is experimental and subject to change } // Methods in inherent impl impl S { extern "rust-intrinsic" fn im1() {} //~ ERROR intrinsics are subject to change - //~^ ERROR intrinsic must be in + //~^ ERROR intrinsic must be in extern "platform-intrinsic" fn im2() {} //~ ERROR platform intrinsics are experimental - //~^ ERROR intrinsic must be in + //~^ ERROR intrinsic must be in extern "vectorcall" fn im3() {} //~ ERROR vectorcall is experimental and subject to change extern "rust-call" fn im4(_: ()) {} //~ ERROR rust-call ABI is subject to change extern "msp430-interrupt" fn im5() {} //~ ERROR msp430-interrupt ABI is experimental @@ -80,6 +83,7 @@ impl S { extern "thiscall" fn im8() {} //~ ERROR thiscall is experimental and subject to change extern "amdgpu-kernel" fn im9() {} //~ ERROR amdgpu-kernel ABI is experimental and subject to change extern "efiapi" fn im10() {} //~ ERROR efiapi ABI is experimental and subject to change + extern "wasm" fn im11() {} //~ ERROR wasm ABI is experimental and subject to change } // Function pointer types @@ -88,11 +92,12 @@ type A2 = extern "platform-intrinsic" fn(); //~ ERROR platform intrinsics are ex type A3 = extern "vectorcall" fn(); //~ ERROR vectorcall is experimental and subject to change type A4 = extern "rust-call" fn(_: ()); //~ ERROR rust-call ABI is subject to change type A5 = extern "msp430-interrupt" fn(); //~ ERROR msp430-interrupt ABI is experimental -type A6 = extern "ptx-kernel" fn (); //~ ERROR PTX ABIs are experimental and subject to change +type A6 = extern "ptx-kernel" fn(); //~ ERROR PTX ABIs are experimental and subject to change type A7 = extern "x86-interrupt" fn(); //~ ERROR x86-interrupt ABI is experimental type A8 = extern "thiscall" fn(); //~ ERROR thiscall is experimental and subject to change type A9 = extern "amdgpu-kernel" fn(); //~ ERROR amdgpu-kernel ABI is experimental and subject to change type A10 = extern "efiapi" fn(); //~ ERROR efiapi ABI is experimental and subject to change +type A11 = extern "wasm" fn(); //~ ERROR wasm ABI is experimental and subject to change // Foreign modules extern "rust-intrinsic" {} //~ ERROR intrinsics are subject to change @@ -105,5 +110,6 @@ extern "x86-interrupt" {} //~ ERROR x86-interrupt ABI is experimental extern "thiscall" {} //~ ERROR thiscall is experimental and subject to change extern "amdgpu-kernel" {} //~ ERROR amdgpu-kernel ABI is experimental and subject to change extern "efiapi" {} //~ ERROR efiapi ABI is experimental and subject to change +extern "wasm" {} //~ ERROR wasm ABI is experimental and subject to change fn main() {} diff --git a/src/test/ui/feature-gates/feature-gate-abi.stderr b/src/test/ui/feature-gates/feature-gate-abi.stderr index 25f0c259d111c..078d21ad36f90 100644 --- a/src/test/ui/feature-gates/feature-gate-abi.stderr +++ b/src/test/ui/feature-gates/feature-gate-abi.stderr @@ -1,5 +1,5 @@ error[E0658]: intrinsics are subject to change - --> $DIR/feature-gate-abi.rs:13:8 + --> $DIR/feature-gate-abi.rs:12:8 | LL | extern "rust-intrinsic" fn f1() {} | ^^^^^^^^^^^^^^^^ @@ -7,7 +7,7 @@ LL | extern "rust-intrinsic" fn f1() {} = help: add `#![feature(intrinsics)]` to the crate attributes to enable error[E0658]: platform intrinsics are experimental and possibly buggy - --> $DIR/feature-gate-abi.rs:15:8 + --> $DIR/feature-gate-abi.rs:14:8 | LL | extern "platform-intrinsic" fn f2() {} | ^^^^^^^^^^^^^^^^^^^^ @@ -16,7 +16,7 @@ LL | extern "platform-intrinsic" fn f2() {} = help: add `#![feature(platform_intrinsics)]` to the crate attributes to enable error[E0658]: vectorcall is experimental and subject to change - --> $DIR/feature-gate-abi.rs:17:8 + --> $DIR/feature-gate-abi.rs:16:8 | LL | extern "vectorcall" fn f3() {} | ^^^^^^^^^^^^ @@ -24,7 +24,7 @@ LL | extern "vectorcall" fn f3() {} = help: add `#![feature(abi_vectorcall)]` to the crate attributes to enable error[E0658]: rust-call ABI is subject to change - --> $DIR/feature-gate-abi.rs:18:8 + --> $DIR/feature-gate-abi.rs:17:8 | LL | extern "rust-call" fn f4(_: ()) {} | ^^^^^^^^^^^ @@ -33,7 +33,7 @@ LL | extern "rust-call" fn f4(_: ()) {} = help: add `#![feature(unboxed_closures)]` to the crate attributes to enable error[E0658]: msp430-interrupt ABI is experimental and subject to change - --> $DIR/feature-gate-abi.rs:19:8 + --> $DIR/feature-gate-abi.rs:18:8 | LL | extern "msp430-interrupt" fn f5() {} | ^^^^^^^^^^^^^^^^^^ @@ -42,7 +42,7 @@ LL | extern "msp430-interrupt" fn f5() {} = help: add `#![feature(abi_msp430_interrupt)]` to the crate attributes to enable error[E0658]: PTX ABIs are experimental and subject to change - --> $DIR/feature-gate-abi.rs:20:8 + --> $DIR/feature-gate-abi.rs:19:8 | LL | extern "ptx-kernel" fn f6() {} | ^^^^^^^^^^^^ @@ -51,7 +51,7 @@ LL | extern "ptx-kernel" fn f6() {} = help: add `#![feature(abi_ptx)]` to the crate attributes to enable error[E0658]: x86-interrupt ABI is experimental and subject to change - --> $DIR/feature-gate-abi.rs:21:8 + --> $DIR/feature-gate-abi.rs:20:8 | LL | extern "x86-interrupt" fn f7() {} | ^^^^^^^^^^^^^^^ @@ -60,7 +60,7 @@ LL | extern "x86-interrupt" fn f7() {} = help: add `#![feature(abi_x86_interrupt)]` to the crate attributes to enable error[E0658]: thiscall is experimental and subject to change - --> $DIR/feature-gate-abi.rs:22:8 + --> $DIR/feature-gate-abi.rs:21:8 | LL | extern "thiscall" fn f8() {} | ^^^^^^^^^^ @@ -68,7 +68,7 @@ LL | extern "thiscall" fn f8() {} = help: add `#![feature(abi_thiscall)]` to the crate attributes to enable error[E0658]: amdgpu-kernel ABI is experimental and subject to change - --> $DIR/feature-gate-abi.rs:23:8 + --> $DIR/feature-gate-abi.rs:22:8 | LL | extern "amdgpu-kernel" fn f9() {} | ^^^^^^^^^^^^^^^ @@ -77,7 +77,7 @@ LL | extern "amdgpu-kernel" fn f9() {} = help: add `#![feature(abi_amdgpu_kernel)]` to the crate attributes to enable error[E0658]: efiapi ABI is experimental and subject to change - --> $DIR/feature-gate-abi.rs:24:8 + --> $DIR/feature-gate-abi.rs:23:8 | LL | extern "efiapi" fn f10() {} | ^^^^^^^^ @@ -85,6 +85,15 @@ LL | extern "efiapi" fn f10() {} = note: see issue #65815 <https://github.com/rust-lang/rust/issues/65815> for more information = help: add `#![feature(abi_efiapi)]` to the crate attributes to enable +error[E0658]: wasm ABI is experimental and subject to change + --> $DIR/feature-gate-abi.rs:24:8 + | +LL | extern "wasm" fn f11() {} + | ^^^^^^ + | + = note: see issue #83788 <https://github.com/rust-lang/rust/issues/83788> for more information + = help: add `#![feature(wasm_abi)]` to the crate attributes to enable + error[E0658]: intrinsics are subject to change --> $DIR/feature-gate-abi.rs:28:12 | @@ -172,8 +181,17 @@ LL | extern "efiapi" fn m10(); = note: see issue #65815 <https://github.com/rust-lang/rust/issues/65815> for more information = help: add `#![feature(abi_efiapi)]` to the crate attributes to enable +error[E0658]: wasm ABI is experimental and subject to change + --> $DIR/feature-gate-abi.rs:40:12 + | +LL | extern "wasm" fn m11() {} + | ^^^^^^ + | + = note: see issue #83788 <https://github.com/rust-lang/rust/issues/83788> for more information + = help: add `#![feature(wasm_abi)]` to the crate attributes to enable + error[E0658]: vectorcall is experimental and subject to change - --> $DIR/feature-gate-abi.rs:41:12 + --> $DIR/feature-gate-abi.rs:42:12 | LL | extern "vectorcall" fn dm3() {} | ^^^^^^^^^^^^ @@ -181,7 +199,7 @@ LL | extern "vectorcall" fn dm3() {} = help: add `#![feature(abi_vectorcall)]` to the crate attributes to enable error[E0658]: rust-call ABI is subject to change - --> $DIR/feature-gate-abi.rs:42:12 + --> $DIR/feature-gate-abi.rs:43:12 | LL | extern "rust-call" fn dm4(_: ()) {} | ^^^^^^^^^^^ @@ -190,7 +208,7 @@ LL | extern "rust-call" fn dm4(_: ()) {} = help: add `#![feature(unboxed_closures)]` to the crate attributes to enable error[E0658]: msp430-interrupt ABI is experimental and subject to change - --> $DIR/feature-gate-abi.rs:43:12 + --> $DIR/feature-gate-abi.rs:44:12 | LL | extern "msp430-interrupt" fn dm5() {} | ^^^^^^^^^^^^^^^^^^ @@ -199,7 +217,7 @@ LL | extern "msp430-interrupt" fn dm5() {} = help: add `#![feature(abi_msp430_interrupt)]` to the crate attributes to enable error[E0658]: PTX ABIs are experimental and subject to change - --> $DIR/feature-gate-abi.rs:44:12 + --> $DIR/feature-gate-abi.rs:45:12 | LL | extern "ptx-kernel" fn dm6() {} | ^^^^^^^^^^^^ @@ -208,7 +226,7 @@ LL | extern "ptx-kernel" fn dm6() {} = help: add `#![feature(abi_ptx)]` to the crate attributes to enable error[E0658]: x86-interrupt ABI is experimental and subject to change - --> $DIR/feature-gate-abi.rs:45:12 + --> $DIR/feature-gate-abi.rs:46:12 | LL | extern "x86-interrupt" fn dm7() {} | ^^^^^^^^^^^^^^^ @@ -217,7 +235,7 @@ LL | extern "x86-interrupt" fn dm7() {} = help: add `#![feature(abi_x86_interrupt)]` to the crate attributes to enable error[E0658]: thiscall is experimental and subject to change - --> $DIR/feature-gate-abi.rs:46:12 + --> $DIR/feature-gate-abi.rs:47:12 | LL | extern "thiscall" fn dm8() {} | ^^^^^^^^^^ @@ -225,7 +243,7 @@ LL | extern "thiscall" fn dm8() {} = help: add `#![feature(abi_thiscall)]` to the crate attributes to enable error[E0658]: amdgpu-kernel ABI is experimental and subject to change - --> $DIR/feature-gate-abi.rs:47:12 + --> $DIR/feature-gate-abi.rs:48:12 | LL | extern "amdgpu-kernel" fn dm9() {} | ^^^^^^^^^^^^^^^ @@ -234,7 +252,7 @@ LL | extern "amdgpu-kernel" fn dm9() {} = help: add `#![feature(abi_amdgpu_kernel)]` to the crate attributes to enable error[E0658]: efiapi ABI is experimental and subject to change - --> $DIR/feature-gate-abi.rs:48:12 + --> $DIR/feature-gate-abi.rs:49:12 | LL | extern "efiapi" fn dm10() {} | ^^^^^^^^ @@ -242,8 +260,17 @@ LL | extern "efiapi" fn dm10() {} = note: see issue #65815 <https://github.com/rust-lang/rust/issues/65815> for more information = help: add `#![feature(abi_efiapi)]` to the crate attributes to enable +error[E0658]: wasm ABI is experimental and subject to change + --> $DIR/feature-gate-abi.rs:50:12 + | +LL | extern "wasm" fn dm11() {} + | ^^^^^^ + | + = note: see issue #83788 <https://github.com/rust-lang/rust/issues/83788> for more information + = help: add `#![feature(wasm_abi)]` to the crate attributes to enable + error[E0658]: intrinsics are subject to change - --> $DIR/feature-gate-abi.rs:55:12 + --> $DIR/feature-gate-abi.rs:57:12 | LL | extern "rust-intrinsic" fn m1() {} | ^^^^^^^^^^^^^^^^ @@ -251,7 +278,7 @@ LL | extern "rust-intrinsic" fn m1() {} = help: add `#![feature(intrinsics)]` to the crate attributes to enable error[E0658]: platform intrinsics are experimental and possibly buggy - --> $DIR/feature-gate-abi.rs:57:12 + --> $DIR/feature-gate-abi.rs:59:12 | LL | extern "platform-intrinsic" fn m2() {} | ^^^^^^^^^^^^^^^^^^^^ @@ -260,7 +287,7 @@ LL | extern "platform-intrinsic" fn m2() {} = help: add `#![feature(platform_intrinsics)]` to the crate attributes to enable error[E0658]: vectorcall is experimental and subject to change - --> $DIR/feature-gate-abi.rs:59:12 + --> $DIR/feature-gate-abi.rs:61:12 | LL | extern "vectorcall" fn m3() {} | ^^^^^^^^^^^^ @@ -268,7 +295,7 @@ LL | extern "vectorcall" fn m3() {} = help: add `#![feature(abi_vectorcall)]` to the crate attributes to enable error[E0658]: rust-call ABI is subject to change - --> $DIR/feature-gate-abi.rs:60:12 + --> $DIR/feature-gate-abi.rs:62:12 | LL | extern "rust-call" fn m4(_: ()) {} | ^^^^^^^^^^^ @@ -277,7 +304,7 @@ LL | extern "rust-call" fn m4(_: ()) {} = help: add `#![feature(unboxed_closures)]` to the crate attributes to enable error[E0658]: msp430-interrupt ABI is experimental and subject to change - --> $DIR/feature-gate-abi.rs:61:12 + --> $DIR/feature-gate-abi.rs:63:12 | LL | extern "msp430-interrupt" fn m5() {} | ^^^^^^^^^^^^^^^^^^ @@ -286,7 +313,7 @@ LL | extern "msp430-interrupt" fn m5() {} = help: add `#![feature(abi_msp430_interrupt)]` to the crate attributes to enable error[E0658]: PTX ABIs are experimental and subject to change - --> $DIR/feature-gate-abi.rs:62:12 + --> $DIR/feature-gate-abi.rs:64:12 | LL | extern "ptx-kernel" fn m6() {} | ^^^^^^^^^^^^ @@ -295,7 +322,7 @@ LL | extern "ptx-kernel" fn m6() {} = help: add `#![feature(abi_ptx)]` to the crate attributes to enable error[E0658]: x86-interrupt ABI is experimental and subject to change - --> $DIR/feature-gate-abi.rs:63:12 + --> $DIR/feature-gate-abi.rs:65:12 | LL | extern "x86-interrupt" fn m7() {} | ^^^^^^^^^^^^^^^ @@ -304,7 +331,7 @@ LL | extern "x86-interrupt" fn m7() {} = help: add `#![feature(abi_x86_interrupt)]` to the crate attributes to enable error[E0658]: thiscall is experimental and subject to change - --> $DIR/feature-gate-abi.rs:64:12 + --> $DIR/feature-gate-abi.rs:66:12 | LL | extern "thiscall" fn m8() {} | ^^^^^^^^^^ @@ -312,7 +339,7 @@ LL | extern "thiscall" fn m8() {} = help: add `#![feature(abi_thiscall)]` to the crate attributes to enable error[E0658]: amdgpu-kernel ABI is experimental and subject to change - --> $DIR/feature-gate-abi.rs:65:12 + --> $DIR/feature-gate-abi.rs:67:12 | LL | extern "amdgpu-kernel" fn m9() {} | ^^^^^^^^^^^^^^^ @@ -321,7 +348,7 @@ LL | extern "amdgpu-kernel" fn m9() {} = help: add `#![feature(abi_amdgpu_kernel)]` to the crate attributes to enable error[E0658]: efiapi ABI is experimental and subject to change - --> $DIR/feature-gate-abi.rs:66:12 + --> $DIR/feature-gate-abi.rs:68:12 | LL | extern "efiapi" fn m10() {} | ^^^^^^^^ @@ -329,8 +356,17 @@ LL | extern "efiapi" fn m10() {} = note: see issue #65815 <https://github.com/rust-lang/rust/issues/65815> for more information = help: add `#![feature(abi_efiapi)]` to the crate attributes to enable +error[E0658]: wasm ABI is experimental and subject to change + --> $DIR/feature-gate-abi.rs:69:12 + | +LL | extern "wasm" fn m11() {} + | ^^^^^^ + | + = note: see issue #83788 <https://github.com/rust-lang/rust/issues/83788> for more information + = help: add `#![feature(wasm_abi)]` to the crate attributes to enable + error[E0658]: intrinsics are subject to change - --> $DIR/feature-gate-abi.rs:71:12 + --> $DIR/feature-gate-abi.rs:74:12 | LL | extern "rust-intrinsic" fn im1() {} | ^^^^^^^^^^^^^^^^ @@ -338,7 +374,7 @@ LL | extern "rust-intrinsic" fn im1() {} = help: add `#![feature(intrinsics)]` to the crate attributes to enable error[E0658]: platform intrinsics are experimental and possibly buggy - --> $DIR/feature-gate-abi.rs:73:12 + --> $DIR/feature-gate-abi.rs:76:12 | LL | extern "platform-intrinsic" fn im2() {} | ^^^^^^^^^^^^^^^^^^^^ @@ -347,7 +383,7 @@ LL | extern "platform-intrinsic" fn im2() {} = help: add `#![feature(platform_intrinsics)]` to the crate attributes to enable error[E0658]: vectorcall is experimental and subject to change - --> $DIR/feature-gate-abi.rs:75:12 + --> $DIR/feature-gate-abi.rs:78:12 | LL | extern "vectorcall" fn im3() {} | ^^^^^^^^^^^^ @@ -355,7 +391,7 @@ LL | extern "vectorcall" fn im3() {} = help: add `#![feature(abi_vectorcall)]` to the crate attributes to enable error[E0658]: rust-call ABI is subject to change - --> $DIR/feature-gate-abi.rs:76:12 + --> $DIR/feature-gate-abi.rs:79:12 | LL | extern "rust-call" fn im4(_: ()) {} | ^^^^^^^^^^^ @@ -364,7 +400,7 @@ LL | extern "rust-call" fn im4(_: ()) {} = help: add `#![feature(unboxed_closures)]` to the crate attributes to enable error[E0658]: msp430-interrupt ABI is experimental and subject to change - --> $DIR/feature-gate-abi.rs:77:12 + --> $DIR/feature-gate-abi.rs:80:12 | LL | extern "msp430-interrupt" fn im5() {} | ^^^^^^^^^^^^^^^^^^ @@ -373,7 +409,7 @@ LL | extern "msp430-interrupt" fn im5() {} = help: add `#![feature(abi_msp430_interrupt)]` to the crate attributes to enable error[E0658]: PTX ABIs are experimental and subject to change - --> $DIR/feature-gate-abi.rs:78:12 + --> $DIR/feature-gate-abi.rs:81:12 | LL | extern "ptx-kernel" fn im6() {} | ^^^^^^^^^^^^ @@ -382,7 +418,7 @@ LL | extern "ptx-kernel" fn im6() {} = help: add `#![feature(abi_ptx)]` to the crate attributes to enable error[E0658]: x86-interrupt ABI is experimental and subject to change - --> $DIR/feature-gate-abi.rs:79:12 + --> $DIR/feature-gate-abi.rs:82:12 | LL | extern "x86-interrupt" fn im7() {} | ^^^^^^^^^^^^^^^ @@ -391,7 +427,7 @@ LL | extern "x86-interrupt" fn im7() {} = help: add `#![feature(abi_x86_interrupt)]` to the crate attributes to enable error[E0658]: thiscall is experimental and subject to change - --> $DIR/feature-gate-abi.rs:80:12 + --> $DIR/feature-gate-abi.rs:83:12 | LL | extern "thiscall" fn im8() {} | ^^^^^^^^^^ @@ -399,7 +435,7 @@ LL | extern "thiscall" fn im8() {} = help: add `#![feature(abi_thiscall)]` to the crate attributes to enable error[E0658]: amdgpu-kernel ABI is experimental and subject to change - --> $DIR/feature-gate-abi.rs:81:12 + --> $DIR/feature-gate-abi.rs:84:12 | LL | extern "amdgpu-kernel" fn im9() {} | ^^^^^^^^^^^^^^^ @@ -408,7 +444,7 @@ LL | extern "amdgpu-kernel" fn im9() {} = help: add `#![feature(abi_amdgpu_kernel)]` to the crate attributes to enable error[E0658]: efiapi ABI is experimental and subject to change - --> $DIR/feature-gate-abi.rs:82:12 + --> $DIR/feature-gate-abi.rs:85:12 | LL | extern "efiapi" fn im10() {} | ^^^^^^^^ @@ -416,8 +452,17 @@ LL | extern "efiapi" fn im10() {} = note: see issue #65815 <https://github.com/rust-lang/rust/issues/65815> for more information = help: add `#![feature(abi_efiapi)]` to the crate attributes to enable +error[E0658]: wasm ABI is experimental and subject to change + --> $DIR/feature-gate-abi.rs:86:12 + | +LL | extern "wasm" fn im11() {} + | ^^^^^^ + | + = note: see issue #83788 <https://github.com/rust-lang/rust/issues/83788> for more information + = help: add `#![feature(wasm_abi)]` to the crate attributes to enable + error[E0658]: intrinsics are subject to change - --> $DIR/feature-gate-abi.rs:86:18 + --> $DIR/feature-gate-abi.rs:90:18 | LL | type A1 = extern "rust-intrinsic" fn(); | ^^^^^^^^^^^^^^^^ @@ -425,7 +470,7 @@ LL | type A1 = extern "rust-intrinsic" fn(); = help: add `#![feature(intrinsics)]` to the crate attributes to enable error[E0658]: platform intrinsics are experimental and possibly buggy - --> $DIR/feature-gate-abi.rs:87:18 + --> $DIR/feature-gate-abi.rs:91:18 | LL | type A2 = extern "platform-intrinsic" fn(); | ^^^^^^^^^^^^^^^^^^^^ @@ -434,7 +479,7 @@ LL | type A2 = extern "platform-intrinsic" fn(); = help: add `#![feature(platform_intrinsics)]` to the crate attributes to enable error[E0658]: vectorcall is experimental and subject to change - --> $DIR/feature-gate-abi.rs:88:18 + --> $DIR/feature-gate-abi.rs:92:18 | LL | type A3 = extern "vectorcall" fn(); | ^^^^^^^^^^^^ @@ -442,7 +487,7 @@ LL | type A3 = extern "vectorcall" fn(); = help: add `#![feature(abi_vectorcall)]` to the crate attributes to enable error[E0658]: rust-call ABI is subject to change - --> $DIR/feature-gate-abi.rs:89:18 + --> $DIR/feature-gate-abi.rs:93:18 | LL | type A4 = extern "rust-call" fn(_: ()); | ^^^^^^^^^^^ @@ -451,7 +496,7 @@ LL | type A4 = extern "rust-call" fn(_: ()); = help: add `#![feature(unboxed_closures)]` to the crate attributes to enable error[E0658]: msp430-interrupt ABI is experimental and subject to change - --> $DIR/feature-gate-abi.rs:90:18 + --> $DIR/feature-gate-abi.rs:94:18 | LL | type A5 = extern "msp430-interrupt" fn(); | ^^^^^^^^^^^^^^^^^^ @@ -460,16 +505,16 @@ LL | type A5 = extern "msp430-interrupt" fn(); = help: add `#![feature(abi_msp430_interrupt)]` to the crate attributes to enable error[E0658]: PTX ABIs are experimental and subject to change - --> $DIR/feature-gate-abi.rs:91:18 + --> $DIR/feature-gate-abi.rs:95:18 | -LL | type A6 = extern "ptx-kernel" fn (); +LL | type A6 = extern "ptx-kernel" fn(); | ^^^^^^^^^^^^ | = note: see issue #38788 <https://github.com/rust-lang/rust/issues/38788> for more information = help: add `#![feature(abi_ptx)]` to the crate attributes to enable error[E0658]: x86-interrupt ABI is experimental and subject to change - --> $DIR/feature-gate-abi.rs:92:18 + --> $DIR/feature-gate-abi.rs:96:18 | LL | type A7 = extern "x86-interrupt" fn(); | ^^^^^^^^^^^^^^^ @@ -478,7 +523,7 @@ LL | type A7 = extern "x86-interrupt" fn(); = help: add `#![feature(abi_x86_interrupt)]` to the crate attributes to enable error[E0658]: thiscall is experimental and subject to change - --> $DIR/feature-gate-abi.rs:93:18 + --> $DIR/feature-gate-abi.rs:97:18 | LL | type A8 = extern "thiscall" fn(); | ^^^^^^^^^^ @@ -486,7 +531,7 @@ LL | type A8 = extern "thiscall" fn(); = help: add `#![feature(abi_thiscall)]` to the crate attributes to enable error[E0658]: amdgpu-kernel ABI is experimental and subject to change - --> $DIR/feature-gate-abi.rs:94:18 + --> $DIR/feature-gate-abi.rs:98:18 | LL | type A9 = extern "amdgpu-kernel" fn(); | ^^^^^^^^^^^^^^^ @@ -495,7 +540,7 @@ LL | type A9 = extern "amdgpu-kernel" fn(); = help: add `#![feature(abi_amdgpu_kernel)]` to the crate attributes to enable error[E0658]: efiapi ABI is experimental and subject to change - --> $DIR/feature-gate-abi.rs:95:19 + --> $DIR/feature-gate-abi.rs:99:19 | LL | type A10 = extern "efiapi" fn(); | ^^^^^^^^ @@ -503,8 +548,17 @@ LL | type A10 = extern "efiapi" fn(); = note: see issue #65815 <https://github.com/rust-lang/rust/issues/65815> for more information = help: add `#![feature(abi_efiapi)]` to the crate attributes to enable +error[E0658]: wasm ABI is experimental and subject to change + --> $DIR/feature-gate-abi.rs:100:19 + | +LL | type A11 = extern "wasm" fn(); + | ^^^^^^ + | + = note: see issue #83788 <https://github.com/rust-lang/rust/issues/83788> for more information + = help: add `#![feature(wasm_abi)]` to the crate attributes to enable + error[E0658]: intrinsics are subject to change - --> $DIR/feature-gate-abi.rs:98:8 + --> $DIR/feature-gate-abi.rs:103:8 | LL | extern "rust-intrinsic" {} | ^^^^^^^^^^^^^^^^ @@ -512,7 +566,7 @@ LL | extern "rust-intrinsic" {} = help: add `#![feature(intrinsics)]` to the crate attributes to enable error[E0658]: platform intrinsics are experimental and possibly buggy - --> $DIR/feature-gate-abi.rs:99:8 + --> $DIR/feature-gate-abi.rs:104:8 | LL | extern "platform-intrinsic" {} | ^^^^^^^^^^^^^^^^^^^^ @@ -521,7 +575,7 @@ LL | extern "platform-intrinsic" {} = help: add `#![feature(platform_intrinsics)]` to the crate attributes to enable error[E0658]: vectorcall is experimental and subject to change - --> $DIR/feature-gate-abi.rs:100:8 + --> $DIR/feature-gate-abi.rs:105:8 | LL | extern "vectorcall" {} | ^^^^^^^^^^^^ @@ -529,7 +583,7 @@ LL | extern "vectorcall" {} = help: add `#![feature(abi_vectorcall)]` to the crate attributes to enable error[E0658]: rust-call ABI is subject to change - --> $DIR/feature-gate-abi.rs:101:8 + --> $DIR/feature-gate-abi.rs:106:8 | LL | extern "rust-call" {} | ^^^^^^^^^^^ @@ -538,7 +592,7 @@ LL | extern "rust-call" {} = help: add `#![feature(unboxed_closures)]` to the crate attributes to enable error[E0658]: msp430-interrupt ABI is experimental and subject to change - --> $DIR/feature-gate-abi.rs:102:8 + --> $DIR/feature-gate-abi.rs:107:8 | LL | extern "msp430-interrupt" {} | ^^^^^^^^^^^^^^^^^^ @@ -547,7 +601,7 @@ LL | extern "msp430-interrupt" {} = help: add `#![feature(abi_msp430_interrupt)]` to the crate attributes to enable error[E0658]: PTX ABIs are experimental and subject to change - --> $DIR/feature-gate-abi.rs:103:8 + --> $DIR/feature-gate-abi.rs:108:8 | LL | extern "ptx-kernel" {} | ^^^^^^^^^^^^ @@ -556,7 +610,7 @@ LL | extern "ptx-kernel" {} = help: add `#![feature(abi_ptx)]` to the crate attributes to enable error[E0658]: x86-interrupt ABI is experimental and subject to change - --> $DIR/feature-gate-abi.rs:104:8 + --> $DIR/feature-gate-abi.rs:109:8 | LL | extern "x86-interrupt" {} | ^^^^^^^^^^^^^^^ @@ -565,7 +619,7 @@ LL | extern "x86-interrupt" {} = help: add `#![feature(abi_x86_interrupt)]` to the crate attributes to enable error[E0658]: thiscall is experimental and subject to change - --> $DIR/feature-gate-abi.rs:105:8 + --> $DIR/feature-gate-abi.rs:110:8 | LL | extern "thiscall" {} | ^^^^^^^^^^ @@ -573,7 +627,7 @@ LL | extern "thiscall" {} = help: add `#![feature(abi_thiscall)]` to the crate attributes to enable error[E0658]: amdgpu-kernel ABI is experimental and subject to change - --> $DIR/feature-gate-abi.rs:106:8 + --> $DIR/feature-gate-abi.rs:111:8 | LL | extern "amdgpu-kernel" {} | ^^^^^^^^^^^^^^^ @@ -582,7 +636,7 @@ LL | extern "amdgpu-kernel" {} = help: add `#![feature(abi_amdgpu_kernel)]` to the crate attributes to enable error[E0658]: efiapi ABI is experimental and subject to change - --> $DIR/feature-gate-abi.rs:107:8 + --> $DIR/feature-gate-abi.rs:112:8 | LL | extern "efiapi" {} | ^^^^^^^^ @@ -590,6 +644,15 @@ LL | extern "efiapi" {} = note: see issue #65815 <https://github.com/rust-lang/rust/issues/65815> for more information = help: add `#![feature(abi_efiapi)]` to the crate attributes to enable +error[E0658]: wasm ABI is experimental and subject to change + --> $DIR/feature-gate-abi.rs:113:8 + | +LL | extern "wasm" {} + | ^^^^^^ + | + = note: see issue #83788 <https://github.com/rust-lang/rust/issues/83788> for more information + = help: add `#![feature(wasm_abi)]` to the crate attributes to enable + error: intrinsic must be in `extern "rust-intrinsic" { ... }` block --> $DIR/feature-gate-abi.rs:28:32 | @@ -603,41 +666,41 @@ LL | extern "platform-intrinsic" fn m2(); | ^^ error: intrinsic must be in `extern "rust-intrinsic" { ... }` block - --> $DIR/feature-gate-abi.rs:13:33 + --> $DIR/feature-gate-abi.rs:12:33 | LL | extern "rust-intrinsic" fn f1() {} | ^^ error: intrinsic must be in `extern "rust-intrinsic" { ... }` block - --> $DIR/feature-gate-abi.rs:15:37 + --> $DIR/feature-gate-abi.rs:14:37 | LL | extern "platform-intrinsic" fn f2() {} | ^^ error: intrinsic must be in `extern "rust-intrinsic" { ... }` block - --> $DIR/feature-gate-abi.rs:55:37 + --> $DIR/feature-gate-abi.rs:57:37 | LL | extern "rust-intrinsic" fn m1() {} | ^^ error: intrinsic must be in `extern "rust-intrinsic" { ... }` block - --> $DIR/feature-gate-abi.rs:57:41 + --> $DIR/feature-gate-abi.rs:59:41 | LL | extern "platform-intrinsic" fn m2() {} | ^^ error: intrinsic must be in `extern "rust-intrinsic" { ... }` block - --> $DIR/feature-gate-abi.rs:71:38 + --> $DIR/feature-gate-abi.rs:74:38 | LL | extern "rust-intrinsic" fn im1() {} | ^^ error: intrinsic must be in `extern "rust-intrinsic" { ... }` block - --> $DIR/feature-gate-abi.rs:73:42 + --> $DIR/feature-gate-abi.rs:76:42 | LL | extern "platform-intrinsic" fn im2() {} | ^^ -error: aborting due to 76 previous errors +error: aborting due to 83 previous errors For more information about this error, try `rustc --explain E0658`. diff --git a/src/test/ui/feature-gates/feature-gate-default_type_parameter_fallback.stderr b/src/test/ui/feature-gates/feature-gate-default_type_parameter_fallback.stderr index f13803b80f308..5cd38ebab370e 100644 --- a/src/test/ui/feature-gates/feature-gate-default_type_parameter_fallback.stderr +++ b/src/test/ui/feature-gates/feature-gate-default_type_parameter_fallback.stderr @@ -1,4 +1,4 @@ -error: defaults for type parameters are only allowed in `struct`, `enum`, `type`, or `trait` definitions. +error: defaults for type parameters are only allowed in `struct`, `enum`, `type`, or `trait` definitions --> $DIR/feature-gate-default_type_parameter_fallback.rs:3:8 | LL | fn avg<T=i32>(_: T) {} @@ -8,7 +8,7 @@ LL | fn avg<T=i32>(_: T) {} = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! = note: for more information, see issue #36887 <https://github.com/rust-lang/rust/issues/36887> -error: defaults for type parameters are only allowed in `struct`, `enum`, `type`, or `trait` definitions. +error: defaults for type parameters are only allowed in `struct`, `enum`, `type`, or `trait` definitions --> $DIR/feature-gate-default_type_parameter_fallback.rs:8:6 | LL | impl<T=i32> S<T> {} diff --git a/src/test/ui/feature-gates/feature-gate-doc_notable_trait.rs b/src/test/ui/feature-gates/feature-gate-doc_notable_trait.rs new file mode 100644 index 0000000000000..7f3392eadadb3 --- /dev/null +++ b/src/test/ui/feature-gates/feature-gate-doc_notable_trait.rs @@ -0,0 +1,4 @@ +#[doc(notable_trait)] //~ ERROR: `#[doc(notable_trait)]` is experimental +trait SomeTrait {} + +fn main() {} diff --git a/src/test/ui/feature-gates/feature-gate-doc_notable_trait.stderr b/src/test/ui/feature-gates/feature-gate-doc_notable_trait.stderr new file mode 100644 index 0000000000000..1f9bef40c4e73 --- /dev/null +++ b/src/test/ui/feature-gates/feature-gate-doc_notable_trait.stderr @@ -0,0 +1,12 @@ +error[E0658]: `#[doc(notable_trait)]` is experimental + --> $DIR/feature-gate-doc_notable_trait.rs:1:1 + | +LL | #[doc(notable_trait)] + | ^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #45040 <https://github.com/rust-lang/rust/issues/45040> for more information + = help: add `#![feature(doc_notable_trait)]` to the crate attributes to enable + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0658`. diff --git a/src/test/ui/feature-gates/feature-gate-doc_spotlight.rs b/src/test/ui/feature-gates/feature-gate-doc_spotlight.rs deleted file mode 100644 index 452b45b34456b..0000000000000 --- a/src/test/ui/feature-gates/feature-gate-doc_spotlight.rs +++ /dev/null @@ -1,4 +0,0 @@ -#[doc(spotlight)] //~ ERROR: `#[doc(spotlight)]` is experimental -trait SomeTrait {} - -fn main() {} diff --git a/src/test/ui/feature-gates/feature-gate-doc_spotlight.stderr b/src/test/ui/feature-gates/feature-gate-doc_spotlight.stderr deleted file mode 100644 index 010d74054a412..0000000000000 --- a/src/test/ui/feature-gates/feature-gate-doc_spotlight.stderr +++ /dev/null @@ -1,12 +0,0 @@ -error[E0658]: `#[doc(spotlight)]` is experimental - --> $DIR/feature-gate-doc_spotlight.rs:1:1 - | -LL | #[doc(spotlight)] - | ^^^^^^^^^^^^^^^^^ - | - = note: see issue #45040 <https://github.com/rust-lang/rust/issues/45040> for more information - = help: add `#![feature(doc_spotlight)]` to the crate attributes to enable - -error: aborting due to previous error - -For more information about this error, try `rustc --explain E0658`. diff --git a/src/test/ui/feature-gates/feature-gate-edition_macro_pats.rs b/src/test/ui/feature-gates/feature-gate-edition_macro_pats.rs index bd8a21ea36acd..430a9437cee34 100644 --- a/src/test/ui/feature-gates/feature-gate-edition_macro_pats.rs +++ b/src/test/ui/feature-gates/feature-gate-edition_macro_pats.rs @@ -1,8 +1,8 @@ // Feature gate test for `edition_macro_pats` feature. macro_rules! foo { - ($x:pat2018) => {}; //~ERROR `pat2018` and `pat2021` are unstable - ($x:pat2021) => {}; //~ERROR `pat2018` and `pat2021` are unstable + ($x:pat2015) => {}; //~ERROR `pat2015` and `pat2021` are unstable + ($x:pat2021) => {}; //~ERROR `pat2015` and `pat2021` are unstable } fn main() {} diff --git a/src/test/ui/feature-gates/feature-gate-edition_macro_pats.stderr b/src/test/ui/feature-gates/feature-gate-edition_macro_pats.stderr index 89bfb239d9eca..d25bcaf929bcd 100644 --- a/src/test/ui/feature-gates/feature-gate-edition_macro_pats.stderr +++ b/src/test/ui/feature-gates/feature-gate-edition_macro_pats.stderr @@ -1,13 +1,13 @@ -error[E0658]: `pat2018` and `pat2021` are unstable. +error[E0658]: `pat2015` and `pat2021` are unstable. --> $DIR/feature-gate-edition_macro_pats.rs:4:9 | -LL | ($x:pat2018) => {}; +LL | ($x:pat2015) => {}; | ^^^^^^^ | = note: see issue #54883 <https://github.com/rust-lang/rust/issues/54883> for more information = help: add `#![feature(edition_macro_pats)]` to the crate attributes to enable -error[E0658]: `pat2018` and `pat2021` are unstable. +error[E0658]: `pat2015` and `pat2021` are unstable. --> $DIR/feature-gate-edition_macro_pats.rs:5:9 | LL | ($x:pat2021) => {}; diff --git a/src/test/ui/feature-gates/feature-gate-fn_align.rs b/src/test/ui/feature-gates/feature-gate-fn_align.rs new file mode 100644 index 0000000000000..ea873dba269c4 --- /dev/null +++ b/src/test/ui/feature-gates/feature-gate-fn_align.rs @@ -0,0 +1,4 @@ +#![crate_type = "lib"] + +#[repr(align(16))] //~ ERROR `repr(align)` attributes on functions are unstable +fn requires_alignment() {} diff --git a/src/test/ui/feature-gates/feature-gate-fn_align.stderr b/src/test/ui/feature-gates/feature-gate-fn_align.stderr new file mode 100644 index 0000000000000..5ff124e48dca9 --- /dev/null +++ b/src/test/ui/feature-gates/feature-gate-fn_align.stderr @@ -0,0 +1,12 @@ +error[E0658]: `repr(align)` attributes on functions are unstable + --> $DIR/feature-gate-fn_align.rs:3:8 + | +LL | #[repr(align(16))] + | ^^^^^^^^^ + | + = note: see issue #82232 <https://github.com/rust-lang/rust/issues/82232> for more information + = help: add `#![feature(fn_align)]` to the crate attributes to enable + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0658`. diff --git a/src/test/ui/feature-gates/feature-gate-generic_associated_types.stderr b/src/test/ui/feature-gates/feature-gate-generic_associated_types.stderr index 266008cc0def4..4afbde5021f91 100644 --- a/src/test/ui/feature-gates/feature-gate-generic_associated_types.stderr +++ b/src/test/ui/feature-gates/feature-gate-generic_associated_types.stderr @@ -69,8 +69,8 @@ LL | type Pointer2<U32> = Box<U32>; | help: consider restricting type parameter `U32` | -LL | type Pointer2<U32: Clone> = Box<U32>; - | ^^^^^^^ +LL | type Pointer2<U32: std::clone::Clone> = Box<U32>; + | ^^^^^^^^^^^^^^^^^^^ error: aborting due to 8 previous errors diff --git a/src/test/ui/feature-gates/feature-gate-in_band_lifetimes.stderr b/src/test/ui/feature-gates/feature-gate-in_band_lifetimes.stderr index 0f0406b8e17d8..497b8a429e0ed 100644 --- a/src/test/ui/feature-gates/feature-gate-in_band_lifetimes.stderr +++ b/src/test/ui/feature-gates/feature-gate-in_band_lifetimes.stderr @@ -1,3 +1,87 @@ +error[E0261]: use of undeclared lifetime name `'a` + --> $DIR/feature-gate-in_band_lifetimes.rs:50:14 + | +LL | impl MyTrait<'a> for Y<&'a u8> { + | - ^^ undeclared lifetime + | | + | help: consider introducing lifetime `'a` here: `<'a>` + | + = help: if you want to experiment with in-band lifetime bindings, add `#![feature(in_band_lifetimes)]` to the crate attributes + +error[E0261]: use of undeclared lifetime name `'a` + --> $DIR/feature-gate-in_band_lifetimes.rs:50:25 + | +LL | impl MyTrait<'a> for Y<&'a u8> { + | - ^^ undeclared lifetime + | | + | help: consider introducing lifetime `'a` here: `<'a>` + | + = help: if you want to experiment with in-band lifetime bindings, add `#![feature(in_band_lifetimes)]` to the crate attributes + +error[E0261]: use of undeclared lifetime name `'a` + --> $DIR/feature-gate-in_band_lifetimes.rs:53:31 + | +LL | fn my_lifetime(&self) -> &'a u8 { self.0 } + | ^^ undeclared lifetime + | + = help: if you want to experiment with in-band lifetime bindings, add `#![feature(in_band_lifetimes)]` to the crate attributes +help: consider introducing lifetime `'a` here + | +LL | impl<'a> MyTrait<'a> for Y<&'a u8> { + | ^^^^ +help: consider introducing lifetime `'a` here + | +LL | fn my_lifetime<'a>(&self) -> &'a u8 { self.0 } + | ^^^^ + +error[E0261]: use of undeclared lifetime name `'b` + --> $DIR/feature-gate-in_band_lifetimes.rs:55:27 + | +LL | fn any_lifetime() -> &'b u8 { &0 } + | ^^ undeclared lifetime + | + = help: if you want to experiment with in-band lifetime bindings, add `#![feature(in_band_lifetimes)]` to the crate attributes +help: consider introducing lifetime `'b` here + | +LL | impl<'b> MyTrait<'a> for Y<&'a u8> { + | ^^^^ +help: consider introducing lifetime `'b` here + | +LL | fn any_lifetime<'b>() -> &'b u8 { &0 } + | ^^^^ + +error[E0261]: use of undeclared lifetime name `'b` + --> $DIR/feature-gate-in_band_lifetimes.rs:57:27 + | +LL | fn borrowed_lifetime(&'b self) -> &'b u8 { &*self.0 } + | ^^ undeclared lifetime + | + = help: if you want to experiment with in-band lifetime bindings, add `#![feature(in_band_lifetimes)]` to the crate attributes +help: consider introducing lifetime `'b` here + | +LL | impl<'b> MyTrait<'a> for Y<&'a u8> { + | ^^^^ +help: consider introducing lifetime `'b` here + | +LL | fn borrowed_lifetime<'b>(&'b self) -> &'b u8 { &*self.0 } + | ^^^^ + +error[E0261]: use of undeclared lifetime name `'b` + --> $DIR/feature-gate-in_band_lifetimes.rs:57:40 + | +LL | fn borrowed_lifetime(&'b self) -> &'b u8 { &*self.0 } + | ^^ undeclared lifetime + | + = help: if you want to experiment with in-band lifetime bindings, add `#![feature(in_band_lifetimes)]` to the crate attributes +help: consider introducing lifetime `'b` here + | +LL | impl<'b> MyTrait<'a> for Y<&'a u8> { + | ^^^^ +help: consider introducing lifetime `'b` here + | +LL | fn borrowed_lifetime<'b>(&'b self) -> &'b u8 { &*self.0 } + | ^^^^ + error[E0261]: use of undeclared lifetime name `'x` --> $DIR/feature-gate-in_band_lifetimes.rs:3:12 | @@ -142,90 +226,6 @@ help: consider introducing lifetime `'b` here LL | fn borrowed_lifetime<'b>(&'b self) -> &'b u8; | ^^^^ -error[E0261]: use of undeclared lifetime name `'a` - --> $DIR/feature-gate-in_band_lifetimes.rs:50:14 - | -LL | impl MyTrait<'a> for Y<&'a u8> { - | - ^^ undeclared lifetime - | | - | help: consider introducing lifetime `'a` here: `<'a>` - | - = help: if you want to experiment with in-band lifetime bindings, add `#![feature(in_band_lifetimes)]` to the crate attributes - -error[E0261]: use of undeclared lifetime name `'a` - --> $DIR/feature-gate-in_band_lifetimes.rs:50:25 - | -LL | impl MyTrait<'a> for Y<&'a u8> { - | - ^^ undeclared lifetime - | | - | help: consider introducing lifetime `'a` here: `<'a>` - | - = help: if you want to experiment with in-band lifetime bindings, add `#![feature(in_band_lifetimes)]` to the crate attributes - -error[E0261]: use of undeclared lifetime name `'a` - --> $DIR/feature-gate-in_band_lifetimes.rs:53:31 - | -LL | fn my_lifetime(&self) -> &'a u8 { self.0 } - | ^^ undeclared lifetime - | - = help: if you want to experiment with in-band lifetime bindings, add `#![feature(in_band_lifetimes)]` to the crate attributes -help: consider introducing lifetime `'a` here - | -LL | impl<'a> MyTrait<'a> for Y<&'a u8> { - | ^^^^ -help: consider introducing lifetime `'a` here - | -LL | fn my_lifetime<'a>(&self) -> &'a u8 { self.0 } - | ^^^^ - -error[E0261]: use of undeclared lifetime name `'b` - --> $DIR/feature-gate-in_band_lifetimes.rs:55:27 - | -LL | fn any_lifetime() -> &'b u8 { &0 } - | ^^ undeclared lifetime - | - = help: if you want to experiment with in-band lifetime bindings, add `#![feature(in_band_lifetimes)]` to the crate attributes -help: consider introducing lifetime `'b` here - | -LL | impl<'b> MyTrait<'a> for Y<&'a u8> { - | ^^^^ -help: consider introducing lifetime `'b` here - | -LL | fn any_lifetime<'b>() -> &'b u8 { &0 } - | ^^^^ - -error[E0261]: use of undeclared lifetime name `'b` - --> $DIR/feature-gate-in_band_lifetimes.rs:57:27 - | -LL | fn borrowed_lifetime(&'b self) -> &'b u8 { &*self.0 } - | ^^ undeclared lifetime - | - = help: if you want to experiment with in-band lifetime bindings, add `#![feature(in_band_lifetimes)]` to the crate attributes -help: consider introducing lifetime `'b` here - | -LL | impl<'b> MyTrait<'a> for Y<&'a u8> { - | ^^^^ -help: consider introducing lifetime `'b` here - | -LL | fn borrowed_lifetime<'b>(&'b self) -> &'b u8 { &*self.0 } - | ^^^^ - -error[E0261]: use of undeclared lifetime name `'b` - --> $DIR/feature-gate-in_band_lifetimes.rs:57:40 - | -LL | fn borrowed_lifetime(&'b self) -> &'b u8 { &*self.0 } - | ^^ undeclared lifetime - | - = help: if you want to experiment with in-band lifetime bindings, add `#![feature(in_band_lifetimes)]` to the crate attributes -help: consider introducing lifetime `'b` here - | -LL | impl<'b> MyTrait<'a> for Y<&'a u8> { - | ^^^^ -help: consider introducing lifetime `'b` here - | -LL | fn borrowed_lifetime<'b>(&'b self) -> &'b u8 { &*self.0 } - | ^^^^ - error: aborting due to 17 previous errors For more information about this error, try `rustc --explain E0261`. diff --git a/src/test/ui/feature-gates/feature-gate-link_args.rs b/src/test/ui/feature-gates/feature-gate-link_args.rs deleted file mode 100644 index e1c651f46fb49..0000000000000 --- a/src/test/ui/feature-gates/feature-gate-link_args.rs +++ /dev/null @@ -1,17 +0,0 @@ -// Test that `#[link_args]` attribute is gated by `link_args` -// feature gate, both when it occurs where expected (atop -// `extern { }` blocks) and where unexpected. - -// sidestep warning (which is correct, but misleading for -// purposes of this test) -#![allow(unused_attributes)] -#![link_args = "-l unexpected_use_as_inner_attr_on_mod"] -//~^ ERROR the `link_args` attribute is experimental - -#[link_args = "-l expected_use_case"] -//~^ ERROR the `link_args` attribute is experimental -extern "C" {} - -#[link_args = "-l unexected_use_on_non_extern_item"] -//~^ ERROR: the `link_args` attribute is experimental -fn main() {} diff --git a/src/test/ui/feature-gates/feature-gate-link_args.stderr b/src/test/ui/feature-gates/feature-gate-link_args.stderr deleted file mode 100644 index ae4918f5c9f54..0000000000000 --- a/src/test/ui/feature-gates/feature-gate-link_args.stderr +++ /dev/null @@ -1,30 +0,0 @@ -error[E0658]: the `link_args` attribute is experimental and not portable across platforms, it is recommended to use `#[link(name = "foo")] instead - --> $DIR/feature-gate-link_args.rs:11:1 - | -LL | #[link_args = "-l expected_use_case"] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: see issue #29596 <https://github.com/rust-lang/rust/issues/29596> for more information - = help: add `#![feature(link_args)]` to the crate attributes to enable - -error[E0658]: the `link_args` attribute is experimental and not portable across platforms, it is recommended to use `#[link(name = "foo")] instead - --> $DIR/feature-gate-link_args.rs:15:1 - | -LL | #[link_args = "-l unexected_use_on_non_extern_item"] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: see issue #29596 <https://github.com/rust-lang/rust/issues/29596> for more information - = help: add `#![feature(link_args)]` to the crate attributes to enable - -error[E0658]: the `link_args` attribute is experimental and not portable across platforms, it is recommended to use `#[link(name = "foo")] instead - --> $DIR/feature-gate-link_args.rs:8:1 - | -LL | #![link_args = "-l unexpected_use_as_inner_attr_on_mod"] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: see issue #29596 <https://github.com/rust-lang/rust/issues/29596> for more information - = help: add `#![feature(link_args)]` to the crate attributes to enable - -error: aborting due to 3 previous errors - -For more information about this error, try `rustc --explain E0658`. diff --git a/src/test/ui/feature-gates/feature-gate-main.rs b/src/test/ui/feature-gates/feature-gate-main.rs deleted file mode 100644 index 9c304a1792fc2..0000000000000 --- a/src/test/ui/feature-gates/feature-gate-main.rs +++ /dev/null @@ -1,2 +0,0 @@ -#[main] -fn foo() {} //~ ERROR: declaration of a non-standard `#[main]` function may change over time diff --git a/src/test/ui/feature-gates/feature-gate-main.stderr b/src/test/ui/feature-gates/feature-gate-main.stderr deleted file mode 100644 index f0ef3b38c6209..0000000000000 --- a/src/test/ui/feature-gates/feature-gate-main.stderr +++ /dev/null @@ -1,12 +0,0 @@ -error[E0658]: declaration of a non-standard `#[main]` function may change over time, for now a top-level `fn main()` is required - --> $DIR/feature-gate-main.rs:2:1 - | -LL | fn foo() {} - | ^^^^^^^^^^^ - | - = note: see issue #29634 <https://github.com/rust-lang/rust/issues/29634> for more information - = help: add `#![feature(main)]` to the crate attributes to enable - -error: aborting due to previous error - -For more information about this error, try `rustc --explain E0658`. diff --git a/src/test/ui/feature-gates/feature-gate-min_type_alias_impl_trait.rs b/src/test/ui/feature-gates/feature-gate-min_type_alias_impl_trait.rs new file mode 100644 index 0000000000000..ffe297a5a6a2f --- /dev/null +++ b/src/test/ui/feature-gates/feature-gate-min_type_alias_impl_trait.rs @@ -0,0 +1,50 @@ +// ignore-compare-mode-chalk +use std::fmt::Debug; + +type Foo = impl Debug; //~ ERROR `impl Trait` in type aliases is unstable + +trait Bar { + type Baa: Debug; + fn define() -> Self::Baa; +} + +impl Bar for () { + type Baa = impl Debug; //~ ERROR `impl Trait` in type aliases is unstable + fn define() -> Self::Baa { + 0 + } +} + +fn define() -> Foo { + 0 +} + +trait TraitWithDefault { + type Assoc = impl Debug; + //~^ ERROR associated type defaults are unstable + //~| ERROR `impl Trait` not allowed outside of function + //~| ERROR `impl Trait` in type aliases is unstable +} + +type NestedFree = (Vec<impl Debug>, impl Debug, impl Iterator<Item = impl Debug>); +//~^ ERROR `impl Trait` in type aliases is unstable +//~| ERROR `impl Trait` in type aliases is unstable +//~| ERROR `impl Trait` in type aliases is unstable +//~| ERROR `impl Trait` in type aliases is unstable + +fn define_multiple() -> NestedFree { + (vec![true], 0u8, 0i32..1) +} + +impl Bar for u8 { + type Baa = (Vec<impl Debug>, impl Debug, impl Iterator<Item = impl Debug> + Debug); + //~^ ERROR `impl Trait` in type aliases is unstable + //~| ERROR `impl Trait` in type aliases is unstable + //~| ERROR `impl Trait` in type aliases is unstable + //~| ERROR `impl Trait` in type aliases is unstable + fn define() -> Self::Baa { + (vec![true], 0u8, 0i32..1) + } +} + +fn main() {} diff --git a/src/test/ui/feature-gates/feature-gate-min_type_alias_impl_trait.stderr b/src/test/ui/feature-gates/feature-gate-min_type_alias_impl_trait.stderr new file mode 100644 index 0000000000000..b82867c67025a --- /dev/null +++ b/src/test/ui/feature-gates/feature-gate-min_type_alias_impl_trait.stderr @@ -0,0 +1,118 @@ +error[E0658]: `impl Trait` in type aliases is unstable + --> $DIR/feature-gate-min_type_alias_impl_trait.rs:4:12 + | +LL | type Foo = impl Debug; + | ^^^^^^^^^^ + | + = note: see issue #63063 <https://github.com/rust-lang/rust/issues/63063> for more information + = help: add `#![feature(min_type_alias_impl_trait)]` to the crate attributes to enable + +error[E0658]: `impl Trait` in type aliases is unstable + --> $DIR/feature-gate-min_type_alias_impl_trait.rs:12:16 + | +LL | type Baa = impl Debug; + | ^^^^^^^^^^ + | + = note: see issue #63063 <https://github.com/rust-lang/rust/issues/63063> for more information + = help: add `#![feature(min_type_alias_impl_trait)]` to the crate attributes to enable + +error[E0658]: associated type defaults are unstable + --> $DIR/feature-gate-min_type_alias_impl_trait.rs:23:5 + | +LL | type Assoc = impl Debug; + | ^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #29661 <https://github.com/rust-lang/rust/issues/29661> for more information + = help: add `#![feature(associated_type_defaults)]` to the crate attributes to enable + +error[E0658]: `impl Trait` in type aliases is unstable + --> $DIR/feature-gate-min_type_alias_impl_trait.rs:23:18 + | +LL | type Assoc = impl Debug; + | ^^^^^^^^^^ + | + = note: see issue #63063 <https://github.com/rust-lang/rust/issues/63063> for more information + = help: add `#![feature(min_type_alias_impl_trait)]` to the crate attributes to enable + +error[E0658]: `impl Trait` in type aliases is unstable + --> $DIR/feature-gate-min_type_alias_impl_trait.rs:29:24 + | +LL | type NestedFree = (Vec<impl Debug>, impl Debug, impl Iterator<Item = impl Debug>); + | ^^^^^^^^^^ + | + = note: see issue #63063 <https://github.com/rust-lang/rust/issues/63063> for more information + = help: add `#![feature(min_type_alias_impl_trait)]` to the crate attributes to enable + +error[E0658]: `impl Trait` in type aliases is unstable + --> $DIR/feature-gate-min_type_alias_impl_trait.rs:29:37 + | +LL | type NestedFree = (Vec<impl Debug>, impl Debug, impl Iterator<Item = impl Debug>); + | ^^^^^^^^^^ + | + = note: see issue #63063 <https://github.com/rust-lang/rust/issues/63063> for more information + = help: add `#![feature(min_type_alias_impl_trait)]` to the crate attributes to enable + +error[E0658]: `impl Trait` in type aliases is unstable + --> $DIR/feature-gate-min_type_alias_impl_trait.rs:29:49 + | +LL | type NestedFree = (Vec<impl Debug>, impl Debug, impl Iterator<Item = impl Debug>); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #63063 <https://github.com/rust-lang/rust/issues/63063> for more information + = help: add `#![feature(min_type_alias_impl_trait)]` to the crate attributes to enable + +error[E0658]: `impl Trait` in type aliases is unstable + --> $DIR/feature-gate-min_type_alias_impl_trait.rs:29:70 + | +LL | type NestedFree = (Vec<impl Debug>, impl Debug, impl Iterator<Item = impl Debug>); + | ^^^^^^^^^^ + | + = note: see issue #63063 <https://github.com/rust-lang/rust/issues/63063> for more information + = help: add `#![feature(min_type_alias_impl_trait)]` to the crate attributes to enable + +error[E0658]: `impl Trait` in type aliases is unstable + --> $DIR/feature-gate-min_type_alias_impl_trait.rs:40:21 + | +LL | type Baa = (Vec<impl Debug>, impl Debug, impl Iterator<Item = impl Debug> + Debug); + | ^^^^^^^^^^ + | + = note: see issue #63063 <https://github.com/rust-lang/rust/issues/63063> for more information + = help: add `#![feature(min_type_alias_impl_trait)]` to the crate attributes to enable + +error[E0658]: `impl Trait` in type aliases is unstable + --> $DIR/feature-gate-min_type_alias_impl_trait.rs:40:34 + | +LL | type Baa = (Vec<impl Debug>, impl Debug, impl Iterator<Item = impl Debug> + Debug); + | ^^^^^^^^^^ + | + = note: see issue #63063 <https://github.com/rust-lang/rust/issues/63063> for more information + = help: add `#![feature(min_type_alias_impl_trait)]` to the crate attributes to enable + +error[E0658]: `impl Trait` in type aliases is unstable + --> $DIR/feature-gate-min_type_alias_impl_trait.rs:40:46 + | +LL | type Baa = (Vec<impl Debug>, impl Debug, impl Iterator<Item = impl Debug> + Debug); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #63063 <https://github.com/rust-lang/rust/issues/63063> for more information + = help: add `#![feature(min_type_alias_impl_trait)]` to the crate attributes to enable + +error[E0658]: `impl Trait` in type aliases is unstable + --> $DIR/feature-gate-min_type_alias_impl_trait.rs:40:67 + | +LL | type Baa = (Vec<impl Debug>, impl Debug, impl Iterator<Item = impl Debug> + Debug); + | ^^^^^^^^^^ + | + = note: see issue #63063 <https://github.com/rust-lang/rust/issues/63063> for more information + = help: add `#![feature(min_type_alias_impl_trait)]` to the crate attributes to enable + +error[E0562]: `impl Trait` not allowed outside of function and inherent method return types + --> $DIR/feature-gate-min_type_alias_impl_trait.rs:23:18 + | +LL | type Assoc = impl Debug; + | ^^^^^^^^^^ + +error: aborting due to 13 previous errors + +Some errors have detailed explanations: E0562, E0658. +For more information about an error, try `rustc --explain E0562`. diff --git a/src/test/ui/feature-gates/feature-gate-naked_functions.rs b/src/test/ui/feature-gates/feature-gate-naked_functions.rs index 06bddc422cf80..71ca5b9373a68 100644 --- a/src/test/ui/feature-gates/feature-gate-naked_functions.rs +++ b/src/test/ui/feature-gates/feature-gate-naked_functions.rs @@ -1,3 +1,4 @@ +// needs-asm-support #![feature(asm)] #[naked] diff --git a/src/test/ui/feature-gates/feature-gate-naked_functions.stderr b/src/test/ui/feature-gates/feature-gate-naked_functions.stderr index d95561d20133e..653d7b738da1a 100644 --- a/src/test/ui/feature-gates/feature-gate-naked_functions.stderr +++ b/src/test/ui/feature-gates/feature-gate-naked_functions.stderr @@ -1,5 +1,5 @@ error[E0658]: the `#[naked]` attribute is an experimental feature - --> $DIR/feature-gate-naked_functions.rs:3:1 + --> $DIR/feature-gate-naked_functions.rs:4:1 | LL | #[naked] | ^^^^^^^^ @@ -8,7 +8,7 @@ LL | #[naked] = help: add `#![feature(naked_functions)]` to the crate attributes to enable error[E0658]: the `#[naked]` attribute is an experimental feature - --> $DIR/feature-gate-naked_functions.rs:9:1 + --> $DIR/feature-gate-naked_functions.rs:10:1 | LL | #[naked] | ^^^^^^^^ diff --git a/src/test/ui/feature-gates/feature-gate-non_ascii_idents.rs b/src/test/ui/feature-gates/feature-gate-non_ascii_idents.rs deleted file mode 100644 index 5cc04ad5cdfdc..0000000000000 --- a/src/test/ui/feature-gates/feature-gate-non_ascii_idents.rs +++ /dev/null @@ -1,33 +0,0 @@ -extern crate core as bäz; //~ ERROR non-ascii idents - -use föö::bar; //~ ERROR non-ascii idents - -mod föö { //~ ERROR non-ascii idents - pub fn bar() {} -} - -fn bär( //~ ERROR non-ascii idents - bäz: isize //~ ERROR non-ascii idents - ) { - let _ö: isize; //~ ERROR non-ascii idents - - match (1, 2) { - (_ä, _) => {} //~ ERROR non-ascii idents - } -} - -struct Föö { //~ ERROR non-ascii idents - föö: isize //~ ERROR non-ascii idents -} - -enum Bär { //~ ERROR non-ascii idents - Bäz { //~ ERROR non-ascii idents - qüx: isize //~ ERROR non-ascii idents - } -} - -extern "C" { - fn qüx(); //~ ERROR non-ascii idents -} - -fn main() {} diff --git a/src/test/ui/feature-gates/feature-gate-non_ascii_idents.stderr b/src/test/ui/feature-gates/feature-gate-non_ascii_idents.stderr deleted file mode 100644 index e9392ace4ce25..0000000000000 --- a/src/test/ui/feature-gates/feature-gate-non_ascii_idents.stderr +++ /dev/null @@ -1,120 +0,0 @@ -error[E0658]: non-ascii idents are not fully supported - --> $DIR/feature-gate-non_ascii_idents.rs:1:22 - | -LL | extern crate core as bäz; - | ^^^ - | - = note: see issue #55467 <https://github.com/rust-lang/rust/issues/55467> for more information - = help: add `#![feature(non_ascii_idents)]` to the crate attributes to enable - -error[E0658]: non-ascii idents are not fully supported - --> $DIR/feature-gate-non_ascii_idents.rs:3:5 - | -LL | use föö::bar; - | ^^^ - | - = note: see issue #55467 <https://github.com/rust-lang/rust/issues/55467> for more information - = help: add `#![feature(non_ascii_idents)]` to the crate attributes to enable - -error[E0658]: non-ascii idents are not fully supported - --> $DIR/feature-gate-non_ascii_idents.rs:5:5 - | -LL | mod föö { - | ^^^ - | - = note: see issue #55467 <https://github.com/rust-lang/rust/issues/55467> for more information - = help: add `#![feature(non_ascii_idents)]` to the crate attributes to enable - -error[E0658]: non-ascii idents are not fully supported - --> $DIR/feature-gate-non_ascii_idents.rs:9:4 - | -LL | fn bär( - | ^^^ - | - = note: see issue #55467 <https://github.com/rust-lang/rust/issues/55467> for more information - = help: add `#![feature(non_ascii_idents)]` to the crate attributes to enable - -error[E0658]: non-ascii idents are not fully supported - --> $DIR/feature-gate-non_ascii_idents.rs:10:5 - | -LL | bäz: isize - | ^^^ - | - = note: see issue #55467 <https://github.com/rust-lang/rust/issues/55467> for more information - = help: add `#![feature(non_ascii_idents)]` to the crate attributes to enable - -error[E0658]: non-ascii idents are not fully supported - --> $DIR/feature-gate-non_ascii_idents.rs:12:9 - | -LL | let _ö: isize; - | ^^ - | - = note: see issue #55467 <https://github.com/rust-lang/rust/issues/55467> for more information - = help: add `#![feature(non_ascii_idents)]` to the crate attributes to enable - -error[E0658]: non-ascii idents are not fully supported - --> $DIR/feature-gate-non_ascii_idents.rs:15:10 - | -LL | (_ä, _) => {} - | ^^ - | - = note: see issue #55467 <https://github.com/rust-lang/rust/issues/55467> for more information - = help: add `#![feature(non_ascii_idents)]` to the crate attributes to enable - -error[E0658]: non-ascii idents are not fully supported - --> $DIR/feature-gate-non_ascii_idents.rs:19:8 - | -LL | struct Föö { - | ^^^ - | - = note: see issue #55467 <https://github.com/rust-lang/rust/issues/55467> for more information - = help: add `#![feature(non_ascii_idents)]` to the crate attributes to enable - -error[E0658]: non-ascii idents are not fully supported - --> $DIR/feature-gate-non_ascii_idents.rs:20:5 - | -LL | föö: isize - | ^^^ - | - = note: see issue #55467 <https://github.com/rust-lang/rust/issues/55467> for more information - = help: add `#![feature(non_ascii_idents)]` to the crate attributes to enable - -error[E0658]: non-ascii idents are not fully supported - --> $DIR/feature-gate-non_ascii_idents.rs:23:6 - | -LL | enum Bär { - | ^^^ - | - = note: see issue #55467 <https://github.com/rust-lang/rust/issues/55467> for more information - = help: add `#![feature(non_ascii_idents)]` to the crate attributes to enable - -error[E0658]: non-ascii idents are not fully supported - --> $DIR/feature-gate-non_ascii_idents.rs:24:5 - | -LL | Bäz { - | ^^^ - | - = note: see issue #55467 <https://github.com/rust-lang/rust/issues/55467> for more information - = help: add `#![feature(non_ascii_idents)]` to the crate attributes to enable - -error[E0658]: non-ascii idents are not fully supported - --> $DIR/feature-gate-non_ascii_idents.rs:25:9 - | -LL | qüx: isize - | ^^^ - | - = note: see issue #55467 <https://github.com/rust-lang/rust/issues/55467> for more information - = help: add `#![feature(non_ascii_idents)]` to the crate attributes to enable - -error[E0658]: non-ascii idents are not fully supported - --> $DIR/feature-gate-non_ascii_idents.rs:30:8 - | -LL | fn qüx(); - | ^^^ - | - = note: see issue #55467 <https://github.com/rust-lang/rust/issues/55467> for more information - = help: add `#![feature(non_ascii_idents)]` to the crate attributes to enable - -error: aborting due to 13 previous errors - -For more information about this error, try `rustc --explain E0658`. diff --git a/src/test/ui/feature-gates/feature-gate-object_safe_for_dispatch.rs b/src/test/ui/feature-gates/feature-gate-object_safe_for_dispatch.rs index 8945360b7be6d..37348e476d408 100644 --- a/src/test/ui/feature-gates/feature-gate-object_safe_for_dispatch.rs +++ b/src/test/ui/feature-gates/feature-gate-object_safe_for_dispatch.rs @@ -12,7 +12,7 @@ trait NonObjectSafe3 { } trait NonObjectSafe4 { - fn foo(&self, &Self); + fn foo(&self, s: &Self); } fn takes_non_object_safe_ref<T>(obj: &dyn NonObjectSafe1) { diff --git a/src/test/ui/feature-gates/feature-gate-object_safe_for_dispatch.stderr b/src/test/ui/feature-gates/feature-gate-object_safe_for_dispatch.stderr index b61d560445582..12195bc1071a0 100644 --- a/src/test/ui/feature-gates/feature-gate-object_safe_for_dispatch.stderr +++ b/src/test/ui/feature-gates/feature-gate-object_safe_for_dispatch.stderr @@ -57,12 +57,12 @@ LL | fn return_non_object_safe_rc() -> std::rc::Rc<dyn NonObjectSafe4> { | = help: consider moving `foo` to another trait note: for a trait to be "object safe" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit <https://doc.rust-lang.org/reference/items/traits.html#object-safety> - --> $DIR/feature-gate-object_safe_for_dispatch.rs:15:19 + --> $DIR/feature-gate-object_safe_for_dispatch.rs:15:22 | LL | trait NonObjectSafe4 { | -------------- this trait cannot be made into an object... -LL | fn foo(&self, &Self); - | ^^^^^ ...because method `foo` references the `Self` type in this parameter +LL | fn foo(&self, s: &Self); + | ^^^^^ ...because method `foo` references the `Self` type in this parameter error[E0038]: the trait `NonObjectSafe1` cannot be made into an object --> $DIR/feature-gate-object_safe_for_dispatch.rs:38:16 diff --git a/src/test/ui/feature-gates/feature-gate-overlapping_marker_traits.stderr b/src/test/ui/feature-gates/feature-gate-overlapping_marker_traits.stderr index 5ce4c5cccb31e..0526c6dc83980 100644 --- a/src/test/ui/feature-gates/feature-gate-overlapping_marker_traits.stderr +++ b/src/test/ui/feature-gates/feature-gate-overlapping_marker_traits.stderr @@ -1,4 +1,4 @@ -error[E0119]: conflicting implementations of trait `MyMarker`: +error[E0119]: conflicting implementations of trait `MyMarker` --> $DIR/feature-gate-overlapping_marker_traits.rs:6:1 | LL | impl<T: Display> MyMarker for T {} diff --git a/src/test/ui/feature-gates/feature-gate-rustc-attrs-1.rs b/src/test/ui/feature-gates/feature-gate-rustc-attrs-1.rs index 9f5c92349e06f..04f816ea50164 100644 --- a/src/test/ui/feature-gates/feature-gate-rustc-attrs-1.rs +++ b/src/test/ui/feature-gates/feature-gate-rustc-attrs-1.rs @@ -1,5 +1,3 @@ -// ignore-tidy-linelength - // Test that `#[rustc_*]` attributes are gated by `rustc_attrs` feature gate. #[rustc_variance] //~ ERROR the `#[rustc_variance]` attribute is just used for rustc unit tests and will never be stable diff --git a/src/test/ui/feature-gates/feature-gate-rustc-attrs-1.stderr b/src/test/ui/feature-gates/feature-gate-rustc-attrs-1.stderr index 82dec1fd4cf21..822368a5946ec 100644 --- a/src/test/ui/feature-gates/feature-gate-rustc-attrs-1.stderr +++ b/src/test/ui/feature-gates/feature-gate-rustc-attrs-1.stderr @@ -1,5 +1,5 @@ error[E0658]: the `#[rustc_variance]` attribute is just used for rustc unit tests and will never be stable - --> $DIR/feature-gate-rustc-attrs-1.rs:5:1 + --> $DIR/feature-gate-rustc-attrs-1.rs:3:1 | LL | #[rustc_variance] | ^^^^^^^^^^^^^^^^^ @@ -7,7 +7,7 @@ LL | #[rustc_variance] = help: add `#![feature(rustc_attrs)]` to the crate attributes to enable error[E0658]: the `#[rustc_error]` attribute is just used for rustc unit tests and will never be stable - --> $DIR/feature-gate-rustc-attrs-1.rs:6:1 + --> $DIR/feature-gate-rustc-attrs-1.rs:4:1 | LL | #[rustc_error] | ^^^^^^^^^^^^^^ @@ -15,7 +15,7 @@ LL | #[rustc_error] = help: add `#![feature(rustc_attrs)]` to the crate attributes to enable error[E0658]: the `#[rustc_nonnull_optimization_guaranteed]` attribute is just used to enable niche optimizations in libcore and will never be stable - --> $DIR/feature-gate-rustc-attrs-1.rs:7:1 + --> $DIR/feature-gate-rustc-attrs-1.rs:5:1 | LL | #[rustc_nonnull_optimization_guaranteed] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/src/test/ui/feature-gates/feature-gate-trivial_bounds.stderr b/src/test/ui/feature-gates/feature-gate-trivial_bounds.stderr index f3fa641209563..78904b383f49a 100644 --- a/src/test/ui/feature-gates/feature-gate-trivial_bounds.stderr +++ b/src/test/ui/feature-gates/feature-gate-trivial_bounds.stderr @@ -107,7 +107,11 @@ LL | | } | |_^ doesn't have a size known at compile-time | = help: within `Dst<(dyn A + 'static)>`, the trait `Sized` is not implemented for `(dyn A + 'static)` - = note: required because it appears within the type `Dst<(dyn A + 'static)>` +note: required because it appears within the type `Dst<(dyn A + 'static)>` + --> $DIR/feature-gate-trivial_bounds.rs:48:8 + | +LL | struct Dst<X: ?Sized> { + | ^^^ = help: see issue #48214 = help: add `#![feature(trivial_bounds)]` to the crate attributes to enable diff --git a/src/test/ui/feature-gates/feature-gate-type_alias_impl_trait.rs b/src/test/ui/feature-gates/feature-gate-type_alias_impl_trait.rs index ffe297a5a6a2f..dfd82a25f4c87 100644 --- a/src/test/ui/feature-gates/feature-gate-type_alias_impl_trait.rs +++ b/src/test/ui/feature-gates/feature-gate-type_alias_impl_trait.rs @@ -1,50 +1,37 @@ // ignore-compare-mode-chalk +#![feature(min_type_alias_impl_trait)] use std::fmt::Debug; -type Foo = impl Debug; //~ ERROR `impl Trait` in type aliases is unstable +type Foo = impl Debug; +//~^ ERROR could not find defining uses -trait Bar { - type Baa: Debug; - fn define() -> Self::Baa; +struct Bar(Foo); +fn define() -> Bar { + Bar(42) //~ ERROR mismatched types } -impl Bar for () { - type Baa = impl Debug; //~ ERROR `impl Trait` in type aliases is unstable - fn define() -> Self::Baa { - 0 - } -} - -fn define() -> Foo { - 0 -} +type Foo2 = impl Debug; -trait TraitWithDefault { - type Assoc = impl Debug; - //~^ ERROR associated type defaults are unstable - //~| ERROR `impl Trait` not allowed outside of function - //~| ERROR `impl Trait` in type aliases is unstable +fn define2() { + let x = || -> Foo2 { 42 }; //~ ERROR not permitted here } -type NestedFree = (Vec<impl Debug>, impl Debug, impl Iterator<Item = impl Debug>); -//~^ ERROR `impl Trait` in type aliases is unstable -//~| ERROR `impl Trait` in type aliases is unstable -//~| ERROR `impl Trait` in type aliases is unstable -//~| ERROR `impl Trait` in type aliases is unstable +type Foo3 = impl Debug; +//~^ ERROR could not find defining uses -fn define_multiple() -> NestedFree { - (vec![true], 0u8, 0i32..1) +fn define3(x: Foo3) { + let y: i32 = x; //~ ERROR mismatched types } +fn define3_1() { + define3(42) //~ ERROR mismatched types +} + +type Foo4 = impl Debug; +//~^ ERROR could not find defining uses -impl Bar for u8 { - type Baa = (Vec<impl Debug>, impl Debug, impl Iterator<Item = impl Debug> + Debug); - //~^ ERROR `impl Trait` in type aliases is unstable - //~| ERROR `impl Trait` in type aliases is unstable - //~| ERROR `impl Trait` in type aliases is unstable - //~| ERROR `impl Trait` in type aliases is unstable - fn define() -> Self::Baa { - (vec![true], 0u8, 0i32..1) - } +fn define4() { + let y: Foo4 = 42; + //~^ ERROR not permitted here } fn main() {} diff --git a/src/test/ui/feature-gates/feature-gate-type_alias_impl_trait.stderr b/src/test/ui/feature-gates/feature-gate-type_alias_impl_trait.stderr index b32cf94985497..43fd76ef0ed9f 100644 --- a/src/test/ui/feature-gates/feature-gate-type_alias_impl_trait.stderr +++ b/src/test/ui/feature-gates/feature-gate-type_alias_impl_trait.stderr @@ -1,118 +1,78 @@ -error[E0658]: `impl Trait` in type aliases is unstable - --> $DIR/feature-gate-type_alias_impl_trait.rs:4:12 +error[E0308]: mismatched types + --> $DIR/feature-gate-type_alias_impl_trait.rs:10:9 | LL | type Foo = impl Debug; - | ^^^^^^^^^^ + | ---------- the expected opaque type +... +LL | Bar(42) + | ^^ expected opaque type, found integer | - = note: see issue #63063 <https://github.com/rust-lang/rust/issues/63063> for more information - = help: add `#![feature(type_alias_impl_trait)]` to the crate attributes to enable + = note: expected opaque type `impl Debug` + found type `{integer}` -error[E0658]: `impl Trait` in type aliases is unstable - --> $DIR/feature-gate-type_alias_impl_trait.rs:12:16 +error[E0658]: type alias impl trait is not permitted here + --> $DIR/feature-gate-type_alias_impl_trait.rs:16:19 | -LL | type Baa = impl Debug; - | ^^^^^^^^^^ +LL | let x = || -> Foo2 { 42 }; + | ^^^^ | = note: see issue #63063 <https://github.com/rust-lang/rust/issues/63063> for more information = help: add `#![feature(type_alias_impl_trait)]` to the crate attributes to enable -error[E0658]: associated type defaults are unstable - --> $DIR/feature-gate-type_alias_impl_trait.rs:23:5 - | -LL | type Assoc = impl Debug; - | ^^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: see issue #29661 <https://github.com/rust-lang/rust/issues/29661> for more information - = help: add `#![feature(associated_type_defaults)]` to the crate attributes to enable - -error[E0658]: `impl Trait` in type aliases is unstable +error[E0308]: mismatched types --> $DIR/feature-gate-type_alias_impl_trait.rs:23:18 | -LL | type Assoc = impl Debug; - | ^^^^^^^^^^ +LL | type Foo3 = impl Debug; + | ---------- the found opaque type +... +LL | let y: i32 = x; + | --- ^ expected `i32`, found opaque type + | | + | expected due to this | - = note: see issue #63063 <https://github.com/rust-lang/rust/issues/63063> for more information - = help: add `#![feature(type_alias_impl_trait)]` to the crate attributes to enable + = note: expected type `i32` + found opaque type `impl Debug` -error[E0658]: `impl Trait` in type aliases is unstable - --> $DIR/feature-gate-type_alias_impl_trait.rs:29:24 +error[E0308]: mismatched types + --> $DIR/feature-gate-type_alias_impl_trait.rs:26:13 | -LL | type NestedFree = (Vec<impl Debug>, impl Debug, impl Iterator<Item = impl Debug>); - | ^^^^^^^^^^ +LL | type Foo3 = impl Debug; + | ---------- the expected opaque type +... +LL | define3(42) + | ^^ expected opaque type, found integer | - = note: see issue #63063 <https://github.com/rust-lang/rust/issues/63063> for more information - = help: add `#![feature(type_alias_impl_trait)]` to the crate attributes to enable + = note: expected opaque type `impl Debug` + found type `{integer}` -error[E0658]: `impl Trait` in type aliases is unstable - --> $DIR/feature-gate-type_alias_impl_trait.rs:29:37 +error[E0658]: type alias impl trait is not permitted here + --> $DIR/feature-gate-type_alias_impl_trait.rs:33:12 | -LL | type NestedFree = (Vec<impl Debug>, impl Debug, impl Iterator<Item = impl Debug>); - | ^^^^^^^^^^ +LL | let y: Foo4 = 42; + | ^^^^ | - = note: see issue #63063 <https://github.com/rust-lang/rust/issues/63063> for more information - = help: add `#![feature(type_alias_impl_trait)]` to the crate attributes to enable - -error[E0658]: `impl Trait` in type aliases is unstable - --> $DIR/feature-gate-type_alias_impl_trait.rs:29:49 - | -LL | type NestedFree = (Vec<impl Debug>, impl Debug, impl Iterator<Item = impl Debug>); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: see issue #63063 <https://github.com/rust-lang/rust/issues/63063> for more information - = help: add `#![feature(type_alias_impl_trait)]` to the crate attributes to enable + = note: see issue #63065 <https://github.com/rust-lang/rust/issues/63065> for more information + = help: add `#![feature(impl_trait_in_bindings)]` to the crate attributes to enable -error[E0658]: `impl Trait` in type aliases is unstable - --> $DIR/feature-gate-type_alias_impl_trait.rs:29:70 - | -LL | type NestedFree = (Vec<impl Debug>, impl Debug, impl Iterator<Item = impl Debug>); - | ^^^^^^^^^^ +error: could not find defining uses + --> $DIR/feature-gate-type_alias_impl_trait.rs:5:12 | - = note: see issue #63063 <https://github.com/rust-lang/rust/issues/63063> for more information - = help: add `#![feature(type_alias_impl_trait)]` to the crate attributes to enable - -error[E0658]: `impl Trait` in type aliases is unstable - --> $DIR/feature-gate-type_alias_impl_trait.rs:40:21 - | -LL | type Baa = (Vec<impl Debug>, impl Debug, impl Iterator<Item = impl Debug> + Debug); - | ^^^^^^^^^^ - | - = note: see issue #63063 <https://github.com/rust-lang/rust/issues/63063> for more information - = help: add `#![feature(type_alias_impl_trait)]` to the crate attributes to enable - -error[E0658]: `impl Trait` in type aliases is unstable - --> $DIR/feature-gate-type_alias_impl_trait.rs:40:34 - | -LL | type Baa = (Vec<impl Debug>, impl Debug, impl Iterator<Item = impl Debug> + Debug); - | ^^^^^^^^^^ - | - = note: see issue #63063 <https://github.com/rust-lang/rust/issues/63063> for more information - = help: add `#![feature(type_alias_impl_trait)]` to the crate attributes to enable - -error[E0658]: `impl Trait` in type aliases is unstable - --> $DIR/feature-gate-type_alias_impl_trait.rs:40:46 - | -LL | type Baa = (Vec<impl Debug>, impl Debug, impl Iterator<Item = impl Debug> + Debug); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: see issue #63063 <https://github.com/rust-lang/rust/issues/63063> for more information - = help: add `#![feature(type_alias_impl_trait)]` to the crate attributes to enable +LL | type Foo = impl Debug; + | ^^^^^^^^^^ -error[E0658]: `impl Trait` in type aliases is unstable - --> $DIR/feature-gate-type_alias_impl_trait.rs:40:67 - | -LL | type Baa = (Vec<impl Debug>, impl Debug, impl Iterator<Item = impl Debug> + Debug); - | ^^^^^^^^^^ +error: could not find defining uses + --> $DIR/feature-gate-type_alias_impl_trait.rs:19:13 | - = note: see issue #63063 <https://github.com/rust-lang/rust/issues/63063> for more information - = help: add `#![feature(type_alias_impl_trait)]` to the crate attributes to enable +LL | type Foo3 = impl Debug; + | ^^^^^^^^^^ -error[E0562]: `impl Trait` not allowed outside of function and inherent method return types - --> $DIR/feature-gate-type_alias_impl_trait.rs:23:18 +error: could not find defining uses + --> $DIR/feature-gate-type_alias_impl_trait.rs:29:13 | -LL | type Assoc = impl Debug; - | ^^^^^^^^^^ +LL | type Foo4 = impl Debug; + | ^^^^^^^^^^ -error: aborting due to 13 previous errors +error: aborting due to 8 previous errors -Some errors have detailed explanations: E0562, E0658. -For more information about an error, try `rustc --explain E0562`. +Some errors have detailed explanations: E0308, E0658. +For more information about an error, try `rustc --explain E0308`. diff --git a/src/test/ui/feature-gates/feature-gate-unsafe_block_in_unsafe_fn.rs b/src/test/ui/feature-gates/feature-gate-unsafe_block_in_unsafe_fn.rs deleted file mode 100644 index 61e512a12a18d..0000000000000 --- a/src/test/ui/feature-gates/feature-gate-unsafe_block_in_unsafe_fn.rs +++ /dev/null @@ -1,6 +0,0 @@ -#![deny(unsafe_op_in_unsafe_fn)] -//~^ ERROR the `unsafe_op_in_unsafe_fn` lint is unstable -//~| ERROR the `unsafe_op_in_unsafe_fn` lint is unstable -//~| ERROR the `unsafe_op_in_unsafe_fn` lint is unstable - -fn main() {} diff --git a/src/test/ui/feature-gates/feature-gate-unsafe_block_in_unsafe_fn.stderr b/src/test/ui/feature-gates/feature-gate-unsafe_block_in_unsafe_fn.stderr deleted file mode 100644 index c5cad4a98d9ca..0000000000000 --- a/src/test/ui/feature-gates/feature-gate-unsafe_block_in_unsafe_fn.stderr +++ /dev/null @@ -1,30 +0,0 @@ -error[E0658]: the `unsafe_op_in_unsafe_fn` lint is unstable - --> $DIR/feature-gate-unsafe_block_in_unsafe_fn.rs:1:1 - | -LL | #![deny(unsafe_op_in_unsafe_fn)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: see issue #71668 <https://github.com/rust-lang/rust/issues/71668> for more information - = help: add `#![feature(unsafe_block_in_unsafe_fn)]` to the crate attributes to enable - -error[E0658]: the `unsafe_op_in_unsafe_fn` lint is unstable - --> $DIR/feature-gate-unsafe_block_in_unsafe_fn.rs:1:1 - | -LL | #![deny(unsafe_op_in_unsafe_fn)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: see issue #71668 <https://github.com/rust-lang/rust/issues/71668> for more information - = help: add `#![feature(unsafe_block_in_unsafe_fn)]` to the crate attributes to enable - -error[E0658]: the `unsafe_op_in_unsafe_fn` lint is unstable - --> $DIR/feature-gate-unsafe_block_in_unsafe_fn.rs:1:1 - | -LL | #![deny(unsafe_op_in_unsafe_fn)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: see issue #71668 <https://github.com/rust-lang/rust/issues/71668> for more information - = help: add `#![feature(unsafe_block_in_unsafe_fn)]` to the crate attributes to enable - -error: aborting due to 3 previous errors - -For more information about this error, try `rustc --explain E0658`. diff --git a/src/test/ui/feature-gates/feature-gate-unsized_fn_params.stderr b/src/test/ui/feature-gates/feature-gate-unsized_fn_params.stderr index 71acbb174ac66..3631a03938a65 100644 --- a/src/test/ui/feature-gates/feature-gate-unsized_fn_params.stderr +++ b/src/test/ui/feature-gates/feature-gate-unsized_fn_params.stderr @@ -8,8 +8,8 @@ LL | fn foo(x: dyn Foo) { = help: unsized fn params are gated as an unstable feature help: function arguments must have a statically known size, borrowed types always have a known size | -LL | fn foo(&x: dyn Foo) { - | ^ +LL | fn foo(x: &dyn Foo) { + | ^ error[E0277]: the size for values of type `(dyn Foo + 'static)` cannot be known at compilation time --> $DIR/feature-gate-unsized_fn_params.rs:24:5 diff --git a/src/test/ui/feature-gates/feature-gate-unsized_locals.stderr b/src/test/ui/feature-gates/feature-gate-unsized_locals.stderr index 522542208724b..0919c2f3a1e0e 100644 --- a/src/test/ui/feature-gates/feature-gate-unsized_locals.stderr +++ b/src/test/ui/feature-gates/feature-gate-unsized_locals.stderr @@ -8,8 +8,8 @@ LL | fn f(f: dyn FnOnce()) {} = help: unsized fn params are gated as an unstable feature help: function arguments must have a statically known size, borrowed types always have a known size | -LL | fn f(&f: dyn FnOnce()) {} - | ^ +LL | fn f(f: &dyn FnOnce()) {} + | ^ error: aborting due to previous error diff --git a/src/test/ui/feature-gates/feature-gate-wasm_abi.rs b/src/test/ui/feature-gates/feature-gate-wasm_abi.rs new file mode 100644 index 0000000000000..8c8de0763658e --- /dev/null +++ b/src/test/ui/feature-gates/feature-gate-wasm_abi.rs @@ -0,0 +1,7 @@ +extern "wasm" fn foo() { + //~^ ERROR: wasm ABI is experimental and subject to change +} + +fn main() { + foo(); +} diff --git a/src/test/ui/feature-gates/feature-gate-wasm_abi.stderr b/src/test/ui/feature-gates/feature-gate-wasm_abi.stderr new file mode 100644 index 0000000000000..c4113fd6af979 --- /dev/null +++ b/src/test/ui/feature-gates/feature-gate-wasm_abi.stderr @@ -0,0 +1,12 @@ +error[E0658]: wasm ABI is experimental and subject to change + --> $DIR/feature-gate-wasm_abi.rs:1:8 + | +LL | extern "wasm" fn foo() { + | ^^^^^^ + | + = note: see issue #83788 <https://github.com/rust-lang/rust/issues/83788> for more information + = help: add `#![feature(wasm_abi)]` to the crate attributes to enable + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0658`. diff --git a/src/test/ui/feature-gates/issue-43106-gating-of-builtin-attrs-error.rs b/src/test/ui/feature-gates/issue-43106-gating-of-builtin-attrs-error.rs index 6404b2c3115cb..d1fbd38ce07d9 100644 --- a/src/test/ui/feature-gates/issue-43106-gating-of-builtin-attrs-error.rs +++ b/src/test/ui/feature-gates/issue-43106-gating-of-builtin-attrs-error.rs @@ -8,12 +8,11 @@ // which would mess up the treatment of other cases in // issue-43106-gating-of-builtin-attrs.rs) -// ignore-tidy-linelength #![macro_export] //~^ ERROR: `macro_export` attribute cannot be used at crate level -#![main] -//~^ ERROR: `main` attribute cannot be used at crate level +#![rustc_main] //~ ERROR: the `#[rustc_main]` attribute is used internally to specify +//~^ ERROR: `rustc_main` attribute cannot be used at crate level #![start] //~^ ERROR: `start` attribute cannot be used at crate level #![repr()] @@ -107,24 +106,6 @@ mod export_name { //~| NOTE not a function or static } -#[main] -//~^ ERROR: `main` attribute can only be used on functions -mod main { - mod inner { #![main] } - //~^ ERROR: `main` attribute can only be used on functions - - // for `fn f()` case, see feature-gate-main.rs - - #[main] struct S; - //~^ ERROR: `main` attribute can only be used on functions - - #[main] type T = S; - //~^ ERROR: `main` attribute can only be used on functions - - #[main] impl S { } - //~^ ERROR: `main` attribute can only be used on functions -} - #[start] //~^ ERROR: `start` attribute can only be used on functions mod start { diff --git a/src/test/ui/feature-gates/issue-43106-gating-of-builtin-attrs-error.stderr b/src/test/ui/feature-gates/issue-43106-gating-of-builtin-attrs-error.stderr index 3ca1bd2ea7e48..ae2c428cc75ce 100644 --- a/src/test/ui/feature-gates/issue-43106-gating-of-builtin-attrs-error.stderr +++ b/src/test/ui/feature-gates/issue-43106-gating-of-builtin-attrs-error.stderr @@ -1,5 +1,13 @@ +error[E0658]: the `#[rustc_main]` attribute is used internally to specify test entry point function + --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:14:1 + | +LL | #![rustc_main] + | ^^^^^^^^^^^^^^ + | + = help: add `#![feature(rustc_attrs)]` to the crate attributes to enable + error: attribute must be of the form `#[inline]` or `#[inline(always|never)]` - --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:41:5 + --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:40:5 | LL | #[inline = "2100"] fn f() { } | ^^^^^^^^^^^^^^^^^^ @@ -8,68 +16,38 @@ LL | #[inline = "2100"] fn f() { } = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! = note: for more information, see issue #57571 <https://github.com/rust-lang/rust/issues/57571> -error: `main` attribute can only be used on functions - --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:110:1 - | -LL | #[main] - | ^^^^^^^ - -error: `main` attribute can only be used on functions - --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:113:17 - | -LL | mod inner { #![main] } - | ^^^^^^^^ - -error: `main` attribute can only be used on functions - --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:118:5 - | -LL | #[main] struct S; - | ^^^^^^^ - -error: `main` attribute can only be used on functions - --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:121:5 - | -LL | #[main] type T = S; - | ^^^^^^^ - -error: `main` attribute can only be used on functions - --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:124:5 - | -LL | #[main] impl S { } - | ^^^^^^^ - error: `start` attribute can only be used on functions - --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:128:1 + --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:109:1 | LL | #[start] | ^^^^^^^^ error: `start` attribute can only be used on functions - --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:131:17 + --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:112:17 | LL | mod inner { #![start] } | ^^^^^^^^^ error: `start` attribute can only be used on functions - --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:136:5 + --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:117:5 | LL | #[start] struct S; | ^^^^^^^^ error: `start` attribute can only be used on functions - --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:139:5 + --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:120:5 | LL | #[start] type T = S; | ^^^^^^^^ error: `start` attribute can only be used on functions - --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:142:5 + --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:123:5 | LL | #[start] impl S { } | ^^^^^^^^ error[E0518]: attribute should be applied to function or closure - --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:32:1 + --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:31:1 | LL | #[inline] | ^^^^^^^^^ @@ -84,7 +62,7 @@ LL | | } | |_- not a function or closure error: attribute should be applied to an `extern crate` item - --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:60:1 + --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:59:1 | LL | #[no_link] | ^^^^^^^^^^ @@ -99,7 +77,7 @@ LL | | } | |_- not an `extern crate` item error: attribute should be applied to a function or static - --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:86:1 + --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:85:1 | LL | #[export_name = "2200"] | ^^^^^^^^^^^^^^^^^^^^^^^ @@ -114,137 +92,138 @@ LL | | } | |_- not a function or static error: attribute should be applied to an `extern crate` item - --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:26:1 + --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:25:1 | LL | #![no_link] | ^^^^^^^^^^^ error: attribute should be applied to a function or static - --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:28:1 + --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:27:1 | LL | #![export_name = "2200"] | ^^^^^^^^^^^^^^^^^^^^^^^^ error[E0518]: attribute should be applied to function or closure - --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:30:1 + --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:29:1 | LL | #![inline] | ^^^^^^^^^^ error: `macro_export` attribute cannot be used at crate level - --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:13:1 + --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:12:1 | LL | #![macro_export] | ^^^^^^^^^^^^^^^^ -error: `main` attribute cannot be used at crate level - --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:15:1 +error: `rustc_main` attribute cannot be used at crate level + --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:14:1 | -LL | #![main] - | ^^^^^^^^ +LL | #![rustc_main] + | ^^^^^^^^^^^^^^ error: `start` attribute cannot be used at crate level - --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:17:1 + --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:16:1 | LL | #![start] | ^^^^^^^^^ error: `repr` attribute cannot be used at crate level - --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:19:1 + --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:18:1 | LL | #![repr()] | ^^^^^^^^^^ error: `path` attribute cannot be used at crate level - --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:21:1 + --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:20:1 | LL | #![path = "3800"] | ^^^^^^^^^^^^^^^^^ error: `automatically_derived` attribute cannot be used at crate level - --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:23:1 + --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:22:1 | LL | #![automatically_derived] | ^^^^^^^^^^^^^^^^^^^^^^^^^ error[E0518]: attribute should be applied to function or closure - --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:37:17 + --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:36:17 | LL | mod inner { #![inline] } | ------------^^^^^^^^^^-- not a function or closure error[E0518]: attribute should be applied to function or closure - --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:47:5 + --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:46:5 | LL | #[inline] struct S; | ^^^^^^^^^ --------- not a function or closure error[E0518]: attribute should be applied to function or closure - --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:51:5 + --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:50:5 | LL | #[inline] type T = S; | ^^^^^^^^^ ----------- not a function or closure error[E0518]: attribute should be applied to function or closure - --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:55:5 + --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:54:5 | LL | #[inline] impl S { } | ^^^^^^^^^ ---------- not a function or closure error: attribute should be applied to an `extern crate` item - --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:65:17 + --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:64:17 | LL | mod inner { #![no_link] } | ------------^^^^^^^^^^^-- not an `extern crate` item error: attribute should be applied to an `extern crate` item - --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:69:5 + --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:68:5 | LL | #[no_link] fn f() { } | ^^^^^^^^^^ ---------- not an `extern crate` item error: attribute should be applied to an `extern crate` item - --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:73:5 + --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:72:5 | LL | #[no_link] struct S; | ^^^^^^^^^^ --------- not an `extern crate` item error: attribute should be applied to an `extern crate` item - --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:77:5 + --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:76:5 | LL | #[no_link]type T = S; | ^^^^^^^^^^----------- not an `extern crate` item error: attribute should be applied to an `extern crate` item - --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:81:5 + --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:80:5 | LL | #[no_link] impl S { } | ^^^^^^^^^^ ---------- not an `extern crate` item error: attribute should be applied to a function or static - --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:91:17 + --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:90:17 | LL | mod inner { #![export_name="2200"] } | ------------^^^^^^^^^^^^^^^^^^^^^^-- not a function or static error: attribute should be applied to a function or static - --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:97:5 + --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:96:5 | LL | #[export_name = "2200"] struct S; | ^^^^^^^^^^^^^^^^^^^^^^^ --------- not a function or static error: attribute should be applied to a function or static - --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:101:5 + --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:100:5 | LL | #[export_name = "2200"] type T = S; | ^^^^^^^^^^^^^^^^^^^^^^^ ----------- not a function or static error: attribute should be applied to a function or static - --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:105:5 + --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:104:5 | LL | #[export_name = "2200"] impl S { } | ^^^^^^^^^^^^^^^^^^^^^^^ ---------- not a function or static -error: aborting due to 36 previous errors +error: aborting due to 32 previous errors -For more information about this error, try `rustc --explain E0518`. +Some errors have detailed explanations: E0518, E0658. +For more information about an error, try `rustc --explain E0518`. diff --git a/src/test/ui/feature-gates/issue-43106-gating-of-builtin-attrs.rs b/src/test/ui/feature-gates/issue-43106-gating-of-builtin-attrs.rs index 21f40524f63df..19a60f1bcfffe 100644 --- a/src/test/ui/feature-gates/issue-43106-gating-of-builtin-attrs.rs +++ b/src/test/ui/feature-gates/issue-43106-gating-of-builtin-attrs.rs @@ -34,7 +34,6 @@ // occurrences in the source text. // check-pass -// ignore-tidy-linelength #![feature(test, plugin_registrar)] #![warn(unused_attributes, unknown_lints)] diff --git a/src/test/ui/feature-gates/issue-43106-gating-of-builtin-attrs.stderr b/src/test/ui/feature-gates/issue-43106-gating-of-builtin-attrs.stderr index c908d2589cf49..c207c05455fea 100644 --- a/src/test/ui/feature-gates/issue-43106-gating-of-builtin-attrs.stderr +++ b/src/test/ui/feature-gates/issue-43106-gating-of-builtin-attrs.stderr @@ -1,179 +1,179 @@ warning: unknown lint: `x5400` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:53:9 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:52:9 | LL | #![warn(x5400)] | ^^^^^ | note: the lint level is defined here - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:40:28 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:39:28 | LL | #![warn(unused_attributes, unknown_lints)] | ^^^^^^^^^^^^^ warning: unknown lint: `x5300` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:54:10 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:53:10 | LL | #![allow(x5300)] | ^^^^^ warning: unknown lint: `x5200` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:55:11 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:54:11 | LL | #![forbid(x5200)] | ^^^^^ warning: unknown lint: `x5100` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:56:9 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:55:9 | LL | #![deny(x5100)] | ^^^^^ warning: unknown lint: `x5400` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:111:8 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:110:8 | LL | #[warn(x5400)] | ^^^^^ warning: unknown lint: `x5400` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:114:25 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:113:25 | LL | mod inner { #![warn(x5400)] } | ^^^^^ warning: unknown lint: `x5400` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:117:12 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:116:12 | LL | #[warn(x5400)] fn f() { } | ^^^^^ warning: unknown lint: `x5400` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:120:12 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:119:12 | LL | #[warn(x5400)] struct S; | ^^^^^ warning: unknown lint: `x5400` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:123:12 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:122:12 | LL | #[warn(x5400)] type T = S; | ^^^^^ warning: unknown lint: `x5400` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:126:12 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:125:12 | LL | #[warn(x5400)] impl S { } | ^^^^^ warning: unknown lint: `x5300` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:130:9 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:129:9 | LL | #[allow(x5300)] | ^^^^^ warning: unknown lint: `x5300` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:133:26 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:132:26 | LL | mod inner { #![allow(x5300)] } | ^^^^^ warning: unknown lint: `x5300` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:136:13 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:135:13 | LL | #[allow(x5300)] fn f() { } | ^^^^^ warning: unknown lint: `x5300` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:139:13 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:138:13 | LL | #[allow(x5300)] struct S; | ^^^^^ warning: unknown lint: `x5300` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:142:13 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:141:13 | LL | #[allow(x5300)] type T = S; | ^^^^^ warning: unknown lint: `x5300` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:145:13 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:144:13 | LL | #[allow(x5300)] impl S { } | ^^^^^ warning: unknown lint: `x5200` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:149:10 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:148:10 | LL | #[forbid(x5200)] | ^^^^^ warning: unknown lint: `x5200` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:152:27 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:151:27 | LL | mod inner { #![forbid(x5200)] } | ^^^^^ warning: unknown lint: `x5200` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:155:14 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:154:14 | LL | #[forbid(x5200)] fn f() { } | ^^^^^ warning: unknown lint: `x5200` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:158:14 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:157:14 | LL | #[forbid(x5200)] struct S; | ^^^^^ warning: unknown lint: `x5200` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:161:14 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:160:14 | LL | #[forbid(x5200)] type T = S; | ^^^^^ warning: unknown lint: `x5200` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:164:14 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:163:14 | LL | #[forbid(x5200)] impl S { } | ^^^^^ warning: unknown lint: `x5100` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:168:8 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:167:8 | LL | #[deny(x5100)] | ^^^^^ warning: unknown lint: `x5100` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:171:25 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:170:25 | LL | mod inner { #![deny(x5100)] } | ^^^^^ warning: unknown lint: `x5100` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:174:12 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:173:12 | LL | #[deny(x5100)] fn f() { } | ^^^^^ warning: unknown lint: `x5100` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:177:12 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:176:12 | LL | #[deny(x5100)] struct S; | ^^^^^ warning: unknown lint: `x5100` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:180:12 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:179:12 | LL | #[deny(x5100)] type T = S; | ^^^^^ warning: unknown lint: `x5100` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:183:12 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:182:12 | LL | #[deny(x5100)] impl S { } | ^^^^^ warning: `#[macro_escape]` is a deprecated synonym for `#[macro_use]` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:441:17 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:440:17 | LL | mod inner { #![macro_escape] } | ^^^^^^^^^^^^^^^^ @@ -181,13 +181,13 @@ LL | mod inner { #![macro_escape] } = help: try an outer attribute: `#[macro_use]` warning: `#[macro_escape]` is a deprecated synonym for `#[macro_use]` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:438:1 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:437:1 | LL | #[macro_escape] | ^^^^^^^^^^^^^^^ warning: use of deprecated attribute `plugin_registrar`: compiler plugins are deprecated. See https://github.com/rust-lang/rust/pull/64675 - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:228:17 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:227:17 | LL | mod inner { #![plugin_registrar] } | ^^^^^^^^^^^^^^^^^^^^ help: may be removed in a future compiler version @@ -195,49 +195,49 @@ LL | mod inner { #![plugin_registrar] } = note: `#[warn(deprecated)]` on by default warning: use of deprecated attribute `plugin_registrar`: compiler plugins are deprecated. See https://github.com/rust-lang/rust/pull/64675 - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:236:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:235:5 | LL | #[plugin_registrar] struct S; | ^^^^^^^^^^^^^^^^^^^ help: may be removed in a future compiler version warning: use of deprecated attribute `plugin_registrar`: compiler plugins are deprecated. See https://github.com/rust-lang/rust/pull/64675 - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:241:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:240:5 | LL | #[plugin_registrar] type T = S; | ^^^^^^^^^^^^^^^^^^^ help: may be removed in a future compiler version warning: use of deprecated attribute `plugin_registrar`: compiler plugins are deprecated. See https://github.com/rust-lang/rust/pull/64675 - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:246:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:245:5 | LL | #[plugin_registrar] impl S { } | ^^^^^^^^^^^^^^^^^^^ help: may be removed in a future compiler version warning: use of deprecated attribute `plugin_registrar`: compiler plugins are deprecated. See https://github.com/rust-lang/rust/pull/64675 - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:223:1 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:222:1 | LL | #[plugin_registrar] | ^^^^^^^^^^^^^^^^^^^ help: may be removed in a future compiler version warning: use of deprecated attribute `plugin_registrar`: compiler plugins are deprecated. See https://github.com/rust-lang/rust/pull/64675 - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:46:1 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:45:1 | LL | #![plugin_registrar] | ^^^^^^^^^^^^^^^^^^^^ help: may be removed in a future compiler version warning: use of deprecated attribute `crate_id`: no longer used. - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:91:1 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:90:1 | LL | #![crate_id = "10"] | ^^^^^^^^^^^^^^^^^^^ help: remove this attribute warning: use of deprecated attribute `no_start`: no longer used. - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:100:1 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:99:1 | LL | #![no_start] | ^^^^^^^^^^^^ help: remove this attribute warning: attribute should be applied to a function or static - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:333:1 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:332:1 | LL | #[no_mangle] | ^^^^^^^^^^^^ @@ -252,14 +252,14 @@ LL | | } | |_- not a function or static | note: the lint level is defined here - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:40:9 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:39:9 | LL | #![warn(unused_attributes, unknown_lints)] | ^^^^^^^^^^^^^^^^^ = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! warning: attribute should be applied to a function - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:500:1 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:499:1 | LL | #[cold] | ^^^^^^^ @@ -276,7 +276,7 @@ LL | | } = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! warning: attribute should be applied to a foreign function or static - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:529:1 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:528:1 | LL | #[link_name = "1900"] | ^^^^^^^^^^^^^^^^^^^^^ @@ -293,7 +293,7 @@ LL | | } = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! warning: attribute should be applied to a function or static - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:568:1 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:567:1 | LL | #[link_section = "1800"] | ^^^^^^^^^^^^^^^^^^^^^^^^ @@ -310,7 +310,7 @@ LL | | } = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! warning: attribute should be applied to a function - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:69:1 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:68:1 | LL | #![cold] | ^^^^^^^^ @@ -318,7 +318,7 @@ LL | #![cold] = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! warning: attribute should be applied to a foreign function or static - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:73:1 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:72:1 | LL | #![link_name = "1900"] | ^^^^^^^^^^^^^^^^^^^^^^ @@ -326,7 +326,7 @@ LL | #![link_name = "1900"] = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! warning: attribute should be applied to a function or static - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:76:1 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:75:1 | LL | #![link_section = "1800"] | ^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -334,7 +334,7 @@ LL | #![link_section = "1800"] = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! warning: attribute should be applied to a function or static - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:338:17 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:337:17 | LL | mod inner { #![no_mangle] } | ------------^^^^^^^^^^^^^-- not a function or static @@ -342,7 +342,7 @@ LL | mod inner { #![no_mangle] } = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! warning: attribute should be applied to a function or static - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:345:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:344:5 | LL | #[no_mangle] struct S; | ^^^^^^^^^^^^ --------- not a function or static @@ -350,7 +350,7 @@ LL | #[no_mangle] struct S; = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! warning: attribute should be applied to a function or static - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:350:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:349:5 | LL | #[no_mangle] type T = S; | ^^^^^^^^^^^^ ----------- not a function or static @@ -358,7 +358,7 @@ LL | #[no_mangle] type T = S; = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! warning: attribute should be applied to a function or static - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:355:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:354:5 | LL | #[no_mangle] impl S { } | ^^^^^^^^^^^^ ---------- not a function or static @@ -366,7 +366,7 @@ LL | #[no_mangle] impl S { } = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! warning: attribute should be applied to a function - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:506:17 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:505:17 | LL | mod inner { #![cold] } | ------------^^^^^^^^-- not a function @@ -374,7 +374,7 @@ LL | mod inner { #![cold] } = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! warning: attribute should be applied to a function - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:513:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:512:5 | LL | #[cold] struct S; | ^^^^^^^ --------- not a function @@ -382,7 +382,7 @@ LL | #[cold] struct S; = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! warning: attribute should be applied to a function - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:518:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:517:5 | LL | #[cold] type T = S; | ^^^^^^^ ----------- not a function @@ -390,7 +390,7 @@ LL | #[cold] type T = S; = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! warning: attribute should be applied to a function - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:523:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:522:5 | LL | #[cold] impl S { } | ^^^^^^^ ---------- not a function @@ -398,7 +398,7 @@ LL | #[cold] impl S { } = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! warning: attribute should be applied to a foreign function or static - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:535:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:534:5 | LL | #[link_name = "1900"] | ^^^^^^^^^^^^^^^^^^^^^ @@ -408,13 +408,13 @@ LL | extern "C" { } | = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! help: try `#[link(name = "1900")]` instead - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:535:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:534:5 | LL | #[link_name = "1900"] | ^^^^^^^^^^^^^^^^^^^^^ warning: attribute should be applied to a foreign function or static - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:542:17 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:541:17 | LL | mod inner { #![link_name="1900"] } | ------------^^^^^^^^^^^^^^^^^^^^-- not a foreign function or static @@ -422,7 +422,7 @@ LL | mod inner { #![link_name="1900"] } = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! warning: attribute should be applied to a foreign function or static - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:547:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:546:5 | LL | #[link_name = "1900"] fn f() { } | ^^^^^^^^^^^^^^^^^^^^^ ---------- not a foreign function or static @@ -430,7 +430,7 @@ LL | #[link_name = "1900"] fn f() { } = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! warning: attribute should be applied to a foreign function or static - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:552:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:551:5 | LL | #[link_name = "1900"] struct S; | ^^^^^^^^^^^^^^^^^^^^^ --------- not a foreign function or static @@ -438,7 +438,7 @@ LL | #[link_name = "1900"] struct S; = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! warning: attribute should be applied to a foreign function or static - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:557:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:556:5 | LL | #[link_name = "1900"] type T = S; | ^^^^^^^^^^^^^^^^^^^^^ ----------- not a foreign function or static @@ -446,7 +446,7 @@ LL | #[link_name = "1900"] type T = S; = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! warning: attribute should be applied to a foreign function or static - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:562:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:561:5 | LL | #[link_name = "1900"] impl S { } | ^^^^^^^^^^^^^^^^^^^^^ ---------- not a foreign function or static @@ -454,7 +454,7 @@ LL | #[link_name = "1900"] impl S { } = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! warning: attribute should be applied to a function or static - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:574:17 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:573:17 | LL | mod inner { #![link_section="1800"] } | ------------^^^^^^^^^^^^^^^^^^^^^^^-- not a function or static @@ -462,7 +462,7 @@ LL | mod inner { #![link_section="1800"] } = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! warning: attribute should be applied to a function or static - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:581:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:580:5 | LL | #[link_section = "1800"] struct S; | ^^^^^^^^^^^^^^^^^^^^^^^^ --------- not a function or static @@ -470,7 +470,7 @@ LL | #[link_section = "1800"] struct S; = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! warning: attribute should be applied to a function or static - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:586:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:585:5 | LL | #[link_section = "1800"] type T = S; | ^^^^^^^^^^^^^^^^^^^^^^^^ ----------- not a function or static @@ -478,7 +478,7 @@ LL | #[link_section = "1800"] type T = S; = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! warning: attribute should be applied to a function or static - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:591:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:590:5 | LL | #[link_section = "1800"] impl S { } | ^^^^^^^^^^^^^^^^^^^^^^^^ ---------- not a function or static @@ -486,7 +486,7 @@ LL | #[link_section = "1800"] impl S { } = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! warning: the feature `rust1` has been stable since 1.0.0 and no longer requires an attribute to enable - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:96:12 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:95:12 | LL | #![feature(rust1)] | ^^^^^ @@ -494,850 +494,850 @@ LL | #![feature(rust1)] = note: `#[warn(stable_features)]` on by default warning: unused attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:191:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:45:1 + | +LL | #![plugin_registrar] + | ^^^^^^^^^^^^^^^^^^^^ + +warning: unused attribute + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:59:1 + | +LL | #![should_panic] + | ^^^^^^^^^^^^^^^^ + +warning: unused attribute + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:60:1 + | +LL | #![ignore] + | ^^^^^^^^^^ + +warning: unused attribute + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:66:1 + | +LL | #![proc_macro_derive()] + | ^^^^^^^^^^^^^^^^^^^^^^^ + +warning: unused attribute + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:190:5 | LL | #[macro_use] fn f() { } | ^^^^^^^^^^^^ warning: unused attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:194:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:193:5 | LL | #[macro_use] struct S; | ^^^^^^^^^^^^ warning: unused attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:197:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:196:5 | LL | #[macro_use] type T = S; | ^^^^^^^^^^^^ warning: unused attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:200:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:199:5 | LL | #[macro_use] impl S { } | ^^^^^^^^^^^^ warning: unused attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:207:17 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:203:1 + | +LL | #[macro_export] + | ^^^^^^^^^^^^^^^ + +warning: unused attribute + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:206:17 | LL | mod inner { #![macro_export] } | ^^^^^^^^^^^^^^^^ warning: unused attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:210:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:209:5 | LL | #[macro_export] fn f() { } | ^^^^^^^^^^^^^^^ warning: unused attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:213:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:212:5 | LL | #[macro_export] struct S; | ^^^^^^^^^^^^^^^ warning: unused attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:216:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:215:5 | LL | #[macro_export] type T = S; | ^^^^^^^^^^^^^^^ warning: unused attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:219:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:218:5 | LL | #[macro_export] impl S { } | ^^^^^^^^^^^^^^^ warning: unused attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:204:1 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:222:1 | -LL | #[macro_export] - | ^^^^^^^^^^^^^^^ +LL | #[plugin_registrar] + | ^^^^^^^^^^^^^^^^^^^ warning: unused attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:228:17 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:227:17 | LL | mod inner { #![plugin_registrar] } | ^^^^^^^^^^^^^^^^^^^^ warning: unused attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:236:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:235:5 | LL | #[plugin_registrar] struct S; | ^^^^^^^^^^^^^^^^^^^ warning: unused attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:241:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:240:5 | LL | #[plugin_registrar] type T = S; | ^^^^^^^^^^^^^^^^^^^ warning: unused attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:246:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:245:5 | LL | #[plugin_registrar] impl S { } | ^^^^^^^^^^^^^^^^^^^ warning: unused attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:223:1 - | -LL | #[plugin_registrar] - | ^^^^^^^^^^^^^^^^^^^ - -warning: unused attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:301:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:300:5 | LL | #[path = "3800"] fn f() { } | ^^^^^^^^^^^^^^^^ warning: unused attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:304:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:303:5 | LL | #[path = "3800"] struct S; | ^^^^^^^^^^^^^^^^ warning: unused attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:307:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:306:5 | LL | #[path = "3800"] type T = S; | ^^^^^^^^^^^^^^^^ warning: unused attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:310:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:309:5 | LL | #[path = "3800"] impl S { } | ^^^^^^^^^^^^^^^^ warning: unused attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:317:17 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:313:1 + | +LL | #[automatically_derived] + | ^^^^^^^^^^^^^^^^^^^^^^^^ + +warning: unused attribute + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:316:17 | LL | mod inner { #![automatically_derived] } | ^^^^^^^^^^^^^^^^^^^^^^^^^ warning: unused attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:320:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:319:5 | LL | #[automatically_derived] fn f() { } | ^^^^^^^^^^^^^^^^^^^^^^^^ warning: unused attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:323:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:322:5 | LL | #[automatically_derived] struct S; | ^^^^^^^^^^^^^^^^^^^^^^^^ warning: unused attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:326:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:325:5 | LL | #[automatically_derived] type T = S; | ^^^^^^^^^^^^^^^^^^^^^^^^ warning: unused attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:329:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:328:5 | LL | #[automatically_derived] impl S { } | ^^^^^^^^^^^^^^^^^^^^^^^^ warning: unused attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:314:1 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:360:1 | -LL | #[automatically_derived] - | ^^^^^^^^^^^^^^^^^^^^^^^^ +LL | #[should_panic] + | ^^^^^^^^^^^^^^^ warning: unused attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:364:17 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:363:17 | LL | mod inner { #![should_panic] } | ^^^^^^^^^^^^^^^^ warning: unused attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:367:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:366:5 | LL | #[should_panic] fn f() { } | ^^^^^^^^^^^^^^^ warning: unused attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:370:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:369:5 | LL | #[should_panic] struct S; | ^^^^^^^^^^^^^^^ warning: unused attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:373:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:372:5 | LL | #[should_panic] type T = S; | ^^^^^^^^^^^^^^^ warning: unused attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:376:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:375:5 | LL | #[should_panic] impl S { } | ^^^^^^^^^^^^^^^ warning: unused attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:361:1 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:379:1 | -LL | #[should_panic] - | ^^^^^^^^^^^^^^^ +LL | #[ignore] + | ^^^^^^^^^ warning: unused attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:383:17 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:382:17 | LL | mod inner { #![ignore] } | ^^^^^^^^^^ warning: unused attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:386:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:385:5 | LL | #[ignore] fn f() { } | ^^^^^^^^^ warning: unused attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:389:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:388:5 | LL | #[ignore] struct S; | ^^^^^^^^^ warning: unused attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:392:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:391:5 | LL | #[ignore] type T = S; | ^^^^^^^^^ warning: unused attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:395:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:394:5 | LL | #[ignore] impl S { } | ^^^^^^^^^ warning: unused attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:380:1 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:398:1 | -LL | #[ignore] - | ^^^^^^^^^ +LL | #[no_implicit_prelude] + | ^^^^^^^^^^^^^^^^^^^^^^ warning: unused attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:402:17 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:401:17 | LL | mod inner { #![no_implicit_prelude] } | ^^^^^^^^^^^^^^^^^^^^^^^ warning: unused attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:405:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:404:5 | LL | #[no_implicit_prelude] fn f() { } | ^^^^^^^^^^^^^^^^^^^^^^ warning: unused attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:408:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:407:5 | LL | #[no_implicit_prelude] struct S; | ^^^^^^^^^^^^^^^^^^^^^^ warning: unused attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:411:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:410:5 | LL | #[no_implicit_prelude] type T = S; | ^^^^^^^^^^^^^^^^^^^^^^ warning: unused attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:414:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:413:5 | LL | #[no_implicit_prelude] impl S { } | ^^^^^^^^^^^^^^^^^^^^^^ warning: unused attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:399:1 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:417:1 | -LL | #[no_implicit_prelude] - | ^^^^^^^^^^^^^^^^^^^^^^ +LL | #[reexport_test_harness_main = "2900"] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ warning: unused attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:421:17 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:420:17 | LL | mod inner { #![reexport_test_harness_main="2900"] } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ warning: unused attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:424:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:423:5 | LL | #[reexport_test_harness_main = "2900"] fn f() { } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ warning: unused attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:427:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:426:5 | LL | #[reexport_test_harness_main = "2900"] struct S; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ warning: unused attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:430:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:429:5 | LL | #[reexport_test_harness_main = "2900"] type T = S; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ warning: unused attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:433:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:432:5 | LL | #[reexport_test_harness_main = "2900"] impl S { } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ warning: unused attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:418:1 - | -LL | #[reexport_test_harness_main = "2900"] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -warning: unused attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:445:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:444:5 | LL | #[macro_escape] fn f() { } | ^^^^^^^^^^^^^^^ warning: unused attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:448:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:447:5 | LL | #[macro_escape] struct S; | ^^^^^^^^^^^^^^^ warning: unused attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:451:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:450:5 | LL | #[macro_escape] type T = S; | ^^^^^^^^^^^^^^^ warning: unused attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:454:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:453:5 | LL | #[macro_escape] impl S { } | ^^^^^^^^^^^^^^^ warning: unused attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:462:17 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:457:1 + | +LL | #[no_std] + | ^^^^^^^^^ + +warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![foo]` + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:457:1 + | +LL | #[no_std] + | ^^^^^^^^^ + +warning: unused attribute + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:461:17 | LL | mod inner { #![no_std] } | ^^^^^^^^^^ warning: crate-level attribute should be in the root module - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:462:17 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:461:17 | LL | mod inner { #![no_std] } | ^^^^^^^^^^ warning: unused attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:466:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:465:5 | LL | #[no_std] fn f() { } | ^^^^^^^^^ warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![foo]` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:466:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:465:5 | LL | #[no_std] fn f() { } | ^^^^^^^^^ warning: unused attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:470:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:469:5 | LL | #[no_std] struct S; | ^^^^^^^^^ warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![foo]` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:470:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:469:5 | LL | #[no_std] struct S; | ^^^^^^^^^ warning: unused attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:474:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:473:5 | LL | #[no_std] type T = S; | ^^^^^^^^^ warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![foo]` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:474:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:473:5 | LL | #[no_std] type T = S; | ^^^^^^^^^ warning: unused attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:478:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:477:5 | LL | #[no_std] impl S { } | ^^^^^^^^^ warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![foo]` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:478:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:477:5 | LL | #[no_std] impl S { } | ^^^^^^^^^ warning: unused attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:458:1 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:658:1 | -LL | #[no_std] - | ^^^^^^^^^ +LL | #[crate_name = "0900"] + | ^^^^^^^^^^^^^^^^^^^^^^ warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![foo]` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:458:1 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:658:1 | -LL | #[no_std] - | ^^^^^^^^^ +LL | #[crate_name = "0900"] + | ^^^^^^^^^^^^^^^^^^^^^^ warning: unused attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:663:17 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:662:17 | LL | mod inner { #![crate_name="0900"] } | ^^^^^^^^^^^^^^^^^^^^^ warning: crate-level attribute should be in the root module - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:663:17 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:662:17 | LL | mod inner { #![crate_name="0900"] } | ^^^^^^^^^^^^^^^^^^^^^ warning: unused attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:667:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:666:5 | LL | #[crate_name = "0900"] fn f() { } | ^^^^^^^^^^^^^^^^^^^^^^ warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![foo]` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:667:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:666:5 | LL | #[crate_name = "0900"] fn f() { } | ^^^^^^^^^^^^^^^^^^^^^^ warning: unused attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:671:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:670:5 | LL | #[crate_name = "0900"] struct S; | ^^^^^^^^^^^^^^^^^^^^^^ warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![foo]` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:671:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:670:5 | LL | #[crate_name = "0900"] struct S; | ^^^^^^^^^^^^^^^^^^^^^^ warning: unused attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:675:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:674:5 | LL | #[crate_name = "0900"] type T = S; | ^^^^^^^^^^^^^^^^^^^^^^ warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![foo]` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:675:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:674:5 | LL | #[crate_name = "0900"] type T = S; | ^^^^^^^^^^^^^^^^^^^^^^ warning: unused attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:679:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:678:5 | LL | #[crate_name = "0900"] impl S { } | ^^^^^^^^^^^^^^^^^^^^^^ warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![foo]` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:679:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:678:5 | LL | #[crate_name = "0900"] impl S { } | ^^^^^^^^^^^^^^^^^^^^^^ warning: unused attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:659:1 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:683:1 | -LL | #[crate_name = "0900"] +LL | #[crate_type = "0800"] | ^^^^^^^^^^^^^^^^^^^^^^ warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![foo]` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:659:1 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:683:1 | -LL | #[crate_name = "0900"] +LL | #[crate_type = "0800"] | ^^^^^^^^^^^^^^^^^^^^^^ warning: unused attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:688:17 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:687:17 | LL | mod inner { #![crate_type="0800"] } | ^^^^^^^^^^^^^^^^^^^^^ warning: crate-level attribute should be in the root module - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:688:17 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:687:17 | LL | mod inner { #![crate_type="0800"] } | ^^^^^^^^^^^^^^^^^^^^^ warning: unused attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:692:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:691:5 | LL | #[crate_type = "0800"] fn f() { } | ^^^^^^^^^^^^^^^^^^^^^^ warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![foo]` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:692:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:691:5 | LL | #[crate_type = "0800"] fn f() { } | ^^^^^^^^^^^^^^^^^^^^^^ warning: unused attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:696:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:695:5 | LL | #[crate_type = "0800"] struct S; | ^^^^^^^^^^^^^^^^^^^^^^ warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![foo]` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:696:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:695:5 | LL | #[crate_type = "0800"] struct S; | ^^^^^^^^^^^^^^^^^^^^^^ warning: unused attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:700:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:699:5 | LL | #[crate_type = "0800"] type T = S; | ^^^^^^^^^^^^^^^^^^^^^^ warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![foo]` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:700:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:699:5 | LL | #[crate_type = "0800"] type T = S; | ^^^^^^^^^^^^^^^^^^^^^^ warning: unused attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:704:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:703:5 | LL | #[crate_type = "0800"] impl S { } | ^^^^^^^^^^^^^^^^^^^^^^ warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![foo]` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:704:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:703:5 | LL | #[crate_type = "0800"] impl S { } | ^^^^^^^^^^^^^^^^^^^^^^ warning: unused attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:684:1 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:708:1 | -LL | #[crate_type = "0800"] - | ^^^^^^^^^^^^^^^^^^^^^^ +LL | #[feature(x0600)] + | ^^^^^^^^^^^^^^^^^ warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![foo]` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:684:1 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:708:1 | -LL | #[crate_type = "0800"] - | ^^^^^^^^^^^^^^^^^^^^^^ +LL | #[feature(x0600)] + | ^^^^^^^^^^^^^^^^^ warning: unused attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:713:17 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:712:17 | LL | mod inner { #![feature(x0600)] } | ^^^^^^^^^^^^^^^^^^ warning: crate-level attribute should be in the root module - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:713:17 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:712:17 | LL | mod inner { #![feature(x0600)] } | ^^^^^^^^^^^^^^^^^^ warning: unused attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:717:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:716:5 | LL | #[feature(x0600)] fn f() { } | ^^^^^^^^^^^^^^^^^ warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![foo]` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:717:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:716:5 | LL | #[feature(x0600)] fn f() { } | ^^^^^^^^^^^^^^^^^ warning: unused attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:721:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:720:5 | LL | #[feature(x0600)] struct S; | ^^^^^^^^^^^^^^^^^ warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![foo]` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:721:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:720:5 | LL | #[feature(x0600)] struct S; | ^^^^^^^^^^^^^^^^^ warning: unused attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:725:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:724:5 | LL | #[feature(x0600)] type T = S; | ^^^^^^^^^^^^^^^^^ warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![foo]` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:725:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:724:5 | LL | #[feature(x0600)] type T = S; | ^^^^^^^^^^^^^^^^^ warning: unused attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:729:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:728:5 | LL | #[feature(x0600)] impl S { } | ^^^^^^^^^^^^^^^^^ warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![foo]` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:729:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:728:5 | LL | #[feature(x0600)] impl S { } | ^^^^^^^^^^^^^^^^^ warning: unused attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:709:1 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:734:1 | -LL | #[feature(x0600)] - | ^^^^^^^^^^^^^^^^^ +LL | #[no_main] + | ^^^^^^^^^^ warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![foo]` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:709:1 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:734:1 | -LL | #[feature(x0600)] - | ^^^^^^^^^^^^^^^^^ +LL | #[no_main] + | ^^^^^^^^^^ warning: unused attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:739:17 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:738:17 | LL | mod inner { #![no_main] } | ^^^^^^^^^^^ warning: crate-level attribute should be in the root module - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:739:17 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:738:17 | LL | mod inner { #![no_main] } | ^^^^^^^^^^^ warning: unused attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:743:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:742:5 | LL | #[no_main] fn f() { } | ^^^^^^^^^^ warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![foo]` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:743:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:742:5 | LL | #[no_main] fn f() { } | ^^^^^^^^^^ warning: unused attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:747:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:746:5 | LL | #[no_main] struct S; | ^^^^^^^^^^ warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![foo]` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:747:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:746:5 | LL | #[no_main] struct S; | ^^^^^^^^^^ warning: unused attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:751:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:750:5 | LL | #[no_main] type T = S; | ^^^^^^^^^^ warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![foo]` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:751:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:750:5 | LL | #[no_main] type T = S; | ^^^^^^^^^^ warning: unused attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:755:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:754:5 | LL | #[no_main] impl S { } | ^^^^^^^^^^ warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![foo]` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:755:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:754:5 | LL | #[no_main] impl S { } | ^^^^^^^^^^ warning: unused attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:735:1 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:772:1 | -LL | #[no_main] - | ^^^^^^^^^^ +LL | #[recursion_limit="0200"] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![foo]` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:735:1 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:772:1 | -LL | #[no_main] - | ^^^^^^^^^^ +LL | #[recursion_limit="0200"] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ warning: unused attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:777:17 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:776:17 | LL | mod inner { #![recursion_limit="0200"] } | ^^^^^^^^^^^^^^^^^^^^^^^^^^ warning: crate-level attribute should be in the root module - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:777:17 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:776:17 | LL | mod inner { #![recursion_limit="0200"] } | ^^^^^^^^^^^^^^^^^^^^^^^^^^ warning: unused attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:781:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:780:5 | LL | #[recursion_limit="0200"] fn f() { } | ^^^^^^^^^^^^^^^^^^^^^^^^^ warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![foo]` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:781:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:780:5 | LL | #[recursion_limit="0200"] fn f() { } | ^^^^^^^^^^^^^^^^^^^^^^^^^ warning: unused attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:785:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:784:5 | LL | #[recursion_limit="0200"] struct S; | ^^^^^^^^^^^^^^^^^^^^^^^^^ warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![foo]` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:785:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:784:5 | LL | #[recursion_limit="0200"] struct S; | ^^^^^^^^^^^^^^^^^^^^^^^^^ warning: unused attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:789:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:788:5 | LL | #[recursion_limit="0200"] type T = S; | ^^^^^^^^^^^^^^^^^^^^^^^^^ warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![foo]` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:789:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:788:5 | LL | #[recursion_limit="0200"] type T = S; | ^^^^^^^^^^^^^^^^^^^^^^^^^ warning: unused attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:793:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:792:5 | LL | #[recursion_limit="0200"] impl S { } | ^^^^^^^^^^^^^^^^^^^^^^^^^ warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![foo]` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:793:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:792:5 | LL | #[recursion_limit="0200"] impl S { } | ^^^^^^^^^^^^^^^^^^^^^^^^^ warning: unused attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:773:1 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:797:1 | -LL | #[recursion_limit="0200"] - | ^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | #[type_length_limit="0100"] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![foo]` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:773:1 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:797:1 | -LL | #[recursion_limit="0200"] - | ^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | #[type_length_limit="0100"] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ warning: unused attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:802:17 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:801:17 | LL | mod inner { #![type_length_limit="0100"] } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ warning: crate-level attribute should be in the root module - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:802:17 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:801:17 | LL | mod inner { #![type_length_limit="0100"] } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ warning: unused attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:806:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:805:5 | LL | #[type_length_limit="0100"] fn f() { } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![foo]` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:806:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:805:5 | LL | #[type_length_limit="0100"] fn f() { } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ warning: unused attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:810:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:809:5 | LL | #[type_length_limit="0100"] struct S; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![foo]` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:810:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:809:5 | LL | #[type_length_limit="0100"] struct S; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ warning: unused attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:814:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:813:5 | LL | #[type_length_limit="0100"] type T = S; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![foo]` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:814:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:813:5 | LL | #[type_length_limit="0100"] type T = S; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ warning: unused attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:818:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:817:5 | LL | #[type_length_limit="0100"] impl S { } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![foo]` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:818:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:817:5 | LL | #[type_length_limit="0100"] impl S { } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ -warning: unused attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:798:1 - | -LL | #[type_length_limit="0100"] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![foo]` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:798:1 - | -LL | #[type_length_limit="0100"] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -warning: unused attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:46:1 - | -LL | #![plugin_registrar] - | ^^^^^^^^^^^^^^^^^^^^ - -warning: unused attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:60:1 - | -LL | #![should_panic] - | ^^^^^^^^^^^^^^^^ - -warning: unused attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:61:1 - | -LL | #![ignore] - | ^^^^^^^^^^ - -warning: unused attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:67:1 - | -LL | #![proc_macro_derive()] - | ^^^^^^^^^^^^^^^^^^^^^^^ - warning: 205 warnings emitted diff --git a/src/test/ui/feature-gates/thread-local-const-init.rs b/src/test/ui/feature-gates/thread-local-const-init.rs new file mode 100644 index 0000000000000..6584ffa7cf949 --- /dev/null +++ b/src/test/ui/feature-gates/thread-local-const-init.rs @@ -0,0 +1,4 @@ +thread_local!(static X: u32 = const { 0 }); +//~^ ERROR: use of unstable library feature 'thread_local_const_init' + +fn main() {} diff --git a/src/test/ui/feature-gates/thread-local-const-init.stderr b/src/test/ui/feature-gates/thread-local-const-init.stderr new file mode 100644 index 0000000000000..a35b18a9c290b --- /dev/null +++ b/src/test/ui/feature-gates/thread-local-const-init.stderr @@ -0,0 +1,13 @@ +error[E0658]: use of unstable library feature 'thread_local_const_init' + --> $DIR/thread-local-const-init.rs:1:1 + | +LL | thread_local!(static X: u32 = const { 0 }); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #84223 <https://github.com/rust-lang/rust/issues/84223> for more information + = help: add `#![feature(thread_local_const_init)]` to the crate attributes to enable + = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0658`. diff --git a/src/test/ui/bad/bad-main.rs b/src/test/ui/fn/bad-main.rs similarity index 100% rename from src/test/ui/bad/bad-main.rs rename to src/test/ui/fn/bad-main.rs diff --git a/src/test/ui/bad/bad-main.stderr b/src/test/ui/fn/bad-main.stderr similarity index 100% rename from src/test/ui/bad/bad-main.stderr rename to src/test/ui/fn/bad-main.stderr diff --git a/src/test/ui/issues/issue-45729-unsafe-in-generator.rs b/src/test/ui/generator/issue-45729-unsafe-in-generator.rs similarity index 100% rename from src/test/ui/issues/issue-45729-unsafe-in-generator.rs rename to src/test/ui/generator/issue-45729-unsafe-in-generator.rs diff --git a/src/test/ui/issues/issue-45729-unsafe-in-generator.stderr b/src/test/ui/generator/issue-45729-unsafe-in-generator.stderr similarity index 100% rename from src/test/ui/issues/issue-45729-unsafe-in-generator.stderr rename to src/test/ui/generator/issue-45729-unsafe-in-generator.stderr diff --git a/src/test/ui/generator/layout-error.full_tait.stderr b/src/test/ui/generator/layout-error.full_tait.stderr new file mode 100644 index 0000000000000..805a4d1d000cd --- /dev/null +++ b/src/test/ui/generator/layout-error.full_tait.stderr @@ -0,0 +1,26 @@ +error[E0425]: cannot find value `Foo` in this scope + --> $DIR/layout-error.rs:25:17 + | +LL | let a = Foo; + | ^^^ not found in this scope + +warning: the feature `impl_trait_in_bindings` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/layout-error.rs:8:32 + | +LL | #![cfg_attr(full_tait, feature(impl_trait_in_bindings, type_alias_impl_trait))] + | ^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #63065 <https://github.com/rust-lang/rust/issues/63065> for more information + +warning: the feature `type_alias_impl_trait` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/layout-error.rs:8:56 + | +LL | #![cfg_attr(full_tait, feature(impl_trait_in_bindings, type_alias_impl_trait))] + | ^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #63063 <https://github.com/rust-lang/rust/issues/63063> for more information + +error: aborting due to previous error; 2 warnings emitted + +For more information about this error, try `rustc --explain E0425`. diff --git a/src/test/ui/generator/layout-error.min_tait.stderr b/src/test/ui/generator/layout-error.min_tait.stderr new file mode 100644 index 0000000000000..be469d781b5c6 --- /dev/null +++ b/src/test/ui/generator/layout-error.min_tait.stderr @@ -0,0 +1,40 @@ +error[E0425]: cannot find value `Foo` in this scope + --> $DIR/layout-error.rs:25:17 + | +LL | let a = Foo; + | ^^^ not found in this scope + +error[E0658]: type alias impl trait is not permitted here + --> $DIR/layout-error.rs:31:27 + | +LL | Task::spawn(&POOL, || cb()); + | ^ + | + = note: see issue #63063 <https://github.com/rust-lang/rust/issues/63063> for more information + = help: add `#![feature(type_alias_impl_trait)]` to the crate attributes to enable + +error[E0658]: type alias impl trait is not permitted here + --> $DIR/layout-error.rs:30:28 + | +LL | static POOL: Task<F> = Task::new(); + | ^^^^^^^^^^^ + | + = note: see issue #63065 <https://github.com/rust-lang/rust/issues/63065> for more information + = help: add `#![feature(impl_trait_in_bindings)]` to the crate attributes to enable + +error: concrete type differs from previous defining opaque type use + --> $DIR/layout-error.rs:31:24 + | +LL | Task::spawn(&POOL, || cb()); + | ^^^^^^^ expected `[type error]`, got `impl Future` + | +note: previous use here + --> $DIR/layout-error.rs:30:5 + | +LL | static POOL: Task<F> = Task::new(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 4 previous errors + +Some errors have detailed explanations: E0425, E0658. +For more information about an error, try `rustc --explain E0425`. diff --git a/src/test/ui/generator/layout-error.rs b/src/test/ui/generator/layout-error.rs index 059867277ad43..9f15a6b2eca00 100644 --- a/src/test/ui/generator/layout-error.rs +++ b/src/test/ui/generator/layout-error.rs @@ -3,12 +3,16 @@ // // edition:2018 -#![feature(type_alias_impl_trait)] +// revisions: min_tait full_tait +#![feature(min_type_alias_impl_trait)] +#![cfg_attr(full_tait, feature(impl_trait_in_bindings, type_alias_impl_trait))] +//[full_tait]~^ WARN incomplete +//[full_tait]~| WARN incomplete use std::future::Future; pub struct Task<F: Future>(F); impl<F: Future> Task<F> { - fn new() -> Self { + const fn new() -> Self { todo!() } fn spawn(&self, _: impl FnOnce() -> F) { @@ -23,6 +27,7 @@ fn main() { type F = impl Future; // Check that statics are inhabited computes they layout. - static POOL: Task<F> = Task::new(); - Task::spawn(&POOL, || cb()); + static POOL: Task<F> = Task::new(); //[min_tait]~ ERROR not permitted here + Task::spawn(&POOL, || cb()); //[min_tait]~ ERROR type alias impl trait is not permitted here + //[min_tait]~^ ERROR concrete type differs from previous } diff --git a/src/test/ui/generator/layout-error.stderr b/src/test/ui/generator/layout-error.stderr deleted file mode 100644 index b1a258f4f2ca7..0000000000000 --- a/src/test/ui/generator/layout-error.stderr +++ /dev/null @@ -1,9 +0,0 @@ -error[E0425]: cannot find value `Foo` in this scope - --> $DIR/layout-error.rs:21:17 - | -LL | let a = Foo; - | ^^^ not found in this scope - -error: aborting due to previous error - -For more information about this error, try `rustc --explain E0425`. diff --git a/src/test/ui/generator/metadata-sufficient-for-layout.full_tait.stderr b/src/test/ui/generator/metadata-sufficient-for-layout.full_tait.stderr new file mode 100644 index 0000000000000..ce874c1518c0e --- /dev/null +++ b/src/test/ui/generator/metadata-sufficient-for-layout.full_tait.stderr @@ -0,0 +1,25 @@ +warning: the feature `type_alias_impl_trait` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/metadata-sufficient-for-layout.rs:10:32 + | +LL | #![cfg_attr(full_tait, feature(type_alias_impl_trait, impl_trait_in_bindings))] + | ^^^^^^^^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #63063 <https://github.com/rust-lang/rust/issues/63063> for more information + +warning: the feature `impl_trait_in_bindings` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/metadata-sufficient-for-layout.rs:10:55 + | +LL | #![cfg_attr(full_tait, feature(type_alias_impl_trait, impl_trait_in_bindings))] + | ^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #63065 <https://github.com/rust-lang/rust/issues/63065> for more information + +error: fatal error triggered by #[rustc_error] + --> $DIR/metadata-sufficient-for-layout.rs:29:1 + | +LL | fn main() {} + | ^^^^^^^^^ + +error: aborting due to previous error; 2 warnings emitted + diff --git a/src/test/ui/generator/metadata-sufficient-for-layout.min_tait.stderr b/src/test/ui/generator/metadata-sufficient-for-layout.min_tait.stderr new file mode 100644 index 0000000000000..e2b0d3622a600 --- /dev/null +++ b/src/test/ui/generator/metadata-sufficient-for-layout.min_tait.stderr @@ -0,0 +1,24 @@ +error[E0658]: type alias impl trait is not permitted here + --> $DIR/metadata-sufficient-for-layout.rs:22:23 + | +LL | static A: Option<F> = None; + | ^^^^ + | + = note: see issue #63065 <https://github.com/rust-lang/rust/issues/63065> for more information + = help: add `#![feature(impl_trait_in_bindings)]` to the crate attributes to enable + +error: concrete type differs from previous defining opaque type use + --> $DIR/metadata-sufficient-for-layout.rs:25:1 + | +LL | fn f() -> F { metadata_sufficient_for_layout::g() } + | ^^^^^^^^^^^ expected `[type error]`, got `impl Generator` + | +note: previous use here + --> $DIR/metadata-sufficient-for-layout.rs:22:1 + | +LL | static A: Option<F> = None; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0658`. diff --git a/src/test/ui/generator/metadata-sufficient-for-layout.rs b/src/test/ui/generator/metadata-sufficient-for-layout.rs index 9c82359a0be05..f206093d9710c 100644 --- a/src/test/ui/generator/metadata-sufficient-for-layout.rs +++ b/src/test/ui/generator/metadata-sufficient-for-layout.rs @@ -4,9 +4,12 @@ // Regression test for #80998. // // aux-build:metadata-sufficient-for-layout.rs -// check-pass -#![feature(type_alias_impl_trait)] +// revisions: min_tait full_tait +#![feature(min_type_alias_impl_trait, rustc_attrs)] +#![cfg_attr(full_tait, feature(type_alias_impl_trait, impl_trait_in_bindings))] +//[full_tait]~^ WARN incomplete +//[full_tait]~| WARN incomplete #![feature(generator_trait)] extern crate metadata_sufficient_for_layout; @@ -17,7 +20,10 @@ type F = impl Generator<(), Yield = (), Return = ()>; // Static queries the layout of the generator. static A: Option<F> = None; +//[min_tait]~^ ERROR not permitted here fn f() -> F { metadata_sufficient_for_layout::g() } +//[min_tait]~^ ERROR concrete type differs -fn main() {} +#[rustc_error] +fn main() {} //[full_tait]~ ERROR diff --git a/src/test/ui/generator/print/generator-print-verbose-1.stderr b/src/test/ui/generator/print/generator-print-verbose-1.stderr index b5c63584c6c14..78100318dc33a 100644 --- a/src/test/ui/generator/print/generator-print-verbose-1.stderr +++ b/src/test/ui/generator/print/generator-print-verbose-1.stderr @@ -12,7 +12,7 @@ note: generator is not `Send` as this value is used across a yield --> $DIR/generator-print-verbose-1.rs:35:9 | LL | let _non_send_gen = make_non_send_generator(); - | ------------- has type `Opaque(DefId(0:24 ~ generator_print_verbose_1[317d]::make_non_send_generator::{opaque#0}), [])` which is not `Send` + | ------------- has type `Opaque(DefId(0:34 ~ generator_print_verbose_1[317d]::make_non_send_generator::{opaque#0}), [])` which is not `Send` LL | yield; | ^^^^^ yield occurs here, with `_non_send_gen` maybe used later LL | }; @@ -30,10 +30,10 @@ LL | require_send(send_gen); = help: the trait `Sync` is not implemented for `RefCell<i32>` = note: required because of the requirements on the impl of `Send` for `Arc<RefCell<i32>>` = note: required because it appears within the type `[make_gen2<Arc<RefCell<i32>>>::{closure#0} upvar_tys=(Arc<RefCell<i32>>) {()}]` - = note: required because it appears within the type `Opaque(DefId(0:29 ~ generator_print_verbose_1[317d]::make_gen2::{opaque#0}), [std::sync::Arc<std::cell::RefCell<i32>>])` - = note: required because it appears within the type `Opaque(DefId(0:32 ~ generator_print_verbose_1[317d]::make_non_send_generator2::{opaque#0}), [])` - = note: required because it appears within the type `{Opaque(DefId(0:32 ~ generator_print_verbose_1[317d]::make_non_send_generator2::{opaque#0}), []), ()}` - = note: required because it appears within the type `[test2::{closure#0} upvar_tys=() {Opaque(DefId(0:32 ~ generator_print_verbose_1[317d]::make_non_send_generator2::{opaque#0}), []), ()}]` + = note: required because it appears within the type `Opaque(DefId(0:39 ~ generator_print_verbose_1[317d]::make_gen2::{opaque#0}), [std::sync::Arc<std::cell::RefCell<i32>>])` + = note: required because it appears within the type `Opaque(DefId(0:42 ~ generator_print_verbose_1[317d]::make_non_send_generator2::{opaque#0}), [])` + = note: required because it appears within the type `{Opaque(DefId(0:42 ~ generator_print_verbose_1[317d]::make_non_send_generator2::{opaque#0}), []), ()}` + = note: required because it appears within the type `[test2::{closure#0} upvar_tys=() {Opaque(DefId(0:42 ~ generator_print_verbose_1[317d]::make_non_send_generator2::{opaque#0}), []), ()}]` error: aborting due to 2 previous errors diff --git a/src/test/ui/generator/static-not-unpin.stderr b/src/test/ui/generator/static-not-unpin.stderr index 881064d2f8418..74ac53a7f9407 100644 --- a/src/test/ui/generator/static-not-unpin.stderr +++ b/src/test/ui/generator/static-not-unpin.stderr @@ -6,6 +6,8 @@ LL | fn assert_unpin<T: Unpin>(_: T) { ... LL | assert_unpin(generator); | ^^^^^^^^^ the trait `Unpin` is not implemented for `[static generator@$DIR/static-not-unpin.rs:11:25: 13:6]` + | + = note: consider using `Box::pin` error: aborting due to previous error diff --git a/src/test/ui/generator/type-mismatch-signature-deduction.stderr b/src/test/ui/generator/type-mismatch-signature-deduction.stderr index 30e23ea8f650c..3f1f33a3b123f 100644 --- a/src/test/ui/generator/type-mismatch-signature-deduction.stderr +++ b/src/test/ui/generator/type-mismatch-signature-deduction.stderr @@ -16,10 +16,10 @@ error[E0271]: type mismatch resolving `<[generator@$DIR/type-mismatch-signature- --> $DIR/type-mismatch-signature-deduction.rs:5:13 | LL | fn foo() -> impl Generator<Return = i32> { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected enum `Result`, found `i32` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `i32`, found enum `Result` | - = note: expected enum `Result<{integer}, _>` - found type `i32` + = note: expected type `i32` + found enum `Result<{integer}, _>` error: aborting due to 2 previous errors diff --git a/src/test/ui/generic-associated-types/impl_bounds.rs b/src/test/ui/generic-associated-types/impl_bounds.rs index 089a214667ea2..50e1c50644e35 100644 --- a/src/test/ui/generic-associated-types/impl_bounds.rs +++ b/src/test/ui/generic-associated-types/impl_bounds.rs @@ -6,6 +6,7 @@ trait Foo { type A<'a> where Self: 'a; type B<'a, 'b> where 'a: 'b; type C where Self: Clone; + fn d() where Self: Clone; } #[derive(Copy, Clone)] @@ -19,6 +20,8 @@ impl<T> Foo for Fooy<T> { //~| ERROR lifetime bound not satisfied type C where Self: Copy = String; //~^ ERROR the trait bound `T: Copy` is not satisfied + fn d() where Self: Copy {} + //~^ ERROR the trait bound `T: Copy` is not satisfied } fn main() {} diff --git a/src/test/ui/generic-associated-types/impl_bounds.stderr b/src/test/ui/generic-associated-types/impl_bounds.stderr index 645d292714562..58bcb13e68ebd 100644 --- a/src/test/ui/generic-associated-types/impl_bounds.stderr +++ b/src/test/ui/generic-associated-types/impl_bounds.stderr @@ -1,5 +1,5 @@ error[E0310]: the parameter type `T` may not live long enough - --> $DIR/impl_bounds.rs:15:5 + --> $DIR/impl_bounds.rs:16:5 | LL | type A<'a> where Self: 'static = (&'a ()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -8,53 +8,80 @@ LL | type A<'a> where Self: 'static = (&'a ()); = note: ...so that the type `Fooy<T>` will meet its required lifetime bounds error[E0478]: lifetime bound not satisfied - --> $DIR/impl_bounds.rs:17:5 + --> $DIR/impl_bounds.rs:18:5 | LL | type B<'a, 'b> where 'b: 'a = (&'a(), &'b ()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | -note: lifetime parameter instantiated with the lifetime `'b` as defined on the associated item at 17:16 - --> $DIR/impl_bounds.rs:17:16 +note: lifetime parameter instantiated with the lifetime `'b` as defined on the associated item at 18:16 + --> $DIR/impl_bounds.rs:18:16 | LL | type B<'a, 'b> where 'b: 'a = (&'a(), &'b ()); | ^^ -note: but lifetime parameter must outlive the lifetime `'a` as defined on the associated item at 17:12 - --> $DIR/impl_bounds.rs:17:12 +note: but lifetime parameter must outlive the lifetime `'a` as defined on the associated item at 18:12 + --> $DIR/impl_bounds.rs:18:12 | LL | type B<'a, 'b> where 'b: 'a = (&'a(), &'b ()); | ^^ error[E0478]: lifetime bound not satisfied - --> $DIR/impl_bounds.rs:17:5 + --> $DIR/impl_bounds.rs:18:5 | LL | type B<'a, 'b> where 'b: 'a = (&'a(), &'b ()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | -note: lifetime parameter instantiated with the lifetime `'a` as defined on the associated item at 17:12 - --> $DIR/impl_bounds.rs:17:12 +note: lifetime parameter instantiated with the lifetime `'a` as defined on the associated item at 18:12 + --> $DIR/impl_bounds.rs:18:12 | LL | type B<'a, 'b> where 'b: 'a = (&'a(), &'b ()); | ^^ -note: but lifetime parameter must outlive the lifetime `'b` as defined on the associated item at 17:16 - --> $DIR/impl_bounds.rs:17:16 +note: but lifetime parameter must outlive the lifetime `'b` as defined on the associated item at 18:16 + --> $DIR/impl_bounds.rs:18:16 | LL | type B<'a, 'b> where 'b: 'a = (&'a(), &'b ()); | ^^ error[E0277]: the trait bound `T: Copy` is not satisfied - --> $DIR/impl_bounds.rs:20:5 + --> $DIR/impl_bounds.rs:21:5 | LL | type C where Self: Copy = String; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Copy` is not implemented for `T` | = note: required because of the requirements on the impl of `Copy` for `Fooy<T>` - = note: the requirement `Fooy<T>: Copy` appears on the associated impl type but not on the corresponding associated trait type +note: the requirement `Fooy<T>: Copy` appears on the associated impl type `C` but not on the corresponding associated trait type + --> $DIR/impl_bounds.rs:8:5 + | +LL | trait Foo { + | --- in this trait +... +LL | type C where Self: Clone; + | ^^^^^^^^^^^^^^^^^^^^^^^^^ this trait associated type doesn't have the requirement `Fooy<T>: Copy` +help: consider restricting type parameter `T` + | +LL | impl<T: std::marker::Copy> Foo for Fooy<T> { + | ^^^^^^^^^^^^^^^^^^^ + +error[E0277]: the trait bound `T: Copy` is not satisfied + --> $DIR/impl_bounds.rs:23:5 + | +LL | fn d() where Self: Copy {} + | ^^^^^^^^^^^^^^^^^^^^^^^ the trait `Copy` is not implemented for `T` + | + = note: required because of the requirements on the impl of `Copy` for `Fooy<T>` +note: the requirement `Fooy<T>: Copy` appears on the impl method `d` but not on the corresponding trait method + --> $DIR/impl_bounds.rs:9:8 + | +LL | trait Foo { + | --- in this trait +... +LL | fn d() where Self: Clone; + | ^ this trait method doesn't have the requirement `Fooy<T>: Copy` help: consider restricting type parameter `T` | -LL | impl<T: Copy> Foo for Fooy<T> { - | ^^^^^^ +LL | impl<T: std::marker::Copy> Foo for Fooy<T> { + | ^^^^^^^^^^^^^^^^^^^ -error: aborting due to 4 previous errors +error: aborting due to 5 previous errors Some errors have detailed explanations: E0277, E0310, E0478. For more information about an error, try `rustc --explain E0277`. diff --git a/src/test/ui/generic-associated-types/issue-68641-check-gat-bounds.stderr b/src/test/ui/generic-associated-types/issue-68641-check-gat-bounds.stderr index b380f0da2ea40..c92800c3746ad 100644 --- a/src/test/ui/generic-associated-types/issue-68641-check-gat-bounds.stderr +++ b/src/test/ui/generic-associated-types/issue-68641-check-gat-bounds.stderr @@ -18,8 +18,8 @@ LL | type Item<'a> = T; | help: consider restricting type parameter `T` | -LL | impl<T: Copy> UnsafeCopy for T { - | ^^^^^^ +LL | impl<T: std::marker::Copy> UnsafeCopy for T { + | ^^^^^^^^^^^^^^^^^^^ error: aborting due to previous error; 1 warning emitted diff --git a/src/test/ui/generic-associated-types/issue-68642-broken-llvm-ir.stderr b/src/test/ui/generic-associated-types/issue-68642-broken-llvm-ir.stderr index 61950478c32a8..e44547b10c17b 100644 --- a/src/test/ui/generic-associated-types/issue-68642-broken-llvm-ir.stderr +++ b/src/test/ui/generic-associated-types/issue-68642-broken-llvm-ir.stderr @@ -19,8 +19,8 @@ LL | type F<'a> = Self; = note: wrap the `T` in a closure with no arguments: `|| { /* code */ }` help: consider restricting type parameter `T` | -LL | impl<T: Fn<()>> Fun for T { - | ^^^^^^^^ +LL | impl<T: std::ops::Fn<()>> Fun for T { + | ^^^^^^^^^^^^^^^^^^ error: aborting due to previous error; 1 warning emitted diff --git a/src/test/ui/generic-associated-types/issue-68643-broken-mir.stderr b/src/test/ui/generic-associated-types/issue-68643-broken-mir.stderr index 13980618987be..fd0b4733d9359 100644 --- a/src/test/ui/generic-associated-types/issue-68643-broken-mir.stderr +++ b/src/test/ui/generic-associated-types/issue-68643-broken-mir.stderr @@ -19,8 +19,8 @@ LL | type F<'a> = Self; = note: wrap the `T` in a closure with no arguments: `|| { /* code */ }` help: consider restricting type parameter `T` | -LL | impl<T: Fn<()>> Fun for T { - | ^^^^^^^^ +LL | impl<T: std::ops::Fn<()>> Fun for T { + | ^^^^^^^^^^^^^^^^^^ error: aborting due to previous error; 1 warning emitted diff --git a/src/test/ui/generic-associated-types/issue-68644-codegen-selection.stderr b/src/test/ui/generic-associated-types/issue-68644-codegen-selection.stderr index 8112425146959..0c23c870f0106 100644 --- a/src/test/ui/generic-associated-types/issue-68644-codegen-selection.stderr +++ b/src/test/ui/generic-associated-types/issue-68644-codegen-selection.stderr @@ -19,8 +19,8 @@ LL | type F<'a> = Self; = note: wrap the `T` in a closure with no arguments: `|| { /* code */ }` help: consider restricting type parameter `T` | -LL | impl<T: Fn<()>> Fun for T { - | ^^^^^^^^ +LL | impl<T: std::ops::Fn<()>> Fun for T { + | ^^^^^^^^^^^^^^^^^^ error: aborting due to previous error; 1 warning emitted diff --git a/src/test/ui/generic-associated-types/issue-68645-codegen-fulfillment.stderr b/src/test/ui/generic-associated-types/issue-68645-codegen-fulfillment.stderr index 22f50b3949828..85d8d3f8e936e 100644 --- a/src/test/ui/generic-associated-types/issue-68645-codegen-fulfillment.stderr +++ b/src/test/ui/generic-associated-types/issue-68645-codegen-fulfillment.stderr @@ -19,8 +19,8 @@ LL | type F<'a> = Self; = note: wrap the `T` in a closure with no arguments: `|| { /* code */ }` help: consider restricting type parameter `T` | -LL | impl<T: Fn<()>> Fun for T { - | ^^^^^^^^ +LL | impl<T: std::ops::Fn<()>> Fun for T { + | ^^^^^^^^^^^^^^^^^^ error: aborting due to previous error; 1 warning emitted diff --git a/src/test/ui/generic-associated-types/issue-70303.rs b/src/test/ui/generic-associated-types/issue-70303.rs new file mode 100644 index 0000000000000..a1cb2295b639e --- /dev/null +++ b/src/test/ui/generic-associated-types/issue-70303.rs @@ -0,0 +1,60 @@ +// check-pass + +#![allow(incomplete_features)] +#![feature(generic_associated_types)] + +trait Document { + type Cursor<'a>: DocCursor<'a>; + + fn cursor(&self) -> Self::Cursor<'_>; +} + +struct DocumentImpl {} + +impl Document for DocumentImpl { + type Cursor<'a> = DocCursorImpl<'a>; + + fn cursor(&self) -> Self::Cursor<'_> { + DocCursorImpl { + document: &self, + } + } +} + + +trait DocCursor<'a> {} + +struct DocCursorImpl<'a> { + document: &'a DocumentImpl, +} + +impl<'a> DocCursor<'a> for DocCursorImpl<'a> {} + +struct Lexer<'d, Cursor> +where + Cursor: DocCursor<'d>, +{ + cursor: Cursor, + _phantom: std::marker::PhantomData<&'d ()>, +} + + +impl<'d, Cursor> Lexer<'d, Cursor> +where + Cursor: DocCursor<'d>, +{ + pub fn from<Doc>(document: &'d Doc) -> Lexer<'d, Cursor> + where + Doc: Document<Cursor<'d> = Cursor>, + { + Lexer { + cursor: document.cursor(), + _phantom: std::marker::PhantomData, + } + } +} + +pub fn main() { + let doc = DocumentImpl {}; + let lexer: Lexer<'_, DocCursorImpl<'_>> = Lexer::from(&doc); +} diff --git a/src/test/ui/generic-associated-types/issue-70304.rs b/src/test/ui/generic-associated-types/issue-70304.rs new file mode 100644 index 0000000000000..225f61d132ee6 --- /dev/null +++ b/src/test/ui/generic-associated-types/issue-70304.rs @@ -0,0 +1,63 @@ +#![allow(incomplete_features)] +#![feature(generic_associated_types)] + +trait Document { + type Cursor<'a>: DocCursor<'a>; + + fn cursor(&self) -> Self::Cursor<'_>; +} + +struct DocumentImpl {} + +impl Document for DocumentImpl { + type Cursor<'a> = DocCursorImpl<'a>; + + fn cursor(&self) -> Self::Cursor<'_> { + DocCursorImpl { + document: &self, + } + } +} + + +trait DocCursor<'a> {} + +struct DocCursorImpl<'a> { + document: &'a DocumentImpl, +} + +impl<'a> DocCursor<'a> for DocCursorImpl<'a> {} + +struct Lexer<'d, Cursor> +where + Cursor: DocCursor<'d>, +{ + cursor: Cursor, + _phantom: std::marker::PhantomData<&'d ()>, +} + + +impl<'d, Cursor> Lexer<'d, Cursor> +where + Cursor: DocCursor<'d>, +{ + pub fn from<Doc>(document: &'d Doc) -> Lexer<'d, Cursor> + where + Doc: Document<Cursor<'d> = Cursor>, + { + Lexer { + cursor: document.cursor(), + _phantom: std::marker::PhantomData, + } + } +} + +fn create_doc() -> impl Document<Cursor<'_> = DocCursorImpl<'_>> { + //~^ ERROR: missing lifetime specifier + DocumentImpl {} +} + +pub fn main() { + let doc = create_doc(); + let lexer: Lexer<'_, DocCursorImpl<'_>> = Lexer::from(&doc); +} diff --git a/src/test/ui/generic-associated-types/issue-70304.stderr b/src/test/ui/generic-associated-types/issue-70304.stderr new file mode 100644 index 0000000000000..dfa86018976dc --- /dev/null +++ b/src/test/ui/generic-associated-types/issue-70304.stderr @@ -0,0 +1,15 @@ +error[E0106]: missing lifetime specifier + --> $DIR/issue-70304.rs:55:41 + | +LL | fn create_doc() -> impl Document<Cursor<'_> = DocCursorImpl<'_>> { + | ^^ expected named lifetime parameter + | + = help: this function's return type contains a borrowed value, but there is no value for it to be borrowed from +help: consider using the `'static` lifetime + | +LL | fn create_doc() -> impl Document<Cursor<'static> = DocCursorImpl<'_>> { + | ^^^^^^^ + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0106`. diff --git a/src/test/ui/generic-associated-types/issue-71176.rs b/src/test/ui/generic-associated-types/issue-71176.rs new file mode 100644 index 0000000000000..470476bf476a0 --- /dev/null +++ b/src/test/ui/generic-associated-types/issue-71176.rs @@ -0,0 +1,21 @@ +#![allow(incomplete_features)] +#![feature(generic_associated_types)] + +trait Provider { + type A<'a>; + //~^ ERROR: missing generics for associated type +} + +impl Provider for () { + type A<'a> = (); +} + +struct Holder<B> { + inner: Box<dyn Provider<A = B>>, +} + +fn main() { + Holder { + inner: Box::new(()), + }; +} diff --git a/src/test/ui/generic-associated-types/issue-71176.stderr b/src/test/ui/generic-associated-types/issue-71176.stderr new file mode 100644 index 0000000000000..dd19dd4ad8e83 --- /dev/null +++ b/src/test/ui/generic-associated-types/issue-71176.stderr @@ -0,0 +1,19 @@ +error[E0107]: missing generics for associated type `Provider::A` + --> $DIR/issue-71176.rs:5:10 + | +LL | type A<'a>; + | ^ expected 1 lifetime argument + | +note: associated type defined here, with 1 lifetime parameter: `'a` + --> $DIR/issue-71176.rs:5:10 + | +LL | type A<'a>; + | ^ -- +help: use angle brackets to add missing lifetime argument + | +LL | type A<'a><'a>; + | ^^^^ + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0107`. diff --git a/src/test/ui/generic-associated-types/issue-74824.stderr b/src/test/ui/generic-associated-types/issue-74824.stderr index 34a2c1932ebcc..7a7b5fd4f1c55 100644 --- a/src/test/ui/generic-associated-types/issue-74824.stderr +++ b/src/test/ui/generic-associated-types/issue-74824.stderr @@ -19,8 +19,8 @@ LL | type Copy<T>: Copy = Box<T>; = note: required because of the requirements on the impl of `Clone` for `Box<T>` help: consider restricting type parameter `T` | -LL | type Copy<T: Clone>: Copy = Box<T>; - | ^^^^^^^ +LL | type Copy<T: std::clone::Clone>: Copy = Box<T>; + | ^^^^^^^^^^^^^^^^^^^ error: aborting due to 2 previous errors diff --git a/src/test/ui/generic-associated-types/issue-78671.rs b/src/test/ui/generic-associated-types/issue-78671.rs new file mode 100644 index 0000000000000..1b02aac8bcb24 --- /dev/null +++ b/src/test/ui/generic-associated-types/issue-78671.rs @@ -0,0 +1,14 @@ +#![allow(incomplete_features)] +#![feature(generic_associated_types)] + +trait CollectionFamily { + type Member<T>; + //~^ ERROR: missing generics for associated type +} +fn floatify() { + Box::new(Family) as &dyn CollectionFamily<Member=usize> +} + +struct Family; + +fn main() {} diff --git a/src/test/ui/generic-associated-types/issue-78671.stderr b/src/test/ui/generic-associated-types/issue-78671.stderr new file mode 100644 index 0000000000000..7a9aced5beab8 --- /dev/null +++ b/src/test/ui/generic-associated-types/issue-78671.stderr @@ -0,0 +1,19 @@ +error[E0107]: missing generics for associated type `CollectionFamily::Member` + --> $DIR/issue-78671.rs:5:10 + | +LL | type Member<T>; + | ^^^^^^ expected 1 type argument + | +note: associated type defined here, with 1 type parameter: `T` + --> $DIR/issue-78671.rs:5:10 + | +LL | type Member<T>; + | ^^^^^^ - +help: use angle brackets to add missing type argument + | +LL | type Member<T><T>; + | ^^^ + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0107`. diff --git a/src/test/ui/generic-associated-types/issue-79636-1.rs b/src/test/ui/generic-associated-types/issue-79636-1.rs new file mode 100644 index 0000000000000..17f9387e29204 --- /dev/null +++ b/src/test/ui/generic-associated-types/issue-79636-1.rs @@ -0,0 +1,24 @@ +#![allow(incomplete_features)] +#![feature(generic_associated_types)] + +trait Monad { + type Unwrapped; + type Wrapped<B>; + //~^ ERROR: missing generics for associated type `Monad::Wrapped` + + fn bind<B, F>(self, f: F) -> Self::Wrapped<B> { + todo!() + } +} + +fn join<MOuter, MInner, A>(outer: MOuter) -> MOuter::Wrapped<A> +where + MOuter: Monad<Unwrapped = MInner>, + MInner: Monad<Unwrapped = A, Wrapped = MOuter::Wrapped<A>>, +{ + outer.bind(|inner| inner) +} + +fn main() { + assert_eq!(join(Some(Some(true))), Some(true)); +} diff --git a/src/test/ui/generic-associated-types/issue-79636-1.stderr b/src/test/ui/generic-associated-types/issue-79636-1.stderr new file mode 100644 index 0000000000000..58eeb43f70d66 --- /dev/null +++ b/src/test/ui/generic-associated-types/issue-79636-1.stderr @@ -0,0 +1,19 @@ +error[E0107]: missing generics for associated type `Monad::Wrapped` + --> $DIR/issue-79636-1.rs:6:10 + | +LL | type Wrapped<B>; + | ^^^^^^^ expected 1 type argument + | +note: associated type defined here, with 1 type parameter: `B` + --> $DIR/issue-79636-1.rs:6:10 + | +LL | type Wrapped<B>; + | ^^^^^^^ - +help: use angle brackets to add missing type argument + | +LL | type Wrapped<B><B>; + | ^^^ + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0107`. diff --git a/src/test/ui/generic-associated-types/issue-79636-2.rs b/src/test/ui/generic-associated-types/issue-79636-2.rs new file mode 100644 index 0000000000000..5a6542193752b --- /dev/null +++ b/src/test/ui/generic-associated-types/issue-79636-2.rs @@ -0,0 +1,18 @@ +#![allow(incomplete_features)] +#![feature(generic_associated_types)] + +trait SomeTrait { + type Wrapped<A>: SomeTrait; + //~^ ERROR: missing generics for associated type `SomeTrait::Wrapped` + + fn f() -> (); +} + +fn program<W>() -> () +where + W: SomeTrait<Wrapped = W>, +{ + return W::f(); +} + +fn main() {} diff --git a/src/test/ui/generic-associated-types/issue-79636-2.stderr b/src/test/ui/generic-associated-types/issue-79636-2.stderr new file mode 100644 index 0000000000000..d5e3c56ebb9ef --- /dev/null +++ b/src/test/ui/generic-associated-types/issue-79636-2.stderr @@ -0,0 +1,19 @@ +error[E0107]: missing generics for associated type `SomeTrait::Wrapped` + --> $DIR/issue-79636-2.rs:5:10 + | +LL | type Wrapped<A>: SomeTrait; + | ^^^^^^^ expected 1 type argument + | +note: associated type defined here, with 1 type parameter: `A` + --> $DIR/issue-79636-2.rs:5:10 + | +LL | type Wrapped<A>: SomeTrait; + | ^^^^^^^ - +help: use angle brackets to add missing type argument + | +LL | type Wrapped<A><A>: SomeTrait; + | ^^^ + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0107`. diff --git a/src/test/ui/generic-associated-types/issue-81712-cyclic-traits.rs b/src/test/ui/generic-associated-types/issue-81712-cyclic-traits.rs new file mode 100644 index 0000000000000..934870afc119c --- /dev/null +++ b/src/test/ui/generic-associated-types/issue-81712-cyclic-traits.rs @@ -0,0 +1,21 @@ +// Regression test for #81712. + +#![feature(generic_associated_types)] +#![allow(incomplete_features)] + +trait A { + type BType: B<AType = Self>; +} + +trait B { + type AType: A<BType = Self>; +} +trait C { + type DType<T>: D<T, CType = Self>; + //~^ ERROR: missing generics for associated type `C::DType` [E0107] +} +trait D<T> { + type CType: C<DType = Self>; +} + +fn main() {} diff --git a/src/test/ui/generic-associated-types/issue-81712-cyclic-traits.stderr b/src/test/ui/generic-associated-types/issue-81712-cyclic-traits.stderr new file mode 100644 index 0000000000000..75f68cd314893 --- /dev/null +++ b/src/test/ui/generic-associated-types/issue-81712-cyclic-traits.stderr @@ -0,0 +1,19 @@ +error[E0107]: missing generics for associated type `C::DType` + --> $DIR/issue-81712-cyclic-traits.rs:14:10 + | +LL | type DType<T>: D<T, CType = Self>; + | ^^^^^ expected 1 type argument + | +note: associated type defined here, with 1 type parameter: `T` + --> $DIR/issue-81712-cyclic-traits.rs:14:10 + | +LL | type DType<T>: D<T, CType = Self>; + | ^^^^^ - +help: use angle brackets to add missing type argument + | +LL | type DType<T><T>: D<T, CType = Self>; + | ^^^ + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0107`. diff --git a/src/test/ui/generic-associated-types/shadowing.stderr b/src/test/ui/generic-associated-types/shadowing.stderr index 95cebbb868115..d45ef83873af7 100644 --- a/src/test/ui/generic-associated-types/shadowing.stderr +++ b/src/test/ui/generic-associated-types/shadowing.stderr @@ -14,14 +14,6 @@ LL | impl<T> NoShadowT<T> for Option<T> { LL | type Bar<T> = i32; | ^ already used -error[E0496]: lifetime name `'a` shadows a lifetime name that is already in scope - --> $DIR/shadowing.rs:5:14 - | -LL | trait Shadow<'a> { - | -- first declared here -LL | type Bar<'a>; - | ^^ lifetime `'a` already in scope - error[E0496]: lifetime name `'a` shadows a lifetime name that is already in scope --> $DIR/shadowing.rs:14:14 | @@ -30,6 +22,14 @@ LL | impl<'a> NoShadow<'a> for &'a u32 { LL | type Bar<'a> = i32; | ^^ lifetime `'a` already in scope +error[E0496]: lifetime name `'a` shadows a lifetime name that is already in scope + --> $DIR/shadowing.rs:5:14 + | +LL | trait Shadow<'a> { + | -- first declared here +LL | type Bar<'a>; + | ^^ lifetime `'a` already in scope + error: aborting due to 4 previous errors Some errors have detailed explanations: E0403, E0496. diff --git a/src/test/ui/bad/bad-mid-path-type-params.rs b/src/test/ui/generics/bad-mid-path-type-params.rs similarity index 100% rename from src/test/ui/bad/bad-mid-path-type-params.rs rename to src/test/ui/generics/bad-mid-path-type-params.rs diff --git a/src/test/ui/bad/bad-mid-path-type-params.stderr b/src/test/ui/generics/bad-mid-path-type-params.stderr similarity index 100% rename from src/test/ui/bad/bad-mid-path-type-params.stderr rename to src/test/ui/generics/bad-mid-path-type-params.stderr diff --git a/src/test/ui/generics/generic-non-trailing-defaults.rs b/src/test/ui/generics/generic-non-trailing-defaults.rs index 8d9883dc7a939..cb2bb2832b70e 100644 --- a/src/test/ui/generics/generic-non-trailing-defaults.rs +++ b/src/test/ui/generics/generic-non-trailing-defaults.rs @@ -1,10 +1,9 @@ struct Heap; struct Vec<A = Heap, T>(A, T); -//~^ ERROR type parameters with a default must be trailing +//~^ ERROR generic parameters with a default must be trailing struct Foo<A, B = Vec<C>, C>(A, B, C); -//~^ ERROR type parameters with a default must be trailing -//~| ERROR type parameters with a default cannot use forward declared identifiers +//~^ ERROR generic parameters with a default must be trailing fn main() {} diff --git a/src/test/ui/generics/generic-non-trailing-defaults.stderr b/src/test/ui/generics/generic-non-trailing-defaults.stderr index fb359f673a95e..6d76861750335 100644 --- a/src/test/ui/generics/generic-non-trailing-defaults.stderr +++ b/src/test/ui/generics/generic-non-trailing-defaults.stderr @@ -1,21 +1,14 @@ -error: type parameters with a default must be trailing +error: generic parameters with a default must be trailing --> $DIR/generic-non-trailing-defaults.rs:3:12 | LL | struct Vec<A = Heap, T>(A, T); | ^ -error: type parameters with a default must be trailing +error: generic parameters with a default must be trailing --> $DIR/generic-non-trailing-defaults.rs:6:15 | LL | struct Foo<A, B = Vec<C>, C>(A, B, C); | ^ -error[E0128]: type parameters with a default cannot use forward declared identifiers - --> $DIR/generic-non-trailing-defaults.rs:6:23 - | -LL | struct Foo<A, B = Vec<C>, C>(A, B, C); - | ^ defaulted type parameters cannot be forward declared - -error: aborting due to 3 previous errors +error: aborting due to 2 previous errors -For more information about this error, try `rustc --explain E0128`. diff --git a/src/test/ui/generics/generic-type-params-forward-mention.rs b/src/test/ui/generics/generic-type-params-forward-mention.rs index ac0cab20d78a2..000c47095d27c 100644 --- a/src/test/ui/generics/generic-type-params-forward-mention.rs +++ b/src/test/ui/generics/generic-type-params-forward-mention.rs @@ -1,6 +1,6 @@ // Ensure that we get an error and not an ICE for this problematic case. struct Foo<T = Option<U>, U = bool>(T, U); -//~^ ERROR type parameters with a default cannot use forward declared identifiers +//~^ ERROR generic parameters with a default cannot use forward declared identifiers fn main() { let x: Foo; } diff --git a/src/test/ui/generics/generic-type-params-forward-mention.stderr b/src/test/ui/generics/generic-type-params-forward-mention.stderr index 97389c337fcbe..fa661c274ebca 100644 --- a/src/test/ui/generics/generic-type-params-forward-mention.stderr +++ b/src/test/ui/generics/generic-type-params-forward-mention.stderr @@ -1,8 +1,8 @@ -error[E0128]: type parameters with a default cannot use forward declared identifiers +error[E0128]: generic parameters with a default cannot use forward declared identifiers --> $DIR/generic-type-params-forward-mention.rs:2:23 | LL | struct Foo<T = Option<U>, U = bool>(T, U); - | ^ defaulted type parameters cannot be forward declared + | ^ defaulted generic parameters cannot be forward declared error: aborting due to previous error diff --git a/src/test/ui/generics/issue-65285-incorrect-explicit-lifetime-name-needed.stderr b/src/test/ui/generics/issue-65285-incorrect-explicit-lifetime-name-needed.stderr index 1106a06782280..ce853d4d36ddd 100644 --- a/src/test/ui/generics/issue-65285-incorrect-explicit-lifetime-name-needed.stderr +++ b/src/test/ui/generics/issue-65285-incorrect-explicit-lifetime-name-needed.stderr @@ -4,6 +4,17 @@ error[E0637]: `&` without an explicit lifetime name cannot be used here LL | fn should_error<T>() where T : Into<&u32> {} | ^ explicit lifetime name needed here +error[E0106]: missing lifetime specifier + --> $DIR/issue-65285-incorrect-explicit-lifetime-name-needed.rs:13:17 + | +LL | fn bar<'b, L: X<&'b Nested<i32>>>(){} + | ^ expected named lifetime parameter + | +help: consider using the `'b` lifetime + | +LL | fn bar<'b, L: X<'b, &'b Nested<i32>>>(){} + | ^^^ + error[E0106]: missing lifetime specifier --> $DIR/issue-65285-incorrect-explicit-lifetime-name-needed.rs:9:21 | @@ -22,17 +33,6 @@ help: consider using one of the available lifetimes here LL | fn foo<'b, L: X<'lifetime, &'b Nested<K>>>(); | ^^^^^^^^^^ -error[E0106]: missing lifetime specifier - --> $DIR/issue-65285-incorrect-explicit-lifetime-name-needed.rs:13:17 - | -LL | fn bar<'b, L: X<&'b Nested<i32>>>(){} - | ^ expected named lifetime parameter - | -help: consider using the `'b` lifetime - | -LL | fn bar<'b, L: X<'b, &'b Nested<i32>>>(){} - | ^^^ - error: aborting due to 3 previous errors Some errors have detailed explanations: E0106, E0637. diff --git a/src/test/ui/generics/wrong-number-of-args.stderr b/src/test/ui/generics/wrong-number-of-args.stderr index 73bd76aa5fad0..94fdd355d48ca 100644 --- a/src/test/ui/generics/wrong-number-of-args.stderr +++ b/src/test/ui/generics/wrong-number-of-args.stderr @@ -1,36 +1,3 @@ -error[E0106]: missing lifetime specifier - --> $DIR/wrong-number-of-args.rs:44:14 - | -LL | type A = Ty; - | ^^ expected named lifetime parameter - | -help: consider introducing a named lifetime parameter - | -LL | type A<'a> = Ty<'a>; - | ^^^^ ^^^^^^ - -error[E0106]: missing lifetime specifier - --> $DIR/wrong-number-of-args.rs:54:17 - | -LL | type C = Ty<usize>; - | ^ expected named lifetime parameter - | -help: consider introducing a named lifetime parameter - | -LL | type C<'a> = Ty<'a, usize>; - | ^^^^ ^^^ - -error[E0106]: missing lifetime specifier - --> $DIR/wrong-number-of-args.rs:100:22 - | -LL | type B = Box<dyn GenericLifetime>; - | ^^^^^^^^^^^^^^^ expected named lifetime parameter - | -help: consider introducing a named lifetime parameter - | -LL | type B<'a> = Box<dyn GenericLifetime<'a>>; - | ^^^^ ^^^^^^^^^^^^^^^^^^^ - error[E0107]: this struct takes 0 lifetime arguments but 1 lifetime argument was supplied --> $DIR/wrong-number-of-args.rs:6:14 | @@ -165,6 +132,17 @@ help: use angle brackets to add missing type argument LL | type A = Ty<T>; | ^^^ +error[E0106]: missing lifetime specifier + --> $DIR/wrong-number-of-args.rs:44:14 + | +LL | type A = Ty; + | ^^ expected named lifetime parameter + | +help: consider introducing a named lifetime parameter + | +LL | type A<'a> = Ty<'a>; + | ^^^^ ^^^^^^ + error[E0107]: this struct takes 1 type argument but 0 type arguments were supplied --> $DIR/wrong-number-of-args.rs:50:14 | @@ -181,6 +159,17 @@ help: add missing type argument LL | type B = Ty<'static, T>; | ^^^ +error[E0106]: missing lifetime specifier + --> $DIR/wrong-number-of-args.rs:54:17 + | +LL | type C = Ty<usize>; + | ^ expected named lifetime parameter + | +help: consider introducing a named lifetime parameter + | +LL | type C<'a> = Ty<'a, usize>; + | ^^^^ ^^^ + error[E0107]: missing generics for struct `type_and_type_and_type::Ty` --> $DIR/wrong-number-of-args.rs:64:14 | @@ -243,6 +232,17 @@ note: trait defined here, with 0 type parameters LL | trait NonGeneric { | ^^^^^^^^^^ +error[E0106]: missing lifetime specifier + --> $DIR/wrong-number-of-args.rs:100:22 + | +LL | type B = Box<dyn GenericLifetime>; + | ^^^^^^^^^^^^^^^ expected named lifetime parameter + | +help: consider introducing a named lifetime parameter + | +LL | type B<'a> = Box<dyn GenericLifetime<'a>>; + | ^^^^ ^^^^^^^^^^^^^^^^^^^ + error[E0107]: this trait takes 1 lifetime argument but 2 lifetime arguments were supplied --> $DIR/wrong-number-of-args.rs:104:22 | diff --git a/src/test/ui/hrtb/complex.rs b/src/test/ui/hrtb/complex.rs new file mode 100644 index 0000000000000..8cdfe247e025e --- /dev/null +++ b/src/test/ui/hrtb/complex.rs @@ -0,0 +1,28 @@ +// check-pass + +trait A<'a> {} +trait B<'b> {} +fn foo<T>() where for<'a> T: A<'a> + 'a {} +trait C<'c>: for<'a> A<'a> + for<'b> B<'b> { + type As; +} +struct D<T> where T: for<'c> C<'c, As=&'c ()> { + t: std::marker::PhantomData<T>, +} +trait E<'e, 'g> { + type As; +} +trait F<'f>: for<'a> A<'a> + for<'e> E<'e, 'f> {} +struct G<T> where T: for<'f> F<'f, As=&'f ()> { + t: std::marker::PhantomData<T>, +} +trait H<'a, 'b> { + type As; +} +trait I<'a>: for<'b> H<'a, 'b> {} + +struct J<T> where T: for<'i> I<'i, As=&'i ()> { + t: std::marker::PhantomData<T>, +} + +fn main() {} diff --git a/src/test/ui/hrtb/issue-62203-hrtb-ice.stderr b/src/test/ui/hrtb/issue-62203-hrtb-ice.stderr index 2342a4f6e172a..7b81beeed4167 100644 --- a/src/test/ui/hrtb/issue-62203-hrtb-ice.stderr +++ b/src/test/ui/hrtb/issue-62203-hrtb-ice.stderr @@ -15,7 +15,11 @@ error[E0271]: type mismatch resolving `<[closure@$DIR/issue-62203-hrtb-ice.rs:42 LL | let v = Unit2.m( | ^ expected struct `Unit4`, found struct `Unit3` | - = note: required because of the requirements on the impl of `for<'r> T0<'r, (<Unit2 as Ty<'r>>::V,)>` for `L<[closure@$DIR/issue-62203-hrtb-ice.rs:42:17: 42:39]>` +note: required because of the requirements on the impl of `for<'r> T0<'r, (<Unit2 as Ty<'r>>::V,)>` for `L<[closure@$DIR/issue-62203-hrtb-ice.rs:42:17: 42:39]>` + --> $DIR/issue-62203-hrtb-ice.rs:17:16 + | +LL | impl<'a, A, T> T0<'a, A> for L<T> + | ^^^^^^^^^ ^^^^ error: aborting due to 2 previous errors diff --git a/src/test/ui/impl-trait/associated-impl-trait-type-generic-trait.full_tait.stderr b/src/test/ui/impl-trait/associated-impl-trait-type-generic-trait.full_tait.stderr new file mode 100644 index 0000000000000..174c4c62da92e --- /dev/null +++ b/src/test/ui/impl-trait/associated-impl-trait-type-generic-trait.full_tait.stderr @@ -0,0 +1,11 @@ +warning: the feature `type_alias_impl_trait` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/associated-impl-trait-type-generic-trait.rs:3:32 + | +LL | #![cfg_attr(full_tait, feature(type_alias_impl_trait))] + | ^^^^^^^^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #63063 <https://github.com/rust-lang/rust/issues/63063> for more information + +warning: 1 warning emitted + diff --git a/src/test/ui/impl-trait/associated-impl-trait-type-generic-trait.rs b/src/test/ui/impl-trait/associated-impl-trait-type-generic-trait.rs index 6c7c46b0e3dbb..b2dad596ffa9c 100644 --- a/src/test/ui/impl-trait/associated-impl-trait-type-generic-trait.rs +++ b/src/test/ui/impl-trait/associated-impl-trait-type-generic-trait.rs @@ -1,4 +1,7 @@ -#![feature(type_alias_impl_trait)] +// revisions: min_tait full_tait +#![feature(min_type_alias_impl_trait)] +#![cfg_attr(full_tait, feature(type_alias_impl_trait))] +//[full_tait]~^ WARN incomplete // build-pass (FIXME(62277): could be check-pass?) trait Bar {} diff --git a/src/test/ui/impl-trait/associated-impl-trait-type-trivial.full_tait.stderr b/src/test/ui/impl-trait/associated-impl-trait-type-trivial.full_tait.stderr new file mode 100644 index 0000000000000..e3dd4a60b4df7 --- /dev/null +++ b/src/test/ui/impl-trait/associated-impl-trait-type-trivial.full_tait.stderr @@ -0,0 +1,11 @@ +warning: the feature `type_alias_impl_trait` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/associated-impl-trait-type-trivial.rs:3:32 + | +LL | #![cfg_attr(full_tait, feature(type_alias_impl_trait))] + | ^^^^^^^^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #63063 <https://github.com/rust-lang/rust/issues/63063> for more information + +warning: 1 warning emitted + diff --git a/src/test/ui/impl-trait/associated-impl-trait-type-trivial.rs b/src/test/ui/impl-trait/associated-impl-trait-type-trivial.rs index cdda341cad87a..c101ba2294830 100644 --- a/src/test/ui/impl-trait/associated-impl-trait-type-trivial.rs +++ b/src/test/ui/impl-trait/associated-impl-trait-type-trivial.rs @@ -1,4 +1,7 @@ -#![feature(type_alias_impl_trait)] +// revisions: min_tait full_tait +#![feature(min_type_alias_impl_trait)] +#![cfg_attr(full_tait, feature(type_alias_impl_trait))] +//[full_tait]~^ WARN incomplete // build-pass (FIXME(62277): could be check-pass?) trait Bar {} diff --git a/src/test/ui/impl-trait/associated-impl-trait-type.full_tait.stderr b/src/test/ui/impl-trait/associated-impl-trait-type.full_tait.stderr new file mode 100644 index 0000000000000..39147b00a346e --- /dev/null +++ b/src/test/ui/impl-trait/associated-impl-trait-type.full_tait.stderr @@ -0,0 +1,11 @@ +warning: the feature `type_alias_impl_trait` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/associated-impl-trait-type.rs:3:32 + | +LL | #![cfg_attr(full_tait, feature(type_alias_impl_trait))] + | ^^^^^^^^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #63063 <https://github.com/rust-lang/rust/issues/63063> for more information + +warning: 1 warning emitted + diff --git a/src/test/ui/impl-trait/associated-impl-trait-type.rs b/src/test/ui/impl-trait/associated-impl-trait-type.rs index d0661d66f4b48..5c28eba2261fc 100644 --- a/src/test/ui/impl-trait/associated-impl-trait-type.rs +++ b/src/test/ui/impl-trait/associated-impl-trait-type.rs @@ -1,4 +1,7 @@ -#![feature(type_alias_impl_trait)] +// revisions: min_tait full_tait +#![feature(min_type_alias_impl_trait)] +#![cfg_attr(full_tait, feature(type_alias_impl_trait))] +//[full_tait]~^ WARN incomplete // build-pass (FIXME(62277): could be check-pass?) trait Bar {} diff --git a/src/test/ui/impl-trait/auto-trait.full_tait.stderr b/src/test/ui/impl-trait/auto-trait.full_tait.stderr new file mode 100644 index 0000000000000..314617748b0ba --- /dev/null +++ b/src/test/ui/impl-trait/auto-trait.full_tait.stderr @@ -0,0 +1,21 @@ +warning: the feature `type_alias_impl_trait` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/auto-trait.rs:5:32 + | +LL | #![cfg_attr(full_tait, feature(type_alias_impl_trait))] + | ^^^^^^^^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #63063 <https://github.com/rust-lang/rust/issues/63063> for more information + +error[E0119]: conflicting implementations of trait `AnotherTrait` for type `D<impl OpaqueTrait>` + --> $DIR/auto-trait.rs:24:1 + | +LL | impl<T: Send> AnotherTrait for T {} + | -------------------------------- first implementation here +... +LL | impl AnotherTrait for D<OpaqueType> { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `D<impl OpaqueTrait>` + +error: aborting due to previous error; 1 warning emitted + +For more information about this error, try `rustc --explain E0119`. diff --git a/src/test/ui/impl-trait/auto-trait.min_tait.stderr b/src/test/ui/impl-trait/auto-trait.min_tait.stderr new file mode 100644 index 0000000000000..75a5b0cb87db1 --- /dev/null +++ b/src/test/ui/impl-trait/auto-trait.min_tait.stderr @@ -0,0 +1,12 @@ +error[E0119]: conflicting implementations of trait `AnotherTrait` for type `D<impl OpaqueTrait>` + --> $DIR/auto-trait.rs:24:1 + | +LL | impl<T: Send> AnotherTrait for T {} + | -------------------------------- first implementation here +... +LL | impl AnotherTrait for D<OpaqueType> { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `D<impl OpaqueTrait>` + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0119`. diff --git a/src/test/ui/impl-trait/auto-trait.rs b/src/test/ui/impl-trait/auto-trait.rs index cf2773f4ef59d..c965a34c12b32 100644 --- a/src/test/ui/impl-trait/auto-trait.rs +++ b/src/test/ui/impl-trait/auto-trait.rs @@ -1,6 +1,9 @@ // Tests that type alias impls traits do not leak auto-traits for // the purposes of coherence checking -#![feature(type_alias_impl_trait)] +// revisions: min_tait full_tait +#![feature(min_type_alias_impl_trait)] +#![cfg_attr(full_tait, feature(type_alias_impl_trait))] +//[full_tait]~^ WARN incomplete trait OpaqueTrait {} impl<T> OpaqueTrait for T {} diff --git a/src/test/ui/impl-trait/auto-trait.stderr b/src/test/ui/impl-trait/auto-trait.stderr deleted file mode 100644 index 16fe1b56b50c6..0000000000000 --- a/src/test/ui/impl-trait/auto-trait.stderr +++ /dev/null @@ -1,12 +0,0 @@ -error[E0119]: conflicting implementations of trait `AnotherTrait` for type `D<impl OpaqueTrait>`: - --> $DIR/auto-trait.rs:21:1 - | -LL | impl<T: Send> AnotherTrait for T {} - | -------------------------------- first implementation here -... -LL | impl AnotherTrait for D<OpaqueType> { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `D<impl OpaqueTrait>` - -error: aborting due to previous error - -For more information about this error, try `rustc --explain E0119`. diff --git a/src/test/ui/impl-trait/bound-normalization-fail.rs b/src/test/ui/impl-trait/bound-normalization-fail.rs index 5bf3ec733f5d7..d3056fb885125 100644 --- a/src/test/ui/impl-trait/bound-normalization-fail.rs +++ b/src/test/ui/impl-trait/bound-normalization-fail.rs @@ -1,4 +1,3 @@ -// ignore-tidy-linelength // edition:2018 #![feature(impl_trait_in_bindings)] diff --git a/src/test/ui/impl-trait/bound-normalization-fail.stderr b/src/test/ui/impl-trait/bound-normalization-fail.stderr index a7d06c71663de..ba3a2e7f8d4c9 100644 --- a/src/test/ui/impl-trait/bound-normalization-fail.stderr +++ b/src/test/ui/impl-trait/bound-normalization-fail.stderr @@ -1,5 +1,5 @@ warning: the feature `impl_trait_in_bindings` is incomplete and may not be safe to use and/or cause compiler crashes - --> $DIR/bound-normalization-fail.rs:4:12 + --> $DIR/bound-normalization-fail.rs:3:12 | LL | #![feature(impl_trait_in_bindings)] | ^^^^^^^^^^^^^^^^^^^^^^ @@ -8,32 +8,32 @@ LL | #![feature(impl_trait_in_bindings)] = note: see issue #63065 <https://github.com/rust-lang/rust/issues/63065> for more information error[E0271]: type mismatch resolving `<Foo<()> as FooLike>::Output == <T as impl_trait::Trait>::Assoc` - --> $DIR/bound-normalization-fail.rs:27:32 + --> $DIR/bound-normalization-fail.rs:26:32 | LL | fn foo_fail<T: Trait>() -> impl FooLike<Output=T::Assoc> { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `()`, found associated type + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected associated type, found `()` | - = note: expected type `()` - found associated type `<T as impl_trait::Trait>::Assoc` + = note: expected associated type `<T as impl_trait::Trait>::Assoc` + found type `()` help: consider constraining the associated type `<T as impl_trait::Trait>::Assoc` to `()` | LL | fn foo_fail<T: Trait<Assoc = ()>>() -> impl FooLike<Output=T::Assoc> { | ^^^^^^^^^^^^ error[E0760]: `impl Trait` return type cannot contain a projection or `Self` that references lifetimes from a parent scope - --> $DIR/bound-normalization-fail.rs:43:41 + --> $DIR/bound-normalization-fail.rs:42:41 | LL | fn foo2_fail<'a, T: Trait<'a>>() -> impl FooLike<Output=T::Assoc> { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error[E0271]: type mismatch resolving `<Foo<()> as FooLike>::Output == <T as lifetimes::Trait<'static>>::Assoc` - --> $DIR/bound-normalization-fail.rs:43:41 + --> $DIR/bound-normalization-fail.rs:42:41 | LL | fn foo2_fail<'a, T: Trait<'a>>() -> impl FooLike<Output=T::Assoc> { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `()`, found associated type + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected associated type, found `()` | - = note: expected type `()` - found associated type `<T as lifetimes::Trait<'static>>::Assoc` + = note: expected associated type `<T as lifetimes::Trait<'static>>::Assoc` + found type `()` help: consider constraining the associated type `<T as lifetimes::Trait<'static>>::Assoc` to `()` | LL | fn foo2_fail<'a, T: Trait<'a, Assoc = ()>>() -> impl FooLike<Output=T::Assoc> { diff --git a/src/test/ui/impl-trait/bound-normalization-pass.rs b/src/test/ui/impl-trait/bound-normalization-pass.rs index 3e6884ef10fa6..6a01753b4c29c 100644 --- a/src/test/ui/impl-trait/bound-normalization-pass.rs +++ b/src/test/ui/impl-trait/bound-normalization-pass.rs @@ -4,7 +4,7 @@ //[sa] compile-flags: -Z save-analysis //-^ To make this the regression test for #75962. -#![feature(type_alias_impl_trait)] +#![feature(min_type_alias_impl_trait)] #![feature(impl_trait_in_bindings)] //~^ WARNING the feature `impl_trait_in_bindings` is incomplete diff --git a/src/test/ui/impl-trait/equality2.stderr b/src/test/ui/impl-trait/equality2.stderr index 1443b76048b32..3318866c52cf9 100644 --- a/src/test/ui/impl-trait/equality2.stderr +++ b/src/test/ui/impl-trait/equality2.stderr @@ -35,8 +35,10 @@ LL | let _: i32 = Leak::leak(hide(0_i32)); | = note: expected type `i32` found associated type `<impl Foo as Leak>::T` - = help: consider constraining the associated type `<impl Foo as Leak>::T` to `i32` - = note: for more information, visit https://doc.rust-lang.org/book/ch19-03-advanced-traits.html +help: consider constraining the associated type `<impl Foo as Leak>::T` to `i32` + | +LL | fn hide<T: Foo>(x: T) -> impl Foo<T = i32> { + | ^^^^^^^^^ error[E0308]: mismatched types --> $DIR/equality2.rs:38:10 diff --git a/src/test/ui/impl-trait/impl-generic-mismatch-ab.stderr b/src/test/ui/impl-trait/impl-generic-mismatch-ab.stderr index 638a0093fb21d..d37670db08537 100644 --- a/src/test/ui/impl-trait/impl-generic-mismatch-ab.stderr +++ b/src/test/ui/impl-trait/impl-generic-mismatch-ab.stderr @@ -5,8 +5,10 @@ LL | fn foo<A: Debug>(&self, a: &A, b: &impl Debug); | -- type in trait ... LL | fn foo<B: Debug>(&self, a: &impl Debug, b: &B) { } - | - ^^^^^^^^^^^ expected type parameter `B`, found type parameter `impl Debug` - | | + | - ^^^^^^^^^^^ + | | | + | | expected type parameter `B`, found type parameter `impl Debug` + | | help: change the parameter type to match the trait: `&B` | expected type parameter | = note: expected fn pointer `fn(&(), &B, &impl Debug)` diff --git a/src/test/ui/impl-trait-in-bindings-issue-73003.rs b/src/test/ui/impl-trait/impl-trait-in-bindings-issue-73003.rs similarity index 100% rename from src/test/ui/impl-trait-in-bindings-issue-73003.rs rename to src/test/ui/impl-trait/impl-trait-in-bindings-issue-73003.rs diff --git a/src/test/ui/impl-trait-in-bindings-issue-73003.stderr b/src/test/ui/impl-trait/impl-trait-in-bindings-issue-73003.stderr similarity index 100% rename from src/test/ui/impl-trait-in-bindings-issue-73003.stderr rename to src/test/ui/impl-trait/impl-trait-in-bindings-issue-73003.stderr diff --git a/src/test/ui/impl-trait-in-bindings.rs b/src/test/ui/impl-trait/impl-trait-in-bindings.rs similarity index 100% rename from src/test/ui/impl-trait-in-bindings.rs rename to src/test/ui/impl-trait/impl-trait-in-bindings.rs diff --git a/src/test/ui/impl-trait-in-bindings.stderr b/src/test/ui/impl-trait/impl-trait-in-bindings.stderr similarity index 100% rename from src/test/ui/impl-trait-in-bindings.stderr rename to src/test/ui/impl-trait/impl-trait-in-bindings.stderr diff --git a/src/test/ui/impl-trait/impl-trait-in-macro.rs b/src/test/ui/impl-trait/impl-trait-in-macro.rs new file mode 100644 index 0000000000000..3165c9b991572 --- /dev/null +++ b/src/test/ui/impl-trait/impl-trait-in-macro.rs @@ -0,0 +1,20 @@ +use std::fmt::Debug; + +macro_rules! i { + ($($tr:tt)*) => { impl $($tr)* }; +} + +fn foo(x: i!(Debug), y: i!(Debug)) -> String { + let mut a = x; + a = y; //~ ERROR mismatched + format!("{:?}", a) +} + +trait S<T> {} + +fn much_universe<T: S<i!(Debug)>, U: IntoIterator<Item = i!(Iterator<Item = i!(Clone)>)>>( + _: i!(Debug + Clone), +) { +} + +fn main() {} diff --git a/src/test/ui/impl-trait/impl-trait-in-macro.stderr b/src/test/ui/impl-trait/impl-trait-in-macro.stderr new file mode 100644 index 0000000000000..b5f9986ce4089 --- /dev/null +++ b/src/test/ui/impl-trait/impl-trait-in-macro.stderr @@ -0,0 +1,20 @@ +error[E0308]: mismatched types + --> $DIR/impl-trait-in-macro.rs:9:9 + | +LL | ($($tr:tt)*) => { impl $($tr)* }; + | ---- + | | + | expected type parameter + | found type parameter +... +LL | a = y; + | ^ expected type parameter `impl Debug`, found a different type parameter `impl Debug` + | + = note: expected type parameter `impl Debug` (type parameter `impl Debug`) + found type parameter `impl Debug` (type parameter `impl Debug`) + = note: a type parameter was expected, but a different one was found; you might be missing a type parameter or trait bound + = note: for more information, visit https://doc.rust-lang.org/book/ch10-02-traits.html#traits-as-parameters + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0308`. diff --git a/src/test/ui/impl-trait/issue-55872-1.full_tait.stderr b/src/test/ui/impl-trait/issue-55872-1.full_tait.stderr new file mode 100644 index 0000000000000..286dd7aafb43f --- /dev/null +++ b/src/test/ui/impl-trait/issue-55872-1.full_tait.stderr @@ -0,0 +1,57 @@ +warning: the feature `type_alias_impl_trait` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/issue-55872-1.rs:3:32 + | +LL | #![cfg_attr(full_tait, feature(type_alias_impl_trait))] + | ^^^^^^^^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #63063 <https://github.com/rust-lang/rust/issues/63063> for more information + +error[E0276]: impl has stricter requirements than trait + --> $DIR/issue-55872-1.rs:17:5 + | +LL | fn foo<T>() -> Self::E; + | ----------------------- definition of `foo` from trait +... +LL | fn foo<T: Default>() -> Self::E { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ impl has extra requirement `T: Default` + +error[E0277]: the trait bound `S: Copy` is not satisfied in `(S, T)` + --> $DIR/issue-55872-1.rs:13:14 + | +LL | type E = impl Copy; + | ^^^^^^^^^ within `(S, T)`, the trait `Copy` is not implemented for `S` + | + = note: required because it appears within the type `(S, T)` +help: consider further restricting this bound + | +LL | impl<S: Default + std::marker::Copy> Bar for S { + | ^^^^^^^^^^^^^^^^^^^ + +error[E0277]: the trait bound `T: Copy` is not satisfied in `(S, T)` + --> $DIR/issue-55872-1.rs:13:14 + | +LL | type E = impl Copy; + | ^^^^^^^^^ within `(S, T)`, the trait `Copy` is not implemented for `T` + | + = note: required because it appears within the type `(S, T)` +help: consider further restricting this bound + | +LL | fn foo<T: Default + std::marker::Copy>() -> Self::E { + | ^^^^^^^^^^^^^^^^^^^ + +error: type parameter `T` is part of concrete type but not used in parameter list for the `impl Trait` type alias + --> $DIR/issue-55872-1.rs:17:37 + | +LL | fn foo<T: Default>() -> Self::E { + | _____________________________________^ +LL | | +LL | | +LL | | (S::default(), T::default()) +LL | | } + | |_____^ + +error: aborting due to 4 previous errors; 1 warning emitted + +Some errors have detailed explanations: E0276, E0277. +For more information about an error, try `rustc --explain E0276`. diff --git a/src/test/ui/impl-trait/issue-55872-1.min_tait.stderr b/src/test/ui/impl-trait/issue-55872-1.min_tait.stderr new file mode 100644 index 0000000000000..653299f4cbcee --- /dev/null +++ b/src/test/ui/impl-trait/issue-55872-1.min_tait.stderr @@ -0,0 +1,48 @@ +error[E0276]: impl has stricter requirements than trait + --> $DIR/issue-55872-1.rs:17:5 + | +LL | fn foo<T>() -> Self::E; + | ----------------------- definition of `foo` from trait +... +LL | fn foo<T: Default>() -> Self::E { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ impl has extra requirement `T: Default` + +error[E0277]: the trait bound `S: Copy` is not satisfied in `(S, T)` + --> $DIR/issue-55872-1.rs:13:14 + | +LL | type E = impl Copy; + | ^^^^^^^^^ within `(S, T)`, the trait `Copy` is not implemented for `S` + | + = note: required because it appears within the type `(S, T)` +help: consider further restricting this bound + | +LL | impl<S: Default + std::marker::Copy> Bar for S { + | ^^^^^^^^^^^^^^^^^^^ + +error[E0277]: the trait bound `T: Copy` is not satisfied in `(S, T)` + --> $DIR/issue-55872-1.rs:13:14 + | +LL | type E = impl Copy; + | ^^^^^^^^^ within `(S, T)`, the trait `Copy` is not implemented for `T` + | + = note: required because it appears within the type `(S, T)` +help: consider further restricting this bound + | +LL | fn foo<T: Default + std::marker::Copy>() -> Self::E { + | ^^^^^^^^^^^^^^^^^^^ + +error: type parameter `T` is part of concrete type but not used in parameter list for the `impl Trait` type alias + --> $DIR/issue-55872-1.rs:17:37 + | +LL | fn foo<T: Default>() -> Self::E { + | _____________________________________^ +LL | | +LL | | +LL | | (S::default(), T::default()) +LL | | } + | |_____^ + +error: aborting due to 4 previous errors + +Some errors have detailed explanations: E0276, E0277. +For more information about an error, try `rustc --explain E0276`. diff --git a/src/test/ui/impl-trait/issue-55872-1.rs b/src/test/ui/impl-trait/issue-55872-1.rs index a746ed09af55b..a9e9c9b5bebeb 100644 --- a/src/test/ui/impl-trait/issue-55872-1.rs +++ b/src/test/ui/impl-trait/issue-55872-1.rs @@ -1,5 +1,7 @@ -// ignore-tidy-linelength -#![feature(type_alias_impl_trait)] +// revisions: min_tait full_tait +#![feature(min_type_alias_impl_trait)] +#![cfg_attr(full_tait, feature(type_alias_impl_trait))] +//[full_tait]~^ WARN incomplete pub trait Bar { type E: Copy; diff --git a/src/test/ui/impl-trait/issue-55872-1.stderr b/src/test/ui/impl-trait/issue-55872-1.stderr deleted file mode 100644 index 64c536cf1fe30..0000000000000 --- a/src/test/ui/impl-trait/issue-55872-1.stderr +++ /dev/null @@ -1,48 +0,0 @@ -error[E0276]: impl has stricter requirements than trait - --> $DIR/issue-55872-1.rs:15:5 - | -LL | fn foo<T>() -> Self::E; - | ----------------------- definition of `foo` from trait -... -LL | fn foo<T: Default>() -> Self::E { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ impl has extra requirement `T: Default` - -error[E0277]: the trait bound `S: Copy` is not satisfied in `(S, T)` - --> $DIR/issue-55872-1.rs:11:14 - | -LL | type E = impl Copy; - | ^^^^^^^^^ within `(S, T)`, the trait `Copy` is not implemented for `S` - | - = note: required because it appears within the type `(S, T)` -help: consider further restricting this bound - | -LL | impl<S: Default + Copy> Bar for S { - | ^^^^^^ - -error[E0277]: the trait bound `T: Copy` is not satisfied in `(S, T)` - --> $DIR/issue-55872-1.rs:11:14 - | -LL | type E = impl Copy; - | ^^^^^^^^^ within `(S, T)`, the trait `Copy` is not implemented for `T` - | - = note: required because it appears within the type `(S, T)` -help: consider further restricting this bound - | -LL | fn foo<T: Default + Copy>() -> Self::E { - | ^^^^^^ - -error: type parameter `T` is part of concrete type but not used in parameter list for the `impl Trait` type alias - --> $DIR/issue-55872-1.rs:15:37 - | -LL | fn foo<T: Default>() -> Self::E { - | _____________________________________^ -LL | | -LL | | -LL | | (S::default(), T::default()) -LL | | } - | |_____^ - -error: aborting due to 4 previous errors - -Some errors have detailed explanations: E0276, E0277. -For more information about an error, try `rustc --explain E0276`. diff --git a/src/test/ui/impl-trait/issue-55872-2.full_tait.stderr b/src/test/ui/impl-trait/issue-55872-2.full_tait.stderr new file mode 100644 index 0000000000000..a8fc681a093d5 --- /dev/null +++ b/src/test/ui/impl-trait/issue-55872-2.full_tait.stderr @@ -0,0 +1,28 @@ +warning: the feature `type_alias_impl_trait` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/issue-55872-2.rs:6:32 + | +LL | #![cfg_attr(full_tait, feature(type_alias_impl_trait))] + | ^^^^^^^^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #63063 <https://github.com/rust-lang/rust/issues/63063> for more information + +error[E0277]: the trait bound `impl Future: Copy` is not satisfied + --> $DIR/issue-55872-2.rs:16:14 + | +LL | type E = impl std::marker::Copy; + | ^^^^^^^^^^^^^^^^^^^^^^ the trait `Copy` is not implemented for `impl Future` + +error: type parameter `T` is part of concrete type but not used in parameter list for the `impl Trait` type alias + --> $DIR/issue-55872-2.rs:18:28 + | +LL | fn foo<T>() -> Self::E { + | ____________________________^ +LL | | +LL | | async {} +LL | | } + | |_____^ + +error: aborting due to 2 previous errors; 1 warning emitted + +For more information about this error, try `rustc --explain E0277`. diff --git a/src/test/ui/impl-trait/issue-55872-2.min_tait.stderr b/src/test/ui/impl-trait/issue-55872-2.min_tait.stderr new file mode 100644 index 0000000000000..57f81443dccdf --- /dev/null +++ b/src/test/ui/impl-trait/issue-55872-2.min_tait.stderr @@ -0,0 +1,19 @@ +error[E0277]: the trait bound `impl Future: Copy` is not satisfied + --> $DIR/issue-55872-2.rs:16:14 + | +LL | type E = impl std::marker::Copy; + | ^^^^^^^^^^^^^^^^^^^^^^ the trait `Copy` is not implemented for `impl Future` + +error: type parameter `T` is part of concrete type but not used in parameter list for the `impl Trait` type alias + --> $DIR/issue-55872-2.rs:18:28 + | +LL | fn foo<T>() -> Self::E { + | ____________________________^ +LL | | +LL | | async {} +LL | | } + | |_____^ + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0277`. diff --git a/src/test/ui/impl-trait/issue-55872-2.rs b/src/test/ui/impl-trait/issue-55872-2.rs index ee09a66ef537a..cd72b2eec3cf9 100644 --- a/src/test/ui/impl-trait/issue-55872-2.rs +++ b/src/test/ui/impl-trait/issue-55872-2.rs @@ -1,8 +1,10 @@ // edition:2018 -// ignore-tidy-linelength // ignore-compare-mode-chalk -#![feature(type_alias_impl_trait)] +// revisions: min_tait full_tait +#![feature(min_type_alias_impl_trait)] +#![cfg_attr(full_tait, feature(type_alias_impl_trait))] +//[full_tait]~^ WARN incomplete pub trait Bar { type E: Copy; @@ -14,7 +16,7 @@ impl<S> Bar for S { type E = impl std::marker::Copy; //~^ ERROR the trait bound `impl Future: Copy` is not satisfied [E0277] fn foo<T>() -> Self::E { - //~^ ERROR type parameter `T` is part of concrete type but not used in parameter list for the `impl Trait` type alias + //~^ ERROR type parameter `T` is part of concrete type but not used in parameter list for the `impl Trait` type alias async {} } } diff --git a/src/test/ui/impl-trait/issue-55872-2.stderr b/src/test/ui/impl-trait/issue-55872-2.stderr deleted file mode 100644 index f954ce9f090e6..0000000000000 --- a/src/test/ui/impl-trait/issue-55872-2.stderr +++ /dev/null @@ -1,19 +0,0 @@ -error[E0277]: the trait bound `impl Future: Copy` is not satisfied - --> $DIR/issue-55872-2.rs:14:14 - | -LL | type E = impl std::marker::Copy; - | ^^^^^^^^^^^^^^^^^^^^^^ the trait `Copy` is not implemented for `impl Future` - -error: type parameter `T` is part of concrete type but not used in parameter list for the `impl Trait` type alias - --> $DIR/issue-55872-2.rs:16:28 - | -LL | fn foo<T>() -> Self::E { - | ____________________________^ -LL | | -LL | | async {} -LL | | } - | |_____^ - -error: aborting due to 2 previous errors - -For more information about this error, try `rustc --explain E0277`. diff --git a/src/test/ui/impl-trait/issue-55872.full_tait.stderr b/src/test/ui/impl-trait/issue-55872.full_tait.stderr new file mode 100644 index 0000000000000..e549fec1c229e --- /dev/null +++ b/src/test/ui/impl-trait/issue-55872.full_tait.stderr @@ -0,0 +1,21 @@ +warning: the feature `type_alias_impl_trait` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/issue-55872.rs:4:32 + | +LL | #![cfg_attr(full_tait, feature(type_alias_impl_trait))] + | ^^^^^^^^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #63063 <https://github.com/rust-lang/rust/issues/63063> for more information + +error: type parameter `T` is part of concrete type but not used in parameter list for the `impl Trait` type alias + --> $DIR/issue-55872.rs:16:28 + | +LL | fn foo<T>() -> Self::E { + | ____________________________^ +LL | | +LL | | || () +LL | | } + | |_____^ + +error: aborting due to previous error; 1 warning emitted + diff --git a/src/test/ui/impl-trait/issue-55872.min_tait.stderr b/src/test/ui/impl-trait/issue-55872.min_tait.stderr new file mode 100644 index 0000000000000..341dba95cad85 --- /dev/null +++ b/src/test/ui/impl-trait/issue-55872.min_tait.stderr @@ -0,0 +1,12 @@ +error: type parameter `T` is part of concrete type but not used in parameter list for the `impl Trait` type alias + --> $DIR/issue-55872.rs:16:28 + | +LL | fn foo<T>() -> Self::E { + | ____________________________^ +LL | | +LL | | || () +LL | | } + | |_____^ + +error: aborting due to previous error + diff --git a/src/test/ui/impl-trait/issue-55872.rs b/src/test/ui/impl-trait/issue-55872.rs index b12bfbcda9dc3..e3fc523feccb9 100644 --- a/src/test/ui/impl-trait/issue-55872.rs +++ b/src/test/ui/impl-trait/issue-55872.rs @@ -1,6 +1,8 @@ -// ignore-tidy-linelength // ignore-compare-mode-chalk -#![feature(type_alias_impl_trait)] +// revisions: min_tait full_tait +#![feature(min_type_alias_impl_trait)] +#![cfg_attr(full_tait, feature(type_alias_impl_trait))] +//[full_tait]~^ WARN incomplete pub trait Bar { type E: Copy; diff --git a/src/test/ui/impl-trait/issue-55872.stderr b/src/test/ui/impl-trait/issue-55872.stderr deleted file mode 100644 index 41d7beb22fa22..0000000000000 --- a/src/test/ui/impl-trait/issue-55872.stderr +++ /dev/null @@ -1,12 +0,0 @@ -error: type parameter `T` is part of concrete type but not used in parameter list for the `impl Trait` type alias - --> $DIR/issue-55872.rs:14:28 - | -LL | fn foo<T>() -> Self::E { - | ____________________________^ -LL | | -LL | | || () -LL | | } - | |_____^ - -error: aborting due to previous error - diff --git a/src/test/ui/impl-trait/issues/issue-53457.full_tait.stderr b/src/test/ui/impl-trait/issues/issue-53457.full_tait.stderr new file mode 100644 index 0000000000000..906578c291f16 --- /dev/null +++ b/src/test/ui/impl-trait/issues/issue-53457.full_tait.stderr @@ -0,0 +1,11 @@ +warning: the feature `type_alias_impl_trait` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/issue-53457.rs:5:32 + | +LL | #![cfg_attr(full_tait, feature(type_alias_impl_trait))] + | ^^^^^^^^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #63063 <https://github.com/rust-lang/rust/issues/63063> for more information + +warning: 1 warning emitted + diff --git a/src/test/ui/impl-trait/issues/issue-53457.rs b/src/test/ui/impl-trait/issues/issue-53457.rs index 3f97502492506..c44e7f01f25d0 100644 --- a/src/test/ui/impl-trait/issues/issue-53457.rs +++ b/src/test/ui/impl-trait/issues/issue-53457.rs @@ -1,6 +1,9 @@ // check-pass -#![feature(type_alias_impl_trait)] +// revisions: min_tait full_tait +#![feature(min_type_alias_impl_trait)] +#![cfg_attr(full_tait, feature(type_alias_impl_trait))] +//[full_tait]~^ WARN incomplete type X = impl Clone; diff --git a/src/test/ui/impl-trait/issues/issue-70877.full_tait.stderr b/src/test/ui/impl-trait/issues/issue-70877.full_tait.stderr new file mode 100644 index 0000000000000..8e42b9d46db39 --- /dev/null +++ b/src/test/ui/impl-trait/issues/issue-70877.full_tait.stderr @@ -0,0 +1,15 @@ +error[E0271]: type mismatch resolving `<Bar as Iterator>::Item == Box<(dyn for<'r> Fn(&'r (dyn ToString + 'r)) -> Option<String> + 'static)>` + --> $DIR/issue-70877.rs:11:12 + | +LL | type FooRet = impl std::fmt::Debug; + | -------------------- the found opaque type +... +LL | type Foo = impl Iterator<Item = FooItem>; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected enum `Option`, found opaque type + | + = note: expected struct `Box<(dyn for<'r> Fn(&'r (dyn ToString + 'r)) -> Option<String> + 'static)>` + found struct `Box<(dyn for<'r> Fn(&'r (dyn ToString + 'r)) -> impl Debug + 'static)>` + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0271`. diff --git a/src/test/ui/impl-trait/issues/issue-70877.min_tait.stderr b/src/test/ui/impl-trait/issues/issue-70877.min_tait.stderr new file mode 100644 index 0000000000000..8e42b9d46db39 --- /dev/null +++ b/src/test/ui/impl-trait/issues/issue-70877.min_tait.stderr @@ -0,0 +1,15 @@ +error[E0271]: type mismatch resolving `<Bar as Iterator>::Item == Box<(dyn for<'r> Fn(&'r (dyn ToString + 'r)) -> Option<String> + 'static)>` + --> $DIR/issue-70877.rs:11:12 + | +LL | type FooRet = impl std::fmt::Debug; + | -------------------- the found opaque type +... +LL | type Foo = impl Iterator<Item = FooItem>; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected enum `Option`, found opaque type + | + = note: expected struct `Box<(dyn for<'r> Fn(&'r (dyn ToString + 'r)) -> Option<String> + 'static)>` + found struct `Box<(dyn for<'r> Fn(&'r (dyn ToString + 'r)) -> impl Debug + 'static)>` + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0271`. diff --git a/src/test/ui/impl-trait/issues/issue-70877.rs b/src/test/ui/impl-trait/issues/issue-70877.rs index a4a59f98fd83f..7ca0f90e2dced 100644 --- a/src/test/ui/impl-trait/issues/issue-70877.rs +++ b/src/test/ui/impl-trait/issues/issue-70877.rs @@ -1,4 +1,6 @@ -#![feature(type_alias_impl_trait)] +// revisions: min_tait full_tait +#![feature(min_type_alias_impl_trait)] +#![cfg_attr(full_tait, feature(type_alias_impl_trait))] #![feature(impl_trait_in_bindings)] #![allow(incomplete_features)] diff --git a/src/test/ui/impl-trait/issues/issue-70877.stderr b/src/test/ui/impl-trait/issues/issue-70877.stderr deleted file mode 100644 index 3ef7087b08a10..0000000000000 --- a/src/test/ui/impl-trait/issues/issue-70877.stderr +++ /dev/null @@ -1,15 +0,0 @@ -error[E0271]: type mismatch resolving `<Bar as Iterator>::Item == Box<(dyn for<'r> Fn(&'r (dyn ToString + 'r)) -> Option<String> + 'static)>` - --> $DIR/issue-70877.rs:9:12 - | -LL | type FooRet = impl std::fmt::Debug; - | -------------------- the expected opaque type -... -LL | type Foo = impl Iterator<Item = FooItem>; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected opaque type, found enum `Option` - | - = note: expected struct `Box<(dyn for<'r> Fn(&'r (dyn ToString + 'r)) -> impl Debug + 'static)>` - found struct `Box<(dyn for<'r> Fn(&'r (dyn ToString + 'r)) -> Option<String> + 'static)>` - -error: aborting due to previous error - -For more information about this error, try `rustc --explain E0271`. diff --git a/src/test/ui/impl-trait/issues/issue-78722.full_tait.stderr b/src/test/ui/impl-trait/issues/issue-78722.full_tait.stderr new file mode 100644 index 0000000000000..d7327aa46bcef --- /dev/null +++ b/src/test/ui/impl-trait/issues/issue-78722.full_tait.stderr @@ -0,0 +1,35 @@ +warning: the feature `type_alias_impl_trait` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/issue-78722.rs:5:32 + | +LL | #![cfg_attr(full_tait, feature(type_alias_impl_trait))] + | ^^^^^^^^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #63063 <https://github.com/rust-lang/rust/issues/63063> for more information + +warning: the feature `impl_trait_in_bindings` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/issue-78722.rs:7:12 + | +LL | #![feature(impl_trait_in_bindings)] + | ^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #63065 <https://github.com/rust-lang/rust/issues/63065> for more information + +error: `async` blocks are not allowed in constants + --> $DIR/issue-78722.rs:17:20 + | +LL | let f: F = async { 1 }; + | ^^^^^^^^^^^ + +error[E0493]: destructors cannot be evaluated at compile-time + --> $DIR/issue-78722.rs:17:13 + | +LL | let f: F = async { 1 }; + | ^ constants cannot evaluate destructors +... +LL | }], + | - value is dropped here + +error: aborting due to 2 previous errors; 2 warnings emitted + +For more information about this error, try `rustc --explain E0493`. diff --git a/src/test/ui/impl-trait/issues/issue-78722.min_tait.stderr b/src/test/ui/impl-trait/issues/issue-78722.min_tait.stderr new file mode 100644 index 0000000000000..01ec71e7a5053 --- /dev/null +++ b/src/test/ui/impl-trait/issues/issue-78722.min_tait.stderr @@ -0,0 +1,27 @@ +warning: the feature `impl_trait_in_bindings` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/issue-78722.rs:7:12 + | +LL | #![feature(impl_trait_in_bindings)] + | ^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #63065 <https://github.com/rust-lang/rust/issues/63065> for more information + +error: `async` blocks are not allowed in constants + --> $DIR/issue-78722.rs:17:20 + | +LL | let f: F = async { 1 }; + | ^^^^^^^^^^^ + +error[E0493]: destructors cannot be evaluated at compile-time + --> $DIR/issue-78722.rs:17:13 + | +LL | let f: F = async { 1 }; + | ^ constants cannot evaluate destructors +... +LL | }], + | - value is dropped here + +error: aborting due to 2 previous errors; 1 warning emitted + +For more information about this error, try `rustc --explain E0493`. diff --git a/src/test/ui/impl-trait/issues/issue-78722.rs b/src/test/ui/impl-trait/issues/issue-78722.rs index 58734d3a44554..0999ec63e032c 100644 --- a/src/test/ui/impl-trait/issues/issue-78722.rs +++ b/src/test/ui/impl-trait/issues/issue-78722.rs @@ -1,6 +1,9 @@ // edition:2018 -#![feature(type_alias_impl_trait)] +// revisions: min_tait full_tait +#![feature(min_type_alias_impl_trait)] +#![cfg_attr(full_tait, feature(type_alias_impl_trait))] +//[full_tait]~^ WARN incomplete #![feature(impl_trait_in_bindings)] //~^ WARN the feature `impl_trait_in_bindings` is incomplete diff --git a/src/test/ui/impl-trait/issues/issue-78722.stderr b/src/test/ui/impl-trait/issues/issue-78722.stderr deleted file mode 100644 index 0e1e92b912080..0000000000000 --- a/src/test/ui/impl-trait/issues/issue-78722.stderr +++ /dev/null @@ -1,27 +0,0 @@ -warning: the feature `impl_trait_in_bindings` is incomplete and may not be safe to use and/or cause compiler crashes - --> $DIR/issue-78722.rs:4:12 - | -LL | #![feature(impl_trait_in_bindings)] - | ^^^^^^^^^^^^^^^^^^^^^^ - | - = note: `#[warn(incomplete_features)]` on by default - = note: see issue #63065 <https://github.com/rust-lang/rust/issues/63065> for more information - -error: `async` blocks are not allowed in constants - --> $DIR/issue-78722.rs:14:20 - | -LL | let f: F = async { 1 }; - | ^^^^^^^^^^^ - -error[E0493]: destructors cannot be evaluated at compile-time - --> $DIR/issue-78722.rs:14:13 - | -LL | let f: F = async { 1 }; - | ^ constants cannot evaluate destructors -... -LL | }], - | - value is dropped here - -error: aborting due to 2 previous errors; 1 warning emitted - -For more information about this error, try `rustc --explain E0493`. diff --git a/src/test/ui/impl-trait/issues/issue-83929-impl-trait-in-generic-default.rs b/src/test/ui/impl-trait/issues/issue-83929-impl-trait-in-generic-default.rs new file mode 100644 index 0000000000000..d9d2e3929b10c --- /dev/null +++ b/src/test/ui/impl-trait/issues/issue-83929-impl-trait-in-generic-default.rs @@ -0,0 +1,12 @@ +struct Foo<T = impl Copy>(T); +//~^ ERROR `impl Trait` not allowed outside of function and inherent method return types + +type Result<T, E = impl std::error::Error> = std::result::Result<T, E>; +//~^ ERROR `impl Trait` not allowed outside of function and inherent method return types + +// should not cause ICE +fn x() -> Foo { + Foo(0) +} + +fn main() -> Result<()> {} diff --git a/src/test/ui/impl-trait/issues/issue-83929-impl-trait-in-generic-default.stderr b/src/test/ui/impl-trait/issues/issue-83929-impl-trait-in-generic-default.stderr new file mode 100644 index 0000000000000..eef6844adfccb --- /dev/null +++ b/src/test/ui/impl-trait/issues/issue-83929-impl-trait-in-generic-default.stderr @@ -0,0 +1,15 @@ +error[E0562]: `impl Trait` not allowed outside of function and inherent method return types + --> $DIR/issue-83929-impl-trait-in-generic-default.rs:1:16 + | +LL | struct Foo<T = impl Copy>(T); + | ^^^^^^^^^ + +error[E0562]: `impl Trait` not allowed outside of function and inherent method return types + --> $DIR/issue-83929-impl-trait-in-generic-default.rs:4:20 + | +LL | type Result<T, E = impl std::error::Error> = std::result::Result<T, E>; + | ^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0562`. diff --git a/src/test/ui/impl-trait/multiple-lifetimes/error-handling-2.full_tait.stderr b/src/test/ui/impl-trait/multiple-lifetimes/error-handling-2.full_tait.stderr new file mode 100644 index 0000000000000..b2ebc7230b945 --- /dev/null +++ b/src/test/ui/impl-trait/multiple-lifetimes/error-handling-2.full_tait.stderr @@ -0,0 +1,24 @@ +warning: the feature `type_alias_impl_trait` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/error-handling-2.rs:6:32 + | +LL | #![cfg_attr(full_tait, feature(type_alias_impl_trait))] + | ^^^^^^^^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #63063 <https://github.com/rust-lang/rust/issues/63063> for more information + +error[E0700]: hidden type for `impl Trait` captures lifetime that does not appear in bounds + --> $DIR/error-handling-2.rs:16:60 + | +LL | fn foo<'a: 'b, 'b, 'c>(x: &'static i32, mut y: &'a i32) -> E<'b, 'c> { + | ^^^^^^^^^ + | +note: hidden type `*mut &'a i32` captures the lifetime `'a` as defined on the function body at 16:8 + --> $DIR/error-handling-2.rs:16:8 + | +LL | fn foo<'a: 'b, 'b, 'c>(x: &'static i32, mut y: &'a i32) -> E<'b, 'c> { + | ^^ + +error: aborting due to previous error; 1 warning emitted + +For more information about this error, try `rustc --explain E0700`. diff --git a/src/test/ui/impl-trait/multiple-lifetimes/error-handling-2.min_tait.stderr b/src/test/ui/impl-trait/multiple-lifetimes/error-handling-2.min_tait.stderr new file mode 100644 index 0000000000000..53745c290d03d --- /dev/null +++ b/src/test/ui/impl-trait/multiple-lifetimes/error-handling-2.min_tait.stderr @@ -0,0 +1,15 @@ +error[E0700]: hidden type for `impl Trait` captures lifetime that does not appear in bounds + --> $DIR/error-handling-2.rs:16:60 + | +LL | fn foo<'a: 'b, 'b, 'c>(x: &'static i32, mut y: &'a i32) -> E<'b, 'c> { + | ^^^^^^^^^ + | +note: hidden type `*mut &'a i32` captures the lifetime `'a` as defined on the function body at 16:8 + --> $DIR/error-handling-2.rs:16:8 + | +LL | fn foo<'a: 'b, 'b, 'c>(x: &'static i32, mut y: &'a i32) -> E<'b, 'c> { + | ^^ + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0700`. diff --git a/src/test/ui/impl-trait/multiple-lifetimes/error-handling-2.rs b/src/test/ui/impl-trait/multiple-lifetimes/error-handling-2.rs index 96d891b2cf1d7..196ddf9b8e4ab 100644 --- a/src/test/ui/impl-trait/multiple-lifetimes/error-handling-2.rs +++ b/src/test/ui/impl-trait/multiple-lifetimes/error-handling-2.rs @@ -1,7 +1,10 @@ // compile-flags:-Zborrowck=mir #![feature(member_constraints)] -#![feature(type_alias_impl_trait)] +// revisions: min_tait full_tait +#![feature(min_type_alias_impl_trait)] +#![cfg_attr(full_tait, feature(type_alias_impl_trait))] +//[full_tait]~^ WARN incomplete #[derive(Clone)] struct CopyIfEq<T, U>(T, U); diff --git a/src/test/ui/impl-trait/multiple-lifetimes/error-handling-2.stderr b/src/test/ui/impl-trait/multiple-lifetimes/error-handling-2.stderr deleted file mode 100644 index 59105f11805cd..0000000000000 --- a/src/test/ui/impl-trait/multiple-lifetimes/error-handling-2.stderr +++ /dev/null @@ -1,15 +0,0 @@ -error[E0700]: hidden type for `impl Trait` captures lifetime that does not appear in bounds - --> $DIR/error-handling-2.rs:13:60 - | -LL | fn foo<'a: 'b, 'b, 'c>(x: &'static i32, mut y: &'a i32) -> E<'b, 'c> { - | ^^^^^^^^^ - | -note: hidden type `*mut &'a i32` captures the lifetime `'a` as defined on the function body at 13:8 - --> $DIR/error-handling-2.rs:13:8 - | -LL | fn foo<'a: 'b, 'b, 'c>(x: &'static i32, mut y: &'a i32) -> E<'b, 'c> { - | ^^ - -error: aborting due to previous error - -For more information about this error, try `rustc --explain E0700`. diff --git a/src/test/ui/impl-trait/multiple-lifetimes/error-handling.full_tait.stderr b/src/test/ui/impl-trait/multiple-lifetimes/error-handling.full_tait.stderr new file mode 100644 index 0000000000000..d7a9e5463b358 --- /dev/null +++ b/src/test/ui/impl-trait/multiple-lifetimes/error-handling.full_tait.stderr @@ -0,0 +1,24 @@ +warning: the feature `type_alias_impl_trait` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/error-handling.rs:6:32 + | +LL | #![cfg_attr(full_tait, feature(type_alias_impl_trait))] + | ^^^^^^^^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #63063 <https://github.com/rust-lang/rust/issues/63063> for more information + +error: lifetime may not live long enough + --> $DIR/error-handling.rs:26:16 + | +LL | fn foo<'a, 'b, 'c>(x: &'static i32, mut y: &'a i32) -> E<'b, 'c> { + | -- -- lifetime `'b` defined here + | | + | lifetime `'a` defined here +... +LL | let _: &'b i32 = *u.0; + | ^^^^^^^ type annotation requires that `'a` must outlive `'b` + | + = help: consider adding the following bound: `'a: 'b` + +error: aborting due to previous error; 1 warning emitted + diff --git a/src/test/ui/impl-trait/multiple-lifetimes/error-handling.min_tait.stderr b/src/test/ui/impl-trait/multiple-lifetimes/error-handling.min_tait.stderr new file mode 100644 index 0000000000000..e2d745cdec804 --- /dev/null +++ b/src/test/ui/impl-trait/multiple-lifetimes/error-handling.min_tait.stderr @@ -0,0 +1,15 @@ +error: lifetime may not live long enough + --> $DIR/error-handling.rs:26:16 + | +LL | fn foo<'a, 'b, 'c>(x: &'static i32, mut y: &'a i32) -> E<'b, 'c> { + | -- -- lifetime `'b` defined here + | | + | lifetime `'a` defined here +... +LL | let _: &'b i32 = *u.0; + | ^^^^^^^ type annotation requires that `'a` must outlive `'b` + | + = help: consider adding the following bound: `'a: 'b` + +error: aborting due to previous error + diff --git a/src/test/ui/impl-trait/multiple-lifetimes/error-handling.rs b/src/test/ui/impl-trait/multiple-lifetimes/error-handling.rs index 8d02d635094be..b5adabb7abd22 100644 --- a/src/test/ui/impl-trait/multiple-lifetimes/error-handling.rs +++ b/src/test/ui/impl-trait/multiple-lifetimes/error-handling.rs @@ -1,7 +1,10 @@ // compile-flags:-Zborrowck=mir #![feature(member_constraints)] -#![feature(type_alias_impl_trait)] +// revisions: min_tait full_tait +#![feature(min_type_alias_impl_trait)] +#![cfg_attr(full_tait, feature(type_alias_impl_trait))] +//[full_tait]~^ WARN incomplete #[derive(Clone)] struct CopyIfEq<T, U>(T, U); diff --git a/src/test/ui/impl-trait/multiple-lifetimes/error-handling.stderr b/src/test/ui/impl-trait/multiple-lifetimes/error-handling.stderr deleted file mode 100644 index 6ce3aaf49eb33..0000000000000 --- a/src/test/ui/impl-trait/multiple-lifetimes/error-handling.stderr +++ /dev/null @@ -1,15 +0,0 @@ -error: lifetime may not live long enough - --> $DIR/error-handling.rs:23:16 - | -LL | fn foo<'a, 'b, 'c>(x: &'static i32, mut y: &'a i32) -> E<'b, 'c> { - | -- -- lifetime `'b` defined here - | | - | lifetime `'a` defined here -... -LL | let _: &'b i32 = *u.0; - | ^^^^^^^ type annotation requires that `'a` must outlive `'b` - | - = help: consider adding the following bound: `'a: 'b` - -error: aborting due to previous error - diff --git a/src/test/ui/impl-trait/multiple-lifetimes/ordinary-bounds-pick-original-type-alias-impl-trait.rs b/src/test/ui/impl-trait/multiple-lifetimes/ordinary-bounds-pick-original-type-alias-impl-trait.rs index 877940c74037b..9d345502aab4d 100644 --- a/src/test/ui/impl-trait/multiple-lifetimes/ordinary-bounds-pick-original-type-alias-impl-trait.rs +++ b/src/test/ui/impl-trait/multiple-lifetimes/ordinary-bounds-pick-original-type-alias-impl-trait.rs @@ -4,8 +4,7 @@ //[mir]compile-flags: -Z borrowck=mir #![feature(member_constraints)] -#![feature(type_alias_impl_trait)] - +#![feature(min_type_alias_impl_trait)] trait Trait<'a, 'b> { } impl<T> Trait<'_, '_> for T { } diff --git a/src/test/ui/impl-trait/negative-reasoning.full_tait.stderr b/src/test/ui/impl-trait/negative-reasoning.full_tait.stderr new file mode 100644 index 0000000000000..bccbc8cb36b15 --- /dev/null +++ b/src/test/ui/impl-trait/negative-reasoning.full_tait.stderr @@ -0,0 +1,23 @@ +warning: the feature `type_alias_impl_trait` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/negative-reasoning.rs:5:32 + | +LL | #![cfg_attr(full_tait, feature(type_alias_impl_trait))] + | ^^^^^^^^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #63063 <https://github.com/rust-lang/rust/issues/63063> for more information + +error[E0119]: conflicting implementations of trait `AnotherTrait` for type `D<impl OpaqueTrait>` + --> $DIR/negative-reasoning.rs:22:1 + | +LL | impl<T: std::fmt::Debug> AnotherTrait for T {} + | ------------------------------------------- first implementation here +... +LL | impl AnotherTrait for D<OpaqueType> { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `D<impl OpaqueTrait>` + | + = note: upstream crates may add a new impl of trait `std::fmt::Debug` for type `impl OpaqueTrait` in future versions + +error: aborting due to previous error; 1 warning emitted + +For more information about this error, try `rustc --explain E0119`. diff --git a/src/test/ui/impl-trait/negative-reasoning.min_tait.stderr b/src/test/ui/impl-trait/negative-reasoning.min_tait.stderr new file mode 100644 index 0000000000000..5727a372ddbe8 --- /dev/null +++ b/src/test/ui/impl-trait/negative-reasoning.min_tait.stderr @@ -0,0 +1,14 @@ +error[E0119]: conflicting implementations of trait `AnotherTrait` for type `D<impl OpaqueTrait>` + --> $DIR/negative-reasoning.rs:22:1 + | +LL | impl<T: std::fmt::Debug> AnotherTrait for T {} + | ------------------------------------------- first implementation here +... +LL | impl AnotherTrait for D<OpaqueType> { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `D<impl OpaqueTrait>` + | + = note: upstream crates may add a new impl of trait `std::fmt::Debug` for type `impl OpaqueTrait` in future versions + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0119`. diff --git a/src/test/ui/impl-trait/negative-reasoning.rs b/src/test/ui/impl-trait/negative-reasoning.rs index d173fe83fb791..7f608cc99db15 100644 --- a/src/test/ui/impl-trait/negative-reasoning.rs +++ b/src/test/ui/impl-trait/negative-reasoning.rs @@ -1,6 +1,9 @@ // Tests that we cannot assume that an opaque type does *not* implement some // other trait -#![feature(type_alias_impl_trait)] +// revisions: min_tait full_tait +#![feature(min_type_alias_impl_trait)] +#![cfg_attr(full_tait, feature(type_alias_impl_trait))] +//[full_tait]~^ WARN incomplete trait OpaqueTrait {} impl<T> OpaqueTrait for T {} diff --git a/src/test/ui/impl-trait/negative-reasoning.stderr b/src/test/ui/impl-trait/negative-reasoning.stderr deleted file mode 100644 index e43d8c857b257..0000000000000 --- a/src/test/ui/impl-trait/negative-reasoning.stderr +++ /dev/null @@ -1,14 +0,0 @@ -error[E0119]: conflicting implementations of trait `AnotherTrait` for type `D<impl OpaqueTrait>`: - --> $DIR/negative-reasoning.rs:19:1 - | -LL | impl<T: std::fmt::Debug> AnotherTrait for T {} - | ------------------------------------------- first implementation here -... -LL | impl AnotherTrait for D<OpaqueType> { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `D<impl OpaqueTrait>` - | - = note: upstream crates may add a new impl of trait `std::fmt::Debug` for type `impl OpaqueTrait` in future versions - -error: aborting due to previous error - -For more information about this error, try `rustc --explain E0119`. diff --git a/src/test/ui/impl-trait/projection-mismatch-in-impl-where-clause.rs b/src/test/ui/impl-trait/projection-mismatch-in-impl-where-clause.rs new file mode 100644 index 0000000000000..b4fd6b3e74364 --- /dev/null +++ b/src/test/ui/impl-trait/projection-mismatch-in-impl-where-clause.rs @@ -0,0 +1,20 @@ +pub trait Super { + type Assoc; +} + +impl Super for () { + type Assoc = u8; +} + +pub trait Test {} + +impl<T> Test for T where T: Super<Assoc = ()> {} + +fn test() -> impl Test { + //~^ERROR type mismatch resolving `<() as Super>::Assoc == ()` + () +} + +fn main() { + let a = test(); +} diff --git a/src/test/ui/impl-trait/projection-mismatch-in-impl-where-clause.stderr b/src/test/ui/impl-trait/projection-mismatch-in-impl-where-clause.stderr new file mode 100644 index 0000000000000..54eb5a96c9d81 --- /dev/null +++ b/src/test/ui/impl-trait/projection-mismatch-in-impl-where-clause.stderr @@ -0,0 +1,15 @@ +error[E0271]: type mismatch resolving `<() as Super>::Assoc == ()` + --> $DIR/projection-mismatch-in-impl-where-clause.rs:13:14 + | +LL | fn test() -> impl Test { + | ^^^^^^^^^ expected `()`, found `u8` + | +note: required because of the requirements on the impl of `Test` for `()` + --> $DIR/projection-mismatch-in-impl-where-clause.rs:11:9 + | +LL | impl<T> Test for T where T: Super<Assoc = ()> {} + | ^^^^ ^ + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0271`. diff --git a/src/test/ui/impl-trait/trait_type.stderr b/src/test/ui/impl-trait/trait_type.stderr index 961bb7351181e..bea24339837a2 100644 --- a/src/test/ui/impl-trait/trait_type.stderr +++ b/src/test/ui/impl-trait/trait_type.stderr @@ -1,8 +1,11 @@ error[E0053]: method `fmt` has an incompatible type for trait - --> $DIR/trait_type.rs:7:4 + --> $DIR/trait_type.rs:7:21 | LL | fn fmt(&self, x: &str) -> () { } - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ types differ in mutability + | ^^^^ + | | + | types differ in mutability + | help: change the parameter type to match the trait: `&mut Formatter<'_>` | = note: expected fn pointer `fn(&MyType, &mut Formatter<'_>) -> Result<(), std::fmt::Error>` found fn pointer `fn(&MyType, &str)` diff --git a/src/test/ui/impl-trait/type-alias-generic-param.full_tait.stderr b/src/test/ui/impl-trait/type-alias-generic-param.full_tait.stderr new file mode 100644 index 0000000000000..965e3e4887bb2 --- /dev/null +++ b/src/test/ui/impl-trait/type-alias-generic-param.full_tait.stderr @@ -0,0 +1,11 @@ +warning: the feature `type_alias_impl_trait` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/type-alias-generic-param.rs:8:32 + | +LL | #![cfg_attr(full_tait, feature(type_alias_impl_trait))] + | ^^^^^^^^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #63063 <https://github.com/rust-lang/rust/issues/63063> for more information + +warning: 1 warning emitted + diff --git a/src/test/ui/impl-trait/type-alias-generic-param.rs b/src/test/ui/impl-trait/type-alias-generic-param.rs index d834d9bb112f5..733e10e56f88e 100644 --- a/src/test/ui/impl-trait/type-alias-generic-param.rs +++ b/src/test/ui/impl-trait/type-alias-generic-param.rs @@ -3,7 +3,10 @@ // types in 'item' position when generic parameters are involved // // run-pass -#![feature(type_alias_impl_trait)] +// revisions: min_tait full_tait +#![feature(min_type_alias_impl_trait)] +#![cfg_attr(full_tait, feature(type_alias_impl_trait))] +//[full_tait]~^ WARN incomplete trait Meow { type MeowType; diff --git a/src/test/ui/impl-trait/type-alias-impl-trait-in-fn-body.full_tait.stderr b/src/test/ui/impl-trait/type-alias-impl-trait-in-fn-body.full_tait.stderr new file mode 100644 index 0000000000000..ec939e5718a94 --- /dev/null +++ b/src/test/ui/impl-trait/type-alias-impl-trait-in-fn-body.full_tait.stderr @@ -0,0 +1,11 @@ +warning: the feature `type_alias_impl_trait` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/type-alias-impl-trait-in-fn-body.rs:5:32 + | +LL | #![cfg_attr(full_tait, feature(type_alias_impl_trait))] + | ^^^^^^^^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #63063 <https://github.com/rust-lang/rust/issues/63063> for more information + +warning: 1 warning emitted + diff --git a/src/test/ui/impl-trait/type-alias-impl-trait-in-fn-body.rs b/src/test/ui/impl-trait/type-alias-impl-trait-in-fn-body.rs index 91be4efd56a15..32ca4af304348 100644 --- a/src/test/ui/impl-trait/type-alias-impl-trait-in-fn-body.rs +++ b/src/test/ui/impl-trait/type-alias-impl-trait-in-fn-body.rs @@ -1,6 +1,9 @@ // build-pass (FIXME(62277): could be check-pass?) -#![feature(type_alias_impl_trait)] +// revisions: min_tait full_tait +#![feature(min_type_alias_impl_trait)] +#![cfg_attr(full_tait, feature(type_alias_impl_trait))] +//[full_tait]~^ WARN incomplete use std::fmt::Debug; diff --git a/src/test/ui/impl-trait/universal-two-impl-traits.rs b/src/test/ui/impl-trait/universal-two-impl-traits.rs index 1ed47d8f49ead..689c240128d80 100644 --- a/src/test/ui/impl-trait/universal-two-impl-traits.rs +++ b/src/test/ui/impl-trait/universal-two-impl-traits.rs @@ -6,4 +6,11 @@ fn foo(x: impl Debug, y: impl Debug) -> String { format!("{:?}", a) } -fn main() { } +trait S<T> {} + +fn much_universe<T: S<impl Debug>, U: IntoIterator<Item = impl Iterator<Item = impl Clone>>>( + _: impl Debug + Clone, +) { +} + +fn main() {} diff --git a/src/test/ui/impl-trait/where-allowed.rs b/src/test/ui/impl-trait/where-allowed.rs index 72b880fb92c65..c3e21c81f03be 100644 --- a/src/test/ui/impl-trait/where-allowed.rs +++ b/src/test/ui/impl-trait/where-allowed.rs @@ -56,12 +56,10 @@ fn in_impl_Fn_return_in_parameters(_: &impl Fn() -> impl Debug) { panic!() } fn in_impl_Fn_parameter_in_return() -> &'static impl Fn(impl Debug) { panic!() } //~^ ERROR `impl Trait` not allowed outside of function and inherent method return types //~| ERROR nested `impl Trait` is not allowed -//~| ERROR cannot resolve opaque type // Disallowed fn in_impl_Fn_return_in_return() -> &'static impl Fn() -> impl Debug { panic!() } //~^ ERROR `impl Trait` not allowed outside of function and inherent method return types -//~| ERROR cannot resolve opaque type // Disallowed fn in_Fn_parameter_in_generics<F: Fn(impl Debug)> (_: F) { panic!() } @@ -120,7 +118,6 @@ trait DummyTrait { impl DummyTrait for () { type Out = impl Debug; //~^ ERROR `impl Trait` in type aliases is unstable - //~^^ ERROR could not find defining uses fn in_trait_impl_parameter(_: impl Debug) { } // Allowed @@ -156,7 +153,6 @@ extern "C" fn in_extern_fn_return() -> impl Debug { type InTypeAlias<R> = impl Debug; //~^ ERROR `impl Trait` in type aliases is unstable -//~^^ ERROR could not find defining uses type InReturnInTypeAlias<R> = fn() -> impl Debug; //~^ ERROR `impl Trait` not allowed outside of function and inherent method return types @@ -218,6 +214,34 @@ fn in_Fn_return_in_fn_where_clause<T>() { } +// Disallowed +struct InStructGenericParamDefault<T = impl Debug>(T); +//~^ ERROR `impl Trait` not allowed outside of function and inherent method return types + +// Disallowed +enum InEnumGenericParamDefault<T = impl Debug> { Variant(T) } +//~^ ERROR `impl Trait` not allowed outside of function and inherent method return types + +// Disallowed +trait InTraitGenericParamDefault<T = impl Debug> {} +//~^ ERROR `impl Trait` not allowed outside of function and inherent method return types + +// Disallowed +type InTypeAliasGenericParamDefault<T = impl Debug> = T; +//~^ ERROR `impl Trait` not allowed outside of function and inherent method return types + +// Disallowed +impl <T = impl Debug> T {} +//~^ ERROR defaults for type parameters are only allowed in `struct`, `enum`, `type`, or `trait` definitions +//~| WARNING this was previously accepted by the compiler but is being phased out +//~| ERROR `impl Trait` not allowed outside of function and inherent method return types + +// Disallowed +fn in_method_generic_param_default<T = impl Debug>(_: T) {} +//~^ ERROR defaults for type parameters are only allowed in `struct`, `enum`, `type`, or `trait` definitions +//~| WARNING this was previously accepted by the compiler but is being phased out +//~| ERROR `impl Trait` not allowed outside of function and inherent method return types + fn main() { let _in_local_variable: impl Fn() = || {}; //~^ ERROR `impl Trait` not allowed outside of function and inherent method return types diff --git a/src/test/ui/impl-trait/where-allowed.stderr b/src/test/ui/impl-trait/where-allowed.stderr index 93f9724140ef6..09ec4d5b202c3 100644 --- a/src/test/ui/impl-trait/where-allowed.stderr +++ b/src/test/ui/impl-trait/where-allowed.stderr @@ -17,31 +17,31 @@ LL | fn in_impl_Fn_parameter_in_return() -> &'static impl Fn(impl Debug) { panic | outer `impl Trait` error[E0658]: `impl Trait` in type aliases is unstable - --> $DIR/where-allowed.rs:121:16 + --> $DIR/where-allowed.rs:119:16 | LL | type Out = impl Debug; | ^^^^^^^^^^ | = note: see issue #63063 <https://github.com/rust-lang/rust/issues/63063> for more information - = help: add `#![feature(type_alias_impl_trait)]` to the crate attributes to enable + = help: add `#![feature(min_type_alias_impl_trait)]` to the crate attributes to enable error[E0658]: `impl Trait` in type aliases is unstable - --> $DIR/where-allowed.rs:157:23 + --> $DIR/where-allowed.rs:154:23 | LL | type InTypeAlias<R> = impl Debug; | ^^^^^^^^^^ | = note: see issue #63063 <https://github.com/rust-lang/rust/issues/63063> for more information - = help: add `#![feature(type_alias_impl_trait)]` to the crate attributes to enable + = help: add `#![feature(min_type_alias_impl_trait)]` to the crate attributes to enable error[E0658]: `impl Trait` in type aliases is unstable - --> $DIR/where-allowed.rs:161:39 + --> $DIR/where-allowed.rs:157:39 | LL | type InReturnInTypeAlias<R> = fn() -> impl Debug; | ^^^^^^^^^^ | = note: see issue #63063 <https://github.com/rust-lang/rust/issues/63063> for more information - = help: add `#![feature(type_alias_impl_trait)]` to the crate attributes to enable + = help: add `#![feature(min_type_alias_impl_trait)]` to the crate attributes to enable error[E0562]: `impl Trait` not allowed outside of function and inherent method return types --> $DIR/where-allowed.rs:15:40 @@ -110,139 +110,175 @@ LL | fn in_impl_Fn_parameter_in_return() -> &'static impl Fn(impl Debug) { panic | ^^^^^^^^^^ error[E0562]: `impl Trait` not allowed outside of function and inherent method return types - --> $DIR/where-allowed.rs:62:59 + --> $DIR/where-allowed.rs:61:59 | LL | fn in_impl_Fn_return_in_return() -> &'static impl Fn() -> impl Debug { panic!() } | ^^^^^^^^^^ error[E0562]: `impl Trait` not allowed outside of function and inherent method return types - --> $DIR/where-allowed.rs:67:38 + --> $DIR/where-allowed.rs:65:38 | LL | fn in_Fn_parameter_in_generics<F: Fn(impl Debug)> (_: F) { panic!() } | ^^^^^^^^^^ error[E0562]: `impl Trait` not allowed outside of function and inherent method return types - --> $DIR/where-allowed.rs:71:40 + --> $DIR/where-allowed.rs:69:40 | LL | fn in_Fn_return_in_generics<F: Fn() -> impl Debug> (_: F) { panic!() } | ^^^^^^^^^^ error[E0562]: `impl Trait` not allowed outside of function and inherent method return types - --> $DIR/where-allowed.rs:84:32 + --> $DIR/where-allowed.rs:82:32 | LL | struct InBraceStructField { x: impl Debug } | ^^^^^^^^^^ error[E0562]: `impl Trait` not allowed outside of function and inherent method return types - --> $DIR/where-allowed.rs:88:41 + --> $DIR/where-allowed.rs:86:41 | LL | struct InAdtInBraceStructField { x: Vec<impl Debug> } | ^^^^^^^^^^ error[E0562]: `impl Trait` not allowed outside of function and inherent method return types - --> $DIR/where-allowed.rs:92:27 + --> $DIR/where-allowed.rs:90:27 | LL | struct InTupleStructField(impl Debug); | ^^^^^^^^^^ error[E0562]: `impl Trait` not allowed outside of function and inherent method return types - --> $DIR/where-allowed.rs:97:25 + --> $DIR/where-allowed.rs:95:25 | LL | InBraceVariant { x: impl Debug }, | ^^^^^^^^^^ error[E0562]: `impl Trait` not allowed outside of function and inherent method return types - --> $DIR/where-allowed.rs:99:20 + --> $DIR/where-allowed.rs:97:20 | LL | InTupleVariant(impl Debug), | ^^^^^^^^^^ error[E0562]: `impl Trait` not allowed outside of function and inherent method return types - --> $DIR/where-allowed.rs:110:23 + --> $DIR/where-allowed.rs:108:23 | LL | fn in_return() -> impl Debug; | ^^^^^^^^^^ error[E0562]: `impl Trait` not allowed outside of function and inherent method return types - --> $DIR/where-allowed.rs:128:34 + --> $DIR/where-allowed.rs:125:34 | LL | fn in_trait_impl_return() -> impl Debug { () } | ^^^^^^^^^^ error[E0562]: `impl Trait` not allowed outside of function and inherent method return types - --> $DIR/where-allowed.rs:141:33 + --> $DIR/where-allowed.rs:138:33 | LL | fn in_foreign_parameters(_: impl Debug); | ^^^^^^^^^^ error[E0562]: `impl Trait` not allowed outside of function and inherent method return types - --> $DIR/where-allowed.rs:144:31 + --> $DIR/where-allowed.rs:141:31 | LL | fn in_foreign_return() -> impl Debug; | ^^^^^^^^^^ error[E0562]: `impl Trait` not allowed outside of function and inherent method return types - --> $DIR/where-allowed.rs:161:39 + --> $DIR/where-allowed.rs:157:39 | LL | type InReturnInTypeAlias<R> = fn() -> impl Debug; | ^^^^^^^^^^ error[E0562]: `impl Trait` not allowed outside of function and inherent method return types - --> $DIR/where-allowed.rs:166:16 + --> $DIR/where-allowed.rs:162:16 | LL | impl PartialEq<impl Debug> for () { | ^^^^^^^^^^ error[E0562]: `impl Trait` not allowed outside of function and inherent method return types - --> $DIR/where-allowed.rs:171:24 + --> $DIR/where-allowed.rs:167:24 | LL | impl PartialEq<()> for impl Debug { | ^^^^^^^^^^ error[E0562]: `impl Trait` not allowed outside of function and inherent method return types - --> $DIR/where-allowed.rs:176:6 + --> $DIR/where-allowed.rs:172:6 | LL | impl impl Debug { | ^^^^^^^^^^ error[E0562]: `impl Trait` not allowed outside of function and inherent method return types - --> $DIR/where-allowed.rs:182:24 + --> $DIR/where-allowed.rs:178:24 | LL | impl InInherentImplAdt<impl Debug> { | ^^^^^^^^^^ error[E0562]: `impl Trait` not allowed outside of function and inherent method return types - --> $DIR/where-allowed.rs:188:11 + --> $DIR/where-allowed.rs:184:11 | LL | where impl Debug: Debug | ^^^^^^^^^^ error[E0562]: `impl Trait` not allowed outside of function and inherent method return types - --> $DIR/where-allowed.rs:195:15 + --> $DIR/where-allowed.rs:191:15 | LL | where Vec<impl Debug>: Debug | ^^^^^^^^^^ error[E0562]: `impl Trait` not allowed outside of function and inherent method return types - --> $DIR/where-allowed.rs:202:24 + --> $DIR/where-allowed.rs:198:24 | LL | where T: PartialEq<impl Debug> | ^^^^^^^^^^ error[E0562]: `impl Trait` not allowed outside of function and inherent method return types - --> $DIR/where-allowed.rs:209:17 + --> $DIR/where-allowed.rs:205:17 | LL | where T: Fn(impl Debug) | ^^^^^^^^^^ error[E0562]: `impl Trait` not allowed outside of function and inherent method return types - --> $DIR/where-allowed.rs:216:22 + --> $DIR/where-allowed.rs:212:22 | LL | where T: Fn() -> impl Debug | ^^^^^^^^^^ error[E0562]: `impl Trait` not allowed outside of function and inherent method return types - --> $DIR/where-allowed.rs:222:29 + --> $DIR/where-allowed.rs:218:40 + | +LL | struct InStructGenericParamDefault<T = impl Debug>(T); + | ^^^^^^^^^^ + +error[E0562]: `impl Trait` not allowed outside of function and inherent method return types + --> $DIR/where-allowed.rs:222:36 + | +LL | enum InEnumGenericParamDefault<T = impl Debug> { Variant(T) } + | ^^^^^^^^^^ + +error[E0562]: `impl Trait` not allowed outside of function and inherent method return types + --> $DIR/where-allowed.rs:226:38 + | +LL | trait InTraitGenericParamDefault<T = impl Debug> {} + | ^^^^^^^^^^ + +error[E0562]: `impl Trait` not allowed outside of function and inherent method return types + --> $DIR/where-allowed.rs:230:41 + | +LL | type InTypeAliasGenericParamDefault<T = impl Debug> = T; + | ^^^^^^^^^^ + +error[E0562]: `impl Trait` not allowed outside of function and inherent method return types + --> $DIR/where-allowed.rs:234:11 + | +LL | impl <T = impl Debug> T {} + | ^^^^^^^^^^ + +error[E0562]: `impl Trait` not allowed outside of function and inherent method return types + --> $DIR/where-allowed.rs:240:40 + | +LL | fn in_method_generic_param_default<T = impl Debug>(_: T) {} + | ^^^^^^^^^^ + +error[E0562]: `impl Trait` not allowed outside of function and inherent method return types + --> $DIR/where-allowed.rs:246:29 | LL | let _in_local_variable: impl Fn() = || {}; | ^^^^^^^^^ @@ -250,44 +286,31 @@ LL | let _in_local_variable: impl Fn() = || {}; = help: add `#![feature(impl_trait_in_bindings)]` to the crate attributes to enable error[E0562]: `impl Trait` not allowed outside of function and inherent method return types - --> $DIR/where-allowed.rs:224:46 + --> $DIR/where-allowed.rs:248:46 | LL | let _in_return_in_local_variable = || -> impl Fn() { || {} }; | ^^^^^^^^^ -error[E0720]: cannot resolve opaque type - --> $DIR/where-allowed.rs:56:49 - | -LL | fn in_impl_Fn_parameter_in_return() -> &'static impl Fn(impl Debug) { panic!() } - | ^^^^^^^^^^^^^^^^^^^ -------- this returned value is of `!` type - | | - | cannot resolve opaque type - | - = help: this error will resolve once the item's body returns a concrete type - -error[E0720]: cannot resolve opaque type - --> $DIR/where-allowed.rs:62:46 +error: defaults for type parameters are only allowed in `struct`, `enum`, `type`, or `trait` definitions + --> $DIR/where-allowed.rs:234:7 | -LL | fn in_impl_Fn_return_in_return() -> &'static impl Fn() -> impl Debug { panic!() } - | ^^^^^^^^^^^^^^^^^^^^^^^ -------- this returned value is of `!` type - | | - | cannot resolve opaque type +LL | impl <T = impl Debug> T {} + | ^ | - = help: this error will resolve once the item's body returns a concrete type + = note: `#[deny(invalid_type_param_default)]` on by default + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #36887 <https://github.com/rust-lang/rust/issues/36887> -error: could not find defining uses - --> $DIR/where-allowed.rs:121:16 +error: defaults for type parameters are only allowed in `struct`, `enum`, `type`, or `trait` definitions + --> $DIR/where-allowed.rs:240:36 | -LL | type Out = impl Debug; - | ^^^^^^^^^^ - -error: could not find defining uses - --> $DIR/where-allowed.rs:157:23 +LL | fn in_method_generic_param_default<T = impl Debug>(_: T) {} + | ^ | -LL | type InTypeAlias<R> = impl Debug; - | ^^^^^^^^^^ + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #36887 <https://github.com/rust-lang/rust/issues/36887> -error: aborting due to 44 previous errors +error: aborting due to 48 previous errors -Some errors have detailed explanations: E0562, E0658, E0666, E0720. +Some errors have detailed explanations: E0562, E0658, E0666. For more information about an error, try `rustc --explain E0562`. diff --git a/src/test/ui/imports/extern-prelude-extern-crate-fail.rs b/src/test/ui/imports/extern-prelude-extern-crate-fail.rs index 4a0c612020194..feb1ab09dc9e2 100644 --- a/src/test/ui/imports/extern-prelude-extern-crate-fail.rs +++ b/src/test/ui/imports/extern-prelude-extern-crate-fail.rs @@ -1,5 +1,3 @@ -// ignore-tidy-linelength - // aux-build:two_macros.rs // compile-flags:--extern non_existent diff --git a/src/test/ui/imports/extern-prelude-extern-crate-fail.stderr b/src/test/ui/imports/extern-prelude-extern-crate-fail.stderr index 2d7a1bf468e33..011ea02050820 100644 --- a/src/test/ui/imports/extern-prelude-extern-crate-fail.stderr +++ b/src/test/ui/imports/extern-prelude-extern-crate-fail.stderr @@ -1,5 +1,5 @@ error: macro-expanded `extern crate` items cannot shadow names passed with `--extern` - --> $DIR/extern-prelude-extern-crate-fail.rs:18:9 + --> $DIR/extern-prelude-extern-crate-fail.rs:16:9 | LL | extern crate std as non_existent; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -10,7 +10,7 @@ LL | define_std_as_non_existent!(); = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) error[E0433]: failed to resolve: use of undeclared crate or module `two_macros` - --> $DIR/extern-prelude-extern-crate-fail.rs:12:9 + --> $DIR/extern-prelude-extern-crate-fail.rs:10:9 | LL | two_macros::m!(); | ^^^^^^^^^^ use of undeclared crate or module `two_macros` diff --git a/src/test/ui/imports/extern-prelude-extern-crate-restricted-shadowing.stderr b/src/test/ui/imports/extern-prelude-extern-crate-restricted-shadowing.stderr index 016c48118b3ce..4cda07758527a 100644 --- a/src/test/ui/imports/extern-prelude-extern-crate-restricted-shadowing.stderr +++ b/src/test/ui/imports/extern-prelude-extern-crate-restricted-shadowing.stderr @@ -24,10 +24,10 @@ LL | extern crate std as Vec; LL | define_vec!(); | -------------- in this macro invocation note: `Vec` could also refer to the struct defined here - --> $SRC_DIR/std/src/prelude/v1.rs:LL:COL + --> $SRC_DIR/std/src/prelude/mod.rs:LL:COL | -LL | pub use crate::vec::Vec; - | ^^^^^^^^^^^^^^^ +LL | pub use super::v1::*; + | ^^^^^^^^^^^^ = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) error: aborting due to 2 previous errors diff --git a/src/test/ui/imports/local-modularized-tricky-fail-2.rs b/src/test/ui/imports/local-modularized-tricky-fail-2.rs index e637edadb0074..386de88bc3d62 100644 --- a/src/test/ui/imports/local-modularized-tricky-fail-2.rs +++ b/src/test/ui/imports/local-modularized-tricky-fail-2.rs @@ -1,48 +1,22 @@ -// `#[macro_export] macro_rules` that doesn't originate from macro expansions can be placed -// into the root module soon enough to act as usual items and shadow globs and preludes. +// Crate-local macro expanded `macro_export` macros cannot be accessed with module-relative paths. -#![feature(decl_macro)] - -// `macro_export` shadows globs -use inner1::*; - -mod inner1 { - pub macro exported() {} -} - -exported!(); - -mod deep { - fn deep() { - type Deeper = [u8; { - #[macro_export] - macro_rules! exported { - () => ( struct Б; ) //~ ERROR non-ascii idents are not fully supported - } - - 0 - }]; +macro_rules! define_exported { () => { + #[macro_export] + macro_rules! exported { + () => () } -} +}} -// `macro_export` shadows std prelude -fn main() { - panic!(); -} +define_exported!(); -mod inner3 { - #[macro_export] - macro_rules! panic { - () => ( struct Г; ) //~ ERROR non-ascii idents are not fully supported - } +mod m { + use exported; + //~^ ERROR macro-expanded `macro_export` macros from the current crate cannot + //~| WARN this was previously accepted } -// `macro_export` shadows builtin macros -include!(); - -mod inner4 { - #[macro_export] - macro_rules! include { - () => ( struct Д; ) //~ ERROR non-ascii idents are not fully supported - } +fn main() { + ::exported!(); + //~^ ERROR macro-expanded `macro_export` macros from the current crate cannot + //~| WARN this was previously accepted } diff --git a/src/test/ui/imports/local-modularized-tricky-fail-2.stderr b/src/test/ui/imports/local-modularized-tricky-fail-2.stderr index 714c04add5f79..5cc6fa1e40a88 100644 --- a/src/test/ui/imports/local-modularized-tricky-fail-2.stderr +++ b/src/test/ui/imports/local-modularized-tricky-fail-2.stderr @@ -1,42 +1,43 @@ -error[E0658]: non-ascii idents are not fully supported - --> $DIR/local-modularized-tricky-fail-2.rs:20:32 +error: macro-expanded `macro_export` macros from the current crate cannot be referred to by absolute paths + --> $DIR/local-modularized-tricky-fail-2.rs:13:9 | -LL | exported!(); - | ------------ in this macro invocation -... -LL | () => ( struct Б; ) - | ^ +LL | use exported; + | ^^^^^^^^ | - = note: see issue #55467 <https://github.com/rust-lang/rust/issues/55467> for more information - = help: add `#![feature(non_ascii_idents)]` to the crate attributes to enable - = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) - -error[E0658]: non-ascii idents are not fully supported - --> $DIR/local-modularized-tricky-fail-2.rs:36:24 + = note: `#[deny(macro_expanded_macro_exports_accessed_by_absolute_paths)]` on by default + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #52234 <https://github.com/rust-lang/rust/issues/52234> +note: the macro is defined here + --> $DIR/local-modularized-tricky-fail-2.rs:5:5 | -LL | panic!(); - | --------- in this macro invocation +LL | / macro_rules! exported { +LL | | () => () +LL | | } + | |_____^ ... -LL | () => ( struct Г; ) - | ^ - | - = note: see issue #55467 <https://github.com/rust-lang/rust/issues/55467> for more information - = help: add `#![feature(non_ascii_idents)]` to the crate attributes to enable +LL | define_exported!(); + | ------------------- in this macro invocation = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) -error[E0658]: non-ascii idents are not fully supported - --> $DIR/local-modularized-tricky-fail-2.rs:46:24 +error: macro-expanded `macro_export` macros from the current crate cannot be referred to by absolute paths + --> $DIR/local-modularized-tricky-fail-2.rs:19:5 | -LL | include!(); - | ----------- in this macro invocation -... -LL | () => ( struct Д; ) - | ^ +LL | ::exported!(); + | ^^^^^^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #52234 <https://github.com/rust-lang/rust/issues/52234> +note: the macro is defined here + --> $DIR/local-modularized-tricky-fail-2.rs:5:5 | - = note: see issue #55467 <https://github.com/rust-lang/rust/issues/55467> for more information - = help: add `#![feature(non_ascii_idents)]` to the crate attributes to enable +LL | / macro_rules! exported { +LL | | () => () +LL | | } + | |_____^ +... +LL | define_exported!(); + | ------------------- in this macro invocation = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) -error: aborting due to 3 previous errors +error: aborting due to 2 previous errors -For more information about this error, try `rustc --explain E0658`. diff --git a/src/test/ui/imports/local-modularized-tricky-fail-3.rs b/src/test/ui/imports/local-modularized-tricky-fail-3.rs deleted file mode 100644 index 386de88bc3d62..0000000000000 --- a/src/test/ui/imports/local-modularized-tricky-fail-3.rs +++ /dev/null @@ -1,22 +0,0 @@ -// Crate-local macro expanded `macro_export` macros cannot be accessed with module-relative paths. - -macro_rules! define_exported { () => { - #[macro_export] - macro_rules! exported { - () => () - } -}} - -define_exported!(); - -mod m { - use exported; - //~^ ERROR macro-expanded `macro_export` macros from the current crate cannot - //~| WARN this was previously accepted -} - -fn main() { - ::exported!(); - //~^ ERROR macro-expanded `macro_export` macros from the current crate cannot - //~| WARN this was previously accepted -} diff --git a/src/test/ui/imports/local-modularized-tricky-fail-3.stderr b/src/test/ui/imports/local-modularized-tricky-fail-3.stderr deleted file mode 100644 index 4494a88a5cf95..0000000000000 --- a/src/test/ui/imports/local-modularized-tricky-fail-3.stderr +++ /dev/null @@ -1,43 +0,0 @@ -error: macro-expanded `macro_export` macros from the current crate cannot be referred to by absolute paths - --> $DIR/local-modularized-tricky-fail-3.rs:13:9 - | -LL | use exported; - | ^^^^^^^^ - | - = note: `#[deny(macro_expanded_macro_exports_accessed_by_absolute_paths)]` on by default - = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = note: for more information, see issue #52234 <https://github.com/rust-lang/rust/issues/52234> -note: the macro is defined here - --> $DIR/local-modularized-tricky-fail-3.rs:5:5 - | -LL | / macro_rules! exported { -LL | | () => () -LL | | } - | |_____^ -... -LL | define_exported!(); - | ------------------- in this macro invocation - = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) - -error: macro-expanded `macro_export` macros from the current crate cannot be referred to by absolute paths - --> $DIR/local-modularized-tricky-fail-3.rs:19:5 - | -LL | ::exported!(); - | ^^^^^^^^^^ - | - = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = note: for more information, see issue #52234 <https://github.com/rust-lang/rust/issues/52234> -note: the macro is defined here - --> $DIR/local-modularized-tricky-fail-3.rs:5:5 - | -LL | / macro_rules! exported { -LL | | () => () -LL | | } - | |_____^ -... -LL | define_exported!(); - | ------------------- in this macro invocation - = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) - -error: aborting due to 2 previous errors - diff --git a/src/test/ui/imports/local-modularized-tricky-pass.rs b/src/test/ui/imports/local-modularized-tricky-pass-1.rs similarity index 100% rename from src/test/ui/imports/local-modularized-tricky-pass.rs rename to src/test/ui/imports/local-modularized-tricky-pass-1.rs diff --git a/src/test/ui/imports/local-modularized-tricky-pass-2.rs b/src/test/ui/imports/local-modularized-tricky-pass-2.rs new file mode 100644 index 0000000000000..d5efbdf78af0f --- /dev/null +++ b/src/test/ui/imports/local-modularized-tricky-pass-2.rs @@ -0,0 +1,50 @@ +// check-pass +// +// `#[macro_export] macro_rules` that doesn't originate from macro expansions can be placed +// into the root module soon enough to act as usual items and shadow globs and preludes. + +#![feature(decl_macro)] + +// `macro_export` shadows globs +use inner1::*; + +mod inner1 { + pub macro exported() {} +} + +exported!(); + +mod deep { + fn deep() { + type Deeper = [u8; { + #[macro_export] + macro_rules! exported { + () => ( struct Б; ) + } + + 0 + }]; + } +} + +// `macro_export` shadows std prelude +fn main() { + panic!(); +} + +mod inner3 { + #[macro_export] + macro_rules! panic { + () => ( struct Г; ) + } +} + +// `macro_export` shadows builtin macros +include!(); + +mod inner4 { + #[macro_export] + macro_rules! include { + () => ( struct Д; ) + } +} diff --git a/src/test/ui/imports/tool-mod-child.rs b/src/test/ui/imports/tool-mod-child.rs new file mode 100644 index 0000000000000..4581dc2e2ad88 --- /dev/null +++ b/src/test/ui/imports/tool-mod-child.rs @@ -0,0 +1,7 @@ +use clippy::a; //~ ERROR unresolved import `clippy` +use clippy::a::b; //~ ERROR failed to resolve: maybe a missing crate `clippy`? + +use rustdoc::a; //~ ERROR unresolved import `rustdoc` +use rustdoc::a::b; //~ ERROR failed to resolve: maybe a missing crate `rustdoc`? + +fn main() {} diff --git a/src/test/ui/imports/tool-mod-child.stderr b/src/test/ui/imports/tool-mod-child.stderr new file mode 100644 index 0000000000000..efab4f6a74f83 --- /dev/null +++ b/src/test/ui/imports/tool-mod-child.stderr @@ -0,0 +1,28 @@ +error[E0433]: failed to resolve: maybe a missing crate `clippy`? + --> $DIR/tool-mod-child.rs:2:5 + | +LL | use clippy::a::b; + | ^^^^^^ maybe a missing crate `clippy`? + +error[E0432]: unresolved import `clippy` + --> $DIR/tool-mod-child.rs:1:5 + | +LL | use clippy::a; + | ^^^^^^ maybe a missing crate `clippy`? + +error[E0433]: failed to resolve: maybe a missing crate `rustdoc`? + --> $DIR/tool-mod-child.rs:5:5 + | +LL | use rustdoc::a::b; + | ^^^^^^^ maybe a missing crate `rustdoc`? + +error[E0432]: unresolved import `rustdoc` + --> $DIR/tool-mod-child.rs:4:5 + | +LL | use rustdoc::a; + | ^^^^^^^ maybe a missing crate `rustdoc`? + +error: aborting due to 4 previous errors + +Some errors have detailed explanations: E0432, E0433. +For more information about an error, try `rustc --explain E0432`. diff --git a/src/test/ui/inference/issue-81522.rs b/src/test/ui/inference/issue-81522.rs new file mode 100644 index 0000000000000..902f8fdde58e9 --- /dev/null +++ b/src/test/ui/inference/issue-81522.rs @@ -0,0 +1,31 @@ +// Regression test for #81522. +// Ensures that `#[allow(unstable_name_collisions)]` appended to things other than function +// suppresses the corresponding diagnostics emitted from inside them. +// But note that this attribute doesn't work for macro invocations if it is appended directly. + +// aux-build:inference_unstable_iterator.rs +// aux-build:inference_unstable_itertools.rs +// run-pass + +extern crate inference_unstable_iterator; +extern crate inference_unstable_itertools; + +#[allow(unused_imports)] +use inference_unstable_iterator::IpuIterator; +use inference_unstable_itertools::IpuItertools; + +fn main() { + // expression statement + #[allow(unstable_name_collisions)] + 'x'.ipu_flatten(); + + // let statement + #[allow(unstable_name_collisions)] + let _ = 'x'.ipu_flatten(); + + // block expression + #[allow(unstable_name_collisions)] + { + 'x'.ipu_flatten(); + } +} diff --git a/src/test/ui/inference/issue-83606.rs b/src/test/ui/inference/issue-83606.rs new file mode 100644 index 0000000000000..be56a3020cc33 --- /dev/null +++ b/src/test/ui/inference/issue-83606.rs @@ -0,0 +1,10 @@ +// Regression test for #83606. + +fn foo<const N: usize>(_: impl std::fmt::Display) -> [usize; N] { + [0; N] +} + +fn main() { + let _ = foo("foo"); //<- Do not suggest `foo::<N>("foo");`! + //~^ ERROR: type annotations needed for `[usize; _]` +} diff --git a/src/test/ui/inference/issue-83606.stderr b/src/test/ui/inference/issue-83606.stderr new file mode 100644 index 0000000000000..65f3336b9358a --- /dev/null +++ b/src/test/ui/inference/issue-83606.stderr @@ -0,0 +1,11 @@ +error[E0282]: type annotations needed for `[usize; _]` + --> $DIR/issue-83606.rs:8:13 + | +LL | let _ = foo("foo"); //<- Do not suggest `foo::<N>("foo");`! + | - ^^^ cannot infer the value of const parameter `N` declared on the function `foo` + | | + | consider giving this pattern the explicit type `[usize; _]`, where the type parameter `N` is specified + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0282`. diff --git a/src/test/ui/bad/bad-intrinsic-monomorphization.rs b/src/test/ui/intrinsics/bad-intrinsic-monomorphization.rs similarity index 100% rename from src/test/ui/bad/bad-intrinsic-monomorphization.rs rename to src/test/ui/intrinsics/bad-intrinsic-monomorphization.rs diff --git a/src/test/ui/bad/bad-intrinsic-monomorphization.stderr b/src/test/ui/intrinsics/bad-intrinsic-monomorphization.stderr similarity index 100% rename from src/test/ui/bad/bad-intrinsic-monomorphization.stderr rename to src/test/ui/intrinsics/bad-intrinsic-monomorphization.stderr diff --git a/src/test/ui/intrinsics/intrinsic-alignment.rs b/src/test/ui/intrinsics/intrinsic-alignment.rs index 2bf40db5ad290..592409ba89f9b 100644 --- a/src/test/ui/intrinsics/intrinsic-alignment.rs +++ b/src/test/ui/intrinsics/intrinsic-alignment.rs @@ -1,7 +1,7 @@ // run-pass // ignore-wasm32-bare seems not important to test here -#![feature(intrinsics, main)] +#![feature(intrinsics)] mod rusti { extern "rust-intrinsic" { @@ -21,7 +21,6 @@ mod rusti { target_os = "solaris", target_os = "vxworks"))] mod m { - #[main] #[cfg(target_arch = "x86")] pub fn main() { unsafe { @@ -30,7 +29,6 @@ mod m { } } - #[main] #[cfg(not(target_arch = "x86"))] pub fn main() { unsafe { @@ -42,7 +40,6 @@ mod m { #[cfg(target_env = "sgx")] mod m { - #[main] #[cfg(target_arch = "x86_64")] pub fn main() { unsafe { @@ -54,7 +51,6 @@ mod m { #[cfg(target_os = "windows")] mod m { - #[main] pub fn main() { unsafe { assert_eq!(::rusti::pref_align_of::<u64>(), 8); @@ -62,3 +58,7 @@ mod m { } } } + +fn main() { + m::main(); +} diff --git a/src/test/ui/invalid/invalid_rustc_layout_scalar_valid_range.rs b/src/test/ui/invalid/invalid_rustc_layout_scalar_valid_range.rs new file mode 100644 index 0000000000000..06cf8c0f0f6d5 --- /dev/null +++ b/src/test/ui/invalid/invalid_rustc_layout_scalar_valid_range.rs @@ -0,0 +1,30 @@ +#![feature(rustc_attrs)] + +#[rustc_layout_scalar_valid_range_start(u32::MAX)] //~ ERROR +pub struct A(u32); + +#[rustc_layout_scalar_valid_range_end(1, 2)] //~ ERROR +pub struct B(u8); + +#[rustc_layout_scalar_valid_range_end(a = "a")] //~ ERROR +pub struct C(i32); + +#[rustc_layout_scalar_valid_range_end(1)] //~ ERROR +enum E { + X = 1, + Y = 14, +} + +#[rustc_layout_scalar_valid_range_start(rustc_layout_scalar_valid_range_start)] //~ ERROR +struct NonZero<T>(T); + +fn not_field() -> impl Send { + NonZero(false) +} + +fn main() { + let _ = A(0); + let _ = B(0); + let _ = C(0); + let _ = E::X; +} diff --git a/src/test/ui/invalid/invalid_rustc_layout_scalar_valid_range.stderr b/src/test/ui/invalid/invalid_rustc_layout_scalar_valid_range.stderr new file mode 100644 index 0000000000000..7879e7358c00a --- /dev/null +++ b/src/test/ui/invalid/invalid_rustc_layout_scalar_valid_range.stderr @@ -0,0 +1,37 @@ +error: expected exactly one integer literal argument + --> $DIR/invalid_rustc_layout_scalar_valid_range.rs:3:1 + | +LL | #[rustc_layout_scalar_valid_range_start(u32::MAX)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: expected exactly one integer literal argument + --> $DIR/invalid_rustc_layout_scalar_valid_range.rs:6:1 + | +LL | #[rustc_layout_scalar_valid_range_end(1, 2)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: expected exactly one integer literal argument + --> $DIR/invalid_rustc_layout_scalar_valid_range.rs:9:1 + | +LL | #[rustc_layout_scalar_valid_range_end(a = "a")] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: attribute should be applied to a struct + --> $DIR/invalid_rustc_layout_scalar_valid_range.rs:12:1 + | +LL | #[rustc_layout_scalar_valid_range_end(1)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | / enum E { +LL | | X = 1, +LL | | Y = 14, +LL | | } + | |_- not a struct + +error: expected exactly one integer literal argument + --> $DIR/invalid_rustc_layout_scalar_valid_range.rs:18:1 + | +LL | #[rustc_layout_scalar_valid_range_start(rustc_layout_scalar_valid_range_start)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 5 previous errors + diff --git a/src/test/ui/issue-83639.rs b/src/test/ui/issue-83639.rs new file mode 100644 index 0000000000000..6ddbedfa0bc03 --- /dev/null +++ b/src/test/ui/issue-83639.rs @@ -0,0 +1,6 @@ +// check-fail +// ignore-tidy-tab + +fn main() { + """ " //~ ERROR +} diff --git a/src/test/ui/issue-83639.stderr b/src/test/ui/issue-83639.stderr new file mode 100644 index 0000000000000..4c10df1917c9f --- /dev/null +++ b/src/test/ui/issue-83639.stderr @@ -0,0 +1,8 @@ +error: expected one of `.`, `;`, `?`, `}`, or an operator, found `" "` + --> $DIR/issue-83639.rs:5:7 + | +LL | """ " + | ^^^^^^ expected one of `.`, `;`, `?`, `}`, or an operator + +error: aborting due to previous error + diff --git a/src/test/ui/issues/issue-13033.stderr b/src/test/ui/issues/issue-13033.stderr index 57447fa48aacc..6c3651ff1217a 100644 --- a/src/test/ui/issues/issue-13033.stderr +++ b/src/test/ui/issues/issue-13033.stderr @@ -8,7 +8,7 @@ LL | fn bar(&mut self, other: &dyn Foo) {} | ^^^^^^^^ | | | types differ in mutability - | help: consider changing the mutability to match the trait: `&mut dyn Foo` + | help: change the parameter type to match the trait: `&mut dyn Foo` | = note: expected fn pointer `fn(&mut Baz, &mut dyn Foo)` found fn pointer `fn(&mut Baz, &dyn Foo)` diff --git a/src/test/ui/issues/issue-15487.rs b/src/test/ui/issues/issue-15487.rs deleted file mode 100644 index 34ac53be5becc..0000000000000 --- a/src/test/ui/issues/issue-15487.rs +++ /dev/null @@ -1,13 +0,0 @@ -// run-pass -#![allow(unused_attributes)] -// ignore-windows -// ignore-wasm32-bare no libs to link -// ignore-sgx no libs to link -#![feature(link_args)] - -#[link_args = "-lc -lm"] -#[link_args = " -lc"] -#[link_args = "-lc "] -extern "C" {} - -fn main() {} diff --git a/src/test/ui/issues/issue-18183.stderr b/src/test/ui/issues/issue-18183.stderr index c8f8ac9296dc0..a7dc64708d856 100644 --- a/src/test/ui/issues/issue-18183.stderr +++ b/src/test/ui/issues/issue-18183.stderr @@ -1,8 +1,8 @@ -error[E0128]: type parameters with a default cannot use forward declared identifiers +error[E0128]: generic parameters with a default cannot use forward declared identifiers --> $DIR/issue-18183.rs:1:20 | LL | pub struct Foo<Bar=Bar>(Bar); - | ^^^ defaulted type parameters cannot be forward declared + | ^^^ defaulted generic parameters cannot be forward declared error: aborting due to previous error diff --git a/src/test/ui/issues/issue-18400.stderr b/src/test/ui/issues/issue-18400.stderr index 3bd9c656e8bd5..92d53088442e6 100644 --- a/src/test/ui/issues/issue-18400.stderr +++ b/src/test/ui/issues/issue-18400.stderr @@ -5,7 +5,11 @@ LL | 0.contains(bits); | ^^^^^^^^ | = help: consider adding a `#![recursion_limit="256"]` attribute to your crate (`issue_18400`) - = note: required because of the requirements on the impl of `Set<&[_]>` for `{integer}` +note: required because of the requirements on the impl of `Set<&[_]>` for `{integer}` + --> $DIR/issue-18400.rs:6:16 + | +LL | impl<'a, T, S> Set<&'a [T]> for S where + | ^^^^^^^^^^^^ ^ = note: 128 redundant requirements hidden = note: required because of the requirements on the impl of `Set<&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[_]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]>` for `{integer}` diff --git a/src/test/ui/issues/issue-20225.stderr b/src/test/ui/issues/issue-20225.stderr index 3bcc50ded8425..6f4813ca6235b 100644 --- a/src/test/ui/issues/issue-20225.stderr +++ b/src/test/ui/issues/issue-20225.stderr @@ -1,33 +1,42 @@ error[E0053]: method `call` has an incompatible type for trait - --> $DIR/issue-20225.rs:6:3 + --> $DIR/issue-20225.rs:6:43 | LL | impl<'a, T> Fn<(&'a T,)> for Foo { | - this type parameter LL | extern "rust-call" fn call(&self, (_,): (T,)) {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `&T`, found type parameter `T` + | ^^^^ + | | + | expected `&T`, found type parameter `T` + | help: change the parameter type to match the trait: `(&'a T,)` | = note: expected fn pointer `extern "rust-call" fn(&Foo, (&'a T,))` found fn pointer `extern "rust-call" fn(&Foo, (T,))` error[E0053]: method `call_mut` has an incompatible type for trait - --> $DIR/issue-20225.rs:11:3 + --> $DIR/issue-20225.rs:11:51 | LL | impl<'a, T> FnMut<(&'a T,)> for Foo { | - this type parameter LL | extern "rust-call" fn call_mut(&mut self, (_,): (T,)) {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `&T`, found type parameter `T` + | ^^^^ + | | + | expected `&T`, found type parameter `T` + | help: change the parameter type to match the trait: `(&'a T,)` | = note: expected fn pointer `extern "rust-call" fn(&mut Foo, (&'a T,))` found fn pointer `extern "rust-call" fn(&mut Foo, (T,))` error[E0053]: method `call_once` has an incompatible type for trait - --> $DIR/issue-20225.rs:18:3 + --> $DIR/issue-20225.rs:18:47 | LL | impl<'a, T> FnOnce<(&'a T,)> for Foo { | - this type parameter ... LL | extern "rust-call" fn call_once(self, (_,): (T,)) {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `&T`, found type parameter `T` + | ^^^^ + | | + | expected `&T`, found type parameter `T` + | help: change the parameter type to match the trait: `(&'a T,)` | = note: expected fn pointer `extern "rust-call" fn(Foo, (&'a T,))` found fn pointer `extern "rust-call" fn(Foo, (T,))` diff --git a/src/test/ui/issues/issue-20413.stderr b/src/test/ui/issues/issue-20413.stderr index b167bb77b5101..7fb1e3f2bba4f 100644 --- a/src/test/ui/issues/issue-20413.stderr +++ b/src/test/ui/issues/issue-20413.stderr @@ -16,7 +16,11 @@ LL | impl<T> Foo for T where NoData<T>: Foo { | ^^^ | = help: consider adding a `#![recursion_limit="256"]` attribute to your crate (`issue_20413`) - = note: required because of the requirements on the impl of `Foo` for `NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<T>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>` +note: required because of the requirements on the impl of `Foo` for `NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<T>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>` + --> $DIR/issue-20413.rs:8:9 + | +LL | impl<T> Foo for T where NoData<T>: Foo { + | ^^^ ^ = note: 127 redundant requirements hidden = note: required because of the requirements on the impl of `Foo` for `NoData<T>` @@ -30,8 +34,16 @@ LL | impl<T> Bar for T where EvenLessData<T>: Baz { | ^^^ | = help: consider adding a `#![recursion_limit="256"]` attribute to your crate (`issue_20413`) - = note: required because of the requirements on the impl of `Bar` for `AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<T>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>` - = note: required because of the requirements on the impl of `Baz` for `EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<T>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>` +note: required because of the requirements on the impl of `Bar` for `AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<T>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>` + --> $DIR/issue-20413.rs:28:9 + | +LL | impl<T> Bar for T where EvenLessData<T>: Baz { + | ^^^ ^ +note: required because of the requirements on the impl of `Baz` for `EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<T>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>` + --> $DIR/issue-20413.rs:36:9 + | +LL | impl<T> Baz for T where AlmostNoData<T>: Bar { + | ^^^ ^ = note: 126 redundant requirements hidden = note: required because of the requirements on the impl of `Baz` for `EvenLessData<T>` @@ -45,8 +57,16 @@ LL | impl<T> Baz for T where AlmostNoData<T>: Bar { | ^^^ | = help: consider adding a `#![recursion_limit="256"]` attribute to your crate (`issue_20413`) - = note: required because of the requirements on the impl of `Baz` for `EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<T>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>` - = note: required because of the requirements on the impl of `Bar` for `AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<T>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>` +note: required because of the requirements on the impl of `Baz` for `EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<T>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>` + --> $DIR/issue-20413.rs:36:9 + | +LL | impl<T> Baz for T where AlmostNoData<T>: Bar { + | ^^^ ^ +note: required because of the requirements on the impl of `Bar` for `AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<T>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>` + --> $DIR/issue-20413.rs:28:9 + | +LL | impl<T> Bar for T where EvenLessData<T>: Baz { + | ^^^ ^ = note: 126 redundant requirements hidden = note: required because of the requirements on the impl of `Bar` for `AlmostNoData<T>` @@ -60,7 +80,11 @@ LL | impl<T> Foo for T where NoData<T>: Foo { | ^^^ | = help: consider adding a `#![recursion_limit="256"]` attribute to your crate (`issue_20413`) - = note: required because of the requirements on the impl of `Foo` for `NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<T>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>` +note: required because of the requirements on the impl of `Foo` for `NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<T>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>` + --> $DIR/issue-20413.rs:8:9 + | +LL | impl<T> Foo for T where NoData<T>: Foo { + | ^^^ ^ = note: 127 redundant requirements hidden = note: required because of the requirements on the impl of `Foo` for `NoData<T>` @@ -74,8 +98,16 @@ LL | impl<T> Bar for T where EvenLessData<T>: Baz { | ^^^ | = help: consider adding a `#![recursion_limit="256"]` attribute to your crate (`issue_20413`) - = note: required because of the requirements on the impl of `Bar` for `AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<T>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>` - = note: required because of the requirements on the impl of `Baz` for `EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<T>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>` +note: required because of the requirements on the impl of `Bar` for `AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<T>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>` + --> $DIR/issue-20413.rs:28:9 + | +LL | impl<T> Bar for T where EvenLessData<T>: Baz { + | ^^^ ^ +note: required because of the requirements on the impl of `Baz` for `EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<T>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>` + --> $DIR/issue-20413.rs:36:9 + | +LL | impl<T> Baz for T where AlmostNoData<T>: Bar { + | ^^^ ^ = note: 126 redundant requirements hidden = note: required because of the requirements on the impl of `Baz` for `EvenLessData<T>` @@ -89,8 +121,16 @@ LL | impl<T> Baz for T where AlmostNoData<T>: Bar { | ^^^ | = help: consider adding a `#![recursion_limit="256"]` attribute to your crate (`issue_20413`) - = note: required because of the requirements on the impl of `Baz` for `EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<T>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>` - = note: required because of the requirements on the impl of `Bar` for `AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<T>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>` +note: required because of the requirements on the impl of `Baz` for `EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<T>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>` + --> $DIR/issue-20413.rs:36:9 + | +LL | impl<T> Baz for T where AlmostNoData<T>: Bar { + | ^^^ ^ +note: required because of the requirements on the impl of `Bar` for `AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<T>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>` + --> $DIR/issue-20413.rs:28:9 + | +LL | impl<T> Bar for T where EvenLessData<T>: Baz { + | ^^^ ^ = note: 126 redundant requirements hidden = note: required because of the requirements on the impl of `Bar` for `AlmostNoData<T>` diff --git a/src/test/ui/issues/issue-21332.stderr b/src/test/ui/issues/issue-21332.stderr index 35863fbebe315..d92966da17c4c 100644 --- a/src/test/ui/issues/issue-21332.stderr +++ b/src/test/ui/issues/issue-21332.stderr @@ -1,8 +1,11 @@ error[E0053]: method `next` has an incompatible type for trait - --> $DIR/issue-21332.rs:5:5 + --> $DIR/issue-21332.rs:5:27 | LL | fn next(&mut self) -> Result<i32, i32> { Ok(7) } - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected enum `Option`, found enum `Result` + | ^^^^^^^^^^^^^^^^ + | | + | expected enum `Option`, found enum `Result` + | help: change the output type to match the trait: `Option<i32>` | = note: expected fn pointer `fn(&mut S) -> Option<i32>` found fn pointer `fn(&mut S) -> Result<i32, i32>` diff --git a/src/test/ui/issues/issue-22872.stderr b/src/test/ui/issues/issue-22872.stderr index c65a97d999998..fd3db9546991c 100644 --- a/src/test/ui/issues/issue-22872.stderr +++ b/src/test/ui/issues/issue-22872.stderr @@ -5,7 +5,11 @@ LL | let _: Box<dyn for<'b> Wrap<'b>> = Box::new(Wrapper(process)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ `<P as Process<'_>>::Item` is not an iterator | = help: the trait `Iterator` is not implemented for `<P as Process<'_>>::Item` - = note: required because of the requirements on the impl of `for<'b> Wrap<'b>` for `Wrapper<P>` +note: required because of the requirements on the impl of `for<'b> Wrap<'b>` for `Wrapper<P>` + --> $DIR/issue-22872.rs:7:13 + | +LL | impl<'b, P> Wrap<'b> for Wrapper<P> + | ^^^^^^^^ ^^^^^^^^^^ = note: required for the cast to the object type `dyn for<'b> Wrap<'b>` help: consider further restricting the associated type | diff --git a/src/test/ui/issues/issue-23122-2.stderr b/src/test/ui/issues/issue-23122-2.stderr index ce3bffe602ca0..5008a499986d4 100644 --- a/src/test/ui/issues/issue-23122-2.stderr +++ b/src/test/ui/issues/issue-23122-2.stderr @@ -5,7 +5,11 @@ LL | type Next = <GetNext<T::Next> as Next>::Next; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = help: consider adding a `#![recursion_limit="256"]` attribute to your crate (`issue_23122_2`) - = note: required because of the requirements on the impl of `Next` for `GetNext<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<T as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next>` +note: required because of the requirements on the impl of `Next` for `GetNext<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<T as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next>` + --> $DIR/issue-23122-2.rs:8:15 + | +LL | impl<T: Next> Next for GetNext<T> { + | ^^^^ ^^^^^^^^^^ error: aborting due to previous error diff --git a/src/test/ui/issues/issue-2633-2.rs b/src/test/ui/issues/issue-2633-2.rs deleted file mode 100644 index 02c1a166301eb..0000000000000 --- a/src/test/ui/issues/issue-2633-2.rs +++ /dev/null @@ -1,14 +0,0 @@ -// run-pass -// pretty-expanded FIXME #23616 - -#![feature(box_syntax)] - - -fn a_val(x: Box<isize>, y: Box<isize>) -> isize { - *x + *y -} - -pub fn main() { - let z: Box<_> = box 22; - a_val(z.clone(), z.clone()); -} diff --git a/src/test/ui/issues/issue-2633.rs b/src/test/ui/issues/issue-2633.rs deleted file mode 100644 index 7e8cea75fc8db..0000000000000 --- a/src/test/ui/issues/issue-2633.rs +++ /dev/null @@ -1,31 +0,0 @@ -// run-pass -#![allow(dead_code)] -#![allow(non_camel_case_types)] - -#[derive(Copy, Clone)] -struct cat { - meow: extern "Rust" fn(), -} - -fn meow() { - println!("meow") -} - -fn cat() -> cat { - cat { - meow: meow, - } -} - -#[derive(Copy, Clone)] -struct KittyInfo {kitty: cat} - -// Code compiles and runs successfully if we add a + before the first arg -fn nyan(kitty: cat, _kitty_info: KittyInfo) { - (kitty.meow)(); -} - -pub fn main() { - let kitty = cat(); - nyan(kitty, KittyInfo {kitty: kitty}); -} diff --git a/src/test/ui/issues/issue-26812.rs b/src/test/ui/issues/issue-26812.rs index b2494a91a3f8a..3391ea4b350af 100644 --- a/src/test/ui/issues/issue-26812.rs +++ b/src/test/ui/issues/issue-26812.rs @@ -1,6 +1,6 @@ #![feature(default_type_parameter_fallback)] fn avg<T=T::Item>(_: T) {} -//~^ ERROR type parameters with a default cannot use forward declared identifiers +//~^ ERROR generic parameters with a default cannot use forward declared identifiers fn main() {} diff --git a/src/test/ui/issues/issue-26812.stderr b/src/test/ui/issues/issue-26812.stderr index 8d507a7ea0144..fd1bbb9c5673d 100644 --- a/src/test/ui/issues/issue-26812.stderr +++ b/src/test/ui/issues/issue-26812.stderr @@ -1,8 +1,8 @@ -error[E0128]: type parameters with a default cannot use forward declared identifiers +error[E0128]: generic parameters with a default cannot use forward declared identifiers --> $DIR/issue-26812.rs:3:10 | LL | fn avg<T=T::Item>(_: T) {} - | ^^^^^^^ defaulted type parameters cannot be forward declared + | ^^^^^^^ defaulted generic parameters cannot be forward declared error: aborting due to previous error diff --git a/src/test/ui/issues/issue-27033.stderr b/src/test/ui/issues/issue-27033.stderr index ad48fc23a386b..fa42611d0b32e 100644 --- a/src/test/ui/issues/issue-27033.stderr +++ b/src/test/ui/issues/issue-27033.stderr @@ -4,10 +4,10 @@ error[E0530]: match bindings cannot shadow unit variants LL | None @ _ => {} | ^^^^ cannot be named the same as a unit variant | - ::: $SRC_DIR/std/src/prelude/v1.rs:LL:COL + ::: $SRC_DIR/std/src/prelude/mod.rs:LL:COL | -LL | pub use crate::option::Option::{self, None, Some}; - | ---- the unit variant `None` is defined here +LL | pub use super::v1::*; + | ------------ the unit variant `None` is defined here error[E0530]: match bindings cannot shadow constants --> $DIR/issue-27033.rs:7:9 diff --git a/src/test/ui/issues/issue-27060-2.stderr b/src/test/ui/issues/issue-27060-2.stderr index c4faecbdf2f9e..5dbcc96e87488 100644 --- a/src/test/ui/issues/issue-27060-2.stderr +++ b/src/test/ui/issues/issue-27060-2.stderr @@ -2,7 +2,7 @@ error[E0277]: the size for values of type `T` cannot be known at compilation tim --> $DIR/issue-27060-2.rs:3:11 | LL | pub struct Bad<T: ?Sized> { - | - this type parameter needs to be `Sized` + | - this type parameter needs to be `std::marker::Sized` LL | data: T, | ^ doesn't have a size known at compile-time | diff --git a/src/test/ui/issues/issue-27060-rpass.rs b/src/test/ui/issues/issue-27060-rpass.rs index b20d614b3036b..d9159f6669d60 100644 --- a/src/test/ui/issues/issue-27060-rpass.rs +++ b/src/test/ui/issues/issue-27060-rpass.rs @@ -8,14 +8,12 @@ pub struct Good { } // kill this test when that turns to a hard error -#[allow(safe_packed_borrows)] +#[allow(unaligned_references)] fn main() { let good = Good { data: &0, data2: [&0, &0], aligned: [0; 32] }; - unsafe { - let _ = &good.data; // ok - let _ = &good.data2[0]; // ok - } + let _ = &good.data; // ok + let _ = &good.data2[0]; // ok let _ = &good.data; let _ = &good.data2[0]; diff --git a/src/test/ui/issues/issue-27060.rs b/src/test/ui/issues/issue-27060.rs index 78f2022ed38df..5317a61671945 100644 --- a/src/test/ui/issues/issue-27060.rs +++ b/src/test/ui/issues/issue-27060.rs @@ -5,7 +5,7 @@ pub struct Good { aligned: [u8; 32], } -#[deny(safe_packed_borrows)] +#[deny(unaligned_references)] fn main() { let good = Good { data: &0, @@ -13,14 +13,14 @@ fn main() { aligned: [0; 32] }; - unsafe { - let _ = &good.data; // ok - let _ = &good.data2[0]; // ok - } + let _ = &good.data; //~ ERROR reference to packed field + //~| hard error + let _ = &good.data2[0]; //~ ERROR reference to packed field + //~| hard error - let _ = &good.data; //~ ERROR borrow of packed field is unsafe + let _ = &good.data; //~ ERROR reference to packed field //~| hard error - let _ = &good.data2[0]; //~ ERROR borrow of packed field is unsafe + let _ = &good.data2[0]; //~ ERROR reference to packed field //~| hard error let _ = &*good.data; // ok, behind a pointer let _ = &good.aligned; // ok, has align 1 diff --git a/src/test/ui/issues/issue-27060.stderr b/src/test/ui/issues/issue-27060.stderr index d14ae4d41d5c5..09297884ed377 100644 --- a/src/test/ui/issues/issue-27060.stderr +++ b/src/test/ui/issues/issue-27060.stderr @@ -1,5 +1,5 @@ -error: borrow of packed field is unsafe and requires unsafe function or block (error E0133) - --> $DIR/issue-27060.rs:21:13 +error: reference to packed field is unaligned + --> $DIR/issue-27060.rs:16:13 | LL | let _ = &good.data; | ^^^^^^^^^^ @@ -7,21 +7,41 @@ LL | let _ = &good.data; note: the lint level is defined here --> $DIR/issue-27060.rs:8:8 | -LL | #[deny(safe_packed_borrows)] - | ^^^^^^^^^^^^^^^^^^^ +LL | #[deny(unaligned_references)] + | ^^^^^^^^^^^^^^^^^^^^ + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #82523 <https://github.com/rust-lang/rust/issues/82523> + = note: fields of packed structs are not properly aligned, and creating a misaligned reference is undefined behavior (even if that reference is never dereferenced) + +error: reference to packed field is unaligned + --> $DIR/issue-27060.rs:18:13 + | +LL | let _ = &good.data2[0]; + | ^^^^^^^^^^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #82523 <https://github.com/rust-lang/rust/issues/82523> + = note: fields of packed structs are not properly aligned, and creating a misaligned reference is undefined behavior (even if that reference is never dereferenced) + +error: reference to packed field is unaligned + --> $DIR/issue-27060.rs:21:13 + | +LL | let _ = &good.data; + | ^^^^^^^^^^ + | = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = note: for more information, see issue #46043 <https://github.com/rust-lang/rust/issues/46043> - = note: fields of packed structs might be misaligned: dereferencing a misaligned pointer or even just creating a misaligned reference is undefined behavior + = note: for more information, see issue #82523 <https://github.com/rust-lang/rust/issues/82523> + = note: fields of packed structs are not properly aligned, and creating a misaligned reference is undefined behavior (even if that reference is never dereferenced) -error: borrow of packed field is unsafe and requires unsafe function or block (error E0133) +error: reference to packed field is unaligned --> $DIR/issue-27060.rs:23:13 | LL | let _ = &good.data2[0]; | ^^^^^^^^^^^^^^ | = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = note: for more information, see issue #46043 <https://github.com/rust-lang/rust/issues/46043> - = note: fields of packed structs might be misaligned: dereferencing a misaligned pointer or even just creating a misaligned reference is undefined behavior + = note: for more information, see issue #82523 <https://github.com/rust-lang/rust/issues/82523> + = note: fields of packed structs are not properly aligned, and creating a misaligned reference is undefined behavior (even if that reference is never dereferenced) -error: aborting due to 2 previous errors +error: aborting due to 4 previous errors diff --git a/src/test/ui/issues/issue-2823.stderr b/src/test/ui/issues/issue-2823.stderr index e044352e954bc..b3bc946292f70 100644 --- a/src/test/ui/issues/issue-2823.stderr +++ b/src/test/ui/issues/issue-2823.stderr @@ -6,14 +6,6 @@ LL | struct C { ... LL | let _d = c.clone(); | ^^^^^ method not found in `C` - | - ::: $SRC_DIR/core/src/clone.rs:LL:COL - | -LL | fn clone(&self) -> Self; - | ----- - | | - | the method is available for `Arc<C>` here - | the method is available for `Rc<C>` here | = help: items from traits can only be used if the trait is implemented and in scope = note: the following trait defines an item `clone`, perhaps you need to implement it: diff --git a/src/test/ui/issues/issue-28568.stderr b/src/test/ui/issues/issue-28568.stderr index 7729b9d240d28..be3f7c627800d 100644 --- a/src/test/ui/issues/issue-28568.stderr +++ b/src/test/ui/issues/issue-28568.stderr @@ -1,4 +1,4 @@ -error[E0119]: conflicting implementations of trait `std::ops::Drop` for type `MyStruct`: +error[E0119]: conflicting implementations of trait `std::ops::Drop` for type `MyStruct` --> $DIR/issue-28568.rs:7:1 | LL | impl Drop for MyStruct { diff --git a/src/test/ui/issues/issue-3214.rs b/src/test/ui/issues/issue-3214.rs index 9bb164f1ddd2d..ccfaf23b4b9ff 100644 --- a/src/test/ui/issues/issue-3214.rs +++ b/src/test/ui/issues/issue-3214.rs @@ -1,5 +1,3 @@ -// ignore-tidy-linelength - fn foo<T>() { struct Foo { x: T, //~ ERROR can't use generic parameters from outer function diff --git a/src/test/ui/issues/issue-3214.stderr b/src/test/ui/issues/issue-3214.stderr index c2268924bc4e9..0da095b7fdab8 100644 --- a/src/test/ui/issues/issue-3214.stderr +++ b/src/test/ui/issues/issue-3214.stderr @@ -1,5 +1,5 @@ error[E0401]: can't use generic parameters from outer function - --> $DIR/issue-3214.rs:5:12 + --> $DIR/issue-3214.rs:3:12 | LL | fn foo<T>() { | --- - type parameter from outer function @@ -10,7 +10,7 @@ LL | x: T, | ^ use of generic parameter from outer function error[E0107]: this struct takes 0 type arguments but 1 type argument was supplied - --> $DIR/issue-3214.rs:8:22 + --> $DIR/issue-3214.rs:6:22 | LL | impl<T> Drop for Foo<T> { | ^^^--- help: remove these generics @@ -18,13 +18,13 @@ LL | impl<T> Drop for Foo<T> { | expected 0 type arguments | note: struct defined here, with 0 type parameters - --> $DIR/issue-3214.rs:4:12 + --> $DIR/issue-3214.rs:2:12 | LL | struct Foo { | ^^^ error[E0207]: the type parameter `T` is not constrained by the impl trait, self type, or predicates - --> $DIR/issue-3214.rs:8:10 + --> $DIR/issue-3214.rs:6:10 | LL | impl<T> Drop for Foo<T> { | ^ unconstrained type parameter diff --git a/src/test/ui/issues/issue-33941.stderr b/src/test/ui/issues/issue-33941.stderr index e91dae08b3a33..043658c9508f3 100644 --- a/src/test/ui/issues/issue-33941.stderr +++ b/src/test/ui/issues/issue-33941.stderr @@ -11,10 +11,10 @@ error[E0271]: type mismatch resolving `<std::collections::hash_map::Iter<'_, _, --> $DIR/issue-33941.rs:4:14 | LL | for _ in HashMap::new().iter().cloned() {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected tuple, found reference + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected reference, found tuple | - = note: expected tuple `(&_, &_)` - found reference `&_` + = note: expected reference `&_` + found tuple `(&_, &_)` = note: required because of the requirements on the impl of `Iterator` for `Cloned<std::collections::hash_map::Iter<'_, _, _>>` = note: required because of the requirements on the impl of `IntoIterator` for `Cloned<std::collections::hash_map::Iter<'_, _, _>>` = note: required by `into_iter` @@ -23,10 +23,10 @@ error[E0271]: type mismatch resolving `<std::collections::hash_map::Iter<'_, _, --> $DIR/issue-33941.rs:4:14 | LL | for _ in HashMap::new().iter().cloned() {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected tuple, found reference + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected reference, found tuple | - = note: expected tuple `(&_, &_)` - found reference `&_` + = note: expected reference `&_` + found tuple `(&_, &_)` = note: required because of the requirements on the impl of `Iterator` for `Cloned<std::collections::hash_map::Iter<'_, _, _>>` = note: required by `std::iter::Iterator::next` diff --git a/src/test/ui/issues/issue-35869.stderr b/src/test/ui/issues/issue-35869.stderr index f80561bf6be80..71b2a9df09553 100644 --- a/src/test/ui/issues/issue-35869.stderr +++ b/src/test/ui/issues/issue-35869.stderr @@ -5,7 +5,10 @@ LL | fn foo(_: fn(u8) -> ()); | ------------ type in trait ... LL | fn foo(_: fn(u16) -> ()) {} - | ^^^^^^^^^^^^^ expected `u8`, found `u16` + | ^^^^^^^^^^^^^ + | | + | expected `u8`, found `u16` + | help: change the parameter type to match the trait: `fn(u8)` | = note: expected fn pointer `fn(fn(u8))` found fn pointer `fn(fn(u16))` @@ -17,7 +20,10 @@ LL | fn bar(_: Option<u8>); | ---------- type in trait ... LL | fn bar(_: Option<u16>) {} - | ^^^^^^^^^^^ expected `u8`, found `u16` + | ^^^^^^^^^^^ + | | + | expected `u8`, found `u16` + | help: change the parameter type to match the trait: `Option<u8>` | = note: expected fn pointer `fn(Option<u8>)` found fn pointer `fn(Option<u16>)` @@ -29,7 +35,10 @@ LL | fn baz(_: (u8, u16)); | --------- type in trait ... LL | fn baz(_: (u16, u16)) {} - | ^^^^^^^^^^ expected `u8`, found `u16` + | ^^^^^^^^^^ + | | + | expected `u8`, found `u16` + | help: change the parameter type to match the trait: `(u8, u16)` | = note: expected fn pointer `fn((u8, _))` found fn pointer `fn((u16, _))` @@ -41,7 +50,10 @@ LL | fn qux() -> u8; | -- type in trait ... LL | fn qux() -> u16 { 5u16 } - | ^^^ expected `u8`, found `u16` + | ^^^ + | | + | expected `u8`, found `u16` + | help: change the output type to match the trait: `u8` | = note: expected fn pointer `fn() -> u8` found fn pointer `fn() -> u16` diff --git a/src/test/ui/issues/issue-38821.stderr b/src/test/ui/issues/issue-38821.stderr index e355094261de1..296efab7512f1 100644 --- a/src/test/ui/issues/issue-38821.stderr +++ b/src/test/ui/issues/issue-38821.stderr @@ -4,7 +4,11 @@ error[E0277]: the trait bound `<Col as Expression>::SqlType: NotNull` is not sat LL | #[derive(Debug, Copy, Clone)] | ^^^^ the trait `NotNull` is not implemented for `<Col as Expression>::SqlType` | - = note: required because of the requirements on the impl of `IntoNullable` for `<Col as Expression>::SqlType` +note: required because of the requirements on the impl of `IntoNullable` for `<Col as Expression>::SqlType` + --> $DIR/issue-38821.rs:9:18 + | +LL | impl<T: NotNull> IntoNullable for T { + | ^^^^^^^^^^^^ ^ = note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info) error: aborting due to previous error diff --git a/src/test/ui/issues/issue-39970.stderr b/src/test/ui/issues/issue-39970.stderr index 6f342b459c076..2a0693a581c31 100644 --- a/src/test/ui/issues/issue-39970.stderr +++ b/src/test/ui/issues/issue-39970.stderr @@ -5,9 +5,13 @@ LL | fn visit() {} | ---------- required by `Visit::visit` ... LL | <() as Visit>::visit(); - | ^^^^^^^^^^^^^^^^^^^^ expected `&()`, found `()` + | ^^^^^^^^^^^^^^^^^^^^ expected `()`, found `&()` | - = note: required because of the requirements on the impl of `Visit` for `()` +note: required because of the requirements on the impl of `Visit` for `()` + --> $DIR/issue-39970.rs:13:6 + | +LL | impl Visit for () where + | ^^^^^ ^^ error: aborting due to previous error diff --git a/src/test/ui/issues/issue-40827.stderr b/src/test/ui/issues/issue-40827.stderr index 5ea795d139715..38c3af935c564 100644 --- a/src/test/ui/issues/issue-40827.stderr +++ b/src/test/ui/issues/issue-40827.stderr @@ -1,30 +1,46 @@ -error[E0277]: `Rc<Foo>` cannot be sent between threads safely +error[E0277]: `Rc<Foo>` cannot be shared between threads safely --> $DIR/issue-40827.rs:14:5 | LL | fn f<T: Send>(_: T) {} | ---- required by this bound in `f` ... LL | f(Foo(Arc::new(Bar::B(None)))); - | ^ `Rc<Foo>` cannot be sent between threads safely + | ^ `Rc<Foo>` cannot be shared between threads safely | - = help: within `Bar`, the trait `Send` is not implemented for `Rc<Foo>` - = note: required because it appears within the type `Bar` + = help: within `Bar`, the trait `Sync` is not implemented for `Rc<Foo>` +note: required because it appears within the type `Bar` + --> $DIR/issue-40827.rs:6:6 + | +LL | enum Bar { + | ^^^ = note: required because of the requirements on the impl of `Send` for `Arc<Bar>` - = note: required because it appears within the type `Foo` +note: required because it appears within the type `Foo` + --> $DIR/issue-40827.rs:4:8 + | +LL | struct Foo(Arc<Bar>); + | ^^^ -error[E0277]: `Rc<Foo>` cannot be shared between threads safely +error[E0277]: `Rc<Foo>` cannot be sent between threads safely --> $DIR/issue-40827.rs:14:5 | LL | fn f<T: Send>(_: T) {} | ---- required by this bound in `f` ... LL | f(Foo(Arc::new(Bar::B(None)))); - | ^ `Rc<Foo>` cannot be shared between threads safely + | ^ `Rc<Foo>` cannot be sent between threads safely | - = help: within `Bar`, the trait `Sync` is not implemented for `Rc<Foo>` - = note: required because it appears within the type `Bar` + = help: within `Bar`, the trait `Send` is not implemented for `Rc<Foo>` +note: required because it appears within the type `Bar` + --> $DIR/issue-40827.rs:6:6 + | +LL | enum Bar { + | ^^^ = note: required because of the requirements on the impl of `Send` for `Arc<Bar>` - = note: required because it appears within the type `Foo` +note: required because it appears within the type `Foo` + --> $DIR/issue-40827.rs:4:8 + | +LL | struct Foo(Arc<Bar>); + | ^^^ error: aborting due to 2 previous errors diff --git a/src/test/ui/issues/issue-41974.stderr b/src/test/ui/issues/issue-41974.stderr index cde285f73d6b8..11d77857d60cc 100644 --- a/src/test/ui/issues/issue-41974.stderr +++ b/src/test/ui/issues/issue-41974.stderr @@ -1,4 +1,4 @@ -error[E0119]: conflicting implementations of trait `std::ops::Drop` for type `std::boxed::Box<_, _>`: +error[E0119]: conflicting implementations of trait `std::ops::Drop` for type `std::boxed::Box<_, _>` --> $DIR/issue-41974.rs:7:1 | LL | impl<T> Drop for T where T: A { diff --git a/src/test/ui/issues/issue-43355.stderr b/src/test/ui/issues/issue-43355.stderr index 75c69e5b3e3f5..23d8ed1848f59 100644 --- a/src/test/ui/issues/issue-43355.stderr +++ b/src/test/ui/issues/issue-43355.stderr @@ -1,4 +1,4 @@ -error[E0119]: conflicting implementations of trait `Trait1<std::boxed::Box<_>>` for type `A`: +error[E0119]: conflicting implementations of trait `Trait1<std::boxed::Box<_>>` for type `A` --> $DIR/issue-43355.rs:13:1 | LL | impl<X, T> Trait1<X> for T where T: Trait2<X> { diff --git a/src/test/ui/issues/issue-43784-supertrait.stderr b/src/test/ui/issues/issue-43784-supertrait.stderr index c73536ba97332..45a2350ddfd0e 100644 --- a/src/test/ui/issues/issue-43784-supertrait.stderr +++ b/src/test/ui/issues/issue-43784-supertrait.stderr @@ -9,8 +9,8 @@ LL | impl<T> Complete for T {} | help: consider restricting type parameter `T` | -LL | impl<T: Copy> Complete for T {} - | ^^^^^^ +LL | impl<T: std::marker::Copy> Complete for T {} + | ^^^^^^^^^^^^^^^^^^^ error: aborting due to previous error diff --git a/src/test/ui/issues/issue-43988.rs b/src/test/ui/issues/issue-43988.rs index 4b3a0269baea2..b114e8e03333d 100644 --- a/src/test/ui/issues/issue-43988.rs +++ b/src/test/ui/issues/issue-43988.rs @@ -13,13 +13,13 @@ fn main() { #[repr(nothing)] let _x = 0; - //~^^ ERROR attribute should be applied to a struct, enum, or union + //~^^ ERROR E0552 #[repr(something_not_real)] loop { () }; - //~^^^^ ERROR attribute should be applied to a struct, enum, or union + //~^^^^ ERROR E0552 #[repr] let _y = "123"; diff --git a/src/test/ui/issues/issue-43988.stderr b/src/test/ui/issues/issue-43988.stderr index f1205d447e4ba..03aa37f52075f 100644 --- a/src/test/ui/issues/issue-43988.stderr +++ b/src/test/ui/issues/issue-43988.stderr @@ -26,23 +26,17 @@ LL | #[inline(XYZ)] LL | let _b = 4; | ----------- not a function or closure -error[E0517]: attribute should be applied to a struct, enum, or union +error[E0552]: unrecognized representation hint --> $DIR/issue-43988.rs:14:12 | LL | #[repr(nothing)] | ^^^^^^^ -LL | let _x = 0; - | ----------- not a struct, enum, or union -error[E0517]: attribute should be applied to a struct, enum, or union +error[E0552]: unrecognized representation hint --> $DIR/issue-43988.rs:18:12 | -LL | #[repr(something_not_real)] - | ^^^^^^^^^^^^^^^^^^ -LL | / loop { -LL | | () -LL | | }; - | |_____- not a struct, enum, or union +LL | #[repr(something_not_real)] + | ^^^^^^^^^^^^^^^^^^ error[E0518]: attribute should be applied to function or closure --> $DIR/issue-43988.rs:30:5 @@ -54,5 +48,5 @@ LL | foo(); error: aborting due to 7 previous errors -Some errors have detailed explanations: E0517, E0518. -For more information about an error, try `rustc --explain E0517`. +Some errors have detailed explanations: E0518, E0552. +For more information about an error, try `rustc --explain E0518`. diff --git a/src/test/ui/issues/issue-44023.rs b/src/test/ui/issues/issue-44023.rs index 4c38ddfcdf189..e4320b7dac592 100644 --- a/src/test/ui/issues/issue-44023.rs +++ b/src/test/ui/issues/issue-44023.rs @@ -1,5 +1,3 @@ -#![feature(non_ascii_idents)] - pub fn main () {} fn საჭმელად_გემრიელი_სადილი ( ) -> isize { //~ ERROR mismatched types diff --git a/src/test/ui/issues/issue-44023.stderr b/src/test/ui/issues/issue-44023.stderr index fc54e7c62bb24..9e97012416a94 100644 --- a/src/test/ui/issues/issue-44023.stderr +++ b/src/test/ui/issues/issue-44023.stderr @@ -1,5 +1,5 @@ error[E0308]: mismatched types - --> $DIR/issue-44023.rs:5:36 + --> $DIR/issue-44023.rs:3:36 | LL | fn საჭმელად_გემრიელი_სადილი ( ) -> isize { | ------------------------ ^^^^^ expected `isize`, found `()` diff --git a/src/test/ui/issues/issue-45157.rs b/src/test/ui/issues/issue-45157.rs index bd18784289fe2..8d2bf22a03c9a 100644 --- a/src/test/ui/issues/issue-45157.rs +++ b/src/test/ui/issues/issue-45157.rs @@ -1,6 +1,5 @@ #![allow(unused)] -// ignore-tidy-linelength #[derive(Clone, Copy, Default)] struct S { diff --git a/src/test/ui/issues/issue-45157.stderr b/src/test/ui/issues/issue-45157.stderr index 1b879e0b48c87..57fd8d49c887c 100644 --- a/src/test/ui/issues/issue-45157.stderr +++ b/src/test/ui/issues/issue-45157.stderr @@ -1,5 +1,5 @@ error[E0502]: cannot borrow `u` (via `u.z.c`) as immutable because it is also borrowed as mutable (via `u.s.a`) - --> $DIR/issue-45157.rs:28:20 + --> $DIR/issue-45157.rs:27:20 | LL | let mref = &mut u.s.a; | ---------- mutable borrow occurs here (via `u.s.a`) diff --git a/src/test/ui/issues/issue-47725.rs b/src/test/ui/issues/issue-47725.rs index 21108da500614..9ec55be58723c 100644 --- a/src/test/ui/issues/issue-47725.rs +++ b/src/test/ui/issues/issue-47725.rs @@ -1,4 +1,3 @@ -// ignore-tidy-linelength #![warn(unused_attributes)] //~ NOTE lint level is defined here #[link_name = "foo"] diff --git a/src/test/ui/issues/issue-47725.stderr b/src/test/ui/issues/issue-47725.stderr index b1e8d3292eb93..c7a9bfe317f00 100644 --- a/src/test/ui/issues/issue-47725.stderr +++ b/src/test/ui/issues/issue-47725.stderr @@ -1,11 +1,11 @@ error: malformed `link_name` attribute input - --> $DIR/issue-47725.rs:18:1 + --> $DIR/issue-47725.rs:17:1 | LL | #[link_name] | ^^^^^^^^^^^^ help: must be of the form: `#[link_name = "name"]` warning: attribute should be applied to a foreign function or static - --> $DIR/issue-47725.rs:4:1 + --> $DIR/issue-47725.rs:3:1 | LL | #[link_name = "foo"] | ^^^^^^^^^^^^^^^^^^^^ @@ -14,14 +14,14 @@ LL | struct Foo; | ----------- not a foreign function or static | note: the lint level is defined here - --> $DIR/issue-47725.rs:2:9 + --> $DIR/issue-47725.rs:1:9 | LL | #![warn(unused_attributes)] | ^^^^^^^^^^^^^^^^^ = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! warning: attribute should be applied to a foreign function or static - --> $DIR/issue-47725.rs:9:1 + --> $DIR/issue-47725.rs:8:1 | LL | #[link_name = "foobar"] | ^^^^^^^^^^^^^^^^^^^^^^^ @@ -33,13 +33,13 @@ LL | | } | = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! help: try `#[link(name = "foobar")]` instead - --> $DIR/issue-47725.rs:9:1 + --> $DIR/issue-47725.rs:8:1 | LL | #[link_name = "foobar"] | ^^^^^^^^^^^^^^^^^^^^^^^ warning: attribute should be applied to a foreign function or static - --> $DIR/issue-47725.rs:18:1 + --> $DIR/issue-47725.rs:17:1 | LL | #[link_name] | ^^^^^^^^^^^^ @@ -51,7 +51,7 @@ LL | | } | = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! help: try `#[link(name = "...")]` instead - --> $DIR/issue-47725.rs:18:1 + --> $DIR/issue-47725.rs:17:1 | LL | #[link_name] | ^^^^^^^^^^^^ diff --git a/src/test/ui/issues/issue-48728.stderr b/src/test/ui/issues/issue-48728.stderr index a0698c2079835..ca2e234cee668 100644 --- a/src/test/ui/issues/issue-48728.stderr +++ b/src/test/ui/issues/issue-48728.stderr @@ -1,4 +1,4 @@ -error[E0119]: conflicting implementations of trait `std::clone::Clone` for type `Node<[_]>`: +error[E0119]: conflicting implementations of trait `std::clone::Clone` for type `Node<[_]>` --> $DIR/issue-48728.rs:4:10 | LL | #[derive(Clone)] diff --git a/src/test/ui/issues/issue-50411.rs b/src/test/ui/issues/issue-50411.rs index 1bf1270781747..cd728b15256e9 100644 --- a/src/test/ui/issues/issue-50411.rs +++ b/src/test/ui/issues/issue-50411.rs @@ -3,7 +3,7 @@ // elaborate-drops invoked on it) and then try to elaboate drops a // second time. Uncool. -// compile-flags:-Zmir-opt-level=3 +// compile-flags:-Zmir-opt-level=4 // build-pass fn main() { diff --git a/src/test/ui/issues/issue-50865-private-impl-trait/auxiliary/lib.rs b/src/test/ui/issues/issue-50865-private-impl-trait/auxiliary/lib.rs index fb4bf2b8b44e7..1e20a54606955 100644 --- a/src/test/ui/issues/issue-50865-private-impl-trait/auxiliary/lib.rs +++ b/src/test/ui/issues/issue-50865-private-impl-trait/auxiliary/lib.rs @@ -1,5 +1,5 @@ // revisions: default miropt -//[miropt]compile-flags: -Z mir-opt-level=2 +//[miropt]compile-flags: -Z mir-opt-level=3 // ~^ This flag is for #77668, it used to be ICE. #![crate_type = "lib"] diff --git a/src/test/ui/issues/issue-53251.rs b/src/test/ui/issues/issue-53251.rs index 309b9800b7d37..937271d42f4bd 100644 --- a/src/test/ui/issues/issue-53251.rs +++ b/src/test/ui/issues/issue-53251.rs @@ -1,5 +1,3 @@ -// ignore-tidy-linelength - struct S; impl S { diff --git a/src/test/ui/issues/issue-53251.stderr b/src/test/ui/issues/issue-53251.stderr index 5d1a6d4a522f6..1676c508a4dcc 100644 --- a/src/test/ui/issues/issue-53251.stderr +++ b/src/test/ui/issues/issue-53251.stderr @@ -1,5 +1,5 @@ error[E0107]: this associated function takes 0 type arguments but 1 type argument was supplied - --> $DIR/issue-53251.rs:13:20 + --> $DIR/issue-53251.rs:11:20 | LL | S::f::<i64>(); | ^------- help: remove these generics @@ -10,14 +10,14 @@ LL | impl_add!(a b); | --------------- in this macro invocation | note: associated function defined here, with 0 type parameters - --> $DIR/issue-53251.rs:6:8 + --> $DIR/issue-53251.rs:4:8 | LL | fn f() {} | ^ = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) error[E0107]: this associated function takes 0 type arguments but 1 type argument was supplied - --> $DIR/issue-53251.rs:13:20 + --> $DIR/issue-53251.rs:11:20 | LL | S::f::<i64>(); | ^------- help: remove these generics @@ -28,7 +28,7 @@ LL | impl_add!(a b); | --------------- in this macro invocation | note: associated function defined here, with 0 type parameters - --> $DIR/issue-53251.rs:6:8 + --> $DIR/issue-53251.rs:4:8 | LL | fn f() {} | ^ diff --git a/src/test/ui/issues/issue-54044.rs b/src/test/ui/issues/issue-54044.rs index 3f0b8bc5e384e..809ea7a87dbea 100644 --- a/src/test/ui/issues/issue-54044.rs +++ b/src/test/ui/issues/issue-54044.rs @@ -1,4 +1,3 @@ -// ignore-tidy-linelength #![deny(unused_attributes)] //~ NOTE lint level is defined here #[cold] diff --git a/src/test/ui/issues/issue-54044.stderr b/src/test/ui/issues/issue-54044.stderr index a13e84bbee1a6..0200a6a629d8f 100644 --- a/src/test/ui/issues/issue-54044.stderr +++ b/src/test/ui/issues/issue-54044.stderr @@ -1,5 +1,5 @@ error: attribute should be applied to a function - --> $DIR/issue-54044.rs:4:1 + --> $DIR/issue-54044.rs:3:1 | LL | #[cold] | ^^^^^^^ @@ -8,14 +8,14 @@ LL | struct Foo; | ----------- not a function | note: the lint level is defined here - --> $DIR/issue-54044.rs:2:9 + --> $DIR/issue-54044.rs:1:9 | LL | #![deny(unused_attributes)] | ^^^^^^^^^^^^^^^^^ = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! error: attribute should be applied to a function - --> $DIR/issue-54044.rs:10:5 + --> $DIR/issue-54044.rs:9:5 | LL | #[cold] | ^^^^^^^ diff --git a/src/test/ui/issues/issue-5883.rs b/src/test/ui/issues/issue-5883.rs index 0de535023972e..82866b355573c 100644 --- a/src/test/ui/issues/issue-5883.rs +++ b/src/test/ui/issues/issue-5883.rs @@ -4,9 +4,9 @@ struct Struct { r: dyn A + 'static } -fn new_struct(r: dyn A + 'static) - -> Struct { //~^ ERROR the size for values of type - //~^ ERROR the size for values of type +fn new_struct( + r: dyn A + 'static //~ ERROR the size for values of type +) -> Struct { //~ ERROR the size for values of type Struct { r: r } } diff --git a/src/test/ui/issues/issue-5883.stderr b/src/test/ui/issues/issue-5883.stderr index 5798733e04be0..de598a70ee06a 100644 --- a/src/test/ui/issues/issue-5883.stderr +++ b/src/test/ui/issues/issue-5883.stderr @@ -1,27 +1,30 @@ error[E0277]: the size for values of type `(dyn A + 'static)` cannot be known at compilation time - --> $DIR/issue-5883.rs:7:15 + --> $DIR/issue-5883.rs:8:5 | -LL | fn new_struct(r: dyn A + 'static) - | ^ doesn't have a size known at compile-time +LL | r: dyn A + 'static + | ^ doesn't have a size known at compile-time | = help: the trait `Sized` is not implemented for `(dyn A + 'static)` = help: unsized fn params are gated as an unstable feature help: function arguments must have a statically known size, borrowed types always have a known size | -LL | fn new_struct(&r: dyn A + 'static) - | ^ +LL | r: &dyn A + 'static + | ^ error[E0277]: the size for values of type `(dyn A + 'static)` cannot be known at compilation time - --> $DIR/issue-5883.rs:8:8 + --> $DIR/issue-5883.rs:9:6 | -LL | -> Struct { - | ^^^^^^ doesn't have a size known at compile-time -LL | +LL | ) -> Struct { + | ^^^^^^ doesn't have a size known at compile-time LL | Struct { r: r } | --------------- this returned value is of type `Struct` | = help: within `Struct`, the trait `Sized` is not implemented for `(dyn A + 'static)` - = note: required because it appears within the type `Struct` +note: required because it appears within the type `Struct` + --> $DIR/issue-5883.rs:3:8 + | +LL | struct Struct { + | ^^^^^^ = note: the return type of a function must have a statically known size error: aborting due to 2 previous errors diff --git a/src/test/ui/issues/issue-60622.rs b/src/test/ui/issues/issue-60622.rs index 1d9bd2dd2dc1c..1018c88ae55e3 100644 --- a/src/test/ui/issues/issue-60622.rs +++ b/src/test/ui/issues/issue-60622.rs @@ -1,5 +1,3 @@ -// ignore-tidy-linelength - #![deny(warnings)] struct Borked {} diff --git a/src/test/ui/issues/issue-60622.stderr b/src/test/ui/issues/issue-60622.stderr index 47f2f181f2d7a..f970a63e4b2fe 100644 --- a/src/test/ui/issues/issue-60622.stderr +++ b/src/test/ui/issues/issue-60622.stderr @@ -1,5 +1,5 @@ error: cannot specify lifetime arguments explicitly if late bound lifetime parameters are present - --> $DIR/issue-60622.rs:12:11 + --> $DIR/issue-60622.rs:10:11 | LL | fn a(&self) {} | - the late bound lifetime parameter is introduced here @@ -8,7 +8,7 @@ LL | b.a::<'_, T>(); | ^^ | note: the lint level is defined here - --> $DIR/issue-60622.rs:3:9 + --> $DIR/issue-60622.rs:1:9 | LL | #![deny(warnings)] | ^^^^^^^^ @@ -17,7 +17,7 @@ LL | #![deny(warnings)] = note: for more information, see issue #42868 <https://github.com/rust-lang/rust/issues/42868> error[E0107]: this associated function takes 0 type arguments but 1 type argument was supplied - --> $DIR/issue-60622.rs:12:7 + --> $DIR/issue-60622.rs:10:7 | LL | b.a::<'_, T>(); | ^ --- help: remove this type argument @@ -25,7 +25,7 @@ LL | b.a::<'_, T>(); | expected 0 type arguments | note: associated function defined here, with 0 type parameters - --> $DIR/issue-60622.rs:8:8 + --> $DIR/issue-60622.rs:6:8 | LL | fn a(&self) {} | ^ diff --git a/src/test/ui/issues/issue-60662.full_tait.stderr b/src/test/ui/issues/issue-60662.full_tait.stderr new file mode 100644 index 0000000000000..a448f85b7625d --- /dev/null +++ b/src/test/ui/issues/issue-60662.full_tait.stderr @@ -0,0 +1,11 @@ +warning: the feature `type_alias_impl_trait` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/issue-60662.rs:6:32 + | +LL | #![cfg_attr(full_tait, feature(type_alias_impl_trait))] + | ^^^^^^^^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #63063 <https://github.com/rust-lang/rust/issues/63063> for more information + +warning: 1 warning emitted + diff --git a/src/test/ui/issues/issue-60662.full_tait.stdout b/src/test/ui/issues/issue-60662.full_tait.stdout new file mode 100644 index 0000000000000..9ee4434d55959 --- /dev/null +++ b/src/test/ui/issues/issue-60662.full_tait.stdout @@ -0,0 +1,16 @@ +// check-pass +// compile-flags: -Z unpretty=hir + +// revisions: min_tait full_tait +#![feature(min_type_alias_impl_trait)] +#![feature(type_alias_impl_trait)] +#[prelude_import] +use ::std::prelude::rust_2015::*; +#[macro_use] +extern crate std; + +trait Animal { } + +fn main() { + pub type ServeFut = /*impl Trait*/; + } diff --git a/src/test/ui/issues/issue-60662.min_tait.stdout b/src/test/ui/issues/issue-60662.min_tait.stdout new file mode 100644 index 0000000000000..d0db578f57091 --- /dev/null +++ b/src/test/ui/issues/issue-60662.min_tait.stdout @@ -0,0 +1,15 @@ +// check-pass +// compile-flags: -Z unpretty=hir + +// revisions: min_tait full_tait +#![feature(min_type_alias_impl_trait)] +#[prelude_import] +use ::std::prelude::rust_2015::*; +#[macro_use] +extern crate std; + +trait Animal { } + +fn main() { + pub type ServeFut = /*impl Trait*/; + } diff --git a/src/test/ui/issues/issue-60662.rs b/src/test/ui/issues/issue-60662.rs index 967c5f8ed7313..1320d2bbe93d0 100644 --- a/src/test/ui/issues/issue-60662.rs +++ b/src/test/ui/issues/issue-60662.rs @@ -1,7 +1,10 @@ // check-pass // compile-flags: -Z unpretty=hir -#![feature(type_alias_impl_trait)] +// revisions: min_tait full_tait +#![feature(min_type_alias_impl_trait)] +#![cfg_attr(full_tait, feature(type_alias_impl_trait))] +//[full_tait]~^ WARN incomplete trait Animal { } diff --git a/src/test/ui/issues/issue-60662.stdout b/src/test/ui/issues/issue-60662.stdout deleted file mode 100644 index cebe834824a61..0000000000000 --- a/src/test/ui/issues/issue-60662.stdout +++ /dev/null @@ -1,14 +0,0 @@ -// check-pass -// compile-flags: -Z unpretty=hir - -#![feature(type_alias_impl_trait)] -#[prelude_import] -use ::std::prelude::v1::*; -#[macro_use] -extern crate std; - -trait Animal { } - -fn main() { - pub type ServeFut = /*impl Trait*/; - } diff --git a/src/test/ui/issues/issue-69225-SCEVAddExpr-wrap-flag.rs b/src/test/ui/issues/issue-69225-SCEVAddExpr-wrap-flag.rs index 6e030f1cc4875..e04dec24e445e 100644 --- a/src/test/ui/issues/issue-69225-SCEVAddExpr-wrap-flag.rs +++ b/src/test/ui/issues/issue-69225-SCEVAddExpr-wrap-flag.rs @@ -1,5 +1,6 @@ // run-fail // compile-flags: -C opt-level=3 +// min-llvm-version: 11.0 // error-pattern: index out of bounds: the len is 0 but the index is 16777216 // ignore-wasm no panic or subprocess support // ignore-emscripten no panic or subprocess support diff --git a/src/test/ui/issues/issue-69725.stderr b/src/test/ui/issues/issue-69725.stderr index 48c71d76af097..4dd6b4bbb68be 100644 --- a/src/test/ui/issues/issue-69725.stderr +++ b/src/test/ui/issues/issue-69725.stderr @@ -8,14 +8,6 @@ LL | let _ = Struct::<A>::new().clone(); | LL | pub struct Struct<A>(A); | ------------------------ doesn't satisfy `Struct<A>: Clone` - | - ::: $SRC_DIR/core/src/clone.rs:LL:COL - | -LL | fn clone(&self) -> Self; - | ----- - | | - | the method is available for `Arc<Struct<A>>` here - | the method is available for `Rc<Struct<A>>` here | = note: the following trait bounds were not satisfied: `A: Clone` diff --git a/src/test/ui/issues/issue-7013.stderr b/src/test/ui/issues/issue-7013.stderr index 5f3156a540271..98ed67507b1d8 100644 --- a/src/test/ui/issues/issue-7013.stderr +++ b/src/test/ui/issues/issue-7013.stderr @@ -6,7 +6,11 @@ LL | let a = A {v: box B{v: None} as Box<dyn Foo + Send>}; | = help: within `B`, the trait `Send` is not implemented for `Rc<RefCell<A>>` = note: required because it appears within the type `Option<Rc<RefCell<A>>>` - = note: required because it appears within the type `B` +note: required because it appears within the type `B` + --> $DIR/issue-7013.rs:10:8 + | +LL | struct B { + | ^ = note: required for the cast to the object type `dyn Foo + Send` error: aborting due to previous error diff --git a/src/test/ui/issues/issue-77002.rs b/src/test/ui/issues/issue-77002.rs index c7dd3cf810938..0c37346eaf803 100644 --- a/src/test/ui/issues/issue-77002.rs +++ b/src/test/ui/issues/issue-77002.rs @@ -1,4 +1,4 @@ -// compile-flags: -Zmir-opt-level=2 -Copt-level=0 +// compile-flags: -Zmir-opt-level=3 -Copt-level=0 // run-pass type M = [i64; 2]; diff --git a/src/test/ui/issues/issue-78622.stderr b/src/test/ui/issues/issue-78622.stderr index f13073da0a36e..f7d44f21d3bec 100644 --- a/src/test/ui/issues/issue-78622.stderr +++ b/src/test/ui/issues/issue-78622.stderr @@ -2,7 +2,7 @@ error[E0223]: ambiguous associated type --> $DIR/issue-78622.rs:5:5 | LL | S::A::<f> {} - | ^^^^^^^^^ help: use fully-qualified syntax: `<S as Trait>::A` + | ^^^^ help: use fully-qualified syntax: `<S as Trait>::A` error: aborting due to previous error diff --git a/src/test/ui/issues/issue-78720.rs b/src/test/ui/issues/issue-78720.rs index 57615d1a207fd..4cdb9f491130c 100644 --- a/src/test/ui/issues/issue-78720.rs +++ b/src/test/ui/issues/issue-78720.rs @@ -4,7 +4,7 @@ fn server() -> impl { } trait FilterBase2 { - fn map2<F>(self, F) -> Map2<F> {} + fn map2<F>(self, f: F) -> Map2<F> {} //~^ ERROR mismatched types //~^^ ERROR the size for values of type `Self` cannot be known at compilation time } diff --git a/src/test/ui/issues/issue-78720.stderr b/src/test/ui/issues/issue-78720.stderr index a3a14e34acbc1..ee15826d18941 100644 --- a/src/test/ui/issues/issue-78720.stderr +++ b/src/test/ui/issues/issue-78720.stderr @@ -25,10 +25,10 @@ LL | struct Map2<Segment2, F> { | ^^^ error[E0308]: mismatched types - --> $DIR/issue-78720.rs:7:36 + --> $DIR/issue-78720.rs:7:39 | -LL | fn map2<F>(self, F) -> Map2<F> {} - | ^^ expected struct `Map2`, found `()` +LL | fn map2<F>(self, f: F) -> Map2<F> {} + | ^^ expected struct `Map2`, found `()` | = note: expected struct `Map2<F>` found unit type `()` @@ -36,17 +36,17 @@ LL | fn map2<F>(self, F) -> Map2<F> {} error[E0277]: the size for values of type `Self` cannot be known at compilation time --> $DIR/issue-78720.rs:7:16 | -LL | fn map2<F>(self, F) -> Map2<F> {} +LL | fn map2<F>(self, f: F) -> Map2<F> {} | ^^^^ doesn't have a size known at compile-time | = help: unsized fn params are gated as an unstable feature help: consider further restricting `Self` | -LL | fn map2<F>(self, F) -> Map2<F> where Self: Sized {} - | ^^^^^^^^^^^^^^^^^ +LL | fn map2<F>(self, f: F) -> Map2<F> where Self: Sized {} + | ^^^^^^^^^^^^^^^^^ help: function arguments must have a statically known size, borrowed types always have a known size | -LL | fn map2<F>(&self, F) -> Map2<F> {} +LL | fn map2<F>(&self, f: F) -> Map2<F> {} | ^ error: aborting due to 4 previous errors diff --git a/src/test/ui/issues/issue-81584.fixed b/src/test/ui/issues/issue-81584.fixed new file mode 100644 index 0000000000000..1cad59f1062c6 --- /dev/null +++ b/src/test/ui/issues/issue-81584.fixed @@ -0,0 +1,8 @@ +// run-rustfix +fn main() { + let _ = vec![vec![0, 1], vec![2]] + .into_iter() + .map(|y| y.iter().map(|x| x + 1).collect::<Vec<_>>()) + //~^ ERROR cannot return value referencing function parameter `y` + .collect::<Vec<_>>(); +} diff --git a/src/test/ui/issues/issue-81584.rs b/src/test/ui/issues/issue-81584.rs new file mode 100644 index 0000000000000..452288db08bd8 --- /dev/null +++ b/src/test/ui/issues/issue-81584.rs @@ -0,0 +1,8 @@ +// run-rustfix +fn main() { + let _ = vec![vec![0, 1], vec![2]] + .into_iter() + .map(|y| y.iter().map(|x| x + 1)) + //~^ ERROR cannot return value referencing function parameter `y` + .collect::<Vec<_>>(); +} diff --git a/src/test/ui/issues/issue-81584.stderr b/src/test/ui/issues/issue-81584.stderr new file mode 100644 index 0000000000000..d57f1b778df17 --- /dev/null +++ b/src/test/ui/issues/issue-81584.stderr @@ -0,0 +1,14 @@ +error[E0515]: cannot return value referencing function parameter `y` + --> $DIR/issue-81584.rs:5:22 + | +LL | .map(|y| y.iter().map(|x| x + 1)) + | -^^^^^^^^^^^^^^^^^^^^^^ + | | + | returns a value referencing data owned by the current function + | `y` is borrowed here + | + = help: use `.collect()` to allocate the iterator + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0515`. diff --git a/src/test/ui/issues/issue-82833-slice-miscompile.rs b/src/test/ui/issues/issue-82833-slice-miscompile.rs new file mode 100644 index 0000000000000..8cf6a3137e2d6 --- /dev/null +++ b/src/test/ui/issues/issue-82833-slice-miscompile.rs @@ -0,0 +1,16 @@ +// run-pass +// compile-flags: -Ccodegen-units=1 -Cinline-threshold=0 -Clink-dead-code -Copt-level=0 -Cdebuginfo=2 + +// Make sure LLVM does not miscompile this. + +fn make_string(ch: char) -> String { + let mut bytes = [0u8; 4]; + ch.encode_utf8(&mut bytes).into() +} + +fn main() { + let ch = '😃'; + dbg!(ch); + let string = make_string(ch); + dbg!(string); +} diff --git a/src/test/ui/issues/issue-82859-slice-miscompile.rs b/src/test/ui/issues/issue-82859-slice-miscompile.rs new file mode 100644 index 0000000000000..b64eb49907139 --- /dev/null +++ b/src/test/ui/issues/issue-82859-slice-miscompile.rs @@ -0,0 +1,19 @@ +// run-pass +// compile-flags: -Copt-level=0 -Cdebuginfo=2 + +// Make sure LLVM does not miscompile this. + +fn indirect_get_slice() -> &'static [usize] { + &[] +} + +#[inline(always)] +fn get_slice() -> &'static [usize] { + let ret = indirect_get_slice(); + ret +} + +fn main() { + let output = get_slice().len(); + assert_eq!(output, 0); +} diff --git a/src/test/ui/issues/issue-82869.rs b/src/test/ui/issues/issue-82869.rs new file mode 100644 index 0000000000000..a8e688cbe1ff3 --- /dev/null +++ b/src/test/ui/issues/issue-82869.rs @@ -0,0 +1,23 @@ +// only-x86_64 +// Make sure rustc doesn't ICE on asm! for a foreign architecture. + +#![feature(asm)] +#![crate_type = "rlib"] + +pub unsafe fn aarch64(a: f64, b: f64) -> f64 { + let c; + asm!("add {:d}, {:d}, d0", out(vreg) c, in(vreg) a, in("d0") { + || {}; + b + }); + //~^^^^ invalid register class + //~^^^^^ invalid register class + //~^^^^^^ invalid register + c +} + +pub unsafe fn x86(a: f64, b: f64) -> f64 { + let c; + asm!("addsd {}, {}, xmm0", out(xmm_reg) c, in(xmm_reg) a, in("xmm0") b); + c +} diff --git a/src/test/ui/issues/issue-82869.stderr b/src/test/ui/issues/issue-82869.stderr new file mode 100644 index 0000000000000..d05714ea6f215 --- /dev/null +++ b/src/test/ui/issues/issue-82869.stderr @@ -0,0 +1,24 @@ +error: invalid register class `vreg`: unknown register class + --> $DIR/issue-82869.rs:9:32 + | +LL | asm!("add {:d}, {:d}, d0", out(vreg) c, in(vreg) a, in("d0") { + | ^^^^^^^^^^^ + +error: invalid register class `vreg`: unknown register class + --> $DIR/issue-82869.rs:9:45 + | +LL | asm!("add {:d}, {:d}, d0", out(vreg) c, in(vreg) a, in("d0") { + | ^^^^^^^^^^ + +error: invalid register `d0`: unknown register + --> $DIR/issue-82869.rs:9:57 + | +LL | asm!("add {:d}, {:d}, d0", out(vreg) c, in(vreg) a, in("d0") { + | _________________________________________________________^ +LL | | || {}; +LL | | b +LL | | }); + | |_____^ + +error: aborting due to 3 previous errors + diff --git a/src/test/ui/issues/issue-83048.rs b/src/test/ui/issues/issue-83048.rs new file mode 100644 index 0000000000000..520ae974398b4 --- /dev/null +++ b/src/test/ui/issues/issue-83048.rs @@ -0,0 +1,5 @@ +// compile-flags: -Z unpretty=thir-tree + +pub fn main() { + break; //~ ERROR: `break` outside of a loop [E0268] +} diff --git a/src/test/ui/issues/issue-83048.stderr b/src/test/ui/issues/issue-83048.stderr new file mode 100644 index 0000000000000..62d67d75844a4 --- /dev/null +++ b/src/test/ui/issues/issue-83048.stderr @@ -0,0 +1,9 @@ +error[E0268]: `break` outside of a loop + --> $DIR/issue-83048.rs:4:5 + | +LL | break; + | ^^^^^ cannot `break` outside of a loop + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0268`. diff --git a/src/test/ui/issues/issue-83924.fixed b/src/test/ui/issues/issue-83924.fixed new file mode 100644 index 0000000000000..aa40da12b875d --- /dev/null +++ b/src/test/ui/issues/issue-83924.fixed @@ -0,0 +1,20 @@ +// run-rustfix + +fn main() { + let mut values = vec![10, 11, 12]; + let v = &mut values; + + let mut max = 0; + + for n in &mut *v { + max = std::cmp::max(max, *n); + } + + println!("max is {}", max); + println!("Converting to percentages of maximum value..."); + for n in v { + //~^ ERROR: use of moved value: `v` [E0382] + *n = 100 * (*n) / max; + } + println!("values: {:#?}", values); +} diff --git a/src/test/ui/issues/issue-83924.rs b/src/test/ui/issues/issue-83924.rs new file mode 100644 index 0000000000000..22b80fe2f383f --- /dev/null +++ b/src/test/ui/issues/issue-83924.rs @@ -0,0 +1,20 @@ +// run-rustfix + +fn main() { + let mut values = vec![10, 11, 12]; + let v = &mut values; + + let mut max = 0; + + for n in v { + max = std::cmp::max(max, *n); + } + + println!("max is {}", max); + println!("Converting to percentages of maximum value..."); + for n in v { + //~^ ERROR: use of moved value: `v` [E0382] + *n = 100 * (*n) / max; + } + println!("values: {:#?}", values); +} diff --git a/src/test/ui/issues/issue-83924.stderr b/src/test/ui/issues/issue-83924.stderr new file mode 100644 index 0000000000000..682bc323cb051 --- /dev/null +++ b/src/test/ui/issues/issue-83924.stderr @@ -0,0 +1,25 @@ +error[E0382]: use of moved value: `v` + --> $DIR/issue-83924.rs:15:14 + | +LL | let v = &mut values; + | - move occurs because `v` has type `&mut Vec<i32>`, which does not implement the `Copy` trait +... +LL | for n in v { + | - `v` moved due to this implicit call to `.into_iter()` +... +LL | for n in v { + | ^ value used here after move + | +note: this function takes ownership of the receiver `self`, which moves `v` + --> $SRC_DIR/core/src/iter/traits/collect.rs:LL:COL + | +LL | fn into_iter(self) -> Self::IntoIter; + | ^^^^ +help: consider creating a fresh reborrow of `v` here + | +LL | for n in &mut *v { + | ^^^^^^ + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0382`. diff --git a/src/test/ui/iterators/array-of-ranges.stderr b/src/test/ui/iterators/array-of-ranges.stderr index 601983a6153fd..7d58eb948ea81 100644 --- a/src/test/ui/iterators/array-of-ranges.stderr +++ b/src/test/ui/iterators/array-of-ranges.stderr @@ -13,10 +13,10 @@ error[E0277]: `[RangeInclusive<{integer}>; 1]` is not an iterator --> $DIR/array-of-ranges.rs:4:14 | LL | for _ in [0..=1] {} - | ^^^^^^^ borrow the array with `&` or call `.iter()` on it to iterate over it + | ^^^^^^^ if you meant to iterate between two values, remove the square brackets | = help: the trait `Iterator` is not implemented for `[RangeInclusive<{integer}>; 1]` - = note: arrays are not iterators, but slices like the following are: `&[1, 2, 3]` + = note: `[start..=end]` is an array of one `RangeInclusive`; you might have meant to have a `RangeInclusive` without the brackets: `start..=end` = note: required because of the requirements on the impl of `IntoIterator` for `[RangeInclusive<{integer}>; 1]` = note: required by `into_iter` @@ -24,10 +24,10 @@ error[E0277]: `[RangeFrom<{integer}>; 1]` is not an iterator --> $DIR/array-of-ranges.rs:6:14 | LL | for _ in [0..] {} - | ^^^^^ borrow the array with `&` or call `.iter()` on it to iterate over it + | ^^^^^ if you meant to iterate from a value onwards, remove the square brackets | = help: the trait `Iterator` is not implemented for `[RangeFrom<{integer}>; 1]` - = note: arrays are not iterators, but slices like the following are: `&[1, 2, 3]` + = note: `[start..]` is an array of one `RangeFrom`; you might have meant to have a `RangeFrom` without the brackets: `start..`, keeping in mind that iterating over an unbounded iterator will run forever unless you `break` or `return` from within the loop = note: required because of the requirements on the impl of `IntoIterator` for `[RangeFrom<{integer}>; 1]` = note: required by `into_iter` @@ -35,10 +35,10 @@ error[E0277]: `[RangeTo<{integer}>; 1]` is not an iterator --> $DIR/array-of-ranges.rs:8:14 | LL | for _ in [..1] {} - | ^^^^^ borrow the array with `&` or call `.iter()` on it to iterate over it + | ^^^^^ if you meant to iterate until a value, remove the square brackets and add a starting value | = help: the trait `Iterator` is not implemented for `[RangeTo<{integer}>; 1]` - = note: arrays are not iterators, but slices like the following are: `&[1, 2, 3]` + = note: `[..end]` is an array of one `RangeTo`; you might have meant to have a bounded `Range` without the brackets: `0..end` = note: required because of the requirements on the impl of `IntoIterator` for `[RangeTo<{integer}>; 1]` = note: required by `into_iter` @@ -46,10 +46,10 @@ error[E0277]: `[RangeToInclusive<{integer}>; 1]` is not an iterator --> $DIR/array-of-ranges.rs:10:14 | LL | for _ in [..=1] {} - | ^^^^^^ borrow the array with `&` or call `.iter()` on it to iterate over it + | ^^^^^^ if you meant to iterate until a value (including it), remove the square brackets and add a starting value | = help: the trait `Iterator` is not implemented for `[RangeToInclusive<{integer}>; 1]` - = note: arrays are not iterators, but slices like the following are: `&[1, 2, 3]` + = note: `[..=end]` is an array of one `RangeToInclusive`; you might have meant to have a bounded `RangeInclusive` without the brackets: `0..=end` = note: required because of the requirements on the impl of `IntoIterator` for `[RangeToInclusive<{integer}>; 1]` = note: required by `into_iter` @@ -79,10 +79,10 @@ error[E0277]: `[std::ops::Range<{integer}>; 2]` is not an iterator --> $DIR/array-of-ranges.rs:19:14 | LL | for _ in [0..1, 2..3] {} - | ^^^^^^^^^^^^ borrow the array with `&` or call `.iter()` on it to iterate over it + | ^^^^^^^^^^^^ arrays do not yet implement `IntoIterator`; try using `std::array::IntoIter::new(arr)` | = help: the trait `Iterator` is not implemented for `[std::ops::Range<{integer}>; 2]` - = note: arrays are not iterators, but slices like the following are: `&[1, 2, 3]` + = note: see <https://github.com/rust-lang/rust/pull/65819> for more details = note: required because of the requirements on the impl of `IntoIterator` for `[std::ops::Range<{integer}>; 2]` = note: required by `into_iter` @@ -90,10 +90,10 @@ error[E0277]: `[RangeInclusive<{integer}>; 1]` is not an iterator --> $DIR/array-of-ranges.rs:21:14 | LL | for _ in [0..=1] {} - | ^^^^^^^ borrow the array with `&` or call `.iter()` on it to iterate over it + | ^^^^^^^ if you meant to iterate between two values, remove the square brackets | = help: the trait `Iterator` is not implemented for `[RangeInclusive<{integer}>; 1]` - = note: arrays are not iterators, but slices like the following are: `&[1, 2, 3]` + = note: `[start..=end]` is an array of one `RangeInclusive`; you might have meant to have a `RangeInclusive` without the brackets: `start..=end` = note: required because of the requirements on the impl of `IntoIterator` for `[RangeInclusive<{integer}>; 1]` = note: required by `into_iter` diff --git a/src/test/ui/iterators/array.stderr b/src/test/ui/iterators/array.stderr index 68c6de5493f13..7e2b600fb7af2 100644 --- a/src/test/ui/iterators/array.stderr +++ b/src/test/ui/iterators/array.stderr @@ -2,10 +2,10 @@ error[E0277]: `[{integer}; 2]` is not an iterator --> $DIR/array.rs:2:14 | LL | for _ in [1, 2] {} - | ^^^^^^ borrow the array with `&` or call `.iter()` on it to iterate over it + | ^^^^^^ arrays do not yet implement `IntoIterator`; try using `std::array::IntoIter::new(arr)` | = help: the trait `Iterator` is not implemented for `[{integer}; 2]` - = note: arrays are not iterators, but slices like the following are: `&[1, 2, 3]` + = note: see <https://github.com/rust-lang/rust/pull/65819> for more details = note: required because of the requirements on the impl of `IntoIterator` for `[{integer}; 2]` = note: required by `into_iter` @@ -13,10 +13,10 @@ error[E0277]: `[{integer}; 2]` is not an iterator --> $DIR/array.rs:5:14 | LL | for _ in x {} - | ^ borrow the array with `&` or call `.iter()` on it to iterate over it + | ^ arrays do not yet implement `IntoIterator`; try using `std::array::IntoIter::new(arr)` | = help: the trait `Iterator` is not implemented for `[{integer}; 2]` - = note: arrays are not iterators, but slices like the following are: `&[1, 2, 3]` + = note: see <https://github.com/rust-lang/rust/pull/65819> for more details = note: required because of the requirements on the impl of `IntoIterator` for `[{integer}; 2]` = note: required by `into_iter` @@ -24,10 +24,10 @@ error[E0277]: `[{float}; 2]` is not an iterator --> $DIR/array.rs:7:14 | LL | for _ in [1.0, 2.0] {} - | ^^^^^^^^^^ borrow the array with `&` or call `.iter()` on it to iterate over it + | ^^^^^^^^^^ arrays do not yet implement `IntoIterator`; try using `std::array::IntoIter::new(arr)` | = help: the trait `Iterator` is not implemented for `[{float}; 2]` - = note: arrays are not iterators, but slices like the following are: `&[1, 2, 3]` + = note: see <https://github.com/rust-lang/rust/pull/65819> for more details = note: required because of the requirements on the impl of `IntoIterator` for `[{float}; 2]` = note: required by `into_iter` diff --git a/src/test/ui/iterators/ranges.stderr b/src/test/ui/iterators/ranges.stderr index 4678bafd196b5..73844329e361d 100644 --- a/src/test/ui/iterators/ranges.stderr +++ b/src/test/ui/iterators/ranges.stderr @@ -2,9 +2,10 @@ error[E0277]: `RangeTo<{integer}>` is not an iterator --> $DIR/ranges.rs:2:14 | LL | for _ in ..10 {} - | ^^^^ `RangeTo<{integer}>` is not an iterator + | ^^^^ if you meant to iterate until a value, add a starting value | = help: the trait `Iterator` is not implemented for `RangeTo<{integer}>` + = note: `..end` is a `RangeTo`, which cannot be iterated on; you might have meant to have a bounded `Range`: `0..end` = note: required because of the requirements on the impl of `IntoIterator` for `RangeTo<{integer}>` = note: required by `into_iter` @@ -12,9 +13,10 @@ error[E0277]: `RangeToInclusive<{integer}>` is not an iterator --> $DIR/ranges.rs:4:14 | LL | for _ in ..=10 {} - | ^^^^^ `RangeToInclusive<{integer}>` is not an iterator + | ^^^^^ if you meant to iterate until a value (including it), add a starting value | = help: the trait `Iterator` is not implemented for `RangeToInclusive<{integer}>` + = note: `..=end` is a `RangeToInclusive`, which cannot be iterated on; you might have meant to have a bounded `RangeInclusive`: `0..=end` = note: required because of the requirements on the impl of `IntoIterator` for `RangeToInclusive<{integer}>` = note: required by `into_iter` diff --git a/src/test/ui/iterators/string.stderr b/src/test/ui/iterators/string.stderr index 1653006682427..1d77bcb753630 100644 --- a/src/test/ui/iterators/string.stderr +++ b/src/test/ui/iterators/string.stderr @@ -2,7 +2,7 @@ error[E0277]: `String` is not an iterator --> $DIR/string.rs:2:14 | LL | for _ in "".to_owned() {} - | ^^^^^^^^^^^^^ `String` is not an iterator + | ^^^^^^^^^^^^^ `String` is not an iterator; try calling `.chars()` or `.bytes()` | = help: the trait `Iterator` is not implemented for `String` = note: required because of the requirements on the impl of `IntoIterator` for `String` diff --git a/src/test/ui/kindck/kindck-impl-type-params-2.stderr b/src/test/ui/kindck/kindck-impl-type-params-2.stderr index 7e0f6e0b2de91..c635ebdbb7f46 100644 --- a/src/test/ui/kindck/kindck-impl-type-params-2.stderr +++ b/src/test/ui/kindck/kindck-impl-type-params-2.stderr @@ -7,7 +7,11 @@ LL | fn take_param<T:Foo>(foo: &T) { } LL | take_param(&x); | ^^ the trait `Copy` is not implemented for `Box<{integer}>` | - = note: required because of the requirements on the impl of `Foo` for `Box<{integer}>` +note: required because of the requirements on the impl of `Foo` for `Box<{integer}>` + --> $DIR/kindck-impl-type-params-2.rs:6:14 + | +LL | impl<T:Copy> Foo for T { + | ^^^ ^ error: aborting due to previous error diff --git a/src/test/ui/kindck/kindck-impl-type-params.nll.stderr b/src/test/ui/kindck/kindck-impl-type-params.nll.stderr index b01b8258e735f..035501009bda4 100644 --- a/src/test/ui/kindck/kindck-impl-type-params.nll.stderr +++ b/src/test/ui/kindck/kindck-impl-type-params.nll.stderr @@ -4,12 +4,16 @@ error[E0277]: `T` cannot be sent between threads safely LL | let a = &t as &dyn Gettable<T>; | ^^ `T` cannot be sent between threads safely | - = note: required because of the requirements on the impl of `Gettable<T>` for `S<T>` +note: required because of the requirements on the impl of `Gettable<T>` for `S<T>` + --> $DIR/kindck-impl-type-params.rs:14:32 + | +LL | impl<T: Send + Copy + 'static> Gettable<T> for S<T> {} + | ^^^^^^^^^^^ ^^^^ = note: required for the cast to the object type `dyn Gettable<T>` help: consider restricting type parameter `T` | -LL | fn f<T: Send>(val: T) { - | ^^^^^^ +LL | fn f<T: std::marker::Send>(val: T) { + | ^^^^^^^^^^^^^^^^^^^ error[E0277]: the trait bound `T: Copy` is not satisfied --> $DIR/kindck-impl-type-params.rs:18:13 @@ -17,12 +21,16 @@ error[E0277]: the trait bound `T: Copy` is not satisfied LL | let a = &t as &dyn Gettable<T>; | ^^ the trait `Copy` is not implemented for `T` | - = note: required because of the requirements on the impl of `Gettable<T>` for `S<T>` +note: required because of the requirements on the impl of `Gettable<T>` for `S<T>` + --> $DIR/kindck-impl-type-params.rs:14:32 + | +LL | impl<T: Send + Copy + 'static> Gettable<T> for S<T> {} + | ^^^^^^^^^^^ ^^^^ = note: required for the cast to the object type `dyn Gettable<T>` help: consider restricting type parameter `T` | -LL | fn f<T: Copy>(val: T) { - | ^^^^^^ +LL | fn f<T: std::marker::Copy>(val: T) { + | ^^^^^^^^^^^^^^^^^^^ error[E0277]: `T` cannot be sent between threads safely --> $DIR/kindck-impl-type-params.rs:25:31 @@ -30,12 +38,16 @@ error[E0277]: `T` cannot be sent between threads safely LL | let a: &dyn Gettable<T> = &t; | ^^ `T` cannot be sent between threads safely | - = note: required because of the requirements on the impl of `Gettable<T>` for `S<T>` +note: required because of the requirements on the impl of `Gettable<T>` for `S<T>` + --> $DIR/kindck-impl-type-params.rs:14:32 + | +LL | impl<T: Send + Copy + 'static> Gettable<T> for S<T> {} + | ^^^^^^^^^^^ ^^^^ = note: required for the cast to the object type `dyn Gettable<T>` help: consider restricting type parameter `T` | -LL | fn g<T: Send>(val: T) { - | ^^^^^^ +LL | fn g<T: std::marker::Send>(val: T) { + | ^^^^^^^^^^^^^^^^^^^ error[E0277]: the trait bound `T: Copy` is not satisfied --> $DIR/kindck-impl-type-params.rs:25:31 @@ -43,12 +55,16 @@ error[E0277]: the trait bound `T: Copy` is not satisfied LL | let a: &dyn Gettable<T> = &t; | ^^ the trait `Copy` is not implemented for `T` | - = note: required because of the requirements on the impl of `Gettable<T>` for `S<T>` +note: required because of the requirements on the impl of `Gettable<T>` for `S<T>` + --> $DIR/kindck-impl-type-params.rs:14:32 + | +LL | impl<T: Send + Copy + 'static> Gettable<T> for S<T> {} + | ^^^^^^^^^^^ ^^^^ = note: required for the cast to the object type `dyn Gettable<T>` help: consider restricting type parameter `T` | -LL | fn g<T: Copy>(val: T) { - | ^^^^^^ +LL | fn g<T: std::marker::Copy>(val: T) { + | ^^^^^^^^^^^^^^^^^^^ error[E0277]: the trait bound `String: Copy` is not satisfied --> $DIR/kindck-impl-type-params.rs:38:13 @@ -56,7 +72,11 @@ error[E0277]: the trait bound `String: Copy` is not satisfied LL | let a = t as Box<dyn Gettable<String>>; | ^ the trait `Copy` is not implemented for `String` | - = note: required because of the requirements on the impl of `Gettable<String>` for `S<String>` +note: required because of the requirements on the impl of `Gettable<String>` for `S<String>` + --> $DIR/kindck-impl-type-params.rs:14:32 + | +LL | impl<T: Send + Copy + 'static> Gettable<T> for S<T> {} + | ^^^^^^^^^^^ ^^^^ = note: required for the cast to the object type `dyn Gettable<String>` error[E0277]: the trait bound `Foo: Copy` is not satisfied @@ -65,7 +85,11 @@ error[E0277]: the trait bound `Foo: Copy` is not satisfied LL | let a: Box<dyn Gettable<Foo>> = t; | ^ the trait `Copy` is not implemented for `Foo` | - = note: required because of the requirements on the impl of `Gettable<Foo>` for `S<Foo>` +note: required because of the requirements on the impl of `Gettable<Foo>` for `S<Foo>` + --> $DIR/kindck-impl-type-params.rs:14:32 + | +LL | impl<T: Send + Copy + 'static> Gettable<T> for S<T> {} + | ^^^^^^^^^^^ ^^^^ = note: required for the cast to the object type `dyn Gettable<Foo>` error: aborting due to 6 previous errors diff --git a/src/test/ui/kindck/kindck-impl-type-params.stderr b/src/test/ui/kindck/kindck-impl-type-params.stderr index ddf8adf3637b3..241fe367fd339 100644 --- a/src/test/ui/kindck/kindck-impl-type-params.stderr +++ b/src/test/ui/kindck/kindck-impl-type-params.stderr @@ -4,12 +4,16 @@ error[E0277]: `T` cannot be sent between threads safely LL | let a = &t as &dyn Gettable<T>; | ^^ `T` cannot be sent between threads safely | - = note: required because of the requirements on the impl of `Gettable<T>` for `S<T>` +note: required because of the requirements on the impl of `Gettable<T>` for `S<T>` + --> $DIR/kindck-impl-type-params.rs:14:32 + | +LL | impl<T: Send + Copy + 'static> Gettable<T> for S<T> {} + | ^^^^^^^^^^^ ^^^^ = note: required for the cast to the object type `dyn Gettable<T>` help: consider restricting type parameter `T` | -LL | fn f<T: Send>(val: T) { - | ^^^^^^ +LL | fn f<T: std::marker::Send>(val: T) { + | ^^^^^^^^^^^^^^^^^^^ error[E0277]: the trait bound `T: Copy` is not satisfied --> $DIR/kindck-impl-type-params.rs:18:13 @@ -17,12 +21,16 @@ error[E0277]: the trait bound `T: Copy` is not satisfied LL | let a = &t as &dyn Gettable<T>; | ^^ the trait `Copy` is not implemented for `T` | - = note: required because of the requirements on the impl of `Gettable<T>` for `S<T>` +note: required because of the requirements on the impl of `Gettable<T>` for `S<T>` + --> $DIR/kindck-impl-type-params.rs:14:32 + | +LL | impl<T: Send + Copy + 'static> Gettable<T> for S<T> {} + | ^^^^^^^^^^^ ^^^^ = note: required for the cast to the object type `dyn Gettable<T>` help: consider restricting type parameter `T` | -LL | fn f<T: Copy>(val: T) { - | ^^^^^^ +LL | fn f<T: std::marker::Copy>(val: T) { + | ^^^^^^^^^^^^^^^^^^^ error[E0277]: `T` cannot be sent between threads safely --> $DIR/kindck-impl-type-params.rs:25:31 @@ -30,12 +38,16 @@ error[E0277]: `T` cannot be sent between threads safely LL | let a: &dyn Gettable<T> = &t; | ^^ `T` cannot be sent between threads safely | - = note: required because of the requirements on the impl of `Gettable<T>` for `S<T>` +note: required because of the requirements on the impl of `Gettable<T>` for `S<T>` + --> $DIR/kindck-impl-type-params.rs:14:32 + | +LL | impl<T: Send + Copy + 'static> Gettable<T> for S<T> {} + | ^^^^^^^^^^^ ^^^^ = note: required for the cast to the object type `dyn Gettable<T>` help: consider restricting type parameter `T` | -LL | fn g<T: Send>(val: T) { - | ^^^^^^ +LL | fn g<T: std::marker::Send>(val: T) { + | ^^^^^^^^^^^^^^^^^^^ error[E0277]: the trait bound `T: Copy` is not satisfied --> $DIR/kindck-impl-type-params.rs:25:31 @@ -43,12 +55,16 @@ error[E0277]: the trait bound `T: Copy` is not satisfied LL | let a: &dyn Gettable<T> = &t; | ^^ the trait `Copy` is not implemented for `T` | - = note: required because of the requirements on the impl of `Gettable<T>` for `S<T>` +note: required because of the requirements on the impl of `Gettable<T>` for `S<T>` + --> $DIR/kindck-impl-type-params.rs:14:32 + | +LL | impl<T: Send + Copy + 'static> Gettable<T> for S<T> {} + | ^^^^^^^^^^^ ^^^^ = note: required for the cast to the object type `dyn Gettable<T>` help: consider restricting type parameter `T` | -LL | fn g<T: Copy>(val: T) { - | ^^^^^^ +LL | fn g<T: std::marker::Copy>(val: T) { + | ^^^^^^^^^^^^^^^^^^^ error[E0477]: the type `&'a isize` does not fulfill the required lifetime --> $DIR/kindck-impl-type-params.rs:32:13 @@ -64,7 +80,11 @@ error[E0277]: the trait bound `String: Copy` is not satisfied LL | let a = t as Box<dyn Gettable<String>>; | ^ the trait `Copy` is not implemented for `String` | - = note: required because of the requirements on the impl of `Gettable<String>` for `S<String>` +note: required because of the requirements on the impl of `Gettable<String>` for `S<String>` + --> $DIR/kindck-impl-type-params.rs:14:32 + | +LL | impl<T: Send + Copy + 'static> Gettable<T> for S<T> {} + | ^^^^^^^^^^^ ^^^^ = note: required for the cast to the object type `dyn Gettable<String>` error[E0277]: the trait bound `Foo: Copy` is not satisfied @@ -73,7 +93,11 @@ error[E0277]: the trait bound `Foo: Copy` is not satisfied LL | let a: Box<dyn Gettable<Foo>> = t; | ^ the trait `Copy` is not implemented for `Foo` | - = note: required because of the requirements on the impl of `Gettable<Foo>` for `S<Foo>` +note: required because of the requirements on the impl of `Gettable<Foo>` for `S<Foo>` + --> $DIR/kindck-impl-type-params.rs:14:32 + | +LL | impl<T: Send + Copy + 'static> Gettable<T> for S<T> {} + | ^^^^^^^^^^^ ^^^^ = note: required for the cast to the object type `dyn Gettable<Foo>` error: aborting due to 7 previous errors diff --git a/src/test/ui/kindck/kindck-inherited-copy-bound.curr.stderr b/src/test/ui/kindck/kindck-inherited-copy-bound.curr.stderr index 64e56f8c79043..86eaca83f2040 100644 --- a/src/test/ui/kindck/kindck-inherited-copy-bound.curr.stderr +++ b/src/test/ui/kindck/kindck-inherited-copy-bound.curr.stderr @@ -7,7 +7,11 @@ LL | fn take_param<T:Foo>(foo: &T) { } LL | take_param(&x); | ^^ the trait `Copy` is not implemented for `Box<{integer}>` | - = note: required because of the requirements on the impl of `Foo` for `Box<{integer}>` +note: required because of the requirements on the impl of `Foo` for `Box<{integer}>` + --> $DIR/kindck-inherited-copy-bound.rs:14:14 + | +LL | impl<T:Copy> Foo for T { + | ^^^ ^ error[E0038]: the trait `Foo` cannot be made into an object --> $DIR/kindck-inherited-copy-bound.rs:28:19 diff --git a/src/test/ui/kindck/kindck-inherited-copy-bound.object_safe_for_dispatch.stderr b/src/test/ui/kindck/kindck-inherited-copy-bound.object_safe_for_dispatch.stderr index 57f7551fd4018..49c5cd40b589a 100644 --- a/src/test/ui/kindck/kindck-inherited-copy-bound.object_safe_for_dispatch.stderr +++ b/src/test/ui/kindck/kindck-inherited-copy-bound.object_safe_for_dispatch.stderr @@ -7,7 +7,11 @@ LL | fn take_param<T:Foo>(foo: &T) { } LL | take_param(&x); | ^^ the trait `Copy` is not implemented for `Box<{integer}>` | - = note: required because of the requirements on the impl of `Foo` for `Box<{integer}>` +note: required because of the requirements on the impl of `Foo` for `Box<{integer}>` + --> $DIR/kindck-inherited-copy-bound.rs:14:14 + | +LL | impl<T:Copy> Foo for T { + | ^^^ ^ error[E0038]: the trait `Foo` cannot be made into an object --> $DIR/kindck-inherited-copy-bound.rs:28:13 diff --git a/src/test/ui/kinds-of-primitive-impl.rs b/src/test/ui/kinds-of-primitive-impl.rs index cbd4d7ae904fd..b045b050d77a4 100644 --- a/src/test/ui/kinds-of-primitive-impl.rs +++ b/src/test/ui/kinds-of-primitive-impl.rs @@ -1,6 +1,3 @@ -// ignore-tidy-linelength - - impl u8 { //~^ error: only a single inherent implementation marked with `#[lang = "u8"]` is allowed for the `u8` primitive pub const B: u8 = 0; diff --git a/src/test/ui/kinds-of-primitive-impl.stderr b/src/test/ui/kinds-of-primitive-impl.stderr index d19c85b17f9e7..f1fb295308352 100644 --- a/src/test/ui/kinds-of-primitive-impl.stderr +++ b/src/test/ui/kinds-of-primitive-impl.stderr @@ -1,5 +1,5 @@ error[E0390]: only a single inherent implementation marked with `#[lang = "u8"]` is allowed for the `u8` primitive - --> $DIR/kinds-of-primitive-impl.rs:4:1 + --> $DIR/kinds-of-primitive-impl.rs:1:1 | LL | / impl u8 { LL | | @@ -10,7 +10,7 @@ LL | | } = help: consider using a trait to implement this constant error[E0390]: only a single inherent implementation marked with `#[lang = "str"]` is allowed for the `str` primitive - --> $DIR/kinds-of-primitive-impl.rs:9:1 + --> $DIR/kinds-of-primitive-impl.rs:6:1 | LL | / impl str { LL | | @@ -22,7 +22,7 @@ LL | | } = help: consider using a trait to implement these methods error[E0390]: only a single inherent implementation marked with `#[lang = "char"]` is allowed for the `char` primitive - --> $DIR/kinds-of-primitive-impl.rs:15:1 + --> $DIR/kinds-of-primitive-impl.rs:12:1 | LL | / impl char { LL | | diff --git a/src/test/ui/layout/debug.rs b/src/test/ui/layout/debug.rs index 299151df66493..317955f0e39dd 100644 --- a/src/test/ui/layout/debug.rs +++ b/src/test/ui/layout/debug.rs @@ -1,5 +1,5 @@ // normalize-stderr-test "pref: Align \{\n *pow2: [1-3],\n *\}" -> "pref: $$PREF_ALIGN" -#![feature(never_type, rustc_attrs, type_alias_impl_trait)] +#![feature(never_type, rustc_attrs, min_type_alias_impl_trait)] #![crate_type = "lib"] #[rustc_layout(debug)] diff --git a/src/test/ui/layout/hexagon-enum.rs b/src/test/ui/layout/hexagon-enum.rs index 8c6c97206649c..4c58537e309ea 100644 --- a/src/test/ui/layout/hexagon-enum.rs +++ b/src/test/ui/layout/hexagon-enum.rs @@ -4,7 +4,7 @@ // Verify that the hexagon targets implement the repr(C) for enums correctly. // // See #82100 -#![feature(never_type, rustc_attrs, type_alias_impl_trait, no_core, lang_items)] +#![feature(never_type, rustc_attrs, no_core, lang_items)] #![crate_type = "lib"] #![no_core] diff --git a/src/test/ui/lifetimes/issue-83737-binders-across-types.rs b/src/test/ui/lifetimes/issue-83737-binders-across-types.rs new file mode 100644 index 0000000000000..e130561e46673 --- /dev/null +++ b/src/test/ui/lifetimes/issue-83737-binders-across-types.rs @@ -0,0 +1,14 @@ +// build-pass +// compile-flags: --edition 2018 +// compile-flags: --crate-type rlib + +use std::future::Future; + +async fn handle<F>(slf: &F) +where + F: Fn(&()) -> Box<dyn Future<Output = ()> + Unpin>, +{ + (slf)(&()).await; +} + +fn main() {} diff --git a/src/test/ui/lifetimes/issue-83753-invalid-associated-type-supertrait-hrtb.rs b/src/test/ui/lifetimes/issue-83753-invalid-associated-type-supertrait-hrtb.rs new file mode 100644 index 0000000000000..7f0ea730dd37b --- /dev/null +++ b/src/test/ui/lifetimes/issue-83753-invalid-associated-type-supertrait-hrtb.rs @@ -0,0 +1,8 @@ +// check-fail + +struct Foo {} +impl Foo { + fn bar(foo: Foo<Target = usize>) {} + //~^ associated type bindings are not allowed here +} +fn main() {} diff --git a/src/test/ui/lifetimes/issue-83753-invalid-associated-type-supertrait-hrtb.stderr b/src/test/ui/lifetimes/issue-83753-invalid-associated-type-supertrait-hrtb.stderr new file mode 100644 index 0000000000000..f7bdee6336e2d --- /dev/null +++ b/src/test/ui/lifetimes/issue-83753-invalid-associated-type-supertrait-hrtb.stderr @@ -0,0 +1,9 @@ +error[E0229]: associated type bindings are not allowed here + --> $DIR/issue-83753-invalid-associated-type-supertrait-hrtb.rs:5:21 + | +LL | fn bar(foo: Foo<Target = usize>) {} + | ^^^^^^^^^^^^^^ associated type not allowed here + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0229`. diff --git a/src/test/ui/lifetimes/issue-83907-invalid-fn-like-path.rs b/src/test/ui/lifetimes/issue-83907-invalid-fn-like-path.rs new file mode 100644 index 0000000000000..604687ce71126 --- /dev/null +++ b/src/test/ui/lifetimes/issue-83907-invalid-fn-like-path.rs @@ -0,0 +1,7 @@ +// check-fail + +static STATIC_VAR_FIVE: &One(); +//~^ cannot find type +//~| free static item without body + +fn main() {} diff --git a/src/test/ui/lifetimes/issue-83907-invalid-fn-like-path.stderr b/src/test/ui/lifetimes/issue-83907-invalid-fn-like-path.stderr new file mode 100644 index 0000000000000..e57933da558fd --- /dev/null +++ b/src/test/ui/lifetimes/issue-83907-invalid-fn-like-path.stderr @@ -0,0 +1,17 @@ +error: free static item without body + --> $DIR/issue-83907-invalid-fn-like-path.rs:3:1 + | +LL | static STATIC_VAR_FIVE: &One(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- + | | + | help: provide a definition for the static: `= <expr>;` + +error[E0412]: cannot find type `One` in this scope + --> $DIR/issue-83907-invalid-fn-like-path.rs:3:26 + | +LL | static STATIC_VAR_FIVE: &One(); + | ^^^ not found in this scope + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0412`. diff --git a/src/test/ui/lifetimes/lifetime-errors/ex3-both-anon-regions-one-is-struct-2.nll.stderr b/src/test/ui/lifetimes/lifetime-errors/ex3-both-anon-regions-one-is-struct-2.nll.stderr index 5751c3194894e..bbd62902d9f44 100644 --- a/src/test/ui/lifetimes/lifetime-errors/ex3-both-anon-regions-one-is-struct-2.nll.stderr +++ b/src/test/ui/lifetimes/lifetime-errors/ex3-both-anon-regions-one-is-struct-2.nll.stderr @@ -12,7 +12,7 @@ error[E0384]: cannot assign to immutable argument `y` --> $DIR/ex3-both-anon-regions-one-is-struct-2.rs:4:5 | LL | fn foo(mut x: Ref, y: &u32) { - | - help: make this binding mutable: `mut y` + | - help: consider making this binding mutable: `mut y` LL | y = x.b; | ^^^^^^^ cannot assign to immutable argument diff --git a/src/test/ui/lifetimes/lifetime-errors/liveness-assign-imm-local-notes.stderr b/src/test/ui/lifetimes/lifetime-errors/liveness-assign-imm-local-notes.stderr index c646912d3b679..b47a47d631e5f 100644 --- a/src/test/ui/lifetimes/lifetime-errors/liveness-assign-imm-local-notes.stderr +++ b/src/test/ui/lifetimes/lifetime-errors/liveness-assign-imm-local-notes.stderr @@ -2,7 +2,7 @@ error[E0384]: cannot assign twice to immutable variable `x` --> $DIR/liveness-assign-imm-local-notes.rs:10:9 | LL | let x; - | - help: make this binding mutable: `mut x` + | - help: consider making this binding mutable: `mut x` ... LL | x = 2; | ----- first assignment to `x` @@ -13,7 +13,7 @@ error[E0384]: cannot assign twice to immutable variable `x` --> $DIR/liveness-assign-imm-local-notes.rs:21:13 | LL | let x; - | - help: make this binding mutable: `mut x` + | - help: consider making this binding mutable: `mut x` ... LL | x = 2; | ----- first assignment to `x` @@ -24,7 +24,7 @@ error[E0384]: cannot assign twice to immutable variable `x` --> $DIR/liveness-assign-imm-local-notes.rs:30:13 | LL | let x; - | - help: make this binding mutable: `mut x` + | - help: consider making this binding mutable: `mut x` ... LL | x = 1; | ^^^^^ cannot assign twice to immutable variable @@ -33,7 +33,7 @@ error[E0384]: cannot assign twice to immutable variable `x` --> $DIR/liveness-assign-imm-local-notes.rs:32:13 | LL | let x; - | - help: make this binding mutable: `mut x` + | - help: consider making this binding mutable: `mut x` ... LL | x = 1; | ----- first assignment to `x` diff --git a/src/test/ui/lifetimes/undeclared-lifetime-used-in-debug-macro-issue-70152.stderr b/src/test/ui/lifetimes/undeclared-lifetime-used-in-debug-macro-issue-70152.stderr index 30142e24cd5f1..16333a7ca380c 100644 --- a/src/test/ui/lifetimes/undeclared-lifetime-used-in-debug-macro-issue-70152.stderr +++ b/src/test/ui/lifetimes/undeclared-lifetime-used-in-debug-macro-issue-70152.stderr @@ -1,3 +1,13 @@ +error[E0261]: use of undeclared lifetime name `'b` + --> $DIR/undeclared-lifetime-used-in-debug-macro-issue-70152.rs:3:9 + | +LL | struct Test { + | - help: consider introducing lifetime `'b` here: `<'b>` +LL | a: &'b str, + | ^^ undeclared lifetime + | + = help: if you want to experiment with in-band lifetime bindings, add `#![feature(in_band_lifetimes)]` to the crate attributes + error[E0261]: use of undeclared lifetime name `'b` --> $DIR/undeclared-lifetime-used-in-debug-macro-issue-70152.rs:13:13 | @@ -14,16 +24,6 @@ help: consider introducing lifetime `'b` here LL | fn foo<'b>(&'b self) {} | ^^^^ -error[E0261]: use of undeclared lifetime name `'b` - --> $DIR/undeclared-lifetime-used-in-debug-macro-issue-70152.rs:3:9 - | -LL | struct Test { - | - help: consider introducing lifetime `'b` here: `<'b>` -LL | a: &'b str, - | ^^ undeclared lifetime - | - = help: if you want to experiment with in-band lifetime bindings, add `#![feature(in_band_lifetimes)]` to the crate attributes - error[E0261]: use of undeclared lifetime name `'b` --> $DIR/undeclared-lifetime-used-in-debug-macro-issue-70152.rs:3:9 | diff --git a/src/test/ui/bad/bad-extern-link-attrs.rs b/src/test/ui/linkage-attr/bad-extern-link-attrs.rs similarity index 100% rename from src/test/ui/bad/bad-extern-link-attrs.rs rename to src/test/ui/linkage-attr/bad-extern-link-attrs.rs diff --git a/src/test/ui/bad/bad-extern-link-attrs.stderr b/src/test/ui/linkage-attr/bad-extern-link-attrs.stderr similarity index 100% rename from src/test/ui/bad/bad-extern-link-attrs.stderr rename to src/test/ui/linkage-attr/bad-extern-link-attrs.stderr diff --git a/src/test/ui/linkage-attr/invalid-link-args.rs b/src/test/ui/linkage-attr/invalid-link-args.rs deleted file mode 100644 index 7418691d01409..0000000000000 --- a/src/test/ui/linkage-attr/invalid-link-args.rs +++ /dev/null @@ -1,14 +0,0 @@ -// build-fail -// dont-check-compiler-stderr -// ignore-msvc due to linker-flavor=ld -// error-pattern:aFdEfSeVEEE -// compile-flags: -C linker-flavor=ld - -/* Make sure invalid link_args are printed to stderr. */ - -#![feature(link_args)] - -#[link_args = "aFdEfSeVEEE"] -extern "C" {} - -fn main() {} diff --git a/src/test/ui/linkage-attr/linkage-detect-extern-generated-name-collision.rs b/src/test/ui/linkage-attr/linkage-detect-extern-generated-name-collision.rs index 7b2f5365acaa3..3a0910658b7a9 100644 --- a/src/test/ui/linkage-attr/linkage-detect-extern-generated-name-collision.rs +++ b/src/test/ui/linkage-attr/linkage-detect-extern-generated-name-collision.rs @@ -4,6 +4,8 @@ // build-fail // aux-build:def_colliding_external.rs +// FIXME(#83838) codegen-units=1 triggers llvm asserts +// compile-flags: -Ccodegen-units=16 extern crate def_colliding_external as dep1; diff --git a/src/test/ui/linkage-attr/linkage-detect-local-generated-name-collision.rs b/src/test/ui/linkage-attr/linkage-detect-local-generated-name-collision.rs index b712f3225ed16..c1df9ccef2273 100644 --- a/src/test/ui/linkage-attr/linkage-detect-local-generated-name-collision.rs +++ b/src/test/ui/linkage-attr/linkage-detect-local-generated-name-collision.rs @@ -1,5 +1,6 @@ // build-fail - +// FIXME(#83838) codegen-units=1 triggers llvm asserts +// compile-flags: -Ccodegen-units=16 #![feature(linkage)] mod dep1 { diff --git a/src/test/ui/linkage-attr/linkage-detect-local-generated-name-collision.stderr b/src/test/ui/linkage-attr/linkage-detect-local-generated-name-collision.stderr index d008acc6e7c82..7e395e67378f8 100644 --- a/src/test/ui/linkage-attr/linkage-detect-local-generated-name-collision.stderr +++ b/src/test/ui/linkage-attr/linkage-detect-local-generated-name-collision.stderr @@ -1,5 +1,5 @@ error: symbol `collision` is already defined - --> $DIR/linkage-detect-local-generated-name-collision.rs:9:9 + --> $DIR/linkage-detect-local-generated-name-collision.rs:10:9 | LL | pub static collision: *const i32; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/src/test/ui/bad/bad-lint-cap.rs b/src/test/ui/lint/bad-lint-cap.rs similarity index 100% rename from src/test/ui/bad/bad-lint-cap.rs rename to src/test/ui/lint/bad-lint-cap.rs diff --git a/src/test/ui/bad/bad-lint-cap.stderr b/src/test/ui/lint/bad-lint-cap.stderr similarity index 100% rename from src/test/ui/bad/bad-lint-cap.stderr rename to src/test/ui/lint/bad-lint-cap.stderr diff --git a/src/test/ui/bad/bad-lint-cap2.rs b/src/test/ui/lint/bad-lint-cap2.rs similarity index 100% rename from src/test/ui/bad/bad-lint-cap2.rs rename to src/test/ui/lint/bad-lint-cap2.rs diff --git a/src/test/ui/bad/bad-lint-cap2.stderr b/src/test/ui/lint/bad-lint-cap2.stderr similarity index 100% rename from src/test/ui/bad/bad-lint-cap2.stderr rename to src/test/ui/lint/bad-lint-cap2.stderr diff --git a/src/test/ui/bad/bad-lint-cap3.rs b/src/test/ui/lint/bad-lint-cap3.rs similarity index 100% rename from src/test/ui/bad/bad-lint-cap3.rs rename to src/test/ui/lint/bad-lint-cap3.rs diff --git a/src/test/ui/bad/bad-lint-cap3.stderr b/src/test/ui/lint/bad-lint-cap3.stderr similarity index 100% rename from src/test/ui/bad/bad-lint-cap3.stderr rename to src/test/ui/lint/bad-lint-cap3.stderr diff --git a/src/test/ui/lint/bare-trait-objects-path.rs b/src/test/ui/lint/bare-trait-objects-path.rs new file mode 100644 index 0000000000000..4c961e998df64 --- /dev/null +++ b/src/test/ui/lint/bare-trait-objects-path.rs @@ -0,0 +1,18 @@ +#![feature(associated_type_defaults)] + +trait Assoc { + fn func() {} + const CONST: u8 = 0; + type Ty = u8; +} + +trait Dyn {} + +impl Assoc for dyn Dyn {} + +fn main() { + Dyn::func(); //~ WARN trait objects without an explicit `dyn` are deprecated + ::Dyn::func(); //~ WARN trait objects without an explicit `dyn` are deprecated + Dyn::CONST; //~ WARN trait objects without an explicit `dyn` are deprecated + let _: Dyn::Ty; //~ ERROR ambiguous associated type +} diff --git a/src/test/ui/lint/bare-trait-objects-path.stderr b/src/test/ui/lint/bare-trait-objects-path.stderr new file mode 100644 index 0000000000000..0a2dc5858285a --- /dev/null +++ b/src/test/ui/lint/bare-trait-objects-path.stderr @@ -0,0 +1,29 @@ +error[E0223]: ambiguous associated type + --> $DIR/bare-trait-objects-path.rs:17:12 + | +LL | let _: Dyn::Ty; + | ^^^^^^^ help: use fully-qualified syntax: `<dyn Dyn as Trait>::Ty` + +warning: trait objects without an explicit `dyn` are deprecated + --> $DIR/bare-trait-objects-path.rs:14:5 + | +LL | Dyn::func(); + | ^^^ help: use `dyn`: `<dyn Dyn>` + | + = note: `#[warn(bare_trait_objects)]` on by default + +warning: trait objects without an explicit `dyn` are deprecated + --> $DIR/bare-trait-objects-path.rs:15:5 + | +LL | ::Dyn::func(); + | ^^^^^ help: use `dyn`: `<dyn (::Dyn)>` + +warning: trait objects without an explicit `dyn` are deprecated + --> $DIR/bare-trait-objects-path.rs:16:5 + | +LL | Dyn::CONST; + | ^^^ help: use `dyn`: `<dyn Dyn>` + +error: aborting due to previous error; 3 warnings emitted + +For more information about this error, try `rustc --explain E0223`. diff --git a/src/test/ui/lint/dead-code/lint-dead-code-2.rs b/src/test/ui/lint/dead-code/lint-dead-code-2.rs index 88db4f88c3feb..2cfe6e539dbf4 100644 --- a/src/test/ui/lint/dead-code/lint-dead-code-2.rs +++ b/src/test/ui/lint/dead-code/lint-dead-code-2.rs @@ -1,6 +1,6 @@ #![allow(unused_variables)] #![deny(dead_code)] -#![feature(main, start)] +#![feature(rustc_attrs, start)] struct Foo; @@ -21,7 +21,7 @@ fn live_fn() {} fn dead_fn() {} //~ ERROR: function is never used -#[main] +#[rustc_main] fn dead_fn2() {} //~ ERROR: function is never used fn used_fn() {} diff --git a/src/test/ui/fn_must_use.rs b/src/test/ui/lint/fn_must_use.rs similarity index 100% rename from src/test/ui/fn_must_use.rs rename to src/test/ui/lint/fn_must_use.rs diff --git a/src/test/ui/fn_must_use.stderr b/src/test/ui/lint/fn_must_use.stderr similarity index 100% rename from src/test/ui/fn_must_use.stderr rename to src/test/ui/lint/fn_must_use.stderr diff --git a/src/test/ui/lint/inline-trait-and-foreign-items.full_tait.stderr b/src/test/ui/lint/inline-trait-and-foreign-items.full_tait.stderr new file mode 100644 index 0000000000000..65b4631010f20 --- /dev/null +++ b/src/test/ui/lint/inline-trait-and-foreign-items.full_tait.stderr @@ -0,0 +1,81 @@ +warning: the feature `type_alias_impl_trait` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/inline-trait-and-foreign-items.rs:4:32 + | +LL | #![cfg_attr(full_tait, feature(type_alias_impl_trait))] + | ^^^^^^^^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #63063 <https://github.com/rust-lang/rust/issues/63063> for more information + +warning: `#[inline]` is ignored on constants + --> $DIR/inline-trait-and-foreign-items.rs:10:5 + | +LL | #[inline] + | ^^^^^^^^^ + | +note: the lint level is defined here + --> $DIR/inline-trait-and-foreign-items.rs:7:9 + | +LL | #![warn(unused_attributes)] + | ^^^^^^^^^^^^^^^^^ + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: see issue #65833 <https://github.com/rust-lang/rust/issues/65833> for more information + +error[E0518]: attribute should be applied to function or closure + --> $DIR/inline-trait-and-foreign-items.rs:14:5 + | +LL | #[inline] + | ^^^^^^^^^ +LL | type T; + | ------- not a function or closure + +warning: `#[inline]` is ignored on constants + --> $DIR/inline-trait-and-foreign-items.rs:21:5 + | +LL | #[inline] + | ^^^^^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: see issue #65833 <https://github.com/rust-lang/rust/issues/65833> for more information + +error[E0518]: attribute should be applied to function or closure + --> $DIR/inline-trait-and-foreign-items.rs:25:5 + | +LL | #[inline] + | ^^^^^^^^^ +LL | type T = Self; + | -------------- not a function or closure + +error[E0518]: attribute should be applied to function or closure + --> $DIR/inline-trait-and-foreign-items.rs:28:5 + | +LL | #[inline] + | ^^^^^^^^^ +LL | type U = impl Trait; + | -------------------- not a function or closure + +error[E0518]: attribute should be applied to function or closure + --> $DIR/inline-trait-and-foreign-items.rs:33:5 + | +LL | #[inline] + | ^^^^^^^^^ +LL | static X: u32; + | -------------- not a function or closure + +error[E0518]: attribute should be applied to function or closure + --> $DIR/inline-trait-and-foreign-items.rs:36:5 + | +LL | #[inline] + | ^^^^^^^^^ +LL | type T; + | ------- not a function or closure + +error: could not find defining uses + --> $DIR/inline-trait-and-foreign-items.rs:29:14 + | +LL | type U = impl Trait; + | ^^^^^^^^^^ + +error: aborting due to 6 previous errors; 3 warnings emitted + +For more information about this error, try `rustc --explain E0518`. diff --git a/src/test/ui/lint/inline-trait-and-foreign-items.min_tait.stderr b/src/test/ui/lint/inline-trait-and-foreign-items.min_tait.stderr new file mode 100644 index 0000000000000..b9f123905a70c --- /dev/null +++ b/src/test/ui/lint/inline-trait-and-foreign-items.min_tait.stderr @@ -0,0 +1,72 @@ +warning: `#[inline]` is ignored on constants + --> $DIR/inline-trait-and-foreign-items.rs:10:5 + | +LL | #[inline] + | ^^^^^^^^^ + | +note: the lint level is defined here + --> $DIR/inline-trait-and-foreign-items.rs:7:9 + | +LL | #![warn(unused_attributes)] + | ^^^^^^^^^^^^^^^^^ + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: see issue #65833 <https://github.com/rust-lang/rust/issues/65833> for more information + +error[E0518]: attribute should be applied to function or closure + --> $DIR/inline-trait-and-foreign-items.rs:14:5 + | +LL | #[inline] + | ^^^^^^^^^ +LL | type T; + | ------- not a function or closure + +warning: `#[inline]` is ignored on constants + --> $DIR/inline-trait-and-foreign-items.rs:21:5 + | +LL | #[inline] + | ^^^^^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: see issue #65833 <https://github.com/rust-lang/rust/issues/65833> for more information + +error[E0518]: attribute should be applied to function or closure + --> $DIR/inline-trait-and-foreign-items.rs:25:5 + | +LL | #[inline] + | ^^^^^^^^^ +LL | type T = Self; + | -------------- not a function or closure + +error[E0518]: attribute should be applied to function or closure + --> $DIR/inline-trait-and-foreign-items.rs:28:5 + | +LL | #[inline] + | ^^^^^^^^^ +LL | type U = impl Trait; + | -------------------- not a function or closure + +error[E0518]: attribute should be applied to function or closure + --> $DIR/inline-trait-and-foreign-items.rs:33:5 + | +LL | #[inline] + | ^^^^^^^^^ +LL | static X: u32; + | -------------- not a function or closure + +error[E0518]: attribute should be applied to function or closure + --> $DIR/inline-trait-and-foreign-items.rs:36:5 + | +LL | #[inline] + | ^^^^^^^^^ +LL | type T; + | ------- not a function or closure + +error: could not find defining uses + --> $DIR/inline-trait-and-foreign-items.rs:29:14 + | +LL | type U = impl Trait; + | ^^^^^^^^^^ + +error: aborting due to 6 previous errors; 2 warnings emitted + +For more information about this error, try `rustc --explain E0518`. diff --git a/src/test/ui/lint/inline-trait-and-foreign-items.rs b/src/test/ui/lint/inline-trait-and-foreign-items.rs index 6321b3c76e4d1..3103cc9d31dc8 100644 --- a/src/test/ui/lint/inline-trait-and-foreign-items.rs +++ b/src/test/ui/lint/inline-trait-and-foreign-items.rs @@ -1,5 +1,8 @@ #![feature(extern_types)] -#![feature(type_alias_impl_trait)] +// revisions: min_tait full_tait +#![feature(min_type_alias_impl_trait)] +#![cfg_attr(full_tait, feature(type_alias_impl_trait))] +//[full_tait]~^ WARN incomplete #![warn(unused_attributes)] diff --git a/src/test/ui/lint/inline-trait-and-foreign-items.stderr b/src/test/ui/lint/inline-trait-and-foreign-items.stderr deleted file mode 100644 index 6ac884c12ceb9..0000000000000 --- a/src/test/ui/lint/inline-trait-and-foreign-items.stderr +++ /dev/null @@ -1,72 +0,0 @@ -warning: `#[inline]` is ignored on constants - --> $DIR/inline-trait-and-foreign-items.rs:7:5 - | -LL | #[inline] - | ^^^^^^^^^ - | -note: the lint level is defined here - --> $DIR/inline-trait-and-foreign-items.rs:4:9 - | -LL | #![warn(unused_attributes)] - | ^^^^^^^^^^^^^^^^^ - = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = note: see issue #65833 <https://github.com/rust-lang/rust/issues/65833> for more information - -error[E0518]: attribute should be applied to function or closure - --> $DIR/inline-trait-and-foreign-items.rs:11:5 - | -LL | #[inline] - | ^^^^^^^^^ -LL | type T; - | ------- not a function or closure - -warning: `#[inline]` is ignored on constants - --> $DIR/inline-trait-and-foreign-items.rs:18:5 - | -LL | #[inline] - | ^^^^^^^^^ - | - = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = note: see issue #65833 <https://github.com/rust-lang/rust/issues/65833> for more information - -error[E0518]: attribute should be applied to function or closure - --> $DIR/inline-trait-and-foreign-items.rs:22:5 - | -LL | #[inline] - | ^^^^^^^^^ -LL | type T = Self; - | -------------- not a function or closure - -error[E0518]: attribute should be applied to function or closure - --> $DIR/inline-trait-and-foreign-items.rs:25:5 - | -LL | #[inline] - | ^^^^^^^^^ -LL | type U = impl Trait; - | -------------------- not a function or closure - -error[E0518]: attribute should be applied to function or closure - --> $DIR/inline-trait-and-foreign-items.rs:30:5 - | -LL | #[inline] - | ^^^^^^^^^ -LL | static X: u32; - | -------------- not a function or closure - -error[E0518]: attribute should be applied to function or closure - --> $DIR/inline-trait-and-foreign-items.rs:33:5 - | -LL | #[inline] - | ^^^^^^^^^ -LL | type T; - | ------- not a function or closure - -error: could not find defining uses - --> $DIR/inline-trait-and-foreign-items.rs:26:14 - | -LL | type U = impl Trait; - | ^^^^^^^^^^ - -error: aborting due to 6 previous errors; 2 warnings emitted - -For more information about this error, try `rustc --explain E0518`. diff --git a/src/test/ui/lint/issue-54538-unused-parens-lint.fixed b/src/test/ui/lint/issue-54538-unused-parens-lint.fixed index c70c39b5f6dc7..0b3fe9371f775 100644 --- a/src/test/ui/lint/issue-54538-unused-parens-lint.fixed +++ b/src/test/ui/lint/issue-54538-unused-parens-lint.fixed @@ -1,7 +1,6 @@ // run-rustfix #![feature(box_patterns, stmt_expr_attributes)] -#![feature(or_patterns)] #![allow( dead_code, diff --git a/src/test/ui/lint/issue-54538-unused-parens-lint.rs b/src/test/ui/lint/issue-54538-unused-parens-lint.rs index 9dd3b63c0fed7..1e78ec5f7d95d 100644 --- a/src/test/ui/lint/issue-54538-unused-parens-lint.rs +++ b/src/test/ui/lint/issue-54538-unused-parens-lint.rs @@ -1,7 +1,6 @@ // run-rustfix #![feature(box_patterns, stmt_expr_attributes)] -#![feature(or_patterns)] #![allow( dead_code, diff --git a/src/test/ui/lint/issue-54538-unused-parens-lint.stderr b/src/test/ui/lint/issue-54538-unused-parens-lint.stderr index ee466857757a3..e6d0a359c5ca7 100644 --- a/src/test/ui/lint/issue-54538-unused-parens-lint.stderr +++ b/src/test/ui/lint/issue-54538-unused-parens-lint.stderr @@ -1,149 +1,149 @@ error: unnecessary parentheses around pattern - --> $DIR/issue-54538-unused-parens-lint.rs:17:9 + --> $DIR/issue-54538-unused-parens-lint.rs:16:9 | LL | let (a) = 0; | ^^^ help: remove these parentheses | note: the lint level is defined here - --> $DIR/issue-54538-unused-parens-lint.rs:14:9 + --> $DIR/issue-54538-unused-parens-lint.rs:13:9 | LL | #![deny(unused_parens)] | ^^^^^^^^^^^^^ error: unnecessary parentheses around pattern - --> $DIR/issue-54538-unused-parens-lint.rs:18:9 + --> $DIR/issue-54538-unused-parens-lint.rs:17:9 | LL | for (a) in 0..1 {} | ^^^ help: remove these parentheses error: unnecessary parentheses around pattern - --> $DIR/issue-54538-unused-parens-lint.rs:19:12 + --> $DIR/issue-54538-unused-parens-lint.rs:18:12 | LL | if let (a) = 0 {} | ^^^ help: remove these parentheses error: unnecessary parentheses around pattern - --> $DIR/issue-54538-unused-parens-lint.rs:20:15 + --> $DIR/issue-54538-unused-parens-lint.rs:19:15 | LL | while let (a) = 0 {} | ^^^ help: remove these parentheses error: unnecessary parentheses around pattern - --> $DIR/issue-54538-unused-parens-lint.rs:21:12 + --> $DIR/issue-54538-unused-parens-lint.rs:20:12 | LL | fn foo((a): u8) {} | ^^^ help: remove these parentheses error: unnecessary parentheses around pattern - --> $DIR/issue-54538-unused-parens-lint.rs:22:14 + --> $DIR/issue-54538-unused-parens-lint.rs:21:14 | LL | let _ = |(a): u8| 0; | ^^^ help: remove these parentheses error: unnecessary parentheses around pattern - --> $DIR/issue-54538-unused-parens-lint.rs:50:12 + --> $DIR/issue-54538-unused-parens-lint.rs:49:12 | LL | if let (0 | 1) = 0 {} | ^^^^^^^ help: remove these parentheses error: unnecessary parentheses around pattern - --> $DIR/issue-54538-unused-parens-lint.rs:51:13 + --> $DIR/issue-54538-unused-parens-lint.rs:50:13 | LL | if let ((0 | 1),) = (0,) {} | ^^^^^^^ help: remove these parentheses error: unnecessary parentheses around pattern - --> $DIR/issue-54538-unused-parens-lint.rs:52:13 + --> $DIR/issue-54538-unused-parens-lint.rs:51:13 | LL | if let [(0 | 1)] = [0] {} | ^^^^^^^ help: remove these parentheses error: unnecessary parentheses around pattern - --> $DIR/issue-54538-unused-parens-lint.rs:53:16 + --> $DIR/issue-54538-unused-parens-lint.rs:52:16 | LL | if let 0 | (1 | 2) = 0 {} | ^^^^^^^ help: remove these parentheses error: unnecessary parentheses around pattern - --> $DIR/issue-54538-unused-parens-lint.rs:55:15 + --> $DIR/issue-54538-unused-parens-lint.rs:54:15 | LL | if let TS((0 | 1)) = TS(0) {} | ^^^^^^^ help: remove these parentheses error: unnecessary parentheses around pattern - --> $DIR/issue-54538-unused-parens-lint.rs:57:20 + --> $DIR/issue-54538-unused-parens-lint.rs:56:20 | LL | if let NS { f: (0 | 1) } = (NS { f: 0 }) {} | ^^^^^^^ help: remove these parentheses error: unnecessary parentheses around pattern - --> $DIR/issue-54538-unused-parens-lint.rs:67:9 + --> $DIR/issue-54538-unused-parens-lint.rs:66:9 | LL | (_) => {} | ^^^ help: remove these parentheses error: unnecessary parentheses around pattern - --> $DIR/issue-54538-unused-parens-lint.rs:68:9 + --> $DIR/issue-54538-unused-parens-lint.rs:67:9 | LL | (y) => {} | ^^^ help: remove these parentheses error: unnecessary parentheses around pattern - --> $DIR/issue-54538-unused-parens-lint.rs:69:9 + --> $DIR/issue-54538-unused-parens-lint.rs:68:9 | LL | (ref r) => {} | ^^^^^^^ help: remove these parentheses error: unnecessary parentheses around pattern - --> $DIR/issue-54538-unused-parens-lint.rs:70:9 + --> $DIR/issue-54538-unused-parens-lint.rs:69:9 | LL | (e @ 1...2) => {} | ^^^^^^^^^^^ help: remove these parentheses error: unnecessary parentheses around pattern - --> $DIR/issue-54538-unused-parens-lint.rs:76:9 + --> $DIR/issue-54538-unused-parens-lint.rs:75:9 | LL | (e @ &(1...2)) => {} | ^^^^^^^^^^^^^^ help: remove these parentheses error: unnecessary parentheses around pattern - --> $DIR/issue-54538-unused-parens-lint.rs:77:10 + --> $DIR/issue-54538-unused-parens-lint.rs:76:10 | LL | &(_) => {} | ^^^ help: remove these parentheses error: unnecessary parentheses around pattern - --> $DIR/issue-54538-unused-parens-lint.rs:88:9 + --> $DIR/issue-54538-unused-parens-lint.rs:87:9 | LL | (_) => {} | ^^^ help: remove these parentheses error: unnecessary parentheses around pattern - --> $DIR/issue-54538-unused-parens-lint.rs:89:9 + --> $DIR/issue-54538-unused-parens-lint.rs:88:9 | LL | (y) => {} | ^^^ help: remove these parentheses error: unnecessary parentheses around pattern - --> $DIR/issue-54538-unused-parens-lint.rs:90:9 + --> $DIR/issue-54538-unused-parens-lint.rs:89:9 | LL | (ref r) => {} | ^^^^^^^ help: remove these parentheses error: unnecessary parentheses around pattern - --> $DIR/issue-54538-unused-parens-lint.rs:91:9 + --> $DIR/issue-54538-unused-parens-lint.rs:90:9 | LL | (e @ 1..=2) => {} | ^^^^^^^^^^^ help: remove these parentheses error: unnecessary parentheses around pattern - --> $DIR/issue-54538-unused-parens-lint.rs:97:9 + --> $DIR/issue-54538-unused-parens-lint.rs:96:9 | LL | (e @ &(1..=2)) => {} | ^^^^^^^^^^^^^^ help: remove these parentheses error: unnecessary parentheses around pattern - --> $DIR/issue-54538-unused-parens-lint.rs:98:10 + --> $DIR/issue-54538-unused-parens-lint.rs:97:10 | LL | &(_) => {} | ^^^ help: remove these parentheses diff --git a/src/test/ui/lint/issue-67691-unused-field-in-or-pattern.rs b/src/test/ui/lint/issue-67691-unused-field-in-or-pattern.rs index b21f1ef6b2603..e3631d014d63b 100644 --- a/src/test/ui/lint/issue-67691-unused-field-in-or-pattern.rs +++ b/src/test/ui/lint/issue-67691-unused-field-in-or-pattern.rs @@ -1,7 +1,6 @@ // FIXME: should be run-rustfix, but rustfix doesn't currently support multipart suggestions, see // #53934 -#![feature(or_patterns)] #![deny(unused)] pub enum MyEnum { diff --git a/src/test/ui/lint/issue-67691-unused-field-in-or-pattern.stderr b/src/test/ui/lint/issue-67691-unused-field-in-or-pattern.stderr index 9cff2900908e6..8aefe243a944b 100644 --- a/src/test/ui/lint/issue-67691-unused-field-in-or-pattern.stderr +++ b/src/test/ui/lint/issue-67691-unused-field-in-or-pattern.stderr @@ -1,11 +1,11 @@ error: unused variable: `j` - --> $DIR/issue-67691-unused-field-in-or-pattern.rs:21:16 + --> $DIR/issue-67691-unused-field-in-or-pattern.rs:20:16 | LL | A { i, j } | B { i, j } => { | ^ ^ | note: the lint level is defined here - --> $DIR/issue-67691-unused-field-in-or-pattern.rs:5:9 + --> $DIR/issue-67691-unused-field-in-or-pattern.rs:4:9 | LL | #![deny(unused)] | ^^^^^^ @@ -16,7 +16,7 @@ LL | A { i, j: _ } | B { i, j: _ } => { | ^^^^ ^^^^ error: unused variable: `j` - --> $DIR/issue-67691-unused-field-in-or-pattern.rs:31:16 + --> $DIR/issue-67691-unused-field-in-or-pattern.rs:30:16 | LL | A { i, ref j } | B { i, ref j } => { | ^^^^^ ^^^^^ @@ -27,7 +27,7 @@ LL | A { i, j: _ } | B { i, j: _ } => { | ^^^^ ^^^^ error: unused variable: `j` - --> $DIR/issue-67691-unused-field-in-or-pattern.rs:41:21 + --> $DIR/issue-67691-unused-field-in-or-pattern.rs:40:21 | LL | Some(A { i, j } | B { i, j }) => { | ^ ^ @@ -38,7 +38,7 @@ LL | Some(A { i, j: _ } | B { i, j: _ }) => { | ^^^^ ^^^^ error: unused variable: `j` - --> $DIR/issue-67691-unused-field-in-or-pattern.rs:53:21 + --> $DIR/issue-67691-unused-field-in-or-pattern.rs:52:21 | LL | Some(A { i, ref j } | B { i, ref j }) => { | ^^^^^ ^^^^^ @@ -49,7 +49,7 @@ LL | Some(A { i, j: _ } | B { i, j: _ }) => { | ^^^^ ^^^^ error: unused variable: `i` - --> $DIR/issue-67691-unused-field-in-or-pattern.rs:63:24 + --> $DIR/issue-67691-unused-field-in-or-pattern.rs:62:24 | LL | MixedEnum::A { i } | MixedEnum::B(i) => { | ^ ^ @@ -60,7 +60,7 @@ LL | MixedEnum::A { i: _ } | MixedEnum::B(_) => { | ^^^^ ^ error: unused variable: `i` - --> $DIR/issue-67691-unused-field-in-or-pattern.rs:71:24 + --> $DIR/issue-67691-unused-field-in-or-pattern.rs:70:24 | LL | MixedEnum::A { ref i } | MixedEnum::B(ref i) => { | ^^^^^ ^^^^^ diff --git a/src/test/ui/lint/issue-83477.rs b/src/test/ui/lint/issue-83477.rs new file mode 100644 index 0000000000000..0eba52acfa3d8 --- /dev/null +++ b/src/test/ui/lint/issue-83477.rs @@ -0,0 +1,16 @@ +// check-pass +#![warn(rustc::internal)] + +#[allow(rustc::foo::bar::default_hash_types)] +//~^ WARN unknown lint: `rustc::foo::bar::default_hash_types` +//~| HELP did you mean +//~| SUGGESTION rustc::default_hash_types +#[allow(rustc::foo::default_hash_types)] +//~^ WARN unknown lint: `rustc::foo::default_hash_types` +//~| HELP did you mean +//~| SUGGESTION rustc::default_hash_types +fn main() { + let _ = std::collections::HashMap::<String, String>::new(); + //~^ WARN Prefer FxHashMap over HashMap, it has better performance + //~| HELP use +} diff --git a/src/test/ui/lint/issue-83477.stderr b/src/test/ui/lint/issue-83477.stderr new file mode 100644 index 0000000000000..dbe0c9e01301f --- /dev/null +++ b/src/test/ui/lint/issue-83477.stderr @@ -0,0 +1,30 @@ +warning: unknown lint: `rustc::foo::bar::default_hash_types` + --> $DIR/issue-83477.rs:4:9 + | +LL | #[allow(rustc::foo::bar::default_hash_types)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: did you mean: `rustc::default_hash_types` + | + = note: `#[warn(unknown_lints)]` on by default + +warning: unknown lint: `rustc::foo::default_hash_types` + --> $DIR/issue-83477.rs:8:9 + | +LL | #[allow(rustc::foo::default_hash_types)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: did you mean: `rustc::default_hash_types` + +warning: Prefer FxHashMap over HashMap, it has better performance + --> $DIR/issue-83477.rs:13:31 + | +LL | let _ = std::collections::HashMap::<String, String>::new(); + | ^^^^^^^ help: use: `FxHashMap` + | +note: the lint level is defined here + --> $DIR/issue-83477.rs:2:9 + | +LL | #![warn(rustc::internal)] + | ^^^^^^^^^^^^^^^ + = note: `#[warn(rustc::default_hash_types)]` implied by `#[warn(rustc::internal)]` + = note: a `use rustc_data_structures::fx::FxHashMap` may be necessary + +warning: 3 warnings emitted + diff --git a/src/test/ui/lint/lint-ctypes-73249-2.full_tait.stderr b/src/test/ui/lint/lint-ctypes-73249-2.full_tait.stderr new file mode 100644 index 0000000000000..619ca15839b39 --- /dev/null +++ b/src/test/ui/lint/lint-ctypes-73249-2.full_tait.stderr @@ -0,0 +1,24 @@ +warning: the feature `type_alias_impl_trait` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/lint-ctypes-73249-2.rs:3:32 + | +LL | #![cfg_attr(full_tait, feature(type_alias_impl_trait))] + | ^^^^^^^^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #63063 <https://github.com/rust-lang/rust/issues/63063> for more information + +error: `extern` block uses type `impl Baz`, which is not FFI-safe + --> $DIR/lint-ctypes-73249-2.rs:29:25 + | +LL | pub fn lint_me() -> A<()>; + | ^^^^^ not FFI-safe + | +note: the lint level is defined here + --> $DIR/lint-ctypes-73249-2.rs:5:9 + | +LL | #![deny(improper_ctypes)] + | ^^^^^^^^^^^^^^^ + = note: opaque types have no C equivalent + +error: aborting due to previous error; 1 warning emitted + diff --git a/src/test/ui/lint/lint-ctypes-73249-2.min_tait.stderr b/src/test/ui/lint/lint-ctypes-73249-2.min_tait.stderr new file mode 100644 index 0000000000000..479bd5744735f --- /dev/null +++ b/src/test/ui/lint/lint-ctypes-73249-2.min_tait.stderr @@ -0,0 +1,15 @@ +error: `extern` block uses type `impl Baz`, which is not FFI-safe + --> $DIR/lint-ctypes-73249-2.rs:29:25 + | +LL | pub fn lint_me() -> A<()>; + | ^^^^^ not FFI-safe + | +note: the lint level is defined here + --> $DIR/lint-ctypes-73249-2.rs:5:9 + | +LL | #![deny(improper_ctypes)] + | ^^^^^^^^^^^^^^^ + = note: opaque types have no C equivalent + +error: aborting due to previous error + diff --git a/src/test/ui/lint/lint-ctypes-73249-2.rs b/src/test/ui/lint/lint-ctypes-73249-2.rs index 86cc5e2c31e81..f3313f892175b 100644 --- a/src/test/ui/lint/lint-ctypes-73249-2.rs +++ b/src/test/ui/lint/lint-ctypes-73249-2.rs @@ -1,4 +1,7 @@ -#![feature(type_alias_impl_trait)] +// revisions: min_tait full_tait +#![feature(min_type_alias_impl_trait)] +#![cfg_attr(full_tait, feature(type_alias_impl_trait))] +//[full_tait]~^ WARN incomplete #![deny(improper_ctypes)] pub trait Baz { } diff --git a/src/test/ui/lint/lint-ctypes-73249-2.stderr b/src/test/ui/lint/lint-ctypes-73249-2.stderr deleted file mode 100644 index 36dbe3217d75a..0000000000000 --- a/src/test/ui/lint/lint-ctypes-73249-2.stderr +++ /dev/null @@ -1,15 +0,0 @@ -error: `extern` block uses type `impl Baz`, which is not FFI-safe - --> $DIR/lint-ctypes-73249-2.rs:26:25 - | -LL | pub fn lint_me() -> A<()>; - | ^^^^^ not FFI-safe - | -note: the lint level is defined here - --> $DIR/lint-ctypes-73249-2.rs:2:9 - | -LL | #![deny(improper_ctypes)] - | ^^^^^^^^^^^^^^^ - = note: opaque types have no C equivalent - -error: aborting due to previous error - diff --git a/src/test/ui/lint/lint-ctypes-73249-3.full_tait.stderr b/src/test/ui/lint/lint-ctypes-73249-3.full_tait.stderr new file mode 100644 index 0000000000000..af0f26a9f43e6 --- /dev/null +++ b/src/test/ui/lint/lint-ctypes-73249-3.full_tait.stderr @@ -0,0 +1,24 @@ +warning: the feature `type_alias_impl_trait` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/lint-ctypes-73249-3.rs:3:32 + | +LL | #![cfg_attr(full_tait, feature(type_alias_impl_trait))] + | ^^^^^^^^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #63063 <https://github.com/rust-lang/rust/issues/63063> for more information + +error: `extern` block uses type `impl Baz`, which is not FFI-safe + --> $DIR/lint-ctypes-73249-3.rs:21:25 + | +LL | pub fn lint_me() -> A; + | ^ not FFI-safe + | +note: the lint level is defined here + --> $DIR/lint-ctypes-73249-3.rs:5:9 + | +LL | #![deny(improper_ctypes)] + | ^^^^^^^^^^^^^^^ + = note: opaque types have no C equivalent + +error: aborting due to previous error; 1 warning emitted + diff --git a/src/test/ui/lint/lint-ctypes-73249-3.min_tait.stderr b/src/test/ui/lint/lint-ctypes-73249-3.min_tait.stderr new file mode 100644 index 0000000000000..880581ff05d22 --- /dev/null +++ b/src/test/ui/lint/lint-ctypes-73249-3.min_tait.stderr @@ -0,0 +1,15 @@ +error: `extern` block uses type `impl Baz`, which is not FFI-safe + --> $DIR/lint-ctypes-73249-3.rs:21:25 + | +LL | pub fn lint_me() -> A; + | ^ not FFI-safe + | +note: the lint level is defined here + --> $DIR/lint-ctypes-73249-3.rs:5:9 + | +LL | #![deny(improper_ctypes)] + | ^^^^^^^^^^^^^^^ + = note: opaque types have no C equivalent + +error: aborting due to previous error + diff --git a/src/test/ui/lint/lint-ctypes-73249-3.rs b/src/test/ui/lint/lint-ctypes-73249-3.rs index 25c4e7c92a854..966c7d5ce3cb6 100644 --- a/src/test/ui/lint/lint-ctypes-73249-3.rs +++ b/src/test/ui/lint/lint-ctypes-73249-3.rs @@ -1,4 +1,7 @@ -#![feature(type_alias_impl_trait)] +// revisions: min_tait full_tait +#![feature(min_type_alias_impl_trait)] +#![cfg_attr(full_tait, feature(type_alias_impl_trait))] +//[full_tait]~^ WARN incomplete #![deny(improper_ctypes)] pub trait Baz { } diff --git a/src/test/ui/lint/lint-ctypes-73249-3.stderr b/src/test/ui/lint/lint-ctypes-73249-3.stderr deleted file mode 100644 index 7d133287bd73e..0000000000000 --- a/src/test/ui/lint/lint-ctypes-73249-3.stderr +++ /dev/null @@ -1,15 +0,0 @@ -error: `extern` block uses type `impl Baz`, which is not FFI-safe - --> $DIR/lint-ctypes-73249-3.rs:18:25 - | -LL | pub fn lint_me() -> A; - | ^ not FFI-safe - | -note: the lint level is defined here - --> $DIR/lint-ctypes-73249-3.rs:2:9 - | -LL | #![deny(improper_ctypes)] - | ^^^^^^^^^^^^^^^ - = note: opaque types have no C equivalent - -error: aborting due to previous error - diff --git a/src/test/ui/lint/lint-ctypes-73249-5.full_tait.stderr b/src/test/ui/lint/lint-ctypes-73249-5.full_tait.stderr new file mode 100644 index 0000000000000..b80084fce068c --- /dev/null +++ b/src/test/ui/lint/lint-ctypes-73249-5.full_tait.stderr @@ -0,0 +1,24 @@ +warning: the feature `type_alias_impl_trait` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/lint-ctypes-73249-5.rs:3:32 + | +LL | #![cfg_attr(full_tait, feature(type_alias_impl_trait))] + | ^^^^^^^^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #63063 <https://github.com/rust-lang/rust/issues/63063> for more information + +error: `extern` block uses type `impl Baz`, which is not FFI-safe + --> $DIR/lint-ctypes-73249-5.rs:21:25 + | +LL | pub fn lint_me() -> A; + | ^ not FFI-safe + | +note: the lint level is defined here + --> $DIR/lint-ctypes-73249-5.rs:5:9 + | +LL | #![deny(improper_ctypes)] + | ^^^^^^^^^^^^^^^ + = note: opaque types have no C equivalent + +error: aborting due to previous error; 1 warning emitted + diff --git a/src/test/ui/lint/lint-ctypes-73249-5.min_tait.stderr b/src/test/ui/lint/lint-ctypes-73249-5.min_tait.stderr new file mode 100644 index 0000000000000..f42549d909656 --- /dev/null +++ b/src/test/ui/lint/lint-ctypes-73249-5.min_tait.stderr @@ -0,0 +1,15 @@ +error: `extern` block uses type `impl Baz`, which is not FFI-safe + --> $DIR/lint-ctypes-73249-5.rs:21:25 + | +LL | pub fn lint_me() -> A; + | ^ not FFI-safe + | +note: the lint level is defined here + --> $DIR/lint-ctypes-73249-5.rs:5:9 + | +LL | #![deny(improper_ctypes)] + | ^^^^^^^^^^^^^^^ + = note: opaque types have no C equivalent + +error: aborting due to previous error + diff --git a/src/test/ui/lint/lint-ctypes-73249-5.rs b/src/test/ui/lint/lint-ctypes-73249-5.rs index 61e46983ede65..81979a9b6e696 100644 --- a/src/test/ui/lint/lint-ctypes-73249-5.rs +++ b/src/test/ui/lint/lint-ctypes-73249-5.rs @@ -1,4 +1,7 @@ -#![feature(type_alias_impl_trait)] +// revisions: min_tait full_tait +#![feature(min_type_alias_impl_trait)] +#![cfg_attr(full_tait, feature(type_alias_impl_trait))] +//[full_tait]~^ WARN incomplete #![deny(improper_ctypes)] pub trait Baz { } diff --git a/src/test/ui/lint/lint-ctypes-73249-5.stderr b/src/test/ui/lint/lint-ctypes-73249-5.stderr deleted file mode 100644 index d2780cb60e7dd..0000000000000 --- a/src/test/ui/lint/lint-ctypes-73249-5.stderr +++ /dev/null @@ -1,15 +0,0 @@ -error: `extern` block uses type `impl Baz`, which is not FFI-safe - --> $DIR/lint-ctypes-73249-5.rs:18:25 - | -LL | pub fn lint_me() -> A; - | ^ not FFI-safe - | -note: the lint level is defined here - --> $DIR/lint-ctypes-73249-5.rs:2:9 - | -LL | #![deny(improper_ctypes)] - | ^^^^^^^^^^^^^^^ - = note: opaque types have no C equivalent - -error: aborting due to previous error - diff --git a/src/test/ui/lint/lint-ctypes-73251-1.full_tait.stderr b/src/test/ui/lint/lint-ctypes-73251-1.full_tait.stderr new file mode 100644 index 0000000000000..5610230380b80 --- /dev/null +++ b/src/test/ui/lint/lint-ctypes-73251-1.full_tait.stderr @@ -0,0 +1,24 @@ +warning: the feature `type_alias_impl_trait` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/lint-ctypes-73251-1.rs:3:32 + | +LL | #![cfg_attr(full_tait, feature(type_alias_impl_trait))] + | ^^^^^^^^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #63063 <https://github.com/rust-lang/rust/issues/63063> for more information + +error: `extern` block uses type `impl Baz`, which is not FFI-safe + --> $DIR/lint-ctypes-73251-1.rs:24:25 + | +LL | pub fn lint_me() -> <u32 as Foo>::Assoc; + | ^^^^^^^^^^^^^^^^^^^ not FFI-safe + | +note: the lint level is defined here + --> $DIR/lint-ctypes-73251-1.rs:5:9 + | +LL | #![deny(improper_ctypes)] + | ^^^^^^^^^^^^^^^ + = note: opaque types have no C equivalent + +error: aborting due to previous error; 1 warning emitted + diff --git a/src/test/ui/lint/lint-ctypes-73251-1.min_tait.stderr b/src/test/ui/lint/lint-ctypes-73251-1.min_tait.stderr new file mode 100644 index 0000000000000..5b00fc4cce657 --- /dev/null +++ b/src/test/ui/lint/lint-ctypes-73251-1.min_tait.stderr @@ -0,0 +1,15 @@ +error: `extern` block uses type `impl Baz`, which is not FFI-safe + --> $DIR/lint-ctypes-73251-1.rs:24:25 + | +LL | pub fn lint_me() -> <u32 as Foo>::Assoc; + | ^^^^^^^^^^^^^^^^^^^ not FFI-safe + | +note: the lint level is defined here + --> $DIR/lint-ctypes-73251-1.rs:5:9 + | +LL | #![deny(improper_ctypes)] + | ^^^^^^^^^^^^^^^ + = note: opaque types have no C equivalent + +error: aborting due to previous error + diff --git a/src/test/ui/lint/lint-ctypes-73251-1.rs b/src/test/ui/lint/lint-ctypes-73251-1.rs index 2ce80982f5ca1..3f15a2fb42c42 100644 --- a/src/test/ui/lint/lint-ctypes-73251-1.rs +++ b/src/test/ui/lint/lint-ctypes-73251-1.rs @@ -1,4 +1,7 @@ -#![feature(type_alias_impl_trait)] +// revisions: min_tait full_tait +#![feature(min_type_alias_impl_trait)] +#![cfg_attr(full_tait, feature(type_alias_impl_trait))] +//[full_tait]~^ WARN incomplete #![deny(improper_ctypes)] pub trait Baz { } diff --git a/src/test/ui/lint/lint-ctypes-73251-1.stderr b/src/test/ui/lint/lint-ctypes-73251-1.stderr deleted file mode 100644 index 0b4237bb96fb7..0000000000000 --- a/src/test/ui/lint/lint-ctypes-73251-1.stderr +++ /dev/null @@ -1,15 +0,0 @@ -error: `extern` block uses type `impl Baz`, which is not FFI-safe - --> $DIR/lint-ctypes-73251-1.rs:21:25 - | -LL | pub fn lint_me() -> <u32 as Foo>::Assoc; - | ^^^^^^^^^^^^^^^^^^^ not FFI-safe - | -note: the lint level is defined here - --> $DIR/lint-ctypes-73251-1.rs:2:9 - | -LL | #![deny(improper_ctypes)] - | ^^^^^^^^^^^^^^^ - = note: opaque types have no C equivalent - -error: aborting due to previous error - diff --git a/src/test/ui/lint/lint-ctypes-73251-2.full_tait.stderr b/src/test/ui/lint/lint-ctypes-73251-2.full_tait.stderr new file mode 100644 index 0000000000000..19911264a36d7 --- /dev/null +++ b/src/test/ui/lint/lint-ctypes-73251-2.full_tait.stderr @@ -0,0 +1,24 @@ +warning: the feature `type_alias_impl_trait` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/lint-ctypes-73251-2.rs:3:32 + | +LL | #![cfg_attr(full_tait, feature(type_alias_impl_trait))] + | ^^^^^^^^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #63063 <https://github.com/rust-lang/rust/issues/63063> for more information + +error: `extern` block uses type `impl TraitA`, which is not FFI-safe + --> $DIR/lint-ctypes-73251-2.rs:32:25 + | +LL | pub fn lint_me() -> <AliasB as TraitB>::Assoc; + | ^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe + | +note: the lint level is defined here + --> $DIR/lint-ctypes-73251-2.rs:5:9 + | +LL | #![deny(improper_ctypes)] + | ^^^^^^^^^^^^^^^ + = note: opaque types have no C equivalent + +error: aborting due to previous error; 1 warning emitted + diff --git a/src/test/ui/lint/lint-ctypes-73251-2.min_tait.stderr b/src/test/ui/lint/lint-ctypes-73251-2.min_tait.stderr new file mode 100644 index 0000000000000..4130ee7b4cb6b --- /dev/null +++ b/src/test/ui/lint/lint-ctypes-73251-2.min_tait.stderr @@ -0,0 +1,15 @@ +error: `extern` block uses type `impl TraitA`, which is not FFI-safe + --> $DIR/lint-ctypes-73251-2.rs:32:25 + | +LL | pub fn lint_me() -> <AliasB as TraitB>::Assoc; + | ^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe + | +note: the lint level is defined here + --> $DIR/lint-ctypes-73251-2.rs:5:9 + | +LL | #![deny(improper_ctypes)] + | ^^^^^^^^^^^^^^^ + = note: opaque types have no C equivalent + +error: aborting due to previous error + diff --git a/src/test/ui/lint/lint-ctypes-73251-2.rs b/src/test/ui/lint/lint-ctypes-73251-2.rs index 3427c657b42ac..888671daca5e4 100644 --- a/src/test/ui/lint/lint-ctypes-73251-2.rs +++ b/src/test/ui/lint/lint-ctypes-73251-2.rs @@ -1,4 +1,7 @@ -#![feature(type_alias_impl_trait)] +// revisions: min_tait full_tait +#![feature(min_type_alias_impl_trait)] +#![cfg_attr(full_tait, feature(type_alias_impl_trait))] +//[full_tait]~^ WARN incomplete #![deny(improper_ctypes)] pub trait TraitA { diff --git a/src/test/ui/lint/lint-ctypes-73251-2.stderr b/src/test/ui/lint/lint-ctypes-73251-2.stderr deleted file mode 100644 index 43f7629b043a9..0000000000000 --- a/src/test/ui/lint/lint-ctypes-73251-2.stderr +++ /dev/null @@ -1,15 +0,0 @@ -error: `extern` block uses type `impl TraitA`, which is not FFI-safe - --> $DIR/lint-ctypes-73251-2.rs:29:25 - | -LL | pub fn lint_me() -> <AliasB as TraitB>::Assoc; - | ^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe - | -note: the lint level is defined here - --> $DIR/lint-ctypes-73251-2.rs:2:9 - | -LL | #![deny(improper_ctypes)] - | ^^^^^^^^^^^^^^^ - = note: opaque types have no C equivalent - -error: aborting due to previous error - diff --git a/src/test/ui/lint/lint-ctypes-73251.full_tait.stderr b/src/test/ui/lint/lint-ctypes-73251.full_tait.stderr new file mode 100644 index 0000000000000..577cf6cf5453c --- /dev/null +++ b/src/test/ui/lint/lint-ctypes-73251.full_tait.stderr @@ -0,0 +1,11 @@ +warning: the feature `type_alias_impl_trait` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/lint-ctypes-73251.rs:5:32 + | +LL | #![cfg_attr(full_tait, feature(type_alias_impl_trait))] + | ^^^^^^^^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #63063 <https://github.com/rust-lang/rust/issues/63063> for more information + +warning: 1 warning emitted + diff --git a/src/test/ui/lint/lint-ctypes-73251.rs b/src/test/ui/lint/lint-ctypes-73251.rs index ebc2ca77b67a1..36e541612d16b 100644 --- a/src/test/ui/lint/lint-ctypes-73251.rs +++ b/src/test/ui/lint/lint-ctypes-73251.rs @@ -1,6 +1,9 @@ // check-pass -#![feature(type_alias_impl_trait)] +// revisions: min_tait full_tait +#![feature(min_type_alias_impl_trait)] +#![cfg_attr(full_tait, feature(type_alias_impl_trait))] +//[full_tait]~^ WARN incomplete #![deny(improper_ctypes)] pub trait Foo { diff --git a/src/test/ui/lint/lint-deref-nullptr.rs b/src/test/ui/lint/lint-deref-nullptr.rs new file mode 100644 index 0000000000000..d052dbd9b647c --- /dev/null +++ b/src/test/ui/lint/lint-deref-nullptr.rs @@ -0,0 +1,38 @@ +// test the deref_nullptr lint + +#![deny(deref_nullptr)] + +use std::ptr; + +struct Struct { + field: u8, +} + +fn f() { + unsafe { + let a = 1; + let ub = *(a as *const i32); + let ub = *(0 as *const i32); + //~^ ERROR dereferencing a null pointer + let ub = *ptr::null::<i32>(); + //~^ ERROR dereferencing a null pointer + let ub = *ptr::null_mut::<i32>(); + //~^ ERROR dereferencing a null pointer + let ub = *(ptr::null::<i16>() as *const i32); + //~^ ERROR dereferencing a null pointer + let ub = *(ptr::null::<i16>() as *mut i32 as *mut usize as *const u8); + //~^ ERROR dereferencing a null pointer + let ub = &*ptr::null::<i32>(); + //~^ ERROR dereferencing a null pointer + let ub = &*ptr::null_mut::<i32>(); + //~^ ERROR dereferencing a null pointer + ptr::addr_of!(*ptr::null::<i32>()); + //~^ ERROR dereferencing a null pointer + ptr::addr_of_mut!(*ptr::null_mut::<i32>()); + //~^ ERROR dereferencing a null pointer + let offset = ptr::addr_of!((*ptr::null::<Struct>()).field); + //~^ ERROR dereferencing a null pointer + } +} + +fn main() {} diff --git a/src/test/ui/lint/lint-deref-nullptr.stderr b/src/test/ui/lint/lint-deref-nullptr.stderr new file mode 100644 index 0000000000000..c6f432e4e4207 --- /dev/null +++ b/src/test/ui/lint/lint-deref-nullptr.stderr @@ -0,0 +1,68 @@ +error: dereferencing a null pointer + --> $DIR/lint-deref-nullptr.rs:15:18 + | +LL | let ub = *(0 as *const i32); + | ^^^^^^^^^^^^^^^^^^ this code causes undefined behavior when executed + | +note: the lint level is defined here + --> $DIR/lint-deref-nullptr.rs:3:9 + | +LL | #![deny(deref_nullptr)] + | ^^^^^^^^^^^^^ + +error: dereferencing a null pointer + --> $DIR/lint-deref-nullptr.rs:17:18 + | +LL | let ub = *ptr::null::<i32>(); + | ^^^^^^^^^^^^^^^^^^^ this code causes undefined behavior when executed + +error: dereferencing a null pointer + --> $DIR/lint-deref-nullptr.rs:19:18 + | +LL | let ub = *ptr::null_mut::<i32>(); + | ^^^^^^^^^^^^^^^^^^^^^^^ this code causes undefined behavior when executed + +error: dereferencing a null pointer + --> $DIR/lint-deref-nullptr.rs:21:18 + | +LL | let ub = *(ptr::null::<i16>() as *const i32); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ this code causes undefined behavior when executed + +error: dereferencing a null pointer + --> $DIR/lint-deref-nullptr.rs:23:18 + | +LL | let ub = *(ptr::null::<i16>() as *mut i32 as *mut usize as *const u8); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ this code causes undefined behavior when executed + +error: dereferencing a null pointer + --> $DIR/lint-deref-nullptr.rs:25:19 + | +LL | let ub = &*ptr::null::<i32>(); + | ^^^^^^^^^^^^^^^^^^^ this code causes undefined behavior when executed + +error: dereferencing a null pointer + --> $DIR/lint-deref-nullptr.rs:27:19 + | +LL | let ub = &*ptr::null_mut::<i32>(); + | ^^^^^^^^^^^^^^^^^^^^^^^ this code causes undefined behavior when executed + +error: dereferencing a null pointer + --> $DIR/lint-deref-nullptr.rs:29:23 + | +LL | ptr::addr_of!(*ptr::null::<i32>()); + | ^^^^^^^^^^^^^^^^^^^ this code causes undefined behavior when executed + +error: dereferencing a null pointer + --> $DIR/lint-deref-nullptr.rs:31:27 + | +LL | ptr::addr_of_mut!(*ptr::null_mut::<i32>()); + | ^^^^^^^^^^^^^^^^^^^^^^^ this code causes undefined behavior when executed + +error: dereferencing a null pointer + --> $DIR/lint-deref-nullptr.rs:33:36 + | +LL | let offset = ptr::addr_of!((*ptr::null::<Struct>()).field); + | ^^^^^^^^^^^^^^^^^^^^^^^^ this code causes undefined behavior when executed + +error: aborting due to 10 previous errors + diff --git a/src/test/ui/lint-expr-stmt-attrs-for-early-lints.rs b/src/test/ui/lint/lint-expr-stmt-attrs-for-early-lints.rs similarity index 100% rename from src/test/ui/lint-expr-stmt-attrs-for-early-lints.rs rename to src/test/ui/lint/lint-expr-stmt-attrs-for-early-lints.rs diff --git a/src/test/ui/lint/lint-non-snake-case-no-lowercase-equivalent.rs b/src/test/ui/lint/lint-non-snake-case-no-lowercase-equivalent.rs index 710eebe4b6525..9f0c87dcaa61c 100644 --- a/src/test/ui/lint/lint-non-snake-case-no-lowercase-equivalent.rs +++ b/src/test/ui/lint/lint-non-snake-case-no-lowercase-equivalent.rs @@ -3,7 +3,6 @@ #![allow(dead_code)] // pretty-expanded FIXME #23616 -#![feature(non_ascii_idents)] #![deny(non_snake_case)] // This name is neither upper nor lower case diff --git a/src/test/ui/lint/lint-nonstandard-style-unicode-1.rs b/src/test/ui/lint/lint-nonstandard-style-unicode-1.rs index 034499145b780..527d0ea943285 100644 --- a/src/test/ui/lint/lint-nonstandard-style-unicode-1.rs +++ b/src/test/ui/lint/lint-nonstandard-style-unicode-1.rs @@ -1,7 +1,6 @@ #![allow(dead_code)] #![forbid(non_camel_case_types)] -#![feature(non_ascii_idents)] // Some scripts (e.g., hiragana) don't have a concept of // upper/lowercase diff --git a/src/test/ui/lint/lint-nonstandard-style-unicode-1.stderr b/src/test/ui/lint/lint-nonstandard-style-unicode-1.stderr index 371002656591c..6c2aa225e602e 100644 --- a/src/test/ui/lint/lint-nonstandard-style-unicode-1.stderr +++ b/src/test/ui/lint/lint-nonstandard-style-unicode-1.stderr @@ -1,5 +1,5 @@ error: type `χa` should have an upper camel case name - --> $DIR/lint-nonstandard-style-unicode-1.rs:15:8 + --> $DIR/lint-nonstandard-style-unicode-1.rs:14:8 | LL | struct χa; | ^^ help: convert the identifier to upper camel case: `Χa` @@ -11,37 +11,37 @@ LL | #![forbid(non_camel_case_types)] | ^^^^^^^^^^^^^^^^^^^^ error: type `__χa` should have an upper camel case name - --> $DIR/lint-nonstandard-style-unicode-1.rs:23:8 + --> $DIR/lint-nonstandard-style-unicode-1.rs:22:8 | LL | struct __χa; | ^^^^ help: convert the identifier to upper camel case: `Χa` error: type `对__否` should have an upper camel case name - --> $DIR/lint-nonstandard-style-unicode-1.rs:28:8 + --> $DIR/lint-nonstandard-style-unicode-1.rs:27:8 | LL | struct 对__否; | ^^^^^^ help: convert the identifier to upper camel case: `对_否` error: type `ヒ__χ` should have an upper camel case name - --> $DIR/lint-nonstandard-style-unicode-1.rs:31:8 + --> $DIR/lint-nonstandard-style-unicode-1.rs:30:8 | LL | struct ヒ__χ; | ^^^^^ help: convert the identifier to upper camel case: `ヒΧ` error: type `Hello_你好` should have an upper camel case name - --> $DIR/lint-nonstandard-style-unicode-1.rs:37:8 + --> $DIR/lint-nonstandard-style-unicode-1.rs:36:8 | LL | struct Hello_你好; | ^^^^^^^^^^ help: convert the identifier to upper camel case: `Hello你好` error: type `Hello_World` should have an upper camel case name - --> $DIR/lint-nonstandard-style-unicode-1.rs:40:8 + --> $DIR/lint-nonstandard-style-unicode-1.rs:39:8 | LL | struct Hello_World; | ^^^^^^^^^^^ help: convert the identifier to upper camel case: `HelloWorld` error: type `你_ӟ` should have an upper camel case name - --> $DIR/lint-nonstandard-style-unicode-1.rs:43:8 + --> $DIR/lint-nonstandard-style-unicode-1.rs:42:8 | LL | struct 你_ӟ; | ^^^^ help: convert the identifier to upper camel case: `你Ӟ` diff --git a/src/test/ui/lint/lint-nonstandard-style-unicode-2.rs b/src/test/ui/lint/lint-nonstandard-style-unicode-2.rs index 0b52a5fde35dc..9690be5908b73 100644 --- a/src/test/ui/lint/lint-nonstandard-style-unicode-2.rs +++ b/src/test/ui/lint/lint-nonstandard-style-unicode-2.rs @@ -1,7 +1,6 @@ #![allow(dead_code)] #![forbid(non_snake_case)] -#![feature(non_ascii_idents)] // Some scripts (e.g., hiragana) don't have a concept of // upper/lowercase diff --git a/src/test/ui/lint/lint-nonstandard-style-unicode-2.stderr b/src/test/ui/lint/lint-nonstandard-style-unicode-2.stderr index 0b309e315a411..8eb0654e0a193 100644 --- a/src/test/ui/lint/lint-nonstandard-style-unicode-2.stderr +++ b/src/test/ui/lint/lint-nonstandard-style-unicode-2.stderr @@ -1,5 +1,5 @@ error: function `Ц` should have a snake case name - --> $DIR/lint-nonstandard-style-unicode-2.rs:18:4 + --> $DIR/lint-nonstandard-style-unicode-2.rs:17:4 | LL | fn Ц() {} | ^ help: convert the identifier to snake case: `ц` @@ -11,7 +11,7 @@ LL | #![forbid(non_snake_case)] | ^^^^^^^^^^^^^^ error: function `分__隔` should have a snake case name - --> $DIR/lint-nonstandard-style-unicode-2.rs:23:4 + --> $DIR/lint-nonstandard-style-unicode-2.rs:22:4 | LL | fn 分__隔() {} | ^^^^^^ help: convert the identifier to snake case: `分_隔` diff --git a/src/test/ui/lint/lint-nonstandard-style-unicode-3.rs b/src/test/ui/lint/lint-nonstandard-style-unicode-3.rs index b17c2de39a0c0..9175be7a0f49d 100644 --- a/src/test/ui/lint/lint-nonstandard-style-unicode-3.rs +++ b/src/test/ui/lint/lint-nonstandard-style-unicode-3.rs @@ -1,7 +1,6 @@ #![allow(dead_code)] #![forbid(non_upper_case_globals)] -#![feature(non_ascii_idents)] // Some scripts (e.g., hiragana) don't have a concept of // upper/lowercase diff --git a/src/test/ui/lint/lint-nonstandard-style-unicode-3.stderr b/src/test/ui/lint/lint-nonstandard-style-unicode-3.stderr index 44bd5ad55ff5c..970e6b838ada0 100644 --- a/src/test/ui/lint/lint-nonstandard-style-unicode-3.stderr +++ b/src/test/ui/lint/lint-nonstandard-style-unicode-3.stderr @@ -1,5 +1,5 @@ error: static variable `τεχ` should have an upper case name - --> $DIR/lint-nonstandard-style-unicode-3.rs:18:8 + --> $DIR/lint-nonstandard-style-unicode-3.rs:17:8 | LL | static τεχ: f32 = 3.14159265; | ^^^ help: convert the identifier to upper case: `ΤΕΧ` diff --git a/src/test/ui/lint/lint-stability-deprecated.rs b/src/test/ui/lint/lint-stability-deprecated.rs index a6fde11495c5a..5bdddf714186c 100644 --- a/src/test/ui/lint/lint-stability-deprecated.rs +++ b/src/test/ui/lint/lint-stability-deprecated.rs @@ -3,7 +3,6 @@ // aux-build:inherited_stability.rs // aux-build:stability_cfg1.rs // aux-build:stability-cfg2.rs -// ignore-tidy-linelength #![warn(deprecated)] #![feature(staged_api, unstable_test_feature)] diff --git a/src/test/ui/lint/lint-stability-deprecated.stderr b/src/test/ui/lint/lint-stability-deprecated.stderr index d8dd83b0d06bd..47dc8e4a63c07 100644 --- a/src/test/ui/lint/lint-stability-deprecated.stderr +++ b/src/test/ui/lint/lint-stability-deprecated.stderr @@ -1,653 +1,653 @@ warning: use of deprecated function `lint_stability::deprecated`: text - --> $DIR/lint-stability-deprecated.rs:25:9 + --> $DIR/lint-stability-deprecated.rs:24:9 | LL | deprecated(); | ^^^^^^^^^^ | note: the lint level is defined here - --> $DIR/lint-stability-deprecated.rs:7:9 + --> $DIR/lint-stability-deprecated.rs:6:9 | LL | #![warn(deprecated)] | ^^^^^^^^^^ warning: use of deprecated associated function `lint_stability::Trait::trait_deprecated`: text - --> $DIR/lint-stability-deprecated.rs:30:9 + --> $DIR/lint-stability-deprecated.rs:29:9 | LL | Trait::trait_deprecated(&foo); | ^^^^^^^^^^^^^^^^^^^^^^^ warning: use of deprecated associated function `lint_stability::Trait::trait_deprecated`: text - --> $DIR/lint-stability-deprecated.rs:32:9 + --> $DIR/lint-stability-deprecated.rs:31:9 | LL | <Foo as Trait>::trait_deprecated(&foo); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ warning: use of deprecated function `lint_stability::deprecated_text`: text - --> $DIR/lint-stability-deprecated.rs:34:9 + --> $DIR/lint-stability-deprecated.rs:33:9 | LL | deprecated_text(); | ^^^^^^^^^^^^^^^ warning: use of deprecated associated function `lint_stability::Trait::trait_deprecated_text`: text - --> $DIR/lint-stability-deprecated.rs:39:9 + --> $DIR/lint-stability-deprecated.rs:38:9 | LL | ... Trait::trait_deprecated_text(&foo); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ warning: use of deprecated associated function `lint_stability::Trait::trait_deprecated_text`: text - --> $DIR/lint-stability-deprecated.rs:41:9 + --> $DIR/lint-stability-deprecated.rs:40:9 | LL | ... <Foo as Trait>::trait_deprecated_text(&foo); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ warning: use of deprecated function `lint_stability::deprecated_unstable`: text - --> $DIR/lint-stability-deprecated.rs:43:9 + --> $DIR/lint-stability-deprecated.rs:42:9 | LL | deprecated_unstable(); | ^^^^^^^^^^^^^^^^^^^ warning: use of deprecated associated function `lint_stability::Trait::trait_deprecated_unstable`: text - --> $DIR/lint-stability-deprecated.rs:48:9 + --> $DIR/lint-stability-deprecated.rs:47:9 | LL | ... Trait::trait_deprecated_unstable(&foo); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ warning: use of deprecated associated function `lint_stability::Trait::trait_deprecated_unstable`: text - --> $DIR/lint-stability-deprecated.rs:50:9 + --> $DIR/lint-stability-deprecated.rs:49:9 | LL | ... <Foo as Trait>::trait_deprecated_unstable(&foo); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ warning: use of deprecated function `lint_stability::deprecated_unstable_text`: text - --> $DIR/lint-stability-deprecated.rs:52:9 + --> $DIR/lint-stability-deprecated.rs:51:9 | LL | deprecated_unstable_text(); | ^^^^^^^^^^^^^^^^^^^^^^^^ warning: use of deprecated associated function `lint_stability::Trait::trait_deprecated_unstable_text`: text - --> $DIR/lint-stability-deprecated.rs:57:9 + --> $DIR/lint-stability-deprecated.rs:56:9 | LL | ... Trait::trait_deprecated_unstable_text(&foo); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ warning: use of deprecated associated function `lint_stability::Trait::trait_deprecated_unstable_text`: text - --> $DIR/lint-stability-deprecated.rs:59:9 + --> $DIR/lint-stability-deprecated.rs:58:9 | LL | ... <Foo as Trait>::trait_deprecated_unstable_text(&foo); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ warning: use of deprecated struct `lint_stability::DeprecatedStruct`: text - --> $DIR/lint-stability-deprecated.rs:109:17 + --> $DIR/lint-stability-deprecated.rs:108:17 | LL | let _ = DeprecatedStruct { | ^^^^^^^^^^^^^^^^ warning: use of deprecated struct `lint_stability::DeprecatedUnstableStruct`: text - --> $DIR/lint-stability-deprecated.rs:112:17 + --> $DIR/lint-stability-deprecated.rs:111:17 | LL | let _ = DeprecatedUnstableStruct { | ^^^^^^^^^^^^^^^^^^^^^^^^ warning: use of deprecated struct `lint_stability::DeprecatedUnitStruct`: text - --> $DIR/lint-stability-deprecated.rs:119:17 + --> $DIR/lint-stability-deprecated.rs:118:17 | LL | let _ = DeprecatedUnitStruct; | ^^^^^^^^^^^^^^^^^^^^ warning: use of deprecated struct `lint_stability::DeprecatedUnstableUnitStruct`: text - --> $DIR/lint-stability-deprecated.rs:120:17 + --> $DIR/lint-stability-deprecated.rs:119:17 | LL | let _ = DeprecatedUnstableUnitStruct; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ warning: use of deprecated variant `lint_stability::Enum::DeprecatedVariant`: text - --> $DIR/lint-stability-deprecated.rs:124:17 + --> $DIR/lint-stability-deprecated.rs:123:17 | LL | let _ = Enum::DeprecatedVariant; | ^^^^^^^^^^^^^^^^^^^^^^^ warning: use of deprecated variant `lint_stability::Enum::DeprecatedUnstableVariant`: text - --> $DIR/lint-stability-deprecated.rs:125:17 + --> $DIR/lint-stability-deprecated.rs:124:17 | LL | let _ = Enum::DeprecatedUnstableVariant; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ warning: use of deprecated struct `lint_stability::DeprecatedTupleStruct`: text - --> $DIR/lint-stability-deprecated.rs:129:17 + --> $DIR/lint-stability-deprecated.rs:128:17 | LL | let _ = DeprecatedTupleStruct (1); | ^^^^^^^^^^^^^^^^^^^^^ warning: use of deprecated struct `lint_stability::DeprecatedUnstableTupleStruct`: text - --> $DIR/lint-stability-deprecated.rs:130:17 + --> $DIR/lint-stability-deprecated.rs:129:17 | LL | let _ = DeprecatedUnstableTupleStruct (1); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ warning: use of deprecated function `lint_stability::deprecated_text`: text - --> $DIR/lint-stability-deprecated.rs:139:25 + --> $DIR/lint-stability-deprecated.rs:138:25 | LL | macro_test_arg!(deprecated_text()); | ^^^^^^^^^^^^^^^ warning: use of deprecated function `lint_stability::deprecated_unstable_text`: text - --> $DIR/lint-stability-deprecated.rs:140:25 + --> $DIR/lint-stability-deprecated.rs:139:25 | LL | macro_test_arg!(deprecated_unstable_text()); | ^^^^^^^^^^^^^^^^^^^^^^^^ warning: use of deprecated function `lint_stability::deprecated_text`: text - --> $DIR/lint-stability-deprecated.rs:141:41 + --> $DIR/lint-stability-deprecated.rs:140:41 | LL | macro_test_arg!(macro_test_arg!(deprecated_text())); | ^^^^^^^^^^^^^^^ warning: use of deprecated associated function `lint_stability::Trait::trait_deprecated`: text - --> $DIR/lint-stability-deprecated.rs:146:9 + --> $DIR/lint-stability-deprecated.rs:145:9 | LL | Trait::trait_deprecated(&foo); | ^^^^^^^^^^^^^^^^^^^^^^^ warning: use of deprecated associated function `lint_stability::Trait::trait_deprecated`: text - --> $DIR/lint-stability-deprecated.rs:148:9 + --> $DIR/lint-stability-deprecated.rs:147:9 | LL | <Foo as Trait>::trait_deprecated(&foo); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ warning: use of deprecated associated function `lint_stability::Trait::trait_deprecated_text`: text - --> $DIR/lint-stability-deprecated.rs:150:9 + --> $DIR/lint-stability-deprecated.rs:149:9 | LL | ... Trait::trait_deprecated_text(&foo); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ warning: use of deprecated associated function `lint_stability::Trait::trait_deprecated_text`: text - --> $DIR/lint-stability-deprecated.rs:152:9 + --> $DIR/lint-stability-deprecated.rs:151:9 | LL | ... <Foo as Trait>::trait_deprecated_text(&foo); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ warning: use of deprecated associated function `lint_stability::Trait::trait_deprecated_unstable`: text - --> $DIR/lint-stability-deprecated.rs:154:9 + --> $DIR/lint-stability-deprecated.rs:153:9 | LL | ... Trait::trait_deprecated_unstable(&foo); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ warning: use of deprecated associated function `lint_stability::Trait::trait_deprecated_unstable`: text - --> $DIR/lint-stability-deprecated.rs:156:9 + --> $DIR/lint-stability-deprecated.rs:155:9 | LL | ... <Foo as Trait>::trait_deprecated_unstable(&foo); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ warning: use of deprecated associated function `lint_stability::Trait::trait_deprecated_unstable_text`: text - --> $DIR/lint-stability-deprecated.rs:158:9 + --> $DIR/lint-stability-deprecated.rs:157:9 | LL | ... Trait::trait_deprecated_unstable_text(&foo); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ warning: use of deprecated associated function `lint_stability::Trait::trait_deprecated_unstable_text`: text - --> $DIR/lint-stability-deprecated.rs:160:9 + --> $DIR/lint-stability-deprecated.rs:159:9 | LL | ... <Foo as Trait>::trait_deprecated_unstable_text(&foo); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ warning: use of deprecated trait `lint_stability::DeprecatedTrait`: text - --> $DIR/lint-stability-deprecated.rs:188:10 + --> $DIR/lint-stability-deprecated.rs:187:10 | LL | impl DeprecatedTrait for S {} | ^^^^^^^^^^^^^^^ warning: use of deprecated trait `lint_stability::DeprecatedTrait`: text - --> $DIR/lint-stability-deprecated.rs:190:25 + --> $DIR/lint-stability-deprecated.rs:189:25 | LL | trait LocalTrait2 : DeprecatedTrait { } | ^^^^^^^^^^^^^^^ warning: use of deprecated function `inheritance::inherited_stability::unstable_mod::deprecated`: text - --> $DIR/lint-stability-deprecated.rs:209:9 + --> $DIR/lint-stability-deprecated.rs:208:9 | LL | unstable_mod::deprecated(); | ^^^^^^^^^^^^^^^^^^^^^^^^ warning: use of deprecated function `this_crate::deprecated`: text - --> $DIR/lint-stability-deprecated.rs:331:9 + --> $DIR/lint-stability-deprecated.rs:330:9 | LL | deprecated(); | ^^^^^^^^^^ warning: use of deprecated associated function `this_crate::Trait::trait_deprecated`: text - --> $DIR/lint-stability-deprecated.rs:336:9 + --> $DIR/lint-stability-deprecated.rs:335:9 | LL | Trait::trait_deprecated(&foo); | ^^^^^^^^^^^^^^^^^^^^^^^ warning: use of deprecated associated function `this_crate::Trait::trait_deprecated`: text - --> $DIR/lint-stability-deprecated.rs:338:9 + --> $DIR/lint-stability-deprecated.rs:337:9 | LL | <Foo as Trait>::trait_deprecated(&foo); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ warning: use of deprecated function `this_crate::deprecated_text`: text - --> $DIR/lint-stability-deprecated.rs:340:9 + --> $DIR/lint-stability-deprecated.rs:339:9 | LL | deprecated_text(); | ^^^^^^^^^^^^^^^ warning: use of deprecated associated function `this_crate::Trait::trait_deprecated_text`: text - --> $DIR/lint-stability-deprecated.rs:345:9 + --> $DIR/lint-stability-deprecated.rs:344:9 | LL | Trait::trait_deprecated_text(&foo); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ warning: use of deprecated associated function `this_crate::Trait::trait_deprecated_text`: text - --> $DIR/lint-stability-deprecated.rs:347:9 + --> $DIR/lint-stability-deprecated.rs:346:9 | LL | ... <Foo as Trait>::trait_deprecated_text(&foo); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ warning: use of deprecated struct `this_crate::DeprecatedStruct`: text - --> $DIR/lint-stability-deprecated.rs:385:17 + --> $DIR/lint-stability-deprecated.rs:384:17 | LL | let _ = DeprecatedStruct { | ^^^^^^^^^^^^^^^^ warning: use of deprecated unit struct `this_crate::DeprecatedUnitStruct`: text - --> $DIR/lint-stability-deprecated.rs:392:17 + --> $DIR/lint-stability-deprecated.rs:391:17 | LL | let _ = DeprecatedUnitStruct; | ^^^^^^^^^^^^^^^^^^^^ warning: use of deprecated unit variant `this_crate::Enum::DeprecatedVariant`: text - --> $DIR/lint-stability-deprecated.rs:396:17 + --> $DIR/lint-stability-deprecated.rs:395:17 | LL | let _ = Enum::DeprecatedVariant; | ^^^^^^^^^^^^^^^^^^^^^^^ warning: use of deprecated tuple struct `this_crate::DeprecatedTupleStruct`: text - --> $DIR/lint-stability-deprecated.rs:400:17 + --> $DIR/lint-stability-deprecated.rs:399:17 | LL | let _ = DeprecatedTupleStruct (1); | ^^^^^^^^^^^^^^^^^^^^^ warning: use of deprecated associated function `this_crate::Trait::trait_deprecated`: text - --> $DIR/lint-stability-deprecated.rs:407:9 + --> $DIR/lint-stability-deprecated.rs:406:9 | LL | Trait::trait_deprecated(&foo); | ^^^^^^^^^^^^^^^^^^^^^^^ warning: use of deprecated associated function `this_crate::Trait::trait_deprecated`: text - --> $DIR/lint-stability-deprecated.rs:409:9 + --> $DIR/lint-stability-deprecated.rs:408:9 | LL | <Foo as Trait>::trait_deprecated(&foo); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ warning: use of deprecated associated function `this_crate::Trait::trait_deprecated_text`: text - --> $DIR/lint-stability-deprecated.rs:411:9 + --> $DIR/lint-stability-deprecated.rs:410:9 | LL | Trait::trait_deprecated_text(&foo); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ warning: use of deprecated associated function `this_crate::Trait::trait_deprecated_text`: text - --> $DIR/lint-stability-deprecated.rs:413:9 + --> $DIR/lint-stability-deprecated.rs:412:9 | LL | ... <Foo as Trait>::trait_deprecated_text(&foo); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ warning: use of deprecated function `this_crate::test_fn_body::fn_in_body`: text - --> $DIR/lint-stability-deprecated.rs:440:9 + --> $DIR/lint-stability-deprecated.rs:439:9 | LL | fn_in_body(); | ^^^^^^^^^^ warning: use of deprecated trait `this_crate::DeprecatedTrait`: text - --> $DIR/lint-stability-deprecated.rs:460:10 + --> $DIR/lint-stability-deprecated.rs:459:10 | LL | impl DeprecatedTrait for S { } | ^^^^^^^^^^^^^^^ warning: use of deprecated trait `this_crate::DeprecatedTrait`: text - --> $DIR/lint-stability-deprecated.rs:462:24 + --> $DIR/lint-stability-deprecated.rs:461:24 | LL | trait LocalTrait : DeprecatedTrait { } | ^^^^^^^^^^^^^^^ warning: use of deprecated function `this_crate::MethodTester::test_method_body::fn_in_body`: text - --> $DIR/lint-stability-deprecated.rs:448:13 + --> $DIR/lint-stability-deprecated.rs:447:13 | LL | fn_in_body(); | ^^^^^^^^^^ warning: use of deprecated associated type `lint_stability::TraitWithAssociatedTypes::TypeDeprecated`: text - --> $DIR/lint-stability-deprecated.rs:98:48 + --> $DIR/lint-stability-deprecated.rs:97:48 | LL | struct S2<T: TraitWithAssociatedTypes>(T::TypeDeprecated); | ^^^^^^^^^^^^^^^^^ warning: use of deprecated associated type `lint_stability::TraitWithAssociatedTypes::TypeDeprecated`: text - --> $DIR/lint-stability-deprecated.rs:103:13 + --> $DIR/lint-stability-deprecated.rs:102:13 | LL | TypeDeprecated = u16, | ^^^^^^^^^^^^^^^^^^^^ warning: use of deprecated associated function `lint_stability::MethodTester::method_deprecated`: text - --> $DIR/lint-stability-deprecated.rs:26:13 + --> $DIR/lint-stability-deprecated.rs:25:13 | LL | foo.method_deprecated(); | ^^^^^^^^^^^^^^^^^ warning: use of deprecated associated function `lint_stability::MethodTester::method_deprecated`: text - --> $DIR/lint-stability-deprecated.rs:27:9 + --> $DIR/lint-stability-deprecated.rs:26:9 | LL | Foo::method_deprecated(&foo); | ^^^^^^^^^^^^^^^^^^^^^^ warning: use of deprecated associated function `lint_stability::MethodTester::method_deprecated`: text - --> $DIR/lint-stability-deprecated.rs:28:9 + --> $DIR/lint-stability-deprecated.rs:27:9 | LL | <Foo>::method_deprecated(&foo); | ^^^^^^^^^^^^^^^^^^^^^^^^ warning: use of deprecated associated function `lint_stability::Trait::trait_deprecated`: text - --> $DIR/lint-stability-deprecated.rs:29:13 + --> $DIR/lint-stability-deprecated.rs:28:13 | LL | foo.trait_deprecated(); | ^^^^^^^^^^^^^^^^ warning: use of deprecated associated function `lint_stability::Trait::trait_deprecated`: text - --> $DIR/lint-stability-deprecated.rs:31:9 + --> $DIR/lint-stability-deprecated.rs:30:9 | LL | <Foo>::trait_deprecated(&foo); | ^^^^^^^^^^^^^^^^^^^^^^^ warning: use of deprecated associated function `lint_stability::MethodTester::method_deprecated_text`: text - --> $DIR/lint-stability-deprecated.rs:35:13 + --> $DIR/lint-stability-deprecated.rs:34:13 | LL | ... foo.method_deprecated_text(); | ^^^^^^^^^^^^^^^^^^^^^^ warning: use of deprecated associated function `lint_stability::MethodTester::method_deprecated_text`: text - --> $DIR/lint-stability-deprecated.rs:36:9 + --> $DIR/lint-stability-deprecated.rs:35:9 | LL | ... Foo::method_deprecated_text(&foo); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ warning: use of deprecated associated function `lint_stability::MethodTester::method_deprecated_text`: text - --> $DIR/lint-stability-deprecated.rs:37:9 + --> $DIR/lint-stability-deprecated.rs:36:9 | LL | ... <Foo>::method_deprecated_text(&foo); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ warning: use of deprecated associated function `lint_stability::Trait::trait_deprecated_text`: text - --> $DIR/lint-stability-deprecated.rs:38:13 + --> $DIR/lint-stability-deprecated.rs:37:13 | LL | foo.trait_deprecated_text(); | ^^^^^^^^^^^^^^^^^^^^^ warning: use of deprecated associated function `lint_stability::Trait::trait_deprecated_text`: text - --> $DIR/lint-stability-deprecated.rs:40:9 + --> $DIR/lint-stability-deprecated.rs:39:9 | LL | ... <Foo>::trait_deprecated_text(&foo); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ warning: use of deprecated associated function `lint_stability::MethodTester::method_deprecated_unstable`: text - --> $DIR/lint-stability-deprecated.rs:44:13 + --> $DIR/lint-stability-deprecated.rs:43:13 | LL | ... foo.method_deprecated_unstable(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ warning: use of deprecated associated function `lint_stability::MethodTester::method_deprecated_unstable`: text - --> $DIR/lint-stability-deprecated.rs:45:9 + --> $DIR/lint-stability-deprecated.rs:44:9 | LL | ... Foo::method_deprecated_unstable(&foo); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ warning: use of deprecated associated function `lint_stability::MethodTester::method_deprecated_unstable`: text - --> $DIR/lint-stability-deprecated.rs:46:9 + --> $DIR/lint-stability-deprecated.rs:45:9 | LL | ... <Foo>::method_deprecated_unstable(&foo); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ warning: use of deprecated associated function `lint_stability::Trait::trait_deprecated_unstable`: text - --> $DIR/lint-stability-deprecated.rs:47:13 + --> $DIR/lint-stability-deprecated.rs:46:13 | LL | foo.trait_deprecated_unstable(); | ^^^^^^^^^^^^^^^^^^^^^^^^^ warning: use of deprecated associated function `lint_stability::Trait::trait_deprecated_unstable`: text - --> $DIR/lint-stability-deprecated.rs:49:9 + --> $DIR/lint-stability-deprecated.rs:48:9 | LL | ... <Foo>::trait_deprecated_unstable(&foo); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ warning: use of deprecated associated function `lint_stability::MethodTester::method_deprecated_unstable_text`: text - --> $DIR/lint-stability-deprecated.rs:53:13 + --> $DIR/lint-stability-deprecated.rs:52:13 | LL | ... foo.method_deprecated_unstable_text(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ warning: use of deprecated associated function `lint_stability::MethodTester::method_deprecated_unstable_text`: text - --> $DIR/lint-stability-deprecated.rs:54:9 + --> $DIR/lint-stability-deprecated.rs:53:9 | LL | ... Foo::method_deprecated_unstable_text(&foo); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ warning: use of deprecated associated function `lint_stability::MethodTester::method_deprecated_unstable_text`: text - --> $DIR/lint-stability-deprecated.rs:55:9 + --> $DIR/lint-stability-deprecated.rs:54:9 | LL | ... <Foo>::method_deprecated_unstable_text(&foo); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ warning: use of deprecated associated function `lint_stability::Trait::trait_deprecated_unstable_text`: text - --> $DIR/lint-stability-deprecated.rs:56:13 + --> $DIR/lint-stability-deprecated.rs:55:13 | LL | ... foo.trait_deprecated_unstable_text(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ warning: use of deprecated associated function `lint_stability::Trait::trait_deprecated_unstable_text`: text - --> $DIR/lint-stability-deprecated.rs:58:9 + --> $DIR/lint-stability-deprecated.rs:57:9 | LL | ... <Foo>::trait_deprecated_unstable_text(&foo); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ warning: use of deprecated field `lint_stability::DeprecatedStruct::i`: text - --> $DIR/lint-stability-deprecated.rs:110:13 + --> $DIR/lint-stability-deprecated.rs:109:13 | LL | i: 0 | ^^^^ warning: use of deprecated field `lint_stability::DeprecatedUnstableStruct::i`: text - --> $DIR/lint-stability-deprecated.rs:114:13 + --> $DIR/lint-stability-deprecated.rs:113:13 | LL | i: 0 | ^^^^ warning: use of deprecated associated function `lint_stability::Trait::trait_deprecated`: text - --> $DIR/lint-stability-deprecated.rs:145:13 + --> $DIR/lint-stability-deprecated.rs:144:13 | LL | foo.trait_deprecated(); | ^^^^^^^^^^^^^^^^ warning: use of deprecated associated function `lint_stability::Trait::trait_deprecated`: text - --> $DIR/lint-stability-deprecated.rs:147:9 + --> $DIR/lint-stability-deprecated.rs:146:9 | LL | <Foo>::trait_deprecated(&foo); | ^^^^^^^^^^^^^^^^^^^^^^^ warning: use of deprecated associated function `lint_stability::Trait::trait_deprecated_text`: text - --> $DIR/lint-stability-deprecated.rs:149:13 + --> $DIR/lint-stability-deprecated.rs:148:13 | LL | foo.trait_deprecated_text(); | ^^^^^^^^^^^^^^^^^^^^^ warning: use of deprecated associated function `lint_stability::Trait::trait_deprecated_text`: text - --> $DIR/lint-stability-deprecated.rs:151:9 + --> $DIR/lint-stability-deprecated.rs:150:9 | LL | ... <Foo>::trait_deprecated_text(&foo); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ warning: use of deprecated associated function `lint_stability::Trait::trait_deprecated_unstable`: text - --> $DIR/lint-stability-deprecated.rs:153:13 + --> $DIR/lint-stability-deprecated.rs:152:13 | LL | foo.trait_deprecated_unstable(); | ^^^^^^^^^^^^^^^^^^^^^^^^^ warning: use of deprecated associated function `lint_stability::Trait::trait_deprecated_unstable`: text - --> $DIR/lint-stability-deprecated.rs:155:9 + --> $DIR/lint-stability-deprecated.rs:154:9 | LL | ... <Foo>::trait_deprecated_unstable(&foo); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ warning: use of deprecated associated function `lint_stability::Trait::trait_deprecated_unstable_text`: text - --> $DIR/lint-stability-deprecated.rs:157:13 + --> $DIR/lint-stability-deprecated.rs:156:13 | LL | ... foo.trait_deprecated_unstable_text(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ warning: use of deprecated associated function `lint_stability::Trait::trait_deprecated_unstable_text`: text - --> $DIR/lint-stability-deprecated.rs:159:9 + --> $DIR/lint-stability-deprecated.rs:158:9 | LL | ... <Foo>::trait_deprecated_unstable_text(&foo); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ warning: use of deprecated associated function `lint_stability::Trait::trait_deprecated`: text - --> $DIR/lint-stability-deprecated.rs:176:13 + --> $DIR/lint-stability-deprecated.rs:175:13 | LL | foo.trait_deprecated(); | ^^^^^^^^^^^^^^^^ warning: use of deprecated associated function `lint_stability::Trait::trait_deprecated_text`: text - --> $DIR/lint-stability-deprecated.rs:177:13 + --> $DIR/lint-stability-deprecated.rs:176:13 | LL | foo.trait_deprecated_text(); | ^^^^^^^^^^^^^^^^^^^^^ warning: use of deprecated associated function `lint_stability::Trait::trait_deprecated_unstable`: text - --> $DIR/lint-stability-deprecated.rs:178:13 + --> $DIR/lint-stability-deprecated.rs:177:13 | LL | foo.trait_deprecated_unstable(); | ^^^^^^^^^^^^^^^^^^^^^^^^^ warning: use of deprecated associated function `lint_stability::Trait::trait_deprecated_unstable_text`: text - --> $DIR/lint-stability-deprecated.rs:179:13 + --> $DIR/lint-stability-deprecated.rs:178:13 | LL | ... foo.trait_deprecated_unstable_text(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ warning: use of deprecated associated function `this_crate::MethodTester::method_deprecated`: text - --> $DIR/lint-stability-deprecated.rs:332:13 + --> $DIR/lint-stability-deprecated.rs:331:13 | LL | foo.method_deprecated(); | ^^^^^^^^^^^^^^^^^ warning: use of deprecated associated function `this_crate::MethodTester::method_deprecated`: text - --> $DIR/lint-stability-deprecated.rs:333:9 + --> $DIR/lint-stability-deprecated.rs:332:9 | LL | Foo::method_deprecated(&foo); | ^^^^^^^^^^^^^^^^^^^^^^ warning: use of deprecated associated function `this_crate::MethodTester::method_deprecated`: text - --> $DIR/lint-stability-deprecated.rs:334:9 + --> $DIR/lint-stability-deprecated.rs:333:9 | LL | <Foo>::method_deprecated(&foo); | ^^^^^^^^^^^^^^^^^^^^^^^^ warning: use of deprecated associated function `this_crate::Trait::trait_deprecated`: text - --> $DIR/lint-stability-deprecated.rs:335:13 + --> $DIR/lint-stability-deprecated.rs:334:13 | LL | foo.trait_deprecated(); | ^^^^^^^^^^^^^^^^ warning: use of deprecated associated function `this_crate::Trait::trait_deprecated`: text - --> $DIR/lint-stability-deprecated.rs:337:9 + --> $DIR/lint-stability-deprecated.rs:336:9 | LL | <Foo>::trait_deprecated(&foo); | ^^^^^^^^^^^^^^^^^^^^^^^ warning: use of deprecated associated function `this_crate::MethodTester::method_deprecated_text`: text - --> $DIR/lint-stability-deprecated.rs:341:13 + --> $DIR/lint-stability-deprecated.rs:340:13 | LL | ... foo.method_deprecated_text(); | ^^^^^^^^^^^^^^^^^^^^^^ warning: use of deprecated associated function `this_crate::MethodTester::method_deprecated_text`: text - --> $DIR/lint-stability-deprecated.rs:342:9 + --> $DIR/lint-stability-deprecated.rs:341:9 | LL | ... Foo::method_deprecated_text(&foo); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ warning: use of deprecated associated function `this_crate::MethodTester::method_deprecated_text`: text - --> $DIR/lint-stability-deprecated.rs:343:9 + --> $DIR/lint-stability-deprecated.rs:342:9 | LL | ... <Foo>::method_deprecated_text(&foo); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ warning: use of deprecated associated function `this_crate::Trait::trait_deprecated_text`: text - --> $DIR/lint-stability-deprecated.rs:344:13 + --> $DIR/lint-stability-deprecated.rs:343:13 | LL | foo.trait_deprecated_text(); | ^^^^^^^^^^^^^^^^^^^^^ warning: use of deprecated associated function `this_crate::Trait::trait_deprecated_text`: text - --> $DIR/lint-stability-deprecated.rs:346:9 + --> $DIR/lint-stability-deprecated.rs:345:9 | LL | <Foo>::trait_deprecated_text(&foo); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ warning: use of deprecated field `this_crate::DeprecatedStruct::i`: text - --> $DIR/lint-stability-deprecated.rs:387:13 + --> $DIR/lint-stability-deprecated.rs:386:13 | LL | i: 0 | ^^^^ warning: use of deprecated associated function `this_crate::Trait::trait_deprecated`: text - --> $DIR/lint-stability-deprecated.rs:406:13 + --> $DIR/lint-stability-deprecated.rs:405:13 | LL | foo.trait_deprecated(); | ^^^^^^^^^^^^^^^^ warning: use of deprecated associated function `this_crate::Trait::trait_deprecated`: text - --> $DIR/lint-stability-deprecated.rs:408:9 + --> $DIR/lint-stability-deprecated.rs:407:9 | LL | <Foo>::trait_deprecated(&foo); | ^^^^^^^^^^^^^^^^^^^^^^^ warning: use of deprecated associated function `this_crate::Trait::trait_deprecated_text`: text - --> $DIR/lint-stability-deprecated.rs:410:13 + --> $DIR/lint-stability-deprecated.rs:409:13 | LL | foo.trait_deprecated_text(); | ^^^^^^^^^^^^^^^^^^^^^ warning: use of deprecated associated function `this_crate::Trait::trait_deprecated_text`: text - --> $DIR/lint-stability-deprecated.rs:412:9 + --> $DIR/lint-stability-deprecated.rs:411:9 | LL | <Foo>::trait_deprecated_text(&foo); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ warning: use of deprecated associated function `this_crate::Trait::trait_deprecated`: text - --> $DIR/lint-stability-deprecated.rs:429:13 + --> $DIR/lint-stability-deprecated.rs:428:13 | LL | foo.trait_deprecated(); | ^^^^^^^^^^^^^^^^ warning: use of deprecated associated function `this_crate::Trait::trait_deprecated_text`: text - --> $DIR/lint-stability-deprecated.rs:430:13 + --> $DIR/lint-stability-deprecated.rs:429:13 | LL | foo.trait_deprecated_text(); | ^^^^^^^^^^^^^^^^^^^^^ warning: use of deprecated associated type `lint_stability::TraitWithAssociatedTypes::TypeDeprecated`: text - --> $DIR/lint-stability-deprecated.rs:98:48 + --> $DIR/lint-stability-deprecated.rs:97:48 | LL | struct S2<T: TraitWithAssociatedTypes>(T::TypeDeprecated); | ^^^^^^^^^^^^^^^^^ warning: use of deprecated associated type `lint_stability::TraitWithAssociatedTypes::TypeDeprecated`: text - --> $DIR/lint-stability-deprecated.rs:103:13 + --> $DIR/lint-stability-deprecated.rs:102:13 | LL | TypeDeprecated = u16, | ^^^^^^^^^^^^^^^^^^^^ warning: use of deprecated associated type `lint_stability::TraitWithAssociatedTypes::TypeDeprecated`: text - --> $DIR/lint-stability-deprecated.rs:103:13 + --> $DIR/lint-stability-deprecated.rs:102:13 | LL | TypeDeprecated = u16, | ^^^^^^^^^^^^^^^^^^^^ diff --git a/src/test/ui/lint/noop-method-call.rs b/src/test/ui/lint/noop-method-call.rs new file mode 100644 index 0000000000000..9870c813572e3 --- /dev/null +++ b/src/test/ui/lint/noop-method-call.rs @@ -0,0 +1,54 @@ +// check-pass + +#![allow(unused)] +#![warn(noop_method_call)] + +use std::borrow::Borrow; +use std::ops::Deref; + +struct PlainType<T>(T); + +#[derive(Clone)] +struct CloneType<T>(T); + +fn main() { + let non_clone_type_ref = &PlainType(1u32); + let non_clone_type_ref_clone: &PlainType<u32> = non_clone_type_ref.clone(); + //~^ WARNING call to `.clone()` on a reference in this situation does nothing + + let clone_type_ref = &CloneType(1u32); + let clone_type_ref_clone: CloneType<u32> = clone_type_ref.clone(); + + // Calling clone on a double reference doesn't warn since the method call itself + // peels the outer reference off + let clone_type_ref = &&CloneType(1u32); + let clone_type_ref_clone: &CloneType<u32> = clone_type_ref.clone(); + + let non_deref_type = &PlainType(1u32); + let non_deref_type_deref: &PlainType<u32> = non_deref_type.deref(); + //~^ WARNING call to `.deref()` on a reference in this situation does nothing + + // Dereferencing a &&T does not warn since it has collapsed the double reference + let non_deref_type = &&PlainType(1u32); + let non_deref_type_deref: &PlainType<u32> = non_deref_type.deref(); + + let non_borrow_type = &PlainType(1u32); + let non_borrow_type_borrow: &PlainType<u32> = non_borrow_type.borrow(); + //~^ WARNING call to `.borrow()` on a reference in this situation does nothing + + // Borrowing a &&T does not warn since it has collapsed the double reference + let non_borrow_type = &&PlainType(1u32); + let non_borrow_type_borrow: &PlainType<u32> = non_borrow_type.borrow(); + + let xs = ["a", "b", "c"]; + let _v: Vec<&str> = xs.iter().map(|x| x.clone()).collect(); // ok, but could use `*x` instead +} + +fn generic<T>(non_clone_type: &PlainType<T>) { + non_clone_type.clone(); +} + +fn non_generic(non_clone_type: &PlainType<u32>) { + non_clone_type.clone(); + //~^ WARNING call to `.clone()` on a reference in this situation does nothing +} diff --git a/src/test/ui/lint/noop-method-call.stderr b/src/test/ui/lint/noop-method-call.stderr new file mode 100644 index 0000000000000..7f6f96bf1d142 --- /dev/null +++ b/src/test/ui/lint/noop-method-call.stderr @@ -0,0 +1,39 @@ +warning: call to `.clone()` on a reference in this situation does nothing + --> $DIR/noop-method-call.rs:16:71 + | +LL | let non_clone_type_ref_clone: &PlainType<u32> = non_clone_type_ref.clone(); + | ^^^^^^^^ unnecessary method call + | +note: the lint level is defined here + --> $DIR/noop-method-call.rs:4:9 + | +LL | #![warn(noop_method_call)] + | ^^^^^^^^^^^^^^^^ + = note: the type `&PlainType<u32>` which `clone` is being called on is the same as the type returned from `clone`, so the method call does not do anything and can be removed + +warning: call to `.deref()` on a reference in this situation does nothing + --> $DIR/noop-method-call.rs:28:63 + | +LL | let non_deref_type_deref: &PlainType<u32> = non_deref_type.deref(); + | ^^^^^^^^ unnecessary method call + | + = note: the type `&PlainType<u32>` which `deref` is being called on is the same as the type returned from `deref`, so the method call does not do anything and can be removed + +warning: call to `.borrow()` on a reference in this situation does nothing + --> $DIR/noop-method-call.rs:36:66 + | +LL | let non_borrow_type_borrow: &PlainType<u32> = non_borrow_type.borrow(); + | ^^^^^^^^^ unnecessary method call + | + = note: the type `&PlainType<u32>` which `borrow` is being called on is the same as the type returned from `borrow`, so the method call does not do anything and can be removed + +warning: call to `.clone()` on a reference in this situation does nothing + --> $DIR/noop-method-call.rs:52:19 + | +LL | non_clone_type.clone(); + | ^^^^^^^^ unnecessary method call + | + = note: the type `&PlainType<u32>` which `clone` is being called on is the same as the type returned from `clone`, so the method call does not do anything and can be removed + +warning: 4 warnings emitted + diff --git a/src/test/ui/lint/opaque-ty-ffi-unsafe.full_tait.stderr b/src/test/ui/lint/opaque-ty-ffi-unsafe.full_tait.stderr new file mode 100644 index 0000000000000..5433d6e6ae241 --- /dev/null +++ b/src/test/ui/lint/opaque-ty-ffi-unsafe.full_tait.stderr @@ -0,0 +1,24 @@ +warning: the feature `type_alias_impl_trait` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/opaque-ty-ffi-unsafe.rs:3:32 + | +LL | #![cfg_attr(full_tait, feature(type_alias_impl_trait))] + | ^^^^^^^^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #63063 <https://github.com/rust-lang/rust/issues/63063> for more information + +error: `extern` block uses type `impl Fn<()>`, which is not FFI-safe + --> $DIR/opaque-ty-ffi-unsafe.rs:14:17 + | +LL | pub fn a(_: A); + | ^ not FFI-safe + | +note: the lint level is defined here + --> $DIR/opaque-ty-ffi-unsafe.rs:5:9 + | +LL | #![deny(improper_ctypes)] + | ^^^^^^^^^^^^^^^ + = note: opaque types have no C equivalent + +error: aborting due to previous error; 1 warning emitted + diff --git a/src/test/ui/lint/opaque-ty-ffi-unsafe.min_tait.stderr b/src/test/ui/lint/opaque-ty-ffi-unsafe.min_tait.stderr new file mode 100644 index 0000000000000..2f20912d2a6ea --- /dev/null +++ b/src/test/ui/lint/opaque-ty-ffi-unsafe.min_tait.stderr @@ -0,0 +1,15 @@ +error: `extern` block uses type `impl Fn<()>`, which is not FFI-safe + --> $DIR/opaque-ty-ffi-unsafe.rs:14:17 + | +LL | pub fn a(_: A); + | ^ not FFI-safe + | +note: the lint level is defined here + --> $DIR/opaque-ty-ffi-unsafe.rs:5:9 + | +LL | #![deny(improper_ctypes)] + | ^^^^^^^^^^^^^^^ + = note: opaque types have no C equivalent + +error: aborting due to previous error + diff --git a/src/test/ui/lint/opaque-ty-ffi-unsafe.rs b/src/test/ui/lint/opaque-ty-ffi-unsafe.rs index 4ceb0c3da0823..0e9df9195523a 100644 --- a/src/test/ui/lint/opaque-ty-ffi-unsafe.rs +++ b/src/test/ui/lint/opaque-ty-ffi-unsafe.rs @@ -1,4 +1,7 @@ -#![feature(type_alias_impl_trait)] +// revisions: min_tait full_tait +#![feature(min_type_alias_impl_trait)] +#![cfg_attr(full_tait, feature(type_alias_impl_trait))] +//[full_tait]~^ WARN incomplete #![deny(improper_ctypes)] type A = impl Fn(); diff --git a/src/test/ui/lint/opaque-ty-ffi-unsafe.stderr b/src/test/ui/lint/opaque-ty-ffi-unsafe.stderr deleted file mode 100644 index 9d46f6d936e25..0000000000000 --- a/src/test/ui/lint/opaque-ty-ffi-unsafe.stderr +++ /dev/null @@ -1,15 +0,0 @@ -error: `extern` block uses type `impl Fn<()>`, which is not FFI-safe - --> $DIR/opaque-ty-ffi-unsafe.rs:11:17 - | -LL | pub fn a(_: A); - | ^ not FFI-safe - | -note: the lint level is defined here - --> $DIR/opaque-ty-ffi-unsafe.rs:2:9 - | -LL | #![deny(improper_ctypes)] - | ^^^^^^^^^^^^^^^ - = note: opaque types have no C equivalent - -error: aborting due to previous error - diff --git a/src/test/ui/lint/recommend-literal.rs b/src/test/ui/lint/recommend-literal.rs new file mode 100644 index 0000000000000..f60d3d10dce47 --- /dev/null +++ b/src/test/ui/lint/recommend-literal.rs @@ -0,0 +1,35 @@ +type Real = double; +//~^ ERROR cannot find type `double` in this scope +//~| HELP perhaps you intended to use this type + +fn main() { + let x: Real = 3.5; + let y: long = 74802374902374923; + //~^ ERROR cannot find type `long` in this scope + //~| HELP perhaps you intended to use this type +} + +fn z(a: boolean) { + //~^ ERROR cannot find type `boolean` in this scope + //~| HELP perhaps you intended to use this type +} + +fn a() -> byte { +//~^ ERROR cannot find type `byte` in this scope +//~| HELP perhaps you intended to use this type + 3 +} + +struct Data { //~ HELP you might be missing a type parameter + width: float, + //~^ ERROR cannot find type `float` in this scope + //~| HELP perhaps you intended to use this type + depth: Option<int>, + //~^ ERROR cannot find type `int` in this scope + //~| HELP perhaps you intended to use this type +} + +trait Stuff {} +impl Stuff for short {} +//~^ ERROR cannot find type `short` in this scope +//~| HELP perhaps you intended to use this type diff --git a/src/test/ui/lint/recommend-literal.stderr b/src/test/ui/lint/recommend-literal.stderr new file mode 100644 index 0000000000000..b01073b42b864 --- /dev/null +++ b/src/test/ui/lint/recommend-literal.stderr @@ -0,0 +1,72 @@ +error[E0412]: cannot find type `double` in this scope + --> $DIR/recommend-literal.rs:1:13 + | +LL | type Real = double; + | ^^^^^^ + | | + | not found in this scope + | help: perhaps you intended to use this type: `f64` + +error[E0412]: cannot find type `long` in this scope + --> $DIR/recommend-literal.rs:7:12 + | +LL | let y: long = 74802374902374923; + | ^^^^ + | | + | not found in this scope + | help: perhaps you intended to use this type: `i64` + +error[E0412]: cannot find type `boolean` in this scope + --> $DIR/recommend-literal.rs:12:9 + | +LL | fn z(a: boolean) { + | ^^^^^^^ + | | + | not found in this scope + | help: perhaps you intended to use this type: `bool` + +error[E0412]: cannot find type `byte` in this scope + --> $DIR/recommend-literal.rs:17:11 + | +LL | fn a() -> byte { + | ^^^^ + | | + | not found in this scope + | help: perhaps you intended to use this type: `u8` + +error[E0412]: cannot find type `float` in this scope + --> $DIR/recommend-literal.rs:24:12 + | +LL | width: float, + | ^^^^^ + | | + | not found in this scope + | help: perhaps you intended to use this type: `f32` + +error[E0412]: cannot find type `int` in this scope + --> $DIR/recommend-literal.rs:27:19 + | +LL | depth: Option<int>, + | ^^^ not found in this scope + | +help: perhaps you intended to use this type + | +LL | depth: Option<i32>, + | ^^^ +help: you might be missing a type parameter + | +LL | struct Data<int> { + | ^^^^^ + +error[E0412]: cannot find type `short` in this scope + --> $DIR/recommend-literal.rs:33:16 + | +LL | impl Stuff for short {} + | ^^^^^ + | | + | not found in this scope + | help: perhaps you intended to use this type: `i16` + +error: aborting due to 7 previous errors + +For more information about this error, try `rustc --explain E0412`. diff --git a/src/test/ui/lint/register-tool-lint.rs b/src/test/ui/lint/register-tool-lint.rs new file mode 100644 index 0000000000000..0ba5a37b167cd --- /dev/null +++ b/src/test/ui/lint/register-tool-lint.rs @@ -0,0 +1,11 @@ +#![crate_type = "lib"] +#![feature(register_tool)] +#![register_tool(xyz)] +#![warn(xyz::my_lint)] // this should not error +#![warn(abc::my_lint)] +//~^ ERROR unknown tool name `abc` found in scoped lint +//~| HELP add `#![register_tool(abc)]` +//~| ERROR unknown tool name `abc` +//~| HELP add `#![register_tool(abc)]` +//~| ERROR unknown tool name `abc` +//~| HELP add `#![register_tool(abc)]` diff --git a/src/test/ui/lint/register-tool-lint.stderr b/src/test/ui/lint/register-tool-lint.stderr new file mode 100644 index 0000000000000..750c74cec1eb9 --- /dev/null +++ b/src/test/ui/lint/register-tool-lint.stderr @@ -0,0 +1,27 @@ +error[E0710]: unknown tool name `abc` found in scoped lint: `abc::my_lint` + --> $DIR/register-tool-lint.rs:5:9 + | +LL | #![warn(abc::my_lint)] + | ^^^ + | + = help: add `#![register_tool(abc)]` to the crate root + +error[E0710]: unknown tool name `abc` found in scoped lint: `abc::my_lint` + --> $DIR/register-tool-lint.rs:5:9 + | +LL | #![warn(abc::my_lint)] + | ^^^ + | + = help: add `#![register_tool(abc)]` to the crate root + +error[E0710]: unknown tool name `abc` found in scoped lint: `abc::my_lint` + --> $DIR/register-tool-lint.rs:5:9 + | +LL | #![warn(abc::my_lint)] + | ^^^ + | + = help: add `#![register_tool(abc)]` to the crate root + +error: aborting due to 3 previous errors + +For more information about this error, try `rustc --explain E0710`. diff --git a/src/test/ui/lint/rfc-2457-non-ascii-idents/lint-confusable-idents.rs b/src/test/ui/lint/rfc-2457-non-ascii-idents/lint-confusable-idents.rs index 2c711f994043f..e7da825ae36d1 100644 --- a/src/test/ui/lint/rfc-2457-non-ascii-idents/lint-confusable-idents.rs +++ b/src/test/ui/lint/rfc-2457-non-ascii-idents/lint-confusable-idents.rs @@ -1,4 +1,3 @@ -#![feature(non_ascii_idents)] #![deny(confusable_idents)] #![allow(uncommon_codepoints, non_upper_case_globals)] diff --git a/src/test/ui/lint/rfc-2457-non-ascii-idents/lint-confusable-idents.stderr b/src/test/ui/lint/rfc-2457-non-ascii-idents/lint-confusable-idents.stderr index b9af60963adf6..e9906c83d126c 100644 --- a/src/test/ui/lint/rfc-2457-non-ascii-idents/lint-confusable-idents.stderr +++ b/src/test/ui/lint/rfc-2457-non-ascii-idents/lint-confusable-idents.stderr @@ -1,5 +1,5 @@ error: identifier pair considered confusable between `s` and `s` - --> $DIR/lint-confusable-idents.rs:9:9 + --> $DIR/lint-confusable-idents.rs:8:9 | LL | const s: usize = 42; | -- this is where the previous identifier occurred @@ -8,13 +8,13 @@ LL | let s = "rust"; | ^ | note: the lint level is defined here - --> $DIR/lint-confusable-idents.rs:2:9 + --> $DIR/lint-confusable-idents.rs:1:9 | LL | #![deny(confusable_idents)] | ^^^^^^^^^^^^^^^^^ error: identifier pair considered confusable between `s_s` and `s_s` - --> $DIR/lint-confusable-idents.rs:10:9 + --> $DIR/lint-confusable-idents.rs:9:9 | LL | const s_s: usize = 42; | --- this is where the previous identifier occurred diff --git a/src/test/ui/lint/rfc-2457-non-ascii-idents/lint-mixed-script-confusables-2.rs b/src/test/ui/lint/rfc-2457-non-ascii-idents/lint-mixed-script-confusables-2.rs index a5b45466da5ca..f62c8a19031dc 100644 --- a/src/test/ui/lint/rfc-2457-non-ascii-idents/lint-mixed-script-confusables-2.rs +++ b/src/test/ui/lint/rfc-2457-non-ascii-idents/lint-mixed-script-confusables-2.rs @@ -1,5 +1,4 @@ // check-pass -#![feature(non_ascii_idents)] #![deny(mixed_script_confusables)] struct ΑctuallyNotLatin; diff --git a/src/test/ui/lint/rfc-2457-non-ascii-idents/lint-mixed-script-confusables.rs b/src/test/ui/lint/rfc-2457-non-ascii-idents/lint-mixed-script-confusables.rs index 4637b03f250de..7ee9c41f6a091 100644 --- a/src/test/ui/lint/rfc-2457-non-ascii-idents/lint-mixed-script-confusables.rs +++ b/src/test/ui/lint/rfc-2457-non-ascii-idents/lint-mixed-script-confusables.rs @@ -1,4 +1,3 @@ -#![feature(non_ascii_idents)] #![deny(mixed_script_confusables)] struct ΑctuallyNotLatin; diff --git a/src/test/ui/lint/rfc-2457-non-ascii-idents/lint-mixed-script-confusables.stderr b/src/test/ui/lint/rfc-2457-non-ascii-idents/lint-mixed-script-confusables.stderr index 6f75a1ece3766..4018b381fb8c5 100644 --- a/src/test/ui/lint/rfc-2457-non-ascii-idents/lint-mixed-script-confusables.stderr +++ b/src/test/ui/lint/rfc-2457-non-ascii-idents/lint-mixed-script-confusables.stderr @@ -1,11 +1,11 @@ error: The usage of Script Group `Greek` in this crate consists solely of mixed script confusables - --> $DIR/lint-mixed-script-confusables.rs:4:8 + --> $DIR/lint-mixed-script-confusables.rs:3:8 | LL | struct ΑctuallyNotLatin; | ^^^^^^^^^^^^^^^^ | note: the lint level is defined here - --> $DIR/lint-mixed-script-confusables.rs:2:9 + --> $DIR/lint-mixed-script-confusables.rs:1:9 | LL | #![deny(mixed_script_confusables)] | ^^^^^^^^^^^^^^^^^^^^^^^^ @@ -13,7 +13,7 @@ LL | #![deny(mixed_script_confusables)] = note: Please recheck to make sure their usages are indeed what you want. error: The usage of Script Group `Cyrillic` in this crate consists solely of mixed script confusables - --> $DIR/lint-mixed-script-confusables.rs:11:5 + --> $DIR/lint-mixed-script-confusables.rs:10:5 | LL | mod роре { | ^^^^ @@ -22,7 +22,7 @@ LL | mod роре { = note: Please recheck to make sure their usages are indeed what you want. error: The usage of Script Group `Japanese, Katakana` in this crate consists solely of mixed script confusables - --> $DIR/lint-mixed-script-confusables.rs:13:11 + --> $DIR/lint-mixed-script-confusables.rs:12:11 | LL | const エ: &'static str = "アイウ"; | ^^ diff --git a/src/test/ui/lint/rfc-2457-non-ascii-idents/lint-non-ascii-idents.rs b/src/test/ui/lint/rfc-2457-non-ascii-idents/lint-non-ascii-idents.rs index 20d00cf701a15..8ae1744096dde 100644 --- a/src/test/ui/lint/rfc-2457-non-ascii-idents/lint-non-ascii-idents.rs +++ b/src/test/ui/lint/rfc-2457-non-ascii-idents/lint-non-ascii-idents.rs @@ -1,4 +1,3 @@ -#![feature(non_ascii_idents)] #![deny(non_ascii_idents)] const חלודה: usize = 2; //~ ERROR identifier contains non-ASCII characters diff --git a/src/test/ui/lint/rfc-2457-non-ascii-idents/lint-non-ascii-idents.stderr b/src/test/ui/lint/rfc-2457-non-ascii-idents/lint-non-ascii-idents.stderr index 048b6ff5d687f..8ed7f093ce340 100644 --- a/src/test/ui/lint/rfc-2457-non-ascii-idents/lint-non-ascii-idents.stderr +++ b/src/test/ui/lint/rfc-2457-non-ascii-idents/lint-non-ascii-idents.stderr @@ -1,23 +1,23 @@ error: identifier contains non-ASCII characters - --> $DIR/lint-non-ascii-idents.rs:4:7 + --> $DIR/lint-non-ascii-idents.rs:3:7 | LL | const חלודה: usize = 2; | ^^^^^ | note: the lint level is defined here - --> $DIR/lint-non-ascii-idents.rs:2:9 + --> $DIR/lint-non-ascii-idents.rs:1:9 | LL | #![deny(non_ascii_idents)] | ^^^^^^^^^^^^^^^^ error: identifier contains non-ASCII characters - --> $DIR/lint-non-ascii-idents.rs:6:4 + --> $DIR/lint-non-ascii-idents.rs:5:4 | LL | fn coöperation() {} | ^^^^^^^^^^^ error: identifier contains non-ASCII characters - --> $DIR/lint-non-ascii-idents.rs:9:9 + --> $DIR/lint-non-ascii-idents.rs:8:9 | LL | let naïveté = 2; | ^^^^^^^ diff --git a/src/test/ui/lint/rfc-2457-non-ascii-idents/lint-uncommon-codepoints.rs b/src/test/ui/lint/rfc-2457-non-ascii-idents/lint-uncommon-codepoints.rs index b5e251e047b5a..81a3427a10274 100644 --- a/src/test/ui/lint/rfc-2457-non-ascii-idents/lint-uncommon-codepoints.rs +++ b/src/test/ui/lint/rfc-2457-non-ascii-idents/lint-uncommon-codepoints.rs @@ -1,4 +1,3 @@ -#![feature(non_ascii_idents)] #![deny(uncommon_codepoints)] const µ: f64 = 0.000001; //~ ERROR identifier contains uncommon Unicode codepoints diff --git a/src/test/ui/lint/rfc-2457-non-ascii-idents/lint-uncommon-codepoints.stderr b/src/test/ui/lint/rfc-2457-non-ascii-idents/lint-uncommon-codepoints.stderr index 05ea3d5de7dbc..d435282a6e855 100644 --- a/src/test/ui/lint/rfc-2457-non-ascii-idents/lint-uncommon-codepoints.stderr +++ b/src/test/ui/lint/rfc-2457-non-ascii-idents/lint-uncommon-codepoints.stderr @@ -1,23 +1,23 @@ error: identifier contains uncommon Unicode codepoints - --> $DIR/lint-uncommon-codepoints.rs:4:7 + --> $DIR/lint-uncommon-codepoints.rs:3:7 | LL | const µ: f64 = 0.000001; | ^ | note: the lint level is defined here - --> $DIR/lint-uncommon-codepoints.rs:2:9 + --> $DIR/lint-uncommon-codepoints.rs:1:9 | LL | #![deny(uncommon_codepoints)] | ^^^^^^^^^^^^^^^^^^^ error: identifier contains uncommon Unicode codepoints - --> $DIR/lint-uncommon-codepoints.rs:6:4 + --> $DIR/lint-uncommon-codepoints.rs:5:4 | LL | fn dijkstra() {} | ^^^^^^^ error: identifier contains uncommon Unicode codepoints - --> $DIR/lint-uncommon-codepoints.rs:9:9 + --> $DIR/lint-uncommon-codepoints.rs:8:9 | LL | let ㇻㇲㇳ = "rust"; | ^^^^^^ diff --git a/src/test/ui/lint/rustdoc-group.rs b/src/test/ui/lint/rustdoc-group.rs new file mode 100644 index 0000000000000..130abe4253a13 --- /dev/null +++ b/src/test/ui/lint/rustdoc-group.rs @@ -0,0 +1,5 @@ +// check-pass +// compile-flags: --crate-type lib +#![deny(rustdoc)] +//~^ WARNING removed: use `rustdoc::all` +#![deny(rustdoc::all)] // has no effect when run with rustc directly diff --git a/src/test/ui/lint/rustdoc-group.stderr b/src/test/ui/lint/rustdoc-group.stderr new file mode 100644 index 0000000000000..fddc863ae1e99 --- /dev/null +++ b/src/test/ui/lint/rustdoc-group.stderr @@ -0,0 +1,10 @@ +warning: lint `rustdoc` has been removed: use `rustdoc::all` instead + --> $DIR/rustdoc-group.rs:3:9 + | +LL | #![deny(rustdoc)] + | ^^^^^^^ + | + = note: `#[warn(renamed_and_removed_lints)]` on by default + +warning: 1 warning emitted + diff --git a/src/test/ui/lint/rustdoc-renamed.rs b/src/test/ui/lint/rustdoc-renamed.rs index 71e88bd7f54a5..ecd6155b76909 100644 --- a/src/test/ui/lint/rustdoc-renamed.rs +++ b/src/test/ui/lint/rustdoc-renamed.rs @@ -11,4 +11,5 @@ #![deny(intra_doc_link_resolution_failure)] //~^ ERROR removed: use `rustdoc::broken_intra_doc_links` #![deny(non_autolinks)] -//~^ ERROR removed: use `rustdoc::non_autolinks` +// FIXME: the old names for rustdoc lints should warn by default once `rustdoc::` makes it to the +// stable channel. diff --git a/src/test/ui/lint/rustdoc-renamed.stderr b/src/test/ui/lint/rustdoc-renamed.stderr index a7fe3e29d5be0..096e867aa16db 100644 --- a/src/test/ui/lint/rustdoc-renamed.stderr +++ b/src/test/ui/lint/rustdoc-renamed.stderr @@ -10,11 +10,5 @@ note: the lint level is defined here LL | #![deny(renamed_and_removed_lints)] | ^^^^^^^^^^^^^^^^^^^^^^^^^ -error: lint `non_autolinks` has been removed: use `rustdoc::non_autolinks` instead - --> $DIR/rustdoc-renamed.rs:13:9 - | -LL | #![deny(non_autolinks)] - | ^^^^^^^^^^^^^ - -error: aborting due to 2 previous errors +error: aborting due to previous error diff --git a/src/test/ui/lint/special-upper-lower-cases.rs b/src/test/ui/lint/special-upper-lower-cases.rs index 71ebf05dd31ab..761be61fa07f6 100644 --- a/src/test/ui/lint/special-upper-lower-cases.rs +++ b/src/test/ui/lint/special-upper-lower-cases.rs @@ -5,7 +5,6 @@ // check-pass -#![feature(non_ascii_idents)] #![allow(uncommon_codepoints, unused)] struct 𝕟𝕠𝕥𝕒𝕔𝕒𝕞𝕖𝕝; diff --git a/src/test/ui/lint/special-upper-lower-cases.stderr b/src/test/ui/lint/special-upper-lower-cases.stderr index e3b451a15a2cc..2aa13c33be3a6 100644 --- a/src/test/ui/lint/special-upper-lower-cases.stderr +++ b/src/test/ui/lint/special-upper-lower-cases.stderr @@ -1,5 +1,5 @@ warning: type `𝕟𝕠𝕥𝕒𝕔𝕒𝕞𝕖𝕝` should have an upper camel case name - --> $DIR/special-upper-lower-cases.rs:11:8 + --> $DIR/special-upper-lower-cases.rs:10:8 | LL | struct 𝕟𝕠𝕥𝕒𝕔𝕒𝕞𝕖𝕝; | ^^^^^^^^^ should have an UpperCamelCase name @@ -7,13 +7,13 @@ LL | struct 𝕟𝕠𝕥𝕒𝕔𝕒𝕞𝕖𝕝; = note: `#[warn(non_camel_case_types)]` on by default warning: type `𝕟𝕠𝕥_𝕒_𝕔𝕒𝕞𝕖𝕝` should have an upper camel case name - --> $DIR/special-upper-lower-cases.rs:15:8 + --> $DIR/special-upper-lower-cases.rs:14:8 | LL | struct 𝕟𝕠𝕥_𝕒_𝕔𝕒𝕞𝕖𝕝; | ^^^^^^^^^^^ should have an UpperCamelCase name warning: static variable `𝗻𝗼𝗻𝘂𝗽𝗽𝗲𝗿𝗰𝗮𝘀𝗲` should have an upper case name - --> $DIR/special-upper-lower-cases.rs:18:8 + --> $DIR/special-upper-lower-cases.rs:17:8 | LL | static 𝗻𝗼𝗻𝘂𝗽𝗽𝗲𝗿𝗰𝗮𝘀𝗲: i32 = 1; | ^^^^^^^^^^^^ should have an UPPER_CASE name @@ -21,7 +21,7 @@ LL | static 𝗻𝗼𝗻𝘂𝗽𝗽𝗲𝗿𝗰𝗮𝘀𝗲: i32 = 1; = note: `#[warn(non_upper_case_globals)]` on by default warning: variable `𝓢𝓝𝓐𝓐𝓐𝓐𝓚𝓔𝓢` should have a snake case name - --> $DIR/special-upper-lower-cases.rs:22:9 + --> $DIR/special-upper-lower-cases.rs:21:9 | LL | let 𝓢𝓝𝓐𝓐𝓐𝓐𝓚𝓔𝓢 = 1; | ^^^^^^^^^ should have a snake_case name diff --git a/src/test/ui/lint/type-overflow.stderr b/src/test/ui/lint/type-overflow.stderr index 521223e325650..8a31fd4474614 100644 --- a/src/test/ui/lint/type-overflow.stderr +++ b/src/test/ui/lint/type-overflow.stderr @@ -60,7 +60,8 @@ warning: literal out of range for `i8` LL | let fail = -0b1111_1111i8; | ^^^^^^^^^^^^^ help: consider using the type `i16` instead: `0b1111_1111i16` | - = note: the literal `0b1111_1111i8` (decimal `255`) does not fit into the type `i8` and will become `-1i8` + = note: the literal `0b1111_1111i8` (decimal `255`) does not fit into the type `i8` + = note: and the value `-0b1111_1111i8` will become `1i8` warning: 7 warnings emitted diff --git a/src/test/ui/lint/unaligned_references.rs b/src/test/ui/lint/unaligned_references.rs index c4e5d065643c8..d06b06b504f06 100644 --- a/src/test/ui/lint/unaligned_references.rs +++ b/src/test/ui/lint/unaligned_references.rs @@ -8,22 +8,43 @@ pub struct Good { aligned: [u8; 32], } +#[repr(packed(2))] +pub struct Packed2 { + x: u32, + y: u16, + z: u8, +} + fn main() { unsafe { let good = Good { data: 0, ptr: &0, data2: [0, 0], aligned: [0; 32] }; let _ = &good.ptr; //~ ERROR reference to packed field + //~^ previously accepted let _ = &good.data; //~ ERROR reference to packed field + //~^ previously accepted // Error even when turned into raw pointer immediately. let _ = &good.data as *const _; //~ ERROR reference to packed field + //~^ previously accepted let _: *const _ = &good.data; //~ ERROR reference to packed field + //~^ previously accepted // Error on method call. let _ = good.data.clone(); //~ ERROR reference to packed field + //~^ previously accepted // Error for nested fields. let _ = &good.data2[0]; //~ ERROR reference to packed field + //~^ previously accepted let _ = &*good.ptr; // ok, behind a pointer let _ = &good.aligned; // ok, has align 1 let _ = &good.aligned[2]; // ok, has align 1 } + + unsafe { + let packed2 = Packed2 { x: 0, y: 0, z: 0 }; + let _ = &packed2.x; //~ ERROR reference to packed field + //~^ previously accepted + let _ = &packed2.y; // ok, has align 2 in packed(2) struct + let _ = &packed2.z; // ok, has align 1 + } } diff --git a/src/test/ui/lint/unaligned_references.stderr b/src/test/ui/lint/unaligned_references.stderr index 8786b9c05db27..b4cce3cfea217 100644 --- a/src/test/ui/lint/unaligned_references.stderr +++ b/src/test/ui/lint/unaligned_references.stderr @@ -1,5 +1,5 @@ error: reference to packed field is unaligned - --> $DIR/unaligned_references.rs:15:17 + --> $DIR/unaligned_references.rs:22:17 | LL | let _ = &good.ptr; | ^^^^^^^^^ @@ -9,47 +9,69 @@ note: the lint level is defined here | LL | #![deny(unaligned_references)] | ^^^^^^^^^^^^^^^^^^^^ + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #82523 <https://github.com/rust-lang/rust/issues/82523> = note: fields of packed structs are not properly aligned, and creating a misaligned reference is undefined behavior (even if that reference is never dereferenced) error: reference to packed field is unaligned - --> $DIR/unaligned_references.rs:16:17 + --> $DIR/unaligned_references.rs:24:17 | LL | let _ = &good.data; | ^^^^^^^^^^ | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #82523 <https://github.com/rust-lang/rust/issues/82523> = note: fields of packed structs are not properly aligned, and creating a misaligned reference is undefined behavior (even if that reference is never dereferenced) error: reference to packed field is unaligned - --> $DIR/unaligned_references.rs:18:17 + --> $DIR/unaligned_references.rs:27:17 | LL | let _ = &good.data as *const _; | ^^^^^^^^^^ | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #82523 <https://github.com/rust-lang/rust/issues/82523> = note: fields of packed structs are not properly aligned, and creating a misaligned reference is undefined behavior (even if that reference is never dereferenced) error: reference to packed field is unaligned - --> $DIR/unaligned_references.rs:19:27 + --> $DIR/unaligned_references.rs:29:27 | LL | let _: *const _ = &good.data; | ^^^^^^^^^^ | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #82523 <https://github.com/rust-lang/rust/issues/82523> = note: fields of packed structs are not properly aligned, and creating a misaligned reference is undefined behavior (even if that reference is never dereferenced) error: reference to packed field is unaligned - --> $DIR/unaligned_references.rs:21:17 + --> $DIR/unaligned_references.rs:32:17 | LL | let _ = good.data.clone(); | ^^^^^^^^^ | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #82523 <https://github.com/rust-lang/rust/issues/82523> = note: fields of packed structs are not properly aligned, and creating a misaligned reference is undefined behavior (even if that reference is never dereferenced) error: reference to packed field is unaligned - --> $DIR/unaligned_references.rs:23:17 + --> $DIR/unaligned_references.rs:35:17 | LL | let _ = &good.data2[0]; | ^^^^^^^^^^^^^^ | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #82523 <https://github.com/rust-lang/rust/issues/82523> = note: fields of packed structs are not properly aligned, and creating a misaligned reference is undefined behavior (even if that reference is never dereferenced) -error: aborting due to 6 previous errors +error: reference to packed field is unaligned + --> $DIR/unaligned_references.rs:45:17 + | +LL | let _ = &packed2.x; + | ^^^^^^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #82523 <https://github.com/rust-lang/rust/issues/82523> + = note: fields of packed structs are not properly aligned, and creating a misaligned reference is undefined behavior (even if that reference is never dereferenced) + +error: aborting due to 7 previous errors diff --git a/src/test/ui/lint/unaligned_references_external_macro.rs b/src/test/ui/lint/unaligned_references_external_macro.rs index 6ac501fb0e0db..cb597c38e7792 100644 --- a/src/test/ui/lint/unaligned_references_external_macro.rs +++ b/src/test/ui/lint/unaligned_references_external_macro.rs @@ -1,10 +1,9 @@ // aux-build:unaligned_references_external_crate.rs -#![allow(safe_packed_borrows)] - extern crate unaligned_references_external_crate; unaligned_references_external_crate::mac! { //~ERROR reference to packed field is unaligned + //~^ previously accepted #[repr(packed)] pub struct X { pub field: u16 diff --git a/src/test/ui/lint/unaligned_references_external_macro.stderr b/src/test/ui/lint/unaligned_references_external_macro.stderr index 140294b65ccd8..4e7c6bfc98d9d 100644 --- a/src/test/ui/lint/unaligned_references_external_macro.stderr +++ b/src/test/ui/lint/unaligned_references_external_macro.stderr @@ -1,7 +1,8 @@ error: reference to packed field is unaligned - --> $DIR/unaligned_references_external_macro.rs:7:1 + --> $DIR/unaligned_references_external_macro.rs:5:1 | LL | / unaligned_references_external_crate::mac! { +LL | | LL | | #[repr(packed)] LL | | pub struct X { LL | | pub field: u16 @@ -10,15 +11,18 @@ LL | | } | |_^ | note: the lint level is defined here - --> $DIR/unaligned_references_external_macro.rs:7:1 + --> $DIR/unaligned_references_external_macro.rs:5:1 | LL | / unaligned_references_external_crate::mac! { +LL | | LL | | #[repr(packed)] LL | | pub struct X { LL | | pub field: u16 LL | | } LL | | } | |_^ + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #82523 <https://github.com/rust-lang/rust/issues/82523> = note: fields of packed structs are not properly aligned, and creating a misaligned reference is undefined behavior (even if that reference is never dereferenced) = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/src/test/ui/lint/uninitialized-zeroed.rs b/src/test/ui/lint/uninitialized-zeroed.rs index 78d3060886dd0..122933c3c4e46 100644 --- a/src/test/ui/lint/uninitialized-zeroed.rs +++ b/src/test/ui/lint/uninitialized-zeroed.rs @@ -1,4 +1,3 @@ -// ignore-tidy-linelength // This test checks that calling `mem::{uninitialized,zeroed}` with certain types results // in a lint. diff --git a/src/test/ui/lint/uninitialized-zeroed.stderr b/src/test/ui/lint/uninitialized-zeroed.stderr index de1b6c7617675..0af185ef61b5e 100644 --- a/src/test/ui/lint/uninitialized-zeroed.stderr +++ b/src/test/ui/lint/uninitialized-zeroed.stderr @@ -1,5 +1,5 @@ error: the type `&T` does not permit zero-initialization - --> $DIR/uninitialized-zeroed.rs:41:32 + --> $DIR/uninitialized-zeroed.rs:40:32 | LL | let _val: &'static T = mem::zeroed(); | ^^^^^^^^^^^^^ @@ -8,14 +8,14 @@ LL | let _val: &'static T = mem::zeroed(); | help: use `MaybeUninit<T>` instead, and only call `assume_init` after initialization is done | note: the lint level is defined here - --> $DIR/uninitialized-zeroed.rs:7:9 + --> $DIR/uninitialized-zeroed.rs:6:9 | LL | #![deny(invalid_value)] | ^^^^^^^^^^^^^ = note: references must be non-null error: the type `&T` does not permit being left uninitialized - --> $DIR/uninitialized-zeroed.rs:42:32 + --> $DIR/uninitialized-zeroed.rs:41:32 | LL | let _val: &'static T = mem::uninitialized(); | ^^^^^^^^^^^^^^^^^^^^ @@ -26,7 +26,7 @@ LL | let _val: &'static T = mem::uninitialized(); = note: references must be non-null error: the type `Wrap<&T>` does not permit zero-initialization - --> $DIR/uninitialized-zeroed.rs:44:38 + --> $DIR/uninitialized-zeroed.rs:43:38 | LL | let _val: Wrap<&'static T> = mem::zeroed(); | ^^^^^^^^^^^^^ @@ -35,13 +35,13 @@ LL | let _val: Wrap<&'static T> = mem::zeroed(); | help: use `MaybeUninit<T>` instead, and only call `assume_init` after initialization is done | note: references must be non-null (in this struct field) - --> $DIR/uninitialized-zeroed.rs:18:18 + --> $DIR/uninitialized-zeroed.rs:17:18 | LL | struct Wrap<T> { wrapped: T } | ^^^^^^^^^^ error: the type `Wrap<&T>` does not permit being left uninitialized - --> $DIR/uninitialized-zeroed.rs:45:38 + --> $DIR/uninitialized-zeroed.rs:44:38 | LL | let _val: Wrap<&'static T> = mem::uninitialized(); | ^^^^^^^^^^^^^^^^^^^^ @@ -50,13 +50,13 @@ LL | let _val: Wrap<&'static T> = mem::uninitialized(); | help: use `MaybeUninit<T>` instead, and only call `assume_init` after initialization is done | note: references must be non-null (in this struct field) - --> $DIR/uninitialized-zeroed.rs:18:18 + --> $DIR/uninitialized-zeroed.rs:17:18 | LL | struct Wrap<T> { wrapped: T } | ^^^^^^^^^^ error: the type `!` does not permit zero-initialization - --> $DIR/uninitialized-zeroed.rs:52:23 + --> $DIR/uninitialized-zeroed.rs:51:23 | LL | let _val: ! = mem::zeroed(); | ^^^^^^^^^^^^^ @@ -67,7 +67,7 @@ LL | let _val: ! = mem::zeroed(); = note: the `!` type has no valid value error: the type `!` does not permit being left uninitialized - --> $DIR/uninitialized-zeroed.rs:53:23 + --> $DIR/uninitialized-zeroed.rs:52:23 | LL | let _val: ! = mem::uninitialized(); | ^^^^^^^^^^^^^^^^^^^^ @@ -78,7 +78,7 @@ LL | let _val: ! = mem::uninitialized(); = note: the `!` type has no valid value error: the type `(i32, !)` does not permit zero-initialization - --> $DIR/uninitialized-zeroed.rs:55:30 + --> $DIR/uninitialized-zeroed.rs:54:30 | LL | let _val: (i32, !) = mem::zeroed(); | ^^^^^^^^^^^^^ @@ -89,7 +89,7 @@ LL | let _val: (i32, !) = mem::zeroed(); = note: the `!` type has no valid value error: the type `(i32, !)` does not permit being left uninitialized - --> $DIR/uninitialized-zeroed.rs:56:30 + --> $DIR/uninitialized-zeroed.rs:55:30 | LL | let _val: (i32, !) = mem::uninitialized(); | ^^^^^^^^^^^^^^^^^^^^ @@ -100,7 +100,7 @@ LL | let _val: (i32, !) = mem::uninitialized(); = note: the `!` type has no valid value error: the type `Void` does not permit zero-initialization - --> $DIR/uninitialized-zeroed.rs:58:26 + --> $DIR/uninitialized-zeroed.rs:57:26 | LL | let _val: Void = mem::zeroed(); | ^^^^^^^^^^^^^ @@ -111,7 +111,7 @@ LL | let _val: Void = mem::zeroed(); = note: enums with no variants have no valid value error: the type `Void` does not permit being left uninitialized - --> $DIR/uninitialized-zeroed.rs:59:26 + --> $DIR/uninitialized-zeroed.rs:58:26 | LL | let _val: Void = mem::uninitialized(); | ^^^^^^^^^^^^^^^^^^^^ @@ -122,7 +122,7 @@ LL | let _val: Void = mem::uninitialized(); = note: enums with no variants have no valid value error: the type `&i32` does not permit zero-initialization - --> $DIR/uninitialized-zeroed.rs:61:34 + --> $DIR/uninitialized-zeroed.rs:60:34 | LL | let _val: &'static i32 = mem::zeroed(); | ^^^^^^^^^^^^^ @@ -133,7 +133,7 @@ LL | let _val: &'static i32 = mem::zeroed(); = note: references must be non-null error: the type `&i32` does not permit being left uninitialized - --> $DIR/uninitialized-zeroed.rs:62:34 + --> $DIR/uninitialized-zeroed.rs:61:34 | LL | let _val: &'static i32 = mem::uninitialized(); | ^^^^^^^^^^^^^^^^^^^^ @@ -144,7 +144,7 @@ LL | let _val: &'static i32 = mem::uninitialized(); = note: references must be non-null error: the type `Ref` does not permit zero-initialization - --> $DIR/uninitialized-zeroed.rs:64:25 + --> $DIR/uninitialized-zeroed.rs:63:25 | LL | let _val: Ref = mem::zeroed(); | ^^^^^^^^^^^^^ @@ -153,13 +153,13 @@ LL | let _val: Ref = mem::zeroed(); | help: use `MaybeUninit<T>` instead, and only call `assume_init` after initialization is done | note: references must be non-null (in this struct field) - --> $DIR/uninitialized-zeroed.rs:15:12 + --> $DIR/uninitialized-zeroed.rs:14:12 | LL | struct Ref(&'static i32); | ^^^^^^^^^^^^ error: the type `Ref` does not permit being left uninitialized - --> $DIR/uninitialized-zeroed.rs:65:25 + --> $DIR/uninitialized-zeroed.rs:64:25 | LL | let _val: Ref = mem::uninitialized(); | ^^^^^^^^^^^^^^^^^^^^ @@ -168,13 +168,13 @@ LL | let _val: Ref = mem::uninitialized(); | help: use `MaybeUninit<T>` instead, and only call `assume_init` after initialization is done | note: references must be non-null (in this struct field) - --> $DIR/uninitialized-zeroed.rs:15:12 + --> $DIR/uninitialized-zeroed.rs:14:12 | LL | struct Ref(&'static i32); | ^^^^^^^^^^^^ error: the type `fn()` does not permit zero-initialization - --> $DIR/uninitialized-zeroed.rs:67:26 + --> $DIR/uninitialized-zeroed.rs:66:26 | LL | let _val: fn() = mem::zeroed(); | ^^^^^^^^^^^^^ @@ -185,7 +185,7 @@ LL | let _val: fn() = mem::zeroed(); = note: function pointers must be non-null error: the type `fn()` does not permit being left uninitialized - --> $DIR/uninitialized-zeroed.rs:68:26 + --> $DIR/uninitialized-zeroed.rs:67:26 | LL | let _val: fn() = mem::uninitialized(); | ^^^^^^^^^^^^^^^^^^^^ @@ -196,7 +196,7 @@ LL | let _val: fn() = mem::uninitialized(); = note: function pointers must be non-null error: the type `Wrap<fn()>` does not permit zero-initialization - --> $DIR/uninitialized-zeroed.rs:70:32 + --> $DIR/uninitialized-zeroed.rs:69:32 | LL | let _val: Wrap<fn()> = mem::zeroed(); | ^^^^^^^^^^^^^ @@ -205,13 +205,13 @@ LL | let _val: Wrap<fn()> = mem::zeroed(); | help: use `MaybeUninit<T>` instead, and only call `assume_init` after initialization is done | note: function pointers must be non-null (in this struct field) - --> $DIR/uninitialized-zeroed.rs:18:18 + --> $DIR/uninitialized-zeroed.rs:17:18 | LL | struct Wrap<T> { wrapped: T } | ^^^^^^^^^^ error: the type `Wrap<fn()>` does not permit being left uninitialized - --> $DIR/uninitialized-zeroed.rs:71:32 + --> $DIR/uninitialized-zeroed.rs:70:32 | LL | let _val: Wrap<fn()> = mem::uninitialized(); | ^^^^^^^^^^^^^^^^^^^^ @@ -220,13 +220,13 @@ LL | let _val: Wrap<fn()> = mem::uninitialized(); | help: use `MaybeUninit<T>` instead, and only call `assume_init` after initialization is done | note: function pointers must be non-null (in this struct field) - --> $DIR/uninitialized-zeroed.rs:18:18 + --> $DIR/uninitialized-zeroed.rs:17:18 | LL | struct Wrap<T> { wrapped: T } | ^^^^^^^^^^ error: the type `WrapEnum<fn()>` does not permit zero-initialization - --> $DIR/uninitialized-zeroed.rs:73:36 + --> $DIR/uninitialized-zeroed.rs:72:36 | LL | let _val: WrapEnum<fn()> = mem::zeroed(); | ^^^^^^^^^^^^^ @@ -235,13 +235,13 @@ LL | let _val: WrapEnum<fn()> = mem::zeroed(); | help: use `MaybeUninit<T>` instead, and only call `assume_init` after initialization is done | note: function pointers must be non-null (in this enum field) - --> $DIR/uninitialized-zeroed.rs:19:28 + --> $DIR/uninitialized-zeroed.rs:18:28 | LL | enum WrapEnum<T> { Wrapped(T) } | ^ error: the type `WrapEnum<fn()>` does not permit being left uninitialized - --> $DIR/uninitialized-zeroed.rs:74:36 + --> $DIR/uninitialized-zeroed.rs:73:36 | LL | let _val: WrapEnum<fn()> = mem::uninitialized(); | ^^^^^^^^^^^^^^^^^^^^ @@ -250,13 +250,13 @@ LL | let _val: WrapEnum<fn()> = mem::uninitialized(); | help: use `MaybeUninit<T>` instead, and only call `assume_init` after initialization is done | note: function pointers must be non-null (in this enum field) - --> $DIR/uninitialized-zeroed.rs:19:28 + --> $DIR/uninitialized-zeroed.rs:18:28 | LL | enum WrapEnum<T> { Wrapped(T) } | ^ error: the type `Wrap<(RefPair, i32)>` does not permit zero-initialization - --> $DIR/uninitialized-zeroed.rs:76:42 + --> $DIR/uninitialized-zeroed.rs:75:42 | LL | let _val: Wrap<(RefPair, i32)> = mem::zeroed(); | ^^^^^^^^^^^^^ @@ -265,13 +265,13 @@ LL | let _val: Wrap<(RefPair, i32)> = mem::zeroed(); | help: use `MaybeUninit<T>` instead, and only call `assume_init` after initialization is done | note: references must be non-null (in this struct field) - --> $DIR/uninitialized-zeroed.rs:16:16 + --> $DIR/uninitialized-zeroed.rs:15:16 | LL | struct RefPair((&'static i32, i32)); | ^^^^^^^^^^^^^^^^^^^ error: the type `Wrap<(RefPair, i32)>` does not permit being left uninitialized - --> $DIR/uninitialized-zeroed.rs:77:42 + --> $DIR/uninitialized-zeroed.rs:76:42 | LL | let _val: Wrap<(RefPair, i32)> = mem::uninitialized(); | ^^^^^^^^^^^^^^^^^^^^ @@ -280,13 +280,13 @@ LL | let _val: Wrap<(RefPair, i32)> = mem::uninitialized(); | help: use `MaybeUninit<T>` instead, and only call `assume_init` after initialization is done | note: references must be non-null (in this struct field) - --> $DIR/uninitialized-zeroed.rs:16:16 + --> $DIR/uninitialized-zeroed.rs:15:16 | LL | struct RefPair((&'static i32, i32)); | ^^^^^^^^^^^^^^^^^^^ error: the type `NonNull<i32>` does not permit zero-initialization - --> $DIR/uninitialized-zeroed.rs:79:34 + --> $DIR/uninitialized-zeroed.rs:78:34 | LL | let _val: NonNull<i32> = mem::zeroed(); | ^^^^^^^^^^^^^ @@ -297,7 +297,7 @@ LL | let _val: NonNull<i32> = mem::zeroed(); = note: `std::ptr::NonNull<i32>` must be non-null error: the type `NonNull<i32>` does not permit being left uninitialized - --> $DIR/uninitialized-zeroed.rs:80:34 + --> $DIR/uninitialized-zeroed.rs:79:34 | LL | let _val: NonNull<i32> = mem::uninitialized(); | ^^^^^^^^^^^^^^^^^^^^ @@ -308,7 +308,7 @@ LL | let _val: NonNull<i32> = mem::uninitialized(); = note: `std::ptr::NonNull<i32>` must be non-null error: the type `*const dyn Send` does not permit zero-initialization - --> $DIR/uninitialized-zeroed.rs:82:37 + --> $DIR/uninitialized-zeroed.rs:81:37 | LL | let _val: *const dyn Send = mem::zeroed(); | ^^^^^^^^^^^^^ @@ -319,7 +319,7 @@ LL | let _val: *const dyn Send = mem::zeroed(); = note: the vtable of a wide raw pointer must be non-null error: the type `*const dyn Send` does not permit being left uninitialized - --> $DIR/uninitialized-zeroed.rs:83:37 + --> $DIR/uninitialized-zeroed.rs:82:37 | LL | let _val: *const dyn Send = mem::uninitialized(); | ^^^^^^^^^^^^^^^^^^^^ @@ -330,7 +330,7 @@ LL | let _val: *const dyn Send = mem::uninitialized(); = note: the vtable of a wide raw pointer must be non-null error: the type `bool` does not permit being left uninitialized - --> $DIR/uninitialized-zeroed.rs:87:26 + --> $DIR/uninitialized-zeroed.rs:86:26 | LL | let _val: bool = mem::uninitialized(); | ^^^^^^^^^^^^^^^^^^^^ @@ -341,7 +341,7 @@ LL | let _val: bool = mem::uninitialized(); = note: booleans must be either `true` or `false` error: the type `Wrap<char>` does not permit being left uninitialized - --> $DIR/uninitialized-zeroed.rs:90:32 + --> $DIR/uninitialized-zeroed.rs:89:32 | LL | let _val: Wrap<char> = mem::uninitialized(); | ^^^^^^^^^^^^^^^^^^^^ @@ -350,13 +350,13 @@ LL | let _val: Wrap<char> = mem::uninitialized(); | help: use `MaybeUninit<T>` instead, and only call `assume_init` after initialization is done | note: characters must be a valid Unicode codepoint (in this struct field) - --> $DIR/uninitialized-zeroed.rs:18:18 + --> $DIR/uninitialized-zeroed.rs:17:18 | LL | struct Wrap<T> { wrapped: T } | ^^^^^^^^^^ error: the type `NonBig` does not permit being left uninitialized - --> $DIR/uninitialized-zeroed.rs:93:28 + --> $DIR/uninitialized-zeroed.rs:92:28 | LL | let _val: NonBig = mem::uninitialized(); | ^^^^^^^^^^^^^^^^^^^^ @@ -367,7 +367,7 @@ LL | let _val: NonBig = mem::uninitialized(); = note: `NonBig` must be initialized inside its custom valid range error: the type `Fruit` does not permit being left uninitialized - --> $DIR/uninitialized-zeroed.rs:96:27 + --> $DIR/uninitialized-zeroed.rs:95:27 | LL | let _val: Fruit = mem::uninitialized(); | ^^^^^^^^^^^^^^^^^^^^ @@ -376,7 +376,7 @@ LL | let _val: Fruit = mem::uninitialized(); | help: use `MaybeUninit<T>` instead, and only call `assume_init` after initialization is done | note: enums have to be initialized to a variant - --> $DIR/uninitialized-zeroed.rs:27:1 + --> $DIR/uninitialized-zeroed.rs:26:1 | LL | / enum Fruit { LL | | Apple, @@ -385,7 +385,7 @@ LL | | } | |_^ error: the type `&i32` does not permit zero-initialization - --> $DIR/uninitialized-zeroed.rs:99:34 + --> $DIR/uninitialized-zeroed.rs:98:34 | LL | let _val: &'static i32 = mem::transmute(0usize); | ^^^^^^^^^^^^^^^^^^^^^^ @@ -396,7 +396,7 @@ LL | let _val: &'static i32 = mem::transmute(0usize); = note: references must be non-null error: the type `&[i32]` does not permit zero-initialization - --> $DIR/uninitialized-zeroed.rs:100:36 + --> $DIR/uninitialized-zeroed.rs:99:36 | LL | let _val: &'static [i32] = mem::transmute((0usize, 0usize)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -407,7 +407,7 @@ LL | let _val: &'static [i32] = mem::transmute((0usize, 0usize)); = note: references must be non-null error: the type `NonZeroU32` does not permit zero-initialization - --> $DIR/uninitialized-zeroed.rs:101:32 + --> $DIR/uninitialized-zeroed.rs:100:32 | LL | let _val: NonZeroU32 = mem::transmute(0); | ^^^^^^^^^^^^^^^^^ @@ -418,7 +418,7 @@ LL | let _val: NonZeroU32 = mem::transmute(0); = note: `std::num::NonZeroU32` must be non-null error: the type `NonNull<i32>` does not permit zero-initialization - --> $DIR/uninitialized-zeroed.rs:104:34 + --> $DIR/uninitialized-zeroed.rs:103:34 | LL | let _val: NonNull<i32> = MaybeUninit::zeroed().assume_init(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -429,7 +429,7 @@ LL | let _val: NonNull<i32> = MaybeUninit::zeroed().assume_init(); = note: `std::ptr::NonNull<i32>` must be non-null error: the type `NonNull<i32>` does not permit being left uninitialized - --> $DIR/uninitialized-zeroed.rs:105:34 + --> $DIR/uninitialized-zeroed.rs:104:34 | LL | let _val: NonNull<i32> = MaybeUninit::uninit().assume_init(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -440,7 +440,7 @@ LL | let _val: NonNull<i32> = MaybeUninit::uninit().assume_init(); = note: `std::ptr::NonNull<i32>` must be non-null error: the type `bool` does not permit being left uninitialized - --> $DIR/uninitialized-zeroed.rs:106:26 + --> $DIR/uninitialized-zeroed.rs:105:26 | LL | let _val: bool = MaybeUninit::uninit().assume_init(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/src/test/ui/liveness/liveness-assign/liveness-assign-imm-local-in-loop.rs b/src/test/ui/liveness/liveness-assign/liveness-assign-imm-local-in-loop.rs index c9e1851b9a980..08911c5bde781 100644 --- a/src/test/ui/liveness/liveness-assign/liveness-assign-imm-local-in-loop.rs +++ b/src/test/ui/liveness/liveness-assign/liveness-assign-imm-local-in-loop.rs @@ -1,6 +1,6 @@ fn test() { let v: isize; - //~^ HELP make this binding mutable + //~^ HELP consider making this binding mutable //~| SUGGESTION mut v loop { v = 1; //~ ERROR cannot assign twice to immutable variable `v` diff --git a/src/test/ui/liveness/liveness-assign/liveness-assign-imm-local-in-loop.stderr b/src/test/ui/liveness/liveness-assign/liveness-assign-imm-local-in-loop.stderr index 69dff734ee4be..66cdce7dacf83 100644 --- a/src/test/ui/liveness/liveness-assign/liveness-assign-imm-local-in-loop.stderr +++ b/src/test/ui/liveness/liveness-assign/liveness-assign-imm-local-in-loop.stderr @@ -2,7 +2,7 @@ error[E0384]: cannot assign twice to immutable variable `v` --> $DIR/liveness-assign-imm-local-in-loop.rs:6:9 | LL | let v: isize; - | - help: make this binding mutable: `mut v` + | - help: consider making this binding mutable: `mut v` ... LL | v = 1; | ^^^^^ cannot assign twice to immutable variable diff --git a/src/test/ui/liveness/liveness-assign/liveness-assign-imm-local-in-op-eq.rs b/src/test/ui/liveness/liveness-assign/liveness-assign-imm-local-in-op-eq.rs index f24f7d2bcfc52..1752d969086e8 100644 --- a/src/test/ui/liveness/liveness-assign/liveness-assign-imm-local-in-op-eq.rs +++ b/src/test/ui/liveness/liveness-assign/liveness-assign-imm-local-in-op-eq.rs @@ -1,6 +1,6 @@ fn test() { let v: isize; - //~^ HELP make this binding mutable + //~^ HELP consider making this binding mutable //~| SUGGESTION mut v v = 2; //~ NOTE first assignment v += 1; //~ ERROR cannot assign twice to immutable variable `v` diff --git a/src/test/ui/liveness/liveness-assign/liveness-assign-imm-local-in-op-eq.stderr b/src/test/ui/liveness/liveness-assign/liveness-assign-imm-local-in-op-eq.stderr index 182958dd49244..5db9539cbf1e5 100644 --- a/src/test/ui/liveness/liveness-assign/liveness-assign-imm-local-in-op-eq.stderr +++ b/src/test/ui/liveness/liveness-assign/liveness-assign-imm-local-in-op-eq.stderr @@ -2,7 +2,7 @@ error[E0384]: cannot assign twice to immutable variable `v` --> $DIR/liveness-assign-imm-local-in-op-eq.rs:6:5 | LL | let v: isize; - | - help: make this binding mutable: `mut v` + | - help: consider making this binding mutable: `mut v` ... LL | v = 2; | ----- first assignment to `v` diff --git a/src/test/ui/liveness/liveness-assign/liveness-assign-imm-local-with-drop.rs b/src/test/ui/liveness/liveness-assign/liveness-assign-imm-local-with-drop.rs index 8963e32717e22..c9b16e43910e8 100644 --- a/src/test/ui/liveness/liveness-assign/liveness-assign-imm-local-with-drop.rs +++ b/src/test/ui/liveness/liveness-assign/liveness-assign-imm-local-with-drop.rs @@ -1,6 +1,6 @@ fn test() { let b = Box::new(1); //~ NOTE first assignment - //~| HELP make this binding mutable + //~| HELP consider making this binding mutable //~| SUGGESTION mut b drop(b); b = Box::new(2); //~ ERROR cannot assign twice to immutable variable `b` diff --git a/src/test/ui/liveness/liveness-assign/liveness-assign-imm-local-with-drop.stderr b/src/test/ui/liveness/liveness-assign/liveness-assign-imm-local-with-drop.stderr index 7c4af624b2735..bb7e7e27a4ca0 100644 --- a/src/test/ui/liveness/liveness-assign/liveness-assign-imm-local-with-drop.stderr +++ b/src/test/ui/liveness/liveness-assign/liveness-assign-imm-local-with-drop.stderr @@ -5,7 +5,7 @@ LL | let b = Box::new(1); | - | | | first assignment to `b` - | help: make this binding mutable: `mut b` + | help: consider making this binding mutable: `mut b` ... LL | b = Box::new(2); | ^ cannot assign twice to immutable variable diff --git a/src/test/ui/liveness/liveness-assign/liveness-assign-imm-local-with-init.rs b/src/test/ui/liveness/liveness-assign/liveness-assign-imm-local-with-init.rs index 4ab222af8d088..4bb2db27a1652 100644 --- a/src/test/ui/liveness/liveness-assign/liveness-assign-imm-local-with-init.rs +++ b/src/test/ui/liveness/liveness-assign/liveness-assign-imm-local-with-init.rs @@ -1,6 +1,6 @@ fn test() { let v: isize = 1; //~ NOTE first assignment - //~| HELP make this binding mutable + //~| HELP consider making this binding mutable //~| SUGGESTION mut v v.clone(); v = 2; //~ ERROR cannot assign twice to immutable variable `v` diff --git a/src/test/ui/liveness/liveness-assign/liveness-assign-imm-local-with-init.stderr b/src/test/ui/liveness/liveness-assign/liveness-assign-imm-local-with-init.stderr index 6f5d5574877c9..80458a70a0107 100644 --- a/src/test/ui/liveness/liveness-assign/liveness-assign-imm-local-with-init.stderr +++ b/src/test/ui/liveness/liveness-assign/liveness-assign-imm-local-with-init.stderr @@ -5,7 +5,7 @@ LL | let v: isize = 1; | - | | | first assignment to `v` - | help: make this binding mutable: `mut v` + | help: consider making this binding mutable: `mut v` ... LL | v = 2; | ^^^^^ cannot assign twice to immutable variable diff --git a/src/test/ui/llvm-asm/llvm-asm-out-assign-imm.stderr b/src/test/ui/llvm-asm/llvm-asm-out-assign-imm.stderr index 9b0aa6be1e91e..3e5893f68b6c1 100644 --- a/src/test/ui/llvm-asm/llvm-asm-out-assign-imm.stderr +++ b/src/test/ui/llvm-asm/llvm-asm-out-assign-imm.stderr @@ -2,7 +2,7 @@ error[E0384]: cannot assign twice to immutable variable `x` --> $DIR/llvm-asm-out-assign-imm.rs:25:39 | LL | let x: isize; - | - help: make this binding mutable: `mut x` + | - help: consider making this binding mutable: `mut x` LL | x = 1; | ----- first assignment to `x` ... diff --git a/src/test/ui/loops/issue-82916.rs b/src/test/ui/loops/issue-82916.rs new file mode 100644 index 0000000000000..8633ea1e8cb3d --- /dev/null +++ b/src/test/ui/loops/issue-82916.rs @@ -0,0 +1,10 @@ +struct S(i32); + +fn foo(x: Vec<S>) { + for y in x { + + } + let z = x; //~ ERROR use of moved value: `x` +} + +fn main() {} diff --git a/src/test/ui/loops/issue-82916.stderr b/src/test/ui/loops/issue-82916.stderr new file mode 100644 index 0000000000000..ad42cce71f686 --- /dev/null +++ b/src/test/ui/loops/issue-82916.stderr @@ -0,0 +1,23 @@ +error[E0382]: use of moved value: `x` + --> $DIR/issue-82916.rs:7:13 + | +LL | fn foo(x: Vec<S>) { + | - move occurs because `x` has type `Vec<S>`, which does not implement the `Copy` trait +LL | for y in x { + | - + | | + | `x` moved due to this implicit call to `.into_iter()` + | help: consider borrowing to avoid moving into the for loop: `&x` +... +LL | let z = x; + | ^ value used here after move + | +note: this function takes ownership of the receiver `self`, which moves `x` + --> $SRC_DIR/core/src/iter/traits/collect.rs:LL:COL + | +LL | fn into_iter(self) -> Self::IntoIter; + | ^^^^ + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0382`. diff --git a/src/test/ui/loops/loops-reject-duplicate-labels-2.rs b/src/test/ui/loops/loops-reject-duplicate-labels-2.rs index a0f3aeffe9f7c..3a860f508ff78 100644 --- a/src/test/ui/loops/loops-reject-duplicate-labels-2.rs +++ b/src/test/ui/loops/loops-reject-duplicate-labels-2.rs @@ -1,6 +1,5 @@ // check-pass -// ignore-tidy-linelength // Issue #21633: reject duplicate loop labels in function bodies. // diff --git a/src/test/ui/loops/loops-reject-duplicate-labels-2.stderr b/src/test/ui/loops/loops-reject-duplicate-labels-2.stderr index 724c36e5203e6..6c53d04e10790 100644 --- a/src/test/ui/loops/loops-reject-duplicate-labels-2.stderr +++ b/src/test/ui/loops/loops-reject-duplicate-labels-2.stderr @@ -1,5 +1,5 @@ warning: label name `'fl` shadows a label name that is already in scope - --> $DIR/loops-reject-duplicate-labels-2.rs:14:7 + --> $DIR/loops-reject-duplicate-labels-2.rs:13:7 | LL | { 'fl: for _ in 0..10 { break; } } | --- first declared here @@ -7,7 +7,7 @@ LL | { 'fl: loop { break; } } | ^^^ label `'fl` already in scope warning: label name `'lf` shadows a label name that is already in scope - --> $DIR/loops-reject-duplicate-labels-2.rs:16:7 + --> $DIR/loops-reject-duplicate-labels-2.rs:15:7 | LL | { 'lf: loop { break; } } | --- first declared here @@ -15,7 +15,7 @@ LL | { 'lf: for _ in 0..10 { break; } } | ^^^ label `'lf` already in scope warning: label name `'wl` shadows a label name that is already in scope - --> $DIR/loops-reject-duplicate-labels-2.rs:18:7 + --> $DIR/loops-reject-duplicate-labels-2.rs:17:7 | LL | { 'wl: while 2 > 1 { break; } } | --- first declared here @@ -23,7 +23,7 @@ LL | { 'wl: loop { break; } } | ^^^ label `'wl` already in scope warning: label name `'lw` shadows a label name that is already in scope - --> $DIR/loops-reject-duplicate-labels-2.rs:20:7 + --> $DIR/loops-reject-duplicate-labels-2.rs:19:7 | LL | { 'lw: loop { break; } } | --- first declared here @@ -31,7 +31,7 @@ LL | { 'lw: while 2 > 1 { break; } } | ^^^ label `'lw` already in scope warning: label name `'fw` shadows a label name that is already in scope - --> $DIR/loops-reject-duplicate-labels-2.rs:22:7 + --> $DIR/loops-reject-duplicate-labels-2.rs:21:7 | LL | { 'fw: for _ in 0..10 { break; } } | --- first declared here @@ -39,7 +39,7 @@ LL | { 'fw: while 2 > 1 { break; } } | ^^^ label `'fw` already in scope warning: label name `'wf` shadows a label name that is already in scope - --> $DIR/loops-reject-duplicate-labels-2.rs:24:7 + --> $DIR/loops-reject-duplicate-labels-2.rs:23:7 | LL | { 'wf: while 2 > 1 { break; } } | --- first declared here @@ -47,7 +47,7 @@ LL | { 'wf: for _ in 0..10 { break; } } | ^^^ label `'wf` already in scope warning: label name `'tl` shadows a label name that is already in scope - --> $DIR/loops-reject-duplicate-labels-2.rs:26:7 + --> $DIR/loops-reject-duplicate-labels-2.rs:25:7 | LL | { 'tl: while let Some(_) = None::<i32> { break; } } | --- first declared here @@ -55,7 +55,7 @@ LL | { 'tl: loop { break; } } | ^^^ label `'tl` already in scope warning: label name `'lt` shadows a label name that is already in scope - --> $DIR/loops-reject-duplicate-labels-2.rs:28:7 + --> $DIR/loops-reject-duplicate-labels-2.rs:27:7 | LL | { 'lt: loop { break; } } | --- first declared here diff --git a/src/test/ui/loops/loops-reject-duplicate-labels.rs b/src/test/ui/loops/loops-reject-duplicate-labels.rs index a501ac18588fb..d9334ce385718 100644 --- a/src/test/ui/loops/loops-reject-duplicate-labels.rs +++ b/src/test/ui/loops/loops-reject-duplicate-labels.rs @@ -1,6 +1,5 @@ // check-pass -// ignore-tidy-linelength // Issue #21633: reject duplicate loop labels in function bodies. // This is testing the exact cases that are in the issue description. diff --git a/src/test/ui/loops/loops-reject-duplicate-labels.stderr b/src/test/ui/loops/loops-reject-duplicate-labels.stderr index 2d11281201797..5bdf64849f305 100644 --- a/src/test/ui/loops/loops-reject-duplicate-labels.stderr +++ b/src/test/ui/loops/loops-reject-duplicate-labels.stderr @@ -1,5 +1,5 @@ warning: label name `'fl` shadows a label name that is already in scope - --> $DIR/loops-reject-duplicate-labels.rs:11:5 + --> $DIR/loops-reject-duplicate-labels.rs:10:5 | LL | 'fl: for _ in 0..10 { break; } | --- first declared here @@ -7,7 +7,7 @@ LL | 'fl: loop { break; } | ^^^ label `'fl` already in scope warning: label name `'lf` shadows a label name that is already in scope - --> $DIR/loops-reject-duplicate-labels.rs:14:5 + --> $DIR/loops-reject-duplicate-labels.rs:13:5 | LL | 'lf: loop { break; } | --- first declared here @@ -15,7 +15,7 @@ LL | 'lf: for _ in 0..10 { break; } | ^^^ label `'lf` already in scope warning: label name `'wl` shadows a label name that is already in scope - --> $DIR/loops-reject-duplicate-labels.rs:16:5 + --> $DIR/loops-reject-duplicate-labels.rs:15:5 | LL | 'wl: while 2 > 1 { break; } | --- first declared here @@ -23,7 +23,7 @@ LL | 'wl: loop { break; } | ^^^ label `'wl` already in scope warning: label name `'lw` shadows a label name that is already in scope - --> $DIR/loops-reject-duplicate-labels.rs:18:5 + --> $DIR/loops-reject-duplicate-labels.rs:17:5 | LL | 'lw: loop { break; } | --- first declared here @@ -31,7 +31,7 @@ LL | 'lw: while 2 > 1 { break; } | ^^^ label `'lw` already in scope warning: label name `'fw` shadows a label name that is already in scope - --> $DIR/loops-reject-duplicate-labels.rs:20:5 + --> $DIR/loops-reject-duplicate-labels.rs:19:5 | LL | 'fw: for _ in 0..10 { break; } | --- first declared here @@ -39,7 +39,7 @@ LL | 'fw: while 2 > 1 { break; } | ^^^ label `'fw` already in scope warning: label name `'wf` shadows a label name that is already in scope - --> $DIR/loops-reject-duplicate-labels.rs:22:5 + --> $DIR/loops-reject-duplicate-labels.rs:21:5 | LL | 'wf: while 2 > 1 { break; } | --- first declared here @@ -47,7 +47,7 @@ LL | 'wf: for _ in 0..10 { break; } | ^^^ label `'wf` already in scope warning: label name `'tl` shadows a label name that is already in scope - --> $DIR/loops-reject-duplicate-labels.rs:24:5 + --> $DIR/loops-reject-duplicate-labels.rs:23:5 | LL | 'tl: while let Some(_) = None::<i32> { break; } | --- first declared here @@ -55,7 +55,7 @@ LL | 'tl: loop { break; } | ^^^ label `'tl` already in scope warning: label name `'lt` shadows a label name that is already in scope - --> $DIR/loops-reject-duplicate-labels.rs:26:5 + --> $DIR/loops-reject-duplicate-labels.rs:25:5 | LL | 'lt: loop { break; } | --- first declared here diff --git a/src/test/ui/macros/assert-eq-macro-msg.rs b/src/test/ui/macros/assert-eq-macro-msg.rs new file mode 100644 index 0000000000000..accbd2d1e7f50 --- /dev/null +++ b/src/test/ui/macros/assert-eq-macro-msg.rs @@ -0,0 +1,9 @@ +// run-fail +// error-pattern:panicked at 'assertion failed: `(left == right)` +// error-pattern: left: `2` +// error-pattern:right: `3`: 1 + 1 definitely should be 3' +// ignore-emscripten no processes + +fn main() { + assert_eq!(1 + 1, 3, "1 + 1 definitely should be 3"); +} diff --git a/src/test/ui/macros/assert-matches-macro-msg.rs b/src/test/ui/macros/assert-matches-macro-msg.rs new file mode 100644 index 0000000000000..43be9532f5d1c --- /dev/null +++ b/src/test/ui/macros/assert-matches-macro-msg.rs @@ -0,0 +1,11 @@ +// run-fail +// error-pattern:panicked at 'assertion failed: `(left matches right)` +// error-pattern: left: `2` +// error-pattern:right: `3`: 1 + 1 definitely should be 3' +// ignore-emscripten no processes + +#![feature(assert_matches)] + +fn main() { + assert_matches!(1 + 1, 3, "1 + 1 definitely should be 3"); +} diff --git a/src/test/ui/macros/assert-ne-macro-msg.rs b/src/test/ui/macros/assert-ne-macro-msg.rs new file mode 100644 index 0000000000000..fc0472b99b428 --- /dev/null +++ b/src/test/ui/macros/assert-ne-macro-msg.rs @@ -0,0 +1,9 @@ +// run-fail +// error-pattern:panicked at 'assertion failed: `(left != right)` +// error-pattern: left: `2` +// error-pattern:right: `2`: 1 + 1 definitely should not be 2' +// ignore-emscripten no processes + +fn main() { + assert_ne!(1 + 1, 2, "1 + 1 definitely should not be 2"); +} diff --git a/src/test/ui/macros/attr-empty-expr.rs b/src/test/ui/macros/attr-empty-expr.rs new file mode 100644 index 0000000000000..d4d1a3ee71e67 --- /dev/null +++ b/src/test/ui/macros/attr-empty-expr.rs @@ -0,0 +1,11 @@ +// AST-based macro attributes expanding to an empty expression produce an error and not ICE. + +#![feature(custom_test_frameworks)] +#![feature(stmt_expr_attributes)] +#![feature(test)] + +fn main() { + let _ = #[test] 0; //~ ERROR removing an expression is not supported in this position + let _ = #[bench] 1; //~ ERROR removing an expression is not supported in this position + let _ = #[test_case] 2; //~ ERROR removing an expression is not supported in this position +} diff --git a/src/test/ui/macros/attr-empty-expr.stderr b/src/test/ui/macros/attr-empty-expr.stderr new file mode 100644 index 0000000000000..53721053bcc08 --- /dev/null +++ b/src/test/ui/macros/attr-empty-expr.stderr @@ -0,0 +1,20 @@ +error: removing an expression is not supported in this position + --> $DIR/attr-empty-expr.rs:8:13 + | +LL | let _ = #[test] 0; + | ^^^^^^^ + +error: removing an expression is not supported in this position + --> $DIR/attr-empty-expr.rs:9:13 + | +LL | let _ = #[bench] 1; + | ^^^^^^^^ + +error: removing an expression is not supported in this position + --> $DIR/attr-empty-expr.rs:10:13 + | +LL | let _ = #[test_case] 2; + | ^^^^^^^^^^^^ + +error: aborting due to 3 previous errors + diff --git a/src/test/ui/macros/builtin-std-paths-fail.stderr b/src/test/ui/macros/builtin-std-paths-fail.stderr index 4f1a76b0d6e08..ba6261011907c 100644 --- a/src/test/ui/macros/builtin-std-paths-fail.stderr +++ b/src/test/ui/macros/builtin-std-paths-fail.stderr @@ -10,6 +10,18 @@ error[E0433]: failed to resolve: could not find `RustcDecodable` in `core` LL | core::RustcDecodable, | ^^^^^^^^^^^^^^ could not find `RustcDecodable` in `core` +error[E0433]: failed to resolve: could not find `RustcDecodable` in `core` + --> $DIR/builtin-std-paths-fail.rs:2:11 + | +LL | core::RustcDecodable, + | ^^^^^^^^^^^^^^ could not find `RustcDecodable` in `core` + +error[E0433]: failed to resolve: could not find `RustcDecodable` in `core` + --> $DIR/builtin-std-paths-fail.rs:4:11 + | +LL | core::RustcDecodable, + | ^^^^^^^^^^^^^^ could not find `RustcDecodable` in `core` + error[E0433]: failed to resolve: could not find `bench` in `core` --> $DIR/builtin-std-paths-fail.rs:7:9 | @@ -34,17 +46,17 @@ error[E0433]: failed to resolve: could not find `test` in `core` LL | #[core::test] | ^^^^ could not find `test` in `core` -error[E0433]: failed to resolve: could not find `RustcDecodable` in `core` - --> $DIR/builtin-std-paths-fail.rs:4:11 +error[E0433]: failed to resolve: could not find `RustcDecodable` in `std` + --> $DIR/builtin-std-paths-fail.rs:14:10 | -LL | core::RustcDecodable, - | ^^^^^^^^^^^^^^ could not find `RustcDecodable` in `core` +LL | std::RustcDecodable, + | ^^^^^^^^^^^^^^ could not find `RustcDecodable` in `std` -error[E0433]: failed to resolve: could not find `RustcDecodable` in `core` - --> $DIR/builtin-std-paths-fail.rs:2:11 +error[E0433]: failed to resolve: could not find `RustcDecodable` in `std` + --> $DIR/builtin-std-paths-fail.rs:16:10 | -LL | core::RustcDecodable, - | ^^^^^^^^^^^^^^ could not find `RustcDecodable` in `core` +LL | std::RustcDecodable, + | ^^^^^^^^^^^^^^ could not find `RustcDecodable` in `std` error[E0433]: failed to resolve: could not find `RustcDecodable` in `std` --> $DIR/builtin-std-paths-fail.rs:14:10 @@ -82,18 +94,6 @@ error[E0433]: failed to resolve: could not find `test` in `std` LL | #[std::test] | ^^^^ could not find `test` in `std` -error[E0433]: failed to resolve: could not find `RustcDecodable` in `std` - --> $DIR/builtin-std-paths-fail.rs:16:10 - | -LL | std::RustcDecodable, - | ^^^^^^^^^^^^^^ could not find `RustcDecodable` in `std` - -error[E0433]: failed to resolve: could not find `RustcDecodable` in `std` - --> $DIR/builtin-std-paths-fail.rs:14:10 - | -LL | std::RustcDecodable, - | ^^^^^^^^^^^^^^ could not find `RustcDecodable` in `std` - error: aborting due to 16 previous errors For more information about this error, try `rustc --explain E0433`. diff --git a/src/test/ui/concat-rpass.rs b/src/test/ui/macros/concat-rpass.rs similarity index 100% rename from src/test/ui/concat-rpass.rs rename to src/test/ui/macros/concat-rpass.rs diff --git a/src/test/ui/macros/edition-macro-pats.rs b/src/test/ui/macros/edition-macro-pats.rs index ea1f9bff6bf70..58f9271030583 100644 --- a/src/test/ui/macros/edition-macro-pats.rs +++ b/src/test/ui/macros/edition-macro-pats.rs @@ -1,10 +1,9 @@ // run-pass -#![feature(or_patterns)] #![feature(edition_macro_pats)] macro_rules! foo { - (a $x:pat2018) => {}; + (a $x:pat2015) => {}; (b $x:pat2021) => {}; } diff --git a/src/test/ui/macros/issue-83340.rs b/src/test/ui/macros/issue-83340.rs new file mode 100644 index 0000000000000..d26200295cdcf --- /dev/null +++ b/src/test/ui/macros/issue-83340.rs @@ -0,0 +1,8 @@ +// check-fail + +fn main() { + println!( + "\ +\n {} │", //~ ERROR: 1 positional argument in format string, but no arguments were given + ); +} diff --git a/src/test/ui/macros/issue-83340.stderr b/src/test/ui/macros/issue-83340.stderr new file mode 100644 index 0000000000000..1935de02b57a9 --- /dev/null +++ b/src/test/ui/macros/issue-83340.stderr @@ -0,0 +1,8 @@ +error: 1 positional argument in format string, but no arguments were given + --> $DIR/issue-83340.rs:6:4 + | +LL | \n {} │", + | ^^ + +error: aborting due to previous error + diff --git a/src/test/ui/macros/issue-83344.rs b/src/test/ui/macros/issue-83344.rs new file mode 100644 index 0000000000000..c5f7f72358780 --- /dev/null +++ b/src/test/ui/macros/issue-83344.rs @@ -0,0 +1,6 @@ +// check-fail + +fn main() { + println!("{}\ +"); //~^ ERROR: 1 positional argument in format string, but no arguments were given +} diff --git a/src/test/ui/macros/issue-83344.stderr b/src/test/ui/macros/issue-83344.stderr new file mode 100644 index 0000000000000..1ef70f87a1fb4 --- /dev/null +++ b/src/test/ui/macros/issue-83344.stderr @@ -0,0 +1,8 @@ +error: 1 positional argument in format string, but no arguments were given + --> $DIR/issue-83344.rs:4:15 + | +LL | println!("{}\ + | ^^ + +error: aborting due to previous error + diff --git a/src/test/ui/macros/issue-84195-lint-anon-const.rs b/src/test/ui/macros/issue-84195-lint-anon-const.rs new file mode 100644 index 0000000000000..71c7683201528 --- /dev/null +++ b/src/test/ui/macros/issue-84195-lint-anon-const.rs @@ -0,0 +1,14 @@ +// Regression test for issue #84195 +// Checks that we properly fire lints that occur inside +// anon consts. + +#![deny(semicolon_in_expressions_from_macros)] + +macro_rules! len { + () => { 0; }; //~ ERROR trailing semicolon + //~| WARN this was previously accepted +} + +fn main() { + let val: [u8; len!()] = []; +} diff --git a/src/test/ui/macros/issue-84195-lint-anon-const.stderr b/src/test/ui/macros/issue-84195-lint-anon-const.stderr new file mode 100644 index 0000000000000..558e0349e2f32 --- /dev/null +++ b/src/test/ui/macros/issue-84195-lint-anon-const.stderr @@ -0,0 +1,20 @@ +error: trailing semicolon in macro used in expression position + --> $DIR/issue-84195-lint-anon-const.rs:8:14 + | +LL | () => { 0; }; + | ^ +... +LL | let val: [u8; len!()] = []; + | ------ in this macro invocation + | +note: the lint level is defined here + --> $DIR/issue-84195-lint-anon-const.rs:5:9 + | +LL | #![deny(semicolon_in_expressions_from_macros)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #79813 <https://github.com/rust-lang/rust/issues/79813> + = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: aborting due to previous error + diff --git a/src/test/ui/macros/macro-or-patterns-back-compat.fixed b/src/test/ui/macros/macro-or-patterns-back-compat.fixed new file mode 100644 index 0000000000000..f089f0fda4e77 --- /dev/null +++ b/src/test/ui/macros/macro-or-patterns-back-compat.fixed @@ -0,0 +1,25 @@ +// run-rustfix + +#![feature(edition_macro_pats)] +#![deny(or_patterns_back_compat)] +#![allow(unused_macros)] +macro_rules! foo { ($x:pat2015 | $y:pat) => {} } //~ ERROR the meaning of the `pat` fragment specifier is changing in Rust 2021, which may affect this macro +macro_rules! bar { ($($x:pat2015)+ | $($y:pat)+) => {} } //~ ERROR the meaning of the `pat` fragment specifier is changing in Rust 2021, which may affect this macro +macro_rules! baz { ($x:pat2015 | $y:pat2015) => {} } // should be ok +macro_rules! qux { ($x:pat2015 | $y:pat) => {} } // should be ok +macro_rules! ogg { ($x:pat2015 | $y:pat2015) => {} } //~ ERROR the meaning of the `pat` fragment specifier is changing in Rust 2021, which may affect this macro +macro_rules! match_any { + ( $expr:expr , $( $( $pat:pat2015 )|+ => $expr_arm:expr ),+ ) => { //~ ERROR the meaning of the `pat` fragment specifier is changing in Rust 2021, which may affect this macro + match $expr { + $( + $( $pat => $expr_arm, )+ + )+ + } + }; +} + +fn main() { + let result: Result<i64, i32> = Err(42); + let int: i64 = match_any!(result, Ok(i) | Err(i) => i.into()); + assert_eq!(int, 42); +} diff --git a/src/test/ui/macros/macro-or-patterns-back-compat.rs b/src/test/ui/macros/macro-or-patterns-back-compat.rs new file mode 100644 index 0000000000000..0252581d5f168 --- /dev/null +++ b/src/test/ui/macros/macro-or-patterns-back-compat.rs @@ -0,0 +1,25 @@ +// run-rustfix + +#![feature(edition_macro_pats)] +#![deny(or_patterns_back_compat)] +#![allow(unused_macros)] +macro_rules! foo { ($x:pat | $y:pat) => {} } //~ ERROR the meaning of the `pat` fragment specifier is changing in Rust 2021, which may affect this macro +macro_rules! bar { ($($x:pat)+ | $($y:pat)+) => {} } //~ ERROR the meaning of the `pat` fragment specifier is changing in Rust 2021, which may affect this macro +macro_rules! baz { ($x:pat2015 | $y:pat2015) => {} } // should be ok +macro_rules! qux { ($x:pat2015 | $y:pat) => {} } // should be ok +macro_rules! ogg { ($x:pat | $y:pat2015) => {} } //~ ERROR the meaning of the `pat` fragment specifier is changing in Rust 2021, which may affect this macro +macro_rules! match_any { + ( $expr:expr , $( $( $pat:pat )|+ => $expr_arm:expr ),+ ) => { //~ ERROR the meaning of the `pat` fragment specifier is changing in Rust 2021, which may affect this macro + match $expr { + $( + $( $pat => $expr_arm, )+ + )+ + } + }; +} + +fn main() { + let result: Result<i64, i32> = Err(42); + let int: i64 = match_any!(result, Ok(i) | Err(i) => i.into()); + assert_eq!(int, 42); +} diff --git a/src/test/ui/macros/macro-or-patterns-back-compat.stderr b/src/test/ui/macros/macro-or-patterns-back-compat.stderr new file mode 100644 index 0000000000000..d8f19fa580779 --- /dev/null +++ b/src/test/ui/macros/macro-or-patterns-back-compat.stderr @@ -0,0 +1,32 @@ +error: the meaning of the `pat` fragment specifier is changing in Rust 2021, which may affect this macro + --> $DIR/macro-or-patterns-back-compat.rs:6:21 + | +LL | macro_rules! foo { ($x:pat | $y:pat) => {} } + | ^^^^^^ help: use pat2015 to preserve semantics: `$x:pat2015` + | +note: the lint level is defined here + --> $DIR/macro-or-patterns-back-compat.rs:4:9 + | +LL | #![deny(or_patterns_back_compat)] + | ^^^^^^^^^^^^^^^^^^^^^^^ + +error: the meaning of the `pat` fragment specifier is changing in Rust 2021, which may affect this macro + --> $DIR/macro-or-patterns-back-compat.rs:7:23 + | +LL | macro_rules! bar { ($($x:pat)+ | $($y:pat)+) => {} } + | ^^^^^^ help: use pat2015 to preserve semantics: `$x:pat2015` + +error: the meaning of the `pat` fragment specifier is changing in Rust 2021, which may affect this macro + --> $DIR/macro-or-patterns-back-compat.rs:10:21 + | +LL | macro_rules! ogg { ($x:pat | $y:pat2015) => {} } + | ^^^^^^ help: use pat2015 to preserve semantics: `$x:pat2015` + +error: the meaning of the `pat` fragment specifier is changing in Rust 2021, which may affect this macro + --> $DIR/macro-or-patterns-back-compat.rs:12:26 + | +LL | ( $expr:expr , $( $( $pat:pat )|+ => $expr_arm:expr ),+ ) => { + | ^^^^^^^^ help: use pat2015 to preserve semantics: `$pat:pat2015` + +error: aborting due to 4 previous errors + diff --git a/src/test/ui/macros/macro-pat-pattern-followed-by-or-in-2021.rs b/src/test/ui/macros/macro-pat-pattern-followed-by-or-in-2021.rs new file mode 100644 index 0000000000000..f5a97eca21bdb --- /dev/null +++ b/src/test/ui/macros/macro-pat-pattern-followed-by-or-in-2021.rs @@ -0,0 +1,20 @@ +// edition:2021 +#![allow(unused_macros)] +macro_rules! foo { ($x:pat | $y:pat) => {} } //~ ERROR `$x:pat` is followed by `|`, which is not allowed for `pat` fragments +macro_rules! bar { ($($x:pat)+ | $($y:pat)+) => {} } //~ ERROR `$x:pat` is followed by `|`, which is not allowed for `pat` fragments +macro_rules! qux { ($x:pat, $y:pat) => {} } // should be ok +macro_rules! match_any { + ( $expr:expr , $( $( $pat:pat )|+ => $expr_arm:expr ),+ ) => { //~ ERROR `$pat:pat` may be followed by `|`, which is not allowed for `pat` fragments + match $expr { + $( + $( $pat => $expr_arm, )+ + )+ + } + }; +} + +fn main() { + let result: Result<i64, i32> = Err(42); + let int: i64 = match_any!(result, Ok(i) | Err(i) => i.into()); + assert_eq!(int, 42); +} diff --git a/src/test/ui/macros/macro-pat-pattern-followed-by-or-in-2021.stderr b/src/test/ui/macros/macro-pat-pattern-followed-by-or-in-2021.stderr new file mode 100644 index 0000000000000..a5987a25551d2 --- /dev/null +++ b/src/test/ui/macros/macro-pat-pattern-followed-by-or-in-2021.stderr @@ -0,0 +1,26 @@ +error: `$x:pat` is followed by `|`, which is not allowed for `pat` fragments + --> $DIR/macro-pat-pattern-followed-by-or-in-2021.rs:3:28 + | +LL | macro_rules! foo { ($x:pat | $y:pat) => {} } + | ^ not allowed after `pat` fragments + | + = note: allowed there are: `=>`, `,`, `=`, `if` or `in` + +error: `$x:pat` is followed by `|`, which is not allowed for `pat` fragments + --> $DIR/macro-pat-pattern-followed-by-or-in-2021.rs:4:32 + | +LL | macro_rules! bar { ($($x:pat)+ | $($y:pat)+) => {} } + | ^ not allowed after `pat` fragments + | + = note: allowed there are: `=>`, `,`, `=`, `if` or `in` + +error: `$pat:pat` may be followed by `|`, which is not allowed for `pat` fragments + --> $DIR/macro-pat-pattern-followed-by-or-in-2021.rs:7:36 + | +LL | ( $expr:expr , $( $( $pat:pat )|+ => $expr_arm:expr ),+ ) => { + | ^ not allowed after `pat` fragments + | + = note: allowed there are: `=>`, `,`, `=`, `if` or `in` + +error: aborting due to 3 previous errors + diff --git a/src/test/ui/macros/macro-pat-pattern-followed-by-or.rs b/src/test/ui/macros/macro-pat-pattern-followed-by-or.rs new file mode 100644 index 0000000000000..54bd13d7ebce8 --- /dev/null +++ b/src/test/ui/macros/macro-pat-pattern-followed-by-or.rs @@ -0,0 +1,20 @@ +// run-pass +#![allow(unused_macros)] +macro_rules! foo { ($x:pat | $y:pat) => {} } // should be ok +macro_rules! bar { ($($x:pat)+ | $($y:pat)+) => {} } // should be ok +macro_rules! qux { ($x:pat, $y:pat) => {} } // should be ok +macro_rules! match_any { + ( $expr:expr , $( $( $pat:pat )|+ => $expr_arm:expr ),+ ) => { // should be ok + match $expr { + $( + $( $pat => $expr_arm, )+ + )+ + } + }; +} + +fn main() { + let result: Result<i64, i32> = Err(42); + let int: i64 = match_any!(result, Ok(i) | Err(i) => i.into()); + assert_eq!(int, 42); +} diff --git a/src/test/ui/macros/macro-pat2021-pattern-followed-by-or.rs b/src/test/ui/macros/macro-pat2021-pattern-followed-by-or.rs new file mode 100644 index 0000000000000..edd3f3e7646b8 --- /dev/null +++ b/src/test/ui/macros/macro-pat2021-pattern-followed-by-or.rs @@ -0,0 +1,21 @@ +#![feature(edition_macro_pats)] +#![allow(unused_macros)] +macro_rules! foo { ($x:pat2021 | $y:pat2021) => {} } //~ ERROR `$x:pat2021` is followed by `|`, which is not allowed for `pat2021` fragments +macro_rules! baz { ($x:pat2015 | $y:pat2015) => {} } // should be ok +macro_rules! qux { ($x:pat2015 | $y:pat2021) => {} } // should be ok +macro_rules! ogg { ($x:pat2021 | $y:pat2015) => {} } //~ ERROR `$x:pat2021` is followed by `|`, which is not allowed for `pat2021` fragments +macro_rules! match_any { + ( $expr:expr , $( $( $pat:pat2021 )|+ => $expr_arm:pat2021 ),+ ) => { //~ ERROR `$pat:pat2021` may be followed by `|`, which is not allowed for `pat2021` fragments + match $expr { + $( + $( $pat => $expr_arm, )+ + )+ + } + }; +} + +fn main() { + let result: Result<i64, i32> = Err(42); + let int: i64 = match_any!(result, Ok(i) | Err(i) => i.into()); + assert_eq!(int, 42); +} diff --git a/src/test/ui/macros/macro-pat2021-pattern-followed-by-or.stderr b/src/test/ui/macros/macro-pat2021-pattern-followed-by-or.stderr new file mode 100644 index 0000000000000..fe0b40cd86e12 --- /dev/null +++ b/src/test/ui/macros/macro-pat2021-pattern-followed-by-or.stderr @@ -0,0 +1,26 @@ +error: `$x:pat2021` is followed by `|`, which is not allowed for `pat2021` fragments + --> $DIR/macro-pat2021-pattern-followed-by-or.rs:3:32 + | +LL | macro_rules! foo { ($x:pat2021 | $y:pat2021) => {} } + | ^ not allowed after `pat2021` fragments + | + = note: allowed there are: `=>`, `,`, `=`, `if` or `in` + +error: `$x:pat2021` is followed by `|`, which is not allowed for `pat2021` fragments + --> $DIR/macro-pat2021-pattern-followed-by-or.rs:6:32 + | +LL | macro_rules! ogg { ($x:pat2021 | $y:pat2015) => {} } + | ^ not allowed after `pat2021` fragments + | + = note: allowed there are: `=>`, `,`, `=`, `if` or `in` + +error: `$pat:pat2021` may be followed by `|`, which is not allowed for `pat2021` fragments + --> $DIR/macro-pat2021-pattern-followed-by-or.rs:8:40 + | +LL | ( $expr:expr , $( $( $pat:pat2021 )|+ => $expr_arm:pat2021 ),+ ) => { + | ^ not allowed after `pat2021` fragments + | + = note: allowed there are: `=>`, `,`, `=`, `if` or `in` + +error: aborting due to 3 previous errors + diff --git a/src/test/ui/macros/none-delim-lookahead.rs b/src/test/ui/macros/none-delim-lookahead.rs new file mode 100644 index 0000000000000..bf4fddea14ba2 --- /dev/null +++ b/src/test/ui/macros/none-delim-lookahead.rs @@ -0,0 +1,15 @@ +// check-pass + +macro_rules! make_struct { + ($name:ident) => { + #[derive(Debug)] + struct Foo { + #[cfg(not(FALSE))] + field: fn($name: bool) + } + } +} + +make_struct!(param_name); + +fn main() {} diff --git a/src/test/ui/main-wrong-location.rs b/src/test/ui/main-wrong-location.rs index f75d08813cdcd..f3acd80a7a48f 100644 --- a/src/test/ui/main-wrong-location.rs +++ b/src/test/ui/main-wrong-location.rs @@ -1,6 +1,6 @@ mod m { //~^ ERROR `main` function not found - // An inferred main entry point (that doesn't use #[main]) + // An inferred main entry point // must appear at the top of the crate fn main() { } } diff --git a/src/test/ui/main-wrong-location.stderr b/src/test/ui/main-wrong-location.stderr index e301c2ff09ad7..754ff0f80eb9a 100644 --- a/src/test/ui/main-wrong-location.stderr +++ b/src/test/ui/main-wrong-location.stderr @@ -3,7 +3,7 @@ error[E0601]: `main` function not found in crate `main_wrong_location` | LL | / mod m { LL | | -LL | | // An inferred main entry point (that doesn't use #[main]) +LL | | // An inferred main entry point LL | | // must appear at the top of the crate LL | | fn main() { } LL | | } @@ -15,7 +15,7 @@ note: here is a function named `main` LL | fn main() { } | ^^^^^^^^^^^^^ = note: you have one or more functions named `main` not defined at the crate level - = help: either move the `main` function definitions or attach the `#[main]` attribute to one of them + = help: consider moving the `main` function definitions error: aborting due to previous error diff --git a/src/test/ui/match/issue-72680.rs b/src/test/ui/match/issue-72680.rs index 5b933edc8205e..c13cace760086 100644 --- a/src/test/ui/match/issue-72680.rs +++ b/src/test/ui/match/issue-72680.rs @@ -1,7 +1,5 @@ // run-pass -#![feature(or_patterns)] - fn main() { assert!(f("", 0)); assert!(f("a", 1)); diff --git a/src/test/ui/methods/method-call-lifetime-args-fail.rs b/src/test/ui/methods/method-call-lifetime-args-fail.rs index 8a840ba62cc0d..af1738512527c 100644 --- a/src/test/ui/methods/method-call-lifetime-args-fail.rs +++ b/src/test/ui/methods/method-call-lifetime-args-fail.rs @@ -1,5 +1,3 @@ -// ignore-tidy-linelength - struct S; impl S { diff --git a/src/test/ui/methods/method-call-lifetime-args-fail.stderr b/src/test/ui/methods/method-call-lifetime-args-fail.stderr index 34a2e3dec2ecf..2907309c27c7b 100644 --- a/src/test/ui/methods/method-call-lifetime-args-fail.stderr +++ b/src/test/ui/methods/method-call-lifetime-args-fail.stderr @@ -1,5 +1,5 @@ error[E0107]: this associated function takes 2 lifetime arguments but only 1 lifetime argument was supplied - --> $DIR/method-call-lifetime-args-fail.rs:18:7 + --> $DIR/method-call-lifetime-args-fail.rs:16:7 | LL | S.early::<'static>(); | ^^^^^ ------- supplied 1 lifetime argument @@ -7,7 +7,7 @@ LL | S.early::<'static>(); | expected 2 lifetime arguments | note: associated function defined here, with 2 lifetime parameters: `'a`, `'b` - --> $DIR/method-call-lifetime-args-fail.rs:8:8 + --> $DIR/method-call-lifetime-args-fail.rs:6:8 | LL | fn early<'a, 'b>(self) -> (&'a u8, &'b u8) { loop {} } | ^^^^^ -- -- @@ -17,7 +17,7 @@ LL | S.early::<'static, 'b>(); | ^^^^ error[E0107]: this associated function takes 2 lifetime arguments but 3 lifetime arguments were supplied - --> $DIR/method-call-lifetime-args-fail.rs:20:7 + --> $DIR/method-call-lifetime-args-fail.rs:18:7 | LL | S.early::<'static, 'static, 'static>(); | ^^^^^ --------- help: remove this lifetime argument @@ -25,181 +25,181 @@ LL | S.early::<'static, 'static, 'static>(); | expected 2 lifetime arguments | note: associated function defined here, with 2 lifetime parameters: `'a`, `'b` - --> $DIR/method-call-lifetime-args-fail.rs:8:8 + --> $DIR/method-call-lifetime-args-fail.rs:6:8 | LL | fn early<'a, 'b>(self) -> (&'a u8, &'b u8) { loop {} } | ^^^^^ -- -- error: cannot specify lifetime arguments explicitly if late bound lifetime parameters are present - --> $DIR/method-call-lifetime-args-fail.rs:29:15 + --> $DIR/method-call-lifetime-args-fail.rs:27:15 | LL | S::late::<'static>(S, &0, &0); | ^^^^^^^ | note: the late bound lifetime parameter is introduced here - --> $DIR/method-call-lifetime-args-fail.rs:6:13 + --> $DIR/method-call-lifetime-args-fail.rs:4:13 | LL | fn late<'a, 'b>(self, _: &'a u8, _: &'b u8) {} | ^^ error: cannot specify lifetime arguments explicitly if late bound lifetime parameters are present - --> $DIR/method-call-lifetime-args-fail.rs:31:15 + --> $DIR/method-call-lifetime-args-fail.rs:29:15 | LL | S::late::<'static, 'static>(S, &0, &0); | ^^^^^^^ | note: the late bound lifetime parameter is introduced here - --> $DIR/method-call-lifetime-args-fail.rs:6:13 + --> $DIR/method-call-lifetime-args-fail.rs:4:13 | LL | fn late<'a, 'b>(self, _: &'a u8, _: &'b u8) {} | ^^ error: cannot specify lifetime arguments explicitly if late bound lifetime parameters are present - --> $DIR/method-call-lifetime-args-fail.rs:33:15 + --> $DIR/method-call-lifetime-args-fail.rs:31:15 | LL | S::late::<'static, 'static, 'static>(S, &0, &0); | ^^^^^^^ | note: the late bound lifetime parameter is introduced here - --> $DIR/method-call-lifetime-args-fail.rs:6:13 + --> $DIR/method-call-lifetime-args-fail.rs:4:13 | LL | fn late<'a, 'b>(self, _: &'a u8, _: &'b u8) {} | ^^ error: cannot specify lifetime arguments explicitly if late bound lifetime parameters are present - --> $DIR/method-call-lifetime-args-fail.rs:36:21 + --> $DIR/method-call-lifetime-args-fail.rs:34:21 | LL | S::late_early::<'static, 'static>(S, &0); | ^^^^^^^ | note: the late bound lifetime parameter is introduced here - --> $DIR/method-call-lifetime-args-fail.rs:9:19 + --> $DIR/method-call-lifetime-args-fail.rs:7:19 | LL | fn late_early<'a, 'b>(self, _: &'a u8) -> &'b u8 { loop {} } | ^^ error: cannot specify lifetime arguments explicitly if late bound lifetime parameters are present - --> $DIR/method-call-lifetime-args-fail.rs:38:21 + --> $DIR/method-call-lifetime-args-fail.rs:36:21 | LL | S::late_early::<'static, 'static, 'static>(S, &0); | ^^^^^^^ | note: the late bound lifetime parameter is introduced here - --> $DIR/method-call-lifetime-args-fail.rs:9:19 + --> $DIR/method-call-lifetime-args-fail.rs:7:19 | LL | fn late_early<'a, 'b>(self, _: &'a u8) -> &'b u8 { loop {} } | ^^ error: cannot specify lifetime arguments explicitly if late bound lifetime parameters are present - --> $DIR/method-call-lifetime-args-fail.rs:42:24 + --> $DIR/method-call-lifetime-args-fail.rs:40:24 | LL | S::late_implicit::<'static>(S, &0, &0); | ^^^^^^^ | note: the late bound lifetime parameter is introduced here - --> $DIR/method-call-lifetime-args-fail.rs:7:31 + --> $DIR/method-call-lifetime-args-fail.rs:5:31 | LL | fn late_implicit(self, _: &u8, _: &u8) {} | ^ error: cannot specify lifetime arguments explicitly if late bound lifetime parameters are present - --> $DIR/method-call-lifetime-args-fail.rs:44:24 + --> $DIR/method-call-lifetime-args-fail.rs:42:24 | LL | S::late_implicit::<'static, 'static>(S, &0, &0); | ^^^^^^^ | note: the late bound lifetime parameter is introduced here - --> $DIR/method-call-lifetime-args-fail.rs:7:31 + --> $DIR/method-call-lifetime-args-fail.rs:5:31 | LL | fn late_implicit(self, _: &u8, _: &u8) {} | ^ error: cannot specify lifetime arguments explicitly if late bound lifetime parameters are present - --> $DIR/method-call-lifetime-args-fail.rs:46:24 + --> $DIR/method-call-lifetime-args-fail.rs:44:24 | LL | S::late_implicit::<'static, 'static, 'static>(S, &0, &0); | ^^^^^^^ | note: the late bound lifetime parameter is introduced here - --> $DIR/method-call-lifetime-args-fail.rs:7:31 + --> $DIR/method-call-lifetime-args-fail.rs:5:31 | LL | fn late_implicit(self, _: &u8, _: &u8) {} | ^ error: cannot specify lifetime arguments explicitly if late bound lifetime parameters are present - --> $DIR/method-call-lifetime-args-fail.rs:49:30 + --> $DIR/method-call-lifetime-args-fail.rs:47:30 | LL | S::late_implicit_early::<'static, 'static>(S, &0); | ^^^^^^^ | note: the late bound lifetime parameter is introduced here - --> $DIR/method-call-lifetime-args-fail.rs:10:41 + --> $DIR/method-call-lifetime-args-fail.rs:8:41 | LL | fn late_implicit_early<'b>(self, _: &u8) -> &'b u8 { loop {} } | ^ error: cannot specify lifetime arguments explicitly if late bound lifetime parameters are present - --> $DIR/method-call-lifetime-args-fail.rs:51:30 + --> $DIR/method-call-lifetime-args-fail.rs:49:30 | LL | S::late_implicit_early::<'static, 'static, 'static>(S, &0); | ^^^^^^^ | note: the late bound lifetime parameter is introduced here - --> $DIR/method-call-lifetime-args-fail.rs:10:41 + --> $DIR/method-call-lifetime-args-fail.rs:8:41 | LL | fn late_implicit_early<'b>(self, _: &u8) -> &'b u8 { loop {} } | ^ error: cannot specify lifetime arguments explicitly if late bound lifetime parameters are present - --> $DIR/method-call-lifetime-args-fail.rs:54:35 + --> $DIR/method-call-lifetime-args-fail.rs:52:35 | LL | S::late_implicit_self_early::<'static, 'static>(&S); | ^^^^^^^ | note: the late bound lifetime parameter is introduced here - --> $DIR/method-call-lifetime-args-fail.rs:11:37 + --> $DIR/method-call-lifetime-args-fail.rs:9:37 | LL | fn late_implicit_self_early<'b>(&self) -> &'b u8 { loop {} } | ^ error: cannot specify lifetime arguments explicitly if late bound lifetime parameters are present - --> $DIR/method-call-lifetime-args-fail.rs:56:35 + --> $DIR/method-call-lifetime-args-fail.rs:54:35 | LL | S::late_implicit_self_early::<'static, 'static, 'static>(&S); | ^^^^^^^ | note: the late bound lifetime parameter is introduced here - --> $DIR/method-call-lifetime-args-fail.rs:11:37 + --> $DIR/method-call-lifetime-args-fail.rs:9:37 | LL | fn late_implicit_self_early<'b>(&self) -> &'b u8 { loop {} } | ^ error: cannot specify lifetime arguments explicitly if late bound lifetime parameters are present - --> $DIR/method-call-lifetime-args-fail.rs:59:28 + --> $DIR/method-call-lifetime-args-fail.rs:57:28 | LL | S::late_unused_early::<'static, 'static>(S); | ^^^^^^^ | note: the late bound lifetime parameter is introduced here - --> $DIR/method-call-lifetime-args-fail.rs:12:26 + --> $DIR/method-call-lifetime-args-fail.rs:10:26 | LL | fn late_unused_early<'a, 'b>(self) -> &'b u8 { loop {} } | ^^ error: cannot specify lifetime arguments explicitly if late bound lifetime parameters are present - --> $DIR/method-call-lifetime-args-fail.rs:61:28 + --> $DIR/method-call-lifetime-args-fail.rs:59:28 | LL | S::late_unused_early::<'static, 'static, 'static>(S); | ^^^^^^^ | note: the late bound lifetime parameter is introduced here - --> $DIR/method-call-lifetime-args-fail.rs:12:26 + --> $DIR/method-call-lifetime-args-fail.rs:10:26 | LL | fn late_unused_early<'a, 'b>(self) -> &'b u8 { loop {} } | ^^ error[E0107]: this associated function takes 2 lifetime arguments but only 1 lifetime argument was supplied - --> $DIR/method-call-lifetime-args-fail.rs:65:8 + --> $DIR/method-call-lifetime-args-fail.rs:63:8 | LL | S::early::<'static>(S); | ^^^^^ ------- supplied 1 lifetime argument @@ -207,7 +207,7 @@ LL | S::early::<'static>(S); | expected 2 lifetime arguments | note: associated function defined here, with 2 lifetime parameters: `'a`, `'b` - --> $DIR/method-call-lifetime-args-fail.rs:8:8 + --> $DIR/method-call-lifetime-args-fail.rs:6:8 | LL | fn early<'a, 'b>(self) -> (&'a u8, &'b u8) { loop {} } | ^^^^^ -- -- @@ -217,7 +217,7 @@ LL | S::early::<'static, 'b>(S); | ^^^^ error[E0107]: this associated function takes 2 lifetime arguments but 3 lifetime arguments were supplied - --> $DIR/method-call-lifetime-args-fail.rs:67:8 + --> $DIR/method-call-lifetime-args-fail.rs:65:8 | LL | S::early::<'static, 'static, 'static>(S); | ^^^^^ --------- help: remove this lifetime argument @@ -225,7 +225,7 @@ LL | S::early::<'static, 'static, 'static>(S); | expected 2 lifetime arguments | note: associated function defined here, with 2 lifetime parameters: `'a`, `'b` - --> $DIR/method-call-lifetime-args-fail.rs:8:8 + --> $DIR/method-call-lifetime-args-fail.rs:6:8 | LL | fn early<'a, 'b>(self) -> (&'a u8, &'b u8) { loop {} } | ^^^^^ -- -- diff --git a/src/test/ui/mir/auxiliary/issue_76375_aux.rs b/src/test/ui/mir/auxiliary/issue_76375_aux.rs index 72f32ecf7ea22..90f4df739f12a 100644 --- a/src/test/ui/mir/auxiliary/issue_76375_aux.rs +++ b/src/test/ui/mir/auxiliary/issue_76375_aux.rs @@ -1,5 +1,5 @@ // edition:2018 -// compile-flags: -Z mir-opt-level=2 +// compile-flags: -Z mir-opt-level=3 #[inline(always)] pub fn copy_prop(s: bool) -> String { diff --git a/src/test/ui/mir/issue-66851.rs b/src/test/ui/mir/issue-66851.rs index 72d62a30a337a..878ad4e475a1e 100644 --- a/src/test/ui/mir/issue-66851.rs +++ b/src/test/ui/mir/issue-66851.rs @@ -2,7 +2,7 @@ // did not check that the types matched up in the `Ok(r)` branch. // // run-pass -// compile-flags: -Zmir-opt-level=2 +// compile-flags: -Zmir-opt-level=3 #[derive(Debug, PartialEq, Eq)] enum SpecialsRes { Res(u64) } diff --git a/src/test/ui/mir/issue-67639-normalization-ice.rs b/src/test/ui/mir/issue-67639-normalization-ice.rs index 21851a725254f..71150a80bc0a1 100644 --- a/src/test/ui/mir/issue-67639-normalization-ice.rs +++ b/src/test/ui/mir/issue-67639-normalization-ice.rs @@ -1,4 +1,4 @@ -// compile-flags: -Z mir-opt-level=3 +// compile-flags: -Z mir-opt-level=4 // build-pass // This used to ICE in const-prop due diff --git a/src/test/ui/mir/issue-67710-inline-projection.rs b/src/test/ui/mir/issue-67710-inline-projection.rs index 37d8f2eac9be1..1ff6b4d628c8b 100644 --- a/src/test/ui/mir/issue-67710-inline-projection.rs +++ b/src/test/ui/mir/issue-67710-inline-projection.rs @@ -1,4 +1,4 @@ -// compile-flags: -Z mir-opt-level=2 +// compile-flags: -Z mir-opt-level=3 // build-pass // This used to ICE due to the inling pass not examining projections diff --git a/src/test/ui/mir/issue-68841.rs b/src/test/ui/mir/issue-68841.rs index 14884a97fabcb..550bd452a8093 100644 --- a/src/test/ui/mir/issue-68841.rs +++ b/src/test/ui/mir/issue-68841.rs @@ -1,4 +1,4 @@ -// compile-flags: -Z mir-opt-level=2 +// compile-flags: -Z mir-opt-level=3 // edition:2018 // build-pass diff --git a/src/test/ui/mir/issue-71793-inline-args-storage.rs b/src/test/ui/mir/issue-71793-inline-args-storage.rs index 87b2806d4e208..18f2e38d14c4a 100644 --- a/src/test/ui/mir/issue-71793-inline-args-storage.rs +++ b/src/test/ui/mir/issue-71793-inline-args-storage.rs @@ -4,7 +4,7 @@ // // check-pass // edition:2018 -// compile-args: -Zmir-opt-level=2 +// compile-args: -Zmir-opt-level=3 #![crate_type = "lib"] diff --git a/src/test/ui/mir/issue-75053.full_tait.stderr b/src/test/ui/mir/issue-75053.full_tait.stderr new file mode 100644 index 0000000000000..aff19094b7af8 --- /dev/null +++ b/src/test/ui/mir/issue-75053.full_tait.stderr @@ -0,0 +1,21 @@ +warning: the feature `type_alias_impl_trait` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/issue-75053.rs:5:32 + | +LL | #![cfg_attr(full_tait, feature(type_alias_impl_trait))] + | ^^^^^^^^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #63063 <https://github.com/rust-lang/rust/issues/63063> for more information + +error[E0658]: type alias impl trait is not permitted here + --> $DIR/issue-75053.rs:52:15 + | +LL | let _pos: Phantom1<DummyT<()>> = Scope::new().my_index(); + | ^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #63065 <https://github.com/rust-lang/rust/issues/63065> for more information + = help: add `#![feature(impl_trait_in_bindings)]` to the crate attributes to enable + +error: aborting due to previous error; 1 warning emitted + +For more information about this error, try `rustc --explain E0658`. diff --git a/src/test/ui/mir/issue-75053.in_bindings.stderr b/src/test/ui/mir/issue-75053.in_bindings.stderr new file mode 100644 index 0000000000000..a43fabc8f5d91 --- /dev/null +++ b/src/test/ui/mir/issue-75053.in_bindings.stderr @@ -0,0 +1,24 @@ +warning: the feature `impl_trait_in_bindings` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/issue-75053.rs:7:34 + | +LL | #![cfg_attr(in_bindings, feature(impl_trait_in_bindings))] + | ^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #63065 <https://github.com/rust-lang/rust/issues/63065> for more information + +error[E0282]: type annotations needed + --> $DIR/issue-75053.rs:52:38 + | +LL | type O; + | ------- `<Self as MyIndex<T>>::O` defined here +... +LL | let _pos: Phantom1<DummyT<()>> = Scope::new().my_index(); + | ^^^^^^^^^^------------- + | | + | this method call resolves to `<Self as MyIndex<T>>::O` + | cannot infer type for type parameter `T` + +error: aborting due to previous error; 1 warning emitted + +For more information about this error, try `rustc --explain E0282`. diff --git a/src/test/ui/mir/issue-75053.min_tait.stderr b/src/test/ui/mir/issue-75053.min_tait.stderr new file mode 100644 index 0000000000000..7ce91e851a755 --- /dev/null +++ b/src/test/ui/mir/issue-75053.min_tait.stderr @@ -0,0 +1,12 @@ +error[E0658]: type alias impl trait is not permitted here + --> $DIR/issue-75053.rs:52:15 + | +LL | let _pos: Phantom1<DummyT<()>> = Scope::new().my_index(); + | ^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #63065 <https://github.com/rust-lang/rust/issues/63065> for more information + = help: add `#![feature(impl_trait_in_bindings)]` to the crate attributes to enable + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0658`. diff --git a/src/test/ui/mir/issue-75053.rs b/src/test/ui/mir/issue-75053.rs index 6e7211c2ee60e..89ae3ca3006b9 100644 --- a/src/test/ui/mir/issue-75053.rs +++ b/src/test/ui/mir/issue-75053.rs @@ -1,7 +1,11 @@ -// compile-flags: -Z mir-opt-level=2 -// build-pass +// compile-flags: -Z mir-opt-level=3 -#![feature(type_alias_impl_trait)] +// revisions: min_tait full_tait in_bindings +#![feature(min_type_alias_impl_trait, rustc_attrs)] +#![cfg_attr(full_tait, feature(type_alias_impl_trait))] +//[full_tait]~^ WARN incomplete +#![cfg_attr(in_bindings, feature(impl_trait_in_bindings))] +//[in_bindings]~^ WARN incomplete use std::marker::PhantomData; @@ -43,6 +47,9 @@ impl<T: MyFrom<Phantom2<DummyT<U>>>, U> MyIndex<Phantom1<T>> for Scope<U> { } } +#[rustc_error] fn main() { let _pos: Phantom1<DummyT<()>> = Scope::new().my_index(); + //[min_tait,full_tait]~^ ERROR not permitted here + //[in_bindings]~^^ ERROR type annotations needed } diff --git a/src/test/ui/mir/issue-76248.rs b/src/test/ui/mir/issue-76248.rs index b01a9727852cd..18473e79e86a5 100644 --- a/src/test/ui/mir/issue-76248.rs +++ b/src/test/ui/mir/issue-76248.rs @@ -3,7 +3,7 @@ // Regression test for #76248. // // build-pass -// compile-flags: -Zmir-opt-level=2 +// compile-flags: -Zmir-opt-level=3 const N: usize = 1; diff --git a/src/test/ui/mir/issue-76375.rs b/src/test/ui/mir/issue-76375.rs index a7772cb1fe68d..e635caca9fdf4 100644 --- a/src/test/ui/mir/issue-76375.rs +++ b/src/test/ui/mir/issue-76375.rs @@ -2,7 +2,7 @@ // // edition:2018 // build-pass -// compile-flags: -Z mir-opt-level=2 +// compile-flags: -Z mir-opt-level=3 // aux-build:issue_76375_aux.rs #![crate_type = "lib"] diff --git a/src/test/ui/mir/issue-76740-copy-propagation.rs b/src/test/ui/mir/issue-76740-copy-propagation.rs index 90999a3e5564b..1d4ec11762afd 100644 --- a/src/test/ui/mir/issue-76740-copy-propagation.rs +++ b/src/test/ui/mir/issue-76740-copy-propagation.rs @@ -1,6 +1,6 @@ // Regression test for issue #76740. // run-pass -// compile-flags: -Zmir-opt-level=3 +// compile-flags: -Zmir-opt-level=4 #[derive(Copy, Clone)] pub struct V([usize; 4]); diff --git a/src/test/ui/mir/issue-77911.rs b/src/test/ui/mir/issue-77911.rs index fff303495e75b..acf4c20542d72 100644 --- a/src/test/ui/mir/issue-77911.rs +++ b/src/test/ui/mir/issue-77911.rs @@ -1,4 +1,4 @@ -// compile-flags: -Z mir-opt-level=2 +// compile-flags: -Z mir-opt-level=3 // build-pass use std::fs::File; diff --git a/src/test/ui/mir/issue-78496.rs b/src/test/ui/mir/issue-78496.rs index 1b0687cfac3f6..a0d1f5a780e0e 100644 --- a/src/test/ui/mir/issue-78496.rs +++ b/src/test/ui/mir/issue-78496.rs @@ -1,5 +1,5 @@ // run-pass -// compile-flags: -Z mir-opt-level=2 -C opt-level=0 +// compile-flags: -Z mir-opt-level=3 -C opt-level=0 // example from #78496 pub enum E<'a> { diff --git a/src/test/ui/mir/issue-80742.stderr b/src/test/ui/mir/issue-80742.stderr index 8cbd0220e6768..8400aab308e06 100644 --- a/src/test/ui/mir/issue-80742.stderr +++ b/src/test/ui/mir/issue-80742.stderr @@ -56,7 +56,7 @@ LL | struct Inline<T> | - required by this bound in `Inline` ... LL | let dst = Inline::<dyn Debug>::new(0); - | ^^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time + | ^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time | = help: the trait `Sized` is not implemented for `dyn Debug` help: consider relaxing the implicit `Sized` restriction diff --git a/src/test/ui/mir/issue-83499-input-output-iteration-ice.rs b/src/test/ui/mir/issue-83499-input-output-iteration-ice.rs new file mode 100644 index 0000000000000..4d404d015ec0b --- /dev/null +++ b/src/test/ui/mir/issue-83499-input-output-iteration-ice.rs @@ -0,0 +1,10 @@ +// Test that when in MIR the amount of local_decls and amount of normalized_input_tys don't match +// that an out-of-bounds access does not occur. +#![feature(c_variadic)] + +fn main() {} + +fn foo(_: Bar, ...) -> impl {} +//~^ ERROR only foreign or `unsafe extern "C" functions may be C-variadic +//~| ERROR cannot find type `Bar` in this scope +//~| ERROR at least one trait must be specified diff --git a/src/test/ui/mir/issue-83499-input-output-iteration-ice.stderr b/src/test/ui/mir/issue-83499-input-output-iteration-ice.stderr new file mode 100644 index 0000000000000..eb172684899cf --- /dev/null +++ b/src/test/ui/mir/issue-83499-input-output-iteration-ice.stderr @@ -0,0 +1,21 @@ +error: only foreign or `unsafe extern "C" functions may be C-variadic + --> $DIR/issue-83499-input-output-iteration-ice.rs:7:16 + | +LL | fn foo(_: Bar, ...) -> impl {} + | ^^^ + +error: at least one trait must be specified + --> $DIR/issue-83499-input-output-iteration-ice.rs:7:24 + | +LL | fn foo(_: Bar, ...) -> impl {} + | ^^^^ + +error[E0412]: cannot find type `Bar` in this scope + --> $DIR/issue-83499-input-output-iteration-ice.rs:7:11 + | +LL | fn foo(_: Bar, ...) -> impl {} + | ^^^ not found in this scope + +error: aborting due to 3 previous errors + +For more information about this error, try `rustc --explain E0412`. diff --git a/src/test/ui/mir/issue66339.rs b/src/test/ui/mir/issue66339.rs index 98e178c055146..2507af38cdf73 100644 --- a/src/test/ui/mir/issue66339.rs +++ b/src/test/ui/mir/issue66339.rs @@ -1,4 +1,4 @@ -// compile-flags: -Z mir-opt-level=2 +// compile-flags: -Z mir-opt-level=3 // build-pass // This used to ICE in const-prop diff --git a/src/test/ui/mir/mir-inlining/array-clone-with-generic-size.rs b/src/test/ui/mir/mir-inlining/array-clone-with-generic-size.rs index eec0a4599c30c..e36e8bd746d90 100644 --- a/src/test/ui/mir/mir-inlining/array-clone-with-generic-size.rs +++ b/src/test/ui/mir/mir-inlining/array-clone-with-generic-size.rs @@ -2,7 +2,7 @@ // Regression test for issue #79269. // // build-pass -// compile-flags: -Zmir-opt-level=2 -Zvalidate-mir +// compile-flags: -Zmir-opt-level=3 -Zvalidate-mir #[derive(Clone)] struct Array<T, const N: usize>([T; N]); diff --git a/src/test/ui/mir/mir-inlining/ice-issue-45493.rs b/src/test/ui/mir/mir-inlining/ice-issue-45493.rs index 1bd16dc43e135..04a23212e7b9f 100644 --- a/src/test/ui/mir/mir-inlining/ice-issue-45493.rs +++ b/src/test/ui/mir/mir-inlining/ice-issue-45493.rs @@ -1,5 +1,5 @@ // run-pass -// compile-flags:-Zmir-opt-level=2 +// compile-flags:-Zmir-opt-level=3 trait Array { type Item; diff --git a/src/test/ui/mir/mir-inlining/ice-issue-45885.rs b/src/test/ui/mir/mir-inlining/ice-issue-45885.rs index e930a4d1ccd1f..09b1279ef34e1 100644 --- a/src/test/ui/mir/mir-inlining/ice-issue-45885.rs +++ b/src/test/ui/mir/mir-inlining/ice-issue-45885.rs @@ -1,5 +1,5 @@ // run-pass -// compile-flags:-Zmir-opt-level=2 +// compile-flags:-Zmir-opt-level=3 pub enum Enum { A, diff --git a/src/test/ui/mir/mir-inlining/ice-issue-68347.rs b/src/test/ui/mir/mir-inlining/ice-issue-68347.rs index 88b80bc3333c1..7c135250940dc 100644 --- a/src/test/ui/mir/mir-inlining/ice-issue-68347.rs +++ b/src/test/ui/mir/mir-inlining/ice-issue-68347.rs @@ -1,5 +1,5 @@ // run-pass -// compile-flags:-Zmir-opt-level=2 +// compile-flags:-Zmir-opt-level=3 pub fn main() { let _x: fn() = handle_debug_column; } diff --git a/src/test/ui/mir/mir-inlining/ice-issue-77306-1.rs b/src/test/ui/mir/mir-inlining/ice-issue-77306-1.rs index 4d083bf232155..ef05ff9ce03b9 100644 --- a/src/test/ui/mir/mir-inlining/ice-issue-77306-1.rs +++ b/src/test/ui/mir/mir-inlining/ice-issue-77306-1.rs @@ -1,5 +1,5 @@ // run-pass -// compile-flags:-Zmir-opt-level=2 +// compile-flags:-Zmir-opt-level=3 // Previously ICEd because we did not normalize during inlining, // see https://github.com/rust-lang/rust/pull/77306 for more discussion. diff --git a/src/test/ui/mir/mir-inlining/ice-issue-77306-2.rs b/src/test/ui/mir/mir-inlining/ice-issue-77306-2.rs index a346d450586bb..cb208401313e9 100644 --- a/src/test/ui/mir/mir-inlining/ice-issue-77306-2.rs +++ b/src/test/ui/mir/mir-inlining/ice-issue-77306-2.rs @@ -1,5 +1,5 @@ // run-pass -// compile-flags:-Zmir-opt-level=2 +// compile-flags:-Zmir-opt-level=3 struct Cursor {} struct TokenTree {} diff --git a/src/test/ui/mir/mir-inlining/ice-issue-77564.rs b/src/test/ui/mir/mir-inlining/ice-issue-77564.rs index 262402df2cc5a..0d3fbfe5d1a06 100644 --- a/src/test/ui/mir/mir-inlining/ice-issue-77564.rs +++ b/src/test/ui/mir/mir-inlining/ice-issue-77564.rs @@ -1,5 +1,5 @@ // run-pass -// compile-flags:-Zmir-opt-level=2 +// compile-flags:-Zmir-opt-level=3 use std::mem::MaybeUninit; const N: usize = 2; diff --git a/src/test/ui/mir/mir-inlining/inline-instrument-coverage-fail.rs b/src/test/ui/mir/mir-inlining/inline-instrument-coverage-fail.rs deleted file mode 100644 index 2437155d9810a..0000000000000 --- a/src/test/ui/mir/mir-inlining/inline-instrument-coverage-fail.rs +++ /dev/null @@ -1,21 +0,0 @@ -// Ensures -Zmir-opt-level=2 (specifically, inlining) is not allowed with -Zinstrument-coverage. -// Regression test for issue #80060. -// -// needs-profiler-support -// build-pass -// compile-flags: -Zmir-opt-level=2 -Zinstrument-coverage -#[inline(never)] -fn foo() {} - -pub fn baz() { - bar(); -} - -#[inline(always)] -fn bar() { - foo(); -} - -fn main() { - bar(); -} diff --git a/src/test/ui/mir/mir-inlining/inline-instrument-coverage-fail.stderr b/src/test/ui/mir/mir-inlining/inline-instrument-coverage-fail.stderr deleted file mode 100644 index eb50e5075ca8f..0000000000000 --- a/src/test/ui/mir/mir-inlining/inline-instrument-coverage-fail.stderr +++ /dev/null @@ -1,2 +0,0 @@ -warning: `-Z mir-opt-level=2` (or any level > 1) enables function inlining, which is incompatible with `-Z instrument-coverage`. Inlining will be disabled. - diff --git a/src/test/ui/mir/mir-inlining/no-trait-method-issue-40473.rs b/src/test/ui/mir/mir-inlining/no-trait-method-issue-40473.rs index 8f570dbd4ad74..8b3cb703dc0de 100644 --- a/src/test/ui/mir/mir-inlining/no-trait-method-issue-40473.rs +++ b/src/test/ui/mir/mir-inlining/no-trait-method-issue-40473.rs @@ -1,5 +1,5 @@ // run-pass -// compile-flags:-Zmir-opt-level=2 +// compile-flags:-Zmir-opt-level=3 pub trait Foo { fn bar(&self) -> usize { 2 } } diff --git a/src/test/ui/mir/mir-inlining/var-debuginfo-issue-67586.rs b/src/test/ui/mir/mir-inlining/var-debuginfo-issue-67586.rs index 23cc114880c63..e2620682679b2 100644 --- a/src/test/ui/mir/mir-inlining/var-debuginfo-issue-67586.rs +++ b/src/test/ui/mir/mir-inlining/var-debuginfo-issue-67586.rs @@ -1,5 +1,5 @@ // run-pass -// compile-flags: -Z mir-opt-level=2 -C opt-level=0 -C debuginfo=2 +// compile-flags: -Z mir-opt-level=3 -C opt-level=0 -C debuginfo=2 #[inline(never)] pub fn foo(bar: usize) -> usize { diff --git a/src/test/ui/mir/mir_const_prop_tuple_field_reorder.rs b/src/test/ui/mir/mir_const_prop_tuple_field_reorder.rs index 629b50dec65f9..b66a85d07d3b9 100644 --- a/src/test/ui/mir/mir_const_prop_tuple_field_reorder.rs +++ b/src/test/ui/mir/mir_const_prop_tuple_field_reorder.rs @@ -1,4 +1,4 @@ -// compile-flags: -Z mir-opt-level=2 +// compile-flags: -Z mir-opt-level=3 // build-pass #![crate_type="lib"] diff --git a/src/test/ui/mir/ssa-analysis-regression-50041.rs b/src/test/ui/mir/ssa-analysis-regression-50041.rs index c818f2976e1cf..1892807253ae3 100644 --- a/src/test/ui/mir/ssa-analysis-regression-50041.rs +++ b/src/test/ui/mir/ssa-analysis-regression-50041.rs @@ -1,5 +1,5 @@ // build-pass -// compile-flags: -Z mir-opt-level=3 +// compile-flags: -Z mir-opt-level=4 #![crate_type="lib"] #![feature(lang_items)] diff --git a/src/test/ui/mismatched_types/E0053.stderr b/src/test/ui/mismatched_types/E0053.stderr index e0a3ce922b970..6ce8126b9f970 100644 --- a/src/test/ui/mismatched_types/E0053.stderr +++ b/src/test/ui/mismatched_types/E0053.stderr @@ -5,7 +5,10 @@ LL | fn foo(x: u16); | --- type in trait ... LL | fn foo(x: i16) { } - | ^^^ expected `u16`, found `i16` + | ^^^ + | | + | expected `u16`, found `i16` + | help: change the parameter type to match the trait: `u16` | = note: expected fn pointer `fn(u16)` found fn pointer `fn(i16)` @@ -20,7 +23,7 @@ LL | fn bar(&mut self) { } | ^^^^^^^^^ | | | types differ in mutability - | help: consider changing the mutability to match the trait: `&self` + | help: change the self-receiver type to match the trait: `self: &Bar` | = note: expected fn pointer `fn(&Bar)` found fn pointer `fn(&mut Bar)` diff --git a/src/test/ui/mismatched_types/issue-74918-missing-lifetime.rs b/src/test/ui/mismatched_types/issue-74918-missing-lifetime.rs index 0e3ea4bc8c9de..6aa34354a7ad9 100644 --- a/src/test/ui/mismatched_types/issue-74918-missing-lifetime.rs +++ b/src/test/ui/mismatched_types/issue-74918-missing-lifetime.rs @@ -8,7 +8,7 @@ struct ChunkingIterator<T, S: 'static + Iterator<Item = T>> { impl<T, S: Iterator<Item = T>> Iterator for ChunkingIterator<T, S> { type Item = IteratorChunk<T, S>; //~ ERROR missing lifetime - fn next(&mut self) -> Option<IteratorChunk<T, S>> { //~ ERROR `impl` + fn next(&mut self) -> Option<IteratorChunk<T, S>> { todo!() } } diff --git a/src/test/ui/mismatched_types/issue-74918-missing-lifetime.stderr b/src/test/ui/mismatched_types/issue-74918-missing-lifetime.stderr index 5df35fa571c55..1e035ff99d0bc 100644 --- a/src/test/ui/mismatched_types/issue-74918-missing-lifetime.stderr +++ b/src/test/ui/mismatched_types/issue-74918-missing-lifetime.stderr @@ -9,22 +9,6 @@ help: consider introducing a named lifetime parameter LL | type Item<'a> = IteratorChunk<'a, T, S>; | ^^^^ ^^^ -error: `impl` item signature doesn't match `trait` item signature - --> $DIR/issue-74918-missing-lifetime.rs:11:5 - | -LL | fn next(&mut self) -> Option<IteratorChunk<T, S>> { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ found `fn(&mut ChunkingIterator<T, S>) -> Option<IteratorChunk<'_, T, S>>` - | - ::: $SRC_DIR/core/src/iter/traits/iterator.rs:LL:COL - | -LL | fn next(&mut self) -> Option<Self::Item>; - | ----------------------------------------- expected `fn(&mut ChunkingIterator<T, S>) -> Option<IteratorChunk<'static, _, _>>` - | - = note: expected `fn(&mut ChunkingIterator<T, S>) -> Option<IteratorChunk<'static, _, _>>` - found `fn(&mut ChunkingIterator<T, S>) -> Option<IteratorChunk<'_, _, _>>` - = help: the lifetime requirements from the `impl` do not correspond to the requirements in the `trait` - = help: verify the lifetime relationships in the `trait` and `impl` between the `self` argument, the other inputs and its output - -error: aborting due to 2 previous errors +error: aborting due to previous error For more information about this error, try `rustc --explain E0106`. diff --git a/src/test/ui/mismatched_types/issue-75361-mismatched-impl.stderr b/src/test/ui/mismatched_types/issue-75361-mismatched-impl.stderr index 4b86a1fede163..a64cb82305a48 100644 --- a/src/test/ui/mismatched_types/issue-75361-mismatched-impl.stderr +++ b/src/test/ui/mismatched_types/issue-75361-mismatched-impl.stderr @@ -13,7 +13,7 @@ help: the lifetime requirements from the `impl` do not correspond to the require --> $DIR/issue-75361-mismatched-impl.rs:12:55 | LL | fn adjacent_edges(&self) -> Box<dyn MyTrait<Item = &Self::EdgeType>>; - | ^^^^^^^^^^^^^^ consider borrowing this type parameter in the trait + | ^^^^ consider borrowing this type parameter in the trait error: aborting due to previous error diff --git a/src/test/ui/mismatched_types/trait-impl-fn-incompatibility.stderr b/src/test/ui/mismatched_types/trait-impl-fn-incompatibility.stderr index 161843473b6c1..2ac4d1c33a9b9 100644 --- a/src/test/ui/mismatched_types/trait-impl-fn-incompatibility.stderr +++ b/src/test/ui/mismatched_types/trait-impl-fn-incompatibility.stderr @@ -5,7 +5,10 @@ LL | fn foo(x: u16); | --- type in trait ... LL | fn foo(x: i16) { } - | ^^^ expected `u16`, found `i16` + | ^^^ + | | + | expected `u16`, found `i16` + | help: change the parameter type to match the trait: `u16` | = note: expected fn pointer `fn(u16)` found fn pointer `fn(i16)` @@ -20,7 +23,7 @@ LL | fn bar(&mut self, bar: &Bar) { } | ^^^^ | | | types differ in mutability - | help: consider changing the mutability to match the trait: `&mut Bar` + | help: change the parameter type to match the trait: `&mut Bar` | = note: expected fn pointer `fn(&mut Bar, &mut Bar)` found fn pointer `fn(&mut Bar, &Bar)` diff --git a/src/test/ui/modules/path-no-file-name.rs b/src/test/ui/modules/path-no-file-name.rs new file mode 100644 index 0000000000000..f62cd2a9eb4e4 --- /dev/null +++ b/src/test/ui/modules/path-no-file-name.rs @@ -0,0 +1,7 @@ +// normalize-stderr-test: "\.:.*\(" -> ".: $$ACCESS_DENIED_MSG (" +// normalize-stderr-test: "os error \d+" -> "os error $$ACCESS_DENIED_CODE" + +#[path = "."] +mod m; //~ ERROR couldn't read + +fn main() {} diff --git a/src/test/ui/modules/path-no-file-name.stderr b/src/test/ui/modules/path-no-file-name.stderr new file mode 100644 index 0000000000000..32a213c68f654 --- /dev/null +++ b/src/test/ui/modules/path-no-file-name.stderr @@ -0,0 +1,8 @@ +error: couldn't read $DIR/.: $ACCESS_DENIED_MSG (os error $ACCESS_DENIED_CODE) + --> $DIR/path-no-file-name.rs:5:1 + | +LL | mod m; + | ^^^^^^ + +error: aborting due to previous error + diff --git a/src/test/ui/multiple-main-2.rs b/src/test/ui/multiple-main-2.rs deleted file mode 100644 index e4685b1e00456..0000000000000 --- a/src/test/ui/multiple-main-2.rs +++ /dev/null @@ -1,9 +0,0 @@ -#![feature(main)] - -#[main] -fn bar() { -} - -#[main] -fn foo() { //~ ERROR multiple functions with a `#[main]` attribute -} diff --git a/src/test/ui/multiple-main-2.stderr b/src/test/ui/multiple-main-2.stderr deleted file mode 100644 index 24bc9a8878b08..0000000000000 --- a/src/test/ui/multiple-main-2.stderr +++ /dev/null @@ -1,14 +0,0 @@ -error[E0137]: multiple functions with a `#[main]` attribute - --> $DIR/multiple-main-2.rs:8:1 - | -LL | / fn bar() { -LL | | } - | |_- first `#[main]` function -... -LL | / fn foo() { -LL | | } - | |_^ additional `#[main]` function - -error: aborting due to previous error - -For more information about this error, try `rustc --explain E0137`. diff --git a/src/test/ui/multiple-main-3.rs b/src/test/ui/multiple-main-3.rs deleted file mode 100644 index d1b5ae9a83315..0000000000000 --- a/src/test/ui/multiple-main-3.rs +++ /dev/null @@ -1,11 +0,0 @@ -#![feature(main)] - -#[main] -fn main1() { -} - -mod foo { - #[main] - fn main2() { //~ ERROR multiple functions with a `#[main]` attribute - } -} diff --git a/src/test/ui/multiple-main-3.stderr b/src/test/ui/multiple-main-3.stderr deleted file mode 100644 index ec171b76a284a..0000000000000 --- a/src/test/ui/multiple-main-3.stderr +++ /dev/null @@ -1,14 +0,0 @@ -error[E0137]: multiple functions with a `#[main]` attribute - --> $DIR/multiple-main-3.rs:9:5 - | -LL | / fn main1() { -LL | | } - | |_- first `#[main]` function -... -LL | / fn main2() { -LL | | } - | |_____^ additional `#[main]` function - -error: aborting due to previous error - -For more information about this error, try `rustc --explain E0137`. diff --git a/src/test/ui/mut/mut-pattern-internal-mutability.stderr b/src/test/ui/mut/mut-pattern-internal-mutability.stderr index eaa33453a7507..6583546aa5c1f 100644 --- a/src/test/ui/mut/mut-pattern-internal-mutability.stderr +++ b/src/test/ui/mut/mut-pattern-internal-mutability.stderr @@ -5,7 +5,7 @@ LL | let &mut x = foo; | - | | | first assignment to `x` - | help: make this binding mutable: `mut x` + | help: consider making this binding mutable: `mut x` LL | x += 1; | ^^^^^^ cannot assign twice to immutable variable diff --git a/src/test/ui/mut/mutable-enum-indirect.stderr b/src/test/ui/mut/mutable-enum-indirect.stderr index 3be6acb41a947..5b26f94115ac9 100644 --- a/src/test/ui/mut/mutable-enum-indirect.stderr +++ b/src/test/ui/mut/mutable-enum-indirect.stderr @@ -8,7 +8,11 @@ LL | bar(&x); | ^^^ `NoSync` cannot be shared between threads safely | = help: within `&Foo`, the trait `Sync` is not implemented for `NoSync` - = note: required because it appears within the type `Foo` +note: required because it appears within the type `Foo` + --> $DIR/mutable-enum-indirect.rs:11:6 + | +LL | enum Foo { A(NoSync) } + | ^^^ = note: required because it appears within the type `&Foo` error: aborting due to previous error diff --git a/src/test/ui/never_type/defaulted-never-note.rs b/src/test/ui/never_type/defaulted-never-note.rs index c96c4784dcf32..6979c3ec44318 100644 --- a/src/test/ui/never_type/defaulted-never-note.rs +++ b/src/test/ui/never_type/defaulted-never-note.rs @@ -26,7 +26,9 @@ fn smeg() { foo(_x); //~^ ERROR the trait bound //~| NOTE the trait `ImplementedForUnitButNotNever` is not implemented - //~| NOTE the trait is implemented for `()` + //~| NOTE this trait is implemented for `()` + //~| NOTE this error might have been caused + //~| HELP did you intend } fn main() { diff --git a/src/test/ui/never_type/defaulted-never-note.stderr b/src/test/ui/never_type/defaulted-never-note.stderr index 69691883de1e3..99738375022f9 100644 --- a/src/test/ui/never_type/defaulted-never-note.stderr +++ b/src/test/ui/never_type/defaulted-never-note.stderr @@ -7,7 +7,9 @@ LL | fn foo<T: ImplementedForUnitButNotNever>(_t: T) {} LL | foo(_x); | ^^^ the trait `ImplementedForUnitButNotNever` is not implemented for `!` | - = note: the trait is implemented for `()`. Possibly this error has been caused by changes to Rust's type-inference algorithm (see issue #48950 <https://github.com/rust-lang/rust/issues/48950> for more information). Consider whether you meant to use the type `()` here instead. + = note: this trait is implemented for `()`. + = note: this error might have been caused by changes to Rust's type-inference algorithm (see issue #48950 <https://github.com/rust-lang/rust/issues/48950> for more information). + = help: did you intend to use the type `()` here instead? error: aborting due to previous error diff --git a/src/test/ui/never_type/never-from-impl-is-reserved.stderr b/src/test/ui/never_type/never-from-impl-is-reserved.stderr index 8b8d0f4ea73be..871c512052821 100644 --- a/src/test/ui/never_type/never-from-impl-is-reserved.stderr +++ b/src/test/ui/never_type/never-from-impl-is-reserved.stderr @@ -1,4 +1,4 @@ -error[E0119]: conflicting implementations of trait `MyTrait` for type `MyFoo`: +error[E0119]: conflicting implementations of trait `MyTrait` for type `MyFoo` --> $DIR/never-from-impl-is-reserved.rs:10:1 | LL | impl MyTrait for MyFoo {} diff --git a/src/test/ui/nll/closure-requirements/escape-argument-callee.stderr b/src/test/ui/nll/closure-requirements/escape-argument-callee.stderr index 4e122d930fc4e..ff16bf0e078fc 100644 --- a/src/test/ui/nll/closure-requirements/escape-argument-callee.stderr +++ b/src/test/ui/nll/closure-requirements/escape-argument-callee.stderr @@ -6,7 +6,7 @@ LL | let mut closure = expect_sig(|p, y| *p = y); | = note: defining type: test::{closure#0} with closure substs [ i16, - for<'r, 's, 't0> extern "rust-call" fn((&ReLateBound(DebruijnIndex(0), BoundRegion { kind: BrNamed('r) }) mut &ReLateBound(DebruijnIndex(0), BoundRegion { kind: BrNamed('s) }) i32, &ReLateBound(DebruijnIndex(0), BoundRegion { kind: BrNamed('t0) }) i32)), + for<'r, 's, 't0> extern "rust-call" fn((&ReLateBound(DebruijnIndex(0), BoundRegion { var: 0, kind: BrNamed('r) }) mut &ReLateBound(DebruijnIndex(0), BoundRegion { var: 1, kind: BrNamed('s) }) i32, &ReLateBound(DebruijnIndex(0), BoundRegion { var: 2, kind: BrNamed('t0) }) i32)), (), ] diff --git a/src/test/ui/nll/closure-requirements/escape-argument.stderr b/src/test/ui/nll/closure-requirements/escape-argument.stderr index 44d1d2327fcdb..22398f0857255 100644 --- a/src/test/ui/nll/closure-requirements/escape-argument.stderr +++ b/src/test/ui/nll/closure-requirements/escape-argument.stderr @@ -6,7 +6,7 @@ LL | let mut closure = expect_sig(|p, y| *p = y); | = note: defining type: test::{closure#0} with closure substs [ i16, - for<'r, 's> extern "rust-call" fn((&ReLateBound(DebruijnIndex(0), BoundRegion { kind: BrNamed('r) }) mut &ReLateBound(DebruijnIndex(0), BoundRegion { kind: BrNamed('s) }) i32, &ReLateBound(DebruijnIndex(0), BoundRegion { kind: BrNamed('s) }) i32)), + for<'r, 's> extern "rust-call" fn((&ReLateBound(DebruijnIndex(0), BoundRegion { var: 0, kind: BrNamed('r) }) mut &ReLateBound(DebruijnIndex(0), BoundRegion { var: 1, kind: BrNamed('s) }) i32, &ReLateBound(DebruijnIndex(0), BoundRegion { var: 1, kind: BrNamed('s) }) i32)), (), ] diff --git a/src/test/ui/nll/closure-requirements/propagate-approximated-fail-no-postdom.stderr b/src/test/ui/nll/closure-requirements/propagate-approximated-fail-no-postdom.stderr index fa9f994c4fae3..11420efaa066e 100644 --- a/src/test/ui/nll/closure-requirements/propagate-approximated-fail-no-postdom.stderr +++ b/src/test/ui/nll/closure-requirements/propagate-approximated-fail-no-postdom.stderr @@ -10,7 +10,7 @@ LL | | }, | = note: defining type: supply::{closure#0} with closure substs [ i16, - for<'r, 's> extern "rust-call" fn((std::cell::Cell<&'_#1r &ReLateBound(DebruijnIndex(0), BoundRegion { kind: BrNamed('r) }) u32>, std::cell::Cell<&'_#2r &ReLateBound(DebruijnIndex(0), BoundRegion { kind: BrNamed('r) }) u32>, std::cell::Cell<&ReLateBound(DebruijnIndex(0), BoundRegion { kind: BrNamed('s) }) &'_#3r u32>, std::cell::Cell<&ReLateBound(DebruijnIndex(0), BoundRegion { kind: BrNamed('r) }) u32>, std::cell::Cell<&ReLateBound(DebruijnIndex(0), BoundRegion { kind: BrNamed('s) }) u32>)), + for<'r, 's> extern "rust-call" fn((std::cell::Cell<&'_#1r &ReLateBound(DebruijnIndex(0), BoundRegion { var: 0, kind: BrNamed('r) }) u32>, std::cell::Cell<&'_#2r &ReLateBound(DebruijnIndex(0), BoundRegion { var: 0, kind: BrNamed('r) }) u32>, std::cell::Cell<&ReLateBound(DebruijnIndex(0), BoundRegion { var: 1, kind: BrNamed('s) }) &'_#3r u32>, std::cell::Cell<&ReLateBound(DebruijnIndex(0), BoundRegion { var: 0, kind: BrNamed('r) }) u32>, std::cell::Cell<&ReLateBound(DebruijnIndex(0), BoundRegion { var: 1, kind: BrNamed('s) }) u32>)), (), ] = note: late-bound region is '_#4r diff --git a/src/test/ui/nll/closure-requirements/propagate-approximated-ref.stderr b/src/test/ui/nll/closure-requirements/propagate-approximated-ref.stderr index 0555f79bcb03b..98c3c28fb43ff 100644 --- a/src/test/ui/nll/closure-requirements/propagate-approximated-ref.stderr +++ b/src/test/ui/nll/closure-requirements/propagate-approximated-ref.stderr @@ -11,7 +11,7 @@ LL | | }); | = note: defining type: supply::{closure#0} with closure substs [ i16, - for<'r, 's, 't0, 't1, 't2, 't3> extern "rust-call" fn((&ReLateBound(DebruijnIndex(0), BoundRegion { kind: BrNamed('r) }) std::cell::Cell<&'_#1r &ReLateBound(DebruijnIndex(0), BoundRegion { kind: BrNamed('s) }) u32>, &ReLateBound(DebruijnIndex(0), BoundRegion { kind: BrNamed('t0) }) std::cell::Cell<&ReLateBound(DebruijnIndex(0), BoundRegion { kind: BrNamed('t1) }) &'_#2r u32>, &ReLateBound(DebruijnIndex(0), BoundRegion { kind: BrNamed('t2) }) std::cell::Cell<&ReLateBound(DebruijnIndex(0), BoundRegion { kind: BrNamed('s) }) u32>, &ReLateBound(DebruijnIndex(0), BoundRegion { kind: BrNamed('t3) }) std::cell::Cell<&ReLateBound(DebruijnIndex(0), BoundRegion { kind: BrNamed('t1) }) u32>)), + for<'r, 's, 't0, 't1, 't2, 't3> extern "rust-call" fn((&ReLateBound(DebruijnIndex(0), BoundRegion { var: 0, kind: BrNamed('r) }) std::cell::Cell<&'_#1r &ReLateBound(DebruijnIndex(0), BoundRegion { var: 1, kind: BrNamed('s) }) u32>, &ReLateBound(DebruijnIndex(0), BoundRegion { var: 2, kind: BrNamed('t0) }) std::cell::Cell<&ReLateBound(DebruijnIndex(0), BoundRegion { var: 3, kind: BrNamed('t1) }) &'_#2r u32>, &ReLateBound(DebruijnIndex(0), BoundRegion { var: 4, kind: BrNamed('t2) }) std::cell::Cell<&ReLateBound(DebruijnIndex(0), BoundRegion { var: 1, kind: BrNamed('s) }) u32>, &ReLateBound(DebruijnIndex(0), BoundRegion { var: 5, kind: BrNamed('t3) }) std::cell::Cell<&ReLateBound(DebruijnIndex(0), BoundRegion { var: 3, kind: BrNamed('t1) }) u32>)), (), ] = note: late-bound region is '_#3r diff --git a/src/test/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-comparing-against-free.stderr b/src/test/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-comparing-against-free.stderr index 0932f9415480c..30ef343b26123 100644 --- a/src/test/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-comparing-against-free.stderr +++ b/src/test/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-comparing-against-free.stderr @@ -10,7 +10,7 @@ LL | | }) | = note: defining type: case1::{closure#0} with closure substs [ i32, - for<'r> extern "rust-call" fn((std::cell::Cell<&'_#1r u32>, std::cell::Cell<&ReLateBound(DebruijnIndex(0), BoundRegion { kind: BrNamed('r) }) u32>)), + for<'r> extern "rust-call" fn((std::cell::Cell<&'_#1r u32>, std::cell::Cell<&ReLateBound(DebruijnIndex(0), BoundRegion { var: 0, kind: BrNamed('r) }) u32>)), (), ] @@ -49,7 +49,7 @@ LL | | }) | = note: defining type: case2::{closure#0} with closure substs [ i32, - for<'r> extern "rust-call" fn((std::cell::Cell<&'_#1r u32>, std::cell::Cell<&ReLateBound(DebruijnIndex(0), BoundRegion { kind: BrNamed('r) }) u32>)), + for<'r> extern "rust-call" fn((std::cell::Cell<&'_#1r u32>, std::cell::Cell<&ReLateBound(DebruijnIndex(0), BoundRegion { var: 0, kind: BrNamed('r) }) u32>)), (), ] = note: number of external vids: 2 diff --git a/src/test/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-no-bound.stderr b/src/test/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-no-bound.stderr index bf6e2a922ed08..29993b129c71a 100644 --- a/src/test/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-no-bound.stderr +++ b/src/test/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-no-bound.stderr @@ -12,7 +12,7 @@ LL | | }); | = note: defining type: supply::{closure#0} with closure substs [ i16, - for<'r, 's, 't0, 't1, 't2> extern "rust-call" fn((&ReLateBound(DebruijnIndex(0), BoundRegion { kind: BrNamed('r) }) std::cell::Cell<&'_#1r &ReLateBound(DebruijnIndex(0), BoundRegion { kind: BrNamed('s) }) u32>, &ReLateBound(DebruijnIndex(0), BoundRegion { kind: BrNamed('t0) }) std::cell::Cell<&ReLateBound(DebruijnIndex(0), BoundRegion { kind: BrNamed('s) }) u32>, &ReLateBound(DebruijnIndex(0), BoundRegion { kind: BrNamed('t1) }) std::cell::Cell<&ReLateBound(DebruijnIndex(0), BoundRegion { kind: BrNamed('t2) }) u32>)), + for<'r, 's, 't0, 't1, 't2> extern "rust-call" fn((&ReLateBound(DebruijnIndex(0), BoundRegion { var: 0, kind: BrNamed('r) }) std::cell::Cell<&'_#1r &ReLateBound(DebruijnIndex(0), BoundRegion { var: 1, kind: BrNamed('s) }) u32>, &ReLateBound(DebruijnIndex(0), BoundRegion { var: 2, kind: BrNamed('t0) }) std::cell::Cell<&ReLateBound(DebruijnIndex(0), BoundRegion { var: 1, kind: BrNamed('s) }) u32>, &ReLateBound(DebruijnIndex(0), BoundRegion { var: 3, kind: BrNamed('t1) }) std::cell::Cell<&ReLateBound(DebruijnIndex(0), BoundRegion { var: 4, kind: BrNamed('t2) }) u32>)), (), ] = note: late-bound region is '_#2r diff --git a/src/test/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-wrong-bound.stderr b/src/test/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-wrong-bound.stderr index a3d993848cbaa..cb505d8b1eceb 100644 --- a/src/test/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-wrong-bound.stderr +++ b/src/test/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-wrong-bound.stderr @@ -12,7 +12,7 @@ LL | | }); | = note: defining type: supply::{closure#0} with closure substs [ i16, - for<'r, 's, 't0, 't1, 't2, 't3> extern "rust-call" fn((&ReLateBound(DebruijnIndex(0), BoundRegion { kind: BrNamed('r) }) std::cell::Cell<&'_#1r &ReLateBound(DebruijnIndex(0), BoundRegion { kind: BrNamed('s) }) u32>, &ReLateBound(DebruijnIndex(0), BoundRegion { kind: BrNamed('t0) }) std::cell::Cell<&'_#2r &ReLateBound(DebruijnIndex(0), BoundRegion { kind: BrNamed('t1) }) u32>, &ReLateBound(DebruijnIndex(0), BoundRegion { kind: BrNamed('t2) }) std::cell::Cell<&ReLateBound(DebruijnIndex(0), BoundRegion { kind: BrNamed('s) }) u32>, &ReLateBound(DebruijnIndex(0), BoundRegion { kind: BrNamed('t3) }) std::cell::Cell<&ReLateBound(DebruijnIndex(0), BoundRegion { kind: BrNamed('t1) }) u32>)), + for<'r, 's, 't0, 't1, 't2, 't3> extern "rust-call" fn((&ReLateBound(DebruijnIndex(0), BoundRegion { var: 0, kind: BrNamed('r) }) std::cell::Cell<&'_#1r &ReLateBound(DebruijnIndex(0), BoundRegion { var: 1, kind: BrNamed('s) }) u32>, &ReLateBound(DebruijnIndex(0), BoundRegion { var: 2, kind: BrNamed('t0) }) std::cell::Cell<&'_#2r &ReLateBound(DebruijnIndex(0), BoundRegion { var: 3, kind: BrNamed('t1) }) u32>, &ReLateBound(DebruijnIndex(0), BoundRegion { var: 4, kind: BrNamed('t2) }) std::cell::Cell<&ReLateBound(DebruijnIndex(0), BoundRegion { var: 1, kind: BrNamed('s) }) u32>, &ReLateBound(DebruijnIndex(0), BoundRegion { var: 5, kind: BrNamed('t3) }) std::cell::Cell<&ReLateBound(DebruijnIndex(0), BoundRegion { var: 3, kind: BrNamed('t1) }) u32>)), (), ] = note: late-bound region is '_#3r diff --git a/src/test/ui/nll/closure-requirements/propagate-approximated-val.stderr b/src/test/ui/nll/closure-requirements/propagate-approximated-val.stderr index 60dca1baa40e7..2ec9d4d8db1a6 100644 --- a/src/test/ui/nll/closure-requirements/propagate-approximated-val.stderr +++ b/src/test/ui/nll/closure-requirements/propagate-approximated-val.stderr @@ -11,7 +11,7 @@ LL | | }); | = note: defining type: test::{closure#0} with closure substs [ i16, - for<'r, 's> extern "rust-call" fn((std::cell::Cell<&'_#1r &ReLateBound(DebruijnIndex(0), BoundRegion { kind: BrNamed('r) }) u32>, std::cell::Cell<&ReLateBound(DebruijnIndex(0), BoundRegion { kind: BrNamed('s) }) &'_#2r u32>, std::cell::Cell<&ReLateBound(DebruijnIndex(0), BoundRegion { kind: BrNamed('r) }) u32>, std::cell::Cell<&ReLateBound(DebruijnIndex(0), BoundRegion { kind: BrNamed('s) }) u32>)), + for<'r, 's> extern "rust-call" fn((std::cell::Cell<&'_#1r &ReLateBound(DebruijnIndex(0), BoundRegion { var: 0, kind: BrNamed('r) }) u32>, std::cell::Cell<&ReLateBound(DebruijnIndex(0), BoundRegion { var: 1, kind: BrNamed('s) }) &'_#2r u32>, std::cell::Cell<&ReLateBound(DebruijnIndex(0), BoundRegion { var: 0, kind: BrNamed('r) }) u32>, std::cell::Cell<&ReLateBound(DebruijnIndex(0), BoundRegion { var: 1, kind: BrNamed('s) }) u32>)), (), ] = note: late-bound region is '_#3r diff --git a/src/test/ui/nll/closure-requirements/propagate-despite-same-free-region.stderr b/src/test/ui/nll/closure-requirements/propagate-despite-same-free-region.stderr index cbb10eb187ed8..21e4232c788fb 100644 --- a/src/test/ui/nll/closure-requirements/propagate-despite-same-free-region.stderr +++ b/src/test/ui/nll/closure-requirements/propagate-despite-same-free-region.stderr @@ -10,7 +10,7 @@ LL | | }, | = note: defining type: supply::{closure#0} with closure substs [ i16, - for<'r, 's> extern "rust-call" fn((std::cell::Cell<&'_#1r &ReLateBound(DebruijnIndex(0), BoundRegion { kind: BrNamed('r) }) u32>, std::cell::Cell<&ReLateBound(DebruijnIndex(0), BoundRegion { kind: BrNamed('s) }) &'_#2r u32>, std::cell::Cell<&ReLateBound(DebruijnIndex(0), BoundRegion { kind: BrNamed('r) }) u32>, std::cell::Cell<&ReLateBound(DebruijnIndex(0), BoundRegion { kind: BrNamed('s) }) u32>)), + for<'r, 's> extern "rust-call" fn((std::cell::Cell<&'_#1r &ReLateBound(DebruijnIndex(0), BoundRegion { var: 0, kind: BrNamed('r) }) u32>, std::cell::Cell<&ReLateBound(DebruijnIndex(0), BoundRegion { var: 1, kind: BrNamed('s) }) &'_#2r u32>, std::cell::Cell<&ReLateBound(DebruijnIndex(0), BoundRegion { var: 0, kind: BrNamed('r) }) u32>, std::cell::Cell<&ReLateBound(DebruijnIndex(0), BoundRegion { var: 1, kind: BrNamed('s) }) u32>)), (), ] = note: late-bound region is '_#3r diff --git a/src/test/ui/nll/closure-requirements/propagate-fail-to-approximate-longer-no-bounds.stderr b/src/test/ui/nll/closure-requirements/propagate-fail-to-approximate-longer-no-bounds.stderr index f9f1d8bb6fffa..8b9b043542057 100644 --- a/src/test/ui/nll/closure-requirements/propagate-fail-to-approximate-longer-no-bounds.stderr +++ b/src/test/ui/nll/closure-requirements/propagate-fail-to-approximate-longer-no-bounds.stderr @@ -11,7 +11,7 @@ LL | | }); | = note: defining type: supply::{closure#0} with closure substs [ i16, - for<'r, 's, 't0, 't1, 't2> extern "rust-call" fn((&ReLateBound(DebruijnIndex(0), BoundRegion { kind: BrNamed('r) }) std::cell::Cell<&ReLateBound(DebruijnIndex(0), BoundRegion { kind: BrNamed('s) }) &'_#1r u32>, &ReLateBound(DebruijnIndex(0), BoundRegion { kind: BrNamed('t0) }) std::cell::Cell<&ReLateBound(DebruijnIndex(0), BoundRegion { kind: BrNamed('t1) }) u32>, &ReLateBound(DebruijnIndex(0), BoundRegion { kind: BrNamed('t2) }) std::cell::Cell<&ReLateBound(DebruijnIndex(0), BoundRegion { kind: BrNamed('s) }) u32>)), + for<'r, 's, 't0, 't1, 't2> extern "rust-call" fn((&ReLateBound(DebruijnIndex(0), BoundRegion { var: 0, kind: BrNamed('r) }) std::cell::Cell<&ReLateBound(DebruijnIndex(0), BoundRegion { var: 1, kind: BrNamed('s) }) &'_#1r u32>, &ReLateBound(DebruijnIndex(0), BoundRegion { var: 2, kind: BrNamed('t0) }) std::cell::Cell<&ReLateBound(DebruijnIndex(0), BoundRegion { var: 3, kind: BrNamed('t1) }) u32>, &ReLateBound(DebruijnIndex(0), BoundRegion { var: 4, kind: BrNamed('t2) }) std::cell::Cell<&ReLateBound(DebruijnIndex(0), BoundRegion { var: 1, kind: BrNamed('s) }) u32>)), (), ] = note: late-bound region is '_#2r diff --git a/src/test/ui/nll/closure-requirements/propagate-fail-to-approximate-longer-wrong-bounds.stderr b/src/test/ui/nll/closure-requirements/propagate-fail-to-approximate-longer-wrong-bounds.stderr index 1587c28e1bef3..060ce690f0306 100644 --- a/src/test/ui/nll/closure-requirements/propagate-fail-to-approximate-longer-wrong-bounds.stderr +++ b/src/test/ui/nll/closure-requirements/propagate-fail-to-approximate-longer-wrong-bounds.stderr @@ -11,7 +11,7 @@ LL | | }); | = note: defining type: supply::{closure#0} with closure substs [ i16, - for<'r, 's, 't0, 't1, 't2, 't3> extern "rust-call" fn((&ReLateBound(DebruijnIndex(0), BoundRegion { kind: BrNamed('r) }) std::cell::Cell<&ReLateBound(DebruijnIndex(0), BoundRegion { kind: BrNamed('s) }) &'_#1r u32>, &ReLateBound(DebruijnIndex(0), BoundRegion { kind: BrNamed('t0) }) std::cell::Cell<&ReLateBound(DebruijnIndex(0), BoundRegion { kind: BrNamed('t1) }) &'_#2r u32>, &ReLateBound(DebruijnIndex(0), BoundRegion { kind: BrNamed('t2) }) std::cell::Cell<&ReLateBound(DebruijnIndex(0), BoundRegion { kind: BrNamed('s) }) u32>, &ReLateBound(DebruijnIndex(0), BoundRegion { kind: BrNamed('t3) }) std::cell::Cell<&ReLateBound(DebruijnIndex(0), BoundRegion { kind: BrNamed('t1) }) u32>)), + for<'r, 's, 't0, 't1, 't2, 't3> extern "rust-call" fn((&ReLateBound(DebruijnIndex(0), BoundRegion { var: 0, kind: BrNamed('r) }) std::cell::Cell<&ReLateBound(DebruijnIndex(0), BoundRegion { var: 1, kind: BrNamed('s) }) &'_#1r u32>, &ReLateBound(DebruijnIndex(0), BoundRegion { var: 2, kind: BrNamed('t0) }) std::cell::Cell<&ReLateBound(DebruijnIndex(0), BoundRegion { var: 3, kind: BrNamed('t1) }) &'_#2r u32>, &ReLateBound(DebruijnIndex(0), BoundRegion { var: 4, kind: BrNamed('t2) }) std::cell::Cell<&ReLateBound(DebruijnIndex(0), BoundRegion { var: 1, kind: BrNamed('s) }) u32>, &ReLateBound(DebruijnIndex(0), BoundRegion { var: 5, kind: BrNamed('t3) }) std::cell::Cell<&ReLateBound(DebruijnIndex(0), BoundRegion { var: 3, kind: BrNamed('t1) }) u32>)), (), ] = note: late-bound region is '_#3r diff --git a/src/test/ui/nll/closure-requirements/return-wrong-bound-region.stderr b/src/test/ui/nll/closure-requirements/return-wrong-bound-region.stderr index 44f743310b483..5fc1d5c436181 100644 --- a/src/test/ui/nll/closure-requirements/return-wrong-bound-region.stderr +++ b/src/test/ui/nll/closure-requirements/return-wrong-bound-region.stderr @@ -6,7 +6,7 @@ LL | expect_sig(|a, b| b); // ought to return `a` | = note: defining type: test::{closure#0} with closure substs [ i16, - for<'r, 's> extern "rust-call" fn((&ReLateBound(DebruijnIndex(0), BoundRegion { kind: BrNamed('r) }) i32, &ReLateBound(DebruijnIndex(0), BoundRegion { kind: BrNamed('s) }) i32)) -> &ReLateBound(DebruijnIndex(0), BoundRegion { kind: BrNamed('r) }) i32, + for<'r, 's> extern "rust-call" fn((&ReLateBound(DebruijnIndex(0), BoundRegion { var: 0, kind: BrNamed('r) }) i32, &ReLateBound(DebruijnIndex(0), BoundRegion { var: 1, kind: BrNamed('s) }) i32)) -> &ReLateBound(DebruijnIndex(0), BoundRegion { var: 0, kind: BrNamed('r) }) i32, (), ] diff --git a/src/test/ui/nll/issue-51268.rs b/src/test/ui/nll/issue-51268.rs index 12d0449abb19b..dcdedf7d4c513 100644 --- a/src/test/ui/nll/issue-51268.rs +++ b/src/test/ui/nll/issue-51268.rs @@ -1,5 +1,3 @@ -// ignore-tidy-linelength - struct Bar; impl Bar { diff --git a/src/test/ui/nll/issue-51268.stderr b/src/test/ui/nll/issue-51268.stderr index 420c94f8e1bd2..e6dadc9f6ce3c 100644 --- a/src/test/ui/nll/issue-51268.stderr +++ b/src/test/ui/nll/issue-51268.stderr @@ -1,5 +1,5 @@ error[E0502]: cannot borrow `self.thing` as mutable because it is also borrowed as immutable - --> $DIR/issue-51268.rs:16:9 + --> $DIR/issue-51268.rs:14:9 | LL | self.thing.bar(|| { | ^ --- -- immutable borrow occurs here diff --git a/src/test/ui/issues/issue-52992.rs b/src/test/ui/nll/issue-52992.rs similarity index 100% rename from src/test/ui/issues/issue-52992.rs rename to src/test/ui/nll/issue-52992.rs diff --git a/src/test/ui/nll/issue-57100.rs b/src/test/ui/nll/issue-57100.rs index c7f3e9d730367..f15929334bb40 100644 --- a/src/test/ui/nll/issue-57100.rs +++ b/src/test/ui/nll/issue-57100.rs @@ -1,6 +1,5 @@ #![allow(unused)] -// ignore-tidy-linelength // This tests the error messages for borrows of union fields when the unions are embedded in other // structs or unions. diff --git a/src/test/ui/nll/issue-57100.stderr b/src/test/ui/nll/issue-57100.stderr index 5f733c14036b0..523c3e8d0a2c2 100644 --- a/src/test/ui/nll/issue-57100.stderr +++ b/src/test/ui/nll/issue-57100.stderr @@ -1,5 +1,5 @@ error[E0502]: cannot borrow `r.r2_union.f3_union` (via `r.r2_union.f3_union.s2_leaf.l1_u8`) as immutable because it is also borrowed as mutable (via `r.r2_union.f3_union.s1_leaf.l1_u8`) - --> $DIR/issue-57100.rs:43:20 + --> $DIR/issue-57100.rs:42:20 | LL | let mref = &mut r.r2_union.f3_union.s1_leaf.l1_u8; | -------------------------------------- mutable borrow occurs here (via `r.r2_union.f3_union.s1_leaf.l1_u8`) @@ -13,7 +13,7 @@ LL | println!("{} {}", mref, nref) = note: `r.r2_union.f3_union.s2_leaf.l1_u8` is a field of the union `Second`, so it overlaps the field `r.r2_union.f3_union.s1_leaf.l1_u8` error[E0502]: cannot borrow `r.r2_union` (via `r.r2_union.f1_leaf.l1_u8`) as immutable because it is also borrowed as mutable (via `r.r2_union.f2_leaf.l1_u8`) - --> $DIR/issue-57100.rs:61:20 + --> $DIR/issue-57100.rs:60:20 | LL | let mref = &mut r.r2_union.f2_leaf.l1_u8; | ----------------------------- mutable borrow occurs here (via `r.r2_union.f2_leaf.l1_u8`) diff --git a/src/test/ui/nll/ty-outlives/ty-param-closure-approximate-lower-bound.stderr b/src/test/ui/nll/ty-outlives/ty-param-closure-approximate-lower-bound.stderr index dbf76cd1329cb..baf223b786b08 100644 --- a/src/test/ui/nll/ty-outlives/ty-param-closure-approximate-lower-bound.stderr +++ b/src/test/ui/nll/ty-outlives/ty-param-closure-approximate-lower-bound.stderr @@ -6,7 +6,7 @@ LL | twice(cell, value, |a, b| invoke(a, b)); | = note: defining type: generic::<T>::{closure#0} with closure substs [ i16, - for<'r, 's> extern "rust-call" fn((std::option::Option<std::cell::Cell<&'_#1r &ReLateBound(DebruijnIndex(0), BoundRegion { kind: BrNamed('r) }) ()>>, &ReLateBound(DebruijnIndex(0), BoundRegion { kind: BrNamed('s) }) T)), + for<'r, 's> extern "rust-call" fn((std::option::Option<std::cell::Cell<&'_#1r &ReLateBound(DebruijnIndex(0), BoundRegion { var: 0, kind: BrNamed('r) }) ()>>, &ReLateBound(DebruijnIndex(0), BoundRegion { var: 1, kind: BrNamed('s) }) T)), (), ] = note: number of external vids: 2 @@ -31,7 +31,7 @@ LL | twice(cell, value, |a, b| invoke(a, b)); | = note: defining type: generic_fail::<T>::{closure#0} with closure substs [ i16, - for<'r, 's> extern "rust-call" fn((std::option::Option<std::cell::Cell<&'_#1r &ReLateBound(DebruijnIndex(0), BoundRegion { kind: BrNamed('r) }) ()>>, &ReLateBound(DebruijnIndex(0), BoundRegion { kind: BrNamed('s) }) T)), + for<'r, 's> extern "rust-call" fn((std::option::Option<std::cell::Cell<&'_#1r &ReLateBound(DebruijnIndex(0), BoundRegion { var: 0, kind: BrNamed('r) }) ()>>, &ReLateBound(DebruijnIndex(0), BoundRegion { var: 1, kind: BrNamed('s) }) T)), (), ] = note: late-bound region is '_#2r diff --git a/src/test/ui/no-send-res-ports.stderr b/src/test/ui/no-send-res-ports.stderr index ef7fb4ad7b266..2ee32029b40dc 100644 --- a/src/test/ui/no-send-res-ports.stderr +++ b/src/test/ui/no-send-res-ports.stderr @@ -17,8 +17,16 @@ LL | F: Send + 'static, | ---- required by this bound in `spawn` | = help: within `[closure@$DIR/no-send-res-ports.rs:25:19: 29:6]`, the trait `Send` is not implemented for `Rc<()>` - = note: required because it appears within the type `Port<()>` - = note: required because it appears within the type `Foo` +note: required because it appears within the type `Port<()>` + --> $DIR/no-send-res-ports.rs:5:8 + | +LL | struct Port<T>(Rc<T>); + | ^^^^ +note: required because it appears within the type `Foo` + --> $DIR/no-send-res-ports.rs:9:12 + | +LL | struct Foo { + | ^^^ = note: required because it appears within the type `[closure@$DIR/no-send-res-ports.rs:25:19: 29:6]` error: aborting due to previous error diff --git a/src/test/ui/no_send-enum.stderr b/src/test/ui/no_send-enum.stderr index b617fe410fa9b..9d755839d37c1 100644 --- a/src/test/ui/no_send-enum.stderr +++ b/src/test/ui/no_send-enum.stderr @@ -8,7 +8,11 @@ LL | bar(x); | ^^^ `NoSend` cannot be sent between threads safely | = help: within `Foo`, the trait `Send` is not implemented for `NoSend` - = note: required because it appears within the type `Foo` +note: required because it appears within the type `Foo` + --> $DIR/no_send-enum.rs:8:6 + | +LL | enum Foo { + | ^^^ error: aborting due to previous error diff --git a/src/test/ui/no_share-enum.stderr b/src/test/ui/no_share-enum.stderr index 4a93edc100ec6..a8ab69200ecf1 100644 --- a/src/test/ui/no_share-enum.stderr +++ b/src/test/ui/no_share-enum.stderr @@ -8,7 +8,11 @@ LL | bar(x); | ^^^ `NoSync` cannot be shared between threads safely | = help: within `Foo`, the trait `Sync` is not implemented for `NoSync` - = note: required because it appears within the type `Foo` +note: required because it appears within the type `Foo` + --> $DIR/no_share-enum.rs:8:6 + | +LL | enum Foo { A(NoSync) } + | ^^^ error: aborting due to previous error diff --git a/src/test/ui/non-copyable-void.stderr b/src/test/ui/non-copyable-void.stderr index 8395a3a056317..99af04e7cd97e 100644 --- a/src/test/ui/non-copyable-void.stderr +++ b/src/test/ui/non-copyable-void.stderr @@ -3,14 +3,6 @@ error[E0599]: no method named `clone` found for enum `c_void` in the current sco | LL | let _z = (*y).clone(); | ^^^^^ method not found in `c_void` - | - ::: $SRC_DIR/core/src/clone.rs:LL:COL - | -LL | fn clone(&self) -> Self; - | ----- - | | - | the method is available for `Arc<c_void>` here - | the method is available for `Rc<c_void>` here error: aborting due to previous error diff --git a/src/test/ui/non-ice-error-on-worker-io-fail.rs b/src/test/ui/non-ice-error-on-worker-io-fail.rs index 30779fc65c0fd..134e7d420e3ea 100644 --- a/src/test/ui/non-ice-error-on-worker-io-fail.rs +++ b/src/test/ui/non-ice-error-on-worker-io-fail.rs @@ -24,7 +24,6 @@ // On Linux, we get an error like the below // normalize-stderr-test "couldn't create a temp dir.*" -> "io error modifying /does-not-exist/" -// ignore-tidy-linelength // ignore-windows - this is a unix-specific test // ignore-emscripten - the file-system issues do not replicate here // ignore-wasm - the file-system issues do not replicate here diff --git a/src/test/ui/noncopyable-class.stderr b/src/test/ui/noncopyable-class.stderr index b8e467d8402be..4674c16eb433a 100644 --- a/src/test/ui/noncopyable-class.stderr +++ b/src/test/ui/noncopyable-class.stderr @@ -6,14 +6,6 @@ LL | struct Foo { ... LL | let _y = x.clone(); | ^^^^^ method not found in `Foo` - | - ::: $SRC_DIR/core/src/clone.rs:LL:COL - | -LL | fn clone(&self) -> Self; - | ----- - | | - | the method is available for `Arc<Foo>` here - | the method is available for `Rc<Foo>` here | = help: items from traits can only be used if the trait is implemented and in scope = note: the following trait defines an item `clone`, perhaps you need to implement it: diff --git a/src/test/ui/or-patterns/already-bound-name.rs b/src/test/ui/or-patterns/already-bound-name.rs index 726e17b7ec226..65c25293d9ecc 100644 --- a/src/test/ui/or-patterns/already-bound-name.rs +++ b/src/test/ui/or-patterns/already-bound-name.rs @@ -1,8 +1,6 @@ // This test ensures that the "already bound identifier in a product pattern" // correctly accounts for or-patterns. -#![feature(or_patterns)] - enum E<T> { A(T, T), B(T) } use E::*; @@ -18,10 +16,10 @@ fn main() { let (A(a, _) | B(a), a) = (A(0, 1), 2); //~^ ERROR identifier `a` is bound more than once in the same pattern - let A(a, a) | B(a) = A(0, 1); + let (A(a, a) | B(a)) = A(0, 1); //~^ ERROR identifier `a` is bound more than once in the same pattern - let B(a) | A(a, a) = A(0, 1); + let (B(a) | A(a, a)) = A(0, 1); //~^ ERROR identifier `a` is bound more than once in the same pattern match A(0, 1) { @@ -29,17 +27,17 @@ fn main() { //~^ ERROR identifier `a` is bound more than once in the same pattern } - let B(A(a, _) | B(a)) | A(a, A(a, _) | B(a)) = B(B(1)); + let (B(A(a, _) | B(a)) | A(a, A(a, _) | B(a))) = B(B(1)); //~^ ERROR identifier `a` is bound more than once in the same pattern //~| ERROR identifier `a` is bound more than once in the same pattern //~| ERROR mismatched types - let B(_) | A(A(a, _) | B(a), A(a, _) | B(a)) = B(B(1)); + let (B(_) | A(A(a, _) | B(a), A(a, _) | B(a))) = B(B(1)); //~^ ERROR identifier `a` is bound more than once in the same pattern //~| ERROR identifier `a` is bound more than once in the same pattern //~| ERROR variable `a` is not bound in all patterns - let B(A(a, _) | B(a)) | A(A(a, _) | B(a), A(a, _) | B(a)) = B(B(1)); + let (B(A(a, _) | B(a)) | A(A(a, _) | B(a), A(a, _) | B(a))) = B(B(1)); //~^ ERROR identifier `a` is bound more than once in the same pattern //~| ERROR identifier `a` is bound more than once in the same pattern } diff --git a/src/test/ui/or-patterns/already-bound-name.stderr b/src/test/ui/or-patterns/already-bound-name.stderr index 97933ca122944..66112165622b9 100644 --- a/src/test/ui/or-patterns/already-bound-name.stderr +++ b/src/test/ui/or-patterns/already-bound-name.stderr @@ -1,95 +1,95 @@ error[E0416]: identifier `a` is bound more than once in the same pattern - --> $DIR/already-bound-name.rs:11:13 + --> $DIR/already-bound-name.rs:9:13 | LL | let (a, a) = (0, 1); // Standard duplication without an or-pattern. | ^ used in a pattern more than once error[E0416]: identifier `a` is bound more than once in the same pattern - --> $DIR/already-bound-name.rs:14:15 + --> $DIR/already-bound-name.rs:12:15 | LL | let (a, A(a, _) | B(a)) = (0, A(1, 2)); | ^ used in a pattern more than once error[E0416]: identifier `a` is bound more than once in the same pattern - --> $DIR/already-bound-name.rs:14:25 + --> $DIR/already-bound-name.rs:12:25 | LL | let (a, A(a, _) | B(a)) = (0, A(1, 2)); | ^ used in a pattern more than once error[E0416]: identifier `a` is bound more than once in the same pattern - --> $DIR/already-bound-name.rs:18:26 + --> $DIR/already-bound-name.rs:16:26 | LL | let (A(a, _) | B(a), a) = (A(0, 1), 2); | ^ used in a pattern more than once error[E0416]: identifier `a` is bound more than once in the same pattern - --> $DIR/already-bound-name.rs:21:14 + --> $DIR/already-bound-name.rs:19:15 | -LL | let A(a, a) | B(a) = A(0, 1); - | ^ used in a pattern more than once +LL | let (A(a, a) | B(a)) = A(0, 1); + | ^ used in a pattern more than once error[E0416]: identifier `a` is bound more than once in the same pattern - --> $DIR/already-bound-name.rs:24:21 + --> $DIR/already-bound-name.rs:22:22 | -LL | let B(a) | A(a, a) = A(0, 1); - | ^ used in a pattern more than once +LL | let (B(a) | A(a, a)) = A(0, 1); + | ^ used in a pattern more than once error[E0416]: identifier `a` is bound more than once in the same pattern - --> $DIR/already-bound-name.rs:28:21 + --> $DIR/already-bound-name.rs:26:21 | LL | B(a) | A(a, a) => {} // Let's ensure `match` has no funny business. | ^ used in a pattern more than once error[E0416]: identifier `a` is bound more than once in the same pattern - --> $DIR/already-bound-name.rs:32:36 + --> $DIR/already-bound-name.rs:30:37 | -LL | let B(A(a, _) | B(a)) | A(a, A(a, _) | B(a)) = B(B(1)); - | ^ used in a pattern more than once +LL | let (B(A(a, _) | B(a)) | A(a, A(a, _) | B(a))) = B(B(1)); + | ^ used in a pattern more than once error[E0416]: identifier `a` is bound more than once in the same pattern - --> $DIR/already-bound-name.rs:32:46 + --> $DIR/already-bound-name.rs:30:47 | -LL | let B(A(a, _) | B(a)) | A(a, A(a, _) | B(a)) = B(B(1)); - | ^ used in a pattern more than once +LL | let (B(A(a, _) | B(a)) | A(a, A(a, _) | B(a))) = B(B(1)); + | ^ used in a pattern more than once error[E0416]: identifier `a` is bound more than once in the same pattern - --> $DIR/already-bound-name.rs:37:36 + --> $DIR/already-bound-name.rs:35:37 | -LL | let B(_) | A(A(a, _) | B(a), A(a, _) | B(a)) = B(B(1)); - | ^ used in a pattern more than once +LL | let (B(_) | A(A(a, _) | B(a), A(a, _) | B(a))) = B(B(1)); + | ^ used in a pattern more than once error[E0416]: identifier `a` is bound more than once in the same pattern - --> $DIR/already-bound-name.rs:37:46 + --> $DIR/already-bound-name.rs:35:47 | -LL | let B(_) | A(A(a, _) | B(a), A(a, _) | B(a)) = B(B(1)); - | ^ used in a pattern more than once +LL | let (B(_) | A(A(a, _) | B(a), A(a, _) | B(a))) = B(B(1)); + | ^ used in a pattern more than once error[E0408]: variable `a` is not bound in all patterns - --> $DIR/already-bound-name.rs:37:9 + --> $DIR/already-bound-name.rs:35:10 | -LL | let B(_) | A(A(a, _) | B(a), A(a, _) | B(a)) = B(B(1)); - | ^^^^ pattern doesn't bind `a` - variable not in all patterns +LL | let (B(_) | A(A(a, _) | B(a), A(a, _) | B(a))) = B(B(1)); + | ^^^^ pattern doesn't bind `a` - variable not in all patterns error[E0416]: identifier `a` is bound more than once in the same pattern - --> $DIR/already-bound-name.rs:42:49 + --> $DIR/already-bound-name.rs:40:50 | -LL | let B(A(a, _) | B(a)) | A(A(a, _) | B(a), A(a, _) | B(a)) = B(B(1)); - | ^ used in a pattern more than once +LL | let (B(A(a, _) | B(a)) | A(A(a, _) | B(a), A(a, _) | B(a))) = B(B(1)); + | ^ used in a pattern more than once error[E0416]: identifier `a` is bound more than once in the same pattern - --> $DIR/already-bound-name.rs:42:59 + --> $DIR/already-bound-name.rs:40:60 | -LL | let B(A(a, _) | B(a)) | A(A(a, _) | B(a), A(a, _) | B(a)) = B(B(1)); - | ^ used in a pattern more than once +LL | let (B(A(a, _) | B(a)) | A(A(a, _) | B(a), A(a, _) | B(a))) = B(B(1)); + | ^ used in a pattern more than once error[E0308]: mismatched types - --> $DIR/already-bound-name.rs:32:31 + --> $DIR/already-bound-name.rs:30:32 | -LL | let B(A(a, _) | B(a)) | A(a, A(a, _) | B(a)) = B(B(1)); - | - ^ ------- this expression has type `E<E<{integer}>>` - | | | - | | expected integer, found enum `E` - | first introduced with type `{integer}` here +LL | let (B(A(a, _) | B(a)) | A(a, A(a, _) | B(a))) = B(B(1)); + | - ^ ------- this expression has type `E<E<{integer}>>` + | | | + | | expected integer, found enum `E` + | first introduced with type `{integer}` here | = note: expected type `{integer}` found type `E<{integer}>` diff --git a/src/test/ui/or-patterns/basic-switch.rs b/src/test/ui/or-patterns/basic-switch.rs index 6daa9d9255b80..674fbc3cc99fc 100644 --- a/src/test/ui/or-patterns/basic-switch.rs +++ b/src/test/ui/or-patterns/basic-switch.rs @@ -3,8 +3,6 @@ // run-pass -#![feature(or_patterns)] - #[derive(Debug)] enum Test { Foo, diff --git a/src/test/ui/or-patterns/basic-switchint.rs b/src/test/ui/or-patterns/basic-switchint.rs index c5a6d894eacec..adb902caf011d 100644 --- a/src/test/ui/or-patterns/basic-switchint.rs +++ b/src/test/ui/or-patterns/basic-switchint.rs @@ -3,8 +3,6 @@ // run-pass -#![feature(or_patterns)] - #[derive(Debug, PartialEq)] enum MatchArm { Arm(usize), diff --git a/src/test/ui/or-patterns/bindings-runpass-1.rs b/src/test/ui/or-patterns/bindings-runpass-1.rs index 0087167af7ecc..3406d5197c497 100644 --- a/src/test/ui/or-patterns/bindings-runpass-1.rs +++ b/src/test/ui/or-patterns/bindings-runpass-1.rs @@ -1,7 +1,5 @@ // run-pass -#![feature(or_patterns)] - fn two_bindings(x: &((bool, bool), u8)) -> u8 { match x { &((true, y) | (y, true), z @ (0 | 4)) => (y as u8) + z, diff --git a/src/test/ui/or-patterns/bindings-runpass-2.rs b/src/test/ui/or-patterns/bindings-runpass-2.rs index 0e1eb7b2e030b..5b9bb748c7cee 100644 --- a/src/test/ui/or-patterns/bindings-runpass-2.rs +++ b/src/test/ui/or-patterns/bindings-runpass-2.rs @@ -1,7 +1,5 @@ // run-pass -#![feature(or_patterns)] - fn or_at(x: Result<u32, u32>) -> u32 { match x { Ok(x @ 4) | Err(x @ (6 | 8)) => x, diff --git a/src/test/ui/or-patterns/box-patterns.rs b/src/test/ui/or-patterns/box-patterns.rs index aafd47993836c..73051401c181e 100644 --- a/src/test/ui/or-patterns/box-patterns.rs +++ b/src/test/ui/or-patterns/box-patterns.rs @@ -2,7 +2,6 @@ // run-pass -#![feature(or_patterns)] #![feature(box_patterns)] #[derive(Debug, PartialEq)] diff --git a/src/test/ui/or-patterns/consistent-bindings.rs b/src/test/ui/or-patterns/consistent-bindings.rs index 3ee57978bb009..ecae1d8a2732a 100644 --- a/src/test/ui/or-patterns/consistent-bindings.rs +++ b/src/test/ui/or-patterns/consistent-bindings.rs @@ -4,13 +4,11 @@ // check-pass -#![feature(or_patterns)] - fn main() { // One level: - let Ok(a) | Err(a) = Ok(0); - let Ok(ref a) | Err(ref a) = Ok(0); - let Ok(ref mut a) | Err(ref mut a) = Ok(0); + let (Ok(a) | Err(a)) = Ok(0); + let (Ok(ref a) | Err(ref a)) = Ok(0); + let (Ok(ref mut a) | Err(ref mut a)) = Ok(0); // Two levels: enum Tri<S, T, U> { @@ -20,10 +18,10 @@ fn main() { } use Tri::*; - let Ok((V1(a) | V2(a) | V3(a), b)) | Err(Ok((a, b)) | Err((a, b))): Result<_, Result<_, _>> = + let (Ok((V1(a) | V2(a) | V3(a), b)) | Err(Ok((a, b)) | Err((a, b)))): Result<_, Result<_, _>> = Ok((V1(1), 1)); - let Ok((V1(a) | V2(a) | V3(a), ref b)) | Err(Ok((a, ref b)) | Err((a, ref b))): Result< + let (Ok((V1(a) | V2(a) | V3(a), ref b)) | Err(Ok((a, ref b)) | Err((a, ref b)))): Result< _, Result<_, _>, > = Ok((V1(1), 1)); diff --git a/src/test/ui/or-patterns/const-fn.rs b/src/test/ui/or-patterns/const-fn.rs index f4af2f0d2dd40..ca512ac711905 100644 --- a/src/test/ui/or-patterns/const-fn.rs +++ b/src/test/ui/or-patterns/const-fn.rs @@ -1,30 +1,29 @@ // check-pass -#![feature(or_patterns)] const fn foo((Ok(a) | Err(a)): Result<i32, i32>) { let x = Ok(3); - let Ok(y) | Err(y) = x; + let (Ok(y) | Err(y)) = x; } const X: () = { let x = Ok(3); - let Ok(y) | Err(y) = x; + let (Ok(y) | Err(y)) = x; }; static Y: () = { let x = Ok(3); - let Ok(y) | Err(y) = x; + let (Ok(y) | Err(y)) = x; }; static mut Z: () = { let x = Ok(3); - let Ok(y) | Err(y) = x; + let (Ok(y) | Err(y)) = x; }; fn main() { let _: [(); { let x = Ok(3); - let Ok(y) | Err(y) = x; + let (Ok(y) | Err(y)) = x; 2 }]; } diff --git a/src/test/ui/or-patterns/exhaustiveness-non-exhaustive.rs b/src/test/ui/or-patterns/exhaustiveness-non-exhaustive.rs index f2d5de75b65bd..5999e04e0e2dd 100644 --- a/src/test/ui/or-patterns/exhaustiveness-non-exhaustive.rs +++ b/src/test/ui/or-patterns/exhaustiveness-non-exhaustive.rs @@ -1,4 +1,3 @@ -#![feature(or_patterns)] #![deny(unreachable_patterns)] // We wrap patterns in a tuple because top-level or-patterns were special-cased. diff --git a/src/test/ui/or-patterns/exhaustiveness-non-exhaustive.stderr b/src/test/ui/or-patterns/exhaustiveness-non-exhaustive.stderr index d3ae798ead5a7..44f334eee9386 100644 --- a/src/test/ui/or-patterns/exhaustiveness-non-exhaustive.stderr +++ b/src/test/ui/or-patterns/exhaustiveness-non-exhaustive.stderr @@ -1,5 +1,5 @@ error[E0004]: non-exhaustive patterns: `(2_u8..=u8::MAX, _)` not covered - --> $DIR/exhaustiveness-non-exhaustive.rs:6:11 + --> $DIR/exhaustiveness-non-exhaustive.rs:5:11 | LL | match (0u8, 0u8) { | ^^^^^^^^^^ pattern `(2_u8..=u8::MAX, _)` not covered @@ -8,7 +8,7 @@ LL | match (0u8, 0u8) { = note: the matched value is of type `(u8, u8)` error[E0004]: non-exhaustive patterns: `((4_u8..=u8::MAX))` not covered - --> $DIR/exhaustiveness-non-exhaustive.rs:10:11 + --> $DIR/exhaustiveness-non-exhaustive.rs:9:11 | LL | match ((0u8,),) { | ^^^^^^^^^ pattern `((4_u8..=u8::MAX))` not covered @@ -17,7 +17,7 @@ LL | match ((0u8,),) { = note: the matched value is of type `((u8,),)` error[E0004]: non-exhaustive patterns: `(Some(2_u8..=u8::MAX))` not covered - --> $DIR/exhaustiveness-non-exhaustive.rs:14:11 + --> $DIR/exhaustiveness-non-exhaustive.rs:13:11 | LL | match (Some(0u8),) { | ^^^^^^^^^^^^ pattern `(Some(2_u8..=u8::MAX))` not covered diff --git a/src/test/ui/or-patterns/exhaustiveness-pass.rs b/src/test/ui/or-patterns/exhaustiveness-pass.rs index 8dcf8792f6f96..e8c8a0e7ba52a 100644 --- a/src/test/ui/or-patterns/exhaustiveness-pass.rs +++ b/src/test/ui/or-patterns/exhaustiveness-pass.rs @@ -1,4 +1,3 @@ -#![feature(or_patterns)] #![deny(unreachable_patterns)] // check-pass diff --git a/src/test/ui/or-patterns/exhaustiveness-unreachable-pattern.rs b/src/test/ui/or-patterns/exhaustiveness-unreachable-pattern.rs index bdb7a1ec92b7f..8429799cabf15 100644 --- a/src/test/ui/or-patterns/exhaustiveness-unreachable-pattern.rs +++ b/src/test/ui/or-patterns/exhaustiveness-unreachable-pattern.rs @@ -1,4 +1,3 @@ -#![feature(or_patterns)] #![deny(unreachable_patterns)] // We wrap patterns in a tuple because top-level or-patterns were special-cased. diff --git a/src/test/ui/or-patterns/exhaustiveness-unreachable-pattern.stderr b/src/test/ui/or-patterns/exhaustiveness-unreachable-pattern.stderr index 51991fc603967..a84681e0056f0 100644 --- a/src/test/ui/or-patterns/exhaustiveness-unreachable-pattern.stderr +++ b/src/test/ui/or-patterns/exhaustiveness-unreachable-pattern.stderr @@ -1,138 +1,138 @@ error: unreachable pattern - --> $DIR/exhaustiveness-unreachable-pattern.rs:8:9 + --> $DIR/exhaustiveness-unreachable-pattern.rs:7:9 | LL | (1,) => {} | ^^^^ | note: the lint level is defined here - --> $DIR/exhaustiveness-unreachable-pattern.rs:2:9 + --> $DIR/exhaustiveness-unreachable-pattern.rs:1:9 | LL | #![deny(unreachable_patterns)] | ^^^^^^^^^^^^^^^^^^^^ error: unreachable pattern - --> $DIR/exhaustiveness-unreachable-pattern.rs:13:9 + --> $DIR/exhaustiveness-unreachable-pattern.rs:12:9 | LL | (2,) => {} | ^^^^ error: unreachable pattern - --> $DIR/exhaustiveness-unreachable-pattern.rs:19:9 + --> $DIR/exhaustiveness-unreachable-pattern.rs:18:9 | LL | (1 | 2,) => {} | ^^^^^^^^ error: unreachable pattern - --> $DIR/exhaustiveness-unreachable-pattern.rs:24:9 + --> $DIR/exhaustiveness-unreachable-pattern.rs:23:9 | LL | (1, 3) => {} | ^^^^^^ error: unreachable pattern - --> $DIR/exhaustiveness-unreachable-pattern.rs:25:9 + --> $DIR/exhaustiveness-unreachable-pattern.rs:24:9 | LL | (1, 4) => {} | ^^^^^^ error: unreachable pattern - --> $DIR/exhaustiveness-unreachable-pattern.rs:26:9 + --> $DIR/exhaustiveness-unreachable-pattern.rs:25:9 | LL | (2, 4) => {} | ^^^^^^ error: unreachable pattern - --> $DIR/exhaustiveness-unreachable-pattern.rs:27:9 + --> $DIR/exhaustiveness-unreachable-pattern.rs:26:9 | LL | (2 | 1, 4) => {} | ^^^^^^^^^^ error: unreachable pattern - --> $DIR/exhaustiveness-unreachable-pattern.rs:29:9 + --> $DIR/exhaustiveness-unreachable-pattern.rs:28:9 | LL | (1, 4 | 5) => {} | ^^^^^^^^^^ error: unreachable pattern - --> $DIR/exhaustiveness-unreachable-pattern.rs:37:9 + --> $DIR/exhaustiveness-unreachable-pattern.rs:36:9 | LL | (Some(1),) => {} | ^^^^^^^^^^ error: unreachable pattern - --> $DIR/exhaustiveness-unreachable-pattern.rs:38:9 + --> $DIR/exhaustiveness-unreachable-pattern.rs:37:9 | LL | (None,) => {} | ^^^^^^^ error: unreachable pattern - --> $DIR/exhaustiveness-unreachable-pattern.rs:43:9 + --> $DIR/exhaustiveness-unreachable-pattern.rs:42:9 | LL | ((1..=4,),) => {} | ^^^^^^^^^^^ error: unreachable pattern - --> $DIR/exhaustiveness-unreachable-pattern.rs:48:14 + --> $DIR/exhaustiveness-unreachable-pattern.rs:47:14 | LL | (1 | 1,) => {} | ^ error: unreachable pattern - --> $DIR/exhaustiveness-unreachable-pattern.rs:52:19 + --> $DIR/exhaustiveness-unreachable-pattern.rs:51:19 | LL | (0 | 1) | 1 => {} | ^ error: unreachable pattern - --> $DIR/exhaustiveness-unreachable-pattern.rs:58:14 + --> $DIR/exhaustiveness-unreachable-pattern.rs:57:14 | LL | 0 | (0 | 0) => {} | ^ error: unreachable pattern - --> $DIR/exhaustiveness-unreachable-pattern.rs:58:18 + --> $DIR/exhaustiveness-unreachable-pattern.rs:57:18 | LL | 0 | (0 | 0) => {} | ^ error: unreachable pattern - --> $DIR/exhaustiveness-unreachable-pattern.rs:66:13 + --> $DIR/exhaustiveness-unreachable-pattern.rs:65:13 | LL | / Some( LL | | 0 | 0) => {} | |______________________^ error: unreachable pattern - --> $DIR/exhaustiveness-unreachable-pattern.rs:72:15 + --> $DIR/exhaustiveness-unreachable-pattern.rs:71:15 | LL | | 0 | ^ error: unreachable pattern - --> $DIR/exhaustiveness-unreachable-pattern.rs:74:15 + --> $DIR/exhaustiveness-unreachable-pattern.rs:73:15 | LL | | 0] => {} | ^ error: unreachable pattern - --> $DIR/exhaustiveness-unreachable-pattern.rs:82:10 + --> $DIR/exhaustiveness-unreachable-pattern.rs:81:10 | LL | [1 | ^ error: unreachable pattern - --> $DIR/exhaustiveness-unreachable-pattern.rs:94:10 + --> $DIR/exhaustiveness-unreachable-pattern.rs:93:10 | LL | [true | ^^^^ error: unreachable pattern - --> $DIR/exhaustiveness-unreachable-pattern.rs:101:36 + --> $DIR/exhaustiveness-unreachable-pattern.rs:100:36 | LL | (true | false, None | Some(true | ^^^^ error: unreachable pattern - --> $DIR/exhaustiveness-unreachable-pattern.rs:106:14 + --> $DIR/exhaustiveness-unreachable-pattern.rs:105:14 | LL | (true | ^^^^ @@ -143,25 +143,25 @@ LL | (true | false, None | Some(t_or_f!())) => {} = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) error: unreachable pattern - --> $DIR/exhaustiveness-unreachable-pattern.rs:117:14 + --> $DIR/exhaustiveness-unreachable-pattern.rs:116:14 | LL | Some(0 | ^ error: unreachable pattern - --> $DIR/exhaustiveness-unreachable-pattern.rs:136:19 + --> $DIR/exhaustiveness-unreachable-pattern.rs:135:19 | LL | | false) => {} | ^^^^^ error: unreachable pattern - --> $DIR/exhaustiveness-unreachable-pattern.rs:144:15 + --> $DIR/exhaustiveness-unreachable-pattern.rs:143:15 | LL | | true) => {} | ^^^^ error: unreachable pattern - --> $DIR/exhaustiveness-unreachable-pattern.rs:150:15 + --> $DIR/exhaustiveness-unreachable-pattern.rs:149:15 | LL | | true, | ^^^^ diff --git a/src/test/ui/or-patterns/feature-gate-or_patterns-leading-for.rs b/src/test/ui/or-patterns/feature-gate-or_patterns-leading-for.rs deleted file mode 100644 index de8e1bba5576c..0000000000000 --- a/src/test/ui/or-patterns/feature-gate-or_patterns-leading-for.rs +++ /dev/null @@ -1,8 +0,0 @@ -// Test feature gating for a sole leading `|` in `let`. - -fn main() {} - -#[cfg(FALSE)] -fn gated_leading_vert_in_let() { - for | A in 0 {} //~ ERROR or-patterns syntax is experimental -} diff --git a/src/test/ui/or-patterns/feature-gate-or_patterns-leading-for.stderr b/src/test/ui/or-patterns/feature-gate-or_patterns-leading-for.stderr deleted file mode 100644 index abcee43553020..0000000000000 --- a/src/test/ui/or-patterns/feature-gate-or_patterns-leading-for.stderr +++ /dev/null @@ -1,12 +0,0 @@ -error[E0658]: or-patterns syntax is experimental - --> $DIR/feature-gate-or_patterns-leading-for.rs:7:9 - | -LL | for | A in 0 {} - | ^ - | - = note: see issue #54883 <https://github.com/rust-lang/rust/issues/54883> for more information - = help: add `#![feature(or_patterns)]` to the crate attributes to enable - -error: aborting due to previous error - -For more information about this error, try `rustc --explain E0658`. diff --git a/src/test/ui/or-patterns/feature-gate-or_patterns-leading-let.rs b/src/test/ui/or-patterns/feature-gate-or_patterns-leading-let.rs deleted file mode 100644 index a4ea4e25d861e..0000000000000 --- a/src/test/ui/or-patterns/feature-gate-or_patterns-leading-let.rs +++ /dev/null @@ -1,8 +0,0 @@ -// Test feature gating for a sole leading `|` in `let`. - -fn main() {} - -#[cfg(FALSE)] -fn gated_leading_vert_in_let() { - let | A; //~ ERROR or-patterns syntax is experimental -} diff --git a/src/test/ui/or-patterns/feature-gate-or_patterns-leading-let.stderr b/src/test/ui/or-patterns/feature-gate-or_patterns-leading-let.stderr deleted file mode 100644 index 499f60dd545ff..0000000000000 --- a/src/test/ui/or-patterns/feature-gate-or_patterns-leading-let.stderr +++ /dev/null @@ -1,12 +0,0 @@ -error[E0658]: or-patterns syntax is experimental - --> $DIR/feature-gate-or_patterns-leading-let.rs:7:9 - | -LL | let | A; - | ^ - | - = note: see issue #54883 <https://github.com/rust-lang/rust/issues/54883> for more information - = help: add `#![feature(or_patterns)]` to the crate attributes to enable - -error: aborting due to previous error - -For more information about this error, try `rustc --explain E0658`. diff --git a/src/test/ui/or-patterns/feature-gate-or_patterns.rs b/src/test/ui/or-patterns/feature-gate-or_patterns.rs deleted file mode 100644 index e638838147a4d..0000000000000 --- a/src/test/ui/or-patterns/feature-gate-or_patterns.rs +++ /dev/null @@ -1,52 +0,0 @@ -fn main() {} - -pub fn example(x: Option<usize>) { - match x { - Some(0 | 1 | 2) => {} - //~^ ERROR: or-patterns syntax is experimental - _ => {} - } -} - -// Test the `pat` macro fragment parser: -macro_rules! accept_pat { - ($p:pat) => {} -} - -accept_pat!((p | q)); //~ ERROR or-patterns syntax is experimental -accept_pat!((p | q,)); //~ ERROR or-patterns syntax is experimental -accept_pat!(TS(p | q)); //~ ERROR or-patterns syntax is experimental -accept_pat!(NS { f: p | q }); //~ ERROR or-patterns syntax is experimental -accept_pat!([p | q]); //~ ERROR or-patterns syntax is experimental - -// Non-macro tests: - -#[cfg(FALSE)] -fn or_patterns() { - // Gated: - - let | A | B; //~ ERROR or-patterns syntax is experimental - let A | B; //~ ERROR or-patterns syntax is experimental - for | A | B in 0 {} //~ ERROR or-patterns syntax is experimental - for A | B in 0 {} //~ ERROR or-patterns syntax is experimental - fn fun((A | B): _) {} //~ ERROR or-patterns syntax is experimental - let _ = |(A | B): u8| (); //~ ERROR or-patterns syntax is experimental - let (A | B); //~ ERROR or-patterns syntax is experimental - let (A | B,); //~ ERROR or-patterns syntax is experimental - let A(B | C); //~ ERROR or-patterns syntax is experimental - let E::V(B | C); //~ ERROR or-patterns syntax is experimental - let S { f1: B | C, f2 }; //~ ERROR or-patterns syntax is experimental - let E::V { f1: B | C, f2 }; //~ ERROR or-patterns syntax is experimental - let [A | B]; //~ ERROR or-patterns syntax is experimental - - // Top level of `while`, `if`, and `match` arms are allowed: - - while let | A = 0 {} - while let A | B = 0 {} - if let | A = 0 {} - if let A | B = 0 {} - match 0 { - | A => {}, - A | B => {}, - } -} diff --git a/src/test/ui/or-patterns/feature-gate-or_patterns.stderr b/src/test/ui/or-patterns/feature-gate-or_patterns.stderr deleted file mode 100644 index c01d17c0b3d7e..0000000000000 --- a/src/test/ui/or-patterns/feature-gate-or_patterns.stderr +++ /dev/null @@ -1,174 +0,0 @@ -error[E0658]: or-patterns syntax is experimental - --> $DIR/feature-gate-or_patterns.rs:5:14 - | -LL | Some(0 | 1 | 2) => {} - | ^^^^^^^^^ - | - = note: see issue #54883 <https://github.com/rust-lang/rust/issues/54883> for more information - = help: add `#![feature(or_patterns)]` to the crate attributes to enable - -error[E0658]: or-patterns syntax is experimental - --> $DIR/feature-gate-or_patterns.rs:28:9 - | -LL | let | A | B; - | ^^^^^^^ - | - = note: see issue #54883 <https://github.com/rust-lang/rust/issues/54883> for more information - = help: add `#![feature(or_patterns)]` to the crate attributes to enable - -error[E0658]: or-patterns syntax is experimental - --> $DIR/feature-gate-or_patterns.rs:29:9 - | -LL | let A | B; - | ^^^^^ - | - = note: see issue #54883 <https://github.com/rust-lang/rust/issues/54883> for more information - = help: add `#![feature(or_patterns)]` to the crate attributes to enable - -error[E0658]: or-patterns syntax is experimental - --> $DIR/feature-gate-or_patterns.rs:30:9 - | -LL | for | A | B in 0 {} - | ^^^^^^^ - | - = note: see issue #54883 <https://github.com/rust-lang/rust/issues/54883> for more information - = help: add `#![feature(or_patterns)]` to the crate attributes to enable - -error[E0658]: or-patterns syntax is experimental - --> $DIR/feature-gate-or_patterns.rs:31:9 - | -LL | for A | B in 0 {} - | ^^^^^ - | - = note: see issue #54883 <https://github.com/rust-lang/rust/issues/54883> for more information - = help: add `#![feature(or_patterns)]` to the crate attributes to enable - -error[E0658]: or-patterns syntax is experimental - --> $DIR/feature-gate-or_patterns.rs:32:13 - | -LL | fn fun((A | B): _) {} - | ^^^^^ - | - = note: see issue #54883 <https://github.com/rust-lang/rust/issues/54883> for more information - = help: add `#![feature(or_patterns)]` to the crate attributes to enable - -error[E0658]: or-patterns syntax is experimental - --> $DIR/feature-gate-or_patterns.rs:33:15 - | -LL | let _ = |(A | B): u8| (); - | ^^^^^ - | - = note: see issue #54883 <https://github.com/rust-lang/rust/issues/54883> for more information - = help: add `#![feature(or_patterns)]` to the crate attributes to enable - -error[E0658]: or-patterns syntax is experimental - --> $DIR/feature-gate-or_patterns.rs:34:10 - | -LL | let (A | B); - | ^^^^^ - | - = note: see issue #54883 <https://github.com/rust-lang/rust/issues/54883> for more information - = help: add `#![feature(or_patterns)]` to the crate attributes to enable - -error[E0658]: or-patterns syntax is experimental - --> $DIR/feature-gate-or_patterns.rs:35:10 - | -LL | let (A | B,); - | ^^^^^ - | - = note: see issue #54883 <https://github.com/rust-lang/rust/issues/54883> for more information - = help: add `#![feature(or_patterns)]` to the crate attributes to enable - -error[E0658]: or-patterns syntax is experimental - --> $DIR/feature-gate-or_patterns.rs:36:11 - | -LL | let A(B | C); - | ^^^^^ - | - = note: see issue #54883 <https://github.com/rust-lang/rust/issues/54883> for more information - = help: add `#![feature(or_patterns)]` to the crate attributes to enable - -error[E0658]: or-patterns syntax is experimental - --> $DIR/feature-gate-or_patterns.rs:37:14 - | -LL | let E::V(B | C); - | ^^^^^ - | - = note: see issue #54883 <https://github.com/rust-lang/rust/issues/54883> for more information - = help: add `#![feature(or_patterns)]` to the crate attributes to enable - -error[E0658]: or-patterns syntax is experimental - --> $DIR/feature-gate-or_patterns.rs:38:17 - | -LL | let S { f1: B | C, f2 }; - | ^^^^^ - | - = note: see issue #54883 <https://github.com/rust-lang/rust/issues/54883> for more information - = help: add `#![feature(or_patterns)]` to the crate attributes to enable - -error[E0658]: or-patterns syntax is experimental - --> $DIR/feature-gate-or_patterns.rs:39:20 - | -LL | let E::V { f1: B | C, f2 }; - | ^^^^^ - | - = note: see issue #54883 <https://github.com/rust-lang/rust/issues/54883> for more information - = help: add `#![feature(or_patterns)]` to the crate attributes to enable - -error[E0658]: or-patterns syntax is experimental - --> $DIR/feature-gate-or_patterns.rs:40:10 - | -LL | let [A | B]; - | ^^^^^ - | - = note: see issue #54883 <https://github.com/rust-lang/rust/issues/54883> for more information - = help: add `#![feature(or_patterns)]` to the crate attributes to enable - -error[E0658]: or-patterns syntax is experimental - --> $DIR/feature-gate-or_patterns.rs:16:14 - | -LL | accept_pat!((p | q)); - | ^^^^^ - | - = note: see issue #54883 <https://github.com/rust-lang/rust/issues/54883> for more information - = help: add `#![feature(or_patterns)]` to the crate attributes to enable - -error[E0658]: or-patterns syntax is experimental - --> $DIR/feature-gate-or_patterns.rs:17:14 - | -LL | accept_pat!((p | q,)); - | ^^^^^ - | - = note: see issue #54883 <https://github.com/rust-lang/rust/issues/54883> for more information - = help: add `#![feature(or_patterns)]` to the crate attributes to enable - -error[E0658]: or-patterns syntax is experimental - --> $DIR/feature-gate-or_patterns.rs:18:16 - | -LL | accept_pat!(TS(p | q)); - | ^^^^^ - | - = note: see issue #54883 <https://github.com/rust-lang/rust/issues/54883> for more information - = help: add `#![feature(or_patterns)]` to the crate attributes to enable - -error[E0658]: or-patterns syntax is experimental - --> $DIR/feature-gate-or_patterns.rs:19:21 - | -LL | accept_pat!(NS { f: p | q }); - | ^^^^^ - | - = note: see issue #54883 <https://github.com/rust-lang/rust/issues/54883> for more information - = help: add `#![feature(or_patterns)]` to the crate attributes to enable - -error[E0658]: or-patterns syntax is experimental - --> $DIR/feature-gate-or_patterns.rs:20:14 - | -LL | accept_pat!([p | q]); - | ^^^^^ - | - = note: see issue #54883 <https://github.com/rust-lang/rust/issues/54883> for more information - = help: add `#![feature(or_patterns)]` to the crate attributes to enable - -error: aborting due to 19 previous errors - -For more information about this error, try `rustc --explain E0658`. diff --git a/src/test/ui/or-patterns/fn-param-wrap-parens.fixed b/src/test/ui/or-patterns/fn-param-wrap-parens.fixed index bbc75d2b411eb..b9490aaf9de05 100644 --- a/src/test/ui/or-patterns/fn-param-wrap-parens.fixed +++ b/src/test/ui/or-patterns/fn-param-wrap-parens.fixed @@ -2,7 +2,6 @@ // run-rustfix -#![feature(or_patterns)] #![allow(warnings)] fn main() {} @@ -11,4 +10,4 @@ enum E { A, B } use E::*; #[cfg(FALSE)] -fn fun1((A | B): E) {} //~ ERROR an or-pattern parameter must be wrapped in parentheses +fn fun1((A | B): E) {} //~ ERROR top-level or-patterns are not allowed diff --git a/src/test/ui/or-patterns/fn-param-wrap-parens.rs b/src/test/ui/or-patterns/fn-param-wrap-parens.rs index 65b93dcbf7467..8e703d274c785 100644 --- a/src/test/ui/or-patterns/fn-param-wrap-parens.rs +++ b/src/test/ui/or-patterns/fn-param-wrap-parens.rs @@ -2,7 +2,6 @@ // run-rustfix -#![feature(or_patterns)] #![allow(warnings)] fn main() {} @@ -11,4 +10,4 @@ enum E { A, B } use E::*; #[cfg(FALSE)] -fn fun1(A | B: E) {} //~ ERROR an or-pattern parameter must be wrapped in parentheses +fn fun1(A | B: E) {} //~ ERROR top-level or-patterns are not allowed diff --git a/src/test/ui/or-patterns/fn-param-wrap-parens.stderr b/src/test/ui/or-patterns/fn-param-wrap-parens.stderr index 0e6424a430043..73270284131f9 100644 --- a/src/test/ui/or-patterns/fn-param-wrap-parens.stderr +++ b/src/test/ui/or-patterns/fn-param-wrap-parens.stderr @@ -1,5 +1,5 @@ -error: an or-pattern parameter must be wrapped in parentheses - --> $DIR/fn-param-wrap-parens.rs:14:9 +error: top-level or-patterns are not allowed in function parameters + --> $DIR/fn-param-wrap-parens.rs:13:9 | LL | fn fun1(A | B: E) {} | ^^^^^ help: wrap the pattern in parentheses: `(A | B)` diff --git a/src/test/ui/or-patterns/for-loop.rs b/src/test/ui/or-patterns/for-loop.rs index b79af6c402ec5..11b61cb69f1d2 100644 --- a/src/test/ui/or-patterns/for-loop.rs +++ b/src/test/ui/or-patterns/for-loop.rs @@ -1,8 +1,6 @@ // Check that or patterns are lowered correctly in `for` loops. // run-pass -#![feature(or_patterns)] - fn main() { let v = vec![Ok(2), Err(3), Ok(5)]; let mut w = Vec::new(); diff --git a/src/test/ui/or-patterns/if-let-while-let.rs b/src/test/ui/or-patterns/if-let-while-let.rs index 9256360b29db9..92a1bb256668b 100644 --- a/src/test/ui/or-patterns/if-let-while-let.rs +++ b/src/test/ui/or-patterns/if-let-while-let.rs @@ -1,8 +1,6 @@ // Check that or patterns are lowered correctly in `if let` and `while let` expressions. // run-pass -#![feature(or_patterns)] - fn main() { let mut opt = Some(3); let mut w = Vec::new(); diff --git a/src/test/ui/or-patterns/inconsistent-modes.rs b/src/test/ui/or-patterns/inconsistent-modes.rs index fd5cb01ab42c5..a87a10ce8a4ca 100644 --- a/src/test/ui/or-patterns/inconsistent-modes.rs +++ b/src/test/ui/or-patterns/inconsistent-modes.rs @@ -1,26 +1,25 @@ // This test ensures that or patterns require binding mode consistency across arms. -#![feature(or_patterns)] #![allow(non_camel_case_types)] fn main() { // One level: - let Ok(a) | Err(ref a): Result<&u8, u8> = Ok(&0); + let (Ok(a) | Err(ref a)): Result<&u8, u8> = Ok(&0); //~^ ERROR variable `a` is bound inconsistently - let Ok(ref mut a) | Err(a): Result<u8, &mut u8> = Ok(0); + let (Ok(ref mut a) | Err(a)): Result<u8, &mut u8> = Ok(0); //~^ ERROR variable `a` is bound inconsistently - let Ok(ref a) | Err(ref mut a): Result<&u8, &mut u8> = Ok(&0); + let (Ok(ref a) | Err(ref mut a)): Result<&u8, &mut u8> = Ok(&0); //~^ ERROR variable `a` is bound inconsistently //~| ERROR mismatched types - let Ok((ref a, b)) | Err((ref mut a, ref b)) = Ok((0, &0)); + let (Ok((ref a, b)) | Err((ref mut a, ref b))) = Ok((0, &0)); //~^ ERROR variable `a` is bound inconsistently //~| ERROR variable `b` is bound inconsistently //~| ERROR mismatched types // Two levels: - let Ok(Ok(a) | Err(a)) | Err(ref a) = Err(0); + let (Ok(Ok(a) | Err(a)) | Err(ref a)) = Err(0); //~^ ERROR variable `a` is bound inconsistently // Three levels: - let Ok([Ok((Ok(ref a) | Err(a),)) | Err(a)]) | Err(a) = Err(&1); + let (Ok([Ok((Ok(ref a) | Err(a),)) | Err(a)]) | Err(a)) = Err(&1); //~^ ERROR variable `a` is bound inconsistently } diff --git a/src/test/ui/or-patterns/inconsistent-modes.stderr b/src/test/ui/or-patterns/inconsistent-modes.stderr index 15790771043df..dae6bb41e74e2 100644 --- a/src/test/ui/or-patterns/inconsistent-modes.stderr +++ b/src/test/ui/or-patterns/inconsistent-modes.stderr @@ -1,74 +1,74 @@ error[E0409]: variable `a` is bound inconsistently across alternatives separated by `|` - --> $DIR/inconsistent-modes.rs:7:25 + --> $DIR/inconsistent-modes.rs:6:26 | -LL | let Ok(a) | Err(ref a): Result<&u8, u8> = Ok(&0); - | - ^ bound in different ways - | | - | first binding +LL | let (Ok(a) | Err(ref a)): Result<&u8, u8> = Ok(&0); + | - ^ bound in different ways + | | + | first binding error[E0409]: variable `a` is bound inconsistently across alternatives separated by `|` - --> $DIR/inconsistent-modes.rs:9:29 + --> $DIR/inconsistent-modes.rs:8:30 | -LL | let Ok(ref mut a) | Err(a): Result<u8, &mut u8> = Ok(0); - | - ^ bound in different ways - | | - | first binding +LL | let (Ok(ref mut a) | Err(a)): Result<u8, &mut u8> = Ok(0); + | - ^ bound in different ways + | | + | first binding error[E0409]: variable `a` is bound inconsistently across alternatives separated by `|` - --> $DIR/inconsistent-modes.rs:11:33 + --> $DIR/inconsistent-modes.rs:10:34 | -LL | let Ok(ref a) | Err(ref mut a): Result<&u8, &mut u8> = Ok(&0); - | - first binding ^ bound in different ways +LL | let (Ok(ref a) | Err(ref mut a)): Result<&u8, &mut u8> = Ok(&0); + | - first binding ^ bound in different ways error[E0409]: variable `a` is bound inconsistently across alternatives separated by `|` - --> $DIR/inconsistent-modes.rs:14:39 + --> $DIR/inconsistent-modes.rs:13:40 | -LL | let Ok((ref a, b)) | Err((ref mut a, ref b)) = Ok((0, &0)); - | - first binding ^ bound in different ways +LL | let (Ok((ref a, b)) | Err((ref mut a, ref b))) = Ok((0, &0)); + | - first binding ^ bound in different ways error[E0409]: variable `b` is bound inconsistently across alternatives separated by `|` - --> $DIR/inconsistent-modes.rs:14:46 + --> $DIR/inconsistent-modes.rs:13:47 | -LL | let Ok((ref a, b)) | Err((ref mut a, ref b)) = Ok((0, &0)); - | - first binding ^ bound in different ways +LL | let (Ok((ref a, b)) | Err((ref mut a, ref b))) = Ok((0, &0)); + | - first binding ^ bound in different ways error[E0409]: variable `a` is bound inconsistently across alternatives separated by `|` - --> $DIR/inconsistent-modes.rs:20:38 + --> $DIR/inconsistent-modes.rs:19:39 | -LL | let Ok(Ok(a) | Err(a)) | Err(ref a) = Err(0); - | - ^ bound in different ways - | | - | first binding +LL | let (Ok(Ok(a) | Err(a)) | Err(ref a)) = Err(0); + | - ^ bound in different ways + | | + | first binding error[E0409]: variable `a` is bound inconsistently across alternatives separated by `|` - --> $DIR/inconsistent-modes.rs:24:33 + --> $DIR/inconsistent-modes.rs:23:34 | -LL | let Ok([Ok((Ok(ref a) | Err(a),)) | Err(a)]) | Err(a) = Err(&1); - | - ^ bound in different ways - | | - | first binding +LL | let (Ok([Ok((Ok(ref a) | Err(a),)) | Err(a)]) | Err(a)) = Err(&1); + | - ^ bound in different ways + | | + | first binding error[E0308]: mismatched types - --> $DIR/inconsistent-modes.rs:11:25 + --> $DIR/inconsistent-modes.rs:10:26 | -LL | let Ok(ref a) | Err(ref mut a): Result<&u8, &mut u8> = Ok(&0); - | ----- ^^^^^^^^^ -------------------- expected due to this - | | | - | | types differ in mutability - | first introduced with type `&&u8` here +LL | let (Ok(ref a) | Err(ref mut a)): Result<&u8, &mut u8> = Ok(&0); + | ----- ^^^^^^^^^ -------------------- expected due to this + | | | + | | types differ in mutability + | first introduced with type `&&u8` here | = note: expected type `&&u8` found type `&mut &mut u8` = note: a binding must have the same type in all alternatives error[E0308]: mismatched types - --> $DIR/inconsistent-modes.rs:14:31 + --> $DIR/inconsistent-modes.rs:13:32 | -LL | let Ok((ref a, b)) | Err((ref mut a, ref b)) = Ok((0, &0)); - | ----- ^^^^^^^^^ ----------- this expression has type `Result<({integer}, &{integer}), (_, _)>` - | | | - | | types differ in mutability - | first introduced with type `&{integer}` here +LL | let (Ok((ref a, b)) | Err((ref mut a, ref b))) = Ok((0, &0)); + | ----- ^^^^^^^^^ ----------- this expression has type `Result<({integer}, &{integer}), (_, _)>` + | | | + | | types differ in mutability + | first introduced with type `&{integer}` here | = note: expected type `&{integer}` found type `&mut _` diff --git a/src/test/ui/or-patterns/issue-67514-irrefutable-param.rs b/src/test/ui/or-patterns/issue-67514-irrefutable-param.rs index 0c2ae44e546d9..73931def89584 100644 --- a/src/test/ui/or-patterns/issue-67514-irrefutable-param.rs +++ b/src/test/ui/or-patterns/issue-67514-irrefutable-param.rs @@ -2,8 +2,6 @@ // check-pass -#![feature(or_patterns)] - fn foo((Some(_) | None): Option<u32>) {} fn main() { diff --git a/src/test/ui/or-patterns/issue-68785-irrefutable-param-with-at.rs b/src/test/ui/or-patterns/issue-68785-irrefutable-param-with-at.rs index 1a65a1e544b9e..7339a7e23f9e2 100644 --- a/src/test/ui/or-patterns/issue-68785-irrefutable-param-with-at.rs +++ b/src/test/ui/or-patterns/issue-68785-irrefutable-param-with-at.rs @@ -1,7 +1,5 @@ // check-pass -#![feature(or_patterns)] - enum MyEnum { FirstCase(u8), OtherCase(u16), diff --git a/src/test/ui/or-patterns/issue-69875-should-have-been-expanded-earlier-non-exhaustive.rs b/src/test/ui/or-patterns/issue-69875-should-have-been-expanded-earlier-non-exhaustive.rs index 59533cefea64c..3538aad5d772f 100644 --- a/src/test/ui/or-patterns/issue-69875-should-have-been-expanded-earlier-non-exhaustive.rs +++ b/src/test/ui/or-patterns/issue-69875-should-have-been-expanded-earlier-non-exhaustive.rs @@ -1,7 +1,5 @@ -#![feature(or_patterns)] - fn main() { - let 0 | (1 | 2) = 0; //~ ERROR refutable pattern in local binding + let (0 | (1 | 2)) = 0; //~ ERROR refutable pattern in local binding match 0 { //~^ ERROR non-exhaustive patterns 0 | (1 | 2) => {} diff --git a/src/test/ui/or-patterns/issue-69875-should-have-been-expanded-earlier-non-exhaustive.stderr b/src/test/ui/or-patterns/issue-69875-should-have-been-expanded-earlier-non-exhaustive.stderr index 2acf1f41c6fa6..61175b84ee1de 100644 --- a/src/test/ui/or-patterns/issue-69875-should-have-been-expanded-earlier-non-exhaustive.stderr +++ b/src/test/ui/or-patterns/issue-69875-should-have-been-expanded-earlier-non-exhaustive.stderr @@ -1,19 +1,19 @@ error[E0005]: refutable pattern in local binding: `i32::MIN..=-1_i32` and `3_i32..=i32::MAX` not covered - --> $DIR/issue-69875-should-have-been-expanded-earlier-non-exhaustive.rs:4:9 + --> $DIR/issue-69875-should-have-been-expanded-earlier-non-exhaustive.rs:2:10 | -LL | let 0 | (1 | 2) = 0; - | ^^^^^^^^^^^ patterns `i32::MIN..=-1_i32` and `3_i32..=i32::MAX` not covered +LL | let (0 | (1 | 2)) = 0; + | ^^^^^^^^^^^ patterns `i32::MIN..=-1_i32` and `3_i32..=i32::MAX` not covered | = note: `let` bindings require an "irrefutable pattern", like a `struct` or an `enum` with only one variant = note: for more information, visit https://doc.rust-lang.org/book/ch18-02-refutability.html = note: the matched value is of type `i32` help: you might want to use `if let` to ignore the variant that isn't matched | -LL | if let 0 | (1 | 2) = 0 { /* */ } - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | if let (0 | (1 | 2)) = 0 { /* */ } + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error[E0004]: non-exhaustive patterns: `i32::MIN..=-1_i32` and `3_i32..=i32::MAX` not covered - --> $DIR/issue-69875-should-have-been-expanded-earlier-non-exhaustive.rs:5:11 + --> $DIR/issue-69875-should-have-been-expanded-earlier-non-exhaustive.rs:3:11 | LL | match 0 { | ^ patterns `i32::MIN..=-1_i32` and `3_i32..=i32::MAX` not covered diff --git a/src/test/ui/or-patterns/issue-69875-should-have-been-expanded-earlier.rs b/src/test/ui/or-patterns/issue-69875-should-have-been-expanded-earlier.rs index 1de563dedbf18..408ac24f39a49 100644 --- a/src/test/ui/or-patterns/issue-69875-should-have-been-expanded-earlier.rs +++ b/src/test/ui/or-patterns/issue-69875-should-have-been-expanded-earlier.rs @@ -1,9 +1,7 @@ // check-pass -#![feature(or_patterns)] - fn main() { - let 0 | (1 | _) = 0; + let (0 | (1 | _)) = 0; if let 0 | (1 | 2) = 0 {} if let x @ 0 | x @ (1 | 2) = 0 {} } diff --git a/src/test/ui/or-patterns/issue-70413-no-unreachable-pat-and-guard.rs b/src/test/ui/or-patterns/issue-70413-no-unreachable-pat-and-guard.rs index eb6706e50006f..8a3c640b10dee 100644 --- a/src/test/ui/or-patterns/issue-70413-no-unreachable-pat-and-guard.rs +++ b/src/test/ui/or-patterns/issue-70413-no-unreachable-pat-and-guard.rs @@ -2,7 +2,6 @@ #![deny(unreachable_patterns)] -#![feature(or_patterns)] fn main() { match (3,42) { (a,_) | (_,a) if a > 10 => {println!("{}", a)} diff --git a/src/test/ui/or-patterns/let-pattern.rs b/src/test/ui/or-patterns/let-pattern.rs index 07e37412ce842..97207e83e2e65 100644 --- a/src/test/ui/or-patterns/let-pattern.rs +++ b/src/test/ui/or-patterns/let-pattern.rs @@ -1,9 +1,7 @@ -#![feature(or_patterns)] - // run-pass fn or_pat_let(x: Result<u32, u32>) -> u32 { - let Ok(y) | Err(y) = x; + let (Ok(y) | Err(y)) = x; y } diff --git a/src/test/ui/or-patterns/macro-pat.rs b/src/test/ui/or-patterns/macro-pat.rs index 8749407675b37..8c581b630dece 100644 --- a/src/test/ui/or-patterns/macro-pat.rs +++ b/src/test/ui/or-patterns/macro-pat.rs @@ -3,8 +3,6 @@ // ignore-test // FIXME(mark-i-m): enable this test again when 2021 machinery is available -#![feature(or_patterns)] - use Foo::*; #[derive(Eq, PartialEq, Debug)] diff --git a/src/test/ui/or-patterns/mismatched-bindings-async-fn.rs b/src/test/ui/or-patterns/mismatched-bindings-async-fn.rs index 5c5c68f81d1f2..d1cb73aafa0c0 100644 --- a/src/test/ui/or-patterns/mismatched-bindings-async-fn.rs +++ b/src/test/ui/or-patterns/mismatched-bindings-async-fn.rs @@ -1,14 +1,12 @@ // Regression test for #71297 // edition:2018 -#![feature(or_patterns)] - async fn a((x | s): String) {} //~^ ERROR variable `x` is not bound in all patterns //~| ERROR variable `s` is not bound in all patterns async fn b() { - let x | s = String::new(); + let (x | s) = String::new(); //~^ ERROR variable `x` is not bound in all patterns //~| ERROR variable `s` is not bound in all patterns } diff --git a/src/test/ui/or-patterns/mismatched-bindings-async-fn.stderr b/src/test/ui/or-patterns/mismatched-bindings-async-fn.stderr index 998577cf4b5e0..81602fffa8d07 100644 --- a/src/test/ui/or-patterns/mismatched-bindings-async-fn.stderr +++ b/src/test/ui/or-patterns/mismatched-bindings-async-fn.stderr @@ -1,5 +1,5 @@ error[E0408]: variable `s` is not bound in all patterns - --> $DIR/mismatched-bindings-async-fn.rs:6:13 + --> $DIR/mismatched-bindings-async-fn.rs:4:13 | LL | async fn a((x | s): String) {} | ^ - variable not in all patterns @@ -7,7 +7,7 @@ LL | async fn a((x | s): String) {} | pattern doesn't bind `s` error[E0408]: variable `x` is not bound in all patterns - --> $DIR/mismatched-bindings-async-fn.rs:6:17 + --> $DIR/mismatched-bindings-async-fn.rs:4:17 | LL | async fn a((x | s): String) {} | - ^ pattern doesn't bind `x` @@ -15,20 +15,20 @@ LL | async fn a((x | s): String) {} | variable not in all patterns error[E0408]: variable `s` is not bound in all patterns - --> $DIR/mismatched-bindings-async-fn.rs:11:9 + --> $DIR/mismatched-bindings-async-fn.rs:9:10 | -LL | let x | s = String::new(); - | ^ - variable not in all patterns - | | - | pattern doesn't bind `s` +LL | let (x | s) = String::new(); + | ^ - variable not in all patterns + | | + | pattern doesn't bind `s` error[E0408]: variable `x` is not bound in all patterns - --> $DIR/mismatched-bindings-async-fn.rs:11:13 + --> $DIR/mismatched-bindings-async-fn.rs:9:14 | -LL | let x | s = String::new(); - | - ^ pattern doesn't bind `x` - | | - | variable not in all patterns +LL | let (x | s) = String::new(); + | - ^ pattern doesn't bind `x` + | | + | variable not in all patterns error: aborting due to 4 previous errors diff --git a/src/test/ui/or-patterns/missing-bindings.rs b/src/test/ui/or-patterns/missing-bindings.rs index 67cf52fa8c418..7c26012c0e93d 100644 --- a/src/test/ui/or-patterns/missing-bindings.rs +++ b/src/test/ui/or-patterns/missing-bindings.rs @@ -2,7 +2,6 @@ // edition:2018 -#![feature(or_patterns)] #![allow(non_camel_case_types)] fn main() {} @@ -17,7 +16,7 @@ fn check_handling_of_paths() { } use bar::foo::{alpha, charlie}; - let alpha | beta | charlie = alpha; //~ ERROR variable `beta` is not bound in all patterns + let (alpha | beta | charlie) = alpha; //~ ERROR variable `beta` is not bound in all patterns match Some(alpha) { Some(alpha | beta) => {} //~ ERROR variable `beta` is not bound in all patterns } @@ -31,19 +30,19 @@ fn check_misc_nesting() { // One level: const X: E<u8> = B(0); - let A(a, _) | _ = X; //~ ERROR variable `a` is not bound in all patterns - let _ | B(a) = X; //~ ERROR variable `a` is not bound in all patterns - let A(..) | B(a) = X; //~ ERROR variable `a` is not bound in all patterns - let A(a, _) | B(_) = X; //~ ERROR variable `a` is not bound in all patterns - let A(_, a) | B(_) = X; //~ ERROR variable `a` is not bound in all patterns - let A(a, b) | B(a) = X; //~ ERROR variable `b` is not bound in all patterns + let (A(a, _) | _) = X; //~ ERROR variable `a` is not bound in all patterns + let (_ | B(a)) = X; //~ ERROR variable `a` is not bound in all patterns + let (A(..) | B(a)) = X; //~ ERROR variable `a` is not bound in all patterns + let (A(a, _) | B(_)) = X; //~ ERROR variable `a` is not bound in all patterns + let (A(_, a) | B(_)) = X; //~ ERROR variable `a` is not bound in all patterns + let (A(a, b) | B(a)) = X; //~ ERROR variable `b` is not bound in all patterns // Two levels: const Y: E<E<u8>> = B(B(0)); - let A(A(..) | B(_), _) | B(a) = Y; //~ ERROR variable `a` is not bound in all patterns - let A(A(..) | B(a), _) | B(A(a, _) | B(a)) = Y; + let (A(A(..) | B(_), _) | B(a)) = Y; //~ ERROR variable `a` is not bound in all patterns + let (A(A(..) | B(a), _) | B(A(a, _) | B(a))) = Y; //~^ ERROR variable `a` is not bound in all patterns - let A(A(a, b) | B(c), d) | B(e) = Y; + let (A(A(a, b) | B(c), d) | B(e)) = Y; //~^ ERROR variable `a` is not bound in all patterns //~| ERROR variable `a` is not bound in all patterns //~| ERROR variable `b` is not bound in all patterns diff --git a/src/test/ui/or-patterns/missing-bindings.stderr b/src/test/ui/or-patterns/missing-bindings.stderr index 57270e4412351..c173a3a9aba20 100644 --- a/src/test/ui/or-patterns/missing-bindings.stderr +++ b/src/test/ui/or-patterns/missing-bindings.stderr @@ -1,14 +1,14 @@ error[E0408]: variable `beta` is not bound in all patterns - --> $DIR/missing-bindings.rs:20:9 + --> $DIR/missing-bindings.rs:19:10 | -LL | let alpha | beta | charlie = alpha; - | ^^^^^ ---- ^^^^^^^ pattern doesn't bind `beta` - | | | - | | variable not in all patterns - | pattern doesn't bind `beta` +LL | let (alpha | beta | charlie) = alpha; + | ^^^^^ ---- ^^^^^^^ pattern doesn't bind `beta` + | | | + | | variable not in all patterns + | pattern doesn't bind `beta` error[E0408]: variable `beta` is not bound in all patterns - --> $DIR/missing-bindings.rs:22:14 + --> $DIR/missing-bindings.rs:21:14 | LL | Some(alpha | beta) => {} | ^^^^^ ---- variable not in all patterns @@ -16,135 +16,135 @@ LL | Some(alpha | beta) => {} | pattern doesn't bind `beta` error[E0408]: variable `a` is not bound in all patterns - --> $DIR/missing-bindings.rs:34:19 + --> $DIR/missing-bindings.rs:33:20 | -LL | let A(a, _) | _ = X; - | - ^ pattern doesn't bind `a` - | | - | variable not in all patterns +LL | let (A(a, _) | _) = X; + | - ^ pattern doesn't bind `a` + | | + | variable not in all patterns error[E0408]: variable `a` is not bound in all patterns - --> $DIR/missing-bindings.rs:35:9 + --> $DIR/missing-bindings.rs:34:10 | -LL | let _ | B(a) = X; - | ^ - variable not in all patterns - | | - | pattern doesn't bind `a` +LL | let (_ | B(a)) = X; + | ^ - variable not in all patterns + | | + | pattern doesn't bind `a` error[E0408]: variable `a` is not bound in all patterns - --> $DIR/missing-bindings.rs:36:9 + --> $DIR/missing-bindings.rs:35:10 | -LL | let A(..) | B(a) = X; - | ^^^^^ - variable not in all patterns - | | - | pattern doesn't bind `a` +LL | let (A(..) | B(a)) = X; + | ^^^^^ - variable not in all patterns + | | + | pattern doesn't bind `a` error[E0408]: variable `a` is not bound in all patterns - --> $DIR/missing-bindings.rs:37:19 + --> $DIR/missing-bindings.rs:36:20 | -LL | let A(a, _) | B(_) = X; - | - ^^^^ pattern doesn't bind `a` - | | - | variable not in all patterns +LL | let (A(a, _) | B(_)) = X; + | - ^^^^ pattern doesn't bind `a` + | | + | variable not in all patterns error[E0408]: variable `a` is not bound in all patterns - --> $DIR/missing-bindings.rs:38:19 + --> $DIR/missing-bindings.rs:37:20 | -LL | let A(_, a) | B(_) = X; - | - ^^^^ pattern doesn't bind `a` - | | - | variable not in all patterns +LL | let (A(_, a) | B(_)) = X; + | - ^^^^ pattern doesn't bind `a` + | | + | variable not in all patterns error[E0408]: variable `b` is not bound in all patterns - --> $DIR/missing-bindings.rs:39:19 + --> $DIR/missing-bindings.rs:38:20 | -LL | let A(a, b) | B(a) = X; - | - ^^^^ pattern doesn't bind `b` - | | - | variable not in all patterns +LL | let (A(a, b) | B(a)) = X; + | - ^^^^ pattern doesn't bind `b` + | | + | variable not in all patterns error[E0408]: variable `a` is not bound in all patterns - --> $DIR/missing-bindings.rs:43:9 + --> $DIR/missing-bindings.rs:42:10 | -LL | let A(A(..) | B(_), _) | B(a) = Y; - | ^^^^^^^^^^^^^^^^^^ - variable not in all patterns - | | - | pattern doesn't bind `a` +LL | let (A(A(..) | B(_), _) | B(a)) = Y; + | ^^^^^^^^^^^^^^^^^^ - variable not in all patterns + | | + | pattern doesn't bind `a` error[E0408]: variable `a` is not bound in all patterns - --> $DIR/missing-bindings.rs:44:11 + --> $DIR/missing-bindings.rs:43:12 | -LL | let A(A(..) | B(a), _) | B(A(a, _) | B(a)) = Y; - | ^^^^^ - variable not in all patterns - | | - | pattern doesn't bind `a` +LL | let (A(A(..) | B(a), _) | B(A(a, _) | B(a))) = Y; + | ^^^^^ - variable not in all patterns + | | + | pattern doesn't bind `a` error[E0408]: variable `a` is not bound in all patterns - --> $DIR/missing-bindings.rs:46:21 + --> $DIR/missing-bindings.rs:45:22 | -LL | let A(A(a, b) | B(c), d) | B(e) = Y; - | - ^^^^ pattern doesn't bind `a` - | | - | variable not in all patterns +LL | let (A(A(a, b) | B(c), d) | B(e)) = Y; + | - ^^^^ pattern doesn't bind `a` + | | + | variable not in all patterns error[E0408]: variable `b` is not bound in all patterns - --> $DIR/missing-bindings.rs:46:21 + --> $DIR/missing-bindings.rs:45:22 | -LL | let A(A(a, b) | B(c), d) | B(e) = Y; - | - ^^^^ pattern doesn't bind `b` - | | - | variable not in all patterns +LL | let (A(A(a, b) | B(c), d) | B(e)) = Y; + | - ^^^^ pattern doesn't bind `b` + | | + | variable not in all patterns error[E0408]: variable `c` is not bound in all patterns - --> $DIR/missing-bindings.rs:46:11 + --> $DIR/missing-bindings.rs:45:12 | -LL | let A(A(a, b) | B(c), d) | B(e) = Y; - | ^^^^^^^ - variable not in all patterns - | | - | pattern doesn't bind `c` +LL | let (A(A(a, b) | B(c), d) | B(e)) = Y; + | ^^^^^^^ - variable not in all patterns + | | + | pattern doesn't bind `c` error[E0408]: variable `a` is not bound in all patterns - --> $DIR/missing-bindings.rs:46:32 + --> $DIR/missing-bindings.rs:45:33 | -LL | let A(A(a, b) | B(c), d) | B(e) = Y; - | - ^^^^ pattern doesn't bind `a` - | | - | variable not in all patterns +LL | let (A(A(a, b) | B(c), d) | B(e)) = Y; + | - ^^^^ pattern doesn't bind `a` + | | + | variable not in all patterns error[E0408]: variable `b` is not bound in all patterns - --> $DIR/missing-bindings.rs:46:32 + --> $DIR/missing-bindings.rs:45:33 | -LL | let A(A(a, b) | B(c), d) | B(e) = Y; - | - ^^^^ pattern doesn't bind `b` - | | - | variable not in all patterns +LL | let (A(A(a, b) | B(c), d) | B(e)) = Y; + | - ^^^^ pattern doesn't bind `b` + | | + | variable not in all patterns error[E0408]: variable `c` is not bound in all patterns - --> $DIR/missing-bindings.rs:46:32 + --> $DIR/missing-bindings.rs:45:33 | -LL | let A(A(a, b) | B(c), d) | B(e) = Y; - | - ^^^^ pattern doesn't bind `c` - | | - | variable not in all patterns +LL | let (A(A(a, b) | B(c), d) | B(e)) = Y; + | - ^^^^ pattern doesn't bind `c` + | | + | variable not in all patterns error[E0408]: variable `d` is not bound in all patterns - --> $DIR/missing-bindings.rs:46:32 + --> $DIR/missing-bindings.rs:45:33 | -LL | let A(A(a, b) | B(c), d) | B(e) = Y; - | - ^^^^ pattern doesn't bind `d` - | | - | variable not in all patterns +LL | let (A(A(a, b) | B(c), d) | B(e)) = Y; + | - ^^^^ pattern doesn't bind `d` + | | + | variable not in all patterns error[E0408]: variable `e` is not bound in all patterns - --> $DIR/missing-bindings.rs:46:9 + --> $DIR/missing-bindings.rs:45:10 | -LL | let A(A(a, b) | B(c), d) | B(e) = Y; - | ^^^^^^^^^^^^^^^^^^^^ - variable not in all patterns - | | - | pattern doesn't bind `e` +LL | let (A(A(a, b) | B(c), d) | B(e)) = Y; + | ^^^^^^^^^^^^^^^^^^^^ - variable not in all patterns + | | + | pattern doesn't bind `e` error[E0408]: variable `a` is not bound in all patterns - --> $DIR/missing-bindings.rs:62:29 + --> $DIR/missing-bindings.rs:61:29 | LL | Ok(a) | Err(_), | - ^^^^^^ pattern doesn't bind `a` @@ -152,7 +152,7 @@ LL | Ok(a) | Err(_), | variable not in all patterns error[E0408]: variable `a` is not bound in all patterns - --> $DIR/missing-bindings.rs:70:21 + --> $DIR/missing-bindings.rs:69:21 | LL | A(_, a) | | - variable not in all patterns @@ -160,7 +160,7 @@ LL | B(b), | ^^^^ pattern doesn't bind `a` error[E0408]: variable `b` is not bound in all patterns - --> $DIR/missing-bindings.rs:69:21 + --> $DIR/missing-bindings.rs:68:21 | LL | A(_, a) | | ^^^^^^^ pattern doesn't bind `b` @@ -168,7 +168,7 @@ LL | B(b), | - variable not in all patterns error[E0408]: variable `a` is not bound in all patterns - --> $DIR/missing-bindings.rs:73:17 + --> $DIR/missing-bindings.rs:72:17 | LL | A(_, a) | | - variable not in all patterns @@ -177,7 +177,7 @@ LL | B(_) | ^^^^ pattern doesn't bind `a` error[E0408]: variable `b` is not bound in all patterns - --> $DIR/missing-bindings.rs:73:17 + --> $DIR/missing-bindings.rs:72:17 | LL | B(b), | - variable not in all patterns @@ -186,7 +186,7 @@ LL | B(_) | ^^^^ pattern doesn't bind `b` error[E0408]: variable `a` is not bound in all patterns - --> $DIR/missing-bindings.rs:77:13 + --> $DIR/missing-bindings.rs:76:13 | LL | B(Ok(a) | Err(a)) | - variable not in all patterns @@ -198,7 +198,7 @@ LL | V3(c), | ^^^^^ pattern doesn't bind `a` error[E0408]: variable `b` is not bound in all patterns - --> $DIR/missing-bindings.rs:58:13 + --> $DIR/missing-bindings.rs:57:13 | LL | / V1( LL | | @@ -216,7 +216,7 @@ LL | V3(c), | ^^^^^ pattern doesn't bind `b` error[E0408]: variable `c` is not bound in all patterns - --> $DIR/missing-bindings.rs:58:13 + --> $DIR/missing-bindings.rs:57:13 | LL | / V1( LL | | diff --git a/src/test/ui/or-patterns/mix-with-wild.rs b/src/test/ui/or-patterns/mix-with-wild.rs index 37f20df1b312d..d9911cda1b685 100644 --- a/src/test/ui/or-patterns/mix-with-wild.rs +++ b/src/test/ui/or-patterns/mix-with-wild.rs @@ -4,7 +4,6 @@ // 2) or-patterns should work with simplifyable patterns. // run-pass -#![feature(or_patterns)] pub fn test(x: Option<usize>) -> bool { match x { diff --git a/src/test/ui/or-patterns/multiple-pattern-typo.rs b/src/test/ui/or-patterns/multiple-pattern-typo.rs index 4d06101044f6c..5f201253304c4 100644 --- a/src/test/ui/or-patterns/multiple-pattern-typo.rs +++ b/src/test/ui/or-patterns/multiple-pattern-typo.rs @@ -1,4 +1,4 @@ -#![feature(or_patterns)] +//! Test for `||` in or-patterns fn main() { let x = 3; diff --git a/src/test/ui/or-patterns/nested-undelimited-precedence.rs b/src/test/ui/or-patterns/nested-undelimited-precedence.rs new file mode 100644 index 0000000000000..208662b1c425d --- /dev/null +++ b/src/test/ui/or-patterns/nested-undelimited-precedence.rs @@ -0,0 +1,46 @@ +// This test tests the precedence of `|` (or-patterns) undelimited nested patterns. In particular, +// we want to reserve the syntactic space of a pattern followed by a type annotation for possible +// future type ascription, so we need to make sure that any time a pattern is followed by type +// annotation (for now), the pattern is not a top-level or-pattern. However, there are also a few +// types of patterns that allow undelimited subpatterns that could cause the same ambiguity. +// Currently, those should be impossible due to precedence rule. This test enforces that. + +#![feature(or_patterns)] + +enum E { + A, + B, +} + +fn foo() { + use E::*; + + // ok + let b @ (A | B): E = A; + + let b @ A | B: E = A; //~ERROR `b` is not bound in all patterns + //~^ ERROR top-level or-patterns are not allowed +} + +enum F { + A(usize), + B(usize), +} + +fn bar() { + use F::*; + + // ok + let (A(x) | B(x)): F = A(3); + + let &A(_) | B(_): F = A(3); //~ERROR mismatched types + //~^ ERROR top-level or-patterns are not allowed + let &&A(_) | B(_): F = A(3); //~ERROR mismatched types + //~^ ERROR top-level or-patterns are not allowed + let &mut A(_) | B(_): F = A(3); //~ERROR mismatched types + //~^ ERROR top-level or-patterns are not allowed + let &&mut A(_) | B(_): F = A(3); //~ERROR mismatched types + //~^ ERROR top-level or-patterns are not allowed +} + +fn main() {} diff --git a/src/test/ui/or-patterns/nested-undelimited-precedence.stderr b/src/test/ui/or-patterns/nested-undelimited-precedence.stderr new file mode 100644 index 0000000000000..1d78d5193cb88 --- /dev/null +++ b/src/test/ui/or-patterns/nested-undelimited-precedence.stderr @@ -0,0 +1,86 @@ +error: top-level or-patterns are not allowed in `let` bindings + --> $DIR/nested-undelimited-precedence.rs:21:9 + | +LL | let b @ A | B: E = A; + | ^^^^^^^^^ help: wrap the pattern in parentheses: `(b @ A | B)` + +error: top-level or-patterns are not allowed in `let` bindings + --> $DIR/nested-undelimited-precedence.rs:36:9 + | +LL | let &A(_) | B(_): F = A(3); + | ^^^^^^^^^^^^ help: wrap the pattern in parentheses: `(&A(_) | B(_))` + +error: top-level or-patterns are not allowed in `let` bindings + --> $DIR/nested-undelimited-precedence.rs:38:9 + | +LL | let &&A(_) | B(_): F = A(3); + | ^^^^^^^^^^^^^ help: wrap the pattern in parentheses: `(&&A(_) | B(_))` + +error: top-level or-patterns are not allowed in `let` bindings + --> $DIR/nested-undelimited-precedence.rs:40:9 + | +LL | let &mut A(_) | B(_): F = A(3); + | ^^^^^^^^^^^^^^^^ help: wrap the pattern in parentheses: `(&mut A(_) | B(_))` + +error: top-level or-patterns are not allowed in `let` bindings + --> $DIR/nested-undelimited-precedence.rs:42:9 + | +LL | let &&mut A(_) | B(_): F = A(3); + | ^^^^^^^^^^^^^^^^^ help: wrap the pattern in parentheses: `(&&mut A(_) | B(_))` + +error[E0408]: variable `b` is not bound in all patterns + --> $DIR/nested-undelimited-precedence.rs:21:17 + | +LL | let b @ A | B: E = A; + | - ^ pattern doesn't bind `b` + | | + | variable not in all patterns + +error[E0308]: mismatched types + --> $DIR/nested-undelimited-precedence.rs:36:9 + | +LL | let &A(_) | B(_): F = A(3); + | ^^^^^ - expected due to this + | | + | expected enum `F`, found reference + | + = note: expected enum `F` + found reference `&_` + +error[E0308]: mismatched types + --> $DIR/nested-undelimited-precedence.rs:38:9 + | +LL | let &&A(_) | B(_): F = A(3); + | ^^^^^^ - expected due to this + | | + | expected enum `F`, found reference + | + = note: expected enum `F` + found reference `&_` + +error[E0308]: mismatched types + --> $DIR/nested-undelimited-precedence.rs:40:9 + | +LL | let &mut A(_) | B(_): F = A(3); + | ^^^^^^^^^ - expected due to this + | | + | expected enum `F`, found `&mut _` + | + = note: expected enum `F` + found mutable reference `&mut _` + +error[E0308]: mismatched types + --> $DIR/nested-undelimited-precedence.rs:42:9 + | +LL | let &&mut A(_) | B(_): F = A(3); + | ^^^^^^^^^^ - expected due to this + | | + | expected enum `F`, found reference + | + = note: expected enum `F` + found reference `&_` + +error: aborting due to 10 previous errors + +Some errors have detailed explanations: E0308, E0408. +For more information about an error, try `rustc --explain E0308`. diff --git a/src/test/ui/or-patterns/or-patterns-binding-type-mismatch.rs b/src/test/ui/or-patterns/or-patterns-binding-type-mismatch.rs index 5ec7dc6962c18..fa470de7fb555 100644 --- a/src/test/ui/or-patterns/or-patterns-binding-type-mismatch.rs +++ b/src/test/ui/or-patterns/or-patterns-binding-type-mismatch.rs @@ -1,8 +1,6 @@ // Here we test type checking of bindings when combined with or-patterns. // Specifically, we ensure that introducing bindings of different types result in type errors. -#![feature(or_patterns)] - fn main() { enum Blah { A(isize, isize, usize), @@ -52,10 +50,10 @@ fn main() { = Some((0u8, Some((1u16, 2u32)))) {} - let Blah::A(_, x, y) | Blah::B(x, y) = Blah::A(1, 1, 2); + let (Blah::A(_, x, y) | Blah::B(x, y)) = Blah::A(1, 1, 2); //~^ ERROR mismatched types - let (x, y) | (y, x) = (0u8, 1u16); + let ((x, y) | (y, x)) = (0u8, 1u16); //~^ ERROR mismatched types //~| ERROR mismatched types diff --git a/src/test/ui/or-patterns/or-patterns-binding-type-mismatch.stderr b/src/test/ui/or-patterns/or-patterns-binding-type-mismatch.stderr index 00dba053a59d3..00ce46c5690d5 100644 --- a/src/test/ui/or-patterns/or-patterns-binding-type-mismatch.stderr +++ b/src/test/ui/or-patterns/or-patterns-binding-type-mismatch.stderr @@ -1,5 +1,5 @@ error[E0308]: mismatched types - --> $DIR/or-patterns-binding-type-mismatch.rs:13:39 + --> $DIR/or-patterns-binding-type-mismatch.rs:11:39 | LL | match Blah::A(1, 1, 2) { | ---------------- this expression has type `Blah` @@ -11,7 +11,7 @@ LL | Blah::A(_, x, y) | Blah::B(x, y) => {} = note: in the same arm, a binding must have the same type in all alternatives error[E0308]: mismatched types - --> $DIR/or-patterns-binding-type-mismatch.rs:17:44 + --> $DIR/or-patterns-binding-type-mismatch.rs:15:44 | LL | match Some(Blah::A(1, 1, 2)) { | ---------------------- this expression has type `Option<Blah>` @@ -23,7 +23,7 @@ LL | Some(Blah::A(_, x, y) | Blah::B(x, y)) => {} = note: in the same arm, a binding must have the same type in all alternatives error[E0308]: mismatched types - --> $DIR/or-patterns-binding-type-mismatch.rs:21:19 + --> $DIR/or-patterns-binding-type-mismatch.rs:19:19 | LL | match (0u8, 1u16) { | ----------- this expression has type `(u8, u16)` @@ -35,7 +35,7 @@ LL | (x, y) | (y, x) => {} = note: in the same arm, a binding must have the same type in all alternatives error[E0308]: mismatched types - --> $DIR/or-patterns-binding-type-mismatch.rs:21:22 + --> $DIR/or-patterns-binding-type-mismatch.rs:19:22 | LL | match (0u8, 1u16) { | ----------- this expression has type `(u8, u16)` @@ -47,7 +47,7 @@ LL | (x, y) | (y, x) => {} = note: in the same arm, a binding must have the same type in all alternatives error[E0308]: mismatched types - --> $DIR/or-patterns-binding-type-mismatch.rs:26:41 + --> $DIR/or-patterns-binding-type-mismatch.rs:24:41 | LL | match Some((0u8, Some((1u16, 2u32)))) { | ------------------------------- this expression has type `Option<(u8, Option<(u16, u32)>)>` @@ -59,7 +59,7 @@ LL | Some((x, Some((y, z)))) | Some((y, Some((x, z) | (z, x)))) => {} = note: in the same arm, a binding must have the same type in all alternatives error[E0308]: mismatched types - --> $DIR/or-patterns-binding-type-mismatch.rs:26:50 + --> $DIR/or-patterns-binding-type-mismatch.rs:24:50 | LL | match Some((0u8, Some((1u16, 2u32)))) { | ------------------------------- this expression has type `Option<(u8, Option<(u16, u32)>)>` @@ -71,7 +71,7 @@ LL | Some((x, Some((y, z)))) | Some((y, Some((x, z) | (z, x)))) => {} = note: in the same arm, a binding must have the same type in all alternatives error[E0308]: mismatched types - --> $DIR/or-patterns-binding-type-mismatch.rs:26:59 + --> $DIR/or-patterns-binding-type-mismatch.rs:24:59 | LL | match Some((0u8, Some((1u16, 2u32)))) { | ------------------------------- this expression has type `Option<(u8, Option<(u16, u32)>)>` @@ -83,7 +83,7 @@ LL | Some((x, Some((y, z)))) | Some((y, Some((x, z) | (z, x)))) => {} = note: in the same arm, a binding must have the same type in all alternatives error[E0308]: mismatched types - --> $DIR/or-patterns-binding-type-mismatch.rs:26:62 + --> $DIR/or-patterns-binding-type-mismatch.rs:24:62 | LL | match Some((0u8, Some((1u16, 2u32)))) { | ------------------------------- this expression has type `Option<(u8, Option<(u16, u32)>)>` @@ -93,7 +93,7 @@ LL | Some((x, Some((y, z)))) | Some((y, Some((x, z) | (z, x)))) => {} = note: in the same arm, a binding must have the same type in all alternatives error[E0308]: mismatched types - --> $DIR/or-patterns-binding-type-mismatch.rs:34:42 + --> $DIR/or-patterns-binding-type-mismatch.rs:32:42 | LL | if let Blah::A(_, x, y) | Blah::B(x, y) = Blah::A(1, 1, 2) { | - ^ ---------------- this expression has type `Blah` @@ -104,7 +104,7 @@ LL | if let Blah::A(_, x, y) | Blah::B(x, y) = Blah::A(1, 1, 2) { = note: a binding must have the same type in all alternatives error[E0308]: mismatched types - --> $DIR/or-patterns-binding-type-mismatch.rs:38:47 + --> $DIR/or-patterns-binding-type-mismatch.rs:36:47 | LL | if let Some(Blah::A(_, x, y) | Blah::B(x, y)) = Some(Blah::A(1, 1, 2)) { | - ^ ---------------------- this expression has type `Option<Blah>` @@ -115,7 +115,7 @@ LL | if let Some(Blah::A(_, x, y) | Blah::B(x, y)) = Some(Blah::A(1, 1, 2)) = note: a binding must have the same type in all alternatives error[E0308]: mismatched types - --> $DIR/or-patterns-binding-type-mismatch.rs:42:22 + --> $DIR/or-patterns-binding-type-mismatch.rs:40:22 | LL | if let (x, y) | (y, x) = (0u8, 1u16) { | - ^ ----------- this expression has type `(u8, u16)` @@ -126,7 +126,7 @@ LL | if let (x, y) | (y, x) = (0u8, 1u16) { = note: a binding must have the same type in all alternatives error[E0308]: mismatched types - --> $DIR/or-patterns-binding-type-mismatch.rs:42:25 + --> $DIR/or-patterns-binding-type-mismatch.rs:40:25 | LL | if let (x, y) | (y, x) = (0u8, 1u16) { | - ^ ----------- this expression has type `(u8, u16)` @@ -137,7 +137,7 @@ LL | if let (x, y) | (y, x) = (0u8, 1u16) { = note: a binding must have the same type in all alternatives error[E0308]: mismatched types - --> $DIR/or-patterns-binding-type-mismatch.rs:47:44 + --> $DIR/or-patterns-binding-type-mismatch.rs:45:44 | LL | if let Some((x, Some((y, z)))) | Some((y, Some((x, z) | (z, x)))) | - ^ expected `u16`, found `u8` @@ -150,7 +150,7 @@ LL | = Some((0u8, Some((1u16, 2u32)))) = note: a binding must have the same type in all alternatives error[E0308]: mismatched types - --> $DIR/or-patterns-binding-type-mismatch.rs:47:53 + --> $DIR/or-patterns-binding-type-mismatch.rs:45:53 | LL | if let Some((x, Some((y, z)))) | Some((y, Some((x, z) | (z, x)))) | - ^ expected `u8`, found `u16` @@ -163,7 +163,7 @@ LL | = Some((0u8, Some((1u16, 2u32)))) = note: a binding must have the same type in all alternatives error[E0308]: mismatched types - --> $DIR/or-patterns-binding-type-mismatch.rs:47:62 + --> $DIR/or-patterns-binding-type-mismatch.rs:45:62 | LL | if let Some((x, Some((y, z)))) | Some((y, Some((x, z) | (z, x)))) | - ^ expected `u32`, found `u16` @@ -176,7 +176,7 @@ LL | = Some((0u8, Some((1u16, 2u32)))) = note: a binding must have the same type in all alternatives error[E0308]: mismatched types - --> $DIR/or-patterns-binding-type-mismatch.rs:47:65 + --> $DIR/or-patterns-binding-type-mismatch.rs:45:65 | LL | if let Some((x, Some((y, z)))) | Some((y, Some((x, z) | (z, x)))) | - first introduced with type `u8` here ^ expected `u8`, found `u32` @@ -187,40 +187,40 @@ LL | = Some((0u8, Some((1u16, 2u32)))) = note: a binding must have the same type in all alternatives error[E0308]: mismatched types - --> $DIR/or-patterns-binding-type-mismatch.rs:55:39 + --> $DIR/or-patterns-binding-type-mismatch.rs:53:40 | -LL | let Blah::A(_, x, y) | Blah::B(x, y) = Blah::A(1, 1, 2); - | - ^ ---------------- this expression has type `Blah` - | | | - | | expected `usize`, found `isize` - | first introduced with type `usize` here +LL | let (Blah::A(_, x, y) | Blah::B(x, y)) = Blah::A(1, 1, 2); + | - ^ ---------------- this expression has type `Blah` + | | | + | | expected `usize`, found `isize` + | first introduced with type `usize` here | = note: a binding must have the same type in all alternatives error[E0308]: mismatched types - --> $DIR/or-patterns-binding-type-mismatch.rs:58:19 + --> $DIR/or-patterns-binding-type-mismatch.rs:56:20 | -LL | let (x, y) | (y, x) = (0u8, 1u16); - | - ^ ----------- this expression has type `(u8, u16)` - | | | - | | expected `u16`, found `u8` - | first introduced with type `u16` here +LL | let ((x, y) | (y, x)) = (0u8, 1u16); + | - ^ ----------- this expression has type `(u8, u16)` + | | | + | | expected `u16`, found `u8` + | first introduced with type `u16` here | = note: a binding must have the same type in all alternatives error[E0308]: mismatched types - --> $DIR/or-patterns-binding-type-mismatch.rs:58:22 + --> $DIR/or-patterns-binding-type-mismatch.rs:56:23 | -LL | let (x, y) | (y, x) = (0u8, 1u16); - | - ^ ----------- this expression has type `(u8, u16)` - | | | - | | expected `u8`, found `u16` - | first introduced with type `u8` here +LL | let ((x, y) | (y, x)) = (0u8, 1u16); + | - ^ ----------- this expression has type `(u8, u16)` + | | | + | | expected `u8`, found `u16` + | first introduced with type `u8` here | = note: a binding must have the same type in all alternatives error[E0308]: mismatched types - --> $DIR/or-patterns-binding-type-mismatch.rs:62:42 + --> $DIR/or-patterns-binding-type-mismatch.rs:60:42 | LL | fn f1((Blah::A(_, x, y) | Blah::B(x, y)): Blah) {} | - ^ ---- expected due to this @@ -231,7 +231,7 @@ LL | fn f1((Blah::A(_, x, y) | Blah::B(x, y)): Blah) {} = note: a binding must have the same type in all alternatives error[E0308]: mismatched types - --> $DIR/or-patterns-binding-type-mismatch.rs:65:22 + --> $DIR/or-patterns-binding-type-mismatch.rs:63:22 | LL | fn f2(((x, y) | (y, x)): (u8, u16)) {} | - ^ --------- expected due to this @@ -242,7 +242,7 @@ LL | fn f2(((x, y) | (y, x)): (u8, u16)) {} = note: a binding must have the same type in all alternatives error[E0308]: mismatched types - --> $DIR/or-patterns-binding-type-mismatch.rs:65:25 + --> $DIR/or-patterns-binding-type-mismatch.rs:63:25 | LL | fn f2(((x, y) | (y, x)): (u8, u16)) {} | - ^ --------- expected due to this diff --git a/src/test/ui/or-patterns/or-patterns-default-binding-modes.rs b/src/test/ui/or-patterns/or-patterns-default-binding-modes.rs index 3b6047c7be47d..e56f9ffe23c63 100644 --- a/src/test/ui/or-patterns/or-patterns-default-binding-modes.rs +++ b/src/test/ui/or-patterns/or-patterns-default-binding-modes.rs @@ -2,7 +2,6 @@ // check-pass -#![feature(or_patterns)] #![allow(irrefutable_let_patterns)] fn main() { @@ -37,11 +36,11 @@ fn main() { if let &(Ok(x) | Err(x)) = res { drop::<u8>(x); } - let Ok(mut x) | &Err(mut x) = res; + let (Ok(mut x) | &Err(mut x)) = res; drop::<u8>(x); let &(Ok(x) | Err(x)) = res; drop::<u8>(x); - let Ok(x) | Err(x) = res; + let (Ok(x) | Err(x)) = res; drop::<&u8>(x); for Ok(mut x) | &Err(mut x) in std::iter::once(res) { drop::<u8>(x); @@ -119,9 +118,9 @@ fn main() { } let tri = &Tri::A(&Ok(0)); - let Tri::A(Ok(mut x) | Err(mut x)) + let (Tri::A(Ok(mut x) | Err(mut x)) | Tri::B(&Ok(mut x) | Err(mut x)) - | &Tri::C(Ok(mut x) | Err(mut x)) = tri; + | &Tri::C(Ok(mut x) | Err(mut x))) = tri; drop::<u8>(x); match tri { diff --git a/src/test/ui/or-patterns/or-patterns-syntactic-fail.rs b/src/test/ui/or-patterns/or-patterns-syntactic-fail.rs index cbc24eb26fa47..358e9d034c474 100644 --- a/src/test/ui/or-patterns/or-patterns-syntactic-fail.rs +++ b/src/test/ui/or-patterns/or-patterns-syntactic-fail.rs @@ -1,8 +1,6 @@ // Test some cases where or-patterns may ostensibly be allowed but are in fact not. // This is not a semantic test. We only test parsing. -#![feature(or_patterns)] - fn main() {} enum E { A, B } @@ -14,8 +12,19 @@ fn no_top_level_or_patterns() { // -------- This looks like an or-pattern but is in fact `|A| (B: E | ())`. // ...and for now neither do we allow or-patterns at the top level of functions. - fn fun1(A | B: E) {} //~ ERROR an or-pattern parameter must be wrapped in parentheses + fn fun1(A | B: E) {} + //~^ ERROR top-level or-patterns are not allowed fn fun2(| A | B: E) {} - //~^ ERROR an or-pattern parameter must be wrapped in parentheses + //~^ ERROR top-level or-patterns are not allowed + + // We don't allow top-level or-patterns before type annotation in let-statements because we + // want to reserve this syntactic space for possible future type ascription. + let A | B: E = A; + //~^ ERROR top-level or-patterns are not allowed + + let | A | B: E = A; + //~^ ERROR top-level or-patterns are not allowed + + let (A | B): E = A; // ok -- wrapped in parens } diff --git a/src/test/ui/or-patterns/or-patterns-syntactic-fail.stderr b/src/test/ui/or-patterns/or-patterns-syntactic-fail.stderr index db4d827757b03..5406d51c644a5 100644 --- a/src/test/ui/or-patterns/or-patterns-syntactic-fail.stderr +++ b/src/test/ui/or-patterns/or-patterns-syntactic-fail.stderr @@ -1,17 +1,29 @@ -error: an or-pattern parameter must be wrapped in parentheses - --> $DIR/or-patterns-syntactic-fail.rs:17:13 +error: top-level or-patterns are not allowed in function parameters + --> $DIR/or-patterns-syntactic-fail.rs:15:13 | LL | fn fun1(A | B: E) {} | ^^^^^ help: wrap the pattern in parentheses: `(A | B)` -error: an or-pattern parameter must be wrapped in parentheses - --> $DIR/or-patterns-syntactic-fail.rs:19:13 +error: top-level or-patterns are not allowed in function parameters + --> $DIR/or-patterns-syntactic-fail.rs:18:13 | LL | fn fun2(| A | B: E) {} | ^^^^^^^ help: wrap the pattern in parentheses: `(A | B)` +error: top-level or-patterns are not allowed in `let` bindings + --> $DIR/or-patterns-syntactic-fail.rs:23:9 + | +LL | let A | B: E = A; + | ^^^^^ help: wrap the pattern in parentheses: `(A | B)` + +error: top-level or-patterns are not allowed in `let` bindings + --> $DIR/or-patterns-syntactic-fail.rs:26:9 + | +LL | let | A | B: E = A; + | ^^^^^^^ help: wrap the pattern in parentheses: `(A | B)` + error[E0369]: no implementation for `E | ()` - --> $DIR/or-patterns-syntactic-fail.rs:13:22 + --> $DIR/or-patterns-syntactic-fail.rs:11:22 | LL | let _ = |A | B: E| (); | ----^ -- () @@ -20,6 +32,6 @@ LL | let _ = |A | B: E| (); | = note: an implementation of `std::ops::BitOr` might be missing for `E` -error: aborting due to 3 previous errors +error: aborting due to 5 previous errors For more information about this error, try `rustc --explain E0369`. diff --git a/src/test/ui/or-patterns/or-patterns-syntactic-pass.rs b/src/test/ui/or-patterns/or-patterns-syntactic-pass.rs index 5fe72caf9c1ff..6f9a631b092a7 100644 --- a/src/test/ui/or-patterns/or-patterns-syntactic-pass.rs +++ b/src/test/ui/or-patterns/or-patterns-syntactic-pass.rs @@ -3,8 +3,6 @@ // check-pass -#![feature(or_patterns)] - fn main() {} // Test the `pat` macro fragment parser: @@ -23,11 +21,11 @@ accept_pat!([p | q]); #[cfg(FALSE)] fn or_patterns() { // Top level of `let`: - let | A | B; - let A | B; - let A | B: u8; - let A | B = 0; - let A | B: u8 = 0; + let (| A | B); + let (A | B); + let (A | B): u8; + let (A | B) = 0; + let (A | B): u8 = 0; // Top level of `for`: for | A | B in 0 {} @@ -69,10 +67,10 @@ fn or_patterns() { let [A | B, .. | ..]; // These bind as `(prefix p) | q` as opposed to `prefix (p | q)`: - let box 0 | 1; // Unstable; we *can* the precedence if we want. - let &0 | 1; - let &mut 0 | 1; - let x @ 0 | 1; - let ref x @ 0 | 1; - let ref mut x @ 0 | 1; + let (box 0 | 1); // Unstable; we *can* change the precedence if we want. + let (&0 | 1); + let (&mut 0 | 1); + let (x @ 0 | 1); + let (ref x @ 0 | 1); + let (ref mut x @ 0 | 1); } diff --git a/src/test/ui/or-patterns/remove-leading-vert.fixed b/src/test/ui/or-patterns/remove-leading-vert.fixed index c8fac4faa2a66..b1cd0a94437f6 100644 --- a/src/test/ui/or-patterns/remove-leading-vert.fixed +++ b/src/test/ui/or-patterns/remove-leading-vert.fixed @@ -2,14 +2,13 @@ // run-rustfix -#![feature(or_patterns)] #![allow(warnings)] fn main() {} #[cfg(FALSE)] fn leading() { - fn fun1( A: E) {} //~ ERROR an or-pattern parameter must be wrapped in parentheses + fn fun1( A: E) {} //~ ERROR top-level or-patterns are not allowed fn fun2( A: E) {} //~ ERROR unexpected `||` before function parameter let ( | A): E; let ( | A): (E); //~ ERROR unexpected token `||` in pattern @@ -40,6 +39,9 @@ fn trailing() { //~^ ERROR a trailing `|` is not allowed in an or-pattern } + // These test trailing-vert in `let` bindings, but they also test that we don't emit a + // duplicate suggestion that would confuse rustfix. + let a : u8 = 0; //~ ERROR a trailing `|` is not allowed in an or-pattern let a = 0; //~ ERROR a trailing `|` is not allowed in an or-pattern let a ; //~ ERROR a trailing `|` is not allowed in an or-pattern diff --git a/src/test/ui/or-patterns/remove-leading-vert.rs b/src/test/ui/or-patterns/remove-leading-vert.rs index 2cf6b27ab1aac..dc12382aa3a23 100644 --- a/src/test/ui/or-patterns/remove-leading-vert.rs +++ b/src/test/ui/or-patterns/remove-leading-vert.rs @@ -2,14 +2,13 @@ // run-rustfix -#![feature(or_patterns)] #![allow(warnings)] fn main() {} #[cfg(FALSE)] fn leading() { - fn fun1( | A: E) {} //~ ERROR an or-pattern parameter must be wrapped in parentheses + fn fun1( | A: E) {} //~ ERROR top-level or-patterns are not allowed fn fun2( || A: E) {} //~ ERROR unexpected `||` before function parameter let ( | A): E; let ( || A): (E); //~ ERROR unexpected token `||` in pattern @@ -40,6 +39,9 @@ fn trailing() { //~^ ERROR a trailing `|` is not allowed in an or-pattern } + // These test trailing-vert in `let` bindings, but they also test that we don't emit a + // duplicate suggestion that would confuse rustfix. + let a | : u8 = 0; //~ ERROR a trailing `|` is not allowed in an or-pattern let a | = 0; //~ ERROR a trailing `|` is not allowed in an or-pattern let a | ; //~ ERROR a trailing `|` is not allowed in an or-pattern diff --git a/src/test/ui/or-patterns/remove-leading-vert.stderr b/src/test/ui/or-patterns/remove-leading-vert.stderr index 5c9efd44a187f..af51c67e1c8ba 100644 --- a/src/test/ui/or-patterns/remove-leading-vert.stderr +++ b/src/test/ui/or-patterns/remove-leading-vert.stderr @@ -1,11 +1,11 @@ -error: an or-pattern parameter must be wrapped in parentheses - --> $DIR/remove-leading-vert.rs:12:14 +error: top-level or-patterns are not allowed in function parameters + --> $DIR/remove-leading-vert.rs:11:14 | LL | fn fun1( | A: E) {} - | ^^^ help: remove the leading `|`: `A` + | ^^^ help: remove the `|`: `A` error: unexpected `||` before function parameter - --> $DIR/remove-leading-vert.rs:13:14 + --> $DIR/remove-leading-vert.rs:12:14 | LL | fn fun2( || A: E) {} | ^^ help: remove the `||` @@ -13,31 +13,31 @@ LL | fn fun2( || A: E) {} = note: alternatives in or-patterns are separated with `|`, not `||` error: unexpected token `||` in pattern - --> $DIR/remove-leading-vert.rs:15:11 + --> $DIR/remove-leading-vert.rs:14:11 | LL | let ( || A): (E); | ^^ help: use a single `|` to separate multiple alternative patterns: `|` error: unexpected token `||` in pattern - --> $DIR/remove-leading-vert.rs:18:11 + --> $DIR/remove-leading-vert.rs:17:11 | LL | let [ || A ]: [E; 1]; | ^^ help: use a single `|` to separate multiple alternative patterns: `|` error: unexpected token `||` in pattern - --> $DIR/remove-leading-vert.rs:20:13 + --> $DIR/remove-leading-vert.rs:19:13 | LL | let TS( || A ): TS; | ^^ help: use a single `|` to separate multiple alternative patterns: `|` error: unexpected token `||` in pattern - --> $DIR/remove-leading-vert.rs:22:17 + --> $DIR/remove-leading-vert.rs:21:17 | LL | let NS { f: || A }: NS; | ^^ help: use a single `|` to separate multiple alternative patterns: `|` error: a trailing `|` is not allowed in an or-pattern - --> $DIR/remove-leading-vert.rs:27:13 + --> $DIR/remove-leading-vert.rs:26:13 | LL | let ( A | ): E; | - ^ help: remove the `|` @@ -45,7 +45,7 @@ LL | let ( A | ): E; | while parsing this or-pattern starting here error: a trailing `|` is not allowed in an or-pattern - --> $DIR/remove-leading-vert.rs:28:12 + --> $DIR/remove-leading-vert.rs:27:12 | LL | let (a |,): (E,); | - ^ help: remove the `|` @@ -53,7 +53,7 @@ LL | let (a |,): (E,); | while parsing this or-pattern starting here error: a trailing `|` is not allowed in an or-pattern - --> $DIR/remove-leading-vert.rs:29:17 + --> $DIR/remove-leading-vert.rs:28:17 | LL | let ( A | B | ): E; | - ^ help: remove the `|` @@ -61,7 +61,7 @@ LL | let ( A | B | ): E; | while parsing this or-pattern starting here error: a trailing `|` is not allowed in an or-pattern - --> $DIR/remove-leading-vert.rs:30:17 + --> $DIR/remove-leading-vert.rs:29:17 | LL | let [ A | B | ]: [E; 1]; | - ^ help: remove the `|` @@ -69,7 +69,7 @@ LL | let [ A | B | ]: [E; 1]; | while parsing this or-pattern starting here error: a trailing `|` is not allowed in an or-pattern - --> $DIR/remove-leading-vert.rs:31:18 + --> $DIR/remove-leading-vert.rs:30:18 | LL | let S { f: B | }; | - ^ help: remove the `|` @@ -77,7 +77,7 @@ LL | let S { f: B | }; | while parsing this or-pattern starting here error: unexpected token `||` in pattern - --> $DIR/remove-leading-vert.rs:32:13 + --> $DIR/remove-leading-vert.rs:31:13 | LL | let ( A || B | ): E; | - ^^ help: use a single `|` to separate multiple alternative patterns: `|` @@ -85,7 +85,7 @@ LL | let ( A || B | ): E; | while parsing this or-pattern starting here error: a trailing `|` is not allowed in an or-pattern - --> $DIR/remove-leading-vert.rs:32:18 + --> $DIR/remove-leading-vert.rs:31:18 | LL | let ( A || B | ): E; | - ^ help: remove the `|` @@ -93,7 +93,7 @@ LL | let ( A || B | ): E; | while parsing this or-pattern starting here error: a trailing `|` is not allowed in an or-pattern - --> $DIR/remove-leading-vert.rs:35:11 + --> $DIR/remove-leading-vert.rs:34:11 | LL | A | => {} | - ^ help: remove the `|` @@ -101,7 +101,7 @@ LL | A | => {} | while parsing this or-pattern starting here error: a trailing `|` is not allowed in an or-pattern - --> $DIR/remove-leading-vert.rs:36:11 + --> $DIR/remove-leading-vert.rs:35:11 | LL | A || => {} | - ^^ help: remove the `||` @@ -111,7 +111,7 @@ LL | A || => {} = note: alternatives in or-patterns are separated with `|`, not `||` error: unexpected token `||` in pattern - --> $DIR/remove-leading-vert.rs:37:11 + --> $DIR/remove-leading-vert.rs:36:11 | LL | A || B | => {} | - ^^ help: use a single `|` to separate multiple alternative patterns: `|` @@ -119,7 +119,7 @@ LL | A || B | => {} | while parsing this or-pattern starting here error: a trailing `|` is not allowed in an or-pattern - --> $DIR/remove-leading-vert.rs:37:16 + --> $DIR/remove-leading-vert.rs:36:16 | LL | A || B | => {} | - ^ help: remove the `|` @@ -127,7 +127,7 @@ LL | A || B | => {} | while parsing this or-pattern starting here error: a trailing `|` is not allowed in an or-pattern - --> $DIR/remove-leading-vert.rs:39:17 + --> $DIR/remove-leading-vert.rs:38:17 | LL | | A | B | => {} | - ^ help: remove the `|` @@ -135,7 +135,7 @@ LL | | A | B | => {} | while parsing this or-pattern starting here error: a trailing `|` is not allowed in an or-pattern - --> $DIR/remove-leading-vert.rs:43:11 + --> $DIR/remove-leading-vert.rs:45:11 | LL | let a | : u8 = 0; | - ^ help: remove the `|` @@ -143,7 +143,7 @@ LL | let a | : u8 = 0; | while parsing this or-pattern starting here error: a trailing `|` is not allowed in an or-pattern - --> $DIR/remove-leading-vert.rs:44:11 + --> $DIR/remove-leading-vert.rs:46:11 | LL | let a | = 0; | - ^ help: remove the `|` @@ -151,7 +151,7 @@ LL | let a | = 0; | while parsing this or-pattern starting here error: a trailing `|` is not allowed in an or-pattern - --> $DIR/remove-leading-vert.rs:45:11 + --> $DIR/remove-leading-vert.rs:47:11 | LL | let a | ; | - ^ help: remove the `|` diff --git a/src/test/ui/or-patterns/search-via-bindings.rs b/src/test/ui/or-patterns/search-via-bindings.rs index 067e617373a63..d98606deda570 100644 --- a/src/test/ui/or-patterns/search-via-bindings.rs +++ b/src/test/ui/or-patterns/search-via-bindings.rs @@ -2,8 +2,6 @@ // run-pass -#![feature(or_patterns)] - fn search(target: (bool, bool, bool)) -> u32 { let x = ((false, true), (false, true), (false, true)); let mut guard_count = 0; diff --git a/src/test/ui/or-patterns/slice-patterns.rs b/src/test/ui/or-patterns/slice-patterns.rs index 05c907e824679..ed5eace0b7e6c 100644 --- a/src/test/ui/or-patterns/slice-patterns.rs +++ b/src/test/ui/or-patterns/slice-patterns.rs @@ -2,8 +2,6 @@ // run-pass -#![feature(or_patterns)] - #[derive(Debug, PartialEq)] enum MatchArm { Arm(usize), diff --git a/src/test/ui/or-patterns/struct-like.rs b/src/test/ui/or-patterns/struct-like.rs index 3794a8b6c1510..7de690d2d816a 100644 --- a/src/test/ui/or-patterns/struct-like.rs +++ b/src/test/ui/or-patterns/struct-like.rs @@ -1,7 +1,5 @@ // run-pass -#![feature(or_patterns)] - #[derive(Debug)] enum Other { One, diff --git a/src/test/ui/packed/packed-struct-address-of-element.rs b/src/test/ui/packed/packed-struct-address-of-element.rs index 812d23fb58023..fb3875e680496 100644 --- a/src/test/ui/packed/packed-struct-address-of-element.rs +++ b/src/test/ui/packed/packed-struct-address-of-element.rs @@ -1,6 +1,6 @@ // run-pass #![allow(dead_code)] -#![deny(safe_packed_borrows)] +#![deny(unaligned_references)] #![feature(raw_ref_op)] // ignore-emscripten weird assertion? diff --git a/src/test/ui/packed/packed-struct-borrow-element-64bit.rs b/src/test/ui/packed/packed-struct-borrow-element-64bit.rs new file mode 100644 index 0000000000000..ad932fdcd01c4 --- /dev/null +++ b/src/test/ui/packed/packed-struct-borrow-element-64bit.rs @@ -0,0 +1,17 @@ +// run-pass (note: this is spec-UB, but it works for now) +// ignore-32bit (needs `usize` to be 8-aligned to reproduce all the errors below) +#![allow(dead_code)] +// ignore-emscripten weird assertion? + +#[repr(C, packed(4))] +struct Foo4C { + bar: u8, + baz: usize +} + +pub fn main() { + let foo = Foo4C { bar: 1, baz: 2 }; + let brw = &foo.baz; //~WARN reference to packed field is unaligned + //~^ previously accepted + assert_eq!(*brw, 2); +} diff --git a/src/test/ui/packed/packed-struct-borrow-element-64bit.stderr b/src/test/ui/packed/packed-struct-borrow-element-64bit.stderr new file mode 100644 index 0000000000000..766d8a72c349e --- /dev/null +++ b/src/test/ui/packed/packed-struct-borrow-element-64bit.stderr @@ -0,0 +1,13 @@ +warning: reference to packed field is unaligned + --> $DIR/packed-struct-borrow-element-64bit.rs:14:15 + | +LL | let brw = &foo.baz; + | ^^^^^^^^ + | + = note: `#[warn(unaligned_references)]` on by default + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #82523 <https://github.com/rust-lang/rust/issues/82523> + = note: fields of packed structs are not properly aligned, and creating a misaligned reference is undefined behavior (even if that reference is never dereferenced) + +warning: 1 warning emitted + diff --git a/src/test/ui/packed/packed-struct-borrow-element.rs b/src/test/ui/packed/packed-struct-borrow-element.rs index 0072b6191ebb0..fb6c9c11d5694 100644 --- a/src/test/ui/packed/packed-struct-borrow-element.rs +++ b/src/test/ui/packed/packed-struct-borrow-element.rs @@ -22,14 +22,12 @@ struct Foo4C { pub fn main() { let foo = Foo1 { bar: 1, baz: 2 }; - let brw = unsafe { &foo.baz }; + let brw = &foo.baz; //~WARN reference to packed field is unaligned + //~^ previously accepted assert_eq!(*brw, 2); let foo = Foo2 { bar: 1, baz: 2 }; - let brw = unsafe { &foo.baz }; - assert_eq!(*brw, 2); - - let foo = Foo4C { bar: 1, baz: 2 }; - let brw = unsafe { &foo.baz }; + let brw = &foo.baz; //~WARN reference to packed field is unaligned + //~^ previously accepted assert_eq!(*brw, 2); } diff --git a/src/test/ui/packed/packed-struct-borrow-element.stderr b/src/test/ui/packed/packed-struct-borrow-element.stderr new file mode 100644 index 0000000000000..5764e951a4600 --- /dev/null +++ b/src/test/ui/packed/packed-struct-borrow-element.stderr @@ -0,0 +1,23 @@ +warning: reference to packed field is unaligned + --> $DIR/packed-struct-borrow-element.rs:25:15 + | +LL | let brw = &foo.baz; + | ^^^^^^^^ + | + = note: `#[warn(unaligned_references)]` on by default + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #82523 <https://github.com/rust-lang/rust/issues/82523> + = note: fields of packed structs are not properly aligned, and creating a misaligned reference is undefined behavior (even if that reference is never dereferenced) + +warning: reference to packed field is unaligned + --> $DIR/packed-struct-borrow-element.rs:30:15 + | +LL | let brw = &foo.baz; + | ^^^^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #82523 <https://github.com/rust-lang/rust/issues/82523> + = note: fields of packed structs are not properly aligned, and creating a misaligned reference is undefined behavior (even if that reference is never dereferenced) + +warning: 2 warnings emitted + diff --git a/src/test/ui/panic-runtime/two-panic-runtimes.rs b/src/test/ui/panic-runtime/two-panic-runtimes.rs index c968b5ea1e186..7ec658ebcf2e9 100644 --- a/src/test/ui/panic-runtime/two-panic-runtimes.rs +++ b/src/test/ui/panic-runtime/two-panic-runtimes.rs @@ -1,7 +1,6 @@ // build-fail // dont-check-compiler-stderr // error-pattern:cannot link together two panic runtimes: panic_runtime_unwind and panic_runtime_unwind2 -// ignore-tidy-linelength // aux-build:panic-runtime-unwind.rs // aux-build:panic-runtime-unwind2.rs // aux-build:panic-runtime-lang-items.rs diff --git a/src/test/ui/panic-runtime/unwind-tables-panic-required.rs b/src/test/ui/panic-runtime/unwind-tables-panic-required.rs deleted file mode 100644 index 6393a27046b8a..0000000000000 --- a/src/test/ui/panic-runtime/unwind-tables-panic-required.rs +++ /dev/null @@ -1,11 +0,0 @@ -// Tests that the compiler errors if the user tries to turn off unwind tables -// when they are required. -// -// dont-check-compiler-stderr -// compile-flags: -C panic=unwind -C force-unwind-tables=no -// ignore-tidy-linelength -// -// error-pattern: panic=unwind requires unwind tables, they cannot be disabled with `-C force-unwind-tables=no`. - -pub fn main() { -} diff --git a/src/test/ui/panic-runtime/unwind-tables-target-required.rs b/src/test/ui/panic-runtime/unwind-tables-target-required.rs index 14c1789376414..3abb52b675a60 100644 --- a/src/test/ui/panic-runtime/unwind-tables-target-required.rs +++ b/src/test/ui/panic-runtime/unwind-tables-target-required.rs @@ -3,7 +3,6 @@ // // only-x86_64-windows-msvc // compile-flags: -C force-unwind-tables=no -// ignore-tidy-linelength // // error-pattern: target requires unwind tables, they cannot be disabled with `-C force-unwind-tables=no`. diff --git a/src/test/ui/bad/bad-crate-name.rs b/src/test/ui/parser/bad-crate-name.rs similarity index 100% rename from src/test/ui/bad/bad-crate-name.rs rename to src/test/ui/parser/bad-crate-name.rs diff --git a/src/test/ui/bad/bad-crate-name.stderr b/src/test/ui/parser/bad-crate-name.stderr similarity index 100% rename from src/test/ui/bad/bad-crate-name.stderr rename to src/test/ui/parser/bad-crate-name.stderr diff --git a/src/test/ui/parser/circular_modules_main.rs b/src/test/ui/parser/circular_modules_main.rs index 1ae36a1f7605e..d4b47efe68158 100644 --- a/src/test/ui/parser/circular_modules_main.rs +++ b/src/test/ui/parser/circular_modules_main.rs @@ -1,10 +1,12 @@ +// error-pattern: circular modules + #[path = "circular_modules_hello.rs"] -mod circular_modules_hello; //~ ERROR: circular modules +mod circular_modules_hello; pub fn hi_str() -> String { "Hi!".to_string() } fn main() { - circular_modules_hello::say_hello(); //~ ERROR cannot find function `say_hello` in module + circular_modules_hello::say_hello(); } diff --git a/src/test/ui/parser/circular_modules_main.stderr b/src/test/ui/parser/circular_modules_main.stderr index 5d4db8c31a2c3..ee45f65a3bd5a 100644 --- a/src/test/ui/parser/circular_modules_main.stderr +++ b/src/test/ui/parser/circular_modules_main.stderr @@ -1,18 +1,18 @@ -error: circular modules: $DIR/circular_modules_hello.rs -> $DIR/circular_modules_main.rs -> $DIR/circular_modules_hello.rs - --> $DIR/circular_modules_main.rs:2:1 +error: circular modules: $DIR/circular_modules_main.rs -> $DIR/circular_modules_hello.rs -> $DIR/circular_modules_main.rs + --> $DIR/circular_modules_hello.rs:4:1 | -LL | mod circular_modules_hello; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | mod circular_modules_main; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ -error[E0425]: cannot find function `say_hello` in module `circular_modules_hello` - --> $DIR/circular_modules_main.rs:9:29 +error[E0425]: cannot find function `hi_str` in module `circular_modules_main` + --> $DIR/circular_modules_hello.rs:7:43 | -LL | circular_modules_hello::say_hello(); - | ^^^^^^^^^ not found in `circular_modules_hello` +LL | println!("{}", circular_modules_main::hi_str()); + | ^^^^^^ not found in `circular_modules_main` | help: consider importing this function | -LL | use circular_modules_hello::say_hello; +LL | use hi_str; | error: aborting due to 2 previous errors diff --git a/src/test/ui/parser/duplicate-visibility.rs b/src/test/ui/parser/duplicate-visibility.rs index 547329cfb1b84..87ba230eab556 100644 --- a/src/test/ui/parser/duplicate-visibility.rs +++ b/src/test/ui/parser/duplicate-visibility.rs @@ -2,6 +2,5 @@ fn main() {} extern "C" { pub pub fn foo(); - //~^ ERROR visibility `pub` is not followed by an item - //~| ERROR non-item in item list + //~^ ERROR expected one of `(`, `async`, `const`, `default`, `extern`, `fn`, `pub`, `unsafe`, or `use`, found keyword `pub` } diff --git a/src/test/ui/parser/duplicate-visibility.stderr b/src/test/ui/parser/duplicate-visibility.stderr index 8d8122292ae9a..d9815fc7395b4 100644 --- a/src/test/ui/parser/duplicate-visibility.stderr +++ b/src/test/ui/parser/duplicate-visibility.stderr @@ -1,21 +1,16 @@ -error: visibility `pub` is not followed by an item - --> $DIR/duplicate-visibility.rs:4:5 - | -LL | pub pub fn foo(); - | ^^^ the visibility - | - = help: you likely meant to define an item, e.g., `pub fn foo() {}` - -error: non-item in item list +error: expected one of `(`, `async`, `const`, `default`, `extern`, `fn`, `pub`, `unsafe`, or `use`, found keyword `pub` --> $DIR/duplicate-visibility.rs:4:9 | LL | extern "C" { - | - item list starts here + | - while parsing this item list starting here LL | pub pub fn foo(); - | ^^^ non-item starts here -... + | ^^^ + | | + | expected one of 9 possible tokens + | help: visibility `pub` must come before `pub pub`: `pub pub pub` +LL | LL | } - | - item list ends here + | - the item list ends here -error: aborting due to 2 previous errors +error: aborting due to previous error diff --git a/src/test/ui/issues/issue-48508-aux.rs b/src/test/ui/parser/issue-48508-aux.rs similarity index 100% rename from src/test/ui/issues/issue-48508-aux.rs rename to src/test/ui/parser/issue-48508-aux.rs diff --git a/src/test/ui/issues/issue-48508.rs b/src/test/ui/parser/issue-48508.rs similarity index 95% rename from src/test/ui/issues/issue-48508.rs rename to src/test/ui/parser/issue-48508.rs index 8dc9351260ebc..37d04c5d65f06 100644 --- a/src/test/ui/issues/issue-48508.rs +++ b/src/test/ui/parser/issue-48508.rs @@ -10,7 +10,6 @@ // ignore-pretty issue #37195 // ignore-asmjs wasm2js does not support source maps yet -#![feature(non_ascii_idents)] #![allow(uncommon_codepoints)] #[path = "issue-48508-aux.rs"] diff --git a/src/test/ui/parser/issue-63116.stderr b/src/test/ui/parser/issue-63116.stderr index e249a93df92a2..4766dfafea1c9 100644 --- a/src/test/ui/parser/issue-63116.stderr +++ b/src/test/ui/parser/issue-63116.stderr @@ -12,11 +12,11 @@ error: expected one of `!`, `(`, `)`, `+`, `,`, `::`, or `<`, found `;` LL | impl W <s(f;Y(;] | ^ expected one of 7 possible tokens -error: expected one of `!`, `&&`, `&`, `(`, `)`, `*`, `+`, `,`, `->`, `...`, `::`, `:`, `<`, `=`, `>`, `?`, `[`, `_`, `async`, `const`, `dyn`, `extern`, `fn`, `for`, `impl`, `unsafe`, lifetime, or path, found `;` - --> $DIR/issue-63116.rs:3:15 +error: mismatched closing delimiter: `]` + --> $DIR/issue-63116.rs:3:16 | LL | impl W <s(f;Y(;] - | -^ help: `)` may belong here + | - ^ mismatched closing delimiter | | | unclosed delimiter diff --git a/src/test/ui/parser/issue-66357-unexpected-unreachable.rs b/src/test/ui/parser/issue-66357-unexpected-unreachable.rs index 5ec143fae2344..aed428bfc2a79 100644 --- a/src/test/ui/parser/issue-66357-unexpected-unreachable.rs +++ b/src/test/ui/parser/issue-66357-unexpected-unreachable.rs @@ -1,5 +1,3 @@ -// ignore-tidy-linelength - // The problem in #66357 was that the call trace: // // - parse_fn_block_decl diff --git a/src/test/ui/parser/issue-66357-unexpected-unreachable.stderr b/src/test/ui/parser/issue-66357-unexpected-unreachable.stderr index c3810999d2395..332711df72f3c 100644 --- a/src/test/ui/parser/issue-66357-unexpected-unreachable.stderr +++ b/src/test/ui/parser/issue-66357-unexpected-unreachable.stderr @@ -1,11 +1,11 @@ error: expected one of `,` or `:`, found `(` - --> $DIR/issue-66357-unexpected-unreachable.rs:14:13 + --> $DIR/issue-66357-unexpected-unreachable.rs:12:13 | LL | fn f() { |[](* } | ^ expected one of `,` or `:` error: expected one of `&`, `(`, `)`, `-`, `...`, `..=`, `..`, `[`, `_`, `box`, `mut`, `ref`, `|`, identifier, or path, found `*` - --> $DIR/issue-66357-unexpected-unreachable.rs:14:14 + --> $DIR/issue-66357-unexpected-unreachable.rs:12:14 | LL | fn f() { |[](* } | -^ help: `)` may belong here diff --git a/src/test/ui/issues/issue-68000-unicode-ident-after-missing-comma.rs b/src/test/ui/parser/issue-68000-unicode-ident-after-missing-comma.rs similarity index 100% rename from src/test/ui/issues/issue-68000-unicode-ident-after-missing-comma.rs rename to src/test/ui/parser/issue-68000-unicode-ident-after-missing-comma.rs diff --git a/src/test/ui/issues/issue-68000-unicode-ident-after-missing-comma.stderr b/src/test/ui/parser/issue-68000-unicode-ident-after-missing-comma.stderr similarity index 100% rename from src/test/ui/issues/issue-68000-unicode-ident-after-missing-comma.stderr rename to src/test/ui/parser/issue-68000-unicode-ident-after-missing-comma.stderr diff --git a/src/test/ui/issues/issue-7222.rs b/src/test/ui/parser/issue-7222.rs similarity index 100% rename from src/test/ui/issues/issue-7222.rs rename to src/test/ui/parser/issue-7222.rs diff --git a/src/test/ui/parser/issue-76437-async.rs b/src/test/ui/parser/issue-76437-async.rs new file mode 100644 index 0000000000000..84ee3dd21123c --- /dev/null +++ b/src/test/ui/parser/issue-76437-async.rs @@ -0,0 +1,7 @@ +// edition:2018 + +mod t { + async pub fn t() {} + //~^ ERROR expected one of `extern`, `fn`, or `unsafe`, found keyword `pub` + //~| HELP visibility `pub` must come before `async` +} diff --git a/src/test/ui/parser/issue-76437-async.stderr b/src/test/ui/parser/issue-76437-async.stderr new file mode 100644 index 0000000000000..2c9c2a8cfba9b --- /dev/null +++ b/src/test/ui/parser/issue-76437-async.stderr @@ -0,0 +1,11 @@ +error: expected one of `extern`, `fn`, or `unsafe`, found keyword `pub` + --> $DIR/issue-76437-async.rs:4:11 + | +LL | async pub fn t() {} + | ------^^^ + | | | + | | expected one of `extern`, `fn`, or `unsafe` + | help: visibility `pub` must come before `async`: `pub async` + +error: aborting due to previous error + diff --git a/src/test/ui/parser/issue-76437-const-async-unsafe.rs b/src/test/ui/parser/issue-76437-const-async-unsafe.rs new file mode 100644 index 0000000000000..f1e06e4ad89e7 --- /dev/null +++ b/src/test/ui/parser/issue-76437-const-async-unsafe.rs @@ -0,0 +1,7 @@ +// edition:2018 + +mod t { + const async unsafe pub fn t() {} + //~^ ERROR expected one of `extern` or `fn`, found keyword `pub` + //~| HELP visibility `pub` must come before `const async unsafe` +} diff --git a/src/test/ui/parser/issue-76437-const-async-unsafe.stderr b/src/test/ui/parser/issue-76437-const-async-unsafe.stderr new file mode 100644 index 0000000000000..2e91beda1165d --- /dev/null +++ b/src/test/ui/parser/issue-76437-const-async-unsafe.stderr @@ -0,0 +1,11 @@ +error: expected one of `extern` or `fn`, found keyword `pub` + --> $DIR/issue-76437-const-async-unsafe.rs:4:24 + | +LL | const async unsafe pub fn t() {} + | -------------------^^^ + | | | + | | expected one of `extern` or `fn` + | help: visibility `pub` must come before `const async unsafe`: `pub const async unsafe` + +error: aborting due to previous error + diff --git a/src/test/ui/parser/issue-76437-const-async.rs b/src/test/ui/parser/issue-76437-const-async.rs new file mode 100644 index 0000000000000..3c789fdcd0228 --- /dev/null +++ b/src/test/ui/parser/issue-76437-const-async.rs @@ -0,0 +1,7 @@ +// edition:2018 + +mod t { + const async pub fn t() {} + //~^ ERROR expected one of `extern`, `fn`, or `unsafe`, found keyword `pub` + //~| HELP visibility `pub` must come before `const async` +} diff --git a/src/test/ui/parser/issue-76437-const-async.stderr b/src/test/ui/parser/issue-76437-const-async.stderr new file mode 100644 index 0000000000000..21b96c14d7de0 --- /dev/null +++ b/src/test/ui/parser/issue-76437-const-async.stderr @@ -0,0 +1,11 @@ +error: expected one of `extern`, `fn`, or `unsafe`, found keyword `pub` + --> $DIR/issue-76437-const-async.rs:4:17 + | +LL | const async pub fn t() {} + | ------------^^^ + | | | + | | expected one of `extern`, `fn`, or `unsafe` + | help: visibility `pub` must come before `const async`: `pub const async` + +error: aborting due to previous error + diff --git a/src/test/ui/parser/issue-76437-const.rs b/src/test/ui/parser/issue-76437-const.rs new file mode 100644 index 0000000000000..d3815a52346ee --- /dev/null +++ b/src/test/ui/parser/issue-76437-const.rs @@ -0,0 +1,7 @@ +// edition:2018 + +mod t { + const pub fn t() {} + //~^ ERROR expected one of `async`, `extern`, `fn`, or `unsafe`, found keyword `pub` + //~| HELP visibility `pub` must come before `const` +} diff --git a/src/test/ui/parser/issue-76437-const.stderr b/src/test/ui/parser/issue-76437-const.stderr new file mode 100644 index 0000000000000..cf80d9a9037c9 --- /dev/null +++ b/src/test/ui/parser/issue-76437-const.stderr @@ -0,0 +1,11 @@ +error: expected one of `async`, `extern`, `fn`, or `unsafe`, found keyword `pub` + --> $DIR/issue-76437-const.rs:4:11 + | +LL | const pub fn t() {} + | ------^^^ + | | | + | | expected one of `async`, `extern`, `fn`, or `unsafe` + | help: visibility `pub` must come before `const`: `pub const` + +error: aborting due to previous error + diff --git a/src/test/ui/parser/issue-76437-pub-crate-unsafe.rs b/src/test/ui/parser/issue-76437-pub-crate-unsafe.rs new file mode 100644 index 0000000000000..daa1d120795fd --- /dev/null +++ b/src/test/ui/parser/issue-76437-pub-crate-unsafe.rs @@ -0,0 +1,7 @@ +// edition:2018 + +mod t { + unsafe pub(crate) fn t() {} + //~^ ERROR expected one of `extern` or `fn`, found keyword `pub` + //~| HELP visibility `pub(crate)` must come before `unsafe` +} diff --git a/src/test/ui/parser/issue-76437-pub-crate-unsafe.stderr b/src/test/ui/parser/issue-76437-pub-crate-unsafe.stderr new file mode 100644 index 0000000000000..fa8f13721c894 --- /dev/null +++ b/src/test/ui/parser/issue-76437-pub-crate-unsafe.stderr @@ -0,0 +1,11 @@ +error: expected one of `extern` or `fn`, found keyword `pub` + --> $DIR/issue-76437-pub-crate-unsafe.rs:4:12 + | +LL | unsafe pub(crate) fn t() {} + | -------^^^------- + | | | + | | expected one of `extern` or `fn` + | help: visibility `pub(crate)` must come before `unsafe`: `pub(crate) unsafe` + +error: aborting due to previous error + diff --git a/src/test/ui/parser/issue-76437-unsafe.rs b/src/test/ui/parser/issue-76437-unsafe.rs new file mode 100644 index 0000000000000..785a79a79a2e0 --- /dev/null +++ b/src/test/ui/parser/issue-76437-unsafe.rs @@ -0,0 +1,7 @@ +// edition:2018 + +mod t { + unsafe pub fn t() {} + //~^ ERROR expected one of `extern` or `fn`, found keyword `pub` + //~| HELP visibility `pub` must come before `unsafe` +} diff --git a/src/test/ui/parser/issue-76437-unsafe.stderr b/src/test/ui/parser/issue-76437-unsafe.stderr new file mode 100644 index 0000000000000..c63292ef85395 --- /dev/null +++ b/src/test/ui/parser/issue-76437-unsafe.stderr @@ -0,0 +1,11 @@ +error: expected one of `extern` or `fn`, found keyword `pub` + --> $DIR/issue-76437-unsafe.rs:4:12 + | +LL | unsafe pub fn t() {} + | -------^^^ + | | | + | | expected one of `extern` or `fn` + | help: visibility `pub` must come before `unsafe`: `pub unsafe` + +error: aborting due to previous error + diff --git a/src/test/ui/parser/issue-84117.rs b/src/test/ui/parser/issue-84117.rs new file mode 100644 index 0000000000000..0f20073591598 --- /dev/null +++ b/src/test/ui/parser/issue-84117.rs @@ -0,0 +1,9 @@ +fn main() { + let outer_local:e_outer<&str, { let inner_local:e_inner<&str, } + //~^ ERROR expected one of `>`, a const expression + //~| ERROR expected one of `>`, a const expression, lifetime, or type, found `}` + //~| ERROR expected one of `!`, `.`, `::`, `;`, `?`, `{`, or an operator, found `,` + //~| ERROR expected one of `!`, `.`, `::`, `;`, `?`, `{`, or an operator, found `,` + //~| ERROR expected one of `!`, `.`, `::`, `;`, `?`, `{`, or an operator, found `,` +} +//~^ ERROR expected one of `,`, `:`, `=`, or `>`, found `}` diff --git a/src/test/ui/parser/issue-84117.stderr b/src/test/ui/parser/issue-84117.stderr new file mode 100644 index 0000000000000..d667a4977d0d3 --- /dev/null +++ b/src/test/ui/parser/issue-84117.stderr @@ -0,0 +1,49 @@ +error: expected one of `>`, a const expression, lifetime, or type, found `}` + --> $DIR/issue-84117.rs:2:67 + | +LL | let outer_local:e_outer<&str, { let inner_local:e_inner<&str, } + | ------------ ^ expected one of `>`, a const expression, lifetime, or type + | | | + | | help: use `=` if you meant to assign + | while parsing the type for `inner_local` + +error: expected one of `!`, `.`, `::`, `;`, `?`, `{`, or an operator, found `,` + --> $DIR/issue-84117.rs:2:65 + | +LL | let outer_local:e_outer<&str, { let inner_local:e_inner<&str, } + | ^ expected one of 7 possible tokens + +error: expected one of `,`, `:`, `=`, or `>`, found `}` + --> $DIR/issue-84117.rs:8:1 + | +LL | let outer_local:e_outer<&str, { let inner_local:e_inner<&str, } + | ------------ help: use `=` if you meant to assign - expected one of `,`, `:`, `=`, or `>` + | | + | while parsing the type for `outer_local` +... +LL | } + | ^ unexpected token + +error: expected one of `>`, a const expression, lifetime, or type, found `}` + --> $DIR/issue-84117.rs:2:67 + | +LL | let outer_local:e_outer<&str, { let inner_local:e_inner<&str, } + | ------------ ^ expected one of `>`, a const expression, lifetime, or type + | | | + | | help: use `=` if you meant to assign + | while parsing the type for `inner_local` + +error: expected one of `!`, `.`, `::`, `;`, `?`, `{`, or an operator, found `,` + --> $DIR/issue-84117.rs:2:65 + | +LL | let outer_local:e_outer<&str, { let inner_local:e_inner<&str, } + | ^ expected one of 7 possible tokens + +error: expected one of `!`, `.`, `::`, `;`, `?`, `{`, or an operator, found `,` + --> $DIR/issue-84117.rs:2:33 + | +LL | let outer_local:e_outer<&str, { let inner_local:e_inner<&str, } + | ^ expected one of 7 possible tokens + +error: aborting due to 6 previous errors + diff --git a/src/test/ui/parser/issue-8537.stderr b/src/test/ui/parser/issue-8537.stderr index 3f63c08021098..5a29ce2221fc2 100644 --- a/src/test/ui/parser/issue-8537.stderr +++ b/src/test/ui/parser/issue-8537.stderr @@ -4,7 +4,7 @@ error[E0703]: invalid ABI: found `invalid-ab_isize` LL | "invalid-ab_isize" | ^^^^^^^^^^^^^^^^^^ invalid ABI | - = help: valid ABIs: Rust, C, cdecl, stdcall, fastcall, vectorcall, thiscall, aapcs, win64, sysv64, ptx-kernel, msp430-interrupt, x86-interrupt, amdgpu-kernel, efiapi, avr-interrupt, avr-non-blocking-interrupt, C-cmse-nonsecure-call, system, rust-intrinsic, rust-call, platform-intrinsic, unadjusted + = help: valid ABIs: Rust, C, C-unwind, cdecl, stdcall, stdcall-unwind, fastcall, vectorcall, thiscall, thiscall-unwind, aapcs, win64, sysv64, ptx-kernel, msp430-interrupt, x86-interrupt, amdgpu-kernel, efiapi, avr-interrupt, avr-non-blocking-interrupt, C-cmse-nonsecure-call, wasm, system, system-unwind, rust-intrinsic, rust-call, platform-intrinsic, unadjusted error: aborting due to previous error diff --git a/src/test/ui/parser/item-free-const-no-body-semantic-fail.rs b/src/test/ui/parser/item-free-const-no-body-semantic-fail.rs index 613b3c9856171..15a15a207b1ca 100644 --- a/src/test/ui/parser/item-free-const-no-body-semantic-fail.rs +++ b/src/test/ui/parser/item-free-const-no-body-semantic-fail.rs @@ -4,4 +4,3 @@ fn main() {} const A: u8; //~ ERROR free constant item without body const B; //~ ERROR free constant item without body -//~^ ERROR missing type for `const` item diff --git a/src/test/ui/parser/item-free-const-no-body-semantic-fail.stderr b/src/test/ui/parser/item-free-const-no-body-semantic-fail.stderr index 4e97229fa1a41..aa75e5cee01d4 100644 --- a/src/test/ui/parser/item-free-const-no-body-semantic-fail.stderr +++ b/src/test/ui/parser/item-free-const-no-body-semantic-fail.stderr @@ -14,11 +14,5 @@ LL | const B; | | | help: provide a definition for the constant: `= <expr>;` -error: missing type for `const` item - --> $DIR/item-free-const-no-body-semantic-fail.rs:6:7 - | -LL | const B; - | ^ help: provide a type for the item: `B: [type error]` - -error: aborting due to 3 previous errors +error: aborting due to 2 previous errors diff --git a/src/test/ui/parser/item-free-static-no-body-semantic-fail.rs b/src/test/ui/parser/item-free-static-no-body-semantic-fail.rs index 780479e3d26ac..61d3eab24d8c7 100644 --- a/src/test/ui/parser/item-free-static-no-body-semantic-fail.rs +++ b/src/test/ui/parser/item-free-static-no-body-semantic-fail.rs @@ -4,8 +4,6 @@ fn main() {} static A: u8; //~ ERROR free static item without body static B; //~ ERROR free static item without body -//~^ ERROR missing type for `static` item static mut C: u8; //~ ERROR free static item without body static mut D; //~ ERROR free static item without body -//~^ ERROR missing type for `static mut` item diff --git a/src/test/ui/parser/item-free-static-no-body-semantic-fail.stderr b/src/test/ui/parser/item-free-static-no-body-semantic-fail.stderr index 60b7bb34c698b..7b408323674de 100644 --- a/src/test/ui/parser/item-free-static-no-body-semantic-fail.stderr +++ b/src/test/ui/parser/item-free-static-no-body-semantic-fail.stderr @@ -15,7 +15,7 @@ LL | static B; | help: provide a definition for the static: `= <expr>;` error: free static item without body - --> $DIR/item-free-static-no-body-semantic-fail.rs:9:1 + --> $DIR/item-free-static-no-body-semantic-fail.rs:8:1 | LL | static mut C: u8; | ^^^^^^^^^^^^^^^^- @@ -23,24 +23,12 @@ LL | static mut C: u8; | help: provide a definition for the static: `= <expr>;` error: free static item without body - --> $DIR/item-free-static-no-body-semantic-fail.rs:10:1 + --> $DIR/item-free-static-no-body-semantic-fail.rs:9:1 | LL | static mut D; | ^^^^^^^^^^^^- | | | help: provide a definition for the static: `= <expr>;` -error: missing type for `static` item - --> $DIR/item-free-static-no-body-semantic-fail.rs:6:8 - | -LL | static B; - | ^ help: provide a type for the item: `B: [type error]` - -error: missing type for `static mut` item - --> $DIR/item-free-static-no-body-semantic-fail.rs:10:12 - | -LL | static mut D; - | ^ help: provide a type for the item: `D: [type error]` - -error: aborting due to 6 previous errors +error: aborting due to 4 previous errors diff --git a/src/test/ui/parser/let-binop.stderr b/src/test/ui/parser/let-binop.stderr index 90295854a2d2d..dd33e9157cfd5 100644 --- a/src/test/ui/parser/let-binop.stderr +++ b/src/test/ui/parser/let-binop.stderr @@ -3,18 +3,24 @@ error: can't reassign to an uninitialized variable | LL | let a: i8 *= 1; | ^^ help: initialize the variable + | + = help: if you meant to overwrite, remove the `let` binding error: can't reassign to an uninitialized variable --> $DIR/let-binop.rs:6:11 | LL | let b += 1; | ^^ help: initialize the variable + | + = help: if you meant to overwrite, remove the `let` binding error: can't reassign to an uninitialized variable --> $DIR/let-binop.rs:8:11 | LL | let c *= 1; | ^^ help: initialize the variable + | + = help: if you meant to overwrite, remove the `let` binding error: aborting due to 3 previous errors diff --git a/src/test/ui/lex-bare-cr-nondoc-comment.rs b/src/test/ui/parser/lex-bare-cr-nondoc-comment.rs similarity index 100% rename from src/test/ui/lex-bare-cr-nondoc-comment.rs rename to src/test/ui/parser/lex-bare-cr-nondoc-comment.rs diff --git a/src/test/ui/lexer-crlf-line-endings-string-literal-doc-comment.rs b/src/test/ui/parser/lexer-crlf-line-endings-string-literal-doc-comment.rs similarity index 100% rename from src/test/ui/lexer-crlf-line-endings-string-literal-doc-comment.rs rename to src/test/ui/parser/lexer-crlf-line-endings-string-literal-doc-comment.rs diff --git a/src/test/ui/parser/lifetime-in-pattern.stderr b/src/test/ui/parser/lifetime-in-pattern.stderr index 71fd3cdf72370..4ffee657cabbe 100644 --- a/src/test/ui/parser/lifetime-in-pattern.stderr +++ b/src/test/ui/parser/lifetime-in-pattern.stderr @@ -9,6 +9,20 @@ error: expected one of `:`, `@`, or `|`, found `)` | LL | fn test(&'a str) { | ^ expected one of `:`, `@`, or `|` + | + = note: anonymous parameters are removed in the 2018 edition (see RFC 1685) +help: if this is a `self` type, give it a parameter name + | +LL | fn test(self: &str) { + | ^^^^^^^^^^ +help: if this is a parameter name, give it a type + | +LL | fn test(str: &TypeName) { + | ^^^^^^^^^^^^^^ +help: if this is a type, explicitly ignore the parameter name + | +LL | fn test(_: &str) { + | ^^^^^^^ error: aborting due to 2 previous errors diff --git a/src/test/ui/parser/match-arm-without-braces.rs b/src/test/ui/parser/match-arm-without-braces.rs new file mode 100644 index 0000000000000..55a887427696d --- /dev/null +++ b/src/test/ui/parser/match-arm-without-braces.rs @@ -0,0 +1,87 @@ +struct S; + +impl S { + fn get<K, V: Default>(_: K) -> Option<V> { + Default::default() + } +} + +enum Val { + Foo, + Bar, +} + +impl Default for Val { + fn default() -> Self { + Val::Foo + } +} + +fn main() { + match S::get(1) { + Some(Val::Foo) => {} + _ => {} + } + match S::get(2) { + Some(Val::Foo) => 3; //~ ERROR `match` arm body without braces + _ => 4, + } + match S::get(5) { + Some(Val::Foo) => + 7; //~ ERROR `match` arm body without braces + 8; + _ => 9, + } + match S::get(10) { + Some(Val::Foo) => + 11; //~ ERROR `match` arm body without braces + 12; + _ => (), + } + match S::get(13) { + None => {} + Some(Val::Foo) => + 14; //~ ERROR `match` arm body without braces + 15; + } + match S::get(16) { + Some(Val::Foo) => 17 + _ => 18, //~ ERROR expected one of + } + match S::get(19) { + Some(Val::Foo) => + 20; //~ ERROR `match` arm body without braces + 21 + _ => 22, + } + match S::get(23) { + Some(Val::Foo) => + 24; //~ ERROR `match` arm body without braces + 25 + _ => (), + } + match S::get(26) { + None => {} + Some(Val::Foo) => + 27; //~ ERROR `match` arm body without braces + 28 + } + match S::get(29) { + Some(Val::Foo) => + 30; //~ ERROR expected one of + 31, + _ => 32, + } + match S::get(33) { + Some(Val::Foo) => + 34; //~ ERROR expected one of + 35, + _ => (), + } + match S::get(36) { + None => {} + Some(Val::Foo) => + 37; //~ ERROR expected one of + 38, + } +} diff --git a/src/test/ui/parser/match-arm-without-braces.stderr b/src/test/ui/parser/match-arm-without-braces.stderr new file mode 100644 index 0000000000000..03ae351bf7902 --- /dev/null +++ b/src/test/ui/parser/match-arm-without-braces.stderr @@ -0,0 +1,135 @@ +error: `match` arm body without braces + --> $DIR/match-arm-without-braces.rs:26:27 + | +LL | Some(Val::Foo) => 3; + | -- ^- help: use a comma to end a `match` arm expression: `,` + | | | + | | this statement is not surrounded by a body + | while parsing the `match` arm starting here + +error: `match` arm body without braces + --> $DIR/match-arm-without-braces.rs:31:11 + | +LL | Some(Val::Foo) => + | -- while parsing the `match` arm starting here +LL | / 7; +LL | | 8; + | |____________^ these statements are not surrounded by a body + | +help: surround the statements with a body + | +LL | { 7; +LL | 8; } + | + +error: `match` arm body without braces + --> $DIR/match-arm-without-braces.rs:37:11 + | +LL | Some(Val::Foo) => + | -- while parsing the `match` arm starting here +LL | / 11; +LL | | 12; + | |_____________^ these statements are not surrounded by a body + | +help: surround the statements with a body + | +LL | { 11; +LL | 12; } + | + +error: `match` arm body without braces + --> $DIR/match-arm-without-braces.rs:44:11 + | +LL | Some(Val::Foo) => + | -- while parsing the `match` arm starting here +LL | / 14; +LL | | 15; + | |_____________^ these statements are not surrounded by a body + | +help: surround the statements with a body + | +LL | { 14; +LL | 15; } + | + +error: expected one of `,`, `.`, `?`, `}`, or an operator, found reserved identifier `_` + --> $DIR/match-arm-without-braces.rs:49:9 + | +LL | Some(Val::Foo) => 17 + | -- - expected one of `,`, `.`, `?`, `}`, or an operator + | | + | while parsing the `match` arm starting here +LL | _ => 18, + | ^ unexpected token + +error: `match` arm body without braces + --> $DIR/match-arm-without-braces.rs:53:11 + | +LL | Some(Val::Foo) => + | -- while parsing the `match` arm starting here +LL | / 20; +LL | | 21 + | |____________^ these statements are not surrounded by a body + | +help: surround the statements with a body + | +LL | { 20; +LL | 21 } + | + +error: `match` arm body without braces + --> $DIR/match-arm-without-braces.rs:59:11 + | +LL | Some(Val::Foo) => + | -- while parsing the `match` arm starting here +LL | / 24; +LL | | 25 + | |____________^ these statements are not surrounded by a body + | +help: surround the statements with a body + | +LL | { 24; +LL | 25 } + | + +error: `match` arm body without braces + --> $DIR/match-arm-without-braces.rs:66:11 + | +LL | Some(Val::Foo) => + | -- while parsing the `match` arm starting here +LL | / 27; +LL | | 28 + | |____________^ these statements are not surrounded by a body + | +help: surround the statements with a body + | +LL | { 27; +LL | 28 } + | + +error: expected one of `,`, `.`, `?`, `}`, or an operator, found `;` + --> $DIR/match-arm-without-braces.rs:71:13 + | +LL | Some(Val::Foo) => + | -- while parsing the `match` arm starting here +LL | 30; + | ^ expected one of `,`, `.`, `?`, `}`, or an operator + +error: expected one of `,`, `.`, `?`, `}`, or an operator, found `;` + --> $DIR/match-arm-without-braces.rs:77:13 + | +LL | Some(Val::Foo) => + | -- while parsing the `match` arm starting here +LL | 34; + | ^ expected one of `,`, `.`, `?`, `}`, or an operator + +error: expected one of `,`, `.`, `?`, `}`, or an operator, found `;` + --> $DIR/match-arm-without-braces.rs:84:13 + | +LL | Some(Val::Foo) => + | -- while parsing the `match` arm starting here +LL | 37; + | ^ expected one of `,`, `.`, `?`, `}`, or an operator + +error: aborting due to 11 previous errors + diff --git a/src/test/ui/parser/multibyte-char-use-seperator-issue-80134.rs b/src/test/ui/parser/multibyte-char-use-seperator-issue-80134.rs index f3ae3aba9b9e6..7e7995d6724eb 100644 --- a/src/test/ui/parser/multibyte-char-use-seperator-issue-80134.rs +++ b/src/test/ui/parser/multibyte-char-use-seperator-issue-80134.rs @@ -4,9 +4,7 @@ fn main() { (()é); //~^ ERROR: expected one of `)`, `,`, `.`, `?`, or an operator //~| ERROR: cannot find value `é` in this scope - //~| ERROR: non-ascii idents are not fully supported (()氷); //~^ ERROR: expected one of `)`, `,`, `.`, `?`, or an operator //~| ERROR: cannot find value `氷` in this scope - //~| ERROR: non-ascii idents are not fully supported } diff --git a/src/test/ui/parser/multibyte-char-use-seperator-issue-80134.stderr b/src/test/ui/parser/multibyte-char-use-seperator-issue-80134.stderr index 892cc92b1bdeb..21e71aa121560 100644 --- a/src/test/ui/parser/multibyte-char-use-seperator-issue-80134.stderr +++ b/src/test/ui/parser/multibyte-char-use-seperator-issue-80134.stderr @@ -8,7 +8,7 @@ LL | (()é); | help: missing `,` error: expected one of `)`, `,`, `.`, `?`, or an operator, found `氷` - --> $DIR/multibyte-char-use-seperator-issue-80134.rs:8:8 + --> $DIR/multibyte-char-use-seperator-issue-80134.rs:7:8 | LL | (()氷); | -^ @@ -23,30 +23,11 @@ LL | (()é); | ^ not found in this scope error[E0425]: cannot find value `氷` in this scope - --> $DIR/multibyte-char-use-seperator-issue-80134.rs:8:8 + --> $DIR/multibyte-char-use-seperator-issue-80134.rs:7:8 | LL | (()氷); | ^^ not found in this scope -error[E0658]: non-ascii idents are not fully supported - --> $DIR/multibyte-char-use-seperator-issue-80134.rs:4:8 - | -LL | (()é); - | ^ - | - = note: see issue #55467 <https://github.com/rust-lang/rust/issues/55467> for more information - = help: add `#![feature(non_ascii_idents)]` to the crate attributes to enable - -error[E0658]: non-ascii idents are not fully supported - --> $DIR/multibyte-char-use-seperator-issue-80134.rs:8:8 - | -LL | (()氷); - | ^^ - | - = note: see issue #55467 <https://github.com/rust-lang/rust/issues/55467> for more information - = help: add `#![feature(non_ascii_idents)]` to the crate attributes to enable - -error: aborting due to 6 previous errors +error: aborting due to 4 previous errors -Some errors have detailed explanations: E0425, E0658. -For more information about an error, try `rustc --explain E0425`. +For more information about this error, try `rustc --explain E0425`. diff --git a/src/test/ui/parser/unicode-quote-chars.rs b/src/test/ui/parser/unicode-quote-chars.rs index eeaea3628bbe5..868d2b227b74c 100644 --- a/src/test/ui/parser/unicode-quote-chars.rs +++ b/src/test/ui/parser/unicode-quote-chars.rs @@ -1,5 +1,3 @@ -// ignore-tidy-linelength - fn main() { println!(“hello world”); //~^ ERROR unknown start of token: \u{201c} diff --git a/src/test/ui/parser/unicode-quote-chars.stderr b/src/test/ui/parser/unicode-quote-chars.stderr index d9ec92b3f8a83..04ea0c6e95f39 100644 --- a/src/test/ui/parser/unicode-quote-chars.stderr +++ b/src/test/ui/parser/unicode-quote-chars.stderr @@ -1,5 +1,5 @@ error: unknown start of token: \u{201c} - --> $DIR/unicode-quote-chars.rs:4:14 + --> $DIR/unicode-quote-chars.rs:2:14 | LL | println!(“hello world”); | ^ @@ -10,7 +10,7 @@ LL | println!("hello world"); | ^^^^^^^^^^^^^ error: unknown start of token: \u{201d} - --> $DIR/unicode-quote-chars.rs:4:26 + --> $DIR/unicode-quote-chars.rs:2:26 | LL | println!(“hello world”); | ^ @@ -21,7 +21,7 @@ LL | println!(“hello world"); | ^ error: expected `,`, found `world` - --> $DIR/unicode-quote-chars.rs:4:21 + --> $DIR/unicode-quote-chars.rs:2:21 | LL | println!(“hello world”); | ^^^^^ expected `,` diff --git a/src/test/ui/parser/use-unclosed-brace.rs b/src/test/ui/parser/use-unclosed-brace.rs new file mode 100644 index 0000000000000..41742f37f3c50 --- /dev/null +++ b/src/test/ui/parser/use-unclosed-brace.rs @@ -0,0 +1,12 @@ +// error-pattern: expected one of `,`, `::`, `as`, or `}`, found `;` +// error-pattern: this file contains an unclosed delimiter +// error-pattern: expected item, found `}` +use foo::{bar, baz; + +use std::fmt::Display; + +mod bar { } + +mod baz { } + +fn main() {} diff --git a/src/test/ui/parser/use-unclosed-brace.stderr b/src/test/ui/parser/use-unclosed-brace.stderr new file mode 100644 index 0000000000000..d29a68f821481 --- /dev/null +++ b/src/test/ui/parser/use-unclosed-brace.stderr @@ -0,0 +1,27 @@ +error: this file contains an unclosed delimiter + --> $DIR/use-unclosed-brace.rs:12:14 + | +LL | use foo::{bar, baz; + | - unclosed delimiter +... +LL | fn main() {} + | ^ + +error: expected one of `,`, `::`, `as`, or `}`, found `;` + --> $DIR/use-unclosed-brace.rs:4:19 + | +LL | use foo::{bar, baz; + | - ^ + | | | + | | expected one of `,`, `::`, `as`, or `}` + | | help: `}` may belong here + | unclosed delimiter + +error: expected item, found `}` + --> $DIR/use-unclosed-brace.rs:12:14 + | +LL | fn main() {} + | ^ expected item + +error: aborting due to 3 previous errors + diff --git a/src/test/ui/parser/variadic-ffi-semantic-restrictions.rs b/src/test/ui/parser/variadic-ffi-semantic-restrictions.rs index 404f409075519..fe993a6ee1302 100644 --- a/src/test/ui/parser/variadic-ffi-semantic-restrictions.rs +++ b/src/test/ui/parser/variadic-ffi-semantic-restrictions.rs @@ -1,4 +1,5 @@ #![feature(c_variadic)] +#![allow(anonymous_parameters)] fn main() {} diff --git a/src/test/ui/parser/variadic-ffi-semantic-restrictions.stderr b/src/test/ui/parser/variadic-ffi-semantic-restrictions.stderr index ebfe4979fb6f2..10fd05c0bef3f 100644 --- a/src/test/ui/parser/variadic-ffi-semantic-restrictions.stderr +++ b/src/test/ui/parser/variadic-ffi-semantic-restrictions.stderr @@ -1,203 +1,203 @@ error: only foreign or `unsafe extern "C" functions may be C-variadic - --> $DIR/variadic-ffi-semantic-restrictions.rs:5:19 + --> $DIR/variadic-ffi-semantic-restrictions.rs:6:19 | LL | fn f1_1(x: isize, ...) {} | ^^^ error: C-variadic function must be declared with at least one named argument - --> $DIR/variadic-ffi-semantic-restrictions.rs:8:9 + --> $DIR/variadic-ffi-semantic-restrictions.rs:9:9 | LL | fn f1_2(...) {} | ^^^ error: only foreign or `unsafe extern "C" functions may be C-variadic - --> $DIR/variadic-ffi-semantic-restrictions.rs:8:9 + --> $DIR/variadic-ffi-semantic-restrictions.rs:9:9 | LL | fn f1_2(...) {} | ^^^ error: only foreign or `unsafe extern "C" functions may be C-variadic - --> $DIR/variadic-ffi-semantic-restrictions.rs:12:30 + --> $DIR/variadic-ffi-semantic-restrictions.rs:13:30 | LL | extern "C" fn f2_1(x: isize, ...) {} | ^^^ error: C-variadic function must be declared with at least one named argument - --> $DIR/variadic-ffi-semantic-restrictions.rs:15:20 + --> $DIR/variadic-ffi-semantic-restrictions.rs:16:20 | LL | extern "C" fn f2_2(...) {} | ^^^ error: only foreign or `unsafe extern "C" functions may be C-variadic - --> $DIR/variadic-ffi-semantic-restrictions.rs:15:20 + --> $DIR/variadic-ffi-semantic-restrictions.rs:16:20 | LL | extern "C" fn f2_2(...) {} | ^^^ error: `...` must be the last argument of a C-variadic function - --> $DIR/variadic-ffi-semantic-restrictions.rs:19:20 + --> $DIR/variadic-ffi-semantic-restrictions.rs:20:20 | LL | extern "C" fn f2_3(..., x: isize) {} | ^^^ error: only foreign or `unsafe extern "C" functions may be C-variadic - --> $DIR/variadic-ffi-semantic-restrictions.rs:19:20 + --> $DIR/variadic-ffi-semantic-restrictions.rs:20:20 | LL | extern "C" fn f2_3(..., x: isize) {} | ^^^ error: only foreign or `unsafe extern "C" functions may be C-variadic - --> $DIR/variadic-ffi-semantic-restrictions.rs:23:30 + --> $DIR/variadic-ffi-semantic-restrictions.rs:24:30 | LL | extern "C" fn f3_1(x: isize, ...) {} | ^^^ error: C-variadic function must be declared with at least one named argument - --> $DIR/variadic-ffi-semantic-restrictions.rs:26:20 + --> $DIR/variadic-ffi-semantic-restrictions.rs:27:20 | LL | extern "C" fn f3_2(...) {} | ^^^ error: only foreign or `unsafe extern "C" functions may be C-variadic - --> $DIR/variadic-ffi-semantic-restrictions.rs:26:20 + --> $DIR/variadic-ffi-semantic-restrictions.rs:27:20 | LL | extern "C" fn f3_2(...) {} | ^^^ error: `...` must be the last argument of a C-variadic function - --> $DIR/variadic-ffi-semantic-restrictions.rs:30:20 + --> $DIR/variadic-ffi-semantic-restrictions.rs:31:20 | LL | extern "C" fn f3_3(..., x: isize) {} | ^^^ error: only foreign or `unsafe extern "C" functions may be C-variadic - --> $DIR/variadic-ffi-semantic-restrictions.rs:30:20 + --> $DIR/variadic-ffi-semantic-restrictions.rs:31:20 | LL | extern "C" fn f3_3(..., x: isize) {} | ^^^ error: C-variadic function must be declared with at least one named argument - --> $DIR/variadic-ffi-semantic-restrictions.rs:35:13 + --> $DIR/variadic-ffi-semantic-restrictions.rs:36:13 | LL | fn e_f1(...); | ^^^ error: `...` must be the last argument of a C-variadic function - --> $DIR/variadic-ffi-semantic-restrictions.rs:37:13 + --> $DIR/variadic-ffi-semantic-restrictions.rs:38:13 | LL | fn e_f2(..., x: isize); | ^^^ error: only foreign or `unsafe extern "C" functions may be C-variadic - --> $DIR/variadic-ffi-semantic-restrictions.rs:44:23 + --> $DIR/variadic-ffi-semantic-restrictions.rs:45:23 | LL | fn i_f1(x: isize, ...) {} | ^^^ error: C-variadic function must be declared with at least one named argument - --> $DIR/variadic-ffi-semantic-restrictions.rs:46:13 + --> $DIR/variadic-ffi-semantic-restrictions.rs:47:13 | LL | fn i_f2(...) {} | ^^^ error: only foreign or `unsafe extern "C" functions may be C-variadic - --> $DIR/variadic-ffi-semantic-restrictions.rs:46:13 + --> $DIR/variadic-ffi-semantic-restrictions.rs:47:13 | LL | fn i_f2(...) {} | ^^^ error: `...` must be the last argument of a C-variadic function - --> $DIR/variadic-ffi-semantic-restrictions.rs:49:13 + --> $DIR/variadic-ffi-semantic-restrictions.rs:50:13 | LL | fn i_f3(..., x: isize, ...) {} | ^^^ error: only foreign or `unsafe extern "C" functions may be C-variadic - --> $DIR/variadic-ffi-semantic-restrictions.rs:49:13 + --> $DIR/variadic-ffi-semantic-restrictions.rs:50:13 | LL | fn i_f3(..., x: isize, ...) {} | ^^^ error: only foreign or `unsafe extern "C" functions may be C-variadic - --> $DIR/variadic-ffi-semantic-restrictions.rs:49:28 + --> $DIR/variadic-ffi-semantic-restrictions.rs:50:28 | LL | fn i_f3(..., x: isize, ...) {} | ^^^ error: `...` must be the last argument of a C-variadic function - --> $DIR/variadic-ffi-semantic-restrictions.rs:53:13 + --> $DIR/variadic-ffi-semantic-restrictions.rs:54:13 | LL | fn i_f4(..., x: isize, ...) {} | ^^^ error: only foreign or `unsafe extern "C" functions may be C-variadic - --> $DIR/variadic-ffi-semantic-restrictions.rs:53:13 + --> $DIR/variadic-ffi-semantic-restrictions.rs:54:13 | LL | fn i_f4(..., x: isize, ...) {} | ^^^ error: only foreign or `unsafe extern "C" functions may be C-variadic - --> $DIR/variadic-ffi-semantic-restrictions.rs:53:28 + --> $DIR/variadic-ffi-semantic-restrictions.rs:54:28 | LL | fn i_f4(..., x: isize, ...) {} | ^^^ error: only foreign or `unsafe extern "C" functions may be C-variadic - --> $DIR/variadic-ffi-semantic-restrictions.rs:60:23 + --> $DIR/variadic-ffi-semantic-restrictions.rs:61:23 | LL | fn t_f1(x: isize, ...) {} | ^^^ error: only foreign or `unsafe extern "C" functions may be C-variadic - --> $DIR/variadic-ffi-semantic-restrictions.rs:62:23 + --> $DIR/variadic-ffi-semantic-restrictions.rs:63:23 | LL | fn t_f2(x: isize, ...); | ^^^ error: C-variadic function must be declared with at least one named argument - --> $DIR/variadic-ffi-semantic-restrictions.rs:64:13 + --> $DIR/variadic-ffi-semantic-restrictions.rs:65:13 | LL | fn t_f3(...) {} | ^^^ error: only foreign or `unsafe extern "C" functions may be C-variadic - --> $DIR/variadic-ffi-semantic-restrictions.rs:64:13 + --> $DIR/variadic-ffi-semantic-restrictions.rs:65:13 | LL | fn t_f3(...) {} | ^^^ error: C-variadic function must be declared with at least one named argument - --> $DIR/variadic-ffi-semantic-restrictions.rs:67:13 + --> $DIR/variadic-ffi-semantic-restrictions.rs:68:13 | LL | fn t_f4(...); | ^^^ error: only foreign or `unsafe extern "C" functions may be C-variadic - --> $DIR/variadic-ffi-semantic-restrictions.rs:67:13 + --> $DIR/variadic-ffi-semantic-restrictions.rs:68:13 | LL | fn t_f4(...); | ^^^ error: `...` must be the last argument of a C-variadic function - --> $DIR/variadic-ffi-semantic-restrictions.rs:70:13 + --> $DIR/variadic-ffi-semantic-restrictions.rs:71:13 | LL | fn t_f5(..., x: isize) {} | ^^^ error: only foreign or `unsafe extern "C" functions may be C-variadic - --> $DIR/variadic-ffi-semantic-restrictions.rs:70:13 + --> $DIR/variadic-ffi-semantic-restrictions.rs:71:13 | LL | fn t_f5(..., x: isize) {} | ^^^ error: `...` must be the last argument of a C-variadic function - --> $DIR/variadic-ffi-semantic-restrictions.rs:73:13 + --> $DIR/variadic-ffi-semantic-restrictions.rs:74:13 | LL | fn t_f6(..., x: isize); | ^^^ error: only foreign or `unsafe extern "C" functions may be C-variadic - --> $DIR/variadic-ffi-semantic-restrictions.rs:73:13 + --> $DIR/variadic-ffi-semantic-restrictions.rs:74:13 | LL | fn t_f6(..., x: isize); | ^^^ diff --git a/src/test/ui/pattern/bindings-after-at/or-patterns-box-patterns.rs b/src/test/ui/pattern/bindings-after-at/or-patterns-box-patterns.rs index ca8826f03f1ad..cffff80eead11 100644 --- a/src/test/ui/pattern/bindings-after-at/or-patterns-box-patterns.rs +++ b/src/test/ui/pattern/bindings-after-at/or-patterns-box-patterns.rs @@ -3,7 +3,6 @@ // run-pass #![feature(bindings_after_at)] -#![feature(or_patterns)] #![feature(box_patterns)] #[derive(Debug, PartialEq)] diff --git a/src/test/ui/pattern/bindings-after-at/or-patterns-slice-patterns.rs b/src/test/ui/pattern/bindings-after-at/or-patterns-slice-patterns.rs index 65c2b3741b3e3..a99ec0158f02d 100644 --- a/src/test/ui/pattern/bindings-after-at/or-patterns-slice-patterns.rs +++ b/src/test/ui/pattern/bindings-after-at/or-patterns-slice-patterns.rs @@ -3,7 +3,6 @@ // run-pass #![feature(bindings_after_at)] -#![feature(or_patterns)] #[derive(Debug, PartialEq)] enum MatchArm { diff --git a/src/test/ui/pattern/bindings-after-at/or-patterns.rs b/src/test/ui/pattern/bindings-after-at/or-patterns.rs index a0e14004ab1b0..caf6d91e0011b 100644 --- a/src/test/ui/pattern/bindings-after-at/or-patterns.rs +++ b/src/test/ui/pattern/bindings-after-at/or-patterns.rs @@ -3,7 +3,6 @@ // run-pass #![feature(bindings_after_at)] -#![feature(or_patterns)] #[derive(Debug, PartialEq)] enum MatchArm { diff --git a/src/test/ui/pattern/bindings-after-at/pat-at-same-name-both.rs b/src/test/ui/pattern/bindings-after-at/pat-at-same-name-both.rs index e8b5b492b7738..09df15f754538 100644 --- a/src/test/ui/pattern/bindings-after-at/pat-at-same-name-both.rs +++ b/src/test/ui/pattern/bindings-after-at/pat-at-same-name-both.rs @@ -2,7 +2,6 @@ // The code that is tested here lives in resolve (see `resolve_pattern_inner`). #![feature(bindings_after_at)] -#![feature(or_patterns)] fn main() { fn f(a @ a @ a: ()) {} diff --git a/src/test/ui/pattern/bindings-after-at/pat-at-same-name-both.stderr b/src/test/ui/pattern/bindings-after-at/pat-at-same-name-both.stderr index cba17d82e93c5..77a4937322430 100644 --- a/src/test/ui/pattern/bindings-after-at/pat-at-same-name-both.stderr +++ b/src/test/ui/pattern/bindings-after-at/pat-at-same-name-both.stderr @@ -1,59 +1,59 @@ error[E0415]: identifier `a` is bound more than once in this parameter list - --> $DIR/pat-at-same-name-both.rs:8:14 + --> $DIR/pat-at-same-name-both.rs:7:14 | LL | fn f(a @ a @ a: ()) {} | ^ used as parameter more than once error[E0415]: identifier `a` is bound more than once in this parameter list - --> $DIR/pat-at-same-name-both.rs:8:18 + --> $DIR/pat-at-same-name-both.rs:7:18 | LL | fn f(a @ a @ a: ()) {} | ^ used as parameter more than once error[E0416]: identifier `a` is bound more than once in the same pattern - --> $DIR/pat-at-same-name-both.rs:13:20 + --> $DIR/pat-at-same-name-both.rs:12:20 | LL | Ok(a @ b @ a) | ^ used in a pattern more than once error[E0416]: identifier `a` is bound more than once in the same pattern - --> $DIR/pat-at-same-name-both.rs:15:23 + --> $DIR/pat-at-same-name-both.rs:14:23 | LL | | Err(a @ b @ a) | ^ used in a pattern more than once error[E0416]: identifier `a` is bound more than once in the same pattern - --> $DIR/pat-at-same-name-both.rs:20:13 + --> $DIR/pat-at-same-name-both.rs:19:13 | LL | let a @ a @ a = (); | ^ used in a pattern more than once error[E0416]: identifier `a` is bound more than once in the same pattern - --> $DIR/pat-at-same-name-both.rs:20:17 + --> $DIR/pat-at-same-name-both.rs:19:17 | LL | let a @ a @ a = (); | ^ used in a pattern more than once error[E0416]: identifier `a` is bound more than once in the same pattern - --> $DIR/pat-at-same-name-both.rs:23:21 + --> $DIR/pat-at-same-name-both.rs:22:21 | LL | let ref a @ ref a = (); | ^ used in a pattern more than once error[E0416]: identifier `a` is bound more than once in the same pattern - --> $DIR/pat-at-same-name-both.rs:25:29 + --> $DIR/pat-at-same-name-both.rs:24:29 | LL | let ref mut a @ ref mut a = (); | ^ used in a pattern more than once error[E0416]: identifier `a` is bound more than once in the same pattern - --> $DIR/pat-at-same-name-both.rs:28:17 + --> $DIR/pat-at-same-name-both.rs:27:17 | LL | let a @ (Ok(a) | Err(a)) = Ok(()); | ^ used in a pattern more than once error[E0416]: identifier `a` is bound more than once in the same pattern - --> $DIR/pat-at-same-name-both.rs:28:26 + --> $DIR/pat-at-same-name-both.rs:27:26 | LL | let a @ (Ok(a) | Err(a)) = Ok(()); | ^ used in a pattern more than once diff --git a/src/test/ui/issues/issue-10392.rs b/src/test/ui/pattern/issue-10392.rs similarity index 100% rename from src/test/ui/issues/issue-10392.rs rename to src/test/ui/pattern/issue-10392.rs diff --git a/src/test/ui/pattern/issue-82290.stderr b/src/test/ui/pattern/issue-82290.stderr index 65ef018dc9737..666b1e785bf6b 100644 --- a/src/test/ui/pattern/issue-82290.stderr +++ b/src/test/ui/pattern/issue-82290.stderr @@ -4,8 +4,7 @@ error: `let` expressions are not supported here LL | if true && let x = 1 { | ^^^^^^^^^ | - = note: only supported directly in conditions of `if`- and `while`-expressions - = note: as well as when nested within `&&` and parenthesis in those conditions + = note: only supported directly without parentheses in conditions of `if`- and `while`-expressions, as well as in `let` chains within parentheses warning: the feature `let_chains` is incomplete and may not be safe to use and/or cause compiler crashes --> $DIR/issue-82290.rs:1:12 diff --git a/src/test/ui/pattern/move-ref-patterns/borrowck-move-ref-pattern.stderr b/src/test/ui/pattern/move-ref-patterns/borrowck-move-ref-pattern.stderr index 285c203f382df..d0726f05cc3be 100644 --- a/src/test/ui/pattern/move-ref-patterns/borrowck-move-ref-pattern.stderr +++ b/src/test/ui/pattern/move-ref-patterns/borrowck-move-ref-pattern.stderr @@ -16,7 +16,7 @@ LL | let [ref _x0_hold, _x1, ref xs_hold @ ..] = arr; | --- | | | first assignment to `_x1` - | help: make this binding mutable: `mut _x1` + | help: consider making this binding mutable: `mut _x1` LL | _x1 = U; | ^^^^^^^ cannot assign twice to immutable variable @@ -74,7 +74,7 @@ LL | let (ref _x0, _x1, ref _x2, ..) = tup; | --- | | | first assignment to `_x1` - | help: make this binding mutable: `mut _x1` + | help: consider making this binding mutable: `mut _x1` LL | _x1 = U; | ^^^^^^^ cannot assign twice to immutable variable diff --git a/src/test/ui/pattern/usefulness/refutable-pattern-errors.rs b/src/test/ui/pattern/usefulness/refutable-pattern-errors.rs index 75658c490c4e2..7c9aa51e7484c 100644 --- a/src/test/ui/pattern/usefulness/refutable-pattern-errors.rs +++ b/src/test/ui/pattern/usefulness/refutable-pattern-errors.rs @@ -1,5 +1,3 @@ -// ignore-tidy-linelength - fn func((1, (Some(1), 2..=3)): (isize, (Option<isize>, isize))) { } //~^ ERROR refutable pattern in function argument: `(_, _)` not covered diff --git a/src/test/ui/pattern/usefulness/refutable-pattern-errors.stderr b/src/test/ui/pattern/usefulness/refutable-pattern-errors.stderr index 99af71cadfc1e..74ec646e31cca 100644 --- a/src/test/ui/pattern/usefulness/refutable-pattern-errors.stderr +++ b/src/test/ui/pattern/usefulness/refutable-pattern-errors.stderr @@ -1,5 +1,5 @@ error[E0005]: refutable pattern in function argument: `(_, _)` not covered - --> $DIR/refutable-pattern-errors.rs:3:9 + --> $DIR/refutable-pattern-errors.rs:1:9 | LL | fn func((1, (Some(1), 2..=3)): (isize, (Option<isize>, isize))) { } | ^^^^^^^^^^^^^^^^^^^^^ pattern `(_, _)` not covered @@ -7,7 +7,7 @@ LL | fn func((1, (Some(1), 2..=3)): (isize, (Option<isize>, isize))) { } = note: the matched value is of type `(isize, (Option<isize>, isize))` error[E0005]: refutable pattern in local binding: `(i32::MIN..=0_i32, _)` and `(2_i32..=i32::MAX, _)` not covered - --> $DIR/refutable-pattern-errors.rs:7:9 + --> $DIR/refutable-pattern-errors.rs:5:9 | LL | let (1, (Some(1), 2..=3)) = (1, (None, 2)); | ^^^^^^^^^^^^^^^^^^^^^ patterns `(i32::MIN..=0_i32, _)` and `(2_i32..=i32::MAX, _)` not covered diff --git a/src/test/ui/phantom-auto-trait.stderr b/src/test/ui/phantom-auto-trait.stderr index 779919f9d643f..de13176ddc2a5 100644 --- a/src/test/ui/phantom-auto-trait.stderr +++ b/src/test/ui/phantom-auto-trait.stderr @@ -7,13 +7,21 @@ LL | fn is_zen<T: Zen>(_: T) {} LL | is_zen(x) | ^ `T` cannot be shared between threads safely | - = note: required because of the requirements on the impl of `Zen` for `&T` +note: required because of the requirements on the impl of `Zen` for `&T` + --> $DIR/phantom-auto-trait.rs:10:24 + | +LL | unsafe impl<'a, T: 'a> Zen for &'a T where T: Sync {} + | ^^^ ^^^^^ = note: required because it appears within the type `PhantomData<&T>` - = note: required because it appears within the type `Guard<'_, T>` +note: required because it appears within the type `Guard<'_, T>` + --> $DIR/phantom-auto-trait.rs:12:8 + | +LL | struct Guard<'a, T: 'a> { + | ^^^^^ help: consider restricting type parameter `T` | -LL | fn not_sync<T: Sync>(x: Guard<T>) { - | ^^^^^^ +LL | fn not_sync<T: std::marker::Sync>(x: Guard<T>) { + | ^^^^^^^^^^^^^^^^^^^ error[E0277]: `T` cannot be shared between threads safely --> $DIR/phantom-auto-trait.rs:26:12 @@ -24,14 +32,26 @@ LL | fn is_zen<T: Zen>(_: T) {} LL | is_zen(x) | ^ `T` cannot be shared between threads safely | - = note: required because of the requirements on the impl of `Zen` for `&T` +note: required because of the requirements on the impl of `Zen` for `&T` + --> $DIR/phantom-auto-trait.rs:10:24 + | +LL | unsafe impl<'a, T: 'a> Zen for &'a T where T: Sync {} + | ^^^ ^^^^^ = note: required because it appears within the type `PhantomData<&T>` - = note: required because it appears within the type `Guard<'_, T>` - = note: required because it appears within the type `Nested<Guard<'_, T>>` +note: required because it appears within the type `Guard<'_, T>` + --> $DIR/phantom-auto-trait.rs:12:8 + | +LL | struct Guard<'a, T: 'a> { + | ^^^^^ +note: required because it appears within the type `Nested<Guard<'_, T>>` + --> $DIR/phantom-auto-trait.rs:16:8 + | +LL | struct Nested<T>(T); + | ^^^^^^ help: consider restricting type parameter `T` | -LL | fn nested_not_sync<T: Sync>(x: Nested<Guard<T>>) { - | ^^^^^^ +LL | fn nested_not_sync<T: std::marker::Sync>(x: Nested<Guard<T>>) { + | ^^^^^^^^^^^^^^^^^^^ error: aborting due to 2 previous errors diff --git a/src/test/ui/polymorphization/promoted-function-3.rs b/src/test/ui/polymorphization/promoted-function-3.rs index 1c84df13e10b0..bbd991e36ccaf 100644 --- a/src/test/ui/polymorphization/promoted-function-3.rs +++ b/src/test/ui/polymorphization/promoted-function-3.rs @@ -1,5 +1,5 @@ // run-pass -// compile-flags: -Zpolymorphize=on -Zmir-opt-level=3 +// compile-flags: -Zpolymorphize=on -Zmir-opt-level=4 fn caller<T, U>() -> &'static usize { callee::<U>() diff --git a/src/test/ui/privacy/associated-item-privacy-inherent.stderr b/src/test/ui/privacy/associated-item-privacy-inherent.stderr index 1e94e7c620d03..f8585014fd6d8 100644 --- a/src/test/ui/privacy/associated-item-privacy-inherent.stderr +++ b/src/test/ui/privacy/associated-item-privacy-inherent.stderr @@ -222,7 +222,7 @@ error: type `priv_parent_substs::Priv` is private --> $DIR/associated-item-privacy-inherent.rs:101:9 | LL | Pub::CONST; - | ^^^^^^^^^^ private type + | ^^^ private type ... LL | priv_parent_substs::mac!(); | --------------------------- in this macro invocation diff --git a/src/test/ui/privacy/associated-item-privacy-trait.rs b/src/test/ui/privacy/associated-item-privacy-trait.rs index b4e98debcf3f5..c07aeed99c74a 100644 --- a/src/test/ui/privacy/associated-item-privacy-trait.rs +++ b/src/test/ui/privacy/associated-item-privacy-trait.rs @@ -1,5 +1,3 @@ -// ignore-tidy-linelength - #![feature(decl_macro, associated_type_defaults)] #![allow(unused, private_in_public)] diff --git a/src/test/ui/privacy/associated-item-privacy-trait.stderr b/src/test/ui/privacy/associated-item-privacy-trait.stderr index 8e58a2fa08d7d..e36ce8d541500 100644 --- a/src/test/ui/privacy/associated-item-privacy-trait.stderr +++ b/src/test/ui/privacy/associated-item-privacy-trait.stderr @@ -1,5 +1,5 @@ error: type `for<'r> fn(&'r priv_trait::Pub) {<priv_trait::Pub as PrivTr>::method}` is private - --> $DIR/associated-item-privacy-trait.rs:17:21 + --> $DIR/associated-item-privacy-trait.rs:15:21 | LL | let value = <Pub as PrivTr>::method; | ^^^^^^^^^^^^^^^^^^^^^^^ private type @@ -10,7 +10,7 @@ LL | priv_trait::mac!(); = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) error: type `for<'r> fn(&'r priv_trait::Pub) {<priv_trait::Pub as PrivTr>::method}` is private - --> $DIR/associated-item-privacy-trait.rs:19:9 + --> $DIR/associated-item-privacy-trait.rs:17:9 | LL | value; | ^^^^^ private type @@ -21,7 +21,7 @@ LL | priv_trait::mac!(); = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) error: type `for<'r> fn(&'r Self) {<Self as PrivTr>::method}` is private - --> $DIR/associated-item-privacy-trait.rs:21:13 + --> $DIR/associated-item-privacy-trait.rs:19:13 | LL | Pub.method(); | ^^^^^^ private type @@ -32,7 +32,7 @@ LL | priv_trait::mac!(); = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) error: associated constant `<Pub as PrivTr>::CONST` is private - --> $DIR/associated-item-privacy-trait.rs:23:9 + --> $DIR/associated-item-privacy-trait.rs:21:9 | LL | <Pub as PrivTr>::CONST; | ^^^^^^^^^^^^^^^^^^^^^^ private associated constant @@ -43,7 +43,7 @@ LL | priv_trait::mac!(); = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) error: associated type `<Pub as PrivTr>::AssocTy` is private - --> $DIR/associated-item-privacy-trait.rs:25:16 + --> $DIR/associated-item-privacy-trait.rs:23:16 | LL | let _: <Pub as PrivTr>::AssocTy; | ^^^^^^^^^^^^^^^^^^^^^^^^ private associated type @@ -54,7 +54,7 @@ LL | priv_trait::mac!(); = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) error: trait `PrivTr` is private - --> $DIR/associated-item-privacy-trait.rs:27:34 + --> $DIR/associated-item-privacy-trait.rs:25:34 | LL | pub type InSignatureTy = <Pub as PrivTr>::AssocTy; | ^^^^^^^^^^^^^^^^^^^^^^^^ private trait @@ -65,7 +65,7 @@ LL | priv_trait::mac!(); = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) error: trait `PrivTr` is private - --> $DIR/associated-item-privacy-trait.rs:29:34 + --> $DIR/associated-item-privacy-trait.rs:27:34 | LL | pub trait InSignatureTr: PrivTr {} | ^^^^^^ private trait @@ -76,7 +76,7 @@ LL | priv_trait::mac!(); = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) error: trait `PrivTr` is private - --> $DIR/associated-item-privacy-trait.rs:31:14 + --> $DIR/associated-item-privacy-trait.rs:29:14 | LL | impl PrivTr for u8 {} | ^^^^^^ private trait @@ -87,7 +87,7 @@ LL | priv_trait::mac!(); = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) error: type `priv_signature::Priv` is private - --> $DIR/associated-item-privacy-trait.rs:48:21 + --> $DIR/associated-item-privacy-trait.rs:46:21 | LL | let value = <Pub as PubTr>::method; | ^^^^^^^^^^^^^^^^^^^^^^ private type @@ -98,7 +98,7 @@ LL | priv_signature::mac!(); = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) error: type `priv_signature::Priv` is private - --> $DIR/associated-item-privacy-trait.rs:50:9 + --> $DIR/associated-item-privacy-trait.rs:48:9 | LL | value; | ^^^^^ private type @@ -109,7 +109,7 @@ LL | priv_signature::mac!(); = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) error: type `priv_signature::Priv` is private - --> $DIR/associated-item-privacy-trait.rs:52:13 + --> $DIR/associated-item-privacy-trait.rs:50:13 | LL | Pub.method(loop {}); | ^^^^^^ private type @@ -120,7 +120,7 @@ LL | priv_signature::mac!(); = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) error: type `priv_substs::Priv` is private - --> $DIR/associated-item-privacy-trait.rs:69:21 + --> $DIR/associated-item-privacy-trait.rs:67:21 | LL | let value = <Pub as PubTr>::method::<Priv>; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ private type @@ -131,7 +131,7 @@ LL | priv_substs::mac!(); = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) error: type `priv_substs::Priv` is private - --> $DIR/associated-item-privacy-trait.rs:71:9 + --> $DIR/associated-item-privacy-trait.rs:69:9 | LL | value; | ^^^^^ private type @@ -142,7 +142,7 @@ LL | priv_substs::mac!(); = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) error: type `priv_substs::Priv` is private - --> $DIR/associated-item-privacy-trait.rs:73:9 + --> $DIR/associated-item-privacy-trait.rs:71:9 | LL | Pub.method::<Priv>(); | ^^^^^^^^^^^^^^^^^^^^ private type @@ -153,7 +153,7 @@ LL | priv_substs::mac!(); = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) error: type `priv_parent_substs::Priv` is private - --> $DIR/associated-item-privacy-trait.rs:93:21 + --> $DIR/associated-item-privacy-trait.rs:91:21 | LL | let value = <Pub as PubTr>::method; | ^^^^^^^^^^^^^^^^^^^^^^ private type @@ -164,7 +164,7 @@ LL | priv_parent_substs::mac!(); = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) error: type `priv_parent_substs::Priv` is private - --> $DIR/associated-item-privacy-trait.rs:95:9 + --> $DIR/associated-item-privacy-trait.rs:93:9 | LL | value; | ^^^^^ private type @@ -175,7 +175,7 @@ LL | priv_parent_substs::mac!(); = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) error: type `priv_parent_substs::Priv` is private - --> $DIR/associated-item-privacy-trait.rs:97:21 + --> $DIR/associated-item-privacy-trait.rs:95:21 | LL | let value = <Pub as PubTr<_>>::method; | ^^^^^^^^^^^^^^^^^^^^^^^^^ private type @@ -186,7 +186,7 @@ LL | priv_parent_substs::mac!(); = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) error: type `priv_parent_substs::Priv` is private - --> $DIR/associated-item-privacy-trait.rs:99:9 + --> $DIR/associated-item-privacy-trait.rs:97:9 | LL | value; | ^^^^^ private type @@ -197,7 +197,7 @@ LL | priv_parent_substs::mac!(); = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) error: type `priv_parent_substs::Priv` is private - --> $DIR/associated-item-privacy-trait.rs:101:9 + --> $DIR/associated-item-privacy-trait.rs:99:9 | LL | Pub.method(); | ^^^^^^^^^^^^ private type @@ -208,7 +208,7 @@ LL | priv_parent_substs::mac!(); = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) error: type `priv_parent_substs::Priv` is private - --> $DIR/associated-item-privacy-trait.rs:104:21 + --> $DIR/associated-item-privacy-trait.rs:102:21 | LL | let value = <Priv as PubTr<_>>::method; | ^^^^^^^^^^^^^^^^^^^^^^^^^^ private type @@ -219,7 +219,7 @@ LL | priv_parent_substs::mac!(); = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) error: type `priv_parent_substs::Priv` is private - --> $DIR/associated-item-privacy-trait.rs:106:9 + --> $DIR/associated-item-privacy-trait.rs:104:9 | LL | value; | ^^^^^ private type @@ -230,7 +230,7 @@ LL | priv_parent_substs::mac!(); = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) error: type `priv_parent_substs::Priv` is private - --> $DIR/associated-item-privacy-trait.rs:108:9 + --> $DIR/associated-item-privacy-trait.rs:106:9 | LL | Priv.method(); | ^^^^^^^^^^^^^ private type @@ -241,7 +241,7 @@ LL | priv_parent_substs::mac!(); = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) error: type `priv_parent_substs::Priv` is private - --> $DIR/associated-item-privacy-trait.rs:111:9 + --> $DIR/associated-item-privacy-trait.rs:109:9 | LL | <Pub as PubTr>::CONST; | ^^^^^^^^^^^^^^^^^^^^^ private type @@ -252,7 +252,7 @@ LL | priv_parent_substs::mac!(); = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) error: type `priv_parent_substs::Priv` is private - --> $DIR/associated-item-privacy-trait.rs:113:9 + --> $DIR/associated-item-privacy-trait.rs:111:9 | LL | <Pub as PubTr<_>>::CONST; | ^^^^^^^^^^^^^^^^^^^^^^^^ private type @@ -263,7 +263,7 @@ LL | priv_parent_substs::mac!(); = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) error: type `priv_parent_substs::Priv` is private - --> $DIR/associated-item-privacy-trait.rs:115:9 + --> $DIR/associated-item-privacy-trait.rs:113:9 | LL | <Priv as PubTr<_>>::CONST; | ^^^^^^^^^^^^^^^^^^^^^^^^^ private type @@ -274,7 +274,7 @@ LL | priv_parent_substs::mac!(); = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) error: type `priv_parent_substs::Priv` is private - --> $DIR/associated-item-privacy-trait.rs:119:30 + --> $DIR/associated-item-privacy-trait.rs:117:30 | LL | let _: <Pub as PubTr<_>>::AssocTy; | ^ private type @@ -285,7 +285,7 @@ LL | priv_parent_substs::mac!(); = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) error: type `priv_parent_substs::Priv` is private - --> $DIR/associated-item-privacy-trait.rs:121:17 + --> $DIR/associated-item-privacy-trait.rs:119:17 | LL | let _: <Priv as PubTr<_>>::AssocTy; | ^^^^ private type @@ -296,7 +296,7 @@ LL | priv_parent_substs::mac!(); = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) error: type `priv_parent_substs::Priv` is private - --> $DIR/associated-item-privacy-trait.rs:124:35 + --> $DIR/associated-item-privacy-trait.rs:122:35 | LL | pub type InSignatureTy1 = <Pub as PubTr>::AssocTy; | ^^^^^^^^^^^^^^^^^^^^^^^ private type @@ -307,7 +307,7 @@ LL | priv_parent_substs::mac!(); = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) error: type `priv_parent_substs::Priv` is private - --> $DIR/associated-item-privacy-trait.rs:126:35 + --> $DIR/associated-item-privacy-trait.rs:124:35 | LL | pub type InSignatureTy2 = <Priv as PubTr<Pub>>::AssocTy; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ private type @@ -318,7 +318,7 @@ LL | priv_parent_substs::mac!(); = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) error: type `priv_parent_substs::Priv` is private - --> $DIR/associated-item-privacy-trait.rs:128:14 + --> $DIR/associated-item-privacy-trait.rs:126:14 | LL | impl PubTr for u8 {} | ^^^^^ private type diff --git a/src/test/ui/privacy/private-in-public-assoc-ty.full_tait.stderr b/src/test/ui/privacy/private-in-public-assoc-ty.full_tait.stderr new file mode 100644 index 0000000000000..a74c7c93a2eb5 --- /dev/null +++ b/src/test/ui/privacy/private-in-public-assoc-ty.full_tait.stderr @@ -0,0 +1,77 @@ +warning: the feature `type_alias_impl_trait` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/private-in-public-assoc-ty.rs:7:32 + | +LL | #![cfg_attr(full_tait, feature(type_alias_impl_trait))] + | ^^^^^^^^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #63063 <https://github.com/rust-lang/rust/issues/63063> for more information + +error[E0446]: private type `Priv` in public interface + --> $DIR/private-in-public-assoc-ty.rs:20:9 + | +LL | struct Priv; + | ------------ `Priv` declared as private +... +LL | type A = Priv; + | ^^^^^^^^^^^^^^ can't leak private type + +warning: private trait `PrivTr` in public interface (error E0445) + --> $DIR/private-in-public-assoc-ty.rs:27:9 + | +LL | type Alias1: PrivTr; + | ^^^^^^^^^^^^^^^^^^^^ + | + = note: `#[warn(private_in_public)]` on by default + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #34537 <https://github.com/rust-lang/rust/issues/34537> + +warning: private type `Priv` in public interface (error E0446) + --> $DIR/private-in-public-assoc-ty.rs:30:9 + | +LL | type Alias2: PubTrAux1<Priv> = u8; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #34537 <https://github.com/rust-lang/rust/issues/34537> + +warning: private type `Priv` in public interface (error E0446) + --> $DIR/private-in-public-assoc-ty.rs:33:9 + | +LL | type Alias3: PubTrAux2<A = Priv> = u8; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #34537 <https://github.com/rust-lang/rust/issues/34537> + +error[E0446]: private type `Priv` in public interface + --> $DIR/private-in-public-assoc-ty.rs:37:9 + | +LL | struct Priv; + | ------------ `Priv` declared as private +... +LL | type Alias4 = Priv; + | ^^^^^^^^^^^^^^^^^^^ can't leak private type + +error[E0446]: private type `Priv` in public interface + --> $DIR/private-in-public-assoc-ty.rs:44:9 + | +LL | struct Priv; + | ------------ `Priv` declared as private +... +LL | type Alias1 = Priv; + | ^^^^^^^^^^^^^^^^^^^ can't leak private type + +error[E0445]: private trait `PrivTr` in public interface + --> $DIR/private-in-public-assoc-ty.rs:47:9 + | +LL | trait PrivTr {} + | ------------ `PrivTr` declared as private +... +LL | type Exist = impl PrivTr; + | ^^^^^^^^^^^^^^^^^^^^^^^^^ can't leak private trait + +error: aborting due to 4 previous errors; 4 warnings emitted + +Some errors have detailed explanations: E0445, E0446. +For more information about an error, try `rustc --explain E0445`. diff --git a/src/test/ui/privacy/private-in-public-assoc-ty.min_tait.stderr b/src/test/ui/privacy/private-in-public-assoc-ty.min_tait.stderr new file mode 100644 index 0000000000000..36230ffd04b7c --- /dev/null +++ b/src/test/ui/privacy/private-in-public-assoc-ty.min_tait.stderr @@ -0,0 +1,68 @@ +error[E0446]: private type `Priv` in public interface + --> $DIR/private-in-public-assoc-ty.rs:20:9 + | +LL | struct Priv; + | ------------ `Priv` declared as private +... +LL | type A = Priv; + | ^^^^^^^^^^^^^^ can't leak private type + +warning: private trait `PrivTr` in public interface (error E0445) + --> $DIR/private-in-public-assoc-ty.rs:27:9 + | +LL | type Alias1: PrivTr; + | ^^^^^^^^^^^^^^^^^^^^ + | + = note: `#[warn(private_in_public)]` on by default + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #34537 <https://github.com/rust-lang/rust/issues/34537> + +warning: private type `Priv` in public interface (error E0446) + --> $DIR/private-in-public-assoc-ty.rs:30:9 + | +LL | type Alias2: PubTrAux1<Priv> = u8; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #34537 <https://github.com/rust-lang/rust/issues/34537> + +warning: private type `Priv` in public interface (error E0446) + --> $DIR/private-in-public-assoc-ty.rs:33:9 + | +LL | type Alias3: PubTrAux2<A = Priv> = u8; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #34537 <https://github.com/rust-lang/rust/issues/34537> + +error[E0446]: private type `Priv` in public interface + --> $DIR/private-in-public-assoc-ty.rs:37:9 + | +LL | struct Priv; + | ------------ `Priv` declared as private +... +LL | type Alias4 = Priv; + | ^^^^^^^^^^^^^^^^^^^ can't leak private type + +error[E0446]: private type `Priv` in public interface + --> $DIR/private-in-public-assoc-ty.rs:44:9 + | +LL | struct Priv; + | ------------ `Priv` declared as private +... +LL | type Alias1 = Priv; + | ^^^^^^^^^^^^^^^^^^^ can't leak private type + +error[E0445]: private trait `PrivTr` in public interface + --> $DIR/private-in-public-assoc-ty.rs:47:9 + | +LL | trait PrivTr {} + | ------------ `PrivTr` declared as private +... +LL | type Exist = impl PrivTr; + | ^^^^^^^^^^^^^^^^^^^^^^^^^ can't leak private trait + +error: aborting due to 4 previous errors; 3 warnings emitted + +Some errors have detailed explanations: E0445, E0446. +For more information about an error, try `rustc --explain E0445`. diff --git a/src/test/ui/privacy/private-in-public-assoc-ty.rs b/src/test/ui/privacy/private-in-public-assoc-ty.rs index fba72c13170fe..f8fb565943092 100644 --- a/src/test/ui/privacy/private-in-public-assoc-ty.rs +++ b/src/test/ui/privacy/private-in-public-assoc-ty.rs @@ -2,7 +2,10 @@ // This test also ensures that the checks are performed even inside private modules. #![feature(associated_type_defaults)] -#![feature(type_alias_impl_trait)] +// revisions: min_tait full_tait +#![feature(min_type_alias_impl_trait)] +#![cfg_attr(full_tait, feature(type_alias_impl_trait))] +//[full_tait]~^ WARN incomplete mod m { struct Priv; diff --git a/src/test/ui/privacy/private-in-public-assoc-ty.stderr b/src/test/ui/privacy/private-in-public-assoc-ty.stderr deleted file mode 100644 index ba62a228b09fe..0000000000000 --- a/src/test/ui/privacy/private-in-public-assoc-ty.stderr +++ /dev/null @@ -1,68 +0,0 @@ -error[E0446]: private type `Priv` in public interface - --> $DIR/private-in-public-assoc-ty.rs:17:9 - | -LL | struct Priv; - | ------------ `Priv` declared as private -... -LL | type A = Priv; - | ^^^^^^^^^^^^^^ can't leak private type - -warning: private trait `PrivTr` in public interface (error E0445) - --> $DIR/private-in-public-assoc-ty.rs:24:9 - | -LL | type Alias1: PrivTr; - | ^^^^^^^^^^^^^^^^^^^^ - | - = note: `#[warn(private_in_public)]` on by default - = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = note: for more information, see issue #34537 <https://github.com/rust-lang/rust/issues/34537> - -warning: private type `Priv` in public interface (error E0446) - --> $DIR/private-in-public-assoc-ty.rs:27:9 - | -LL | type Alias2: PubTrAux1<Priv> = u8; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = note: for more information, see issue #34537 <https://github.com/rust-lang/rust/issues/34537> - -warning: private type `Priv` in public interface (error E0446) - --> $DIR/private-in-public-assoc-ty.rs:30:9 - | -LL | type Alias3: PubTrAux2<A = Priv> = u8; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = note: for more information, see issue #34537 <https://github.com/rust-lang/rust/issues/34537> - -error[E0446]: private type `Priv` in public interface - --> $DIR/private-in-public-assoc-ty.rs:34:9 - | -LL | struct Priv; - | ------------ `Priv` declared as private -... -LL | type Alias4 = Priv; - | ^^^^^^^^^^^^^^^^^^^ can't leak private type - -error[E0446]: private type `Priv` in public interface - --> $DIR/private-in-public-assoc-ty.rs:41:9 - | -LL | struct Priv; - | ------------ `Priv` declared as private -... -LL | type Alias1 = Priv; - | ^^^^^^^^^^^^^^^^^^^ can't leak private type - -error[E0445]: private trait `PrivTr` in public interface - --> $DIR/private-in-public-assoc-ty.rs:44:9 - | -LL | trait PrivTr {} - | ------------ `PrivTr` declared as private -... -LL | type Exist = impl PrivTr; - | ^^^^^^^^^^^^^^^^^^^^^^^^^ can't leak private trait - -error: aborting due to 4 previous errors; 3 warnings emitted - -Some errors have detailed explanations: E0445, E0446. -For more information about an error, try `rustc --explain E0445`. diff --git a/src/test/ui/privacy/private-in-public-type-alias-impl-trait.full_tait.stderr b/src/test/ui/privacy/private-in-public-type-alias-impl-trait.full_tait.stderr new file mode 100644 index 0000000000000..c419c7ee4d44a --- /dev/null +++ b/src/test/ui/privacy/private-in-public-type-alias-impl-trait.full_tait.stderr @@ -0,0 +1,11 @@ +warning: the feature `type_alias_impl_trait` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/private-in-public-type-alias-impl-trait.rs:5:32 + | +LL | #![cfg_attr(full_tait, feature(type_alias_impl_trait))] + | ^^^^^^^^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #63063 <https://github.com/rust-lang/rust/issues/63063> for more information + +warning: 1 warning emitted + diff --git a/src/test/ui/privacy/private-in-public-type-alias-impl-trait.rs b/src/test/ui/privacy/private-in-public-type-alias-impl-trait.rs index 40bba720b0fa2..8443631253f78 100644 --- a/src/test/ui/privacy/private-in-public-type-alias-impl-trait.rs +++ b/src/test/ui/privacy/private-in-public-type-alias-impl-trait.rs @@ -1,6 +1,9 @@ // build-pass (FIXME(62277): could be check-pass?) -#![feature(type_alias_impl_trait)] +// revisions: min_tait full_tait +#![feature(min_type_alias_impl_trait)] +#![cfg_attr(full_tait, feature(type_alias_impl_trait))] +//[full_tait]~^ WARN incomplete #![deny(private_in_public)] pub type Pub = impl Default; diff --git a/src/test/ui/privacy/private-inferred-type.stderr b/src/test/ui/privacy/private-inferred-type.stderr index 8c8163d3906b3..11bcb9074d097 100644 --- a/src/test/ui/privacy/private-inferred-type.stderr +++ b/src/test/ui/privacy/private-inferred-type.stderr @@ -56,7 +56,7 @@ error: type `Priv` is private --> $DIR/private-inferred-type.rs:104:5 | LL | m::Pub::INHERENT_ASSOC_CONST; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ private type + | ^^^^^^ private type error: type `Priv` is private --> $DIR/private-inferred-type.rs:105:5 diff --git a/src/test/ui/proc-macro/attr-complex-fn.stdout b/src/test/ui/proc-macro/attr-complex-fn.stdout index a395a9ac3e8fa..72783efe08dba 100644 --- a/src/test/ui/proc-macro/attr-complex-fn.stdout +++ b/src/test/ui/proc-macro/attr-complex-fn.stdout @@ -80,67 +80,67 @@ PRINT-ATTR INPUT (DISPLAY): impl < T > MyTrait < T > for MyStruct < { true } > { PRINT-ATTR INPUT (DEBUG): TokenStream [ Ident { ident: "impl", - span: $DIR/attr-complex-fn.rs:21:1: 24:2 (#0), + span: $DIR/attr-complex-fn.rs:21:1: 21:5 (#0), }, Punct { ch: '<', spacing: Alone, - span: $DIR/attr-complex-fn.rs:21:1: 24:2 (#0), + span: $DIR/attr-complex-fn.rs:21:5: 21:6 (#0), }, Ident { ident: "T", - span: $DIR/attr-complex-fn.rs:21:1: 24:2 (#0), + span: $DIR/attr-complex-fn.rs:21:6: 21:7 (#0), }, Punct { ch: '>', spacing: Alone, - span: $DIR/attr-complex-fn.rs:21:1: 24:2 (#0), + span: $DIR/attr-complex-fn.rs:21:7: 21:8 (#0), }, Ident { ident: "MyTrait", - span: $DIR/attr-complex-fn.rs:21:1: 24:2 (#0), + span: $DIR/attr-complex-fn.rs:21:9: 21:16 (#0), }, Punct { ch: '<', spacing: Alone, - span: $DIR/attr-complex-fn.rs:21:1: 24:2 (#0), + span: $DIR/attr-complex-fn.rs:21:16: 21:17 (#0), }, Ident { ident: "T", - span: $DIR/attr-complex-fn.rs:21:1: 24:2 (#0), + span: $DIR/attr-complex-fn.rs:21:17: 21:18 (#0), }, Punct { ch: '>', spacing: Alone, - span: $DIR/attr-complex-fn.rs:21:1: 24:2 (#0), + span: $DIR/attr-complex-fn.rs:21:18: 21:19 (#0), }, Ident { ident: "for", - span: $DIR/attr-complex-fn.rs:21:1: 24:2 (#0), + span: $DIR/attr-complex-fn.rs:21:20: 21:23 (#0), }, Ident { ident: "MyStruct", - span: $DIR/attr-complex-fn.rs:21:1: 24:2 (#0), + span: $DIR/attr-complex-fn.rs:21:24: 21:32 (#0), }, Punct { ch: '<', spacing: Alone, - span: $DIR/attr-complex-fn.rs:21:1: 24:2 (#0), + span: $DIR/attr-complex-fn.rs:21:32: 21:33 (#0), }, Group { delimiter: Brace, stream: TokenStream [ Ident { ident: "true", - span: $DIR/attr-complex-fn.rs:21:1: 24:2 (#0), + span: $DIR/attr-complex-fn.rs:21:34: 21:38 (#0), }, ], - span: $DIR/attr-complex-fn.rs:21:1: 24:2 (#0), + span: $DIR/attr-complex-fn.rs:21:33: 21:39 (#0), }, Punct { ch: '>', spacing: Alone, - span: $DIR/attr-complex-fn.rs:21:1: 24:2 (#0), + span: $DIR/attr-complex-fn.rs:21:39: 21:40 (#0), }, Group { delimiter: Brace, @@ -148,24 +148,24 @@ PRINT-ATTR INPUT (DEBUG): TokenStream [ Punct { ch: '#', spacing: Joint, - span: $DIR/attr-complex-fn.rs:21:1: 24:2 (#0), + span: $DIR/attr-complex-fn.rs:23:5: 23:6 (#0), }, Punct { ch: '!', spacing: Alone, - span: $DIR/attr-complex-fn.rs:21:1: 24:2 (#0), + span: $DIR/attr-complex-fn.rs:23:6: 23:7 (#0), }, Group { delimiter: Bracket, stream: TokenStream [ Ident { ident: "rustc_dummy", - span: $DIR/attr-complex-fn.rs:21:1: 24:2 (#0), + span: $DIR/attr-complex-fn.rs:23:8: 23:19 (#0), }, ], - span: $DIR/attr-complex-fn.rs:21:1: 24:2 (#0), + span: $DIR/attr-complex-fn.rs:23:7: 23:20 (#0), }, ], - span: $DIR/attr-complex-fn.rs:21:1: 24:2 (#0), + span: $DIR/attr-complex-fn.rs:21:41: 24:2 (#0), }, ] diff --git a/src/test/ui/proc-macro/attribute-after-derive.stdout b/src/test/ui/proc-macro/attribute-after-derive.stdout index 11f492353271a..4c48e41ff338b 100644 --- a/src/test/ui/proc-macro/attribute-after-derive.stdout +++ b/src/test/ui/proc-macro/attribute-after-derive.stdout @@ -87,32 +87,16 @@ PRINT-DERIVE INPUT (DISPLAY): struct AttributeDerive { } PRINT-DERIVE INPUT (DEBUG): TokenStream [ Ident { ident: "struct", - span: $DIR/attribute-after-derive.rs:18:1: 21:2 (#0), + span: $DIR/attribute-after-derive.rs:18:1: 18:7 (#0), }, Ident { ident: "AttributeDerive", - span: $DIR/attribute-after-derive.rs:18:1: 21:2 (#0), - }, - Group { - delimiter: Brace, - stream: TokenStream [], - span: $DIR/attribute-after-derive.rs:18:1: 21:2 (#0), - }, -] -PRINT-ATTR INPUT (DISPLAY): struct DeriveAttribute { } -PRINT-ATTR INPUT (DEBUG): TokenStream [ - Ident { - ident: "struct", - span: $DIR/attribute-after-derive.rs:25:1: 28:2 (#0), - }, - Ident { - ident: "DeriveAttribute", - span: $DIR/attribute-after-derive.rs:25:1: 28:2 (#0), + span: $DIR/attribute-after-derive.rs:18:8: 18:23 (#0), }, Group { delimiter: Brace, stream: TokenStream [], - span: $DIR/attribute-after-derive.rs:25:1: 28:2 (#0), + span: $DIR/attribute-after-derive.rs:18:24: 21:2 (#0), }, ] PRINT-DERIVE INPUT (DISPLAY): #[print_attr] struct DeriveAttribute { } @@ -120,29 +104,45 @@ PRINT-DERIVE INPUT (DEBUG): TokenStream [ Punct { ch: '#', spacing: Alone, - span: $DIR/attribute-after-derive.rs:25:1: 28:2 (#0), + span: $DIR/attribute-after-derive.rs:24:1: 24:2 (#0), }, Group { delimiter: Bracket, stream: TokenStream [ Ident { ident: "print_attr", - span: $DIR/attribute-after-derive.rs:25:1: 28:2 (#0), + span: $DIR/attribute-after-derive.rs:24:3: 24:13 (#0), }, ], - span: $DIR/attribute-after-derive.rs:25:1: 28:2 (#0), + span: $DIR/attribute-after-derive.rs:24:2: 24:14 (#0), + }, + Ident { + ident: "struct", + span: $DIR/attribute-after-derive.rs:25:1: 25:7 (#0), + }, + Ident { + ident: "DeriveAttribute", + span: $DIR/attribute-after-derive.rs:25:8: 25:23 (#0), }, + Group { + delimiter: Brace, + stream: TokenStream [], + span: $DIR/attribute-after-derive.rs:25:24: 28:2 (#0), + }, +] +PRINT-ATTR INPUT (DISPLAY): struct DeriveAttribute { } +PRINT-ATTR INPUT (DEBUG): TokenStream [ Ident { ident: "struct", - span: $DIR/attribute-after-derive.rs:25:1: 28:2 (#0), + span: $DIR/attribute-after-derive.rs:25:1: 25:7 (#0), }, Ident { ident: "DeriveAttribute", - span: $DIR/attribute-after-derive.rs:25:1: 28:2 (#0), + span: $DIR/attribute-after-derive.rs:25:8: 25:23 (#0), }, Group { delimiter: Brace, stream: TokenStream [], - span: $DIR/attribute-after-derive.rs:25:1: 28:2 (#0), + span: $DIR/attribute-after-derive.rs:25:24: 28:2 (#0), }, ] diff --git a/src/test/ui/proc-macro/auxiliary/issue-75801.rs b/src/test/ui/proc-macro/auxiliary/issue-75801.rs new file mode 100644 index 0000000000000..d6c031d7d4f7e --- /dev/null +++ b/src/test/ui/proc-macro/auxiliary/issue-75801.rs @@ -0,0 +1,13 @@ +// force-host +// no-prefer-dynamic + +#![crate_type = "proc-macro"] + +extern crate proc_macro; + +use proc_macro::TokenStream; + +#[proc_macro_attribute] +pub fn foo(_args: TokenStream, item: TokenStream) -> TokenStream { + item +} diff --git a/src/test/ui/proc-macro/auxiliary/issue-79825.rs b/src/test/ui/proc-macro/auxiliary/issue-79825.rs new file mode 100644 index 0000000000000..930891b1d43f9 --- /dev/null +++ b/src/test/ui/proc-macro/auxiliary/issue-79825.rs @@ -0,0 +1,14 @@ +// force-host +// no-prefer-dynamic +#![crate_type = "proc-macro"] + +extern crate proc_macro; + +use proc_macro::TokenStream; + +#[proc_macro_attribute] +pub fn assert_input(args: TokenStream, input: TokenStream) -> TokenStream { + assert_eq!(input.to_string(), "trait Alias = Sized ;"); + assert!(args.is_empty()); + TokenStream::new() +} diff --git a/src/test/ui/proc-macro/auxiliary/issue-83510.rs b/src/test/ui/proc-macro/auxiliary/issue-83510.rs new file mode 100644 index 0000000000000..1d6ef3914a91b --- /dev/null +++ b/src/test/ui/proc-macro/auxiliary/issue-83510.rs @@ -0,0 +1,19 @@ +// force-host +// no-prefer-dynamic + +#![crate_type = "proc-macro"] + +extern crate proc_macro; + +use proc_macro::TokenStream; + +#[proc_macro] +pub fn dance_like_you_want_to_ice(_: TokenStream) -> TokenStream { + r#" + impl Foo { + type Bar = Box<()> + Baz; + } + "# + .parse() + .unwrap() +} diff --git a/src/test/ui/proc-macro/auxiliary/multiple-derives.rs b/src/test/ui/proc-macro/auxiliary/multiple-derives.rs new file mode 100644 index 0000000000000..e3f6607b2ae8d --- /dev/null +++ b/src/test/ui/proc-macro/auxiliary/multiple-derives.rs @@ -0,0 +1,22 @@ +// force-host +// no-prefer-dynamic + +#![crate_type = "proc-macro"] + +extern crate proc_macro; + +use proc_macro::TokenStream; + +macro_rules! make_derives { + ($($name:ident),*) => { + $( + #[proc_macro_derive($name)] + pub fn $name(input: TokenStream) -> TokenStream { + println!("Derive {}: {}", stringify!($name), input); + TokenStream::new() + } + )* + } +} + +make_derives!(First, Second, Third, Fourth, Fifth); diff --git a/src/test/ui/proc-macro/auxiliary/nested-macro-rules.rs b/src/test/ui/proc-macro/auxiliary/nested-macro-rules.rs index 52ebe8e7fb2ef..27676a5cb8112 100644 --- a/src/test/ui/proc-macro/auxiliary/nested-macro-rules.rs +++ b/src/test/ui/proc-macro/auxiliary/nested-macro-rules.rs @@ -2,14 +2,15 @@ pub struct FirstStruct; #[macro_export] macro_rules! outer_macro { - ($name:ident) => { + ($name:ident, $attr_struct_name:ident) => { #[macro_export] macro_rules! inner_macro { - ($wrapper:ident) => { - $wrapper!($name) + ($bang_macro:ident, $attr_macro:ident) => { + $bang_macro!($name); + #[$attr_macro] struct $attr_struct_name {} } } } } -outer_macro!(FirstStruct); +outer_macro!(FirstStruct, FirstAttrStruct); diff --git a/src/test/ui/proc-macro/auxiliary/nonterminal-recollect-attr.rs b/src/test/ui/proc-macro/auxiliary/nonterminal-recollect-attr.rs index a6903283aa108..ea5ff466570e6 100644 --- a/src/test/ui/proc-macro/auxiliary/nonterminal-recollect-attr.rs +++ b/src/test/ui/proc-macro/auxiliary/nonterminal-recollect-attr.rs @@ -10,6 +10,7 @@ use proc_macro::{TokenStream, quote}; #[proc_macro_attribute] pub fn first_attr(_: TokenStream, input: TokenStream) -> TokenStream { let recollected: TokenStream = input.into_iter().collect(); + println!("First recollected: {:#?}", recollected); quote! { #[second_attr] $recollected @@ -18,6 +19,7 @@ pub fn first_attr(_: TokenStream, input: TokenStream) -> TokenStream { #[proc_macro_attribute] pub fn second_attr(_: TokenStream, input: TokenStream) -> TokenStream { - let _recollected: TokenStream = input.into_iter().collect(); + let recollected: TokenStream = input.into_iter().collect(); + println!("Second recollected: {:#?}", recollected); TokenStream::new() } diff --git a/src/test/ui/proc-macro/auxiliary/test-macros.rs b/src/test/ui/proc-macro/auxiliary/test-macros.rs index 1a5003616400f..7a46aee462b7d 100644 --- a/src/test/ui/proc-macro/auxiliary/test-macros.rs +++ b/src/test/ui/proc-macro/auxiliary/test-macros.rs @@ -83,21 +83,52 @@ fn print_helper(input: TokenStream, kind: &str) -> TokenStream { print_helper_ext(input, kind, true) } +fn deep_recollect(input: TokenStream) -> TokenStream { + input.into_iter().map(|tree| { + match tree { + TokenTree::Group(group) => { + let inner = deep_recollect(group.stream()); + let mut new_group = TokenTree::Group( + proc_macro::Group::new(group.delimiter(), inner) + ); + new_group.set_span(group.span()); + new_group + } + _ => tree, + } + }).collect() +} + fn print_helper_ext(input: TokenStream, kind: &str, debug: bool) -> TokenStream { let input_display = format!("{}", input); let input_debug = format!("{:#?}", input); - let recollected = input.into_iter().collect(); + let recollected = input.clone().into_iter().collect(); let recollected_display = format!("{}", recollected); let recollected_debug = format!("{:#?}", recollected); + + let deep_recollected = deep_recollect(input); + let deep_recollected_display = format!("{}", deep_recollected); + let deep_recollected_debug = format!("{:#?}", deep_recollected); + + + println!("PRINT-{} INPUT (DISPLAY): {}", kind, input_display); if recollected_display != input_display { println!("PRINT-{} RE-COLLECTED (DISPLAY): {}", kind, recollected_display); } + + if deep_recollected_display != recollected_display { + println!("PRINT-{} DEEP-RE-COLLECTED (DISPLAY): {}", kind, deep_recollected_display); + } + if debug { println!("PRINT-{} INPUT (DEBUG): {}", kind, input_debug); if recollected_debug != input_debug { println!("PRINT-{} RE-COLLECTED (DEBUG): {}", kind, recollected_debug); } + if deep_recollected_debug != recollected_debug { + println!("PRINT-{} DEEP-RE-COLLETED (DEBUG): {}", kind, deep_recollected_debug); + } } recollected } diff --git a/src/test/ui/proc-macro/cfg-eval-fail.rs b/src/test/ui/proc-macro/cfg-eval-fail.rs new file mode 100644 index 0000000000000..379491f3126b0 --- /dev/null +++ b/src/test/ui/proc-macro/cfg-eval-fail.rs @@ -0,0 +1,9 @@ +#![feature(cfg_eval)] +#![feature(stmt_expr_attributes)] + +fn main() { + let _ = #[cfg_eval] #[cfg(FALSE)] 0; + //~^ ERROR removing an expression is not supported in this position + //~| ERROR removing an expression is not supported in this position + //~| ERROR removing an expression is not supported in this position +} diff --git a/src/test/ui/proc-macro/cfg-eval-fail.stderr b/src/test/ui/proc-macro/cfg-eval-fail.stderr new file mode 100644 index 0000000000000..010ac006b0bee --- /dev/null +++ b/src/test/ui/proc-macro/cfg-eval-fail.stderr @@ -0,0 +1,20 @@ +error: removing an expression is not supported in this position + --> $DIR/cfg-eval-fail.rs:5:25 + | +LL | let _ = #[cfg_eval] #[cfg(FALSE)] 0; + | ^^^^^^^^^^^^^ + +error: removing an expression is not supported in this position + --> $DIR/cfg-eval-fail.rs:5:25 + | +LL | let _ = #[cfg_eval] #[cfg(FALSE)] 0; + | ^^^^^^^^^^^^^ + +error: removing an expression is not supported in this position + --> $DIR/cfg-eval-fail.rs:5:25 + | +LL | let _ = #[cfg_eval] #[cfg(FALSE)] 0; + | ^^^^^^^^^^^^^ + +error: aborting due to 3 previous errors + diff --git a/src/test/ui/proc-macro/cfg-eval-inner.rs b/src/test/ui/proc-macro/cfg-eval-inner.rs new file mode 100644 index 0000000000000..5fd3ca0d163bc --- /dev/null +++ b/src/test/ui/proc-macro/cfg-eval-inner.rs @@ -0,0 +1,39 @@ +// compile-flags: -Z span-debug +// aux-build:test-macros.rs +// check-pass + +#![feature(cfg_eval)] +#![feature(custom_inner_attributes)] +#![feature(stmt_expr_attributes)] +#![feature(rustc_attrs)] + +#![no_std] // Don't load unnecessary hygiene information from std +extern crate std; + +#[macro_use] +extern crate test_macros; + +struct Foo<T>(T); + +impl Foo<[u8; { + #![cfg_attr(not(FALSE), rustc_dummy(cursed_inner))] + #![allow(unused)] + struct Inner { + field: [u8; { + #![cfg_attr(not(FALSE), rustc_dummy(another_cursed_inner))] + 1 + }] + } + + 0 +}]> { + #![cfg_eval] + #![print_attr] + #![cfg_attr(not(FALSE), rustc_dummy(evaluated_attr))] + + fn bar() { + #[cfg(FALSE)] let a = 1; + } +} + +fn main() {} diff --git a/src/test/ui/proc-macro/cfg-eval-inner.stdout b/src/test/ui/proc-macro/cfg-eval-inner.stdout new file mode 100644 index 0000000000000..1f2b003958908 --- /dev/null +++ b/src/test/ui/proc-macro/cfg-eval-inner.stdout @@ -0,0 +1,247 @@ +PRINT-ATTR INPUT (DISPLAY): impl Foo < +[u8 ; + { + # ! [rustc_dummy(cursed_inner)] # ! [allow(unused)] struct Inner + { field : [u8 ; { # ! [rustc_dummy(another_cursed_inner)] 1 }] } 0 + }] > { # ! [rustc_dummy(evaluated_attr)] fn bar() { } } +PRINT-ATTR INPUT (DEBUG): TokenStream [ + Ident { + ident: "impl", + span: $DIR/cfg-eval-inner.rs:18:1: 18:5 (#0), + }, + Ident { + ident: "Foo", + span: $DIR/cfg-eval-inner.rs:18:6: 18:9 (#0), + }, + Punct { + ch: '<', + spacing: Alone, + span: $DIR/cfg-eval-inner.rs:18:9: 18:10 (#0), + }, + Group { + delimiter: Bracket, + stream: TokenStream [ + Ident { + ident: "u8", + span: $DIR/cfg-eval-inner.rs:18:11: 18:13 (#0), + }, + Punct { + ch: ';', + spacing: Alone, + span: $DIR/cfg-eval-inner.rs:18:13: 18:14 (#0), + }, + Group { + delimiter: Brace, + stream: TokenStream [ + Punct { + ch: '#', + spacing: Alone, + span: $DIR/cfg-eval-inner.rs:19:5: 19:6 (#0), + }, + Punct { + ch: '!', + spacing: Alone, + span: $DIR/cfg-eval-inner.rs:19:6: 19:7 (#0), + }, + Group { + delimiter: Bracket, + stream: TokenStream [ + Ident { + ident: "rustc_dummy", + span: $DIR/cfg-eval-inner.rs:19:29: 19:40 (#0), + }, + Group { + delimiter: Parenthesis, + stream: TokenStream [ + Ident { + ident: "cursed_inner", + span: $DIR/cfg-eval-inner.rs:19:41: 19:53 (#0), + }, + ], + span: $DIR/cfg-eval-inner.rs:19:40: 19:54 (#0), + }, + ], + span: $DIR/cfg-eval-inner.rs:19:5: 19:6 (#0), + }, + Punct { + ch: '#', + spacing: Joint, + span: $DIR/cfg-eval-inner.rs:20:5: 20:6 (#0), + }, + Punct { + ch: '!', + spacing: Alone, + span: $DIR/cfg-eval-inner.rs:20:6: 20:7 (#0), + }, + Group { + delimiter: Bracket, + stream: TokenStream [ + Ident { + ident: "allow", + span: $DIR/cfg-eval-inner.rs:20:8: 20:13 (#0), + }, + Group { + delimiter: Parenthesis, + stream: TokenStream [ + Ident { + ident: "unused", + span: $DIR/cfg-eval-inner.rs:20:14: 20:20 (#0), + }, + ], + span: $DIR/cfg-eval-inner.rs:20:13: 20:21 (#0), + }, + ], + span: $DIR/cfg-eval-inner.rs:20:7: 20:22 (#0), + }, + Ident { + ident: "struct", + span: $DIR/cfg-eval-inner.rs:21:5: 21:11 (#0), + }, + Ident { + ident: "Inner", + span: $DIR/cfg-eval-inner.rs:21:12: 21:17 (#0), + }, + Group { + delimiter: Brace, + stream: TokenStream [ + Ident { + ident: "field", + span: $DIR/cfg-eval-inner.rs:22:9: 22:14 (#0), + }, + Punct { + ch: ':', + spacing: Alone, + span: $DIR/cfg-eval-inner.rs:22:14: 22:15 (#0), + }, + Group { + delimiter: Bracket, + stream: TokenStream [ + Ident { + ident: "u8", + span: $DIR/cfg-eval-inner.rs:22:17: 22:19 (#0), + }, + Punct { + ch: ';', + spacing: Alone, + span: $DIR/cfg-eval-inner.rs:22:19: 22:20 (#0), + }, + Group { + delimiter: Brace, + stream: TokenStream [ + Punct { + ch: '#', + spacing: Alone, + span: $DIR/cfg-eval-inner.rs:23:13: 23:14 (#0), + }, + Punct { + ch: '!', + spacing: Alone, + span: $DIR/cfg-eval-inner.rs:23:14: 23:15 (#0), + }, + Group { + delimiter: Bracket, + stream: TokenStream [ + Ident { + ident: "rustc_dummy", + span: $DIR/cfg-eval-inner.rs:23:37: 23:48 (#0), + }, + Group { + delimiter: Parenthesis, + stream: TokenStream [ + Ident { + ident: "another_cursed_inner", + span: $DIR/cfg-eval-inner.rs:23:49: 23:69 (#0), + }, + ], + span: $DIR/cfg-eval-inner.rs:23:48: 23:70 (#0), + }, + ], + span: $DIR/cfg-eval-inner.rs:23:13: 23:14 (#0), + }, + Literal { + kind: Integer, + symbol: "1", + suffix: None, + span: $DIR/cfg-eval-inner.rs:24:13: 24:14 (#0), + }, + ], + span: $DIR/cfg-eval-inner.rs:22:21: 25:10 (#0), + }, + ], + span: $DIR/cfg-eval-inner.rs:22:16: 25:11 (#0), + }, + ], + span: $DIR/cfg-eval-inner.rs:21:18: 26:6 (#0), + }, + Literal { + kind: Integer, + symbol: "0", + suffix: None, + span: $DIR/cfg-eval-inner.rs:28:5: 28:6 (#0), + }, + ], + span: $DIR/cfg-eval-inner.rs:18:15: 29:2 (#0), + }, + ], + span: $DIR/cfg-eval-inner.rs:18:10: 29:3 (#0), + }, + Punct { + ch: '>', + spacing: Alone, + span: $DIR/cfg-eval-inner.rs:29:3: 29:4 (#0), + }, + Group { + delimiter: Brace, + stream: TokenStream [ + Punct { + ch: '#', + spacing: Alone, + span: $DIR/cfg-eval-inner.rs:32:5: 32:6 (#0), + }, + Punct { + ch: '!', + spacing: Alone, + span: $DIR/cfg-eval-inner.rs:32:6: 32:7 (#0), + }, + Group { + delimiter: Bracket, + stream: TokenStream [ + Ident { + ident: "rustc_dummy", + span: $DIR/cfg-eval-inner.rs:32:29: 32:40 (#0), + }, + Group { + delimiter: Parenthesis, + stream: TokenStream [ + Ident { + ident: "evaluated_attr", + span: $DIR/cfg-eval-inner.rs:32:41: 32:55 (#0), + }, + ], + span: $DIR/cfg-eval-inner.rs:32:40: 32:56 (#0), + }, + ], + span: $DIR/cfg-eval-inner.rs:32:5: 32:6 (#0), + }, + Ident { + ident: "fn", + span: $DIR/cfg-eval-inner.rs:34:5: 34:7 (#0), + }, + Ident { + ident: "bar", + span: $DIR/cfg-eval-inner.rs:34:8: 34:11 (#0), + }, + Group { + delimiter: Parenthesis, + stream: TokenStream [], + span: $DIR/cfg-eval-inner.rs:34:11: 34:13 (#0), + }, + Group { + delimiter: Brace, + stream: TokenStream [], + span: $DIR/cfg-eval-inner.rs:34:14: 36:6 (#0), + }, + ], + span: $DIR/cfg-eval-inner.rs:29:5: 37:2 (#0), + }, +] diff --git a/src/test/ui/proc-macro/cfg-eval.rs b/src/test/ui/proc-macro/cfg-eval.rs new file mode 100644 index 0000000000000..fa6d015e48eb8 --- /dev/null +++ b/src/test/ui/proc-macro/cfg-eval.rs @@ -0,0 +1,37 @@ +// check-pass +// compile-flags: -Z span-debug +// aux-build:test-macros.rs + +#![feature(cfg_eval)] +#![feature(proc_macro_hygiene)] +#![feature(stmt_expr_attributes)] +#![feature(rustc_attrs)] +#![no_std] // Don't load unnecessary hygiene information from std +extern crate std; + +#[macro_use] +extern crate test_macros; + +#[cfg_eval] +#[print_attr] +struct S1 { + #[cfg(FALSE)] + field_false: u8, + #[cfg(all(/*true*/))] + #[cfg_attr(FALSE, unknown_attr)] + #[cfg_attr(all(/*true*/), allow())] + field_true: u8, +} + +#[cfg_eval] +#[cfg(FALSE)] +struct S2 {} + +fn main() { + // Subtle - we need a trailing comma after the '1' - otherwise, `#[cfg_eval]` will + // turn this into `(#[cfg(all())] 1)`, which is a parenthesized expression, not a tuple + // expression. `#[cfg]` is not supported inside parenthesized expressions, so this will + // produce an error when attribute collection runs. + let _ = #[cfg_eval] #[print_attr] #[cfg_attr(not(FALSE), rustc_dummy)] + (#[cfg(FALSE)] 0, #[cfg(all(/*true*/))] 1,); +} diff --git a/src/test/ui/proc-macro/cfg-eval.stdout b/src/test/ui/proc-macro/cfg-eval.stdout new file mode 100644 index 0000000000000..6732caf08dd76 --- /dev/null +++ b/src/test/ui/proc-macro/cfg-eval.stdout @@ -0,0 +1,150 @@ +PRINT-ATTR INPUT (DISPLAY): struct S1 { #[cfg(all())] #[allow()] field_true : u8, } +PRINT-ATTR INPUT (DEBUG): TokenStream [ + Ident { + ident: "struct", + span: $DIR/cfg-eval.rs:17:1: 17:7 (#0), + }, + Ident { + ident: "S1", + span: $DIR/cfg-eval.rs:17:8: 17:10 (#0), + }, + Group { + delimiter: Brace, + stream: TokenStream [ + Punct { + ch: '#', + spacing: Alone, + span: $DIR/cfg-eval.rs:20:5: 20:6 (#0), + }, + Group { + delimiter: Bracket, + stream: TokenStream [ + Ident { + ident: "cfg", + span: $DIR/cfg-eval.rs:20:7: 20:10 (#0), + }, + Group { + delimiter: Parenthesis, + stream: TokenStream [ + Ident { + ident: "all", + span: $DIR/cfg-eval.rs:20:11: 20:14 (#0), + }, + Group { + delimiter: Parenthesis, + stream: TokenStream [], + span: $DIR/cfg-eval.rs:20:14: 20:24 (#0), + }, + ], + span: $DIR/cfg-eval.rs:20:10: 20:25 (#0), + }, + ], + span: $DIR/cfg-eval.rs:20:6: 20:26 (#0), + }, + Punct { + ch: '#', + spacing: Alone, + span: $DIR/cfg-eval.rs:22:5: 22:6 (#0), + }, + Group { + delimiter: Bracket, + stream: TokenStream [ + Ident { + ident: "allow", + span: $DIR/cfg-eval.rs:22:31: 22:36 (#0), + }, + Group { + delimiter: Parenthesis, + stream: TokenStream [], + span: $DIR/cfg-eval.rs:22:36: 22:38 (#0), + }, + ], + span: $DIR/cfg-eval.rs:22:5: 22:6 (#0), + }, + Ident { + ident: "field_true", + span: $DIR/cfg-eval.rs:23:5: 23:15 (#0), + }, + Punct { + ch: ':', + spacing: Alone, + span: $DIR/cfg-eval.rs:23:15: 23:16 (#0), + }, + Ident { + ident: "u8", + span: $DIR/cfg-eval.rs:23:17: 23:19 (#0), + }, + Punct { + ch: ',', + spacing: Alone, + span: $DIR/cfg-eval.rs:23:19: 23:20 (#0), + }, + ], + span: $DIR/cfg-eval.rs:17:11: 24:2 (#0), + }, +] +PRINT-ATTR INPUT (DISPLAY): #[rustc_dummy] (#[cfg(all())] 1,) +PRINT-ATTR INPUT (DEBUG): TokenStream [ + Punct { + ch: '#', + spacing: Alone, + span: $DIR/cfg-eval.rs:35:39: 35:40 (#0), + }, + Group { + delimiter: Bracket, + stream: TokenStream [ + Ident { + ident: "rustc_dummy", + span: $DIR/cfg-eval.rs:35:62: 35:73 (#0), + }, + ], + span: $DIR/cfg-eval.rs:35:39: 35:40 (#0), + }, + Group { + delimiter: Parenthesis, + stream: TokenStream [ + Punct { + ch: '#', + spacing: Alone, + span: $DIR/cfg-eval.rs:36:23: 36:24 (#0), + }, + Group { + delimiter: Bracket, + stream: TokenStream [ + Ident { + ident: "cfg", + span: $DIR/cfg-eval.rs:36:25: 36:28 (#0), + }, + Group { + delimiter: Parenthesis, + stream: TokenStream [ + Ident { + ident: "all", + span: $DIR/cfg-eval.rs:36:29: 36:32 (#0), + }, + Group { + delimiter: Parenthesis, + stream: TokenStream [], + span: $DIR/cfg-eval.rs:36:32: 36:42 (#0), + }, + ], + span: $DIR/cfg-eval.rs:36:28: 36:43 (#0), + }, + ], + span: $DIR/cfg-eval.rs:36:24: 36:44 (#0), + }, + Literal { + kind: Integer, + symbol: "1", + suffix: None, + span: $DIR/cfg-eval.rs:36:45: 36:46 (#0), + }, + Punct { + ch: ',', + spacing: Alone, + span: $DIR/cfg-eval.rs:36:46: 36:47 (#0), + }, + ], + span: $DIR/cfg-eval.rs:36:5: 36:48 (#0), + }, +] diff --git a/src/test/ui/proc-macro/derive-expand-order.rs b/src/test/ui/proc-macro/derive-expand-order.rs new file mode 100644 index 0000000000000..0cf1ceb91dea1 --- /dev/null +++ b/src/test/ui/proc-macro/derive-expand-order.rs @@ -0,0 +1,14 @@ +// run-pass +// aux-build:multiple-derives.rs + +extern crate multiple_derives; + +use multiple_derives::*; + +#[derive(First)] +#[derive(Second)] +#[derive(Third, Fourth)] +#[derive(Fifth)] +pub struct Foo {} + +fn main() {} diff --git a/src/test/ui/proc-macro/derive-expand-order.stdout b/src/test/ui/proc-macro/derive-expand-order.stdout new file mode 100644 index 0000000000000..3ac1adf92c24f --- /dev/null +++ b/src/test/ui/proc-macro/derive-expand-order.stdout @@ -0,0 +1,5 @@ +Derive First: #[derive(Second)] #[derive(Third, Fourth)] #[derive(Fifth)] pub struct Foo { } +Derive Second: #[derive(Third, Fourth)] #[derive(Fifth)] pub struct Foo { } +Derive Third: #[derive(Fifth)] pub struct Foo { } +Derive Fourth: #[derive(Fifth)] pub struct Foo { } +Derive Fifth: pub struct Foo { } diff --git a/src/test/ui/proc-macro/expand-to-derive.stdout b/src/test/ui/proc-macro/expand-to-derive.stdout index 7eb68643342c8..a6437982a3737 100644 --- a/src/test/ui/proc-macro/expand-to-derive.stdout +++ b/src/test/ui/proc-macro/expand-to-derive.stdout @@ -1,40 +1,40 @@ PRINT-DERIVE INPUT (DISPLAY): struct Foo { field : - [bool ; { #[rustc_dummy] struct Inner { other_inner_field : u8, } 0 }], + [bool ; { #[rustc_dummy] struct Inner { other_inner_field : u8, } 0 }] } PRINT-DERIVE INPUT (DEBUG): TokenStream [ Ident { ident: "struct", - span: $DIR/expand-to-derive.rs:16:9: 22:10 (#4), + span: $DIR/expand-to-derive.rs:16:9: 16:15 (#4), }, Ident { ident: "Foo", - span: $DIR/expand-to-derive.rs:16:9: 22:10 (#4), + span: $DIR/expand-to-derive.rs:16:16: 16:19 (#4), }, Group { delimiter: Brace, stream: TokenStream [ Ident { ident: "field", - span: $DIR/expand-to-derive.rs:16:9: 22:10 (#4), + span: $DIR/expand-to-derive.rs:18:13: 18:18 (#4), }, Punct { ch: ':', spacing: Alone, - span: $DIR/expand-to-derive.rs:16:9: 22:10 (#4), + span: $DIR/expand-to-derive.rs:18:18: 18:19 (#4), }, Group { delimiter: Bracket, stream: TokenStream [ Ident { ident: "bool", - span: $DIR/expand-to-derive.rs:16:9: 22:10 (#4), + span: $DIR/expand-to-derive.rs:18:21: 18:25 (#4), }, Punct { ch: ';', spacing: Alone, - span: $DIR/expand-to-derive.rs:16:9: 22:10 (#4), + span: $DIR/expand-to-derive.rs:18:25: 18:26 (#4), }, Group { delimiter: Brace, @@ -42,68 +42,63 @@ PRINT-DERIVE INPUT (DEBUG): TokenStream [ Punct { ch: '#', spacing: Alone, - span: $DIR/expand-to-derive.rs:16:9: 22:10 (#4), + span: $DIR/expand-to-derive.rs:27:5: 27:6 (#0), }, Group { delimiter: Bracket, stream: TokenStream [ Ident { ident: "rustc_dummy", - span: $DIR/expand-to-derive.rs:16:9: 22:10 (#4), + span: $DIR/expand-to-derive.rs:27:28: 27:39 (#0), }, ], - span: $DIR/expand-to-derive.rs:16:9: 22:10 (#4), + span: $DIR/expand-to-derive.rs:27:5: 27:6 (#0), }, Ident { ident: "struct", - span: $DIR/expand-to-derive.rs:16:9: 22:10 (#4), + span: $DIR/expand-to-derive.rs:28:5: 28:11 (#0), }, Ident { ident: "Inner", - span: $DIR/expand-to-derive.rs:16:9: 22:10 (#4), + span: $DIR/expand-to-derive.rs:28:12: 28:17 (#0), }, Group { delimiter: Brace, stream: TokenStream [ Ident { ident: "other_inner_field", - span: $DIR/expand-to-derive.rs:16:9: 22:10 (#4), + span: $DIR/expand-to-derive.rs:30:9: 30:26 (#0), }, Punct { ch: ':', spacing: Alone, - span: $DIR/expand-to-derive.rs:16:9: 22:10 (#4), + span: $DIR/expand-to-derive.rs:30:26: 30:27 (#0), }, Ident { ident: "u8", - span: $DIR/expand-to-derive.rs:16:9: 22:10 (#4), + span: $DIR/expand-to-derive.rs:30:28: 30:30 (#0), }, Punct { ch: ',', spacing: Alone, - span: $DIR/expand-to-derive.rs:16:9: 22:10 (#4), + span: $DIR/expand-to-derive.rs:30:30: 30:31 (#0), }, ], - span: $DIR/expand-to-derive.rs:16:9: 22:10 (#4), + span: $DIR/expand-to-derive.rs:28:18: 31:6 (#0), }, Literal { kind: Integer, symbol: "0", suffix: None, - span: $DIR/expand-to-derive.rs:16:9: 22:10 (#4), + span: $DIR/expand-to-derive.rs:20:17: 20:18 (#4), }, ], - span: $DIR/expand-to-derive.rs:16:9: 22:10 (#4), + span: $DIR/expand-to-derive.rs:18:27: 21:14 (#4), }, ], - span: $DIR/expand-to-derive.rs:16:9: 22:10 (#4), - }, - Punct { - ch: ',', - spacing: Alone, - span: $DIR/expand-to-derive.rs:16:9: 22:10 (#4), + span: $DIR/expand-to-derive.rs:18:20: 21:15 (#4), }, ], - span: $DIR/expand-to-derive.rs:16:9: 22:10 (#4), + span: $DIR/expand-to-derive.rs:16:20: 22:10 (#4), }, ] diff --git a/src/test/ui/proc-macro/generate-mod.stderr b/src/test/ui/proc-macro/generate-mod.stderr index 9b946b5e2449e..5a4ed65ecdc8f 100644 --- a/src/test/ui/proc-macro/generate-mod.stderr +++ b/src/test/ui/proc-macro/generate-mod.stderr @@ -46,7 +46,8 @@ LL | #[derive(generate_mod::CheckDerive)] | = note: `#[warn(proc_macro_derive_resolution_fallback)]` on by default = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = note: for more information, see issue #50504 <https://github.com/rust-lang/rust/issues/50504> + = note: for more information, see issue #83583 <https://github.com/rust-lang/rust/issues/83583> + = note: this warning originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info) warning: cannot find type `OuterDerive` in this scope --> $DIR/generate-mod.rs:16:10 @@ -55,7 +56,8 @@ LL | #[derive(generate_mod::CheckDerive)] | ^^^^^^^^^^^^^^^^^^^^^^^^^ names from parent modules are not accessible without an explicit import | = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = note: for more information, see issue #50504 <https://github.com/rust-lang/rust/issues/50504> + = note: for more information, see issue #83583 <https://github.com/rust-lang/rust/issues/83583> + = note: this warning originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info) warning: cannot find type `FromOutside` in this scope --> $DIR/generate-mod.rs:23:14 @@ -64,7 +66,8 @@ LL | #[derive(generate_mod::CheckDerive)] | ^^^^^^^^^^^^^^^^^^^^^^^^^ names from parent modules are not accessible without an explicit import | = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = note: for more information, see issue #50504 <https://github.com/rust-lang/rust/issues/50504> + = note: for more information, see issue #83583 <https://github.com/rust-lang/rust/issues/83583> + = note: this warning originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info) warning: cannot find type `OuterDerive` in this scope --> $DIR/generate-mod.rs:23:14 @@ -73,7 +76,8 @@ LL | #[derive(generate_mod::CheckDerive)] | ^^^^^^^^^^^^^^^^^^^^^^^^^ names from parent modules are not accessible without an explicit import | = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = note: for more information, see issue #50504 <https://github.com/rust-lang/rust/issues/50504> + = note: for more information, see issue #83583 <https://github.com/rust-lang/rust/issues/83583> + = note: this warning originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info) error: aborting due to 4 previous errors; 4 warnings emitted diff --git a/src/test/ui/proc-macro/group-compat-hack/auxiliary/group-compat-hack.rs b/src/test/ui/proc-macro/group-compat-hack/auxiliary/group-compat-hack.rs deleted file mode 100644 index 5cd3b40a2e42a..0000000000000 --- a/src/test/ui/proc-macro/group-compat-hack/auxiliary/group-compat-hack.rs +++ /dev/null @@ -1,13 +0,0 @@ -// force-host -// no-prefer-dynamic - -#![crate_type = "proc-macro"] - -extern crate proc_macro; -use proc_macro::TokenStream; - -#[proc_macro_attribute] -pub fn my_macro(_attr: TokenStream, input: TokenStream) -> TokenStream { - println!("Called proc_macro_hack with {:?}", input); - input -} diff --git a/src/test/ui/proc-macro/group-compat-hack/auxiliary/pin-project-internal-0.4.0.rs b/src/test/ui/proc-macro/group-compat-hack/auxiliary/pin-project-internal-0.4.0.rs new file mode 100644 index 0000000000000..baa4fd3a10559 --- /dev/null +++ b/src/test/ui/proc-macro/group-compat-hack/auxiliary/pin-project-internal-0.4.0.rs @@ -0,0 +1,17 @@ +// force-host +// no-prefer-dynamic + +#![crate_type = "proc-macro"] +#![crate_name = "group_compat_hack"] + +// This file has an unusual name in order to trigger the back-compat +// code in the compiler + +extern crate proc_macro; +use proc_macro::TokenStream; + +#[proc_macro_attribute] +pub fn my_macro(_attr: TokenStream, input: TokenStream) -> TokenStream { + println!("Called proc_macro_hack with {:?}", input); + input +} diff --git a/src/test/ui/proc-macro/group-compat-hack/group-compat-hack.rs b/src/test/ui/proc-macro/group-compat-hack/group-compat-hack.rs index 652fabf34ac3d..2b742771d6f2c 100644 --- a/src/test/ui/proc-macro/group-compat-hack/group-compat-hack.rs +++ b/src/test/ui/proc-macro/group-compat-hack/group-compat-hack.rs @@ -1,5 +1,5 @@ // check-pass -// aux-build:group-compat-hack.rs +// aux-build:pin-project-internal-0.4.0.rs // compile-flags: -Z span-debug #![no_std] // Don't load unnecessary hygiene information from std @@ -24,7 +24,8 @@ mod no_version { } struct Foo; - impl_macros!(Foo); + impl_macros!(Foo); //~ WARN using an old version + //~| WARN this was previously arrays!(Foo); other!(Foo); } @@ -40,8 +41,10 @@ mod with_version { } struct Foo; - impl_macros!(Foo); - arrays!(Foo); + impl_macros!(Foo); //~ WARN using an old version + //~| WARN this was previously + arrays!(Foo); //~ WARN using an old version + //~| WARN this was previously other!(Foo); } @@ -49,14 +52,16 @@ mod actix_web_test { include!("actix-web/src/extract.rs"); struct Foo; - tuple_from_req!(Foo); + tuple_from_req!(Foo); //~ WARN using an old version + //~| WARN this was previously } mod actix_web_version_test { include!("actix-web-2.0.0/src/extract.rs"); struct Foo; - tuple_from_req!(Foo); + tuple_from_req!(Foo); //~ WARN using an old version + //~| WARN this was previously } mod actori_web_test { @@ -73,5 +78,11 @@ mod actori_web_version_test { tuple_from_req!(Foo); } +mod with_good_js_sys_version { + include!("js-sys-0.3.40/src/lib.rs"); + struct Foo; + arrays!(Foo); +} + fn main() {} diff --git a/src/test/ui/proc-macro/group-compat-hack/group-compat-hack.stderr b/src/test/ui/proc-macro/group-compat-hack/group-compat-hack.stderr new file mode 100644 index 0000000000000..effcd68cf967c --- /dev/null +++ b/src/test/ui/proc-macro/group-compat-hack/group-compat-hack.stderr @@ -0,0 +1,169 @@ +warning: using an old version of `time-macros-impl` + --> $DIR/time-macros-impl/src/lib.rs:5:32 + | +LL | #[my_macro] struct One($name); + | ^^^^^ + | + ::: $DIR/group-compat-hack.rs:27:5 + | +LL | impl_macros!(Foo); + | ------------------ in this macro invocation + | + = note: `#[warn(proc_macro_back_compat)]` on by default + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #83125 <https://github.com/rust-lang/rust/issues/83125> + = note: the `time-macros-impl` crate will stop compiling in futures version of Rust. Please update to the latest version of the `time` crate to avoid breakage + = note: this warning originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) + +warning: using an old version of `time-macros-impl` + --> $DIR/time-macros-impl-0.1.0/src/lib.rs:5:32 + | +LL | #[my_macro] struct One($name); + | ^^^^^ + | + ::: $DIR/group-compat-hack.rs:44:5 + | +LL | impl_macros!(Foo); + | ------------------ in this macro invocation + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #83125 <https://github.com/rust-lang/rust/issues/83125> + = note: the `time-macros-impl` crate will stop compiling in futures version of Rust. Please update to the latest version of the `time` crate to avoid breakage + = note: this warning originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) + +warning: using an old version of `js-sys` + --> $DIR/js-sys-0.3.17/src/lib.rs:5:32 + | +LL | #[my_macro] struct Two($name); + | ^^^^^ + | + ::: $DIR/group-compat-hack.rs:46:5 + | +LL | arrays!(Foo); + | ------------- in this macro invocation + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #83125 <https://github.com/rust-lang/rust/issues/83125> + = note: older versions of the `js-sys` crate will stop compiling in future versions of Rust; please update to `js-sys` v0.3.40 or above + = note: this warning originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) + +warning: using an old version of `actix-web` + --> $DIR/actix-web/src/extract.rs:5:34 + | +LL | #[my_macro] struct Three($T); + | ^^ + | + ::: $DIR/group-compat-hack.rs:55:5 + | +LL | tuple_from_req!(Foo); + | --------------------- in this macro invocation + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #83125 <https://github.com/rust-lang/rust/issues/83125> + = note: the version of `actix-web` you are using might stop compiling in future versions of Rust; please update to the latest version of the `actix-web` crate to avoid breakage + = note: this warning originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) + +warning: using an old version of `actix-web` + --> $DIR/actix-web-2.0.0/src/extract.rs:5:34 + | +LL | #[my_macro] struct Three($T); + | ^^ + | + ::: $DIR/group-compat-hack.rs:63:5 + | +LL | tuple_from_req!(Foo); + | --------------------- in this macro invocation + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #83125 <https://github.com/rust-lang/rust/issues/83125> + = note: the version of `actix-web` you are using might stop compiling in future versions of Rust; please update to the latest version of the `actix-web` crate to avoid breakage + = note: this warning originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) + +warning: 5 warnings emitted + +Future incompatibility report: Future breakage date: None, diagnostic: +warning: using an old version of `time-macros-impl` + --> $DIR/time-macros-impl/src/lib.rs:5:32 + | +LL | #[my_macro] struct One($name); + | ^^^^^ + | + ::: $DIR/group-compat-hack.rs:27:5 + | +LL | impl_macros!(Foo); + | ------------------ in this macro invocation + | + = note: `#[warn(proc_macro_back_compat)]` on by default + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #83125 <https://github.com/rust-lang/rust/issues/83125> + = note: the `time-macros-impl` crate will stop compiling in futures version of Rust. Please update to the latest version of the `time` crate to avoid breakage + = note: this warning originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) + +Future breakage date: None, diagnostic: +warning: using an old version of `time-macros-impl` + --> $DIR/time-macros-impl-0.1.0/src/lib.rs:5:32 + | +LL | #[my_macro] struct One($name); + | ^^^^^ + | + ::: $DIR/group-compat-hack.rs:44:5 + | +LL | impl_macros!(Foo); + | ------------------ in this macro invocation + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #83125 <https://github.com/rust-lang/rust/issues/83125> + = note: the `time-macros-impl` crate will stop compiling in futures version of Rust. Please update to the latest version of the `time` crate to avoid breakage + = note: this warning originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) + +Future breakage date: None, diagnostic: +warning: using an old version of `js-sys` + --> $DIR/js-sys-0.3.17/src/lib.rs:5:32 + | +LL | #[my_macro] struct Two($name); + | ^^^^^ + | + ::: $DIR/group-compat-hack.rs:46:5 + | +LL | arrays!(Foo); + | ------------- in this macro invocation + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #83125 <https://github.com/rust-lang/rust/issues/83125> + = note: older versions of the `js-sys` crate will stop compiling in future versions of Rust; please update to `js-sys` v0.3.40 or above + = note: this warning originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) + +Future breakage date: None, diagnostic: +warning: using an old version of `actix-web` + --> $DIR/actix-web/src/extract.rs:5:34 + | +LL | #[my_macro] struct Three($T); + | ^^ + | + ::: $DIR/group-compat-hack.rs:55:5 + | +LL | tuple_from_req!(Foo); + | --------------------- in this macro invocation + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #83125 <https://github.com/rust-lang/rust/issues/83125> + = note: the version of `actix-web` you are using might stop compiling in future versions of Rust; please update to the latest version of the `actix-web` crate to avoid breakage + = note: this warning originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) + +Future breakage date: None, diagnostic: +warning: using an old version of `actix-web` + --> $DIR/actix-web-2.0.0/src/extract.rs:5:34 + | +LL | #[my_macro] struct Three($T); + | ^^ + | + ::: $DIR/group-compat-hack.rs:63:5 + | +LL | tuple_from_req!(Foo); + | --------------------- in this macro invocation + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #83125 <https://github.com/rust-lang/rust/issues/83125> + = note: the version of `actix-web` you are using might stop compiling in future versions of Rust; please update to the latest version of the `actix-web` crate to avoid breakage + = note: this warning originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) + diff --git a/src/test/ui/proc-macro/group-compat-hack/group-compat-hack.stdout b/src/test/ui/proc-macro/group-compat-hack/group-compat-hack.stdout index c6b18ab674baa..82d6bc33bf95b 100644 --- a/src/test/ui/proc-macro/group-compat-hack/group-compat-hack.stdout +++ b/src/test/ui/proc-macro/group-compat-hack/group-compat-hack.stdout @@ -1,10 +1,11 @@ Called proc_macro_hack with TokenStream [Ident { ident: "struct", span: $DIR/time-macros-impl/src/lib.rs:5:21: 5:27 (#6) }, Ident { ident: "One", span: $DIR/time-macros-impl/src/lib.rs:5:28: 5:31 (#6) }, Group { delimiter: Parenthesis, stream: TokenStream [Ident { ident: "Foo", span: $DIR/group-compat-hack.rs:27:18: 27:21 (#0) }], span: $DIR/time-macros-impl/src/lib.rs:5:31: 5:38 (#6) }, Punct { ch: ';', spacing: Alone, span: $DIR/time-macros-impl/src/lib.rs:5:38: 5:39 (#6) }] -Called proc_macro_hack with TokenStream [Ident { ident: "struct", span: $DIR/js-sys/src/lib.rs:5:21: 5:27 (#10) }, Ident { ident: "Two", span: $DIR/js-sys/src/lib.rs:5:28: 5:31 (#10) }, Group { delimiter: Parenthesis, stream: TokenStream [Ident { ident: "Foo", span: $DIR/group-compat-hack.rs:28:13: 28:16 (#0) }], span: $DIR/js-sys/src/lib.rs:5:31: 5:38 (#10) }, Punct { ch: ';', spacing: Alone, span: $DIR/js-sys/src/lib.rs:5:38: 5:39 (#10) }] -Called proc_macro_hack with TokenStream [Ident { ident: "struct", span: $DIR/group-compat-hack.rs:22:25: 22:31 (#14) }, Ident { ident: "Three", span: $DIR/group-compat-hack.rs:22:32: 22:37 (#14) }, Group { delimiter: Parenthesis, stream: TokenStream [Group { delimiter: None, stream: TokenStream [Ident { ident: "Foo", span: $DIR/group-compat-hack.rs:29:12: 29:15 (#0) }], span: $DIR/group-compat-hack.rs:22:38: 22:43 (#14) }], span: $DIR/group-compat-hack.rs:22:37: 22:44 (#14) }, Punct { ch: ';', spacing: Alone, span: $DIR/group-compat-hack.rs:22:44: 22:45 (#14) }] -Called proc_macro_hack with TokenStream [Ident { ident: "struct", span: $DIR/time-macros-impl-0.1.0/src/lib.rs:5:21: 5:27 (#20) }, Ident { ident: "One", span: $DIR/time-macros-impl-0.1.0/src/lib.rs:5:28: 5:31 (#20) }, Group { delimiter: Parenthesis, stream: TokenStream [Ident { ident: "Foo", span: $DIR/group-compat-hack.rs:43:18: 43:21 (#0) }], span: $DIR/time-macros-impl-0.1.0/src/lib.rs:5:31: 5:38 (#20) }, Punct { ch: ';', spacing: Alone, span: $DIR/time-macros-impl-0.1.0/src/lib.rs:5:38: 5:39 (#20) }] -Called proc_macro_hack with TokenStream [Ident { ident: "struct", span: $DIR/js-sys-0.3.17/src/lib.rs:5:21: 5:27 (#24) }, Ident { ident: "Two", span: $DIR/js-sys-0.3.17/src/lib.rs:5:28: 5:31 (#24) }, Group { delimiter: Parenthesis, stream: TokenStream [Ident { ident: "Foo", span: $DIR/group-compat-hack.rs:44:13: 44:16 (#0) }], span: $DIR/js-sys-0.3.17/src/lib.rs:5:31: 5:38 (#24) }, Punct { ch: ';', spacing: Alone, span: $DIR/js-sys-0.3.17/src/lib.rs:5:38: 5:39 (#24) }] -Called proc_macro_hack with TokenStream [Ident { ident: "struct", span: $DIR/group-compat-hack.rs:38:25: 38:31 (#28) }, Ident { ident: "Three", span: $DIR/group-compat-hack.rs:38:32: 38:37 (#28) }, Group { delimiter: Parenthesis, stream: TokenStream [Group { delimiter: None, stream: TokenStream [Ident { ident: "Foo", span: $DIR/group-compat-hack.rs:45:12: 45:15 (#0) }], span: $DIR/group-compat-hack.rs:38:38: 38:43 (#28) }], span: $DIR/group-compat-hack.rs:38:37: 38:44 (#28) }, Punct { ch: ';', spacing: Alone, span: $DIR/group-compat-hack.rs:38:44: 38:45 (#28) }] -Called proc_macro_hack with TokenStream [Ident { ident: "struct", span: $DIR/actix-web/src/extract.rs:5:21: 5:27 (#33) }, Ident { ident: "Three", span: $DIR/actix-web/src/extract.rs:5:28: 5:33 (#33) }, Group { delimiter: Parenthesis, stream: TokenStream [Ident { ident: "Foo", span: $DIR/group-compat-hack.rs:52:21: 52:24 (#0) }], span: $DIR/actix-web/src/extract.rs:5:33: 5:37 (#33) }, Punct { ch: ';', spacing: Alone, span: $DIR/actix-web/src/extract.rs:5:37: 5:38 (#33) }] -Called proc_macro_hack with TokenStream [Ident { ident: "struct", span: $DIR/actix-web-2.0.0/src/extract.rs:5:21: 5:27 (#38) }, Ident { ident: "Three", span: $DIR/actix-web-2.0.0/src/extract.rs:5:28: 5:33 (#38) }, Group { delimiter: Parenthesis, stream: TokenStream [Ident { ident: "Foo", span: $DIR/group-compat-hack.rs:59:21: 59:24 (#0) }], span: $DIR/actix-web-2.0.0/src/extract.rs:5:33: 5:37 (#38) }, Punct { ch: ';', spacing: Alone, span: $DIR/actix-web-2.0.0/src/extract.rs:5:37: 5:38 (#38) }] -Called proc_macro_hack with TokenStream [Ident { ident: "struct", span: $DIR/actori-web/src/extract.rs:5:21: 5:27 (#43) }, Ident { ident: "Four", span: $DIR/actori-web/src/extract.rs:5:28: 5:32 (#43) }, Group { delimiter: Parenthesis, stream: TokenStream [Ident { ident: "Foo", span: $DIR/group-compat-hack.rs:66:21: 66:24 (#0) }], span: $DIR/actori-web/src/extract.rs:5:32: 5:36 (#43) }, Punct { ch: ';', spacing: Alone, span: $DIR/actori-web/src/extract.rs:5:36: 5:37 (#43) }] -Called proc_macro_hack with TokenStream [Ident { ident: "struct", span: $DIR/actori-web-2.0.0/src/extract.rs:5:21: 5:27 (#48) }, Ident { ident: "Four", span: $DIR/actori-web-2.0.0/src/extract.rs:5:28: 5:32 (#48) }, Group { delimiter: Parenthesis, stream: TokenStream [Ident { ident: "Foo", span: $DIR/group-compat-hack.rs:73:21: 73:24 (#0) }], span: $DIR/actori-web-2.0.0/src/extract.rs:5:32: 5:36 (#48) }, Punct { ch: ';', spacing: Alone, span: $DIR/actori-web-2.0.0/src/extract.rs:5:36: 5:37 (#48) }] +Called proc_macro_hack with TokenStream [Ident { ident: "struct", span: $DIR/js-sys/src/lib.rs:5:21: 5:27 (#10) }, Ident { ident: "Two", span: $DIR/js-sys/src/lib.rs:5:28: 5:31 (#10) }, Group { delimiter: Parenthesis, stream: TokenStream [Group { delimiter: None, stream: TokenStream [Ident { ident: "Foo", span: $DIR/group-compat-hack.rs:29:13: 29:16 (#0) }], span: $DIR/js-sys/src/lib.rs:5:32: 5:37 (#10) }], span: $DIR/js-sys/src/lib.rs:5:31: 5:38 (#10) }, Punct { ch: ';', spacing: Alone, span: $DIR/js-sys/src/lib.rs:5:38: 5:39 (#10) }] +Called proc_macro_hack with TokenStream [Ident { ident: "struct", span: $DIR/group-compat-hack.rs:22:25: 22:31 (#14) }, Ident { ident: "Three", span: $DIR/group-compat-hack.rs:22:32: 22:37 (#14) }, Group { delimiter: Parenthesis, stream: TokenStream [Group { delimiter: None, stream: TokenStream [Ident { ident: "Foo", span: $DIR/group-compat-hack.rs:30:12: 30:15 (#0) }], span: $DIR/group-compat-hack.rs:22:38: 22:43 (#14) }], span: $DIR/group-compat-hack.rs:22:37: 22:44 (#14) }, Punct { ch: ';', spacing: Alone, span: $DIR/group-compat-hack.rs:22:44: 22:45 (#14) }] +Called proc_macro_hack with TokenStream [Ident { ident: "struct", span: $DIR/time-macros-impl-0.1.0/src/lib.rs:5:21: 5:27 (#20) }, Ident { ident: "One", span: $DIR/time-macros-impl-0.1.0/src/lib.rs:5:28: 5:31 (#20) }, Group { delimiter: Parenthesis, stream: TokenStream [Ident { ident: "Foo", span: $DIR/group-compat-hack.rs:44:18: 44:21 (#0) }], span: $DIR/time-macros-impl-0.1.0/src/lib.rs:5:31: 5:38 (#20) }, Punct { ch: ';', spacing: Alone, span: $DIR/time-macros-impl-0.1.0/src/lib.rs:5:38: 5:39 (#20) }] +Called proc_macro_hack with TokenStream [Ident { ident: "struct", span: $DIR/js-sys-0.3.17/src/lib.rs:5:21: 5:27 (#24) }, Ident { ident: "Two", span: $DIR/js-sys-0.3.17/src/lib.rs:5:28: 5:31 (#24) }, Group { delimiter: Parenthesis, stream: TokenStream [Ident { ident: "Foo", span: $DIR/group-compat-hack.rs:46:13: 46:16 (#0) }], span: $DIR/js-sys-0.3.17/src/lib.rs:5:31: 5:38 (#24) }, Punct { ch: ';', spacing: Alone, span: $DIR/js-sys-0.3.17/src/lib.rs:5:38: 5:39 (#24) }] +Called proc_macro_hack with TokenStream [Ident { ident: "struct", span: $DIR/group-compat-hack.rs:39:25: 39:31 (#28) }, Ident { ident: "Three", span: $DIR/group-compat-hack.rs:39:32: 39:37 (#28) }, Group { delimiter: Parenthesis, stream: TokenStream [Group { delimiter: None, stream: TokenStream [Ident { ident: "Foo", span: $DIR/group-compat-hack.rs:48:12: 48:15 (#0) }], span: $DIR/group-compat-hack.rs:39:38: 39:43 (#28) }], span: $DIR/group-compat-hack.rs:39:37: 39:44 (#28) }, Punct { ch: ';', spacing: Alone, span: $DIR/group-compat-hack.rs:39:44: 39:45 (#28) }] +Called proc_macro_hack with TokenStream [Ident { ident: "struct", span: $DIR/actix-web/src/extract.rs:5:21: 5:27 (#33) }, Ident { ident: "Three", span: $DIR/actix-web/src/extract.rs:5:28: 5:33 (#33) }, Group { delimiter: Parenthesis, stream: TokenStream [Ident { ident: "Foo", span: $DIR/group-compat-hack.rs:55:21: 55:24 (#0) }], span: $DIR/actix-web/src/extract.rs:5:33: 5:37 (#33) }, Punct { ch: ';', spacing: Alone, span: $DIR/actix-web/src/extract.rs:5:37: 5:38 (#33) }] +Called proc_macro_hack with TokenStream [Ident { ident: "struct", span: $DIR/actix-web-2.0.0/src/extract.rs:5:21: 5:27 (#38) }, Ident { ident: "Three", span: $DIR/actix-web-2.0.0/src/extract.rs:5:28: 5:33 (#38) }, Group { delimiter: Parenthesis, stream: TokenStream [Ident { ident: "Foo", span: $DIR/group-compat-hack.rs:63:21: 63:24 (#0) }], span: $DIR/actix-web-2.0.0/src/extract.rs:5:33: 5:37 (#38) }, Punct { ch: ';', spacing: Alone, span: $DIR/actix-web-2.0.0/src/extract.rs:5:37: 5:38 (#38) }] +Called proc_macro_hack with TokenStream [Ident { ident: "struct", span: $DIR/actori-web/src/extract.rs:5:21: 5:27 (#43) }, Ident { ident: "Four", span: $DIR/actori-web/src/extract.rs:5:28: 5:32 (#43) }, Group { delimiter: Parenthesis, stream: TokenStream [Group { delimiter: None, stream: TokenStream [Ident { ident: "Foo", span: $DIR/group-compat-hack.rs:71:21: 71:24 (#0) }], span: $DIR/actori-web/src/extract.rs:5:33: 5:35 (#43) }], span: $DIR/actori-web/src/extract.rs:5:32: 5:36 (#43) }, Punct { ch: ';', spacing: Alone, span: $DIR/actori-web/src/extract.rs:5:36: 5:37 (#43) }] +Called proc_macro_hack with TokenStream [Ident { ident: "struct", span: $DIR/actori-web-2.0.0/src/extract.rs:5:21: 5:27 (#48) }, Ident { ident: "Four", span: $DIR/actori-web-2.0.0/src/extract.rs:5:28: 5:32 (#48) }, Group { delimiter: Parenthesis, stream: TokenStream [Group { delimiter: None, stream: TokenStream [Ident { ident: "Foo", span: $DIR/group-compat-hack.rs:78:21: 78:24 (#0) }], span: $DIR/actori-web-2.0.0/src/extract.rs:5:33: 5:35 (#48) }], span: $DIR/actori-web-2.0.0/src/extract.rs:5:32: 5:36 (#48) }, Punct { ch: ';', spacing: Alone, span: $DIR/actori-web-2.0.0/src/extract.rs:5:36: 5:37 (#48) }] +Called proc_macro_hack with TokenStream [Ident { ident: "struct", span: $DIR/js-sys-0.3.40/src/lib.rs:5:21: 5:27 (#53) }, Ident { ident: "Two", span: $DIR/js-sys-0.3.40/src/lib.rs:5:28: 5:31 (#53) }, Group { delimiter: Parenthesis, stream: TokenStream [Group { delimiter: None, stream: TokenStream [Ident { ident: "Foo", span: $DIR/group-compat-hack.rs:84:13: 84:16 (#0) }], span: $DIR/js-sys-0.3.40/src/lib.rs:5:32: 5:37 (#53) }], span: $DIR/js-sys-0.3.40/src/lib.rs:5:31: 5:38 (#53) }, Punct { ch: ';', spacing: Alone, span: $DIR/js-sys-0.3.40/src/lib.rs:5:38: 5:39 (#53) }] diff --git a/src/test/ui/proc-macro/group-compat-hack/js-sys-0.3.40/src/lib.rs b/src/test/ui/proc-macro/group-compat-hack/js-sys-0.3.40/src/lib.rs new file mode 100644 index 0000000000000..d1a66940ebf3c --- /dev/null +++ b/src/test/ui/proc-macro/group-compat-hack/js-sys-0.3.40/src/lib.rs @@ -0,0 +1,7 @@ +// ignore-test this is not a test + +macro_rules! arrays { + ($name:ident) => { + #[my_macro] struct Two($name); + } +} diff --git a/src/test/ui/proc-macro/inner-attr-non-inline-mod.rs b/src/test/ui/proc-macro/inner-attr-non-inline-mod.rs new file mode 100644 index 0000000000000..30c2666df470c --- /dev/null +++ b/src/test/ui/proc-macro/inner-attr-non-inline-mod.rs @@ -0,0 +1,18 @@ +// compile-flags: -Z span-debug +// error-pattern:custom inner attributes are unstable +// error-pattern:inner macro attributes are unstable +// error-pattern:this was previously accepted +// aux-build:test-macros.rs + +#![no_std] // Don't load unnecessary hygiene information from std +extern crate std; + +#[macro_use] +extern crate test_macros; + +#[deny(unused_attributes)] +mod module_with_attrs; +//~^ ERROR non-inline modules in proc macro input are unstable +//~| ERROR custom inner attributes are unstable + +fn main() {} diff --git a/src/test/ui/proc-macro/inner-attr-non-inline-mod.stderr b/src/test/ui/proc-macro/inner-attr-non-inline-mod.stderr new file mode 100644 index 0000000000000..4286896dfc392 --- /dev/null +++ b/src/test/ui/proc-macro/inner-attr-non-inline-mod.stderr @@ -0,0 +1,40 @@ +error[E0658]: inner macro attributes are unstable + --> $DIR/module_with_attrs.rs:4:4 + | +LL | #![print_attr] + | ^^^^^^^^^^ + | + = note: see issue #54726 <https://github.com/rust-lang/rust/issues/54726> for more information + = help: add `#![feature(custom_inner_attributes)]` to the crate attributes to enable + +error[E0658]: non-inline modules in proc macro input are unstable + --> $DIR/inner-attr-non-inline-mod.rs:14:1 + | +LL | mod module_with_attrs; + | ^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #54727 <https://github.com/rust-lang/rust/issues/54727> for more information + = help: add `#![feature(proc_macro_hygiene)]` to the crate attributes to enable + +error[E0658]: custom inner attributes are unstable + --> $DIR/inner-attr-non-inline-mod.rs:14:1 + | +LL | mod module_with_attrs; + | ^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #54726 <https://github.com/rust-lang/rust/issues/54726> for more information + = help: add `#![feature(custom_inner_attributes)]` to the crate attributes to enable + +error: custom inner attributes are unstable + --> $DIR/module_with_attrs.rs:3:4 + | +LL | #![rustfmt::skip] + | ^^^^^^^^^^^^^ + | + = note: `#[deny(soft_unstable)]` on by default + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #64266 <https://github.com/rust-lang/rust/issues/64266> + +error: aborting due to 4 previous errors + +For more information about this error, try `rustc --explain E0658`. diff --git a/src/test/ui/proc-macro/inner-attr-non-inline-mod.stdout b/src/test/ui/proc-macro/inner-attr-non-inline-mod.stdout new file mode 100644 index 0000000000000..dbef342ef241d --- /dev/null +++ b/src/test/ui/proc-macro/inner-attr-non-inline-mod.stdout @@ -0,0 +1,76 @@ +PRINT-ATTR INPUT (DISPLAY): #[deny(unused_attributes)] mod module_with_attrs { # ! [rustfmt :: skip] } +PRINT-ATTR INPUT (DEBUG): TokenStream [ + Punct { + ch: '#', + spacing: Alone, + span: $DIR/inner-attr-non-inline-mod.rs:14:1: 14:23 (#0), + }, + Group { + delimiter: Bracket, + stream: TokenStream [ + Ident { + ident: "deny", + span: $DIR/inner-attr-non-inline-mod.rs:14:1: 14:23 (#0), + }, + Group { + delimiter: Parenthesis, + stream: TokenStream [ + Ident { + ident: "unused_attributes", + span: $DIR/inner-attr-non-inline-mod.rs:14:1: 14:23 (#0), + }, + ], + span: $DIR/inner-attr-non-inline-mod.rs:14:1: 14:23 (#0), + }, + ], + span: $DIR/inner-attr-non-inline-mod.rs:14:1: 14:23 (#0), + }, + Ident { + ident: "mod", + span: $DIR/inner-attr-non-inline-mod.rs:14:1: 14:23 (#0), + }, + Ident { + ident: "module_with_attrs", + span: $DIR/inner-attr-non-inline-mod.rs:14:1: 14:23 (#0), + }, + Group { + delimiter: Brace, + stream: TokenStream [ + Punct { + ch: '#', + spacing: Joint, + span: $DIR/inner-attr-non-inline-mod.rs:14:1: 14:23 (#0), + }, + Punct { + ch: '!', + spacing: Alone, + span: $DIR/inner-attr-non-inline-mod.rs:14:1: 14:23 (#0), + }, + Group { + delimiter: Bracket, + stream: TokenStream [ + Ident { + ident: "rustfmt", + span: $DIR/inner-attr-non-inline-mod.rs:14:1: 14:23 (#0), + }, + Punct { + ch: ':', + spacing: Joint, + span: $DIR/inner-attr-non-inline-mod.rs:14:1: 14:23 (#0), + }, + Punct { + ch: ':', + spacing: Alone, + span: $DIR/inner-attr-non-inline-mod.rs:14:1: 14:23 (#0), + }, + Ident { + ident: "skip", + span: $DIR/inner-attr-non-inline-mod.rs:14:1: 14:23 (#0), + }, + ], + span: $DIR/inner-attr-non-inline-mod.rs:14:1: 14:23 (#0), + }, + ], + span: $DIR/inner-attr-non-inline-mod.rs:14:1: 14:23 (#0), + }, +] diff --git a/src/test/ui/proc-macro/inner-attrs.rs b/src/test/ui/proc-macro/inner-attrs.rs index 6a353ca3263d0..14ec57ad62606 100644 --- a/src/test/ui/proc-macro/inner-attrs.rs +++ b/src/test/ui/proc-macro/inner-attrs.rs @@ -1,10 +1,10 @@ -// check-pass // compile-flags: -Z span-debug --error-format human // aux-build:test-macros.rs - +// edition:2018 #![feature(custom_inner_attributes)] #![feature(proc_macro_hygiene)] #![feature(stmt_expr_attributes)] +#![feature(rustc_attrs)] #![no_std] // Don't load unnecessary hygiene information from std extern crate std; @@ -19,25 +19,94 @@ fn foo() { #![print_target_and_args(fourth)] } +#[print_target_and_args(mod_first)] +#[print_target_and_args(mod_second)] +mod inline_mod { + #![print_target_and_args(mod_third)] + #![print_target_and_args(mod_fourth)] +} + struct MyStruct { field: bool } +#[derive(Print)] +struct MyDerivePrint { + field: [u8; { + match true { + #![cfg_attr(not(FALSE), rustc_dummy(first))] + #![cfg_attr(not(FALSE), rustc_dummy(second))] + _ => { + #![cfg_attr(not(FALSE), rustc_dummy(third))] + true + } + }; + 0 + }] +} + fn bar() { (#![print_target_and_args(fifth)] 1, 2); + //~^ ERROR expected non-macro inner attribute, found attribute macro + + #[print_target_and_args(tuple_attrs)] ( + #![cfg_attr(FALSE, rustc_dummy)] + 3, 4, { + #![cfg_attr(not(FALSE), rustc_dummy(innermost))] + 5 + } + ); + + #[print_target_and_args(array_attrs)] [ + #![rustc_dummy(inner)] + true; 0 + ]; + + #[print_target_and_args(tuple_attrs)] ( + #![cfg_attr(FALSE, rustc_dummy)] + 3, 4, { + #![cfg_attr(not(FALSE), rustc_dummy(innermost))] + 5 + } + ); + + #[print_target_and_args(array_attrs)] [ + #![rustc_dummy(inner)] + true; 0 + ]; [#![print_target_and_args(sixth)] 1 , 2]; + //~^ ERROR expected non-macro inner attribute, found attribute macro [#![print_target_and_args(seventh)] true ; 5]; - + //~^ ERROR expected non-macro inner attribute, found attribute macro match 0 { #![print_target_and_args(eighth)] + //~^ ERROR expected non-macro inner attribute, found attribute macro _ => {} } MyStruct { #![print_target_and_args(ninth)] field: true }; + //~^ ERROR expected non-macro inner attribute, found attribute macro + + for _ in &[true] { + #![print_attr] //~ ERROR expected non-macro inner attribute + } + + let _ = { + #![print_attr] //~ ERROR expected non-macro inner attribute + }; + + let _ = async { + #![print_attr] //~ ERROR expected non-macro inner attribute + }; + + { + #![print_attr] //~ ERROR expected non-macro inner attribute + }; } + extern { fn weird_extern() { #![print_target_and_args_consume(tenth)] diff --git a/src/test/ui/proc-macro/inner-attrs.stderr b/src/test/ui/proc-macro/inner-attrs.stderr new file mode 100644 index 0000000000000..7f22c5f30d660 --- /dev/null +++ b/src/test/ui/proc-macro/inner-attrs.stderr @@ -0,0 +1,56 @@ +error: expected non-macro inner attribute, found attribute macro `print_target_and_args` + --> $DIR/inner-attrs.rs:49:9 + | +LL | (#![print_target_and_args(fifth)] 1, 2); + | ^^^^^^^^^^^^^^^^^^^^^ not a non-macro inner attribute + +error: expected non-macro inner attribute, found attribute macro `print_target_and_args` + --> $DIR/inner-attrs.rs:78:9 + | +LL | [#![print_target_and_args(sixth)] 1 , 2]; + | ^^^^^^^^^^^^^^^^^^^^^ not a non-macro inner attribute + +error: expected non-macro inner attribute, found attribute macro `print_target_and_args` + --> $DIR/inner-attrs.rs:80:9 + | +LL | [#![print_target_and_args(seventh)] true ; 5]; + | ^^^^^^^^^^^^^^^^^^^^^ not a non-macro inner attribute + +error: expected non-macro inner attribute, found attribute macro `print_target_and_args` + --> $DIR/inner-attrs.rs:84:12 + | +LL | #![print_target_and_args(eighth)] + | ^^^^^^^^^^^^^^^^^^^^^ not a non-macro inner attribute + +error: expected non-macro inner attribute, found attribute macro `print_target_and_args` + --> $DIR/inner-attrs.rs:89:19 + | +LL | MyStruct { #![print_target_and_args(ninth)] field: true }; + | ^^^^^^^^^^^^^^^^^^^^^ not a non-macro inner attribute + +error: expected non-macro inner attribute, found attribute macro `print_attr` + --> $DIR/inner-attrs.rs:93:12 + | +LL | #![print_attr] + | ^^^^^^^^^^ not a non-macro inner attribute + +error: expected non-macro inner attribute, found attribute macro `print_attr` + --> $DIR/inner-attrs.rs:97:12 + | +LL | #![print_attr] + | ^^^^^^^^^^ not a non-macro inner attribute + +error: expected non-macro inner attribute, found attribute macro `print_attr` + --> $DIR/inner-attrs.rs:101:12 + | +LL | #![print_attr] + | ^^^^^^^^^^ not a non-macro inner attribute + +error: expected non-macro inner attribute, found attribute macro `print_attr` + --> $DIR/inner-attrs.rs:105:12 + | +LL | #![print_attr] + | ^^^^^^^^^^ not a non-macro inner attribute + +error: aborting due to 9 previous errors + diff --git a/src/test/ui/proc-macro/inner-attrs.stdout b/src/test/ui/proc-macro/inner-attrs.stdout index 2fd8d8a242ef5..2f442e83002ea 100644 --- a/src/test/ui/proc-macro/inner-attrs.stdout +++ b/src/test/ui/proc-macro/inner-attrs.stdout @@ -11,40 +11,40 @@ PRINT-ATTR INPUT (DEBUG): TokenStream [ Punct { ch: '#', spacing: Alone, - span: $DIR/inner-attrs.rs:17:1: 20:2 (#0), + span: $DIR/inner-attrs.rs:16:1: 16:2 (#0), }, Group { delimiter: Bracket, stream: TokenStream [ Ident { ident: "print_target_and_args", - span: $DIR/inner-attrs.rs:17:1: 20:2 (#0), + span: $DIR/inner-attrs.rs:16:3: 16:24 (#0), }, Group { delimiter: Parenthesis, stream: TokenStream [ Ident { ident: "second", - span: $DIR/inner-attrs.rs:17:1: 20:2 (#0), + span: $DIR/inner-attrs.rs:16:25: 16:31 (#0), }, ], - span: $DIR/inner-attrs.rs:17:1: 20:2 (#0), + span: $DIR/inner-attrs.rs:16:24: 16:32 (#0), }, ], - span: $DIR/inner-attrs.rs:17:1: 20:2 (#0), + span: $DIR/inner-attrs.rs:16:2: 16:33 (#0), }, Ident { ident: "fn", - span: $DIR/inner-attrs.rs:17:1: 20:2 (#0), + span: $DIR/inner-attrs.rs:17:1: 17:3 (#0), }, Ident { ident: "foo", - span: $DIR/inner-attrs.rs:17:1: 20:2 (#0), + span: $DIR/inner-attrs.rs:17:4: 17:7 (#0), }, Group { delimiter: Parenthesis, stream: TokenStream [], - span: $DIR/inner-attrs.rs:17:1: 20:2 (#0), + span: $DIR/inner-attrs.rs:17:7: 17:9 (#0), }, Group { delimiter: Brace, @@ -52,72 +52,72 @@ PRINT-ATTR INPUT (DEBUG): TokenStream [ Punct { ch: '#', spacing: Joint, - span: $DIR/inner-attrs.rs:17:1: 20:2 (#0), + span: $DIR/inner-attrs.rs:18:5: 18:6 (#0), }, Punct { ch: '!', spacing: Alone, - span: $DIR/inner-attrs.rs:17:1: 20:2 (#0), + span: $DIR/inner-attrs.rs:18:6: 18:7 (#0), }, Group { delimiter: Bracket, stream: TokenStream [ Ident { ident: "print_target_and_args", - span: $DIR/inner-attrs.rs:17:1: 20:2 (#0), + span: $DIR/inner-attrs.rs:18:8: 18:29 (#0), }, Group { delimiter: Parenthesis, stream: TokenStream [ Ident { ident: "third", - span: $DIR/inner-attrs.rs:17:1: 20:2 (#0), + span: $DIR/inner-attrs.rs:18:30: 18:35 (#0), }, ], - span: $DIR/inner-attrs.rs:17:1: 20:2 (#0), + span: $DIR/inner-attrs.rs:18:29: 18:36 (#0), }, ], - span: $DIR/inner-attrs.rs:17:1: 20:2 (#0), + span: $DIR/inner-attrs.rs:18:7: 18:37 (#0), }, Punct { ch: '#', spacing: Joint, - span: $DIR/inner-attrs.rs:17:1: 20:2 (#0), + span: $DIR/inner-attrs.rs:19:5: 19:6 (#0), }, Punct { ch: '!', spacing: Alone, - span: $DIR/inner-attrs.rs:17:1: 20:2 (#0), + span: $DIR/inner-attrs.rs:19:6: 19:7 (#0), }, Group { delimiter: Bracket, stream: TokenStream [ Ident { ident: "print_target_and_args", - span: $DIR/inner-attrs.rs:17:1: 20:2 (#0), + span: $DIR/inner-attrs.rs:19:8: 19:29 (#0), }, Group { delimiter: Parenthesis, stream: TokenStream [ Ident { ident: "fourth", - span: $DIR/inner-attrs.rs:17:1: 20:2 (#0), + span: $DIR/inner-attrs.rs:19:30: 19:36 (#0), }, ], - span: $DIR/inner-attrs.rs:17:1: 20:2 (#0), + span: $DIR/inner-attrs.rs:19:29: 19:37 (#0), }, ], - span: $DIR/inner-attrs.rs:17:1: 20:2 (#0), + span: $DIR/inner-attrs.rs:19:7: 19:38 (#0), }, ], - span: $DIR/inner-attrs.rs:17:1: 20:2 (#0), + span: $DIR/inner-attrs.rs:17:10: 20:2 (#0), }, ] PRINT-ATTR_ARGS INPUT (DISPLAY): second PRINT-ATTR_ARGS INPUT (DEBUG): TokenStream [ Ident { ident: "second", - span: $DIR/inner-attrs.rs:17:1: 20:2 (#0), + span: $DIR/inner-attrs.rs:16:25: 16:31 (#0), }, ] PRINT-ATTR INPUT (DISPLAY): fn foo() @@ -125,16 +125,16 @@ PRINT-ATTR INPUT (DISPLAY): fn foo() PRINT-ATTR INPUT (DEBUG): TokenStream [ Ident { ident: "fn", - span: $DIR/inner-attrs.rs:17:1: 20:2 (#0), + span: $DIR/inner-attrs.rs:17:1: 17:3 (#0), }, Ident { ident: "foo", - span: $DIR/inner-attrs.rs:17:1: 20:2 (#0), + span: $DIR/inner-attrs.rs:17:4: 17:7 (#0), }, Group { delimiter: Parenthesis, stream: TokenStream [], - span: $DIR/inner-attrs.rs:17:1: 20:2 (#0), + span: $DIR/inner-attrs.rs:17:7: 17:9 (#0), }, Group { delimiter: Brace, @@ -142,88 +142,88 @@ PRINT-ATTR INPUT (DEBUG): TokenStream [ Punct { ch: '#', spacing: Joint, - span: $DIR/inner-attrs.rs:17:1: 20:2 (#0), + span: $DIR/inner-attrs.rs:18:5: 18:6 (#0), }, Punct { ch: '!', spacing: Alone, - span: $DIR/inner-attrs.rs:17:1: 20:2 (#0), + span: $DIR/inner-attrs.rs:18:6: 18:7 (#0), }, Group { delimiter: Bracket, stream: TokenStream [ Ident { ident: "print_target_and_args", - span: $DIR/inner-attrs.rs:17:1: 20:2 (#0), + span: $DIR/inner-attrs.rs:18:8: 18:29 (#0), }, Group { delimiter: Parenthesis, stream: TokenStream [ Ident { ident: "third", - span: $DIR/inner-attrs.rs:17:1: 20:2 (#0), + span: $DIR/inner-attrs.rs:18:30: 18:35 (#0), }, ], - span: $DIR/inner-attrs.rs:17:1: 20:2 (#0), + span: $DIR/inner-attrs.rs:18:29: 18:36 (#0), }, ], - span: $DIR/inner-attrs.rs:17:1: 20:2 (#0), + span: $DIR/inner-attrs.rs:18:7: 18:37 (#0), }, Punct { ch: '#', spacing: Joint, - span: $DIR/inner-attrs.rs:17:1: 20:2 (#0), + span: $DIR/inner-attrs.rs:19:5: 19:6 (#0), }, Punct { ch: '!', spacing: Alone, - span: $DIR/inner-attrs.rs:17:1: 20:2 (#0), + span: $DIR/inner-attrs.rs:19:6: 19:7 (#0), }, Group { delimiter: Bracket, stream: TokenStream [ Ident { ident: "print_target_and_args", - span: $DIR/inner-attrs.rs:17:1: 20:2 (#0), + span: $DIR/inner-attrs.rs:19:8: 19:29 (#0), }, Group { delimiter: Parenthesis, stream: TokenStream [ Ident { ident: "fourth", - span: $DIR/inner-attrs.rs:17:1: 20:2 (#0), + span: $DIR/inner-attrs.rs:19:30: 19:36 (#0), }, ], - span: $DIR/inner-attrs.rs:17:1: 20:2 (#0), + span: $DIR/inner-attrs.rs:19:29: 19:37 (#0), }, ], - span: $DIR/inner-attrs.rs:17:1: 20:2 (#0), + span: $DIR/inner-attrs.rs:19:7: 19:38 (#0), }, ], - span: $DIR/inner-attrs.rs:17:1: 20:2 (#0), + span: $DIR/inner-attrs.rs:17:10: 20:2 (#0), }, ] PRINT-ATTR_ARGS INPUT (DISPLAY): third PRINT-ATTR_ARGS INPUT (DEBUG): TokenStream [ Ident { ident: "third", - span: $DIR/inner-attrs.rs:17:1: 20:2 (#0), + span: $DIR/inner-attrs.rs:18:30: 18:35 (#0), }, ] PRINT-ATTR INPUT (DISPLAY): fn foo() { # ! [print_target_and_args(fourth)] } PRINT-ATTR INPUT (DEBUG): TokenStream [ Ident { ident: "fn", - span: $DIR/inner-attrs.rs:17:1: 20:2 (#0), + span: $DIR/inner-attrs.rs:17:1: 17:3 (#0), }, Ident { ident: "foo", - span: $DIR/inner-attrs.rs:17:1: 20:2 (#0), + span: $DIR/inner-attrs.rs:17:4: 17:7 (#0), }, Group { delimiter: Parenthesis, stream: TokenStream [], - span: $DIR/inner-attrs.rs:17:1: 20:2 (#0), + span: $DIR/inner-attrs.rs:17:7: 17:9 (#0), }, Group { delimiter: Brace, @@ -231,290 +231,925 @@ PRINT-ATTR INPUT (DEBUG): TokenStream [ Punct { ch: '#', spacing: Joint, - span: $DIR/inner-attrs.rs:17:1: 20:2 (#0), + span: $DIR/inner-attrs.rs:19:5: 19:6 (#0), }, Punct { ch: '!', spacing: Alone, - span: $DIR/inner-attrs.rs:17:1: 20:2 (#0), + span: $DIR/inner-attrs.rs:19:6: 19:7 (#0), }, Group { delimiter: Bracket, stream: TokenStream [ Ident { ident: "print_target_and_args", - span: $DIR/inner-attrs.rs:17:1: 20:2 (#0), + span: $DIR/inner-attrs.rs:19:8: 19:29 (#0), }, Group { delimiter: Parenthesis, stream: TokenStream [ Ident { ident: "fourth", - span: $DIR/inner-attrs.rs:17:1: 20:2 (#0), + span: $DIR/inner-attrs.rs:19:30: 19:36 (#0), }, ], - span: $DIR/inner-attrs.rs:17:1: 20:2 (#0), + span: $DIR/inner-attrs.rs:19:29: 19:37 (#0), }, ], - span: $DIR/inner-attrs.rs:17:1: 20:2 (#0), + span: $DIR/inner-attrs.rs:19:7: 19:38 (#0), }, ], - span: $DIR/inner-attrs.rs:17:1: 20:2 (#0), + span: $DIR/inner-attrs.rs:17:10: 20:2 (#0), }, ] PRINT-ATTR_ARGS INPUT (DISPLAY): fourth PRINT-ATTR_ARGS INPUT (DEBUG): TokenStream [ Ident { ident: "fourth", - span: $DIR/inner-attrs.rs:17:1: 20:2 (#0), + span: $DIR/inner-attrs.rs:19:30: 19:36 (#0), }, ] PRINT-ATTR INPUT (DISPLAY): fn foo() { } PRINT-ATTR INPUT (DEBUG): TokenStream [ Ident { ident: "fn", - span: $DIR/inner-attrs.rs:17:1: 20:2 (#0), + span: $DIR/inner-attrs.rs:17:1: 17:3 (#0), }, Ident { ident: "foo", - span: $DIR/inner-attrs.rs:17:1: 20:2 (#0), + span: $DIR/inner-attrs.rs:17:4: 17:7 (#0), }, Group { delimiter: Parenthesis, stream: TokenStream [], - span: $DIR/inner-attrs.rs:17:1: 20:2 (#0), + span: $DIR/inner-attrs.rs:17:7: 17:9 (#0), }, Group { delimiter: Brace, stream: TokenStream [], - span: $DIR/inner-attrs.rs:17:1: 20:2 (#0), + span: $DIR/inner-attrs.rs:17:10: 20:2 (#0), }, ] -PRINT-ATTR_ARGS INPUT (DISPLAY): fifth +PRINT-ATTR_ARGS INPUT (DISPLAY): mod_first PRINT-ATTR_ARGS INPUT (DEBUG): TokenStream [ Ident { - ident: "fifth", - span: $DIR/inner-attrs.rs:27:31: 27:36 (#0), + ident: "mod_first", + span: $DIR/inner-attrs.rs:22:25: 22:34 (#0), }, ] -PRINT-ATTR INPUT (DISPLAY): (1, 2) ; +PRINT-ATTR INPUT (DISPLAY): #[print_target_and_args(mod_second)] mod inline_mod +{ + # ! [print_target_and_args(mod_third)] # ! + [print_target_and_args(mod_fourth)] +} PRINT-ATTR INPUT (DEBUG): TokenStream [ + Punct { + ch: '#', + spacing: Alone, + span: $DIR/inner-attrs.rs:23:1: 23:2 (#0), + }, Group { - delimiter: Parenthesis, + delimiter: Bracket, stream: TokenStream [ - Literal { - kind: Integer, - symbol: "1", - suffix: None, - span: $DIR/inner-attrs.rs:27:5: 27:45 (#0), + Ident { + ident: "print_target_and_args", + span: $DIR/inner-attrs.rs:23:3: 23:24 (#0), + }, + Group { + delimiter: Parenthesis, + stream: TokenStream [ + Ident { + ident: "mod_second", + span: $DIR/inner-attrs.rs:23:25: 23:35 (#0), + }, + ], + span: $DIR/inner-attrs.rs:23:24: 23:36 (#0), + }, + ], + span: $DIR/inner-attrs.rs:23:2: 23:37 (#0), + }, + Ident { + ident: "mod", + span: $DIR/inner-attrs.rs:24:1: 24:4 (#0), + }, + Ident { + ident: "inline_mod", + span: $DIR/inner-attrs.rs:24:5: 24:15 (#0), + }, + Group { + delimiter: Brace, + stream: TokenStream [ + Punct { + ch: '#', + spacing: Joint, + span: $DIR/inner-attrs.rs:25:5: 25:6 (#0), }, Punct { - ch: ',', + ch: '!', spacing: Alone, - span: $DIR/inner-attrs.rs:27:5: 27:45 (#0), + span: $DIR/inner-attrs.rs:25:6: 25:7 (#0), }, - Literal { - kind: Integer, - symbol: "2", - suffix: None, - span: $DIR/inner-attrs.rs:27:5: 27:45 (#0), + Group { + delimiter: Bracket, + stream: TokenStream [ + Ident { + ident: "print_target_and_args", + span: $DIR/inner-attrs.rs:25:8: 25:29 (#0), + }, + Group { + delimiter: Parenthesis, + stream: TokenStream [ + Ident { + ident: "mod_third", + span: $DIR/inner-attrs.rs:25:30: 25:39 (#0), + }, + ], + span: $DIR/inner-attrs.rs:25:29: 25:40 (#0), + }, + ], + span: $DIR/inner-attrs.rs:25:7: 25:41 (#0), + }, + Punct { + ch: '#', + spacing: Joint, + span: $DIR/inner-attrs.rs:26:5: 26:6 (#0), + }, + Punct { + ch: '!', + spacing: Alone, + span: $DIR/inner-attrs.rs:26:6: 26:7 (#0), + }, + Group { + delimiter: Bracket, + stream: TokenStream [ + Ident { + ident: "print_target_and_args", + span: $DIR/inner-attrs.rs:26:8: 26:29 (#0), + }, + Group { + delimiter: Parenthesis, + stream: TokenStream [ + Ident { + ident: "mod_fourth", + span: $DIR/inner-attrs.rs:26:30: 26:40 (#0), + }, + ], + span: $DIR/inner-attrs.rs:26:29: 26:41 (#0), + }, + ], + span: $DIR/inner-attrs.rs:26:7: 26:42 (#0), }, ], - span: $DIR/inner-attrs.rs:27:5: 27:45 (#0), + span: $DIR/inner-attrs.rs:24:16: 27:2 (#0), }, - Punct { - ch: ';', - spacing: Alone, - span: $DIR/inner-attrs.rs:27:5: 27:45 (#0), +] +PRINT-ATTR_ARGS INPUT (DISPLAY): mod_second +PRINT-ATTR_ARGS INPUT (DEBUG): TokenStream [ + Ident { + ident: "mod_second", + span: $DIR/inner-attrs.rs:23:25: 23:35 (#0), + }, +] +PRINT-ATTR INPUT (DISPLAY): mod inline_mod +{ + # ! [print_target_and_args(mod_third)] # ! + [print_target_and_args(mod_fourth)] +} +PRINT-ATTR INPUT (DEBUG): TokenStream [ + Ident { + ident: "mod", + span: $DIR/inner-attrs.rs:24:1: 24:4 (#0), + }, + Ident { + ident: "inline_mod", + span: $DIR/inner-attrs.rs:24:5: 24:15 (#0), + }, + Group { + delimiter: Brace, + stream: TokenStream [ + Punct { + ch: '#', + spacing: Joint, + span: $DIR/inner-attrs.rs:25:5: 25:6 (#0), + }, + Punct { + ch: '!', + spacing: Alone, + span: $DIR/inner-attrs.rs:25:6: 25:7 (#0), + }, + Group { + delimiter: Bracket, + stream: TokenStream [ + Ident { + ident: "print_target_and_args", + span: $DIR/inner-attrs.rs:25:8: 25:29 (#0), + }, + Group { + delimiter: Parenthesis, + stream: TokenStream [ + Ident { + ident: "mod_third", + span: $DIR/inner-attrs.rs:25:30: 25:39 (#0), + }, + ], + span: $DIR/inner-attrs.rs:25:29: 25:40 (#0), + }, + ], + span: $DIR/inner-attrs.rs:25:7: 25:41 (#0), + }, + Punct { + ch: '#', + spacing: Joint, + span: $DIR/inner-attrs.rs:26:5: 26:6 (#0), + }, + Punct { + ch: '!', + spacing: Alone, + span: $DIR/inner-attrs.rs:26:6: 26:7 (#0), + }, + Group { + delimiter: Bracket, + stream: TokenStream [ + Ident { + ident: "print_target_and_args", + span: $DIR/inner-attrs.rs:26:8: 26:29 (#0), + }, + Group { + delimiter: Parenthesis, + stream: TokenStream [ + Ident { + ident: "mod_fourth", + span: $DIR/inner-attrs.rs:26:30: 26:40 (#0), + }, + ], + span: $DIR/inner-attrs.rs:26:29: 26:41 (#0), + }, + ], + span: $DIR/inner-attrs.rs:26:7: 26:42 (#0), + }, + ], + span: $DIR/inner-attrs.rs:24:16: 27:2 (#0), }, ] -PRINT-ATTR_ARGS INPUT (DISPLAY): sixth +PRINT-ATTR_ARGS INPUT (DISPLAY): mod_third PRINT-ATTR_ARGS INPUT (DEBUG): TokenStream [ Ident { - ident: "sixth", - span: $DIR/inner-attrs.rs:29:31: 29:36 (#0), + ident: "mod_third", + span: $DIR/inner-attrs.rs:25:30: 25:39 (#0), }, ] -PRINT-ATTR INPUT (DISPLAY): [1, 2] ; +PRINT-ATTR INPUT (DISPLAY): mod inline_mod { # ! [print_target_and_args(mod_fourth)] } PRINT-ATTR INPUT (DEBUG): TokenStream [ + Ident { + ident: "mod", + span: $DIR/inner-attrs.rs:24:1: 24:4 (#0), + }, + Ident { + ident: "inline_mod", + span: $DIR/inner-attrs.rs:24:5: 24:15 (#0), + }, Group { - delimiter: Bracket, + delimiter: Brace, + stream: TokenStream [ + Punct { + ch: '#', + spacing: Joint, + span: $DIR/inner-attrs.rs:26:5: 26:6 (#0), + }, + Punct { + ch: '!', + spacing: Alone, + span: $DIR/inner-attrs.rs:26:6: 26:7 (#0), + }, + Group { + delimiter: Bracket, + stream: TokenStream [ + Ident { + ident: "print_target_and_args", + span: $DIR/inner-attrs.rs:26:8: 26:29 (#0), + }, + Group { + delimiter: Parenthesis, + stream: TokenStream [ + Ident { + ident: "mod_fourth", + span: $DIR/inner-attrs.rs:26:30: 26:40 (#0), + }, + ], + span: $DIR/inner-attrs.rs:26:29: 26:41 (#0), + }, + ], + span: $DIR/inner-attrs.rs:26:7: 26:42 (#0), + }, + ], + span: $DIR/inner-attrs.rs:24:16: 27:2 (#0), + }, +] +PRINT-ATTR_ARGS INPUT (DISPLAY): mod_fourth +PRINT-ATTR_ARGS INPUT (DEBUG): TokenStream [ + Ident { + ident: "mod_fourth", + span: $DIR/inner-attrs.rs:26:30: 26:40 (#0), + }, +] +PRINT-ATTR INPUT (DISPLAY): mod inline_mod { } +PRINT-ATTR INPUT (DEBUG): TokenStream [ + Ident { + ident: "mod", + span: $DIR/inner-attrs.rs:24:1: 24:4 (#0), + }, + Ident { + ident: "inline_mod", + span: $DIR/inner-attrs.rs:24:5: 24:15 (#0), + }, + Group { + delimiter: Brace, + stream: TokenStream [], + span: $DIR/inner-attrs.rs:24:16: 27:2 (#0), + }, +] +PRINT-DERIVE INPUT (DISPLAY): struct MyDerivePrint +{ + field : + [u8 ; + { + match true + { + # ! [rustc_dummy(first)] # ! [rustc_dummy(second)] _ => + { # ! [rustc_dummy(third)] true } + } ; 0 + }] +} +PRINT-DERIVE INPUT (DEBUG): TokenStream [ + Ident { + ident: "struct", + span: $DIR/inner-attrs.rs:34:1: 34:7 (#0), + }, + Ident { + ident: "MyDerivePrint", + span: $DIR/inner-attrs.rs:34:8: 34:21 (#0), + }, + Group { + delimiter: Brace, + stream: TokenStream [ + Ident { + ident: "field", + span: $DIR/inner-attrs.rs:35:5: 35:10 (#0), + }, + Punct { + ch: ':', + spacing: Alone, + span: $DIR/inner-attrs.rs:35:10: 35:11 (#0), + }, + Group { + delimiter: Bracket, + stream: TokenStream [ + Ident { + ident: "u8", + span: $DIR/inner-attrs.rs:35:13: 35:15 (#0), + }, + Punct { + ch: ';', + spacing: Alone, + span: $DIR/inner-attrs.rs:35:15: 35:16 (#0), + }, + Group { + delimiter: Brace, + stream: TokenStream [ + Ident { + ident: "match", + span: $DIR/inner-attrs.rs:36:9: 36:14 (#0), + }, + Ident { + ident: "true", + span: $DIR/inner-attrs.rs:36:15: 36:19 (#0), + }, + Group { + delimiter: Brace, + stream: TokenStream [ + Punct { + ch: '#', + spacing: Alone, + span: $DIR/inner-attrs.rs:37:13: 37:14 (#0), + }, + Punct { + ch: '!', + spacing: Alone, + span: $DIR/inner-attrs.rs:37:14: 37:15 (#0), + }, + Group { + delimiter: Bracket, + stream: TokenStream [ + Ident { + ident: "rustc_dummy", + span: $DIR/inner-attrs.rs:37:37: 37:48 (#0), + }, + Group { + delimiter: Parenthesis, + stream: TokenStream [ + Ident { + ident: "first", + span: $DIR/inner-attrs.rs:37:49: 37:54 (#0), + }, + ], + span: $DIR/inner-attrs.rs:37:48: 37:55 (#0), + }, + ], + span: $DIR/inner-attrs.rs:37:13: 37:14 (#0), + }, + Punct { + ch: '#', + spacing: Alone, + span: $DIR/inner-attrs.rs:38:13: 38:14 (#0), + }, + Punct { + ch: '!', + spacing: Alone, + span: $DIR/inner-attrs.rs:38:14: 38:15 (#0), + }, + Group { + delimiter: Bracket, + stream: TokenStream [ + Ident { + ident: "rustc_dummy", + span: $DIR/inner-attrs.rs:38:37: 38:48 (#0), + }, + Group { + delimiter: Parenthesis, + stream: TokenStream [ + Ident { + ident: "second", + span: $DIR/inner-attrs.rs:38:49: 38:55 (#0), + }, + ], + span: $DIR/inner-attrs.rs:38:48: 38:56 (#0), + }, + ], + span: $DIR/inner-attrs.rs:38:13: 38:14 (#0), + }, + Ident { + ident: "_", + span: $DIR/inner-attrs.rs:39:13: 39:14 (#0), + }, + Punct { + ch: '=', + spacing: Joint, + span: $DIR/inner-attrs.rs:39:15: 39:17 (#0), + }, + Punct { + ch: '>', + spacing: Alone, + span: $DIR/inner-attrs.rs:39:15: 39:17 (#0), + }, + Group { + delimiter: Brace, + stream: TokenStream [ + Punct { + ch: '#', + spacing: Alone, + span: $DIR/inner-attrs.rs:40:17: 40:18 (#0), + }, + Punct { + ch: '!', + spacing: Alone, + span: $DIR/inner-attrs.rs:40:18: 40:19 (#0), + }, + Group { + delimiter: Bracket, + stream: TokenStream [ + Ident { + ident: "rustc_dummy", + span: $DIR/inner-attrs.rs:40:41: 40:52 (#0), + }, + Group { + delimiter: Parenthesis, + stream: TokenStream [ + Ident { + ident: "third", + span: $DIR/inner-attrs.rs:40:53: 40:58 (#0), + }, + ], + span: $DIR/inner-attrs.rs:40:52: 40:59 (#0), + }, + ], + span: $DIR/inner-attrs.rs:40:17: 40:18 (#0), + }, + Ident { + ident: "true", + span: $DIR/inner-attrs.rs:41:17: 41:21 (#0), + }, + ], + span: $DIR/inner-attrs.rs:39:18: 42:14 (#0), + }, + ], + span: $DIR/inner-attrs.rs:36:20: 43:10 (#0), + }, + Punct { + ch: ';', + spacing: Alone, + span: $DIR/inner-attrs.rs:43:10: 43:11 (#0), + }, + Literal { + kind: Integer, + symbol: "0", + suffix: None, + span: $DIR/inner-attrs.rs:44:9: 44:10 (#0), + }, + ], + span: $DIR/inner-attrs.rs:35:17: 45:6 (#0), + }, + ], + span: $DIR/inner-attrs.rs:35:12: 45:7 (#0), + }, + ], + span: $DIR/inner-attrs.rs:34:22: 46:2 (#0), + }, +] +PRINT-ATTR_ARGS INPUT (DISPLAY): tuple_attrs +PRINT-ATTR_ARGS INPUT (DEBUG): TokenStream [ + Ident { + ident: "tuple_attrs", + span: $DIR/inner-attrs.rs:52:29: 52:40 (#0), + }, +] +PRINT-ATTR INPUT (DISPLAY): (3, 4, { # ! [cfg_attr(not(FALSE), rustc_dummy(innermost))] 5 }) ; +PRINT-ATTR INPUT (DEBUG): TokenStream [ + Group { + delimiter: Parenthesis, stream: TokenStream [ Literal { kind: Integer, - symbol: "1", + symbol: "3", suffix: None, - span: $DIR/inner-attrs.rs:29:5: 29:46 (#0), + span: $DIR/inner-attrs.rs:54:9: 54:10 (#0), }, Punct { ch: ',', spacing: Alone, - span: $DIR/inner-attrs.rs:29:5: 29:46 (#0), + span: $DIR/inner-attrs.rs:54:10: 54:11 (#0), }, Literal { kind: Integer, - symbol: "2", + symbol: "4", suffix: None, - span: $DIR/inner-attrs.rs:29:5: 29:46 (#0), + span: $DIR/inner-attrs.rs:54:12: 54:13 (#0), + }, + Punct { + ch: ',', + spacing: Alone, + span: $DIR/inner-attrs.rs:54:13: 54:14 (#0), + }, + Group { + delimiter: Brace, + stream: TokenStream [ + Punct { + ch: '#', + spacing: Joint, + span: $DIR/inner-attrs.rs:55:13: 55:14 (#0), + }, + Punct { + ch: '!', + spacing: Alone, + span: $DIR/inner-attrs.rs:55:14: 55:15 (#0), + }, + Group { + delimiter: Bracket, + stream: TokenStream [ + Ident { + ident: "cfg_attr", + span: $DIR/inner-attrs.rs:55:16: 55:24 (#0), + }, + Group { + delimiter: Parenthesis, + stream: TokenStream [ + Ident { + ident: "not", + span: $DIR/inner-attrs.rs:55:25: 55:28 (#0), + }, + Group { + delimiter: Parenthesis, + stream: TokenStream [ + Ident { + ident: "FALSE", + span: $DIR/inner-attrs.rs:55:29: 55:34 (#0), + }, + ], + span: $DIR/inner-attrs.rs:55:28: 55:35 (#0), + }, + Punct { + ch: ',', + spacing: Alone, + span: $DIR/inner-attrs.rs:55:35: 55:36 (#0), + }, + Ident { + ident: "rustc_dummy", + span: $DIR/inner-attrs.rs:55:37: 55:48 (#0), + }, + Group { + delimiter: Parenthesis, + stream: TokenStream [ + Ident { + ident: "innermost", + span: $DIR/inner-attrs.rs:55:49: 55:58 (#0), + }, + ], + span: $DIR/inner-attrs.rs:55:48: 55:59 (#0), + }, + ], + span: $DIR/inner-attrs.rs:55:24: 55:60 (#0), + }, + ], + span: $DIR/inner-attrs.rs:55:15: 55:61 (#0), + }, + Literal { + kind: Integer, + symbol: "5", + suffix: None, + span: $DIR/inner-attrs.rs:56:13: 56:14 (#0), + }, + ], + span: $DIR/inner-attrs.rs:54:15: 57:10 (#0), }, ], - span: $DIR/inner-attrs.rs:29:5: 29:46 (#0), + span: $DIR/inner-attrs.rs:52:43: 58:6 (#0), }, Punct { ch: ';', spacing: Alone, - span: $DIR/inner-attrs.rs:29:5: 29:46 (#0), + span: $DIR/inner-attrs.rs:58:6: 58:7 (#0), }, ] -PRINT-ATTR_ARGS INPUT (DISPLAY): seventh +PRINT-ATTR_ARGS INPUT (DISPLAY): array_attrs PRINT-ATTR_ARGS INPUT (DEBUG): TokenStream [ Ident { - ident: "seventh", - span: $DIR/inner-attrs.rs:30:31: 30:38 (#0), + ident: "array_attrs", + span: $DIR/inner-attrs.rs:60:29: 60:40 (#0), }, ] -PRINT-ATTR INPUT (DISPLAY): [true ; 5] ; +PRINT-ATTR INPUT (DISPLAY): [# ! [rustc_dummy(inner)] true ; 0] ; PRINT-ATTR INPUT (DEBUG): TokenStream [ Group { delimiter: Bracket, stream: TokenStream [ + Punct { + ch: '#', + spacing: Joint, + span: $DIR/inner-attrs.rs:61:9: 61:10 (#0), + }, + Punct { + ch: '!', + spacing: Alone, + span: $DIR/inner-attrs.rs:61:10: 61:11 (#0), + }, + Group { + delimiter: Bracket, + stream: TokenStream [ + Ident { + ident: "rustc_dummy", + span: $DIR/inner-attrs.rs:61:12: 61:23 (#0), + }, + Group { + delimiter: Parenthesis, + stream: TokenStream [ + Ident { + ident: "inner", + span: $DIR/inner-attrs.rs:61:24: 61:29 (#0), + }, + ], + span: $DIR/inner-attrs.rs:61:23: 61:30 (#0), + }, + ], + span: $DIR/inner-attrs.rs:61:11: 61:31 (#0), + }, Ident { ident: "true", - span: $DIR/inner-attrs.rs:30:5: 30:51 (#0), + span: $DIR/inner-attrs.rs:62:9: 62:13 (#0), }, Punct { ch: ';', spacing: Alone, - span: $DIR/inner-attrs.rs:30:5: 30:51 (#0), + span: $DIR/inner-attrs.rs:62:13: 62:14 (#0), }, Literal { kind: Integer, - symbol: "5", + symbol: "0", suffix: None, - span: $DIR/inner-attrs.rs:30:5: 30:51 (#0), + span: $DIR/inner-attrs.rs:62:15: 62:16 (#0), }, ], - span: $DIR/inner-attrs.rs:30:5: 30:51 (#0), + span: $DIR/inner-attrs.rs:60:43: 63:6 (#0), }, Punct { ch: ';', spacing: Alone, - span: $DIR/inner-attrs.rs:30:5: 30:51 (#0), + span: $DIR/inner-attrs.rs:63:6: 63:7 (#0), }, ] -PRINT-ATTR_ARGS INPUT (DISPLAY): eighth +PRINT-ATTR_ARGS INPUT (DISPLAY): tuple_attrs PRINT-ATTR_ARGS INPUT (DEBUG): TokenStream [ Ident { - ident: "eighth", - span: $DIR/inner-attrs.rs:34:34: 34:40 (#0), + ident: "tuple_attrs", + span: $DIR/inner-attrs.rs:65:29: 65:40 (#0), }, ] -PRINT-ATTR INPUT (DISPLAY): match 0 { _ => { } } +PRINT-ATTR INPUT (DISPLAY): (3, 4, { # ! [cfg_attr(not(FALSE), rustc_dummy(innermost))] 5 }) ; PRINT-ATTR INPUT (DEBUG): TokenStream [ - Ident { - ident: "match", - span: $DIR/inner-attrs.rs:33:5: 36:6 (#0), - }, - Literal { - kind: Integer, - symbol: "0", - suffix: None, - span: $DIR/inner-attrs.rs:33:5: 36:6 (#0), - }, Group { - delimiter: Brace, + delimiter: Parenthesis, stream: TokenStream [ - Ident { - ident: "_", - span: $DIR/inner-attrs.rs:33:5: 36:6 (#0), + Literal { + kind: Integer, + symbol: "3", + suffix: None, + span: $DIR/inner-attrs.rs:67:9: 67:10 (#0), }, Punct { - ch: '=', - spacing: Joint, - span: $DIR/inner-attrs.rs:33:5: 36:6 (#0), + ch: ',', + spacing: Alone, + span: $DIR/inner-attrs.rs:67:10: 67:11 (#0), + }, + Literal { + kind: Integer, + symbol: "4", + suffix: None, + span: $DIR/inner-attrs.rs:67:12: 67:13 (#0), }, Punct { - ch: '>', + ch: ',', spacing: Alone, - span: $DIR/inner-attrs.rs:33:5: 36:6 (#0), + span: $DIR/inner-attrs.rs:67:13: 67:14 (#0), }, Group { delimiter: Brace, - stream: TokenStream [], - span: $DIR/inner-attrs.rs:33:5: 36:6 (#0), + stream: TokenStream [ + Punct { + ch: '#', + spacing: Joint, + span: $DIR/inner-attrs.rs:68:13: 68:14 (#0), + }, + Punct { + ch: '!', + spacing: Alone, + span: $DIR/inner-attrs.rs:68:14: 68:15 (#0), + }, + Group { + delimiter: Bracket, + stream: TokenStream [ + Ident { + ident: "cfg_attr", + span: $DIR/inner-attrs.rs:68:16: 68:24 (#0), + }, + Group { + delimiter: Parenthesis, + stream: TokenStream [ + Ident { + ident: "not", + span: $DIR/inner-attrs.rs:68:25: 68:28 (#0), + }, + Group { + delimiter: Parenthesis, + stream: TokenStream [ + Ident { + ident: "FALSE", + span: $DIR/inner-attrs.rs:68:29: 68:34 (#0), + }, + ], + span: $DIR/inner-attrs.rs:68:28: 68:35 (#0), + }, + Punct { + ch: ',', + spacing: Alone, + span: $DIR/inner-attrs.rs:68:35: 68:36 (#0), + }, + Ident { + ident: "rustc_dummy", + span: $DIR/inner-attrs.rs:68:37: 68:48 (#0), + }, + Group { + delimiter: Parenthesis, + stream: TokenStream [ + Ident { + ident: "innermost", + span: $DIR/inner-attrs.rs:68:49: 68:58 (#0), + }, + ], + span: $DIR/inner-attrs.rs:68:48: 68:59 (#0), + }, + ], + span: $DIR/inner-attrs.rs:68:24: 68:60 (#0), + }, + ], + span: $DIR/inner-attrs.rs:68:15: 68:61 (#0), + }, + Literal { + kind: Integer, + symbol: "5", + suffix: None, + span: $DIR/inner-attrs.rs:69:13: 69:14 (#0), + }, + ], + span: $DIR/inner-attrs.rs:67:15: 70:10 (#0), }, ], - span: $DIR/inner-attrs.rs:33:5: 36:6 (#0), + span: $DIR/inner-attrs.rs:65:43: 71:6 (#0), + }, + Punct { + ch: ';', + spacing: Alone, + span: $DIR/inner-attrs.rs:71:6: 71:7 (#0), }, ] -PRINT-ATTR_ARGS INPUT (DISPLAY): ninth +PRINT-ATTR_ARGS INPUT (DISPLAY): array_attrs PRINT-ATTR_ARGS INPUT (DEBUG): TokenStream [ Ident { - ident: "ninth", - span: $DIR/inner-attrs.rs:38:41: 38:46 (#0), + ident: "array_attrs", + span: $DIR/inner-attrs.rs:73:29: 73:40 (#0), }, ] -PRINT-ATTR INPUT (DISPLAY): MyStruct { field : true, } ; +PRINT-ATTR INPUT (DISPLAY): [# ! [rustc_dummy(inner)] true ; 0] ; PRINT-ATTR INPUT (DEBUG): TokenStream [ - Ident { - ident: "MyStruct", - span: $DIR/inner-attrs.rs:38:5: 38:63 (#0), - }, Group { - delimiter: Brace, + delimiter: Bracket, stream: TokenStream [ - Ident { - ident: "field", - span: $DIR/inner-attrs.rs:38:5: 38:63 (#0), + Punct { + ch: '#', + spacing: Joint, + span: $DIR/inner-attrs.rs:74:9: 74:10 (#0), }, Punct { - ch: ':', + ch: '!', spacing: Alone, - span: $DIR/inner-attrs.rs:38:5: 38:63 (#0), + span: $DIR/inner-attrs.rs:74:10: 74:11 (#0), + }, + Group { + delimiter: Bracket, + stream: TokenStream [ + Ident { + ident: "rustc_dummy", + span: $DIR/inner-attrs.rs:74:12: 74:23 (#0), + }, + Group { + delimiter: Parenthesis, + stream: TokenStream [ + Ident { + ident: "inner", + span: $DIR/inner-attrs.rs:74:24: 74:29 (#0), + }, + ], + span: $DIR/inner-attrs.rs:74:23: 74:30 (#0), + }, + ], + span: $DIR/inner-attrs.rs:74:11: 74:31 (#0), }, Ident { ident: "true", - span: $DIR/inner-attrs.rs:38:5: 38:63 (#0), + span: $DIR/inner-attrs.rs:75:9: 75:13 (#0), }, Punct { - ch: ',', + ch: ';', spacing: Alone, - span: $DIR/inner-attrs.rs:38:5: 38:63 (#0), + span: $DIR/inner-attrs.rs:75:13: 75:14 (#0), + }, + Literal { + kind: Integer, + symbol: "0", + suffix: None, + span: $DIR/inner-attrs.rs:75:15: 75:16 (#0), }, ], - span: $DIR/inner-attrs.rs:38:5: 38:63 (#0), + span: $DIR/inner-attrs.rs:73:43: 76:6 (#0), }, Punct { ch: ';', spacing: Alone, - span: $DIR/inner-attrs.rs:38:5: 38:63 (#0), + span: $DIR/inner-attrs.rs:76:6: 76:7 (#0), }, ] PRINT-ATTR_ARGS INPUT (DISPLAY): tenth PRINT-ATTR_ARGS INPUT (DEBUG): TokenStream [ Ident { ident: "tenth", - span: $DIR/inner-attrs.rs:43:42: 43:47 (#0), + span: $DIR/inner-attrs.rs:112:42: 112:47 (#0), }, ] PRINT-ATTR INPUT (DISPLAY): fn weird_extern() { } PRINT-ATTR INPUT (DEBUG): TokenStream [ Ident { ident: "fn", - span: $DIR/inner-attrs.rs:42:5: 44:6 (#0), + span: $DIR/inner-attrs.rs:111:5: 111:7 (#0), }, Ident { ident: "weird_extern", - span: $DIR/inner-attrs.rs:42:5: 44:6 (#0), + span: $DIR/inner-attrs.rs:111:8: 111:20 (#0), }, Group { delimiter: Parenthesis, stream: TokenStream [], - span: $DIR/inner-attrs.rs:42:5: 44:6 (#0), + span: $DIR/inner-attrs.rs:111:20: 111:22 (#0), }, Group { delimiter: Brace, stream: TokenStream [], - span: $DIR/inner-attrs.rs:42:5: 44:6 (#0), + span: $DIR/inner-attrs.rs:111:23: 113:6 (#0), }, ] diff --git a/src/test/ui/proc-macro/issue-36935.stderr b/src/test/ui/proc-macro/issue-36935.stderr index 2b2e28fdb2fda..079e134c6f844 100644 --- a/src/test/ui/proc-macro/issue-36935.stderr +++ b/src/test/ui/proc-macro/issue-36935.stderr @@ -1,11 +1,3 @@ -error: proc-macro derive panicked - --> $DIR/issue-36935.rs:6:20 - | -LL | #[derive(Identity, Panic)] - | ^^^^^ - | - = help: message: panic-derive - error[E0428]: the name `Baz` is defined multiple times --> $DIR/issue-36935.rs:7:1 | @@ -17,6 +9,14 @@ LL | struct Baz { | = note: `Baz` must be defined only once in the type namespace of this module +error: proc-macro derive panicked + --> $DIR/issue-36935.rs:6:20 + | +LL | #[derive(Identity, Panic)] + | ^^^^^ + | + = help: message: panic-derive + error: aborting due to 2 previous errors For more information about this error, try `rustc --explain E0428`. diff --git a/src/test/ui/proc-macro/issue-73933-procedural-masquerade.rs b/src/test/ui/proc-macro/issue-73933-procedural-masquerade.rs new file mode 100644 index 0000000000000..abc3d2691a307 --- /dev/null +++ b/src/test/ui/proc-macro/issue-73933-procedural-masquerade.rs @@ -0,0 +1,13 @@ +// check-pass +// aux-build:test-macros.rs + +#[macro_use] +extern crate test_macros; + +#[derive(Print)] +enum ProceduralMasqueradeDummyType { //~ WARN using +//~| WARN this was previously + Input +} + +fn main() {} diff --git a/src/test/ui/proc-macro/issue-73933-procedural-masquerade.stderr b/src/test/ui/proc-macro/issue-73933-procedural-masquerade.stderr new file mode 100644 index 0000000000000..0b930705e3510 --- /dev/null +++ b/src/test/ui/proc-macro/issue-73933-procedural-masquerade.stderr @@ -0,0 +1,25 @@ +warning: using `procedural-masquerade` crate + --> $DIR/issue-73933-procedural-masquerade.rs:8:6 + | +LL | enum ProceduralMasqueradeDummyType { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `#[warn(proc_macro_back_compat)]` on by default + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #83125 <https://github.com/rust-lang/rust/issues/83125> + = note: The `procedural-masquerade` crate has been unnecessary since Rust 1.30.0. Versions of this crate below 0.1.7 will eventually stop compiling. + +warning: 1 warning emitted + +Future incompatibility report: Future breakage date: None, diagnostic: +warning: using `procedural-masquerade` crate + --> $DIR/issue-73933-procedural-masquerade.rs:8:6 + | +LL | enum ProceduralMasqueradeDummyType { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `#[warn(proc_macro_back_compat)]` on by default + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #83125 <https://github.com/rust-lang/rust/issues/83125> + = note: The `procedural-masquerade` crate has been unnecessary since Rust 1.30.0. Versions of this crate below 0.1.7 will eventually stop compiling. + diff --git a/src/test/ui/proc-macro/issue-73933-procedural-masquerade.stdout b/src/test/ui/proc-macro/issue-73933-procedural-masquerade.stdout new file mode 100644 index 0000000000000..8edd68f8a3b84 --- /dev/null +++ b/src/test/ui/proc-macro/issue-73933-procedural-masquerade.stdout @@ -0,0 +1,22 @@ +PRINT-DERIVE INPUT (DISPLAY): enum ProceduralMasqueradeDummyType { Input, } +PRINT-DERIVE RE-COLLECTED (DISPLAY): enum ProceduralMasqueradeDummyType { Input } +PRINT-DERIVE INPUT (DEBUG): TokenStream [ + Ident { + ident: "enum", + span: #0 bytes(100..104), + }, + Ident { + ident: "ProceduralMasqueradeDummyType", + span: #0 bytes(105..134), + }, + Group { + delimiter: Brace, + stream: TokenStream [ + Ident { + ident: "Input", + span: #0 bytes(186..191), + }, + ], + span: #0 bytes(135..193), + }, +] diff --git a/src/test/ui/proc-macro/issue-75801.rs b/src/test/ui/proc-macro/issue-75801.rs new file mode 100644 index 0000000000000..b07cde0fabd74 --- /dev/null +++ b/src/test/ui/proc-macro/issue-75801.rs @@ -0,0 +1,19 @@ +// aux-build: issue-75801.rs + +// Regression test for #75801. + +#[macro_use] +extern crate issue_75801; + +macro_rules! foo { + ($arg:expr) => { + #[foo] + fn bar() { + let _bar: u32 = $arg; + } + }; +} + +foo!("baz"); //~ ERROR: mismatched types [E0308] + +fn main() {} diff --git a/src/test/ui/proc-macro/issue-75801.stderr b/src/test/ui/proc-macro/issue-75801.stderr new file mode 100644 index 0000000000000..ee0a9bd7783e6 --- /dev/null +++ b/src/test/ui/proc-macro/issue-75801.stderr @@ -0,0 +1,12 @@ +error[E0308]: mismatched types + --> $DIR/issue-75801.rs:17:6 + | +LL | let _bar: u32 = $arg; + | --- expected due to this +... +LL | foo!("baz"); + | ^^^^^ expected `u32`, found `&str` + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0308`. diff --git a/src/test/ui/proc-macro/issue-75930-derive-cfg.stdout b/src/test/ui/proc-macro/issue-75930-derive-cfg.stdout index 7cbc0c669a5dd..d7adc5331cd77 100644 --- a/src/test/ui/proc-macro/issue-75930-derive-cfg.stdout +++ b/src/test/ui/proc-macro/issue-75930-derive-cfg.stdout @@ -1279,152 +1279,152 @@ PRINT-DERIVE INPUT (DISPLAY): #[print_helper(a)] #[allow(dead_code)] #[print_hel [u8 ; { #[cfg(not(FALSE))] struct Inner ; match true - { #[allow(warnings)] false => { } _ => { } } ; #[print_helper(c)] + { #[allow(warnings)] false => { }, _ => { } } ; #[print_helper(c)] #[cfg(not(FALSE))] fn kept_fn() { # ! [cfg(not(FALSE))] let my_val = true ; } enum TupleEnum - { Foo(#[cfg(not(FALSE))] i32, u8), } struct + { Foo(#[cfg(not(FALSE))] i32, u8) } struct TupleStruct(#[cfg(not(FALSE))] i32, u8) ; 0 - }], #[print_helper(d)] fourth : B, + }], #[print_helper(d)] fourth : B } PRINT-DERIVE INPUT (DEBUG): TokenStream [ Punct { ch: '#', spacing: Alone, - span: $DIR/issue-75930-derive-cfg.rs:25:1: 72:2 (#0), + span: $DIR/issue-75930-derive-cfg.rs:19:1: 19:2 (#0), }, Group { delimiter: Bracket, stream: TokenStream [ Ident { ident: "print_helper", - span: $DIR/issue-75930-derive-cfg.rs:25:1: 72:2 (#0), + span: $DIR/issue-75930-derive-cfg.rs:19:3: 19:15 (#0), }, Group { delimiter: Parenthesis, stream: TokenStream [ Ident { ident: "a", - span: $DIR/issue-75930-derive-cfg.rs:25:1: 72:2 (#0), + span: $DIR/issue-75930-derive-cfg.rs:19:16: 19:17 (#0), }, ], - span: $DIR/issue-75930-derive-cfg.rs:25:1: 72:2 (#0), + span: $DIR/issue-75930-derive-cfg.rs:19:15: 19:18 (#0), }, ], - span: $DIR/issue-75930-derive-cfg.rs:25:1: 72:2 (#0), + span: $DIR/issue-75930-derive-cfg.rs:19:2: 19:19 (#0), }, Punct { ch: '#', spacing: Alone, - span: $DIR/issue-75930-derive-cfg.rs:25:1: 72:2 (#0), + span: $DIR/issue-75930-derive-cfg.rs:21:1: 21:2 (#0), }, Group { delimiter: Bracket, stream: TokenStream [ Ident { ident: "allow", - span: $DIR/issue-75930-derive-cfg.rs:25:1: 72:2 (#0), + span: $DIR/issue-75930-derive-cfg.rs:21:24: 21:29 (#0), }, Group { delimiter: Parenthesis, stream: TokenStream [ Ident { ident: "dead_code", - span: $DIR/issue-75930-derive-cfg.rs:25:1: 72:2 (#0), + span: $DIR/issue-75930-derive-cfg.rs:21:30: 21:39 (#0), }, ], - span: $DIR/issue-75930-derive-cfg.rs:25:1: 72:2 (#0), + span: $DIR/issue-75930-derive-cfg.rs:21:29: 21:40 (#0), }, ], - span: $DIR/issue-75930-derive-cfg.rs:25:1: 72:2 (#0), + span: $DIR/issue-75930-derive-cfg.rs:21:1: 21:2 (#0), }, Punct { ch: '#', spacing: Alone, - span: $DIR/issue-75930-derive-cfg.rs:25:1: 72:2 (#0), + span: $DIR/issue-75930-derive-cfg.rs:24:1: 24:2 (#0), }, Group { delimiter: Bracket, stream: TokenStream [ Ident { ident: "print_helper", - span: $DIR/issue-75930-derive-cfg.rs:25:1: 72:2 (#0), + span: $DIR/issue-75930-derive-cfg.rs:24:3: 24:15 (#0), }, Group { delimiter: Parenthesis, stream: TokenStream [ Ident { ident: "b", - span: $DIR/issue-75930-derive-cfg.rs:25:1: 72:2 (#0), + span: $DIR/issue-75930-derive-cfg.rs:24:16: 24:17 (#0), }, ], - span: $DIR/issue-75930-derive-cfg.rs:25:1: 72:2 (#0), + span: $DIR/issue-75930-derive-cfg.rs:24:15: 24:18 (#0), }, ], - span: $DIR/issue-75930-derive-cfg.rs:25:1: 72:2 (#0), + span: $DIR/issue-75930-derive-cfg.rs:24:2: 24:19 (#0), }, Ident { ident: "struct", - span: $DIR/issue-75930-derive-cfg.rs:25:1: 72:2 (#0), + span: $DIR/issue-75930-derive-cfg.rs:25:1: 25:7 (#0), }, Ident { ident: "Foo", - span: $DIR/issue-75930-derive-cfg.rs:25:1: 72:2 (#0), + span: $DIR/issue-75930-derive-cfg.rs:25:8: 25:11 (#0), }, Punct { ch: '<', - spacing: Alone, - span: $DIR/issue-75930-derive-cfg.rs:25:1: 72:2 (#0), + spacing: Joint, + span: $DIR/issue-75930-derive-cfg.rs:25:11: 25:12 (#0), }, Ident { ident: "B", - span: $DIR/issue-75930-derive-cfg.rs:25:1: 72:2 (#0), + span: $DIR/issue-75930-derive-cfg.rs:25:29: 25:30 (#0), }, Punct { ch: '>', spacing: Alone, - span: $DIR/issue-75930-derive-cfg.rs:25:1: 72:2 (#0), + span: $DIR/issue-75930-derive-cfg.rs:25:30: 25:31 (#0), }, Group { delimiter: Brace, stream: TokenStream [ Ident { ident: "second", - span: $DIR/issue-75930-derive-cfg.rs:25:1: 72:2 (#0), + span: $DIR/issue-75930-derive-cfg.rs:27:40: 27:46 (#0), }, Punct { ch: ':', spacing: Alone, - span: $DIR/issue-75930-derive-cfg.rs:25:1: 72:2 (#0), + span: $DIR/issue-75930-derive-cfg.rs:27:46: 27:47 (#0), }, Ident { ident: "bool", - span: $DIR/issue-75930-derive-cfg.rs:25:1: 72:2 (#0), + span: $DIR/issue-75930-derive-cfg.rs:27:48: 27:52 (#0), }, Punct { ch: ',', spacing: Alone, - span: $DIR/issue-75930-derive-cfg.rs:25:1: 72:2 (#0), + span: $DIR/issue-75930-derive-cfg.rs:27:52: 27:53 (#0), }, Ident { ident: "third", - span: $DIR/issue-75930-derive-cfg.rs:25:1: 72:2 (#0), + span: $DIR/issue-75930-derive-cfg.rs:28:5: 28:10 (#0), }, Punct { ch: ':', spacing: Alone, - span: $DIR/issue-75930-derive-cfg.rs:25:1: 72:2 (#0), + span: $DIR/issue-75930-derive-cfg.rs:28:10: 28:11 (#0), }, Group { delimiter: Bracket, stream: TokenStream [ Ident { ident: "u8", - span: $DIR/issue-75930-derive-cfg.rs:25:1: 72:2 (#0), + span: $DIR/issue-75930-derive-cfg.rs:28:13: 28:15 (#0), }, Punct { ch: ';', spacing: Alone, - span: $DIR/issue-75930-derive-cfg.rs:25:1: 72:2 (#0), + span: $DIR/issue-75930-derive-cfg.rs:28:15: 28:16 (#0), }, Group { delimiter: Brace, @@ -1432,58 +1432,58 @@ PRINT-DERIVE INPUT (DEBUG): TokenStream [ Punct { ch: '#', spacing: Alone, - span: $DIR/issue-75930-derive-cfg.rs:25:1: 72:2 (#0), + span: $DIR/issue-75930-derive-cfg.rs:30:9: 30:10 (#0), }, Group { delimiter: Bracket, stream: TokenStream [ Ident { ident: "cfg", - span: $DIR/issue-75930-derive-cfg.rs:25:1: 72:2 (#0), + span: $DIR/issue-75930-derive-cfg.rs:30:11: 30:14 (#0), }, Group { delimiter: Parenthesis, stream: TokenStream [ Ident { ident: "not", - span: $DIR/issue-75930-derive-cfg.rs:25:1: 72:2 (#0), + span: $DIR/issue-75930-derive-cfg.rs:30:15: 30:18 (#0), }, Group { delimiter: Parenthesis, stream: TokenStream [ Ident { ident: "FALSE", - span: $DIR/issue-75930-derive-cfg.rs:25:1: 72:2 (#0), + span: $DIR/issue-75930-derive-cfg.rs:30:19: 30:24 (#0), }, ], - span: $DIR/issue-75930-derive-cfg.rs:25:1: 72:2 (#0), + span: $DIR/issue-75930-derive-cfg.rs:30:18: 30:25 (#0), }, ], - span: $DIR/issue-75930-derive-cfg.rs:25:1: 72:2 (#0), + span: $DIR/issue-75930-derive-cfg.rs:30:14: 30:26 (#0), }, ], - span: $DIR/issue-75930-derive-cfg.rs:25:1: 72:2 (#0), + span: $DIR/issue-75930-derive-cfg.rs:30:10: 30:27 (#0), }, Ident { ident: "struct", - span: $DIR/issue-75930-derive-cfg.rs:25:1: 72:2 (#0), + span: $DIR/issue-75930-derive-cfg.rs:30:28: 30:34 (#0), }, Ident { ident: "Inner", - span: $DIR/issue-75930-derive-cfg.rs:25:1: 72:2 (#0), + span: $DIR/issue-75930-derive-cfg.rs:30:35: 30:40 (#0), }, Punct { ch: ';', spacing: Alone, - span: $DIR/issue-75930-derive-cfg.rs:25:1: 72:2 (#0), + span: $DIR/issue-75930-derive-cfg.rs:30:40: 30:41 (#0), }, Ident { ident: "match", - span: $DIR/issue-75930-derive-cfg.rs:25:1: 72:2 (#0), + span: $DIR/issue-75930-derive-cfg.rs:32:9: 32:14 (#0), }, Ident { ident: "true", - span: $DIR/issue-75930-derive-cfg.rs:25:1: 72:2 (#0), + span: $DIR/issue-75930-derive-cfg.rs:32:15: 32:19 (#0), }, Group { delimiter: Brace, @@ -1491,146 +1491,151 @@ PRINT-DERIVE INPUT (DEBUG): TokenStream [ Punct { ch: '#', spacing: Alone, - span: $DIR/issue-75930-derive-cfg.rs:25:1: 72:2 (#0), + span: $DIR/issue-75930-derive-cfg.rs:34:13: 34:14 (#0), }, Group { delimiter: Bracket, stream: TokenStream [ Ident { ident: "allow", - span: $DIR/issue-75930-derive-cfg.rs:25:1: 72:2 (#0), + span: $DIR/issue-75930-derive-cfg.rs:34:36: 34:41 (#0), }, Group { delimiter: Parenthesis, stream: TokenStream [ Ident { ident: "warnings", - span: $DIR/issue-75930-derive-cfg.rs:25:1: 72:2 (#0), + span: $DIR/issue-75930-derive-cfg.rs:34:42: 34:50 (#0), }, ], - span: $DIR/issue-75930-derive-cfg.rs:25:1: 72:2 (#0), + span: $DIR/issue-75930-derive-cfg.rs:34:41: 34:51 (#0), }, ], - span: $DIR/issue-75930-derive-cfg.rs:25:1: 72:2 (#0), + span: $DIR/issue-75930-derive-cfg.rs:34:13: 34:14 (#0), }, Ident { ident: "false", - span: $DIR/issue-75930-derive-cfg.rs:25:1: 72:2 (#0), + span: $DIR/issue-75930-derive-cfg.rs:34:54: 34:59 (#0), }, Punct { ch: '=', spacing: Joint, - span: $DIR/issue-75930-derive-cfg.rs:25:1: 72:2 (#0), + span: $DIR/issue-75930-derive-cfg.rs:34:60: 34:62 (#0), }, Punct { ch: '>', spacing: Alone, - span: $DIR/issue-75930-derive-cfg.rs:25:1: 72:2 (#0), + span: $DIR/issue-75930-derive-cfg.rs:34:60: 34:62 (#0), }, Group { delimiter: Brace, stream: TokenStream [], - span: $DIR/issue-75930-derive-cfg.rs:25:1: 72:2 (#0), + span: $DIR/issue-75930-derive-cfg.rs:34:63: 34:65 (#0), + }, + Punct { + ch: ',', + spacing: Alone, + span: $DIR/issue-75930-derive-cfg.rs:34:65: 34:66 (#0), }, Ident { ident: "_", - span: $DIR/issue-75930-derive-cfg.rs:25:1: 72:2 (#0), + span: $DIR/issue-75930-derive-cfg.rs:35:13: 35:14 (#0), }, Punct { ch: '=', spacing: Joint, - span: $DIR/issue-75930-derive-cfg.rs:25:1: 72:2 (#0), + span: $DIR/issue-75930-derive-cfg.rs:35:15: 35:17 (#0), }, Punct { ch: '>', spacing: Alone, - span: $DIR/issue-75930-derive-cfg.rs:25:1: 72:2 (#0), + span: $DIR/issue-75930-derive-cfg.rs:35:15: 35:17 (#0), }, Group { delimiter: Brace, stream: TokenStream [], - span: $DIR/issue-75930-derive-cfg.rs:25:1: 72:2 (#0), + span: $DIR/issue-75930-derive-cfg.rs:35:18: 35:20 (#0), }, ], - span: $DIR/issue-75930-derive-cfg.rs:25:1: 72:2 (#0), + span: $DIR/issue-75930-derive-cfg.rs:32:20: 36:10 (#0), }, Punct { ch: ';', spacing: Alone, - span: $DIR/issue-75930-derive-cfg.rs:25:1: 72:2 (#0), + span: $DIR/issue-75930-derive-cfg.rs:36:10: 36:11 (#0), }, Punct { ch: '#', spacing: Alone, - span: $DIR/issue-75930-derive-cfg.rs:25:1: 72:2 (#0), + span: $DIR/issue-75930-derive-cfg.rs:43:9: 43:10 (#0), }, Group { delimiter: Bracket, stream: TokenStream [ Ident { ident: "print_helper", - span: $DIR/issue-75930-derive-cfg.rs:25:1: 72:2 (#0), + span: $DIR/issue-75930-derive-cfg.rs:43:11: 43:23 (#0), }, Group { delimiter: Parenthesis, stream: TokenStream [ Ident { ident: "c", - span: $DIR/issue-75930-derive-cfg.rs:25:1: 72:2 (#0), + span: $DIR/issue-75930-derive-cfg.rs:43:24: 43:25 (#0), }, ], - span: $DIR/issue-75930-derive-cfg.rs:25:1: 72:2 (#0), + span: $DIR/issue-75930-derive-cfg.rs:43:23: 43:26 (#0), }, ], - span: $DIR/issue-75930-derive-cfg.rs:25:1: 72:2 (#0), + span: $DIR/issue-75930-derive-cfg.rs:43:10: 43:27 (#0), }, Punct { ch: '#', spacing: Alone, - span: $DIR/issue-75930-derive-cfg.rs:25:1: 72:2 (#0), + span: $DIR/issue-75930-derive-cfg.rs:43:28: 43:29 (#0), }, Group { delimiter: Bracket, stream: TokenStream [ Ident { ident: "cfg", - span: $DIR/issue-75930-derive-cfg.rs:25:1: 72:2 (#0), + span: $DIR/issue-75930-derive-cfg.rs:43:30: 43:33 (#0), }, Group { delimiter: Parenthesis, stream: TokenStream [ Ident { ident: "not", - span: $DIR/issue-75930-derive-cfg.rs:25:1: 72:2 (#0), + span: $DIR/issue-75930-derive-cfg.rs:43:34: 43:37 (#0), }, Group { delimiter: Parenthesis, stream: TokenStream [ Ident { ident: "FALSE", - span: $DIR/issue-75930-derive-cfg.rs:25:1: 72:2 (#0), + span: $DIR/issue-75930-derive-cfg.rs:43:38: 43:43 (#0), }, ], - span: $DIR/issue-75930-derive-cfg.rs:25:1: 72:2 (#0), + span: $DIR/issue-75930-derive-cfg.rs:43:37: 43:44 (#0), }, ], - span: $DIR/issue-75930-derive-cfg.rs:25:1: 72:2 (#0), + span: $DIR/issue-75930-derive-cfg.rs:43:33: 43:45 (#0), }, ], - span: $DIR/issue-75930-derive-cfg.rs:25:1: 72:2 (#0), + span: $DIR/issue-75930-derive-cfg.rs:43:29: 43:46 (#0), }, Ident { ident: "fn", - span: $DIR/issue-75930-derive-cfg.rs:25:1: 72:2 (#0), + span: $DIR/issue-75930-derive-cfg.rs:43:47: 43:49 (#0), }, Ident { ident: "kept_fn", - span: $DIR/issue-75930-derive-cfg.rs:25:1: 72:2 (#0), + span: $DIR/issue-75930-derive-cfg.rs:43:50: 43:57 (#0), }, Group { delimiter: Parenthesis, stream: TokenStream [], - span: $DIR/issue-75930-derive-cfg.rs:25:1: 72:2 (#0), + span: $DIR/issue-75930-derive-cfg.rs:43:57: 43:59 (#0), }, Group { delimiter: Brace, @@ -1638,82 +1643,82 @@ PRINT-DERIVE INPUT (DEBUG): TokenStream [ Punct { ch: '#', spacing: Joint, - span: $DIR/issue-75930-derive-cfg.rs:25:1: 72:2 (#0), + span: $DIR/issue-75930-derive-cfg.rs:44:13: 44:14 (#0), }, Punct { ch: '!', spacing: Alone, - span: $DIR/issue-75930-derive-cfg.rs:25:1: 72:2 (#0), + span: $DIR/issue-75930-derive-cfg.rs:44:14: 44:15 (#0), }, Group { delimiter: Bracket, stream: TokenStream [ Ident { ident: "cfg", - span: $DIR/issue-75930-derive-cfg.rs:25:1: 72:2 (#0), + span: $DIR/issue-75930-derive-cfg.rs:44:16: 44:19 (#0), }, Group { delimiter: Parenthesis, stream: TokenStream [ Ident { ident: "not", - span: $DIR/issue-75930-derive-cfg.rs:25:1: 72:2 (#0), + span: $DIR/issue-75930-derive-cfg.rs:44:20: 44:23 (#0), }, Group { delimiter: Parenthesis, stream: TokenStream [ Ident { ident: "FALSE", - span: $DIR/issue-75930-derive-cfg.rs:25:1: 72:2 (#0), + span: $DIR/issue-75930-derive-cfg.rs:44:24: 44:29 (#0), }, ], - span: $DIR/issue-75930-derive-cfg.rs:25:1: 72:2 (#0), + span: $DIR/issue-75930-derive-cfg.rs:44:23: 44:30 (#0), }, ], - span: $DIR/issue-75930-derive-cfg.rs:25:1: 72:2 (#0), + span: $DIR/issue-75930-derive-cfg.rs:44:19: 44:31 (#0), }, ], - span: $DIR/issue-75930-derive-cfg.rs:25:1: 72:2 (#0), + span: $DIR/issue-75930-derive-cfg.rs:44:15: 44:32 (#0), }, Ident { ident: "let", - span: $DIR/issue-75930-derive-cfg.rs:25:1: 72:2 (#0), + span: $DIR/issue-75930-derive-cfg.rs:45:13: 45:16 (#0), }, Ident { ident: "my_val", - span: $DIR/issue-75930-derive-cfg.rs:25:1: 72:2 (#0), + span: $DIR/issue-75930-derive-cfg.rs:45:17: 45:23 (#0), }, Punct { ch: '=', spacing: Alone, - span: $DIR/issue-75930-derive-cfg.rs:25:1: 72:2 (#0), + span: $DIR/issue-75930-derive-cfg.rs:45:24: 45:25 (#0), }, Ident { ident: "true", - span: $DIR/issue-75930-derive-cfg.rs:25:1: 72:2 (#0), + span: $DIR/issue-75930-derive-cfg.rs:45:26: 45:30 (#0), }, Punct { ch: ';', spacing: Alone, - span: $DIR/issue-75930-derive-cfg.rs:25:1: 72:2 (#0), + span: $DIR/issue-75930-derive-cfg.rs:45:30: 45:31 (#0), }, ], - span: $DIR/issue-75930-derive-cfg.rs:25:1: 72:2 (#0), + span: $DIR/issue-75930-derive-cfg.rs:43:60: 46:10 (#0), }, Ident { ident: "enum", - span: $DIR/issue-75930-derive-cfg.rs:25:1: 72:2 (#0), + span: $DIR/issue-75930-derive-cfg.rs:48:9: 48:13 (#0), }, Ident { ident: "TupleEnum", - span: $DIR/issue-75930-derive-cfg.rs:25:1: 72:2 (#0), + span: $DIR/issue-75930-derive-cfg.rs:48:14: 48:23 (#0), }, Group { delimiter: Brace, stream: TokenStream [ Ident { ident: "Foo", - span: $DIR/issue-75930-derive-cfg.rs:25:1: 72:2 (#0), + span: $DIR/issue-75930-derive-cfg.rs:49:13: 49:16 (#0), }, Group { delimiter: Parenthesis, @@ -1721,69 +1726,64 @@ PRINT-DERIVE INPUT (DEBUG): TokenStream [ Punct { ch: '#', spacing: Alone, - span: $DIR/issue-75930-derive-cfg.rs:25:1: 72:2 (#0), + span: $DIR/issue-75930-derive-cfg.rs:52:17: 52:18 (#0), }, Group { delimiter: Bracket, stream: TokenStream [ Ident { ident: "cfg", - span: $DIR/issue-75930-derive-cfg.rs:25:1: 72:2 (#0), + span: $DIR/issue-75930-derive-cfg.rs:52:19: 52:22 (#0), }, Group { delimiter: Parenthesis, stream: TokenStream [ Ident { ident: "not", - span: $DIR/issue-75930-derive-cfg.rs:25:1: 72:2 (#0), + span: $DIR/issue-75930-derive-cfg.rs:52:23: 52:26 (#0), }, Group { delimiter: Parenthesis, stream: TokenStream [ Ident { ident: "FALSE", - span: $DIR/issue-75930-derive-cfg.rs:25:1: 72:2 (#0), + span: $DIR/issue-75930-derive-cfg.rs:52:27: 52:32 (#0), }, ], - span: $DIR/issue-75930-derive-cfg.rs:25:1: 72:2 (#0), + span: $DIR/issue-75930-derive-cfg.rs:52:26: 52:33 (#0), }, ], - span: $DIR/issue-75930-derive-cfg.rs:25:1: 72:2 (#0), + span: $DIR/issue-75930-derive-cfg.rs:52:22: 52:34 (#0), }, ], - span: $DIR/issue-75930-derive-cfg.rs:25:1: 72:2 (#0), + span: $DIR/issue-75930-derive-cfg.rs:52:18: 52:35 (#0), }, Ident { ident: "i32", - span: $DIR/issue-75930-derive-cfg.rs:25:1: 72:2 (#0), + span: $DIR/issue-75930-derive-cfg.rs:52:36: 52:39 (#0), }, Punct { ch: ',', spacing: Alone, - span: $DIR/issue-75930-derive-cfg.rs:25:1: 72:2 (#0), + span: $DIR/issue-75930-derive-cfg.rs:52:39: 52:40 (#0), }, Ident { ident: "u8", - span: $DIR/issue-75930-derive-cfg.rs:25:1: 72:2 (#0), + span: $DIR/issue-75930-derive-cfg.rs:53:39: 53:41 (#0), }, ], - span: $DIR/issue-75930-derive-cfg.rs:25:1: 72:2 (#0), - }, - Punct { - ch: ',', - spacing: Alone, - span: $DIR/issue-75930-derive-cfg.rs:25:1: 72:2 (#0), + span: $DIR/issue-75930-derive-cfg.rs:49:16: 54:14 (#0), }, ], - span: $DIR/issue-75930-derive-cfg.rs:25:1: 72:2 (#0), + span: $DIR/issue-75930-derive-cfg.rs:48:24: 55:10 (#0), }, Ident { ident: "struct", - span: $DIR/issue-75930-derive-cfg.rs:25:1: 72:2 (#0), + span: $DIR/issue-75930-derive-cfg.rs:57:9: 57:15 (#0), }, Ident { ident: "TupleStruct", - span: $DIR/issue-75930-derive-cfg.rs:25:1: 72:2 (#0), + span: $DIR/issue-75930-derive-cfg.rs:57:16: 57:27 (#0), }, Group { delimiter: Parenthesis, @@ -1791,120 +1791,115 @@ PRINT-DERIVE INPUT (DEBUG): TokenStream [ Punct { ch: '#', spacing: Alone, - span: $DIR/issue-75930-derive-cfg.rs:25:1: 72:2 (#0), + span: $DIR/issue-75930-derive-cfg.rs:59:13: 59:14 (#0), }, Group { delimiter: Bracket, stream: TokenStream [ Ident { ident: "cfg", - span: $DIR/issue-75930-derive-cfg.rs:25:1: 72:2 (#0), + span: $DIR/issue-75930-derive-cfg.rs:59:15: 59:18 (#0), }, Group { delimiter: Parenthesis, stream: TokenStream [ Ident { ident: "not", - span: $DIR/issue-75930-derive-cfg.rs:25:1: 72:2 (#0), + span: $DIR/issue-75930-derive-cfg.rs:59:19: 59:22 (#0), }, Group { delimiter: Parenthesis, stream: TokenStream [ Ident { ident: "FALSE", - span: $DIR/issue-75930-derive-cfg.rs:25:1: 72:2 (#0), + span: $DIR/issue-75930-derive-cfg.rs:59:23: 59:28 (#0), }, ], - span: $DIR/issue-75930-derive-cfg.rs:25:1: 72:2 (#0), + span: $DIR/issue-75930-derive-cfg.rs:59:22: 59:29 (#0), }, ], - span: $DIR/issue-75930-derive-cfg.rs:25:1: 72:2 (#0), + span: $DIR/issue-75930-derive-cfg.rs:59:18: 59:30 (#0), }, ], - span: $DIR/issue-75930-derive-cfg.rs:25:1: 72:2 (#0), + span: $DIR/issue-75930-derive-cfg.rs:59:14: 59:31 (#0), }, Ident { ident: "i32", - span: $DIR/issue-75930-derive-cfg.rs:25:1: 72:2 (#0), + span: $DIR/issue-75930-derive-cfg.rs:59:32: 59:35 (#0), }, Punct { ch: ',', spacing: Alone, - span: $DIR/issue-75930-derive-cfg.rs:25:1: 72:2 (#0), + span: $DIR/issue-75930-derive-cfg.rs:59:35: 59:36 (#0), }, Ident { ident: "u8", - span: $DIR/issue-75930-derive-cfg.rs:25:1: 72:2 (#0), + span: $DIR/issue-75930-derive-cfg.rs:61:13: 61:15 (#0), }, ], - span: $DIR/issue-75930-derive-cfg.rs:25:1: 72:2 (#0), + span: $DIR/issue-75930-derive-cfg.rs:57:27: 62:10 (#0), }, Punct { ch: ';', spacing: Alone, - span: $DIR/issue-75930-derive-cfg.rs:25:1: 72:2 (#0), + span: $DIR/issue-75930-derive-cfg.rs:62:10: 62:11 (#0), }, Literal { kind: Integer, symbol: "0", suffix: None, - span: $DIR/issue-75930-derive-cfg.rs:25:1: 72:2 (#0), + span: $DIR/issue-75930-derive-cfg.rs:68:9: 68:10 (#0), }, ], - span: $DIR/issue-75930-derive-cfg.rs:25:1: 72:2 (#0), + span: $DIR/issue-75930-derive-cfg.rs:28:17: 69:6 (#0), }, ], - span: $DIR/issue-75930-derive-cfg.rs:25:1: 72:2 (#0), + span: $DIR/issue-75930-derive-cfg.rs:28:12: 69:7 (#0), }, Punct { ch: ',', spacing: Alone, - span: $DIR/issue-75930-derive-cfg.rs:25:1: 72:2 (#0), + span: $DIR/issue-75930-derive-cfg.rs:69:7: 69:8 (#0), }, Punct { ch: '#', spacing: Alone, - span: $DIR/issue-75930-derive-cfg.rs:25:1: 72:2 (#0), + span: $DIR/issue-75930-derive-cfg.rs:70:5: 70:6 (#0), }, Group { delimiter: Bracket, stream: TokenStream [ Ident { ident: "print_helper", - span: $DIR/issue-75930-derive-cfg.rs:25:1: 72:2 (#0), + span: $DIR/issue-75930-derive-cfg.rs:70:7: 70:19 (#0), }, Group { delimiter: Parenthesis, stream: TokenStream [ Ident { ident: "d", - span: $DIR/issue-75930-derive-cfg.rs:25:1: 72:2 (#0), + span: $DIR/issue-75930-derive-cfg.rs:70:20: 70:21 (#0), }, ], - span: $DIR/issue-75930-derive-cfg.rs:25:1: 72:2 (#0), + span: $DIR/issue-75930-derive-cfg.rs:70:19: 70:22 (#0), }, ], - span: $DIR/issue-75930-derive-cfg.rs:25:1: 72:2 (#0), + span: $DIR/issue-75930-derive-cfg.rs:70:6: 70:23 (#0), }, Ident { ident: "fourth", - span: $DIR/issue-75930-derive-cfg.rs:25:1: 72:2 (#0), + span: $DIR/issue-75930-derive-cfg.rs:71:5: 71:11 (#0), }, Punct { ch: ':', spacing: Alone, - span: $DIR/issue-75930-derive-cfg.rs:25:1: 72:2 (#0), + span: $DIR/issue-75930-derive-cfg.rs:71:11: 71:12 (#0), }, Ident { ident: "B", - span: $DIR/issue-75930-derive-cfg.rs:25:1: 72:2 (#0), - }, - Punct { - ch: ',', - spacing: Alone, - span: $DIR/issue-75930-derive-cfg.rs:25:1: 72:2 (#0), + span: $DIR/issue-75930-derive-cfg.rs:71:13: 71:14 (#0), }, ], - span: $DIR/issue-75930-derive-cfg.rs:25:1: 72:2 (#0), + span: $DIR/issue-75930-derive-cfg.rs:25:32: 72:2 (#0), }, ] diff --git a/src/test/ui/proc-macro/issue-78675-captured-inner-attrs.stdout b/src/test/ui/proc-macro/issue-78675-captured-inner-attrs.stdout index 40da5aa93bfa8..607c2954c7cf5 100644 --- a/src/test/ui/proc-macro/issue-78675-captured-inner-attrs.stdout +++ b/src/test/ui/proc-macro/issue-78675-captured-inner-attrs.stdout @@ -1,6 +1,7 @@ PRINT-BANG INPUT (DISPLAY): foo ! { #[fake_attr] mod bar { #![doc = r" Foo"] } } +PRINT-BANG DEEP-RE-COLLECTED (DISPLAY): foo ! { #[fake_attr] mod bar { # ! [doc = r" Foo"] } } PRINT-BANG INPUT (DEBUG): TokenStream [ Ident { ident: "foo", @@ -34,48 +35,48 @@ PRINT-BANG INPUT (DEBUG): TokenStream [ stream: TokenStream [ Ident { ident: "mod", - span: $DIR/issue-78675-captured-inner-attrs.rs:27:5: 29:6 (#0), + span: $DIR/issue-78675-captured-inner-attrs.rs:27:5: 27:8 (#0), }, Ident { ident: "bar", - span: $DIR/issue-78675-captured-inner-attrs.rs:27:5: 29:6 (#0), + span: $DIR/issue-78675-captured-inner-attrs.rs:27:9: 27:12 (#0), }, Group { delimiter: Brace, stream: TokenStream [ Punct { ch: '#', - spacing: Joint, - span: $DIR/issue-78675-captured-inner-attrs.rs:27:5: 29:6 (#0), + spacing: Alone, + span: $DIR/issue-78675-captured-inner-attrs.rs:28:9: 28:16 (#0), }, Punct { ch: '!', spacing: Alone, - span: $DIR/issue-78675-captured-inner-attrs.rs:27:5: 29:6 (#0), + span: $DIR/issue-78675-captured-inner-attrs.rs:28:9: 28:16 (#0), }, Group { delimiter: Bracket, stream: TokenStream [ Ident { ident: "doc", - span: $DIR/issue-78675-captured-inner-attrs.rs:27:5: 29:6 (#0), + span: $DIR/issue-78675-captured-inner-attrs.rs:28:9: 28:16 (#0), }, Punct { ch: '=', spacing: Alone, - span: $DIR/issue-78675-captured-inner-attrs.rs:27:5: 29:6 (#0), + span: $DIR/issue-78675-captured-inner-attrs.rs:28:9: 28:16 (#0), }, Literal { kind: StrRaw(0), symbol: " Foo", suffix: None, - span: $DIR/issue-78675-captured-inner-attrs.rs:27:5: 29:6 (#0), + span: $DIR/issue-78675-captured-inner-attrs.rs:28:9: 28:16 (#0), }, ], - span: $DIR/issue-78675-captured-inner-attrs.rs:27:5: 29:6 (#0), + span: $DIR/issue-78675-captured-inner-attrs.rs:28:9: 28:16 (#0), }, ], - span: $DIR/issue-78675-captured-inner-attrs.rs:27:5: 29:6 (#0), + span: $DIR/issue-78675-captured-inner-attrs.rs:27:13: 29:6 (#0), }, ], span: $DIR/issue-78675-captured-inner-attrs.rs:22:13: 22:18 (#4), diff --git a/src/test/ui/proc-macro/issue-79825.rs b/src/test/ui/proc-macro/issue-79825.rs new file mode 100644 index 0000000000000..f628469ce3a62 --- /dev/null +++ b/src/test/ui/proc-macro/issue-79825.rs @@ -0,0 +1,10 @@ +// check-pass +// aux-build:issue-79825.rs +#![feature(trait_alias)] + +extern crate issue_79825; + +#[issue_79825::assert_input] +trait Alias = Sized; + +fn main() {} diff --git a/src/test/ui/proc-macro/issue-81555.rs b/src/test/ui/proc-macro/issue-81555.rs new file mode 100644 index 0000000000000..693f1f7dc39ff --- /dev/null +++ b/src/test/ui/proc-macro/issue-81555.rs @@ -0,0 +1,15 @@ +// check-pass +// aux-build:test-macros.rs +#![feature(stmt_expr_attributes, proc_macro_hygiene)] + +extern crate test_macros; + +use test_macros::identity_attr; + +#[identity_attr] +fn main() { + let _x; + let y = (); + #[identity_attr] + _x = y; +} diff --git a/src/test/ui/proc-macro/issue-83469-global-alloc-invalid-stmt.rs b/src/test/ui/proc-macro/issue-83469-global-alloc-invalid-stmt.rs new file mode 100644 index 0000000000000..25adc5d25780b --- /dev/null +++ b/src/test/ui/proc-macro/issue-83469-global-alloc-invalid-stmt.rs @@ -0,0 +1,10 @@ +// Regression test for issue #83469 +// Ensures that we recover from `#[global_alloc]` on an invalid +// stmt without an ICE + +fn outer() { + #[global_allocator] + fn inner() {} //~ ERROR allocators must be statics +} + +fn main() {} diff --git a/src/test/ui/proc-macro/issue-83469-global-alloc-invalid-stmt.stderr b/src/test/ui/proc-macro/issue-83469-global-alloc-invalid-stmt.stderr new file mode 100644 index 0000000000000..ec0e3c4c7546f --- /dev/null +++ b/src/test/ui/proc-macro/issue-83469-global-alloc-invalid-stmt.stderr @@ -0,0 +1,8 @@ +error: allocators must be statics + --> $DIR/issue-83469-global-alloc-invalid-stmt.rs:7:5 + | +LL | fn inner() {} + | ^^^^^^^^^^^^^ + +error: aborting due to previous error + diff --git a/src/test/ui/proc-macro/issue-83510.rs b/src/test/ui/proc-macro/issue-83510.rs new file mode 100644 index 0000000000000..2b1aec4df0be3 --- /dev/null +++ b/src/test/ui/proc-macro/issue-83510.rs @@ -0,0 +1,11 @@ +// aux-build: issue-83510.rs + +extern crate issue_83510; + +issue_83510::dance_like_you_want_to_ice!(); +//~^ ERROR: cannot find type `Foo` in this scope +//~| ERROR: expected trait, found struct `Box` +//~| ERROR: cannot find trait `Baz` in this scope +//~| ERROR: inherent associated types are unstable + +fn main() {} diff --git a/src/test/ui/proc-macro/issue-83510.stderr b/src/test/ui/proc-macro/issue-83510.stderr new file mode 100644 index 0000000000000..e0803550906db --- /dev/null +++ b/src/test/ui/proc-macro/issue-83510.stderr @@ -0,0 +1,38 @@ +error[E0412]: cannot find type `Foo` in this scope + --> $DIR/issue-83510.rs:5:1 + | +LL | issue_83510::dance_like_you_want_to_ice!(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not found in this scope + | + = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0404]: expected trait, found struct `Box` + --> $DIR/issue-83510.rs:5:1 + | +LL | issue_83510::dance_like_you_want_to_ice!(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not a trait + | + = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0405]: cannot find trait `Baz` in this scope + --> $DIR/issue-83510.rs:5:1 + | +LL | issue_83510::dance_like_you_want_to_ice!(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not found in this scope + | + = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0658]: inherent associated types are unstable + --> $DIR/issue-83510.rs:5:1 + | +LL | issue_83510::dance_like_you_want_to_ice!(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #8995 <https://github.com/rust-lang/rust/issues/8995> for more information + = help: add `#![feature(inherent_associated_types)]` to the crate attributes to enable + = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: aborting due to 4 previous errors + +Some errors have detailed explanations: E0404, E0405, E0412, E0658. +For more information about an error, try `rustc --explain E0404`. diff --git a/src/test/ui/proc-macro/macro-rules-derive-cfg.stdout b/src/test/ui/proc-macro/macro-rules-derive-cfg.stdout index 5db18590bdf82..a0b0cbb19e4db 100644 --- a/src/test/ui/proc-macro/macro-rules-derive-cfg.stdout +++ b/src/test/ui/proc-macro/macro-rules-derive-cfg.stdout @@ -5,172 +5,167 @@ PRINT-DERIVE INPUT (DISPLAY): struct Foo { let a = #[rustc_dummy(first)] #[rustc_dummy(second)] { # ! [allow(unused)] 30 } ; 0 - }], + }] } PRINT-DERIVE INPUT (DEBUG): TokenStream [ Ident { ident: "struct", - span: $DIR/macro-rules-derive-cfg.rs:17:9: 22:10 (#4), + span: $DIR/macro-rules-derive-cfg.rs:17:9: 17:15 (#4), }, Ident { ident: "Foo", - span: $DIR/macro-rules-derive-cfg.rs:17:9: 22:10 (#4), + span: $DIR/macro-rules-derive-cfg.rs:17:16: 17:19 (#4), }, Group { delimiter: Brace, stream: TokenStream [ Ident { ident: "val", - span: $DIR/macro-rules-derive-cfg.rs:17:9: 22:10 (#4), + span: $DIR/macro-rules-derive-cfg.rs:18:13: 18:16 (#4), }, Punct { ch: ':', spacing: Alone, - span: $DIR/macro-rules-derive-cfg.rs:17:9: 22:10 (#4), + span: $DIR/macro-rules-derive-cfg.rs:18:16: 18:17 (#4), }, Group { delimiter: Bracket, stream: TokenStream [ Ident { ident: "bool", - span: $DIR/macro-rules-derive-cfg.rs:17:9: 22:10 (#4), + span: $DIR/macro-rules-derive-cfg.rs:18:19: 18:23 (#4), }, Punct { ch: ';', spacing: Alone, - span: $DIR/macro-rules-derive-cfg.rs:17:9: 22:10 (#4), + span: $DIR/macro-rules-derive-cfg.rs:18:23: 18:24 (#4), }, Group { delimiter: Brace, stream: TokenStream [ Ident { ident: "let", - span: $DIR/macro-rules-derive-cfg.rs:17:9: 22:10 (#4), + span: $DIR/macro-rules-derive-cfg.rs:19:17: 19:20 (#4), }, Ident { ident: "a", - span: $DIR/macro-rules-derive-cfg.rs:17:9: 22:10 (#4), + span: $DIR/macro-rules-derive-cfg.rs:19:21: 19:22 (#4), }, Punct { ch: '=', spacing: Alone, - span: $DIR/macro-rules-derive-cfg.rs:17:9: 22:10 (#4), + span: $DIR/macro-rules-derive-cfg.rs:19:23: 19:24 (#4), }, Punct { ch: '#', spacing: Alone, - span: $DIR/macro-rules-derive-cfg.rs:17:9: 22:10 (#4), + span: $DIR/macro-rules-derive-cfg.rs:19:25: 19:26 (#4), }, Group { delimiter: Bracket, stream: TokenStream [ Ident { ident: "rustc_dummy", - span: $DIR/macro-rules-derive-cfg.rs:17:9: 22:10 (#4), + span: $DIR/macro-rules-derive-cfg.rs:19:48: 19:59 (#4), }, Group { delimiter: Parenthesis, stream: TokenStream [ Ident { ident: "first", - span: $DIR/macro-rules-derive-cfg.rs:17:9: 22:10 (#4), + span: $DIR/macro-rules-derive-cfg.rs:19:60: 19:65 (#4), }, ], - span: $DIR/macro-rules-derive-cfg.rs:17:9: 22:10 (#4), + span: $DIR/macro-rules-derive-cfg.rs:19:59: 19:66 (#4), }, ], - span: $DIR/macro-rules-derive-cfg.rs:17:9: 22:10 (#4), + span: $DIR/macro-rules-derive-cfg.rs:19:25: 19:26 (#4), }, Punct { ch: '#', spacing: Alone, - span: $DIR/macro-rules-derive-cfg.rs:17:9: 22:10 (#4), + span: $DIR/macro-rules-derive-cfg.rs:26:13: 26:14 (#0), }, Group { delimiter: Bracket, stream: TokenStream [ Ident { ident: "rustc_dummy", - span: $DIR/macro-rules-derive-cfg.rs:17:9: 22:10 (#4), + span: $DIR/macro-rules-derive-cfg.rs:26:36: 26:47 (#0), }, Group { delimiter: Parenthesis, stream: TokenStream [ Ident { ident: "second", - span: $DIR/macro-rules-derive-cfg.rs:17:9: 22:10 (#4), + span: $DIR/macro-rules-derive-cfg.rs:26:48: 26:54 (#0), }, ], - span: $DIR/macro-rules-derive-cfg.rs:17:9: 22:10 (#4), + span: $DIR/macro-rules-derive-cfg.rs:26:47: 26:55 (#0), }, ], - span: $DIR/macro-rules-derive-cfg.rs:17:9: 22:10 (#4), + span: $DIR/macro-rules-derive-cfg.rs:26:13: 26:14 (#0), }, Group { delimiter: Brace, stream: TokenStream [ Punct { ch: '#', - spacing: Joint, - span: $DIR/macro-rules-derive-cfg.rs:17:9: 22:10 (#4), + spacing: Alone, + span: $DIR/macro-rules-derive-cfg.rs:27:5: 27:6 (#0), }, Punct { ch: '!', spacing: Alone, - span: $DIR/macro-rules-derive-cfg.rs:17:9: 22:10 (#4), + span: $DIR/macro-rules-derive-cfg.rs:27:6: 27:7 (#0), }, Group { delimiter: Bracket, stream: TokenStream [ Ident { ident: "allow", - span: $DIR/macro-rules-derive-cfg.rs:17:9: 22:10 (#4), + span: $DIR/macro-rules-derive-cfg.rs:27:29: 27:34 (#0), }, Group { delimiter: Parenthesis, stream: TokenStream [ Ident { ident: "unused", - span: $DIR/macro-rules-derive-cfg.rs:17:9: 22:10 (#4), + span: $DIR/macro-rules-derive-cfg.rs:27:35: 27:41 (#0), }, ], - span: $DIR/macro-rules-derive-cfg.rs:17:9: 22:10 (#4), + span: $DIR/macro-rules-derive-cfg.rs:27:34: 27:42 (#0), }, ], - span: $DIR/macro-rules-derive-cfg.rs:17:9: 22:10 (#4), + span: $DIR/macro-rules-derive-cfg.rs:27:5: 27:6 (#0), }, Literal { kind: Integer, symbol: "30", suffix: None, - span: $DIR/macro-rules-derive-cfg.rs:17:9: 22:10 (#4), + span: $DIR/macro-rules-derive-cfg.rs:28:5: 28:7 (#0), }, ], - span: $DIR/macro-rules-derive-cfg.rs:17:9: 22:10 (#4), + span: $DIR/macro-rules-derive-cfg.rs:26:58: 29:2 (#0), }, Punct { ch: ';', spacing: Alone, - span: $DIR/macro-rules-derive-cfg.rs:17:9: 22:10 (#4), + span: $DIR/macro-rules-derive-cfg.rs:19:74: 19:75 (#4), }, Literal { kind: Integer, symbol: "0", suffix: None, - span: $DIR/macro-rules-derive-cfg.rs:17:9: 22:10 (#4), + span: $DIR/macro-rules-derive-cfg.rs:20:17: 20:18 (#4), }, ], - span: $DIR/macro-rules-derive-cfg.rs:17:9: 22:10 (#4), + span: $DIR/macro-rules-derive-cfg.rs:18:25: 21:14 (#4), }, ], - span: $DIR/macro-rules-derive-cfg.rs:17:9: 22:10 (#4), - }, - Punct { - ch: ',', - spacing: Alone, - span: $DIR/macro-rules-derive-cfg.rs:17:9: 22:10 (#4), + span: $DIR/macro-rules-derive-cfg.rs:18:18: 21:15 (#4), }, ], - span: $DIR/macro-rules-derive-cfg.rs:17:9: 22:10 (#4), + span: $DIR/macro-rules-derive-cfg.rs:17:20: 22:10 (#4), }, ] diff --git a/src/test/ui/proc-macro/meta-macro-hygiene.rs b/src/test/ui/proc-macro/meta-macro-hygiene.rs index 7e839f747f339..2536b2fa9021f 100644 --- a/src/test/ui/proc-macro/meta-macro-hygiene.rs +++ b/src/test/ui/proc-macro/meta-macro-hygiene.rs @@ -1,4 +1,3 @@ -// ignore-tidy-linelength // aux-build:make-macro.rs // aux-build:meta-macro.rs // edition:2018 diff --git a/src/test/ui/proc-macro/meta-macro-hygiene.stdout b/src/test/ui/proc-macro/meta-macro-hygiene.stdout index a067b7b5411dd..b7a37ab10ed5c 100644 --- a/src/test/ui/proc-macro/meta-macro-hygiene.stdout +++ b/src/test/ui/proc-macro/meta-macro-hygiene.stdout @@ -1,8 +1,7 @@ Def site: $DIR/auxiliary/make-macro.rs:7:9: 7:56 (#5) -Input: TokenStream [Ident { ident: "$crate", span: $DIR/meta-macro-hygiene.rs:24:37: 24:43 (#4) }, Punct { ch: ':', spacing: Joint, span: $DIR/meta-macro-hygiene.rs:24:43: 24:45 (#4) }, Punct { ch: ':', spacing: Alone, span: $DIR/meta-macro-hygiene.rs:24:43: 24:45 (#4) }, Ident { ident: "dummy", span: $DIR/meta-macro-hygiene.rs:24:45: 24:50 (#4) }, Punct { ch: '!', spacing: Alone, span: $DIR/meta-macro-hygiene.rs:24:50: 24:51 (#4) }, Group { delimiter: Parenthesis, stream: TokenStream [], span: $DIR/meta-macro-hygiene.rs:24:51: 24:53 (#4) }] +Input: TokenStream [Ident { ident: "$crate", span: $DIR/meta-macro-hygiene.rs:23:37: 23:43 (#4) }, Punct { ch: ':', spacing: Joint, span: $DIR/meta-macro-hygiene.rs:23:43: 23:45 (#4) }, Punct { ch: ':', spacing: Alone, span: $DIR/meta-macro-hygiene.rs:23:43: 23:45 (#4) }, Ident { ident: "dummy", span: $DIR/meta-macro-hygiene.rs:23:45: 23:50 (#4) }, Punct { ch: '!', spacing: Alone, span: $DIR/meta-macro-hygiene.rs:23:50: 23:51 (#4) }, Group { delimiter: Parenthesis, stream: TokenStream [], span: $DIR/meta-macro-hygiene.rs:23:51: 23:53 (#4) }] Respanned: TokenStream [Ident { ident: "$crate", span: $DIR/auxiliary/make-macro.rs:7:9: 7:56 (#5) }, Punct { ch: ':', spacing: Joint, span: $DIR/auxiliary/make-macro.rs:7:9: 7:56 (#5) }, Punct { ch: ':', spacing: Alone, span: $DIR/auxiliary/make-macro.rs:7:9: 7:56 (#5) }, Ident { ident: "dummy", span: $DIR/auxiliary/make-macro.rs:7:9: 7:56 (#5) }, Punct { ch: '!', spacing: Alone, span: $DIR/auxiliary/make-macro.rs:7:9: 7:56 (#5) }, Group { delimiter: Parenthesis, stream: TokenStream [], span: $DIR/auxiliary/make-macro.rs:7:9: 7:56 (#5) }] #![feature /* 0#0 */(prelude_import)] -// ignore-tidy-linelength // aux-build:make-macro.rs // aux-build:meta-macro.rs // edition:2018 @@ -15,7 +14,7 @@ Respanned: TokenStream [Ident { ident: "$crate", span: $DIR/auxiliary/make-macro #![no_std /* 0#0 */] #[prelude_import /* 0#1 */] -use core /* 0#1 */::prelude /* 0#1 */::v1 /* 0#1 */::*; +use core /* 0#1 */::prelude /* 0#1 */::rust_2018 /* 0#1 */::*; #[macro_use /* 0#1 */] extern crate core /* 0#1 */; #[macro_use /* 0#1 */] diff --git a/src/test/ui/proc-macro/module_with_attrs.rs b/src/test/ui/proc-macro/module_with_attrs.rs new file mode 100644 index 0000000000000..63e66a62ac380 --- /dev/null +++ b/src/test/ui/proc-macro/module_with_attrs.rs @@ -0,0 +1,4 @@ +// ignore-test + +#![rustfmt::skip] +#![print_attr] diff --git a/src/test/ui/proc-macro/nested-derive-cfg.stdout b/src/test/ui/proc-macro/nested-derive-cfg.stdout index cf4e5d94d8a33..9a562c971c887 100644 --- a/src/test/ui/proc-macro/nested-derive-cfg.stdout +++ b/src/test/ui/proc-macro/nested-derive-cfg.stdout @@ -1,94 +1,81 @@ PRINT-DERIVE INPUT (DISPLAY): struct Foo -{ - my_array : - [bool ; { struct Inner { non_removed_inner_field : usize, } 0 }], -} +{ my_array : [bool ; { struct Inner { non_removed_inner_field : usize } 0 }] } PRINT-DERIVE INPUT (DEBUG): TokenStream [ Ident { ident: "struct", - span: $DIR/nested-derive-cfg.rs:12:1: 21:2 (#0), + span: $DIR/nested-derive-cfg.rs:12:1: 12:7 (#0), }, Ident { ident: "Foo", - span: $DIR/nested-derive-cfg.rs:12:1: 21:2 (#0), + span: $DIR/nested-derive-cfg.rs:12:8: 12:11 (#0), }, Group { delimiter: Brace, stream: TokenStream [ Ident { ident: "my_array", - span: $DIR/nested-derive-cfg.rs:12:1: 21:2 (#0), + span: $DIR/nested-derive-cfg.rs:14:5: 14:13 (#0), }, Punct { ch: ':', spacing: Alone, - span: $DIR/nested-derive-cfg.rs:12:1: 21:2 (#0), + span: $DIR/nested-derive-cfg.rs:14:13: 14:14 (#0), }, Group { delimiter: Bracket, stream: TokenStream [ Ident { ident: "bool", - span: $DIR/nested-derive-cfg.rs:12:1: 21:2 (#0), + span: $DIR/nested-derive-cfg.rs:14:16: 14:20 (#0), }, Punct { ch: ';', spacing: Alone, - span: $DIR/nested-derive-cfg.rs:12:1: 21:2 (#0), + span: $DIR/nested-derive-cfg.rs:14:20: 14:21 (#0), }, Group { delimiter: Brace, stream: TokenStream [ Ident { ident: "struct", - span: $DIR/nested-derive-cfg.rs:12:1: 21:2 (#0), + span: $DIR/nested-derive-cfg.rs:15:9: 15:15 (#0), }, Ident { ident: "Inner", - span: $DIR/nested-derive-cfg.rs:12:1: 21:2 (#0), + span: $DIR/nested-derive-cfg.rs:15:16: 15:21 (#0), }, Group { delimiter: Brace, stream: TokenStream [ Ident { ident: "non_removed_inner_field", - span: $DIR/nested-derive-cfg.rs:12:1: 21:2 (#0), + span: $DIR/nested-derive-cfg.rs:17:13: 17:36 (#0), }, Punct { ch: ':', spacing: Alone, - span: $DIR/nested-derive-cfg.rs:12:1: 21:2 (#0), + span: $DIR/nested-derive-cfg.rs:17:36: 17:37 (#0), }, Ident { ident: "usize", - span: $DIR/nested-derive-cfg.rs:12:1: 21:2 (#0), - }, - Punct { - ch: ',', - spacing: Alone, - span: $DIR/nested-derive-cfg.rs:12:1: 21:2 (#0), + span: $DIR/nested-derive-cfg.rs:17:38: 17:43 (#0), }, ], - span: $DIR/nested-derive-cfg.rs:12:1: 21:2 (#0), + span: $DIR/nested-derive-cfg.rs:15:22: 18:10 (#0), }, Literal { kind: Integer, symbol: "0", suffix: None, - span: $DIR/nested-derive-cfg.rs:12:1: 21:2 (#0), + span: $DIR/nested-derive-cfg.rs:19:9: 19:10 (#0), }, ], - span: $DIR/nested-derive-cfg.rs:12:1: 21:2 (#0), + span: $DIR/nested-derive-cfg.rs:14:22: 20:6 (#0), }, ], - span: $DIR/nested-derive-cfg.rs:12:1: 21:2 (#0), - }, - Punct { - ch: ',', - spacing: Alone, - span: $DIR/nested-derive-cfg.rs:12:1: 21:2 (#0), + span: $DIR/nested-derive-cfg.rs:14:15: 20:7 (#0), }, ], - span: $DIR/nested-derive-cfg.rs:12:1: 21:2 (#0), + span: $DIR/nested-derive-cfg.rs:12:12: 21:2 (#0), }, ] diff --git a/src/test/ui/proc-macro/nested-macro-rules.rs b/src/test/ui/proc-macro/nested-macro-rules.rs index 2fef0e5fad074..25ffcfad7c7ea 100644 --- a/src/test/ui/proc-macro/nested-macro-rules.rs +++ b/src/test/ui/proc-macro/nested-macro-rules.rs @@ -1,7 +1,7 @@ // run-pass // aux-build:nested-macro-rules.rs // aux-build:test-macros.rs -// compile-flags: -Z span-debug +// compile-flags: -Z span-debug -Z macro-backtrace // edition:2018 #![no_std] // Don't load unnecessary hygiene information from std @@ -10,14 +10,14 @@ extern crate std; extern crate nested_macro_rules; extern crate test_macros; -use test_macros::print_bang; +use test_macros::{print_bang, print_attr}; use nested_macro_rules::FirstStruct; struct SecondStruct; fn main() { - nested_macro_rules::inner_macro!(print_bang); + nested_macro_rules::inner_macro!(print_bang, print_attr); - nested_macro_rules::outer_macro!(SecondStruct); - inner_macro!(print_bang); + nested_macro_rules::outer_macro!(SecondStruct, SecondAttrStruct); + inner_macro!(print_bang, print_attr); } diff --git a/src/test/ui/proc-macro/nested-macro-rules.stdout b/src/test/ui/proc-macro/nested-macro-rules.stdout index dcafe3b4bda60..8292617fc1675 100644 --- a/src/test/ui/proc-macro/nested-macro-rules.stdout +++ b/src/test/ui/proc-macro/nested-macro-rules.stdout @@ -5,10 +5,32 @@ PRINT-BANG INPUT (DEBUG): TokenStream [ stream: TokenStream [ Ident { ident: "FirstStruct", - span: $DIR/auxiliary/nested-macro-rules.rs:15:14: 15:25 (#7), + span: $DIR/auxiliary/nested-macro-rules.rs:16:14: 16:25 (#7), }, ], - span: $DIR/auxiliary/nested-macro-rules.rs:9:27: 9:32 (#6), + span: $DIR/auxiliary/nested-macro-rules.rs:9:30: 9:35 (#6), + }, +] +PRINT-ATTR INPUT (DISPLAY): struct FirstAttrStruct { } +PRINT-ATTR INPUT (DEBUG): TokenStream [ + Ident { + ident: "struct", + span: $DIR/auxiliary/nested-macro-rules.rs:10:32: 10:38 (#6), + }, + Group { + delimiter: None, + stream: TokenStream [ + Ident { + ident: "FirstAttrStruct", + span: $DIR/auxiliary/nested-macro-rules.rs:16:27: 16:42 (#7), + }, + ], + span: $DIR/auxiliary/nested-macro-rules.rs:10:39: 10:56 (#6), + }, + Group { + delimiter: Brace, + stream: TokenStream [], + span: $DIR/auxiliary/nested-macro-rules.rs:10:57: 10:59 (#6), }, ] PRINT-BANG INPUT (DISPLAY): SecondStruct @@ -18,9 +40,31 @@ PRINT-BANG INPUT (DEBUG): TokenStream [ stream: TokenStream [ Ident { ident: "SecondStruct", - span: $DIR/nested-macro-rules.rs:21:38: 21:50 (#13), + span: $DIR/nested-macro-rules.rs:21:38: 21:50 (#16), }, ], - span: $DIR/auxiliary/nested-macro-rules.rs:9:27: 9:32 (#12), + span: $DIR/auxiliary/nested-macro-rules.rs:9:30: 9:35 (#15), + }, +] +PRINT-ATTR INPUT (DISPLAY): struct SecondAttrStruct { } +PRINT-ATTR INPUT (DEBUG): TokenStream [ + Ident { + ident: "struct", + span: $DIR/auxiliary/nested-macro-rules.rs:10:32: 10:38 (#15), + }, + Group { + delimiter: None, + stream: TokenStream [ + Ident { + ident: "SecondAttrStruct", + span: $DIR/nested-macro-rules.rs:21:52: 21:68 (#16), + }, + ], + span: $DIR/auxiliary/nested-macro-rules.rs:10:39: 10:56 (#15), + }, + Group { + delimiter: Brace, + stream: TokenStream [], + span: $DIR/auxiliary/nested-macro-rules.rs:10:57: 10:59 (#15), }, ] diff --git a/src/test/ui/proc-macro/nodelim-groups.stdout b/src/test/ui/proc-macro/nodelim-groups.stdout index 6b410f0bfb7e3..4ffe3f35e8e42 100644 --- a/src/test/ui/proc-macro/nodelim-groups.stdout +++ b/src/test/ui/proc-macro/nodelim-groups.stdout @@ -71,6 +71,7 @@ PRINT-BANG INPUT (DEBUG): TokenStream [ }, ] PRINT-BANG INPUT (DISPLAY): "hi" "hello".len() + "world".len() (1 + 1) +PRINT-BANG DEEP-RE-COLLECTED (DISPLAY): "hi" "hello" . len() + "world" . len() (1 + 1) PRINT-BANG INPUT (DEBUG): TokenStream [ Literal { kind: Str, diff --git a/src/test/ui/proc-macro/nonterminal-recollect-attr.rs b/src/test/ui/proc-macro/nonterminal-recollect-attr.rs index 5d4649b78c270..79c4ad4cd2a24 100644 --- a/src/test/ui/proc-macro/nonterminal-recollect-attr.rs +++ b/src/test/ui/proc-macro/nonterminal-recollect-attr.rs @@ -1,6 +1,10 @@ // check-pass +// compile-flags: -Z span-debug // aux-build:nonterminal-recollect-attr.rs +#![no_std] // Don't load unnecessary hygiene information from std +extern crate std; + extern crate nonterminal_recollect_attr; use nonterminal_recollect_attr::*; diff --git a/src/test/ui/proc-macro/nonterminal-recollect-attr.stdout b/src/test/ui/proc-macro/nonterminal-recollect-attr.stdout new file mode 100644 index 0000000000000..7331a25abd328 --- /dev/null +++ b/src/test/ui/proc-macro/nonterminal-recollect-attr.stdout @@ -0,0 +1,72 @@ +First recollected: TokenStream [ + Group { + delimiter: None, + stream: TokenStream [ + Ident { + ident: "pub", + span: $DIR/nonterminal-recollect-attr.rs:20:11: 20:14 (#0), + }, + ], + span: $DIR/nonterminal-recollect-attr.rs:14:9: 14:11 (#4), + }, + Ident { + ident: "struct", + span: $DIR/nonterminal-recollect-attr.rs:14:12: 14:18 (#4), + }, + Ident { + ident: "Foo", + span: $DIR/nonterminal-recollect-attr.rs:14:19: 14:22 (#4), + }, + Group { + delimiter: Brace, + stream: TokenStream [ + Ident { + ident: "field", + span: $DIR/nonterminal-recollect-attr.rs:15:13: 15:18 (#4), + }, + Punct { + ch: ':', + spacing: Alone, + span: $DIR/nonterminal-recollect-attr.rs:15:18: 15:19 (#4), + }, + Ident { + ident: "u8", + span: $DIR/nonterminal-recollect-attr.rs:15:20: 15:22 (#4), + }, + ], + span: $DIR/nonterminal-recollect-attr.rs:14:23: 16:10 (#4), + }, +] +Second recollected: TokenStream [ + Ident { + ident: "pub", + span: $DIR/nonterminal-recollect-attr.rs:20:11: 20:14 (#0), + }, + Ident { + ident: "struct", + span: $DIR/nonterminal-recollect-attr.rs:14:12: 14:18 (#4), + }, + Ident { + ident: "Foo", + span: $DIR/nonterminal-recollect-attr.rs:14:19: 14:22 (#4), + }, + Group { + delimiter: Brace, + stream: TokenStream [ + Ident { + ident: "field", + span: $DIR/nonterminal-recollect-attr.rs:15:13: 15:18 (#4), + }, + Punct { + ch: ':', + spacing: Alone, + span: $DIR/nonterminal-recollect-attr.rs:15:18: 15:19 (#4), + }, + Ident { + ident: "u8", + span: $DIR/nonterminal-recollect-attr.rs:15:20: 15:22 (#4), + }, + ], + span: $DIR/nonterminal-recollect-attr.rs:14:23: 16:10 (#4), + }, +] diff --git a/src/test/ui/proc-macro/nonterminal-token-hygiene.stdout b/src/test/ui/proc-macro/nonterminal-token-hygiene.stdout index 1623d67772639..ba3b3ee782784 100644 --- a/src/test/ui/proc-macro/nonterminal-token-hygiene.stdout +++ b/src/test/ui/proc-macro/nonterminal-token-hygiene.stdout @@ -35,7 +35,7 @@ PRINT-BANG INPUT (DEBUG): TokenStream [ #![no_std /* 0#0 */] #[prelude_import /* 0#1 */] -use ::core /* 0#1 */::prelude /* 0#1 */::v1 /* 0#1 */::*; +use ::core /* 0#1 */::prelude /* 0#1 */::rust_2015 /* 0#1 */::*; #[macro_use /* 0#1 */] extern crate core /* 0#2 */; #[macro_use /* 0#1 */] diff --git a/src/test/ui/proc-macro/simple-tuple.rs b/src/test/ui/proc-macro/simple-tuple.rs new file mode 100644 index 0000000000000..c94c5877e2240 --- /dev/null +++ b/src/test/ui/proc-macro/simple-tuple.rs @@ -0,0 +1,19 @@ +// check-pass +// compile-flags: -Z span-debug --error-format human +// aux-build:test-macros.rs +// edition:2018 + +#![feature(proc_macro_hygiene)] + +#![no_std] // Don't load unnecessary hygiene information from std +extern crate std; + +#[macro_use] +extern crate test_macros; + +fn main() { + #[print_target_and_args(my_arg)] ( + #![cfg_attr(not(FALSE), allow(unused))] + 1, 2, 3 + ); +} diff --git a/src/test/ui/proc-macro/simple-tuple.stdout b/src/test/ui/proc-macro/simple-tuple.stdout new file mode 100644 index 0000000000000..1cc8579a4675a --- /dev/null +++ b/src/test/ui/proc-macro/simple-tuple.stdout @@ -0,0 +1,79 @@ +PRINT-ATTR_ARGS INPUT (DISPLAY): my_arg +PRINT-ATTR_ARGS INPUT (DEBUG): TokenStream [ + Ident { + ident: "my_arg", + span: $DIR/simple-tuple.rs:15:29: 15:35 (#0), + }, +] +PRINT-ATTR INPUT (DISPLAY): (# ! [allow(unused)] 1, 2, 3) ; +PRINT-ATTR INPUT (DEBUG): TokenStream [ + Group { + delimiter: Parenthesis, + stream: TokenStream [ + Punct { + ch: '#', + spacing: Alone, + span: $DIR/simple-tuple.rs:16:9: 16:10 (#0), + }, + Punct { + ch: '!', + spacing: Alone, + span: $DIR/simple-tuple.rs:16:10: 16:11 (#0), + }, + Group { + delimiter: Bracket, + stream: TokenStream [ + Ident { + ident: "allow", + span: $DIR/simple-tuple.rs:16:33: 16:38 (#0), + }, + Group { + delimiter: Parenthesis, + stream: TokenStream [ + Ident { + ident: "unused", + span: $DIR/simple-tuple.rs:16:39: 16:45 (#0), + }, + ], + span: $DIR/simple-tuple.rs:16:38: 16:46 (#0), + }, + ], + span: $DIR/simple-tuple.rs:16:9: 16:10 (#0), + }, + Literal { + kind: Integer, + symbol: "1", + suffix: None, + span: $DIR/simple-tuple.rs:17:9: 17:10 (#0), + }, + Punct { + ch: ',', + spacing: Alone, + span: $DIR/simple-tuple.rs:17:10: 17:11 (#0), + }, + Literal { + kind: Integer, + symbol: "2", + suffix: None, + span: $DIR/simple-tuple.rs:17:12: 17:13 (#0), + }, + Punct { + ch: ',', + spacing: Alone, + span: $DIR/simple-tuple.rs:17:13: 17:14 (#0), + }, + Literal { + kind: Integer, + symbol: "3", + suffix: None, + span: $DIR/simple-tuple.rs:17:15: 17:16 (#0), + }, + ], + span: $DIR/simple-tuple.rs:15:38: 18:6 (#0), + }, + Punct { + ch: ';', + spacing: Alone, + span: $DIR/simple-tuple.rs:18:6: 18:7 (#0), + }, +] diff --git a/src/test/ui/proc-macro/trait-fn-args-2015.rs b/src/test/ui/proc-macro/trait-fn-args-2015.rs index 3a448d4b2201d..6b8df78a061c7 100644 --- a/src/test/ui/proc-macro/trait-fn-args-2015.rs +++ b/src/test/ui/proc-macro/trait-fn-args-2015.rs @@ -3,6 +3,8 @@ // check-pass // aux-build:test-macros.rs +#![allow(anonymous_parameters)] + #[macro_use] extern crate test_macros; diff --git a/src/test/ui/proc-macro/weird-braces.stdout b/src/test/ui/proc-macro/weird-braces.stdout index 25f0eaf0dd4e2..990829456e88a 100644 --- a/src/test/ui/proc-macro/weird-braces.stdout +++ b/src/test/ui/proc-macro/weird-braces.stdout @@ -15,40 +15,40 @@ PRINT-ATTR INPUT (DEBUG): TokenStream [ Punct { ch: '#', spacing: Alone, - span: $DIR/weird-braces.rs:18:1: 21:2 (#0), + span: $DIR/weird-braces.rs:17:1: 17:2 (#0), }, Group { delimiter: Bracket, stream: TokenStream [ Ident { ident: "print_target_and_args", - span: $DIR/weird-braces.rs:18:1: 21:2 (#0), + span: $DIR/weird-braces.rs:17:3: 17:24 (#0), }, Group { delimiter: Parenthesis, stream: TokenStream [ Ident { ident: "second_outer", - span: $DIR/weird-braces.rs:18:1: 21:2 (#0), + span: $DIR/weird-braces.rs:17:25: 17:37 (#0), }, ], - span: $DIR/weird-braces.rs:18:1: 21:2 (#0), + span: $DIR/weird-braces.rs:17:24: 17:38 (#0), }, ], - span: $DIR/weird-braces.rs:18:1: 21:2 (#0), + span: $DIR/weird-braces.rs:17:2: 17:39 (#0), }, Ident { ident: "impl", - span: $DIR/weird-braces.rs:18:1: 21:2 (#0), + span: $DIR/weird-braces.rs:18:1: 18:5 (#0), }, Ident { ident: "Bar", - span: $DIR/weird-braces.rs:18:1: 21:2 (#0), + span: $DIR/weird-braces.rs:18:6: 18:9 (#0), }, Punct { ch: '<', spacing: Alone, - span: $DIR/weird-braces.rs:18:1: 21:2 (#0), + span: $DIR/weird-braces.rs:18:9: 18:10 (#0), }, Group { delimiter: Brace, @@ -57,54 +57,54 @@ PRINT-ATTR INPUT (DEBUG): TokenStream [ kind: Integer, symbol: "1", suffix: None, - span: $DIR/weird-braces.rs:18:1: 21:2 (#0), + span: $DIR/weird-braces.rs:18:11: 18:12 (#0), }, Punct { ch: '>', spacing: Alone, - span: $DIR/weird-braces.rs:18:1: 21:2 (#0), + span: $DIR/weird-braces.rs:18:13: 18:14 (#0), }, Literal { kind: Integer, symbol: "0", suffix: None, - span: $DIR/weird-braces.rs:18:1: 21:2 (#0), + span: $DIR/weird-braces.rs:18:15: 18:16 (#0), }, ], - span: $DIR/weird-braces.rs:18:1: 21:2 (#0), + span: $DIR/weird-braces.rs:18:10: 18:17 (#0), }, Punct { ch: '>', spacing: Alone, - span: $DIR/weird-braces.rs:18:1: 21:2 (#0), + span: $DIR/weird-braces.rs:18:17: 18:18 (#0), }, Ident { ident: "for", - span: $DIR/weird-braces.rs:18:1: 21:2 (#0), + span: $DIR/weird-braces.rs:18:19: 18:22 (#0), }, Ident { ident: "Foo", - span: $DIR/weird-braces.rs:18:1: 21:2 (#0), + span: $DIR/weird-braces.rs:18:23: 18:26 (#0), }, Punct { ch: '<', spacing: Alone, - span: $DIR/weird-braces.rs:18:1: 21:2 (#0), + span: $DIR/weird-braces.rs:18:26: 18:27 (#0), }, Group { delimiter: Brace, stream: TokenStream [ Ident { ident: "true", - span: $DIR/weird-braces.rs:18:1: 21:2 (#0), + span: $DIR/weird-braces.rs:18:28: 18:32 (#0), }, ], - span: $DIR/weird-braces.rs:18:1: 21:2 (#0), + span: $DIR/weird-braces.rs:18:27: 18:33 (#0), }, Punct { ch: '>', spacing: Alone, - span: $DIR/weird-braces.rs:18:1: 21:2 (#0), + span: $DIR/weird-braces.rs:18:33: 18:34 (#0), }, Group { delimiter: Brace, @@ -112,72 +112,72 @@ PRINT-ATTR INPUT (DEBUG): TokenStream [ Punct { ch: '#', spacing: Joint, - span: $DIR/weird-braces.rs:18:1: 21:2 (#0), + span: $DIR/weird-braces.rs:19:5: 19:6 (#0), }, Punct { ch: '!', spacing: Alone, - span: $DIR/weird-braces.rs:18:1: 21:2 (#0), + span: $DIR/weird-braces.rs:19:6: 19:7 (#0), }, Group { delimiter: Bracket, stream: TokenStream [ Ident { ident: "print_target_and_args", - span: $DIR/weird-braces.rs:18:1: 21:2 (#0), + span: $DIR/weird-braces.rs:19:8: 19:29 (#0), }, Group { delimiter: Parenthesis, stream: TokenStream [ Ident { ident: "first_inner", - span: $DIR/weird-braces.rs:18:1: 21:2 (#0), + span: $DIR/weird-braces.rs:19:30: 19:41 (#0), }, ], - span: $DIR/weird-braces.rs:18:1: 21:2 (#0), + span: $DIR/weird-braces.rs:19:29: 19:42 (#0), }, ], - span: $DIR/weird-braces.rs:18:1: 21:2 (#0), + span: $DIR/weird-braces.rs:19:7: 19:43 (#0), }, Punct { ch: '#', spacing: Joint, - span: $DIR/weird-braces.rs:18:1: 21:2 (#0), + span: $DIR/weird-braces.rs:20:5: 20:6 (#0), }, Punct { ch: '!', spacing: Alone, - span: $DIR/weird-braces.rs:18:1: 21:2 (#0), + span: $DIR/weird-braces.rs:20:6: 20:7 (#0), }, Group { delimiter: Bracket, stream: TokenStream [ Ident { ident: "print_target_and_args", - span: $DIR/weird-braces.rs:18:1: 21:2 (#0), + span: $DIR/weird-braces.rs:20:8: 20:29 (#0), }, Group { delimiter: Parenthesis, stream: TokenStream [ Ident { ident: "second_inner", - span: $DIR/weird-braces.rs:18:1: 21:2 (#0), + span: $DIR/weird-braces.rs:20:30: 20:42 (#0), }, ], - span: $DIR/weird-braces.rs:18:1: 21:2 (#0), + span: $DIR/weird-braces.rs:20:29: 20:43 (#0), }, ], - span: $DIR/weird-braces.rs:18:1: 21:2 (#0), + span: $DIR/weird-braces.rs:20:7: 20:44 (#0), }, ], - span: $DIR/weird-braces.rs:18:1: 21:2 (#0), + span: $DIR/weird-braces.rs:18:35: 21:2 (#0), }, ] PRINT-ATTR_ARGS INPUT (DISPLAY): second_outer PRINT-ATTR_ARGS INPUT (DEBUG): TokenStream [ Ident { ident: "second_outer", - span: $DIR/weird-braces.rs:18:1: 21:2 (#0), + span: $DIR/weird-braces.rs:17:25: 17:37 (#0), }, ] PRINT-ATTR INPUT (DISPLAY): impl Bar < { 1 > 0 } > for Foo < { true } > @@ -188,16 +188,16 @@ PRINT-ATTR INPUT (DISPLAY): impl Bar < { 1 > 0 } > for Foo < { true } > PRINT-ATTR INPUT (DEBUG): TokenStream [ Ident { ident: "impl", - span: $DIR/weird-braces.rs:18:1: 21:2 (#0), + span: $DIR/weird-braces.rs:18:1: 18:5 (#0), }, Ident { ident: "Bar", - span: $DIR/weird-braces.rs:18:1: 21:2 (#0), + span: $DIR/weird-braces.rs:18:6: 18:9 (#0), }, Punct { ch: '<', spacing: Alone, - span: $DIR/weird-braces.rs:18:1: 21:2 (#0), + span: $DIR/weird-braces.rs:18:9: 18:10 (#0), }, Group { delimiter: Brace, @@ -206,54 +206,54 @@ PRINT-ATTR INPUT (DEBUG): TokenStream [ kind: Integer, symbol: "1", suffix: None, - span: $DIR/weird-braces.rs:18:1: 21:2 (#0), + span: $DIR/weird-braces.rs:18:11: 18:12 (#0), }, Punct { ch: '>', spacing: Alone, - span: $DIR/weird-braces.rs:18:1: 21:2 (#0), + span: $DIR/weird-braces.rs:18:13: 18:14 (#0), }, Literal { kind: Integer, symbol: "0", suffix: None, - span: $DIR/weird-braces.rs:18:1: 21:2 (#0), + span: $DIR/weird-braces.rs:18:15: 18:16 (#0), }, ], - span: $DIR/weird-braces.rs:18:1: 21:2 (#0), + span: $DIR/weird-braces.rs:18:10: 18:17 (#0), }, Punct { ch: '>', spacing: Alone, - span: $DIR/weird-braces.rs:18:1: 21:2 (#0), + span: $DIR/weird-braces.rs:18:17: 18:18 (#0), }, Ident { ident: "for", - span: $DIR/weird-braces.rs:18:1: 21:2 (#0), + span: $DIR/weird-braces.rs:18:19: 18:22 (#0), }, Ident { ident: "Foo", - span: $DIR/weird-braces.rs:18:1: 21:2 (#0), + span: $DIR/weird-braces.rs:18:23: 18:26 (#0), }, Punct { ch: '<', spacing: Alone, - span: $DIR/weird-braces.rs:18:1: 21:2 (#0), + span: $DIR/weird-braces.rs:18:26: 18:27 (#0), }, Group { delimiter: Brace, stream: TokenStream [ Ident { ident: "true", - span: $DIR/weird-braces.rs:18:1: 21:2 (#0), + span: $DIR/weird-braces.rs:18:28: 18:32 (#0), }, ], - span: $DIR/weird-braces.rs:18:1: 21:2 (#0), + span: $DIR/weird-braces.rs:18:27: 18:33 (#0), }, Punct { ch: '>', spacing: Alone, - span: $DIR/weird-braces.rs:18:1: 21:2 (#0), + span: $DIR/weird-braces.rs:18:33: 18:34 (#0), }, Group { delimiter: Brace, @@ -261,72 +261,72 @@ PRINT-ATTR INPUT (DEBUG): TokenStream [ Punct { ch: '#', spacing: Joint, - span: $DIR/weird-braces.rs:18:1: 21:2 (#0), + span: $DIR/weird-braces.rs:19:5: 19:6 (#0), }, Punct { ch: '!', spacing: Alone, - span: $DIR/weird-braces.rs:18:1: 21:2 (#0), + span: $DIR/weird-braces.rs:19:6: 19:7 (#0), }, Group { delimiter: Bracket, stream: TokenStream [ Ident { ident: "print_target_and_args", - span: $DIR/weird-braces.rs:18:1: 21:2 (#0), + span: $DIR/weird-braces.rs:19:8: 19:29 (#0), }, Group { delimiter: Parenthesis, stream: TokenStream [ Ident { ident: "first_inner", - span: $DIR/weird-braces.rs:18:1: 21:2 (#0), + span: $DIR/weird-braces.rs:19:30: 19:41 (#0), }, ], - span: $DIR/weird-braces.rs:18:1: 21:2 (#0), + span: $DIR/weird-braces.rs:19:29: 19:42 (#0), }, ], - span: $DIR/weird-braces.rs:18:1: 21:2 (#0), + span: $DIR/weird-braces.rs:19:7: 19:43 (#0), }, Punct { ch: '#', spacing: Joint, - span: $DIR/weird-braces.rs:18:1: 21:2 (#0), + span: $DIR/weird-braces.rs:20:5: 20:6 (#0), }, Punct { ch: '!', spacing: Alone, - span: $DIR/weird-braces.rs:18:1: 21:2 (#0), + span: $DIR/weird-braces.rs:20:6: 20:7 (#0), }, Group { delimiter: Bracket, stream: TokenStream [ Ident { ident: "print_target_and_args", - span: $DIR/weird-braces.rs:18:1: 21:2 (#0), + span: $DIR/weird-braces.rs:20:8: 20:29 (#0), }, Group { delimiter: Parenthesis, stream: TokenStream [ Ident { ident: "second_inner", - span: $DIR/weird-braces.rs:18:1: 21:2 (#0), + span: $DIR/weird-braces.rs:20:30: 20:42 (#0), }, ], - span: $DIR/weird-braces.rs:18:1: 21:2 (#0), + span: $DIR/weird-braces.rs:20:29: 20:43 (#0), }, ], - span: $DIR/weird-braces.rs:18:1: 21:2 (#0), + span: $DIR/weird-braces.rs:20:7: 20:44 (#0), }, ], - span: $DIR/weird-braces.rs:18:1: 21:2 (#0), + span: $DIR/weird-braces.rs:18:35: 21:2 (#0), }, ] PRINT-ATTR_ARGS INPUT (DISPLAY): first_inner PRINT-ATTR_ARGS INPUT (DEBUG): TokenStream [ Ident { ident: "first_inner", - span: $DIR/weird-braces.rs:18:1: 21:2 (#0), + span: $DIR/weird-braces.rs:19:30: 19:41 (#0), }, ] PRINT-ATTR INPUT (DISPLAY): impl Bar < { 1 > 0 } > for Foo < { true } > @@ -334,16 +334,16 @@ PRINT-ATTR INPUT (DISPLAY): impl Bar < { 1 > 0 } > for Foo < { true } > PRINT-ATTR INPUT (DEBUG): TokenStream [ Ident { ident: "impl", - span: $DIR/weird-braces.rs:18:1: 21:2 (#0), + span: $DIR/weird-braces.rs:18:1: 18:5 (#0), }, Ident { ident: "Bar", - span: $DIR/weird-braces.rs:18:1: 21:2 (#0), + span: $DIR/weird-braces.rs:18:6: 18:9 (#0), }, Punct { ch: '<', spacing: Alone, - span: $DIR/weird-braces.rs:18:1: 21:2 (#0), + span: $DIR/weird-braces.rs:18:9: 18:10 (#0), }, Group { delimiter: Brace, @@ -352,54 +352,54 @@ PRINT-ATTR INPUT (DEBUG): TokenStream [ kind: Integer, symbol: "1", suffix: None, - span: $DIR/weird-braces.rs:18:1: 21:2 (#0), + span: $DIR/weird-braces.rs:18:11: 18:12 (#0), }, Punct { ch: '>', spacing: Alone, - span: $DIR/weird-braces.rs:18:1: 21:2 (#0), + span: $DIR/weird-braces.rs:18:13: 18:14 (#0), }, Literal { kind: Integer, symbol: "0", suffix: None, - span: $DIR/weird-braces.rs:18:1: 21:2 (#0), + span: $DIR/weird-braces.rs:18:15: 18:16 (#0), }, ], - span: $DIR/weird-braces.rs:18:1: 21:2 (#0), + span: $DIR/weird-braces.rs:18:10: 18:17 (#0), }, Punct { ch: '>', spacing: Alone, - span: $DIR/weird-braces.rs:18:1: 21:2 (#0), + span: $DIR/weird-braces.rs:18:17: 18:18 (#0), }, Ident { ident: "for", - span: $DIR/weird-braces.rs:18:1: 21:2 (#0), + span: $DIR/weird-braces.rs:18:19: 18:22 (#0), }, Ident { ident: "Foo", - span: $DIR/weird-braces.rs:18:1: 21:2 (#0), + span: $DIR/weird-braces.rs:18:23: 18:26 (#0), }, Punct { ch: '<', spacing: Alone, - span: $DIR/weird-braces.rs:18:1: 21:2 (#0), + span: $DIR/weird-braces.rs:18:26: 18:27 (#0), }, Group { delimiter: Brace, stream: TokenStream [ Ident { ident: "true", - span: $DIR/weird-braces.rs:18:1: 21:2 (#0), + span: $DIR/weird-braces.rs:18:28: 18:32 (#0), }, ], - span: $DIR/weird-braces.rs:18:1: 21:2 (#0), + span: $DIR/weird-braces.rs:18:27: 18:33 (#0), }, Punct { ch: '>', spacing: Alone, - span: $DIR/weird-braces.rs:18:1: 21:2 (#0), + span: $DIR/weird-braces.rs:18:33: 18:34 (#0), }, Group { delimiter: Brace, @@ -407,58 +407,58 @@ PRINT-ATTR INPUT (DEBUG): TokenStream [ Punct { ch: '#', spacing: Joint, - span: $DIR/weird-braces.rs:18:1: 21:2 (#0), + span: $DIR/weird-braces.rs:20:5: 20:6 (#0), }, Punct { ch: '!', spacing: Alone, - span: $DIR/weird-braces.rs:18:1: 21:2 (#0), + span: $DIR/weird-braces.rs:20:6: 20:7 (#0), }, Group { delimiter: Bracket, stream: TokenStream [ Ident { ident: "print_target_and_args", - span: $DIR/weird-braces.rs:18:1: 21:2 (#0), + span: $DIR/weird-braces.rs:20:8: 20:29 (#0), }, Group { delimiter: Parenthesis, stream: TokenStream [ Ident { ident: "second_inner", - span: $DIR/weird-braces.rs:18:1: 21:2 (#0), + span: $DIR/weird-braces.rs:20:30: 20:42 (#0), }, ], - span: $DIR/weird-braces.rs:18:1: 21:2 (#0), + span: $DIR/weird-braces.rs:20:29: 20:43 (#0), }, ], - span: $DIR/weird-braces.rs:18:1: 21:2 (#0), + span: $DIR/weird-braces.rs:20:7: 20:44 (#0), }, ], - span: $DIR/weird-braces.rs:18:1: 21:2 (#0), + span: $DIR/weird-braces.rs:18:35: 21:2 (#0), }, ] PRINT-ATTR_ARGS INPUT (DISPLAY): second_inner PRINT-ATTR_ARGS INPUT (DEBUG): TokenStream [ Ident { ident: "second_inner", - span: $DIR/weird-braces.rs:18:1: 21:2 (#0), + span: $DIR/weird-braces.rs:20:30: 20:42 (#0), }, ] PRINT-ATTR INPUT (DISPLAY): impl Bar < { 1 > 0 } > for Foo < { true } > { } PRINT-ATTR INPUT (DEBUG): TokenStream [ Ident { ident: "impl", - span: $DIR/weird-braces.rs:18:1: 21:2 (#0), + span: $DIR/weird-braces.rs:18:1: 18:5 (#0), }, Ident { ident: "Bar", - span: $DIR/weird-braces.rs:18:1: 21:2 (#0), + span: $DIR/weird-braces.rs:18:6: 18:9 (#0), }, Punct { ch: '<', spacing: Alone, - span: $DIR/weird-braces.rs:18:1: 21:2 (#0), + span: $DIR/weird-braces.rs:18:9: 18:10 (#0), }, Group { delimiter: Brace, @@ -467,58 +467,58 @@ PRINT-ATTR INPUT (DEBUG): TokenStream [ kind: Integer, symbol: "1", suffix: None, - span: $DIR/weird-braces.rs:18:1: 21:2 (#0), + span: $DIR/weird-braces.rs:18:11: 18:12 (#0), }, Punct { ch: '>', spacing: Alone, - span: $DIR/weird-braces.rs:18:1: 21:2 (#0), + span: $DIR/weird-braces.rs:18:13: 18:14 (#0), }, Literal { kind: Integer, symbol: "0", suffix: None, - span: $DIR/weird-braces.rs:18:1: 21:2 (#0), + span: $DIR/weird-braces.rs:18:15: 18:16 (#0), }, ], - span: $DIR/weird-braces.rs:18:1: 21:2 (#0), + span: $DIR/weird-braces.rs:18:10: 18:17 (#0), }, Punct { ch: '>', spacing: Alone, - span: $DIR/weird-braces.rs:18:1: 21:2 (#0), + span: $DIR/weird-braces.rs:18:17: 18:18 (#0), }, Ident { ident: "for", - span: $DIR/weird-braces.rs:18:1: 21:2 (#0), + span: $DIR/weird-braces.rs:18:19: 18:22 (#0), }, Ident { ident: "Foo", - span: $DIR/weird-braces.rs:18:1: 21:2 (#0), + span: $DIR/weird-braces.rs:18:23: 18:26 (#0), }, Punct { ch: '<', spacing: Alone, - span: $DIR/weird-braces.rs:18:1: 21:2 (#0), + span: $DIR/weird-braces.rs:18:26: 18:27 (#0), }, Group { delimiter: Brace, stream: TokenStream [ Ident { ident: "true", - span: $DIR/weird-braces.rs:18:1: 21:2 (#0), + span: $DIR/weird-braces.rs:18:28: 18:32 (#0), }, ], - span: $DIR/weird-braces.rs:18:1: 21:2 (#0), + span: $DIR/weird-braces.rs:18:27: 18:33 (#0), }, Punct { ch: '>', spacing: Alone, - span: $DIR/weird-braces.rs:18:1: 21:2 (#0), + span: $DIR/weird-braces.rs:18:33: 18:34 (#0), }, Group { delimiter: Brace, stream: TokenStream [], - span: $DIR/weird-braces.rs:18:1: 21:2 (#0), + span: $DIR/weird-braces.rs:18:35: 21:2 (#0), }, ] diff --git a/src/test/ui/pub/pub-reexport-priv-extern-crate.rs b/src/test/ui/pub/pub-reexport-priv-extern-crate.rs index e95d6924026ca..dd5cd420fa546 100644 --- a/src/test/ui/pub/pub-reexport-priv-extern-crate.rs +++ b/src/test/ui/pub/pub-reexport-priv-extern-crate.rs @@ -1,5 +1,3 @@ -#![allow(unused)] - extern crate core; pub use core as reexported_core; //~ ERROR `core` is private, and cannot be re-exported //~^ WARN this was previously accepted @@ -9,16 +7,14 @@ mod foo1 { } mod foo2 { - use foo1::core; //~ ERROR `core` is private, and cannot be re-exported - //~^ WARN this was previously accepted + use foo1::core; //~ ERROR crate import `core` is private pub mod bar { extern crate core; } } mod baz { - pub use foo2::bar::core; //~ ERROR `core` is private, and cannot be re-exported - //~^ WARN this was previously accepted + pub use foo2::bar::core; //~ ERROR crate import `core` is private } fn main() {} diff --git a/src/test/ui/pub/pub-reexport-priv-extern-crate.stderr b/src/test/ui/pub/pub-reexport-priv-extern-crate.stderr index 0b44c5a6525f6..e4d73c6475dc4 100644 --- a/src/test/ui/pub/pub-reexport-priv-extern-crate.stderr +++ b/src/test/ui/pub/pub-reexport-priv-extern-crate.stderr @@ -1,30 +1,37 @@ -error: extern crate `core` is private, and cannot be re-exported (error E0365), consider declaring with `pub` - --> $DIR/pub-reexport-priv-extern-crate.rs:4:9 +error[E0603]: crate import `core` is private + --> $DIR/pub-reexport-priv-extern-crate.rs:10:15 | -LL | pub use core as reexported_core; - | ^^^^^^^^^^^^^^^^^^^^^^^ +LL | use foo1::core; + | ^^^^ private crate import | - = note: `#[deny(pub_use_of_private_extern_crate)]` on by default - = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = note: for more information, see issue #34537 <https://github.com/rust-lang/rust/issues/34537> +note: the crate import `core` is defined here + --> $DIR/pub-reexport-priv-extern-crate.rs:6:5 + | +LL | extern crate core; + | ^^^^^^^^^^^^^^^^^^ -error: extern crate `core` is private, and cannot be re-exported (error E0365), consider declaring with `pub` - --> $DIR/pub-reexport-priv-extern-crate.rs:12:9 +error[E0603]: crate import `core` is private + --> $DIR/pub-reexport-priv-extern-crate.rs:17:24 | -LL | use foo1::core; - | ^^^^^^^^^^ +LL | pub use foo2::bar::core; + | ^^^^ private crate import | - = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = note: for more information, see issue #34537 <https://github.com/rust-lang/rust/issues/34537> +note: the crate import `core` is defined here + --> $DIR/pub-reexport-priv-extern-crate.rs:12:9 + | +LL | extern crate core; + | ^^^^^^^^^^^^^^^^^^ error: extern crate `core` is private, and cannot be re-exported (error E0365), consider declaring with `pub` - --> $DIR/pub-reexport-priv-extern-crate.rs:20:13 + --> $DIR/pub-reexport-priv-extern-crate.rs:2:9 | -LL | pub use foo2::bar::core; - | ^^^^^^^^^^^^^^^ +LL | pub use core as reexported_core; + | ^^^^^^^^^^^^^^^^^^^^^^^ | + = note: `#[deny(pub_use_of_private_extern_crate)]` on by default = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! = note: for more information, see issue #34537 <https://github.com/rust-lang/rust/issues/34537> error: aborting due to 3 previous errors +For more information about this error, try `rustc --explain E0603`. diff --git a/src/test/ui/recursion/issue-38591-non-regular-dropck-recursion.stderr b/src/test/ui/recursion/issue-38591-non-regular-dropck-recursion.stderr index 5bf381607c5ed..d749ee00c22bf 100644 --- a/src/test/ui/recursion/issue-38591-non-regular-dropck-recursion.stderr +++ b/src/test/ui/recursion/issue-38591-non-regular-dropck-recursion.stderr @@ -1,10 +1,10 @@ -error: reached the recursion limit while instantiating `drop_in_place::<S<fn(fn(fn(fn(fn...)))))))))))))))))))))))))))))>))` +error: reached the recursion limit while instantiating `std::ptr::drop_in_place::<S<fn(f...)))))))))))))))))))))))))))))>))` --> $SRC_DIR/core/src/ptr/mod.rs:LL:COL | LL | pub unsafe fn drop_in_place<T: ?Sized>(to_drop: *mut T) { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | -note: `drop_in_place` defined here +note: `std::ptr::drop_in_place` defined here --> $SRC_DIR/core/src/ptr/mod.rs:LL:COL | LL | pub unsafe fn drop_in_place<T: ?Sized>(to_drop: *mut T) { diff --git a/src/test/ui/recursion/issue-83150.rs b/src/test/ui/recursion/issue-83150.rs new file mode 100644 index 0000000000000..650ad22d206db --- /dev/null +++ b/src/test/ui/recursion/issue-83150.rs @@ -0,0 +1,11 @@ +// build-fail + //~^ overflow evaluating + +fn main() { + let mut iter = 0u8..1; + func(&mut iter) +} + +fn func<T: Iterator<Item = u8>>(iter: &mut T) { + func(&mut iter.map(|x| x + 1)) +} diff --git a/src/test/ui/recursion/issue-83150.stderr b/src/test/ui/recursion/issue-83150.stderr new file mode 100644 index 0000000000000..943d51330976a --- /dev/null +++ b/src/test/ui/recursion/issue-83150.stderr @@ -0,0 +1,8 @@ +error[E0275]: overflow evaluating the requirement `Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut std::ops::Range<u8>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>: Iterator` + | + = help: consider adding a `#![recursion_limit="256"]` attribute to your crate (`issue_83150`) + = note: required because of the requirements on the impl of `Iterator` for `&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut std::ops::Range<u8>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>` + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0275`. diff --git a/src/test/ui/recursion/recursive-requirements.stderr b/src/test/ui/recursion/recursive-requirements.stderr index 6c0be0f7f8d77..0518cc507b50b 100644 --- a/src/test/ui/recursion/recursive-requirements.stderr +++ b/src/test/ui/recursion/recursive-requirements.stderr @@ -8,7 +8,11 @@ LL | let _: AssertSync<Foo> = unimplemented!(); | ^^^^^^^^^^^^^^^ `*const Bar` cannot be shared between threads safely | = help: within `Foo`, the trait `Sync` is not implemented for `*const Bar` - = note: required because it appears within the type `Foo` +note: required because it appears within the type `Foo` + --> $DIR/recursive-requirements.rs:5:12 + | +LL | pub struct Foo { + | ^^^ error[E0277]: `*const Foo` cannot be shared between threads safely --> $DIR/recursive-requirements.rs:16:12 @@ -20,9 +24,17 @@ LL | let _: AssertSync<Foo> = unimplemented!(); | ^^^^^^^^^^^^^^^ `*const Foo` cannot be shared between threads safely | = help: within `Foo`, the trait `Sync` is not implemented for `*const Foo` - = note: required because it appears within the type `Bar` +note: required because it appears within the type `Bar` + --> $DIR/recursive-requirements.rs:10:12 + | +LL | pub struct Bar { + | ^^^ = note: required because it appears within the type `PhantomData<Bar>` - = note: required because it appears within the type `Foo` +note: required because it appears within the type `Foo` + --> $DIR/recursive-requirements.rs:5:12 + | +LL | pub struct Foo { + | ^^^ error: aborting due to 2 previous errors diff --git a/src/test/ui/regions/issue-28848.stderr b/src/test/ui/regions/issue-28848.stderr index 726844a31841f..83313b34316b4 100644 --- a/src/test/ui/regions/issue-28848.stderr +++ b/src/test/ui/regions/issue-28848.stderr @@ -2,7 +2,7 @@ error[E0478]: lifetime bound not satisfied --> $DIR/issue-28848.rs:10:5 | LL | Foo::<'a, 'b>::xmute(u) - | ^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^ | note: lifetime parameter instantiated with the lifetime `'b` as defined on the function body at 9:16 --> $DIR/issue-28848.rs:9:16 diff --git a/src/test/ui/issue-6157.rs b/src/test/ui/regions/issue-6157.rs similarity index 100% rename from src/test/ui/issue-6157.rs rename to src/test/ui/regions/issue-6157.rs diff --git a/src/test/ui/regions/regions-enum-not-wf.rs b/src/test/ui/regions/regions-enum-not-wf.rs index 6de08f66d7536..8b491ee4e303f 100644 --- a/src/test/ui/regions/regions-enum-not-wf.rs +++ b/src/test/ui/regions/regions-enum-not-wf.rs @@ -1,5 +1,3 @@ -// ignore-tidy-linelength - // Various examples of structs whose fields are not well-formed. #![allow(dead_code)] diff --git a/src/test/ui/regions/regions-enum-not-wf.stderr b/src/test/ui/regions/regions-enum-not-wf.stderr index 36686eaf92f32..553a3e71c169a 100644 --- a/src/test/ui/regions/regions-enum-not-wf.stderr +++ b/src/test/ui/regions/regions-enum-not-wf.stderr @@ -1,5 +1,5 @@ error[E0309]: the parameter type `T` may not live long enough - --> $DIR/regions-enum-not-wf.rs:19:18 + --> $DIR/regions-enum-not-wf.rs:17:18 | LL | enum Ref1<'a, T> { | - help: consider adding an explicit lifetime bound...: `T: 'a` @@ -7,7 +7,7 @@ LL | Ref1Variant1(RequireOutlives<'a, T>), | ^^^^^^^^^^^^^^^^^^^^^^ ...so that the type `T` will meet its required lifetime bounds error[E0309]: the parameter type `T` may not live long enough - --> $DIR/regions-enum-not-wf.rs:24:25 + --> $DIR/regions-enum-not-wf.rs:22:25 | LL | enum Ref2<'a, T> { | - help: consider adding an explicit lifetime bound...: `T: 'a` @@ -16,7 +16,7 @@ LL | Ref2Variant2(isize, RequireOutlives<'a, T>), | ^^^^^^^^^^^^^^^^^^^^^^ ...so that the type `T` will meet its required lifetime bounds error[E0309]: the parameter type `T` may not live long enough - --> $DIR/regions-enum-not-wf.rs:37:23 + --> $DIR/regions-enum-not-wf.rs:35:23 | LL | enum RefDouble<'a, 'b, T> { | - help: consider adding an explicit lifetime bound...: `T: 'b` diff --git a/src/test/ui/regions/regions-name-undeclared.stderr b/src/test/ui/regions/regions-name-undeclared.stderr index ad0e7bd5afbc0..5f6ff280e6531 100644 --- a/src/test/ui/regions/regions-name-undeclared.stderr +++ b/src/test/ui/regions/regions-name-undeclared.stderr @@ -1,3 +1,23 @@ +error[E0261]: use of undeclared lifetime name `'a` + --> $DIR/regions-name-undeclared.rs:28:13 + | +LL | enum E { + | - help: consider introducing lifetime `'a` here: `<'a>` +LL | E1(&'a isize) + | ^^ undeclared lifetime + | + = help: if you want to experiment with in-band lifetime bindings, add `#![feature(in_band_lifetimes)]` to the crate attributes + +error[E0261]: use of undeclared lifetime name `'a` + --> $DIR/regions-name-undeclared.rs:31:13 + | +LL | struct S { + | - help: consider introducing lifetime `'a` here: `<'a>` +LL | f: &'a isize + | ^^ undeclared lifetime + | + = help: if you want to experiment with in-band lifetime bindings, add `#![feature(in_band_lifetimes)]` to the crate attributes + error[E0261]: use of undeclared lifetime name `'b` --> $DIR/regions-name-undeclared.rs:16:24 | @@ -56,26 +76,6 @@ LL | type X = Option<&'a isize>; | = help: if you want to experiment with in-band lifetime bindings, add `#![feature(in_band_lifetimes)]` to the crate attributes -error[E0261]: use of undeclared lifetime name `'a` - --> $DIR/regions-name-undeclared.rs:28:13 - | -LL | enum E { - | - help: consider introducing lifetime `'a` here: `<'a>` -LL | E1(&'a isize) - | ^^ undeclared lifetime - | - = help: if you want to experiment with in-band lifetime bindings, add `#![feature(in_band_lifetimes)]` to the crate attributes - -error[E0261]: use of undeclared lifetime name `'a` - --> $DIR/regions-name-undeclared.rs:31:13 - | -LL | struct S { - | - help: consider introducing lifetime `'a` here: `<'a>` -LL | f: &'a isize - | ^^ undeclared lifetime - | - = help: if you want to experiment with in-band lifetime bindings, add `#![feature(in_band_lifetimes)]` to the crate attributes - error[E0261]: use of undeclared lifetime name `'a` --> $DIR/regions-name-undeclared.rs:33:14 | diff --git a/src/test/ui/reify-intrinsic.rs b/src/test/ui/reify-intrinsic.rs index 09baa059e5567..05535b92cca73 100644 --- a/src/test/ui/reify-intrinsic.rs +++ b/src/test/ui/reify-intrinsic.rs @@ -12,4 +12,12 @@ fn b() { //~^ ERROR casting } +fn c() { + let _ = [ + std::intrinsics::copy_nonoverlapping::<i32>, + std::intrinsics::copy::<i32>, + //~^ ERROR cannot coerce + ]; +} + fn main() {} diff --git a/src/test/ui/reify-intrinsic.stderr b/src/test/ui/reify-intrinsic.stderr index 675447f972179..5d82fdbd31190 100644 --- a/src/test/ui/reify-intrinsic.stderr +++ b/src/test/ui/reify-intrinsic.stderr @@ -19,7 +19,16 @@ error[E0606]: casting `unsafe extern "rust-intrinsic" fn(_) -> _ {transmute::<_, LL | let _ = std::mem::transmute as unsafe extern "rust-intrinsic" fn(isize) -> usize; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: aborting due to 2 previous errors +error[E0308]: cannot coerce intrinsics to function pointers + --> $DIR/reify-intrinsic.rs:18:9 + | +LL | std::intrinsics::copy::<i32>, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot coerce intrinsics to function pointers + | + = note: expected type `unsafe extern "rust-intrinsic" fn(_, _, _) {copy_nonoverlapping::<i32>}` + found fn item `unsafe extern "rust-intrinsic" fn(_, _, _) {std::intrinsics::copy::<i32>}` + +error: aborting due to 3 previous errors Some errors have detailed explanations: E0308, E0606. For more information about an error, try `rustc --explain E0308`. diff --git a/src/test/ui/repr/repr-disallow-on-variant.rs b/src/test/ui/repr/repr-disallow-on-variant.rs index 90cad7e647b05..d9bd0b0e38a69 100644 --- a/src/test/ui/repr/repr-disallow-on-variant.rs +++ b/src/test/ui/repr/repr-disallow-on-variant.rs @@ -2,7 +2,7 @@ struct Test; enum Foo { #[repr(u8)] - //~^ ERROR attribute should be applied to a struct, enum, or union + //~^ ERROR attribute should be applied to an enum Variant, } diff --git a/src/test/ui/repr/repr-disallow-on-variant.stderr b/src/test/ui/repr/repr-disallow-on-variant.stderr index 70b45e393fcf1..f7e4dcc9d81be 100644 --- a/src/test/ui/repr/repr-disallow-on-variant.stderr +++ b/src/test/ui/repr/repr-disallow-on-variant.stderr @@ -1,11 +1,11 @@ -error[E0517]: attribute should be applied to a struct, enum, or union +error[E0517]: attribute should be applied to an enum --> $DIR/repr-disallow-on-variant.rs:4:12 | LL | #[repr(u8)] | ^^ LL | LL | Variant, - | ------- not a struct, enum, or union + | ------- not an enum error: aborting due to previous error diff --git a/src/test/ui/resolve/auxiliary/issue-80079.rs b/src/test/ui/resolve/auxiliary/issue-80079.rs new file mode 100644 index 0000000000000..190ca75aba86a --- /dev/null +++ b/src/test/ui/resolve/auxiliary/issue-80079.rs @@ -0,0 +1,18 @@ +#![crate_type = "lib"] + +pub mod public { + use private_import; + + // should not be suggested since it is private + struct Foo; + + mod private_module { + // should not be suggested since it is private + pub struct Foo; + } +} + +mod private_import { + // should not be suggested since it is private + pub struct Foo; +} diff --git a/src/test/ui/bad/bad-env-capture.rs b/src/test/ui/resolve/bad-env-capture.rs similarity index 100% rename from src/test/ui/bad/bad-env-capture.rs rename to src/test/ui/resolve/bad-env-capture.rs diff --git a/src/test/ui/bad/bad-env-capture.stderr b/src/test/ui/resolve/bad-env-capture.stderr similarity index 100% rename from src/test/ui/bad/bad-env-capture.stderr rename to src/test/ui/resolve/bad-env-capture.stderr diff --git a/src/test/ui/bad/bad-env-capture2.rs b/src/test/ui/resolve/bad-env-capture2.rs similarity index 100% rename from src/test/ui/bad/bad-env-capture2.rs rename to src/test/ui/resolve/bad-env-capture2.rs diff --git a/src/test/ui/bad/bad-env-capture2.stderr b/src/test/ui/resolve/bad-env-capture2.stderr similarity index 100% rename from src/test/ui/bad/bad-env-capture2.stderr rename to src/test/ui/resolve/bad-env-capture2.stderr diff --git a/src/test/ui/bad/bad-env-capture3.rs b/src/test/ui/resolve/bad-env-capture3.rs similarity index 100% rename from src/test/ui/bad/bad-env-capture3.rs rename to src/test/ui/resolve/bad-env-capture3.rs diff --git a/src/test/ui/bad/bad-env-capture3.stderr b/src/test/ui/resolve/bad-env-capture3.stderr similarity index 100% rename from src/test/ui/bad/bad-env-capture3.stderr rename to src/test/ui/resolve/bad-env-capture3.stderr diff --git a/src/test/ui/bad/bad-expr-path.rs b/src/test/ui/resolve/bad-expr-path.rs similarity index 100% rename from src/test/ui/bad/bad-expr-path.rs rename to src/test/ui/resolve/bad-expr-path.rs diff --git a/src/test/ui/bad/bad-expr-path.stderr b/src/test/ui/resolve/bad-expr-path.stderr similarity index 100% rename from src/test/ui/bad/bad-expr-path.stderr rename to src/test/ui/resolve/bad-expr-path.stderr diff --git a/src/test/ui/bad/bad-expr-path2.rs b/src/test/ui/resolve/bad-expr-path2.rs similarity index 100% rename from src/test/ui/bad/bad-expr-path2.rs rename to src/test/ui/resolve/bad-expr-path2.rs diff --git a/src/test/ui/bad/bad-expr-path2.stderr b/src/test/ui/resolve/bad-expr-path2.stderr similarity index 100% rename from src/test/ui/bad/bad-expr-path2.stderr rename to src/test/ui/resolve/bad-expr-path2.stderr diff --git a/src/test/ui/bad/bad-module.rs b/src/test/ui/resolve/bad-module.rs similarity index 100% rename from src/test/ui/bad/bad-module.rs rename to src/test/ui/resolve/bad-module.rs diff --git a/src/test/ui/bad/bad-module.stderr b/src/test/ui/resolve/bad-module.stderr similarity index 100% rename from src/test/ui/bad/bad-module.stderr rename to src/test/ui/resolve/bad-module.stderr diff --git a/src/test/ui/bad/bad-type-env-capture.rs b/src/test/ui/resolve/bad-type-env-capture.rs similarity index 100% rename from src/test/ui/bad/bad-type-env-capture.rs rename to src/test/ui/resolve/bad-type-env-capture.rs diff --git a/src/test/ui/bad/bad-type-env-capture.stderr b/src/test/ui/resolve/bad-type-env-capture.stderr similarity index 100% rename from src/test/ui/bad/bad-type-env-capture.stderr rename to src/test/ui/resolve/bad-type-env-capture.stderr diff --git a/src/test/ui/issues/issue-2330.rs b/src/test/ui/resolve/issue-2330.rs similarity index 100% rename from src/test/ui/issues/issue-2330.rs rename to src/test/ui/resolve/issue-2330.rs diff --git a/src/test/ui/issues/issue-2330.stderr b/src/test/ui/resolve/issue-2330.stderr similarity index 100% rename from src/test/ui/issues/issue-2330.stderr rename to src/test/ui/resolve/issue-2330.stderr diff --git a/src/test/ui/resolve/issue-3907.stderr b/src/test/ui/resolve/issue-3907.stderr index 4d0b0af58a320..6fc61cae84339 100644 --- a/src/test/ui/resolve/issue-3907.stderr +++ b/src/test/ui/resolve/issue-3907.stderr @@ -5,10 +5,9 @@ LL | impl Foo for S { | ^^^ type aliases cannot be used as traits | help: you might have meant to use `#![feature(trait_alias)]` instead of a `type` alias - --> $DIR/issue-3907.rs:5:1 | -LL | type Foo = dyn issue_3907::Foo; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | trait Foo = dyn issue_3907::Foo; + | help: consider importing this trait instead | LL | use issue_3907::Foo; diff --git a/src/test/ui/resolve/issue-5035-2.stderr b/src/test/ui/resolve/issue-5035-2.stderr index b2084a7a426a6..e94877fded784 100644 --- a/src/test/ui/resolve/issue-5035-2.stderr +++ b/src/test/ui/resolve/issue-5035-2.stderr @@ -8,8 +8,8 @@ LL | fn foo(_x: K) {} = help: unsized fn params are gated as an unstable feature help: function arguments must have a statically known size, borrowed types always have a known size | -LL | fn foo(&_x: K) {} - | ^ +LL | fn foo(_x: &K) {} + | ^ error: aborting due to previous error diff --git a/src/test/ui/resolve/issue-5035.stderr b/src/test/ui/resolve/issue-5035.stderr index 41dff2fe54205..a8aa50b7c3ab2 100644 --- a/src/test/ui/resolve/issue-5035.stderr +++ b/src/test/ui/resolve/issue-5035.stderr @@ -11,16 +11,16 @@ LL | trait I {} | ------- similarly named trait `I` defined here LL | type K = dyn I; LL | impl K for isize {} - | ^ - | | - | type aliases cannot be used as traits - | help: a trait with a similar name exists: `I` + | ^ type aliases cannot be used as traits | help: you might have meant to use `#![feature(trait_alias)]` instead of a `type` alias - --> $DIR/issue-5035.rs:2:1 | -LL | type K = dyn I; - | ^^^^^^^^^^^^^^^ +LL | trait K = dyn I; + | +help: a trait with a similar name exists + | +LL | impl I for isize {} + | ^ error: aborting due to 2 previous errors diff --git a/src/test/ui/resolve/issue-80079.rs b/src/test/ui/resolve/issue-80079.rs new file mode 100644 index 0000000000000..4795ed062c8f6 --- /dev/null +++ b/src/test/ui/resolve/issue-80079.rs @@ -0,0 +1,12 @@ +// aux-build:issue-80079.rs + +// using a module from another crate should not cause errors to suggest private +// items in that module + +extern crate issue_80079; + +use issue_80079::public; + +fn main() { + let _ = Foo; //~ ERROR cannot find value `Foo` in this scope +} diff --git a/src/test/ui/resolve/issue-80079.stderr b/src/test/ui/resolve/issue-80079.stderr new file mode 100644 index 0000000000000..93e8c0341a13a --- /dev/null +++ b/src/test/ui/resolve/issue-80079.stderr @@ -0,0 +1,9 @@ +error[E0425]: cannot find value `Foo` in this scope + --> $DIR/issue-80079.rs:11:13 + | +LL | let _ = Foo; + | ^^^ not found in this scope + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0425`. diff --git a/src/test/ui/resolve/issue-81508.rs b/src/test/ui/resolve/issue-81508.rs new file mode 100644 index 0000000000000..23605cd2fd91d --- /dev/null +++ b/src/test/ui/resolve/issue-81508.rs @@ -0,0 +1,22 @@ +// Confusing diagnostic when using variable as a type: +// +// Previous warnings indicate Foo is not used, when in fact it is +// used improperly as a variable or constant. New warning points +// out user may be trying to use variable as a type. Test demonstrates +// cases for both local variable and const. + +fn main() { + let Baz: &str = ""; + + println!("{}", Baz::Bar); //~ ERROR: failed to resolve: use of undeclared type `Baz` +} + +#[allow(non_upper_case_globals)] +pub const Foo: &str = ""; + +mod submod { + use super::Foo; + fn function() { + println!("{}", Foo::Bar); //~ ERROR: failed to resolve: use of undeclared type `Foo` + } +} diff --git a/src/test/ui/resolve/issue-81508.stderr b/src/test/ui/resolve/issue-81508.stderr new file mode 100644 index 0000000000000..15555631b9047 --- /dev/null +++ b/src/test/ui/resolve/issue-81508.stderr @@ -0,0 +1,21 @@ +error[E0433]: failed to resolve: use of undeclared type `Baz` + --> $DIR/issue-81508.rs:11:20 + | +LL | let Baz: &str = ""; + | --- help: `Baz` is defined here, but is not a type +LL | +LL | println!("{}", Baz::Bar); + | ^^^ use of undeclared type `Baz` + +error[E0433]: failed to resolve: use of undeclared type `Foo` + --> $DIR/issue-81508.rs:20:24 + | +LL | use super::Foo; + | ---------- help: `Foo` is defined here, but is not a type +LL | fn function() { +LL | println!("{}", Foo::Bar); + | ^^^ use of undeclared type `Foo` + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0433`. diff --git a/src/test/ui/resolve/issue-82865.rs b/src/test/ui/resolve/issue-82865.rs new file mode 100644 index 0000000000000..07d88c413bfa8 --- /dev/null +++ b/src/test/ui/resolve/issue-82865.rs @@ -0,0 +1,13 @@ +// Regression test for #82865. + +#![feature(decl_macro)] + +use x::y::z; //~ ERROR: failed to resolve: maybe a missing crate `x`? + +macro mac () { + Box::z //~ ERROR: no function or associated item +} + +fn main() { + mac!(); +} diff --git a/src/test/ui/resolve/issue-82865.stderr b/src/test/ui/resolve/issue-82865.stderr new file mode 100644 index 0000000000000..027d7a0e0e448 --- /dev/null +++ b/src/test/ui/resolve/issue-82865.stderr @@ -0,0 +1,21 @@ +error[E0433]: failed to resolve: maybe a missing crate `x`? + --> $DIR/issue-82865.rs:5:5 + | +LL | use x::y::z; + | ^ maybe a missing crate `x`? + +error[E0599]: no function or associated item named `z` found for struct `Box<_, _>` in the current scope + --> $DIR/issue-82865.rs:8:10 + | +LL | Box::z + | ^ function or associated item not found in `Box<_, _>` +... +LL | mac!(); + | ------- in this macro invocation + | + = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: aborting due to 2 previous errors + +Some errors have detailed explanations: E0433, E0599. +For more information about an error, try `rustc --explain E0433`. diff --git a/src/test/ui/no-implicit-prelude.rs b/src/test/ui/resolve/no-implicit-prelude.rs similarity index 100% rename from src/test/ui/no-implicit-prelude.rs rename to src/test/ui/resolve/no-implicit-prelude.rs diff --git a/src/test/ui/no-implicit-prelude.stderr b/src/test/ui/resolve/no-implicit-prelude.stderr similarity index 100% rename from src/test/ui/no-implicit-prelude.stderr rename to src/test/ui/resolve/no-implicit-prelude.stderr diff --git a/src/test/ui/resolve/raw-ident-in-path.rs b/src/test/ui/resolve/raw-ident-in-path.rs index 1bcbef5943741..7f1163bebde67 100644 --- a/src/test/ui/resolve/raw-ident-in-path.rs +++ b/src/test/ui/resolve/raw-ident-in-path.rs @@ -1,5 +1,5 @@ // Regression test for issue #63882. -type A = crate::r#break; //~ ERROR cannot find type `r#break` in module `crate` +type A = crate::r#break; //~ ERROR cannot find type `r#break` in the crate root fn main() {} diff --git a/src/test/ui/resolve/raw-ident-in-path.stderr b/src/test/ui/resolve/raw-ident-in-path.stderr index f2efcbc8e8586..771dacbbb20d9 100644 --- a/src/test/ui/resolve/raw-ident-in-path.stderr +++ b/src/test/ui/resolve/raw-ident-in-path.stderr @@ -1,8 +1,8 @@ -error[E0412]: cannot find type `r#break` in module `crate` +error[E0412]: cannot find type `r#break` in the crate root --> $DIR/raw-ident-in-path.rs:3:17 | LL | type A = crate::r#break; - | ^^^^^^^ not found in `crate` + | ^^^^^^^ not found in the crate root error: aborting due to previous error diff --git a/src/test/ui/resolve/unboxed-closure-sugar-nonexistent-trait.stderr b/src/test/ui/resolve/unboxed-closure-sugar-nonexistent-trait.stderr index 2974d08eb23b1..8addc0303fb91 100644 --- a/src/test/ui/resolve/unboxed-closure-sugar-nonexistent-trait.stderr +++ b/src/test/ui/resolve/unboxed-closure-sugar-nonexistent-trait.stderr @@ -11,10 +11,9 @@ LL | fn g<F:Typedef(isize) -> isize>(x: F) {} | ^^^^^^^^^^^^^^^^^^^^^^^ type aliases cannot be used as traits | help: you might have meant to use `#![feature(trait_alias)]` instead of a `type` alias - --> $DIR/unboxed-closure-sugar-nonexistent-trait.rs:4:1 | -LL | type Typedef = isize; - | ^^^^^^^^^^^^^^^^^^^^^ +LL | trait Typedef = isize; + | error: aborting due to 2 previous errors diff --git a/src/test/ui/rfc-2091-track-caller/caller-location-intrinsic.rs b/src/test/ui/rfc-2091-track-caller/caller-location-intrinsic.rs index b2ed8a0d7da68..e5754d355d9cb 100644 --- a/src/test/ui/rfc-2091-track-caller/caller-location-intrinsic.rs +++ b/src/test/ui/rfc-2091-track-caller/caller-location-intrinsic.rs @@ -1,6 +1,6 @@ // run-pass // revisions: default mir-opt -//[mir-opt] compile-flags: -Zmir-opt-level=3 +//[mir-opt] compile-flags: -Zmir-opt-level=4 #[inline(never)] #[track_caller] diff --git a/src/test/ui/rfc-2091-track-caller/const-caller-location.rs b/src/test/ui/rfc-2091-track-caller/const-caller-location.rs index 89a42acafba78..89b0b69f38dc0 100644 --- a/src/test/ui/rfc-2091-track-caller/const-caller-location.rs +++ b/src/test/ui/rfc-2091-track-caller/const-caller-location.rs @@ -1,6 +1,6 @@ // run-pass // revisions: default mir-opt -//[mir-opt] compile-flags: -Zmir-opt-level=3 +//[mir-opt] compile-flags: -Zmir-opt-level=4 #![feature(const_caller_location, const_fn)] diff --git a/src/test/ui/rfc-2091-track-caller/error-with-naked.rs b/src/test/ui/rfc-2091-track-caller/error-with-naked.rs index 70ec0e3033c6f..9464ffe872282 100644 --- a/src/test/ui/rfc-2091-track-caller/error-with-naked.rs +++ b/src/test/ui/rfc-2091-track-caller/error-with-naked.rs @@ -1,3 +1,4 @@ +// needs-asm-support #![feature(asm, naked_functions)] #[track_caller] //~ ERROR cannot use `#[track_caller]` with `#[naked]` diff --git a/src/test/ui/rfc-2091-track-caller/error-with-naked.stderr b/src/test/ui/rfc-2091-track-caller/error-with-naked.stderr index 1b49148d629b2..5f17d6b2b5173 100644 --- a/src/test/ui/rfc-2091-track-caller/error-with-naked.stderr +++ b/src/test/ui/rfc-2091-track-caller/error-with-naked.stderr @@ -1,11 +1,11 @@ error[E0736]: cannot use `#[track_caller]` with `#[naked]` - --> $DIR/error-with-naked.rs:3:1 + --> $DIR/error-with-naked.rs:4:1 | LL | #[track_caller] | ^^^^^^^^^^^^^^^ error[E0736]: cannot use `#[track_caller]` with `#[naked]` - --> $DIR/error-with-naked.rs:12:5 + --> $DIR/error-with-naked.rs:13:5 | LL | #[track_caller] | ^^^^^^^^^^^^^^^ diff --git a/src/test/ui/rfc-2091-track-caller/intrinsic-wrapper.rs b/src/test/ui/rfc-2091-track-caller/intrinsic-wrapper.rs index d072fb76f10d8..87e52881c152f 100644 --- a/src/test/ui/rfc-2091-track-caller/intrinsic-wrapper.rs +++ b/src/test/ui/rfc-2091-track-caller/intrinsic-wrapper.rs @@ -1,6 +1,6 @@ // run-pass // revisions: default mir-opt -//[mir-opt] compile-flags: -Zmir-opt-level=3 +//[mir-opt] compile-flags: -Zmir-opt-level=4 macro_rules! caller_location_from_macro { () => (core::panic::Location::caller()); diff --git a/src/test/ui/rfc-2091-track-caller/pass.rs b/src/test/ui/rfc-2091-track-caller/pass.rs index 4d548d4f0aa39..1b13ea3e93c89 100644 --- a/src/test/ui/rfc-2091-track-caller/pass.rs +++ b/src/test/ui/rfc-2091-track-caller/pass.rs @@ -1,6 +1,6 @@ // run-pass // revisions: default mir-opt -//[mir-opt] compile-flags: -Zmir-opt-level=3 +//[mir-opt] compile-flags: -Zmir-opt-level=4 #[track_caller] fn f() {} diff --git a/src/test/ui/rfc-2091-track-caller/std-panic-locations.rs b/src/test/ui/rfc-2091-track-caller/std-panic-locations.rs index 0d7a5c42532f1..f0850d5c1f1e9 100644 --- a/src/test/ui/rfc-2091-track-caller/std-panic-locations.rs +++ b/src/test/ui/rfc-2091-track-caller/std-panic-locations.rs @@ -1,9 +1,8 @@ // run-pass // ignore-wasm32-bare compiled with panic=abort by default // revisions: default mir-opt -//[mir-opt] compile-flags: -Zmir-opt-level=3 +//[mir-opt] compile-flags: -Zmir-opt-level=4 -#![feature(option_expect_none, option_unwrap_none)] #![allow(unconditional_panic)] //! Test that panic locations for `#[track_caller]` functions in std have the correct @@ -32,10 +31,6 @@ fn main() { assert_panicked(|| nope.unwrap()); assert_panicked(|| nope.expect("")); - let yep: Option<()> = Some(()); - assert_panicked(|| yep.unwrap_none()); - assert_panicked(|| yep.expect_none("")); - let oops: Result<(), ()> = Err(()); assert_panicked(|| oops.unwrap()); assert_panicked(|| oops.expect("")); diff --git a/src/test/ui/rfc-2091-track-caller/track-caller-attribute.rs b/src/test/ui/rfc-2091-track-caller/track-caller-attribute.rs index 43135ebbf65df..9d28eb9de095c 100644 --- a/src/test/ui/rfc-2091-track-caller/track-caller-attribute.rs +++ b/src/test/ui/rfc-2091-track-caller/track-caller-attribute.rs @@ -1,6 +1,6 @@ // run-pass // revisions: default mir-opt -//[mir-opt] compile-flags: -Zmir-opt-level=3 +//[mir-opt] compile-flags: -Zmir-opt-level=4 use std::panic::Location; diff --git a/src/test/ui/rfc-2091-track-caller/tracked-fn-ptr-with-arg.rs b/src/test/ui/rfc-2091-track-caller/tracked-fn-ptr-with-arg.rs index 2122a4ca21925..65881257815ac 100644 --- a/src/test/ui/rfc-2091-track-caller/tracked-fn-ptr-with-arg.rs +++ b/src/test/ui/rfc-2091-track-caller/tracked-fn-ptr-with-arg.rs @@ -1,6 +1,6 @@ // run-pass // revisions: default mir-opt -//[mir-opt] compile-flags: -Zmir-opt-level=3 +//[mir-opt] compile-flags: -Zmir-opt-level=4 fn pass_to_ptr_call<T>(f: fn(T), x: T) { f(x); diff --git a/src/test/ui/rfc-2091-track-caller/tracked-fn-ptr.rs b/src/test/ui/rfc-2091-track-caller/tracked-fn-ptr.rs index 922d2d7b7021a..8bb4dd288f054 100644 --- a/src/test/ui/rfc-2091-track-caller/tracked-fn-ptr.rs +++ b/src/test/ui/rfc-2091-track-caller/tracked-fn-ptr.rs @@ -1,6 +1,6 @@ // run-pass // revisions: default mir-opt -//[mir-opt] compile-flags: -Zmir-opt-level=3 +//[mir-opt] compile-flags: -Zmir-opt-level=4 fn ptr_call(f: fn()) { f(); diff --git a/src/test/ui/rfc-2093-infer-outlives/regions-enum-not-wf.rs b/src/test/ui/rfc-2093-infer-outlives/regions-enum-not-wf.rs index 6de08f66d7536..8b491ee4e303f 100644 --- a/src/test/ui/rfc-2093-infer-outlives/regions-enum-not-wf.rs +++ b/src/test/ui/rfc-2093-infer-outlives/regions-enum-not-wf.rs @@ -1,5 +1,3 @@ -// ignore-tidy-linelength - // Various examples of structs whose fields are not well-formed. #![allow(dead_code)] diff --git a/src/test/ui/rfc-2093-infer-outlives/regions-enum-not-wf.stderr b/src/test/ui/rfc-2093-infer-outlives/regions-enum-not-wf.stderr index 36686eaf92f32..553a3e71c169a 100644 --- a/src/test/ui/rfc-2093-infer-outlives/regions-enum-not-wf.stderr +++ b/src/test/ui/rfc-2093-infer-outlives/regions-enum-not-wf.stderr @@ -1,5 +1,5 @@ error[E0309]: the parameter type `T` may not live long enough - --> $DIR/regions-enum-not-wf.rs:19:18 + --> $DIR/regions-enum-not-wf.rs:17:18 | LL | enum Ref1<'a, T> { | - help: consider adding an explicit lifetime bound...: `T: 'a` @@ -7,7 +7,7 @@ LL | Ref1Variant1(RequireOutlives<'a, T>), | ^^^^^^^^^^^^^^^^^^^^^^ ...so that the type `T` will meet its required lifetime bounds error[E0309]: the parameter type `T` may not live long enough - --> $DIR/regions-enum-not-wf.rs:24:25 + --> $DIR/regions-enum-not-wf.rs:22:25 | LL | enum Ref2<'a, T> { | - help: consider adding an explicit lifetime bound...: `T: 'a` @@ -16,7 +16,7 @@ LL | Ref2Variant2(isize, RequireOutlives<'a, T>), | ^^^^^^^^^^^^^^^^^^^^^^ ...so that the type `T` will meet its required lifetime bounds error[E0309]: the parameter type `T` may not live long enough - --> $DIR/regions-enum-not-wf.rs:37:23 + --> $DIR/regions-enum-not-wf.rs:35:23 | LL | enum RefDouble<'a, 'b, T> { | - help: consider adding an explicit lifetime bound...: `T: 'b` diff --git a/src/test/ui/rfc-2126-extern-absolute-paths/non-existent-2.rs b/src/test/ui/rfc-2126-extern-absolute-paths/non-existent-2.rs index 61212f299bec7..def60feb5a6ef 100644 --- a/src/test/ui/rfc-2126-extern-absolute-paths/non-existent-2.rs +++ b/src/test/ui/rfc-2126-extern-absolute-paths/non-existent-2.rs @@ -2,5 +2,5 @@ fn main() { let s = ::xcrate::S; - //~^ ERROR failed to resolve: could not find `xcrate` in crate root + //~^ ERROR failed to resolve: could not find `xcrate` in the list of imported crates } diff --git a/src/test/ui/rfc-2126-extern-absolute-paths/non-existent-2.stderr b/src/test/ui/rfc-2126-extern-absolute-paths/non-existent-2.stderr index 8b2a6933f37bf..7df4f06d1c7e5 100644 --- a/src/test/ui/rfc-2126-extern-absolute-paths/non-existent-2.stderr +++ b/src/test/ui/rfc-2126-extern-absolute-paths/non-existent-2.stderr @@ -1,8 +1,8 @@ -error[E0433]: failed to resolve: could not find `xcrate` in crate root +error[E0433]: failed to resolve: could not find `xcrate` in the list of imported crates --> $DIR/non-existent-2.rs:4:15 | LL | let s = ::xcrate::S; - | ^^^^^^ could not find `xcrate` in crate root + | ^^^^^^ could not find `xcrate` in the list of imported crates error: aborting due to previous error diff --git a/src/test/ui/rfc-2294-if-let-guard/feature-gate.stderr b/src/test/ui/rfc-2294-if-let-guard/feature-gate.stderr index 113870c19f5d5..00811fe3044df 100644 --- a/src/test/ui/rfc-2294-if-let-guard/feature-gate.stderr +++ b/src/test/ui/rfc-2294-if-let-guard/feature-gate.stderr @@ -15,6 +15,7 @@ LL | () if let 0 = 1 => {} | = note: see issue #51114 <https://github.com/rust-lang/rust/issues/51114> for more information = help: add `#![feature(if_let_guard)]` to the crate attributes to enable + = help: you can write `if matches!(<expr>, <pattern>)` instead of `if let <pattern> = <expr>` error[E0658]: `if let` guards are experimental --> $DIR/feature-gate.rs:76:12 @@ -24,6 +25,7 @@ LL | () if let 0 = 1 => {} | = note: see issue #51114 <https://github.com/rust-lang/rust/issues/51114> for more information = help: add `#![feature(if_let_guard)]` to the crate attributes to enable + = help: you can write `if matches!(<expr>, <pattern>)` instead of `if let <pattern> = <expr>` error[E0658]: `let` expressions in this position are experimental --> $DIR/feature-gate.rs:10:16 @@ -33,6 +35,7 @@ LL | () if (let 0 = 1) => {} | = note: see issue #53667 <https://github.com/rust-lang/rust/issues/53667> for more information = help: add `#![feature(let_chains)]` to the crate attributes to enable + = help: you can write `matches!(<expr>, <pattern>)` instead of `let <pattern> = <expr>` error[E0658]: `let` expressions in this position are experimental --> $DIR/feature-gate.rs:14:18 @@ -42,6 +45,7 @@ LL | () if (((let 0 = 1))) => {} | = note: see issue #53667 <https://github.com/rust-lang/rust/issues/53667> for more information = help: add `#![feature(let_chains)]` to the crate attributes to enable + = help: you can write `matches!(<expr>, <pattern>)` instead of `let <pattern> = <expr>` error[E0658]: `let` expressions in this position are experimental --> $DIR/feature-gate.rs:18:23 @@ -51,6 +55,7 @@ LL | () if true && let 0 = 1 => {} | = note: see issue #53667 <https://github.com/rust-lang/rust/issues/53667> for more information = help: add `#![feature(let_chains)]` to the crate attributes to enable + = help: you can write `matches!(<expr>, <pattern>)` instead of `let <pattern> = <expr>` error[E0658]: `let` expressions in this position are experimental --> $DIR/feature-gate.rs:22:15 @@ -60,6 +65,7 @@ LL | () if let 0 = 1 && true => {} | = note: see issue #53667 <https://github.com/rust-lang/rust/issues/53667> for more information = help: add `#![feature(let_chains)]` to the crate attributes to enable + = help: you can write `matches!(<expr>, <pattern>)` instead of `let <pattern> = <expr>` error[E0658]: `let` expressions in this position are experimental --> $DIR/feature-gate.rs:26:16 @@ -69,6 +75,7 @@ LL | () if (let 0 = 1) && true => {} | = note: see issue #53667 <https://github.com/rust-lang/rust/issues/53667> for more information = help: add `#![feature(let_chains)]` to the crate attributes to enable + = help: you can write `matches!(<expr>, <pattern>)` instead of `let <pattern> = <expr>` error[E0658]: `let` expressions in this position are experimental --> $DIR/feature-gate.rs:30:24 @@ -78,6 +85,7 @@ LL | () if true && (let 0 = 1) => {} | = note: see issue #53667 <https://github.com/rust-lang/rust/issues/53667> for more information = help: add `#![feature(let_chains)]` to the crate attributes to enable + = help: you can write `matches!(<expr>, <pattern>)` instead of `let <pattern> = <expr>` error[E0658]: `let` expressions in this position are experimental --> $DIR/feature-gate.rs:34:16 @@ -87,6 +95,7 @@ LL | () if (let 0 = 1) && (let 0 = 1) => {} | = note: see issue #53667 <https://github.com/rust-lang/rust/issues/53667> for more information = help: add `#![feature(let_chains)]` to the crate attributes to enable + = help: you can write `matches!(<expr>, <pattern>)` instead of `let <pattern> = <expr>` error[E0658]: `let` expressions in this position are experimental --> $DIR/feature-gate.rs:34:31 @@ -96,6 +105,7 @@ LL | () if (let 0 = 1) && (let 0 = 1) => {} | = note: see issue #53667 <https://github.com/rust-lang/rust/issues/53667> for more information = help: add `#![feature(let_chains)]` to the crate attributes to enable + = help: you can write `matches!(<expr>, <pattern>)` instead of `let <pattern> = <expr>` error[E0658]: `let` expressions in this position are experimental --> $DIR/feature-gate.rs:40:15 @@ -105,6 +115,7 @@ LL | () if let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = | = note: see issue #53667 <https://github.com/rust-lang/rust/issues/53667> for more information = help: add `#![feature(let_chains)]` to the crate attributes to enable + = help: you can write `matches!(<expr>, <pattern>)` instead of `let <pattern> = <expr>` error[E0658]: `let` expressions in this position are experimental --> $DIR/feature-gate.rs:40:28 @@ -114,6 +125,7 @@ LL | () if let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = | = note: see issue #53667 <https://github.com/rust-lang/rust/issues/53667> for more information = help: add `#![feature(let_chains)]` to the crate attributes to enable + = help: you can write `matches!(<expr>, <pattern>)` instead of `let <pattern> = <expr>` error[E0658]: `let` expressions in this position are experimental --> $DIR/feature-gate.rs:40:42 @@ -123,6 +135,7 @@ LL | () if let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = | = note: see issue #53667 <https://github.com/rust-lang/rust/issues/53667> for more information = help: add `#![feature(let_chains)]` to the crate attributes to enable + = help: you can write `matches!(<expr>, <pattern>)` instead of `let <pattern> = <expr>` error[E0658]: `let` expressions in this position are experimental --> $DIR/feature-gate.rs:40:55 @@ -132,6 +145,7 @@ LL | () if let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = | = note: see issue #53667 <https://github.com/rust-lang/rust/issues/53667> for more information = help: add `#![feature(let_chains)]` to the crate attributes to enable + = help: you can write `matches!(<expr>, <pattern>)` instead of `let <pattern> = <expr>` error[E0658]: `let` expressions in this position are experimental --> $DIR/feature-gate.rs:40:68 @@ -141,6 +155,7 @@ LL | () if let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = | = note: see issue #53667 <https://github.com/rust-lang/rust/issues/53667> for more information = help: add `#![feature(let_chains)]` to the crate attributes to enable + = help: you can write `matches!(<expr>, <pattern>)` instead of `let <pattern> = <expr>` error[E0658]: `let` expressions in this position are experimental --> $DIR/feature-gate.rs:52:15 @@ -150,6 +165,7 @@ LL | () if let Range { start: _, end: _ } = (true..true) && false => {} | = note: see issue #53667 <https://github.com/rust-lang/rust/issues/53667> for more information = help: add `#![feature(let_chains)]` to the crate attributes to enable + = help: you can write `matches!(<expr>, <pattern>)` instead of `let <pattern> = <expr>` error[E0658]: `let` expressions in this position are experimental --> $DIR/feature-gate.rs:68:16 @@ -159,6 +175,7 @@ LL | use_expr!((let 0 = 1 && 0 == 0)); | = note: see issue #53667 <https://github.com/rust-lang/rust/issues/53667> for more information = help: add `#![feature(let_chains)]` to the crate attributes to enable + = help: you can write `matches!(<expr>, <pattern>)` instead of `let <pattern> = <expr>` error[E0658]: `let` expressions in this position are experimental --> $DIR/feature-gate.rs:71:16 @@ -168,6 +185,7 @@ LL | use_expr!((let 0 = 1)); | = note: see issue #53667 <https://github.com/rust-lang/rust/issues/53667> for more information = help: add `#![feature(let_chains)]` to the crate attributes to enable + = help: you can write `matches!(<expr>, <pattern>)` instead of `let <pattern> = <expr>` error: `let` expressions are not supported here --> $DIR/feature-gate.rs:10:16 @@ -175,8 +193,7 @@ error: `let` expressions are not supported here LL | () if (let 0 = 1) => {} | ^^^^^^^^^ | - = note: only supported directly in conditions of `if`- and `while`-expressions - = note: as well as when nested within `&&` and parenthesis in those conditions + = note: only supported directly without parentheses in conditions of `if`- and `while`-expressions, as well as in `let` chains within parentheses error: `let` expressions are not supported here --> $DIR/feature-gate.rs:14:18 @@ -184,8 +201,7 @@ error: `let` expressions are not supported here LL | () if (((let 0 = 1))) => {} | ^^^^^^^^^ | - = note: only supported directly in conditions of `if`- and `while`-expressions - = note: as well as when nested within `&&` and parenthesis in those conditions + = note: only supported directly without parentheses in conditions of `if`- and `while`-expressions, as well as in `let` chains within parentheses error: `let` expressions are not supported here --> $DIR/feature-gate.rs:18:23 @@ -193,8 +209,7 @@ error: `let` expressions are not supported here LL | () if true && let 0 = 1 => {} | ^^^^^^^^^ | - = note: only supported directly in conditions of `if`- and `while`-expressions - = note: as well as when nested within `&&` and parenthesis in those conditions + = note: only supported directly without parentheses in conditions of `if`- and `while`-expressions, as well as in `let` chains within parentheses error: `let` expressions are not supported here --> $DIR/feature-gate.rs:22:15 @@ -202,8 +217,7 @@ error: `let` expressions are not supported here LL | () if let 0 = 1 && true => {} | ^^^^^^^^^ | - = note: only supported directly in conditions of `if`- and `while`-expressions - = note: as well as when nested within `&&` and parenthesis in those conditions + = note: only supported directly without parentheses in conditions of `if`- and `while`-expressions, as well as in `let` chains within parentheses error: `let` expressions are not supported here --> $DIR/feature-gate.rs:26:16 @@ -211,8 +225,7 @@ error: `let` expressions are not supported here LL | () if (let 0 = 1) && true => {} | ^^^^^^^^^ | - = note: only supported directly in conditions of `if`- and `while`-expressions - = note: as well as when nested within `&&` and parenthesis in those conditions + = note: only supported directly without parentheses in conditions of `if`- and `while`-expressions, as well as in `let` chains within parentheses error: `let` expressions are not supported here --> $DIR/feature-gate.rs:30:24 @@ -220,8 +233,7 @@ error: `let` expressions are not supported here LL | () if true && (let 0 = 1) => {} | ^^^^^^^^^ | - = note: only supported directly in conditions of `if`- and `while`-expressions - = note: as well as when nested within `&&` and parenthesis in those conditions + = note: only supported directly without parentheses in conditions of `if`- and `while`-expressions, as well as in `let` chains within parentheses error: `let` expressions are not supported here --> $DIR/feature-gate.rs:34:16 @@ -229,8 +241,7 @@ error: `let` expressions are not supported here LL | () if (let 0 = 1) && (let 0 = 1) => {} | ^^^^^^^^^ | - = note: only supported directly in conditions of `if`- and `while`-expressions - = note: as well as when nested within `&&` and parenthesis in those conditions + = note: only supported directly without parentheses in conditions of `if`- and `while`-expressions, as well as in `let` chains within parentheses error: `let` expressions are not supported here --> $DIR/feature-gate.rs:34:31 @@ -238,8 +249,7 @@ error: `let` expressions are not supported here LL | () if (let 0 = 1) && (let 0 = 1) => {} | ^^^^^^^^^ | - = note: only supported directly in conditions of `if`- and `while`-expressions - = note: as well as when nested within `&&` and parenthesis in those conditions + = note: only supported directly without parentheses in conditions of `if`- and `while`-expressions, as well as in `let` chains within parentheses error: `let` expressions are not supported here --> $DIR/feature-gate.rs:40:15 @@ -247,8 +257,7 @@ error: `let` expressions are not supported here LL | () if let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) => {} | ^^^^^^^^^ | - = note: only supported directly in conditions of `if`- and `while`-expressions - = note: as well as when nested within `&&` and parenthesis in those conditions + = note: only supported directly without parentheses in conditions of `if`- and `while`-expressions, as well as in `let` chains within parentheses error: `let` expressions are not supported here --> $DIR/feature-gate.rs:40:28 @@ -256,8 +265,7 @@ error: `let` expressions are not supported here LL | () if let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) => {} | ^^^^^^^^^ | - = note: only supported directly in conditions of `if`- and `while`-expressions - = note: as well as when nested within `&&` and parenthesis in those conditions + = note: only supported directly without parentheses in conditions of `if`- and `while`-expressions, as well as in `let` chains within parentheses error: `let` expressions are not supported here --> $DIR/feature-gate.rs:40:42 @@ -265,8 +273,7 @@ error: `let` expressions are not supported here LL | () if let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) => {} | ^^^^^^^^^ | - = note: only supported directly in conditions of `if`- and `while`-expressions - = note: as well as when nested within `&&` and parenthesis in those conditions + = note: only supported directly without parentheses in conditions of `if`- and `while`-expressions, as well as in `let` chains within parentheses error: `let` expressions are not supported here --> $DIR/feature-gate.rs:40:55 @@ -274,8 +281,7 @@ error: `let` expressions are not supported here LL | () if let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) => {} | ^^^^^^^^^ | - = note: only supported directly in conditions of `if`- and `while`-expressions - = note: as well as when nested within `&&` and parenthesis in those conditions + = note: only supported directly without parentheses in conditions of `if`- and `while`-expressions, as well as in `let` chains within parentheses error: `let` expressions are not supported here --> $DIR/feature-gate.rs:40:68 @@ -283,8 +289,7 @@ error: `let` expressions are not supported here LL | () if let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) => {} | ^^^^^^^^^ | - = note: only supported directly in conditions of `if`- and `while`-expressions - = note: as well as when nested within `&&` and parenthesis in those conditions + = note: only supported directly without parentheses in conditions of `if`- and `while`-expressions, as well as in `let` chains within parentheses error: `let` expressions are not supported here --> $DIR/feature-gate.rs:52:15 @@ -292,8 +297,7 @@ error: `let` expressions are not supported here LL | () if let Range { start: _, end: _ } = (true..true) && false => {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = note: only supported directly in conditions of `if`- and `while`-expressions - = note: as well as when nested within `&&` and parenthesis in those conditions + = note: only supported directly without parentheses in conditions of `if`- and `while`-expressions, as well as in `let` chains within parentheses error: `let` expressions are not supported here --> $DIR/feature-gate.rs:68:16 @@ -301,8 +305,7 @@ error: `let` expressions are not supported here LL | use_expr!((let 0 = 1 && 0 == 0)); | ^^^^^^^^^ | - = note: only supported directly in conditions of `if`- and `while`-expressions - = note: as well as when nested within `&&` and parenthesis in those conditions + = note: only supported directly without parentheses in conditions of `if`- and `while`-expressions, as well as in `let` chains within parentheses error: `let` expressions are not supported here --> $DIR/feature-gate.rs:71:16 @@ -310,8 +313,7 @@ error: `let` expressions are not supported here LL | use_expr!((let 0 = 1)); | ^^^^^^^^^ | - = note: only supported directly in conditions of `if`- and `while`-expressions - = note: as well as when nested within `&&` and parenthesis in those conditions + = note: only supported directly without parentheses in conditions of `if`- and `while`-expressions, as well as in `let` chains within parentheses error: aborting due to 35 previous errors diff --git a/src/test/ui/rfc-2457/crate_name_nonascii_forbidden-1.rs b/src/test/ui/rfc-2457/crate_name_nonascii_forbidden-1.rs index 310545b92d549..c07ba54af4032 100644 --- a/src/test/ui/rfc-2457/crate_name_nonascii_forbidden-1.rs +++ b/src/test/ui/rfc-2457/crate_name_nonascii_forbidden-1.rs @@ -1,5 +1,3 @@ -#![feature(non_ascii_idents)] - extern crate ьаг; //~ ERROR cannot load a crate with a non-ascii name `ьаг` fn main() {} diff --git a/src/test/ui/rfc-2457/crate_name_nonascii_forbidden-1.stderr b/src/test/ui/rfc-2457/crate_name_nonascii_forbidden-1.stderr index 11108f2fb8678..a8f3abe599eb2 100644 --- a/src/test/ui/rfc-2457/crate_name_nonascii_forbidden-1.stderr +++ b/src/test/ui/rfc-2457/crate_name_nonascii_forbidden-1.stderr @@ -1,5 +1,5 @@ error: cannot load a crate with a non-ascii name `ьаг` - --> $DIR/crate_name_nonascii_forbidden-1.rs:3:1 + --> $DIR/crate_name_nonascii_forbidden-1.rs:1:1 | LL | extern crate ьаг; | ^^^^^^^^^^^^^^^^^ diff --git a/src/test/ui/rfc-2457/crate_name_nonascii_forbidden-2.rs b/src/test/ui/rfc-2457/crate_name_nonascii_forbidden-2.rs index 0249848b35ac0..f8e033937c607 100644 --- a/src/test/ui/rfc-2457/crate_name_nonascii_forbidden-2.rs +++ b/src/test/ui/rfc-2457/crate_name_nonascii_forbidden-2.rs @@ -1,6 +1,5 @@ // compile-flags:--extern му_сгате // edition:2018 -#![feature(non_ascii_idents)] use му_сгате::baz; //~ ERROR cannot load a crate with a non-ascii name `му_сгате` diff --git a/src/test/ui/rfc-2457/crate_name_nonascii_forbidden-2.stderr b/src/test/ui/rfc-2457/crate_name_nonascii_forbidden-2.stderr index 8d3548ed33dcf..05fc4fb22ad1e 100644 --- a/src/test/ui/rfc-2457/crate_name_nonascii_forbidden-2.stderr +++ b/src/test/ui/rfc-2457/crate_name_nonascii_forbidden-2.stderr @@ -1,5 +1,5 @@ error: cannot load a crate with a non-ascii name `му_сгате` - --> $DIR/crate_name_nonascii_forbidden-2.rs:5:5 + --> $DIR/crate_name_nonascii_forbidden-2.rs:4:5 | LL | use му_сгате::baz; | ^^^^^^^^ diff --git a/src/test/ui/rfc-2457/extern_block_nonascii_forbidden.rs b/src/test/ui/rfc-2457/extern_block_nonascii_forbidden.rs new file mode 100644 index 0000000000000..ad6825404306a --- /dev/null +++ b/src/test/ui/rfc-2457/extern_block_nonascii_forbidden.rs @@ -0,0 +1,9 @@ +#![feature(extern_types)] + +extern "C" { + type 一; //~ items in `extern` blocks cannot use non-ascii identifiers + fn 二(); //~ items in `extern` blocks cannot use non-ascii identifiers + static 三: usize; //~ items in `extern` blocks cannot use non-ascii identifiers +} + +fn main() {} diff --git a/src/test/ui/rfc-2457/extern_block_nonascii_forbidden.stderr b/src/test/ui/rfc-2457/extern_block_nonascii_forbidden.stderr new file mode 100644 index 0000000000000..ab8832e916315 --- /dev/null +++ b/src/test/ui/rfc-2457/extern_block_nonascii_forbidden.stderr @@ -0,0 +1,34 @@ +error: items in `extern` blocks cannot use non-ascii identifiers + --> $DIR/extern_block_nonascii_forbidden.rs:4:10 + | +LL | extern "C" { + | ---------- in this `extern` block +LL | type 一; + | ^^ + | + = note: This limitation may be lifted in the future; see issue #83942 <https://github.com/rust-lang/rust/issues/83942> for more information + +error: items in `extern` blocks cannot use non-ascii identifiers + --> $DIR/extern_block_nonascii_forbidden.rs:5:8 + | +LL | extern "C" { + | ---------- in this `extern` block +LL | type 一; +LL | fn 二(); + | ^^ + | + = note: This limitation may be lifted in the future; see issue #83942 <https://github.com/rust-lang/rust/issues/83942> for more information + +error: items in `extern` blocks cannot use non-ascii identifiers + --> $DIR/extern_block_nonascii_forbidden.rs:6:12 + | +LL | extern "C" { + | ---------- in this `extern` block +... +LL | static 三: usize; + | ^^ + | + = note: This limitation may be lifted in the future; see issue #83942 <https://github.com/rust-lang/rust/issues/83942> for more information + +error: aborting due to 3 previous errors + diff --git a/src/test/ui/rfc-2457/idents-normalized.rs b/src/test/ui/rfc-2457/idents-normalized.rs index 109cec7548e2f..1023fee37d5dd 100644 --- a/src/test/ui/rfc-2457/idents-normalized.rs +++ b/src/test/ui/rfc-2457/idents-normalized.rs @@ -1,5 +1,4 @@ // check-pass -#![feature(non_ascii_idents)] struct Résumé; // ['LATIN SMALL LETTER E WITH ACUTE'] diff --git a/src/test/ui/rfc-2457/mod_file_nonascii_forbidden.rs b/src/test/ui/rfc-2457/mod_file_nonascii_forbidden.rs index efd2932f15294..e949e2319c1c4 100644 --- a/src/test/ui/rfc-2457/mod_file_nonascii_forbidden.rs +++ b/src/test/ui/rfc-2457/mod_file_nonascii_forbidden.rs @@ -1,5 +1,3 @@ -#![feature(non_ascii_idents)] - mod řųśť; //~ trying to load file for //~^ file not found for diff --git a/src/test/ui/rfc-2457/mod_file_nonascii_forbidden.stderr b/src/test/ui/rfc-2457/mod_file_nonascii_forbidden.stderr index be729836f4f21..e857a1e60e5fe 100644 --- a/src/test/ui/rfc-2457/mod_file_nonascii_forbidden.stderr +++ b/src/test/ui/rfc-2457/mod_file_nonascii_forbidden.stderr @@ -1,13 +1,13 @@ error[E0583]: file not found for module `řųśť` - --> $DIR/mod_file_nonascii_forbidden.rs:3:1 + --> $DIR/mod_file_nonascii_forbidden.rs:1:1 | LL | mod řųśť; | ^^^^^^^^^ | = help: to create the module `řųśť`, create file "$DIR/řųśť.rs" -error[E0754]: trying to load file for module `řųśť` with non ascii identifer name - --> $DIR/mod_file_nonascii_forbidden.rs:3:5 +error[E0754]: trying to load file for module `řųśť` with non-ascii identifier name + --> $DIR/mod_file_nonascii_forbidden.rs:1:5 | LL | mod řųśť; | ^^^^ diff --git a/src/test/ui/rfc-2457/mod_file_nonascii_with_path_allowed.rs b/src/test/ui/rfc-2457/mod_file_nonascii_with_path_allowed.rs index e9f3fba2fb01e..94327846d6169 100644 --- a/src/test/ui/rfc-2457/mod_file_nonascii_with_path_allowed.rs +++ b/src/test/ui/rfc-2457/mod_file_nonascii_with_path_allowed.rs @@ -1,5 +1,4 @@ // check-pass -#![feature(non_ascii_idents)] #[path="auxiliary/mod_file_nonascii_with_path_allowed-aux.rs"] mod řųśť; diff --git a/src/test/ui/rfc-2457/mod_inline_nonascii_allowed.rs b/src/test/ui/rfc-2457/mod_inline_nonascii_allowed.rs index dd27da432ba65..e1d836b7c3e34 100644 --- a/src/test/ui/rfc-2457/mod_inline_nonascii_allowed.rs +++ b/src/test/ui/rfc-2457/mod_inline_nonascii_allowed.rs @@ -1,5 +1,4 @@ // check-pass -#![feature(non_ascii_idents)] mod řųśť { const IS_GREAT: bool = true; diff --git a/src/test/ui/rfc-2457/no_mangle_nonascii_forbidden.rs b/src/test/ui/rfc-2457/no_mangle_nonascii_forbidden.rs index a408c9757165c..0325d6436abcb 100644 --- a/src/test/ui/rfc-2457/no_mangle_nonascii_forbidden.rs +++ b/src/test/ui/rfc-2457/no_mangle_nonascii_forbidden.rs @@ -1,5 +1,3 @@ -#![feature(non_ascii_idents)] - #[no_mangle] pub fn řųśť() {} //~ `#[no_mangle]` requires ASCII identifier diff --git a/src/test/ui/rfc-2457/no_mangle_nonascii_forbidden.stderr b/src/test/ui/rfc-2457/no_mangle_nonascii_forbidden.stderr index 4ca83e4103208..b4b2b0c7ee001 100644 --- a/src/test/ui/rfc-2457/no_mangle_nonascii_forbidden.stderr +++ b/src/test/ui/rfc-2457/no_mangle_nonascii_forbidden.stderr @@ -1,5 +1,5 @@ error[E0754]: `#[no_mangle]` requires ASCII identifier - --> $DIR/no_mangle_nonascii_forbidden.rs:4:1 + --> $DIR/no_mangle_nonascii_forbidden.rs:2:1 | LL | pub fn řųśť() {} | ^^^^^^^^^^^^^ diff --git a/src/test/ui/rfc-2497-if-let-chains/ast-pretty-check.stdout b/src/test/ui/rfc-2497-if-let-chains/ast-pretty-check.stdout index c88f50c6813ec..aeee43b01cc02 100644 --- a/src/test/ui/rfc-2497-if-let-chains/ast-pretty-check.stdout +++ b/src/test/ui/rfc-2497-if-let-chains/ast-pretty-check.stdout @@ -1,7 +1,7 @@ #![feature(prelude_import)] #![no_std] #[prelude_import] -use ::std::prelude::v1::*; +use ::std::prelude::rust_2015::*; #[macro_use] extern crate std; // build-pass (FIXME(62277): could be check-pass?) diff --git a/src/test/ui/rfc-2497-if-let-chains/disallowed-positions.stderr b/src/test/ui/rfc-2497-if-let-chains/disallowed-positions.stderr index 861a4a80ad652..1adce5e01506d 100644 --- a/src/test/ui/rfc-2497-if-let-chains/disallowed-positions.stderr +++ b/src/test/ui/rfc-2497-if-let-chains/disallowed-positions.stderr @@ -15,8 +15,7 @@ error: `let` expressions are not supported here LL | if &let 0 = 0 {} | ^^^^^^^^^ | - = note: only supported directly in conditions of `if`- and `while`-expressions - = note: as well as when nested within `&&` and parenthesis in those conditions + = note: only supported directly without parentheses in conditions of `if`- and `while`-expressions, as well as in `let` chains within parentheses error: `let` expressions are not supported here --> $DIR/disallowed-positions.rs:35:9 @@ -24,8 +23,7 @@ error: `let` expressions are not supported here LL | if !let 0 = 0 {} | ^^^^^^^^^ | - = note: only supported directly in conditions of `if`- and `while`-expressions - = note: as well as when nested within `&&` and parenthesis in those conditions + = note: only supported directly without parentheses in conditions of `if`- and `while`-expressions, as well as in `let` chains within parentheses error: `let` expressions are not supported here --> $DIR/disallowed-positions.rs:36:9 @@ -33,8 +31,7 @@ error: `let` expressions are not supported here LL | if *let 0 = 0 {} | ^^^^^^^^^ | - = note: only supported directly in conditions of `if`- and `while`-expressions - = note: as well as when nested within `&&` and parenthesis in those conditions + = note: only supported directly without parentheses in conditions of `if`- and `while`-expressions, as well as in `let` chains within parentheses error: `let` expressions are not supported here --> $DIR/disallowed-positions.rs:38:9 @@ -42,8 +39,7 @@ error: `let` expressions are not supported here LL | if -let 0 = 0 {} | ^^^^^^^^^ | - = note: only supported directly in conditions of `if`- and `while`-expressions - = note: as well as when nested within `&&` and parenthesis in those conditions + = note: only supported directly without parentheses in conditions of `if`- and `while`-expressions, as well as in `let` chains within parentheses error: `let` expressions are not supported here --> $DIR/disallowed-positions.rs:46:9 @@ -51,8 +47,7 @@ error: `let` expressions are not supported here LL | if (let 0 = 0)? {} | ^^^^^^^^^ | - = note: only supported directly in conditions of `if`- and `while`-expressions - = note: as well as when nested within `&&` and parenthesis in those conditions + = note: only supported directly without parentheses in conditions of `if`- and `while`-expressions, as well as in `let` chains within parentheses error: `let` expressions are not supported here --> $DIR/disallowed-positions.rs:50:16 @@ -60,8 +55,7 @@ error: `let` expressions are not supported here LL | if true || let 0 = 0 {} | ^^^^^^^^^ | - = note: only supported directly in conditions of `if`- and `while`-expressions - = note: as well as when nested within `&&` and parenthesis in those conditions + = note: only supported directly without parentheses in conditions of `if`- and `while`-expressions, as well as in `let` chains within parentheses error: `let` expressions are not supported here --> $DIR/disallowed-positions.rs:51:17 @@ -69,8 +63,7 @@ error: `let` expressions are not supported here LL | if (true || let 0 = 0) {} | ^^^^^^^^^ | - = note: only supported directly in conditions of `if`- and `while`-expressions - = note: as well as when nested within `&&` and parenthesis in those conditions + = note: only supported directly without parentheses in conditions of `if`- and `while`-expressions, as well as in `let` chains within parentheses error: `let` expressions are not supported here --> $DIR/disallowed-positions.rs:52:25 @@ -78,8 +71,7 @@ error: `let` expressions are not supported here LL | if true && (true || let 0 = 0) {} | ^^^^^^^^^ | - = note: only supported directly in conditions of `if`- and `while`-expressions - = note: as well as when nested within `&&` and parenthesis in those conditions + = note: only supported directly without parentheses in conditions of `if`- and `while`-expressions, as well as in `let` chains within parentheses error: `let` expressions are not supported here --> $DIR/disallowed-positions.rs:53:25 @@ -87,8 +79,7 @@ error: `let` expressions are not supported here LL | if true || (true && let 0 = 0) {} | ^^^^^^^^^ | - = note: only supported directly in conditions of `if`- and `while`-expressions - = note: as well as when nested within `&&` and parenthesis in those conditions + = note: only supported directly without parentheses in conditions of `if`- and `while`-expressions, as well as in `let` chains within parentheses error: `let` expressions are not supported here --> $DIR/disallowed-positions.rs:56:12 @@ -96,8 +87,7 @@ error: `let` expressions are not supported here LL | if x = let 0 = 0 {} | ^^^^^^^^^ | - = note: only supported directly in conditions of `if`- and `while`-expressions - = note: as well as when nested within `&&` and parenthesis in those conditions + = note: only supported directly without parentheses in conditions of `if`- and `while`-expressions, as well as in `let` chains within parentheses error: `let` expressions are not supported here --> $DIR/disallowed-positions.rs:59:15 @@ -105,8 +95,7 @@ error: `let` expressions are not supported here LL | if true..(let 0 = 0) {} | ^^^^^^^^^ | - = note: only supported directly in conditions of `if`- and `while`-expressions - = note: as well as when nested within `&&` and parenthesis in those conditions + = note: only supported directly without parentheses in conditions of `if`- and `while`-expressions, as well as in `let` chains within parentheses error: `let` expressions are not supported here --> $DIR/disallowed-positions.rs:61:11 @@ -114,8 +103,7 @@ error: `let` expressions are not supported here LL | if ..(let 0 = 0) {} | ^^^^^^^^^ | - = note: only supported directly in conditions of `if`- and `while`-expressions - = note: as well as when nested within `&&` and parenthesis in those conditions + = note: only supported directly without parentheses in conditions of `if`- and `while`-expressions, as well as in `let` chains within parentheses error: `let` expressions are not supported here --> $DIR/disallowed-positions.rs:63:9 @@ -123,8 +111,7 @@ error: `let` expressions are not supported here LL | if (let 0 = 0).. {} | ^^^^^^^^^ | - = note: only supported directly in conditions of `if`- and `while`-expressions - = note: as well as when nested within `&&` and parenthesis in those conditions + = note: only supported directly without parentheses in conditions of `if`- and `while`-expressions, as well as in `let` chains within parentheses error: `let` expressions are not supported here --> $DIR/disallowed-positions.rs:67:8 @@ -132,8 +119,7 @@ error: `let` expressions are not supported here LL | if let Range { start: _, end: _ } = true..true && false {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = note: only supported directly in conditions of `if`- and `while`-expressions - = note: as well as when nested within `&&` and parenthesis in those conditions + = note: only supported directly without parentheses in conditions of `if`- and `while`-expressions, as well as in `let` chains within parentheses error: `let` expressions are not supported here --> $DIR/disallowed-positions.rs:71:8 @@ -141,8 +127,7 @@ error: `let` expressions are not supported here LL | if let Range { start: _, end: _ } = true..true || false {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = note: only supported directly in conditions of `if`- and `while`-expressions - = note: as well as when nested within `&&` and parenthesis in those conditions + = note: only supported directly without parentheses in conditions of `if`- and `while`-expressions, as well as in `let` chains within parentheses error: `let` expressions are not supported here --> $DIR/disallowed-positions.rs:78:8 @@ -150,8 +135,7 @@ error: `let` expressions are not supported here LL | if let Range { start: F, end } = F..|| true {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = note: only supported directly in conditions of `if`- and `while`-expressions - = note: as well as when nested within `&&` and parenthesis in those conditions + = note: only supported directly without parentheses in conditions of `if`- and `while`-expressions, as well as in `let` chains within parentheses error: `let` expressions are not supported here --> $DIR/disallowed-positions.rs:86:8 @@ -159,8 +143,7 @@ error: `let` expressions are not supported here LL | if let Range { start: true, end } = t..&&false {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = note: only supported directly in conditions of `if`- and `while`-expressions - = note: as well as when nested within `&&` and parenthesis in those conditions + = note: only supported directly without parentheses in conditions of `if`- and `while`-expressions, as well as in `let` chains within parentheses error: `let` expressions are not supported here --> $DIR/disallowed-positions.rs:92:19 @@ -168,8 +151,7 @@ error: `let` expressions are not supported here LL | if let true = let true = true {} | ^^^^^^^^^^^^^^^ | - = note: only supported directly in conditions of `if`- and `while`-expressions - = note: as well as when nested within `&&` and parenthesis in those conditions + = note: only supported directly without parentheses in conditions of `if`- and `while`-expressions, as well as in `let` chains within parentheses error: `let` expressions are not supported here --> $DIR/disallowed-positions.rs:96:12 @@ -177,8 +159,7 @@ error: `let` expressions are not supported here LL | while &let 0 = 0 {} | ^^^^^^^^^ | - = note: only supported directly in conditions of `if`- and `while`-expressions - = note: as well as when nested within `&&` and parenthesis in those conditions + = note: only supported directly without parentheses in conditions of `if`- and `while`-expressions, as well as in `let` chains within parentheses error: `let` expressions are not supported here --> $DIR/disallowed-positions.rs:99:12 @@ -186,8 +167,7 @@ error: `let` expressions are not supported here LL | while !let 0 = 0 {} | ^^^^^^^^^ | - = note: only supported directly in conditions of `if`- and `while`-expressions - = note: as well as when nested within `&&` and parenthesis in those conditions + = note: only supported directly without parentheses in conditions of `if`- and `while`-expressions, as well as in `let` chains within parentheses error: `let` expressions are not supported here --> $DIR/disallowed-positions.rs:100:12 @@ -195,8 +175,7 @@ error: `let` expressions are not supported here LL | while *let 0 = 0 {} | ^^^^^^^^^ | - = note: only supported directly in conditions of `if`- and `while`-expressions - = note: as well as when nested within `&&` and parenthesis in those conditions + = note: only supported directly without parentheses in conditions of `if`- and `while`-expressions, as well as in `let` chains within parentheses error: `let` expressions are not supported here --> $DIR/disallowed-positions.rs:102:12 @@ -204,8 +183,7 @@ error: `let` expressions are not supported here LL | while -let 0 = 0 {} | ^^^^^^^^^ | - = note: only supported directly in conditions of `if`- and `while`-expressions - = note: as well as when nested within `&&` and parenthesis in those conditions + = note: only supported directly without parentheses in conditions of `if`- and `while`-expressions, as well as in `let` chains within parentheses error: `let` expressions are not supported here --> $DIR/disallowed-positions.rs:110:12 @@ -213,8 +191,7 @@ error: `let` expressions are not supported here LL | while (let 0 = 0)? {} | ^^^^^^^^^ | - = note: only supported directly in conditions of `if`- and `while`-expressions - = note: as well as when nested within `&&` and parenthesis in those conditions + = note: only supported directly without parentheses in conditions of `if`- and `while`-expressions, as well as in `let` chains within parentheses error: `let` expressions are not supported here --> $DIR/disallowed-positions.rs:114:19 @@ -222,8 +199,7 @@ error: `let` expressions are not supported here LL | while true || let 0 = 0 {} | ^^^^^^^^^ | - = note: only supported directly in conditions of `if`- and `while`-expressions - = note: as well as when nested within `&&` and parenthesis in those conditions + = note: only supported directly without parentheses in conditions of `if`- and `while`-expressions, as well as in `let` chains within parentheses error: `let` expressions are not supported here --> $DIR/disallowed-positions.rs:115:20 @@ -231,8 +207,7 @@ error: `let` expressions are not supported here LL | while (true || let 0 = 0) {} | ^^^^^^^^^ | - = note: only supported directly in conditions of `if`- and `while`-expressions - = note: as well as when nested within `&&` and parenthesis in those conditions + = note: only supported directly without parentheses in conditions of `if`- and `while`-expressions, as well as in `let` chains within parentheses error: `let` expressions are not supported here --> $DIR/disallowed-positions.rs:116:28 @@ -240,8 +215,7 @@ error: `let` expressions are not supported here LL | while true && (true || let 0 = 0) {} | ^^^^^^^^^ | - = note: only supported directly in conditions of `if`- and `while`-expressions - = note: as well as when nested within `&&` and parenthesis in those conditions + = note: only supported directly without parentheses in conditions of `if`- and `while`-expressions, as well as in `let` chains within parentheses error: `let` expressions are not supported here --> $DIR/disallowed-positions.rs:117:28 @@ -249,8 +223,7 @@ error: `let` expressions are not supported here LL | while true || (true && let 0 = 0) {} | ^^^^^^^^^ | - = note: only supported directly in conditions of `if`- and `while`-expressions - = note: as well as when nested within `&&` and parenthesis in those conditions + = note: only supported directly without parentheses in conditions of `if`- and `while`-expressions, as well as in `let` chains within parentheses error: `let` expressions are not supported here --> $DIR/disallowed-positions.rs:120:15 @@ -258,8 +231,7 @@ error: `let` expressions are not supported here LL | while x = let 0 = 0 {} | ^^^^^^^^^ | - = note: only supported directly in conditions of `if`- and `while`-expressions - = note: as well as when nested within `&&` and parenthesis in those conditions + = note: only supported directly without parentheses in conditions of `if`- and `while`-expressions, as well as in `let` chains within parentheses error: `let` expressions are not supported here --> $DIR/disallowed-positions.rs:123:18 @@ -267,8 +239,7 @@ error: `let` expressions are not supported here LL | while true..(let 0 = 0) {} | ^^^^^^^^^ | - = note: only supported directly in conditions of `if`- and `while`-expressions - = note: as well as when nested within `&&` and parenthesis in those conditions + = note: only supported directly without parentheses in conditions of `if`- and `while`-expressions, as well as in `let` chains within parentheses error: `let` expressions are not supported here --> $DIR/disallowed-positions.rs:125:14 @@ -276,8 +247,7 @@ error: `let` expressions are not supported here LL | while ..(let 0 = 0) {} | ^^^^^^^^^ | - = note: only supported directly in conditions of `if`- and `while`-expressions - = note: as well as when nested within `&&` and parenthesis in those conditions + = note: only supported directly without parentheses in conditions of `if`- and `while`-expressions, as well as in `let` chains within parentheses error: `let` expressions are not supported here --> $DIR/disallowed-positions.rs:127:12 @@ -285,8 +255,7 @@ error: `let` expressions are not supported here LL | while (let 0 = 0).. {} | ^^^^^^^^^ | - = note: only supported directly in conditions of `if`- and `while`-expressions - = note: as well as when nested within `&&` and parenthesis in those conditions + = note: only supported directly without parentheses in conditions of `if`- and `while`-expressions, as well as in `let` chains within parentheses error: `let` expressions are not supported here --> $DIR/disallowed-positions.rs:131:11 @@ -294,8 +263,7 @@ error: `let` expressions are not supported here LL | while let Range { start: _, end: _ } = true..true && false {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = note: only supported directly in conditions of `if`- and `while`-expressions - = note: as well as when nested within `&&` and parenthesis in those conditions + = note: only supported directly without parentheses in conditions of `if`- and `while`-expressions, as well as in `let` chains within parentheses error: `let` expressions are not supported here --> $DIR/disallowed-positions.rs:135:11 @@ -303,8 +271,7 @@ error: `let` expressions are not supported here LL | while let Range { start: _, end: _ } = true..true || false {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = note: only supported directly in conditions of `if`- and `while`-expressions - = note: as well as when nested within `&&` and parenthesis in those conditions + = note: only supported directly without parentheses in conditions of `if`- and `while`-expressions, as well as in `let` chains within parentheses error: `let` expressions are not supported here --> $DIR/disallowed-positions.rs:142:11 @@ -312,8 +279,7 @@ error: `let` expressions are not supported here LL | while let Range { start: F, end } = F..|| true {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = note: only supported directly in conditions of `if`- and `while`-expressions - = note: as well as when nested within `&&` and parenthesis in those conditions + = note: only supported directly without parentheses in conditions of `if`- and `while`-expressions, as well as in `let` chains within parentheses error: `let` expressions are not supported here --> $DIR/disallowed-positions.rs:150:11 @@ -321,8 +287,7 @@ error: `let` expressions are not supported here LL | while let Range { start: true, end } = t..&&false {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = note: only supported directly in conditions of `if`- and `while`-expressions - = note: as well as when nested within `&&` and parenthesis in those conditions + = note: only supported directly without parentheses in conditions of `if`- and `while`-expressions, as well as in `let` chains within parentheses error: `let` expressions are not supported here --> $DIR/disallowed-positions.rs:156:22 @@ -330,8 +295,7 @@ error: `let` expressions are not supported here LL | while let true = let true = true {} | ^^^^^^^^^^^^^^^ | - = note: only supported directly in conditions of `if`- and `while`-expressions - = note: as well as when nested within `&&` and parenthesis in those conditions + = note: only supported directly without parentheses in conditions of `if`- and `while`-expressions, as well as in `let` chains within parentheses error: `let` expressions are not supported here --> $DIR/disallowed-positions.rs:170:6 @@ -339,8 +303,7 @@ error: `let` expressions are not supported here LL | &let 0 = 0; | ^^^^^^^^^ | - = note: only supported directly in conditions of `if`- and `while`-expressions - = note: as well as when nested within `&&` and parenthesis in those conditions + = note: only supported directly without parentheses in conditions of `if`- and `while`-expressions, as well as in `let` chains within parentheses error: `let` expressions are not supported here --> $DIR/disallowed-positions.rs:172:6 @@ -348,8 +311,7 @@ error: `let` expressions are not supported here LL | !let 0 = 0; | ^^^^^^^^^ | - = note: only supported directly in conditions of `if`- and `while`-expressions - = note: as well as when nested within `&&` and parenthesis in those conditions + = note: only supported directly without parentheses in conditions of `if`- and `while`-expressions, as well as in `let` chains within parentheses error: `let` expressions are not supported here --> $DIR/disallowed-positions.rs:173:6 @@ -357,8 +319,7 @@ error: `let` expressions are not supported here LL | *let 0 = 0; | ^^^^^^^^^ | - = note: only supported directly in conditions of `if`- and `while`-expressions - = note: as well as when nested within `&&` and parenthesis in those conditions + = note: only supported directly without parentheses in conditions of `if`- and `while`-expressions, as well as in `let` chains within parentheses error: `let` expressions are not supported here --> $DIR/disallowed-positions.rs:175:6 @@ -366,8 +327,7 @@ error: `let` expressions are not supported here LL | -let 0 = 0; | ^^^^^^^^^ | - = note: only supported directly in conditions of `if`- and `while`-expressions - = note: as well as when nested within `&&` and parenthesis in those conditions + = note: only supported directly without parentheses in conditions of `if`- and `while`-expressions, as well as in `let` chains within parentheses error: `let` expressions are not supported here --> $DIR/disallowed-positions.rs:183:6 @@ -375,8 +335,7 @@ error: `let` expressions are not supported here LL | (let 0 = 0)?; | ^^^^^^^^^ | - = note: only supported directly in conditions of `if`- and `while`-expressions - = note: as well as when nested within `&&` and parenthesis in those conditions + = note: only supported directly without parentheses in conditions of `if`- and `while`-expressions, as well as in `let` chains within parentheses error: `let` expressions are not supported here --> $DIR/disallowed-positions.rs:187:13 @@ -384,8 +343,7 @@ error: `let` expressions are not supported here LL | true || let 0 = 0; | ^^^^^^^^^ | - = note: only supported directly in conditions of `if`- and `while`-expressions - = note: as well as when nested within `&&` and parenthesis in those conditions + = note: only supported directly without parentheses in conditions of `if`- and `while`-expressions, as well as in `let` chains within parentheses error: `let` expressions are not supported here --> $DIR/disallowed-positions.rs:188:14 @@ -393,8 +351,7 @@ error: `let` expressions are not supported here LL | (true || let 0 = 0); | ^^^^^^^^^ | - = note: only supported directly in conditions of `if`- and `while`-expressions - = note: as well as when nested within `&&` and parenthesis in those conditions + = note: only supported directly without parentheses in conditions of `if`- and `while`-expressions, as well as in `let` chains within parentheses error: `let` expressions are not supported here --> $DIR/disallowed-positions.rs:189:22 @@ -402,8 +359,7 @@ error: `let` expressions are not supported here LL | true && (true || let 0 = 0); | ^^^^^^^^^ | - = note: only supported directly in conditions of `if`- and `while`-expressions - = note: as well as when nested within `&&` and parenthesis in those conditions + = note: only supported directly without parentheses in conditions of `if`- and `while`-expressions, as well as in `let` chains within parentheses error: `let` expressions are not supported here --> $DIR/disallowed-positions.rs:192:9 @@ -411,8 +367,7 @@ error: `let` expressions are not supported here LL | x = let 0 = 0; | ^^^^^^^^^ | - = note: only supported directly in conditions of `if`- and `while`-expressions - = note: as well as when nested within `&&` and parenthesis in those conditions + = note: only supported directly without parentheses in conditions of `if`- and `while`-expressions, as well as in `let` chains within parentheses error: `let` expressions are not supported here --> $DIR/disallowed-positions.rs:194:12 @@ -420,8 +375,7 @@ error: `let` expressions are not supported here LL | true..(let 0 = 0); | ^^^^^^^^^ | - = note: only supported directly in conditions of `if`- and `while`-expressions - = note: as well as when nested within `&&` and parenthesis in those conditions + = note: only supported directly without parentheses in conditions of `if`- and `while`-expressions, as well as in `let` chains within parentheses error: `let` expressions are not supported here --> $DIR/disallowed-positions.rs:195:8 @@ -429,8 +383,7 @@ error: `let` expressions are not supported here LL | ..(let 0 = 0); | ^^^^^^^^^ | - = note: only supported directly in conditions of `if`- and `while`-expressions - = note: as well as when nested within `&&` and parenthesis in those conditions + = note: only supported directly without parentheses in conditions of `if`- and `while`-expressions, as well as in `let` chains within parentheses error: `let` expressions are not supported here --> $DIR/disallowed-positions.rs:196:6 @@ -438,8 +391,7 @@ error: `let` expressions are not supported here LL | (let 0 = 0)..; | ^^^^^^^^^ | - = note: only supported directly in conditions of `if`- and `while`-expressions - = note: as well as when nested within `&&` and parenthesis in those conditions + = note: only supported directly without parentheses in conditions of `if`- and `while`-expressions, as well as in `let` chains within parentheses error: `let` expressions are not supported here --> $DIR/disallowed-positions.rs:198:6 @@ -447,8 +399,7 @@ error: `let` expressions are not supported here LL | (let Range { start: _, end: _ } = true..true || false); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = note: only supported directly in conditions of `if`- and `while`-expressions - = note: as well as when nested within `&&` and parenthesis in those conditions + = note: only supported directly without parentheses in conditions of `if`- and `while`-expressions, as well as in `let` chains within parentheses error: `let` expressions are not supported here --> $DIR/disallowed-positions.rs:202:6 @@ -456,8 +407,7 @@ error: `let` expressions are not supported here LL | (let true = let true = true); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = note: only supported directly in conditions of `if`- and `while`-expressions - = note: as well as when nested within `&&` and parenthesis in those conditions + = note: only supported directly without parentheses in conditions of `if`- and `while`-expressions, as well as in `let` chains within parentheses error: `let` expressions are not supported here --> $DIR/disallowed-positions.rs:202:17 @@ -465,8 +415,7 @@ error: `let` expressions are not supported here LL | (let true = let true = true); | ^^^^^^^^^^^^^^^ | - = note: only supported directly in conditions of `if`- and `while`-expressions - = note: as well as when nested within `&&` and parenthesis in those conditions + = note: only supported directly without parentheses in conditions of `if`- and `while`-expressions, as well as in `let` chains within parentheses error: `let` expressions are not supported here --> $DIR/disallowed-positions.rs:207:6 @@ -474,8 +423,7 @@ error: `let` expressions are not supported here LL | &let 0 = 0 | ^^^^^^^^^ | - = note: only supported directly in conditions of `if`- and `while`-expressions - = note: as well as when nested within `&&` and parenthesis in those conditions + = note: only supported directly without parentheses in conditions of `if`- and `while`-expressions, as well as in `let` chains within parentheses error: `let` expressions are not supported here --> $DIR/disallowed-positions.rs:218:17 @@ -483,8 +431,7 @@ error: `let` expressions are not supported here LL | true && let 1 = 1 | ^^^^^^^^^ | - = note: only supported directly in conditions of `if`- and `while`-expressions - = note: as well as when nested within `&&` and parenthesis in those conditions + = note: only supported directly without parentheses in conditions of `if`- and `while`-expressions, as well as in `let` chains within parentheses error: `let` expressions are not supported here --> $DIR/disallowed-positions.rs:222:17 @@ -492,8 +439,7 @@ error: `let` expressions are not supported here LL | true && let 1 = 1 | ^^^^^^^^^ | - = note: only supported directly in conditions of `if`- and `while`-expressions - = note: as well as when nested within `&&` and parenthesis in those conditions + = note: only supported directly without parentheses in conditions of `if`- and `while`-expressions, as well as in `let` chains within parentheses error: `let` expressions are not supported here --> $DIR/disallowed-positions.rs:226:17 @@ -501,8 +447,7 @@ error: `let` expressions are not supported here LL | true && let 1 = 1 | ^^^^^^^^^ | - = note: only supported directly in conditions of `if`- and `while`-expressions - = note: as well as when nested within `&&` and parenthesis in those conditions + = note: only supported directly without parentheses in conditions of `if`- and `while`-expressions, as well as in `let` chains within parentheses error: `let` expressions are not supported here --> $DIR/disallowed-positions.rs:236:17 @@ -510,8 +455,7 @@ error: `let` expressions are not supported here LL | true && let 1 = 1 | ^^^^^^^^^ | - = note: only supported directly in conditions of `if`- and `while`-expressions - = note: as well as when nested within `&&` and parenthesis in those conditions + = note: only supported directly without parentheses in conditions of `if`- and `while`-expressions, as well as in `let` chains within parentheses warning: the feature `const_generics` is incomplete and may not be safe to use and/or cause compiler crashes --> $DIR/disallowed-positions.rs:20:12 diff --git a/src/test/ui/rfc-2497-if-let-chains/feature-gate.rs b/src/test/ui/rfc-2497-if-let-chains/feature-gate.rs index f5cb1860d4786..0b38b5f47efef 100644 --- a/src/test/ui/rfc-2497-if-let-chains/feature-gate.rs +++ b/src/test/ui/rfc-2497-if-let-chains/feature-gate.rs @@ -13,11 +13,11 @@ fn _if() { if (let 0 = 1) {} //~^ ERROR `let` expressions in this position are experimental [E0658] - //~| ERROR `let` expressions are not supported here + //~| ERROR invalid parentheses around `let` expression in `if let` if (((let 0 = 1))) {} //~^ ERROR `let` expressions in this position are experimental [E0658] - //~| ERROR `let` expressions are not supported here + //~| ERROR invalid parentheses around `let` expression in `if let` if true && let 0 = 1 {} //~^ ERROR `let` expressions in this position are experimental [E0658] @@ -126,7 +126,7 @@ fn _macros() { //~| ERROR `let` expressions are not supported here use_expr!((let 0 = 1)); //~^ ERROR `let` expressions in this position are experimental [E0658] - //~| ERROR `let` expressions are not supported here + //~| ERROR invalid parentheses around `let` expression in `if let` //~| ERROR `let` expressions are not supported here #[cfg(FALSE)] (let 0 = 1); //~^ ERROR `let` expressions in this position are experimental [E0658] diff --git a/src/test/ui/rfc-2497-if-let-chains/feature-gate.stderr b/src/test/ui/rfc-2497-if-let-chains/feature-gate.stderr index 178e86272877d..7364f62c92229 100644 --- a/src/test/ui/rfc-2497-if-let-chains/feature-gate.stderr +++ b/src/test/ui/rfc-2497-if-let-chains/feature-gate.stderr @@ -15,6 +15,7 @@ LL | if (let 0 = 1) {} | = note: see issue #53667 <https://github.com/rust-lang/rust/issues/53667> for more information = help: add `#![feature(let_chains)]` to the crate attributes to enable + = help: you can write `matches!(<expr>, <pattern>)` instead of `let <pattern> = <expr>` error[E0658]: `let` expressions in this position are experimental --> $DIR/feature-gate.rs:18:11 @@ -24,6 +25,7 @@ LL | if (((let 0 = 1))) {} | = note: see issue #53667 <https://github.com/rust-lang/rust/issues/53667> for more information = help: add `#![feature(let_chains)]` to the crate attributes to enable + = help: you can write `matches!(<expr>, <pattern>)` instead of `let <pattern> = <expr>` error[E0658]: `let` expressions in this position are experimental --> $DIR/feature-gate.rs:22:16 @@ -33,6 +35,7 @@ LL | if true && let 0 = 1 {} | = note: see issue #53667 <https://github.com/rust-lang/rust/issues/53667> for more information = help: add `#![feature(let_chains)]` to the crate attributes to enable + = help: you can write `matches!(<expr>, <pattern>)` instead of `let <pattern> = <expr>` error[E0658]: `let` expressions in this position are experimental --> $DIR/feature-gate.rs:26:8 @@ -42,6 +45,7 @@ LL | if let 0 = 1 && true {} | = note: see issue #53667 <https://github.com/rust-lang/rust/issues/53667> for more information = help: add `#![feature(let_chains)]` to the crate attributes to enable + = help: you can write `matches!(<expr>, <pattern>)` instead of `let <pattern> = <expr>` error[E0658]: `let` expressions in this position are experimental --> $DIR/feature-gate.rs:30:9 @@ -51,6 +55,7 @@ LL | if (let 0 = 1) && true {} | = note: see issue #53667 <https://github.com/rust-lang/rust/issues/53667> for more information = help: add `#![feature(let_chains)]` to the crate attributes to enable + = help: you can write `matches!(<expr>, <pattern>)` instead of `let <pattern> = <expr>` error[E0658]: `let` expressions in this position are experimental --> $DIR/feature-gate.rs:34:17 @@ -60,6 +65,7 @@ LL | if true && (let 0 = 1) {} | = note: see issue #53667 <https://github.com/rust-lang/rust/issues/53667> for more information = help: add `#![feature(let_chains)]` to the crate attributes to enable + = help: you can write `matches!(<expr>, <pattern>)` instead of `let <pattern> = <expr>` error[E0658]: `let` expressions in this position are experimental --> $DIR/feature-gate.rs:38:9 @@ -69,6 +75,7 @@ LL | if (let 0 = 1) && (let 0 = 1) {} | = note: see issue #53667 <https://github.com/rust-lang/rust/issues/53667> for more information = help: add `#![feature(let_chains)]` to the crate attributes to enable + = help: you can write `matches!(<expr>, <pattern>)` instead of `let <pattern> = <expr>` error[E0658]: `let` expressions in this position are experimental --> $DIR/feature-gate.rs:38:24 @@ -78,6 +85,7 @@ LL | if (let 0 = 1) && (let 0 = 1) {} | = note: see issue #53667 <https://github.com/rust-lang/rust/issues/53667> for more information = help: add `#![feature(let_chains)]` to the crate attributes to enable + = help: you can write `matches!(<expr>, <pattern>)` instead of `let <pattern> = <expr>` error[E0658]: `let` expressions in this position are experimental --> $DIR/feature-gate.rs:44:8 @@ -87,6 +95,7 @@ LL | if let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) {} | = note: see issue #53667 <https://github.com/rust-lang/rust/issues/53667> for more information = help: add `#![feature(let_chains)]` to the crate attributes to enable + = help: you can write `matches!(<expr>, <pattern>)` instead of `let <pattern> = <expr>` error[E0658]: `let` expressions in this position are experimental --> $DIR/feature-gate.rs:44:21 @@ -96,6 +105,7 @@ LL | if let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) {} | = note: see issue #53667 <https://github.com/rust-lang/rust/issues/53667> for more information = help: add `#![feature(let_chains)]` to the crate attributes to enable + = help: you can write `matches!(<expr>, <pattern>)` instead of `let <pattern> = <expr>` error[E0658]: `let` expressions in this position are experimental --> $DIR/feature-gate.rs:44:35 @@ -105,6 +115,7 @@ LL | if let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) {} | = note: see issue #53667 <https://github.com/rust-lang/rust/issues/53667> for more information = help: add `#![feature(let_chains)]` to the crate attributes to enable + = help: you can write `matches!(<expr>, <pattern>)` instead of `let <pattern> = <expr>` error[E0658]: `let` expressions in this position are experimental --> $DIR/feature-gate.rs:44:48 @@ -114,6 +125,7 @@ LL | if let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) {} | = note: see issue #53667 <https://github.com/rust-lang/rust/issues/53667> for more information = help: add `#![feature(let_chains)]` to the crate attributes to enable + = help: you can write `matches!(<expr>, <pattern>)` instead of `let <pattern> = <expr>` error[E0658]: `let` expressions in this position are experimental --> $DIR/feature-gate.rs:44:61 @@ -123,6 +135,7 @@ LL | if let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) {} | = note: see issue #53667 <https://github.com/rust-lang/rust/issues/53667> for more information = help: add `#![feature(let_chains)]` to the crate attributes to enable + = help: you can write `matches!(<expr>, <pattern>)` instead of `let <pattern> = <expr>` error[E0658]: `let` expressions in this position are experimental --> $DIR/feature-gate.rs:56:8 @@ -132,6 +145,7 @@ LL | if let Range { start: _, end: _ } = (true..true) && false {} | = note: see issue #53667 <https://github.com/rust-lang/rust/issues/53667> for more information = help: add `#![feature(let_chains)]` to the crate attributes to enable + = help: you can write `matches!(<expr>, <pattern>)` instead of `let <pattern> = <expr>` error[E0658]: `let` expressions in this position are experimental --> $DIR/feature-gate.rs:64:12 @@ -141,6 +155,7 @@ LL | while (let 0 = 1) {} | = note: see issue #53667 <https://github.com/rust-lang/rust/issues/53667> for more information = help: add `#![feature(let_chains)]` to the crate attributes to enable + = help: you can write `matches!(<expr>, <pattern>)` instead of `let <pattern> = <expr>` error[E0658]: `let` expressions in this position are experimental --> $DIR/feature-gate.rs:68:14 @@ -150,6 +165,7 @@ LL | while (((let 0 = 1))) {} | = note: see issue #53667 <https://github.com/rust-lang/rust/issues/53667> for more information = help: add `#![feature(let_chains)]` to the crate attributes to enable + = help: you can write `matches!(<expr>, <pattern>)` instead of `let <pattern> = <expr>` error[E0658]: `let` expressions in this position are experimental --> $DIR/feature-gate.rs:72:19 @@ -159,6 +175,7 @@ LL | while true && let 0 = 1 {} | = note: see issue #53667 <https://github.com/rust-lang/rust/issues/53667> for more information = help: add `#![feature(let_chains)]` to the crate attributes to enable + = help: you can write `matches!(<expr>, <pattern>)` instead of `let <pattern> = <expr>` error[E0658]: `let` expressions in this position are experimental --> $DIR/feature-gate.rs:76:11 @@ -168,6 +185,7 @@ LL | while let 0 = 1 && true {} | = note: see issue #53667 <https://github.com/rust-lang/rust/issues/53667> for more information = help: add `#![feature(let_chains)]` to the crate attributes to enable + = help: you can write `matches!(<expr>, <pattern>)` instead of `let <pattern> = <expr>` error[E0658]: `let` expressions in this position are experimental --> $DIR/feature-gate.rs:80:12 @@ -177,6 +195,7 @@ LL | while (let 0 = 1) && true {} | = note: see issue #53667 <https://github.com/rust-lang/rust/issues/53667> for more information = help: add `#![feature(let_chains)]` to the crate attributes to enable + = help: you can write `matches!(<expr>, <pattern>)` instead of `let <pattern> = <expr>` error[E0658]: `let` expressions in this position are experimental --> $DIR/feature-gate.rs:84:20 @@ -186,6 +205,7 @@ LL | while true && (let 0 = 1) {} | = note: see issue #53667 <https://github.com/rust-lang/rust/issues/53667> for more information = help: add `#![feature(let_chains)]` to the crate attributes to enable + = help: you can write `matches!(<expr>, <pattern>)` instead of `let <pattern> = <expr>` error[E0658]: `let` expressions in this position are experimental --> $DIR/feature-gate.rs:88:12 @@ -195,6 +215,7 @@ LL | while (let 0 = 1) && (let 0 = 1) {} | = note: see issue #53667 <https://github.com/rust-lang/rust/issues/53667> for more information = help: add `#![feature(let_chains)]` to the crate attributes to enable + = help: you can write `matches!(<expr>, <pattern>)` instead of `let <pattern> = <expr>` error[E0658]: `let` expressions in this position are experimental --> $DIR/feature-gate.rs:88:27 @@ -204,6 +225,7 @@ LL | while (let 0 = 1) && (let 0 = 1) {} | = note: see issue #53667 <https://github.com/rust-lang/rust/issues/53667> for more information = help: add `#![feature(let_chains)]` to the crate attributes to enable + = help: you can write `matches!(<expr>, <pattern>)` instead of `let <pattern> = <expr>` error[E0658]: `let` expressions in this position are experimental --> $DIR/feature-gate.rs:94:11 @@ -213,6 +235,7 @@ LL | while let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) { | = note: see issue #53667 <https://github.com/rust-lang/rust/issues/53667> for more information = help: add `#![feature(let_chains)]` to the crate attributes to enable + = help: you can write `matches!(<expr>, <pattern>)` instead of `let <pattern> = <expr>` error[E0658]: `let` expressions in this position are experimental --> $DIR/feature-gate.rs:94:24 @@ -222,6 +245,7 @@ LL | while let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) { | = note: see issue #53667 <https://github.com/rust-lang/rust/issues/53667> for more information = help: add `#![feature(let_chains)]` to the crate attributes to enable + = help: you can write `matches!(<expr>, <pattern>)` instead of `let <pattern> = <expr>` error[E0658]: `let` expressions in this position are experimental --> $DIR/feature-gate.rs:94:38 @@ -231,6 +255,7 @@ LL | while let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) { | = note: see issue #53667 <https://github.com/rust-lang/rust/issues/53667> for more information = help: add `#![feature(let_chains)]` to the crate attributes to enable + = help: you can write `matches!(<expr>, <pattern>)` instead of `let <pattern> = <expr>` error[E0658]: `let` expressions in this position are experimental --> $DIR/feature-gate.rs:94:51 @@ -240,6 +265,7 @@ LL | while let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) { | = note: see issue #53667 <https://github.com/rust-lang/rust/issues/53667> for more information = help: add `#![feature(let_chains)]` to the crate attributes to enable + = help: you can write `matches!(<expr>, <pattern>)` instead of `let <pattern> = <expr>` error[E0658]: `let` expressions in this position are experimental --> $DIR/feature-gate.rs:94:64 @@ -249,6 +275,7 @@ LL | while let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) { | = note: see issue #53667 <https://github.com/rust-lang/rust/issues/53667> for more information = help: add `#![feature(let_chains)]` to the crate attributes to enable + = help: you can write `matches!(<expr>, <pattern>)` instead of `let <pattern> = <expr>` error[E0658]: `let` expressions in this position are experimental --> $DIR/feature-gate.rs:106:11 @@ -258,6 +285,7 @@ LL | while let Range { start: _, end: _ } = (true..true) && false {} | = note: see issue #53667 <https://github.com/rust-lang/rust/issues/53667> for more information = help: add `#![feature(let_chains)]` to the crate attributes to enable + = help: you can write `matches!(<expr>, <pattern>)` instead of `let <pattern> = <expr>` error[E0658]: `let` expressions in this position are experimental --> $DIR/feature-gate.rs:131:20 @@ -267,6 +295,7 @@ LL | #[cfg(FALSE)] (let 0 = 1); | = note: see issue #53667 <https://github.com/rust-lang/rust/issues/53667> for more information = help: add `#![feature(let_chains)]` to the crate attributes to enable + = help: you can write `matches!(<expr>, <pattern>)` instead of `let <pattern> = <expr>` error[E0658]: `let` expressions in this position are experimental --> $DIR/feature-gate.rs:114:17 @@ -276,6 +305,7 @@ LL | noop_expr!((let 0 = 1)); | = note: see issue #53667 <https://github.com/rust-lang/rust/issues/53667> for more information = help: add `#![feature(let_chains)]` to the crate attributes to enable + = help: you can write `matches!(<expr>, <pattern>)` instead of `let <pattern> = <expr>` error[E0658]: `let` expressions in this position are experimental --> $DIR/feature-gate.rs:123:16 @@ -285,6 +315,7 @@ LL | use_expr!((let 0 = 1 && 0 == 0)); | = note: see issue #53667 <https://github.com/rust-lang/rust/issues/53667> for more information = help: add `#![feature(let_chains)]` to the crate attributes to enable + = help: you can write `matches!(<expr>, <pattern>)` instead of `let <pattern> = <expr>` error[E0658]: `let` expressions in this position are experimental --> $DIR/feature-gate.rs:127:16 @@ -294,24 +325,29 @@ LL | use_expr!((let 0 = 1)); | = note: see issue #53667 <https://github.com/rust-lang/rust/issues/53667> for more information = help: add `#![feature(let_chains)]` to the crate attributes to enable + = help: you can write `matches!(<expr>, <pattern>)` instead of `let <pattern> = <expr>` -error: `let` expressions are not supported here - --> $DIR/feature-gate.rs:14:9 +error: invalid parentheses around `let` expression in `if let` + --> $DIR/feature-gate.rs:14:8 | LL | if (let 0 = 1) {} - | ^^^^^^^^^ + | ^ ^ + | +help: `if let` needs to be written without parentheses | - = note: only supported directly in conditions of `if`- and `while`-expressions - = note: as well as when nested within `&&` and parenthesis in those conditions +LL | if let 0 = 1 {} + | -- -- -error: `let` expressions are not supported here - --> $DIR/feature-gate.rs:18:11 +error: invalid parentheses around `let` expression in `if let` + --> $DIR/feature-gate.rs:18:8 | LL | if (((let 0 = 1))) {} - | ^^^^^^^^^ + | ^^^ ^^^ | - = note: only supported directly in conditions of `if`- and `while`-expressions - = note: as well as when nested within `&&` and parenthesis in those conditions +help: `if let` needs to be written without parentheses + | +LL | if let 0 = 1 {} + | -- -- error: `let` expressions are not supported here --> $DIR/feature-gate.rs:22:16 @@ -319,8 +355,7 @@ error: `let` expressions are not supported here LL | if true && let 0 = 1 {} | ^^^^^^^^^ | - = note: only supported directly in conditions of `if`- and `while`-expressions - = note: as well as when nested within `&&` and parenthesis in those conditions + = note: only supported directly without parentheses in conditions of `if`- and `while`-expressions, as well as in `let` chains within parentheses error: `let` expressions are not supported here --> $DIR/feature-gate.rs:26:8 @@ -328,8 +363,7 @@ error: `let` expressions are not supported here LL | if let 0 = 1 && true {} | ^^^^^^^^^ | - = note: only supported directly in conditions of `if`- and `while`-expressions - = note: as well as when nested within `&&` and parenthesis in those conditions + = note: only supported directly without parentheses in conditions of `if`- and `while`-expressions, as well as in `let` chains within parentheses error: `let` expressions are not supported here --> $DIR/feature-gate.rs:30:9 @@ -337,8 +371,7 @@ error: `let` expressions are not supported here LL | if (let 0 = 1) && true {} | ^^^^^^^^^ | - = note: only supported directly in conditions of `if`- and `while`-expressions - = note: as well as when nested within `&&` and parenthesis in those conditions + = note: only supported directly without parentheses in conditions of `if`- and `while`-expressions, as well as in `let` chains within parentheses error: `let` expressions are not supported here --> $DIR/feature-gate.rs:34:17 @@ -346,8 +379,7 @@ error: `let` expressions are not supported here LL | if true && (let 0 = 1) {} | ^^^^^^^^^ | - = note: only supported directly in conditions of `if`- and `while`-expressions - = note: as well as when nested within `&&` and parenthesis in those conditions + = note: only supported directly without parentheses in conditions of `if`- and `while`-expressions, as well as in `let` chains within parentheses error: `let` expressions are not supported here --> $DIR/feature-gate.rs:38:9 @@ -355,8 +387,7 @@ error: `let` expressions are not supported here LL | if (let 0 = 1) && (let 0 = 1) {} | ^^^^^^^^^ | - = note: only supported directly in conditions of `if`- and `while`-expressions - = note: as well as when nested within `&&` and parenthesis in those conditions + = note: only supported directly without parentheses in conditions of `if`- and `while`-expressions, as well as in `let` chains within parentheses error: `let` expressions are not supported here --> $DIR/feature-gate.rs:38:24 @@ -364,8 +395,7 @@ error: `let` expressions are not supported here LL | if (let 0 = 1) && (let 0 = 1) {} | ^^^^^^^^^ | - = note: only supported directly in conditions of `if`- and `while`-expressions - = note: as well as when nested within `&&` and parenthesis in those conditions + = note: only supported directly without parentheses in conditions of `if`- and `while`-expressions, as well as in `let` chains within parentheses error: `let` expressions are not supported here --> $DIR/feature-gate.rs:44:8 @@ -373,8 +403,7 @@ error: `let` expressions are not supported here LL | if let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) {} | ^^^^^^^^^ | - = note: only supported directly in conditions of `if`- and `while`-expressions - = note: as well as when nested within `&&` and parenthesis in those conditions + = note: only supported directly without parentheses in conditions of `if`- and `while`-expressions, as well as in `let` chains within parentheses error: `let` expressions are not supported here --> $DIR/feature-gate.rs:44:21 @@ -382,8 +411,7 @@ error: `let` expressions are not supported here LL | if let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) {} | ^^^^^^^^^ | - = note: only supported directly in conditions of `if`- and `while`-expressions - = note: as well as when nested within `&&` and parenthesis in those conditions + = note: only supported directly without parentheses in conditions of `if`- and `while`-expressions, as well as in `let` chains within parentheses error: `let` expressions are not supported here --> $DIR/feature-gate.rs:44:35 @@ -391,8 +419,7 @@ error: `let` expressions are not supported here LL | if let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) {} | ^^^^^^^^^ | - = note: only supported directly in conditions of `if`- and `while`-expressions - = note: as well as when nested within `&&` and parenthesis in those conditions + = note: only supported directly without parentheses in conditions of `if`- and `while`-expressions, as well as in `let` chains within parentheses error: `let` expressions are not supported here --> $DIR/feature-gate.rs:44:48 @@ -400,8 +427,7 @@ error: `let` expressions are not supported here LL | if let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) {} | ^^^^^^^^^ | - = note: only supported directly in conditions of `if`- and `while`-expressions - = note: as well as when nested within `&&` and parenthesis in those conditions + = note: only supported directly without parentheses in conditions of `if`- and `while`-expressions, as well as in `let` chains within parentheses error: `let` expressions are not supported here --> $DIR/feature-gate.rs:44:61 @@ -409,8 +435,7 @@ error: `let` expressions are not supported here LL | if let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) {} | ^^^^^^^^^ | - = note: only supported directly in conditions of `if`- and `while`-expressions - = note: as well as when nested within `&&` and parenthesis in those conditions + = note: only supported directly without parentheses in conditions of `if`- and `while`-expressions, as well as in `let` chains within parentheses error: `let` expressions are not supported here --> $DIR/feature-gate.rs:56:8 @@ -418,8 +443,7 @@ error: `let` expressions are not supported here LL | if let Range { start: _, end: _ } = (true..true) && false {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = note: only supported directly in conditions of `if`- and `while`-expressions - = note: as well as when nested within `&&` and parenthesis in those conditions + = note: only supported directly without parentheses in conditions of `if`- and `while`-expressions, as well as in `let` chains within parentheses error: `let` expressions are not supported here --> $DIR/feature-gate.rs:64:12 @@ -427,8 +451,7 @@ error: `let` expressions are not supported here LL | while (let 0 = 1) {} | ^^^^^^^^^ | - = note: only supported directly in conditions of `if`- and `while`-expressions - = note: as well as when nested within `&&` and parenthesis in those conditions + = note: only supported directly without parentheses in conditions of `if`- and `while`-expressions, as well as in `let` chains within parentheses error: `let` expressions are not supported here --> $DIR/feature-gate.rs:68:14 @@ -436,8 +459,7 @@ error: `let` expressions are not supported here LL | while (((let 0 = 1))) {} | ^^^^^^^^^ | - = note: only supported directly in conditions of `if`- and `while`-expressions - = note: as well as when nested within `&&` and parenthesis in those conditions + = note: only supported directly without parentheses in conditions of `if`- and `while`-expressions, as well as in `let` chains within parentheses error: `let` expressions are not supported here --> $DIR/feature-gate.rs:72:19 @@ -445,8 +467,7 @@ error: `let` expressions are not supported here LL | while true && let 0 = 1 {} | ^^^^^^^^^ | - = note: only supported directly in conditions of `if`- and `while`-expressions - = note: as well as when nested within `&&` and parenthesis in those conditions + = note: only supported directly without parentheses in conditions of `if`- and `while`-expressions, as well as in `let` chains within parentheses error: `let` expressions are not supported here --> $DIR/feature-gate.rs:76:11 @@ -454,8 +475,7 @@ error: `let` expressions are not supported here LL | while let 0 = 1 && true {} | ^^^^^^^^^ | - = note: only supported directly in conditions of `if`- and `while`-expressions - = note: as well as when nested within `&&` and parenthesis in those conditions + = note: only supported directly without parentheses in conditions of `if`- and `while`-expressions, as well as in `let` chains within parentheses error: `let` expressions are not supported here --> $DIR/feature-gate.rs:80:12 @@ -463,8 +483,7 @@ error: `let` expressions are not supported here LL | while (let 0 = 1) && true {} | ^^^^^^^^^ | - = note: only supported directly in conditions of `if`- and `while`-expressions - = note: as well as when nested within `&&` and parenthesis in those conditions + = note: only supported directly without parentheses in conditions of `if`- and `while`-expressions, as well as in `let` chains within parentheses error: `let` expressions are not supported here --> $DIR/feature-gate.rs:84:20 @@ -472,8 +491,7 @@ error: `let` expressions are not supported here LL | while true && (let 0 = 1) {} | ^^^^^^^^^ | - = note: only supported directly in conditions of `if`- and `while`-expressions - = note: as well as when nested within `&&` and parenthesis in those conditions + = note: only supported directly without parentheses in conditions of `if`- and `while`-expressions, as well as in `let` chains within parentheses error: `let` expressions are not supported here --> $DIR/feature-gate.rs:88:12 @@ -481,8 +499,7 @@ error: `let` expressions are not supported here LL | while (let 0 = 1) && (let 0 = 1) {} | ^^^^^^^^^ | - = note: only supported directly in conditions of `if`- and `while`-expressions - = note: as well as when nested within `&&` and parenthesis in those conditions + = note: only supported directly without parentheses in conditions of `if`- and `while`-expressions, as well as in `let` chains within parentheses error: `let` expressions are not supported here --> $DIR/feature-gate.rs:88:27 @@ -490,8 +507,7 @@ error: `let` expressions are not supported here LL | while (let 0 = 1) && (let 0 = 1) {} | ^^^^^^^^^ | - = note: only supported directly in conditions of `if`- and `while`-expressions - = note: as well as when nested within `&&` and parenthesis in those conditions + = note: only supported directly without parentheses in conditions of `if`- and `while`-expressions, as well as in `let` chains within parentheses error: `let` expressions are not supported here --> $DIR/feature-gate.rs:94:11 @@ -499,8 +515,7 @@ error: `let` expressions are not supported here LL | while let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) {} | ^^^^^^^^^ | - = note: only supported directly in conditions of `if`- and `while`-expressions - = note: as well as when nested within `&&` and parenthesis in those conditions + = note: only supported directly without parentheses in conditions of `if`- and `while`-expressions, as well as in `let` chains within parentheses error: `let` expressions are not supported here --> $DIR/feature-gate.rs:94:24 @@ -508,8 +523,7 @@ error: `let` expressions are not supported here LL | while let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) {} | ^^^^^^^^^ | - = note: only supported directly in conditions of `if`- and `while`-expressions - = note: as well as when nested within `&&` and parenthesis in those conditions + = note: only supported directly without parentheses in conditions of `if`- and `while`-expressions, as well as in `let` chains within parentheses error: `let` expressions are not supported here --> $DIR/feature-gate.rs:94:38 @@ -517,8 +531,7 @@ error: `let` expressions are not supported here LL | while let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) {} | ^^^^^^^^^ | - = note: only supported directly in conditions of `if`- and `while`-expressions - = note: as well as when nested within `&&` and parenthesis in those conditions + = note: only supported directly without parentheses in conditions of `if`- and `while`-expressions, as well as in `let` chains within parentheses error: `let` expressions are not supported here --> $DIR/feature-gate.rs:94:51 @@ -526,8 +539,7 @@ error: `let` expressions are not supported here LL | while let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) {} | ^^^^^^^^^ | - = note: only supported directly in conditions of `if`- and `while`-expressions - = note: as well as when nested within `&&` and parenthesis in those conditions + = note: only supported directly without parentheses in conditions of `if`- and `while`-expressions, as well as in `let` chains within parentheses error: `let` expressions are not supported here --> $DIR/feature-gate.rs:94:64 @@ -535,8 +547,7 @@ error: `let` expressions are not supported here LL | while let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) {} | ^^^^^^^^^ | - = note: only supported directly in conditions of `if`- and `while`-expressions - = note: as well as when nested within `&&` and parenthesis in those conditions + = note: only supported directly without parentheses in conditions of `if`- and `while`-expressions, as well as in `let` chains within parentheses error: `let` expressions are not supported here --> $DIR/feature-gate.rs:106:11 @@ -544,8 +555,7 @@ error: `let` expressions are not supported here LL | while let Range { start: _, end: _ } = (true..true) && false {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = note: only supported directly in conditions of `if`- and `while`-expressions - = note: as well as when nested within `&&` and parenthesis in those conditions + = note: only supported directly without parentheses in conditions of `if`- and `while`-expressions, as well as in `let` chains within parentheses error: `let` expressions are not supported here --> $DIR/feature-gate.rs:123:16 @@ -553,8 +563,7 @@ error: `let` expressions are not supported here LL | use_expr!((let 0 = 1 && 0 == 0)); | ^^^^^^^^^ | - = note: only supported directly in conditions of `if`- and `while`-expressions - = note: as well as when nested within `&&` and parenthesis in those conditions + = note: only supported directly without parentheses in conditions of `if`- and `while`-expressions, as well as in `let` chains within parentheses error: `let` expressions are not supported here --> $DIR/feature-gate.rs:123:16 @@ -562,17 +571,18 @@ error: `let` expressions are not supported here LL | use_expr!((let 0 = 1 && 0 == 0)); | ^^^^^^^^^ | - = note: only supported directly in conditions of `if`- and `while`-expressions - = note: as well as when nested within `&&` and parenthesis in those conditions + = note: only supported directly without parentheses in conditions of `if`- and `while`-expressions, as well as in `let` chains within parentheses -error: `let` expressions are not supported here - --> $DIR/feature-gate.rs:127:16 +error: invalid parentheses around `let` expression in `if let` + --> $DIR/feature-gate.rs:127:15 | LL | use_expr!((let 0 = 1)); - | ^^^^^^^^^ + | ^ ^ + | +help: `if let` needs to be written without parentheses | - = note: only supported directly in conditions of `if`- and `while`-expressions - = note: as well as when nested within `&&` and parenthesis in those conditions +LL | use_expr!(let 0 = 1); + | -- -- error: `let` expressions are not supported here --> $DIR/feature-gate.rs:127:16 @@ -580,8 +590,7 @@ error: `let` expressions are not supported here LL | use_expr!((let 0 = 1)); | ^^^^^^^^^ | - = note: only supported directly in conditions of `if`- and `while`-expressions - = note: as well as when nested within `&&` and parenthesis in those conditions + = note: only supported directly without parentheses in conditions of `if`- and `while`-expressions, as well as in `let` chains within parentheses error: aborting due to 65 previous errors diff --git a/src/test/ui/rfc-2565-param-attrs/proc-macro-cannot-be-used.rs b/src/test/ui/rfc-2565-param-attrs/proc-macro-cannot-be-used.rs index fcfa610ec8554..54f2f451bbe53 100644 --- a/src/test/ui/rfc-2565-param-attrs/proc-macro-cannot-be-used.rs +++ b/src/test/ui/rfc-2565-param-attrs/proc-macro-cannot-be-used.rs @@ -1,6 +1,7 @@ // aux-build:ident-mac.rs #![feature(c_variadic)] +#![allow(anonymous_parameters)] extern crate ident_mac; use ident_mac::id; diff --git a/src/test/ui/rfc-2565-param-attrs/proc-macro-cannot-be-used.stderr b/src/test/ui/rfc-2565-param-attrs/proc-macro-cannot-be-used.stderr index 38c5050f3428d..e74d05d49b8d7 100644 --- a/src/test/ui/rfc-2565-param-attrs/proc-macro-cannot-be-used.stderr +++ b/src/test/ui/rfc-2565-param-attrs/proc-macro-cannot-be-used.stderr @@ -1,173 +1,173 @@ error: expected non-macro attribute, found attribute macro `id` - --> $DIR/proc-macro-cannot-be-used.rs:10:23 + --> $DIR/proc-macro-cannot-be-used.rs:11:23 | LL | extern "C" { fn ffi(#[id] arg1: i32, #[id] ...); } | ^^ not a non-macro attribute error: expected non-macro attribute, found attribute macro `id` - --> $DIR/proc-macro-cannot-be-used.rs:10:40 + --> $DIR/proc-macro-cannot-be-used.rs:11:40 | LL | extern "C" { fn ffi(#[id] arg1: i32, #[id] ...); } | ^^ not a non-macro attribute error: expected non-macro attribute, found attribute macro `id` - --> $DIR/proc-macro-cannot-be-used.rs:14:40 + --> $DIR/proc-macro-cannot-be-used.rs:15:40 | LL | unsafe extern "C" fn cvar(arg1: i32, #[id] mut args: ...) {} | ^^ not a non-macro attribute error: expected non-macro attribute, found attribute macro `id` - --> $DIR/proc-macro-cannot-be-used.rs:17:30 + --> $DIR/proc-macro-cannot-be-used.rs:18:30 | LL | type Alias = extern "C" fn(#[id] u8, #[id] ...); | ^^ not a non-macro attribute error: expected non-macro attribute, found attribute macro `id` - --> $DIR/proc-macro-cannot-be-used.rs:17:40 + --> $DIR/proc-macro-cannot-be-used.rs:18:40 | LL | type Alias = extern "C" fn(#[id] u8, #[id] ...); | ^^ not a non-macro attribute error: expected non-macro attribute, found attribute macro `id` - --> $DIR/proc-macro-cannot-be-used.rs:21:11 + --> $DIR/proc-macro-cannot-be-used.rs:22:11 | LL | fn free(#[id] arg1: u8) { | ^^ not a non-macro attribute error: expected non-macro attribute, found attribute macro `id` - --> $DIR/proc-macro-cannot-be-used.rs:23:18 + --> $DIR/proc-macro-cannot-be-used.rs:24:18 | LL | let lam = |#[id] W(x), #[id] y: usize| (); | ^^ not a non-macro attribute error: expected non-macro attribute, found attribute macro `id` - --> $DIR/proc-macro-cannot-be-used.rs:23:30 + --> $DIR/proc-macro-cannot-be-used.rs:24:30 | LL | let lam = |#[id] W(x), #[id] y: usize| (); | ^^ not a non-macro attribute error: expected non-macro attribute, found attribute macro `id` - --> $DIR/proc-macro-cannot-be-used.rs:29:20 + --> $DIR/proc-macro-cannot-be-used.rs:30:20 | LL | fn inherent1(#[id] self, #[id] arg1: u8) {} | ^^ not a non-macro attribute error: expected non-macro attribute, found attribute macro `id` - --> $DIR/proc-macro-cannot-be-used.rs:29:32 + --> $DIR/proc-macro-cannot-be-used.rs:30:32 | LL | fn inherent1(#[id] self, #[id] arg1: u8) {} | ^^ not a non-macro attribute error: expected non-macro attribute, found attribute macro `id` - --> $DIR/proc-macro-cannot-be-used.rs:32:20 + --> $DIR/proc-macro-cannot-be-used.rs:33:20 | LL | fn inherent2(#[id] &self, #[id] arg1: u8) {} | ^^ not a non-macro attribute error: expected non-macro attribute, found attribute macro `id` - --> $DIR/proc-macro-cannot-be-used.rs:32:33 + --> $DIR/proc-macro-cannot-be-used.rs:33:33 | LL | fn inherent2(#[id] &self, #[id] arg1: u8) {} | ^^ not a non-macro attribute error: expected non-macro attribute, found attribute macro `id` - --> $DIR/proc-macro-cannot-be-used.rs:35:24 + --> $DIR/proc-macro-cannot-be-used.rs:36:24 | LL | fn inherent3<'a>(#[id] &'a mut self, #[id] arg1: u8) {} | ^^ not a non-macro attribute error: expected non-macro attribute, found attribute macro `id` - --> $DIR/proc-macro-cannot-be-used.rs:35:44 + --> $DIR/proc-macro-cannot-be-used.rs:36:44 | LL | fn inherent3<'a>(#[id] &'a mut self, #[id] arg1: u8) {} | ^^ not a non-macro attribute error: expected non-macro attribute, found attribute macro `id` - --> $DIR/proc-macro-cannot-be-used.rs:38:24 + --> $DIR/proc-macro-cannot-be-used.rs:39:24 | LL | fn inherent4<'a>(#[id] self: Box<Self>, #[id] arg1: u8) {} | ^^ not a non-macro attribute error: expected non-macro attribute, found attribute macro `id` - --> $DIR/proc-macro-cannot-be-used.rs:38:47 + --> $DIR/proc-macro-cannot-be-used.rs:39:47 | LL | fn inherent4<'a>(#[id] self: Box<Self>, #[id] arg1: u8) {} | ^^ not a non-macro attribute error: expected non-macro attribute, found attribute macro `id` - --> $DIR/proc-macro-cannot-be-used.rs:41:40 + --> $DIR/proc-macro-cannot-be-used.rs:42:40 | LL | fn issue_64682_associated_fn<'a>(#[id] arg1: u8, #[id] arg2: u8) {} | ^^ not a non-macro attribute error: expected non-macro attribute, found attribute macro `id` - --> $DIR/proc-macro-cannot-be-used.rs:41:56 + --> $DIR/proc-macro-cannot-be-used.rs:42:56 | LL | fn issue_64682_associated_fn<'a>(#[id] arg1: u8, #[id] arg2: u8) {} | ^^ not a non-macro attribute error: expected non-macro attribute, found attribute macro `id` - --> $DIR/proc-macro-cannot-be-used.rs:47:17 + --> $DIR/proc-macro-cannot-be-used.rs:48:17 | LL | fn trait1(#[id] self, #[id] arg1: u8); | ^^ not a non-macro attribute error: expected non-macro attribute, found attribute macro `id` - --> $DIR/proc-macro-cannot-be-used.rs:47:29 + --> $DIR/proc-macro-cannot-be-used.rs:48:29 | LL | fn trait1(#[id] self, #[id] arg1: u8); | ^^ not a non-macro attribute error: expected non-macro attribute, found attribute macro `id` - --> $DIR/proc-macro-cannot-be-used.rs:50:17 + --> $DIR/proc-macro-cannot-be-used.rs:51:17 | LL | fn trait2(#[id] &self, #[id] arg1: u8); | ^^ not a non-macro attribute error: expected non-macro attribute, found attribute macro `id` - --> $DIR/proc-macro-cannot-be-used.rs:50:30 + --> $DIR/proc-macro-cannot-be-used.rs:51:30 | LL | fn trait2(#[id] &self, #[id] arg1: u8); | ^^ not a non-macro attribute error: expected non-macro attribute, found attribute macro `id` - --> $DIR/proc-macro-cannot-be-used.rs:53:21 + --> $DIR/proc-macro-cannot-be-used.rs:54:21 | LL | fn trait3<'a>(#[id] &'a mut self, #[id] arg1: u8); | ^^ not a non-macro attribute error: expected non-macro attribute, found attribute macro `id` - --> $DIR/proc-macro-cannot-be-used.rs:53:41 + --> $DIR/proc-macro-cannot-be-used.rs:54:41 | LL | fn trait3<'a>(#[id] &'a mut self, #[id] arg1: u8); | ^^ not a non-macro attribute error: expected non-macro attribute, found attribute macro `id` - --> $DIR/proc-macro-cannot-be-used.rs:56:21 + --> $DIR/proc-macro-cannot-be-used.rs:57:21 | LL | fn trait4<'a>(#[id] self: Box<Self>, #[id] arg1: u8, #[id] Vec<u8>); | ^^ not a non-macro attribute error: expected non-macro attribute, found attribute macro `id` - --> $DIR/proc-macro-cannot-be-used.rs:56:44 + --> $DIR/proc-macro-cannot-be-used.rs:57:44 | LL | fn trait4<'a>(#[id] self: Box<Self>, #[id] arg1: u8, #[id] Vec<u8>); | ^^ not a non-macro attribute error: expected non-macro attribute, found attribute macro `id` - --> $DIR/proc-macro-cannot-be-used.rs:56:60 + --> $DIR/proc-macro-cannot-be-used.rs:57:60 | LL | fn trait4<'a>(#[id] self: Box<Self>, #[id] arg1: u8, #[id] Vec<u8>); | ^^ not a non-macro attribute error: expected non-macro attribute, found attribute macro `id` - --> $DIR/proc-macro-cannot-be-used.rs:60:40 + --> $DIR/proc-macro-cannot-be-used.rs:61:40 | LL | fn issue_64682_associated_fn<'a>(#[id] arg1: u8, #[id] arg2: u8); | ^^ not a non-macro attribute error: expected non-macro attribute, found attribute macro `id` - --> $DIR/proc-macro-cannot-be-used.rs:60:56 + --> $DIR/proc-macro-cannot-be-used.rs:61:56 | LL | fn issue_64682_associated_fn<'a>(#[id] arg1: u8, #[id] arg2: u8); | ^^ not a non-macro attribute diff --git a/src/test/ui/rfc-2632-const-trait-impl/const-and-non-const-impl.stderr b/src/test/ui/rfc-2632-const-trait-impl/const-and-non-const-impl.stderr index 2b4fa66ecf282..99560ed06888a 100644 --- a/src/test/ui/rfc-2632-const-trait-impl/const-and-non-const-impl.stderr +++ b/src/test/ui/rfc-2632-const-trait-impl/const-and-non-const-impl.stderr @@ -1,4 +1,4 @@ -error[E0119]: conflicting implementations of trait `std::ops::Add` for type `i32`: +error[E0119]: conflicting implementations of trait `std::ops::Add` for type `i32` --> $DIR/const-and-non-const-impl.rs:6:1 | LL | impl const std::ops::Add for i32 { @@ -7,7 +7,7 @@ LL | impl const std::ops::Add for i32 { = note: conflicting implementation in crate `core`: - impl Add for i32; -error[E0119]: conflicting implementations of trait `std::ops::Add` for type `Int`: +error[E0119]: conflicting implementations of trait `std::ops::Add` for type `Int` --> $DIR/const-and-non-const-impl.rs:24:1 | LL | impl std::ops::Add for Int { diff --git a/src/test/ui/rfc1623.nll.stderr b/src/test/ui/rfc1623.nll.stderr index b5dd0c9d2a6c8..a3d94679434d1 100644 --- a/src/test/ui/rfc1623.nll.stderr +++ b/src/test/ui/rfc1623.nll.stderr @@ -11,7 +11,11 @@ LL | | }; | = help: within `&SomeStruct`, the trait `Sync` is not implemented for `dyn for<'a, 'b> Fn(&'a Foo<'b>) -> &'a Foo<'b>` = note: required because it appears within the type `&dyn for<'a, 'b> Fn(&'a Foo<'b>) -> &'a Foo<'b>` - = note: required because it appears within the type `SomeStruct` +note: required because it appears within the type `SomeStruct` + --> $DIR/rfc1623.rs:11:8 + | +LL | struct SomeStruct<'x, 'y, 'z: 'x> { + | ^^^^^^^^^^ = note: required because it appears within the type `&SomeStruct` = note: shared static variables must have a type that implements `Sync` diff --git a/src/test/ui/rustdoc/README.md b/src/test/ui/rustdoc/README.md new file mode 100644 index 0000000000000..1c98ab038ab72 --- /dev/null +++ b/src/test/ui/rustdoc/README.md @@ -0,0 +1,3 @@ +This directory is for tests that have to do with rustdoc, but test the behavior +of rustc. For example, rustc should not warn that an attribute rustdoc uses is +unknown. diff --git a/src/test/ui/cfg-rustdoc.rs b/src/test/ui/rustdoc/cfg-rustdoc.rs similarity index 100% rename from src/test/ui/cfg-rustdoc.rs rename to src/test/ui/rustdoc/cfg-rustdoc.rs diff --git a/src/test/ui/cfg-rustdoc.stderr b/src/test/ui/rustdoc/cfg-rustdoc.stderr similarity index 100% rename from src/test/ui/cfg-rustdoc.stderr rename to src/test/ui/rustdoc/cfg-rustdoc.stderr diff --git a/src/test/ui/check-doc-alias-attr-location.rs b/src/test/ui/rustdoc/check-doc-alias-attr-location.rs similarity index 100% rename from src/test/ui/check-doc-alias-attr-location.rs rename to src/test/ui/rustdoc/check-doc-alias-attr-location.rs diff --git a/src/test/ui/check-doc-alias-attr-location.stderr b/src/test/ui/rustdoc/check-doc-alias-attr-location.stderr similarity index 100% rename from src/test/ui/check-doc-alias-attr-location.stderr rename to src/test/ui/rustdoc/check-doc-alias-attr-location.stderr diff --git a/src/test/ui/rustdoc/check-doc-alias-attr.rs b/src/test/ui/rustdoc/check-doc-alias-attr.rs new file mode 100644 index 0000000000000..719b98604c497 --- /dev/null +++ b/src/test/ui/rustdoc/check-doc-alias-attr.rs @@ -0,0 +1,28 @@ +#![crate_type = "lib"] + +#[doc(alias = "foo")] // ok! +#[doc(alias("bar", "baz"))] // ok! +pub struct Bar; + +#[doc(alias)] //~ ERROR +#[doc(alias = 0)] //~ ERROR +#[doc(alias = "\"")] //~ ERROR +#[doc(alias = "\n")] //~ ERROR +#[doc(alias = " +")] //~^ ERROR +#[doc(alias = "\t")] //~ ERROR +#[doc(alias = " hello")] //~ ERROR +#[doc(alias = "hello ")] //~ ERROR +#[doc(alias = "")] //~ ERROR +pub struct Foo; + +#[doc(alias(0))] //~ ERROR +#[doc(alias("\""))] //~ ERROR +#[doc(alias("\n"))] //~ ERROR +#[doc(alias(" +"))] //~^ ERROR +#[doc(alias("\t"))] //~ ERROR +#[doc(alias(" hello"))] //~ ERROR +#[doc(alias("hello "))] //~ ERROR +#[doc(alias(""))] //~ ERROR +pub struct Foo2; diff --git a/src/test/ui/rustdoc/check-doc-alias-attr.stderr b/src/test/ui/rustdoc/check-doc-alias-attr.stderr new file mode 100644 index 0000000000000..250568be3333f --- /dev/null +++ b/src/test/ui/rustdoc/check-doc-alias-attr.stderr @@ -0,0 +1,108 @@ +error: doc alias attribute expects a string `#[doc(alias = "a")]` or a list of strings `#[doc(alias("a", "b"))]` + --> $DIR/check-doc-alias-attr.rs:7:7 + | +LL | #[doc(alias)] + | ^^^^^ + +error: doc alias attribute expects a string `#[doc(alias = "a")]` or a list of strings `#[doc(alias("a", "b"))]` + --> $DIR/check-doc-alias-attr.rs:8:7 + | +LL | #[doc(alias = 0)] + | ^^^^^^^^^ + +error: '"' character isn't allowed in `#[doc(alias = "...")]` + --> $DIR/check-doc-alias-attr.rs:9:15 + | +LL | #[doc(alias = "\"")] + | ^^^^ + +error: '\n' character isn't allowed in `#[doc(alias = "...")]` + --> $DIR/check-doc-alias-attr.rs:10:15 + | +LL | #[doc(alias = "\n")] + | ^^^^ + +error: '\n' character isn't allowed in `#[doc(alias = "...")]` + --> $DIR/check-doc-alias-attr.rs:11:15 + | +LL | #[doc(alias = " + | _______________^ +LL | | ")] + | |_^ + +error: '\t' character isn't allowed in `#[doc(alias = "...")]` + --> $DIR/check-doc-alias-attr.rs:13:15 + | +LL | #[doc(alias = "\t")] + | ^^^^ + +error: `#[doc(alias = "...")]` cannot start or end with ' ' + --> $DIR/check-doc-alias-attr.rs:14:15 + | +LL | #[doc(alias = " hello")] + | ^^^^^^^^ + +error: `#[doc(alias = "...")]` cannot start or end with ' ' + --> $DIR/check-doc-alias-attr.rs:15:15 + | +LL | #[doc(alias = "hello ")] + | ^^^^^^^^ + +error: `#[doc(alias = "...")]` attribute cannot have empty value + --> $DIR/check-doc-alias-attr.rs:16:15 + | +LL | #[doc(alias = "")] + | ^^ + +error: `#[doc(alias("a"))]` expects string literals + --> $DIR/check-doc-alias-attr.rs:19:13 + | +LL | #[doc(alias(0))] + | ^ + +error: '"' character isn't allowed in `#[doc(alias("..."))]` + --> $DIR/check-doc-alias-attr.rs:20:13 + | +LL | #[doc(alias("\""))] + | ^^^^ + +error: '\n' character isn't allowed in `#[doc(alias("..."))]` + --> $DIR/check-doc-alias-attr.rs:21:13 + | +LL | #[doc(alias("\n"))] + | ^^^^ + +error: '\n' character isn't allowed in `#[doc(alias("..."))]` + --> $DIR/check-doc-alias-attr.rs:22:13 + | +LL | #[doc(alias(" + | _____________^ +LL | | "))] + | |_^ + +error: '\t' character isn't allowed in `#[doc(alias("..."))]` + --> $DIR/check-doc-alias-attr.rs:24:13 + | +LL | #[doc(alias("\t"))] + | ^^^^ + +error: `#[doc(alias("..."))]` cannot start or end with ' ' + --> $DIR/check-doc-alias-attr.rs:25:13 + | +LL | #[doc(alias(" hello"))] + | ^^^^^^^^ + +error: `#[doc(alias("..."))]` cannot start or end with ' ' + --> $DIR/check-doc-alias-attr.rs:26:13 + | +LL | #[doc(alias("hello "))] + | ^^^^^^^^ + +error: `#[doc(alias("..."))]` attribute cannot have empty value + --> $DIR/check-doc-alias-attr.rs:27:13 + | +LL | #[doc(alias(""))] + | ^^ + +error: aborting due to 17 previous errors + diff --git a/src/test/ui/doc-alias-crate-level.rs b/src/test/ui/rustdoc/doc-alias-crate-level.rs similarity index 100% rename from src/test/ui/doc-alias-crate-level.rs rename to src/test/ui/rustdoc/doc-alias-crate-level.rs diff --git a/src/test/ui/doc-alias-crate-level.stderr b/src/test/ui/rustdoc/doc-alias-crate-level.stderr similarity index 84% rename from src/test/ui/doc-alias-crate-level.stderr rename to src/test/ui/rustdoc/doc-alias-crate-level.stderr index c0467514ae1c7..bd32609ade296 100644 --- a/src/test/ui/doc-alias-crate-level.stderr +++ b/src/test/ui/rustdoc/doc-alias-crate-level.stderr @@ -4,7 +4,7 @@ error: '\'' character isn't allowed in `#[doc(alias = "...")]` LL | #[doc(alias = "shouldn't work!")] | ^^^^^^^^^^^^^^^^^ -error: `#![doc(alias = "...")]` isn't allowed as a crate level attribute +error: `#![doc(alias = "...")]` isn't allowed as a crate-level attribute --> $DIR/doc-alias-crate-level.rs:5:8 | LL | #![doc(alias = "not working!")] diff --git a/src/test/ui/doc-alias-same-name.rs b/src/test/ui/rustdoc/doc-alias-same-name.rs similarity index 100% rename from src/test/ui/doc-alias-same-name.rs rename to src/test/ui/rustdoc/doc-alias-same-name.rs diff --git a/src/test/ui/doc-alias-same-name.stderr b/src/test/ui/rustdoc/doc-alias-same-name.stderr similarity index 100% rename from src/test/ui/doc-alias-same-name.stderr rename to src/test/ui/rustdoc/doc-alias-same-name.stderr diff --git a/src/test/ui/doc_keyword.rs b/src/test/ui/rustdoc/doc_keyword.rs similarity index 100% rename from src/test/ui/doc_keyword.rs rename to src/test/ui/rustdoc/doc_keyword.rs diff --git a/src/test/ui/doc_keyword.stderr b/src/test/ui/rustdoc/doc_keyword.stderr similarity index 87% rename from src/test/ui/doc_keyword.stderr rename to src/test/ui/rustdoc/doc_keyword.stderr index d72a876163eb3..0679bb8c5a7a6 100644 --- a/src/test/ui/doc_keyword.stderr +++ b/src/test/ui/rustdoc/doc_keyword.stderr @@ -10,7 +10,7 @@ error: `#[doc(keyword = "...")]` can only be used on modules LL | #[doc(keyword = "hall")] | ^^^^^^^^^^^^^^^^ -error: `#![doc(keyword = "...")]` isn't allowed as a crate level attribute +error: `#![doc(keyword = "...")]` isn't allowed as a crate-level attribute --> $DIR/doc_keyword.rs:4:8 | LL | #![doc(keyword = "hello")] diff --git a/src/test/ui/unterminated-doc-comment.rs b/src/test/ui/rustdoc/unterminated-doc-comment.rs similarity index 100% rename from src/test/ui/unterminated-doc-comment.rs rename to src/test/ui/rustdoc/unterminated-doc-comment.rs diff --git a/src/test/ui/unterminated-doc-comment.stderr b/src/test/ui/rustdoc/unterminated-doc-comment.stderr similarity index 100% rename from src/test/ui/unterminated-doc-comment.stderr rename to src/test/ui/rustdoc/unterminated-doc-comment.stderr diff --git a/src/test/ui/sanitize/hwaddress.rs b/src/test/ui/sanitize/hwaddress.rs index ad5d0245457ec..88769b7cb4554 100644 --- a/src/test/ui/sanitize/hwaddress.rs +++ b/src/test/ui/sanitize/hwaddress.rs @@ -1,7 +1,8 @@ // needs-sanitizer-support // needs-sanitizer-hwaddress // -// compile-flags: -Z sanitizer=hwaddress -O -g +// FIXME(#83989): codegen-units=1 triggers linker errors on aarch64-gnu +// compile-flags: -Z sanitizer=hwaddress -O -g -C codegen-units=16 // // run-fail // error-pattern: HWAddressSanitizer: tag-mismatch diff --git a/src/test/ui/sanitize/unsupported-target.rs b/src/test/ui/sanitize/unsupported-target.rs index 6ccc9988cdecc..3fb749815f7c3 100644 --- a/src/test/ui/sanitize/unsupported-target.rs +++ b/src/test/ui/sanitize/unsupported-target.rs @@ -1,6 +1,5 @@ // compile-flags: -Z sanitizer=leak --target i686-unknown-linux-gnu -// error-pattern: error: `-Zsanitizer=leak` only works with targets: - +// error-pattern: error: leak sanitizer is not supported for this target #![feature(no_core)] #![no_core] #![no_main] diff --git a/src/test/ui/sanitize/unsupported-target.stderr b/src/test/ui/sanitize/unsupported-target.stderr index 093678707fb99..9bb8405020d73 100644 --- a/src/test/ui/sanitize/unsupported-target.stderr +++ b/src/test/ui/sanitize/unsupported-target.stderr @@ -1,4 +1,4 @@ -error: `-Zsanitizer=leak` only works with targets: aarch64-apple-darwin, aarch64-unknown-linux-gnu, x86_64-apple-darwin, x86_64-unknown-linux-gnu +error: leak sanitizer is not supported for this target error: aborting due to previous error diff --git a/src/test/ui/save-analysis/issue-68621.full_tait.stderr b/src/test/ui/save-analysis/issue-68621.full_tait.stderr new file mode 100644 index 0000000000000..193aed2615ce6 --- /dev/null +++ b/src/test/ui/save-analysis/issue-68621.full_tait.stderr @@ -0,0 +1,17 @@ +warning: the feature `type_alias_impl_trait` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/issue-68621.rs:5:32 + | +LL | #![cfg_attr(full_tait, feature(type_alias_impl_trait))] + | ^^^^^^^^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #63063 <https://github.com/rust-lang/rust/issues/63063> for more information + +error: could not find defining uses + --> $DIR/issue-68621.rs:17:19 + | +LL | type Future = impl Trait; + | ^^^^^^^^^^ + +error: aborting due to previous error; 1 warning emitted + diff --git a/src/test/ui/save-analysis/issue-68621.min_tait.stderr b/src/test/ui/save-analysis/issue-68621.min_tait.stderr new file mode 100644 index 0000000000000..cccfb3de8fb1b --- /dev/null +++ b/src/test/ui/save-analysis/issue-68621.min_tait.stderr @@ -0,0 +1,8 @@ +error: could not find defining uses + --> $DIR/issue-68621.rs:17:19 + | +LL | type Future = impl Trait; + | ^^^^^^^^^^ + +error: aborting due to previous error + diff --git a/src/test/ui/save-analysis/issue-68621.rs b/src/test/ui/save-analysis/issue-68621.rs index 96af085c5b6b8..1f4a9080cc934 100644 --- a/src/test/ui/save-analysis/issue-68621.rs +++ b/src/test/ui/save-analysis/issue-68621.rs @@ -1,6 +1,9 @@ // compile-flags: -Zsave-analysis -#![feature(type_alias_impl_trait)] +// revisions: min_tait full_tait +#![feature(min_type_alias_impl_trait)] +#![cfg_attr(full_tait, feature(type_alias_impl_trait))] +//[full_tait]~^ WARN incomplete trait Trait {} diff --git a/src/test/ui/save-analysis/issue-68621.stderr b/src/test/ui/save-analysis/issue-68621.stderr deleted file mode 100644 index 3af6d0a3e076e..0000000000000 --- a/src/test/ui/save-analysis/issue-68621.stderr +++ /dev/null @@ -1,8 +0,0 @@ -error: could not find defining uses - --> $DIR/issue-68621.rs:14:19 - | -LL | type Future = impl Trait; - | ^^^^^^^^^^ - -error: aborting due to previous error - diff --git a/src/test/ui/self/point-at-arbitrary-self-type-method.stderr b/src/test/ui/self/point-at-arbitrary-self-type-method.stderr index 2954a499c18c9..b804ddfb024bf 100644 --- a/src/test/ui/self/point-at-arbitrary-self-type-method.stderr +++ b/src/test/ui/self/point-at-arbitrary-self-type-method.stderr @@ -9,6 +9,11 @@ LL | fn foo(self: Box<Self>) {} ... LL | A.foo(); | ^^^ method not found in `A` + | +help: consider wrapping the receiver expression with the appropriate type + | +LL | Box::new(A).foo(); + | ^^^^^^^^^ ^ error: aborting due to previous error diff --git a/src/test/ui/self/point-at-arbitrary-self-type-trait-method.stderr b/src/test/ui/self/point-at-arbitrary-self-type-trait-method.stderr index 89fe84c0d2d43..e1ed0e42f985c 100644 --- a/src/test/ui/self/point-at-arbitrary-self-type-trait-method.stderr +++ b/src/test/ui/self/point-at-arbitrary-self-type-trait-method.stderr @@ -10,6 +10,11 @@ LL | struct A; ... LL | A.foo() | ^^^ method not found in `A` + | +help: consider wrapping the receiver expression with the appropriate type + | +LL | Box::new(A).foo() + | ^^^^^^^^^ ^ error: aborting due to previous error diff --git a/src/test/ui/simd-intrinsic/simd-intrinsic-generic-arithmetic-saturating.rs b/src/test/ui/simd-intrinsic/simd-intrinsic-generic-arithmetic-saturating.rs index 4d23a1911a3cb..9736d1b964da3 100644 --- a/src/test/ui/simd-intrinsic/simd-intrinsic-generic-arithmetic-saturating.rs +++ b/src/test/ui/simd-intrinsic/simd-intrinsic-generic-arithmetic-saturating.rs @@ -1,6 +1,5 @@ // build-fail // ignore-emscripten -// ignore-tidy-linelength #![feature(repr_simd, platform_intrinsics)] #![allow(non_camel_case_types)] #[repr(simd)] diff --git a/src/test/ui/simd-intrinsic/simd-intrinsic-generic-arithmetic-saturating.stderr b/src/test/ui/simd-intrinsic/simd-intrinsic-generic-arithmetic-saturating.stderr index 1ed472a485d13..0e88540bcc8e5 100644 --- a/src/test/ui/simd-intrinsic/simd-intrinsic-generic-arithmetic-saturating.stderr +++ b/src/test/ui/simd-intrinsic/simd-intrinsic-generic-arithmetic-saturating.stderr @@ -1,11 +1,11 @@ error[E0511]: invalid monomorphization of `simd_saturating_add` intrinsic: expected element type `f32` of vector type `f32x4` to be a signed or unsigned integer type - --> $DIR/simd-intrinsic-generic-arithmetic-saturating.rs:34:9 + --> $DIR/simd-intrinsic-generic-arithmetic-saturating.rs:33:9 | LL | simd_saturating_add(z, z); | ^^^^^^^^^^^^^^^^^^^^^^^^^ error[E0511]: invalid monomorphization of `simd_saturating_sub` intrinsic: expected element type `f32` of vector type `f32x4` to be a signed or unsigned integer type - --> $DIR/simd-intrinsic-generic-arithmetic-saturating.rs:36:9 + --> $DIR/simd-intrinsic-generic-arithmetic-saturating.rs:35:9 | LL | simd_saturating_sub(z, z); | ^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/src/test/ui/simd-intrinsic/simd-intrinsic-generic-arithmetic.rs b/src/test/ui/simd-intrinsic/simd-intrinsic-generic-arithmetic.rs index f95f548fee8ca..3576eed71ab50 100644 --- a/src/test/ui/simd-intrinsic/simd-intrinsic-generic-arithmetic.rs +++ b/src/test/ui/simd-intrinsic/simd-intrinsic-generic-arithmetic.rs @@ -25,6 +25,8 @@ extern "platform-intrinsic" { fn simd_and<T>(x: T, y: T) -> T; fn simd_or<T>(x: T, y: T) -> T; fn simd_xor<T>(x: T, y: T) -> T; + + fn simd_neg<T>(x: T) -> T; } fn main() { @@ -60,6 +62,9 @@ fn main() { simd_xor(x, x); simd_xor(y, y); + simd_neg(x); + simd_neg(z); + simd_add(0, 0); //~^ ERROR expected SIMD input type, found non-SIMD `i32` @@ -80,6 +85,9 @@ fn main() { simd_xor(0, 0); //~^ ERROR expected SIMD input type, found non-SIMD `i32` + simd_neg(0); + //~^ ERROR expected SIMD input type, found non-SIMD `i32` + simd_shl(z, z); //~^ ERROR unsupported operation on `f32x4` with element `f32` diff --git a/src/test/ui/simd-intrinsic/simd-intrinsic-generic-arithmetic.stderr b/src/test/ui/simd-intrinsic/simd-intrinsic-generic-arithmetic.stderr index 70cdc34684da7..99c5196334324 100644 --- a/src/test/ui/simd-intrinsic/simd-intrinsic-generic-arithmetic.stderr +++ b/src/test/ui/simd-intrinsic/simd-intrinsic-generic-arithmetic.stderr @@ -1,87 +1,93 @@ error[E0511]: invalid monomorphization of `simd_add` intrinsic: expected SIMD input type, found non-SIMD `i32` - --> $DIR/simd-intrinsic-generic-arithmetic.rs:64:9 + --> $DIR/simd-intrinsic-generic-arithmetic.rs:69:9 | LL | simd_add(0, 0); | ^^^^^^^^^^^^^^ error[E0511]: invalid monomorphization of `simd_sub` intrinsic: expected SIMD input type, found non-SIMD `i32` - --> $DIR/simd-intrinsic-generic-arithmetic.rs:66:9 + --> $DIR/simd-intrinsic-generic-arithmetic.rs:71:9 | LL | simd_sub(0, 0); | ^^^^^^^^^^^^^^ error[E0511]: invalid monomorphization of `simd_mul` intrinsic: expected SIMD input type, found non-SIMD `i32` - --> $DIR/simd-intrinsic-generic-arithmetic.rs:68:9 + --> $DIR/simd-intrinsic-generic-arithmetic.rs:73:9 | LL | simd_mul(0, 0); | ^^^^^^^^^^^^^^ error[E0511]: invalid monomorphization of `simd_div` intrinsic: expected SIMD input type, found non-SIMD `i32` - --> $DIR/simd-intrinsic-generic-arithmetic.rs:70:9 + --> $DIR/simd-intrinsic-generic-arithmetic.rs:75:9 | LL | simd_div(0, 0); | ^^^^^^^^^^^^^^ error[E0511]: invalid monomorphization of `simd_shl` intrinsic: expected SIMD input type, found non-SIMD `i32` - --> $DIR/simd-intrinsic-generic-arithmetic.rs:72:9 + --> $DIR/simd-intrinsic-generic-arithmetic.rs:77:9 | LL | simd_shl(0, 0); | ^^^^^^^^^^^^^^ error[E0511]: invalid monomorphization of `simd_shr` intrinsic: expected SIMD input type, found non-SIMD `i32` - --> $DIR/simd-intrinsic-generic-arithmetic.rs:74:9 + --> $DIR/simd-intrinsic-generic-arithmetic.rs:79:9 | LL | simd_shr(0, 0); | ^^^^^^^^^^^^^^ error[E0511]: invalid monomorphization of `simd_and` intrinsic: expected SIMD input type, found non-SIMD `i32` - --> $DIR/simd-intrinsic-generic-arithmetic.rs:76:9 + --> $DIR/simd-intrinsic-generic-arithmetic.rs:81:9 | LL | simd_and(0, 0); | ^^^^^^^^^^^^^^ error[E0511]: invalid monomorphization of `simd_or` intrinsic: expected SIMD input type, found non-SIMD `i32` - --> $DIR/simd-intrinsic-generic-arithmetic.rs:78:9 + --> $DIR/simd-intrinsic-generic-arithmetic.rs:83:9 | LL | simd_or(0, 0); | ^^^^^^^^^^^^^ error[E0511]: invalid monomorphization of `simd_xor` intrinsic: expected SIMD input type, found non-SIMD `i32` - --> $DIR/simd-intrinsic-generic-arithmetic.rs:80:9 + --> $DIR/simd-intrinsic-generic-arithmetic.rs:85:9 | LL | simd_xor(0, 0); | ^^^^^^^^^^^^^^ +error[E0511]: invalid monomorphization of `simd_neg` intrinsic: expected SIMD input type, found non-SIMD `i32` + --> $DIR/simd-intrinsic-generic-arithmetic.rs:88:9 + | +LL | simd_neg(0); + | ^^^^^^^^^^^ + error[E0511]: invalid monomorphization of `simd_shl` intrinsic: unsupported operation on `f32x4` with element `f32` - --> $DIR/simd-intrinsic-generic-arithmetic.rs:84:9 + --> $DIR/simd-intrinsic-generic-arithmetic.rs:92:9 | LL | simd_shl(z, z); | ^^^^^^^^^^^^^^ error[E0511]: invalid monomorphization of `simd_shr` intrinsic: unsupported operation on `f32x4` with element `f32` - --> $DIR/simd-intrinsic-generic-arithmetic.rs:86:9 + --> $DIR/simd-intrinsic-generic-arithmetic.rs:94:9 | LL | simd_shr(z, z); | ^^^^^^^^^^^^^^ error[E0511]: invalid monomorphization of `simd_and` intrinsic: unsupported operation on `f32x4` with element `f32` - --> $DIR/simd-intrinsic-generic-arithmetic.rs:88:9 + --> $DIR/simd-intrinsic-generic-arithmetic.rs:96:9 | LL | simd_and(z, z); | ^^^^^^^^^^^^^^ error[E0511]: invalid monomorphization of `simd_or` intrinsic: unsupported operation on `f32x4` with element `f32` - --> $DIR/simd-intrinsic-generic-arithmetic.rs:90:9 + --> $DIR/simd-intrinsic-generic-arithmetic.rs:98:9 | LL | simd_or(z, z); | ^^^^^^^^^^^^^ error[E0511]: invalid monomorphization of `simd_xor` intrinsic: unsupported operation on `f32x4` with element `f32` - --> $DIR/simd-intrinsic-generic-arithmetic.rs:92:9 + --> $DIR/simd-intrinsic-generic-arithmetic.rs:100:9 | LL | simd_xor(z, z); | ^^^^^^^^^^^^^^ -error: aborting due to 14 previous errors +error: aborting due to 15 previous errors For more information about this error, try `rustc --explain E0511`. diff --git a/src/test/ui/simd-intrinsic/simd-intrinsic-inlining-issue67557-ice.rs b/src/test/ui/simd-intrinsic/simd-intrinsic-inlining-issue67557-ice.rs index 4c09ae25c5f3e..4be6818305010 100644 --- a/src/test/ui/simd-intrinsic/simd-intrinsic-inlining-issue67557-ice.rs +++ b/src/test/ui/simd-intrinsic/simd-intrinsic-inlining-issue67557-ice.rs @@ -2,7 +2,7 @@ // passed the wrong Instance, causing issues with inlining. See #67557. // // run-pass -// compile-flags: -Zmir-opt-level=3 +// compile-flags: -Zmir-opt-level=4 #![feature(platform_intrinsics, repr_simd)] extern "platform-intrinsic" { diff --git a/src/test/ui/simd-intrinsic/simd-intrinsic-inlining-issue67557.rs b/src/test/ui/simd-intrinsic/simd-intrinsic-inlining-issue67557.rs index 7a0d955686bb6..67556829815b3 100644 --- a/src/test/ui/simd-intrinsic/simd-intrinsic-inlining-issue67557.rs +++ b/src/test/ui/simd-intrinsic/simd-intrinsic-inlining-issue67557.rs @@ -2,7 +2,7 @@ // the wrong Instance, causing issues with inlining. See #67557. // // run-pass -// compile-flags: -Zmir-opt-level=3 +// compile-flags: -Zmir-opt-level=4 #![feature(platform_intrinsics, repr_simd)] extern "platform-intrinsic" { diff --git a/src/test/ui/simd/simd-intrinsic-float-math.rs b/src/test/ui/simd/simd-intrinsic-float-math.rs index 220a59535cae4..7a4f7466559ff 100644 --- a/src/test/ui/simd/simd-intrinsic-float-math.rs +++ b/src/test/ui/simd/simd-intrinsic-float-math.rs @@ -20,16 +20,20 @@ extern "platform-intrinsic" { fn simd_fabs<T>(x: T) -> T; fn simd_fsin<T>(x: T) -> T; fn simd_fcos<T>(x: T) -> T; - fn simd_ceil<T>(x: T) -> T; fn simd_fexp<T>(x: T) -> T; fn simd_fexp2<T>(x: T) -> T; - fn simd_floor<T>(x: T) -> T; fn simd_fma<T>(x: T, y: T, z: T) -> T; fn simd_flog<T>(x: T) -> T; fn simd_flog10<T>(x: T) -> T; fn simd_flog2<T>(x: T) -> T; fn simd_fpow<T>(x: T, y: T) -> T; fn simd_fpowi<T>(x: T, y: i32) -> T; + + // rounding functions + fn simd_ceil<T>(x: T) -> T; + fn simd_floor<T>(x: T) -> T; + fn simd_round<T>(x: T) -> T; + fn simd_trunc<T>(x: T) -> T; } macro_rules! assert_approx_eq_f32 { @@ -64,18 +68,12 @@ fn main() { let r = simd_fcos(z); assert_approx_eq!(x, r); - let r = simd_ceil(h); - assert_approx_eq!(x, r); - let r = simd_fexp(z); assert_approx_eq!(x, r); let r = simd_fexp2(z); assert_approx_eq!(x, r); - let r = simd_floor(h); - assert_approx_eq!(z, r); - let r = simd_fma(x, h, h); assert_approx_eq!(x, r); @@ -99,5 +97,18 @@ fn main() { let r = simd_fsin(z); assert_approx_eq!(z, r); + + // rounding functions + let r = simd_floor(h); + assert_eq!(z, r); + + let r = simd_ceil(h); + assert_eq!(x, r); + + let r = simd_round(h); + assert_eq!(x, r); + + let r = simd_trunc(h); + assert_eq!(z, r); } } diff --git a/src/test/ui/simd/simd-intrinsic-generic-arithmetic.rs b/src/test/ui/simd/simd-intrinsic-generic-arithmetic.rs index 96c2c6c539927..c507b8d31ecb4 100644 --- a/src/test/ui/simd/simd-intrinsic-generic-arithmetic.rs +++ b/src/test/ui/simd/simd-intrinsic-generic-arithmetic.rs @@ -45,6 +45,8 @@ extern "platform-intrinsic" { fn simd_and<T>(x: T, y: T) -> T; fn simd_or<T>(x: T, y: T) -> T; fn simd_xor<T>(x: T, y: T) -> T; + + fn simd_neg<T>(x: T) -> T; } fn main() { @@ -125,5 +127,10 @@ fn main() { all_eq_!(simd_xor(y1, y2), U32::<4>([3, 1, 7, 1])); all_eq_!(simd_xor(y2, y1), U32::<4>([3, 1, 7, 1])); + all_eq!(simd_neg(x1), i32x4(-1, -2, -3, -4)); + all_eq!(simd_neg(x2), i32x4(-2, -3, -4, -5)); + all_eq!(simd_neg(z1), f32x4(-1.0, -2.0, -3.0, -4.0)); + all_eq!(simd_neg(z2), f32x4(-2.0, -3.0, -4.0, -5.0)); + } } diff --git a/src/test/ui/simd/simd-intrinsic-generic-reduction.rs b/src/test/ui/simd/simd-intrinsic-generic-reduction.rs index 8b5afeac0bc2d..7193fb3527fa2 100644 --- a/src/test/ui/simd/simd-intrinsic-generic-reduction.rs +++ b/src/test/ui/simd/simd-intrinsic-generic-reduction.rs @@ -2,7 +2,6 @@ #![allow(non_camel_case_types)] // ignore-emscripten -// min-system-llvm-version: 9.0 // Test that the simd_reduce_{op} intrinsics produce the correct results. diff --git a/src/test/ui/simd/simd-type-generic-monomorphisation.rs b/src/test/ui/simd/simd-type-generic-monomorphisation.rs index 0275f0ce4c159..12f9d65d77af0 100644 --- a/src/test/ui/simd/simd-type-generic-monomorphisation.rs +++ b/src/test/ui/simd/simd-type-generic-monomorphisation.rs @@ -2,7 +2,6 @@ #![feature(repr_simd, platform_intrinsics)] -// ignore-tidy-linelength // error-pattern:monomorphising SIMD type `Simd2<X>` with a non-primitive-scalar (integer/float/pointer) element type `X` diff --git a/src/test/ui/simd/simd-type.rs b/src/test/ui/simd/simd-type.rs index 73d032a0c8e55..d82c70b8d8268 100644 --- a/src/test/ui/simd/simd-type.rs +++ b/src/test/ui/simd/simd-type.rs @@ -1,7 +1,6 @@ #![feature(repr_simd)] #![allow(non_camel_case_types)] -// ignore-tidy-linelength #[repr(simd)] struct empty; //~ ERROR SIMD vector cannot be empty diff --git a/src/test/ui/simd/simd-type.stderr b/src/test/ui/simd/simd-type.stderr index 823f10f5daf20..4e4a19ea32ad0 100644 --- a/src/test/ui/simd/simd-type.stderr +++ b/src/test/ui/simd/simd-type.stderr @@ -1,35 +1,35 @@ error[E0075]: SIMD vector cannot be empty - --> $DIR/simd-type.rs:7:1 + --> $DIR/simd-type.rs:6:1 | LL | struct empty; | ^^^^^^^^^^^^^ error[E0075]: SIMD vector cannot be empty - --> $DIR/simd-type.rs:10:1 + --> $DIR/simd-type.rs:9:1 | LL | struct empty2([f32; 0]); | ^^^^^^^^^^^^^^^^^^^^^^^^ error[E0076]: SIMD vector should be homogeneous - --> $DIR/simd-type.rs:16:1 + --> $DIR/simd-type.rs:15:1 | LL | struct i64f64(i64, f64); | ^^^^^^^^^^^^^^^^^^^^^^^^ SIMD elements must have the same type error[E0077]: SIMD vector element type should be a primitive scalar (integer/float/pointer) type - --> $DIR/simd-type.rs:21:1 + --> $DIR/simd-type.rs:20:1 | LL | struct FooV(Foo, Foo); | ^^^^^^^^^^^^^^^^^^^^^^ error[E0077]: SIMD vector element type should be a primitive scalar (integer/float/pointer) type - --> $DIR/simd-type.rs:24:1 + --> $DIR/simd-type.rs:23:1 | LL | struct FooV2([Foo; 2]); | ^^^^^^^^^^^^^^^^^^^^^^^ error[E0075]: SIMD vector cannot have more than 32768 elements - --> $DIR/simd-type.rs:27:1 + --> $DIR/simd-type.rs:26:1 | LL | struct TooBig([f32; 65536]); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/src/test/ui/single-primitive-inherent-impl.rs b/src/test/ui/single-primitive-inherent-impl.rs index baa23396c1624..75c62feec32d0 100644 --- a/src/test/ui/single-primitive-inherent-impl.rs +++ b/src/test/ui/single-primitive-inherent-impl.rs @@ -1,5 +1,3 @@ -// ignore-tidy-linelength - #![crate_type = "lib"] #![feature(lang_items)] #![no_std] diff --git a/src/test/ui/single-primitive-inherent-impl.stderr b/src/test/ui/single-primitive-inherent-impl.stderr index 50a0d5bef86d3..349a12eac05a0 100644 --- a/src/test/ui/single-primitive-inherent-impl.stderr +++ b/src/test/ui/single-primitive-inherent-impl.stderr @@ -1,5 +1,5 @@ error[E0390]: only a single inherent implementation marked with `#[lang = "str"]` is allowed for the `str` primitive - --> $DIR/single-primitive-inherent-impl.rs:11:1 + --> $DIR/single-primitive-inherent-impl.rs:9:1 | LL | / impl str { LL | | diff --git a/src/test/ui/single-use-lifetime/one-use-in-fn-argument-in-band.stderr b/src/test/ui/single-use-lifetime/one-use-in-fn-argument-in-band.stderr index b251e8a438ac1..723c4a7a1fbf0 100644 --- a/src/test/ui/single-use-lifetime/one-use-in-fn-argument-in-band.stderr +++ b/src/test/ui/single-use-lifetime/one-use-in-fn-argument-in-band.stderr @@ -1,11 +1,11 @@ -error: lifetime parameter `'a` only used once - --> $DIR/one-use-in-fn-argument-in-band.rs:11:10 +error: lifetime parameter `'b` only used once + --> $DIR/one-use-in-fn-argument-in-band.rs:11:22 | LL | fn a(x: &'a u32, y: &'b u32) { - | ^^- - | | - | this lifetime is only used here - | help: elide the single-use lifetime + | ^^- + | | + | this lifetime is only used here + | help: elide the single-use lifetime | note: the lint level is defined here --> $DIR/one-use-in-fn-argument-in-band.rs:4:9 @@ -13,14 +13,14 @@ note: the lint level is defined here LL | #![deny(single_use_lifetimes)] | ^^^^^^^^^^^^^^^^^^^^ -error: lifetime parameter `'b` only used once - --> $DIR/one-use-in-fn-argument-in-band.rs:11:22 +error: lifetime parameter `'a` only used once + --> $DIR/one-use-in-fn-argument-in-band.rs:11:10 | LL | fn a(x: &'a u32, y: &'b u32) { - | ^^- - | | - | this lifetime is only used here - | help: elide the single-use lifetime + | ^^- + | | + | this lifetime is only used here + | help: elide the single-use lifetime error: aborting due to 2 previous errors diff --git a/src/test/ui/specialization/deafult-generic-associated-type-bound.stderr b/src/test/ui/specialization/deafult-generic-associated-type-bound.stderr index b5a1877ea1931..eb5d80bc4dda6 100644 --- a/src/test/ui/specialization/deafult-generic-associated-type-bound.stderr +++ b/src/test/ui/specialization/deafult-generic-associated-type-bound.stderr @@ -28,8 +28,8 @@ LL | default type U<'a> = &'a T; = note: required because of the requirements on the impl of `PartialEq` for `&'a T` help: consider further restricting this bound | -LL | impl<T: 'static + PartialEq> X for T { - | ^^^^^^^^^^^ +LL | impl<T: 'static + std::cmp::PartialEq> X for T { + | ^^^^^^^^^^^^^^^^^^^^^ error: aborting due to previous error; 2 warnings emitted diff --git a/src/test/ui/specialization/defaultimpl/specialization-wfcheck.stderr b/src/test/ui/specialization/defaultimpl/specialization-wfcheck.stderr index 57b90c457cb77..e416f30cb415d 100644 --- a/src/test/ui/specialization/defaultimpl/specialization-wfcheck.stderr +++ b/src/test/ui/specialization/defaultimpl/specialization-wfcheck.stderr @@ -19,8 +19,8 @@ LL | default impl<U> Foo<'static, U> for () {} | help: consider restricting type parameter `U` | -LL | default impl<U: Eq> Foo<'static, U> for () {} - | ^^^^ +LL | default impl<U: std::cmp::Eq> Foo<'static, U> for () {} + | ^^^^^^^^^^^^^^ error: aborting due to previous error; 1 warning emitted diff --git a/src/test/ui/specialization/issue-33017.rs b/src/test/ui/specialization/issue-33017.rs new file mode 100644 index 0000000000000..4d19230df6bad --- /dev/null +++ b/src/test/ui/specialization/issue-33017.rs @@ -0,0 +1,45 @@ +// Test to ensure that trait bounds are propertly +// checked on specializable associated types + +#![allow(incomplete_features)] +#![feature(specialization)] + +trait UncheckedCopy: Sized { + type Output: From<Self> + Copy + Into<Self>; +} + +impl<T> UncheckedCopy for T { + default type Output = Self; + //~^ ERROR: the trait bound `T: Copy` is not satisfied +} + +fn unchecked_copy<T: UncheckedCopy>(other: &T::Output) -> T { + (*other).into() +} + +fn bug(origin: String) { + // Turn the String into it's Output type... + // Which we can just do by `.into()`, the assoc type states `From<Self>`. + let origin_output = origin.into(); + + // Make a copy of String::Output, which is a String... + let mut copy: String = unchecked_copy::<String>(&origin_output); + + // Turn the Output type into a String again, + // Which we can just do by `.into()`, the assoc type states `Into<Self>`. + let mut origin: String = origin_output.into(); + + // assert both Strings use the same buffer. + assert_eq!(copy.as_ptr(), origin.as_ptr()); + + // Any use of the copy we made becomes invalid, + drop(origin); + + // OH NO! UB UB UB UB! + copy.push_str(" world!"); + println!("{}", copy); +} + +fn main() { + bug(String::from("hello")); +} diff --git a/src/test/ui/specialization/issue-33017.stderr b/src/test/ui/specialization/issue-33017.stderr new file mode 100644 index 0000000000000..bff4618d0be4c --- /dev/null +++ b/src/test/ui/specialization/issue-33017.stderr @@ -0,0 +1,17 @@ +error[E0277]: the trait bound `T: Copy` is not satisfied + --> $DIR/issue-33017.rs:12:5 + | +LL | type Output: From<Self> + Copy + Into<Self>; + | ---- required by this bound in `UncheckedCopy::Output` +... +LL | default type Output = Self; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Copy` is not implemented for `T` + | +help: consider restricting type parameter `T` + | +LL | impl<T: std::marker::Copy> UncheckedCopy for T { + | ^^^^^^^^^^^^^^^^^^^ + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0277`. diff --git a/src/test/ui/specialization/issue-38091-2.stderr b/src/test/ui/specialization/issue-38091-2.stderr index bd5ed498d92ca..a93f27ff051fc 100644 --- a/src/test/ui/specialization/issue-38091-2.stderr +++ b/src/test/ui/specialization/issue-38091-2.stderr @@ -10,7 +10,11 @@ LL | #![feature(specialization)] error[E0275]: overflow evaluating the requirement `i32: Check` | - = note: required because of the requirements on the impl of `Iterate` for `i32` +note: required because of the requirements on the impl of `Iterate` for `i32` + --> $DIR/issue-38091-2.rs:11:13 + | +LL | impl<'a, T> Iterate<'a> for T + | ^^^^^^^^^^^ ^ error: aborting due to previous error; 1 warning emitted diff --git a/src/test/ui/specialization/issue-39448.rs b/src/test/ui/specialization/issue-39448.rs index 9dd47a4a17e43..a15c4bd6b7ffc 100644 --- a/src/test/ui/specialization/issue-39448.rs +++ b/src/test/ui/specialization/issue-39448.rs @@ -18,7 +18,7 @@ impl FromA<u8> for u16 { } trait FromA<T> { - fn from(T) -> Self; + fn from(t: T) -> Self; } impl<T: A, U: A + FromA<T>> FromA<T> for U { diff --git a/src/test/ui/specialization/issue-39448.stderr b/src/test/ui/specialization/issue-39448.stderr index 98e49b1bab3b7..c4fc44c737ec1 100644 --- a/src/test/ui/specialization/issue-39448.stderr +++ b/src/test/ui/specialization/issue-39448.stderr @@ -14,8 +14,16 @@ error[E0275]: overflow evaluating the requirement `T: FromA<U>` LL | x.foo(y.to()).to() | ^^ | - = note: required because of the requirements on the impl of `FromA<U>` for `T` - = note: required because of the requirements on the impl of `ToA<T>` for `U` +note: required because of the requirements on the impl of `FromA<U>` for `T` + --> $DIR/issue-39448.rs:24:29 + | +LL | impl<T: A, U: A + FromA<T>> FromA<T> for U { + | ^^^^^^^^ ^ +note: required because of the requirements on the impl of `ToA<T>` for `U` + --> $DIR/issue-39448.rs:34:12 + | +LL | impl<T, U> ToA<U> for T + | ^^^^^^ ^ error: aborting due to previous error; 1 warning emitted diff --git a/src/test/ui/specialization/issue-51892.rs b/src/test/ui/specialization/issue-51892.rs new file mode 100644 index 0000000000000..3cd0711ae42c6 --- /dev/null +++ b/src/test/ui/specialization/issue-51892.rs @@ -0,0 +1,19 @@ +#![allow(incomplete_features)] +#![feature(const_generics)] +#![feature(const_evaluatable_checked)] +#![feature(specialization)] + +pub trait Trait { + type Type; +} + +impl<T: ?Sized> Trait for T { + default type Type = [u8; 1]; +} + +impl<T: Trait> Trait for *const T { + type Type = [u8; std::mem::size_of::<<T as Trait>::Type>()]; + //~^ ERROR: unconstrained generic constant +} + +fn main() {} diff --git a/src/test/ui/specialization/issue-51892.stderr b/src/test/ui/specialization/issue-51892.stderr new file mode 100644 index 0000000000000..2d30164380a8e --- /dev/null +++ b/src/test/ui/specialization/issue-51892.stderr @@ -0,0 +1,10 @@ +error: unconstrained generic constant + --> $DIR/issue-51892.rs:15:5 + | +LL | type Type = [u8; std::mem::size_of::<<T as Trait>::Type>()]; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: try adding a `where` bound using this expression: `where [(); std::mem::size_of::<<T as Trait>::Type>()]:` + +error: aborting due to previous error + diff --git a/src/test/ui/specialization/issue-52050.stderr b/src/test/ui/specialization/issue-52050.stderr index 27070f8e4a1ca..ab3cf27d0d059 100644 --- a/src/test/ui/specialization/issue-52050.stderr +++ b/src/test/ui/specialization/issue-52050.stderr @@ -8,7 +8,7 @@ LL | #![feature(specialization)] = note: see issue #31844 <https://github.com/rust-lang/rust/issues/31844> for more information = help: consider using `min_specialization` instead, which is more stable and complete -error[E0119]: conflicting implementations of trait `IntoPyDictPointer` for type `()`: +error[E0119]: conflicting implementations of trait `IntoPyDictPointer` for type `()` --> $DIR/issue-52050.rs:28:1 | LL | / impl<I> IntoPyDictPointer for I diff --git a/src/test/ui/specialization/issue-68830-spurious-diagnostics.rs b/src/test/ui/specialization/issue-68830-spurious-diagnostics.rs new file mode 100644 index 0000000000000..d11ec79833217 --- /dev/null +++ b/src/test/ui/specialization/issue-68830-spurious-diagnostics.rs @@ -0,0 +1,23 @@ +// A regression test for #68830. This checks we don't emit +// a verbose `conflicting implementations` error. + +#![feature(specialization)] +#![allow(incomplete_features)] + +struct BadStruct { + err: MissingType //~ ERROR: cannot find type `MissingType` in this scope +} + +trait MyTrait<T> { + fn foo(); +} + +impl<T, D> MyTrait<T> for D { + default fn foo() {} +} + +impl<T> MyTrait<T> for BadStruct { + fn foo() {} +} + +fn main() {} diff --git a/src/test/ui/specialization/issue-68830-spurious-diagnostics.stderr b/src/test/ui/specialization/issue-68830-spurious-diagnostics.stderr new file mode 100644 index 0000000000000..833f61dca8cac --- /dev/null +++ b/src/test/ui/specialization/issue-68830-spurious-diagnostics.stderr @@ -0,0 +1,9 @@ +error[E0412]: cannot find type `MissingType` in this scope + --> $DIR/issue-68830-spurious-diagnostics.rs:8:10 + | +LL | err: MissingType + | ^^^^^^^^^^^ not found in this scope + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0412`. diff --git a/src/test/ui/specialization/min_specialization/repeated_projection_type.stderr b/src/test/ui/specialization/min_specialization/repeated_projection_type.stderr index 92208231b1735..fa59d7a031397 100644 --- a/src/test/ui/specialization/min_specialization/repeated_projection_type.stderr +++ b/src/test/ui/specialization/min_specialization/repeated_projection_type.stderr @@ -1,4 +1,4 @@ -error: cannot specialize on `Binder(ProjectionPredicate(ProjectionTy { substs: [V], item_def_id: DefId(0:6 ~ repeated_projection_type[317d]::Id::This) }, (I,)))` +error: cannot specialize on `Binder(ProjectionPredicate(ProjectionTy { substs: [V], item_def_id: DefId(0:6 ~ repeated_projection_type[317d]::Id::This) }, (I,)), [])` --> $DIR/repeated_projection_type.rs:19:1 | LL | / impl<I, V: Id<This = (I,)>> X for V { diff --git a/src/test/ui/specialization/specialization-feature-gate-overlap.stderr b/src/test/ui/specialization/specialization-feature-gate-overlap.stderr index baaf7aa43321a..9157ad0d46a1a 100644 --- a/src/test/ui/specialization/specialization-feature-gate-overlap.stderr +++ b/src/test/ui/specialization/specialization-feature-gate-overlap.stderr @@ -1,4 +1,4 @@ -error[E0119]: conflicting implementations of trait `Foo` for type `u8`: +error[E0119]: conflicting implementations of trait `Foo` for type `u8` --> $DIR/specialization-feature-gate-overlap.rs:13:1 | LL | impl<T> Foo for T { diff --git a/src/test/ui/specialization/specialization-overlap.stderr b/src/test/ui/specialization/specialization-overlap.stderr index 7e5c96ac1c884..3ccbe1616e803 100644 --- a/src/test/ui/specialization/specialization-overlap.stderr +++ b/src/test/ui/specialization/specialization-overlap.stderr @@ -8,7 +8,7 @@ LL | #![feature(specialization)] = note: see issue #31844 <https://github.com/rust-lang/rust/issues/31844> for more information = help: consider using `min_specialization` instead, which is more stable and complete -error[E0119]: conflicting implementations of trait `Foo` for type `std::vec::Vec<_>`: +error[E0119]: conflicting implementations of trait `Foo` for type `std::vec::Vec<_>` --> $DIR/specialization-overlap.rs:5:1 | LL | impl<T: Clone> Foo for T {} @@ -16,7 +16,7 @@ LL | impl<T: Clone> Foo for T {} LL | impl<T> Foo for Vec<T> {} | ^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `std::vec::Vec<_>` -error[E0119]: conflicting implementations of trait `Bar` for type `(u8, u8)`: +error[E0119]: conflicting implementations of trait `Bar` for type `(u8, u8)` --> $DIR/specialization-overlap.rs:9:1 | LL | impl<T> Bar for (T, u8) {} @@ -24,7 +24,7 @@ LL | impl<T> Bar for (T, u8) {} LL | impl<T> Bar for (u8, T) {} | ^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `(u8, u8)` -error[E0119]: conflicting implementations of trait `Baz<u8>` for type `u8`: +error[E0119]: conflicting implementations of trait `Baz<u8>` for type `u8` --> $DIR/specialization-overlap.rs:13:1 | LL | impl<T> Baz<T> for u8 {} @@ -32,7 +32,7 @@ LL | impl<T> Baz<T> for u8 {} LL | impl<T> Baz<u8> for T {} | ^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `u8` -error[E0119]: conflicting implementations of trait `Qux`: +error[E0119]: conflicting implementations of trait `Qux` --> $DIR/specialization-overlap.rs:17:1 | LL | impl<T: Clone> Qux for T {} diff --git a/src/test/ui/stability-attribute/generics-default-stability-where.rs b/src/test/ui/stability-attribute/generics-default-stability-where.rs index 3fd14e25d0ef2..4afbca2626491 100644 --- a/src/test/ui/stability-attribute/generics-default-stability-where.rs +++ b/src/test/ui/stability-attribute/generics-default-stability-where.rs @@ -1,4 +1,3 @@ -// ignore-tidy-linelength // aux-build:unstable_generic_param.rs extern crate unstable_generic_param; diff --git a/src/test/ui/stability-attribute/generics-default-stability-where.stderr b/src/test/ui/stability-attribute/generics-default-stability-where.stderr index 19fa09f311ba8..61253adc892b9 100644 --- a/src/test/ui/stability-attribute/generics-default-stability-where.stderr +++ b/src/test/ui/stability-attribute/generics-default-stability-where.stderr @@ -1,5 +1,5 @@ error[E0658]: use of unstable library feature 'unstable_default' - --> $DIR/generics-default-stability-where.rs:8:45 + --> $DIR/generics-default-stability-where.rs:7:45 | LL | impl<T> Trait3<usize> for T where T: Trait2<usize> { | ^^^^^ diff --git a/src/test/ui/stability-attribute/generics-default-stability.rs b/src/test/ui/stability-attribute/generics-default-stability.rs index d6f28e3e447e2..67f2334efc88e 100644 --- a/src/test/ui/stability-attribute/generics-default-stability.rs +++ b/src/test/ui/stability-attribute/generics-default-stability.rs @@ -1,4 +1,3 @@ -// ignore-tidy-linelength // aux-build:unstable_generic_param.rs #![feature(unstable_default6)] diff --git a/src/test/ui/stability-attribute/generics-default-stability.stderr b/src/test/ui/stability-attribute/generics-default-stability.stderr index a5df70bb8b3dd..99523f8eb645d 100644 --- a/src/test/ui/stability-attribute/generics-default-stability.stderr +++ b/src/test/ui/stability-attribute/generics-default-stability.stderr @@ -1,5 +1,5 @@ error[E0658]: use of unstable library feature 'unstable_default' - --> $DIR/generics-default-stability.rs:17:13 + --> $DIR/generics-default-stability.rs:16:13 | LL | impl Trait1<usize> for S { | ^^^^^ @@ -7,7 +7,7 @@ LL | impl Trait1<usize> for S { = help: add `#![feature(unstable_default)]` to the crate attributes to enable error[E0658]: use of unstable library feature 'unstable_default' - --> $DIR/generics-default-stability.rs:21:13 + --> $DIR/generics-default-stability.rs:20:13 | LL | impl Trait1<isize> for S { | ^^^^^ @@ -15,7 +15,7 @@ LL | impl Trait1<isize> for S { = help: add `#![feature(unstable_default)]` to the crate attributes to enable error[E0658]: use of unstable library feature 'unstable_default' - --> $DIR/generics-default-stability.rs:25:13 + --> $DIR/generics-default-stability.rs:24:13 | LL | impl Trait2<usize> for S { | ^^^^^ @@ -23,7 +23,7 @@ LL | impl Trait2<usize> for S { = help: add `#![feature(unstable_default)]` to the crate attributes to enable warning: use of deprecated struct `unstable_generic_param::Struct4`: test - --> $DIR/generics-default-stability.rs:84:29 + --> $DIR/generics-default-stability.rs:83:29 | LL | let _: Struct4<isize> = Struct4 { field: 1 }; | ^^^^^^^ @@ -31,217 +31,217 @@ LL | let _: Struct4<isize> = Struct4 { field: 1 }; = note: `#[warn(deprecated)]` on by default warning: use of deprecated struct `unstable_generic_param::Struct4`: test - --> $DIR/generics-default-stability.rs:84:12 + --> $DIR/generics-default-stability.rs:83:12 | LL | let _: Struct4<isize> = Struct4 { field: 1 }; | ^^^^^^^^^^^^^^ warning: use of deprecated struct `unstable_generic_param::Struct4`: test - --> $DIR/generics-default-stability.rs:89:12 + --> $DIR/generics-default-stability.rs:88:12 | LL | let _: Struct4 = STRUCT4; | ^^^^^^^ warning: use of deprecated struct `unstable_generic_param::Struct4`: test - --> $DIR/generics-default-stability.rs:90:12 + --> $DIR/generics-default-stability.rs:89:12 | LL | let _: Struct4<usize> = STRUCT4; | ^^^^^^^^^^^^^^ warning: use of deprecated struct `unstable_generic_param::Struct4`: test - --> $DIR/generics-default-stability.rs:91:29 + --> $DIR/generics-default-stability.rs:90:29 | LL | let _: Struct4<isize> = Struct4 { field: 0 }; | ^^^^^^^ warning: use of deprecated struct `unstable_generic_param::Struct4`: test - --> $DIR/generics-default-stability.rs:91:12 + --> $DIR/generics-default-stability.rs:90:12 | LL | let _: Struct4<isize> = Struct4 { field: 0 }; | ^^^^^^^^^^^^^^ warning: use of deprecated struct `unstable_generic_param::Struct5`: test - --> $DIR/generics-default-stability.rs:97:29 + --> $DIR/generics-default-stability.rs:96:29 | LL | let _: Struct5<isize> = Struct5 { field: 1 }; | ^^^^^^^ warning: use of deprecated struct `unstable_generic_param::Struct5`: test - --> $DIR/generics-default-stability.rs:97:12 + --> $DIR/generics-default-stability.rs:96:12 | LL | let _: Struct5<isize> = Struct5 { field: 1 }; | ^^^^^^^^^^^^^^ warning: use of deprecated struct `unstable_generic_param::Struct5`: test - --> $DIR/generics-default-stability.rs:102:12 + --> $DIR/generics-default-stability.rs:101:12 | LL | let _: Struct5 = STRUCT5; | ^^^^^^^ warning: use of deprecated struct `unstable_generic_param::Struct5`: test - --> $DIR/generics-default-stability.rs:103:12 + --> $DIR/generics-default-stability.rs:102:12 | LL | let _: Struct5<usize> = STRUCT5; | ^^^^^^^^^^^^^^ warning: use of deprecated struct `unstable_generic_param::Struct5`: test - --> $DIR/generics-default-stability.rs:105:29 + --> $DIR/generics-default-stability.rs:104:29 | LL | let _: Struct5<isize> = Struct5 { field: 0 }; | ^^^^^^^ warning: use of deprecated struct `unstable_generic_param::Struct5`: test - --> $DIR/generics-default-stability.rs:105:12 + --> $DIR/generics-default-stability.rs:104:12 | LL | let _: Struct5<isize> = Struct5 { field: 0 }; | ^^^^^^^^^^^^^^ warning: use of deprecated type alias `unstable_generic_param::Alias4`: test - --> $DIR/generics-default-stability.rs:160:28 + --> $DIR/generics-default-stability.rs:159:28 | LL | let _: Alias4<isize> = Alias4::Some(1); - | ^^^^^^^^^^^^ + | ^^^^^^ warning: use of deprecated type alias `unstable_generic_param::Alias4`: test - --> $DIR/generics-default-stability.rs:160:12 + --> $DIR/generics-default-stability.rs:159:12 | LL | let _: Alias4<isize> = Alias4::Some(1); | ^^^^^^^^^^^^^ warning: use of deprecated type alias `unstable_generic_param::Alias4`: test - --> $DIR/generics-default-stability.rs:164:12 + --> $DIR/generics-default-stability.rs:163:12 | LL | let _: Alias4 = ALIAS4; | ^^^^^^ warning: use of deprecated type alias `unstable_generic_param::Alias4`: test - --> $DIR/generics-default-stability.rs:165:12 + --> $DIR/generics-default-stability.rs:164:12 | LL | let _: Alias4<usize> = ALIAS4; | ^^^^^^^^^^^^^ warning: use of deprecated type alias `unstable_generic_param::Alias4`: test - --> $DIR/generics-default-stability.rs:166:28 + --> $DIR/generics-default-stability.rs:165:28 | LL | let _: Alias4<isize> = Alias4::Some(0); - | ^^^^^^^^^^^^ + | ^^^^^^ warning: use of deprecated type alias `unstable_generic_param::Alias4`: test - --> $DIR/generics-default-stability.rs:166:12 + --> $DIR/generics-default-stability.rs:165:12 | LL | let _: Alias4<isize> = Alias4::Some(0); | ^^^^^^^^^^^^^ warning: use of deprecated type alias `unstable_generic_param::Alias5`: test - --> $DIR/generics-default-stability.rs:171:28 + --> $DIR/generics-default-stability.rs:170:28 | LL | let _: Alias5<isize> = Alias5::Some(1); - | ^^^^^^^^^^^^ + | ^^^^^^ warning: use of deprecated type alias `unstable_generic_param::Alias5`: test - --> $DIR/generics-default-stability.rs:171:12 + --> $DIR/generics-default-stability.rs:170:12 | LL | let _: Alias5<isize> = Alias5::Some(1); | ^^^^^^^^^^^^^ warning: use of deprecated type alias `unstable_generic_param::Alias5`: test - --> $DIR/generics-default-stability.rs:175:12 + --> $DIR/generics-default-stability.rs:174:12 | LL | let _: Alias5 = ALIAS5; | ^^^^^^ warning: use of deprecated type alias `unstable_generic_param::Alias5`: test - --> $DIR/generics-default-stability.rs:176:12 + --> $DIR/generics-default-stability.rs:175:12 | LL | let _: Alias5<usize> = ALIAS5; | ^^^^^^^^^^^^^ warning: use of deprecated type alias `unstable_generic_param::Alias5`: test - --> $DIR/generics-default-stability.rs:178:28 + --> $DIR/generics-default-stability.rs:177:28 | LL | let _: Alias5<isize> = Alias5::Some(0); - | ^^^^^^^^^^^^ + | ^^^^^^ warning: use of deprecated type alias `unstable_generic_param::Alias5`: test - --> $DIR/generics-default-stability.rs:178:12 + --> $DIR/generics-default-stability.rs:177:12 | LL | let _: Alias5<isize> = Alias5::Some(0); | ^^^^^^^^^^^^^ warning: use of deprecated variant `unstable_generic_param::Enum4::Some`: test - --> $DIR/generics-default-stability.rs:232:27 + --> $DIR/generics-default-stability.rs:231:27 | LL | let _: Enum4<isize> = Enum4::Some(1); | ^^^^^^^^^^^ warning: use of deprecated enum `unstable_generic_param::Enum4`: test - --> $DIR/generics-default-stability.rs:232:12 + --> $DIR/generics-default-stability.rs:231:12 | LL | let _: Enum4<isize> = Enum4::Some(1); | ^^^^^^^^^^^^ warning: use of deprecated enum `unstable_generic_param::Enum4`: test - --> $DIR/generics-default-stability.rs:236:12 + --> $DIR/generics-default-stability.rs:235:12 | LL | let _: Enum4 = ENUM4; | ^^^^^ warning: use of deprecated enum `unstable_generic_param::Enum4`: test - --> $DIR/generics-default-stability.rs:237:12 + --> $DIR/generics-default-stability.rs:236:12 | LL | let _: Enum4<usize> = ENUM4; | ^^^^^^^^^^^^ warning: use of deprecated variant `unstable_generic_param::Enum4::Some`: test - --> $DIR/generics-default-stability.rs:238:27 + --> $DIR/generics-default-stability.rs:237:27 | LL | let _: Enum4<isize> = Enum4::Some(0); | ^^^^^^^^^^^ warning: use of deprecated enum `unstable_generic_param::Enum4`: test - --> $DIR/generics-default-stability.rs:238:12 + --> $DIR/generics-default-stability.rs:237:12 | LL | let _: Enum4<isize> = Enum4::Some(0); | ^^^^^^^^^^^^ warning: use of deprecated variant `unstable_generic_param::Enum5::Some`: test - --> $DIR/generics-default-stability.rs:243:27 + --> $DIR/generics-default-stability.rs:242:27 | LL | let _: Enum5<isize> = Enum5::Some(1); | ^^^^^^^^^^^ warning: use of deprecated enum `unstable_generic_param::Enum5`: test - --> $DIR/generics-default-stability.rs:243:12 + --> $DIR/generics-default-stability.rs:242:12 | LL | let _: Enum5<isize> = Enum5::Some(1); | ^^^^^^^^^^^^ warning: use of deprecated enum `unstable_generic_param::Enum5`: test - --> $DIR/generics-default-stability.rs:247:12 + --> $DIR/generics-default-stability.rs:246:12 | LL | let _: Enum5 = ENUM5; | ^^^^^ warning: use of deprecated enum `unstable_generic_param::Enum5`: test - --> $DIR/generics-default-stability.rs:248:12 + --> $DIR/generics-default-stability.rs:247:12 | LL | let _: Enum5<usize> = ENUM5; | ^^^^^^^^^^^^ warning: use of deprecated variant `unstable_generic_param::Enum5::Some`: test - --> $DIR/generics-default-stability.rs:250:27 + --> $DIR/generics-default-stability.rs:249:27 | LL | let _: Enum5<isize> = Enum5::Some(0); | ^^^^^^^^^^^ warning: use of deprecated enum `unstable_generic_param::Enum5`: test - --> $DIR/generics-default-stability.rs:250:12 + --> $DIR/generics-default-stability.rs:249:12 | LL | let _: Enum5<isize> = Enum5::Some(0); | ^^^^^^^^^^^^ error[E0658]: use of unstable library feature 'unstable_default' - --> $DIR/generics-default-stability.rs:36:20 + --> $DIR/generics-default-stability.rs:35:20 | LL | let _: Struct1<isize> = Struct1 { field: 1 }; | ^^^^^ @@ -249,7 +249,7 @@ LL | let _: Struct1<isize> = Struct1 { field: 1 }; = help: add `#![feature(unstable_default)]` to the crate attributes to enable error[E0658]: use of unstable library feature 'unstable_default' - --> $DIR/generics-default-stability.rs:40:20 + --> $DIR/generics-default-stability.rs:39:20 | LL | let _: Struct1<usize> = STRUCT1; | ^^^^^ @@ -257,7 +257,7 @@ LL | let _: Struct1<usize> = STRUCT1; = help: add `#![feature(unstable_default)]` to the crate attributes to enable error[E0658]: use of unstable library feature 'unstable_default' - --> $DIR/generics-default-stability.rs:41:20 + --> $DIR/generics-default-stability.rs:40:20 | LL | let _: Struct1<isize> = Struct1 { field: 0 }; | ^^^^^ @@ -265,7 +265,7 @@ LL | let _: Struct1<isize> = Struct1 { field: 0 }; = help: add `#![feature(unstable_default)]` to the crate attributes to enable error[E0658]: use of unstable library feature 'unstable_default' - --> $DIR/generics-default-stability.rs:70:27 + --> $DIR/generics-default-stability.rs:69:27 | LL | let _: Struct3<isize, usize> = STRUCT3; | ^^^^^ @@ -273,7 +273,7 @@ LL | let _: Struct3<isize, usize> = STRUCT3; = help: add `#![feature(unstable_default)]` to the crate attributes to enable error[E0658]: use of unstable library feature 'unstable_default' - --> $DIR/generics-default-stability.rs:72:27 + --> $DIR/generics-default-stability.rs:71:27 | LL | let _: Struct3<isize, isize> = Struct3 { field1: 0, field2: 0 }; | ^^^^^ @@ -281,7 +281,7 @@ LL | let _: Struct3<isize, isize> = Struct3 { field1: 0, field2: 0 }; = help: add `#![feature(unstable_default)]` to the crate attributes to enable error[E0658]: use of unstable library feature 'unstable_default' - --> $DIR/generics-default-stability.rs:73:27 + --> $DIR/generics-default-stability.rs:72:27 | LL | let _: Struct3<usize, usize> = Struct3 { field1: 0, field2: 0 }; | ^^^^^ @@ -289,7 +289,7 @@ LL | let _: Struct3<usize, usize> = Struct3 { field1: 0, field2: 0 }; = help: add `#![feature(unstable_default)]` to the crate attributes to enable error[E0658]: use of unstable library feature 'unstable_default' - --> $DIR/generics-default-stability.rs:97:20 + --> $DIR/generics-default-stability.rs:96:20 | LL | let _: Struct5<isize> = Struct5 { field: 1 }; | ^^^^^ @@ -297,7 +297,7 @@ LL | let _: Struct5<isize> = Struct5 { field: 1 }; = help: add `#![feature(unstable_default)]` to the crate attributes to enable error[E0658]: use of unstable library feature 'unstable_default' - --> $DIR/generics-default-stability.rs:103:20 + --> $DIR/generics-default-stability.rs:102:20 | LL | let _: Struct5<usize> = STRUCT5; | ^^^^^ @@ -305,7 +305,7 @@ LL | let _: Struct5<usize> = STRUCT5; = help: add `#![feature(unstable_default)]` to the crate attributes to enable error[E0658]: use of unstable library feature 'unstable_default' - --> $DIR/generics-default-stability.rs:105:20 + --> $DIR/generics-default-stability.rs:104:20 | LL | let _: Struct5<isize> = Struct5 { field: 0 }; | ^^^^^ @@ -313,7 +313,7 @@ LL | let _: Struct5<isize> = Struct5 { field: 0 }; = help: add `#![feature(unstable_default)]` to the crate attributes to enable error[E0658]: use of unstable library feature 'unstable_default' - --> $DIR/generics-default-stability.rs:113:19 + --> $DIR/generics-default-stability.rs:112:19 | LL | let _: Alias1<isize> = Alias1::Some(1); | ^^^^^ @@ -321,7 +321,7 @@ LL | let _: Alias1<isize> = Alias1::Some(1); = help: add `#![feature(unstable_default)]` to the crate attributes to enable error[E0658]: use of unstable library feature 'unstable_default' - --> $DIR/generics-default-stability.rs:117:19 + --> $DIR/generics-default-stability.rs:116:19 | LL | let _: Alias1<usize> = ALIAS1; | ^^^^^ @@ -329,7 +329,7 @@ LL | let _: Alias1<usize> = ALIAS1; = help: add `#![feature(unstable_default)]` to the crate attributes to enable error[E0658]: use of unstable library feature 'unstable_default' - --> $DIR/generics-default-stability.rs:118:19 + --> $DIR/generics-default-stability.rs:117:19 | LL | let _: Alias1<isize> = Alias1::Some(0); | ^^^^^ @@ -337,7 +337,7 @@ LL | let _: Alias1<isize> = Alias1::Some(0); = help: add `#![feature(unstable_default)]` to the crate attributes to enable error[E0658]: use of unstable library feature 'unstable_default' - --> $DIR/generics-default-stability.rs:146:26 + --> $DIR/generics-default-stability.rs:145:26 | LL | let _: Alias3<isize, usize> = ALIAS3; | ^^^^^ @@ -345,7 +345,7 @@ LL | let _: Alias3<isize, usize> = ALIAS3; = help: add `#![feature(unstable_default)]` to the crate attributes to enable error[E0658]: use of unstable library feature 'unstable_default' - --> $DIR/generics-default-stability.rs:148:26 + --> $DIR/generics-default-stability.rs:147:26 | LL | let _: Alias3<isize, isize> = Alias3::Ok(0); | ^^^^^ @@ -353,7 +353,7 @@ LL | let _: Alias3<isize, isize> = Alias3::Ok(0); = help: add `#![feature(unstable_default)]` to the crate attributes to enable error[E0658]: use of unstable library feature 'unstable_default' - --> $DIR/generics-default-stability.rs:149:26 + --> $DIR/generics-default-stability.rs:148:26 | LL | let _: Alias3<usize, usize> = Alias3::Ok(0); | ^^^^^ @@ -361,7 +361,7 @@ LL | let _: Alias3<usize, usize> = Alias3::Ok(0); = help: add `#![feature(unstable_default)]` to the crate attributes to enable error[E0658]: use of unstable library feature 'unstable_default' - --> $DIR/generics-default-stability.rs:171:19 + --> $DIR/generics-default-stability.rs:170:19 | LL | let _: Alias5<isize> = Alias5::Some(1); | ^^^^^ @@ -369,7 +369,7 @@ LL | let _: Alias5<isize> = Alias5::Some(1); = help: add `#![feature(unstable_default)]` to the crate attributes to enable error[E0658]: use of unstable library feature 'unstable_default' - --> $DIR/generics-default-stability.rs:176:19 + --> $DIR/generics-default-stability.rs:175:19 | LL | let _: Alias5<usize> = ALIAS5; | ^^^^^ @@ -377,7 +377,7 @@ LL | let _: Alias5<usize> = ALIAS5; = help: add `#![feature(unstable_default)]` to the crate attributes to enable error[E0658]: use of unstable library feature 'unstable_default' - --> $DIR/generics-default-stability.rs:178:19 + --> $DIR/generics-default-stability.rs:177:19 | LL | let _: Alias5<isize> = Alias5::Some(0); | ^^^^^ @@ -385,7 +385,7 @@ LL | let _: Alias5<isize> = Alias5::Some(0); = help: add `#![feature(unstable_default)]` to the crate attributes to enable error[E0658]: use of unstable library feature 'unstable_default' - --> $DIR/generics-default-stability.rs:185:18 + --> $DIR/generics-default-stability.rs:184:18 | LL | let _: Enum1<isize> = Enum1::Some(1); | ^^^^^ @@ -393,7 +393,7 @@ LL | let _: Enum1<isize> = Enum1::Some(1); = help: add `#![feature(unstable_default)]` to the crate attributes to enable error[E0658]: use of unstable library feature 'unstable_default' - --> $DIR/generics-default-stability.rs:189:18 + --> $DIR/generics-default-stability.rs:188:18 | LL | let _: Enum1<usize> = ENUM1; | ^^^^^ @@ -401,7 +401,7 @@ LL | let _: Enum1<usize> = ENUM1; = help: add `#![feature(unstable_default)]` to the crate attributes to enable error[E0658]: use of unstable library feature 'unstable_default' - --> $DIR/generics-default-stability.rs:190:18 + --> $DIR/generics-default-stability.rs:189:18 | LL | let _: Enum1<isize> = Enum1::Some(0); | ^^^^^ @@ -409,7 +409,7 @@ LL | let _: Enum1<isize> = Enum1::Some(0); = help: add `#![feature(unstable_default)]` to the crate attributes to enable error[E0658]: use of unstable library feature 'unstable_default' - --> $DIR/generics-default-stability.rs:218:25 + --> $DIR/generics-default-stability.rs:217:25 | LL | let _: Enum3<isize, usize> = ENUM3; | ^^^^^ @@ -417,7 +417,7 @@ LL | let _: Enum3<isize, usize> = ENUM3; = help: add `#![feature(unstable_default)]` to the crate attributes to enable error[E0658]: use of unstable library feature 'unstable_default' - --> $DIR/generics-default-stability.rs:220:25 + --> $DIR/generics-default-stability.rs:219:25 | LL | let _: Enum3<isize, isize> = Enum3::Ok(0); | ^^^^^ @@ -425,7 +425,7 @@ LL | let _: Enum3<isize, isize> = Enum3::Ok(0); = help: add `#![feature(unstable_default)]` to the crate attributes to enable error[E0658]: use of unstable library feature 'unstable_default' - --> $DIR/generics-default-stability.rs:221:25 + --> $DIR/generics-default-stability.rs:220:25 | LL | let _: Enum3<usize, usize> = Enum3::Ok(0); | ^^^^^ @@ -433,7 +433,7 @@ LL | let _: Enum3<usize, usize> = Enum3::Ok(0); = help: add `#![feature(unstable_default)]` to the crate attributes to enable error[E0658]: use of unstable library feature 'unstable_default' - --> $DIR/generics-default-stability.rs:243:18 + --> $DIR/generics-default-stability.rs:242:18 | LL | let _: Enum5<isize> = Enum5::Some(1); | ^^^^^ @@ -441,7 +441,7 @@ LL | let _: Enum5<isize> = Enum5::Some(1); = help: add `#![feature(unstable_default)]` to the crate attributes to enable error[E0658]: use of unstable library feature 'unstable_default' - --> $DIR/generics-default-stability.rs:248:18 + --> $DIR/generics-default-stability.rs:247:18 | LL | let _: Enum5<usize> = ENUM5; | ^^^^^ @@ -449,7 +449,7 @@ LL | let _: Enum5<usize> = ENUM5; = help: add `#![feature(unstable_default)]` to the crate attributes to enable error[E0658]: use of unstable library feature 'unstable_default' - --> $DIR/generics-default-stability.rs:250:18 + --> $DIR/generics-default-stability.rs:249:18 | LL | let _: Enum5<isize> = Enum5::Some(0); | ^^^^^ @@ -457,7 +457,7 @@ LL | let _: Enum5<isize> = Enum5::Some(0); = help: add `#![feature(unstable_default)]` to the crate attributes to enable error[E0658]: use of unstable library feature 'box_alloc_param' - --> $DIR/generics-default-stability.rs:257:24 + --> $DIR/generics-default-stability.rs:256:24 | LL | let _: Box1<isize, System> = Box1::new(1); | ^^^^^^ @@ -465,25 +465,25 @@ LL | let _: Box1<isize, System> = Box1::new(1); = help: add `#![feature(box_alloc_param)]` to the crate attributes to enable warning: use of deprecated field `unstable_generic_param::Struct4::field`: test - --> $DIR/generics-default-stability.rs:84:39 + --> $DIR/generics-default-stability.rs:83:39 | LL | let _: Struct4<isize> = Struct4 { field: 1 }; | ^^^^^^^^ warning: use of deprecated field `unstable_generic_param::Struct4::field`: test - --> $DIR/generics-default-stability.rs:91:39 + --> $DIR/generics-default-stability.rs:90:39 | LL | let _: Struct4<isize> = Struct4 { field: 0 }; | ^^^^^^^^ warning: use of deprecated field `unstable_generic_param::Struct5::field`: test - --> $DIR/generics-default-stability.rs:97:39 + --> $DIR/generics-default-stability.rs:96:39 | LL | let _: Struct5<isize> = Struct5 { field: 1 }; | ^^^^^^^^ warning: use of deprecated field `unstable_generic_param::Struct5::field`: test - --> $DIR/generics-default-stability.rs:105:39 + --> $DIR/generics-default-stability.rs:104:39 | LL | let _: Struct5<isize> = Struct5 { field: 0 }; | ^^^^^^^^ diff --git a/src/test/ui/bad/bad-const-type.rs b/src/test/ui/static/bad-const-type.rs similarity index 100% rename from src/test/ui/bad/bad-const-type.rs rename to src/test/ui/static/bad-const-type.rs diff --git a/src/test/ui/bad/bad-const-type.stderr b/src/test/ui/static/bad-const-type.stderr similarity index 100% rename from src/test/ui/bad/bad-const-type.stderr rename to src/test/ui/static/bad-const-type.stderr diff --git a/src/test/ui/static/static-reference-to-fn-2.stderr b/src/test/ui/static/static-reference-to-fn-2.stderr index 028e11a60cef4..ff15884bd445d 100644 --- a/src/test/ui/static/static-reference-to-fn-2.stderr +++ b/src/test/ui/static/static-reference-to-fn-2.stderr @@ -40,6 +40,8 @@ LL | | statefn: &id(state1 as StateMachineFunc) | | ------------------------------ temporary value created here LL | | } | |_____^ returns a value referencing data owned by the current function + | + = help: use `.collect()` to allocate the iterator error: aborting due to 4 previous errors diff --git a/src/test/ui/issues/issue-15261.rs b/src/test/ui/statics/issue-15261.rs similarity index 100% rename from src/test/ui/issues/issue-15261.rs rename to src/test/ui/statics/issue-15261.rs diff --git a/src/test/ui/issues/issue-44373-2.rs b/src/test/ui/statics/issue-44373-2.rs similarity index 100% rename from src/test/ui/issues/issue-44373-2.rs rename to src/test/ui/statics/issue-44373-2.rs diff --git a/src/test/ui/issues/issue-44373.rs b/src/test/ui/statics/issue-44373.rs similarity index 100% rename from src/test/ui/issues/issue-44373.rs rename to src/test/ui/statics/issue-44373.rs diff --git a/src/test/ui/issues/issue-44373.stderr b/src/test/ui/statics/issue-44373.stderr similarity index 100% rename from src/test/ui/issues/issue-44373.stderr rename to src/test/ui/statics/issue-44373.stderr diff --git a/src/test/ui/issues/issue-1701.rs b/src/test/ui/structs-enums/issue-1701.rs similarity index 100% rename from src/test/ui/issues/issue-1701.rs rename to src/test/ui/structs-enums/issue-1701.rs diff --git a/src/test/ui/structs/80853.stderr b/src/test/ui/structs/80853.stderr deleted file mode 100644 index 8a38e32c1d0ca..0000000000000 --- a/src/test/ui/structs/80853.stderr +++ /dev/null @@ -1,13 +0,0 @@ -error[E0618]: expected function, found `S` - --> $DIR/80853.rs:4:5 - | -LL | fn repro_ref(thing: S) { - | ----- `thing` has type `S` -LL | thing(); - | ^^^^^-- - | | - | call expression requires function - -error: aborting due to previous error - -For more information about this error, try `rustc --explain E0618`. diff --git a/src/test/ui/structs/80853.rs b/src/test/ui/structs/issue-80853.rs similarity index 100% rename from src/test/ui/structs/80853.rs rename to src/test/ui/structs/issue-80853.rs diff --git a/src/test/ui/structs/issue-80853.stderr b/src/test/ui/structs/issue-80853.stderr new file mode 100644 index 0000000000000..1c7d52b6d6054 --- /dev/null +++ b/src/test/ui/structs/issue-80853.stderr @@ -0,0 +1,13 @@ +error[E0618]: expected function, found `S` + --> $DIR/issue-80853.rs:4:5 + | +LL | fn repro_ref(thing: S) { + | ----- `thing` has type `S` +LL | thing(); + | ^^^^^-- + | | + | call expression requires function + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0618`. diff --git a/src/test/ui/structs/struct-path-associated-type.stderr b/src/test/ui/structs/struct-path-associated-type.stderr index f8a2c7c6b6c20..0b1b6a5e3af28 100644 --- a/src/test/ui/structs/struct-path-associated-type.stderr +++ b/src/test/ui/structs/struct-path-associated-type.stderr @@ -14,7 +14,7 @@ error[E0071]: expected struct, variant or union type, found associated type --> $DIR/struct-path-associated-type.rs:14:13 | LL | let z = T::A::<u8> {}; - | ^^^^^^^^^^ not a struct + | ^^^^ not a struct error[E0071]: expected struct, variant or union type, found associated type --> $DIR/struct-path-associated-type.rs:18:9 @@ -38,7 +38,7 @@ error[E0223]: ambiguous associated type --> $DIR/struct-path-associated-type.rs:33:13 | LL | let z = S::A::<u8> {}; - | ^^^^^^^^^^ help: use fully-qualified syntax: `<S as Trait>::A` + | ^^^^ help: use fully-qualified syntax: `<S as Trait>::A` error[E0223]: ambiguous associated type --> $DIR/struct-path-associated-type.rs:35:9 diff --git a/src/test/ui/structs/struct-variant-privacy-xc.rs b/src/test/ui/structs/struct-variant-privacy-xc.rs index 10e9639096f0f..763ab952738b7 100644 --- a/src/test/ui/structs/struct-variant-privacy-xc.rs +++ b/src/test/ui/structs/struct-variant-privacy-xc.rs @@ -1,7 +1,8 @@ // aux-build:struct_variant_privacy.rs extern crate struct_variant_privacy; -fn f(b: struct_variant_privacy::Bar) { //~ ERROR enum `Bar` is private +fn f(b: struct_variant_privacy::Bar) { + //~^ ERROR enum `Bar` is private match b { struct_variant_privacy::Bar::Baz { a: _a } => {} //~ ERROR enum `Bar` is private } diff --git a/src/test/ui/structs/struct-variant-privacy-xc.stderr b/src/test/ui/structs/struct-variant-privacy-xc.stderr index 4e022cef1b2d3..1c1caaef8b79d 100644 --- a/src/test/ui/structs/struct-variant-privacy-xc.stderr +++ b/src/test/ui/structs/struct-variant-privacy-xc.stderr @@ -11,7 +11,7 @@ LL | enum Bar { | ^^^^^^^^ error[E0603]: enum `Bar` is private - --> $DIR/struct-variant-privacy-xc.rs:6:33 + --> $DIR/struct-variant-privacy-xc.rs:7:33 | LL | struct_variant_privacy::Bar::Baz { a: _a } => {} | ^^^ private enum diff --git a/src/test/ui/structs/struct-variant-privacy.rs b/src/test/ui/structs/struct-variant-privacy.rs index 8355879e7d94d..fcdf9a22baf4b 100644 --- a/src/test/ui/structs/struct-variant-privacy.rs +++ b/src/test/ui/structs/struct-variant-privacy.rs @@ -1,10 +1,11 @@ mod foo { enum Bar { - Baz { a: isize } + Baz { a: isize }, } } -fn f(b: foo::Bar) { //~ ERROR enum `Bar` is private +fn f(b: foo::Bar) { + //~^ ERROR enum `Bar` is private match b { foo::Bar::Baz { a: _a } => {} //~ ERROR enum `Bar` is private } diff --git a/src/test/ui/structs/struct-variant-privacy.stderr b/src/test/ui/structs/struct-variant-privacy.stderr index a6bc381ff6b38..eafd26c716f11 100644 --- a/src/test/ui/structs/struct-variant-privacy.stderr +++ b/src/test/ui/structs/struct-variant-privacy.stderr @@ -11,7 +11,7 @@ LL | enum Bar { | ^^^^^^^^ error[E0603]: enum `Bar` is private - --> $DIR/struct-variant-privacy.rs:9:14 + --> $DIR/struct-variant-privacy.rs:10:14 | LL | foo::Bar::Baz { a: _a } => {} | ^^^ private enum diff --git a/src/test/ui/structs/structure-constructor-type-mismatch.rs b/src/test/ui/structs/structure-constructor-type-mismatch.rs index 56c8ffb3e65b7..efc6304a6f742 100644 --- a/src/test/ui/structs/structure-constructor-type-mismatch.rs +++ b/src/test/ui/structs/structure-constructor-type-mismatch.rs @@ -1,5 +1,3 @@ -// ignore-tidy-linelength - struct Point<T> { x: T, y: T, diff --git a/src/test/ui/structs/structure-constructor-type-mismatch.stderr b/src/test/ui/structs/structure-constructor-type-mismatch.stderr index 4611414967290..6438127868133 100644 --- a/src/test/ui/structs/structure-constructor-type-mismatch.stderr +++ b/src/test/ui/structs/structure-constructor-type-mismatch.stderr @@ -1,5 +1,5 @@ error[E0308]: mismatched types - --> $DIR/structure-constructor-type-mismatch.rs:19:12 + --> $DIR/structure-constructor-type-mismatch.rs:17:12 | LL | x: 1, | ^ @@ -8,7 +8,7 @@ LL | x: 1, | help: use a float literal: `1.0` error[E0308]: mismatched types - --> $DIR/structure-constructor-type-mismatch.rs:22:12 + --> $DIR/structure-constructor-type-mismatch.rs:20:12 | LL | y: 2, | ^ @@ -17,7 +17,7 @@ LL | y: 2, | help: use a float literal: `2.0` error[E0308]: mismatched types - --> $DIR/structure-constructor-type-mismatch.rs:28:12 + --> $DIR/structure-constructor-type-mismatch.rs:26:12 | LL | x: 3, | ^ @@ -26,7 +26,7 @@ LL | x: 3, | help: use a float literal: `3.0` error[E0308]: mismatched types - --> $DIR/structure-constructor-type-mismatch.rs:31:12 + --> $DIR/structure-constructor-type-mismatch.rs:29:12 | LL | y: 4, | ^ @@ -35,7 +35,7 @@ LL | y: 4, | help: use a float literal: `4.0` error[E0308]: mismatched types - --> $DIR/structure-constructor-type-mismatch.rs:37:12 + --> $DIR/structure-constructor-type-mismatch.rs:35:12 | LL | x: 5, | ^ @@ -44,7 +44,7 @@ LL | x: 5, | help: use a float literal: `5.0` error[E0308]: mismatched types - --> $DIR/structure-constructor-type-mismatch.rs:44:12 + --> $DIR/structure-constructor-type-mismatch.rs:42:12 | LL | x: 7, | ^ @@ -53,7 +53,7 @@ LL | x: 7, | help: use a float literal: `7.0` error[E0107]: this type alias takes 0 type arguments but 1 type argument was supplied - --> $DIR/structure-constructor-type-mismatch.rs:50:15 + --> $DIR/structure-constructor-type-mismatch.rs:48:15 | LL | let pt3 = PointF::<i32> { | ^^^^^^------- help: remove these generics @@ -61,13 +61,13 @@ LL | let pt3 = PointF::<i32> { | expected 0 type arguments | note: type alias defined here, with 0 type parameters - --> $DIR/structure-constructor-type-mismatch.rs:8:6 + --> $DIR/structure-constructor-type-mismatch.rs:6:6 | LL | type PointF = Point<f32>; | ^^^^^^ error[E0308]: mismatched types - --> $DIR/structure-constructor-type-mismatch.rs:51:12 + --> $DIR/structure-constructor-type-mismatch.rs:49:12 | LL | x: 9, | ^ @@ -76,7 +76,7 @@ LL | x: 9, | help: use a float literal: `9.0` error[E0308]: mismatched types - --> $DIR/structure-constructor-type-mismatch.rs:52:12 + --> $DIR/structure-constructor-type-mismatch.rs:50:12 | LL | y: 10, | ^^ @@ -85,7 +85,7 @@ LL | y: 10, | help: use a float literal: `10.0` error[E0107]: this type alias takes 0 type arguments but 1 type argument was supplied - --> $DIR/structure-constructor-type-mismatch.rs:56:9 + --> $DIR/structure-constructor-type-mismatch.rs:54:9 | LL | PointF::<u32> { .. } => {} | ^^^^^^------- help: remove these generics @@ -93,13 +93,13 @@ LL | PointF::<u32> { .. } => {} | expected 0 type arguments | note: type alias defined here, with 0 type parameters - --> $DIR/structure-constructor-type-mismatch.rs:8:6 + --> $DIR/structure-constructor-type-mismatch.rs:6:6 | LL | type PointF = Point<f32>; | ^^^^^^ error[E0308]: mismatched types - --> $DIR/structure-constructor-type-mismatch.rs:56:9 + --> $DIR/structure-constructor-type-mismatch.rs:54:9 | LL | match (Point { x: 1, y: 2 }) { | ---------------------- this expression has type `Point<{integer}>` @@ -110,7 +110,7 @@ LL | PointF::<u32> { .. } => {} found struct `Point<f32>` error[E0308]: mismatched types - --> $DIR/structure-constructor-type-mismatch.rs:61:9 + --> $DIR/structure-constructor-type-mismatch.rs:59:9 | LL | match (Point { x: 1, y: 2 }) { | ---------------------- this expression has type `Point<{integer}>` @@ -121,7 +121,7 @@ LL | PointF { .. } => {} found struct `Point<f32>` error[E0308]: mismatched types - --> $DIR/structure-constructor-type-mismatch.rs:69:9 + --> $DIR/structure-constructor-type-mismatch.rs:67:9 | LL | match (Pair { x: 1, y: 2 }) { | --------------------- this expression has type `Pair<{integer}, {integer}>` diff --git a/src/test/ui/substs-ppaux.normal.stderr b/src/test/ui/substs-ppaux.normal.stderr index 89be3d29e0cff..5bbf4225812b6 100644 --- a/src/test/ui/substs-ppaux.normal.stderr +++ b/src/test/ui/substs-ppaux.normal.stderr @@ -80,7 +80,11 @@ LL | <str as Foo<u8>>::bar; | ^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time | = help: the trait `Sized` is not implemented for `str` - = note: required because of the requirements on the impl of `Foo<'_, '_, u8>` for `str` +note: required because of the requirements on the impl of `Foo<'_, '_, u8>` for `str` + --> $DIR/substs-ppaux.rs:11:17 + | +LL | impl<'a,'b,T,S> Foo<'a, 'b, S> for T {} + | ^^^^^^^^^^^^^^ ^ error: aborting due to 5 previous errors diff --git a/src/test/ui/substs-ppaux.verbose.stderr b/src/test/ui/substs-ppaux.verbose.stderr index e37d087fcc958..20d7655337448 100644 --- a/src/test/ui/substs-ppaux.verbose.stderr +++ b/src/test/ui/substs-ppaux.verbose.stderr @@ -80,7 +80,11 @@ LL | <str as Foo<u8>>::bar; | ^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time | = help: the trait `Sized` is not implemented for `str` - = note: required because of the requirements on the impl of `Foo<'_#0r, '_#1r, u8>` for `str` +note: required because of the requirements on the impl of `Foo<'_#0r, '_#1r, u8>` for `str` + --> $DIR/substs-ppaux.rs:11:17 + | +LL | impl<'a,'b,T,S> Foo<'a, 'b, S> for T {} + | ^^^^^^^^^^^^^^ ^ error: aborting due to 5 previous errors diff --git a/src/test/ui/suggestions/adt-param-with-implicit-sized-bound.stderr b/src/test/ui/suggestions/adt-param-with-implicit-sized-bound.stderr index 9437fbe61cc7e..5cb3a404037a7 100644 --- a/src/test/ui/suggestions/adt-param-with-implicit-sized-bound.stderr +++ b/src/test/ui/suggestions/adt-param-with-implicit-sized-bound.stderr @@ -5,7 +5,7 @@ LL | struct X<T>(T); | - required by this bound in `X` ... LL | struct Struct5<T: ?Sized>{ - | - this type parameter needs to be `Sized` + | - this type parameter needs to be `std::marker::Sized` LL | _t: X<T>, | ^^^^ doesn't have a size known at compile-time | diff --git a/src/test/ui/suggestions/as-ref-2.fixed b/src/test/ui/suggestions/as-ref-2.fixed new file mode 100644 index 0000000000000..13bbb233f3986 --- /dev/null +++ b/src/test/ui/suggestions/as-ref-2.fixed @@ -0,0 +1,13 @@ +// run-rustfix + +struct Struct; + +fn bar(_: &Struct) -> Struct { + Struct +} + +fn main() { + let foo = Some(Struct); + let _x: Option<Struct> = foo.as_ref().map(|s| bar(&s)); + let _y = foo; //~ERROR use of moved value: `foo` +} diff --git a/src/test/ui/suggestions/as-ref-2.rs b/src/test/ui/suggestions/as-ref-2.rs new file mode 100644 index 0000000000000..74d61cdd95f8d --- /dev/null +++ b/src/test/ui/suggestions/as-ref-2.rs @@ -0,0 +1,13 @@ +// run-rustfix + +struct Struct; + +fn bar(_: &Struct) -> Struct { + Struct +} + +fn main() { + let foo = Some(Struct); + let _x: Option<Struct> = foo.map(|s| bar(&s)); + let _y = foo; //~ERROR use of moved value: `foo` +} diff --git a/src/test/ui/suggestions/as-ref-2.stderr b/src/test/ui/suggestions/as-ref-2.stderr new file mode 100644 index 0000000000000..f2eddf2fb098e --- /dev/null +++ b/src/test/ui/suggestions/as-ref-2.stderr @@ -0,0 +1,23 @@ +error[E0382]: use of moved value: `foo` + --> $DIR/as-ref-2.rs:12:14 + | +LL | let foo = Some(Struct); + | --- move occurs because `foo` has type `Option<Struct>`, which does not implement the `Copy` trait +LL | let _x: Option<Struct> = foo.map(|s| bar(&s)); + | ---------------- `foo` moved due to this method call +LL | let _y = foo; + | ^^^ value used here after move + | +note: this function takes ownership of the receiver `self`, which moves `foo` + --> $SRC_DIR/core/src/option.rs:LL:COL + | +LL | pub fn map<U, F: FnOnce(T) -> U>(self, f: F) -> Option<U> { + | ^^^^ +help: consider calling `.as_ref()` to borrow the type's contents + | +LL | let _x: Option<Struct> = foo.as_ref().map(|s| bar(&s)); + | ^^^^^^^^^ + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0382`. diff --git a/src/test/ui/suggestions/as-ref.rs b/src/test/ui/suggestions/as-ref.rs index 03f04c389f1f3..46d9461538c78 100644 --- a/src/test/ui/suggestions/as-ref.rs +++ b/src/test/ui/suggestions/as-ref.rs @@ -1,25 +1,20 @@ struct Foo; + fn takes_ref(_: &Foo) {} fn main() { - let ref opt = Some(Foo); - opt.map(|arg| takes_ref(arg)); - //~^ ERROR mismatched types [E0308] - opt.and_then(|arg| Some(takes_ref(arg))); - //~^ ERROR mismatched types [E0308] - let ref opt: Result<_, ()> = Ok(Foo); - opt.map(|arg| takes_ref(arg)); - //~^ ERROR mismatched types [E0308] - opt.and_then(|arg| Ok(takes_ref(arg))); - //~^ ERROR mismatched types [E0308] - let x: &Option<usize> = &Some(3); - let y: Option<&usize> = x; - //~^ ERROR mismatched types [E0308] - let x: &Result<usize, usize> = &Ok(3); - let y: Result<&usize, &usize> = x; - //~^ ERROR mismatched types [E0308] - // note: do not suggest because of `E: usize` - let x: &Result<usize, usize> = &Ok(3); - let y: Result<&usize, usize> = x; - //~^ ERROR mismatched types [E0308] + let ref opt = Some(Foo); + opt.map(|arg| takes_ref(arg)); //~ ERROR mismatched types [E0308] + opt.and_then(|arg| Some(takes_ref(arg))); //~ ERROR mismatched types [E0308] + let ref opt: Result<_, ()> = Ok(Foo); + opt.map(|arg| takes_ref(arg)); //~ ERROR mismatched types [E0308] + opt.and_then(|arg| Ok(takes_ref(arg))); //~ ERROR mismatched types [E0308] + let x: &Option<usize> = &Some(3); + let y: Option<&usize> = x; //~ ERROR mismatched types [E0308] + let x: &Result<usize, usize> = &Ok(3); + let y: Result<&usize, &usize> = x; + //~^ ERROR mismatched types [E0308] + // note: do not suggest because of `E: usize` + let x: &Result<usize, usize> = &Ok(3); + let y: Result<&usize, usize> = x; //~ ERROR mismatched types [E0308] } diff --git a/src/test/ui/suggestions/as-ref.stderr b/src/test/ui/suggestions/as-ref.stderr index 7e4d7fb3933d3..dc5d7efd752cf 100644 --- a/src/test/ui/suggestions/as-ref.stderr +++ b/src/test/ui/suggestions/as-ref.stderr @@ -1,70 +1,70 @@ error[E0308]: mismatched types - --> $DIR/as-ref.rs:6:27 + --> $DIR/as-ref.rs:7:29 | -LL | opt.map(|arg| takes_ref(arg)); - | --- ^^^ expected `&Foo`, found struct `Foo` - | | - | help: consider using `as_ref` instead: `as_ref().map` +LL | opt.map(|arg| takes_ref(arg)); + | --- ^^^ expected `&Foo`, found struct `Foo` + | | + | help: consider using `as_ref` instead: `as_ref().map` error[E0308]: mismatched types - --> $DIR/as-ref.rs:8:37 + --> $DIR/as-ref.rs:8:39 | -LL | opt.and_then(|arg| Some(takes_ref(arg))); - | -------- ^^^ expected `&Foo`, found struct `Foo` - | | - | help: consider using `as_ref` instead: `as_ref().and_then` +LL | opt.and_then(|arg| Some(takes_ref(arg))); + | -------- ^^^ expected `&Foo`, found struct `Foo` + | | + | help: consider using `as_ref` instead: `as_ref().and_then` error[E0308]: mismatched types - --> $DIR/as-ref.rs:11:27 + --> $DIR/as-ref.rs:10:29 | -LL | opt.map(|arg| takes_ref(arg)); - | --- ^^^ expected `&Foo`, found struct `Foo` - | | - | help: consider using `as_ref` instead: `as_ref().map` +LL | opt.map(|arg| takes_ref(arg)); + | --- ^^^ expected `&Foo`, found struct `Foo` + | | + | help: consider using `as_ref` instead: `as_ref().map` error[E0308]: mismatched types - --> $DIR/as-ref.rs:13:35 + --> $DIR/as-ref.rs:11:37 | -LL | opt.and_then(|arg| Ok(takes_ref(arg))); - | -------- ^^^ expected `&Foo`, found struct `Foo` - | | - | help: consider using `as_ref` instead: `as_ref().and_then` +LL | opt.and_then(|arg| Ok(takes_ref(arg))); + | -------- ^^^ expected `&Foo`, found struct `Foo` + | | + | help: consider using `as_ref` instead: `as_ref().and_then` error[E0308]: mismatched types - --> $DIR/as-ref.rs:16:27 + --> $DIR/as-ref.rs:13:29 | -LL | let y: Option<&usize> = x; - | -------------- ^ - | | | - | | expected enum `Option`, found `&Option<usize>` - | | help: you can convert from `&Option<T>` to `Option<&T>` using `.as_ref()`: `x.as_ref()` - | expected due to this +LL | let y: Option<&usize> = x; + | -------------- ^ + | | | + | | expected enum `Option`, found `&Option<usize>` + | | help: you can convert from `&Option<T>` to `Option<&T>` using `.as_ref()`: `x.as_ref()` + | expected due to this | = note: expected enum `Option<&usize>` found reference `&Option<usize>` error[E0308]: mismatched types - --> $DIR/as-ref.rs:19:35 + --> $DIR/as-ref.rs:15:37 | -LL | let y: Result<&usize, &usize> = x; - | ---------------------- ^ expected enum `Result`, found reference - | | - | expected due to this +LL | let y: Result<&usize, &usize> = x; + | ---------------------- ^ expected enum `Result`, found reference + | | + | expected due to this | = note: expected enum `Result<&usize, &usize>` found reference `&Result<usize, usize>` help: you can convert from `&Result<T, E>` to `Result<&T, &E>` using `.as_ref()` | -LL | let y: Result<&usize, &usize> = x.as_ref(); - | ^^^^^^^^^^ +LL | let y: Result<&usize, &usize> = x.as_ref(); + | ^^^^^^^^^^ error[E0308]: mismatched types - --> $DIR/as-ref.rs:23:34 + --> $DIR/as-ref.rs:19:36 | -LL | let y: Result<&usize, usize> = x; - | --------------------- ^ expected enum `Result`, found reference - | | - | expected due to this +LL | let y: Result<&usize, usize> = x; + | --------------------- ^ expected enum `Result`, found reference + | | + | expected due to this | = note: expected enum `Result<&usize, usize>` found reference `&Result<usize, usize>` diff --git a/src/test/ui/suggestions/expected-boxed-future-isnt-pinned.stderr b/src/test/ui/suggestions/expected-boxed-future-isnt-pinned.stderr index 32961b7f87be0..3786457fb1ae3 100644 --- a/src/test/ui/suggestions/expected-boxed-future-isnt-pinned.stderr +++ b/src/test/ui/suggestions/expected-boxed-future-isnt-pinned.stderr @@ -46,6 +46,7 @@ error[E0277]: `dyn Future<Output = i32> + Send` cannot be unpinned LL | Pin::new(x) | ^^^^^^^^ the trait `Unpin` is not implemented for `dyn Future<Output = i32> + Send` | + = note: consider using `Box::pin` = note: required by `Pin::<P>::new` error[E0277]: `dyn Future<Output = i32> + Send` cannot be unpinned @@ -54,6 +55,7 @@ error[E0277]: `dyn Future<Output = i32> + Send` cannot be unpinned LL | Pin::new(Box::new(x)) | ^^^^^^^^ the trait `Unpin` is not implemented for `dyn Future<Output = i32> + Send` | + = note: consider using `Box::pin` = note: required by `Pin::<P>::new` error[E0308]: mismatched types diff --git a/src/test/ui/suggestions/into-str.stderr b/src/test/ui/suggestions/into-str.stderr index 2854b830ba822..26efd50bb8fd3 100644 --- a/src/test/ui/suggestions/into-str.stderr +++ b/src/test/ui/suggestions/into-str.stderr @@ -7,6 +7,7 @@ LL | fn foo<'a, T>(_t: T) where T: Into<&'a str> {} LL | foo(String::new()); | ^^^ the trait `From<String>` is not implemented for `&str` | + = note: to coerce a `String` into a `&str`, use `&*` as a prefix = note: required because of the requirements on the impl of `Into<&str>` for `String` error: aborting due to previous error diff --git a/src/test/ui/suggestions/issue-82361.stderr b/src/test/ui/suggestions/issue-82361.stderr index c19d59ccd4c66..4c78293ebb7f9 100644 --- a/src/test/ui/suggestions/issue-82361.stderr +++ b/src/test/ui/suggestions/issue-82361.stderr @@ -21,10 +21,10 @@ LL | | 1 | | - expected because of this LL | | } else { LL | | &1 - | | -^ + | | ^^ | | | | | expected integer, found `&{integer}` - | | help: consider removing the `&` + | | help: consider removing the borrow: `1` LL | | }; | |_____- `if` and `else` have incompatible types @@ -36,10 +36,10 @@ LL | | 1 | | - expected because of this LL | | } else { LL | | &mut 1 - | | -----^ + | | ^^^^^^ | | | | | expected integer, found `&mut {integer}` - | | help: consider removing the `&mut` + | | help: consider removing the borrow: `1` LL | | }; | |_____- `if` and `else` have incompatible types diff --git a/src/test/ui/suggestions/issue-83892.fixed b/src/test/ui/suggestions/issue-83892.fixed new file mode 100644 index 0000000000000..dd093a7a0e312 --- /dev/null +++ b/src/test/ui/suggestions/issue-83892.fixed @@ -0,0 +1,11 @@ +// run-rustfix + +fn func() -> u8 { + 0 +} + +fn main() { + match () { + () => func() //~ ERROR mismatched types + }; +} diff --git a/src/test/ui/suggestions/issue-83892.rs b/src/test/ui/suggestions/issue-83892.rs new file mode 100644 index 0000000000000..1d56ecee868a1 --- /dev/null +++ b/src/test/ui/suggestions/issue-83892.rs @@ -0,0 +1,11 @@ +// run-rustfix + +fn func() -> u8 { + 0 +} + +fn main() { + match () { + () => func() //~ ERROR mismatched types + } +} diff --git a/src/test/ui/suggestions/issue-83892.stderr b/src/test/ui/suggestions/issue-83892.stderr new file mode 100644 index 0000000000000..baf6b1447e69d --- /dev/null +++ b/src/test/ui/suggestions/issue-83892.stderr @@ -0,0 +1,14 @@ +error[E0308]: mismatched types + --> $DIR/issue-83892.rs:9:15 + | +LL | fn main() { + | - expected `()` because of default return type +LL | match () { +LL | () => func() + | ^^^^^^ expected `()`, found `u8` +LL | } + | - help: consider using a semicolon here: `;` + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0308`. diff --git a/src/test/ui/suggestions/issue-83943.fixed b/src/test/ui/suggestions/issue-83943.fixed new file mode 100644 index 0000000000000..e0d4ee29ebf88 --- /dev/null +++ b/src/test/ui/suggestions/issue-83943.fixed @@ -0,0 +1,9 @@ +// run-rustfix + +fn main() { + if true { + "A".to_string() + } else { + "B".to_string() //~ ERROR `if` and `else` have incompatible types + }; +} diff --git a/src/test/ui/suggestions/issue-83943.rs b/src/test/ui/suggestions/issue-83943.rs new file mode 100644 index 0000000000000..68d50c1775c7f --- /dev/null +++ b/src/test/ui/suggestions/issue-83943.rs @@ -0,0 +1,9 @@ +// run-rustfix + +fn main() { + if true { + "A".to_string() + } else { + "B" //~ ERROR `if` and `else` have incompatible types + }; +} diff --git a/src/test/ui/suggestions/issue-83943.stderr b/src/test/ui/suggestions/issue-83943.stderr new file mode 100644 index 0000000000000..a26700ea3c7c5 --- /dev/null +++ b/src/test/ui/suggestions/issue-83943.stderr @@ -0,0 +1,18 @@ +error[E0308]: `if` and `else` have incompatible types + --> $DIR/issue-83943.rs:7:9 + | +LL | / if true { +LL | | "A".to_string() + | | --------------- expected because of this +LL | | } else { +LL | | "B" + | | ^^^ + | | | + | | expected struct `String`, found `&str` + | | help: try using a conversion method: `"B".to_string()` +LL | | }; + | |_____- `if` and `else` have incompatible types + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0308`. diff --git a/src/test/ui/suggestions/lifetimes/missing-lifetimes-in-signature.nll.stderr b/src/test/ui/suggestions/lifetimes/missing-lifetimes-in-signature.nll.stderr deleted file mode 100644 index b509610b89e26..0000000000000 --- a/src/test/ui/suggestions/lifetimes/missing-lifetimes-in-signature.nll.stderr +++ /dev/null @@ -1,92 +0,0 @@ -error[E0261]: use of undeclared lifetime name `'a` - --> $DIR/missing-lifetimes-in-signature.rs:37:11 - | -LL | fn baz<G: 'a, T>(g: G, dest: &mut T) -> impl FnOnce() + '_ - | - ^^ undeclared lifetime - | | - | help: consider introducing lifetime `'a` here: `'a,` - -error: lifetime may not live long enough - --> $DIR/missing-lifetimes-in-signature.rs:15:37 - | -LL | fn foo<G, T>(g: G, dest: &mut T) -> impl FnOnce() - | - ^^^^^^^^^^^^^ opaque type requires that `'1` must outlive `'static` - | | - | let's call the lifetime of this reference `'1` - | -help: to allow this `impl Trait` to capture borrowed data with lifetime `'1`, add `'_` as a bound - | -LL | fn foo<G, T>(g: G, dest: &mut T) -> impl FnOnce() + '_ - | ^^^^^^^^^^^^^^^^^^ - -error[E0311]: the parameter type `G` may not live long enough - --> $DIR/missing-lifetimes-in-signature.rs:25:37 - | -LL | fn bar<G, T>(g: G, dest: &mut T) -> impl FnOnce() + '_ - | ^^^^^^^^^^^^^^^^^^ - | -note: the parameter type `G` must be valid for the anonymous lifetime defined on the function body at 25:26... - --> $DIR/missing-lifetimes-in-signature.rs:25:26 - | -LL | fn bar<G, T>(g: G, dest: &mut T) -> impl FnOnce() + '_ - | ^^^^^^ - -error[E0311]: the parameter type `G` may not live long enough - --> $DIR/missing-lifetimes-in-signature.rs:47:45 - | -LL | fn qux<'a, G: 'a, T>(g: G, dest: &mut T) -> impl FnOnce() + '_ - | ^^^^^^^^^^^^^^^^^^ - | -note: the parameter type `G` must be valid for the anonymous lifetime defined on the function body at 47:34... - --> $DIR/missing-lifetimes-in-signature.rs:47:34 - | -LL | fn qux<'a, G: 'a, T>(g: G, dest: &mut T) -> impl FnOnce() + '_ - | ^^^^^^ - -error[E0311]: the parameter type `G` may not live long enough - --> $DIR/missing-lifetimes-in-signature.rs:59:58 - | -LL | fn qux<'b, G: Get<T> + 'b, T>(g: G, dest: &mut T) -> impl FnOnce() + '_ { - | ^^^^^^^^^^^^^^^^^^ - | -note: the parameter type `G` must be valid for the anonymous lifetime defined on the method body at 59:47... - --> $DIR/missing-lifetimes-in-signature.rs:59:47 - | -LL | fn qux<'b, G: Get<T> + 'b, T>(g: G, dest: &mut T) -> impl FnOnce() + '_ { - | ^^^^^^ - -error[E0311]: the parameter type `G` may not live long enough - --> $DIR/missing-lifetimes-in-signature.rs:68:45 - | -LL | fn bat<'a, G: 'a, T>(g: G, dest: &mut T) -> impl FnOnce() + '_ + 'a - | ^^^^^^^^^^^^^^^^^^^^^^^ - | -note: the parameter type `G` must be valid for the anonymous lifetime defined on the function body at 68:34... - --> $DIR/missing-lifetimes-in-signature.rs:68:34 - | -LL | fn bat<'a, G: 'a, T>(g: G, dest: &mut T) -> impl FnOnce() + '_ + 'a - | ^^^^^^ - -error[E0621]: explicit lifetime required in the type of `dest` - --> $DIR/missing-lifetimes-in-signature.rs:73:5 - | -LL | fn bat<'a, G: 'a, T>(g: G, dest: &mut T) -> impl FnOnce() + '_ + 'a - | ------ help: add explicit lifetime `'a` to the type of `dest`: `&'a mut T` -... -LL | / move || { -LL | | *dest = g.get(); -LL | | } - | |_____^ lifetime `'a` required - -error[E0309]: the parameter type `G` may not live long enough - --> $DIR/missing-lifetimes-in-signature.rs:79:44 - | -LL | fn bak<'a, G, T>(g: G, dest: &'a mut T) -> impl FnOnce() + 'a - | ^^^^^^^^^^^^^^^^^^ - | - = help: consider adding an explicit lifetime bound `G: 'a`... - -error: aborting due to 8 previous errors - -Some errors have detailed explanations: E0261, E0309, E0621. -For more information about an error, try `rustc --explain E0261`. diff --git a/src/test/ui/suggestions/lifetimes/missing-lifetimes-in-signature.rs b/src/test/ui/suggestions/lifetimes/missing-lifetimes-in-signature.rs index 94dd826a15cae..dd434ea531878 100644 --- a/src/test/ui/suggestions/lifetimes/missing-lifetimes-in-signature.rs +++ b/src/test/ui/suggestions/lifetimes/missing-lifetimes-in-signature.rs @@ -16,14 +16,13 @@ fn foo<G, T>(g: G, dest: &mut T) -> impl FnOnce() where G: Get<T> { - move || { //~ ERROR `dest` + move || { *dest = g.get(); } } // After applying suggestion for `foo`: fn bar<G, T>(g: G, dest: &mut T) -> impl FnOnce() + '_ -//~^ ERROR the parameter type `G` may not live long enough where G: Get<T> { @@ -45,7 +44,6 @@ where // After applying suggestion for `baz`: fn qux<'a, G: 'a, T>(g: G, dest: &mut T) -> impl FnOnce() + '_ -//~^ ERROR the parameter type `G` may not live long enough where G: Get<T> { @@ -57,7 +55,6 @@ where // Same as above, but show that we pay attention to lifetime names from parent item impl<'a> Foo { fn qux<'b, G: Get<T> + 'b, T>(g: G, dest: &mut T) -> impl FnOnce() + '_ { - //~^ ERROR the parameter type `G` may not live long enough move || { *dest = g.get(); } @@ -66,7 +63,6 @@ impl<'a> Foo { // After applying suggestion for `qux`: fn bat<'a, G: 'a, T>(g: G, dest: &mut T) -> impl FnOnce() + '_ + 'a -//~^ ERROR explicit lifetime required in the type of `dest` where G: Get<T> { @@ -77,7 +73,6 @@ where // Potential incorrect attempt: fn bak<'a, G, T>(g: G, dest: &'a mut T) -> impl FnOnce() + 'a -//~^ ERROR the parameter type `G` may not live long enough where G: Get<T> { diff --git a/src/test/ui/suggestions/lifetimes/missing-lifetimes-in-signature.stderr b/src/test/ui/suggestions/lifetimes/missing-lifetimes-in-signature.stderr index 789fff7acc29b..916a6c2bf12af 100644 --- a/src/test/ui/suggestions/lifetimes/missing-lifetimes-in-signature.stderr +++ b/src/test/ui/suggestions/lifetimes/missing-lifetimes-in-signature.stderr @@ -1,112 +1,11 @@ error[E0261]: use of undeclared lifetime name `'a` - --> $DIR/missing-lifetimes-in-signature.rs:37:11 + --> $DIR/missing-lifetimes-in-signature.rs:36:11 | LL | fn baz<G: 'a, T>(g: G, dest: &mut T) -> impl FnOnce() + '_ | - ^^ undeclared lifetime | | | help: consider introducing lifetime `'a` here: `'a,` -error[E0759]: `dest` has an anonymous lifetime `'_` but it needs to satisfy a `'static` lifetime requirement - --> $DIR/missing-lifetimes-in-signature.rs:19:5 - | -LL | fn foo<G, T>(g: G, dest: &mut T) -> impl FnOnce() - | ------ this data with an anonymous lifetime `'_`... -... -LL | / move || { -LL | | *dest = g.get(); -LL | | } - | |_____^ ...is captured here... - | -note: ...and is required to live as long as `'static` here - --> $DIR/missing-lifetimes-in-signature.rs:15:37 - | -LL | fn foo<G, T>(g: G, dest: &mut T) -> impl FnOnce() - | ^^^^^^^^^^^^^ -help: to declare that the `impl Trait` captures data from argument `dest`, you can add an explicit `'_` lifetime bound - | -LL | fn foo<G, T>(g: G, dest: &mut T) -> impl FnOnce() + '_ - | ^^^^ - -error[E0311]: the parameter type `G` may not live long enough - --> $DIR/missing-lifetimes-in-signature.rs:25:37 - | -LL | fn bar<G, T>(g: G, dest: &mut T) -> impl FnOnce() + '_ - | ^^^^^^^^^^^^^^^^^^ - | -note: the parameter type `G` must be valid for the anonymous lifetime defined on the function body at 25:26... - --> $DIR/missing-lifetimes-in-signature.rs:25:26 - | -LL | fn bar<G, T>(g: G, dest: &mut T) -> impl FnOnce() + '_ - | ^^^^^^ -note: ...so that the type `[closure@$DIR/missing-lifetimes-in-signature.rs:30:5: 32:6]` will meet its required lifetime bounds - --> $DIR/missing-lifetimes-in-signature.rs:25:37 - | -LL | fn bar<G, T>(g: G, dest: &mut T) -> impl FnOnce() + '_ - | ^^^^^^^^^^^^^^^^^^ -help: consider introducing an explicit lifetime bound - | -LL | fn bar<'a, G: 'a, T>(g: G, dest: &mut T) -> impl FnOnce() + '_ + 'a - | ^^^^^ ^^^^ - -error[E0311]: the parameter type `G` may not live long enough - --> $DIR/missing-lifetimes-in-signature.rs:47:45 - | -LL | fn qux<'a, G: 'a, T>(g: G, dest: &mut T) -> impl FnOnce() + '_ - | ^^^^^^^^^^^^^^^^^^ - | -note: the parameter type `G` must be valid for the anonymous lifetime defined on the function body at 47:34... - --> $DIR/missing-lifetimes-in-signature.rs:47:34 - | -LL | fn qux<'a, G: 'a, T>(g: G, dest: &mut T) -> impl FnOnce() + '_ - | ^^^^^^ -note: ...so that the type `[closure@$DIR/missing-lifetimes-in-signature.rs:52:5: 54:6]` will meet its required lifetime bounds - --> $DIR/missing-lifetimes-in-signature.rs:47:45 - | -LL | fn qux<'a, G: 'a, T>(g: G, dest: &mut T) -> impl FnOnce() + '_ - | ^^^^^^^^^^^^^^^^^^ -help: consider introducing an explicit lifetime bound - | -LL | fn qux<'b, 'a, G: 'b + 'a, T>(g: G, dest: &mut T) -> impl FnOnce() + '_ + 'b - | ^^^ ^^^^^^^ ^^^^ - -error[E0311]: the parameter type `G` may not live long enough - --> $DIR/missing-lifetimes-in-signature.rs:59:58 - | -LL | fn qux<'b, G: Get<T> + 'b, T>(g: G, dest: &mut T) -> impl FnOnce() + '_ { - | ^^^^^^^^^^^^^^^^^^ - | -note: the parameter type `G` must be valid for the anonymous lifetime defined on the method body at 59:47... - --> $DIR/missing-lifetimes-in-signature.rs:59:47 - | -LL | fn qux<'b, G: Get<T> + 'b, T>(g: G, dest: &mut T) -> impl FnOnce() + '_ { - | ^^^^^^ -note: ...so that the type `[closure@$DIR/missing-lifetimes-in-signature.rs:61:9: 63:10]` will meet its required lifetime bounds - --> $DIR/missing-lifetimes-in-signature.rs:59:58 - | -LL | fn qux<'b, G: Get<T> + 'b, T>(g: G, dest: &mut T) -> impl FnOnce() + '_ { - | ^^^^^^^^^^^^^^^^^^ -help: consider introducing an explicit lifetime bound - | -LL | fn qux<'c, 'b, G: 'c + Get<T> + 'b, T>(g: G, dest: &mut T) -> impl FnOnce() + '_ + 'c { - | ^^^ ^^^^^^^ ^^^^ - -error[E0621]: explicit lifetime required in the type of `dest` - --> $DIR/missing-lifetimes-in-signature.rs:68:45 - | -LL | fn bat<'a, G: 'a, T>(g: G, dest: &mut T) -> impl FnOnce() + '_ + 'a - | ------ ^^^^^^^^^^^^^^^^^^^^^^^ lifetime `'a` required - | | - | help: add explicit lifetime `'a` to the type of `dest`: `&'a mut T` - -error[E0309]: the parameter type `G` may not live long enough - --> $DIR/missing-lifetimes-in-signature.rs:79:44 - | -LL | fn bak<'a, G, T>(g: G, dest: &'a mut T) -> impl FnOnce() + 'a - | - ^^^^^^^^^^^^^^^^^^ ...so that the type `[closure@$DIR/missing-lifetimes-in-signature.rs:84:5: 86:6]` will meet its required lifetime bounds - | | - | help: consider adding an explicit lifetime bound...: `G: 'a` - -error: aborting due to 7 previous errors +error: aborting due to previous error -Some errors have detailed explanations: E0261, E0309, E0621, E0759. -For more information about an error, try `rustc --explain E0261`. +For more information about this error, try `rustc --explain E0261`. diff --git a/src/test/ui/suggestions/missing-lifetime-specifier.stderr b/src/test/ui/suggestions/missing-lifetime-specifier.stderr index e6cec5cbd45b9..489926ea78a9d 100644 --- a/src/test/ui/suggestions/missing-lifetime-specifier.stderr +++ b/src/test/ui/suggestions/missing-lifetime-specifier.stderr @@ -142,30 +142,6 @@ help: consider using the `'static` lifetime LL | static d: RefCell<HashMap<i32, Vec<Vec<&Tar<'static, 'static, i32>>>>> = RefCell::new(HashMap::new()); | ^^^^^^^^^^^^^^^^^ -error[E0106]: missing lifetime specifier - --> $DIR/missing-lifetime-specifier.rs:50:44 - | -LL | static f: RefCell<HashMap<i32, Vec<Vec<&Tar<'static, i32>>>>> = RefCell::new(HashMap::new()); - | ^ expected named lifetime parameter - | - = help: this function's return type contains a borrowed value, but there is no value for it to be borrowed from -help: consider using the `'static` lifetime - | -LL | static f: RefCell<HashMap<i32, Vec<Vec<&'static Tar<'static, i32>>>>> = RefCell::new(HashMap::new()); - | ^^^^^^^^ - -error[E0106]: missing lifetime specifier - --> $DIR/missing-lifetime-specifier.rs:50:44 - | -LL | static f: RefCell<HashMap<i32, Vec<Vec<&Tar<'static, i32>>>>> = RefCell::new(HashMap::new()); - | ^ expected named lifetime parameter - | - = help: this function's return type contains a borrowed value, but there is no value for it to be borrowed from -help: consider using the `'static` lifetime - | -LL | static f: RefCell<HashMap<i32, Vec<Vec<&'static Tar<'static, i32>>>>> = RefCell::new(HashMap::new()); - | ^^^^^^^^ - error[E0107]: this union takes 2 lifetime arguments but only 1 lifetime argument was supplied --> $DIR/missing-lifetime-specifier.rs:43:44 | @@ -256,6 +232,18 @@ help: add missing lifetime argument LL | static f: RefCell<HashMap<i32, Vec<Vec<&Tar<'static, 'k, i32>>>>> = RefCell::new(HashMap::new()); | ^^^^ +error[E0106]: missing lifetime specifier + --> $DIR/missing-lifetime-specifier.rs:50:44 + | +LL | static f: RefCell<HashMap<i32, Vec<Vec<&Tar<'static, i32>>>>> = RefCell::new(HashMap::new()); + | ^ expected named lifetime parameter + | + = help: this function's return type contains a borrowed value, but there is no value for it to be borrowed from +help: consider using the `'static` lifetime + | +LL | static f: RefCell<HashMap<i32, Vec<Vec<&'static Tar<'static, i32>>>>> = RefCell::new(HashMap::new()); + | ^^^^^^^^ + error[E0107]: this trait takes 2 lifetime arguments but only 1 lifetime argument was supplied --> $DIR/missing-lifetime-specifier.rs:50:45 | @@ -274,6 +262,18 @@ help: add missing lifetime argument LL | static f: RefCell<HashMap<i32, Vec<Vec<&Tar<'static, 'k, i32>>>>> = RefCell::new(HashMap::new()); | ^^^^ +error[E0106]: missing lifetime specifier + --> $DIR/missing-lifetime-specifier.rs:50:44 + | +LL | static f: RefCell<HashMap<i32, Vec<Vec<&Tar<'static, i32>>>>> = RefCell::new(HashMap::new()); + | ^ expected named lifetime parameter + | + = help: this function's return type contains a borrowed value, but there is no value for it to be borrowed from +help: consider using the `'static` lifetime + | +LL | static f: RefCell<HashMap<i32, Vec<Vec<&'static Tar<'static, i32>>>>> = RefCell::new(HashMap::new()); + | ^^^^^^^^ + error[E0107]: this trait takes 2 lifetime arguments but only 1 lifetime argument was supplied --> $DIR/missing-lifetime-specifier.rs:50:45 | diff --git a/src/test/ui/suggestions/missing-trait-bounds-for-method-call.stderr b/src/test/ui/suggestions/missing-trait-bounds-for-method-call.stderr index c7376b0007f97..0a64a0d7d5ebc 100644 --- a/src/test/ui/suggestions/missing-trait-bounds-for-method-call.stderr +++ b/src/test/ui/suggestions/missing-trait-bounds-for-method-call.stderr @@ -8,10 +8,10 @@ LL | self.foo(); | ^^^ method cannot be called on `&Foo<T>` due to unsatisfied trait bounds | = note: the following trait bounds were not satisfied: - `T: Bar` - which is required by `Foo<T>: Bar` `T: Default` which is required by `Foo<T>: Bar` + `T: Bar` + which is required by `Foo<T>: Bar` help: consider restricting the type parameters to satisfy the trait bounds | LL | struct Foo<T> where T: Bar, T: Default { diff --git a/src/test/ui/suggestions/mut-borrow-needed-by-trait.stderr b/src/test/ui/suggestions/mut-borrow-needed-by-trait.stderr index 3120b739c0295..b8ef230b44bb7 100644 --- a/src/test/ui/suggestions/mut-borrow-needed-by-trait.stderr +++ b/src/test/ui/suggestions/mut-borrow-needed-by-trait.stderr @@ -11,7 +11,7 @@ error[E0277]: the trait bound `&dyn std::io::Write: std::io::Write` is not satis --> $DIR/mut-borrow-needed-by-trait.rs:17:14 | LL | let fp = BufWriter::new(fp); - | ^^^^^^^^^^^^^^ the trait `std::io::Write` is not implemented for `&dyn std::io::Write` + | ^^^^^^^^^ the trait `std::io::Write` is not implemented for `&dyn std::io::Write` | ::: $SRC_DIR/std/src/io/buffered/bufwriter.rs:LL:COL | diff --git a/src/test/ui/suggestions/path-by-value.stderr b/src/test/ui/suggestions/path-by-value.stderr index 19fc3406ccfe4..5919a6f749205 100644 --- a/src/test/ui/suggestions/path-by-value.stderr +++ b/src/test/ui/suggestions/path-by-value.stderr @@ -9,8 +9,8 @@ LL | fn f(p: Path) { } = help: unsized fn params are gated as an unstable feature help: function arguments must have a statically known size, borrowed types always have a known size | -LL | fn f(&p: Path) { } - | ^ +LL | fn f(p: &Path) { } + | ^ error: aborting due to previous error diff --git a/src/test/ui/suggestions/path-display.stderr b/src/test/ui/suggestions/path-display.stderr index b08e22eaab7bb..3ee2860b4ffc7 100644 --- a/src/test/ui/suggestions/path-display.stderr +++ b/src/test/ui/suggestions/path-display.stderr @@ -2,10 +2,10 @@ error[E0277]: `Path` doesn't implement `std::fmt::Display` --> $DIR/path-display.rs:5:20 | LL | println!("{}", path); - | ^^^^ `Path` cannot be formatted with the default formatter + | ^^^^ `Path` cannot be formatted with the default formatter; call `.display()` on it | = help: the trait `std::fmt::Display` is not implemented for `Path` - = note: in format strings you may be able to use `{:?}` (or {:#?} for pretty-print) instead + = note: call `.display()` or `.to_string_lossy()` to safely print paths, as they may contain non-Unicode data = note: required because of the requirements on the impl of `std::fmt::Display` for `&Path` = note: required by `std::fmt::Display::fmt` = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/src/test/ui/suggestions/restrict-type-argument.stderr b/src/test/ui/suggestions/restrict-type-argument.stderr index 7a7242a6369df..33af13d943f74 100644 --- a/src/test/ui/suggestions/restrict-type-argument.stderr +++ b/src/test/ui/suggestions/restrict-type-argument.stderr @@ -9,8 +9,8 @@ LL | is_send(val); | help: consider further restricting this bound | -LL | fn use_impl_sync(val: impl Sync + Send) { - | ^^^^^^ +LL | fn use_impl_sync(val: impl Sync + std::marker::Send) { + | ^^^^^^^^^^^^^^^^^^^ error[E0277]: `S` cannot be sent between threads safely --> $DIR/restrict-type-argument.rs:8:13 @@ -23,8 +23,8 @@ LL | is_send(val); | help: consider further restricting this bound | -LL | fn use_where<S>(val: S) where S: Sync + Send { - | ^^^^^^ +LL | fn use_where<S>(val: S) where S: Sync + std::marker::Send { + | ^^^^^^^^^^^^^^^^^^^ error[E0277]: `S` cannot be sent between threads safely --> $DIR/restrict-type-argument.rs:12:13 @@ -37,8 +37,8 @@ LL | is_send(val); | help: consider further restricting this bound | -LL | fn use_bound<S: Sync + Send>(val: S) { - | ^^^^^^ +LL | fn use_bound<S: Sync + std::marker::Send>(val: S) { + | ^^^^^^^^^^^^^^^^^^^ error[E0277]: `S` cannot be sent between threads safely --> $DIR/restrict-type-argument.rs:20:13 @@ -51,8 +51,8 @@ LL | is_send(val); | help: consider further restricting this bound | -LL | Sync + Send - | ^^^^^^ +LL | Sync + std::marker::Send + | ^^^^^^^^^^^^^^^^^^^ error[E0277]: `S` cannot be sent between threads safely --> $DIR/restrict-type-argument.rs:24:13 @@ -65,8 +65,8 @@ LL | is_send(val); | help: consider further restricting this bound | -LL | fn use_bound_and_where<S: Sync>(val: S) where S: std::fmt::Debug + Send { - | ^^^^^^ +LL | fn use_bound_and_where<S: Sync>(val: S) where S: std::fmt::Debug + std::marker::Send { + | ^^^^^^^^^^^^^^^^^^^ error[E0277]: `S` cannot be sent between threads safely --> $DIR/restrict-type-argument.rs:28:13 @@ -79,8 +79,8 @@ LL | is_send(val); | help: consider restricting type parameter `S` | -LL | fn use_unbound<S: Send>(val: S) { - | ^^^^^^ +LL | fn use_unbound<S: std::marker::Send>(val: S) { + | ^^^^^^^^^^^^^^^^^^^ error: aborting due to 6 previous errors diff --git a/src/test/ui/suggestions/suggest-std-when-using-type.stderr b/src/test/ui/suggestions/suggest-std-when-using-type.stderr index 5199faa5c8ec6..7f4c80f50e267 100644 --- a/src/test/ui/suggestions/suggest-std-when-using-type.stderr +++ b/src/test/ui/suggestions/suggest-std-when-using-type.stderr @@ -2,12 +2,12 @@ error[E0223]: ambiguous associated type --> $DIR/suggest-std-when-using-type.rs:2:14 | LL | let pi = f32::consts::PI; - | ^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^ | help: you are looking for the module in `std`, not the primitive type | LL | let pi = std::f32::consts::PI; - | ^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^ error[E0599]: no function or associated item named `from_utf8` found for type `str` in the current scope --> $DIR/suggest-std-when-using-type.rs:5:14 diff --git a/src/test/ui/symbol-names/basic.legacy.stderr b/src/test/ui/symbol-names/basic.legacy.stderr index dec57e06ea24b..3dd2b19fbf987 100644 --- a/src/test/ui/symbol-names/basic.legacy.stderr +++ b/src/test/ui/symbol-names/basic.legacy.stderr @@ -1,10 +1,10 @@ -error: symbol-name(_ZN5basic4main17h4272b3de5e868f5aE) +error: symbol-name(_ZN5basic4main17h6c535bbea2051f85E) --> $DIR/basic.rs:8:1 | LL | #[rustc_symbol_name] | ^^^^^^^^^^^^^^^^^^^^ -error: demangling(basic::main::h4272b3de5e868f5a) +error: demangling(basic::main::h6c535bbea2051f85) --> $DIR/basic.rs:8:1 | LL | #[rustc_symbol_name] diff --git a/src/test/ui/symbol-names/basic.rs b/src/test/ui/symbol-names/basic.rs index ddc349d7f106d..bd107c1020707 100644 --- a/src/test/ui/symbol-names/basic.rs +++ b/src/test/ui/symbol-names/basic.rs @@ -9,8 +9,8 @@ //[legacy]~^ ERROR symbol-name(_ZN5basic4main //[legacy]~| ERROR demangling(basic::main //[legacy]~| ERROR demangling-alt(basic::main) - //[v0]~^^^^ ERROR symbol-name(_RNvCs4fqI2P2rA04_5basic4main) - //[v0]~| ERROR demangling(basic[317d481089b8c8fe]::main) + //[v0]~^^^^ ERROR symbol-name(_RNvCs21hi0yVfW1J_5basic4main) + //[v0]~| ERROR demangling(basic[17891616a171812d]::main) //[v0]~| ERROR demangling-alt(basic::main) #[rustc_def_path] //[legacy]~^ ERROR def-path(main) diff --git a/src/test/ui/symbol-names/basic.v0.stderr b/src/test/ui/symbol-names/basic.v0.stderr index 36dba0dfc975d..519efc9d7b4b9 100644 --- a/src/test/ui/symbol-names/basic.v0.stderr +++ b/src/test/ui/symbol-names/basic.v0.stderr @@ -1,10 +1,10 @@ -error: symbol-name(_RNvCs4fqI2P2rA04_5basic4main) +error: symbol-name(_RNvCs21hi0yVfW1J_5basic4main) --> $DIR/basic.rs:8:1 | LL | #[rustc_symbol_name] | ^^^^^^^^^^^^^^^^^^^^ -error: demangling(basic[317d481089b8c8fe]::main) +error: demangling(basic[17891616a171812d]::main) --> $DIR/basic.rs:8:1 | LL | #[rustc_symbol_name] diff --git a/src/test/ui/symbol-names/const-generics-demangling.rs b/src/test/ui/symbol-names/const-generics-demangling.rs index 55ab17fcd5a9a..bd7e1c0f33698 100644 --- a/src/test/ui/symbol-names/const-generics-demangling.rs +++ b/src/test/ui/symbol-names/const-generics-demangling.rs @@ -5,32 +5,32 @@ pub struct Unsigned<const F: u8>; #[rustc_symbol_name] -//~^ ERROR symbol-name(_RMCs4fqI2P2rA04_25const_generics_demanglingINtB0_8UnsignedKhb_E) -//~| ERROR demangling(<const_generics_demangling[317d481089b8c8fe]::Unsigned<11: u8>>) +//~^ ERROR symbol-name(_RMCs21hi0yVfW1J_25const_generics_demanglingINtB0_8UnsignedKhb_E) +//~| ERROR demangling(<const_generics_demangling[17891616a171812d]::Unsigned<11: u8>>) //~| ERROR demangling-alt(<const_generics_demangling::Unsigned<11>>) impl Unsigned<11> {} pub struct Signed<const F: i16>; #[rustc_symbol_name] -//~^ ERROR symbol-name(_RMs_Cs4fqI2P2rA04_25const_generics_demanglingINtB2_6SignedKsn98_E) -//~| ERROR demangling(<const_generics_demangling[317d481089b8c8fe]::Signed<-152: i16>>) +//~^ ERROR symbol-name(_RMs_Cs21hi0yVfW1J_25const_generics_demanglingINtB2_6SignedKsn98_E) +//~| ERROR demangling(<const_generics_demangling[17891616a171812d]::Signed<-152: i16>>) //~| ERROR demangling-alt(<const_generics_demangling::Signed<-152>>) impl Signed<-152> {} pub struct Bool<const F: bool>; #[rustc_symbol_name] -//~^ ERROR symbol-name(_RMs0_Cs4fqI2P2rA04_25const_generics_demanglingINtB3_4BoolKb1_E) -//~| ERROR demangling(<const_generics_demangling[317d481089b8c8fe]::Bool<true: bool>>) +//~^ ERROR symbol-name(_RMs0_Cs21hi0yVfW1J_25const_generics_demanglingINtB3_4BoolKb1_E) +//~| ERROR demangling(<const_generics_demangling[17891616a171812d]::Bool<true: bool>>) //~| ERROR demangling-alt(<const_generics_demangling::Bool<true>>) impl Bool<true> {} pub struct Char<const F: char>; #[rustc_symbol_name] -//~^ ERROR symbol-name(_RMs1_Cs4fqI2P2rA04_25const_generics_demanglingINtB3_4CharKc2202_E) -//~| ERROR demangling(<const_generics_demangling[317d481089b8c8fe]::Char<'∂': char>>) +//~^ ERROR symbol-name(_RMs1_Cs21hi0yVfW1J_25const_generics_demanglingINtB3_4CharKc2202_E) +//~| ERROR demangling(<const_generics_demangling[17891616a171812d]::Char<'∂': char>>) //~| ERROR demangling-alt(<const_generics_demangling::Char<'∂'>>) impl Char<'∂'> {} diff --git a/src/test/ui/symbol-names/const-generics-demangling.stderr b/src/test/ui/symbol-names/const-generics-demangling.stderr index a9574cacea3e7..13995403f7791 100644 --- a/src/test/ui/symbol-names/const-generics-demangling.stderr +++ b/src/test/ui/symbol-names/const-generics-demangling.stderr @@ -1,10 +1,10 @@ -error: symbol-name(_RMCs4fqI2P2rA04_25const_generics_demanglingINtB0_8UnsignedKhb_E) +error: symbol-name(_RMCs21hi0yVfW1J_25const_generics_demanglingINtB0_8UnsignedKhb_E) --> $DIR/const-generics-demangling.rs:7:1 | LL | #[rustc_symbol_name] | ^^^^^^^^^^^^^^^^^^^^ -error: demangling(<const_generics_demangling[317d481089b8c8fe]::Unsigned<11: u8>>) +error: demangling(<const_generics_demangling[17891616a171812d]::Unsigned<11: u8>>) --> $DIR/const-generics-demangling.rs:7:1 | LL | #[rustc_symbol_name] @@ -16,13 +16,13 @@ error: demangling-alt(<const_generics_demangling::Unsigned<11>>) LL | #[rustc_symbol_name] | ^^^^^^^^^^^^^^^^^^^^ -error: symbol-name(_RMs_Cs4fqI2P2rA04_25const_generics_demanglingINtB2_6SignedKsn98_E) +error: symbol-name(_RMs_Cs21hi0yVfW1J_25const_generics_demanglingINtB2_6SignedKsn98_E) --> $DIR/const-generics-demangling.rs:15:1 | LL | #[rustc_symbol_name] | ^^^^^^^^^^^^^^^^^^^^ -error: demangling(<const_generics_demangling[317d481089b8c8fe]::Signed<-152: i16>>) +error: demangling(<const_generics_demangling[17891616a171812d]::Signed<-152: i16>>) --> $DIR/const-generics-demangling.rs:15:1 | LL | #[rustc_symbol_name] @@ -34,13 +34,13 @@ error: demangling-alt(<const_generics_demangling::Signed<-152>>) LL | #[rustc_symbol_name] | ^^^^^^^^^^^^^^^^^^^^ -error: symbol-name(_RMs0_Cs4fqI2P2rA04_25const_generics_demanglingINtB3_4BoolKb1_E) +error: symbol-name(_RMs0_Cs21hi0yVfW1J_25const_generics_demanglingINtB3_4BoolKb1_E) --> $DIR/const-generics-demangling.rs:23:1 | LL | #[rustc_symbol_name] | ^^^^^^^^^^^^^^^^^^^^ -error: demangling(<const_generics_demangling[317d481089b8c8fe]::Bool<true: bool>>) +error: demangling(<const_generics_demangling[17891616a171812d]::Bool<true: bool>>) --> $DIR/const-generics-demangling.rs:23:1 | LL | #[rustc_symbol_name] @@ -52,13 +52,13 @@ error: demangling-alt(<const_generics_demangling::Bool<true>>) LL | #[rustc_symbol_name] | ^^^^^^^^^^^^^^^^^^^^ -error: symbol-name(_RMs1_Cs4fqI2P2rA04_25const_generics_demanglingINtB3_4CharKc2202_E) +error: symbol-name(_RMs1_Cs21hi0yVfW1J_25const_generics_demanglingINtB3_4CharKc2202_E) --> $DIR/const-generics-demangling.rs:31:1 | LL | #[rustc_symbol_name] | ^^^^^^^^^^^^^^^^^^^^ -error: demangling(<const_generics_demangling[317d481089b8c8fe]::Char<'∂': char>>) +error: demangling(<const_generics_demangling[17891616a171812d]::Char<'∂': char>>) --> $DIR/const-generics-demangling.rs:31:1 | LL | #[rustc_symbol_name] diff --git a/src/test/ui/symbol-names/impl1.legacy.stderr b/src/test/ui/symbol-names/impl1.legacy.stderr index c866f9bd3b791..b6012e41594b5 100644 --- a/src/test/ui/symbol-names/impl1.legacy.stderr +++ b/src/test/ui/symbol-names/impl1.legacy.stderr @@ -1,71 +1,71 @@ -error: symbol-name(_ZN5impl13foo3Foo3bar17ha318160f105e638cE) - --> $DIR/impl1.rs:16:9 +error: symbol-name(_ZN5impl13foo3Foo3bar17<SYMBOL_HASH>) + --> $DIR/impl1.rs:14:9 | LL | #[rustc_symbol_name] | ^^^^^^^^^^^^^^^^^^^^ -error: demangling(impl1::foo::Foo::bar::ha318160f105e638c) - --> $DIR/impl1.rs:16:9 +error: demangling(impl1::foo::Foo::bar::<SYMBOL_HASH>) + --> $DIR/impl1.rs:14:9 | LL | #[rustc_symbol_name] | ^^^^^^^^^^^^^^^^^^^^ error: demangling-alt(impl1::foo::Foo::bar) - --> $DIR/impl1.rs:16:9 + --> $DIR/impl1.rs:14:9 | LL | #[rustc_symbol_name] | ^^^^^^^^^^^^^^^^^^^^ error: def-path(foo::Foo::bar) - --> $DIR/impl1.rs:23:9 + --> $DIR/impl1.rs:21:9 | LL | #[rustc_def_path] | ^^^^^^^^^^^^^^^^^ -error: symbol-name(_ZN5impl13bar33_$LT$impl$u20$impl1..foo..Foo$GT$3baz17h6c2dbab6e66f9fa3E) - --> $DIR/impl1.rs:34:9 +error: symbol-name(_ZN5impl13bar33_$LT$impl$u20$impl1..foo..Foo$GT$3baz17<SYMBOL_HASH>) + --> $DIR/impl1.rs:32:9 | LL | #[rustc_symbol_name] | ^^^^^^^^^^^^^^^^^^^^ -error: demangling(impl1::bar::<impl impl1::foo::Foo>::baz::h6c2dbab6e66f9fa3) - --> $DIR/impl1.rs:34:9 +error: demangling(impl1::bar::<impl impl1::foo::Foo>::baz::<SYMBOL_HASH>) + --> $DIR/impl1.rs:32:9 | LL | #[rustc_symbol_name] | ^^^^^^^^^^^^^^^^^^^^ error: demangling-alt(impl1::bar::<impl impl1::foo::Foo>::baz) - --> $DIR/impl1.rs:34:9 + --> $DIR/impl1.rs:32:9 | LL | #[rustc_symbol_name] | ^^^^^^^^^^^^^^^^^^^^ error: def-path(bar::<impl foo::Foo>::baz) - --> $DIR/impl1.rs:41:9 + --> $DIR/impl1.rs:39:9 | LL | #[rustc_def_path] | ^^^^^^^^^^^^^^^^^ -error: symbol-name(_ZN209_$LT$$u5b$$RF$dyn$u20$impl1..Foo$u2b$Assoc$u20$$u3d$$u20$extern$u20$$u22$C$u22$$u20$fn$LP$$RF$u8$C$$u20$...$RP$$u2b$impl1..AutoTrait$u3b$$u20$3$u5d$$u20$as$u20$impl1..main..$u7b$$u7b$closure$u7d$$u7d$..Bar$GT$6method17SYMBOL_HASHE) - --> $DIR/impl1.rs:64:13 +error: symbol-name(_ZN209_$LT$$u5b$$RF$dyn$u20$impl1..Foo$u2b$Assoc$u20$$u3d$$u20$extern$u20$$u22$C$u22$$u20$fn$LP$$RF$u8$C$$u20$...$RP$$u2b$impl1..AutoTrait$u3b$$u20$3$u5d$$u20$as$u20$impl1..main..$u7b$$u7b$closure$u7d$$u7d$..Bar$GT$6method17<SYMBOL_HASH>) + --> $DIR/impl1.rs:62:13 | LL | #[rustc_symbol_name] | ^^^^^^^^^^^^^^^^^^^^ -error: demangling(<[&dyn impl1::Foo+Assoc = extern "C" fn(&u8, ::.)+impl1::AutoTrait; 3] as impl1::main::{{closure}}::Bar>::method::SYMBOL_HASH) - --> $DIR/impl1.rs:64:13 +error: demangling(<[&dyn impl1::Foo+Assoc = extern "C" fn(&u8, ::.)+impl1::AutoTrait; 3] as impl1::main::{{closure}}::Bar>::method::<SYMBOL_HASH>) + --> $DIR/impl1.rs:62:13 | LL | #[rustc_symbol_name] | ^^^^^^^^^^^^^^^^^^^^ error: demangling-alt(<[&dyn impl1::Foo+Assoc = extern "C" fn(&u8, ::.)+impl1::AutoTrait; 3] as impl1::main::{{closure}}::Bar>::method) - --> $DIR/impl1.rs:64:13 + --> $DIR/impl1.rs:62:13 | LL | #[rustc_symbol_name] | ^^^^^^^^^^^^^^^^^^^^ error: def-path(<[&dyn Foo<Assoc = for<'r> extern "C" fn(&'r u8, ...)> + AutoTrait; 3] as main::{closure#1}::Bar>::method) - --> $DIR/impl1.rs:71:13 + --> $DIR/impl1.rs:69:13 | LL | #[rustc_def_path] | ^^^^^^^^^^^^^^^^^ diff --git a/src/test/ui/symbol-names/impl1.rs b/src/test/ui/symbol-names/impl1.rs index 24bdf6d669e88..b0b31a57d0692 100644 --- a/src/test/ui/symbol-names/impl1.rs +++ b/src/test/ui/symbol-names/impl1.rs @@ -1,10 +1,8 @@ // build-fail -// ignore-tidy-linelength // revisions: legacy v0 //[legacy]compile-flags: -Z symbol-mangling-version=legacy //[v0]compile-flags: -Z symbol-mangling-version=v0 -//[legacy]normalize-stderr-32bit: "hee444285569b39c2" -> "SYMBOL_HASH" -//[legacy]normalize-stderr-64bit: "h310ea0259fc3d32d" -> "SYMBOL_HASH" +//[legacy]normalize-stderr-test: "h[\w]{16}E?\)" -> "<SYMBOL_HASH>)" #![feature(auto_traits, rustc_attrs)] #![allow(dead_code)] @@ -17,8 +15,8 @@ mod foo { //[legacy]~^ ERROR symbol-name(_ZN5impl13foo3Foo3bar //[legacy]~| ERROR demangling(impl1::foo::Foo::bar //[legacy]~| ERROR demangling-alt(impl1::foo::Foo::bar) - //[v0]~^^^^ ERROR symbol-name(_RNvMNtCs4fqI2P2rA04_5impl13fooNtB2_3Foo3bar) - //[v0]~| ERROR demangling(<impl1[317d481089b8c8fe]::foo::Foo>::bar) + //[v0]~^^^^ ERROR symbol-name(_RNvMNtCs21hi0yVfW1J_5impl13fooNtB2_3Foo3bar) + //[v0]~| ERROR demangling(<impl1[17891616a171812d]::foo::Foo>::bar) //[v0]~| ERROR demangling-alt(<impl1::foo::Foo>::bar) #[rustc_def_path] //[legacy]~^ ERROR def-path(foo::Foo::bar) @@ -35,8 +33,8 @@ mod bar { //[legacy]~^ ERROR symbol-name(_ZN5impl13bar33_$LT$impl$u20$impl1..foo..Foo$GT$3baz //[legacy]~| ERROR demangling(impl1::bar::<impl impl1::foo::Foo>::baz //[legacy]~| ERROR demangling-alt(impl1::bar::<impl impl1::foo::Foo>::baz) - //[v0]~^^^^ ERROR symbol-name(_RNvMNtCs4fqI2P2rA04_5impl13barNtNtB4_3foo3Foo3baz) - //[v0]~| ERROR demangling(<impl1[317d481089b8c8fe]::foo::Foo>::baz) + //[v0]~^^^^ ERROR symbol-name(_RNvMNtCs21hi0yVfW1J_5impl13barNtNtB4_3foo3Foo3baz) + //[v0]~| ERROR demangling(<impl1[17891616a171812d]::foo::Foo>::baz) //[v0]~| ERROR demangling-alt(<impl1::foo::Foo>::baz) #[rustc_def_path] //[legacy]~^ ERROR def-path(bar::<impl foo::Foo>::baz) @@ -65,8 +63,8 @@ fn main() { //[legacy]~^ ERROR symbol-name(_ZN209_$LT$$u5b$$RF$dyn$u20$impl1..Foo$u2b$Assoc$u20$$u3d$$u20$extern$u20$$u22$C$u22$$u20$fn$LP$$RF$u8$C$$u20$...$RP$$u2b$impl1..AutoTrait$u3b$$u20$3$u5d$$u20$as$u20$impl1..main..$u7b$$u7b$closure$u7d$$u7d$..Bar$GT$6method //[legacy]~| ERROR demangling(<[&dyn impl1::Foo+Assoc = extern "C" fn(&u8, ::.)+impl1::AutoTrait; 3] as impl1::main::{{closure}}::Bar>::method //[legacy]~| ERROR demangling-alt(<[&dyn impl1::Foo+Assoc = extern "C" fn(&u8, ::.)+impl1::AutoTrait; 3] as impl1::main::{{closure}}::Bar>::method) - //[v0]~^^^^ ERROR symbol-name(_RNvXNCNvCs4fqI2P2rA04_5impl14mains_0ARDNtB6_3Foop5AssocFG_KCRL0_hvEuNtB6_9AutoTraitEL_j3_NtB2_3Bar6method) - //[v0]~| ERROR demangling(<[&dyn impl1[317d481089b8c8fe]::Foo<Assoc = for<'a> extern "C" fn(&'a u8, ...)> + impl1[317d481089b8c8fe]::AutoTrait; 3: usize] as impl1[317d481089b8c8fe]::main::{closure#1}::Bar>::method) + //[v0]~^^^^ ERROR symbol-name(_RNvXNCNvCs21hi0yVfW1J_5impl14mains_0ARDNtB6_3Foop5AssocFG_KCRL0_hvEuNtB6_9AutoTraitEL_j3_NtB2_3Bar6method) + //[v0]~| ERROR demangling(<[&dyn impl1[17891616a171812d]::Foo<Assoc = for<'a> extern "C" fn(&'a u8, ...)> + impl1[17891616a171812d]::AutoTrait; 3: usize] as impl1[17891616a171812d]::main::{closure#1}::Bar>::method) //[v0]~| ERROR demangling-alt(<[&dyn impl1::Foo<Assoc = for<'a> extern "C" fn(&'a u8, ...)> + impl1::AutoTrait; 3] as impl1::main::{closure#1}::Bar>::method) #[rustc_def_path] //[legacy]~^ ERROR def-path(<[&dyn Foo<Assoc = for<'r> extern "C" fn(&'r u8, ...)> + AutoTrait; 3] as main::{closure#1}::Bar>::method) diff --git a/src/test/ui/symbol-names/impl1.stderr b/src/test/ui/symbol-names/impl1.stderr deleted file mode 100644 index 20e48782a3a9e..0000000000000 --- a/src/test/ui/symbol-names/impl1.stderr +++ /dev/null @@ -1,26 +0,0 @@ -error: symbol-name(_ZN5impl13foo3Foo3bar17he53b9bee7600ed8dE) - --> $DIR/impl1.rs:8:9 - | -LL | #[rustc_symbol_name] - | ^^^^^^^^^^^^^^^^^^^^ - -error: def-path(foo::Foo::bar) - --> $DIR/impl1.rs:9:9 - | -LL | #[rustc_def_path] - | ^^^^^^^^^^^^^^^^^ - -error: symbol-name(_ZN5impl13bar33_$LT$impl$u20$impl1..foo..Foo$GT$3baz17h86c41f0462d901d4E) - --> $DIR/impl1.rs:18:9 - | -LL | #[rustc_symbol_name] - | ^^^^^^^^^^^^^^^^^^^^ - -error: def-path(bar::<impl foo::Foo>::baz) - --> $DIR/impl1.rs:19:9 - | -LL | #[rustc_def_path] - | ^^^^^^^^^^^^^^^^^ - -error: aborting due to 4 previous errors - diff --git a/src/test/ui/symbol-names/impl1.v0.stderr b/src/test/ui/symbol-names/impl1.v0.stderr index db5eda0db5074..e5b0deee36e3c 100644 --- a/src/test/ui/symbol-names/impl1.v0.stderr +++ b/src/test/ui/symbol-names/impl1.v0.stderr @@ -1,71 +1,71 @@ -error: symbol-name(_RNvMNtCs4fqI2P2rA04_5impl13fooNtB2_3Foo3bar) - --> $DIR/impl1.rs:16:9 +error: symbol-name(_RNvMNtCs21hi0yVfW1J_5impl13fooNtB2_3Foo3bar) + --> $DIR/impl1.rs:14:9 | LL | #[rustc_symbol_name] | ^^^^^^^^^^^^^^^^^^^^ -error: demangling(<impl1[317d481089b8c8fe]::foo::Foo>::bar) - --> $DIR/impl1.rs:16:9 +error: demangling(<impl1[17891616a171812d]::foo::Foo>::bar) + --> $DIR/impl1.rs:14:9 | LL | #[rustc_symbol_name] | ^^^^^^^^^^^^^^^^^^^^ error: demangling-alt(<impl1::foo::Foo>::bar) - --> $DIR/impl1.rs:16:9 + --> $DIR/impl1.rs:14:9 | LL | #[rustc_symbol_name] | ^^^^^^^^^^^^^^^^^^^^ error: def-path(foo::Foo::bar) - --> $DIR/impl1.rs:23:9 + --> $DIR/impl1.rs:21:9 | LL | #[rustc_def_path] | ^^^^^^^^^^^^^^^^^ -error: symbol-name(_RNvMNtCs4fqI2P2rA04_5impl13barNtNtB4_3foo3Foo3baz) - --> $DIR/impl1.rs:34:9 +error: symbol-name(_RNvMNtCs21hi0yVfW1J_5impl13barNtNtB4_3foo3Foo3baz) + --> $DIR/impl1.rs:32:9 | LL | #[rustc_symbol_name] | ^^^^^^^^^^^^^^^^^^^^ -error: demangling(<impl1[317d481089b8c8fe]::foo::Foo>::baz) - --> $DIR/impl1.rs:34:9 +error: demangling(<impl1[17891616a171812d]::foo::Foo>::baz) + --> $DIR/impl1.rs:32:9 | LL | #[rustc_symbol_name] | ^^^^^^^^^^^^^^^^^^^^ error: demangling-alt(<impl1::foo::Foo>::baz) - --> $DIR/impl1.rs:34:9 + --> $DIR/impl1.rs:32:9 | LL | #[rustc_symbol_name] | ^^^^^^^^^^^^^^^^^^^^ error: def-path(bar::<impl foo::Foo>::baz) - --> $DIR/impl1.rs:41:9 + --> $DIR/impl1.rs:39:9 | LL | #[rustc_def_path] | ^^^^^^^^^^^^^^^^^ -error: symbol-name(_RNvXNCNvCs4fqI2P2rA04_5impl14mains_0ARDNtB6_3Foop5AssocFG_KCRL0_hvEuNtB6_9AutoTraitEL_j3_NtB2_3Bar6method) - --> $DIR/impl1.rs:64:13 +error: symbol-name(_RNvXNCNvCs21hi0yVfW1J_5impl14mains_0ARDNtB6_3Foop5AssocFG_KCRL0_hvEuNtB6_9AutoTraitEL_j3_NtB2_3Bar6method) + --> $DIR/impl1.rs:62:13 | LL | #[rustc_symbol_name] | ^^^^^^^^^^^^^^^^^^^^ -error: demangling(<[&dyn impl1[317d481089b8c8fe]::Foo<Assoc = for<'a> extern "C" fn(&'a u8, ...)> + impl1[317d481089b8c8fe]::AutoTrait; 3: usize] as impl1[317d481089b8c8fe]::main::{closure#1}::Bar>::method) - --> $DIR/impl1.rs:64:13 +error: demangling(<[&dyn impl1[17891616a171812d]::Foo<Assoc = for<'a> extern "C" fn(&'a u8, ...)> + impl1[17891616a171812d]::AutoTrait; 3: usize] as impl1[17891616a171812d]::main::{closure#1}::Bar>::method) + --> $DIR/impl1.rs:62:13 | LL | #[rustc_symbol_name] | ^^^^^^^^^^^^^^^^^^^^ error: demangling-alt(<[&dyn impl1::Foo<Assoc = for<'a> extern "C" fn(&'a u8, ...)> + impl1::AutoTrait; 3] as impl1::main::{closure#1}::Bar>::method) - --> $DIR/impl1.rs:64:13 + --> $DIR/impl1.rs:62:13 | LL | #[rustc_symbol_name] | ^^^^^^^^^^^^^^^^^^^^ error: def-path(<[&dyn Foo<Assoc = for<'r> extern "C" fn(&'r u8, ...)> + AutoTrait; 3] as main::{closure#1}::Bar>::method) - --> $DIR/impl1.rs:71:13 + --> $DIR/impl1.rs:69:13 | LL | #[rustc_def_path] | ^^^^^^^^^^^^^^^^^ diff --git a/src/test/ui/symbol-names/issue-60925.legacy.stderr b/src/test/ui/symbol-names/issue-60925.legacy.stderr index f5699052795b8..8357678399235 100644 --- a/src/test/ui/symbol-names/issue-60925.legacy.stderr +++ b/src/test/ui/symbol-names/issue-60925.legacy.stderr @@ -1,17 +1,17 @@ -error: symbol-name(_ZN11issue_609253foo37Foo$LT$issue_60925..llv$u6d$..Foo$GT$3foo17h79d9aaa05f4b26d6E) - --> $DIR/issue-60925.rs:22:9 +error: symbol-name(_ZN11issue_609253foo37Foo$LT$issue_60925..llv$u6d$..Foo$GT$3foo17h6244e5288326926aE) + --> $DIR/issue-60925.rs:21:9 | LL | #[rustc_symbol_name] | ^^^^^^^^^^^^^^^^^^^^ -error: demangling(issue_60925::foo::Foo<issue_60925::llvm::Foo>::foo::h79d9aaa05f4b26d6) - --> $DIR/issue-60925.rs:22:9 +error: demangling(issue_60925::foo::Foo<issue_60925::llvm::Foo>::foo::h6244e5288326926a) + --> $DIR/issue-60925.rs:21:9 | LL | #[rustc_symbol_name] | ^^^^^^^^^^^^^^^^^^^^ error: demangling-alt(issue_60925::foo::Foo<issue_60925::llvm::Foo>::foo) - --> $DIR/issue-60925.rs:22:9 + --> $DIR/issue-60925.rs:21:9 | LL | #[rustc_symbol_name] | ^^^^^^^^^^^^^^^^^^^^ diff --git a/src/test/ui/symbol-names/issue-60925.rs b/src/test/ui/symbol-names/issue-60925.rs index 55b7041935c23..3238eb1e579f0 100644 --- a/src/test/ui/symbol-names/issue-60925.rs +++ b/src/test/ui/symbol-names/issue-60925.rs @@ -1,5 +1,4 @@ // build-fail -// ignore-tidy-linelength // revisions: legacy v0 //[legacy]compile-flags: -Z symbol-mangling-version=legacy //[v0]compile-flags: -Z symbol-mangling-version=v0 @@ -23,8 +22,8 @@ mod foo { //[legacy]~^ ERROR symbol-name(_ZN11issue_609253foo37Foo$LT$issue_60925..llv$u6d$..Foo$GT$3foo //[legacy]~| ERROR demangling(issue_60925::foo::Foo<issue_60925::llvm::Foo>::foo //[legacy]~| ERROR demangling-alt(issue_60925::foo::Foo<issue_60925::llvm::Foo>::foo) - //[v0]~^^^^ ERROR symbol-name(_RNvMNtCs4fqI2P2rA04_11issue_609253fooINtB2_3FooNtNtB4_4llvm3FooE3foo) - //[v0]~| ERROR demangling(<issue_60925[317d481089b8c8fe]::foo::Foo<issue_60925[317d481089b8c8fe]::llvm::Foo>>::foo) + //[v0]~^^^^ ERROR symbol-name(_RNvMNtCs21hi0yVfW1J_11issue_609253fooINtB2_3FooNtNtB4_4llvm3FooE3foo) + //[v0]~| ERROR demangling(<issue_60925[17891616a171812d]::foo::Foo<issue_60925[17891616a171812d]::llvm::Foo>>::foo) //[v0]~| ERROR demangling-alt(<issue_60925::foo::Foo<issue_60925::llvm::Foo>>::foo) pub(crate) fn foo() { for _ in 0..0 { diff --git a/src/test/ui/symbol-names/issue-60925.stderr b/src/test/ui/symbol-names/issue-60925.stderr deleted file mode 100644 index ae753f0cebbcd..0000000000000 --- a/src/test/ui/symbol-names/issue-60925.stderr +++ /dev/null @@ -1,20 +0,0 @@ -error: symbol-name(_ZN11issue_609253foo36Foo$LT$issue_60925..llv$6d$..Foo$GT$3foo17h059a991a004536adE) - --> $DIR/issue-60925.rs:16:9 - | -LL | #[rustc_symbol_name] - | ^^^^^^^^^^^^^^^^^^^^ - -error: demangling(issue_60925::foo::Foo<issue_60925::llv$6d$..Foo$GT$::foo::h059a991a004536ad) - --> $DIR/issue-60925.rs:16:9 - | -LL | #[rustc_symbol_name] - | ^^^^^^^^^^^^^^^^^^^^ - -error: demangling-alt(issue_60925::foo::Foo<issue_60925::llv$6d$..Foo$GT$::foo) - --> $DIR/issue-60925.rs:16:9 - | -LL | #[rustc_symbol_name] - | ^^^^^^^^^^^^^^^^^^^^ - -error: aborting due to 3 previous errors - diff --git a/src/test/ui/symbol-names/issue-60925.v0.stderr b/src/test/ui/symbol-names/issue-60925.v0.stderr index 78594b88b455d..6a5885e1ea32b 100644 --- a/src/test/ui/symbol-names/issue-60925.v0.stderr +++ b/src/test/ui/symbol-names/issue-60925.v0.stderr @@ -1,17 +1,17 @@ -error: symbol-name(_RNvMNtCs4fqI2P2rA04_11issue_609253fooINtB2_3FooNtNtB4_4llvm3FooE3foo) - --> $DIR/issue-60925.rs:22:9 +error: symbol-name(_RNvMNtCs21hi0yVfW1J_11issue_609253fooINtB2_3FooNtNtB4_4llvm3FooE3foo) + --> $DIR/issue-60925.rs:21:9 | LL | #[rustc_symbol_name] | ^^^^^^^^^^^^^^^^^^^^ -error: demangling(<issue_60925[317d481089b8c8fe]::foo::Foo<issue_60925[317d481089b8c8fe]::llvm::Foo>>::foo) - --> $DIR/issue-60925.rs:22:9 +error: demangling(<issue_60925[17891616a171812d]::foo::Foo<issue_60925[17891616a171812d]::llvm::Foo>>::foo) + --> $DIR/issue-60925.rs:21:9 | LL | #[rustc_symbol_name] | ^^^^^^^^^^^^^^^^^^^^ error: demangling-alt(<issue_60925::foo::Foo<issue_60925::llvm::Foo>>::foo) - --> $DIR/issue-60925.rs:22:9 + --> $DIR/issue-60925.rs:21:9 | LL | #[rustc_symbol_name] | ^^^^^^^^^^^^^^^^^^^^ diff --git a/src/test/ui/symbol-names/issue-75326.legacy.stderr b/src/test/ui/symbol-names/issue-75326.legacy.stderr index 5f822f6660c09..aadc0cf43a212 100644 --- a/src/test/ui/symbol-names/issue-75326.legacy.stderr +++ b/src/test/ui/symbol-names/issue-75326.legacy.stderr @@ -1,17 +1,17 @@ error: symbol-name(_ZN72_$LT$issue_75326..Foo$LT$I$C$E$GT$$u20$as$u20$issue_75326..Iterator2$GT$4next17SYMBOL_HASH) - --> $DIR/issue-75326.rs:43:5 + --> $DIR/issue-75326.rs:41:5 | LL | #[rustc_symbol_name] | ^^^^^^^^^^^^^^^^^^^^ error: demangling(<issue_75326::Foo<I,E> as issue_75326::Iterator2>::next::SYMBOL_HASH) - --> $DIR/issue-75326.rs:43:5 + --> $DIR/issue-75326.rs:41:5 | LL | #[rustc_symbol_name] | ^^^^^^^^^^^^^^^^^^^^ error: demangling-alt(<issue_75326::Foo<I,E> as issue_75326::Iterator2>::next) - --> $DIR/issue-75326.rs:43:5 + --> $DIR/issue-75326.rs:41:5 | LL | #[rustc_symbol_name] | ^^^^^^^^^^^^^^^^^^^^ diff --git a/src/test/ui/symbol-names/issue-75326.rs b/src/test/ui/symbol-names/issue-75326.rs index ce315164cefd3..4d061cafef3b2 100644 --- a/src/test/ui/symbol-names/issue-75326.rs +++ b/src/test/ui/symbol-names/issue-75326.rs @@ -1,10 +1,8 @@ // build-fail -// ignore-tidy-linelength // revisions: legacy v0 //[legacy]compile-flags: -Z symbol-mangling-version=legacy //[v0]compile-flags: -Z symbol-mangling-version=v0 -//[legacy]normalize-stderr-32bit: "h[\d\w]+" -> "SYMBOL_HASH" -//[legacy]normalize-stderr-64bit: "h[\d\w]+" -> "SYMBOL_HASH" +//[legacy]normalize-stderr-test: "h[\w{16}]+" -> "SYMBOL_HASH" #![feature(rustc_attrs)] @@ -44,8 +42,8 @@ where //[legacy]~^ ERROR symbol-name(_ZN72_$LT$issue_75326..Foo$LT$I$C$E$GT$$u20$as$u20$issue_75326..Iterator2$GT$4next //[legacy]~| ERROR demangling(<issue_75326::Foo<I,E> as issue_75326::Iterator2>::next //[legacy]~| ERROR demangling-alt(<issue_75326::Foo<I,E> as issue_75326::Iterator2>::next) - //[v0]~^^^^ ERROR symbol-name(_RNvXINICs4fqI2P2rA04_11issue_75326s_0pppEINtB5_3FooppENtB5_9Iterator24nextB5_) - //[v0]~| ERROR demangling(<issue_75326[317d481089b8c8fe]::Foo<_, _> as issue_75326[317d481089b8c8fe]::Iterator2>::next) + //[v0]~^^^^ ERROR symbol-name(_RNvXINICs21hi0yVfW1J_11issue_75326s_0pppEINtB5_3FooppENtB5_9Iterator24nextB5_) + //[v0]~| ERROR demangling(<issue_75326[17891616a171812d]::Foo<_, _> as issue_75326[17891616a171812d]::Iterator2>::next) //[v0]~| ERROR demangling-alt(<issue_75326::Foo<_, _> as issue_75326::Iterator2>::next) fn next(&mut self) -> Option<Self::Item> { self.find(|_| true) diff --git a/src/test/ui/symbol-names/issue-75326.v0.stderr b/src/test/ui/symbol-names/issue-75326.v0.stderr index 59bdfa8ca3681..98844aafb6552 100644 --- a/src/test/ui/symbol-names/issue-75326.v0.stderr +++ b/src/test/ui/symbol-names/issue-75326.v0.stderr @@ -1,17 +1,17 @@ -error: symbol-name(_RNvXINICs4fqI2P2rA04_11issue_75326s_0pppEINtB5_3FooppENtB5_9Iterator24nextB5_) - --> $DIR/issue-75326.rs:43:5 +error: symbol-name(_RNvXINICs21hi0yVfW1J_11issue_75326s_0pppEINtB5_3FooppENtB5_9Iterator24nextB5_) + --> $DIR/issue-75326.rs:41:5 | LL | #[rustc_symbol_name] | ^^^^^^^^^^^^^^^^^^^^ -error: demangling(<issue_75326[317d481089b8c8fe]::Foo<_, _> as issue_75326[317d481089b8c8fe]::Iterator2>::next) - --> $DIR/issue-75326.rs:43:5 +error: demangling(<issue_75326[17891616a171812d]::Foo<_, _> as issue_75326[17891616a171812d]::Iterator2>::next) + --> $DIR/issue-75326.rs:41:5 | LL | #[rustc_symbol_name] | ^^^^^^^^^^^^^^^^^^^^ error: demangling-alt(<issue_75326::Foo<_, _> as issue_75326::Iterator2>::next) - --> $DIR/issue-75326.rs:43:5 + --> $DIR/issue-75326.rs:41:5 | LL | #[rustc_symbol_name] | ^^^^^^^^^^^^^^^^^^^^ diff --git a/src/test/ui/test-attrs/test-main-not-dead-attr.rs b/src/test/ui/test-attrs/test-main-not-dead-attr.rs index 628b1896ace4f..0b2a9a3541b39 100644 --- a/src/test/ui/test-attrs/test-main-not-dead-attr.rs +++ b/src/test/ui/test-attrs/test-main-not-dead-attr.rs @@ -1,9 +1,9 @@ // run-pass // compile-flags: --test -#![feature(main)] +#![feature(rustc_attrs)] #![deny(dead_code)] -#[main] +#[rustc_main] fn foo() { panic!(); } diff --git a/src/test/ui/test-attrs/test-runner-hides-buried-main.rs b/src/test/ui/test-attrs/test-runner-hides-buried-main.rs index bf5482056d4b4..346aa868eb467 100644 --- a/src/test/ui/test-attrs/test-runner-hides-buried-main.rs +++ b/src/test/ui/test-attrs/test-runner-hides-buried-main.rs @@ -1,14 +1,14 @@ // run-pass // compile-flags: --test -#![feature(main)] +#![feature(rustc_attrs)] #![allow(dead_code)] mod a { fn b() { (|| { - #[main] + #[rustc_main] fn c() { panic!(); } })(); } diff --git a/src/test/ui/tool_lints.rs b/src/test/ui/tool_lints.rs index 9c8540eede792..9e4aa7a939acb 100644 --- a/src/test/ui/tool_lints.rs +++ b/src/test/ui/tool_lints.rs @@ -1,5 +1,5 @@ #[warn(foo::bar)] -//~^ ERROR an unknown tool name found in scoped lint: `foo::bar` -//~| ERROR an unknown tool name found in scoped lint: `foo::bar` -//~| ERROR an unknown tool name found in scoped lint: `foo::bar` +//~^ ERROR unknown tool name `foo` found in scoped lint: `foo::bar` +//~| ERROR unknown tool name `foo` found in scoped lint: `foo::bar` +//~| ERROR unknown tool name `foo` found in scoped lint: `foo::bar` fn main() {} diff --git a/src/test/ui/tool_lints.stderr b/src/test/ui/tool_lints.stderr index 1bcd7fd735de8..e06f6ddc1ca3a 100644 --- a/src/test/ui/tool_lints.stderr +++ b/src/test/ui/tool_lints.stderr @@ -1,20 +1,26 @@ -error[E0710]: an unknown tool name found in scoped lint: `foo::bar` +error[E0710]: unknown tool name `foo` found in scoped lint: `foo::bar` --> $DIR/tool_lints.rs:1:8 | LL | #[warn(foo::bar)] | ^^^ + | + = help: add `#![register_tool(foo)]` to the crate root -error[E0710]: an unknown tool name found in scoped lint: `foo::bar` +error[E0710]: unknown tool name `foo` found in scoped lint: `foo::bar` --> $DIR/tool_lints.rs:1:8 | LL | #[warn(foo::bar)] | ^^^ + | + = help: add `#![register_tool(foo)]` to the crate root -error[E0710]: an unknown tool name found in scoped lint: `foo::bar` +error[E0710]: unknown tool name `foo` found in scoped lint: `foo::bar` --> $DIR/tool_lints.rs:1:8 | LL | #[warn(foo::bar)] | ^^^ + | + = help: add `#![register_tool(foo)]` to the crate root error: aborting due to 3 previous errors diff --git a/src/test/ui/trait-impl-bound-suggestions.fixed b/src/test/ui/trait-impl-bound-suggestions.fixed index db3a95f5c4f61..744e7bef04e97 100644 --- a/src/test/ui/trait-impl-bound-suggestions.fixed +++ b/src/test/ui/trait-impl-bound-suggestions.fixed @@ -10,7 +10,7 @@ struct ConstrainedStruct<X: Copy> { } #[allow(dead_code)] -trait InsufficientlyConstrainedGeneric<X=()> where X: Copy { +trait InsufficientlyConstrainedGeneric<X=()> where X: std::marker::Copy { fn return_the_constrained_type(&self, x: X) -> ConstrainedStruct<X> { //~^ ERROR the trait bound `X: Copy` is not satisfied ConstrainedStruct { x } diff --git a/src/test/ui/trait-impl-bound-suggestions.stderr b/src/test/ui/trait-impl-bound-suggestions.stderr index 3a21e9c6b2ad4..110ca79908079 100644 --- a/src/test/ui/trait-impl-bound-suggestions.stderr +++ b/src/test/ui/trait-impl-bound-suggestions.stderr @@ -9,8 +9,8 @@ LL | fn return_the_constrained_type(&self, x: X) -> ConstrainedStruct<X> { | help: consider further restricting type parameter `X` | -LL | trait InsufficientlyConstrainedGeneric<X=()> where X: Copy { - | ^^^^^^^^^^^^^ +LL | trait InsufficientlyConstrainedGeneric<X=()> where X: std::marker::Copy { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ error: aborting due to previous error diff --git a/src/test/ui/traits/alias/issue-83613.rs b/src/test/ui/traits/alias/issue-83613.rs new file mode 100644 index 0000000000000..146920ac685bb --- /dev/null +++ b/src/test/ui/traits/alias/issue-83613.rs @@ -0,0 +1,13 @@ +#![feature(min_type_alias_impl_trait)] +trait OpaqueTrait {} +impl<T> OpaqueTrait for T {} +type OpaqueType = impl OpaqueTrait; +fn mk_opaque() -> OpaqueType { + || 0 +} +trait AnotherTrait {} +impl<T: Send> AnotherTrait for T {} +impl AnotherTrait for OpaqueType {} +//~^ ERROR conflicting implementations of trait `AnotherTrait` for type `impl OpaqueTrait` +//~| ERROR cannot implement trait on type alias impl trait +fn main() {} diff --git a/src/test/ui/traits/alias/issue-83613.stderr b/src/test/ui/traits/alias/issue-83613.stderr new file mode 100644 index 0000000000000..0ab39ae667288 --- /dev/null +++ b/src/test/ui/traits/alias/issue-83613.stderr @@ -0,0 +1,23 @@ +error[E0119]: conflicting implementations of trait `AnotherTrait` for type `impl OpaqueTrait` + --> $DIR/issue-83613.rs:10:1 + | +LL | impl<T: Send> AnotherTrait for T {} + | -------------------------------- first implementation here +LL | impl AnotherTrait for OpaqueType {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `impl OpaqueTrait` + +error: cannot implement trait on type alias impl trait + --> $DIR/issue-83613.rs:10:1 + | +LL | impl AnotherTrait for OpaqueType {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: type alias impl trait defined here + --> $DIR/issue-83613.rs:4:19 + | +LL | type OpaqueType = impl OpaqueTrait; + | ^^^^^^^^^^^^^^^^ + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0119`. diff --git a/src/test/ui/traits/alias/suggest-trait-alias-instead-of-type.fixed b/src/test/ui/traits/alias/suggest-trait-alias-instead-of-type.fixed new file mode 100644 index 0000000000000..8a94abaeb0744 --- /dev/null +++ b/src/test/ui/traits/alias/suggest-trait-alias-instead-of-type.fixed @@ -0,0 +1,13 @@ +// Regression test of #43913. + +// run-rustfix + +#![feature(trait_alias)] +#![allow(bare_trait_objects, dead_code)] + +trait Strings = Iterator<Item=String>; + +struct Struct<S: Strings>(S); +//~^ ERROR: expected trait, found type alias `Strings` + +fn main() {} diff --git a/src/test/ui/traits/alias/suggest-trait-alias-instead-of-type.rs b/src/test/ui/traits/alias/suggest-trait-alias-instead-of-type.rs new file mode 100644 index 0000000000000..40c678c281f4c --- /dev/null +++ b/src/test/ui/traits/alias/suggest-trait-alias-instead-of-type.rs @@ -0,0 +1,13 @@ +// Regression test of #43913. + +// run-rustfix + +#![feature(trait_alias)] +#![allow(bare_trait_objects, dead_code)] + +type Strings = Iterator<Item=String>; + +struct Struct<S: Strings>(S); +//~^ ERROR: expected trait, found type alias `Strings` + +fn main() {} diff --git a/src/test/ui/traits/alias/suggest-trait-alias-instead-of-type.stderr b/src/test/ui/traits/alias/suggest-trait-alias-instead-of-type.stderr new file mode 100644 index 0000000000000..6e03eeada499c --- /dev/null +++ b/src/test/ui/traits/alias/suggest-trait-alias-instead-of-type.stderr @@ -0,0 +1,14 @@ +error[E0404]: expected trait, found type alias `Strings` + --> $DIR/suggest-trait-alias-instead-of-type.rs:10:18 + | +LL | struct Struct<S: Strings>(S); + | ^^^^^^^ type aliases cannot be used as traits + | +help: you might have meant to use `#![feature(trait_alias)]` instead of a `type` alias + | +LL | trait Strings = Iterator<Item=String>; + | + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0404`. diff --git a/src/test/ui/traits/associated_type_bound/issue-51446.rs b/src/test/ui/traits/associated_type_bound/issue-51446.rs new file mode 100644 index 0000000000000..7dd95de73ba41 --- /dev/null +++ b/src/test/ui/traits/associated_type_bound/issue-51446.rs @@ -0,0 +1,34 @@ +// Regression test for #51446. +// check-pass + +trait Foo { + type Item; + fn get(&self) -> Self::Item; +} + +fn blah<T, F>(x: T, f: F) -> B<T::Item, impl Fn(T::Item)> +where + T: Foo, + F: Fn(T::Item), +{ + B { x: x.get(), f } +} + +pub struct B<T, F> +where + F: Fn(T), +{ + pub x: T, + pub f: F, +} + +impl Foo for i32 { + type Item = i32; + fn get(&self) -> i32 { + *self + } +} + +fn main() { + let _ = blah(0, |_| ()); +} diff --git a/src/test/ui/bad/bad-method-typaram-kind.rs b/src/test/ui/traits/bad-method-typaram-kind.rs similarity index 100% rename from src/test/ui/bad/bad-method-typaram-kind.rs rename to src/test/ui/traits/bad-method-typaram-kind.rs diff --git a/src/test/ui/bad/bad-method-typaram-kind.stderr b/src/test/ui/traits/bad-method-typaram-kind.stderr similarity index 79% rename from src/test/ui/bad/bad-method-typaram-kind.stderr rename to src/test/ui/traits/bad-method-typaram-kind.stderr index 5b68d97a9ea55..fd3999ae6fbec 100644 --- a/src/test/ui/bad/bad-method-typaram-kind.stderr +++ b/src/test/ui/traits/bad-method-typaram-kind.stderr @@ -6,8 +6,8 @@ LL | 1.bar::<T>(); | help: consider further restricting this bound | -LL | fn foo<T:'static + Send>() { - | ^^^^^^ +LL | fn foo<T:'static + std::marker::Send>() { + | ^^^^^^^^^^^^^^^^^^^ error: aborting due to previous error diff --git a/src/test/ui/bad/bad-sized.rs b/src/test/ui/traits/bad-sized.rs similarity index 85% rename from src/test/ui/bad/bad-sized.rs rename to src/test/ui/traits/bad-sized.rs index b899c59ff2ea7..a15219679788d 100644 --- a/src/test/ui/bad/bad-sized.rs +++ b/src/test/ui/traits/bad-sized.rs @@ -5,4 +5,5 @@ pub fn main() { //~^ ERROR only auto traits can be used as additional traits in a trait object //~| ERROR the size for values of type //~| ERROR the size for values of type + //~| ERROR the size for values of type } diff --git a/src/test/ui/traits/bad-sized.stderr b/src/test/ui/traits/bad-sized.stderr new file mode 100644 index 0000000000000..768893d6e25d4 --- /dev/null +++ b/src/test/ui/traits/bad-sized.stderr @@ -0,0 +1,50 @@ +error[E0225]: only auto traits can be used as additional traits in a trait object + --> $DIR/bad-sized.rs:4:28 + | +LL | let x: Vec<dyn Trait + Sized> = Vec::new(); + | ----- ^^^^^ additional non-auto trait + | | + | first non-auto trait + | + = help: consider creating a new trait with all of these as super-traits and using that trait here instead: `trait NewTrait: Trait + Sized {}` + = note: auto-traits like `Send` and `Sync` are traits that have special properties; for more information on them, visit <https://doc.rust-lang.org/reference/special-types-and-traits.html#auto-traits> + +error[E0277]: the size for values of type `dyn Trait` cannot be known at compilation time + --> $DIR/bad-sized.rs:4:12 + | +LL | let x: Vec<dyn Trait + Sized> = Vec::new(); + | ^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time + | + ::: $SRC_DIR/alloc/src/vec/mod.rs:LL:COL + | +LL | pub struct Vec<T, #[unstable(feature = "allocator_api", issue = "32838")] A: Allocator = Global> { + | - required by this bound in `Vec` + | + = help: the trait `Sized` is not implemented for `dyn Trait` + +error[E0277]: the size for values of type `dyn Trait` cannot be known at compilation time + --> $DIR/bad-sized.rs:4:37 + | +LL | let x: Vec<dyn Trait + Sized> = Vec::new(); + | ^^^^^^^^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `dyn Trait` + = note: required by `Vec::<T>::new` + +error[E0277]: the size for values of type `dyn Trait` cannot be known at compilation time + --> $DIR/bad-sized.rs:4:37 + | +LL | let x: Vec<dyn Trait + Sized> = Vec::new(); + | ^^^ doesn't have a size known at compile-time + | + ::: $SRC_DIR/alloc/src/vec/mod.rs:LL:COL + | +LL | pub struct Vec<T, #[unstable(feature = "allocator_api", issue = "32838")] A: Allocator = Global> { + | - required by this bound in `Vec` + | + = help: the trait `Sized` is not implemented for `dyn Trait` + +error: aborting due to 4 previous errors + +Some errors have detailed explanations: E0225, E0277. +For more information about an error, try `rustc --explain E0225`. diff --git a/src/test/ui/traits/bound/not-on-bare-trait.stderr b/src/test/ui/traits/bound/not-on-bare-trait.stderr index e7fc0fa5ec0ec..b8ae88ace02dd 100644 --- a/src/test/ui/traits/bound/not-on-bare-trait.stderr +++ b/src/test/ui/traits/bound/not-on-bare-trait.stderr @@ -16,8 +16,8 @@ LL | fn foo(_x: Foo + Send) { = help: unsized fn params are gated as an unstable feature help: function arguments must have a statically known size, borrowed types always have a known size | -LL | fn foo(&_x: Foo + Send) { - | ^ +LL | fn foo(_x: &Foo + Send) { + | ^ error: aborting due to previous error; 1 warning emitted diff --git a/src/test/ui/traits/cycle-cache-err-60010.stderr b/src/test/ui/traits/cycle-cache-err-60010.stderr index b2702d977f8b6..40386f706132b 100644 --- a/src/test/ui/traits/cycle-cache-err-60010.stderr +++ b/src/test/ui/traits/cycle-cache-err-60010.stderr @@ -10,9 +10,21 @@ LL | SourceDatabase::parse(db); = note: required because it appears within the type `*const SalsaStorage` = note: required because it appears within the type `Unique<SalsaStorage>` = note: required because it appears within the type `Box<SalsaStorage>` - = note: required because it appears within the type `Runtime<RootDatabase>` - = note: required because it appears within the type `RootDatabase` - = note: required because of the requirements on the impl of `SourceDatabase` for `RootDatabase` +note: required because it appears within the type `Runtime<RootDatabase>` + --> $DIR/cycle-cache-err-60010.rs:23:8 + | +LL | struct Runtime<DB: Database> { + | ^^^^^^^ +note: required because it appears within the type `RootDatabase` + --> $DIR/cycle-cache-err-60010.rs:20:8 + | +LL | struct RootDatabase { + | ^^^^^^^^^^^^ +note: required because of the requirements on the impl of `SourceDatabase` for `RootDatabase` + --> $DIR/cycle-cache-err-60010.rs:43:9 + | +LL | impl<T> SourceDatabase for T + | ^^^^^^^^^^^^^^ ^ error: aborting due to previous error diff --git a/src/test/ui/impl-bounds-checking.rs b/src/test/ui/traits/impl-bounds-checking.rs similarity index 100% rename from src/test/ui/impl-bounds-checking.rs rename to src/test/ui/traits/impl-bounds-checking.rs diff --git a/src/test/ui/impl-bounds-checking.stderr b/src/test/ui/traits/impl-bounds-checking.stderr similarity index 100% rename from src/test/ui/impl-bounds-checking.stderr rename to src/test/ui/traits/impl-bounds-checking.stderr diff --git a/src/test/ui/traits/impl.rs b/src/test/ui/traits/impl.rs index 14796ce19c88e..f512d91ebeb4a 100644 --- a/src/test/ui/traits/impl.rs +++ b/src/test/ui/traits/impl.rs @@ -30,8 +30,8 @@ fn main() { let x: &dyn T = &42; x.foo(); - T::foo(x); - T::bar(); + <dyn T>::foo(x); + <dyn T>::bar(); unsafe { assert_eq!(COUNT, 12); } diff --git a/src/test/ui/traits/inductive-overflow/lifetime.rs b/src/test/ui/traits/inductive-overflow/lifetime.rs index 205d50a2ed9ce..e23dfa57cd0d4 100644 --- a/src/test/ui/traits/inductive-overflow/lifetime.rs +++ b/src/test/ui/traits/inductive-overflow/lifetime.rs @@ -16,7 +16,7 @@ struct C<'a>(&'a ()); struct X<T: Y>(T::P); impl<T: NotAuto> NotAuto for Box<T> {} -impl<T: Y> NotAuto for X<T> where T::P: NotAuto {} +impl<T: Y> NotAuto for X<T> where T::P: NotAuto {} //~ NOTE: required impl<'a> NotAuto for C<'a> {} fn is_send<S: NotAuto>() {} @@ -26,5 +26,4 @@ fn main() { // Should only be a few notes. is_send::<X<C<'static>>>(); //~^ ERROR overflow evaluating - //~| NOTE: required } diff --git a/src/test/ui/traits/inductive-overflow/lifetime.stderr b/src/test/ui/traits/inductive-overflow/lifetime.stderr index 659f9e26e3e62..752154b35cabe 100644 --- a/src/test/ui/traits/inductive-overflow/lifetime.stderr +++ b/src/test/ui/traits/inductive-overflow/lifetime.stderr @@ -7,7 +7,11 @@ LL | fn is_send<S: NotAuto>() {} LL | is_send::<X<C<'static>>>(); | ^^^^^^^^^^^^^^^^^^^^^^^^ | - = note: required because of the requirements on the impl of `NotAuto` for `X<C<'static>>` +note: required because of the requirements on the impl of `NotAuto` for `X<C<'static>>` + --> $DIR/lifetime.rs:19:12 + | +LL | impl<T: Y> NotAuto for X<T> where T::P: NotAuto {} + | ^^^^^^^ ^^^^ error: aborting due to previous error diff --git a/src/test/ui/traits/inductive-overflow/simultaneous.stderr b/src/test/ui/traits/inductive-overflow/simultaneous.stderr index 484ac8511790f..94a255fcb84de 100644 --- a/src/test/ui/traits/inductive-overflow/simultaneous.stderr +++ b/src/test/ui/traits/inductive-overflow/simultaneous.stderr @@ -1,4 +1,4 @@ -error[E0275]: overflow evaluating the requirement `{integer}: Tweedledum` +error[E0275]: overflow evaluating the requirement `{integer}: Tweedledee` --> $DIR/simultaneous.rs:18:5 | LL | fn is_ee<T: Combo>(t: T) { @@ -7,7 +7,11 @@ LL | fn is_ee<T: Combo>(t: T) { LL | is_ee(4); | ^^^^^ | - = note: required because of the requirements on the impl of `Combo` for `{integer}` +note: required because of the requirements on the impl of `Combo` for `{integer}` + --> $DIR/simultaneous.rs:11:34 + | +LL | impl<T: Tweedledee + Tweedledum> Combo for T {} + | ^^^^^ ^ error: aborting due to previous error diff --git a/src/test/ui/traits/inductive-overflow/supertrait.stderr b/src/test/ui/traits/inductive-overflow/supertrait.stderr index dfb967601e987..5ed1c2cc2da6f 100644 --- a/src/test/ui/traits/inductive-overflow/supertrait.stderr +++ b/src/test/ui/traits/inductive-overflow/supertrait.stderr @@ -7,7 +7,11 @@ LL | fn copy<T: Magic>(x: T) -> (T, T) { (x, x) } LL | let (a, b) = copy(NoClone); | ^^^^ | - = note: required because of the requirements on the impl of `Magic` for `NoClone` +note: required because of the requirements on the impl of `Magic` for `NoClone` + --> $DIR/supertrait.rs:5:16 + | +LL | impl<T: Magic> Magic for T {} + | ^^^^^ ^ error: aborting due to previous error diff --git a/src/test/ui/traits/inductive-overflow/two-traits.stderr b/src/test/ui/traits/inductive-overflow/two-traits.stderr index d3f2931f25d47..508a12d859a62 100644 --- a/src/test/ui/traits/inductive-overflow/two-traits.stderr +++ b/src/test/ui/traits/inductive-overflow/two-traits.stderr @@ -9,8 +9,8 @@ LL | type X = Self; | help: consider further restricting this bound | -LL | impl<T: Magic + Sync> Magic for T { - | ^^^^^^ +LL | impl<T: Magic + std::marker::Sync> Magic for T { + | ^^^^^^^^^^^^^^^^^^^ error[E0275]: overflow evaluating the requirement `*mut (): Magic` --> $DIR/two-traits.rs:20:5 diff --git a/src/test/ui/traits/inheritance/repeated-supertrait-ambig.rs b/src/test/ui/traits/inheritance/repeated-supertrait-ambig.rs index 6aaef8a305bce..727897d20cb4c 100644 --- a/src/test/ui/traits/inheritance/repeated-supertrait-ambig.rs +++ b/src/test/ui/traits/inheritance/repeated-supertrait-ambig.rs @@ -31,7 +31,7 @@ fn with_trait<C:CompareToInts>(c: &C) -> bool { } fn with_ufcs1<C:CompareToInts>(c: &C) -> bool { - CompareToInts::same_as(c, 22) //~ ERROR `dyn CompareToInts: CompareTo<i32>` is not satisfied + <dyn CompareToInts>::same_as(c, 22) //~ ERROR `dyn CompareToInts: CompareTo<i32>` is not satisfi } fn with_ufcs2<C:CompareToInts>(c: &C) -> bool { diff --git a/src/test/ui/traits/inheritance/repeated-supertrait-ambig.stderr b/src/test/ui/traits/inheritance/repeated-supertrait-ambig.stderr index 5353b5e2260d4..bb11f18e54505 100644 --- a/src/test/ui/traits/inheritance/repeated-supertrait-ambig.stderr +++ b/src/test/ui/traits/inheritance/repeated-supertrait-ambig.stderr @@ -21,8 +21,8 @@ error[E0277]: the trait bound `dyn CompareToInts: CompareTo<i32>` is not satisfi LL | fn same_as(&self, t: T) -> bool; | -------------------------------- required by `CompareTo::same_as` ... -LL | CompareToInts::same_as(c, 22) - | ^^^^^^^^^^^^^^^^^^^^^^ the trait `CompareTo<i32>` is not implemented for `dyn CompareToInts` +LL | <dyn CompareToInts>::same_as(c, 22) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `CompareTo<i32>` is not implemented for `dyn CompareToInts` error[E0277]: the trait bound `C: CompareTo<i32>` is not satisfied --> $DIR/repeated-supertrait-ambig.rs:38:5 diff --git a/src/test/ui/traits/inheritance/repeated-supertrait.rs b/src/test/ui/traits/inheritance/repeated-supertrait.rs index 339f9c37eeae8..cb2581ffa99a5 100644 --- a/src/test/ui/traits/inheritance/repeated-supertrait.rs +++ b/src/test/ui/traits/inheritance/repeated-supertrait.rs @@ -31,7 +31,7 @@ fn with_trait<C:CompareToInts>(c: &C) -> bool { } fn with_ufcs1<C:CompareToInts>(c: &C) -> bool { - CompareToInts::same_as(c, 22_i64) && CompareToInts::same_as(c, 22_u64) + <dyn CompareToInts>::same_as(c, 22_i64) && <dyn CompareToInts>::same_as(c, 22_u64) } fn with_ufcs2<C:CompareToInts>(c: &C) -> bool { diff --git a/src/test/ui/issues/issue-28576.rs b/src/test/ui/traits/issue-28576.rs similarity index 100% rename from src/test/ui/issues/issue-28576.rs rename to src/test/ui/traits/issue-28576.rs diff --git a/src/test/ui/issues/issue-28576.stderr b/src/test/ui/traits/issue-28576.stderr similarity index 100% rename from src/test/ui/issues/issue-28576.stderr rename to src/test/ui/traits/issue-28576.stderr diff --git a/src/test/ui/traits/issue-33140-hack-boundaries.stderr b/src/test/ui/traits/issue-33140-hack-boundaries.stderr index ae65701ecb52a..c87f1ff1f3f15 100644 --- a/src/test/ui/traits/issue-33140-hack-boundaries.stderr +++ b/src/test/ui/traits/issue-33140-hack-boundaries.stderr @@ -1,4 +1,4 @@ -error[E0119]: conflicting implementations of trait `Trait1` for type `(dyn std::marker::Send + 'static)`: +error[E0119]: conflicting implementations of trait `Trait1` for type `(dyn std::marker::Send + 'static)` --> $DIR/issue-33140-hack-boundaries.rs:18:1 | LL | impl Trait1 for dyn Send {} @@ -14,7 +14,7 @@ LL | impl Trait2 for dyn Send {} LL | impl !Trait2 for dyn Send {} | ^^^^^^^^^^^^^^^^^^^^^^^^^ negative implementation here -error[E0119]: conflicting implementations of trait `Trait3<(dyn std::marker::Sync + 'static)>` for type `(dyn std::marker::Send + 'static)`: +error[E0119]: conflicting implementations of trait `Trait3<(dyn std::marker::Sync + 'static)>` for type `(dyn std::marker::Send + 'static)` --> $DIR/issue-33140-hack-boundaries.rs:32:1 | LL | impl Trait3<dyn Sync> for dyn Send {} @@ -22,7 +22,7 @@ LL | impl Trait3<dyn Sync> for dyn Send {} LL | impl Trait3<dyn Sync> for dyn Send {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `(dyn std::marker::Send + 'static)` -error[E0119]: conflicting implementations of trait `Trait4a` for type `(dyn std::marker::Send + 'static)`: +error[E0119]: conflicting implementations of trait `Trait4a` for type `(dyn std::marker::Send + 'static)` --> $DIR/issue-33140-hack-boundaries.rs:39:1 | LL | impl<T: ?Sized> Trait4a for T {} @@ -30,7 +30,7 @@ LL | impl<T: ?Sized> Trait4a for T {} LL | impl Trait4a for dyn Send {} | ^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `(dyn std::marker::Send + 'static)` -error[E0119]: conflicting implementations of trait `Trait4b` for type `()`: +error[E0119]: conflicting implementations of trait `Trait4b` for type `()` --> $DIR/issue-33140-hack-boundaries.rs:46:1 | LL | impl Trait4b for () {} @@ -38,7 +38,7 @@ LL | impl Trait4b for () {} LL | impl Trait4b for () {} | ^^^^^^^^^^^^^^^^^^^ conflicting implementation for `()` -error[E0119]: conflicting implementations of trait `Trait4c` for type `(dyn Trait1 + std::marker::Send + 'static)`: +error[E0119]: conflicting implementations of trait `Trait4c` for type `(dyn Trait1 + std::marker::Send + 'static)` --> $DIR/issue-33140-hack-boundaries.rs:53:1 | LL | impl Trait4c for dyn Trait1 + Send {} @@ -46,7 +46,7 @@ LL | impl Trait4c for dyn Trait1 + Send {} LL | impl Trait4c for dyn Trait1 + Send {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `(dyn Trait1 + std::marker::Send + 'static)` -error[E0119]: conflicting implementations of trait `Trait4d` for type `dyn std::marker::Send`: +error[E0119]: conflicting implementations of trait `Trait4d` for type `dyn std::marker::Send` --> $DIR/issue-33140-hack-boundaries.rs:60:1 | LL | impl<'a> Trait4d for dyn Send + 'a {} @@ -54,7 +54,7 @@ LL | impl<'a> Trait4d for dyn Send + 'a {} LL | impl<'a> Trait4d for dyn Send + 'a {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `dyn std::marker::Send` -error[E0119]: conflicting implementations of trait `Trait5` for type `(dyn std::marker::Send + 'static)`: +error[E0119]: conflicting implementations of trait `Trait5` for type `(dyn std::marker::Send + 'static)` --> $DIR/issue-33140-hack-boundaries.rs:67:1 | LL | impl Trait5 for dyn Send {} diff --git a/src/test/ui/issues/issue-33140.rs b/src/test/ui/traits/issue-33140.rs similarity index 100% rename from src/test/ui/issues/issue-33140.rs rename to src/test/ui/traits/issue-33140.rs diff --git a/src/test/ui/issues/issue-33140.stderr b/src/test/ui/traits/issue-33140.stderr similarity index 90% rename from src/test/ui/issues/issue-33140.stderr rename to src/test/ui/traits/issue-33140.stderr index 9a900d2fc9404..392c56a282d7e 100644 --- a/src/test/ui/issues/issue-33140.stderr +++ b/src/test/ui/traits/issue-33140.stderr @@ -1,4 +1,4 @@ -error[E0119]: conflicting implementations of trait `Trait` for type `(dyn std::marker::Send + std::marker::Sync + 'static)`: +error[E0119]: conflicting implementations of trait `Trait` for type `(dyn std::marker::Send + std::marker::Sync + 'static)` --> $DIR/issue-33140.rs:9:1 | LL | impl Trait for dyn Send + Sync { @@ -7,7 +7,7 @@ LL | impl Trait for dyn Send + Sync { LL | impl Trait for dyn Sync + Send { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `(dyn std::marker::Send + std::marker::Sync + 'static)` -error[E0119]: conflicting implementations of trait `Trait2` for type `(dyn std::marker::Send + std::marker::Sync + 'static)`: +error[E0119]: conflicting implementations of trait `Trait2` for type `(dyn std::marker::Send + std::marker::Sync + 'static)` --> $DIR/issue-33140.rs:22:1 | LL | impl Trait2 for dyn Send + Sync { diff --git a/src/test/ui/issues/issue-78372.rs b/src/test/ui/traits/issue-78372.rs similarity index 100% rename from src/test/ui/issues/issue-78372.rs rename to src/test/ui/traits/issue-78372.rs diff --git a/src/test/ui/issues/issue-78372.stderr b/src/test/ui/traits/issue-78372.stderr similarity index 100% rename from src/test/ui/issues/issue-78372.stderr rename to src/test/ui/traits/issue-78372.stderr diff --git a/src/test/ui/traits/item-privacy.rs b/src/test/ui/traits/item-privacy.rs index 1ea1d65df627c..38d06b967bc79 100644 --- a/src/test/ui/traits/item-privacy.rs +++ b/src/test/ui/traits/item-privacy.rs @@ -81,8 +81,8 @@ fn check_method() { //~^ ERROR no function or associated item named `b` found S::c(&S); // OK // a, b, c are resolved as inherent items, their traits don't need to be in scope - C::a(&S); //~ ERROR associated function `a` is private - C::b(&S); // OK + <dyn C>::a(&S); //~ ERROR associated function `a` is private + <dyn C>::b(&S); // OK C::c(&S); // OK } @@ -98,9 +98,9 @@ fn check_assoc_const() { S::B; //~ ERROR no associated item named `B` found S::C; // OK // A, B, C are resolved as inherent items, their traits don't need to be in scope - C::A; //~ ERROR associated constant `A` is private - //~^ ERROR the trait `assoc_const::C` cannot be made into an object - C::B; // ERROR the trait `assoc_const::C` cannot be made into an object + <dyn C>::A; //~ ERROR associated constant `A` is private + //~^ ERROR the trait `assoc_const::C` cannot be made into an object + <dyn C>::B; // ERROR the trait `assoc_const::C` cannot be made into an object C::C; // OK } diff --git a/src/test/ui/traits/item-privacy.stderr b/src/test/ui/traits/item-privacy.stderr index 6fd82142d61f7..68d527dc786aa 100644 --- a/src/test/ui/traits/item-privacy.stderr +++ b/src/test/ui/traits/item-privacy.stderr @@ -67,10 +67,10 @@ LL | use method::B; | error[E0624]: associated function `a` is private - --> $DIR/item-privacy.rs:84:8 + --> $DIR/item-privacy.rs:84:14 | -LL | C::a(&S); - | ^ private associated function +LL | <dyn C>::a(&S); + | ^ private associated function error[E0599]: no associated item named `A` found for struct `S` in the current scope --> $DIR/item-privacy.rs:97:8 @@ -104,16 +104,16 @@ LL | use assoc_const::B; | error[E0624]: associated constant `A` is private - --> $DIR/item-privacy.rs:101:8 + --> $DIR/item-privacy.rs:101:14 | -LL | C::A; - | ^ private associated constant +LL | <dyn C>::A; + | ^ private associated constant error[E0038]: the trait `assoc_const::C` cannot be made into an object - --> $DIR/item-privacy.rs:101:5 + --> $DIR/item-privacy.rs:101:6 | -LL | C::A; - | ^^^^ `assoc_const::C` cannot be made into an object +LL | <dyn C>::A; + | ^^^^^ `assoc_const::C` cannot be made into an object | = help: consider moving `C` to another trait = help: consider moving `B` to another trait diff --git a/src/test/ui/traits/negative-impls/explicitly-unimplemented-error-message.stderr b/src/test/ui/traits/negative-impls/explicitly-unimplemented-error-message.stderr index d39daaba206d2..01e36a4a62a1b 100644 --- a/src/test/ui/traits/negative-impls/explicitly-unimplemented-error-message.stderr +++ b/src/test/ui/traits/negative-impls/explicitly-unimplemented-error-message.stderr @@ -6,14 +6,6 @@ LL | struct Qux; ... LL | Qux.clone(); | ^^^^^ method not found in `Qux` - | - ::: $SRC_DIR/core/src/clone.rs:LL:COL - | -LL | fn clone(&self) -> Self; - | ----- - | | - | the method is available for `Arc<Qux>` here - | the method is available for `Rc<Qux>` here | = help: items from traits can only be used if the trait is implemented and in scope = note: the trait `Clone` defines an item `clone`, but is explicitely unimplemented diff --git a/src/test/ui/traits/negative-impls/negated-auto-traits-error.stderr b/src/test/ui/traits/negative-impls/negated-auto-traits-error.stderr index 83b1b83d1939d..4f7d1be793891 100644 --- a/src/test/ui/traits/negative-impls/negated-auto-traits-error.stderr +++ b/src/test/ui/traits/negative-impls/negated-auto-traits-error.stderr @@ -69,7 +69,11 @@ LL | is_send(Box::new(Outer2(TestType))); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ `dummy3::TestType` cannot be sent between threads safely | = help: within `Outer2<dummy3::TestType>`, the trait `Send` is not implemented for `dummy3::TestType` - = note: required because it appears within the type `Outer2<dummy3::TestType>` +note: required because it appears within the type `Outer2<dummy3::TestType>` + --> $DIR/negated-auto-traits-error.rs:12:8 + | +LL | struct Outer2<T>(T); + | ^^^^^^ = note: required because of the requirements on the impl of `Send` for `Unique<Outer2<dummy3::TestType>>` = note: required because it appears within the type `Box<Outer2<dummy3::TestType>>` @@ -86,7 +90,11 @@ LL | is_sync(Outer2(TestType)); | help: consider borrowing here: `&Outer2(TestType)` | = note: the trait bound `main::TestType: Sync` is not satisfied - = note: required because of the requirements on the impl of `Sync` for `Outer2<main::TestType>` +note: required because of the requirements on the impl of `Sync` for `Outer2<main::TestType>` + --> $DIR/negated-auto-traits-error.rs:14:22 + | +LL | unsafe impl<T: Send> Sync for Outer2<T> {} + | ^^^^ ^^^^^^^^^ error: aborting due to 7 previous errors diff --git a/src/test/ui/traits/negative-impls/rely-on-negative-impl-in-coherence.stderr b/src/test/ui/traits/negative-impls/rely-on-negative-impl-in-coherence.stderr index 7cce45d2c8f8f..b970ad762088d 100644 --- a/src/test/ui/traits/negative-impls/rely-on-negative-impl-in-coherence.stderr +++ b/src/test/ui/traits/negative-impls/rely-on-negative-impl-in-coherence.stderr @@ -1,4 +1,4 @@ -error[E0119]: conflicting implementations of trait `LocalTrait` for type `std::string::String`: +error[E0119]: conflicting implementations of trait `LocalTrait` for type `std::string::String` --> $DIR/rely-on-negative-impl-in-coherence.rs:19:1 | LL | impl<T: ForeignTrait> LocalTrait for T { } diff --git a/src/test/ui/issues/issue-33140-traitobject-crate.rs b/src/test/ui/traits/object/issue-33140-traitobject-crate.rs similarity index 100% rename from src/test/ui/issues/issue-33140-traitobject-crate.rs rename to src/test/ui/traits/object/issue-33140-traitobject-crate.rs diff --git a/src/test/ui/issues/issue-33140-traitobject-crate.stderr b/src/test/ui/traits/object/issue-33140-traitobject-crate.stderr similarity index 100% rename from src/test/ui/issues/issue-33140-traitobject-crate.stderr rename to src/test/ui/traits/object/issue-33140-traitobject-crate.stderr diff --git a/src/test/ui/traits/overlap-not-permitted-for-builtin-trait.stderr b/src/test/ui/traits/overlap-not-permitted-for-builtin-trait.stderr index 94a0c287f4a32..910c5e29dac0e 100644 --- a/src/test/ui/traits/overlap-not-permitted-for-builtin-trait.stderr +++ b/src/test/ui/traits/overlap-not-permitted-for-builtin-trait.stderr @@ -1,4 +1,4 @@ -error[E0119]: conflicting implementations of trait `std::marker::Send` for type `MyStruct`: +error[E0119]: conflicting implementations of trait `std::marker::Send` for type `MyStruct` --> $DIR/overlap-not-permitted-for-builtin-trait.rs:7:1 | LL | impl !Send for MyStruct {} diff --git a/src/test/ui/traits/reservation-impl/coherence-conflict.stderr b/src/test/ui/traits/reservation-impl/coherence-conflict.stderr index 1a227a85c06f6..a811d7e32016b 100644 --- a/src/test/ui/traits/reservation-impl/coherence-conflict.stderr +++ b/src/test/ui/traits/reservation-impl/coherence-conflict.stderr @@ -1,4 +1,4 @@ -error[E0119]: conflicting implementations of trait `OtherTrait` for type `()`: +error[E0119]: conflicting implementations of trait `OtherTrait` for type `()` --> $DIR/coherence-conflict.rs:11:1 | LL | impl OtherTrait for () {} diff --git a/src/test/ui/traits/suggest-where-clause.stderr b/src/test/ui/traits/suggest-where-clause.stderr index 86d589ffa9e31..025706480821d 100644 --- a/src/test/ui/traits/suggest-where-clause.stderr +++ b/src/test/ui/traits/suggest-where-clause.stderr @@ -2,7 +2,7 @@ error[E0277]: the size for values of type `U` cannot be known at compilation tim --> $DIR/suggest-where-clause.rs:7:20 | LL | fn check<T: Iterator, U: ?Sized>() { - | - this type parameter needs to be `Sized` + | - this type parameter needs to be `std::marker::Sized` LL | // suggest a where-clause, if needed LL | mem::size_of::<U>(); | ^ doesn't have a size known at compile-time @@ -16,7 +16,7 @@ error[E0277]: the size for values of type `U` cannot be known at compilation tim --> $DIR/suggest-where-clause.rs:10:5 | LL | fn check<T: Iterator, U: ?Sized>() { - | - this type parameter needs to be `Sized` + | - this type parameter needs to be `std::marker::Sized` ... LL | mem::size_of::<Misc<U>>(); | ^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time @@ -26,7 +26,11 @@ LL | mem::size_of::<Misc<U>>(); LL | pub const fn size_of<T>() -> usize { | - required by this bound in `std::mem::size_of` | - = note: required because it appears within the type `Misc<U>` +note: required because it appears within the type `Misc<U>` + --> $DIR/suggest-where-clause.rs:3:8 + | +LL | struct Misc<T:?Sized>(T); + | ^^^^ error[E0277]: the trait bound `u64: From<T>` is not satisfied --> $DIR/suggest-where-clause.rs:15:5 diff --git a/src/test/ui/try-block.rs b/src/test/ui/try-block/try-block.rs similarity index 100% rename from src/test/ui/try-block.rs rename to src/test/ui/try-block/try-block.rs diff --git a/src/test/ui/tup.rs b/src/test/ui/tuple/tup.rs similarity index 100% rename from src/test/ui/tup.rs rename to src/test/ui/tuple/tup.rs diff --git a/src/test/ui/type-alias-enum-variants/incorrect-variant-form-through-alias-caught.rs b/src/test/ui/type-alias-enum-variants/incorrect-variant-form-through-alias-caught.rs index 5772450477c1b..e4abb96b4bff3 100644 --- a/src/test/ui/type-alias-enum-variants/incorrect-variant-form-through-alias-caught.rs +++ b/src/test/ui/type-alias-enum-variants/incorrect-variant-form-through-alias-caught.rs @@ -1,5 +1,3 @@ -// ignore-tidy-linelength - // Check that creating/matching on an enum variant through an alias with // the wrong braced/unit form is caught as an error. diff --git a/src/test/ui/type-alias-enum-variants/incorrect-variant-form-through-alias-caught.stderr b/src/test/ui/type-alias-enum-variants/incorrect-variant-form-through-alias-caught.stderr index b0de3ee42e339..f80abade0fd54 100644 --- a/src/test/ui/type-alias-enum-variants/incorrect-variant-form-through-alias-caught.stderr +++ b/src/test/ui/type-alias-enum-variants/incorrect-variant-form-through-alias-caught.stderr @@ -1,23 +1,23 @@ error[E0533]: expected unit struct, unit variant or constant, found struct variant `Alias::Braced` - --> $DIR/incorrect-variant-form-through-alias-caught.rs:10:5 + --> $DIR/incorrect-variant-form-through-alias-caught.rs:8:5 | LL | Alias::Braced; | ^^^^^^^^^^^^^ error[E0533]: expected unit struct, unit variant or constant, found struct variant `Alias::Braced` - --> $DIR/incorrect-variant-form-through-alias-caught.rs:12:9 + --> $DIR/incorrect-variant-form-through-alias-caught.rs:10:9 | LL | let Alias::Braced = panic!(); | ^^^^^^^^^^^^^ error[E0164]: expected tuple struct or tuple variant, found struct variant `Alias::Braced` - --> $DIR/incorrect-variant-form-through-alias-caught.rs:14:9 + --> $DIR/incorrect-variant-form-through-alias-caught.rs:12:9 | LL | let Alias::Braced(..) = panic!(); | ^^^^^^^^^^^^^^^^^ not a tuple variant or struct error[E0618]: expected function, found enum variant `Alias::Unit` - --> $DIR/incorrect-variant-form-through-alias-caught.rs:17:5 + --> $DIR/incorrect-variant-form-through-alias-caught.rs:15:5 | LL | enum Enum { Braced {}, Unit, Tuple() } | ---- `Alias::Unit` defined here @@ -33,7 +33,7 @@ LL | Alias::Unit; | ^^^^^^^^^^^ error[E0164]: expected tuple struct or tuple variant, found unit variant `Alias::Unit` - --> $DIR/incorrect-variant-form-through-alias-caught.rs:19:9 + --> $DIR/incorrect-variant-form-through-alias-caught.rs:17:9 | LL | let Alias::Unit() = panic!(); | ^^^^^^^^^^^^^ not a tuple variant or struct diff --git a/src/test/ui/type-alias-impl-trait/assoc-type-const.full_tait.stderr b/src/test/ui/type-alias-impl-trait/assoc-type-const.full_tait.stderr new file mode 100644 index 0000000000000..7b43d1d86bcd4 --- /dev/null +++ b/src/test/ui/type-alias-impl-trait/assoc-type-const.full_tait.stderr @@ -0,0 +1,19 @@ +warning: the feature `type_alias_impl_trait` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/assoc-type-const.rs:7:32 + | +LL | #![cfg_attr(full_tait, feature(type_alias_impl_trait))] + | ^^^^^^^^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #63063 <https://github.com/rust-lang/rust/issues/63063> for more information + +warning: the feature `const_generics` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/assoc-type-const.rs:9:12 + | +LL | #![feature(const_generics)] + | ^^^^^^^^^^^^^^ + | + = note: see issue #44580 <https://github.com/rust-lang/rust/issues/44580> for more information + +warning: 2 warnings emitted + diff --git a/src/test/ui/type-alias-impl-trait/assoc-type-const.min_tait.stderr b/src/test/ui/type-alias-impl-trait/assoc-type-const.min_tait.stderr new file mode 100644 index 0000000000000..d4b9132cc927b --- /dev/null +++ b/src/test/ui/type-alias-impl-trait/assoc-type-const.min_tait.stderr @@ -0,0 +1,11 @@ +warning: the feature `const_generics` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/assoc-type-const.rs:9:12 + | +LL | #![feature(const_generics)] + | ^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #44580 <https://github.com/rust-lang/rust/issues/44580> for more information + +warning: 1 warning emitted + diff --git a/src/test/ui/type-alias-impl-trait/assoc-type-const.rs b/src/test/ui/type-alias-impl-trait/assoc-type-const.rs index d53f562e99f4b..be065c3b6beac 100644 --- a/src/test/ui/type-alias-impl-trait/assoc-type-const.rs +++ b/src/test/ui/type-alias-impl-trait/assoc-type-const.rs @@ -2,7 +2,10 @@ // const generics in an associated opaque type // check-pass -#![feature(type_alias_impl_trait)] +// revisions: min_tait full_tait +#![feature(min_type_alias_impl_trait)] +#![cfg_attr(full_tait, feature(type_alias_impl_trait))] +//[full_tait]~^ WARN incomplete #![feature(const_generics)] //~^ WARN the feature `const_generics` is incomplete diff --git a/src/test/ui/type-alias-impl-trait/assoc-type-const.stderr b/src/test/ui/type-alias-impl-trait/assoc-type-const.stderr deleted file mode 100644 index e0c1b02386127..0000000000000 --- a/src/test/ui/type-alias-impl-trait/assoc-type-const.stderr +++ /dev/null @@ -1,11 +0,0 @@ -warning: the feature `const_generics` is incomplete and may not be safe to use and/or cause compiler crashes - --> $DIR/assoc-type-const.rs:6:12 - | -LL | #![feature(const_generics)] - | ^^^^^^^^^^^^^^ - | - = note: `#[warn(incomplete_features)]` on by default - = note: see issue #44580 <https://github.com/rust-lang/rust/issues/44580> for more information - -warning: 1 warning emitted - diff --git a/src/test/ui/type-alias-impl-trait/assoc-type-lifetime-unconstrained.full_tait.stderr b/src/test/ui/type-alias-impl-trait/assoc-type-lifetime-unconstrained.full_tait.stderr new file mode 100644 index 0000000000000..01263cde8bb19 --- /dev/null +++ b/src/test/ui/type-alias-impl-trait/assoc-type-lifetime-unconstrained.full_tait.stderr @@ -0,0 +1,18 @@ +warning: the feature `type_alias_impl_trait` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/assoc-type-lifetime-unconstrained.rs:6:32 + | +LL | #![cfg_attr(full_tait, feature(type_alias_impl_trait))] + | ^^^^^^^^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #63063 <https://github.com/rust-lang/rust/issues/63063> for more information + +error[E0207]: the lifetime parameter `'a` is not constrained by the impl trait, self type, or predicates + --> $DIR/assoc-type-lifetime-unconstrained.rs:20:6 + | +LL | impl<'a, I> UnwrapItemsExt for I { + | ^^ unconstrained lifetime parameter + +error: aborting due to previous error; 1 warning emitted + +For more information about this error, try `rustc --explain E0207`. diff --git a/src/test/ui/type-alias-impl-trait/assoc-type-lifetime-unconstrained.min_tait.stderr b/src/test/ui/type-alias-impl-trait/assoc-type-lifetime-unconstrained.min_tait.stderr new file mode 100644 index 0000000000000..afcdab5f479df --- /dev/null +++ b/src/test/ui/type-alias-impl-trait/assoc-type-lifetime-unconstrained.min_tait.stderr @@ -0,0 +1,9 @@ +error[E0207]: the lifetime parameter `'a` is not constrained by the impl trait, self type, or predicates + --> $DIR/assoc-type-lifetime-unconstrained.rs:20:6 + | +LL | impl<'a, I> UnwrapItemsExt for I { + | ^^ unconstrained lifetime parameter + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0207`. diff --git a/src/test/ui/type-alias-impl-trait/assoc-type-lifetime-unconstrained.rs b/src/test/ui/type-alias-impl-trait/assoc-type-lifetime-unconstrained.rs index 3f34b00ec77e1..39cc75e688b83 100644 --- a/src/test/ui/type-alias-impl-trait/assoc-type-lifetime-unconstrained.rs +++ b/src/test/ui/type-alias-impl-trait/assoc-type-lifetime-unconstrained.rs @@ -1,7 +1,10 @@ // Tests that we don't allow unconstrained lifetime parameters in impls when // the lifetime is used in an associated opaque type. -#![feature(type_alias_impl_trait)] +// revisions: min_tait full_tait +#![feature(min_type_alias_impl_trait)] +#![cfg_attr(full_tait, feature(type_alias_impl_trait))] +//[full_tait]~^ WARN incomplete trait UnwrapItemsExt { type Iter; diff --git a/src/test/ui/type-alias-impl-trait/assoc-type-lifetime-unconstrained.stderr b/src/test/ui/type-alias-impl-trait/assoc-type-lifetime-unconstrained.stderr deleted file mode 100644 index e594dc577b1cd..0000000000000 --- a/src/test/ui/type-alias-impl-trait/assoc-type-lifetime-unconstrained.stderr +++ /dev/null @@ -1,9 +0,0 @@ -error[E0207]: the lifetime parameter `'a` is not constrained by the impl trait, self type, or predicates - --> $DIR/assoc-type-lifetime-unconstrained.rs:17:6 - | -LL | impl<'a, I> UnwrapItemsExt for I { - | ^^ unconstrained lifetime parameter - -error: aborting due to previous error - -For more information about this error, try `rustc --explain E0207`. diff --git a/src/test/ui/type-alias-impl-trait/assoc-type-lifetime.full_tait.stderr b/src/test/ui/type-alias-impl-trait/assoc-type-lifetime.full_tait.stderr new file mode 100644 index 0000000000000..31afbf14e658c --- /dev/null +++ b/src/test/ui/type-alias-impl-trait/assoc-type-lifetime.full_tait.stderr @@ -0,0 +1,11 @@ +warning: the feature `type_alias_impl_trait` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/assoc-type-lifetime.rs:7:32 + | +LL | #![cfg_attr(full_tait, feature(type_alias_impl_trait))] + | ^^^^^^^^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #63063 <https://github.com/rust-lang/rust/issues/63063> for more information + +warning: 1 warning emitted + diff --git a/src/test/ui/type-alias-impl-trait/assoc-type-lifetime.rs b/src/test/ui/type-alias-impl-trait/assoc-type-lifetime.rs index 39f785d8cc55d..ebbdbb67dbe6f 100644 --- a/src/test/ui/type-alias-impl-trait/assoc-type-lifetime.rs +++ b/src/test/ui/type-alias-impl-trait/assoc-type-lifetime.rs @@ -2,7 +2,10 @@ // lifetimes are used in an associated opaque type // check-pass -#![feature(type_alias_impl_trait)] +// revisions: min_tait full_tait +#![feature(min_type_alias_impl_trait)] +#![cfg_attr(full_tait, feature(type_alias_impl_trait))] +//[full_tait]~^ WARN incomplete trait UnwrapItemsExt<'a> { type Iter; diff --git a/src/test/ui/type-alias-impl-trait/associated-type-alias-impl-trait.full_tait.stderr b/src/test/ui/type-alias-impl-trait/associated-type-alias-impl-trait.full_tait.stderr new file mode 100644 index 0000000000000..2c48d815e0de4 --- /dev/null +++ b/src/test/ui/type-alias-impl-trait/associated-type-alias-impl-trait.full_tait.stderr @@ -0,0 +1,11 @@ +warning: the feature `type_alias_impl_trait` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/associated-type-alias-impl-trait.rs:3:32 + | +LL | #![cfg_attr(full_tait, feature(type_alias_impl_trait))] + | ^^^^^^^^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #63063 <https://github.com/rust-lang/rust/issues/63063> for more information + +warning: 1 warning emitted + diff --git a/src/test/ui/type-alias-impl-trait/associated-type-alias-impl-trait.rs b/src/test/ui/type-alias-impl-trait/associated-type-alias-impl-trait.rs index 42f07d49ffe25..8d2b1826790b3 100644 --- a/src/test/ui/type-alias-impl-trait/associated-type-alias-impl-trait.rs +++ b/src/test/ui/type-alias-impl-trait/associated-type-alias-impl-trait.rs @@ -1,4 +1,7 @@ -#![feature(type_alias_impl_trait)] +// revisions: min_tait full_tait +#![feature(min_type_alias_impl_trait)] +#![cfg_attr(full_tait, feature(type_alias_impl_trait))] +//[full_tait]~^ WARN incomplete // build-pass (FIXME(62277): could be check-pass?) trait Bar {} diff --git a/src/test/ui/type-alias-impl-trait/associated-type-lifetime-ice.rs b/src/test/ui/type-alias-impl-trait/associated-type-lifetime-ice.rs new file mode 100644 index 0000000000000..967d4c3f0f7f9 --- /dev/null +++ b/src/test/ui/type-alias-impl-trait/associated-type-lifetime-ice.rs @@ -0,0 +1,33 @@ +// failure-status: 101 +// rustc-env:RUST_BACKTRACE=0 +// normalize-stderr-test "note: .*\n\n" -> "" +// normalize-stderr-test "thread 'rustc' panicked.*\n" -> "" + +// compile-flags: --crate-type=rlib + +// Regression test for https://github.com/rust-lang/rust/issues/78450 + +#![feature(min_type_alias_impl_trait)] +#![no_std] + +pub trait AssociatedImpl { + type ImplTrait; + + fn f() -> Self::ImplTrait; +} + +struct S<T>(T); + +trait Associated { + type A; +} + +// ICE +impl<'a, T: Associated<A = &'a ()>> AssociatedImpl for S<T> { + type ImplTrait = impl core::fmt::Debug; + + fn f() -> Self::ImplTrait { + //~^ ERROR unexpected concrete region in borrowck: ReEarlyBound(0, 'a) + () + } +} diff --git a/src/test/ui/type-alias-impl-trait/associated-type-lifetime-ice.stderr b/src/test/ui/type-alias-impl-trait/associated-type-lifetime-ice.stderr new file mode 100644 index 0000000000000..64ab7b70b1a98 --- /dev/null +++ b/src/test/ui/type-alias-impl-trait/associated-type-lifetime-ice.stderr @@ -0,0 +1,13 @@ +error: internal compiler error: unexpected concrete region in borrowck: ReEarlyBound(0, 'a) + --> $DIR/associated-type-lifetime-ice.rs:29:5 + | +LL | / fn f() -> Self::ImplTrait { +LL | | +LL | | () +LL | | } + | |_____^ + | + = error: internal compiler error: unexpected panic + +query stack during panic: +end of query stack diff --git a/src/test/ui/type-alias-impl-trait/auxiliary/cross_crate_ice.rs b/src/test/ui/type-alias-impl-trait/auxiliary/cross_crate_ice.rs index f61807cbdbd58..94e1fa73de884 100644 --- a/src/test/ui/type-alias-impl-trait/auxiliary/cross_crate_ice.rs +++ b/src/test/ui/type-alias-impl-trait/auxiliary/cross_crate_ice.rs @@ -1,8 +1,10 @@ // Crate that exports an opaque `impl Trait` type. Used for testing cross-crate. -#![crate_type="rlib"] - -#![feature(type_alias_impl_trait)] +#![crate_type = "rlib"] +// revisions: min_tait full_tait +#![feature(min_type_alias_impl_trait)] +#![cfg_attr(full_tait, feature(type_alias_impl_trait))] +//[full_tait]~^ WARN incomplete pub type Foo = impl std::fmt::Debug; diff --git a/src/test/ui/type-alias-impl-trait/auxiliary/cross_crate_ice2.rs b/src/test/ui/type-alias-impl-trait/auxiliary/cross_crate_ice2.rs index 0082345626729..65bc594dcf2f7 100644 --- a/src/test/ui/type-alias-impl-trait/auxiliary/cross_crate_ice2.rs +++ b/src/test/ui/type-alias-impl-trait/auxiliary/cross_crate_ice2.rs @@ -1,8 +1,10 @@ // Crate that exports an opaque `impl Trait` type. Used for testing cross-crate. -#![crate_type="rlib"] - -#![feature(type_alias_impl_trait)] +#![crate_type = "rlib"] +// revisions: min_tait full_tait +#![feature(min_type_alias_impl_trait)] +#![cfg_attr(full_tait, feature(type_alias_impl_trait))] +//[full_tait]~^ WARN incomplete pub trait View { type Tmp: Iterator<Item = u32>; diff --git a/src/test/ui/type-alias-impl-trait/bound_reduction.rs b/src/test/ui/type-alias-impl-trait/bound_reduction.rs index 18c840d8ed9c7..b45105ea8f29e 100644 --- a/src/test/ui/type-alias-impl-trait/bound_reduction.rs +++ b/src/test/ui/type-alias-impl-trait/bound_reduction.rs @@ -1,8 +1,9 @@ // build-pass (FIXME(62277): could be check-pass?) #![allow(warnings)] - -#![feature(type_alias_impl_trait)] +// revisions: min_tait full_tait +#![feature(min_type_alias_impl_trait)] +#![cfg_attr(full_tait, feature(type_alias_impl_trait))] fn main() { } diff --git a/src/test/ui/type-alias-impl-trait/bound_reduction2.full_tait.stderr b/src/test/ui/type-alias-impl-trait/bound_reduction2.full_tait.stderr new file mode 100644 index 0000000000000..164564e8516fc --- /dev/null +++ b/src/test/ui/type-alias-impl-trait/bound_reduction2.full_tait.stderr @@ -0,0 +1,23 @@ +warning: the feature `type_alias_impl_trait` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/bound_reduction2.rs:3:32 + | +LL | #![cfg_attr(full_tait, feature(type_alias_impl_trait))] + | ^^^^^^^^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #63063 <https://github.com/rust-lang/rust/issues/63063> for more information + +error: non-defining opaque type use in defining scope + --> $DIR/bound_reduction2.rs:18: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:12:10 + | +LL | type Foo<V> = impl Trait<V>; + | ^ + +error: aborting due to previous error; 1 warning emitted + diff --git a/src/test/ui/type-alias-impl-trait/bound_reduction2.min_tait.stderr b/src/test/ui/type-alias-impl-trait/bound_reduction2.min_tait.stderr new file mode 100644 index 0000000000000..d3520a9bac2f3 --- /dev/null +++ b/src/test/ui/type-alias-impl-trait/bound_reduction2.min_tait.stderr @@ -0,0 +1,14 @@ +error: non-defining opaque type use in defining scope + --> $DIR/bound_reduction2.rs:18: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:12:10 + | +LL | type Foo<V> = impl Trait<V>; + | ^ + +error: aborting due to previous error + 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 a15074c35936b..b94b21e4244a2 100644 --- a/src/test/ui/type-alias-impl-trait/bound_reduction2.rs +++ b/src/test/ui/type-alias-impl-trait/bound_reduction2.rs @@ -1,4 +1,7 @@ -#![feature(type_alias_impl_trait)] +// revisions: min_tait full_tait +#![feature(min_type_alias_impl_trait)] +#![cfg_attr(full_tait, feature(type_alias_impl_trait))] +//[full_tait]~^ WARN incomplete fn main() {} diff --git a/src/test/ui/type-alias-impl-trait/bound_reduction2.stderr b/src/test/ui/type-alias-impl-trait/bound_reduction2.stderr deleted file mode 100644 index c9d6a43b9094a..0000000000000 --- a/src/test/ui/type-alias-impl-trait/bound_reduction2.stderr +++ /dev/null @@ -1,14 +0,0 @@ -error: non-defining opaque type use in defining scope - --> $DIR/bound_reduction2.rs:15: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 - | -LL | type Foo<V> = impl Trait<V>; - | ^ - -error: aborting due to previous error - diff --git a/src/test/ui/type-alias-impl-trait/bounds-are-checked-2.full_tait.stderr b/src/test/ui/type-alias-impl-trait/bounds-are-checked-2.full_tait.stderr new file mode 100644 index 0000000000000..cfb1fe9c19a8b --- /dev/null +++ b/src/test/ui/type-alias-impl-trait/bounds-are-checked-2.full_tait.stderr @@ -0,0 +1,23 @@ +warning: the feature `type_alias_impl_trait` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/bounds-are-checked-2.rs:6:32 + | +LL | #![cfg_attr(full_tait, feature(type_alias_impl_trait))] + | ^^^^^^^^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #63063 <https://github.com/rust-lang/rust/issues/63063> for more information + +error[E0277]: the trait bound `T: Clone` is not satisfied + --> $DIR/bounds-are-checked-2.rs:9:13 + | +LL | type X<T> = impl Clone; + | ^^^^^^^^^^ the trait `Clone` is not implemented for `T` + | +help: consider restricting type parameter `T` + | +LL | type X<T: std::clone::Clone> = impl Clone; + | ^^^^^^^^^^^^^^^^^^^ + +error: aborting due to previous error; 1 warning emitted + +For more information about this error, try `rustc --explain E0277`. diff --git a/src/test/ui/type-alias-impl-trait/bounds-are-checked-2.min_tait.stderr b/src/test/ui/type-alias-impl-trait/bounds-are-checked-2.min_tait.stderr new file mode 100644 index 0000000000000..735b96d5df98e --- /dev/null +++ b/src/test/ui/type-alias-impl-trait/bounds-are-checked-2.min_tait.stderr @@ -0,0 +1,14 @@ +error[E0277]: the trait bound `T: Clone` is not satisfied + --> $DIR/bounds-are-checked-2.rs:9:13 + | +LL | type X<T> = impl Clone; + | ^^^^^^^^^^ the trait `Clone` is not implemented for `T` + | +help: consider restricting type parameter `T` + | +LL | type X<T: std::clone::Clone> = impl Clone; + | ^^^^^^^^^^^^^^^^^^^ + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0277`. diff --git a/src/test/ui/type-alias-impl-trait/bounds-are-checked-2.rs b/src/test/ui/type-alias-impl-trait/bounds-are-checked-2.rs index c0359159aebea..fecc2543bce19 100644 --- a/src/test/ui/type-alias-impl-trait/bounds-are-checked-2.rs +++ b/src/test/ui/type-alias-impl-trait/bounds-are-checked-2.rs @@ -1,7 +1,10 @@ // Make sure that we check that impl trait types implement the traits that they // claim to. -#![feature(type_alias_impl_trait)] +// revisions: min_tait full_tait +#![feature(min_type_alias_impl_trait)] +#![cfg_attr(full_tait, feature(type_alias_impl_trait))] +//[full_tait]~^ WARN incomplete type X<T> = impl Clone; //~^ ERROR the trait bound `T: Clone` is not satisfied diff --git a/src/test/ui/type-alias-impl-trait/bounds-are-checked-2.stderr b/src/test/ui/type-alias-impl-trait/bounds-are-checked-2.stderr deleted file mode 100644 index 26a2f4135cb0a..0000000000000 --- a/src/test/ui/type-alias-impl-trait/bounds-are-checked-2.stderr +++ /dev/null @@ -1,14 +0,0 @@ -error[E0277]: the trait bound `T: Clone` is not satisfied - --> $DIR/bounds-are-checked-2.rs:6:13 - | -LL | type X<T> = impl Clone; - | ^^^^^^^^^^ the trait `Clone` is not implemented for `T` - | -help: consider restricting type parameter `T` - | -LL | type X<T: Clone> = impl Clone; - | ^^^^^^^ - -error: aborting due to previous error - -For more information about this error, try `rustc --explain E0277`. diff --git a/src/test/ui/type-alias-impl-trait/bounds-are-checked.full_tait.stderr b/src/test/ui/type-alias-impl-trait/bounds-are-checked.full_tait.stderr new file mode 100644 index 0000000000000..ca89421cd8bf1 --- /dev/null +++ b/src/test/ui/type-alias-impl-trait/bounds-are-checked.full_tait.stderr @@ -0,0 +1,35 @@ +warning: the feature `type_alias_impl_trait` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/bounds-are-checked.rs:6:32 + | +LL | #![cfg_attr(full_tait, feature(type_alias_impl_trait))] + | ^^^^^^^^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #63063 <https://github.com/rust-lang/rust/issues/63063> for more information + +warning: unnecessary lifetime parameter `'a` + --> $DIR/bounds-are-checked.rs:12:6 + | +LL | fn f<'a: 'static>(t: &'a str) -> X<'a> { + | ^^^^^^^^^^^ + | + = help: you can use the `'static` lifetime directly, in place of `'a` + +error[E0308]: mismatched types + --> $DIR/bounds-are-checked.rs:9:14 + | +LL | type X<'a> = impl Into<&'static str> + From<&'a str>; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ lifetime mismatch + | + = note: expected trait `From<&'a str>` + found trait `From<&'static str>` +note: the lifetime `'a` as defined on the item at 9:8... + --> $DIR/bounds-are-checked.rs:9:8 + | +LL | type X<'a> = impl Into<&'static str> + From<&'a str>; + | ^^ + = note: ...does not necessarily outlive the static lifetime + +error: aborting due to previous error; 2 warnings emitted + +For more information about this error, try `rustc --explain E0308`. diff --git a/src/test/ui/type-alias-impl-trait/bounds-are-checked.min_tait.stderr b/src/test/ui/type-alias-impl-trait/bounds-are-checked.min_tait.stderr new file mode 100644 index 0000000000000..9a451cb108373 --- /dev/null +++ b/src/test/ui/type-alias-impl-trait/bounds-are-checked.min_tait.stderr @@ -0,0 +1,26 @@ +warning: unnecessary lifetime parameter `'a` + --> $DIR/bounds-are-checked.rs:12:6 + | +LL | fn f<'a: 'static>(t: &'a str) -> X<'a> { + | ^^^^^^^^^^^ + | + = help: you can use the `'static` lifetime directly, in place of `'a` + +error[E0308]: mismatched types + --> $DIR/bounds-are-checked.rs:9:14 + | +LL | type X<'a> = impl Into<&'static str> + From<&'a str>; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ lifetime mismatch + | + = note: expected trait `From<&'a str>` + found trait `From<&'static str>` +note: the lifetime `'a` as defined on the item at 9:8... + --> $DIR/bounds-are-checked.rs:9:8 + | +LL | type X<'a> = impl Into<&'static str> + From<&'a str>; + | ^^ + = note: ...does not necessarily outlive the static lifetime + +error: aborting due to previous error; 1 warning emitted + +For more information about this error, try `rustc --explain E0308`. diff --git a/src/test/ui/type-alias-impl-trait/bounds-are-checked.rs b/src/test/ui/type-alias-impl-trait/bounds-are-checked.rs index 759bf4f4f0dee..5e1faaa3b0839 100644 --- a/src/test/ui/type-alias-impl-trait/bounds-are-checked.rs +++ b/src/test/ui/type-alias-impl-trait/bounds-are-checked.rs @@ -1,7 +1,10 @@ // Make sure that we check that impl trait types implement the traits that they // claim to. -#![feature(type_alias_impl_trait)] +// revisions: min_tait full_tait +#![feature(min_type_alias_impl_trait)] +#![cfg_attr(full_tait, feature(type_alias_impl_trait))] +//[full_tait]~^ WARN incomplete type X<'a> = impl Into<&'static str> + From<&'a str>; //~^ ERROR mismatched types diff --git a/src/test/ui/type-alias-impl-trait/bounds-are-checked.stderr b/src/test/ui/type-alias-impl-trait/bounds-are-checked.stderr deleted file mode 100644 index d5fafe05887b9..0000000000000 --- a/src/test/ui/type-alias-impl-trait/bounds-are-checked.stderr +++ /dev/null @@ -1,26 +0,0 @@ -warning: unnecessary lifetime parameter `'a` - --> $DIR/bounds-are-checked.rs:9:6 - | -LL | fn f<'a: 'static>(t: &'a str) -> X<'a> { - | ^^^^^^^^^^^ - | - = help: you can use the `'static` lifetime directly, in place of `'a` - -error[E0308]: mismatched types - --> $DIR/bounds-are-checked.rs:6:14 - | -LL | type X<'a> = impl Into<&'static str> + From<&'a str>; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ lifetime mismatch - | - = note: expected trait `From<&'a str>` - found trait `From<&'static str>` -note: the lifetime `'a` as defined on the item at 6:8... - --> $DIR/bounds-are-checked.rs:6:8 - | -LL | type X<'a> = impl Into<&'static str> + From<&'a str>; - | ^^ - = note: ...does not necessarily outlive the static lifetime - -error: aborting due to previous error; 1 warning emitted - -For more information about this error, try `rustc --explain E0308`. diff --git a/src/test/ui/type-alias-impl-trait/coherence.full_tait.stderr b/src/test/ui/type-alias-impl-trait/coherence.full_tait.stderr new file mode 100644 index 0000000000000..68de0c961ba1c --- /dev/null +++ b/src/test/ui/type-alias-impl-trait/coherence.full_tait.stderr @@ -0,0 +1,18 @@ +warning: the feature `type_alias_impl_trait` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/coherence.rs:4:32 + | +LL | #![cfg_attr(full_tait, feature(type_alias_impl_trait))] + | ^^^^^^^^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #63063 <https://github.com/rust-lang/rust/issues/63063> for more information + +error[E0207]: the type parameter `T` is not constrained by the impl trait, self type, or predicates + --> $DIR/coherence.rs:17:6 + | +LL | impl<T> foreign_crate::ForeignTrait for AliasOfForeignType<T> {} + | ^ unconstrained type parameter + +error: aborting due to previous error; 1 warning emitted + +For more information about this error, try `rustc --explain E0207`. diff --git a/src/test/ui/type-alias-impl-trait/coherence.min_tait.stderr b/src/test/ui/type-alias-impl-trait/coherence.min_tait.stderr new file mode 100644 index 0000000000000..4da52369fdb5d --- /dev/null +++ b/src/test/ui/type-alias-impl-trait/coherence.min_tait.stderr @@ -0,0 +1,9 @@ +error[E0207]: the type parameter `T` is not constrained by the impl trait, self type, or predicates + --> $DIR/coherence.rs:17:6 + | +LL | impl<T> foreign_crate::ForeignTrait for AliasOfForeignType<T> {} + | ^ unconstrained type parameter + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0207`. diff --git a/src/test/ui/type-alias-impl-trait/coherence.rs b/src/test/ui/type-alias-impl-trait/coherence.rs index 1c0f83d6c12da..51babb0e61bd3 100644 --- a/src/test/ui/type-alias-impl-trait/coherence.rs +++ b/src/test/ui/type-alias-impl-trait/coherence.rs @@ -1,5 +1,8 @@ // aux-build:foreign-crate.rs -#![feature(type_alias_impl_trait)] +// revisions: min_tait full_tait +#![feature(min_type_alias_impl_trait)] +#![cfg_attr(full_tait, feature(type_alias_impl_trait))] +//[full_tait]~^ WARN incomplete extern crate foreign_crate; diff --git a/src/test/ui/type-alias-impl-trait/coherence.stderr b/src/test/ui/type-alias-impl-trait/coherence.stderr deleted file mode 100644 index 6ede0fa14ba70..0000000000000 --- a/src/test/ui/type-alias-impl-trait/coherence.stderr +++ /dev/null @@ -1,9 +0,0 @@ -error[E0207]: the type parameter `T` is not constrained by the impl trait, self type, or predicates - --> $DIR/coherence.rs:14:6 - | -LL | impl<T> foreign_crate::ForeignTrait for AliasOfForeignType<T> {} - | ^ unconstrained type parameter - -error: aborting due to previous error - -For more information about this error, try `rustc --explain E0207`. diff --git a/src/test/ui/type-alias-impl-trait/declared_but_never_defined.full_tait.stderr b/src/test/ui/type-alias-impl-trait/declared_but_never_defined.full_tait.stderr new file mode 100644 index 0000000000000..5fc79cf9561fa --- /dev/null +++ b/src/test/ui/type-alias-impl-trait/declared_but_never_defined.full_tait.stderr @@ -0,0 +1,17 @@ +warning: the feature `type_alias_impl_trait` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/declared_but_never_defined.rs:3:32 + | +LL | #![cfg_attr(full_tait, feature(type_alias_impl_trait))] + | ^^^^^^^^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #63063 <https://github.com/rust-lang/rust/issues/63063> for more information + +error: could not find defining uses + --> $DIR/declared_but_never_defined.rs:9:12 + | +LL | type Bar = impl std::fmt::Debug; + | ^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to previous error; 1 warning emitted + diff --git a/src/test/ui/type-alias-impl-trait/declared_but_never_defined.min_tait.stderr b/src/test/ui/type-alias-impl-trait/declared_but_never_defined.min_tait.stderr new file mode 100644 index 0000000000000..b731e41b149a8 --- /dev/null +++ b/src/test/ui/type-alias-impl-trait/declared_but_never_defined.min_tait.stderr @@ -0,0 +1,8 @@ +error: could not find defining uses + --> $DIR/declared_but_never_defined.rs:9:12 + | +LL | type Bar = impl std::fmt::Debug; + | ^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to previous error + diff --git a/src/test/ui/type-alias-impl-trait/declared_but_never_defined.rs b/src/test/ui/type-alias-impl-trait/declared_but_never_defined.rs index c4bf56a919721..ac92bea8c47a4 100644 --- a/src/test/ui/type-alias-impl-trait/declared_but_never_defined.rs +++ b/src/test/ui/type-alias-impl-trait/declared_but_never_defined.rs @@ -1,4 +1,7 @@ -#![feature(type_alias_impl_trait)] +// revisions: min_tait full_tait +#![feature(min_type_alias_impl_trait)] +#![cfg_attr(full_tait, feature(type_alias_impl_trait))] +//[full_tait]~^ WARN incomplete fn main() {} diff --git a/src/test/ui/type-alias-impl-trait/declared_but_never_defined.stderr b/src/test/ui/type-alias-impl-trait/declared_but_never_defined.stderr deleted file mode 100644 index 21c2e8a9db618..0000000000000 --- a/src/test/ui/type-alias-impl-trait/declared_but_never_defined.stderr +++ /dev/null @@ -1,8 +0,0 @@ -error: could not find defining uses - --> $DIR/declared_but_never_defined.rs:6:12 - | -LL | type Bar = impl std::fmt::Debug; - | ^^^^^^^^^^^^^^^^^^^^ - -error: aborting due to previous error - diff --git a/src/test/ui/type-alias-impl-trait/declared_but_not_defined_in_scope.full_tait.stderr b/src/test/ui/type-alias-impl-trait/declared_but_not_defined_in_scope.full_tait.stderr new file mode 100644 index 0000000000000..eeccc598f529c --- /dev/null +++ b/src/test/ui/type-alias-impl-trait/declared_but_not_defined_in_scope.full_tait.stderr @@ -0,0 +1,32 @@ +warning: the feature `type_alias_impl_trait` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/declared_but_not_defined_in_scope.rs:3:32 + | +LL | #![cfg_attr(full_tait, feature(type_alias_impl_trait))] + | ^^^^^^^^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #63063 <https://github.com/rust-lang/rust/issues/63063> for more information + +error: could not find defining uses + --> $DIR/declared_but_not_defined_in_scope.rs:10:20 + | +LL | pub type Boo = impl ::std::fmt::Debug; + | ^^^^^^^^^^^^^^^^^^^^^^ + +error[E0308]: mismatched types + --> $DIR/declared_but_not_defined_in_scope.rs:14:5 + | +LL | pub type Boo = impl ::std::fmt::Debug; + | ---------------------- the expected opaque type +... +LL | fn bomp() -> boo::Boo { + | -------- expected `impl Debug` because of return type +LL | "" + | ^^ expected opaque type, found `&str` + | + = note: expected opaque type `impl Debug` + found reference `&'static str` + +error: aborting due to 2 previous errors; 1 warning emitted + +For more information about this error, try `rustc --explain E0308`. diff --git a/src/test/ui/type-alias-impl-trait/declared_but_not_defined_in_scope.min_tait.stderr b/src/test/ui/type-alias-impl-trait/declared_but_not_defined_in_scope.min_tait.stderr new file mode 100644 index 0000000000000..20057c3aa5185 --- /dev/null +++ b/src/test/ui/type-alias-impl-trait/declared_but_not_defined_in_scope.min_tait.stderr @@ -0,0 +1,23 @@ +error: could not find defining uses + --> $DIR/declared_but_not_defined_in_scope.rs:10:20 + | +LL | pub type Boo = impl ::std::fmt::Debug; + | ^^^^^^^^^^^^^^^^^^^^^^ + +error[E0308]: mismatched types + --> $DIR/declared_but_not_defined_in_scope.rs:14:5 + | +LL | pub type Boo = impl ::std::fmt::Debug; + | ---------------------- the expected opaque type +... +LL | fn bomp() -> boo::Boo { + | -------- expected `impl Debug` because of return type +LL | "" + | ^^ expected opaque type, found `&str` + | + = note: expected opaque type `impl Debug` + found reference `&'static str` + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0308`. diff --git a/src/test/ui/type-alias-impl-trait/declared_but_not_defined_in_scope.rs b/src/test/ui/type-alias-impl-trait/declared_but_not_defined_in_scope.rs index 7ea517eb734a4..2bbae98db448c 100644 --- a/src/test/ui/type-alias-impl-trait/declared_but_not_defined_in_scope.rs +++ b/src/test/ui/type-alias-impl-trait/declared_but_not_defined_in_scope.rs @@ -1,4 +1,7 @@ -#![feature(type_alias_impl_trait)] +// revisions: min_tait full_tait +#![feature(min_type_alias_impl_trait)] +#![cfg_attr(full_tait, feature(type_alias_impl_trait))] +//[full_tait]~^ WARN incomplete fn main() {} diff --git a/src/test/ui/type-alias-impl-trait/declared_but_not_defined_in_scope.stderr b/src/test/ui/type-alias-impl-trait/declared_but_not_defined_in_scope.stderr deleted file mode 100644 index 0b4c262bbb43b..0000000000000 --- a/src/test/ui/type-alias-impl-trait/declared_but_not_defined_in_scope.stderr +++ /dev/null @@ -1,23 +0,0 @@ -error: could not find defining uses - --> $DIR/declared_but_not_defined_in_scope.rs:7:20 - | -LL | pub type Boo = impl ::std::fmt::Debug; - | ^^^^^^^^^^^^^^^^^^^^^^ - -error[E0308]: mismatched types - --> $DIR/declared_but_not_defined_in_scope.rs:11:5 - | -LL | pub type Boo = impl ::std::fmt::Debug; - | ---------------------- the expected opaque type -... -LL | fn bomp() -> boo::Boo { - | -------- expected `impl Debug` because of return type -LL | "" - | ^^ expected opaque type, found `&str` - | - = note: expected opaque type `impl Debug` - found reference `&'static str` - -error: aborting due to 2 previous errors - -For more information about this error, try `rustc --explain E0308`. diff --git a/src/test/ui/type-alias-impl-trait/different_defining_uses.full_tait.stderr b/src/test/ui/type-alias-impl-trait/different_defining_uses.full_tait.stderr new file mode 100644 index 0000000000000..60fa141884db3 --- /dev/null +++ b/src/test/ui/type-alias-impl-trait/different_defining_uses.full_tait.stderr @@ -0,0 +1,23 @@ +warning: the feature `type_alias_impl_trait` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/different_defining_uses.rs:3:32 + | +LL | #![cfg_attr(full_tait, feature(type_alias_impl_trait))] + | ^^^^^^^^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #63063 <https://github.com/rust-lang/rust/issues/63063> for more information + +error: concrete type differs from previous defining opaque type use + --> $DIR/different_defining_uses.rs:15:1 + | +LL | fn bar() -> Foo { + | ^^^^^^^^^^^^^^^ expected `&'static str`, got `i32` + | +note: previous use here + --> $DIR/different_defining_uses.rs:11:1 + | +LL | fn foo() -> Foo { + | ^^^^^^^^^^^^^^^ + +error: aborting due to previous error; 1 warning emitted + diff --git a/src/test/ui/type-alias-impl-trait/different_defining_uses.min_tait.stderr b/src/test/ui/type-alias-impl-trait/different_defining_uses.min_tait.stderr new file mode 100644 index 0000000000000..904ee58685cbf --- /dev/null +++ b/src/test/ui/type-alias-impl-trait/different_defining_uses.min_tait.stderr @@ -0,0 +1,14 @@ +error: concrete type differs from previous defining opaque type use + --> $DIR/different_defining_uses.rs:15:1 + | +LL | fn bar() -> Foo { + | ^^^^^^^^^^^^^^^ expected `&'static str`, got `i32` + | +note: previous use here + --> $DIR/different_defining_uses.rs:11:1 + | +LL | fn foo() -> Foo { + | ^^^^^^^^^^^^^^^ + +error: aborting due to previous error + diff --git a/src/test/ui/type-alias-impl-trait/different_defining_uses.rs b/src/test/ui/type-alias-impl-trait/different_defining_uses.rs index 2d7780a126cce..542de6b62e197 100644 --- a/src/test/ui/type-alias-impl-trait/different_defining_uses.rs +++ b/src/test/ui/type-alias-impl-trait/different_defining_uses.rs @@ -1,4 +1,7 @@ -#![feature(type_alias_impl_trait)] +// revisions: min_tait full_tait +#![feature(min_type_alias_impl_trait)] +#![cfg_attr(full_tait, feature(type_alias_impl_trait))] +//[full_tait]~^ WARN incomplete fn main() {} diff --git a/src/test/ui/type-alias-impl-trait/different_defining_uses.stderr b/src/test/ui/type-alias-impl-trait/different_defining_uses.stderr deleted file mode 100644 index eaa716bc71c3e..0000000000000 --- a/src/test/ui/type-alias-impl-trait/different_defining_uses.stderr +++ /dev/null @@ -1,14 +0,0 @@ -error: concrete type differs from previous defining opaque type use - --> $DIR/different_defining_uses.rs:12:1 - | -LL | fn bar() -> Foo { - | ^^^^^^^^^^^^^^^ expected `&'static str`, got `i32` - | -note: previous use here - --> $DIR/different_defining_uses.rs:8:1 - | -LL | fn foo() -> Foo { - | ^^^^^^^^^^^^^^^ - -error: aborting due to previous error - diff --git a/src/test/ui/type-alias-impl-trait/different_defining_uses_never_type.full_tait.stderr b/src/test/ui/type-alias-impl-trait/different_defining_uses_never_type.full_tait.stderr new file mode 100644 index 0000000000000..5c5ae7eadcbcf --- /dev/null +++ b/src/test/ui/type-alias-impl-trait/different_defining_uses_never_type.full_tait.stderr @@ -0,0 +1,35 @@ +warning: the feature `type_alias_impl_trait` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/different_defining_uses_never_type.rs:3:32 + | +LL | #![cfg_attr(full_tait, feature(type_alias_impl_trait))] + | ^^^^^^^^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #63063 <https://github.com/rust-lang/rust/issues/63063> for more information + +error: concrete type differs from previous defining opaque type use + --> $DIR/different_defining_uses_never_type.rs:15:1 + | +LL | fn bar() -> Foo { + | ^^^^^^^^^^^^^^^ expected `&'static str`, got `()` + | +note: previous use here + --> $DIR/different_defining_uses_never_type.rs:11:1 + | +LL | fn foo() -> Foo { + | ^^^^^^^^^^^^^^^ + +error: concrete type differs from previous defining opaque type use + --> $DIR/different_defining_uses_never_type.rs:19:1 + | +LL | fn boo() -> Foo { + | ^^^^^^^^^^^^^^^ expected `&'static str`, got `()` + | +note: previous use here + --> $DIR/different_defining_uses_never_type.rs:11:1 + | +LL | fn foo() -> Foo { + | ^^^^^^^^^^^^^^^ + +error: aborting due to 2 previous errors; 1 warning emitted + diff --git a/src/test/ui/type-alias-impl-trait/different_defining_uses_never_type.min_tait.stderr b/src/test/ui/type-alias-impl-trait/different_defining_uses_never_type.min_tait.stderr new file mode 100644 index 0000000000000..9cf2c58368737 --- /dev/null +++ b/src/test/ui/type-alias-impl-trait/different_defining_uses_never_type.min_tait.stderr @@ -0,0 +1,26 @@ +error: concrete type differs from previous defining opaque type use + --> $DIR/different_defining_uses_never_type.rs:15:1 + | +LL | fn bar() -> Foo { + | ^^^^^^^^^^^^^^^ expected `&'static str`, got `()` + | +note: previous use here + --> $DIR/different_defining_uses_never_type.rs:11:1 + | +LL | fn foo() -> Foo { + | ^^^^^^^^^^^^^^^ + +error: concrete type differs from previous defining opaque type use + --> $DIR/different_defining_uses_never_type.rs:19:1 + | +LL | fn boo() -> Foo { + | ^^^^^^^^^^^^^^^ expected `&'static str`, got `()` + | +note: previous use here + --> $DIR/different_defining_uses_never_type.rs:11:1 + | +LL | fn foo() -> Foo { + | ^^^^^^^^^^^^^^^ + +error: aborting due to 2 previous errors + diff --git a/src/test/ui/type-alias-impl-trait/different_defining_uses_never_type.rs b/src/test/ui/type-alias-impl-trait/different_defining_uses_never_type.rs index 289b97b00ada7..72352a74a04b6 100644 --- a/src/test/ui/type-alias-impl-trait/different_defining_uses_never_type.rs +++ b/src/test/ui/type-alias-impl-trait/different_defining_uses_never_type.rs @@ -1,4 +1,7 @@ -#![feature(type_alias_impl_trait)] +// revisions: min_tait full_tait +#![feature(min_type_alias_impl_trait)] +#![cfg_attr(full_tait, feature(type_alias_impl_trait))] +//[full_tait]~^ WARN incomplete fn main() {} diff --git a/src/test/ui/type-alias-impl-trait/different_defining_uses_never_type.stderr b/src/test/ui/type-alias-impl-trait/different_defining_uses_never_type.stderr deleted file mode 100644 index 9a587e4f06ee8..0000000000000 --- a/src/test/ui/type-alias-impl-trait/different_defining_uses_never_type.stderr +++ /dev/null @@ -1,26 +0,0 @@ -error: concrete type differs from previous defining opaque type use - --> $DIR/different_defining_uses_never_type.rs:12:1 - | -LL | fn bar() -> Foo { - | ^^^^^^^^^^^^^^^ expected `&'static str`, got `()` - | -note: previous use here - --> $DIR/different_defining_uses_never_type.rs:8:1 - | -LL | fn foo() -> Foo { - | ^^^^^^^^^^^^^^^ - -error: concrete type differs from previous defining opaque type use - --> $DIR/different_defining_uses_never_type.rs:16:1 - | -LL | fn boo() -> Foo { - | ^^^^^^^^^^^^^^^ expected `&'static str`, got `()` - | -note: previous use here - --> $DIR/different_defining_uses_never_type.rs:8:1 - | -LL | fn foo() -> Foo { - | ^^^^^^^^^^^^^^^ - -error: aborting due to 2 previous errors - diff --git a/src/test/ui/type-alias-impl-trait/different_defining_uses_never_type2.full_tait.stderr b/src/test/ui/type-alias-impl-trait/different_defining_uses_never_type2.full_tait.stderr new file mode 100644 index 0000000000000..b69622b77bfc4 --- /dev/null +++ b/src/test/ui/type-alias-impl-trait/different_defining_uses_never_type2.full_tait.stderr @@ -0,0 +1,11 @@ +warning: the feature `type_alias_impl_trait` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/different_defining_uses_never_type2.rs:5:32 + | +LL | #![cfg_attr(full_tait, feature(type_alias_impl_trait))] + | ^^^^^^^^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #63063 <https://github.com/rust-lang/rust/issues/63063> for more information + +warning: 1 warning emitted + diff --git a/src/test/ui/type-alias-impl-trait/different_defining_uses_never_type2.rs b/src/test/ui/type-alias-impl-trait/different_defining_uses_never_type2.rs index 8549687ea7814..ac9884b3e4501 100644 --- a/src/test/ui/type-alias-impl-trait/different_defining_uses_never_type2.rs +++ b/src/test/ui/type-alias-impl-trait/different_defining_uses_never_type2.rs @@ -1,6 +1,9 @@ // build-pass (FIXME(62277): could be check-pass?) -#![feature(type_alias_impl_trait)] +// revisions: min_tait full_tait +#![feature(min_type_alias_impl_trait)] +#![cfg_attr(full_tait, feature(type_alias_impl_trait))] +//[full_tait]~^ WARN incomplete fn main() {} diff --git a/src/test/ui/type-alias-impl-trait/fallback.full_tait.stderr b/src/test/ui/type-alias-impl-trait/fallback.full_tait.stderr new file mode 100644 index 0000000000000..9641d756cb03e --- /dev/null +++ b/src/test/ui/type-alias-impl-trait/fallback.full_tait.stderr @@ -0,0 +1,11 @@ +warning: the feature `type_alias_impl_trait` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/fallback.rs:7:32 + | +LL | #![cfg_attr(full_tait, feature(type_alias_impl_trait))] + | ^^^^^^^^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #63063 <https://github.com/rust-lang/rust/issues/63063> for more information + +warning: 1 warning emitted + diff --git a/src/test/ui/type-alias-impl-trait/fallback.rs b/src/test/ui/type-alias-impl-trait/fallback.rs index fe1ca2230daca..84fec75655fb2 100644 --- a/src/test/ui/type-alias-impl-trait/fallback.rs +++ b/src/test/ui/type-alias-impl-trait/fallback.rs @@ -2,7 +2,10 @@ // inference variable being completely unconstrained. // // check-pass -#![feature(type_alias_impl_trait)] +// revisions: min_tait full_tait +#![feature(min_type_alias_impl_trait)] +#![cfg_attr(full_tait, feature(type_alias_impl_trait))] +//[full_tait]~^ WARN incomplete type Foo = impl Copy; diff --git a/src/test/ui/type-alias-impl-trait/generic_different_defining_uses.full_tait.stderr b/src/test/ui/type-alias-impl-trait/generic_different_defining_uses.full_tait.stderr new file mode 100644 index 0000000000000..972e5d9428825 --- /dev/null +++ b/src/test/ui/type-alias-impl-trait/generic_different_defining_uses.full_tait.stderr @@ -0,0 +1,23 @@ +warning: the feature `type_alias_impl_trait` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/generic_different_defining_uses.rs:3:32 + | +LL | #![cfg_attr(full_tait, feature(type_alias_impl_trait))] + | ^^^^^^^^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #63063 <https://github.com/rust-lang/rust/issues/63063> for more information + +error: concrete type differs from previous defining opaque type use + --> $DIR/generic_different_defining_uses.rs:14:1 + | +LL | fn my_iter2<T>(t: T) -> MyIter<T> { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `std::iter::Once<T>`, got `std::option::IntoIter<T>` + | +note: previous use here + --> $DIR/generic_different_defining_uses.rs:10:1 + | +LL | fn my_iter<T>(t: T) -> MyIter<T> { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to previous error; 1 warning emitted + diff --git a/src/test/ui/type-alias-impl-trait/generic_different_defining_uses.min_tait.stderr b/src/test/ui/type-alias-impl-trait/generic_different_defining_uses.min_tait.stderr new file mode 100644 index 0000000000000..911683862c263 --- /dev/null +++ b/src/test/ui/type-alias-impl-trait/generic_different_defining_uses.min_tait.stderr @@ -0,0 +1,14 @@ +error: concrete type differs from previous defining opaque type use + --> $DIR/generic_different_defining_uses.rs:14:1 + | +LL | fn my_iter2<T>(t: T) -> MyIter<T> { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `std::iter::Once<T>`, got `std::option::IntoIter<T>` + | +note: previous use here + --> $DIR/generic_different_defining_uses.rs:10:1 + | +LL | fn my_iter<T>(t: T) -> MyIter<T> { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to previous error + diff --git a/src/test/ui/type-alias-impl-trait/generic_different_defining_uses.rs b/src/test/ui/type-alias-impl-trait/generic_different_defining_uses.rs index ac87c2d446a89..4eb603df583a8 100644 --- a/src/test/ui/type-alias-impl-trait/generic_different_defining_uses.rs +++ b/src/test/ui/type-alias-impl-trait/generic_different_defining_uses.rs @@ -1,4 +1,7 @@ -#![feature(type_alias_impl_trait)] +// revisions: min_tait full_tait +#![feature(min_type_alias_impl_trait)] +#![cfg_attr(full_tait, feature(type_alias_impl_trait))] +//[full_tait]~^ WARN incomplete fn main() {} diff --git a/src/test/ui/type-alias-impl-trait/generic_different_defining_uses.stderr b/src/test/ui/type-alias-impl-trait/generic_different_defining_uses.stderr deleted file mode 100644 index f8a058170e37f..0000000000000 --- a/src/test/ui/type-alias-impl-trait/generic_different_defining_uses.stderr +++ /dev/null @@ -1,14 +0,0 @@ -error: concrete type differs from previous defining opaque type use - --> $DIR/generic_different_defining_uses.rs:11:1 - | -LL | fn my_iter2<T>(t: T) -> MyIter<T> { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `std::iter::Once<T>`, got `std::option::IntoIter<T>` - | -note: previous use here - --> $DIR/generic_different_defining_uses.rs:7:1 - | -LL | fn my_iter<T>(t: T) -> MyIter<T> { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: aborting due to previous error - diff --git a/src/test/ui/type-alias-impl-trait/generic_duplicate_lifetime_param.full_tait.stderr b/src/test/ui/type-alias-impl-trait/generic_duplicate_lifetime_param.full_tait.stderr new file mode 100644 index 0000000000000..ecf0e39ed6f61 --- /dev/null +++ b/src/test/ui/type-alias-impl-trait/generic_duplicate_lifetime_param.full_tait.stderr @@ -0,0 +1,23 @@ +warning: the feature `type_alias_impl_trait` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/generic_duplicate_lifetime_param.rs:3:32 + | +LL | #![cfg_attr(full_tait, feature(type_alias_impl_trait))] + | ^^^^^^^^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #63063 <https://github.com/rust-lang/rust/issues/63063> for more information + +error: non-defining opaque type use in defining scope + --> $DIR/generic_duplicate_lifetime_param.rs:10:26 + | +LL | fn one<'a>(t: &'a ()) -> Two<'a, 'a> { + | ^^^^^^^^^^^ + | +note: lifetime used multiple times + --> $DIR/generic_duplicate_lifetime_param.rs:8:10 + | +LL | type Two<'a, 'b> = impl std::fmt::Debug; + | ^^ ^^ + +error: aborting due to previous error; 1 warning emitted + diff --git a/src/test/ui/type-alias-impl-trait/generic_duplicate_lifetime_param.min_tait.stderr b/src/test/ui/type-alias-impl-trait/generic_duplicate_lifetime_param.min_tait.stderr new file mode 100644 index 0000000000000..1aad312056bb5 --- /dev/null +++ b/src/test/ui/type-alias-impl-trait/generic_duplicate_lifetime_param.min_tait.stderr @@ -0,0 +1,14 @@ +error: non-defining opaque type use in defining scope + --> $DIR/generic_duplicate_lifetime_param.rs:10:26 + | +LL | fn one<'a>(t: &'a ()) -> Two<'a, 'a> { + | ^^^^^^^^^^^ + | +note: lifetime used multiple times + --> $DIR/generic_duplicate_lifetime_param.rs:8:10 + | +LL | type Two<'a, 'b> = impl std::fmt::Debug; + | ^^ ^^ + +error: aborting due to previous error + diff --git a/src/test/ui/type-alias-impl-trait/generic_duplicate_lifetime_param.rs b/src/test/ui/type-alias-impl-trait/generic_duplicate_lifetime_param.rs index c18a711675876..d838d497238d5 100644 --- a/src/test/ui/type-alias-impl-trait/generic_duplicate_lifetime_param.rs +++ b/src/test/ui/type-alias-impl-trait/generic_duplicate_lifetime_param.rs @@ -1,4 +1,7 @@ -#![feature(type_alias_impl_trait)] +// revisions: min_tait full_tait +#![feature(min_type_alias_impl_trait)] +#![cfg_attr(full_tait, feature(type_alias_impl_trait))] +//[full_tait]~^ WARN incomplete fn main() {} diff --git a/src/test/ui/type-alias-impl-trait/generic_duplicate_lifetime_param.stderr b/src/test/ui/type-alias-impl-trait/generic_duplicate_lifetime_param.stderr deleted file mode 100644 index 08b26b8fc1307..0000000000000 --- a/src/test/ui/type-alias-impl-trait/generic_duplicate_lifetime_param.stderr +++ /dev/null @@ -1,14 +0,0 @@ -error: non-defining opaque type use in defining scope - --> $DIR/generic_duplicate_lifetime_param.rs:7:26 - | -LL | fn one<'a>(t: &'a ()) -> Two<'a, 'a> { - | ^^^^^^^^^^^ - | -note: lifetime used multiple times - --> $DIR/generic_duplicate_lifetime_param.rs:5:10 - | -LL | type Two<'a, 'b> = impl std::fmt::Debug; - | ^^ ^^ - -error: aborting due to previous error - diff --git a/src/test/ui/type-alias-impl-trait/generic_duplicate_param_use.full_tait.stderr b/src/test/ui/type-alias-impl-trait/generic_duplicate_param_use.full_tait.stderr new file mode 100644 index 0000000000000..805cea7127ddf --- /dev/null +++ b/src/test/ui/type-alias-impl-trait/generic_duplicate_param_use.full_tait.stderr @@ -0,0 +1,38 @@ +error: non-defining opaque type use in defining scope + --> $DIR/generic_duplicate_param_use.rs:16:30 + | +LL | fn one_ty<T: Debug>(t: T) -> TwoTys<T, T> { + | ^^^^^^^^^^^^ + | +note: type used multiple times + --> $DIR/generic_duplicate_param_use.rs:12:13 + | +LL | type TwoTys<T, U> = impl Debug; + | ^ ^ + +error: non-defining opaque type use in defining scope + --> $DIR/generic_duplicate_param_use.rs:21:36 + | +LL | fn one_lifetime<'a>(t: &'a u32) -> TwoLifetimes<'a, 'a> { + | ^^^^^^^^^^^^^^^^^^^^ + | +note: lifetime used multiple times + --> $DIR/generic_duplicate_param_use.rs:13:19 + | +LL | type TwoLifetimes<'a, 'b> = impl Debug; + | ^^ ^^ + +error: non-defining opaque type use in defining scope + --> $DIR/generic_duplicate_param_use.rs:26:50 + | +LL | fn one_const<const N: usize>(t: *mut [u8; N]) -> TwoConsts<N, N> { + | ^^^^^^^^^^^^^^^ + | +note: constant used multiple times + --> $DIR/generic_duplicate_param_use.rs:14:22 + | +LL | type TwoConsts<const X: usize, const Y: usize> = impl Debug; + | ^ ^ + +error: aborting due to 3 previous errors + diff --git a/src/test/ui/type-alias-impl-trait/generic_duplicate_param_use.min_tait.stderr b/src/test/ui/type-alias-impl-trait/generic_duplicate_param_use.min_tait.stderr new file mode 100644 index 0000000000000..805cea7127ddf --- /dev/null +++ b/src/test/ui/type-alias-impl-trait/generic_duplicate_param_use.min_tait.stderr @@ -0,0 +1,38 @@ +error: non-defining opaque type use in defining scope + --> $DIR/generic_duplicate_param_use.rs:16:30 + | +LL | fn one_ty<T: Debug>(t: T) -> TwoTys<T, T> { + | ^^^^^^^^^^^^ + | +note: type used multiple times + --> $DIR/generic_duplicate_param_use.rs:12:13 + | +LL | type TwoTys<T, U> = impl Debug; + | ^ ^ + +error: non-defining opaque type use in defining scope + --> $DIR/generic_duplicate_param_use.rs:21:36 + | +LL | fn one_lifetime<'a>(t: &'a u32) -> TwoLifetimes<'a, 'a> { + | ^^^^^^^^^^^^^^^^^^^^ + | +note: lifetime used multiple times + --> $DIR/generic_duplicate_param_use.rs:13:19 + | +LL | type TwoLifetimes<'a, 'b> = impl Debug; + | ^^ ^^ + +error: non-defining opaque type use in defining scope + --> $DIR/generic_duplicate_param_use.rs:26:50 + | +LL | fn one_const<const N: usize>(t: *mut [u8; N]) -> TwoConsts<N, N> { + | ^^^^^^^^^^^^^^^ + | +note: constant used multiple times + --> $DIR/generic_duplicate_param_use.rs:14:22 + | +LL | type TwoConsts<const X: usize, const Y: usize> = impl Debug; + | ^ ^ + +error: aborting due to 3 previous errors + diff --git a/src/test/ui/type-alias-impl-trait/generic_duplicate_param_use.rs b/src/test/ui/type-alias-impl-trait/generic_duplicate_param_use.rs index 4503607a83638..12ff5a2e713d8 100644 --- a/src/test/ui/type-alias-impl-trait/generic_duplicate_param_use.rs +++ b/src/test/ui/type-alias-impl-trait/generic_duplicate_param_use.rs @@ -1,4 +1,7 @@ -#![feature(type_alias_impl_trait, const_generics)] +#![feature(const_generics)] +// revisions: min_tait full_tait +#![feature(min_type_alias_impl_trait)] +#![cfg_attr(full_tait, feature(type_alias_impl_trait))] #![allow(incomplete_features)] use std::fmt::Debug; diff --git a/src/test/ui/type-alias-impl-trait/generic_duplicate_param_use.stderr b/src/test/ui/type-alias-impl-trait/generic_duplicate_param_use.stderr deleted file mode 100644 index b4757e2763d06..0000000000000 --- a/src/test/ui/type-alias-impl-trait/generic_duplicate_param_use.stderr +++ /dev/null @@ -1,38 +0,0 @@ -error: non-defining opaque type use in defining scope - --> $DIR/generic_duplicate_param_use.rs:13:30 - | -LL | fn one_ty<T: Debug>(t: T) -> TwoTys<T, T> { - | ^^^^^^^^^^^^ - | -note: type used multiple times - --> $DIR/generic_duplicate_param_use.rs:9:13 - | -LL | type TwoTys<T, U> = impl Debug; - | ^ ^ - -error: non-defining opaque type use in defining scope - --> $DIR/generic_duplicate_param_use.rs:18:36 - | -LL | fn one_lifetime<'a>(t: &'a u32) -> TwoLifetimes<'a, 'a> { - | ^^^^^^^^^^^^^^^^^^^^ - | -note: lifetime used multiple times - --> $DIR/generic_duplicate_param_use.rs:10:19 - | -LL | type TwoLifetimes<'a, 'b> = impl Debug; - | ^^ ^^ - -error: non-defining opaque type use in defining scope - --> $DIR/generic_duplicate_param_use.rs:23:50 - | -LL | fn one_const<const N: usize>(t: *mut [u8; N]) -> TwoConsts<N, N> { - | ^^^^^^^^^^^^^^^ - | -note: constant used multiple times - --> $DIR/generic_duplicate_param_use.rs:11:22 - | -LL | type TwoConsts<const X: usize, const Y: usize> = impl Debug; - | ^ ^ - -error: aborting due to 3 previous errors - diff --git a/src/test/ui/type-alias-impl-trait/generic_duplicate_param_use10.full_tait.stderr b/src/test/ui/type-alias-impl-trait/generic_duplicate_param_use10.full_tait.stderr new file mode 100644 index 0000000000000..ef7d4f1be4eea --- /dev/null +++ b/src/test/ui/type-alias-impl-trait/generic_duplicate_param_use10.full_tait.stderr @@ -0,0 +1,11 @@ +warning: the feature `type_alias_impl_trait` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/generic_duplicate_param_use10.rs:4:32 + | +LL | #![cfg_attr(full_tait, feature(type_alias_impl_trait))] + | ^^^^^^^^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #63063 <https://github.com/rust-lang/rust/issues/63063> for more information + +warning: 1 warning emitted + diff --git a/src/test/ui/type-alias-impl-trait/generic_duplicate_param_use10.rs b/src/test/ui/type-alias-impl-trait/generic_duplicate_param_use10.rs index c17d595dbb3ad..c7a7ded2127a9 100644 --- a/src/test/ui/type-alias-impl-trait/generic_duplicate_param_use10.rs +++ b/src/test/ui/type-alias-impl-trait/generic_duplicate_param_use10.rs @@ -1,5 +1,8 @@ // check-pass -#![feature(type_alias_impl_trait)] +// revisions: min_tait full_tait +#![feature(min_type_alias_impl_trait)] +#![cfg_attr(full_tait, feature(type_alias_impl_trait))] +//[full_tait]~^ WARN incomplete use std::fmt::Debug; diff --git a/src/test/ui/type-alias-impl-trait/generic_duplicate_param_use2.full_tait.stderr b/src/test/ui/type-alias-impl-trait/generic_duplicate_param_use2.full_tait.stderr new file mode 100644 index 0000000000000..357d477c9f1e2 --- /dev/null +++ b/src/test/ui/type-alias-impl-trait/generic_duplicate_param_use2.full_tait.stderr @@ -0,0 +1,23 @@ +warning: the feature `type_alias_impl_trait` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/generic_duplicate_param_use2.rs:3:32 + | +LL | #![cfg_attr(full_tait, feature(type_alias_impl_trait))] + | ^^^^^^^^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #63063 <https://github.com/rust-lang/rust/issues/63063> for more information + +error: non-defining opaque type use in defining scope + --> $DIR/generic_duplicate_param_use2.rs:13:27 + | +LL | fn one<T: Debug>(t: T) -> Two<T, T> { + | ^^^^^^^^^ + | +note: type used multiple times + --> $DIR/generic_duplicate_param_use2.rs:11:10 + | +LL | type Two<T, U> = impl Debug; + | ^ ^ + +error: aborting due to previous error; 1 warning emitted + diff --git a/src/test/ui/type-alias-impl-trait/generic_duplicate_param_use2.min_tait.stderr b/src/test/ui/type-alias-impl-trait/generic_duplicate_param_use2.min_tait.stderr new file mode 100644 index 0000000000000..805ab67816f97 --- /dev/null +++ b/src/test/ui/type-alias-impl-trait/generic_duplicate_param_use2.min_tait.stderr @@ -0,0 +1,14 @@ +error: non-defining opaque type use in defining scope + --> $DIR/generic_duplicate_param_use2.rs:13:27 + | +LL | fn one<T: Debug>(t: T) -> Two<T, T> { + | ^^^^^^^^^ + | +note: type used multiple times + --> $DIR/generic_duplicate_param_use2.rs:11:10 + | +LL | type Two<T, U> = impl Debug; + | ^ ^ + +error: aborting due to previous error + diff --git a/src/test/ui/type-alias-impl-trait/generic_duplicate_param_use2.rs b/src/test/ui/type-alias-impl-trait/generic_duplicate_param_use2.rs index a74731df69515..ff51662282394 100644 --- a/src/test/ui/type-alias-impl-trait/generic_duplicate_param_use2.rs +++ b/src/test/ui/type-alias-impl-trait/generic_duplicate_param_use2.rs @@ -1,4 +1,7 @@ -#![feature(type_alias_impl_trait)] +// revisions: min_tait full_tait +#![feature(min_type_alias_impl_trait)] +#![cfg_attr(full_tait, feature(type_alias_impl_trait))] +//[full_tait]~^ WARN incomplete use std::fmt::Debug; diff --git a/src/test/ui/type-alias-impl-trait/generic_duplicate_param_use2.stderr b/src/test/ui/type-alias-impl-trait/generic_duplicate_param_use2.stderr deleted file mode 100644 index d87e8c5783b65..0000000000000 --- a/src/test/ui/type-alias-impl-trait/generic_duplicate_param_use2.stderr +++ /dev/null @@ -1,14 +0,0 @@ -error: non-defining opaque type use in defining scope - --> $DIR/generic_duplicate_param_use2.rs:10:27 - | -LL | fn one<T: Debug>(t: T) -> Two<T, T> { - | ^^^^^^^^^ - | -note: type used multiple times - --> $DIR/generic_duplicate_param_use2.rs:8:10 - | -LL | type Two<T, U> = impl Debug; - | ^ ^ - -error: aborting due to previous error - diff --git a/src/test/ui/type-alias-impl-trait/generic_duplicate_param_use3.full_tait.stderr b/src/test/ui/type-alias-impl-trait/generic_duplicate_param_use3.full_tait.stderr new file mode 100644 index 0000000000000..b2004c8416c49 --- /dev/null +++ b/src/test/ui/type-alias-impl-trait/generic_duplicate_param_use3.full_tait.stderr @@ -0,0 +1,23 @@ +warning: the feature `type_alias_impl_trait` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/generic_duplicate_param_use3.rs:3:32 + | +LL | #![cfg_attr(full_tait, feature(type_alias_impl_trait))] + | ^^^^^^^^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #63063 <https://github.com/rust-lang/rust/issues/63063> for more information + +error: non-defining opaque type use in defining scope + --> $DIR/generic_duplicate_param_use3.rs:13:27 + | +LL | fn one<T: Debug>(t: T) -> Two<T, T> { + | ^^^^^^^^^ + | +note: type used multiple times + --> $DIR/generic_duplicate_param_use3.rs:11:10 + | +LL | type Two<T, U> = impl Debug; + | ^ ^ + +error: aborting due to previous error; 1 warning emitted + diff --git a/src/test/ui/type-alias-impl-trait/generic_duplicate_param_use3.min_tait.stderr b/src/test/ui/type-alias-impl-trait/generic_duplicate_param_use3.min_tait.stderr new file mode 100644 index 0000000000000..43af9eca63ef2 --- /dev/null +++ b/src/test/ui/type-alias-impl-trait/generic_duplicate_param_use3.min_tait.stderr @@ -0,0 +1,14 @@ +error: non-defining opaque type use in defining scope + --> $DIR/generic_duplicate_param_use3.rs:13:27 + | +LL | fn one<T: Debug>(t: T) -> Two<T, T> { + | ^^^^^^^^^ + | +note: type used multiple times + --> $DIR/generic_duplicate_param_use3.rs:11:10 + | +LL | type Two<T, U> = impl Debug; + | ^ ^ + +error: aborting due to previous error + diff --git a/src/test/ui/type-alias-impl-trait/generic_duplicate_param_use3.rs b/src/test/ui/type-alias-impl-trait/generic_duplicate_param_use3.rs index 0597b8385d252..f7d1cc1ad4e5c 100644 --- a/src/test/ui/type-alias-impl-trait/generic_duplicate_param_use3.rs +++ b/src/test/ui/type-alias-impl-trait/generic_duplicate_param_use3.rs @@ -1,4 +1,7 @@ -#![feature(type_alias_impl_trait)] +// revisions: min_tait full_tait +#![feature(min_type_alias_impl_trait)] +#![cfg_attr(full_tait, feature(type_alias_impl_trait))] +//[full_tait]~^ WARN incomplete use std::fmt::Debug; diff --git a/src/test/ui/type-alias-impl-trait/generic_duplicate_param_use3.stderr b/src/test/ui/type-alias-impl-trait/generic_duplicate_param_use3.stderr deleted file mode 100644 index 711de855f0d10..0000000000000 --- a/src/test/ui/type-alias-impl-trait/generic_duplicate_param_use3.stderr +++ /dev/null @@ -1,14 +0,0 @@ -error: non-defining opaque type use in defining scope - --> $DIR/generic_duplicate_param_use3.rs:10:27 - | -LL | fn one<T: Debug>(t: T) -> Two<T, T> { - | ^^^^^^^^^ - | -note: type used multiple times - --> $DIR/generic_duplicate_param_use3.rs:8:10 - | -LL | type Two<T, U> = impl Debug; - | ^ ^ - -error: aborting due to previous error - diff --git a/src/test/ui/type-alias-impl-trait/generic_duplicate_param_use4.full_tait.stderr b/src/test/ui/type-alias-impl-trait/generic_duplicate_param_use4.full_tait.stderr new file mode 100644 index 0000000000000..f5db77a08d74c --- /dev/null +++ b/src/test/ui/type-alias-impl-trait/generic_duplicate_param_use4.full_tait.stderr @@ -0,0 +1,23 @@ +warning: the feature `type_alias_impl_trait` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/generic_duplicate_param_use4.rs:3:32 + | +LL | #![cfg_attr(full_tait, feature(type_alias_impl_trait))] + | ^^^^^^^^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #63063 <https://github.com/rust-lang/rust/issues/63063> for more information + +error: non-defining opaque type use in defining scope + --> $DIR/generic_duplicate_param_use4.rs:13:27 + | +LL | fn one<T: Debug>(t: T) -> Two<T, T> { + | ^^^^^^^^^ + | +note: type used multiple times + --> $DIR/generic_duplicate_param_use4.rs:11:10 + | +LL | type Two<T, U> = impl Debug; + | ^ ^ + +error: aborting due to previous error; 1 warning emitted + diff --git a/src/test/ui/type-alias-impl-trait/generic_duplicate_param_use4.min_tait.stderr b/src/test/ui/type-alias-impl-trait/generic_duplicate_param_use4.min_tait.stderr new file mode 100644 index 0000000000000..f8e2b5104608d --- /dev/null +++ b/src/test/ui/type-alias-impl-trait/generic_duplicate_param_use4.min_tait.stderr @@ -0,0 +1,14 @@ +error: non-defining opaque type use in defining scope + --> $DIR/generic_duplicate_param_use4.rs:13:27 + | +LL | fn one<T: Debug>(t: T) -> Two<T, T> { + | ^^^^^^^^^ + | +note: type used multiple times + --> $DIR/generic_duplicate_param_use4.rs:11:10 + | +LL | type Two<T, U> = impl Debug; + | ^ ^ + +error: aborting due to previous error + diff --git a/src/test/ui/type-alias-impl-trait/generic_duplicate_param_use4.rs b/src/test/ui/type-alias-impl-trait/generic_duplicate_param_use4.rs index 40388c3b6c88a..26583a6ce9b9f 100644 --- a/src/test/ui/type-alias-impl-trait/generic_duplicate_param_use4.rs +++ b/src/test/ui/type-alias-impl-trait/generic_duplicate_param_use4.rs @@ -1,4 +1,7 @@ -#![feature(type_alias_impl_trait)] +// revisions: min_tait full_tait +#![feature(min_type_alias_impl_trait)] +#![cfg_attr(full_tait, feature(type_alias_impl_trait))] +//[full_tait]~^ WARN incomplete use std::fmt::Debug; diff --git a/src/test/ui/type-alias-impl-trait/generic_duplicate_param_use4.stderr b/src/test/ui/type-alias-impl-trait/generic_duplicate_param_use4.stderr deleted file mode 100644 index fcf01f5164ae4..0000000000000 --- a/src/test/ui/type-alias-impl-trait/generic_duplicate_param_use4.stderr +++ /dev/null @@ -1,14 +0,0 @@ -error: non-defining opaque type use in defining scope - --> $DIR/generic_duplicate_param_use4.rs:10:27 - | -LL | fn one<T: Debug>(t: T) -> Two<T, T> { - | ^^^^^^^^^ - | -note: type used multiple times - --> $DIR/generic_duplicate_param_use4.rs:8:10 - | -LL | type Two<T, U> = impl Debug; - | ^ ^ - -error: aborting due to previous error - diff --git a/src/test/ui/type-alias-impl-trait/generic_duplicate_param_use5.full_tait.stderr b/src/test/ui/type-alias-impl-trait/generic_duplicate_param_use5.full_tait.stderr new file mode 100644 index 0000000000000..aab64e72b7bca --- /dev/null +++ b/src/test/ui/type-alias-impl-trait/generic_duplicate_param_use5.full_tait.stderr @@ -0,0 +1,48 @@ +warning: the feature `type_alias_impl_trait` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/generic_duplicate_param_use5.rs:3:32 + | +LL | #![cfg_attr(full_tait, feature(type_alias_impl_trait))] + | ^^^^^^^^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #63063 <https://github.com/rust-lang/rust/issues/63063> for more information + +error: concrete type differs from previous defining opaque type use + --> $DIR/generic_duplicate_param_use5.rs:19:1 + | +LL | fn three<T: Debug, U: Debug>(t: T, u: U) -> Two<T, U> { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `(T, U)`, got `(U, T)` + | +note: previous use here + --> $DIR/generic_duplicate_param_use5.rs:15:1 + | +LL | fn two<T: Debug, U: Debug>(t: T, u: U) -> Two<T, U> { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error[E0277]: `T` doesn't implement `Debug` + --> $DIR/generic_duplicate_param_use5.rs:11:18 + | +LL | type Two<T, U> = impl Debug; + | ^^^^^^^^^^ `T` cannot be formatted using `{:?}` because it doesn't implement `Debug` + | + = note: required because of the requirements on the impl of `Debug` for `(T, U)` +help: consider restricting type parameter `T` + | +LL | type Two<T: std::fmt::Debug, U> = impl Debug; + | ^^^^^^^^^^^^^^^^^ + +error[E0277]: `U` doesn't implement `Debug` + --> $DIR/generic_duplicate_param_use5.rs:11:18 + | +LL | type Two<T, U> = impl Debug; + | ^^^^^^^^^^ `U` cannot be formatted using `{:?}` because it doesn't implement `Debug` + | + = note: required because of the requirements on the impl of `Debug` for `(T, U)` +help: consider restricting type parameter `U` + | +LL | type Two<T, U: std::fmt::Debug> = impl Debug; + | ^^^^^^^^^^^^^^^^^ + +error: aborting due to 3 previous errors; 1 warning emitted + +For more information about this error, try `rustc --explain E0277`. diff --git a/src/test/ui/type-alias-impl-trait/generic_duplicate_param_use5.min_tait.stderr b/src/test/ui/type-alias-impl-trait/generic_duplicate_param_use5.min_tait.stderr new file mode 100644 index 0000000000000..5c8c5b8977906 --- /dev/null +++ b/src/test/ui/type-alias-impl-trait/generic_duplicate_param_use5.min_tait.stderr @@ -0,0 +1,39 @@ +error: concrete type differs from previous defining opaque type use + --> $DIR/generic_duplicate_param_use5.rs:19:1 + | +LL | fn three<T: Debug, U: Debug>(t: T, u: U) -> Two<T, U> { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `(T, U)`, got `(U, T)` + | +note: previous use here + --> $DIR/generic_duplicate_param_use5.rs:15:1 + | +LL | fn two<T: Debug, U: Debug>(t: T, u: U) -> Two<T, U> { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error[E0277]: `T` doesn't implement `Debug` + --> $DIR/generic_duplicate_param_use5.rs:11:18 + | +LL | type Two<T, U> = impl Debug; + | ^^^^^^^^^^ `T` cannot be formatted using `{:?}` because it doesn't implement `Debug` + | + = note: required because of the requirements on the impl of `Debug` for `(T, U)` +help: consider restricting type parameter `T` + | +LL | type Two<T: std::fmt::Debug, U> = impl Debug; + | ^^^^^^^^^^^^^^^^^ + +error[E0277]: `U` doesn't implement `Debug` + --> $DIR/generic_duplicate_param_use5.rs:11:18 + | +LL | type Two<T, U> = impl Debug; + | ^^^^^^^^^^ `U` cannot be formatted using `{:?}` because it doesn't implement `Debug` + | + = note: required because of the requirements on the impl of `Debug` for `(T, U)` +help: consider restricting type parameter `U` + | +LL | type Two<T, U: std::fmt::Debug> = impl Debug; + | ^^^^^^^^^^^^^^^^^ + +error: aborting due to 3 previous errors + +For more information about this error, try `rustc --explain E0277`. diff --git a/src/test/ui/type-alias-impl-trait/generic_duplicate_param_use5.rs b/src/test/ui/type-alias-impl-trait/generic_duplicate_param_use5.rs index dd2f202cf5dc0..af473e8245acf 100644 --- a/src/test/ui/type-alias-impl-trait/generic_duplicate_param_use5.rs +++ b/src/test/ui/type-alias-impl-trait/generic_duplicate_param_use5.rs @@ -1,4 +1,7 @@ -#![feature(type_alias_impl_trait)] +// revisions: min_tait full_tait +#![feature(min_type_alias_impl_trait)] +#![cfg_attr(full_tait, feature(type_alias_impl_trait))] +//[full_tait]~^ WARN incomplete use std::fmt::Debug; diff --git a/src/test/ui/type-alias-impl-trait/generic_duplicate_param_use5.stderr b/src/test/ui/type-alias-impl-trait/generic_duplicate_param_use5.stderr deleted file mode 100644 index b4aed4a6323bf..0000000000000 --- a/src/test/ui/type-alias-impl-trait/generic_duplicate_param_use5.stderr +++ /dev/null @@ -1,39 +0,0 @@ -error: concrete type differs from previous defining opaque type use - --> $DIR/generic_duplicate_param_use5.rs:16:1 - | -LL | fn three<T: Debug, U: Debug>(t: T, u: U) -> Two<T, U> { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `(T, U)`, got `(U, T)` - | -note: previous use here - --> $DIR/generic_duplicate_param_use5.rs:12:1 - | -LL | fn two<T: Debug, U: Debug>(t: T, u: U) -> Two<T, U> { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error[E0277]: `T` doesn't implement `Debug` - --> $DIR/generic_duplicate_param_use5.rs:8:18 - | -LL | type Two<T, U> = impl Debug; - | ^^^^^^^^^^ `T` cannot be formatted using `{:?}` because it doesn't implement `Debug` - | - = note: required because of the requirements on the impl of `Debug` for `(T, U)` -help: consider restricting type parameter `T` - | -LL | type Two<T: Debug, U> = impl Debug; - | ^^^^^^^ - -error[E0277]: `U` doesn't implement `Debug` - --> $DIR/generic_duplicate_param_use5.rs:8:18 - | -LL | type Two<T, U> = impl Debug; - | ^^^^^^^^^^ `U` cannot be formatted using `{:?}` because it doesn't implement `Debug` - | - = note: required because of the requirements on the impl of `Debug` for `(T, U)` -help: consider restricting type parameter `U` - | -LL | type Two<T, U: Debug> = impl Debug; - | ^^^^^^^ - -error: aborting due to 3 previous errors - -For more information about this error, try `rustc --explain E0277`. diff --git a/src/test/ui/type-alias-impl-trait/generic_duplicate_param_use6.full_tait.stderr b/src/test/ui/type-alias-impl-trait/generic_duplicate_param_use6.full_tait.stderr new file mode 100644 index 0000000000000..a69e99bf8b05f --- /dev/null +++ b/src/test/ui/type-alias-impl-trait/generic_duplicate_param_use6.full_tait.stderr @@ -0,0 +1,36 @@ +warning: the feature `type_alias_impl_trait` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/generic_duplicate_param_use6.rs:3:32 + | +LL | #![cfg_attr(full_tait, feature(type_alias_impl_trait))] + | ^^^^^^^^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #63063 <https://github.com/rust-lang/rust/issues/63063> for more information + +error: concrete type differs from previous defining opaque type use + --> $DIR/generic_duplicate_param_use6.rs:18:1 + | +LL | fn three<T: Copy + Debug, U: Debug>(t: T, u: U) -> Two<T, U> { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `(T, T)`, got `(U, T)` + | +note: previous use here + --> $DIR/generic_duplicate_param_use6.rs:14:1 + | +LL | fn two<T: Copy + Debug, U: Debug>(t: T, u: U) -> Two<T, U> { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error[E0277]: `T` doesn't implement `Debug` + --> $DIR/generic_duplicate_param_use6.rs:11:18 + | +LL | type Two<T, U> = impl Debug; + | ^^^^^^^^^^ `T` cannot be formatted using `{:?}` because it doesn't implement `Debug` + | + = note: required because of the requirements on the impl of `Debug` for `(T, T)` +help: consider restricting type parameter `T` + | +LL | type Two<T: std::fmt::Debug, U> = impl Debug; + | ^^^^^^^^^^^^^^^^^ + +error: aborting due to 2 previous errors; 1 warning emitted + +For more information about this error, try `rustc --explain E0277`. diff --git a/src/test/ui/type-alias-impl-trait/generic_duplicate_param_use6.min_tait.stderr b/src/test/ui/type-alias-impl-trait/generic_duplicate_param_use6.min_tait.stderr new file mode 100644 index 0000000000000..a377ef2d87322 --- /dev/null +++ b/src/test/ui/type-alias-impl-trait/generic_duplicate_param_use6.min_tait.stderr @@ -0,0 +1,27 @@ +error: concrete type differs from previous defining opaque type use + --> $DIR/generic_duplicate_param_use6.rs:18:1 + | +LL | fn three<T: Copy + Debug, U: Debug>(t: T, u: U) -> Two<T, U> { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `(T, T)`, got `(U, T)` + | +note: previous use here + --> $DIR/generic_duplicate_param_use6.rs:14:1 + | +LL | fn two<T: Copy + Debug, U: Debug>(t: T, u: U) -> Two<T, U> { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error[E0277]: `T` doesn't implement `Debug` + --> $DIR/generic_duplicate_param_use6.rs:11:18 + | +LL | type Two<T, U> = impl Debug; + | ^^^^^^^^^^ `T` cannot be formatted using `{:?}` because it doesn't implement `Debug` + | + = note: required because of the requirements on the impl of `Debug` for `(T, T)` +help: consider restricting type parameter `T` + | +LL | type Two<T: std::fmt::Debug, U> = impl Debug; + | ^^^^^^^^^^^^^^^^^ + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0277`. diff --git a/src/test/ui/type-alias-impl-trait/generic_duplicate_param_use6.rs b/src/test/ui/type-alias-impl-trait/generic_duplicate_param_use6.rs index d54d3cd62e020..01afb2f449648 100644 --- a/src/test/ui/type-alias-impl-trait/generic_duplicate_param_use6.rs +++ b/src/test/ui/type-alias-impl-trait/generic_duplicate_param_use6.rs @@ -1,4 +1,7 @@ -#![feature(type_alias_impl_trait)] +// revisions: min_tait full_tait +#![feature(min_type_alias_impl_trait)] +#![cfg_attr(full_tait, feature(type_alias_impl_trait))] +//[full_tait]~^ WARN incomplete use std::fmt::Debug; diff --git a/src/test/ui/type-alias-impl-trait/generic_duplicate_param_use6.stderr b/src/test/ui/type-alias-impl-trait/generic_duplicate_param_use6.stderr deleted file mode 100644 index 22e4d00667492..0000000000000 --- a/src/test/ui/type-alias-impl-trait/generic_duplicate_param_use6.stderr +++ /dev/null @@ -1,27 +0,0 @@ -error: concrete type differs from previous defining opaque type use - --> $DIR/generic_duplicate_param_use6.rs:15:1 - | -LL | fn three<T: Copy + Debug, U: Debug>(t: T, u: U) -> Two<T, U> { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `(T, T)`, got `(U, T)` - | -note: previous use here - --> $DIR/generic_duplicate_param_use6.rs:11:1 - | -LL | fn two<T: Copy + Debug, U: Debug>(t: T, u: U) -> Two<T, U> { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error[E0277]: `T` doesn't implement `Debug` - --> $DIR/generic_duplicate_param_use6.rs:8:18 - | -LL | type Two<T, U> = impl Debug; - | ^^^^^^^^^^ `T` cannot be formatted using `{:?}` because it doesn't implement `Debug` - | - = note: required because of the requirements on the impl of `Debug` for `(T, T)` -help: consider restricting type parameter `T` - | -LL | type Two<T: Debug, U> = impl Debug; - | ^^^^^^^ - -error: aborting due to 2 previous errors - -For more information about this error, try `rustc --explain E0277`. diff --git a/src/test/ui/type-alias-impl-trait/generic_duplicate_param_use7.full_tait.stderr b/src/test/ui/type-alias-impl-trait/generic_duplicate_param_use7.full_tait.stderr new file mode 100644 index 0000000000000..d80c8326ad6c6 --- /dev/null +++ b/src/test/ui/type-alias-impl-trait/generic_duplicate_param_use7.full_tait.stderr @@ -0,0 +1,11 @@ +warning: the feature `type_alias_impl_trait` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/generic_duplicate_param_use7.rs:4:32 + | +LL | #![cfg_attr(full_tait, feature(type_alias_impl_trait))] + | ^^^^^^^^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #63063 <https://github.com/rust-lang/rust/issues/63063> for more information + +warning: 1 warning emitted + diff --git a/src/test/ui/type-alias-impl-trait/generic_duplicate_param_use7.rs b/src/test/ui/type-alias-impl-trait/generic_duplicate_param_use7.rs index feebf81eef2a7..184e3a5f926ba 100644 --- a/src/test/ui/type-alias-impl-trait/generic_duplicate_param_use7.rs +++ b/src/test/ui/type-alias-impl-trait/generic_duplicate_param_use7.rs @@ -1,5 +1,8 @@ // check-pass -#![feature(type_alias_impl_trait)] +// revisions: min_tait full_tait +#![feature(min_type_alias_impl_trait)] +#![cfg_attr(full_tait, feature(type_alias_impl_trait))] +//[full_tait]~^ WARN incomplete use std::fmt::Debug; diff --git a/src/test/ui/type-alias-impl-trait/generic_duplicate_param_use8.full_tait.stderr b/src/test/ui/type-alias-impl-trait/generic_duplicate_param_use8.full_tait.stderr new file mode 100644 index 0000000000000..e73ac88500e03 --- /dev/null +++ b/src/test/ui/type-alias-impl-trait/generic_duplicate_param_use8.full_tait.stderr @@ -0,0 +1,36 @@ +warning: the feature `type_alias_impl_trait` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/generic_duplicate_param_use8.rs:3:32 + | +LL | #![cfg_attr(full_tait, feature(type_alias_impl_trait))] + | ^^^^^^^^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #63063 <https://github.com/rust-lang/rust/issues/63063> for more information + +error: concrete type differs from previous defining opaque type use + --> $DIR/generic_duplicate_param_use8.rs:17:1 + | +LL | fn three<T: Debug, U: Debug>(_: T, u: U) -> Two<T, U> { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `(T, u32)`, got `(U, u32)` + | +note: previous use here + --> $DIR/generic_duplicate_param_use8.rs:13:1 + | +LL | fn two<T: Debug, U: Debug>(t: T, _: U) -> Two<T, U> { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error[E0277]: `T` doesn't implement `Debug` + --> $DIR/generic_duplicate_param_use8.rs:10:18 + | +LL | type Two<T, U> = impl Debug; + | ^^^^^^^^^^ `T` cannot be formatted using `{:?}` because it doesn't implement `Debug` + | + = note: required because of the requirements on the impl of `Debug` for `(T, u32)` +help: consider restricting type parameter `T` + | +LL | type Two<T: std::fmt::Debug, U> = impl Debug; + | ^^^^^^^^^^^^^^^^^ + +error: aborting due to 2 previous errors; 1 warning emitted + +For more information about this error, try `rustc --explain E0277`. diff --git a/src/test/ui/type-alias-impl-trait/generic_duplicate_param_use8.min_tait.stderr b/src/test/ui/type-alias-impl-trait/generic_duplicate_param_use8.min_tait.stderr new file mode 100644 index 0000000000000..d7edce7a491de --- /dev/null +++ b/src/test/ui/type-alias-impl-trait/generic_duplicate_param_use8.min_tait.stderr @@ -0,0 +1,27 @@ +error: concrete type differs from previous defining opaque type use + --> $DIR/generic_duplicate_param_use8.rs:17:1 + | +LL | fn three<T: Debug, U: Debug>(_: T, u: U) -> Two<T, U> { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `(T, u32)`, got `(U, u32)` + | +note: previous use here + --> $DIR/generic_duplicate_param_use8.rs:13:1 + | +LL | fn two<T: Debug, U: Debug>(t: T, _: U) -> Two<T, U> { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error[E0277]: `T` doesn't implement `Debug` + --> $DIR/generic_duplicate_param_use8.rs:10:18 + | +LL | type Two<T, U> = impl Debug; + | ^^^^^^^^^^ `T` cannot be formatted using `{:?}` because it doesn't implement `Debug` + | + = note: required because of the requirements on the impl of `Debug` for `(T, u32)` +help: consider restricting type parameter `T` + | +LL | type Two<T: std::fmt::Debug, U> = impl Debug; + | ^^^^^^^^^^^^^^^^^ + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0277`. diff --git a/src/test/ui/type-alias-impl-trait/generic_duplicate_param_use8.rs b/src/test/ui/type-alias-impl-trait/generic_duplicate_param_use8.rs index 4a723b64cdc4f..ecb8b2cedb488 100644 --- a/src/test/ui/type-alias-impl-trait/generic_duplicate_param_use8.rs +++ b/src/test/ui/type-alias-impl-trait/generic_duplicate_param_use8.rs @@ -1,4 +1,7 @@ -#![feature(type_alias_impl_trait)] +// revisions: min_tait full_tait +#![feature(min_type_alias_impl_trait)] +#![cfg_attr(full_tait, feature(type_alias_impl_trait))] +//[full_tait]~^ WARN incomplete use std::fmt::Debug; diff --git a/src/test/ui/type-alias-impl-trait/generic_duplicate_param_use8.stderr b/src/test/ui/type-alias-impl-trait/generic_duplicate_param_use8.stderr deleted file mode 100644 index 82da704c9ee22..0000000000000 --- a/src/test/ui/type-alias-impl-trait/generic_duplicate_param_use8.stderr +++ /dev/null @@ -1,27 +0,0 @@ -error: concrete type differs from previous defining opaque type use - --> $DIR/generic_duplicate_param_use8.rs:14:1 - | -LL | fn three<T: Debug, U: Debug>(_: T, u: U) -> Two<T, U> { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `(T, u32)`, got `(U, u32)` - | -note: previous use here - --> $DIR/generic_duplicate_param_use8.rs:10:1 - | -LL | fn two<T: Debug, U: Debug>(t: T, _: U) -> Two<T, U> { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error[E0277]: `T` doesn't implement `Debug` - --> $DIR/generic_duplicate_param_use8.rs:7:18 - | -LL | type Two<T, U> = impl Debug; - | ^^^^^^^^^^ `T` cannot be formatted using `{:?}` because it doesn't implement `Debug` - | - = note: required because of the requirements on the impl of `Debug` for `(T, u32)` -help: consider restricting type parameter `T` - | -LL | type Two<T: Debug, U> = impl Debug; - | ^^^^^^^ - -error: aborting due to 2 previous errors - -For more information about this error, try `rustc --explain E0277`. diff --git a/src/test/ui/type-alias-impl-trait/generic_duplicate_param_use9.full_tait.stderr b/src/test/ui/type-alias-impl-trait/generic_duplicate_param_use9.full_tait.stderr new file mode 100644 index 0000000000000..0b3d72d67b242 --- /dev/null +++ b/src/test/ui/type-alias-impl-trait/generic_duplicate_param_use9.full_tait.stderr @@ -0,0 +1,60 @@ +warning: the feature `type_alias_impl_trait` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/generic_duplicate_param_use9.rs:3:32 + | +LL | #![cfg_attr(full_tait, feature(type_alias_impl_trait))] + | ^^^^^^^^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #63063 <https://github.com/rust-lang/rust/issues/63063> for more information + +error: concrete type differs from previous defining opaque type use + --> $DIR/generic_duplicate_param_use9.rs:24:1 + | +LL | fn three<T: Debug, U: Debug>(t: T, u: U) -> Two<T, U> { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `(A, B, <A as Foo>::Bar)`, got `(A, B, i32)` + | +note: previous use here + --> $DIR/generic_duplicate_param_use9.rs:20:1 + | +LL | fn two<T: Debug + Foo, U: Debug>(t: T, u: U) -> Two<T, U> { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error[E0277]: the trait bound `A: Foo` is not satisfied in `(A, B, <A as Foo>::Bar)` + --> $DIR/generic_duplicate_param_use9.rs:10:18 + | +LL | type Two<A, B> = impl Debug; + | ^^^^^^^^^^ within `(A, B, <A as Foo>::Bar)`, the trait `Foo` is not implemented for `A` + | + = note: required because it appears within the type `(A, B, <A as Foo>::Bar)` +help: consider restricting type parameter `A` + | +LL | type Two<A: Foo, B> = impl Debug; + | ^^^^^ + +error[E0277]: `A` doesn't implement `Debug` + --> $DIR/generic_duplicate_param_use9.rs:10:18 + | +LL | type Two<A, B> = impl Debug; + | ^^^^^^^^^^ `A` cannot be formatted using `{:?}` because it doesn't implement `Debug` + | + = note: required because of the requirements on the impl of `Debug` for `(A, B, <A as Foo>::Bar)` +help: consider restricting type parameter `A` + | +LL | type Two<A: std::fmt::Debug, B> = impl Debug; + | ^^^^^^^^^^^^^^^^^ + +error[E0277]: `B` doesn't implement `Debug` + --> $DIR/generic_duplicate_param_use9.rs:10:18 + | +LL | type Two<A, B> = impl Debug; + | ^^^^^^^^^^ `B` cannot be formatted using `{:?}` because it doesn't implement `Debug` + | + = note: required because of the requirements on the impl of `Debug` for `(A, B, <A as Foo>::Bar)` +help: consider restricting type parameter `B` + | +LL | type Two<A, B: std::fmt::Debug> = impl Debug; + | ^^^^^^^^^^^^^^^^^ + +error: aborting due to 4 previous errors; 1 warning emitted + +For more information about this error, try `rustc --explain E0277`. diff --git a/src/test/ui/type-alias-impl-trait/generic_duplicate_param_use9.min_tait.stderr b/src/test/ui/type-alias-impl-trait/generic_duplicate_param_use9.min_tait.stderr new file mode 100644 index 0000000000000..fd1081d7b71de --- /dev/null +++ b/src/test/ui/type-alias-impl-trait/generic_duplicate_param_use9.min_tait.stderr @@ -0,0 +1,51 @@ +error: concrete type differs from previous defining opaque type use + --> $DIR/generic_duplicate_param_use9.rs:24:1 + | +LL | fn three<T: Debug, U: Debug>(t: T, u: U) -> Two<T, U> { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `(A, B, <A as Foo>::Bar)`, got `(A, B, i32)` + | +note: previous use here + --> $DIR/generic_duplicate_param_use9.rs:20:1 + | +LL | fn two<T: Debug + Foo, U: Debug>(t: T, u: U) -> Two<T, U> { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error[E0277]: the trait bound `A: Foo` is not satisfied in `(A, B, <A as Foo>::Bar)` + --> $DIR/generic_duplicate_param_use9.rs:10:18 + | +LL | type Two<A, B> = impl Debug; + | ^^^^^^^^^^ within `(A, B, <A as Foo>::Bar)`, the trait `Foo` is not implemented for `A` + | + = note: required because it appears within the type `(A, B, <A as Foo>::Bar)` +help: consider restricting type parameter `A` + | +LL | type Two<A: Foo, B> = impl Debug; + | ^^^^^ + +error[E0277]: `A` doesn't implement `Debug` + --> $DIR/generic_duplicate_param_use9.rs:10:18 + | +LL | type Two<A, B> = impl Debug; + | ^^^^^^^^^^ `A` cannot be formatted using `{:?}` because it doesn't implement `Debug` + | + = note: required because of the requirements on the impl of `Debug` for `(A, B, <A as Foo>::Bar)` +help: consider restricting type parameter `A` + | +LL | type Two<A: std::fmt::Debug, B> = impl Debug; + | ^^^^^^^^^^^^^^^^^ + +error[E0277]: `B` doesn't implement `Debug` + --> $DIR/generic_duplicate_param_use9.rs:10:18 + | +LL | type Two<A, B> = impl Debug; + | ^^^^^^^^^^ `B` cannot be formatted using `{:?}` because it doesn't implement `Debug` + | + = note: required because of the requirements on the impl of `Debug` for `(A, B, <A as Foo>::Bar)` +help: consider restricting type parameter `B` + | +LL | type Two<A, B: std::fmt::Debug> = impl Debug; + | ^^^^^^^^^^^^^^^^^ + +error: aborting due to 4 previous errors + +For more information about this error, try `rustc --explain E0277`. diff --git a/src/test/ui/type-alias-impl-trait/generic_duplicate_param_use9.rs b/src/test/ui/type-alias-impl-trait/generic_duplicate_param_use9.rs index 747081933172b..5eced6cfa5a0b 100644 --- a/src/test/ui/type-alias-impl-trait/generic_duplicate_param_use9.rs +++ b/src/test/ui/type-alias-impl-trait/generic_duplicate_param_use9.rs @@ -1,4 +1,7 @@ -#![feature(type_alias_impl_trait)] +// revisions: min_tait full_tait +#![feature(min_type_alias_impl_trait)] +#![cfg_attr(full_tait, feature(type_alias_impl_trait))] +//[full_tait]~^ WARN incomplete use std::fmt::Debug; diff --git a/src/test/ui/type-alias-impl-trait/generic_duplicate_param_use9.stderr b/src/test/ui/type-alias-impl-trait/generic_duplicate_param_use9.stderr deleted file mode 100644 index 63aa0f8a81c9a..0000000000000 --- a/src/test/ui/type-alias-impl-trait/generic_duplicate_param_use9.stderr +++ /dev/null @@ -1,51 +0,0 @@ -error: concrete type differs from previous defining opaque type use - --> $DIR/generic_duplicate_param_use9.rs:21:1 - | -LL | fn three<T: Debug, U: Debug>(t: T, u: U) -> Two<T, U> { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `(A, B, <A as Foo>::Bar)`, got `(A, B, i32)` - | -note: previous use here - --> $DIR/generic_duplicate_param_use9.rs:17:1 - | -LL | fn two<T: Debug + Foo, U: Debug>(t: T, u: U) -> Two<T, U> { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error[E0277]: the trait bound `A: Foo` is not satisfied in `(A, B, <A as Foo>::Bar)` - --> $DIR/generic_duplicate_param_use9.rs:7:18 - | -LL | type Two<A, B> = impl Debug; - | ^^^^^^^^^^ within `(A, B, <A as Foo>::Bar)`, the trait `Foo` is not implemented for `A` - | - = note: required because it appears within the type `(A, B, <A as Foo>::Bar)` -help: consider restricting type parameter `A` - | -LL | type Two<A: Foo, B> = impl Debug; - | ^^^^^ - -error[E0277]: `A` doesn't implement `Debug` - --> $DIR/generic_duplicate_param_use9.rs:7:18 - | -LL | type Two<A, B> = impl Debug; - | ^^^^^^^^^^ `A` cannot be formatted using `{:?}` because it doesn't implement `Debug` - | - = note: required because of the requirements on the impl of `Debug` for `(A, B, <A as Foo>::Bar)` -help: consider restricting type parameter `A` - | -LL | type Two<A: Debug, B> = impl Debug; - | ^^^^^^^ - -error[E0277]: `B` doesn't implement `Debug` - --> $DIR/generic_duplicate_param_use9.rs:7:18 - | -LL | type Two<A, B> = impl Debug; - | ^^^^^^^^^^ `B` cannot be formatted using `{:?}` because it doesn't implement `Debug` - | - = note: required because of the requirements on the impl of `Debug` for `(A, B, <A as Foo>::Bar)` -help: consider restricting type parameter `B` - | -LL | type Two<A, B: Debug> = impl Debug; - | ^^^^^^^ - -error: aborting due to 4 previous errors - -For more information about this error, try `rustc --explain E0277`. diff --git a/src/test/ui/type-alias-impl-trait/generic_lifetime_param.full_tait.stderr b/src/test/ui/type-alias-impl-trait/generic_lifetime_param.full_tait.stderr new file mode 100644 index 0000000000000..39817757f18b4 --- /dev/null +++ b/src/test/ui/type-alias-impl-trait/generic_lifetime_param.full_tait.stderr @@ -0,0 +1,11 @@ +warning: the feature `type_alias_impl_trait` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/generic_lifetime_param.rs:5:32 + | +LL | #![cfg_attr(full_tait, feature(type_alias_impl_trait))] + | ^^^^^^^^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #63063 <https://github.com/rust-lang/rust/issues/63063> for more information + +warning: 1 warning emitted + diff --git a/src/test/ui/type-alias-impl-trait/generic_lifetime_param.rs b/src/test/ui/type-alias-impl-trait/generic_lifetime_param.rs index e109c38c98695..dbe7cfd1c81fc 100644 --- a/src/test/ui/type-alias-impl-trait/generic_lifetime_param.rs +++ b/src/test/ui/type-alias-impl-trait/generic_lifetime_param.rs @@ -1,6 +1,9 @@ // build-pass (FIXME(62277): could be check-pass?) -#![feature(type_alias_impl_trait)] +// revisions: min_tait full_tait +#![feature(min_type_alias_impl_trait)] +#![cfg_attr(full_tait, feature(type_alias_impl_trait))] +//[full_tait]~^ WARN incomplete fn main() {} diff --git a/src/test/ui/type-alias-impl-trait/generic_nondefining_use.full_tait.stderr b/src/test/ui/type-alias-impl-trait/generic_nondefining_use.full_tait.stderr new file mode 100644 index 0000000000000..4d7eddfb2fb45 --- /dev/null +++ b/src/test/ui/type-alias-impl-trait/generic_nondefining_use.full_tait.stderr @@ -0,0 +1,35 @@ +error: non-defining opaque type use in defining scope + --> $DIR/generic_nondefining_use.rs:17:21 + | +LL | fn concrete_ty() -> OneTy<u32> { + | ^^^^^^^^^^ + | +note: used non-generic type `u32` for generic parameter + --> $DIR/generic_nondefining_use.rs:11:12 + | +LL | type OneTy<T> = impl Debug; + | ^ + +error: non-defining opaque type use in defining scope + --> $DIR/generic_nondefining_use.rs:22:27 + | +LL | type OneLifetime<'a> = impl Debug; + | -- cannot use static lifetime; use a bound lifetime instead or remove the lifetime parameter from the opaque type +... +LL | fn concrete_lifetime() -> OneLifetime<'static> { + | ^^^^^^^^^^^^^^^^^^^^ + +error: non-defining opaque type use in defining scope + --> $DIR/generic_nondefining_use.rs:27:24 + | +LL | fn concrete_const() -> OneConst<{123}> { + | ^^^^^^^^^^^^^^^ + | +note: used non-generic constant `{123}` for generic parameter + --> $DIR/generic_nondefining_use.rs:13:21 + | +LL | type OneConst<const X: usize> = impl Debug; + | ^ + +error: aborting due to 3 previous errors + diff --git a/src/test/ui/type-alias-impl-trait/generic_nondefining_use.min_tait.stderr b/src/test/ui/type-alias-impl-trait/generic_nondefining_use.min_tait.stderr new file mode 100644 index 0000000000000..4d7eddfb2fb45 --- /dev/null +++ b/src/test/ui/type-alias-impl-trait/generic_nondefining_use.min_tait.stderr @@ -0,0 +1,35 @@ +error: non-defining opaque type use in defining scope + --> $DIR/generic_nondefining_use.rs:17:21 + | +LL | fn concrete_ty() -> OneTy<u32> { + | ^^^^^^^^^^ + | +note: used non-generic type `u32` for generic parameter + --> $DIR/generic_nondefining_use.rs:11:12 + | +LL | type OneTy<T> = impl Debug; + | ^ + +error: non-defining opaque type use in defining scope + --> $DIR/generic_nondefining_use.rs:22:27 + | +LL | type OneLifetime<'a> = impl Debug; + | -- cannot use static lifetime; use a bound lifetime instead or remove the lifetime parameter from the opaque type +... +LL | fn concrete_lifetime() -> OneLifetime<'static> { + | ^^^^^^^^^^^^^^^^^^^^ + +error: non-defining opaque type use in defining scope + --> $DIR/generic_nondefining_use.rs:27:24 + | +LL | fn concrete_const() -> OneConst<{123}> { + | ^^^^^^^^^^^^^^^ + | +note: used non-generic constant `{123}` for generic parameter + --> $DIR/generic_nondefining_use.rs:13:21 + | +LL | type OneConst<const X: usize> = impl Debug; + | ^ + +error: aborting due to 3 previous errors + diff --git a/src/test/ui/type-alias-impl-trait/generic_nondefining_use.rs b/src/test/ui/type-alias-impl-trait/generic_nondefining_use.rs index b1782120f84cc..c69884d8d6edb 100644 --- a/src/test/ui/type-alias-impl-trait/generic_nondefining_use.rs +++ b/src/test/ui/type-alias-impl-trait/generic_nondefining_use.rs @@ -1,4 +1,7 @@ -#![feature(type_alias_impl_trait, const_generics)] +#![feature(const_generics)] +// revisions: min_tait full_tait +#![feature(min_type_alias_impl_trait)] +#![cfg_attr(full_tait, feature(type_alias_impl_trait))] #![allow(incomplete_features)] use std::fmt::Debug; diff --git a/src/test/ui/type-alias-impl-trait/generic_nondefining_use.stderr b/src/test/ui/type-alias-impl-trait/generic_nondefining_use.stderr deleted file mode 100644 index 88f8dbe1a7d72..0000000000000 --- a/src/test/ui/type-alias-impl-trait/generic_nondefining_use.stderr +++ /dev/null @@ -1,35 +0,0 @@ -error: non-defining opaque type use in defining scope - --> $DIR/generic_nondefining_use.rs:14:21 - | -LL | fn concrete_ty() -> OneTy<u32> { - | ^^^^^^^^^^ - | -note: used non-generic type `u32` for generic parameter - --> $DIR/generic_nondefining_use.rs:8:12 - | -LL | type OneTy<T> = impl Debug; - | ^ - -error: non-defining opaque type use in defining scope - --> $DIR/generic_nondefining_use.rs:19:27 - | -LL | type OneLifetime<'a> = impl Debug; - | -- cannot use static lifetime; use a bound lifetime instead or remove the lifetime parameter from the opaque type -... -LL | fn concrete_lifetime() -> OneLifetime<'static> { - | ^^^^^^^^^^^^^^^^^^^^ - -error: non-defining opaque type use in defining scope - --> $DIR/generic_nondefining_use.rs:24:24 - | -LL | fn concrete_const() -> OneConst<{123}> { - | ^^^^^^^^^^^^^^^ - | -note: used non-generic constant `{123}` for generic parameter - --> $DIR/generic_nondefining_use.rs:10:21 - | -LL | type OneConst<const X: usize> = impl Debug; - | ^ - -error: aborting due to 3 previous errors - diff --git a/src/test/ui/type-alias-impl-trait/generic_not_used.full_tait.stderr b/src/test/ui/type-alias-impl-trait/generic_not_used.full_tait.stderr new file mode 100644 index 0000000000000..819ef7b771232 --- /dev/null +++ b/src/test/ui/type-alias-impl-trait/generic_not_used.full_tait.stderr @@ -0,0 +1,27 @@ +error: at least one trait must be specified + --> $DIR/generic_not_used.rs:8:33 + | +LL | type WrongGeneric<T: 'static> = impl 'static; + | ^^^^^^^^^^^^ + +warning: the feature `type_alias_impl_trait` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/generic_not_used.rs:3:32 + | +LL | #![cfg_attr(full_tait, feature(type_alias_impl_trait))] + | ^^^^^^^^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #63063 <https://github.com/rust-lang/rust/issues/63063> for more information + +error: type parameter `V` is part of concrete type but not used in parameter list for the `impl Trait` type alias + --> $DIR/generic_not_used.rs:11:73 + | +LL | fn wrong_generic<U: 'static, V: 'static>(_: U, v: V) -> WrongGeneric<U> { + | _________________________________________________________________________^ +LL | | +LL | | v +LL | | } + | |_^ + +error: aborting due to 2 previous errors; 1 warning emitted + diff --git a/src/test/ui/type-alias-impl-trait/generic_not_used.min_tait.stderr b/src/test/ui/type-alias-impl-trait/generic_not_used.min_tait.stderr new file mode 100644 index 0000000000000..fe7f5f6c71e04 --- /dev/null +++ b/src/test/ui/type-alias-impl-trait/generic_not_used.min_tait.stderr @@ -0,0 +1,18 @@ +error: at least one trait must be specified + --> $DIR/generic_not_used.rs:8:33 + | +LL | type WrongGeneric<T: 'static> = impl 'static; + | ^^^^^^^^^^^^ + +error: type parameter `V` is part of concrete type but not used in parameter list for the `impl Trait` type alias + --> $DIR/generic_not_used.rs:11:73 + | +LL | fn wrong_generic<U: 'static, V: 'static>(_: U, v: V) -> WrongGeneric<U> { + | _________________________________________________________________________^ +LL | | +LL | | v +LL | | } + | |_^ + +error: aborting due to 2 previous errors + diff --git a/src/test/ui/type-alias-impl-trait/generic_not_used.rs b/src/test/ui/type-alias-impl-trait/generic_not_used.rs index ace52dc83ad79..68f50b2151ff1 100644 --- a/src/test/ui/type-alias-impl-trait/generic_not_used.rs +++ b/src/test/ui/type-alias-impl-trait/generic_not_used.rs @@ -1,4 +1,7 @@ -#![feature(type_alias_impl_trait)] +// revisions: min_tait full_tait +#![feature(min_type_alias_impl_trait)] +#![cfg_attr(full_tait, feature(type_alias_impl_trait))] +//[full_tait]~^ WARN incomplete fn main() {} diff --git a/src/test/ui/type-alias-impl-trait/generic_not_used.stderr b/src/test/ui/type-alias-impl-trait/generic_not_used.stderr deleted file mode 100644 index 8015ff7eded90..0000000000000 --- a/src/test/ui/type-alias-impl-trait/generic_not_used.stderr +++ /dev/null @@ -1,18 +0,0 @@ -error: at least one trait must be specified - --> $DIR/generic_not_used.rs:5:33 - | -LL | type WrongGeneric<T: 'static> = impl 'static; - | ^^^^^^^^^^^^ - -error: type parameter `V` is part of concrete type but not used in parameter list for the `impl Trait` type alias - --> $DIR/generic_not_used.rs:8:73 - | -LL | fn wrong_generic<U: 'static, V: 'static>(_: U, v: V) -> WrongGeneric<U> { - | _________________________________________________________________________^ -LL | | -LL | | v -LL | | } - | |_^ - -error: aborting due to 2 previous errors - diff --git a/src/test/ui/type-alias-impl-trait/generic_type_does_not_live_long_enough.full_tait.nll.stderr b/src/test/ui/type-alias-impl-trait/generic_type_does_not_live_long_enough.full_tait.nll.stderr new file mode 100644 index 0000000000000..d0f3be336dccb --- /dev/null +++ b/src/test/ui/type-alias-impl-trait/generic_type_does_not_live_long_enough.full_tait.nll.stderr @@ -0,0 +1,50 @@ +error: at least one trait must be specified + --> $DIR/generic_type_does_not_live_long_enough.rs:12:24 + | +LL | type WrongGeneric<T> = impl 'static; + | ^^^^^^^^^^^^ + +warning: the feature `type_alias_impl_trait` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/generic_type_does_not_live_long_enough.rs:3:32 + | +LL | #![cfg_attr(full_tait, feature(type_alias_impl_trait))] + | ^^^^^^^^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #63063 <https://github.com/rust-lang/rust/issues/63063> for more information + +error[E0308]: mismatched types + --> $DIR/generic_type_does_not_live_long_enough.rs:9:18 + | +LL | let z: i32 = x; + | --- ^ expected `i32`, found opaque type + | | + | expected due to this +... +LL | type WrongGeneric<T> = impl 'static; + | ------------ the found opaque type + | + = note: expected type `i32` + found opaque type `impl Sized` + +error[E0310]: the parameter type `T` may not live long enough + --> $DIR/generic_type_does_not_live_long_enough.rs:17:30 + | +LL | fn wrong_generic<T>(t: T) -> WrongGeneric<T> { + | ^^^^^^^^^^^^^^^ + | + = help: consider adding an explicit lifetime bound `T: 'static`... + +error[E0310]: the parameter type `T` may not live long enough + --> $DIR/generic_type_does_not_live_long_enough.rs:12:24 + | +LL | type WrongGeneric<T> = impl 'static; + | ^^^^^^^^^^^^ + | + = help: consider adding an explicit lifetime bound `T: 'static`... + = note: ...so that the type `T` will meet its required lifetime bounds + +error: aborting due to 4 previous errors; 1 warning emitted + +Some errors have detailed explanations: E0308, E0310. +For more information about an error, try `rustc --explain E0308`. diff --git a/src/test/ui/type-alias-impl-trait/generic_type_does_not_live_long_enough.full_tait.stderr b/src/test/ui/type-alias-impl-trait/generic_type_does_not_live_long_enough.full_tait.stderr new file mode 100644 index 0000000000000..6394a1f8e8509 --- /dev/null +++ b/src/test/ui/type-alias-impl-trait/generic_type_does_not_live_long_enough.full_tait.stderr @@ -0,0 +1,51 @@ +error: at least one trait must be specified + --> $DIR/generic_type_does_not_live_long_enough.rs:12:24 + | +LL | type WrongGeneric<T> = impl 'static; + | ^^^^^^^^^^^^ + +warning: the feature `type_alias_impl_trait` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/generic_type_does_not_live_long_enough.rs:3:32 + | +LL | #![cfg_attr(full_tait, feature(type_alias_impl_trait))] + | ^^^^^^^^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #63063 <https://github.com/rust-lang/rust/issues/63063> for more information + +error[E0308]: mismatched types + --> $DIR/generic_type_does_not_live_long_enough.rs:9:18 + | +LL | let z: i32 = x; + | --- ^ expected `i32`, found opaque type + | | + | expected due to this +... +LL | type WrongGeneric<T> = impl 'static; + | ------------ the found opaque type + | + = note: expected type `i32` + found opaque type `impl Sized` + +error[E0310]: the parameter type `T` may not live long enough + --> $DIR/generic_type_does_not_live_long_enough.rs:12:24 + | +LL | type WrongGeneric<T> = impl 'static; + | ^^^^^^^^^^^^ ...so that the type `T` will meet its required lifetime bounds +... +LL | fn wrong_generic<T>(t: T) -> WrongGeneric<T> { + | - help: consider adding an explicit lifetime bound...: `T: 'static` + +error[E0310]: the parameter type `T` may not live long enough + --> $DIR/generic_type_does_not_live_long_enough.rs:12:24 + | +LL | type WrongGeneric<T> = impl 'static; + | ^^^^^^^^^^^^ + | + = help: consider adding an explicit lifetime bound `T: 'static`... + = note: ...so that the type `T` will meet its required lifetime bounds + +error: aborting due to 4 previous errors; 1 warning emitted + +Some errors have detailed explanations: E0308, E0310. +For more information about an error, try `rustc --explain E0308`. diff --git a/src/test/ui/type-alias-impl-trait/generic_type_does_not_live_long_enough.min_tait.nll.stderr b/src/test/ui/type-alias-impl-trait/generic_type_does_not_live_long_enough.min_tait.nll.stderr new file mode 100644 index 0000000000000..7496d96fa2136 --- /dev/null +++ b/src/test/ui/type-alias-impl-trait/generic_type_does_not_live_long_enough.min_tait.nll.stderr @@ -0,0 +1,41 @@ +error: at least one trait must be specified + --> $DIR/generic_type_does_not_live_long_enough.rs:12:24 + | +LL | type WrongGeneric<T> = impl 'static; + | ^^^^^^^^^^^^ + +error[E0308]: mismatched types + --> $DIR/generic_type_does_not_live_long_enough.rs:9:18 + | +LL | let z: i32 = x; + | --- ^ expected `i32`, found opaque type + | | + | expected due to this +... +LL | type WrongGeneric<T> = impl 'static; + | ------------ the found opaque type + | + = note: expected type `i32` + found opaque type `impl Sized` + +error[E0310]: the parameter type `T` may not live long enough + --> $DIR/generic_type_does_not_live_long_enough.rs:17:30 + | +LL | fn wrong_generic<T>(t: T) -> WrongGeneric<T> { + | ^^^^^^^^^^^^^^^ + | + = help: consider adding an explicit lifetime bound `T: 'static`... + +error[E0310]: the parameter type `T` may not live long enough + --> $DIR/generic_type_does_not_live_long_enough.rs:12:24 + | +LL | type WrongGeneric<T> = impl 'static; + | ^^^^^^^^^^^^ + | + = help: consider adding an explicit lifetime bound `T: 'static`... + = note: ...so that the type `T` will meet its required lifetime bounds + +error: aborting due to 4 previous errors + +Some errors have detailed explanations: E0308, E0310. +For more information about an error, try `rustc --explain E0308`. diff --git a/src/test/ui/type-alias-impl-trait/generic_type_does_not_live_long_enough.min_tait.stderr b/src/test/ui/type-alias-impl-trait/generic_type_does_not_live_long_enough.min_tait.stderr new file mode 100644 index 0000000000000..49ead8b094c19 --- /dev/null +++ b/src/test/ui/type-alias-impl-trait/generic_type_does_not_live_long_enough.min_tait.stderr @@ -0,0 +1,42 @@ +error: at least one trait must be specified + --> $DIR/generic_type_does_not_live_long_enough.rs:12:24 + | +LL | type WrongGeneric<T> = impl 'static; + | ^^^^^^^^^^^^ + +error[E0308]: mismatched types + --> $DIR/generic_type_does_not_live_long_enough.rs:9:18 + | +LL | let z: i32 = x; + | --- ^ expected `i32`, found opaque type + | | + | expected due to this +... +LL | type WrongGeneric<T> = impl 'static; + | ------------ the found opaque type + | + = note: expected type `i32` + found opaque type `impl Sized` + +error[E0310]: the parameter type `T` may not live long enough + --> $DIR/generic_type_does_not_live_long_enough.rs:12:24 + | +LL | type WrongGeneric<T> = impl 'static; + | ^^^^^^^^^^^^ ...so that the type `T` will meet its required lifetime bounds +... +LL | fn wrong_generic<T>(t: T) -> WrongGeneric<T> { + | - help: consider adding an explicit lifetime bound...: `T: 'static` + +error[E0310]: the parameter type `T` may not live long enough + --> $DIR/generic_type_does_not_live_long_enough.rs:12:24 + | +LL | type WrongGeneric<T> = impl 'static; + | ^^^^^^^^^^^^ + | + = help: consider adding an explicit lifetime bound `T: 'static`... + = note: ...so that the type `T` will meet its required lifetime bounds + +error: aborting due to 4 previous errors + +Some errors have detailed explanations: E0308, E0310. +For more information about an error, try `rustc --explain E0308`. diff --git a/src/test/ui/type-alias-impl-trait/generic_type_does_not_live_long_enough.nll.stderr b/src/test/ui/type-alias-impl-trait/generic_type_does_not_live_long_enough.nll.stderr deleted file mode 100644 index ffd6f34c4b00e..0000000000000 --- a/src/test/ui/type-alias-impl-trait/generic_type_does_not_live_long_enough.nll.stderr +++ /dev/null @@ -1,41 +0,0 @@ -error: at least one trait must be specified - --> $DIR/generic_type_does_not_live_long_enough.rs:9:24 - | -LL | type WrongGeneric<T> = impl 'static; - | ^^^^^^^^^^^^ - -error[E0308]: mismatched types - --> $DIR/generic_type_does_not_live_long_enough.rs:6:18 - | -LL | let z: i32 = x; - | --- ^ expected `i32`, found opaque type - | | - | expected due to this -... -LL | type WrongGeneric<T> = impl 'static; - | ------------ the found opaque type - | - = note: expected type `i32` - found opaque type `impl Sized` - -error[E0310]: the parameter type `T` may not live long enough - --> $DIR/generic_type_does_not_live_long_enough.rs:14:30 - | -LL | fn wrong_generic<T>(t: T) -> WrongGeneric<T> { - | ^^^^^^^^^^^^^^^ - | - = help: consider adding an explicit lifetime bound `T: 'static`... - -error[E0310]: the parameter type `T` may not live long enough - --> $DIR/generic_type_does_not_live_long_enough.rs:9:24 - | -LL | type WrongGeneric<T> = impl 'static; - | ^^^^^^^^^^^^ - | - = help: consider adding an explicit lifetime bound `T: 'static`... - = note: ...so that the type `T` will meet its required lifetime bounds - -error: aborting due to 4 previous errors - -Some errors have detailed explanations: E0308, E0310. -For more information about an error, try `rustc --explain E0308`. diff --git a/src/test/ui/type-alias-impl-trait/generic_type_does_not_live_long_enough.rs b/src/test/ui/type-alias-impl-trait/generic_type_does_not_live_long_enough.rs index f6d490960365a..3dda34ff668ce 100644 --- a/src/test/ui/type-alias-impl-trait/generic_type_does_not_live_long_enough.rs +++ b/src/test/ui/type-alias-impl-trait/generic_type_does_not_live_long_enough.rs @@ -1,4 +1,7 @@ -#![feature(type_alias_impl_trait)] +// revisions: min_tait full_tait +#![feature(min_type_alias_impl_trait)] +#![cfg_attr(full_tait, feature(type_alias_impl_trait))] +//[full_tait]~^ WARN incomplete fn main() { let y = 42; diff --git a/src/test/ui/type-alias-impl-trait/generic_type_does_not_live_long_enough.stderr b/src/test/ui/type-alias-impl-trait/generic_type_does_not_live_long_enough.stderr deleted file mode 100644 index 4924c447d7e40..0000000000000 --- a/src/test/ui/type-alias-impl-trait/generic_type_does_not_live_long_enough.stderr +++ /dev/null @@ -1,42 +0,0 @@ -error: at least one trait must be specified - --> $DIR/generic_type_does_not_live_long_enough.rs:9:24 - | -LL | type WrongGeneric<T> = impl 'static; - | ^^^^^^^^^^^^ - -error[E0308]: mismatched types - --> $DIR/generic_type_does_not_live_long_enough.rs:6:18 - | -LL | let z: i32 = x; - | --- ^ expected `i32`, found opaque type - | | - | expected due to this -... -LL | type WrongGeneric<T> = impl 'static; - | ------------ the found opaque type - | - = note: expected type `i32` - found opaque type `impl Sized` - -error[E0310]: the parameter type `T` may not live long enough - --> $DIR/generic_type_does_not_live_long_enough.rs:9:24 - | -LL | type WrongGeneric<T> = impl 'static; - | ^^^^^^^^^^^^ ...so that the type `T` will meet its required lifetime bounds -... -LL | fn wrong_generic<T>(t: T) -> WrongGeneric<T> { - | - help: consider adding an explicit lifetime bound...: `T: 'static` - -error[E0310]: the parameter type `T` may not live long enough - --> $DIR/generic_type_does_not_live_long_enough.rs:9:24 - | -LL | type WrongGeneric<T> = impl 'static; - | ^^^^^^^^^^^^ - | - = help: consider adding an explicit lifetime bound `T: 'static`... - = note: ...so that the type `T` will meet its required lifetime bounds - -error: aborting due to 4 previous errors - -Some errors have detailed explanations: E0308, E0310. -For more information about an error, try `rustc --explain E0308`. diff --git a/src/test/ui/type-alias-impl-trait/generic_underconstrained.full_tait.stderr b/src/test/ui/type-alias-impl-trait/generic_underconstrained.full_tait.stderr new file mode 100644 index 0000000000000..c4778a46fb088 --- /dev/null +++ b/src/test/ui/type-alias-impl-trait/generic_underconstrained.full_tait.stderr @@ -0,0 +1,32 @@ +error: at least one trait must be specified + --> $DIR/generic_underconstrained.rs:9:35 + | +LL | type Underconstrained<T: Trait> = impl 'static; + | ^^^^^^^^^^^^ + +warning: the feature `type_alias_impl_trait` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/generic_underconstrained.rs:3:32 + | +LL | #![cfg_attr(full_tait, feature(type_alias_impl_trait))] + | ^^^^^^^^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #63063 <https://github.com/rust-lang/rust/issues/63063> for more information + +error[E0277]: the trait bound `T: Trait` is not satisfied + --> $DIR/generic_underconstrained.rs:13:31 + | +LL | type Underconstrained<T: Trait> = impl 'static; + | ----- required by this bound in `Underconstrained` +... +LL | fn underconstrain<T>(_: T) -> Underconstrained<T> { + | ^^^^^^^^^^^^^^^^^^^ the trait `Trait` is not implemented for `T` + | +help: consider restricting type parameter `T` + | +LL | fn underconstrain<T: Trait>(_: T) -> Underconstrained<T> { + | ^^^^^^^ + +error: aborting due to 2 previous errors; 1 warning emitted + +For more information about this error, try `rustc --explain E0277`. diff --git a/src/test/ui/type-alias-impl-trait/generic_underconstrained.min_tait.stderr b/src/test/ui/type-alias-impl-trait/generic_underconstrained.min_tait.stderr new file mode 100644 index 0000000000000..6c1d6debee816 --- /dev/null +++ b/src/test/ui/type-alias-impl-trait/generic_underconstrained.min_tait.stderr @@ -0,0 +1,23 @@ +error: at least one trait must be specified + --> $DIR/generic_underconstrained.rs:9:35 + | +LL | type Underconstrained<T: Trait> = impl 'static; + | ^^^^^^^^^^^^ + +error[E0277]: the trait bound `T: Trait` is not satisfied + --> $DIR/generic_underconstrained.rs:13:31 + | +LL | type Underconstrained<T: Trait> = impl 'static; + | ----- required by this bound in `Underconstrained` +... +LL | fn underconstrain<T>(_: T) -> Underconstrained<T> { + | ^^^^^^^^^^^^^^^^^^^ the trait `Trait` is not implemented for `T` + | +help: consider restricting type parameter `T` + | +LL | fn underconstrain<T: Trait>(_: T) -> Underconstrained<T> { + | ^^^^^^^ + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0277`. diff --git a/src/test/ui/type-alias-impl-trait/generic_underconstrained.rs b/src/test/ui/type-alias-impl-trait/generic_underconstrained.rs index 766ee36c02be2..f4c4a1b72cb9d 100644 --- a/src/test/ui/type-alias-impl-trait/generic_underconstrained.rs +++ b/src/test/ui/type-alias-impl-trait/generic_underconstrained.rs @@ -1,4 +1,7 @@ -#![feature(type_alias_impl_trait)] +// revisions: min_tait full_tait +#![feature(min_type_alias_impl_trait)] +#![cfg_attr(full_tait, feature(type_alias_impl_trait))] +//[full_tait]~^ WARN incomplete fn main() {} diff --git a/src/test/ui/type-alias-impl-trait/generic_underconstrained.stderr b/src/test/ui/type-alias-impl-trait/generic_underconstrained.stderr deleted file mode 100644 index cefc5d99b379e..0000000000000 --- a/src/test/ui/type-alias-impl-trait/generic_underconstrained.stderr +++ /dev/null @@ -1,23 +0,0 @@ -error: at least one trait must be specified - --> $DIR/generic_underconstrained.rs:6:35 - | -LL | type Underconstrained<T: Trait> = impl 'static; - | ^^^^^^^^^^^^ - -error[E0277]: the trait bound `T: Trait` is not satisfied - --> $DIR/generic_underconstrained.rs:10:31 - | -LL | type Underconstrained<T: Trait> = impl 'static; - | ----- required by this bound in `Underconstrained` -... -LL | fn underconstrain<T>(_: T) -> Underconstrained<T> { - | ^^^^^^^^^^^^^^^^^^^ the trait `Trait` is not implemented for `T` - | -help: consider restricting type parameter `T` - | -LL | fn underconstrain<T: Trait>(_: T) -> Underconstrained<T> { - | ^^^^^^^ - -error: aborting due to 2 previous errors - -For more information about this error, try `rustc --explain E0277`. diff --git a/src/test/ui/type-alias-impl-trait/generic_underconstrained2.full_tait.stderr b/src/test/ui/type-alias-impl-trait/generic_underconstrained2.full_tait.stderr new file mode 100644 index 0000000000000..7ab73d24274ce --- /dev/null +++ b/src/test/ui/type-alias-impl-trait/generic_underconstrained2.full_tait.stderr @@ -0,0 +1,52 @@ +error: at least one trait must be specified + --> $DIR/generic_underconstrained2.rs:8:45 + | +LL | type Underconstrained<T: std::fmt::Debug> = impl 'static; + | ^^^^^^^^^^^^ + +error: at least one trait must be specified + --> $DIR/generic_underconstrained2.rs:17:46 + | +LL | type Underconstrained2<T: std::fmt::Debug> = impl 'static; + | ^^^^^^^^^^^^ + +warning: the feature `type_alias_impl_trait` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/generic_underconstrained2.rs:3:32 + | +LL | #![cfg_attr(full_tait, feature(type_alias_impl_trait))] + | ^^^^^^^^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #63063 <https://github.com/rust-lang/rust/issues/63063> for more information + +error[E0277]: `U` doesn't implement `Debug` + --> $DIR/generic_underconstrained2.rs:12:33 + | +LL | type Underconstrained<T: std::fmt::Debug> = impl 'static; + | --------------- required by this bound in `Underconstrained` +... +LL | fn underconstrained<U>(_: U) -> Underconstrained<U> { + | ^^^^^^^^^^^^^^^^^^^ `U` cannot be formatted using `{:?}` because it doesn't implement `Debug` + | +help: consider restricting type parameter `U` + | +LL | fn underconstrained<U: std::fmt::Debug>(_: U) -> Underconstrained<U> { + | ^^^^^^^^^^^^^^^^^ + +error[E0277]: `V` doesn't implement `Debug` + --> $DIR/generic_underconstrained2.rs:21:43 + | +LL | type Underconstrained2<T: std::fmt::Debug> = impl 'static; + | --------------- required by this bound in `Underconstrained2` +... +LL | fn underconstrained2<U, V>(_: U, _: V) -> Underconstrained2<V> { + | ^^^^^^^^^^^^^^^^^^^^ `V` cannot be formatted using `{:?}` because it doesn't implement `Debug` + | +help: consider restricting type parameter `V` + | +LL | fn underconstrained2<U, V: std::fmt::Debug>(_: U, _: V) -> Underconstrained2<V> { + | ^^^^^^^^^^^^^^^^^ + +error: aborting due to 4 previous errors; 1 warning emitted + +For more information about this error, try `rustc --explain E0277`. diff --git a/src/test/ui/type-alias-impl-trait/generic_underconstrained2.min_tait.stderr b/src/test/ui/type-alias-impl-trait/generic_underconstrained2.min_tait.stderr new file mode 100644 index 0000000000000..a4f5f4b8645b1 --- /dev/null +++ b/src/test/ui/type-alias-impl-trait/generic_underconstrained2.min_tait.stderr @@ -0,0 +1,43 @@ +error: at least one trait must be specified + --> $DIR/generic_underconstrained2.rs:8:45 + | +LL | type Underconstrained<T: std::fmt::Debug> = impl 'static; + | ^^^^^^^^^^^^ + +error: at least one trait must be specified + --> $DIR/generic_underconstrained2.rs:17:46 + | +LL | type Underconstrained2<T: std::fmt::Debug> = impl 'static; + | ^^^^^^^^^^^^ + +error[E0277]: `U` doesn't implement `Debug` + --> $DIR/generic_underconstrained2.rs:12:33 + | +LL | type Underconstrained<T: std::fmt::Debug> = impl 'static; + | --------------- required by this bound in `Underconstrained` +... +LL | fn underconstrained<U>(_: U) -> Underconstrained<U> { + | ^^^^^^^^^^^^^^^^^^^ `U` cannot be formatted using `{:?}` because it doesn't implement `Debug` + | +help: consider restricting type parameter `U` + | +LL | fn underconstrained<U: std::fmt::Debug>(_: U) -> Underconstrained<U> { + | ^^^^^^^^^^^^^^^^^ + +error[E0277]: `V` doesn't implement `Debug` + --> $DIR/generic_underconstrained2.rs:21:43 + | +LL | type Underconstrained2<T: std::fmt::Debug> = impl 'static; + | --------------- required by this bound in `Underconstrained2` +... +LL | fn underconstrained2<U, V>(_: U, _: V) -> Underconstrained2<V> { + | ^^^^^^^^^^^^^^^^^^^^ `V` cannot be formatted using `{:?}` because it doesn't implement `Debug` + | +help: consider restricting type parameter `V` + | +LL | fn underconstrained2<U, V: std::fmt::Debug>(_: U, _: V) -> Underconstrained2<V> { + | ^^^^^^^^^^^^^^^^^ + +error: aborting due to 4 previous errors + +For more information about this error, try `rustc --explain E0277`. diff --git a/src/test/ui/type-alias-impl-trait/generic_underconstrained2.rs b/src/test/ui/type-alias-impl-trait/generic_underconstrained2.rs index cd7c962e2d15b..b3fb0748d2a62 100644 --- a/src/test/ui/type-alias-impl-trait/generic_underconstrained2.rs +++ b/src/test/ui/type-alias-impl-trait/generic_underconstrained2.rs @@ -1,4 +1,7 @@ -#![feature(type_alias_impl_trait)] +// revisions: min_tait full_tait +#![feature(min_type_alias_impl_trait)] +#![cfg_attr(full_tait, feature(type_alias_impl_trait))] +//[full_tait]~^ WARN incomplete fn main() {} diff --git a/src/test/ui/type-alias-impl-trait/generic_underconstrained2.stderr b/src/test/ui/type-alias-impl-trait/generic_underconstrained2.stderr deleted file mode 100644 index 669546aef8669..0000000000000 --- a/src/test/ui/type-alias-impl-trait/generic_underconstrained2.stderr +++ /dev/null @@ -1,43 +0,0 @@ -error: at least one trait must be specified - --> $DIR/generic_underconstrained2.rs:5:45 - | -LL | type Underconstrained<T: std::fmt::Debug> = impl 'static; - | ^^^^^^^^^^^^ - -error: at least one trait must be specified - --> $DIR/generic_underconstrained2.rs:14:46 - | -LL | type Underconstrained2<T: std::fmt::Debug> = impl 'static; - | ^^^^^^^^^^^^ - -error[E0277]: `U` doesn't implement `Debug` - --> $DIR/generic_underconstrained2.rs:9:33 - | -LL | type Underconstrained<T: std::fmt::Debug> = impl 'static; - | --------------- required by this bound in `Underconstrained` -... -LL | fn underconstrained<U>(_: U) -> Underconstrained<U> { - | ^^^^^^^^^^^^^^^^^^^ `U` cannot be formatted using `{:?}` because it doesn't implement `Debug` - | -help: consider restricting type parameter `U` - | -LL | fn underconstrained<U: Debug>(_: U) -> Underconstrained<U> { - | ^^^^^^^ - -error[E0277]: `V` doesn't implement `Debug` - --> $DIR/generic_underconstrained2.rs:18:43 - | -LL | type Underconstrained2<T: std::fmt::Debug> = impl 'static; - | --------------- required by this bound in `Underconstrained2` -... -LL | fn underconstrained2<U, V>(_: U, _: V) -> Underconstrained2<V> { - | ^^^^^^^^^^^^^^^^^^^^ `V` cannot be formatted using `{:?}` because it doesn't implement `Debug` - | -help: consider restricting type parameter `V` - | -LL | fn underconstrained2<U, V: Debug>(_: U, _: V) -> Underconstrained2<V> { - | ^^^^^^^ - -error: aborting due to 4 previous errors - -For more information about this error, try `rustc --explain E0277`. diff --git a/src/test/ui/type-alias-impl-trait/impl-with-unconstrained-param.full_tait.stderr b/src/test/ui/type-alias-impl-trait/impl-with-unconstrained-param.full_tait.stderr new file mode 100644 index 0000000000000..bdd88134290c8 --- /dev/null +++ b/src/test/ui/type-alias-impl-trait/impl-with-unconstrained-param.full_tait.stderr @@ -0,0 +1,18 @@ +warning: the feature `type_alias_impl_trait` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/impl-with-unconstrained-param.rs:6:32 + | +LL | #![cfg_attr(full_tait, feature(type_alias_impl_trait))] + | ^^^^^^^^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #63063 <https://github.com/rust-lang/rust/issues/63063> for more information + +error[E0207]: the type parameter `T` is not constrained by the impl trait, self type, or predicates + --> $DIR/impl-with-unconstrained-param.rs:14:6 + | +LL | impl<T> X for () { + | ^ unconstrained type parameter + +error: aborting due to previous error; 1 warning emitted + +For more information about this error, try `rustc --explain E0207`. diff --git a/src/test/ui/type-alias-impl-trait/impl-with-unconstrained-param.min_tait.stderr b/src/test/ui/type-alias-impl-trait/impl-with-unconstrained-param.min_tait.stderr new file mode 100644 index 0000000000000..eb59766b91396 --- /dev/null +++ b/src/test/ui/type-alias-impl-trait/impl-with-unconstrained-param.min_tait.stderr @@ -0,0 +1,9 @@ +error[E0207]: the type parameter `T` is not constrained by the impl trait, self type, or predicates + --> $DIR/impl-with-unconstrained-param.rs:14:6 + | +LL | impl<T> X for () { + | ^ unconstrained type parameter + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0207`. diff --git a/src/test/ui/type-alias-impl-trait/impl-with-unconstrained-param.rs b/src/test/ui/type-alias-impl-trait/impl-with-unconstrained-param.rs index 851c2f66c475a..1aefa2474ffcd 100644 --- a/src/test/ui/type-alias-impl-trait/impl-with-unconstrained-param.rs +++ b/src/test/ui/type-alias-impl-trait/impl-with-unconstrained-param.rs @@ -1,7 +1,10 @@ // Ensure that we don't ICE if associated type impl trait is used in an impl // with an unconstrained type parameter. -#![feature(type_alias_impl_trait)] +// revisions: min_tait full_tait +#![feature(min_type_alias_impl_trait)] +#![cfg_attr(full_tait, feature(type_alias_impl_trait))] +//[full_tait]~^ WARN incomplete trait X { type I; diff --git a/src/test/ui/type-alias-impl-trait/impl-with-unconstrained-param.stderr b/src/test/ui/type-alias-impl-trait/impl-with-unconstrained-param.stderr deleted file mode 100644 index 8cf8fb1d16c4d..0000000000000 --- a/src/test/ui/type-alias-impl-trait/impl-with-unconstrained-param.stderr +++ /dev/null @@ -1,9 +0,0 @@ -error[E0207]: the type parameter `T` is not constrained by the impl trait, self type, or predicates - --> $DIR/impl-with-unconstrained-param.rs:11:6 - | -LL | impl<T> X for () { - | ^ unconstrained type parameter - -error: aborting due to previous error - -For more information about this error, try `rustc --explain E0207`. diff --git a/src/test/ui/type-alias-impl-trait/incoherent-assoc-imp-trait.full_tait.stderr b/src/test/ui/type-alias-impl-trait/incoherent-assoc-imp-trait.full_tait.stderr new file mode 100644 index 0000000000000..6d1a59aafeda4 --- /dev/null +++ b/src/test/ui/type-alias-impl-trait/incoherent-assoc-imp-trait.full_tait.stderr @@ -0,0 +1,32 @@ +warning: the feature `type_alias_impl_trait` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/incoherent-assoc-imp-trait.rs:6:32 + | +LL | #![cfg_attr(full_tait, feature(type_alias_impl_trait))] + | ^^^^^^^^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #63063 <https://github.com/rust-lang/rust/issues/63063> for more information + +error[E0119]: conflicting implementations of trait `std::ops::FnOnce<()>` for type `&_` + --> $DIR/incoherent-assoc-imp-trait.rs:13:1 + | +LL | impl<F> FnOnce<()> for &F { + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: conflicting implementation in crate `core`: + - impl<A, F> FnOnce<A> for &F + where F: Fn<A>, F: ?Sized; + +error[E0210]: type parameter `F` must be used as the type parameter for some local type (e.g., `MyStruct<F>`) + --> $DIR/incoherent-assoc-imp-trait.rs:13:6 + | +LL | impl<F> FnOnce<()> for &F { + | ^ type parameter `F` must be used as the type parameter for some local type + | + = note: implementing a foreign trait is only possible if at least one of the types for which it is implemented is local + = note: only traits defined in the current crate can be implemented for a type parameter + +error: aborting due to 2 previous errors; 1 warning emitted + +Some errors have detailed explanations: E0119, E0210. +For more information about an error, try `rustc --explain E0119`. diff --git a/src/test/ui/type-alias-impl-trait/incoherent-assoc-imp-trait.min_tait.stderr b/src/test/ui/type-alias-impl-trait/incoherent-assoc-imp-trait.min_tait.stderr new file mode 100644 index 0000000000000..5c02b602d528b --- /dev/null +++ b/src/test/ui/type-alias-impl-trait/incoherent-assoc-imp-trait.min_tait.stderr @@ -0,0 +1,23 @@ +error[E0119]: conflicting implementations of trait `std::ops::FnOnce<()>` for type `&_` + --> $DIR/incoherent-assoc-imp-trait.rs:13:1 + | +LL | impl<F> FnOnce<()> for &F { + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: conflicting implementation in crate `core`: + - impl<A, F> FnOnce<A> for &F + where F: Fn<A>, F: ?Sized; + +error[E0210]: type parameter `F` must be used as the type parameter for some local type (e.g., `MyStruct<F>`) + --> $DIR/incoherent-assoc-imp-trait.rs:13:6 + | +LL | impl<F> FnOnce<()> for &F { + | ^ type parameter `F` must be used as the type parameter for some local type + | + = note: implementing a foreign trait is only possible if at least one of the types for which it is implemented is local + = note: only traits defined in the current crate can be implemented for a type parameter + +error: aborting due to 2 previous errors + +Some errors have detailed explanations: E0119, E0210. +For more information about an error, try `rustc --explain E0119`. diff --git a/src/test/ui/type-alias-impl-trait/incoherent-assoc-imp-trait.rs b/src/test/ui/type-alias-impl-trait/incoherent-assoc-imp-trait.rs index c46c4715924e5..b52b9c18f0845 100644 --- a/src/test/ui/type-alias-impl-trait/incoherent-assoc-imp-trait.rs +++ b/src/test/ui/type-alias-impl-trait/incoherent-assoc-imp-trait.rs @@ -1,7 +1,10 @@ // Regression test for issue 67856 #![feature(unboxed_closures)] -#![feature(type_alias_impl_trait)] +// revisions: min_tait full_tait +#![feature(min_type_alias_impl_trait)] +#![cfg_attr(full_tait, feature(type_alias_impl_trait))] +//[full_tait]~^ WARN incomplete #![feature(fn_traits)] trait MyTrait {} diff --git a/src/test/ui/type-alias-impl-trait/incoherent-assoc-imp-trait.stderr b/src/test/ui/type-alias-impl-trait/incoherent-assoc-imp-trait.stderr deleted file mode 100644 index eafbd2ad0725b..0000000000000 --- a/src/test/ui/type-alias-impl-trait/incoherent-assoc-imp-trait.stderr +++ /dev/null @@ -1,23 +0,0 @@ -error[E0119]: conflicting implementations of trait `std::ops::FnOnce<()>` for type `&_`: - --> $DIR/incoherent-assoc-imp-trait.rs:10:1 - | -LL | impl<F> FnOnce<()> for &F { - | ^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: conflicting implementation in crate `core`: - - impl<A, F> FnOnce<A> for &F - where F: Fn<A>, F: ?Sized; - -error[E0210]: type parameter `F` must be used as the type parameter for some local type (e.g., `MyStruct<F>`) - --> $DIR/incoherent-assoc-imp-trait.rs:10:6 - | -LL | impl<F> FnOnce<()> for &F { - | ^ type parameter `F` must be used as the type parameter for some local type - | - = note: implementing a foreign trait is only possible if at least one of the types for which it is implemented is local - = note: only traits defined in the current crate can be implemented for a type parameter - -error: aborting due to 2 previous errors - -Some errors have detailed explanations: E0119, E0210. -For more information about an error, try `rustc --explain E0119`. diff --git a/src/test/ui/type-alias-impl-trait/issue-52843-closure-constrain.full_tait.stderr b/src/test/ui/type-alias-impl-trait/issue-52843-closure-constrain.full_tait.stderr new file mode 100644 index 0000000000000..4effd87120e7d --- /dev/null +++ b/src/test/ui/type-alias-impl-trait/issue-52843-closure-constrain.full_tait.stderr @@ -0,0 +1,23 @@ +warning: the feature `type_alias_impl_trait` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/issue-52843-closure-constrain.rs:5:32 + | +LL | #![cfg_attr(full_tait, feature(type_alias_impl_trait))] + | ^^^^^^^^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #63063 <https://github.com/rust-lang/rust/issues/63063> for more information + +error: concrete type differs from previous defining opaque type use + --> $DIR/issue-52843-closure-constrain.rs:13:16 + | +LL | let null = || -> Opaque { 0 }; + | ^^^^^^^^^^^^^^^^^^ expected `String`, got `i32` + | +note: previous use here + --> $DIR/issue-52843-closure-constrain.rs:12:5 + | +LL | fn _unused() -> Opaque { String::new() } + | ^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to previous error; 1 warning emitted + diff --git a/src/test/ui/type-alias-impl-trait/issue-52843-closure-constrain.min_tait.stderr b/src/test/ui/type-alias-impl-trait/issue-52843-closure-constrain.min_tait.stderr new file mode 100644 index 0000000000000..c2cb4829fb78a --- /dev/null +++ b/src/test/ui/type-alias-impl-trait/issue-52843-closure-constrain.min_tait.stderr @@ -0,0 +1,24 @@ +error[E0658]: type alias impl trait is not permitted here + --> $DIR/issue-52843-closure-constrain.rs:13:22 + | +LL | let null = || -> Opaque { 0 }; + | ^^^^^^ + | + = note: see issue #63063 <https://github.com/rust-lang/rust/issues/63063> for more information + = help: add `#![feature(type_alias_impl_trait)]` to the crate attributes to enable + +error: concrete type differs from previous defining opaque type use + --> $DIR/issue-52843-closure-constrain.rs:13:16 + | +LL | let null = || -> Opaque { 0 }; + | ^^^^^^^^^^^^^^^^^^ expected `String`, got `[type error]` + | +note: previous use here + --> $DIR/issue-52843-closure-constrain.rs:12:5 + | +LL | fn _unused() -> Opaque { String::new() } + | ^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0658`. diff --git a/src/test/ui/type-alias-impl-trait/issue-52843-closure-constrain.rs b/src/test/ui/type-alias-impl-trait/issue-52843-closure-constrain.rs index 50eeff0b18fd4..01f874155fa1e 100644 --- a/src/test/ui/type-alias-impl-trait/issue-52843-closure-constrain.rs +++ b/src/test/ui/type-alias-impl-trait/issue-52843-closure-constrain.rs @@ -1,13 +1,16 @@ // Checks to ensure that we properly detect when a closure constrains an opaque type -#![feature(type_alias_impl_trait)] +// revisions: min_tait full_tait +#![feature(min_type_alias_impl_trait)] +#![cfg_attr(full_tait, feature(type_alias_impl_trait))] +//[full_tait]~^ WARN incomplete use std::fmt::Debug; fn main() { type Opaque = impl Debug; fn _unused() -> Opaque { String::new() } - let null = || -> Opaque { 0 }; + let null = || -> Opaque { 0 }; //[min_tait]~ ERROR: not permitted here //~^ ERROR: concrete type differs from previous defining opaque type use println!("{:?}", null()); } diff --git a/src/test/ui/type-alias-impl-trait/issue-52843-closure-constrain.stderr b/src/test/ui/type-alias-impl-trait/issue-52843-closure-constrain.stderr deleted file mode 100644 index d82050e263ee6..0000000000000 --- a/src/test/ui/type-alias-impl-trait/issue-52843-closure-constrain.stderr +++ /dev/null @@ -1,14 +0,0 @@ -error: concrete type differs from previous defining opaque type use - --> $DIR/issue-52843-closure-constrain.rs:10:16 - | -LL | let null = || -> Opaque { 0 }; - | ^^^^^^^^^^^^^^^^^^ expected `String`, got `i32` - | -note: previous use here - --> $DIR/issue-52843-closure-constrain.rs:9:5 - | -LL | fn _unused() -> Opaque { String::new() } - | ^^^^^^^^^^^^^^^^^^^^^^ - -error: aborting due to previous error - diff --git a/src/test/ui/type-alias-impl-trait/issue-52843.full_tait.stderr b/src/test/ui/type-alias-impl-trait/issue-52843.full_tait.stderr new file mode 100644 index 0000000000000..35ac0993b29ca --- /dev/null +++ b/src/test/ui/type-alias-impl-trait/issue-52843.full_tait.stderr @@ -0,0 +1,23 @@ +warning: the feature `type_alias_impl_trait` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/issue-52843.rs:3:32 + | +LL | #![cfg_attr(full_tait, feature(type_alias_impl_trait))] + | ^^^^^^^^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #63063 <https://github.com/rust-lang/rust/issues/63063> for more information + +error[E0277]: the trait bound `T: Default` is not satisfied + --> $DIR/issue-52843.rs:6:15 + | +LL | type Foo<T> = impl Default; + | ^^^^^^^^^^^^ the trait `Default` is not implemented for `T` + | +help: consider restricting type parameter `T` + | +LL | type Foo<T: std::default::Default> = impl Default; + | ^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to previous error; 1 warning emitted + +For more information about this error, try `rustc --explain E0277`. diff --git a/src/test/ui/type-alias-impl-trait/issue-52843.min_tait.stderr b/src/test/ui/type-alias-impl-trait/issue-52843.min_tait.stderr new file mode 100644 index 0000000000000..9fb760f34c19a --- /dev/null +++ b/src/test/ui/type-alias-impl-trait/issue-52843.min_tait.stderr @@ -0,0 +1,14 @@ +error[E0277]: the trait bound `T: Default` is not satisfied + --> $DIR/issue-52843.rs:6:15 + | +LL | type Foo<T> = impl Default; + | ^^^^^^^^^^^^ the trait `Default` is not implemented for `T` + | +help: consider restricting type parameter `T` + | +LL | type Foo<T: std::default::Default> = impl Default; + | ^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0277`. diff --git a/src/test/ui/type-alias-impl-trait/issue-52843.rs b/src/test/ui/type-alias-impl-trait/issue-52843.rs index b24959d720720..14f9db6eae2c7 100644 --- a/src/test/ui/type-alias-impl-trait/issue-52843.rs +++ b/src/test/ui/type-alias-impl-trait/issue-52843.rs @@ -1,4 +1,7 @@ -#![feature(type_alias_impl_trait)] +// revisions: min_tait full_tait +#![feature(min_type_alias_impl_trait)] +#![cfg_attr(full_tait, feature(type_alias_impl_trait))] +//[full_tait]~^ WARN incomplete type Foo<T> = impl Default; //~^ ERROR: the trait bound `T: Default` is not satisfied diff --git a/src/test/ui/type-alias-impl-trait/issue-52843.stderr b/src/test/ui/type-alias-impl-trait/issue-52843.stderr deleted file mode 100644 index 25db8dfabfc29..0000000000000 --- a/src/test/ui/type-alias-impl-trait/issue-52843.stderr +++ /dev/null @@ -1,14 +0,0 @@ -error[E0277]: the trait bound `T: Default` is not satisfied - --> $DIR/issue-52843.rs:3:15 - | -LL | type Foo<T> = impl Default; - | ^^^^^^^^^^^^ the trait `Default` is not implemented for `T` - | -help: consider restricting type parameter `T` - | -LL | type Foo<T: Default> = impl Default; - | ^^^^^^^^^ - -error: aborting due to previous error - -For more information about this error, try `rustc --explain E0277`. diff --git a/src/test/ui/type-alias-impl-trait/issue-53096.full_tait.stderr b/src/test/ui/type-alias-impl-trait/issue-53096.full_tait.stderr new file mode 100644 index 0000000000000..1a03ce7966111 --- /dev/null +++ b/src/test/ui/type-alias-impl-trait/issue-53096.full_tait.stderr @@ -0,0 +1,25 @@ +warning: the feature `impl_trait_in_bindings` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/issue-53096.rs:4:32 + | +LL | #![cfg_attr(full_tait, feature(impl_trait_in_bindings, type_alias_impl_trait))] + | ^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #63065 <https://github.com/rust-lang/rust/issues/63065> for more information + +warning: the feature `type_alias_impl_trait` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/issue-53096.rs:4:56 + | +LL | #![cfg_attr(full_tait, feature(impl_trait_in_bindings, type_alias_impl_trait))] + | ^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #63063 <https://github.com/rust-lang/rust/issues/63063> for more information + +error: fatal error triggered by #[rustc_error] + --> $DIR/issue-53096.rs:14:1 + | +LL | fn main() {} + | ^^^^^^^^^ + +error: aborting due to previous error; 2 warnings emitted + diff --git a/src/test/ui/type-alias-impl-trait/issue-53096.min_tait.stderr b/src/test/ui/type-alias-impl-trait/issue-53096.min_tait.stderr new file mode 100644 index 0000000000000..c6c7a51618dd3 --- /dev/null +++ b/src/test/ui/type-alias-impl-trait/issue-53096.min_tait.stderr @@ -0,0 +1,12 @@ +error[E0658]: type alias impl trait is not permitted here + --> $DIR/issue-53096.rs:10:19 + | +LL | const BAZR: Foo = bar(); + | ^^^^^ + | + = note: see issue #63065 <https://github.com/rust-lang/rust/issues/63065> for more information + = help: add `#![feature(impl_trait_in_bindings)]` to the crate attributes to enable + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0658`. diff --git a/src/test/ui/type-alias-impl-trait/issue-53096.rs b/src/test/ui/type-alias-impl-trait/issue-53096.rs index 6e1973bd18aab..fb621dc0bce9f 100644 --- a/src/test/ui/type-alias-impl-trait/issue-53096.rs +++ b/src/test/ui/type-alias-impl-trait/issue-53096.rs @@ -1,9 +1,14 @@ -// check-pass -#![feature(const_impl_trait, const_fn_fn_ptr_basics)] -#![feature(type_alias_impl_trait)] +#![feature(const_impl_trait, const_fn_fn_ptr_basics, rustc_attrs)] +// revisions: min_tait full_tait +#![feature(min_type_alias_impl_trait)] +#![cfg_attr(full_tait, feature(impl_trait_in_bindings, type_alias_impl_trait))] +//[full_tait]~^ WARN incomplete +//[full_tait]~| WARN incomplete type Foo = impl Fn() -> usize; const fn bar() -> Foo { || 0usize } const BAZR: Foo = bar(); +//[min_tait]~^ ERROR not permitted here -fn main() {} +#[rustc_error] +fn main() {} //[full_tait]~ ERROR diff --git a/src/test/ui/type-alias-impl-trait/issue-53598.full_tait.stderr b/src/test/ui/type-alias-impl-trait/issue-53598.full_tait.stderr new file mode 100644 index 0000000000000..ee4b7eef0bdc4 --- /dev/null +++ b/src/test/ui/type-alias-impl-trait/issue-53598.full_tait.stderr @@ -0,0 +1,21 @@ +warning: the feature `type_alias_impl_trait` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/issue-53598.rs:4:32 + | +LL | #![cfg_attr(full_tait, feature(type_alias_impl_trait))] + | ^^^^^^^^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #63063 <https://github.com/rust-lang/rust/issues/63063> for more information + +error: type parameter `T` is part of concrete type but not used in parameter list for the `impl Trait` type alias + --> $DIR/issue-53598.rs:23:42 + | +LL | fn foo<T: Debug>(_: T) -> Self::Item { + | __________________________________________^ +LL | | +LL | | S::<T>(Default::default()) +LL | | } + | |_____^ + +error: aborting due to previous error; 1 warning emitted + diff --git a/src/test/ui/type-alias-impl-trait/issue-53598.min_tait.stderr b/src/test/ui/type-alias-impl-trait/issue-53598.min_tait.stderr new file mode 100644 index 0000000000000..728721b6dbd71 --- /dev/null +++ b/src/test/ui/type-alias-impl-trait/issue-53598.min_tait.stderr @@ -0,0 +1,12 @@ +error: type parameter `T` is part of concrete type but not used in parameter list for the `impl Trait` type alias + --> $DIR/issue-53598.rs:23:42 + | +LL | fn foo<T: Debug>(_: T) -> Self::Item { + | __________________________________________^ +LL | | +LL | | S::<T>(Default::default()) +LL | | } + | |_____^ + +error: aborting due to previous error + diff --git a/src/test/ui/type-alias-impl-trait/issue-53598.rs b/src/test/ui/type-alias-impl-trait/issue-53598.rs index add9662938069..38067a7222374 100644 --- a/src/test/ui/type-alias-impl-trait/issue-53598.rs +++ b/src/test/ui/type-alias-impl-trait/issue-53598.rs @@ -1,6 +1,8 @@ -// ignore-tidy-linelength // ignore-compare-mode-chalk -#![feature(type_alias_impl_trait)] +// revisions: min_tait full_tait +#![feature(min_type_alias_impl_trait)] +#![cfg_attr(full_tait, feature(type_alias_impl_trait))] +//[full_tait]~^ WARN incomplete use std::fmt::Debug; diff --git a/src/test/ui/type-alias-impl-trait/issue-53598.stderr b/src/test/ui/type-alias-impl-trait/issue-53598.stderr deleted file mode 100644 index 79e2682da3ea8..0000000000000 --- a/src/test/ui/type-alias-impl-trait/issue-53598.stderr +++ /dev/null @@ -1,12 +0,0 @@ -error: type parameter `T` is part of concrete type but not used in parameter list for the `impl Trait` type alias - --> $DIR/issue-53598.rs:21:42 - | -LL | fn foo<T: Debug>(_: T) -> Self::Item { - | __________________________________________^ -LL | | -LL | | S::<T>(Default::default()) -LL | | } - | |_____^ - -error: aborting due to previous error - diff --git a/src/test/ui/type-alias-impl-trait/issue-53678-generator-and-const-fn.full_tait.stderr b/src/test/ui/type-alias-impl-trait/issue-53678-generator-and-const-fn.full_tait.stderr new file mode 100644 index 0000000000000..70d049ffa76bd --- /dev/null +++ b/src/test/ui/type-alias-impl-trait/issue-53678-generator-and-const-fn.full_tait.stderr @@ -0,0 +1,25 @@ +warning: the feature `impl_trait_in_bindings` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/issue-53678-generator-and-const-fn.rs:4:32 + | +LL | #![cfg_attr(full_tait, feature(impl_trait_in_bindings, type_alias_impl_trait))] + | ^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #63065 <https://github.com/rust-lang/rust/issues/63065> for more information + +warning: the feature `type_alias_impl_trait` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/issue-53678-generator-and-const-fn.rs:4:56 + | +LL | #![cfg_attr(full_tait, feature(impl_trait_in_bindings, type_alias_impl_trait))] + | ^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #63063 <https://github.com/rust-lang/rust/issues/63063> for more information + +error: fatal error triggered by #[rustc_error] + --> $DIR/issue-53678-generator-and-const-fn.rs:23:1 + | +LL | fn main() {} + | ^^^^^^^^^ + +error: aborting due to previous error; 2 warnings emitted + diff --git a/src/test/ui/type-alias-impl-trait/issue-53678-generator-and-const-fn.min_tait.stderr b/src/test/ui/type-alias-impl-trait/issue-53678-generator-and-const-fn.min_tait.stderr new file mode 100644 index 0000000000000..a3dea45a6a5ad --- /dev/null +++ b/src/test/ui/type-alias-impl-trait/issue-53678-generator-and-const-fn.min_tait.stderr @@ -0,0 +1,12 @@ +error[E0658]: type alias impl trait is not permitted here + --> $DIR/issue-53678-generator-and-const-fn.rs:20:36 + | +LL | const FOO: GenOnce<usize, usize> = const_generator(10, 100); + | ^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #63065 <https://github.com/rust-lang/rust/issues/63065> for more information + = help: add `#![feature(impl_trait_in_bindings)]` to the crate attributes to enable + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0658`. diff --git a/src/test/ui/type-alias-impl-trait/issue-53678-generator-and-const-fn.rs b/src/test/ui/type-alias-impl-trait/issue-53678-generator-and-const-fn.rs index 4582d5386f08f..bf607d29ce462 100644 --- a/src/test/ui/type-alias-impl-trait/issue-53678-generator-and-const-fn.rs +++ b/src/test/ui/type-alias-impl-trait/issue-53678-generator-and-const-fn.rs @@ -1,6 +1,9 @@ -// check-pass - -#![feature(const_impl_trait, generators, generator_trait, type_alias_impl_trait)] +#![feature(const_impl_trait, generators, generator_trait, rustc_attrs)] +// revisions: min_tait full_tait +#![feature(min_type_alias_impl_trait)] +#![cfg_attr(full_tait, feature(impl_trait_in_bindings, type_alias_impl_trait))] +//[full_tait]~^ WARN incomplete +//[full_tait]~| WARN incomplete use std::ops::Generator; @@ -14,6 +17,7 @@ const fn const_generator<Y, R>(yielding: Y, returning: R) -> GenOnce<Y, R> { } } -const FOO: GenOnce<usize, usize> = const_generator(10, 100); +const FOO: GenOnce<usize, usize> = const_generator(10, 100); //[min_tait]~ ERROR not permitted here -fn main() {} +#[rustc_error] +fn main() {} //[full_tait]~ ERROR diff --git a/src/test/ui/type-alias-impl-trait/issue-55099-lifetime-inference.full_tait.stderr b/src/test/ui/type-alias-impl-trait/issue-55099-lifetime-inference.full_tait.stderr new file mode 100644 index 0000000000000..98778210bfb37 --- /dev/null +++ b/src/test/ui/type-alias-impl-trait/issue-55099-lifetime-inference.full_tait.stderr @@ -0,0 +1,11 @@ +warning: the feature `type_alias_impl_trait` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/issue-55099-lifetime-inference.rs:8:32 + | +LL | #![cfg_attr(full_tait, feature(type_alias_impl_trait))] + | ^^^^^^^^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #63063 <https://github.com/rust-lang/rust/issues/63063> for more information + +warning: 1 warning emitted + diff --git a/src/test/ui/type-alias-impl-trait/issue-55099-lifetime-inference.rs b/src/test/ui/type-alias-impl-trait/issue-55099-lifetime-inference.rs index 8e8508cdd6f30..2c24e9d744ae2 100644 --- a/src/test/ui/type-alias-impl-trait/issue-55099-lifetime-inference.rs +++ b/src/test/ui/type-alias-impl-trait/issue-55099-lifetime-inference.rs @@ -3,7 +3,10 @@ // Tests that we don't incorrectly consider a lifetime to part // of the concrete type -#![feature(type_alias_impl_trait)] +// revisions: min_tait full_tait +#![feature(min_type_alias_impl_trait)] +#![cfg_attr(full_tait, feature(type_alias_impl_trait))] +//[full_tait]~^ WARN incomplete trait Future { } diff --git a/src/test/ui/type-alias-impl-trait/issue-57188-associate-impl-capture.full_tait.stderr b/src/test/ui/type-alias-impl-trait/issue-57188-associate-impl-capture.full_tait.stderr new file mode 100644 index 0000000000000..d31c232fc2391 --- /dev/null +++ b/src/test/ui/type-alias-impl-trait/issue-57188-associate-impl-capture.full_tait.stderr @@ -0,0 +1,11 @@ +warning: the feature `type_alias_impl_trait` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/issue-57188-associate-impl-capture.rs:7:32 + | +LL | #![cfg_attr(full_tait, feature(type_alias_impl_trait))] + | ^^^^^^^^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #63063 <https://github.com/rust-lang/rust/issues/63063> for more information + +warning: 1 warning emitted + diff --git a/src/test/ui/type-alias-impl-trait/issue-57188-associate-impl-capture.rs b/src/test/ui/type-alias-impl-trait/issue-57188-associate-impl-capture.rs index 3a7a5da075f11..645a4c5696d45 100644 --- a/src/test/ui/type-alias-impl-trait/issue-57188-associate-impl-capture.rs +++ b/src/test/ui/type-alias-impl-trait/issue-57188-associate-impl-capture.rs @@ -2,7 +2,10 @@ // check-pass -#![feature(type_alias_impl_trait)] +// revisions: min_tait full_tait +#![feature(min_type_alias_impl_trait)] +#![cfg_attr(full_tait, feature(type_alias_impl_trait))] +//[full_tait]~^ WARN incomplete struct Baz<'a> { source: &'a str, diff --git a/src/test/ui/type-alias-impl-trait/issue-57611-trait-alias.full_tait.nll.stderr b/src/test/ui/type-alias-impl-trait/issue-57611-trait-alias.full_tait.nll.stderr new file mode 100644 index 0000000000000..481d66dd8110f --- /dev/null +++ b/src/test/ui/type-alias-impl-trait/issue-57611-trait-alias.full_tait.nll.stderr @@ -0,0 +1,47 @@ +warning: the feature `type_alias_impl_trait` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/issue-57611-trait-alias.rs:8:32 + | +LL | #![cfg_attr(full_tait, feature(type_alias_impl_trait))] + | ^^^^^^^^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #63063 <https://github.com/rust-lang/rust/issues/63063> for more information + +error: higher-ranked subtype error + --> $DIR/issue-57611-trait-alias.rs:28:9 + | +LL | |x| x + | ^^^^^ + +error: higher-ranked subtype error + --> $DIR/issue-57611-trait-alias.rs:28:9 + | +LL | |x| x + | ^^^^^ + +error[E0308]: mismatched types + --> $DIR/issue-57611-trait-alias.rs:20: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:28:9 + | +LL | |x| x + | ^^^^^ + +error: implementation of `FnOnce` is not general enough + --> $DIR/issue-57611-trait-alias.rs:20: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; 1 warning emitted + +For more information about this error, try `rustc --explain E0308`. diff --git a/src/test/ui/type-alias-impl-trait/issue-57611-trait-alias.full_tait.stderr b/src/test/ui/type-alias-impl-trait/issue-57611-trait-alias.full_tait.stderr new file mode 100644 index 0000000000000..d90f328708a7b --- /dev/null +++ b/src/test/ui/type-alias-impl-trait/issue-57611-trait-alias.full_tait.stderr @@ -0,0 +1,67 @@ +warning: the feature `type_alias_impl_trait` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/issue-57611-trait-alias.rs:8:32 + | +LL | #![cfg_attr(full_tait, feature(type_alias_impl_trait))] + | ^^^^^^^^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #63063 <https://github.com/rust-lang/rust/issues/63063> for more information + +error: implementation of `FnOnce` is not general enough + --> $DIR/issue-57611-trait-alias.rs:20:16 + | +LL | type Bar = impl Baz<Self, Self>; + | ^^^^^^^^^^^^^^^^^^^^ implementation of `FnOnce` is not general enough + | + = note: closure with signature `fn(&'2 X) -> &X` must implement `FnOnce<(&'1 X,)>`, for any lifetime `'1`... + = note: ...but it actually implements `FnOnce<(&'2 X,)>`, for some specific lifetime `'2` + +error[E0308]: mismatched types + --> $DIR/issue-57611-trait-alias.rs:20: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<(&'<empty> X,)>` +note: this closure does not fulfill the lifetime requirements + --> $DIR/issue-57611-trait-alias.rs:28:9 + | +LL | |x| x + | ^^^^^ + +error: implementation of `FnOnce` is not general enough + --> $DIR/issue-57611-trait-alias.rs:20:16 + | +LL | type Bar = impl Baz<Self, Self>; + | ^^^^^^^^^^^^^^^^^^^^ implementation of `FnOnce` is not general enough + | + = note: closure with signature `fn(&'2 X) -> &'2 X` must implement `FnOnce<(&'1 X,)>`, for any lifetime `'1`... + = note: ...but it actually implements `FnOnce<(&'2 X,)>`, for some specific lifetime `'2` + +error[E0308]: mismatched types + --> $DIR/issue-57611-trait-alias.rs:20: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<(&'<empty> X,)>` +note: this closure does not fulfill the lifetime requirements + --> $DIR/issue-57611-trait-alias.rs:28:9 + | +LL | |x| x + | ^^^^^ + +error: implementation of `FnOnce` is not general enough + --> $DIR/issue-57611-trait-alias.rs:20:16 + | +LL | type Bar = impl Baz<Self, Self>; + | ^^^^^^^^^^^^^^^^^^^^ implementation of `FnOnce` is not general enough + | + = note: closure with signature `fn(&'2 X) -> &'2 X` must implement `FnOnce<(&'1 X,)>`, for any lifetime `'1`... + = note: ...but it actually implements `FnOnce<(&'2 X,)>`, for some specific lifetime `'2` + +error: aborting due to 5 previous errors; 1 warning emitted + +For more information about this error, try `rustc --explain E0308`. diff --git a/src/test/ui/type-alias-impl-trait/issue-57611-trait-alias.min_tait.nll.stderr b/src/test/ui/type-alias-impl-trait/issue-57611-trait-alias.min_tait.nll.stderr new file mode 100644 index 0000000000000..0343a769564af --- /dev/null +++ b/src/test/ui/type-alias-impl-trait/issue-57611-trait-alias.min_tait.nll.stderr @@ -0,0 +1,38 @@ +error: higher-ranked subtype error + --> $DIR/issue-57611-trait-alias.rs:28:9 + | +LL | |x| x + | ^^^^^ + +error: higher-ranked subtype error + --> $DIR/issue-57611-trait-alias.rs:28:9 + | +LL | |x| x + | ^^^^^ + +error[E0308]: mismatched types + --> $DIR/issue-57611-trait-alias.rs:20: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:28:9 + | +LL | |x| x + | ^^^^^ + +error: implementation of `FnOnce` is not general enough + --> $DIR/issue-57611-trait-alias.rs:20: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/test/ui/type-alias-impl-trait/issue-57611-trait-alias.min_tait.stderr b/src/test/ui/type-alias-impl-trait/issue-57611-trait-alias.min_tait.stderr new file mode 100644 index 0000000000000..d019f40757eed --- /dev/null +++ b/src/test/ui/type-alias-impl-trait/issue-57611-trait-alias.min_tait.stderr @@ -0,0 +1,58 @@ +error: implementation of `FnOnce` is not general enough + --> $DIR/issue-57611-trait-alias.rs:20:16 + | +LL | type Bar = impl Baz<Self, Self>; + | ^^^^^^^^^^^^^^^^^^^^ implementation of `FnOnce` is not general enough + | + = note: closure with signature `fn(&'2 X) -> &X` must implement `FnOnce<(&'1 X,)>`, for any lifetime `'1`... + = note: ...but it actually implements `FnOnce<(&'2 X,)>`, for some specific lifetime `'2` + +error[E0308]: mismatched types + --> $DIR/issue-57611-trait-alias.rs:20: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<(&'<empty> X,)>` +note: this closure does not fulfill the lifetime requirements + --> $DIR/issue-57611-trait-alias.rs:28:9 + | +LL | |x| x + | ^^^^^ + +error: implementation of `FnOnce` is not general enough + --> $DIR/issue-57611-trait-alias.rs:20:16 + | +LL | type Bar = impl Baz<Self, Self>; + | ^^^^^^^^^^^^^^^^^^^^ implementation of `FnOnce` is not general enough + | + = note: closure with signature `fn(&'2 X) -> &'2 X` must implement `FnOnce<(&'1 X,)>`, for any lifetime `'1`... + = note: ...but it actually implements `FnOnce<(&'2 X,)>`, for some specific lifetime `'2` + +error[E0308]: mismatched types + --> $DIR/issue-57611-trait-alias.rs:20: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<(&'<empty> X,)>` +note: this closure does not fulfill the lifetime requirements + --> $DIR/issue-57611-trait-alias.rs:28:9 + | +LL | |x| x + | ^^^^^ + +error: implementation of `FnOnce` is not general enough + --> $DIR/issue-57611-trait-alias.rs:20:16 + | +LL | type Bar = impl Baz<Self, Self>; + | ^^^^^^^^^^^^^^^^^^^^ implementation of `FnOnce` is not general enough + | + = note: closure with signature `fn(&'2 X) -> &'2 X` must implement `FnOnce<(&'1 X,)>`, for any lifetime `'1`... + = note: ...but it actually implements `FnOnce<(&'2 X,)>`, for some specific lifetime `'2` + +error: aborting due to 5 previous errors + +For more information about this error, try `rustc --explain E0308`. 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 deleted file mode 100644 index 998e178966a22..0000000000000 --- a/src/test/ui/type-alias-impl-trait/issue-57611-trait-alias.nll.stderr +++ /dev/null @@ -1,38 +0,0 @@ -error: higher-ranked subtype error - --> $DIR/issue-57611-trait-alias.rs:25:9 - | -LL | |x| x - | ^^^^^ - -error: higher-ranked subtype error - --> $DIR/issue-57611-trait-alias.rs:25:9 - | -LL | |x| x - | ^^^^^ - -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:25: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/test/ui/type-alias-impl-trait/issue-57611-trait-alias.rs b/src/test/ui/type-alias-impl-trait/issue-57611-trait-alias.rs index 561528c2abbd5..ccc727e0bf01b 100644 --- a/src/test/ui/type-alias-impl-trait/issue-57611-trait-alias.rs +++ b/src/test/ui/type-alias-impl-trait/issue-57611-trait-alias.rs @@ -3,7 +3,10 @@ // FIXME: This should compile, but it currently doesn't #![feature(trait_alias)] -#![feature(type_alias_impl_trait)] +// revisions: min_tait full_tait +#![feature(min_type_alias_impl_trait)] +#![cfg_attr(full_tait, feature(type_alias_impl_trait))] +//[full_tait]~^ WARN incomplete trait Foo { type Bar: Baz<Self, Self>; diff --git a/src/test/ui/type-alias-impl-trait/issue-57611-trait-alias.stderr b/src/test/ui/type-alias-impl-trait/issue-57611-trait-alias.stderr deleted file mode 100644 index 91c9d459ad8ce..0000000000000 --- a/src/test/ui/type-alias-impl-trait/issue-57611-trait-alias.stderr +++ /dev/null @@ -1,58 +0,0 @@ -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(&'2 X) -> &X` must implement `FnOnce<(&'1 X,)>`, for any lifetime `'1`... - = note: ...but it actually implements `FnOnce<(&'2 X,)>`, for some specific lifetime `'2` - -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<(&'<empty> X,)>` -note: this closure does not fulfill the lifetime requirements - --> $DIR/issue-57611-trait-alias.rs:25: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(&'2 X) -> &'2 X` must implement `FnOnce<(&'1 X,)>`, for any lifetime `'1`... - = note: ...but it actually implements `FnOnce<(&'2 X,)>`, for some specific lifetime `'2` - -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<(&'<empty> X,)>` -note: this closure does not fulfill the lifetime requirements - --> $DIR/issue-57611-trait-alias.rs:25: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(&'2 X) -> &'2 X` must implement `FnOnce<(&'1 X,)>`, for any lifetime `'1`... - = note: ...but it actually implements `FnOnce<(&'2 X,)>`, for some specific lifetime `'2` - -error: aborting due to 5 previous errors - -For more information about this error, try `rustc --explain E0308`. diff --git a/src/test/ui/type-alias-impl-trait/issue-57700.full_tait.stderr b/src/test/ui/type-alias-impl-trait/issue-57700.full_tait.stderr new file mode 100644 index 0000000000000..4336532cdbbaf --- /dev/null +++ b/src/test/ui/type-alias-impl-trait/issue-57700.full_tait.stderr @@ -0,0 +1,21 @@ +warning: the feature `type_alias_impl_trait` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/issue-57700.rs:5:32 + | +LL | #![cfg_attr(full_tait, feature(type_alias_impl_trait))] + | ^^^^^^^^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #63063 <https://github.com/rust-lang/rust/issues/63063> for more information + +error: type parameter `impl Deref<Target = Self>` is part of concrete type but not used in parameter list for the `impl Trait` type alias + --> $DIR/issue-57700.rs:19:58 + | +LL | fn foo(self: impl Deref<Target = Self>) -> Self::Bar { + | __________________________________________________________^ +LL | | +LL | | self +LL | | } + | |_____^ + +error: aborting due to previous error; 1 warning emitted + diff --git a/src/test/ui/type-alias-impl-trait/issue-57700.min_tait.stderr b/src/test/ui/type-alias-impl-trait/issue-57700.min_tait.stderr new file mode 100644 index 0000000000000..47ab31cd797b8 --- /dev/null +++ b/src/test/ui/type-alias-impl-trait/issue-57700.min_tait.stderr @@ -0,0 +1,12 @@ +error: type parameter `impl Deref<Target = Self>` is part of concrete type but not used in parameter list for the `impl Trait` type alias + --> $DIR/issue-57700.rs:19:58 + | +LL | fn foo(self: impl Deref<Target = Self>) -> Self::Bar { + | __________________________________________________________^ +LL | | +LL | | self +LL | | } + | |_____^ + +error: aborting due to previous error + diff --git a/src/test/ui/type-alias-impl-trait/issue-57700.rs b/src/test/ui/type-alias-impl-trait/issue-57700.rs index c785ea2ee57d0..476a188cde698 100644 --- a/src/test/ui/type-alias-impl-trait/issue-57700.rs +++ b/src/test/ui/type-alias-impl-trait/issue-57700.rs @@ -1,7 +1,9 @@ -// ignore-tidy-linelength // ignore-compare-mode-chalk #![feature(arbitrary_self_types)] -#![feature(type_alias_impl_trait)] +// revisions: min_tait full_tait +#![feature(min_type_alias_impl_trait)] +#![cfg_attr(full_tait, feature(type_alias_impl_trait))] +//[full_tait]~^ WARN incomplete use std::ops::Deref; diff --git a/src/test/ui/type-alias-impl-trait/issue-57700.stderr b/src/test/ui/type-alias-impl-trait/issue-57700.stderr deleted file mode 100644 index dc6be87ee9365..0000000000000 --- a/src/test/ui/type-alias-impl-trait/issue-57700.stderr +++ /dev/null @@ -1,12 +0,0 @@ -error: type parameter `impl Deref<Target = Self>` is part of concrete type but not used in parameter list for the `impl Trait` type alias - --> $DIR/issue-57700.rs:17:58 - | -LL | fn foo(self: impl Deref<Target = Self>) -> Self::Bar { - | __________________________________________________________^ -LL | | -LL | | self -LL | | } - | |_____^ - -error: aborting due to previous error - diff --git a/src/test/ui/type-alias-impl-trait/issue-57807-associated-type.full_tait.stderr b/src/test/ui/type-alias-impl-trait/issue-57807-associated-type.full_tait.stderr new file mode 100644 index 0000000000000..b35361c89a485 --- /dev/null +++ b/src/test/ui/type-alias-impl-trait/issue-57807-associated-type.full_tait.stderr @@ -0,0 +1,11 @@ +warning: the feature `type_alias_impl_trait` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/issue-57807-associated-type.rs:7:32 + | +LL | #![cfg_attr(full_tait, feature(type_alias_impl_trait))] + | ^^^^^^^^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #63063 <https://github.com/rust-lang/rust/issues/63063> for more information + +warning: 1 warning emitted + diff --git a/src/test/ui/type-alias-impl-trait/issue-57807-associated-type.rs b/src/test/ui/type-alias-impl-trait/issue-57807-associated-type.rs index fcab2c7db2605..0a021149e700d 100644 --- a/src/test/ui/type-alias-impl-trait/issue-57807-associated-type.rs +++ b/src/test/ui/type-alias-impl-trait/issue-57807-associated-type.rs @@ -2,7 +2,10 @@ // that we properly unify associated types within // a type alias impl trait // check-pass -#![feature(type_alias_impl_trait)] +// revisions: min_tait full_tait +#![feature(min_type_alias_impl_trait)] +#![cfg_attr(full_tait, feature(type_alias_impl_trait))] +//[full_tait]~^ WARN incomplete trait Bar { type A; diff --git a/src/test/ui/type-alias-impl-trait/issue-58887.full_tait.stderr b/src/test/ui/type-alias-impl-trait/issue-58887.full_tait.stderr new file mode 100644 index 0000000000000..f0fd5f2f056ab --- /dev/null +++ b/src/test/ui/type-alias-impl-trait/issue-58887.full_tait.stderr @@ -0,0 +1,11 @@ +warning: the feature `type_alias_impl_trait` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/issue-58887.rs:5:32 + | +LL | #![cfg_attr(full_tait, feature(type_alias_impl_trait))] + | ^^^^^^^^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #63063 <https://github.com/rust-lang/rust/issues/63063> for more information + +warning: 1 warning emitted + diff --git a/src/test/ui/type-alias-impl-trait/issue-58887.rs b/src/test/ui/type-alias-impl-trait/issue-58887.rs index 96ac7860283ac..f0803a663de39 100644 --- a/src/test/ui/type-alias-impl-trait/issue-58887.rs +++ b/src/test/ui/type-alias-impl-trait/issue-58887.rs @@ -1,6 +1,9 @@ // run-pass -#![feature(type_alias_impl_trait)] +// revisions: min_tait full_tait +#![feature(min_type_alias_impl_trait)] +#![cfg_attr(full_tait, feature(type_alias_impl_trait))] +//[full_tait]~^ WARN incomplete trait UnwrapItemsExt { type Iter; diff --git a/src/test/ui/type-alias-impl-trait/issue-58951.full_tait.stderr b/src/test/ui/type-alias-impl-trait/issue-58951.full_tait.stderr new file mode 100644 index 0000000000000..164b8881714e7 --- /dev/null +++ b/src/test/ui/type-alias-impl-trait/issue-58951.full_tait.stderr @@ -0,0 +1,11 @@ +warning: the feature `type_alias_impl_trait` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/issue-58951.rs:5:32 + | +LL | #![cfg_attr(full_tait, feature(type_alias_impl_trait))] + | ^^^^^^^^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #63063 <https://github.com/rust-lang/rust/issues/63063> for more information + +warning: 1 warning emitted + diff --git a/src/test/ui/type-alias-impl-trait/issue-58951.rs b/src/test/ui/type-alias-impl-trait/issue-58951.rs index 3416c6745bb0d..3b1ea0d676bb4 100644 --- a/src/test/ui/type-alias-impl-trait/issue-58951.rs +++ b/src/test/ui/type-alias-impl-trait/issue-58951.rs @@ -1,6 +1,9 @@ // check-pass -#![feature(type_alias_impl_trait)] +// revisions: min_tait full_tait +#![feature(min_type_alias_impl_trait)] +#![cfg_attr(full_tait, feature(type_alias_impl_trait))] +//[full_tait]~^ WARN incomplete type A = impl Iterator; diff --git a/src/test/ui/type-alias-impl-trait/issue-60371.rs b/src/test/ui/type-alias-impl-trait/issue-60371.rs index 14d21418ae853..4ac7f9423ff41 100644 --- a/src/test/ui/type-alias-impl-trait/issue-60371.rs +++ b/src/test/ui/type-alias-impl-trait/issue-60371.rs @@ -12,6 +12,7 @@ impl Bug for &() { //~^^ ERROR could not find defining uses const FUN: fn() -> Self::Item = || (); + //~^ ERROR type alias impl trait is not permitted here } fn main() {} diff --git a/src/test/ui/type-alias-impl-trait/issue-60371.stderr b/src/test/ui/type-alias-impl-trait/issue-60371.stderr index d8e514a2cf45b..255d381bf0683 100644 --- a/src/test/ui/type-alias-impl-trait/issue-60371.stderr +++ b/src/test/ui/type-alias-impl-trait/issue-60371.stderr @@ -5,7 +5,16 @@ LL | type Item = impl Bug; | ^^^^^^^^ | = note: see issue #63063 <https://github.com/rust-lang/rust/issues/63063> for more information - = help: add `#![feature(type_alias_impl_trait)]` to the crate attributes to enable + = help: add `#![feature(min_type_alias_impl_trait)]` to the crate attributes to enable + +error[E0658]: type alias impl trait is not permitted here + --> $DIR/issue-60371.rs:14:37 + | +LL | const FUN: fn() -> Self::Item = || (); + | ^^^^^ + | + = note: see issue #63065 <https://github.com/rust-lang/rust/issues/63065> for more information + = help: add `#![feature(impl_trait_in_bindings)]` to the crate attributes to enable error[E0277]: the trait bound `(): Bug` is not satisfied --> $DIR/issue-60371.rs:10:17 @@ -22,7 +31,7 @@ error: could not find defining uses LL | type Item = impl Bug; | ^^^^^^^^ -error: aborting due to 3 previous errors +error: aborting due to 4 previous errors Some errors have detailed explanations: E0277, E0658. For more information about an error, try `rustc --explain E0277`. diff --git a/src/test/ui/type-alias-impl-trait/issue-60407.full_tait.stderr b/src/test/ui/type-alias-impl-trait/issue-60407.full_tait.stderr new file mode 100644 index 0000000000000..5edf73c8cedc6 --- /dev/null +++ b/src/test/ui/type-alias-impl-trait/issue-60407.full_tait.stderr @@ -0,0 +1,25 @@ +warning: the feature `type_alias_impl_trait` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/issue-60407.rs:3:32 + | +LL | #![cfg_attr(full_tait, feature(type_alias_impl_trait, impl_trait_in_bindings))] + | ^^^^^^^^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #63063 <https://github.com/rust-lang/rust/issues/63063> for more information + +warning: the feature `impl_trait_in_bindings` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/issue-60407.rs:3:55 + | +LL | #![cfg_attr(full_tait, feature(type_alias_impl_trait, impl_trait_in_bindings))] + | ^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #63065 <https://github.com/rust-lang/rust/issues/63065> for more information + +error: fatal error triggered by #[rustc_error] + --> $DIR/issue-60407.rs:12:1 + | +LL | fn main() { + | ^^^^^^^^^ + +error: aborting due to previous error; 2 warnings emitted + diff --git a/src/test/ui/type-alias-impl-trait/issue-60407.min_tait.stderr b/src/test/ui/type-alias-impl-trait/issue-60407.min_tait.stderr new file mode 100644 index 0000000000000..edb8141c1b104 --- /dev/null +++ b/src/test/ui/type-alias-impl-trait/issue-60407.min_tait.stderr @@ -0,0 +1,24 @@ +error[E0658]: type alias impl trait is not permitted here + --> $DIR/issue-60407.rs:9:39 + | +LL | static mut TEST: Option<Debuggable> = None; + | ^^^^ + | + = note: see issue #63065 <https://github.com/rust-lang/rust/issues/63065> for more information + = help: add `#![feature(impl_trait_in_bindings)]` to the crate attributes to enable + +error: concrete type differs from previous defining opaque type use + --> $DIR/issue-60407.rs:16:1 + | +LL | fn foo() -> Debuggable { + | ^^^^^^^^^^^^^^^^^^^^^^ expected `[type error]`, got `u32` + | +note: previous use here + --> $DIR/issue-60407.rs:9:1 + | +LL | static mut TEST: Option<Debuggable> = None; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0658`. diff --git a/src/test/ui/type-alias-impl-trait/issue-60407.rs b/src/test/ui/type-alias-impl-trait/issue-60407.rs index 7d462f057cb84..afcbf313cc855 100644 --- a/src/test/ui/type-alias-impl-trait/issue-60407.rs +++ b/src/test/ui/type-alias-impl-trait/issue-60407.rs @@ -1,15 +1,18 @@ -// check-pass - -#![feature(type_alias_impl_trait)] +// revisions: min_tait full_tait +#![feature(min_type_alias_impl_trait, rustc_attrs)] +#![cfg_attr(full_tait, feature(type_alias_impl_trait, impl_trait_in_bindings))] +//[full_tait]~^ WARN incomplete +//[full_tait]~| WARN incomplete type Debuggable = impl core::fmt::Debug; -static mut TEST: Option<Debuggable> = None; +static mut TEST: Option<Debuggable> = None; //[min_tait]~ ERROR not permitted here -fn main() { +#[rustc_error] +fn main() { //[full_tait]~ ERROR unsafe { TEST = Some(foo()) } } -fn foo() -> Debuggable { +fn foo() -> Debuggable { //[min_tait]~ ERROR concrete type differs 0u32 } diff --git a/src/test/ui/type-alias-impl-trait/issue-60564.full_tait.stderr b/src/test/ui/type-alias-impl-trait/issue-60564.full_tait.stderr new file mode 100644 index 0000000000000..72b6b0648f98f --- /dev/null +++ b/src/test/ui/type-alias-impl-trait/issue-60564.full_tait.stderr @@ -0,0 +1,23 @@ +warning: the feature `type_alias_impl_trait` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/issue-60564.rs:3:32 + | +LL | #![cfg_attr(full_tait, feature(type_alias_impl_trait))] + | ^^^^^^^^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #63063 <https://github.com/rust-lang/rust/issues/63063> for more information + +error: non-defining opaque type use in defining scope + --> $DIR/issue-60564.rs:22:34 + | +LL | fn iter_bits(self, n: u8) -> Self::BitsIter { + | ^^^^^^^^^^^^^^ + | +note: used non-generic type `u8` for generic parameter + --> $DIR/issue-60564.rs:11:25 + | +LL | type IterBitsIter<T, E, I> = impl std::iter::Iterator<Item = I>; + | ^ + +error: aborting due to previous error; 1 warning emitted + diff --git a/src/test/ui/type-alias-impl-trait/issue-60564.min_tait.stderr b/src/test/ui/type-alias-impl-trait/issue-60564.min_tait.stderr new file mode 100644 index 0000000000000..72668c8056fc5 --- /dev/null +++ b/src/test/ui/type-alias-impl-trait/issue-60564.min_tait.stderr @@ -0,0 +1,14 @@ +error: non-defining opaque type use in defining scope + --> $DIR/issue-60564.rs:22:34 + | +LL | fn iter_bits(self, n: u8) -> Self::BitsIter { + | ^^^^^^^^^^^^^^ + | +note: used non-generic type `u8` for generic parameter + --> $DIR/issue-60564.rs:11:25 + | +LL | type IterBitsIter<T, E, I> = impl std::iter::Iterator<Item = I>; + | ^ + +error: aborting due to previous error + diff --git a/src/test/ui/type-alias-impl-trait/issue-60564.rs b/src/test/ui/type-alias-impl-trait/issue-60564.rs index 78def0d1136de..10fbbd2ef292e 100644 --- a/src/test/ui/type-alias-impl-trait/issue-60564.rs +++ b/src/test/ui/type-alias-impl-trait/issue-60564.rs @@ -1,4 +1,7 @@ -#![feature(type_alias_impl_trait)] +// revisions: min_tait full_tait +#![feature(min_type_alias_impl_trait)] +#![cfg_attr(full_tait, feature(type_alias_impl_trait))] +//[full_tait]~^ WARN incomplete trait IterBits { type BitsIter: Iterator<Item = u8>; diff --git a/src/test/ui/type-alias-impl-trait/issue-60564.stderr b/src/test/ui/type-alias-impl-trait/issue-60564.stderr deleted file mode 100644 index 66fa862ef9d7a..0000000000000 --- a/src/test/ui/type-alias-impl-trait/issue-60564.stderr +++ /dev/null @@ -1,14 +0,0 @@ -error: non-defining opaque type use in defining scope - --> $DIR/issue-60564.rs:19:34 - | -LL | fn iter_bits(self, n: u8) -> Self::BitsIter { - | ^^^^^^^^^^^^^^ - | -note: used non-generic type `u8` for generic parameter - --> $DIR/issue-60564.rs:8:25 - | -LL | type IterBitsIter<T, E, I> = impl std::iter::Iterator<Item = I>; - | ^ - -error: aborting due to previous error - diff --git a/src/test/ui/type-alias-impl-trait/issue-62000-associate-impl-trait-lifetimes.full_tait.stderr b/src/test/ui/type-alias-impl-trait/issue-62000-associate-impl-trait-lifetimes.full_tait.stderr new file mode 100644 index 0000000000000..1fc38d2f549a1 --- /dev/null +++ b/src/test/ui/type-alias-impl-trait/issue-62000-associate-impl-trait-lifetimes.full_tait.stderr @@ -0,0 +1,11 @@ +warning: the feature `type_alias_impl_trait` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/issue-62000-associate-impl-trait-lifetimes.rs:7:32 + | +LL | #![cfg_attr(full_tait, feature(type_alias_impl_trait))] + | ^^^^^^^^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #63063 <https://github.com/rust-lang/rust/issues/63063> for more information + +warning: 1 warning emitted + diff --git a/src/test/ui/type-alias-impl-trait/issue-62000-associate-impl-trait-lifetimes.rs b/src/test/ui/type-alias-impl-trait/issue-62000-associate-impl-trait-lifetimes.rs index 36779a0ce89c3..4d9ca47b9474a 100644 --- a/src/test/ui/type-alias-impl-trait/issue-62000-associate-impl-trait-lifetimes.rs +++ b/src/test/ui/type-alias-impl-trait/issue-62000-associate-impl-trait-lifetimes.rs @@ -2,7 +2,10 @@ // check-pass -#![feature(type_alias_impl_trait)] +// revisions: min_tait full_tait +#![feature(min_type_alias_impl_trait)] +#![cfg_attr(full_tait, feature(type_alias_impl_trait))] +//[full_tait]~^ WARN incomplete trait MyTrait { type AssocType: Send; diff --git a/src/test/ui/type-alias-impl-trait/issue-63263-closure-return.full_tait.stderr b/src/test/ui/type-alias-impl-trait/issue-63263-closure-return.full_tait.stderr new file mode 100644 index 0000000000000..1c6759e3454b5 --- /dev/null +++ b/src/test/ui/type-alias-impl-trait/issue-63263-closure-return.full_tait.stderr @@ -0,0 +1,11 @@ +warning: the feature `type_alias_impl_trait` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/issue-63263-closure-return.rs:9:32 + | +LL | #![cfg_attr(full_tait, feature(type_alias_impl_trait))] + | ^^^^^^^^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #63063 <https://github.com/rust-lang/rust/issues/63063> for more information + +warning: 1 warning emitted + diff --git a/src/test/ui/type-alias-impl-trait/issue-63263-closure-return.rs b/src/test/ui/type-alias-impl-trait/issue-63263-closure-return.rs index 7414611a74893..3b8f6afcb0054 100644 --- a/src/test/ui/type-alias-impl-trait/issue-63263-closure-return.rs +++ b/src/test/ui/type-alias-impl-trait/issue-63263-closure-return.rs @@ -4,7 +4,8 @@ // check-pass -#![feature(type_alias_impl_trait)] +#![feature(min_type_alias_impl_trait, type_alias_impl_trait)] +//~^ WARN incomplete pub type Closure = impl FnOnce(); diff --git a/src/test/ui/type-alias-impl-trait/issue-63263-closure-return.stderr b/src/test/ui/type-alias-impl-trait/issue-63263-closure-return.stderr new file mode 100644 index 0000000000000..e1f4ecd3b9814 --- /dev/null +++ b/src/test/ui/type-alias-impl-trait/issue-63263-closure-return.stderr @@ -0,0 +1,11 @@ +warning: the feature `type_alias_impl_trait` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/issue-63263-closure-return.rs:7:39 + | +LL | #![feature(min_type_alias_impl_trait, type_alias_impl_trait)] + | ^^^^^^^^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #63063 <https://github.com/rust-lang/rust/issues/63063> for more information + +warning: 1 warning emitted + diff --git a/src/test/ui/type-alias-impl-trait/issue-63279.full_tait.stderr b/src/test/ui/type-alias-impl-trait/issue-63279.full_tait.stderr new file mode 100644 index 0000000000000..53a0016c08eb5 --- /dev/null +++ b/src/test/ui/type-alias-impl-trait/issue-63279.full_tait.stderr @@ -0,0 +1,21 @@ +warning: the feature `type_alias_impl_trait` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/issue-63279.rs:5:32 + | +LL | #![cfg_attr(full_tait, feature(type_alias_impl_trait))] + | ^^^^^^^^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #63063 <https://github.com/rust-lang/rust/issues/63063> for more information + +error[E0271]: type mismatch resolving `<[closure@$DIR/issue-63279.rs:11:5: 11:28] as FnOnce<()>>::Output == ()` + --> $DIR/issue-63279.rs:8:16 + | +LL | type Closure = impl FnOnce(); + | ^^^^^^^^^^^^^ expected `()`, found opaque type + | + = note: expected unit type `()` + found opaque type `impl FnOnce<()>` + +error: aborting due to previous error; 1 warning emitted + +For more information about this error, try `rustc --explain E0271`. diff --git a/src/test/ui/type-alias-impl-trait/issue-63279.min_tait.stderr b/src/test/ui/type-alias-impl-trait/issue-63279.min_tait.stderr new file mode 100644 index 0000000000000..be386ab90ea00 --- /dev/null +++ b/src/test/ui/type-alias-impl-trait/issue-63279.min_tait.stderr @@ -0,0 +1,22 @@ +error[E0658]: type alias impl trait is not permitted here + --> $DIR/issue-63279.rs:11:11 + | +LL | || -> Closure { || () } + | ^^^^^^^ + | + = note: see issue #63063 <https://github.com/rust-lang/rust/issues/63063> for more information + = help: add `#![feature(type_alias_impl_trait)]` to the crate attributes to enable + +error[E0271]: type mismatch resolving `<[closure@$DIR/issue-63279.rs:11:5: 11:28] as FnOnce<()>>::Output == ()` + --> $DIR/issue-63279.rs:8:16 + | +LL | type Closure = impl FnOnce(); + | ^^^^^^^^^^^^^ expected `()`, found opaque type + | + = note: expected unit type `()` + found opaque type `impl FnOnce<()>` + +error: aborting due to 2 previous errors + +Some errors have detailed explanations: E0271, E0658. +For more information about an error, try `rustc --explain E0271`. diff --git a/src/test/ui/type-alias-impl-trait/issue-63279.rs b/src/test/ui/type-alias-impl-trait/issue-63279.rs index b97192a2aed4a..086715626bc16 100644 --- a/src/test/ui/type-alias-impl-trait/issue-63279.rs +++ b/src/test/ui/type-alias-impl-trait/issue-63279.rs @@ -1,11 +1,14 @@ // compile-flags: -Zsave-analysis -#![feature(type_alias_impl_trait)] +// revisions: min_tait full_tait +#![feature(min_type_alias_impl_trait)] +#![cfg_attr(full_tait, feature(type_alias_impl_trait))] +//[full_tait]~^ WARN incomplete type Closure = impl FnOnce(); //~ ERROR: type mismatch resolving fn c() -> Closure { - || -> Closure { || () } + || -> Closure { || () } //[min_tait]~ ERROR: not permitted here } fn main() {} diff --git a/src/test/ui/type-alias-impl-trait/issue-63279.stderr b/src/test/ui/type-alias-impl-trait/issue-63279.stderr deleted file mode 100644 index 8615b3f741bf5..0000000000000 --- a/src/test/ui/type-alias-impl-trait/issue-63279.stderr +++ /dev/null @@ -1,12 +0,0 @@ -error[E0271]: type mismatch resolving `<[closure@$DIR/issue-63279.rs:8:5: 8:28] as FnOnce<()>>::Output == ()` - --> $DIR/issue-63279.rs:5:16 - | -LL | type Closure = impl FnOnce(); - | ^^^^^^^^^^^^^ expected opaque type, found `()` - | - = note: expected opaque type `impl FnOnce<()>` - found unit type `()` - -error: aborting due to previous error - -For more information about this error, try `rustc --explain E0271`. diff --git a/src/test/ui/type-alias-impl-trait/issue-63677-type-alias-coherence.full_tait.stderr b/src/test/ui/type-alias-impl-trait/issue-63677-type-alias-coherence.full_tait.stderr new file mode 100644 index 0000000000000..fc8d8147167b3 --- /dev/null +++ b/src/test/ui/type-alias-impl-trait/issue-63677-type-alias-coherence.full_tait.stderr @@ -0,0 +1,11 @@ +warning: the feature `type_alias_impl_trait` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/issue-63677-type-alias-coherence.rs:7:32 + | +LL | #![cfg_attr(full_tait, feature(type_alias_impl_trait))] + | ^^^^^^^^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #63063 <https://github.com/rust-lang/rust/issues/63063> for more information + +warning: 1 warning emitted + diff --git a/src/test/ui/type-alias-impl-trait/issue-63677-type-alias-coherence.rs b/src/test/ui/type-alias-impl-trait/issue-63677-type-alias-coherence.rs index 28f4a85c9f290..62d11ad38d62d 100644 --- a/src/test/ui/type-alias-impl-trait/issue-63677-type-alias-coherence.rs +++ b/src/test/ui/type-alias-impl-trait/issue-63677-type-alias-coherence.rs @@ -2,7 +2,10 @@ // Regression test for issue #63677 - ensure that // coherence checking can properly handle 'impl trait' // in type aliases -#![feature(type_alias_impl_trait)] +// revisions: min_tait full_tait +#![feature(min_type_alias_impl_trait)] +#![cfg_attr(full_tait, feature(type_alias_impl_trait))] +//[full_tait]~^ WARN incomplete pub trait Trait {} pub struct S1<T>(T); diff --git a/src/test/ui/type-alias-impl-trait/issue-65679-inst-opaque-ty-from-val-twice.full_tait.stderr b/src/test/ui/type-alias-impl-trait/issue-65679-inst-opaque-ty-from-val-twice.full_tait.stderr new file mode 100644 index 0000000000000..cf668fc6e0666 --- /dev/null +++ b/src/test/ui/type-alias-impl-trait/issue-65679-inst-opaque-ty-from-val-twice.full_tait.stderr @@ -0,0 +1,25 @@ +warning: the feature `type_alias_impl_trait` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/issue-65679-inst-opaque-ty-from-val-twice.rs:5:32 + | +LL | #![cfg_attr(full_tait, feature(type_alias_impl_trait, impl_trait_in_bindings))] + | ^^^^^^^^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #63063 <https://github.com/rust-lang/rust/issues/63063> for more information + +warning: the feature `impl_trait_in_bindings` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/issue-65679-inst-opaque-ty-from-val-twice.rs:5:55 + | +LL | #![cfg_attr(full_tait, feature(type_alias_impl_trait, impl_trait_in_bindings))] + | ^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #63065 <https://github.com/rust-lang/rust/issues/63065> for more information + +error: fatal error triggered by #[rustc_error] + --> $DIR/issue-65679-inst-opaque-ty-from-val-twice.rs:19:1 + | +LL | fn main() { + | ^^^^^^^^^ + +error: aborting due to previous error; 2 warnings emitted + diff --git a/src/test/ui/type-alias-impl-trait/issue-65679-inst-opaque-ty-from-val-twice.min_tait.stderr b/src/test/ui/type-alias-impl-trait/issue-65679-inst-opaque-ty-from-val-twice.min_tait.stderr new file mode 100644 index 0000000000000..5b6c4b5cda1fd --- /dev/null +++ b/src/test/ui/type-alias-impl-trait/issue-65679-inst-opaque-ty-from-val-twice.min_tait.stderr @@ -0,0 +1,21 @@ +error[E0658]: type alias impl trait is not permitted here + --> $DIR/issue-65679-inst-opaque-ty-from-val-twice.rs:20:13 + | +LL | take(|| {}); + | ^ + | + = note: see issue #63063 <https://github.com/rust-lang/rust/issues/63063> for more information + = help: add `#![feature(type_alias_impl_trait)]` to the crate attributes to enable + +error[E0658]: type alias impl trait is not permitted here + --> $DIR/issue-65679-inst-opaque-ty-from-val-twice.rs:22:13 + | +LL | take(|| {}); + | ^ + | + = note: see issue #63063 <https://github.com/rust-lang/rust/issues/63063> for more information + = help: add `#![feature(type_alias_impl_trait)]` to the crate attributes to enable + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0658`. diff --git a/src/test/ui/type-alias-impl-trait/issue-65679-inst-opaque-ty-from-val-twice.rs b/src/test/ui/type-alias-impl-trait/issue-65679-inst-opaque-ty-from-val-twice.rs index 26d97cea989c5..b7f05bd83ed6e 100644 --- a/src/test/ui/type-alias-impl-trait/issue-65679-inst-opaque-ty-from-val-twice.rs +++ b/src/test/ui/type-alias-impl-trait/issue-65679-inst-opaque-ty-from-val-twice.rs @@ -1,7 +1,10 @@ // compile-flags: -Zsave-analysis -// check-pass -#![feature(type_alias_impl_trait)] +// revisions: min_tait full_tait +#![feature(min_type_alias_impl_trait, rustc_attrs)] +#![cfg_attr(full_tait, feature(type_alias_impl_trait, impl_trait_in_bindings))] +//[full_tait]~^ WARN incomplete +//[full_tait]~| WARN incomplete type T = impl Sized; // The concrete type referred by impl-trait-type-alias(`T`) is guaranteed @@ -12,7 +15,10 @@ type T = impl Sized; fn take(_: fn() -> T) {} -fn main() { +#[rustc_error] +fn main() { //[full_tait]~ ERROR fatal error triggered by #[rustc_error] take(|| {}); + //[min_tait]~^ ERROR not permitted here take(|| {}); + //[min_tait]~^ ERROR not permitted here } diff --git a/src/test/ui/type-alias-impl-trait/issue-65918.rs b/src/test/ui/type-alias-impl-trait/issue-65918.rs index af6d501092017..ab05e4b1cbeca 100644 --- a/src/test/ui/type-alias-impl-trait/issue-65918.rs +++ b/src/test/ui/type-alias-impl-trait/issue-65918.rs @@ -2,7 +2,10 @@ // build-pass -#![feature(type_alias_impl_trait)] +// revisions: min_tait full_tait +#![feature(min_type_alias_impl_trait)] +#![cfg_attr(full_tait, feature(type_alias_impl_trait))] +//[full_tait]~^ WARN incomplete use std::marker::PhantomData; diff --git a/src/test/ui/type-alias-impl-trait/issue-66580-closure-coherence.full_tait.stderr b/src/test/ui/type-alias-impl-trait/issue-66580-closure-coherence.full_tait.stderr new file mode 100644 index 0000000000000..2fc46574512cc --- /dev/null +++ b/src/test/ui/type-alias-impl-trait/issue-66580-closure-coherence.full_tait.stderr @@ -0,0 +1,11 @@ +warning: the feature `type_alias_impl_trait` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/issue-66580-closure-coherence.rs:7:32 + | +LL | #![cfg_attr(full_tait, feature(type_alias_impl_trait))] + | ^^^^^^^^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #63063 <https://github.com/rust-lang/rust/issues/63063> for more information + +warning: 1 warning emitted + diff --git a/src/test/ui/type-alias-impl-trait/issue-66580-closure-coherence.rs b/src/test/ui/type-alias-impl-trait/issue-66580-closure-coherence.rs index 1d95cc7530c5e..7b3ddc7886841 100644 --- a/src/test/ui/type-alias-impl-trait/issue-66580-closure-coherence.rs +++ b/src/test/ui/type-alias-impl-trait/issue-66580-closure-coherence.rs @@ -2,7 +2,10 @@ // Ensures that we don't try to determine whether a closure // is foreign when it's the underlying type of an opaque type // check-pass -#![feature(type_alias_impl_trait)] +// revisions: min_tait full_tait +#![feature(min_type_alias_impl_trait)] +#![cfg_attr(full_tait, feature(type_alias_impl_trait))] +//[full_tait]~^ WARN incomplete type Closure = impl FnOnce(); diff --git a/src/test/ui/type-alias-impl-trait/issue-67844-nested-opaque.full_tait.stderr b/src/test/ui/type-alias-impl-trait/issue-67844-nested-opaque.full_tait.stderr new file mode 100644 index 0000000000000..81e27da0949af --- /dev/null +++ b/src/test/ui/type-alias-impl-trait/issue-67844-nested-opaque.full_tait.stderr @@ -0,0 +1,11 @@ +warning: the feature `type_alias_impl_trait` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/issue-67844-nested-opaque.rs:8:32 + | +LL | #![cfg_attr(full_tait, feature(type_alias_impl_trait))] + | ^^^^^^^^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #63063 <https://github.com/rust-lang/rust/issues/63063> for more information + +warning: 1 warning emitted + diff --git a/src/test/ui/type-alias-impl-trait/issue-67844-nested-opaque.rs b/src/test/ui/type-alias-impl-trait/issue-67844-nested-opaque.rs index 7da0b049264c2..b9e29a01cddf9 100644 --- a/src/test/ui/type-alias-impl-trait/issue-67844-nested-opaque.rs +++ b/src/test/ui/type-alias-impl-trait/issue-67844-nested-opaque.rs @@ -3,7 +3,10 @@ // Ensures that we properly handle nested TAIT occurrences // with generic parameters -#![feature(type_alias_impl_trait)] +// revisions: min_tait full_tait +#![feature(min_type_alias_impl_trait)] +#![cfg_attr(full_tait, feature(type_alias_impl_trait))] +//[full_tait]~^ WARN incomplete trait WithAssoc { type AssocType; } diff --git a/src/test/ui/type-alias-impl-trait/issue-68368-non-defining-use.full_tait.stderr b/src/test/ui/type-alias-impl-trait/issue-68368-non-defining-use.full_tait.stderr new file mode 100644 index 0000000000000..8b1ad19ac0cbf --- /dev/null +++ b/src/test/ui/type-alias-impl-trait/issue-68368-non-defining-use.full_tait.stderr @@ -0,0 +1,23 @@ +warning: the feature `type_alias_impl_trait` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/issue-68368-non-defining-use.rs:7:32 + | +LL | #![cfg_attr(full_tait, feature(type_alias_impl_trait))] + | ^^^^^^^^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #63063 <https://github.com/rust-lang/rust/issues/63063> for more information + +error: non-defining opaque type use in defining scope + --> $DIR/issue-68368-non-defining-use.rs:11:15 + | +LL | fn f<'a>() -> Alias<'a, ()> {} + | ^^^^^^^^^^^^^ + | +note: used non-generic type `()` for generic parameter + --> $DIR/issue-68368-non-defining-use.rs:10:16 + | +LL | type Alias<'a, U> = impl Trait<U>; + | ^ + +error: aborting due to previous error; 1 warning emitted + diff --git a/src/test/ui/type-alias-impl-trait/issue-68368-non-defining-use.min_tait.stderr b/src/test/ui/type-alias-impl-trait/issue-68368-non-defining-use.min_tait.stderr new file mode 100644 index 0000000000000..01ae457851f24 --- /dev/null +++ b/src/test/ui/type-alias-impl-trait/issue-68368-non-defining-use.min_tait.stderr @@ -0,0 +1,14 @@ +error: non-defining opaque type use in defining scope + --> $DIR/issue-68368-non-defining-use.rs:11:15 + | +LL | fn f<'a>() -> Alias<'a, ()> {} + | ^^^^^^^^^^^^^ + | +note: used non-generic type `()` for generic parameter + --> $DIR/issue-68368-non-defining-use.rs:10:16 + | +LL | type Alias<'a, U> = impl Trait<U>; + | ^ + +error: aborting due to previous error + diff --git a/src/test/ui/type-alias-impl-trait/issue-68368-non-defining-use.rs b/src/test/ui/type-alias-impl-trait/issue-68368-non-defining-use.rs index 3b6decbe9c65e..92465076733bc 100644 --- a/src/test/ui/type-alias-impl-trait/issue-68368-non-defining-use.rs +++ b/src/test/ui/type-alias-impl-trait/issue-68368-non-defining-use.rs @@ -2,7 +2,10 @@ // Ensures that we don't ICE when emitting an error // for a non-defining use when lifetimes are involved -#![feature(type_alias_impl_trait)] +// revisions: min_tait full_tait +#![feature(min_type_alias_impl_trait)] +#![cfg_attr(full_tait, feature(type_alias_impl_trait))] +//[full_tait]~^ WARN incomplete trait Trait<T> {} type Alias<'a, U> = impl Trait<U>; fn f<'a>() -> Alias<'a, ()> {} diff --git a/src/test/ui/type-alias-impl-trait/issue-68368-non-defining-use.stderr b/src/test/ui/type-alias-impl-trait/issue-68368-non-defining-use.stderr deleted file mode 100644 index c2fa54f50f881..0000000000000 --- a/src/test/ui/type-alias-impl-trait/issue-68368-non-defining-use.stderr +++ /dev/null @@ -1,14 +0,0 @@ -error: non-defining opaque type use in defining scope - --> $DIR/issue-68368-non-defining-use.rs:8:15 - | -LL | fn f<'a>() -> Alias<'a, ()> {} - | ^^^^^^^^^^^^^ - | -note: used non-generic type `()` for generic parameter - --> $DIR/issue-68368-non-defining-use.rs:7:16 - | -LL | type Alias<'a, U> = impl Trait<U>; - | ^ - -error: aborting due to previous error - diff --git a/src/test/ui/type-alias-impl-trait/issue-69136-inner-lifetime-resolve-error.full_tait.stderr b/src/test/ui/type-alias-impl-trait/issue-69136-inner-lifetime-resolve-error.full_tait.stderr new file mode 100644 index 0000000000000..7f120af2408bf --- /dev/null +++ b/src/test/ui/type-alias-impl-trait/issue-69136-inner-lifetime-resolve-error.full_tait.stderr @@ -0,0 +1,20 @@ +warning: the feature `type_alias_impl_trait` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/issue-69136-inner-lifetime-resolve-error.rs:5:32 + | +LL | #![cfg_attr(full_tait, feature(type_alias_impl_trait))] + | ^^^^^^^^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #63063 <https://github.com/rust-lang/rust/issues/63063> for more information + +error[E0261]: use of undeclared lifetime name `'a` + --> $DIR/issue-69136-inner-lifetime-resolve-error.rs:20:65 + | +LL | type Return<A> = impl WithAssoc<A, AssocType = impl SomeTrait + 'a>; + | - ^^ undeclared lifetime + | | + | help: consider introducing lifetime `'a` here: `'a,` + +error: aborting due to previous error; 1 warning emitted + +For more information about this error, try `rustc --explain E0261`. diff --git a/src/test/ui/type-alias-impl-trait/issue-69136-inner-lifetime-resolve-error.min_tait.stderr b/src/test/ui/type-alias-impl-trait/issue-69136-inner-lifetime-resolve-error.min_tait.stderr new file mode 100644 index 0000000000000..e1fc94c3472a3 --- /dev/null +++ b/src/test/ui/type-alias-impl-trait/issue-69136-inner-lifetime-resolve-error.min_tait.stderr @@ -0,0 +1,11 @@ +error[E0261]: use of undeclared lifetime name `'a` + --> $DIR/issue-69136-inner-lifetime-resolve-error.rs:20:65 + | +LL | type Return<A> = impl WithAssoc<A, AssocType = impl SomeTrait + 'a>; + | - ^^ undeclared lifetime + | | + | help: consider introducing lifetime `'a` here: `'a,` + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0261`. diff --git a/src/test/ui/type-alias-impl-trait/issue-69136-inner-lifetime-resolve-error.rs b/src/test/ui/type-alias-impl-trait/issue-69136-inner-lifetime-resolve-error.rs index 6732902c09a50..61161d44e51ab 100644 --- a/src/test/ui/type-alias-impl-trait/issue-69136-inner-lifetime-resolve-error.rs +++ b/src/test/ui/type-alias-impl-trait/issue-69136-inner-lifetime-resolve-error.rs @@ -1,6 +1,9 @@ // Regression test for #69136 -#![feature(type_alias_impl_trait)] +// revisions: min_tait full_tait +#![feature(min_type_alias_impl_trait)] +#![cfg_attr(full_tait, feature(type_alias_impl_trait))] +//[full_tait]~^ WARN incomplete trait SomeTrait {} diff --git a/src/test/ui/type-alias-impl-trait/issue-69136-inner-lifetime-resolve-error.stderr b/src/test/ui/type-alias-impl-trait/issue-69136-inner-lifetime-resolve-error.stderr deleted file mode 100644 index fe45e39d938f0..0000000000000 --- a/src/test/ui/type-alias-impl-trait/issue-69136-inner-lifetime-resolve-error.stderr +++ /dev/null @@ -1,11 +0,0 @@ -error[E0261]: use of undeclared lifetime name `'a` - --> $DIR/issue-69136-inner-lifetime-resolve-error.rs:17:65 - | -LL | type Return<A> = impl WithAssoc<A, AssocType = impl SomeTrait + 'a>; - | - ^^ undeclared lifetime - | | - | help: consider introducing lifetime `'a` here: `'a,` - -error: aborting due to previous error - -For more information about this error, try `rustc --explain E0261`. diff --git a/src/test/ui/type-alias-impl-trait/issue-69136-inner-lifetime-resolve-ok.full_tait.stderr b/src/test/ui/type-alias-impl-trait/issue-69136-inner-lifetime-resolve-ok.full_tait.stderr new file mode 100644 index 0000000000000..61e2e20dfd043 --- /dev/null +++ b/src/test/ui/type-alias-impl-trait/issue-69136-inner-lifetime-resolve-ok.full_tait.stderr @@ -0,0 +1,11 @@ +warning: the feature `type_alias_impl_trait` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/issue-69136-inner-lifetime-resolve-ok.rs:7:32 + | +LL | #![cfg_attr(full_tait, feature(type_alias_impl_trait))] + | ^^^^^^^^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #63063 <https://github.com/rust-lang/rust/issues/63063> for more information + +warning: 1 warning emitted + diff --git a/src/test/ui/type-alias-impl-trait/issue-69136-inner-lifetime-resolve-ok.rs b/src/test/ui/type-alias-impl-trait/issue-69136-inner-lifetime-resolve-ok.rs index a6916eda8b093..01df967a4a515 100644 --- a/src/test/ui/type-alias-impl-trait/issue-69136-inner-lifetime-resolve-ok.rs +++ b/src/test/ui/type-alias-impl-trait/issue-69136-inner-lifetime-resolve-ok.rs @@ -2,7 +2,10 @@ // check-pass -#![feature(type_alias_impl_trait)] +// revisions: min_tait full_tait +#![feature(min_type_alias_impl_trait)] +#![cfg_attr(full_tait, feature(type_alias_impl_trait))] +//[full_tait]~^ WARN incomplete trait SomeTrait {} diff --git a/src/test/ui/type-alias-impl-trait/issue-70121.full_tait.stderr b/src/test/ui/type-alias-impl-trait/issue-70121.full_tait.stderr new file mode 100644 index 0000000000000..9fe2e2cf88ee9 --- /dev/null +++ b/src/test/ui/type-alias-impl-trait/issue-70121.full_tait.stderr @@ -0,0 +1,11 @@ +warning: the feature `type_alias_impl_trait` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/issue-70121.rs:5:32 + | +LL | #![cfg_attr(full_tait, feature(type_alias_impl_trait))] + | ^^^^^^^^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #63063 <https://github.com/rust-lang/rust/issues/63063> for more information + +warning: 1 warning emitted + diff --git a/src/test/ui/type-alias-impl-trait/issue-70121.rs b/src/test/ui/type-alias-impl-trait/issue-70121.rs index dff0d89d465dd..150eca46975da 100644 --- a/src/test/ui/type-alias-impl-trait/issue-70121.rs +++ b/src/test/ui/type-alias-impl-trait/issue-70121.rs @@ -1,6 +1,9 @@ // check-pass -#![feature(type_alias_impl_trait)] +// revisions: min_tait full_tait +#![feature(min_type_alias_impl_trait)] +#![cfg_attr(full_tait, feature(type_alias_impl_trait))] +//[full_tait]~^ WARN incomplete pub type Successors<'a> = impl Iterator<Item = &'a ()>; diff --git a/src/test/ui/type-alias-impl-trait/issue-74244.full_tait.stderr b/src/test/ui/type-alias-impl-trait/issue-74244.full_tait.stderr new file mode 100644 index 0000000000000..e7729e47c36dd --- /dev/null +++ b/src/test/ui/type-alias-impl-trait/issue-74244.full_tait.stderr @@ -0,0 +1,18 @@ +warning: the feature `type_alias_impl_trait` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/issue-74244.rs:3:32 + | +LL | #![cfg_attr(full_tait, feature(type_alias_impl_trait))] + | ^^^^^^^^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #63063 <https://github.com/rust-lang/rust/issues/63063> for more information + +error[E0207]: the type parameter `T` is not constrained by the impl trait, self type, or predicates + --> $DIR/issue-74244.rs:12:6 + | +LL | impl<T> Allocator for DefaultAllocator { + | ^ unconstrained type parameter + +error: aborting due to previous error; 1 warning emitted + +For more information about this error, try `rustc --explain E0207`. diff --git a/src/test/ui/type-alias-impl-trait/issue-74244.min_tait.stderr b/src/test/ui/type-alias-impl-trait/issue-74244.min_tait.stderr new file mode 100644 index 0000000000000..6ee8c58826dd4 --- /dev/null +++ b/src/test/ui/type-alias-impl-trait/issue-74244.min_tait.stderr @@ -0,0 +1,9 @@ +error[E0207]: the type parameter `T` is not constrained by the impl trait, self type, or predicates + --> $DIR/issue-74244.rs:12:6 + | +LL | impl<T> Allocator for DefaultAllocator { + | ^ unconstrained type parameter + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0207`. diff --git a/src/test/ui/type-alias-impl-trait/issue-74244.rs b/src/test/ui/type-alias-impl-trait/issue-74244.rs index bb4104b3d2519..890eb23ef9511 100644 --- a/src/test/ui/type-alias-impl-trait/issue-74244.rs +++ b/src/test/ui/type-alias-impl-trait/issue-74244.rs @@ -1,4 +1,7 @@ -#![feature(type_alias_impl_trait)] +// revisions: min_tait full_tait +#![feature(min_type_alias_impl_trait)] +#![cfg_attr(full_tait, feature(type_alias_impl_trait))] +//[full_tait]~^ WARN incomplete trait Allocator { type Buffer; diff --git a/src/test/ui/type-alias-impl-trait/issue-74244.stderr b/src/test/ui/type-alias-impl-trait/issue-74244.stderr deleted file mode 100644 index ff6bacd277e1e..0000000000000 --- a/src/test/ui/type-alias-impl-trait/issue-74244.stderr +++ /dev/null @@ -1,9 +0,0 @@ -error[E0207]: the type parameter `T` is not constrained by the impl trait, self type, or predicates - --> $DIR/issue-74244.rs:9:6 - | -LL | impl<T> Allocator for DefaultAllocator { - | ^ unconstrained type parameter - -error: aborting due to previous error - -For more information about this error, try `rustc --explain E0207`. diff --git a/src/test/ui/type-alias-impl-trait/issue-74761.full_tait.stderr b/src/test/ui/type-alias-impl-trait/issue-74761.full_tait.stderr new file mode 100644 index 0000000000000..0880136d71b01 --- /dev/null +++ b/src/test/ui/type-alias-impl-trait/issue-74761.full_tait.stderr @@ -0,0 +1,24 @@ +warning: the feature `type_alias_impl_trait` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/issue-74761.rs:4:32 + | +LL | #![cfg_attr(full_tait, feature(type_alias_impl_trait))] + | ^^^^^^^^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #63063 <https://github.com/rust-lang/rust/issues/63063> for more information + +error[E0207]: the lifetime parameter `'a` is not constrained by the impl trait, self type, or predicates + --> $DIR/issue-74761.rs:11:6 + | +LL | impl<'a, 'b> A for () { + | ^^ unconstrained lifetime parameter + +error[E0207]: the lifetime parameter `'b` is not constrained by the impl trait, self type, or predicates + --> $DIR/issue-74761.rs:11:10 + | +LL | impl<'a, 'b> A for () { + | ^^ unconstrained lifetime parameter + +error: aborting due to 2 previous errors; 1 warning emitted + +For more information about this error, try `rustc --explain E0207`. diff --git a/src/test/ui/type-alias-impl-trait/issue-74761.min_tait.stderr b/src/test/ui/type-alias-impl-trait/issue-74761.min_tait.stderr new file mode 100644 index 0000000000000..20ebdd9cb504c --- /dev/null +++ b/src/test/ui/type-alias-impl-trait/issue-74761.min_tait.stderr @@ -0,0 +1,15 @@ +error[E0207]: the lifetime parameter `'a` is not constrained by the impl trait, self type, or predicates + --> $DIR/issue-74761.rs:11:6 + | +LL | impl<'a, 'b> A for () { + | ^^ unconstrained lifetime parameter + +error[E0207]: the lifetime parameter `'b` is not constrained by the impl trait, self type, or predicates + --> $DIR/issue-74761.rs:11:10 + | +LL | impl<'a, 'b> A for () { + | ^^ unconstrained lifetime parameter + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0207`. diff --git a/src/test/ui/type-alias-impl-trait/issue-74761.rs b/src/test/ui/type-alias-impl-trait/issue-74761.rs index 4345b5d886ee2..66bb079b25a81 100644 --- a/src/test/ui/type-alias-impl-trait/issue-74761.rs +++ b/src/test/ui/type-alias-impl-trait/issue-74761.rs @@ -1,5 +1,8 @@ #![feature(member_constraints)] -#![feature(type_alias_impl_trait)] +// revisions: min_tait full_tait +#![feature(min_type_alias_impl_trait)] +#![cfg_attr(full_tait, feature(type_alias_impl_trait))] +//[full_tait]~^ WARN incomplete pub trait A { type B; diff --git a/src/test/ui/type-alias-impl-trait/issue-74761.stderr b/src/test/ui/type-alias-impl-trait/issue-74761.stderr deleted file mode 100644 index 3f38fa4de0165..0000000000000 --- a/src/test/ui/type-alias-impl-trait/issue-74761.stderr +++ /dev/null @@ -1,15 +0,0 @@ -error[E0207]: the lifetime parameter `'a` is not constrained by the impl trait, self type, or predicates - --> $DIR/issue-74761.rs:8:6 - | -LL | impl<'a, 'b> A for () { - | ^^ unconstrained lifetime parameter - -error[E0207]: the lifetime parameter `'b` is not constrained by the impl trait, self type, or predicates - --> $DIR/issue-74761.rs:8:10 - | -LL | impl<'a, 'b> A for () { - | ^^ unconstrained lifetime parameter - -error: aborting due to 2 previous errors - -For more information about this error, try `rustc --explain E0207`. diff --git a/src/test/ui/type-alias-impl-trait/issue-76202-trait-impl-for-tait.full_tait.stderr b/src/test/ui/type-alias-impl-trait/issue-76202-trait-impl-for-tait.full_tait.stderr new file mode 100644 index 0000000000000..8fb4a3b662565 --- /dev/null +++ b/src/test/ui/type-alias-impl-trait/issue-76202-trait-impl-for-tait.full_tait.stderr @@ -0,0 +1,23 @@ +warning: the feature `type_alias_impl_trait` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/issue-76202-trait-impl-for-tait.rs:6:32 + | +LL | #![cfg_attr(full_tait, feature(type_alias_impl_trait))] + | ^^^^^^^^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #63063 <https://github.com/rust-lang/rust/issues/63063> for more information + +error: cannot implement trait on type alias impl trait + --> $DIR/issue-76202-trait-impl-for-tait.rs:19:1 + | +LL | impl Test for F { + | ^^^^^^^^^^^^^^^ + | +note: type alias impl trait defined here + --> $DIR/issue-76202-trait-impl-for-tait.rs:12:10 + | +LL | type F = impl Dummy; + | ^^^^^^^^^^ + +error: aborting due to previous error; 1 warning emitted + diff --git a/src/test/ui/type-alias-impl-trait/issue-76202-trait-impl-for-tait.min_tait.stderr b/src/test/ui/type-alias-impl-trait/issue-76202-trait-impl-for-tait.min_tait.stderr new file mode 100644 index 0000000000000..3754553772c25 --- /dev/null +++ b/src/test/ui/type-alias-impl-trait/issue-76202-trait-impl-for-tait.min_tait.stderr @@ -0,0 +1,14 @@ +error: cannot implement trait on type alias impl trait + --> $DIR/issue-76202-trait-impl-for-tait.rs:19:1 + | +LL | impl Test for F { + | ^^^^^^^^^^^^^^^ + | +note: type alias impl trait defined here + --> $DIR/issue-76202-trait-impl-for-tait.rs:12:10 + | +LL | type F = impl Dummy; + | ^^^^^^^^^^ + +error: aborting due to previous error + diff --git a/src/test/ui/type-alias-impl-trait/issue-76202-trait-impl-for-tait.rs b/src/test/ui/type-alias-impl-trait/issue-76202-trait-impl-for-tait.rs index 9ce19536e7949..276c770fe486f 100644 --- a/src/test/ui/type-alias-impl-trait/issue-76202-trait-impl-for-tait.rs +++ b/src/test/ui/type-alias-impl-trait/issue-76202-trait-impl-for-tait.rs @@ -1,7 +1,10 @@ // Regression test for issue #76202 // Tests that we don't ICE when we have a trait impl on a TAIT. -#![feature(type_alias_impl_trait)] +// revisions: min_tait full_tait +#![feature(min_type_alias_impl_trait)] +#![cfg_attr(full_tait, feature(type_alias_impl_trait))] +//[full_tait]~^ WARN incomplete trait Dummy {} impl Dummy for () {} diff --git a/src/test/ui/type-alias-impl-trait/issue-76202-trait-impl-for-tait.stderr b/src/test/ui/type-alias-impl-trait/issue-76202-trait-impl-for-tait.stderr deleted file mode 100644 index 8689ee53660f6..0000000000000 --- a/src/test/ui/type-alias-impl-trait/issue-76202-trait-impl-for-tait.stderr +++ /dev/null @@ -1,14 +0,0 @@ -error: cannot implement trait on type alias impl trait - --> $DIR/issue-76202-trait-impl-for-tait.rs:16:1 - | -LL | impl Test for F { - | ^^^^^^^^^^^^^^^ - | -note: type alias impl trait defined here - --> $DIR/issue-76202-trait-impl-for-tait.rs:9:10 - | -LL | type F = impl Dummy; - | ^^^^^^^^^^ - -error: aborting due to previous error - diff --git a/src/test/ui/type-alias-impl-trait/nested_type_alias_impl_trait.full_tait.stderr b/src/test/ui/type-alias-impl-trait/nested_type_alias_impl_trait.full_tait.stderr new file mode 100644 index 0000000000000..5e4307be17a04 --- /dev/null +++ b/src/test/ui/type-alias-impl-trait/nested_type_alias_impl_trait.full_tait.stderr @@ -0,0 +1,11 @@ +warning: the feature `type_alias_impl_trait` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/nested_type_alias_impl_trait.rs:3:32 + | +LL | #![cfg_attr(full_tait, feature(type_alias_impl_trait))] + | ^^^^^^^^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #63063 <https://github.com/rust-lang/rust/issues/63063> for more information + +warning: 1 warning emitted + diff --git a/src/test/ui/type-alias-impl-trait/nested_type_alias_impl_trait.rs b/src/test/ui/type-alias-impl-trait/nested_type_alias_impl_trait.rs index 82c9ecd2ac6bf..f03563dfed6d2 100644 --- a/src/test/ui/type-alias-impl-trait/nested_type_alias_impl_trait.rs +++ b/src/test/ui/type-alias-impl-trait/nested_type_alias_impl_trait.rs @@ -1,4 +1,7 @@ -#![feature(type_alias_impl_trait)] +// revisions: min_tait full_tait +#![feature(min_type_alias_impl_trait)] +#![cfg_attr(full_tait, feature(type_alias_impl_trait))] +//[full_tait]~^ WARN incomplete // build-pass (FIXME(62277): could be check-pass?) mod my_mod { use std::fmt::Debug; diff --git a/src/test/ui/type-alias-impl-trait/never_reveal_concrete_type.full_tait.stderr b/src/test/ui/type-alias-impl-trait/never_reveal_concrete_type.full_tait.stderr new file mode 100644 index 0000000000000..45c95469e95c2 --- /dev/null +++ b/src/test/ui/type-alias-impl-trait/never_reveal_concrete_type.full_tait.stderr @@ -0,0 +1,33 @@ +warning: the feature `type_alias_impl_trait` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/never_reveal_concrete_type.rs:3:32 + | +LL | #![cfg_attr(full_tait, feature(type_alias_impl_trait))] + | ^^^^^^^^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #63063 <https://github.com/rust-lang/rust/issues/63063> for more information + +error[E0308]: mismatched types + --> $DIR/never_reveal_concrete_type.rs:16:27 + | +LL | type NoReveal = impl std::fmt::Debug; + | -------------------- the found opaque type +... +LL | let _: &'static str = x; + | ------------ ^ expected `&str`, found opaque type + | | + | expected due to this + | + = note: expected reference `&'static str` + found opaque type `impl Debug` + +error[E0605]: non-primitive cast: `impl Debug` as `&'static str` + --> $DIR/never_reveal_concrete_type.rs:17:13 + | +LL | let _ = x as &'static str; + | ^^^^^^^^^^^^^^^^^ an `as` expression can only be used to convert between primitive types or to coerce to a specific trait object + +error: aborting due to 2 previous errors; 1 warning emitted + +Some errors have detailed explanations: E0308, E0605. +For more information about an error, try `rustc --explain E0308`. diff --git a/src/test/ui/type-alias-impl-trait/never_reveal_concrete_type.min_tait.stderr b/src/test/ui/type-alias-impl-trait/never_reveal_concrete_type.min_tait.stderr new file mode 100644 index 0000000000000..530f8876cf200 --- /dev/null +++ b/src/test/ui/type-alias-impl-trait/never_reveal_concrete_type.min_tait.stderr @@ -0,0 +1,24 @@ +error[E0308]: mismatched types + --> $DIR/never_reveal_concrete_type.rs:16:27 + | +LL | type NoReveal = impl std::fmt::Debug; + | -------------------- the found opaque type +... +LL | let _: &'static str = x; + | ------------ ^ expected `&str`, found opaque type + | | + | expected due to this + | + = note: expected reference `&'static str` + found opaque type `impl Debug` + +error[E0605]: non-primitive cast: `impl Debug` as `&'static str` + --> $DIR/never_reveal_concrete_type.rs:17:13 + | +LL | let _ = x as &'static str; + | ^^^^^^^^^^^^^^^^^ an `as` expression can only be used to convert between primitive types or to coerce to a specific trait object + +error: aborting due to 2 previous errors + +Some errors have detailed explanations: E0308, E0605. +For more information about an error, try `rustc --explain E0308`. diff --git a/src/test/ui/type-alias-impl-trait/never_reveal_concrete_type.rs b/src/test/ui/type-alias-impl-trait/never_reveal_concrete_type.rs index 8787c023eb0c7..c555b9cdded8a 100644 --- a/src/test/ui/type-alias-impl-trait/never_reveal_concrete_type.rs +++ b/src/test/ui/type-alias-impl-trait/never_reveal_concrete_type.rs @@ -1,4 +1,7 @@ -#![feature(type_alias_impl_trait)] +// revisions: min_tait full_tait +#![feature(min_type_alias_impl_trait)] +#![cfg_attr(full_tait, feature(type_alias_impl_trait))] +//[full_tait]~^ WARN incomplete fn main() {} diff --git a/src/test/ui/type-alias-impl-trait/never_reveal_concrete_type.stderr b/src/test/ui/type-alias-impl-trait/never_reveal_concrete_type.stderr deleted file mode 100644 index b438f84451649..0000000000000 --- a/src/test/ui/type-alias-impl-trait/never_reveal_concrete_type.stderr +++ /dev/null @@ -1,24 +0,0 @@ -error[E0308]: mismatched types - --> $DIR/never_reveal_concrete_type.rs:13:27 - | -LL | type NoReveal = impl std::fmt::Debug; - | -------------------- the found opaque type -... -LL | let _: &'static str = x; - | ------------ ^ expected `&str`, found opaque type - | | - | expected due to this - | - = note: expected reference `&'static str` - found opaque type `impl Debug` - -error[E0605]: non-primitive cast: `impl Debug` as `&'static str` - --> $DIR/never_reveal_concrete_type.rs:14:13 - | -LL | let _ = x as &'static str; - | ^^^^^^^^^^^^^^^^^ an `as` expression can only be used to convert between primitive types or to coerce to a specific trait object - -error: aborting due to 2 previous errors - -Some errors have detailed explanations: E0308, E0605. -For more information about an error, try `rustc --explain E0308`. diff --git a/src/test/ui/type-alias-impl-trait/no_inferrable_concrete_type.full_tait.stderr b/src/test/ui/type-alias-impl-trait/no_inferrable_concrete_type.full_tait.stderr new file mode 100644 index 0000000000000..40949c84d2395 --- /dev/null +++ b/src/test/ui/type-alias-impl-trait/no_inferrable_concrete_type.full_tait.stderr @@ -0,0 +1,25 @@ +warning: the feature `type_alias_impl_trait` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/no_inferrable_concrete_type.rs:6:32 + | +LL | #![cfg_attr(full_tait, feature(type_alias_impl_trait, impl_trait_in_bindings))] + | ^^^^^^^^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #63063 <https://github.com/rust-lang/rust/issues/63063> for more information + +warning: the feature `impl_trait_in_bindings` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/no_inferrable_concrete_type.rs:6:55 + | +LL | #![cfg_attr(full_tait, feature(type_alias_impl_trait, impl_trait_in_bindings))] + | ^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #63065 <https://github.com/rust-lang/rust/issues/63065> for more information + +error: could not find defining uses + --> $DIR/no_inferrable_concrete_type.rs:10:12 + | +LL | type Foo = impl Copy; + | ^^^^^^^^^ + +error: aborting due to previous error; 2 warnings emitted + diff --git a/src/test/ui/type-alias-impl-trait/no_inferrable_concrete_type.min_tait.stderr b/src/test/ui/type-alias-impl-trait/no_inferrable_concrete_type.min_tait.stderr new file mode 100644 index 0000000000000..d69e4cccdf0af --- /dev/null +++ b/src/test/ui/type-alias-impl-trait/no_inferrable_concrete_type.min_tait.stderr @@ -0,0 +1,18 @@ +error[E0658]: type alias impl trait is not permitted here + --> $DIR/no_inferrable_concrete_type.rs:16:12 + | +LL | let _: Foo = std::mem::transmute(0u8); + | ^^^ + | + = note: see issue #63065 <https://github.com/rust-lang/rust/issues/63065> for more information + = help: add `#![feature(impl_trait_in_bindings)]` to the crate attributes to enable + +error: could not find defining uses + --> $DIR/no_inferrable_concrete_type.rs:10:12 + | +LL | type Foo = impl Copy; + | ^^^^^^^^^ + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0658`. diff --git a/src/test/ui/type-alias-impl-trait/no_inferrable_concrete_type.rs b/src/test/ui/type-alias-impl-trait/no_inferrable_concrete_type.rs index c9ca504f78097..8ff588ef278ca 100644 --- a/src/test/ui/type-alias-impl-trait/no_inferrable_concrete_type.rs +++ b/src/test/ui/type-alias-impl-trait/no_inferrable_concrete_type.rs @@ -1,7 +1,11 @@ // Issue 52985: user code provides no use case that allows a type alias `impl Trait` // We now emit a 'could not find defining uses' error -#![feature(type_alias_impl_trait)] +// revisions: min_tait full_tait +#![feature(min_type_alias_impl_trait)] +#![cfg_attr(full_tait, feature(type_alias_impl_trait, impl_trait_in_bindings))] +//[full_tait]~^ WARN incomplete +//[full_tait]~| WARN incomplete type Foo = impl Copy; //~ could not find defining uses @@ -9,5 +13,5 @@ type Foo = impl Copy; //~ could not find defining uses fn bar(x: Foo) -> Foo { x } fn main() { - let _: Foo = std::mem::transmute(0u8); + let _: Foo = std::mem::transmute(0u8); //[min_tait]~ ERROR not permitted here } diff --git a/src/test/ui/type-alias-impl-trait/no_inferrable_concrete_type.stderr b/src/test/ui/type-alias-impl-trait/no_inferrable_concrete_type.stderr deleted file mode 100644 index 61025e846921e..0000000000000 --- a/src/test/ui/type-alias-impl-trait/no_inferrable_concrete_type.stderr +++ /dev/null @@ -1,8 +0,0 @@ -error: could not find defining uses - --> $DIR/no_inferrable_concrete_type.rs:6:12 - | -LL | type Foo = impl Copy; - | ^^^^^^^^^ - -error: aborting due to previous error - diff --git a/src/test/ui/type-alias-impl-trait/no_revealing_outside_defining_module.full_tait.stderr b/src/test/ui/type-alias-impl-trait/no_revealing_outside_defining_module.full_tait.stderr new file mode 100644 index 0000000000000..8699b21929ff2 --- /dev/null +++ b/src/test/ui/type-alias-impl-trait/no_revealing_outside_defining_module.full_tait.stderr @@ -0,0 +1,40 @@ +warning: the feature `type_alias_impl_trait` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/no_revealing_outside_defining_module.rs:3:32 + | +LL | #![cfg_attr(full_tait, feature(type_alias_impl_trait))] + | ^^^^^^^^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #63063 <https://github.com/rust-lang/rust/issues/63063> for more information + +error[E0308]: mismatched types + --> $DIR/no_revealing_outside_defining_module.rs:18:19 + | +LL | pub type Boo = impl ::std::fmt::Debug; + | ---------------------- the found opaque type +... +LL | let _: &str = bomp(); + | ---- ^^^^^^ expected `&str`, found opaque type + | | + | expected due to this + | + = note: expected reference `&str` + found opaque type `impl Debug` + +error[E0308]: mismatched types + --> $DIR/no_revealing_outside_defining_module.rs:22:5 + | +LL | pub type Boo = impl ::std::fmt::Debug; + | ---------------------- the expected opaque type +... +LL | fn bomp() -> boo::Boo { + | -------- expected `impl Debug` because of return type +LL | "" + | ^^ expected opaque type, found `&str` + | + = note: expected opaque type `impl Debug` + found reference `&'static str` + +error: aborting due to 2 previous errors; 1 warning emitted + +For more information about this error, try `rustc --explain E0308`. diff --git a/src/test/ui/type-alias-impl-trait/no_revealing_outside_defining_module.min_tait.stderr b/src/test/ui/type-alias-impl-trait/no_revealing_outside_defining_module.min_tait.stderr new file mode 100644 index 0000000000000..faaab4393521e --- /dev/null +++ b/src/test/ui/type-alias-impl-trait/no_revealing_outside_defining_module.min_tait.stderr @@ -0,0 +1,31 @@ +error[E0308]: mismatched types + --> $DIR/no_revealing_outside_defining_module.rs:18:19 + | +LL | pub type Boo = impl ::std::fmt::Debug; + | ---------------------- the found opaque type +... +LL | let _: &str = bomp(); + | ---- ^^^^^^ expected `&str`, found opaque type + | | + | expected due to this + | + = note: expected reference `&str` + found opaque type `impl Debug` + +error[E0308]: mismatched types + --> $DIR/no_revealing_outside_defining_module.rs:22:5 + | +LL | pub type Boo = impl ::std::fmt::Debug; + | ---------------------- the expected opaque type +... +LL | fn bomp() -> boo::Boo { + | -------- expected `impl Debug` because of return type +LL | "" + | ^^ expected opaque type, found `&str` + | + = note: expected opaque type `impl Debug` + found reference `&'static str` + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0308`. diff --git a/src/test/ui/type-alias-impl-trait/no_revealing_outside_defining_module.rs b/src/test/ui/type-alias-impl-trait/no_revealing_outside_defining_module.rs index 61153b1e17141..566c311b9bdf3 100644 --- a/src/test/ui/type-alias-impl-trait/no_revealing_outside_defining_module.rs +++ b/src/test/ui/type-alias-impl-trait/no_revealing_outside_defining_module.rs @@ -1,4 +1,7 @@ -#![feature(type_alias_impl_trait)] +// revisions: min_tait full_tait +#![feature(min_type_alias_impl_trait)] +#![cfg_attr(full_tait, feature(type_alias_impl_trait))] +//[full_tait]~^ WARN incomplete fn main() {} diff --git a/src/test/ui/type-alias-impl-trait/no_revealing_outside_defining_module.stderr b/src/test/ui/type-alias-impl-trait/no_revealing_outside_defining_module.stderr deleted file mode 100644 index 67752acb8c9af..0000000000000 --- a/src/test/ui/type-alias-impl-trait/no_revealing_outside_defining_module.stderr +++ /dev/null @@ -1,31 +0,0 @@ -error[E0308]: mismatched types - --> $DIR/no_revealing_outside_defining_module.rs:15:19 - | -LL | pub type Boo = impl ::std::fmt::Debug; - | ---------------------- the found opaque type -... -LL | let _: &str = bomp(); - | ---- ^^^^^^ expected `&str`, found opaque type - | | - | expected due to this - | - = note: expected reference `&str` - found opaque type `impl Debug` - -error[E0308]: mismatched types - --> $DIR/no_revealing_outside_defining_module.rs:19:5 - | -LL | pub type Boo = impl ::std::fmt::Debug; - | ---------------------- the expected opaque type -... -LL | fn bomp() -> boo::Boo { - | -------- expected `impl Debug` because of return type -LL | "" - | ^^ expected opaque type, found `&str` - | - = note: expected opaque type `impl Debug` - found reference `&'static str` - -error: aborting due to 2 previous errors - -For more information about this error, try `rustc --explain E0308`. diff --git a/src/test/ui/type-alias-impl-trait/not_a_defining_use.full_tait.stderr b/src/test/ui/type-alias-impl-trait/not_a_defining_use.full_tait.stderr new file mode 100644 index 0000000000000..76e3500e8a90e --- /dev/null +++ b/src/test/ui/type-alias-impl-trait/not_a_defining_use.full_tait.stderr @@ -0,0 +1,23 @@ +warning: the feature `type_alias_impl_trait` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/not_a_defining_use.rs:3:32 + | +LL | #![cfg_attr(full_tait, feature(type_alias_impl_trait))] + | ^^^^^^^^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #63063 <https://github.com/rust-lang/rust/issues/63063> for more information + +error: non-defining opaque type use in defining scope + --> $DIR/not_a_defining_use.rs:12:27 + | +LL | fn two<T: Debug>(t: T) -> Two<T, u32> { + | ^^^^^^^^^^^ + | +note: used non-generic type `u32` for generic parameter + --> $DIR/not_a_defining_use.rs:10:13 + | +LL | type Two<T, U> = impl Debug; + | ^ + +error: aborting due to previous error; 1 warning emitted + diff --git a/src/test/ui/type-alias-impl-trait/not_a_defining_use.min_tait.stderr b/src/test/ui/type-alias-impl-trait/not_a_defining_use.min_tait.stderr new file mode 100644 index 0000000000000..f5dbfecc742f5 --- /dev/null +++ b/src/test/ui/type-alias-impl-trait/not_a_defining_use.min_tait.stderr @@ -0,0 +1,14 @@ +error: non-defining opaque type use in defining scope + --> $DIR/not_a_defining_use.rs:12:27 + | +LL | fn two<T: Debug>(t: T) -> Two<T, u32> { + | ^^^^^^^^^^^ + | +note: used non-generic type `u32` for generic parameter + --> $DIR/not_a_defining_use.rs:10:13 + | +LL | type Two<T, U> = impl Debug; + | ^ + +error: aborting due to previous error + diff --git a/src/test/ui/type-alias-impl-trait/not_a_defining_use.rs b/src/test/ui/type-alias-impl-trait/not_a_defining_use.rs index f29b980dfd0e5..e58f33fcff48d 100644 --- a/src/test/ui/type-alias-impl-trait/not_a_defining_use.rs +++ b/src/test/ui/type-alias-impl-trait/not_a_defining_use.rs @@ -1,4 +1,7 @@ -#![feature(type_alias_impl_trait)] +// revisions: min_tait full_tait +#![feature(min_type_alias_impl_trait)] +#![cfg_attr(full_tait, feature(type_alias_impl_trait))] +//[full_tait]~^ WARN incomplete use std::fmt::Debug; diff --git a/src/test/ui/type-alias-impl-trait/not_a_defining_use.stderr b/src/test/ui/type-alias-impl-trait/not_a_defining_use.stderr deleted file mode 100644 index 2fa236b373a40..0000000000000 --- a/src/test/ui/type-alias-impl-trait/not_a_defining_use.stderr +++ /dev/null @@ -1,14 +0,0 @@ -error: non-defining opaque type use in defining scope - --> $DIR/not_a_defining_use.rs:9:27 - | -LL | fn two<T: Debug>(t: T) -> Two<T, u32> { - | ^^^^^^^^^^^ - | -note: used non-generic type `u32` for generic parameter - --> $DIR/not_a_defining_use.rs:7:13 - | -LL | type Two<T, U> = impl Debug; - | ^ - -error: aborting due to previous error - diff --git a/src/test/ui/type-alias-impl-trait/not_well_formed.full_tait.stderr b/src/test/ui/type-alias-impl-trait/not_well_formed.full_tait.stderr new file mode 100644 index 0000000000000..38f086ec45d68 --- /dev/null +++ b/src/test/ui/type-alias-impl-trait/not_well_formed.full_tait.stderr @@ -0,0 +1,18 @@ +warning: the feature `type_alias_impl_trait` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/not_well_formed.rs:3:32 + | +LL | #![cfg_attr(full_tait, feature(type_alias_impl_trait))] + | ^^^^^^^^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #63063 <https://github.com/rust-lang/rust/issues/63063> for more information + +error[E0220]: associated type `Assoc` not found for `V` + --> $DIR/not_well_formed.rs:13:29 + | +LL | type Foo<V> = impl Trait<V::Assoc>; + | ^^^^^ associated type `Assoc` not found + +error: aborting due to previous error; 1 warning emitted + +For more information about this error, try `rustc --explain E0220`. diff --git a/src/test/ui/type-alias-impl-trait/not_well_formed.min_tait.stderr b/src/test/ui/type-alias-impl-trait/not_well_formed.min_tait.stderr new file mode 100644 index 0000000000000..3bac38cef7da8 --- /dev/null +++ b/src/test/ui/type-alias-impl-trait/not_well_formed.min_tait.stderr @@ -0,0 +1,9 @@ +error[E0220]: associated type `Assoc` not found for `V` + --> $DIR/not_well_formed.rs:13:29 + | +LL | type Foo<V> = impl Trait<V::Assoc>; + | ^^^^^ associated type `Assoc` not found + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0220`. diff --git a/src/test/ui/type-alias-impl-trait/not_well_formed.rs b/src/test/ui/type-alias-impl-trait/not_well_formed.rs index 36ec9b34ebdb1..6d511c581888c 100644 --- a/src/test/ui/type-alias-impl-trait/not_well_formed.rs +++ b/src/test/ui/type-alias-impl-trait/not_well_formed.rs @@ -1,4 +1,7 @@ -#![feature(type_alias_impl_trait)] +// revisions: min_tait full_tait +#![feature(min_type_alias_impl_trait)] +#![cfg_attr(full_tait, feature(type_alias_impl_trait))] +//[full_tait]~^ WARN incomplete fn main() { } diff --git a/src/test/ui/type-alias-impl-trait/not_well_formed.stderr b/src/test/ui/type-alias-impl-trait/not_well_formed.stderr deleted file mode 100644 index c2b6f7eae9f3d..0000000000000 --- a/src/test/ui/type-alias-impl-trait/not_well_formed.stderr +++ /dev/null @@ -1,9 +0,0 @@ -error[E0220]: associated type `Assoc` not found for `V` - --> $DIR/not_well_formed.rs:10:29 - | -LL | type Foo<V> = impl Trait<V::Assoc>; - | ^^^^^ associated type `Assoc` not found - -error: aborting due to previous error - -For more information about this error, try `rustc --explain E0220`. diff --git a/src/test/ui/type-alias-impl-trait/structural-match-no-leak.full_tait.stderr b/src/test/ui/type-alias-impl-trait/structural-match-no-leak.full_tait.stderr new file mode 100644 index 0000000000000..b560c0c918a86 --- /dev/null +++ b/src/test/ui/type-alias-impl-trait/structural-match-no-leak.full_tait.stderr @@ -0,0 +1,25 @@ +warning: the feature `type_alias_impl_trait` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/structural-match-no-leak.rs:4:32 + | +LL | #![cfg_attr(full_tait, feature(type_alias_impl_trait, impl_trait_in_bindings))] + | ^^^^^^^^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #63063 <https://github.com/rust-lang/rust/issues/63063> for more information + +warning: the feature `impl_trait_in_bindings` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/structural-match-no-leak.rs:4:55 + | +LL | #![cfg_attr(full_tait, feature(type_alias_impl_trait, impl_trait_in_bindings))] + | ^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #63065 <https://github.com/rust-lang/rust/issues/63065> for more information + +error: `impl Send` cannot be used in patterns + --> $DIR/structural-match-no-leak.rs:19:9 + | +LL | LEAK_FREE => (), + | ^^^^^^^^^ + +error: aborting due to previous error; 2 warnings emitted + diff --git a/src/test/ui/type-alias-impl-trait/structural-match-no-leak.min_tait.stderr b/src/test/ui/type-alias-impl-trait/structural-match-no-leak.min_tait.stderr new file mode 100644 index 0000000000000..e962b23a587de --- /dev/null +++ b/src/test/ui/type-alias-impl-trait/structural-match-no-leak.min_tait.stderr @@ -0,0 +1,12 @@ +error[E0658]: type alias impl trait is not permitted here + --> $DIR/structural-match-no-leak.rs:15:24 + | +LL | const LEAK_FREE: Bar = leak_free(); + | ^^^^^^^^^^^ + | + = note: see issue #63065 <https://github.com/rust-lang/rust/issues/63065> for more information + = help: add `#![feature(impl_trait_in_bindings)]` to the crate attributes to enable + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0658`. diff --git a/src/test/ui/type-alias-impl-trait/structural-match-no-leak.rs b/src/test/ui/type-alias-impl-trait/structural-match-no-leak.rs index 58f0f5b2f65dc..d0bae30f9963f 100644 --- a/src/test/ui/type-alias-impl-trait/structural-match-no-leak.rs +++ b/src/test/ui/type-alias-impl-trait/structural-match-no-leak.rs @@ -1,4 +1,9 @@ -#![feature(const_impl_trait, type_alias_impl_trait)] +#![feature(const_impl_trait)] +// revisions: min_tait full_tait +#![feature(min_type_alias_impl_trait)] +#![cfg_attr(full_tait, feature(type_alias_impl_trait, impl_trait_in_bindings))] +//[full_tait]~^ WARN incomplete +//[full_tait]~| WARN incomplete type Bar = impl Send; @@ -7,12 +12,12 @@ type Bar = impl Send; const fn leak_free() -> Bar { 7i32 } -const LEAK_FREE: Bar = leak_free(); +const LEAK_FREE: Bar = leak_free(); //[min_tait]~ ERROR not permitted here fn leak_free_test() { match todo!() { LEAK_FREE => (), - //~^ `impl Send` cannot be used in patterns + //[full_tait]~^ `impl Send` cannot be used in patterns _ => (), } } diff --git a/src/test/ui/type-alias-impl-trait/structural-match-no-leak.stderr b/src/test/ui/type-alias-impl-trait/structural-match-no-leak.stderr deleted file mode 100644 index 889c4fd4b0405..0000000000000 --- a/src/test/ui/type-alias-impl-trait/structural-match-no-leak.stderr +++ /dev/null @@ -1,8 +0,0 @@ -error: `impl Send` cannot be used in patterns - --> $DIR/structural-match-no-leak.rs:14:9 - | -LL | LEAK_FREE => (), - | ^^^^^^^^^ - -error: aborting due to previous error - diff --git a/src/test/ui/type-alias-impl-trait/structural-match.full_tait.stderr b/src/test/ui/type-alias-impl-trait/structural-match.full_tait.stderr new file mode 100644 index 0000000000000..b94e06e6d0ef4 --- /dev/null +++ b/src/test/ui/type-alias-impl-trait/structural-match.full_tait.stderr @@ -0,0 +1,25 @@ +warning: the feature `type_alias_impl_trait` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/structural-match.rs:4:32 + | +LL | #![cfg_attr(full_tait, feature(type_alias_impl_trait, impl_trait_in_bindings))] + | ^^^^^^^^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #63063 <https://github.com/rust-lang/rust/issues/63063> for more information + +warning: the feature `impl_trait_in_bindings` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/structural-match.rs:4:55 + | +LL | #![cfg_attr(full_tait, feature(type_alias_impl_trait, impl_trait_in_bindings))] + | ^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #63065 <https://github.com/rust-lang/rust/issues/63065> for more information + +error: `impl Send` cannot be used in patterns + --> $DIR/structural-match.rs:20:9 + | +LL | VALUE => (), + | ^^^^^ + +error: aborting due to previous error; 2 warnings emitted + diff --git a/src/test/ui/type-alias-impl-trait/structural-match.min_tait.stderr b/src/test/ui/type-alias-impl-trait/structural-match.min_tait.stderr new file mode 100644 index 0000000000000..36c49a954bda8 --- /dev/null +++ b/src/test/ui/type-alias-impl-trait/structural-match.min_tait.stderr @@ -0,0 +1,12 @@ +error[E0658]: type alias impl trait is not permitted here + --> $DIR/structural-match.rs:16:20 + | +LL | const VALUE: Foo = value(); + | ^^^^^^^ + | + = note: see issue #63065 <https://github.com/rust-lang/rust/issues/63065> for more information + = help: add `#![feature(impl_trait_in_bindings)]` to the crate attributes to enable + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0658`. diff --git a/src/test/ui/type-alias-impl-trait/structural-match.rs b/src/test/ui/type-alias-impl-trait/structural-match.rs index 74ffa6084268e..daf914cc494ac 100644 --- a/src/test/ui/type-alias-impl-trait/structural-match.rs +++ b/src/test/ui/type-alias-impl-trait/structural-match.rs @@ -1,4 +1,9 @@ -#![feature(const_impl_trait, type_alias_impl_trait)] +#![feature(const_impl_trait)] +// revisions: min_tait full_tait +#![feature(min_type_alias_impl_trait)] +#![cfg_attr(full_tait, feature(type_alias_impl_trait, impl_trait_in_bindings))] +//[full_tait]~^ WARN incomplete +//[full_tait]~| WARN incomplete type Foo = impl Send; @@ -8,12 +13,12 @@ struct A; const fn value() -> Foo { A } -const VALUE: Foo = value(); +const VALUE: Foo = value(); //[min_tait]~ ERROR not permitted here fn test() { match todo!() { VALUE => (), - //~^ `impl Send` cannot be used in patterns + //[full_tait]~^ `impl Send` cannot be used in patterns _ => (), } } diff --git a/src/test/ui/type-alias-impl-trait/structural-match.stderr b/src/test/ui/type-alias-impl-trait/structural-match.stderr deleted file mode 100644 index 262fd0726137e..0000000000000 --- a/src/test/ui/type-alias-impl-trait/structural-match.stderr +++ /dev/null @@ -1,8 +0,0 @@ -error: `impl Send` cannot be used in patterns - --> $DIR/structural-match.rs:15:9 - | -LL | VALUE => (), - | ^^^^^ - -error: aborting due to previous error - diff --git a/src/test/ui/type-alias-impl-trait/type-alias-impl-trait-const.full_tait.stderr b/src/test/ui/type-alias-impl-trait/type-alias-impl-trait-const.full_tait.stderr new file mode 100644 index 0000000000000..894d61502a721 --- /dev/null +++ b/src/test/ui/type-alias-impl-trait/type-alias-impl-trait-const.full_tait.stderr @@ -0,0 +1,19 @@ +warning: the feature `type_alias_impl_trait` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/type-alias-impl-trait-const.rs:5:32 + | +LL | #![cfg_attr(full_tait, feature(type_alias_impl_trait))] + | ^^^^^^^^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #63063 <https://github.com/rust-lang/rust/issues/63063> for more information + +warning: the feature `impl_trait_in_bindings` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/type-alias-impl-trait-const.rs:11:12 + | +LL | #![feature(impl_trait_in_bindings)] + | ^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #63065 <https://github.com/rust-lang/rust/issues/63065> for more information + +warning: 2 warnings emitted + diff --git a/src/test/ui/type-alias-impl-trait/type-alias-impl-trait-const.min_tait.stderr b/src/test/ui/type-alias-impl-trait/type-alias-impl-trait-const.min_tait.stderr new file mode 100644 index 0000000000000..66e4c242168d1 --- /dev/null +++ b/src/test/ui/type-alias-impl-trait/type-alias-impl-trait-const.min_tait.stderr @@ -0,0 +1,11 @@ +warning: the feature `impl_trait_in_bindings` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/type-alias-impl-trait-const.rs:11:12 + | +LL | #![feature(impl_trait_in_bindings)] + | ^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #63065 <https://github.com/rust-lang/rust/issues/63065> for more information + +warning: 1 warning emitted + diff --git a/src/test/ui/type-alias-impl-trait/type-alias-impl-trait-const.rs b/src/test/ui/type-alias-impl-trait/type-alias-impl-trait-const.rs index 01769f711536c..0b0551cd96d3b 100644 --- a/src/test/ui/type-alias-impl-trait/type-alias-impl-trait-const.rs +++ b/src/test/ui/type-alias-impl-trait/type-alias-impl-trait-const.rs @@ -1,6 +1,9 @@ // check-pass -#![feature(type_alias_impl_trait)] +// revisions: min_tait full_tait +#![feature(min_type_alias_impl_trait)] +#![cfg_attr(full_tait, feature(type_alias_impl_trait))] +//[full_tait]~^ WARN incomplete // Currently, the `type_alias_impl_trait` feature implicitly // depends on `impl_trait_in_bindings` in order to work properly. // Specifically, this line requires `impl_trait_in_bindings` to be enabled: diff --git a/src/test/ui/type-alias-impl-trait/type-alias-impl-trait-const.stderr b/src/test/ui/type-alias-impl-trait/type-alias-impl-trait-const.stderr deleted file mode 100644 index b0593d51a250c..0000000000000 --- a/src/test/ui/type-alias-impl-trait/type-alias-impl-trait-const.stderr +++ /dev/null @@ -1,11 +0,0 @@ -warning: the feature `impl_trait_in_bindings` is incomplete and may not be safe to use and/or cause compiler crashes - --> $DIR/type-alias-impl-trait-const.rs:8:12 - | -LL | #![feature(impl_trait_in_bindings)] - | ^^^^^^^^^^^^^^^^^^^^^^ - | - = note: `#[warn(incomplete_features)]` on by default - = note: see issue #63065 <https://github.com/rust-lang/rust/issues/63065> for more information - -warning: 1 warning emitted - diff --git a/src/test/ui/type-alias-impl-trait/type-alias-impl-trait-fns.full_tait.stderr b/src/test/ui/type-alias-impl-trait/type-alias-impl-trait-fns.full_tait.stderr new file mode 100644 index 0000000000000..fb88c2043bd3f --- /dev/null +++ b/src/test/ui/type-alias-impl-trait/type-alias-impl-trait-fns.full_tait.stderr @@ -0,0 +1,11 @@ +warning: the feature `type_alias_impl_trait` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/type-alias-impl-trait-fns.rs:5:32 + | +LL | #![cfg_attr(full_tait, feature(type_alias_impl_trait))] + | ^^^^^^^^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #63063 <https://github.com/rust-lang/rust/issues/63063> for more information + +warning: 1 warning emitted + diff --git a/src/test/ui/type-alias-impl-trait/type-alias-impl-trait-fns.rs b/src/test/ui/type-alias-impl-trait/type-alias-impl-trait-fns.rs index a22b12cae6bcb..a6b00220084ad 100644 --- a/src/test/ui/type-alias-impl-trait/type-alias-impl-trait-fns.rs +++ b/src/test/ui/type-alias-impl-trait/type-alias-impl-trait-fns.rs @@ -1,6 +1,9 @@ // check-pass -#![feature(type_alias_impl_trait)] +// revisions: min_tait full_tait +#![feature(min_type_alias_impl_trait)] +#![cfg_attr(full_tait, feature(type_alias_impl_trait))] +//[full_tait]~^ WARN incomplete // Regression test for issue #61863 diff --git a/src/test/ui/type-alias-impl-trait/type-alias-impl-trait-sized.full_tait.stderr b/src/test/ui/type-alias-impl-trait/type-alias-impl-trait-sized.full_tait.stderr new file mode 100644 index 0000000000000..cbecd0bb3d486 --- /dev/null +++ b/src/test/ui/type-alias-impl-trait/type-alias-impl-trait-sized.full_tait.stderr @@ -0,0 +1,11 @@ +warning: the feature `type_alias_impl_trait` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/type-alias-impl-trait-sized.rs:5:32 + | +LL | #![cfg_attr(full_tait, feature(type_alias_impl_trait))] + | ^^^^^^^^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #63063 <https://github.com/rust-lang/rust/issues/63063> for more information + +warning: 1 warning emitted + diff --git a/src/test/ui/type-alias-impl-trait/type-alias-impl-trait-sized.rs b/src/test/ui/type-alias-impl-trait/type-alias-impl-trait-sized.rs index c54df664243e7..b062739921fa6 100644 --- a/src/test/ui/type-alias-impl-trait/type-alias-impl-trait-sized.rs +++ b/src/test/ui/type-alias-impl-trait/type-alias-impl-trait-sized.rs @@ -1,6 +1,9 @@ // check-pass -#![feature(type_alias_impl_trait)] +// revisions: min_tait full_tait +#![feature(min_type_alias_impl_trait)] +#![cfg_attr(full_tait, feature(type_alias_impl_trait))] +//[full_tait]~^ WARN incomplete type A = impl Sized; fn f1() -> A { 0 } diff --git a/src/test/ui/type-alias-impl-trait/type-alias-impl-trait-tuple.full_tait.stderr b/src/test/ui/type-alias-impl-trait/type-alias-impl-trait-tuple.full_tait.stderr new file mode 100644 index 0000000000000..1a351867bff2d --- /dev/null +++ b/src/test/ui/type-alias-impl-trait/type-alias-impl-trait-tuple.full_tait.stderr @@ -0,0 +1,11 @@ +warning: the feature `type_alias_impl_trait` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/type-alias-impl-trait-tuple.rs:5:32 + | +LL | #![cfg_attr(full_tait, feature(type_alias_impl_trait))] + | ^^^^^^^^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #63063 <https://github.com/rust-lang/rust/issues/63063> for more information + +warning: 1 warning emitted + diff --git a/src/test/ui/type-alias-impl-trait/type-alias-impl-trait-tuple.rs b/src/test/ui/type-alias-impl-trait/type-alias-impl-trait-tuple.rs index 86c9d48214383..ad2c11d4f994e 100644 --- a/src/test/ui/type-alias-impl-trait/type-alias-impl-trait-tuple.rs +++ b/src/test/ui/type-alias-impl-trait/type-alias-impl-trait-tuple.rs @@ -1,6 +1,9 @@ // check-pass -#![feature(type_alias_impl_trait)] +// revisions: min_tait full_tait +#![feature(min_type_alias_impl_trait)] +#![cfg_attr(full_tait, feature(type_alias_impl_trait))] +//[full_tait]~^ WARN incomplete #![allow(dead_code)] pub trait MyTrait {} diff --git a/src/test/ui/type-alias-impl-trait/type-alias-impl-trait-unconstrained-lifetime.full_tait.stderr b/src/test/ui/type-alias-impl-trait/type-alias-impl-trait-unconstrained-lifetime.full_tait.stderr new file mode 100644 index 0000000000000..b16d9c2c143bd --- /dev/null +++ b/src/test/ui/type-alias-impl-trait/type-alias-impl-trait-unconstrained-lifetime.full_tait.stderr @@ -0,0 +1,18 @@ +warning: the feature `type_alias_impl_trait` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/type-alias-impl-trait-unconstrained-lifetime.rs:5:32 + | +LL | #![cfg_attr(full_tait, feature(type_alias_impl_trait))] + | ^^^^^^^^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #63063 <https://github.com/rust-lang/rust/issues/63063> for more information + +error[E0207]: the lifetime parameter `'a` is not constrained by the impl trait, self type, or predicates + --> $DIR/type-alias-impl-trait-unconstrained-lifetime.rs:13:6 + | +LL | impl<'a, I: Iterator<Item = i32>> Trait for (i32, I) { + | ^^ unconstrained lifetime parameter + +error: aborting due to previous error; 1 warning emitted + +For more information about this error, try `rustc --explain E0207`. diff --git a/src/test/ui/type-alias-impl-trait/type-alias-impl-trait-unconstrained-lifetime.min_tait.stderr b/src/test/ui/type-alias-impl-trait/type-alias-impl-trait-unconstrained-lifetime.min_tait.stderr new file mode 100644 index 0000000000000..7d4abb0fafbbc --- /dev/null +++ b/src/test/ui/type-alias-impl-trait/type-alias-impl-trait-unconstrained-lifetime.min_tait.stderr @@ -0,0 +1,9 @@ +error[E0207]: the lifetime parameter `'a` is not constrained by the impl trait, self type, or predicates + --> $DIR/type-alias-impl-trait-unconstrained-lifetime.rs:13:6 + | +LL | impl<'a, I: Iterator<Item = i32>> Trait for (i32, I) { + | ^^ unconstrained lifetime parameter + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0207`. diff --git a/src/test/ui/type-alias-impl-trait/type-alias-impl-trait-unconstrained-lifetime.rs b/src/test/ui/type-alias-impl-trait/type-alias-impl-trait-unconstrained-lifetime.rs index efbf4f1e351f7..97294e81dca66 100644 --- a/src/test/ui/type-alias-impl-trait/type-alias-impl-trait-unconstrained-lifetime.rs +++ b/src/test/ui/type-alias-impl-trait/type-alias-impl-trait-unconstrained-lifetime.rs @@ -1,6 +1,9 @@ // regression test for #74018 -#![feature(type_alias_impl_trait)] +// revisions: min_tait full_tait +#![feature(min_type_alias_impl_trait)] +#![cfg_attr(full_tait, feature(type_alias_impl_trait))] +//[full_tait]~^ WARN incomplete trait Trait { type Associated; diff --git a/src/test/ui/type-alias-impl-trait/type-alias-impl-trait-unconstrained-lifetime.stderr b/src/test/ui/type-alias-impl-trait/type-alias-impl-trait-unconstrained-lifetime.stderr deleted file mode 100644 index 8cdce2f8e81ca..0000000000000 --- a/src/test/ui/type-alias-impl-trait/type-alias-impl-trait-unconstrained-lifetime.stderr +++ /dev/null @@ -1,9 +0,0 @@ -error[E0207]: the lifetime parameter `'a` is not constrained by the impl trait, self type, or predicates - --> $DIR/type-alias-impl-trait-unconstrained-lifetime.rs:10:6 - | -LL | impl<'a, I: Iterator<Item = i32>> Trait for (i32, I) { - | ^^ unconstrained lifetime parameter - -error: aborting due to previous error - -For more information about this error, try `rustc --explain E0207`. diff --git a/src/test/ui/type-alias-impl-trait/type-alias-impl-trait-with-cycle-error.full_tait.stderr b/src/test/ui/type-alias-impl-trait/type-alias-impl-trait-with-cycle-error.full_tait.stderr new file mode 100644 index 0000000000000..e3fd076fc492b --- /dev/null +++ b/src/test/ui/type-alias-impl-trait/type-alias-impl-trait-with-cycle-error.full_tait.stderr @@ -0,0 +1,17 @@ +warning: the feature `type_alias_impl_trait` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/type-alias-impl-trait-with-cycle-error.rs:3:32 + | +LL | #![cfg_attr(full_tait, feature(type_alias_impl_trait))] + | ^^^^^^^^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #63063 <https://github.com/rust-lang/rust/issues/63063> for more information + +error: could not find defining uses + --> $DIR/type-alias-impl-trait-with-cycle-error.rs:6:12 + | +LL | type Foo = impl Fn() -> Foo; + | ^^^^^^^^^^^^^^^^ + +error: aborting due to previous error; 1 warning emitted + diff --git a/src/test/ui/type-alias-impl-trait/type-alias-impl-trait-with-cycle-error.min_tait.stderr b/src/test/ui/type-alias-impl-trait/type-alias-impl-trait-with-cycle-error.min_tait.stderr new file mode 100644 index 0000000000000..556779574e43c --- /dev/null +++ b/src/test/ui/type-alias-impl-trait/type-alias-impl-trait-with-cycle-error.min_tait.stderr @@ -0,0 +1,8 @@ +error: could not find defining uses + --> $DIR/type-alias-impl-trait-with-cycle-error.rs:6:12 + | +LL | type Foo = impl Fn() -> Foo; + | ^^^^^^^^^^^^^^^^ + +error: aborting due to previous error + diff --git a/src/test/ui/type-alias-impl-trait/type-alias-impl-trait-with-cycle-error.rs b/src/test/ui/type-alias-impl-trait/type-alias-impl-trait-with-cycle-error.rs index c009952eab750..dba0d2c2cdf46 100644 --- a/src/test/ui/type-alias-impl-trait/type-alias-impl-trait-with-cycle-error.rs +++ b/src/test/ui/type-alias-impl-trait/type-alias-impl-trait-with-cycle-error.rs @@ -1,4 +1,7 @@ -#![feature(type_alias_impl_trait)] +// revisions: min_tait full_tait +#![feature(min_type_alias_impl_trait)] +#![cfg_attr(full_tait, feature(type_alias_impl_trait))] +//[full_tait]~^ WARN incomplete type Foo = impl Fn() -> Foo; //~^ ERROR: could not find defining uses diff --git a/src/test/ui/type-alias-impl-trait/type-alias-impl-trait-with-cycle-error.stderr b/src/test/ui/type-alias-impl-trait/type-alias-impl-trait-with-cycle-error.stderr deleted file mode 100644 index 726f4ea6e00f7..0000000000000 --- a/src/test/ui/type-alias-impl-trait/type-alias-impl-trait-with-cycle-error.stderr +++ /dev/null @@ -1,8 +0,0 @@ -error: could not find defining uses - --> $DIR/type-alias-impl-trait-with-cycle-error.rs:3:12 - | -LL | type Foo = impl Fn() -> Foo; - | ^^^^^^^^^^^^^^^^ - -error: aborting due to previous error - diff --git a/src/test/ui/type-alias-impl-trait/type-alias-impl-trait-with-cycle-error2.full_tait.stderr b/src/test/ui/type-alias-impl-trait/type-alias-impl-trait-with-cycle-error2.full_tait.stderr new file mode 100644 index 0000000000000..eccf08b4a1247 --- /dev/null +++ b/src/test/ui/type-alias-impl-trait/type-alias-impl-trait-with-cycle-error2.full_tait.stderr @@ -0,0 +1,17 @@ +warning: the feature `type_alias_impl_trait` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/type-alias-impl-trait-with-cycle-error2.rs:3:32 + | +LL | #![cfg_attr(full_tait, feature(type_alias_impl_trait))] + | ^^^^^^^^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #63063 <https://github.com/rust-lang/rust/issues/63063> for more information + +error: could not find defining uses + --> $DIR/type-alias-impl-trait-with-cycle-error2.rs:10:12 + | +LL | type Foo = impl Bar<Foo, Item = Foo>; + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to previous error; 1 warning emitted + diff --git a/src/test/ui/type-alias-impl-trait/type-alias-impl-trait-with-cycle-error2.min_tait.stderr b/src/test/ui/type-alias-impl-trait/type-alias-impl-trait-with-cycle-error2.min_tait.stderr new file mode 100644 index 0000000000000..44d279cad171f --- /dev/null +++ b/src/test/ui/type-alias-impl-trait/type-alias-impl-trait-with-cycle-error2.min_tait.stderr @@ -0,0 +1,8 @@ +error: could not find defining uses + --> $DIR/type-alias-impl-trait-with-cycle-error2.rs:10:12 + | +LL | type Foo = impl Bar<Foo, Item = Foo>; + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to previous error + diff --git a/src/test/ui/type-alias-impl-trait/type-alias-impl-trait-with-cycle-error2.rs b/src/test/ui/type-alias-impl-trait/type-alias-impl-trait-with-cycle-error2.rs index 32ecc36661898..f204403712588 100644 --- a/src/test/ui/type-alias-impl-trait/type-alias-impl-trait-with-cycle-error2.rs +++ b/src/test/ui/type-alias-impl-trait/type-alias-impl-trait-with-cycle-error2.rs @@ -1,4 +1,7 @@ -#![feature(type_alias_impl_trait)] +// revisions: min_tait full_tait +#![feature(min_type_alias_impl_trait)] +#![cfg_attr(full_tait, feature(type_alias_impl_trait))] +//[full_tait]~^ WARN incomplete pub trait Bar<T> { type Item; diff --git a/src/test/ui/type-alias-impl-trait/type-alias-impl-trait-with-cycle-error2.stderr b/src/test/ui/type-alias-impl-trait/type-alias-impl-trait-with-cycle-error2.stderr deleted file mode 100644 index 3947cc4d27055..0000000000000 --- a/src/test/ui/type-alias-impl-trait/type-alias-impl-trait-with-cycle-error2.stderr +++ /dev/null @@ -1,8 +0,0 @@ -error: could not find defining uses - --> $DIR/type-alias-impl-trait-with-cycle-error2.rs:7:12 - | -LL | type Foo = impl Bar<Foo, Item = Foo>; - | ^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: aborting due to previous error - diff --git a/src/test/ui/type-alias-impl-trait/type-alias-impl-trait-with-no-traits.full_tait.stderr b/src/test/ui/type-alias-impl-trait/type-alias-impl-trait-with-no-traits.full_tait.stderr new file mode 100644 index 0000000000000..6ada4b2fdffe0 --- /dev/null +++ b/src/test/ui/type-alias-impl-trait/type-alias-impl-trait-with-no-traits.full_tait.stderr @@ -0,0 +1,23 @@ +error: at least one trait must be specified + --> $DIR/type-alias-impl-trait-with-no-traits.rs:6:12 + | +LL | type Foo = impl 'static; + | ^^^^^^^^^^^^ + +error: at least one trait must be specified + --> $DIR/type-alias-impl-trait-with-no-traits.rs:13:13 + | +LL | fn bar() -> impl 'static { + | ^^^^^^^^^^^^ + +warning: the feature `type_alias_impl_trait` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/type-alias-impl-trait-with-no-traits.rs:3:32 + | +LL | #![cfg_attr(full_tait, feature(type_alias_impl_trait))] + | ^^^^^^^^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #63063 <https://github.com/rust-lang/rust/issues/63063> for more information + +error: aborting due to 2 previous errors; 1 warning emitted + diff --git a/src/test/ui/type-alias-impl-trait/type-alias-impl-trait-with-no-traits.min_tait.stderr b/src/test/ui/type-alias-impl-trait/type-alias-impl-trait-with-no-traits.min_tait.stderr new file mode 100644 index 0000000000000..5fd8aac550751 --- /dev/null +++ b/src/test/ui/type-alias-impl-trait/type-alias-impl-trait-with-no-traits.min_tait.stderr @@ -0,0 +1,14 @@ +error: at least one trait must be specified + --> $DIR/type-alias-impl-trait-with-no-traits.rs:6:12 + | +LL | type Foo = impl 'static; + | ^^^^^^^^^^^^ + +error: at least one trait must be specified + --> $DIR/type-alias-impl-trait-with-no-traits.rs:13:13 + | +LL | fn bar() -> impl 'static { + | ^^^^^^^^^^^^ + +error: aborting due to 2 previous errors + diff --git a/src/test/ui/type-alias-impl-trait/type-alias-impl-trait-with-no-traits.rs b/src/test/ui/type-alias-impl-trait/type-alias-impl-trait-with-no-traits.rs index 8ca279eec921b..7efc790058864 100644 --- a/src/test/ui/type-alias-impl-trait/type-alias-impl-trait-with-no-traits.rs +++ b/src/test/ui/type-alias-impl-trait/type-alias-impl-trait-with-no-traits.rs @@ -1,4 +1,7 @@ -#![feature(type_alias_impl_trait)] +// revisions: min_tait full_tait +#![feature(min_type_alias_impl_trait)] +#![cfg_attr(full_tait, feature(type_alias_impl_trait))] +//[full_tait]~^ WARN incomplete type Foo = impl 'static; //~^ ERROR: at least one trait must be specified diff --git a/src/test/ui/type-alias-impl-trait/type-alias-impl-trait-with-no-traits.stderr b/src/test/ui/type-alias-impl-trait/type-alias-impl-trait-with-no-traits.stderr deleted file mode 100644 index 3f7acd3383010..0000000000000 --- a/src/test/ui/type-alias-impl-trait/type-alias-impl-trait-with-no-traits.stderr +++ /dev/null @@ -1,14 +0,0 @@ -error: at least one trait must be specified - --> $DIR/type-alias-impl-trait-with-no-traits.rs:3:12 - | -LL | type Foo = impl 'static; - | ^^^^^^^^^^^^ - -error: at least one trait must be specified - --> $DIR/type-alias-impl-trait-with-no-traits.rs:10:13 - | -LL | fn bar() -> impl 'static { - | ^^^^^^^^^^^^ - -error: aborting due to 2 previous errors - diff --git a/src/test/ui/type-alias-impl-trait/type-alias-impl-trait.full_tait.stderr b/src/test/ui/type-alias-impl-trait/type-alias-impl-trait.full_tait.stderr new file mode 100644 index 0000000000000..8c0f7758dae60 --- /dev/null +++ b/src/test/ui/type-alias-impl-trait/type-alias-impl-trait.full_tait.stderr @@ -0,0 +1,11 @@ +warning: the feature `type_alias_impl_trait` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/type-alias-impl-trait.rs:8:32 + | +LL | #![cfg_attr(full_tait, feature(type_alias_impl_trait))] + | ^^^^^^^^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #63063 <https://github.com/rust-lang/rust/issues/63063> for more information + +warning: 1 warning emitted + diff --git a/src/test/ui/type-alias-impl-trait/type-alias-impl-trait.rs b/src/test/ui/type-alias-impl-trait/type-alias-impl-trait.rs index 80192d19af98b..6176595534a35 100644 --- a/src/test/ui/type-alias-impl-trait/type-alias-impl-trait.rs +++ b/src/test/ui/type-alias-impl-trait/type-alias-impl-trait.rs @@ -3,7 +3,10 @@ #![allow(dead_code)] #![allow(unused_assignments)] #![allow(unused_variables)] -#![feature(type_alias_impl_trait)] +// revisions: min_tait full_tait +#![feature(min_type_alias_impl_trait)] +#![cfg_attr(full_tait, feature(type_alias_impl_trait))] +//[full_tait]~^ WARN incomplete fn main() { assert_eq!(foo().to_string(), "foo"); diff --git a/src/test/ui/type-alias-impl-trait/type-alias-nested-impl-trait.full_tait.stderr b/src/test/ui/type-alias-impl-trait/type-alias-nested-impl-trait.full_tait.stderr new file mode 100644 index 0000000000000..ed4fe41da3ed3 --- /dev/null +++ b/src/test/ui/type-alias-impl-trait/type-alias-nested-impl-trait.full_tait.stderr @@ -0,0 +1,11 @@ +warning: the feature `type_alias_impl_trait` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/type-alias-nested-impl-trait.rs:5:32 + | +LL | #![cfg_attr(full_tait, feature(type_alias_impl_trait))] + | ^^^^^^^^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #63063 <https://github.com/rust-lang/rust/issues/63063> for more information + +warning: 1 warning emitted + diff --git a/src/test/ui/type-alias-impl-trait/type-alias-nested-impl-trait.rs b/src/test/ui/type-alias-impl-trait/type-alias-nested-impl-trait.rs index fd954801dc047..3023bf06562fb 100644 --- a/src/test/ui/type-alias-impl-trait/type-alias-nested-impl-trait.rs +++ b/src/test/ui/type-alias-impl-trait/type-alias-nested-impl-trait.rs @@ -1,6 +1,9 @@ // run-pass -#![feature(type_alias_impl_trait)] +// revisions: min_tait full_tait +#![feature(min_type_alias_impl_trait)] +#![cfg_attr(full_tait, feature(type_alias_impl_trait))] +//[full_tait]~^ WARN incomplete use std::iter::{once, Chain}; diff --git a/src/test/ui/type-alias-impl-trait/unused_generic_param.full_tait.stderr b/src/test/ui/type-alias-impl-trait/unused_generic_param.full_tait.stderr new file mode 100644 index 0000000000000..b70f36b67ab3d --- /dev/null +++ b/src/test/ui/type-alias-impl-trait/unused_generic_param.full_tait.stderr @@ -0,0 +1,23 @@ +error: at least one trait must be specified + --> $DIR/unused_generic_param.rs:9:28 + | +LL | type PartiallyDefined<T> = impl 'static; + | ^^^^^^^^^^^^ + +error: at least one trait must be specified + --> $DIR/unused_generic_param.rs:16:29 + | +LL | type PartiallyDefined2<T> = impl 'static; + | ^^^^^^^^^^^^ + +warning: the feature `type_alias_impl_trait` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/unused_generic_param.rs:3:32 + | +LL | #![cfg_attr(full_tait, feature(type_alias_impl_trait))] + | ^^^^^^^^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #63063 <https://github.com/rust-lang/rust/issues/63063> for more information + +error: aborting due to 2 previous errors; 1 warning emitted + diff --git a/src/test/ui/type-alias-impl-trait/unused_generic_param.min_tait.stderr b/src/test/ui/type-alias-impl-trait/unused_generic_param.min_tait.stderr new file mode 100644 index 0000000000000..561025c88f8f6 --- /dev/null +++ b/src/test/ui/type-alias-impl-trait/unused_generic_param.min_tait.stderr @@ -0,0 +1,14 @@ +error: at least one trait must be specified + --> $DIR/unused_generic_param.rs:9:28 + | +LL | type PartiallyDefined<T> = impl 'static; + | ^^^^^^^^^^^^ + +error: at least one trait must be specified + --> $DIR/unused_generic_param.rs:16:29 + | +LL | type PartiallyDefined2<T> = impl 'static; + | ^^^^^^^^^^^^ + +error: aborting due to 2 previous errors + diff --git a/src/test/ui/type-alias-impl-trait/unused_generic_param.rs b/src/test/ui/type-alias-impl-trait/unused_generic_param.rs index a9ab727b193e4..59c7646ade263 100644 --- a/src/test/ui/type-alias-impl-trait/unused_generic_param.rs +++ b/src/test/ui/type-alias-impl-trait/unused_generic_param.rs @@ -1,4 +1,7 @@ -#![feature(type_alias_impl_trait)] +// revisions: min_tait full_tait +#![feature(min_type_alias_impl_trait)] +#![cfg_attr(full_tait, feature(type_alias_impl_trait))] +//[full_tait]~^ WARN incomplete fn main() { } diff --git a/src/test/ui/type-alias-impl-trait/unused_generic_param.stderr b/src/test/ui/type-alias-impl-trait/unused_generic_param.stderr deleted file mode 100644 index 9c63701ed3350..0000000000000 --- a/src/test/ui/type-alias-impl-trait/unused_generic_param.stderr +++ /dev/null @@ -1,14 +0,0 @@ -error: at least one trait must be specified - --> $DIR/unused_generic_param.rs:6:28 - | -LL | type PartiallyDefined<T> = impl 'static; - | ^^^^^^^^^^^^ - -error: at least one trait must be specified - --> $DIR/unused_generic_param.rs:13:29 - | -LL | type PartiallyDefined2<T> = impl 'static; - | ^^^^^^^^^^^^ - -error: aborting due to 2 previous errors - diff --git a/src/test/ui/type/type-check-defaults.stderr b/src/test/ui/type/type-check-defaults.stderr index d8c7f595e62ef..ddfa31cf62448 100644 --- a/src/test/ui/type/type-check-defaults.stderr +++ b/src/test/ui/type/type-check-defaults.stderr @@ -56,8 +56,8 @@ LL | trait Base<T = String>: Super<T> { } | help: consider further restricting type parameter `T` | -LL | trait Base<T = String>: Super<T> where T: Copy { } - | ^^^^^^^^^^^^^ +LL | trait Base<T = String>: Super<T> where T: std::marker::Copy { } + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ error[E0277]: cannot add `u8` to `i32` --> $DIR/type-check-defaults.rs:24:66 diff --git a/src/test/ui/issues/issue-65611.rs b/src/test/ui/typeck/issue-65611.rs similarity index 97% rename from src/test/ui/issues/issue-65611.rs rename to src/test/ui/typeck/issue-65611.rs index b74ee1b0c6e54..7645311496d8c 100644 --- a/src/test/ui/issues/issue-65611.rs +++ b/src/test/ui/typeck/issue-65611.rs @@ -17,7 +17,7 @@ pub unsafe trait Array { pub trait Index : PartialEq + Copy { fn to_usize(self) -> usize; - fn from(usize) -> Self; + fn from(i: usize) -> Self; } impl Index for usize { diff --git a/src/test/ui/issues/issue-65611.stderr b/src/test/ui/typeck/issue-65611.stderr similarity index 100% rename from src/test/ui/issues/issue-65611.stderr rename to src/test/ui/typeck/issue-65611.stderr diff --git a/src/test/ui/typeck/issue-79040.rs b/src/test/ui/typeck/issue-79040.rs new file mode 100644 index 0000000000000..af2a9c1ba87ef --- /dev/null +++ b/src/test/ui/typeck/issue-79040.rs @@ -0,0 +1,5 @@ +fn main() { + const FOO = "hello" + 1; //~ ERROR cannot add `{integer}` to `&str` + //~^ ERROR cannot add `{integer}` to `&str` + println!("{}", FOO); +} diff --git a/src/test/ui/typeck/issue-79040.stderr b/src/test/ui/typeck/issue-79040.stderr new file mode 100644 index 0000000000000..32049e5d96860 --- /dev/null +++ b/src/test/ui/typeck/issue-79040.stderr @@ -0,0 +1,19 @@ +error[E0369]: cannot add `{integer}` to `&str` + --> $DIR/issue-79040.rs:2:25 + | +LL | const FOO = "hello" + 1; + | ------- ^ - {integer} + | | + | &str + +error[E0369]: cannot add `{integer}` to `&str` + --> $DIR/issue-79040.rs:2:25 + | +LL | const FOO = "hello" + 1; + | ------- ^ - {integer} + | | + | &str + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0369`. diff --git a/src/test/ui/typeck/issue-82772.rs b/src/test/ui/typeck/issue-82772.rs new file mode 100644 index 0000000000000..326273bfe9229 --- /dev/null +++ b/src/test/ui/typeck/issue-82772.rs @@ -0,0 +1,13 @@ +// edition:2018 + +fn main() { + use a::ModPrivateStruct; + let Box { 0: _, .. }: Box<()>; //~ ERROR field `0` of + let Box { 1: _, .. }: Box<()>; //~ ERROR field `1` of + let ModPrivateStruct { 1: _, .. } = ModPrivateStruct::default(); //~ ERROR field `1` of +} + +mod a { + #[derive(Default)] + pub struct ModPrivateStruct(u8, u8); +} diff --git a/src/test/ui/typeck/issue-82772.stderr b/src/test/ui/typeck/issue-82772.stderr new file mode 100644 index 0000000000000..321143cb9683d --- /dev/null +++ b/src/test/ui/typeck/issue-82772.stderr @@ -0,0 +1,21 @@ +error[E0451]: field `0` of struct `Box` is private + --> $DIR/issue-82772.rs:5:15 + | +LL | let Box { 0: _, .. }: Box<()>; + | ^^^^ private field + +error[E0451]: field `1` of struct `Box` is private + --> $DIR/issue-82772.rs:6:15 + | +LL | let Box { 1: _, .. }: Box<()>; + | ^^^^ private field + +error[E0451]: field `1` of struct `ModPrivateStruct` is private + --> $DIR/issue-82772.rs:7:28 + | +LL | let ModPrivateStruct { 1: _, .. } = ModPrivateStruct::default(); + | ^^^^ private field + +error: aborting due to 3 previous errors + +For more information about this error, try `rustc --explain E0451`. diff --git a/src/test/ui/typeck/issue-83621-placeholder-static-in-extern.rs b/src/test/ui/typeck/issue-83621-placeholder-static-in-extern.rs new file mode 100644 index 0000000000000..16ec2a546434c --- /dev/null +++ b/src/test/ui/typeck/issue-83621-placeholder-static-in-extern.rs @@ -0,0 +1,7 @@ +// Regression test for #83621. + +extern "C" { + static x: _; //~ ERROR: [E0121] +} + +fn main() {} diff --git a/src/test/ui/typeck/issue-83621-placeholder-static-in-extern.stderr b/src/test/ui/typeck/issue-83621-placeholder-static-in-extern.stderr new file mode 100644 index 0000000000000..b1bec4c0827f9 --- /dev/null +++ b/src/test/ui/typeck/issue-83621-placeholder-static-in-extern.stderr @@ -0,0 +1,9 @@ +error[E0121]: the type placeholder `_` is not allowed within types on item signatures + --> $DIR/issue-83621-placeholder-static-in-extern.rs:4:15 + | +LL | static x: _; + | ^ not allowed in type signatures + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0121`. diff --git a/src/test/ui/typeck/typeck-default-trait-impl-negation-sync.stderr b/src/test/ui/typeck/typeck-default-trait-impl-negation-sync.stderr index 1f21e12597001..a9b49ee32630b 100644 --- a/src/test/ui/typeck/typeck-default-trait-impl-negation-sync.stderr +++ b/src/test/ui/typeck/typeck-default-trait-impl-negation-sync.stderr @@ -19,7 +19,11 @@ LL | is_sync::<MyTypeWUnsafe>(); | ^^^^^^^^^^^^^^^^^^^^^^^^ `UnsafeCell<u8>` cannot be shared between threads safely | = help: within `MyTypeWUnsafe`, the trait `Sync` is not implemented for `UnsafeCell<u8>` - = note: required because it appears within the type `MyTypeWUnsafe` +note: required because it appears within the type `MyTypeWUnsafe` + --> $DIR/typeck-default-trait-impl-negation-sync.rs:21:8 + | +LL | struct MyTypeWUnsafe { + | ^^^^^^^^^^^^^ error[E0277]: `Managed` cannot be shared between threads safely --> $DIR/typeck-default-trait-impl-negation-sync.rs:39:5 @@ -31,7 +35,11 @@ LL | is_sync::<MyTypeManaged>(); | ^^^^^^^^^^^^^^^^^^^^^^^^ `Managed` cannot be shared between threads safely | = help: within `MyTypeManaged`, the trait `Sync` is not implemented for `Managed` - = note: required because it appears within the type `MyTypeManaged` +note: required because it appears within the type `MyTypeManaged` + --> $DIR/typeck-default-trait-impl-negation-sync.rs:25:8 + | +LL | struct MyTypeManaged { + | ^^^^^^^^^^^^^ error: aborting due to 3 previous errors diff --git a/src/test/ui/typeck/typeck-default-trait-impl-send-param.stderr b/src/test/ui/typeck/typeck-default-trait-impl-send-param.stderr index 4fb423b9a2c38..7398b48a238d1 100644 --- a/src/test/ui/typeck/typeck-default-trait-impl-send-param.stderr +++ b/src/test/ui/typeck/typeck-default-trait-impl-send-param.stderr @@ -9,8 +9,8 @@ LL | fn is_send<T:Send>() { | help: consider restricting type parameter `T` | -LL | fn foo<T: Send>() { - | ^^^^^^ +LL | fn foo<T: std::marker::Send>() { + | ^^^^^^^^^^^^^^^^^^^ error: aborting due to previous error diff --git a/src/test/ui/typeck/typeck-unsafe-always-share.stderr b/src/test/ui/typeck/typeck-unsafe-always-share.stderr index 2a6ae736d7a8d..91585e78d4b93 100644 --- a/src/test/ui/typeck/typeck-unsafe-always-share.stderr +++ b/src/test/ui/typeck/typeck-unsafe-always-share.stderr @@ -30,7 +30,11 @@ LL | test(ms); | ^^^^ `UnsafeCell<NoSync>` cannot be shared between threads safely | = help: within `MySync<NoSync>`, the trait `Sync` is not implemented for `UnsafeCell<NoSync>` - = note: required because it appears within the type `MySync<NoSync>` +note: required because it appears within the type `MySync<NoSync>` + --> $DIR/typeck-unsafe-always-share.rs:8:8 + | +LL | struct MySync<T> { + | ^^^^^^ error[E0277]: `NoSync` cannot be shared between threads safely --> $DIR/typeck-unsafe-always-share.rs:30:10 diff --git a/src/test/ui/typeck/typeck_type_placeholder_item.full_tait.stderr b/src/test/ui/typeck/typeck_type_placeholder_item.full_tait.stderr new file mode 100644 index 0000000000000..75e4cb3e2d819 --- /dev/null +++ b/src/test/ui/typeck/typeck_type_placeholder_item.full_tait.stderr @@ -0,0 +1,647 @@ +error: expected identifier, found reserved identifier `_` + --> $DIR/typeck_type_placeholder_item.rs:158:18 + | +LL | struct BadStruct<_>(_); + | ^ expected identifier, found reserved identifier + +error: expected identifier, found reserved identifier `_` + --> $DIR/typeck_type_placeholder_item.rs:161:16 + | +LL | trait BadTrait<_> {} + | ^ expected identifier, found reserved identifier + +error: expected identifier, found reserved identifier `_` + --> $DIR/typeck_type_placeholder_item.rs:171:19 + | +LL | struct BadStruct1<_, _>(_); + | ^ expected identifier, found reserved identifier + +error: expected identifier, found reserved identifier `_` + --> $DIR/typeck_type_placeholder_item.rs:171:22 + | +LL | struct BadStruct1<_, _>(_); + | ^ expected identifier, found reserved identifier + +error: expected identifier, found reserved identifier `_` + --> $DIR/typeck_type_placeholder_item.rs:176:19 + | +LL | struct BadStruct2<_, T>(_, T); + | ^ expected identifier, found reserved identifier + +error: associated constant in `impl` without body + --> $DIR/typeck_type_placeholder_item.rs:209:5 + | +LL | const C: _; + | ^^^^^^^^^^- + | | + | help: provide a definition for the constant: `= <expr>;` + +error[E0403]: the name `_` is already used for a generic parameter in this item's generic parameters + --> $DIR/typeck_type_placeholder_item.rs:171:22 + | +LL | struct BadStruct1<_, _>(_); + | - ^ already used + | | + | first use of `_` + +warning: the feature `type_alias_impl_trait` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/typeck_type_placeholder_item.rs:5:32 + | +LL | #![cfg_attr(full_tait, feature(type_alias_impl_trait))] + | ^^^^^^^^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #63063 <https://github.com/rust-lang/rust/issues/63063> for more information + +error[E0121]: the type placeholder `_` is not allowed within types on item signatures + --> $DIR/typeck_type_placeholder_item.rs:10:14 + | +LL | fn test() -> _ { 5 } + | ^ + | | + | not allowed in type signatures + | help: replace with the correct return type: `i32` + +error[E0121]: the type placeholder `_` is not allowed within types on item signatures + --> $DIR/typeck_type_placeholder_item.rs:13:16 + | +LL | fn test2() -> (_, _) { (5, 5) } + | -^--^- + | || | + | || not allowed in type signatures + | |not allowed in type signatures + | help: replace with the correct return type: `(i32, i32)` + +error[E0121]: the type placeholder `_` is not allowed within types on item signatures + --> $DIR/typeck_type_placeholder_item.rs:16:15 + | +LL | static TEST3: _ = "test"; + | ^ + | | + | not allowed in type signatures + | help: replace `_` with the correct type: `&str` + +error[E0121]: the type placeholder `_` is not allowed within types on item signatures + --> $DIR/typeck_type_placeholder_item.rs:19:15 + | +LL | static TEST4: _ = 145; + | ^ + | | + | not allowed in type signatures + | help: replace `_` with the correct type: `i32` + +error[E0121]: the type placeholder `_` is not allowed within types on item signatures + --> $DIR/typeck_type_placeholder_item.rs:22:15 + | +LL | static TEST5: (_, _) = (1, 2); + | ^^^^^^ not allowed in type signatures + +error[E0121]: the type placeholder `_` is not allowed within types on item signatures + --> $DIR/typeck_type_placeholder_item.rs:25:13 + | +LL | fn test6(_: _) { } + | ^ not allowed in type signatures + | +help: use type parameters instead + | +LL | fn test6<T>(_: T) { } + | ^^^ ^ + +error[E0121]: the type placeholder `_` is not allowed within types on item signatures + --> $DIR/typeck_type_placeholder_item.rs:28:18 + | +LL | fn test6_b<T>(_: _, _: T) { } + | ^ not allowed in type signatures + | +help: use type parameters instead + | +LL | fn test6_b<T, U>(_: U, _: T) { } + | ^^^ ^ + +error[E0121]: the type placeholder `_` is not allowed within types on item signatures + --> $DIR/typeck_type_placeholder_item.rs:31:30 + | +LL | fn test6_c<T, K, L, A, B>(_: _, _: (T, K, L, A, B)) { } + | ^ not allowed in type signatures + | +help: use type parameters instead + | +LL | fn test6_c<T, K, L, A, B, U>(_: U, _: (T, K, L, A, B)) { } + | ^^^ ^ + +error[E0121]: the type placeholder `_` is not allowed within types on item signatures + --> $DIR/typeck_type_placeholder_item.rs:34:13 + | +LL | fn test7(x: _) { let _x: usize = x; } + | ^ not allowed in type signatures + | +help: use type parameters instead + | +LL | fn test7<T>(x: T) { let _x: usize = x; } + | ^^^ ^ + +error[E0121]: the type placeholder `_` is not allowed within types on item signatures + --> $DIR/typeck_type_placeholder_item.rs:37:22 + | +LL | fn test8(_f: fn() -> _) { } + | ^ + | | + | not allowed in type signatures + | help: use type parameters instead: `T` + +error[E0121]: the type placeholder `_` is not allowed within types on item signatures + --> $DIR/typeck_type_placeholder_item.rs:37:22 + | +LL | fn test8(_f: fn() -> _) { } + | ^ not allowed in type signatures + | +help: use type parameters instead + | +LL | fn test8<T>(_f: fn() -> T) { } + | ^^^ ^ + +error[E0121]: the type placeholder `_` is not allowed within types on item signatures + --> $DIR/typeck_type_placeholder_item.rs:51:26 + | +LL | fn test11(x: &usize) -> &_ { + | -^ + | || + | |not allowed in type signatures + | help: replace with the correct return type: `&'static &'static usize` + +error[E0121]: the type placeholder `_` is not allowed within types on item signatures + --> $DIR/typeck_type_placeholder_item.rs:56:52 + | +LL | unsafe fn test12(x: *const usize) -> *const *const _ { + | --------------^ + | | | + | | not allowed in type signatures + | help: replace with the correct return type: `*const *const usize` + +error[E0121]: the type placeholder `_` is not allowed within types on item signatures + --> $DIR/typeck_type_placeholder_item.rs:70:8 + | +LL | a: _, + | ^ not allowed in type signatures +LL | +LL | b: (_, _), + | ^ ^ not allowed in type signatures + | | + | not allowed in type signatures + | +help: use type parameters instead + | +LL | struct Test10<T> { +LL | a: T, +LL | +LL | b: (T, T), + | + +error: missing type for `static` item + --> $DIR/typeck_type_placeholder_item.rs:76:12 + | +LL | static A = 42; + | ^ help: provide a type for the item: `A: i32` + +error[E0121]: the type placeholder `_` is not allowed within types on item signatures + --> $DIR/typeck_type_placeholder_item.rs:78:15 + | +LL | static B: _ = 42; + | ^ + | | + | not allowed in type signatures + | help: replace `_` with the correct type: `i32` + +error[E0121]: the type placeholder `_` is not allowed within types on item signatures + --> $DIR/typeck_type_placeholder_item.rs:80:15 + | +LL | static C: Option<_> = Some(42); + | ^^^^^^^^^ not allowed in type signatures + +error[E0121]: the type placeholder `_` is not allowed within types on item signatures + --> $DIR/typeck_type_placeholder_item.rs:83:21 + | +LL | fn fn_test() -> _ { 5 } + | ^ + | | + | not allowed in type signatures + | help: replace with the correct return type: `i32` + +error[E0121]: the type placeholder `_` is not allowed within types on item signatures + --> $DIR/typeck_type_placeholder_item.rs:86:23 + | +LL | fn fn_test2() -> (_, _) { (5, 5) } + | -^--^- + | || | + | || not allowed in type signatures + | |not allowed in type signatures + | help: replace with the correct return type: `(i32, i32)` + +error[E0121]: the type placeholder `_` is not allowed within types on item signatures + --> $DIR/typeck_type_placeholder_item.rs:89:22 + | +LL | static FN_TEST3: _ = "test"; + | ^ + | | + | not allowed in type signatures + | help: replace `_` with the correct type: `&str` + +error[E0121]: the type placeholder `_` is not allowed within types on item signatures + --> $DIR/typeck_type_placeholder_item.rs:92:22 + | +LL | static FN_TEST4: _ = 145; + | ^ + | | + | not allowed in type signatures + | help: replace `_` with the correct type: `i32` + +error[E0121]: the type placeholder `_` is not allowed within types on item signatures + --> $DIR/typeck_type_placeholder_item.rs:95:22 + | +LL | static FN_TEST5: (_, _) = (1, 2); + | ^^^^^^ not allowed in type signatures + +error[E0121]: the type placeholder `_` is not allowed within types on item signatures + --> $DIR/typeck_type_placeholder_item.rs:98:20 + | +LL | fn fn_test6(_: _) { } + | ^ not allowed in type signatures + | +help: use type parameters instead + | +LL | fn fn_test6<T>(_: T) { } + | ^^^ ^ + +error[E0121]: the type placeholder `_` is not allowed within types on item signatures + --> $DIR/typeck_type_placeholder_item.rs:101:20 + | +LL | fn fn_test7(x: _) { let _x: usize = x; } + | ^ not allowed in type signatures + | +help: use type parameters instead + | +LL | fn fn_test7<T>(x: T) { let _x: usize = x; } + | ^^^ ^ + +error[E0121]: the type placeholder `_` is not allowed within types on item signatures + --> $DIR/typeck_type_placeholder_item.rs:104:29 + | +LL | fn fn_test8(_f: fn() -> _) { } + | ^ + | | + | not allowed in type signatures + | help: use type parameters instead: `T` + +error[E0121]: the type placeholder `_` is not allowed within types on item signatures + --> $DIR/typeck_type_placeholder_item.rs:104:29 + | +LL | fn fn_test8(_f: fn() -> _) { } + | ^ not allowed in type signatures + | +help: use type parameters instead + | +LL | fn fn_test8<T>(_f: fn() -> T) { } + | ^^^ ^ + +error[E0121]: the type placeholder `_` is not allowed within types on item signatures + --> $DIR/typeck_type_placeholder_item.rs:127:12 + | +LL | a: _, + | ^ not allowed in type signatures +LL | +LL | b: (_, _), + | ^ ^ not allowed in type signatures + | | + | not allowed in type signatures + | +help: use type parameters instead + | +LL | struct FnTest10<T> { +LL | a: T, +LL | +LL | b: (T, T), + | + +error[E0282]: type annotations needed + --> $DIR/typeck_type_placeholder_item.rs:132:18 + | +LL | fn fn_test11(_: _) -> (_, _) { panic!() } + | ^ cannot infer type + +error[E0121]: the type placeholder `_` is not allowed within types on item signatures + --> $DIR/typeck_type_placeholder_item.rs:132:28 + | +LL | fn fn_test11(_: _) -> (_, _) { panic!() } + | ^ ^ not allowed in type signatures + | | + | not allowed in type signatures + +error[E0121]: the type placeholder `_` is not allowed within types on item signatures + --> $DIR/typeck_type_placeholder_item.rs:136:30 + | +LL | fn fn_test12(x: i32) -> (_, _) { (x, x) } + | -^--^- + | || | + | || not allowed in type signatures + | |not allowed in type signatures + | help: replace with the correct return type: `(i32, i32)` + +error[E0121]: the type placeholder `_` is not allowed within types on item signatures + --> $DIR/typeck_type_placeholder_item.rs:139:33 + | +LL | fn fn_test13(x: _) -> (i32, _) { (x, x) } + | ------^- + | | | + | | not allowed in type signatures + | help: replace with the correct return type: `(i32, i32)` + +error[E0121]: the type placeholder `_` is not allowed within types on item signatures + --> $DIR/typeck_type_placeholder_item.rs:158:21 + | +LL | struct BadStruct<_>(_); + | ^ not allowed in type signatures + | +help: use type parameters instead + | +LL | struct BadStruct<T>(T); + | ^ ^ + +error[E0121]: the type placeholder `_` is not allowed within types on item signatures + --> $DIR/typeck_type_placeholder_item.rs:163:15 + | +LL | impl BadTrait<_> for BadStruct<_> {} + | ^ ^ not allowed in type signatures + | | + | not allowed in type signatures + | +help: use type parameters instead + | +LL | impl<T> BadTrait<T> for BadStruct<T> {} + | ^^^ ^ ^ + +error[E0121]: the type placeholder `_` is not allowed within types on item signatures + --> $DIR/typeck_type_placeholder_item.rs:166:34 + | +LL | fn impl_trait() -> impl BadTrait<_> { + | ^ not allowed in type signatures + +error[E0121]: the type placeholder `_` is not allowed within types on item signatures + --> $DIR/typeck_type_placeholder_item.rs:171:25 + | +LL | struct BadStruct1<_, _>(_); + | ^ not allowed in type signatures + | +help: use type parameters instead + | +LL | struct BadStruct1<T, _>(T); + | ^ ^ + +error[E0121]: the type placeholder `_` is not allowed within types on item signatures + --> $DIR/typeck_type_placeholder_item.rs:176:25 + | +LL | struct BadStruct2<_, T>(_, T); + | ^ not allowed in type signatures + | +help: use type parameters instead + | +LL | struct BadStruct2<U, T>(U, T); + | ^ ^ + +error[E0121]: the type placeholder `_` is not allowed within types on item signatures + --> $DIR/typeck_type_placeholder_item.rs:180:14 + | +LL | type X = Box<_>; + | ^ not allowed in type signatures + +error[E0121]: the type placeholder `_` is not allowed within types on item signatures + --> $DIR/typeck_type_placeholder_item.rs:186:21 + | +LL | type Y = impl Trait<_>; + | ^ not allowed in type signatures + +error[E0121]: the type placeholder `_` is not allowed within types on item signatures + --> $DIR/typeck_type_placeholder_item.rs:220:31 + | +LL | fn value() -> Option<&'static _> { + | ----------------^- + | | | + | | not allowed in type signatures + | help: replace with the correct return type: `Option<&'static u8>` + +error[E0121]: the type placeholder `_` is not allowed within types on item signatures + --> $DIR/typeck_type_placeholder_item.rs:225:10 + | +LL | const _: Option<_> = map(value); + | ^^^^^^^^^ + | | + | not allowed in type signatures + | help: replace `_` with the correct type: `Option<u8>` + +error[E0121]: the type placeholder `_` is not allowed within types on item signatures + --> $DIR/typeck_type_placeholder_item.rs:144:31 + | +LL | fn method_test1(&self, x: _); + | ^ not allowed in type signatures + | +help: use type parameters instead + | +LL | fn method_test1<T>(&self, x: T); + | ^^^ ^ + +error[E0121]: the type placeholder `_` is not allowed within types on item signatures + --> $DIR/typeck_type_placeholder_item.rs:146:31 + | +LL | fn method_test2(&self, x: _) -> _; + | ^ ^ not allowed in type signatures + | | + | not allowed in type signatures + | +help: use type parameters instead + | +LL | fn method_test2<T>(&self, x: T) -> T; + | ^^^ ^ ^ + +error[E0121]: the type placeholder `_` is not allowed within types on item signatures + --> $DIR/typeck_type_placeholder_item.rs:148:31 + | +LL | fn method_test3(&self) -> _; + | ^ not allowed in type signatures + | +help: use type parameters instead + | +LL | fn method_test3<T>(&self) -> T; + | ^^^ ^ + +error[E0121]: the type placeholder `_` is not allowed within types on item signatures + --> $DIR/typeck_type_placeholder_item.rs:150:26 + | +LL | fn assoc_fn_test1(x: _); + | ^ not allowed in type signatures + | +help: use type parameters instead + | +LL | fn assoc_fn_test1<T>(x: T); + | ^^^ ^ + +error[E0121]: the type placeholder `_` is not allowed within types on item signatures + --> $DIR/typeck_type_placeholder_item.rs:152:26 + | +LL | fn assoc_fn_test2(x: _) -> _; + | ^ ^ not allowed in type signatures + | | + | not allowed in type signatures + | +help: use type parameters instead + | +LL | fn assoc_fn_test2<T>(x: T) -> T; + | ^^^ ^ ^ + +error[E0121]: the type placeholder `_` is not allowed within types on item signatures + --> $DIR/typeck_type_placeholder_item.rs:154:28 + | +LL | fn assoc_fn_test3() -> _; + | ^ not allowed in type signatures + | +help: use type parameters instead + | +LL | fn assoc_fn_test3<T>() -> T; + | ^^^ ^ + +error[E0121]: the type placeholder `_` is not allowed within types on item signatures + --> $DIR/typeck_type_placeholder_item.rs:194:14 + | +LL | type B = _; + | ^ not allowed in type signatures + +error[E0121]: the type placeholder `_` is not allowed within types on item signatures + --> $DIR/typeck_type_placeholder_item.rs:196:14 + | +LL | const C: _; + | ^ not allowed in type signatures + +error[E0121]: the type placeholder `_` is not allowed within types on item signatures + --> $DIR/typeck_type_placeholder_item.rs:198:14 + | +LL | const D: _ = 42; + | ^ + | | + | not allowed in type signatures + | help: replace `_` with the correct type: `i32` + +error[E0121]: the type placeholder `_` is not allowed within types on item signatures + --> $DIR/typeck_type_placeholder_item.rs:201:26 + | +LL | type F: std::ops::Fn(_); + | ^ not allowed in type signatures + +error[E0121]: the type placeholder `_` is not allowed within types on item signatures + --> $DIR/typeck_type_placeholder_item.rs:44:24 + | +LL | fn test9(&self) -> _ { () } + | ^ + | | + | not allowed in type signatures + | help: replace with the correct return type: `()` + +error[E0121]: the type placeholder `_` is not allowed within types on item signatures + --> $DIR/typeck_type_placeholder_item.rs:47:27 + | +LL | fn test10(&self, _x : _) { } + | ^ not allowed in type signatures + | +help: use type parameters instead + | +LL | fn test10<T>(&self, _x : T) { } + | ^^^ ^ + +error[E0121]: the type placeholder `_` is not allowed within types on item signatures + --> $DIR/typeck_type_placeholder_item.rs:62:24 + | +LL | fn clone(&self) -> _ { Test9 } + | ^ + | | + | not allowed in type signatures + | help: replace with the correct return type: `Test9` + +error[E0121]: the type placeholder `_` is not allowed within types on item signatures + --> $DIR/typeck_type_placeholder_item.rs:65:37 + | +LL | fn clone_from(&mut self, other: _) { *self = Test9; } + | ^ not allowed in type signatures + | +help: use type parameters instead + | +LL | fn clone_from<T>(&mut self, other: T) { *self = Test9; } + | ^^^ ^ + +error[E0121]: the type placeholder `_` is not allowed within types on item signatures + --> $DIR/typeck_type_placeholder_item.rs:111:31 + | +LL | fn fn_test9(&self) -> _ { () } + | ^ + | | + | not allowed in type signatures + | help: replace with the correct return type: `()` + +error[E0121]: the type placeholder `_` is not allowed within types on item signatures + --> $DIR/typeck_type_placeholder_item.rs:114:34 + | +LL | fn fn_test10(&self, _x : _) { } + | ^ not allowed in type signatures + | +help: use type parameters instead + | +LL | fn fn_test10<T>(&self, _x : T) { } + | ^^^ ^ + +error[E0121]: the type placeholder `_` is not allowed within types on item signatures + --> $DIR/typeck_type_placeholder_item.rs:119:28 + | +LL | fn clone(&self) -> _ { FnTest9 } + | ^ + | | + | not allowed in type signatures + | help: replace with the correct return type: `FnTest9` + +error[E0121]: the type placeholder `_` is not allowed within types on item signatures + --> $DIR/typeck_type_placeholder_item.rs:122:41 + | +LL | fn clone_from(&mut self, other: _) { *self = FnTest9; } + | ^ not allowed in type signatures + | +help: use type parameters instead + | +LL | fn clone_from<T>(&mut self, other: T) { *self = FnTest9; } + | ^^^ ^ + +error[E0121]: the type placeholder `_` is not allowed within types on item signatures + --> $DIR/typeck_type_placeholder_item.rs:205:14 + | +LL | type A = _; + | ^ not allowed in type signatures + +error[E0121]: the type placeholder `_` is not allowed within types on item signatures + --> $DIR/typeck_type_placeholder_item.rs:207:14 + | +LL | type B = _; + | ^ not allowed in type signatures + +error[E0121]: the type placeholder `_` is not allowed within types on item signatures + --> $DIR/typeck_type_placeholder_item.rs:209:14 + | +LL | const C: _; + | ^ not allowed in type signatures + +error[E0121]: the type placeholder `_` is not allowed within types on item signatures + --> $DIR/typeck_type_placeholder_item.rs:212:14 + | +LL | const D: _ = 42; + | ^ + | | + | not allowed in type signatures + | help: replace `_` with the correct type: `i32` + +error: aborting due to 69 previous errors; 1 warning emitted + +Some errors have detailed explanations: E0121, E0282, E0403. +For more information about an error, try `rustc --explain E0121`. diff --git a/src/test/ui/typeck/typeck_type_placeholder_item.min_tait.stderr b/src/test/ui/typeck/typeck_type_placeholder_item.min_tait.stderr new file mode 100644 index 0000000000000..c6758c52a914e --- /dev/null +++ b/src/test/ui/typeck/typeck_type_placeholder_item.min_tait.stderr @@ -0,0 +1,638 @@ +error: expected identifier, found reserved identifier `_` + --> $DIR/typeck_type_placeholder_item.rs:158:18 + | +LL | struct BadStruct<_>(_); + | ^ expected identifier, found reserved identifier + +error: expected identifier, found reserved identifier `_` + --> $DIR/typeck_type_placeholder_item.rs:161:16 + | +LL | trait BadTrait<_> {} + | ^ expected identifier, found reserved identifier + +error: expected identifier, found reserved identifier `_` + --> $DIR/typeck_type_placeholder_item.rs:171:19 + | +LL | struct BadStruct1<_, _>(_); + | ^ expected identifier, found reserved identifier + +error: expected identifier, found reserved identifier `_` + --> $DIR/typeck_type_placeholder_item.rs:171:22 + | +LL | struct BadStruct1<_, _>(_); + | ^ expected identifier, found reserved identifier + +error: expected identifier, found reserved identifier `_` + --> $DIR/typeck_type_placeholder_item.rs:176:19 + | +LL | struct BadStruct2<_, T>(_, T); + | ^ expected identifier, found reserved identifier + +error: associated constant in `impl` without body + --> $DIR/typeck_type_placeholder_item.rs:209:5 + | +LL | const C: _; + | ^^^^^^^^^^- + | | + | help: provide a definition for the constant: `= <expr>;` + +error[E0403]: the name `_` is already used for a generic parameter in this item's generic parameters + --> $DIR/typeck_type_placeholder_item.rs:171:22 + | +LL | struct BadStruct1<_, _>(_); + | - ^ already used + | | + | first use of `_` + +error[E0121]: the type placeholder `_` is not allowed within types on item signatures + --> $DIR/typeck_type_placeholder_item.rs:10:14 + | +LL | fn test() -> _ { 5 } + | ^ + | | + | not allowed in type signatures + | help: replace with the correct return type: `i32` + +error[E0121]: the type placeholder `_` is not allowed within types on item signatures + --> $DIR/typeck_type_placeholder_item.rs:13:16 + | +LL | fn test2() -> (_, _) { (5, 5) } + | -^--^- + | || | + | || not allowed in type signatures + | |not allowed in type signatures + | help: replace with the correct return type: `(i32, i32)` + +error[E0121]: the type placeholder `_` is not allowed within types on item signatures + --> $DIR/typeck_type_placeholder_item.rs:16:15 + | +LL | static TEST3: _ = "test"; + | ^ + | | + | not allowed in type signatures + | help: replace `_` with the correct type: `&str` + +error[E0121]: the type placeholder `_` is not allowed within types on item signatures + --> $DIR/typeck_type_placeholder_item.rs:19:15 + | +LL | static TEST4: _ = 145; + | ^ + | | + | not allowed in type signatures + | help: replace `_` with the correct type: `i32` + +error[E0121]: the type placeholder `_` is not allowed within types on item signatures + --> $DIR/typeck_type_placeholder_item.rs:22:15 + | +LL | static TEST5: (_, _) = (1, 2); + | ^^^^^^ not allowed in type signatures + +error[E0121]: the type placeholder `_` is not allowed within types on item signatures + --> $DIR/typeck_type_placeholder_item.rs:25:13 + | +LL | fn test6(_: _) { } + | ^ not allowed in type signatures + | +help: use type parameters instead + | +LL | fn test6<T>(_: T) { } + | ^^^ ^ + +error[E0121]: the type placeholder `_` is not allowed within types on item signatures + --> $DIR/typeck_type_placeholder_item.rs:28:18 + | +LL | fn test6_b<T>(_: _, _: T) { } + | ^ not allowed in type signatures + | +help: use type parameters instead + | +LL | fn test6_b<T, U>(_: U, _: T) { } + | ^^^ ^ + +error[E0121]: the type placeholder `_` is not allowed within types on item signatures + --> $DIR/typeck_type_placeholder_item.rs:31:30 + | +LL | fn test6_c<T, K, L, A, B>(_: _, _: (T, K, L, A, B)) { } + | ^ not allowed in type signatures + | +help: use type parameters instead + | +LL | fn test6_c<T, K, L, A, B, U>(_: U, _: (T, K, L, A, B)) { } + | ^^^ ^ + +error[E0121]: the type placeholder `_` is not allowed within types on item signatures + --> $DIR/typeck_type_placeholder_item.rs:34:13 + | +LL | fn test7(x: _) { let _x: usize = x; } + | ^ not allowed in type signatures + | +help: use type parameters instead + | +LL | fn test7<T>(x: T) { let _x: usize = x; } + | ^^^ ^ + +error[E0121]: the type placeholder `_` is not allowed within types on item signatures + --> $DIR/typeck_type_placeholder_item.rs:37:22 + | +LL | fn test8(_f: fn() -> _) { } + | ^ + | | + | not allowed in type signatures + | help: use type parameters instead: `T` + +error[E0121]: the type placeholder `_` is not allowed within types on item signatures + --> $DIR/typeck_type_placeholder_item.rs:37:22 + | +LL | fn test8(_f: fn() -> _) { } + | ^ not allowed in type signatures + | +help: use type parameters instead + | +LL | fn test8<T>(_f: fn() -> T) { } + | ^^^ ^ + +error[E0121]: the type placeholder `_` is not allowed within types on item signatures + --> $DIR/typeck_type_placeholder_item.rs:51:26 + | +LL | fn test11(x: &usize) -> &_ { + | -^ + | || + | |not allowed in type signatures + | help: replace with the correct return type: `&'static &'static usize` + +error[E0121]: the type placeholder `_` is not allowed within types on item signatures + --> $DIR/typeck_type_placeholder_item.rs:56:52 + | +LL | unsafe fn test12(x: *const usize) -> *const *const _ { + | --------------^ + | | | + | | not allowed in type signatures + | help: replace with the correct return type: `*const *const usize` + +error[E0121]: the type placeholder `_` is not allowed within types on item signatures + --> $DIR/typeck_type_placeholder_item.rs:70:8 + | +LL | a: _, + | ^ not allowed in type signatures +LL | +LL | b: (_, _), + | ^ ^ not allowed in type signatures + | | + | not allowed in type signatures + | +help: use type parameters instead + | +LL | struct Test10<T> { +LL | a: T, +LL | +LL | b: (T, T), + | + +error: missing type for `static` item + --> $DIR/typeck_type_placeholder_item.rs:76:12 + | +LL | static A = 42; + | ^ help: provide a type for the item: `A: i32` + +error[E0121]: the type placeholder `_` is not allowed within types on item signatures + --> $DIR/typeck_type_placeholder_item.rs:78:15 + | +LL | static B: _ = 42; + | ^ + | | + | not allowed in type signatures + | help: replace `_` with the correct type: `i32` + +error[E0121]: the type placeholder `_` is not allowed within types on item signatures + --> $DIR/typeck_type_placeholder_item.rs:80:15 + | +LL | static C: Option<_> = Some(42); + | ^^^^^^^^^ not allowed in type signatures + +error[E0121]: the type placeholder `_` is not allowed within types on item signatures + --> $DIR/typeck_type_placeholder_item.rs:83:21 + | +LL | fn fn_test() -> _ { 5 } + | ^ + | | + | not allowed in type signatures + | help: replace with the correct return type: `i32` + +error[E0121]: the type placeholder `_` is not allowed within types on item signatures + --> $DIR/typeck_type_placeholder_item.rs:86:23 + | +LL | fn fn_test2() -> (_, _) { (5, 5) } + | -^--^- + | || | + | || not allowed in type signatures + | |not allowed in type signatures + | help: replace with the correct return type: `(i32, i32)` + +error[E0121]: the type placeholder `_` is not allowed within types on item signatures + --> $DIR/typeck_type_placeholder_item.rs:89:22 + | +LL | static FN_TEST3: _ = "test"; + | ^ + | | + | not allowed in type signatures + | help: replace `_` with the correct type: `&str` + +error[E0121]: the type placeholder `_` is not allowed within types on item signatures + --> $DIR/typeck_type_placeholder_item.rs:92:22 + | +LL | static FN_TEST4: _ = 145; + | ^ + | | + | not allowed in type signatures + | help: replace `_` with the correct type: `i32` + +error[E0121]: the type placeholder `_` is not allowed within types on item signatures + --> $DIR/typeck_type_placeholder_item.rs:95:22 + | +LL | static FN_TEST5: (_, _) = (1, 2); + | ^^^^^^ not allowed in type signatures + +error[E0121]: the type placeholder `_` is not allowed within types on item signatures + --> $DIR/typeck_type_placeholder_item.rs:98:20 + | +LL | fn fn_test6(_: _) { } + | ^ not allowed in type signatures + | +help: use type parameters instead + | +LL | fn fn_test6<T>(_: T) { } + | ^^^ ^ + +error[E0121]: the type placeholder `_` is not allowed within types on item signatures + --> $DIR/typeck_type_placeholder_item.rs:101:20 + | +LL | fn fn_test7(x: _) { let _x: usize = x; } + | ^ not allowed in type signatures + | +help: use type parameters instead + | +LL | fn fn_test7<T>(x: T) { let _x: usize = x; } + | ^^^ ^ + +error[E0121]: the type placeholder `_` is not allowed within types on item signatures + --> $DIR/typeck_type_placeholder_item.rs:104:29 + | +LL | fn fn_test8(_f: fn() -> _) { } + | ^ + | | + | not allowed in type signatures + | help: use type parameters instead: `T` + +error[E0121]: the type placeholder `_` is not allowed within types on item signatures + --> $DIR/typeck_type_placeholder_item.rs:104:29 + | +LL | fn fn_test8(_f: fn() -> _) { } + | ^ not allowed in type signatures + | +help: use type parameters instead + | +LL | fn fn_test8<T>(_f: fn() -> T) { } + | ^^^ ^ + +error[E0121]: the type placeholder `_` is not allowed within types on item signatures + --> $DIR/typeck_type_placeholder_item.rs:127:12 + | +LL | a: _, + | ^ not allowed in type signatures +LL | +LL | b: (_, _), + | ^ ^ not allowed in type signatures + | | + | not allowed in type signatures + | +help: use type parameters instead + | +LL | struct FnTest10<T> { +LL | a: T, +LL | +LL | b: (T, T), + | + +error[E0282]: type annotations needed + --> $DIR/typeck_type_placeholder_item.rs:132:18 + | +LL | fn fn_test11(_: _) -> (_, _) { panic!() } + | ^ cannot infer type + +error[E0121]: the type placeholder `_` is not allowed within types on item signatures + --> $DIR/typeck_type_placeholder_item.rs:132:28 + | +LL | fn fn_test11(_: _) -> (_, _) { panic!() } + | ^ ^ not allowed in type signatures + | | + | not allowed in type signatures + +error[E0121]: the type placeholder `_` is not allowed within types on item signatures + --> $DIR/typeck_type_placeholder_item.rs:136:30 + | +LL | fn fn_test12(x: i32) -> (_, _) { (x, x) } + | -^--^- + | || | + | || not allowed in type signatures + | |not allowed in type signatures + | help: replace with the correct return type: `(i32, i32)` + +error[E0121]: the type placeholder `_` is not allowed within types on item signatures + --> $DIR/typeck_type_placeholder_item.rs:139:33 + | +LL | fn fn_test13(x: _) -> (i32, _) { (x, x) } + | ------^- + | | | + | | not allowed in type signatures + | help: replace with the correct return type: `(i32, i32)` + +error[E0121]: the type placeholder `_` is not allowed within types on item signatures + --> $DIR/typeck_type_placeholder_item.rs:158:21 + | +LL | struct BadStruct<_>(_); + | ^ not allowed in type signatures + | +help: use type parameters instead + | +LL | struct BadStruct<T>(T); + | ^ ^ + +error[E0121]: the type placeholder `_` is not allowed within types on item signatures + --> $DIR/typeck_type_placeholder_item.rs:163:15 + | +LL | impl BadTrait<_> for BadStruct<_> {} + | ^ ^ not allowed in type signatures + | | + | not allowed in type signatures + | +help: use type parameters instead + | +LL | impl<T> BadTrait<T> for BadStruct<T> {} + | ^^^ ^ ^ + +error[E0121]: the type placeholder `_` is not allowed within types on item signatures + --> $DIR/typeck_type_placeholder_item.rs:166:34 + | +LL | fn impl_trait() -> impl BadTrait<_> { + | ^ not allowed in type signatures + +error[E0121]: the type placeholder `_` is not allowed within types on item signatures + --> $DIR/typeck_type_placeholder_item.rs:171:25 + | +LL | struct BadStruct1<_, _>(_); + | ^ not allowed in type signatures + | +help: use type parameters instead + | +LL | struct BadStruct1<T, _>(T); + | ^ ^ + +error[E0121]: the type placeholder `_` is not allowed within types on item signatures + --> $DIR/typeck_type_placeholder_item.rs:176:25 + | +LL | struct BadStruct2<_, T>(_, T); + | ^ not allowed in type signatures + | +help: use type parameters instead + | +LL | struct BadStruct2<U, T>(U, T); + | ^ ^ + +error[E0121]: the type placeholder `_` is not allowed within types on item signatures + --> $DIR/typeck_type_placeholder_item.rs:180:14 + | +LL | type X = Box<_>; + | ^ not allowed in type signatures + +error[E0121]: the type placeholder `_` is not allowed within types on item signatures + --> $DIR/typeck_type_placeholder_item.rs:186:21 + | +LL | type Y = impl Trait<_>; + | ^ not allowed in type signatures + +error[E0121]: the type placeholder `_` is not allowed within types on item signatures + --> $DIR/typeck_type_placeholder_item.rs:220:31 + | +LL | fn value() -> Option<&'static _> { + | ----------------^- + | | | + | | not allowed in type signatures + | help: replace with the correct return type: `Option<&'static u8>` + +error[E0121]: the type placeholder `_` is not allowed within types on item signatures + --> $DIR/typeck_type_placeholder_item.rs:225:10 + | +LL | const _: Option<_> = map(value); + | ^^^^^^^^^ + | | + | not allowed in type signatures + | help: replace `_` with the correct type: `Option<u8>` + +error[E0121]: the type placeholder `_` is not allowed within types on item signatures + --> $DIR/typeck_type_placeholder_item.rs:144:31 + | +LL | fn method_test1(&self, x: _); + | ^ not allowed in type signatures + | +help: use type parameters instead + | +LL | fn method_test1<T>(&self, x: T); + | ^^^ ^ + +error[E0121]: the type placeholder `_` is not allowed within types on item signatures + --> $DIR/typeck_type_placeholder_item.rs:146:31 + | +LL | fn method_test2(&self, x: _) -> _; + | ^ ^ not allowed in type signatures + | | + | not allowed in type signatures + | +help: use type parameters instead + | +LL | fn method_test2<T>(&self, x: T) -> T; + | ^^^ ^ ^ + +error[E0121]: the type placeholder `_` is not allowed within types on item signatures + --> $DIR/typeck_type_placeholder_item.rs:148:31 + | +LL | fn method_test3(&self) -> _; + | ^ not allowed in type signatures + | +help: use type parameters instead + | +LL | fn method_test3<T>(&self) -> T; + | ^^^ ^ + +error[E0121]: the type placeholder `_` is not allowed within types on item signatures + --> $DIR/typeck_type_placeholder_item.rs:150:26 + | +LL | fn assoc_fn_test1(x: _); + | ^ not allowed in type signatures + | +help: use type parameters instead + | +LL | fn assoc_fn_test1<T>(x: T); + | ^^^ ^ + +error[E0121]: the type placeholder `_` is not allowed within types on item signatures + --> $DIR/typeck_type_placeholder_item.rs:152:26 + | +LL | fn assoc_fn_test2(x: _) -> _; + | ^ ^ not allowed in type signatures + | | + | not allowed in type signatures + | +help: use type parameters instead + | +LL | fn assoc_fn_test2<T>(x: T) -> T; + | ^^^ ^ ^ + +error[E0121]: the type placeholder `_` is not allowed within types on item signatures + --> $DIR/typeck_type_placeholder_item.rs:154:28 + | +LL | fn assoc_fn_test3() -> _; + | ^ not allowed in type signatures + | +help: use type parameters instead + | +LL | fn assoc_fn_test3<T>() -> T; + | ^^^ ^ + +error[E0121]: the type placeholder `_` is not allowed within types on item signatures + --> $DIR/typeck_type_placeholder_item.rs:194:14 + | +LL | type B = _; + | ^ not allowed in type signatures + +error[E0121]: the type placeholder `_` is not allowed within types on item signatures + --> $DIR/typeck_type_placeholder_item.rs:196:14 + | +LL | const C: _; + | ^ not allowed in type signatures + +error[E0121]: the type placeholder `_` is not allowed within types on item signatures + --> $DIR/typeck_type_placeholder_item.rs:198:14 + | +LL | const D: _ = 42; + | ^ + | | + | not allowed in type signatures + | help: replace `_` with the correct type: `i32` + +error[E0121]: the type placeholder `_` is not allowed within types on item signatures + --> $DIR/typeck_type_placeholder_item.rs:201:26 + | +LL | type F: std::ops::Fn(_); + | ^ not allowed in type signatures + +error[E0121]: the type placeholder `_` is not allowed within types on item signatures + --> $DIR/typeck_type_placeholder_item.rs:44:24 + | +LL | fn test9(&self) -> _ { () } + | ^ + | | + | not allowed in type signatures + | help: replace with the correct return type: `()` + +error[E0121]: the type placeholder `_` is not allowed within types on item signatures + --> $DIR/typeck_type_placeholder_item.rs:47:27 + | +LL | fn test10(&self, _x : _) { } + | ^ not allowed in type signatures + | +help: use type parameters instead + | +LL | fn test10<T>(&self, _x : T) { } + | ^^^ ^ + +error[E0121]: the type placeholder `_` is not allowed within types on item signatures + --> $DIR/typeck_type_placeholder_item.rs:62:24 + | +LL | fn clone(&self) -> _ { Test9 } + | ^ + | | + | not allowed in type signatures + | help: replace with the correct return type: `Test9` + +error[E0121]: the type placeholder `_` is not allowed within types on item signatures + --> $DIR/typeck_type_placeholder_item.rs:65:37 + | +LL | fn clone_from(&mut self, other: _) { *self = Test9; } + | ^ not allowed in type signatures + | +help: use type parameters instead + | +LL | fn clone_from<T>(&mut self, other: T) { *self = Test9; } + | ^^^ ^ + +error[E0121]: the type placeholder `_` is not allowed within types on item signatures + --> $DIR/typeck_type_placeholder_item.rs:111:31 + | +LL | fn fn_test9(&self) -> _ { () } + | ^ + | | + | not allowed in type signatures + | help: replace with the correct return type: `()` + +error[E0121]: the type placeholder `_` is not allowed within types on item signatures + --> $DIR/typeck_type_placeholder_item.rs:114:34 + | +LL | fn fn_test10(&self, _x : _) { } + | ^ not allowed in type signatures + | +help: use type parameters instead + | +LL | fn fn_test10<T>(&self, _x : T) { } + | ^^^ ^ + +error[E0121]: the type placeholder `_` is not allowed within types on item signatures + --> $DIR/typeck_type_placeholder_item.rs:119:28 + | +LL | fn clone(&self) -> _ { FnTest9 } + | ^ + | | + | not allowed in type signatures + | help: replace with the correct return type: `FnTest9` + +error[E0121]: the type placeholder `_` is not allowed within types on item signatures + --> $DIR/typeck_type_placeholder_item.rs:122:41 + | +LL | fn clone_from(&mut self, other: _) { *self = FnTest9; } + | ^ not allowed in type signatures + | +help: use type parameters instead + | +LL | fn clone_from<T>(&mut self, other: T) { *self = FnTest9; } + | ^^^ ^ + +error[E0121]: the type placeholder `_` is not allowed within types on item signatures + --> $DIR/typeck_type_placeholder_item.rs:205:14 + | +LL | type A = _; + | ^ not allowed in type signatures + +error[E0121]: the type placeholder `_` is not allowed within types on item signatures + --> $DIR/typeck_type_placeholder_item.rs:207:14 + | +LL | type B = _; + | ^ not allowed in type signatures + +error[E0121]: the type placeholder `_` is not allowed within types on item signatures + --> $DIR/typeck_type_placeholder_item.rs:209:14 + | +LL | const C: _; + | ^ not allowed in type signatures + +error[E0121]: the type placeholder `_` is not allowed within types on item signatures + --> $DIR/typeck_type_placeholder_item.rs:212:14 + | +LL | const D: _ = 42; + | ^ + | | + | not allowed in type signatures + | help: replace `_` with the correct type: `i32` + +error: aborting due to 69 previous errors + +Some errors have detailed explanations: E0121, E0282, E0403. +For more information about an error, try `rustc --explain E0121`. diff --git a/src/test/ui/typeck/typeck_type_placeholder_item.rs b/src/test/ui/typeck/typeck_type_placeholder_item.rs index 2523362fdc4b6..8a52556ed346e 100644 --- a/src/test/ui/typeck/typeck_type_placeholder_item.rs +++ b/src/test/ui/typeck/typeck_type_placeholder_item.rs @@ -1,5 +1,9 @@ // Needed for `type Y = impl Trait<_>` and `type B = _;` -#![feature(type_alias_impl_trait, associated_type_defaults)] +#![feature(associated_type_defaults)] +// revisions: min_tait full_tait +#![feature(min_type_alias_impl_trait)] +#![cfg_attr(full_tait, feature(type_alias_impl_trait))] +//[full_tait]~^ WARN incomplete // This test checks that it is not possible to enable global type // inference by using the `_` type placeholder. diff --git a/src/test/ui/typeck/typeck_type_placeholder_item.stderr b/src/test/ui/typeck/typeck_type_placeholder_item.stderr deleted file mode 100644 index 1034402bfb08d..0000000000000 --- a/src/test/ui/typeck/typeck_type_placeholder_item.stderr +++ /dev/null @@ -1,638 +0,0 @@ -error: expected identifier, found reserved identifier `_` - --> $DIR/typeck_type_placeholder_item.rs:154:18 - | -LL | struct BadStruct<_>(_); - | ^ expected identifier, found reserved identifier - -error: expected identifier, found reserved identifier `_` - --> $DIR/typeck_type_placeholder_item.rs:157:16 - | -LL | trait BadTrait<_> {} - | ^ expected identifier, found reserved identifier - -error: expected identifier, found reserved identifier `_` - --> $DIR/typeck_type_placeholder_item.rs:167:19 - | -LL | struct BadStruct1<_, _>(_); - | ^ expected identifier, found reserved identifier - -error: expected identifier, found reserved identifier `_` - --> $DIR/typeck_type_placeholder_item.rs:167:22 - | -LL | struct BadStruct1<_, _>(_); - | ^ expected identifier, found reserved identifier - -error: expected identifier, found reserved identifier `_` - --> $DIR/typeck_type_placeholder_item.rs:172:19 - | -LL | struct BadStruct2<_, T>(_, T); - | ^ expected identifier, found reserved identifier - -error: associated constant in `impl` without body - --> $DIR/typeck_type_placeholder_item.rs:205:5 - | -LL | const C: _; - | ^^^^^^^^^^- - | | - | help: provide a definition for the constant: `= <expr>;` - -error[E0403]: the name `_` is already used for a generic parameter in this item's generic parameters - --> $DIR/typeck_type_placeholder_item.rs:167:22 - | -LL | struct BadStruct1<_, _>(_); - | - ^ already used - | | - | first use of `_` - -error[E0121]: the type placeholder `_` is not allowed within types on item signatures - --> $DIR/typeck_type_placeholder_item.rs:6:14 - | -LL | fn test() -> _ { 5 } - | ^ - | | - | not allowed in type signatures - | help: replace with the correct return type: `i32` - -error[E0121]: the type placeholder `_` is not allowed within types on item signatures - --> $DIR/typeck_type_placeholder_item.rs:9:16 - | -LL | fn test2() -> (_, _) { (5, 5) } - | -^--^- - | || | - | || not allowed in type signatures - | |not allowed in type signatures - | help: replace with the correct return type: `(i32, i32)` - -error[E0121]: the type placeholder `_` is not allowed within types on item signatures - --> $DIR/typeck_type_placeholder_item.rs:12:15 - | -LL | static TEST3: _ = "test"; - | ^ - | | - | not allowed in type signatures - | help: replace `_` with the correct type: `&str` - -error[E0121]: the type placeholder `_` is not allowed within types on item signatures - --> $DIR/typeck_type_placeholder_item.rs:15:15 - | -LL | static TEST4: _ = 145; - | ^ - | | - | not allowed in type signatures - | help: replace `_` with the correct type: `i32` - -error[E0121]: the type placeholder `_` is not allowed within types on item signatures - --> $DIR/typeck_type_placeholder_item.rs:18:15 - | -LL | static TEST5: (_, _) = (1, 2); - | ^^^^^^ not allowed in type signatures - -error[E0121]: the type placeholder `_` is not allowed within types on item signatures - --> $DIR/typeck_type_placeholder_item.rs:21:13 - | -LL | fn test6(_: _) { } - | ^ not allowed in type signatures - | -help: use type parameters instead - | -LL | fn test6<T>(_: T) { } - | ^^^ ^ - -error[E0121]: the type placeholder `_` is not allowed within types on item signatures - --> $DIR/typeck_type_placeholder_item.rs:24:18 - | -LL | fn test6_b<T>(_: _, _: T) { } - | ^ not allowed in type signatures - | -help: use type parameters instead - | -LL | fn test6_b<T, U>(_: U, _: T) { } - | ^^^ ^ - -error[E0121]: the type placeholder `_` is not allowed within types on item signatures - --> $DIR/typeck_type_placeholder_item.rs:27:30 - | -LL | fn test6_c<T, K, L, A, B>(_: _, _: (T, K, L, A, B)) { } - | ^ not allowed in type signatures - | -help: use type parameters instead - | -LL | fn test6_c<T, K, L, A, B, U>(_: U, _: (T, K, L, A, B)) { } - | ^^^ ^ - -error[E0121]: the type placeholder `_` is not allowed within types on item signatures - --> $DIR/typeck_type_placeholder_item.rs:30:13 - | -LL | fn test7(x: _) { let _x: usize = x; } - | ^ not allowed in type signatures - | -help: use type parameters instead - | -LL | fn test7<T>(x: T) { let _x: usize = x; } - | ^^^ ^ - -error[E0121]: the type placeholder `_` is not allowed within types on item signatures - --> $DIR/typeck_type_placeholder_item.rs:33:22 - | -LL | fn test8(_f: fn() -> _) { } - | ^ - | | - | not allowed in type signatures - | help: use type parameters instead: `T` - -error[E0121]: the type placeholder `_` is not allowed within types on item signatures - --> $DIR/typeck_type_placeholder_item.rs:33:22 - | -LL | fn test8(_f: fn() -> _) { } - | ^ not allowed in type signatures - | -help: use type parameters instead - | -LL | fn test8<T>(_f: fn() -> T) { } - | ^^^ ^ - -error[E0121]: the type placeholder `_` is not allowed within types on item signatures - --> $DIR/typeck_type_placeholder_item.rs:47:26 - | -LL | fn test11(x: &usize) -> &_ { - | -^ - | || - | |not allowed in type signatures - | help: replace with the correct return type: `&'static &'static usize` - -error[E0121]: the type placeholder `_` is not allowed within types on item signatures - --> $DIR/typeck_type_placeholder_item.rs:52:52 - | -LL | unsafe fn test12(x: *const usize) -> *const *const _ { - | --------------^ - | | | - | | not allowed in type signatures - | help: replace with the correct return type: `*const *const usize` - -error[E0121]: the type placeholder `_` is not allowed within types on item signatures - --> $DIR/typeck_type_placeholder_item.rs:66:8 - | -LL | a: _, - | ^ not allowed in type signatures -LL | -LL | b: (_, _), - | ^ ^ not allowed in type signatures - | | - | not allowed in type signatures - | -help: use type parameters instead - | -LL | struct Test10<T> { -LL | a: T, -LL | -LL | b: (T, T), - | - -error: missing type for `static` item - --> $DIR/typeck_type_placeholder_item.rs:72:12 - | -LL | static A = 42; - | ^ help: provide a type for the item: `A: i32` - -error[E0121]: the type placeholder `_` is not allowed within types on item signatures - --> $DIR/typeck_type_placeholder_item.rs:74:15 - | -LL | static B: _ = 42; - | ^ - | | - | not allowed in type signatures - | help: replace `_` with the correct type: `i32` - -error[E0121]: the type placeholder `_` is not allowed within types on item signatures - --> $DIR/typeck_type_placeholder_item.rs:76:15 - | -LL | static C: Option<_> = Some(42); - | ^^^^^^^^^ not allowed in type signatures - -error[E0121]: the type placeholder `_` is not allowed within types on item signatures - --> $DIR/typeck_type_placeholder_item.rs:79:21 - | -LL | fn fn_test() -> _ { 5 } - | ^ - | | - | not allowed in type signatures - | help: replace with the correct return type: `i32` - -error[E0121]: the type placeholder `_` is not allowed within types on item signatures - --> $DIR/typeck_type_placeholder_item.rs:82:23 - | -LL | fn fn_test2() -> (_, _) { (5, 5) } - | -^--^- - | || | - | || not allowed in type signatures - | |not allowed in type signatures - | help: replace with the correct return type: `(i32, i32)` - -error[E0121]: the type placeholder `_` is not allowed within types on item signatures - --> $DIR/typeck_type_placeholder_item.rs:85:22 - | -LL | static FN_TEST3: _ = "test"; - | ^ - | | - | not allowed in type signatures - | help: replace `_` with the correct type: `&str` - -error[E0121]: the type placeholder `_` is not allowed within types on item signatures - --> $DIR/typeck_type_placeholder_item.rs:88:22 - | -LL | static FN_TEST4: _ = 145; - | ^ - | | - | not allowed in type signatures - | help: replace `_` with the correct type: `i32` - -error[E0121]: the type placeholder `_` is not allowed within types on item signatures - --> $DIR/typeck_type_placeholder_item.rs:91:22 - | -LL | static FN_TEST5: (_, _) = (1, 2); - | ^^^^^^ not allowed in type signatures - -error[E0121]: the type placeholder `_` is not allowed within types on item signatures - --> $DIR/typeck_type_placeholder_item.rs:94:20 - | -LL | fn fn_test6(_: _) { } - | ^ not allowed in type signatures - | -help: use type parameters instead - | -LL | fn fn_test6<T>(_: T) { } - | ^^^ ^ - -error[E0121]: the type placeholder `_` is not allowed within types on item signatures - --> $DIR/typeck_type_placeholder_item.rs:97:20 - | -LL | fn fn_test7(x: _) { let _x: usize = x; } - | ^ not allowed in type signatures - | -help: use type parameters instead - | -LL | fn fn_test7<T>(x: T) { let _x: usize = x; } - | ^^^ ^ - -error[E0121]: the type placeholder `_` is not allowed within types on item signatures - --> $DIR/typeck_type_placeholder_item.rs:100:29 - | -LL | fn fn_test8(_f: fn() -> _) { } - | ^ - | | - | not allowed in type signatures - | help: use type parameters instead: `T` - -error[E0121]: the type placeholder `_` is not allowed within types on item signatures - --> $DIR/typeck_type_placeholder_item.rs:100:29 - | -LL | fn fn_test8(_f: fn() -> _) { } - | ^ not allowed in type signatures - | -help: use type parameters instead - | -LL | fn fn_test8<T>(_f: fn() -> T) { } - | ^^^ ^ - -error[E0121]: the type placeholder `_` is not allowed within types on item signatures - --> $DIR/typeck_type_placeholder_item.rs:123:12 - | -LL | a: _, - | ^ not allowed in type signatures -LL | -LL | b: (_, _), - | ^ ^ not allowed in type signatures - | | - | not allowed in type signatures - | -help: use type parameters instead - | -LL | struct FnTest10<T> { -LL | a: T, -LL | -LL | b: (T, T), - | - -error[E0282]: type annotations needed - --> $DIR/typeck_type_placeholder_item.rs:128:18 - | -LL | fn fn_test11(_: _) -> (_, _) { panic!() } - | ^ cannot infer type - -error[E0121]: the type placeholder `_` is not allowed within types on item signatures - --> $DIR/typeck_type_placeholder_item.rs:128:28 - | -LL | fn fn_test11(_: _) -> (_, _) { panic!() } - | ^ ^ not allowed in type signatures - | | - | not allowed in type signatures - -error[E0121]: the type placeholder `_` is not allowed within types on item signatures - --> $DIR/typeck_type_placeholder_item.rs:132:30 - | -LL | fn fn_test12(x: i32) -> (_, _) { (x, x) } - | -^--^- - | || | - | || not allowed in type signatures - | |not allowed in type signatures - | help: replace with the correct return type: `(i32, i32)` - -error[E0121]: the type placeholder `_` is not allowed within types on item signatures - --> $DIR/typeck_type_placeholder_item.rs:135:33 - | -LL | fn fn_test13(x: _) -> (i32, _) { (x, x) } - | ------^- - | | | - | | not allowed in type signatures - | help: replace with the correct return type: `(i32, i32)` - -error[E0121]: the type placeholder `_` is not allowed within types on item signatures - --> $DIR/typeck_type_placeholder_item.rs:154:21 - | -LL | struct BadStruct<_>(_); - | ^ not allowed in type signatures - | -help: use type parameters instead - | -LL | struct BadStruct<T>(T); - | ^ ^ - -error[E0121]: the type placeholder `_` is not allowed within types on item signatures - --> $DIR/typeck_type_placeholder_item.rs:159:15 - | -LL | impl BadTrait<_> for BadStruct<_> {} - | ^ ^ not allowed in type signatures - | | - | not allowed in type signatures - | -help: use type parameters instead - | -LL | impl<T> BadTrait<T> for BadStruct<T> {} - | ^^^ ^ ^ - -error[E0121]: the type placeholder `_` is not allowed within types on item signatures - --> $DIR/typeck_type_placeholder_item.rs:162:34 - | -LL | fn impl_trait() -> impl BadTrait<_> { - | ^ not allowed in type signatures - -error[E0121]: the type placeholder `_` is not allowed within types on item signatures - --> $DIR/typeck_type_placeholder_item.rs:167:25 - | -LL | struct BadStruct1<_, _>(_); - | ^ not allowed in type signatures - | -help: use type parameters instead - | -LL | struct BadStruct1<T, _>(T); - | ^ ^ - -error[E0121]: the type placeholder `_` is not allowed within types on item signatures - --> $DIR/typeck_type_placeholder_item.rs:172:25 - | -LL | struct BadStruct2<_, T>(_, T); - | ^ not allowed in type signatures - | -help: use type parameters instead - | -LL | struct BadStruct2<U, T>(U, T); - | ^ ^ - -error[E0121]: the type placeholder `_` is not allowed within types on item signatures - --> $DIR/typeck_type_placeholder_item.rs:176:14 - | -LL | type X = Box<_>; - | ^ not allowed in type signatures - -error[E0121]: the type placeholder `_` is not allowed within types on item signatures - --> $DIR/typeck_type_placeholder_item.rs:182:21 - | -LL | type Y = impl Trait<_>; - | ^ not allowed in type signatures - -error[E0121]: the type placeholder `_` is not allowed within types on item signatures - --> $DIR/typeck_type_placeholder_item.rs:216:31 - | -LL | fn value() -> Option<&'static _> { - | ----------------^- - | | | - | | not allowed in type signatures - | help: replace with the correct return type: `Option<&'static u8>` - -error[E0121]: the type placeholder `_` is not allowed within types on item signatures - --> $DIR/typeck_type_placeholder_item.rs:221:10 - | -LL | const _: Option<_> = map(value); - | ^^^^^^^^^ - | | - | not allowed in type signatures - | help: replace `_` with the correct type: `Option<u8>` - -error[E0121]: the type placeholder `_` is not allowed within types on item signatures - --> $DIR/typeck_type_placeholder_item.rs:140:31 - | -LL | fn method_test1(&self, x: _); - | ^ not allowed in type signatures - | -help: use type parameters instead - | -LL | fn method_test1<T>(&self, x: T); - | ^^^ ^ - -error[E0121]: the type placeholder `_` is not allowed within types on item signatures - --> $DIR/typeck_type_placeholder_item.rs:142:31 - | -LL | fn method_test2(&self, x: _) -> _; - | ^ ^ not allowed in type signatures - | | - | not allowed in type signatures - | -help: use type parameters instead - | -LL | fn method_test2<T>(&self, x: T) -> T; - | ^^^ ^ ^ - -error[E0121]: the type placeholder `_` is not allowed within types on item signatures - --> $DIR/typeck_type_placeholder_item.rs:144:31 - | -LL | fn method_test3(&self) -> _; - | ^ not allowed in type signatures - | -help: use type parameters instead - | -LL | fn method_test3<T>(&self) -> T; - | ^^^ ^ - -error[E0121]: the type placeholder `_` is not allowed within types on item signatures - --> $DIR/typeck_type_placeholder_item.rs:146:26 - | -LL | fn assoc_fn_test1(x: _); - | ^ not allowed in type signatures - | -help: use type parameters instead - | -LL | fn assoc_fn_test1<T>(x: T); - | ^^^ ^ - -error[E0121]: the type placeholder `_` is not allowed within types on item signatures - --> $DIR/typeck_type_placeholder_item.rs:148:26 - | -LL | fn assoc_fn_test2(x: _) -> _; - | ^ ^ not allowed in type signatures - | | - | not allowed in type signatures - | -help: use type parameters instead - | -LL | fn assoc_fn_test2<T>(x: T) -> T; - | ^^^ ^ ^ - -error[E0121]: the type placeholder `_` is not allowed within types on item signatures - --> $DIR/typeck_type_placeholder_item.rs:150:28 - | -LL | fn assoc_fn_test3() -> _; - | ^ not allowed in type signatures - | -help: use type parameters instead - | -LL | fn assoc_fn_test3<T>() -> T; - | ^^^ ^ - -error[E0121]: the type placeholder `_` is not allowed within types on item signatures - --> $DIR/typeck_type_placeholder_item.rs:190:14 - | -LL | type B = _; - | ^ not allowed in type signatures - -error[E0121]: the type placeholder `_` is not allowed within types on item signatures - --> $DIR/typeck_type_placeholder_item.rs:192:14 - | -LL | const C: _; - | ^ not allowed in type signatures - -error[E0121]: the type placeholder `_` is not allowed within types on item signatures - --> $DIR/typeck_type_placeholder_item.rs:194:14 - | -LL | const D: _ = 42; - | ^ - | | - | not allowed in type signatures - | help: replace `_` with the correct type: `i32` - -error[E0121]: the type placeholder `_` is not allowed within types on item signatures - --> $DIR/typeck_type_placeholder_item.rs:197:26 - | -LL | type F: std::ops::Fn(_); - | ^ not allowed in type signatures - -error[E0121]: the type placeholder `_` is not allowed within types on item signatures - --> $DIR/typeck_type_placeholder_item.rs:40:24 - | -LL | fn test9(&self) -> _ { () } - | ^ - | | - | not allowed in type signatures - | help: replace with the correct return type: `()` - -error[E0121]: the type placeholder `_` is not allowed within types on item signatures - --> $DIR/typeck_type_placeholder_item.rs:43:27 - | -LL | fn test10(&self, _x : _) { } - | ^ not allowed in type signatures - | -help: use type parameters instead - | -LL | fn test10<T>(&self, _x : T) { } - | ^^^ ^ - -error[E0121]: the type placeholder `_` is not allowed within types on item signatures - --> $DIR/typeck_type_placeholder_item.rs:58:24 - | -LL | fn clone(&self) -> _ { Test9 } - | ^ - | | - | not allowed in type signatures - | help: replace with the correct return type: `Test9` - -error[E0121]: the type placeholder `_` is not allowed within types on item signatures - --> $DIR/typeck_type_placeholder_item.rs:61:37 - | -LL | fn clone_from(&mut self, other: _) { *self = Test9; } - | ^ not allowed in type signatures - | -help: use type parameters instead - | -LL | fn clone_from<T>(&mut self, other: T) { *self = Test9; } - | ^^^ ^ - -error[E0121]: the type placeholder `_` is not allowed within types on item signatures - --> $DIR/typeck_type_placeholder_item.rs:107:31 - | -LL | fn fn_test9(&self) -> _ { () } - | ^ - | | - | not allowed in type signatures - | help: replace with the correct return type: `()` - -error[E0121]: the type placeholder `_` is not allowed within types on item signatures - --> $DIR/typeck_type_placeholder_item.rs:110:34 - | -LL | fn fn_test10(&self, _x : _) { } - | ^ not allowed in type signatures - | -help: use type parameters instead - | -LL | fn fn_test10<T>(&self, _x : T) { } - | ^^^ ^ - -error[E0121]: the type placeholder `_` is not allowed within types on item signatures - --> $DIR/typeck_type_placeholder_item.rs:115:28 - | -LL | fn clone(&self) -> _ { FnTest9 } - | ^ - | | - | not allowed in type signatures - | help: replace with the correct return type: `FnTest9` - -error[E0121]: the type placeholder `_` is not allowed within types on item signatures - --> $DIR/typeck_type_placeholder_item.rs:118:41 - | -LL | fn clone_from(&mut self, other: _) { *self = FnTest9; } - | ^ not allowed in type signatures - | -help: use type parameters instead - | -LL | fn clone_from<T>(&mut self, other: T) { *self = FnTest9; } - | ^^^ ^ - -error[E0121]: the type placeholder `_` is not allowed within types on item signatures - --> $DIR/typeck_type_placeholder_item.rs:201:14 - | -LL | type A = _; - | ^ not allowed in type signatures - -error[E0121]: the type placeholder `_` is not allowed within types on item signatures - --> $DIR/typeck_type_placeholder_item.rs:203:14 - | -LL | type B = _; - | ^ not allowed in type signatures - -error[E0121]: the type placeholder `_` is not allowed within types on item signatures - --> $DIR/typeck_type_placeholder_item.rs:205:14 - | -LL | const C: _; - | ^ not allowed in type signatures - -error[E0121]: the type placeholder `_` is not allowed within types on item signatures - --> $DIR/typeck_type_placeholder_item.rs:208:14 - | -LL | const D: _ = 42; - | ^ - | | - | not allowed in type signatures - | help: replace `_` with the correct type: `i32` - -error: aborting due to 69 previous errors - -Some errors have detailed explanations: E0121, E0282, E0403. -For more information about an error, try `rustc --explain E0121`. diff --git a/src/test/ui/issues/issue-18661.rs b/src/test/ui/unboxed-closures/issue-18661.rs similarity index 100% rename from src/test/ui/issues/issue-18661.rs rename to src/test/ui/unboxed-closures/issue-18661.rs diff --git a/src/test/ui/underscore-imports/hygiene-2.rs b/src/test/ui/underscore-imports/hygiene-2.rs index bea61eae6b51a..510d91d0d4624 100644 --- a/src/test/ui/underscore-imports/hygiene-2.rs +++ b/src/test/ui/underscore-imports/hygiene-2.rs @@ -29,5 +29,6 @@ m!(y); fn main() { use crate::y::*; + #[allow(noop_method_call)] (&()).deref(); } diff --git a/src/test/ui/union/union-deref.rs b/src/test/ui/union/union-deref.rs index df598eea9ef0f..48f5b36bd17f3 100644 --- a/src/test/ui/union/union-deref.rs +++ b/src/test/ui/union/union-deref.rs @@ -1,4 +1,3 @@ -// ignore-tidy-linelength //! Test the part of RFC 2514 that is about not applying `DerefMut` coercions //! of union fields. #![feature(untagged_unions)] diff --git a/src/test/ui/union/union-deref.stderr b/src/test/ui/union/union-deref.stderr index f7722764cd3f0..6af050bc4b79d 100644 --- a/src/test/ui/union/union-deref.stderr +++ b/src/test/ui/union/union-deref.stderr @@ -1,5 +1,5 @@ error: not automatically applying `DerefMut` on `ManuallyDrop` union field - --> $DIR/union-deref.rs:15:14 + --> $DIR/union-deref.rs:14:14 | LL | unsafe { u.f.0 = Vec::new() }; | ^^^ @@ -8,7 +8,7 @@ LL | unsafe { u.f.0 = Vec::new() }; = help: add an explicit `*` if that is desired, or call `ptr::write` to not run the destructor error: not automatically applying `DerefMut` on `ManuallyDrop` union field - --> $DIR/union-deref.rs:17:19 + --> $DIR/union-deref.rs:16:19 | LL | unsafe { &mut u.f.0 }; | ^^^ @@ -17,7 +17,7 @@ LL | unsafe { &mut u.f.0 }; = help: add an explicit `*` if that is desired, or call `ptr::write` to not run the destructor error: not automatically applying `DerefMut` on `ManuallyDrop` union field - --> $DIR/union-deref.rs:19:14 + --> $DIR/union-deref.rs:18:14 | LL | unsafe { u.f.0.push(0) }; | ^^^ @@ -26,7 +26,7 @@ LL | unsafe { u.f.0.push(0) }; = help: add an explicit `*` if that is desired, or call `ptr::write` to not run the destructor error: not automatically applying `DerefMut` on `ManuallyDrop` union field - --> $DIR/union-deref.rs:23:14 + --> $DIR/union-deref.rs:22:14 | LL | unsafe { u.f.0.0 = Vec::new() }; | ^^^^^ @@ -35,7 +35,7 @@ LL | unsafe { u.f.0.0 = Vec::new() }; = help: add an explicit `*` if that is desired, or call `ptr::write` to not run the destructor error: not automatically applying `DerefMut` on `ManuallyDrop` union field - --> $DIR/union-deref.rs:25:19 + --> $DIR/union-deref.rs:24:19 | LL | unsafe { &mut u.f.0.0 }; | ^^^^^ @@ -44,7 +44,7 @@ LL | unsafe { &mut u.f.0.0 }; = help: add an explicit `*` if that is desired, or call `ptr::write` to not run the destructor error: not automatically applying `DerefMut` on `ManuallyDrop` union field - --> $DIR/union-deref.rs:27:14 + --> $DIR/union-deref.rs:26:14 | LL | unsafe { u.f.0.0.push(0) }; | ^^^^^ diff --git a/src/test/ui/union/union-derive-clone.stderr b/src/test/ui/union/union-derive-clone.stderr index a793bf58d23e2..546394664dfe8 100644 --- a/src/test/ui/union/union-derive-clone.stderr +++ b/src/test/ui/union/union-derive-clone.stderr @@ -25,14 +25,6 @@ LL | struct CloneNoCopy; ... LL | let w = u.clone(); | ^^^^^ method cannot be called on `U5<CloneNoCopy>` due to unsatisfied trait bounds - | - ::: $SRC_DIR/core/src/clone.rs:LL:COL - | -LL | fn clone(&self) -> Self; - | ----- - | | - | the method is available for `Arc<U5<CloneNoCopy>>` here - | the method is available for `Rc<U5<CloneNoCopy>>` here | = note: the following trait bounds were not satisfied: `CloneNoCopy: Copy` diff --git a/src/test/ui/union/union-derive.stderr b/src/test/ui/union/union-derive.stderr index 919c6d5ceda65..6ef72c901a533 100644 --- a/src/test/ui/union/union-derive.stderr +++ b/src/test/ui/union/union-derive.stderr @@ -1,20 +1,14 @@ error: this trait cannot be derived for unions - --> $DIR/union-derive.rs:9:5 - | -LL | Debug, - | ^^^^^ - -error: this trait cannot be derived for unions - --> $DIR/union-derive.rs:8:5 + --> $DIR/union-derive.rs:4:5 | -LL | Default, - | ^^^^^^^ +LL | PartialEq, + | ^^^^^^^^^ error: this trait cannot be derived for unions - --> $DIR/union-derive.rs:7:5 + --> $DIR/union-derive.rs:5:5 | -LL | Hash, - | ^^^^ +LL | PartialOrd, + | ^^^^^^^^^^ error: this trait cannot be derived for unions --> $DIR/union-derive.rs:6:5 @@ -23,16 +17,22 @@ LL | Ord, | ^^^ error: this trait cannot be derived for unions - --> $DIR/union-derive.rs:5:5 + --> $DIR/union-derive.rs:7:5 | -LL | PartialOrd, - | ^^^^^^^^^^ +LL | Hash, + | ^^^^ error: this trait cannot be derived for unions - --> $DIR/union-derive.rs:4:5 + --> $DIR/union-derive.rs:8:5 | -LL | PartialEq, - | ^^^^^^^^^ +LL | Default, + | ^^^^^^^ + +error: this trait cannot be derived for unions + --> $DIR/union-derive.rs:9:5 + | +LL | Debug, + | ^^^^^ error: aborting due to 6 previous errors diff --git a/src/test/ui/union/union-sized-field.stderr b/src/test/ui/union/union-sized-field.stderr index cebeeb59a7829..b916bbe8ad10a 100644 --- a/src/test/ui/union/union-sized-field.stderr +++ b/src/test/ui/union/union-sized-field.stderr @@ -2,7 +2,7 @@ error[E0277]: the size for values of type `T` cannot be known at compilation tim --> $DIR/union-sized-field.rs:4:12 | LL | union Foo<T: ?Sized> { - | - this type parameter needs to be `Sized` + | - this type parameter needs to be `std::marker::Sized` LL | value: T, | ^ doesn't have a size known at compile-time | @@ -21,7 +21,7 @@ error[E0277]: the size for values of type `T` cannot be known at compilation tim --> $DIR/union-sized-field.rs:9:12 | LL | struct Foo2<T: ?Sized> { - | - this type parameter needs to be `Sized` + | - this type parameter needs to be `std::marker::Sized` LL | value: T, | ^ doesn't have a size known at compile-time | @@ -40,7 +40,7 @@ error[E0277]: the size for values of type `T` cannot be known at compilation tim --> $DIR/union-sized-field.rs:15:11 | LL | enum Foo3<T: ?Sized> { - | - this type parameter needs to be `Sized` + | - this type parameter needs to be `std::marker::Sized` LL | Value(T), | ^ doesn't have a size known at compile-time | diff --git a/src/test/ui/unique-object-noncopyable.stderr b/src/test/ui/unique-object-noncopyable.stderr index 4bbacfc0a8b60..6a355dd256286 100644 --- a/src/test/ui/unique-object-noncopyable.stderr +++ b/src/test/ui/unique-object-noncopyable.stderr @@ -10,14 +10,6 @@ LL | trait Foo { LL | let _z = y.clone(); | ^^^^^ method cannot be called on `Box<dyn Foo>` due to unsatisfied trait bounds | - ::: $SRC_DIR/core/src/clone.rs:LL:COL - | -LL | fn clone(&self) -> Self; - | ----- - | | - | the method is available for `Arc<Box<dyn Foo>>` here - | the method is available for `Rc<Box<dyn Foo>>` here - | ::: $SRC_DIR/alloc/src/boxed.rs:LL:COL | LL | / pub struct Box< diff --git a/src/test/ui/unique-pinned-nocopy.stderr b/src/test/ui/unique-pinned-nocopy.stderr index dd0b7fc5489ce..a4421bcf8097e 100644 --- a/src/test/ui/unique-pinned-nocopy.stderr +++ b/src/test/ui/unique-pinned-nocopy.stderr @@ -7,14 +7,6 @@ LL | struct R { LL | let _j = i.clone(); | ^^^^^ method cannot be called on `Box<R>` due to unsatisfied trait bounds | - ::: $SRC_DIR/core/src/clone.rs:LL:COL - | -LL | fn clone(&self) -> Self; - | ----- - | | - | the method is available for `Arc<Box<R>>` here - | the method is available for `Rc<Box<R>>` here - | ::: $SRC_DIR/alloc/src/boxed.rs:LL:COL | LL | / pub struct Box< diff --git a/src/test/ui/unknown-lint-tool-name.rs b/src/test/ui/unknown-lint-tool-name.rs index 182aec34b4781..84ab7c1944ab2 100644 --- a/src/test/ui/unknown-lint-tool-name.rs +++ b/src/test/ui/unknown-lint-tool-name.rs @@ -1,8 +1,8 @@ -#![deny(foo::bar)] //~ ERROR an unknown tool name found in scoped lint: `foo::bar` - //~| ERROR an unknown tool name found in scoped lint: `foo::bar` - //~| ERROR an unknown tool name found in scoped lint: `foo::bar` +#![deny(foo::bar)] //~ ERROR unknown tool name `foo` found in scoped lint: `foo::bar` + //~| ERROR unknown tool name `foo` found in scoped lint: `foo::bar` + //~| ERROR unknown tool name `foo` found in scoped lint: `foo::bar` -#[allow(foo::bar)] //~ ERROR an unknown tool name found in scoped lint: `foo::bar` - //~| ERROR an unknown tool name found in scoped lint: `foo::bar` - //~| ERROR an unknown tool name found in scoped lint: `foo::bar` +#[allow(foo::bar)] //~ ERROR unknown tool name `foo` found in scoped lint: `foo::bar` + //~| ERROR unknown tool name `foo` found in scoped lint: `foo::bar` + //~| ERROR unknown tool name `foo` found in scoped lint: `foo::bar` fn main() {} diff --git a/src/test/ui/unknown-lint-tool-name.stderr b/src/test/ui/unknown-lint-tool-name.stderr index 414816d229cdb..1d145515abf66 100644 --- a/src/test/ui/unknown-lint-tool-name.stderr +++ b/src/test/ui/unknown-lint-tool-name.stderr @@ -1,38 +1,50 @@ -error[E0710]: an unknown tool name found in scoped lint: `foo::bar` +error[E0710]: unknown tool name `foo` found in scoped lint: `foo::bar` --> $DIR/unknown-lint-tool-name.rs:1:9 | LL | #![deny(foo::bar)] | ^^^ + | + = help: add `#![register_tool(foo)]` to the crate root -error[E0710]: an unknown tool name found in scoped lint: `foo::bar` +error[E0710]: unknown tool name `foo` found in scoped lint: `foo::bar` --> $DIR/unknown-lint-tool-name.rs:5:9 | LL | #[allow(foo::bar)] | ^^^ + | + = help: add `#![register_tool(foo)]` to the crate root -error[E0710]: an unknown tool name found in scoped lint: `foo::bar` +error[E0710]: unknown tool name `foo` found in scoped lint: `foo::bar` --> $DIR/unknown-lint-tool-name.rs:1:9 | LL | #![deny(foo::bar)] | ^^^ + | + = help: add `#![register_tool(foo)]` to the crate root -error[E0710]: an unknown tool name found in scoped lint: `foo::bar` +error[E0710]: unknown tool name `foo` found in scoped lint: `foo::bar` --> $DIR/unknown-lint-tool-name.rs:5:9 | LL | #[allow(foo::bar)] | ^^^ + | + = help: add `#![register_tool(foo)]` to the crate root -error[E0710]: an unknown tool name found in scoped lint: `foo::bar` +error[E0710]: unknown tool name `foo` found in scoped lint: `foo::bar` --> $DIR/unknown-lint-tool-name.rs:1:9 | LL | #![deny(foo::bar)] | ^^^ + | + = help: add `#![register_tool(foo)]` to the crate root -error[E0710]: an unknown tool name found in scoped lint: `foo::bar` +error[E0710]: unknown tool name `foo` found in scoped lint: `foo::bar` --> $DIR/unknown-lint-tool-name.rs:5:9 | LL | #[allow(foo::bar)] | ^^^ + | + = help: add `#![register_tool(foo)]` to the crate root error: aborting due to 6 previous errors diff --git a/src/test/ui/unsafe/rfc-2585-safe_packed_borrows-in-unsafe-fn.rs b/src/test/ui/unsafe/rfc-2585-safe_packed_borrows-in-unsafe-fn.rs deleted file mode 100644 index 540612a7dce05..0000000000000 --- a/src/test/ui/unsafe/rfc-2585-safe_packed_borrows-in-unsafe-fn.rs +++ /dev/null @@ -1,67 +0,0 @@ -#![feature(unsafe_block_in_unsafe_fn)] - -#[repr(packed)] -pub struct Packed { - data: &'static u32, -} - -const PACKED: Packed = Packed { data: &0 }; - -#[allow(safe_packed_borrows)] -#[allow(unsafe_op_in_unsafe_fn)] -unsafe fn allow_allow() { - &PACKED.data; // allowed -} - -#[allow(safe_packed_borrows)] -#[warn(unsafe_op_in_unsafe_fn)] -unsafe fn allow_warn() { - &PACKED.data; // allowed -} - -#[allow(safe_packed_borrows)] -#[deny(unsafe_op_in_unsafe_fn)] -unsafe fn allow_deny() { - &PACKED.data; // allowed -} - -#[warn(safe_packed_borrows)] -#[allow(unsafe_op_in_unsafe_fn)] -unsafe fn warn_allow() { - &PACKED.data; // allowed -} - -#[warn(safe_packed_borrows)] -#[warn(unsafe_op_in_unsafe_fn)] -unsafe fn warn_warn() { - &PACKED.data; //~ WARN - //~| WARNING this was previously accepted by the compiler but is being phased out -} - -#[warn(safe_packed_borrows)] -#[deny(unsafe_op_in_unsafe_fn)] -unsafe fn warn_deny() { - &PACKED.data; //~ WARN - //~| WARNING this was previously accepted by the compiler but is being phased out -} - -#[deny(safe_packed_borrows)] -#[allow(unsafe_op_in_unsafe_fn)] -unsafe fn deny_allow() { - &PACKED.data; // allowed -} - -#[deny(safe_packed_borrows)] -#[warn(unsafe_op_in_unsafe_fn)] -unsafe fn deny_warn() { - &PACKED.data; //~ WARN -} - -#[deny(safe_packed_borrows)] -#[deny(unsafe_op_in_unsafe_fn)] -unsafe fn deny_deny() { - &PACKED.data; //~ ERROR - //~| WARNING this was previously accepted by the compiler but is being phased out -} - -fn main() {} diff --git a/src/test/ui/unsafe/rfc-2585-safe_packed_borrows-in-unsafe-fn.stderr b/src/test/ui/unsafe/rfc-2585-safe_packed_borrows-in-unsafe-fn.stderr deleted file mode 100644 index fda15159643b6..0000000000000 --- a/src/test/ui/unsafe/rfc-2585-safe_packed_borrows-in-unsafe-fn.stderr +++ /dev/null @@ -1,60 +0,0 @@ -warning: borrow of packed field is unsafe and requires unsafe block (error E0133) - --> $DIR/rfc-2585-safe_packed_borrows-in-unsafe-fn.rs:37:5 - | -LL | &PACKED.data; - | ^^^^^^^^^^^^ borrow of packed field - | -note: the lint level is defined here - --> $DIR/rfc-2585-safe_packed_borrows-in-unsafe-fn.rs:34:8 - | -LL | #[warn(safe_packed_borrows)] - | ^^^^^^^^^^^^^^^^^^^ - = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = note: for more information, see issue #46043 <https://github.com/rust-lang/rust/issues/46043> - = note: fields of packed structs might be misaligned: dereferencing a misaligned pointer or even just creating a misaligned reference is undefined behavior - -warning: borrow of packed field is unsafe and requires unsafe block (error E0133) - --> $DIR/rfc-2585-safe_packed_borrows-in-unsafe-fn.rs:44:5 - | -LL | &PACKED.data; - | ^^^^^^^^^^^^ borrow of packed field - | -note: the lint level is defined here - --> $DIR/rfc-2585-safe_packed_borrows-in-unsafe-fn.rs:41:8 - | -LL | #[warn(safe_packed_borrows)] - | ^^^^^^^^^^^^^^^^^^^ - = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = note: for more information, see issue #46043 <https://github.com/rust-lang/rust/issues/46043> - = note: fields of packed structs might be misaligned: dereferencing a misaligned pointer or even just creating a misaligned reference is undefined behavior - -warning: borrow of packed field is unsafe and requires unsafe block (error E0133) - --> $DIR/rfc-2585-safe_packed_borrows-in-unsafe-fn.rs:57:5 - | -LL | &PACKED.data; - | ^^^^^^^^^^^^ borrow of packed field - | -note: the lint level is defined here - --> $DIR/rfc-2585-safe_packed_borrows-in-unsafe-fn.rs:55:8 - | -LL | #[warn(unsafe_op_in_unsafe_fn)] - | ^^^^^^^^^^^^^^^^^^^^^^ - = note: fields of packed structs might be misaligned: dereferencing a misaligned pointer or even just creating a misaligned reference is undefined behavior - -error: borrow of packed field is unsafe and requires unsafe block (error E0133) - --> $DIR/rfc-2585-safe_packed_borrows-in-unsafe-fn.rs:63:5 - | -LL | &PACKED.data; - | ^^^^^^^^^^^^ borrow of packed field - | -note: the lint level is defined here - --> $DIR/rfc-2585-safe_packed_borrows-in-unsafe-fn.rs:60:8 - | -LL | #[deny(safe_packed_borrows)] - | ^^^^^^^^^^^^^^^^^^^ - = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = note: for more information, see issue #46043 <https://github.com/rust-lang/rust/issues/46043> - = note: fields of packed structs might be misaligned: dereferencing a misaligned pointer or even just creating a misaligned reference is undefined behavior - -error: aborting due to previous error; 3 warnings emitted - diff --git a/src/test/ui/unsafe/rfc-2585-unsafe_op_in_unsafe_fn.rs b/src/test/ui/unsafe/rfc-2585-unsafe_op_in_unsafe_fn.rs index 9eec7e0e8fe62..c8400a6fc4d0d 100644 --- a/src/test/ui/unsafe/rfc-2585-unsafe_op_in_unsafe_fn.rs +++ b/src/test/ui/unsafe/rfc-2585-unsafe_op_in_unsafe_fn.rs @@ -1,4 +1,3 @@ -#![feature(unsafe_block_in_unsafe_fn)] #![deny(unsafe_op_in_unsafe_fn)] #![deny(unused_unsafe)] diff --git a/src/test/ui/unsafe/rfc-2585-unsafe_op_in_unsafe_fn.stderr b/src/test/ui/unsafe/rfc-2585-unsafe_op_in_unsafe_fn.stderr index 278a036c9f19f..3157783acb6af 100644 --- a/src/test/ui/unsafe/rfc-2585-unsafe_op_in_unsafe_fn.stderr +++ b/src/test/ui/unsafe/rfc-2585-unsafe_op_in_unsafe_fn.stderr @@ -1,18 +1,18 @@ error: call to unsafe function is unsafe and requires unsafe block (error E0133) - --> $DIR/rfc-2585-unsafe_op_in_unsafe_fn.rs:10:5 + --> $DIR/rfc-2585-unsafe_op_in_unsafe_fn.rs:9:5 | LL | unsf(); | ^^^^^^ call to unsafe function | note: the lint level is defined here - --> $DIR/rfc-2585-unsafe_op_in_unsafe_fn.rs:2:9 + --> $DIR/rfc-2585-unsafe_op_in_unsafe_fn.rs:1:9 | LL | #![deny(unsafe_op_in_unsafe_fn)] | ^^^^^^^^^^^^^^^^^^^^^^ = note: consult the function's documentation for information on how to avoid undefined behavior error: dereference of raw pointer is unsafe and requires unsafe block (error E0133) - --> $DIR/rfc-2585-unsafe_op_in_unsafe_fn.rs:12:5 + --> $DIR/rfc-2585-unsafe_op_in_unsafe_fn.rs:11:5 | LL | *PTR; | ^^^^ dereference of raw pointer @@ -20,7 +20,7 @@ LL | *PTR; = note: raw pointers may be NULL, dangling or unaligned; they can violate aliasing rules and cause data races: all of these are undefined behavior error: use of mutable static is unsafe and requires unsafe block (error E0133) - --> $DIR/rfc-2585-unsafe_op_in_unsafe_fn.rs:14:5 + --> $DIR/rfc-2585-unsafe_op_in_unsafe_fn.rs:13:5 | LL | VOID = (); | ^^^^^^^^^ use of mutable static @@ -28,25 +28,25 @@ LL | VOID = (); = note: mutable statics can be mutated by multiple threads: aliasing violations or data races will cause undefined behavior error: unnecessary `unsafe` block - --> $DIR/rfc-2585-unsafe_op_in_unsafe_fn.rs:17:5 + --> $DIR/rfc-2585-unsafe_op_in_unsafe_fn.rs:16:5 | LL | unsafe {} | ^^^^^^ unnecessary `unsafe` block | note: the lint level is defined here - --> $DIR/rfc-2585-unsafe_op_in_unsafe_fn.rs:3:9 + --> $DIR/rfc-2585-unsafe_op_in_unsafe_fn.rs:2:9 | LL | #![deny(unused_unsafe)] | ^^^^^^^^^^^^^ error: call to unsafe function is unsafe and requires unsafe block (error E0133) - --> $DIR/rfc-2585-unsafe_op_in_unsafe_fn.rs:25:5 + --> $DIR/rfc-2585-unsafe_op_in_unsafe_fn.rs:24:5 | LL | unsf(); | ^^^^^^ call to unsafe function | note: the lint level is defined here - --> $DIR/rfc-2585-unsafe_op_in_unsafe_fn.rs:23:8 + --> $DIR/rfc-2585-unsafe_op_in_unsafe_fn.rs:22:8 | LL | #[deny(warnings)] | ^^^^^^^^ @@ -54,7 +54,7 @@ LL | #[deny(warnings)] = note: consult the function's documentation for information on how to avoid undefined behavior error: dereference of raw pointer is unsafe and requires unsafe block (error E0133) - --> $DIR/rfc-2585-unsafe_op_in_unsafe_fn.rs:27:5 + --> $DIR/rfc-2585-unsafe_op_in_unsafe_fn.rs:26:5 | LL | *PTR; | ^^^^ dereference of raw pointer @@ -62,7 +62,7 @@ LL | *PTR; = note: raw pointers may be NULL, dangling or unaligned; they can violate aliasing rules and cause data races: all of these are undefined behavior error: use of mutable static is unsafe and requires unsafe block (error E0133) - --> $DIR/rfc-2585-unsafe_op_in_unsafe_fn.rs:29:5 + --> $DIR/rfc-2585-unsafe_op_in_unsafe_fn.rs:28:5 | LL | VOID = (); | ^^^^^^^^^ use of mutable static @@ -70,13 +70,13 @@ LL | VOID = (); = note: mutable statics can be mutated by multiple threads: aliasing violations or data races will cause undefined behavior error: unnecessary `unsafe` block - --> $DIR/rfc-2585-unsafe_op_in_unsafe_fn.rs:31:5 + --> $DIR/rfc-2585-unsafe_op_in_unsafe_fn.rs:30:5 | LL | unsafe {} | ^^^^^^ unnecessary `unsafe` block error: unnecessary `unsafe` block - --> $DIR/rfc-2585-unsafe_op_in_unsafe_fn.rs:45:14 + --> $DIR/rfc-2585-unsafe_op_in_unsafe_fn.rs:44:14 | LL | unsafe { unsafe { unsf() } } | ------ ^^^^^^ unnecessary `unsafe` block @@ -84,7 +84,7 @@ LL | unsafe { unsafe { unsf() } } | because it's nested under this `unsafe` block error: unnecessary `unsafe` block - --> $DIR/rfc-2585-unsafe_op_in_unsafe_fn.rs:56:5 + --> $DIR/rfc-2585-unsafe_op_in_unsafe_fn.rs:55:5 | LL | unsafe fn allow_level() { | ----------------------- because it's nested under this `unsafe` fn @@ -93,7 +93,7 @@ LL | unsafe { unsf() } | ^^^^^^ unnecessary `unsafe` block error: unnecessary `unsafe` block - --> $DIR/rfc-2585-unsafe_op_in_unsafe_fn.rs:68:9 + --> $DIR/rfc-2585-unsafe_op_in_unsafe_fn.rs:67:9 | LL | unsafe fn nested_allow_level() { | ------------------------------ because it's nested under this `unsafe` fn @@ -102,7 +102,7 @@ LL | unsafe { unsf() } | ^^^^^^ unnecessary `unsafe` block error[E0133]: call to unsafe function is unsafe and requires unsafe block - --> $DIR/rfc-2585-unsafe_op_in_unsafe_fn.rs:74:5 + --> $DIR/rfc-2585-unsafe_op_in_unsafe_fn.rs:73:5 | LL | unsf(); | ^^^^^^ call to unsafe function @@ -110,7 +110,7 @@ LL | unsf(); = note: consult the function's documentation for information on how to avoid undefined behavior error[E0133]: call to unsafe function is unsafe and requires unsafe function or block - --> $DIR/rfc-2585-unsafe_op_in_unsafe_fn.rs:78:9 + --> $DIR/rfc-2585-unsafe_op_in_unsafe_fn.rs:77:9 | LL | unsf(); | ^^^^^^ call to unsafe function diff --git a/src/test/ui/unsized-locals/issue-50940-with-feature.stderr b/src/test/ui/unsized-locals/issue-50940-with-feature.stderr index 1b1a584a01ff1..4523d41b60060 100644 --- a/src/test/ui/unsized-locals/issue-50940-with-feature.stderr +++ b/src/test/ui/unsized-locals/issue-50940-with-feature.stderr @@ -14,7 +14,11 @@ LL | A as fn(str) -> A<str>; | ^ doesn't have a size known at compile-time | = help: within `A<str>`, the trait `Sized` is not implemented for `str` - = note: required because it appears within the type `A<str>` +note: required because it appears within the type `A<str>` + --> $DIR/issue-50940-with-feature.rs:5:12 + | +LL | struct A<X: ?Sized>(X); + | ^ = note: the return type of a function must have a statically known size error: aborting due to previous error; 1 warning emitted diff --git a/src/test/ui/unsized-locals/unsized-exprs.stderr b/src/test/ui/unsized-locals/unsized-exprs.stderr index 9fb401aec2cfa..a7f57e3fd1566 100644 --- a/src/test/ui/unsized-locals/unsized-exprs.stderr +++ b/src/test/ui/unsized-locals/unsized-exprs.stderr @@ -15,7 +15,11 @@ LL | udrop::<A<[u8]>>(A { 0: *foo() }); | ^^^^^^^^^^^^^^^ doesn't have a size known at compile-time | = help: within `A<[u8]>`, the trait `Sized` is not implemented for `[u8]` - = note: required because it appears within the type `A<[u8]>` +note: required because it appears within the type `A<[u8]>` + --> $DIR/unsized-exprs.rs:3:8 + | +LL | struct A<X: ?Sized>(X); + | ^ = note: structs must have a statically known size to be initialized error[E0277]: the size for values of type `[u8]` cannot be known at compilation time @@ -25,7 +29,11 @@ LL | udrop::<A<[u8]>>(A(*foo())); | ^ doesn't have a size known at compile-time | = help: within `A<[u8]>`, the trait `Sized` is not implemented for `[u8]` - = note: required because it appears within the type `A<[u8]>` +note: required because it appears within the type `A<[u8]>` + --> $DIR/unsized-exprs.rs:3:8 + | +LL | struct A<X: ?Sized>(X); + | ^ = note: the return type of a function must have a statically known size error: aborting due to 3 previous errors diff --git a/src/test/ui/unsized/return-unsized-from-trait-method.rs b/src/test/ui/unsized/return-unsized-from-trait-method.rs index 2d265d5db5c14..ebe6edd101014 100644 --- a/src/test/ui/unsized/return-unsized-from-trait-method.rs +++ b/src/test/ui/unsized/return-unsized-from-trait-method.rs @@ -1,5 +1,3 @@ -// ignore-tidy-linelength - // regression test for #26376 trait Foo { diff --git a/src/test/ui/unsized/return-unsized-from-trait-method.stderr b/src/test/ui/unsized/return-unsized-from-trait-method.stderr index 7ecdd28616674..4dd7cf5e02fc2 100644 --- a/src/test/ui/unsized/return-unsized-from-trait-method.stderr +++ b/src/test/ui/unsized/return-unsized-from-trait-method.stderr @@ -1,5 +1,5 @@ error[E0161]: cannot move a value of type [u8]: the size of [u8] cannot be statically determined - --> $DIR/return-unsized-from-trait-method.rs:11:17 + --> $DIR/return-unsized-from-trait-method.rs:9:17 | LL | let _ = f.foo(); | ^^^^^^^ diff --git a/src/test/ui/unsized/unsized-bare-typaram.stderr b/src/test/ui/unsized/unsized-bare-typaram.stderr index b2ff3159c9591..19978ae24cacb 100644 --- a/src/test/ui/unsized/unsized-bare-typaram.stderr +++ b/src/test/ui/unsized/unsized-bare-typaram.stderr @@ -6,7 +6,7 @@ LL | fn bar<T: Sized>() { } LL | fn foo<T: ?Sized>() { bar::<T>() } | - ^ doesn't have a size known at compile-time | | - | this type parameter needs to be `Sized` + | this type parameter needs to be `std::marker::Sized` error: aborting due to previous error diff --git a/src/test/ui/unsized/unsized-enum.stderr b/src/test/ui/unsized/unsized-enum.stderr index 3057d4789bd81..601db7d1cd983 100644 --- a/src/test/ui/unsized/unsized-enum.stderr +++ b/src/test/ui/unsized/unsized-enum.stderr @@ -7,7 +7,7 @@ LL | fn foo1<T>() { not_sized::<Foo<T>>() } // Hunky dory. LL | fn foo2<T: ?Sized>() { not_sized::<Foo<T>>() } | - ^^^^^^ doesn't have a size known at compile-time | | - | this type parameter needs to be `Sized` + | this type parameter needs to be `std::marker::Sized` | help: you could relax the implicit `Sized` bound on `U` if it were used through indirection like `&U` or `Box<U>` --> $DIR/unsized-enum.rs:4:10 diff --git a/src/test/ui/unsized/unsized-enum2.stderr b/src/test/ui/unsized/unsized-enum2.stderr index 0a0896638e072..1b6c85858159d 100644 --- a/src/test/ui/unsized/unsized-enum2.stderr +++ b/src/test/ui/unsized/unsized-enum2.stderr @@ -2,7 +2,7 @@ error[E0277]: the size for values of type `W` cannot be known at compilation tim --> $DIR/unsized-enum2.rs:23:8 | LL | enum E<W: ?Sized, X: ?Sized, Y: ?Sized, Z: ?Sized> { - | - this type parameter needs to be `Sized` + | - this type parameter needs to be `std::marker::Sized` LL | // parameter LL | VA(W), | ^ doesn't have a size known at compile-time @@ -22,7 +22,7 @@ error[E0277]: the size for values of type `X` cannot be known at compilation tim --> $DIR/unsized-enum2.rs:25:11 | LL | enum E<W: ?Sized, X: ?Sized, Y: ?Sized, Z: ?Sized> { - | - this type parameter needs to be `Sized` + | - this type parameter needs to be `std::marker::Sized` ... LL | VB{x: X}, | ^ doesn't have a size known at compile-time @@ -42,7 +42,7 @@ error[E0277]: the size for values of type `Y` cannot be known at compilation tim --> $DIR/unsized-enum2.rs:27:15 | LL | enum E<W: ?Sized, X: ?Sized, Y: ?Sized, Z: ?Sized> { - | - this type parameter needs to be `Sized` + | - this type parameter needs to be `std::marker::Sized` ... LL | VC(isize, Y), | ^ doesn't have a size known at compile-time @@ -62,7 +62,7 @@ error[E0277]: the size for values of type `Z` cannot be known at compilation tim --> $DIR/unsized-enum2.rs:29:21 | LL | enum E<W: ?Sized, X: ?Sized, Y: ?Sized, Z: ?Sized> { - | - this type parameter needs to be `Sized` + | - this type parameter needs to be `std::marker::Sized` ... LL | VD{u: isize, x: Z}, | ^ doesn't have a size known at compile-time @@ -301,7 +301,11 @@ LL | VI(Path1), | ^^^^^ doesn't have a size known at compile-time | = help: within `Path1`, the trait `Sized` is not implemented for `(dyn PathHelper1 + 'static)` - = note: required because it appears within the type `Path1` +note: required because it appears within the type `Path1` + --> $DIR/unsized-enum2.rs:16:8 + | +LL | struct Path1(dyn PathHelper1); + | ^^^^^ = note: no field of an enum variant may have a dynamically sized type = help: change the field's type to have a statically known size help: borrowed types always have a statically known size @@ -320,7 +324,11 @@ LL | VJ{x: Path2}, | ^^^^^ doesn't have a size known at compile-time | = help: within `Path2`, the trait `Sized` is not implemented for `(dyn PathHelper2 + 'static)` - = note: required because it appears within the type `Path2` +note: required because it appears within the type `Path2` + --> $DIR/unsized-enum2.rs:17:8 + | +LL | struct Path2(dyn PathHelper2); + | ^^^^^ = note: no field of an enum variant may have a dynamically sized type = help: change the field's type to have a statically known size help: borrowed types always have a statically known size @@ -339,7 +347,11 @@ LL | VK(isize, Path3), | ^^^^^ doesn't have a size known at compile-time | = help: within `Path3`, the trait `Sized` is not implemented for `(dyn PathHelper3 + 'static)` - = note: required because it appears within the type `Path3` +note: required because it appears within the type `Path3` + --> $DIR/unsized-enum2.rs:18:8 + | +LL | struct Path3(dyn PathHelper3); + | ^^^^^ = note: no field of an enum variant may have a dynamically sized type = help: change the field's type to have a statically known size help: borrowed types always have a statically known size @@ -358,7 +370,11 @@ LL | VL{u: isize, x: Path4}, | ^^^^^ doesn't have a size known at compile-time | = help: within `Path4`, the trait `Sized` is not implemented for `(dyn PathHelper4 + 'static)` - = note: required because it appears within the type `Path4` +note: required because it appears within the type `Path4` + --> $DIR/unsized-enum2.rs:19:8 + | +LL | struct Path4(dyn PathHelper4); + | ^^^^^ = note: no field of an enum variant may have a dynamically sized type = help: change the field's type to have a statically known size help: borrowed types always have a statically known size diff --git a/src/test/ui/unsized/unsized-fn-arg.fixed b/src/test/ui/unsized/unsized-fn-arg.fixed new file mode 100644 index 0000000000000..2c686c6c2b271 --- /dev/null +++ b/src/test/ui/unsized/unsized-fn-arg.fixed @@ -0,0 +1,6 @@ +// run-rustfix +#![crate_type="lib"] +#![allow(unused)] + +fn f<T: ?Sized>(t: &T) {} +//~^ ERROR the size for values of type `T` cannot be known at compilation time diff --git a/src/test/ui/unsized/unsized-fn-arg.rs b/src/test/ui/unsized/unsized-fn-arg.rs new file mode 100644 index 0000000000000..9fc08bd6d3e75 --- /dev/null +++ b/src/test/ui/unsized/unsized-fn-arg.rs @@ -0,0 +1,6 @@ +// run-rustfix +#![crate_type="lib"] +#![allow(unused)] + +fn f<T: ?Sized>(t: T) {} +//~^ ERROR the size for values of type `T` cannot be known at compilation time diff --git a/src/test/ui/unsized/unsized-fn-arg.stderr b/src/test/ui/unsized/unsized-fn-arg.stderr new file mode 100644 index 0000000000000..6b802ddf542d5 --- /dev/null +++ b/src/test/ui/unsized/unsized-fn-arg.stderr @@ -0,0 +1,17 @@ +error[E0277]: the size for values of type `T` cannot be known at compilation time + --> $DIR/unsized-fn-arg.rs:5:17 + | +LL | fn f<T: ?Sized>(t: T) {} + | - ^ doesn't have a size known at compile-time + | | + | this type parameter needs to be `std::marker::Sized` + | + = help: unsized fn params are gated as an unstable feature +help: function arguments must have a statically known size, borrowed types always have a known size + | +LL | fn f<T: ?Sized>(t: &T) {} + | ^ + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0277`. diff --git a/src/test/ui/unsized/unsized-inherent-impl-self-type.stderr b/src/test/ui/unsized/unsized-inherent-impl-self-type.stderr index 9d8a1c67734a4..9d072eda4e81c 100644 --- a/src/test/ui/unsized/unsized-inherent-impl-self-type.stderr +++ b/src/test/ui/unsized/unsized-inherent-impl-self-type.stderr @@ -7,7 +7,7 @@ LL | LL | impl<X: ?Sized> S5<X> { | - ^^^^^ doesn't have a size known at compile-time | | - | this type parameter needs to be `Sized` + | this type parameter needs to be `std::marker::Sized` | help: you could relax the implicit `Sized` bound on `Y` if it were used through indirection like `&Y` or `Box<Y>` --> $DIR/unsized-inherent-impl-self-type.rs:5:11 diff --git a/src/test/ui/unsized/unsized-struct.stderr b/src/test/ui/unsized/unsized-struct.stderr index 6661cf358b3ab..e38375bff46cf 100644 --- a/src/test/ui/unsized/unsized-struct.stderr +++ b/src/test/ui/unsized/unsized-struct.stderr @@ -7,7 +7,7 @@ LL | fn foo1<T>() { not_sized::<Foo<T>>() } // Hunky dory. LL | fn foo2<T: ?Sized>() { not_sized::<Foo<T>>() } | - ^^^^^^ doesn't have a size known at compile-time | | - | this type parameter needs to be `Sized` + | this type parameter needs to be `std::marker::Sized` | help: you could relax the implicit `Sized` bound on `T` if it were used through indirection like `&T` or `Box<T>` --> $DIR/unsized-struct.rs:4:12 @@ -26,9 +26,13 @@ LL | fn is_sized<T:Sized>() { } LL | fn bar2<T: ?Sized>() { is_sized::<Bar<T>>() } | - ^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time | | - | this type parameter needs to be `Sized` + | this type parameter needs to be `std::marker::Sized` | - = note: required because it appears within the type `Bar<T>` +note: required because it appears within the type `Bar<T>` + --> $DIR/unsized-struct.rs:11:8 + | +LL | struct Bar<T: ?Sized> { data: T } + | ^^^ error: aborting due to 2 previous errors diff --git a/src/test/ui/unsized/unsized-trait-impl-self-type.stderr b/src/test/ui/unsized/unsized-trait-impl-self-type.stderr index d1b590d813307..aef0d0cbb8395 100644 --- a/src/test/ui/unsized/unsized-trait-impl-self-type.stderr +++ b/src/test/ui/unsized/unsized-trait-impl-self-type.stderr @@ -7,7 +7,7 @@ LL | LL | impl<X: ?Sized> T3<X> for S5<X> { | - ^^^^^ doesn't have a size known at compile-time | | - | this type parameter needs to be `Sized` + | this type parameter needs to be `std::marker::Sized` | help: you could relax the implicit `Sized` bound on `Y` if it were used through indirection like `&Y` or `Box<Y>` --> $DIR/unsized-trait-impl-self-type.rs:8:11 diff --git a/src/test/ui/unsized/unsized-trait-impl-trait-arg.stderr b/src/test/ui/unsized/unsized-trait-impl-trait-arg.stderr index 17fe16ed4fc05..f48d4ef9f1461 100644 --- a/src/test/ui/unsized/unsized-trait-impl-trait-arg.stderr +++ b/src/test/ui/unsized/unsized-trait-impl-trait-arg.stderr @@ -7,7 +7,7 @@ LL | trait T2<Z> { LL | impl<X: ?Sized> T2<X> for S4<X> { | - ^^^^^ doesn't have a size known at compile-time | | - | this type parameter needs to be `Sized` + | this type parameter needs to be `std::marker::Sized` | help: consider relaxing the implicit `Sized` restriction | diff --git a/src/test/ui/unsized-tuple-impls.rs b/src/test/ui/unsized/unsized-tuple-impls.rs similarity index 100% rename from src/test/ui/unsized-tuple-impls.rs rename to src/test/ui/unsized/unsized-tuple-impls.rs diff --git a/src/test/ui/unsized.rs b/src/test/ui/unsized/unsized.rs similarity index 100% rename from src/test/ui/unsized.rs rename to src/test/ui/unsized/unsized.rs diff --git a/src/test/ui/unsized2.rs b/src/test/ui/unsized/unsized2.rs similarity index 100% rename from src/test/ui/unsized2.rs rename to src/test/ui/unsized/unsized2.rs diff --git a/src/test/ui/unsized3-rpass.rs b/src/test/ui/unsized/unsized3-rpass.rs similarity index 100% rename from src/test/ui/unsized3-rpass.rs rename to src/test/ui/unsized/unsized3-rpass.rs diff --git a/src/test/ui/unsized3.rs b/src/test/ui/unsized/unsized3.rs similarity index 100% rename from src/test/ui/unsized3.rs rename to src/test/ui/unsized/unsized3.rs diff --git a/src/test/ui/unsized/unsized3.stderr b/src/test/ui/unsized/unsized3.stderr new file mode 100644 index 0000000000000..bd36008aca044 --- /dev/null +++ b/src/test/ui/unsized/unsized3.stderr @@ -0,0 +1,109 @@ +error[E0277]: the size for values of type `X` cannot be known at compilation time + --> $DIR/unsized3.rs:7:13 + | +LL | fn f1<X: ?Sized>(x: &X) { + | - this type parameter needs to be `std::marker::Sized` +LL | f2::<X>(x); + | ^ doesn't have a size known at compile-time +... +LL | fn f2<X>(x: &X) { + | - required by this bound in `f2` + | +help: consider relaxing the implicit `Sized` restriction + | +LL | fn f2<X: ?Sized>(x: &X) { + | ^^^^^^^^ + +error[E0277]: the size for values of type `X` cannot be known at compilation time + --> $DIR/unsized3.rs:18:13 + | +LL | fn f3<X: ?Sized + T>(x: &X) { + | - this type parameter needs to be `std::marker::Sized` +LL | f4::<X>(x); + | ^ doesn't have a size known at compile-time +... +LL | fn f4<X: T>(x: &X) { + | - required by this bound in `f4` + | +help: consider relaxing the implicit `Sized` restriction + | +LL | fn f4<X: T + ?Sized>(x: &X) { + | ^^^^^^^^ + +error[E0277]: the size for values of type `X` cannot be known at compilation time + --> $DIR/unsized3.rs:33:8 + | +LL | fn f5<Y>(x: &Y) {} + | - required by this bound in `f5` +... +LL | fn f8<X: ?Sized>(x1: &S<X>, x2: &S<X>) { + | - this type parameter needs to be `std::marker::Sized` +LL | f5(x1); + | ^^ doesn't have a size known at compile-time + | +note: required because it appears within the type `S<X>` + --> $DIR/unsized3.rs:28:8 + | +LL | struct S<X: ?Sized> { + | ^ +help: consider relaxing the implicit `Sized` restriction + | +LL | fn f5<Y: ?Sized>(x: &Y) {} + | ^^^^^^^^ + +error[E0277]: the size for values of type `X` cannot be known at compilation time + --> $DIR/unsized3.rs:40:8 + | +LL | fn f9<X: ?Sized>(x1: Box<S<X>>) { + | - this type parameter needs to be `std::marker::Sized` +LL | f5(&(*x1, 34)); + | ^^^^^^^^^^ doesn't have a size known at compile-time + | +note: required because it appears within the type `S<X>` + --> $DIR/unsized3.rs:28:8 + | +LL | struct S<X: ?Sized> { + | ^ + = note: only the last element of a tuple may have a dynamically sized type + +error[E0277]: the size for values of type `X` cannot be known at compilation time + --> $DIR/unsized3.rs:45:9 + | +LL | fn f10<X: ?Sized>(x1: Box<S<X>>) { + | - this type parameter needs to be `std::marker::Sized` +LL | f5(&(32, *x1)); + | ^^^^^^^^^ doesn't have a size known at compile-time + | +note: required because it appears within the type `S<X>` + --> $DIR/unsized3.rs:28:8 + | +LL | struct S<X: ?Sized> { + | ^ + = note: required because it appears within the type `({integer}, S<X>)` + = note: tuples must have a statically known size to be initialized + +error[E0277]: the size for values of type `X` cannot be known at compilation time + --> $DIR/unsized3.rs:45:8 + | +LL | fn f5<Y>(x: &Y) {} + | - required by this bound in `f5` +... +LL | fn f10<X: ?Sized>(x1: Box<S<X>>) { + | - this type parameter needs to be `std::marker::Sized` +LL | f5(&(32, *x1)); + | ^^^^^^^^^^ doesn't have a size known at compile-time + | +note: required because it appears within the type `S<X>` + --> $DIR/unsized3.rs:28:8 + | +LL | struct S<X: ?Sized> { + | ^ + = note: required because it appears within the type `({integer}, S<X>)` +help: consider relaxing the implicit `Sized` restriction + | +LL | fn f5<Y: ?Sized>(x: &Y) {} + | ^^^^^^^^ + +error: aborting due to 6 previous errors + +For more information about this error, try `rustc --explain E0277`. diff --git a/src/test/ui/unsized5.rs b/src/test/ui/unsized/unsized5.rs similarity index 100% rename from src/test/ui/unsized5.rs rename to src/test/ui/unsized/unsized5.rs diff --git a/src/test/ui/unsized5.stderr b/src/test/ui/unsized/unsized5.stderr similarity index 93% rename from src/test/ui/unsized5.stderr rename to src/test/ui/unsized/unsized5.stderr index a7539b0672afe..0bfd4565529aa 100644 --- a/src/test/ui/unsized5.stderr +++ b/src/test/ui/unsized/unsized5.stderr @@ -2,7 +2,7 @@ error[E0277]: the size for values of type `X` cannot be known at compilation tim --> $DIR/unsized5.rs:4:9 | LL | struct S1<X: ?Sized> { - | - this type parameter needs to be `Sized` + | - this type parameter needs to be `std::marker::Sized` LL | f1: X, | ^ doesn't have a size known at compile-time | @@ -21,7 +21,7 @@ error[E0277]: the size for values of type `X` cannot be known at compilation tim --> $DIR/unsized5.rs:10:8 | LL | struct S2<X: ?Sized> { - | - this type parameter needs to be `Sized` + | - this type parameter needs to be `std::marker::Sized` LL | f: isize, LL | g: X, | ^ doesn't have a size known at compile-time @@ -77,7 +77,7 @@ error[E0277]: the size for values of type `X` cannot be known at compilation tim --> $DIR/unsized5.rs:25:8 | LL | enum E<X: ?Sized> { - | - this type parameter needs to be `Sized` + | - this type parameter needs to be `std::marker::Sized` LL | V1(X, isize), | ^ doesn't have a size known at compile-time | @@ -96,7 +96,7 @@ error[E0277]: the size for values of type `X` cannot be known at compilation tim --> $DIR/unsized5.rs:29:12 | LL | enum F<X: ?Sized> { - | - this type parameter needs to be `Sized` + | - this type parameter needs to be `std::marker::Sized` LL | V2{f1: X, f: isize}, | ^ doesn't have a size known at compile-time | diff --git a/src/test/ui/unsized6.rs b/src/test/ui/unsized/unsized6.rs similarity index 100% rename from src/test/ui/unsized6.rs rename to src/test/ui/unsized/unsized6.rs diff --git a/src/test/ui/unsized6.stderr b/src/test/ui/unsized/unsized6.stderr similarity index 85% rename from src/test/ui/unsized6.stderr rename to src/test/ui/unsized/unsized6.stderr index 71dac236fa316..8e5734dffb145 100644 --- a/src/test/ui/unsized6.stderr +++ b/src/test/ui/unsized/unsized6.stderr @@ -2,7 +2,7 @@ error[E0277]: the size for values of type `Y` cannot be known at compilation tim --> $DIR/unsized6.rs:9:9 | LL | fn f1<W: ?Sized, X: ?Sized, Y: ?Sized, Z: ?Sized>(x: &X) { - | - this type parameter needs to be `Sized` + | - this type parameter needs to be `std::marker::Sized` ... LL | let y: Y; | ^ doesn't have a size known at compile-time @@ -14,7 +14,7 @@ error[E0277]: the size for values of type `X` cannot be known at compilation tim --> $DIR/unsized6.rs:7:12 | LL | fn f1<W: ?Sized, X: ?Sized, Y: ?Sized, Z: ?Sized>(x: &X) { - | - this type parameter needs to be `Sized` + | - this type parameter needs to be `std::marker::Sized` LL | let _: W; // <-- this is OK, no bindings created, no initializer. LL | let _: (isize, (X, isize)); | ^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time @@ -25,7 +25,7 @@ error[E0277]: the size for values of type `Z` cannot be known at compilation tim --> $DIR/unsized6.rs:11:12 | LL | fn f1<W: ?Sized, X: ?Sized, Y: ?Sized, Z: ?Sized>(x: &X) { - | - this type parameter needs to be `Sized` + | - this type parameter needs to be `std::marker::Sized` ... LL | let y: (isize, (Z, usize)); | ^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time @@ -36,7 +36,7 @@ error[E0277]: the size for values of type `X` cannot be known at compilation tim --> $DIR/unsized6.rs:15:9 | LL | fn f2<X: ?Sized, Y: ?Sized>(x: &X) { - | - this type parameter needs to be `Sized` + | - this type parameter needs to be `std::marker::Sized` LL | let y: X; | ^ doesn't have a size known at compile-time | @@ -47,7 +47,7 @@ error[E0277]: the size for values of type `Y` cannot be known at compilation tim --> $DIR/unsized6.rs:17:12 | LL | fn f2<X: ?Sized, Y: ?Sized>(x: &X) { - | - this type parameter needs to be `Sized` + | - this type parameter needs to be `std::marker::Sized` ... LL | let y: (isize, (Y, isize)); | ^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time @@ -58,7 +58,7 @@ error[E0277]: the size for values of type `X` cannot be known at compilation tim --> $DIR/unsized6.rs:22:9 | LL | fn f3<X: ?Sized>(x1: Box<X>, x2: Box<X>, x3: Box<X>) { - | - this type parameter needs to be `Sized` + | - this type parameter needs to be `std::marker::Sized` LL | let y: X = *x1; | ^ doesn't have a size known at compile-time | @@ -69,7 +69,7 @@ error[E0277]: the size for values of type `X` cannot be known at compilation tim --> $DIR/unsized6.rs:24:9 | LL | fn f3<X: ?Sized>(x1: Box<X>, x2: Box<X>, x3: Box<X>) { - | - this type parameter needs to be `Sized` + | - this type parameter needs to be `std::marker::Sized` ... LL | let y = *x2; | ^ doesn't have a size known at compile-time @@ -81,7 +81,7 @@ error[E0277]: the size for values of type `X` cannot be known at compilation tim --> $DIR/unsized6.rs:26:10 | LL | fn f3<X: ?Sized>(x1: Box<X>, x2: Box<X>, x3: Box<X>) { - | - this type parameter needs to be `Sized` + | - this type parameter needs to be `std::marker::Sized` ... LL | let (y, z) = (*x3, 4); | ^ doesn't have a size known at compile-time @@ -93,7 +93,7 @@ error[E0277]: the size for values of type `X` cannot be known at compilation tim --> $DIR/unsized6.rs:30:9 | LL | fn f4<X: ?Sized + T>(x1: Box<X>, x2: Box<X>, x3: Box<X>) { - | - this type parameter needs to be `Sized` + | - this type parameter needs to be `std::marker::Sized` LL | let y: X = *x1; | ^ doesn't have a size known at compile-time | @@ -104,7 +104,7 @@ error[E0277]: the size for values of type `X` cannot be known at compilation tim --> $DIR/unsized6.rs:32:9 | LL | fn f4<X: ?Sized + T>(x1: Box<X>, x2: Box<X>, x3: Box<X>) { - | - this type parameter needs to be `Sized` + | - this type parameter needs to be `std::marker::Sized` ... LL | let y = *x2; | ^ doesn't have a size known at compile-time @@ -116,7 +116,7 @@ error[E0277]: the size for values of type `X` cannot be known at compilation tim --> $DIR/unsized6.rs:34:10 | LL | fn f4<X: ?Sized + T>(x1: Box<X>, x2: Box<X>, x3: Box<X>) { - | - this type parameter needs to be `Sized` + | - this type parameter needs to be `std::marker::Sized` ... LL | let (y, z) = (*x3, 4); | ^ doesn't have a size known at compile-time @@ -130,13 +130,13 @@ error[E0277]: the size for values of type `X` cannot be known at compilation tim LL | fn g1<X: ?Sized>(x: X) {} | - ^ doesn't have a size known at compile-time | | - | this type parameter needs to be `Sized` + | this type parameter needs to be `std::marker::Sized` | = help: unsized fn params are gated as an unstable feature help: function arguments must have a statically known size, borrowed types always have a known size | -LL | fn g1<X: ?Sized>(&x: X) {} - | ^ +LL | fn g1<X: ?Sized>(x: &X) {} + | ^ error[E0277]: the size for values of type `X` cannot be known at compilation time --> $DIR/unsized6.rs:40:22 @@ -144,13 +144,13 @@ error[E0277]: the size for values of type `X` cannot be known at compilation tim LL | fn g2<X: ?Sized + T>(x: X) {} | - ^ doesn't have a size known at compile-time | | - | this type parameter needs to be `Sized` + | this type parameter needs to be `std::marker::Sized` | = help: unsized fn params are gated as an unstable feature help: function arguments must have a statically known size, borrowed types always have a known size | -LL | fn g2<X: ?Sized + T>(&x: X) {} - | ^ +LL | fn g2<X: ?Sized + T>(x: &X) {} + | ^ error: aborting due to 13 previous errors diff --git a/src/test/ui/unsized7.rs b/src/test/ui/unsized/unsized7.rs similarity index 100% rename from src/test/ui/unsized7.rs rename to src/test/ui/unsized/unsized7.rs diff --git a/src/test/ui/unsized7.stderr b/src/test/ui/unsized/unsized7.stderr similarity index 89% rename from src/test/ui/unsized7.stderr rename to src/test/ui/unsized/unsized7.stderr index 5f9a2604ed5b0..7dbddd4ed2443 100644 --- a/src/test/ui/unsized7.stderr +++ b/src/test/ui/unsized/unsized7.stderr @@ -7,7 +7,7 @@ LL | trait T1<Z: T> { LL | impl<X: ?Sized + T> T1<X> for S3<X> { | - ^^^^^ doesn't have a size known at compile-time | | - | this type parameter needs to be `Sized` + | this type parameter needs to be `std::marker::Sized` | help: consider relaxing the implicit `Sized` restriction | diff --git a/src/test/ui/unsized3.stderr b/src/test/ui/unsized3.stderr deleted file mode 100644 index 7ed43c38f1d38..0000000000000 --- a/src/test/ui/unsized3.stderr +++ /dev/null @@ -1,93 +0,0 @@ -error[E0277]: the size for values of type `X` cannot be known at compilation time - --> $DIR/unsized3.rs:7:13 - | -LL | fn f1<X: ?Sized>(x: &X) { - | - this type parameter needs to be `Sized` -LL | f2::<X>(x); - | ^ doesn't have a size known at compile-time -... -LL | fn f2<X>(x: &X) { - | - required by this bound in `f2` - | -help: consider relaxing the implicit `Sized` restriction - | -LL | fn f2<X: ?Sized>(x: &X) { - | ^^^^^^^^ - -error[E0277]: the size for values of type `X` cannot be known at compilation time - --> $DIR/unsized3.rs:18:13 - | -LL | fn f3<X: ?Sized + T>(x: &X) { - | - this type parameter needs to be `Sized` -LL | f4::<X>(x); - | ^ doesn't have a size known at compile-time -... -LL | fn f4<X: T>(x: &X) { - | - required by this bound in `f4` - | -help: consider relaxing the implicit `Sized` restriction - | -LL | fn f4<X: T + ?Sized>(x: &X) { - | ^^^^^^^^ - -error[E0277]: the size for values of type `X` cannot be known at compilation time - --> $DIR/unsized3.rs:33:8 - | -LL | fn f5<Y>(x: &Y) {} - | - required by this bound in `f5` -... -LL | fn f8<X: ?Sized>(x1: &S<X>, x2: &S<X>) { - | - this type parameter needs to be `Sized` -LL | f5(x1); - | ^^ doesn't have a size known at compile-time - | - = note: required because it appears within the type `S<X>` -help: consider relaxing the implicit `Sized` restriction - | -LL | fn f5<Y: ?Sized>(x: &Y) {} - | ^^^^^^^^ - -error[E0277]: the size for values of type `X` cannot be known at compilation time - --> $DIR/unsized3.rs:40:8 - | -LL | fn f9<X: ?Sized>(x1: Box<S<X>>) { - | - this type parameter needs to be `Sized` -LL | f5(&(*x1, 34)); - | ^^^^^^^^^^ doesn't have a size known at compile-time - | - = note: required because it appears within the type `S<X>` - = note: only the last element of a tuple may have a dynamically sized type - -error[E0277]: the size for values of type `X` cannot be known at compilation time - --> $DIR/unsized3.rs:45:9 - | -LL | fn f10<X: ?Sized>(x1: Box<S<X>>) { - | - this type parameter needs to be `Sized` -LL | f5(&(32, *x1)); - | ^^^^^^^^^ doesn't have a size known at compile-time - | - = note: required because it appears within the type `S<X>` - = note: required because it appears within the type `({integer}, S<X>)` - = note: tuples must have a statically known size to be initialized - -error[E0277]: the size for values of type `X` cannot be known at compilation time - --> $DIR/unsized3.rs:45:8 - | -LL | fn f5<Y>(x: &Y) {} - | - required by this bound in `f5` -... -LL | fn f10<X: ?Sized>(x1: Box<S<X>>) { - | - this type parameter needs to be `Sized` -LL | f5(&(32, *x1)); - | ^^^^^^^^^^ doesn't have a size known at compile-time - | - = note: required because it appears within the type `S<X>` - = note: required because it appears within the type `({integer}, S<X>)` -help: consider relaxing the implicit `Sized` restriction - | -LL | fn f5<Y: ?Sized>(x: &Y) {} - | ^^^^^^^^ - -error: aborting due to 6 previous errors - -For more information about this error, try `rustc --explain E0277`. diff --git a/src/test/ui/unspecified-self-in-trait-ref.stderr b/src/test/ui/unspecified-self-in-trait-ref.stderr index 9310b3d7ede00..c9518170222c0 100644 --- a/src/test/ui/unspecified-self-in-trait-ref.stderr +++ b/src/test/ui/unspecified-self-in-trait-ref.stderr @@ -31,7 +31,7 @@ LL | | } | |_- type parameter `A` must be specified for this ... LL | let e = Bar::<usize>::lol(); - | ^^^^^^^^^^^^^^^^^ missing reference to `A` + | ^^^^^^^^^^^^ missing reference to `A` | = note: because of the default `Self` reference, type parameters must be specified on object types diff --git a/src/test/ui/unused/unused-attr.stderr b/src/test/ui/unused/unused-attr.stderr index fd7412db2257c..707521b7802f0 100644 --- a/src/test/ui/unused/unused-attr.stderr +++ b/src/test/ui/unused/unused-attr.stderr @@ -1,8 +1,8 @@ error: unused attribute - --> $DIR/unused-attr.rs:6:1 + --> $DIR/unused-attr.rs:4:1 | -LL | #[rustc_dummy] - | ^^^^^^^^^^^^^^ +LL | #![rustc_dummy] + | ^^^^^^^^^^^^^^^ | note: the lint level is defined here --> $DIR/unused-attr.rs:1:9 @@ -11,16 +11,16 @@ LL | #![deny(unused_attributes)] | ^^^^^^^^^^^^^^^^^ error: unused attribute - --> $DIR/unused-attr.rs:9:1 + --> $DIR/unused-attr.rs:6:1 | LL | #[rustc_dummy] | ^^^^^^^^^^^^^^ error: unused attribute - --> $DIR/unused-attr.rs:14:5 + --> $DIR/unused-attr.rs:9:1 | -LL | #[rustc_dummy] - | ^^^^^^^^^^^^^^ +LL | #[rustc_dummy] + | ^^^^^^^^^^^^^^ error: unused attribute --> $DIR/unused-attr.rs:12:1 @@ -29,10 +29,16 @@ LL | #[rustc_dummy] | ^^^^^^^^^^^^^^ error: unused attribute - --> $DIR/unused-attr.rs:22:9 + --> $DIR/unused-attr.rs:14:5 | -LL | #[rustc_dummy] - | ^^^^^^^^^^^^^^ +LL | #[rustc_dummy] + | ^^^^^^^^^^^^^^ + +error: unused attribute + --> $DIR/unused-attr.rs:18:1 + | +LL | #[rustc_dummy] + | ^^^^^^^^^^^^^^ error: unused attribute --> $DIR/unused-attr.rs:20:5 @@ -41,7 +47,13 @@ LL | #[rustc_dummy] | ^^^^^^^^^^^^^^ error: unused attribute - --> $DIR/unused-attr.rs:18:1 + --> $DIR/unused-attr.rs:22:9 + | +LL | #[rustc_dummy] + | ^^^^^^^^^^^^^^ + +error: unused attribute + --> $DIR/unused-attr.rs:27:1 | LL | #[rustc_dummy] | ^^^^^^^^^^^^^^ @@ -53,7 +65,7 @@ LL | #[rustc_dummy] | ^^^^^^^^^^^^^^ error: unused attribute - --> $DIR/unused-attr.rs:27:1 + --> $DIR/unused-attr.rs:35:1 | LL | #[rustc_dummy] | ^^^^^^^^^^^^^^ @@ -65,7 +77,7 @@ LL | #[rustc_dummy] | ^^^^^^^^^^^^^^ error: unused attribute - --> $DIR/unused-attr.rs:35:1 + --> $DIR/unused-attr.rs:41:1 | LL | #[rustc_dummy] | ^^^^^^^^^^^^^^ @@ -82,17 +94,5 @@ error: unused attribute LL | #[rustc_dummy] | ^^^^^^^^^^^^^^ -error: unused attribute - --> $DIR/unused-attr.rs:41:1 - | -LL | #[rustc_dummy] - | ^^^^^^^^^^^^^^ - -error: unused attribute - --> $DIR/unused-attr.rs:4:1 - | -LL | #![rustc_dummy] - | ^^^^^^^^^^^^^^^ - error: aborting due to 15 previous errors diff --git a/src/test/ui/unused/unused-doc-comments-for-macros.rs b/src/test/ui/unused/unused-doc-comments-for-macros.rs new file mode 100644 index 0000000000000..05828ebb2c353 --- /dev/null +++ b/src/test/ui/unused/unused-doc-comments-for-macros.rs @@ -0,0 +1,17 @@ +#![deny(unused_doc_comments)] +#![feature(rustc_attrs)] + +macro_rules! foo { () => {}; } + +fn main() { + /// line1 //~ ERROR: unused doc comment + /// line2 + /// line3 + foo!(); + + // Ensure we still detect another doc-comment block. + /// line1 //~ ERROR: unused doc comment + /// line2 + /// line3 + foo!(); +} diff --git a/src/test/ui/unused/unused-doc-comments-for-macros.stderr b/src/test/ui/unused/unused-doc-comments-for-macros.stderr new file mode 100644 index 0000000000000..f4f5bb71e551e --- /dev/null +++ b/src/test/ui/unused/unused-doc-comments-for-macros.stderr @@ -0,0 +1,31 @@ +error: unused doc comment + --> $DIR/unused-doc-comments-for-macros.rs:7:5 + | +LL | / /// line1 +LL | | /// line2 +LL | | /// line3 + | |_____--------^ + | | + | rustdoc does not generate documentation for macro invocations + | +note: the lint level is defined here + --> $DIR/unused-doc-comments-for-macros.rs:1:9 + | +LL | #![deny(unused_doc_comments)] + | ^^^^^^^^^^^^^^^^^^^ + = help: to document an item produced by a macro, the macro must produce the documentation as part of its expansion + +error: unused doc comment + --> $DIR/unused-doc-comments-for-macros.rs:13:5 + | +LL | / /// line1 +LL | | /// line2 +LL | | /// line3 + | |_____--------^ + | | + | rustdoc does not generate documentation for macro invocations + | + = help: to document an item produced by a macro, the macro must produce the documentation as part of its expansion + +error: aborting due to 2 previous errors + diff --git a/src/test/ui/useless-comment.rs b/src/test/ui/unused/useless-comment.rs similarity index 100% rename from src/test/ui/useless-comment.rs rename to src/test/ui/unused/useless-comment.rs diff --git a/src/test/ui/useless-comment.stderr b/src/test/ui/unused/useless-comment.stderr similarity index 100% rename from src/test/ui/useless-comment.stderr rename to src/test/ui/unused/useless-comment.stderr diff --git a/src/test/ui/unwind-abis/feature-gate-c-unwind-enabled.rs b/src/test/ui/unwind-abis/feature-gate-c-unwind-enabled.rs new file mode 100644 index 0000000000000..6ff5dbda2d560 --- /dev/null +++ b/src/test/ui/unwind-abis/feature-gate-c-unwind-enabled.rs @@ -0,0 +1,12 @@ +// Test that the "C-unwind" ABI is feature-gated, and *can* be used when the +// `c_unwind` feature gate is enabled. + +// check-pass + +#![feature(c_unwind)] + +extern "C-unwind" fn f() {} + +fn main() { + f(); +} diff --git a/src/test/ui/unwind-abis/feature-gate-c-unwind.rs b/src/test/ui/unwind-abis/feature-gate-c-unwind.rs new file mode 100644 index 0000000000000..f02a368d4e097 --- /dev/null +++ b/src/test/ui/unwind-abis/feature-gate-c-unwind.rs @@ -0,0 +1,9 @@ +// Test that the "C-unwind" ABI is feature-gated, and cannot be used when the +// `c_unwind` feature gate is not used. + +extern "C-unwind" fn f() {} +//~^ ERROR C-unwind ABI is experimental and subject to change [E0658] + +fn main() { + f(); +} diff --git a/src/test/ui/unwind-abis/feature-gate-c-unwind.stderr b/src/test/ui/unwind-abis/feature-gate-c-unwind.stderr new file mode 100644 index 0000000000000..f4c785a235f67 --- /dev/null +++ b/src/test/ui/unwind-abis/feature-gate-c-unwind.stderr @@ -0,0 +1,12 @@ +error[E0658]: C-unwind ABI is experimental and subject to change + --> $DIR/feature-gate-c-unwind.rs:4:8 + | +LL | extern "C-unwind" fn f() {} + | ^^^^^^^^^^ + | + = note: see issue #74990 <https://github.com/rust-lang/rust/issues/74990> for more information + = help: add `#![feature(c_unwind)]` to the crate attributes to enable + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0658`. diff --git a/src/test/ui/unwind-abis/feature-gate-stdcall-unwind.rs b/src/test/ui/unwind-abis/feature-gate-stdcall-unwind.rs new file mode 100644 index 0000000000000..7d4dc8c9343f1 --- /dev/null +++ b/src/test/ui/unwind-abis/feature-gate-stdcall-unwind.rs @@ -0,0 +1,13 @@ +// ignore-arm stdcall isn't supported +// ignore-aarch64 stdcall isn't supported +// ignore-riscv64 stdcall isn't supported + +// Test that the "stdcall-unwind" ABI is feature-gated, and cannot be used when +// the `c_unwind` feature gate is not used. + +extern "stdcall-unwind" fn f() {} +//~^ ERROR stdcall-unwind ABI is experimental and subject to change [E0658] + +fn main() { + f(); +} diff --git a/src/test/ui/unwind-abis/feature-gate-stdcall-unwind.stderr b/src/test/ui/unwind-abis/feature-gate-stdcall-unwind.stderr new file mode 100644 index 0000000000000..e3d569f464f87 --- /dev/null +++ b/src/test/ui/unwind-abis/feature-gate-stdcall-unwind.stderr @@ -0,0 +1,12 @@ +error[E0658]: stdcall-unwind ABI is experimental and subject to change + --> $DIR/feature-gate-stdcall-unwind.rs:8:8 + | +LL | extern "stdcall-unwind" fn f() {} + | ^^^^^^^^^^^^^^^^ + | + = note: see issue #74990 <https://github.com/rust-lang/rust/issues/74990> for more information + = help: add `#![feature(c_unwind)]` to the crate attributes to enable + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0658`. diff --git a/src/test/ui/unwind-abis/feature-gate-system-unwind.rs b/src/test/ui/unwind-abis/feature-gate-system-unwind.rs new file mode 100644 index 0000000000000..26c2de4e81767 --- /dev/null +++ b/src/test/ui/unwind-abis/feature-gate-system-unwind.rs @@ -0,0 +1,9 @@ +// Test that the "system-unwind" ABI is feature-gated, and cannot be used when +// the `c_unwind` feature gate is not used. + +extern "system-unwind" fn f() {} +//~^ ERROR system-unwind ABI is experimental and subject to change [E0658] + +fn main() { + f(); +} diff --git a/src/test/ui/unwind-abis/feature-gate-system-unwind.stderr b/src/test/ui/unwind-abis/feature-gate-system-unwind.stderr new file mode 100644 index 0000000000000..87877336475b4 --- /dev/null +++ b/src/test/ui/unwind-abis/feature-gate-system-unwind.stderr @@ -0,0 +1,12 @@ +error[E0658]: system-unwind ABI is experimental and subject to change + --> $DIR/feature-gate-system-unwind.rs:4:8 + | +LL | extern "system-unwind" fn f() {} + | ^^^^^^^^^^^^^^^ + | + = note: see issue #74990 <https://github.com/rust-lang/rust/issues/74990> for more information + = help: add `#![feature(c_unwind)]` to the crate attributes to enable + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0658`. diff --git a/src/test/ui/unwind-abis/feature-gate-thiscall-unwind.rs b/src/test/ui/unwind-abis/feature-gate-thiscall-unwind.rs new file mode 100644 index 0000000000000..2f4cefccc1967 --- /dev/null +++ b/src/test/ui/unwind-abis/feature-gate-thiscall-unwind.rs @@ -0,0 +1,13 @@ +// ignore-arm thiscall isn't supported +// ignore-aarch64 thiscall isn't supported +// ignore-riscv64 thiscall isn't supported + +// Test that the "thiscall-unwind" ABI is feature-gated, and cannot be used when +// the `c_unwind` feature gate is not used. + +extern "thiscall-unwind" fn f() {} +//~^ ERROR thiscall-unwind ABI is experimental and subject to change [E0658] + +fn main() { + f(); +} diff --git a/src/test/ui/unwind-abis/feature-gate-thiscall-unwind.stderr b/src/test/ui/unwind-abis/feature-gate-thiscall-unwind.stderr new file mode 100644 index 0000000000000..b103bb8d56585 --- /dev/null +++ b/src/test/ui/unwind-abis/feature-gate-thiscall-unwind.stderr @@ -0,0 +1,12 @@ +error[E0658]: thiscall-unwind ABI is experimental and subject to change + --> $DIR/feature-gate-thiscall-unwind.rs:8:8 + | +LL | extern "thiscall-unwind" fn f() {} + | ^^^^^^^^^^^^^^^^^ + | + = note: see issue #74990 <https://github.com/rust-lang/rust/issues/74990> for more information + = help: add `#![feature(c_unwind)]` to the crate attributes to enable + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0658`. diff --git a/src/test/ui/unwind-no-uwtable.rs b/src/test/ui/unwind-no-uwtable.rs new file mode 100644 index 0000000000000..f249d3f457474 --- /dev/null +++ b/src/test/ui/unwind-no-uwtable.rs @@ -0,0 +1,34 @@ +// run-pass +// ignore-windows target requires uwtable +// ignore-wasm32-bare no proper panic=unwind support +// compile-flags: -C panic=unwind -C force-unwind-tables=n + +use std::panic::{self, AssertUnwindSafe}; + +struct Increase<'a>(&'a mut u8); + +impl Drop for Increase<'_> { + fn drop(&mut self) { + *self.0 += 1; + } +} + +#[inline(never)] +fn unwind() { + panic!(); +} + +#[inline(never)] +fn increase(count: &mut u8) { + let _increase = Increase(count); + unwind(); +} + +fn main() { + let mut count = 0; + assert!(panic::catch_unwind(AssertUnwindSafe( + #[inline(never)] + || increase(&mut count) + )).is_err()); + assert_eq!(count, 1); +} diff --git a/src/test/ui/utf8_idents-rpass.rs b/src/test/ui/utf8_idents-rpass.rs index 582b67bc29924..206744a58fde4 100644 --- a/src/test/ui/utf8_idents-rpass.rs +++ b/src/test/ui/utf8_idents-rpass.rs @@ -2,8 +2,6 @@ // #![allow(non_snake_case)] -#![feature(non_ascii_idents)] - pub fn main() { let ε = 0.00001f64; let Π = 3.14f64; diff --git a/src/test/ui/utf8_idents.rs b/src/test/ui/utf8_idents.rs index 6c54086cc2009..1f6326dd94b5c 100644 --- a/src/test/ui/utf8_idents.rs +++ b/src/test/ui/utf8_idents.rs @@ -1,15 +1,16 @@ -#![allow(mixed_script_confusables)] +// check-pass +// +#![allow(mixed_script_confusables, non_camel_case_types)] fn foo< - 'β, //~ ERROR non-ascii idents are not fully supported - γ //~ ERROR non-ascii idents are not fully supported - //~^ WARN type parameter `γ` should have an upper camel case name + 'β, + γ >() {} struct X { - δ: usize //~ ERROR non-ascii idents are not fully supported + δ: usize } pub fn main() { - let α = 0.00001f64; //~ ERROR non-ascii idents are not fully supported + let α = 0.00001f64; } diff --git a/src/test/ui/utf8_idents.stderr b/src/test/ui/utf8_idents.stderr deleted file mode 100644 index 2fc0b1c39effb..0000000000000 --- a/src/test/ui/utf8_idents.stderr +++ /dev/null @@ -1,47 +0,0 @@ -error[E0658]: non-ascii idents are not fully supported - --> $DIR/utf8_idents.rs:4:5 - | -LL | 'β, - | ^^ - | - = note: see issue #55467 <https://github.com/rust-lang/rust/issues/55467> for more information - = help: add `#![feature(non_ascii_idents)]` to the crate attributes to enable - -error[E0658]: non-ascii idents are not fully supported - --> $DIR/utf8_idents.rs:5:5 - | -LL | γ - | ^ - | - = note: see issue #55467 <https://github.com/rust-lang/rust/issues/55467> for more information - = help: add `#![feature(non_ascii_idents)]` to the crate attributes to enable - -error[E0658]: non-ascii idents are not fully supported - --> $DIR/utf8_idents.rs:10:5 - | -LL | δ: usize - | ^ - | - = note: see issue #55467 <https://github.com/rust-lang/rust/issues/55467> for more information - = help: add `#![feature(non_ascii_idents)]` to the crate attributes to enable - -error[E0658]: non-ascii idents are not fully supported - --> $DIR/utf8_idents.rs:14:9 - | -LL | let α = 0.00001f64; - | ^ - | - = note: see issue #55467 <https://github.com/rust-lang/rust/issues/55467> for more information - = help: add `#![feature(non_ascii_idents)]` to the crate attributes to enable - -warning: type parameter `γ` should have an upper camel case name - --> $DIR/utf8_idents.rs:5:5 - | -LL | γ - | ^ help: convert the identifier to upper camel case: `Γ` - | - = note: `#[warn(non_camel_case_types)]` on by default - -error: aborting due to 4 previous errors; 1 warning emitted - -For more information about this error, try `rustc --explain E0658`. diff --git a/src/test/ui/wf/wf-enum-bound.stderr b/src/test/ui/wf/wf-enum-bound.stderr index e7bc858225139..7819110dd98b5 100644 --- a/src/test/ui/wf/wf-enum-bound.stderr +++ b/src/test/ui/wf/wf-enum-bound.stderr @@ -9,8 +9,8 @@ LL | where T: ExtraCopy<U> | help: consider further restricting type parameter `U` | -LL | where T: ExtraCopy<U>, U: Copy - | ^^^^^^^^^ +LL | where T: ExtraCopy<U>, U: std::marker::Copy + | ^^^^^^^^^^^^^^^^^^^^^^ error: aborting due to previous error diff --git a/src/test/ui/wf/wf-enum-fields-struct-variant.stderr b/src/test/ui/wf/wf-enum-fields-struct-variant.stderr index c97ce53885b72..4bfb2413fe99f 100644 --- a/src/test/ui/wf/wf-enum-fields-struct-variant.stderr +++ b/src/test/ui/wf/wf-enum-fields-struct-variant.stderr @@ -9,8 +9,8 @@ LL | f: IsCopy<A> | help: consider restricting type parameter `A` | -LL | enum AnotherEnum<A: Copy> { - | ^^^^^^ +LL | enum AnotherEnum<A: std::marker::Copy> { + | ^^^^^^^^^^^^^^^^^^^ error: aborting due to previous error diff --git a/src/test/ui/wf/wf-enum-fields.stderr b/src/test/ui/wf/wf-enum-fields.stderr index 85da1bf5839a3..c8a75afbab7cb 100644 --- a/src/test/ui/wf/wf-enum-fields.stderr +++ b/src/test/ui/wf/wf-enum-fields.stderr @@ -9,8 +9,8 @@ LL | SomeVariant(IsCopy<A>) | help: consider restricting type parameter `A` | -LL | enum SomeEnum<A: Copy> { - | ^^^^^^ +LL | enum SomeEnum<A: std::marker::Copy> { + | ^^^^^^^^^^^^^^^^^^^ error: aborting due to previous error diff --git a/src/test/ui/wf/wf-fn-where-clause.stderr b/src/test/ui/wf/wf-fn-where-clause.stderr index 22598e58bd744..e463e3db887a5 100644 --- a/src/test/ui/wf/wf-fn-where-clause.stderr +++ b/src/test/ui/wf/wf-fn-where-clause.stderr @@ -9,8 +9,8 @@ LL | fn foo<T,U>() where T: ExtraCopy<U> | help: consider further restricting type parameter `U` | -LL | fn foo<T,U>() where T: ExtraCopy<U>, U: Copy - | ^^^^^^^^^ +LL | fn foo<T,U>() where T: ExtraCopy<U>, U: std::marker::Copy + | ^^^^^^^^^^^^^^^^^^^^^^ error[E0277]: the size for values of type `(dyn Copy + 'static)` cannot be known at compilation time --> $DIR/wf-fn-where-clause.rs:12:16 diff --git a/src/test/ui/wf/wf-in-fn-arg.stderr b/src/test/ui/wf/wf-in-fn-arg.stderr index d6010d1d79e93..9687658feba43 100644 --- a/src/test/ui/wf/wf-in-fn-arg.stderr +++ b/src/test/ui/wf/wf-in-fn-arg.stderr @@ -9,8 +9,8 @@ LL | fn bar<T>(_: &MustBeCopy<T>) | help: consider restricting type parameter `T` | -LL | fn bar<T: Copy>(_: &MustBeCopy<T>) - | ^^^^^^ +LL | fn bar<T: std::marker::Copy>(_: &MustBeCopy<T>) + | ^^^^^^^^^^^^^^^^^^^ error: aborting due to previous error diff --git a/src/test/ui/wf/wf-in-fn-ret.stderr b/src/test/ui/wf/wf-in-fn-ret.stderr index c22252657f12e..f9a962578e650 100644 --- a/src/test/ui/wf/wf-in-fn-ret.stderr +++ b/src/test/ui/wf/wf-in-fn-ret.stderr @@ -9,8 +9,8 @@ LL | fn bar<T>() -> MustBeCopy<T> | help: consider restricting type parameter `T` | -LL | fn bar<T: Copy>() -> MustBeCopy<T> - | ^^^^^^ +LL | fn bar<T: std::marker::Copy>() -> MustBeCopy<T> + | ^^^^^^^^^^^^^^^^^^^ error: aborting due to previous error diff --git a/src/test/ui/wf/wf-in-fn-type-arg.stderr b/src/test/ui/wf/wf-in-fn-type-arg.stderr index 6d249f5f918d3..33300b3964258 100644 --- a/src/test/ui/wf/wf-in-fn-type-arg.stderr +++ b/src/test/ui/wf/wf-in-fn-type-arg.stderr @@ -9,8 +9,8 @@ LL | x: fn(MustBeCopy<T>) | help: consider restricting type parameter `T` | -LL | struct Bar<T: Copy> { - | ^^^^^^ +LL | struct Bar<T: std::marker::Copy> { + | ^^^^^^^^^^^^^^^^^^^ error: aborting due to previous error diff --git a/src/test/ui/wf/wf-in-fn-type-ret.stderr b/src/test/ui/wf/wf-in-fn-type-ret.stderr index 30ff365b116ec..1ffc47e6d826c 100644 --- a/src/test/ui/wf/wf-in-fn-type-ret.stderr +++ b/src/test/ui/wf/wf-in-fn-type-ret.stderr @@ -9,8 +9,8 @@ LL | x: fn() -> MustBeCopy<T> | help: consider restricting type parameter `T` | -LL | struct Foo<T: Copy> { - | ^^^^^^ +LL | struct Foo<T: std::marker::Copy> { + | ^^^^^^^^^^^^^^^^^^^ error: aborting due to previous error diff --git a/src/test/ui/wf/wf-in-fn-where-clause.stderr b/src/test/ui/wf/wf-in-fn-where-clause.stderr index 64e9694c0e16b..7cb9af11d799b 100644 --- a/src/test/ui/wf/wf-in-fn-where-clause.stderr +++ b/src/test/ui/wf/wf-in-fn-where-clause.stderr @@ -9,8 +9,8 @@ LL | where T: MustBeCopy<U> | help: consider further restricting type parameter `U` | -LL | where T: MustBeCopy<U>, U: Copy - | ^^^^^^^^^ +LL | where T: MustBeCopy<U>, U: std::marker::Copy + | ^^^^^^^^^^^^^^^^^^^^^^ error: aborting due to previous error diff --git a/src/test/ui/wf/wf-in-obj-type-trait.stderr b/src/test/ui/wf/wf-in-obj-type-trait.stderr index 55ea08ccbe7ff..8606eabf59c14 100644 --- a/src/test/ui/wf/wf-in-obj-type-trait.stderr +++ b/src/test/ui/wf/wf-in-obj-type-trait.stderr @@ -9,8 +9,8 @@ LL | x: dyn Object<MustBeCopy<T>> | help: consider restricting type parameter `T` | -LL | struct Bar<T: Copy> { - | ^^^^^^ +LL | struct Bar<T: std::marker::Copy> { + | ^^^^^^^^^^^^^^^^^^^ error: aborting due to previous error diff --git a/src/test/ui/wf/wf-inherent-impl-method-where-clause.stderr b/src/test/ui/wf/wf-inherent-impl-method-where-clause.stderr index 7bbdd4bcf2428..8e0ce557e6b43 100644 --- a/src/test/ui/wf/wf-inherent-impl-method-where-clause.stderr +++ b/src/test/ui/wf/wf-inherent-impl-method-where-clause.stderr @@ -9,8 +9,8 @@ LL | fn foo(self) where T: ExtraCopy<U> | help: consider restricting type parameter `U` | -LL | impl<T,U: Copy> Foo<T,U> { - | ^^^^^^ +LL | impl<T,U: std::marker::Copy> Foo<T,U> { + | ^^^^^^^^^^^^^^^^^^^ error: aborting due to previous error diff --git a/src/test/ui/wf/wf-inherent-impl-where-clause.stderr b/src/test/ui/wf/wf-inherent-impl-where-clause.stderr index 2a44e1cdc7162..bf8077ba88f6b 100644 --- a/src/test/ui/wf/wf-inherent-impl-where-clause.stderr +++ b/src/test/ui/wf/wf-inherent-impl-where-clause.stderr @@ -9,8 +9,8 @@ LL | impl<T,U> Foo<T,U> where T: ExtraCopy<U> | help: consider further restricting type parameter `U` | -LL | impl<T,U> Foo<T,U> where T: ExtraCopy<U>, U: Copy - | ^^^^^^^^^ +LL | impl<T,U> Foo<T,U> where T: ExtraCopy<U>, U: std::marker::Copy + | ^^^^^^^^^^^^^^^^^^^^^^ error: aborting due to previous error diff --git a/src/test/ui/wf/wf-static-method.stderr b/src/test/ui/wf/wf-static-method.stderr index 93d16514a5078..0c98a809025ac 100644 --- a/src/test/ui/wf/wf-static-method.stderr +++ b/src/test/ui/wf/wf-static-method.stderr @@ -19,7 +19,7 @@ error[E0478]: lifetime bound not satisfied --> $DIR/wf-static-method.rs:26:18 | LL | let me = Self::make_me(); - | ^^^^^^^^^^^^^ + | ^^^^ | note: lifetime parameter instantiated with the lifetime `'b` as defined on the impl at 23:10 --> $DIR/wf-static-method.rs:23:10 diff --git a/src/test/ui/wf/wf-struct-bound.stderr b/src/test/ui/wf/wf-struct-bound.stderr index 948693ac6f87d..e85f3591438a9 100644 --- a/src/test/ui/wf/wf-struct-bound.stderr +++ b/src/test/ui/wf/wf-struct-bound.stderr @@ -9,8 +9,8 @@ LL | where T: ExtraCopy<U> | help: consider further restricting type parameter `U` | -LL | where T: ExtraCopy<U>, U: Copy - | ^^^^^^^^^ +LL | where T: ExtraCopy<U>, U: std::marker::Copy + | ^^^^^^^^^^^^^^^^^^^^^^ error: aborting due to previous error diff --git a/src/test/ui/wf/wf-struct-field.stderr b/src/test/ui/wf/wf-struct-field.stderr index 04e62a7fcb776..62ef6bb60c7a2 100644 --- a/src/test/ui/wf/wf-struct-field.stderr +++ b/src/test/ui/wf/wf-struct-field.stderr @@ -9,8 +9,8 @@ LL | data: IsCopy<A> | help: consider restricting type parameter `A` | -LL | struct SomeStruct<A: Copy> { - | ^^^^^^ +LL | struct SomeStruct<A: std::marker::Copy> { + | ^^^^^^^^^^^^^^^^^^^ error: aborting due to previous error diff --git a/src/test/ui/wf/wf-trait-associated-type-bound.stderr b/src/test/ui/wf/wf-trait-associated-type-bound.stderr index 166e3626f094e..51adfdb6bd2a9 100644 --- a/src/test/ui/wf/wf-trait-associated-type-bound.stderr +++ b/src/test/ui/wf/wf-trait-associated-type-bound.stderr @@ -9,8 +9,8 @@ LL | type Type1: ExtraCopy<T>; | help: consider restricting type parameter `T` | -LL | trait SomeTrait<T: Copy> { - | ^^^^^^ +LL | trait SomeTrait<T: std::marker::Copy> { + | ^^^^^^^^^^^^^^^^^^^ error: aborting due to previous error diff --git a/src/test/ui/wf/wf-trait-bound.stderr b/src/test/ui/wf/wf-trait-bound.stderr index abb97adcfd4cd..c9e818f8e7dc8 100644 --- a/src/test/ui/wf/wf-trait-bound.stderr +++ b/src/test/ui/wf/wf-trait-bound.stderr @@ -9,8 +9,8 @@ LL | where T: ExtraCopy<U> | help: consider further restricting type parameter `U` | -LL | where T: ExtraCopy<U>, U: Copy - | ^^^^^^^^^ +LL | where T: ExtraCopy<U>, U: std::marker::Copy + | ^^^^^^^^^^^^^^^^^^^^^^ error: aborting due to previous error diff --git a/src/test/ui/wf/wf-trait-superbound.stderr b/src/test/ui/wf/wf-trait-superbound.stderr index db337a7c6d0fd..6926983527178 100644 --- a/src/test/ui/wf/wf-trait-superbound.stderr +++ b/src/test/ui/wf/wf-trait-superbound.stderr @@ -9,8 +9,8 @@ LL | trait SomeTrait<T>: ExtraCopy<T> { | help: consider restricting type parameter `T` | -LL | trait SomeTrait<T: Copy>: ExtraCopy<T> { - | ^^^^^^ +LL | trait SomeTrait<T: std::marker::Copy>: ExtraCopy<T> { + | ^^^^^^^^^^^^^^^^^^^ error: aborting due to previous error diff --git a/src/test/ui/where-clauses/where-clause-constraints-are-local-for-inherent-impl.stderr b/src/test/ui/where-clauses/where-clause-constraints-are-local-for-inherent-impl.stderr index 916c6dc6d53a4..356ba347cc36a 100644 --- a/src/test/ui/where-clauses/where-clause-constraints-are-local-for-inherent-impl.stderr +++ b/src/test/ui/where-clauses/where-clause-constraints-are-local-for-inherent-impl.stderr @@ -9,8 +9,8 @@ LL | require_copy(self.x); | help: consider restricting type parameter `T` | -LL | impl<T: Copy> Foo<T> { - | ^^^^^^ +LL | impl<T: std::marker::Copy> Foo<T> { + | ^^^^^^^^^^^^^^^^^^^ error: aborting due to previous error diff --git a/src/test/ui/where-clauses/where-clause-constraints-are-local-for-trait-impl.stderr b/src/test/ui/where-clauses/where-clause-constraints-are-local-for-trait-impl.stderr index 8814018964aa6..d84242cfe12b8 100644 --- a/src/test/ui/where-clauses/where-clause-constraints-are-local-for-trait-impl.stderr +++ b/src/test/ui/where-clauses/where-clause-constraints-are-local-for-trait-impl.stderr @@ -9,8 +9,8 @@ LL | require_copy(self.x); | help: consider restricting type parameter `T` | -LL | impl<T: Copy> Foo<T> for Bar<T> { - | ^^^^^^ +LL | impl<T: std::marker::Copy> Foo<T> for Bar<T> { + | ^^^^^^^^^^^^^^^^^^^ error: aborting due to previous error diff --git a/src/test/ui/wrong-mul-method-signature.stderr b/src/test/ui/wrong-mul-method-signature.stderr index 4c367fb9e9caf..9f8896f01ee06 100644 --- a/src/test/ui/wrong-mul-method-signature.stderr +++ b/src/test/ui/wrong-mul-method-signature.stderr @@ -1,26 +1,35 @@ error[E0053]: method `mul` has an incompatible type for trait - --> $DIR/wrong-mul-method-signature.rs:16:5 + --> $DIR/wrong-mul-method-signature.rs:16:21 | LL | fn mul(self, s: &f64) -> Vec1 { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `f64`, found `&f64` + | ^^^^ + | | + | expected `f64`, found `&f64` + | help: change the parameter type to match the trait: `f64` | = note: expected fn pointer `fn(Vec1, f64) -> Vec1` found fn pointer `fn(Vec1, &f64) -> Vec1` error[E0053]: method `mul` has an incompatible type for trait - --> $DIR/wrong-mul-method-signature.rs:33:5 + --> $DIR/wrong-mul-method-signature.rs:33:21 | LL | fn mul(self, s: f64) -> Vec2 { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected struct `Vec2`, found `f64` + | ^^^ + | | + | expected struct `Vec2`, found `f64` + | help: change the parameter type to match the trait: `Vec2` | = note: expected fn pointer `fn(Vec2, Vec2) -> f64` found fn pointer `fn(Vec2, f64) -> Vec2` error[E0053]: method `mul` has an incompatible type for trait - --> $DIR/wrong-mul-method-signature.rs:52:5 + --> $DIR/wrong-mul-method-signature.rs:52:29 | LL | fn mul(self, s: f64) -> f64 { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `i32`, found `f64` + | ^^^ + | | + | expected `i32`, found `f64` + | help: change the output type to match the trait: `i32` | = note: expected fn pointer `fn(Vec3, _) -> i32` found fn pointer `fn(Vec3, _) -> f64` diff --git a/src/tools/cargo b/src/tools/cargo index c68432f1e5cbb..65d57e6f384c2 160000 --- a/src/tools/cargo +++ b/src/tools/cargo @@ -1 +1 @@ -Subproject commit c68432f1e5cbbc09833699a951b1b5b059651dff +Subproject commit 65d57e6f384c2317f76626eac116f683e2b63665 diff --git a/src/tools/cargotest/main.rs b/src/tools/cargotest/main.rs index 0a6bac48ebbac..fc608eaabccf7 100644 --- a/src/tools/cargotest/main.rs +++ b/src/tools/cargotest/main.rs @@ -9,6 +9,8 @@ struct Test { sha: &'static str, lock: Option<&'static str>, packages: &'static [&'static str], + features: Option<&'static [&'static str]>, + manifest_path: Option<&'static str>, } const TEST_REPOS: &[Test] = &[ @@ -18,6 +20,8 @@ const TEST_REPOS: &[Test] = &[ sha: "cf056ea5e8052c1feea6141e40ab0306715a2c33", lock: None, packages: &[], + features: None, + manifest_path: None, }, Test { name: "ripgrep", @@ -25,6 +29,8 @@ const TEST_REPOS: &[Test] = &[ sha: "3de31f752729525d85a3d1575ac1978733b3f7e7", lock: None, packages: &[], + features: None, + manifest_path: None, }, Test { name: "tokei", @@ -32,6 +38,8 @@ const TEST_REPOS: &[Test] = &[ sha: "fdf3f8cb279a7aeac0696c87e5d8b0cd946e4f9e", lock: None, packages: &[], + features: None, + manifest_path: None, }, Test { name: "xsv", @@ -39,6 +47,8 @@ const TEST_REPOS: &[Test] = &[ sha: "3de6c04269a7d315f7e9864b9013451cd9580a08", lock: None, packages: &[], + features: None, + manifest_path: None, }, Test { name: "servo", @@ -48,6 +58,23 @@ const TEST_REPOS: &[Test] = &[ // Only test Stylo a.k.a. Quantum CSS, the parts of Servo going into Firefox. // This takes much less time to build than all of Servo and supports stable Rust. packages: &["selectors"], + features: None, + manifest_path: None, + }, + Test { + name: "diesel", + repo: "https://github.com/diesel-rs/diesel", + sha: "91493fe47175076f330ce5fc518f0196c0476f56", + lock: None, + packages: &[], + // Test the embeded sqlite variant of diesel + // This does not require any dependency to be present, + // sqlite will be compiled as part of the build process + features: Some(&["sqlite", "libsqlite3-sys/bundled"]), + // We are only interested in testing diesel itself + // not any other crate present in the diesel workspace + // (This is required to set the feature flags above) + manifest_path: Some("diesel/Cargo.toml"), }, ]; @@ -68,7 +95,7 @@ fn test_repo(cargo: &Path, out_dir: &Path, test: &Test) { if let Some(lockfile) = test.lock { fs::write(&dir.join("Cargo.lock"), lockfile).unwrap(); } - if !run_cargo_test(cargo, &dir, test.packages) { + if !run_cargo_test(cargo, &dir, test.packages, test.features, test.manifest_path) { panic!("tests failed for {}", test.repo); } } @@ -120,12 +147,31 @@ fn clone_repo(test: &Test, out_dir: &Path) -> PathBuf { out_dir } -fn run_cargo_test(cargo_path: &Path, crate_path: &Path, packages: &[&str]) -> bool { +fn run_cargo_test( + cargo_path: &Path, + crate_path: &Path, + packages: &[&str], + features: Option<&[&str]>, + manifest_path: Option<&str>, +) -> bool { let mut command = Command::new(cargo_path); command.arg("test"); + + if let Some(path) = manifest_path { + command.arg(format!("--manifest-path={}", path)); + } + + if let Some(features) = features { + command.arg("--no-default-features"); + for feature in features { + command.arg(format!("--features={}", feature)); + } + } + for name in packages { command.arg("-p").arg(name); } + let status = command // Disable rust-lang/cargo's cross-compile tests .env("CFG_DISABLE_CROSS_TESTS", "1") diff --git a/src/tools/clippy/.cargo/config b/src/tools/clippy/.cargo/config index 1142cc470fe82..9b5add4df1c12 100644 --- a/src/tools/clippy/.cargo/config +++ b/src/tools/clippy/.cargo/config @@ -1,7 +1,7 @@ [alias] uitest = "test --test compile-test" dev = "run --target-dir clippy_dev/target --package clippy_dev --bin clippy_dev --manifest-path clippy_dev/Cargo.toml --" -dev-lintcheck = "run --target-dir clippy_dev/target --package clippy_dev --bin clippy_dev --manifest-path clippy_dev/Cargo.toml --features lintcheck -- lintcheck" +lintcheck = "run --target-dir lintcheck/target --package lintcheck --bin lintcheck --manifest-path lintcheck/Cargo.toml -- " [build] rustflags = ["-Zunstable-options"] diff --git a/src/tools/clippy/.github/workflows/clippy.yml b/src/tools/clippy/.github/workflows/clippy.yml index f6ac936df4d6b..32103f59d8b0e 100644 --- a/src/tools/clippy/.github/workflows/clippy.yml +++ b/src/tools/clippy/.github/workflows/clippy.yml @@ -27,6 +27,7 @@ env: jobs: base: + # NOTE: If you modify this job, make sure you copy the changes to clippy_bors.yml runs-on: ubuntu-latest steps: @@ -50,11 +51,16 @@ jobs: - name: Build run: cargo build --features deny-warnings,internal-lints - - name: Test "--fix -Zunstable-options" - run: cargo run --features deny-warnings,internal-lints --bin cargo-clippy -- clippy --fix -Zunstable-options + - name: Test + run: cargo test --features deny-warnings,internal-lints - - name: Test Workspace - run: cargo test --all --features deny-warnings,internal-lints + - name: Test clippy_lints + run: cargo test --features deny-warnings,internal-lints + working-directory: clippy_lints + + - name: Test rustc_tools_util + run: cargo test --features deny-warnings + working-directory: rustc_tools_util - name: Test clippy_dev run: cargo test --features deny-warnings @@ -64,6 +70,10 @@ jobs: run: ../target/debug/cargo-clippy working-directory: clippy_workspace_tests + - name: Test cargo-clippy --fix + run: ../target/debug/cargo-clippy clippy --fix -Zunstable-options + 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 9d24b0293c4a7..47253eecc4c4c 100644 --- a/src/tools/clippy/.github/workflows/clippy_bors.yml +++ b/src/tools/clippy/.github/workflows/clippy_bors.yml @@ -72,6 +72,7 @@ jobs: runs-on: ${{ matrix.os }} + # NOTE: If you modify this job, make sure you copy the changes to clippy.yml steps: # Setup - uses: rust-lang/simpleinfra/github-actions/cancel-outdated-builds@master @@ -112,8 +113,16 @@ jobs: - name: Build run: cargo build --features deny-warnings,internal-lints - - name: Test Workspace - run: cargo test --all --features deny-warnings,internal-lints + - name: Test + run: cargo test --features deny-warnings,internal-lints + + - name: Test clippy_lints + run: cargo test --features deny-warnings,internal-lints + working-directory: clippy_lints + + - name: Test rustc_tools_util + run: cargo test --features deny-warnings + working-directory: rustc_tools_util - name: Test clippy_dev run: cargo test --features deny-warnings @@ -123,11 +132,22 @@ jobs: run: ../target/debug/cargo-clippy working-directory: clippy_workspace_tests + - name: Test cargo-clippy --fix + run: ../target/debug/cargo-clippy clippy --fix -Zunstable-options + working-directory: clippy_workspace_tests + - name: Test clippy-driver run: bash .github/driver.sh env: OS: ${{ runner.os }} + - name: Test cargo dev new lint + run: | + cargo dev new_lint --name new_early_pass --pass early + cargo dev new_lint --name new_late_pass --pass late + cargo check + git reset --hard HEAD + integration_build: needs: changelog runs-on: ubuntu-latest diff --git a/src/tools/clippy/.gitignore b/src/tools/clippy/.gitignore index 139129d55e330..376528e30853a 100644 --- a/src/tools/clippy/.gitignore +++ b/src/tools/clippy/.gitignore @@ -21,6 +21,7 @@ out /clippy_utils/target /clippy_workspace_tests/target /clippy_dev/target +/lintcheck/target /rustc_tools_util/target # Generated by dogfood diff --git a/src/tools/clippy/CHANGELOG.md b/src/tools/clippy/CHANGELOG.md index d96c74b6e4126..73997192ae0d5 100644 --- a/src/tools/clippy/CHANGELOG.md +++ b/src/tools/clippy/CHANGELOG.md @@ -6,11 +6,146 @@ document. ## Unreleased / In Rust Nightly -[3e41797...master](https://github.com/rust-lang/rust-clippy/compare/3e41797...master) +[6ed6f1e...master](https://github.com/rust-lang/rust-clippy/compare/6ed6f1e...master) + +## Rust 1.52 + +Current beta, release 2021-05-06 + +[3e41797...6ed6f1e](https://github.com/rust-lang/rust-clippy/compare/3e41797...6ed6f1e) + +### New Lints + +* [`from_str_radix_10`] + [#6717](https://github.com/rust-lang/rust-clippy/pull/6717) +* [`implicit_clone`] + [#6730](https://github.com/rust-lang/rust-clippy/pull/6730) +* [`semicolon_if_nothing_returned`] + [#6681](https://github.com/rust-lang/rust-clippy/pull/6681) +* [`manual_flatten`] + [#6646](https://github.com/rust-lang/rust-clippy/pull/6646) +* [`inconsistent_struct_constructor`] + [#6769](https://github.com/rust-lang/rust-clippy/pull/6769) +* [`iter_count`] + [#6791](https://github.com/rust-lang/rust-clippy/pull/6791) +* [`default_numeric_fallback`] + [#6662](https://github.com/rust-lang/rust-clippy/pull/6662) +* [`bytes_nth`] + [#6695](https://github.com/rust-lang/rust-clippy/pull/6695) +* [`filter_map_identity`] + [#6685](https://github.com/rust-lang/rust-clippy/pull/6685) +* [`manual_map`] + [#6573](https://github.com/rust-lang/rust-clippy/pull/6573) + +### Moves and Deprecations + +* Moved [`upper_case_acronyms`] to `pedantic` + [#6775](https://github.com/rust-lang/rust-clippy/pull/6775) +* Moved [`manual_map`] to `nursery` + [#6796](https://github.com/rust-lang/rust-clippy/pull/6796) +* Moved [`unnecessary_wraps`] to `pedantic` + [#6765](https://github.com/rust-lang/rust-clippy/pull/6765) +* Moved [`trivial_regex`] to `nursery` + [#6696](https://github.com/rust-lang/rust-clippy/pull/6696) +* Moved [`naive_bytecount`] to `pedantic` + [#6825](https://github.com/rust-lang/rust-clippy/pull/6825) +* Moved [`upper_case_acronyms`] to `style` + [#6788](https://github.com/rust-lang/rust-clippy/pull/6788) +* Moved [`manual_map`] to `style` + [#6801](https://github.com/rust-lang/rust-clippy/pull/6801) + +### Enhancements + +* [`disallowed_method`]: Now supports functions in addition to methods + [#6674](https://github.com/rust-lang/rust-clippy/pull/6674) +* [`upper_case_acronyms`]: Added a new configuration `upper-case-acronyms-aggressive` to + trigger the lint if there is more than one uppercase character next to each other + [#6788](https://github.com/rust-lang/rust-clippy/pull/6788) +* [`collapsible_match`]: Now supports block comparison with different value names + [#6754](https://github.com/rust-lang/rust-clippy/pull/6754) +* [`unnecessary_wraps`]: Will now suggest removing unnecessary wrapped return unit type, like `Option<()>` + [#6665](https://github.com/rust-lang/rust-clippy/pull/6665) +* Improved value usage detection in closures + [#6698](https://github.com/rust-lang/rust-clippy/pull/6698) + +### False Positive Fixes + +* [`use_self`]: No longer lints in macros + [#6833](https://github.com/rust-lang/rust-clippy/pull/6833) +* [`use_self`]: Fixed multiple false positives for: generics, associated types and derive implementations + [#6179](https://github.com/rust-lang/rust-clippy/pull/6179) +* [`missing_inline_in_public_items`]: No longer lints for procedural macros + [#6814](https://github.com/rust-lang/rust-clippy/pull/6814) +* [`inherent_to_string`]: No longer lints on functions with function generics + [#6771](https://github.com/rust-lang/rust-clippy/pull/6771) +* [`doc_markdown`]: Add `OpenDNS` to the default configuration as an allowed identifier + [#6783](https://github.com/rust-lang/rust-clippy/pull/6783) +* [`missing_panics_doc`]: No longer lints on [`unreachable!`](https://doc.rust-lang.org/std/macro.unreachable.html) + [#6700](https://github.com/rust-lang/rust-clippy/pull/6700) +* [`collapsible_if`]: No longer lints on if statements with attributes + [#6701](https://github.com/rust-lang/rust-clippy/pull/6701) +* [`match_same_arms`]: Only considers empty blocks as equal if the tokens contained are the same + [#6843](https://github.com/rust-lang/rust-clippy/pull/6843) +* [`redundant_closure`]: Now ignores macros + [#6871](https://github.com/rust-lang/rust-clippy/pull/6871) +* [`manual_map`]: Fixed false positives when control flow statements like `return`, `break` etc. are used + [#6801](https://github.com/rust-lang/rust-clippy/pull/6801) +* [`vec_init_then_push`]: Fixed false positives for loops and if statements + [#6697](https://github.com/rust-lang/rust-clippy/pull/6697) +* [`len_without_is_empty`]: Will now consider multiple impl blocks and `#[allow]` on + the `len` method as well as the type definition. + [#6853](https://github.com/rust-lang/rust-clippy/pull/6853) +* [`let_underscore_drop`]: Only lints on types which implement `Drop` + [#6682](https://github.com/rust-lang/rust-clippy/pull/6682) +* [`unit_arg`]: No longer lints on unit arguments when they come from a path expression. + [#6601](https://github.com/rust-lang/rust-clippy/pull/6601) +* [`cargo_common_metadata`]: No longer lints if + [`publish = false`](https://doc.rust-lang.org/cargo/reference/manifest.html#the-publish-field) + is defined in the manifest + [#6650](https://github.com/rust-lang/rust-clippy/pull/6650) + +### Suggestion Fixes/Improvements + +* [`collapsible_match`]: Fixed lint message capitalization + [#6766](https://github.com/rust-lang/rust-clippy/pull/6766) +* [`or_fun_call`]: Improved suggestions for `or_insert(vec![])` + [#6790](https://github.com/rust-lang/rust-clippy/pull/6790) +* [`manual_map`]: No longer expands macros in the suggestions + [#6801](https://github.com/rust-lang/rust-clippy/pull/6801) +* Aligned Clippy's lint messages with the rustc dev guide + [#6787](https://github.com/rust-lang/rust-clippy/pull/6787) + +### ICE Fixes + +* [`zero_sized_map_values`] + [#6866](https://github.com/rust-lang/rust-clippy/pull/6866) + +### Documentation Improvements + +* [`useless_format`]: Improved the documentation example + [#6854](https://github.com/rust-lang/rust-clippy/pull/6854) +* Clippy's [`README.md`]: Includes a new subsection on running Clippy as a rustc wrapper + [#6782](https://github.com/rust-lang/rust-clippy/pull/6782) + +### Others +* Running `cargo clippy` after `cargo check` now works as expected + (`cargo clippy` and `cargo check` no longer shares the same build cache) + [#6687](https://github.com/rust-lang/rust-clippy/pull/6687) +* Cargo now re-runs Clippy if arguments after `--` provided to `cargo clippy` are changed. + [#6834](https://github.com/rust-lang/rust-clippy/pull/6834) +* Extracted Clippy's `utils` module into the new `clippy_utils` crate + [#6756](https://github.com/rust-lang/rust-clippy/pull/6756) +* Clippy lintcheck tool improvements + [#6800](https://github.com/rust-lang/rust-clippy/pull/6800) + [#6735](https://github.com/rust-lang/rust-clippy/pull/6735) + [#6764](https://github.com/rust-lang/rust-clippy/pull/6764) + [#6708](https://github.com/rust-lang/rust-clippy/pull/6708) + [#6780](https://github.com/rust-lang/rust-clippy/pull/6780) + [#6686](https://github.com/rust-lang/rust-clippy/pull/6686) ## Rust 1.51 -Current beta, release 2021-03-25 +Current stable, released 2021-03-25 [4911ab1...3e41797](https://github.com/rust-lang/rust-clippy/compare/4911ab1...3e41797) @@ -125,7 +260,7 @@ Current beta, release 2021-03-25 ## Rust 1.50 -Current stable, released 2021-02-11 +Released 2021-02-11 [b20d4c1...4bd77a1](https://github.com/rust-lang/rust-clippy/compare/b20d4c1...4bd77a1) @@ -1970,6 +2105,7 @@ Released 2018-09-13 [configuration file]: ./rust-clippy#configuration [pull3665]: https://github.com/rust-lang/rust-clippy/pull/3665 [adding_lints]: https://github.com/rust-lang/rust-clippy/blob/master/doc/adding_lints.md +[`README.md`]: https://github.com/rust-lang/rust-clippy/blob/master/README.md <!-- lint disable no-unused-definitions --> <!-- begin autogenerated links to lint list --> @@ -1993,6 +2129,7 @@ Released 2018-09-13 [`borrowed_box`]: https://rust-lang.github.io/rust-clippy/master/index.html#borrowed_box [`box_vec`]: https://rust-lang.github.io/rust-clippy/master/index.html#box_vec [`boxed_local`]: https://rust-lang.github.io/rust-clippy/master/index.html#boxed_local +[`branches_sharing_code`]: https://rust-lang.github.io/rust-clippy/master/index.html#branches_sharing_code [`builtin_type_shadow`]: https://rust-lang.github.io/rust-clippy/master/index.html#builtin_type_shadow [`bytes_nth`]: https://rust-lang.github.io/rust-clippy/master/index.html#bytes_nth [`cargo_common_metadata`]: https://rust-lang.github.io/rust-clippy/master/index.html#cargo_common_metadata @@ -2103,7 +2240,9 @@ Released 2018-09-13 [`if_let_some_result`]: https://rust-lang.github.io/rust-clippy/master/index.html#if_let_some_result [`if_not_else`]: https://rust-lang.github.io/rust-clippy/master/index.html#if_not_else [`if_same_then_else`]: https://rust-lang.github.io/rust-clippy/master/index.html#if_same_then_else +[`if_then_some_else_none`]: https://rust-lang.github.io/rust-clippy/master/index.html#if_then_some_else_none [`ifs_same_cond`]: https://rust-lang.github.io/rust-clippy/master/index.html#ifs_same_cond +[`implicit_clone`]: https://rust-lang.github.io/rust-clippy/master/index.html#implicit_clone [`implicit_hasher`]: https://rust-lang.github.io/rust-clippy/master/index.html#implicit_hasher [`implicit_return`]: https://rust-lang.github.io/rust-clippy/master/index.html#implicit_return [`implicit_saturating_sub`]: https://rust-lang.github.io/rust-clippy/master/index.html#implicit_saturating_sub @@ -2134,6 +2273,7 @@ Released 2018-09-13 [`invisible_characters`]: https://rust-lang.github.io/rust-clippy/master/index.html#invisible_characters [`items_after_statements`]: https://rust-lang.github.io/rust-clippy/master/index.html#items_after_statements [`iter_cloned_collect`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_cloned_collect +[`iter_count`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_count [`iter_next_loop`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_next_loop [`iter_next_slice`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_next_slice [`iter_nth`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_nth @@ -2230,6 +2370,7 @@ Released 2018-09-13 [`needless_collect`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_collect [`needless_continue`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_continue [`needless_doctest_main`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_doctest_main +[`needless_for_each`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_for_each [`needless_lifetimes`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_lifetimes [`needless_pass_by_value`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_pass_by_value [`needless_question_mark`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_question_mark @@ -2243,6 +2384,7 @@ Released 2018-09-13 [`new_without_default`]: https://rust-lang.github.io/rust-clippy/master/index.html#new_without_default [`no_effect`]: https://rust-lang.github.io/rust-clippy/master/index.html#no_effect [`non_ascii_literal`]: https://rust-lang.github.io/rust-clippy/master/index.html#non_ascii_literal +[`non_octal_unix_permissions`]: https://rust-lang.github.io/rust-clippy/master/index.html#non_octal_unix_permissions [`nonminimal_bool`]: https://rust-lang.github.io/rust-clippy/master/index.html#nonminimal_bool [`nonsensical_open_options`]: https://rust-lang.github.io/rust-clippy/master/index.html#nonsensical_open_options [`not_unsafe_ptr_arg_deref`]: https://rust-lang.github.io/rust-clippy/master/index.html#not_unsafe_ptr_arg_deref @@ -2250,6 +2392,7 @@ Released 2018-09-13 [`op_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#op_ref [`option_as_ref_deref`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_as_ref_deref [`option_env_unwrap`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_env_unwrap +[`option_filter_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_filter_map [`option_if_let_else`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_if_let_else [`option_map_or_none`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_map_or_none [`option_map_unit_fn`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_map_unit_fn diff --git a/src/tools/clippy/CONTRIBUTING.md b/src/tools/clippy/CONTRIBUTING.md index 5954ab25d1942..e0a4d4455e9c6 100644 --- a/src/tools/clippy/CONTRIBUTING.md +++ b/src/tools/clippy/CONTRIBUTING.md @@ -18,11 +18,13 @@ All contributors are expected to follow the [Rust Code of Conduct]. - [Finding something to fix/improve](#finding-something-to-fiximprove) - [Writing code](#writing-code) - [Getting code-completion for rustc internals to work](#getting-code-completion-for-rustc-internals-to-work) + - [IntelliJ Rust](#intellij-rust) + - [Rust Analyzer](#rust-analyzer) - [How Clippy works](#how-clippy-works) - - [Syncing changes between Clippy and `rust-lang/rust`](#syncing-changes-between-clippy-and-rust-langrust) + - [Syncing changes between Clippy and [`rust-lang/rust`]](#syncing-changes-between-clippy-and-rust-langrust) - [Patching git-subtree to work with big repos](#patching-git-subtree-to-work-with-big-repos) - - [Performing the sync from `rust-lang/rust` to Clippy](#performing-the-sync-from-rust-langrust-to-clippy) - - [Performing the sync from Clippy to `rust-lang/rust`](#performing-the-sync-from-clippy-to-rust-langrust) + - [Performing the sync from [`rust-lang/rust`] to Clippy](#performing-the-sync-from-rust-langrust-to-clippy) + - [Performing the sync from Clippy to [`rust-lang/rust`]](#performing-the-sync-from-clippy-to-rust-langrust) - [Defining remotes](#defining-remotes) - [Issue and PR triage](#issue-and-pr-triage) - [Bors and Homu](#bors-and-homu) @@ -105,21 +107,41 @@ quick read. ## Getting code-completion for rustc internals to work -Unfortunately, [`rust-analyzer`][ra_homepage] does not (yet?) understand how Clippy uses compiler-internals +### IntelliJ Rust +Unfortunately, [`IntelliJ Rust`][IntelliJ_rust_homepage] does not (yet?) understand how Clippy uses compiler-internals using `extern crate` and it also needs to be able to read the source files of the rustc-compiler which are not available via a `rustup` component at the time of writing. To work around this, you need to have a copy of the [rustc-repo][rustc_repo] available which can be obtained via `git clone https://github.com/rust-lang/rust/`. Then you can run a `cargo dev` command to automatically make Clippy use the rustc-repo via path-dependencies -which rust-analyzer will be able to understand. -Run `cargo dev ra_setup --repo-path <repo-path>` where `<repo-path>` is an absolute path to the rustc repo +which `IntelliJ Rust` will be able to understand. +Run `cargo dev ide_setup --repo-path <repo-path>` where `<repo-path>` is a path to the rustc repo you just cloned. The command will add path-dependencies pointing towards rustc-crates inside the rustc repo to Clippys `Cargo.toml`s and should allow rust-analyzer to understand most of the types that Clippy uses. Just make sure to remove the dependencies again before finally making a pull request! -[ra_homepage]: https://rust-analyzer.github.io/ [rustc_repo]: https://github.com/rust-lang/rust/ +[IntelliJ_rust_homepage]: https://intellij-rust.github.io/ + +### Rust Analyzer +As of [#6869][6869], [`rust-analyzer`][ra_homepage] can understand that Clippy uses compiler-internals +using `extern crate` when `package.metadata.rust-analyzer.rustc_private` is set to `true` in Clippys `Cargo.toml.` +You will required a `nightly` toolchain with the `rustc-dev` component installed. +Make sure that in the `rust-analyzer` configuration, you set +``` +{ "rust-analyzer.rustcSource": "discover" } +``` +and +``` +{ "rust-analyzer.updates.channel": "nightly" } +``` +You should be able to see information on things like `Expr` or `EarlyContext` now if you hover them, also +a lot more type hints. +This will work with `rust-analyzer 2021-03-15` shipped in nightly `1.52.0-nightly (107896c32 2021-03-15)` or later. + +[ra_homepage]: https://rust-analyzer.github.io/ +[6869]: https://github.com/rust-lang/rust-clippy/pull/6869 ## How Clippy works diff --git a/src/tools/clippy/Cargo.toml b/src/tools/clippy/Cargo.toml index ea32a8edd1ffb..cade44a0a9ab4 100644 --- a/src/tools/clippy/Cargo.toml +++ b/src/tools/clippy/Cargo.toml @@ -1,13 +1,7 @@ [package] name = "clippy" -version = "0.1.52" -authors = [ - "Manish Goregaokar <manishsmail@gmail.com>", - "Andre Bogus <bogusandre@gmail.com>", - "Georg Brandl <georg@python.org>", - "Martin Carton <cartonmartin@gmail.com>", - "Oliver Schneider <clippy-iethah7aipeen8neex1a@oli-obk.de>" -] +version = "0.1.53" +authors = ["The Rust Clippy Developers"] description = "A bunch of helpful lints to avoid common pitfalls in Rust" repository = "https://github.com/rust-lang/rust-clippy" readme = "README.md" @@ -42,6 +36,9 @@ tester = "0.9" clippy-mini-macro-test = { version = "0.2", path = "mini-macro" } serde = { version = "1.0", features = ["derive"] } derive-new = "0.5" +regex = "1.4" +quote = "1" +syn = { version = "1", features = ["full"] } # A noop dependency that changes in the Rust repository, it's a bit of a hack. # See the `src/tools/rustc-workspace-hack/README.md` file in `rust-lang/rust` @@ -55,3 +52,7 @@ rustc_tools_util = { version = "0.2.0", path = "rustc_tools_util" } deny-warnings = [] integration = ["tempfile"] internal-lints = ["clippy_lints/internal-lints"] + +[package.metadata.rust-analyzer] +# This package uses #[feature(rustc_private)] +rustc_private = true diff --git a/src/tools/clippy/README.md b/src/tools/clippy/README.md index 3cc03cf360336..8c0c16c443dfc 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 400 lints included in this crate!](https://rust-lang.github.io/rust-clippy/master/index.html) +[There are over 450 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. @@ -202,7 +202,6 @@ the lint(s) you are interested in: ```terminal cargo clippy -- -A clippy::all -W clippy::useless_format -W clippy::... ``` -Note that if you've run clippy before, this may only take effect after you've modified a file or ran `cargo clean`. ### Specifying the minimum supported Rust version diff --git a/src/tools/clippy/clippy_dev/Cargo.toml b/src/tools/clippy/clippy_dev/Cargo.toml index ebf157b80acf1..b1844e29b3273 100644 --- a/src/tools/clippy/clippy_dev/Cargo.toml +++ b/src/tools/clippy/clippy_dev/Cargo.toml @@ -1,27 +1,17 @@ [package] name = "clippy_dev" version = "0.0.1" -authors = ["Philipp Hansch <dev@phansch.net>"] +authors = ["The Rust Clippy Developers"] edition = "2018" - [dependencies] bytecount = "0.6" clap = "2.33" -flate2 = { version = "1.0.19", optional = true } -fs_extra = { version = "1.2.0", optional = true } itertools = "0.9" opener = "0.4" regex = "1" -serde = { version = "1.0", features = ["derive"], optional = true } -serde_json = { version = "1.0", optional = true } shell-escape = "0.1" -tar = { version = "0.4.30", optional = true } -toml = { version = "0.5", optional = true } -ureq = { version = "2.0.0-rc3", optional = true } -rayon = { version = "1.5.0", optional = true } walkdir = "2" [features] -lintcheck = ["flate2", "serde_json", "tar", "toml", "ureq", "serde", "fs_extra", "rayon"] deny-warnings = [] diff --git a/src/tools/clippy/clippy_dev/README.md b/src/tools/clippy/clippy_dev/README.md deleted file mode 100644 index a5ed9e27bd2bc..0000000000000 --- a/src/tools/clippy/clippy_dev/README.md +++ /dev/null @@ -1,77 +0,0 @@ -# Clippy Dev Tool - -The Clippy Dev Tool is a tool to ease Clippy development, similar to `rustc`s -`x.py`. - -Functionalities (incomplete): - -## `lintcheck` - -Runs clippy on a fixed set of crates read from -`clippy_dev/lintcheck_crates.toml` and saves logs of the lint warnings into the -repo. We can then check the diff and spot new or disappearing warnings. - -From the repo root, run: - -``` -cargo run --target-dir clippy_dev/target --package clippy_dev \ ---bin clippy_dev --manifest-path clippy_dev/Cargo.toml --features lintcheck -- lintcheck -``` - -or - -``` -cargo dev-lintcheck -``` - -By default the logs will be saved into -`lintcheck-logs/lintcheck_crates_logs.txt`. - -You can set a custom sources.toml by adding `--crates-toml custom.toml` or using -`LINTCHECK_TOML="custom.toml"` where `custom.toml` must be a relative path from -the repo root. - -The results will then be saved to `lintcheck-logs/custom_logs.toml`. - -### Configuring the Crate Sources - -The sources to check are saved in a `toml` file. There are three types of -sources. - -1. Crates-io Source - - ```toml - bitflags = {name = "bitflags", versions = ['1.2.1']} - ``` - Requires a "name" and one or multiple "versions" to be checked. - -2. `git` Source - ````toml - puffin = {name = "puffin", git_url = "https://github.com/EmbarkStudios/puffin", git_hash = "02dd4a3"} - ```` - Requires a name, the url to the repo and unique identifier of a commit, - branch or tag which is checked out before linting. There is no way to always - check `HEAD` because that would lead to changing lint-results as the repo - would get updated. If `git_url` or `git_hash` is missing, an error will be - thrown. - -3. Local Dependency - ```toml - clippy = {name = "clippy", path = "/home/user/clippy"} - ``` - For when you want to add a repository that is not published yet. - -#### Command Line Options (optional) - -```toml -bitflags = {name = "bitflags", versions = ['1.2.1'], options = ['-Wclippy::pedantic', '-Wclippy::cargo']} -``` - -It is possible to specify command line options for each crate. This makes it -possible to only check a crate for certain lint groups. If no options are -specified, the lint groups `clippy::all`, `clippy::pedantic`, and -`clippy::cargo` are checked. If an empty array is specified only `clippy::all` -is checked. - -**Note:** `-Wclippy::all` is always enabled by default, unless `-Aclippy::all` -is explicitly specified in the options. diff --git a/src/tools/clippy/clippy_dev/lintcheck_crates.toml b/src/tools/clippy/clippy_dev/lintcheck_crates.toml deleted file mode 100644 index 60e70ca4eb22b..0000000000000 --- a/src/tools/clippy/clippy_dev/lintcheck_crates.toml +++ /dev/null @@ -1,23 +0,0 @@ -[crates] -# some of these are from cargotest -cargo = {name = "cargo", versions = ['0.49.0']} -iron = {name = "iron", versions = ['0.6.1']} -ripgrep = {name = "ripgrep", versions = ['12.1.1']} -xsv = {name = "xsv", versions = ['0.13.0']} -# commented out because of 173K clippy::match_same_arms msgs in language_type.rs -#tokei = { name = "tokei", versions = ['12.0.4']} -rayon = {name = "rayon", versions = ['1.5.0']} -serde = {name = "serde", versions = ['1.0.118']} -# top 10 crates.io dls -bitflags = {name = "bitflags", versions = ['1.2.1']} -# crash = {name = "clippy_crash", path = "/tmp/clippy_crash"} -libc = {name = "libc", versions = ['0.2.81']} -log = {name = "log", versions = ['0.4.11']} -proc-macro2 = {name = "proc-macro2", versions = ['1.0.24']} -puffin = {name = "puffin", git_url = "https://github.com/EmbarkStudios/puffin", git_hash = "02dd4a3"} -quote = {name = "quote", versions = ['1.0.7']} -rand = {name = "rand", versions = ['0.7.3']} -rand_core = {name = "rand_core", versions = ['0.6.0']} -regex = {name = "regex", versions = ['1.3.2']} -syn = {name = "syn", versions = ['1.0.54']} -unicode-xid = {name = "unicode-xid", versions = ['0.2.1']} diff --git a/src/tools/clippy/clippy_dev/src/fmt.rs b/src/tools/clippy/clippy_dev/src/fmt.rs index 4d0fdadbd85d1..1517cdc941962 100644 --- a/src/tools/clippy/clippy_dev/src/fmt.rs +++ b/src/tools/clippy/clippy_dev/src/fmt.rs @@ -54,6 +54,7 @@ pub fn run(check: bool, verbose: bool) { success &= cargo_fmt(context, project_root.as_path())?; success &= cargo_fmt(context, &project_root.join("clippy_dev"))?; success &= cargo_fmt(context, &project_root.join("rustc_tools_util"))?; + success &= cargo_fmt(context, &project_root.join("lintcheck"))?; for entry in WalkDir::new(project_root.join("tests")) { let entry = entry?; @@ -67,7 +68,7 @@ pub fn run(check: bool, verbose: bool) { continue; } - success &= rustfmt(context, &path)?; + success &= rustfmt(context, path)?; } Ok(success) @@ -89,7 +90,7 @@ pub fn run(check: bool, verbose: bool) { }, CliError::RaSetupActive => { eprintln!( - "error: a local rustc repo is enabled as path dependency via `cargo dev ra_setup`. + "error: a local rustc repo is enabled as path dependency via `cargo dev ide_setup`. Not formatting because that would format the local repo as well! Please revert the changes to Cargo.tomls first." ); diff --git a/src/tools/clippy/clippy_dev/src/ide_setup.rs b/src/tools/clippy/clippy_dev/src/ide_setup.rs new file mode 100644 index 0000000000000..defb1133e44e6 --- /dev/null +++ b/src/tools/clippy/clippy_dev/src/ide_setup.rs @@ -0,0 +1,103 @@ +use std::fs; +use std::fs::File; +use std::io::prelude::*; +use std::path::{Path, PathBuf}; + +// This module takes an absolute path to a rustc repo and alters the dependencies to point towards +// the respective rustc subcrates instead of using extern crate xyz. +// This allows rust analyzer to analyze rustc internals and show proper information inside clippy +// code. See https://github.com/rust-analyzer/rust-analyzer/issues/3517 and https://github.com/rust-lang/rust-clippy/issues/5514 for details + +/// # Panics +/// +/// Panics if `rustc_path` does not lead to a rustc repo or the files could not be read +pub fn run(rustc_path: Option<&str>) { + // we can unwrap here because the arg is required by clap + let rustc_path = PathBuf::from(rustc_path.unwrap()) + .canonicalize() + .expect("failed to get the absolute repo path"); + assert!(rustc_path.is_dir(), "path is not a directory"); + let rustc_source_basedir = rustc_path.join("compiler"); + assert!( + rustc_source_basedir.is_dir(), + "are you sure the path leads to a rustc repo?" + ); + + let clippy_root_manifest = fs::read_to_string("Cargo.toml").expect("failed to read ./Cargo.toml"); + let clippy_root_lib_rs = fs::read_to_string("src/driver.rs").expect("failed to read ./src/driver.rs"); + inject_deps_into_manifest( + &rustc_source_basedir, + "Cargo.toml", + &clippy_root_manifest, + &clippy_root_lib_rs, + ) + .expect("Failed to inject deps into ./Cargo.toml"); + + let clippy_lints_manifest = + fs::read_to_string("clippy_lints/Cargo.toml").expect("failed to read ./clippy_lints/Cargo.toml"); + let clippy_lints_lib_rs = + fs::read_to_string("clippy_lints/src/lib.rs").expect("failed to read ./clippy_lints/src/lib.rs"); + inject_deps_into_manifest( + &rustc_source_basedir, + "clippy_lints/Cargo.toml", + &clippy_lints_manifest, + &clippy_lints_lib_rs, + ) + .expect("Failed to inject deps into ./clippy_lints/Cargo.toml"); +} + +fn inject_deps_into_manifest( + rustc_source_dir: &Path, + manifest_path: &str, + cargo_toml: &str, + lib_rs: &str, +) -> std::io::Result<()> { + // do not inject deps if we have aleady done so + if cargo_toml.contains("[target.'cfg(NOT_A_PLATFORM)'.dependencies]") { + eprintln!( + "cargo dev ide_setup: warning: deps already found inside {}, doing nothing.", + manifest_path + ); + return Ok(()); + } + + let extern_crates = lib_rs + .lines() + // get the deps + .filter(|line| line.starts_with("extern crate")) + // we have something like "extern crate foo;", we only care about the "foo" + // ↓ ↓ + // extern crate rustc_middle; + .map(|s| &s[13..(s.len() - 1)]); + + let new_deps = extern_crates.map(|dep| { + // format the dependencies that are going to be put inside the Cargo.toml + format!( + "{dep} = {{ path = \"{source_path}/{dep}\" }}\n", + dep = dep, + source_path = rustc_source_dir.display() + ) + }); + + // format a new [dependencies]-block with the new deps we need to inject + let mut all_deps = String::from("[target.'cfg(NOT_A_PLATFORM)'.dependencies]\n"); + new_deps.for_each(|dep_line| { + all_deps.push_str(&dep_line); + }); + all_deps.push_str("\n[dependencies]\n"); + + // replace "[dependencies]" with + // [dependencies] + // dep1 = { path = ... } + // dep2 = { path = ... } + // etc + let new_manifest = cargo_toml.replacen("[dependencies]\n", &all_deps, 1); + + // println!("{}", new_manifest); + let mut file = File::create(manifest_path)?; + file.write_all(new_manifest.as_bytes())?; + + println!("Dependency paths injected: {}", manifest_path); + + Ok(()) +} diff --git a/src/tools/clippy/clippy_dev/src/lib.rs b/src/tools/clippy/clippy_dev/src/lib.rs index 01d1fc9211a94..1e5a140e9648f 100644 --- a/src/tools/clippy/clippy_dev/src/lib.rs +++ b/src/tools/clippy/clippy_dev/src/lib.rs @@ -12,9 +12,8 @@ use walkdir::WalkDir; pub mod bless; pub mod fmt; -pub mod lintcheck; +pub mod ide_setup; pub mod new_lint; -pub mod ra_setup; pub mod serve; pub mod stderr_length_check; pub mod update_lints; @@ -102,7 +101,7 @@ impl Lint { #[must_use] pub fn gen_lint_group_list<'a>(lints: impl Iterator<Item = &'a Lint>) -> Vec<String> { lints - .map(|l| format!(" LintId::of(&{}::{}),", l.module, l.name.to_uppercase())) + .map(|l| format!(" LintId::of({}::{}),", l.module, l.name.to_uppercase())) .sorted() .collect::<Vec<String>>() } @@ -155,17 +154,17 @@ pub fn gen_register_lint_list<'a>( let header = " store.register_lints(&[".to_string(); let footer = " ]);".to_string(); let internal_lints = internal_lints - .sorted_by_key(|l| format!(" &{}::{},", l.module, l.name.to_uppercase())) + .sorted_by_key(|l| format!(" {}::{},", l.module, l.name.to_uppercase())) .map(|l| { format!( - " #[cfg(feature = \"internal-lints\")]\n &{}::{},", + " #[cfg(feature = \"internal-lints\")]\n {}::{},", l.module, l.name.to_uppercase() ) }); let other_lints = usable_lints - .sorted_by_key(|l| format!(" &{}::{},", l.module, l.name.to_uppercase())) - .map(|l| format!(" &{}::{},", l.module, l.name.to_uppercase())) + .sorted_by_key(|l| format!(" {}::{},", l.module, l.name.to_uppercase())) + .map(|l| format!(" {}::{},", l.module, l.name.to_uppercase())) .sorted(); let mut lint_list = vec![header]; lint_list.extend(internal_lints); @@ -530,7 +529,7 @@ fn test_gen_deprecated() { #[should_panic] fn test_gen_deprecated_fail() { let lints = vec![Lint::new("should_assert_eq2", "group2", "abc", None, "module_name")]; - let _ = gen_deprecated(lints.iter()); + let _deprecated_lints = gen_deprecated(lints.iter()); } #[test] @@ -551,9 +550,9 @@ fn test_gen_lint_group_list() { Lint::new("internal", "internal_style", "abc", None, "module_name"), ]; let expected = vec![ - " LintId::of(&module_name::ABC),".to_string(), - " LintId::of(&module_name::INTERNAL),".to_string(), - " LintId::of(&module_name::SHOULD_ASSERT_EQ),".to_string(), + " LintId::of(module_name::ABC),".to_string(), + " LintId::of(module_name::INTERNAL),".to_string(), + " LintId::of(module_name::SHOULD_ASSERT_EQ),".to_string(), ]; assert_eq!(expected, gen_lint_group_list(lints.iter())); } diff --git a/src/tools/clippy/clippy_dev/src/lintcheck.rs b/src/tools/clippy/clippy_dev/src/lintcheck.rs deleted file mode 100644 index b806f54528465..0000000000000 --- a/src/tools/clippy/clippy_dev/src/lintcheck.rs +++ /dev/null @@ -1,613 +0,0 @@ -// Run clippy on a fixed set of crates and collect the warnings. -// This helps observing the impact clippy changs have on a set of real-world code. -// -// When a new lint is introduced, we can search the results for new warnings and check for false -// positives. - -#![cfg(feature = "lintcheck")] -#![allow(clippy::filter_map)] - -use crate::clippy_project_root; - -use std::collections::HashMap; -use std::process::Command; -use std::sync::atomic::{AtomicUsize, Ordering}; -use std::{env, fmt, fs::write, path::PathBuf}; - -use clap::ArgMatches; -use rayon::prelude::*; -use serde::{Deserialize, Serialize}; -use serde_json::Value; - -/// List of sources to check, loaded from a .toml file -#[derive(Debug, Serialize, Deserialize)] -struct SourceList { - crates: HashMap<String, TomlCrate>, -} - -/// A crate source stored inside the .toml -/// will be translated into on one of the `CrateSource` variants -#[derive(Debug, Serialize, Deserialize)] -struct TomlCrate { - name: String, - versions: Option<Vec<String>>, - git_url: Option<String>, - git_hash: Option<String>, - path: Option<String>, - options: Option<Vec<String>>, -} - -/// Represents an archive we download from crates.io, or a git repo, or a local repo/folder -/// Once processed (downloaded/extracted/cloned/copied...), this will be translated into a `Crate` -#[derive(Debug, Serialize, Deserialize, Eq, Hash, PartialEq, Ord, PartialOrd)] -enum CrateSource { - CratesIo { - name: String, - version: String, - options: Option<Vec<String>>, - }, - Git { - name: String, - url: String, - commit: String, - options: Option<Vec<String>>, - }, - Path { - name: String, - path: PathBuf, - options: Option<Vec<String>>, - }, -} - -/// Represents the actual source code of a crate that we ran "cargo clippy" on -#[derive(Debug)] -struct Crate { - version: String, - name: String, - // path to the extracted sources that clippy can check - path: PathBuf, - options: Option<Vec<String>>, -} - -/// A single warning that clippy issued while checking a `Crate` -#[derive(Debug)] -struct ClippyWarning { - crate_name: String, - crate_version: String, - file: String, - line: String, - column: String, - linttype: String, - message: String, - is_ice: bool, -} - -impl std::fmt::Display for ClippyWarning { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - writeln!( - f, - r#"{}-{}/{}:{}:{} {} "{}""#, - &self.crate_name, &self.crate_version, &self.file, &self.line, &self.column, &self.linttype, &self.message - ) - } -} - -impl CrateSource { - /// Makes the sources available on the disk for clippy to check. - /// Clones a git repo and checks out the specified commit or downloads a crate from crates.io or - /// copies a local folder - fn download_and_extract(&self) -> Crate { - match self { - CrateSource::CratesIo { name, version, options } => { - let extract_dir = PathBuf::from("target/lintcheck/crates"); - let krate_download_dir = PathBuf::from("target/lintcheck/downloads"); - - // url to download the crate from crates.io - let url = format!("https://crates.io/api/v1/crates/{}/{}/download", name, version); - println!("Downloading and extracting {} {} from {}", name, version, url); - let _ = std::fs::create_dir("target/lintcheck/"); - let _ = std::fs::create_dir(&krate_download_dir); - let _ = std::fs::create_dir(&extract_dir); - - let krate_file_path = krate_download_dir.join(format!("{}-{}.crate.tar.gz", name, version)); - // don't download/extract if we already have done so - if !krate_file_path.is_file() { - // create a file path to download and write the crate data into - let mut krate_dest = std::fs::File::create(&krate_file_path).unwrap(); - let mut krate_req = ureq::get(&url).call().unwrap().into_reader(); - // copy the crate into the file - std::io::copy(&mut krate_req, &mut krate_dest).unwrap(); - - // unzip the tarball - let ungz_tar = flate2::read::GzDecoder::new(std::fs::File::open(&krate_file_path).unwrap()); - // extract the tar archive - let mut archive = tar::Archive::new(ungz_tar); - archive.unpack(&extract_dir).expect("Failed to extract!"); - } - // crate is extracted, return a new Krate object which contains the path to the extracted - // sources that clippy can check - Crate { - version: version.clone(), - name: name.clone(), - path: extract_dir.join(format!("{}-{}/", name, version)), - options: options.clone(), - } - }, - CrateSource::Git { - name, - url, - commit, - options, - } => { - let repo_path = { - let mut repo_path = PathBuf::from("target/lintcheck/crates"); - // add a -git suffix in case we have the same crate from crates.io and a git repo - repo_path.push(format!("{}-git", name)); - repo_path - }; - // clone the repo if we have not done so - if !repo_path.is_dir() { - println!("Cloning {} and checking out {}", url, commit); - if !Command::new("git") - .arg("clone") - .arg(url) - .arg(&repo_path) - .status() - .expect("Failed to clone git repo!") - .success() - { - eprintln!("Failed to clone {} into {}", url, repo_path.display()) - } - } - // check out the commit/branch/whatever - if !Command::new("git") - .arg("checkout") - .arg(commit) - .current_dir(&repo_path) - .status() - .expect("Failed to check out commit") - .success() - { - eprintln!("Failed to checkout {} of repo at {}", commit, repo_path.display()) - } - - Crate { - version: commit.clone(), - name: name.clone(), - path: repo_path, - options: options.clone(), - } - }, - CrateSource::Path { name, path, options } => { - use fs_extra::dir; - - // simply copy the entire directory into our target dir - let copy_dest = PathBuf::from("target/lintcheck/crates/"); - - // the source path of the crate we copied, ${copy_dest}/crate_name - let crate_root = copy_dest.join(name); // .../crates/local_crate - - if !crate_root.exists() { - println!("Copying {} to {}", path.display(), copy_dest.display()); - - dir::copy(path, ©_dest, &dir::CopyOptions::new()).expect(&format!( - "Failed to copy from {}, to {}", - path.display(), - crate_root.display() - )); - } else { - println!( - "Not copying {} to {}, destination already exists", - path.display(), - crate_root.display() - ); - } - - Crate { - version: String::from("local"), - name: name.clone(), - path: crate_root, - options: options.clone(), - } - }, - } - } -} - -impl Crate { - /// Run `cargo clippy` on the `Crate` and collect and return all the lint warnings that clippy - /// issued - fn run_clippy_lints( - &self, - cargo_clippy_path: &PathBuf, - target_dir_index: &AtomicUsize, - thread_limit: usize, - total_crates_to_lint: usize, - ) -> Vec<ClippyWarning> { - // advance the atomic index by one - let index = target_dir_index.fetch_add(1, Ordering::SeqCst); - // "loop" the index within 0..thread_limit - let target_dir_index = index % thread_limit; - let perc = ((index * 100) as f32 / total_crates_to_lint as f32) as u8; - - if thread_limit == 1 { - println!( - "{}/{} {}% Linting {} {}", - index, total_crates_to_lint, perc, &self.name, &self.version - ); - } else { - println!( - "{}/{} {}% Linting {} {} in target dir {:?}", - index, total_crates_to_lint, perc, &self.name, &self.version, target_dir_index - ); - } - - let cargo_clippy_path = std::fs::canonicalize(cargo_clippy_path).unwrap(); - - let shared_target_dir = clippy_project_root().join("target/lintcheck/shared_target_dir"); - - let mut args = vec!["--", "--message-format=json", "--", "--cap-lints=warn"]; - - if let Some(options) = &self.options { - for opt in options { - args.push(opt); - } - } else { - args.extend(&["-Wclippy::pedantic", "-Wclippy::cargo"]) - } - - let all_output = std::process::Command::new(&cargo_clippy_path) - // use the looping index to create individual target dirs - .env( - "CARGO_TARGET_DIR", - shared_target_dir.join(format!("_{:?}", target_dir_index)), - ) - // lint warnings will look like this: - // src/cargo/ops/cargo_compile.rs:127:35: warning: usage of `FromIterator::from_iter` - .args(&args) - .current_dir(&self.path) - .output() - .unwrap_or_else(|error| { - panic!( - "Encountered error:\n{:?}\ncargo_clippy_path: {}\ncrate path:{}\n", - error, - &cargo_clippy_path.display(), - &self.path.display() - ); - }); - let stdout = String::from_utf8_lossy(&all_output.stdout); - let output_lines = stdout.lines(); - let warnings: Vec<ClippyWarning> = output_lines - .into_iter() - // get all clippy warnings and ICEs - .filter(|line| filter_clippy_warnings(&line)) - .map(|json_msg| parse_json_message(json_msg, &self)) - .collect(); - warnings - } -} - -/// takes a single json-formatted clippy warnings and returns true (we are interested in that line) -/// or false (we aren't) -fn filter_clippy_warnings(line: &str) -> bool { - // we want to collect ICEs because clippy might have crashed. - // these are summarized later - if line.contains("internal compiler error: ") { - return true; - } - // in general, we want all clippy warnings - // however due to some kind of bug, sometimes there are absolute paths - // to libcore files inside the message - // or we end up with cargo-metadata output (https://github.com/rust-lang/rust-clippy/issues/6508) - - // filter out these message to avoid unnecessary noise in the logs - if line.contains("clippy::") - && !(line.contains("could not read cargo metadata") - || (line.contains(".rustup") && line.contains("toolchains"))) - { - return true; - } - false -} - -/// get the path to lintchecks crate sources .toml file, check LINTCHECK_TOML first but if it's -/// empty use the default path -fn lintcheck_config_toml(toml_path: Option<&str>) -> PathBuf { - PathBuf::from( - env::var("LINTCHECK_TOML").unwrap_or( - toml_path - .clone() - .unwrap_or("clippy_dev/lintcheck_crates.toml") - .to_string(), - ), - ) -} - -/// Builds clippy inside the repo to make sure we have a clippy executable we can use. -fn build_clippy() { - let status = Command::new("cargo") - .arg("build") - .status() - .expect("Failed to build clippy!"); - if !status.success() { - eprintln!("Error: Failed to compile Clippy!"); - std::process::exit(1); - } -} - -/// Read a `toml` file and return a list of `CrateSources` that we want to check with clippy -fn read_crates(toml_path: Option<&str>) -> (String, Vec<CrateSource>) { - let toml_path = lintcheck_config_toml(toml_path); - // save it so that we can use the name of the sources.toml as name for the logfile later. - let toml_filename = toml_path.file_stem().unwrap().to_str().unwrap().to_string(); - let toml_content: String = - std::fs::read_to_string(&toml_path).unwrap_or_else(|_| panic!("Failed to read {}", toml_path.display())); - let crate_list: SourceList = - toml::from_str(&toml_content).unwrap_or_else(|e| panic!("Failed to parse {}: \n{}", toml_path.display(), e)); - // parse the hashmap of the toml file into a list of crates - let tomlcrates: Vec<TomlCrate> = crate_list - .crates - .into_iter() - .map(|(_cratename, tomlcrate)| tomlcrate) - .collect(); - - // flatten TomlCrates into CrateSources (one TomlCrates may represent several versions of a crate => - // multiple Cratesources) - let mut crate_sources = Vec::new(); - tomlcrates.into_iter().for_each(|tk| { - if let Some(ref path) = tk.path { - crate_sources.push(CrateSource::Path { - name: tk.name.clone(), - path: PathBuf::from(path), - options: tk.options.clone(), - }); - } - - // if we have multiple versions, save each one - if let Some(ref versions) = tk.versions { - versions.iter().for_each(|ver| { - crate_sources.push(CrateSource::CratesIo { - name: tk.name.clone(), - version: ver.to_string(), - options: tk.options.clone(), - }); - }) - } - // otherwise, we should have a git source - if tk.git_url.is_some() && tk.git_hash.is_some() { - crate_sources.push(CrateSource::Git { - name: tk.name.clone(), - url: tk.git_url.clone().unwrap(), - commit: tk.git_hash.clone().unwrap(), - options: tk.options.clone(), - }); - } - // if we have a version as well as a git data OR only one git data, something is funky - if tk.versions.is_some() && (tk.git_url.is_some() || tk.git_hash.is_some()) - || tk.git_hash.is_some() != tk.git_url.is_some() - { - eprintln!("tomlkrate: {:?}", tk); - if tk.git_hash.is_some() != tk.git_url.is_some() { - panic!("Error: Encountered TomlCrate with only one of git_hash and git_url!"); - } - if tk.path.is_some() && (tk.git_hash.is_some() || tk.versions.is_some()) { - panic!("Error: TomlCrate can only have one of 'git_.*', 'version' or 'path' fields"); - } - unreachable!("Failed to translate TomlCrate into CrateSource!"); - } - }); - // sort the crates - crate_sources.sort(); - - (toml_filename, crate_sources) -} - -/// Parse the json output of clippy and return a `ClippyWarning` -fn parse_json_message(json_message: &str, krate: &Crate) -> ClippyWarning { - let jmsg: Value = serde_json::from_str(&json_message).unwrap_or_else(|e| panic!("Failed to parse json:\n{:?}", e)); - - ClippyWarning { - crate_name: krate.name.to_string(), - crate_version: krate.version.to_string(), - file: jmsg["message"]["spans"][0]["file_name"] - .to_string() - .trim_matches('"') - .into(), - line: jmsg["message"]["spans"][0]["line_start"] - .to_string() - .trim_matches('"') - .into(), - column: jmsg["message"]["spans"][0]["text"][0]["highlight_start"] - .to_string() - .trim_matches('"') - .into(), - linttype: jmsg["message"]["code"]["code"].to_string().trim_matches('"').into(), - message: jmsg["message"]["message"].to_string().trim_matches('"').into(), - is_ice: json_message.contains("internal compiler error: "), - } -} - -/// Generate a short list of occuring lints-types and their count -fn gather_stats(clippy_warnings: &[ClippyWarning]) -> String { - // count lint type occurrences - let mut counter: HashMap<&String, usize> = HashMap::new(); - clippy_warnings - .iter() - .for_each(|wrn| *counter.entry(&wrn.linttype).or_insert(0) += 1); - - // collect into a tupled list for sorting - let mut stats: Vec<(&&String, &usize)> = counter.iter().map(|(lint, count)| (lint, count)).collect(); - // sort by "000{count} {clippy::lintname}" - // to not have a lint with 200 and 2 warnings take the same spot - stats.sort_by_key(|(lint, count)| format!("{:0>4}, {}", count, lint)); - - stats - .iter() - .map(|(lint, count)| format!("{} {}\n", lint, count)) - .collect::<String>() -} - -/// check if the latest modification of the logfile is older than the modification date of the -/// clippy binary, if this is true, we should clean the lintchec shared target directory and recheck -fn lintcheck_needs_rerun(toml_path: Option<&str>) -> bool { - let clippy_modified: std::time::SystemTime = { - let mut times = ["target/debug/clippy-driver", "target/debug/cargo-clippy"] - .iter() - .map(|p| { - std::fs::metadata(p) - .expect("failed to get metadata of file") - .modified() - .expect("failed to get modification date") - }); - // the lates modification of either of the binaries - std::cmp::max(times.next().unwrap(), times.next().unwrap()) - }; - - let logs_modified: std::time::SystemTime = std::fs::metadata(lintcheck_config_toml(toml_path)) - .expect("failed to get metadata of file") - .modified() - .expect("failed to get modification date"); - - // if clippys modification time is bigger (older) than the logs mod time, we need to rerun lintcheck - clippy_modified > logs_modified -} - -/// lintchecks `main()` function -pub fn run(clap_config: &ArgMatches) { - println!("Compiling clippy..."); - build_clippy(); - println!("Done compiling"); - - let clap_toml_path = clap_config.value_of("crates-toml"); - - // if the clippy bin is newer than our logs, throw away target dirs to force clippy to - // refresh the logs - if lintcheck_needs_rerun(clap_toml_path) { - let shared_target_dir = "target/lintcheck/shared_target_dir"; - match std::fs::metadata(&shared_target_dir) { - Ok(metadata) => { - if metadata.is_dir() { - println!("Clippy is newer than lint check logs, clearing lintcheck shared target dir..."); - std::fs::remove_dir_all(&shared_target_dir) - .expect("failed to remove target/lintcheck/shared_target_dir"); - } - }, - Err(_) => { // dir probably does not exist, don't remove anything - }, - } - } - - let cargo_clippy_path: PathBuf = PathBuf::from("target/debug/cargo-clippy") - .canonicalize() - .expect("failed to canonicalize path to clippy binary"); - - // assert that clippy is found - assert!( - cargo_clippy_path.is_file(), - "target/debug/cargo-clippy binary not found! {}", - cargo_clippy_path.display() - ); - - let clippy_ver = std::process::Command::new("target/debug/cargo-clippy") - .arg("--version") - .output() - .map(|o| String::from_utf8_lossy(&o.stdout).into_owned()) - .expect("could not get clippy version!"); - - // download and extract the crates, then run clippy on them and collect clippys warnings - // flatten into one big list of warnings - - let (filename, crates) = read_crates(clap_toml_path); - - let clippy_warnings: Vec<ClippyWarning> = if let Some(only_one_crate) = clap_config.value_of("only") { - // if we don't have the specified crate in the .toml, throw an error - if !crates.iter().any(|krate| { - let name = match krate { - CrateSource::CratesIo { name, .. } => name, - CrateSource::Git { name, .. } => name, - CrateSource::Path { name, .. } => name, - }; - name == only_one_crate - }) { - eprintln!( - "ERROR: could not find crate '{}' in clippy_dev/lintcheck_crates.toml", - only_one_crate - ); - std::process::exit(1); - } - - // only check a single crate that was passed via cmdline - crates - .into_iter() - .map(|krate| krate.download_and_extract()) - .filter(|krate| krate.name == only_one_crate) - .map(|krate| krate.run_clippy_lints(&cargo_clippy_path, &AtomicUsize::new(0), 1, 1)) - .flatten() - .collect() - } else { - let counter = std::sync::atomic::AtomicUsize::new(0); - - // Ask rayon for thread count. Assume that half of that is the number of physical cores - // Use one target dir for each core so that we can run N clippys in parallel. - // We need to use different target dirs because cargo would lock them for a single build otherwise, - // killing the parallelism. However this also means that deps will only be reused half/a - // quarter of the time which might result in a longer wall clock runtime - - // This helps when we check many small crates with dep-trees that don't have a lot of branches in - // order to achive some kind of parallelism - - // by default, use a single thread - let num_cpus = match clap_config.value_of("threads") { - Some(threads) => { - let threads: usize = threads - .parse() - .expect(&format!("Failed to parse '{}' to a digit", threads)); - if threads == 0 { - // automatic choice - // Rayon seems to return thread count so half that for core count - (rayon::current_num_threads() / 2) as usize - } else { - threads - } - }, - // no -j passed, use a single thread - None => 1, - }; - - let num_crates = crates.len(); - - // check all crates (default) - crates - .into_par_iter() - .map(|krate| krate.download_and_extract()) - .map(|krate| krate.run_clippy_lints(&cargo_clippy_path, &counter, num_cpus, num_crates)) - .flatten() - .collect() - }; - - // generate some stats - let stats_formatted = gather_stats(&clippy_warnings); - - // grab crashes/ICEs, save the crate name and the ice message - let ices: Vec<(&String, &String)> = clippy_warnings - .iter() - .filter(|warning| warning.is_ice) - .map(|w| (&w.crate_name, &w.message)) - .collect(); - - let mut all_msgs: Vec<String> = clippy_warnings.iter().map(|warning| warning.to_string()).collect(); - all_msgs.sort(); - all_msgs.push("\n\n\n\nStats\n\n".into()); - all_msgs.push(stats_formatted); - - // save the text into lintcheck-logs/logs.txt - let mut text = clippy_ver; // clippy version number on top - text.push_str(&format!("\n{}", all_msgs.join(""))); - text.push_str("ICEs:\n"); - ices.iter() - .for_each(|(cratename, msg)| text.push_str(&format!("{}: '{}'", cratename, msg))); - - let file = format!("lintcheck-logs/{}_logs.txt", filename); - println!("Writing logs to {}", file); - write(file, text).unwrap(); -} diff --git a/src/tools/clippy/clippy_dev/src/main.rs b/src/tools/clippy/clippy_dev/src/main.rs index 505d465760c57..f4da783502c5f 100644 --- a/src/tools/clippy/clippy_dev/src/main.rs +++ b/src/tools/clippy/clippy_dev/src/main.rs @@ -1,11 +1,7 @@ #![cfg_attr(feature = "deny-warnings", deny(warnings))] use clap::{App, Arg, ArgMatches, SubCommand}; -use clippy_dev::{bless, fmt, new_lint, ra_setup, serve, stderr_length_check, update_lints}; - -#[cfg(feature = "lintcheck")] -use clippy_dev::lintcheck; - +use clippy_dev::{bless, fmt, ide_setup, new_lint, serve, stderr_length_check, update_lints}; fn main() { let matches = get_clap_config(); @@ -13,10 +9,6 @@ fn main() { ("bless", Some(matches)) => { bless::bless(matches.is_present("ignore-timestamp")); }, - #[cfg(feature = "lintcheck")] - ("lintcheck", Some(matches)) => { - lintcheck::run(&matches); - }, ("fmt", Some(matches)) => { fmt::run(matches.is_present("check"), matches.is_present("verbose")); }, @@ -42,7 +34,7 @@ fn main() { ("limit_stderr_length", _) => { stderr_length_check::check(); }, - ("ra_setup", Some(matches)) => ra_setup::run(matches.value_of("rustc-repo-path")), + ("ide_setup", Some(matches)) => ide_setup::run(matches.value_of("rustc-repo-path")), ("serve", Some(matches)) => { let port = matches.value_of("port").unwrap().parse().unwrap(); let lint = matches.value_of("lint"); @@ -53,33 +45,7 @@ fn main() { } fn get_clap_config<'a>() -> ArgMatches<'a> { - #[cfg(feature = "lintcheck")] - let lintcheck_sbcmd = SubCommand::with_name("lintcheck") - .about("run clippy on a set of crates and check output") - .arg( - Arg::with_name("only") - .takes_value(true) - .value_name("CRATE") - .long("only") - .help("only process a single crate of the list"), - ) - .arg( - Arg::with_name("crates-toml") - .takes_value(true) - .value_name("CRATES-SOURCES-TOML-PATH") - .long("crates-toml") - .help("set the path for a crates.toml where lintcheck should read the sources from"), - ) - .arg( - Arg::with_name("threads") - .takes_value(true) - .value_name("N") - .short("j") - .long("jobs") - .help("number of threads to use, 0 automatic choice"), - ); - - let app = App::new("Clippy developer tooling") + App::new("Clippy developer tooling") .subcommand( SubCommand::with_name("bless") .about("bless the test output changes") @@ -172,8 +138,8 @@ fn get_clap_config<'a>() -> ArgMatches<'a> { .about("Ensures that stderr files do not grow longer than a certain amount of lines."), ) .subcommand( - SubCommand::with_name("ra_setup") - .about("Alter dependencies so rust-analyzer can find rustc internals") + SubCommand::with_name("ide_setup") + .about("Alter dependencies so Intellij Rust can find rustc internals") .arg( Arg::with_name("rustc-repo-path") .long("repo-path") @@ -196,10 +162,6 @@ fn get_clap_config<'a>() -> ArgMatches<'a> { .validator_os(serve::validate_port), ) .arg(Arg::with_name("lint").help("Which lint's page to load initially (optional)")), - ); - - #[cfg(feature = "lintcheck")] - let app = app.subcommand(lintcheck_sbcmd); - - app.get_matches() + ) + .get_matches() } diff --git a/src/tools/clippy/clippy_dev/src/ra_setup.rs b/src/tools/clippy/clippy_dev/src/ra_setup.rs deleted file mode 100644 index d0e2193ddc5e5..0000000000000 --- a/src/tools/clippy/clippy_dev/src/ra_setup.rs +++ /dev/null @@ -1,103 +0,0 @@ -use std::fs; -use std::fs::File; -use std::io::prelude::*; -use std::path::{Path, PathBuf}; - -// This module takes an absolute path to a rustc repo and alters the dependencies to point towards -// the respective rustc subcrates instead of using extern crate xyz. -// This allows rust analyzer to analyze rustc internals and show proper information inside clippy -// code. See https://github.com/rust-analyzer/rust-analyzer/issues/3517 and https://github.com/rust-lang/rust-clippy/issues/5514 for details - -/// # Panics -/// -/// Panics if `rustc_path` does not lead to a rustc repo or the files could not be read -pub fn run(rustc_path: Option<&str>) { - // we can unwrap here because the arg is required by clap - let rustc_path = PathBuf::from(rustc_path.unwrap()) - .canonicalize() - .expect("failed to get the absolute repo path"); - assert!(rustc_path.is_dir(), "path is not a directory"); - let rustc_source_basedir = rustc_path.join("compiler"); - assert!( - rustc_source_basedir.is_dir(), - "are you sure the path leads to a rustc repo?" - ); - - let clippy_root_manifest = fs::read_to_string("Cargo.toml").expect("failed to read ./Cargo.toml"); - let clippy_root_lib_rs = fs::read_to_string("src/driver.rs").expect("failed to read ./src/driver.rs"); - inject_deps_into_manifest( - &rustc_source_basedir, - "Cargo.toml", - &clippy_root_manifest, - &clippy_root_lib_rs, - ) - .expect("Failed to inject deps into ./Cargo.toml"); - - let clippy_lints_manifest = - fs::read_to_string("clippy_lints/Cargo.toml").expect("failed to read ./clippy_lints/Cargo.toml"); - let clippy_lints_lib_rs = - fs::read_to_string("clippy_lints/src/lib.rs").expect("failed to read ./clippy_lints/src/lib.rs"); - inject_deps_into_manifest( - &rustc_source_basedir, - "clippy_lints/Cargo.toml", - &clippy_lints_manifest, - &clippy_lints_lib_rs, - ) - .expect("Failed to inject deps into ./clippy_lints/Cargo.toml"); -} - -fn inject_deps_into_manifest( - rustc_source_dir: &Path, - manifest_path: &str, - cargo_toml: &str, - lib_rs: &str, -) -> std::io::Result<()> { - // do not inject deps if we have aleady done so - if cargo_toml.contains("[target.'cfg(NOT_A_PLATFORM)'.dependencies]") { - eprintln!( - "cargo dev ra_setup: warning: deps already found inside {}, doing nothing.", - manifest_path - ); - return Ok(()); - } - - let extern_crates = lib_rs - .lines() - // get the deps - .filter(|line| line.starts_with("extern crate")) - // we have something like "extern crate foo;", we only care about the "foo" - // ↓ ↓ - // extern crate rustc_middle; - .map(|s| &s[13..(s.len() - 1)]); - - let new_deps = extern_crates.map(|dep| { - // format the dependencies that are going to be put inside the Cargo.toml - format!( - "{dep} = {{ path = \"{source_path}/{dep}\" }}\n", - dep = dep, - source_path = rustc_source_dir.display() - ) - }); - - // format a new [dependencies]-block with the new deps we need to inject - let mut all_deps = String::from("[target.'cfg(NOT_A_PLATFORM)'.dependencies]\n"); - new_deps.for_each(|dep_line| { - all_deps.push_str(&dep_line); - }); - all_deps.push_str("\n[dependencies]\n"); - - // replace "[dependencies]" with - // [dependencies] - // dep1 = { path = ... } - // dep2 = { path = ... } - // etc - let new_manifest = cargo_toml.replacen("[dependencies]\n", &all_deps, 1); - - // println!("{}", new_manifest); - let mut file = File::create(manifest_path)?; - file.write_all(new_manifest.as_bytes())?; - - println!("Dependency paths injected: {}", manifest_path); - - Ok(()) -} diff --git a/src/tools/clippy/clippy_dummy/Cargo.toml b/src/tools/clippy/clippy_dummy/Cargo.toml index 7b11795fafdc5..6959de7ffee71 100644 --- a/src/tools/clippy/clippy_dummy/Cargo.toml +++ b/src/tools/clippy/clippy_dummy/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "clippy_dummy" # rename to clippy before publishing version = "0.0.303" -authors = ["Manish Goregaokar <manishsmail@gmail.com>"] +authors = ["The Rust Clippy Developers"] edition = "2018" readme = "crates-readme.md" description = "A bunch of helpful lints to avoid common pitfalls in Rust." diff --git a/src/tools/clippy/clippy_lints/Cargo.toml b/src/tools/clippy/clippy_lints/Cargo.toml index d5ec8597044c8..05cdd9d064a8e 100644 --- a/src/tools/clippy/clippy_lints/Cargo.toml +++ b/src/tools/clippy/clippy_lints/Cargo.toml @@ -1,14 +1,9 @@ [package] name = "clippy_lints" # begin automatic update -version = "0.1.52" +version = "0.1.53" # end automatic update -authors = [ - "Manish Goregaokar <manishsmail@gmail.com>", - "Andre Bogus <bogusandre@gmail.com>", - "Georg Brandl <georg@python.org>", - "Martin Carton <cartonmartin@gmail.com>" -] +authors = ["The Rust Clippy Developers"] description = "A bunch of helpful lints to avoid common pitfalls in Rust" repository = "https://github.com/rust-lang/rust-clippy" readme = "README.md" @@ -25,18 +20,19 @@ pulldown-cmark = { version = "0.8", default-features = false } quine-mc_cluskey = "0.2.2" regex-syntax = "0.6" serde = { version = "1.0", features = ["derive"] } -smallvec = { version = "1", features = ["union"] } toml = "0.5.3" unicode-normalization = "0.1" semver = "0.11" -rustc-semver="1.1.0" +rustc-semver = "1.1.0" # NOTE: cargo requires serde feat in its url dep # see <https://github.com/rust-lang/rust/pull/63587#issuecomment-522343864> -url = { version = "2.1.0", features = ["serde"] } -quote = "1" -syn = { version = "1", features = ["full"] } +url = { version = "2.1.0", features = ["serde"] } [features] deny-warnings = [] # build clippy with internal lints enabled, off by default internal-lints = ["clippy_utils/internal-lints"] + +[package.metadata.rust-analyzer] +# This crate uses #[feature(rustc_private)] +rustc_private = true diff --git a/src/tools/clippy/clippy_lints/src/absurd_extreme_comparisons.rs b/src/tools/clippy/clippy_lints/src/absurd_extreme_comparisons.rs new file mode 100644 index 0000000000000..5fbf4bdbd187b --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/absurd_extreme_comparisons.rs @@ -0,0 +1,173 @@ +use rustc_hir::{BinOpKind, Expr, ExprKind}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::ty; +use rustc_session::{declare_lint_pass, declare_tool_lint}; + +use crate::consts::{constant, Constant}; + +use clippy_utils::comparisons::{normalize_comparison, Rel}; +use clippy_utils::diagnostics::span_lint_and_help; +use clippy_utils::source::snippet; +use clippy_utils::ty::is_isize_or_usize; +use clippy_utils::{clip, int_bits, unsext}; + +declare_clippy_lint! { + /// **What it does:** Checks for comparisons where one side of the relation is + /// either the minimum or maximum value for its type and warns if it involves a + /// case that is always true or always false. Only integer and boolean types are + /// checked. + /// + /// **Why is this bad?** An expression like `min <= x` may misleadingly imply + /// that it is possible for `x` to be less than the minimum. Expressions like + /// `max < x` are probably mistakes. + /// + /// **Known problems:** For `usize` the size of the current compile target will + /// be assumed (e.g., 64 bits on 64 bit systems). This means code that uses such + /// a comparison to detect target pointer width will trigger this lint. One can + /// use `mem::sizeof` and compare its value or conditional compilation + /// attributes + /// like `#[cfg(target_pointer_width = "64")] ..` instead. + /// + /// **Example:** + /// + /// ```rust + /// let vec: Vec<isize> = Vec::new(); + /// if vec.len() <= 0 {} + /// if 100 > i32::MAX {} + /// ``` + pub ABSURD_EXTREME_COMPARISONS, + correctness, + "a comparison with a maximum or minimum value that is always true or false" +} + +declare_lint_pass!(AbsurdExtremeComparisons => [ABSURD_EXTREME_COMPARISONS]); + +impl<'tcx> LateLintPass<'tcx> for AbsurdExtremeComparisons { + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { + if let ExprKind::Binary(ref cmp, lhs, rhs) = expr.kind { + if let Some((culprit, result)) = detect_absurd_comparison(cx, cmp.node, lhs, rhs) { + if !expr.span.from_expansion() { + let msg = "this comparison involving the minimum or maximum element for this \ + type contains a case that is always true or always false"; + + let conclusion = match result { + AbsurdComparisonResult::AlwaysFalse => "this comparison is always false".to_owned(), + AbsurdComparisonResult::AlwaysTrue => "this comparison is always true".to_owned(), + AbsurdComparisonResult::InequalityImpossible => format!( + "the case where the two sides are not equal never occurs, consider using `{} == {}` \ + instead", + snippet(cx, lhs.span, "lhs"), + snippet(cx, rhs.span, "rhs") + ), + }; + + let help = format!( + "because `{}` is the {} value for this type, {}", + snippet(cx, culprit.expr.span, "x"), + match culprit.which { + ExtremeType::Minimum => "minimum", + ExtremeType::Maximum => "maximum", + }, + conclusion + ); + + span_lint_and_help(cx, ABSURD_EXTREME_COMPARISONS, expr.span, msg, None, &help); + } + } + } + } +} + +enum ExtremeType { + Minimum, + Maximum, +} + +struct ExtremeExpr<'a> { + which: ExtremeType, + expr: &'a Expr<'a>, +} + +enum AbsurdComparisonResult { + AlwaysFalse, + AlwaysTrue, + InequalityImpossible, +} + +fn is_cast_between_fixed_and_target<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> bool { + if let ExprKind::Cast(cast_exp, _) = expr.kind { + let precast_ty = cx.typeck_results().expr_ty(cast_exp); + let cast_ty = cx.typeck_results().expr_ty(expr); + + return is_isize_or_usize(precast_ty) != is_isize_or_usize(cast_ty); + } + + false +} + +fn detect_absurd_comparison<'tcx>( + cx: &LateContext<'tcx>, + op: BinOpKind, + lhs: &'tcx Expr<'_>, + rhs: &'tcx Expr<'_>, +) -> Option<(ExtremeExpr<'tcx>, AbsurdComparisonResult)> { + use AbsurdComparisonResult::{AlwaysFalse, AlwaysTrue, InequalityImpossible}; + use ExtremeType::{Maximum, Minimum}; + // absurd comparison only makes sense on primitive types + // primitive types don't implement comparison operators with each other + if cx.typeck_results().expr_ty(lhs) != cx.typeck_results().expr_ty(rhs) { + return None; + } + + // comparisons between fix sized types and target sized types are considered unanalyzable + if is_cast_between_fixed_and_target(cx, lhs) || is_cast_between_fixed_and_target(cx, rhs) { + return None; + } + + let (rel, normalized_lhs, normalized_rhs) = normalize_comparison(op, lhs, rhs)?; + + let lx = detect_extreme_expr(cx, normalized_lhs); + let rx = detect_extreme_expr(cx, normalized_rhs); + + Some(match rel { + Rel::Lt => { + match (lx, rx) { + (Some(l @ ExtremeExpr { which: Maximum, .. }), _) => (l, AlwaysFalse), // max < x + (_, Some(r @ ExtremeExpr { which: Minimum, .. })) => (r, AlwaysFalse), // x < min + _ => return None, + } + }, + Rel::Le => { + match (lx, rx) { + (Some(l @ ExtremeExpr { which: Minimum, .. }), _) => (l, AlwaysTrue), // min <= x + (Some(l @ ExtremeExpr { which: Maximum, .. }), _) => (l, InequalityImpossible), // max <= x + (_, Some(r @ ExtremeExpr { which: Minimum, .. })) => (r, InequalityImpossible), // x <= min + (_, Some(r @ ExtremeExpr { which: Maximum, .. })) => (r, AlwaysTrue), // x <= max + _ => return None, + } + }, + Rel::Ne | Rel::Eq => return None, + }) +} + +fn detect_extreme_expr<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option<ExtremeExpr<'tcx>> { + let ty = cx.typeck_results().expr_ty(expr); + + let cv = constant(cx, cx.typeck_results(), expr)?.0; + + let which = match (ty.kind(), cv) { + (&ty::Bool, Constant::Bool(false)) | (&ty::Uint(_), Constant::Int(0)) => ExtremeType::Minimum, + (&ty::Int(ity), Constant::Int(i)) if i == unsext(cx.tcx, i128::MIN >> (128 - int_bits(cx.tcx, ity)), ity) => { + ExtremeType::Minimum + }, + + (&ty::Bool, Constant::Bool(true)) => ExtremeType::Maximum, + (&ty::Int(ity), Constant::Int(i)) if i == unsext(cx.tcx, i128::MAX >> (128 - int_bits(cx.tcx, ity)), ity) => { + ExtremeType::Maximum + }, + (&ty::Uint(uty), Constant::Int(i)) if clip(cx.tcx, u128::MAX, uty) == i => ExtremeType::Maximum, + + _ => return None, + }; + Some(ExtremeExpr { which, expr }) +} diff --git a/src/tools/clippy/clippy_lints/src/approx_const.rs b/src/tools/clippy/clippy_lints/src/approx_const.rs index 1d511a86c9099..3d04abe094d78 100644 --- a/src/tools/clippy/clippy_lints/src/approx_const.rs +++ b/src/tools/clippy/clippy_lints/src/approx_const.rs @@ -1,4 +1,4 @@ -use crate::utils::span_lint; +use clippy_utils::diagnostics::span_lint; use rustc_ast::ast::{FloatTy, LitFloatType, LitKind}; use rustc_hir::{Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; diff --git a/src/tools/clippy/clippy_lints/src/arithmetic.rs b/src/tools/clippy/clippy_lints/src/arithmetic.rs index 61fdf9495b918..c560f545d6a61 100644 --- a/src/tools/clippy/clippy_lints/src/arithmetic.rs +++ b/src/tools/clippy/clippy_lints/src/arithmetic.rs @@ -1,5 +1,5 @@ use crate::consts::constant_simple; -use crate::utils::span_lint; +use clippy_utils::diagnostics::span_lint; use rustc_hir as hir; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_tool_lint, impl_lint_pass}; diff --git a/src/tools/clippy/clippy_lints/src/as_conversions.rs b/src/tools/clippy/clippy_lints/src/as_conversions.rs index c30d65bbc5704..4b31e16094e9f 100644 --- a/src/tools/clippy/clippy_lints/src/as_conversions.rs +++ b/src/tools/clippy/clippy_lints/src/as_conversions.rs @@ -1,10 +1,9 @@ +use clippy_utils::diagnostics::span_lint_and_help; use rustc_ast::ast::{Expr, ExprKind}; use rustc_lint::{EarlyContext, EarlyLintPass, LintContext}; use rustc_middle::lint::in_external_macro; use rustc_session::{declare_lint_pass, declare_tool_lint}; -use crate::utils::span_lint_and_help; - declare_clippy_lint! { /// **What it does:** Checks for usage of `as` conversions. /// diff --git a/src/tools/clippy/clippy_lints/src/asm_syntax.rs b/src/tools/clippy/clippy_lints/src/asm_syntax.rs index ef1f1a14afcac..b970c71b753ce 100644 --- a/src/tools/clippy/clippy_lints/src/asm_syntax.rs +++ b/src/tools/clippy/clippy_lints/src/asm_syntax.rs @@ -1,6 +1,6 @@ use std::fmt; -use crate::utils::span_lint_and_help; +use clippy_utils::diagnostics::span_lint_and_help; use rustc_ast::ast::{Expr, ExprKind, InlineAsmOptions}; use rustc_lint::{EarlyContext, EarlyLintPass, Lint}; use rustc_session::{declare_lint_pass, declare_tool_lint}; 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 77b26faaa586a..a0993bb6913e7 100644 --- a/src/tools/clippy/clippy_lints/src/assertions_on_constants.rs +++ b/src/tools/clippy/clippy_lints/src/assertions_on_constants.rs @@ -1,5 +1,7 @@ use crate::consts::{constant, Constant}; -use crate::utils::{is_direct_expn_of, is_expn_of, match_panic_call, snippet_opt, span_lint_and_help}; +use clippy_utils::diagnostics::span_lint_and_help; +use clippy_utils::source::snippet_opt; +use clippy_utils::{is_direct_expn_of, is_expn_of, match_panic_call}; use if_chain::if_chain; use rustc_hir::{Expr, ExprKind, UnOp}; use rustc_lint::{LateContext, LateLintPass}; @@ -69,7 +71,7 @@ impl<'tcx> LateLintPass<'tcx> for AssertionsOnConstants { return; } if_chain! { - if let ExprKind::Unary(_, ref lit) = e.kind; + if let ExprKind::Unary(_, lit) = e.kind; if let Some((Constant::Bool(is_true), _)) = constant(cx, cx.typeck_results(), lit); if is_true; then { @@ -80,7 +82,7 @@ impl<'tcx> LateLintPass<'tcx> for AssertionsOnConstants { if assert_span.from_expansion() { return; } - if let Some(assert_match) = match_assert_with_message(&cx, e) { + 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(), @@ -111,17 +113,17 @@ enum AssertKind { /// 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 ExprKind::If(ref cond, ref then, _) = expr.kind; - if let ExprKind::Unary(UnOp::Not, ref expr) = cond.kind; + if let ExprKind::If(cond, then, _) = expr.kind; + 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); // block - if let ExprKind::Block(ref block, _) = then.kind; + if let ExprKind::Block(block, _) = then.kind; if block.stmts.is_empty(); if let Some(block_expr) = &block.expr; // inner block is optional. unwrap it if it exists, or use the expression as is otherwise. if let Some(begin_panic_call) = match block_expr.kind { - ExprKind::Block(ref inner_block, _) => &inner_block.expr, + ExprKind::Block(inner_block, _) => &inner_block.expr, _ => &block.expr, }; // function call diff --git a/src/tools/clippy/clippy_lints/src/assign_ops.rs b/src/tools/clippy/clippy_lints/src/assign_ops.rs index b3185b8884014..bc6eec0051a41 100644 --- a/src/tools/clippy/clippy_lints/src/assign_ops.rs +++ b/src/tools/clippy/clippy_lints/src/assign_ops.rs @@ -1,7 +1,8 @@ -use crate::utils::{ - eq_expr_value, get_trait_def_id, implements_trait, snippet_opt, span_lint_and_then, trait_ref_of_method, -}; -use crate::utils::{higher, sugg}; +use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::source::snippet_opt; +use clippy_utils::ty::implements_trait; +use clippy_utils::{eq_expr_value, get_trait_def_id, trait_ref_of_method}; +use clippy_utils::{higher, paths, sugg}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir as hir; @@ -92,7 +93,7 @@ impl<'tcx> LateLintPass<'tcx> for AssignOps { $($trait_name:ident),+) => { match $op { $(hir::BinOpKind::$trait_name => { - let [krate, module] = crate::utils::paths::OPS_MODULE; + let [krate, module] = paths::OPS_MODULE; let path: [&str; 3] = [krate, module, concat!(stringify!($trait_name), "Assign")]; let trait_id = if let Some(trait_id) = get_trait_def_id($cx, &path) { trait_id @@ -209,7 +210,7 @@ fn lint_misrefactored_assign_op( diag.span_suggestion( expr.span, &format!( - "Did you mean `{} = {} {} {}` or `{}`? Consider replacing it with", + "did you mean `{} = {} {} {}` or `{}`? Consider replacing it with", snip_a, snip_a, op.node.as_str(), diff --git a/src/tools/clippy/clippy_lints/src/async_yields_async.rs b/src/tools/clippy/clippy_lints/src/async_yields_async.rs index 88d9d3b5a263d..e6c7c68f91a07 100644 --- a/src/tools/clippy/clippy_lints/src/async_yields_async.rs +++ b/src/tools/clippy/clippy_lints/src/async_yields_async.rs @@ -1,4 +1,6 @@ -use crate::utils::{implements_trait, snippet, span_lint_and_then}; +use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::source::snippet; +use clippy_utils::ty::implements_trait; use rustc_errors::Applicability; use rustc_hir::{AsyncGeneratorKind, Body, BodyId, ExprKind, GeneratorKind, QPath}; use rustc_lint::{LateContext, LateLintPass}; @@ -50,8 +52,7 @@ impl<'tcx> LateLintPass<'tcx> for AsyncYieldsAsync { let body_id = BodyId { hir_id: body.value.hir_id, }; - let def_id = cx.tcx.hir().body_owner_def_id(body_id); - let typeck_results = cx.tcx.typeck(def_id); + let typeck_results = cx.tcx.typeck_body(body_id); let expr_ty = typeck_results.expr_ty(&body.value); if implements_trait(cx, expr_ty, future_trait_def_id, &[]) { diff --git a/src/tools/clippy/clippy_lints/src/atomic_ordering.rs b/src/tools/clippy/clippy_lints/src/atomic_ordering.rs index 703d8a6f62bb1..7ceb01f5590fc 100644 --- a/src/tools/clippy/clippy_lints/src/atomic_ordering.rs +++ b/src/tools/clippy/clippy_lints/src/atomic_ordering.rs @@ -1,4 +1,5 @@ -use crate::utils::{match_def_path, span_lint_and_help}; +use clippy_utils::diagnostics::span_lint_and_help; +use clippy_utils::match_def_path; use if_chain::if_chain; use rustc_hir::def_id::DefId; use rustc_hir::{Expr, ExprKind}; @@ -84,7 +85,7 @@ fn match_ordering_def_path(cx: &LateContext<'_>, did: DefId, orderings: &[&str]) fn check_atomic_load_store(cx: &LateContext<'_>, expr: &Expr<'_>) { if_chain! { - if let ExprKind::MethodCall(ref method_path, _, args, _) = &expr.kind; + if let ExprKind::MethodCall(method_path, _, args, _) = &expr.kind; let method = method_path.ident.name.as_str(); if type_is_atomic(cx, &args[0]); if method == "load" || method == "store"; @@ -119,7 +120,7 @@ fn check_atomic_load_store(cx: &LateContext<'_>, expr: &Expr<'_>) { fn check_memory_fence(cx: &LateContext<'_>, expr: &Expr<'_>) { if_chain! { - if let ExprKind::Call(ref func, ref args) = expr.kind; + if let ExprKind::Call(func, args) = expr.kind; if let ExprKind::Path(ref func_qpath) = func.kind; if let Some(def_id) = cx.qpath_res(func_qpath, func.hir_id).opt_def_id(); if ["fence", "compiler_fence"] @@ -151,7 +152,7 @@ fn opt_ordering_defid(cx: &LateContext<'_>, ord_arg: &Expr<'_>) -> Option<DefId> fn check_atomic_compare_exchange(cx: &LateContext<'_>, expr: &Expr<'_>) { if_chain! { - if let ExprKind::MethodCall(ref method_path, _, args, _) = &expr.kind; + if let ExprKind::MethodCall(method_path, _, args, _) = &expr.kind; let method = method_path.ident.name.as_str(); if type_is_atomic(cx, &args[0]); if method == "compare_exchange" || method == "compare_exchange_weak" || method == "fetch_update"; diff --git a/src/tools/clippy/clippy_lints/src/attrs.rs b/src/tools/clippy/clippy_lints/src/attrs.rs index 652d1fa16b6de..c5b01461c1c00 100644 --- a/src/tools/clippy/clippy_lints/src/attrs.rs +++ b/src/tools/clippy/clippy_lints/src/attrs.rs @@ -1,9 +1,8 @@ //! checks for attributes -use crate::utils::{ - first_line_of_span, is_present_in_source, match_panic_def_id, snippet_opt, span_lint, span_lint_and_help, - span_lint_and_sugg, span_lint_and_then, without_block_comments, -}; +use clippy_utils::diagnostics::{span_lint, span_lint_and_help, span_lint_and_sugg, span_lint_and_then}; +use clippy_utils::match_panic_def_id; +use clippy_utils::source::{first_line_of_span, is_present_in_source, snippet_opt, without_block_comments}; use if_chain::if_chain; use rustc_ast::{AttrKind, AttrStyle, Attribute, Lit, LitKind, MetaItemKind, NestedMetaItem}; use rustc_errors::Applicability; @@ -251,12 +250,8 @@ impl<'tcx> LateLintPass<'tcx> for Attributes { fn check_attribute(&mut self, cx: &LateContext<'tcx>, attr: &'tcx Attribute) { if let Some(items) = &attr.meta_item_list() { if let Some(ident) = attr.ident() { - let ident = &*ident.as_str(); - match ident { - "allow" | "warn" | "deny" | "forbid" => { - check_clippy_lint_names(cx, ident, items); - }, - _ => {}, + if is_lint_level(ident.name) { + check_clippy_lint_names(cx, ident.name, items); } if items.is_empty() || !attr.has_name(sym::deprecated) { return; @@ -276,72 +271,67 @@ impl<'tcx> LateLintPass<'tcx> for Attributes { } fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) { + let attrs = cx.tcx.hir().attrs(item.hir_id()); if is_relevant_item(cx, item) { - check_attrs(cx, item.span, item.ident.name, &item.attrs) + check_attrs(cx, item.span, item.ident.name, attrs) } match item.kind { ItemKind::ExternCrate(..) | ItemKind::Use(..) => { - let skip_unused_imports = item.attrs.iter().any(|attr| attr.has_name(sym::macro_use)); + let skip_unused_imports = attrs.iter().any(|attr| attr.has_name(sym::macro_use)); - for attr in item.attrs { + for attr in attrs { if in_external_macro(cx.sess(), attr.span) { return; } if let Some(lint_list) = &attr.meta_item_list() { - if let Some(ident) = attr.ident() { - match &*ident.as_str() { - "allow" | "warn" | "deny" | "forbid" => { - // permit `unused_imports`, `deprecated`, `unreachable_pub`, - // `clippy::wildcard_imports`, and `clippy::enum_glob_use` for `use` items - // and `unused_imports` for `extern crate` items with `macro_use` - for lint in lint_list { - match item.kind { - ItemKind::Use(..) => { - if is_word(lint, sym!(unused_imports)) - || is_word(lint, sym::deprecated) - || is_word(lint, sym!(unreachable_pub)) - || is_word(lint, sym!(unused)) - || extract_clippy_lint(lint) - .map_or(false, |s| s == "wildcard_imports") - || extract_clippy_lint(lint).map_or(false, |s| s == "enum_glob_use") - { - return; - } - }, - ItemKind::ExternCrate(..) => { - if is_word(lint, sym!(unused_imports)) && skip_unused_imports { - return; - } - if is_word(lint, sym!(unused_extern_crates)) { - return; - } - }, - _ => {}, + if attr.ident().map_or(false, |ident| is_lint_level(ident.name)) { + // permit `unused_imports`, `deprecated`, `unreachable_pub`, + // `clippy::wildcard_imports`, and `clippy::enum_glob_use` for `use` items + // and `unused_imports` for `extern crate` items with `macro_use` + for lint in lint_list { + match item.kind { + ItemKind::Use(..) => { + if is_word(lint, sym!(unused_imports)) + || is_word(lint, sym::deprecated) + || is_word(lint, sym!(unreachable_pub)) + || is_word(lint, sym!(unused)) + || extract_clippy_lint(lint).map_or(false, |s| s == "wildcard_imports") + || extract_clippy_lint(lint).map_or(false, |s| s == "enum_glob_use") + { + return; + } + }, + ItemKind::ExternCrate(..) => { + if is_word(lint, sym!(unused_imports)) && skip_unused_imports { + return; + } + if is_word(lint, sym!(unused_extern_crates)) { + return; } - } - let line_span = first_line_of_span(cx, attr.span); - - if let Some(mut sugg) = snippet_opt(cx, line_span) { - if sugg.contains("#[") { - span_lint_and_then( - cx, - USELESS_ATTRIBUTE, + }, + _ => {}, + } + } + let line_span = first_line_of_span(cx, attr.span); + + if let Some(mut sugg) = snippet_opt(cx, line_span) { + if sugg.contains("#[") { + span_lint_and_then( + cx, + USELESS_ATTRIBUTE, + line_span, + "useless lint attribute", + |diag| { + sugg = sugg.replacen("#[", "#![", 1); + diag.span_suggestion( line_span, - "useless lint attribute", - |diag| { - sugg = sugg.replacen("#[", "#![", 1); - diag.span_suggestion( - line_span, - "if you just forgot a `!`, use", - sugg, - Applicability::MaybeIncorrect, - ); - }, + "if you just forgot a `!`, use", + sugg, + Applicability::MaybeIncorrect, ); - } - } - }, - _ => {}, + }, + ); + } } } } @@ -353,13 +343,13 @@ impl<'tcx> LateLintPass<'tcx> for Attributes { fn check_impl_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx ImplItem<'_>) { if is_relevant_impl(cx, item) { - check_attrs(cx, item.span, item.ident.name, &item.attrs) + check_attrs(cx, item.span, item.ident.name, cx.tcx.hir().attrs(item.hir_id())) } } fn check_trait_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx TraitItem<'_>) { if is_relevant_trait(cx, item) { - check_attrs(cx, item.span, item.ident.name, &item.attrs) + check_attrs(cx, item.span, item.ident.name, cx.tcx.hir().attrs(item.hir_id())) } } } @@ -371,18 +361,18 @@ fn extract_clippy_lint(lint: &NestedMetaItem) -> Option<SymbolStr> { if meta_item.path.segments.len() > 1; if let tool_name = meta_item.path.segments[0].ident; if tool_name.name == sym::clippy; - let lint_name = meta_item.path.segments.last().unwrap().ident.name; then { + let lint_name = meta_item.path.segments.last().unwrap().ident.name; return Some(lint_name.as_str()); } } None } -fn check_clippy_lint_names(cx: &LateContext<'_>, ident: &str, items: &[NestedMetaItem]) { +fn check_clippy_lint_names(cx: &LateContext<'_>, name: Symbol, items: &[NestedMetaItem]) { for lint in items { if let Some(lint_name) = extract_clippy_lint(lint) { - if lint_name == "restriction" && ident != "allow" { + if lint_name == "restriction" && name != sym::allow { span_lint_and_help( cx, BLANKET_CLIPPY_RESTRICTION_LINTS, @@ -430,7 +420,7 @@ fn is_relevant_block(cx: &LateContext<'_>, typeck_results: &ty::TypeckResults<'_ |stmt| match &stmt.kind { StmtKind::Local(_) => true, StmtKind::Expr(expr) | StmtKind::Semi(expr) => is_relevant_expr(cx, typeck_results, expr), - _ => false, + StmtKind::Item(_) => false, }, ) } @@ -564,7 +554,7 @@ fn check_deprecated_cfg_attr(cx: &EarlyContext<'_>, attr: &Attribute) { // check for `rustfmt_skip` and `rustfmt::skip` if let Some(skip_item) = &items[1].meta_item(); if skip_item.has_name(sym!(rustfmt_skip)) || - skip_item.path.segments.last().expect("empty path in attribute").ident.name == sym!(skip); + skip_item.path.segments.last().expect("empty path in attribute").ident.name == sym::skip; // Only lint outer attributes, because custom inner attributes are unstable // Tracking issue: https://github.com/rust-lang/rust/issues/54726 if let AttrStyle::Outer = attr.style; @@ -602,7 +592,7 @@ fn check_mismatched_target_os(cx: &EarlyContext<'_>, attr: &Attribute) { if let NestedMetaItem::MetaItem(meta) = item { match &meta.kind { MetaItemKind::List(list) => { - mismatched.extend(find_mismatched_target_os(&list)); + mismatched.extend(find_mismatched_target_os(list)); }, MetaItemKind::Word => { if_chain! { @@ -613,7 +603,7 @@ fn check_mismatched_target_os(cx: &EarlyContext<'_>, attr: &Attribute) { } } }, - _ => {}, + MetaItemKind::NameValue(..) => {}, } } } @@ -629,7 +619,7 @@ fn check_mismatched_target_os(cx: &EarlyContext<'_>, attr: &Attribute) { then { let mess = "operating system used in target family position"; - span_lint_and_then(cx, MISMATCHED_TARGET_OS, attr.span, &mess, |diag| { + span_lint_and_then(cx, MISMATCHED_TARGET_OS, attr.span, mess, |diag| { // Avoid showing the unix suggestion multiple times in case // we have more than one mismatch for unix-like systems let mut unix_suggested = false; @@ -639,7 +629,7 @@ fn check_mismatched_target_os(cx: &EarlyContext<'_>, attr: &Attribute) { diag.span_suggestion(span, "try", sugg, Applicability::MaybeIncorrect); if !unix_suggested && is_unix(os) { - diag.help("Did you mean `unix`?"); + diag.help("did you mean `unix`?"); unix_suggested = true; } } @@ -647,3 +637,7 @@ fn check_mismatched_target_os(cx: &EarlyContext<'_>, attr: &Attribute) { } } } + +fn is_lint_level(symbol: Symbol) -> bool { + matches!(symbol, sym::allow | sym::warn | sym::deny | sym::forbid) +} diff --git a/src/tools/clippy/clippy_lints/src/await_holding_invalid.rs b/src/tools/clippy/clippy_lints/src/await_holding_invalid.rs index ae64c68874454..1739a57a240b2 100644 --- a/src/tools/clippy/clippy_lints/src/await_holding_invalid.rs +++ b/src/tools/clippy/clippy_lints/src/await_holding_invalid.rs @@ -1,4 +1,5 @@ -use crate::utils::{match_def_path, paths, span_lint_and_note}; +use clippy_utils::diagnostics::span_lint_and_note; +use clippy_utils::{match_def_path, paths}; use rustc_hir::def_id::DefId; use rustc_hir::{AsyncGeneratorKind, Body, BodyId, GeneratorKind}; use rustc_lint::{LateContext, LateLintPass}; @@ -97,11 +98,10 @@ impl LateLintPass<'_> for AwaitHolding { let body_id = BodyId { hir_id: body.value.hir_id, }; - let def_id = cx.tcx.hir().body_owner_def_id(body_id); - let typeck_results = cx.tcx.typeck(def_id); + let typeck_results = cx.tcx.typeck_body(body_id); check_interior_types( cx, - &typeck_results.generator_interior_types.as_ref().skip_binder(), + typeck_results.generator_interior_types.as_ref().skip_binder(), body.value.span, ); } @@ -116,20 +116,20 @@ fn check_interior_types(cx: &LateContext<'_>, ty_causes: &[GeneratorInteriorType cx, AWAIT_HOLDING_LOCK, ty_cause.span, - "this MutexGuard is held across an 'await' point. Consider using an async-aware Mutex type or ensuring the MutexGuard is dropped before calling await.", + "this MutexGuard is held across an 'await' point. Consider using an async-aware Mutex type or ensuring the MutexGuard is dropped before calling await", ty_cause.scope_span.or(Some(span)), "these are all the await points this lock is held through", ); } if is_refcell_ref(cx, adt.did) { span_lint_and_note( - cx, - AWAIT_HOLDING_REFCELL_REF, - ty_cause.span, - "this RefCell Ref is held across an 'await' point. Consider ensuring the Ref is dropped before calling await.", - ty_cause.scope_span.or(Some(span)), - "these are all the await points this ref is held through", - ); + cx, + AWAIT_HOLDING_REFCELL_REF, + ty_cause.span, + "this RefCell Ref is held across an 'await' point. Consider ensuring the Ref is dropped before calling await", + ty_cause.scope_span.or(Some(span)), + "these are all the await points this ref is held through", + ); } } } diff --git a/src/tools/clippy/clippy_lints/src/bit_mask.rs b/src/tools/clippy/clippy_lints/src/bit_mask.rs index a4ee54076ee98..f7daf3dab4948 100644 --- a/src/tools/clippy/clippy_lints/src/bit_mask.rs +++ b/src/tools/clippy/clippy_lints/src/bit_mask.rs @@ -1,6 +1,6 @@ use crate::consts::{constant, Constant}; -use crate::utils::sugg::Sugg; -use crate::utils::{span_lint, span_lint_and_then}; +use clippy_utils::diagnostics::{span_lint, span_lint_and_then}; +use clippy_utils::sugg::Sugg; use if_chain::if_chain; use rustc_ast::ast::LitKind; use rustc_errors::Applicability; diff --git a/src/tools/clippy/clippy_lints/src/blacklisted_name.rs b/src/tools/clippy/clippy_lints/src/blacklisted_name.rs index 153870fb41658..b26ef33e05698 100644 --- a/src/tools/clippy/clippy_lints/src/blacklisted_name.rs +++ b/src/tools/clippy/clippy_lints/src/blacklisted_name.rs @@ -1,4 +1,4 @@ -use crate::utils::span_lint; +use clippy_utils::diagnostics::span_lint; use rustc_data_structures::fx::FxHashSet; use rustc_hir::{Pat, PatKind}; use rustc_lint::{LateContext, LateLintPass}; diff --git a/src/tools/clippy/clippy_lints/src/blocks_in_if_conditions.rs b/src/tools/clippy/clippy_lints/src/blocks_in_if_conditions.rs index b53f80fd8bc15..badcf8d2a43cd 100644 --- a/src/tools/clippy/clippy_lints/src/blocks_in_if_conditions.rs +++ b/src/tools/clippy/clippy_lints/src/blocks_in_if_conditions.rs @@ -1,7 +1,7 @@ -use crate::utils::{ - differing_macro_contexts, get_parent_expr, get_trait_def_id, implements_trait, paths, - snippet_block_with_applicability, span_lint, span_lint_and_sugg, -}; +use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg}; +use clippy_utils::source::snippet_block_with_applicability; +use clippy_utils::ty::implements_trait; +use clippy_utils::{differing_macro_contexts, get_parent_expr}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::intravisit::{walk_expr, NestedVisitorMap, Visitor}; @@ -10,6 +10,7 @@ use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::hir::map::Map; use rustc_middle::lint::in_external_macro; use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::sym; declare_clippy_lint! { /// **What it does:** Checks for `if` conditions that use blocks containing an @@ -61,7 +62,7 @@ impl<'a, 'tcx> Visitor<'tcx> for ExVisitor<'a, 'tcx> { if let Some(parent) = get_parent_expr(self.cx, expr); if let ExprKind::MethodCall(_, _, args, _) = parent.kind; let caller = self.cx.typeck_results().expr_ty(&args[0]); - if let Some(iter_id) = get_trait_def_id(self.cx, &paths::ITERATOR); + if let Some(iter_id) = self.cx.tcx.get_diagnostic_item(sym::Iterator); if implements_trait(self.cx, caller, iter_id, &[]); then { return; diff --git a/src/tools/clippy/clippy_lints/src/booleans.rs b/src/tools/clippy/clippy_lints/src/booleans.rs index 0713303ec4b67..58d9aa9c005c2 100644 --- a/src/tools/clippy/clippy_lints/src/booleans.rs +++ b/src/tools/clippy/clippy_lints/src/booleans.rs @@ -1,7 +1,7 @@ -use crate::utils::{ - eq_expr_value, get_trait_def_id, implements_trait, in_macro, is_type_diagnostic_item, paths, snippet_opt, - span_lint_and_sugg, span_lint_and_then, -}; +use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then}; +use clippy_utils::source::snippet_opt; +use clippy_utils::ty::{implements_trait, is_type_diagnostic_item}; +use clippy_utils::{eq_expr_value, get_trait_def_id, in_macro, paths}; use if_chain::if_chain; use rustc_ast::ast::LitKind; use rustc_errors::Applicability; diff --git a/src/tools/clippy/clippy_lints/src/bytecount.rs b/src/tools/clippy/clippy_lints/src/bytecount.rs index b8828719f627c..877ae002d36e9 100644 --- a/src/tools/clippy/clippy_lints/src/bytecount.rs +++ b/src/tools/clippy/clippy_lints/src/bytecount.rs @@ -1,6 +1,7 @@ -use crate::utils::{ - contains_name, get_pat_name, match_type, paths, single_segment_path, snippet_with_applicability, span_lint_and_sugg, -}; +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::source::snippet_with_applicability; +use clippy_utils::ty::match_type; +use clippy_utils::{contains_name, get_pat_name, paths, single_segment_path}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::{BinOpKind, BorrowKind, Expr, ExprKind, UnOp}; @@ -28,7 +29,7 @@ declare_clippy_lint! { /// &vec.iter().filter(|x| **x == 0u8).count(); // use bytecount::count instead /// ``` pub NAIVE_BYTECOUNT, - perf, + pedantic, "use of naive `<slice>.filter(|&x| x == y).count()` to count byte values" } @@ -37,59 +38,55 @@ declare_lint_pass!(ByteCount => [NAIVE_BYTECOUNT]); impl<'tcx> LateLintPass<'tcx> for ByteCount { fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) { if_chain! { - if let ExprKind::MethodCall(ref count, _, ref count_args, _) = expr.kind; + if let ExprKind::MethodCall(count, _, count_args, _) = expr.kind; if count.ident.name == sym!(count); if count_args.len() == 1; - if let ExprKind::MethodCall(ref filter, _, ref filter_args, _) = count_args[0].kind; + if let ExprKind::MethodCall(filter, _, filter_args, _) = count_args[0].kind; if filter.ident.name == sym!(filter); if filter_args.len() == 2; if let ExprKind::Closure(_, _, body_id, _, _) = filter_args[1].kind; + let body = cx.tcx.hir().body(body_id); + if body.params.len() == 1; + if let Some(argname) = get_pat_name(body.params[0].pat); + if let ExprKind::Binary(ref op, l, r) = body.value.kind; + if op.node == BinOpKind::Eq; + if match_type(cx, + cx.typeck_results().expr_ty(&filter_args[0]).peel_refs(), + &paths::SLICE_ITER); then { - let body = cx.tcx.hir().body(body_id); - if_chain! { - if body.params.len() == 1; - if let Some(argname) = get_pat_name(&body.params[0].pat); - if let ExprKind::Binary(ref op, ref l, ref r) = body.value.kind; - if op.node == BinOpKind::Eq; - if match_type(cx, - cx.typeck_results().expr_ty(&filter_args[0]).peel_refs(), - &paths::SLICE_ITER); - then { - let needle = match get_path_name(l) { - Some(name) if check_arg(name, argname, r) => r, - _ => match get_path_name(r) { - Some(name) if check_arg(name, argname, l) => l, - _ => { return; } - } - }; - if ty::Uint(UintTy::U8) != *cx.typeck_results().expr_ty(needle).peel_refs().kind() { - return; - } - let haystack = if let ExprKind::MethodCall(ref path, _, ref args, _) = - filter_args[0].kind { - let p = path.ident.name; - if (p == sym::iter || p == sym!(iter_mut)) && args.len() == 1 { - &args[0] - } else { - &filter_args[0] - } - } else { - &filter_args[0] - }; - let mut applicability = Applicability::MaybeIncorrect; - span_lint_and_sugg( - cx, - NAIVE_BYTECOUNT, - expr.span, - "you appear to be counting bytes the naive way", - "consider using the bytecount crate", - format!("bytecount::count({}, {})", - snippet_with_applicability(cx, haystack.span, "..", &mut applicability), - snippet_with_applicability(cx, needle.span, "..", &mut applicability)), - applicability, - ); + let needle = match get_path_name(l) { + Some(name) if check_arg(name, argname, r) => r, + _ => match get_path_name(r) { + Some(name) if check_arg(name, argname, l) => l, + _ => { return; } } }; + if ty::Uint(UintTy::U8) != *cx.typeck_results().expr_ty(needle).peel_refs().kind() { + return; + } + let haystack = if let ExprKind::MethodCall(path, _, args, _) = + filter_args[0].kind { + let p = path.ident.name; + if (p == sym::iter || p == sym!(iter_mut)) && args.len() == 1 { + &args[0] + } else { + &filter_args[0] + } + } else { + &filter_args[0] + }; + let mut applicability = Applicability::MaybeIncorrect; + span_lint_and_sugg( + cx, + NAIVE_BYTECOUNT, + expr.span, + "you appear to be counting bytes the naive way", + "consider using the bytecount crate", + format!("bytecount::count({}, {})", + snippet_with_applicability(cx, haystack.span, "..", &mut applicability), + snippet_with_applicability(cx, needle.span, "..", &mut applicability)), + applicability, + ); } }; } @@ -101,10 +98,10 @@ fn check_arg(name: Symbol, arg: Symbol, needle: &Expr<'_>) -> bool { fn get_path_name(expr: &Expr<'_>) -> Option<Symbol> { match expr.kind { - ExprKind::Box(ref e) | ExprKind::AddrOf(BorrowKind::Ref, _, ref e) | ExprKind::Unary(UnOp::Deref, ref e) => { + ExprKind::Box(e) | ExprKind::AddrOf(BorrowKind::Ref, _, e) | ExprKind::Unary(UnOp::Deref, e) => { get_path_name(e) }, - ExprKind::Block(ref b, _) => { + ExprKind::Block(b, _) => { if b.stmts.is_empty() { b.expr.as_ref().and_then(|p| get_path_name(p)) } else { diff --git a/src/tools/clippy/clippy_lints/src/cargo_common_metadata.rs b/src/tools/clippy/clippy_lints/src/cargo_common_metadata.rs index cc2869ab495c8..8097a1c832613 100644 --- a/src/tools/clippy/clippy_lints/src/cargo_common_metadata.rs +++ b/src/tools/clippy/clippy_lints/src/cargo_common_metadata.rs @@ -2,7 +2,8 @@ use std::path::PathBuf; -use crate::utils::{run_lints, span_lint}; +use clippy_utils::diagnostics::span_lint; +use clippy_utils::run_lints; use rustc_hir::{hir_id::CRATE_HIR_ID, Crate}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_tool_lint, impl_lint_pass}; @@ -19,11 +20,10 @@ declare_clippy_lint! { /// /// **Example:** /// ```toml - /// # This `Cargo.toml` is missing an authors field: + /// # This `Cargo.toml` is missing a description field: /// [package] /// name = "clippy" /// version = "0.0.212" - /// description = "A bunch of helpful lints to avoid common pitfalls in Rust" /// repository = "https://github.com/rust-lang/rust-clippy" /// readme = "README.md" /// license = "MIT OR Apache-2.0" @@ -31,14 +31,13 @@ declare_clippy_lint! { /// categories = ["development-tools", "development-tools::cargo-plugins"] /// ``` /// - /// Should include an authors field like: + /// Should include a description field like: /// /// ```toml /// # This `Cargo.toml` includes all common metadata /// [package] /// name = "clippy" /// version = "0.0.212" - /// authors = ["Someone <someone@rust-lang.org>"] /// description = "A bunch of helpful lints to avoid common pitfalls in Rust" /// repository = "https://github.com/rust-lang/rust-clippy" /// readme = "README.md" @@ -96,10 +95,6 @@ impl LateLintPass<'_> for CargoCommonMetadata { // only run the lint if publish is `None` (`publish = true` or skipped entirely) // or if the vector isn't empty (`publish = ["something"]`) if package.publish.as_ref().filter(|publish| publish.is_empty()).is_none() || self.ignore_publish { - if is_empty_vec(&package.authors) { - missing_warning(cx, &package, "package.authors"); - } - if is_empty_str(&package.description) { missing_warning(cx, &package, "package.description"); } 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 6969ac949d845..c9ef379be565d 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 @@ -1,12 +1,11 @@ -use crate::utils::paths::STRING; -use crate::utils::{match_def_path, span_lint_and_help}; +use clippy_utils::diagnostics::span_lint_and_help; use if_chain::if_chain; use rustc_ast::ast::LitKind; use rustc_hir::{Expr, ExprKind, PathSegment}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty; use rustc_session::{declare_lint_pass, declare_tool_lint}; -use rustc_span::{source_map::Spanned, Span}; +use rustc_span::{source_map::Spanned, symbol::sym, Span}; declare_clippy_lint! { /// **What it does:** @@ -59,7 +58,7 @@ fn check_case_sensitive_file_extension_comparison(ctx: &LateContext<'_>, expr: & return Some(span); }, ty::Adt(&ty::AdtDef { did, .. }, _) => { - if match_def_path(ctx, did, &STRING) { + if ctx.tcx.is_diagnostic_item(sym::string_type, did) { return Some(span); } }, diff --git a/src/tools/clippy/clippy_lints/src/casts/cast_lossless.rs b/src/tools/clippy/clippy_lints/src/casts/cast_lossless.rs new file mode 100644 index 0000000000000..869deecfbd53a --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/casts/cast_lossless.rs @@ -0,0 +1,89 @@ +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::in_constant; +use clippy_utils::source::snippet_opt; +use clippy_utils::ty::is_isize_or_usize; +use rustc_errors::Applicability; +use rustc_hir::{Expr, ExprKind}; +use rustc_lint::LateContext; +use rustc_middle::ty::{self, FloatTy, Ty}; + +use super::{utils, CAST_LOSSLESS}; + +pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_op: &Expr<'_>, cast_from: Ty<'_>, cast_to: Ty<'_>) { + if !should_lint(cx, expr, cast_from, cast_to) { + return; + } + + // The suggestion is to use a function call, so if the original expression + // has parens on the outside, they are no longer needed. + let mut applicability = Applicability::MachineApplicable; + let opt = snippet_opt(cx, cast_op.span); + let sugg = opt.as_ref().map_or_else( + || { + applicability = Applicability::HasPlaceholders; + ".." + }, + |snip| { + if should_strip_parens(cast_op, snip) { + &snip[1..snip.len() - 1] + } else { + snip.as_str() + } + }, + ); + + span_lint_and_sugg( + cx, + CAST_LOSSLESS, + expr.span, + &format!( + "casting `{}` to `{}` may become silently lossy if you later change the type", + cast_from, cast_to + ), + "try", + format!("{}::from({})", cast_to, sugg), + applicability, + ); +} + +fn should_lint(cx: &LateContext<'_>, expr: &Expr<'_>, cast_from: Ty<'_>, cast_to: Ty<'_>) -> bool { + // Do not suggest using From in consts/statics until it is valid to do so (see #2267). + if in_constant(cx, expr.hir_id) { + return false; + } + + match (cast_from.is_integral(), cast_to.is_integral()) { + (true, true) => { + let cast_signed_to_unsigned = cast_from.is_signed() && !cast_to.is_signed(); + let from_nbits = utils::int_ty_to_nbits(cast_from, cx.tcx); + let to_nbits = utils::int_ty_to_nbits(cast_to, cx.tcx); + !is_isize_or_usize(cast_from) + && !is_isize_or_usize(cast_to) + && from_nbits < to_nbits + && !cast_signed_to_unsigned + }, + + (true, false) => { + let from_nbits = utils::int_ty_to_nbits(cast_from, cx.tcx); + let to_nbits = if let ty::Float(FloatTy::F32) = cast_to.kind() { + 32 + } else { + 64 + }; + from_nbits < to_nbits + }, + + (_, _) => { + matches!(cast_from.kind(), ty::Float(FloatTy::F32)) && matches!(cast_to.kind(), ty::Float(FloatTy::F64)) + }, + } +} + +fn should_strip_parens(cast_expr: &Expr<'_>, snip: &str) -> bool { + if let ExprKind::Binary(_, _, _) = cast_expr.kind { + if snip.starts_with('(') && snip.ends_with(')') { + return true; + } + } + false +} diff --git a/src/tools/clippy/clippy_lints/src/casts/cast_possible_truncation.rs b/src/tools/clippy/clippy_lints/src/casts/cast_possible_truncation.rs new file mode 100644 index 0000000000000..833ad122e0d4e --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/casts/cast_possible_truncation.rs @@ -0,0 +1,54 @@ +use clippy_utils::diagnostics::span_lint; +use clippy_utils::ty::is_isize_or_usize; +use rustc_hir::Expr; +use rustc_lint::LateContext; +use rustc_middle::ty::{self, FloatTy, Ty}; + +use super::{utils, CAST_POSSIBLE_TRUNCATION}; + +pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_from: Ty<'_>, cast_to: Ty<'_>) { + let msg = match (cast_from.is_integral(), cast_to.is_integral()) { + (true, true) => { + let from_nbits = utils::int_ty_to_nbits(cast_from, cx.tcx); + let to_nbits = utils::int_ty_to_nbits(cast_to, cx.tcx); + + let (should_lint, suffix) = match (is_isize_or_usize(cast_from), is_isize_or_usize(cast_to)) { + (true, true) | (false, false) => (to_nbits < from_nbits, ""), + (true, false) => ( + to_nbits <= 32, + if to_nbits == 32 { + " on targets with 64-bit wide pointers" + } else { + "" + }, + ), + (false, true) => (from_nbits == 64, " on targets with 32-bit wide pointers"), + }; + + if !should_lint { + return; + } + + format!( + "casting `{}` to `{}` may truncate the value{}", + cast_from, cast_to, suffix, + ) + }, + + (false, true) => { + format!("casting `{}` to `{}` may truncate the value", cast_from, cast_to) + }, + + (_, _) => { + if matches!(cast_from.kind(), &ty::Float(FloatTy::F64)) + && matches!(cast_to.kind(), &ty::Float(FloatTy::F32)) + { + "casting `f64` to `f32` may truncate the value".to_string() + } else { + return; + } + }, + }; + + span_lint(cx, CAST_POSSIBLE_TRUNCATION, expr.span, &msg); +} diff --git a/src/tools/clippy/clippy_lints/src/casts/cast_possible_wrap.rs b/src/tools/clippy/clippy_lints/src/casts/cast_possible_wrap.rs new file mode 100644 index 0000000000000..2c5c1d7cb4654 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/casts/cast_possible_wrap.rs @@ -0,0 +1,44 @@ +use clippy_utils::diagnostics::span_lint; +use clippy_utils::ty::is_isize_or_usize; +use rustc_hir::Expr; +use rustc_lint::LateContext; +use rustc_middle::ty::Ty; + +use super::{utils, CAST_POSSIBLE_WRAP}; + +pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_from: Ty<'_>, cast_to: Ty<'_>) { + if !(cast_from.is_integral() && cast_to.is_integral()) { + return; + } + + let arch_64_suffix = " on targets with 64-bit wide pointers"; + let arch_32_suffix = " on targets with 32-bit wide pointers"; + let cast_unsigned_to_signed = !cast_from.is_signed() && cast_to.is_signed(); + let from_nbits = utils::int_ty_to_nbits(cast_from, cx.tcx); + let to_nbits = utils::int_ty_to_nbits(cast_to, cx.tcx); + + let (should_lint, suffix) = match (is_isize_or_usize(cast_from), is_isize_or_usize(cast_to)) { + (true, true) | (false, false) => (to_nbits == from_nbits && cast_unsigned_to_signed, ""), + (true, false) => (to_nbits <= 32 && cast_unsigned_to_signed, arch_32_suffix), + (false, true) => ( + cast_unsigned_to_signed, + if from_nbits == 64 { + arch_64_suffix + } else { + arch_32_suffix + }, + ), + }; + + if should_lint { + span_lint( + cx, + CAST_POSSIBLE_WRAP, + expr.span, + &format!( + "casting `{}` to `{}` may wrap around the value{}", + cast_from, cast_to, suffix, + ), + ); + } +} diff --git a/src/tools/clippy/clippy_lints/src/casts/cast_precision_loss.rs b/src/tools/clippy/clippy_lints/src/casts/cast_precision_loss.rs new file mode 100644 index 0000000000000..63ac8fd2dd269 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/casts/cast_precision_loss.rs @@ -0,0 +1,51 @@ +use clippy_utils::diagnostics::span_lint; +use clippy_utils::ty::is_isize_or_usize; +use rustc_hir::Expr; +use rustc_lint::LateContext; +use rustc_middle::ty::{self, FloatTy, Ty}; + +use super::{utils, CAST_PRECISION_LOSS}; + +pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_from: Ty<'_>, cast_to: Ty<'_>) { + if !cast_from.is_integral() || cast_to.is_integral() { + return; + } + + let from_nbits = utils::int_ty_to_nbits(cast_from, cx.tcx); + let to_nbits = if let ty::Float(FloatTy::F32) = cast_to.kind() { + 32 + } else { + 64 + }; + + if !(is_isize_or_usize(cast_from) || from_nbits >= to_nbits) { + return; + } + + let cast_to_f64 = to_nbits == 64; + let mantissa_nbits = if cast_to_f64 { 52 } else { 23 }; + let arch_dependent = is_isize_or_usize(cast_from) && cast_to_f64; + let arch_dependent_str = "on targets with 64-bit wide pointers "; + let from_nbits_str = if arch_dependent { + "64".to_owned() + } else if is_isize_or_usize(cast_from) { + "32 or 64".to_owned() + } else { + utils::int_ty_to_nbits(cast_from, cx.tcx).to_string() + }; + + span_lint( + cx, + CAST_PRECISION_LOSS, + expr.span, + &format!( + "casting `{0}` to `{1}` causes a loss of precision {2}(`{0}` is {3} bits wide, \ + but `{1}`'s mantissa is only {4} bits wide)", + cast_from, + if cast_to_f64 { "f64" } else { "f32" }, + if arch_dependent { arch_dependent_str } else { "" }, + from_nbits_str, + mantissa_nbits + ), + ); +} 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 new file mode 100644 index 0000000000000..62a119d662bb2 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/casts/cast_ptr_alignment.rs @@ -0,0 +1,80 @@ +use clippy_utils::diagnostics::span_lint; +use clippy_utils::is_hir_ty_cfg_dependant; +use if_chain::if_chain; +use rustc_hir::{Expr, ExprKind, GenericArg}; +use rustc_lint::LateContext; +use rustc_middle::ty::{self, Ty}; +use rustc_span::symbol::sym; +use rustc_target::abi::LayoutOf; + +use super::CAST_PTR_ALIGNMENT; + +pub(super) fn check(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { + if let ExprKind::Cast(cast_expr, cast_to) = expr.kind { + if is_hir_ty_cfg_dependant(cx, cast_to) { + return; + } + let (cast_from, cast_to) = ( + cx.typeck_results().expr_ty(cast_expr), + cx.typeck_results().expr_ty(expr), + ); + lint_cast_ptr_alignment(cx, expr, cast_from, cast_to); + } else if let ExprKind::MethodCall(method_path, _, args, _) = expr.kind { + if_chain! { + if method_path.ident.name == sym!(cast); + if let Some(generic_args) = method_path.args; + if let [GenericArg::Type(cast_to)] = generic_args.args; + // There probably is no obvious reason to do this, just to be consistent with `as` cases. + if !is_hir_ty_cfg_dependant(cx, cast_to); + then { + let (cast_from, cast_to) = + (cx.typeck_results().expr_ty(&args[0]), cx.typeck_results().expr_ty(expr)); + lint_cast_ptr_alignment(cx, expr, cast_from, cast_to); + } + } + } +} + +fn lint_cast_ptr_alignment<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>, cast_from: Ty<'tcx>, cast_to: Ty<'tcx>) { + if_chain! { + if let ty::RawPtr(from_ptr_ty) = &cast_from.kind(); + if let ty::RawPtr(to_ptr_ty) = &cast_to.kind(); + if let Ok(from_layout) = cx.layout_of(from_ptr_ty.ty); + if let Ok(to_layout) = cx.layout_of(to_ptr_ty.ty); + if from_layout.align.abi < to_layout.align.abi; + // with c_void, we inherently need to trust the user + if !is_c_void(cx, from_ptr_ty.ty); + // when casting from a ZST, we don't know enough to properly lint + if !from_layout.is_zst(); + then { + span_lint( + cx, + CAST_PTR_ALIGNMENT, + expr.span, + &format!( + "casting from `{}` to a more-strictly-aligned pointer (`{}`) ({} < {} bytes)", + cast_from, + cast_to, + from_layout.align.abi.bytes(), + to_layout.align.abi.bytes(), + ), + ); + } + } +} + +/// Check if the given type is either `core::ffi::c_void` or +/// one of the platform specific `libc::<platform>::c_void` of libc. +fn is_c_void(cx: &LateContext<'_>, ty: Ty<'_>) -> bool { + if let ty::Adt(adt, _) = ty.kind() { + let names = cx.get_def_path(adt.did); + + if names.is_empty() { + return false; + } + if names[0] == sym::libc || names[0] == sym::core && *names.last().unwrap() == sym!(c_void) { + return true; + } + } + false +} 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 new file mode 100644 index 0000000000000..d9bf1ea58b97b --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/casts/cast_ref_to_mut.rs @@ -0,0 +1,26 @@ +use clippy_utils::diagnostics::span_lint; +use if_chain::if_chain; +use rustc_hir::{Expr, ExprKind, MutTy, Mutability, TyKind, UnOp}; +use rustc_lint::LateContext; +use rustc_middle::ty; + +use super::CAST_REF_TO_MUT; + +pub(super) fn check(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { + if_chain! { + if let ExprKind::Unary(UnOp::Deref, e) = &expr.kind; + if let ExprKind::Cast(e, t) = &e.kind; + if let TyKind::Ptr(MutTy { mutbl: Mutability::Mut, .. }) = t.kind; + if let ExprKind::Cast(e, t) = &e.kind; + if let TyKind::Ptr(MutTy { mutbl: Mutability::Not, .. }) = t.kind; + if let ty::Ref(..) = cx.typeck_results().node_type(e.hir_id).kind(); + then { + span_lint( + cx, + CAST_REF_TO_MUT, + expr.span, + "casting `&T` to `&mut T` may cause undefined behavior, consider instead using an `UnsafeCell`", + ); + } + } +} diff --git a/src/tools/clippy/clippy_lints/src/casts/cast_sign_loss.rs b/src/tools/clippy/clippy_lints/src/casts/cast_sign_loss.rs new file mode 100644 index 0000000000000..040e0ca886458 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/casts/cast_sign_loss.rs @@ -0,0 +1,69 @@ +use crate::consts::{constant, Constant}; +use clippy_utils::diagnostics::span_lint; +use clippy_utils::{method_chain_args, sext}; +use if_chain::if_chain; +use rustc_hir::{Expr, ExprKind}; +use rustc_lint::LateContext; +use rustc_middle::ty::{self, Ty}; + +use super::CAST_SIGN_LOSS; + +pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_op: &Expr<'_>, cast_from: Ty<'_>, cast_to: Ty<'_>) { + if should_lint(cx, cast_op, cast_from, cast_to) { + span_lint( + cx, + CAST_SIGN_LOSS, + expr.span, + &format!( + "casting `{}` to `{}` may lose the sign of the value", + cast_from, cast_to + ), + ); + } +} + +fn should_lint(cx: &LateContext<'_>, cast_op: &Expr<'_>, cast_from: Ty<'_>, cast_to: Ty<'_>) -> bool { + match (cast_from.is_integral(), cast_to.is_integral()) { + (true, true) => { + if !cast_from.is_signed() || cast_to.is_signed() { + return false; + } + + // Don't lint for positive constants. + let const_val = constant(cx, cx.typeck_results(), cast_op); + if_chain! { + if let Some((Constant::Int(n), _)) = const_val; + if let ty::Int(ity) = *cast_from.kind(); + if sext(cx.tcx, n, ity) >= 0; + then { + return false; + } + } + + // Don't lint for the result of methods that always return non-negative values. + if let ExprKind::MethodCall(path, _, _, _) = cast_op.kind { + let mut method_name = path.ident.name.as_str(); + let allowed_methods = ["abs", "checked_abs", "rem_euclid", "checked_rem_euclid"]; + + if_chain! { + if method_name == "unwrap"; + if let Some(arglist) = method_chain_args(cast_op, &["unwrap"]); + if let ExprKind::MethodCall(inner_path, _, _, _) = &arglist[0][0].kind; + then { + method_name = inner_path.ident.name.as_str(); + } + } + + if allowed_methods.iter().any(|&name| method_name == name) { + return false; + } + } + + true + }, + + (false, true) => !cast_to.is_signed(), + + (_, _) => false, + } +} 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 new file mode 100644 index 0000000000000..099a0de881ff0 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/casts/char_lit_as_u8.rs @@ -0,0 +1,41 @@ +use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::source::snippet_with_applicability; +use if_chain::if_chain; +use rustc_ast::LitKind; +use rustc_errors::Applicability; +use rustc_hir::{Expr, ExprKind}; +use rustc_lint::LateContext; +use rustc_middle::ty::{self, UintTy}; + +use super::CHAR_LIT_AS_U8; + +pub(super) fn check(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { + if_chain! { + if let ExprKind::Cast(e, _) = &expr.kind; + if let ExprKind::Lit(l) = &e.kind; + if let LitKind::Char(c) = l.node; + if ty::Uint(UintTy::U8) == *cx.typeck_results().expr_ty(expr).kind(); + then { + let mut applicability = Applicability::MachineApplicable; + let snippet = snippet_with_applicability(cx, e.span, "'x'", &mut applicability); + + span_lint_and_then( + cx, + CHAR_LIT_AS_U8, + expr.span, + "casting a character literal to `u8` truncates", + |diag| { + diag.note("`char` is four bytes wide, but `u8` is a single byte"); + + if c.is_ascii() { + diag.span_suggestion( + expr.span, + "use a byte literal instead", + format!("b{}", snippet), + applicability, + ); + } + }); + } + } +} diff --git a/src/tools/clippy/clippy_lints/src/casts/fn_to_numeric_cast.rs b/src/tools/clippy/clippy_lints/src/casts/fn_to_numeric_cast.rs new file mode 100644 index 0000000000000..35350d8a25b86 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/casts/fn_to_numeric_cast.rs @@ -0,0 +1,37 @@ +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::source::snippet_with_applicability; +use rustc_errors::Applicability; +use rustc_hir::Expr; +use rustc_lint::LateContext; +use rustc_middle::ty::{self, Ty, UintTy}; + +use super::{utils, FN_TO_NUMERIC_CAST}; + +pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_expr: &Expr<'_>, cast_from: Ty<'_>, cast_to: Ty<'_>) { + // We only want to check casts to `ty::Uint` or `ty::Int` + match cast_to.kind() { + ty::Uint(_) | ty::Int(..) => { /* continue on */ }, + _ => return, + } + + match cast_from.kind() { + ty::FnDef(..) | ty::FnPtr(_) => { + let mut applicability = Applicability::MaybeIncorrect; + let from_snippet = snippet_with_applicability(cx, cast_expr.span, "x", &mut applicability); + let to_nbits = utils::int_ty_to_nbits(cast_to, cx.tcx); + + if (to_nbits >= cx.tcx.data_layout.pointer_size.bits()) && (*cast_to.kind() != ty::Uint(UintTy::Usize)) { + span_lint_and_sugg( + cx, + FN_TO_NUMERIC_CAST, + expr.span, + &format!("casting function pointer `{}` to `{}`", from_snippet, cast_to), + "try", + format!("{} as usize", from_snippet), + applicability, + ); + } + }, + _ => {}, + } +} diff --git a/src/tools/clippy/clippy_lints/src/casts/fn_to_numeric_cast_with_truncation.rs b/src/tools/clippy/clippy_lints/src/casts/fn_to_numeric_cast_with_truncation.rs new file mode 100644 index 0000000000000..6287f479b5bfe --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/casts/fn_to_numeric_cast_with_truncation.rs @@ -0,0 +1,39 @@ +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::source::snippet_with_applicability; +use rustc_errors::Applicability; +use rustc_hir::Expr; +use rustc_lint::LateContext; +use rustc_middle::ty::{self, Ty}; + +use super::{utils, FN_TO_NUMERIC_CAST_WITH_TRUNCATION}; + +pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_expr: &Expr<'_>, cast_from: Ty<'_>, cast_to: Ty<'_>) { + // We only want to check casts to `ty::Uint` or `ty::Int` + match cast_to.kind() { + ty::Uint(_) | ty::Int(..) => { /* continue on */ }, + _ => return, + } + match cast_from.kind() { + ty::FnDef(..) | ty::FnPtr(_) => { + let mut applicability = Applicability::MaybeIncorrect; + let from_snippet = snippet_with_applicability(cx, cast_expr.span, "x", &mut applicability); + + let to_nbits = utils::int_ty_to_nbits(cast_to, cx.tcx); + if to_nbits < cx.tcx.data_layout.pointer_size.bits() { + span_lint_and_sugg( + cx, + FN_TO_NUMERIC_CAST_WITH_TRUNCATION, + expr.span, + &format!( + "casting function pointer `{}` to `{}`, which truncates the value", + from_snippet, cast_to + ), + "try", + format!("{} as usize", from_snippet), + applicability, + ); + } + }, + _ => {}, + } +} diff --git a/src/tools/clippy/clippy_lints/src/casts/mod.rs b/src/tools/clippy/clippy_lints/src/casts/mod.rs new file mode 100644 index 0000000000000..ae4fdd12c41e8 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/casts/mod.rs @@ -0,0 +1,406 @@ +mod cast_lossless; +mod cast_possible_truncation; +mod cast_possible_wrap; +mod cast_precision_loss; +mod cast_ptr_alignment; +mod cast_ref_to_mut; +mod cast_sign_loss; +mod char_lit_as_u8; +mod fn_to_numeric_cast; +mod fn_to_numeric_cast_with_truncation; +mod ptr_as_ptr; +mod unnecessary_cast; +mod utils; + +use clippy_utils::is_hir_ty_cfg_dependant; +use rustc_hir::{Expr, ExprKind}; +use rustc_lint::{LateContext, LateLintPass, LintContext}; +use rustc_middle::lint::in_external_macro; +use rustc_semver::RustcVersion; +use rustc_session::{declare_tool_lint, impl_lint_pass}; + +declare_clippy_lint! { + /// **What it does:** Checks for casts from any numerical to a float type where + /// the receiving type cannot store all values from the original type without + /// rounding errors. This possible rounding is to be expected, so this lint is + /// `Allow` by default. + /// + /// Basically, this warns on casting any integer with 32 or more bits to `f32` + /// or any 64-bit integer to `f64`. + /// + /// **Why is this bad?** It's not bad at all. But in some applications it can be + /// helpful to know where precision loss can take place. This lint can help find + /// those places in the code. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// ```rust + /// let x = u64::MAX; + /// x as f64; + /// ``` + pub CAST_PRECISION_LOSS, + pedantic, + "casts that cause loss of precision, e.g., `x as f32` where `x: u64`" +} + +declare_clippy_lint! { + /// **What it does:** Checks for casts from a signed to an unsigned numerical + /// type. In this case, negative values wrap around to large positive values, + /// which can be quite surprising in practice. However, as the cast works as + /// defined, this lint is `Allow` by default. + /// + /// **Why is this bad?** Possibly surprising results. You can activate this lint + /// as a one-time check to see where numerical wrapping can arise. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// ```rust + /// let y: i8 = -1; + /// y as u128; // will return 18446744073709551615 + /// ``` + pub CAST_SIGN_LOSS, + pedantic, + "casts from signed types to unsigned types, e.g., `x as u32` where `x: i32`" +} + +declare_clippy_lint! { + /// **What it does:** Checks for casts between numerical types that may + /// truncate large values. This is expected behavior, so the cast is `Allow` by + /// default. + /// + /// **Why is this bad?** In some problem domains, it is good practice to avoid + /// truncation. This lint can be activated to help assess where additional + /// checks could be beneficial. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// ```rust + /// fn as_u8(x: u64) -> u8 { + /// x as u8 + /// } + /// ``` + pub CAST_POSSIBLE_TRUNCATION, + pedantic, + "casts that may cause truncation of the value, e.g., `x as u8` where `x: u32`, or `x as i32` where `x: f32`" +} + +declare_clippy_lint! { + /// **What it does:** Checks for casts from an unsigned type to a signed type of + /// the same size. Performing such a cast is a 'no-op' for the compiler, + /// i.e., nothing is changed at the bit level, and the binary representation of + /// the value is reinterpreted. This can cause wrapping if the value is too big + /// for the target signed type. However, the cast works as defined, so this lint + /// is `Allow` by default. + /// + /// **Why is this bad?** While such a cast is not bad in itself, the results can + /// be surprising when this is not the intended behavior, as demonstrated by the + /// example below. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// ```rust + /// u32::MAX as i32; // will yield a value of `-1` + /// ``` + pub CAST_POSSIBLE_WRAP, + pedantic, + "casts that may cause wrapping around the value, e.g., `x as i32` where `x: u32` and `x > i32::MAX`" +} + +declare_clippy_lint! { + /// **What it does:** Checks for casts between numerical types that may + /// be replaced by safe conversion functions. + /// + /// **Why is this bad?** Rust's `as` keyword will perform many kinds of + /// conversions, including silently lossy conversions. Conversion functions such + /// as `i32::from` will only perform lossless conversions. Using the conversion + /// functions prevents conversions from turning into silent lossy conversions if + /// the types of the input expressions ever change, and make it easier for + /// people reading the code to know that the conversion is lossless. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// ```rust + /// fn as_u64(x: u8) -> u64 { + /// x as u64 + /// } + /// ``` + /// + /// Using `::from` would look like this: + /// + /// ```rust + /// fn as_u64(x: u8) -> u64 { + /// u64::from(x) + /// } + /// ``` + pub CAST_LOSSLESS, + pedantic, + "casts using `as` that are known to be lossless, e.g., `x as u64` where `x: u8`" +} + +declare_clippy_lint! { + /// **What it does:** Checks for casts to the same type, casts of int literals to integer types + /// and casts of float literals to float types. + /// + /// **Why is this bad?** It's just unnecessary. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// ```rust + /// let _ = 2i32 as i32; + /// let _ = 0.5 as f32; + /// ``` + /// + /// Better: + /// + /// ```rust + /// let _ = 2_i32; + /// let _ = 0.5_f32; + /// ``` + pub UNNECESSARY_CAST, + complexity, + "cast to the same type, e.g., `x as i32` where `x: i32`" +} + +declare_clippy_lint! { + /// **What it does:** Checks for casts, using `as` or `pointer::cast`, + /// from a less-strictly-aligned pointer to a more-strictly-aligned pointer + /// + /// **Why is this bad?** Dereferencing the resulting pointer may be undefined + /// behavior. + /// + /// **Known problems:** Using `std::ptr::read_unaligned` and `std::ptr::write_unaligned` or similar + /// on the resulting pointer is fine. Is over-zealous: Casts with manual alignment checks or casts like + /// u64-> u8 -> u16 can be fine. Miri is able to do a more in-depth analysis. + /// + /// **Example:** + /// ```rust + /// let _ = (&1u8 as *const u8) as *const u16; + /// let _ = (&mut 1u8 as *mut u8) as *mut u16; + /// + /// (&1u8 as *const u8).cast::<u16>(); + /// (&mut 1u8 as *mut u8).cast::<u16>(); + /// ``` + pub CAST_PTR_ALIGNMENT, + pedantic, + "cast from a pointer to a more-strictly-aligned pointer" +} + +declare_clippy_lint! { + /// **What it does:** Checks for casts of function pointers to something other than usize + /// + /// **Why is this bad?** + /// Casting a function pointer to anything other than usize/isize is not portable across + /// architectures, because you end up losing bits if the target type is too small or end up with a + /// bunch of extra bits that waste space and add more instructions to the final binary than + /// strictly necessary for the problem + /// + /// Casting to isize also doesn't make sense since there are no signed addresses. + /// + /// **Example** + /// + /// ```rust + /// // Bad + /// fn fun() -> i32 { 1 } + /// let a = fun as i64; + /// + /// // Good + /// fn fun2() -> i32 { 1 } + /// let a = fun2 as usize; + /// ``` + pub FN_TO_NUMERIC_CAST, + style, + "casting a function pointer to a numeric type other than usize" +} + +declare_clippy_lint! { + /// **What it does:** Checks for casts of a function pointer to a numeric type not wide enough to + /// store address. + /// + /// **Why is this bad?** + /// Such a cast discards some bits of the function's address. If this is intended, it would be more + /// clearly expressed by casting to usize first, then casting the usize to the intended type (with + /// a comment) to perform the truncation. + /// + /// **Example** + /// + /// ```rust + /// // Bad + /// fn fn1() -> i16 { + /// 1 + /// }; + /// let _ = fn1 as i32; + /// + /// // Better: Cast to usize first, then comment with the reason for the truncation + /// fn fn2() -> i16 { + /// 1 + /// }; + /// let fn_ptr = fn2 as usize; + /// let fn_ptr_truncated = fn_ptr as i32; + /// ``` + pub FN_TO_NUMERIC_CAST_WITH_TRUNCATION, + style, + "casting a function pointer to a numeric type not wide enough to store the address" +} + +declare_clippy_lint! { + /// **What it does:** Checks for casts of `&T` to `&mut T` anywhere in the code. + /// + /// **Why is this bad?** It’s basically guaranteed to be undefined behaviour. + /// `UnsafeCell` is the only way to obtain aliasable data that is considered + /// mutable. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// ```rust,ignore + /// fn x(r: &i32) { + /// unsafe { + /// *(r as *const _ as *mut _) += 1; + /// } + /// } + /// ``` + /// + /// Instead consider using interior mutability types. + /// + /// ```rust + /// use std::cell::UnsafeCell; + /// + /// fn x(r: &UnsafeCell<i32>) { + /// unsafe { + /// *r.get() += 1; + /// } + /// } + /// ``` + pub CAST_REF_TO_MUT, + correctness, + "a cast of reference to a mutable pointer" +} + +declare_clippy_lint! { + /// **What it does:** Checks for expressions where a character literal is cast + /// to `u8` and suggests using a byte literal instead. + /// + /// **Why is this bad?** In general, casting values to smaller types is + /// error-prone and should be avoided where possible. In the particular case of + /// converting a character literal to u8, it is easy to avoid by just using a + /// byte literal instead. As an added bonus, `b'a'` is even slightly shorter + /// than `'a' as u8`. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// ```rust,ignore + /// 'x' as u8 + /// ``` + /// + /// A better version, using the byte literal: + /// + /// ```rust,ignore + /// b'x' + /// ``` + pub CHAR_LIT_AS_U8, + complexity, + "casting a character literal to `u8` truncates" +} + +declare_clippy_lint! { + /// **What it does:** + /// Checks for `as` casts between raw pointers without changing its mutability, + /// namely `*const T` to `*const U` and `*mut T` to `*mut U`. + /// + /// **Why is this bad?** + /// Though `as` casts between raw pointers is not terrible, `pointer::cast` is safer because + /// it cannot accidentally change the pointer's mutability nor cast the pointer to other types like `usize`. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// + /// ```rust + /// let ptr: *const u32 = &42_u32; + /// let mut_ptr: *mut u32 = &mut 42_u32; + /// let _ = ptr as *const i32; + /// let _ = mut_ptr as *mut i32; + /// ``` + /// Use instead: + /// ```rust + /// let ptr: *const u32 = &42_u32; + /// let mut_ptr: *mut u32 = &mut 42_u32; + /// let _ = ptr.cast::<i32>(); + /// let _ = mut_ptr.cast::<i32>(); + /// ``` + pub PTR_AS_PTR, + pedantic, + "casting using `as` from and to raw pointers that doesn't change its mutability, where `pointer::cast` could take the place of `as`" +} + +pub struct Casts { + msrv: Option<RustcVersion>, +} + +impl Casts { + #[must_use] + pub fn new(msrv: Option<RustcVersion>) -> Self { + Self { msrv } + } +} + +impl_lint_pass!(Casts => [ + CAST_PRECISION_LOSS, + CAST_SIGN_LOSS, + CAST_POSSIBLE_TRUNCATION, + CAST_POSSIBLE_WRAP, + CAST_LOSSLESS, + CAST_REF_TO_MUT, + CAST_PTR_ALIGNMENT, + UNNECESSARY_CAST, + FN_TO_NUMERIC_CAST, + FN_TO_NUMERIC_CAST_WITH_TRUNCATION, + CHAR_LIT_AS_U8, + PTR_AS_PTR, +]); + +impl<'tcx> LateLintPass<'tcx> for Casts { + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { + if expr.span.from_expansion() { + return; + } + + if let ExprKind::Cast(cast_expr, cast_to) = expr.kind { + if is_hir_ty_cfg_dependant(cx, cast_to) { + return; + } + let (cast_from, cast_to) = ( + cx.typeck_results().expr_ty(cast_expr), + cx.typeck_results().expr_ty(expr), + ); + + if unnecessary_cast::check(cx, expr, cast_expr, cast_from, cast_to) { + return; + } + + fn_to_numeric_cast::check(cx, expr, cast_expr, cast_from, cast_to); + fn_to_numeric_cast_with_truncation::check(cx, expr, cast_expr, cast_from, cast_to); + if cast_from.is_numeric() && cast_to.is_numeric() && !in_external_macro(cx.sess(), expr.span) { + cast_possible_truncation::check(cx, expr, cast_from, cast_to); + cast_possible_wrap::check(cx, expr, cast_from, cast_to); + cast_precision_loss::check(cx, expr, cast_from, cast_to); + cast_lossless::check(cx, expr, cast_expr, cast_from, cast_to); + cast_sign_loss::check(cx, expr, cast_expr, cast_from, cast_to); + } + } + + cast_ref_to_mut::check(cx, expr); + cast_ptr_alignment::check(cx, expr); + char_lit_as_u8::check(cx, expr); + ptr_as_ptr::check(cx, expr, &self.msrv); + } + + extract_msrv_attr!(LateContext); +} 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 new file mode 100644 index 0000000000000..9113e5a0920a2 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/casts/ptr_as_ptr.rs @@ -0,0 +1,51 @@ +use std::borrow::Cow; + +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::meets_msrv; +use clippy_utils::sugg::Sugg; +use if_chain::if_chain; +use rustc_errors::Applicability; +use rustc_hir::{Expr, ExprKind, Mutability, TyKind}; +use rustc_lint::LateContext; +use rustc_middle::ty::{self, TypeAndMut}; +use rustc_semver::RustcVersion; + +use super::PTR_AS_PTR; + +const PTR_AS_PTR_MSRV: RustcVersion = RustcVersion::new(1, 38, 0); + +pub(super) fn check(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, msrv: &Option<RustcVersion>) { + if !meets_msrv(msrv.as_ref(), &PTR_AS_PTR_MSRV) { + return; + } + + if_chain! { + if let ExprKind::Cast(cast_expr, cast_to_hir_ty) = expr.kind; + let (cast_from, cast_to) = (cx.typeck_results().expr_ty(cast_expr), cx.typeck_results().expr_ty(expr)); + if let ty::RawPtr(TypeAndMut { mutbl: from_mutbl, .. }) = cast_from.kind(); + if let ty::RawPtr(TypeAndMut { ty: to_pointee_ty, mutbl: to_mutbl }) = cast_to.kind(); + if matches!((from_mutbl, to_mutbl), + (Mutability::Not, Mutability::Not) | (Mutability::Mut, Mutability::Mut)); + // The `U` in `pointer::cast` have to be `Sized` + // as explained here: https://github.com/rust-lang/rust/issues/60602. + if to_pointee_ty.is_sized(cx.tcx.at(expr.span), cx.param_env); + then { + let mut applicability = Applicability::MachineApplicable; + let cast_expr_sugg = Sugg::hir_with_applicability(cx, cast_expr, "_", &mut applicability); + let turbofish = match &cast_to_hir_ty.kind { + TyKind::Infer => Cow::Borrowed(""), + TyKind::Ptr(mut_ty) if matches!(mut_ty.ty.kind, TyKind::Infer) => Cow::Borrowed(""), + _ => Cow::Owned(format!("::<{}>", to_pointee_ty)), + }; + span_lint_and_sugg( + cx, + PTR_AS_PTR, + expr.span, + "`as` casting between raw pointers without changing its mutability", + "try `pointer::cast`, a safer alternative", + format!("{}.cast{}()", cast_expr_sugg.maybe_par(), turbofish), + applicability, + ); + } + } +} diff --git a/src/tools/clippy/clippy_lints/src/casts/unnecessary_cast.rs b/src/tools/clippy/clippy_lints/src/casts/unnecessary_cast.rs new file mode 100644 index 0000000000000..9ed359922fd4d --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/casts/unnecessary_cast.rs @@ -0,0 +1,118 @@ +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::numeric_literal::NumericLiteral; +use clippy_utils::source::snippet_opt; +use if_chain::if_chain; +use rustc_ast::{LitFloatType, LitIntType, LitKind}; +use rustc_errors::Applicability; +use rustc_hir::{Expr, ExprKind, Lit, UnOp}; +use rustc_lint::{LateContext, LintContext}; +use rustc_middle::lint::in_external_macro; +use rustc_middle::ty::{self, FloatTy, InferTy, Ty}; + +use super::UNNECESSARY_CAST; + +pub(super) fn check( + cx: &LateContext<'_>, + expr: &Expr<'_>, + cast_expr: &Expr<'_>, + cast_from: Ty<'_>, + cast_to: Ty<'_>, +) -> bool { + if let Some(lit) = get_numeric_literal(cast_expr) { + let literal_str = snippet_opt(cx, cast_expr.span).unwrap_or_default(); + + if_chain! { + if let LitKind::Int(n, _) = lit.node; + if let Some(src) = snippet_opt(cx, lit.span); + if cast_to.is_floating_point(); + if let Some(num_lit) = NumericLiteral::from_lit_kind(&src, &lit.node); + let from_nbits = 128 - n.leading_zeros(); + let to_nbits = fp_ty_mantissa_nbits(cast_to); + if from_nbits != 0 && to_nbits != 0 && from_nbits <= to_nbits && num_lit.is_decimal(); + then { + let literal_str = if is_unary_neg(cast_expr) { format!("-{}", num_lit.integer) } else { num_lit.integer.into() }; + lint_unnecessary_cast(cx, expr, &literal_str, cast_from, cast_to); + return true + } + } + + match lit.node { + LitKind::Int(_, LitIntType::Unsuffixed) if cast_to.is_integral() => { + lint_unnecessary_cast(cx, expr, &literal_str, cast_from, cast_to); + }, + LitKind::Float(_, LitFloatType::Unsuffixed) if cast_to.is_floating_point() => { + lint_unnecessary_cast(cx, expr, &literal_str, cast_from, cast_to); + }, + LitKind::Int(_, LitIntType::Unsuffixed) | LitKind::Float(_, LitFloatType::Unsuffixed) => {}, + LitKind::Int(_, LitIntType::Signed(_) | LitIntType::Unsigned(_)) + | LitKind::Float(_, LitFloatType::Suffixed(_)) + if cast_from.kind() == cast_to.kind() => + { + if let Some(src) = snippet_opt(cx, lit.span) { + let num_lit = NumericLiteral::from_lit_kind(&src, &lit.node).unwrap(); + lint_unnecessary_cast(cx, expr, num_lit.integer, cast_from, cast_to); + } + }, + _ => { + if cast_from.kind() == cast_to.kind() && !in_external_macro(cx.sess(), expr.span) { + span_lint_and_sugg( + cx, + UNNECESSARY_CAST, + expr.span, + &format!( + "casting to the same type is unnecessary (`{}` -> `{}`)", + cast_from, cast_to + ), + "try", + literal_str, + Applicability::MachineApplicable, + ); + return true; + } + }, + } + } + + false +} + +fn lint_unnecessary_cast(cx: &LateContext<'_>, expr: &Expr<'_>, literal_str: &str, cast_from: Ty<'_>, cast_to: Ty<'_>) { + let literal_kind_name = if cast_from.is_integral() { "integer" } else { "float" }; + span_lint_and_sugg( + cx, + UNNECESSARY_CAST, + expr.span, + &format!("casting {} literal to `{}` is unnecessary", literal_kind_name, cast_to), + "try", + format!("{}_{}", literal_str.trim_end_matches('.'), cast_to), + Applicability::MachineApplicable, + ); +} + +fn get_numeric_literal<'e>(expr: &'e Expr<'e>) -> Option<&'e Lit> { + match expr.kind { + ExprKind::Lit(ref lit) => Some(lit), + ExprKind::Unary(UnOp::Neg, e) => { + if let ExprKind::Lit(ref lit) = e.kind { + Some(lit) + } else { + None + } + }, + _ => None, + } +} + +/// Returns the mantissa bits wide of a fp type. +/// Will return 0 if the type is not a fp +fn fp_ty_mantissa_nbits(typ: Ty<'_>) -> u32 { + match typ.kind() { + ty::Float(FloatTy::F32) => 23, + ty::Float(FloatTy::F64) | ty::Infer(InferTy::FloatVar(_)) => 52, + _ => 0, + } +} + +fn is_unary_neg(expr: &Expr<'_>) -> bool { + matches!(expr.kind, ExprKind::Unary(UnOp::Neg, _)) +} diff --git a/src/tools/clippy/clippy_lints/src/casts/utils.rs b/src/tools/clippy/clippy_lints/src/casts/utils.rs new file mode 100644 index 0000000000000..00fd0b3473b44 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/casts/utils.rs @@ -0,0 +1,25 @@ +use rustc_middle::ty::{self, IntTy, Ty, TyCtxt, UintTy}; + +/// Returns the size in bits of an integral type. +/// Will return 0 if the type is not an int or uint variant +pub(super) fn int_ty_to_nbits(typ: Ty<'_>, tcx: TyCtxt<'_>) -> u64 { + match typ.kind() { + ty::Int(i) => match i { + IntTy::Isize => tcx.data_layout.pointer_size.bits(), + IntTy::I8 => 8, + IntTy::I16 => 16, + IntTy::I32 => 32, + IntTy::I64 => 64, + IntTy::I128 => 128, + }, + ty::Uint(i) => match i { + UintTy::Usize => tcx.data_layout.pointer_size.bits(), + UintTy::U8 => 8, + UintTy::U16 => 16, + UintTy::U32 => 32, + UintTy::U64 => 64, + UintTy::U128 => 128, + }, + _ => 0, + } +} diff --git a/src/tools/clippy/clippy_lints/src/checked_conversions.rs b/src/tools/clippy/clippy_lints/src/checked_conversions.rs index 54bc69e058bc7..d7136f84cc3af 100644 --- a/src/tools/clippy/clippy_lints/src/checked_conversions.rs +++ b/src/tools/clippy/clippy_lints/src/checked_conversions.rs @@ -1,5 +1,8 @@ //! lint on manually implemented checked conversions that could be transformed into `try_from` +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::source::snippet_with_applicability; +use clippy_utils::{meets_msrv, SpanlessEq}; use if_chain::if_chain; use rustc_ast::ast::LitKind; use rustc_errors::Applicability; @@ -9,8 +12,6 @@ use rustc_middle::lint::in_external_macro; use rustc_semver::RustcVersion; use rustc_session::{declare_tool_lint, impl_lint_pass}; -use crate::utils::{meets_msrv, snippet_with_applicability, span_lint_and_sugg, SpanlessEq}; - const CHECKED_CONVERSIONS_MSRV: RustcVersion = RustcVersion::new(1, 34, 0); declare_clippy_lint! { @@ -63,7 +64,7 @@ impl<'tcx> LateLintPass<'tcx> for CheckedConversions { let result = if_chain! { if !in_external_macro(cx.sess(), item.span); - if let ExprKind::Binary(op, ref left, ref right) = &item.kind; + if let ExprKind::Binary(op, left, right) = &item.kind; then { match op.node { @@ -199,7 +200,7 @@ impl ConversionType { /// Check for `expr <= (to_type::MAX as from_type)` fn check_upper_bound<'tcx>(expr: &'tcx Expr<'tcx>) -> Option<Conversion<'tcx>> { if_chain! { - if let ExprKind::Binary(ref op, ref left, ref right) = &expr.kind; + if let ExprKind::Binary(ref op, left, right) = &expr.kind; if let Some((candidate, check)) = normalize_le_ge(op, left, right); if let Some((from, to)) = get_types_from_cast(check, INTS, "max_value", "MAX"); @@ -218,7 +219,7 @@ fn check_lower_bound<'tcx>(expr: &'tcx Expr<'tcx>) -> Option<Conversion<'tcx>> { } // First of we need a binary containing the expression & the cast - if let ExprKind::Binary(ref op, ref left, ref right) = &expr.kind { + if let ExprKind::Binary(ref op, left, right) = &expr.kind { normalize_le_ge(op, right, left).and_then(|(l, r)| check_function(l, r)) } else { None @@ -259,7 +260,7 @@ fn get_types_from_cast<'a>( // or `to_type::MAX as from_type` let call_from_cast: Option<(&Expr<'_>, &str)> = if_chain! { // to_type::max_value(), from_type - if let ExprKind::Cast(ref limit, ref from_type) = &expr.kind; + if let ExprKind::Cast(limit, from_type) = &expr.kind; if let TyKind::Path(ref from_type_path) = &from_type.kind; if let Some(from_sym) = int_ty_to_sym(from_type_path); @@ -274,7 +275,7 @@ fn get_types_from_cast<'a>( let limit_from: Option<(&Expr<'_>, &str)> = call_from_cast.or_else(|| { if_chain! { // `from_type::from, to_type::max_value()` - if let ExprKind::Call(ref from_func, ref args) = &expr.kind; + if let ExprKind::Call(from_func, args) = &expr.kind; // `to_type::max_value()` if args.len() == 1; if let limit = &args[0]; @@ -316,13 +317,12 @@ fn get_types_from_cast<'a>( /// Gets the type which implements the called function fn get_implementing_type<'a>(path: &QPath<'_>, candidates: &'a [&str], function: &str) -> Option<&'a str> { if_chain! { - if let QPath::TypeRelative(ref ty, ref path) = &path; + if let QPath::TypeRelative(ty, path) = &path; if path.ident.name.as_str() == function; - if let TyKind::Path(QPath::Resolved(None, ref tp)) = &ty.kind; + if let TyKind::Path(QPath::Resolved(None, tp)) = &ty.kind; if let [int] = &*tp.segments; - let name = &int.ident.name.as_str(); - then { + let name = &int.ident.name.as_str(); candidates.iter().find(|c| name == *c).cloned() } else { None @@ -333,11 +333,10 @@ fn get_implementing_type<'a>(path: &QPath<'_>, candidates: &'a [&str], function: /// Gets the type as a string, if it is a supported integer fn int_ty_to_sym<'tcx>(path: &QPath<'_>) -> Option<&'tcx str> { if_chain! { - if let QPath::Resolved(_, ref path) = *path; + if let QPath::Resolved(_, path) = *path; if let [ty] = &*path.segments; - let name = &ty.ident.name.as_str(); - then { + let name = &ty.ident.name.as_str(); INTS.iter().find(|c| name == *c).cloned() } else { None diff --git a/src/tools/clippy/clippy_lints/src/cognitive_complexity.rs b/src/tools/clippy/clippy_lints/src/cognitive_complexity.rs index f21a734bb439f..f62c6a9c3251c 100644 --- a/src/tools/clippy/clippy_lints/src/cognitive_complexity.rs +++ b/src/tools/clippy/clippy_lints/src/cognitive_complexity.rs @@ -1,5 +1,9 @@ //! calculate cognitive complexity and warn about overly complex functions +use clippy_utils::diagnostics::span_lint_and_help; +use clippy_utils::source::snippet_opt; +use clippy_utils::ty::is_type_diagnostic_item; +use clippy_utils::LimitStack; use rustc_ast::ast::Attribute; use rustc_hir::intravisit::{walk_expr, FnKind, NestedVisitorMap, Visitor}; use rustc_hir::{Body, Expr, ExprKind, FnDecl, HirId}; @@ -9,8 +13,6 @@ use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::source_map::Span; use rustc_span::{sym, BytePos}; -use crate::utils::{is_type_diagnostic_item, snippet_opt, span_lint_and_help, LimitStack}; - declare_clippy_lint! { /// **What it does:** Checks for methods with high cognitive complexity. /// @@ -76,8 +78,8 @@ impl CognitiveComplexity { if rust_cc > self.limit.limit() { let fn_span = match kind { - FnKind::ItemFn(ident, _, _, _, _) | FnKind::Method(ident, _, _, _) => ident.span, - FnKind::Closure(_) => { + FnKind::ItemFn(ident, _, _, _) | FnKind::Method(ident, _, _) => ident.span, + FnKind::Closure => { let header_span = body_span.with_hi(decl.output.span().lo()); let pos = snippet_opt(cx, header_span).and_then(|snip| { let low_offset = snip.find('|')?; @@ -150,7 +152,7 @@ impl<'tcx> Visitor<'tcx> for CcHelper { ExprKind::If(_, _, _) => { self.cc += 1; }, - ExprKind::Match(_, ref arms, _) => { + ExprKind::Match(_, arms, _) => { if arms.len() > 1 { self.cc += 1; } diff --git a/src/tools/clippy/clippy_lints/src/collapsible_if.rs b/src/tools/clippy/clippy_lints/src/collapsible_if.rs index 34f0e6ab02705..dae5c86bd4437 100644 --- a/src/tools/clippy/clippy_lints/src/collapsible_if.rs +++ b/src/tools/clippy/clippy_lints/src/collapsible_if.rs @@ -12,15 +12,15 @@ //! //! This lint is **warn** by default +use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then}; +use clippy_utils::source::{snippet_block, snippet_block_with_applicability}; +use clippy_utils::sugg::Sugg; use if_chain::if_chain; use rustc_ast::ast; +use rustc_errors::Applicability; use rustc_lint::{EarlyContext, EarlyLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; -use crate::utils::sugg::Sugg; -use crate::utils::{snippet_block, snippet_block_with_applicability, span_lint_and_sugg, span_lint_and_then}; -use rustc_errors::Applicability; - declare_clippy_lint! { /// **What it does:** Checks for nested `if` statements which can be collapsed /// by `&&`-combining their conditions. diff --git a/src/tools/clippy/clippy_lints/src/collapsible_match.rs b/src/tools/clippy/clippy_lints/src/collapsible_match.rs index 3c45525684be4..04fff237bb4cb 100644 --- a/src/tools/clippy/clippy_lints/src/collapsible_match.rs +++ b/src/tools/clippy/clippy_lints/src/collapsible_match.rs @@ -1,5 +1,6 @@ -use crate::utils::visitors::LocalUsedVisitor; -use crate::utils::{path_to_local, span_lint_and_then, SpanlessEq}; +use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::visitors::LocalUsedVisitor; +use clippy_utils::{path_to_local, SpanlessEq}; use if_chain::if_chain; use rustc_hir::def::{CtorKind, CtorOf, DefKind, Res}; use rustc_hir::{Arm, Expr, ExprKind, Guard, HirId, Pat, PatKind, QPath, StmtKind, UnOp}; @@ -61,8 +62,8 @@ impl<'tcx> LateLintPass<'tcx> for CollapsibleMatch { } fn check_arm<'tcx>(arm: &Arm<'tcx>, wild_outer_arm: &Arm<'tcx>, cx: &LateContext<'tcx>) { + let expr = strip_singleton_blocks(arm.body); if_chain! { - let expr = strip_singleton_blocks(arm.body); if let ExprKind::Match(expr_in, arms_inner, _) = expr.kind; // the outer arm pattern and the inner match if expr_in.span.ctxt() == arm.pat.span.ctxt(); diff --git a/src/tools/clippy/clippy_lints/src/comparison_chain.rs b/src/tools/clippy/clippy_lints/src/comparison_chain.rs index 90d31dece1311..31ae63b51849c 100644 --- a/src/tools/clippy/clippy_lints/src/comparison_chain.rs +++ b/src/tools/clippy/clippy_lints/src/comparison_chain.rs @@ -1,6 +1,6 @@ -use crate::utils::{ - get_trait_def_id, if_sequence, implements_trait, parent_node_is_if_expr, paths, span_lint_and_help, SpanlessEq, -}; +use clippy_utils::diagnostics::span_lint_and_help; +use clippy_utils::ty::implements_trait; +use clippy_utils::{get_trait_def_id, if_sequence, parent_node_is_if_expr, paths, SpanlessEq}; use rustc_hir::{BinOpKind, Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; @@ -71,10 +71,8 @@ impl<'tcx> LateLintPass<'tcx> for ComparisonChain { } for cond in conds.windows(2) { - if let ( - &ExprKind::Binary(ref kind1, ref lhs1, ref rhs1), - &ExprKind::Binary(ref kind2, ref lhs2, ref rhs2), - ) = (&cond[0].kind, &cond[1].kind) + if let (&ExprKind::Binary(ref kind1, lhs1, rhs1), &ExprKind::Binary(ref kind2, lhs2, rhs2)) = + (&cond[0].kind, &cond[1].kind) { if !kind_is_cmp(kind1.node) || !kind_is_cmp(kind2.node) { return; @@ -117,7 +115,7 @@ impl<'tcx> LateLintPass<'tcx> for ComparisonChain { expr.span, "`if` chain can be rewritten with `match`", None, - "Consider rewriting the `if` chain to use `cmp` and `match`.", + "consider rewriting the `if` chain to use `cmp` and `match`", ) } } diff --git a/src/tools/clippy/clippy_lints/src/copies.rs b/src/tools/clippy/clippy_lints/src/copies.rs index 944aaafb46de5..8b503c9a0306b 100644 --- a/src/tools/clippy/clippy_lints/src/copies.rs +++ b/src/tools/clippy/clippy_lints/src/copies.rs @@ -1,8 +1,19 @@ -use crate::utils::{eq_expr_value, in_macro, search_same, SpanlessEq, SpanlessHash}; -use crate::utils::{get_parent_expr, if_sequence, span_lint_and_note}; -use rustc_hir::{Block, Expr, ExprKind}; +use clippy_utils::diagnostics::{span_lint_and_note, span_lint_and_then}; +use clippy_utils::source::{first_line_of_span, indent_of, reindent_multiline, snippet, snippet_opt}; +use clippy_utils::{ + both, count_eq, eq_expr_value, get_enclosing_block, get_parent_expr, if_sequence, in_macro, parent_node_is_if_expr, + run_lints, search_same, ContainsName, SpanlessEq, SpanlessHash, +}; +use if_chain::if_chain; +use rustc_data_structures::fx::FxHashSet; +use rustc_errors::{Applicability, DiagnosticBuilder}; +use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor}; +use rustc_hir::{Block, Expr, ExprKind, HirId}; use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::hir::map::Map; use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::{source_map::Span, symbol::Symbol, BytePos}; +use std::borrow::Cow; declare_clippy_lint! { /// **What it does:** Checks for consecutive `if`s with the same condition. @@ -103,47 +114,457 @@ declare_clippy_lint! { "`if` with the same `then` and `else` blocks" } -declare_lint_pass!(CopyAndPaste => [IFS_SAME_COND, SAME_FUNCTIONS_IN_IF_CONDITION, IF_SAME_THEN_ELSE]); +declare_clippy_lint! { + /// **What it does:** Checks if the `if` and `else` block contain shared code that can be + /// moved out of the blocks. + /// + /// **Why is this bad?** Duplicate code is less maintainable. + /// + /// **Known problems:** Hopefully none. + /// + /// **Example:** + /// ```ignore + /// let foo = if … { + /// println!("Hello World"); + /// 13 + /// } else { + /// println!("Hello World"); + /// 42 + /// }; + /// ``` + /// + /// Could be written as: + /// ```ignore + /// println!("Hello World"); + /// let foo = if … { + /// 13 + /// } else { + /// 42 + /// }; + /// ``` + pub BRANCHES_SHARING_CODE, + complexity, + "`if` statement with shared code in all blocks" +} + +declare_lint_pass!(CopyAndPaste => [ + IFS_SAME_COND, + SAME_FUNCTIONS_IN_IF_CONDITION, + IF_SAME_THEN_ELSE, + BRANCHES_SHARING_CODE +]); impl<'tcx> LateLintPass<'tcx> for CopyAndPaste { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { if !expr.span.from_expansion() { - // skip ifs directly in else, it will be checked in the parent if - if let Some(&Expr { - kind: ExprKind::If(_, _, Some(ref else_expr)), - .. - }) = get_parent_expr(cx, expr) - { - if else_expr.hir_id == expr.hir_id { - return; + if let ExprKind::If(_, _, _) = expr.kind { + // skip ifs directly in else, it will be checked in the parent if + if let Some(&Expr { + kind: ExprKind::If(_, _, Some(else_expr)), + .. + }) = get_parent_expr(cx, expr) + { + if else_expr.hir_id == expr.hir_id { + return; + } } + + let (conds, blocks) = if_sequence(expr); + // Conditions + lint_same_cond(cx, &conds); + lint_same_fns_in_if_cond(cx, &conds); + // Block duplication + lint_same_then_else(cx, &blocks, conds.len() == blocks.len(), expr); } + } + } +} - let (conds, blocks) = if_sequence(expr); - lint_same_then_else(cx, &blocks); - lint_same_cond(cx, &conds); - lint_same_fns_in_if_cond(cx, &conds); +/// Implementation of `BRANCHES_SHARING_CODE` and `IF_SAME_THEN_ELSE` if the blocks are equal. +fn lint_same_then_else<'tcx>( + cx: &LateContext<'tcx>, + blocks: &[&Block<'tcx>], + has_conditional_else: bool, + expr: &'tcx Expr<'_>, +) { + // We only lint ifs with multiple blocks + if blocks.len() < 2 || parent_node_is_if_expr(expr, cx) { + return; + } + + // Check if each block has shared code + let has_expr = blocks[0].expr.is_some(); + let (start_eq, mut end_eq, expr_eq) = scan_block_for_eq(cx, blocks); + + // BRANCHES_SHARING_CODE prerequisites + if has_conditional_else || (start_eq == 0 && end_eq == 0 && (has_expr && !expr_eq)) { + return; + } + + // Only the start is the same + if start_eq != 0 && end_eq == 0 && (!has_expr || !expr_eq) { + let block = blocks[0]; + let start_stmts = block.stmts.split_at(start_eq).0; + + let mut start_walker = UsedValueFinderVisitor::new(cx); + for stmt in start_stmts { + intravisit::walk_stmt(&mut start_walker, stmt); } + + emit_branches_sharing_code_lint( + cx, + start_eq, + 0, + false, + check_for_warn_of_moved_symbol(cx, &start_walker.def_symbols, expr), + blocks, + expr, + ); + } else if end_eq != 0 || (has_expr && expr_eq) { + let block = blocks[blocks.len() - 1]; + let (start_stmts, block_stmts) = block.stmts.split_at(start_eq); + let (block_stmts, end_stmts) = block_stmts.split_at(block_stmts.len() - end_eq); + + // Scan start + let mut start_walker = UsedValueFinderVisitor::new(cx); + for stmt in start_stmts { + intravisit::walk_stmt(&mut start_walker, stmt); + } + let mut moved_syms = start_walker.def_symbols; + + // Scan block + let mut block_walker = UsedValueFinderVisitor::new(cx); + for stmt in block_stmts { + intravisit::walk_stmt(&mut block_walker, stmt); + } + let mut block_defs = block_walker.defs; + + // Scan moved stmts + let mut moved_start: Option<usize> = None; + let mut end_walker = UsedValueFinderVisitor::new(cx); + for (index, stmt) in end_stmts.iter().enumerate() { + intravisit::walk_stmt(&mut end_walker, stmt); + + for value in &end_walker.uses { + // Well we can't move this and all prev statements. So reset + if block_defs.contains(value) { + moved_start = Some(index + 1); + end_walker.defs.drain().for_each(|x| { + block_defs.insert(x); + }); + + end_walker.def_symbols.clear(); + } + } + + end_walker.uses.clear(); + } + + if let Some(moved_start) = moved_start { + end_eq -= moved_start; + } + + let end_linable = block.expr.map_or_else( + || end_eq != 0, + |expr| { + intravisit::walk_expr(&mut end_walker, expr); + end_walker.uses.iter().any(|x| !block_defs.contains(x)) + }, + ); + + if end_linable { + end_walker.def_symbols.drain().for_each(|x| { + moved_syms.insert(x); + }); + } + + emit_branches_sharing_code_lint( + cx, + start_eq, + end_eq, + end_linable, + check_for_warn_of_moved_symbol(cx, &moved_syms, expr), + blocks, + expr, + ); } } -/// Implementation of `IF_SAME_THEN_ELSE`. -fn lint_same_then_else(cx: &LateContext<'_>, blocks: &[&Block<'_>]) { - let eq: &dyn Fn(&&Block<'_>, &&Block<'_>) -> bool = - &|&lhs, &rhs| -> bool { SpanlessEq::new(cx).eq_block(lhs, rhs) }; +fn scan_block_for_eq(cx: &LateContext<'tcx>, blocks: &[&Block<'tcx>]) -> (usize, usize, bool) { + let mut start_eq = usize::MAX; + let mut end_eq = usize::MAX; + let mut expr_eq = true; + for win in blocks.windows(2) { + let l_stmts = win[0].stmts; + let r_stmts = win[1].stmts; - if let Some((i, j)) = search_same_sequenced(blocks, eq) { - span_lint_and_note( + // `SpanlessEq` now keeps track of the locals and is therefore context sensitive clippy#6752. + // The comparison therefore needs to be done in a way that builds the correct context. + let mut evaluator = SpanlessEq::new(cx); + let mut evaluator = evaluator.inter_expr(); + + let current_start_eq = count_eq(&mut l_stmts.iter(), &mut r_stmts.iter(), |l, r| evaluator.eq_stmt(l, r)); + + let current_end_eq = { + // We skip the middle statements which can't be equal + let end_comparison_count = l_stmts.len().min(r_stmts.len()) - current_start_eq; + let it1 = l_stmts.iter().skip(l_stmts.len() - end_comparison_count); + let it2 = r_stmts.iter().skip(r_stmts.len() - end_comparison_count); + it1.zip(it2) + .fold(0, |acc, (l, r)| if evaluator.eq_stmt(l, r) { acc + 1 } else { 0 }) + }; + let block_expr_eq = both(&win[0].expr, &win[1].expr, |l, r| evaluator.eq_expr(l, r)); + + // IF_SAME_THEN_ELSE + if_chain! { + if block_expr_eq; + if l_stmts.len() == r_stmts.len(); + if l_stmts.len() == current_start_eq; + if run_lints(cx, &[IF_SAME_THEN_ELSE], win[0].hir_id); + if run_lints(cx, &[IF_SAME_THEN_ELSE], win[1].hir_id); + then { + span_lint_and_note( + cx, + IF_SAME_THEN_ELSE, + win[0].span, + "this `if` has identical blocks", + Some(win[1].span), + "same as this", + ); + + return (0, 0, false); + } + } + + start_eq = start_eq.min(current_start_eq); + end_eq = end_eq.min(current_end_eq); + expr_eq &= block_expr_eq; + } + + let has_expr = blocks[0].expr.is_some(); + if has_expr && !expr_eq { + end_eq = 0; + } + + // Check if the regions are overlapping. Set `end_eq` to prevent the overlap + let min_block_size = blocks.iter().map(|x| x.stmts.len()).min().unwrap(); + if (start_eq + end_eq) > min_block_size { + end_eq = min_block_size - start_eq; + } + + (start_eq, end_eq, expr_eq) +} + +fn check_for_warn_of_moved_symbol( + cx: &LateContext<'tcx>, + symbols: &FxHashSet<Symbol>, + if_expr: &'tcx 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); + + symbols + .iter() + .filter(|sym| !sym.as_str().starts_with('_')) + .any(move |sym| { + let mut walker = ContainsName { + name: *sym, + result: false, + }; + + // Scan block + block + .stmts + .iter() + .filter(|stmt| !ignore_span.overlaps(stmt.span)) + .for_each(|stmt| intravisit::walk_stmt(&mut walker, stmt)); + + if let Some(expr) = block.expr { + intravisit::walk_expr(&mut walker, expr); + } + + walker.result + }) + }) +} + +fn emit_branches_sharing_code_lint( + cx: &LateContext<'tcx>, + start_stmts: usize, + end_stmts: usize, + lint_end: bool, + warn_about_moved_symbol: bool, + blocks: &[&Block<'tcx>], + if_expr: &'tcx Expr<'_>, +) { + if start_stmts == 0 && !lint_end { + return; + } + + // (help, span, suggestion) + let mut suggestions: Vec<(&str, Span, String)> = vec![]; + let mut add_expr_note = false; + + // Construct suggestions + if start_stmts > 0 { + let block = blocks[0]; + let span_start = first_line_of_span(cx, if_expr.span).shrink_to_lo(); + let span_end = block.stmts[start_stmts - 1].span.source_callsite(); + + let cond_span = first_line_of_span(cx, if_expr.span).until(block.span); + let cond_snippet = reindent_multiline(snippet(cx, cond_span, "_"), false, None); + let cond_indent = indent_of(cx, cond_span); + let moved_span = block.stmts[0].span.source_callsite().to(span_end); + let moved_snippet = reindent_multiline(snippet(cx, moved_span, "_"), true, None); + let suggestion = moved_snippet.to_string() + "\n" + &cond_snippet + "{"; + let suggestion = reindent_multiline(Cow::Borrowed(&suggestion), true, cond_indent); + + let span = span_start.to(span_end); + suggestions.push(("start", span, suggestion.to_string())); + } + + if lint_end { + let block = blocks[blocks.len() - 1]; + let span_end = block.span.shrink_to_hi(); + + let moved_start = if end_stmts == 0 && block.expr.is_some() { + block.expr.unwrap().span + } else { + block.stmts[block.stmts.len() - end_stmts].span + } + .source_callsite(); + let moved_end = block + .expr + .map_or_else(|| block.stmts[block.stmts.len() - 1].span, |expr| expr.span) + .source_callsite(); + + let moved_span = moved_start.to(moved_end); + let moved_snipped = reindent_multiline(snippet(cx, moved_span, "_"), true, None); + let indent = indent_of(cx, if_expr.span.shrink_to_hi()); + let suggestion = "}\n".to_string() + &moved_snipped; + let suggestion = reindent_multiline(Cow::Borrowed(&suggestion), true, indent); + + let mut span = moved_start.to(span_end); + // Improve formatting if the inner block has indention (i.e. normal Rust formatting) + let test_span = Span::new(span.lo() - BytePos(4), span.lo(), span.ctxt()); + if snippet_opt(cx, test_span) + .map(|snip| snip == " ") + .unwrap_or_default() + { + span = span.with_lo(test_span.lo()); + } + + suggestions.push(("end", span, suggestion.to_string())); + add_expr_note = !cx.typeck_results().expr_ty(if_expr).is_unit() + } + + let add_optional_msgs = |diag: &mut DiagnosticBuilder<'_>| { + if add_expr_note { + diag.note("The end suggestion probably needs some adjustments to use the expression result correctly"); + } + + if warn_about_moved_symbol { + diag.warn("Some moved values might need to be renamed to avoid wrong references"); + } + }; + + // Emit lint + if suggestions.len() == 1 { + let (place_str, span, sugg) = suggestions.pop().unwrap(); + let msg = format!("all if blocks contain the same code at the {}", place_str); + let help = format!("consider moving the {} statements out like this", place_str); + span_lint_and_then(cx, BRANCHES_SHARING_CODE, span, msg.as_str(), |diag| { + diag.span_suggestion(span, help.as_str(), sugg, Applicability::Unspecified); + + add_optional_msgs(diag); + }); + } else if suggestions.len() == 2 { + let (_, end_span, end_sugg) = suggestions.pop().unwrap(); + let (_, start_span, start_sugg) = suggestions.pop().unwrap(); + span_lint_and_then( cx, - IF_SAME_THEN_ELSE, - j.span, - "this `if` has identical blocks", - Some(i.span), - "same as this", + BRANCHES_SHARING_CODE, + start_span, + "all if blocks contain the same code at the start and the end. Here at the start", + move |diag| { + diag.span_note(end_span, "and here at the end"); + + diag.span_suggestion( + start_span, + "consider moving the start statements out like this", + start_sugg, + Applicability::Unspecified, + ); + + diag.span_suggestion( + end_span, + "and consider moving the end statements out like this", + end_sugg, + Applicability::Unspecified, + ); + + add_optional_msgs(diag); + }, ); } } +/// This visitor collects `HirId`s and Symbols of defined symbols and `HirId`s of used values. +struct UsedValueFinderVisitor<'a, 'tcx> { + cx: &'a LateContext<'tcx>, + + /// The `HirId`s of defined values in the scanned statements + defs: FxHashSet<HirId>, + + /// The Symbols of the defined symbols in the scanned statements + def_symbols: FxHashSet<Symbol>, + + /// The `HirId`s of the used values + uses: FxHashSet<HirId>, +} + +impl<'a, 'tcx> UsedValueFinderVisitor<'a, 'tcx> { + fn new(cx: &'a LateContext<'tcx>) -> Self { + UsedValueFinderVisitor { + cx, + defs: FxHashSet::default(), + def_symbols: FxHashSet::default(), + uses: FxHashSet::default(), + } + } +} + +impl<'a, 'tcx> Visitor<'tcx> for UsedValueFinderVisitor<'a, 'tcx> { + type Map = Map<'tcx>; + + fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> { + NestedVisitorMap::All(self.cx.tcx.hir()) + } + + fn visit_local(&mut self, l: &'tcx rustc_hir::Local<'tcx>) { + let local_id = l.pat.hir_id; + self.defs.insert(local_id); + + if let Some(sym) = l.pat.simple_ident() { + self.def_symbols.insert(sym.name); + } + + if let Some(expr) = l.init { + intravisit::walk_expr(self, expr); + } + } + + fn visit_qpath(&mut self, qpath: &'tcx rustc_hir::QPath<'tcx>, id: HirId, _span: rustc_span::Span) { + if let rustc_hir::QPath::Resolved(_, path) = *qpath { + if path.segments.len() == 1 { + if let rustc_hir::def::Res::Local(var) = self.cx.qpath_res(qpath, id) { + self.uses.insert(var); + } + } + } + } +} + /// Implementation of `IFS_SAME_COND`. fn lint_same_cond(cx: &LateContext<'_>, conds: &[&Expr<'_>]) { let hash: &dyn Fn(&&Expr<'_>) -> u64 = &|expr| -> u64 { @@ -197,15 +618,3 @@ fn lint_same_fns_in_if_cond(cx: &LateContext<'_>, conds: &[&Expr<'_>]) { ); } } - -fn search_same_sequenced<T, Eq>(exprs: &[T], eq: Eq) -> Option<(&T, &T)> -where - Eq: Fn(&T, &T) -> bool, -{ - for win in exprs.windows(2) { - if eq(&win[0], &win[1]) { - return Some((&win[0], &win[1])); - } - } - None -} diff --git a/src/tools/clippy/clippy_lints/src/copy_iterator.rs b/src/tools/clippy/clippy_lints/src/copy_iterator.rs index 004bce5f62a8f..35079c6bedc02 100644 --- a/src/tools/clippy/clippy_lints/src/copy_iterator.rs +++ b/src/tools/clippy/clippy_lints/src/copy_iterator.rs @@ -1,7 +1,11 @@ -use crate::utils::{is_copy, match_path, paths, span_lint_and_note}; +use clippy_utils::diagnostics::span_lint_and_note; +use clippy_utils::ty::is_copy; use rustc_hir::{Impl, Item, ItemKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::sym; + +use if_chain::if_chain; declare_clippy_lint! { /// **What it does:** Checks for types that implement `Copy` as well as @@ -33,14 +37,16 @@ declare_lint_pass!(CopyIterator => [COPY_ITERATOR]); impl<'tcx> LateLintPass<'tcx> for CopyIterator { fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) { - if let ItemKind::Impl(Impl { - of_trait: Some(ref trait_ref), - .. - }) = item.kind - { + if_chain! { + if let ItemKind::Impl(Impl { + of_trait: Some(ref trait_ref), + .. + }) = item.kind; let ty = cx.tcx.type_of(item.def_id); - - if is_copy(cx, ty) && match_path(&trait_ref.path, &paths::ITERATOR) { + if is_copy(cx, ty); + if let Some(trait_id) = trait_ref.trait_def_id(); + if cx.tcx.is_diagnostic_item(sym::Iterator, trait_id); + then { span_lint_and_note( cx, COPY_ITERATOR, diff --git a/src/tools/clippy/clippy_lints/src/create_dir.rs b/src/tools/clippy/clippy_lints/src/create_dir.rs index 200b6a565cc43..7b5cce6462a43 100644 --- a/src/tools/clippy/clippy_lints/src/create_dir.rs +++ b/src/tools/clippy/clippy_lints/src/create_dir.rs @@ -1,4 +1,6 @@ -use crate::utils::{match_def_path, paths, snippet, span_lint_and_sugg}; +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::source::snippet; +use clippy_utils::{match_def_path, paths}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind}; @@ -31,7 +33,7 @@ declare_lint_pass!(CreateDir => [CREATE_DIR]); impl LateLintPass<'_> for CreateDir { fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) { if_chain! { - if let ExprKind::Call(ref func, ref args) = expr.kind; + if let ExprKind::Call(func, args) = expr.kind; if let ExprKind::Path(ref path) = func.kind; if let Some(def_id) = cx.qpath_res(path, func.hir_id).opt_def_id(); if match_def_path(cx, def_id, &paths::STD_FS_CREATE_DIR); diff --git a/src/tools/clippy/clippy_lints/src/dbg_macro.rs b/src/tools/clippy/clippy_lints/src/dbg_macro.rs index e513dcce64e53..286cc7e223efb 100644 --- a/src/tools/clippy/clippy_lints/src/dbg_macro.rs +++ b/src/tools/clippy/clippy_lints/src/dbg_macro.rs @@ -1,4 +1,5 @@ -use crate::utils::{snippet_opt, span_lint_and_help, span_lint_and_sugg}; +use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_sugg}; +use clippy_utils::source::snippet_opt; use rustc_ast::ast; use rustc_ast::tokenstream::TokenStream; use rustc_errors::Applicability; diff --git a/src/tools/clippy/clippy_lints/src/default.rs b/src/tools/clippy/clippy_lints/src/default.rs index 6fa1378b8c73d..710da8fe9e037 100644 --- a/src/tools/clippy/clippy_lints/src/default.rs +++ b/src/tools/clippy/clippy_lints/src/default.rs @@ -1,7 +1,6 @@ -use crate::utils::{ - any_parent_is_automatically_derived, contains_name, match_def_path, paths, snippet_with_macro_callsite, -}; -use crate::utils::{span_lint_and_note, span_lint_and_sugg}; +use clippy_utils::diagnostics::{span_lint_and_note, span_lint_and_sugg}; +use clippy_utils::source::snippet_with_macro_callsite; +use clippy_utils::{any_parent_is_automatically_derived, contains_name, match_def_path, paths}; use if_chain::if_chain; use rustc_data_structures::fx::FxHashSet; use rustc_errors::Applicability; @@ -78,33 +77,33 @@ impl LateLintPass<'_> for Default { if_chain! { // Avoid cases already linted by `field_reassign_with_default` if !self.reassigned_linted.contains(&expr.span); - if let ExprKind::Call(ref path, ..) = expr.kind; + if let ExprKind::Call(path, ..) = expr.kind; if !any_parent_is_automatically_derived(cx.tcx, expr.hir_id); if let ExprKind::Path(ref qpath) = path.kind; if let Some(def_id) = cx.qpath_res(qpath, path.hir_id).opt_def_id(); if match_def_path(cx, def_id, &paths::DEFAULT_TRAIT_METHOD); // Detect and ignore <Foo as Default>::default() because these calls do explicitly name the type. if let QPath::Resolved(None, _path) = qpath; + let expr_ty = cx.typeck_results().expr_ty(expr); + if let ty::Adt(def, ..) = expr_ty.kind(); then { - let expr_ty = cx.typeck_results().expr_ty(expr); - if let ty::Adt(def, ..) = expr_ty.kind() { - // TODO: Work out a way to put "whatever the imported way of referencing - // this type in this file" rather than a fully-qualified type. - let replacement = format!("{}::default()", cx.tcx.def_path_str(def.did)); - span_lint_and_sugg( - cx, - DEFAULT_TRAIT_ACCESS, - expr.span, - &format!("calling `{}` is more clear than this expression", replacement), - "try", - replacement, - Applicability::Unspecified, // First resolve the TODO above - ); - } + // TODO: Work out a way to put "whatever the imported way of referencing + // this type in this file" rather than a fully-qualified type. + let replacement = format!("{}::default()", cx.tcx.def_path_str(def.did)); + span_lint_and_sugg( + cx, + DEFAULT_TRAIT_ACCESS, + expr.span, + &format!("calling `{}` is more clear than this expression", replacement), + "try", + replacement, + Applicability::Unspecified, // First resolve the TODO above + ); } } } + #[allow(clippy::too_many_lines)] fn check_block<'tcx>(&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 @@ -198,6 +197,24 @@ impl LateLintPass<'_> for Default { .collect::<Vec<String>>() .join(", "); + // give correct suggestion if generics are involved (see #6944) + let binding_type = if_chain! { + if let ty::Adt(adt_def, substs) = binding_type.kind(); + if !substs.is_empty(); + then { + let adt_def_ty_name = cx.tcx.item_name(adt_def.did); + let generic_args = substs.iter().collect::<Vec<_>>(); + let tys_str = generic_args + .iter() + .map(ToString::to_string) + .collect::<Vec<_>>() + .join(", "); + format!("{}::<{}>", adt_def_ty_name, &tys_str) + } else { + binding_type.to_string() + } + }; + let sugg = if ext_with_default { if field_list.is_empty() { format!("{}::default()", binding_type) @@ -229,7 +246,7 @@ impl LateLintPass<'_> for Default { /// Checks if the given expression is the `default` method belonging to the `Default` trait. fn is_expr_default<'tcx>(expr: &'tcx Expr<'tcx>, cx: &LateContext<'tcx>) -> bool { if_chain! { - if let ExprKind::Call(ref fn_expr, _) = &expr.kind; + if let ExprKind::Call(fn_expr, _) = &expr.kind; if let ExprKind::Path(qpath) = &fn_expr.kind; if let Res::Def(_, def_id) = cx.qpath_res(qpath, fn_expr.hir_id); then { @@ -245,11 +262,11 @@ fn is_expr_default<'tcx>(expr: &'tcx Expr<'tcx>, cx: &LateContext<'tcx>) -> bool fn field_reassigned_by_stmt<'tcx>(this: &Stmt<'tcx>, binding_name: Symbol) -> Option<(Ident, &'tcx Expr<'tcx>)> { if_chain! { // only take assignments - if let StmtKind::Semi(ref later_expr) = this.kind; - if let ExprKind::Assign(ref assign_lhs, ref assign_rhs, _) = later_expr.kind; + if let StmtKind::Semi(later_expr) = this.kind; + if let ExprKind::Assign(assign_lhs, assign_rhs, _) = later_expr.kind; // only take assignments to fields where the left-hand side field is a field of // the same binding as the previous statement - if let ExprKind::Field(ref binding, field_ident) = assign_lhs.kind; + if let ExprKind::Field(binding, field_ident) = assign_lhs.kind; if let ExprKind::Path(QPath::Resolved(_, path)) = binding.kind; if let Some(second_binding_name) = path.segments.last(); if second_binding_name.ident.name == binding_name; 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 6ace9aa6bdfc0..6e88394268042 100644 --- a/src/tools/clippy/clippy_lints/src/default_numeric_fallback.rs +++ b/src/tools/clippy/clippy_lints/src/default_numeric_fallback.rs @@ -1,3 +1,6 @@ +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::source::snippet; +use if_chain::if_chain; use rustc_ast::ast::{LitFloatType, LitIntType, LitKind}; use rustc_errors::Applicability; use rustc_hir::{ @@ -10,10 +13,7 @@ use rustc_middle::{ ty::{self, FloatTy, IntTy, PolyFnSig, Ty}, }; use rustc_session::{declare_lint_pass, declare_tool_lint}; - -use if_chain::if_chain; - -use crate::utils::{snippet, span_lint_and_sugg}; +use std::iter; declare_clippy_lint! { /// **What it does:** Checks for usage of unconstrained numeric literals which may cause default numeric fallback in type @@ -108,7 +108,7 @@ impl<'a, 'tcx> Visitor<'tcx> for NumericFallbackVisitor<'a, 'tcx> { match &expr.kind { ExprKind::Call(func, args) => { if let Some(fn_sig) = fn_sig_opt(self.cx, func.hir_id) { - for (expr, bound) in args.iter().zip(fn_sig.skip_binder().inputs().iter()) { + for (expr, bound) in iter::zip(*args, fn_sig.skip_binder().inputs()) { // Push found arg type, then visit arg. self.ty_bounds.push(TyBound::Ty(bound)); self.visit_expr(expr); @@ -121,7 +121,7 @@ impl<'a, 'tcx> Visitor<'tcx> for NumericFallbackVisitor<'a, 'tcx> { ExprKind::MethodCall(_, _, args, _) => { if let Some(def_id) = self.cx.typeck_results().type_dependent_def_id(expr.hir_id) { let fn_sig = self.cx.tcx.fn_sig(def_id).skip_binder(); - for (expr, bound) in args.iter().zip(fn_sig.inputs().iter()) { + for (expr, bound) in iter::zip(*args, fn_sig.inputs()) { self.ty_bounds.push(TyBound::Ty(bound)); self.visit_expr(expr); self.ty_bounds.pop(); @@ -130,10 +130,9 @@ impl<'a, 'tcx> Visitor<'tcx> for NumericFallbackVisitor<'a, 'tcx> { } }, - ExprKind::Struct(qpath, fields, base) => { + ExprKind::Struct(_, fields, base) => { + let ty = self.cx.typeck_results().expr_ty(expr); if_chain! { - if let Some(def_id) = self.cx.qpath_res(qpath, expr.hir_id).opt_def_id(); - let ty = self.cx.tcx.type_of(def_id); if let Some(adt_def) = ty.ty_adt_def(); if adt_def.is_struct(); if let Some(variant) = adt_def.variants.iter().next(); diff --git a/src/tools/clippy/clippy_lints/src/dereference.rs b/src/tools/clippy/clippy_lints/src/dereference.rs index b5fb51af1c7f3..1415f8e235a7e 100644 --- a/src/tools/clippy/clippy_lints/src/dereference.rs +++ b/src/tools/clippy/clippy_lints/src/dereference.rs @@ -1,11 +1,14 @@ -use crate::utils::{get_parent_expr, implements_trait, snippet, span_lint_and_sugg}; -use if_chain::if_chain; -use rustc_ast::util::parser::{ExprPrecedence, PREC_POSTFIX, PREC_PREFIX}; +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::source::snippet_with_context; +use clippy_utils::ty::peel_mid_ty_refs; +use clippy_utils::{get_parent_node, in_macro, is_allowed}; +use rustc_ast::util::parser::PREC_PREFIX; use rustc_errors::Applicability; -use rustc_hir::{Expr, ExprKind}; +use rustc_hir::{BorrowKind, Expr, ExprKind, HirId, MatchSource, Mutability, Node, UnOp}; use rustc_lint::{LateContext, LateLintPass}; -use rustc_session::{declare_lint_pass, declare_tool_lint}; -use rustc_span::source_map::Span; +use rustc_middle::ty::{self, Ty, TyCtxt, TyS, TypeckResults}; +use rustc_session::{declare_tool_lint, impl_lint_pass}; +use rustc_span::{symbol::sym, Span}; declare_clippy_lint! { /// **What it does:** Checks for explicit `deref()` or `deref_mut()` method calls. @@ -34,76 +37,265 @@ declare_clippy_lint! { "Explicit use of deref or deref_mut method while not in a method chain." } -declare_lint_pass!(Dereferencing => [ - EXPLICIT_DEREF_METHODS +impl_lint_pass!(Dereferencing => [ + EXPLICIT_DEREF_METHODS, ]); +#[derive(Default)] +pub struct Dereferencing { + state: Option<(State, StateData)>, + + // While parsing a `deref` method call in ufcs form, the path to the function is itself an + // expression. This is to store the id of that expression so it can be skipped when + // `check_expr` is called for it. + skip_expr: Option<HirId>, +} + +struct StateData { + /// Span of the top level expression + span: Span, + /// The required mutability + target_mut: Mutability, +} + +enum State { + // Any number of deref method calls. + DerefMethod { + // The number of calls in a sequence which changed the referenced type + ty_changed_count: usize, + is_final_ufcs: bool, + }, +} + +// A reference operation considered by this lint pass +enum RefOp { + Method(Mutability), + Deref, + AddrOf, +} + impl<'tcx> LateLintPass<'tcx> for Dereferencing { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - if_chain! { - if !expr.span.from_expansion(); - if let ExprKind::MethodCall(ref method_name, _, ref args, _) = &expr.kind; - if args.len() == 1; - - then { - if let Some(parent_expr) = get_parent_expr(cx, expr) { - // Check if we have the whole call chain here - if let ExprKind::MethodCall(..) = parent_expr.kind { - return; - } - // Check for Expr that we don't want to be linted - let precedence = parent_expr.precedence(); - match precedence { - // Lint a Call is ok though - ExprPrecedence::Call | ExprPrecedence::AddrOf => (), - _ => { - if precedence.order() >= PREC_PREFIX && precedence.order() <= PREC_POSTFIX { - return; - } - } + // Skip path expressions from deref calls. e.g. `Deref::deref(e)` + if Some(expr.hir_id) == self.skip_expr.take() { + return; + } + + // Stop processing sub expressions when a macro call is seen + if in_macro(expr.span) { + if let Some((state, data)) = self.state.take() { + report(cx, expr, state, data); + } + return; + } + + let typeck = cx.typeck_results(); + let (kind, sub_expr) = if let Some(x) = try_parse_ref_op(cx.tcx, typeck, expr) { + x + } else { + // The whole chain of reference operations has been seen + if let Some((state, data)) = self.state.take() { + report(cx, expr, state, data); + } + return; + }; + + match (self.state.take(), kind) { + (None, kind) => { + let parent = get_parent_node(cx.tcx, expr.hir_id); + let expr_ty = typeck.expr_ty(expr); + + match kind { + RefOp::Method(target_mut) + if !is_allowed(cx, EXPLICIT_DEREF_METHODS, expr.hir_id) + && is_linted_explicit_deref_position(parent, expr.hir_id, expr.span) => + { + self.state = Some(( + State::DerefMethod { + ty_changed_count: if deref_method_same_type(expr_ty, typeck.expr_ty(sub_expr)) { + 0 + } else { + 1 + }, + is_final_ufcs: matches!(expr.kind, ExprKind::Call(..)), + }, + StateData { + span: expr.span, + target_mut, + }, + )); } + _ => (), } - let name = method_name.ident.as_str(); - lint_deref(cx, &*name, &args[0], args[0].span, expr.span); - } + }, + (Some((State::DerefMethod { ty_changed_count, .. }, data)), RefOp::Method(_)) => { + self.state = Some(( + State::DerefMethod { + ty_changed_count: if deref_method_same_type(typeck.expr_ty(expr), typeck.expr_ty(sub_expr)) { + ty_changed_count + } else { + ty_changed_count + 1 + }, + is_final_ufcs: matches!(expr.kind, ExprKind::Call(..)), + }, + data, + )); + }, + + (Some((state, data)), _) => report(cx, expr, state, data), } } } -fn lint_deref(cx: &LateContext<'_>, method_name: &str, call_expr: &Expr<'_>, var_span: Span, expr_span: Span) { - match method_name { - "deref" => { - let impls_deref_trait = cx.tcx.lang_items().deref_trait().map_or(false, |id| { - implements_trait(cx, cx.typeck_results().expr_ty(&call_expr), id, &[]) - }); - if impls_deref_trait { - span_lint_and_sugg( - cx, - EXPLICIT_DEREF_METHODS, - expr_span, - "explicit deref method call", - "try this", - format!("&*{}", &snippet(cx, var_span, "..")), - Applicability::MachineApplicable, - ); - } +fn try_parse_ref_op( + tcx: TyCtxt<'tcx>, + typeck: &'tcx TypeckResults<'_>, + expr: &'tcx Expr<'_>, +) -> Option<(RefOp, &'tcx Expr<'tcx>)> { + let (def_id, arg) = match expr.kind { + ExprKind::MethodCall(_, _, [arg], _) => (typeck.type_dependent_def_id(expr.hir_id)?, arg), + ExprKind::Call( + Expr { + kind: ExprKind::Path(path), + hir_id, + .. + }, + [arg], + ) => (typeck.qpath_res(path, *hir_id).opt_def_id()?, arg), + ExprKind::Unary(UnOp::Deref, sub_expr) if !typeck.expr_ty(sub_expr).is_unsafe_ptr() => { + return Some((RefOp::Deref, sub_expr)); }, - "deref_mut" => { - let impls_deref_mut_trait = cx.tcx.lang_items().deref_mut_trait().map_or(false, |id| { - implements_trait(cx, cx.typeck_results().expr_ty(&call_expr), id, &[]) - }); - if impls_deref_mut_trait { - span_lint_and_sugg( - cx, - EXPLICIT_DEREF_METHODS, - expr_span, - "explicit deref_mut method call", - "try this", - format!("&mut *{}", &snippet(cx, var_span, "..")), - Applicability::MachineApplicable, - ); - } + ExprKind::AddrOf(BorrowKind::Ref, _, sub_expr) => return Some((RefOp::AddrOf, sub_expr)), + _ => return None, + }; + if tcx.is_diagnostic_item(sym::deref_method, def_id) { + Some((RefOp::Method(Mutability::Not), arg)) + } else if tcx.trait_of_item(def_id)? == tcx.lang_items().deref_mut_trait()? { + Some((RefOp::Method(Mutability::Mut), arg)) + } else { + None + } +} + +// 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 { + match (result_ty.kind(), arg_ty.kind()) { + (ty::Ref(_, result_ty, _), ty::Ref(_, arg_ty, _)) => TyS::same_type(result_ty, arg_ty), + + // The result type for a deref method is always a reference + // Not matching the previous pattern means the argument type is not a reference + // This means that the type did change + _ => false, + } +} + +// Checks whether the parent node is a suitable context for switching from a deref method to the +// deref operator. +fn is_linted_explicit_deref_position(parent: Option<Node<'_>>, child_id: HirId, child_span: Span) -> bool { + let parent = match parent { + Some(Node::Expr(e)) if e.span.ctxt() == child_span.ctxt() => e, + _ => return true, + }; + match parent.kind { + // Leave deref calls in the middle of a method chain. + // e.g. x.deref().foo() + ExprKind::MethodCall(_, _, [self_arg, ..], _) if self_arg.hir_id == child_id => false, + + // Leave deref calls resulting in a called function + // e.g. (x.deref())() + ExprKind::Call(func_expr, _) if func_expr.hir_id == child_id => false, + + // Makes an ugly suggestion + // e.g. *x.deref() => *&*x + ExprKind::Unary(UnOp::Deref, _) + // Postfix expressions would require parens + | ExprKind::Match(_, _, MatchSource::TryDesugar | MatchSource::AwaitDesugar) + | ExprKind::Field(..) + | ExprKind::Index(..) + | ExprKind::Err => false, + + ExprKind::Box(..) + | ExprKind::ConstBlock(..) + | ExprKind::Array(_) + | ExprKind::Call(..) + | ExprKind::MethodCall(..) + | ExprKind::Tup(..) + | ExprKind::Binary(..) + | ExprKind::Unary(..) + | ExprKind::Lit(..) + | ExprKind::Cast(..) + | ExprKind::Type(..) + | ExprKind::DropTemps(..) + | ExprKind::If(..) + | ExprKind::Loop(..) + | ExprKind::Match(..) + | ExprKind::Closure(..) + | ExprKind::Block(..) + | ExprKind::Assign(..) + | ExprKind::AssignOp(..) + | ExprKind::Path(..) + | ExprKind::AddrOf(..) + | ExprKind::Break(..) + | ExprKind::Continue(..) + | ExprKind::Ret(..) + | ExprKind::InlineAsm(..) + | ExprKind::LlvmInlineAsm(..) + | ExprKind::Struct(..) + | ExprKind::Repeat(..) + | ExprKind::Yield(..) => true, + } +} + +#[allow(clippy::needless_pass_by_value)] +fn report(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, state: State, data: StateData) { + match state { + State::DerefMethod { + ty_changed_count, + is_final_ufcs, + } => { + let mut app = Applicability::MachineApplicable; + let (expr_str, expr_is_macro_call) = snippet_with_context(cx, expr.span, data.span.ctxt(), "..", &mut app); + let ty = cx.typeck_results().expr_ty(expr); + let (_, ref_count) = peel_mid_ty_refs(ty); + let deref_str = if ty_changed_count >= ref_count && ref_count != 0 { + // a deref call changing &T -> &U requires two deref operators the first time + // this occurs. One to remove the reference, a second to call the deref impl. + "*".repeat(ty_changed_count + 1) + } else { + "*".repeat(ty_changed_count) + }; + let addr_of_str = if ty_changed_count < ref_count { + // Check if a reborrow from &mut T -> &T is required. + if data.target_mut == Mutability::Not && matches!(ty.kind(), ty::Ref(_, _, Mutability::Mut)) { + "&*" + } else { + "" + } + } else if data.target_mut == Mutability::Mut { + "&mut " + } else { + "&" + }; + + let expr_str = if !expr_is_macro_call && is_final_ufcs && expr.precedence().order() < PREC_PREFIX { + format!("({})", expr_str) + } else { + expr_str.into_owned() + }; + + span_lint_and_sugg( + cx, + EXPLICIT_DEREF_METHODS, + data.span, + match data.target_mut { + Mutability::Not => "explicit `deref` method call", + Mutability::Mut => "explicit `deref_mut` method call", + }, + "try this", + format!("{}{}{}", addr_of_str, deref_str, expr_str), + app, + ); }, - _ => (), } } diff --git a/src/tools/clippy/clippy_lints/src/derive.rs b/src/tools/clippy/clippy_lints/src/derive.rs index e8510bde9adcd..647af3bdc04d0 100644 --- a/src/tools/clippy/clippy_lints/src/derive.rs +++ b/src/tools/clippy/clippy_lints/src/derive.rs @@ -1,8 +1,7 @@ -use crate::utils::paths; -use crate::utils::{ - get_trait_def_id, is_allowed, is_automatically_derived, is_copy, match_def_path, match_path, span_lint_and_help, - span_lint_and_note, span_lint_and_then, -}; +use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_note, span_lint_and_then}; +use clippy_utils::paths; +use clippy_utils::ty::{implements_trait, is_copy}; +use clippy_utils::{get_trait_def_id, is_allowed, is_automatically_derived, match_def_path}; use if_chain::if_chain; use rustc_hir::def_id::DefId; use rustc_hir::intravisit::{walk_expr, walk_fn, walk_item, FnKind, NestedVisitorMap, Visitor}; @@ -13,7 +12,7 @@ use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::hir::map::Map; use rustc_middle::ty::{self, Ty}; use rustc_session::{declare_lint_pass, declare_tool_lint}; -use rustc_span::source_map::Span; +use rustc_span::{def_id::LOCAL_CRATE, source_map::Span}; declare_clippy_lint! { /// **What it does:** Checks for deriving `Hash` but implementing `PartialEq` @@ -170,7 +169,8 @@ impl<'tcx> LateLintPass<'tcx> for Derive { }) = item.kind { let ty = cx.tcx.type_of(item.def_id); - let is_automatically_derived = is_automatically_derived(&*item.attrs); + let attrs = cx.tcx.hir().attrs(item.hir_id()); + let is_automatically_derived = is_automatically_derived(attrs); check_hash_peq(cx, item.span, trait_ref, ty, is_automatically_derived); check_ord_partial_ord(cx, item.span, trait_ref, ty, is_automatically_derived); @@ -199,7 +199,7 @@ fn check_hash_peq<'tcx>( then { // Look for the PartialEq implementations for `ty` cx.tcx.for_each_relevant_impl(peq_trait_def_id, ty, |impl_id| { - let peq_is_automatically_derived = is_automatically_derived(&cx.tcx.get_attrs(impl_id)); + let peq_is_automatically_derived = is_automatically_derived(cx.tcx.get_attrs(impl_id)); if peq_is_automatically_derived == hash_is_automatically_derived { return; @@ -253,7 +253,7 @@ fn check_ord_partial_ord<'tcx>( then { // Look for the PartialOrd implementations for `ty` cx.tcx.for_each_relevant_impl(partial_ord_trait_def_id, ty, |impl_id| { - let partial_ord_is_automatically_derived = is_automatically_derived(&cx.tcx.get_attrs(impl_id)); + let partial_ord_is_automatically_derived = is_automatically_derived(cx.tcx.get_attrs(impl_id)); if partial_ord_is_automatically_derived == ord_is_automatically_derived { return; @@ -293,43 +293,53 @@ fn check_ord_partial_ord<'tcx>( /// Implementation of the `EXPL_IMPL_CLONE_ON_COPY` lint. fn check_copy_clone<'tcx>(cx: &LateContext<'tcx>, item: &Item<'_>, trait_ref: &TraitRef<'_>, ty: Ty<'tcx>) { - if match_path(&trait_ref.path, &paths::CLONE_TRAIT) { - if !is_copy(cx, ty) { + let clone_id = match cx.tcx.lang_items().clone_trait() { + Some(id) if trait_ref.trait_def_id() == Some(id) => id, + _ => return, + }; + let copy_id = match cx.tcx.lang_items().copy_trait() { + Some(id) => id, + None => return, + }; + let (ty_adt, ty_subs) = match *ty.kind() { + // Unions can't derive clone. + ty::Adt(adt, subs) if !adt.is_union() => (adt, subs), + _ => return, + }; + // If the current self type doesn't implement Copy (due to generic constraints), search to see if + // there's a Copy impl for any instance of the adt. + if !is_copy(cx, ty) { + if ty_subs.non_erasable_generics().next().is_some() { + let has_copy_impl = cx + .tcx + .all_local_trait_impls(LOCAL_CRATE) + .get(©_id) + .map_or(false, |impls| { + impls + .iter() + .any(|&id| matches!(cx.tcx.type_of(id).kind(), ty::Adt(adt, _) if ty_adt.did == adt.did)) + }); + if !has_copy_impl { + return; + } + } else { return; } - - match *ty.kind() { - ty::Adt(def, _) if def.is_union() => return, - - // Some types are not Clone by default but could be cloned “by hand” if necessary - ty::Adt(def, substs) => { - for variant in &def.variants { - for field in &variant.fields { - if let ty::FnDef(..) = field.ty(cx.tcx, substs).kind() { - return; - } - } - for subst in substs { - if let ty::subst::GenericArgKind::Type(subst) = subst.unpack() { - if let ty::Param(_) = subst.kind() { - return; - } - } - } - } - }, - _ => (), - } - - span_lint_and_note( - cx, - EXPL_IMPL_CLONE_ON_COPY, - item.span, - "you are implementing `Clone` explicitly on a `Copy` type", - Some(item.span), - "consider deriving `Clone` or removing `Copy`", - ); } + // Derive constrains all generic types to requiring Clone. Check if any type is not constrained for + // this impl. + if ty_subs.types().any(|ty| !implements_trait(cx, ty, clone_id, &[])) { + return; + } + + span_lint_and_note( + cx, + EXPL_IMPL_CLONE_ON_COPY, + item.span, + "you are implementing `Clone` explicitly on a `Copy` type", + Some(item.span), + "consider deriving `Clone` or removing `Copy`", + ); } /// Implementation of the `UNSAFE_DERIVE_DESERIALIZE` lint. diff --git a/src/tools/clippy/clippy_lints/src/disallowed_method.rs b/src/tools/clippy/clippy_lints/src/disallowed_method.rs index 56dc6d18a58f2..ded0a0fff54b2 100644 --- a/src/tools/clippy/clippy_lints/src/disallowed_method.rs +++ b/src/tools/clippy/clippy_lints/src/disallowed_method.rs @@ -1,4 +1,5 @@ -use crate::utils::{fn_def_id, span_lint}; +use clippy_utils::diagnostics::span_lint; +use clippy_utils::fn_def_id; use rustc_data_structures::fx::FxHashSet; use rustc_hir::Expr; diff --git a/src/tools/clippy/clippy_lints/src/doc.rs b/src/tools/clippy/clippy_lints/src/doc.rs index 39a202f281cb7..fb53b55ebd6ac 100644 --- a/src/tools/clippy/clippy_lints/src/doc.rs +++ b/src/tools/clippy/clippy_lints/src/doc.rs @@ -1,7 +1,6 @@ -use crate::utils::{ - implements_trait, is_entrypoint_fn, is_expn_of, is_type_diagnostic_item, match_panic_def_id, method_chain_args, - return_ty, span_lint, span_lint_and_note, -}; +use clippy_utils::diagnostics::{span_lint, span_lint_and_note}; +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 if_chain::if_chain; use itertools::Itertools; use rustc_ast::ast::{Async, AttrKind, Attribute, FnKind, FnRetTy, ItemKind}; @@ -12,7 +11,7 @@ use rustc_errors::emitter::EmitterWriter; use rustc_errors::Handler; use rustc_hir as hir; use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor}; -use rustc_hir::{Expr, ExprKind, QPath}; +use rustc_hir::{AnonConst, Expr, ExprKind, QPath}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::hir::map::Map; use rustc_middle::lint::in_external_macro; @@ -208,12 +207,14 @@ impl_lint_pass!(DocMarkdown => ); impl<'tcx> LateLintPass<'tcx> for DocMarkdown { - fn check_crate(&mut self, cx: &LateContext<'tcx>, krate: &'tcx hir::Crate<'_>) { - check_attrs(cx, &self.valid_idents, &krate.item.attrs); + fn check_crate(&mut self, cx: &LateContext<'tcx>, _: &'tcx hir::Crate<'_>) { + let attrs = cx.tcx.hir().attrs(hir::CRATE_HIR_ID); + check_attrs(cx, &self.valid_idents, attrs); } fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) { - let headers = check_attrs(cx, &self.valid_idents, &item.attrs); + let attrs = cx.tcx.hir().attrs(item.hir_id()); + let headers = check_attrs(cx, &self.valid_idents, attrs); match item.kind { hir::ItemKind::Fn(ref sig, _, body_id) => { if !(is_entrypoint_fn(cx, item.def_id.to_def_id()) || in_external_macro(cx.tcx.sess, item.span)) { @@ -249,7 +250,8 @@ impl<'tcx> LateLintPass<'tcx> for DocMarkdown { } fn check_trait_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::TraitItem<'_>) { - let headers = check_attrs(cx, &self.valid_idents, &item.attrs); + let attrs = cx.tcx.hir().attrs(item.hir_id()); + let headers = check_attrs(cx, &self.valid_idents, attrs); if let hir::TraitItemKind::Fn(ref sig, ..) = item.kind { if !in_external_macro(cx.tcx.sess, item.span) { lint_for_missing_headers(cx, item.hir_id(), item.span, sig, headers, None, None); @@ -258,7 +260,8 @@ impl<'tcx> LateLintPass<'tcx> for DocMarkdown { } fn check_impl_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::ImplItem<'_>) { - let headers = check_attrs(cx, &self.valid_idents, &item.attrs); + let attrs = cx.tcx.hir().attrs(item.hir_id()); + let headers = check_attrs(cx, &self.valid_idents, attrs); if self.in_trait_impl || in_external_macro(cx.tcx.sess, item.span) { return; } @@ -325,9 +328,9 @@ fn lint_for_missing_headers<'tcx>( if_chain! { if let Some(body_id) = body_id; if let Some(future) = cx.tcx.lang_items().future_trait(); - let def_id = cx.tcx.hir().body_owner_def_id(body_id); - let mir = cx.tcx.optimized_mir(def_id.to_def_id()); - let ret_ty = mir.return_ty(); + let typeck = cx.tcx.typeck_body(body_id); + let body = cx.tcx.hir().body(body_id); + let ret_ty = typeck.expr_ty(&body.value); if implements_trait(cx, ret_ty, future, &[]); if let ty::Opaque(_, subs) = ret_ty.kind(); if let Some(gen) = subs.types().next(); @@ -580,7 +583,7 @@ fn check_code(cx: &LateContext<'_>, text: &str, edition: Edition, span: Span) { let returns_nothing = match &sig.decl.output { FnRetTy::Default(..) => true, FnRetTy::Ty(ty) if ty.kind.is_unit() => true, - _ => false, + FnRetTy::Ty(_) => false, }; if returns_nothing && !is_async && !block.stmts.is_empty() { @@ -707,16 +710,22 @@ impl<'a, 'tcx> Visitor<'tcx> for FindPanicUnwrap<'a, 'tcx> { // check for `begin_panic` if_chain! { - if let ExprKind::Call(ref func_expr, _) = expr.kind; - if let ExprKind::Path(QPath::Resolved(_, ref path)) = func_expr.kind; + 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); } } + // 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 reciever_ty = self.typeck_results.expr_ty(&arglists[0][0]).peel_refs(); @@ -731,7 +740,15 @@ impl<'a, 'tcx> Visitor<'tcx> for FindPanicUnwrap<'a, 'tcx> { intravisit::walk_expr(self, expr); } + // Panics in const blocks will cause compilation to fail. + fn visit_anon_const(&mut self, _: &'tcx AnonConst) {} + fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> { 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/double_comparison.rs b/src/tools/clippy/clippy_lints/src/double_comparison.rs index 19f56195ec1b4..58543ae6e4e31 100644 --- a/src/tools/clippy/clippy_lints/src/double_comparison.rs +++ b/src/tools/clippy/clippy_lints/src/double_comparison.rs @@ -1,13 +1,14 @@ //! Lint on unnecessary double comparisons. Some examples: +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::eq_expr_value; +use clippy_utils::source::snippet_with_applicability; use rustc_errors::Applicability; use rustc_hir::{BinOpKind, Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::source_map::Span; -use crate::utils::{eq_expr_value, snippet_with_applicability, span_lint_and_sugg}; - declare_clippy_lint! { /// **What it does:** Checks for double comparisons that could be simplified to a single expression. /// @@ -46,7 +47,7 @@ impl<'tcx> DoubleComparisons { }, _ => return, }; - if !(eq_expr_value(cx, &llhs, &rlhs) && eq_expr_value(cx, &lrhs, &rrhs)) { + if !(eq_expr_value(cx, llhs, rlhs) && eq_expr_value(cx, lrhs, rrhs)) { return; } macro_rules! lint_double_comparison { @@ -87,7 +88,7 @@ impl<'tcx> DoubleComparisons { impl<'tcx> LateLintPass<'tcx> for DoubleComparisons { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - if let ExprKind::Binary(ref kind, ref lhs, ref rhs) = expr.kind { + if let ExprKind::Binary(ref kind, lhs, rhs) = expr.kind { Self::check_binop(cx, kind.node, lhs, rhs, expr.span); } } diff --git a/src/tools/clippy/clippy_lints/src/double_parens.rs b/src/tools/clippy/clippy_lints/src/double_parens.rs index abbcaf43f4151..e4e4a93b011fd 100644 --- a/src/tools/clippy/clippy_lints/src/double_parens.rs +++ b/src/tools/clippy/clippy_lints/src/double_parens.rs @@ -1,4 +1,4 @@ -use crate::utils::span_lint; +use clippy_utils::diagnostics::span_lint; use rustc_ast::ast::{Expr, ExprKind}; use rustc_lint::{EarlyContext, EarlyLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; @@ -50,7 +50,7 @@ impl EarlyLintPass for DoubleParens { match expr.kind { ExprKind::Paren(ref in_paren) => match in_paren.kind { ExprKind::Paren(_) | ExprKind::Tup(_) => { - span_lint(cx, DOUBLE_PARENS, expr.span, &msg); + span_lint(cx, DOUBLE_PARENS, expr.span, msg); }, _ => {}, }, @@ -58,7 +58,7 @@ impl EarlyLintPass for DoubleParens { if params.len() == 1 { let param = ¶ms[0]; if let ExprKind::Paren(_) = param.kind { - span_lint(cx, DOUBLE_PARENS, param.span, &msg); + span_lint(cx, DOUBLE_PARENS, param.span, msg); } } }, @@ -66,7 +66,7 @@ impl EarlyLintPass for DoubleParens { if params.len() == 2 { let param = ¶ms[1]; if let ExprKind::Paren(_) = param.kind { - span_lint(cx, DOUBLE_PARENS, param.span, &msg); + span_lint(cx, DOUBLE_PARENS, param.span, msg); } } }, diff --git a/src/tools/clippy/clippy_lints/src/drop_forget_ref.rs b/src/tools/clippy/clippy_lints/src/drop_forget_ref.rs index a84f9c4628716..b5b29760636ba 100644 --- a/src/tools/clippy/clippy_lints/src/drop_forget_ref.rs +++ b/src/tools/clippy/clippy_lints/src/drop_forget_ref.rs @@ -1,4 +1,6 @@ -use crate::utils::{is_copy, match_def_path, paths, span_lint_and_note}; +use clippy_utils::diagnostics::span_lint_and_note; +use clippy_utils::ty::is_copy; +use clippy_utils::{match_def_path, paths}; use if_chain::if_chain; use rustc_hir::{Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; @@ -98,20 +100,20 @@ declare_clippy_lint! { } const DROP_REF_SUMMARY: &str = "calls to `std::mem::drop` with a reference instead of an owned value. \ - Dropping a reference does nothing."; + Dropping a reference does nothing"; const FORGET_REF_SUMMARY: &str = "calls to `std::mem::forget` with a reference instead of an owned value. \ - Forgetting a reference does nothing."; + Forgetting a reference does nothing"; const DROP_COPY_SUMMARY: &str = "calls to `std::mem::drop` with a value that implements `Copy`. \ - Dropping a copy leaves the original intact."; + Dropping a copy leaves the original intact"; const FORGET_COPY_SUMMARY: &str = "calls to `std::mem::forget` with a value that implements `Copy`. \ - Forgetting a copy leaves the original intact."; + Forgetting a copy leaves the original intact"; declare_lint_pass!(DropForgetRef => [DROP_REF, FORGET_REF, DROP_COPY, FORGET_COPY]); impl<'tcx> LateLintPass<'tcx> for DropForgetRef { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { if_chain! { - if let ExprKind::Call(ref path, ref args) = expr.kind; + if let ExprKind::Call(path, args) = expr.kind; if let ExprKind::Path(ref qpath) = path.kind; if args.len() == 1; if let Some(def_id) = cx.qpath_res(qpath, path.hir_id).opt_def_id(); diff --git a/src/tools/clippy/clippy_lints/src/duration_subsec.rs b/src/tools/clippy/clippy_lints/src/duration_subsec.rs index c0529a34cc411..529807770f3cd 100644 --- a/src/tools/clippy/clippy_lints/src/duration_subsec.rs +++ b/src/tools/clippy/clippy_lints/src/duration_subsec.rs @@ -1,3 +1,5 @@ +use clippy_utils::source::snippet_with_applicability; +use clippy_utils::ty::match_type; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::{BinOpKind, Expr, ExprKind}; @@ -6,8 +8,8 @@ use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::source_map::Spanned; use crate::consts::{constant, Constant}; -use crate::utils::paths; -use crate::utils::{match_type, snippet_with_applicability, span_lint_and_sugg}; +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::paths; declare_clippy_lint! { /// **What it does:** Checks for calculation of subsecond microseconds or milliseconds @@ -41,8 +43,8 @@ declare_lint_pass!(DurationSubsec => [DURATION_SUBSEC]); impl<'tcx> LateLintPass<'tcx> for DurationSubsec { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { if_chain! { - if let ExprKind::Binary(Spanned { node: BinOpKind::Div, .. }, ref left, ref right) = expr.kind; - if let ExprKind::MethodCall(ref method_path, _ , ref args, _) = left.kind; + if let ExprKind::Binary(Spanned { node: BinOpKind::Div, .. }, left, right) = expr.kind; + if let ExprKind::MethodCall(method_path, _ , args, _) = left.kind; if match_type(cx, cx.typeck_results().expr_ty(&args[0]).peel_refs(), &paths::DURATION); if let Some((Constant::Int(divisor), _)) = constant(cx, cx.typeck_results(), right); then { diff --git a/src/tools/clippy/clippy_lints/src/else_if_without_else.rs b/src/tools/clippy/clippy_lints/src/else_if_without_else.rs index 95123e6ff6fe2..26984df953977 100644 --- a/src/tools/clippy/clippy_lints/src/else_if_without_else.rs +++ b/src/tools/clippy/clippy_lints/src/else_if_without_else.rs @@ -1,12 +1,11 @@ //! Lint on if expressions with an else if, but without a final else branch. +use clippy_utils::diagnostics::span_lint_and_help; use rustc_ast::ast::{Expr, ExprKind}; use rustc_lint::{EarlyContext, EarlyLintPass, LintContext}; use rustc_middle::lint::in_external_macro; use rustc_session::{declare_lint_pass, declare_tool_lint}; -use crate::utils::span_lint_and_help; - declare_clippy_lint! { /// **What it does:** Checks for usage of if expressions with an `else if` branch, /// but without a final `else` branch. diff --git a/src/tools/clippy/clippy_lints/src/empty_enum.rs b/src/tools/clippy/clippy_lints/src/empty_enum.rs index 077c3b75fb8c8..c92984a98346d 100644 --- a/src/tools/clippy/clippy_lints/src/empty_enum.rs +++ b/src/tools/clippy/clippy_lints/src/empty_enum.rs @@ -1,6 +1,6 @@ //! lint when there is an enum with no variants -use crate::utils::span_lint_and_help; +use clippy_utils::diagnostics::span_lint_and_help; use rustc_hir::{Item, ItemKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; diff --git a/src/tools/clippy/clippy_lints/src/entry.rs b/src/tools/clippy/clippy_lints/src/entry.rs index 55575969927ba..a815df1691a1c 100644 --- a/src/tools/clippy/clippy_lints/src/entry.rs +++ b/src/tools/clippy/clippy_lints/src/entry.rs @@ -1,6 +1,8 @@ -use crate::utils::SpanlessEq; -use crate::utils::{get_item_name, is_type_diagnostic_item, match_type, paths, snippet, snippet_opt}; -use crate::utils::{snippet_with_applicability, span_lint_and_then}; +use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::source::{snippet, snippet_opt, snippet_with_applicability}; +use clippy_utils::ty::{is_type_diagnostic_item, match_type}; +use clippy_utils::SpanlessEq; +use clippy_utils::{get_item_name, paths}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::intravisit::{walk_expr, NestedVisitorMap, Visitor}; @@ -55,14 +57,14 @@ declare_lint_pass!(HashMapPass => [MAP_ENTRY]); impl<'tcx> LateLintPass<'tcx> for HashMapPass { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - if let ExprKind::If(ref check, ref then_block, ref else_block) = expr.kind { - if let ExprKind::Unary(UnOp::Not, ref check) = check.kind { + if let ExprKind::If(check, then_block, ref else_block) = expr.kind { + if let ExprKind::Unary(UnOp::Not, check) = check.kind { if let Some((ty, map, key)) = check_cond(cx, check) { // in case of `if !m.contains_key(&k) { m.insert(k, v); }` // we can give a better error message let sole_expr = { else_block.is_none() - && if let ExprKind::Block(ref then_block, _) = then_block.kind { + && if let ExprKind::Block(then_block, _) = then_block.kind { (then_block.expr.is_some() as usize) + then_block.stmts.len() == 1 } else { true @@ -79,9 +81,9 @@ impl<'tcx> LateLintPass<'tcx> for HashMapPass { sole_expr, }; - walk_expr(&mut visitor, &**then_block); + walk_expr(&mut visitor, then_block); } - } else if let Some(ref else_block) = *else_block { + } else if let Some(else_block) = *else_block { if let Some((ty, map, key)) = check_cond(cx, check) { let mut visitor = InsertVisitor { cx, @@ -101,10 +103,10 @@ impl<'tcx> LateLintPass<'tcx> for HashMapPass { fn check_cond<'a>(cx: &LateContext<'_>, check: &'a Expr<'a>) -> Option<(&'static str, &'a Expr<'a>, &'a Expr<'a>)> { if_chain! { - if let ExprKind::MethodCall(ref path, _, ref params, _) = check.kind; + if let ExprKind::MethodCall(path, _, params, _) = check.kind; if params.len() >= 2; if path.ident.name == sym!(contains_key); - if let ExprKind::AddrOf(BorrowKind::Ref, _, ref key) = params[1].kind; + if let ExprKind::AddrOf(BorrowKind::Ref, _, key) = params[1].kind; then { let map = ¶ms[0]; let obj_ty = cx.typeck_results().expr_ty(map).peel_refs(); @@ -138,7 +140,7 @@ impl<'a, 'tcx, 'b> Visitor<'tcx> for InsertVisitor<'a, 'tcx, 'b> { fn visit_expr(&mut self, expr: &'tcx Expr<'_>) { if_chain! { - if let ExprKind::MethodCall(ref path, _, ref params, _) = expr.kind; + if let ExprKind::MethodCall(path, _, params, _) = expr.kind; if params.len() == 3; if path.ident.name == sym!(insert); if get_item_name(self.cx, self.map) == get_item_name(self.cx, ¶ms[0]); diff --git a/src/tools/clippy/clippy_lints/src/enum_clike.rs b/src/tools/clippy/clippy_lints/src/enum_clike.rs index aa235642ac310..7a98ae39d3ae9 100644 --- a/src/tools/clippy/clippy_lints/src/enum_clike.rs +++ b/src/tools/clippy/clippy_lints/src/enum_clike.rs @@ -2,7 +2,7 @@ //! don't fit into an `i32` use crate::consts::{miri_to_const, Constant}; -use crate::utils::span_lint; +use clippy_utils::diagnostics::span_lint; use rustc_hir::{Item, ItemKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::util::IntTypeExt; diff --git a/src/tools/clippy/clippy_lints/src/enum_variants.rs b/src/tools/clippy/clippy_lints/src/enum_variants.rs index 67a463538568e..0ecc0bc3eb60a 100644 --- a/src/tools/clippy/clippy_lints/src/enum_variants.rs +++ b/src/tools/clippy/clippy_lints/src/enum_variants.rs @@ -1,7 +1,8 @@ //! lint on enum variants that are prefixed or suffixed by the same characters -use crate::utils::{camel_case, is_present_in_source}; -use crate::utils::{span_lint, span_lint_and_help}; +use clippy_utils::camel_case; +use clippy_utils::diagnostics::{span_lint, span_lint_and_help}; +use clippy_utils::source::is_present_in_source; use rustc_ast::ast::{EnumDef, Item, ItemKind, VisibilityKind}; use rustc_lint::{EarlyContext, EarlyLintPass, Lint}; use rustc_session::{declare_tool_lint, impl_lint_pass}; diff --git a/src/tools/clippy/clippy_lints/src/eq_op.rs b/src/tools/clippy/clippy_lints/src/eq_op.rs index 6308f6e2e7e9d..90f391b5f5c89 100644 --- a/src/tools/clippy/clippy_lints/src/eq_op.rs +++ b/src/tools/clippy/clippy_lints/src/eq_op.rs @@ -1,7 +1,7 @@ -use crate::utils::{ - ast_utils::is_useless_with_eq_exprs, eq_expr_value, higher, implements_trait, in_macro, is_copy, is_expn_of, - multispan_sugg, snippet, span_lint, span_lint_and_then, -}; +use clippy_utils::diagnostics::{multispan_sugg, span_lint, span_lint_and_then}; +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, in_macro, is_expn_of}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::{BinOpKind, BorrowKind, Expr, ExprKind, StmtKind}; @@ -65,12 +65,12 @@ const ASSERT_MACRO_NAMES: [&str; 4] = ["assert_eq", "assert_ne", "debug_assert_e 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(ref block, _) = e.kind { + 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(ref matchexpr) = stmt.kind; + 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]); @@ -88,12 +88,12 @@ impl<'tcx> LateLintPass<'tcx> for EqOp { } } } - if let ExprKind::Binary(op, ref left, ref right) = e.kind { + if let ExprKind::Binary(op, left, right) = e.kind { if e.span.from_expansion() { return; } let macro_with_not_op = |expr_kind: &ExprKind<'_>| { - if let ExprKind::Unary(_, ref expr) = *expr_kind { + if let ExprKind::Unary(_, expr) = *expr_kind { in_macro(expr.span) } else { false @@ -135,7 +135,7 @@ impl<'tcx> LateLintPass<'tcx> for EqOp { // do not suggest to dereference literals (&ExprKind::Lit(..), _) | (_, &ExprKind::Lit(..)) => {}, // &foo == &bar - (&ExprKind::AddrOf(BorrowKind::Ref, _, ref l), &ExprKind::AddrOf(BorrowKind::Ref, _, ref r)) => { + (&ExprKind::AddrOf(BorrowKind::Ref, _, l), &ExprKind::AddrOf(BorrowKind::Ref, _, r)) => { let lty = cx.typeck_results().expr_ty(l); let rty = cx.typeck_results().expr_ty(r); let lcpy = is_copy(cx, lty); @@ -198,7 +198,7 @@ impl<'tcx> LateLintPass<'tcx> for EqOp { } }, // &foo == bar - (&ExprKind::AddrOf(BorrowKind::Ref, _, ref l), _) => { + (&ExprKind::AddrOf(BorrowKind::Ref, _, l), _) => { let lty = cx.typeck_results().expr_ty(l); let lcpy = is_copy(cx, lty); if (requires_ref || lcpy) @@ -222,7 +222,7 @@ impl<'tcx> LateLintPass<'tcx> for EqOp { } }, // foo == &bar - (_, &ExprKind::AddrOf(BorrowKind::Ref, _, ref r)) => { + (_, &ExprKind::AddrOf(BorrowKind::Ref, _, r)) => { let rty = cx.typeck_results().expr_ty(r); let rcpy = is_copy(cx, rty); if (requires_ref || rcpy) diff --git a/src/tools/clippy/clippy_lints/src/erasing_op.rs b/src/tools/clippy/clippy_lints/src/erasing_op.rs index dbd1ff514f0e1..f95ca86a2d015 100644 --- a/src/tools/clippy/clippy_lints/src/erasing_op.rs +++ b/src/tools/clippy/clippy_lints/src/erasing_op.rs @@ -1,10 +1,10 @@ +use clippy_utils::diagnostics::span_lint; use rustc_hir::{BinOpKind, Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::source_map::Span; use crate::consts::{constant_simple, Constant}; -use crate::utils::span_lint; declare_clippy_lint! { /// **What it does:** Checks for erasing operations, e.g., `x * 0`. @@ -34,7 +34,7 @@ impl<'tcx> LateLintPass<'tcx> for ErasingOp { if e.span.from_expansion() { return; } - if let ExprKind::Binary(ref cmp, ref left, ref right) = e.kind { + if let ExprKind::Binary(ref cmp, left, right) = e.kind { match cmp.node { BinOpKind::Mul | BinOpKind::BitAnd => { check(cx, left, e.span); diff --git a/src/tools/clippy/clippy_lints/src/escape.rs b/src/tools/clippy/clippy_lints/src/escape.rs index f8ef2a464d5c3..3581ab41906f4 100644 --- a/src/tools/clippy/clippy_lints/src/escape.rs +++ b/src/tools/clippy/clippy_lints/src/escape.rs @@ -1,7 +1,10 @@ +use clippy_utils::diagnostics::span_lint; +use clippy_utils::ty::contains_ty; use rustc_hir::intravisit; use rustc_hir::{self, AssocItemKind, Body, FnDecl, HirId, HirIdSet, Impl, ItemKind, Node}; use rustc_infer::infer::TyCtxtInferExt; use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::mir::FakeReadCause; use rustc_middle::ty::{self, TraitRef, Ty}; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::source_map::Span; @@ -10,8 +13,6 @@ use rustc_target::abi::LayoutOf; use rustc_target::spec::abi::Abi; use rustc_typeck::expr_use_visitor::{ConsumeMode, Delegate, ExprUseVisitor, PlaceBase, PlaceWithHirId}; -use crate::utils::{contains_ty, span_lint}; - #[derive(Copy, Clone)] pub struct BoxedLocal { pub too_large_for_stack: u64, @@ -184,6 +185,8 @@ impl<'a, 'tcx> Delegate<'tcx> for EscapeDelegate<'a, 'tcx> { } } } + + fn fake_read(&mut self, _: rustc_typeck::expr_use_visitor::Place<'tcx>, _: FakeReadCause, _: HirId) {} } impl<'a, 'tcx> EscapeDelegate<'a, 'tcx> { diff --git a/src/tools/clippy/clippy_lints/src/eta_reduction.rs b/src/tools/clippy/clippy_lints/src/eta_reduction.rs index 1a722d39f730b..2f1aa53236d33 100644 --- a/src/tools/clippy/clippy_lints/src/eta_reduction.rs +++ b/src/tools/clippy/clippy_lints/src/eta_reduction.rs @@ -1,3 +1,9 @@ +use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then}; +use clippy_utils::higher; +use clippy_utils::higher::VecArgs; +use clippy_utils::source::snippet_opt; +use clippy_utils::ty::{implements_trait, type_is_unsafe_function}; +use clippy_utils::{is_adjusted, iter_input_pats}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::{def_id, Expr, ExprKind, Param, PatKind, QPath}; @@ -6,11 +12,6 @@ use rustc_middle::lint::in_external_macro; use rustc_middle::ty::{self, Ty}; use rustc_session::{declare_lint_pass, declare_tool_lint}; -use crate::utils::{ - implements_trait, is_adjusted, iter_input_pats, snippet_opt, span_lint_and_sugg, span_lint_and_then, - type_is_unsafe_function, -}; - declare_clippy_lint! { /// **What it does:** Checks for closures which just call another function where /// the function can be called directly. `unsafe` functions or calls where types @@ -74,7 +75,10 @@ impl<'tcx> LateLintPass<'tcx> for EtaReduction { match expr.kind { ExprKind::Call(_, args) | ExprKind::MethodCall(_, _, args, _) => { for arg in args { - check_closure(cx, arg) + // skip `foo(macro!())` + if arg.span.ctxt() == expr.span.ctxt() { + check_closure(cx, arg) + } } }, _ => (), @@ -83,12 +87,29 @@ impl<'tcx> LateLintPass<'tcx> for EtaReduction { } fn check_closure(cx: &LateContext<'_>, expr: &Expr<'_>) { - if let ExprKind::Closure(_, ref decl, eid, _, _) = expr.kind { + if let ExprKind::Closure(_, decl, eid, _, _) = expr.kind { let body = cx.tcx.hir().body(eid); let ex = &body.value; + if ex.span.ctxt() != expr.span.ctxt() { + if let Some(VecArgs::Vec(&[])) = higher::vec_macro(cx, ex) { + // replace `|| vec![]` with `Vec::new` + span_lint_and_sugg( + cx, + REDUNDANT_CLOSURE, + expr.span, + "redundant closure", + "replace the closure with `Vec::new`", + "std::vec::Vec::new".into(), + Applicability::MachineApplicable, + ); + } + // skip `foo(|| macro!())` + return; + } + if_chain!( - if let ExprKind::Call(ref caller, ref args) = ex.kind; + if let ExprKind::Call(caller, args) = ex.kind; if let ExprKind::Path(_) = caller.kind; @@ -107,11 +128,11 @@ fn check_closure(cx: &LateContext<'_>, expr: &Expr<'_>) { if compare_inputs(&mut iter_input_pats(decl, body), &mut args.iter()); then { - span_lint_and_then(cx, REDUNDANT_CLOSURE, expr.span, "redundant closure found", |diag| { + span_lint_and_then(cx, REDUNDANT_CLOSURE, expr.span, "redundant closure", |diag| { if let Some(snippet) = snippet_opt(cx, caller.span) { diag.span_suggestion( expr.span, - "remove closure as shown", + "replace the closure with the function itself", snippet, Applicability::MachineApplicable, ); @@ -121,7 +142,7 @@ fn check_closure(cx: &LateContext<'_>, expr: &Expr<'_>) { ); if_chain!( - if let ExprKind::MethodCall(ref path, _, ref args, _) = ex.kind; + if let ExprKind::MethodCall(path, _, args, _) = ex.kind; // Not the same number of arguments, there is no way the closure is the same as the function return; if args.len() == decl.inputs.len(); @@ -141,8 +162,8 @@ fn check_closure(cx: &LateContext<'_>, expr: &Expr<'_>) { cx, REDUNDANT_CLOSURE_FOR_METHOD_CALLS, expr.span, - "redundant closure found", - "remove closure as shown", + "redundant closure", + "replace the closure with the method itself", format!("{}::{}", name, path.ident.name), Applicability::MachineApplicable, ); @@ -157,7 +178,7 @@ fn get_ufcs_type_name(cx: &LateContext<'_>, method_def_id: def_id::DefId, self_a let actual_type_of_self = &cx.typeck_results().node_type(self_arg.hir_id); if let Some(trait_id) = cx.tcx.trait_of_item(method_def_id) { - if match_borrow_depth(expected_type_of_self, &actual_type_of_self) + if match_borrow_depth(expected_type_of_self, actual_type_of_self) && implements_trait(cx, actual_type_of_self, trait_id, &[]) { return Some(cx.tcx.def_path_str(trait_id)); @@ -166,8 +187,8 @@ fn get_ufcs_type_name(cx: &LateContext<'_>, method_def_id: def_id::DefId, self_a cx.tcx.impl_of_method(method_def_id).and_then(|_| { //a type may implicitly implement other type's methods (e.g. Deref) - if match_types(expected_type_of_self, &actual_type_of_self) { - return Some(get_type_name(cx, &actual_type_of_self)); + if match_types(expected_type_of_self, actual_type_of_self) { + return Some(get_type_name(cx, actual_type_of_self)); } None }) @@ -175,7 +196,7 @@ fn get_ufcs_type_name(cx: &LateContext<'_>, method_def_id: def_id::DefId, self_a fn match_borrow_depth(lhs: Ty<'_>, rhs: Ty<'_>) -> bool { match (&lhs.kind(), &rhs.kind()) { - (ty::Ref(_, t1, mut1), ty::Ref(_, t2, mut2)) => mut1 == mut2 && match_borrow_depth(&t1, &t2), + (ty::Ref(_, t1, mut1), ty::Ref(_, t2, mut2)) => mut1 == mut2 && match_borrow_depth(t1, t2), (l, r) => !matches!((l, r), (ty::Ref(_, _, _), _) | (_, ty::Ref(_, _, _))), } } @@ -197,7 +218,7 @@ fn match_types(lhs: Ty<'_>, rhs: Ty<'_>) -> bool { fn get_type_name(cx: &LateContext<'_>, ty: Ty<'_>) -> String { match ty.kind() { ty::Adt(t, _) => cx.tcx.def_path_str(t.did), - ty::Ref(_, r, _) => get_type_name(cx, &r), + ty::Ref(_, r, _) => get_type_name(cx, r), _ => ty.to_string(), } } @@ -209,7 +230,7 @@ fn compare_inputs( for (closure_input, function_arg) in closure_inputs.zip(call_args) { if let PatKind::Binding(_, _, ident, _) = closure_input.pat.kind { // XXXManishearth Should I be checking the binding mode here? - if let ExprKind::Path(QPath::Resolved(None, ref p)) = function_arg.kind { + if let ExprKind::Path(QPath::Resolved(None, p)) = function_arg.kind { if p.segments.len() != 1 { // If it's a proper path, it can't be a local variable return false; diff --git a/src/tools/clippy/clippy_lints/src/eval_order_dependence.rs b/src/tools/clippy/clippy_lints/src/eval_order_dependence.rs index 83cee11c3a859..762f64fe37ad6 100644 --- a/src/tools/clippy/clippy_lints/src/eval_order_dependence.rs +++ b/src/tools/clippy/clippy_lints/src/eval_order_dependence.rs @@ -1,4 +1,5 @@ -use crate::utils::{get_parent_expr, path_to_local, path_to_local_id, span_lint, span_lint_and_note}; +use clippy_utils::diagnostics::{span_lint, span_lint_and_note}; +use clippy_utils::{get_parent_expr, path_to_local, path_to_local_id}; use rustc_hir::intravisit::{walk_expr, NestedVisitorMap, Visitor}; use rustc_hir::{BinOpKind, Block, Expr, ExprKind, Guard, HirId, Local, Node, Stmt, StmtKind}; use rustc_lint::{LateContext, LateLintPass}; @@ -70,7 +71,7 @@ impl<'tcx> LateLintPass<'tcx> for EvalOrderDependence { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { // Find a write to a local variable. match expr.kind { - ExprKind::Assign(ref lhs, ..) | ExprKind::AssignOp(_, ref lhs, _) => { + ExprKind::Assign(lhs, ..) | ExprKind::AssignOp(_, lhs, _) => { if let Some(var) = path_to_local(lhs) { let mut visitor = ReadVisitor { cx, @@ -86,12 +87,12 @@ impl<'tcx> LateLintPass<'tcx> for EvalOrderDependence { } fn check_stmt(&mut self, cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'_>) { match stmt.kind { - StmtKind::Local(ref local) => { - if let Local { init: Some(ref e), .. } = **local { + StmtKind::Local(local) => { + if let Local { init: Some(e), .. } = local { DivergenceVisitor { cx }.visit_expr(e); } }, - StmtKind::Expr(ref e) | StmtKind::Semi(ref e) => DivergenceVisitor { cx }.maybe_walk_expr(e), + StmtKind::Expr(e) | StmtKind::Semi(e) => DivergenceVisitor { cx }.maybe_walk_expr(e), StmtKind::Item(..) => {}, } } @@ -105,7 +106,7 @@ impl<'a, 'tcx> DivergenceVisitor<'a, 'tcx> { fn maybe_walk_expr(&mut self, e: &'tcx Expr<'_>) { match e.kind { ExprKind::Closure(..) => {}, - ExprKind::Match(ref e, arms, _) => { + ExprKind::Match(e, arms, _) => { self.visit_expr(e); for arm in arms { if let Some(Guard::If(if_expr)) = arm.guard { @@ -129,7 +130,7 @@ impl<'a, 'tcx> Visitor<'tcx> for DivergenceVisitor<'a, 'tcx> { fn visit_expr(&mut self, e: &'tcx Expr<'_>) { match e.kind { ExprKind::Continue(_) | ExprKind::Break(_, _) | ExprKind::Ret(_) => self.report_diverging_sub_expr(e), - ExprKind::Call(ref func, _) => { + ExprKind::Call(func, _) => { let typ = self.cx.typeck_results().expr_ty(func); match typ.kind() { ty::FnDef(..) | ty::FnPtr(_) => { @@ -265,14 +266,14 @@ fn check_expr<'a, 'tcx>(vis: &mut ReadVisitor<'a, 'tcx>, expr: &'tcx Expr<'_>) - fn check_stmt<'a, 'tcx>(vis: &mut ReadVisitor<'a, 'tcx>, stmt: &'tcx Stmt<'_>) -> StopEarly { match stmt.kind { - StmtKind::Expr(ref expr) | StmtKind::Semi(ref expr) => check_expr(vis, expr), + StmtKind::Expr(expr) | StmtKind::Semi(expr) => check_expr(vis, expr), // If the declaration is of a local variable, check its initializer // expression if it has one. Otherwise, keep going. - StmtKind::Local(ref local) => local + StmtKind::Local(local) => local .init .as_ref() .map_or(StopEarly::KeepGoing, |expr| check_expr(vis, expr)), - _ => StopEarly::KeepGoing, + StmtKind::Item(..) => StopEarly::KeepGoing, } } @@ -342,7 +343,7 @@ impl<'a, 'tcx> Visitor<'tcx> for ReadVisitor<'a, 'tcx> { /// Returns `true` if `expr` is the LHS of an assignment, like `expr = ...`. fn is_in_assignment_position(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { if let Some(parent) = get_parent_expr(cx, expr) { - if let ExprKind::Assign(ref lhs, ..) = parent.kind { + if let ExprKind::Assign(lhs, ..) = parent.kind { return lhs.hir_id == expr.hir_id; } } diff --git a/src/tools/clippy/clippy_lints/src/excessive_bools.rs b/src/tools/clippy/clippy_lints/src/excessive_bools.rs index 6f22f65deac80..249ee27330bf9 100644 --- a/src/tools/clippy/clippy_lints/src/excessive_bools.rs +++ b/src/tools/clippy/clippy_lints/src/excessive_bools.rs @@ -1,10 +1,9 @@ -use crate::utils::{attr_by_name, in_macro, match_path_ast, span_lint_and_help}; -use rustc_ast::ast::{ - AssocItemKind, Extern, FnKind, FnSig, ImplKind, Item, ItemKind, TraitKind, Ty, TyKind, -}; +use clippy_utils::diagnostics::span_lint_and_help; +use clippy_utils::{in_macro, match_path_ast}; +use rustc_ast::ast::{AssocItemKind, Extern, FnKind, FnSig, ImplKind, Item, ItemKind, TraitKind, Ty, TyKind}; use rustc_lint::{EarlyContext, EarlyLintPass}; use rustc_session::{declare_tool_lint, impl_lint_pass}; -use rustc_span::Span; +use rustc_span::{sym, Span}; use std::convert::TryInto; @@ -139,7 +138,7 @@ impl EarlyLintPass for ExcessiveBools { } match &item.kind { ItemKind::Struct(variant_data, _) => { - if attr_by_name(&item.attrs, "repr").is_some() { + if item.attrs.iter().any(|attr| attr.has_name(sym::repr)) { return; } diff --git a/src/tools/clippy/clippy_lints/src/exhaustive_items.rs b/src/tools/clippy/clippy_lints/src/exhaustive_items.rs index ab9be3398bfa6..60ad2e8ee1404 100644 --- a/src/tools/clippy/clippy_lints/src/exhaustive_items.rs +++ b/src/tools/clippy/clippy_lints/src/exhaustive_items.rs @@ -1,4 +1,5 @@ -use crate::utils::{indent_of, span_lint_and_then}; +use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::source::indent_of; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::{Item, ItemKind}; @@ -73,7 +74,8 @@ impl LateLintPass<'_> for ExhaustiveItems { if_chain! { if let ItemKind::Enum(..) | ItemKind::Struct(..) = item.kind; if cx.access_levels.is_exported(item.hir_id()); - if !item.attrs.iter().any(|a| a.has_name(sym::non_exhaustive)); + let attrs = cx.tcx.hir().attrs(item.hir_id()); + if !attrs.iter().any(|a| a.has_name(sym::non_exhaustive)); then { let (lint, msg) = if let ItemKind::Struct(ref v, ..) = item.kind { if v.fields().iter().any(|f| !f.vis.node.is_pub()) { diff --git a/src/tools/clippy/clippy_lints/src/exit.rs b/src/tools/clippy/clippy_lints/src/exit.rs index 915859270009b..16246e548b613 100644 --- a/src/tools/clippy/clippy_lints/src/exit.rs +++ b/src/tools/clippy/clippy_lints/src/exit.rs @@ -1,4 +1,5 @@ -use crate::utils::{is_entrypoint_fn, match_def_path, paths, span_lint}; +use clippy_utils::diagnostics::span_lint; +use clippy_utils::{is_entrypoint_fn, match_def_path, paths}; use if_chain::if_chain; use rustc_hir::{Expr, ExprKind, Item, ItemKind, Node}; use rustc_lint::{LateContext, LateLintPass}; @@ -27,20 +28,18 @@ declare_lint_pass!(Exit => [EXIT]); impl<'tcx> LateLintPass<'tcx> for Exit { fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) { if_chain! { - if let ExprKind::Call(ref path_expr, ref _args) = e.kind; + if let ExprKind::Call(path_expr, _args) = e.kind; if let ExprKind::Path(ref path) = path_expr.kind; if let Some(def_id) = cx.qpath_res(path, path_expr.hir_id).opt_def_id(); if match_def_path(cx, def_id, &paths::EXIT); + let parent = cx.tcx.hir().get_parent_item(e.hir_id); + if let Some(Node::Item(Item{kind: ItemKind::Fn(..), ..})) = cx.tcx.hir().find(parent); + // If the next item up is a function we check if it is an entry point + // and only then emit a linter warning + let def_id = cx.tcx.hir().local_def_id(parent); + if !is_entrypoint_fn(cx, def_id.to_def_id()); then { - let parent = cx.tcx.hir().get_parent_item(e.hir_id); - if let Some(Node::Item(Item{kind: ItemKind::Fn(..), ..})) = cx.tcx.hir().find(parent) { - // If the next item up is a function we check if it is an entry point - // and only then emit a linter warning - let def_id = cx.tcx.hir().local_def_id(parent); - if !is_entrypoint_fn(cx, def_id.to_def_id()) { - span_lint(cx, EXIT, e.span, "usage of `process::exit`"); - } - } + span_lint(cx, EXIT, e.span, "usage of `process::exit`"); } } } diff --git a/src/tools/clippy/clippy_lints/src/explicit_write.rs b/src/tools/clippy/clippy_lints/src/explicit_write.rs index f8038d06e5034..da4936ff25b6d 100644 --- a/src/tools/clippy/clippy_lints/src/explicit_write.rs +++ b/src/tools/clippy/clippy_lints/src/explicit_write.rs @@ -1,4 +1,5 @@ -use crate::utils::{is_expn_of, match_function_call, paths, span_lint, span_lint_and_sugg}; +use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg}; +use clippy_utils::{is_expn_of, match_function_call, paths}; use if_chain::if_chain; use rustc_ast::ast::LitKind; use rustc_errors::Applicability; @@ -33,11 +34,11 @@ impl<'tcx> LateLintPass<'tcx> for ExplicitWrite { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { if_chain! { // match call to unwrap - if let ExprKind::MethodCall(ref unwrap_fun, _, ref unwrap_args, _) = expr.kind; + if let ExprKind::MethodCall(unwrap_fun, _, unwrap_args, _) = expr.kind; if unwrap_fun.ident.name == sym::unwrap; // match call to write_fmt if !unwrap_args.is_empty(); - if let ExprKind::MethodCall(ref write_fun, _, write_args, _) = + if let ExprKind::MethodCall(write_fun, _, write_args, _) = unwrap_args[0].kind; if write_fun.ident.name == sym!(write_fmt); // match calls to std::io::stdout() / std::io::stderr () @@ -134,10 +135,10 @@ fn write_output_string(write_args: &[Expr<'_>]) -> Option<String> { if_chain! { // Obtain the string that should be printed if write_args.len() > 1; - if let ExprKind::Call(_, ref output_args) = write_args[1].kind; + if let ExprKind::Call(_, output_args) = write_args[1].kind; if !output_args.is_empty(); - if let ExprKind::AddrOf(BorrowKind::Ref, _, ref output_string_expr) = output_args[0].kind; - if let ExprKind::Array(ref string_exprs) = output_string_expr.kind; + if let ExprKind::AddrOf(BorrowKind::Ref, _, output_string_expr) = output_args[0].kind; + if let ExprKind::Array(string_exprs) = output_string_expr.kind; // we only want to provide an automatic suggestion for simple (non-format) strings if string_exprs.len() == 1; if let ExprKind::Lit(ref lit) = string_exprs[0].kind; 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 6d522c7ef3398..2937fcb9ca0f3 100644 --- a/src/tools/clippy/clippy_lints/src/fallible_impl_from.rs +++ b/src/tools/clippy/clippy_lints/src/fallible_impl_from.rs @@ -1,4 +1,6 @@ -use crate::utils::{is_expn_of, is_type_diagnostic_item, match_panic_def_id, method_chain_args, span_lint_and_then}; +use clippy_utils::diagnostics::span_lint_and_then; +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}; @@ -79,8 +81,8 @@ fn lint_impl_body<'tcx>(cx: &LateContext<'tcx>, impl_span: Span, impl_items: &[h fn visit_expr(&mut self, expr: &'tcx Expr<'_>) { // check for `begin_panic` if_chain! { - if let ExprKind::Call(ref func_expr, _) = expr.kind; - if let ExprKind::Path(QPath::Resolved(_, ref path)) = func_expr.kind; + 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(); @@ -133,7 +135,7 @@ fn lint_impl_body<'tcx>(cx: &LateContext<'tcx>, impl_span: Span, impl_items: &[h move |diag| { diag.help( "`From` is intended for infallible conversions only. \ - Use `TryFrom` if there's a possibility for the conversion to fail."); + Use `TryFrom` if there's a possibility for the conversion to fail"); diag.span_note(fpu.result, "potential failure(s)"); }); } diff --git a/src/tools/clippy/clippy_lints/src/float_equality_without_abs.rs b/src/tools/clippy/clippy_lints/src/float_equality_without_abs.rs index c1c08597ee670..b5ebe5f90ba25 100644 --- a/src/tools/clippy/clippy_lints/src/float_equality_without_abs.rs +++ b/src/tools/clippy/clippy_lints/src/float_equality_without_abs.rs @@ -1,4 +1,5 @@ -use crate::utils::{match_def_path, paths, span_lint_and_then, sugg}; +use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::{match_def_path, paths, sugg}; use if_chain::if_chain; use rustc_ast::util::parser::AssocOp; use rustc_errors::Applicability; @@ -47,7 +48,7 @@ impl<'tcx> LateLintPass<'tcx> for FloatEqualityWithoutAbs { let rhs; // check if expr is a binary expression with a lt or gt operator - if let ExprKind::Binary(op, ref left, ref right) = expr.kind { + if let ExprKind::Binary(op, left, right) = expr.kind { match op.node { BinOpKind::Lt => { lhs = left; @@ -87,8 +88,8 @@ impl<'tcx> LateLintPass<'tcx> for FloatEqualityWithoutAbs { if let ty::Float(_) = t_val_r.kind(); then { - let sug_l = sugg::Sugg::hir(cx, &val_l, ".."); - let sug_r = sugg::Sugg::hir(cx, &val_r, ".."); + let sug_l = sugg::Sugg::hir(cx, val_l, ".."); + let sug_r = sugg::Sugg::hir(cx, val_r, ".."); // format the suggestion let suggestion = format!("{}.abs()", sugg::make_assoc(AssocOp::Subtract, &sug_l, &sug_r).maybe_par()); // spans the lint diff --git a/src/tools/clippy/clippy_lints/src/float_literal.rs b/src/tools/clippy/clippy_lints/src/float_literal.rs index be646cbe4d043..7968e7b764df3 100644 --- a/src/tools/clippy/clippy_lints/src/float_literal.rs +++ b/src/tools/clippy/clippy_lints/src/float_literal.rs @@ -1,4 +1,5 @@ -use crate::utils::{numeric_literal, span_lint_and_sugg}; +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::numeric_literal; use if_chain::if_chain; use rustc_ast::ast::{self, LitFloatType, LitKind}; use rustc_errors::Applicability; @@ -60,8 +61,8 @@ declare_lint_pass!(FloatLiteral => [EXCESSIVE_PRECISION, LOSSY_FLOAT_LITERAL]); impl<'tcx> LateLintPass<'tcx> for FloatLiteral { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) { + let ty = cx.typeck_results().expr_ty(expr); if_chain! { - let ty = cx.typeck_results().expr_ty(expr); if let ty::Float(fty) = *ty.kind(); if let hir::ExprKind::Lit(ref lit) = expr.kind; if let LitKind::Float(sym, lit_float_ty) = lit.node; @@ -145,11 +146,7 @@ fn count_digits(s: &str) -> usize { .take_while(|c| *c != 'e' && *c != 'E') .fold(0, |count, c| { // leading zeros - if c == '0' && count == 0 { - count - } else { - count + 1 - } + if c == '0' && count == 0 { count } else { count + 1 } }) } diff --git a/src/tools/clippy/clippy_lints/src/floating_point_arithmetic.rs b/src/tools/clippy/clippy_lints/src/floating_point_arithmetic.rs index 086a791520fa8..e0b687b020521 100644 --- a/src/tools/clippy/clippy_lints/src/floating_point_arithmetic.rs +++ b/src/tools/clippy/clippy_lints/src/floating_point_arithmetic.rs @@ -2,7 +2,8 @@ use crate::consts::{ constant, constant_simple, Constant, Constant::{Int, F32, F64}, }; -use crate::utils::{eq_expr_value, get_parent_expr, numeric_literal, span_lint_and_sugg, sugg}; +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::{eq_expr_value, get_parent_expr, numeric_literal, sugg}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::{BinOpKind, Expr, ExprKind, PathSegment, UnOp}; @@ -130,7 +131,7 @@ fn prepare_receiver_sugg<'a>(cx: &LateContext<'_>, mut expr: &'a Expr<'a>) -> Su let mut suggestion = Sugg::hir(cx, expr, ".."); if let ExprKind::Unary(UnOp::Neg, inner_expr) = &expr.kind { - expr = &inner_expr; + expr = inner_expr; } if_chain! { @@ -312,8 +313,8 @@ fn check_powi(cx: &LateContext<'_>, expr: &Expr<'_>, args: &[Expr<'_>]) { Spanned { node: BinOpKind::Add, .. }, - ref lhs, - ref rhs, + lhs, + rhs, ) = parent.kind { let other_addend = if lhs.hir_id == expr.hir_id { rhs } else { lhs }; @@ -328,7 +329,7 @@ fn check_powi(cx: &LateContext<'_>, expr: &Expr<'_>, args: &[Expr<'_>]) { "{}.mul_add({}, {})", Sugg::hir(cx, &args[0], ".."), Sugg::hir(cx, &args[0], ".."), - Sugg::hir(cx, &other_addend, ".."), + Sugg::hir(cx, other_addend, ".."), ), Applicability::MachineApplicable, ); @@ -355,18 +356,18 @@ fn detect_hypot(cx: &LateContext<'_>, args: &[Expr<'_>]) -> Option<String> { Spanned { node: BinOpKind::Add, .. }, - ref add_lhs, - ref add_rhs, + add_lhs, + add_rhs, ) = args[0].kind { // check if expression of the form x * x + y * y if_chain! { - if let ExprKind::Binary(Spanned { node: BinOpKind::Mul, .. }, ref lmul_lhs, ref lmul_rhs) = add_lhs.kind; - if let ExprKind::Binary(Spanned { node: BinOpKind::Mul, .. }, ref rmul_lhs, ref rmul_rhs) = add_rhs.kind; + if let ExprKind::Binary(Spanned { node: BinOpKind::Mul, .. }, lmul_lhs, lmul_rhs) = add_lhs.kind; + if let ExprKind::Binary(Spanned { node: BinOpKind::Mul, .. }, rmul_lhs, rmul_rhs) = add_rhs.kind; if eq_expr_value(cx, lmul_lhs, lmul_rhs); if eq_expr_value(cx, rmul_lhs, rmul_rhs); then { - return Some(format!("{}.hypot({})", Sugg::hir(cx, &lmul_lhs, ".."), Sugg::hir(cx, &rmul_lhs, ".."))); + return Some(format!("{}.hypot({})", Sugg::hir(cx, lmul_lhs, ".."), Sugg::hir(cx, rmul_lhs, ".."))); } } @@ -375,13 +376,13 @@ fn detect_hypot(cx: &LateContext<'_>, args: &[Expr<'_>]) -> Option<String> { if let ExprKind::MethodCall( PathSegment { ident: lmethod_name, .. }, ref _lspan, - ref largs, + largs, _ ) = add_lhs.kind; if let ExprKind::MethodCall( PathSegment { ident: rmethod_name, .. }, ref _rspan, - ref rargs, + rargs, _ ) = add_rhs.kind; if lmethod_name.as_str() == "powi" && rmethod_name.as_str() == "powi"; @@ -415,11 +416,11 @@ fn check_hypot(cx: &LateContext<'_>, expr: &Expr<'_>, args: &[Expr<'_>]) { // and suggest usage of `x.exp_m1() - (y - 1)` instead fn check_expm1(cx: &LateContext<'_>, expr: &Expr<'_>) { if_chain! { - if let ExprKind::Binary(Spanned { node: BinOpKind::Sub, .. }, ref lhs, ref rhs) = expr.kind; + if let ExprKind::Binary(Spanned { node: BinOpKind::Sub, .. }, lhs, rhs) = expr.kind; if cx.typeck_results().expr_ty(lhs).is_floating_point(); if let Some((value, _)) = constant(cx, cx.typeck_results(), rhs); if F32(1.0) == value || F64(1.0) == value; - if let ExprKind::MethodCall(ref path, _, ref method_args, _) = lhs.kind; + if let ExprKind::MethodCall(path, _, method_args, _) = lhs.kind; if cx.typeck_results().expr_ty(&method_args[0]).is_floating_point(); if path.ident.name.as_str() == "exp"; then { @@ -441,7 +442,7 @@ fn check_expm1(cx: &LateContext<'_>, expr: &Expr<'_>) { fn is_float_mul_expr<'a>(cx: &LateContext<'_>, expr: &'a Expr<'a>) -> Option<(&'a Expr<'a>, &'a Expr<'a>)> { if_chain! { - if let ExprKind::Binary(Spanned { node: BinOpKind::Mul, .. }, ref lhs, ref rhs) = &expr.kind; + if let ExprKind::Binary(Spanned { node: BinOpKind::Mul, .. }, lhs, rhs) = &expr.kind; if cx.typeck_results().expr_ty(lhs).is_floating_point(); if cx.typeck_results().expr_ty(rhs).is_floating_point(); then { @@ -603,8 +604,8 @@ fn check_custom_abs(cx: &LateContext<'_>, expr: &Expr<'_>) { fn are_same_base_logs(cx: &LateContext<'_>, expr_a: &Expr<'_>, expr_b: &Expr<'_>) -> bool { if_chain! { - if let ExprKind::MethodCall(PathSegment { ident: method_name_a, .. }, _, ref args_a, _) = expr_a.kind; - if let ExprKind::MethodCall(PathSegment { ident: method_name_b, .. }, _, ref args_b, _) = expr_b.kind; + if let ExprKind::MethodCall(PathSegment { ident: method_name_a, .. }, _, args_a, _) = expr_a.kind; + if let ExprKind::MethodCall(PathSegment { ident: method_name_b, .. }, _, args_b, _) = expr_b.kind; then { return method_name_a.as_str() == method_name_b.as_str() && args_a.len() == args_b.len() && @@ -629,8 +630,8 @@ fn check_log_division(cx: &LateContext<'_>, expr: &Expr<'_>) { rhs, ) = &expr.kind; if are_same_base_logs(cx, lhs, rhs); - if let ExprKind::MethodCall(_, _, ref largs, _) = lhs.kind; - if let ExprKind::MethodCall(_, _, ref rargs, _) = rhs.kind; + if let ExprKind::MethodCall(_, _, largs, _) = lhs.kind; + if let ExprKind::MethodCall(_, _, rargs, _) = rhs.kind; then { span_lint_and_sugg( cx, @@ -674,7 +675,7 @@ fn check_radians(cx: &LateContext<'_>, expr: &Expr<'_>) { expr.span, "conversion to degrees can be done more accurately", "consider using", - format!("{}.to_degrees()", Sugg::hir(cx, &mul_lhs, "..")), + format!("{}.to_degrees()", Sugg::hir(cx, mul_lhs, "..")), Applicability::MachineApplicable, ); } else if @@ -687,7 +688,7 @@ fn check_radians(cx: &LateContext<'_>, expr: &Expr<'_>) { expr.span, "conversion to radians can be done more accurately", "consider using", - format!("{}.to_radians()", Sugg::hir(cx, &mul_lhs, "..")), + format!("{}.to_radians()", Sugg::hir(cx, mul_lhs, "..")), Applicability::MachineApplicable, ); } @@ -697,7 +698,7 @@ fn check_radians(cx: &LateContext<'_>, expr: &Expr<'_>) { impl<'tcx> LateLintPass<'tcx> for FloatingPointArithmetic { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - if let ExprKind::MethodCall(ref path, _, args, _) = &expr.kind { + if let ExprKind::MethodCall(path, _, args, _) = &expr.kind { let recv_ty = cx.typeck_results().expr_ty(&args[0]); if recv_ty.is_floating_point() { diff --git a/src/tools/clippy/clippy_lints/src/format.rs b/src/tools/clippy/clippy_lints/src/format.rs index 8e41e0e34daf7..4729abbd8e3f7 100644 --- a/src/tools/clippy/clippy_lints/src/format.rs +++ b/src/tools/clippy/clippy_lints/src/format.rs @@ -1,8 +1,8 @@ -use crate::utils::paths; -use crate::utils::{ - is_expn_of, is_type_diagnostic_item, last_path_segment, match_def_path, match_function_call, snippet, snippet_opt, - span_lint_and_then, -}; +use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::paths; +use clippy_utils::source::{snippet, snippet_opt}; +use clippy_utils::ty::is_type_diagnostic_item; +use clippy_utils::{is_expn_of, last_path_segment, match_def_path, match_function_call}; use if_chain::if_chain; use rustc_ast::ast::LitKind; use rustc_errors::Applicability; @@ -28,11 +28,11 @@ declare_clippy_lint! { /// ```rust /// /// // Bad - /// # let foo = "foo"; + /// let foo = "foo"; /// format!("{}", foo); /// /// // Good - /// format!("foo"); + /// foo.to_owned(); /// ``` pub USELESS_FORMAT, complexity, @@ -78,8 +78,8 @@ fn span_useless_format<T: LintContext>(cx: &T, span: Span, help: &str, mut sugg: fn on_argumentv1_new<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, arms: &'tcx [Arm<'_>]) -> Option<String> { if_chain! { - if let ExprKind::AddrOf(BorrowKind::Ref, _, ref format_args) = expr.kind; - if let ExprKind::Array(ref elems) = arms[0].body.kind; + if let ExprKind::AddrOf(BorrowKind::Ref, _, format_args) = expr.kind; + if let ExprKind::Array(elems) = arms[0].body.kind; if elems.len() == 1; if let Some(args) = match_function_call(cx, &elems[0], &paths::FMT_ARGUMENTV1_NEW); // matches `core::fmt::Display::fmt` @@ -88,10 +88,10 @@ fn on_argumentv1_new<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, arms: & if let Some(did) = cx.qpath_res(qpath, args[1].hir_id).opt_def_id(); if match_def_path(cx, did, &paths::DISPLAY_FMT_METHOD); // check `(arg0,)` in match block - if let PatKind::Tuple(ref pats, None) = arms[0].pat.kind; + if let PatKind::Tuple(pats, None) = arms[0].pat.kind; if pats.len() == 1; then { - let ty = cx.typeck_results().pat_ty(&pats[0]).peel_refs(); + let ty = cx.typeck_results().pat_ty(pats[0]).peel_refs(); if *ty.kind() != rustc_middle::ty::Str && !is_type_diagnostic_item(cx, ty, sym::string_type) { return None; } @@ -101,7 +101,7 @@ fn on_argumentv1_new<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, arms: & } } else { let snip = snippet(cx, format_args.span, "<arg>"); - if let ExprKind::MethodCall(ref path, _, _, _) = format_args.kind { + if let ExprKind::MethodCall(path, _, _, _) = format_args.kind { if path.ident.name == sym!(to_string) { return Some(format!("{}", snip)); } @@ -120,16 +120,16 @@ fn on_new_v1<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option<Strin if let Some(args) = match_function_call(cx, expr, &paths::FMT_ARGUMENTS_NEW_V1); if args.len() == 2; // Argument 1 in `new_v1()` - if let ExprKind::AddrOf(BorrowKind::Ref, _, ref arr) = args[0].kind; - if let ExprKind::Array(ref pieces) = arr.kind; + if let ExprKind::AddrOf(BorrowKind::Ref, _, arr) = args[0].kind; + if let ExprKind::Array(pieces) = arr.kind; if pieces.len() == 1; if let ExprKind::Lit(ref lit) = pieces[0].kind; if let LitKind::Str(ref s, _) = lit.node; // Argument 2 in `new_v1()` - if let ExprKind::AddrOf(BorrowKind::Ref, _, ref arg1) = args[1].kind; - if let ExprKind::Match(ref matchee, ref arms, MatchSource::Normal) = arg1.kind; + if let ExprKind::AddrOf(BorrowKind::Ref, _, arg1) = args[1].kind; + if let ExprKind::Match(matchee, arms, MatchSource::Normal) = arg1.kind; if arms.len() == 1; - if let ExprKind::Tup(ref tup) = matchee.kind; + if let ExprKind::Tup(tup) = matchee.kind; then { // `format!("foo")` expansion contains `match () { () => [], }` if tup.is_empty() { @@ -152,16 +152,16 @@ fn on_new_v1_fmt<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option<S if args.len() == 3; if check_unformatted(&args[2]); // Argument 1 in `new_v1_formatted()` - if let ExprKind::AddrOf(BorrowKind::Ref, _, ref arr) = args[0].kind; - if let ExprKind::Array(ref pieces) = arr.kind; + if let ExprKind::AddrOf(BorrowKind::Ref, _, arr) = args[0].kind; + if let ExprKind::Array(pieces) = arr.kind; if pieces.len() == 1; if let ExprKind::Lit(ref lit) = pieces[0].kind; if let LitKind::Str(..) = lit.node; // Argument 2 in `new_v1_formatted()` - if let ExprKind::AddrOf(BorrowKind::Ref, _, ref arg1) = args[1].kind; - if let ExprKind::Match(ref matchee, ref arms, MatchSource::Normal) = arg1.kind; + if let ExprKind::AddrOf(BorrowKind::Ref, _, arg1) = args[1].kind; + if let ExprKind::Match(matchee, arms, MatchSource::Normal) = arg1.kind; if arms.len() == 1; - if let ExprKind::Tup(ref tup) = matchee.kind; + if let ExprKind::Tup(tup) = matchee.kind; then { return on_argumentv1_new(cx, &tup[0], arms); } @@ -182,14 +182,14 @@ fn on_new_v1_fmt<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option<S /// ``` fn check_unformatted(expr: &Expr<'_>) -> bool { if_chain! { - if let ExprKind::AddrOf(BorrowKind::Ref, _, ref expr) = expr.kind; - if let ExprKind::Array(ref exprs) = expr.kind; + if let ExprKind::AddrOf(BorrowKind::Ref, _, expr) = expr.kind; + if let ExprKind::Array(exprs) = expr.kind; if exprs.len() == 1; // struct `core::fmt::rt::v1::Argument` - if let ExprKind::Struct(_, ref fields, _) = exprs[0].kind; + if let ExprKind::Struct(_, fields, _) = exprs[0].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(_, ref fields, _) = format_field.expr.kind; + if let ExprKind::Struct(_, fields, _) = format_field.expr.kind; if let Some(precision_field) = fields.iter().find(|f| f.ident.name == sym::precision); if let ExprKind::Path(ref precision_path) = precision_field.expr.kind; if last_path_segment(precision_path).ident.name == sym::Implied; diff --git a/src/tools/clippy/clippy_lints/src/formatting.rs b/src/tools/clippy/clippy_lints/src/formatting.rs index 1bd16e6cce53a..48612befc68d9 100644 --- a/src/tools/clippy/clippy_lints/src/formatting.rs +++ b/src/tools/clippy/clippy_lints/src/formatting.rs @@ -1,4 +1,6 @@ -use crate::utils::{differing_macro_contexts, snippet_opt, span_lint_and_help, span_lint_and_note}; +use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_note}; +use clippy_utils::differing_macro_contexts; +use clippy_utils::source::snippet_opt; use if_chain::if_chain; use rustc_ast::ast::{BinOpKind, Block, Expr, ExprKind, StmtKind, UnOp}; use rustc_lint::{EarlyContext, EarlyLintPass}; @@ -215,9 +217,8 @@ fn check_else(cx: &EarlyContext<'_>, expr: &Expr) { if let Some(else_snippet) = snippet_opt(cx, else_span); if let Some(else_pos) = else_snippet.find("else"); if else_snippet[else_pos..].contains('\n'); - let else_desc = if is_if(else_) { "if" } else { "{..}" }; - then { + let else_desc = if is_if(else_) { "if" } else { "{..}" }; span_lint_and_note( cx, SUSPICIOUS_ELSE_FORMATTING, 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 b644bb079908f..e5ec245e5029b 100644 --- a/src/tools/clippy/clippy_lints/src/from_over_into.rs +++ b/src/tools/clippy/clippy_lints/src/from_over_into.rs @@ -1,5 +1,6 @@ -use crate::utils::paths::INTO; -use crate::utils::{match_def_path, meets_msrv, span_lint_and_help}; +use clippy_utils::diagnostics::span_lint_and_help; +use clippy_utils::paths::INTO; +use clippy_utils::{match_def_path, meets_msrv}; use if_chain::if_chain; use rustc_hir as hir; use rustc_lint::{LateContext, LateLintPass, LintContext}; 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 0933f9830147c..3da5bc95b6db1 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 @@ -1,3 +1,6 @@ +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::sugg::Sugg; +use clippy_utils::ty::is_type_diagnostic_item; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::{def, Expr, ExprKind, PrimTy, QPath, TyKind}; @@ -6,10 +9,6 @@ use rustc_middle::ty::Ty; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::symbol::sym; -use crate::utils::is_type_diagnostic_item; -use crate::utils::span_lint_and_sugg; -use crate::utils::sugg::Sugg; - declare_clippy_lint! { /// **What it does:** /// Checks for function invocations of the form `primitive::from_str_radix(s, 10)` diff --git a/src/tools/clippy/clippy_lints/src/functions.rs b/src/tools/clippy/clippy_lints/src/functions.rs deleted file mode 100644 index e4b3a9009f612..0000000000000 --- a/src/tools/clippy/clippy_lints/src/functions.rs +++ /dev/null @@ -1,736 +0,0 @@ -use crate::utils::{ - attr_by_name, attrs::is_proc_macro, is_must_use_ty, is_trait_impl_item, is_type_diagnostic_item, iter_input_pats, - match_def_path, must_use_attr, path_to_local, return_ty, snippet, snippet_opt, span_lint, span_lint_and_help, - span_lint_and_then, trait_ref_of_method, type_is_unsafe_function, -}; -use if_chain::if_chain; -use rustc_ast::ast::Attribute; -use rustc_data_structures::fx::FxHashSet; -use rustc_errors::Applicability; -use rustc_hir as hir; -use rustc_hir::intravisit; -use rustc_hir::{def::Res, def_id::DefId, QPath}; -use rustc_lint::{LateContext, LateLintPass, LintContext}; -use rustc_middle::hir::map::Map; -use rustc_middle::lint::in_external_macro; -use rustc_middle::ty::{self, Ty}; -use rustc_session::{declare_tool_lint, impl_lint_pass}; -use rustc_span::source_map::Span; -use rustc_span::sym; -use rustc_target::spec::abi::Abi; -use rustc_typeck::hir_ty_to_ty; - -declare_clippy_lint! { - /// **What it does:** Checks for functions with too many parameters. - /// - /// **Why is this bad?** Functions with lots of parameters are considered bad - /// style and reduce readability (“what does the 5th parameter mean?”). Consider - /// grouping some parameters into a new type. - /// - /// **Known problems:** None. - /// - /// **Example:** - /// ```rust - /// # struct Color; - /// fn foo(x: u32, y: u32, name: &str, c: Color, w: f32, h: f32, a: f32, b: f32) { - /// // .. - /// } - /// ``` - pub TOO_MANY_ARGUMENTS, - complexity, - "functions with too many arguments" -} - -declare_clippy_lint! { - /// **What it does:** Checks for functions with a large amount of lines. - /// - /// **Why is this bad?** Functions with a lot of lines are harder to understand - /// due to having to look at a larger amount of code to understand what the - /// function is doing. Consider splitting the body of the function into - /// multiple functions. - /// - /// **Known problems:** None. - /// - /// **Example:** - /// ```rust - /// fn im_too_long() { - /// println!(""); - /// // ... 100 more LoC - /// println!(""); - /// } - /// ``` - pub TOO_MANY_LINES, - pedantic, - "functions with too many lines" -} - -declare_clippy_lint! { - /// **What it does:** Checks for public functions that dereference raw pointer - /// arguments but are not marked unsafe. - /// - /// **Why is this bad?** The function should probably be marked `unsafe`, since - /// for an arbitrary raw pointer, there is no way of telling for sure if it is - /// valid. - /// - /// **Known problems:** - /// - /// * It does not check functions recursively so if the pointer is passed to a - /// private non-`unsafe` function which does the dereferencing, the lint won't - /// trigger. - /// * It only checks for arguments whose type are raw pointers, not raw pointers - /// got from an argument in some other way (`fn foo(bar: &[*const u8])` or - /// `some_argument.get_raw_ptr()`). - /// - /// **Example:** - /// ```rust,ignore - /// // Bad - /// pub fn foo(x: *const u8) { - /// println!("{}", unsafe { *x }); - /// } - /// - /// // Good - /// pub unsafe fn foo(x: *const u8) { - /// println!("{}", unsafe { *x }); - /// } - /// ``` - pub NOT_UNSAFE_PTR_ARG_DEREF, - correctness, - "public functions dereferencing raw pointer arguments but not marked `unsafe`" -} - -declare_clippy_lint! { - /// **What it does:** Checks for a [`#[must_use]`] attribute on - /// unit-returning functions and methods. - /// - /// [`#[must_use]`]: https://doc.rust-lang.org/reference/attributes/diagnostics.html#the-must_use-attribute - /// - /// **Why is this bad?** Unit values are useless. The attribute is likely - /// a remnant of a refactoring that removed the return type. - /// - /// **Known problems:** None. - /// - /// **Examples:** - /// ```rust - /// #[must_use] - /// fn useless() { } - /// ``` - pub MUST_USE_UNIT, - style, - "`#[must_use]` attribute on a unit-returning function / method" -} - -declare_clippy_lint! { - /// **What it does:** Checks for a [`#[must_use]`] attribute without - /// further information on functions and methods that return a type already - /// marked as `#[must_use]`. - /// - /// [`#[must_use]`]: https://doc.rust-lang.org/reference/attributes/diagnostics.html#the-must_use-attribute - /// - /// **Why is this bad?** The attribute isn't needed. Not using the result - /// will already be reported. Alternatively, one can add some text to the - /// attribute to improve the lint message. - /// - /// **Known problems:** None. - /// - /// **Examples:** - /// ```rust - /// #[must_use] - /// fn double_must_use() -> Result<(), ()> { - /// unimplemented!(); - /// } - /// ``` - pub DOUBLE_MUST_USE, - style, - "`#[must_use]` attribute on a `#[must_use]`-returning function / method" -} - -declare_clippy_lint! { - /// **What it does:** Checks for public functions that have no - /// [`#[must_use]`] attribute, but return something not already marked - /// must-use, have no mutable arg and mutate no statics. - /// - /// [`#[must_use]`]: https://doc.rust-lang.org/reference/attributes/diagnostics.html#the-must_use-attribute - /// - /// **Why is this bad?** Not bad at all, this lint just shows places where - /// you could add the attribute. - /// - /// **Known problems:** The lint only checks the arguments for mutable - /// types without looking if they are actually changed. On the other hand, - /// it also ignores a broad range of potentially interesting side effects, - /// because we cannot decide whether the programmer intends the function to - /// be called for the side effect or the result. Expect many false - /// positives. At least we don't lint if the result type is unit or already - /// `#[must_use]`. - /// - /// **Examples:** - /// ```rust - /// // this could be annotated with `#[must_use]`. - /// fn id<T>(t: T) -> T { t } - /// ``` - pub MUST_USE_CANDIDATE, - pedantic, - "function or method that could take a `#[must_use]` attribute" -} - -declare_clippy_lint! { - /// **What it does:** Checks for public functions that return a `Result` - /// with an `Err` type of `()`. It suggests using a custom type that - /// implements [`std::error::Error`]. - /// - /// **Why is this bad?** Unit does not implement `Error` and carries no - /// further information about what went wrong. - /// - /// **Known problems:** Of course, this lint assumes that `Result` is used - /// for a fallible operation (which is after all the intended use). However - /// code may opt to (mis)use it as a basic two-variant-enum. In that case, - /// the suggestion is misguided, and the code should use a custom enum - /// instead. - /// - /// **Examples:** - /// ```rust - /// pub fn read_u8() -> Result<u8, ()> { Err(()) } - /// ``` - /// should become - /// ```rust,should_panic - /// use std::fmt; - /// - /// #[derive(Debug)] - /// pub struct EndOfStream; - /// - /// impl fmt::Display for EndOfStream { - /// fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - /// write!(f, "End of Stream") - /// } - /// } - /// - /// impl std::error::Error for EndOfStream { } - /// - /// pub fn read_u8() -> Result<u8, EndOfStream> { Err(EndOfStream) } - ///# fn main() { - ///# read_u8().unwrap(); - ///# } - /// ``` - /// - /// Note that there are crates that simplify creating the error type, e.g. - /// [`thiserror`](https://docs.rs/thiserror). - pub RESULT_UNIT_ERR, - style, - "public function returning `Result` with an `Err` type of `()`" -} - -#[derive(Copy, Clone)] -pub struct Functions { - threshold: u64, - max_lines: u64, -} - -impl Functions { - pub fn new(threshold: u64, max_lines: u64) -> Self { - Self { threshold, max_lines } - } -} - -impl_lint_pass!(Functions => [ - TOO_MANY_ARGUMENTS, - TOO_MANY_LINES, - NOT_UNSAFE_PTR_ARG_DEREF, - MUST_USE_UNIT, - DOUBLE_MUST_USE, - MUST_USE_CANDIDATE, - RESULT_UNIT_ERR, -]); - -impl<'tcx> LateLintPass<'tcx> for Functions { - fn check_fn( - &mut self, - cx: &LateContext<'tcx>, - kind: intravisit::FnKind<'tcx>, - decl: &'tcx hir::FnDecl<'_>, - body: &'tcx hir::Body<'_>, - span: Span, - hir_id: hir::HirId, - ) { - let unsafety = match kind { - intravisit::FnKind::ItemFn(_, _, hir::FnHeader { unsafety, .. }, _, _) => unsafety, - intravisit::FnKind::Method(_, sig, _, _) => sig.header.unsafety, - intravisit::FnKind::Closure(_) => return, - }; - - // don't warn for implementations, it's not their fault - if !is_trait_impl_item(cx, hir_id) { - // don't lint extern functions decls, it's not their fault either - match kind { - intravisit::FnKind::Method( - _, - &hir::FnSig { - header: hir::FnHeader { abi: Abi::Rust, .. }, - .. - }, - _, - _, - ) - | intravisit::FnKind::ItemFn(_, _, hir::FnHeader { abi: Abi::Rust, .. }, _, _) => { - self.check_arg_number(cx, decl, span.with_hi(decl.output.span().hi())) - }, - _ => {}, - } - } - - Self::check_raw_ptr(cx, unsafety, decl, body, hir_id); - self.check_line_number(cx, span, body); - } - - fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) { - let attr = must_use_attr(&item.attrs); - if let hir::ItemKind::Fn(ref sig, ref _generics, ref body_id) = item.kind { - let is_public = cx.access_levels.is_exported(item.hir_id()); - let fn_header_span = item.span.with_hi(sig.decl.output.span().hi()); - if is_public { - check_result_unit_err(cx, &sig.decl, item.span, fn_header_span); - } - if let Some(attr) = attr { - check_needless_must_use(cx, &sig.decl, item.hir_id(), item.span, fn_header_span, attr); - return; - } - if is_public && !is_proc_macro(cx.sess(), &item.attrs) && attr_by_name(&item.attrs, "no_mangle").is_none() { - check_must_use_candidate( - cx, - &sig.decl, - cx.tcx.hir().body(*body_id), - item.span, - item.hir_id(), - item.span.with_hi(sig.decl.output.span().hi()), - "this function could have a `#[must_use]` attribute", - ); - } - } - } - - fn check_impl_item(&mut self, 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.hir_id()); - let fn_header_span = item.span.with_hi(sig.decl.output.span().hi()); - if is_public && trait_ref_of_method(cx, item.hir_id()).is_none() { - check_result_unit_err(cx, &sig.decl, item.span, fn_header_span); - } - let attr = must_use_attr(&item.attrs); - if let Some(attr) = attr { - check_needless_must_use(cx, &sig.decl, item.hir_id(), item.span, fn_header_span, attr); - } else if is_public - && !is_proc_macro(cx.sess(), &item.attrs) - && trait_ref_of_method(cx, item.hir_id()).is_none() - { - check_must_use_candidate( - cx, - &sig.decl, - cx.tcx.hir().body(*body_id), - item.span, - item.hir_id(), - item.span.with_hi(sig.decl.output.span().hi()), - "this method could have a `#[must_use]` attribute", - ); - } - } - } - - fn check_trait_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::TraitItem<'_>) { - if let hir::TraitItemKind::Fn(ref sig, ref eid) = item.kind { - // don't lint extern functions decls, it's not their fault - if sig.header.abi == Abi::Rust { - self.check_arg_number(cx, &sig.decl, item.span.with_hi(sig.decl.output.span().hi())); - } - let is_public = cx.access_levels.is_exported(item.hir_id()); - let fn_header_span = item.span.with_hi(sig.decl.output.span().hi()); - if is_public { - check_result_unit_err(cx, &sig.decl, item.span, fn_header_span); - } - - let attr = must_use_attr(&item.attrs); - if let Some(attr) = attr { - check_needless_must_use(cx, &sig.decl, item.hir_id(), item.span, fn_header_span, attr); - } - if let hir::TraitFn::Provided(eid) = *eid { - let body = cx.tcx.hir().body(eid); - Self::check_raw_ptr(cx, sig.header.unsafety, &sig.decl, body, item.hir_id()); - - if attr.is_none() && is_public && !is_proc_macro(cx.sess(), &item.attrs) { - check_must_use_candidate( - cx, - &sig.decl, - body, - item.span, - item.hir_id(), - item.span.with_hi(sig.decl.output.span().hi()), - "this method could have a `#[must_use]` attribute", - ); - } - } - } - } -} - -impl<'tcx> Functions { - fn check_arg_number(self, cx: &LateContext<'_>, decl: &hir::FnDecl<'_>, fn_span: Span) { - let args = decl.inputs.len() as u64; - if args > self.threshold { - span_lint( - cx, - TOO_MANY_ARGUMENTS, - fn_span, - &format!("this function has too many arguments ({}/{})", args, self.threshold), - ); - } - } - - fn check_line_number(self, cx: &LateContext<'_>, span: Span, body: &'tcx hir::Body<'_>) { - if in_external_macro(cx.sess(), span) { - return; - } - - let code_snippet = snippet(cx, body.value.span, ".."); - let mut line_count: u64 = 0; - let mut in_comment = false; - let mut code_in_line; - - // Skip the surrounding function decl. - let start_brace_idx = code_snippet.find('{').map_or(0, |i| i + 1); - let end_brace_idx = code_snippet.rfind('}').unwrap_or_else(|| code_snippet.len()); - let function_lines = code_snippet[start_brace_idx..end_brace_idx].lines(); - - for mut line in function_lines { - code_in_line = false; - loop { - line = line.trim_start(); - if line.is_empty() { - break; - } - if in_comment { - if let Some(i) = line.find("*/") { - line = &line[i + 2..]; - in_comment = false; - continue; - } - } else { - let multi_idx = line.find("/*").unwrap_or_else(|| line.len()); - let single_idx = line.find("//").unwrap_or_else(|| line.len()); - code_in_line |= multi_idx > 0 && single_idx > 0; - // Implies multi_idx is below line.len() - if multi_idx < single_idx { - line = &line[multi_idx + 2..]; - in_comment = true; - continue; - } - } - break; - } - if code_in_line { - line_count += 1; - } - } - - if line_count > self.max_lines { - span_lint( - cx, - TOO_MANY_LINES, - span, - &format!("this function has too many lines ({}/{})", line_count, self.max_lines), - ) - } - } - - fn check_raw_ptr( - cx: &LateContext<'tcx>, - unsafety: hir::Unsafety, - decl: &'tcx hir::FnDecl<'_>, - body: &'tcx hir::Body<'_>, - hir_id: hir::HirId, - ) { - let expr = &body.value; - if unsafety == hir::Unsafety::Normal && cx.access_levels.is_exported(hir_id) { - let raw_ptrs = iter_input_pats(decl, body) - .zip(decl.inputs.iter()) - .filter_map(|(arg, ty)| raw_ptr_arg(arg, ty)) - .collect::<FxHashSet<_>>(); - - if !raw_ptrs.is_empty() { - let typeck_results = cx.tcx.typeck_body(body.id()); - let mut v = DerefVisitor { - cx, - ptrs: raw_ptrs, - typeck_results, - }; - - intravisit::walk_expr(&mut v, expr); - } - } - } -} - -fn check_result_unit_err(cx: &LateContext<'_>, decl: &hir::FnDecl<'_>, item_span: Span, fn_header_span: Span) { - if_chain! { - if !in_external_macro(cx.sess(), item_span); - if let hir::FnRetTy::Return(ref ty) = decl.output; - let ty = hir_ty_to_ty(cx.tcx, ty); - if is_type_diagnostic_item(cx, ty, sym::result_type); - if let ty::Adt(_, substs) = ty.kind(); - let err_ty = substs.type_at(1); - if err_ty.is_unit(); - then { - span_lint_and_help( - cx, - RESULT_UNIT_ERR, - fn_header_span, - "this returns a `Result<_, ()>", - None, - "use a custom Error type instead", - ); - } - } -} - -fn check_needless_must_use( - cx: &LateContext<'_>, - decl: &hir::FnDecl<'_>, - item_id: hir::HirId, - item_span: Span, - fn_header_span: Span, - attr: &Attribute, -) { - if in_external_macro(cx.sess(), item_span) { - return; - } - if returns_unit(decl) { - span_lint_and_then( - cx, - MUST_USE_UNIT, - fn_header_span, - "this unit-returning function has a `#[must_use]` attribute", - |diag| { - diag.span_suggestion( - attr.span, - "remove the attribute", - "".into(), - Applicability::MachineApplicable, - ); - }, - ); - } else if !attr.is_value_str() && is_must_use_ty(cx, return_ty(cx, item_id)) { - span_lint_and_help( - cx, - DOUBLE_MUST_USE, - fn_header_span, - "this function has an empty `#[must_use]` attribute, but returns a type already marked as `#[must_use]`", - None, - "either add some descriptive text or remove the attribute", - ); - } -} - -fn check_must_use_candidate<'tcx>( - cx: &LateContext<'tcx>, - decl: &'tcx hir::FnDecl<'_>, - body: &'tcx hir::Body<'_>, - item_span: Span, - item_id: hir::HirId, - fn_span: Span, - msg: &str, -) { - if has_mutable_arg(cx, body) - || mutates_static(cx, body) - || in_external_macro(cx.sess(), item_span) - || returns_unit(decl) - || !cx.access_levels.is_exported(item_id) - || is_must_use_ty(cx, return_ty(cx, item_id)) - { - return; - } - span_lint_and_then(cx, MUST_USE_CANDIDATE, fn_span, msg, |diag| { - if let Some(snippet) = snippet_opt(cx, fn_span) { - diag.span_suggestion( - fn_span, - "add the attribute", - format!("#[must_use] {}", snippet), - Applicability::MachineApplicable, - ); - } - }); -} - -fn returns_unit(decl: &hir::FnDecl<'_>) -> bool { - match decl.output { - hir::FnRetTy::DefaultReturn(_) => true, - hir::FnRetTy::Return(ref ty) => match ty.kind { - hir::TyKind::Tup(ref tys) => tys.is_empty(), - hir::TyKind::Never => true, - _ => false, - }, - } -} - -fn has_mutable_arg(cx: &LateContext<'_>, body: &hir::Body<'_>) -> bool { - let mut tys = FxHashSet::default(); - body.params.iter().any(|param| is_mutable_pat(cx, ¶m.pat, &mut tys)) -} - -fn is_mutable_pat(cx: &LateContext<'_>, pat: &hir::Pat<'_>, tys: &mut FxHashSet<DefId>) -> bool { - if let hir::PatKind::Wild = pat.kind { - return false; // ignore `_` patterns - } - if cx.tcx.has_typeck_results(pat.hir_id.owner.to_def_id()) { - is_mutable_ty(cx, &cx.tcx.typeck(pat.hir_id.owner).pat_ty(pat), pat.span, tys) - } else { - false - } -} - -static KNOWN_WRAPPER_TYS: &[&[&str]] = &[&["alloc", "rc", "Rc"], &["std", "sync", "Arc"]]; - -fn is_mutable_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, span: Span, tys: &mut FxHashSet<DefId>) -> bool { - match *ty.kind() { - // primitive types are never mutable - ty::Bool | ty::Char | ty::Int(_) | ty::Uint(_) | ty::Float(_) | ty::Str => false, - ty::Adt(ref adt, ref substs) => { - tys.insert(adt.did) && !ty.is_freeze(cx.tcx.at(span), cx.param_env) - || KNOWN_WRAPPER_TYS.iter().any(|path| match_def_path(cx, adt.did, path)) - && substs.types().any(|ty| is_mutable_ty(cx, ty, span, tys)) - }, - ty::Tuple(ref substs) => substs.types().any(|ty| is_mutable_ty(cx, ty, span, tys)), - ty::Array(ty, _) | ty::Slice(ty) => is_mutable_ty(cx, ty, span, tys), - ty::RawPtr(ty::TypeAndMut { ty, mutbl }) | ty::Ref(_, ty, mutbl) => { - mutbl == hir::Mutability::Mut || is_mutable_ty(cx, ty, span, tys) - }, - // calling something constitutes a side effect, so return true on all callables - // also never calls need not be used, so return true for them, too - _ => true, - } -} - -fn raw_ptr_arg(arg: &hir::Param<'_>, ty: &hir::Ty<'_>) -> Option<hir::HirId> { - if let (&hir::PatKind::Binding(_, id, _, _), &hir::TyKind::Ptr(_)) = (&arg.pat.kind, &ty.kind) { - Some(id) - } else { - None - } -} - -struct DerefVisitor<'a, 'tcx> { - cx: &'a LateContext<'tcx>, - ptrs: FxHashSet<hir::HirId>, - typeck_results: &'a ty::TypeckResults<'tcx>, -} - -impl<'a, 'tcx> intravisit::Visitor<'tcx> for DerefVisitor<'a, 'tcx> { - type Map = Map<'tcx>; - - fn visit_expr(&mut self, expr: &'tcx hir::Expr<'_>) { - match expr.kind { - hir::ExprKind::Call(ref f, args) => { - let ty = self.typeck_results.expr_ty(f); - - if type_is_unsafe_function(self.cx, ty) { - for arg in args { - self.check_arg(arg); - } - } - }, - hir::ExprKind::MethodCall(_, _, args, _) => { - let def_id = self.typeck_results.type_dependent_def_id(expr.hir_id).unwrap(); - let base_type = self.cx.tcx.type_of(def_id); - - if type_is_unsafe_function(self.cx, base_type) { - for arg in args { - self.check_arg(arg); - } - } - }, - hir::ExprKind::Unary(hir::UnOp::Deref, ref ptr) => self.check_arg(ptr), - _ => (), - } - - intravisit::walk_expr(self, expr); - } - - fn nested_visit_map(&mut self) -> intravisit::NestedVisitorMap<Self::Map> { - intravisit::NestedVisitorMap::None - } -} - -impl<'a, 'tcx> DerefVisitor<'a, 'tcx> { - fn check_arg(&self, ptr: &hir::Expr<'_>) { - if let Some(id) = path_to_local(ptr) { - if self.ptrs.contains(&id) { - span_lint( - self.cx, - NOT_UNSAFE_PTR_ARG_DEREF, - ptr.span, - "this public function dereferences a raw pointer but is not marked `unsafe`", - ); - } - } - } -} - -struct StaticMutVisitor<'a, 'tcx> { - cx: &'a LateContext<'tcx>, - mutates_static: bool, -} - -impl<'a, 'tcx> intravisit::Visitor<'tcx> for StaticMutVisitor<'a, 'tcx> { - type Map = Map<'tcx>; - - fn visit_expr(&mut self, expr: &'tcx hir::Expr<'_>) { - use hir::ExprKind::{AddrOf, Assign, AssignOp, Call, MethodCall}; - - if self.mutates_static { - return; - } - match expr.kind { - Call(_, args) | MethodCall(_, _, args, _) => { - let mut tys = FxHashSet::default(); - for arg in args { - if self.cx.tcx.has_typeck_results(arg.hir_id.owner.to_def_id()) - && is_mutable_ty( - self.cx, - self.cx.tcx.typeck(arg.hir_id.owner).expr_ty(arg), - arg.span, - &mut tys, - ) - && is_mutated_static(arg) - { - self.mutates_static = true; - return; - } - tys.clear(); - } - }, - Assign(ref target, ..) | AssignOp(_, ref target, _) | AddrOf(_, hir::Mutability::Mut, ref target) => { - self.mutates_static |= is_mutated_static(target) - }, - _ => {}, - } - } - - fn nested_visit_map(&mut self) -> intravisit::NestedVisitorMap<Self::Map> { - intravisit::NestedVisitorMap::None - } -} - -fn is_mutated_static(e: &hir::Expr<'_>) -> bool { - use hir::ExprKind::{Field, Index, Path}; - - match e.kind { - Path(QPath::Resolved(_, path)) => !matches!(path.res, Res::Local(_)), - Path(_) => true, - Field(ref inner, _) | Index(ref inner, _) => is_mutated_static(inner), - _ => false, - } -} - -fn mutates_static<'tcx>(cx: &LateContext<'tcx>, body: &'tcx hir::Body<'_>) -> bool { - let mut v = StaticMutVisitor { - cx, - mutates_static: false, - }; - intravisit::walk_expr(&mut v, &body.value); - v.mutates_static -} diff --git a/src/tools/clippy/clippy_lints/src/functions/mod.rs b/src/tools/clippy/clippy_lints/src/functions/mod.rs new file mode 100644 index 0000000000000..2beb9bc94bf06 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/functions/mod.rs @@ -0,0 +1,267 @@ +mod must_use; +mod not_unsafe_ptr_arg_deref; +mod result_unit_err; +mod too_many_arguments; +mod too_many_lines; + +use rustc_hir as hir; +use rustc_hir::intravisit; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_session::{declare_tool_lint, impl_lint_pass}; +use rustc_span::Span; + +declare_clippy_lint! { + /// **What it does:** Checks for functions with too many parameters. + /// + /// **Why is this bad?** Functions with lots of parameters are considered bad + /// style and reduce readability (“what does the 5th parameter mean?”). Consider + /// grouping some parameters into a new type. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// ```rust + /// # struct Color; + /// fn foo(x: u32, y: u32, name: &str, c: Color, w: f32, h: f32, a: f32, b: f32) { + /// // .. + /// } + /// ``` + pub TOO_MANY_ARGUMENTS, + complexity, + "functions with too many arguments" +} + +declare_clippy_lint! { + /// **What it does:** Checks for functions with a large amount of lines. + /// + /// **Why is this bad?** Functions with a lot of lines are harder to understand + /// due to having to look at a larger amount of code to understand what the + /// function is doing. Consider splitting the body of the function into + /// multiple functions. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// ```rust + /// fn im_too_long() { + /// println!(""); + /// // ... 100 more LoC + /// println!(""); + /// } + /// ``` + pub TOO_MANY_LINES, + pedantic, + "functions with too many lines" +} + +declare_clippy_lint! { + /// **What it does:** Checks for public functions that dereference raw pointer + /// arguments but are not marked `unsafe`. + /// + /// **Why is this bad?** The function should probably be marked `unsafe`, since + /// for an arbitrary raw pointer, there is no way of telling for sure if it is + /// valid. + /// + /// **Known problems:** + /// + /// * It does not check functions recursively so if the pointer is passed to a + /// private non-`unsafe` function which does the dereferencing, the lint won't + /// trigger. + /// * It only checks for arguments whose type are raw pointers, not raw pointers + /// got from an argument in some other way (`fn foo(bar: &[*const u8])` or + /// `some_argument.get_raw_ptr()`). + /// + /// **Example:** + /// ```rust,ignore + /// // Bad + /// pub fn foo(x: *const u8) { + /// println!("{}", unsafe { *x }); + /// } + /// + /// // Good + /// pub unsafe fn foo(x: *const u8) { + /// println!("{}", unsafe { *x }); + /// } + /// ``` + pub NOT_UNSAFE_PTR_ARG_DEREF, + correctness, + "public functions dereferencing raw pointer arguments but not marked `unsafe`" +} + +declare_clippy_lint! { + /// **What it does:** Checks for a [`#[must_use]`] attribute on + /// unit-returning functions and methods. + /// + /// [`#[must_use]`]: https://doc.rust-lang.org/reference/attributes/diagnostics.html#the-must_use-attribute + /// + /// **Why is this bad?** Unit values are useless. The attribute is likely + /// a remnant of a refactoring that removed the return type. + /// + /// **Known problems:** None. + /// + /// **Examples:** + /// ```rust + /// #[must_use] + /// fn useless() { } + /// ``` + pub MUST_USE_UNIT, + style, + "`#[must_use]` attribute on a unit-returning function / method" +} + +declare_clippy_lint! { + /// **What it does:** Checks for a [`#[must_use]`] attribute without + /// further information on functions and methods that return a type already + /// marked as `#[must_use]`. + /// + /// [`#[must_use]`]: https://doc.rust-lang.org/reference/attributes/diagnostics.html#the-must_use-attribute + /// + /// **Why is this bad?** The attribute isn't needed. Not using the result + /// will already be reported. Alternatively, one can add some text to the + /// attribute to improve the lint message. + /// + /// **Known problems:** None. + /// + /// **Examples:** + /// ```rust + /// #[must_use] + /// fn double_must_use() -> Result<(), ()> { + /// unimplemented!(); + /// } + /// ``` + pub DOUBLE_MUST_USE, + style, + "`#[must_use]` attribute on a `#[must_use]`-returning function / method" +} + +declare_clippy_lint! { + /// **What it does:** Checks for public functions that have no + /// [`#[must_use]`] attribute, but return something not already marked + /// must-use, have no mutable arg and mutate no statics. + /// + /// [`#[must_use]`]: https://doc.rust-lang.org/reference/attributes/diagnostics.html#the-must_use-attribute + /// + /// **Why is this bad?** Not bad at all, this lint just shows places where + /// you could add the attribute. + /// + /// **Known problems:** The lint only checks the arguments for mutable + /// types without looking if they are actually changed. On the other hand, + /// it also ignores a broad range of potentially interesting side effects, + /// because we cannot decide whether the programmer intends the function to + /// be called for the side effect or the result. Expect many false + /// positives. At least we don't lint if the result type is unit or already + /// `#[must_use]`. + /// + /// **Examples:** + /// ```rust + /// // this could be annotated with `#[must_use]`. + /// fn id<T>(t: T) -> T { t } + /// ``` + pub MUST_USE_CANDIDATE, + pedantic, + "function or method that could take a `#[must_use]` attribute" +} + +declare_clippy_lint! { + /// **What it does:** Checks for public functions that return a `Result` + /// with an `Err` type of `()`. It suggests using a custom type that + /// implements `std::error::Error`. + /// + /// **Why is this bad?** Unit does not implement `Error` and carries no + /// further information about what went wrong. + /// + /// **Known problems:** Of course, this lint assumes that `Result` is used + /// for a fallible operation (which is after all the intended use). However + /// code may opt to (mis)use it as a basic two-variant-enum. In that case, + /// the suggestion is misguided, and the code should use a custom enum + /// instead. + /// + /// **Examples:** + /// ```rust + /// pub fn read_u8() -> Result<u8, ()> { Err(()) } + /// ``` + /// should become + /// ```rust,should_panic + /// use std::fmt; + /// + /// #[derive(Debug)] + /// pub struct EndOfStream; + /// + /// impl fmt::Display for EndOfStream { + /// fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + /// write!(f, "End of Stream") + /// } + /// } + /// + /// impl std::error::Error for EndOfStream { } + /// + /// pub fn read_u8() -> Result<u8, EndOfStream> { Err(EndOfStream) } + ///# fn main() { + ///# read_u8().unwrap(); + ///# } + /// ``` + /// + /// Note that there are crates that simplify creating the error type, e.g. + /// [`thiserror`](https://docs.rs/thiserror). + pub RESULT_UNIT_ERR, + style, + "public function returning `Result` with an `Err` type of `()`" +} + +#[derive(Copy, Clone)] +pub struct Functions { + too_many_arguments_threshold: u64, + too_many_lines_threshold: u64, +} + +impl Functions { + pub fn new(too_many_arguments_threshold: u64, too_many_lines_threshold: u64) -> Self { + Self { + too_many_arguments_threshold, + too_many_lines_threshold, + } + } +} + +impl_lint_pass!(Functions => [ + TOO_MANY_ARGUMENTS, + TOO_MANY_LINES, + NOT_UNSAFE_PTR_ARG_DEREF, + MUST_USE_UNIT, + DOUBLE_MUST_USE, + MUST_USE_CANDIDATE, + RESULT_UNIT_ERR, +]); + +impl<'tcx> LateLintPass<'tcx> for Functions { + fn check_fn( + &mut self, + cx: &LateContext<'tcx>, + kind: intravisit::FnKind<'tcx>, + decl: &'tcx hir::FnDecl<'_>, + body: &'tcx hir::Body<'_>, + span: Span, + hir_id: hir::HirId, + ) { + too_many_arguments::check_fn(cx, kind, decl, span, hir_id, self.too_many_arguments_threshold); + too_many_lines::check_fn(cx, span, body, self.too_many_lines_threshold); + not_unsafe_ptr_arg_deref::check_fn(cx, kind, decl, body, hir_id); + } + + fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) { + must_use::check_item(cx, item); + result_unit_err::check_item(cx, item); + } + + fn check_impl_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::ImplItem<'_>) { + must_use::check_impl_item(cx, item); + result_unit_err::check_impl_item(cx, item); + } + + fn check_trait_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::TraitItem<'_>) { + too_many_arguments::check_trait_item(cx, item, self.too_many_arguments_threshold); + not_unsafe_ptr_arg_deref::check_trait_item(cx, item); + must_use::check_trait_item(cx, item); + result_unit_err::check_trait_item(cx, item); + } +} diff --git a/src/tools/clippy/clippy_lints/src/functions/must_use.rs b/src/tools/clippy/clippy_lints/src/functions/must_use.rs new file mode 100644 index 0000000000000..20288427b4a74 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/functions/must_use.rs @@ -0,0 +1,272 @@ +use rustc_ast::ast::Attribute; +use rustc_errors::Applicability; +use rustc_hir::def_id::DefIdSet; +use rustc_hir::{self as hir, def::Res, intravisit, QPath}; +use rustc_lint::{LateContext, LintContext}; +use rustc_middle::{ + hir::map::Map, + lint::in_external_macro, + ty::{self, Ty}, +}; +use rustc_span::{sym, Span}; + +use clippy_utils::attrs::is_proc_macro; +use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_then}; +use clippy_utils::source::snippet_opt; +use clippy_utils::ty::is_must_use_ty; +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<'_>) { + 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 { + let is_public = cx.access_levels.is_exported(item.hir_id()); + let fn_header_span = item.span.with_hi(sig.decl.output.span().hi()); + if let Some(attr) = attr { + check_needless_must_use(cx, sig.decl, item.hir_id(), item.span, fn_header_span, attr); + return; + } else if is_public && !is_proc_macro(cx.sess(), attrs) && !attrs.iter().any(|a| a.has_name(sym::no_mangle)) { + check_must_use_candidate( + cx, + sig.decl, + cx.tcx.hir().body(*body_id), + item.span, + item.hir_id(), + item.span.with_hi(sig.decl.output.span().hi()), + "this function could have a `#[must_use]` attribute", + ); + } + } +} + +pub(super) fn check_impl_item(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.hir_id()); + let fn_header_span = item.span.with_hi(sig.decl.output.span().hi()); + let attrs = cx.tcx.hir().attrs(item.hir_id()); + let attr = must_use_attr(attrs); + if let Some(attr) = attr { + check_needless_must_use(cx, sig.decl, item.hir_id(), item.span, fn_header_span, attr); + } else if is_public && !is_proc_macro(cx.sess(), attrs) && trait_ref_of_method(cx, item.hir_id()).is_none() { + check_must_use_candidate( + cx, + sig.decl, + cx.tcx.hir().body(*body_id), + item.span, + item.hir_id(), + item.span.with_hi(sig.decl.output.span().hi()), + "this method could have a `#[must_use]` attribute", + ); + } + } +} + +pub(super) fn check_trait_item(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.hir_id()); + let fn_header_span = item.span.with_hi(sig.decl.output.span().hi()); + + let attrs = cx.tcx.hir().attrs(item.hir_id()); + let attr = must_use_attr(attrs); + if let Some(attr) = attr { + check_needless_must_use(cx, sig.decl, item.hir_id(), item.span, fn_header_span, attr); + } else if let hir::TraitFn::Provided(eid) = *eid { + let body = cx.tcx.hir().body(eid); + if attr.is_none() && is_public && !is_proc_macro(cx.sess(), attrs) { + check_must_use_candidate( + cx, + sig.decl, + body, + item.span, + item.hir_id(), + item.span.with_hi(sig.decl.output.span().hi()), + "this method could have a `#[must_use]` attribute", + ); + } + } + } +} + +fn check_needless_must_use( + cx: &LateContext<'_>, + decl: &hir::FnDecl<'_>, + item_id: hir::HirId, + item_span: Span, + fn_header_span: Span, + attr: &Attribute, +) { + if in_external_macro(cx.sess(), item_span) { + return; + } + if returns_unit(decl) { + span_lint_and_then( + cx, + MUST_USE_UNIT, + fn_header_span, + "this unit-returning function has a `#[must_use]` attribute", + |diag| { + diag.span_suggestion( + attr.span, + "remove the attribute", + "".into(), + Applicability::MachineApplicable, + ); + }, + ); + } else if attr.value_str().is_none() && is_must_use_ty(cx, return_ty(cx, item_id)) { + span_lint_and_help( + cx, + DOUBLE_MUST_USE, + fn_header_span, + "this function has an empty `#[must_use]` attribute, but returns a type already marked as `#[must_use]`", + None, + "either add some descriptive text or remove the attribute", + ); + } +} + +fn check_must_use_candidate<'tcx>( + cx: &LateContext<'tcx>, + decl: &'tcx hir::FnDecl<'_>, + body: &'tcx hir::Body<'_>, + item_span: Span, + item_id: hir::HirId, + fn_span: Span, + msg: &str, +) { + if has_mutable_arg(cx, body) + || mutates_static(cx, body) + || in_external_macro(cx.sess(), item_span) + || returns_unit(decl) + || !cx.access_levels.is_exported(item_id) + || is_must_use_ty(cx, return_ty(cx, item_id)) + { + return; + } + span_lint_and_then(cx, MUST_USE_CANDIDATE, fn_span, msg, |diag| { + if let Some(snippet) = snippet_opt(cx, fn_span) { + diag.span_suggestion( + fn_span, + "add the attribute", + format!("#[must_use] {}", snippet), + Applicability::MachineApplicable, + ); + } + }); +} + +fn returns_unit(decl: &hir::FnDecl<'_>) -> bool { + match decl.output { + hir::FnRetTy::DefaultReturn(_) => true, + hir::FnRetTy::Return(ty) => match ty.kind { + hir::TyKind::Tup(tys) => tys.is_empty(), + hir::TyKind::Never => true, + _ => false, + }, + } +} + +fn has_mutable_arg(cx: &LateContext<'_>, body: &hir::Body<'_>) -> bool { + let mut tys = DefIdSet::default(); + body.params.iter().any(|param| is_mutable_pat(cx, param.pat, &mut tys)) +} + +fn is_mutable_pat(cx: &LateContext<'_>, pat: &hir::Pat<'_>, tys: &mut DefIdSet) -> bool { + if let hir::PatKind::Wild = pat.kind { + return false; // ignore `_` patterns + } + if cx.tcx.has_typeck_results(pat.hir_id.owner.to_def_id()) { + is_mutable_ty(cx, cx.tcx.typeck(pat.hir_id.owner).pat_ty(pat), pat.span, tys) + } else { + false + } +} + +static KNOWN_WRAPPER_TYS: &[&[&str]] = &[&["alloc", "rc", "Rc"], &["std", "sync", "Arc"]]; + +fn is_mutable_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, span: Span, tys: &mut DefIdSet) -> bool { + match *ty.kind() { + // primitive types are never mutable + ty::Bool | ty::Char | ty::Int(_) | ty::Uint(_) | ty::Float(_) | ty::Str => false, + ty::Adt(adt, substs) => { + tys.insert(adt.did) && !ty.is_freeze(cx.tcx.at(span), cx.param_env) + || KNOWN_WRAPPER_TYS.iter().any(|path| match_def_path(cx, adt.did, path)) + && substs.types().any(|ty| is_mutable_ty(cx, ty, span, tys)) + }, + ty::Tuple(substs) => substs.types().any(|ty| is_mutable_ty(cx, ty, span, tys)), + ty::Array(ty, _) | ty::Slice(ty) => is_mutable_ty(cx, ty, span, tys), + ty::RawPtr(ty::TypeAndMut { ty, mutbl }) | ty::Ref(_, ty, mutbl) => { + mutbl == hir::Mutability::Mut || is_mutable_ty(cx, ty, span, tys) + }, + // calling something constitutes a side effect, so return true on all callables + // also never calls need not be used, so return true for them, too + _ => true, + } +} + +struct StaticMutVisitor<'a, 'tcx> { + cx: &'a LateContext<'tcx>, + mutates_static: bool, +} + +impl<'a, 'tcx> intravisit::Visitor<'tcx> for StaticMutVisitor<'a, 'tcx> { + type Map = Map<'tcx>; + + fn visit_expr(&mut self, expr: &'tcx hir::Expr<'_>) { + use hir::ExprKind::{AddrOf, Assign, AssignOp, Call, MethodCall}; + + if self.mutates_static { + return; + } + match expr.kind { + Call(_, args) | MethodCall(_, _, args, _) => { + let mut tys = DefIdSet::default(); + for arg in args { + if self.cx.tcx.has_typeck_results(arg.hir_id.owner.to_def_id()) + && is_mutable_ty( + self.cx, + self.cx.tcx.typeck(arg.hir_id.owner).expr_ty(arg), + arg.span, + &mut tys, + ) + && is_mutated_static(arg) + { + self.mutates_static = true; + return; + } + tys.clear(); + } + }, + Assign(target, ..) | AssignOp(_, target, _) | AddrOf(_, hir::Mutability::Mut, target) => { + self.mutates_static |= is_mutated_static(target) + }, + _ => {}, + } + } + + fn nested_visit_map(&mut self) -> intravisit::NestedVisitorMap<Self::Map> { + intravisit::NestedVisitorMap::None + } +} + +fn is_mutated_static(e: &hir::Expr<'_>) -> bool { + use hir::ExprKind::{Field, Index, Path}; + + match e.kind { + Path(QPath::Resolved(_, path)) => !matches!(path.res, Res::Local(_)), + Path(_) => true, + Field(inner, _) | Index(inner, _) => is_mutated_static(inner), + _ => false, + } +} + +fn mutates_static<'tcx>(cx: &LateContext<'tcx>, body: &'tcx hir::Body<'_>) -> bool { + let mut v = StaticMutVisitor { + cx, + mutates_static: false, + }; + intravisit::walk_expr(&mut v, &body.value); + v.mutates_static +} 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 new file mode 100644 index 0000000000000..b8ea699086656 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/functions/not_unsafe_ptr_arg_deref.rs @@ -0,0 +1,124 @@ +use rustc_hir::{self as hir, intravisit, HirIdSet}; +use rustc_lint::LateContext; +use rustc_middle::{hir::map::Map, ty}; + +use clippy_utils::diagnostics::span_lint; +use clippy_utils::ty::type_is_unsafe_function; +use clippy_utils::{iter_input_pats, path_to_local}; + +use super::NOT_UNSAFE_PTR_ARG_DEREF; + +pub(super) fn check_fn( + cx: &LateContext<'tcx>, + kind: intravisit::FnKind<'tcx>, + decl: &'tcx hir::FnDecl<'tcx>, + body: &'tcx hir::Body<'tcx>, + hir_id: hir::HirId, +) { + let unsafety = match kind { + intravisit::FnKind::ItemFn(_, _, hir::FnHeader { unsafety, .. }, _) => unsafety, + intravisit::FnKind::Method(_, sig, _) => sig.header.unsafety, + intravisit::FnKind::Closure => return, + }; + + check_raw_ptr(cx, unsafety, decl, body, hir_id); +} + +pub(super) fn check_trait_item(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.hir_id()); + } +} + +fn check_raw_ptr( + cx: &LateContext<'tcx>, + unsafety: hir::Unsafety, + decl: &'tcx hir::FnDecl<'tcx>, + body: &'tcx hir::Body<'tcx>, + hir_id: hir::HirId, +) { + let expr = &body.value; + if unsafety == hir::Unsafety::Normal && cx.access_levels.is_exported(hir_id) { + let raw_ptrs = iter_input_pats(decl, body) + .zip(decl.inputs.iter()) + .filter_map(|(arg, ty)| raw_ptr_arg(arg, ty)) + .collect::<HirIdSet>(); + + if !raw_ptrs.is_empty() { + let typeck_results = cx.tcx.typeck_body(body.id()); + let mut v = DerefVisitor { + cx, + ptrs: raw_ptrs, + typeck_results, + }; + + intravisit::walk_expr(&mut v, expr); + } + } +} + +fn raw_ptr_arg(arg: &hir::Param<'_>, ty: &hir::Ty<'_>) -> Option<hir::HirId> { + if let (&hir::PatKind::Binding(_, id, _, _), &hir::TyKind::Ptr(_)) = (&arg.pat.kind, &ty.kind) { + Some(id) + } else { + None + } +} + +struct DerefVisitor<'a, 'tcx> { + cx: &'a LateContext<'tcx>, + ptrs: HirIdSet, + typeck_results: &'a ty::TypeckResults<'tcx>, +} + +impl<'a, 'tcx> intravisit::Visitor<'tcx> for DerefVisitor<'a, 'tcx> { + type Map = Map<'tcx>; + + fn visit_expr(&mut self, expr: &'tcx hir::Expr<'_>) { + match expr.kind { + hir::ExprKind::Call(f, args) => { + let ty = self.typeck_results.expr_ty(f); + + if type_is_unsafe_function(self.cx, ty) { + for arg in args { + self.check_arg(arg); + } + } + }, + hir::ExprKind::MethodCall(_, _, args, _) => { + let def_id = self.typeck_results.type_dependent_def_id(expr.hir_id).unwrap(); + let base_type = self.cx.tcx.type_of(def_id); + + if type_is_unsafe_function(self.cx, base_type) { + for arg in args { + self.check_arg(arg); + } + } + }, + hir::ExprKind::Unary(hir::UnOp::Deref, ptr) => self.check_arg(ptr), + _ => (), + } + + intravisit::walk_expr(self, expr); + } + + fn nested_visit_map(&mut self) -> intravisit::NestedVisitorMap<Self::Map> { + intravisit::NestedVisitorMap::None + } +} + +impl<'a, 'tcx> DerefVisitor<'a, 'tcx> { + fn check_arg(&self, ptr: &hir::Expr<'_>) { + if let Some(id) = path_to_local(ptr) { + if self.ptrs.contains(&id) { + span_lint( + self.cx, + NOT_UNSAFE_PTR_ARG_DEREF, + ptr.span, + "this public function dereferences a raw pointer but is not marked `unsafe`", + ); + } + } + } +} 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 new file mode 100644 index 0000000000000..c073f312d386f --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/functions/result_unit_err.rs @@ -0,0 +1,66 @@ +use rustc_hir as hir; +use rustc_lint::{LateContext, LintContext}; +use rustc_middle::lint::in_external_macro; +use rustc_middle::ty; +use rustc_span::{sym, Span}; +use rustc_typeck::hir_ty_to_ty; + +use if_chain::if_chain; + +use clippy_utils::diagnostics::span_lint_and_help; +use clippy_utils::trait_ref_of_method; +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<'_>) { + if let hir::ItemKind::Fn(ref sig, ref _generics, _) = item.kind { + let is_public = cx.access_levels.is_exported(item.hir_id()); + let fn_header_span = item.span.with_hi(sig.decl.output.span().hi()); + if is_public { + check_result_unit_err(cx, sig.decl, item.span, fn_header_span); + } + } +} + +pub(super) fn check_impl_item(cx: &LateContext<'tcx>, item: &'tcx hir::ImplItem<'_>) { + if let hir::ImplItemKind::Fn(ref sig, _) = item.kind { + let is_public = cx.access_levels.is_exported(item.hir_id()); + let fn_header_span = item.span.with_hi(sig.decl.output.span().hi()); + if is_public && trait_ref_of_method(cx, item.hir_id()).is_none() { + check_result_unit_err(cx, sig.decl, item.span, fn_header_span); + } + } +} + +pub(super) fn check_trait_item(cx: &LateContext<'tcx>, item: &'tcx hir::TraitItem<'_>) { + if let hir::TraitItemKind::Fn(ref sig, _) = item.kind { + let is_public = cx.access_levels.is_exported(item.hir_id()); + let fn_header_span = item.span.with_hi(sig.decl.output.span().hi()); + if is_public { + check_result_unit_err(cx, sig.decl, item.span, fn_header_span); + } + } +} + +fn check_result_unit_err(cx: &LateContext<'_>, decl: &hir::FnDecl<'_>, item_span: Span, fn_header_span: Span) { + if_chain! { + if !in_external_macro(cx.sess(), item_span); + if let hir::FnRetTy::Return(ty) = decl.output; + let ty = hir_ty_to_ty(cx.tcx, ty); + if is_type_diagnostic_item(cx, ty, sym::result_type); + if let ty::Adt(_, substs) = ty.kind(); + let err_ty = substs.type_at(1); + if err_ty.is_unit(); + then { + span_lint_and_help( + cx, + RESULT_UNIT_ERR, + fn_header_span, + "this returns a `Result<_, ()>`", + None, + "use a custom `Error` type instead", + ); + } + } +} 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 new file mode 100644 index 0000000000000..63a14d8d4cde8 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/functions/too_many_arguments.rs @@ -0,0 +1,73 @@ +use rustc_hir::{self as hir, intravisit}; +use rustc_lint::LateContext; +use rustc_span::Span; +use rustc_target::spec::abi::Abi; + +use clippy_utils::diagnostics::span_lint; +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<'_>, + span: Span, + hir_id: hir::HirId, + too_many_arguments_threshold: u64, +) { + // don't warn for implementations, it's not their fault + if !is_trait_impl_item(cx, hir_id) { + // don't lint extern functions decls, it's not their fault either + match kind { + intravisit::FnKind::Method( + _, + &hir::FnSig { + header: hir::FnHeader { abi: Abi::Rust, .. }, + .. + }, + _, + ) + | intravisit::FnKind::ItemFn(_, _, hir::FnHeader { abi: Abi::Rust, .. }, _) => check_arg_number( + cx, + decl, + span.with_hi(decl.output.span().hi()), + too_many_arguments_threshold, + ), + _ => {}, + } + } +} + +pub(super) fn check_trait_item( + cx: &LateContext<'tcx>, + item: &'tcx 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 { + check_arg_number( + cx, + sig.decl, + item.span.with_hi(sig.decl.output.span().hi()), + too_many_arguments_threshold, + ); + } + } +} + +fn check_arg_number(cx: &LateContext<'_>, decl: &hir::FnDecl<'_>, fn_span: Span, too_many_arguments_threshold: u64) { + let args = decl.inputs.len() as u64; + if args > too_many_arguments_threshold { + span_lint( + cx, + TOO_MANY_ARGUMENTS, + fn_span, + &format!( + "this function has too many arguments ({}/{})", + args, too_many_arguments_threshold + ), + ); + } +} diff --git a/src/tools/clippy/clippy_lints/src/functions/too_many_lines.rs b/src/tools/clippy/clippy_lints/src/functions/too_many_lines.rs new file mode 100644 index 0000000000000..aa5494d5a7d2c --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/functions/too_many_lines.rs @@ -0,0 +1,68 @@ +use rustc_hir as hir; +use rustc_lint::{LateContext, LintContext}; +use rustc_middle::lint::in_external_macro; +use rustc_span::Span; + +use clippy_utils::diagnostics::span_lint; +use clippy_utils::source::snippet; + +use super::TOO_MANY_LINES; + +pub(super) fn check_fn(cx: &LateContext<'_>, span: Span, body: &'tcx hir::Body<'_>, too_many_lines_threshold: u64) { + if in_external_macro(cx.sess(), span) { + return; + } + + let code_snippet = snippet(cx, body.value.span, ".."); + let mut line_count: u64 = 0; + let mut in_comment = false; + let mut code_in_line; + + // Skip the surrounding function decl. + let start_brace_idx = code_snippet.find('{').map_or(0, |i| i + 1); + let end_brace_idx = code_snippet.rfind('}').unwrap_or_else(|| code_snippet.len()); + let function_lines = code_snippet[start_brace_idx..end_brace_idx].lines(); + + for mut line in function_lines { + code_in_line = false; + loop { + line = line.trim_start(); + if line.is_empty() { + break; + } + if in_comment { + if let Some(i) = line.find("*/") { + line = &line[i + 2..]; + in_comment = false; + continue; + } + } else { + let multi_idx = line.find("/*").unwrap_or_else(|| line.len()); + let single_idx = line.find("//").unwrap_or_else(|| line.len()); + code_in_line |= multi_idx > 0 && single_idx > 0; + // Implies multi_idx is below line.len() + if multi_idx < single_idx { + line = &line[multi_idx + 2..]; + in_comment = true; + continue; + } + } + break; + } + if code_in_line { + line_count += 1; + } + } + + if line_count > too_many_lines_threshold { + span_lint( + cx, + TOO_MANY_LINES, + span, + &format!( + "this function has too many lines ({}/{})", + line_count, too_many_lines_threshold + ), + ) + } +} diff --git a/src/tools/clippy/clippy_lints/src/future_not_send.rs b/src/tools/clippy/clippy_lints/src/future_not_send.rs index 7208e66ff7be1..04730ace887c9 100644 --- a/src/tools/clippy/clippy_lints/src/future_not_send.rs +++ b/src/tools/clippy/clippy_lints/src/future_not_send.rs @@ -1,4 +1,5 @@ -use crate::utils; +use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::return_ty; use rustc_hir::intravisit::FnKind; use rustc_hir::{Body, FnDecl, HirId}; use rustc_infer::infer::TyCtxtInferExt; @@ -58,10 +59,10 @@ impl<'tcx> LateLintPass<'tcx> for FutureNotSend { _: Span, hir_id: HirId, ) { - if let FnKind::Closure(_) = kind { + if let FnKind::Closure = kind { return; } - let ret_ty = utils::return_ty(cx, hir_id); + let ret_ty = return_ty(cx, hir_id); if let Opaque(id, subst) = *ret_ty.kind() { let preds = cx.tcx.explicit_item_bounds(id); let mut is_future = false; @@ -84,7 +85,7 @@ impl<'tcx> LateLintPass<'tcx> for FutureNotSend { fulfillment_cx.select_all_or_error(&infcx) }); if let Err(send_errors) = send_result { - utils::span_lint_and_then( + span_lint_and_then( cx, FUTURE_NOT_SEND, span, diff --git a/src/tools/clippy/clippy_lints/src/get_last_with_len.rs b/src/tools/clippy/clippy_lints/src/get_last_with_len.rs index cdd8a42e7cd12..3707e792177d3 100644 --- a/src/tools/clippy/clippy_lints/src/get_last_with_len.rs +++ b/src/tools/clippy/clippy_lints/src/get_last_with_len.rs @@ -1,6 +1,9 @@ //! lint on using `x.get(x.len() - 1)` instead of `x.last()` -use crate::utils::{is_type_diagnostic_item, snippet_with_applicability, span_lint_and_sugg, SpanlessEq}; +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::source::snippet_with_applicability; +use clippy_utils::ty::is_type_diagnostic_item; +use clippy_utils::SpanlessEq; use if_chain::if_chain; use rustc_ast::ast::LitKind; use rustc_errors::Applicability; @@ -48,7 +51,7 @@ impl<'tcx> LateLintPass<'tcx> for GetLastWithLen { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { if_chain! { // Is a method call - if let ExprKind::MethodCall(ref path, _, ref args, _) = expr.kind; + if let ExprKind::MethodCall(path, _, args, _) = expr.kind; // Method name is "get" if path.ident.name == sym!(get); diff --git a/src/tools/clippy/clippy_lints/src/identity_op.rs b/src/tools/clippy/clippy_lints/src/identity_op.rs index 8501d34770201..366b3b46a8aec 100644 --- a/src/tools/clippy/clippy_lints/src/identity_op.rs +++ b/src/tools/clippy/clippy_lints/src/identity_op.rs @@ -1,3 +1,4 @@ +use clippy_utils::source::snippet; use if_chain::if_chain; use rustc_hir::{BinOp, BinOpKind, Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; @@ -6,7 +7,8 @@ use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::source_map::Span; use crate::consts::{constant_simple, Constant}; -use crate::utils::{clip, snippet, span_lint, unsext}; +use clippy_utils::diagnostics::span_lint; +use clippy_utils::{clip, unsext}; declare_clippy_lint! { /// **What it does:** Checks for identity operations, e.g., `x + 0`. @@ -33,7 +35,7 @@ impl<'tcx> LateLintPass<'tcx> for IdentityOp { if e.span.from_expansion() { return; } - if let ExprKind::Binary(cmp, ref left, ref right) = e.kind { + if let ExprKind::Binary(cmp, left, right) = e.kind { if is_allowed(cx, cmp, left, right) { return; } diff --git a/src/tools/clippy/clippy_lints/src/if_let_mutex.rs b/src/tools/clippy/clippy_lints/src/if_let_mutex.rs index 58511c6d57c68..f661f7ede821a 100644 --- a/src/tools/clippy/clippy_lints/src/if_let_mutex.rs +++ b/src/tools/clippy/clippy_lints/src/if_let_mutex.rs @@ -1,4 +1,6 @@ -use crate::utils::{is_type_diagnostic_item, span_lint_and_help, SpanlessEq}; +use clippy_utils::diagnostics::span_lint_and_help; +use clippy_utils::ty::is_type_diagnostic_item; +use clippy_utils::SpanlessEq; use if_chain::if_chain; use rustc_hir::intravisit::{self as visit, NestedVisitorMap, Visitor}; use rustc_hir::{Expr, ExprKind, MatchSource}; @@ -53,8 +55,8 @@ impl<'tcx> LateLintPass<'tcx> for IfLetMutex { cx, }; if let ExprKind::Match( - ref op, - ref arms, + op, + arms, MatchSource::IfLetDesugar { contains_else_clause: true, }, @@ -62,7 +64,7 @@ impl<'tcx> LateLintPass<'tcx> for IfLetMutex { { op_visit.visit_expr(op); if op_visit.mutex_lock_called { - for arm in *arms { + for arm in arms { arm_visit.visit_arm(arm); } @@ -92,13 +94,10 @@ impl<'tcx> Visitor<'tcx> for OppVisitor<'_, 'tcx> { type Map = Map<'tcx>; fn visit_expr(&mut self, expr: &'tcx Expr<'_>) { - if_chain! { - if let Some(mutex) = is_mutex_lock_call(self.cx, expr); - then { - self.found_mutex = Some(mutex); - self.mutex_lock_called = true; - return; - } + if let Some(mutex) = is_mutex_lock_call(self.cx, expr) { + self.found_mutex = Some(mutex); + self.mutex_lock_called = true; + return; } visit::walk_expr(self, expr); } @@ -119,13 +118,10 @@ impl<'tcx> Visitor<'tcx> for ArmVisitor<'_, 'tcx> { type Map = Map<'tcx>; fn visit_expr(&mut self, expr: &'tcx Expr<'tcx>) { - if_chain! { - if let Some(mutex) = is_mutex_lock_call(self.cx, expr); - then { - self.found_mutex = Some(mutex); - self.mutex_lock_called = true; - return; - } + if let Some(mutex) = is_mutex_lock_call(self.cx, expr) { + self.found_mutex = Some(mutex); + self.mutex_lock_called = true; + return; } visit::walk_expr(self, expr); } diff --git a/src/tools/clippy/clippy_lints/src/if_let_some_result.rs b/src/tools/clippy/clippy_lints/src/if_let_some_result.rs index 1194bd7e55e25..611da3744eeee 100644 --- a/src/tools/clippy/clippy_lints/src/if_let_some_result.rs +++ b/src/tools/clippy/clippy_lints/src/if_let_some_result.rs @@ -1,4 +1,7 @@ -use crate::utils::{is_type_diagnostic_item, method_chain_args, snippet_with_applicability, span_lint_and_sugg}; +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::method_chain_args; +use clippy_utils::source::snippet_with_applicability; +use clippy_utils::ty::is_type_diagnostic_item; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind, MatchSource, PatKind, QPath}; @@ -41,9 +44,9 @@ declare_lint_pass!(OkIfLet => [IF_LET_SOME_RESULT]); impl<'tcx> LateLintPass<'tcx> for OkIfLet { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { if_chain! { //begin checking variables - if let ExprKind::Match(ref op, ref body, MatchSource::IfLetDesugar { .. }) = expr.kind; //test if expr is if let - if let ExprKind::MethodCall(_, ok_span, ref result_types, _) = op.kind; //check is expr.ok() has type Result<T,E>.ok(, _) - if let PatKind::TupleStruct(QPath::Resolved(_, ref x), ref y, _) = body[0].pat.kind; //get operation + if let ExprKind::Match(op, body, MatchSource::IfLetDesugar { .. }) = expr.kind; //test if expr is if let + if let ExprKind::MethodCall(_, ok_span, result_types, _) = op.kind; //check is expr.ok() has type Result<T,E>.ok(, _) + if let PatKind::TupleStruct(QPath::Resolved(_, x), y, _) = body[0].pat.kind; //get operation if method_chain_args(op, &["ok"]).is_some(); //test to see if using ok() methoduse std::marker::Sized; if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(&result_types[0]), sym::result_type); if rustc_hir_pretty::to_string(rustc_hir_pretty::NO_ANN, |s| s.print_path(x, false)) == "Some"; diff --git a/src/tools/clippy/clippy_lints/src/if_not_else.rs b/src/tools/clippy/clippy_lints/src/if_not_else.rs index b86d2e766566b..c56f67df0618f 100644 --- a/src/tools/clippy/clippy_lints/src/if_not_else.rs +++ b/src/tools/clippy/clippy_lints/src/if_not_else.rs @@ -1,13 +1,12 @@ //! lint on if branches that could be swapped so no `!` operation is necessary //! on the condition +use clippy_utils::diagnostics::span_lint_and_help; use rustc_ast::ast::{BinOpKind, Expr, ExprKind, UnOp}; use rustc_lint::{EarlyContext, EarlyLintPass, LintContext}; use rustc_middle::lint::in_external_macro; use rustc_session::{declare_lint_pass, declare_tool_lint}; -use crate::utils::span_lint_and_help; - declare_clippy_lint! { /// **What it does:** Checks for usage of `!` or `!=` in an if condition with an /// else branch. 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 new file mode 100644 index 0000000000000..ee16519692f9a --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/if_then_some_else_none.rs @@ -0,0 +1,117 @@ +use clippy_utils::diagnostics::span_lint_and_help; +use clippy_utils::source::snippet_with_macro_callsite; +use clippy_utils::{match_qpath, meets_msrv, parent_node_is_if_expr}; +use if_chain::if_chain; +use rustc_hir::{Expr, ExprKind}; +use rustc_lint::{LateContext, LateLintPass, LintContext}; +use rustc_middle::lint::in_external_macro; +use rustc_semver::RustcVersion; +use rustc_session::{declare_tool_lint, impl_lint_pass}; + +const IF_THEN_SOME_ELSE_NONE_MSRV: RustcVersion = RustcVersion::new(1, 50, 0); + +declare_clippy_lint! { + /// **What it does:** Checks for if-else that could be written to `bool::then`. + /// + /// **Why is this bad?** Looks a little redundant. Using `bool::then` helps it have less lines of code. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// + /// ```rust + /// # let v = vec![0]; + /// let a = if v.is_empty() { + /// println!("true!"); + /// Some(42) + /// } else { + /// None + /// }; + /// ``` + /// + /// Could be written: + /// + /// ```rust + /// # let v = vec![0]; + /// let a = v.is_empty().then(|| { + /// println!("true!"); + /// 42 + /// }); + /// ``` + pub IF_THEN_SOME_ELSE_NONE, + restriction, + "Finds if-else that could be written using `bool::then`" +} + +pub struct IfThenSomeElseNone { + msrv: Option<RustcVersion>, +} + +impl IfThenSomeElseNone { + #[must_use] + pub fn new(msrv: Option<RustcVersion>) -> Self { + Self { msrv } + } +} + +impl_lint_pass!(IfThenSomeElseNone => [IF_THEN_SOME_ELSE_NONE]); + +impl LateLintPass<'_> for IfThenSomeElseNone { + fn check_expr(&mut self, cx: &LateContext<'_>, expr: &'tcx Expr<'_>) { + if !meets_msrv(self.msrv.as_ref(), &IF_THEN_SOME_ELSE_NONE_MSRV) { + return; + } + + if in_external_macro(cx.sess(), expr.span) { + return; + } + + // We only care about the top-most `if` in the chain + if parent_node_is_if_expr(expr, cx) { + return; + } + + if_chain! { + if let ExprKind::If(cond, then, Some(els)) = expr.kind; + if let ExprKind::Block(then_block, _) = then.kind; + if let Some(then_expr) = then_block.expr; + if let ExprKind::Call(then_call, [then_arg]) = then_expr.kind; + if let ExprKind::Path(ref then_call_qpath) = then_call.kind; + if match_qpath(then_call_qpath, &clippy_utils::paths::OPTION_SOME); + if let ExprKind::Block(els_block, _) = els.kind; + if els_block.stmts.is_empty(); + if let Some(els_expr) = els_block.expr; + if let ExprKind::Path(ref els_call_qpath) = els_expr.kind; + if match_qpath(els_call_qpath, &clippy_utils::paths::OPTION_NONE); + then { + let cond_snip = snippet_with_macro_callsite(cx, cond.span, "[condition]"); + let cond_snip = if matches!(cond.kind, ExprKind::Unary(_, _) | ExprKind::Binary(_, _, _)) { + format!("({})", cond_snip) + } else { + cond_snip.into_owned() + }; + let arg_snip = snippet_with_macro_callsite(cx, then_arg.span, ""); + let closure_body = if then_block.stmts.is_empty() { + arg_snip.into_owned() + } else { + format!("{{ /* snippet */ {} }}", arg_snip) + }; + let help = format!( + "consider using `bool::then` like: `{}.then(|| {})`", + cond_snip, + closure_body, + ); + span_lint_and_help( + cx, + IF_THEN_SOME_ELSE_NONE, + expr.span, + "this could be simplified with `bool::then`", + None, + &help, + ); + } + } + } + + extract_msrv_attr!(LateContext); +} diff --git a/src/tools/clippy/clippy_lints/src/implicit_hasher.rs b/src/tools/clippy/clippy_lints/src/implicit_hasher.rs new file mode 100644 index 0000000000000..77a38544edc87 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/implicit_hasher.rs @@ -0,0 +1,377 @@ +#![allow(rustc::default_hash_types)] + +use std::borrow::Cow; +use std::collections::BTreeMap; + +use rustc_errors::DiagnosticBuilder; +use rustc_hir as hir; +use rustc_hir::intravisit::{walk_body, walk_expr, walk_ty, NestedVisitorMap, Visitor}; +use rustc_hir::{Body, Expr, ExprKind, GenericArg, Item, ItemKind, QPath, TyKind}; +use rustc_lint::{LateContext, LateLintPass, LintContext}; +use rustc_middle::hir::map::Map; +use rustc_middle::lint::in_external_macro; +use rustc_middle::ty::{Ty, TyS, TypeckResults}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::source_map::Span; +use rustc_span::symbol::sym; +use rustc_typeck::hir_ty_to_ty; + +use if_chain::if_chain; + +use clippy_utils::diagnostics::{multispan_sugg, span_lint_and_then}; +use clippy_utils::paths; +use clippy_utils::source::{snippet, snippet_opt}; +use clippy_utils::ty::is_type_diagnostic_item; +use clippy_utils::{differing_macro_contexts, match_path}; + +declare_clippy_lint! { + /// **What it does:** Checks for public `impl` or `fn` missing generalization + /// over different hashers and implicitly defaulting to the default hashing + /// algorithm (`SipHash`). + /// + /// **Why is this bad?** `HashMap` or `HashSet` with custom hashers cannot be + /// used with them. + /// + /// **Known problems:** Suggestions for replacing constructors can contain + /// false-positives. Also applying suggestions can require modification of other + /// pieces of code, possibly including external crates. + /// + /// **Example:** + /// ```rust + /// # use std::collections::HashMap; + /// # use std::hash::{Hash, BuildHasher}; + /// # trait Serialize {}; + /// impl<K: Hash + Eq, V> Serialize for HashMap<K, V> { } + /// + /// pub fn foo(map: &mut HashMap<i32, i32>) { } + /// ``` + /// could be rewritten as + /// ```rust + /// # use std::collections::HashMap; + /// # use std::hash::{Hash, BuildHasher}; + /// # trait Serialize {}; + /// impl<K: Hash + Eq, V, S: BuildHasher> Serialize for HashMap<K, V, S> { } + /// + /// pub fn foo<S: BuildHasher>(map: &mut HashMap<i32, i32, S>) { } + /// ``` + pub IMPLICIT_HASHER, + pedantic, + "missing generalization over different hashers" +} + +declare_lint_pass!(ImplicitHasher => [IMPLICIT_HASHER]); + +impl<'tcx> LateLintPass<'tcx> for ImplicitHasher { + #[allow(clippy::cast_possible_truncation, clippy::too_many_lines)] + fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) { + use rustc_span::BytePos; + + fn suggestion<'tcx>( + cx: &LateContext<'tcx>, + diag: &mut DiagnosticBuilder<'_>, + generics_span: Span, + generics_suggestion_span: Span, + target: &ImplicitHasherType<'_>, + vis: ImplicitHasherConstructorVisitor<'_, '_, '_>, + ) { + let generics_snip = snippet(cx, generics_span, ""); + // trim `<` `>` + let generics_snip = if generics_snip.is_empty() { + "" + } else { + &generics_snip[1..generics_snip.len() - 1] + }; + + multispan_sugg( + diag, + "consider adding a type parameter", + vec![ + ( + generics_suggestion_span, + format!( + "<{}{}S: ::std::hash::BuildHasher{}>", + generics_snip, + if generics_snip.is_empty() { "" } else { ", " }, + if vis.suggestions.is_empty() { + "" + } else { + // request users to add `Default` bound so that generic constructors can be used + " + Default" + }, + ), + ), + ( + target.span(), + format!("{}<{}, S>", target.type_name(), target.type_arguments(),), + ), + ], + ); + + if !vis.suggestions.is_empty() { + multispan_sugg(diag, "...and use generic constructor", vis.suggestions); + } + } + + if !cx.access_levels.is_exported(item.hir_id()) { + return; + } + + match item.kind { + ItemKind::Impl(ref impl_) => { + let mut vis = ImplicitHasherTypeVisitor::new(cx); + vis.visit_ty(impl_.self_ty); + + for target in &vis.found { + if differing_macro_contexts(item.span, target.span()) { + return; + } + + let generics_suggestion_span = impl_.generics.span.substitute_dummy({ + let pos = snippet_opt(cx, item.span.until(target.span())) + .and_then(|snip| Some(item.span.lo() + BytePos(snip.find("impl")? as u32 + 4))); + if let Some(pos) = pos { + Span::new(pos, pos, item.span.data().ctxt) + } else { + return; + } + }); + + let mut ctr_vis = ImplicitHasherConstructorVisitor::new(cx, target); + for item in impl_.items.iter().map(|item| cx.tcx.hir().impl_item(item.id)) { + ctr_vis.visit_impl_item(item); + } + + span_lint_and_then( + cx, + IMPLICIT_HASHER, + target.span(), + &format!( + "impl for `{}` should be generalized over different hashers", + target.type_name() + ), + move |diag| { + suggestion(cx, diag, impl_.generics.span, generics_suggestion_span, target, ctr_vis); + }, + ); + } + }, + ItemKind::Fn(ref sig, ref generics, body_id) => { + let body = cx.tcx.hir().body(body_id); + + for ty in sig.decl.inputs { + let mut vis = ImplicitHasherTypeVisitor::new(cx); + vis.visit_ty(ty); + + for target in &vis.found { + if in_external_macro(cx.sess(), generics.span) { + continue; + } + let generics_suggestion_span = generics.span.substitute_dummy({ + let pos = snippet_opt(cx, item.span.until(body.params[0].pat.span)) + .and_then(|snip| { + let i = snip.find("fn")?; + Some(item.span.lo() + BytePos((i + (&snip[i..]).find('(')?) as u32)) + }) + .expect("failed to create span for type parameters"); + Span::new(pos, pos, item.span.data().ctxt) + }); + + let mut ctr_vis = ImplicitHasherConstructorVisitor::new(cx, target); + ctr_vis.visit_body(body); + + span_lint_and_then( + cx, + IMPLICIT_HASHER, + target.span(), + &format!( + "parameter of type `{}` should be generalized over different hashers", + target.type_name() + ), + move |diag| { + suggestion(cx, diag, generics.span, generics_suggestion_span, target, ctr_vis); + }, + ); + } + } + }, + _ => {}, + } + } +} + +enum ImplicitHasherType<'tcx> { + HashMap(Span, Ty<'tcx>, Cow<'static, str>, Cow<'static, str>), + HashSet(Span, Ty<'tcx>, Cow<'static, str>), +} + +impl<'tcx> ImplicitHasherType<'tcx> { + /// Checks that `ty` is a target type without a `BuildHasher`. + fn new(cx: &LateContext<'tcx>, hir_ty: &hir::Ty<'_>) -> Option<Self> { + if let TyKind::Path(QPath::Resolved(None, path)) = hir_ty.kind { + let params: Vec<_> = path + .segments + .last() + .as_ref()? + .args + .as_ref()? + .args + .iter() + .filter_map(|arg| match arg { + GenericArg::Type(ty) => Some(ty), + _ => None, + }) + .collect(); + let params_len = params.len(); + + let ty = hir_ty_to_ty(cx.tcx, hir_ty); + + if is_type_diagnostic_item(cx, ty, sym::hashmap_type) && params_len == 2 { + Some(ImplicitHasherType::HashMap( + hir_ty.span, + ty, + snippet(cx, params[0].span, "K"), + snippet(cx, params[1].span, "V"), + )) + } else if is_type_diagnostic_item(cx, ty, sym::hashset_type) && params_len == 1 { + Some(ImplicitHasherType::HashSet( + hir_ty.span, + ty, + snippet(cx, params[0].span, "T"), + )) + } else { + None + } + } else { + None + } + } + + fn type_name(&self) -> &'static str { + match *self { + ImplicitHasherType::HashMap(..) => "HashMap", + ImplicitHasherType::HashSet(..) => "HashSet", + } + } + + fn type_arguments(&self) -> String { + match *self { + ImplicitHasherType::HashMap(.., ref k, ref v) => format!("{}, {}", k, v), + ImplicitHasherType::HashSet(.., ref t) => format!("{}", t), + } + } + + fn ty(&self) -> Ty<'tcx> { + match *self { + ImplicitHasherType::HashMap(_, ty, ..) | ImplicitHasherType::HashSet(_, ty, ..) => ty, + } + } + + fn span(&self) -> Span { + match *self { + ImplicitHasherType::HashMap(span, ..) | ImplicitHasherType::HashSet(span, ..) => span, + } + } +} + +struct ImplicitHasherTypeVisitor<'a, 'tcx> { + cx: &'a LateContext<'tcx>, + found: Vec<ImplicitHasherType<'tcx>>, +} + +impl<'a, 'tcx> ImplicitHasherTypeVisitor<'a, 'tcx> { + fn new(cx: &'a LateContext<'tcx>) -> Self { + Self { cx, found: vec![] } + } +} + +impl<'a, 'tcx> Visitor<'tcx> for ImplicitHasherTypeVisitor<'a, 'tcx> { + type Map = Map<'tcx>; + + fn visit_ty(&mut self, t: &'tcx hir::Ty<'_>) { + if let Some(target) = ImplicitHasherType::new(self.cx, t) { + self.found.push(target); + } + + walk_ty(self, t); + } + + fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> { + NestedVisitorMap::None + } +} + +/// Looks for default-hasher-dependent constructors like `HashMap::new`. +struct ImplicitHasherConstructorVisitor<'a, 'b, 'tcx> { + cx: &'a LateContext<'tcx>, + maybe_typeck_results: Option<&'tcx TypeckResults<'tcx>>, + target: &'b ImplicitHasherType<'tcx>, + suggestions: BTreeMap<Span, String>, +} + +impl<'a, 'b, 'tcx> ImplicitHasherConstructorVisitor<'a, 'b, 'tcx> { + fn new(cx: &'a LateContext<'tcx>, target: &'b ImplicitHasherType<'tcx>) -> Self { + Self { + cx, + maybe_typeck_results: cx.maybe_typeck_results(), + target, + suggestions: BTreeMap::new(), + } + } +} + +impl<'a, 'b, 'tcx> Visitor<'tcx> for ImplicitHasherConstructorVisitor<'a, 'b, 'tcx> { + type Map = Map<'tcx>; + + fn visit_body(&mut self, body: &'tcx Body<'_>) { + let old_maybe_typeck_results = self.maybe_typeck_results.replace(self.cx.tcx.typeck_body(body.id())); + walk_body(self, body); + self.maybe_typeck_results = old_maybe_typeck_results; + } + + fn visit_expr(&mut self, e: &'tcx Expr<'_>) { + if_chain! { + if let ExprKind::Call(fun, args) = e.kind; + if let ExprKind::Path(QPath::TypeRelative(ty, method)) = fun.kind; + if let TyKind::Path(QPath::Resolved(None, ty_path)) = ty.kind; + then { + if !TyS::same_type(self.target.ty(), self.maybe_typeck_results.unwrap().expr_ty(e)) { + return; + } + + if match_path(ty_path, &paths::HASHMAP) { + if method.ident.name == sym::new { + self.suggestions + .insert(e.span, "HashMap::default()".to_string()); + } else if method.ident.name == sym!(with_capacity) { + self.suggestions.insert( + e.span, + format!( + "HashMap::with_capacity_and_hasher({}, Default::default())", + snippet(self.cx, args[0].span, "capacity"), + ), + ); + } + } else if match_path(ty_path, &paths::HASHSET) { + if method.ident.name == sym::new { + self.suggestions + .insert(e.span, "HashSet::default()".to_string()); + } else if method.ident.name == sym!(with_capacity) { + self.suggestions.insert( + e.span, + format!( + "HashSet::with_capacity_and_hasher({}, Default::default())", + snippet(self.cx, args[0].span, "capacity"), + ), + ); + } + } + } + } + + walk_expr(self, e); + } + + fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> { + NestedVisitorMap::OnlyBodies(self.cx.tcx.hir()) + } +} diff --git a/src/tools/clippy/clippy_lints/src/implicit_return.rs b/src/tools/clippy/clippy_lints/src/implicit_return.rs index 109d90ff772b5..6b379b0d59b2b 100644 --- a/src/tools/clippy/clippy_lints/src/implicit_return.rs +++ b/src/tools/clippy/clippy_lints/src/implicit_return.rs @@ -1,4 +1,6 @@ -use crate::utils::{fn_has_unsatisfiable_preds, match_panic_def_id, snippet_opt, span_lint_and_then}; +use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::match_panic_def_id; +use clippy_utils::source::snippet_opt; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::intravisit::FnKind; @@ -98,10 +100,10 @@ fn expr_match(cx: &LateContext<'_>, expr: &Expr<'_>) { if check_all_arms { for arm in arms { - expr_match(cx, &arm.body); + expr_match(cx, arm.body); } } else { - expr_match(cx, &arms.first().expect("`if let` doesn't have a single arm").body); + expr_match(cx, arms.first().expect("`if let` doesn't have a single arm").body); } }, // skip if it already has a return statement @@ -133,19 +135,13 @@ impl<'tcx> LateLintPass<'tcx> for ImplicitReturn { span: Span, _: HirId, ) { - let def_id = cx.tcx.hir().body_owner_def_id(body.id()); - - // Building MIR for `fn`s with unsatisfiable preds results in ICE. - if fn_has_unsatisfiable_preds(cx, def_id.to_def_id()) { + if span.from_expansion() { return; } - - let mir = cx.tcx.optimized_mir(def_id.to_def_id()); - - // checking return type through MIR, HIR is not able to determine inferred closure return types - // make sure it's not a macro - if !mir.return_ty().is_unit() && !span.from_expansion() { - expr_match(cx, &body.value); + let body = cx.tcx.hir().body(body.id()); + if cx.typeck_results().expr_ty(&body.value).is_unit() { + return; } + expr_match(cx, &body.value); } } diff --git a/src/tools/clippy/clippy_lints/src/implicit_saturating_sub.rs b/src/tools/clippy/clippy_lints/src/implicit_saturating_sub.rs index 16e162badb5ee..cba3183e86950 100644 --- a/src/tools/clippy/clippy_lints/src/implicit_saturating_sub.rs +++ b/src/tools/clippy/clippy_lints/src/implicit_saturating_sub.rs @@ -1,4 +1,5 @@ -use crate::utils::{in_macro, match_qpath, span_lint_and_sugg, SpanlessEq}; +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::{in_macro, match_qpath, SpanlessEq}; use if_chain::if_chain; use rustc_ast::ast::LitKind; use rustc_errors::Applicability; @@ -45,21 +46,21 @@ impl<'tcx> LateLintPass<'tcx> for ImplicitSaturatingSub { if let ExprKind::If(cond, then, None) = &expr.kind; // Check if the conditional expression is a binary operation - if let ExprKind::Binary(ref cond_op, ref cond_left, ref cond_right) = cond.kind; + if let ExprKind::Binary(ref cond_op, cond_left, cond_right) = cond.kind; // Ensure that the binary operator is >, != and < if BinOpKind::Ne == cond_op.node || BinOpKind::Gt == cond_op.node || BinOpKind::Lt == cond_op.node; // Check if the true condition block has only one statement - if let ExprKind::Block(ref block, _) = then.kind; + if let ExprKind::Block(block, _) = then.kind; if block.stmts.len() == 1 && block.expr.is_none(); // Check if assign operation is done - if let StmtKind::Semi(ref e) = block.stmts[0].kind; + if let StmtKind::Semi(e) = block.stmts[0].kind; if let Some(target) = subtracts_one(cx, e); // Extracting out the variable name - if let ExprKind::Path(QPath::Resolved(_, ref ares_path)) = target.kind; + if let ExprKind::Path(QPath::Resolved(_, ares_path)) = target.kind; then { // Handle symmetric conditions in the if statement @@ -103,7 +104,7 @@ impl<'tcx> LateLintPass<'tcx> for ImplicitSaturatingSub { print_lint_and_sugg(cx, &var_name, expr); }; }, - ExprKind::Call(ref func, _) => { + ExprKind::Call(func, _) => { if let ExprKind::Path(ref cond_num_path) = func.kind { if INT_TYPES.iter().any(|int_type| match_qpath(cond_num_path, &[int_type, "min_value"])) { print_lint_and_sugg(cx, &var_name, expr); @@ -119,7 +120,7 @@ impl<'tcx> LateLintPass<'tcx> for ImplicitSaturatingSub { fn subtracts_one<'a>(cx: &LateContext<'_>, expr: &Expr<'a>) -> Option<&'a Expr<'a>> { match expr.kind { - ExprKind::AssignOp(ref op1, ref target, ref value) => { + ExprKind::AssignOp(ref op1, target, value) => { if_chain! { if BinOpKind::Sub == op1.node; // Check if literal being subtracted is one @@ -132,9 +133,9 @@ fn subtracts_one<'a>(cx: &LateContext<'_>, expr: &Expr<'a>) -> Option<&'a Expr<' } } }, - ExprKind::Assign(ref target, ref value, _) => { + ExprKind::Assign(target, value, _) => { if_chain! { - if let ExprKind::Binary(ref op1, ref left1, ref right1) = value.kind; + if let ExprKind::Binary(ref op1, left1, right1) = value.kind; if BinOpKind::Sub == op1.node; if SpanlessEq::new(cx).eq_expr(left1, target); 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 c5afdf530eb70..d7ca24487a884 100644 --- a/src/tools/clippy/clippy_lints/src/inconsistent_struct_constructor.rs +++ b/src/tools/clippy/clippy_lints/src/inconsistent_struct_constructor.rs @@ -1,3 +1,6 @@ +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::source::snippet; +use if_chain::if_chain; use rustc_data_structures::fx::FxHashMap; use rustc_errors::Applicability; use rustc_hir::{self as hir, ExprKind}; @@ -5,13 +8,10 @@ use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::symbol::Symbol; -use if_chain::if_chain; - -use crate::utils::{snippet, span_lint_and_sugg}; - declare_clippy_lint! { - /// **What it does:** Checks for struct constructors where the order of the field init - /// shorthand in the constructor is inconsistent with the order in the struct definition. + /// **What it does:** Checks for struct constructors where all fields are shorthand and + /// the order of the field init shorthand in the constructor is inconsistent + /// with the order in the struct definition. /// /// **Why is this bad?** Since the order of fields in a constructor doesn't affect the /// resulted instance as the below example indicates, @@ -25,11 +25,11 @@ declare_clippy_lint! { /// let x = 1; /// let y = 2; /// - /// // This assertion never fails. + /// // This assertion never fails: /// assert_eq!(Foo { x, y }, Foo { y, x }); /// ``` /// - /// inconsistent order means nothing and just decreases readability and consistency. + /// inconsistent order can be confusing and decreases readability and consistency. /// /// **Known problems:** None. /// @@ -42,6 +42,7 @@ declare_clippy_lint! { /// } /// let x = 1; /// let y = 2; + /// /// Foo { y, x }; /// ``` /// @@ -66,8 +67,7 @@ impl LateLintPass<'_> for InconsistentStructConstructor { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) { if_chain! { if let ExprKind::Struct(qpath, fields, base) = expr.kind; - if let Some(def_id) = cx.qpath_res(qpath, expr.hir_id).opt_def_id(); - let ty = cx.tcx.type_of(def_id); + let ty = cx.typeck_results().expr_ty(expr); if let Some(adt_def) = ty.ty_adt_def(); if adt_def.is_struct(); if let Some(variant) = adt_def.variants.iter().next(); @@ -108,7 +108,7 @@ impl LateLintPass<'_> for InconsistentStructConstructor { cx, INCONSISTENT_STRUCT_CONSTRUCTOR, expr.span, - "inconsistent struct constructor", + "struct constructor field order is inconsistent with struct definition field order", "try", sugg, Applicability::MachineApplicable, @@ -120,7 +120,7 @@ impl LateLintPass<'_> for InconsistentStructConstructor { // Check whether the order of the fields in the constructor is consistent with the order in the // definition. -fn is_consistent_order<'tcx>(fields: &'tcx [hir::Field<'tcx>], def_order_map: &FxHashMap<Symbol, usize>) -> bool { +fn is_consistent_order<'tcx>(fields: &'tcx [hir::ExprField<'tcx>], def_order_map: &FxHashMap<Symbol, usize>) -> bool { let mut cur_idx = usize::MIN; for f in fields { let next_idx = def_order_map[&f.ident.name]; diff --git a/src/tools/clippy/clippy_lints/src/indexing_slicing.rs b/src/tools/clippy/clippy_lints/src/indexing_slicing.rs index 741195f3b10d5..1c54599abc405 100644 --- a/src/tools/clippy/clippy_lints/src/indexing_slicing.rs +++ b/src/tools/clippy/clippy_lints/src/indexing_slicing.rs @@ -1,7 +1,8 @@ //! lint on indexing and slicing operations use crate::consts::{constant, Constant}; -use crate::utils::{higher, span_lint, span_lint_and_help}; +use clippy_utils::diagnostics::{span_lint, span_lint_and_help}; +use clippy_utils::higher; use rustc_ast::ast::RangeLimits; use rustc_hir::{Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; @@ -87,7 +88,7 @@ declare_lint_pass!(IndexingSlicing => [INDEXING_SLICING, OUT_OF_BOUNDS_INDEXING] impl<'tcx> LateLintPass<'tcx> for IndexingSlicing { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - if let ExprKind::Index(ref array, ref index) = &expr.kind { + if let ExprKind::Index(array, index) = &expr.kind { let ty = cx.typeck_results().expr_ty(array).peel_refs(); if let Some(range) = higher::range(index) { // Ranged indexes, i.e., &x[n..m], &x[n..], &x[..n] and &x[..] @@ -132,13 +133,13 @@ impl<'tcx> LateLintPass<'tcx> for IndexingSlicing { } let help_msg = match (range.start, range.end) { - (None, Some(_)) => "Consider using `.get(..n)`or `.get_mut(..n)` instead", - (Some(_), None) => "Consider using `.get(n..)` or .get_mut(n..)` instead", - (Some(_), Some(_)) => "Consider using `.get(n..m)` or `.get_mut(n..m)` instead", + (None, Some(_)) => "consider using `.get(..n)`or `.get_mut(..n)` instead", + (Some(_), None) => "consider using `.get(n..)` or .get_mut(n..)` instead", + (Some(_), Some(_)) => "consider using `.get(n..m)` or `.get_mut(n..m)` instead", (None, None) => return, // [..] is ok. }; - span_lint_and_help(cx, INDEXING_SLICING, expr.span, "slicing may panic.", None, help_msg); + span_lint_and_help(cx, INDEXING_SLICING, expr.span, "slicing may panic", None, help_msg); } else { // Catchall non-range index, i.e., [n] or [n << m] if let ty::Array(..) = ty.kind() { @@ -153,9 +154,9 @@ impl<'tcx> LateLintPass<'tcx> for IndexingSlicing { cx, INDEXING_SLICING, expr.span, - "indexing may panic.", + "indexing may panic", None, - "Consider using `.get(n)` or `.get_mut(n)` instead", + "consider using `.get(n)` or `.get_mut(n)` instead", ); } } diff --git a/src/tools/clippy/clippy_lints/src/infinite_iter.rs b/src/tools/clippy/clippy_lints/src/infinite_iter.rs index 129abd7d89749..bbb4ddc613af5 100644 --- a/src/tools/clippy/clippy_lints/src/infinite_iter.rs +++ b/src/tools/clippy/clippy_lints/src/infinite_iter.rs @@ -1,9 +1,10 @@ +use clippy_utils::diagnostics::span_lint; +use clippy_utils::ty::{implements_trait, match_type}; +use clippy_utils::{get_trait_def_id, higher, match_qpath, paths}; use rustc_hir::{BorrowKind, Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; -use crate::utils::{get_trait_def_id, higher, implements_trait, match_qpath, match_type, paths, span_lint}; - declare_clippy_lint! { /// **What it does:** Checks for iteration that is guaranteed to be infinite. /// @@ -89,11 +90,7 @@ impl Finiteness { impl From<bool> for Finiteness { #[must_use] fn from(b: bool) -> Self { - if b { - Infinite - } else { - Finite - } + if b { Infinite } else { Finite } } } @@ -142,7 +139,7 @@ const HEURISTICS: [(&str, usize, Heuristic, Finiteness); 19] = [ fn is_infinite(cx: &LateContext<'_>, expr: &Expr<'_>) -> Finiteness { match expr.kind { - ExprKind::MethodCall(ref method, _, ref args, _) => { + ExprKind::MethodCall(method, _, args, _) => { for &(name, len, heuristic, cap) in &HEURISTICS { if method.ident.name.as_str() == name && args.len() == len { return (match heuristic { @@ -162,9 +159,9 @@ fn is_infinite(cx: &LateContext<'_>, expr: &Expr<'_>) -> Finiteness { } Finite }, - ExprKind::Block(ref block, _) => block.expr.as_ref().map_or(Finite, |e| is_infinite(cx, e)), - ExprKind::Box(ref e) | ExprKind::AddrOf(BorrowKind::Ref, _, ref e) => is_infinite(cx, e), - ExprKind::Call(ref path, _) => { + ExprKind::Block(block, _) => block.expr.as_ref().map_or(Finite, |e| is_infinite(cx, e)), + ExprKind::Box(e) | ExprKind::AddrOf(BorrowKind::Ref, _, e) => is_infinite(cx, e), + ExprKind::Call(path, _) => { if let ExprKind::Path(ref qpath) = path.kind { match_qpath(qpath, &paths::REPEAT).into() } else { @@ -218,7 +215,7 @@ const INFINITE_COLLECTORS: [&[&str]; 8] = [ fn complete_infinite_iter(cx: &LateContext<'_>, expr: &Expr<'_>) -> Finiteness { match expr.kind { - ExprKind::MethodCall(ref method, _, ref args, _) => { + ExprKind::MethodCall(method, _, args, _) => { for &(name, len) in &COMPLETING_METHODS { if method.ident.name.as_str() == name && args.len() == len { return is_infinite(cx, &args[0]); @@ -243,7 +240,7 @@ fn complete_infinite_iter(cx: &LateContext<'_>, expr: &Expr<'_>) -> Finiteness { } } }, - ExprKind::Binary(op, ref l, ref r) => { + ExprKind::Binary(op, l, r) => { if op.node.is_comparison() { return is_infinite(cx, l).and(is_infinite(cx, r)).and(MaybeInfinite); } diff --git a/src/tools/clippy/clippy_lints/src/inherent_impl.rs b/src/tools/clippy/clippy_lints/src/inherent_impl.rs index 005c461f105e6..c31013e49be59 100644 --- a/src/tools/clippy/clippy_lints/src/inherent_impl.rs +++ b/src/tools/clippy/clippy_lints/src/inherent_impl.rs @@ -1,7 +1,8 @@ //! lint on inherent implementations -use crate::utils::{in_macro, span_lint_and_then}; -use rustc_data_structures::fx::FxHashMap; +use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::in_macro; +use rustc_hir::def_id::DefIdMap; use rustc_hir::{def_id, Crate, Impl, Item, ItemKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_tool_lint, impl_lint_pass}; @@ -42,7 +43,7 @@ declare_clippy_lint! { #[allow(clippy::module_name_repetitions)] #[derive(Default)] pub struct MultipleInherentImpl { - impls: FxHashMap<def_id::DefId, Span>, + impls: DefIdMap<Span>, } impl_lint_pass!(MultipleInherentImpl => [MULTIPLE_INHERENT_IMPL]); diff --git a/src/tools/clippy/clippy_lints/src/inherent_to_string.rs b/src/tools/clippy/clippy_lints/src/inherent_to_string.rs index a95321ea7e2af..b023e13e846c7 100644 --- a/src/tools/clippy/clippy_lints/src/inherent_to_string.rs +++ b/src/tools/clippy/clippy_lints/src/inherent_to_string.rs @@ -1,14 +1,12 @@ +use clippy_utils::diagnostics::span_lint_and_help; +use clippy_utils::ty::{implements_trait, is_type_diagnostic_item}; +use clippy_utils::{get_trait_def_id, paths, return_ty, trait_ref_of_method}; use if_chain::if_chain; use rustc_hir::{ImplItem, ImplItemKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::sym; -use crate::utils::{ - get_trait_def_id, implements_trait, is_type_diagnostic_item, paths, return_ty, span_lint_and_help, - trait_ref_of_method, -}; - declare_clippy_lint! { /// **What it does:** Checks for the definition of inherent methods with a signature of `to_string(&self) -> String`. /// @@ -139,7 +137,7 @@ fn show_lint(cx: &LateContext<'_>, item: &ImplItem<'_>) { self_type.to_string() ), None, - &format!("remove the inherent method from type `{}`", self_type.to_string()) + &format!("remove the inherent method from type `{}`", self_type.to_string()), ); } else { span_lint_and_help( diff --git a/src/tools/clippy/clippy_lints/src/inline_fn_without_body.rs b/src/tools/clippy/clippy_lints/src/inline_fn_without_body.rs index d1c3fdc71461b..20f00bd51ba86 100644 --- a/src/tools/clippy/clippy_lints/src/inline_fn_without_body.rs +++ b/src/tools/clippy/clippy_lints/src/inline_fn_without_body.rs @@ -1,7 +1,7 @@ //! checks for `#[inline]` on trait methods without bodies -use crate::utils::span_lint_and_then; -use crate::utils::sugg::DiagnosticBuilderExt; +use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::sugg::DiagnosticBuilderExt; use rustc_ast::ast::Attribute; use rustc_errors::Applicability; use rustc_hir::{TraitFn, TraitItem, TraitItemKind}; @@ -34,7 +34,8 @@ declare_lint_pass!(InlineFnWithoutBody => [INLINE_FN_WITHOUT_BODY]); impl<'tcx> LateLintPass<'tcx> for InlineFnWithoutBody { fn check_trait_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx TraitItem<'_>) { if let TraitItemKind::Fn(_, TraitFn::Required(_)) = item.kind { - check_attrs(cx, item.ident.name, &item.attrs); + let attrs = cx.tcx.hir().attrs(item.hir_id()); + check_attrs(cx, item.ident.name, attrs); } } } diff --git a/src/tools/clippy/clippy_lints/src/int_plus_one.rs b/src/tools/clippy/clippy_lints/src/int_plus_one.rs index 260b8988d3711..c4a1222b51fbe 100644 --- a/src/tools/clippy/clippy_lints/src/int_plus_one.rs +++ b/src/tools/clippy/clippy_lints/src/int_plus_one.rs @@ -1,12 +1,12 @@ //! lint on blocks unnecessarily using >= with a + 1 or - 1 +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::source::snippet_opt; use rustc_ast::ast::{BinOpKind, Expr, ExprKind, Lit, LitKind}; use rustc_errors::Applicability; use rustc_lint::{EarlyContext, EarlyLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; -use crate::utils::{snippet_opt, span_lint_and_sugg}; - declare_clippy_lint! { /// **What it does:** Checks for usage of `x >= y + 1` or `x - 1 >= y` (and `<=`) in a block /// diff --git a/src/tools/clippy/clippy_lints/src/integer_division.rs b/src/tools/clippy/clippy_lints/src/integer_division.rs index 31181c10d23db..e5482f675e78b 100644 --- a/src/tools/clippy/clippy_lints/src/integer_division.rs +++ b/src/tools/clippy/clippy_lints/src/integer_division.rs @@ -1,4 +1,4 @@ -use crate::utils::span_lint_and_help; +use clippy_utils::diagnostics::span_lint_and_help; use if_chain::if_chain; use rustc_hir as hir; use rustc_lint::{LateContext, LateLintPass}; @@ -39,7 +39,7 @@ impl<'tcx> LateLintPass<'tcx> for IntegerDivision { expr.span, "integer division", None, - "division of integers may cause loss of precision. consider using floats.", + "division of integers may cause loss of precision. consider using floats", ); } } diff --git a/src/tools/clippy/clippy_lints/src/invalid_upcast_comparisons.rs b/src/tools/clippy/clippy_lints/src/invalid_upcast_comparisons.rs new file mode 100644 index 0000000000000..c67c02eefa5f6 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/invalid_upcast_comparisons.rs @@ -0,0 +1,221 @@ +use std::cmp::Ordering; + +use rustc_hir::{Expr, ExprKind}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::ty::{self, IntTy, UintTy}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::Span; +use rustc_target::abi::LayoutOf; + +use crate::consts::{constant, Constant}; + +use clippy_utils::comparisons::Rel; +use clippy_utils::diagnostics::span_lint; +use clippy_utils::source::snippet; +use clippy_utils::{comparisons, sext}; + +declare_clippy_lint! { + /// **What it does:** Checks for comparisons where the relation is always either + /// true or false, but where one side has been upcast so that the comparison is + /// necessary. Only integer types are checked. + /// + /// **Why is this bad?** An expression like `let x : u8 = ...; (x as u32) > 300` + /// will mistakenly imply that it is possible for `x` to be outside the range of + /// `u8`. + /// + /// **Known problems:** + /// https://github.com/rust-lang/rust-clippy/issues/886 + /// + /// **Example:** + /// ```rust + /// let x: u8 = 1; + /// (x as u32) > 300; + /// ``` + pub INVALID_UPCAST_COMPARISONS, + pedantic, + "a comparison involving an upcast which is always true or false" +} + +declare_lint_pass!(InvalidUpcastComparisons => [INVALID_UPCAST_COMPARISONS]); + +#[derive(Copy, Clone, Debug, Eq)] +enum FullInt { + S(i128), + U(u128), +} + +impl FullInt { + #[allow(clippy::cast_sign_loss)] + #[must_use] + fn cmp_s_u(s: i128, u: u128) -> Ordering { + if s < 0 { + Ordering::Less + } else if u > (i128::MAX as u128) { + Ordering::Greater + } else { + (s as u128).cmp(&u) + } + } +} + +impl PartialEq for FullInt { + #[must_use] + fn eq(&self, other: &Self) -> bool { + self.partial_cmp(other).expect("`partial_cmp` only returns `Some(_)`") == Ordering::Equal + } +} + +impl PartialOrd for FullInt { + #[must_use] + fn partial_cmp(&self, other: &Self) -> Option<Ordering> { + Some(match (self, other) { + (&Self::S(s), &Self::S(o)) => s.cmp(&o), + (&Self::U(s), &Self::U(o)) => s.cmp(&o), + (&Self::S(s), &Self::U(o)) => Self::cmp_s_u(s, o), + (&Self::U(s), &Self::S(o)) => Self::cmp_s_u(o, s).reverse(), + }) + } +} + +impl Ord for FullInt { + #[must_use] + fn cmp(&self, other: &Self) -> Ordering { + self.partial_cmp(other) + .expect("`partial_cmp` for FullInt can never return `None`") + } +} + +fn numeric_cast_precast_bounds<'a>(cx: &LateContext<'_>, expr: &'a Expr<'_>) -> Option<(FullInt, FullInt)> { + if let ExprKind::Cast(cast_exp, _) = expr.kind { + let pre_cast_ty = cx.typeck_results().expr_ty(cast_exp); + let cast_ty = cx.typeck_results().expr_ty(expr); + // if it's a cast from i32 to u32 wrapping will invalidate all these checks + if cx.layout_of(pre_cast_ty).ok().map(|l| l.size) == cx.layout_of(cast_ty).ok().map(|l| l.size) { + return None; + } + match pre_cast_ty.kind() { + ty::Int(int_ty) => Some(match int_ty { + IntTy::I8 => (FullInt::S(i128::from(i8::MIN)), FullInt::S(i128::from(i8::MAX))), + IntTy::I16 => (FullInt::S(i128::from(i16::MIN)), FullInt::S(i128::from(i16::MAX))), + IntTy::I32 => (FullInt::S(i128::from(i32::MIN)), FullInt::S(i128::from(i32::MAX))), + IntTy::I64 => (FullInt::S(i128::from(i64::MIN)), FullInt::S(i128::from(i64::MAX))), + IntTy::I128 => (FullInt::S(i128::MIN), FullInt::S(i128::MAX)), + IntTy::Isize => (FullInt::S(isize::MIN as i128), FullInt::S(isize::MAX as i128)), + }), + ty::Uint(uint_ty) => Some(match uint_ty { + UintTy::U8 => (FullInt::U(u128::from(u8::MIN)), FullInt::U(u128::from(u8::MAX))), + UintTy::U16 => (FullInt::U(u128::from(u16::MIN)), FullInt::U(u128::from(u16::MAX))), + UintTy::U32 => (FullInt::U(u128::from(u32::MIN)), FullInt::U(u128::from(u32::MAX))), + UintTy::U64 => (FullInt::U(u128::from(u64::MIN)), FullInt::U(u128::from(u64::MAX))), + UintTy::U128 => (FullInt::U(u128::MIN), FullInt::U(u128::MAX)), + UintTy::Usize => (FullInt::U(usize::MIN as u128), FullInt::U(usize::MAX as u128)), + }), + _ => None, + } + } else { + None + } +} + +fn node_as_const_fullint<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option<FullInt> { + let val = constant(cx, cx.typeck_results(), expr)?.0; + if let Constant::Int(const_int) = val { + match *cx.typeck_results().expr_ty(expr).kind() { + ty::Int(ity) => Some(FullInt::S(sext(cx.tcx, const_int, ity))), + ty::Uint(_) => Some(FullInt::U(const_int)), + _ => None, + } + } else { + None + } +} + +fn err_upcast_comparison(cx: &LateContext<'_>, span: Span, expr: &Expr<'_>, always: bool) { + if let ExprKind::Cast(cast_val, _) = expr.kind { + span_lint( + cx, + INVALID_UPCAST_COMPARISONS, + span, + &format!( + "because of the numeric bounds on `{}` prior to casting, this expression is always {}", + snippet(cx, cast_val.span, "the expression"), + if always { "true" } else { "false" }, + ), + ); + } +} + +fn upcast_comparison_bounds_err<'tcx>( + cx: &LateContext<'tcx>, + span: Span, + rel: comparisons::Rel, + lhs_bounds: Option<(FullInt, FullInt)>, + lhs: &'tcx Expr<'_>, + rhs: &'tcx Expr<'_>, + invert: bool, +) { + if let Some((lb, ub)) = lhs_bounds { + if let Some(norm_rhs_val) = node_as_const_fullint(cx, rhs) { + if rel == Rel::Eq || rel == Rel::Ne { + if norm_rhs_val < lb || norm_rhs_val > ub { + err_upcast_comparison(cx, span, lhs, rel == Rel::Ne); + } + } else if match rel { + Rel::Lt => { + if invert { + norm_rhs_val < lb + } else { + ub < norm_rhs_val + } + }, + Rel::Le => { + if invert { + norm_rhs_val <= lb + } else { + ub <= norm_rhs_val + } + }, + Rel::Eq | Rel::Ne => unreachable!(), + } { + err_upcast_comparison(cx, span, lhs, true) + } else if match rel { + Rel::Lt => { + if invert { + norm_rhs_val >= ub + } else { + lb >= norm_rhs_val + } + }, + Rel::Le => { + if invert { + norm_rhs_val > ub + } else { + lb > norm_rhs_val + } + }, + Rel::Eq | Rel::Ne => unreachable!(), + } { + err_upcast_comparison(cx, span, lhs, false) + } + } + } +} + +impl<'tcx> LateLintPass<'tcx> for InvalidUpcastComparisons { + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { + if let ExprKind::Binary(ref cmp, lhs, rhs) = expr.kind { + let normalized = comparisons::normalize_comparison(cmp.node, lhs, rhs); + let (rel, normalized_lhs, normalized_rhs) = if let Some(val) = normalized { + val + } else { + return; + }; + + let lhs_bounds = numeric_cast_precast_bounds(cx, normalized_lhs); + let rhs_bounds = numeric_cast_precast_bounds(cx, normalized_rhs); + + upcast_comparison_bounds_err(cx, expr.span, rel, lhs_bounds, normalized_lhs, normalized_rhs, false); + upcast_comparison_bounds_err(cx, expr.span, rel, rhs_bounds, normalized_rhs, normalized_lhs, true); + } + } +} diff --git a/src/tools/clippy/clippy_lints/src/items_after_statements.rs b/src/tools/clippy/clippy_lints/src/items_after_statements.rs index 0927d218446dd..c69571f32a244 100644 --- a/src/tools/clippy/clippy_lints/src/items_after_statements.rs +++ b/src/tools/clippy/clippy_lints/src/items_after_statements.rs @@ -1,6 +1,6 @@ //! lint when items are used after statements -use crate::utils::span_lint; +use clippy_utils::diagnostics::span_lint; use rustc_ast::ast::{Block, ItemKind, StmtKind}; use rustc_lint::{EarlyContext, EarlyLintPass, LintContext}; use rustc_middle::lint::in_external_macro; diff --git a/src/tools/clippy/clippy_lints/src/large_const_arrays.rs b/src/tools/clippy/clippy_lints/src/large_const_arrays.rs index a76595ed0897d..48dc5fefe9978 100644 --- a/src/tools/clippy/clippy_lints/src/large_const_arrays.rs +++ b/src/tools/clippy/clippy_lints/src/large_const_arrays.rs @@ -1,5 +1,5 @@ use crate::rustc_target::abi::LayoutOf; -use crate::utils::span_lint_and_then; +use clippy_utils::diagnostics::span_lint_and_then; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::{Item, ItemKind}; diff --git a/src/tools/clippy/clippy_lints/src/large_enum_variant.rs b/src/tools/clippy/clippy_lints/src/large_enum_variant.rs index ab4cb33612d38..f166748d86b81 100644 --- a/src/tools/clippy/clippy_lints/src/large_enum_variant.rs +++ b/src/tools/clippy/clippy_lints/src/large_enum_variant.rs @@ -1,6 +1,7 @@ //! lint when there is a large size difference between variants on an enum -use crate::utils::{snippet_opt, span_lint_and_then}; +use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::source::snippet_opt; use rustc_errors::Applicability; use rustc_hir::{Item, ItemKind, VariantData}; use rustc_lint::{LateContext, LateLintPass}; @@ -112,7 +113,7 @@ impl<'tcx> LateLintPass<'tcx> for LargeEnumVariant { ); if variant.fields.len() == 1 { let span = match def.variants[i].data { - VariantData::Struct(ref fields, ..) | VariantData::Tuple(ref fields, ..) => { + VariantData::Struct(fields, ..) | VariantData::Tuple(fields, ..) => { fields[0].ty.span }, VariantData::Unit(..) => unreachable!(), diff --git a/src/tools/clippy/clippy_lints/src/large_stack_arrays.rs b/src/tools/clippy/clippy_lints/src/large_stack_arrays.rs index 9a448ab125686..c46b98022c6ca 100644 --- a/src/tools/clippy/clippy_lints/src/large_stack_arrays.rs +++ b/src/tools/clippy/clippy_lints/src/large_stack_arrays.rs @@ -1,13 +1,13 @@ +use clippy_utils::diagnostics::span_lint_and_help; +use clippy_utils::source::snippet; +use if_chain::if_chain; use rustc_hir::{Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::mir::interpret::ConstValue; use rustc_middle::ty::{self, ConstKind}; use rustc_session::{declare_tool_lint, impl_lint_pass}; -use if_chain::if_chain; - use crate::rustc_target::abi::LayoutOf; -use crate::utils::{snippet, span_lint_and_help}; declare_clippy_lint! { /// **What it does:** Checks for local arrays that may be too large. diff --git a/src/tools/clippy/clippy_lints/src/len_zero.rs b/src/tools/clippy/clippy_lints/src/len_zero.rs index dab3e0565cafb..bb57adff7bea1 100644 --- a/src/tools/clippy/clippy_lints/src/len_zero.rs +++ b/src/tools/clippy/clippy_lints/src/len_zero.rs @@ -1,13 +1,21 @@ -use crate::utils::{get_item_name, snippet_with_applicability, span_lint, span_lint_and_sugg}; +use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg, span_lint_and_then}; +use clippy_utils::source::snippet_with_applicability; +use clippy_utils::{get_item_name, get_parent_as_impl, is_allowed}; +use if_chain::if_chain; use rustc_ast::ast::LitKind; -use rustc_data_structures::fx::FxHashSet; use rustc_errors::Applicability; -use rustc_hir::def_id::DefId; -use rustc_hir::{AssocItemKind, BinOpKind, Expr, ExprKind, Impl, ImplItemRef, Item, ItemKind, TraitItemRef}; +use rustc_hir::def_id::DefIdSet; +use rustc_hir::{ + def_id::DefId, AssocItemKind, BinOpKind, Expr, ExprKind, FnRetTy, ImplItem, ImplItemKind, ImplicitSelfKind, Item, + ItemKind, Mutability, Node, TraitItemRef, TyKind, +}; use rustc_lint::{LateContext, LateLintPass}; -use rustc_middle::ty; +use rustc_middle::ty::{self, AssocKind, FnSig, Ty, TyS}; use rustc_session::{declare_lint_pass, declare_tool_lint}; -use rustc_span::source_map::{Span, Spanned, Symbol}; +use rustc_span::{ + source_map::{Span, Spanned, Symbol}, + symbol::sym, +}; declare_clippy_lint! { /// **What it does:** Checks for getting the length of something via `.len()` @@ -113,14 +121,39 @@ impl<'tcx> LateLintPass<'tcx> for LenZero { return; } - match item.kind { - ItemKind::Trait(_, _, _, _, ref trait_items) => check_trait_items(cx, item, trait_items), - ItemKind::Impl(Impl { - of_trait: None, - items: ref impl_items, - .. - }) => check_impl_items(cx, item, impl_items), - _ => (), + if let ItemKind::Trait(_, _, _, _, trait_items) = item.kind { + check_trait_items(cx, item, trait_items); + } + } + + fn check_impl_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx ImplItem<'_>) { + if_chain! { + if item.ident.as_str() == "len"; + if let ImplItemKind::Fn(sig, _) = &item.kind; + if sig.decl.implicit_self.has_implicit_self(); + if cx.access_levels.is_exported(item.hir_id()); + if matches!(sig.decl.output, FnRetTy::Return(_)); + if let Some(imp) = get_parent_as_impl(cx.tcx, item.hir_id()); + if imp.of_trait.is_none(); + if let TyKind::Path(ty_path) = &imp.self_ty.kind; + if let Some(ty_id) = cx.qpath_res(ty_path, imp.self_ty.hir_id).opt_def_id(); + if let Some(local_id) = ty_id.as_local(); + let ty_hir_id = cx.tcx.hir().local_def_id_to_hir_id(local_id); + if !is_allowed(cx, LEN_WITHOUT_IS_EMPTY, ty_hir_id); + if let Some(output) = parse_len_output(cx, cx.tcx.fn_sig(item.def_id).skip_binder()); + then { + let (name, kind) = match cx.tcx.hir().find(ty_hir_id) { + Some(Node::ForeignItem(x)) => (x.ident.name, "extern type"), + Some(Node::Item(x)) => match x.kind { + ItemKind::Struct(..) => (x.ident.name, "struct"), + ItemKind::Enum(..) => (x.ident.name, "enum"), + ItemKind::Union(..) => (x.ident.name, "union"), + _ => (x.ident.name, "type"), + } + _ => return, + }; + check_for_is_empty(cx, sig.span, sig.decl.implicit_self, output, ty_id, name, kind) + } } } @@ -129,7 +162,7 @@ impl<'tcx> LateLintPass<'tcx> for LenZero { return; } - if let ExprKind::Binary(Spanned { node: cmp, .. }, ref left, ref right) = expr.kind { + if let ExprKind::Binary(Spanned { node: cmp, .. }, left, right) = expr.kind { match cmp { BinOpKind::Eq => { check_cmp(cx, expr.span, left, right, "", 0); // len == 0 @@ -166,7 +199,7 @@ fn check_trait_items(cx: &LateContext<'_>, visited_trait: &Item<'_>, trait_items } // fill the set with current and super traits - fn fill_trait_set(traitt: DefId, set: &mut FxHashSet<DefId>, cx: &LateContext<'_>) { + fn fill_trait_set(traitt: DefId, set: &mut DefIdSet, cx: &LateContext<'_>) { if set.insert(traitt) { for supertrait in rustc_trait_selection::traits::supertrait_def_ids(cx.tcx, traitt) { fill_trait_set(supertrait, set, cx); @@ -175,7 +208,7 @@ fn check_trait_items(cx: &LateContext<'_>, visited_trait: &Item<'_>, trait_items } if cx.access_levels.is_exported(visited_trait.hir_id()) && trait_items.iter().any(|i| is_named_self(cx, i, "len")) { - let mut current_and_super_traits = FxHashSet::default(); + 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_method_found = current_and_super_traits @@ -202,45 +235,144 @@ fn check_trait_items(cx: &LateContext<'_>, visited_trait: &Item<'_>, trait_items } } -fn check_impl_items(cx: &LateContext<'_>, item: &Item<'_>, impl_items: &[ImplItemRef<'_>]) { - fn is_named_self(cx: &LateContext<'_>, item: &ImplItemRef<'_>, name: &str) -> bool { - item.ident.name.as_str() == name - && if let AssocItemKind::Fn { has_self } = item.kind { - has_self && cx.tcx.fn_sig(item.id.def_id).inputs().skip_binder().len() == 1 - } else { - false - } +#[derive(Debug, Clone, Copy)] +enum LenOutput<'tcx> { + Integral, + Option(DefId), + Result(DefId, Ty<'tcx>), +} +fn parse_len_output(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_type, adt.did) => { + subs.type_at(0).is_integral().then(|| LenOutput::Option(adt.did)) + }, + ty::Adt(adt, subs) if cx.tcx.is_diagnostic_item(sym::result_type, adt.did) => subs + .type_at(0) + .is_integral() + .then(|| LenOutput::Result(adt.did, subs.type_at(1))), + _ => None, } +} - let is_empty = if let Some(is_empty) = impl_items.iter().find(|i| is_named_self(cx, i, "is_empty")) { - if cx.access_levels.is_exported(is_empty.id.hir_id()) { - return; +impl LenOutput<'_> { + fn matches_is_empty_output(self, ty: Ty<'_>) -> bool { + match (self, ty.kind()) { + (_, &ty::Bool) => true, + (Self::Option(id), &ty::Adt(adt, subs)) if id == adt.did => subs.type_at(0).is_bool(), + (Self::Result(id, err_ty), &ty::Adt(adt, subs)) if id == adt.did => { + subs.type_at(0).is_bool() && TyS::same_type(subs.type_at(1), err_ty) + }, + _ => false, } - "a private" - } else { - "no corresponding" - }; + } - if let Some(i) = impl_items.iter().find(|i| is_named_self(cx, i, "len")) { - if cx.access_levels.is_exported(i.id.hir_id()) { - let ty = cx.tcx.type_of(item.def_id); + fn expected_sig(self, self_kind: ImplicitSelfKind) -> String { + let self_ref = match self_kind { + ImplicitSelfKind::ImmRef => "&", + ImplicitSelfKind::MutRef => "&mut ", + _ => "", + }; + match self { + Self::Integral => format!("expected signature: `({}self) -> bool`", self_ref), + Self::Option(_) => format!( + "expected signature: `({}self) -> bool` or `({}self) -> Option<bool>", + self_ref, self_ref + ), + Self::Result(..) => format!( + "expected signature: `({}self) -> bool` or `({}self) -> Result<bool>", + self_ref, self_ref + ), + } + } +} - span_lint( - cx, - LEN_WITHOUT_IS_EMPTY, - item.span, - &format!( - "item `{}` has a public `len` method but {} `is_empty` method", - ty, is_empty +/// Checks if the given signature matches the expectations for `is_empty` +fn check_is_empty_sig(sig: FnSig<'_>, self_kind: ImplicitSelfKind, len_output: LenOutput<'_>) -> bool { + match &**sig.inputs_and_output { + [arg, res] if len_output.matches_is_empty_output(res) => { + matches!( + (arg.kind(), self_kind), + (ty::Ref(_, _, Mutability::Not), ImplicitSelfKind::ImmRef) + | (ty::Ref(_, _, Mutability::Mut), ImplicitSelfKind::MutRef) + ) || (!arg.is_ref() && matches!(self_kind, ImplicitSelfKind::Imm | ImplicitSelfKind::Mut)) + }, + _ => false, + } +} + +/// Checks if the given type has an `is_empty` method with the appropriate signature. +fn check_for_is_empty( + cx: &LateContext<'_>, + span: Span, + self_kind: ImplicitSelfKind, + output: LenOutput<'_>, + impl_ty: DefId, + item_name: Symbol, + item_kind: &str, +) { + let is_empty = Symbol::intern("is_empty"); + let is_empty = cx + .tcx + .inherent_impls(impl_ty) + .iter() + .flat_map(|&id| cx.tcx.associated_items(id).filter_by_name_unhygienic(is_empty)) + .find(|item| item.kind == AssocKind::Fn); + + let (msg, is_empty_span, self_kind) = match is_empty { + None => ( + format!( + "{} `{}` has a public `len` method, but no `is_empty` method", + item_kind, + item_name.as_str(), + ), + None, + None, + ), + Some(is_empty) + if !cx + .access_levels + .is_exported(cx.tcx.hir().local_def_id_to_hir_id(is_empty.def_id.expect_local())) => + { + ( + format!( + "{} `{}` has a public `len` method, but a private `is_empty` method", + item_kind, + item_name.as_str(), ), - ); + Some(cx.tcx.def_span(is_empty.def_id)), + None, + ) + }, + Some(is_empty) + if !(is_empty.fn_has_self_parameter + && check_is_empty_sig(cx.tcx.fn_sig(is_empty.def_id).skip_binder(), self_kind, output)) => + { + ( + format!( + "{} `{}` has a public `len` method, but the `is_empty` method has an unexpected signature", + item_kind, + item_name.as_str(), + ), + Some(cx.tcx.def_span(is_empty.def_id)), + Some(self_kind), + ) + }, + Some(_) => return, + }; + + span_lint_and_then(cx, LEN_WITHOUT_IS_EMPTY, span, &msg, |db| { + if let Some(span) = is_empty_span { + db.span_note(span, "`is_empty` defined here"); } - } + if let Some(self_kind) = self_kind { + db.note(&output.expected_sig(self_kind)); + } + }); } fn check_cmp(cx: &LateContext<'_>, span: Span, method: &Expr<'_>, lit: &Expr<'_>, op: &str, compare_to: u32) { - if let (&ExprKind::MethodCall(ref method_path, _, ref args, _), &ExprKind::Lit(ref lit)) = (&method.kind, &lit.kind) - { + if let (&ExprKind::MethodCall(method_path, _, args, _), &ExprKind::Lit(ref lit)) = (&method.kind, &lit.kind) { // check if we are in an is_empty() method if let Some(name) = get_item_name(cx, method) { if name.as_str() == "is_empty" { @@ -318,7 +450,7 @@ fn is_empty_string(expr: &Expr<'_>) -> bool { } fn is_empty_array(expr: &Expr<'_>) -> bool { - if let ExprKind::Array(ref arr) = expr.kind { + if let ExprKind::Array(arr) = expr.kind { return arr.is_empty(); } false @@ -347,17 +479,17 @@ fn has_is_empty(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { cx.tcx .associated_items(*imp) .in_definition_order() - .any(|item| is_is_empty(cx, &item)) + .any(|item| is_is_empty(cx, item)) }) } let ty = &cx.typeck_results().expr_ty(expr).peel_refs(); match ty.kind() { - ty::Dynamic(ref tt, ..) => tt.principal().map_or(false, |principal| { + ty::Dynamic(tt, ..) => tt.principal().map_or(false, |principal| { cx.tcx .associated_items(principal.def_id()) .in_definition_order() - .any(|item| is_is_empty(cx, &item)) + .any(|item| is_is_empty(cx, item)) }), ty::Projection(ref proj) => has_is_empty_impl(cx, proj.item_def_id), ty::Adt(id, _) => has_is_empty_impl(cx, id.did), diff --git a/src/tools/clippy/clippy_lints/src/let_if_seq.rs b/src/tools/clippy/clippy_lints/src/let_if_seq.rs index 5863eef8a26f8..67eae4d87bbdb 100644 --- a/src/tools/clippy/clippy_lints/src/let_if_seq.rs +++ b/src/tools/clippy/clippy_lints/src/let_if_seq.rs @@ -1,4 +1,6 @@ -use crate::utils::{path_to_local_id, snippet, span_lint_and_then, visitors::LocalUsedVisitor}; +use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::source::snippet; +use clippy_utils::{path_to_local_id, visitors::LocalUsedVisitor}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir as hir; @@ -59,13 +61,13 @@ impl<'tcx> LateLintPass<'tcx> for LetIfSeq { while let Some(stmt) = it.next() { if_chain! { if let Some(expr) = it.peek(); - if let hir::StmtKind::Local(ref local) = stmt.kind; + if let hir::StmtKind::Local(local) = stmt.kind; if let hir::PatKind::Binding(mode, canonical_id, ident, None) = local.pat.kind; - if let hir::StmtKind::Expr(ref if_) = expr.kind; - if let hir::ExprKind::If(ref cond, ref then, ref else_) = if_.kind; + if let hir::StmtKind::Expr(if_) = expr.kind; + if let hir::ExprKind::If(cond, then, ref else_) = if_.kind; let mut used_visitor = LocalUsedVisitor::new(cx, canonical_id); if !used_visitor.check_expr(cond); - if let hir::ExprKind::Block(ref then, _) = then.kind; + if let hir::ExprKind::Block(then, _) = then.kind; if let Some(value) = check_assign(cx, canonical_id, &*then); if !used_visitor.check_expr(value); then { @@ -77,20 +79,20 @@ impl<'tcx> LateLintPass<'tcx> for LetIfSeq { ); if has_interior_mutability { return; } - let (default_multi_stmts, default) = if let Some(ref else_) = *else_ { - if let hir::ExprKind::Block(ref else_, _) = else_.kind { + let (default_multi_stmts, default) = if let Some(else_) = *else_ { + if let hir::ExprKind::Block(else_, _) = else_.kind { if let Some(default) = check_assign(cx, canonical_id, else_) { (else_.stmts.len() > 1, default) - } else if let Some(ref default) = local.init { - (true, &**default) + } else if let Some(default) = local.init { + (true, default) } else { continue; } } else { continue; } - } else if let Some(ref default) = local.init { - (false, &**default) + } else if let Some(default) = local.init { + (false, default) } else { continue; }; @@ -142,8 +144,8 @@ fn check_assign<'tcx>( if_chain! { if block.expr.is_none(); if let Some(expr) = block.stmts.iter().last(); - if let hir::StmtKind::Semi(ref expr) = expr.kind; - if let hir::ExprKind::Assign(ref var, ref value, _) = expr.kind; + if let hir::StmtKind::Semi(expr) = expr.kind; + if let hir::ExprKind::Assign(var, value, _) = expr.kind; if path_to_local_id(var, decl); then { let mut v = LocalUsedVisitor::new(cx, decl); diff --git a/src/tools/clippy/clippy_lints/src/let_underscore.rs b/src/tools/clippy/clippy_lints/src/let_underscore.rs index 7e96dfcc7da0f..17e23781db7d6 100644 --- a/src/tools/clippy/clippy_lints/src/let_underscore.rs +++ b/src/tools/clippy/clippy_lints/src/let_underscore.rs @@ -1,3 +1,6 @@ +use clippy_utils::diagnostics::span_lint_and_help; +use clippy_utils::ty::{is_must_use_ty, match_type}; +use clippy_utils::{is_must_use_func_call, paths}; use if_chain::if_chain; use rustc_hir::{Local, PatKind}; use rustc_lint::{LateContext, LateLintPass}; @@ -5,8 +8,6 @@ use rustc_middle::lint::in_external_macro; use rustc_middle::ty::subst::GenericArgKind; use rustc_session::{declare_lint_pass, declare_tool_lint}; -use crate::utils::{is_must_use_func_call, is_must_use_ty, match_type, paths, span_lint_and_help}; - declare_clippy_lint! { /// **What it does:** Checks for `let _ = <expr>` /// where expr is #[must_use] @@ -115,7 +116,7 @@ impl<'tcx> LateLintPass<'tcx> for LetUnderscore { if_chain! { if let PatKind::Wild = local.pat.kind; - if let Some(ref init) = local.init; + if let Some(init) = local.init; then { let init_ty = cx.typeck_results().expr_ty(init); let contains_sync_guard = init_ty.walk().any(|inner| match inner.unpack() { diff --git a/src/tools/clippy/clippy_lints/src/lib.rs b/src/tools/clippy/clippy_lints/src/lib.rs index 176eeadcc630a..11fef30945d78 100644 --- a/src/tools/clippy/clippy_lints/src/lib.rs +++ b/src/tools/clippy/clippy_lints/src/lib.rs @@ -4,8 +4,9 @@ #![feature(box_syntax)] #![feature(drain_filter)] #![feature(in_band_lifetimes)] +#![feature(iter_zip)] #![feature(once_cell)] -#![feature(or_patterns)] +#![cfg_attr(bootstrap, feature(or_patterns))] #![feature(rustc_private)] #![feature(stmt_expr_attributes)] #![feature(control_flow_enum)] @@ -22,7 +23,6 @@ // (Currently there is no way to opt into sysroot crates without `extern crate`.) extern crate rustc_ast; extern crate rustc_ast_pretty; -extern crate rustc_attr; extern crate rustc_data_structures; extern crate rustc_driver; extern crate rustc_errors; @@ -42,7 +42,7 @@ extern crate rustc_target; extern crate rustc_trait_selection; extern crate rustc_typeck; -use crate::utils::parse_msrv; +use clippy_utils::parse_msrv; use rustc_data_structures::fx::FxHashSet; use rustc_lint::LintId; use rustc_session::Session; @@ -165,6 +165,7 @@ mod consts; mod utils; // begin lints modules, do not remove this comment, it’s used in `update_lints` +mod absurd_extreme_comparisons; mod approx_const; mod arithmetic; mod as_conversions; @@ -182,6 +183,7 @@ mod booleans; mod bytecount; mod cargo_common_metadata; mod case_sensitive_file_extension_comparisons; +mod casts; mod checked_conversions; mod cognitive_complexity; mod collapsible_if; @@ -230,6 +232,8 @@ mod identity_op; mod if_let_mutex; mod if_let_some_result; mod if_not_else; +mod if_then_some_else_none; +mod implicit_hasher; mod implicit_return; mod implicit_saturating_sub; mod inconsistent_struct_constructor; @@ -240,6 +244,7 @@ mod inherent_to_string; mod inline_fn_without_body; mod int_plus_one; mod integer_division; +mod invalid_upcast_comparisons; mod items_after_statements; mod large_const_arrays; mod large_enum_variant; @@ -287,6 +292,7 @@ mod needless_bool; mod needless_borrow; mod needless_borrowed_ref; mod needless_continue; +mod needless_for_each; mod needless_pass_by_value; mod needless_question_mark; mod needless_update; @@ -296,6 +302,7 @@ mod new_without_default; mod no_effect; mod non_copy_const; mod non_expressive_names; +mod non_octal_unix_permissions; mod open_options; mod option_env_unwrap; mod option_if_let_else; @@ -348,6 +355,7 @@ mod types; mod undropped_manually_drops; mod unicode; mod unit_return_expecting_ord; +mod unit_types; mod unnamed_address; mod unnecessary_sort_by; mod unnecessary_wraps; @@ -535,473 +543,482 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: // begin register lints, do not remove this comment, it’s used in `update_lints` store.register_lints(&[ #[cfg(feature = "internal-lints")] - &utils::internal_lints::CLIPPY_LINTS_INTERNAL, + utils::internal_lints::CLIPPY_LINTS_INTERNAL, #[cfg(feature = "internal-lints")] - &utils::internal_lints::COLLAPSIBLE_SPAN_LINT_CALLS, + utils::internal_lints::COLLAPSIBLE_SPAN_LINT_CALLS, #[cfg(feature = "internal-lints")] - &utils::internal_lints::COMPILER_LINT_FUNCTIONS, + utils::internal_lints::COMPILER_LINT_FUNCTIONS, #[cfg(feature = "internal-lints")] - &utils::internal_lints::DEFAULT_LINT, + utils::internal_lints::DEFAULT_LINT, #[cfg(feature = "internal-lints")] - &utils::internal_lints::INTERNING_DEFINED_SYMBOL, + utils::internal_lints::IF_CHAIN_STYLE, #[cfg(feature = "internal-lints")] - &utils::internal_lints::INVALID_PATHS, + utils::internal_lints::INTERNING_DEFINED_SYMBOL, #[cfg(feature = "internal-lints")] - &utils::internal_lints::LINT_WITHOUT_LINT_PASS, + utils::internal_lints::INVALID_PATHS, #[cfg(feature = "internal-lints")] - &utils::internal_lints::MATCH_TYPE_ON_DIAGNOSTIC_ITEM, + utils::internal_lints::LINT_WITHOUT_LINT_PASS, #[cfg(feature = "internal-lints")] - &utils::internal_lints::OUTER_EXPN_EXPN_DATA, + utils::internal_lints::MATCH_TYPE_ON_DIAGNOSTIC_ITEM, #[cfg(feature = "internal-lints")] - &utils::internal_lints::PRODUCE_ICE, + utils::internal_lints::OUTER_EXPN_EXPN_DATA, #[cfg(feature = "internal-lints")] - &utils::internal_lints::UNNECESSARY_SYMBOL_STR, - &approx_const::APPROX_CONSTANT, - &arithmetic::FLOAT_ARITHMETIC, - &arithmetic::INTEGER_ARITHMETIC, - &as_conversions::AS_CONVERSIONS, - &asm_syntax::INLINE_ASM_X86_ATT_SYNTAX, - &asm_syntax::INLINE_ASM_X86_INTEL_SYNTAX, - &assertions_on_constants::ASSERTIONS_ON_CONSTANTS, - &assign_ops::ASSIGN_OP_PATTERN, - &assign_ops::MISREFACTORED_ASSIGN_OP, - &async_yields_async::ASYNC_YIELDS_ASYNC, - &atomic_ordering::INVALID_ATOMIC_ORDERING, - &attrs::BLANKET_CLIPPY_RESTRICTION_LINTS, - &attrs::DEPRECATED_CFG_ATTR, - &attrs::DEPRECATED_SEMVER, - &attrs::EMPTY_LINE_AFTER_OUTER_ATTR, - &attrs::INLINE_ALWAYS, - &attrs::MISMATCHED_TARGET_OS, - &attrs::USELESS_ATTRIBUTE, - &await_holding_invalid::AWAIT_HOLDING_LOCK, - &await_holding_invalid::AWAIT_HOLDING_REFCELL_REF, - &bit_mask::BAD_BIT_MASK, - &bit_mask::INEFFECTIVE_BIT_MASK, - &bit_mask::VERBOSE_BIT_MASK, - &blacklisted_name::BLACKLISTED_NAME, - &blocks_in_if_conditions::BLOCKS_IN_IF_CONDITIONS, - &booleans::LOGIC_BUG, - &booleans::NONMINIMAL_BOOL, - &bytecount::NAIVE_BYTECOUNT, - &cargo_common_metadata::CARGO_COMMON_METADATA, - &case_sensitive_file_extension_comparisons::CASE_SENSITIVE_FILE_EXTENSION_COMPARISONS, - &checked_conversions::CHECKED_CONVERSIONS, - &cognitive_complexity::COGNITIVE_COMPLEXITY, - &collapsible_if::COLLAPSIBLE_ELSE_IF, - &collapsible_if::COLLAPSIBLE_IF, - &collapsible_match::COLLAPSIBLE_MATCH, - &comparison_chain::COMPARISON_CHAIN, - &copies::IFS_SAME_COND, - &copies::IF_SAME_THEN_ELSE, - &copies::SAME_FUNCTIONS_IN_IF_CONDITION, - ©_iterator::COPY_ITERATOR, - &create_dir::CREATE_DIR, - &dbg_macro::DBG_MACRO, - &default::DEFAULT_TRAIT_ACCESS, - &default::FIELD_REASSIGN_WITH_DEFAULT, - &default_numeric_fallback::DEFAULT_NUMERIC_FALLBACK, - &dereference::EXPLICIT_DEREF_METHODS, - &derive::DERIVE_HASH_XOR_EQ, - &derive::DERIVE_ORD_XOR_PARTIAL_ORD, - &derive::EXPL_IMPL_CLONE_ON_COPY, - &derive::UNSAFE_DERIVE_DESERIALIZE, - &disallowed_method::DISALLOWED_METHOD, - &doc::DOC_MARKDOWN, - &doc::MISSING_ERRORS_DOC, - &doc::MISSING_PANICS_DOC, - &doc::MISSING_SAFETY_DOC, - &doc::NEEDLESS_DOCTEST_MAIN, - &double_comparison::DOUBLE_COMPARISONS, - &double_parens::DOUBLE_PARENS, - &drop_forget_ref::DROP_COPY, - &drop_forget_ref::DROP_REF, - &drop_forget_ref::FORGET_COPY, - &drop_forget_ref::FORGET_REF, - &duration_subsec::DURATION_SUBSEC, - &else_if_without_else::ELSE_IF_WITHOUT_ELSE, - &empty_enum::EMPTY_ENUM, - &entry::MAP_ENTRY, - &enum_clike::ENUM_CLIKE_UNPORTABLE_VARIANT, - &enum_variants::ENUM_VARIANT_NAMES, - &enum_variants::MODULE_INCEPTION, - &enum_variants::MODULE_NAME_REPETITIONS, - &enum_variants::PUB_ENUM_VARIANT_NAMES, - &eq_op::EQ_OP, - &eq_op::OP_REF, - &erasing_op::ERASING_OP, - &escape::BOXED_LOCAL, - &eta_reduction::REDUNDANT_CLOSURE, - &eta_reduction::REDUNDANT_CLOSURE_FOR_METHOD_CALLS, - &eval_order_dependence::DIVERGING_SUB_EXPRESSION, - &eval_order_dependence::EVAL_ORDER_DEPENDENCE, - &excessive_bools::FN_PARAMS_EXCESSIVE_BOOLS, - &excessive_bools::STRUCT_EXCESSIVE_BOOLS, - &exhaustive_items::EXHAUSTIVE_ENUMS, - &exhaustive_items::EXHAUSTIVE_STRUCTS, - &exit::EXIT, - &explicit_write::EXPLICIT_WRITE, - &fallible_impl_from::FALLIBLE_IMPL_FROM, - &float_equality_without_abs::FLOAT_EQUALITY_WITHOUT_ABS, - &float_literal::EXCESSIVE_PRECISION, - &float_literal::LOSSY_FLOAT_LITERAL, - &floating_point_arithmetic::IMPRECISE_FLOPS, - &floating_point_arithmetic::SUBOPTIMAL_FLOPS, - &format::USELESS_FORMAT, - &formatting::POSSIBLE_MISSING_COMMA, - &formatting::SUSPICIOUS_ASSIGNMENT_FORMATTING, - &formatting::SUSPICIOUS_ELSE_FORMATTING, - &formatting::SUSPICIOUS_UNARY_OP_FORMATTING, - &from_over_into::FROM_OVER_INTO, - &from_str_radix_10::FROM_STR_RADIX_10, - &functions::DOUBLE_MUST_USE, - &functions::MUST_USE_CANDIDATE, - &functions::MUST_USE_UNIT, - &functions::NOT_UNSAFE_PTR_ARG_DEREF, - &functions::RESULT_UNIT_ERR, - &functions::TOO_MANY_ARGUMENTS, - &functions::TOO_MANY_LINES, - &future_not_send::FUTURE_NOT_SEND, - &get_last_with_len::GET_LAST_WITH_LEN, - &identity_op::IDENTITY_OP, - &if_let_mutex::IF_LET_MUTEX, - &if_let_some_result::IF_LET_SOME_RESULT, - &if_not_else::IF_NOT_ELSE, - &implicit_return::IMPLICIT_RETURN, - &implicit_saturating_sub::IMPLICIT_SATURATING_SUB, - &inconsistent_struct_constructor::INCONSISTENT_STRUCT_CONSTRUCTOR, - &indexing_slicing::INDEXING_SLICING, - &indexing_slicing::OUT_OF_BOUNDS_INDEXING, - &infinite_iter::INFINITE_ITER, - &infinite_iter::MAYBE_INFINITE_ITER, - &inherent_impl::MULTIPLE_INHERENT_IMPL, - &inherent_to_string::INHERENT_TO_STRING, - &inherent_to_string::INHERENT_TO_STRING_SHADOW_DISPLAY, - &inline_fn_without_body::INLINE_FN_WITHOUT_BODY, - &int_plus_one::INT_PLUS_ONE, - &integer_division::INTEGER_DIVISION, - &items_after_statements::ITEMS_AFTER_STATEMENTS, - &large_const_arrays::LARGE_CONST_ARRAYS, - &large_enum_variant::LARGE_ENUM_VARIANT, - &large_stack_arrays::LARGE_STACK_ARRAYS, - &len_zero::COMPARISON_TO_EMPTY, - &len_zero::LEN_WITHOUT_IS_EMPTY, - &len_zero::LEN_ZERO, - &let_if_seq::USELESS_LET_IF_SEQ, - &let_underscore::LET_UNDERSCORE_DROP, - &let_underscore::LET_UNDERSCORE_LOCK, - &let_underscore::LET_UNDERSCORE_MUST_USE, - &lifetimes::EXTRA_UNUSED_LIFETIMES, - &lifetimes::NEEDLESS_LIFETIMES, - &literal_representation::DECIMAL_LITERAL_REPRESENTATION, - &literal_representation::INCONSISTENT_DIGIT_GROUPING, - &literal_representation::LARGE_DIGIT_GROUPS, - &literal_representation::MISTYPED_LITERAL_SUFFIXES, - &literal_representation::UNREADABLE_LITERAL, - &literal_representation::UNUSUAL_BYTE_GROUPINGS, - &loops::EMPTY_LOOP, - &loops::EXPLICIT_COUNTER_LOOP, - &loops::EXPLICIT_INTO_ITER_LOOP, - &loops::EXPLICIT_ITER_LOOP, - &loops::FOR_KV_MAP, - &loops::FOR_LOOPS_OVER_FALLIBLES, - &loops::ITER_NEXT_LOOP, - &loops::MANUAL_FLATTEN, - &loops::MANUAL_MEMCPY, - &loops::MUT_RANGE_BOUND, - &loops::NEEDLESS_COLLECT, - &loops::NEEDLESS_RANGE_LOOP, - &loops::NEVER_LOOP, - &loops::SAME_ITEM_PUSH, - &loops::SINGLE_ELEMENT_LOOP, - &loops::WHILE_IMMUTABLE_CONDITION, - &loops::WHILE_LET_LOOP, - &loops::WHILE_LET_ON_ITERATOR, - ¯o_use::MACRO_USE_IMPORTS, - &main_recursion::MAIN_RECURSION, - &manual_async_fn::MANUAL_ASYNC_FN, - &manual_map::MANUAL_MAP, - &manual_non_exhaustive::MANUAL_NON_EXHAUSTIVE, - &manual_ok_or::MANUAL_OK_OR, - &manual_strip::MANUAL_STRIP, - &manual_unwrap_or::MANUAL_UNWRAP_OR, - &map_clone::MAP_CLONE, - &map_err_ignore::MAP_ERR_IGNORE, - &map_identity::MAP_IDENTITY, - &map_unit_fn::OPTION_MAP_UNIT_FN, - &map_unit_fn::RESULT_MAP_UNIT_FN, - &match_on_vec_items::MATCH_ON_VEC_ITEMS, - &matches::INFALLIBLE_DESTRUCTURING_MATCH, - &matches::MATCH_AS_REF, - &matches::MATCH_BOOL, - &matches::MATCH_LIKE_MATCHES_MACRO, - &matches::MATCH_OVERLAPPING_ARM, - &matches::MATCH_REF_PATS, - &matches::MATCH_SAME_ARMS, - &matches::MATCH_SINGLE_BINDING, - &matches::MATCH_WILDCARD_FOR_SINGLE_VARIANTS, - &matches::MATCH_WILD_ERR_ARM, - &matches::REDUNDANT_PATTERN_MATCHING, - &matches::REST_PAT_IN_FULLY_BOUND_STRUCTS, - &matches::SINGLE_MATCH, - &matches::SINGLE_MATCH_ELSE, - &matches::WILDCARD_ENUM_MATCH_ARM, - &matches::WILDCARD_IN_OR_PATTERNS, - &mem_discriminant::MEM_DISCRIMINANT_NON_ENUM, - &mem_forget::MEM_FORGET, - &mem_replace::MEM_REPLACE_OPTION_WITH_NONE, - &mem_replace::MEM_REPLACE_WITH_DEFAULT, - &mem_replace::MEM_REPLACE_WITH_UNINIT, - &methods::BIND_INSTEAD_OF_MAP, - &methods::BYTES_NTH, - &methods::CHARS_LAST_CMP, - &methods::CHARS_NEXT_CMP, - &methods::CLONE_DOUBLE_REF, - &methods::CLONE_ON_COPY, - &methods::CLONE_ON_REF_PTR, - &methods::EXPECT_FUN_CALL, - &methods::EXPECT_USED, - &methods::FILETYPE_IS_FILE, - &methods::FILTER_MAP, - &methods::FILTER_MAP_IDENTITY, - &methods::FILTER_MAP_NEXT, - &methods::FILTER_NEXT, - &methods::FLAT_MAP_IDENTITY, - &methods::FROM_ITER_INSTEAD_OF_COLLECT, - &methods::GET_UNWRAP, - &methods::INEFFICIENT_TO_STRING, - &methods::INSPECT_FOR_EACH, - &methods::INTO_ITER_ON_REF, - &methods::ITERATOR_STEP_BY_ZERO, - &methods::ITER_CLONED_COLLECT, - &methods::ITER_NEXT_SLICE, - &methods::ITER_NTH, - &methods::ITER_NTH_ZERO, - &methods::ITER_SKIP_NEXT, - &methods::MANUAL_FILTER_MAP, - &methods::MANUAL_FIND_MAP, - &methods::MANUAL_SATURATING_ARITHMETIC, - &methods::MAP_COLLECT_RESULT_UNIT, - &methods::MAP_FLATTEN, - &methods::MAP_UNWRAP_OR, - &methods::NEW_RET_NO_SELF, - &methods::OK_EXPECT, - &methods::OPTION_AS_REF_DEREF, - &methods::OPTION_MAP_OR_NONE, - &methods::OR_FUN_CALL, - &methods::RESULT_MAP_OR_INTO_OPTION, - &methods::SEARCH_IS_SOME, - &methods::SHOULD_IMPLEMENT_TRAIT, - &methods::SINGLE_CHAR_ADD_STR, - &methods::SINGLE_CHAR_PATTERN, - &methods::SKIP_WHILE_NEXT, - &methods::STRING_EXTEND_CHARS, - &methods::SUSPICIOUS_MAP, - &methods::UNINIT_ASSUMED_INIT, - &methods::UNNECESSARY_FILTER_MAP, - &methods::UNNECESSARY_FOLD, - &methods::UNNECESSARY_LAZY_EVALUATIONS, - &methods::UNWRAP_USED, - &methods::USELESS_ASREF, - &methods::WRONG_PUB_SELF_CONVENTION, - &methods::WRONG_SELF_CONVENTION, - &methods::ZST_OFFSET, - &minmax::MIN_MAX, - &misc::CMP_NAN, - &misc::CMP_OWNED, - &misc::FLOAT_CMP, - &misc::FLOAT_CMP_CONST, - &misc::MODULO_ONE, - &misc::SHORT_CIRCUIT_STATEMENT, - &misc::TOPLEVEL_REF_ARG, - &misc::USED_UNDERSCORE_BINDING, - &misc::ZERO_PTR, - &misc_early::BUILTIN_TYPE_SHADOW, - &misc_early::DOUBLE_NEG, - &misc_early::DUPLICATE_UNDERSCORE_ARGUMENT, - &misc_early::MIXED_CASE_HEX_LITERALS, - &misc_early::REDUNDANT_PATTERN, - &misc_early::UNNEEDED_FIELD_PATTERN, - &misc_early::UNNEEDED_WILDCARD_PATTERN, - &misc_early::UNSEPARATED_LITERAL_SUFFIX, - &misc_early::ZERO_PREFIXED_LITERAL, - &missing_const_for_fn::MISSING_CONST_FOR_FN, - &missing_doc::MISSING_DOCS_IN_PRIVATE_ITEMS, - &missing_inline::MISSING_INLINE_IN_PUBLIC_ITEMS, - &modulo_arithmetic::MODULO_ARITHMETIC, - &multiple_crate_versions::MULTIPLE_CRATE_VERSIONS, - &mut_key::MUTABLE_KEY_TYPE, - &mut_mut::MUT_MUT, - &mut_mutex_lock::MUT_MUTEX_LOCK, - &mut_reference::UNNECESSARY_MUT_PASSED, - &mutable_debug_assertion::DEBUG_ASSERT_WITH_MUT_CALL, - &mutex_atomic::MUTEX_ATOMIC, - &mutex_atomic::MUTEX_INTEGER, - &needless_arbitrary_self_type::NEEDLESS_ARBITRARY_SELF_TYPE, - &needless_bool::BOOL_COMPARISON, - &needless_bool::NEEDLESS_BOOL, - &needless_borrow::NEEDLESS_BORROW, - &needless_borrowed_ref::NEEDLESS_BORROWED_REFERENCE, - &needless_continue::NEEDLESS_CONTINUE, - &needless_pass_by_value::NEEDLESS_PASS_BY_VALUE, - &needless_question_mark::NEEDLESS_QUESTION_MARK, - &needless_update::NEEDLESS_UPDATE, - &neg_cmp_op_on_partial_ord::NEG_CMP_OP_ON_PARTIAL_ORD, - &neg_multiply::NEG_MULTIPLY, - &new_without_default::NEW_WITHOUT_DEFAULT, - &no_effect::NO_EFFECT, - &no_effect::UNNECESSARY_OPERATION, - &non_copy_const::BORROW_INTERIOR_MUTABLE_CONST, - &non_copy_const::DECLARE_INTERIOR_MUTABLE_CONST, - &non_expressive_names::JUST_UNDERSCORES_AND_DIGITS, - &non_expressive_names::MANY_SINGLE_CHAR_NAMES, - &non_expressive_names::SIMILAR_NAMES, - &open_options::NONSENSICAL_OPEN_OPTIONS, - &option_env_unwrap::OPTION_ENV_UNWRAP, - &option_if_let_else::OPTION_IF_LET_ELSE, - &overflow_check_conditional::OVERFLOW_CHECK_CONDITIONAL, - &panic_in_result_fn::PANIC_IN_RESULT_FN, - &panic_unimplemented::PANIC, - &panic_unimplemented::TODO, - &panic_unimplemented::UNIMPLEMENTED, - &panic_unimplemented::UNREACHABLE, - &partialeq_ne_impl::PARTIALEQ_NE_IMPL, - &pass_by_ref_or_value::LARGE_TYPES_PASSED_BY_VALUE, - &pass_by_ref_or_value::TRIVIALLY_COPY_PASS_BY_REF, - &path_buf_push_overwrite::PATH_BUF_PUSH_OVERWRITE, - &pattern_type_mismatch::PATTERN_TYPE_MISMATCH, - &precedence::PRECEDENCE, - &ptr::CMP_NULL, - &ptr::MUT_FROM_REF, - &ptr::PTR_ARG, - &ptr_eq::PTR_EQ, - &ptr_offset_with_cast::PTR_OFFSET_WITH_CAST, - &question_mark::QUESTION_MARK, - &ranges::MANUAL_RANGE_CONTAINS, - &ranges::RANGE_MINUS_ONE, - &ranges::RANGE_PLUS_ONE, - &ranges::RANGE_ZIP_WITH_LEN, - &ranges::REVERSED_EMPTY_RANGES, - &redundant_clone::REDUNDANT_CLONE, - &redundant_closure_call::REDUNDANT_CLOSURE_CALL, - &redundant_else::REDUNDANT_ELSE, - &redundant_field_names::REDUNDANT_FIELD_NAMES, - &redundant_pub_crate::REDUNDANT_PUB_CRATE, - &redundant_slicing::REDUNDANT_SLICING, - &redundant_static_lifetimes::REDUNDANT_STATIC_LIFETIMES, - &ref_option_ref::REF_OPTION_REF, - &reference::DEREF_ADDROF, - &reference::REF_IN_DEREF, - ®ex::INVALID_REGEX, - ®ex::TRIVIAL_REGEX, - &repeat_once::REPEAT_ONCE, - &returns::LET_AND_RETURN, - &returns::NEEDLESS_RETURN, - &self_assignment::SELF_ASSIGNMENT, - &semicolon_if_nothing_returned::SEMICOLON_IF_NOTHING_RETURNED, - &serde_api::SERDE_API_MISUSE, - &shadow::SHADOW_REUSE, - &shadow::SHADOW_SAME, - &shadow::SHADOW_UNRELATED, - &single_component_path_imports::SINGLE_COMPONENT_PATH_IMPORTS, - &size_of_in_element_count::SIZE_OF_IN_ELEMENT_COUNT, - &slow_vector_initialization::SLOW_VECTOR_INITIALIZATION, - &stable_sort_primitive::STABLE_SORT_PRIMITIVE, - &strings::STRING_ADD, - &strings::STRING_ADD_ASSIGN, - &strings::STRING_FROM_UTF8_AS_BYTES, - &strings::STRING_LIT_AS_BYTES, - &strings::STRING_TO_STRING, - &strings::STR_TO_STRING, - &suspicious_operation_groupings::SUSPICIOUS_OPERATION_GROUPINGS, - &suspicious_trait_impl::SUSPICIOUS_ARITHMETIC_IMPL, - &suspicious_trait_impl::SUSPICIOUS_OP_ASSIGN_IMPL, - &swap::ALMOST_SWAPPED, - &swap::MANUAL_SWAP, - &tabs_in_doc_comments::TABS_IN_DOC_COMMENTS, - &temporary_assignment::TEMPORARY_ASSIGNMENT, - &to_digit_is_some::TO_DIGIT_IS_SOME, - &to_string_in_display::TO_STRING_IN_DISPLAY, - &trait_bounds::TRAIT_DUPLICATION_IN_BOUNDS, - &trait_bounds::TYPE_REPETITION_IN_BOUNDS, - &transmute::CROSSPOINTER_TRANSMUTE, - &transmute::TRANSMUTES_EXPRESSIBLE_AS_PTR_CASTS, - &transmute::TRANSMUTE_BYTES_TO_STR, - &transmute::TRANSMUTE_FLOAT_TO_INT, - &transmute::TRANSMUTE_INT_TO_BOOL, - &transmute::TRANSMUTE_INT_TO_CHAR, - &transmute::TRANSMUTE_INT_TO_FLOAT, - &transmute::TRANSMUTE_PTR_TO_PTR, - &transmute::TRANSMUTE_PTR_TO_REF, - &transmute::UNSOUND_COLLECTION_TRANSMUTE, - &transmute::USELESS_TRANSMUTE, - &transmute::WRONG_TRANSMUTE, - &transmuting_null::TRANSMUTING_NULL, - &try_err::TRY_ERR, - &types::ABSURD_EXTREME_COMPARISONS, - &types::BORROWED_BOX, - &types::BOX_VEC, - &types::CAST_LOSSLESS, - &types::CAST_POSSIBLE_TRUNCATION, - &types::CAST_POSSIBLE_WRAP, - &types::CAST_PRECISION_LOSS, - &types::CAST_PTR_ALIGNMENT, - &types::CAST_REF_TO_MUT, - &types::CAST_SIGN_LOSS, - &types::CHAR_LIT_AS_U8, - &types::FN_TO_NUMERIC_CAST, - &types::FN_TO_NUMERIC_CAST_WITH_TRUNCATION, - &types::IMPLICIT_HASHER, - &types::INVALID_UPCAST_COMPARISONS, - &types::LET_UNIT_VALUE, - &types::LINKEDLIST, - &types::OPTION_OPTION, - &types::PTR_AS_PTR, - &types::RC_BUFFER, - &types::REDUNDANT_ALLOCATION, - &types::TYPE_COMPLEXITY, - &types::UNIT_ARG, - &types::UNIT_CMP, - &types::UNNECESSARY_CAST, - &types::VEC_BOX, - &undropped_manually_drops::UNDROPPED_MANUALLY_DROPS, - &unicode::INVISIBLE_CHARACTERS, - &unicode::NON_ASCII_LITERAL, - &unicode::UNICODE_NOT_NFC, - &unit_return_expecting_ord::UNIT_RETURN_EXPECTING_ORD, - &unnamed_address::FN_ADDRESS_COMPARISONS, - &unnamed_address::VTABLE_ADDRESS_COMPARISONS, - &unnecessary_sort_by::UNNECESSARY_SORT_BY, - &unnecessary_wraps::UNNECESSARY_WRAPS, - &unnested_or_patterns::UNNESTED_OR_PATTERNS, - &unsafe_removed_from_name::UNSAFE_REMOVED_FROM_NAME, - &unused_io_amount::UNUSED_IO_AMOUNT, - &unused_self::UNUSED_SELF, - &unused_unit::UNUSED_UNIT, - &unwrap::PANICKING_UNWRAP, - &unwrap::UNNECESSARY_UNWRAP, - &unwrap_in_result::UNWRAP_IN_RESULT, - &upper_case_acronyms::UPPER_CASE_ACRONYMS, - &use_self::USE_SELF, - &useless_conversion::USELESS_CONVERSION, - &vec::USELESS_VEC, - &vec_init_then_push::VEC_INIT_THEN_PUSH, - &vec_resize_to_zero::VEC_RESIZE_TO_ZERO, - &verbose_file_reads::VERBOSE_FILE_READS, - &wildcard_dependencies::WILDCARD_DEPENDENCIES, - &wildcard_imports::ENUM_GLOB_USE, - &wildcard_imports::WILDCARD_IMPORTS, - &write::PRINTLN_EMPTY_STRING, - &write::PRINT_LITERAL, - &write::PRINT_STDERR, - &write::PRINT_STDOUT, - &write::PRINT_WITH_NEWLINE, - &write::USE_DEBUG, - &write::WRITELN_EMPTY_STRING, - &write::WRITE_LITERAL, - &write::WRITE_WITH_NEWLINE, - &zero_div_zero::ZERO_DIVIDED_BY_ZERO, - &zero_sized_map_values::ZERO_SIZED_MAP_VALUES, + utils::internal_lints::PRODUCE_ICE, + #[cfg(feature = "internal-lints")] + utils::internal_lints::UNNECESSARY_SYMBOL_STR, + absurd_extreme_comparisons::ABSURD_EXTREME_COMPARISONS, + approx_const::APPROX_CONSTANT, + arithmetic::FLOAT_ARITHMETIC, + arithmetic::INTEGER_ARITHMETIC, + as_conversions::AS_CONVERSIONS, + asm_syntax::INLINE_ASM_X86_ATT_SYNTAX, + asm_syntax::INLINE_ASM_X86_INTEL_SYNTAX, + assertions_on_constants::ASSERTIONS_ON_CONSTANTS, + assign_ops::ASSIGN_OP_PATTERN, + assign_ops::MISREFACTORED_ASSIGN_OP, + async_yields_async::ASYNC_YIELDS_ASYNC, + atomic_ordering::INVALID_ATOMIC_ORDERING, + attrs::BLANKET_CLIPPY_RESTRICTION_LINTS, + attrs::DEPRECATED_CFG_ATTR, + attrs::DEPRECATED_SEMVER, + attrs::EMPTY_LINE_AFTER_OUTER_ATTR, + attrs::INLINE_ALWAYS, + attrs::MISMATCHED_TARGET_OS, + attrs::USELESS_ATTRIBUTE, + await_holding_invalid::AWAIT_HOLDING_LOCK, + await_holding_invalid::AWAIT_HOLDING_REFCELL_REF, + bit_mask::BAD_BIT_MASK, + bit_mask::INEFFECTIVE_BIT_MASK, + bit_mask::VERBOSE_BIT_MASK, + blacklisted_name::BLACKLISTED_NAME, + blocks_in_if_conditions::BLOCKS_IN_IF_CONDITIONS, + booleans::LOGIC_BUG, + booleans::NONMINIMAL_BOOL, + bytecount::NAIVE_BYTECOUNT, + cargo_common_metadata::CARGO_COMMON_METADATA, + case_sensitive_file_extension_comparisons::CASE_SENSITIVE_FILE_EXTENSION_COMPARISONS, + casts::CAST_LOSSLESS, + casts::CAST_POSSIBLE_TRUNCATION, + casts::CAST_POSSIBLE_WRAP, + casts::CAST_PRECISION_LOSS, + casts::CAST_PTR_ALIGNMENT, + casts::CAST_REF_TO_MUT, + casts::CAST_SIGN_LOSS, + casts::CHAR_LIT_AS_U8, + casts::FN_TO_NUMERIC_CAST, + casts::FN_TO_NUMERIC_CAST_WITH_TRUNCATION, + casts::PTR_AS_PTR, + casts::UNNECESSARY_CAST, + checked_conversions::CHECKED_CONVERSIONS, + cognitive_complexity::COGNITIVE_COMPLEXITY, + collapsible_if::COLLAPSIBLE_ELSE_IF, + collapsible_if::COLLAPSIBLE_IF, + collapsible_match::COLLAPSIBLE_MATCH, + comparison_chain::COMPARISON_CHAIN, + copies::BRANCHES_SHARING_CODE, + copies::IFS_SAME_COND, + copies::IF_SAME_THEN_ELSE, + copies::SAME_FUNCTIONS_IN_IF_CONDITION, + copy_iterator::COPY_ITERATOR, + create_dir::CREATE_DIR, + dbg_macro::DBG_MACRO, + default::DEFAULT_TRAIT_ACCESS, + default::FIELD_REASSIGN_WITH_DEFAULT, + default_numeric_fallback::DEFAULT_NUMERIC_FALLBACK, + dereference::EXPLICIT_DEREF_METHODS, + derive::DERIVE_HASH_XOR_EQ, + derive::DERIVE_ORD_XOR_PARTIAL_ORD, + derive::EXPL_IMPL_CLONE_ON_COPY, + derive::UNSAFE_DERIVE_DESERIALIZE, + disallowed_method::DISALLOWED_METHOD, + doc::DOC_MARKDOWN, + doc::MISSING_ERRORS_DOC, + doc::MISSING_PANICS_DOC, + doc::MISSING_SAFETY_DOC, + doc::NEEDLESS_DOCTEST_MAIN, + double_comparison::DOUBLE_COMPARISONS, + double_parens::DOUBLE_PARENS, + drop_forget_ref::DROP_COPY, + drop_forget_ref::DROP_REF, + drop_forget_ref::FORGET_COPY, + drop_forget_ref::FORGET_REF, + duration_subsec::DURATION_SUBSEC, + else_if_without_else::ELSE_IF_WITHOUT_ELSE, + empty_enum::EMPTY_ENUM, + entry::MAP_ENTRY, + enum_clike::ENUM_CLIKE_UNPORTABLE_VARIANT, + enum_variants::ENUM_VARIANT_NAMES, + enum_variants::MODULE_INCEPTION, + enum_variants::MODULE_NAME_REPETITIONS, + enum_variants::PUB_ENUM_VARIANT_NAMES, + eq_op::EQ_OP, + eq_op::OP_REF, + erasing_op::ERASING_OP, + escape::BOXED_LOCAL, + eta_reduction::REDUNDANT_CLOSURE, + eta_reduction::REDUNDANT_CLOSURE_FOR_METHOD_CALLS, + eval_order_dependence::DIVERGING_SUB_EXPRESSION, + eval_order_dependence::EVAL_ORDER_DEPENDENCE, + excessive_bools::FN_PARAMS_EXCESSIVE_BOOLS, + excessive_bools::STRUCT_EXCESSIVE_BOOLS, + exhaustive_items::EXHAUSTIVE_ENUMS, + exhaustive_items::EXHAUSTIVE_STRUCTS, + exit::EXIT, + explicit_write::EXPLICIT_WRITE, + fallible_impl_from::FALLIBLE_IMPL_FROM, + float_equality_without_abs::FLOAT_EQUALITY_WITHOUT_ABS, + float_literal::EXCESSIVE_PRECISION, + float_literal::LOSSY_FLOAT_LITERAL, + floating_point_arithmetic::IMPRECISE_FLOPS, + floating_point_arithmetic::SUBOPTIMAL_FLOPS, + format::USELESS_FORMAT, + formatting::POSSIBLE_MISSING_COMMA, + formatting::SUSPICIOUS_ASSIGNMENT_FORMATTING, + formatting::SUSPICIOUS_ELSE_FORMATTING, + formatting::SUSPICIOUS_UNARY_OP_FORMATTING, + from_over_into::FROM_OVER_INTO, + from_str_radix_10::FROM_STR_RADIX_10, + functions::DOUBLE_MUST_USE, + functions::MUST_USE_CANDIDATE, + functions::MUST_USE_UNIT, + functions::NOT_UNSAFE_PTR_ARG_DEREF, + functions::RESULT_UNIT_ERR, + functions::TOO_MANY_ARGUMENTS, + functions::TOO_MANY_LINES, + future_not_send::FUTURE_NOT_SEND, + get_last_with_len::GET_LAST_WITH_LEN, + identity_op::IDENTITY_OP, + if_let_mutex::IF_LET_MUTEX, + if_let_some_result::IF_LET_SOME_RESULT, + if_not_else::IF_NOT_ELSE, + if_then_some_else_none::IF_THEN_SOME_ELSE_NONE, + implicit_hasher::IMPLICIT_HASHER, + implicit_return::IMPLICIT_RETURN, + implicit_saturating_sub::IMPLICIT_SATURATING_SUB, + inconsistent_struct_constructor::INCONSISTENT_STRUCT_CONSTRUCTOR, + indexing_slicing::INDEXING_SLICING, + indexing_slicing::OUT_OF_BOUNDS_INDEXING, + infinite_iter::INFINITE_ITER, + infinite_iter::MAYBE_INFINITE_ITER, + inherent_impl::MULTIPLE_INHERENT_IMPL, + inherent_to_string::INHERENT_TO_STRING, + inherent_to_string::INHERENT_TO_STRING_SHADOW_DISPLAY, + inline_fn_without_body::INLINE_FN_WITHOUT_BODY, + int_plus_one::INT_PLUS_ONE, + integer_division::INTEGER_DIVISION, + invalid_upcast_comparisons::INVALID_UPCAST_COMPARISONS, + items_after_statements::ITEMS_AFTER_STATEMENTS, + large_const_arrays::LARGE_CONST_ARRAYS, + large_enum_variant::LARGE_ENUM_VARIANT, + large_stack_arrays::LARGE_STACK_ARRAYS, + len_zero::COMPARISON_TO_EMPTY, + len_zero::LEN_WITHOUT_IS_EMPTY, + len_zero::LEN_ZERO, + let_if_seq::USELESS_LET_IF_SEQ, + let_underscore::LET_UNDERSCORE_DROP, + let_underscore::LET_UNDERSCORE_LOCK, + let_underscore::LET_UNDERSCORE_MUST_USE, + lifetimes::EXTRA_UNUSED_LIFETIMES, + lifetimes::NEEDLESS_LIFETIMES, + literal_representation::DECIMAL_LITERAL_REPRESENTATION, + literal_representation::INCONSISTENT_DIGIT_GROUPING, + literal_representation::LARGE_DIGIT_GROUPS, + literal_representation::MISTYPED_LITERAL_SUFFIXES, + literal_representation::UNREADABLE_LITERAL, + literal_representation::UNUSUAL_BYTE_GROUPINGS, + loops::EMPTY_LOOP, + loops::EXPLICIT_COUNTER_LOOP, + loops::EXPLICIT_INTO_ITER_LOOP, + loops::EXPLICIT_ITER_LOOP, + loops::FOR_KV_MAP, + loops::FOR_LOOPS_OVER_FALLIBLES, + loops::ITER_NEXT_LOOP, + loops::MANUAL_FLATTEN, + loops::MANUAL_MEMCPY, + loops::MUT_RANGE_BOUND, + loops::NEEDLESS_COLLECT, + loops::NEEDLESS_RANGE_LOOP, + loops::NEVER_LOOP, + loops::SAME_ITEM_PUSH, + loops::SINGLE_ELEMENT_LOOP, + loops::WHILE_IMMUTABLE_CONDITION, + loops::WHILE_LET_LOOP, + loops::WHILE_LET_ON_ITERATOR, + macro_use::MACRO_USE_IMPORTS, + main_recursion::MAIN_RECURSION, + manual_async_fn::MANUAL_ASYNC_FN, + manual_map::MANUAL_MAP, + manual_non_exhaustive::MANUAL_NON_EXHAUSTIVE, + manual_ok_or::MANUAL_OK_OR, + manual_strip::MANUAL_STRIP, + manual_unwrap_or::MANUAL_UNWRAP_OR, + map_clone::MAP_CLONE, + map_err_ignore::MAP_ERR_IGNORE, + map_identity::MAP_IDENTITY, + map_unit_fn::OPTION_MAP_UNIT_FN, + map_unit_fn::RESULT_MAP_UNIT_FN, + match_on_vec_items::MATCH_ON_VEC_ITEMS, + matches::INFALLIBLE_DESTRUCTURING_MATCH, + matches::MATCH_AS_REF, + matches::MATCH_BOOL, + matches::MATCH_LIKE_MATCHES_MACRO, + matches::MATCH_OVERLAPPING_ARM, + matches::MATCH_REF_PATS, + matches::MATCH_SAME_ARMS, + matches::MATCH_SINGLE_BINDING, + matches::MATCH_WILDCARD_FOR_SINGLE_VARIANTS, + matches::MATCH_WILD_ERR_ARM, + matches::REDUNDANT_PATTERN_MATCHING, + matches::REST_PAT_IN_FULLY_BOUND_STRUCTS, + matches::SINGLE_MATCH, + matches::SINGLE_MATCH_ELSE, + matches::WILDCARD_ENUM_MATCH_ARM, + matches::WILDCARD_IN_OR_PATTERNS, + mem_discriminant::MEM_DISCRIMINANT_NON_ENUM, + mem_forget::MEM_FORGET, + mem_replace::MEM_REPLACE_OPTION_WITH_NONE, + mem_replace::MEM_REPLACE_WITH_DEFAULT, + mem_replace::MEM_REPLACE_WITH_UNINIT, + methods::BIND_INSTEAD_OF_MAP, + methods::BYTES_NTH, + methods::CHARS_LAST_CMP, + methods::CHARS_NEXT_CMP, + methods::CLONE_DOUBLE_REF, + methods::CLONE_ON_COPY, + methods::CLONE_ON_REF_PTR, + methods::EXPECT_FUN_CALL, + methods::EXPECT_USED, + methods::FILETYPE_IS_FILE, + methods::FILTER_MAP, + methods::FILTER_MAP_IDENTITY, + methods::FILTER_MAP_NEXT, + methods::FILTER_NEXT, + methods::FLAT_MAP_IDENTITY, + methods::FROM_ITER_INSTEAD_OF_COLLECT, + methods::GET_UNWRAP, + methods::IMPLICIT_CLONE, + methods::INEFFICIENT_TO_STRING, + methods::INSPECT_FOR_EACH, + methods::INTO_ITER_ON_REF, + methods::ITERATOR_STEP_BY_ZERO, + methods::ITER_CLONED_COLLECT, + methods::ITER_COUNT, + methods::ITER_NEXT_SLICE, + methods::ITER_NTH, + methods::ITER_NTH_ZERO, + methods::ITER_SKIP_NEXT, + methods::MANUAL_FILTER_MAP, + methods::MANUAL_FIND_MAP, + methods::MANUAL_SATURATING_ARITHMETIC, + methods::MAP_COLLECT_RESULT_UNIT, + methods::MAP_FLATTEN, + methods::MAP_UNWRAP_OR, + methods::NEW_RET_NO_SELF, + methods::OK_EXPECT, + methods::OPTION_AS_REF_DEREF, + methods::OPTION_FILTER_MAP, + methods::OPTION_MAP_OR_NONE, + methods::OR_FUN_CALL, + methods::RESULT_MAP_OR_INTO_OPTION, + methods::SEARCH_IS_SOME, + methods::SHOULD_IMPLEMENT_TRAIT, + methods::SINGLE_CHAR_ADD_STR, + methods::SINGLE_CHAR_PATTERN, + methods::SKIP_WHILE_NEXT, + methods::STRING_EXTEND_CHARS, + methods::SUSPICIOUS_MAP, + methods::UNINIT_ASSUMED_INIT, + methods::UNNECESSARY_FILTER_MAP, + methods::UNNECESSARY_FOLD, + methods::UNNECESSARY_LAZY_EVALUATIONS, + methods::UNWRAP_USED, + methods::USELESS_ASREF, + methods::WRONG_PUB_SELF_CONVENTION, + methods::WRONG_SELF_CONVENTION, + methods::ZST_OFFSET, + minmax::MIN_MAX, + misc::CMP_NAN, + misc::CMP_OWNED, + misc::FLOAT_CMP, + misc::FLOAT_CMP_CONST, + misc::MODULO_ONE, + misc::SHORT_CIRCUIT_STATEMENT, + misc::TOPLEVEL_REF_ARG, + misc::USED_UNDERSCORE_BINDING, + misc::ZERO_PTR, + misc_early::BUILTIN_TYPE_SHADOW, + misc_early::DOUBLE_NEG, + misc_early::DUPLICATE_UNDERSCORE_ARGUMENT, + misc_early::MIXED_CASE_HEX_LITERALS, + misc_early::REDUNDANT_PATTERN, + misc_early::UNNEEDED_FIELD_PATTERN, + misc_early::UNNEEDED_WILDCARD_PATTERN, + misc_early::UNSEPARATED_LITERAL_SUFFIX, + misc_early::ZERO_PREFIXED_LITERAL, + missing_const_for_fn::MISSING_CONST_FOR_FN, + missing_doc::MISSING_DOCS_IN_PRIVATE_ITEMS, + missing_inline::MISSING_INLINE_IN_PUBLIC_ITEMS, + modulo_arithmetic::MODULO_ARITHMETIC, + multiple_crate_versions::MULTIPLE_CRATE_VERSIONS, + mut_key::MUTABLE_KEY_TYPE, + mut_mut::MUT_MUT, + mut_mutex_lock::MUT_MUTEX_LOCK, + mut_reference::UNNECESSARY_MUT_PASSED, + mutable_debug_assertion::DEBUG_ASSERT_WITH_MUT_CALL, + mutex_atomic::MUTEX_ATOMIC, + mutex_atomic::MUTEX_INTEGER, + needless_arbitrary_self_type::NEEDLESS_ARBITRARY_SELF_TYPE, + needless_bool::BOOL_COMPARISON, + needless_bool::NEEDLESS_BOOL, + needless_borrow::NEEDLESS_BORROW, + needless_borrowed_ref::NEEDLESS_BORROWED_REFERENCE, + needless_continue::NEEDLESS_CONTINUE, + needless_for_each::NEEDLESS_FOR_EACH, + needless_pass_by_value::NEEDLESS_PASS_BY_VALUE, + needless_question_mark::NEEDLESS_QUESTION_MARK, + needless_update::NEEDLESS_UPDATE, + neg_cmp_op_on_partial_ord::NEG_CMP_OP_ON_PARTIAL_ORD, + neg_multiply::NEG_MULTIPLY, + new_without_default::NEW_WITHOUT_DEFAULT, + no_effect::NO_EFFECT, + no_effect::UNNECESSARY_OPERATION, + non_copy_const::BORROW_INTERIOR_MUTABLE_CONST, + non_copy_const::DECLARE_INTERIOR_MUTABLE_CONST, + non_expressive_names::JUST_UNDERSCORES_AND_DIGITS, + non_expressive_names::MANY_SINGLE_CHAR_NAMES, + non_expressive_names::SIMILAR_NAMES, + non_octal_unix_permissions::NON_OCTAL_UNIX_PERMISSIONS, + open_options::NONSENSICAL_OPEN_OPTIONS, + option_env_unwrap::OPTION_ENV_UNWRAP, + option_if_let_else::OPTION_IF_LET_ELSE, + overflow_check_conditional::OVERFLOW_CHECK_CONDITIONAL, + panic_in_result_fn::PANIC_IN_RESULT_FN, + panic_unimplemented::PANIC, + panic_unimplemented::TODO, + panic_unimplemented::UNIMPLEMENTED, + panic_unimplemented::UNREACHABLE, + partialeq_ne_impl::PARTIALEQ_NE_IMPL, + pass_by_ref_or_value::LARGE_TYPES_PASSED_BY_VALUE, + pass_by_ref_or_value::TRIVIALLY_COPY_PASS_BY_REF, + path_buf_push_overwrite::PATH_BUF_PUSH_OVERWRITE, + pattern_type_mismatch::PATTERN_TYPE_MISMATCH, + precedence::PRECEDENCE, + ptr::CMP_NULL, + ptr::MUT_FROM_REF, + ptr::PTR_ARG, + ptr_eq::PTR_EQ, + ptr_offset_with_cast::PTR_OFFSET_WITH_CAST, + question_mark::QUESTION_MARK, + ranges::MANUAL_RANGE_CONTAINS, + ranges::RANGE_MINUS_ONE, + ranges::RANGE_PLUS_ONE, + ranges::RANGE_ZIP_WITH_LEN, + ranges::REVERSED_EMPTY_RANGES, + redundant_clone::REDUNDANT_CLONE, + redundant_closure_call::REDUNDANT_CLOSURE_CALL, + redundant_else::REDUNDANT_ELSE, + redundant_field_names::REDUNDANT_FIELD_NAMES, + redundant_pub_crate::REDUNDANT_PUB_CRATE, + redundant_slicing::REDUNDANT_SLICING, + redundant_static_lifetimes::REDUNDANT_STATIC_LIFETIMES, + ref_option_ref::REF_OPTION_REF, + reference::DEREF_ADDROF, + reference::REF_IN_DEREF, + regex::INVALID_REGEX, + regex::TRIVIAL_REGEX, + repeat_once::REPEAT_ONCE, + returns::LET_AND_RETURN, + returns::NEEDLESS_RETURN, + self_assignment::SELF_ASSIGNMENT, + semicolon_if_nothing_returned::SEMICOLON_IF_NOTHING_RETURNED, + serde_api::SERDE_API_MISUSE, + shadow::SHADOW_REUSE, + shadow::SHADOW_SAME, + shadow::SHADOW_UNRELATED, + single_component_path_imports::SINGLE_COMPONENT_PATH_IMPORTS, + size_of_in_element_count::SIZE_OF_IN_ELEMENT_COUNT, + slow_vector_initialization::SLOW_VECTOR_INITIALIZATION, + stable_sort_primitive::STABLE_SORT_PRIMITIVE, + strings::STRING_ADD, + strings::STRING_ADD_ASSIGN, + strings::STRING_FROM_UTF8_AS_BYTES, + strings::STRING_LIT_AS_BYTES, + strings::STRING_TO_STRING, + strings::STR_TO_STRING, + suspicious_operation_groupings::SUSPICIOUS_OPERATION_GROUPINGS, + suspicious_trait_impl::SUSPICIOUS_ARITHMETIC_IMPL, + suspicious_trait_impl::SUSPICIOUS_OP_ASSIGN_IMPL, + swap::ALMOST_SWAPPED, + swap::MANUAL_SWAP, + tabs_in_doc_comments::TABS_IN_DOC_COMMENTS, + temporary_assignment::TEMPORARY_ASSIGNMENT, + to_digit_is_some::TO_DIGIT_IS_SOME, + to_string_in_display::TO_STRING_IN_DISPLAY, + trait_bounds::TRAIT_DUPLICATION_IN_BOUNDS, + trait_bounds::TYPE_REPETITION_IN_BOUNDS, + transmute::CROSSPOINTER_TRANSMUTE, + transmute::TRANSMUTES_EXPRESSIBLE_AS_PTR_CASTS, + transmute::TRANSMUTE_BYTES_TO_STR, + transmute::TRANSMUTE_FLOAT_TO_INT, + transmute::TRANSMUTE_INT_TO_BOOL, + transmute::TRANSMUTE_INT_TO_CHAR, + transmute::TRANSMUTE_INT_TO_FLOAT, + transmute::TRANSMUTE_PTR_TO_PTR, + transmute::TRANSMUTE_PTR_TO_REF, + transmute::UNSOUND_COLLECTION_TRANSMUTE, + transmute::USELESS_TRANSMUTE, + transmute::WRONG_TRANSMUTE, + transmuting_null::TRANSMUTING_NULL, + try_err::TRY_ERR, + types::BORROWED_BOX, + types::BOX_VEC, + types::LINKEDLIST, + types::OPTION_OPTION, + types::RC_BUFFER, + types::REDUNDANT_ALLOCATION, + types::TYPE_COMPLEXITY, + types::VEC_BOX, + undropped_manually_drops::UNDROPPED_MANUALLY_DROPS, + unicode::INVISIBLE_CHARACTERS, + unicode::NON_ASCII_LITERAL, + unicode::UNICODE_NOT_NFC, + unit_return_expecting_ord::UNIT_RETURN_EXPECTING_ORD, + unit_types::LET_UNIT_VALUE, + unit_types::UNIT_ARG, + unit_types::UNIT_CMP, + unnamed_address::FN_ADDRESS_COMPARISONS, + unnamed_address::VTABLE_ADDRESS_COMPARISONS, + unnecessary_sort_by::UNNECESSARY_SORT_BY, + unnecessary_wraps::UNNECESSARY_WRAPS, + unnested_or_patterns::UNNESTED_OR_PATTERNS, + unsafe_removed_from_name::UNSAFE_REMOVED_FROM_NAME, + unused_io_amount::UNUSED_IO_AMOUNT, + unused_self::UNUSED_SELF, + unused_unit::UNUSED_UNIT, + unwrap::PANICKING_UNWRAP, + unwrap::UNNECESSARY_UNWRAP, + unwrap_in_result::UNWRAP_IN_RESULT, + upper_case_acronyms::UPPER_CASE_ACRONYMS, + use_self::USE_SELF, + useless_conversion::USELESS_CONVERSION, + vec::USELESS_VEC, + vec_init_then_push::VEC_INIT_THEN_PUSH, + vec_resize_to_zero::VEC_RESIZE_TO_ZERO, + verbose_file_reads::VERBOSE_FILE_READS, + wildcard_dependencies::WILDCARD_DEPENDENCIES, + wildcard_imports::ENUM_GLOB_USE, + wildcard_imports::WILDCARD_IMPORTS, + write::PRINTLN_EMPTY_STRING, + write::PRINT_LITERAL, + write::PRINT_STDERR, + write::PRINT_STDOUT, + write::PRINT_WITH_NEWLINE, + write::USE_DEBUG, + write::WRITELN_EMPTY_STRING, + write::WRITE_LITERAL, + write::WRITE_WITH_NEWLINE, + zero_div_zero::ZERO_DIVIDED_BY_ZERO, + zero_sized_map_values::ZERO_SIZED_MAP_VALUES, ]); // end register lints, do not remove this comment, it’s used in `update_lints` @@ -1013,6 +1030,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| box utils::inspector::DeepCodeInspector); store.register_late_pass(|| box utils::internal_lints::CollapsibleCalls); store.register_late_pass(|| box utils::internal_lints::CompilerLintFunctions::new()); + store.register_late_pass(|| box utils::internal_lints::IfChainStyle); store.register_late_pass(|| box utils::internal_lints::InvalidPaths); store.register_late_pass(|| box utils::internal_lints::InterningDefinedSymbol::default()); store.register_late_pass(|| box utils::internal_lints::LintWithoutLintPass::default()); @@ -1023,7 +1041,8 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| box await_holding_invalid::AwaitHolding); store.register_late_pass(|| box serde_api::SerdeApi); let vec_box_size_threshold = conf.vec_box_size_threshold; - store.register_late_pass(move || box types::Types::new(vec_box_size_threshold)); + let type_complexity_threshold = conf.type_complexity_threshold; + store.register_late_pass(move || box types::Types::new(vec_box_size_threshold, type_complexity_threshold)); store.register_late_pass(|| box booleans::NonminimalBool); store.register_late_pass(|| box eq_op::EqOp); store.register_late_pass(|| box enum_clike::UnportableVariant); @@ -1034,6 +1053,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| box ptr_eq::PtrEq); store.register_late_pass(|| box needless_bool::NeedlessBool); store.register_late_pass(|| box needless_bool::BoolComparison); + store.register_late_pass(|| box needless_for_each::NeedlessForEach); store.register_late_pass(|| box approx_const::ApproxConstant); store.register_late_pass(|| box misc::MiscLints); store.register_late_pass(|| box eta_reduction::EtaReduction); @@ -1052,6 +1072,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| box implicit_saturating_sub::ImplicitSaturatingSub); store.register_late_pass(|| box default_numeric_fallback::DefaultNumericFallback); store.register_late_pass(|| box inconsistent_struct_constructor::InconsistentStructConstructor); + store.register_late_pass(|| box non_octal_unix_permissions::NonOctalUnixPermissions); let msrv = conf.msrv.as_ref().and_then(|s| { parse_msrv(s, None, None).or_else(|| { @@ -1073,20 +1094,18 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(move || box use_self::UseSelf::new(msrv)); store.register_late_pass(move || box missing_const_for_fn::MissingConstForFn::new(msrv)); store.register_late_pass(move || box needless_question_mark::NeedlessQuestionMark::new(msrv)); + store.register_late_pass(move || box casts::Casts::new(msrv)); + store.register_early_pass(move || box unnested_or_patterns::UnnestedOrPatterns::new(msrv)); store.register_late_pass(|| box size_of_in_element_count::SizeOfInElementCount); store.register_late_pass(|| box map_clone::MapClone); store.register_late_pass(|| box map_err_ignore::MapErrIgnore); store.register_late_pass(|| box shadow::Shadow); - store.register_late_pass(|| box types::LetUnitValue); - store.register_late_pass(|| box types::UnitCmp); + store.register_late_pass(|| box unit_types::UnitTypes); store.register_late_pass(|| box loops::Loops); store.register_late_pass(|| box main_recursion::MainRecursion::default()); store.register_late_pass(|| box lifetimes::Lifetimes); store.register_late_pass(|| box entry::HashMapPass); - store.register_late_pass(|| box types::Casts); - let type_complexity_threshold = conf.type_complexity_threshold; - store.register_late_pass(move || box types::TypeComplexity::new(type_complexity_threshold)); store.register_late_pass(|| box minmax::MinMaxPass); store.register_late_pass(|| box open_options::OpenOptions); store.register_late_pass(|| box zero_div_zero::ZeroDiv); @@ -1105,12 +1124,11 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| box panic_unimplemented::PanicUnimplemented); store.register_late_pass(|| box strings::StringLitAsBytes); store.register_late_pass(|| box derive::Derive); - store.register_late_pass(|| box types::CharLitAsU8); store.register_late_pass(|| box get_last_with_len::GetLastWithLen); store.register_late_pass(|| box drop_forget_ref::DropForgetRef); store.register_late_pass(|| box empty_enum::EmptyEnum); - store.register_late_pass(|| box types::AbsurdExtremeComparisons); - store.register_late_pass(|| box types::InvalidUpcastComparisons); + store.register_late_pass(|| box absurd_extreme_comparisons::AbsurdExtremeComparisons); + store.register_late_pass(|| box invalid_upcast_comparisons::InvalidUpcastComparisons); store.register_late_pass(|| box regex::Regex::default()); store.register_late_pass(|| box copies::CopyAndPaste); store.register_late_pass(|| box copy_iterator::CopyIterator); @@ -1120,9 +1138,9 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| box new_without_default::NewWithoutDefault::default()); let blacklisted_names = conf.blacklisted_names.iter().cloned().collect::<FxHashSet<_>>(); store.register_late_pass(move || box blacklisted_name::BlacklistedName::new(blacklisted_names.clone())); - let too_many_arguments_threshold1 = conf.too_many_arguments_threshold; - let too_many_lines_threshold2 = conf.too_many_lines_threshold; - store.register_late_pass(move || box functions::Functions::new(too_many_arguments_threshold1, too_many_lines_threshold2)); + let too_many_arguments_threshold = conf.too_many_arguments_threshold; + let too_many_lines_threshold = conf.too_many_lines_threshold; + store.register_late_pass(move || box functions::Functions::new(too_many_arguments_threshold, too_many_lines_threshold)); let doc_valid_idents = conf.doc_valid_idents.iter().cloned().collect::<FxHashSet<_>>(); store.register_late_pass(move || box doc::DocMarkdown::new(doc_valid_idents.clone())); store.register_late_pass(|| box neg_multiply::NegMultiply); @@ -1154,9 +1172,8 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| box infinite_iter::InfiniteIter); store.register_late_pass(|| box inline_fn_without_body::InlineFnWithoutBody); store.register_late_pass(|| box useless_conversion::UselessConversion::default()); - store.register_late_pass(|| box types::ImplicitHasher); + store.register_late_pass(|| box implicit_hasher::ImplicitHasher); store.register_late_pass(|| box fallible_impl_from::FallibleImplFrom); - store.register_late_pass(|| box types::UnitArg); store.register_late_pass(|| box double_comparison::DoubleComparisons); store.register_late_pass(|| box question_mark::QuestionMark); store.register_early_pass(|| box suspicious_operation_groupings::SuspiciousOperationGroupings); @@ -1173,7 +1190,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| box slow_vector_initialization::SlowVectorInit); store.register_late_pass(|| box unnecessary_sort_by::UnnecessarySortBy); store.register_late_pass(|| box unnecessary_wraps::UnnecessaryWraps); - store.register_late_pass(|| box types::RefToMut); store.register_late_pass(|| box assertions_on_constants::AssertionsOnConstants); store.register_late_pass(|| box transmuting_null::TransmutingNull); store.register_late_pass(|| box path_buf_push_overwrite::PathBufPushOverwrite); @@ -1240,7 +1256,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| box verbose_file_reads::VerboseFileReads); store.register_late_pass(|| box redundant_pub_crate::RedundantPubCrate::default()); store.register_late_pass(|| box unnamed_address::UnnamedAddress); - store.register_late_pass(|| box dereference::Dereferencing); + store.register_late_pass(|| box dereference::Dereferencing::default()); store.register_late_pass(|| box option_if_let_else::OptionIfLetElse); store.register_late_pass(|| box future_not_send::FutureNotSend); store.register_late_pass(|| box if_let_mutex::IfLetMutex); @@ -1253,7 +1269,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_early_pass(move || box non_expressive_names::NonExpressiveNames { single_char_binding_names_threshold, }); - store.register_early_pass(|| box unnested_or_patterns::UnnestedOrPatterns); store.register_late_pass(|| box macro_use::MacroUseImports::default()); store.register_late_pass(|| box map_identity::MapIdentity); store.register_late_pass(|| box pattern_type_mismatch::PatternTypeMismatch); @@ -1275,788 +1290,799 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| box strings::StringToString); store.register_late_pass(|| box zero_sized_map_values::ZeroSizedMapValues); store.register_late_pass(|| box vec_init_then_push::VecInitThenPush::default()); - store.register_late_pass(move || box types::PtrAsPtr::new(msrv)); store.register_late_pass(|| box case_sensitive_file_extension_comparisons::CaseSensitiveFileExtensionComparisons); store.register_late_pass(|| box redundant_slicing::RedundantSlicing); store.register_late_pass(|| box from_str_radix_10::FromStrRadix10); store.register_late_pass(|| box manual_map::ManualMap); + store.register_late_pass(move || box if_then_some_else_none::IfThenSomeElseNone::new(msrv)); store.register_group(true, "clippy::restriction", Some("clippy_restriction"), vec![ - LintId::of(&arithmetic::FLOAT_ARITHMETIC), - LintId::of(&arithmetic::INTEGER_ARITHMETIC), - LintId::of(&as_conversions::AS_CONVERSIONS), - LintId::of(&asm_syntax::INLINE_ASM_X86_ATT_SYNTAX), - LintId::of(&asm_syntax::INLINE_ASM_X86_INTEL_SYNTAX), - LintId::of(&create_dir::CREATE_DIR), - LintId::of(&dbg_macro::DBG_MACRO), - LintId::of(&default_numeric_fallback::DEFAULT_NUMERIC_FALLBACK), - LintId::of(&else_if_without_else::ELSE_IF_WITHOUT_ELSE), - LintId::of(&exhaustive_items::EXHAUSTIVE_ENUMS), - LintId::of(&exhaustive_items::EXHAUSTIVE_STRUCTS), - LintId::of(&exit::EXIT), - LintId::of(&float_literal::LOSSY_FLOAT_LITERAL), - LintId::of(&implicit_return::IMPLICIT_RETURN), - LintId::of(&indexing_slicing::INDEXING_SLICING), - LintId::of(&inherent_impl::MULTIPLE_INHERENT_IMPL), - LintId::of(&integer_division::INTEGER_DIVISION), - LintId::of(&let_underscore::LET_UNDERSCORE_MUST_USE), - LintId::of(&literal_representation::DECIMAL_LITERAL_REPRESENTATION), - LintId::of(&map_err_ignore::MAP_ERR_IGNORE), - LintId::of(&matches::REST_PAT_IN_FULLY_BOUND_STRUCTS), - LintId::of(&matches::WILDCARD_ENUM_MATCH_ARM), - LintId::of(&mem_forget::MEM_FORGET), - LintId::of(&methods::CLONE_ON_REF_PTR), - LintId::of(&methods::EXPECT_USED), - LintId::of(&methods::FILETYPE_IS_FILE), - LintId::of(&methods::GET_UNWRAP), - LintId::of(&methods::UNWRAP_USED), - LintId::of(&methods::WRONG_PUB_SELF_CONVENTION), - LintId::of(&misc::FLOAT_CMP_CONST), - LintId::of(&misc_early::UNNEEDED_FIELD_PATTERN), - LintId::of(&missing_doc::MISSING_DOCS_IN_PRIVATE_ITEMS), - LintId::of(&missing_inline::MISSING_INLINE_IN_PUBLIC_ITEMS), - LintId::of(&modulo_arithmetic::MODULO_ARITHMETIC), - LintId::of(&panic_in_result_fn::PANIC_IN_RESULT_FN), - LintId::of(&panic_unimplemented::PANIC), - LintId::of(&panic_unimplemented::TODO), - LintId::of(&panic_unimplemented::UNIMPLEMENTED), - LintId::of(&panic_unimplemented::UNREACHABLE), - LintId::of(&pattern_type_mismatch::PATTERN_TYPE_MISMATCH), - LintId::of(&semicolon_if_nothing_returned::SEMICOLON_IF_NOTHING_RETURNED), - LintId::of(&shadow::SHADOW_REUSE), - LintId::of(&shadow::SHADOW_SAME), - LintId::of(&strings::STRING_ADD), - LintId::of(&strings::STRING_TO_STRING), - LintId::of(&strings::STR_TO_STRING), - LintId::of(&types::RC_BUFFER), - LintId::of(&unwrap_in_result::UNWRAP_IN_RESULT), - LintId::of(&verbose_file_reads::VERBOSE_FILE_READS), - LintId::of(&write::PRINT_STDERR), - LintId::of(&write::PRINT_STDOUT), - LintId::of(&write::USE_DEBUG), + LintId::of(arithmetic::FLOAT_ARITHMETIC), + LintId::of(arithmetic::INTEGER_ARITHMETIC), + LintId::of(as_conversions::AS_CONVERSIONS), + LintId::of(asm_syntax::INLINE_ASM_X86_ATT_SYNTAX), + LintId::of(asm_syntax::INLINE_ASM_X86_INTEL_SYNTAX), + LintId::of(create_dir::CREATE_DIR), + LintId::of(dbg_macro::DBG_MACRO), + LintId::of(default_numeric_fallback::DEFAULT_NUMERIC_FALLBACK), + LintId::of(else_if_without_else::ELSE_IF_WITHOUT_ELSE), + LintId::of(exhaustive_items::EXHAUSTIVE_ENUMS), + LintId::of(exhaustive_items::EXHAUSTIVE_STRUCTS), + LintId::of(exit::EXIT), + LintId::of(float_literal::LOSSY_FLOAT_LITERAL), + LintId::of(if_then_some_else_none::IF_THEN_SOME_ELSE_NONE), + LintId::of(implicit_return::IMPLICIT_RETURN), + LintId::of(indexing_slicing::INDEXING_SLICING), + LintId::of(inherent_impl::MULTIPLE_INHERENT_IMPL), + LintId::of(integer_division::INTEGER_DIVISION), + LintId::of(let_underscore::LET_UNDERSCORE_MUST_USE), + LintId::of(literal_representation::DECIMAL_LITERAL_REPRESENTATION), + LintId::of(map_err_ignore::MAP_ERR_IGNORE), + LintId::of(matches::REST_PAT_IN_FULLY_BOUND_STRUCTS), + LintId::of(matches::WILDCARD_ENUM_MATCH_ARM), + LintId::of(mem_forget::MEM_FORGET), + LintId::of(methods::CLONE_ON_REF_PTR), + LintId::of(methods::EXPECT_USED), + LintId::of(methods::FILETYPE_IS_FILE), + LintId::of(methods::GET_UNWRAP), + LintId::of(methods::UNWRAP_USED), + LintId::of(methods::WRONG_PUB_SELF_CONVENTION), + LintId::of(misc::FLOAT_CMP_CONST), + LintId::of(misc_early::UNNEEDED_FIELD_PATTERN), + LintId::of(missing_doc::MISSING_DOCS_IN_PRIVATE_ITEMS), + LintId::of(missing_inline::MISSING_INLINE_IN_PUBLIC_ITEMS), + LintId::of(modulo_arithmetic::MODULO_ARITHMETIC), + LintId::of(panic_in_result_fn::PANIC_IN_RESULT_FN), + LintId::of(panic_unimplemented::PANIC), + LintId::of(panic_unimplemented::TODO), + LintId::of(panic_unimplemented::UNIMPLEMENTED), + LintId::of(panic_unimplemented::UNREACHABLE), + LintId::of(pattern_type_mismatch::PATTERN_TYPE_MISMATCH), + LintId::of(semicolon_if_nothing_returned::SEMICOLON_IF_NOTHING_RETURNED), + LintId::of(shadow::SHADOW_REUSE), + LintId::of(shadow::SHADOW_SAME), + LintId::of(strings::STRING_ADD), + LintId::of(strings::STRING_TO_STRING), + LintId::of(strings::STR_TO_STRING), + LintId::of(types::RC_BUFFER), + LintId::of(unwrap_in_result::UNWRAP_IN_RESULT), + LintId::of(verbose_file_reads::VERBOSE_FILE_READS), + LintId::of(write::PRINT_STDERR), + LintId::of(write::PRINT_STDOUT), + LintId::of(write::USE_DEBUG), ]); store.register_group(true, "clippy::pedantic", Some("clippy_pedantic"), vec![ - LintId::of(&attrs::INLINE_ALWAYS), - 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(&case_sensitive_file_extension_comparisons::CASE_SENSITIVE_FILE_EXTENSION_COMPARISONS), - LintId::of(&checked_conversions::CHECKED_CONVERSIONS), - LintId::of(&copies::SAME_FUNCTIONS_IN_IF_CONDITION), - LintId::of(©_iterator::COPY_ITERATOR), - LintId::of(&default::DEFAULT_TRAIT_ACCESS), - LintId::of(&dereference::EXPLICIT_DEREF_METHODS), - LintId::of(&derive::EXPL_IMPL_CLONE_ON_COPY), - LintId::of(&derive::UNSAFE_DERIVE_DESERIALIZE), - LintId::of(&doc::DOC_MARKDOWN), - LintId::of(&doc::MISSING_ERRORS_DOC), - LintId::of(&doc::MISSING_PANICS_DOC), - LintId::of(&empty_enum::EMPTY_ENUM), - LintId::of(&enum_variants::MODULE_NAME_REPETITIONS), - LintId::of(&enum_variants::PUB_ENUM_VARIANT_NAMES), - LintId::of(&eta_reduction::REDUNDANT_CLOSURE_FOR_METHOD_CALLS), - LintId::of(&excessive_bools::FN_PARAMS_EXCESSIVE_BOOLS), - LintId::of(&excessive_bools::STRUCT_EXCESSIVE_BOOLS), - LintId::of(&functions::MUST_USE_CANDIDATE), - LintId::of(&functions::TOO_MANY_LINES), - LintId::of(&if_not_else::IF_NOT_ELSE), - LintId::of(&implicit_saturating_sub::IMPLICIT_SATURATING_SUB), - LintId::of(&infinite_iter::MAYBE_INFINITE_ITER), - LintId::of(&items_after_statements::ITEMS_AFTER_STATEMENTS), - LintId::of(&large_stack_arrays::LARGE_STACK_ARRAYS), - LintId::of(&let_underscore::LET_UNDERSCORE_DROP), - LintId::of(&literal_representation::LARGE_DIGIT_GROUPS), - LintId::of(&literal_representation::UNREADABLE_LITERAL), - LintId::of(&loops::EXPLICIT_INTO_ITER_LOOP), - LintId::of(&loops::EXPLICIT_ITER_LOOP), - LintId::of(¯o_use::MACRO_USE_IMPORTS), - LintId::of(&manual_ok_or::MANUAL_OK_OR), - LintId::of(&match_on_vec_items::MATCH_ON_VEC_ITEMS), - LintId::of(&matches::MATCH_BOOL), - LintId::of(&matches::MATCH_SAME_ARMS), - LintId::of(&matches::MATCH_WILDCARD_FOR_SINGLE_VARIANTS), - LintId::of(&matches::MATCH_WILD_ERR_ARM), - LintId::of(&matches::SINGLE_MATCH_ELSE), - LintId::of(&methods::FILTER_MAP), - LintId::of(&methods::FILTER_MAP_NEXT), - LintId::of(&methods::INEFFICIENT_TO_STRING), - LintId::of(&methods::MAP_FLATTEN), - LintId::of(&methods::MAP_UNWRAP_OR), - LintId::of(&misc::USED_UNDERSCORE_BINDING), - LintId::of(&misc_early::UNSEPARATED_LITERAL_SUFFIX), - LintId::of(&mut_mut::MUT_MUT), - LintId::of(&needless_continue::NEEDLESS_CONTINUE), - LintId::of(&needless_pass_by_value::NEEDLESS_PASS_BY_VALUE), - LintId::of(&non_expressive_names::SIMILAR_NAMES), - LintId::of(&option_if_let_else::OPTION_IF_LET_ELSE), - LintId::of(&pass_by_ref_or_value::LARGE_TYPES_PASSED_BY_VALUE), - LintId::of(&pass_by_ref_or_value::TRIVIALLY_COPY_PASS_BY_REF), - LintId::of(&ranges::RANGE_MINUS_ONE), - LintId::of(&ranges::RANGE_PLUS_ONE), - LintId::of(&redundant_else::REDUNDANT_ELSE), - LintId::of(&ref_option_ref::REF_OPTION_REF), - LintId::of(&shadow::SHADOW_UNRELATED), - LintId::of(&strings::STRING_ADD_ASSIGN), - LintId::of(&trait_bounds::TRAIT_DUPLICATION_IN_BOUNDS), - LintId::of(&trait_bounds::TYPE_REPETITION_IN_BOUNDS), - LintId::of(&types::CAST_LOSSLESS), - LintId::of(&types::CAST_POSSIBLE_TRUNCATION), - LintId::of(&types::CAST_POSSIBLE_WRAP), - LintId::of(&types::CAST_PRECISION_LOSS), - LintId::of(&types::CAST_PTR_ALIGNMENT), - LintId::of(&types::CAST_SIGN_LOSS), - LintId::of(&types::IMPLICIT_HASHER), - LintId::of(&types::INVALID_UPCAST_COMPARISONS), - LintId::of(&types::LET_UNIT_VALUE), - LintId::of(&types::LINKEDLIST), - LintId::of(&types::OPTION_OPTION), - LintId::of(&types::PTR_AS_PTR), - LintId::of(&unicode::NON_ASCII_LITERAL), - LintId::of(&unicode::UNICODE_NOT_NFC), - LintId::of(&unnecessary_wraps::UNNECESSARY_WRAPS), - LintId::of(&unnested_or_patterns::UNNESTED_OR_PATTERNS), - LintId::of(&unused_self::UNUSED_SELF), - LintId::of(&wildcard_imports::ENUM_GLOB_USE), - LintId::of(&wildcard_imports::WILDCARD_IMPORTS), - LintId::of(&zero_sized_map_values::ZERO_SIZED_MAP_VALUES), + LintId::of(attrs::INLINE_ALWAYS), + 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(bytecount::NAIVE_BYTECOUNT), + LintId::of(case_sensitive_file_extension_comparisons::CASE_SENSITIVE_FILE_EXTENSION_COMPARISONS), + LintId::of(casts::CAST_LOSSLESS), + LintId::of(casts::CAST_POSSIBLE_TRUNCATION), + LintId::of(casts::CAST_POSSIBLE_WRAP), + LintId::of(casts::CAST_PRECISION_LOSS), + LintId::of(casts::CAST_PTR_ALIGNMENT), + LintId::of(casts::CAST_SIGN_LOSS), + LintId::of(casts::PTR_AS_PTR), + LintId::of(checked_conversions::CHECKED_CONVERSIONS), + LintId::of(copies::SAME_FUNCTIONS_IN_IF_CONDITION), + LintId::of(copy_iterator::COPY_ITERATOR), + LintId::of(default::DEFAULT_TRAIT_ACCESS), + LintId::of(dereference::EXPLICIT_DEREF_METHODS), + LintId::of(derive::EXPL_IMPL_CLONE_ON_COPY), + LintId::of(derive::UNSAFE_DERIVE_DESERIALIZE), + LintId::of(doc::DOC_MARKDOWN), + LintId::of(doc::MISSING_ERRORS_DOC), + LintId::of(doc::MISSING_PANICS_DOC), + LintId::of(empty_enum::EMPTY_ENUM), + LintId::of(enum_variants::MODULE_NAME_REPETITIONS), + LintId::of(enum_variants::PUB_ENUM_VARIANT_NAMES), + LintId::of(eta_reduction::REDUNDANT_CLOSURE_FOR_METHOD_CALLS), + LintId::of(excessive_bools::FN_PARAMS_EXCESSIVE_BOOLS), + LintId::of(excessive_bools::STRUCT_EXCESSIVE_BOOLS), + LintId::of(functions::MUST_USE_CANDIDATE), + LintId::of(functions::TOO_MANY_LINES), + LintId::of(if_not_else::IF_NOT_ELSE), + LintId::of(implicit_hasher::IMPLICIT_HASHER), + LintId::of(implicit_saturating_sub::IMPLICIT_SATURATING_SUB), + LintId::of(infinite_iter::MAYBE_INFINITE_ITER), + LintId::of(invalid_upcast_comparisons::INVALID_UPCAST_COMPARISONS), + LintId::of(items_after_statements::ITEMS_AFTER_STATEMENTS), + LintId::of(large_stack_arrays::LARGE_STACK_ARRAYS), + LintId::of(let_underscore::LET_UNDERSCORE_DROP), + LintId::of(literal_representation::LARGE_DIGIT_GROUPS), + LintId::of(literal_representation::UNREADABLE_LITERAL), + LintId::of(loops::EXPLICIT_INTO_ITER_LOOP), + LintId::of(loops::EXPLICIT_ITER_LOOP), + LintId::of(macro_use::MACRO_USE_IMPORTS), + LintId::of(manual_ok_or::MANUAL_OK_OR), + LintId::of(match_on_vec_items::MATCH_ON_VEC_ITEMS), + LintId::of(matches::MATCH_BOOL), + LintId::of(matches::MATCH_SAME_ARMS), + LintId::of(matches::MATCH_WILDCARD_FOR_SINGLE_VARIANTS), + LintId::of(matches::MATCH_WILD_ERR_ARM), + LintId::of(matches::SINGLE_MATCH_ELSE), + LintId::of(methods::FILTER_MAP), + LintId::of(methods::FILTER_MAP_NEXT), + LintId::of(methods::IMPLICIT_CLONE), + LintId::of(methods::INEFFICIENT_TO_STRING), + LintId::of(methods::MAP_FLATTEN), + LintId::of(methods::MAP_UNWRAP_OR), + LintId::of(misc::USED_UNDERSCORE_BINDING), + LintId::of(misc_early::UNSEPARATED_LITERAL_SUFFIX), + LintId::of(mut_mut::MUT_MUT), + LintId::of(needless_continue::NEEDLESS_CONTINUE), + LintId::of(needless_for_each::NEEDLESS_FOR_EACH), + LintId::of(needless_pass_by_value::NEEDLESS_PASS_BY_VALUE), + LintId::of(non_expressive_names::SIMILAR_NAMES), + LintId::of(option_if_let_else::OPTION_IF_LET_ELSE), + LintId::of(pass_by_ref_or_value::LARGE_TYPES_PASSED_BY_VALUE), + LintId::of(pass_by_ref_or_value::TRIVIALLY_COPY_PASS_BY_REF), + LintId::of(ranges::RANGE_MINUS_ONE), + LintId::of(ranges::RANGE_PLUS_ONE), + LintId::of(redundant_else::REDUNDANT_ELSE), + LintId::of(ref_option_ref::REF_OPTION_REF), + LintId::of(shadow::SHADOW_UNRELATED), + LintId::of(strings::STRING_ADD_ASSIGN), + LintId::of(trait_bounds::TRAIT_DUPLICATION_IN_BOUNDS), + LintId::of(trait_bounds::TYPE_REPETITION_IN_BOUNDS), + LintId::of(types::LINKEDLIST), + LintId::of(types::OPTION_OPTION), + LintId::of(unicode::NON_ASCII_LITERAL), + LintId::of(unicode::UNICODE_NOT_NFC), + LintId::of(unit_types::LET_UNIT_VALUE), + LintId::of(unnecessary_wraps::UNNECESSARY_WRAPS), + LintId::of(unnested_or_patterns::UNNESTED_OR_PATTERNS), + LintId::of(unused_self::UNUSED_SELF), + LintId::of(wildcard_imports::ENUM_GLOB_USE), + LintId::of(wildcard_imports::WILDCARD_IMPORTS), + LintId::of(zero_sized_map_values::ZERO_SIZED_MAP_VALUES), ]); #[cfg(feature = "internal-lints")] store.register_group(true, "clippy::internal", Some("clippy_internal"), vec![ - LintId::of(&utils::internal_lints::CLIPPY_LINTS_INTERNAL), - LintId::of(&utils::internal_lints::COLLAPSIBLE_SPAN_LINT_CALLS), - LintId::of(&utils::internal_lints::COMPILER_LINT_FUNCTIONS), - LintId::of(&utils::internal_lints::DEFAULT_LINT), - LintId::of(&utils::internal_lints::INTERNING_DEFINED_SYMBOL), - LintId::of(&utils::internal_lints::INVALID_PATHS), - LintId::of(&utils::internal_lints::LINT_WITHOUT_LINT_PASS), - LintId::of(&utils::internal_lints::MATCH_TYPE_ON_DIAGNOSTIC_ITEM), - LintId::of(&utils::internal_lints::OUTER_EXPN_EXPN_DATA), - LintId::of(&utils::internal_lints::PRODUCE_ICE), - LintId::of(&utils::internal_lints::UNNECESSARY_SYMBOL_STR), + LintId::of(utils::internal_lints::CLIPPY_LINTS_INTERNAL), + LintId::of(utils::internal_lints::COLLAPSIBLE_SPAN_LINT_CALLS), + LintId::of(utils::internal_lints::COMPILER_LINT_FUNCTIONS), + LintId::of(utils::internal_lints::DEFAULT_LINT), + LintId::of(utils::internal_lints::IF_CHAIN_STYLE), + LintId::of(utils::internal_lints::INTERNING_DEFINED_SYMBOL), + LintId::of(utils::internal_lints::INVALID_PATHS), + LintId::of(utils::internal_lints::LINT_WITHOUT_LINT_PASS), + LintId::of(utils::internal_lints::MATCH_TYPE_ON_DIAGNOSTIC_ITEM), + LintId::of(utils::internal_lints::OUTER_EXPN_EXPN_DATA), + LintId::of(utils::internal_lints::PRODUCE_ICE), + LintId::of(utils::internal_lints::UNNECESSARY_SYMBOL_STR), ]); store.register_group(true, "clippy::all", Some("clippy"), vec![ - LintId::of(&approx_const::APPROX_CONSTANT), - LintId::of(&assertions_on_constants::ASSERTIONS_ON_CONSTANTS), - LintId::of(&assign_ops::ASSIGN_OP_PATTERN), - LintId::of(&assign_ops::MISREFACTORED_ASSIGN_OP), - LintId::of(&async_yields_async::ASYNC_YIELDS_ASYNC), - LintId::of(&atomic_ordering::INVALID_ATOMIC_ORDERING), - LintId::of(&attrs::BLANKET_CLIPPY_RESTRICTION_LINTS), - LintId::of(&attrs::DEPRECATED_CFG_ATTR), - LintId::of(&attrs::DEPRECATED_SEMVER), - LintId::of(&attrs::MISMATCHED_TARGET_OS), - LintId::of(&attrs::USELESS_ATTRIBUTE), - LintId::of(&bit_mask::BAD_BIT_MASK), - LintId::of(&bit_mask::INEFFECTIVE_BIT_MASK), - LintId::of(&blacklisted_name::BLACKLISTED_NAME), - LintId::of(&blocks_in_if_conditions::BLOCKS_IN_IF_CONDITIONS), - LintId::of(&booleans::LOGIC_BUG), - LintId::of(&booleans::NONMINIMAL_BOOL), - LintId::of(&bytecount::NAIVE_BYTECOUNT), - LintId::of(&collapsible_if::COLLAPSIBLE_ELSE_IF), - LintId::of(&collapsible_if::COLLAPSIBLE_IF), - LintId::of(&collapsible_match::COLLAPSIBLE_MATCH), - LintId::of(&comparison_chain::COMPARISON_CHAIN), - LintId::of(&copies::IFS_SAME_COND), - LintId::of(&copies::IF_SAME_THEN_ELSE), - LintId::of(&default::FIELD_REASSIGN_WITH_DEFAULT), - LintId::of(&derive::DERIVE_HASH_XOR_EQ), - LintId::of(&derive::DERIVE_ORD_XOR_PARTIAL_ORD), - LintId::of(&doc::MISSING_SAFETY_DOC), - LintId::of(&doc::NEEDLESS_DOCTEST_MAIN), - LintId::of(&double_comparison::DOUBLE_COMPARISONS), - LintId::of(&double_parens::DOUBLE_PARENS), - LintId::of(&drop_forget_ref::DROP_COPY), - LintId::of(&drop_forget_ref::DROP_REF), - LintId::of(&drop_forget_ref::FORGET_COPY), - LintId::of(&drop_forget_ref::FORGET_REF), - LintId::of(&duration_subsec::DURATION_SUBSEC), - LintId::of(&entry::MAP_ENTRY), - LintId::of(&enum_clike::ENUM_CLIKE_UNPORTABLE_VARIANT), - LintId::of(&enum_variants::ENUM_VARIANT_NAMES), - LintId::of(&enum_variants::MODULE_INCEPTION), - LintId::of(&eq_op::EQ_OP), - LintId::of(&eq_op::OP_REF), - LintId::of(&erasing_op::ERASING_OP), - LintId::of(&escape::BOXED_LOCAL), - LintId::of(&eta_reduction::REDUNDANT_CLOSURE), - LintId::of(&eval_order_dependence::DIVERGING_SUB_EXPRESSION), - LintId::of(&eval_order_dependence::EVAL_ORDER_DEPENDENCE), - LintId::of(&explicit_write::EXPLICIT_WRITE), - LintId::of(&float_equality_without_abs::FLOAT_EQUALITY_WITHOUT_ABS), - LintId::of(&float_literal::EXCESSIVE_PRECISION), - LintId::of(&format::USELESS_FORMAT), - LintId::of(&formatting::POSSIBLE_MISSING_COMMA), - LintId::of(&formatting::SUSPICIOUS_ASSIGNMENT_FORMATTING), - LintId::of(&formatting::SUSPICIOUS_ELSE_FORMATTING), - LintId::of(&formatting::SUSPICIOUS_UNARY_OP_FORMATTING), - LintId::of(&from_over_into::FROM_OVER_INTO), - LintId::of(&from_str_radix_10::FROM_STR_RADIX_10), - LintId::of(&functions::DOUBLE_MUST_USE), - LintId::of(&functions::MUST_USE_UNIT), - LintId::of(&functions::NOT_UNSAFE_PTR_ARG_DEREF), - LintId::of(&functions::RESULT_UNIT_ERR), - LintId::of(&functions::TOO_MANY_ARGUMENTS), - LintId::of(&get_last_with_len::GET_LAST_WITH_LEN), - LintId::of(&identity_op::IDENTITY_OP), - LintId::of(&if_let_mutex::IF_LET_MUTEX), - LintId::of(&if_let_some_result::IF_LET_SOME_RESULT), - LintId::of(&inconsistent_struct_constructor::INCONSISTENT_STRUCT_CONSTRUCTOR), - LintId::of(&indexing_slicing::OUT_OF_BOUNDS_INDEXING), - LintId::of(&infinite_iter::INFINITE_ITER), - LintId::of(&inherent_to_string::INHERENT_TO_STRING), - LintId::of(&inherent_to_string::INHERENT_TO_STRING_SHADOW_DISPLAY), - LintId::of(&inline_fn_without_body::INLINE_FN_WITHOUT_BODY), - LintId::of(&int_plus_one::INT_PLUS_ONE), - LintId::of(&large_const_arrays::LARGE_CONST_ARRAYS), - LintId::of(&large_enum_variant::LARGE_ENUM_VARIANT), - LintId::of(&len_zero::COMPARISON_TO_EMPTY), - LintId::of(&len_zero::LEN_WITHOUT_IS_EMPTY), - LintId::of(&len_zero::LEN_ZERO), - LintId::of(&let_underscore::LET_UNDERSCORE_LOCK), - LintId::of(&lifetimes::EXTRA_UNUSED_LIFETIMES), - LintId::of(&lifetimes::NEEDLESS_LIFETIMES), - LintId::of(&literal_representation::INCONSISTENT_DIGIT_GROUPING), - LintId::of(&literal_representation::MISTYPED_LITERAL_SUFFIXES), - LintId::of(&literal_representation::UNUSUAL_BYTE_GROUPINGS), - LintId::of(&loops::EMPTY_LOOP), - LintId::of(&loops::EXPLICIT_COUNTER_LOOP), - LintId::of(&loops::FOR_KV_MAP), - LintId::of(&loops::FOR_LOOPS_OVER_FALLIBLES), - LintId::of(&loops::ITER_NEXT_LOOP), - LintId::of(&loops::MANUAL_FLATTEN), - LintId::of(&loops::MANUAL_MEMCPY), - LintId::of(&loops::MUT_RANGE_BOUND), - LintId::of(&loops::NEEDLESS_COLLECT), - LintId::of(&loops::NEEDLESS_RANGE_LOOP), - LintId::of(&loops::NEVER_LOOP), - LintId::of(&loops::SAME_ITEM_PUSH), - LintId::of(&loops::SINGLE_ELEMENT_LOOP), - LintId::of(&loops::WHILE_IMMUTABLE_CONDITION), - LintId::of(&loops::WHILE_LET_LOOP), - LintId::of(&loops::WHILE_LET_ON_ITERATOR), - LintId::of(&main_recursion::MAIN_RECURSION), - LintId::of(&manual_async_fn::MANUAL_ASYNC_FN), - LintId::of(&manual_map::MANUAL_MAP), - LintId::of(&manual_non_exhaustive::MANUAL_NON_EXHAUSTIVE), - LintId::of(&manual_strip::MANUAL_STRIP), - LintId::of(&manual_unwrap_or::MANUAL_UNWRAP_OR), - LintId::of(&map_clone::MAP_CLONE), - LintId::of(&map_identity::MAP_IDENTITY), - LintId::of(&map_unit_fn::OPTION_MAP_UNIT_FN), - LintId::of(&map_unit_fn::RESULT_MAP_UNIT_FN), - LintId::of(&matches::INFALLIBLE_DESTRUCTURING_MATCH), - LintId::of(&matches::MATCH_AS_REF), - LintId::of(&matches::MATCH_LIKE_MATCHES_MACRO), - LintId::of(&matches::MATCH_OVERLAPPING_ARM), - LintId::of(&matches::MATCH_REF_PATS), - LintId::of(&matches::MATCH_SINGLE_BINDING), - LintId::of(&matches::REDUNDANT_PATTERN_MATCHING), - LintId::of(&matches::SINGLE_MATCH), - LintId::of(&matches::WILDCARD_IN_OR_PATTERNS), - LintId::of(&mem_discriminant::MEM_DISCRIMINANT_NON_ENUM), - LintId::of(&mem_replace::MEM_REPLACE_OPTION_WITH_NONE), - LintId::of(&mem_replace::MEM_REPLACE_WITH_DEFAULT), - LintId::of(&mem_replace::MEM_REPLACE_WITH_UNINIT), - LintId::of(&methods::BIND_INSTEAD_OF_MAP), - LintId::of(&methods::BYTES_NTH), - LintId::of(&methods::CHARS_LAST_CMP), - LintId::of(&methods::CHARS_NEXT_CMP), - LintId::of(&methods::CLONE_DOUBLE_REF), - LintId::of(&methods::CLONE_ON_COPY), - LintId::of(&methods::EXPECT_FUN_CALL), - LintId::of(&methods::FILTER_MAP_IDENTITY), - LintId::of(&methods::FILTER_NEXT), - LintId::of(&methods::FLAT_MAP_IDENTITY), - LintId::of(&methods::FROM_ITER_INSTEAD_OF_COLLECT), - LintId::of(&methods::INSPECT_FOR_EACH), - LintId::of(&methods::INTO_ITER_ON_REF), - LintId::of(&methods::ITERATOR_STEP_BY_ZERO), - LintId::of(&methods::ITER_CLONED_COLLECT), - LintId::of(&methods::ITER_NEXT_SLICE), - LintId::of(&methods::ITER_NTH), - LintId::of(&methods::ITER_NTH_ZERO), - LintId::of(&methods::ITER_SKIP_NEXT), - LintId::of(&methods::MANUAL_FILTER_MAP), - LintId::of(&methods::MANUAL_FIND_MAP), - LintId::of(&methods::MANUAL_SATURATING_ARITHMETIC), - LintId::of(&methods::MAP_COLLECT_RESULT_UNIT), - LintId::of(&methods::NEW_RET_NO_SELF), - LintId::of(&methods::OK_EXPECT), - LintId::of(&methods::OPTION_AS_REF_DEREF), - LintId::of(&methods::OPTION_MAP_OR_NONE), - LintId::of(&methods::OR_FUN_CALL), - LintId::of(&methods::RESULT_MAP_OR_INTO_OPTION), - LintId::of(&methods::SEARCH_IS_SOME), - LintId::of(&methods::SHOULD_IMPLEMENT_TRAIT), - LintId::of(&methods::SINGLE_CHAR_ADD_STR), - LintId::of(&methods::SINGLE_CHAR_PATTERN), - LintId::of(&methods::SKIP_WHILE_NEXT), - LintId::of(&methods::STRING_EXTEND_CHARS), - LintId::of(&methods::SUSPICIOUS_MAP), - LintId::of(&methods::UNINIT_ASSUMED_INIT), - LintId::of(&methods::UNNECESSARY_FILTER_MAP), - LintId::of(&methods::UNNECESSARY_FOLD), - LintId::of(&methods::UNNECESSARY_LAZY_EVALUATIONS), - LintId::of(&methods::USELESS_ASREF), - LintId::of(&methods::WRONG_SELF_CONVENTION), - LintId::of(&methods::ZST_OFFSET), - LintId::of(&minmax::MIN_MAX), - LintId::of(&misc::CMP_NAN), - LintId::of(&misc::CMP_OWNED), - LintId::of(&misc::FLOAT_CMP), - LintId::of(&misc::MODULO_ONE), - LintId::of(&misc::SHORT_CIRCUIT_STATEMENT), - LintId::of(&misc::TOPLEVEL_REF_ARG), - LintId::of(&misc::ZERO_PTR), - LintId::of(&misc_early::BUILTIN_TYPE_SHADOW), - LintId::of(&misc_early::DOUBLE_NEG), - LintId::of(&misc_early::DUPLICATE_UNDERSCORE_ARGUMENT), - LintId::of(&misc_early::MIXED_CASE_HEX_LITERALS), - LintId::of(&misc_early::REDUNDANT_PATTERN), - LintId::of(&misc_early::UNNEEDED_WILDCARD_PATTERN), - LintId::of(&misc_early::ZERO_PREFIXED_LITERAL), - 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), - LintId::of(&needless_borrowed_ref::NEEDLESS_BORROWED_REFERENCE), - LintId::of(&needless_question_mark::NEEDLESS_QUESTION_MARK), - LintId::of(&needless_update::NEEDLESS_UPDATE), - LintId::of(&neg_cmp_op_on_partial_ord::NEG_CMP_OP_ON_PARTIAL_ORD), - LintId::of(&neg_multiply::NEG_MULTIPLY), - LintId::of(&new_without_default::NEW_WITHOUT_DEFAULT), - LintId::of(&no_effect::NO_EFFECT), - LintId::of(&no_effect::UNNECESSARY_OPERATION), - LintId::of(&non_copy_const::BORROW_INTERIOR_MUTABLE_CONST), - LintId::of(&non_copy_const::DECLARE_INTERIOR_MUTABLE_CONST), - LintId::of(&non_expressive_names::JUST_UNDERSCORES_AND_DIGITS), - LintId::of(&non_expressive_names::MANY_SINGLE_CHAR_NAMES), - LintId::of(&open_options::NONSENSICAL_OPEN_OPTIONS), - LintId::of(&option_env_unwrap::OPTION_ENV_UNWRAP), - LintId::of(&overflow_check_conditional::OVERFLOW_CHECK_CONDITIONAL), - LintId::of(&partialeq_ne_impl::PARTIALEQ_NE_IMPL), - LintId::of(&precedence::PRECEDENCE), - LintId::of(&ptr::CMP_NULL), - LintId::of(&ptr::MUT_FROM_REF), - LintId::of(&ptr::PTR_ARG), - LintId::of(&ptr_eq::PTR_EQ), - LintId::of(&ptr_offset_with_cast::PTR_OFFSET_WITH_CAST), - LintId::of(&question_mark::QUESTION_MARK), - LintId::of(&ranges::MANUAL_RANGE_CONTAINS), - LintId::of(&ranges::RANGE_ZIP_WITH_LEN), - LintId::of(&ranges::REVERSED_EMPTY_RANGES), - LintId::of(&redundant_clone::REDUNDANT_CLONE), - LintId::of(&redundant_closure_call::REDUNDANT_CLOSURE_CALL), - LintId::of(&redundant_field_names::REDUNDANT_FIELD_NAMES), - LintId::of(&redundant_slicing::REDUNDANT_SLICING), - LintId::of(&redundant_static_lifetimes::REDUNDANT_STATIC_LIFETIMES), - LintId::of(&reference::DEREF_ADDROF), - LintId::of(&reference::REF_IN_DEREF), - LintId::of(®ex::INVALID_REGEX), - LintId::of(&repeat_once::REPEAT_ONCE), - LintId::of(&returns::LET_AND_RETURN), - LintId::of(&returns::NEEDLESS_RETURN), - LintId::of(&self_assignment::SELF_ASSIGNMENT), - LintId::of(&serde_api::SERDE_API_MISUSE), - LintId::of(&single_component_path_imports::SINGLE_COMPONENT_PATH_IMPORTS), - LintId::of(&size_of_in_element_count::SIZE_OF_IN_ELEMENT_COUNT), - LintId::of(&slow_vector_initialization::SLOW_VECTOR_INITIALIZATION), - LintId::of(&stable_sort_primitive::STABLE_SORT_PRIMITIVE), - LintId::of(&strings::STRING_FROM_UTF8_AS_BYTES), - LintId::of(&suspicious_operation_groupings::SUSPICIOUS_OPERATION_GROUPINGS), - LintId::of(&suspicious_trait_impl::SUSPICIOUS_ARITHMETIC_IMPL), - LintId::of(&suspicious_trait_impl::SUSPICIOUS_OP_ASSIGN_IMPL), - LintId::of(&swap::ALMOST_SWAPPED), - LintId::of(&swap::MANUAL_SWAP), - LintId::of(&tabs_in_doc_comments::TABS_IN_DOC_COMMENTS), - LintId::of(&temporary_assignment::TEMPORARY_ASSIGNMENT), - LintId::of(&to_digit_is_some::TO_DIGIT_IS_SOME), - LintId::of(&to_string_in_display::TO_STRING_IN_DISPLAY), - LintId::of(&transmute::CROSSPOINTER_TRANSMUTE), - LintId::of(&transmute::TRANSMUTES_EXPRESSIBLE_AS_PTR_CASTS), - LintId::of(&transmute::TRANSMUTE_BYTES_TO_STR), - LintId::of(&transmute::TRANSMUTE_FLOAT_TO_INT), - LintId::of(&transmute::TRANSMUTE_INT_TO_BOOL), - LintId::of(&transmute::TRANSMUTE_INT_TO_CHAR), - LintId::of(&transmute::TRANSMUTE_INT_TO_FLOAT), - LintId::of(&transmute::TRANSMUTE_PTR_TO_PTR), - LintId::of(&transmute::TRANSMUTE_PTR_TO_REF), - LintId::of(&transmute::UNSOUND_COLLECTION_TRANSMUTE), - LintId::of(&transmute::WRONG_TRANSMUTE), - LintId::of(&transmuting_null::TRANSMUTING_NULL), - LintId::of(&try_err::TRY_ERR), - LintId::of(&types::ABSURD_EXTREME_COMPARISONS), - LintId::of(&types::BORROWED_BOX), - LintId::of(&types::BOX_VEC), - LintId::of(&types::CAST_REF_TO_MUT), - LintId::of(&types::CHAR_LIT_AS_U8), - LintId::of(&types::FN_TO_NUMERIC_CAST), - LintId::of(&types::FN_TO_NUMERIC_CAST_WITH_TRUNCATION), - LintId::of(&types::REDUNDANT_ALLOCATION), - LintId::of(&types::TYPE_COMPLEXITY), - LintId::of(&types::UNIT_ARG), - LintId::of(&types::UNIT_CMP), - LintId::of(&types::UNNECESSARY_CAST), - LintId::of(&types::VEC_BOX), - LintId::of(&undropped_manually_drops::UNDROPPED_MANUALLY_DROPS), - LintId::of(&unicode::INVISIBLE_CHARACTERS), - LintId::of(&unit_return_expecting_ord::UNIT_RETURN_EXPECTING_ORD), - LintId::of(&unnamed_address::FN_ADDRESS_COMPARISONS), - LintId::of(&unnamed_address::VTABLE_ADDRESS_COMPARISONS), - LintId::of(&unnecessary_sort_by::UNNECESSARY_SORT_BY), - LintId::of(&unsafe_removed_from_name::UNSAFE_REMOVED_FROM_NAME), - LintId::of(&unused_io_amount::UNUSED_IO_AMOUNT), - LintId::of(&unused_unit::UNUSED_UNIT), - LintId::of(&unwrap::PANICKING_UNWRAP), - LintId::of(&unwrap::UNNECESSARY_UNWRAP), - LintId::of(&upper_case_acronyms::UPPER_CASE_ACRONYMS), - LintId::of(&useless_conversion::USELESS_CONVERSION), - LintId::of(&vec::USELESS_VEC), - LintId::of(&vec_init_then_push::VEC_INIT_THEN_PUSH), - LintId::of(&vec_resize_to_zero::VEC_RESIZE_TO_ZERO), - LintId::of(&write::PRINTLN_EMPTY_STRING), - LintId::of(&write::PRINT_LITERAL), - LintId::of(&write::PRINT_WITH_NEWLINE), - LintId::of(&write::WRITELN_EMPTY_STRING), - LintId::of(&write::WRITE_LITERAL), - LintId::of(&write::WRITE_WITH_NEWLINE), - LintId::of(&zero_div_zero::ZERO_DIVIDED_BY_ZERO), + LintId::of(absurd_extreme_comparisons::ABSURD_EXTREME_COMPARISONS), + LintId::of(approx_const::APPROX_CONSTANT), + LintId::of(assertions_on_constants::ASSERTIONS_ON_CONSTANTS), + LintId::of(assign_ops::ASSIGN_OP_PATTERN), + LintId::of(assign_ops::MISREFACTORED_ASSIGN_OP), + LintId::of(async_yields_async::ASYNC_YIELDS_ASYNC), + LintId::of(atomic_ordering::INVALID_ATOMIC_ORDERING), + LintId::of(attrs::BLANKET_CLIPPY_RESTRICTION_LINTS), + LintId::of(attrs::DEPRECATED_CFG_ATTR), + LintId::of(attrs::DEPRECATED_SEMVER), + LintId::of(attrs::MISMATCHED_TARGET_OS), + LintId::of(attrs::USELESS_ATTRIBUTE), + LintId::of(bit_mask::BAD_BIT_MASK), + LintId::of(bit_mask::INEFFECTIVE_BIT_MASK), + LintId::of(blacklisted_name::BLACKLISTED_NAME), + LintId::of(blocks_in_if_conditions::BLOCKS_IN_IF_CONDITIONS), + LintId::of(booleans::LOGIC_BUG), + LintId::of(booleans::NONMINIMAL_BOOL), + LintId::of(casts::CAST_REF_TO_MUT), + LintId::of(casts::CHAR_LIT_AS_U8), + LintId::of(casts::FN_TO_NUMERIC_CAST), + LintId::of(casts::FN_TO_NUMERIC_CAST_WITH_TRUNCATION), + LintId::of(casts::UNNECESSARY_CAST), + LintId::of(collapsible_if::COLLAPSIBLE_ELSE_IF), + LintId::of(collapsible_if::COLLAPSIBLE_IF), + LintId::of(collapsible_match::COLLAPSIBLE_MATCH), + LintId::of(comparison_chain::COMPARISON_CHAIN), + LintId::of(copies::BRANCHES_SHARING_CODE), + LintId::of(copies::IFS_SAME_COND), + LintId::of(copies::IF_SAME_THEN_ELSE), + LintId::of(default::FIELD_REASSIGN_WITH_DEFAULT), + LintId::of(derive::DERIVE_HASH_XOR_EQ), + LintId::of(derive::DERIVE_ORD_XOR_PARTIAL_ORD), + LintId::of(doc::MISSING_SAFETY_DOC), + LintId::of(doc::NEEDLESS_DOCTEST_MAIN), + LintId::of(double_comparison::DOUBLE_COMPARISONS), + LintId::of(double_parens::DOUBLE_PARENS), + LintId::of(drop_forget_ref::DROP_COPY), + LintId::of(drop_forget_ref::DROP_REF), + LintId::of(drop_forget_ref::FORGET_COPY), + LintId::of(drop_forget_ref::FORGET_REF), + LintId::of(duration_subsec::DURATION_SUBSEC), + LintId::of(entry::MAP_ENTRY), + LintId::of(enum_clike::ENUM_CLIKE_UNPORTABLE_VARIANT), + LintId::of(enum_variants::ENUM_VARIANT_NAMES), + LintId::of(enum_variants::MODULE_INCEPTION), + LintId::of(eq_op::EQ_OP), + LintId::of(eq_op::OP_REF), + LintId::of(erasing_op::ERASING_OP), + LintId::of(escape::BOXED_LOCAL), + LintId::of(eta_reduction::REDUNDANT_CLOSURE), + LintId::of(eval_order_dependence::DIVERGING_SUB_EXPRESSION), + LintId::of(eval_order_dependence::EVAL_ORDER_DEPENDENCE), + LintId::of(explicit_write::EXPLICIT_WRITE), + LintId::of(float_equality_without_abs::FLOAT_EQUALITY_WITHOUT_ABS), + LintId::of(float_literal::EXCESSIVE_PRECISION), + LintId::of(format::USELESS_FORMAT), + LintId::of(formatting::POSSIBLE_MISSING_COMMA), + LintId::of(formatting::SUSPICIOUS_ASSIGNMENT_FORMATTING), + LintId::of(formatting::SUSPICIOUS_ELSE_FORMATTING), + LintId::of(formatting::SUSPICIOUS_UNARY_OP_FORMATTING), + LintId::of(from_over_into::FROM_OVER_INTO), + LintId::of(from_str_radix_10::FROM_STR_RADIX_10), + LintId::of(functions::DOUBLE_MUST_USE), + LintId::of(functions::MUST_USE_UNIT), + LintId::of(functions::NOT_UNSAFE_PTR_ARG_DEREF), + LintId::of(functions::RESULT_UNIT_ERR), + LintId::of(functions::TOO_MANY_ARGUMENTS), + LintId::of(get_last_with_len::GET_LAST_WITH_LEN), + LintId::of(identity_op::IDENTITY_OP), + LintId::of(if_let_mutex::IF_LET_MUTEX), + LintId::of(if_let_some_result::IF_LET_SOME_RESULT), + LintId::of(inconsistent_struct_constructor::INCONSISTENT_STRUCT_CONSTRUCTOR), + LintId::of(indexing_slicing::OUT_OF_BOUNDS_INDEXING), + LintId::of(infinite_iter::INFINITE_ITER), + LintId::of(inherent_to_string::INHERENT_TO_STRING), + LintId::of(inherent_to_string::INHERENT_TO_STRING_SHADOW_DISPLAY), + LintId::of(inline_fn_without_body::INLINE_FN_WITHOUT_BODY), + LintId::of(int_plus_one::INT_PLUS_ONE), + LintId::of(large_const_arrays::LARGE_CONST_ARRAYS), + LintId::of(large_enum_variant::LARGE_ENUM_VARIANT), + LintId::of(len_zero::COMPARISON_TO_EMPTY), + LintId::of(len_zero::LEN_WITHOUT_IS_EMPTY), + LintId::of(len_zero::LEN_ZERO), + LintId::of(let_underscore::LET_UNDERSCORE_LOCK), + LintId::of(lifetimes::EXTRA_UNUSED_LIFETIMES), + LintId::of(lifetimes::NEEDLESS_LIFETIMES), + LintId::of(literal_representation::INCONSISTENT_DIGIT_GROUPING), + LintId::of(literal_representation::MISTYPED_LITERAL_SUFFIXES), + LintId::of(literal_representation::UNUSUAL_BYTE_GROUPINGS), + LintId::of(loops::EMPTY_LOOP), + LintId::of(loops::EXPLICIT_COUNTER_LOOP), + LintId::of(loops::FOR_KV_MAP), + LintId::of(loops::FOR_LOOPS_OVER_FALLIBLES), + LintId::of(loops::ITER_NEXT_LOOP), + LintId::of(loops::MANUAL_FLATTEN), + LintId::of(loops::MANUAL_MEMCPY), + LintId::of(loops::MUT_RANGE_BOUND), + LintId::of(loops::NEEDLESS_COLLECT), + LintId::of(loops::NEEDLESS_RANGE_LOOP), + LintId::of(loops::NEVER_LOOP), + LintId::of(loops::SAME_ITEM_PUSH), + LintId::of(loops::SINGLE_ELEMENT_LOOP), + LintId::of(loops::WHILE_IMMUTABLE_CONDITION), + LintId::of(loops::WHILE_LET_LOOP), + LintId::of(loops::WHILE_LET_ON_ITERATOR), + LintId::of(main_recursion::MAIN_RECURSION), + LintId::of(manual_async_fn::MANUAL_ASYNC_FN), + LintId::of(manual_map::MANUAL_MAP), + LintId::of(manual_non_exhaustive::MANUAL_NON_EXHAUSTIVE), + LintId::of(manual_strip::MANUAL_STRIP), + LintId::of(manual_unwrap_or::MANUAL_UNWRAP_OR), + LintId::of(map_clone::MAP_CLONE), + LintId::of(map_identity::MAP_IDENTITY), + LintId::of(map_unit_fn::OPTION_MAP_UNIT_FN), + LintId::of(map_unit_fn::RESULT_MAP_UNIT_FN), + LintId::of(matches::INFALLIBLE_DESTRUCTURING_MATCH), + LintId::of(matches::MATCH_AS_REF), + LintId::of(matches::MATCH_LIKE_MATCHES_MACRO), + LintId::of(matches::MATCH_OVERLAPPING_ARM), + LintId::of(matches::MATCH_REF_PATS), + LintId::of(matches::MATCH_SINGLE_BINDING), + LintId::of(matches::REDUNDANT_PATTERN_MATCHING), + LintId::of(matches::SINGLE_MATCH), + LintId::of(matches::WILDCARD_IN_OR_PATTERNS), + LintId::of(mem_discriminant::MEM_DISCRIMINANT_NON_ENUM), + LintId::of(mem_replace::MEM_REPLACE_OPTION_WITH_NONE), + LintId::of(mem_replace::MEM_REPLACE_WITH_DEFAULT), + LintId::of(mem_replace::MEM_REPLACE_WITH_UNINIT), + LintId::of(methods::BIND_INSTEAD_OF_MAP), + LintId::of(methods::BYTES_NTH), + LintId::of(methods::CHARS_LAST_CMP), + LintId::of(methods::CHARS_NEXT_CMP), + LintId::of(methods::CLONE_DOUBLE_REF), + LintId::of(methods::CLONE_ON_COPY), + LintId::of(methods::EXPECT_FUN_CALL), + LintId::of(methods::FILTER_MAP_IDENTITY), + LintId::of(methods::FILTER_NEXT), + LintId::of(methods::FLAT_MAP_IDENTITY), + LintId::of(methods::FROM_ITER_INSTEAD_OF_COLLECT), + LintId::of(methods::INSPECT_FOR_EACH), + LintId::of(methods::INTO_ITER_ON_REF), + LintId::of(methods::ITERATOR_STEP_BY_ZERO), + LintId::of(methods::ITER_CLONED_COLLECT), + LintId::of(methods::ITER_COUNT), + LintId::of(methods::ITER_NEXT_SLICE), + LintId::of(methods::ITER_NTH), + LintId::of(methods::ITER_NTH_ZERO), + LintId::of(methods::ITER_SKIP_NEXT), + LintId::of(methods::MANUAL_FILTER_MAP), + LintId::of(methods::MANUAL_FIND_MAP), + LintId::of(methods::MANUAL_SATURATING_ARITHMETIC), + LintId::of(methods::MAP_COLLECT_RESULT_UNIT), + LintId::of(methods::NEW_RET_NO_SELF), + LintId::of(methods::OK_EXPECT), + LintId::of(methods::OPTION_AS_REF_DEREF), + LintId::of(methods::OPTION_FILTER_MAP), + LintId::of(methods::OPTION_MAP_OR_NONE), + LintId::of(methods::OR_FUN_CALL), + LintId::of(methods::RESULT_MAP_OR_INTO_OPTION), + LintId::of(methods::SEARCH_IS_SOME), + LintId::of(methods::SHOULD_IMPLEMENT_TRAIT), + LintId::of(methods::SINGLE_CHAR_ADD_STR), + LintId::of(methods::SINGLE_CHAR_PATTERN), + LintId::of(methods::SKIP_WHILE_NEXT), + LintId::of(methods::STRING_EXTEND_CHARS), + LintId::of(methods::SUSPICIOUS_MAP), + LintId::of(methods::UNINIT_ASSUMED_INIT), + LintId::of(methods::UNNECESSARY_FILTER_MAP), + LintId::of(methods::UNNECESSARY_FOLD), + LintId::of(methods::UNNECESSARY_LAZY_EVALUATIONS), + LintId::of(methods::USELESS_ASREF), + LintId::of(methods::WRONG_SELF_CONVENTION), + LintId::of(methods::ZST_OFFSET), + LintId::of(minmax::MIN_MAX), + LintId::of(misc::CMP_NAN), + LintId::of(misc::CMP_OWNED), + LintId::of(misc::FLOAT_CMP), + LintId::of(misc::MODULO_ONE), + LintId::of(misc::SHORT_CIRCUIT_STATEMENT), + LintId::of(misc::TOPLEVEL_REF_ARG), + LintId::of(misc::ZERO_PTR), + LintId::of(misc_early::BUILTIN_TYPE_SHADOW), + LintId::of(misc_early::DOUBLE_NEG), + LintId::of(misc_early::DUPLICATE_UNDERSCORE_ARGUMENT), + LintId::of(misc_early::MIXED_CASE_HEX_LITERALS), + LintId::of(misc_early::REDUNDANT_PATTERN), + LintId::of(misc_early::UNNEEDED_WILDCARD_PATTERN), + LintId::of(misc_early::ZERO_PREFIXED_LITERAL), + 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), + LintId::of(needless_borrowed_ref::NEEDLESS_BORROWED_REFERENCE), + LintId::of(needless_question_mark::NEEDLESS_QUESTION_MARK), + LintId::of(needless_update::NEEDLESS_UPDATE), + LintId::of(neg_cmp_op_on_partial_ord::NEG_CMP_OP_ON_PARTIAL_ORD), + LintId::of(neg_multiply::NEG_MULTIPLY), + LintId::of(new_without_default::NEW_WITHOUT_DEFAULT), + LintId::of(no_effect::NO_EFFECT), + LintId::of(no_effect::UNNECESSARY_OPERATION), + LintId::of(non_copy_const::BORROW_INTERIOR_MUTABLE_CONST), + LintId::of(non_copy_const::DECLARE_INTERIOR_MUTABLE_CONST), + LintId::of(non_expressive_names::JUST_UNDERSCORES_AND_DIGITS), + LintId::of(non_expressive_names::MANY_SINGLE_CHAR_NAMES), + LintId::of(non_octal_unix_permissions::NON_OCTAL_UNIX_PERMISSIONS), + LintId::of(open_options::NONSENSICAL_OPEN_OPTIONS), + LintId::of(option_env_unwrap::OPTION_ENV_UNWRAP), + LintId::of(overflow_check_conditional::OVERFLOW_CHECK_CONDITIONAL), + LintId::of(partialeq_ne_impl::PARTIALEQ_NE_IMPL), + LintId::of(precedence::PRECEDENCE), + LintId::of(ptr::CMP_NULL), + LintId::of(ptr::MUT_FROM_REF), + LintId::of(ptr::PTR_ARG), + LintId::of(ptr_eq::PTR_EQ), + LintId::of(ptr_offset_with_cast::PTR_OFFSET_WITH_CAST), + LintId::of(question_mark::QUESTION_MARK), + LintId::of(ranges::MANUAL_RANGE_CONTAINS), + LintId::of(ranges::RANGE_ZIP_WITH_LEN), + LintId::of(ranges::REVERSED_EMPTY_RANGES), + LintId::of(redundant_clone::REDUNDANT_CLONE), + LintId::of(redundant_closure_call::REDUNDANT_CLOSURE_CALL), + LintId::of(redundant_field_names::REDUNDANT_FIELD_NAMES), + LintId::of(redundant_slicing::REDUNDANT_SLICING), + LintId::of(redundant_static_lifetimes::REDUNDANT_STATIC_LIFETIMES), + LintId::of(reference::DEREF_ADDROF), + LintId::of(reference::REF_IN_DEREF), + LintId::of(regex::INVALID_REGEX), + LintId::of(repeat_once::REPEAT_ONCE), + LintId::of(returns::LET_AND_RETURN), + LintId::of(returns::NEEDLESS_RETURN), + LintId::of(self_assignment::SELF_ASSIGNMENT), + LintId::of(serde_api::SERDE_API_MISUSE), + LintId::of(single_component_path_imports::SINGLE_COMPONENT_PATH_IMPORTS), + LintId::of(size_of_in_element_count::SIZE_OF_IN_ELEMENT_COUNT), + LintId::of(slow_vector_initialization::SLOW_VECTOR_INITIALIZATION), + LintId::of(stable_sort_primitive::STABLE_SORT_PRIMITIVE), + LintId::of(strings::STRING_FROM_UTF8_AS_BYTES), + LintId::of(suspicious_operation_groupings::SUSPICIOUS_OPERATION_GROUPINGS), + LintId::of(suspicious_trait_impl::SUSPICIOUS_ARITHMETIC_IMPL), + LintId::of(suspicious_trait_impl::SUSPICIOUS_OP_ASSIGN_IMPL), + LintId::of(swap::ALMOST_SWAPPED), + LintId::of(swap::MANUAL_SWAP), + LintId::of(tabs_in_doc_comments::TABS_IN_DOC_COMMENTS), + LintId::of(temporary_assignment::TEMPORARY_ASSIGNMENT), + LintId::of(to_digit_is_some::TO_DIGIT_IS_SOME), + LintId::of(to_string_in_display::TO_STRING_IN_DISPLAY), + LintId::of(transmute::CROSSPOINTER_TRANSMUTE), + LintId::of(transmute::TRANSMUTES_EXPRESSIBLE_AS_PTR_CASTS), + LintId::of(transmute::TRANSMUTE_BYTES_TO_STR), + LintId::of(transmute::TRANSMUTE_FLOAT_TO_INT), + LintId::of(transmute::TRANSMUTE_INT_TO_BOOL), + LintId::of(transmute::TRANSMUTE_INT_TO_CHAR), + LintId::of(transmute::TRANSMUTE_INT_TO_FLOAT), + LintId::of(transmute::TRANSMUTE_PTR_TO_PTR), + LintId::of(transmute::TRANSMUTE_PTR_TO_REF), + LintId::of(transmute::UNSOUND_COLLECTION_TRANSMUTE), + LintId::of(transmute::WRONG_TRANSMUTE), + LintId::of(transmuting_null::TRANSMUTING_NULL), + LintId::of(try_err::TRY_ERR), + LintId::of(types::BORROWED_BOX), + LintId::of(types::BOX_VEC), + LintId::of(types::REDUNDANT_ALLOCATION), + LintId::of(types::TYPE_COMPLEXITY), + LintId::of(types::VEC_BOX), + LintId::of(undropped_manually_drops::UNDROPPED_MANUALLY_DROPS), + LintId::of(unicode::INVISIBLE_CHARACTERS), + LintId::of(unit_return_expecting_ord::UNIT_RETURN_EXPECTING_ORD), + LintId::of(unit_types::UNIT_ARG), + LintId::of(unit_types::UNIT_CMP), + LintId::of(unnamed_address::FN_ADDRESS_COMPARISONS), + LintId::of(unnamed_address::VTABLE_ADDRESS_COMPARISONS), + LintId::of(unnecessary_sort_by::UNNECESSARY_SORT_BY), + LintId::of(unsafe_removed_from_name::UNSAFE_REMOVED_FROM_NAME), + LintId::of(unused_io_amount::UNUSED_IO_AMOUNT), + LintId::of(unused_unit::UNUSED_UNIT), + LintId::of(unwrap::PANICKING_UNWRAP), + LintId::of(unwrap::UNNECESSARY_UNWRAP), + LintId::of(upper_case_acronyms::UPPER_CASE_ACRONYMS), + LintId::of(useless_conversion::USELESS_CONVERSION), + LintId::of(vec::USELESS_VEC), + LintId::of(vec_init_then_push::VEC_INIT_THEN_PUSH), + LintId::of(vec_resize_to_zero::VEC_RESIZE_TO_ZERO), + LintId::of(write::PRINTLN_EMPTY_STRING), + LintId::of(write::PRINT_LITERAL), + LintId::of(write::PRINT_WITH_NEWLINE), + LintId::of(write::WRITELN_EMPTY_STRING), + LintId::of(write::WRITE_LITERAL), + LintId::of(write::WRITE_WITH_NEWLINE), + LintId::of(zero_div_zero::ZERO_DIVIDED_BY_ZERO), ]); store.register_group(true, "clippy::style", Some("clippy_style"), vec![ - LintId::of(&assertions_on_constants::ASSERTIONS_ON_CONSTANTS), - LintId::of(&assign_ops::ASSIGN_OP_PATTERN), - LintId::of(&attrs::BLANKET_CLIPPY_RESTRICTION_LINTS), - LintId::of(&blacklisted_name::BLACKLISTED_NAME), - LintId::of(&blocks_in_if_conditions::BLOCKS_IN_IF_CONDITIONS), - LintId::of(&collapsible_if::COLLAPSIBLE_ELSE_IF), - LintId::of(&collapsible_if::COLLAPSIBLE_IF), - LintId::of(&collapsible_match::COLLAPSIBLE_MATCH), - LintId::of(&comparison_chain::COMPARISON_CHAIN), - LintId::of(&default::FIELD_REASSIGN_WITH_DEFAULT), - LintId::of(&doc::MISSING_SAFETY_DOC), - LintId::of(&doc::NEEDLESS_DOCTEST_MAIN), - LintId::of(&enum_variants::ENUM_VARIANT_NAMES), - LintId::of(&enum_variants::MODULE_INCEPTION), - LintId::of(&eq_op::OP_REF), - LintId::of(&eta_reduction::REDUNDANT_CLOSURE), - LintId::of(&float_literal::EXCESSIVE_PRECISION), - LintId::of(&formatting::SUSPICIOUS_ASSIGNMENT_FORMATTING), - LintId::of(&formatting::SUSPICIOUS_ELSE_FORMATTING), - LintId::of(&formatting::SUSPICIOUS_UNARY_OP_FORMATTING), - LintId::of(&from_over_into::FROM_OVER_INTO), - LintId::of(&from_str_radix_10::FROM_STR_RADIX_10), - LintId::of(&functions::DOUBLE_MUST_USE), - LintId::of(&functions::MUST_USE_UNIT), - LintId::of(&functions::RESULT_UNIT_ERR), - LintId::of(&if_let_some_result::IF_LET_SOME_RESULT), - LintId::of(&inconsistent_struct_constructor::INCONSISTENT_STRUCT_CONSTRUCTOR), - LintId::of(&inherent_to_string::INHERENT_TO_STRING), - LintId::of(&len_zero::COMPARISON_TO_EMPTY), - LintId::of(&len_zero::LEN_WITHOUT_IS_EMPTY), - LintId::of(&len_zero::LEN_ZERO), - LintId::of(&literal_representation::INCONSISTENT_DIGIT_GROUPING), - LintId::of(&literal_representation::UNUSUAL_BYTE_GROUPINGS), - LintId::of(&loops::EMPTY_LOOP), - LintId::of(&loops::FOR_KV_MAP), - LintId::of(&loops::NEEDLESS_RANGE_LOOP), - LintId::of(&loops::SAME_ITEM_PUSH), - LintId::of(&loops::WHILE_LET_ON_ITERATOR), - LintId::of(&main_recursion::MAIN_RECURSION), - LintId::of(&manual_async_fn::MANUAL_ASYNC_FN), - LintId::of(&manual_map::MANUAL_MAP), - LintId::of(&manual_non_exhaustive::MANUAL_NON_EXHAUSTIVE), - LintId::of(&map_clone::MAP_CLONE), - LintId::of(&matches::INFALLIBLE_DESTRUCTURING_MATCH), - LintId::of(&matches::MATCH_LIKE_MATCHES_MACRO), - LintId::of(&matches::MATCH_OVERLAPPING_ARM), - LintId::of(&matches::MATCH_REF_PATS), - LintId::of(&matches::REDUNDANT_PATTERN_MATCHING), - LintId::of(&matches::SINGLE_MATCH), - LintId::of(&mem_replace::MEM_REPLACE_OPTION_WITH_NONE), - LintId::of(&mem_replace::MEM_REPLACE_WITH_DEFAULT), - LintId::of(&methods::BYTES_NTH), - LintId::of(&methods::CHARS_LAST_CMP), - LintId::of(&methods::CHARS_NEXT_CMP), - LintId::of(&methods::FROM_ITER_INSTEAD_OF_COLLECT), - LintId::of(&methods::INTO_ITER_ON_REF), - LintId::of(&methods::ITER_CLONED_COLLECT), - LintId::of(&methods::ITER_NEXT_SLICE), - LintId::of(&methods::ITER_NTH_ZERO), - LintId::of(&methods::ITER_SKIP_NEXT), - LintId::of(&methods::MANUAL_SATURATING_ARITHMETIC), - LintId::of(&methods::MAP_COLLECT_RESULT_UNIT), - LintId::of(&methods::NEW_RET_NO_SELF), - LintId::of(&methods::OK_EXPECT), - LintId::of(&methods::OPTION_MAP_OR_NONE), - LintId::of(&methods::RESULT_MAP_OR_INTO_OPTION), - LintId::of(&methods::SHOULD_IMPLEMENT_TRAIT), - LintId::of(&methods::SINGLE_CHAR_ADD_STR), - LintId::of(&methods::STRING_EXTEND_CHARS), - LintId::of(&methods::UNNECESSARY_FOLD), - LintId::of(&methods::UNNECESSARY_LAZY_EVALUATIONS), - LintId::of(&methods::WRONG_SELF_CONVENTION), - LintId::of(&misc::TOPLEVEL_REF_ARG), - LintId::of(&misc::ZERO_PTR), - LintId::of(&misc_early::BUILTIN_TYPE_SHADOW), - LintId::of(&misc_early::DOUBLE_NEG), - LintId::of(&misc_early::DUPLICATE_UNDERSCORE_ARGUMENT), - LintId::of(&misc_early::MIXED_CASE_HEX_LITERALS), - LintId::of(&misc_early::REDUNDANT_PATTERN), - LintId::of(&mut_mutex_lock::MUT_MUTEX_LOCK), - LintId::of(&mut_reference::UNNECESSARY_MUT_PASSED), - LintId::of(&neg_multiply::NEG_MULTIPLY), - LintId::of(&new_without_default::NEW_WITHOUT_DEFAULT), - LintId::of(&non_copy_const::BORROW_INTERIOR_MUTABLE_CONST), - LintId::of(&non_copy_const::DECLARE_INTERIOR_MUTABLE_CONST), - LintId::of(&non_expressive_names::JUST_UNDERSCORES_AND_DIGITS), - LintId::of(&non_expressive_names::MANY_SINGLE_CHAR_NAMES), - LintId::of(&ptr::CMP_NULL), - LintId::of(&ptr::PTR_ARG), - LintId::of(&ptr_eq::PTR_EQ), - LintId::of(&question_mark::QUESTION_MARK), - LintId::of(&ranges::MANUAL_RANGE_CONTAINS), - LintId::of(&redundant_field_names::REDUNDANT_FIELD_NAMES), - LintId::of(&redundant_static_lifetimes::REDUNDANT_STATIC_LIFETIMES), - LintId::of(&returns::LET_AND_RETURN), - LintId::of(&returns::NEEDLESS_RETURN), - LintId::of(&single_component_path_imports::SINGLE_COMPONENT_PATH_IMPORTS), - LintId::of(&suspicious_operation_groupings::SUSPICIOUS_OPERATION_GROUPINGS), - LintId::of(&tabs_in_doc_comments::TABS_IN_DOC_COMMENTS), - LintId::of(&to_digit_is_some::TO_DIGIT_IS_SOME), - LintId::of(&try_err::TRY_ERR), - LintId::of(&types::FN_TO_NUMERIC_CAST), - LintId::of(&types::FN_TO_NUMERIC_CAST_WITH_TRUNCATION), - LintId::of(&unsafe_removed_from_name::UNSAFE_REMOVED_FROM_NAME), - LintId::of(&unused_unit::UNUSED_UNIT), - LintId::of(&upper_case_acronyms::UPPER_CASE_ACRONYMS), - LintId::of(&write::PRINTLN_EMPTY_STRING), - LintId::of(&write::PRINT_LITERAL), - LintId::of(&write::PRINT_WITH_NEWLINE), - LintId::of(&write::WRITELN_EMPTY_STRING), - LintId::of(&write::WRITE_LITERAL), - LintId::of(&write::WRITE_WITH_NEWLINE), + LintId::of(assertions_on_constants::ASSERTIONS_ON_CONSTANTS), + LintId::of(assign_ops::ASSIGN_OP_PATTERN), + LintId::of(attrs::BLANKET_CLIPPY_RESTRICTION_LINTS), + LintId::of(blacklisted_name::BLACKLISTED_NAME), + LintId::of(blocks_in_if_conditions::BLOCKS_IN_IF_CONDITIONS), + LintId::of(casts::FN_TO_NUMERIC_CAST), + LintId::of(casts::FN_TO_NUMERIC_CAST_WITH_TRUNCATION), + LintId::of(collapsible_if::COLLAPSIBLE_ELSE_IF), + LintId::of(collapsible_if::COLLAPSIBLE_IF), + LintId::of(collapsible_match::COLLAPSIBLE_MATCH), + LintId::of(comparison_chain::COMPARISON_CHAIN), + LintId::of(default::FIELD_REASSIGN_WITH_DEFAULT), + LintId::of(doc::MISSING_SAFETY_DOC), + LintId::of(doc::NEEDLESS_DOCTEST_MAIN), + LintId::of(enum_variants::ENUM_VARIANT_NAMES), + LintId::of(enum_variants::MODULE_INCEPTION), + LintId::of(eq_op::OP_REF), + LintId::of(eta_reduction::REDUNDANT_CLOSURE), + LintId::of(float_literal::EXCESSIVE_PRECISION), + LintId::of(formatting::SUSPICIOUS_ASSIGNMENT_FORMATTING), + LintId::of(formatting::SUSPICIOUS_ELSE_FORMATTING), + LintId::of(formatting::SUSPICIOUS_UNARY_OP_FORMATTING), + LintId::of(from_over_into::FROM_OVER_INTO), + LintId::of(from_str_radix_10::FROM_STR_RADIX_10), + LintId::of(functions::DOUBLE_MUST_USE), + LintId::of(functions::MUST_USE_UNIT), + LintId::of(functions::RESULT_UNIT_ERR), + LintId::of(if_let_some_result::IF_LET_SOME_RESULT), + LintId::of(inconsistent_struct_constructor::INCONSISTENT_STRUCT_CONSTRUCTOR), + LintId::of(inherent_to_string::INHERENT_TO_STRING), + LintId::of(len_zero::COMPARISON_TO_EMPTY), + LintId::of(len_zero::LEN_WITHOUT_IS_EMPTY), + LintId::of(len_zero::LEN_ZERO), + LintId::of(literal_representation::INCONSISTENT_DIGIT_GROUPING), + LintId::of(literal_representation::UNUSUAL_BYTE_GROUPINGS), + LintId::of(loops::EMPTY_LOOP), + LintId::of(loops::FOR_KV_MAP), + LintId::of(loops::NEEDLESS_RANGE_LOOP), + LintId::of(loops::SAME_ITEM_PUSH), + LintId::of(loops::WHILE_LET_ON_ITERATOR), + LintId::of(main_recursion::MAIN_RECURSION), + LintId::of(manual_async_fn::MANUAL_ASYNC_FN), + LintId::of(manual_map::MANUAL_MAP), + LintId::of(manual_non_exhaustive::MANUAL_NON_EXHAUSTIVE), + LintId::of(map_clone::MAP_CLONE), + LintId::of(matches::INFALLIBLE_DESTRUCTURING_MATCH), + LintId::of(matches::MATCH_LIKE_MATCHES_MACRO), + LintId::of(matches::MATCH_OVERLAPPING_ARM), + LintId::of(matches::MATCH_REF_PATS), + LintId::of(matches::REDUNDANT_PATTERN_MATCHING), + LintId::of(matches::SINGLE_MATCH), + LintId::of(mem_replace::MEM_REPLACE_OPTION_WITH_NONE), + LintId::of(mem_replace::MEM_REPLACE_WITH_DEFAULT), + LintId::of(methods::BYTES_NTH), + LintId::of(methods::CHARS_LAST_CMP), + LintId::of(methods::CHARS_NEXT_CMP), + LintId::of(methods::FROM_ITER_INSTEAD_OF_COLLECT), + LintId::of(methods::INTO_ITER_ON_REF), + LintId::of(methods::ITER_CLONED_COLLECT), + LintId::of(methods::ITER_NEXT_SLICE), + LintId::of(methods::ITER_NTH_ZERO), + LintId::of(methods::ITER_SKIP_NEXT), + LintId::of(methods::MANUAL_SATURATING_ARITHMETIC), + LintId::of(methods::MAP_COLLECT_RESULT_UNIT), + LintId::of(methods::NEW_RET_NO_SELF), + LintId::of(methods::OK_EXPECT), + LintId::of(methods::OPTION_MAP_OR_NONE), + LintId::of(methods::RESULT_MAP_OR_INTO_OPTION), + LintId::of(methods::SHOULD_IMPLEMENT_TRAIT), + LintId::of(methods::SINGLE_CHAR_ADD_STR), + LintId::of(methods::STRING_EXTEND_CHARS), + LintId::of(methods::UNNECESSARY_FOLD), + LintId::of(methods::UNNECESSARY_LAZY_EVALUATIONS), + LintId::of(methods::WRONG_SELF_CONVENTION), + LintId::of(misc::TOPLEVEL_REF_ARG), + LintId::of(misc::ZERO_PTR), + LintId::of(misc_early::BUILTIN_TYPE_SHADOW), + LintId::of(misc_early::DOUBLE_NEG), + LintId::of(misc_early::DUPLICATE_UNDERSCORE_ARGUMENT), + LintId::of(misc_early::MIXED_CASE_HEX_LITERALS), + LintId::of(misc_early::REDUNDANT_PATTERN), + LintId::of(mut_mutex_lock::MUT_MUTEX_LOCK), + LintId::of(mut_reference::UNNECESSARY_MUT_PASSED), + LintId::of(neg_multiply::NEG_MULTIPLY), + LintId::of(new_without_default::NEW_WITHOUT_DEFAULT), + LintId::of(non_copy_const::BORROW_INTERIOR_MUTABLE_CONST), + LintId::of(non_copy_const::DECLARE_INTERIOR_MUTABLE_CONST), + LintId::of(non_expressive_names::JUST_UNDERSCORES_AND_DIGITS), + LintId::of(non_expressive_names::MANY_SINGLE_CHAR_NAMES), + LintId::of(ptr::CMP_NULL), + LintId::of(ptr::PTR_ARG), + LintId::of(ptr_eq::PTR_EQ), + LintId::of(question_mark::QUESTION_MARK), + LintId::of(ranges::MANUAL_RANGE_CONTAINS), + LintId::of(redundant_field_names::REDUNDANT_FIELD_NAMES), + LintId::of(redundant_static_lifetimes::REDUNDANT_STATIC_LIFETIMES), + LintId::of(returns::LET_AND_RETURN), + LintId::of(returns::NEEDLESS_RETURN), + LintId::of(single_component_path_imports::SINGLE_COMPONENT_PATH_IMPORTS), + LintId::of(suspicious_operation_groupings::SUSPICIOUS_OPERATION_GROUPINGS), + LintId::of(tabs_in_doc_comments::TABS_IN_DOC_COMMENTS), + LintId::of(to_digit_is_some::TO_DIGIT_IS_SOME), + LintId::of(try_err::TRY_ERR), + LintId::of(unsafe_removed_from_name::UNSAFE_REMOVED_FROM_NAME), + LintId::of(unused_unit::UNUSED_UNIT), + LintId::of(upper_case_acronyms::UPPER_CASE_ACRONYMS), + LintId::of(write::PRINTLN_EMPTY_STRING), + LintId::of(write::PRINT_LITERAL), + LintId::of(write::PRINT_WITH_NEWLINE), + LintId::of(write::WRITELN_EMPTY_STRING), + LintId::of(write::WRITE_LITERAL), + LintId::of(write::WRITE_WITH_NEWLINE), ]); store.register_group(true, "clippy::complexity", Some("clippy_complexity"), vec![ - LintId::of(&assign_ops::MISREFACTORED_ASSIGN_OP), - LintId::of(&attrs::DEPRECATED_CFG_ATTR), - LintId::of(&booleans::NONMINIMAL_BOOL), - LintId::of(&double_comparison::DOUBLE_COMPARISONS), - LintId::of(&double_parens::DOUBLE_PARENS), - LintId::of(&duration_subsec::DURATION_SUBSEC), - LintId::of(&eval_order_dependence::DIVERGING_SUB_EXPRESSION), - LintId::of(&eval_order_dependence::EVAL_ORDER_DEPENDENCE), - LintId::of(&explicit_write::EXPLICIT_WRITE), - LintId::of(&format::USELESS_FORMAT), - LintId::of(&functions::TOO_MANY_ARGUMENTS), - LintId::of(&get_last_with_len::GET_LAST_WITH_LEN), - LintId::of(&identity_op::IDENTITY_OP), - LintId::of(&int_plus_one::INT_PLUS_ONE), - LintId::of(&lifetimes::EXTRA_UNUSED_LIFETIMES), - LintId::of(&lifetimes::NEEDLESS_LIFETIMES), - LintId::of(&loops::EXPLICIT_COUNTER_LOOP), - LintId::of(&loops::MANUAL_FLATTEN), - LintId::of(&loops::MUT_RANGE_BOUND), - LintId::of(&loops::SINGLE_ELEMENT_LOOP), - LintId::of(&loops::WHILE_LET_LOOP), - LintId::of(&manual_strip::MANUAL_STRIP), - LintId::of(&manual_unwrap_or::MANUAL_UNWRAP_OR), - LintId::of(&map_identity::MAP_IDENTITY), - LintId::of(&map_unit_fn::OPTION_MAP_UNIT_FN), - LintId::of(&map_unit_fn::RESULT_MAP_UNIT_FN), - LintId::of(&matches::MATCH_AS_REF), - LintId::of(&matches::MATCH_SINGLE_BINDING), - LintId::of(&matches::WILDCARD_IN_OR_PATTERNS), - LintId::of(&methods::BIND_INSTEAD_OF_MAP), - LintId::of(&methods::CLONE_ON_COPY), - LintId::of(&methods::FILTER_MAP_IDENTITY), - LintId::of(&methods::FILTER_NEXT), - LintId::of(&methods::FLAT_MAP_IDENTITY), - LintId::of(&methods::INSPECT_FOR_EACH), - LintId::of(&methods::MANUAL_FILTER_MAP), - LintId::of(&methods::MANUAL_FIND_MAP), - LintId::of(&methods::OPTION_AS_REF_DEREF), - LintId::of(&methods::SEARCH_IS_SOME), - LintId::of(&methods::SKIP_WHILE_NEXT), - LintId::of(&methods::SUSPICIOUS_MAP), - LintId::of(&methods::UNNECESSARY_FILTER_MAP), - LintId::of(&methods::USELESS_ASREF), - LintId::of(&misc::SHORT_CIRCUIT_STATEMENT), - LintId::of(&misc_early::UNNEEDED_WILDCARD_PATTERN), - LintId::of(&misc_early::ZERO_PREFIXED_LITERAL), - LintId::of(&needless_arbitrary_self_type::NEEDLESS_ARBITRARY_SELF_TYPE), - LintId::of(&needless_bool::BOOL_COMPARISON), - LintId::of(&needless_bool::NEEDLESS_BOOL), - LintId::of(&needless_borrowed_ref::NEEDLESS_BORROWED_REFERENCE), - LintId::of(&needless_question_mark::NEEDLESS_QUESTION_MARK), - LintId::of(&needless_update::NEEDLESS_UPDATE), - LintId::of(&neg_cmp_op_on_partial_ord::NEG_CMP_OP_ON_PARTIAL_ORD), - LintId::of(&no_effect::NO_EFFECT), - LintId::of(&no_effect::UNNECESSARY_OPERATION), - LintId::of(&overflow_check_conditional::OVERFLOW_CHECK_CONDITIONAL), - LintId::of(&partialeq_ne_impl::PARTIALEQ_NE_IMPL), - LintId::of(&precedence::PRECEDENCE), - LintId::of(&ptr_offset_with_cast::PTR_OFFSET_WITH_CAST), - LintId::of(&ranges::RANGE_ZIP_WITH_LEN), - LintId::of(&redundant_closure_call::REDUNDANT_CLOSURE_CALL), - LintId::of(&redundant_slicing::REDUNDANT_SLICING), - LintId::of(&reference::DEREF_ADDROF), - LintId::of(&reference::REF_IN_DEREF), - LintId::of(&repeat_once::REPEAT_ONCE), - LintId::of(&strings::STRING_FROM_UTF8_AS_BYTES), - LintId::of(&swap::MANUAL_SWAP), - LintId::of(&temporary_assignment::TEMPORARY_ASSIGNMENT), - LintId::of(&transmute::CROSSPOINTER_TRANSMUTE), - LintId::of(&transmute::TRANSMUTES_EXPRESSIBLE_AS_PTR_CASTS), - LintId::of(&transmute::TRANSMUTE_BYTES_TO_STR), - LintId::of(&transmute::TRANSMUTE_FLOAT_TO_INT), - LintId::of(&transmute::TRANSMUTE_INT_TO_BOOL), - LintId::of(&transmute::TRANSMUTE_INT_TO_CHAR), - LintId::of(&transmute::TRANSMUTE_INT_TO_FLOAT), - LintId::of(&transmute::TRANSMUTE_PTR_TO_PTR), - LintId::of(&transmute::TRANSMUTE_PTR_TO_REF), - LintId::of(&types::BORROWED_BOX), - LintId::of(&types::CHAR_LIT_AS_U8), - LintId::of(&types::TYPE_COMPLEXITY), - LintId::of(&types::UNIT_ARG), - LintId::of(&types::UNNECESSARY_CAST), - LintId::of(&types::VEC_BOX), - LintId::of(&unnecessary_sort_by::UNNECESSARY_SORT_BY), - LintId::of(&unwrap::UNNECESSARY_UNWRAP), - LintId::of(&useless_conversion::USELESS_CONVERSION), - LintId::of(&zero_div_zero::ZERO_DIVIDED_BY_ZERO), + LintId::of(assign_ops::MISREFACTORED_ASSIGN_OP), + LintId::of(attrs::DEPRECATED_CFG_ATTR), + LintId::of(booleans::NONMINIMAL_BOOL), + LintId::of(casts::CHAR_LIT_AS_U8), + LintId::of(casts::UNNECESSARY_CAST), + LintId::of(copies::BRANCHES_SHARING_CODE), + LintId::of(double_comparison::DOUBLE_COMPARISONS), + LintId::of(double_parens::DOUBLE_PARENS), + LintId::of(duration_subsec::DURATION_SUBSEC), + LintId::of(eval_order_dependence::DIVERGING_SUB_EXPRESSION), + LintId::of(eval_order_dependence::EVAL_ORDER_DEPENDENCE), + LintId::of(explicit_write::EXPLICIT_WRITE), + LintId::of(format::USELESS_FORMAT), + LintId::of(functions::TOO_MANY_ARGUMENTS), + LintId::of(get_last_with_len::GET_LAST_WITH_LEN), + LintId::of(identity_op::IDENTITY_OP), + LintId::of(int_plus_one::INT_PLUS_ONE), + LintId::of(lifetimes::EXTRA_UNUSED_LIFETIMES), + LintId::of(lifetimes::NEEDLESS_LIFETIMES), + LintId::of(loops::EXPLICIT_COUNTER_LOOP), + LintId::of(loops::MANUAL_FLATTEN), + LintId::of(loops::MUT_RANGE_BOUND), + LintId::of(loops::SINGLE_ELEMENT_LOOP), + LintId::of(loops::WHILE_LET_LOOP), + LintId::of(manual_strip::MANUAL_STRIP), + LintId::of(manual_unwrap_or::MANUAL_UNWRAP_OR), + LintId::of(map_identity::MAP_IDENTITY), + LintId::of(map_unit_fn::OPTION_MAP_UNIT_FN), + LintId::of(map_unit_fn::RESULT_MAP_UNIT_FN), + LintId::of(matches::MATCH_AS_REF), + LintId::of(matches::MATCH_SINGLE_BINDING), + LintId::of(matches::WILDCARD_IN_OR_PATTERNS), + LintId::of(methods::BIND_INSTEAD_OF_MAP), + LintId::of(methods::CLONE_ON_COPY), + LintId::of(methods::FILTER_MAP_IDENTITY), + LintId::of(methods::FILTER_NEXT), + LintId::of(methods::FLAT_MAP_IDENTITY), + LintId::of(methods::INSPECT_FOR_EACH), + LintId::of(methods::ITER_COUNT), + LintId::of(methods::MANUAL_FILTER_MAP), + LintId::of(methods::MANUAL_FIND_MAP), + LintId::of(methods::OPTION_AS_REF_DEREF), + LintId::of(methods::OPTION_FILTER_MAP), + LintId::of(methods::SEARCH_IS_SOME), + LintId::of(methods::SKIP_WHILE_NEXT), + LintId::of(methods::SUSPICIOUS_MAP), + LintId::of(methods::UNNECESSARY_FILTER_MAP), + LintId::of(methods::USELESS_ASREF), + LintId::of(misc::SHORT_CIRCUIT_STATEMENT), + LintId::of(misc_early::UNNEEDED_WILDCARD_PATTERN), + LintId::of(misc_early::ZERO_PREFIXED_LITERAL), + LintId::of(needless_arbitrary_self_type::NEEDLESS_ARBITRARY_SELF_TYPE), + LintId::of(needless_bool::BOOL_COMPARISON), + LintId::of(needless_bool::NEEDLESS_BOOL), + LintId::of(needless_borrowed_ref::NEEDLESS_BORROWED_REFERENCE), + LintId::of(needless_question_mark::NEEDLESS_QUESTION_MARK), + LintId::of(needless_update::NEEDLESS_UPDATE), + LintId::of(neg_cmp_op_on_partial_ord::NEG_CMP_OP_ON_PARTIAL_ORD), + LintId::of(no_effect::NO_EFFECT), + LintId::of(no_effect::UNNECESSARY_OPERATION), + LintId::of(overflow_check_conditional::OVERFLOW_CHECK_CONDITIONAL), + LintId::of(partialeq_ne_impl::PARTIALEQ_NE_IMPL), + LintId::of(precedence::PRECEDENCE), + LintId::of(ptr_offset_with_cast::PTR_OFFSET_WITH_CAST), + LintId::of(ranges::RANGE_ZIP_WITH_LEN), + LintId::of(redundant_closure_call::REDUNDANT_CLOSURE_CALL), + LintId::of(redundant_slicing::REDUNDANT_SLICING), + LintId::of(reference::DEREF_ADDROF), + LintId::of(reference::REF_IN_DEREF), + LintId::of(repeat_once::REPEAT_ONCE), + LintId::of(strings::STRING_FROM_UTF8_AS_BYTES), + LintId::of(swap::MANUAL_SWAP), + LintId::of(temporary_assignment::TEMPORARY_ASSIGNMENT), + LintId::of(transmute::CROSSPOINTER_TRANSMUTE), + LintId::of(transmute::TRANSMUTES_EXPRESSIBLE_AS_PTR_CASTS), + LintId::of(transmute::TRANSMUTE_BYTES_TO_STR), + LintId::of(transmute::TRANSMUTE_FLOAT_TO_INT), + LintId::of(transmute::TRANSMUTE_INT_TO_BOOL), + LintId::of(transmute::TRANSMUTE_INT_TO_CHAR), + LintId::of(transmute::TRANSMUTE_INT_TO_FLOAT), + LintId::of(transmute::TRANSMUTE_PTR_TO_PTR), + LintId::of(transmute::TRANSMUTE_PTR_TO_REF), + LintId::of(types::BORROWED_BOX), + LintId::of(types::TYPE_COMPLEXITY), + LintId::of(types::VEC_BOX), + LintId::of(unit_types::UNIT_ARG), + LintId::of(unnecessary_sort_by::UNNECESSARY_SORT_BY), + LintId::of(unwrap::UNNECESSARY_UNWRAP), + LintId::of(useless_conversion::USELESS_CONVERSION), + LintId::of(zero_div_zero::ZERO_DIVIDED_BY_ZERO), ]); store.register_group(true, "clippy::correctness", Some("clippy_correctness"), vec![ - LintId::of(&approx_const::APPROX_CONSTANT), - LintId::of(&async_yields_async::ASYNC_YIELDS_ASYNC), - LintId::of(&atomic_ordering::INVALID_ATOMIC_ORDERING), - LintId::of(&attrs::DEPRECATED_SEMVER), - LintId::of(&attrs::MISMATCHED_TARGET_OS), - LintId::of(&attrs::USELESS_ATTRIBUTE), - LintId::of(&bit_mask::BAD_BIT_MASK), - LintId::of(&bit_mask::INEFFECTIVE_BIT_MASK), - LintId::of(&booleans::LOGIC_BUG), - LintId::of(&copies::IFS_SAME_COND), - LintId::of(&copies::IF_SAME_THEN_ELSE), - LintId::of(&derive::DERIVE_HASH_XOR_EQ), - LintId::of(&derive::DERIVE_ORD_XOR_PARTIAL_ORD), - LintId::of(&drop_forget_ref::DROP_COPY), - LintId::of(&drop_forget_ref::DROP_REF), - LintId::of(&drop_forget_ref::FORGET_COPY), - LintId::of(&drop_forget_ref::FORGET_REF), - LintId::of(&enum_clike::ENUM_CLIKE_UNPORTABLE_VARIANT), - LintId::of(&eq_op::EQ_OP), - LintId::of(&erasing_op::ERASING_OP), - LintId::of(&float_equality_without_abs::FLOAT_EQUALITY_WITHOUT_ABS), - LintId::of(&formatting::POSSIBLE_MISSING_COMMA), - LintId::of(&functions::NOT_UNSAFE_PTR_ARG_DEREF), - LintId::of(&if_let_mutex::IF_LET_MUTEX), - LintId::of(&indexing_slicing::OUT_OF_BOUNDS_INDEXING), - LintId::of(&infinite_iter::INFINITE_ITER), - LintId::of(&inherent_to_string::INHERENT_TO_STRING_SHADOW_DISPLAY), - LintId::of(&inline_fn_without_body::INLINE_FN_WITHOUT_BODY), - LintId::of(&let_underscore::LET_UNDERSCORE_LOCK), - LintId::of(&literal_representation::MISTYPED_LITERAL_SUFFIXES), - LintId::of(&loops::FOR_LOOPS_OVER_FALLIBLES), - LintId::of(&loops::ITER_NEXT_LOOP), - LintId::of(&loops::NEVER_LOOP), - LintId::of(&loops::WHILE_IMMUTABLE_CONDITION), - LintId::of(&mem_discriminant::MEM_DISCRIMINANT_NON_ENUM), - LintId::of(&mem_replace::MEM_REPLACE_WITH_UNINIT), - LintId::of(&methods::CLONE_DOUBLE_REF), - LintId::of(&methods::ITERATOR_STEP_BY_ZERO), - LintId::of(&methods::UNINIT_ASSUMED_INIT), - LintId::of(&methods::ZST_OFFSET), - LintId::of(&minmax::MIN_MAX), - LintId::of(&misc::CMP_NAN), - LintId::of(&misc::FLOAT_CMP), - LintId::of(&misc::MODULO_ONE), - LintId::of(&mut_key::MUTABLE_KEY_TYPE), - LintId::of(&open_options::NONSENSICAL_OPEN_OPTIONS), - LintId::of(&option_env_unwrap::OPTION_ENV_UNWRAP), - LintId::of(&ptr::MUT_FROM_REF), - LintId::of(&ranges::REVERSED_EMPTY_RANGES), - LintId::of(®ex::INVALID_REGEX), - LintId::of(&self_assignment::SELF_ASSIGNMENT), - LintId::of(&serde_api::SERDE_API_MISUSE), - LintId::of(&size_of_in_element_count::SIZE_OF_IN_ELEMENT_COUNT), - LintId::of(&suspicious_trait_impl::SUSPICIOUS_ARITHMETIC_IMPL), - LintId::of(&suspicious_trait_impl::SUSPICIOUS_OP_ASSIGN_IMPL), - LintId::of(&swap::ALMOST_SWAPPED), - LintId::of(&to_string_in_display::TO_STRING_IN_DISPLAY), - LintId::of(&transmute::UNSOUND_COLLECTION_TRANSMUTE), - LintId::of(&transmute::WRONG_TRANSMUTE), - LintId::of(&transmuting_null::TRANSMUTING_NULL), - LintId::of(&types::ABSURD_EXTREME_COMPARISONS), - LintId::of(&types::CAST_REF_TO_MUT), - LintId::of(&types::UNIT_CMP), - LintId::of(&undropped_manually_drops::UNDROPPED_MANUALLY_DROPS), - LintId::of(&unicode::INVISIBLE_CHARACTERS), - LintId::of(&unit_return_expecting_ord::UNIT_RETURN_EXPECTING_ORD), - LintId::of(&unnamed_address::FN_ADDRESS_COMPARISONS), - LintId::of(&unnamed_address::VTABLE_ADDRESS_COMPARISONS), - LintId::of(&unused_io_amount::UNUSED_IO_AMOUNT), - LintId::of(&unwrap::PANICKING_UNWRAP), - LintId::of(&vec_resize_to_zero::VEC_RESIZE_TO_ZERO), + LintId::of(absurd_extreme_comparisons::ABSURD_EXTREME_COMPARISONS), + LintId::of(approx_const::APPROX_CONSTANT), + LintId::of(async_yields_async::ASYNC_YIELDS_ASYNC), + LintId::of(atomic_ordering::INVALID_ATOMIC_ORDERING), + LintId::of(attrs::DEPRECATED_SEMVER), + LintId::of(attrs::MISMATCHED_TARGET_OS), + LintId::of(attrs::USELESS_ATTRIBUTE), + LintId::of(bit_mask::BAD_BIT_MASK), + LintId::of(bit_mask::INEFFECTIVE_BIT_MASK), + LintId::of(booleans::LOGIC_BUG), + LintId::of(casts::CAST_REF_TO_MUT), + LintId::of(copies::IFS_SAME_COND), + LintId::of(copies::IF_SAME_THEN_ELSE), + LintId::of(derive::DERIVE_HASH_XOR_EQ), + LintId::of(derive::DERIVE_ORD_XOR_PARTIAL_ORD), + LintId::of(drop_forget_ref::DROP_COPY), + LintId::of(drop_forget_ref::DROP_REF), + LintId::of(drop_forget_ref::FORGET_COPY), + LintId::of(drop_forget_ref::FORGET_REF), + LintId::of(enum_clike::ENUM_CLIKE_UNPORTABLE_VARIANT), + LintId::of(eq_op::EQ_OP), + LintId::of(erasing_op::ERASING_OP), + LintId::of(float_equality_without_abs::FLOAT_EQUALITY_WITHOUT_ABS), + LintId::of(formatting::POSSIBLE_MISSING_COMMA), + LintId::of(functions::NOT_UNSAFE_PTR_ARG_DEREF), + LintId::of(if_let_mutex::IF_LET_MUTEX), + LintId::of(indexing_slicing::OUT_OF_BOUNDS_INDEXING), + LintId::of(infinite_iter::INFINITE_ITER), + LintId::of(inherent_to_string::INHERENT_TO_STRING_SHADOW_DISPLAY), + LintId::of(inline_fn_without_body::INLINE_FN_WITHOUT_BODY), + LintId::of(let_underscore::LET_UNDERSCORE_LOCK), + LintId::of(literal_representation::MISTYPED_LITERAL_SUFFIXES), + LintId::of(loops::FOR_LOOPS_OVER_FALLIBLES), + LintId::of(loops::ITER_NEXT_LOOP), + LintId::of(loops::NEVER_LOOP), + LintId::of(loops::WHILE_IMMUTABLE_CONDITION), + LintId::of(mem_discriminant::MEM_DISCRIMINANT_NON_ENUM), + LintId::of(mem_replace::MEM_REPLACE_WITH_UNINIT), + LintId::of(methods::CLONE_DOUBLE_REF), + LintId::of(methods::ITERATOR_STEP_BY_ZERO), + LintId::of(methods::UNINIT_ASSUMED_INIT), + LintId::of(methods::ZST_OFFSET), + LintId::of(minmax::MIN_MAX), + LintId::of(misc::CMP_NAN), + LintId::of(misc::FLOAT_CMP), + LintId::of(misc::MODULO_ONE), + LintId::of(mut_key::MUTABLE_KEY_TYPE), + LintId::of(non_octal_unix_permissions::NON_OCTAL_UNIX_PERMISSIONS), + LintId::of(open_options::NONSENSICAL_OPEN_OPTIONS), + LintId::of(option_env_unwrap::OPTION_ENV_UNWRAP), + LintId::of(ptr::MUT_FROM_REF), + LintId::of(ranges::REVERSED_EMPTY_RANGES), + LintId::of(regex::INVALID_REGEX), + LintId::of(self_assignment::SELF_ASSIGNMENT), + LintId::of(serde_api::SERDE_API_MISUSE), + LintId::of(size_of_in_element_count::SIZE_OF_IN_ELEMENT_COUNT), + LintId::of(suspicious_trait_impl::SUSPICIOUS_ARITHMETIC_IMPL), + LintId::of(suspicious_trait_impl::SUSPICIOUS_OP_ASSIGN_IMPL), + LintId::of(swap::ALMOST_SWAPPED), + LintId::of(to_string_in_display::TO_STRING_IN_DISPLAY), + LintId::of(transmute::UNSOUND_COLLECTION_TRANSMUTE), + LintId::of(transmute::WRONG_TRANSMUTE), + LintId::of(transmuting_null::TRANSMUTING_NULL), + LintId::of(undropped_manually_drops::UNDROPPED_MANUALLY_DROPS), + LintId::of(unicode::INVISIBLE_CHARACTERS), + LintId::of(unit_return_expecting_ord::UNIT_RETURN_EXPECTING_ORD), + LintId::of(unit_types::UNIT_CMP), + LintId::of(unnamed_address::FN_ADDRESS_COMPARISONS), + LintId::of(unnamed_address::VTABLE_ADDRESS_COMPARISONS), + LintId::of(unused_io_amount::UNUSED_IO_AMOUNT), + LintId::of(unwrap::PANICKING_UNWRAP), + LintId::of(vec_resize_to_zero::VEC_RESIZE_TO_ZERO), ]); store.register_group(true, "clippy::perf", Some("clippy_perf"), vec![ - LintId::of(&bytecount::NAIVE_BYTECOUNT), - LintId::of(&entry::MAP_ENTRY), - LintId::of(&escape::BOXED_LOCAL), - LintId::of(&large_const_arrays::LARGE_CONST_ARRAYS), - LintId::of(&large_enum_variant::LARGE_ENUM_VARIANT), - LintId::of(&loops::MANUAL_MEMCPY), - LintId::of(&loops::NEEDLESS_COLLECT), - LintId::of(&methods::EXPECT_FUN_CALL), - LintId::of(&methods::ITER_NTH), - LintId::of(&methods::OR_FUN_CALL), - LintId::of(&methods::SINGLE_CHAR_PATTERN), - 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), - LintId::of(&types::BOX_VEC), - LintId::of(&types::REDUNDANT_ALLOCATION), - LintId::of(&vec::USELESS_VEC), - LintId::of(&vec_init_then_push::VEC_INIT_THEN_PUSH), + LintId::of(entry::MAP_ENTRY), + LintId::of(escape::BOXED_LOCAL), + LintId::of(large_const_arrays::LARGE_CONST_ARRAYS), + LintId::of(large_enum_variant::LARGE_ENUM_VARIANT), + LintId::of(loops::MANUAL_MEMCPY), + LintId::of(loops::NEEDLESS_COLLECT), + LintId::of(methods::EXPECT_FUN_CALL), + LintId::of(methods::ITER_NTH), + LintId::of(methods::OR_FUN_CALL), + LintId::of(methods::SINGLE_CHAR_PATTERN), + 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), + LintId::of(types::BOX_VEC), + LintId::of(types::REDUNDANT_ALLOCATION), + LintId::of(vec::USELESS_VEC), + LintId::of(vec_init_then_push::VEC_INIT_THEN_PUSH), ]); store.register_group(true, "clippy::cargo", Some("clippy_cargo"), vec![ - LintId::of(&cargo_common_metadata::CARGO_COMMON_METADATA), - LintId::of(&multiple_crate_versions::MULTIPLE_CRATE_VERSIONS), - LintId::of(&wildcard_dependencies::WILDCARD_DEPENDENCIES), + LintId::of(cargo_common_metadata::CARGO_COMMON_METADATA), + LintId::of(multiple_crate_versions::MULTIPLE_CRATE_VERSIONS), + LintId::of(wildcard_dependencies::WILDCARD_DEPENDENCIES), ]); 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(&disallowed_method::DISALLOWED_METHOD), - LintId::of(&fallible_impl_from::FALLIBLE_IMPL_FROM), - LintId::of(&floating_point_arithmetic::IMPRECISE_FLOPS), - LintId::of(&floating_point_arithmetic::SUBOPTIMAL_FLOPS), - LintId::of(&future_not_send::FUTURE_NOT_SEND), - 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_INTEGER), - LintId::of(&needless_borrow::NEEDLESS_BORROW), - LintId::of(&path_buf_push_overwrite::PATH_BUF_PUSH_OVERWRITE), - LintId::of(&redundant_pub_crate::REDUNDANT_PUB_CRATE), - LintId::of(®ex::TRIVIAL_REGEX), - LintId::of(&strings::STRING_LIT_AS_BYTES), - LintId::of(&transmute::USELESS_TRANSMUTE), - LintId::of(&use_self::USE_SELF), + LintId::of(attrs::EMPTY_LINE_AFTER_OUTER_ATTR), + LintId::of(cognitive_complexity::COGNITIVE_COMPLEXITY), + LintId::of(disallowed_method::DISALLOWED_METHOD), + LintId::of(fallible_impl_from::FALLIBLE_IMPL_FROM), + LintId::of(floating_point_arithmetic::IMPRECISE_FLOPS), + LintId::of(floating_point_arithmetic::SUBOPTIMAL_FLOPS), + LintId::of(future_not_send::FUTURE_NOT_SEND), + 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_INTEGER), + LintId::of(needless_borrow::NEEDLESS_BORROW), + LintId::of(path_buf_push_overwrite::PATH_BUF_PUSH_OVERWRITE), + LintId::of(redundant_pub_crate::REDUNDANT_PUB_CRATE), + LintId::of(regex::TRIVIAL_REGEX), + LintId::of(strings::STRING_LIT_AS_BYTES), + LintId::of(transmute::USELESS_TRANSMUTE), + LintId::of(use_self::USE_SELF), ]); } diff --git a/src/tools/clippy/clippy_lints/src/lifetimes.rs b/src/tools/clippy/clippy_lints/src/lifetimes.rs index 50e6383263dd3..116ad07283792 100644 --- a/src/tools/clippy/clippy_lints/src/lifetimes.rs +++ b/src/tools/clippy/clippy_lints/src/lifetimes.rs @@ -1,5 +1,5 @@ -use crate::utils::paths; -use crate::utils::{get_trait_def_id, in_macro, span_lint, trait_ref_of_method}; +use clippy_utils::diagnostics::span_lint; +use clippy_utils::{in_macro, trait_ref_of_method}; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_hir::intravisit::{ walk_fn_decl, walk_generic_param, walk_generics, walk_item, walk_param_bound, walk_poly_trait_ref, walk_ty, @@ -8,8 +8,8 @@ use rustc_hir::intravisit::{ use rustc_hir::FnRetTy::Return; use rustc_hir::{ BareFnTy, BodyId, FnDecl, GenericArg, GenericBound, GenericParam, GenericParamKind, Generics, ImplItem, - ImplItemKind, Item, ItemKind, Lifetime, LifetimeName, ParamName, PolyTraitRef, TraitBoundModifier, TraitFn, - TraitItem, TraitItemKind, Ty, TyKind, WhereClause, WherePredicate, + ImplItemKind, Item, ItemKind, LangItem, Lifetime, LifetimeName, ParamName, PolyTraitRef, TraitBoundModifier, + TraitFn, TraitItem, TraitItemKind, Ty, TyKind, WhereClause, WherePredicate, }; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::hir::map::Map; @@ -81,7 +81,7 @@ declare_lint_pass!(Lifetimes => [NEEDLESS_LIFETIMES, EXTRA_UNUSED_LIFETIMES]); impl<'tcx> LateLintPass<'tcx> for Lifetimes { fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) { if let ItemKind::Fn(ref sig, ref generics, id) = item.kind { - check_fn_inner(cx, &sig.decl, Some(id), generics, item.span, true); + check_fn_inner(cx, sig.decl, Some(id), generics, item.span, true); } } @@ -90,7 +90,7 @@ impl<'tcx> LateLintPass<'tcx> for Lifetimes { let report_extra_lifetimes = trait_ref_of_method(cx, item.hir_id()).is_none(); check_fn_inner( cx, - &sig.decl, + sig.decl, Some(id), &item.generics, item.span, @@ -105,7 +105,7 @@ impl<'tcx> LateLintPass<'tcx> for Lifetimes { TraitFn::Required(_) => None, TraitFn::Provided(id) => Some(id), }; - check_fn_inner(cx, &sig.decl, body, &item.generics, item.span, true); + check_fn_inner(cx, sig.decl, body, &item.generics, item.span, true); } } } @@ -149,7 +149,7 @@ fn check_fn_inner<'tcx>( .last() .expect("a path must have at least one segment") .args; - if let Some(ref params) = *params { + if let Some(params) = *params { let lifetimes = params.args.iter().filter_map(|arg| match arg { GenericArg::Lifetime(lt) => Some(lt), _ => None, @@ -163,7 +163,7 @@ fn check_fn_inner<'tcx>( } } } - if could_use_elision(cx, decl, body, &generics.params) { + if could_use_elision(cx, decl, body, generics.params) { span_lint( cx, NEEDLESS_LIFETIMES, @@ -201,7 +201,7 @@ fn could_use_elision<'tcx>( input_visitor.visit_ty(arg); } // extract lifetimes in output type - if let Return(ref ty) = func.output { + if let Return(ty) = func.output { output_visitor.visit_ty(ty); } for lt in named_generics { @@ -300,7 +300,7 @@ fn unique_lifetimes(lts: &[RefLt]) -> usize { lts.iter().collect::<FxHashSet<_>>().len() } -const CLOSURE_TRAIT_BOUNDS: [&[&str]; 3] = [&paths::FN, &paths::FN_MUT, &paths::FN_ONCE]; +const CLOSURE_TRAIT_BOUNDS: [LangItem; 3] = [LangItem::Fn, LangItem::FnMut, LangItem::FnOnce]; /// A visitor usable for `rustc_front::visit::walk_ty()`. struct RefVisitor<'a, 'tcx> { @@ -359,10 +359,13 @@ impl<'a, 'tcx> Visitor<'tcx> for RefVisitor<'a, 'tcx> { fn visit_poly_trait_ref(&mut self, poly_tref: &'tcx PolyTraitRef<'tcx>, tbm: TraitBoundModifier) { let trait_ref = &poly_tref.trait_ref; - if CLOSURE_TRAIT_BOUNDS - .iter() - .any(|trait_path| trait_ref.trait_def_id() == get_trait_def_id(self.cx, trait_path)) - { + if CLOSURE_TRAIT_BOUNDS.iter().any(|&item| { + self.cx + .tcx + .lang_items() + .require(item) + .map_or(false, |id| Some(id) == trait_ref.trait_def_id()) + }) { let mut sub_visitor = RefVisitor::new(self.cx); sub_visitor.visit_trait_ref(trait_ref); self.nested_elision_site_lts.append(&mut sub_visitor.all_lts()); @@ -385,7 +388,7 @@ impl<'a, 'tcx> Visitor<'tcx> for RefVisitor<'a, 'tcx> { self.nested_elision_site_lts.append(&mut sub_visitor.all_lts()); return; }, - TyKind::TraitObject(bounds, ref lt) => { + TyKind::TraitObject(bounds, ref lt, _) => { if !lt.is_elided() { self.unelided_trait_object_lifetime = true; } @@ -413,12 +416,12 @@ fn has_where_lifetimes<'tcx>(cx: &LateContext<'tcx>, where_clause: &'tcx WhereCl // a predicate like F: Trait or F: for<'a> Trait<'a> let mut visitor = RefVisitor::new(cx); // walk the type F, it may not contain LT refs - walk_ty(&mut visitor, &pred.bounded_ty); + walk_ty(&mut visitor, pred.bounded_ty); if !visitor.all_lts().is_empty() { return true; } // if the bounds define new lifetimes, they are fine to occur - let allowed_lts = allowed_lts_from(&pred.bound_generic_params); + let allowed_lts = allowed_lts_from(pred.bound_generic_params); // now walk the bounds for bound in pred.bounds.iter() { walk_param_bound(&mut visitor, bound); @@ -430,8 +433,8 @@ fn has_where_lifetimes<'tcx>(cx: &LateContext<'tcx>, where_clause: &'tcx WhereCl }, WherePredicate::EqPredicate(ref pred) => { let mut visitor = RefVisitor::new(cx); - walk_ty(&mut visitor, &pred.lhs_ty); - walk_ty(&mut visitor, &pred.rhs_ty); + walk_ty(&mut visitor, pred.lhs_ty); + walk_ty(&mut visitor, pred.rhs_ty); if !visitor.lts.is_empty() { return true; } diff --git a/src/tools/clippy/clippy_lints/src/literal_representation.rs b/src/tools/clippy/clippy_lints/src/literal_representation.rs index 87a957a9bd241..e93b2e36b860a 100644 --- a/src/tools/clippy/clippy_lints/src/literal_representation.rs +++ b/src/tools/clippy/clippy_lints/src/literal_representation.rs @@ -1,10 +1,11 @@ //! Lints concerned with the grouping of digits with underscores in integral or //! floating-point literal expressions. -use crate::utils::{ +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::source::snippet_opt; +use clippy_utils::{ in_macro, numeric_literal::{NumericLiteral, Radix}, - snippet_opt, span_lint_and_sugg, }; use if_chain::if_chain; use rustc_ast::ast::{Expr, ExprKind, Lit, LitKind}; @@ -12,6 +13,7 @@ use rustc_errors::Applicability; use rustc_lint::{EarlyContext, EarlyLintPass, LintContext}; use rustc_middle::lint::in_external_macro; use rustc_session::{declare_tool_lint, impl_lint_pass}; +use std::iter; declare_clippy_lint! { /// **What it does:** Warns if a long integral or floating-point constant does @@ -247,7 +249,7 @@ impl LiteralDigitGrouping { fn check_lit(self, cx: &EarlyContext<'_>, lit: &Lit) { if_chain! { if let Some(src) = snippet_opt(cx, lit.span); - if let Some(mut num_lit) = NumericLiteral::from_lit(&src, &lit); + if let Some(mut num_lit) = NumericLiteral::from_lit(&src, lit); then { if !Self::check_for_mistyped_suffix(cx, lit.span, &mut num_lit) { return; @@ -348,7 +350,7 @@ impl LiteralDigitGrouping { let group_sizes: Vec<usize> = num_lit.integer.split('_').map(str::len).collect(); if UUID_GROUP_LENS.len() == group_sizes.len() { - UUID_GROUP_LENS.iter().zip(&group_sizes).all(|(&a, &b)| a == b) + iter::zip(&UUID_GROUP_LENS, &group_sizes).all(|(&a, &b)| a == b) } else { false } @@ -437,7 +439,7 @@ impl DecimalLiteralRepresentation { if_chain! { if let LitKind::Int(val, _) = lit.kind; if let Some(src) = snippet_opt(cx, lit.span); - if let Some(num_lit) = NumericLiteral::from_lit(&src, &lit); + if let Some(num_lit) = NumericLiteral::from_lit(&src, lit); if num_lit.radix == Radix::Decimal; if val >= u128::from(self.threshold); then { diff --git a/src/tools/clippy/clippy_lints/src/loops.rs b/src/tools/clippy/clippy_lints/src/loops.rs deleted file mode 100644 index 63d9b7f864597..0000000000000 --- a/src/tools/clippy/clippy_lints/src/loops.rs +++ /dev/null @@ -1,3180 +0,0 @@ -use crate::consts::constant; -use crate::utils::sugg::Sugg; -use crate::utils::usage::mutated_variables; -use crate::utils::visitors::LocalUsedVisitor; -use crate::utils::{ - contains_name, get_enclosing_block, get_parent_expr, get_trait_def_id, has_iter_method, higher, implements_trait, - indent_of, is_in_panic_handler, is_integer_const, is_no_std_crate, is_ok_ctor, is_refutable, is_some_ctor, - is_type_diagnostic_item, last_path_segment, match_trait_method, match_type, multispan_sugg, path_to_local, - path_to_local_id, paths, single_segment_path, snippet, snippet_with_applicability, snippet_with_macro_callsite, - span_lint, span_lint_and_help, span_lint_and_sugg, span_lint_and_then, sugg, SpanlessEq, -}; -use if_chain::if_chain; -use rustc_ast::ast; -use rustc_data_structures::fx::{FxHashMap, FxHashSet}; -use rustc_errors::Applicability; -use rustc_hir::def::{DefKind, Res}; -use rustc_hir::intravisit::{walk_block, walk_expr, walk_pat, walk_stmt, NestedVisitorMap, Visitor}; -use rustc_hir::{ - def_id, BinOpKind, BindingAnnotation, Block, BorrowKind, Expr, ExprKind, GenericArg, HirId, InlineAsmOperand, - Local, LoopSource, MatchSource, Mutability, Node, Pat, PatKind, QPath, Stmt, StmtKind, -}; -use rustc_infer::infer::TyCtxtInferExt; -use rustc_lint::{LateContext, LateLintPass, LintContext}; -use rustc_middle::hir::map::Map; -use rustc_middle::lint::in_external_macro; -use rustc_middle::middle::region; -use rustc_middle::ty::{self, Ty, TyS}; -use rustc_session::{declare_lint_pass, declare_tool_lint}; -use rustc_span::source_map::Span; -use rustc_span::symbol::{sym, Ident, Symbol}; -use rustc_typeck::expr_use_visitor::{ConsumeMode, Delegate, ExprUseVisitor, PlaceBase, PlaceWithHirId}; -use std::iter::{once, Iterator}; -use std::mem; - -declare_clippy_lint! { - /// **What it does:** Checks for for-loops that manually copy items between - /// slices that could be optimized by having a memcpy. - /// - /// **Why is this bad?** It is not as fast as a memcpy. - /// - /// **Known problems:** None. - /// - /// **Example:** - /// ```rust - /// # let src = vec![1]; - /// # let mut dst = vec![0; 65]; - /// for i in 0..src.len() { - /// dst[i + 64] = src[i]; - /// } - /// ``` - /// Could be written as: - /// ```rust - /// # let src = vec![1]; - /// # let mut dst = vec![0; 65]; - /// dst[64..(src.len() + 64)].clone_from_slice(&src[..]); - /// ``` - pub MANUAL_MEMCPY, - perf, - "manually copying items between slices" -} - -declare_clippy_lint! { - /// **What it does:** Checks for looping over the range of `0..len` of some - /// collection just to get the values by index. - /// - /// **Why is this bad?** Just iterating the collection itself makes the intent - /// more clear and is probably faster. - /// - /// **Known problems:** None. - /// - /// **Example:** - /// ```rust - /// let vec = vec!['a', 'b', 'c']; - /// for i in 0..vec.len() { - /// println!("{}", vec[i]); - /// } - /// ``` - /// Could be written as: - /// ```rust - /// let vec = vec!['a', 'b', 'c']; - /// for i in vec { - /// println!("{}", i); - /// } - /// ``` - pub NEEDLESS_RANGE_LOOP, - style, - "for-looping over a range of indices where an iterator over items would do" -} - -declare_clippy_lint! { - /// **What it does:** Checks for loops on `x.iter()` where `&x` will do, and - /// suggests the latter. - /// - /// **Why is this bad?** Readability. - /// - /// **Known problems:** False negatives. We currently only warn on some known - /// types. - /// - /// **Example:** - /// ```rust - /// // with `y` a `Vec` or slice: - /// # let y = vec![1]; - /// for x in y.iter() { - /// // .. - /// } - /// ``` - /// can be rewritten to - /// ```rust - /// # let y = vec![1]; - /// for x in &y { - /// // .. - /// } - /// ``` - pub EXPLICIT_ITER_LOOP, - pedantic, - "for-looping over `_.iter()` or `_.iter_mut()` when `&_` or `&mut _` would do" -} - -declare_clippy_lint! { - /// **What it does:** Checks for loops on `y.into_iter()` where `y` will do, and - /// suggests the latter. - /// - /// **Why is this bad?** Readability. - /// - /// **Known problems:** None - /// - /// **Example:** - /// ```rust - /// # let y = vec![1]; - /// // with `y` a `Vec` or slice: - /// for x in y.into_iter() { - /// // .. - /// } - /// ``` - /// can be rewritten to - /// ```rust - /// # let y = vec![1]; - /// for x in y { - /// // .. - /// } - /// ``` - pub EXPLICIT_INTO_ITER_LOOP, - pedantic, - "for-looping over `_.into_iter()` when `_` would do" -} - -declare_clippy_lint! { - /// **What it does:** Checks for loops on `x.next()`. - /// - /// **Why is this bad?** `next()` returns either `Some(value)` if there was a - /// value, or `None` otherwise. The insidious thing is that `Option<_>` - /// implements `IntoIterator`, so that possibly one value will be iterated, - /// leading to some hard to find bugs. No one will want to write such code - /// [except to win an Underhanded Rust - /// Contest](https://www.reddit.com/r/rust/comments/3hb0wm/underhanded_rust_contest/cu5yuhr). - /// - /// **Known problems:** None. - /// - /// **Example:** - /// ```ignore - /// for x in y.next() { - /// .. - /// } - /// ``` - pub ITER_NEXT_LOOP, - correctness, - "for-looping over `_.next()` which is probably not intended" -} - -declare_clippy_lint! { - /// **What it does:** Checks for `for` loops over `Option` or `Result` values. - /// - /// **Why is this bad?** Readability. This is more clearly expressed as an `if - /// let`. - /// - /// **Known problems:** None. - /// - /// **Example:** - /// ```rust - /// # let opt = Some(1); - /// - /// // Bad - /// for x in opt { - /// // .. - /// } - /// - /// // Good - /// if let Some(x) = opt { - /// // .. - /// } - /// ``` - /// - /// // or - /// - /// ```rust - /// # let res: Result<i32, std::io::Error> = Ok(1); - /// - /// // Bad - /// for x in &res { - /// // .. - /// } - /// - /// // Good - /// if let Ok(x) = res { - /// // .. - /// } - /// ``` - pub FOR_LOOPS_OVER_FALLIBLES, - correctness, - "for-looping over an `Option` or a `Result`, which is more clearly expressed as an `if let`" -} - -declare_clippy_lint! { - /// **What it does:** Detects `loop + match` combinations that are easier - /// written as a `while let` loop. - /// - /// **Why is this bad?** The `while let` loop is usually shorter and more - /// readable. - /// - /// **Known problems:** Sometimes the wrong binding is displayed ([#383](https://github.com/rust-lang/rust-clippy/issues/383)). - /// - /// **Example:** - /// ```rust,no_run - /// # let y = Some(1); - /// loop { - /// let x = match y { - /// Some(x) => x, - /// None => break, - /// }; - /// // .. do something with x - /// } - /// // is easier written as - /// while let Some(x) = y { - /// // .. do something with x - /// }; - /// ``` - pub WHILE_LET_LOOP, - complexity, - "`loop { if let { ... } else break }`, which can be written as a `while let` loop" -} - -declare_clippy_lint! { - /// **What it does:** Checks for functions collecting an iterator when collect - /// is not needed. - /// - /// **Why is this bad?** `collect` causes the allocation of a new data structure, - /// when this allocation may not be needed. - /// - /// **Known problems:** - /// None - /// - /// **Example:** - /// ```rust - /// # let iterator = vec![1].into_iter(); - /// let len = iterator.clone().collect::<Vec<_>>().len(); - /// // should be - /// let len = iterator.count(); - /// ``` - pub NEEDLESS_COLLECT, - perf, - "collecting an iterator when collect is not needed" -} - -declare_clippy_lint! { - /// **What it does:** Checks `for` loops over slices with an explicit counter - /// and suggests the use of `.enumerate()`. - /// - /// **Why is it bad?** Using `.enumerate()` makes the intent more clear, - /// declutters the code and may be faster in some instances. - /// - /// **Known problems:** None. - /// - /// **Example:** - /// ```rust - /// # let v = vec![1]; - /// # fn bar(bar: usize, baz: usize) {} - /// let mut i = 0; - /// for item in &v { - /// bar(i, *item); - /// i += 1; - /// } - /// ``` - /// Could be written as - /// ```rust - /// # let v = vec![1]; - /// # fn bar(bar: usize, baz: usize) {} - /// for (i, item) in v.iter().enumerate() { bar(i, *item); } - /// ``` - pub EXPLICIT_COUNTER_LOOP, - complexity, - "for-looping with an explicit counter when `_.enumerate()` would do" -} - -declare_clippy_lint! { - /// **What it does:** Checks for empty `loop` expressions. - /// - /// **Why is this bad?** These busy loops burn CPU cycles without doing - /// anything. It is _almost always_ a better idea to `panic!` than to have - /// a busy loop. - /// - /// If panicking isn't possible, think of the environment and either: - /// - block on something - /// - sleep the thread for some microseconds - /// - yield or pause the thread - /// - /// For `std` targets, this can be done with - /// [`std::thread::sleep`](https://doc.rust-lang.org/std/thread/fn.sleep.html) - /// or [`std::thread::yield_now`](https://doc.rust-lang.org/std/thread/fn.yield_now.html). - /// - /// For `no_std` targets, doing this is more complicated, especially because - /// `#[panic_handler]`s can't panic. To stop/pause the thread, you will - /// probably need to invoke some target-specific intrinsic. Examples include: - /// - [`x86_64::instructions::hlt`](https://docs.rs/x86_64/0.12.2/x86_64/instructions/fn.hlt.html) - /// - [`cortex_m::asm::wfi`](https://docs.rs/cortex-m/0.6.3/cortex_m/asm/fn.wfi.html) - /// - /// **Known problems:** None. - /// - /// **Example:** - /// ```no_run - /// loop {} - /// ``` - pub EMPTY_LOOP, - style, - "empty `loop {}`, which should block or sleep" -} - -declare_clippy_lint! { - /// **What it does:** Checks for `while let` expressions on iterators. - /// - /// **Why is this bad?** Readability. A simple `for` loop is shorter and conveys - /// the intent better. - /// - /// **Known problems:** None. - /// - /// **Example:** - /// ```ignore - /// while let Some(val) = iter() { - /// .. - /// } - /// ``` - pub WHILE_LET_ON_ITERATOR, - style, - "using a `while let` loop instead of a for loop on an iterator" -} - -declare_clippy_lint! { - /// **What it does:** Checks for iterating a map (`HashMap` or `BTreeMap`) and - /// ignoring either the keys or values. - /// - /// **Why is this bad?** Readability. There are `keys` and `values` methods that - /// can be used to express that don't need the values or keys. - /// - /// **Known problems:** None. - /// - /// **Example:** - /// ```ignore - /// for (k, _) in &map { - /// .. - /// } - /// ``` - /// - /// could be replaced by - /// - /// ```ignore - /// for k in map.keys() { - /// .. - /// } - /// ``` - pub FOR_KV_MAP, - style, - "looping on a map using `iter` when `keys` or `values` would do" -} - -declare_clippy_lint! { - /// **What it does:** Checks for loops that will always `break`, `return` or - /// `continue` an outer loop. - /// - /// **Why is this bad?** This loop never loops, all it does is obfuscating the - /// code. - /// - /// **Known problems:** None - /// - /// **Example:** - /// ```rust - /// loop { - /// ..; - /// break; - /// } - /// ``` - pub NEVER_LOOP, - correctness, - "any loop that will always `break` or `return`" -} - -declare_clippy_lint! { - /// **What it does:** Checks for loops which have a range bound that is a mutable variable - /// - /// **Why is this bad?** One might think that modifying the mutable variable changes the loop bounds - /// - /// **Known problems:** None - /// - /// **Example:** - /// ```rust - /// let mut foo = 42; - /// for i in 0..foo { - /// foo -= 1; - /// println!("{}", i); // prints numbers from 0 to 42, not 0 to 21 - /// } - /// ``` - pub MUT_RANGE_BOUND, - complexity, - "for loop over a range where one of the bounds is a mutable variable" -} - -declare_clippy_lint! { - /// **What it does:** Checks whether variables used within while loop condition - /// can be (and are) mutated in the body. - /// - /// **Why is this bad?** If the condition is unchanged, entering the body of the loop - /// will lead to an infinite loop. - /// - /// **Known problems:** If the `while`-loop is in a closure, the check for mutation of the - /// condition variables in the body can cause false negatives. For example when only `Upvar` `a` is - /// in the condition and only `Upvar` `b` gets mutated in the body, the lint will not trigger. - /// - /// **Example:** - /// ```rust - /// let i = 0; - /// while i > 10 { - /// println!("let me loop forever!"); - /// } - /// ``` - pub WHILE_IMMUTABLE_CONDITION, - correctness, - "variables used within while expression are not mutated in the body" -} - -declare_clippy_lint! { - /// **What it does:** Checks whether a for loop is being used to push a constant - /// value into a Vec. - /// - /// **Why is this bad?** This kind of operation can be expressed more succinctly with - /// `vec![item;SIZE]` or `vec.resize(NEW_SIZE, item)` and using these alternatives may also - /// have better performance. - /// **Known problems:** None - /// - /// **Example:** - /// ```rust - /// let item1 = 2; - /// let item2 = 3; - /// let mut vec: Vec<u8> = Vec::new(); - /// for _ in 0..20 { - /// vec.push(item1); - /// } - /// for _ in 0..30 { - /// vec.push(item2); - /// } - /// ``` - /// could be written as - /// ```rust - /// let item1 = 2; - /// let item2 = 3; - /// let mut vec: Vec<u8> = vec![item1; 20]; - /// vec.resize(20 + 30, item2); - /// ``` - pub SAME_ITEM_PUSH, - style, - "the same item is pushed inside of a for loop" -} - -declare_clippy_lint! { - /// **What it does:** Checks whether a for loop has a single element. - /// - /// **Why is this bad?** There is no reason to have a loop of a - /// single element. - /// **Known problems:** None - /// - /// **Example:** - /// ```rust - /// let item1 = 2; - /// for item in &[item1] { - /// println!("{}", item); - /// } - /// ``` - /// could be written as - /// ```rust - /// let item1 = 2; - /// let item = &item1; - /// println!("{}", item); - /// ``` - pub SINGLE_ELEMENT_LOOP, - complexity, - "there is no reason to have a single element loop" -} - -declare_clippy_lint! { - /// **What it does:** Check for unnecessary `if let` usage in a for loop - /// where only the `Some` or `Ok` variant of the iterator element is used. - /// - /// **Why is this bad?** It is verbose and can be simplified - /// by first calling the `flatten` method on the `Iterator`. - /// - /// **Known problems:** None. - /// - /// **Example:** - /// - /// ```rust - /// let x = vec![Some(1), Some(2), Some(3)]; - /// for n in x { - /// if let Some(n) = n { - /// println!("{}", n); - /// } - /// } - /// ``` - /// Use instead: - /// ```rust - /// let x = vec![Some(1), Some(2), Some(3)]; - /// for n in x.into_iter().flatten() { - /// println!("{}", n); - /// } - /// ``` - pub MANUAL_FLATTEN, - complexity, - "for loops over `Option`s or `Result`s with a single expression can be simplified" -} - -declare_lint_pass!(Loops => [ - MANUAL_MEMCPY, - MANUAL_FLATTEN, - NEEDLESS_RANGE_LOOP, - EXPLICIT_ITER_LOOP, - EXPLICIT_INTO_ITER_LOOP, - ITER_NEXT_LOOP, - FOR_LOOPS_OVER_FALLIBLES, - WHILE_LET_LOOP, - NEEDLESS_COLLECT, - EXPLICIT_COUNTER_LOOP, - EMPTY_LOOP, - WHILE_LET_ON_ITERATOR, - FOR_KV_MAP, - NEVER_LOOP, - MUT_RANGE_BOUND, - WHILE_IMMUTABLE_CONDITION, - SAME_ITEM_PUSH, - SINGLE_ELEMENT_LOOP, -]); - -impl<'tcx> LateLintPass<'tcx> for Loops { - #[allow(clippy::too_many_lines)] - fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - if let Some((pat, arg, body, span)) = higher::for_loop(expr) { - // we don't want to check expanded macros - // this check is not at the top of the function - // since higher::for_loop expressions are marked as expansions - if body.span.from_expansion() { - return; - } - check_for_loop(cx, pat, arg, body, expr, span); - } - - // we don't want to check expanded macros - if expr.span.from_expansion() { - return; - } - - // check for never_loop - if let ExprKind::Loop(ref block, _, _, _) = expr.kind { - match never_loop_block(block, expr.hir_id) { - NeverLoopResult::AlwaysBreak => span_lint(cx, NEVER_LOOP, expr.span, "this loop never actually loops"), - NeverLoopResult::MayContinueMainLoop | NeverLoopResult::Otherwise => (), - } - } - - // check for `loop { if let {} else break }` that could be `while let` - // (also matches an explicit "match" instead of "if let") - // (even if the "match" or "if let" is used for declaration) - if let ExprKind::Loop(ref block, _, LoopSource::Loop, _) = expr.kind { - // also check for empty `loop {}` statements, skipping those in #[panic_handler] - if block.stmts.is_empty() && 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.tcx.hir().krate()) { - "you should either use `panic!()` or add a call pausing or sleeping the thread to the loop body" - } else { - "you should either use `panic!()` or add `std::thread::sleep(..);` to the loop body" - }; - span_lint_and_help(cx, EMPTY_LOOP, expr.span, msg, None, help); - } - - // extract the expression from the first statement (if any) in a block - let inner_stmt_expr = extract_expr_from_first_stmt(block); - // or extract the first expression (if any) from the block - if let Some(inner) = inner_stmt_expr.or_else(|| extract_first_expr(block)) { - if let ExprKind::Match(ref matchexpr, ref arms, ref source) = inner.kind { - // ensure "if let" compatible match structure - match *source { - MatchSource::Normal | MatchSource::IfLetDesugar { .. } => { - if arms.len() == 2 - && arms[0].guard.is_none() - && arms[1].guard.is_none() - && is_simple_break_expr(&arms[1].body) - { - if in_external_macro(cx.sess(), expr.span) { - return; - } - - // NOTE: we used to build a body here instead of using - // ellipsis, this was removed because: - // 1) it was ugly with big bodies; - // 2) it was not indented properly; - // 3) it wasn’t very smart (see #675). - let mut applicability = Applicability::HasPlaceholders; - span_lint_and_sugg( - cx, - WHILE_LET_LOOP, - expr.span, - "this loop could be written as a `while let` loop", - "try", - format!( - "while let {} = {} {{ .. }}", - snippet_with_applicability(cx, arms[0].pat.span, "..", &mut applicability), - snippet_with_applicability(cx, matchexpr.span, "..", &mut applicability), - ), - applicability, - ); - } - }, - _ => (), - } - } - } - } - if let ExprKind::Match(ref match_expr, ref arms, MatchSource::WhileLetDesugar) = expr.kind { - let pat = &arms[0].pat.kind; - if let ( - &PatKind::TupleStruct(ref qpath, ref pat_args, _), - &ExprKind::MethodCall(ref method_path, _, ref method_args, _), - ) = (pat, &match_expr.kind) - { - let iter_expr = &method_args[0]; - - // Don't lint when the iterator is recreated on every iteration - if_chain! { - if let ExprKind::MethodCall(..) | ExprKind::Call(..) = iter_expr.kind; - if let Some(iter_def_id) = get_trait_def_id(cx, &paths::ITERATOR); - if implements_trait(cx, cx.typeck_results().expr_ty(iter_expr), iter_def_id, &[]); - then { - return; - } - } - - let lhs_constructor = last_path_segment(qpath); - if method_path.ident.name == sym::next - && match_trait_method(cx, match_expr, &paths::ITERATOR) - && lhs_constructor.ident.name == sym::Some - && (pat_args.is_empty() - || !is_refutable(cx, &pat_args[0]) - && !is_used_inside(cx, iter_expr, &arms[0].body) - && !is_iterator_used_after_while_let(cx, iter_expr) - && !is_nested(cx, expr, &method_args[0])) - { - let mut applicability = Applicability::MachineApplicable; - let iterator = snippet_with_applicability(cx, method_args[0].span, "_", &mut applicability); - let loop_var = if pat_args.is_empty() { - "_".to_string() - } else { - snippet_with_applicability(cx, pat_args[0].span, "_", &mut applicability).into_owned() - }; - span_lint_and_sugg( - cx, - WHILE_LET_ON_ITERATOR, - expr.span.with_hi(match_expr.span.hi()), - "this loop could be written as a `for` loop", - "try", - format!("for {} in {}", loop_var, iterator), - applicability, - ); - } - } - } - - if let Some((cond, body)) = higher::while_loop(&expr) { - check_infinite_loop(cx, cond, body); - } - - check_needless_collect(expr, cx); - } -} - -enum NeverLoopResult { - // A break/return always get triggered but not necessarily for the main loop. - AlwaysBreak, - // A continue may occur for the main loop. - MayContinueMainLoop, - Otherwise, -} - -#[must_use] -fn absorb_break(arg: &NeverLoopResult) -> NeverLoopResult { - match *arg { - NeverLoopResult::AlwaysBreak | NeverLoopResult::Otherwise => NeverLoopResult::Otherwise, - NeverLoopResult::MayContinueMainLoop => NeverLoopResult::MayContinueMainLoop, - } -} - -// Combine two results for parts that are called in order. -#[must_use] -fn combine_seq(first: NeverLoopResult, second: NeverLoopResult) -> NeverLoopResult { - match first { - NeverLoopResult::AlwaysBreak | NeverLoopResult::MayContinueMainLoop => first, - NeverLoopResult::Otherwise => second, - } -} - -// Combine two results where both parts are called but not necessarily in order. -#[must_use] -fn combine_both(left: NeverLoopResult, right: NeverLoopResult) -> NeverLoopResult { - match (left, right) { - (NeverLoopResult::MayContinueMainLoop, _) | (_, NeverLoopResult::MayContinueMainLoop) => { - NeverLoopResult::MayContinueMainLoop - }, - (NeverLoopResult::AlwaysBreak, _) | (_, NeverLoopResult::AlwaysBreak) => NeverLoopResult::AlwaysBreak, - (NeverLoopResult::Otherwise, NeverLoopResult::Otherwise) => NeverLoopResult::Otherwise, - } -} - -// Combine two results where only one of the part may have been executed. -#[must_use] -fn combine_branches(b1: NeverLoopResult, b2: NeverLoopResult) -> NeverLoopResult { - match (b1, b2) { - (NeverLoopResult::AlwaysBreak, NeverLoopResult::AlwaysBreak) => NeverLoopResult::AlwaysBreak, - (NeverLoopResult::MayContinueMainLoop, _) | (_, NeverLoopResult::MayContinueMainLoop) => { - NeverLoopResult::MayContinueMainLoop - }, - (NeverLoopResult::Otherwise, _) | (_, NeverLoopResult::Otherwise) => NeverLoopResult::Otherwise, - } -} - -fn never_loop_block(block: &Block<'_>, main_loop_id: HirId) -> NeverLoopResult { - let stmts = block.stmts.iter().map(stmt_to_expr); - let expr = once(block.expr.as_deref()); - let mut iter = stmts.chain(expr).flatten(); - never_loop_expr_seq(&mut iter, main_loop_id) -} - -fn stmt_to_expr<'tcx>(stmt: &Stmt<'tcx>) -> Option<&'tcx Expr<'tcx>> { - match stmt.kind { - StmtKind::Semi(ref e, ..) | StmtKind::Expr(ref e, ..) => Some(e), - StmtKind::Local(ref local) => local.init.as_deref(), - _ => None, - } -} - -fn never_loop_expr(expr: &Expr<'_>, main_loop_id: HirId) -> NeverLoopResult { - match expr.kind { - ExprKind::Box(ref e) - | ExprKind::Unary(_, ref e) - | ExprKind::Cast(ref e, _) - | ExprKind::Type(ref e, _) - | ExprKind::Field(ref e, _) - | ExprKind::AddrOf(_, _, ref e) - | ExprKind::Struct(_, _, Some(ref e)) - | ExprKind::Repeat(ref e, _) - | ExprKind::DropTemps(ref e) => never_loop_expr(e, main_loop_id), - ExprKind::Array(ref es) | ExprKind::MethodCall(_, _, ref es, _) | ExprKind::Tup(ref es) => { - never_loop_expr_all(&mut es.iter(), main_loop_id) - }, - ExprKind::Call(ref e, ref es) => never_loop_expr_all(&mut once(&**e).chain(es.iter()), main_loop_id), - ExprKind::Binary(_, ref e1, ref e2) - | ExprKind::Assign(ref e1, ref e2, _) - | ExprKind::AssignOp(_, ref e1, ref e2) - | ExprKind::Index(ref e1, ref e2) => never_loop_expr_all(&mut [&**e1, &**e2].iter().cloned(), main_loop_id), - ExprKind::Loop(ref b, _, _, _) => { - // Break can come from the inner loop so remove them. - absorb_break(&never_loop_block(b, main_loop_id)) - }, - ExprKind::If(ref e, ref e2, ref e3) => { - let e1 = never_loop_expr(e, main_loop_id); - let e2 = never_loop_expr(e2, main_loop_id); - let e3 = e3 - .as_ref() - .map_or(NeverLoopResult::Otherwise, |e| never_loop_expr(e, main_loop_id)); - combine_seq(e1, combine_branches(e2, e3)) - }, - ExprKind::Match(ref e, ref arms, _) => { - let e = never_loop_expr(e, main_loop_id); - if arms.is_empty() { - e - } else { - let arms = never_loop_expr_branch(&mut arms.iter().map(|a| &*a.body), main_loop_id); - combine_seq(e, arms) - } - }, - ExprKind::Block(ref b, _) => never_loop_block(b, main_loop_id), - ExprKind::Continue(d) => { - let id = d - .target_id - .expect("target ID can only be missing in the presence of compilation errors"); - if id == main_loop_id { - NeverLoopResult::MayContinueMainLoop - } else { - NeverLoopResult::AlwaysBreak - } - }, - ExprKind::Break(_, ref e) | ExprKind::Ret(ref e) => e.as_ref().map_or(NeverLoopResult::AlwaysBreak, |e| { - combine_seq(never_loop_expr(e, main_loop_id), NeverLoopResult::AlwaysBreak) - }), - ExprKind::InlineAsm(ref asm) => asm - .operands - .iter() - .map(|(o, _)| match o { - InlineAsmOperand::In { expr, .. } - | InlineAsmOperand::InOut { expr, .. } - | InlineAsmOperand::Const { expr } - | InlineAsmOperand::Sym { expr } => never_loop_expr(expr, main_loop_id), - InlineAsmOperand::Out { expr, .. } => never_loop_expr_all(&mut expr.iter(), main_loop_id), - InlineAsmOperand::SplitInOut { in_expr, out_expr, .. } => { - never_loop_expr_all(&mut once(in_expr).chain(out_expr.iter()), main_loop_id) - }, - }) - .fold(NeverLoopResult::Otherwise, combine_both), - ExprKind::Struct(_, _, None) - | ExprKind::Yield(_, _) - | ExprKind::Closure(_, _, _, _, _) - | ExprKind::LlvmInlineAsm(_) - | ExprKind::Path(_) - | ExprKind::ConstBlock(_) - | ExprKind::Lit(_) - | ExprKind::Err => NeverLoopResult::Otherwise, - } -} - -fn never_loop_expr_seq<'a, T: Iterator<Item = &'a Expr<'a>>>(es: &mut T, main_loop_id: HirId) -> NeverLoopResult { - es.map(|e| never_loop_expr(e, main_loop_id)) - .fold(NeverLoopResult::Otherwise, combine_seq) -} - -fn never_loop_expr_all<'a, T: Iterator<Item = &'a Expr<'a>>>(es: &mut T, main_loop_id: HirId) -> NeverLoopResult { - es.map(|e| never_loop_expr(e, main_loop_id)) - .fold(NeverLoopResult::Otherwise, combine_both) -} - -fn never_loop_expr_branch<'a, T: Iterator<Item = &'a Expr<'a>>>(e: &mut T, main_loop_id: HirId) -> NeverLoopResult { - e.map(|e| never_loop_expr(e, main_loop_id)) - .fold(NeverLoopResult::AlwaysBreak, combine_branches) -} - -fn check_for_loop<'tcx>( - cx: &LateContext<'tcx>, - pat: &'tcx Pat<'_>, - arg: &'tcx Expr<'_>, - body: &'tcx Expr<'_>, - expr: &'tcx Expr<'_>, - span: Span, -) { - let is_manual_memcpy_triggered = detect_manual_memcpy(cx, pat, arg, body, expr); - if !is_manual_memcpy_triggered { - check_for_loop_range(cx, pat, arg, body, expr); - check_for_loop_explicit_counter(cx, pat, arg, body, expr); - } - check_for_loop_arg(cx, pat, arg, expr); - check_for_loop_over_map_kv(cx, pat, arg, body, expr); - check_for_mut_range_bound(cx, arg, body); - check_for_single_element_loop(cx, pat, arg, body, expr); - detect_same_item_push(cx, pat, arg, body, expr); - check_manual_flatten(cx, pat, arg, body, span); -} - -// this function assumes the given expression is a `for` loop. -fn get_span_of_entire_for_loop(expr: &Expr<'_>) -> Span { - // for some reason this is the only way to get the `Span` - // of the entire `for` loop - if let ExprKind::Match(_, arms, _) = &expr.kind { - arms[0].body.span - } else { - unreachable!() - } -} - -/// a wrapper of `Sugg`. Besides what `Sugg` do, this removes unnecessary `0`; -/// and also, it avoids subtracting a variable from the same one by replacing it with `0`. -/// it exists for the convenience of the overloaded operators while normal functions can do the -/// same. -#[derive(Clone)] -struct MinifyingSugg<'a>(Sugg<'a>); - -impl<'a> MinifyingSugg<'a> { - fn as_str(&self) -> &str { - let Sugg::NonParen(s) | Sugg::MaybeParen(s) | Sugg::BinOp(_, s) = &self.0; - s.as_ref() - } - - fn into_sugg(self) -> Sugg<'a> { - self.0 - } -} - -impl<'a> From<Sugg<'a>> for MinifyingSugg<'a> { - fn from(sugg: Sugg<'a>) -> Self { - Self(sugg) - } -} - -impl std::ops::Add for &MinifyingSugg<'static> { - type Output = MinifyingSugg<'static>; - fn add(self, rhs: &MinifyingSugg<'static>) -> MinifyingSugg<'static> { - match (self.as_str(), rhs.as_str()) { - ("0", _) => rhs.clone(), - (_, "0") => self.clone(), - (_, _) => (&self.0 + &rhs.0).into(), - } - } -} - -impl std::ops::Sub for &MinifyingSugg<'static> { - type Output = MinifyingSugg<'static>; - fn sub(self, rhs: &MinifyingSugg<'static>) -> MinifyingSugg<'static> { - match (self.as_str(), rhs.as_str()) { - (_, "0") => self.clone(), - ("0", _) => (-rhs.0.clone()).into(), - (x, y) if x == y => sugg::ZERO.into(), - (_, _) => (&self.0 - &rhs.0).into(), - } - } -} - -impl std::ops::Add<&MinifyingSugg<'static>> for MinifyingSugg<'static> { - type Output = MinifyingSugg<'static>; - fn add(self, rhs: &MinifyingSugg<'static>) -> MinifyingSugg<'static> { - match (self.as_str(), rhs.as_str()) { - ("0", _) => rhs.clone(), - (_, "0") => self, - (_, _) => (self.0 + &rhs.0).into(), - } - } -} - -impl std::ops::Sub<&MinifyingSugg<'static>> for MinifyingSugg<'static> { - type Output = MinifyingSugg<'static>; - fn sub(self, rhs: &MinifyingSugg<'static>) -> MinifyingSugg<'static> { - match (self.as_str(), rhs.as_str()) { - (_, "0") => self, - ("0", _) => (-rhs.0.clone()).into(), - (x, y) if x == y => sugg::ZERO.into(), - (_, _) => (self.0 - &rhs.0).into(), - } - } -} - -/// a wrapper around `MinifyingSugg`, which carries a operator like currying -/// so that the suggested code become more efficient (e.g. `foo + -bar` `foo - bar`). -struct Offset { - value: MinifyingSugg<'static>, - sign: OffsetSign, -} - -#[derive(Clone, Copy)] -enum OffsetSign { - Positive, - Negative, -} - -impl Offset { - fn negative(value: Sugg<'static>) -> Self { - Self { - value: value.into(), - sign: OffsetSign::Negative, - } - } - - fn positive(value: Sugg<'static>) -> Self { - Self { - value: value.into(), - sign: OffsetSign::Positive, - } - } - - fn empty() -> Self { - Self::positive(sugg::ZERO) - } -} - -fn apply_offset(lhs: &MinifyingSugg<'static>, rhs: &Offset) -> MinifyingSugg<'static> { - match rhs.sign { - OffsetSign::Positive => lhs + &rhs.value, - OffsetSign::Negative => lhs - &rhs.value, - } -} - -#[derive(Debug, Clone, Copy)] -enum StartKind<'hir> { - Range, - Counter { initializer: &'hir Expr<'hir> }, -} - -struct IndexExpr<'hir> { - base: &'hir Expr<'hir>, - idx: StartKind<'hir>, - idx_offset: Offset, -} - -struct Start<'hir> { - id: HirId, - 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_type) || is_type_diagnostic_item(cx, ty, sym::vecdeque_type) -} - -fn fetch_cloned_expr<'tcx>(expr: &'tcx Expr<'tcx>) -> &'tcx Expr<'tcx> { - if_chain! { - if let ExprKind::MethodCall(method, _, args, _) = expr.kind; - if method.ident.name == sym::clone; - if args.len() == 1; - if let Some(arg) = args.get(0); - then { arg } else { expr } - } -} - -fn get_details_from_idx<'tcx>( - cx: &LateContext<'tcx>, - idx: &Expr<'_>, - starts: &[Start<'tcx>], -) -> Option<(StartKind<'tcx>, Offset)> { - fn get_start<'tcx>(e: &Expr<'_>, starts: &[Start<'tcx>]) -> Option<StartKind<'tcx>> { - let id = path_to_local(e)?; - starts.iter().find(|start| start.id == id).map(|start| start.kind) - } - - fn get_offset<'tcx>(cx: &LateContext<'tcx>, e: &Expr<'_>, starts: &[Start<'tcx>]) -> Option<Sugg<'static>> { - match &e.kind { - ExprKind::Lit(l) => match l.node { - ast::LitKind::Int(x, _ty) => Some(Sugg::NonParen(x.to_string().into())), - _ => None, - }, - ExprKind::Path(..) if get_start(e, starts).is_none() => Some(Sugg::hir(cx, e, "???")), - _ => None, - } - } - - match idx.kind { - ExprKind::Binary(op, lhs, rhs) => match op.node { - BinOpKind::Add => { - let offset_opt = get_start(lhs, starts) - .and_then(|s| get_offset(cx, rhs, starts).map(|o| (s, o))) - .or_else(|| get_start(rhs, starts).and_then(|s| get_offset(cx, lhs, starts).map(|o| (s, o)))); - - offset_opt.map(|(s, o)| (s, Offset::positive(o))) - }, - BinOpKind::Sub => { - get_start(lhs, starts).and_then(|s| get_offset(cx, rhs, starts).map(|o| (s, Offset::negative(o)))) - }, - _ => None, - }, - ExprKind::Path(..) => get_start(idx, starts).map(|s| (s, Offset::empty())), - _ => None, - } -} - -fn get_assignment<'tcx>(e: &'tcx Expr<'tcx>) -> Option<(&'tcx Expr<'tcx>, &'tcx Expr<'tcx>)> { - if let ExprKind::Assign(lhs, rhs, _) = e.kind { - Some((lhs, rhs)) - } else { - None - } -} - -/// Get assignments from the given block. -/// The returned iterator yields `None` if no assignment expressions are there, -/// filtering out the increments of the given whitelisted loop counters; -/// because its job is to make sure there's nothing other than assignments and the increments. -fn get_assignments<'a, 'tcx>( - Block { stmts, expr, .. }: &'tcx Block<'tcx>, - loop_counters: &'a [Start<'tcx>], -) -> impl Iterator<Item = Option<(&'tcx Expr<'tcx>, &'tcx Expr<'tcx>)>> + 'a { - // As the `filter` and `map` below do different things, I think putting together - // just increases complexity. (cc #3188 and #4193) - stmts - .iter() - .filter_map(move |stmt| match stmt.kind { - StmtKind::Local(..) | StmtKind::Item(..) => None, - StmtKind::Expr(e) | StmtKind::Semi(e) => Some(e), - }) - .chain((*expr).into_iter()) - .filter(move |e| { - if let ExprKind::AssignOp(_, place, _) = e.kind { - path_to_local(place).map_or(false, |id| { - !loop_counters - .iter() - // skip the first item which should be `StartKind::Range` - // this makes it possible to use the slice with `StartKind::Range` in the same iterator loop. - .skip(1) - .any(|counter| counter.id == id) - }) - } else { - true - } - }) - .map(get_assignment) -} - -fn get_loop_counters<'a, 'tcx>( - cx: &'a LateContext<'tcx>, - body: &'tcx Block<'tcx>, - expr: &'tcx Expr<'_>, -) -> Option<impl Iterator<Item = Start<'tcx>> + 'a> { - // Look for variables that are incremented once per loop iteration. - let mut increment_visitor = IncrementVisitor::new(cx); - walk_block(&mut increment_visitor, body); - - // For each candidate, check the parent block to see if - // it's initialized to zero at the start of the loop. - get_enclosing_block(&cx, expr.hir_id).and_then(|block| { - increment_visitor - .into_results() - .filter_map(move |var_id| { - let mut initialize_visitor = InitializeVisitor::new(cx, expr, var_id); - walk_block(&mut initialize_visitor, block); - - initialize_visitor.get_result().map(|(_, initializer)| Start { - id: var_id, - kind: StartKind::Counter { initializer }, - }) - }) - .into() - }) -} - -fn build_manual_memcpy_suggestion<'tcx>( - cx: &LateContext<'tcx>, - start: &Expr<'_>, - end: &Expr<'_>, - limits: ast::RangeLimits, - dst: &IndexExpr<'_>, - src: &IndexExpr<'_>, -) -> String { - fn print_offset(offset: MinifyingSugg<'static>) -> MinifyingSugg<'static> { - if offset.as_str() == "0" { - sugg::EMPTY.into() - } else { - offset - } - } - - let print_limit = |end: &Expr<'_>, end_str: &str, base: &Expr<'_>, sugg: MinifyingSugg<'static>| { - if_chain! { - if let ExprKind::MethodCall(method, _, len_args, _) = end.kind; - if method.ident.name == sym!(len); - if len_args.len() == 1; - if let Some(arg) = len_args.get(0); - if path_to_local(arg) == path_to_local(base); - then { - if sugg.as_str() == end_str { - sugg::EMPTY.into() - } else { - sugg - } - } else { - match limits { - ast::RangeLimits::Closed => { - sugg + &sugg::ONE.into() - }, - ast::RangeLimits::HalfOpen => sugg, - } - } - } - }; - - let start_str = Sugg::hir(cx, start, "").into(); - let end_str: MinifyingSugg<'_> = Sugg::hir(cx, end, "").into(); - - let print_offset_and_limit = |idx_expr: &IndexExpr<'_>| match idx_expr.idx { - StartKind::Range => ( - print_offset(apply_offset(&start_str, &idx_expr.idx_offset)).into_sugg(), - print_limit( - end, - end_str.as_str(), - idx_expr.base, - apply_offset(&end_str, &idx_expr.idx_offset), - ) - .into_sugg(), - ), - StartKind::Counter { initializer } => { - let counter_start = Sugg::hir(cx, initializer, "").into(); - ( - print_offset(apply_offset(&counter_start, &idx_expr.idx_offset)).into_sugg(), - print_limit( - end, - end_str.as_str(), - idx_expr.base, - apply_offset(&end_str, &idx_expr.idx_offset) + &counter_start - &start_str, - ) - .into_sugg(), - ) - }, - }; - - let (dst_offset, dst_limit) = print_offset_and_limit(&dst); - let (src_offset, src_limit) = print_offset_and_limit(&src); - - let dst_base_str = snippet(cx, dst.base.span, "???"); - let src_base_str = snippet(cx, src.base.span, "???"); - - let dst = if dst_offset == sugg::EMPTY && dst_limit == sugg::EMPTY { - dst_base_str - } else { - format!( - "{}[{}..{}]", - dst_base_str, - dst_offset.maybe_par(), - dst_limit.maybe_par() - ) - .into() - }; - - format!( - "{}.clone_from_slice(&{}[{}..{}]);", - dst, - src_base_str, - src_offset.maybe_par(), - src_limit.maybe_par() - ) -} - -/// Checks for for loops that sequentially copy items from one slice-like -/// object to another. -fn detect_manual_memcpy<'tcx>( - cx: &LateContext<'tcx>, - pat: &'tcx Pat<'_>, - arg: &'tcx Expr<'_>, - body: &'tcx Expr<'_>, - expr: &'tcx Expr<'_>, -) -> bool { - if let Some(higher::Range { - start: Some(start), - end: Some(end), - limits, - }) = higher::range(arg) - { - // the var must be a single name - if let PatKind::Binding(_, canonical_id, _, _) = pat.kind { - let mut starts = vec![Start { - id: canonical_id, - kind: StartKind::Range, - }]; - - // This is one of few ways to return different iterators - // derived from: https://stackoverflow.com/questions/29760668/conditionally-iterate-over-one-of-several-possible-iterators/52064434#52064434 - let mut iter_a = None; - let mut iter_b = None; - - if let ExprKind::Block(block, _) = body.kind { - if let Some(loop_counters) = get_loop_counters(cx, block, expr) { - starts.extend(loop_counters); - } - iter_a = Some(get_assignments(block, &starts)); - } else { - iter_b = Some(get_assignment(body)); - } - - let assignments = iter_a.into_iter().flatten().chain(iter_b.into_iter()); - - let big_sugg = assignments - // The only statements in the for loops can be indexed assignments from - // indexed retrievals (except increments of loop counters). - .map(|o| { - o.and_then(|(lhs, rhs)| { - let rhs = fetch_cloned_expr(rhs); - 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)) - && is_slice_like(cx, cx.typeck_results().expr_ty(base_right)); - 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 }, - IndexExpr { base: base_right, idx: start_right, idx_offset: offset_right })) - } else { - None - } - } - }) - }) - .map(|o| o.map(|(dst, src)| build_manual_memcpy_suggestion(cx, start, end, limits, &dst, &src))) - .collect::<Option<Vec<_>>>() - .filter(|v| !v.is_empty()) - .map(|v| v.join("\n ")); - - if let Some(big_sugg) = big_sugg { - span_lint_and_sugg( - cx, - MANUAL_MEMCPY, - get_span_of_entire_for_loop(expr), - "it looks like you're manually copying between slices", - "try replacing the loop by", - big_sugg, - Applicability::Unspecified, - ); - return true; - } - } - } - false -} - -// Scans the body of the for loop and determines whether lint should be given -struct SameItemPushVisitor<'a, 'tcx> { - should_lint: bool, - // this field holds the last vec push operation visited, which should be the only push seen - vec_push: Option<(&'tcx Expr<'tcx>, &'tcx Expr<'tcx>)>, - cx: &'a LateContext<'tcx>, -} - -impl<'a, 'tcx> Visitor<'tcx> for SameItemPushVisitor<'a, 'tcx> { - type Map = Map<'tcx>; - - fn visit_expr(&mut self, expr: &'tcx Expr<'_>) { - match &expr.kind { - // Non-determinism may occur ... don't give a lint - ExprKind::Loop(..) | ExprKind::Match(..) => self.should_lint = false, - ExprKind::Block(block, _) => self.visit_block(block), - _ => {}, - } - } - - fn visit_block(&mut self, b: &'tcx Block<'_>) { - for stmt in b.stmts.iter() { - self.visit_stmt(stmt); - } - } - - fn visit_stmt(&mut self, s: &'tcx Stmt<'_>) { - let vec_push_option = get_vec_push(self.cx, s); - if vec_push_option.is_none() { - // Current statement is not a push so visit inside - match &s.kind { - StmtKind::Expr(expr) | StmtKind::Semi(expr) => self.visit_expr(&expr), - _ => {}, - } - } else { - // Current statement is a push ...check whether another - // push had been previously done - if self.vec_push.is_none() { - self.vec_push = vec_push_option; - } else { - // There are multiple pushes ... don't lint - self.should_lint = false; - } - } - } - - fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> { - NestedVisitorMap::None - } -} - -// Given some statement, determine if that statement is a push on a Vec. If it is, return -// the Vec being pushed into and the item being pushed -fn get_vec_push<'tcx>(cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'_>) -> Option<(&'tcx Expr<'tcx>, &'tcx Expr<'tcx>)> { - if_chain! { - // Extract method being called - if let StmtKind::Semi(semi_stmt) = &stmt.kind; - if let ExprKind::MethodCall(path, _, args, _) = &semi_stmt.kind; - // Figure out the parameters for the method call - if let Some(self_expr) = args.get(0); - if let Some(pushed_item) = args.get(1); - // Check that the method being called is push() on a Vec - if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(self_expr), sym::vec_type); - if path.ident.name.as_str() == "push"; - then { - return Some((self_expr, pushed_item)) - } - } - None -} - -/// Detects for loop pushing the same item into a Vec -fn detect_same_item_push<'tcx>( - cx: &LateContext<'tcx>, - pat: &'tcx Pat<'_>, - _: &'tcx Expr<'_>, - body: &'tcx Expr<'_>, - _: &'tcx Expr<'_>, -) { - fn emit_lint(cx: &LateContext<'_>, vec: &Expr<'_>, pushed_item: &Expr<'_>) { - let vec_str = snippet_with_macro_callsite(cx, vec.span, ""); - let item_str = snippet_with_macro_callsite(cx, pushed_item.span, ""); - - span_lint_and_help( - cx, - SAME_ITEM_PUSH, - vec.span, - "it looks like the same item is being pushed into this Vec", - None, - &format!( - "try using vec![{};SIZE] or {}.resize(NEW_SIZE, {})", - item_str, vec_str, item_str - ), - ) - } - - if !matches!(pat.kind, PatKind::Wild) { - return; - } - - // Determine whether it is safe to lint the body - let mut same_item_push_visitor = SameItemPushVisitor { - should_lint: true, - vec_push: None, - cx, - }; - walk_expr(&mut same_item_push_visitor, body); - if same_item_push_visitor.should_lint { - if let Some((vec, pushed_item)) = same_item_push_visitor.vec_push { - let vec_ty = cx.typeck_results().expr_ty(vec); - let ty = vec_ty.walk().nth(1).unwrap().expect_ty(); - if cx - .tcx - .lang_items() - .clone_trait() - .map_or(false, |id| implements_trait(cx, ty, id, &[])) - { - // Make sure that the push does not involve possibly mutating values - match pushed_item.kind { - ExprKind::Path(ref qpath) => { - match cx.qpath_res(qpath, pushed_item.hir_id) { - // immutable bindings that are initialized with literal or constant - Res::Local(hir_id) => { - if_chain! { - let node = cx.tcx.hir().get(hir_id); - if let Node::Binding(pat) = node; - if let PatKind::Binding(bind_ann, ..) = pat.kind; - if !matches!(bind_ann, BindingAnnotation::RefMut | BindingAnnotation::Mutable); - let parent_node = cx.tcx.hir().get_parent_node(hir_id); - if let Some(Node::Local(parent_let_expr)) = cx.tcx.hir().find(parent_node); - if let Some(init) = parent_let_expr.init; - then { - match init.kind { - // immutable bindings that are initialized with literal - ExprKind::Lit(..) => emit_lint(cx, vec, pushed_item), - // immutable bindings that are initialized with constant - ExprKind::Path(ref path) => { - if let Res::Def(DefKind::Const, ..) = cx.qpath_res(path, init.hir_id) { - emit_lint(cx, vec, pushed_item); - } - } - _ => {}, - } - } - } - }, - // constant - Res::Def(DefKind::Const, ..) => emit_lint(cx, vec, pushed_item), - _ => {}, - } - }, - ExprKind::Lit(..) => emit_lint(cx, vec, pushed_item), - _ => {}, - } - } - } - } -} - -/// Checks for looping over a range and then indexing a sequence with it. -/// The iteratee must be a range literal. -#[allow(clippy::too_many_lines)] -fn check_for_loop_range<'tcx>( - cx: &LateContext<'tcx>, - pat: &'tcx Pat<'_>, - arg: &'tcx Expr<'_>, - body: &'tcx Expr<'_>, - expr: &'tcx Expr<'_>, -) { - if let Some(higher::Range { - start: Some(start), - ref end, - limits, - }) = higher::range(arg) - { - // the var must be a single name - if let PatKind::Binding(_, canonical_id, ident, _) = pat.kind { - let mut visitor = VarVisitor { - cx, - var: canonical_id, - indexed_mut: FxHashSet::default(), - indexed_indirectly: FxHashMap::default(), - indexed_directly: FxHashMap::default(), - referenced: FxHashSet::default(), - nonindex: false, - prefer_mutable: false, - }; - walk_expr(&mut visitor, body); - - // linting condition: we only indexed one variable, and indexed it directly - if visitor.indexed_indirectly.is_empty() && visitor.indexed_directly.len() == 1 { - let (indexed, (indexed_extent, indexed_ty)) = visitor - .indexed_directly - .into_iter() - .next() - .expect("already checked that we have exactly 1 element"); - - // ensure that the indexed variable was declared before the loop, see #601 - if let Some(indexed_extent) = indexed_extent { - let parent_id = cx.tcx.hir().get_parent_item(expr.hir_id); - let parent_def_id = cx.tcx.hir().local_def_id(parent_id); - let region_scope_tree = cx.tcx.region_scope_tree(parent_def_id); - let pat_extent = region_scope_tree.var_scope(pat.hir_id.local_id); - if region_scope_tree.is_subscope_of(indexed_extent, pat_extent) { - return; - } - } - - // don't lint if the container that is indexed does not have .iter() method - let has_iter = has_iter_method(cx, indexed_ty); - if has_iter.is_none() { - return; - } - - // don't lint if the container that is indexed into is also used without - // indexing - if visitor.referenced.contains(&indexed) { - return; - } - - let starts_at_zero = is_integer_const(cx, start, 0); - - let skip = if starts_at_zero { - String::new() - } else if visitor.indexed_mut.contains(&indexed) && contains_name(indexed, start) { - return; - } else { - format!(".skip({})", snippet(cx, start.span, "..")) - }; - - let mut end_is_start_plus_val = false; - - let take = if let Some(end) = *end { - let mut take_expr = end; - - if let ExprKind::Binary(ref op, ref left, ref right) = end.kind { - if let BinOpKind::Add = op.node { - let start_equal_left = SpanlessEq::new(cx).eq_expr(start, left); - let start_equal_right = SpanlessEq::new(cx).eq_expr(start, right); - - if start_equal_left { - take_expr = right; - } else if start_equal_right { - take_expr = left; - } - - end_is_start_plus_val = start_equal_left | start_equal_right; - } - } - - if is_len_call(end, indexed) || is_end_eq_array_len(cx, end, limits, indexed_ty) { - String::new() - } else if visitor.indexed_mut.contains(&indexed) && contains_name(indexed, take_expr) { - return; - } else { - match limits { - ast::RangeLimits::Closed => { - let take_expr = sugg::Sugg::hir(cx, take_expr, "<count>"); - format!(".take({})", take_expr + sugg::ONE) - }, - ast::RangeLimits::HalfOpen => format!(".take({})", snippet(cx, take_expr.span, "..")), - } - } - } else { - String::new() - }; - - let (ref_mut, method) = if visitor.indexed_mut.contains(&indexed) { - ("mut ", "iter_mut") - } else { - ("", "iter") - }; - - let take_is_empty = take.is_empty(); - let mut method_1 = take; - let mut method_2 = skip; - - if end_is_start_plus_val { - mem::swap(&mut method_1, &mut method_2); - } - - if visitor.nonindex { - span_lint_and_then( - cx, - NEEDLESS_RANGE_LOOP, - expr.span, - &format!("the loop variable `{}` is used to index `{}`", ident.name, indexed), - |diag| { - multispan_sugg( - diag, - "consider using an iterator", - vec![ - (pat.span, format!("({}, <item>)", ident.name)), - ( - arg.span, - format!("{}.{}().enumerate(){}{}", indexed, method, method_1, method_2), - ), - ], - ); - }, - ); - } else { - let repl = if starts_at_zero && take_is_empty { - format!("&{}{}", ref_mut, indexed) - } else { - format!("{}.{}(){}{}", indexed, method, method_1, method_2) - }; - - span_lint_and_then( - cx, - NEEDLESS_RANGE_LOOP, - expr.span, - &format!( - "the loop variable `{}` is only used to index `{}`.", - ident.name, indexed - ), - |diag| { - multispan_sugg( - diag, - "consider using an iterator", - vec![(pat.span, "<item>".to_string()), (arg.span, repl)], - ); - }, - ); - } - } - } - } -} - -fn is_len_call(expr: &Expr<'_>, var: Symbol) -> bool { - if_chain! { - if let ExprKind::MethodCall(ref method, _, ref len_args, _) = expr.kind; - if len_args.len() == 1; - if method.ident.name == sym!(len); - if let ExprKind::Path(QPath::Resolved(_, ref path)) = len_args[0].kind; - if path.segments.len() == 1; - if path.segments[0].ident.name == var; - then { - return true; - } - } - - false -} - -fn is_end_eq_array_len<'tcx>( - cx: &LateContext<'tcx>, - end: &Expr<'_>, - limits: ast::RangeLimits, - indexed_ty: Ty<'tcx>, -) -> bool { - if_chain! { - if let ExprKind::Lit(ref lit) = end.kind; - if let ast::LitKind::Int(end_int, _) = lit.node; - if let ty::Array(_, arr_len_const) = indexed_ty.kind(); - if let Some(arr_len) = arr_len_const.try_eval_usize(cx.tcx, cx.param_env); - then { - return match limits { - ast::RangeLimits::Closed => end_int + 1 >= arr_len.into(), - ast::RangeLimits::HalfOpen => end_int >= arr_len.into(), - }; - } - } - - false -} - -fn lint_iter_method(cx: &LateContext<'_>, args: &[Expr<'_>], arg: &Expr<'_>, method_name: &str) { - let mut applicability = Applicability::MachineApplicable; - let object = snippet_with_applicability(cx, args[0].span, "_", &mut applicability); - let muta = if method_name == "iter_mut" { "mut " } else { "" }; - span_lint_and_sugg( - cx, - EXPLICIT_ITER_LOOP, - arg.span, - "it is more concise to loop over references to containers instead of using explicit \ - iteration methods", - "to write this more concisely, try", - format!("&{}{}", muta, object), - applicability, - ) -} - -fn check_for_loop_arg(cx: &LateContext<'_>, pat: &Pat<'_>, arg: &Expr<'_>, expr: &Expr<'_>) { - let mut next_loop_linted = false; // whether or not ITER_NEXT_LOOP lint was used - if let ExprKind::MethodCall(ref method, _, ref args, _) = arg.kind { - // just the receiver, no arguments - if args.len() == 1 { - let method_name = &*method.ident.as_str(); - // check for looping over x.iter() or x.iter_mut(), could use &x or &mut x - if method_name == "iter" || method_name == "iter_mut" { - if is_ref_iterable_type(cx, &args[0]) { - lint_iter_method(cx, args, arg, method_name); - } - } else if method_name == "into_iter" && match_trait_method(cx, arg, &paths::INTO_ITERATOR) { - let receiver_ty = cx.typeck_results().expr_ty(&args[0]); - let receiver_ty_adjusted = cx.typeck_results().expr_ty_adjusted(&args[0]); - if TyS::same_type(receiver_ty, receiver_ty_adjusted) { - let mut applicability = Applicability::MachineApplicable; - let object = snippet_with_applicability(cx, args[0].span, "_", &mut applicability); - span_lint_and_sugg( - cx, - EXPLICIT_INTO_ITER_LOOP, - arg.span, - "it is more concise to loop over containers instead of using explicit \ - iteration methods", - "to write this more concisely, try", - object.to_string(), - applicability, - ); - } else { - let ref_receiver_ty = cx.tcx.mk_ref( - cx.tcx.lifetimes.re_erased, - ty::TypeAndMut { - ty: receiver_ty, - mutbl: Mutability::Not, - }, - ); - if TyS::same_type(receiver_ty_adjusted, ref_receiver_ty) { - lint_iter_method(cx, args, arg, method_name) - } - } - } else if method_name == "next" && match_trait_method(cx, arg, &paths::ITERATOR) { - span_lint( - cx, - ITER_NEXT_LOOP, - expr.span, - "you are iterating over `Iterator::next()` which is an Option; this will compile but is \ - probably not what you want", - ); - next_loop_linted = true; - } - } - } - if !next_loop_linted { - check_arg_type(cx, pat, arg); - } -} - -/// Checks for `for` loops over `Option`s and `Result`s. -fn check_arg_type(cx: &LateContext<'_>, pat: &Pat<'_>, arg: &Expr<'_>) { - let ty = cx.typeck_results().expr_ty(arg); - if is_type_diagnostic_item(cx, ty, sym::option_type) { - span_lint_and_help( - cx, - FOR_LOOPS_OVER_FALLIBLES, - arg.span, - &format!( - "for loop over `{0}`, which is an `Option`. This is more readably written as an \ - `if let` statement.", - snippet(cx, arg.span, "_") - ), - None, - &format!( - "consider replacing `for {0} in {1}` with `if let Some({0}) = {1}`", - snippet(cx, pat.span, "_"), - snippet(cx, arg.span, "_") - ), - ); - } else if is_type_diagnostic_item(cx, ty, sym::result_type) { - span_lint_and_help( - cx, - FOR_LOOPS_OVER_FALLIBLES, - arg.span, - &format!( - "for loop over `{0}`, which is a `Result`. This is more readably written as an \ - `if let` statement.", - snippet(cx, arg.span, "_") - ), - None, - &format!( - "consider replacing `for {0} in {1}` with `if let Ok({0}) = {1}`", - snippet(cx, pat.span, "_"), - snippet(cx, arg.span, "_") - ), - ); - } -} - -// To trigger the EXPLICIT_COUNTER_LOOP lint, a variable must be -// incremented exactly once in the loop body, and initialized to zero -// at the start of the loop. -fn check_for_loop_explicit_counter<'tcx>( - cx: &LateContext<'tcx>, - pat: &'tcx Pat<'_>, - arg: &'tcx Expr<'_>, - body: &'tcx Expr<'_>, - expr: &'tcx Expr<'_>, -) { - // Look for variables that are incremented once per loop iteration. - let mut increment_visitor = IncrementVisitor::new(cx); - walk_expr(&mut increment_visitor, body); - - // For each candidate, check the parent block to see if - // it's initialized to zero at the start of the loop. - if let Some(block) = get_enclosing_block(&cx, expr.hir_id) { - for id in increment_visitor.into_results() { - let mut initialize_visitor = InitializeVisitor::new(cx, expr, id); - walk_block(&mut initialize_visitor, block); - - if_chain! { - if let Some((name, initializer)) = initialize_visitor.get_result(); - if is_integer_const(cx, initializer, 0); - then { - let mut applicability = Applicability::MachineApplicable; - - let for_span = get_span_of_entire_for_loop(expr); - - span_lint_and_sugg( - cx, - EXPLICIT_COUNTER_LOOP, - for_span.with_hi(arg.span.hi()), - &format!("the variable `{}` is used as a loop counter.", name), - "consider using", - format!( - "for ({}, {}) in {}.enumerate()", - name, - snippet_with_applicability(cx, pat.span, "item", &mut applicability), - make_iterator_snippet(cx, arg, &mut applicability), - ), - applicability, - ); - } - } - } - } -} - -/// If `arg` was the argument to a `for` loop, return the "cleanest" way of writing the -/// actual `Iterator` that the loop uses. -fn make_iterator_snippet(cx: &LateContext<'_>, arg: &Expr<'_>, applic_ref: &mut Applicability) -> String { - let impls_iterator = get_trait_def_id(cx, &paths::ITERATOR).map_or(false, |id| { - implements_trait(cx, cx.typeck_results().expr_ty(arg), id, &[]) - }); - if impls_iterator { - format!( - "{}", - sugg::Sugg::hir_with_applicability(cx, arg, "_", applic_ref).maybe_par() - ) - } else { - // (&x).into_iter() ==> x.iter() - // (&mut x).into_iter() ==> x.iter_mut() - match &arg.kind { - ExprKind::AddrOf(BorrowKind::Ref, mutability, arg_inner) - if has_iter_method(cx, cx.typeck_results().expr_ty(&arg_inner)).is_some() => - { - let meth_name = match mutability { - Mutability::Mut => "iter_mut", - Mutability::Not => "iter", - }; - format!( - "{}.{}()", - sugg::Sugg::hir_with_applicability(cx, &arg_inner, "_", applic_ref).maybe_par(), - meth_name, - ) - } - _ => format!( - "{}.into_iter()", - sugg::Sugg::hir_with_applicability(cx, arg, "_", applic_ref).maybe_par() - ), - } - } -} - -/// Checks for the `FOR_KV_MAP` lint. -fn check_for_loop_over_map_kv<'tcx>( - cx: &LateContext<'tcx>, - pat: &'tcx Pat<'_>, - arg: &'tcx Expr<'_>, - body: &'tcx Expr<'_>, - expr: &'tcx Expr<'_>, -) { - let pat_span = pat.span; - - if let PatKind::Tuple(ref pat, _) = pat.kind { - if pat.len() == 2 { - let arg_span = arg.span; - let (new_pat_span, kind, ty, mutbl) = match *cx.typeck_results().expr_ty(arg).kind() { - ty::Ref(_, ty, mutbl) => match (&pat[0].kind, &pat[1].kind) { - (key, _) if pat_is_wild(cx, key, body) => (pat[1].span, "value", ty, mutbl), - (_, value) if pat_is_wild(cx, value, body) => (pat[0].span, "key", ty, Mutability::Not), - _ => return, - }, - _ => return, - }; - let mutbl = match mutbl { - Mutability::Not => "", - Mutability::Mut => "_mut", - }; - let arg = match arg.kind { - ExprKind::AddrOf(BorrowKind::Ref, _, ref expr) => &**expr, - _ => arg, - }; - - if is_type_diagnostic_item(cx, ty, sym::hashmap_type) || match_type(cx, ty, &paths::BTREEMAP) { - span_lint_and_then( - cx, - FOR_KV_MAP, - expr.span, - &format!("you seem to want to iterate on a map's {}s", kind), - |diag| { - let map = sugg::Sugg::hir(cx, arg, "map"); - multispan_sugg( - diag, - "use the corresponding method", - vec![ - (pat_span, snippet(cx, new_pat_span, kind).into_owned()), - (arg_span, format!("{}.{}s{}()", map.maybe_par(), kind, mutbl)), - ], - ); - }, - ); - } - } - } -} - -fn check_for_single_element_loop<'tcx>( - cx: &LateContext<'tcx>, - pat: &'tcx Pat<'_>, - arg: &'tcx Expr<'_>, - body: &'tcx Expr<'_>, - expr: &'tcx Expr<'_>, -) { - if_chain! { - if let ExprKind::AddrOf(BorrowKind::Ref, _, ref arg_expr) = arg.kind; - if let PatKind::Binding(.., target, _) = pat.kind; - if let ExprKind::Array([arg_expression]) = arg_expr.kind; - if let ExprKind::Path(ref list_item) = arg_expression.kind; - if let Some(list_item_name) = single_segment_path(list_item).map(|ps| ps.ident.name); - if let ExprKind::Block(ref block, _) = body.kind; - if !block.stmts.is_empty(); - - then { - let for_span = get_span_of_entire_for_loop(expr); - let mut block_str = snippet(cx, block.span, "..").into_owned(); - block_str.remove(0); - block_str.pop(); - - - span_lint_and_sugg( - cx, - SINGLE_ELEMENT_LOOP, - for_span, - "for loop over a single element", - "try", - format!("{{\n{}let {} = &{};{}}}", " ".repeat(indent_of(cx, block.stmts[0].span).unwrap_or(0)), target.name, list_item_name, block_str), - Applicability::MachineApplicable - ) - } - } -} - -/// Check for unnecessary `if let` usage in a for loop where only the `Some` or `Ok` variant of the -/// iterator element is used. -fn check_manual_flatten<'tcx>( - cx: &LateContext<'tcx>, - pat: &'tcx Pat<'_>, - arg: &'tcx Expr<'_>, - body: &'tcx Expr<'_>, - span: Span, -) { - if let ExprKind::Block(ref block, _) = body.kind { - // Ensure the `if let` statement is the only expression or statement in the for-loop - let inner_expr = if block.stmts.len() == 1 && block.expr.is_none() { - let match_stmt = &block.stmts[0]; - if let StmtKind::Semi(inner_expr) = match_stmt.kind { - Some(inner_expr) - } else { - None - } - } else if block.stmts.is_empty() { - block.expr - } else { - None - }; - - if_chain! { - if let Some(inner_expr) = inner_expr; - if let ExprKind::Match( - ref match_expr, ref match_arms, MatchSource::IfLetDesugar{ contains_else_clause: false } - ) = inner_expr.kind; - // Ensure match_expr in `if let` statement is the same as the pat from the for-loop - if let PatKind::Binding(_, pat_hir_id, _, _) = pat.kind; - if path_to_local_id(match_expr, pat_hir_id); - // Ensure the `if let` statement is for the `Some` variant of `Option` or the `Ok` variant of `Result` - if let PatKind::TupleStruct(QPath::Resolved(None, path), _, _) = match_arms[0].pat.kind; - let some_ctor = is_some_ctor(cx, path.res); - let ok_ctor = is_ok_ctor(cx, path.res); - if some_ctor || ok_ctor; - let if_let_type = if some_ctor { "Some" } else { "Ok" }; - - then { - // Prepare the error message - let msg = format!("unnecessary `if let` since only the `{}` variant of the iterator element is used", if_let_type); - - // Prepare the help message - let mut applicability = Applicability::MaybeIncorrect; - let arg_snippet = make_iterator_snippet(cx, arg, &mut applicability); - - span_lint_and_then( - cx, - MANUAL_FLATTEN, - span, - &msg, - |diag| { - let sugg = format!("{}.flatten()", arg_snippet); - diag.span_suggestion( - arg.span, - "try", - sugg, - Applicability::MaybeIncorrect, - ); - diag.span_help( - inner_expr.span, - "...and remove the `if let` statement in the for loop", - ); - } - ); - } - } - } -} - -struct MutatePairDelegate<'a, 'tcx> { - cx: &'a LateContext<'tcx>, - hir_id_low: Option<HirId>, - hir_id_high: Option<HirId>, - span_low: Option<Span>, - span_high: Option<Span>, -} - -impl<'tcx> Delegate<'tcx> for MutatePairDelegate<'_, 'tcx> { - fn consume(&mut self, _: &PlaceWithHirId<'tcx>, _: HirId, _: ConsumeMode) {} - - fn borrow(&mut self, cmt: &PlaceWithHirId<'tcx>, diag_expr_id: HirId, bk: ty::BorrowKind) { - if let ty::BorrowKind::MutBorrow = bk { - if let PlaceBase::Local(id) = cmt.place.base { - if Some(id) == self.hir_id_low { - self.span_low = Some(self.cx.tcx.hir().span(diag_expr_id)) - } - if Some(id) == self.hir_id_high { - self.span_high = Some(self.cx.tcx.hir().span(diag_expr_id)) - } - } - } - } - - fn mutate(&mut self, cmt: &PlaceWithHirId<'tcx>, diag_expr_id: HirId) { - if let PlaceBase::Local(id) = cmt.place.base { - if Some(id) == self.hir_id_low { - self.span_low = Some(self.cx.tcx.hir().span(diag_expr_id)) - } - if Some(id) == self.hir_id_high { - self.span_high = Some(self.cx.tcx.hir().span(diag_expr_id)) - } - } - } -} - -impl MutatePairDelegate<'_, '_> { - fn mutation_span(&self) -> (Option<Span>, Option<Span>) { - (self.span_low, self.span_high) - } -} - -fn check_for_mut_range_bound(cx: &LateContext<'_>, arg: &Expr<'_>, body: &Expr<'_>) { - if let Some(higher::Range { - start: Some(start), - end: Some(end), - .. - }) = higher::range(arg) - { - let mut_ids = vec![check_for_mutability(cx, start), check_for_mutability(cx, end)]; - if mut_ids[0].is_some() || mut_ids[1].is_some() { - let (span_low, span_high) = check_for_mutation(cx, body, &mut_ids); - mut_warn_with_span(cx, span_low); - mut_warn_with_span(cx, span_high); - } - } -} - -fn mut_warn_with_span(cx: &LateContext<'_>, span: Option<Span>) { - if let Some(sp) = span { - span_lint( - cx, - MUT_RANGE_BOUND, - sp, - "attempt to mutate range bound within loop; note that the range of the loop is unchanged", - ); - } -} - -fn check_for_mutability(cx: &LateContext<'_>, bound: &Expr<'_>) -> Option<HirId> { - if_chain! { - if let Some(hir_id) = path_to_local(bound); - if let Node::Binding(pat) = cx.tcx.hir().get(hir_id); - if let PatKind::Binding(BindingAnnotation::Mutable, ..) = pat.kind; - then { - return Some(hir_id); - } - } - None -} - -fn check_for_mutation<'tcx>( - cx: &LateContext<'tcx>, - body: &Expr<'_>, - bound_ids: &[Option<HirId>], -) -> (Option<Span>, Option<Span>) { - let mut delegate = MutatePairDelegate { - cx, - hir_id_low: bound_ids[0], - hir_id_high: bound_ids[1], - span_low: None, - span_high: None, - }; - cx.tcx.infer_ctxt().enter(|infcx| { - ExprUseVisitor::new( - &mut delegate, - &infcx, - body.hir_id.owner, - cx.param_env, - cx.typeck_results(), - ) - .walk_expr(body); - }); - delegate.mutation_span() -} - -/// Returns `true` if the pattern is a `PatWild` or an ident prefixed with `_`. -fn pat_is_wild<'tcx>(cx: &LateContext<'tcx>, pat: &'tcx PatKind<'_>, body: &'tcx Expr<'_>) -> bool { - match *pat { - PatKind::Wild => true, - PatKind::Binding(_, id, ident, None) if ident.as_str().starts_with('_') => { - !LocalUsedVisitor::new(cx, id).check_expr(body) - }, - _ => false, - } -} - -struct VarVisitor<'a, 'tcx> { - /// context reference - cx: &'a LateContext<'tcx>, - /// var name to look for as index - var: HirId, - /// indexed variables that are used mutably - indexed_mut: FxHashSet<Symbol>, - /// indirectly indexed variables (`v[(i + 4) % N]`), the extend is `None` for global - indexed_indirectly: FxHashMap<Symbol, Option<region::Scope>>, - /// subset of `indexed` of vars that are indexed directly: `v[i]` - /// this will not contain cases like `v[calc_index(i)]` or `v[(i + 4) % N]` - indexed_directly: FxHashMap<Symbol, (Option<region::Scope>, Ty<'tcx>)>, - /// Any names that are used outside an index operation. - /// Used to detect things like `&mut vec` used together with `vec[i]` - referenced: FxHashSet<Symbol>, - /// has the loop variable been used in expressions other than the index of - /// an index op? - nonindex: bool, - /// Whether we are inside the `$` in `&mut $` or `$ = foo` or `$.bar`, where bar - /// takes `&mut self` - prefer_mutable: bool, -} - -impl<'a, 'tcx> VarVisitor<'a, 'tcx> { - fn check(&mut self, idx: &'tcx Expr<'_>, seqexpr: &'tcx Expr<'_>, expr: &'tcx Expr<'_>) -> bool { - if_chain! { - // the indexed container is referenced by a name - if let ExprKind::Path(ref seqpath) = seqexpr.kind; - if let QPath::Resolved(None, ref seqvar) = *seqpath; - if seqvar.segments.len() == 1; - then { - let index_used_directly = path_to_local_id(idx, self.var); - let indexed_indirectly = { - let mut used_visitor = LocalUsedVisitor::new(self.cx, self.var); - walk_expr(&mut used_visitor, idx); - used_visitor.used - }; - - if indexed_indirectly || index_used_directly { - if self.prefer_mutable { - self.indexed_mut.insert(seqvar.segments[0].ident.name); - } - let res = self.cx.qpath_res(seqpath, seqexpr.hir_id); - match res { - Res::Local(hir_id) => { - let parent_id = self.cx.tcx.hir().get_parent_item(expr.hir_id); - let parent_def_id = self.cx.tcx.hir().local_def_id(parent_id); - let extent = self.cx.tcx.region_scope_tree(parent_def_id).var_scope(hir_id.local_id); - if indexed_indirectly { - self.indexed_indirectly.insert(seqvar.segments[0].ident.name, Some(extent)); - } - if index_used_directly { - self.indexed_directly.insert( - seqvar.segments[0].ident.name, - (Some(extent), self.cx.typeck_results().node_type(seqexpr.hir_id)), - ); - } - return false; // no need to walk further *on the variable* - } - Res::Def(DefKind::Static | DefKind::Const, ..) => { - if indexed_indirectly { - self.indexed_indirectly.insert(seqvar.segments[0].ident.name, None); - } - if index_used_directly { - self.indexed_directly.insert( - seqvar.segments[0].ident.name, - (None, self.cx.typeck_results().node_type(seqexpr.hir_id)), - ); - } - return false; // no need to walk further *on the variable* - } - _ => (), - } - } - } - } - true - } -} - -impl<'a, 'tcx> Visitor<'tcx> for VarVisitor<'a, 'tcx> { - type Map = Map<'tcx>; - - fn visit_expr(&mut self, expr: &'tcx Expr<'_>) { - if_chain! { - // a range index op - if let ExprKind::MethodCall(ref meth, _, ref args, _) = expr.kind; - if (meth.ident.name == sym::index && match_trait_method(self.cx, expr, &paths::INDEX)) - || (meth.ident.name == sym::index_mut && match_trait_method(self.cx, expr, &paths::INDEX_MUT)); - if !self.check(&args[1], &args[0], expr); - then { return } - } - - if_chain! { - // an index op - if let ExprKind::Index(ref seqexpr, ref idx) = expr.kind; - if !self.check(idx, seqexpr, expr); - then { return } - } - - if_chain! { - // directly using a variable - if let ExprKind::Path(QPath::Resolved(None, path)) = expr.kind; - if let Res::Local(local_id) = path.res; - then { - if local_id == self.var { - self.nonindex = true; - } else { - // not the correct variable, but still a variable - self.referenced.insert(path.segments[0].ident.name); - } - } - } - - let old = self.prefer_mutable; - match expr.kind { - ExprKind::AssignOp(_, ref lhs, ref rhs) | ExprKind::Assign(ref lhs, ref rhs, _) => { - self.prefer_mutable = true; - self.visit_expr(lhs); - self.prefer_mutable = false; - self.visit_expr(rhs); - }, - ExprKind::AddrOf(BorrowKind::Ref, mutbl, ref expr) => { - if mutbl == Mutability::Mut { - self.prefer_mutable = true; - } - self.visit_expr(expr); - }, - ExprKind::Call(ref f, args) => { - self.visit_expr(f); - for expr in args { - let ty = self.cx.typeck_results().expr_ty_adjusted(expr); - self.prefer_mutable = false; - if let ty::Ref(_, _, mutbl) = *ty.kind() { - if mutbl == Mutability::Mut { - self.prefer_mutable = true; - } - } - self.visit_expr(expr); - } - }, - ExprKind::MethodCall(_, _, args, _) => { - let def_id = self.cx.typeck_results().type_dependent_def_id(expr.hir_id).unwrap(); - for (ty, expr) in self.cx.tcx.fn_sig(def_id).inputs().skip_binder().iter().zip(args) { - self.prefer_mutable = false; - if let ty::Ref(_, _, mutbl) = *ty.kind() { - if mutbl == Mutability::Mut { - self.prefer_mutable = true; - } - } - self.visit_expr(expr); - } - }, - ExprKind::Closure(_, _, body_id, ..) => { - let body = self.cx.tcx.hir().body(body_id); - self.visit_expr(&body.value); - }, - _ => walk_expr(self, expr), - } - self.prefer_mutable = old; - } - fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> { - NestedVisitorMap::None - } -} - -fn is_used_inside<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, container: &'tcx Expr<'_>) -> bool { - let def_id = match path_to_local(expr) { - Some(id) => id, - None => return false, - }; - if let Some(used_mutably) = mutated_variables(container, cx) { - if used_mutably.contains(&def_id) { - return true; - } - } - false -} - -fn is_iterator_used_after_while_let<'tcx>(cx: &LateContext<'tcx>, iter_expr: &'tcx Expr<'_>) -> bool { - let def_id = match path_to_local(iter_expr) { - Some(id) => id, - None => return false, - }; - let mut visitor = VarUsedAfterLoopVisitor { - def_id, - iter_expr_id: iter_expr.hir_id, - past_while_let: false, - var_used_after_while_let: false, - }; - if let Some(enclosing_block) = get_enclosing_block(cx, def_id) { - walk_block(&mut visitor, enclosing_block); - } - visitor.var_used_after_while_let -} - -struct VarUsedAfterLoopVisitor { - def_id: HirId, - iter_expr_id: HirId, - past_while_let: bool, - var_used_after_while_let: bool, -} - -impl<'tcx> Visitor<'tcx> for VarUsedAfterLoopVisitor { - type Map = Map<'tcx>; - - fn visit_expr(&mut self, expr: &'tcx Expr<'_>) { - if self.past_while_let { - if path_to_local_id(expr, self.def_id) { - self.var_used_after_while_let = true; - } - } else if self.iter_expr_id == expr.hir_id { - self.past_while_let = true; - } - walk_expr(self, expr); - } - fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> { - NestedVisitorMap::None - } -} - -/// Returns `true` if the type of expr is one that provides `IntoIterator` impls -/// for `&T` and `&mut T`, such as `Vec`. -#[rustfmt::skip] -fn is_ref_iterable_type(cx: &LateContext<'_>, e: &Expr<'_>) -> bool { - // no walk_ptrs_ty: calling iter() on a reference can make sense because it - // will allow further borrows afterwards - let ty = cx.typeck_results().expr_ty(e); - is_iterable_array(ty, cx) || - is_type_diagnostic_item(cx, ty, sym::vec_type) || - match_type(cx, ty, &paths::LINKED_LIST) || - is_type_diagnostic_item(cx, ty, sym::hashmap_type) || - is_type_diagnostic_item(cx, ty, sym::hashset_type) || - is_type_diagnostic_item(cx, ty, sym::vecdeque_type) || - match_type(cx, ty, &paths::BINARY_HEAP) || - match_type(cx, ty, &paths::BTREEMAP) || - match_type(cx, ty, &paths::BTREESET) -} - -fn is_iterable_array<'tcx>(ty: Ty<'tcx>, cx: &LateContext<'tcx>) -> bool { - // IntoIterator is currently only implemented for array sizes <= 32 in rustc - match ty.kind() { - ty::Array(_, n) => n - .try_eval_usize(cx.tcx, cx.param_env) - .map_or(false, |val| (0..=32).contains(&val)), - _ => false, - } -} - -/// If a block begins with a statement (possibly a `let` binding) and has an -/// expression, return it. -fn extract_expr_from_first_stmt<'tcx>(block: &Block<'tcx>) -> Option<&'tcx Expr<'tcx>> { - if block.stmts.is_empty() { - return None; - } - if let StmtKind::Local(ref local) = block.stmts[0].kind { - local.init //.map(|expr| expr) - } else { - None - } -} - -/// If a block begins with an expression (with or without semicolon), return it. -fn extract_first_expr<'tcx>(block: &Block<'tcx>) -> Option<&'tcx Expr<'tcx>> { - match block.expr { - Some(ref expr) if block.stmts.is_empty() => Some(expr), - None if !block.stmts.is_empty() => match block.stmts[0].kind { - StmtKind::Expr(ref expr) | StmtKind::Semi(ref expr) => Some(expr), - StmtKind::Local(..) | StmtKind::Item(..) => None, - }, - _ => None, - } -} - -/// Returns `true` if expr contains a single break expr without destination label -/// and -/// passed expression. The expression may be within a block. -fn is_simple_break_expr(expr: &Expr<'_>) -> bool { - match expr.kind { - ExprKind::Break(dest, ref passed_expr) if dest.label.is_none() && passed_expr.is_none() => true, - ExprKind::Block(ref b, _) => extract_first_expr(b).map_or(false, |subexpr| is_simple_break_expr(subexpr)), - _ => false, - } -} - -#[derive(Debug, PartialEq)] -enum IncrementVisitorVarState { - Initial, // Not examined yet - IncrOnce, // Incremented exactly once, may be a loop counter - DontWarn, -} - -/// Scan a for loop for variables that are incremented exactly once and not used after that. -struct IncrementVisitor<'a, 'tcx> { - cx: &'a LateContext<'tcx>, // context reference - states: FxHashMap<HirId, IncrementVisitorVarState>, // incremented variables - depth: u32, // depth of conditional expressions - done: bool, -} - -impl<'a, 'tcx> IncrementVisitor<'a, 'tcx> { - fn new(cx: &'a LateContext<'tcx>) -> Self { - Self { - cx, - states: FxHashMap::default(), - depth: 0, - done: false, - } - } - - fn into_results(self) -> impl Iterator<Item = HirId> { - self.states.into_iter().filter_map(|(id, state)| { - if state == IncrementVisitorVarState::IncrOnce { - Some(id) - } else { - None - } - }) - } -} - -impl<'a, 'tcx> Visitor<'tcx> for IncrementVisitor<'a, 'tcx> { - type Map = Map<'tcx>; - - fn visit_expr(&mut self, expr: &'tcx Expr<'_>) { - if self.done { - return; - } - - // If node is a variable - if let Some(def_id) = path_to_local(expr) { - if let Some(parent) = get_parent_expr(self.cx, expr) { - let state = self.states.entry(def_id).or_insert(IncrementVisitorVarState::Initial); - if *state == IncrementVisitorVarState::IncrOnce { - *state = IncrementVisitorVarState::DontWarn; - return; - } - - match parent.kind { - ExprKind::AssignOp(op, ref lhs, ref rhs) => { - if lhs.hir_id == expr.hir_id { - *state = if op.node == BinOpKind::Add - && is_integer_const(self.cx, rhs, 1) - && *state == IncrementVisitorVarState::Initial - && self.depth == 0 - { - IncrementVisitorVarState::IncrOnce - } else { - // Assigned some other value or assigned multiple times - IncrementVisitorVarState::DontWarn - }; - } - }, - ExprKind::Assign(ref lhs, _, _) if lhs.hir_id == expr.hir_id => { - *state = IncrementVisitorVarState::DontWarn - }, - ExprKind::AddrOf(BorrowKind::Ref, mutability, _) if mutability == Mutability::Mut => { - *state = IncrementVisitorVarState::DontWarn - }, - _ => (), - } - } - - walk_expr(self, expr); - } else if is_loop(expr) || is_conditional(expr) { - self.depth += 1; - walk_expr(self, expr); - self.depth -= 1; - } else if let ExprKind::Continue(_) = expr.kind { - self.done = true; - } else { - walk_expr(self, expr); - } - } - fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> { - NestedVisitorMap::None - } -} - -enum InitializeVisitorState<'hir> { - Initial, // Not examined yet - Declared(Symbol), // Declared but not (yet) initialized - Initialized { - name: Symbol, - initializer: &'hir Expr<'hir>, - }, - DontWarn, -} - -/// Checks whether a variable is initialized at the start of a loop and not modified -/// and used after the loop. -struct InitializeVisitor<'a, 'tcx> { - cx: &'a LateContext<'tcx>, // context reference - end_expr: &'tcx Expr<'tcx>, // the for loop. Stop scanning here. - var_id: HirId, - state: InitializeVisitorState<'tcx>, - depth: u32, // depth of conditional expressions - past_loop: bool, -} - -impl<'a, 'tcx> InitializeVisitor<'a, 'tcx> { - fn new(cx: &'a LateContext<'tcx>, end_expr: &'tcx Expr<'tcx>, var_id: HirId) -> Self { - Self { - cx, - end_expr, - var_id, - state: InitializeVisitorState::Initial, - depth: 0, - past_loop: false, - } - } - - fn get_result(&self) -> Option<(Symbol, &'tcx Expr<'tcx>)> { - if let InitializeVisitorState::Initialized { name, initializer } = self.state { - Some((name, initializer)) - } else { - None - } - } -} - -impl<'a, 'tcx> Visitor<'tcx> for InitializeVisitor<'a, 'tcx> { - type Map = Map<'tcx>; - - fn visit_stmt(&mut self, stmt: &'tcx Stmt<'_>) { - // Look for declarations of the variable - if_chain! { - if let StmtKind::Local(ref local) = stmt.kind; - if local.pat.hir_id == self.var_id; - if let PatKind::Binding(.., ident, _) = local.pat.kind; - then { - self.state = local.init.map_or(InitializeVisitorState::Declared(ident.name), |init| { - InitializeVisitorState::Initialized { - initializer: init, - name: ident.name, - } - }) - } - } - walk_stmt(self, stmt); - } - - fn visit_expr(&mut self, expr: &'tcx Expr<'_>) { - if matches!(self.state, InitializeVisitorState::DontWarn) { - return; - } - if expr.hir_id == self.end_expr.hir_id { - self.past_loop = true; - return; - } - // No need to visit expressions before the variable is - // declared - if matches!(self.state, InitializeVisitorState::Initial) { - return; - } - - // If node is the desired variable, see how it's used - if path_to_local_id(expr, self.var_id) { - if self.past_loop { - self.state = InitializeVisitorState::DontWarn; - return; - } - - if let Some(parent) = get_parent_expr(self.cx, expr) { - match parent.kind { - ExprKind::AssignOp(_, ref lhs, _) if lhs.hir_id == expr.hir_id => { - self.state = InitializeVisitorState::DontWarn; - }, - ExprKind::Assign(ref lhs, ref rhs, _) if lhs.hir_id == expr.hir_id => { - self.state = if_chain! { - if self.depth == 0; - if let InitializeVisitorState::Declared(name) - | InitializeVisitorState::Initialized { name, ..} = self.state; - then { - InitializeVisitorState::Initialized { initializer: rhs, name } - } else { - InitializeVisitorState::DontWarn - } - } - }, - ExprKind::AddrOf(BorrowKind::Ref, mutability, _) if mutability == Mutability::Mut => { - self.state = InitializeVisitorState::DontWarn - }, - _ => (), - } - } - - walk_expr(self, expr); - } else if !self.past_loop && is_loop(expr) { - self.state = InitializeVisitorState::DontWarn; - } else if is_conditional(expr) { - self.depth += 1; - walk_expr(self, expr); - self.depth -= 1; - } else { - walk_expr(self, expr); - } - } - - fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> { - NestedVisitorMap::OnlyBodies(self.cx.tcx.hir()) - } -} - -fn is_loop(expr: &Expr<'_>) -> bool { - matches!(expr.kind, ExprKind::Loop(..)) -} - -fn is_conditional(expr: &Expr<'_>) -> bool { - matches!(expr.kind, ExprKind::If(..) | ExprKind::Match(..)) -} - -fn is_nested(cx: &LateContext<'_>, match_expr: &Expr<'_>, iter_expr: &Expr<'_>) -> bool { - if_chain! { - if let Some(loop_block) = get_enclosing_block(cx, match_expr.hir_id); - let parent_node = cx.tcx.hir().get_parent_node(loop_block.hir_id); - if let Some(Node::Expr(loop_expr)) = cx.tcx.hir().find(parent_node); - then { - return is_loop_nested(cx, loop_expr, iter_expr) - } - } - false -} - -fn is_loop_nested(cx: &LateContext<'_>, loop_expr: &Expr<'_>, iter_expr: &Expr<'_>) -> bool { - let mut id = loop_expr.hir_id; - let iter_id = if let Some(id) = path_to_local(iter_expr) { - id - } else { - return true; - }; - loop { - let parent = cx.tcx.hir().get_parent_node(id); - if parent == id { - return false; - } - match cx.tcx.hir().find(parent) { - Some(Node::Expr(expr)) => { - if let ExprKind::Loop(..) = expr.kind { - return true; - }; - }, - Some(Node::Block(block)) => { - let mut block_visitor = LoopNestVisitor { - hir_id: id, - iterator: iter_id, - nesting: Unknown, - }; - walk_block(&mut block_visitor, block); - if block_visitor.nesting == RuledOut { - return false; - } - }, - Some(Node::Stmt(_)) => (), - _ => { - return false; - }, - } - id = parent; - } -} - -#[derive(PartialEq, Eq)] -enum Nesting { - Unknown, // no nesting detected yet - RuledOut, // the iterator is initialized or assigned within scope - LookFurther, // no nesting detected, no further walk required -} - -use self::Nesting::{LookFurther, RuledOut, Unknown}; - -struct LoopNestVisitor { - hir_id: HirId, - iterator: HirId, - nesting: Nesting, -} - -impl<'tcx> Visitor<'tcx> for LoopNestVisitor { - type Map = Map<'tcx>; - - fn visit_stmt(&mut self, stmt: &'tcx Stmt<'_>) { - if stmt.hir_id == self.hir_id { - self.nesting = LookFurther; - } else if self.nesting == Unknown { - walk_stmt(self, stmt); - } - } - - fn visit_expr(&mut self, expr: &'tcx Expr<'_>) { - if self.nesting != Unknown { - return; - } - if expr.hir_id == self.hir_id { - self.nesting = LookFurther; - return; - } - match expr.kind { - ExprKind::Assign(ref path, _, _) | ExprKind::AssignOp(_, ref path, _) => { - if path_to_local_id(path, self.iterator) { - self.nesting = RuledOut; - } - }, - _ => walk_expr(self, expr), - } - } - - fn visit_pat(&mut self, pat: &'tcx Pat<'_>) { - if self.nesting != Unknown { - return; - } - if let PatKind::Binding(_, id, ..) = pat.kind { - if id == self.iterator { - self.nesting = RuledOut; - return; - } - } - walk_pat(self, pat) - } - - fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> { - NestedVisitorMap::None - } -} - -fn check_infinite_loop<'tcx>(cx: &LateContext<'tcx>, cond: &'tcx Expr<'_>, expr: &'tcx Expr<'_>) { - if constant(cx, cx.typeck_results(), cond).is_some() { - // A pure constant condition (e.g., `while false`) is not linted. - return; - } - - let mut var_visitor = VarCollectorVisitor { - cx, - ids: FxHashSet::default(), - def_ids: FxHashMap::default(), - skip: false, - }; - var_visitor.visit_expr(cond); - if var_visitor.skip { - return; - } - let used_in_condition = &var_visitor.ids; - let no_cond_variable_mutated = if let Some(used_mutably) = mutated_variables(expr, cx) { - used_in_condition.is_disjoint(&used_mutably) - } else { - return; - }; - let mutable_static_in_cond = var_visitor.def_ids.iter().any(|(_, v)| *v); - - let mut has_break_or_return_visitor = HasBreakOrReturnVisitor { - has_break_or_return: false, - }; - has_break_or_return_visitor.visit_expr(expr); - let has_break_or_return = has_break_or_return_visitor.has_break_or_return; - - if no_cond_variable_mutated && !mutable_static_in_cond { - span_lint_and_then( - cx, - WHILE_IMMUTABLE_CONDITION, - cond.span, - "variables in the condition are not mutated in the loop body", - |diag| { - diag.note("this may lead to an infinite or to a never running loop"); - - if has_break_or_return { - diag.note("this loop contains `return`s or `break`s"); - diag.help("rewrite it as `if cond { loop { } }`"); - } - }, - ); - } -} - -struct HasBreakOrReturnVisitor { - has_break_or_return: bool, -} - -impl<'tcx> Visitor<'tcx> for HasBreakOrReturnVisitor { - type Map = Map<'tcx>; - - fn visit_expr(&mut self, expr: &'tcx Expr<'_>) { - if self.has_break_or_return { - return; - } - - match expr.kind { - ExprKind::Ret(_) | ExprKind::Break(_, _) => { - self.has_break_or_return = true; - return; - }, - _ => {}, - } - - walk_expr(self, expr); - } - - fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> { - NestedVisitorMap::None - } -} - -/// Collects the set of variables in an expression -/// Stops analysis if a function call is found -/// Note: In some cases such as `self`, there are no mutable annotation, -/// All variables definition IDs are collected -struct VarCollectorVisitor<'a, 'tcx> { - cx: &'a LateContext<'tcx>, - ids: FxHashSet<HirId>, - def_ids: FxHashMap<def_id::DefId, bool>, - skip: bool, -} - -impl<'a, 'tcx> VarCollectorVisitor<'a, 'tcx> { - fn insert_def_id(&mut self, ex: &'tcx Expr<'_>) { - if_chain! { - if let ExprKind::Path(ref qpath) = ex.kind; - if let QPath::Resolved(None, _) = *qpath; - let res = self.cx.qpath_res(qpath, ex.hir_id); - then { - match res { - Res::Local(hir_id) => { - self.ids.insert(hir_id); - }, - Res::Def(DefKind::Static, def_id) => { - let mutable = self.cx.tcx.is_mutable_static(def_id); - self.def_ids.insert(def_id, mutable); - }, - _ => {}, - } - } - } - } -} - -impl<'a, 'tcx> Visitor<'tcx> for VarCollectorVisitor<'a, 'tcx> { - type Map = Map<'tcx>; - - fn visit_expr(&mut self, ex: &'tcx Expr<'_>) { - match ex.kind { - ExprKind::Path(_) => self.insert_def_id(ex), - // If there is any function/method call… we just stop analysis - ExprKind::Call(..) | ExprKind::MethodCall(..) => self.skip = true, - - _ => walk_expr(self, ex), - } - } - - fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> { - NestedVisitorMap::None - } -} - -const NEEDLESS_COLLECT_MSG: &str = "avoid using `collect()` when not needed"; - -fn check_needless_collect<'tcx>(expr: &'tcx Expr<'_>, cx: &LateContext<'tcx>) { - check_needless_collect_direct_usage(expr, cx); - check_needless_collect_indirect_usage(expr, cx); -} -fn check_needless_collect_direct_usage<'tcx>(expr: &'tcx Expr<'_>, cx: &LateContext<'tcx>) { - if_chain! { - if let ExprKind::MethodCall(ref method, _, ref args, _) = expr.kind; - if let ExprKind::MethodCall(ref chain_method, _, _, _) = args[0].kind; - if chain_method.ident.name == sym!(collect) && match_trait_method(cx, &args[0], &paths::ITERATOR); - if let Some(ref generic_args) = chain_method.args; - if let Some(GenericArg::Type(ref ty)) = generic_args.args.get(0); - then { - let ty = cx.typeck_results().node_type(ty.hir_id); - if is_type_diagnostic_item(cx, ty, sym::vec_type) || - is_type_diagnostic_item(cx, ty, sym::vecdeque_type) || - match_type(cx, ty, &paths::BTREEMAP) || - is_type_diagnostic_item(cx, ty, sym::hashmap_type) { - if method.ident.name == sym!(len) { - let span = shorten_needless_collect_span(expr); - span_lint_and_sugg( - cx, - NEEDLESS_COLLECT, - span, - NEEDLESS_COLLECT_MSG, - "replace with", - "count()".to_string(), - Applicability::MachineApplicable, - ); - } - if method.ident.name == sym!(is_empty) { - let span = shorten_needless_collect_span(expr); - span_lint_and_sugg( - cx, - NEEDLESS_COLLECT, - span, - NEEDLESS_COLLECT_MSG, - "replace with", - "next().is_none()".to_string(), - Applicability::MachineApplicable, - ); - } - if method.ident.name == sym!(contains) { - let contains_arg = snippet(cx, args[1].span, "??"); - let span = shorten_needless_collect_span(expr); - span_lint_and_then( - cx, - NEEDLESS_COLLECT, - span, - NEEDLESS_COLLECT_MSG, - |diag| { - let (arg, pred) = contains_arg - .strip_prefix('&') - .map_or(("&x", &*contains_arg), |s| ("x", s)); - diag.span_suggestion( - span, - "replace with", - format!( - "any(|{}| x == {})", - arg, pred - ), - Applicability::MachineApplicable, - ); - } - ); - } - } - } - } -} - -fn check_needless_collect_indirect_usage<'tcx>(expr: &'tcx Expr<'_>, cx: &LateContext<'tcx>) { - if let ExprKind::Block(ref block, _) = expr.kind { - for ref stmt in block.stmts { - if_chain! { - if let StmtKind::Local( - Local { pat: Pat { hir_id: pat_id, kind: PatKind::Binding(_, _, ident, .. ), .. }, - init: Some(ref init_expr), .. } - ) = stmt.kind; - if let ExprKind::MethodCall(ref method_name, _, &[ref iter_source], ..) = init_expr.kind; - if method_name.ident.name == sym!(collect) && match_trait_method(cx, &init_expr, &paths::ITERATOR); - if let Some(ref generic_args) = method_name.args; - if let Some(GenericArg::Type(ref ty)) = generic_args.args.get(0); - if let ty = cx.typeck_results().node_type(ty.hir_id); - if is_type_diagnostic_item(cx, ty, sym::vec_type) || - is_type_diagnostic_item(cx, ty, sym::vecdeque_type) || - match_type(cx, ty, &paths::LINKED_LIST); - if let Some(iter_calls) = detect_iter_and_into_iters(block, *ident); - if iter_calls.len() == 1; - then { - let mut used_count_visitor = UsedCountVisitor { - cx, - id: *pat_id, - count: 0, - }; - walk_block(&mut used_count_visitor, block); - if used_count_visitor.count > 1 { - return; - } - - // Suggest replacing iter_call with iter_replacement, and removing stmt - let iter_call = &iter_calls[0]; - span_lint_and_then( - cx, - NEEDLESS_COLLECT, - stmt.span.until(iter_call.span), - NEEDLESS_COLLECT_MSG, - |diag| { - let iter_replacement = format!("{}{}", Sugg::hir(cx, iter_source, ".."), iter_call.get_iter_method(cx)); - diag.multipart_suggestion( - iter_call.get_suggestion_text(), - vec![ - (stmt.span, String::new()), - (iter_call.span, iter_replacement) - ], - Applicability::MachineApplicable,// MaybeIncorrect, - ).emit(); - }, - ); - } - } - } - } -} - -struct IterFunction { - func: IterFunctionKind, - span: Span, -} -impl IterFunction { - fn get_iter_method(&self, cx: &LateContext<'_>) -> String { - match &self.func { - IterFunctionKind::IntoIter => String::new(), - IterFunctionKind::Len => String::from(".count()"), - IterFunctionKind::IsEmpty => String::from(".next().is_none()"), - IterFunctionKind::Contains(span) => { - let s = snippet(cx, *span, ".."); - if let Some(stripped) = s.strip_prefix('&') { - format!(".any(|x| x == {})", stripped) - } else { - format!(".any(|x| x == *{})", s) - } - }, - } - } - fn get_suggestion_text(&self) -> &'static str { - match &self.func { - IterFunctionKind::IntoIter => { - "Use the original Iterator instead of collecting it and then producing a new one" - }, - IterFunctionKind::Len => { - "Take the original Iterator's count instead of collecting it and finding the length" - }, - IterFunctionKind::IsEmpty => { - "Check if the original Iterator has anything instead of collecting it and seeing if it's empty" - }, - IterFunctionKind::Contains(_) => { - "Check if the original Iterator contains an element instead of collecting then checking" - }, - } - } -} -enum IterFunctionKind { - IntoIter, - Len, - IsEmpty, - Contains(Span), -} - -struct IterFunctionVisitor { - uses: Vec<IterFunction>, - seen_other: bool, - target: Ident, -} -impl<'tcx> Visitor<'tcx> for IterFunctionVisitor { - fn visit_expr(&mut self, expr: &'tcx Expr<'tcx>) { - // Check function calls on our collection - if_chain! { - if let ExprKind::MethodCall(method_name, _, ref args, _) = &expr.kind; - if let Some(Expr { kind: ExprKind::Path(QPath::Resolved(_, ref path)), .. }) = args.get(0); - if let &[name] = &path.segments; - if name.ident == self.target; - then { - let len = sym!(len); - let is_empty = sym!(is_empty); - let contains = sym!(contains); - match method_name.ident.name { - sym::into_iter => self.uses.push( - IterFunction { func: IterFunctionKind::IntoIter, span: expr.span } - ), - name if name == len => self.uses.push( - IterFunction { func: IterFunctionKind::Len, span: expr.span } - ), - name if name == is_empty => self.uses.push( - IterFunction { func: IterFunctionKind::IsEmpty, span: expr.span } - ), - name if name == contains => self.uses.push( - IterFunction { func: IterFunctionKind::Contains(args[1].span), span: expr.span } - ), - _ => self.seen_other = true, - } - return - } - } - // Check if the collection is used for anything else - if_chain! { - if let Expr { kind: ExprKind::Path(QPath::Resolved(_, ref path)), .. } = expr; - if let &[name] = &path.segments; - if name.ident == self.target; - then { - self.seen_other = true; - } else { - walk_expr(self, expr); - } - } - } - - type Map = Map<'tcx>; - fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> { - NestedVisitorMap::None - } -} - -struct UsedCountVisitor<'a, 'tcx> { - cx: &'a LateContext<'tcx>, - id: HirId, - count: usize, -} - -impl<'a, 'tcx> Visitor<'tcx> for UsedCountVisitor<'a, 'tcx> { - type Map = Map<'tcx>; - - fn visit_expr(&mut self, expr: &'tcx Expr<'_>) { - if path_to_local_id(expr, self.id) { - self.count += 1; - } else { - walk_expr(self, expr); - } - } - - fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> { - NestedVisitorMap::OnlyBodies(self.cx.tcx.hir()) - } -} - -/// Detect the occurrences of calls to `iter` or `into_iter` for the -/// given identifier -fn detect_iter_and_into_iters<'tcx>(block: &'tcx Block<'tcx>, identifier: Ident) -> Option<Vec<IterFunction>> { - let mut visitor = IterFunctionVisitor { - uses: Vec::new(), - target: identifier, - seen_other: false, - }; - visitor.visit_block(block); - if visitor.seen_other { - None - } else { - Some(visitor.uses) - } -} - -fn shorten_needless_collect_span(expr: &Expr<'_>) -> Span { - if_chain! { - if let ExprKind::MethodCall(.., args, _) = &expr.kind; - if let ExprKind::MethodCall(_, span, ..) = &args[0].kind; - then { - return expr.span.with_lo(span.lo()); - } - } - unreachable!(); -} diff --git a/src/tools/clippy/clippy_lints/src/loops/empty_loop.rs b/src/tools/clippy/clippy_lints/src/loops/empty_loop.rs new file mode 100644 index 0000000000000..dda09fecdf90f --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/loops/empty_loop.rs @@ -0,0 +1,18 @@ +use super::EMPTY_LOOP; +use clippy_utils::diagnostics::span_lint_and_help; +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<'_>) { + 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) { + "you should either use `panic!()` or add a call pausing or sleeping the thread to the loop body" + } else { + "you should either use `panic!()` or add `std::thread::sleep(..);` to the loop body" + }; + span_lint_and_help(cx, EMPTY_LOOP, expr.span, msg, None, help); + } +} diff --git a/src/tools/clippy/clippy_lints/src/loops/explicit_counter_loop.rs b/src/tools/clippy/clippy_lints/src/loops/explicit_counter_loop.rs new file mode 100644 index 0000000000000..98e60f7ed85cf --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/loops/explicit_counter_loop.rs @@ -0,0 +1,60 @@ +use super::{ + get_span_of_entire_for_loop, make_iterator_snippet, IncrementVisitor, InitializeVisitor, EXPLICIT_COUNTER_LOOP, +}; +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::source::snippet_with_applicability; +use clippy_utils::{get_enclosing_block, is_integer_const}; +use if_chain::if_chain; +use rustc_errors::Applicability; +use rustc_hir::intravisit::{walk_block, walk_expr}; +use rustc_hir::{Expr, Pat}; +use rustc_lint::LateContext; + +// To trigger the EXPLICIT_COUNTER_LOOP lint, a variable must be +// incremented exactly once in the loop body, and initialized to zero +// at the start of the loop. +pub(super) fn check<'tcx>( + cx: &LateContext<'tcx>, + pat: &'tcx Pat<'_>, + arg: &'tcx Expr<'_>, + body: &'tcx Expr<'_>, + expr: &'tcx Expr<'_>, +) { + // Look for variables that are incremented once per loop iteration. + let mut increment_visitor = IncrementVisitor::new(cx); + walk_expr(&mut increment_visitor, body); + + // For each candidate, check the parent block to see if + // it's initialized to zero at the start of the loop. + if let Some(block) = get_enclosing_block(cx, expr.hir_id) { + for id in increment_visitor.into_results() { + let mut initialize_visitor = InitializeVisitor::new(cx, expr, id); + walk_block(&mut initialize_visitor, block); + + if_chain! { + if let Some((name, initializer)) = initialize_visitor.get_result(); + if is_integer_const(cx, initializer, 0); + then { + let mut applicability = Applicability::MachineApplicable; + + let for_span = get_span_of_entire_for_loop(expr); + + span_lint_and_sugg( + cx, + EXPLICIT_COUNTER_LOOP, + for_span.with_hi(arg.span.hi()), + &format!("the variable `{}` is used as a loop counter", name), + "consider using", + format!( + "for ({}, {}) in {}.enumerate()", + name, + snippet_with_applicability(cx, pat.span, "item", &mut applicability), + make_iterator_snippet(cx, arg, &mut applicability), + ), + applicability, + ); + } + } + } + } +} 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 new file mode 100644 index 0000000000000..4871a03118739 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/loops/explicit_into_iter_loop.rs @@ -0,0 +1,28 @@ +use super::EXPLICIT_INTO_ITER_LOOP; +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::source::snippet_with_applicability; +use rustc_errors::Applicability; +use rustc_hir::Expr; +use rustc_lint::LateContext; +use rustc_middle::ty::TyS; + +pub(super) fn check(cx: &LateContext<'_>, args: &'hir [Expr<'hir>], arg: &Expr<'_>) { + let receiver_ty = cx.typeck_results().expr_ty(&args[0]); + let receiver_ty_adjusted = cx.typeck_results().expr_ty_adjusted(&args[0]); + if !TyS::same_type(receiver_ty, receiver_ty_adjusted) { + return; + } + + let mut applicability = Applicability::MachineApplicable; + let object = snippet_with_applicability(cx, args[0].span, "_", &mut applicability); + span_lint_and_sugg( + cx, + EXPLICIT_INTO_ITER_LOOP, + arg.span, + "it is more concise to loop over containers instead of using explicit \ + iteration methods", + "to write this more concisely, try", + object.to_string(), + applicability, + ); +} diff --git a/src/tools/clippy/clippy_lints/src/loops/explicit_iter_loop.rs b/src/tools/clippy/clippy_lints/src/loops/explicit_iter_loop.rs new file mode 100644 index 0000000000000..92aa2beb66d45 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/loops/explicit_iter_loop.rs @@ -0,0 +1,75 @@ +use super::EXPLICIT_ITER_LOOP; +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::source::snippet_with_applicability; +use clippy_utils::ty::{is_type_diagnostic_item, match_type}; +use clippy_utils::{match_trait_method, paths}; +use rustc_errors::Applicability; +use rustc_hir::{Expr, Mutability}; +use rustc_lint::LateContext; +use rustc_middle::ty::{self, Ty, TyS}; +use rustc_span::sym; + +pub(super) fn check(cx: &LateContext<'_>, args: &[Expr<'_>], arg: &Expr<'_>, method_name: &str) { + let should_lint = match method_name { + "iter" | "iter_mut" => is_ref_iterable_type(cx, &args[0]), + "into_iter" if match_trait_method(cx, arg, &paths::INTO_ITERATOR) => { + let receiver_ty = cx.typeck_results().expr_ty(&args[0]); + let receiver_ty_adjusted = cx.typeck_results().expr_ty_adjusted(&args[0]); + let ref_receiver_ty = cx.tcx.mk_ref( + cx.tcx.lifetimes.re_erased, + ty::TypeAndMut { + ty: receiver_ty, + mutbl: Mutability::Not, + }, + ); + TyS::same_type(receiver_ty_adjusted, ref_receiver_ty) + }, + _ => false, + }; + + if !should_lint { + return; + } + + let mut applicability = Applicability::MachineApplicable; + let object = snippet_with_applicability(cx, args[0].span, "_", &mut applicability); + let muta = if method_name == "iter_mut" { "mut " } else { "" }; + span_lint_and_sugg( + cx, + EXPLICIT_ITER_LOOP, + arg.span, + "it is more concise to loop over references to containers instead of using explicit \ + iteration methods", + "to write this more concisely, try", + format!("&{}{}", muta, object), + applicability, + ) +} + +/// Returns `true` if the type of expr is one that provides `IntoIterator` impls +/// for `&T` and `&mut T`, such as `Vec`. +#[rustfmt::skip] +fn is_ref_iterable_type(cx: &LateContext<'_>, e: &Expr<'_>) -> bool { + // no walk_ptrs_ty: calling iter() on a reference can make sense because it + // will allow further borrows afterwards + let ty = cx.typeck_results().expr_ty(e); + is_iterable_array(ty, cx) || + is_type_diagnostic_item(cx, ty, sym::vec_type) || + match_type(cx, ty, &paths::LINKED_LIST) || + is_type_diagnostic_item(cx, ty, sym::hashmap_type) || + is_type_diagnostic_item(cx, ty, sym::hashset_type) || + is_type_diagnostic_item(cx, ty, sym::vecdeque_type) || + match_type(cx, ty, &paths::BINARY_HEAP) || + match_type(cx, ty, &paths::BTREEMAP) || + match_type(cx, ty, &paths::BTREESET) +} + +fn is_iterable_array<'tcx>(ty: Ty<'tcx>, cx: &LateContext<'tcx>) -> bool { + // IntoIterator is currently only implemented for array sizes <= 32 in rustc + match ty.kind() { + ty::Array(_, n) => n + .try_eval_usize(cx.tcx, cx.param_env) + .map_or(false, |val| (0..=32).contains(&val)), + _ => false, + } +} diff --git a/src/tools/clippy/clippy_lints/src/loops/for_kv_map.rs b/src/tools/clippy/clippy_lints/src/loops/for_kv_map.rs new file mode 100644 index 0000000000000..666b8c58728c2 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/loops/for_kv_map.rs @@ -0,0 +1,74 @@ +use super::FOR_KV_MAP; +use clippy_utils::diagnostics::{multispan_sugg, span_lint_and_then}; +use clippy_utils::source::snippet; +use clippy_utils::ty::{is_type_diagnostic_item, match_type}; +use clippy_utils::visitors::LocalUsedVisitor; +use clippy_utils::{paths, sugg}; +use rustc_hir::{BorrowKind, Expr, ExprKind, Mutability, Pat, PatKind}; +use rustc_lint::LateContext; +use rustc_middle::ty; +use rustc_span::sym; + +/// Checks for the `FOR_KV_MAP` lint. +pub(super) fn check<'tcx>( + cx: &LateContext<'tcx>, + pat: &'tcx Pat<'_>, + arg: &'tcx Expr<'_>, + body: &'tcx Expr<'_>, + expr: &'tcx Expr<'_>, +) { + let pat_span = pat.span; + + if let PatKind::Tuple(pat, _) = pat.kind { + if pat.len() == 2 { + let arg_span = arg.span; + let (new_pat_span, kind, ty, mutbl) = match *cx.typeck_results().expr_ty(arg).kind() { + ty::Ref(_, ty, mutbl) => match (&pat[0].kind, &pat[1].kind) { + (key, _) if pat_is_wild(cx, key, body) => (pat[1].span, "value", ty, mutbl), + (_, value) if pat_is_wild(cx, value, body) => (pat[0].span, "key", ty, Mutability::Not), + _ => return, + }, + _ => return, + }; + let mutbl = match mutbl { + Mutability::Not => "", + Mutability::Mut => "_mut", + }; + let arg = match arg.kind { + ExprKind::AddrOf(BorrowKind::Ref, _, expr) => expr, + _ => arg, + }; + + if is_type_diagnostic_item(cx, ty, sym::hashmap_type) || match_type(cx, ty, &paths::BTREEMAP) { + span_lint_and_then( + cx, + FOR_KV_MAP, + expr.span, + &format!("you seem to want to iterate on a map's {}s", kind), + |diag| { + let map = sugg::Sugg::hir(cx, arg, "map"); + multispan_sugg( + diag, + "use the corresponding method", + vec![ + (pat_span, snippet(cx, new_pat_span, kind).into_owned()), + (arg_span, format!("{}.{}s{}()", map.maybe_par(), kind, mutbl)), + ], + ); + }, + ); + } + } + } +} + +/// Returns `true` if the pattern is a `PatWild` or an ident prefixed with `_`. +fn pat_is_wild<'tcx>(cx: &LateContext<'tcx>, pat: &'tcx PatKind<'_>, body: &'tcx Expr<'_>) -> bool { + match *pat { + PatKind::Wild => true, + PatKind::Binding(_, id, ident, None) if ident.as_str().starts_with('_') => { + !LocalUsedVisitor::new(cx, id).check_expr(body) + }, + _ => false, + } +} diff --git a/src/tools/clippy/clippy_lints/src/loops/for_loops_over_fallibles.rs b/src/tools/clippy/clippy_lints/src/loops/for_loops_over_fallibles.rs new file mode 100644 index 0000000000000..d49b0517dcf3e --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/loops/for_loops_over_fallibles.rs @@ -0,0 +1,47 @@ +use super::FOR_LOOPS_OVER_FALLIBLES; +use clippy_utils::diagnostics::span_lint_and_help; +use clippy_utils::source::snippet; +use clippy_utils::ty::is_type_diagnostic_item; +use rustc_hir::{Expr, Pat}; +use rustc_lint::LateContext; +use rustc_span::symbol::sym; + +/// Checks for `for` loops over `Option`s and `Result`s. +pub(super) fn check(cx: &LateContext<'_>, pat: &Pat<'_>, arg: &Expr<'_>) { + let ty = cx.typeck_results().expr_ty(arg); + if is_type_diagnostic_item(cx, ty, sym::option_type) { + span_lint_and_help( + cx, + FOR_LOOPS_OVER_FALLIBLES, + arg.span, + &format!( + "for loop over `{0}`, which is an `Option`. This is more readably written as an \ + `if let` statement", + snippet(cx, arg.span, "_") + ), + None, + &format!( + "consider replacing `for {0} in {1}` with `if let Some({0}) = {1}`", + snippet(cx, pat.span, "_"), + snippet(cx, arg.span, "_") + ), + ); + } else if is_type_diagnostic_item(cx, ty, sym::result_type) { + span_lint_and_help( + cx, + FOR_LOOPS_OVER_FALLIBLES, + arg.span, + &format!( + "for loop over `{0}`, which is a `Result`. This is more readably written as an \ + `if let` statement", + snippet(cx, arg.span, "_") + ), + None, + &format!( + "consider replacing `for {0} in {1}` with `if let Ok({0}) = {1}`", + snippet(cx, pat.span, "_"), + snippet(cx, arg.span, "_") + ), + ); + } +} diff --git a/src/tools/clippy/clippy_lints/src/loops/iter_next_loop.rs b/src/tools/clippy/clippy_lints/src/loops/iter_next_loop.rs new file mode 100644 index 0000000000000..9148fbfd497af --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/loops/iter_next_loop.rs @@ -0,0 +1,21 @@ +use super::ITER_NEXT_LOOP; +use clippy_utils::diagnostics::span_lint; +use clippy_utils::is_trait_method; +use rustc_hir::Expr; +use rustc_lint::LateContext; +use rustc_span::sym; + +pub(super) fn check(cx: &LateContext<'_>, arg: &Expr<'_>, expr: &Expr<'_>) -> bool { + if is_trait_method(cx, arg, sym::Iterator) { + span_lint( + cx, + ITER_NEXT_LOOP, + expr.span, + "you are iterating over `Iterator::next()` which is an Option; this will compile but is \ + probably not what you want", + ); + true + } else { + false + } +} diff --git a/src/tools/clippy/clippy_lints/src/loops/manual_flatten.rs b/src/tools/clippy/clippy_lints/src/loops/manual_flatten.rs new file mode 100644 index 0000000000000..94743cfcf4657 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/loops/manual_flatten.rs @@ -0,0 +1,87 @@ +use super::utils::make_iterator_snippet; +use super::MANUAL_FLATTEN; +use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::{is_ok_ctor, is_some_ctor, path_to_local_id}; +use if_chain::if_chain; +use rustc_errors::Applicability; +use rustc_hir::{Expr, ExprKind, MatchSource, Pat, PatKind, QPath, StmtKind}; +use rustc_lint::LateContext; +use rustc_middle::ty; +use rustc_span::source_map::Span; + +/// Check for unnecessary `if let` usage in a for loop where only the `Some` or `Ok` variant of the +/// iterator element is used. +pub(super) fn check<'tcx>( + cx: &LateContext<'tcx>, + pat: &'tcx Pat<'_>, + arg: &'tcx Expr<'_>, + body: &'tcx Expr<'_>, + span: Span, +) { + if let ExprKind::Block(block, _) = body.kind { + // Ensure the `if let` statement is the only expression or statement in the for-loop + let inner_expr = if block.stmts.len() == 1 && block.expr.is_none() { + let match_stmt = &block.stmts[0]; + if let StmtKind::Semi(inner_expr) = match_stmt.kind { + Some(inner_expr) + } else { + None + } + } else if block.stmts.is_empty() { + block.expr + } else { + None + }; + + if_chain! { + if let Some(inner_expr) = inner_expr; + if let ExprKind::Match( + match_expr, match_arms, MatchSource::IfLetDesugar{ contains_else_clause: false } + ) = inner_expr.kind; + // Ensure match_expr in `if let` statement is the same as the pat from the for-loop + if let PatKind::Binding(_, pat_hir_id, _, _) = pat.kind; + if path_to_local_id(match_expr, pat_hir_id); + // Ensure the `if let` statement is for the `Some` variant of `Option` or the `Ok` variant of `Result` + if let PatKind::TupleStruct(QPath::Resolved(None, path), _, _) = match_arms[0].pat.kind; + let some_ctor = is_some_ctor(cx, path.res); + let ok_ctor = is_ok_ctor(cx, path.res); + if some_ctor || ok_ctor; + then { + let if_let_type = if some_ctor { "Some" } else { "Ok" }; + // Prepare the error message + let msg = format!("unnecessary `if let` since only the `{}` variant of the iterator element is used", if_let_type); + + // Prepare the help message + let mut applicability = Applicability::MaybeIncorrect; + let arg_snippet = make_iterator_snippet(cx, arg, &mut applicability); + let copied = match cx.typeck_results().expr_ty(match_expr).kind() { + ty::Ref(_, inner, _) => match inner.kind() { + ty::Ref(..) => ".copied()", + _ => "" + } + _ => "" + }; + + span_lint_and_then( + cx, + MANUAL_FLATTEN, + span, + &msg, + |diag| { + let sugg = format!("{}{}.flatten()", arg_snippet, copied); + diag.span_suggestion( + arg.span, + "try", + sugg, + Applicability::MaybeIncorrect, + ); + diag.span_help( + inner_expr.span, + "...and remove the `if let` statement in the for loop", + ); + } + ); + } + } + } +} diff --git a/src/tools/clippy/clippy_lints/src/loops/manual_memcpy.rs b/src/tools/clippy/clippy_lints/src/loops/manual_memcpy.rs new file mode 100644 index 0000000000000..c91fe88757ec7 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/loops/manual_memcpy.rs @@ -0,0 +1,455 @@ +use super::{get_span_of_entire_for_loop, 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::{get_enclosing_block, higher, path_to_local, sugg}; +use if_chain::if_chain; +use rustc_ast::ast; +use rustc_errors::Applicability; +use rustc_hir::intravisit::walk_block; +use rustc_hir::{BinOpKind, Block, Expr, ExprKind, HirId, Pat, PatKind, StmtKind}; +use rustc_lint::LateContext; +use rustc_middle::ty::{self, Ty}; +use rustc_span::symbol::sym; +use std::iter::Iterator; + +/// Checks for for loops that sequentially copy items from one slice-like +/// object to another. +pub(super) fn check<'tcx>( + cx: &LateContext<'tcx>, + pat: &'tcx Pat<'_>, + arg: &'tcx Expr<'_>, + body: &'tcx Expr<'_>, + expr: &'tcx Expr<'_>, +) -> bool { + if let Some(higher::Range { + start: Some(start), + end: Some(end), + limits, + }) = higher::range(arg) + { + // the var must be a single name + if let PatKind::Binding(_, canonical_id, _, _) = pat.kind { + let mut starts = vec![Start { + id: canonical_id, + kind: StartKind::Range, + }]; + + // This is one of few ways to return different iterators + // derived from: https://stackoverflow.com/questions/29760668/conditionally-iterate-over-one-of-several-possible-iterators/52064434#52064434 + let mut iter_a = None; + let mut iter_b = None; + + if let ExprKind::Block(block, _) = body.kind { + if let Some(loop_counters) = get_loop_counters(cx, block, expr) { + starts.extend(loop_counters); + } + iter_a = Some(get_assignments(block, &starts)); + } else { + iter_b = Some(get_assignment(body)); + } + + let assignments = iter_a.into_iter().flatten().chain(iter_b.into_iter()); + + let big_sugg = assignments + // The only statements in the for loops can be indexed assignments from + // indexed retrievals (except increments of loop counters). + .map(|o| { + o.and_then(|(lhs, rhs)| { + let rhs = fetch_cloned_expr(rhs); + 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((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 }, + IndexExpr { base: base_right, idx: start_right, idx_offset: offset_right })) + } else { + None + } + } + }) + }) + .map(|o| o.map(|(dst, src)| build_manual_memcpy_suggestion(cx, start, end, limits, &dst, &src))) + .collect::<Option<Vec<_>>>() + .filter(|v| !v.is_empty()) + .map(|v| v.join("\n ")); + + if let Some(big_sugg) = big_sugg { + span_lint_and_sugg( + cx, + MANUAL_MEMCPY, + get_span_of_entire_for_loop(expr), + "it looks like you're manually copying between slices", + "try replacing the loop by", + big_sugg, + Applicability::Unspecified, + ); + return true; + } + } + } + false +} + +fn build_manual_memcpy_suggestion<'tcx>( + cx: &LateContext<'tcx>, + start: &Expr<'_>, + end: &Expr<'_>, + limits: ast::RangeLimits, + dst: &IndexExpr<'_>, + src: &IndexExpr<'_>, +) -> String { + fn print_offset(offset: MinifyingSugg<'static>) -> MinifyingSugg<'static> { + if offset.as_str() == "0" { + sugg::EMPTY.into() + } else { + offset + } + } + + let print_limit = |end: &Expr<'_>, end_str: &str, base: &Expr<'_>, sugg: MinifyingSugg<'static>| { + if_chain! { + if let ExprKind::MethodCall(method, _, len_args, _) = end.kind; + if method.ident.name == sym!(len); + if len_args.len() == 1; + if let Some(arg) = len_args.get(0); + if path_to_local(arg) == path_to_local(base); + then { + if sugg.as_str() == end_str { + sugg::EMPTY.into() + } else { + sugg + } + } else { + match limits { + ast::RangeLimits::Closed => { + sugg + &sugg::ONE.into() + }, + ast::RangeLimits::HalfOpen => sugg, + } + } + } + }; + + let start_str = Sugg::hir(cx, start, "").into(); + let end_str: MinifyingSugg<'_> = Sugg::hir(cx, end, "").into(); + + let print_offset_and_limit = |idx_expr: &IndexExpr<'_>| match idx_expr.idx { + StartKind::Range => ( + print_offset(apply_offset(&start_str, &idx_expr.idx_offset)).into_sugg(), + print_limit( + end, + end_str.as_str(), + idx_expr.base, + apply_offset(&end_str, &idx_expr.idx_offset), + ) + .into_sugg(), + ), + StartKind::Counter { initializer } => { + let counter_start = Sugg::hir(cx, initializer, "").into(); + ( + print_offset(apply_offset(&counter_start, &idx_expr.idx_offset)).into_sugg(), + print_limit( + end, + end_str.as_str(), + idx_expr.base, + apply_offset(&end_str, &idx_expr.idx_offset) + &counter_start - &start_str, + ) + .into_sugg(), + ) + }, + }; + + let (dst_offset, dst_limit) = print_offset_and_limit(dst); + let (src_offset, src_limit) = print_offset_and_limit(src); + + let dst_base_str = snippet(cx, dst.base.span, "???"); + let src_base_str = snippet(cx, src.base.span, "???"); + + let dst = if dst_offset == sugg::EMPTY && dst_limit == sugg::EMPTY { + dst_base_str + } else { + format!( + "{}[{}..{}]", + dst_base_str, + dst_offset.maybe_par(), + dst_limit.maybe_par() + ) + .into() + }; + + format!( + "{}.clone_from_slice(&{}[{}..{}]);", + dst, + src_base_str, + src_offset.maybe_par(), + src_limit.maybe_par() + ) +} + +/// a wrapper of `Sugg`. Besides what `Sugg` do, this removes unnecessary `0`; +/// and also, it avoids subtracting a variable from the same one by replacing it with `0`. +/// it exists for the convenience of the overloaded operators while normal functions can do the +/// same. +#[derive(Clone)] +struct MinifyingSugg<'a>(Sugg<'a>); + +impl<'a> MinifyingSugg<'a> { + fn as_str(&self) -> &str { + // HACK: Don't sync to Clippy! Required because something with the `or_patterns` feature + // changed and this would now require parentheses. + match &self.0 { + Sugg::NonParen(s) | Sugg::MaybeParen(s) | Sugg::BinOp(_, s) => s.as_ref(), + } + } + + fn into_sugg(self) -> Sugg<'a> { + self.0 + } +} + +impl<'a> From<Sugg<'a>> for MinifyingSugg<'a> { + fn from(sugg: Sugg<'a>) -> Self { + Self(sugg) + } +} + +impl std::ops::Add for &MinifyingSugg<'static> { + type Output = MinifyingSugg<'static>; + fn add(self, rhs: &MinifyingSugg<'static>) -> MinifyingSugg<'static> { + match (self.as_str(), rhs.as_str()) { + ("0", _) => rhs.clone(), + (_, "0") => self.clone(), + (_, _) => (&self.0 + &rhs.0).into(), + } + } +} + +impl std::ops::Sub for &MinifyingSugg<'static> { + type Output = MinifyingSugg<'static>; + fn sub(self, rhs: &MinifyingSugg<'static>) -> MinifyingSugg<'static> { + match (self.as_str(), rhs.as_str()) { + (_, "0") => self.clone(), + ("0", _) => (-rhs.0.clone()).into(), + (x, y) if x == y => sugg::ZERO.into(), + (_, _) => (&self.0 - &rhs.0).into(), + } + } +} + +impl std::ops::Add<&MinifyingSugg<'static>> for MinifyingSugg<'static> { + type Output = MinifyingSugg<'static>; + fn add(self, rhs: &MinifyingSugg<'static>) -> MinifyingSugg<'static> { + match (self.as_str(), rhs.as_str()) { + ("0", _) => rhs.clone(), + (_, "0") => self, + (_, _) => (self.0 + &rhs.0).into(), + } + } +} + +impl std::ops::Sub<&MinifyingSugg<'static>> for MinifyingSugg<'static> { + type Output = MinifyingSugg<'static>; + fn sub(self, rhs: &MinifyingSugg<'static>) -> MinifyingSugg<'static> { + match (self.as_str(), rhs.as_str()) { + (_, "0") => self, + ("0", _) => (-rhs.0.clone()).into(), + (x, y) if x == y => sugg::ZERO.into(), + (_, _) => (self.0 - &rhs.0).into(), + } + } +} + +/// a wrapper around `MinifyingSugg`, which carries a operator like currying +/// so that the suggested code become more efficient (e.g. `foo + -bar` `foo - bar`). +struct Offset { + value: MinifyingSugg<'static>, + sign: OffsetSign, +} + +#[derive(Clone, Copy)] +enum OffsetSign { + Positive, + Negative, +} + +impl Offset { + fn negative(value: Sugg<'static>) -> Self { + Self { + value: value.into(), + sign: OffsetSign::Negative, + } + } + + fn positive(value: Sugg<'static>) -> Self { + Self { + value: value.into(), + sign: OffsetSign::Positive, + } + } + + fn empty() -> Self { + Self::positive(sugg::ZERO) + } +} + +fn apply_offset(lhs: &MinifyingSugg<'static>, rhs: &Offset) -> MinifyingSugg<'static> { + match rhs.sign { + OffsetSign::Positive => lhs + &rhs.value, + OffsetSign::Negative => lhs - &rhs.value, + } +} + +#[derive(Debug, Clone, Copy)] +enum StartKind<'hir> { + Range, + Counter { initializer: &'hir Expr<'hir> }, +} + +struct IndexExpr<'hir> { + base: &'hir Expr<'hir>, + idx: StartKind<'hir>, + idx_offset: Offset, +} + +struct Start<'hir> { + id: HirId, + 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_type) || is_type_diagnostic_item(cx, ty, sym::vecdeque_type) +} + +fn fetch_cloned_expr<'tcx>(expr: &'tcx Expr<'tcx>) -> &'tcx Expr<'tcx> { + if_chain! { + if let ExprKind::MethodCall(method, _, args, _) = expr.kind; + if method.ident.name == sym::clone; + if args.len() == 1; + if let Some(arg) = args.get(0); + then { arg } else { expr } + } +} + +fn get_details_from_idx<'tcx>( + cx: &LateContext<'tcx>, + idx: &Expr<'_>, + starts: &[Start<'tcx>], +) -> Option<(StartKind<'tcx>, Offset)> { + fn get_start<'tcx>(e: &Expr<'_>, starts: &[Start<'tcx>]) -> Option<StartKind<'tcx>> { + let id = path_to_local(e)?; + starts.iter().find(|start| start.id == id).map(|start| start.kind) + } + + fn get_offset<'tcx>(cx: &LateContext<'tcx>, e: &Expr<'_>, starts: &[Start<'tcx>]) -> Option<Sugg<'static>> { + match &e.kind { + ExprKind::Lit(l) => match l.node { + ast::LitKind::Int(x, _ty) => Some(Sugg::NonParen(x.to_string().into())), + _ => None, + }, + ExprKind::Path(..) if get_start(e, starts).is_none() => Some(Sugg::hir(cx, e, "???")), + _ => None, + } + } + + match idx.kind { + ExprKind::Binary(op, lhs, rhs) => match op.node { + BinOpKind::Add => { + let offset_opt = get_start(lhs, starts) + .and_then(|s| get_offset(cx, rhs, starts).map(|o| (s, o))) + .or_else(|| get_start(rhs, starts).and_then(|s| get_offset(cx, lhs, starts).map(|o| (s, o)))); + + offset_opt.map(|(s, o)| (s, Offset::positive(o))) + }, + BinOpKind::Sub => { + get_start(lhs, starts).and_then(|s| get_offset(cx, rhs, starts).map(|o| (s, Offset::negative(o)))) + }, + _ => None, + }, + ExprKind::Path(..) => get_start(idx, starts).map(|s| (s, Offset::empty())), + _ => None, + } +} + +fn get_assignment<'tcx>(e: &'tcx Expr<'tcx>) -> Option<(&'tcx Expr<'tcx>, &'tcx Expr<'tcx>)> { + if let ExprKind::Assign(lhs, rhs, _) = e.kind { + Some((lhs, rhs)) + } else { + None + } +} + +/// Get assignments from the given block. +/// The returned iterator yields `None` if no assignment expressions are there, +/// filtering out the increments of the given whitelisted loop counters; +/// because its job is to make sure there's nothing other than assignments and the increments. +fn get_assignments<'a, 'tcx>( + Block { stmts, expr, .. }: &'tcx Block<'tcx>, + loop_counters: &'a [Start<'tcx>], +) -> impl Iterator<Item = Option<(&'tcx Expr<'tcx>, &'tcx Expr<'tcx>)>> + 'a { + // As the `filter` and `map` below do different things, I think putting together + // just increases complexity. (cc #3188 and #4193) + stmts + .iter() + .filter_map(move |stmt| match stmt.kind { + StmtKind::Local(..) | StmtKind::Item(..) => None, + StmtKind::Expr(e) | StmtKind::Semi(e) => Some(e), + }) + .chain((*expr).into_iter()) + .filter(move |e| { + if let ExprKind::AssignOp(_, place, _) = e.kind { + path_to_local(place).map_or(false, |id| { + !loop_counters + .iter() + // skip the first item which should be `StartKind::Range` + // this makes it possible to use the slice with `StartKind::Range` in the same iterator loop. + .skip(1) + .any(|counter| counter.id == id) + }) + } else { + true + } + }) + .map(get_assignment) +} + +fn get_loop_counters<'a, 'tcx>( + cx: &'a LateContext<'tcx>, + body: &'tcx Block<'tcx>, + expr: &'tcx Expr<'_>, +) -> Option<impl Iterator<Item = Start<'tcx>> + 'a> { + // Look for variables that are incremented once per loop iteration. + let mut increment_visitor = IncrementVisitor::new(cx); + walk_block(&mut increment_visitor, body); + + // For each candidate, check the parent block to see if + // it's initialized to zero at the start of the loop. + get_enclosing_block(cx, expr.hir_id).and_then(|block| { + increment_visitor + .into_results() + .filter_map(move |var_id| { + let mut initialize_visitor = InitializeVisitor::new(cx, expr, var_id); + walk_block(&mut initialize_visitor, block); + + initialize_visitor.get_result().map(|(_, initializer)| Start { + id: var_id, + kind: StartKind::Counter { initializer }, + }) + }) + .into() + }) +} diff --git a/src/tools/clippy/clippy_lints/src/loops/mod.rs b/src/tools/clippy/clippy_lints/src/loops/mod.rs new file mode 100644 index 0000000000000..28acefd51fef7 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/loops/mod.rs @@ -0,0 +1,627 @@ +mod empty_loop; +mod explicit_counter_loop; +mod explicit_into_iter_loop; +mod explicit_iter_loop; +mod for_kv_map; +mod for_loops_over_fallibles; +mod iter_next_loop; +mod manual_flatten; +mod manual_memcpy; +mod mut_range_bound; +mod needless_collect; +mod needless_range_loop; +mod never_loop; +mod same_item_push; +mod single_element_loop; +mod utils; +mod while_immutable_condition; +mod while_let_loop; +mod while_let_on_iterator; + +use clippy_utils::higher; +use rustc_hir::{Expr, ExprKind, LoopSource, Pat}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::source_map::Span; +use utils::{get_span_of_entire_for_loop, make_iterator_snippet, IncrementVisitor, InitializeVisitor}; + +declare_clippy_lint! { + /// **What it does:** Checks for for-loops that manually copy items between + /// slices that could be optimized by having a memcpy. + /// + /// **Why is this bad?** It is not as fast as a memcpy. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// ```rust + /// # let src = vec![1]; + /// # let mut dst = vec![0; 65]; + /// for i in 0..src.len() { + /// dst[i + 64] = src[i]; + /// } + /// ``` + /// Could be written as: + /// ```rust + /// # let src = vec![1]; + /// # let mut dst = vec![0; 65]; + /// dst[64..(src.len() + 64)].clone_from_slice(&src[..]); + /// ``` + pub MANUAL_MEMCPY, + perf, + "manually copying items between slices" +} + +declare_clippy_lint! { + /// **What it does:** Checks for looping over the range of `0..len` of some + /// collection just to get the values by index. + /// + /// **Why is this bad?** Just iterating the collection itself makes the intent + /// more clear and is probably faster. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// ```rust + /// let vec = vec!['a', 'b', 'c']; + /// for i in 0..vec.len() { + /// println!("{}", vec[i]); + /// } + /// ``` + /// Could be written as: + /// ```rust + /// let vec = vec!['a', 'b', 'c']; + /// for i in vec { + /// println!("{}", i); + /// } + /// ``` + pub NEEDLESS_RANGE_LOOP, + style, + "for-looping over a range of indices where an iterator over items would do" +} + +declare_clippy_lint! { + /// **What it does:** Checks for loops on `x.iter()` where `&x` will do, and + /// suggests the latter. + /// + /// **Why is this bad?** Readability. + /// + /// **Known problems:** False negatives. We currently only warn on some known + /// types. + /// + /// **Example:** + /// ```rust + /// // with `y` a `Vec` or slice: + /// # let y = vec![1]; + /// for x in y.iter() { + /// // .. + /// } + /// ``` + /// can be rewritten to + /// ```rust + /// # let y = vec![1]; + /// for x in &y { + /// // .. + /// } + /// ``` + pub EXPLICIT_ITER_LOOP, + pedantic, + "for-looping over `_.iter()` or `_.iter_mut()` when `&_` or `&mut _` would do" +} + +declare_clippy_lint! { + /// **What it does:** Checks for loops on `y.into_iter()` where `y` will do, and + /// suggests the latter. + /// + /// **Why is this bad?** Readability. + /// + /// **Known problems:** None + /// + /// **Example:** + /// ```rust + /// # let y = vec![1]; + /// // with `y` a `Vec` or slice: + /// for x in y.into_iter() { + /// // .. + /// } + /// ``` + /// can be rewritten to + /// ```rust + /// # let y = vec![1]; + /// for x in y { + /// // .. + /// } + /// ``` + pub EXPLICIT_INTO_ITER_LOOP, + pedantic, + "for-looping over `_.into_iter()` when `_` would do" +} + +declare_clippy_lint! { + /// **What it does:** Checks for loops on `x.next()`. + /// + /// **Why is this bad?** `next()` returns either `Some(value)` if there was a + /// value, or `None` otherwise. The insidious thing is that `Option<_>` + /// implements `IntoIterator`, so that possibly one value will be iterated, + /// leading to some hard to find bugs. No one will want to write such code + /// [except to win an Underhanded Rust + /// Contest](https://www.reddit.com/r/rust/comments/3hb0wm/underhanded_rust_contest/cu5yuhr). + /// + /// **Known problems:** None. + /// + /// **Example:** + /// ```ignore + /// for x in y.next() { + /// .. + /// } + /// ``` + pub ITER_NEXT_LOOP, + correctness, + "for-looping over `_.next()` which is probably not intended" +} + +declare_clippy_lint! { + /// **What it does:** Checks for `for` loops over `Option` or `Result` values. + /// + /// **Why is this bad?** Readability. This is more clearly expressed as an `if + /// let`. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// ```rust + /// # let opt = Some(1); + /// + /// // Bad + /// for x in opt { + /// // .. + /// } + /// + /// // Good + /// if let Some(x) = opt { + /// // .. + /// } + /// ``` + /// + /// // or + /// + /// ```rust + /// # let res: Result<i32, std::io::Error> = Ok(1); + /// + /// // Bad + /// for x in &res { + /// // .. + /// } + /// + /// // Good + /// if let Ok(x) = res { + /// // .. + /// } + /// ``` + pub FOR_LOOPS_OVER_FALLIBLES, + correctness, + "for-looping over an `Option` or a `Result`, which is more clearly expressed as an `if let`" +} + +declare_clippy_lint! { + /// **What it does:** Detects `loop + match` combinations that are easier + /// written as a `while let` loop. + /// + /// **Why is this bad?** The `while let` loop is usually shorter and more + /// readable. + /// + /// **Known problems:** Sometimes the wrong binding is displayed ([#383](https://github.com/rust-lang/rust-clippy/issues/383)). + /// + /// **Example:** + /// ```rust,no_run + /// # let y = Some(1); + /// loop { + /// let x = match y { + /// Some(x) => x, + /// None => break, + /// }; + /// // .. do something with x + /// } + /// // is easier written as + /// while let Some(x) = y { + /// // .. do something with x + /// }; + /// ``` + pub WHILE_LET_LOOP, + complexity, + "`loop { if let { ... } else break }`, which can be written as a `while let` loop" +} + +declare_clippy_lint! { + /// **What it does:** Checks for functions collecting an iterator when collect + /// is not needed. + /// + /// **Why is this bad?** `collect` causes the allocation of a new data structure, + /// when this allocation may not be needed. + /// + /// **Known problems:** + /// None + /// + /// **Example:** + /// ```rust + /// # let iterator = vec![1].into_iter(); + /// let len = iterator.clone().collect::<Vec<_>>().len(); + /// // should be + /// let len = iterator.count(); + /// ``` + pub NEEDLESS_COLLECT, + perf, + "collecting an iterator when collect is not needed" +} + +declare_clippy_lint! { + /// **What it does:** Checks `for` loops over slices with an explicit counter + /// and suggests the use of `.enumerate()`. + /// + /// **Why is it bad?** Using `.enumerate()` makes the intent more clear, + /// declutters the code and may be faster in some instances. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// ```rust + /// # let v = vec![1]; + /// # fn bar(bar: usize, baz: usize) {} + /// let mut i = 0; + /// for item in &v { + /// bar(i, *item); + /// i += 1; + /// } + /// ``` + /// Could be written as + /// ```rust + /// # let v = vec![1]; + /// # fn bar(bar: usize, baz: usize) {} + /// for (i, item) in v.iter().enumerate() { bar(i, *item); } + /// ``` + pub EXPLICIT_COUNTER_LOOP, + complexity, + "for-looping with an explicit counter when `_.enumerate()` would do" +} + +declare_clippy_lint! { + /// **What it does:** Checks for empty `loop` expressions. + /// + /// **Why is this bad?** These busy loops burn CPU cycles without doing + /// anything. It is _almost always_ a better idea to `panic!` than to have + /// a busy loop. + /// + /// If panicking isn't possible, think of the environment and either: + /// - block on something + /// - sleep the thread for some microseconds + /// - yield or pause the thread + /// + /// For `std` targets, this can be done with + /// [`std::thread::sleep`](https://doc.rust-lang.org/std/thread/fn.sleep.html) + /// or [`std::thread::yield_now`](https://doc.rust-lang.org/std/thread/fn.yield_now.html). + /// + /// For `no_std` targets, doing this is more complicated, especially because + /// `#[panic_handler]`s can't panic. To stop/pause the thread, you will + /// probably need to invoke some target-specific intrinsic. Examples include: + /// - [`x86_64::instructions::hlt`](https://docs.rs/x86_64/0.12.2/x86_64/instructions/fn.hlt.html) + /// - [`cortex_m::asm::wfi`](https://docs.rs/cortex-m/0.6.3/cortex_m/asm/fn.wfi.html) + /// + /// **Known problems:** None. + /// + /// **Example:** + /// ```no_run + /// loop {} + /// ``` + pub EMPTY_LOOP, + style, + "empty `loop {}`, which should block or sleep" +} + +declare_clippy_lint! { + /// **What it does:** Checks for `while let` expressions on iterators. + /// + /// **Why is this bad?** Readability. A simple `for` loop is shorter and conveys + /// the intent better. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// ```ignore + /// while let Some(val) = iter() { + /// .. + /// } + /// ``` + pub WHILE_LET_ON_ITERATOR, + style, + "using a `while let` loop instead of a for loop on an iterator" +} + +declare_clippy_lint! { + /// **What it does:** Checks for iterating a map (`HashMap` or `BTreeMap`) and + /// ignoring either the keys or values. + /// + /// **Why is this bad?** Readability. There are `keys` and `values` methods that + /// can be used to express that don't need the values or keys. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// ```ignore + /// for (k, _) in &map { + /// .. + /// } + /// ``` + /// + /// could be replaced by + /// + /// ```ignore + /// for k in map.keys() { + /// .. + /// } + /// ``` + pub FOR_KV_MAP, + style, + "looping on a map using `iter` when `keys` or `values` would do" +} + +declare_clippy_lint! { + /// **What it does:** Checks for loops that will always `break`, `return` or + /// `continue` an outer loop. + /// + /// **Why is this bad?** This loop never loops, all it does is obfuscating the + /// code. + /// + /// **Known problems:** None + /// + /// **Example:** + /// ```rust + /// loop { + /// ..; + /// break; + /// } + /// ``` + pub NEVER_LOOP, + correctness, + "any loop that will always `break` or `return`" +} + +declare_clippy_lint! { + /// **What it does:** Checks for loops which have a range bound that is a mutable variable + /// + /// **Why is this bad?** One might think that modifying the mutable variable changes the loop bounds + /// + /// **Known problems:** None + /// + /// **Example:** + /// ```rust + /// let mut foo = 42; + /// for i in 0..foo { + /// foo -= 1; + /// println!("{}", i); // prints numbers from 0 to 42, not 0 to 21 + /// } + /// ``` + pub MUT_RANGE_BOUND, + complexity, + "for loop over a range where one of the bounds is a mutable variable" +} + +declare_clippy_lint! { + /// **What it does:** Checks whether variables used within while loop condition + /// can be (and are) mutated in the body. + /// + /// **Why is this bad?** If the condition is unchanged, entering the body of the loop + /// will lead to an infinite loop. + /// + /// **Known problems:** If the `while`-loop is in a closure, the check for mutation of the + /// condition variables in the body can cause false negatives. For example when only `Upvar` `a` is + /// in the condition and only `Upvar` `b` gets mutated in the body, the lint will not trigger. + /// + /// **Example:** + /// ```rust + /// let i = 0; + /// while i > 10 { + /// println!("let me loop forever!"); + /// } + /// ``` + pub WHILE_IMMUTABLE_CONDITION, + correctness, + "variables used within while expression are not mutated in the body" +} + +declare_clippy_lint! { + /// **What it does:** Checks whether a for loop is being used to push a constant + /// value into a Vec. + /// + /// **Why is this bad?** This kind of operation can be expressed more succinctly with + /// `vec![item;SIZE]` or `vec.resize(NEW_SIZE, item)` and using these alternatives may also + /// have better performance. + /// **Known problems:** None + /// + /// **Example:** + /// ```rust + /// let item1 = 2; + /// let item2 = 3; + /// let mut vec: Vec<u8> = Vec::new(); + /// for _ in 0..20 { + /// vec.push(item1); + /// } + /// for _ in 0..30 { + /// vec.push(item2); + /// } + /// ``` + /// could be written as + /// ```rust + /// let item1 = 2; + /// let item2 = 3; + /// let mut vec: Vec<u8> = vec![item1; 20]; + /// vec.resize(20 + 30, item2); + /// ``` + pub SAME_ITEM_PUSH, + style, + "the same item is pushed inside of a for loop" +} + +declare_clippy_lint! { + /// **What it does:** Checks whether a for loop has a single element. + /// + /// **Why is this bad?** There is no reason to have a loop of a + /// single element. + /// **Known problems:** None + /// + /// **Example:** + /// ```rust + /// let item1 = 2; + /// for item in &[item1] { + /// println!("{}", item); + /// } + /// ``` + /// could be written as + /// ```rust + /// let item1 = 2; + /// let item = &item1; + /// println!("{}", item); + /// ``` + pub SINGLE_ELEMENT_LOOP, + complexity, + "there is no reason to have a single element loop" +} + +declare_clippy_lint! { + /// **What it does:** Check for unnecessary `if let` usage in a for loop + /// where only the `Some` or `Ok` variant of the iterator element is used. + /// + /// **Why is this bad?** It is verbose and can be simplified + /// by first calling the `flatten` method on the `Iterator`. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// + /// ```rust + /// let x = vec![Some(1), Some(2), Some(3)]; + /// for n in x { + /// if let Some(n) = n { + /// println!("{}", n); + /// } + /// } + /// ``` + /// Use instead: + /// ```rust + /// let x = vec![Some(1), Some(2), Some(3)]; + /// for n in x.into_iter().flatten() { + /// println!("{}", n); + /// } + /// ``` + pub MANUAL_FLATTEN, + complexity, + "for loops over `Option`s or `Result`s with a single expression can be simplified" +} + +declare_lint_pass!(Loops => [ + MANUAL_MEMCPY, + MANUAL_FLATTEN, + NEEDLESS_RANGE_LOOP, + EXPLICIT_ITER_LOOP, + EXPLICIT_INTO_ITER_LOOP, + ITER_NEXT_LOOP, + FOR_LOOPS_OVER_FALLIBLES, + WHILE_LET_LOOP, + NEEDLESS_COLLECT, + EXPLICIT_COUNTER_LOOP, + EMPTY_LOOP, + WHILE_LET_ON_ITERATOR, + FOR_KV_MAP, + NEVER_LOOP, + MUT_RANGE_BOUND, + WHILE_IMMUTABLE_CONDITION, + SAME_ITEM_PUSH, + SINGLE_ELEMENT_LOOP, +]); + +impl<'tcx> LateLintPass<'tcx> for Loops { + #[allow(clippy::too_many_lines)] + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { + if let Some((pat, arg, body, span)) = higher::for_loop(expr) { + // we don't want to check expanded macros + // this check is not at the top of the function + // since higher::for_loop expressions are marked as expansions + if body.span.from_expansion() { + return; + } + check_for_loop(cx, pat, arg, body, expr, span); + } + + // we don't want to check expanded macros + if expr.span.from_expansion() { + return; + } + + // check for never_loop + never_loop::check(cx, expr); + + // check for `loop { if let {} else break }` that could be `while let` + // (also matches an explicit "match" instead of "if let") + // (even if the "match" or "if let" is used for declaration) + if let ExprKind::Loop(block, _, LoopSource::Loop, _) = expr.kind { + // also check for empty `loop {}` statements, skipping those in #[panic_handler] + empty_loop::check(cx, expr, block); + while_let_loop::check(cx, expr, block); + } + + while_let_on_iterator::check(cx, expr); + + if let Some((cond, body)) = higher::while_loop(expr) { + while_immutable_condition::check(cx, cond, body); + } + + needless_collect::check(expr, cx); + } +} + +fn check_for_loop<'tcx>( + cx: &LateContext<'tcx>, + pat: &'tcx Pat<'_>, + arg: &'tcx Expr<'_>, + body: &'tcx Expr<'_>, + expr: &'tcx Expr<'_>, + span: Span, +) { + let is_manual_memcpy_triggered = manual_memcpy::check(cx, pat, arg, body, expr); + if !is_manual_memcpy_triggered { + needless_range_loop::check(cx, pat, arg, body, expr); + explicit_counter_loop::check(cx, pat, arg, body, expr); + } + check_for_loop_arg(cx, pat, arg, expr); + for_kv_map::check(cx, pat, arg, body, expr); + mut_range_bound::check(cx, arg, body); + single_element_loop::check(cx, pat, arg, body, expr); + same_item_push::check(cx, pat, arg, body, expr); + manual_flatten::check(cx, pat, arg, body, span); +} + +fn check_for_loop_arg(cx: &LateContext<'_>, pat: &Pat<'_>, arg: &Expr<'_>, expr: &Expr<'_>) { + let mut next_loop_linted = false; // whether or not ITER_NEXT_LOOP lint was used + + if let ExprKind::MethodCall(method, _, args, _) = arg.kind { + // just the receiver, no arguments + if args.len() == 1 { + let method_name = &*method.ident.as_str(); + // check for looping over x.iter() or x.iter_mut(), could use &x or &mut x + match method_name { + "iter" | "iter_mut" => explicit_iter_loop::check(cx, args, arg, method_name), + "into_iter" => { + explicit_iter_loop::check(cx, args, arg, method_name); + explicit_into_iter_loop::check(cx, args, arg); + }, + "next" => { + next_loop_linted = iter_next_loop::check(cx, arg, expr); + }, + _ => {}, + } + } + } + + if !next_loop_linted { + for_loops_over_fallibles::check(cx, pat, arg); + } +} 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 new file mode 100644 index 0000000000000..1425d50f56046 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/loops/mut_range_bound.rs @@ -0,0 +1,118 @@ +use super::MUT_RANGE_BOUND; +use clippy_utils::diagnostics::span_lint; +use clippy_utils::{higher, path_to_local}; +use if_chain::if_chain; +use rustc_hir::{BindingAnnotation, Expr, HirId, Node, PatKind}; +use rustc_infer::infer::TyCtxtInferExt; +use rustc_lint::LateContext; +use rustc_middle::{mir::FakeReadCause, ty}; +use rustc_span::source_map::Span; +use rustc_typeck::expr_use_visitor::{ConsumeMode, Delegate, ExprUseVisitor, PlaceBase, PlaceWithHirId}; + +pub(super) fn check(cx: &LateContext<'_>, arg: &Expr<'_>, body: &Expr<'_>) { + if let Some(higher::Range { + start: Some(start), + end: Some(end), + .. + }) = higher::range(arg) + { + let mut_ids = vec![check_for_mutability(cx, start), check_for_mutability(cx, end)]; + if mut_ids[0].is_some() || mut_ids[1].is_some() { + let (span_low, span_high) = check_for_mutation(cx, body, &mut_ids); + mut_warn_with_span(cx, span_low); + mut_warn_with_span(cx, span_high); + } + } +} + +fn mut_warn_with_span(cx: &LateContext<'_>, span: Option<Span>) { + if let Some(sp) = span { + span_lint( + cx, + MUT_RANGE_BOUND, + sp, + "attempt to mutate range bound within loop; note that the range of the loop is unchanged", + ); + } +} + +fn check_for_mutability(cx: &LateContext<'_>, bound: &Expr<'_>) -> Option<HirId> { + if_chain! { + if let Some(hir_id) = path_to_local(bound); + if let Node::Binding(pat) = cx.tcx.hir().get(hir_id); + if let PatKind::Binding(BindingAnnotation::Mutable, ..) = pat.kind; + then { + return Some(hir_id); + } + } + None +} + +fn check_for_mutation<'tcx>( + cx: &LateContext<'tcx>, + body: &Expr<'_>, + bound_ids: &[Option<HirId>], +) -> (Option<Span>, Option<Span>) { + let mut delegate = MutatePairDelegate { + cx, + hir_id_low: bound_ids[0], + hir_id_high: bound_ids[1], + span_low: None, + span_high: None, + }; + cx.tcx.infer_ctxt().enter(|infcx| { + ExprUseVisitor::new( + &mut delegate, + &infcx, + body.hir_id.owner, + cx.param_env, + cx.typeck_results(), + ) + .walk_expr(body); + }); + delegate.mutation_span() +} + +struct MutatePairDelegate<'a, 'tcx> { + cx: &'a LateContext<'tcx>, + hir_id_low: Option<HirId>, + hir_id_high: Option<HirId>, + span_low: Option<Span>, + span_high: Option<Span>, +} + +impl<'tcx> Delegate<'tcx> for MutatePairDelegate<'_, 'tcx> { + fn consume(&mut self, _: &PlaceWithHirId<'tcx>, _: HirId, _: ConsumeMode) {} + + fn borrow(&mut self, cmt: &PlaceWithHirId<'tcx>, diag_expr_id: HirId, bk: ty::BorrowKind) { + if let ty::BorrowKind::MutBorrow = bk { + if let PlaceBase::Local(id) = cmt.place.base { + if Some(id) == self.hir_id_low { + self.span_low = Some(self.cx.tcx.hir().span(diag_expr_id)) + } + if Some(id) == self.hir_id_high { + self.span_high = Some(self.cx.tcx.hir().span(diag_expr_id)) + } + } + } + } + + fn mutate(&mut self, cmt: &PlaceWithHirId<'tcx>, diag_expr_id: HirId) { + if let PlaceBase::Local(id) = cmt.place.base { + if Some(id) == self.hir_id_low { + self.span_low = Some(self.cx.tcx.hir().span(diag_expr_id)) + } + if Some(id) == self.hir_id_high { + self.span_high = Some(self.cx.tcx.hir().span(diag_expr_id)) + } + } + } + + fn fake_read(&mut self, _: rustc_typeck::expr_use_visitor::Place<'tcx>, _: FakeReadCause, _: HirId) {} +} + +impl MutatePairDelegate<'_, '_> { + fn mutation_span(&self) -> (Option<Span>, Option<Span>) { + (self.span_low, self.span_high) + } +} diff --git a/src/tools/clippy/clippy_lints/src/loops/needless_collect.rs b/src/tools/clippy/clippy_lints/src/loops/needless_collect.rs new file mode 100644 index 0000000000000..4d73aef76e87e --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/loops/needless_collect.rs @@ -0,0 +1,245 @@ +use super::NEEDLESS_COLLECT; +use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then}; +use clippy_utils::source::snippet; +use clippy_utils::sugg::Sugg; +use clippy_utils::ty::{is_type_diagnostic_item, match_type}; +use clippy_utils::{is_trait_method, path_to_local_id, paths}; +use if_chain::if_chain; +use rustc_errors::Applicability; +use rustc_hir::intravisit::{walk_block, walk_expr, NestedVisitorMap, Visitor}; +use rustc_hir::{Block, Expr, ExprKind, GenericArg, HirId, Local, Pat, PatKind, QPath, StmtKind}; +use rustc_lint::LateContext; +use rustc_middle::hir::map::Map; +use rustc_span::symbol::{sym, Ident}; +use rustc_span::{MultiSpan, Span}; + +const NEEDLESS_COLLECT_MSG: &str = "avoid using `collect()` when not needed"; + +pub(super) fn check<'tcx>(expr: &'tcx Expr<'_>, cx: &LateContext<'tcx>) { + check_needless_collect_direct_usage(expr, cx); + check_needless_collect_indirect_usage(expr, cx); +} +fn check_needless_collect_direct_usage<'tcx>(expr: &'tcx Expr<'_>, cx: &LateContext<'tcx>) { + if_chain! { + if let ExprKind::MethodCall(method, _, args, _) = expr.kind; + if let ExprKind::MethodCall(chain_method, method0_span, _, _) = args[0].kind; + if chain_method.ident.name == sym!(collect) && is_trait_method(cx, &args[0], sym::Iterator); + if let Some(generic_args) = chain_method.args; + if let Some(GenericArg::Type(ref ty)) = generic_args.args.get(0); + let ty = cx.typeck_results().node_type(ty.hir_id); + if is_type_diagnostic_item(cx, ty, sym::vec_type) + || is_type_diagnostic_item(cx, ty, sym::vecdeque_type) + || match_type(cx, ty, &paths::BTREEMAP) + || is_type_diagnostic_item(cx, ty, sym::hashmap_type); + if let Some(sugg) = match &*method.ident.name.as_str() { + "len" => Some("count()".to_string()), + "is_empty" => Some("next().is_none()".to_string()), + "contains" => { + let contains_arg = snippet(cx, args[1].span, "??"); + let (arg, pred) = contains_arg + .strip_prefix('&') + .map_or(("&x", &*contains_arg), |s| ("x", s)); + Some(format!("any(|{}| x == {})", arg, pred)) + } + _ => None, + }; + then { + span_lint_and_sugg( + cx, + NEEDLESS_COLLECT, + method0_span.with_hi(expr.span.hi()), + NEEDLESS_COLLECT_MSG, + "replace with", + sugg, + Applicability::MachineApplicable, + ); + } + } +} + +fn check_needless_collect_indirect_usage<'tcx>(expr: &'tcx Expr<'_>, cx: &LateContext<'tcx>) { + if let ExprKind::Block(block, _) = expr.kind { + for stmt in block.stmts { + if_chain! { + if let StmtKind::Local( + Local { pat: Pat { hir_id: pat_id, kind: PatKind::Binding(_, _, ident, .. ), .. }, + init: Some(init_expr), .. } + ) = stmt.kind; + if let ExprKind::MethodCall(method_name, collect_span, &[ref iter_source], ..) = init_expr.kind; + if method_name.ident.name == sym!(collect) && is_trait_method(cx, init_expr, sym::Iterator); + if let Some(generic_args) = method_name.args; + if let Some(GenericArg::Type(ref ty)) = generic_args.args.get(0); + if let ty = cx.typeck_results().node_type(ty.hir_id); + if is_type_diagnostic_item(cx, ty, sym::vec_type) || + is_type_diagnostic_item(cx, ty, sym::vecdeque_type) || + match_type(cx, ty, &paths::LINKED_LIST); + if let Some(iter_calls) = detect_iter_and_into_iters(block, *ident); + if let [iter_call] = &*iter_calls; + then { + let mut used_count_visitor = UsedCountVisitor { + cx, + id: *pat_id, + count: 0, + }; + walk_block(&mut used_count_visitor, block); + if used_count_visitor.count > 1 { + return; + } + + // Suggest replacing iter_call with iter_replacement, and removing stmt + let mut span = MultiSpan::from_span(collect_span); + span.push_span_label(iter_call.span, "the iterator could be used here instead".into()); + span_lint_and_then( + cx, + super::NEEDLESS_COLLECT, + span, + NEEDLESS_COLLECT_MSG, + |diag| { + let iter_replacement = format!("{}{}", Sugg::hir(cx, iter_source, ".."), iter_call.get_iter_method(cx)); + diag.multipart_suggestion( + iter_call.get_suggestion_text(), + vec![ + (stmt.span, String::new()), + (iter_call.span, iter_replacement) + ], + Applicability::MachineApplicable,// MaybeIncorrect, + ); + }, + ); + } + } + } + } +} + +struct IterFunction { + func: IterFunctionKind, + span: Span, +} +impl IterFunction { + fn get_iter_method(&self, cx: &LateContext<'_>) -> String { + match &self.func { + IterFunctionKind::IntoIter => String::new(), + IterFunctionKind::Len => String::from(".count()"), + IterFunctionKind::IsEmpty => String::from(".next().is_none()"), + IterFunctionKind::Contains(span) => { + let s = snippet(cx, *span, ".."); + if let Some(stripped) = s.strip_prefix('&') { + format!(".any(|x| x == {})", stripped) + } else { + format!(".any(|x| x == *{})", s) + } + }, + } + } + fn get_suggestion_text(&self) -> &'static str { + match &self.func { + IterFunctionKind::IntoIter => { + "use the original Iterator instead of collecting it and then producing a new one" + }, + IterFunctionKind::Len => { + "take the original Iterator's count instead of collecting it and finding the length" + }, + IterFunctionKind::IsEmpty => { + "check if the original Iterator has anything instead of collecting it and seeing if it's empty" + }, + IterFunctionKind::Contains(_) => { + "check if the original Iterator contains an element instead of collecting then checking" + }, + } + } +} +enum IterFunctionKind { + IntoIter, + Len, + IsEmpty, + Contains(Span), +} + +struct IterFunctionVisitor { + uses: Vec<IterFunction>, + seen_other: bool, + target: Ident, +} +impl<'tcx> Visitor<'tcx> for IterFunctionVisitor { + fn visit_expr(&mut self, expr: &'tcx Expr<'tcx>) { + // Check function calls on our collection + if_chain! { + if let ExprKind::MethodCall(method_name, _, args, _) = &expr.kind; + if let Some(Expr { kind: ExprKind::Path(QPath::Resolved(_, path)), .. }) = args.get(0); + if let &[name] = &path.segments; + if name.ident == self.target; + then { + let len = sym!(len); + let is_empty = sym!(is_empty); + let contains = sym!(contains); + match method_name.ident.name { + sym::into_iter => self.uses.push( + IterFunction { func: IterFunctionKind::IntoIter, span: expr.span } + ), + name if name == len => self.uses.push( + IterFunction { func: IterFunctionKind::Len, span: expr.span } + ), + name if name == is_empty => self.uses.push( + IterFunction { func: IterFunctionKind::IsEmpty, span: expr.span } + ), + name if name == contains => self.uses.push( + IterFunction { func: IterFunctionKind::Contains(args[1].span), span: expr.span } + ), + _ => self.seen_other = true, + } + return + } + } + // Check if the collection is used for anything else + if_chain! { + if let Expr { kind: ExprKind::Path(QPath::Resolved(_, path)), .. } = expr; + if let &[name] = &path.segments; + if name.ident == self.target; + then { + self.seen_other = true; + } else { + walk_expr(self, expr); + } + } + } + + type Map = Map<'tcx>; + fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> { + NestedVisitorMap::None + } +} + +struct UsedCountVisitor<'a, 'tcx> { + cx: &'a LateContext<'tcx>, + id: HirId, + count: usize, +} + +impl<'a, 'tcx> Visitor<'tcx> for UsedCountVisitor<'a, 'tcx> { + type Map = Map<'tcx>; + + fn visit_expr(&mut self, expr: &'tcx Expr<'_>) { + if path_to_local_id(expr, self.id) { + self.count += 1; + } else { + walk_expr(self, expr); + } + } + + fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> { + NestedVisitorMap::OnlyBodies(self.cx.tcx.hir()) + } +} + +/// Detect the occurrences of calls to `iter` or `into_iter` for the +/// given identifier +fn detect_iter_and_into_iters<'tcx>(block: &'tcx Block<'tcx>, identifier: Ident) -> Option<Vec<IterFunction>> { + let mut visitor = IterFunctionVisitor { + uses: Vec::new(), + target: identifier, + seen_other: false, + }; + visitor.visit_block(block); + if visitor.seen_other { None } else { Some(visitor.uses) } +} diff --git a/src/tools/clippy/clippy_lints/src/loops/needless_range_loop.rs b/src/tools/clippy/clippy_lints/src/loops/needless_range_loop.rs new file mode 100644 index 0000000000000..3065bcc3e6c81 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/loops/needless_range_loop.rs @@ -0,0 +1,391 @@ +use super::NEEDLESS_RANGE_LOOP; +use clippy_utils::diagnostics::{multispan_sugg, span_lint_and_then}; +use clippy_utils::source::snippet; +use clippy_utils::ty::has_iter_method; +use clippy_utils::visitors::LocalUsedVisitor; +use clippy_utils::{ + contains_name, higher, is_integer_const, match_trait_method, path_to_local_id, paths, sugg, SpanlessEq, +}; +use if_chain::if_chain; +use rustc_ast::ast; +use rustc_data_structures::fx::{FxHashMap, FxHashSet}; +use rustc_hir::def::{DefKind, Res}; +use rustc_hir::intravisit::{walk_expr, NestedVisitorMap, Visitor}; +use rustc_hir::{BinOpKind, BorrowKind, Expr, ExprKind, HirId, Mutability, Pat, PatKind, QPath}; +use rustc_lint::LateContext; +use rustc_middle::hir::map::Map; +use rustc_middle::middle::region; +use rustc_middle::ty::{self, Ty}; +use rustc_span::symbol::{sym, Symbol}; +use std::iter::{self, Iterator}; +use std::mem; + +/// Checks for looping over a range and then indexing a sequence with it. +/// The iteratee must be a range literal. +#[allow(clippy::too_many_lines)] +pub(super) fn check<'tcx>( + cx: &LateContext<'tcx>, + pat: &'tcx Pat<'_>, + arg: &'tcx Expr<'_>, + body: &'tcx Expr<'_>, + expr: &'tcx Expr<'_>, +) { + if let Some(higher::Range { + start: Some(start), + ref end, + limits, + }) = higher::range(arg) + { + // the var must be a single name + if let PatKind::Binding(_, canonical_id, ident, _) = pat.kind { + let mut visitor = VarVisitor { + cx, + var: canonical_id, + indexed_mut: FxHashSet::default(), + indexed_indirectly: FxHashMap::default(), + indexed_directly: FxHashMap::default(), + referenced: FxHashSet::default(), + nonindex: false, + prefer_mutable: false, + }; + walk_expr(&mut visitor, body); + + // linting condition: we only indexed one variable, and indexed it directly + if visitor.indexed_indirectly.is_empty() && visitor.indexed_directly.len() == 1 { + let (indexed, (indexed_extent, indexed_ty)) = visitor + .indexed_directly + .into_iter() + .next() + .expect("already checked that we have exactly 1 element"); + + // ensure that the indexed variable was declared before the loop, see #601 + if let Some(indexed_extent) = indexed_extent { + let parent_id = cx.tcx.hir().get_parent_item(expr.hir_id); + let parent_def_id = cx.tcx.hir().local_def_id(parent_id); + let region_scope_tree = cx.tcx.region_scope_tree(parent_def_id); + let pat_extent = region_scope_tree.var_scope(pat.hir_id.local_id); + if region_scope_tree.is_subscope_of(indexed_extent, pat_extent) { + return; + } + } + + // don't lint if the container that is indexed does not have .iter() method + let has_iter = has_iter_method(cx, indexed_ty); + if has_iter.is_none() { + return; + } + + // don't lint if the container that is indexed into is also used without + // indexing + if visitor.referenced.contains(&indexed) { + return; + } + + let starts_at_zero = is_integer_const(cx, start, 0); + + let skip = if starts_at_zero { + String::new() + } else if visitor.indexed_mut.contains(&indexed) && contains_name(indexed, start) { + return; + } else { + format!(".skip({})", snippet(cx, start.span, "..")) + }; + + let mut end_is_start_plus_val = false; + + let take = if let Some(end) = *end { + let mut take_expr = end; + + if let ExprKind::Binary(ref op, left, right) = end.kind { + if let BinOpKind::Add = op.node { + let start_equal_left = SpanlessEq::new(cx).eq_expr(start, left); + let start_equal_right = SpanlessEq::new(cx).eq_expr(start, right); + + if start_equal_left { + take_expr = right; + } else if start_equal_right { + take_expr = left; + } + + end_is_start_plus_val = start_equal_left | start_equal_right; + } + } + + if is_len_call(end, indexed) || is_end_eq_array_len(cx, end, limits, indexed_ty) { + String::new() + } else if visitor.indexed_mut.contains(&indexed) && contains_name(indexed, take_expr) { + return; + } else { + match limits { + ast::RangeLimits::Closed => { + let take_expr = sugg::Sugg::hir(cx, take_expr, "<count>"); + format!(".take({})", take_expr + sugg::ONE) + }, + ast::RangeLimits::HalfOpen => format!(".take({})", snippet(cx, take_expr.span, "..")), + } + } + } else { + String::new() + }; + + let (ref_mut, method) = if visitor.indexed_mut.contains(&indexed) { + ("mut ", "iter_mut") + } else { + ("", "iter") + }; + + let take_is_empty = take.is_empty(); + let mut method_1 = take; + let mut method_2 = skip; + + if end_is_start_plus_val { + mem::swap(&mut method_1, &mut method_2); + } + + if visitor.nonindex { + span_lint_and_then( + cx, + NEEDLESS_RANGE_LOOP, + expr.span, + &format!("the loop variable `{}` is used to index `{}`", ident.name, indexed), + |diag| { + multispan_sugg( + diag, + "consider using an iterator", + vec![ + (pat.span, format!("({}, <item>)", ident.name)), + ( + arg.span, + format!("{}.{}().enumerate(){}{}", indexed, method, method_1, method_2), + ), + ], + ); + }, + ); + } else { + let repl = if starts_at_zero && take_is_empty { + format!("&{}{}", ref_mut, indexed) + } else { + format!("{}.{}(){}{}", indexed, method, method_1, method_2) + }; + + span_lint_and_then( + cx, + NEEDLESS_RANGE_LOOP, + expr.span, + &format!("the loop variable `{}` is only used to index `{}`", ident.name, indexed), + |diag| { + multispan_sugg( + diag, + "consider using an iterator", + vec![(pat.span, "<item>".to_string()), (arg.span, repl)], + ); + }, + ); + } + } + } + } +} + +fn is_len_call(expr: &Expr<'_>, var: Symbol) -> bool { + if_chain! { + if let ExprKind::MethodCall(method, _, len_args, _) = expr.kind; + if len_args.len() == 1; + if method.ident.name == sym!(len); + if let ExprKind::Path(QPath::Resolved(_, path)) = len_args[0].kind; + if path.segments.len() == 1; + if path.segments[0].ident.name == var; + then { + return true; + } + } + + false +} + +fn is_end_eq_array_len<'tcx>( + cx: &LateContext<'tcx>, + end: &Expr<'_>, + limits: ast::RangeLimits, + indexed_ty: Ty<'tcx>, +) -> bool { + if_chain! { + if let ExprKind::Lit(ref lit) = end.kind; + if let ast::LitKind::Int(end_int, _) = lit.node; + if let ty::Array(_, arr_len_const) = indexed_ty.kind(); + if let Some(arr_len) = arr_len_const.try_eval_usize(cx.tcx, cx.param_env); + then { + return match limits { + ast::RangeLimits::Closed => end_int + 1 >= arr_len.into(), + ast::RangeLimits::HalfOpen => end_int >= arr_len.into(), + }; + } + } + + false +} + +struct VarVisitor<'a, 'tcx> { + /// context reference + cx: &'a LateContext<'tcx>, + /// var name to look for as index + var: HirId, + /// indexed variables that are used mutably + indexed_mut: FxHashSet<Symbol>, + /// indirectly indexed variables (`v[(i + 4) % N]`), the extend is `None` for global + indexed_indirectly: FxHashMap<Symbol, Option<region::Scope>>, + /// subset of `indexed` of vars that are indexed directly: `v[i]` + /// this will not contain cases like `v[calc_index(i)]` or `v[(i + 4) % N]` + indexed_directly: FxHashMap<Symbol, (Option<region::Scope>, Ty<'tcx>)>, + /// Any names that are used outside an index operation. + /// Used to detect things like `&mut vec` used together with `vec[i]` + referenced: FxHashSet<Symbol>, + /// has the loop variable been used in expressions other than the index of + /// an index op? + nonindex: bool, + /// Whether we are inside the `$` in `&mut $` or `$ = foo` or `$.bar`, where bar + /// takes `&mut self` + prefer_mutable: bool, +} + +impl<'a, 'tcx> VarVisitor<'a, 'tcx> { + fn check(&mut self, idx: &'tcx Expr<'_>, seqexpr: &'tcx Expr<'_>, expr: &'tcx Expr<'_>) -> bool { + if_chain! { + // the indexed container is referenced by a name + if let ExprKind::Path(ref seqpath) = seqexpr.kind; + if let QPath::Resolved(None, seqvar) = *seqpath; + if seqvar.segments.len() == 1; + let index_used_directly = path_to_local_id(idx, self.var); + let indexed_indirectly = { + let mut used_visitor = LocalUsedVisitor::new(self.cx, self.var); + walk_expr(&mut used_visitor, idx); + used_visitor.used + }; + if indexed_indirectly || index_used_directly; + then { + if self.prefer_mutable { + self.indexed_mut.insert(seqvar.segments[0].ident.name); + } + let res = self.cx.qpath_res(seqpath, seqexpr.hir_id); + match res { + Res::Local(hir_id) => { + let parent_id = self.cx.tcx.hir().get_parent_item(expr.hir_id); + let parent_def_id = self.cx.tcx.hir().local_def_id(parent_id); + let extent = self.cx.tcx.region_scope_tree(parent_def_id).var_scope(hir_id.local_id); + if indexed_indirectly { + self.indexed_indirectly.insert(seqvar.segments[0].ident.name, Some(extent)); + } + if index_used_directly { + self.indexed_directly.insert( + seqvar.segments[0].ident.name, + (Some(extent), self.cx.typeck_results().node_type(seqexpr.hir_id)), + ); + } + return false; // no need to walk further *on the variable* + } + Res::Def(DefKind::Static | DefKind::Const, ..) => { + if indexed_indirectly { + self.indexed_indirectly.insert(seqvar.segments[0].ident.name, None); + } + if index_used_directly { + self.indexed_directly.insert( + seqvar.segments[0].ident.name, + (None, self.cx.typeck_results().node_type(seqexpr.hir_id)), + ); + } + return false; // no need to walk further *on the variable* + } + _ => (), + } + } + } + true + } +} + +impl<'a, 'tcx> Visitor<'tcx> for VarVisitor<'a, 'tcx> { + type Map = Map<'tcx>; + + fn visit_expr(&mut self, expr: &'tcx Expr<'_>) { + if_chain! { + // a range index op + if let ExprKind::MethodCall(meth, _, args, _) = expr.kind; + if (meth.ident.name == sym::index && match_trait_method(self.cx, expr, &paths::INDEX)) + || (meth.ident.name == sym::index_mut && match_trait_method(self.cx, expr, &paths::INDEX_MUT)); + if !self.check(&args[1], &args[0], expr); + then { return } + } + + if_chain! { + // an index op + if let ExprKind::Index(seqexpr, idx) = expr.kind; + if !self.check(idx, seqexpr, expr); + then { return } + } + + if_chain! { + // directly using a variable + if let ExprKind::Path(QPath::Resolved(None, path)) = expr.kind; + if let Res::Local(local_id) = path.res; + then { + if local_id == self.var { + self.nonindex = true; + } else { + // not the correct variable, but still a variable + self.referenced.insert(path.segments[0].ident.name); + } + } + } + + let old = self.prefer_mutable; + match expr.kind { + ExprKind::AssignOp(_, lhs, rhs) | ExprKind::Assign(lhs, rhs, _) => { + self.prefer_mutable = true; + self.visit_expr(lhs); + self.prefer_mutable = false; + self.visit_expr(rhs); + }, + ExprKind::AddrOf(BorrowKind::Ref, mutbl, expr) => { + if mutbl == Mutability::Mut { + self.prefer_mutable = true; + } + self.visit_expr(expr); + }, + ExprKind::Call(f, args) => { + self.visit_expr(f); + for expr in args { + let ty = self.cx.typeck_results().expr_ty_adjusted(expr); + self.prefer_mutable = false; + if let ty::Ref(_, _, mutbl) = *ty.kind() { + if mutbl == Mutability::Mut { + self.prefer_mutable = true; + } + } + self.visit_expr(expr); + } + }, + ExprKind::MethodCall(_, _, args, _) => { + let def_id = self.cx.typeck_results().type_dependent_def_id(expr.hir_id).unwrap(); + for (ty, expr) in iter::zip(self.cx.tcx.fn_sig(def_id).inputs().skip_binder(), args) { + self.prefer_mutable = false; + if let ty::Ref(_, _, mutbl) = *ty.kind() { + if mutbl == Mutability::Mut { + self.prefer_mutable = true; + } + } + self.visit_expr(expr); + } + }, + ExprKind::Closure(_, _, body_id, ..) => { + let body = self.cx.tcx.hir().body(body_id); + self.visit_expr(&body.value); + }, + _ => walk_expr(self, expr), + } + self.prefer_mutable = old; + } + fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> { + NestedVisitorMap::None + } +} diff --git a/src/tools/clippy/clippy_lints/src/loops/never_loop.rs b/src/tools/clippy/clippy_lints/src/loops/never_loop.rs new file mode 100644 index 0000000000000..96720764e1658 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/loops/never_loop.rs @@ -0,0 +1,172 @@ +use super::NEVER_LOOP; +use clippy_utils::diagnostics::span_lint; +use rustc_hir::{Block, Expr, ExprKind, HirId, InlineAsmOperand, Stmt, StmtKind}; +use rustc_lint::LateContext; +use std::iter::{once, Iterator}; + +pub(super) fn check(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { + if let ExprKind::Loop(block, _, _, _) = expr.kind { + match never_loop_block(block, expr.hir_id) { + NeverLoopResult::AlwaysBreak => span_lint(cx, NEVER_LOOP, expr.span, "this loop never actually loops"), + NeverLoopResult::MayContinueMainLoop | NeverLoopResult::Otherwise => (), + } + } +} + +enum NeverLoopResult { + // A break/return always get triggered but not necessarily for the main loop. + AlwaysBreak, + // A continue may occur for the main loop. + MayContinueMainLoop, + Otherwise, +} + +#[must_use] +fn absorb_break(arg: &NeverLoopResult) -> NeverLoopResult { + match *arg { + NeverLoopResult::AlwaysBreak | NeverLoopResult::Otherwise => NeverLoopResult::Otherwise, + NeverLoopResult::MayContinueMainLoop => NeverLoopResult::MayContinueMainLoop, + } +} + +// Combine two results for parts that are called in order. +#[must_use] +fn combine_seq(first: NeverLoopResult, second: NeverLoopResult) -> NeverLoopResult { + match first { + NeverLoopResult::AlwaysBreak | NeverLoopResult::MayContinueMainLoop => first, + NeverLoopResult::Otherwise => second, + } +} + +// Combine two results where both parts are called but not necessarily in order. +#[must_use] +fn combine_both(left: NeverLoopResult, right: NeverLoopResult) -> NeverLoopResult { + match (left, right) { + (NeverLoopResult::MayContinueMainLoop, _) | (_, NeverLoopResult::MayContinueMainLoop) => { + NeverLoopResult::MayContinueMainLoop + }, + (NeverLoopResult::AlwaysBreak, _) | (_, NeverLoopResult::AlwaysBreak) => NeverLoopResult::AlwaysBreak, + (NeverLoopResult::Otherwise, NeverLoopResult::Otherwise) => NeverLoopResult::Otherwise, + } +} + +// Combine two results where only one of the part may have been executed. +#[must_use] +fn combine_branches(b1: NeverLoopResult, b2: NeverLoopResult) -> NeverLoopResult { + match (b1, b2) { + (NeverLoopResult::AlwaysBreak, NeverLoopResult::AlwaysBreak) => NeverLoopResult::AlwaysBreak, + (NeverLoopResult::MayContinueMainLoop, _) | (_, NeverLoopResult::MayContinueMainLoop) => { + NeverLoopResult::MayContinueMainLoop + }, + (NeverLoopResult::Otherwise, _) | (_, NeverLoopResult::Otherwise) => NeverLoopResult::Otherwise, + } +} + +fn never_loop_block(block: &Block<'_>, main_loop_id: HirId) -> NeverLoopResult { + let stmts = block.stmts.iter().map(stmt_to_expr); + let expr = once(block.expr.as_deref()); + let mut iter = stmts.chain(expr).flatten(); + never_loop_expr_seq(&mut iter, main_loop_id) +} + +fn never_loop_expr_seq<'a, T: Iterator<Item = &'a Expr<'a>>>(es: &mut T, main_loop_id: HirId) -> NeverLoopResult { + es.map(|e| never_loop_expr(e, main_loop_id)) + .fold(NeverLoopResult::Otherwise, combine_seq) +} + +fn stmt_to_expr<'tcx>(stmt: &Stmt<'tcx>) -> Option<&'tcx Expr<'tcx>> { + match stmt.kind { + StmtKind::Semi(e, ..) | StmtKind::Expr(e, ..) => Some(e), + StmtKind::Local(local) => local.init.as_deref(), + StmtKind::Item(..) => None, + } +} + +fn never_loop_expr(expr: &Expr<'_>, main_loop_id: HirId) -> NeverLoopResult { + match expr.kind { + ExprKind::Box(e) + | ExprKind::Unary(_, e) + | ExprKind::Cast(e, _) + | ExprKind::Type(e, _) + | ExprKind::Field(e, _) + | ExprKind::AddrOf(_, _, e) + | ExprKind::Struct(_, _, Some(e)) + | ExprKind::Repeat(e, _) + | ExprKind::DropTemps(e) => never_loop_expr(e, main_loop_id), + ExprKind::Array(es) | ExprKind::MethodCall(_, _, es, _) | ExprKind::Tup(es) => { + never_loop_expr_all(&mut es.iter(), main_loop_id) + }, + ExprKind::Call(e, es) => never_loop_expr_all(&mut once(e).chain(es.iter()), main_loop_id), + ExprKind::Binary(_, e1, e2) + | ExprKind::Assign(e1, e2, _) + | ExprKind::AssignOp(_, e1, e2) + | ExprKind::Index(e1, e2) => never_loop_expr_all(&mut [e1, e2].iter().cloned(), main_loop_id), + ExprKind::Loop(b, _, _, _) => { + // Break can come from the inner loop so remove them. + absorb_break(&never_loop_block(b, main_loop_id)) + }, + ExprKind::If(e, e2, ref e3) => { + let e1 = never_loop_expr(e, main_loop_id); + let e2 = never_loop_expr(e2, main_loop_id); + let e3 = e3 + .as_ref() + .map_or(NeverLoopResult::Otherwise, |e| never_loop_expr(e, main_loop_id)); + combine_seq(e1, combine_branches(e2, e3)) + }, + ExprKind::Match(e, arms, _) => { + let e = never_loop_expr(e, main_loop_id); + if arms.is_empty() { + e + } else { + let arms = never_loop_expr_branch(&mut arms.iter().map(|a| &*a.body), main_loop_id); + combine_seq(e, arms) + } + }, + ExprKind::Block(b, _) => never_loop_block(b, main_loop_id), + ExprKind::Continue(d) => { + let id = d + .target_id + .expect("target ID can only be missing in the presence of compilation errors"); + if id == main_loop_id { + NeverLoopResult::MayContinueMainLoop + } else { + NeverLoopResult::AlwaysBreak + } + }, + ExprKind::Break(_, ref e) | ExprKind::Ret(ref e) => e.as_ref().map_or(NeverLoopResult::AlwaysBreak, |e| { + combine_seq(never_loop_expr(e, main_loop_id), NeverLoopResult::AlwaysBreak) + }), + ExprKind::InlineAsm(asm) => asm + .operands + .iter() + .map(|(o, _)| match o { + InlineAsmOperand::In { expr, .. } + | InlineAsmOperand::InOut { expr, .. } + | InlineAsmOperand::Sym { expr } => never_loop_expr(expr, main_loop_id), + InlineAsmOperand::Out { expr, .. } => never_loop_expr_all(&mut expr.iter(), main_loop_id), + InlineAsmOperand::SplitInOut { in_expr, out_expr, .. } => { + never_loop_expr_all(&mut once(in_expr).chain(out_expr.iter()), main_loop_id) + }, + InlineAsmOperand::Const { .. } => NeverLoopResult::Otherwise, + }) + .fold(NeverLoopResult::Otherwise, combine_both), + ExprKind::Struct(_, _, None) + | ExprKind::Yield(_, _) + | ExprKind::Closure(_, _, _, _, _) + | ExprKind::LlvmInlineAsm(_) + | ExprKind::Path(_) + | ExprKind::ConstBlock(_) + | ExprKind::Lit(_) + | ExprKind::Err => NeverLoopResult::Otherwise, + } +} + +fn never_loop_expr_all<'a, T: Iterator<Item = &'a Expr<'a>>>(es: &mut T, main_loop_id: HirId) -> NeverLoopResult { + es.map(|e| never_loop_expr(e, main_loop_id)) + .fold(NeverLoopResult::Otherwise, combine_both) +} + +fn never_loop_expr_branch<'a, T: Iterator<Item = &'a Expr<'a>>>(e: &mut T, main_loop_id: HirId) -> NeverLoopResult { + e.map(|e| never_loop_expr(e, main_loop_id)) + .fold(NeverLoopResult::AlwaysBreak, combine_branches) +} diff --git a/src/tools/clippy/clippy_lints/src/loops/same_item_push.rs b/src/tools/clippy/clippy_lints/src/loops/same_item_push.rs new file mode 100644 index 0000000000000..cb2c83e90294a --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/loops/same_item_push.rs @@ -0,0 +1,202 @@ +use super::SAME_ITEM_PUSH; +use clippy_utils::diagnostics::span_lint_and_help; +use clippy_utils::path_to_local; +use clippy_utils::source::snippet_with_macro_callsite; +use clippy_utils::ty::{implements_trait, is_type_diagnostic_item}; +use if_chain::if_chain; +use rustc_data_structures::fx::FxHashSet; +use rustc_hir::def::{DefKind, Res}; +use rustc_hir::intravisit::{walk_expr, NestedVisitorMap, Visitor}; +use rustc_hir::{BindingAnnotation, Block, Expr, ExprKind, HirId, Node, Pat, PatKind, Stmt, StmtKind}; +use rustc_lint::LateContext; +use rustc_middle::hir::map::Map; +use rustc_span::symbol::sym; +use std::iter::Iterator; + +/// Detects for loop pushing the same item into a Vec +pub(super) fn check<'tcx>( + cx: &LateContext<'tcx>, + pat: &'tcx Pat<'_>, + _: &'tcx Expr<'_>, + body: &'tcx Expr<'_>, + _: &'tcx Expr<'_>, +) { + fn emit_lint(cx: &LateContext<'_>, vec: &Expr<'_>, pushed_item: &Expr<'_>) { + let vec_str = snippet_with_macro_callsite(cx, vec.span, ""); + let item_str = snippet_with_macro_callsite(cx, pushed_item.span, ""); + + span_lint_and_help( + cx, + SAME_ITEM_PUSH, + vec.span, + "it looks like the same item is being pushed into this Vec", + None, + &format!( + "try using vec![{};SIZE] or {}.resize(NEW_SIZE, {})", + item_str, vec_str, item_str + ), + ) + } + + if !matches!(pat.kind, PatKind::Wild) { + return; + } + + // Determine whether it is safe to lint the body + let mut same_item_push_visitor = SameItemPushVisitor::new(cx); + walk_expr(&mut same_item_push_visitor, body); + if_chain! { + if same_item_push_visitor.should_lint(); + if let Some((vec, pushed_item)) = same_item_push_visitor.vec_push; + let vec_ty = cx.typeck_results().expr_ty(vec); + let ty = vec_ty.walk().nth(1).unwrap().expect_ty(); + if cx + .tcx + .lang_items() + .clone_trait() + .map_or(false, |id| implements_trait(cx, ty, id, &[])); + then { + // Make sure that the push does not involve possibly mutating values + match pushed_item.kind { + ExprKind::Path(ref qpath) => { + match cx.qpath_res(qpath, pushed_item.hir_id) { + // immutable bindings that are initialized with literal or constant + Res::Local(hir_id) => { + let node = cx.tcx.hir().get(hir_id); + if_chain! { + if let Node::Binding(pat) = node; + if let PatKind::Binding(bind_ann, ..) = pat.kind; + if !matches!(bind_ann, BindingAnnotation::RefMut | BindingAnnotation::Mutable); + let parent_node = cx.tcx.hir().get_parent_node(hir_id); + if let Some(Node::Local(parent_let_expr)) = cx.tcx.hir().find(parent_node); + if let Some(init) = parent_let_expr.init; + then { + match init.kind { + // immutable bindings that are initialized with literal + ExprKind::Lit(..) => emit_lint(cx, vec, pushed_item), + // immutable bindings that are initialized with constant + ExprKind::Path(ref path) => { + if let Res::Def(DefKind::Const, ..) = cx.qpath_res(path, init.hir_id) { + emit_lint(cx, vec, pushed_item); + } + } + _ => {}, + } + } + } + }, + // constant + Res::Def(DefKind::Const, ..) => emit_lint(cx, vec, pushed_item), + _ => {}, + } + }, + ExprKind::Lit(..) => emit_lint(cx, vec, pushed_item), + _ => {}, + } + } + } +} + +// Scans the body of the for loop and determines whether lint should be given +struct SameItemPushVisitor<'a, 'tcx> { + non_deterministic_expr: bool, + multiple_pushes: bool, + // this field holds the last vec push operation visited, which should be the only push seen + vec_push: Option<(&'tcx Expr<'tcx>, &'tcx Expr<'tcx>)>, + cx: &'a LateContext<'tcx>, + used_locals: FxHashSet<HirId>, +} + +impl<'a, 'tcx> SameItemPushVisitor<'a, 'tcx> { + fn new(cx: &'a LateContext<'tcx>) -> Self { + Self { + non_deterministic_expr: false, + multiple_pushes: false, + vec_push: None, + cx, + used_locals: FxHashSet::default(), + } + } + + fn should_lint(&self) -> bool { + if_chain! { + if !self.non_deterministic_expr; + if !self.multiple_pushes; + if let Some((vec, _)) = self.vec_push; + if let Some(hir_id) = path_to_local(vec); + then { + !self.used_locals.contains(&hir_id) + } else { + false + } + } + } +} + +impl<'a, 'tcx> Visitor<'tcx> for SameItemPushVisitor<'a, 'tcx> { + type Map = Map<'tcx>; + + fn visit_expr(&mut self, expr: &'tcx Expr<'_>) { + match &expr.kind { + // Non-determinism may occur ... don't give a lint + ExprKind::Loop(..) | ExprKind::Match(..) | ExprKind::If(..) => self.non_deterministic_expr = true, + ExprKind::Block(block, _) => self.visit_block(block), + _ => { + if let Some(hir_id) = path_to_local(expr) { + self.used_locals.insert(hir_id); + } + walk_expr(self, expr); + }, + } + } + + fn visit_block(&mut self, b: &'tcx Block<'_>) { + for stmt in b.stmts.iter() { + self.visit_stmt(stmt); + } + } + + fn visit_stmt(&mut self, s: &'tcx Stmt<'_>) { + let vec_push_option = get_vec_push(self.cx, s); + if vec_push_option.is_none() { + // Current statement is not a push so visit inside + match &s.kind { + StmtKind::Expr(expr) | StmtKind::Semi(expr) => self.visit_expr(expr), + _ => {}, + } + } else { + // Current statement is a push ...check whether another + // push had been previously done + if self.vec_push.is_none() { + self.vec_push = vec_push_option; + } else { + // There are multiple pushes ... don't lint + self.multiple_pushes = true; + } + } + } + + fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> { + NestedVisitorMap::None + } +} + +// Given some statement, determine if that statement is a push on a Vec. If it is, return +// the Vec being pushed into and the item being pushed +fn get_vec_push<'tcx>(cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'_>) -> Option<(&'tcx Expr<'tcx>, &'tcx Expr<'tcx>)> { + if_chain! { + // Extract method being called + if let StmtKind::Semi(semi_stmt) = &stmt.kind; + if let ExprKind::MethodCall(path, _, args, _) = &semi_stmt.kind; + // Figure out the parameters for the method call + if let Some(self_expr) = args.get(0); + if let Some(pushed_item) = args.get(1); + // Check that the method being called is push() on a Vec + if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(self_expr), sym::vec_type); + if path.ident.name.as_str() == "push"; + then { + return Some((self_expr, pushed_item)) + } + } + None +} diff --git a/src/tools/clippy/clippy_lints/src/loops/single_element_loop.rs b/src/tools/clippy/clippy_lints/src/loops/single_element_loop.rs new file mode 100644 index 0000000000000..fc067e81bcafb --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/loops/single_element_loop.rs @@ -0,0 +1,44 @@ +use super::{get_span_of_entire_for_loop, SINGLE_ELEMENT_LOOP}; +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::single_segment_path; +use clippy_utils::source::{indent_of, snippet}; +use if_chain::if_chain; +use rustc_errors::Applicability; +use rustc_hir::{BorrowKind, Expr, ExprKind, Pat, PatKind}; +use rustc_lint::LateContext; + +pub(super) fn check<'tcx>( + cx: &LateContext<'tcx>, + pat: &'tcx Pat<'_>, + arg: &'tcx Expr<'_>, + body: &'tcx Expr<'_>, + expr: &'tcx Expr<'_>, +) { + if_chain! { + if let ExprKind::AddrOf(BorrowKind::Ref, _, arg_expr) = arg.kind; + if let PatKind::Binding(.., target, _) = pat.kind; + if let ExprKind::Array([arg_expression]) = arg_expr.kind; + if let ExprKind::Path(ref list_item) = arg_expression.kind; + if let Some(list_item_name) = single_segment_path(list_item).map(|ps| ps.ident.name); + if let ExprKind::Block(block, _) = body.kind; + if !block.stmts.is_empty(); + + then { + let for_span = get_span_of_entire_for_loop(expr); + let mut block_str = snippet(cx, block.span, "..").into_owned(); + block_str.remove(0); + block_str.pop(); + + + span_lint_and_sugg( + cx, + SINGLE_ELEMENT_LOOP, + for_span, + "for loop over a single element", + "try", + format!("{{\n{}let {} = &{};{}}}", " ".repeat(indent_of(cx, block.stmts[0].span).unwrap_or(0)), target.name, list_item_name, block_str), + Applicability::MachineApplicable + ) + } + } +} diff --git a/src/tools/clippy/clippy_lints/src/loops/utils.rs b/src/tools/clippy/clippy_lints/src/loops/utils.rs new file mode 100644 index 0000000000000..4db6644b9d705 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/loops/utils.rs @@ -0,0 +1,348 @@ +use clippy_utils::ty::{has_iter_method, implements_trait}; +use clippy_utils::{get_parent_expr, is_integer_const, path_to_local, path_to_local_id, sugg}; +use if_chain::if_chain; +use rustc_errors::Applicability; +use rustc_hir::intravisit::{walk_expr, walk_pat, walk_stmt, NestedVisitorMap, Visitor}; +use rustc_hir::HirIdMap; +use rustc_hir::{BinOpKind, BorrowKind, Expr, ExprKind, HirId, Mutability, Pat, PatKind, Stmt, StmtKind}; +use rustc_lint::LateContext; +use rustc_middle::hir::map::Map; +use rustc_span::source_map::Span; +use rustc_span::symbol::{sym, Symbol}; +use std::iter::Iterator; + +#[derive(Debug, PartialEq)] +enum IncrementVisitorVarState { + Initial, // Not examined yet + IncrOnce, // Incremented exactly once, may be a loop counter + DontWarn, +} + +/// Scan a for loop for variables that are incremented exactly once and not used after that. +pub(super) struct IncrementVisitor<'a, 'tcx> { + cx: &'a LateContext<'tcx>, // context reference + states: HirIdMap<IncrementVisitorVarState>, // incremented variables + depth: u32, // depth of conditional expressions + done: bool, +} + +impl<'a, 'tcx> IncrementVisitor<'a, 'tcx> { + pub(super) fn new(cx: &'a LateContext<'tcx>) -> Self { + Self { + cx, + states: HirIdMap::default(), + depth: 0, + done: false, + } + } + + pub(super) fn into_results(self) -> impl Iterator<Item = HirId> { + self.states.into_iter().filter_map(|(id, state)| { + if state == IncrementVisitorVarState::IncrOnce { + Some(id) + } else { + None + } + }) + } +} + +impl<'a, 'tcx> Visitor<'tcx> for IncrementVisitor<'a, 'tcx> { + type Map = Map<'tcx>; + + fn visit_expr(&mut self, expr: &'tcx Expr<'_>) { + if self.done { + return; + } + + // If node is a variable + if let Some(def_id) = path_to_local(expr) { + if let Some(parent) = get_parent_expr(self.cx, expr) { + let state = self.states.entry(def_id).or_insert(IncrementVisitorVarState::Initial); + if *state == IncrementVisitorVarState::IncrOnce { + *state = IncrementVisitorVarState::DontWarn; + return; + } + + match parent.kind { + ExprKind::AssignOp(op, lhs, rhs) => { + if lhs.hir_id == expr.hir_id { + *state = if op.node == BinOpKind::Add + && is_integer_const(self.cx, rhs, 1) + && *state == IncrementVisitorVarState::Initial + && self.depth == 0 + { + IncrementVisitorVarState::IncrOnce + } else { + // Assigned some other value or assigned multiple times + IncrementVisitorVarState::DontWarn + }; + } + }, + ExprKind::Assign(lhs, _, _) if lhs.hir_id == expr.hir_id => { + *state = IncrementVisitorVarState::DontWarn + }, + ExprKind::AddrOf(BorrowKind::Ref, mutability, _) if mutability == Mutability::Mut => { + *state = IncrementVisitorVarState::DontWarn + }, + _ => (), + } + } + + walk_expr(self, expr); + } else if is_loop(expr) || is_conditional(expr) { + self.depth += 1; + walk_expr(self, expr); + self.depth -= 1; + } else if let ExprKind::Continue(_) = expr.kind { + self.done = true; + } else { + walk_expr(self, expr); + } + } + fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> { + NestedVisitorMap::None + } +} + +enum InitializeVisitorState<'hir> { + Initial, // Not examined yet + Declared(Symbol), // Declared but not (yet) initialized + Initialized { + name: Symbol, + initializer: &'hir Expr<'hir>, + }, + DontWarn, +} + +/// Checks whether a variable is initialized at the start of a loop and not modified +/// and used after the loop. +pub(super) struct InitializeVisitor<'a, 'tcx> { + cx: &'a LateContext<'tcx>, // context reference + end_expr: &'tcx Expr<'tcx>, // the for loop. Stop scanning here. + var_id: HirId, + state: InitializeVisitorState<'tcx>, + depth: u32, // depth of conditional expressions + past_loop: bool, +} + +impl<'a, 'tcx> InitializeVisitor<'a, 'tcx> { + pub(super) fn new(cx: &'a LateContext<'tcx>, end_expr: &'tcx Expr<'tcx>, var_id: HirId) -> Self { + Self { + cx, + end_expr, + var_id, + state: InitializeVisitorState::Initial, + depth: 0, + past_loop: false, + } + } + + pub(super) fn get_result(&self) -> Option<(Symbol, &'tcx Expr<'tcx>)> { + if let InitializeVisitorState::Initialized { name, initializer } = self.state { + Some((name, initializer)) + } else { + None + } + } +} + +impl<'a, 'tcx> Visitor<'tcx> for InitializeVisitor<'a, 'tcx> { + type Map = Map<'tcx>; + + fn visit_stmt(&mut self, stmt: &'tcx Stmt<'_>) { + // Look for declarations of the variable + if_chain! { + if let StmtKind::Local(local) = stmt.kind; + if local.pat.hir_id == self.var_id; + if let PatKind::Binding(.., ident, _) = local.pat.kind; + then { + self.state = local.init.map_or(InitializeVisitorState::Declared(ident.name), |init| { + InitializeVisitorState::Initialized { + initializer: init, + name: ident.name, + } + }) + } + } + walk_stmt(self, stmt); + } + + fn visit_expr(&mut self, expr: &'tcx Expr<'_>) { + if matches!(self.state, InitializeVisitorState::DontWarn) { + return; + } + if expr.hir_id == self.end_expr.hir_id { + self.past_loop = true; + return; + } + // No need to visit expressions before the variable is + // declared + if matches!(self.state, InitializeVisitorState::Initial) { + return; + } + + // If node is the desired variable, see how it's used + if path_to_local_id(expr, self.var_id) { + if self.past_loop { + self.state = InitializeVisitorState::DontWarn; + return; + } + + if let Some(parent) = get_parent_expr(self.cx, expr) { + match parent.kind { + ExprKind::AssignOp(_, lhs, _) if lhs.hir_id == expr.hir_id => { + self.state = InitializeVisitorState::DontWarn; + }, + ExprKind::Assign(lhs, rhs, _) if lhs.hir_id == expr.hir_id => { + self.state = if_chain! { + if self.depth == 0; + if let InitializeVisitorState::Declared(name) + | InitializeVisitorState::Initialized { name, ..} = self.state; + then { + InitializeVisitorState::Initialized { initializer: rhs, name } + } else { + InitializeVisitorState::DontWarn + } + } + }, + ExprKind::AddrOf(BorrowKind::Ref, mutability, _) if mutability == Mutability::Mut => { + self.state = InitializeVisitorState::DontWarn + }, + _ => (), + } + } + + walk_expr(self, expr); + } else if !self.past_loop && is_loop(expr) { + self.state = InitializeVisitorState::DontWarn; + } else if is_conditional(expr) { + self.depth += 1; + walk_expr(self, expr); + self.depth -= 1; + } else { + walk_expr(self, expr); + } + } + + fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> { + NestedVisitorMap::OnlyBodies(self.cx.tcx.hir()) + } +} + +fn is_loop(expr: &Expr<'_>) -> bool { + matches!(expr.kind, ExprKind::Loop(..)) +} + +fn is_conditional(expr: &Expr<'_>) -> bool { + matches!(expr.kind, ExprKind::If(..) | ExprKind::Match(..)) +} + +#[derive(PartialEq, Eq)] +pub(super) enum Nesting { + Unknown, // no nesting detected yet + RuledOut, // the iterator is initialized or assigned within scope + LookFurther, // no nesting detected, no further walk required +} + +use self::Nesting::{LookFurther, RuledOut, Unknown}; + +pub(super) struct LoopNestVisitor { + pub(super) hir_id: HirId, + pub(super) iterator: HirId, + pub(super) nesting: Nesting, +} + +impl<'tcx> Visitor<'tcx> for LoopNestVisitor { + type Map = Map<'tcx>; + + fn visit_stmt(&mut self, stmt: &'tcx Stmt<'_>) { + if stmt.hir_id == self.hir_id { + self.nesting = LookFurther; + } else if self.nesting == Unknown { + walk_stmt(self, stmt); + } + } + + fn visit_expr(&mut self, expr: &'tcx Expr<'_>) { + if self.nesting != Unknown { + return; + } + if expr.hir_id == self.hir_id { + self.nesting = LookFurther; + return; + } + match expr.kind { + ExprKind::Assign(path, _, _) | ExprKind::AssignOp(_, path, _) => { + if path_to_local_id(path, self.iterator) { + self.nesting = RuledOut; + } + }, + _ => walk_expr(self, expr), + } + } + + fn visit_pat(&mut self, pat: &'tcx Pat<'_>) { + if self.nesting != Unknown { + return; + } + if let PatKind::Binding(_, id, ..) = pat.kind { + if id == self.iterator { + self.nesting = RuledOut; + return; + } + } + walk_pat(self, pat) + } + + fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> { + NestedVisitorMap::None + } +} + +// this function assumes the given expression is a `for` loop. +pub(super) fn get_span_of_entire_for_loop(expr: &Expr<'_>) -> Span { + // for some reason this is the only way to get the `Span` + // of the entire `for` loop + if let ExprKind::Match(_, arms, _) = &expr.kind { + arms[0].body.span + } else { + unreachable!() + } +} + +/// If `arg` was the argument to a `for` loop, return the "cleanest" way of writing the +/// actual `Iterator` that the loop uses. +pub(super) fn make_iterator_snippet(cx: &LateContext<'_>, arg: &Expr<'_>, applic_ref: &mut Applicability) -> String { + let impls_iterator = cx.tcx.get_diagnostic_item(sym::Iterator).map_or(false, |id| { + implements_trait(cx, cx.typeck_results().expr_ty(arg), id, &[]) + }); + if impls_iterator { + format!( + "{}", + sugg::Sugg::hir_with_applicability(cx, arg, "_", applic_ref).maybe_par() + ) + } else { + // (&x).into_iter() ==> x.iter() + // (&mut x).into_iter() ==> x.iter_mut() + match &arg.kind { + ExprKind::AddrOf(BorrowKind::Ref, mutability, arg_inner) + if has_iter_method(cx, cx.typeck_results().expr_ty(arg_inner)).is_some() => + { + let meth_name = match mutability { + Mutability::Mut => "iter_mut", + Mutability::Not => "iter", + }; + format!( + "{}.{}()", + sugg::Sugg::hir_with_applicability(cx, arg_inner, "_", applic_ref).maybe_par(), + meth_name, + ) + } + _ => format!( + "{}.into_iter()", + sugg::Sugg::hir_with_applicability(cx, arg, "_", applic_ref).maybe_par() + ), + } + } +} diff --git a/src/tools/clippy/clippy_lints/src/loops/while_immutable_condition.rs b/src/tools/clippy/clippy_lints/src/loops/while_immutable_condition.rs new file mode 100644 index 0000000000000..de267cc77d2b4 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/loops/while_immutable_condition.rs @@ -0,0 +1,138 @@ +use super::WHILE_IMMUTABLE_CONDITION; +use crate::consts::constant; +use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::usage::mutated_variables; +use if_chain::if_chain; +use rustc_hir::def::{DefKind, Res}; +use rustc_hir::def_id::DefIdMap; +use rustc_hir::intravisit::{walk_expr, NestedVisitorMap, Visitor}; +use rustc_hir::HirIdSet; +use rustc_hir::{Expr, ExprKind, QPath}; +use rustc_lint::LateContext; +use rustc_middle::hir::map::Map; + +pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, cond: &'tcx Expr<'_>, expr: &'tcx Expr<'_>) { + if constant(cx, cx.typeck_results(), cond).is_some() { + // A pure constant condition (e.g., `while false`) is not linted. + return; + } + + let mut var_visitor = VarCollectorVisitor { + cx, + ids: HirIdSet::default(), + def_ids: DefIdMap::default(), + skip: false, + }; + var_visitor.visit_expr(cond); + if var_visitor.skip { + return; + } + let used_in_condition = &var_visitor.ids; + let no_cond_variable_mutated = if let Some(used_mutably) = mutated_variables(expr, cx) { + used_in_condition.is_disjoint(&used_mutably) + } else { + return; + }; + let mutable_static_in_cond = var_visitor.def_ids.iter().any(|(_, v)| *v); + + let mut has_break_or_return_visitor = HasBreakOrReturnVisitor { + has_break_or_return: false, + }; + has_break_or_return_visitor.visit_expr(expr); + let has_break_or_return = has_break_or_return_visitor.has_break_or_return; + + if no_cond_variable_mutated && !mutable_static_in_cond { + span_lint_and_then( + cx, + WHILE_IMMUTABLE_CONDITION, + cond.span, + "variables in the condition are not mutated in the loop body", + |diag| { + diag.note("this may lead to an infinite or to a never running loop"); + + if has_break_or_return { + diag.note("this loop contains `return`s or `break`s"); + diag.help("rewrite it as `if cond { loop { } }`"); + } + }, + ); + } +} + +struct HasBreakOrReturnVisitor { + has_break_or_return: bool, +} + +impl<'tcx> Visitor<'tcx> for HasBreakOrReturnVisitor { + type Map = Map<'tcx>; + + fn visit_expr(&mut self, expr: &'tcx Expr<'_>) { + if self.has_break_or_return { + return; + } + + match expr.kind { + ExprKind::Ret(_) | ExprKind::Break(_, _) => { + self.has_break_or_return = true; + return; + }, + _ => {}, + } + + walk_expr(self, expr); + } + + fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> { + NestedVisitorMap::None + } +} + +/// Collects the set of variables in an expression +/// Stops analysis if a function call is found +/// Note: In some cases such as `self`, there are no mutable annotation, +/// All variables definition IDs are collected +struct VarCollectorVisitor<'a, 'tcx> { + cx: &'a LateContext<'tcx>, + ids: HirIdSet, + def_ids: DefIdMap<bool>, + skip: bool, +} + +impl<'a, 'tcx> VarCollectorVisitor<'a, 'tcx> { + fn insert_def_id(&mut self, ex: &'tcx Expr<'_>) { + if_chain! { + if let ExprKind::Path(ref qpath) = ex.kind; + if let QPath::Resolved(None, _) = *qpath; + then { + match self.cx.qpath_res(qpath, ex.hir_id) { + Res::Local(hir_id) => { + self.ids.insert(hir_id); + }, + Res::Def(DefKind::Static, def_id) => { + let mutable = self.cx.tcx.is_mutable_static(def_id); + self.def_ids.insert(def_id, mutable); + }, + _ => {}, + } + } + } + } +} + +impl<'a, 'tcx> Visitor<'tcx> for VarCollectorVisitor<'a, 'tcx> { + type Map = Map<'tcx>; + + fn visit_expr(&mut self, ex: &'tcx Expr<'_>) { + match ex.kind { + ExprKind::Path(_) => self.insert_def_id(ex), + // If there is any function/method call… we just stop analysis + ExprKind::Call(..) | ExprKind::MethodCall(..) => self.skip = true, + + _ => walk_expr(self, ex), + } + } + + fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> { + NestedVisitorMap::None + } +} 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 new file mode 100644 index 0000000000000..9c1720798529d --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/loops/while_let_loop.rs @@ -0,0 +1,88 @@ +use super::WHILE_LET_LOOP; +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::source::snippet_with_applicability; +use rustc_errors::Applicability; +use rustc_hir::{Block, Expr, ExprKind, MatchSource, 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<'_>) { + // 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 + if let Some(inner) = inner_stmt_expr.or_else(|| extract_first_expr(loop_block)) { + if let ExprKind::Match(matchexpr, arms, ref source) = inner.kind { + // ensure "if let" compatible match structure + match *source { + MatchSource::Normal | MatchSource::IfLetDesugar { .. } => { + if arms.len() == 2 + && arms[0].guard.is_none() + && arms[1].guard.is_none() + && is_simple_break_expr(arms[1].body) + { + if in_external_macro(cx.sess(), expr.span) { + return; + } + + // NOTE: we used to build a body here instead of using + // ellipsis, this was removed because: + // 1) it was ugly with big bodies; + // 2) it was not indented properly; + // 3) it wasn’t very smart (see #675). + let mut applicability = Applicability::HasPlaceholders; + span_lint_and_sugg( + cx, + WHILE_LET_LOOP, + expr.span, + "this loop could be written as a `while let` loop", + "try", + format!( + "while let {} = {} {{ .. }}", + snippet_with_applicability(cx, arms[0].pat.span, "..", &mut applicability), + snippet_with_applicability(cx, matchexpr.span, "..", &mut applicability), + ), + applicability, + ); + } + }, + _ => (), + } + } + } +} + +/// If a block begins with a statement (possibly a `let` binding) and has an +/// expression, return it. +fn extract_expr_from_first_stmt<'tcx>(block: &Block<'tcx>) -> Option<&'tcx Expr<'tcx>> { + if block.stmts.is_empty() { + return None; + } + if let StmtKind::Local(local) = block.stmts[0].kind { + local.init //.map(|expr| expr) + } else { + None + } +} + +/// If a block begins with an expression (with or without semicolon), return it. +fn extract_first_expr<'tcx>(block: &Block<'tcx>) -> Option<&'tcx Expr<'tcx>> { + match block.expr { + Some(expr) if block.stmts.is_empty() => Some(expr), + None if !block.stmts.is_empty() => match block.stmts[0].kind { + StmtKind::Expr(expr) | StmtKind::Semi(expr) => Some(expr), + StmtKind::Local(..) | StmtKind::Item(..) => None, + }, + _ => None, + } +} + +/// Returns `true` if expr contains a single break expr without destination label +/// and +/// passed expression. The expression may be within a block. +fn is_simple_break_expr(expr: &Expr<'_>) -> bool { + match expr.kind { + ExprKind::Break(dest, ref passed_expr) if dest.label.is_none() && passed_expr.is_none() => true, + ExprKind::Block(b, _) => extract_first_expr(b).map_or(false, |subexpr| is_simple_break_expr(subexpr)), + _ => false, + } +} 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 new file mode 100644 index 0000000000000..82715d9bafacc --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/loops/while_let_on_iterator.rs @@ -0,0 +1,170 @@ +use super::utils::{LoopNestVisitor, Nesting}; +use super::WHILE_LET_ON_ITERATOR; +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::source::snippet_with_applicability; +use clippy_utils::ty::implements_trait; +use clippy_utils::usage::mutated_variables; +use clippy_utils::{ + get_enclosing_block, is_refutable, is_trait_method, last_path_segment, path_to_local, path_to_local_id, +}; +use if_chain::if_chain; +use rustc_errors::Applicability; +use rustc_hir::intravisit::{walk_block, walk_expr, NestedVisitorMap, Visitor}; +use rustc_hir::{Expr, ExprKind, HirId, MatchSource, Node, PatKind}; +use rustc_lint::LateContext; +use rustc_middle::hir::map::Map; +use rustc_span::symbol::sym; + +pub(super) fn check(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { + if let ExprKind::Match(match_expr, arms, MatchSource::WhileLetDesugar) = expr.kind { + let pat = &arms[0].pat.kind; + if let (&PatKind::TupleStruct(ref qpath, pat_args, _), &ExprKind::MethodCall(method_path, _, method_args, _)) = + (pat, &match_expr.kind) + { + let iter_expr = &method_args[0]; + + // Don't lint when the iterator is recreated on every iteration + if_chain! { + if let ExprKind::MethodCall(..) | ExprKind::Call(..) = iter_expr.kind; + if let Some(iter_def_id) = cx.tcx.get_diagnostic_item(sym::Iterator); + if implements_trait(cx, cx.typeck_results().expr_ty(iter_expr), iter_def_id, &[]); + then { + return; + } + } + + let lhs_constructor = last_path_segment(qpath); + if method_path.ident.name == sym::next + && is_trait_method(cx, match_expr, sym::Iterator) + && lhs_constructor.ident.name == sym::Some + && (pat_args.is_empty() + || !is_refutable(cx, pat_args[0]) + && !is_used_inside(cx, iter_expr, arms[0].body) + && !is_iterator_used_after_while_let(cx, iter_expr) + && !is_nested(cx, expr, &method_args[0])) + { + let mut applicability = Applicability::MachineApplicable; + let iterator = snippet_with_applicability(cx, method_args[0].span, "_", &mut applicability); + let loop_var = if pat_args.is_empty() { + "_".to_string() + } else { + snippet_with_applicability(cx, pat_args[0].span, "_", &mut applicability).into_owned() + }; + span_lint_and_sugg( + cx, + WHILE_LET_ON_ITERATOR, + expr.span.with_hi(match_expr.span.hi()), + "this loop could be written as a `for` loop", + "try", + format!("for {} in {}", loop_var, iterator), + applicability, + ); + } + } + } +} + +fn is_used_inside<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, container: &'tcx Expr<'_>) -> bool { + let def_id = match path_to_local(expr) { + Some(id) => id, + None => return false, + }; + if let Some(used_mutably) = mutated_variables(container, cx) { + if used_mutably.contains(&def_id) { + return true; + } + } + false +} + +fn is_iterator_used_after_while_let<'tcx>(cx: &LateContext<'tcx>, iter_expr: &'tcx Expr<'_>) -> bool { + let def_id = match path_to_local(iter_expr) { + Some(id) => id, + None => return false, + }; + let mut visitor = VarUsedAfterLoopVisitor { + def_id, + iter_expr_id: iter_expr.hir_id, + past_while_let: false, + var_used_after_while_let: false, + }; + if let Some(enclosing_block) = get_enclosing_block(cx, def_id) { + walk_block(&mut visitor, enclosing_block); + } + visitor.var_used_after_while_let +} + +fn is_nested(cx: &LateContext<'_>, match_expr: &Expr<'_>, iter_expr: &Expr<'_>) -> bool { + if_chain! { + if let Some(loop_block) = get_enclosing_block(cx, match_expr.hir_id); + let parent_node = cx.tcx.hir().get_parent_node(loop_block.hir_id); + if let Some(Node::Expr(loop_expr)) = cx.tcx.hir().find(parent_node); + then { + return is_loop_nested(cx, loop_expr, iter_expr) + } + } + false +} + +fn is_loop_nested(cx: &LateContext<'_>, loop_expr: &Expr<'_>, iter_expr: &Expr<'_>) -> bool { + let mut id = loop_expr.hir_id; + let iter_id = if let Some(id) = path_to_local(iter_expr) { + id + } else { + return true; + }; + loop { + let parent = cx.tcx.hir().get_parent_node(id); + if parent == id { + return false; + } + match cx.tcx.hir().find(parent) { + Some(Node::Expr(expr)) => { + if let ExprKind::Loop(..) = expr.kind { + return true; + }; + }, + Some(Node::Block(block)) => { + let mut block_visitor = LoopNestVisitor { + hir_id: id, + iterator: iter_id, + nesting: Nesting::Unknown, + }; + walk_block(&mut block_visitor, block); + if block_visitor.nesting == Nesting::RuledOut { + return false; + } + }, + Some(Node::Stmt(_)) => (), + _ => { + return false; + }, + } + id = parent; + } +} + +struct VarUsedAfterLoopVisitor { + def_id: HirId, + iter_expr_id: HirId, + past_while_let: bool, + var_used_after_while_let: bool, +} + +impl<'tcx> Visitor<'tcx> for VarUsedAfterLoopVisitor { + type Map = Map<'tcx>; + + fn visit_expr(&mut self, expr: &'tcx Expr<'_>) { + if self.past_while_let { + if path_to_local_id(expr, self.def_id) { + self.var_used_after_while_let = true; + } + } else if self.iter_expr_id == expr.hir_id { + self.past_while_let = true; + } + walk_expr(self, expr); + } + fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> { + NestedVisitorMap::None + } +} diff --git a/src/tools/clippy/clippy_lints/src/macro_use.rs b/src/tools/clippy/clippy_lints/src/macro_use.rs index 40f04bd677d52..c506d52e7462a 100644 --- a/src/tools/clippy/clippy_lints/src/macro_use.rs +++ b/src/tools/clippy/clippy_lints/src/macro_use.rs @@ -1,4 +1,6 @@ -use crate::utils::{in_macro, snippet, span_lint_and_sugg}; +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; use rustc_ast::ast; @@ -7,7 +9,7 @@ use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_session::{declare_tool_lint, impl_lint_pass}; -use rustc_span::{edition::Edition, Span}; +use rustc_span::{edition::Edition, sym, Span}; declare_clippy_lint! { /// **What it does:** Checks for `#[macro_use] use...`. @@ -107,10 +109,8 @@ impl<'tcx> LateLintPass<'tcx> for MacroUseImports { if_chain! { if cx.sess().opts.edition >= Edition::Edition2018; if let hir::ItemKind::Use(path, _kind) = &item.kind; - if let Some(mac_attr) = item - .attrs - .iter() - .find(|attr| attr.ident().map(|s| s.to_string()) == Some("macro_use".to_string())); + let attrs = cx.tcx.hir().attrs(item.hir_id()); + if let Some(mac_attr) = attrs.iter().find(|attr| attr.has_name(sym::macro_use)); if let Res::Def(DefKind::Mod, id) = path.res; then { for kid in cx.tcx.item_children(id).iter() { diff --git a/src/tools/clippy/clippy_lints/src/main_recursion.rs b/src/tools/clippy/clippy_lints/src/main_recursion.rs index 1ed3f3de83908..07d8a440aea4c 100644 --- a/src/tools/clippy/clippy_lints/src/main_recursion.rs +++ b/src/tools/clippy/clippy_lints/src/main_recursion.rs @@ -1,10 +1,11 @@ +use clippy_utils::diagnostics::span_lint_and_help; +use clippy_utils::source::snippet; +use clippy_utils::{is_entrypoint_fn, is_no_std_crate}; +use if_chain::if_chain; use rustc_hir::{Crate, Expr, ExprKind, QPath}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_tool_lint, impl_lint_pass}; -use crate::utils::{is_entrypoint_fn, is_no_std_crate, snippet, span_lint_and_help}; -use if_chain::if_chain; - declare_clippy_lint! { /// **What it does:** Checks for recursion using the entrypoint. /// @@ -32,8 +33,8 @@ pub struct MainRecursion { impl_lint_pass!(MainRecursion => [MAIN_RECURSION]); impl LateLintPass<'_> for MainRecursion { - fn check_crate(&mut self, _: &LateContext<'_>, krate: &Crate<'_>) { - self.has_no_std_attr = is_no_std_crate(krate); + fn check_crate(&mut self, cx: &LateContext<'_>, _: &Crate<'_>) { + self.has_no_std_attr = is_no_std_crate(cx); } fn check_expr_post(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) { diff --git a/src/tools/clippy/clippy_lints/src/manual_async_fn.rs b/src/tools/clippy/clippy_lints/src/manual_async_fn.rs index 2e2e693592c88..5d88ff3b99f31 100644 --- a/src/tools/clippy/clippy_lints/src/manual_async_fn.rs +++ b/src/tools/clippy/clippy_lints/src/manual_async_fn.rs @@ -1,5 +1,7 @@ -use crate::utils::paths::FUTURE_FROM_GENERATOR; -use crate::utils::{match_function_call, position_before_rarrow, snippet_block, snippet_opt, span_lint_and_then}; +use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::match_function_call; +use clippy_utils::paths::FUTURE_FROM_GENERATOR; +use clippy_utils::source::{position_before_rarrow, snippet_block, snippet_opt}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::intravisit::FnKind; diff --git a/src/tools/clippy/clippy_lints/src/manual_map.rs b/src/tools/clippy/clippy_lints/src/manual_map.rs index a50a3943bab7b..8c9e3af62f482 100644 --- a/src/tools/clippy/clippy_lints/src/manual_map.rs +++ b/src/tools/clippy/clippy_lints/src/manual_map.rs @@ -1,18 +1,22 @@ -use crate::{ - map_unit_fn::OPTION_MAP_UNIT_FN, - matches::MATCH_AS_REF, - utils::{ - is_allowed, is_type_diagnostic_item, match_def_path, match_var, paths, peel_hir_expr_refs, - peel_mid_ty_refs_is_mutable, snippet_with_applicability, span_lint_and_sugg, - }, -}; +use crate::{map_unit_fn::OPTION_MAP_UNIT_FN, matches::MATCH_AS_REF}; +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::source::{snippet_with_applicability, snippet_with_context}; +use clippy_utils::ty::{can_partially_move_ty, is_type_diagnostic_item, peel_mid_ty_refs_is_mutable}; +use clippy_utils::{in_constant, is_allowed, is_else_clause, match_def_path, match_var, paths, peel_hir_expr_refs}; use rustc_ast::util::parser::PREC_POSTFIX; use rustc_errors::Applicability; -use rustc_hir::{Arm, BindingAnnotation, Block, Expr, ExprKind, Mutability, Pat, PatKind, QPath}; +use rustc_hir::{ + def::Res, + intravisit::{walk_expr, ErasedMap, NestedVisitorMap, Visitor}, + Arm, BindingAnnotation, Block, Expr, ExprKind, MatchSource, Mutability, Pat, PatKind, Path, QPath, +}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::lint::in_external_macro; use rustc_session::{declare_lint_pass, declare_tool_lint}; -use rustc_span::symbol::{sym, Ident}; +use rustc_span::{ + symbol::{sym, Ident}, + SyntaxContext, +}; declare_clippy_lint! { /// **What it does:** Checks for usages of `match` which could be implemented using `map` @@ -43,52 +47,58 @@ declare_lint_pass!(ManualMap => [MANUAL_MAP]); impl LateLintPass<'_> for ManualMap { #[allow(clippy::too_many_lines)] fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - if in_external_macro(cx.sess(), expr.span) { - return; - } - - if let ExprKind::Match(scrutinee, [arm1 @ Arm { guard: None, .. }, arm2 @ Arm { guard: None, .. }], _) = - expr.kind + if let ExprKind::Match( + scrutinee, + [arm1 @ Arm { guard: None, .. }, arm2 @ Arm { guard: None, .. }], + match_kind, + ) = expr.kind { + if in_external_macro(cx.sess(), expr.span) || in_constant(cx, expr.hir_id) { + return; + } + let (scrutinee_ty, ty_ref_count, ty_mutability) = peel_mid_ty_refs_is_mutable(cx.typeck_results().expr_ty(scrutinee)); - if !is_type_diagnostic_item(cx, scrutinee_ty, sym::option_type) - || !is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(expr), sym::option_type) + if !(is_type_diagnostic_item(cx, scrutinee_ty, sym::option_type) + && is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(expr), sym::option_type)) { return; } - let (some_expr, some_pat, pat_ref_count, is_wild_none) = - match (try_parse_pattern(cx, arm1.pat), try_parse_pattern(cx, arm2.pat)) { - (Some(OptionPat::Wild), Some(OptionPat::Some { pattern, ref_count })) - if is_none_expr(cx, arm1.body) => - { - (arm2.body, pattern, ref_count, true) - }, - (Some(OptionPat::None), Some(OptionPat::Some { pattern, ref_count })) - if is_none_expr(cx, arm1.body) => - { - (arm2.body, pattern, ref_count, false) - }, - (Some(OptionPat::Some { pattern, ref_count }), Some(OptionPat::Wild)) - if is_none_expr(cx, arm2.body) => - { - (arm1.body, pattern, ref_count, true) - }, - (Some(OptionPat::Some { pattern, ref_count }), Some(OptionPat::None)) - if is_none_expr(cx, arm2.body) => - { - (arm1.body, pattern, ref_count, false) - }, - _ => return, - }; + let expr_ctxt = expr.span.ctxt(); + let (some_expr, some_pat, pat_ref_count, is_wild_none) = match ( + try_parse_pattern(cx, arm1.pat, expr_ctxt), + try_parse_pattern(cx, arm2.pat, expr_ctxt), + ) { + (Some(OptionPat::Wild), Some(OptionPat::Some { pattern, ref_count })) + if is_none_expr(cx, arm1.body) => + { + (arm2.body, pattern, ref_count, true) + }, + (Some(OptionPat::None), Some(OptionPat::Some { pattern, ref_count })) + if is_none_expr(cx, arm1.body) => + { + (arm2.body, pattern, ref_count, false) + }, + (Some(OptionPat::Some { pattern, ref_count }), Some(OptionPat::Wild)) + if is_none_expr(cx, arm2.body) => + { + (arm1.body, pattern, ref_count, true) + }, + (Some(OptionPat::Some { pattern, ref_count }), Some(OptionPat::None)) + if is_none_expr(cx, arm2.body) => + { + (arm1.body, pattern, ref_count, false) + }, + _ => return, + }; // Top level or patterns aren't allowed in closures. if matches!(some_pat.kind, PatKind::Or(_)) { return; } - let some_expr = match get_some_expr(cx, some_expr) { + let some_expr = match get_some_expr(cx, some_expr, expr_ctxt) { Some(expr) => expr, None => return, }; @@ -99,6 +109,10 @@ impl LateLintPass<'_> for ManualMap { return; } + if !can_move_expr_to_closure(cx, some_expr) { + return; + } + // Determine which binding mode to use. let explicit_ref = some_pat.contains_explicit_ref_binding(); let binding_ref = explicit_ref.or_else(|| (ty_ref_count != pat_ref_count).then(|| ty_mutability)); @@ -111,47 +125,50 @@ impl LateLintPass<'_> for ManualMap { let mut app = Applicability::MachineApplicable; - // Remove address-of expressions from the scrutinee. `as_ref` will be called, - // the type is copyable, or the option is being passed by value. + // Remove address-of expressions from the scrutinee. Either `as_ref` will be called, or + // it's being passed by value. let scrutinee = peel_hir_expr_refs(scrutinee).0; - let scrutinee_str = snippet_with_applicability(cx, scrutinee.span, "_", &mut app); - let scrutinee_str = if expr.precedence().order() < PREC_POSTFIX { - // Parens are needed to chain method calls. - format!("({})", scrutinee_str) - } else { - scrutinee_str.into() - }; + let (scrutinee_str, _) = snippet_with_context(cx, scrutinee.span, expr_ctxt, "..", &mut app); + let scrutinee_str = + if scrutinee.span.ctxt() == expr.span.ctxt() && scrutinee.precedence().order() < PREC_POSTFIX { + format!("({})", scrutinee_str) + } else { + scrutinee_str.into() + }; let body_str = if let PatKind::Binding(annotation, _, some_binding, None) = some_pat.kind { - if let Some(func) = can_pass_as_func(cx, some_binding, some_expr) { - snippet_with_applicability(cx, func.span, "..", &mut app).into_owned() - } else { - if match_var(some_expr, some_binding.name) - && !is_allowed(cx, MATCH_AS_REF, expr.hir_id) - && binding_ref.is_some() - { - return; - } + match can_pass_as_func(cx, some_binding, some_expr) { + Some(func) if func.span.ctxt() == some_expr.span.ctxt() => { + snippet_with_applicability(cx, func.span, "..", &mut app).into_owned() + }, + _ => { + if match_var(some_expr, some_binding.name) + && !is_allowed(cx, MATCH_AS_REF, expr.hir_id) + && binding_ref.is_some() + { + return; + } - // `ref` and `ref mut` annotations were handled earlier. - let annotation = if matches!(annotation, BindingAnnotation::Mutable) { - "mut " - } else { - "" - }; - format!( - "|{}{}| {}", - annotation, - some_binding, - snippet_with_applicability(cx, some_expr.span, "..", &mut app) - ) + // `ref` and `ref mut` annotations were handled earlier. + let annotation = if matches!(annotation, BindingAnnotation::Mutable) { + "mut " + } else { + "" + }; + format!( + "|{}{}| {}", + annotation, + some_binding, + snippet_with_context(cx, some_expr.span, expr_ctxt, "..", &mut app).0 + ) + }, } } else if !is_wild_none && explicit_ref.is_none() { // TODO: handle explicit reference annotations. format!( "|{}| {}", - snippet_with_applicability(cx, some_pat.span, "..", &mut app), - snippet_with_applicability(cx, some_expr.span, "..", &mut app) + snippet_with_context(cx, some_pat.span, expr_ctxt, "..", &mut app).0, + snippet_with_context(cx, some_expr.span, expr_ctxt, "..", &mut app).0 ) } else { // Refutable bindings and mixed reference annotations can't be handled by `map`. @@ -164,13 +181,62 @@ impl LateLintPass<'_> for ManualMap { expr.span, "manual implementation of `Option::map`", "try this", - format!("{}{}.map({})", scrutinee_str, as_ref_str, body_str), + if matches!(match_kind, MatchSource::IfLetDesugar { .. }) && is_else_clause(cx.tcx, expr) { + format!("{{ {}{}.map({}) }}", scrutinee_str, as_ref_str, body_str) + } else { + format!("{}{}.map({})", scrutinee_str, as_ref_str, body_str) + }, app, ); } } } +// Checks if the expression can be moved into a closure as is. +fn can_move_expr_to_closure(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> bool { + struct V<'cx, 'tcx> { + cx: &'cx LateContext<'tcx>, + make_closure: bool, + } + impl Visitor<'tcx> for V<'_, 'tcx> { + type Map = ErasedMap<'tcx>; + fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> { + NestedVisitorMap::None + } + + fn visit_expr(&mut self, e: &'tcx Expr<'_>) { + match e.kind { + ExprKind::Break(..) + | ExprKind::Continue(_) + | ExprKind::Ret(_) + | ExprKind::Yield(..) + | ExprKind::InlineAsm(_) + | ExprKind::LlvmInlineAsm(_) => { + self.make_closure = false; + }, + // Accessing a field of a local value can only be done if the type isn't + // partially moved. + ExprKind::Field(base_expr, _) + if matches!( + base_expr.kind, + ExprKind::Path(QPath::Resolved(_, Path { res: Res::Local(_), .. })) + ) && can_partially_move_ty(self.cx, self.cx.typeck_results().expr_ty(base_expr)) => + { + // TODO: check if the local has been partially moved. Assume it has for now. + self.make_closure = false; + return; + } + _ => (), + }; + walk_expr(self, e); + } + } + + let mut v = V { cx, make_closure: true }; + v.visit_expr(expr); + v.make_closure +} + // 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: Ident, expr: &'tcx Expr<'_>) -> Option<&'tcx Expr<'tcx>> { @@ -198,11 +264,11 @@ enum OptionPat<'a> { // 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<'_>) -> Option<OptionPat<'tcx>> { - fn f(cx: &LateContext<'tcx>, pat: &'tcx Pat<'_>, ref_count: usize) -> Option<OptionPat<'tcx>> { +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>> { match pat.kind { PatKind::Wild => Some(OptionPat::Wild), - PatKind::Ref(pat, _) => f(cx, pat, ref_count + 1), + PatKind::Ref(pat, _) => f(cx, pat, ref_count + 1, ctxt), PatKind::Path(QPath::Resolved(None, path)) if path .res @@ -215,18 +281,19 @@ fn try_parse_pattern(cx: &LateContext<'tcx>, pat: &'tcx Pat<'_>) -> Option<Optio if path .res .opt_def_id() - .map_or(false, |id| match_def_path(cx, id, &paths::OPTION_SOME)) => + .map_or(false, |id| match_def_path(cx, id, &paths::OPTION_SOME)) + && pat.span.ctxt() == ctxt => { Some(OptionPat::Some { pattern, ref_count }) }, _ => None, } } - f(cx, pat, 0) + f(cx, pat, 0, ctxt) } // Checks for an expression wrapped by the `Some` constructor. Returns the contained expression. -fn get_some_expr(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option<&'tcx Expr<'tcx>> { +fn get_some_expr(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, ctxt: SyntaxContext) -> Option<&'tcx Expr<'tcx>> { // TODO: Allow more complex expressions. match expr.kind { ExprKind::Call( @@ -235,7 +302,7 @@ fn get_some_expr(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option<&'tcx E .. }, [arg], - ) => { + ) if ctxt == expr.span.ctxt() => { if match_def_path(cx, path.res.opt_def_id()?, &paths::OPTION_SOME) { Some(arg) } else { @@ -249,7 +316,7 @@ fn get_some_expr(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option<&'tcx E .. }, _, - ) => get_some_expr(cx, expr), + ) => get_some_expr(cx, expr, ctxt), _ => None, } } diff --git a/src/tools/clippy/clippy_lints/src/manual_non_exhaustive.rs b/src/tools/clippy/clippy_lints/src/manual_non_exhaustive.rs index 91849e748878f..dc19805b50abd 100644 --- a/src/tools/clippy/clippy_lints/src/manual_non_exhaustive.rs +++ b/src/tools/clippy/clippy_lints/src/manual_non_exhaustive.rs @@ -1,7 +1,9 @@ -use crate::utils::{meets_msrv, snippet_opt, span_lint_and_then}; +use clippy_utils::attrs::is_doc_hidden; +use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::meets_msrv; +use clippy_utils::source::snippet_opt; use if_chain::if_chain; -use rustc_ast::ast::{Attribute, Item, ItemKind, StructField, Variant, VariantData, VisibilityKind}; -use rustc_attr as attr; +use rustc_ast::ast::{FieldDef, Item, ItemKind, Variant, VariantData, VisibilityKind}; use rustc_errors::Applicability; use rustc_lint::{EarlyContext, EarlyLintPass}; use rustc_semver::RustcVersion; @@ -100,19 +102,11 @@ fn check_manual_non_exhaustive_enum(cx: &EarlyContext<'_>, item: &Item, variants fn is_non_exhaustive_marker(variant: &Variant) -> bool { matches!(variant.data, VariantData::Unit(_)) && variant.ident.as_str().starts_with('_') - && variant.attrs.iter().any(|a| is_doc_hidden(a)) - } - - fn is_doc_hidden(attr: &Attribute) -> bool { - attr.has_name(sym::doc) - && match attr.meta_item_list() { - Some(l) => attr::list_contains_name(&l, sym::hidden), - None => false, - } + && is_doc_hidden(&variant.attrs) } + let mut markers = variants.iter().filter(|v| is_non_exhaustive_marker(v)); if_chain! { - let mut markers = variants.iter().filter(|v| is_non_exhaustive_marker(v)); if let Some(marker) = markers.next(); if markers.count() == 0 && variants.len() > 1; then { @@ -142,11 +136,11 @@ fn check_manual_non_exhaustive_enum(cx: &EarlyContext<'_>, item: &Item, variants } fn check_manual_non_exhaustive_struct(cx: &EarlyContext<'_>, item: &Item, data: &VariantData) { - fn is_private(field: &StructField) -> bool { + fn is_private(field: &FieldDef) -> bool { matches!(field.vis.kind, VisibilityKind::Inherited) } - fn is_non_exhaustive_marker(field: &StructField) -> bool { + fn is_non_exhaustive_marker(field: &FieldDef) -> bool { is_private(field) && field.ty.kind.is_unit() && field.ident.map_or(true, |n| n.as_str().starts_with('_')) } 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 efb05b8ffdf4f..9bfae602c407d 100644 --- a/src/tools/clippy/clippy_lints/src/manual_ok_or.rs +++ b/src/tools/clippy/clippy_lints/src/manual_ok_or.rs @@ -1,7 +1,7 @@ -use crate::utils::{ - indent_of, is_type_diagnostic_item, match_qpath, path_to_local_id, paths, reindent_multiline, snippet_opt, - span_lint_and_sugg, -}; +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::source::{indent_of, reindent_multiline, snippet_opt}; +use clippy_utils::ty::is_type_diagnostic_item; +use clippy_utils::{match_qpath, path_to_local_id, paths}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind, PatKind}; diff --git a/src/tools/clippy/clippy_lints/src/manual_strip.rs b/src/tools/clippy/clippy_lints/src/manual_strip.rs index 42a92104a4919..dfa464ddb81ac 100644 --- a/src/tools/clippy/clippy_lints/src/manual_strip.rs +++ b/src/tools/clippy/clippy_lints/src/manual_strip.rs @@ -1,9 +1,8 @@ use crate::consts::{constant, Constant}; -use crate::utils::usage::mutated_variables; -use crate::utils::{ - eq_expr_value, higher, match_def_path, meets_msrv, multispan_sugg, paths, snippet, span_lint_and_then, -}; - +use clippy_utils::diagnostics::{multispan_sugg, span_lint_and_then}; +use clippy_utils::source::snippet; +use clippy_utils::usage::mutated_variables; +use clippy_utils::{eq_expr_value, higher, match_def_path, meets_msrv, paths}; use if_chain::if_chain; use rustc_ast::ast::LitKind; use rustc_hir::def::Res; @@ -92,7 +91,7 @@ impl<'tcx> LateLintPass<'tcx> for ManualStrip { } else { return; }; - let target_res = cx.qpath_res(&target_path, target_arg.hir_id); + let target_res = cx.qpath_res(target_path, target_arg.hir_id); if target_res == Res::Err { return; }; @@ -175,7 +174,7 @@ fn eq_pattern_length<'tcx>(cx: &LateContext<'tcx>, pattern: &Expr<'_>, expr: &'t // Tests if `expr` is a `&str`. fn is_ref_str(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { - match cx.typeck_results().expr_ty_adjusted(&expr).kind() { + match cx.typeck_results().expr_ty_adjusted(expr).kind() { ty::Ref(_, ty, _) => ty.is_str(), _ => false, } 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 b452225b5db6c..f296d6a1a15f5 100644 --- a/src/tools/clippy/clippy_lints/src/manual_unwrap_or.rs +++ b/src/tools/clippy/clippy_lints/src/manual_unwrap_or.rs @@ -1,6 +1,9 @@ use crate::consts::constant_simple; -use crate::utils; -use crate::utils::{path_to_local_id, sugg}; +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::source::{indent_of, reindent_multiline, snippet_opt}; +use clippy_utils::ty::is_type_diagnostic_item; +use clippy_utils::usage::contains_return_break_continue_macro; +use clippy_utils::{in_constant, match_qpath, path_to_local_id, paths, sugg}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::{Arm, Expr, ExprKind, Pat, PatKind}; @@ -42,7 +45,7 @@ declare_lint_pass!(ManualUnwrapOr => [MANUAL_UNWRAP_OR]); impl LateLintPass<'_> for ManualUnwrapOr { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { - if in_external_macro(cx.sess(), expr.span) { + if in_external_macro(cx.sess(), expr.span) || in_constant(cx, expr.hir_id) { return; } lint_manual_unwrap_or(cx, expr); @@ -72,19 +75,19 @@ fn lint_manual_unwrap_or<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { if let Some((idx, or_arm)) = arms.iter().enumerate().find(|(_, arm)| match arm.pat.kind { PatKind::Path(ref some_qpath) => - utils::match_qpath(some_qpath, &utils::paths::OPTION_NONE), + match_qpath(some_qpath, &paths::OPTION_NONE), PatKind::TupleStruct(ref err_qpath, &[Pat { kind: PatKind::Wild, .. }], _) => - utils::match_qpath(err_qpath, &utils::paths::RESULT_ERR), + match_qpath(err_qpath, &paths::RESULT_ERR), _ => false, } ); let unwrap_arm = &arms[1 - idx]; if let PatKind::TupleStruct(ref unwrap_qpath, &[unwrap_pat], _) = unwrap_arm.pat.kind; - if utils::match_qpath(unwrap_qpath, &utils::paths::OPTION_SOME) - || utils::match_qpath(unwrap_qpath, &utils::paths::RESULT_OK); + if match_qpath(unwrap_qpath, &paths::OPTION_SOME) + || match_qpath(unwrap_qpath, &paths::RESULT_OK); if let PatKind::Binding(_, binding_hir_id, ..) = unwrap_pat.kind; if path_to_local_id(unwrap_arm.body, binding_hir_id); - if !utils::usage::contains_return_break_continue_macro(or_arm.body); + if !contains_return_break_continue_macro(or_arm.body); then { Some(or_arm) } else { @@ -96,21 +99,21 @@ fn lint_manual_unwrap_or<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { if_chain! { if let ExprKind::Match(scrutinee, match_arms, _) = expr.kind; let ty = cx.typeck_results().expr_ty(scrutinee); - if let Some(case) = if utils::is_type_diagnostic_item(cx, ty, sym::option_type) { + if let Some(case) = if is_type_diagnostic_item(cx, ty, sym::option_type) { Some(Case::Option) - } else if utils::is_type_diagnostic_item(cx, ty, sym::result_type) { + } else if is_type_diagnostic_item(cx, ty, sym::result_type) { Some(Case::Result) } else { None }; if let Some(or_arm) = applicable_or_arm(match_arms); - if let Some(or_body_snippet) = utils::snippet_opt(cx, or_arm.body.span); - if let Some(indent) = utils::indent_of(cx, expr.span); + if let Some(or_body_snippet) = snippet_opt(cx, or_arm.body.span); + if let Some(indent) = indent_of(cx, expr.span); if constant_simple(cx, cx.typeck_results(), or_arm.body).is_some(); then { let reindented_or_body = - utils::reindent_multiline(or_body_snippet.into(), true, Some(indent)); - utils::span_lint_and_sugg( + reindent_multiline(or_body_snippet.into(), true, Some(indent)); + span_lint_and_sugg( cx, MANUAL_UNWRAP_OR, expr.span, &format!("this pattern reimplements `{}`", case.unwrap_fn_path()), diff --git a/src/tools/clippy/clippy_lints/src/map_clone.rs b/src/tools/clippy/clippy_lints/src/map_clone.rs index bd0be88028904..99c35ae3bbf4c 100644 --- a/src/tools/clippy/clippy_lints/src/map_clone.rs +++ b/src/tools/clippy/clippy_lints/src/map_clone.rs @@ -1,7 +1,8 @@ -use crate::utils::paths; -use crate::utils::{ - is_copy, is_type_diagnostic_item, match_trait_method, remove_blocks, snippet_with_applicability, span_lint_and_sugg, -}; +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::is_trait_method; +use clippy_utils::remove_blocks; +use clippy_utils::source::snippet_with_applicability; +use clippy_utils::ty::{is_copy, is_type_diagnostic_item}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir as hir; @@ -51,17 +52,17 @@ impl<'tcx> LateLintPass<'tcx> for MapClone { } if_chain! { - if let hir::ExprKind::MethodCall(ref method, _, ref args, _) = e.kind; + if let hir::ExprKind::MethodCall(method, _, args, _) = e.kind; if args.len() == 2; if method.ident.name == sym::map; let ty = cx.typeck_results().expr_ty(&args[0]); - if is_type_diagnostic_item(cx, ty, sym::option_type) || match_trait_method(cx, e, &paths::ITERATOR); + if is_type_diagnostic_item(cx, ty, sym::option_type) || is_trait_method(cx, e, sym::Iterator); if let hir::ExprKind::Closure(_, _, body_id, _, _) = args[1].kind; - let closure_body = cx.tcx.hir().body(body_id); - let closure_expr = remove_blocks(&closure_body.value); then { + let closure_body = cx.tcx.hir().body(body_id); + let closure_expr = remove_blocks(&closure_body.value); match closure_body.params[0].pat.kind { - hir::PatKind::Ref(ref inner, hir::Mutability::Not) => if let hir::PatKind::Binding( + hir::PatKind::Ref(inner, hir::Mutability::Not) => if let hir::PatKind::Binding( hir::BindingAnnotation::Unannotated, .., name, None ) = inner.kind { if ident_eq(name, closure_expr) { @@ -70,16 +71,18 @@ impl<'tcx> LateLintPass<'tcx> for MapClone { }, hir::PatKind::Binding(hir::BindingAnnotation::Unannotated, .., name, None) => { match closure_expr.kind { - hir::ExprKind::Unary(hir::UnOp::Deref, ref inner) => { + hir::ExprKind::Unary(hir::UnOp::Deref, inner) => { if ident_eq(name, inner) { if let ty::Ref(.., Mutability::Not) = cx.typeck_results().expr_ty(inner).kind() { lint(cx, e.span, args[0].span, true); } } }, - hir::ExprKind::MethodCall(ref method, _, [obj], _) => if_chain! { + hir::ExprKind::MethodCall(method, _, [obj], _) => if_chain! { if ident_eq(name, obj) && method.ident.name == sym::clone; - if match_trait_method(cx, closure_expr, &paths::CLONE_TRAIT); + if let Some(fn_id) = cx.typeck_results().type_dependent_def_id(closure_expr.hir_id); + if let Some(trait_id) = cx.tcx.trait_of_item(fn_id); + if cx.tcx.lang_items().clone_trait().map_or(false, |id| id == trait_id); // no autoderefs if !cx.typeck_results().expr_adjustments(obj).iter() .any(|a| matches!(a.kind, Adjust::Deref(Some(..)))); @@ -106,7 +109,7 @@ impl<'tcx> LateLintPass<'tcx> for MapClone { } fn ident_eq(name: Ident, path: &hir::Expr<'_>) -> bool { - if let hir::ExprKind::Path(hir::QPath::Resolved(None, ref path)) = path.kind { + if let hir::ExprKind::Path(hir::QPath::Resolved(None, path)) = path.kind { path.segments.len() == 1 && path.segments[0].ident == name } else { false diff --git a/src/tools/clippy/clippy_lints/src/map_err_ignore.rs b/src/tools/clippy/clippy_lints/src/map_err_ignore.rs index 76fe8e776eafd..425a9734e5fee 100644 --- a/src/tools/clippy/clippy_lints/src/map_err_ignore.rs +++ b/src/tools/clippy/clippy_lints/src/map_err_ignore.rs @@ -1,5 +1,4 @@ -use crate::utils::span_lint_and_help; - +use clippy_utils::diagnostics::span_lint_and_help; use rustc_hir::{CaptureBy, Expr, ExprKind, PatKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; @@ -113,7 +112,7 @@ impl<'tcx> LateLintPass<'tcx> for MapErrIgnore { } // check if this is a method call (e.g. x.foo()) - if let ExprKind::MethodCall(ref method, _t_span, ref args, _) = e.kind { + if let ExprKind::MethodCall(method, _t_span, args, _) = e.kind { // only work if the method name is `map_err` and there are only 2 arguments (e.g. x.map_err(|_|[1] // Enum::Variant[2])) if method.ident.as_str() == "map_err" && args.len() == 2 { diff --git a/src/tools/clippy/clippy_lints/src/map_identity.rs b/src/tools/clippy/clippy_lints/src/map_identity.rs index 9f9c108a85a05..e7719e7663d64 100644 --- a/src/tools/clippy/clippy_lints/src/map_identity.rs +++ b/src/tools/clippy/clippy_lints/src/map_identity.rs @@ -1,7 +1,6 @@ -use crate::utils::{ - is_adjusted, is_type_diagnostic_item, match_path, match_trait_method, match_var, paths, remove_blocks, - span_lint_and_sugg, -}; +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::ty::is_type_diagnostic_item; +use clippy_utils::{is_adjusted, is_trait_method, match_path, match_var, paths, remove_blocks}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::{Body, Expr, ExprKind, Pat, PatKind, QPath, StmtKind}; @@ -62,10 +61,10 @@ impl<'tcx> LateLintPass<'tcx> for MapIdentity { /// map(). Otherwise, returns None. fn get_map_argument<'a>(cx: &LateContext<'_>, expr: &'a Expr<'a>) -> Option<&'a [Expr<'a>]> { if_chain! { - if let ExprKind::MethodCall(ref method, _, ref args, _) = expr.kind; + if let ExprKind::MethodCall(method, _, args, _) = expr.kind; if args.len() == 2 && method.ident.name == sym::map; let caller_ty = cx.typeck_results().expr_ty(&args[0]); - if match_trait_method(cx, expr, &paths::ITERATOR) + if is_trait_method(cx, expr, sym::Iterator) || is_type_diagnostic_item(cx, caller_ty, sym::result_type) || is_type_diagnostic_item(cx, caller_ty, sym::option_type); then { @@ -81,7 +80,7 @@ fn get_map_argument<'a>(cx: &LateContext<'_>, expr: &'a Expr<'a>) -> Option<&'a fn is_expr_identity_function(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { match expr.kind { ExprKind::Closure(_, _, body_id, _, _) => is_body_identity_function(cx, cx.tcx.hir().body(body_id)), - ExprKind::Path(QPath::Resolved(_, ref path)) => match_path(path, &paths::STD_CONVERT_IDENTITY), + ExprKind::Path(QPath::Resolved(_, path)) => match_path(path, &paths::STD_CONVERT_IDENTITY), _ => false, } } @@ -100,12 +99,12 @@ fn is_body_identity_function(cx: &LateContext<'_>, func: &Body<'_>) -> bool { match body.kind { ExprKind::Path(QPath::Resolved(None, _)) => match_expr_param(cx, body, params[0].pat), - ExprKind::Ret(Some(ref ret_val)) => match_expr_param(cx, ret_val, params[0].pat), - ExprKind::Block(ref block, _) => { + ExprKind::Ret(Some(ret_val)) => match_expr_param(cx, ret_val, params[0].pat), + ExprKind::Block(block, _) => { if_chain! { if block.stmts.len() == 1; - if let StmtKind::Semi(ref expr) | StmtKind::Expr(ref expr) = block.stmts[0].kind; - if let ExprKind::Ret(Some(ref ret_val)) = expr.kind; + if let StmtKind::Semi(expr) | StmtKind::Expr(expr) = block.stmts[0].kind; + if let ExprKind::Ret(Some(ret_val)) = expr.kind; then { match_expr_param(cx, ret_val, params[0].pat) } else { diff --git a/src/tools/clippy/clippy_lints/src/map_unit_fn.rs b/src/tools/clippy/clippy_lints/src/map_unit_fn.rs index 01126e86199b4..57cd907e77e8d 100644 --- a/src/tools/clippy/clippy_lints/src/map_unit_fn.rs +++ b/src/tools/clippy/clippy_lints/src/map_unit_fn.rs @@ -1,4 +1,7 @@ -use crate::utils::{is_type_diagnostic_item, iter_input_pats, method_chain_args, snippet, span_lint_and_then}; +use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::source::snippet; +use clippy_utils::ty::is_type_diagnostic_item; +use clippy_utils::{iter_input_pats, method_chain_args}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir as hir; @@ -130,7 +133,7 @@ fn reduce_unit_expression<'a>(cx: &LateContext<'_>, expr: &'a hir::Expr<'_>) -> // Calls can't be reduced any more Some(expr.span) }, - hir::ExprKind::Block(ref block, _) => { + hir::ExprKind::Block(block, _) => { match (block.stmts, block.expr.as_ref()) { (&[], Some(inner_expr)) => { // If block only contains an expression, @@ -141,8 +144,8 @@ fn reduce_unit_expression<'a>(cx: &LateContext<'_>, expr: &'a hir::Expr<'_>) -> // If block only contains statements, // reduce `{ X; }` to `X` or `X;` match inner_stmt.kind { - hir::StmtKind::Local(ref local) => Some(local.span), - hir::StmtKind::Expr(ref e) => Some(e.span), + hir::StmtKind::Local(local) => Some(local.span), + hir::StmtKind::Expr(e) => Some(e.span), hir::StmtKind::Semi(..) => Some(inner_stmt.span), hir::StmtKind::Item(..) => None, } @@ -165,17 +168,15 @@ fn unit_closure<'tcx>( cx: &LateContext<'tcx>, expr: &hir::Expr<'_>, ) -> Option<(&'tcx hir::Param<'tcx>, &'tcx hir::Expr<'tcx>)> { - if let hir::ExprKind::Closure(_, ref decl, inner_expr_id, _, _) = expr.kind { + if_chain! { + if let hir::ExprKind::Closure(_, decl, inner_expr_id, _, _) = expr.kind; let body = cx.tcx.hir().body(inner_expr_id); let body_expr = &body.value; - - if_chain! { - if decl.inputs.len() == 1; - if is_unit_expression(cx, body_expr); - if let Some(binding) = iter_input_pats(&decl, body).next(); - then { - return Some((binding, body_expr)); - } + if decl.inputs.len() == 1; + if is_unit_expression(cx, body_expr); + if let Some(binding) = iter_input_pats(decl, body).next(); + then { + return Some((binding, body_expr)); } } None @@ -266,7 +267,7 @@ impl<'tcx> LateLintPass<'tcx> for MapUnit { return; } - if let hir::StmtKind::Semi(ref expr) = stmt.kind { + if let hir::StmtKind::Semi(expr) = stmt.kind { if let Some(arglists) = method_chain_args(expr, &["map"]) { lint_map_unit_fn(cx, stmt, expr, arglists[0]); } diff --git a/src/tools/clippy/clippy_lints/src/match_on_vec_items.rs b/src/tools/clippy/clippy_lints/src/match_on_vec_items.rs index 086dae9422f9b..ca6fb0831fe22 100644 --- a/src/tools/clippy/clippy_lints/src/match_on_vec_items.rs +++ b/src/tools/clippy/clippy_lints/src/match_on_vec_items.rs @@ -1,4 +1,6 @@ -use crate::utils::{is_type_diagnostic_item, is_type_lang_item, snippet, span_lint_and_sugg}; +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::source::snippet; +use clippy_utils::ty::{is_type_diagnostic_item, is_type_lang_item}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind, LangItem, MatchSource}; @@ -49,7 +51,7 @@ impl<'tcx> LateLintPass<'tcx> for MatchOnVecItems { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { if_chain! { if !in_external_macro(cx.sess(), expr.span); - if let ExprKind::Match(ref match_expr, _, MatchSource::Normal) = expr.kind; + if let ExprKind::Match(match_expr, _, MatchSource::Normal) = expr.kind; if let Some(idx_expr) = is_vec_indexing(cx, match_expr); if let ExprKind::Index(vec, idx) = idx_expr.kind; @@ -76,7 +78,7 @@ impl<'tcx> LateLintPass<'tcx> for MatchOnVecItems { fn is_vec_indexing<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> Option<&'tcx Expr<'tcx>> { if_chain! { - if let ExprKind::Index(ref array, ref index) = expr.kind; + if let ExprKind::Index(array, index) = expr.kind; if is_vector(cx, array); if !is_full_range(cx, index); diff --git a/src/tools/clippy/clippy_lints/src/matches.rs b/src/tools/clippy/clippy_lints/src/matches.rs index efc8b13942507..c8ee0abd3dfef 100644 --- a/src/tools/clippy/clippy_lints/src/matches.rs +++ b/src/tools/clippy/clippy_lints/src/matches.rs @@ -1,33 +1,36 @@ use crate::consts::{constant, miri_to_const, Constant}; -use crate::utils::sugg::Sugg; -use crate::utils::visitors::LocalUsedVisitor; -use crate::utils::{ - expr_block, get_parent_expr, implements_trait, in_macro, indent_of, is_allowed, is_expn_of, is_refutable, - is_type_diagnostic_item, is_wild, match_qpath, match_type, meets_msrv, multispan_sugg, path_to_local, - path_to_local_id, peel_hir_pat_refs, peel_mid_ty_refs, peel_n_hir_expr_refs, remove_blocks, snippet, snippet_block, - snippet_opt, snippet_with_applicability, span_lint_and_help, span_lint_and_note, span_lint_and_sugg, - span_lint_and_then, strip_pat_refs, +use clippy_utils::diagnostics::{ + multispan_sugg, span_lint_and_help, span_lint_and_note, span_lint_and_sugg, span_lint_and_then, }; -use crate::utils::{paths, search_same, SpanlessEq, SpanlessHash}; +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::LocalUsedVisitor; +use clippy_utils::{ + get_parent_expr, in_macro, is_allowed, is_expn_of, is_refutable, is_wild, match_qpath, meets_msrv, path_to_local, + path_to_local_id, peel_hir_pat_refs, peel_n_hir_expr_refs, recurse_or_patterns, remove_blocks, strip_pat_refs, +}; +use clippy_utils::{paths, search_same, SpanlessEq, SpanlessHash}; use if_chain::if_chain; use rustc_ast::ast::LitKind; -use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_errors::Applicability; -use rustc_hir::def::CtorKind; +use rustc_hir::def::{CtorKind, DefKind, Res}; use rustc_hir::{ - Arm, BindingAnnotation, Block, BorrowKind, Expr, ExprKind, Guard, HirId, Local, MatchSource, Mutability, Node, Pat, - PatKind, QPath, RangeEnd, + self as hir, Arm, BindingAnnotation, Block, BorrowKind, Expr, ExprKind, Guard, HirId, Local, MatchSource, + Mutability, Node, Pat, PatKind, PathSegment, QPath, RangeEnd, TyKind, }; +use rustc_hir::{HirIdMap, HirIdSet}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::lint::in_external_macro; -use rustc_middle::ty::{self, Ty, TyS}; +use rustc_middle::ty::{self, Ty, TyS, VariantDef}; use rustc_semver::RustcVersion; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::source_map::{Span, Spanned}; use rustc_span::sym; use std::cmp::Ordering; use std::collections::hash_map::Entry; -use std::collections::Bound; +use std::iter; +use std::ops::Bound; declare_clippy_lint! { /// **What it does:** Checks for matches with a single arm where an `if let` @@ -587,7 +590,7 @@ impl<'tcx> LateLintPass<'tcx> for Matches { lint_match_arms(cx, expr); } - if let ExprKind::Match(ref ex, ref arms, MatchSource::Normal) = expr.kind { + if let ExprKind::Match(ex, arms, MatchSource::Normal) = expr.kind { check_single_match(cx, ex, arms, expr); check_match_bool(cx, ex, arms, expr); check_overlapping_arms(cx, ex, arms); @@ -602,7 +605,7 @@ impl<'tcx> LateLintPass<'tcx> for Matches { check_match_single_binding(cx, ex, arms, expr); } } - if let ExprKind::Match(ref ex, ref arms, _) = expr.kind { + if let ExprKind::Match(ex, arms, _) = expr.kind { check_match_ref_pats(cx, ex, arms, expr); } } @@ -611,14 +614,14 @@ impl<'tcx> LateLintPass<'tcx> for Matches { if_chain! { if !in_external_macro(cx.sess(), local.span); if !in_macro(local.span); - if let Some(ref expr) = local.init; - if let ExprKind::Match(ref target, ref arms, MatchSource::Normal) = expr.kind; + if let Some(expr) = local.init; + if let ExprKind::Match(target, arms, MatchSource::Normal) = expr.kind; if arms.len() == 1 && arms[0].guard.is_none(); if let PatKind::TupleStruct( - QPath::Resolved(None, ref variant_name), ref args, _) = arms[0].pat.kind; + QPath::Resolved(None, variant_name), args, _) = arms[0].pat.kind; if args.len() == 1; - if let PatKind::Binding(_, arg, ..) = strip_pat_refs(&args[0]).kind; - let body = remove_blocks(&arms[0].body); + if let PatKind::Binding(_, arg, ..) = strip_pat_refs(args[0]).kind; + let body = remove_blocks(arms[0].body); if path_to_local_id(body, arg); then { @@ -647,7 +650,7 @@ impl<'tcx> LateLintPass<'tcx> for Matches { if_chain! { if !in_external_macro(cx.sess(), pat.span); if !in_macro(pat.span); - if let PatKind::Struct(QPath::Resolved(_, ref path), fields, true) = pat.kind; + if let PatKind::Struct(QPath::Resolved(_, path), fields, true) = pat.kind; if let Some(def_id) = path.res.opt_def_id(); let ty = cx.tcx.type_of(def_id); if let ty::Adt(def, _) = ty.kind(); @@ -730,8 +733,8 @@ fn report_single_match_single_pattern( format!(" else {}", expr_block(cx, els, None, "..", Some(expr.span))) }); + let (pat, pat_ref_count) = peel_hir_pat_refs(arms[0].pat); let (msg, sugg) = if_chain! { - let (pat, pat_ref_count) = peel_hir_pat_refs(arms[0].pat); if let PatKind::Path(_) | PatKind::Lit(_) = pat.kind; let (ty, ty_ref_count) = peel_mid_ty_refs(cx.typeck_results().expr_ty(ex)); if let Some(trait_id) = cx.tcx.lang_items().structural_peq_trait(); @@ -759,7 +762,7 @@ fn report_single_match_single_pattern( // PartialEq for different reference counts may not exist. "&".repeat(ref_count_diff), snippet(cx, arms[0].pat.span, ".."), - expr_block(cx, &arms[0].body, None, "..", Some(expr.span)), + expr_block(cx, arms[0].body, None, "..", Some(expr.span)), els_str, ); (msg, sugg) @@ -769,7 +772,7 @@ fn report_single_match_single_pattern( "if let {} = {} {}{}", snippet(cx, arms[0].pat.span, ".."), snippet(cx, ex.span, ".."), - expr_block(cx, &arms[0].body, None, "..", Some(expr.span)), + expr_block(cx, arms[0].body, None, "..", Some(expr.span)), els_str, ); (msg, sugg) @@ -807,7 +810,7 @@ fn check_single_match_opt_like( ]; let path = match arms[1].pat.kind { - PatKind::TupleStruct(ref path, ref inner, _) => { + PatKind::TupleStruct(ref path, inner, _) => { // Contains any non wildcard patterns (e.g., `Err(err)`)? if !inner.iter().all(is_wild) { return; @@ -839,7 +842,7 @@ fn check_match_bool(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>], expr: move |diag| { if arms.len() == 2 { // no guards - let exprs = if let PatKind::Lit(ref arm_bool) = arms[0].pat.kind { + let exprs = if let PatKind::Lit(arm_bool) = arms[0].pat.kind { if let ExprKind::Lit(ref lit) = arm_bool.kind { match lit.node { LitKind::Bool(true) => Some((&*arms[0].body, &*arms[1].body)), @@ -915,14 +918,14 @@ fn check_wild_err_arm<'tcx>(cx: &LateContext<'tcx>, ex: &Expr<'tcx>, arms: &[Arm let ex_ty = cx.typeck_results().expr_ty(ex).peel_refs(); if is_type_diagnostic_item(cx, ex_ty, sym::result_type) { for arm in arms { - if let PatKind::TupleStruct(ref path, ref inner, _) = arm.pat.kind { + if let PatKind::TupleStruct(ref path, inner, _) = arm.pat.kind { let path_str = rustc_hir_pretty::to_string(rustc_hir_pretty::NO_ANN, |s| s.print_qpath(path, false)); if path_str == "Err" { let mut matching_wild = inner.iter().any(is_wild); let mut ident_bind_name = String::from("_"); if !matching_wild { // Looking for unused bindings (i.e.: `_e`) - inner.iter().for_each(|pat| { + for pat in inner.iter() { if let PatKind::Binding(_, id, ident, None) = pat.kind { if ident.as_str().starts_with('_') && !LocalUsedVisitor::new(cx, id).check_expr(arm.body) @@ -931,11 +934,11 @@ fn check_wild_err_arm<'tcx>(cx: &LateContext<'tcx>, ex: &Expr<'tcx>, arms: &[Arm matching_wild = true; } } - }); + } } if_chain! { if matching_wild; - if let ExprKind::Block(ref block, _) = arm.body.kind; + if let ExprKind::Block(block, _) = arm.body.kind; if is_panic_block(block); then { // `Err(_)` or `Err(_e)` arm with `panic!` found @@ -954,122 +957,194 @@ fn check_wild_err_arm<'tcx>(cx: &LateContext<'tcx>, ex: &Expr<'tcx>, arms: &[Arm } } -fn check_wild_enum_match(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>]) { - let ty = cx.typeck_results().expr_ty(ex); - if !ty.is_enum() { - // If there isn't a nice closed set of possible values that can be conveniently enumerated, - // don't complain about not enumerating the mall. - return; +enum CommonPrefixSearcher<'a> { + None, + Path(&'a [PathSegment<'a>]), + Mixed, +} +impl CommonPrefixSearcher<'a> { + fn with_path(&mut self, path: &'a [PathSegment<'a>]) { + match path { + [path @ .., _] => self.with_prefix(path), + [] => (), + } } + fn with_prefix(&mut self, path: &'a [PathSegment<'a>]) { + match self { + Self::None => *self = Self::Path(path), + Self::Path(self_path) + if path + .iter() + .map(|p| p.ident.name) + .eq(self_path.iter().map(|p| p.ident.name)) => {}, + Self::Path(_) => *self = Self::Mixed, + Self::Mixed => (), + } + } +} + +fn is_doc_hidden(cx: &LateContext<'_>, variant_def: &VariantDef) -> bool { + let attrs = cx.tcx.get_attrs(variant_def.def_id); + clippy_utils::attrs::is_doc_hidden(attrs) +} + +#[allow(clippy::too_many_lines)] +fn check_wild_enum_match(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>]) { + let ty = cx.typeck_results().expr_ty(ex).peel_refs(); + let adt_def = match ty.kind() { + ty::Adt(adt_def, _) + if adt_def.is_enum() + && !(is_type_diagnostic_item(cx, ty, sym::option_type) + || is_type_diagnostic_item(cx, ty, sym::result_type)) => + { + adt_def + }, + _ => return, + }; + // First pass - check for violation, but don't do much book-keeping because this is hopefully // the uncommon case, and the book-keeping is slightly expensive. let mut wildcard_span = None; let mut wildcard_ident = None; + let mut has_non_wild = false; for arm in arms { - if let PatKind::Wild = arm.pat.kind { - wildcard_span = Some(arm.pat.span); - } else if let PatKind::Binding(_, _, ident, None) = arm.pat.kind { - wildcard_span = Some(arm.pat.span); - wildcard_ident = Some(ident); + match peel_hir_pat_refs(arm.pat).0.kind { + PatKind::Wild => wildcard_span = Some(arm.pat.span), + PatKind::Binding(_, _, ident, None) => { + wildcard_span = Some(arm.pat.span); + wildcard_ident = Some(ident); + }, + _ => has_non_wild = true, } } + let wildcard_span = match wildcard_span { + Some(x) if has_non_wild => x, + _ => return, + }; - if let Some(wildcard_span) = wildcard_span { - // Accumulate the variants which should be put in place of the wildcard because they're not - // already covered. + // Accumulate the variants which should be put in place of the wildcard because they're not + // already covered. + let mut missing_variants: Vec<_> = adt_def.variants.iter().collect(); - let mut missing_variants = vec![]; - if let ty::Adt(def, _) = ty.kind() { - for variant in &def.variants { - missing_variants.push(variant); + let mut path_prefix = CommonPrefixSearcher::None; + for arm in arms { + // Guards mean that this case probably isn't exhaustively covered. Technically + // this is incorrect, as we should really check whether each variant is exhaustively + // covered by the set of guards that cover it, but that's really hard to do. + recurse_or_patterns(arm.pat, |pat| { + let path = match &peel_hir_pat_refs(pat).0.kind { + PatKind::Path(path) => { + #[allow(clippy::match_same_arms)] + let id = match cx.qpath_res(path, pat.hir_id) { + Res::Def(DefKind::Const | DefKind::ConstParam | DefKind::AnonConst, _) => return, + Res::Def(_, id) => id, + _ => return, + }; + if arm.guard.is_none() { + missing_variants.retain(|e| e.ctor_def_id != Some(id)); + } + path + }, + PatKind::TupleStruct(path, patterns, ..) => { + if let Some(id) = cx.qpath_res(path, pat.hir_id).opt_def_id() { + if arm.guard.is_none() && patterns.iter().all(|p| !is_refutable(cx, p)) { + missing_variants.retain(|e| e.ctor_def_id != Some(id)); + } + } + path + }, + PatKind::Struct(path, patterns, ..) => { + if let Some(id) = cx.qpath_res(path, pat.hir_id).opt_def_id() { + if arm.guard.is_none() && patterns.iter().all(|p| !is_refutable(cx, p.pat)) { + missing_variants.retain(|e| e.def_id != id); + } + } + path + }, + _ => return, + }; + match path { + QPath::Resolved(_, path) => path_prefix.with_path(path.segments), + QPath::TypeRelative( + hir::Ty { + kind: TyKind::Path(QPath::Resolved(_, path)), + .. + }, + _, + ) => path_prefix.with_prefix(path.segments), + _ => (), } - } + }); + } - for arm in arms { - if arm.guard.is_some() { - // Guards mean that this case probably isn't exhaustively covered. Technically - // this is incorrect, as we should really check whether each variant is exhaustively - // covered by the set of guards that cover it, but that's really hard to do. - continue; - } - if let PatKind::Path(ref path) = arm.pat.kind { - if let QPath::Resolved(_, p) = path { - missing_variants.retain(|e| e.ctor_def_id != Some(p.res.def_id())); - } - } else if let PatKind::TupleStruct(QPath::Resolved(_, p), ref patterns, ..) = arm.pat.kind { - // Some simple checks for exhaustive patterns. - // There is a room for improvements to detect more cases, - // but it can be more expensive to do so. - let is_pattern_exhaustive = - |pat: &&Pat<'_>| matches!(pat.kind, PatKind::Wild | PatKind::Binding(.., None)); - if patterns.iter().all(is_pattern_exhaustive) { - missing_variants.retain(|e| e.ctor_def_id != Some(p.res.def_id())); + let format_suggestion = |variant: &VariantDef| { + format!( + "{}{}{}{}", + if let Some(ident) = wildcard_ident { + format!("{} @ ", ident.name) + } else { + String::new() + }, + if let CommonPrefixSearcher::Path(path_prefix) = path_prefix { + let mut s = String::new(); + for seg in path_prefix { + s.push_str(&seg.ident.as_str()); + s.push_str("::"); } + s + } else { + let mut s = cx.tcx.def_path_str(adt_def.did); + s.push_str("::"); + s + }, + variant.ident.name, + match variant.ctor_kind { + CtorKind::Fn if variant.fields.len() == 1 => "(_)", + CtorKind::Fn => "(..)", + CtorKind::Const => "", + CtorKind::Fictive => "{ .. }", } - } - - let mut suggestion: Vec<String> = missing_variants - .iter() - .map(|v| { - let suffix = match v.ctor_kind { - CtorKind::Fn => "(..)", - CtorKind::Const | CtorKind::Fictive => "", - }; - let ident_str = if let Some(ident) = wildcard_ident { - format!("{} @ ", ident.name) - } else { - String::new() - }; - // This path assumes that the enum type is imported into scope. - format!("{}{}{}", ident_str, cx.tcx.def_path_str(v.def_id), suffix) - }) - .collect(); - - if suggestion.is_empty() { - return; - } - - let mut message = "wildcard match will miss any future added variants"; + ) + }; - if let ty::Adt(def, _) = ty.kind() { - if def.is_variant_list_non_exhaustive() { - message = "match on non-exhaustive enum doesn't explicitly match all known variants"; - suggestion.push(String::from("_")); - } - } + match missing_variants.as_slice() { + [] => (), + [x] if !adt_def.is_variant_list_non_exhaustive() && !is_doc_hidden(cx, x) => span_lint_and_sugg( + cx, + MATCH_WILDCARD_FOR_SINGLE_VARIANTS, + wildcard_span, + "wildcard matches only a single variant and will also match any future added variants", + "try this", + format_suggestion(x), + Applicability::MaybeIncorrect, + ), + variants => { + let mut suggestions: Vec<_> = variants.iter().cloned().map(format_suggestion).collect(); + let message = if adt_def.is_variant_list_non_exhaustive() { + suggestions.push("_".into()); + "wildcard matches known variants and will also match future added variants" + } else { + "wildcard match will also match any future added variants" + }; - if suggestion.len() == 1 { - // No need to check for non-exhaustive enum as in that case len would be greater than 1 span_lint_and_sugg( cx, - MATCH_WILDCARD_FOR_SINGLE_VARIANTS, + WILDCARD_ENUM_MATCH_ARM, wildcard_span, message, "try this", - suggestion[0].clone(), + suggestions.join(" | "), Applicability::MaybeIncorrect, ) - }; - - span_lint_and_sugg( - cx, - WILDCARD_ENUM_MATCH_ARM, - wildcard_span, - message, - "try this", - suggestion.join(" | "), - Applicability::MaybeIncorrect, - ) - } + }, + }; } // If the block contains only a `panic!` macro (as expression or statement) fn is_panic_block(block: &Block<'_>) -> bool { match (&block.expr, block.stmts.len(), block.stmts.first()) { - (&Some(ref exp), 0, _) => { - is_expn_of(exp.span, "panic").is_some() && is_expn_of(exp.span, "unreachable").is_none() - }, + (&Some(exp), 0, _) => is_expn_of(exp.span, "panic").is_some() && is_expn_of(exp.span, "unreachable").is_none(), (&None, 1, Some(stmt)) => { is_expn_of(stmt.span, "panic").is_some() && is_expn_of(stmt.span, "unreachable").is_none() }, @@ -1080,7 +1155,7 @@ fn is_panic_block(block: &Block<'_>) -> bool { fn check_match_ref_pats(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>], expr: &Expr<'_>) { if has_only_ref_pats(arms) { let mut suggs = Vec::with_capacity(arms.len() + 1); - let (title, msg) = if let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Not, ref inner) = ex.kind { + let (title, msg) = if let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Not, inner) = ex.kind { let span = ex.span.source_callsite(); suggs.push((span, Sugg::hir_with_macro_callsite(cx, inner, "..").to_string())); ( @@ -1097,7 +1172,7 @@ fn check_match_ref_pats(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>], e }; suggs.extend(arms.iter().filter_map(|a| { - if let PatKind::Ref(ref refp, _) = a.pat.kind { + if let PatKind::Ref(refp, _) = a.pat.kind { Some((a.pat.span, snippet(cx, refp.span, "..").to_string())) } else { None @@ -1166,16 +1241,16 @@ fn check_match_as_ref(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>], exp fn check_wild_in_or_pats(cx: &LateContext<'_>, arms: &[Arm<'_>]) { for arm in arms { - if let PatKind::Or(ref fields) = arm.pat.kind { + if let PatKind::Or(fields) = arm.pat.kind { // look for multiple fields in this arm that contains at least one Wild pattern if fields.len() > 1 && fields.iter().any(is_wild) { span_lint_and_help( cx, WILDCARD_IN_OR_PATTERNS, arm.pat.span, - "wildcard pattern covers any other pattern as it will match anyway.", + "wildcard pattern covers any other pattern as it will match anyway", None, - "Consider handling `_` separately.", + "consider handling `_` separately", ); } } @@ -1207,11 +1282,11 @@ fn find_matches_sugg(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>], expr if b0 != b1; let if_guard = &b0_arms[0].guard; if if_guard.is_none() || b0_arms.len() == 1; - if b0_arms[0].attrs.is_empty(); + if cx.tcx.hir().attrs(b0_arms[0].hir_id).is_empty(); if b0_arms[1..].iter() .all(|arm| { find_bool_lit(&arm.body.kind, desugared).map_or(false, |b| b == b0) && - arm.guard.is_none() && arm.attrs.is_empty() + arm.guard.is_none() && cx.tcx.hir().attrs(arm.hir_id).is_empty() }); then { // The suggestion may be incorrect, because some arms can have `cfg` attributes @@ -1232,7 +1307,7 @@ fn find_matches_sugg(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>], expr // strip potential borrows (#6503), but only if the type is a reference let mut ex_new = ex; if let ExprKind::AddrOf(BorrowKind::Ref, .., ex_inner) = ex.kind { - if let ty::Ref(..) = cx.typeck_results().expr_ty(&ex_inner).kind() { + if let ty::Ref(..) = cx.typeck_results().expr_ty(ex_inner).kind() { ex_new = ex_inner; } }; @@ -1284,6 +1359,7 @@ fn find_bool_lit(ex: &ExprKind<'_>, desugared: bool) -> Option<bool> { } } +#[allow(clippy::too_many_lines)] fn check_match_single_binding<'a>(cx: &LateContext<'a>, ex: &Expr<'a>, arms: &[Arm<'_>], expr: &Expr<'_>) { if in_macro(expr.span) || arms.len() != 1 || is_refutable(cx, arms[0].pat) { return; @@ -1308,7 +1384,7 @@ fn check_match_single_binding<'a>(cx: &LateContext<'a>, ex: &Expr<'a>, arms: &[A let matched_vars = ex.span; let bind_names = arms[0].pat.span; - let match_body = remove_blocks(&arms[0].body); + let match_body = remove_blocks(arms[0].body); let mut snippet_body = if match_body.span.from_expansion() { Sugg::hir_with_macro_callsite(cx, match_body, "..").to_string() } else { @@ -1319,13 +1395,13 @@ fn check_match_single_binding<'a>(cx: &LateContext<'a>, ex: &Expr<'a>, arms: &[A match match_body.kind { ExprKind::Block(block, _) => { // macro + expr_ty(body) == () - if block.span.from_expansion() && cx.typeck_results().expr_ty(&match_body).is_unit() { + if block.span.from_expansion() && cx.typeck_results().expr_ty(match_body).is_unit() { snippet_body.push(';'); } }, _ => { // expr_ty(body) == () - if cx.typeck_results().expr_ty(&match_body).is_unit() { + if cx.typeck_results().expr_ty(match_body).is_unit() { snippet_body.push(';'); } }, @@ -1358,7 +1434,18 @@ fn check_match_single_binding<'a>(cx: &LateContext<'a>, ex: &Expr<'a>, arms: &[A indent = " ".repeat(indent_of(cx, bind_names).unwrap_or(0)); cbrace_start = format!("{{\n{}", indent); } - }; + } + // If the parent is already an arm, and the body is another match statement, + // we need curly braces around suggestion + let parent_node_id = cx.tcx.hir().get_parent_node(expr.hir_id); + if let Node::Arm(arm) = &cx.tcx.hir().get(parent_node_id) { + if let ExprKind::Match(..) = arm.body.kind { + cbrace_end = format!("\n{}}}", indent); + // Fix body indent due to the match + indent = " ".repeat(indent_of(cx, bind_names).unwrap_or(0)); + cbrace_start = format!("{{\n{}", indent); + } + } ( expr.span, format!( @@ -1399,8 +1486,8 @@ fn check_match_single_binding<'a>(cx: &LateContext<'a>, ex: &Expr<'a>, arms: &[A /// Returns true if the `ex` match expression is in a local (`let`) statement fn opt_parent_let<'a>(cx: &LateContext<'a>, ex: &Expr<'a>) -> Option<&'a Local<'a>> { + let map = &cx.tcx.hir(); if_chain! { - let map = &cx.tcx.hir(); if let Some(Node::Expr(parent_arm_expr)) = map.find(map.get_parent_node(ex.hir_id)); if let Some(Node::Local(parent_let_expr)) = map.find(map.get_parent_node(parent_arm_expr.hir_id)); then { @@ -1414,10 +1501,7 @@ fn opt_parent_let<'a>(cx: &LateContext<'a>, ex: &Expr<'a>) -> Option<&'a Local<' fn all_ranges<'tcx>(cx: &LateContext<'tcx>, arms: &'tcx [Arm<'_>], ty: Ty<'tcx>) -> Vec<SpannedRange<Constant>> { arms.iter() .flat_map(|arm| { - if let Arm { - ref pat, guard: None, .. - } = *arm - { + if let Arm { pat, guard: None, .. } = *arm { if let PatKind::Range(ref lhs, ref rhs, range_end) = pat.kind { let lhs = match lhs { Some(lhs) => constant(cx, cx.typeck_results(), lhs)?.0, @@ -1437,7 +1521,7 @@ fn all_ranges<'tcx>(cx: &LateContext<'tcx>, arms: &'tcx [Arm<'_>], ty: Ty<'tcx>) }); } - if let PatKind::Lit(ref value) = pat.kind { + if let PatKind::Lit(value) = pat.kind { let value = constant(cx, cx.typeck_results(), value)?.0; return Some(SpannedRange { span: pat.span, @@ -1484,8 +1568,8 @@ fn type_ranges(ranges: &[SpannedRange<Constant>]) -> TypedRanges { fn is_unit_expr(expr: &Expr<'_>) -> bool { match expr.kind { - ExprKind::Tup(ref v) if v.is_empty() => true, - ExprKind::Block(ref b, _) if b.stmts.is_empty() && b.expr.is_none() => true, + ExprKind::Tup(v) if v.is_empty() => true, + ExprKind::Block(b, _) if b.stmts.is_empty() && b.expr.is_none() => true, _ => false, } } @@ -1498,14 +1582,14 @@ fn is_none_arm(arm: &Arm<'_>) -> bool { // Checks if arm has the form `Some(ref v) => Some(v)` (checks for `ref` and `ref mut`) fn is_ref_some_arm(arm: &Arm<'_>) -> Option<BindingAnnotation> { if_chain! { - if let PatKind::TupleStruct(ref path, ref pats, _) = arm.pat.kind; + if let PatKind::TupleStruct(ref path, pats, _) = arm.pat.kind; if pats.len() == 1 && match_qpath(path, &paths::OPTION_SOME); if let PatKind::Binding(rb, .., ident, _) = pats[0].kind; if rb == BindingAnnotation::Ref || rb == BindingAnnotation::RefMut; - if let ExprKind::Call(ref e, ref args) = remove_blocks(&arm.body).kind; + if let ExprKind::Call(e, args) = remove_blocks(arm.body).kind; if let ExprKind::Path(ref some_path) = e.kind; if match_qpath(some_path, &paths::OPTION_SOME) && args.len() == 1; - if let ExprKind::Path(QPath::Resolved(_, ref path2)) = args[0].kind; + if let ExprKind::Path(QPath::Resolved(_, path2)) = args[0].kind; if path2.segments.len() == 1 && ident.name == path2.segments[0].ident.name; then { return Some(rb) @@ -1587,7 +1671,7 @@ where values.sort(); - for (a, b) in values.iter().zip(values.iter().skip(1)) { + for (a, b) in iter::zip(&values, values.iter().skip(1)) { match (a, b) { (&Kind::Start(_, ra), &Kind::End(_, rb)) => { if ra.node != rb.node { @@ -1597,7 +1681,7 @@ where (&Kind::End(a, _), &Kind::Start(b, _)) if a != Bound::Included(b) => (), _ => { // skip if the range `a` is completely included into the range `b` - if let Ordering::Equal | Ordering::Less = a.cmp(&b) { + if let Ordering::Equal | Ordering::Less = a.cmp(b) { let kind_a = Kind::End(a.range().node.1, a.range()); let kind_b = Kind::End(b.range().node.1, b.range()); if let Ordering::Equal | Ordering::Greater = kind_a.cmp(&kind_b) { @@ -1614,7 +1698,9 @@ where mod redundant_pattern_match { use super::REDUNDANT_PATTERN_MATCHING; - use crate::utils::{match_qpath, match_trait_method, paths, snippet, span_lint_and_then}; + use clippy_utils::diagnostics::span_lint_and_then; + use clippy_utils::source::snippet; + use clippy_utils::{is_trait_method, match_qpath, paths}; use if_chain::if_chain; use rustc_ast::ast::LitKind; use rustc_errors::Applicability; @@ -1640,8 +1726,14 @@ mod redundant_pattern_match { arms: &[Arm<'_>], keyword: &'static str, ) { - let good_method = match arms[0].pat.kind { - PatKind::TupleStruct(ref path, ref patterns, _) if patterns.len() == 1 => { + // also look inside refs + let mut kind = &arms[0].pat.kind; + // if we have &None for example, peel it so we can detect "if let None = x" + if let PatKind::Ref(inner, _mutability) = kind { + kind = &inner.kind; + } + let good_method = match kind { + PatKind::TupleStruct(ref path, patterns, _) if patterns.len() == 1 => { if let PatKind::Wild = patterns[0].kind { if match_qpath(path, &paths::RESULT_OK) { "is_ok()" @@ -1679,7 +1771,7 @@ mod redundant_pattern_match { if keyword == "while"; if let ExprKind::MethodCall(method_path, _, _, _) = op.kind; if method_path.ident.name == sym::next; - if match_trait_method(cx, op, &paths::ITERATOR); + if is_trait_method(cx, op, sym::Iterator); then { return; } @@ -1722,8 +1814,8 @@ mod redundant_pattern_match { let found_good_method = match node_pair { ( - PatKind::TupleStruct(ref path_left, ref patterns_left, _), - PatKind::TupleStruct(ref path_right, ref patterns_right, _), + PatKind::TupleStruct(ref path_left, patterns_left, _), + PatKind::TupleStruct(ref path_right, patterns_right, _), ) if patterns_left.len() == 1 && patterns_right.len() == 1 => { if let (PatKind::Wild, PatKind::Wild) = (&patterns_left[0].kind, &patterns_right[0].kind) { find_good_method_for_match( @@ -1750,8 +1842,8 @@ mod redundant_pattern_match { None } }, - (PatKind::TupleStruct(ref path_left, ref patterns, _), PatKind::Path(ref path_right)) - | (PatKind::Path(ref path_left), PatKind::TupleStruct(ref path_right, ref patterns, _)) + (PatKind::TupleStruct(ref path_left, patterns, _), PatKind::Path(ref path_right)) + | (PatKind::Path(ref path_left), PatKind::TupleStruct(ref path_right, patterns, _)) if patterns.len() == 1 => { if let PatKind::Wild = patterns[0].kind { @@ -1873,10 +1965,10 @@ fn test_overlapping() { /// Implementation of `MATCH_SAME_ARMS`. fn lint_match_arms<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>) { - if let ExprKind::Match(_, ref arms, MatchSource::Normal) = expr.kind { + if let ExprKind::Match(_, arms, MatchSource::Normal) = expr.kind { let hash = |&(_, arm): &(usize, &Arm<'_>)| -> u64 { let mut h = SpanlessHash::new(cx); - h.hash_expr(&arm.body); + h.hash_expr(arm.body); h.finish() }; @@ -1884,7 +1976,7 @@ fn lint_match_arms<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>) { let min_index = usize::min(lindex, rindex); let max_index = usize::max(lindex, rindex); - let mut local_map: FxHashMap<HirId, HirId> = FxHashMap::default(); + let mut local_map: HirIdMap<HirId> = HirIdMap::default(); let eq_fallback = |a: &Expr<'_>, b: &Expr<'_>| { if_chain! { if let Some(a_id) = path_to_local(a); @@ -1912,7 +2004,7 @@ fn lint_match_arms<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>) { (min_index..=max_index).all(|index| arms[index].guard.is_none()) && SpanlessEq::new(cx) .expr_fallback(eq_fallback) - .eq_expr(&lhs.body, &rhs.body) + .eq_expr(lhs.body, rhs.body) // these checks could be removed to allow unused bindings && bindings_eq(lhs.pat, local_map.keys().copied().collect()) && bindings_eq(rhs.pat, local_map.values().copied().collect()) @@ -1968,7 +2060,7 @@ fn pat_contains_local(pat: &Pat<'_>, id: HirId) -> bool { } /// Returns true if all the bindings in the `Pat` are in `ids` and vice versa -fn bindings_eq(pat: &Pat<'_>, mut ids: FxHashSet<HirId>) -> bool { +fn bindings_eq(pat: &Pat<'_>, mut ids: HirIdSet) -> bool { let mut result = true; pat.each_binding_or_first(&mut |_, id, _, _| result &= ids.remove(&id)); result && ids.is_empty() diff --git a/src/tools/clippy/clippy_lints/src/mem_discriminant.rs b/src/tools/clippy/clippy_lints/src/mem_discriminant.rs index c71c2ee7d70af..a735c616f6e41 100644 --- a/src/tools/clippy/clippy_lints/src/mem_discriminant.rs +++ b/src/tools/clippy/clippy_lints/src/mem_discriminant.rs @@ -1,10 +1,12 @@ -use crate::utils::{match_def_path, paths, snippet, span_lint_and_then, walk_ptrs_ty_depth}; +use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::source::snippet; +use clippy_utils::ty::walk_ptrs_ty_depth; +use clippy_utils::{match_def_path, paths}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::{BorrowKind, Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; - use std::iter; declare_clippy_lint! { @@ -32,7 +34,7 @@ declare_lint_pass!(MemDiscriminant => [MEM_DISCRIMINANT_NON_ENUM]); impl<'tcx> LateLintPass<'tcx> for MemDiscriminant { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { if_chain! { - if let ExprKind::Call(ref func, ref func_args) = expr.kind; + if let ExprKind::Call(func, func_args) = expr.kind; // is `mem::discriminant` if let ExprKind::Path(ref func_qpath) = func.kind; if let Some(def_id) = cx.qpath_res(func_qpath, func.hir_id).opt_def_id(); @@ -57,7 +59,7 @@ impl<'tcx> LateLintPass<'tcx> for MemDiscriminant { let mut derefs_needed = ptr_depth; let mut cur_expr = param; while derefs_needed > 0 { - if let ExprKind::AddrOf(BorrowKind::Ref, _, ref inner_expr) = cur_expr.kind { + if let ExprKind::AddrOf(BorrowKind::Ref, _, inner_expr) = cur_expr.kind { derefs_needed -= 1; cur_expr = inner_expr; } else { diff --git a/src/tools/clippy/clippy_lints/src/mem_forget.rs b/src/tools/clippy/clippy_lints/src/mem_forget.rs index d34f9761e26f9..a28cb5f32fe8a 100644 --- a/src/tools/clippy/clippy_lints/src/mem_forget.rs +++ b/src/tools/clippy/clippy_lints/src/mem_forget.rs @@ -1,4 +1,5 @@ -use crate::utils::{match_def_path, paths, span_lint}; +use clippy_utils::diagnostics::span_lint; +use clippy_utils::{match_def_path, paths}; use rustc_hir::{Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; @@ -27,7 +28,7 @@ declare_lint_pass!(MemForget => [MEM_FORGET]); impl<'tcx> LateLintPass<'tcx> for MemForget { fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) { - if let ExprKind::Call(ref path_expr, ref args) = e.kind { + if let ExprKind::Call(path_expr, args) = e.kind { if let ExprKind::Path(ref qpath) = path_expr.kind { if let Some(def_id) = cx.qpath_res(qpath, path_expr.hir_id).opt_def_id() { if match_def_path(cx, def_id, &paths::MEM_FORGET) { diff --git a/src/tools/clippy/clippy_lints/src/mem_replace.rs b/src/tools/clippy/clippy_lints/src/mem_replace.rs index 19087b0207714..e1d351aee4547 100644 --- a/src/tools/clippy/clippy_lints/src/mem_replace.rs +++ b/src/tools/clippy/clippy_lints/src/mem_replace.rs @@ -1,9 +1,10 @@ -use crate::utils::{ - in_macro, match_def_path, match_qpath, meets_msrv, paths, snippet, snippet_with_applicability, span_lint_and_help, - span_lint_and_sugg, span_lint_and_then, -}; +use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_sugg, span_lint_and_then}; +use clippy_utils::is_diagnostic_assoc_item; +use clippy_utils::source::{snippet, snippet_with_applicability}; +use clippy_utils::{in_macro, match_def_path, match_qpath, meets_msrv, paths}; use if_chain::if_chain; use rustc_errors::Applicability; +use rustc_hir::def_id::DefId; use rustc_hir::{BorrowKind, Expr, ExprKind, Mutability, QPath}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::lint::in_external_macro; @@ -108,14 +109,14 @@ fn check_replace_option_with_none(cx: &LateContext<'_>, src: &Expr<'_>, dest: &E // argument's type. All that's left is to get // replacee's path. let replaced_path = match dest.kind { - ExprKind::AddrOf(BorrowKind::Ref, Mutability::Mut, ref replaced) => { - if let ExprKind::Path(QPath::Resolved(None, ref replaced_path)) = replaced.kind { + ExprKind::AddrOf(BorrowKind::Ref, Mutability::Mut, replaced) => { + if let ExprKind::Path(QPath::Resolved(None, replaced_path)) = replaced.kind { replaced_path } else { return; } }, - ExprKind::Path(QPath::Resolved(None, ref replaced_path)) => replaced_path, + ExprKind::Path(QPath::Resolved(None, replaced_path)) => replaced_path, _ => return, }; @@ -160,7 +161,7 @@ fn check_replace_with_uninit(cx: &LateContext<'_>, src: &Expr<'_>, dest: &Expr<' } if_chain! { - if let ExprKind::Call(ref repl_func, ref repl_args) = src.kind; + if let ExprKind::Call(repl_func, repl_args) = src.kind; if repl_args.is_empty(); if let ExprKind::Path(ref repl_func_qpath) = repl_func.kind; if let Some(repl_def_id) = cx.qpath_res(repl_func_qpath, repl_func.hir_id).opt_def_id(); @@ -194,33 +195,63 @@ fn check_replace_with_uninit(cx: &LateContext<'_>, src: &Expr<'_>, dest: &Expr<' } } +/// Returns true if the `def_id` associated with the `path` is recognized as a "default-equivalent" +/// constructor from the std library +fn is_default_equivalent_ctor(cx: &LateContext<'_>, def_id: DefId, path: &QPath<'_>) -> bool { + let std_types_symbols = &[ + sym::string_type, + sym::vec_type, + sym::vecdeque_type, + sym::LinkedList, + sym::hashmap_type, + sym::BTreeMap, + sym::hashset_type, + sym::BTreeSet, + sym::BinaryHeap, + ]; + + if std_types_symbols + .iter() + .any(|symbol| is_diagnostic_assoc_item(cx, def_id, *symbol)) + { + if let QPath::TypeRelative(_, method) = path { + if method.ident.name == sym::new { + return true; + } + } + } + + false +} + fn check_replace_with_default(cx: &LateContext<'_>, src: &Expr<'_>, dest: &Expr<'_>, expr_span: Span) { - if let ExprKind::Call(ref repl_func, _) = src.kind { - if_chain! { - if !in_external_macro(cx.tcx.sess, expr_span); - if let ExprKind::Path(ref repl_func_qpath) = repl_func.kind; - if let Some(repl_def_id) = cx.qpath_res(repl_func_qpath, repl_func.hir_id).opt_def_id(); - if match_def_path(cx, repl_def_id, &paths::DEFAULT_TRAIT_METHOD); - then { - span_lint_and_then( - cx, - MEM_REPLACE_WITH_DEFAULT, - expr_span, - "replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take`", - |diag| { - if !in_macro(expr_span) { - let suggestion = format!("std::mem::take({})", snippet(cx, dest.span, "")); + if_chain! { + if let ExprKind::Call(repl_func, _) = src.kind; + if !in_external_macro(cx.tcx.sess, expr_span); + if let ExprKind::Path(ref repl_func_qpath) = repl_func.kind; + if let Some(repl_def_id) = cx.qpath_res(repl_func_qpath, repl_func.hir_id).opt_def_id(); + if is_diagnostic_assoc_item(cx, repl_def_id, sym::Default) + || is_default_equivalent_ctor(cx, repl_def_id, repl_func_qpath); - diag.span_suggestion( - expr_span, - "consider using", - suggestion, - Applicability::MachineApplicable - ); - } + then { + span_lint_and_then( + cx, + MEM_REPLACE_WITH_DEFAULT, + expr_span, + "replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take`", + |diag| { + if !in_macro(expr_span) { + let suggestion = format!("std::mem::take({})", snippet(cx, dest.span, "")); + + diag.span_suggestion( + expr_span, + "consider using", + suggestion, + Applicability::MachineApplicable + ); } - ); - } + } + ); } } } @@ -242,11 +273,11 @@ impl<'tcx> LateLintPass<'tcx> for MemReplace { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { if_chain! { // Check that `expr` is a call to `mem::replace()` - if let ExprKind::Call(ref func, ref func_args) = expr.kind; + if let ExprKind::Call(func, func_args) = expr.kind; if let ExprKind::Path(ref func_qpath) = func.kind; if let Some(def_id) = cx.qpath_res(func_qpath, func.hir_id).opt_def_id(); if match_def_path(cx, def_id, &paths::MEM_REPLACE); - if let [dest, src] = &**func_args; + if let [dest, src] = func_args; then { check_replace_option_with_none(cx, src, dest, expr.span); check_replace_with_uninit(cx, src, dest, expr.span); diff --git a/src/tools/clippy/clippy_lints/src/methods/bind_instead_of_map.rs b/src/tools/clippy/clippy_lints/src/methods/bind_instead_of_map.rs index 540a1484a8558..287bff886bfbf 100644 --- a/src/tools/clippy/clippy_lints/src/methods/bind_instead_of_map.rs +++ b/src/tools/clippy/clippy_lints/src/methods/bind_instead_of_map.rs @@ -1,101 +1,82 @@ use super::{contains_return, BIND_INSTEAD_OF_MAP}; -use crate::utils::{ - in_macro, match_qpath, match_type, method_calls, multispan_sugg_with_applicability, paths, remove_blocks, snippet, - snippet_with_macro_callsite, span_lint_and_sugg, span_lint_and_then, visitors::find_all_ret_expressions, -}; +use clippy_utils::diagnostics::{multispan_sugg_with_applicability, span_lint_and_sugg, span_lint_and_then}; +use clippy_utils::source::{snippet, snippet_with_macro_callsite}; +use clippy_utils::{in_macro, remove_blocks, visitors::find_all_ret_expressions}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir as hir; +use rustc_hir::def::{CtorKind, CtorOf, DefKind, Res}; +use rustc_hir::{LangItem, QPath}; use rustc_lint::LateContext; +use rustc_middle::ty::DefIdTree; use rustc_span::Span; pub(crate) struct OptionAndThenSome; impl BindInsteadOfMap for OptionAndThenSome { - const TYPE_NAME: &'static str = "Option"; - const TYPE_QPATH: &'static [&'static str] = &paths::OPTION; - + const VARIANT_LANG_ITEM: LangItem = LangItem::OptionSome; const BAD_METHOD_NAME: &'static str = "and_then"; - const BAD_VARIANT_NAME: &'static str = "Some"; - const BAD_VARIANT_QPATH: &'static [&'static str] = &paths::OPTION_SOME; - const GOOD_METHOD_NAME: &'static str = "map"; } pub(crate) struct ResultAndThenOk; impl BindInsteadOfMap for ResultAndThenOk { - const TYPE_NAME: &'static str = "Result"; - const TYPE_QPATH: &'static [&'static str] = &paths::RESULT; - + const VARIANT_LANG_ITEM: LangItem = LangItem::ResultOk; const BAD_METHOD_NAME: &'static str = "and_then"; - const BAD_VARIANT_NAME: &'static str = "Ok"; - const BAD_VARIANT_QPATH: &'static [&'static str] = &paths::RESULT_OK; - const GOOD_METHOD_NAME: &'static str = "map"; } pub(crate) struct ResultOrElseErrInfo; impl BindInsteadOfMap for ResultOrElseErrInfo { - const TYPE_NAME: &'static str = "Result"; - const TYPE_QPATH: &'static [&'static str] = &paths::RESULT; - + const VARIANT_LANG_ITEM: LangItem = LangItem::ResultErr; const BAD_METHOD_NAME: &'static str = "or_else"; - const BAD_VARIANT_NAME: &'static str = "Err"; - const BAD_VARIANT_QPATH: &'static [&'static str] = &paths::RESULT_ERR; - const GOOD_METHOD_NAME: &'static str = "map_err"; } pub(crate) trait BindInsteadOfMap { - const TYPE_NAME: &'static str; - const TYPE_QPATH: &'static [&'static str]; - + const VARIANT_LANG_ITEM: LangItem; const BAD_METHOD_NAME: &'static str; - const BAD_VARIANT_NAME: &'static str; - const BAD_VARIANT_QPATH: &'static [&'static str]; - const GOOD_METHOD_NAME: &'static str; - fn no_op_msg() -> String { - format!( + fn no_op_msg(cx: &LateContext<'_>) -> Option<String> { + let variant_id = cx.tcx.lang_items().require(Self::VARIANT_LANG_ITEM).ok()?; + let item_id = cx.tcx.parent(variant_id)?; + Some(format!( "using `{}.{}({})`, which is a no-op", - Self::TYPE_NAME, + cx.tcx.item_name(item_id), Self::BAD_METHOD_NAME, - Self::BAD_VARIANT_NAME - ) + cx.tcx.item_name(variant_id), + )) } - fn lint_msg() -> String { - format!( + fn lint_msg(cx: &LateContext<'_>) -> Option<String> { + let variant_id = cx.tcx.lang_items().require(Self::VARIANT_LANG_ITEM).ok()?; + let item_id = cx.tcx.parent(variant_id)?; + Some(format!( "using `{}.{}(|x| {}(y))`, which is more succinctly expressed as `{}(|x| y)`", - Self::TYPE_NAME, + cx.tcx.item_name(item_id), Self::BAD_METHOD_NAME, - Self::BAD_VARIANT_NAME, + cx.tcx.item_name(variant_id), Self::GOOD_METHOD_NAME - ) + )) } fn lint_closure_autofixable( cx: &LateContext<'_>, expr: &hir::Expr<'_>, - args: &[hir::Expr<'_>], + recv: &hir::Expr<'_>, closure_expr: &hir::Expr<'_>, closure_args_span: Span, ) -> bool { if_chain! { - if let hir::ExprKind::Call(ref some_expr, ref some_args) = closure_expr.kind; - if let hir::ExprKind::Path(ref qpath) = some_expr.kind; - if match_qpath(qpath, Self::BAD_VARIANT_QPATH); - if some_args.len() == 1; + if let hir::ExprKind::Call(some_expr, [inner_expr]) = closure_expr.kind; + if let hir::ExprKind::Path(QPath::Resolved(_, path)) = some_expr.kind; + if Self::is_variant(cx, path.res); + if !contains_return(inner_expr); + if let Some(msg) = Self::lint_msg(cx); then { - let inner_expr = &some_args[0]; - - if contains_return(inner_expr) { - return false; - } - let some_inner_snip = if inner_expr.span.from_expansion() { snippet_with_macro_callsite(cx, inner_expr.span, "_") } else { @@ -103,13 +84,13 @@ pub(crate) trait BindInsteadOfMap { }; let closure_args_snip = snippet(cx, closure_args_span, ".."); - let option_snip = snippet(cx, args[0].span, ".."); + let option_snip = snippet(cx, recv.span, ".."); let note = format!("{}.{}({} {})", option_snip, Self::GOOD_METHOD_NAME, closure_args_snip, some_inner_snip); span_lint_and_sugg( cx, BIND_INSTEAD_OF_MAP, expr.span, - Self::lint_msg().as_ref(), + &msg, "try this", note, Applicability::MachineApplicable, @@ -126,68 +107,84 @@ pub(crate) trait BindInsteadOfMap { let can_sugg: bool = find_all_ret_expressions(cx, closure_expr, |ret_expr| { if_chain! { if !in_macro(ret_expr.span); - if let hir::ExprKind::Call(ref func_path, ref args) = ret_expr.kind; - if let hir::ExprKind::Path(ref qpath) = func_path.kind; - if match_qpath(qpath, Self::BAD_VARIANT_QPATH); - if args.len() == 1; - if !contains_return(&args[0]); + if let hir::ExprKind::Call(func_path, [arg]) = ret_expr.kind; + if let hir::ExprKind::Path(QPath::Resolved(_, path)) = func_path.kind; + if Self::is_variant(cx, path.res); + if !contains_return(arg); then { - suggs.push((ret_expr.span, args[0].span.source_callsite())); + suggs.push((ret_expr.span, arg.span.source_callsite())); true } else { false } } }); - - if can_sugg { - span_lint_and_then(cx, BIND_INSTEAD_OF_MAP, expr.span, Self::lint_msg().as_ref(), |diag| { - multispan_sugg_with_applicability( - diag, - "try this", - Applicability::MachineApplicable, - std::iter::once((*method_calls(expr, 1).2.get(0).unwrap(), Self::GOOD_METHOD_NAME.into())).chain( - suggs - .into_iter() - .map(|(span1, span2)| (span1, snippet(cx, span2, "_").into())), - ), - ) - }); - } - can_sugg + let (span, msg) = if_chain! { + if can_sugg; + if let hir::ExprKind::MethodCall(_, span, ..) = expr.kind; + if let Some(msg) = Self::lint_msg(cx); + then { (span, msg) } else { return false; } + }; + span_lint_and_then(cx, BIND_INSTEAD_OF_MAP, expr.span, &msg, |diag| { + multispan_sugg_with_applicability( + diag, + "try this", + Applicability::MachineApplicable, + std::iter::once((span, Self::GOOD_METHOD_NAME.into())).chain( + suggs + .into_iter() + .map(|(span1, span2)| (span1, snippet(cx, span2, "_").into())), + ), + ) + }); + true } /// Lint use of `_.and_then(|x| Some(y))` for `Option`s - fn lint(cx: &LateContext<'_>, expr: &hir::Expr<'_>, args: &[hir::Expr<'_>]) -> bool { - if !match_type(cx, cx.typeck_results().expr_ty(&args[0]), Self::TYPE_QPATH) { - return false; + fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, recv: &hir::Expr<'_>, arg: &hir::Expr<'_>) -> bool { + if_chain! { + if let Some(adt) = cx.typeck_results().expr_ty(recv).ty_adt_def(); + if let Ok(vid) = cx.tcx.lang_items().require(Self::VARIANT_LANG_ITEM); + if Some(adt.did) == cx.tcx.parent(vid); + then {} else { return false; } } - match args[1].kind { + match arg.kind { hir::ExprKind::Closure(_, _, body_id, closure_args_span, _) => { let closure_body = cx.tcx.hir().body(body_id); let closure_expr = remove_blocks(&closure_body.value); - if Self::lint_closure_autofixable(cx, expr, args, closure_expr, closure_args_span) { + if Self::lint_closure_autofixable(cx, expr, recv, closure_expr, closure_args_span) { true } else { Self::lint_closure(cx, expr, closure_expr) } }, // `_.and_then(Some)` case, which is no-op. - hir::ExprKind::Path(ref qpath) if match_qpath(qpath, Self::BAD_VARIANT_QPATH) => { - span_lint_and_sugg( - cx, - BIND_INSTEAD_OF_MAP, - expr.span, - Self::no_op_msg().as_ref(), - "use the expression directly", - snippet(cx, args[0].span, "..").into(), - Applicability::MachineApplicable, - ); + hir::ExprKind::Path(QPath::Resolved(_, path)) if Self::is_variant(cx, path.res) => { + if let Some(msg) = Self::no_op_msg(cx) { + span_lint_and_sugg( + cx, + BIND_INSTEAD_OF_MAP, + expr.span, + &msg, + "use the expression directly", + snippet(cx, recv.span, "..").into(), + Applicability::MachineApplicable, + ); + } true }, _ => false, } } + + fn is_variant(cx: &LateContext<'_>, res: Res) -> bool { + if let Res::Def(DefKind::Ctor(CtorOf::Variant, CtorKind::Fn), id) = res { + if let Ok(variant_id) = cx.tcx.lang_items().require(Self::VARIANT_LANG_ITEM) { + return cx.tcx.parent(id) == Some(variant_id); + } + } + false + } } diff --git a/src/tools/clippy/clippy_lints/src/methods/bytes_nth.rs b/src/tools/clippy/clippy_lints/src/methods/bytes_nth.rs index defc50ede2243..2ad3e673c574f 100644 --- a/src/tools/clippy/clippy_lints/src/methods/bytes_nth.rs +++ b/src/tools/clippy/clippy_lints/src/methods/bytes_nth.rs @@ -1,39 +1,34 @@ -use crate::utils::{is_type_diagnostic_item, snippet_with_applicability, span_lint_and_sugg}; -use if_chain::if_chain; +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::source::snippet_with_applicability; +use clippy_utils::ty::is_type_diagnostic_item; use rustc_errors::Applicability; -use rustc_hir::{Expr, ExprKind}; +use rustc_hir::Expr; use rustc_lint::LateContext; use rustc_span::sym; use super::BYTES_NTH; -pub(super) fn lints<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>, iter_args: &'tcx [Expr<'tcx>]) { - if_chain! { - if let ExprKind::MethodCall(_, _, ref args, _) = expr.kind; - let ty = cx.typeck_results().expr_ty(&iter_args[0]).peel_refs(); - let caller_type = if is_type_diagnostic_item(cx, ty, sym::string_type) { - Some("String") - } else if ty.is_str() { - Some("str") - } else { - None - }; - if let Some(caller_type) = caller_type; - then { - let mut applicability = Applicability::MachineApplicable; - span_lint_and_sugg( - cx, - BYTES_NTH, - expr.span, - &format!("called `.byte().nth()` on a `{}`", caller_type), - "try", - format!( - "{}.as_bytes().get({})", - snippet_with_applicability(cx, iter_args[0].span, "..", &mut applicability), - snippet_with_applicability(cx, args[1].span, "..", &mut applicability) - ), - applicability, - ); - } - } +pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>, recv: &'tcx Expr<'tcx>, n_arg: &'tcx Expr<'tcx>) { + let ty = cx.typeck_results().expr_ty(recv).peel_refs(); + let caller_type = if ty.is_str() { + "str" + } else if is_type_diagnostic_item(cx, ty, sym::string_type) { + "String" + } else { + return; + }; + let mut applicability = Applicability::MachineApplicable; + span_lint_and_sugg( + cx, + BYTES_NTH, + expr.span, + &format!("called `.byte().nth()` on a `{}`", caller_type), + "try", + format!( + "{}.as_bytes().get({})", + snippet_with_applicability(cx, recv.span, "..", &mut applicability), + snippet_with_applicability(cx, n_arg.span, "..", &mut applicability) + ), + applicability, + ); } diff --git a/src/tools/clippy/clippy_lints/src/methods/chars_cmp.rs b/src/tools/clippy/clippy_lints/src/methods/chars_cmp.rs new file mode 100644 index 0000000000000..514c411876551 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/methods/chars_cmp.rs @@ -0,0 +1,54 @@ +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::source::snippet_with_applicability; +use clippy_utils::{method_chain_args, single_segment_path}; +use if_chain::if_chain; +use rustc_errors::Applicability; +use rustc_hir as hir; +use rustc_lint::LateContext; +use rustc_lint::Lint; +use rustc_middle::ty; +use rustc_span::sym; + +/// Wrapper fn for `CHARS_NEXT_CMP` and `CHARS_LAST_CMP` lints. +pub(super) fn check( + cx: &LateContext<'_>, + info: &crate::methods::BinaryExprInfo<'_>, + chain_methods: &[&str], + lint: &'static Lint, + suggest: &str, +) -> bool { + if_chain! { + if let Some(args) = method_chain_args(info.chain, chain_methods); + if let hir::ExprKind::Call(fun, arg_char) = info.other.kind; + if arg_char.len() == 1; + if let hir::ExprKind::Path(ref qpath) = fun.kind; + if let Some(segment) = single_segment_path(qpath); + if segment.ident.name == sym::Some; + then { + let mut applicability = Applicability::MachineApplicable; + let self_ty = cx.typeck_results().expr_ty_adjusted(&args[0][0]).peel_refs(); + + if *self_ty.kind() != ty::Str { + return false; + } + + span_lint_and_sugg( + cx, + lint, + info.expr.span, + &format!("you should use the `{}` method", suggest), + "like this", + format!("{}{}.{}({})", + if info.eq { "" } else { "!" }, + snippet_with_applicability(cx, args[0][0].span, "..", &mut applicability), + suggest, + snippet_with_applicability(cx, arg_char[0].span, "..", &mut applicability)), + applicability, + ); + + return true; + } + } + + false +} diff --git a/src/tools/clippy/clippy_lints/src/methods/chars_cmp_with_unwrap.rs b/src/tools/clippy/clippy_lints/src/methods/chars_cmp_with_unwrap.rs new file mode 100644 index 0000000000000..4275857757fee --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/methods/chars_cmp_with_unwrap.rs @@ -0,0 +1,44 @@ +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::method_chain_args; +use clippy_utils::source::snippet_with_applicability; +use if_chain::if_chain; +use rustc_ast::ast; +use rustc_errors::Applicability; +use rustc_hir as hir; +use rustc_lint::LateContext; +use rustc_lint::Lint; + +/// Wrapper fn for `CHARS_NEXT_CMP` and `CHARS_LAST_CMP` lints with `unwrap()`. +pub(super) fn check<'tcx>( + cx: &LateContext<'tcx>, + info: &crate::methods::BinaryExprInfo<'_>, + chain_methods: &[&str], + lint: &'static Lint, + suggest: &str, +) -> bool { + if_chain! { + if let Some(args) = method_chain_args(info.chain, chain_methods); + if let hir::ExprKind::Lit(ref lit) = info.other.kind; + if let ast::LitKind::Char(c) = lit.node; + then { + let mut applicability = Applicability::MachineApplicable; + span_lint_and_sugg( + cx, + lint, + info.expr.span, + &format!("you should use the `{}` method", suggest), + "like this", + format!("{}{}.{}('{}')", + if info.eq { "" } else { "!" }, + snippet_with_applicability(cx, args[0][0].span, "..", &mut applicability), + suggest, + c), + applicability, + ); + + true + } else { + false + } + } +} diff --git a/src/tools/clippy/clippy_lints/src/methods/chars_last_cmp.rs b/src/tools/clippy/clippy_lints/src/methods/chars_last_cmp.rs new file mode 100644 index 0000000000000..07bbc5ca1bf46 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/methods/chars_last_cmp.rs @@ -0,0 +1,13 @@ +use crate::methods::chars_cmp; +use rustc_lint::LateContext; + +use super::CHARS_LAST_CMP; + +/// Checks for the `CHARS_LAST_CMP` lint. +pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, info: &crate::methods::BinaryExprInfo<'_>) -> bool { + if chars_cmp::check(cx, info, &["chars", "last"], CHARS_LAST_CMP, "ends_with") { + true + } else { + chars_cmp::check(cx, info, &["chars", "next_back"], CHARS_LAST_CMP, "ends_with") + } +} diff --git a/src/tools/clippy/clippy_lints/src/methods/chars_last_cmp_with_unwrap.rs b/src/tools/clippy/clippy_lints/src/methods/chars_last_cmp_with_unwrap.rs new file mode 100644 index 0000000000000..c29ee0ec8c8ca --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/methods/chars_last_cmp_with_unwrap.rs @@ -0,0 +1,13 @@ +use crate::methods::chars_cmp_with_unwrap; +use rustc_lint::LateContext; + +use super::CHARS_LAST_CMP; + +/// Checks for the `CHARS_LAST_CMP` lint with `unwrap()`. +pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, info: &crate::methods::BinaryExprInfo<'_>) -> bool { + if chars_cmp_with_unwrap::check(cx, info, &["chars", "last", "unwrap"], CHARS_LAST_CMP, "ends_with") { + true + } else { + chars_cmp_with_unwrap::check(cx, info, &["chars", "next_back", "unwrap"], CHARS_LAST_CMP, "ends_with") + } +} diff --git a/src/tools/clippy/clippy_lints/src/methods/chars_next_cmp.rs b/src/tools/clippy/clippy_lints/src/methods/chars_next_cmp.rs new file mode 100644 index 0000000000000..a6701d8830e77 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/methods/chars_next_cmp.rs @@ -0,0 +1,8 @@ +use rustc_lint::LateContext; + +use super::CHARS_NEXT_CMP; + +/// Checks for the `CHARS_NEXT_CMP` lint. +pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, info: &crate::methods::BinaryExprInfo<'_>) -> bool { + crate::methods::chars_cmp::check(cx, info, &["chars", "next"], CHARS_NEXT_CMP, "starts_with") +} diff --git a/src/tools/clippy/clippy_lints/src/methods/chars_next_cmp_with_unwrap.rs b/src/tools/clippy/clippy_lints/src/methods/chars_next_cmp_with_unwrap.rs new file mode 100644 index 0000000000000..28ede28e9358b --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/methods/chars_next_cmp_with_unwrap.rs @@ -0,0 +1,8 @@ +use rustc_lint::LateContext; + +use super::CHARS_NEXT_CMP; + +/// Checks for the `CHARS_NEXT_CMP` lint with `unwrap()`. +pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, info: &crate::methods::BinaryExprInfo<'_>) -> bool { + crate::methods::chars_cmp_with_unwrap::check(cx, info, &["chars", "next", "unwrap"], CHARS_NEXT_CMP, "starts_with") +} diff --git a/src/tools/clippy/clippy_lints/src/methods/clone_on_copy.rs b/src/tools/clippy/clippy_lints/src/methods/clone_on_copy.rs new file mode 100644 index 0000000000000..ce2e8fa8b1074 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/methods/clone_on_copy.rs @@ -0,0 +1,133 @@ +use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then}; +use clippy_utils::get_parent_node; +use clippy_utils::source::snippet_with_context; +use clippy_utils::sugg; +use clippy_utils::ty::is_copy; +use rustc_errors::Applicability; +use rustc_hir::{BindingAnnotation, Expr, ExprKind, MatchSource, Node, PatKind}; +use rustc_lint::LateContext; +use rustc_middle::ty::{self, adjustment::Adjust}; +use rustc_span::symbol::{sym, Symbol}; +use std::iter; + +use super::CLONE_DOUBLE_REF; +use super::CLONE_ON_COPY; + +/// Checks for the `CLONE_ON_COPY` lint. +#[allow(clippy::too_many_lines)] +pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, method_name: Symbol, args: &[Expr<'_>]) { + let arg = match args { + [arg] if method_name == sym::clone => arg, + _ => return, + }; + if cx + .typeck_results() + .type_dependent_def_id(expr.hir_id) + .and_then(|id| cx.tcx.trait_of_item(id)) + .zip(cx.tcx.lang_items().clone_trait()) + .map_or(true, |(x, y)| x != y) + { + return; + } + let arg_adjustments = cx.typeck_results().expr_adjustments(arg); + let arg_ty = arg_adjustments + .last() + .map_or_else(|| cx.typeck_results().expr_ty(arg), |a| a.target); + + let ty = cx.typeck_results().expr_ty(expr); + if let ty::Ref(_, inner, _) = arg_ty.kind() { + if let ty::Ref(_, innermost, _) = inner.kind() { + span_lint_and_then( + cx, + CLONE_DOUBLE_REF, + expr.span, + &format!( + "using `clone` on a double-reference; \ + this will copy the reference of type `{}` instead of cloning the inner type", + ty + ), + |diag| { + if let Some(snip) = sugg::Sugg::hir_opt(cx, arg) { + let mut ty = innermost; + let mut n = 0; + while let ty::Ref(_, inner, _) = ty.kind() { + ty = inner; + n += 1; + } + let refs: String = iter::repeat('&').take(n + 1).collect(); + let derefs: String = iter::repeat('*').take(n).collect(); + let explicit = format!("<{}{}>::clone({})", refs, ty, snip); + diag.span_suggestion( + expr.span, + "try dereferencing it", + format!("{}({}{}).clone()", refs, derefs, snip.deref()), + Applicability::MaybeIncorrect, + ); + diag.span_suggestion( + expr.span, + "or try being explicit if you are sure, that you want to clone a reference", + explicit, + Applicability::MaybeIncorrect, + ); + } + }, + ); + return; // don't report clone_on_copy + } + } + + if is_copy(cx, ty) { + let parent_is_suffix_expr = match get_parent_node(cx.tcx, expr.hir_id) { + Some(Node::Expr(parent)) => match parent.kind { + // &*x is a nop, &x.clone() is not + ExprKind::AddrOf(..) => return, + // (*x).func() is useless, x.clone().func() can work in case func borrows self + ExprKind::MethodCall(_, _, [self_arg, ..], _) + if expr.hir_id == self_arg.hir_id && ty != cx.typeck_results().expr_ty_adjusted(expr) => + { + return; + } + ExprKind::MethodCall(_, _, [self_arg, ..], _) if expr.hir_id == self_arg.hir_id => true, + ExprKind::Match(_, _, MatchSource::TryDesugar | MatchSource::AwaitDesugar) + | ExprKind::Field(..) + | ExprKind::Index(..) => true, + _ => false, + }, + // local binding capturing a reference + Some(Node::Local(l)) + if matches!( + l.pat.kind, + PatKind::Binding(BindingAnnotation::Ref | BindingAnnotation::RefMut, ..) + ) => + { + return; + } + _ => false, + }; + + let mut app = Applicability::MachineApplicable; + let snip = snippet_with_context(cx, arg.span, expr.span.ctxt(), "_", &mut app).0; + + let deref_count = arg_adjustments + .iter() + .take_while(|adj| matches!(adj.kind, Adjust::Deref(_))) + .count(); + let (help, sugg) = if deref_count == 0 { + ("try removing the `clone` call", snip.into()) + } else if parent_is_suffix_expr { + ("try dereferencing it", format!("({}{})", "*".repeat(deref_count), snip)) + } else { + ("try dereferencing it", format!("{}{}", "*".repeat(deref_count), snip)) + }; + + span_lint_and_sugg( + cx, + CLONE_ON_COPY, + expr.span, + &format!("using `clone` on type `{}` which implements the `Copy` trait", ty), + help, + sugg, + app, + ); + } +} diff --git a/src/tools/clippy/clippy_lints/src/methods/clone_on_ref_ptr.rs b/src/tools/clippy/clippy_lints/src/methods/clone_on_ref_ptr.rs new file mode 100644 index 0000000000000..6417bc8130473 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/methods/clone_on_ref_ptr.rs @@ -0,0 +1,43 @@ +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::paths; +use clippy_utils::source::snippet_with_macro_callsite; +use clippy_utils::ty::{is_type_diagnostic_item, match_type}; +use rustc_errors::Applicability; +use rustc_hir as hir; +use rustc_lint::LateContext; +use rustc_middle::ty; +use rustc_span::symbol::{sym, Symbol}; + +use super::CLONE_ON_REF_PTR; + +pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, method_name: Symbol, args: &[hir::Expr<'_>]) { + if !(args.len() == 1 && method_name == sym::clone) { + return; + } + let arg = &args[0]; + let obj_ty = cx.typeck_results().expr_ty(arg).peel_refs(); + + if let ty::Adt(_, subst) = obj_ty.kind() { + let caller_type = if is_type_diagnostic_item(cx, obj_ty, sym::Rc) { + "Rc" + } else if is_type_diagnostic_item(cx, obj_ty, sym::Arc) { + "Arc" + } else if match_type(cx, obj_ty, &paths::WEAK_RC) || match_type(cx, obj_ty, &paths::WEAK_ARC) { + "Weak" + } else { + return; + }; + + let snippet = snippet_with_macro_callsite(cx, arg.span, ".."); + + span_lint_and_sugg( + cx, + CLONE_ON_REF_PTR, + expr.span, + "using `.clone()` on a ref-counted pointer", + "try this", + format!("{}::<{}>::clone(&{})", caller_type, subst.type_at(0), snippet), + Applicability::Unspecified, // Sometimes unnecessary ::<_> after Rc/Arc/Weak + ); + } +} 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 new file mode 100644 index 0000000000000..03cb41697d509 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/methods/expect_fun_call.rs @@ -0,0 +1,202 @@ +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::is_expn_of; +use clippy_utils::source::{snippet, snippet_with_applicability}; +use clippy_utils::ty::is_type_diagnostic_item; +use if_chain::if_chain; +use rustc_errors::Applicability; +use rustc_hir as hir; +use rustc_lint::LateContext; +use rustc_middle::ty; +use rustc_span::source_map::Span; +use rustc_span::symbol::sym; +use std::borrow::Cow; + +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<'_>]) { + // 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> { + let mut arg_root = arg; + loop { + arg_root = match &arg_root.kind { + hir::ExprKind::AddrOf(hir::BorrowKind::Ref, _, expr) => expr, + hir::ExprKind::MethodCall(method_name, _, call_args, _) => { + if call_args.len() == 1 + && (method_name.ident.name == sym::as_str || method_name.ident.name == sym!(as_ref)) + && { + let arg_type = cx.typeck_results().expr_ty(&call_args[0]); + let base_type = arg_type.peel_refs(); + *base_type.kind() == ty::Str || is_type_diagnostic_item(cx, base_type, sym::string_type) + } + { + &call_args[0] + } else { + break; + } + }, + _ => break, + }; + } + arg_root + } + + // Only `&'static str` or `String` can be used directly in the `panic!`. Other types should be + // converted to string. + fn requires_to_string(cx: &LateContext<'_>, arg: &hir::Expr<'_>) -> bool { + let arg_ty = cx.typeck_results().expr_ty(arg); + if is_type_diagnostic_item(cx, arg_ty, sym::string_type) { + return false; + } + if let ty::Ref(_, ty, ..) = arg_ty.kind() { + if *ty.kind() == ty::Str && can_be_static_str(cx, arg) { + return false; + } + }; + true + } + + // Check if an expression could have type `&'static str`, knowing that it + // has type `&str` for some lifetime. + fn can_be_static_str(cx: &LateContext<'_>, arg: &hir::Expr<'_>) -> bool { + match arg.kind { + hir::ExprKind::Lit(_) => true, + hir::ExprKind::Call(fun, _) => { + if let hir::ExprKind::Path(ref p) = fun.kind { + match cx.qpath_res(p, fun.hir_id) { + hir::def::Res::Def(hir::def::DefKind::Fn | hir::def::DefKind::AssocFn, def_id) => matches!( + cx.tcx.fn_sig(def_id).output().skip_binder().kind(), + ty::Ref(ty::ReStatic, ..) + ), + _ => false, + } + } else { + false + } + }, + hir::ExprKind::MethodCall(..) => { + cx.typeck_results() + .type_dependent_def_id(arg.hir_id) + .map_or(false, |method_id| { + matches!( + cx.tcx.fn_sig(method_id).output().skip_binder().kind(), + ty::Ref(ty::ReStatic, ..) + ) + }) + }, + hir::ExprKind::Path(ref p) => matches!( + cx.qpath_res(p, arg.hir_id), + hir::def::Res::Def(hir::def::DefKind::Const | hir::def::DefKind::Static, _) + ), + _ => false, + } + } + + fn generate_format_arg_snippet( + cx: &LateContext<'_>, + a: &hir::Expr<'_>, + applicability: &mut Applicability, + ) -> Vec<String> { + if_chain! { + if let hir::ExprKind::AddrOf(hir::BorrowKind::Ref, _, format_arg) = a.kind; + if let hir::ExprKind::Match(format_arg_expr, _, _) = format_arg.kind; + if let hir::ExprKind::Tup(format_arg_expr_tup) = format_arg_expr.kind; + + then { + format_arg_expr_tup + .iter() + .map(|a| snippet_with_applicability(cx, a.span, "..", applicability).into_owned()) + .collect() + } else { + unreachable!() + } + } + } + + fn is_call(node: &hir::ExprKind<'_>) -> bool { + match node { + hir::ExprKind::AddrOf(hir::BorrowKind::Ref, _, expr) => { + is_call(&expr.kind) + }, + hir::ExprKind::Call(..) + | hir::ExprKind::MethodCall(..) + // These variants are debatable or require further examination + | hir::ExprKind::If(..) + | hir::ExprKind::Match(..) + | hir::ExprKind::Block{ .. } => true, + _ => false, + } + } + + if args.len() != 2 || name != "expect" || !is_call(&args[1].kind) { + return; + } + + let receiver_type = cx.typeck_results().expr_ty_adjusted(&args[0]); + let closure_args = if is_type_diagnostic_item(cx, receiver_type, sym::option_type) { + "||" + } else if is_type_diagnostic_item(cx, receiver_type, sym::result_type) { + "|_|" + } else { + return; + }; + + let arg_root = get_arg_root(cx, &args[1]); + + let span_replace_word = method_span.with_hi(expr.span.hi()); + + let mut applicability = Applicability::MachineApplicable; + + //Special handling for `format!` as arg_root + if_chain! { + if let hir::ExprKind::Block(block, None) = &arg_root.kind; + if block.stmts.len() == 1; + if let hir::StmtKind::Local(local) = &block.stmts[0].kind; + if let Some(arg_root) = &local.init; + if let hir::ExprKind::Call(inner_fun, inner_args) = arg_root.kind; + if is_expn_of(inner_fun.span, "format").is_some() && inner_args.len() == 1; + if let hir::ExprKind::Call(_, format_args) = &inner_args[0].kind; + then { + let fmt_spec = &format_args[0]; + let fmt_args = &format_args[1]; + + let mut args = vec![snippet(cx, fmt_spec.span, "..").into_owned()]; + + args.extend(generate_format_arg_snippet(cx, fmt_args, &mut applicability)); + + let sugg = args.join(", "); + + span_lint_and_sugg( + cx, + EXPECT_FUN_CALL, + span_replace_word, + &format!("use of `{}` followed by a function call", name), + "try this", + format!("unwrap_or_else({} panic!({}))", closure_args, sugg), + applicability, + ); + + return; + } + } + + let mut arg_root_snippet: Cow<'_, _> = snippet_with_applicability(cx, arg_root.span, "..", &mut applicability); + if requires_to_string(cx, arg_root) { + arg_root_snippet.to_mut().push_str(".to_string()"); + } + + span_lint_and_sugg( + cx, + EXPECT_FUN_CALL, + span_replace_word, + &format!("use of `{}` followed by a function call", name), + "try this", + format!( + "unwrap_or_else({} {{ panic!(\"{{}}\", {}) }})", + closure_args, arg_root_snippet + ), + applicability, + ); +} diff --git a/src/tools/clippy/clippy_lints/src/methods/expect_used.rs b/src/tools/clippy/clippy_lints/src/methods/expect_used.rs new file mode 100644 index 0000000000000..63a834fdce0cb --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/methods/expect_used.rs @@ -0,0 +1,31 @@ +use clippy_utils::diagnostics::span_lint_and_help; +use clippy_utils::ty::is_type_diagnostic_item; +use rustc_hir as hir; +use rustc_lint::LateContext; +use rustc_span::sym; + +use super::EXPECT_USED; + +/// lint use of `expect()` for `Option`s and `Result`s +pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, recv: &hir::Expr<'_>) { + let obj_ty = cx.typeck_results().expr_ty(recv).peel_refs(); + + let mess = if is_type_diagnostic_item(cx, obj_ty, sym::option_type) { + Some((EXPECT_USED, "an Option", "None")) + } else if is_type_diagnostic_item(cx, obj_ty, sym::result_type) { + Some((EXPECT_USED, "a Result", "Err")) + } else { + None + }; + + if let Some((lint, kind, none_value)) = mess { + span_lint_and_help( + cx, + lint, + expr.span, + &format!("used `expect()` on `{}` value", kind,), + None, + &format!("if this value is an `{}`, it will panic", none_value,), + ); + } +} diff --git a/src/tools/clippy/clippy_lints/src/methods/filetype_is_file.rs b/src/tools/clippy/clippy_lints/src/methods/filetype_is_file.rs new file mode 100644 index 0000000000000..7b2967feb0fe9 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/methods/filetype_is_file.rs @@ -0,0 +1,41 @@ +use clippy_utils::diagnostics::span_lint_and_help; +use clippy_utils::ty::match_type; +use clippy_utils::{get_parent_expr, paths}; +use if_chain::if_chain; +use rustc_hir as hir; +use rustc_lint::LateContext; +use rustc_span::source_map::Span; + +use super::FILETYPE_IS_FILE; + +pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, recv: &hir::Expr<'_>) { + let ty = cx.typeck_results().expr_ty(recv); + + if !match_type(cx, ty, &paths::FILE_TYPE) { + return; + } + + let span: Span; + let verb: &str; + let lint_unary: &str; + let help_unary: &str; + if_chain! { + if let Some(parent) = get_parent_expr(cx, expr); + if let hir::ExprKind::Unary(op, _) = parent.kind; + if op == hir::UnOp::Not; + then { + lint_unary = "!"; + verb = "denies"; + help_unary = ""; + span = parent.span; + } else { + lint_unary = ""; + verb = "covers"; + help_unary = "!"; + span = expr.span; + } + } + let lint_msg = format!("`{}FileType::is_file()` only {} regular files", lint_unary, verb); + let help_msg = format!("use `{}FileType::is_dir()` instead", help_unary); + span_lint_and_help(cx, FILETYPE_IS_FILE, span, &lint_msg, None, &help_msg); +} diff --git a/src/tools/clippy/clippy_lints/src/methods/filter_flat_map.rs b/src/tools/clippy/clippy_lints/src/methods/filter_flat_map.rs new file mode 100644 index 0000000000000..1588eec88824b --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/methods/filter_flat_map.rs @@ -0,0 +1,18 @@ +use clippy_utils::diagnostics::span_lint_and_help; +use clippy_utils::is_trait_method; +use rustc_hir as hir; +use rustc_lint::LateContext; +use rustc_span::sym; + +use super::FILTER_MAP; + +/// lint use of `filter().flat_map()` for `Iterators` +pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) { + // lint if caller of `.filter().flat_map()` is an Iterator + if is_trait_method(cx, expr, sym::Iterator) { + let msg = "called `filter(..).flat_map(..)` on an `Iterator`"; + let hint = "this is more succinctly expressed by calling `.flat_map(..)` \ + and filtering by returning `iter::empty()`"; + span_lint_and_help(cx, FILTER_MAP, expr.span, msg, None, hint); + } +} diff --git a/src/tools/clippy/clippy_lints/src/methods/filter_map.rs b/src/tools/clippy/clippy_lints/src/methods/filter_map.rs new file mode 100644 index 0000000000000..35fae450eeb93 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/methods/filter_map.rs @@ -0,0 +1,174 @@ +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::source::{indent_of, reindent_multiline, snippet}; +use clippy_utils::ty::is_type_diagnostic_item; +use clippy_utils::{is_trait_method, path_to_local_id, remove_blocks, SpanlessEq}; +use if_chain::if_chain; +use rustc_errors::Applicability; +use rustc_hir as hir; +use rustc_hir::def::Res; +use rustc_hir::{Expr, ExprKind, PatKind, QPath, UnOp}; +use rustc_lint::LateContext; +use rustc_middle::ty::TyS; +use rustc_span::source_map::Span; +use rustc_span::symbol::{sym, Symbol}; +use std::borrow::Cow; + +use super::MANUAL_FILTER_MAP; +use super::MANUAL_FIND_MAP; +use super::OPTION_FILTER_MAP; + +fn is_method<'tcx>(cx: &LateContext<'tcx>, expr: &hir::Expr<'_>, method_name: Symbol) -> bool { + match &expr.kind { + hir::ExprKind::Path(QPath::TypeRelative(_, mname)) => mname.ident.name == method_name, + hir::ExprKind::Path(QPath::Resolved(_, segments)) => { + segments.segments.last().unwrap().ident.name == method_name + }, + hir::ExprKind::Closure(_, _, c, _, _) => { + let body = cx.tcx.hir().body(*c); + let closure_expr = remove_blocks(&body.value); + let arg_id = body.params[0].pat.hir_id; + match closure_expr.kind { + hir::ExprKind::MethodCall(hir::PathSegment { ident, .. }, _, args, _) => { + if_chain! { + if ident.name == method_name; + if let hir::ExprKind::Path(path) = &args[0].kind; + if let Res::Local(ref local) = cx.qpath_res(path, args[0].hir_id); + then { + return arg_id == *local + } + } + false + }, + _ => false, + } + }, + _ => false, + } +} + +fn is_option_filter_map<'tcx>(cx: &LateContext<'tcx>, filter_arg: &hir::Expr<'_>, map_arg: &hir::Expr<'_>) -> bool { + is_method(cx, map_arg, sym::unwrap) && is_method(cx, filter_arg, sym!(is_some)) +} + +/// lint use of `filter().map()` for `Iterators` +fn lint_filter_some_map_unwrap( + cx: &LateContext<'_>, + expr: &hir::Expr<'_>, + filter_recv: &hir::Expr<'_>, + filter_arg: &hir::Expr<'_>, + map_arg: &hir::Expr<'_>, + target_span: Span, + methods_span: Span, +) { + let iterator = is_trait_method(cx, expr, sym::Iterator); + let option = is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(filter_recv), sym::option_type); + if (iterator || option) && is_option_filter_map(cx, filter_arg, map_arg) { + let msg = "`filter` for `Some` followed by `unwrap`"; + let help = "consider using `flatten` instead"; + let sugg = format!( + "{}", + reindent_multiline(Cow::Borrowed("flatten()"), true, indent_of(cx, target_span),) + ); + span_lint_and_sugg( + cx, + OPTION_FILTER_MAP, + methods_span, + msg, + help, + sugg, + Applicability::MachineApplicable, + ); + } +} + +/// lint use of `filter().map()` or `find().map()` for `Iterators` +#[allow(clippy::too_many_arguments)] +pub(super) fn check<'tcx>( + cx: &LateContext<'tcx>, + expr: &hir::Expr<'_>, + filter_recv: &hir::Expr<'_>, + filter_arg: &hir::Expr<'_>, + filter_span: Span, + map_recv: &hir::Expr<'_>, + map_arg: &hir::Expr<'_>, + map_span: Span, + is_find: bool, +) { + lint_filter_some_map_unwrap( + cx, + expr, + filter_recv, + filter_arg, + map_arg, + map_span, + filter_span.with_hi(expr.span.hi()), + ); + if_chain! { + if is_trait_method(cx, map_recv, sym::Iterator); + + // filter(|x| ...is_some())... + if let ExprKind::Closure(_, _, filter_body_id, ..) = filter_arg.kind; + let filter_body = cx.tcx.hir().body(filter_body_id); + if let [filter_param] = filter_body.params; + // optional ref pattern: `filter(|&x| ..)` + let (filter_pat, is_filter_param_ref) = if let PatKind::Ref(ref_pat, _) = filter_param.pat.kind { + (ref_pat, true) + } else { + (filter_param.pat, false) + }; + // closure ends with is_some() or is_ok() + if let PatKind::Binding(_, filter_param_id, _, None) = filter_pat.kind; + if let ExprKind::MethodCall(path, _, [filter_arg], _) = filter_body.value.kind; + if let Some(opt_ty) = cx.typeck_results().expr_ty(filter_arg).ty_adt_def(); + if let Some(is_result) = if cx.tcx.is_diagnostic_item(sym::option_type, opt_ty.did) { + Some(false) + } else if cx.tcx.is_diagnostic_item(sym::result_type, opt_ty.did) { + Some(true) + } else { + None + }; + if path.ident.name.as_str() == if is_result { "is_ok" } else { "is_some" }; + + // ...map(|x| ...unwrap()) + if let ExprKind::Closure(_, _, map_body_id, ..) = map_arg.kind; + let map_body = cx.tcx.hir().body(map_body_id); + if let [map_param] = map_body.params; + if let PatKind::Binding(_, map_param_id, map_param_ident, None) = map_param.pat.kind; + // closure ends with expect() or unwrap() + if let ExprKind::MethodCall(seg, _, [map_arg, ..], _) = map_body.value.kind; + if matches!(seg.ident.name, sym::expect | sym::unwrap | sym::unwrap_or); + + let eq_fallback = |a: &Expr<'_>, b: &Expr<'_>| { + // in `filter(|x| ..)`, replace `*x` with `x` + let a_path = if_chain! { + if !is_filter_param_ref; + if let ExprKind::Unary(UnOp::Deref, expr_path) = a.kind; + then { expr_path } else { a } + }; + // let the filter closure arg and the map closure arg be equal + if_chain! { + if path_to_local_id(a_path, filter_param_id); + if path_to_local_id(b, map_param_id); + if TyS::same_type(cx.typeck_results().expr_ty_adjusted(a), cx.typeck_results().expr_ty_adjusted(b)); + then { + return true; + } + } + false + }; + if SpanlessEq::new(cx).expr_fallback(eq_fallback).eq_expr(filter_arg, map_arg); + then { + let span = filter_span.with_hi(expr.span.hi()); + let (filter_name, lint) = if is_find { + ("find", MANUAL_FIND_MAP) + } else { + ("filter", MANUAL_FILTER_MAP) + }; + let msg = format!("`{}(..).map(..)` can be simplified as `{0}_map(..)`", filter_name); + let to_opt = if is_result { ".ok()" } else { "" }; + let sugg = format!("{}_map(|{}| {}{})", filter_name, map_param_ident, + snippet(cx, map_arg.span, ".."), to_opt); + span_lint_and_sugg(cx, lint, span, &msg, "try", sugg, Applicability::MachineApplicable); + } + } +} diff --git a/src/tools/clippy/clippy_lints/src/methods/filter_map_flat_map.rs b/src/tools/clippy/clippy_lints/src/methods/filter_map_flat_map.rs new file mode 100644 index 0000000000000..741b1e7e36125 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/methods/filter_map_flat_map.rs @@ -0,0 +1,18 @@ +use clippy_utils::diagnostics::span_lint_and_help; +use clippy_utils::is_trait_method; +use rustc_hir as hir; +use rustc_lint::LateContext; +use rustc_span::sym; + +use super::FILTER_MAP; + +/// lint use of `filter_map().flat_map()` for `Iterators` +pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) { + // lint if caller of `.filter_map().flat_map()` is an Iterator + if is_trait_method(cx, expr, sym::Iterator) { + let msg = "called `filter_map(..).flat_map(..)` on an `Iterator`"; + let hint = "this is more succinctly expressed by calling `.flat_map(..)` \ + and filtering by returning `iter::empty()`"; + span_lint_and_help(cx, FILTER_MAP, expr.span, msg, None, hint); + } +} diff --git a/src/tools/clippy/clippy_lints/src/methods/filter_map_identity.rs b/src/tools/clippy/clippy_lints/src/methods/filter_map_identity.rs index 9e646360a40c3..3a61f4ccad789 100644 --- a/src/tools/clippy/clippy_lints/src/methods/filter_map_identity.rs +++ b/src/tools/clippy/clippy_lints/src/methods/filter_map_identity.rs @@ -1,21 +1,15 @@ -use crate::utils::{match_qpath, match_trait_method, path_to_local_id, paths, span_lint_and_sugg}; +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::{is_trait_method, match_qpath, path_to_local_id, paths}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::LateContext; -use rustc_span::source_map::Span; +use rustc_span::{source_map::Span, sym}; use super::FILTER_MAP_IDENTITY; -pub(super) fn check( - cx: &LateContext<'_>, - expr: &hir::Expr<'_>, - filter_map_args: &[hir::Expr<'_>], - filter_map_span: Span, -) { - if match_trait_method(cx, expr, &paths::ITERATOR) { - let arg_node = &filter_map_args[1].kind; - +pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, filter_map_arg: &hir::Expr<'_>, filter_map_span: Span) { + if is_trait_method(cx, expr, sym::Iterator) { let apply_lint = |message: &str| { span_lint_and_sugg( cx, @@ -29,8 +23,8 @@ pub(super) fn check( }; if_chain! { - if let hir::ExprKind::Closure(_, _, body_id, _, _) = arg_node; - let body = cx.tcx.hir().body(*body_id); + if let hir::ExprKind::Closure(_, _, body_id, _, _) = filter_map_arg.kind; + let body = cx.tcx.hir().body(body_id); if let hir::PatKind::Binding(_, binding_id, ..) = body.params[0].pat.kind; if path_to_local_id(&body.value, binding_id); @@ -40,7 +34,7 @@ pub(super) fn check( } if_chain! { - if let hir::ExprKind::Path(ref qpath) = arg_node; + if let hir::ExprKind::Path(ref qpath) = filter_map_arg.kind; if match_qpath(qpath, &paths::STD_CONVERT_IDENTITY); diff --git a/src/tools/clippy/clippy_lints/src/methods/filter_map_map.rs b/src/tools/clippy/clippy_lints/src/methods/filter_map_map.rs new file mode 100644 index 0000000000000..713bbf258370d --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/methods/filter_map_map.rs @@ -0,0 +1,17 @@ +use clippy_utils::diagnostics::span_lint_and_help; +use clippy_utils::is_trait_method; +use rustc_hir as hir; +use rustc_lint::LateContext; +use rustc_span::sym; + +use super::FILTER_MAP; + +/// lint use of `filter_map().map()` for `Iterators` +pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) { + // lint if caller of `.filter_map().map()` is an Iterator + if is_trait_method(cx, expr, sym::Iterator) { + let msg = "called `filter_map(..).map(..)` on an `Iterator`"; + let hint = "this is more succinctly expressed by only calling `.filter_map(..)` instead"; + span_lint_and_help(cx, FILTER_MAP, expr.span, msg, None, hint); + } +} diff --git a/src/tools/clippy/clippy_lints/src/methods/filter_map_next.rs b/src/tools/clippy/clippy_lints/src/methods/filter_map_next.rs new file mode 100644 index 0000000000000..2b19e4ee8c055 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/methods/filter_map_next.rs @@ -0,0 +1,44 @@ +use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg}; +use clippy_utils::source::snippet; +use clippy_utils::{is_trait_method, meets_msrv}; +use rustc_errors::Applicability; +use rustc_hir as hir; +use rustc_lint::LateContext; +use rustc_semver::RustcVersion; +use rustc_span::sym; + +use super::FILTER_MAP_NEXT; + +const FILTER_MAP_NEXT_MSRV: RustcVersion = RustcVersion::new(1, 30, 0); + +pub(super) fn check<'tcx>( + cx: &LateContext<'tcx>, + expr: &'tcx hir::Expr<'_>, + recv: &'tcx hir::Expr<'_>, + arg: &'tcx hir::Expr<'_>, + msrv: Option<&RustcVersion>, +) { + if is_trait_method(cx, expr, sym::Iterator) { + if !meets_msrv(msrv, &FILTER_MAP_NEXT_MSRV) { + return; + } + + let msg = "called `filter_map(..).next()` on an `Iterator`. This is more succinctly expressed by calling \ + `.find_map(..)` instead"; + let filter_snippet = snippet(cx, arg.span, ".."); + if filter_snippet.lines().count() <= 1 { + let iter_snippet = snippet(cx, recv.span, ".."); + span_lint_and_sugg( + cx, + FILTER_MAP_NEXT, + expr.span, + msg, + "try this", + format!("{}.find_map({})", iter_snippet, filter_snippet), + Applicability::MachineApplicable, + ); + } else { + span_lint(cx, FILTER_MAP_NEXT, expr.span, msg); + } + } +} diff --git a/src/tools/clippy/clippy_lints/src/methods/filter_next.rs b/src/tools/clippy/clippy_lints/src/methods/filter_next.rs new file mode 100644 index 0000000000000..172714f6b01c6 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/methods/filter_next.rs @@ -0,0 +1,39 @@ +use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg}; +use clippy_utils::is_trait_method; +use clippy_utils::source::snippet; +use rustc_errors::Applicability; +use rustc_hir as hir; +use rustc_lint::LateContext; +use rustc_span::sym; + +use super::FILTER_NEXT; + +/// lint use of `filter().next()` for `Iterators` +pub(super) fn check<'tcx>( + cx: &LateContext<'tcx>, + expr: &'tcx hir::Expr<'_>, + recv: &'tcx hir::Expr<'_>, + filter_arg: &'tcx hir::Expr<'_>, +) { + // lint if caller of `.filter().next()` is an Iterator + if is_trait_method(cx, expr, sym::Iterator) { + let msg = "called `filter(..).next()` on an `Iterator`. This is more succinctly expressed by calling \ + `.find(..)` instead"; + let filter_snippet = snippet(cx, filter_arg.span, ".."); + if filter_snippet.lines().count() <= 1 { + let iter_snippet = snippet(cx, recv.span, ".."); + // add note if not multi-line + span_lint_and_sugg( + cx, + FILTER_NEXT, + expr.span, + msg, + "try this", + format!("{}.find({})", iter_snippet, filter_snippet), + Applicability::MachineApplicable, + ); + } else { + span_lint(cx, FILTER_NEXT, expr.span, msg); + } + } +} diff --git a/src/tools/clippy/clippy_lints/src/methods/flat_map_identity.rs b/src/tools/clippy/clippy_lints/src/methods/flat_map_identity.rs new file mode 100644 index 0000000000000..dd613d0cd6384 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/methods/flat_map_identity.rs @@ -0,0 +1,58 @@ +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::{is_trait_method, match_qpath, paths}; +use if_chain::if_chain; +use rustc_errors::Applicability; +use rustc_hir as hir; +use rustc_lint::LateContext; +use rustc_span::{source_map::Span, sym}; + +use super::FLAT_MAP_IDENTITY; + +/// lint use of `flat_map` for `Iterators` where `flatten` would be sufficient +pub(super) fn check<'tcx>( + cx: &LateContext<'tcx>, + expr: &'tcx hir::Expr<'_>, + flat_map_arg: &'tcx hir::Expr<'_>, + flat_map_span: Span, +) { + if is_trait_method(cx, expr, sym::Iterator) { + let arg_node = &flat_map_arg.kind; + + let apply_lint = |message: &str| { + span_lint_and_sugg( + cx, + FLAT_MAP_IDENTITY, + flat_map_span.with_hi(expr.span.hi()), + message, + "try", + "flatten()".to_string(), + Applicability::MachineApplicable, + ); + }; + + if_chain! { + if let hir::ExprKind::Closure(_, _, body_id, _, _) = arg_node; + let body = cx.tcx.hir().body(*body_id); + + if let hir::PatKind::Binding(_, _, binding_ident, _) = body.params[0].pat.kind; + if let hir::ExprKind::Path(hir::QPath::Resolved(_, path)) = body.value.kind; + + if path.segments.len() == 1; + if path.segments[0].ident.name == binding_ident.name; + + then { + apply_lint("called `flat_map(|x| x)` on an `Iterator`"); + } + } + + if_chain! { + if let hir::ExprKind::Path(ref qpath) = arg_node; + + if match_qpath(qpath, &paths::STD_CONVERT_IDENTITY); + + then { + apply_lint("called `flat_map(std::convert::identity)` on an `Iterator`"); + } + } + } +} 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 new file mode 100644 index 0000000000000..707c54f7a3cad --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/methods/from_iter_instead_of_collect.rs @@ -0,0 +1,72 @@ +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::ty::implements_trait; +use clippy_utils::{get_trait_def_id, match_qpath, paths, sugg}; +use if_chain::if_chain; +use rustc_errors::Applicability; +use rustc_hir as hir; +use rustc_hir::ExprKind; +use rustc_lint::{LateContext, LintContext}; +use rustc_middle::ty::Ty; +use rustc_span::sym; + +use super::FROM_ITER_INSTEAD_OF_COLLECT; + +pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, args: &[hir::Expr<'_>], func_kind: &ExprKind<'_>) { + if_chain! { + if let hir::ExprKind::Path(path) = func_kind; + if match_qpath(path, &["from_iter"]); + let ty = cx.typeck_results().expr_ty(expr); + let arg_ty = cx.typeck_results().expr_ty(&args[0]); + if let Some(from_iter_id) = get_trait_def_id(cx, &paths::FROM_ITERATOR); + if let Some(iter_id) = cx.tcx.get_diagnostic_item(sym::Iterator); + + if implements_trait(cx, ty, from_iter_id, &[]) && implements_trait(cx, arg_ty, iter_id, &[]); + then { + // `expr` implements `FromIterator` trait + let iter_expr = sugg::Sugg::hir(cx, &args[0], "..").maybe_par(); + let turbofish = extract_turbofish(cx, expr, ty); + let sugg = format!("{}.collect::<{}>()", iter_expr, turbofish); + span_lint_and_sugg( + cx, + FROM_ITER_INSTEAD_OF_COLLECT, + expr.span, + "usage of `FromIterator::from_iter`", + "use `.collect()` instead of `::from_iter()`", + sugg, + Applicability::MaybeIncorrect, + ); + } + } +} + +fn extract_turbofish(cx: &LateContext<'_>, expr: &hir::Expr<'_>, ty: Ty<'tcx>) -> String { + let call_site = expr.span.source_callsite(); + if_chain! { + if let Ok(snippet) = cx.sess().source_map().span_to_snippet(call_site); + let snippet_split = snippet.split("::").collect::<Vec<_>>(); + if let Some((_, elements)) = snippet_split.split_last(); + + then { + // is there a type specifier? (i.e.: like `<u32>` in `collections::BTreeSet::<u32>::`) + if let Some(type_specifier) = snippet_split.iter().find(|e| e.starts_with('<') && e.ends_with('>')) { + // remove the type specifier from the path elements + let without_ts = elements.iter().filter_map(|e| { + if e == type_specifier { None } else { Some((*e).to_string()) } + }).collect::<Vec<_>>(); + // join and add the type specifier at the end (i.e.: `collections::BTreeSet<u32>`) + format!("{}{}", without_ts.join("::"), type_specifier) + } else { + // type is not explicitly specified so wildcards are needed + // i.e.: 2 wildcards in `std::collections::BTreeMap<&i32, &char>` + let ty_str = ty.to_string(); + let start = ty_str.find('<').unwrap_or(0); + let end = ty_str.find('>').unwrap_or_else(|| ty_str.len()); + let nb_wildcard = ty_str[start..end].split(',').count(); + let wildcards = format!("_{}", ", _".repeat(nb_wildcard - 1)); + format!("{}<{}>", elements.join("::"), wildcards) + } + } else { + ty.to_string() + } + } +} diff --git a/src/tools/clippy/clippy_lints/src/methods/get_unwrap.rs b/src/tools/clippy/clippy_lints/src/methods/get_unwrap.rs new file mode 100644 index 0000000000000..54f2806438413 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/methods/get_unwrap.rs @@ -0,0 +1,87 @@ +use super::utils::derefs_to_slice; +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::source::snippet_with_applicability; +use clippy_utils::ty::{is_type_diagnostic_item, match_type}; +use clippy_utils::{get_parent_expr, paths}; +use if_chain::if_chain; +use rustc_errors::Applicability; +use rustc_hir as hir; +use rustc_lint::LateContext; +use rustc_span::sym; + +use super::GET_UNWRAP; + +pub(super) fn check<'tcx>( + cx: &LateContext<'tcx>, + expr: &hir::Expr<'_>, + recv: &'tcx hir::Expr<'tcx>, + get_arg: &'tcx hir::Expr<'_>, + is_mut: bool, +) { + // Note: we don't want to lint `get_mut().unwrap` for `HashMap` or `BTreeMap`, + // because they do not implement `IndexMut` + let mut applicability = Applicability::MachineApplicable; + let expr_ty = cx.typeck_results().expr_ty(recv); + let get_args_str = snippet_with_applicability(cx, get_arg.span, "..", &mut applicability); + let mut needs_ref; + let caller_type = if derefs_to_slice(cx, recv, expr_ty).is_some() { + needs_ref = get_args_str.parse::<usize>().is_ok(); + "slice" + } else if is_type_diagnostic_item(cx, expr_ty, sym::vec_type) { + needs_ref = get_args_str.parse::<usize>().is_ok(); + "Vec" + } else if is_type_diagnostic_item(cx, expr_ty, sym::vecdeque_type) { + needs_ref = get_args_str.parse::<usize>().is_ok(); + "VecDeque" + } else if !is_mut && is_type_diagnostic_item(cx, expr_ty, sym::hashmap_type) { + needs_ref = true; + "HashMap" + } else if !is_mut && match_type(cx, expr_ty, &paths::BTREEMAP) { + needs_ref = true; + "BTreeMap" + } else { + return; // caller is not a type that we want to lint + }; + + let mut span = expr.span; + + // Handle the case where the result is immediately dereferenced + // by not requiring ref and pulling the dereference into the + // suggestion. + if_chain! { + if needs_ref; + if let Some(parent) = get_parent_expr(cx, expr); + if let hir::ExprKind::Unary(hir::UnOp::Deref, _) = parent.kind; + then { + needs_ref = false; + span = parent.span; + } + } + + let mut_str = if is_mut { "_mut" } else { "" }; + let borrow_str = if !needs_ref { + "" + } else if is_mut { + "&mut " + } else { + "&" + }; + + span_lint_and_sugg( + cx, + GET_UNWRAP, + span, + &format!( + "called `.get{0}().unwrap()` on a {1}. Using `[]` is more clear and more concise", + mut_str, caller_type + ), + "try this", + format!( + "{}{}[{}]", + borrow_str, + snippet_with_applicability(cx, recv.span, "..", &mut applicability), + get_args_str + ), + applicability, + ); +} diff --git a/src/tools/clippy/clippy_lints/src/methods/implicit_clone.rs b/src/tools/clippy/clippy_lints/src/methods/implicit_clone.rs new file mode 100644 index 0000000000000..1211e2f2bf7cb --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/methods/implicit_clone.rs @@ -0,0 +1,32 @@ +use clippy_utils::diagnostics::span_lint_and_sugg; +use if_chain::if_chain; +use rustc_errors::Applicability; +use rustc_hir as hir; +use rustc_hir::ExprKind; +use rustc_lint::LateContext; +use rustc_middle::ty::TyS; +use rustc_span::symbol::Symbol; + +use super::IMPLICIT_CLONE; +use clippy_utils::is_diagnostic_assoc_item; + +pub fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, trait_diagnostic: Symbol) { + if_chain! { + if let ExprKind::MethodCall(method_path, _, [arg], _) = &expr.kind; + let return_type = cx.typeck_results().expr_ty(expr); + let input_type = cx.typeck_results().expr_ty(arg).peel_refs(); + if let Some(expr_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id); + if let Some(ty_name) = input_type.ty_adt_def().map(|adt_def| cx.tcx.item_name(adt_def.did)); + if TyS::same_type(return_type, input_type); + if is_diagnostic_assoc_item(cx, expr_def_id, trait_diagnostic); + then { + span_lint_and_sugg( + cx,IMPLICIT_CLONE,method_path.ident.span, + &format!("implicitly cloning a `{}` by calling `{}` on its dereferenced type", ty_name, method_path.ident.name), + "consider using", + "clone".to_string(), + Applicability::MachineApplicable + ); + } + } +} diff --git a/src/tools/clippy/clippy_lints/src/methods/inefficient_to_string.rs b/src/tools/clippy/clippy_lints/src/methods/inefficient_to_string.rs index c83b6f2c32963..950ec62c9fe4f 100644 --- a/src/tools/clippy/clippy_lints/src/methods/inefficient_to_string.rs +++ b/src/tools/clippy/clippy_lints/src/methods/inefficient_to_string.rs @@ -1,20 +1,24 @@ -use super::INEFFICIENT_TO_STRING; -use crate::utils::{ - is_type_diagnostic_item, match_def_path, paths, snippet_with_applicability, span_lint_and_then, walk_ptrs_ty_depth, -}; +use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::source::snippet_with_applicability; +use clippy_utils::ty::{is_type_diagnostic_item, walk_ptrs_ty_depth}; +use clippy_utils::{match_def_path, paths}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::LateContext; use rustc_middle::ty::{self, Ty}; -use rustc_span::sym; +use rustc_span::symbol::{sym, Symbol}; + +use super::INEFFICIENT_TO_STRING; /// Checks for the `INEFFICIENT_TO_STRING` lint -pub fn lint<'tcx>(cx: &LateContext<'tcx>, expr: &hir::Expr<'_>, arg: &hir::Expr<'_>, arg_ty: Ty<'tcx>) { +pub fn check<'tcx>(cx: &LateContext<'tcx>, expr: &hir::Expr<'_>, method_name: Symbol, args: &[hir::Expr<'_>]) { if_chain! { + if args.len() == 1 && method_name == sym!(to_string); if let Some(to_string_meth_did) = cx.typeck_results().type_dependent_def_id(expr.hir_id); if match_def_path(cx, to_string_meth_did, &paths::TO_STRING_METHOD); if let Some(substs) = cx.typeck_results().node_substs_opt(expr.hir_id); + let arg_ty = cx.typeck_results().expr_ty_adjusted(&args[0]); let self_ty = substs.type_at(0); let (deref_self_ty, deref_count) = walk_ptrs_ty_depth(self_ty); if deref_count >= 1; @@ -31,7 +35,7 @@ pub fn lint<'tcx>(cx: &LateContext<'tcx>, expr: &hir::Expr<'_>, arg: &hir::Expr< self_ty, deref_self_ty )); let mut applicability = Applicability::MachineApplicable; - let arg_snippet = snippet_with_applicability(cx, arg.span, "..", &mut applicability); + let arg_snippet = snippet_with_applicability(cx, args[0].span, "..", &mut applicability); diag.span_suggestion( expr.span, "try dereferencing the receiver", diff --git a/src/tools/clippy/clippy_lints/src/methods/inspect_for_each.rs b/src/tools/clippy/clippy_lints/src/methods/inspect_for_each.rs index 6d41ee38a2767..7fd3ef1a622a0 100644 --- a/src/tools/clippy/clippy_lints/src/methods/inspect_for_each.rs +++ b/src/tools/clippy/clippy_lints/src/methods/inspect_for_each.rs @@ -1,14 +1,14 @@ +use clippy_utils::diagnostics::span_lint_and_help; +use clippy_utils::is_trait_method; use rustc_hir as hir; use rustc_lint::LateContext; -use rustc_span::source_map::Span; - -use crate::utils::{match_trait_method, paths, span_lint_and_help}; +use rustc_span::{source_map::Span, sym}; use super::INSPECT_FOR_EACH; /// lint use of `inspect().for_each()` for `Iterators` -pub(super) fn lint<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, inspect_span: Span) { - if match_trait_method(cx, expr, &paths::ITERATOR) { +pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, inspect_span: Span) { + if is_trait_method(cx, expr, sym::Iterator) { let msg = "called `inspect(..).for_each(..)` on an `Iterator`"; let hint = "move the code from `inspect(..)` to `for_each(..)` and remove the `inspect(..)`"; span_lint_and_help( diff --git a/src/tools/clippy/clippy_lints/src/methods/into_iter_on_ref.rs b/src/tools/clippy/clippy_lints/src/methods/into_iter_on_ref.rs new file mode 100644 index 0000000000000..da13b4ba37a56 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/methods/into_iter_on_ref.rs @@ -0,0 +1,56 @@ +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::is_trait_method; +use clippy_utils::ty::has_iter_method; +use if_chain::if_chain; +use rustc_errors::Applicability; +use rustc_hir as hir; +use rustc_lint::LateContext; +use rustc_middle::ty::{self, Ty}; +use rustc_span::source_map::Span; +use rustc_span::symbol::{sym, Symbol}; + +use super::INTO_ITER_ON_REF; + +pub(super) fn check( + cx: &LateContext<'_>, + expr: &hir::Expr<'_>, + method_span: Span, + method_name: Symbol, + args: &[hir::Expr<'_>], +) { + let self_ty = cx.typeck_results().expr_ty_adjusted(&args[0]); + if_chain! { + if let ty::Ref(..) = self_ty.kind(); + if method_name == sym::into_iter; + if is_trait_method(cx, expr, sym::IntoIterator); + if let Some((kind, method_name)) = ty_has_iter_method(cx, self_ty); + then { + span_lint_and_sugg( + cx, + INTO_ITER_ON_REF, + method_span, + &format!( + "this `.into_iter()` call is equivalent to `.{}()` and will not consume the `{}`", + method_name, kind, + ), + "call directly", + method_name.to_string(), + Applicability::MachineApplicable, + ); + } + } +} + +fn ty_has_iter_method(cx: &LateContext<'_>, self_ref_ty: Ty<'_>) -> Option<(Symbol, &'static str)> { + has_iter_method(cx, self_ref_ty).map(|ty_name| { + let mutbl = match self_ref_ty.kind() { + ty::Ref(_, _, mutbl) => mutbl, + _ => unreachable!(), + }; + let method_name = match mutbl { + hir::Mutability::Not => "iter", + hir::Mutability::Mut => "iter_mut", + }; + (ty_name, method_name) + }) +} diff --git a/src/tools/clippy/clippy_lints/src/methods/iter_cloned_collect.rs b/src/tools/clippy/clippy_lints/src/methods/iter_cloned_collect.rs new file mode 100644 index 0000000000000..739f313716e99 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/methods/iter_cloned_collect.rs @@ -0,0 +1,31 @@ +use crate::methods::utils::derefs_to_slice; +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::ty::is_type_diagnostic_item; +use if_chain::if_chain; +use rustc_errors::Applicability; +use rustc_hir as hir; +use rustc_lint::LateContext; +use rustc_span::sym; + +use super::ITER_CLONED_COLLECT; + +pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &hir::Expr<'_>, recv: &'tcx hir::Expr<'_>) { + if_chain! { + if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(expr), sym::vec_type); + if let Some(slice) = derefs_to_slice(cx, recv, cx.typeck_results().expr_ty(recv)); + if let Some(to_replace) = expr.span.trim_start(slice.span.source_callsite()); + + then { + span_lint_and_sugg( + cx, + ITER_CLONED_COLLECT, + to_replace, + "called `iter().cloned().collect()` on a slice to create a `Vec`. Calling `to_vec()` is both faster and \ + more readable", + "try", + ".to_vec()".to_string(), + Applicability::MachineApplicable, + ); + } + } +} diff --git a/src/tools/clippy/clippy_lints/src/methods/iter_count.rs b/src/tools/clippy/clippy_lints/src/methods/iter_count.rs new file mode 100644 index 0000000000000..c6b7c7cd1795d --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/methods/iter_count.rs @@ -0,0 +1,49 @@ +use super::utils::derefs_to_slice; +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::paths; +use clippy_utils::source::snippet_with_applicability; +use clippy_utils::ty::{is_type_diagnostic_item, match_type}; +use rustc_errors::Applicability; +use rustc_hir::Expr; +use rustc_lint::LateContext; +use rustc_span::sym; + +use super::ITER_COUNT; + +pub(crate) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>, recv: &'tcx Expr<'tcx>, iter_method: &str) { + let ty = cx.typeck_results().expr_ty(recv); + let caller_type = if derefs_to_slice(cx, recv, ty).is_some() { + "slice" + } else if is_type_diagnostic_item(cx, ty, sym::vec_type) { + "Vec" + } else if is_type_diagnostic_item(cx, ty, sym::vecdeque_type) { + "VecDeque" + } else if is_type_diagnostic_item(cx, ty, sym::hashset_type) { + "HashSet" + } else if is_type_diagnostic_item(cx, ty, sym::hashmap_type) { + "HashMap" + } else if match_type(cx, ty, &paths::BTREEMAP) { + "BTreeMap" + } else if match_type(cx, ty, &paths::BTREESET) { + "BTreeSet" + } else if match_type(cx, ty, &paths::LINKED_LIST) { + "LinkedList" + } else if match_type(cx, ty, &paths::BINARY_HEAP) { + "BinaryHeap" + } else { + return; + }; + let mut applicability = Applicability::MachineApplicable; + span_lint_and_sugg( + cx, + ITER_COUNT, + expr.span, + &format!("called `.{}().count()` on a `{}`", iter_method, caller_type), + "try", + format!( + "{}.len()", + snippet_with_applicability(cx, recv.span, "..", &mut applicability), + ), + applicability, + ); +} diff --git a/src/tools/clippy/clippy_lints/src/methods/iter_next_slice.rs b/src/tools/clippy/clippy_lints/src/methods/iter_next_slice.rs new file mode 100644 index 0000000000000..a49851de38e1e --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/methods/iter_next_slice.rs @@ -0,0 +1,69 @@ +use super::utils::derefs_to_slice; +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::source::snippet_with_applicability; +use clippy_utils::ty::is_type_diagnostic_item; +use clippy_utils::{get_parent_expr, higher}; +use if_chain::if_chain; +use rustc_ast::ast; +use rustc_errors::Applicability; +use rustc_hir as hir; +use rustc_lint::LateContext; +use rustc_middle::ty; +use rustc_span::symbol::sym; + +use super::ITER_NEXT_SLICE; + +pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, caller_expr: &'tcx hir::Expr<'_>) { + // Skip lint if the `iter().next()` expression is a for loop argument, + // since it is already covered by `&loops::ITER_NEXT_LOOP` + let mut parent_expr_opt = get_parent_expr(cx, expr); + while let Some(parent_expr) = parent_expr_opt { + if higher::for_loop(parent_expr).is_some() { + return; + } + parent_expr_opt = get_parent_expr(cx, parent_expr); + } + + if derefs_to_slice(cx, caller_expr, cx.typeck_results().expr_ty(caller_expr)).is_some() { + // caller is a Slice + if_chain! { + if let hir::ExprKind::Index(caller_var, index_expr) = &caller_expr.kind; + if let Some(higher::Range { start: Some(start_expr), end: None, limits: ast::RangeLimits::HalfOpen }) + = higher::range(index_expr); + if let hir::ExprKind::Lit(ref start_lit) = &start_expr.kind; + if let ast::LitKind::Int(start_idx, _) = start_lit.node; + then { + let mut applicability = Applicability::MachineApplicable; + span_lint_and_sugg( + cx, + ITER_NEXT_SLICE, + expr.span, + "using `.iter().next()` on a Slice without end index", + "try calling", + format!("{}.get({})", snippet_with_applicability(cx, caller_var.span, "..", &mut applicability), start_idx), + applicability, + ); + } + } + } else if is_vec_or_array(cx, caller_expr) { + // caller is a Vec or an Array + let mut applicability = Applicability::MachineApplicable; + span_lint_and_sugg( + cx, + ITER_NEXT_SLICE, + expr.span, + "using `.iter().next()` on an array", + "try calling", + format!( + "{}.get(0)", + snippet_with_applicability(cx, caller_expr.span, "..", &mut applicability) + ), + applicability, + ); + } +} + +fn is_vec_or_array<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) -> bool { + is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(expr), sym::vec_type) + || matches!(&cx.typeck_results().expr_ty(expr).peel_refs().kind(), ty::Array(_, _)) +} diff --git a/src/tools/clippy/clippy_lints/src/methods/iter_nth.rs b/src/tools/clippy/clippy_lints/src/methods/iter_nth.rs new file mode 100644 index 0000000000000..c2232239fe43f --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/methods/iter_nth.rs @@ -0,0 +1,39 @@ +use super::utils::derefs_to_slice; +use crate::methods::iter_nth_zero; +use clippy_utils::diagnostics::span_lint_and_help; +use clippy_utils::ty::is_type_diagnostic_item; +use rustc_hir as hir; +use rustc_lint::LateContext; +use rustc_span::symbol::sym; + +use super::ITER_NTH; + +pub(super) fn check<'tcx>( + cx: &LateContext<'tcx>, + expr: &hir::Expr<'_>, + iter_recv: &'tcx hir::Expr<'tcx>, + nth_recv: &hir::Expr<'_>, + nth_arg: &hir::Expr<'_>, + is_mut: bool, +) { + let mut_str = if is_mut { "_mut" } else { "" }; + let caller_type = if derefs_to_slice(cx, iter_recv, cx.typeck_results().expr_ty(iter_recv)).is_some() { + "slice" + } else if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(iter_recv), sym::vec_type) { + "Vec" + } else if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(iter_recv), sym::vecdeque_type) { + "VecDeque" + } else { + iter_nth_zero::check(cx, expr, nth_recv, nth_arg); + return; // caller is not a type that we want to lint + }; + + span_lint_and_help( + cx, + ITER_NTH, + expr.span, + &format!("called `.iter{0}().nth()` on a {1}", mut_str, caller_type), + None, + &format!("calling `.get{}()` is both faster and more readable", mut_str), + ); +} diff --git a/src/tools/clippy/clippy_lints/src/methods/iter_nth_zero.rs b/src/tools/clippy/clippy_lints/src/methods/iter_nth_zero.rs new file mode 100644 index 0000000000000..52d7c15332e80 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/methods/iter_nth_zero.rs @@ -0,0 +1,30 @@ +use crate::consts::{constant, Constant}; +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::is_trait_method; +use clippy_utils::source::snippet_with_applicability; +use if_chain::if_chain; +use rustc_errors::Applicability; +use rustc_hir as hir; +use rustc_lint::LateContext; +use rustc_span::sym; + +use super::ITER_NTH_ZERO; + +pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &hir::Expr<'_>, recv: &hir::Expr<'_>, arg: &hir::Expr<'_>) { + if_chain! { + if is_trait_method(cx, expr, sym::Iterator); + if let Some((Constant::Int(0), _)) = constant(cx, cx.typeck_results(), arg); + then { + let mut applicability = Applicability::MachineApplicable; + span_lint_and_sugg( + cx, + ITER_NTH_ZERO, + expr.span, + "called `.nth(0)` on a `std::iter::Iterator`, when `.next()` is equivalent", + "try calling `.next()` instead of `.nth(0)`", + format!("{}.next()", snippet_with_applicability(cx, recv.span, "..", &mut applicability)), + applicability, + ); + } + } +} diff --git a/src/tools/clippy/clippy_lints/src/methods/iter_skip_next.rs b/src/tools/clippy/clippy_lints/src/methods/iter_skip_next.rs new file mode 100644 index 0000000000000..e32594757d0ca --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/methods/iter_skip_next.rs @@ -0,0 +1,24 @@ +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::is_trait_method; +use clippy_utils::source::snippet; +use rustc_errors::Applicability; +use rustc_hir as hir; +use rustc_lint::LateContext; +use rustc_span::sym; + +use super::ITER_SKIP_NEXT; + +pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, recv: &hir::Expr<'_>, arg: &hir::Expr<'_>) { + // lint if caller of skip is an Iterator + if is_trait_method(cx, expr, sym::Iterator) { + span_lint_and_sugg( + cx, + ITER_SKIP_NEXT, + expr.span.trim_start(recv.span).unwrap(), + "called `skip(..).next()` on an iterator", + "use `nth` instead", + format!(".nth({})", snippet(cx, arg.span, "..")), + Applicability::MachineApplicable, + ); + } +} diff --git a/src/tools/clippy/clippy_lints/src/methods/iterator_step_by_zero.rs b/src/tools/clippy/clippy_lints/src/methods/iterator_step_by_zero.rs new file mode 100644 index 0000000000000..06b12998b1aae --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/methods/iterator_step_by_zero.rs @@ -0,0 +1,21 @@ +use crate::consts::{constant, Constant}; +use clippy_utils::diagnostics::span_lint; +use clippy_utils::is_trait_method; +use rustc_hir as hir; +use rustc_lint::LateContext; +use rustc_span::sym; + +use super::ITERATOR_STEP_BY_ZERO; + +pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &hir::Expr<'_>, arg: &'tcx hir::Expr<'_>) { + if is_trait_method(cx, expr, sym::Iterator) { + if let Some((Constant::Int(0), _)) = constant(cx, cx.typeck_results(), arg) { + span_lint( + cx, + ITERATOR_STEP_BY_ZERO, + expr.span, + "`Iterator::step_by(0)` will panic at runtime", + ); + } + } +} diff --git a/src/tools/clippy/clippy_lints/src/methods/manual_saturating_arithmetic.rs b/src/tools/clippy/clippy_lints/src/methods/manual_saturating_arithmetic.rs index eaa604c2ae63e..ecb8b72ef4610 100644 --- a/src/tools/clippy/clippy_lints/src/methods/manual_saturating_arithmetic.rs +++ b/src/tools/clippy/clippy_lints/src/methods/manual_saturating_arithmetic.rs @@ -1,4 +1,6 @@ -use crate::utils::{match_qpath, snippet_with_applicability, span_lint_and_sugg}; +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::match_qpath; +use clippy_utils::source::snippet_with_applicability; use if_chain::if_chain; use rustc_ast::ast; use rustc_errors::Applicability; @@ -6,11 +8,14 @@ use rustc_hir as hir; use rustc_lint::LateContext; use rustc_target::abi::LayoutOf; -pub fn lint(cx: &LateContext<'_>, expr: &hir::Expr<'_>, args: &[&[hir::Expr<'_>]], arith: &str) { - let unwrap_arg = &args[0][1]; - let arith_lhs = &args[1][0]; - let arith_rhs = &args[1][1]; - +pub fn check( + cx: &LateContext<'_>, + expr: &hir::Expr<'_>, + arith_lhs: &hir::Expr<'_>, + arith_rhs: &hir::Expr<'_>, + unwrap_arg: &hir::Expr<'_>, + arith: &str, +) { let ty = cx.typeck_results().expr_ty(arith_lhs); if !ty.is_integral() { return; @@ -39,44 +44,28 @@ pub fn lint(cx: &LateContext<'_>, expr: &hir::Expr<'_>, args: &[&[hir::Expr<'_>] // "mul" is omitted because lhs can be negative. _ => return, } - - let mut applicability = Applicability::MachineApplicable; - span_lint_and_sugg( - cx, - super::MANUAL_SATURATING_ARITHMETIC, - expr.span, - "manual saturating arithmetic", - &format!("try using `saturating_{}`", arith), - format!( - "{}.saturating_{}({})", - snippet_with_applicability(cx, arith_lhs.span, "..", &mut applicability), - arith, - snippet_with_applicability(cx, arith_rhs.span, "..", &mut applicability), - ), - applicability, - ); } else { match (mm, arith) { (MinMax::Max, "add" | "mul") | (MinMax::Min, "sub") => (), _ => return, } - - let mut applicability = Applicability::MachineApplicable; - span_lint_and_sugg( - cx, - super::MANUAL_SATURATING_ARITHMETIC, - expr.span, - "manual saturating arithmetic", - &format!("try using `saturating_{}`", arith), - format!( - "{}.saturating_{}({})", - snippet_with_applicability(cx, arith_lhs.span, "..", &mut applicability), - arith, - snippet_with_applicability(cx, arith_rhs.span, "..", &mut applicability), - ), - applicability, - ); } + + let mut applicability = Applicability::MachineApplicable; + span_lint_and_sugg( + cx, + super::MANUAL_SATURATING_ARITHMETIC, + expr.span, + "manual saturating arithmetic", + &format!("try using `saturating_{}`", arith), + format!( + "{}.saturating_{}({})", + snippet_with_applicability(cx, arith_lhs.span, "..", &mut applicability), + arith, + snippet_with_applicability(cx, arith_rhs.span, "..", &mut applicability), + ), + applicability, + ); } #[derive(PartialEq, Eq)] diff --git a/src/tools/clippy/clippy_lints/src/methods/map_collect_result_unit.rs b/src/tools/clippy/clippy_lints/src/methods/map_collect_result_unit.rs new file mode 100644 index 0000000000000..82063ad70b535 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/methods/map_collect_result_unit.rs @@ -0,0 +1,47 @@ +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::is_trait_method; +use clippy_utils::source::snippet; +use clippy_utils::ty::is_type_diagnostic_item; +use if_chain::if_chain; +use rustc_errors::Applicability; +use rustc_hir as hir; +use rustc_lint::LateContext; +use rustc_middle::ty; +use rustc_span::symbol::sym; + +use super::MAP_COLLECT_RESULT_UNIT; + +pub(super) fn check( + cx: &LateContext<'_>, + expr: &hir::Expr<'_>, + iter: &hir::Expr<'_>, + map_fn: &hir::Expr<'_>, + collect_recv: &hir::Expr<'_>, +) { + if_chain! { + // called on Iterator + if is_trait_method(cx, collect_recv, sym::Iterator); + // return of collect `Result<(),_>` + let collect_ret_ty = cx.typeck_results().expr_ty(expr); + if is_type_diagnostic_item(cx, collect_ret_ty, sym::result_type); + if let ty::Adt(_, substs) = collect_ret_ty.kind(); + if let Some(result_t) = substs.types().next(); + if result_t.is_unit(); + // get parts for snippet + then { + span_lint_and_sugg( + cx, + MAP_COLLECT_RESULT_UNIT, + expr.span, + "`.map().collect()` can be replaced with `.try_for_each()`", + "try this", + format!( + "{}.try_for_each({})", + snippet(cx, iter.span, ".."), + snippet(cx, map_fn.span, "..") + ), + Applicability::MachineApplicable, + ); + } + } +} diff --git a/src/tools/clippy/clippy_lints/src/methods/map_flatten.rs b/src/tools/clippy/clippy_lints/src/methods/map_flatten.rs new file mode 100644 index 0000000000000..e8ad16bc0def9 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/methods/map_flatten.rs @@ -0,0 +1,69 @@ +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::is_trait_method; +use clippy_utils::source::snippet; +use clippy_utils::ty::is_type_diagnostic_item; +use rustc_errors::Applicability; +use rustc_hir as hir; +use rustc_lint::LateContext; +use rustc_middle::ty; +use rustc_span::symbol::sym; + +use super::MAP_FLATTEN; + +/// lint use of `map().flatten()` for `Iterators` and 'Options' +pub(super) fn check<'tcx>( + cx: &LateContext<'tcx>, + expr: &'tcx hir::Expr<'_>, + recv: &'tcx hir::Expr<'_>, + map_arg: &'tcx hir::Expr<'_>, +) { + // lint if caller of `.map().flatten()` is an Iterator + if is_trait_method(cx, expr, sym::Iterator) { + let map_closure_ty = cx.typeck_results().expr_ty(map_arg); + let is_map_to_option = match map_closure_ty.kind() { + ty::Closure(_, _) | ty::FnDef(_, _) | ty::FnPtr(_) => { + let map_closure_sig = match map_closure_ty.kind() { + ty::Closure(_, substs) => substs.as_closure().sig(), + _ => map_closure_ty.fn_sig(cx.tcx), + }; + let map_closure_return_ty = cx.tcx.erase_late_bound_regions(map_closure_sig.output()); + is_type_diagnostic_item(cx, map_closure_return_ty, sym::option_type) + }, + _ => false, + }; + + let method_to_use = if is_map_to_option { + // `(...).map(...)` has type `impl Iterator<Item=Option<...>> + "filter_map" + } else { + // `(...).map(...)` has type `impl Iterator<Item=impl Iterator<...>> + "flat_map" + }; + let func_snippet = snippet(cx, map_arg.span, ".."); + let hint = format!(".{0}({1})", method_to_use, func_snippet); + span_lint_and_sugg( + cx, + MAP_FLATTEN, + expr.span.with_lo(recv.span.hi()), + "called `map(..).flatten()` on an `Iterator`", + &format!("try using `{}` instead", method_to_use), + hint, + Applicability::MachineApplicable, + ); + } + + // lint if caller of `.map().flatten()` is an Option + if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(recv), sym::option_type) { + let func_snippet = snippet(cx, map_arg.span, ".."); + let hint = format!(".and_then({})", func_snippet); + span_lint_and_sugg( + cx, + MAP_FLATTEN, + expr.span.with_lo(recv.span.hi()), + "called `map(..).flatten()` on an `Option`", + "try using `and_then` instead", + hint, + Applicability::MachineApplicable, + ); + } +} diff --git a/src/tools/clippy/clippy_lints/src/methods/map_unwrap_or.rs b/src/tools/clippy/clippy_lints/src/methods/map_unwrap_or.rs new file mode 100644 index 0000000000000..4330fea727b3a --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/methods/map_unwrap_or.rs @@ -0,0 +1,80 @@ +use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg}; +use clippy_utils::meets_msrv; +use clippy_utils::source::snippet; +use clippy_utils::ty::is_type_diagnostic_item; +use clippy_utils::usage::mutated_variables; +use rustc_errors::Applicability; +use rustc_hir as hir; +use rustc_lint::LateContext; +use rustc_semver::RustcVersion; +use rustc_span::symbol::sym; + +use super::MAP_UNWRAP_OR; + +const MAP_UNWRAP_OR_MSRV: RustcVersion = RustcVersion::new(1, 41, 0); + +/// lint use of `map().unwrap_or_else()` for `Option`s and `Result`s +/// Return true if lint triggered +pub(super) fn check<'tcx>( + cx: &LateContext<'tcx>, + expr: &'tcx hir::Expr<'_>, + recv: &'tcx hir::Expr<'_>, + map_arg: &'tcx hir::Expr<'_>, + unwrap_arg: &'tcx hir::Expr<'_>, + msrv: Option<&RustcVersion>, +) -> bool { + if !meets_msrv(msrv, &MAP_UNWRAP_OR_MSRV) { + return false; + } + // lint if the caller of `map()` is an `Option` + let is_option = is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(recv), sym::option_type); + let is_result = is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(recv), sym::result_type); + + if is_option || is_result { + // Don't make a suggestion that may fail to compile due to mutably borrowing + // the same variable twice. + let map_mutated_vars = mutated_variables(recv, cx); + let unwrap_mutated_vars = mutated_variables(unwrap_arg, cx); + if let (Some(map_mutated_vars), Some(unwrap_mutated_vars)) = (map_mutated_vars, unwrap_mutated_vars) { + if map_mutated_vars.intersection(&unwrap_mutated_vars).next().is_some() { + return false; + } + } else { + return false; + } + + // lint message + let msg = if is_option { + "called `map(<f>).unwrap_or_else(<g>)` on an `Option` value. This can be done more directly by calling \ + `map_or_else(<g>, <f>)` instead" + } else { + "called `map(<f>).unwrap_or_else(<g>)` on a `Result` value. This can be done more directly by calling \ + `.map_or_else(<g>, <f>)` instead" + }; + // get snippets for args to map() and unwrap_or_else() + let map_snippet = snippet(cx, map_arg.span, ".."); + let unwrap_snippet = snippet(cx, unwrap_arg.span, ".."); + // lint, with note if neither arg is > 1 line and both map() and + // unwrap_or_else() have the same span + let multiline = map_snippet.lines().count() > 1 || unwrap_snippet.lines().count() > 1; + let same_span = map_arg.span.ctxt() == unwrap_arg.span.ctxt(); + if same_span && !multiline { + let var_snippet = snippet(cx, recv.span, ".."); + span_lint_and_sugg( + cx, + MAP_UNWRAP_OR, + expr.span, + msg, + "try this", + format!("{}.map_or_else({}, {})", var_snippet, unwrap_snippet, map_snippet), + Applicability::MachineApplicable, + ); + return true; + } else if same_span && multiline { + span_lint(cx, MAP_UNWRAP_OR, expr.span, msg); + return true; + } + } + + false +} diff --git a/src/tools/clippy/clippy_lints/src/methods/mod.rs b/src/tools/clippy/clippy_lints/src/methods/mod.rs index 5163074453b5f..b1ade5addd6aa 100644 --- a/src/tools/clippy/clippy_lints/src/methods/mod.rs +++ b/src/tools/clippy/clippy_lints/src/methods/mod.rs @@ -1,44 +1,81 @@ mod bind_instead_of_map; mod bytes_nth; +mod chars_cmp; +mod chars_cmp_with_unwrap; +mod chars_last_cmp; +mod chars_last_cmp_with_unwrap; +mod chars_next_cmp; +mod chars_next_cmp_with_unwrap; +mod clone_on_copy; +mod clone_on_ref_ptr; +mod expect_fun_call; +mod expect_used; +mod filetype_is_file; +mod filter_flat_map; +mod filter_map; +mod filter_map_flat_map; mod filter_map_identity; +mod filter_map_map; +mod filter_map_next; +mod filter_next; +mod flat_map_identity; +mod from_iter_instead_of_collect; +mod get_unwrap; +mod implicit_clone; mod inefficient_to_string; mod inspect_for_each; +mod into_iter_on_ref; +mod iter_cloned_collect; +mod iter_count; +mod iter_next_slice; +mod iter_nth; +mod iter_nth_zero; +mod iter_skip_next; +mod iterator_step_by_zero; mod manual_saturating_arithmetic; +mod map_collect_result_unit; +mod map_flatten; +mod map_unwrap_or; +mod ok_expect; +mod option_as_ref_deref; +mod option_map_or_none; mod option_map_unwrap_or; +mod or_fun_call; +mod search_is_some; +mod single_char_add_str; +mod single_char_insert_string; +mod single_char_pattern; +mod single_char_push_string; +mod skip_while_next; +mod string_extend_chars; +mod suspicious_map; +mod uninit_assumed_init; mod unnecessary_filter_map; +mod unnecessary_fold; mod unnecessary_lazy_eval; - -use std::borrow::Cow; -use std::fmt; -use std::iter; +mod unwrap_used; +mod useless_asref; +mod utils; +mod wrong_self_convention; +mod zst_offset; use bind_instead_of_map::BindInsteadOfMap; +use clippy_utils::diagnostics::{span_lint, span_lint_and_help}; +use clippy_utils::ty::{contains_adt_constructor, contains_ty, implements_trait, is_copy, is_type_diagnostic_item}; +use clippy_utils::{contains_return, get_trait_def_id, in_macro, iter_input_pats, paths, return_ty}; use if_chain::if_chain; -use rustc_ast::ast; -use rustc_errors::Applicability; use rustc_hir as hir; -use rustc_hir::{Expr, ExprKind, PatKind, TraitItem, TraitItemKind, UnOp}; -use rustc_lint::{LateContext, LateLintPass, Lint, LintContext}; +use rustc_hir::def::Res; +use rustc_hir::{Expr, ExprKind, PrimTy, QPath, TraitItem, TraitItemKind}; +use rustc_lint::{LateContext, LateLintPass, LintContext}; 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::source_map::Span; -use rustc_span::symbol::{sym, Symbol, SymbolStr}; +use rustc_span::symbol::SymbolStr; +use rustc_span::{sym, Span}; use rustc_typeck::hir_ty_to_ty; -use crate::consts::{constant, Constant}; -use crate::utils::eager_or_lazy::is_lazyness_candidate; -use crate::utils::usage::mutated_variables; -use crate::utils::{ - contains_return, contains_ty, get_parent_expr, get_trait_def_id, has_iter_method, higher, implements_trait, - in_macro, is_copy, is_expn_of, is_type_diagnostic_item, iter_input_pats, last_path_segment, match_def_path, - match_qpath, match_trait_method, match_type, meets_msrv, method_calls, method_chain_args, path_to_local_id, paths, - remove_blocks, return_ty, single_segment_path, snippet, snippet_with_applicability, snippet_with_macro_callsite, - span_lint, span_lint_and_help, span_lint_and_sugg, span_lint_and_then, strip_pat_refs, sugg, walk_ptrs_ty_depth, - SpanlessEq, -}; - declare_clippy_lint! { /// **What it does:** Checks for `.unwrap()` calls on `Option`s and on `Result`s. /// @@ -159,13 +196,25 @@ declare_clippy_lint! { /// **What it does:** Checks for methods with certain name prefixes and which /// doesn't match how self is taken. The actual rules are: /// - /// |Prefix |`self` taken | - /// |-------|----------------------| - /// |`as_` |`&self` or `&mut self`| - /// |`from_`| none | - /// |`into_`|`self` | - /// |`is_` |`&self` or none | - /// |`to_` |`&self` | + /// |Prefix |Postfix |`self` taken | `self` type | + /// |-------|------------|-----------------------|--------------| + /// |`as_` | none |`&self` or `&mut self` | any | + /// |`from_`| none | none | any | + /// |`into_`| none |`self` | any | + /// |`is_` | none |`&self` or none | any | + /// |`to_` | `_mut` |`&mut self` | any | + /// |`to_` | not `_mut` |`self` | `Copy` | + /// |`to_` | not `_mut` |`&self` | not `Copy` | + /// + /// Note: Clippy doesn't trigger methods with `to_` prefix in: + /// - Traits definition. + /// Clippy can not tell if a type that implements a trait is `Copy` or not. + /// - Traits implementation, when `&self` is taken. + /// The method signature is controlled by the trait and often `&self` is required for all types that implement the trait + /// (see e.g. the `std::string::ToString` trait). + /// + /// Please find more info here: + /// https://rust-lang.github.io/api-guidelines/naming.html#ad-hoc-conversions-follow-as_-to_-into_-conventions-c-conv /// /// **Why is this bad?** Consistency breeds readability. If you follow the /// conventions, your users won't be surprised that they, e.g., need to supply a @@ -401,7 +450,7 @@ declare_clippy_lint! { } declare_clippy_lint! { - /// **What it does:** Checks for usage of `_.map(_).flatten(_)`, + /// **What it does:** Checks for usage of `_.map(_).flatten(_)` on `Iterator` and `Option` /// /// **Why is this bad?** Readability, this can be written more concisely as /// `_.flat_map(_)` @@ -550,26 +599,31 @@ declare_clippy_lint! { declare_clippy_lint! { /// **What it does:** Checks for an iterator or string search (such as `find()`, - /// `position()`, or `rposition()`) followed by a call to `is_some()`. + /// `position()`, or `rposition()`) followed by a call to `is_some()` or `is_none()`. /// - /// **Why is this bad?** Readability, this can be written more concisely as - /// `_.any(_)` or `_.contains(_)`. + /// **Why is this bad?** Readability, this can be written more concisely as: + /// * `_.any(_)`, or `_.contains(_)` for `is_some()`, + /// * `!_.any(_)`, or `!_.contains(_)` for `is_none()`. /// /// **Known problems:** None. /// /// **Example:** /// ```rust - /// # let vec = vec![1]; + /// let vec = vec![1]; /// vec.iter().find(|x| **x == 0).is_some(); + /// + /// let _ = "hello world".find("world").is_none(); /// ``` /// Could be written as /// ```rust - /// # let vec = vec![1]; + /// let vec = vec![1]; /// vec.iter().any(|x| *x == 0); + /// + /// let _ = !"hello world".contains("world"); /// ``` pub SEARCH_IS_SOME, complexity, - "using an iterator or string search followed by `is_some()`, which is more succinctly expressed as a call to `any()` or `contains()`" + "using an iterator or string search followed by `is_some()` or `is_none()`, which is more succinctly expressed as a call to `any()` or `contains()` (with negation in case of `is_none()`)" } declare_clippy_lint! { @@ -850,6 +904,28 @@ declare_clippy_lint! { "using `Iterator::step_by(0)`, which will panic at runtime" } +declare_clippy_lint! { + /// **What it does:** Checks for indirect collection of populated `Option` + /// + /// **Why is this bad?** `Option` is like a collection of 0-1 things, so `flatten` + /// automatically does this without suspicious-looking `unwrap` calls. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// + /// ```rust + /// let _ = std::iter::empty::<Option<i32>>().filter(Option::is_some).map(Option::unwrap); + /// ``` + /// Use instead: + /// ```rust + /// let _ = std::iter::empty::<Option<i32>>().flatten(); + /// ``` + pub OPTION_FILTER_MAP, + complexity, + "filtering `Option` for `Some` then force-unwrapping, which can be one type-safe operation" +} + declare_clippy_lint! { /// **What it does:** Checks for the use of `iter.nth(0)`. /// @@ -1513,6 +1589,58 @@ declare_clippy_lint! { "replace `.bytes().nth()` with `.as_bytes().get()`" } +declare_clippy_lint! { + /// **What it does:** Checks for the usage of `_.to_owned()`, `vec.to_vec()`, or similar when calling `_.clone()` would be clearer. + /// + /// **Why is this bad?** These methods do the same thing as `_.clone()` but may be confusing as + /// to why we are calling `to_vec` on something that is already a `Vec` or calling `to_owned` on something that is already owned. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// + /// ```rust + /// let a = vec![1, 2, 3]; + /// let b = a.to_vec(); + /// let c = a.to_owned(); + /// ``` + /// Use instead: + /// ```rust + /// let a = vec![1, 2, 3]; + /// let b = a.clone(); + /// let c = a.clone(); + /// ``` + pub IMPLICIT_CLONE, + pedantic, + "implicitly cloning a value by invoking a function on its dereferenced type" +} + +declare_clippy_lint! { + /// **What it does:** Checks for the use of `.iter().count()`. + /// + /// **Why is this bad?** `.len()` is more efficient and more + /// readable. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// + /// ```rust + /// // Bad + /// let some_vec = vec![0, 1, 2, 3]; + /// let _ = some_vec.iter().count(); + /// let _ = &some_vec[..].iter().count(); + /// + /// // Good + /// let some_vec = vec![0, 1, 2, 3]; + /// let _ = some_vec.len(); + /// let _ = &some_vec[..].len(); + /// ``` + pub ITER_COUNT, + complexity, + "replace `.iter().count()` with `.len()`" +} + pub struct Methods { msrv: Option<RustcVersion>, } @@ -1553,11 +1681,13 @@ impl_lint_pass!(Methods => [ FILTER_MAP_IDENTITY, MANUAL_FILTER_MAP, MANUAL_FIND_MAP, + OPTION_FILTER_MAP, FILTER_MAP_NEXT, FLAT_MAP_IDENTITY, MAP_FLATTEN, ITERATOR_STEP_BY_ZERO, ITER_NEXT_SLICE, + ITER_COUNT, ITER_NTH, ITER_NTH_ZERO, BYTES_NTH, @@ -1579,146 +1709,51 @@ impl_lint_pass!(Methods => [ MAP_COLLECT_RESULT_UNIT, FROM_ITER_INSTEAD_OF_COLLECT, INSPECT_FOR_EACH, + IMPLICIT_CLONE ]); +/// Extracts a method call name, args, and `Span` of the method name. +fn method_call<'tcx>(recv: &'tcx hir::Expr<'tcx>) -> Option<(SymbolStr, &'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.as_str(), args, span)); + } + } + None +} + +/// Same as `method_call` but the `SymbolStr` is dereferenced into a temporary `&str` +macro_rules! method_call { + ($expr:expr) => { + method_call($expr) + .as_ref() + .map(|&(ref name, args, span)| (&**name, args, span)) + }; +} + impl<'tcx> LateLintPass<'tcx> for Methods { - #[allow(clippy::too_many_lines)] fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) { if in_macro(expr.span) { return; } - let (method_names, arg_lists, method_spans) = method_calls(expr, 2); - let method_names: Vec<SymbolStr> = method_names.iter().map(|s| s.as_str()).collect(); - let method_names: Vec<&str> = method_names.iter().map(|s| &**s).collect(); - - match method_names.as_slice() { - ["unwrap", "get"] => lint_get_unwrap(cx, expr, arg_lists[1], false), - ["unwrap", "get_mut"] => lint_get_unwrap(cx, expr, arg_lists[1], true), - ["unwrap", ..] => lint_unwrap(cx, expr, arg_lists[0]), - ["expect", "ok"] => lint_ok_expect(cx, expr, arg_lists[1]), - ["expect", ..] => lint_expect(cx, expr, arg_lists[0]), - ["unwrap_or", "map"] => option_map_unwrap_or::lint(cx, expr, arg_lists[1], arg_lists[0], method_spans[1]), - ["unwrap_or_else", "map"] => { - if !lint_map_unwrap_or_else(cx, expr, arg_lists[1], arg_lists[0], self.msrv.as_ref()) { - unnecessary_lazy_eval::lint(cx, expr, arg_lists[0], "unwrap_or"); - } - }, - ["map_or", ..] => lint_map_or_none(cx, expr, arg_lists[0]), - ["and_then", ..] => { - let biom_option_linted = bind_instead_of_map::OptionAndThenSome::lint(cx, expr, arg_lists[0]); - let biom_result_linted = bind_instead_of_map::ResultAndThenOk::lint(cx, expr, arg_lists[0]); - if !biom_option_linted && !biom_result_linted { - unnecessary_lazy_eval::lint(cx, expr, arg_lists[0], "and"); - } - }, - ["or_else", ..] => { - if !bind_instead_of_map::ResultOrElseErrInfo::lint(cx, expr, arg_lists[0]) { - unnecessary_lazy_eval::lint(cx, expr, arg_lists[0], "or"); - } - }, - ["next", "filter"] => lint_filter_next(cx, expr, arg_lists[1]), - ["next", "skip_while"] => lint_skip_while_next(cx, expr, arg_lists[1]), - ["next", "iter"] => lint_iter_next(cx, expr, arg_lists[1]), - ["map", "filter"] => lint_filter_map(cx, expr, false), - ["map", "filter_map"] => lint_filter_map_map(cx, expr, arg_lists[1], arg_lists[0]), - ["next", "filter_map"] => lint_filter_map_next(cx, expr, arg_lists[1], self.msrv.as_ref()), - ["map", "find"] => lint_filter_map(cx, expr, true), - ["flat_map", "filter"] => lint_filter_flat_map(cx, expr, arg_lists[1], arg_lists[0]), - ["flat_map", "filter_map"] => lint_filter_map_flat_map(cx, expr, arg_lists[1], arg_lists[0]), - ["flat_map", ..] => lint_flat_map_identity(cx, expr, arg_lists[0], method_spans[0]), - ["flatten", "map"] => lint_map_flatten(cx, expr, arg_lists[1]), - ["is_some", "find"] => lint_search_is_some(cx, expr, "find", arg_lists[1], arg_lists[0], method_spans[1]), - ["is_some", "position"] => { - lint_search_is_some(cx, expr, "position", arg_lists[1], arg_lists[0], method_spans[1]) - }, - ["is_some", "rposition"] => { - lint_search_is_some(cx, expr, "rposition", arg_lists[1], arg_lists[0], method_spans[1]) - }, - ["extend", ..] => lint_extend(cx, expr, arg_lists[0]), - ["nth", "iter"] => lint_iter_nth(cx, expr, &arg_lists, false), - ["nth", "iter_mut"] => lint_iter_nth(cx, expr, &arg_lists, true), - ["nth", "bytes"] => bytes_nth::lints(cx, expr, &arg_lists[1]), - ["nth", ..] => lint_iter_nth_zero(cx, expr, arg_lists[0]), - ["step_by", ..] => lint_step_by(cx, expr, arg_lists[0]), - ["next", "skip"] => lint_iter_skip_next(cx, expr, arg_lists[1]), - ["collect", "cloned"] => lint_iter_cloned_collect(cx, expr, arg_lists[1]), - ["as_ref"] => lint_asref(cx, expr, "as_ref", arg_lists[0]), - ["as_mut"] => lint_asref(cx, expr, "as_mut", arg_lists[0]), - ["fold", ..] => lint_unnecessary_fold(cx, expr, arg_lists[0], method_spans[0]), - ["filter_map", ..] => { - unnecessary_filter_map::lint(cx, expr, arg_lists[0]); - filter_map_identity::check(cx, expr, arg_lists[0], method_spans[0]); - }, - ["count", "map"] => lint_suspicious_map(cx, expr), - ["assume_init"] => lint_maybe_uninit(cx, &arg_lists[0][0], expr), - ["unwrap_or", arith @ ("checked_add" | "checked_sub" | "checked_mul")] => { - manual_saturating_arithmetic::lint(cx, expr, &arg_lists, &arith["checked_".len()..]) - }, - ["add" | "offset" | "sub" | "wrapping_offset" | "wrapping_add" | "wrapping_sub"] => { - check_pointer_offset(cx, expr, arg_lists[0]) - }, - ["is_file", ..] => lint_filetype_is_file(cx, expr, arg_lists[0]), - ["map", "as_ref"] => { - lint_option_as_ref_deref(cx, expr, arg_lists[1], arg_lists[0], false, self.msrv.as_ref()) - }, - ["map", "as_mut"] => { - lint_option_as_ref_deref(cx, expr, arg_lists[1], arg_lists[0], true, self.msrv.as_ref()) - }, - ["unwrap_or_else", ..] => unnecessary_lazy_eval::lint(cx, expr, arg_lists[0], "unwrap_or"), - ["get_or_insert_with", ..] => unnecessary_lazy_eval::lint(cx, expr, arg_lists[0], "get_or_insert"), - ["ok_or_else", ..] => unnecessary_lazy_eval::lint(cx, expr, arg_lists[0], "ok_or"), - ["collect", "map"] => lint_map_collect(cx, expr, arg_lists[1], arg_lists[0]), - ["for_each", "inspect"] => inspect_for_each::lint(cx, expr, method_spans[1]), - _ => {}, - } + check_methods(cx, expr, self.msrv.as_ref()); match expr.kind { - hir::ExprKind::Call(ref func, ref args) => { - if let hir::ExprKind::Path(path) = &func.kind { - if match_qpath(path, &["from_iter"]) { - lint_from_iter(cx, expr, args); - } - } + hir::ExprKind::Call(func, args) => { + from_iter_instead_of_collect::check(cx, expr, args, &func.kind); }, - hir::ExprKind::MethodCall(ref method_call, ref method_span, ref args, _) => { - lint_or_fun_call(cx, expr, *method_span, &method_call.ident.as_str(), args); - lint_expect_fun_call(cx, expr, *method_span, &method_call.ident.as_str(), args); - - let self_ty = cx.typeck_results().expr_ty_adjusted(&args[0]); - if args.len() == 1 && method_call.ident.name == sym::clone { - lint_clone_on_copy(cx, expr, &args[0], self_ty); - lint_clone_on_ref_ptr(cx, expr, &args[0]); - } - if args.len() == 1 && method_call.ident.name == sym!(to_string) { - inefficient_to_string::lint(cx, expr, &args[0], self_ty); - } - - if let Some(fn_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id) { - if match_def_path(cx, fn_def_id, &paths::PUSH_STR) { - lint_single_char_push_string(cx, expr, args); - } else if match_def_path(cx, fn_def_id, &paths::INSERT_STR) { - lint_single_char_insert_string(cx, expr, args); - } - } - - match self_ty.kind() { - ty::Ref(_, ty, _) if *ty.kind() == ty::Str => { - for &(method, pos) in &PATTERN_METHODS { - if method_call.ident.name.as_str() == method && args.len() > pos { - lint_single_char_pattern(cx, expr, &args[pos]); - } - } - }, - ty::Ref(..) if method_call.ident.name == sym::into_iter => { - lint_into_iter(cx, expr, self_ty, *method_span); - }, - _ => (), - } + hir::ExprKind::MethodCall(method_call, ref method_span, args, _) => { + or_fun_call::check(cx, expr, *method_span, &method_call.ident.as_str(), args); + expect_fun_call::check(cx, expr, *method_span, &method_call.ident.as_str(), args); + clone_on_copy::check(cx, expr, method_call.ident.name, args); + clone_on_ref_ptr::check(cx, expr, method_call.ident.name, args); + inefficient_to_string::check(cx, expr, method_call.ident.name, args); + single_char_add_str::check(cx, expr, args); + into_iter_on_ref::check(cx, expr, *method_span, method_call.ident.name, args); + single_char_pattern::check(cx, expr, method_call.ident.name, args); }, - hir::ExprKind::Binary(op, ref lhs, ref rhs) - if op.node == hir::BinOpKind::Eq || op.node == hir::BinOpKind::Ne => - { + hir::ExprKind::Binary(op, lhs, rhs) if op.node == hir::BinOpKind::Eq || op.node == hir::BinOpKind::Ne => { let mut info = BinaryExprInfo { expr, chain: lhs, @@ -1726,7 +1761,7 @@ impl<'tcx> LateLintPass<'tcx> for Methods { eq: op.node == hir::BinOpKind::Eq, }; lint_binary_expr_with_method_call(cx, &mut info); - } + }, _ => (), } } @@ -1741,14 +1776,10 @@ impl<'tcx> LateLintPass<'tcx> for Methods { let item = cx.tcx.hir().expect_item(parent); let self_ty = cx.tcx.type_of(item.def_id); - // if this impl block implements a trait, lint in trait definition instead - if let hir::ItemKind::Impl(hir::Impl { of_trait: Some(_), .. }) = item.kind { - return; - } - + let implements_trait = matches!(item.kind, hir::ItemKind::Impl(hir::Impl { of_trait: Some(_), .. })); if_chain! { if let hir::ImplItemKind::Fn(ref sig, id) = impl_item.kind; - if let Some(first_arg) = iter_input_pats(&sig.decl, cx.tcx.hir().body(id)).next(); + if let Some(first_arg) = iter_input_pats(sig.decl, cx.tcx.hir().body(id)).next(); let method_sig = cx.tcx.fn_sig(impl_item.def_id); let method_sig = cx.tcx.erase_late_bound_regions(method_sig); @@ -1759,15 +1790,16 @@ impl<'tcx> LateLintPass<'tcx> for Methods { if let Some(first_arg_ty) = first_arg_ty; then { - if cx.access_levels.is_exported(impl_item.hir_id()) { + // if this impl block implements a trait, lint in trait definition instead + if !implements_trait && cx.access_levels.is_exported(impl_item.hir_id()) { // check missing trait implementations for method_config in &TRAIT_METHODS { if name == method_config.method_name && sig.decl.inputs.len() == method_config.param_count && - method_config.output_type.matches(cx, &sig.decl.output) && + method_config.output_type.matches(&sig.decl.output) && method_config.self_kind.matches(cx, self_ty, first_arg_ty) && fn_header_equals(method_config.fn_header, sig.header) && - method_config.lifetime_param_cond(&impl_item) + method_config.lifetime_param_cond(impl_item) { span_lint_and_help( cx, @@ -1789,22 +1821,33 @@ impl<'tcx> LateLintPass<'tcx> for Methods { } } - lint_wrong_self_convention( + wrong_self_convention::check( cx, &name, item.vis.node.is_pub(), self_ty, first_arg_ty, - first_arg.pat.span + first_arg.pat.span, + implements_trait, + false ); } } + // if this impl block implements a trait, lint in trait definition instead + if implements_trait { + return; + } + if let hir::ImplItemKind::Fn(_, _) = impl_item.kind { let ret_ty = return_ty(cx, impl_item.hir_id()); // walk the return type and check for Self (this does not check associated types) - if contains_ty(ret_ty, self_ty) { + if let Some(self_adt) = self_ty.ty_adt_def() { + if contains_adt_constructor(ret_ty, self_adt) { + return; + } + } else if contains_ty(ret_ty, self_ty) { return; } @@ -1814,7 +1857,11 @@ impl<'tcx> LateLintPass<'tcx> for Methods { for &(predicate, _span) in cx.tcx.explicit_item_bounds(def_id) { if let ty::PredicateKind::Projection(projection_predicate) = predicate.kind().skip_binder() { // walk the associated type and check for Self - if contains_ty(projection_predicate.ty, self_ty) { + if let Some(self_adt) = self_ty.ty_adt_def() { + if contains_adt_constructor(projection_predicate.ty, self_adt) { + return; + } + } else if contains_ty(projection_predicate.ty, self_ty) { return; } } @@ -1840,12 +1887,20 @@ impl<'tcx> LateLintPass<'tcx> for Methods { if_chain! { if let TraitItemKind::Fn(ref sig, _) = item.kind; if let Some(first_arg_ty) = sig.decl.inputs.iter().next(); - let first_arg_span = first_arg_ty.span; - let first_arg_ty = hir_ty_to_ty(cx.tcx, first_arg_ty); - let self_ty = TraitRef::identity(cx.tcx, item.def_id.to_def_id()).self_ty(); - then { - lint_wrong_self_convention(cx, &item.ident.name.as_str(), false, self_ty, first_arg_ty, first_arg_span); + let first_arg_span = first_arg_ty.span; + let first_arg_ty = hir_ty_to_ty(cx.tcx, first_arg_ty); + let self_ty = TraitRef::identity(cx.tcx, item.def_id.to_def_id()).self_ty(); + wrong_self_convention::check( + cx, + &item.ident.name.as_str(), + false, + self_ty, + first_arg_ty, + first_arg_span, + false, + true + ); } } @@ -1870,2041 +1925,204 @@ impl<'tcx> LateLintPass<'tcx> for Methods { extract_msrv_attr!(LateContext); } -fn lint_wrong_self_convention<'tcx>( - cx: &LateContext<'tcx>, - item_name: &str, - is_pub: bool, - self_ty: &'tcx TyS<'tcx>, - first_arg_ty: &'tcx TyS<'tcx>, - first_arg_span: Span, -) { - let lint = if is_pub { - WRONG_PUB_SELF_CONVENTION - } else { - WRONG_SELF_CONVENTION - }; - if let Some((ref conv, self_kinds)) = &CONVENTIONS.iter().find(|(ref conv, _)| conv.check(item_name)) { - if !self_kinds.iter().any(|k| k.matches(cx, self_ty, first_arg_ty)) { - span_lint( - cx, - lint, - first_arg_span, - &format!( - "methods called `{}` usually take {}; consider choosing a less ambiguous name", - conv, - &self_kinds - .iter() - .map(|k| k.description()) - .collect::<Vec<_>>() - .join(" or ") - ), - ); - } - } -} - -/// Checks for the `OR_FUN_CALL` lint. #[allow(clippy::too_many_lines)] -fn lint_or_fun_call<'tcx>( - cx: &LateContext<'tcx>, - expr: &hir::Expr<'_>, - method_span: Span, - name: &str, - args: &'tcx [hir::Expr<'_>], -) { - /// Checks for `unwrap_or(T::new())` or `unwrap_or(T::default())`. - fn check_unwrap_or_default( - cx: &LateContext<'_>, - name: &str, - fun: &hir::Expr<'_>, - self_expr: &hir::Expr<'_>, - arg: &hir::Expr<'_>, - or_has_args: bool, - span: Span, - ) -> bool { - if_chain! { - if !or_has_args; - if name == "unwrap_or"; - if let hir::ExprKind::Path(ref qpath) = fun.kind; - let path = &*last_path_segment(qpath).ident.as_str(); - if ["default", "new"].contains(&path); - let arg_ty = cx.typeck_results().expr_ty(arg); - if let Some(default_trait_id) = get_trait_def_id(cx, &paths::DEFAULT_TRAIT); - if implements_trait(cx, arg_ty, default_trait_id, &[]); - - then { - let mut applicability = Applicability::MachineApplicable; - span_lint_and_sugg( - cx, - OR_FUN_CALL, - span, - &format!("use of `{}` followed by a call to `{}`", name, path), - "try this", - format!( - "{}.unwrap_or_default()", - snippet_with_applicability(cx, self_expr.span, "..", &mut applicability) - ), - applicability, - ); - - true - } else { - false - } - } - } - - /// Checks for `*or(foo())`. - #[allow(clippy::too_many_arguments)] - fn check_general_case<'tcx>( - cx: &LateContext<'tcx>, - name: &str, - method_span: Span, - self_expr: &hir::Expr<'_>, - arg: &'tcx hir::Expr<'_>, - span: Span, - // None if lambda is required - fun_span: Option<Span>, - ) { - // (path, fn_has_argument, methods, suffix) - static KNOW_TYPES: [(&[&str], bool, &[&str], &str); 4] = [ - (&paths::BTREEMAP_ENTRY, false, &["or_insert"], "with"), - (&paths::HASHMAP_ENTRY, false, &["or_insert"], "with"), - (&paths::OPTION, false, &["map_or", "ok_or", "or", "unwrap_or"], "else"), - (&paths::RESULT, true, &["or", "unwrap_or"], "else"), - ]; - - if let hir::ExprKind::MethodCall(ref path, _, ref args, _) = &arg.kind { - if path.ident.as_str() == "len" { - let ty = cx.typeck_results().expr_ty(&args[0]).peel_refs(); - - match ty.kind() { - ty::Slice(_) | ty::Array(_, _) => return, - _ => (), +fn check_methods<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, msrv: Option<&RustcVersion>) { + if let Some((name, [recv, args @ ..], span)) = method_call!(expr) { + match (name, args) { + ("add" | "offset" | "sub" | "wrapping_offset" | "wrapping_add" | "wrapping_sub", [recv, _]) => { + zst_offset::check(cx, expr, recv) + }, + ("and_then", [arg]) => { + let biom_option_linted = bind_instead_of_map::OptionAndThenSome::check(cx, expr, recv, arg); + let biom_result_linted = bind_instead_of_map::ResultAndThenOk::check(cx, expr, recv, arg); + if !biom_option_linted && !biom_result_linted { + unnecessary_lazy_eval::check(cx, expr, recv, arg, "and"); } - - if is_type_diagnostic_item(cx, ty, sym::vec_type) { - return; + }, + ("as_mut", []) => useless_asref::check(cx, expr, "as_mut", recv), + ("as_ref", []) => useless_asref::check(cx, expr, "as_ref", recv), + ("assume_init", []) => uninit_assumed_init::check(cx, expr, recv), + ("collect", []) => match method_call!(recv) { + Some(("cloned", [recv2], _)) => iter_cloned_collect::check(cx, expr, recv2), + Some(("map", [m_recv, m_arg], _)) => { + map_collect_result_unit::check(cx, expr, m_recv, m_arg, 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) { + Some(("ok", [recv], _)) => ok_expect::check(cx, expr, recv), + _ => expect_used::check(cx, expr, recv), + }, + ("extend", [arg]) => string_extend_chars::check(cx, expr, recv, arg), + ("filter_map", [arg]) => { + unnecessary_filter_map::check(cx, expr, arg); + filter_map_identity::check(cx, expr, arg, span); + }, + ("flat_map", [flm_arg]) => match method_call!(recv) { + Some(("filter", [_, _], _)) => filter_flat_map::check(cx, expr), + Some(("filter_map", [_, _], _)) => filter_map_flat_map::check(cx, expr), + _ => flat_map_identity::check(cx, expr, flm_arg, span), + }, + ("flatten", []) => { + if let Some(("map", [recv, map_arg], _)) = method_call!(recv) { + map_flatten::check(cx, expr, recv, map_arg); } - } - } - - if_chain! { - if KNOW_TYPES.iter().any(|k| k.2.contains(&name)); - - if is_lazyness_candidate(cx, arg); - if !contains_return(&arg); - - let self_ty = cx.typeck_results().expr_ty(self_expr); - - if let Some(&(_, fn_has_arguments, poss, suffix)) = - KNOW_TYPES.iter().find(|&&i| match_type(cx, self_ty, i.0)); - - if poss.contains(&name); - - then { - let sugg: Cow<'_, str> = { - let (snippet_span, use_lambda) = match (fn_has_arguments, fun_span) { - (false, Some(fun_span)) => (fun_span, false), - _ => (arg.span, true), - }; - let snippet = snippet_with_macro_callsite(cx, snippet_span, ".."); - if use_lambda { - let l_arg = if fn_has_arguments { "_" } else { "" }; - format!("|{}| {}", l_arg, snippet).into() - } else { - snippet + }, + ("fold", [init, acc]) => unnecessary_fold::check(cx, expr, init, acc, span), + ("for_each", [_]) => { + if let Some(("inspect", [_, _], span2)) = method_call!(recv) { + inspect_for_each::check(cx, expr, span2); + } + }, + ("get_or_insert_with", [arg]) => unnecessary_lazy_eval::check(cx, expr, recv, arg, "get_or_insert"), + ("is_file", []) => filetype_is_file::check(cx, expr, recv), + ("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) { + 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), + ("filter", [f_arg]) => { + filter_map::check(cx, expr, recv2, f_arg, span2, recv, m_arg, span, false) + }, + ("filter_map", [_]) => filter_map_map::check(cx, expr), + ("find", [f_arg]) => filter_map::check(cx, expr, recv2, f_arg, span2, recv, m_arg, span, true), + _ => {}, } - }; - let span_replace_word = method_span.with_hi(span.hi()); - span_lint_and_sugg( - cx, - OR_FUN_CALL, - span_replace_word, - &format!("use of `{}` followed by a function call", name), - "try this", - format!("{}_{}({})", name, suffix, sugg), - Applicability::HasPlaceholders, - ); - } - } - } - - if args.len() == 2 { - match args[1].kind { - hir::ExprKind::Call(ref fun, ref or_args) => { - let or_has_args = !or_args.is_empty(); - if !check_unwrap_or_default(cx, name, fun, &args[0], &args[1], or_has_args, expr.span) { - let fun_span = if or_has_args { None } else { Some(fun.span) }; - check_general_case(cx, name, method_span, &args[0], &args[1], expr.span, fun_span); } }, - hir::ExprKind::Index(..) | hir::ExprKind::MethodCall(..) => { - check_general_case(cx, name, method_span, &args[0], &args[1], expr.span, None); + ("map_or", [def, map]) => option_map_or_none::check(cx, expr, recv, def, map), + ("next", []) => { + 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), + ("iter", []) => iter_next_slice::check(cx, expr, recv), + ("skip", [arg]) => iter_skip_next::check(cx, expr, recv, arg), + ("skip_while", [_]) => skip_while_next::check(cx, expr), + _ => {}, + } + } + }, + ("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), + _ => iter_nth_zero::check(cx, expr, recv, n_arg), + }, + ("ok_or_else", [arg]) => unnecessary_lazy_eval::check(cx, expr, recv, arg, "ok_or"), + ("or_else", [arg]) => { + if !bind_instead_of_map::ResultOrElseErrInfo::check(cx, expr, recv, arg) { + unnecessary_lazy_eval::check(cx, expr, recv, arg, "or"); + } + }, + ("step_by", [arg]) => iterator_step_by_zero::check(cx, expr, arg), + ("to_os_string", []) => implicit_clone::check(cx, expr, sym::OsStr), + ("to_owned", []) => implicit_clone::check(cx, expr, sym::ToOwned), + ("to_path_buf", []) => implicit_clone::check(cx, expr, sym::Path), + ("to_vec", []) => implicit_clone::check(cx, expr, sym::slice), + ("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) { + Some((arith @ ("checked_add" | "checked_sub" | "checked_mul"), [lhs, rhs], _)) => { + manual_saturating_arithmetic::check(cx, expr, lhs, rhs, u_arg, &arith["checked_".len()..]); + }, + Some(("map", [m_recv, m_arg], span)) => { + option_map_unwrap_or::check(cx, expr, m_recv, m_arg, recv, u_arg, span) + }, + _ => {}, + }, + ("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) => {}, + _ => unnecessary_lazy_eval::check(cx, expr, recv, u_arg, "unwrap_or"), }, _ => {}, } } } -/// Checks for the `EXPECT_FUN_CALL` lint. -#[allow(clippy::too_many_lines)] -fn lint_expect_fun_call( - cx: &LateContext<'_>, - expr: &hir::Expr<'_>, - method_span: Span, - name: &str, - args: &[hir::Expr<'_>], -) { - // 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> { - let mut arg_root = arg; - loop { - arg_root = match &arg_root.kind { - hir::ExprKind::AddrOf(hir::BorrowKind::Ref, _, expr) => expr, - hir::ExprKind::MethodCall(method_name, _, call_args, _) => { - if call_args.len() == 1 - && (method_name.ident.name == sym::as_str || method_name.ident.name == sym!(as_ref)) - && { - let arg_type = cx.typeck_results().expr_ty(&call_args[0]); - let base_type = arg_type.peel_refs(); - *base_type.kind() == ty::Str || is_type_diagnostic_item(cx, base_type, sym::string_type) - } - { - &call_args[0] - } else { - break; - } - }, - _ => break, - }; - } - arg_root +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) { + search_is_some::check(cx, expr, name, is_some, f_recv, arg, recv, span) } +} - // Only `&'static str` or `String` can be used directly in the `panic!`. Other types should be - // converted to string. - fn requires_to_string(cx: &LateContext<'_>, arg: &hir::Expr<'_>) -> bool { - let arg_ty = cx.typeck_results().expr_ty(arg); - if is_type_diagnostic_item(cx, arg_ty, sym::string_type) { - return false; - } - if let ty::Ref(_, ty, ..) = arg_ty.kind() { - if *ty.kind() == ty::Str && can_be_static_str(cx, arg) { - return false; +/// Used for `lint_binary_expr_with_method_call`. +#[derive(Copy, Clone)] +struct BinaryExprInfo<'a> { + expr: &'a hir::Expr<'a>, + chain: &'a hir::Expr<'a>, + other: &'a hir::Expr<'a>, + eq: bool, +} + +/// Checks for the `CHARS_NEXT_CMP` and `CHARS_LAST_CMP` lints. +fn lint_binary_expr_with_method_call(cx: &LateContext<'_>, info: &mut BinaryExprInfo<'_>) { + macro_rules! lint_with_both_lhs_and_rhs { + ($func:expr, $cx:expr, $info:ident) => { + if !$func($cx, $info) { + ::std::mem::swap(&mut $info.chain, &mut $info.other); + if $func($cx, $info) { + return; + } } }; - true } - // Check if an expression could have type `&'static str`, knowing that it - // has type `&str` for some lifetime. - fn can_be_static_str(cx: &LateContext<'_>, arg: &hir::Expr<'_>) -> bool { - match arg.kind { - hir::ExprKind::Lit(_) => true, - hir::ExprKind::Call(fun, _) => { - if let hir::ExprKind::Path(ref p) = fun.kind { - match cx.qpath_res(p, fun.hir_id) { - hir::def::Res::Def(hir::def::DefKind::Fn | hir::def::DefKind::AssocFn, def_id) => matches!( - cx.tcx.fn_sig(def_id).output().skip_binder().kind(), - ty::Ref(ty::ReStatic, ..) - ), - _ => false, - } - } else { - false - } - }, - hir::ExprKind::MethodCall(..) => { - cx.typeck_results() - .type_dependent_def_id(arg.hir_id) - .map_or(false, |method_id| { - matches!( - cx.tcx.fn_sig(method_id).output().skip_binder().kind(), - ty::Ref(ty::ReStatic, ..) - ) - }) - }, - hir::ExprKind::Path(ref p) => matches!( - cx.qpath_res(p, arg.hir_id), - hir::def::Res::Def(hir::def::DefKind::Const | hir::def::DefKind::Static, _) - ), - _ => false, - } - } + lint_with_both_lhs_and_rhs!(chars_next_cmp::check, cx, info); + lint_with_both_lhs_and_rhs!(chars_last_cmp::check, cx, info); + lint_with_both_lhs_and_rhs!(chars_next_cmp_with_unwrap::check, cx, info); + lint_with_both_lhs_and_rhs!(chars_last_cmp_with_unwrap::check, cx, info); +} - fn generate_format_arg_snippet( - cx: &LateContext<'_>, - a: &hir::Expr<'_>, - applicability: &mut Applicability, - ) -> Vec<String> { - if_chain! { - if let hir::ExprKind::AddrOf(hir::BorrowKind::Ref, _, ref format_arg) = a.kind; - if let hir::ExprKind::Match(ref format_arg_expr, _, _) = format_arg.kind; - if let hir::ExprKind::Tup(ref format_arg_expr_tup) = format_arg_expr.kind; +const FN_HEADER: hir::FnHeader = hir::FnHeader { + unsafety: hir::Unsafety::Normal, + constness: hir::Constness::NotConst, + asyncness: hir::IsAsync::NotAsync, + abi: rustc_target::spec::abi::Abi::Rust, +}; - then { - format_arg_expr_tup - .iter() - .map(|a| snippet_with_applicability(cx, a.span, "..", applicability).into_owned()) - .collect() - } else { - unreachable!() - } - } - } - - fn is_call(node: &hir::ExprKind<'_>) -> bool { - match node { - hir::ExprKind::AddrOf(hir::BorrowKind::Ref, _, expr) => { - is_call(&expr.kind) - }, - hir::ExprKind::Call(..) - | hir::ExprKind::MethodCall(..) - // These variants are debatable or require further examination - | hir::ExprKind::If(..) - | hir::ExprKind::Match(..) - | hir::ExprKind::Block{ .. } => true, - _ => false, - } - } - - if args.len() != 2 || name != "expect" || !is_call(&args[1].kind) { - return; - } - - let receiver_type = cx.typeck_results().expr_ty_adjusted(&args[0]); - let closure_args = if is_type_diagnostic_item(cx, receiver_type, sym::option_type) { - "||" - } else if is_type_diagnostic_item(cx, receiver_type, sym::result_type) { - "|_|" - } else { - return; - }; - - let arg_root = get_arg_root(cx, &args[1]); - - let span_replace_word = method_span.with_hi(expr.span.hi()); - - let mut applicability = Applicability::MachineApplicable; - - //Special handling for `format!` as arg_root - if_chain! { - if let hir::ExprKind::Block(block, None) = &arg_root.kind; - if block.stmts.len() == 1; - if let hir::StmtKind::Local(local) = &block.stmts[0].kind; - if let Some(arg_root) = &local.init; - if let hir::ExprKind::Call(ref inner_fun, ref inner_args) = arg_root.kind; - if is_expn_of(inner_fun.span, "format").is_some() && inner_args.len() == 1; - if let hir::ExprKind::Call(_, format_args) = &inner_args[0].kind; - then { - let fmt_spec = &format_args[0]; - let fmt_args = &format_args[1]; - - let mut args = vec![snippet(cx, fmt_spec.span, "..").into_owned()]; - - args.extend(generate_format_arg_snippet(cx, fmt_args, &mut applicability)); - - let sugg = args.join(", "); - - span_lint_and_sugg( - cx, - EXPECT_FUN_CALL, - span_replace_word, - &format!("use of `{}` followed by a function call", name), - "try this", - format!("unwrap_or_else({} panic!({}))", closure_args, sugg), - applicability, - ); - - return; - } - } - - let mut arg_root_snippet: Cow<'_, _> = snippet_with_applicability(cx, arg_root.span, "..", &mut applicability); - if requires_to_string(cx, arg_root) { - arg_root_snippet.to_mut().push_str(".to_string()"); - } - - span_lint_and_sugg( - cx, - EXPECT_FUN_CALL, - span_replace_word, - &format!("use of `{}` followed by a function call", name), - "try this", - format!( - "unwrap_or_else({} {{ panic!(\"{{}}\", {}) }})", - closure_args, arg_root_snippet - ), - applicability, - ); -} - -/// Checks for the `CLONE_ON_COPY` lint. -fn lint_clone_on_copy(cx: &LateContext<'_>, expr: &hir::Expr<'_>, arg: &hir::Expr<'_>, arg_ty: Ty<'_>) { - let ty = cx.typeck_results().expr_ty(expr); - if let ty::Ref(_, inner, _) = arg_ty.kind() { - if let ty::Ref(_, innermost, _) = inner.kind() { - span_lint_and_then( - cx, - CLONE_DOUBLE_REF, - expr.span, - &format!( - "using `clone` on a double-reference; \ - this will copy the reference of type `{}` instead of cloning the inner type", - ty - ), - |diag| { - if let Some(snip) = sugg::Sugg::hir_opt(cx, arg) { - let mut ty = innermost; - let mut n = 0; - while let ty::Ref(_, inner, _) = ty.kind() { - ty = inner; - n += 1; - } - let refs: String = iter::repeat('&').take(n + 1).collect(); - let derefs: String = iter::repeat('*').take(n).collect(); - let explicit = format!("<{}{}>::clone({})", refs, ty, snip); - diag.span_suggestion( - expr.span, - "try dereferencing it", - format!("{}({}{}).clone()", refs, derefs, snip.deref()), - Applicability::MaybeIncorrect, - ); - diag.span_suggestion( - expr.span, - "or try being explicit if you are sure, that you want to clone a reference", - explicit, - Applicability::MaybeIncorrect, - ); - } - }, - ); - return; // don't report clone_on_copy - } - } - - if is_copy(cx, ty) { - let snip; - if let Some(snippet) = sugg::Sugg::hir_opt(cx, arg) { - let parent = cx.tcx.hir().get_parent_node(expr.hir_id); - match &cx.tcx.hir().get(parent) { - hir::Node::Expr(parent) => match parent.kind { - // &*x is a nop, &x.clone() is not - hir::ExprKind::AddrOf(..) => return, - // (*x).func() is useless, x.clone().func() can work in case func borrows mutably - hir::ExprKind::MethodCall(_, _, parent_args, _) if expr.hir_id == parent_args[0].hir_id => { - return; - }, - - _ => {}, - }, - hir::Node::Stmt(stmt) => { - if let hir::StmtKind::Local(ref loc) = stmt.kind { - if let hir::PatKind::Ref(..) = loc.pat.kind { - // let ref y = *x borrows x, let ref y = x.clone() does not - return; - } - } - }, - _ => {}, - } - - // x.clone() might have dereferenced x, possibly through Deref impls - if cx.typeck_results().expr_ty(arg) == ty { - snip = Some(("try removing the `clone` call", format!("{}", snippet))); - } else { - let deref_count = cx - .typeck_results() - .expr_adjustments(arg) - .iter() - .filter(|adj| matches!(adj.kind, ty::adjustment::Adjust::Deref(_))) - .count(); - let derefs: String = iter::repeat('*').take(deref_count).collect(); - snip = Some(("try dereferencing it", format!("{}{}", derefs, snippet))); - } - } else { - snip = None; - } - span_lint_and_then( - cx, - CLONE_ON_COPY, - expr.span, - &format!("using `clone` on type `{}` which implements the `Copy` trait", ty), - |diag| { - if let Some((text, snip)) = snip { - diag.span_suggestion(expr.span, text, snip, Applicability::MachineApplicable); - } - }, - ); - } -} - -fn lint_clone_on_ref_ptr(cx: &LateContext<'_>, expr: &hir::Expr<'_>, arg: &hir::Expr<'_>) { - let obj_ty = cx.typeck_results().expr_ty(arg).peel_refs(); - - if let ty::Adt(_, subst) = obj_ty.kind() { - let caller_type = if is_type_diagnostic_item(cx, obj_ty, sym::Rc) { - "Rc" - } else if is_type_diagnostic_item(cx, obj_ty, sym::Arc) { - "Arc" - } else if match_type(cx, obj_ty, &paths::WEAK_RC) || match_type(cx, obj_ty, &paths::WEAK_ARC) { - "Weak" - } else { - return; - }; - - let snippet = snippet_with_macro_callsite(cx, arg.span, ".."); - - span_lint_and_sugg( - cx, - CLONE_ON_REF_PTR, - expr.span, - "using `.clone()` on a ref-counted pointer", - "try this", - format!("{}::<{}>::clone(&{})", caller_type, subst.type_at(0), snippet), - Applicability::Unspecified, // Sometimes unnecessary ::<_> after Rc/Arc/Weak - ); - } -} - -fn lint_string_extend(cx: &LateContext<'_>, expr: &hir::Expr<'_>, args: &[hir::Expr<'_>]) { - let arg = &args[1]; - if let Some(arglists) = method_chain_args(arg, &["chars"]) { - let target = &arglists[0][0]; - let self_ty = cx.typeck_results().expr_ty(target).peel_refs(); - let ref_str = if *self_ty.kind() == ty::Str { - "" - } else if is_type_diagnostic_item(cx, self_ty, sym::string_type) { - "&" - } else { - return; - }; - - let mut applicability = Applicability::MachineApplicable; - span_lint_and_sugg( - cx, - STRING_EXTEND_CHARS, - expr.span, - "calling `.extend(_.chars())`", - "try this", - format!( - "{}.push_str({}{})", - snippet_with_applicability(cx, args[0].span, "..", &mut applicability), - ref_str, - snippet_with_applicability(cx, target.span, "..", &mut applicability) - ), - applicability, - ); - } -} - -fn lint_extend(cx: &LateContext<'_>, expr: &hir::Expr<'_>, args: &[hir::Expr<'_>]) { - let obj_ty = cx.typeck_results().expr_ty(&args[0]).peel_refs(); - if is_type_diagnostic_item(cx, obj_ty, sym::string_type) { - lint_string_extend(cx, expr, args); - } -} - -fn lint_iter_cloned_collect<'tcx>(cx: &LateContext<'tcx>, expr: &hir::Expr<'_>, iter_args: &'tcx [hir::Expr<'_>]) { - if_chain! { - if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(expr), sym::vec_type); - if let Some(slice) = derefs_to_slice(cx, &iter_args[0], cx.typeck_results().expr_ty(&iter_args[0])); - if let Some(to_replace) = expr.span.trim_start(slice.span.source_callsite()); - - then { - span_lint_and_sugg( - cx, - ITER_CLONED_COLLECT, - to_replace, - "called `iter().cloned().collect()` on a slice to create a `Vec`. Calling `to_vec()` is both faster and \ - more readable", - "try", - ".to_vec()".to_string(), - Applicability::MachineApplicable, - ); - } - } -} - -fn lint_unnecessary_fold(cx: &LateContext<'_>, expr: &hir::Expr<'_>, fold_args: &[hir::Expr<'_>], fold_span: Span) { - fn check_fold_with_op( - cx: &LateContext<'_>, - expr: &hir::Expr<'_>, - fold_args: &[hir::Expr<'_>], - fold_span: Span, - op: hir::BinOpKind, - replacement_method_name: &str, - replacement_has_args: bool, - ) { - if_chain! { - // Extract the body of the closure passed to fold - if let hir::ExprKind::Closure(_, _, body_id, _, _) = fold_args[2].kind; - let closure_body = cx.tcx.hir().body(body_id); - let closure_expr = remove_blocks(&closure_body.value); - - // Check if the closure body is of the form `acc <op> some_expr(x)` - if let hir::ExprKind::Binary(ref bin_op, ref left_expr, ref right_expr) = closure_expr.kind; - if bin_op.node == op; - - // Extract the names of the two arguments to the closure - if let [param_a, param_b] = closure_body.params; - if let PatKind::Binding(_, first_arg_id, ..) = strip_pat_refs(¶m_a.pat).kind; - if let PatKind::Binding(_, second_arg_id, second_arg_ident, _) = strip_pat_refs(¶m_b.pat).kind; - - if path_to_local_id(left_expr, first_arg_id); - if replacement_has_args || path_to_local_id(right_expr, second_arg_id); - - then { - let mut applicability = Applicability::MachineApplicable; - let sugg = if replacement_has_args { - format!( - "{replacement}(|{s}| {r})", - replacement = replacement_method_name, - s = second_arg_ident, - r = snippet_with_applicability(cx, right_expr.span, "EXPR", &mut applicability), - ) - } else { - format!( - "{replacement}()", - replacement = replacement_method_name, - ) - }; - - span_lint_and_sugg( - cx, - UNNECESSARY_FOLD, - fold_span.with_hi(expr.span.hi()), - // TODO #2371 don't suggest e.g., .any(|x| f(x)) if we can suggest .any(f) - "this `.fold` can be written more succinctly using another method", - "try", - sugg, - applicability, - ); - } - } - } - - // Check that this is a call to Iterator::fold rather than just some function called fold - if !match_trait_method(cx, expr, &paths::ITERATOR) { - return; - } - - assert!( - fold_args.len() == 3, - "Expected fold_args to have three entries - the receiver, the initial value and the closure" - ); - - // Check if the first argument to .fold is a suitable literal - if let hir::ExprKind::Lit(ref lit) = fold_args[1].kind { - match lit.node { - ast::LitKind::Bool(false) => { - check_fold_with_op(cx, expr, fold_args, fold_span, hir::BinOpKind::Or, "any", true) - }, - ast::LitKind::Bool(true) => { - check_fold_with_op(cx, expr, fold_args, fold_span, hir::BinOpKind::And, "all", true) - }, - ast::LitKind::Int(0, _) => { - check_fold_with_op(cx, expr, fold_args, fold_span, hir::BinOpKind::Add, "sum", false) - }, - ast::LitKind::Int(1, _) => { - check_fold_with_op(cx, expr, fold_args, fold_span, hir::BinOpKind::Mul, "product", false) - }, - _ => (), - } - } -} - -fn lint_step_by<'tcx>(cx: &LateContext<'tcx>, expr: &hir::Expr<'_>, args: &'tcx [hir::Expr<'_>]) { - if match_trait_method(cx, expr, &paths::ITERATOR) { - if let Some((Constant::Int(0), _)) = constant(cx, cx.typeck_results(), &args[1]) { - span_lint( - cx, - ITERATOR_STEP_BY_ZERO, - expr.span, - "Iterator::step_by(0) will panic at runtime", - ); - } - } -} - -fn lint_iter_next<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, iter_args: &'tcx [hir::Expr<'_>]) { - let caller_expr = &iter_args[0]; - - // Skip lint if the `iter().next()` expression is a for loop argument, - // since it is already covered by `&loops::ITER_NEXT_LOOP` - let mut parent_expr_opt = get_parent_expr(cx, expr); - while let Some(parent_expr) = parent_expr_opt { - if higher::for_loop(parent_expr).is_some() { - return; - } - parent_expr_opt = get_parent_expr(cx, parent_expr); - } - - if derefs_to_slice(cx, caller_expr, cx.typeck_results().expr_ty(caller_expr)).is_some() { - // caller is a Slice - if_chain! { - if let hir::ExprKind::Index(ref caller_var, ref index_expr) = &caller_expr.kind; - if let Some(higher::Range { start: Some(start_expr), end: None, limits: ast::RangeLimits::HalfOpen }) - = higher::range(index_expr); - if let hir::ExprKind::Lit(ref start_lit) = &start_expr.kind; - if let ast::LitKind::Int(start_idx, _) = start_lit.node; - then { - let mut applicability = Applicability::MachineApplicable; - span_lint_and_sugg( - cx, - ITER_NEXT_SLICE, - expr.span, - "using `.iter().next()` on a Slice without end index", - "try calling", - format!("{}.get({})", snippet_with_applicability(cx, caller_var.span, "..", &mut applicability), start_idx), - applicability, - ); - } - } - } else if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(caller_expr), sym::vec_type) - || matches!( - &cx.typeck_results().expr_ty(caller_expr).peel_refs().kind(), - ty::Array(_, _) - ) - { - // caller is a Vec or an Array - let mut applicability = Applicability::MachineApplicable; - span_lint_and_sugg( - cx, - ITER_NEXT_SLICE, - expr.span, - "using `.iter().next()` on an array", - "try calling", - format!( - "{}.get(0)", - snippet_with_applicability(cx, caller_expr.span, "..", &mut applicability) - ), - applicability, - ); - } -} - -fn lint_iter_nth<'tcx>( - cx: &LateContext<'tcx>, - expr: &hir::Expr<'_>, - nth_and_iter_args: &[&'tcx [hir::Expr<'tcx>]], - is_mut: bool, -) { - let iter_args = nth_and_iter_args[1]; - let mut_str = if is_mut { "_mut" } else { "" }; - let caller_type = if derefs_to_slice(cx, &iter_args[0], cx.typeck_results().expr_ty(&iter_args[0])).is_some() { - "slice" - } else if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(&iter_args[0]), sym::vec_type) { - "Vec" - } else if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(&iter_args[0]), sym::vecdeque_type) { - "VecDeque" - } else { - let nth_args = nth_and_iter_args[0]; - lint_iter_nth_zero(cx, expr, &nth_args); - return; // caller is not a type that we want to lint - }; - - span_lint_and_help( - cx, - ITER_NTH, - expr.span, - &format!("called `.iter{0}().nth()` on a {1}", mut_str, caller_type), - None, - &format!("calling `.get{}()` is both faster and more readable", mut_str), - ); -} - -fn lint_iter_nth_zero<'tcx>(cx: &LateContext<'tcx>, expr: &hir::Expr<'_>, nth_args: &'tcx [hir::Expr<'_>]) { - if_chain! { - if match_trait_method(cx, expr, &paths::ITERATOR); - if let Some((Constant::Int(0), _)) = constant(cx, cx.typeck_results(), &nth_args[1]); - then { - let mut applicability = Applicability::MachineApplicable; - span_lint_and_sugg( - cx, - ITER_NTH_ZERO, - expr.span, - "called `.nth(0)` on a `std::iter::Iterator`, when `.next()` is equivalent", - "try calling `.next()` instead of `.nth(0)`", - format!("{}.next()", snippet_with_applicability(cx, nth_args[0].span, "..", &mut applicability)), - applicability, - ); - } - } -} - -fn lint_get_unwrap<'tcx>(cx: &LateContext<'tcx>, expr: &hir::Expr<'_>, get_args: &'tcx [hir::Expr<'_>], is_mut: bool) { - // Note: we don't want to lint `get_mut().unwrap` for `HashMap` or `BTreeMap`, - // because they do not implement `IndexMut` - let mut applicability = Applicability::MachineApplicable; - let expr_ty = cx.typeck_results().expr_ty(&get_args[0]); - let get_args_str = if get_args.len() > 1 { - snippet_with_applicability(cx, get_args[1].span, "..", &mut applicability) - } else { - return; // not linting on a .get().unwrap() chain or variant - }; - let mut needs_ref; - let caller_type = if derefs_to_slice(cx, &get_args[0], expr_ty).is_some() { - needs_ref = get_args_str.parse::<usize>().is_ok(); - "slice" - } else if is_type_diagnostic_item(cx, expr_ty, sym::vec_type) { - needs_ref = get_args_str.parse::<usize>().is_ok(); - "Vec" - } else if is_type_diagnostic_item(cx, expr_ty, sym::vecdeque_type) { - needs_ref = get_args_str.parse::<usize>().is_ok(); - "VecDeque" - } else if !is_mut && is_type_diagnostic_item(cx, expr_ty, sym::hashmap_type) { - needs_ref = true; - "HashMap" - } else if !is_mut && match_type(cx, expr_ty, &paths::BTREEMAP) { - needs_ref = true; - "BTreeMap" - } else { - return; // caller is not a type that we want to lint - }; - - let mut span = expr.span; - - // Handle the case where the result is immediately dereferenced - // by not requiring ref and pulling the dereference into the - // suggestion. - if_chain! { - if needs_ref; - if let Some(parent) = get_parent_expr(cx, expr); - if let hir::ExprKind::Unary(hir::UnOp::Deref, _) = parent.kind; - then { - needs_ref = false; - span = parent.span; - } - } - - let mut_str = if is_mut { "_mut" } else { "" }; - let borrow_str = if !needs_ref { - "" - } else if is_mut { - "&mut " - } else { - "&" - }; - - span_lint_and_sugg( - cx, - GET_UNWRAP, - span, - &format!( - "called `.get{0}().unwrap()` on a {1}. Using `[]` is more clear and more concise", - mut_str, caller_type - ), - "try this", - format!( - "{}{}[{}]", - borrow_str, - snippet_with_applicability(cx, get_args[0].span, "..", &mut applicability), - get_args_str - ), - applicability, - ); -} - -fn lint_iter_skip_next(cx: &LateContext<'_>, expr: &hir::Expr<'_>, skip_args: &[hir::Expr<'_>]) { - // lint if caller of skip is an Iterator - if match_trait_method(cx, expr, &paths::ITERATOR) { - if let [caller, n] = skip_args { - let hint = format!(".nth({})", snippet(cx, n.span, "..")); - span_lint_and_sugg( - cx, - ITER_SKIP_NEXT, - expr.span.trim_start(caller.span).unwrap(), - "called `skip(..).next()` on an iterator", - "use `nth` instead", - hint, - Applicability::MachineApplicable, - ); - } - } -} - -fn derefs_to_slice<'tcx>( - cx: &LateContext<'tcx>, - expr: &'tcx hir::Expr<'tcx>, - ty: Ty<'tcx>, -) -> Option<&'tcx hir::Expr<'tcx>> { - fn may_slice<'a>(cx: &LateContext<'a>, ty: Ty<'a>) -> bool { - match ty.kind() { - ty::Slice(_) => true, - ty::Adt(def, _) if def.is_box() => may_slice(cx, ty.boxed_ty()), - ty::Adt(..) => is_type_diagnostic_item(cx, ty, sym::vec_type), - ty::Array(_, size) => size - .try_eval_usize(cx.tcx, cx.param_env) - .map_or(false, |size| size < 32), - ty::Ref(_, inner, _) => may_slice(cx, inner), - _ => false, - } - } - - if let hir::ExprKind::MethodCall(ref path, _, ref args, _) = expr.kind { - if path.ident.name == sym::iter && may_slice(cx, cx.typeck_results().expr_ty(&args[0])) { - Some(&args[0]) - } else { - None - } - } else { - match ty.kind() { - ty::Slice(_) => Some(expr), - ty::Adt(def, _) if def.is_box() && may_slice(cx, ty.boxed_ty()) => Some(expr), - ty::Ref(_, inner, _) => { - if may_slice(cx, inner) { - Some(expr) - } else { - None - } - }, - _ => None, - } - } -} - -/// lint use of `unwrap()` for `Option`s and `Result`s -fn lint_unwrap(cx: &LateContext<'_>, expr: &hir::Expr<'_>, unwrap_args: &[hir::Expr<'_>]) { - let obj_ty = cx.typeck_results().expr_ty(&unwrap_args[0]).peel_refs(); - - let mess = if is_type_diagnostic_item(cx, obj_ty, sym::option_type) { - Some((UNWRAP_USED, "an Option", "None")) - } else if is_type_diagnostic_item(cx, obj_ty, sym::result_type) { - Some((UNWRAP_USED, "a Result", "Err")) - } else { - None - }; - - if let Some((lint, kind, none_value)) = mess { - span_lint_and_help( - cx, - lint, - expr.span, - &format!("used `unwrap()` on `{}` value", kind,), - None, - &format!( - "if you don't want to handle the `{}` case gracefully, consider \ - using `expect()` to provide a better panic message", - none_value, - ), - ); - } -} - -/// lint use of `expect()` for `Option`s and `Result`s -fn lint_expect(cx: &LateContext<'_>, expr: &hir::Expr<'_>, expect_args: &[hir::Expr<'_>]) { - let obj_ty = cx.typeck_results().expr_ty(&expect_args[0]).peel_refs(); - - let mess = if is_type_diagnostic_item(cx, obj_ty, sym::option_type) { - Some((EXPECT_USED, "an Option", "None")) - } else if is_type_diagnostic_item(cx, obj_ty, sym::result_type) { - Some((EXPECT_USED, "a Result", "Err")) - } else { - None - }; - - if let Some((lint, kind, none_value)) = mess { - span_lint_and_help( - cx, - lint, - expr.span, - &format!("used `expect()` on `{}` value", kind,), - None, - &format!("if this value is an `{}`, it will panic", none_value,), - ); - } -} - -/// lint use of `ok().expect()` for `Result`s -fn lint_ok_expect(cx: &LateContext<'_>, expr: &hir::Expr<'_>, ok_args: &[hir::Expr<'_>]) { - if_chain! { - // lint if the caller of `ok()` is a `Result` - if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(&ok_args[0]), sym::result_type); - let result_type = cx.typeck_results().expr_ty(&ok_args[0]); - if let Some(error_type) = get_error_type(cx, result_type); - if has_debug_impl(error_type, cx); - - then { - span_lint_and_help( - cx, - OK_EXPECT, - expr.span, - "called `ok().expect()` on a `Result` value", - None, - "you can call `expect()` directly on the `Result`", - ); - } - } -} - -/// lint use of `map().flatten()` for `Iterators` and 'Options' -fn lint_map_flatten<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, map_args: &'tcx [hir::Expr<'_>]) { - // lint if caller of `.map().flatten()` is an Iterator - if match_trait_method(cx, expr, &paths::ITERATOR) { - let map_closure_ty = cx.typeck_results().expr_ty(&map_args[1]); - let is_map_to_option = match map_closure_ty.kind() { - ty::Closure(_, _) | ty::FnDef(_, _) | ty::FnPtr(_) => { - let map_closure_sig = match map_closure_ty.kind() { - ty::Closure(_, substs) => substs.as_closure().sig(), - _ => map_closure_ty.fn_sig(cx.tcx), - }; - let map_closure_return_ty = cx.tcx.erase_late_bound_regions(map_closure_sig.output()); - is_type_diagnostic_item(cx, map_closure_return_ty, sym::option_type) - }, - _ => false, - }; - - let method_to_use = if is_map_to_option { - // `(...).map(...)` has type `impl Iterator<Item=Option<...>> - "filter_map" - } else { - // `(...).map(...)` has type `impl Iterator<Item=impl Iterator<...>> - "flat_map" - }; - let func_snippet = snippet(cx, map_args[1].span, ".."); - let hint = format!(".{0}({1})", method_to_use, func_snippet); - span_lint_and_sugg( - cx, - MAP_FLATTEN, - expr.span.with_lo(map_args[0].span.hi()), - "called `map(..).flatten()` on an `Iterator`", - &format!("try using `{}` instead", method_to_use), - hint, - Applicability::MachineApplicable, - ); - } - - // lint if caller of `.map().flatten()` is an Option - if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(&map_args[0]), sym::option_type) { - let func_snippet = snippet(cx, map_args[1].span, ".."); - let hint = format!(".and_then({})", func_snippet); - span_lint_and_sugg( - cx, - MAP_FLATTEN, - expr.span.with_lo(map_args[0].span.hi()), - "called `map(..).flatten()` on an `Option`", - "try using `and_then` instead", - hint, - Applicability::MachineApplicable, - ); - } -} - -const MAP_UNWRAP_OR_MSRV: RustcVersion = RustcVersion::new(1, 41, 0); - -/// lint use of `map().unwrap_or_else()` for `Option`s and `Result`s -/// Return true if lint triggered -fn lint_map_unwrap_or_else<'tcx>( - cx: &LateContext<'tcx>, - expr: &'tcx hir::Expr<'_>, - map_args: &'tcx [hir::Expr<'_>], - unwrap_args: &'tcx [hir::Expr<'_>], - msrv: Option<&RustcVersion>, -) -> bool { - if !meets_msrv(msrv, &MAP_UNWRAP_OR_MSRV) { - return false; - } - // lint if the caller of `map()` is an `Option` - let is_option = is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(&map_args[0]), sym::option_type); - let is_result = is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(&map_args[0]), sym::result_type); - - if is_option || is_result { - // Don't make a suggestion that may fail to compile due to mutably borrowing - // the same variable twice. - let map_mutated_vars = mutated_variables(&map_args[0], cx); - let unwrap_mutated_vars = mutated_variables(&unwrap_args[1], cx); - if let (Some(map_mutated_vars), Some(unwrap_mutated_vars)) = (map_mutated_vars, unwrap_mutated_vars) { - if map_mutated_vars.intersection(&unwrap_mutated_vars).next().is_some() { - return false; - } - } else { - return false; - } - - // lint message - let msg = if is_option { - "called `map(<f>).unwrap_or_else(<g>)` on an `Option` value. This can be done more directly by calling \ - `map_or_else(<g>, <f>)` instead" - } else { - "called `map(<f>).unwrap_or_else(<g>)` on a `Result` value. This can be done more directly by calling \ - `.map_or_else(<g>, <f>)` instead" - }; - // get snippets for args to map() and unwrap_or_else() - let map_snippet = snippet(cx, map_args[1].span, ".."); - let unwrap_snippet = snippet(cx, unwrap_args[1].span, ".."); - // lint, with note if neither arg is > 1 line and both map() and - // unwrap_or_else() have the same span - let multiline = map_snippet.lines().count() > 1 || unwrap_snippet.lines().count() > 1; - let same_span = map_args[1].span.ctxt() == unwrap_args[1].span.ctxt(); - if same_span && !multiline { - let var_snippet = snippet(cx, map_args[0].span, ".."); - span_lint_and_sugg( - cx, - MAP_UNWRAP_OR, - expr.span, - msg, - "try this", - format!("{}.map_or_else({}, {})", var_snippet, unwrap_snippet, map_snippet), - Applicability::MachineApplicable, - ); - return true; - } else if same_span && multiline { - span_lint(cx, MAP_UNWRAP_OR, expr.span, msg); - return true; - } - } - - false -} - -/// lint use of `_.map_or(None, _)` for `Option`s and `Result`s -fn lint_map_or_none<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, map_or_args: &'tcx [hir::Expr<'_>]) { - let is_option = is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(&map_or_args[0]), sym::option_type); - let is_result = is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(&map_or_args[0]), sym::result_type); - - // There are two variants of this `map_or` lint: - // (1) using `map_or` as an adapter from `Result<T,E>` to `Option<T>` - // (2) using `map_or` as a combinator instead of `and_then` - // - // (For this lint) we don't care if any other type calls `map_or` - if !is_option && !is_result { - return; - } - - let (lint_name, msg, instead, hint) = { - let default_arg_is_none = if let hir::ExprKind::Path(ref qpath) = map_or_args[1].kind { - match_qpath(qpath, &paths::OPTION_NONE) - } else { - return; - }; - - if !default_arg_is_none { - // nothing to lint! - return; - } - - let f_arg_is_some = if let hir::ExprKind::Path(ref qpath) = map_or_args[2].kind { - match_qpath(qpath, &paths::OPTION_SOME) - } else { - false - }; - - if is_option { - let self_snippet = snippet(cx, map_or_args[0].span, ".."); - let func_snippet = snippet(cx, map_or_args[2].span, ".."); - let msg = "called `map_or(None, ..)` on an `Option` value. This can be done more directly by calling \ - `and_then(..)` instead"; - ( - OPTION_MAP_OR_NONE, - msg, - "try using `and_then` instead", - format!("{0}.and_then({1})", self_snippet, func_snippet), - ) - } else if f_arg_is_some { - let msg = "called `map_or(None, Some)` on a `Result` value. This can be done more directly by calling \ - `ok()` instead"; - let self_snippet = snippet(cx, map_or_args[0].span, ".."); - ( - RESULT_MAP_OR_INTO_OPTION, - msg, - "try using `ok` instead", - format!("{0}.ok()", self_snippet), - ) - } else { - // nothing to lint! - return; - } - }; - - span_lint_and_sugg( - cx, - lint_name, - expr.span, - msg, - instead, - hint, - Applicability::MachineApplicable, - ); -} - -/// lint use of `filter().next()` for `Iterators` -fn lint_filter_next<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, filter_args: &'tcx [hir::Expr<'_>]) { - // lint if caller of `.filter().next()` is an Iterator - if match_trait_method(cx, expr, &paths::ITERATOR) { - let msg = "called `filter(..).next()` on an `Iterator`. This is more succinctly expressed by calling \ - `.find(..)` instead."; - let filter_snippet = snippet(cx, filter_args[1].span, ".."); - if filter_snippet.lines().count() <= 1 { - let iter_snippet = snippet(cx, filter_args[0].span, ".."); - // add note if not multi-line - span_lint_and_sugg( - cx, - FILTER_NEXT, - expr.span, - msg, - "try this", - format!("{}.find({})", iter_snippet, filter_snippet), - Applicability::MachineApplicable, - ); - } else { - span_lint(cx, FILTER_NEXT, expr.span, msg); - } - } -} - -/// lint use of `skip_while().next()` for `Iterators` -fn lint_skip_while_next<'tcx>( - cx: &LateContext<'tcx>, - expr: &'tcx hir::Expr<'_>, - _skip_while_args: &'tcx [hir::Expr<'_>], -) { - // lint if caller of `.skip_while().next()` is an Iterator - if match_trait_method(cx, expr, &paths::ITERATOR) { - span_lint_and_help( - cx, - SKIP_WHILE_NEXT, - expr.span, - "called `skip_while(<p>).next()` on an `Iterator`", - None, - "this is more succinctly expressed by calling `.find(!<p>)` instead", - ); - } -} - -/// lint use of `filter().map()` or `find().map()` for `Iterators` -fn lint_filter_map<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, is_find: bool) { - if_chain! { - if let ExprKind::MethodCall(_, _, [map_recv, map_arg], map_span) = expr.kind; - if let ExprKind::MethodCall(_, _, [_, filter_arg], filter_span) = map_recv.kind; - if match_trait_method(cx, map_recv, &paths::ITERATOR); - - // filter(|x| ...is_some())... - if let ExprKind::Closure(_, _, filter_body_id, ..) = filter_arg.kind; - let filter_body = cx.tcx.hir().body(filter_body_id); - if let [filter_param] = filter_body.params; - // optional ref pattern: `filter(|&x| ..)` - let (filter_pat, is_filter_param_ref) = if let PatKind::Ref(ref_pat, _) = filter_param.pat.kind { - (ref_pat, true) - } else { - (filter_param.pat, false) - }; - // closure ends with is_some() or is_ok() - if let PatKind::Binding(_, filter_param_id, _, None) = filter_pat.kind; - if let ExprKind::MethodCall(path, _, [filter_arg], _) = filter_body.value.kind; - if let Some(opt_ty) = cx.typeck_results().expr_ty(filter_arg).ty_adt_def(); - if let Some(is_result) = if cx.tcx.is_diagnostic_item(sym::option_type, opt_ty.did) { - Some(false) - } else if cx.tcx.is_diagnostic_item(sym::result_type, opt_ty.did) { - Some(true) - } else { - None - }; - if path.ident.name.as_str() == if is_result { "is_ok" } else { "is_some" }; - - // ...map(|x| ...unwrap()) - if let ExprKind::Closure(_, _, map_body_id, ..) = map_arg.kind; - let map_body = cx.tcx.hir().body(map_body_id); - if let [map_param] = map_body.params; - if let PatKind::Binding(_, map_param_id, map_param_ident, None) = map_param.pat.kind; - // closure ends with expect() or unwrap() - if let ExprKind::MethodCall(seg, _, [map_arg, ..], _) = map_body.value.kind; - if matches!(seg.ident.name, sym::expect | sym::unwrap | sym::unwrap_or); - - let eq_fallback = |a: &Expr<'_>, b: &Expr<'_>| { - // in `filter(|x| ..)`, replace `*x` with `x` - let a_path = if_chain! { - if !is_filter_param_ref; - if let ExprKind::Unary(UnOp::Deref, expr_path) = a.kind; - then { expr_path } else { a } - }; - // let the filter closure arg and the map closure arg be equal - if_chain! { - if path_to_local_id(a_path, filter_param_id); - if path_to_local_id(b, map_param_id); - if TyS::same_type(cx.typeck_results().expr_ty_adjusted(a), cx.typeck_results().expr_ty_adjusted(b)); - then { - return true; - } - } - false - }; - if SpanlessEq::new(cx).expr_fallback(eq_fallback).eq_expr(filter_arg, map_arg); - then { - let span = filter_span.to(map_span); - let (filter_name, lint) = if is_find { - ("find", MANUAL_FIND_MAP) - } else { - ("filter", MANUAL_FILTER_MAP) - }; - let msg = format!("`{}(..).map(..)` can be simplified as `{0}_map(..)`", filter_name); - let to_opt = if is_result { ".ok()" } else { "" }; - let sugg = format!("{}_map(|{}| {}{})", filter_name, map_param_ident, - snippet(cx, map_arg.span, ".."), to_opt); - span_lint_and_sugg(cx, lint, span, &msg, "try", sugg, Applicability::MachineApplicable); - } - } -} - -const FILTER_MAP_NEXT_MSRV: RustcVersion = RustcVersion::new(1, 30, 0); - -/// lint use of `filter_map().next()` for `Iterators` -fn lint_filter_map_next<'tcx>( - cx: &LateContext<'tcx>, - expr: &'tcx hir::Expr<'_>, - filter_args: &'tcx [hir::Expr<'_>], - msrv: Option<&RustcVersion>, -) { - if match_trait_method(cx, expr, &paths::ITERATOR) { - if !meets_msrv(msrv, &FILTER_MAP_NEXT_MSRV) { - return; - } - - let msg = "called `filter_map(..).next()` on an `Iterator`. This is more succinctly expressed by calling \ - `.find_map(..)` instead."; - let filter_snippet = snippet(cx, filter_args[1].span, ".."); - if filter_snippet.lines().count() <= 1 { - let iter_snippet = snippet(cx, filter_args[0].span, ".."); - span_lint_and_sugg( - cx, - FILTER_MAP_NEXT, - expr.span, - msg, - "try this", - format!("{}.find_map({})", iter_snippet, filter_snippet), - Applicability::MachineApplicable, - ); - } else { - span_lint(cx, FILTER_MAP_NEXT, expr.span, msg); - } - } -} - -/// lint use of `filter_map().map()` for `Iterators` -fn lint_filter_map_map<'tcx>( - cx: &LateContext<'tcx>, - expr: &'tcx hir::Expr<'_>, - _filter_args: &'tcx [hir::Expr<'_>], - _map_args: &'tcx [hir::Expr<'_>], -) { - // lint if caller of `.filter_map().map()` is an Iterator - if match_trait_method(cx, expr, &paths::ITERATOR) { - let msg = "called `filter_map(..).map(..)` on an `Iterator`"; - let hint = "this is more succinctly expressed by only calling `.filter_map(..)` instead"; - span_lint_and_help(cx, FILTER_MAP, expr.span, msg, None, hint); - } -} - -/// lint use of `filter().flat_map()` for `Iterators` -fn lint_filter_flat_map<'tcx>( - cx: &LateContext<'tcx>, - expr: &'tcx hir::Expr<'_>, - _filter_args: &'tcx [hir::Expr<'_>], - _map_args: &'tcx [hir::Expr<'_>], -) { - // lint if caller of `.filter().flat_map()` is an Iterator - if match_trait_method(cx, expr, &paths::ITERATOR) { - let msg = "called `filter(..).flat_map(..)` on an `Iterator`"; - let hint = "this is more succinctly expressed by calling `.flat_map(..)` \ - and filtering by returning `iter::empty()`"; - span_lint_and_help(cx, FILTER_MAP, expr.span, msg, None, hint); - } -} - -/// lint use of `filter_map().flat_map()` for `Iterators` -fn lint_filter_map_flat_map<'tcx>( - cx: &LateContext<'tcx>, - expr: &'tcx hir::Expr<'_>, - _filter_args: &'tcx [hir::Expr<'_>], - _map_args: &'tcx [hir::Expr<'_>], -) { - // lint if caller of `.filter_map().flat_map()` is an Iterator - if match_trait_method(cx, expr, &paths::ITERATOR) { - let msg = "called `filter_map(..).flat_map(..)` on an `Iterator`"; - let hint = "this is more succinctly expressed by calling `.flat_map(..)` \ - and filtering by returning `iter::empty()`"; - span_lint_and_help(cx, FILTER_MAP, expr.span, msg, None, hint); - } -} - -/// lint use of `flat_map` for `Iterators` where `flatten` would be sufficient -fn lint_flat_map_identity<'tcx>( - cx: &LateContext<'tcx>, - expr: &'tcx hir::Expr<'_>, - flat_map_args: &'tcx [hir::Expr<'_>], - flat_map_span: Span, -) { - if match_trait_method(cx, expr, &paths::ITERATOR) { - let arg_node = &flat_map_args[1].kind; - - let apply_lint = |message: &str| { - span_lint_and_sugg( - cx, - FLAT_MAP_IDENTITY, - flat_map_span.with_hi(expr.span.hi()), - message, - "try", - "flatten()".to_string(), - Applicability::MachineApplicable, - ); - }; - - if_chain! { - if let hir::ExprKind::Closure(_, _, body_id, _, _) = arg_node; - let body = cx.tcx.hir().body(*body_id); - - if let hir::PatKind::Binding(_, _, binding_ident, _) = body.params[0].pat.kind; - if let hir::ExprKind::Path(hir::QPath::Resolved(_, ref path)) = body.value.kind; - - if path.segments.len() == 1; - if path.segments[0].ident.name == binding_ident.name; - - then { - apply_lint("called `flat_map(|x| x)` on an `Iterator`"); - } - } - - if_chain! { - if let hir::ExprKind::Path(ref qpath) = arg_node; - - if match_qpath(qpath, &paths::STD_CONVERT_IDENTITY); - - then { - apply_lint("called `flat_map(std::convert::identity)` on an `Iterator`"); - } - } - } -} - -/// lint searching an Iterator followed by `is_some()` -/// or calling `find()` on a string followed by `is_some()` -fn lint_search_is_some<'tcx>( - cx: &LateContext<'tcx>, - expr: &'tcx hir::Expr<'_>, - search_method: &str, - search_args: &'tcx [hir::Expr<'_>], - is_some_args: &'tcx [hir::Expr<'_>], - method_span: Span, -) { - // lint if caller of search is an Iterator - if match_trait_method(cx, &is_some_args[0], &paths::ITERATOR) { - let msg = format!( - "called `is_some()` after searching an `Iterator` with `{}`", - search_method - ); - let hint = "this is more succinctly expressed by calling `any()`"; - let search_snippet = snippet(cx, search_args[1].span, ".."); - if search_snippet.lines().count() <= 1 { - // suggest `any(|x| ..)` instead of `any(|&x| ..)` for `find(|&x| ..).is_some()` - // suggest `any(|..| *..)` instead of `any(|..| **..)` for `find(|..| **..).is_some()` - let any_search_snippet = if_chain! { - if search_method == "find"; - if let hir::ExprKind::Closure(_, _, body_id, ..) = search_args[1].kind; - let closure_body = cx.tcx.hir().body(body_id); - if let Some(closure_arg) = closure_body.params.get(0); - then { - if let hir::PatKind::Ref(..) = closure_arg.pat.kind { - Some(search_snippet.replacen('&', "", 1)) - } else if let PatKind::Binding(_, _, ident, _) = strip_pat_refs(&closure_arg.pat).kind { - let name = &*ident.name.as_str(); - Some(search_snippet.replace(&format!("*{}", name), name)) - } else { - None - } - } else { - None - } - }; - // add note if not multi-line - span_lint_and_sugg( - cx, - SEARCH_IS_SOME, - method_span.with_hi(expr.span.hi()), - &msg, - "use `any()` instead", - format!( - "any({})", - any_search_snippet.as_ref().map_or(&*search_snippet, String::as_str) - ), - Applicability::MachineApplicable, - ); - } else { - span_lint_and_help(cx, SEARCH_IS_SOME, expr.span, &msg, None, hint); - } - } - // lint if `find()` is called by `String` or `&str` - else if search_method == "find" { - let is_string_or_str_slice = |e| { - let self_ty = cx.typeck_results().expr_ty(e).peel_refs(); - if is_type_diagnostic_item(cx, self_ty, sym::string_type) { - true - } else { - *self_ty.kind() == ty::Str - } - }; - if_chain! { - if is_string_or_str_slice(&search_args[0]); - if is_string_or_str_slice(&search_args[1]); - then { - let msg = "called `is_some()` after calling `find()` on a string"; - let mut applicability = Applicability::MachineApplicable; - let find_arg = snippet_with_applicability(cx, search_args[1].span, "..", &mut applicability); - span_lint_and_sugg( - cx, - SEARCH_IS_SOME, - method_span.with_hi(expr.span.hi()), - msg, - "use `contains()` instead", - format!("contains({})", find_arg), - applicability, - ); - } - } - } -} - -/// Used for `lint_binary_expr_with_method_call`. -#[derive(Copy, Clone)] -struct BinaryExprInfo<'a> { - expr: &'a hir::Expr<'a>, - chain: &'a hir::Expr<'a>, - other: &'a hir::Expr<'a>, - eq: bool, -} - -/// Checks for the `CHARS_NEXT_CMP` and `CHARS_LAST_CMP` lints. -fn lint_binary_expr_with_method_call(cx: &LateContext<'_>, info: &mut BinaryExprInfo<'_>) { - macro_rules! lint_with_both_lhs_and_rhs { - ($func:ident, $cx:expr, $info:ident) => { - if !$func($cx, $info) { - ::std::mem::swap(&mut $info.chain, &mut $info.other); - if $func($cx, $info) { - return; - } - } - }; - } - - lint_with_both_lhs_and_rhs!(lint_chars_next_cmp, cx, info); - lint_with_both_lhs_and_rhs!(lint_chars_last_cmp, cx, info); - lint_with_both_lhs_and_rhs!(lint_chars_next_cmp_with_unwrap, cx, info); - lint_with_both_lhs_and_rhs!(lint_chars_last_cmp_with_unwrap, cx, info); -} - -/// Wrapper fn for `CHARS_NEXT_CMP` and `CHARS_LAST_CMP` lints. -fn lint_chars_cmp( - cx: &LateContext<'_>, - info: &BinaryExprInfo<'_>, - chain_methods: &[&str], - lint: &'static Lint, - suggest: &str, -) -> bool { - if_chain! { - if let Some(args) = method_chain_args(info.chain, chain_methods); - if let hir::ExprKind::Call(ref fun, ref arg_char) = info.other.kind; - if arg_char.len() == 1; - if let hir::ExprKind::Path(ref qpath) = fun.kind; - if let Some(segment) = single_segment_path(qpath); - if segment.ident.name == sym::Some; - then { - let mut applicability = Applicability::MachineApplicable; - let self_ty = cx.typeck_results().expr_ty_adjusted(&args[0][0]).peel_refs(); - - if *self_ty.kind() != ty::Str { - return false; - } - - span_lint_and_sugg( - cx, - lint, - info.expr.span, - &format!("you should use the `{}` method", suggest), - "like this", - format!("{}{}.{}({})", - if info.eq { "" } else { "!" }, - snippet_with_applicability(cx, args[0][0].span, "..", &mut applicability), - suggest, - snippet_with_applicability(cx, arg_char[0].span, "..", &mut applicability)), - applicability, - ); - - return true; - } - } - - false -} - -/// Checks for the `CHARS_NEXT_CMP` lint. -fn lint_chars_next_cmp<'tcx>(cx: &LateContext<'tcx>, info: &BinaryExprInfo<'_>) -> bool { - lint_chars_cmp(cx, info, &["chars", "next"], CHARS_NEXT_CMP, "starts_with") -} - -/// Checks for the `CHARS_LAST_CMP` lint. -fn lint_chars_last_cmp<'tcx>(cx: &LateContext<'tcx>, info: &BinaryExprInfo<'_>) -> bool { - if lint_chars_cmp(cx, info, &["chars", "last"], CHARS_LAST_CMP, "ends_with") { - true - } else { - lint_chars_cmp(cx, info, &["chars", "next_back"], CHARS_LAST_CMP, "ends_with") - } -} - -/// Wrapper fn for `CHARS_NEXT_CMP` and `CHARS_LAST_CMP` lints with `unwrap()`. -fn lint_chars_cmp_with_unwrap<'tcx>( - cx: &LateContext<'tcx>, - info: &BinaryExprInfo<'_>, - chain_methods: &[&str], - lint: &'static Lint, - suggest: &str, -) -> bool { - if_chain! { - if let Some(args) = method_chain_args(info.chain, chain_methods); - if let hir::ExprKind::Lit(ref lit) = info.other.kind; - if let ast::LitKind::Char(c) = lit.node; - then { - let mut applicability = Applicability::MachineApplicable; - span_lint_and_sugg( - cx, - lint, - info.expr.span, - &format!("you should use the `{}` method", suggest), - "like this", - format!("{}{}.{}('{}')", - if info.eq { "" } else { "!" }, - snippet_with_applicability(cx, args[0][0].span, "..", &mut applicability), - suggest, - c), - applicability, - ); - - true - } else { - false - } - } -} - -/// Checks for the `CHARS_NEXT_CMP` lint with `unwrap()`. -fn lint_chars_next_cmp_with_unwrap<'tcx>(cx: &LateContext<'tcx>, info: &BinaryExprInfo<'_>) -> bool { - lint_chars_cmp_with_unwrap(cx, info, &["chars", "next", "unwrap"], CHARS_NEXT_CMP, "starts_with") -} - -/// Checks for the `CHARS_LAST_CMP` lint with `unwrap()`. -fn lint_chars_last_cmp_with_unwrap<'tcx>(cx: &LateContext<'tcx>, info: &BinaryExprInfo<'_>) -> bool { - if lint_chars_cmp_with_unwrap(cx, info, &["chars", "last", "unwrap"], CHARS_LAST_CMP, "ends_with") { - true - } else { - lint_chars_cmp_with_unwrap(cx, info, &["chars", "next_back", "unwrap"], CHARS_LAST_CMP, "ends_with") - } -} - -fn get_hint_if_single_char_arg( - cx: &LateContext<'_>, - arg: &hir::Expr<'_>, - applicability: &mut Applicability, -) -> Option<String> { - if_chain! { - if let hir::ExprKind::Lit(lit) = &arg.kind; - if let ast::LitKind::Str(r, style) = lit.node; - let string = r.as_str(); - if string.chars().count() == 1; - then { - let snip = snippet_with_applicability(cx, arg.span, &string, applicability); - let ch = if let ast::StrStyle::Raw(nhash) = style { - let nhash = nhash as usize; - // for raw string: r##"a"## - &snip[(nhash + 2)..(snip.len() - 1 - nhash)] - } else { - // for regular string: "a" - &snip[1..(snip.len() - 1)] - }; - let hint = format!("'{}'", if ch == "'" { "\\'" } else { ch }); - Some(hint) - } else { - None - } - } -} - -/// lint for length-1 `str`s for methods in `PATTERN_METHODS` -fn lint_single_char_pattern(cx: &LateContext<'_>, _expr: &hir::Expr<'_>, arg: &hir::Expr<'_>) { - let mut applicability = Applicability::MachineApplicable; - if let Some(hint) = get_hint_if_single_char_arg(cx, arg, &mut applicability) { - span_lint_and_sugg( - cx, - SINGLE_CHAR_PATTERN, - arg.span, - "single-character string constant used as pattern", - "try using a `char` instead", - hint, - applicability, - ); - } -} - -/// lint for length-1 `str`s as argument for `push_str` -fn lint_single_char_push_string(cx: &LateContext<'_>, expr: &hir::Expr<'_>, args: &[hir::Expr<'_>]) { - let mut applicability = Applicability::MachineApplicable; - if let Some(extension_string) = get_hint_if_single_char_arg(cx, &args[1], &mut applicability) { - let base_string_snippet = - snippet_with_applicability(cx, args[0].span.source_callsite(), "..", &mut applicability); - let sugg = format!("{}.push({})", base_string_snippet, extension_string); - span_lint_and_sugg( - cx, - SINGLE_CHAR_ADD_STR, - expr.span, - "calling `push_str()` using a single-character string literal", - "consider using `push` with a character literal", - sugg, - applicability, - ); - } -} - -/// lint for length-1 `str`s as argument for `insert_str` -fn lint_single_char_insert_string(cx: &LateContext<'_>, expr: &hir::Expr<'_>, args: &[hir::Expr<'_>]) { - let mut applicability = Applicability::MachineApplicable; - if let Some(extension_string) = get_hint_if_single_char_arg(cx, &args[2], &mut applicability) { - let base_string_snippet = - snippet_with_applicability(cx, args[0].span.source_callsite(), "_", &mut applicability); - let pos_arg = snippet_with_applicability(cx, args[1].span, "..", &mut applicability); - let sugg = format!("{}.insert({}, {})", base_string_snippet, pos_arg, extension_string); - span_lint_and_sugg( - cx, - SINGLE_CHAR_ADD_STR, - expr.span, - "calling `insert_str()` using a single-character string literal", - "consider using `insert` with a character literal", - sugg, - applicability, - ); - } -} - -/// Checks for the `USELESS_ASREF` lint. -fn lint_asref(cx: &LateContext<'_>, expr: &hir::Expr<'_>, call_name: &str, as_ref_args: &[hir::Expr<'_>]) { - // when we get here, we've already checked that the call name is "as_ref" or "as_mut" - // check if the call is to the actual `AsRef` or `AsMut` trait - if match_trait_method(cx, expr, &paths::ASREF_TRAIT) || match_trait_method(cx, expr, &paths::ASMUT_TRAIT) { - // check if the type after `as_ref` or `as_mut` is the same as before - let recvr = &as_ref_args[0]; - let rcv_ty = cx.typeck_results().expr_ty(recvr); - let res_ty = cx.typeck_results().expr_ty(expr); - let (base_res_ty, res_depth) = walk_ptrs_ty_depth(res_ty); - let (base_rcv_ty, rcv_depth) = walk_ptrs_ty_depth(rcv_ty); - if base_rcv_ty == base_res_ty && rcv_depth >= res_depth { - // allow the `as_ref` or `as_mut` if it is followed by another method call - if_chain! { - if let Some(parent) = get_parent_expr(cx, expr); - if let hir::ExprKind::MethodCall(_, ref span, _, _) = parent.kind; - if span != &expr.span; - then { - return; - } - } - - let mut applicability = Applicability::MachineApplicable; - span_lint_and_sugg( - cx, - USELESS_ASREF, - expr.span, - &format!("this call to `{}` does nothing", call_name), - "try this", - snippet_with_applicability(cx, recvr.span, "..", &mut applicability).to_string(), - applicability, - ); - } - } -} - -fn ty_has_iter_method(cx: &LateContext<'_>, self_ref_ty: Ty<'_>) -> Option<(Symbol, &'static str)> { - has_iter_method(cx, self_ref_ty).map(|ty_name| { - let mutbl = match self_ref_ty.kind() { - ty::Ref(_, _, mutbl) => mutbl, - _ => unreachable!(), - }; - let method_name = match mutbl { - hir::Mutability::Not => "iter", - hir::Mutability::Mut => "iter_mut", - }; - (ty_name, method_name) - }) -} - -fn lint_into_iter(cx: &LateContext<'_>, expr: &hir::Expr<'_>, self_ref_ty: Ty<'_>, method_span: Span) { - if !match_trait_method(cx, expr, &paths::INTO_ITERATOR) { - return; - } - if let Some((kind, method_name)) = ty_has_iter_method(cx, self_ref_ty) { - span_lint_and_sugg( - cx, - INTO_ITER_ON_REF, - method_span, - &format!( - "this `.into_iter()` call is equivalent to `.{}()` and will not consume the `{}`", - method_name, kind, - ), - "call directly", - method_name.to_string(), - Applicability::MachineApplicable, - ); - } -} - -/// lint for `MaybeUninit::uninit().assume_init()` (we already have the latter) -fn lint_maybe_uninit(cx: &LateContext<'_>, expr: &hir::Expr<'_>, outer: &hir::Expr<'_>) { - if_chain! { - if let hir::ExprKind::Call(ref callee, ref args) = expr.kind; - if args.is_empty(); - if let hir::ExprKind::Path(ref path) = callee.kind; - if match_qpath(path, &paths::MEM_MAYBEUNINIT_UNINIT); - if !is_maybe_uninit_ty_valid(cx, cx.typeck_results().expr_ty_adjusted(outer)); - then { - span_lint( - cx, - UNINIT_ASSUMED_INIT, - outer.span, - "this call for this type may be undefined behavior" - ); - } - } -} - -fn is_maybe_uninit_ty_valid(cx: &LateContext<'_>, ty: Ty<'_>) -> bool { - match ty.kind() { - ty::Array(ref component, _) => is_maybe_uninit_ty_valid(cx, component), - ty::Tuple(ref types) => types.types().all(|ty| is_maybe_uninit_ty_valid(cx, ty)), - ty::Adt(ref adt, _) => match_def_path(cx, adt.did, &paths::MEM_MAYBEUNINIT), - _ => false, - } -} - -fn lint_suspicious_map(cx: &LateContext<'_>, expr: &hir::Expr<'_>) { - span_lint_and_help( - cx, - SUSPICIOUS_MAP, - expr.span, - "this call to `map()` won't have an effect on the call to `count()`", - None, - "make sure you did not confuse `map` with `filter` or `for_each`", - ); -} - -const OPTION_AS_REF_DEREF_MSRV: RustcVersion = RustcVersion::new(1, 40, 0); - -/// lint use of `_.as_ref().map(Deref::deref)` for `Option`s -fn lint_option_as_ref_deref<'tcx>( - cx: &LateContext<'tcx>, - expr: &hir::Expr<'_>, - as_ref_args: &[hir::Expr<'_>], - map_args: &[hir::Expr<'_>], - is_mut: bool, - msrv: Option<&RustcVersion>, -) { - if !meets_msrv(msrv, &OPTION_AS_REF_DEREF_MSRV) { - return; - } - - let same_mutability = |m| (is_mut && m == &hir::Mutability::Mut) || (!is_mut && m == &hir::Mutability::Not); - - let option_ty = cx.typeck_results().expr_ty(&as_ref_args[0]); - if !is_type_diagnostic_item(cx, option_ty, sym::option_type) { - return; - } - - let deref_aliases: [&[&str]; 9] = [ - &paths::DEREF_TRAIT_METHOD, - &paths::DEREF_MUT_TRAIT_METHOD, - &paths::CSTRING_AS_C_STR, - &paths::OS_STRING_AS_OS_STR, - &paths::PATH_BUF_AS_PATH, - &paths::STRING_AS_STR, - &paths::STRING_AS_MUT_STR, - &paths::VEC_AS_SLICE, - &paths::VEC_AS_MUT_SLICE, - ]; - - let is_deref = match map_args[1].kind { - hir::ExprKind::Path(ref expr_qpath) => cx - .qpath_res(expr_qpath, map_args[1].hir_id) - .opt_def_id() - .map_or(false, |fun_def_id| { - deref_aliases.iter().any(|path| match_def_path(cx, fun_def_id, path)) - }), - hir::ExprKind::Closure(_, _, body_id, _, _) => { - let closure_body = cx.tcx.hir().body(body_id); - let closure_expr = remove_blocks(&closure_body.value); - - match &closure_expr.kind { - hir::ExprKind::MethodCall(_, _, args, _) => { - if_chain! { - if args.len() == 1; - if path_to_local_id(&args[0], closure_body.params[0].pat.hir_id); - let adj = cx - .typeck_results() - .expr_adjustments(&args[0]) - .iter() - .map(|x| &x.kind) - .collect::<Box<[_]>>(); - if let [ty::adjustment::Adjust::Deref(None), ty::adjustment::Adjust::Borrow(_)] = *adj; - then { - let method_did = cx.typeck_results().type_dependent_def_id(closure_expr.hir_id).unwrap(); - deref_aliases.iter().any(|path| match_def_path(cx, method_did, path)) - } else { - false - } - } - }, - hir::ExprKind::AddrOf(hir::BorrowKind::Ref, m, ref inner) if same_mutability(m) => { - if_chain! { - if let hir::ExprKind::Unary(hir::UnOp::Deref, ref inner1) = inner.kind; - if let hir::ExprKind::Unary(hir::UnOp::Deref, ref inner2) = inner1.kind; - then { - path_to_local_id(inner2, closure_body.params[0].pat.hir_id) - } else { - false - } - } - }, - _ => false, - } - }, - _ => false, - }; - - if is_deref { - let current_method = if is_mut { - format!(".as_mut().map({})", snippet(cx, map_args[1].span, "..")) - } else { - format!(".as_ref().map({})", snippet(cx, map_args[1].span, "..")) - }; - let method_hint = if is_mut { "as_deref_mut" } else { "as_deref" }; - let hint = format!("{}.{}()", snippet(cx, as_ref_args[0].span, ".."), method_hint); - let suggestion = format!("try using {} instead", method_hint); - - let msg = format!( - "called `{0}` on an Option value. This can be done more directly \ - by calling `{1}` instead", - current_method, hint - ); - span_lint_and_sugg( - cx, - OPTION_AS_REF_DEREF, - expr.span, - &msg, - &suggestion, - hint, - Applicability::MachineApplicable, - ); - } -} - -fn lint_map_collect( - cx: &LateContext<'_>, - expr: &hir::Expr<'_>, - map_args: &[hir::Expr<'_>], - collect_args: &[hir::Expr<'_>], -) { - if_chain! { - // called on Iterator - if let [map_expr] = collect_args; - if match_trait_method(cx, map_expr, &paths::ITERATOR); - // return of collect `Result<(),_>` - let collect_ret_ty = cx.typeck_results().expr_ty(expr); - if is_type_diagnostic_item(cx, collect_ret_ty, sym::result_type); - if let ty::Adt(_, substs) = collect_ret_ty.kind(); - if let Some(result_t) = substs.types().next(); - if result_t.is_unit(); - // get parts for snippet - if let [iter, map_fn] = map_args; - then { - span_lint_and_sugg( - cx, - MAP_COLLECT_RESULT_UNIT, - expr.span, - "`.map().collect()` can be replaced with `.try_for_each()`", - "try this", - format!( - "{}.try_for_each({})", - snippet(cx, iter.span, ".."), - snippet(cx, map_fn.span, "..") - ), - Applicability::MachineApplicable, - ); - } - } -} - -/// Given a `Result<T, E>` type, return its error type (`E`). -fn get_error_type<'a>(cx: &LateContext<'_>, ty: Ty<'a>) -> Option<Ty<'a>> { - match ty.kind() { - ty::Adt(_, substs) if is_type_diagnostic_item(cx, ty, sym::result_type) => substs.types().nth(1), - _ => None, - } -} - -/// This checks whether a given type is known to implement Debug. -fn has_debug_impl<'tcx>(ty: Ty<'tcx>, cx: &LateContext<'tcx>) -> bool { - cx.tcx - .get_diagnostic_item(sym::debug_trait) - .map_or(false, |debug| implements_trait(cx, ty, debug, &[])) -} - -enum Convention { - Eq(&'static str), - StartsWith(&'static str), -} - -#[rustfmt::skip] -const CONVENTIONS: [(Convention, &[SelfKind]); 7] = [ - (Convention::Eq("new"), &[SelfKind::No]), - (Convention::StartsWith("as_"), &[SelfKind::Ref, SelfKind::RefMut]), - (Convention::StartsWith("from_"), &[SelfKind::No]), - (Convention::StartsWith("into_"), &[SelfKind::Value]), - (Convention::StartsWith("is_"), &[SelfKind::Ref, SelfKind::No]), - (Convention::Eq("to_mut"), &[SelfKind::RefMut]), - (Convention::StartsWith("to_"), &[SelfKind::Ref]), -]; - -const FN_HEADER: hir::FnHeader = hir::FnHeader { - unsafety: hir::Unsafety::Normal, - constness: hir::Constness::NotConst, - asyncness: hir::IsAsync::NotAsync, - abi: rustc_target::spec::abi::Abi::Rust, -}; - -struct ShouldImplTraitCase { - trait_name: &'static str, - method_name: &'static str, - param_count: usize, - fn_header: hir::FnHeader, - // implicit self kind expected (none, self, &self, ...) - self_kind: SelfKind, - // checks against the output type - output_type: OutType, - // certain methods with explicit lifetimes can't implement the equivalent trait method - lint_explicit_lifetime: bool, -} -impl ShouldImplTraitCase { - const fn new( - trait_name: &'static str, - method_name: &'static str, - param_count: usize, - fn_header: hir::FnHeader, - self_kind: SelfKind, - output_type: OutType, - lint_explicit_lifetime: bool, - ) -> ShouldImplTraitCase { - ShouldImplTraitCase { - trait_name, - method_name, - param_count, - fn_header, - self_kind, - output_type, - lint_explicit_lifetime, +struct ShouldImplTraitCase { + trait_name: &'static str, + method_name: &'static str, + param_count: usize, + fn_header: hir::FnHeader, + // implicit self kind expected (none, self, &self, ...) + self_kind: SelfKind, + // checks against the output type + output_type: OutType, + // certain methods with explicit lifetimes can't implement the equivalent trait method + lint_explicit_lifetime: bool, +} +impl ShouldImplTraitCase { + const fn new( + trait_name: &'static str, + method_name: &'static str, + param_count: usize, + fn_header: hir::FnHeader, + self_kind: SelfKind, + output_type: OutType, + lint_explicit_lifetime: bool, + ) -> ShouldImplTraitCase { + ShouldImplTraitCase { + trait_name, + method_name, + param_count, + fn_header, + self_kind, + output_type, + lint_explicit_lifetime, } } @@ -4031,29 +2249,10 @@ impl SelfKind { #[must_use] fn description(self) -> &'static str { match self { - Self::Value => "self by value", - Self::Ref => "self by reference", - Self::RefMut => "self by mutable reference", - Self::No => "no self", - } - } -} - -impl Convention { - #[must_use] - fn check(&self, other: &str) -> bool { - match *self { - Self::Eq(this) => this == other, - Self::StartsWith(this) => other.starts_with(this) && this != other, - } - } -} - -impl fmt::Display for Convention { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { - match *self { - Self::Eq(this) => this.fmt(f), - Self::StartsWith(this) => this.fmt(f).and_then(|_| '*'.fmt(f)), + Self::Value => "`self` by value", + Self::Ref => "`self` by reference", + Self::RefMut => "`self` by mutable reference", + Self::No => "no `self`", } } } @@ -4067,130 +2266,27 @@ enum OutType { } impl OutType { - fn matches(self, cx: &LateContext<'_>, ty: &hir::FnRetTy<'_>) -> bool { - let is_unit = |ty: &hir::Ty<'_>| SpanlessEq::new(cx).eq_ty_kind(&ty.kind, &hir::TyKind::Tup(&[])); + fn matches(self, ty: &hir::FnRetTy<'_>) -> bool { + let is_unit = |ty: &hir::Ty<'_>| matches!(ty.kind, hir::TyKind::Tup(&[])); match (self, ty) { (Self::Unit, &hir::FnRetTy::DefaultReturn(_)) => true, - (Self::Unit, &hir::FnRetTy::Return(ref ty)) if is_unit(ty) => true, - (Self::Bool, &hir::FnRetTy::Return(ref ty)) if is_bool(ty) => true, - (Self::Any, &hir::FnRetTy::Return(ref ty)) if !is_unit(ty) => true, - (Self::Ref, &hir::FnRetTy::Return(ref ty)) => matches!(ty.kind, hir::TyKind::Rptr(_, _)), + (Self::Unit, &hir::FnRetTy::Return(ty)) if is_unit(ty) => true, + (Self::Bool, &hir::FnRetTy::Return(ty)) if is_bool(ty) => true, + (Self::Any, &hir::FnRetTy::Return(ty)) if !is_unit(ty) => true, + (Self::Ref, &hir::FnRetTy::Return(ty)) => matches!(ty.kind, hir::TyKind::Rptr(_, _)), _ => false, } } } fn is_bool(ty: &hir::Ty<'_>) -> bool { - if let hir::TyKind::Path(ref p) = ty.kind { - match_qpath(p, &["bool"]) + if let hir::TyKind::Path(QPath::Resolved(_, path)) = ty.kind { + matches!(path.res, Res::PrimTy(PrimTy::Bool)) } else { false } } -fn check_pointer_offset(cx: &LateContext<'_>, expr: &hir::Expr<'_>, args: &[hir::Expr<'_>]) { - if_chain! { - if args.len() == 2; - if let ty::RawPtr(ty::TypeAndMut { ref ty, .. }) = cx.typeck_results().expr_ty(&args[0]).kind(); - if let Ok(layout) = cx.tcx.layout_of(cx.param_env.and(ty)); - if layout.is_zst(); - then { - span_lint(cx, ZST_OFFSET, expr.span, "offset calculation on zero-sized value"); - } - } -} - -fn lint_filetype_is_file(cx: &LateContext<'_>, expr: &hir::Expr<'_>, args: &[hir::Expr<'_>]) { - let ty = cx.typeck_results().expr_ty(&args[0]); - - if !match_type(cx, ty, &paths::FILE_TYPE) { - return; - } - - let span: Span; - let verb: &str; - let lint_unary: &str; - let help_unary: &str; - if_chain! { - if let Some(parent) = get_parent_expr(cx, expr); - if let hir::ExprKind::Unary(op, _) = parent.kind; - if op == hir::UnOp::Not; - then { - lint_unary = "!"; - verb = "denies"; - help_unary = ""; - span = parent.span; - } else { - lint_unary = ""; - verb = "covers"; - help_unary = "!"; - span = expr.span; - } - } - let lint_msg = format!("`{}FileType::is_file()` only {} regular files", lint_unary, verb); - let help_msg = format!("use `{}FileType::is_dir()` instead", help_unary); - span_lint_and_help(cx, FILETYPE_IS_FILE, span, &lint_msg, None, &help_msg); -} - -fn lint_from_iter(cx: &LateContext<'_>, expr: &hir::Expr<'_>, args: &[hir::Expr<'_>]) { - let ty = cx.typeck_results().expr_ty(expr); - let arg_ty = cx.typeck_results().expr_ty(&args[0]); - - if_chain! { - if let Some(from_iter_id) = get_trait_def_id(cx, &paths::FROM_ITERATOR); - if let Some(iter_id) = get_trait_def_id(cx, &paths::ITERATOR); - - if implements_trait(cx, ty, from_iter_id, &[]) && implements_trait(cx, arg_ty, iter_id, &[]); - then { - // `expr` implements `FromIterator` trait - let iter_expr = sugg::Sugg::hir(cx, &args[0], "..").maybe_par(); - let turbofish = extract_turbofish(cx, expr, ty); - let sugg = format!("{}.collect::<{}>()", iter_expr, turbofish); - span_lint_and_sugg( - cx, - FROM_ITER_INSTEAD_OF_COLLECT, - expr.span, - "usage of `FromIterator::from_iter`", - "use `.collect()` instead of `::from_iter()`", - sugg, - Applicability::MaybeIncorrect, - ); - } - } -} - -fn extract_turbofish(cx: &LateContext<'_>, expr: &hir::Expr<'_>, ty: Ty<'tcx>) -> String { - if_chain! { - let call_site = expr.span.source_callsite(); - if let Ok(snippet) = cx.sess().source_map().span_to_snippet(call_site); - let snippet_split = snippet.split("::").collect::<Vec<_>>(); - if let Some((_, elements)) = snippet_split.split_last(); - - then { - // is there a type specifier? (i.e.: like `<u32>` in `collections::BTreeSet::<u32>::`) - if let Some(type_specifier) = snippet_split.iter().find(|e| e.starts_with('<') && e.ends_with('>')) { - // remove the type specifier from the path elements - let without_ts = elements.iter().filter_map(|e| { - if e == type_specifier { None } else { Some((*e).to_string()) } - }).collect::<Vec<_>>(); - // join and add the type specifier at the end (i.e.: `collections::BTreeSet<u32>`) - format!("{}{}", without_ts.join("::"), type_specifier) - } else { - // type is not explicitly specified so wildcards are needed - // i.e.: 2 wildcards in `std::collections::BTreeMap<&i32, &char>` - let ty_str = ty.to_string(); - let start = ty_str.find('<').unwrap_or(0); - let end = ty_str.find('>').unwrap_or_else(|| ty_str.len()); - let nb_wildcard = ty_str[start..end].split(',').count(); - let wildcards = format!("_{}", ", _".repeat(nb_wildcard - 1)); - format!("{}<{}>", elements.join("::"), wildcards) - } - } else { - ty.to_string() - } - } -} - fn fn_header_equals(expected: hir::FnHeader, actual: hir::FnHeader) -> bool { expected.constness == actual.constness && expected.unsafety == actual.unsafety diff --git a/src/tools/clippy/clippy_lints/src/methods/ok_expect.rs b/src/tools/clippy/clippy_lints/src/methods/ok_expect.rs new file mode 100644 index 0000000000000..d0b1b4b84be50 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/methods/ok_expect.rs @@ -0,0 +1,46 @@ +use clippy_utils::diagnostics::span_lint_and_help; +use clippy_utils::ty::{implements_trait, is_type_diagnostic_item}; +use if_chain::if_chain; +use rustc_hir as hir; +use rustc_lint::LateContext; +use rustc_middle::ty::{self, Ty}; +use rustc_span::sym; + +use super::OK_EXPECT; + +/// lint use of `ok().expect()` for `Result`s +pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, recv: &hir::Expr<'_>) { + if_chain! { + // lint if the caller of `ok()` is a `Result` + if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(recv), sym::result_type); + let result_type = cx.typeck_results().expr_ty(recv); + if let Some(error_type) = get_error_type(cx, result_type); + if has_debug_impl(error_type, cx); + + then { + span_lint_and_help( + cx, + OK_EXPECT, + expr.span, + "called `ok().expect()` on a `Result` value", + None, + "you can call `expect()` directly on the `Result`", + ); + } + } +} + +/// Given a `Result<T, E>` type, return its error type (`E`). +fn get_error_type<'a>(cx: &LateContext<'_>, ty: Ty<'a>) -> Option<Ty<'a>> { + match ty.kind() { + ty::Adt(_, substs) if is_type_diagnostic_item(cx, ty, sym::result_type) => substs.types().nth(1), + _ => None, + } +} + +/// This checks whether a given type is known to implement Debug. +fn has_debug_impl<'tcx>(ty: Ty<'tcx>, cx: &LateContext<'tcx>) -> bool { + cx.tcx + .get_diagnostic_item(sym::debug_trait) + .map_or(false, |debug| implements_trait(cx, ty, debug, &[])) +} diff --git a/src/tools/clippy/clippy_lints/src/methods/option_as_ref_deref.rs b/src/tools/clippy/clippy_lints/src/methods/option_as_ref_deref.rs new file mode 100644 index 0000000000000..7e9c8fa829dec --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/methods/option_as_ref_deref.rs @@ -0,0 +1,122 @@ +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::source::snippet; +use clippy_utils::ty::is_type_diagnostic_item; +use clippy_utils::{match_def_path, meets_msrv, path_to_local_id, paths, remove_blocks}; +use if_chain::if_chain; +use rustc_errors::Applicability; +use rustc_hir as hir; +use rustc_lint::LateContext; +use rustc_middle::ty; +use rustc_semver::RustcVersion; +use rustc_span::sym; + +use super::OPTION_AS_REF_DEREF; + +const OPTION_AS_REF_DEREF_MSRV: RustcVersion = RustcVersion::new(1, 40, 0); + +/// lint use of `_.as_ref().map(Deref::deref)` for `Option`s +pub(super) fn check<'tcx>( + cx: &LateContext<'tcx>, + expr: &hir::Expr<'_>, + as_ref_recv: &hir::Expr<'_>, + map_arg: &hir::Expr<'_>, + is_mut: bool, + msrv: Option<&RustcVersion>, +) { + if !meets_msrv(msrv, &OPTION_AS_REF_DEREF_MSRV) { + return; + } + + let same_mutability = |m| (is_mut && m == &hir::Mutability::Mut) || (!is_mut && m == &hir::Mutability::Not); + + let option_ty = cx.typeck_results().expr_ty(as_ref_recv); + if !is_type_diagnostic_item(cx, option_ty, sym::option_type) { + return; + } + + let deref_aliases: [&[&str]; 9] = [ + &paths::DEREF_TRAIT_METHOD, + &paths::DEREF_MUT_TRAIT_METHOD, + &paths::CSTRING_AS_C_STR, + &paths::OS_STRING_AS_OS_STR, + &paths::PATH_BUF_AS_PATH, + &paths::STRING_AS_STR, + &paths::STRING_AS_MUT_STR, + &paths::VEC_AS_SLICE, + &paths::VEC_AS_MUT_SLICE, + ]; + + let is_deref = match map_arg.kind { + hir::ExprKind::Path(ref expr_qpath) => cx + .qpath_res(expr_qpath, map_arg.hir_id) + .opt_def_id() + .map_or(false, |fun_def_id| { + deref_aliases.iter().any(|path| match_def_path(cx, fun_def_id, path)) + }), + hir::ExprKind::Closure(_, _, body_id, _, _) => { + let closure_body = cx.tcx.hir().body(body_id); + let closure_expr = remove_blocks(&closure_body.value); + + match &closure_expr.kind { + hir::ExprKind::MethodCall(_, _, args, _) => { + if_chain! { + if args.len() == 1; + if path_to_local_id(&args[0], closure_body.params[0].pat.hir_id); + let adj = cx + .typeck_results() + .expr_adjustments(&args[0]) + .iter() + .map(|x| &x.kind) + .collect::<Box<[_]>>(); + if let [ty::adjustment::Adjust::Deref(None), ty::adjustment::Adjust::Borrow(_)] = *adj; + then { + let method_did = cx.typeck_results().type_dependent_def_id(closure_expr.hir_id).unwrap(); + deref_aliases.iter().any(|path| match_def_path(cx, method_did, path)) + } else { + false + } + } + }, + hir::ExprKind::AddrOf(hir::BorrowKind::Ref, m, inner) if same_mutability(m) => { + if_chain! { + if let hir::ExprKind::Unary(hir::UnOp::Deref, inner1) = inner.kind; + if let hir::ExprKind::Unary(hir::UnOp::Deref, inner2) = inner1.kind; + then { + path_to_local_id(inner2, closure_body.params[0].pat.hir_id) + } else { + false + } + } + }, + _ => false, + } + }, + _ => false, + }; + + if is_deref { + let current_method = if is_mut { + format!(".as_mut().map({})", snippet(cx, map_arg.span, "..")) + } else { + format!(".as_ref().map({})", snippet(cx, map_arg.span, "..")) + }; + let method_hint = if is_mut { "as_deref_mut" } else { "as_deref" }; + let hint = format!("{}.{}()", snippet(cx, as_ref_recv.span, ".."), method_hint); + let suggestion = format!("try using {} instead", method_hint); + + let msg = format!( + "called `{0}` on an Option value. This can be done more directly \ + by calling `{1}` instead", + current_method, hint + ); + span_lint_and_sugg( + cx, + OPTION_AS_REF_DEREF, + expr.span, + &msg, + &suggestion, + hint, + Applicability::MachineApplicable, + ); + } +} diff --git a/src/tools/clippy/clippy_lints/src/methods/option_map_or_none.rs b/src/tools/clippy/clippy_lints/src/methods/option_map_or_none.rs new file mode 100644 index 0000000000000..013a6f90ac97b --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/methods/option_map_or_none.rs @@ -0,0 +1,87 @@ +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::source::snippet; +use clippy_utils::ty::is_type_diagnostic_item; +use clippy_utils::{match_qpath, paths}; +use rustc_errors::Applicability; +use rustc_hir as hir; +use rustc_lint::LateContext; +use rustc_span::symbol::sym; + +use super::OPTION_MAP_OR_NONE; +use super::RESULT_MAP_OR_INTO_OPTION; + +/// lint use of `_.map_or(None, _)` for `Option`s and `Result`s +pub(super) fn check<'tcx>( + cx: &LateContext<'tcx>, + expr: &'tcx hir::Expr<'_>, + recv: &'tcx hir::Expr<'_>, + def_arg: &'tcx hir::Expr<'_>, + map_arg: &'tcx hir::Expr<'_>, +) { + let is_option = is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(recv), sym::option_type); + let is_result = is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(recv), sym::result_type); + + // There are two variants of this `map_or` lint: + // (1) using `map_or` as an adapter from `Result<T,E>` to `Option<T>` + // (2) using `map_or` as a combinator instead of `and_then` + // + // (For this lint) we don't care if any other type calls `map_or` + if !is_option && !is_result { + return; + } + + let (lint_name, msg, instead, hint) = { + let default_arg_is_none = if let hir::ExprKind::Path(ref qpath) = def_arg.kind { + match_qpath(qpath, &paths::OPTION_NONE) + } else { + return; + }; + + if !default_arg_is_none { + // nothing to lint! + return; + } + + let f_arg_is_some = if let hir::ExprKind::Path(ref qpath) = map_arg.kind { + match_qpath(qpath, &paths::OPTION_SOME) + } else { + false + }; + + if is_option { + let self_snippet = snippet(cx, recv.span, ".."); + let func_snippet = snippet(cx, map_arg.span, ".."); + let msg = "called `map_or(None, ..)` on an `Option` value. This can be done more directly by calling \ + `and_then(..)` instead"; + ( + OPTION_MAP_OR_NONE, + msg, + "try using `and_then` instead", + format!("{0}.and_then({1})", self_snippet, func_snippet), + ) + } else if f_arg_is_some { + let msg = "called `map_or(None, Some)` on a `Result` value. This can be done more directly by calling \ + `ok()` instead"; + let self_snippet = snippet(cx, recv.span, ".."); + ( + RESULT_MAP_OR_INTO_OPTION, + msg, + "try using `ok` instead", + format!("{0}.ok()", self_snippet), + ) + } else { + // nothing to lint! + return; + } + }; + + span_lint_and_sugg( + cx, + lint_name, + expr.span, + msg, + instead, + hint, + Applicability::MachineApplicable, + ); +} diff --git a/src/tools/clippy/clippy_lints/src/methods/option_map_unwrap_or.rs b/src/tools/clippy/clippy_lints/src/methods/option_map_unwrap_or.rs index 7763fd5f113fa..5bca49dec2414 100644 --- a/src/tools/clippy/clippy_lints/src/methods/option_map_unwrap_or.rs +++ b/src/tools/clippy/clippy_lints/src/methods/option_map_unwrap_or.rs @@ -1,5 +1,8 @@ -use crate::utils::{differing_macro_contexts, snippet_with_applicability, span_lint_and_then}; -use crate::utils::{is_copy, is_type_diagnostic_item}; +use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::differing_macro_contexts; +use clippy_utils::source::snippet_with_applicability; +use clippy_utils::ty::is_copy; +use clippy_utils::ty::is_type_diagnostic_item; use rustc_data_structures::fx::FxHashSet; use rustc_errors::Applicability; use rustc_hir::intravisit::{walk_path, NestedVisitorMap, Visitor}; @@ -12,16 +15,18 @@ use rustc_span::{sym, Symbol}; use super::MAP_UNWRAP_OR; /// lint use of `map().unwrap_or()` for `Option`s -pub(super) fn lint<'tcx>( +pub(super) fn check<'tcx>( cx: &LateContext<'tcx>, expr: &rustc_hir::Expr<'_>, - map_args: &'tcx [rustc_hir::Expr<'_>], - unwrap_args: &'tcx [rustc_hir::Expr<'_>], + recv: &rustc_hir::Expr<'_>, + map_arg: &'tcx rustc_hir::Expr<'_>, + unwrap_recv: &rustc_hir::Expr<'_>, + unwrap_arg: &'tcx rustc_hir::Expr<'_>, map_span: Span, ) { // lint if the caller of `map()` is an `Option` - if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(&map_args[0]), sym::option_type) { - if !is_copy(cx, cx.typeck_results().expr_ty(&unwrap_args[1])) { + if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(recv), sym::option_type) { + if !is_copy(cx, cx.typeck_results().expr_ty(unwrap_arg)) { // Do not lint if the `map` argument uses identifiers in the `map` // argument that are also used in the `unwrap_or` argument @@ -29,27 +34,27 @@ pub(super) fn lint<'tcx>( cx, identifiers: FxHashSet::default(), }; - unwrap_visitor.visit_expr(&unwrap_args[1]); + unwrap_visitor.visit_expr(unwrap_arg); let mut map_expr_visitor = MapExprVisitor { cx, identifiers: unwrap_visitor.identifiers, found_identifier: false, }; - map_expr_visitor.visit_expr(&map_args[1]); + map_expr_visitor.visit_expr(map_arg); if map_expr_visitor.found_identifier { return; } } - if differing_macro_contexts(unwrap_args[1].span, map_span) { + if differing_macro_contexts(unwrap_arg.span, map_span) { return; } let mut applicability = Applicability::MachineApplicable; // get snippet for unwrap_or() - let unwrap_snippet = snippet_with_applicability(cx, unwrap_args[1].span, "..", &mut applicability); + let unwrap_snippet = snippet_with_applicability(cx, unwrap_arg.span, "..", &mut applicability); // lint message // comparing the snippet from source to raw text ("None") below is safe // because we already have checked the type. @@ -67,14 +72,14 @@ pub(super) fn lint<'tcx>( ); span_lint_and_then(cx, MAP_UNWRAP_OR, expr.span, msg, |diag| { - let map_arg_span = map_args[1].span; + let map_arg_span = map_arg.span; let mut suggestion = vec![ ( map_span, String::from(if unwrap_snippet_none { "and_then" } else { "map_or" }), ), - (expr.span.with_lo(unwrap_args[0].span.hi()), String::from("")), + (expr.span.with_lo(unwrap_recv.span.hi()), String::from("")), ]; if !unwrap_snippet_none { diff --git a/src/tools/clippy/clippy_lints/src/methods/or_fun_call.rs b/src/tools/clippy/clippy_lints/src/methods/or_fun_call.rs new file mode 100644 index 0000000000000..800172f4cf3dc --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/methods/or_fun_call.rs @@ -0,0 +1,183 @@ +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::eager_or_lazy::is_lazyness_candidate; +use clippy_utils::source::{snippet, snippet_with_applicability, snippet_with_macro_callsite}; +use clippy_utils::ty::{implements_trait, is_type_diagnostic_item, match_type}; +use clippy_utils::{contains_return, get_trait_def_id, last_path_segment, paths}; +use if_chain::if_chain; +use rustc_errors::Applicability; +use rustc_hir as hir; +use rustc_hir::{BlockCheckMode, UnsafeSource}; +use rustc_lint::LateContext; +use rustc_middle::ty; +use rustc_span::source_map::Span; +use rustc_span::symbol::{kw, sym}; +use std::borrow::Cow; + +use super::OR_FUN_CALL; + +/// Checks for the `OR_FUN_CALL` lint. +#[allow(clippy::too_many_lines)] +pub(super) fn check<'tcx>( + cx: &LateContext<'tcx>, + expr: &hir::Expr<'_>, + method_span: Span, + name: &str, + args: &'tcx [hir::Expr<'_>], +) { + /// Checks for `unwrap_or(T::new())` or `unwrap_or(T::default())`. + fn check_unwrap_or_default( + cx: &LateContext<'_>, + name: &str, + fun: &hir::Expr<'_>, + self_expr: &hir::Expr<'_>, + arg: &hir::Expr<'_>, + or_has_args: bool, + span: Span, + ) -> bool { + if_chain! { + if !or_has_args; + if name == "unwrap_or"; + if let hir::ExprKind::Path(ref qpath) = fun.kind; + let path = last_path_segment(qpath).ident.name; + if matches!(path, kw::Default | sym::new); + let arg_ty = cx.typeck_results().expr_ty(arg); + if let Some(default_trait_id) = get_trait_def_id(cx, &paths::DEFAULT_TRAIT); + if implements_trait(cx, arg_ty, default_trait_id, &[]); + + then { + let mut applicability = Applicability::MachineApplicable; + span_lint_and_sugg( + cx, + OR_FUN_CALL, + span, + &format!("use of `{}` followed by a call to `{}`", name, path), + "try this", + format!( + "{}.unwrap_or_default()", + snippet_with_applicability(cx, self_expr.span, "..", &mut applicability) + ), + applicability, + ); + + true + } else { + false + } + } + } + + /// Checks for `*or(foo())`. + #[allow(clippy::too_many_arguments)] + fn check_general_case<'tcx>( + cx: &LateContext<'tcx>, + name: &str, + method_span: Span, + self_expr: &hir::Expr<'_>, + arg: &'tcx hir::Expr<'_>, + span: Span, + // None if lambda is required + fun_span: Option<Span>, + ) { + // (path, fn_has_argument, methods, suffix) + static KNOW_TYPES: [(&[&str], bool, &[&str], &str); 4] = [ + (&paths::BTREEMAP_ENTRY, false, &["or_insert"], "with"), + (&paths::HASHMAP_ENTRY, false, &["or_insert"], "with"), + (&paths::OPTION, false, &["map_or", "ok_or", "or", "unwrap_or"], "else"), + (&paths::RESULT, true, &["or", "unwrap_or"], "else"), + ]; + + if let hir::ExprKind::MethodCall(path, _, args, _) = &arg.kind { + if path.ident.as_str() == "len" { + let ty = cx.typeck_results().expr_ty(&args[0]).peel_refs(); + + match ty.kind() { + ty::Slice(_) | ty::Array(_, _) | ty::Str => return, + _ => (), + } + + if is_type_diagnostic_item(cx, ty, sym::vec_type) { + return; + } + } + } + + if_chain! { + if KNOW_TYPES.iter().any(|k| k.2.contains(&name)); + + if is_lazyness_candidate(cx, arg); + if !contains_return(arg); + + let self_ty = cx.typeck_results().expr_ty(self_expr); + + if let Some(&(_, fn_has_arguments, poss, suffix)) = + KNOW_TYPES.iter().find(|&&i| match_type(cx, self_ty, i.0)); + + if poss.contains(&name); + + then { + let macro_expanded_snipped; + let sugg: Cow<'_, str> = { + let (snippet_span, use_lambda) = match (fn_has_arguments, fun_span) { + (false, Some(fun_span)) => (fun_span, false), + _ => (arg.span, true), + }; + let snippet = { + let not_macro_argument_snippet = snippet_with_macro_callsite(cx, snippet_span, ".."); + if not_macro_argument_snippet == "vec![]" { + macro_expanded_snipped = snippet(cx, snippet_span, ".."); + match macro_expanded_snipped.strip_prefix("$crate::vec::") { + Some(stripped) => Cow::from(stripped), + None => macro_expanded_snipped + } + } + else { + not_macro_argument_snippet + } + }; + + if use_lambda { + let l_arg = if fn_has_arguments { "_" } else { "" }; + format!("|{}| {}", l_arg, snippet).into() + } else { + snippet + } + }; + let span_replace_word = method_span.with_hi(span.hi()); + span_lint_and_sugg( + cx, + OR_FUN_CALL, + span_replace_word, + &format!("use of `{}` followed by a function call", name), + "try this", + format!("{}_{}({})", name, suffix, sugg), + Applicability::HasPlaceholders, + ); + } + } + } + + if args.len() == 2 { + match args[1].kind { + hir::ExprKind::Call(fun, or_args) => { + let or_has_args = !or_args.is_empty(); + if !check_unwrap_or_default(cx, name, fun, &args[0], &args[1], or_has_args, expr.span) { + let fun_span = if or_has_args { None } else { Some(fun.span) }; + check_general_case(cx, name, method_span, &args[0], &args[1], expr.span, fun_span); + } + }, + hir::ExprKind::Index(..) | hir::ExprKind::MethodCall(..) => { + check_general_case(cx, name, method_span, &args[0], &args[1], expr.span, None); + }, + hir::ExprKind::Block(block, _) => { + if let BlockCheckMode::UnsafeBlock(UnsafeSource::UserProvided) = block.rules { + if let Some(block_expr) = block.expr { + if let hir::ExprKind::MethodCall(..) = block_expr.kind { + check_general_case(cx, name, method_span, &args[0], &args[1], expr.span, None); + } + } + } + }, + _ => (), + } + } +} diff --git a/src/tools/clippy/clippy_lints/src/methods/search_is_some.rs b/src/tools/clippy/clippy_lints/src/methods/search_is_some.rs new file mode 100644 index 0000000000000..ecec6fc3bb7fa --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/methods/search_is_some.rs @@ -0,0 +1,148 @@ +use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_sugg}; +use clippy_utils::source::{snippet, snippet_with_applicability}; +use clippy_utils::ty::is_type_diagnostic_item; +use clippy_utils::{is_trait_method, strip_pat_refs}; +use if_chain::if_chain; +use rustc_errors::Applicability; +use rustc_hir as hir; +use rustc_hir::PatKind; +use rustc_lint::LateContext; +use rustc_middle::ty; +use rustc_span::source_map::Span; +use rustc_span::symbol::sym; + +use super::SEARCH_IS_SOME; + +/// lint searching an Iterator followed by `is_some()` +/// or calling `find()` on a string followed by `is_some()` or `is_none()` +#[allow(clippy::too_many_arguments, clippy::too_many_lines)] +pub(super) fn check<'tcx>( + cx: &LateContext<'_>, + expr: &'tcx hir::Expr<'_>, + search_method: &str, + is_some: bool, + search_recv: &hir::Expr<'_>, + search_arg: &'tcx hir::Expr<'_>, + is_some_recv: &hir::Expr<'_>, + method_span: Span, +) { + let option_check_method = if is_some { "is_some" } else { "is_none" }; + // lint if caller of search is an Iterator + if is_trait_method(cx, is_some_recv, sym::Iterator) { + let msg = format!( + "called `{}()` after searching an `Iterator` with `{}`", + option_check_method, search_method + ); + let search_snippet = snippet(cx, search_arg.span, ".."); + if search_snippet.lines().count() <= 1 { + // suggest `any(|x| ..)` instead of `any(|&x| ..)` for `find(|&x| ..).is_some()` + // suggest `any(|..| *..)` instead of `any(|..| **..)` for `find(|..| **..).is_some()` + let any_search_snippet = if_chain! { + if search_method == "find"; + if let hir::ExprKind::Closure(_, _, body_id, ..) = search_arg.kind; + let closure_body = cx.tcx.hir().body(body_id); + if let Some(closure_arg) = closure_body.params.get(0); + then { + if let hir::PatKind::Ref(..) = closure_arg.pat.kind { + Some(search_snippet.replacen('&', "", 1)) + } else if let PatKind::Binding(_, _, ident, _) = strip_pat_refs(closure_arg.pat).kind { + let name = &*ident.name.as_str(); + Some(search_snippet.replace(&format!("*{}", name), name)) + } else { + None + } + } else { + None + } + }; + // add note if not multi-line + if is_some { + span_lint_and_sugg( + cx, + SEARCH_IS_SOME, + method_span.with_hi(expr.span.hi()), + &msg, + "use `any()` instead", + format!( + "any({})", + any_search_snippet.as_ref().map_or(&*search_snippet, String::as_str) + ), + Applicability::MachineApplicable, + ); + } else { + let iter = snippet(cx, search_recv.span, ".."); + span_lint_and_sugg( + cx, + SEARCH_IS_SOME, + expr.span, + &msg, + "use `!_.any()` instead", + format!( + "!{}.any({})", + iter, + any_search_snippet.as_ref().map_or(&*search_snippet, String::as_str) + ), + Applicability::MachineApplicable, + ); + } + } else { + let hint = format!( + "this is more succinctly expressed by calling `any()`{}", + if option_check_method == "is_none" { + " with negation" + } else { + "" + } + ); + span_lint_and_help(cx, SEARCH_IS_SOME, expr.span, &msg, None, &hint); + } + } + // lint if `find()` is called by `String` or `&str` + else if search_method == "find" { + let is_string_or_str_slice = |e| { + let self_ty = cx.typeck_results().expr_ty(e).peel_refs(); + if is_type_diagnostic_item(cx, self_ty, sym::string_type) { + true + } else { + *self_ty.kind() == ty::Str + } + }; + if_chain! { + if is_string_or_str_slice(search_recv); + if is_string_or_str_slice(search_arg); + then { + let msg = format!("called `{}()` after calling `find()` on a string", option_check_method); + match option_check_method { + "is_some" => { + let mut applicability = Applicability::MachineApplicable; + let find_arg = snippet_with_applicability(cx, search_arg.span, "..", &mut applicability); + span_lint_and_sugg( + cx, + SEARCH_IS_SOME, + method_span.with_hi(expr.span.hi()), + &msg, + "use `contains()` instead", + format!("contains({})", find_arg), + applicability, + ); + }, + "is_none" => { + let string = snippet(cx, search_recv.span, ".."); + let mut applicability = Applicability::MachineApplicable; + let find_arg = snippet_with_applicability(cx, search_arg.span, "..", &mut applicability); + span_lint_and_sugg( + cx, + SEARCH_IS_SOME, + expr.span, + &msg, + "use `!_.contains()` instead", + format!("!{}.contains({})", string, find_arg), + applicability, + ); + }, + _ => (), + } + } + } + } +} diff --git a/src/tools/clippy/clippy_lints/src/methods/single_char_add_str.rs b/src/tools/clippy/clippy_lints/src/methods/single_char_add_str.rs new file mode 100644 index 0000000000000..9a5fabcf7cd50 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/methods/single_char_add_str.rs @@ -0,0 +1,14 @@ +use crate::methods::{single_char_insert_string, single_char_push_string}; +use clippy_utils::{match_def_path, paths}; +use rustc_hir as hir; +use rustc_lint::LateContext; + +pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, args: &[hir::Expr<'_>]) { + if let Some(fn_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id) { + if match_def_path(cx, fn_def_id, &paths::PUSH_STR) { + single_char_push_string::check(cx, expr, args); + } else if match_def_path(cx, fn_def_id, &paths::INSERT_STR) { + single_char_insert_string::check(cx, expr, args); + } + } +} diff --git a/src/tools/clippy/clippy_lints/src/methods/single_char_insert_string.rs b/src/tools/clippy/clippy_lints/src/methods/single_char_insert_string.rs new file mode 100644 index 0000000000000..6cdc954c03be1 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/methods/single_char_insert_string.rs @@ -0,0 +1,28 @@ +use super::utils::get_hint_if_single_char_arg; +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::source::snippet_with_applicability; +use rustc_errors::Applicability; +use rustc_hir as hir; +use rustc_lint::LateContext; + +use super::SINGLE_CHAR_ADD_STR; + +/// lint for length-1 `str`s as argument for `insert_str` +pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, args: &[hir::Expr<'_>]) { + let mut applicability = Applicability::MachineApplicable; + if let Some(extension_string) = get_hint_if_single_char_arg(cx, &args[2], &mut applicability) { + let base_string_snippet = + snippet_with_applicability(cx, args[0].span.source_callsite(), "_", &mut applicability); + let pos_arg = snippet_with_applicability(cx, args[1].span, "..", &mut applicability); + let sugg = format!("{}.insert({}, {})", base_string_snippet, pos_arg, extension_string); + span_lint_and_sugg( + cx, + SINGLE_CHAR_ADD_STR, + expr.span, + "calling `insert_str()` using a single-character string literal", + "consider using `insert` with a character literal", + sugg, + applicability, + ); + } +} diff --git a/src/tools/clippy/clippy_lints/src/methods/single_char_pattern.rs b/src/tools/clippy/clippy_lints/src/methods/single_char_pattern.rs new file mode 100644 index 0000000000000..f4090c7c617d4 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/methods/single_char_pattern.rs @@ -0,0 +1,35 @@ +use super::utils::get_hint_if_single_char_arg; +use clippy_utils::diagnostics::span_lint_and_sugg; +use if_chain::if_chain; +use rustc_errors::Applicability; +use rustc_hir as hir; +use rustc_lint::LateContext; +use rustc_middle::ty; +use rustc_span::symbol::Symbol; + +use super::SINGLE_CHAR_PATTERN; + +/// lint for length-1 `str`s for methods in `PATTERN_METHODS` +pub(super) fn check(cx: &LateContext<'_>, _expr: &hir::Expr<'_>, method_name: Symbol, args: &[hir::Expr<'_>]) { + for &(method, pos) in &crate::methods::PATTERN_METHODS { + if_chain! { + if let ty::Ref(_, ty, _) = cx.typeck_results().expr_ty_adjusted(&args[0]).kind(); + if *ty.kind() == ty::Str; + if method_name.as_str() == method && args.len() > pos; + let arg = &args[pos]; + let mut applicability = Applicability::MachineApplicable; + if let Some(hint) = get_hint_if_single_char_arg(cx, arg, &mut applicability); + then { + span_lint_and_sugg( + cx, + SINGLE_CHAR_PATTERN, + arg.span, + "single-character string constant used as pattern", + "try using a `char` instead", + hint, + applicability, + ); + } + } + } +} diff --git a/src/tools/clippy/clippy_lints/src/methods/single_char_push_string.rs b/src/tools/clippy/clippy_lints/src/methods/single_char_push_string.rs new file mode 100644 index 0000000000000..0237d39cbdb4e --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/methods/single_char_push_string.rs @@ -0,0 +1,27 @@ +use super::utils::get_hint_if_single_char_arg; +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::source::snippet_with_applicability; +use rustc_errors::Applicability; +use rustc_hir as hir; +use rustc_lint::LateContext; + +use super::SINGLE_CHAR_ADD_STR; + +/// lint for length-1 `str`s as argument for `push_str` +pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, args: &[hir::Expr<'_>]) { + let mut applicability = Applicability::MachineApplicable; + if let Some(extension_string) = get_hint_if_single_char_arg(cx, &args[1], &mut applicability) { + let base_string_snippet = + snippet_with_applicability(cx, args[0].span.source_callsite(), "..", &mut applicability); + let sugg = format!("{}.push({})", base_string_snippet, extension_string); + span_lint_and_sugg( + cx, + SINGLE_CHAR_ADD_STR, + expr.span, + "calling `push_str()` using a single-character string literal", + "consider using `push` with a character literal", + sugg, + applicability, + ); + } +} diff --git a/src/tools/clippy/clippy_lints/src/methods/skip_while_next.rs b/src/tools/clippy/clippy_lints/src/methods/skip_while_next.rs new file mode 100644 index 0000000000000..9f0b6c34ea2ef --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/methods/skip_while_next.rs @@ -0,0 +1,22 @@ +use clippy_utils::diagnostics::span_lint_and_help; +use clippy_utils::is_trait_method; +use rustc_hir as hir; +use rustc_lint::LateContext; +use rustc_span::sym; + +use super::SKIP_WHILE_NEXT; + +/// lint use of `skip_while().next()` for `Iterators` +pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) { + // lint if caller of `.skip_while().next()` is an Iterator + if is_trait_method(cx, expr, sym::Iterator) { + span_lint_and_help( + cx, + SKIP_WHILE_NEXT, + expr.span, + "called `skip_while(<p>).next()` on an `Iterator`", + None, + "this is more succinctly expressed by calling `.find(!<p>)` instead", + ); + } +} diff --git a/src/tools/clippy/clippy_lints/src/methods/string_extend_chars.rs b/src/tools/clippy/clippy_lints/src/methods/string_extend_chars.rs new file mode 100644 index 0000000000000..6e7890a3080e6 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/methods/string_extend_chars.rs @@ -0,0 +1,45 @@ +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::method_chain_args; +use clippy_utils::source::snippet_with_applicability; +use clippy_utils::ty::is_type_diagnostic_item; +use rustc_errors::Applicability; +use rustc_hir as hir; +use rustc_lint::LateContext; +use rustc_middle::ty; +use rustc_span::symbol::sym; + +use super::STRING_EXTEND_CHARS; + +pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, recv: &hir::Expr<'_>, arg: &hir::Expr<'_>) { + let obj_ty = cx.typeck_results().expr_ty(recv).peel_refs(); + if !is_type_diagnostic_item(cx, obj_ty, sym::string_type) { + return; + } + if let Some(arglists) = method_chain_args(arg, &["chars"]) { + let target = &arglists[0][0]; + let self_ty = cx.typeck_results().expr_ty(target).peel_refs(); + let ref_str = if *self_ty.kind() == ty::Str { + "" + } else if is_type_diagnostic_item(cx, self_ty, sym::string_type) { + "&" + } else { + return; + }; + + let mut applicability = Applicability::MachineApplicable; + span_lint_and_sugg( + cx, + STRING_EXTEND_CHARS, + expr.span, + "calling `.extend(_.chars())`", + "try this", + format!( + "{}.push_str({}{})", + snippet_with_applicability(cx, recv.span, "..", &mut applicability), + ref_str, + snippet_with_applicability(cx, target.span, "..", &mut applicability) + ), + applicability, + ); + } +} diff --git a/src/tools/clippy/clippy_lints/src/methods/suspicious_map.rs b/src/tools/clippy/clippy_lints/src/methods/suspicious_map.rs new file mode 100644 index 0000000000000..0fd0668c73402 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/methods/suspicious_map.rs @@ -0,0 +1,35 @@ +use clippy_utils::diagnostics::span_lint_and_help; +use clippy_utils::usage::mutated_variables; +use clippy_utils::{expr_or_init, is_trait_method}; +use if_chain::if_chain; +use rustc_hir as hir; +use rustc_lint::LateContext; +use rustc_span::sym; + +use super::SUSPICIOUS_MAP; + +pub fn check<'tcx>(cx: &LateContext<'tcx>, expr: &hir::Expr<'_>, count_recv: &hir::Expr<'_>, map_arg: &hir::Expr<'_>) { + if_chain! { + if is_trait_method(cx, count_recv, sym::Iterator); + let closure = expr_or_init(cx, map_arg); + if let Some(body_id) = cx.tcx.hir().maybe_body_owned_by(closure.hir_id); + let closure_body = cx.tcx.hir().body(body_id); + if !cx.typeck_results().expr_ty(&closure_body.value).is_unit(); + then { + if let Some(map_mutated_vars) = mutated_variables(&closure_body.value, cx) { + // A variable is used mutably inside of the closure. Suppress the lint. + if !map_mutated_vars.is_empty() { + return; + } + } + span_lint_and_help( + cx, + SUSPICIOUS_MAP, + expr.span, + "this call to `map()` won't have an effect on the call to `count()`", + None, + "make sure you did not confuse `map` with `filter` or `for_each`", + ); + } + } +} diff --git a/src/tools/clippy/clippy_lints/src/methods/uninit_assumed_init.rs b/src/tools/clippy/clippy_lints/src/methods/uninit_assumed_init.rs new file mode 100644 index 0000000000000..0ae65c0c01dba --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/methods/uninit_assumed_init.rs @@ -0,0 +1,36 @@ +use clippy_utils::diagnostics::span_lint; +use clippy_utils::{match_def_path, match_qpath, paths}; +use if_chain::if_chain; +use rustc_hir as hir; +use rustc_lint::LateContext; +use rustc_middle::ty::{self, Ty}; + +use super::UNINIT_ASSUMED_INIT; + +/// lint for `MaybeUninit::uninit().assume_init()` (we already have the latter) +pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, recv: &hir::Expr<'_>) { + if_chain! { + if let hir::ExprKind::Call(callee, args) = recv.kind; + if args.is_empty(); + if let hir::ExprKind::Path(ref path) = callee.kind; + if match_qpath(path, &paths::MEM_MAYBEUNINIT_UNINIT); + if !is_maybe_uninit_ty_valid(cx, cx.typeck_results().expr_ty_adjusted(expr)); + then { + span_lint( + cx, + UNINIT_ASSUMED_INIT, + expr.span, + "this call for this type may be undefined behavior" + ); + } + } +} + +fn is_maybe_uninit_ty_valid(cx: &LateContext<'_>, ty: Ty<'_>) -> bool { + match ty.kind() { + ty::Array(component, _) => is_maybe_uninit_ty_valid(cx, component), + ty::Tuple(types) => types.types().all(|ty| is_maybe_uninit_ty_valid(cx, ty)), + ty::Adt(adt, _) => match_def_path(cx, adt.did, &paths::MEM_MAYBEUNINIT), + _ => false, + } +} diff --git a/src/tools/clippy/clippy_lints/src/methods/unnecessary_filter_map.rs b/src/tools/clippy/clippy_lints/src/methods/unnecessary_filter_map.rs index 5691fcb88e95c..0f28bfdf09e89 100644 --- a/src/tools/clippy/clippy_lints/src/methods/unnecessary_filter_map.rs +++ b/src/tools/clippy/clippy_lints/src/methods/unnecessary_filter_map.rs @@ -1,28 +1,28 @@ -use crate::utils::usage::mutated_variables; -use crate::utils::{match_qpath, match_trait_method, path_to_local_id, paths, span_lint}; +use clippy_utils::diagnostics::span_lint; +use clippy_utils::usage::mutated_variables; +use clippy_utils::{is_trait_method, match_qpath, path_to_local_id, paths}; use rustc_hir as hir; use rustc_hir::intravisit::{walk_expr, NestedVisitorMap, Visitor}; use rustc_lint::LateContext; use rustc_middle::hir::map::Map; - -use if_chain::if_chain; +use rustc_span::sym; use super::UNNECESSARY_FILTER_MAP; -pub(super) fn lint(cx: &LateContext<'_>, expr: &hir::Expr<'_>, args: &[hir::Expr<'_>]) { - if !match_trait_method(cx, expr, &paths::ITERATOR) { +pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, arg: &hir::Expr<'_>) { + if !is_trait_method(cx, expr, sym::Iterator) { return; } - if let hir::ExprKind::Closure(_, _, body_id, ..) = args[1].kind { + if let hir::ExprKind::Closure(_, _, body_id, ..) = arg.kind { let body = cx.tcx.hir().body(body_id); let arg_id = body.params[0].pat.hir_id; let mutates_arg = mutated_variables(&body.value, cx).map_or(true, |used_mutably| used_mutably.contains(&arg_id)); - let (mut found_mapping, mut found_filtering) = check_expression(&cx, arg_id, &body.value); + let (mut found_mapping, mut found_filtering) = check_expression(cx, arg_id, &body.value); - let mut return_visitor = ReturnVisitor::new(&cx, arg_id); + let mut return_visitor = ReturnVisitor::new(cx, arg_id); return_visitor.visit_expr(&body.value); found_mapping |= return_visitor.found_mapping; found_filtering |= return_visitor.found_filtering; @@ -52,38 +52,35 @@ pub(super) fn lint(cx: &LateContext<'_>, expr: &hir::Expr<'_>, args: &[hir::Expr // returns (found_mapping, found_filtering) fn check_expression<'tcx>(cx: &LateContext<'tcx>, arg_id: hir::HirId, expr: &'tcx hir::Expr<'_>) -> (bool, bool) { match &expr.kind { - hir::ExprKind::Call(ref func, ref args) => { - if_chain! { - if let hir::ExprKind::Path(ref path) = func.kind; - then { - if match_qpath(path, &paths::OPTION_SOME) { - if path_to_local_id(&args[0], arg_id) { - return (false, false) - } - return (true, false); + hir::ExprKind::Call(func, args) => { + if let hir::ExprKind::Path(ref path) = func.kind { + if match_qpath(path, &paths::OPTION_SOME) { + if path_to_local_id(&args[0], arg_id) { + return (false, false); } - // We don't know. It might do anything. - return (true, true); + return (true, false); } + // We don't know. It might do anything. + return (true, true); } (true, true) }, - hir::ExprKind::Block(ref block, _) => block + hir::ExprKind::Block(block, _) => block .expr .as_ref() - .map_or((false, false), |expr| check_expression(cx, arg_id, &expr)), + .map_or((false, false), |expr| check_expression(cx, arg_id, expr)), hir::ExprKind::Match(_, arms, _) => { let mut found_mapping = false; let mut found_filtering = false; for arm in *arms { - let (m, f) = check_expression(cx, arg_id, &arm.body); + let (m, f) = check_expression(cx, arg_id, arm.body); found_mapping |= m; found_filtering |= f; } (found_mapping, found_filtering) }, // There must be an else_arm or there will be a type error - hir::ExprKind::If(_, ref if_arm, Some(ref else_arm)) => { + hir::ExprKind::If(_, if_arm, Some(else_arm)) => { let if_check = check_expression(cx, arg_id, if_arm); let else_check = check_expression(cx, arg_id, else_arm); (if_check.0 | else_check.0, if_check.1 | else_check.1) diff --git a/src/tools/clippy/clippy_lints/src/methods/unnecessary_fold.rs b/src/tools/clippy/clippy_lints/src/methods/unnecessary_fold.rs new file mode 100644 index 0000000000000..75517c48a21c9 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/methods/unnecessary_fold.rs @@ -0,0 +1,95 @@ +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::source::snippet_with_applicability; +use clippy_utils::{is_trait_method, path_to_local_id, remove_blocks, strip_pat_refs}; +use if_chain::if_chain; +use rustc_ast::ast; +use rustc_errors::Applicability; +use rustc_hir as hir; +use rustc_hir::PatKind; +use rustc_lint::LateContext; +use rustc_span::{source_map::Span, sym}; + +use super::UNNECESSARY_FOLD; + +pub(super) fn check( + cx: &LateContext<'_>, + expr: &hir::Expr<'_>, + init: &hir::Expr<'_>, + acc: &hir::Expr<'_>, + fold_span: Span, +) { + fn check_fold_with_op( + cx: &LateContext<'_>, + expr: &hir::Expr<'_>, + acc: &hir::Expr<'_>, + fold_span: Span, + op: hir::BinOpKind, + replacement_method_name: &str, + replacement_has_args: bool, + ) { + if_chain! { + // Extract the body of the closure passed to fold + if let hir::ExprKind::Closure(_, _, body_id, _, _) = acc.kind; + let closure_body = cx.tcx.hir().body(body_id); + let closure_expr = remove_blocks(&closure_body.value); + + // Check if the closure body is of the form `acc <op> some_expr(x)` + if let hir::ExprKind::Binary(ref bin_op, left_expr, right_expr) = closure_expr.kind; + if bin_op.node == op; + + // Extract the names of the two arguments to the closure + if let [param_a, param_b] = closure_body.params; + if let PatKind::Binding(_, first_arg_id, ..) = strip_pat_refs(param_a.pat).kind; + if let PatKind::Binding(_, second_arg_id, second_arg_ident, _) = strip_pat_refs(param_b.pat).kind; + + if path_to_local_id(left_expr, first_arg_id); + if replacement_has_args || path_to_local_id(right_expr, second_arg_id); + + then { + let mut applicability = Applicability::MachineApplicable; + let sugg = if replacement_has_args { + format!( + "{replacement}(|{s}| {r})", + replacement = replacement_method_name, + s = second_arg_ident, + r = snippet_with_applicability(cx, right_expr.span, "EXPR", &mut applicability), + ) + } else { + format!( + "{replacement}()", + replacement = replacement_method_name, + ) + }; + + span_lint_and_sugg( + cx, + UNNECESSARY_FOLD, + fold_span.with_hi(expr.span.hi()), + // TODO #2371 don't suggest e.g., .any(|x| f(x)) if we can suggest .any(f) + "this `.fold` can be written more succinctly using another method", + "try", + sugg, + applicability, + ); + } + } + } + + // Check that this is a call to Iterator::fold rather than just some function called fold + if !is_trait_method(cx, expr, sym::Iterator) { + return; + } + + // Check if the first argument to .fold is a suitable literal + if let hir::ExprKind::Lit(ref lit) = init.kind { + match lit.node { + ast::LitKind::Bool(false) => check_fold_with_op(cx, expr, acc, fold_span, hir::BinOpKind::Or, "any", true), + ast::LitKind::Bool(true) => check_fold_with_op(cx, expr, acc, fold_span, hir::BinOpKind::And, "all", true), + ast::LitKind::Int(0, _) => check_fold_with_op(cx, expr, acc, fold_span, hir::BinOpKind::Add, "sum", false), + ast::LitKind::Int(1, _) => { + check_fold_with_op(cx, expr, acc, fold_span, hir::BinOpKind::Mul, "product", false) + }, + _ => (), + } + } +} diff --git a/src/tools/clippy/clippy_lints/src/methods/unnecessary_lazy_eval.rs b/src/tools/clippy/clippy_lints/src/methods/unnecessary_lazy_eval.rs index a867bdb326d72..b7380883a5eec 100644 --- a/src/tools/clippy/clippy_lints/src/methods/unnecessary_lazy_eval.rs +++ b/src/tools/clippy/clippy_lints/src/methods/unnecessary_lazy_eval.rs @@ -1,5 +1,7 @@ -use crate::utils::{eager_or_lazy, usage}; -use crate::utils::{is_type_diagnostic_item, snippet, span_lint_and_sugg}; +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::source::snippet; +use clippy_utils::ty::is_type_diagnostic_item; +use clippy_utils::{eager_or_lazy, usage}; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::LateContext; @@ -9,17 +11,18 @@ use super::UNNECESSARY_LAZY_EVALUATIONS; /// lint use of `<fn>_else(simple closure)` for `Option`s and `Result`s that can be /// replaced with `<fn>(return value of simple closure)` -pub(super) fn lint<'tcx>( +pub(super) fn check<'tcx>( cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, - args: &'tcx [hir::Expr<'_>], + recv: &'tcx hir::Expr<'_>, + arg: &'tcx hir::Expr<'_>, simplify_using: &str, ) { - let is_option = is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(&args[0]), sym::option_type); - let is_result = is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(&args[0]), sym::result_type); + let is_option = is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(recv), sym::option_type); + let is_result = is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(recv), sym::result_type); if is_option || is_result { - if let hir::ExprKind::Closure(_, _, eid, _, _) = args[1].kind { + if let hir::ExprKind::Closure(_, _, eid, _, _) = arg.kind { let body = cx.tcx.hir().body(eid); let body_expr = &body.value; @@ -50,10 +53,10 @@ pub(super) fn lint<'tcx>( UNNECESSARY_LAZY_EVALUATIONS, expr.span, msg, - &format!("Use `{}` instead", simplify_using), + &format!("use `{}` instead", simplify_using), format!( "{0}.{1}({2})", - snippet(cx, args[0].span, ".."), + snippet(cx, recv.span, ".."), simplify_using, snippet(cx, body_expr.span, ".."), ), diff --git a/src/tools/clippy/clippy_lints/src/methods/unwrap_used.rs b/src/tools/clippy/clippy_lints/src/methods/unwrap_used.rs new file mode 100644 index 0000000000000..7fd1948594d70 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/methods/unwrap_used.rs @@ -0,0 +1,35 @@ +use clippy_utils::diagnostics::span_lint_and_help; +use clippy_utils::ty::is_type_diagnostic_item; +use rustc_hir as hir; +use rustc_lint::LateContext; +use rustc_span::sym; + +use super::UNWRAP_USED; + +/// lint use of `unwrap()` for `Option`s and `Result`s +pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, recv: &hir::Expr<'_>) { + let obj_ty = cx.typeck_results().expr_ty(recv).peel_refs(); + + let mess = if is_type_diagnostic_item(cx, obj_ty, sym::option_type) { + Some((UNWRAP_USED, "an Option", "None")) + } else if is_type_diagnostic_item(cx, obj_ty, sym::result_type) { + Some((UNWRAP_USED, "a Result", "Err")) + } else { + None + }; + + if let Some((lint, kind, none_value)) = mess { + span_lint_and_help( + cx, + lint, + expr.span, + &format!("used `unwrap()` on `{}` value", kind,), + None, + &format!( + "if you don't want to handle the `{}` case gracefully, consider \ + using `expect()` to provide a better panic message", + none_value, + ), + ); + } +} diff --git a/src/tools/clippy/clippy_lints/src/methods/useless_asref.rs b/src/tools/clippy/clippy_lints/src/methods/useless_asref.rs new file mode 100644 index 0000000000000..e0b1de68b37d3 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/methods/useless_asref.rs @@ -0,0 +1,45 @@ +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::source::snippet_with_applicability; +use clippy_utils::ty::walk_ptrs_ty_depth; +use clippy_utils::{get_parent_expr, match_trait_method, paths}; +use if_chain::if_chain; +use rustc_errors::Applicability; +use rustc_hir as hir; +use rustc_lint::LateContext; + +use super::USELESS_ASREF; + +/// Checks for the `USELESS_ASREF` lint. +pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, call_name: &str, recvr: &hir::Expr<'_>) { + // when we get here, we've already checked that the call name is "as_ref" or "as_mut" + // check if the call is to the actual `AsRef` or `AsMut` trait + if match_trait_method(cx, expr, &paths::ASREF_TRAIT) || match_trait_method(cx, expr, &paths::ASMUT_TRAIT) { + // check if the type after `as_ref` or `as_mut` is the same as before + let rcv_ty = cx.typeck_results().expr_ty(recvr); + let res_ty = cx.typeck_results().expr_ty(expr); + let (base_res_ty, res_depth) = walk_ptrs_ty_depth(res_ty); + let (base_rcv_ty, rcv_depth) = walk_ptrs_ty_depth(rcv_ty); + if base_rcv_ty == base_res_ty && rcv_depth >= res_depth { + // allow the `as_ref` or `as_mut` if it is followed by another method call + if_chain! { + if let Some(parent) = get_parent_expr(cx, expr); + if let hir::ExprKind::MethodCall(_, ref span, _, _) = parent.kind; + if span != &expr.span; + then { + return; + } + } + + let mut applicability = Applicability::MachineApplicable; + span_lint_and_sugg( + cx, + USELESS_ASREF, + expr.span, + &format!("this call to `{}` does nothing", call_name), + "try this", + snippet_with_applicability(cx, recvr.span, "..", &mut applicability).to_string(), + applicability, + ); + } + } +} diff --git a/src/tools/clippy/clippy_lints/src/methods/utils.rs b/src/tools/clippy/clippy_lints/src/methods/utils.rs new file mode 100644 index 0000000000000..f6bf37e08b966 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/methods/utils.rs @@ -0,0 +1,77 @@ +use clippy_utils::source::snippet_with_applicability; +use clippy_utils::ty::is_type_diagnostic_item; +use if_chain::if_chain; +use rustc_ast::ast; +use rustc_errors::Applicability; +use rustc_hir as hir; +use rustc_lint::LateContext; +use rustc_middle::ty::{self, Ty}; +use rustc_span::symbol::sym; + +pub(super) fn derefs_to_slice<'tcx>( + cx: &LateContext<'tcx>, + expr: &'tcx hir::Expr<'tcx>, + ty: Ty<'tcx>, +) -> Option<&'tcx hir::Expr<'tcx>> { + fn may_slice<'a>(cx: &LateContext<'a>, ty: Ty<'a>) -> bool { + match ty.kind() { + ty::Slice(_) => true, + ty::Adt(def, _) if def.is_box() => may_slice(cx, ty.boxed_ty()), + ty::Adt(..) => is_type_diagnostic_item(cx, ty, sym::vec_type), + ty::Array(_, size) => size + .try_eval_usize(cx.tcx, cx.param_env) + .map_or(false, |size| size < 32), + ty::Ref(_, inner, _) => may_slice(cx, inner), + _ => false, + } + } + + if let hir::ExprKind::MethodCall(path, _, args, _) = expr.kind { + if path.ident.name == sym::iter && may_slice(cx, cx.typeck_results().expr_ty(&args[0])) { + Some(&args[0]) + } else { + None + } + } else { + match ty.kind() { + ty::Slice(_) => Some(expr), + ty::Adt(def, _) if def.is_box() && may_slice(cx, ty.boxed_ty()) => Some(expr), + ty::Ref(_, inner, _) => { + if may_slice(cx, inner) { + Some(expr) + } else { + None + } + }, + _ => None, + } + } +} + +pub(super) fn get_hint_if_single_char_arg( + cx: &LateContext<'_>, + arg: &hir::Expr<'_>, + applicability: &mut Applicability, +) -> Option<String> { + if_chain! { + if let hir::ExprKind::Lit(lit) = &arg.kind; + if let ast::LitKind::Str(r, style) = lit.node; + let string = r.as_str(); + if string.chars().count() == 1; + then { + let snip = snippet_with_applicability(cx, arg.span, &string, applicability); + let ch = if let ast::StrStyle::Raw(nhash) = style { + let nhash = nhash as usize; + // for raw string: r##"a"## + &snip[(nhash + 2)..(snip.len() - 1 - nhash)] + } else { + // for regular string: "a" + &snip[1..(snip.len() - 1)] + }; + let hint = format!("'{}'", if ch == "'" { "\\'" } else { ch }); + Some(hint) + } else { + None + } + } +} diff --git a/src/tools/clippy/clippy_lints/src/methods/wrong_self_convention.rs b/src/tools/clippy/clippy_lints/src/methods/wrong_self_convention.rs new file mode 100644 index 0000000000000..1e0de249a91f1 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/methods/wrong_self_convention.rs @@ -0,0 +1,153 @@ +use crate::methods::SelfKind; +use clippy_utils::diagnostics::span_lint_and_help; +use clippy_utils::ty::is_copy; +use rustc_lint::LateContext; +use rustc_middle::ty::TyS; +use rustc_span::source_map::Span; +use std::fmt; + +use super::WRONG_PUB_SELF_CONVENTION; +use super::WRONG_SELF_CONVENTION; + +#[rustfmt::skip] +const CONVENTIONS: [(&[Convention], &[SelfKind]); 9] = [ + (&[Convention::Eq("new")], &[SelfKind::No]), + (&[Convention::StartsWith("as_")], &[SelfKind::Ref, SelfKind::RefMut]), + (&[Convention::StartsWith("from_")], &[SelfKind::No]), + (&[Convention::StartsWith("into_")], &[SelfKind::Value]), + (&[Convention::StartsWith("is_")], &[SelfKind::Ref, SelfKind::No]), + (&[Convention::Eq("to_mut")], &[SelfKind::RefMut]), + (&[Convention::StartsWith("to_"), Convention::EndsWith("_mut")], &[SelfKind::RefMut]), + + // Conversion using `to_` can use borrowed (non-Copy types) or owned (Copy types). + // Source: https://rust-lang.github.io/api-guidelines/naming.html#ad-hoc-conversions-follow-as_-to_-into_-conventions-c-conv + (&[Convention::StartsWith("to_"), Convention::NotEndsWith("_mut"), Convention::IsSelfTypeCopy(false), + Convention::IsTraitItem(false)], &[SelfKind::Ref]), + (&[Convention::StartsWith("to_"), Convention::NotEndsWith("_mut"), Convention::IsSelfTypeCopy(true), + Convention::IsTraitItem(false), Convention::ImplementsTrait(false)], &[SelfKind::Value]), +]; + +enum Convention { + Eq(&'static str), + StartsWith(&'static str), + EndsWith(&'static str), + NotEndsWith(&'static str), + IsSelfTypeCopy(bool), + ImplementsTrait(bool), + IsTraitItem(bool), +} + +impl Convention { + #[must_use] + fn check<'tcx>( + &self, + cx: &LateContext<'tcx>, + self_ty: &'tcx TyS<'tcx>, + other: &str, + implements_trait: bool, + is_trait_item: bool, + ) -> bool { + match *self { + Self::Eq(this) => this == other, + Self::StartsWith(this) => other.starts_with(this) && this != other, + Self::EndsWith(this) => other.ends_with(this) && this != other, + Self::NotEndsWith(this) => !Self::EndsWith(this).check(cx, self_ty, other, implements_trait, is_trait_item), + Self::IsSelfTypeCopy(is_true) => is_true == is_copy(cx, self_ty), + Self::ImplementsTrait(is_true) => is_true == implements_trait, + Self::IsTraitItem(is_true) => is_true == is_trait_item, + } + } +} + +impl fmt::Display for Convention { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { + match *self { + Self::Eq(this) => format!("`{}`", this).fmt(f), + Self::StartsWith(this) => format!("`{}*`", this).fmt(f), + Self::EndsWith(this) => format!("`*{}`", this).fmt(f), + Self::NotEndsWith(this) => format!("`~{}`", this).fmt(f), + Self::IsSelfTypeCopy(is_true) => { + format!("`self` type is{} `Copy`", if is_true { "" } else { " not" }).fmt(f) + }, + Self::ImplementsTrait(is_true) => { + let (negation, s_suffix) = if is_true { ("", "s") } else { (" does not", "") }; + format!("method{} implement{} a trait", negation, s_suffix).fmt(f) + }, + Self::IsTraitItem(is_true) => { + let suffix = if is_true { " is" } else { " is not" }; + format!("method{} a trait item", suffix).fmt(f) + }, + } + } +} + +#[allow(clippy::too_many_arguments)] +pub(super) fn check<'tcx>( + cx: &LateContext<'tcx>, + item_name: &str, + is_pub: bool, + self_ty: &'tcx TyS<'tcx>, + first_arg_ty: &'tcx TyS<'tcx>, + first_arg_span: Span, + implements_trait: bool, + is_trait_item: bool, +) { + let lint = if is_pub { + WRONG_PUB_SELF_CONVENTION + } else { + WRONG_SELF_CONVENTION + }; + if let Some((conventions, self_kinds)) = &CONVENTIONS.iter().find(|(convs, _)| { + convs + .iter() + .all(|conv| conv.check(cx, self_ty, item_name, implements_trait, is_trait_item)) + }) { + if !self_kinds.iter().any(|k| k.matches(cx, self_ty, first_arg_ty)) { + let suggestion = { + if conventions.len() > 1 { + // Don't mention `NotEndsWith` when there is also `StartsWith` convention present + let cut_ends_with_conv = conventions.iter().any(|conv| matches!(conv, Convention::StartsWith(_))) + && conventions + .iter() + .any(|conv| matches!(conv, Convention::NotEndsWith(_))); + + let s = conventions + .iter() + .filter_map(|conv| { + if (cut_ends_with_conv && matches!(conv, Convention::NotEndsWith(_))) + || matches!(conv, Convention::ImplementsTrait(_)) + || matches!(conv, Convention::IsTraitItem(_)) + { + None + } else { + Some(conv.to_string()) + } + }) + .collect::<Vec<_>>() + .join(" and "); + + format!("methods with the following characteristics: ({})", &s) + } else { + format!("methods called {}", &conventions[0]) + } + }; + + span_lint_and_help( + cx, + lint, + first_arg_span, + &format!( + "{} usually take {}", + suggestion, + &self_kinds + .iter() + .map(|k| k.description()) + .collect::<Vec<_>>() + .join(" or ") + ), + None, + "consider choosing a less ambiguous name", + ); + } + } +} diff --git a/src/tools/clippy/clippy_lints/src/methods/zst_offset.rs b/src/tools/clippy/clippy_lints/src/methods/zst_offset.rs new file mode 100644 index 0000000000000..866cf616679c2 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/methods/zst_offset.rs @@ -0,0 +1,18 @@ +use clippy_utils::diagnostics::span_lint; +use if_chain::if_chain; +use rustc_hir as hir; +use rustc_lint::LateContext; +use rustc_middle::ty; + +use super::ZST_OFFSET; + +pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, recv: &hir::Expr<'_>) { + if_chain! { + if let ty::RawPtr(ty::TypeAndMut { ty, .. }) = cx.typeck_results().expr_ty(recv).kind(); + if let Ok(layout) = cx.tcx.layout_of(cx.param_env.and(ty)); + if layout.is_zst(); + then { + span_lint(cx, ZST_OFFSET, expr.span, "offset calculation on zero-sized value"); + } + } +} diff --git a/src/tools/clippy/clippy_lints/src/minmax.rs b/src/tools/clippy/clippy_lints/src/minmax.rs index 8d0c3b8e0fe89..45948f4d926bc 100644 --- a/src/tools/clippy/clippy_lints/src/minmax.rs +++ b/src/tools/clippy/clippy_lints/src/minmax.rs @@ -1,5 +1,6 @@ use crate::consts::{constant_simple, Constant}; -use crate::utils::{match_def_path, match_trait_method, paths, span_lint}; +use clippy_utils::diagnostics::span_lint; +use clippy_utils::{match_def_path, match_trait_method, paths}; use if_chain::if_chain; use rustc_hir::{Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; @@ -66,7 +67,7 @@ enum MinMax { fn min_max<'a>(cx: &LateContext<'_>, expr: &'a Expr<'a>) -> Option<(MinMax, Constant, &'a Expr<'a>)> { match expr.kind { - ExprKind::Call(ref path, ref args) => { + ExprKind::Call(path, args) => { if let ExprKind::Path(ref qpath) = path.kind { cx.typeck_results() .qpath_res(qpath, path.hir_id) @@ -84,7 +85,7 @@ fn min_max<'a>(cx: &LateContext<'_>, expr: &'a Expr<'a>) -> Option<(MinMax, Cons None } }, - ExprKind::MethodCall(ref path, _, ref args, _) => { + ExprKind::MethodCall(path, _, args, _) => { if_chain! { if let [obj, _] = args; if cx.typeck_results().expr_ty(obj).is_floating_point() || match_trait_method(cx, expr, &paths::ORD); diff --git a/src/tools/clippy/clippy_lints/src/misc.rs b/src/tools/clippy/clippy_lints/src/misc.rs index 2ef5c6aa2a4e2..afced5a5ce58a 100644 --- a/src/tools/clippy/clippy_lints/src/misc.rs +++ b/src/tools/clippy/clippy_lints/src/misc.rs @@ -1,3 +1,6 @@ +use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg, span_lint_and_then, span_lint_hir_and_then}; +use clippy_utils::source::{snippet, snippet_opt}; +use clippy_utils::ty::implements_trait; use if_chain::if_chain; use rustc_ast::ast::LitKind; use rustc_errors::Applicability; @@ -12,13 +15,13 @@ use rustc_middle::ty::{self, Ty}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::hygiene::DesugaringKind; use rustc_span::source_map::{ExpnKind, Span}; +use rustc_span::symbol::sym; use crate::consts::{constant, Constant}; -use crate::utils::sugg::Sugg; -use crate::utils::{ - get_item_name, get_parent_expr, higher, implements_trait, in_constant, is_integer_const, iter_input_pats, - last_path_segment, match_qpath, match_trait_method, paths, snippet, snippet_opt, span_lint, span_lint_and_sugg, - span_lint_and_then, span_lint_hir_and_then, unsext, SpanlessEq, +use clippy_utils::sugg::Sugg; +use clippy_utils::{ + get_item_name, get_parent_expr, higher, in_constant, is_diagnostic_assoc_item, is_integer_const, iter_input_pats, + last_path_segment, match_qpath, unsext, SpanlessEq, }; declare_clippy_lint! { @@ -278,7 +281,7 @@ impl<'tcx> LateLintPass<'tcx> for MiscLints { span: Span, _: HirId, ) { - if let FnKind::Closure(_) = k { + if let FnKind::Closure = k { // Does not apply to closures return; } @@ -292,7 +295,7 @@ impl<'tcx> LateLintPass<'tcx> for MiscLints { TOPLEVEL_REF_ARG, arg.pat.span, "`ref` directly on a function argument is ignored. \ - Consider using a reference type instead.", + Consider using a reference type instead", ); } } @@ -301,55 +304,54 @@ impl<'tcx> LateLintPass<'tcx> for MiscLints { fn check_stmt(&mut self, cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'_>) { if_chain! { if !in_external_macro(cx.tcx.sess, stmt.span); - if let StmtKind::Local(ref local) = stmt.kind; + if let StmtKind::Local(local) = stmt.kind; if let PatKind::Binding(an, .., name, None) = local.pat.kind; - if let Some(ref init) = local.init; + if let Some(init) = local.init; if !higher::is_from_for_desugar(local); + if an == BindingAnnotation::Ref || an == BindingAnnotation::RefMut; then { - if an == BindingAnnotation::Ref || an == BindingAnnotation::RefMut { - // use the macro callsite when the init span (but not the whole local span) - // comes from an expansion like `vec![1, 2, 3]` in `let ref _ = vec![1, 2, 3];` - let sugg_init = if init.span.from_expansion() && !local.span.from_expansion() { - Sugg::hir_with_macro_callsite(cx, init, "..") - } else { - Sugg::hir(cx, init, "..") - }; - let (mutopt, initref) = if an == BindingAnnotation::RefMut { - ("mut ", sugg_init.mut_addr()) - } else { - ("", sugg_init.addr()) - }; - let tyopt = if let Some(ref ty) = local.ty { - format!(": &{mutopt}{ty}", mutopt=mutopt, ty=snippet(cx, ty.span, "..")) - } else { - String::new() - }; - span_lint_hir_and_then( - cx, - TOPLEVEL_REF_ARG, - init.hir_id, - local.pat.span, - "`ref` on an entire `let` pattern is discouraged, take a reference with `&` instead", - |diag| { - diag.span_suggestion( - stmt.span, - "try", - format!( - "let {name}{tyopt} = {initref};", - name=snippet(cx, name.span, ".."), - tyopt=tyopt, - initref=initref, - ), - Applicability::MachineApplicable, - ); - } - ); - } + // use the macro callsite when the init span (but not the whole local span) + // comes from an expansion like `vec![1, 2, 3]` in `let ref _ = vec![1, 2, 3];` + let sugg_init = if init.span.from_expansion() && !local.span.from_expansion() { + Sugg::hir_with_macro_callsite(cx, init, "..") + } else { + Sugg::hir(cx, init, "..") + }; + let (mutopt, initref) = if an == BindingAnnotation::RefMut { + ("mut ", sugg_init.mut_addr()) + } else { + ("", sugg_init.addr()) + }; + let tyopt = if let Some(ty) = local.ty { + format!(": &{mutopt}{ty}", mutopt=mutopt, ty=snippet(cx, ty.span, "..")) + } else { + String::new() + }; + span_lint_hir_and_then( + cx, + TOPLEVEL_REF_ARG, + init.hir_id, + local.pat.span, + "`ref` on an entire `let` pattern is discouraged, take a reference with `&` instead", + |diag| { + diag.span_suggestion( + stmt.span, + "try", + format!( + "let {name}{tyopt} = {initref};", + name=snippet(cx, name.span, ".."), + tyopt=tyopt, + initref=initref, + ), + Applicability::MachineApplicable, + ); + } + ); } }; if_chain! { - if let StmtKind::Semi(ref expr) = stmt.kind; - if let ExprKind::Binary(ref binop, ref a, ref b) = expr.kind; + if let StmtKind::Semi(expr) = stmt.kind; + if let ExprKind::Binary(ref binop, a, b) = expr.kind; if binop.node == BinOpKind::And || binop.node == BinOpKind::Or; if let Some(sugg) = Sugg::hir_opt(cx, a); then { @@ -376,11 +378,11 @@ impl<'tcx> LateLintPass<'tcx> for MiscLints { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { match expr.kind { - ExprKind::Cast(ref e, ref ty) => { + ExprKind::Cast(e, ty) => { check_cast(cx, expr.span, e, ty); return; }, - ExprKind::Binary(ref cmp, ref left, ref right) => { + ExprKind::Binary(ref cmp, left, right) => { check_binary(cx, expr, cmp, left, right); return; }, @@ -422,7 +424,7 @@ impl<'tcx> LateLintPass<'tcx> for MiscLints { expr.span, &format!( "used binding `{}` which is prefixed with an underscore. A leading \ - underscore signals that a binding will not be used.", + underscore signals that a binding will not be used", binding ), ); @@ -459,21 +461,18 @@ fn check_nan(cx: &LateContext<'_>, expr: &Expr<'_>, cmp_expr: &Expr<'_>) { if_chain! { if !in_constant(cx, cmp_expr.hir_id); if let Some((value, _)) = constant(cx, cx.typeck_results(), expr); + if match value { + Constant::F32(num) => num.is_nan(), + Constant::F64(num) => num.is_nan(), + _ => false, + }; then { - let needs_lint = match value { - Constant::F32(num) => num.is_nan(), - Constant::F64(num) => num.is_nan(), - _ => false, - }; - - if needs_lint { - span_lint( - cx, - CMP_NAN, - cmp_expr.span, - "doomed comparison with `NAN`, use `{f32,f64}::is_nan()` instead", - ); - } + span_lint( + cx, + CMP_NAN, + cmp_expr.span, + "doomed comparison with `NAN`, use `{f32,f64}::is_nan()` instead", + ); } } } @@ -502,12 +501,12 @@ fn is_allowed<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> bool { // Return true if `expr` is the result of `signum()` invoked on a float value. fn is_signum(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { // The negation of a signum is still a signum - if let ExprKind::Unary(UnOp::Neg, ref child_expr) = expr.kind { - return is_signum(cx, &child_expr); + if let ExprKind::Unary(UnOp::Neg, child_expr) = expr.kind { + return is_signum(cx, child_expr); } if_chain! { - if let ExprKind::MethodCall(ref method_name, _, ref expressions, _) = expr.kind; + if let ExprKind::MethodCall(method_name, _, expressions, _) = expr.kind; if sym!(signum) == method_name.ident.name; // Check that the receiver of the signum() is a float (expressions[0] is the receiver of // the method call) @@ -553,14 +552,19 @@ fn check_to_owned(cx: &LateContext<'_>, expr: &Expr<'_>, other: &Expr<'_>, left: } let (arg_ty, snip) = match expr.kind { - ExprKind::MethodCall(.., ref args, _) if args.len() == 1 => { - if match_trait_method(cx, expr, &paths::TO_STRING) || match_trait_method(cx, expr, &paths::TO_OWNED) { - (cx.typeck_results().expr_ty(&args[0]), snippet(cx, args[0].span, "..")) - } else { - return; - } + ExprKind::MethodCall(.., args, _) if args.len() == 1 => { + if_chain!( + if let Some(expr_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id); + if is_diagnostic_assoc_item(cx, expr_def_id, sym::ToString) + || is_diagnostic_assoc_item(cx, expr_def_id, sym::ToOwned); + then { + (cx.typeck_results().expr_ty(&args[0]), snippet(cx, args[0].span, "..")) + } else { + return; + } + ) }, - ExprKind::Call(ref path, ref v) if v.len() == 1 => { + ExprKind::Call(path, v) if v.len() == 1 => { if let ExprKind::Path(ref path) = path.kind { if match_qpath(path, &["String", "from_str"]) || match_qpath(path, &["String", "from"]) { (cx.typeck_results().expr_ty(&v[0]), snippet(cx, v[0].span, "..")) @@ -645,7 +649,7 @@ fn check_to_owned(cx: &LateContext<'_>, expr: &Expr<'_>, other: &Expr<'_>, left: /// of what it means for an expression to be "used". fn is_used(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { get_parent_expr(cx, expr).map_or(true, |parent| match parent.kind { - ExprKind::Assign(_, ref rhs, _) | ExprKind::AssignOp(_, _, ref rhs) => SpanlessEq::new(cx).eq_expr(rhs, expr), + ExprKind::Assign(_, rhs, _) | ExprKind::AssignOp(_, _, rhs) => SpanlessEq::new(cx).eq_expr(rhs, expr), _ => is_used(cx, parent), }) } diff --git a/src/tools/clippy/clippy_lints/src/misc_early.rs b/src/tools/clippy/clippy_lints/src/misc_early.rs index 84a0df92f5b43..3c6a7071c24a7 100644 --- a/src/tools/clippy/clippy_lints/src/misc_early.rs +++ b/src/tools/clippy/clippy_lints/src/misc_early.rs @@ -1,4 +1,5 @@ -use crate::utils::{snippet_opt, span_lint, span_lint_and_help, span_lint_and_sugg, span_lint_and_then}; +use clippy_utils::diagnostics::{span_lint, span_lint_and_help, span_lint_and_sugg, span_lint_and_then}; +use clippy_utils::source::snippet_opt; use rustc_ast::ast::{ BindingMode, Expr, ExprKind, GenericParamKind, Generics, Lit, LitFloatType, LitIntType, LitKind, Mutability, NodeId, Pat, PatKind, UnOp, diff --git a/src/tools/clippy/clippy_lints/src/missing_const_for_fn.rs b/src/tools/clippy/clippy_lints/src/missing_const_for_fn.rs index 6ebeaced62a33..0dc02431ad538 100644 --- a/src/tools/clippy/clippy_lints/src/missing_const_for_fn.rs +++ b/src/tools/clippy/clippy_lints/src/missing_const_for_fn.rs @@ -1,7 +1,7 @@ -use crate::utils::qualify_min_const_fn::is_min_const_fn; -use crate::utils::{ - fn_has_unsatisfiable_preds, has_drop, is_entrypoint_fn, meets_msrv, span_lint, trait_ref_of_method, -}; +use clippy_utils::diagnostics::span_lint; +use clippy_utils::qualify_min_const_fn::is_min_const_fn; +use clippy_utils::ty::has_drop; +use clippy_utils::{fn_has_unsatisfiable_preds, is_entrypoint_fn, meets_msrv, trait_ref_of_method}; use rustc_hir as hir; use rustc_hir::intravisit::FnKind; use rustc_hir::{Body, Constness, FnDecl, GenericParamKind, HirId}; @@ -133,12 +133,12 @@ impl<'tcx> LateLintPass<'tcx> for MissingConstForFn { return; } }, - FnKind::Closure(..) => return, + FnKind::Closure => return, } let mir = cx.tcx.optimized_mir(def_id); - if let Err((span, err)) = is_min_const_fn(cx.tcx, &mir) { + if let Err((span, err)) = is_min_const_fn(cx.tcx, mir) { if rustc_mir::const_eval::is_min_const_fn(cx.tcx, def_id.to_def_id()) { cx.tcx.sess.span_err(span, &err); } diff --git a/src/tools/clippy/clippy_lints/src/missing_doc.rs b/src/tools/clippy/clippy_lints/src/missing_doc.rs index 761b9261772b2..dfab3e8a93112 100644 --- a/src/tools/clippy/clippy_lints/src/missing_doc.rs +++ b/src/tools/clippy/clippy_lints/src/missing_doc.rs @@ -5,10 +5,10 @@ // [`missing_doc`]: https://github.com/rust-lang/rust/blob/cf9cf7c923eb01146971429044f216a3ca905e06/compiler/rustc_lint/src/builtin.rs#L415 // -use crate::utils::span_lint; +use clippy_utils::attrs::is_doc_hidden; +use clippy_utils::diagnostics::span_lint; use if_chain::if_chain; use rustc_ast::ast::{self, MetaItem, MetaItemKind}; -use rustc_ast::attr; use rustc_hir as hir; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::ty; @@ -93,9 +93,9 @@ impl MissingDoc { return; } - let has_doc = attrs - .iter() - .any(|a| a.is_doc_comment() || a.doc_str().is_some() || a.is_value_str() || Self::has_include(a.meta())); + let has_doc = attrs.iter().any(|a| { + a.is_doc_comment() || a.doc_str().is_some() || a.value_str().is_some() || Self::has_include(a.meta()) + }); if !has_doc { span_lint( cx, @@ -111,14 +111,7 @@ impl_lint_pass!(MissingDoc => [MISSING_DOCS_IN_PRIVATE_ITEMS]); impl<'tcx> LateLintPass<'tcx> for MissingDoc { fn enter_lint_attrs(&mut self, _: &LateContext<'tcx>, attrs: &'tcx [ast::Attribute]) { - let doc_hidden = self.doc_hidden() - || attrs.iter().any(|attr| { - attr.has_name(sym::doc) - && match attr.meta_item_list() { - None => false, - Some(l) => attr::list_contains_name(&l[..], sym::hidden), - } - }); + let doc_hidden = self.doc_hidden() || is_doc_hidden(attrs); self.doc_hidden_stack.push(doc_hidden); } @@ -127,7 +120,8 @@ impl<'tcx> LateLintPass<'tcx> for MissingDoc { } fn check_crate(&mut self, cx: &LateContext<'tcx>, krate: &'tcx hir::Crate<'_>) { - self.check_missing_docs_attrs(cx, &krate.item.attrs, krate.item.span, "the", "crate"); + let attrs = cx.tcx.hir().attrs(hir::CRATE_HIR_ID); + self.check_missing_docs_attrs(cx, attrs, krate.item.inner, "the", "crate"); } fn check_item(&mut self, cx: &LateContext<'tcx>, it: &'tcx hir::Item<'_>) { @@ -160,13 +154,15 @@ impl<'tcx> LateLintPass<'tcx> for MissingDoc { let (article, desc) = cx.tcx.article_and_description(it.def_id.to_def_id()); - self.check_missing_docs_attrs(cx, &it.attrs, it.span, article, desc); + let attrs = cx.tcx.hir().attrs(it.hir_id()); + self.check_missing_docs_attrs(cx, attrs, it.span, article, desc); } fn check_trait_item(&mut self, cx: &LateContext<'tcx>, trait_item: &'tcx hir::TraitItem<'_>) { let (article, desc) = cx.tcx.article_and_description(trait_item.def_id.to_def_id()); - self.check_missing_docs_attrs(cx, &trait_item.attrs, trait_item.span, article, desc); + let attrs = cx.tcx.hir().attrs(trait_item.hir_id()); + self.check_missing_docs_attrs(cx, attrs, trait_item.span, article, desc); } fn check_impl_item(&mut self, cx: &LateContext<'tcx>, impl_item: &'tcx hir::ImplItem<'_>) { @@ -181,16 +177,19 @@ impl<'tcx> LateLintPass<'tcx> for MissingDoc { } let (article, desc) = cx.tcx.article_and_description(impl_item.def_id.to_def_id()); - self.check_missing_docs_attrs(cx, &impl_item.attrs, impl_item.span, article, desc); + let attrs = cx.tcx.hir().attrs(impl_item.hir_id()); + self.check_missing_docs_attrs(cx, attrs, impl_item.span, article, desc); } - fn check_struct_field(&mut self, cx: &LateContext<'tcx>, sf: &'tcx hir::StructField<'_>) { + fn check_field_def(&mut self, cx: &LateContext<'tcx>, sf: &'tcx hir::FieldDef<'_>) { if !sf.is_positional() { - self.check_missing_docs_attrs(cx, &sf.attrs, sf.span, "a", "struct field"); + let attrs = cx.tcx.hir().attrs(sf.hir_id); + self.check_missing_docs_attrs(cx, attrs, sf.span, "a", "struct field"); } } fn check_variant(&mut self, cx: &LateContext<'tcx>, v: &'tcx hir::Variant<'_>) { - self.check_missing_docs_attrs(cx, &v.attrs, v.span, "a", "variant"); + let attrs = cx.tcx.hir().attrs(v.id); + self.check_missing_docs_attrs(cx, attrs, v.span, "a", "variant"); } } diff --git a/src/tools/clippy/clippy_lints/src/missing_inline.rs b/src/tools/clippy/clippy_lints/src/missing_inline.rs index 47d7c5306c433..041fe64a1a978 100644 --- a/src/tools/clippy/clippy_lints/src/missing_inline.rs +++ b/src/tools/clippy/clippy_lints/src/missing_inline.rs @@ -1,4 +1,4 @@ -use crate::utils::span_lint; +use clippy_utils::diagnostics::span_lint; use rustc_ast::ast; use rustc_hir as hir; use rustc_lint::{self, LateContext, LateLintPass, LintContext}; @@ -69,21 +69,21 @@ fn check_missing_inline_attrs(cx: &LateContext<'_>, attrs: &[ast::Attribute], sp } } -fn is_executable(cx: &LateContext<'_>) -> bool { +fn is_executable_or_proc_macro(cx: &LateContext<'_>) -> bool { use rustc_session::config::CrateType; cx.tcx .sess .crate_types() .iter() - .any(|t: &CrateType| matches!(t, CrateType::Executable)) + .any(|t: &CrateType| matches!(t, CrateType::Executable | CrateType::ProcMacro)) } declare_lint_pass!(MissingInline => [MISSING_INLINE_IN_PUBLIC_ITEMS]); impl<'tcx> LateLintPass<'tcx> for MissingInline { fn check_item(&mut self, cx: &LateContext<'tcx>, it: &'tcx hir::Item<'_>) { - if rustc_middle::lint::in_external_macro(cx.sess(), it.span) || is_executable(cx) { + if rustc_middle::lint::in_external_macro(cx.sess(), it.span) || is_executable_or_proc_macro(cx) { return; } @@ -93,9 +93,10 @@ impl<'tcx> LateLintPass<'tcx> for MissingInline { match it.kind { hir::ItemKind::Fn(..) => { let desc = "a function"; - check_missing_inline_attrs(cx, &it.attrs, it.span, desc); + let attrs = cx.tcx.hir().attrs(it.hir_id()); + check_missing_inline_attrs(cx, attrs, it.span, desc); }, - hir::ItemKind::Trait(ref _is_auto, ref _unsafe, ref _generics, ref _bounds, trait_items) => { + hir::ItemKind::Trait(ref _is_auto, ref _unsafe, ref _generics, _bounds, trait_items) => { // note: we need to check if the trait is exported so we can't use // `LateLintPass::check_trait_item` here. for tit in trait_items { @@ -108,7 +109,8 @@ impl<'tcx> LateLintPass<'tcx> for MissingInline { // an impl is not provided let desc = "a default trait method"; let item = cx.tcx.hir().trait_item(tit.id); - check_missing_inline_attrs(cx, &item.attrs, item.span, desc); + let attrs = cx.tcx.hir().attrs(item.hir_id()); + check_missing_inline_attrs(cx, attrs, item.span, desc); } }, } @@ -133,7 +135,7 @@ impl<'tcx> LateLintPass<'tcx> for MissingInline { fn check_impl_item(&mut self, cx: &LateContext<'tcx>, impl_item: &'tcx hir::ImplItem<'_>) { use rustc_middle::ty::{ImplContainer, TraitContainer}; - if rustc_middle::lint::in_external_macro(cx.sess(), impl_item.span) || is_executable(cx) { + if rustc_middle::lint::in_external_macro(cx.sess(), impl_item.span) || is_executable_or_proc_macro(cx) { return; } @@ -160,6 +162,7 @@ impl<'tcx> LateLintPass<'tcx> for MissingInline { } } - check_missing_inline_attrs(cx, &impl_item.attrs, impl_item.span, desc); + let attrs = cx.tcx.hir().attrs(impl_item.hir_id()); + check_missing_inline_attrs(cx, attrs, impl_item.span, desc); } } diff --git a/src/tools/clippy/clippy_lints/src/modulo_arithmetic.rs b/src/tools/clippy/clippy_lints/src/modulo_arithmetic.rs index da3ae1d652f6c..6a52de4f71364 100644 --- a/src/tools/clippy/clippy_lints/src/modulo_arithmetic.rs +++ b/src/tools/clippy/clippy_lints/src/modulo_arithmetic.rs @@ -1,5 +1,6 @@ use crate::consts::{constant, Constant}; -use crate::utils::{sext, span_lint_and_then}; +use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::sext; use if_chain::if_chain; use rustc_hir::{BinOpKind, Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; diff --git a/src/tools/clippy/clippy_lints/src/multiple_crate_versions.rs b/src/tools/clippy/clippy_lints/src/multiple_crate_versions.rs index c1773cef7a8b7..584daa5e11992 100644 --- a/src/tools/clippy/clippy_lints/src/multiple_crate_versions.rs +++ b/src/tools/clippy/clippy_lints/src/multiple_crate_versions.rs @@ -1,6 +1,7 @@ //! lint on multiple versions of a crate being used -use crate::utils::{run_lints, span_lint}; +use clippy_utils::diagnostics::span_lint; +use clippy_utils::run_lints; use rustc_hir::def_id::LOCAL_CRATE; use rustc_hir::{Crate, CRATE_HIR_ID}; use rustc_lint::{LateContext, LateLintPass}; diff --git a/src/tools/clippy/clippy_lints/src/mut_key.rs b/src/tools/clippy/clippy_lints/src/mut_key.rs index 908b7bb7ce00d..1786d5805d78b 100644 --- a/src/tools/clippy/clippy_lints/src/mut_key.rs +++ b/src/tools/clippy/clippy_lints/src/mut_key.rs @@ -1,10 +1,12 @@ -use crate::utils::{match_def_path, paths, span_lint, trait_ref_of_method}; +use clippy_utils::diagnostics::span_lint; +use clippy_utils::{match_def_path, paths, trait_ref_of_method}; use rustc_hir as hir; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::TypeFoldable; use rustc_middle::ty::{Adt, Array, RawPtr, Ref, Slice, Tuple, Ty, TypeAndMut}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::source_map::Span; +use std::iter; declare_clippy_lint! { /// **What it does:** Checks for sets/maps with mutable key types. @@ -57,21 +59,21 @@ declare_lint_pass!(MutableKeyType => [ MUTABLE_KEY_TYPE ]); impl<'tcx> LateLintPass<'tcx> for MutableKeyType { fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'tcx>) { if let hir::ItemKind::Fn(ref sig, ..) = item.kind { - check_sig(cx, item.hir_id(), &sig.decl); + check_sig(cx, item.hir_id(), sig.decl); } } fn check_impl_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::ImplItem<'tcx>) { if let hir::ImplItemKind::Fn(ref sig, ..) = item.kind { if trait_ref_of_method(cx, item.hir_id()).is_none() { - check_sig(cx, item.hir_id(), &sig.decl); + check_sig(cx, item.hir_id(), sig.decl); } } } fn check_trait_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::TraitItem<'tcx>) { if let hir::TraitItemKind::Fn(ref sig, ..) = item.kind { - check_sig(cx, item.hir_id(), &sig.decl); + check_sig(cx, item.hir_id(), sig.decl); } } @@ -86,7 +88,7 @@ impl<'tcx> LateLintPass<'tcx> for MutableKeyType { fn check_sig<'tcx>(cx: &LateContext<'tcx>, item_hir_id: hir::HirId, decl: &hir::FnDecl<'_>) { let fn_def_id = cx.tcx.hir().local_def_id(item_hir_id); let fn_sig = cx.tcx.fn_sig(fn_def_id); - for (hir_ty, ty) in decl.inputs.iter().zip(fn_sig.inputs().skip_binder().iter()) { + for (hir_ty, ty) in iter::zip(decl.inputs, fn_sig.inputs().skip_binder()) { check_ty(cx, hir_ty.span, ty); } check_ty(cx, decl.output.span(), cx.tcx.erase_late_bound_regions(fn_sig.output())); diff --git a/src/tools/clippy/clippy_lints/src/mut_mut.rs b/src/tools/clippy/clippy_lints/src/mut_mut.rs index d7239b328bbcd..4b9c51d0c16cc 100644 --- a/src/tools/clippy/clippy_lints/src/mut_mut.rs +++ b/src/tools/clippy/clippy_lints/src/mut_mut.rs @@ -1,4 +1,5 @@ -use crate::utils::{higher, span_lint}; +use clippy_utils::diagnostics::span_lint; +use clippy_utils::higher; use rustc_hir as hir; use rustc_hir::intravisit; use rustc_lint::{LateContext, LateLintPass, LintContext}; @@ -61,7 +62,7 @@ impl<'a, 'tcx> intravisit::Visitor<'tcx> for MutVisitor<'a, 'tcx> { // Let's ignore the generated code. intravisit::walk_expr(self, arg); intravisit::walk_expr(self, body); - } else if let hir::ExprKind::AddrOf(hir::BorrowKind::Ref, hir::Mutability::Mut, ref e) = expr.kind { + } else if let hir::ExprKind::AddrOf(hir::BorrowKind::Ref, hir::Mutability::Mut, e) = expr.kind { if let hir::ExprKind::AddrOf(hir::BorrowKind::Ref, hir::Mutability::Mut, _) = e.kind { span_lint( self.cx, @@ -84,7 +85,7 @@ impl<'a, 'tcx> intravisit::Visitor<'tcx> for MutVisitor<'a, 'tcx> { if let hir::TyKind::Rptr( _, hir::MutTy { - ty: ref pty, + ty: pty, mutbl: hir::Mutability::Mut, }, ) = ty.kind diff --git a/src/tools/clippy/clippy_lints/src/mut_mutex_lock.rs b/src/tools/clippy/clippy_lints/src/mut_mutex_lock.rs index df1cecb328cb1..b9ba74c7d0252 100644 --- a/src/tools/clippy/clippy_lints/src/mut_mutex_lock.rs +++ b/src/tools/clippy/clippy_lints/src/mut_mutex_lock.rs @@ -1,4 +1,5 @@ -use crate::utils::{is_type_diagnostic_item, span_lint_and_sugg}; +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::ty::is_type_diagnostic_item; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind, Mutability}; diff --git a/src/tools/clippy/clippy_lints/src/mut_reference.rs b/src/tools/clippy/clippy_lints/src/mut_reference.rs index 3f0b765df1561..cea6fce119561 100644 --- a/src/tools/clippy/clippy_lints/src/mut_reference.rs +++ b/src/tools/clippy/clippy_lints/src/mut_reference.rs @@ -1,9 +1,10 @@ -use crate::utils::span_lint; +use clippy_utils::diagnostics::span_lint; use rustc_hir::{BorrowKind, Expr, ExprKind, Mutability}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::subst::Subst; use rustc_middle::ty::{self, Ty}; use rustc_session::{declare_lint_pass, declare_tool_lint}; +use std::iter; declare_clippy_lint! { /// **What it does:** Detects passing a mutable reference to a function that only @@ -32,7 +33,7 @@ declare_lint_pass!(UnnecessaryMutPassed => [UNNECESSARY_MUT_PASSED]); impl<'tcx> LateLintPass<'tcx> for UnnecessaryMutPassed { fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) { match e.kind { - ExprKind::Call(ref fn_expr, ref arguments) => { + ExprKind::Call(fn_expr, arguments) => { if let ExprKind::Path(ref path) = fn_expr.kind { check_arguments( cx, @@ -43,7 +44,7 @@ impl<'tcx> LateLintPass<'tcx> for UnnecessaryMutPassed { ); } }, - ExprKind::MethodCall(ref path, _, ref arguments, _) => { + ExprKind::MethodCall(path, _, arguments, _) => { let def_id = cx.typeck_results().type_dependent_def_id(e.hir_id).unwrap(); let substs = cx.typeck_results().node_substs(e.hir_id); let method_type = cx.tcx.type_of(def_id).subst(cx.tcx, substs); @@ -64,7 +65,7 @@ fn check_arguments<'tcx>( match type_definition.kind() { ty::FnDef(..) | ty::FnPtr(_) => { let parameters = type_definition.fn_sig(cx.tcx).skip_binder().inputs(); - for (argument, parameter) in arguments.iter().zip(parameters.iter()) { + for (argument, parameter) in iter::zip(arguments, parameters) { match parameter.kind() { ty::Ref(_, _, Mutability::Not) | ty::RawPtr(ty::TypeAndMut { 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 71f91eb4bfbe3..7dfe12cd4ebc0 100644 --- a/src/tools/clippy/clippy_lints/src/mutable_debug_assertion.rs +++ b/src/tools/clippy/clippy_lints/src/mutable_debug_assertion.rs @@ -1,4 +1,5 @@ -use crate::utils::{higher, is_direct_expn_of, span_lint}; +use clippy_utils::diagnostics::span_lint; +use clippy_utils::{higher, is_direct_expn_of}; use rustc_hir::intravisit::{walk_expr, NestedVisitorMap, Visitor}; use rustc_hir::{BorrowKind, Expr, ExprKind, MatchSource, Mutability}; use rustc_lint::{LateContext, LateLintPass}; @@ -73,11 +74,7 @@ impl<'a, 'tcx> MutArgVisitor<'a, 'tcx> { } fn expr_span(&self) -> Option<Span> { - if self.found { - self.expr_span - } else { - None - } + if self.found { self.expr_span } else { None } } } diff --git a/src/tools/clippy/clippy_lints/src/mutex_atomic.rs b/src/tools/clippy/clippy_lints/src/mutex_atomic.rs index 40b236493a313..354e2c3fb7480 100644 --- a/src/tools/clippy/clippy_lints/src/mutex_atomic.rs +++ b/src/tools/clippy/clippy_lints/src/mutex_atomic.rs @@ -2,7 +2,8 @@ //! //! This lint is **warn** by default -use crate::utils::{is_type_diagnostic_item, span_lint}; +use clippy_utils::diagnostics::span_lint; +use clippy_utils::ty::is_type_diagnostic_item; use rustc_hir::Expr; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::{self, Ty}; diff --git a/src/tools/clippy/clippy_lints/src/needless_arbitrary_self_type.rs b/src/tools/clippy/clippy_lints/src/needless_arbitrary_self_type.rs index 7687962bdd9bf..3e2b2782ed5ff 100644 --- a/src/tools/clippy/clippy_lints/src/needless_arbitrary_self_type.rs +++ b/src/tools/clippy/clippy_lints/src/needless_arbitrary_self_type.rs @@ -1,4 +1,5 @@ -use crate::utils::{in_macro, span_lint_and_sugg}; +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::in_macro; use if_chain::if_chain; use rustc_ast::ast::{BindingMode, Lifetime, Mutability, Param, PatKind, Path, TyKind}; use rustc_errors::Applicability; diff --git a/src/tools/clippy/clippy_lints/src/needless_bool.rs b/src/tools/clippy/clippy_lints/src/needless_bool.rs index f283ff1715fb6..96a58d1410f22 100644 --- a/src/tools/clippy/clippy_lints/src/needless_bool.rs +++ b/src/tools/clippy/clippy_lints/src/needless_bool.rs @@ -2,8 +2,10 @@ //! //! This lint is **warn** by default -use crate::utils::sugg::Sugg; -use crate::utils::{is_expn_of, parent_node_is_if_expr, snippet_with_applicability, span_lint, span_lint_and_sugg}; +use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg}; +use clippy_utils::source::snippet_with_applicability; +use clippy_utils::sugg::Sugg; +use clippy_utils::{is_expn_of, parent_node_is_if_expr}; use rustc_ast::ast::LitKind; use rustc_errors::Applicability; use rustc_hir::{BinOpKind, Block, Expr, ExprKind, StmtKind, UnOp}; @@ -69,7 +71,7 @@ declare_lint_pass!(NeedlessBool => [NEEDLESS_BOOL]); impl<'tcx> LateLintPass<'tcx> for NeedlessBool { fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) { use self::Expression::{Bool, RetBool}; - if let ExprKind::If(ref pred, ref then_block, Some(ref else_expr)) = e.kind { + if let ExprKind::If(pred, then_block, Some(else_expr)) = e.kind { let reduce = |ret, not| { let mut applicability = Applicability::MachineApplicable; let snip = Sugg::hir_with_applicability(cx, pred, "<predicate>", &mut applicability); @@ -79,7 +81,7 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessBool { snip = snip.make_return(); } - if parent_node_is_if_expr(&e, &cx) { + if parent_node_is_if_expr(e, cx) { snip = snip.blockify() } @@ -93,7 +95,7 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessBool { applicability, ); }; - if let ExprKind::Block(ref then_block, _) = then_block.kind { + if let ExprKind::Block(then_block, _) = then_block.kind { match (fetch_bool_block(then_block), fetch_bool_expr(else_expr)) { (RetBool(true), RetBool(true)) | (Bool(true), Bool(true)) => { span_lint( @@ -223,7 +225,7 @@ fn check_comparison<'a, 'tcx>( ) { use self::Expression::{Bool, Other}; - if let ExprKind::Binary(op, ref left_side, ref right_side) = e.kind { + if let ExprKind::Binary(op, left_side, right_side) = e.kind { let (l_ty, r_ty) = ( cx.typeck_results().expr_ty(left_side), cx.typeck_results().expr_ty(right_side), @@ -235,7 +237,7 @@ fn check_comparison<'a, 'tcx>( let mut applicability = Applicability::MachineApplicable; if let BinOpKind::Eq = op.node { - let expression_info = one_side_is_unary_not(&left_side, &right_side); + let expression_info = one_side_is_unary_not(left_side, right_side); if expression_info.one_side_is_unary_not { span_lint_and_sugg( cx, @@ -322,9 +324,9 @@ fn fetch_bool_block(block: &Block<'_>) -> Expression { match (&*block.stmts, block.expr.as_ref()) { (&[], Some(e)) => fetch_bool_expr(&**e), (&[ref e], None) => { - if let StmtKind::Semi(ref e) = e.kind { + if let StmtKind::Semi(e) = e.kind { if let ExprKind::Ret(_) = e.kind { - fetch_bool_expr(&**e) + fetch_bool_expr(e) } else { Expression::Other } @@ -338,7 +340,7 @@ fn fetch_bool_block(block: &Block<'_>) -> Expression { fn fetch_bool_expr(expr: &Expr<'_>) -> Expression { match expr.kind { - ExprKind::Block(ref block, _) => fetch_bool_block(block), + ExprKind::Block(block, _) => fetch_bool_block(block), ExprKind::Lit(ref lit_ptr) => { if let LitKind::Bool(value) = lit_ptr.node { Expression::Bool(value) @@ -346,7 +348,7 @@ fn fetch_bool_expr(expr: &Expr<'_>) -> Expression { Expression::Other } }, - ExprKind::Ret(Some(ref expr)) => match fetch_bool_expr(expr) { + ExprKind::Ret(Some(expr)) => match fetch_bool_expr(expr) { Expression::Bool(value) => Expression::RetBool(value), _ => Expression::Other, }, diff --git a/src/tools/clippy/clippy_lints/src/needless_borrow.rs b/src/tools/clippy/clippy_lints/src/needless_borrow.rs index 1453ea6e8975d..eef3c16730b13 100644 --- a/src/tools/clippy/clippy_lints/src/needless_borrow.rs +++ b/src/tools/clippy/clippy_lints/src/needless_borrow.rs @@ -2,7 +2,9 @@ //! //! This lint is **warn** by default -use crate::utils::{is_automatically_derived, snippet_opt, span_lint_and_then}; +use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::is_automatically_derived; +use clippy_utils::source::snippet_opt; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::{BindingAnnotation, BorrowKind, Expr, ExprKind, Item, Mutability, Pat, PatKind}; @@ -46,7 +48,7 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessBorrow { if e.span.from_expansion() || self.derived_item.is_some() { return; } - if let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Not, ref inner) = e.kind { + if let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Not, inner) = e.kind { if let ty::Ref(_, ty, _) = cx.typeck_results().expr_ty(inner).kind() { for adj3 in cx.typeck_results().expr_adjustments(e).windows(3) { if let [Adjustment { @@ -115,8 +117,9 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessBorrow { } } - fn check_item(&mut self, _: &LateContext<'tcx>, item: &'tcx Item<'_>) { - if is_automatically_derived(item.attrs) { + fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) { + let attrs = cx.tcx.hir().attrs(item.hir_id()); + if is_automatically_derived(attrs) { debug_assert!(self.derived_item.is_none()); self.derived_item = Some(item.def_id); } diff --git a/src/tools/clippy/clippy_lints/src/needless_borrowed_ref.rs b/src/tools/clippy/clippy_lints/src/needless_borrowed_ref.rs index 85184fdea4779..0e976b130ebf1 100644 --- a/src/tools/clippy/clippy_lints/src/needless_borrowed_ref.rs +++ b/src/tools/clippy/clippy_lints/src/needless_borrowed_ref.rs @@ -1,8 +1,5 @@ -//! Checks for useless borrowed references. -//! -//! This lint is **warn** by default - -use crate::utils::{snippet_with_applicability, span_lint_and_then}; +use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::source::snippet_with_applicability; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::{BindingAnnotation, Mutability, Node, Pat, PatKind}; @@ -10,44 +7,37 @@ use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; declare_clippy_lint! { - /// **What it does:** Checks for useless borrowed references. - /// - /// **Why is this bad?** It is mostly useless and make the code look more - /// complex than it - /// actually is. + /// **What it does:** Checks for bindings that destructure a reference and borrow the inner + /// value with `&ref`. /// - /// **Known problems:** It seems that the `&ref` pattern is sometimes useful. - /// For instance in the following snippet: - /// ```rust,ignore - /// enum Animal { - /// Cat(u64), - /// Dog(u64), - /// } + /// **Why is this bad?** This pattern has no effect in almost all cases. /// - /// fn foo(a: &Animal, b: &Animal) { + /// **Known problems:** In some cases, `&ref` is needed to avoid a lifetime mismatch error. + /// Example: + /// ```rust + /// fn foo(a: &Option<String>, b: &Option<String>) { /// match (a, b) { - /// (&Animal::Cat(v), k) | (k, &Animal::Cat(v)) => (), // lifetime mismatch error - /// (&Animal::Dog(ref c), &Animal::Dog(_)) => () - /// } + /// (None, &ref c) | (&ref c, None) => (), + /// (&Some(ref c), _) => (), + /// }; /// } /// ``` - /// There is a lifetime mismatch error for `k` (indeed a and b have distinct - /// lifetime). - /// This can be fixed by using the `&ref` pattern. - /// However, the code can also be fixed by much cleaner ways /// /// **Example:** + /// Bad: /// ```rust /// let mut v = Vec::<String>::new(); /// let _ = v.iter_mut().filter(|&ref a| a.is_empty()); /// ``` - /// This closure takes a reference on something that has been matched as a - /// reference and - /// de-referenced. - /// As such, it could just be |a| a.is_empty() + /// + /// Good: + /// ```rust + /// let mut v = Vec::<String>::new(); + /// let _ = v.iter_mut().filter(|a| a.is_empty()); + /// ``` pub NEEDLESS_BORROWED_REFERENCE, complexity, - "taking a needless borrowed reference" + "destructuring a reference and borrowing the inner value" } declare_lint_pass!(NeedlessBorrowedRef => [NEEDLESS_BORROWED_REFERENCE]); @@ -61,7 +51,7 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessBorrowedRef { if_chain! { // Only lint immutable refs, because `&mut ref T` may be useful. - if let PatKind::Ref(ref sub_pat, Mutability::Not) = pat.kind; + if let PatKind::Ref(sub_pat, Mutability::Not) = pat.kind; // Check sub_pat got a `ref` keyword (excluding `ref mut`). if let PatKind::Binding(BindingAnnotation::Ref, .., spanned_name, _) = sub_pat.kind; diff --git a/src/tools/clippy/clippy_lints/src/needless_continue.rs b/src/tools/clippy/clippy_lints/src/needless_continue.rs index 603071a5f4ac4..91c97ef7c2a40 100644 --- a/src/tools/clippy/clippy_lints/src/needless_continue.rs +++ b/src/tools/clippy/clippy_lints/src/needless_continue.rs @@ -33,14 +33,14 @@ //! ``` //! //! This lint is **warn** by default. +use clippy_utils::diagnostics::span_lint_and_help; +use clippy_utils::source::{indent_of, snippet, snippet_block}; use rustc_ast::ast; use rustc_lint::{EarlyContext, EarlyLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::source_map::{original_sp, DUMMY_SP}; use rustc_span::Span; -use crate::utils::{indent_of, snippet, snippet_block, span_lint_and_help}; - declare_clippy_lint! { /// **What it does:** The lint checks for `if`-statements appearing in loops /// that contain a `continue` statement in either their main blocks or their @@ -416,11 +416,7 @@ fn erode_from_back(s: &str) -> String { break; } } - if ret.is_empty() { - s.to_string() - } else { - ret - } + if ret.is_empty() { s.to_string() } else { ret } } fn span_of_first_expr_in_block(block: &ast::Block) -> Option<Span> { diff --git a/src/tools/clippy/clippy_lints/src/needless_for_each.rs b/src/tools/clippy/clippy_lints/src/needless_for_each.rs new file mode 100644 index 0000000000000..079b6642d5833 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/needless_for_each.rs @@ -0,0 +1,167 @@ +use rustc_errors::Applicability; +use rustc_hir::{ + intravisit::{walk_expr, NestedVisitorMap, Visitor}, + Expr, ExprKind, Stmt, StmtKind, +}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::hir::map::Map; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::{source_map::Span, sym, Symbol}; + +use if_chain::if_chain; + +use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::is_trait_method; +use clippy_utils::source::snippet_with_applicability; +use clippy_utils::ty::has_iter_method; + +declare_clippy_lint! { + /// **What it does:** Checks for usage of `for_each` that would be more simply written as a + /// `for` loop. + /// + /// **Why is this bad?** `for_each` may be used after applying iterator transformers like + /// `filter` for better readability and performance. It may also be used to fit a simple + /// operation on one line. + /// But when none of these apply, a simple `for` loop is more idiomatic. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// + /// ```rust + /// let v = vec![0, 1, 2]; + /// v.iter().for_each(|elem| { + /// println!("{}", elem); + /// }) + /// ``` + /// Use instead: + /// ```rust + /// let v = vec![0, 1, 2]; + /// for elem in v.iter() { + /// println!("{}", elem); + /// } + /// ``` + pub NEEDLESS_FOR_EACH, + pedantic, + "using `for_each` where a `for` loop would be simpler" +} + +declare_lint_pass!(NeedlessForEach => [NEEDLESS_FOR_EACH]); + +impl LateLintPass<'_> 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, + _ => return, + }; + + if_chain! { + // Check the method name is `for_each`. + if let ExprKind::MethodCall(method_name, _, [for_each_recv, for_each_arg], _) = expr.kind; + if method_name.ident.name == Symbol::intern("for_each"); + // Check `for_each` is an associated function of `Iterator`. + if is_trait_method(cx, expr, sym::Iterator); + // Checks the receiver of `for_each` is also a method call. + if let ExprKind::MethodCall(_, _, [iter_recv], _) = for_each_recv.kind; + // Skip the lint if the call chain is too long. e.g. `v.field.iter().for_each()` or + // `v.foo().iter().for_each()` must be skipped. + if matches!( + iter_recv.kind, + ExprKind::Array(..) | ExprKind::Call(..) | ExprKind::Path(..) + ); + // Checks the type of the `iter` method receiver is NOT a user defined type. + if has_iter_method(cx, cx.typeck_results().expr_ty(iter_recv)).is_some(); + // Skip the lint if the body is not block because this is simpler than `for` loop. + // e.g. `v.iter().for_each(f)` is simpler and clearer than using `for` loop. + if let ExprKind::Closure(_, _, body_id, ..) = for_each_arg.kind; + let body = cx.tcx.hir().body(body_id); + if let ExprKind::Block(..) = body.value.kind; + then { + let mut ret_collector = RetCollector::default(); + ret_collector.visit_expr(&body.value); + + // Skip the lint if `return` is used in `Loop` in order not to suggest using `'label`. + if ret_collector.ret_in_loop { + return; + } + + let (mut applicability, ret_suggs) = if ret_collector.spans.is_empty() { + (Applicability::MachineApplicable, None) + } else { + ( + Applicability::MaybeIncorrect, + Some( + ret_collector + .spans + .into_iter() + .map(|span| (span, "continue".to_string())) + .collect(), + ), + ) + }; + + let sugg = format!( + "for {} in {} {}", + snippet_with_applicability(cx, body.params[0].pat.span, "..", &mut applicability), + snippet_with_applicability(cx, for_each_recv.span, "..", &mut applicability), + snippet_with_applicability(cx, body.value.span, "..", &mut applicability), + ); + + span_lint_and_then(cx, NEEDLESS_FOR_EACH, stmt.span, "needless use of `for_each`", |diag| { + diag.span_suggestion(stmt.span, "try", sugg, applicability); + if let Some(ret_suggs) = ret_suggs { + diag.multipart_suggestion("...and replace `return` with `continue`", ret_suggs, applicability); + } + }) + } + } + } +} + +/// This type plays two roles. +/// 1. Collect spans of `return` in the closure body. +/// 2. Detect use of `return` in `Loop` in the closure body. +/// +/// NOTE: The functionality of this type is similar to +/// [`crate::utilts::visitors::find_all_ret_expressions`], but we can't use +/// `find_all_ret_expressions` instead of this type. The reasons are: +/// 1. `find_all_ret_expressions` passes the argument of `ExprKind::Ret` to a callback, but what we +/// need here is `ExprKind::Ret` itself. +/// 2. We can't trace current loop depth with `find_all_ret_expressions`. +#[derive(Default)] +struct RetCollector { + spans: Vec<Span>, + ret_in_loop: bool, + loop_depth: u16, +} + +impl<'tcx> Visitor<'tcx> for RetCollector { + type Map = Map<'tcx>; + + fn visit_expr(&mut self, expr: &Expr<'_>) { + match expr.kind { + ExprKind::Ret(..) => { + if self.loop_depth > 0 && !self.ret_in_loop { + self.ret_in_loop = true + } + + self.spans.push(expr.span) + }, + + ExprKind::Loop(..) => { + self.loop_depth += 1; + walk_expr(self, expr); + self.loop_depth -= 1; + return; + }, + + _ => {}, + } + + walk_expr(self, expr); + } + + fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> { + NestedVisitorMap::None + } +} diff --git a/src/tools/clippy/clippy_lints/src/needless_pass_by_value.rs b/src/tools/clippy/clippy_lints/src/needless_pass_by_value.rs index 54033f4087140..780e224129347 100644 --- a/src/tools/clippy/clippy_lints/src/needless_pass_by_value.rs +++ b/src/tools/clippy/clippy_lints/src/needless_pass_by_value.rs @@ -1,16 +1,18 @@ -use crate::utils::ptr::get_spans; -use crate::utils::{ - get_trait_def_id, implements_trait, is_copy, is_self, is_type_diagnostic_item, multispan_sugg, paths, snippet, - snippet_opt, span_lint_and_then, -}; +use clippy_utils::diagnostics::{multispan_sugg, span_lint_and_then}; +use clippy_utils::ptr::get_spans; +use clippy_utils::source::{snippet, snippet_opt}; +use clippy_utils::ty::{implements_trait, is_copy, is_type_diagnostic_item}; +use clippy_utils::{get_trait_def_id, is_self, paths}; use if_chain::if_chain; use rustc_ast::ast::Attribute; -use rustc_data_structures::fx::{FxHashMap, FxHashSet}; +use rustc_data_structures::fx::FxHashSet; use rustc_errors::{Applicability, DiagnosticBuilder}; use rustc_hir::intravisit::FnKind; use rustc_hir::{BindingAnnotation, Body, FnDecl, GenericArg, HirId, Impl, ItemKind, Node, PatKind, QPath, TyKind}; +use rustc_hir::{HirIdMap, HirIdSet}; use rustc_infer::infer::TyCtxtInferExt; use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::mir::FakeReadCause; use rustc_middle::ty::{self, TypeFoldable}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::symbol::kw; @@ -80,13 +82,14 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByValue { } match kind { - FnKind::ItemFn(.., header, _, attrs) => { + FnKind::ItemFn(.., header, _) => { + let attrs = cx.tcx.hir().attrs(hir_id); if header.abi != Abi::Rust || requires_exact_signature(attrs) { return; } }, FnKind::Method(..) => (), - FnKind::Closure(..) => return, + FnKind::Closure => return, } // Exclude non-inherent impls @@ -205,7 +208,7 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByValue { if is_type_diagnostic_item(cx, ty, sym::vec_type); if let Some(clone_spans) = get_spans(cx, Some(body.id()), idx, &[("clone", ".to_owned()")]); - if let TyKind::Path(QPath::Resolved(_, ref path)) = input.kind; + if let TyKind::Path(QPath::Resolved(_, path)) = input.kind; if let Some(elem_ty) = path.segments.iter() .find(|seg| seg.ident.name == sym::Vec) .and_then(|ps| ps.args.as_ref()) @@ -308,10 +311,10 @@ fn requires_exact_signature(attrs: &[Attribute]) -> bool { #[derive(Default)] struct MovedVariablesCtxt { - moved_vars: FxHashSet<HirId>, + moved_vars: HirIdSet, /// Spans which need to be prefixed with `*` for dereferencing the /// suggested additional reference. - spans_need_deref: FxHashMap<HirId, FxHashSet<Span>>, + spans_need_deref: HirIdMap<FxHashSet<Span>>, } impl MovedVariablesCtxt { @@ -332,4 +335,6 @@ impl<'tcx> euv::Delegate<'tcx> for MovedVariablesCtxt { fn borrow(&mut self, _: &euv::PlaceWithHirId<'tcx>, _: HirId, _: ty::BorrowKind) {} fn mutate(&mut self, _: &euv::PlaceWithHirId<'tcx>, _: HirId) {} + + fn fake_read(&mut self, _: rustc_typeck::expr_use_visitor::Place<'tcx>, _: FakeReadCause, _: HirId) {} } diff --git a/src/tools/clippy/clippy_lints/src/needless_question_mark.rs b/src/tools/clippy/clippy_lints/src/needless_question_mark.rs index fe8d4d07abc15..9852633b7342e 100644 --- a/src/tools/clippy/clippy_lints/src/needless_question_mark.rs +++ b/src/tools/clippy/clippy_lints/src/needless_question_mark.rs @@ -1,3 +1,8 @@ +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::source::snippet; +use clippy_utils::ty::is_type_diagnostic_item; +use clippy_utils::{differing_macro_contexts, is_ok_ctor, is_some_ctor, meets_msrv}; +use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::{Body, Expr, ExprKind, LangItem, MatchSource, QPath}; use rustc_lint::{LateContext, LateLintPass, LintContext}; @@ -5,9 +10,6 @@ use rustc_semver::RustcVersion; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::sym; -use crate::utils; -use if_chain::if_chain; - declare_clippy_lint! { /// **What it does:** /// Suggests alternatives for useless applications of `?` in terminating expressions @@ -138,13 +140,13 @@ fn emit_lint(cx: &LateContext<'_>, expr: &SomeOkCall<'_>) { SomeOkCall::OkCall(outer, inner) | SomeOkCall::SomeCall(outer, inner) => (outer, inner), }; - utils::span_lint_and_sugg( + span_lint_and_sugg( cx, NEEDLESS_QUESTION_MARK, entire_expr.span, - "Question mark operator is useless here", + "question mark operator is useless here", "try", - format!("{}", utils::snippet(cx, inner_expr.span, r#""...""#)), + format!("{}", snippet(cx, inner_expr.span, r#""...""#)), Applicability::MachineApplicable, ); } @@ -158,7 +160,7 @@ fn is_some_or_ok_call<'a>( // Check outer expression matches CALL_IDENT(ARGUMENT) format if let ExprKind::Call(path, args) = &expr.kind; if let ExprKind::Path(QPath::Resolved(None, path)) = &path.kind; - if utils::is_some_ctor(cx, path.res) || utils::is_ok_ctor(cx, path.res); + if is_some_ctor(cx, path.res) || is_ok_ctor(cx, path.res); // Extract inner expression from ARGUMENT if let ExprKind::Match(inner_expr_with_q, _, MatchSource::TryDesugar) = &args[0].kind; @@ -171,29 +173,34 @@ fn is_some_or_ok_call<'a>( // question mark operator let inner_expr = &args[0]; + // if the inner expr is inside macro but the outer one is not, do not lint (#6921) + if differing_macro_contexts(expr.span, inner_expr.span) { + return None; + } + let inner_ty = cx.typeck_results().expr_ty(inner_expr); let outer_ty = cx.typeck_results().expr_ty(expr); // Check if outer and inner type are Option - let outer_is_some = utils::is_type_diagnostic_item(cx, outer_ty, sym::option_type); - let inner_is_some = utils::is_type_diagnostic_item(cx, inner_ty, sym::option_type); + let outer_is_some = is_type_diagnostic_item(cx, outer_ty, sym::option_type); + let inner_is_some = is_type_diagnostic_item(cx, inner_ty, sym::option_type); // Check for Option MSRV - let meets_option_msrv = utils::meets_msrv(nqml.msrv.as_ref(), &NEEDLESS_QUESTION_MARK_OPTION_MSRV); + let meets_option_msrv = meets_msrv(nqml.msrv.as_ref(), &NEEDLESS_QUESTION_MARK_OPTION_MSRV); if outer_is_some && inner_is_some && meets_option_msrv { return Some(SomeOkCall::SomeCall(expr, inner_expr)); } // Check if outer and inner type are Result - let outer_is_result = utils::is_type_diagnostic_item(cx, outer_ty, sym::result_type); - let inner_is_result = utils::is_type_diagnostic_item(cx, inner_ty, sym::result_type); + let outer_is_result = is_type_diagnostic_item(cx, outer_ty, sym::result_type); + let inner_is_result = is_type_diagnostic_item(cx, inner_ty, sym::result_type); // Additional check: if the error type of the Result can be converted // via the From trait, then don't match let does_not_call_from = !has_implicit_error_from(cx, expr, inner_expr); // Must meet Result MSRV - let meets_result_msrv = utils::meets_msrv(nqml.msrv.as_ref(), &NEEDLESS_QUESTION_MARK_RESULT_MSRV); + let meets_result_msrv = meets_msrv(nqml.msrv.as_ref(), &NEEDLESS_QUESTION_MARK_RESULT_MSRV); if outer_is_result && inner_is_result && does_not_call_from && meets_result_msrv { return Some(SomeOkCall::OkCall(expr, inner_expr)); } diff --git a/src/tools/clippy/clippy_lints/src/needless_update.rs b/src/tools/clippy/clippy_lints/src/needless_update.rs index 41cf541ecf5ef..8f325404deb45 100644 --- a/src/tools/clippy/clippy_lints/src/needless_update.rs +++ b/src/tools/clippy/clippy_lints/src/needless_update.rs @@ -1,4 +1,4 @@ -use crate::utils::span_lint; +use clippy_utils::diagnostics::span_lint; use rustc_hir::{Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty; @@ -49,7 +49,7 @@ declare_lint_pass!(NeedlessUpdate => [NEEDLESS_UPDATE]); impl<'tcx> LateLintPass<'tcx> for NeedlessUpdate { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - if let ExprKind::Struct(_, ref fields, Some(ref base)) = expr.kind { + if let ExprKind::Struct(_, fields, Some(base)) = expr.kind { let ty = cx.typeck_results().expr_ty(expr); if let ty::Adt(def, _) = ty.kind() { if fields.len() == def.non_enum_variant().fields.len() diff --git a/src/tools/clippy/clippy_lints/src/neg_cmp_op_on_partial_ord.rs b/src/tools/clippy/clippy_lints/src/neg_cmp_op_on_partial_ord.rs index ec0ad58ca9c3e..0704173a01178 100644 --- a/src/tools/clippy/clippy_lints/src/neg_cmp_op_on_partial_ord.rs +++ b/src/tools/clippy/clippy_lints/src/neg_cmp_op_on_partial_ord.rs @@ -1,11 +1,12 @@ +use clippy_utils::diagnostics::span_lint; +use clippy_utils::ty::implements_trait; +use clippy_utils::{self, get_trait_def_id, paths}; use if_chain::if_chain; use rustc_hir::{BinOpKind, Expr, ExprKind, UnOp}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::lint::in_external_macro; use rustc_session::{declare_lint_pass, declare_tool_lint}; -use crate::utils::{self, paths, span_lint}; - declare_clippy_lint! { /// **What it does:** /// Checks for the usage of negated comparison operators on types which only implement @@ -50,8 +51,8 @@ impl<'tcx> LateLintPass<'tcx> for NoNegCompOpForPartialOrd { if_chain! { if !in_external_macro(cx.sess(), expr.span); - if let ExprKind::Unary(UnOp::Not, ref inner) = expr.kind; - if let ExprKind::Binary(ref op, ref left, _) = inner.kind; + if let ExprKind::Unary(UnOp::Not, inner) = expr.kind; + if let ExprKind::Binary(ref op, left, _) = inner.kind; if let BinOpKind::Le | BinOpKind::Ge | BinOpKind::Lt | BinOpKind::Gt = op.node; then { @@ -59,8 +60,8 @@ impl<'tcx> LateLintPass<'tcx> for NoNegCompOpForPartialOrd { let ty = cx.typeck_results().expr_ty(left); let implements_ord = { - if let Some(id) = utils::get_trait_def_id(cx, &paths::ORD) { - utils::implements_trait(cx, ty, id, &[]) + if let Some(id) = get_trait_def_id(cx, &paths::ORD) { + implements_trait(cx, ty, id, &[]) } else { return; } @@ -68,7 +69,7 @@ impl<'tcx> LateLintPass<'tcx> for NoNegCompOpForPartialOrd { let implements_partial_ord = { if let Some(id) = cx.tcx.lang_items().partial_ord_trait() { - utils::implements_trait(cx, ty, id, &[]) + implements_trait(cx, ty, id, &[]) } else { return; } diff --git a/src/tools/clippy/clippy_lints/src/neg_multiply.rs b/src/tools/clippy/clippy_lints/src/neg_multiply.rs index ef7cc65cfcf0a..34fd012572f4b 100644 --- a/src/tools/clippy/clippy_lints/src/neg_multiply.rs +++ b/src/tools/clippy/clippy_lints/src/neg_multiply.rs @@ -1,3 +1,4 @@ +use clippy_utils::diagnostics::span_lint; use if_chain::if_chain; use rustc_hir::{BinOpKind, Expr, ExprKind, UnOp}; use rustc_lint::{LateContext, LateLintPass}; @@ -5,7 +6,6 @@ use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::source_map::Span; use crate::consts::{self, Constant}; -use crate::utils::span_lint; declare_clippy_lint! { /// **What it does:** Checks for multiplication by -1 as a form of negation. @@ -28,12 +28,12 @@ declare_lint_pass!(NegMultiply => [NEG_MULTIPLY]); #[allow(clippy::match_same_arms)] impl<'tcx> LateLintPass<'tcx> for NegMultiply { fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) { - if let ExprKind::Binary(ref op, ref left, ref right) = e.kind { + if let ExprKind::Binary(ref op, left, right) = e.kind { if BinOpKind::Mul == op.node { match (&left.kind, &right.kind) { (&ExprKind::Unary(..), &ExprKind::Unary(..)) => {}, - (&ExprKind::Unary(UnOp::Neg, ref lit), _) => check_mul(cx, e.span, lit, right), - (_, &ExprKind::Unary(UnOp::Neg, ref lit)) => check_mul(cx, e.span, lit, left), + (&ExprKind::Unary(UnOp::Neg, lit), _) => check_mul(cx, e.span, lit, right), + (_, &ExprKind::Unary(UnOp::Neg, lit)) => check_mul(cx, e.span, lit, left), _ => {}, } } diff --git a/src/tools/clippy/clippy_lints/src/new_without_default.rs b/src/tools/clippy/clippy_lints/src/new_without_default.rs index de2899c3462a4..a5f91eb035f97 100644 --- a/src/tools/clippy/clippy_lints/src/new_without_default.rs +++ b/src/tools/clippy/clippy_lints/src/new_without_default.rs @@ -1,6 +1,8 @@ -use crate::utils::paths; -use crate::utils::sugg::DiagnosticBuilderExt; -use crate::utils::{get_trait_def_id, return_ty, span_lint_hir_and_then}; +use clippy_utils::diagnostics::span_lint_hir_and_then; +use clippy_utils::paths; +use clippy_utils::source::snippet; +use clippy_utils::sugg::DiagnosticBuilderExt; +use clippy_utils::{get_trait_def_id, return_ty}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir as hir; @@ -61,7 +63,10 @@ impl<'tcx> LateLintPass<'tcx> for NewWithoutDefault { #[allow(clippy::too_many_lines)] fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) { if let hir::ItemKind::Impl(hir::Impl { - of_trait: None, items, .. + of_trait: None, + ref generics, + items, + .. }) = item.kind { for assoc_item in items { @@ -92,59 +97,60 @@ impl<'tcx> LateLintPass<'tcx> for NewWithoutDefault { // impl of `Default` return; } - if sig.decl.inputs.is_empty() && name == sym::new && cx.access_levels.is_reachable(id) { + if_chain! { + if sig.decl.inputs.is_empty(); + if name == sym::new; + if cx.access_levels.is_reachable(id); let self_def_id = cx.tcx.hir().local_def_id(cx.tcx.hir().get_parent_item(id)); let self_ty = cx.tcx.type_of(self_def_id); - if_chain! { - if TyS::same_type(self_ty, return_ty(cx, id)); - if let Some(default_trait_id) = get_trait_def_id(cx, &paths::DEFAULT_TRAIT); - then { - if self.impling_types.is_none() { - let mut impls = HirIdSet::default(); - cx.tcx.for_each_impl(default_trait_id, |d| { - if let Some(ty_def) = cx.tcx.type_of(d).ty_adt_def() { - if let Some(local_def_id) = ty_def.did.as_local() { - impls.insert(cx.tcx.hir().local_def_id_to_hir_id(local_def_id)); - } - } - }); - self.impling_types = Some(impls); - } - - // Check if a Default implementation exists for the Self type, regardless of - // generics - if_chain! { - if let Some(ref impling_types) = self.impling_types; - if let Some(self_def) = cx.tcx.type_of(self_def_id).ty_adt_def(); - if let Some(self_local_did) = self_def.did.as_local(); - then { - let self_id = cx.tcx.hir().local_def_id_to_hir_id(self_local_did); - if impling_types.contains(&self_id) { - return; + if TyS::same_type(self_ty, return_ty(cx, id)); + if let Some(default_trait_id) = get_trait_def_id(cx, &paths::DEFAULT_TRAIT); + then { + if self.impling_types.is_none() { + let mut impls = HirIdSet::default(); + cx.tcx.for_each_impl(default_trait_id, |d| { + if let Some(ty_def) = cx.tcx.type_of(d).ty_adt_def() { + if let Some(local_def_id) = ty_def.did.as_local() { + impls.insert(cx.tcx.hir().local_def_id_to_hir_id(local_def_id)); } } - } + }); + self.impling_types = Some(impls); + } - span_lint_hir_and_then( - cx, - NEW_WITHOUT_DEFAULT, - id, - impl_item.span, - &format!( - "you should consider adding a `Default` implementation for `{}`", - self_ty - ), - |diag| { - diag.suggest_prepend_item( - cx, - item.span, - "try this", - &create_new_without_default_suggest_msg(self_ty), - Applicability::MaybeIncorrect, - ); - }, - ); + // Check if a Default implementation exists for the Self type, regardless of + // generics + if_chain! { + if let Some(ref impling_types) = self.impling_types; + if let Some(self_def) = cx.tcx.type_of(self_def_id).ty_adt_def(); + if let Some(self_local_did) = self_def.did.as_local(); + let self_id = cx.tcx.hir().local_def_id_to_hir_id(self_local_did); + if impling_types.contains(&self_id); + then { + return; + } } + + let generics_sugg = snippet(cx, generics.span, ""); + span_lint_hir_and_then( + cx, + NEW_WITHOUT_DEFAULT, + id, + impl_item.span, + &format!( + "you should consider adding a `Default` implementation for `{}`", + self_ty + ), + |diag| { + diag.suggest_prepend_item( + cx, + item.span, + "try this", + &create_new_without_default_suggest_msg(self_ty, &generics_sugg), + Applicability::MaybeIncorrect, + ); + }, + ); } } } @@ -154,12 +160,12 @@ impl<'tcx> LateLintPass<'tcx> for NewWithoutDefault { } } -fn create_new_without_default_suggest_msg(ty: Ty<'_>) -> String { +fn create_new_without_default_suggest_msg(ty: Ty<'_>, generics_sugg: &str) -> String { #[rustfmt::skip] format!( -"impl Default for {} {{ +"impl{} Default for {} {{ fn default() -> Self {{ Self::new() }} -}}", ty) +}}", generics_sugg, ty) } diff --git a/src/tools/clippy/clippy_lints/src/no_effect.rs b/src/tools/clippy/clippy_lints/src/no_effect.rs index 69302d695ce0a..cfcaf5094716b 100644 --- a/src/tools/clippy/clippy_lints/src/no_effect.rs +++ b/src/tools/clippy/clippy_lints/src/no_effect.rs @@ -1,4 +1,6 @@ -use crate::utils::{has_drop, snippet_opt, span_lint, span_lint_and_sugg}; +use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg}; +use clippy_utils::source::snippet_opt; +use clippy_utils::ty::has_drop; use rustc_errors::Applicability; use rustc_hir::def::{DefKind, Res}; use rustc_hir::{BinOpKind, BlockCheckMode, Expr, ExprKind, Stmt, StmtKind, UnsafeSource}; @@ -49,23 +51,21 @@ fn has_no_effect(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { match expr.kind { ExprKind::Lit(..) | ExprKind::Closure(..) => true, ExprKind::Path(..) => !has_drop(cx, cx.typeck_results().expr_ty(expr)), - ExprKind::Index(ref a, ref b) | ExprKind::Binary(_, ref a, ref b) => { - has_no_effect(cx, a) && has_no_effect(cx, b) - }, - ExprKind::Array(ref v) | ExprKind::Tup(ref v) => v.iter().all(|val| has_no_effect(cx, val)), - ExprKind::Repeat(ref inner, _) - | ExprKind::Cast(ref inner, _) - | ExprKind::Type(ref inner, _) - | ExprKind::Unary(_, ref inner) - | ExprKind::Field(ref inner, _) - | ExprKind::AddrOf(_, _, ref inner) - | ExprKind::Box(ref inner) => has_no_effect(cx, inner), - ExprKind::Struct(_, ref fields, ref base) => { + ExprKind::Index(a, b) | ExprKind::Binary(_, a, b) => has_no_effect(cx, a) && has_no_effect(cx, b), + ExprKind::Array(v) | ExprKind::Tup(v) => v.iter().all(|val| has_no_effect(cx, val)), + ExprKind::Repeat(inner, _) + | ExprKind::Cast(inner, _) + | ExprKind::Type(inner, _) + | ExprKind::Unary(_, inner) + | ExprKind::Field(inner, _) + | ExprKind::AddrOf(_, _, inner) + | ExprKind::Box(inner) => has_no_effect(cx, inner), + ExprKind::Struct(_, fields, ref base) => { !has_drop(cx, cx.typeck_results().expr_ty(expr)) - && fields.iter().all(|field| has_no_effect(cx, &field.expr)) + && fields.iter().all(|field| has_no_effect(cx, field.expr)) && base.as_ref().map_or(true, |base| has_no_effect(cx, base)) }, - ExprKind::Call(ref callee, ref args) => { + ExprKind::Call(callee, args) => { if let ExprKind::Path(ref qpath) = callee.kind { let res = cx.qpath_res(qpath, callee.hir_id); match res { @@ -79,7 +79,7 @@ fn has_no_effect(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { false } }, - ExprKind::Block(ref block, _) => { + ExprKind::Block(block, _) => { block.stmts.is_empty() && block.expr.as_ref().map_or(false, |expr| has_no_effect(cx, expr)) }, _ => false, @@ -90,7 +90,7 @@ declare_lint_pass!(NoEffect => [NO_EFFECT, UNNECESSARY_OPERATION]); impl<'tcx> LateLintPass<'tcx> for NoEffect { fn check_stmt(&mut self, cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'_>) { - if let StmtKind::Semi(ref expr) = stmt.kind { + if let StmtKind::Semi(expr) = stmt.kind { if has_no_effect(cx, expr) { span_lint(cx, NO_EFFECT, stmt.span, "statement with no effect"); } else if let Some(reduced) = reduce_expression(cx, expr) { @@ -125,26 +125,26 @@ fn reduce_expression<'a>(cx: &LateContext<'_>, expr: &'a Expr<'a>) -> Option<Vec return None; } match expr.kind { - ExprKind::Index(ref a, ref b) => Some(vec![&**a, &**b]), - ExprKind::Binary(ref binop, ref a, ref b) if binop.node != BinOpKind::And && binop.node != BinOpKind::Or => { - Some(vec![&**a, &**b]) + ExprKind::Index(a, b) => Some(vec![a, b]), + ExprKind::Binary(ref binop, a, b) if binop.node != BinOpKind::And && binop.node != BinOpKind::Or => { + Some(vec![a, b]) }, - ExprKind::Array(ref v) | ExprKind::Tup(ref v) => Some(v.iter().collect()), - ExprKind::Repeat(ref inner, _) - | ExprKind::Cast(ref inner, _) - | ExprKind::Type(ref inner, _) - | ExprKind::Unary(_, ref inner) - | ExprKind::Field(ref inner, _) - | ExprKind::AddrOf(_, _, ref inner) - | ExprKind::Box(ref inner) => reduce_expression(cx, inner).or_else(|| Some(vec![inner])), - ExprKind::Struct(_, ref fields, ref base) => { + ExprKind::Array(v) | ExprKind::Tup(v) => Some(v.iter().collect()), + ExprKind::Repeat(inner, _) + | ExprKind::Cast(inner, _) + | ExprKind::Type(inner, _) + | ExprKind::Unary(_, inner) + | ExprKind::Field(inner, _) + | ExprKind::AddrOf(_, _, inner) + | ExprKind::Box(inner) => reduce_expression(cx, inner).or_else(|| Some(vec![inner])), + ExprKind::Struct(_, fields, ref base) => { if has_drop(cx, cx.typeck_results().expr_ty(expr)) { None } else { Some(fields.iter().map(|f| &f.expr).chain(base).map(Deref::deref).collect()) } }, - ExprKind::Call(ref callee, ref args) => { + ExprKind::Call(callee, args) => { if let ExprKind::Path(ref qpath) = callee.kind { let res = cx.qpath_res(qpath, callee.hir_id); match res { @@ -159,7 +159,7 @@ fn reduce_expression<'a>(cx: &LateContext<'_>, expr: &'a Expr<'a>) -> Option<Vec None } }, - ExprKind::Block(ref block, _) => { + ExprKind::Block(block, _) => { if block.stmts.is_empty() { block.expr.as_ref().and_then(|e| { match block.rules { 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 8aebce67917af..d775cd7c7f740 100644 --- a/src/tools/clippy/clippy_lints/src/non_copy_const.rs +++ b/src/tools/clippy/clippy_lints/src/non_copy_const.rs @@ -4,6 +4,9 @@ use std::ptr; +use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::in_constant; +use if_chain::if_chain; use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::DefId; use rustc_hir::{ @@ -18,9 +21,6 @@ use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::{InnerSpan, Span, DUMMY_SP}; use rustc_typeck::hir_ty_to_ty; -use crate::utils::{in_constant, span_lint_and_then}; -use if_chain::if_chain; - // FIXME: this is a correctness problem but there's no suitable // warn-by-default category. declare_clippy_lint! { @@ -179,9 +179,15 @@ fn is_value_unfrozen_poly<'tcx>(cx: &LateContext<'tcx>, body_id: BodyId, ty: Ty< fn is_value_unfrozen_expr<'tcx>(cx: &LateContext<'tcx>, hir_id: HirId, def_id: DefId, ty: Ty<'tcx>) -> bool { let substs = cx.typeck_results().node_substs(hir_id); - let result = cx - .tcx - .const_eval_resolve(cx.param_env, ty::WithOptConstParam::unknown(def_id), substs, None, None); + let result = cx.tcx.const_eval_resolve( + cx.param_env, + ty::Unevaluated { + def: ty::WithOptConstParam::unknown(def_id), + substs, + promoted: None, + }, + None, + ); is_value_unfrozen_raw(cx, result, ty) } @@ -301,19 +307,17 @@ impl<'tcx> LateLintPass<'tcx> for NonCopyConst { // we should use here as a frozen variant is a potential to be frozen // similar to unknown layouts. // e.g. `layout_of(...).is_err() || has_frozen_variant(...);` + let ty = hir_ty_to_ty(cx.tcx, hir_ty); + let normalized = cx.tcx.normalize_erasing_regions(cx.param_env, ty); + if is_unfrozen(cx, normalized); + if is_value_unfrozen_poly(cx, *body_id, normalized); then { - let ty = hir_ty_to_ty(cx.tcx, hir_ty); - let normalized = cx.tcx.normalize_erasing_regions(cx.param_env, ty); - if is_unfrozen(cx, normalized) - && is_value_unfrozen_poly(cx, *body_id, normalized) - { - lint( - cx, - Source::Assoc { - item: impl_item.span, - }, - ); - } + lint( + cx, + Source::Assoc { + item: impl_item.span, + }, + ); } } }, diff --git a/src/tools/clippy/clippy_lints/src/non_expressive_names.rs b/src/tools/clippy/clippy_lints/src/non_expressive_names.rs index d5222a030d7af..4c8bceaf2cb8b 100644 --- a/src/tools/clippy/clippy_lints/src/non_expressive_names.rs +++ b/src/tools/clippy/clippy_lints/src/non_expressive_names.rs @@ -1,4 +1,4 @@ -use crate::utils::{span_lint, span_lint_and_then}; +use clippy_utils::diagnostics::{span_lint, span_lint_and_then}; use rustc_ast::ast::{ Arm, AssocItem, AssocItemKind, Attribute, Block, FnDecl, FnKind, Item, ItemKind, Local, Pat, PatKind, 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 new file mode 100644 index 0000000000000..a83daea97bf6b --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/non_octal_unix_permissions.rs @@ -0,0 +1,106 @@ +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::source::{snippet_opt, snippet_with_applicability}; +use clippy_utils::ty::match_type; +use clippy_utils::{match_def_path, paths}; +use if_chain::if_chain; +use rustc_errors::Applicability; +use rustc_hir::{Expr, ExprKind}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; + +declare_clippy_lint! { + /// **What it does:** Checks for non-octal values used to set Unix file permissions. + /// + /// **Why is this bad?** They will be converted into octal, creating potentially + /// unintended file permissions. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// + /// ```rust,ignore + /// use std::fs::OpenOptions; + /// use std::os::unix::fs::OpenOptionsExt; + /// + /// let mut options = OpenOptions::new(); + /// options.mode(644); + /// ``` + /// Use instead: + /// ```rust,ignore + /// use std::fs::OpenOptions; + /// use std::os::unix::fs::OpenOptionsExt; + /// + /// let mut options = OpenOptions::new(); + /// options.mode(0o644); + /// ``` + pub NON_OCTAL_UNIX_PERMISSIONS, + correctness, + "use of non-octal value to set unix file permissions, which will be translated into octal" +} + +declare_lint_pass!(NonOctalUnixPermissions => [NON_OCTAL_UNIX_PERMISSIONS]); + +impl LateLintPass<'_> for NonOctalUnixPermissions { + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { + match &expr.kind { + ExprKind::MethodCall(path, _, [func, param], _) => { + let obj_ty = cx.typeck_results().expr_ty(func).peel_refs(); + + if_chain! { + if (path.ident.name == sym!(mode) + && (match_type(cx, obj_ty, &paths::OPEN_OPTIONS) + || match_type(cx, obj_ty, &paths::DIR_BUILDER))) + || (path.ident.name == sym!(set_mode) && match_type(cx, obj_ty, &paths::PERMISSIONS)); + if let ExprKind::Lit(_) = param.kind; + + then { + let snip = match snippet_opt(cx, param.span) { + Some(s) => s, + _ => return, + }; + + if !snip.starts_with("0o") { + show_error(cx, param); + } + } + } + }, + ExprKind::Call(func, [param]) => { + if_chain! { + if let ExprKind::Path(ref path) = func.kind; + if let Some(def_id) = cx.qpath_res(path, func.hir_id).opt_def_id(); + if match_def_path(cx, def_id, &paths::PERMISSIONS_FROM_MODE); + if let ExprKind::Lit(_) = param.kind; + + then { + let snip = match snippet_opt(cx, param.span) { + Some(s) => s, + _ => return, + }; + + if !snip.starts_with("0o") { + show_error(cx, param); + } + } + } + }, + _ => {}, + }; + } +} + +fn show_error(cx: &LateContext<'_>, param: &Expr<'_>) { + let mut applicability = Applicability::MachineApplicable; + span_lint_and_sugg( + cx, + NON_OCTAL_UNIX_PERMISSIONS, + param.span, + "using a non-octal value to set unix file permissions", + "consider using an octal literal instead", + format!( + "0o{}", + snippet_with_applicability(cx, param.span, "0o..", &mut applicability,), + ), + applicability, + ); +} diff --git a/src/tools/clippy/clippy_lints/src/open_options.rs b/src/tools/clippy/clippy_lints/src/open_options.rs index 73a99a3a2f870..9efe45336bfce 100644 --- a/src/tools/clippy/clippy_lints/src/open_options.rs +++ b/src/tools/clippy/clippy_lints/src/open_options.rs @@ -1,4 +1,6 @@ -use crate::utils::{match_type, paths, span_lint}; +use clippy_utils::diagnostics::span_lint; +use clippy_utils::paths; +use clippy_utils::ty::match_type; use rustc_ast::ast::LitKind; use rustc_hir::{Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; @@ -29,7 +31,7 @@ declare_lint_pass!(OpenOptions => [NONSENSICAL_OPEN_OPTIONS]); impl<'tcx> LateLintPass<'tcx> for OpenOptions { fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) { - if let ExprKind::MethodCall(ref path, _, ref arguments, _) = e.kind { + if let ExprKind::MethodCall(path, _, arguments, _) = e.kind { let obj_ty = cx.typeck_results().expr_ty(&arguments[0]).peel_refs(); if path.ident.name == sym!(open) && match_type(cx, obj_ty, &paths::OPEN_OPTIONS) { let mut options = Vec::new(); @@ -57,7 +59,7 @@ enum OpenOption { } fn get_open_options(cx: &LateContext<'_>, argument: &Expr<'_>, options: &mut Vec<(OpenOption, Argument)>) { - if let ExprKind::MethodCall(ref path, _, ref arguments, _) = argument.kind { + if let ExprKind::MethodCall(path, _, arguments, _) = argument.kind { let obj_ty = cx.typeck_results().expr_ty(&arguments[0]).peel_refs(); // Only proceed if this is a call on some object of type std::fs::OpenOptions @@ -69,15 +71,11 @@ fn get_open_options(cx: &LateContext<'_>, argument: &Expr<'_>, options: &mut Vec .. } = *span { - if lit { - Argument::True - } else { - Argument::False - } + if lit { Argument::True } else { Argument::False } } else { - return; // The function is called with a literal - // which is not a boolean literal. This is theoretically - // possible, but not very likely. + // The function is called with a literal which is not a boolean literal. + // This is theoretically possible, but not very likely. + return; } }, _ => Argument::Unknown, diff --git a/src/tools/clippy/clippy_lints/src/option_env_unwrap.rs b/src/tools/clippy/clippy_lints/src/option_env_unwrap.rs index fd653044a1bcb..b6f518661bdb1 100644 --- a/src/tools/clippy/clippy_lints/src/option_env_unwrap.rs +++ b/src/tools/clippy/clippy_lints/src/option_env_unwrap.rs @@ -1,8 +1,10 @@ -use crate::utils::{is_direct_expn_of, span_lint_and_help}; +use clippy_utils::diagnostics::span_lint_and_help; +use clippy_utils::is_direct_expn_of; use if_chain::if_chain; use rustc_ast::ast::{Expr, ExprKind}; use rustc_lint::{EarlyContext, EarlyLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::sym; declare_clippy_lint! { /// **What it does:** Checks for usage of `option_env!(...).unwrap()` and @@ -36,8 +38,7 @@ impl EarlyLintPass for OptionEnvUnwrap { fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) { if_chain! { if let ExprKind::MethodCall(path_segment, args, _) = &expr.kind; - let method_name = path_segment.ident.as_str(); - if method_name == "expect" || method_name == "unwrap"; + if matches!(path_segment.ident.name, sym::expect | sym::unwrap); if let ExprKind::Call(caller, _) = &args[0].kind; if is_direct_expn_of(caller.span, "option_env").is_some(); then { diff --git a/src/tools/clippy/clippy_lints/src/option_if_let_else.rs b/src/tools/clippy/clippy_lints/src/option_if_let_else.rs index 9ef0d267b0b20..1b9120ae45f54 100644 --- a/src/tools/clippy/clippy_lints/src/option_if_let_else.rs +++ b/src/tools/clippy/clippy_lints/src/option_if_let_else.rs @@ -1,9 +1,10 @@ -use crate::utils; -use crate::utils::eager_or_lazy; -use crate::utils::sugg::Sugg; -use crate::utils::{is_type_diagnostic_item, paths, span_lint_and_sugg}; +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::paths; +use clippy_utils::sugg::Sugg; +use clippy_utils::ty::is_type_diagnostic_item; +use clippy_utils::usage::contains_return_break_continue_macro; +use clippy_utils::{eager_or_lazy, get_enclosing_block, in_macro, match_qpath}; use if_chain::if_chain; - use rustc_errors::Applicability; use rustc_hir::{Arm, BindingAnnotation, Block, Expr, ExprKind, MatchSource, Mutability, PatKind, UnOp}; use rustc_lint::{LateContext, LateLintPass}; @@ -65,9 +66,9 @@ declare_lint_pass!(OptionIfLetElse => [OPTION_IF_LET_ELSE]); /// Returns true iff the given expression is the result of calling `Result::ok` fn is_result_ok(cx: &LateContext<'_>, expr: &'_ Expr<'_>) -> bool { - if let ExprKind::MethodCall(ref path, _, &[ref receiver], _) = &expr.kind { + if let ExprKind::MethodCall(path, _, &[ref receiver], _) = &expr.kind { path.ident.name.as_str() == "ok" - && is_type_diagnostic_item(cx, &cx.typeck_results().expr_ty(&receiver), sym::result_type) + && is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(receiver), sym::result_type) } else { false } @@ -96,9 +97,9 @@ fn extract_body_from_arm<'a>(arm: &'a Arm<'a>) -> Option<&'a Expr<'a>> { ) = &arm.body.kind { if let [] = statements { - Some(&expr) + Some(expr) } else { - Some(&arm.body) + Some(arm.body) } } else { None @@ -108,7 +109,7 @@ fn extract_body_from_arm<'a>(arm: &'a Arm<'a>) -> Option<&'a Expr<'a>> { /// If this is the else body of an if/else expression, then we need to wrap /// it in curly braces. Otherwise, we don't. fn should_wrap_in_braces(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { - utils::get_enclosing_block(cx, expr.hir_id).map_or(false, |parent| { + get_enclosing_block(cx, expr.hir_id).map_or(false, |parent| { let mut should_wrap = false; if let Some(Expr { @@ -158,15 +159,15 @@ fn detect_option_if_let_else<'tcx>( expr: &'_ Expr<'tcx>, ) -> Option<OptionIfLetElseOccurence> { if_chain! { - if !utils::in_macro(expr.span); // Don't lint macros, because it behaves weirdly + if !in_macro(expr.span); // Don't lint macros, because it behaves weirdly if let ExprKind::Match(cond_expr, arms, MatchSource::IfLetDesugar{contains_else_clause: true}) = &expr.kind; if arms.len() == 2; if !is_result_ok(cx, cond_expr); // Don't lint on Result::ok because a different lint does it already if let PatKind::TupleStruct(struct_qpath, &[inner_pat], _) = &arms[0].pat.kind; - if utils::match_qpath(struct_qpath, &paths::OPTION_SOME); + if match_qpath(struct_qpath, &paths::OPTION_SOME); if let PatKind::Binding(bind_annotation, _, id, _) = &inner_pat.kind; - if !utils::usage::contains_return_break_continue_macro(arms[0].body); - if !utils::usage::contains_return_break_continue_macro(arms[1].body); + if !contains_return_break_continue_macro(arms[0].body); + if !contains_return_break_continue_macro(arms[1].body); then { let capture_mut = if bind_annotation == &BindingAnnotation::Mutable { "mut " } else { "" }; let some_body = extract_body_from_arm(&arms[0])?; diff --git a/src/tools/clippy/clippy_lints/src/overflow_check_conditional.rs b/src/tools/clippy/clippy_lints/src/overflow_check_conditional.rs index 3c041bac234a5..e222782c2cc8c 100644 --- a/src/tools/clippy/clippy_lints/src/overflow_check_conditional.rs +++ b/src/tools/clippy/clippy_lints/src/overflow_check_conditional.rs @@ -1,4 +1,5 @@ -use crate::utils::{span_lint, SpanlessEq}; +use clippy_utils::diagnostics::span_lint; +use clippy_utils::SpanlessEq; use if_chain::if_chain; use rustc_hir::{BinOpKind, Expr, ExprKind, QPath}; use rustc_lint::{LateContext, LateLintPass}; @@ -30,11 +31,11 @@ impl<'tcx> LateLintPass<'tcx> for OverflowCheckConditional { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { let eq = |l, r| SpanlessEq::new(cx).eq_path_segment(l, r); if_chain! { - if let ExprKind::Binary(ref op, ref first, ref second) = expr.kind; - if let ExprKind::Binary(ref op2, ref ident1, ref ident2) = first.kind; - if let ExprKind::Path(QPath::Resolved(_, ref path1)) = ident1.kind; - if let ExprKind::Path(QPath::Resolved(_, ref path2)) = ident2.kind; - if let ExprKind::Path(QPath::Resolved(_, ref path3)) = second.kind; + if let ExprKind::Binary(ref op, first, second) = expr.kind; + if let ExprKind::Binary(ref op2, ident1, ident2) = first.kind; + if let ExprKind::Path(QPath::Resolved(_, path1)) = ident1.kind; + if let ExprKind::Path(QPath::Resolved(_, path2)) = ident2.kind; + if let ExprKind::Path(QPath::Resolved(_, path3)) = second.kind; if eq(&path1.segments[0], &path3.segments[0]) || eq(&path2.segments[0], &path3.segments[0]); if cx.typeck_results().expr_ty(ident1).is_integral(); if cx.typeck_results().expr_ty(ident2).is_integral(); @@ -55,11 +56,11 @@ impl<'tcx> LateLintPass<'tcx> for OverflowCheckConditional { } if_chain! { - if let ExprKind::Binary(ref op, ref first, ref second) = expr.kind; - if let ExprKind::Binary(ref op2, ref ident1, ref ident2) = second.kind; - if let ExprKind::Path(QPath::Resolved(_, ref path1)) = ident1.kind; - if let ExprKind::Path(QPath::Resolved(_, ref path2)) = ident2.kind; - if let ExprKind::Path(QPath::Resolved(_, ref path3)) = first.kind; + if let ExprKind::Binary(ref op, first, second) = expr.kind; + if let ExprKind::Binary(ref op2, ident1, ident2) = second.kind; + if let ExprKind::Path(QPath::Resolved(_, path1)) = ident1.kind; + if let ExprKind::Path(QPath::Resolved(_, path2)) = ident2.kind; + if let ExprKind::Path(QPath::Resolved(_, path3)) = first.kind; if eq(&path1.segments[0], &path3.segments[0]) || eq(&path2.segments[0], &path3.segments[0]); if cx.typeck_results().expr_ty(ident1).is_integral(); if cx.typeck_results().expr_ty(ident2).is_integral(); 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 37e2b50def17a..d32b937b209c5 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,4 +1,6 @@ -use crate::utils::{find_macro_calls, is_type_diagnostic_item, return_ty, span_lint_and_then}; +use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::ty::is_type_diagnostic_item; +use clippy_utils::{find_macro_calls, return_ty}; use rustc_hir as hir; use rustc_hir::intravisit::FnKind; use rustc_lint::{LateContext, LateLintPass}; @@ -43,9 +45,7 @@ impl<'tcx> LateLintPass<'tcx> for PanicInResultFn { span: Span, hir_id: hir::HirId, ) { - if !matches!(fn_kind, FnKind::Closure(_)) - && is_type_diagnostic_item(cx, return_ty(cx, hir_id), sym::result_type) - { + if !matches!(fn_kind, FnKind::Closure) && is_type_diagnostic_item(cx, return_ty(cx, hir_id), sym::result_type) { lint_impl_body(cx, span, body); } } diff --git a/src/tools/clippy/clippy_lints/src/panic_unimplemented.rs b/src/tools/clippy/clippy_lints/src/panic_unimplemented.rs index 359620cc07975..1e94685894723 100644 --- a/src/tools/clippy/clippy_lints/src/panic_unimplemented.rs +++ b/src/tools/clippy/clippy_lints/src/panic_unimplemented.rs @@ -1,4 +1,5 @@ -use crate::utils::{is_expn_of, match_panic_call, span_lint}; +use clippy_utils::diagnostics::span_lint; +use clippy_utils::{is_expn_of, match_panic_call}; use if_chain::if_chain; use rustc_hir::Expr; use rustc_lint::{LateContext, LateLintPass}; @@ -96,11 +97,10 @@ impl<'tcx> LateLintPass<'tcx> for PanicUnimplemented { fn get_outer_span(expr: &Expr<'_>) -> Span { if_chain! { if expr.span.from_expansion(); - let first = expr.span.ctxt().outer_expn_data(); - if first.call_site.from_expansion(); - let second = first.call_site.ctxt().outer_expn_data(); + let first = expr.span.ctxt().outer_expn_data().call_site; + if first.from_expansion(); then { - second.call_site + first.ctxt().outer_expn_data().call_site } else { expr.span } diff --git a/src/tools/clippy/clippy_lints/src/partialeq_ne_impl.rs b/src/tools/clippy/clippy_lints/src/partialeq_ne_impl.rs index 3d6129aa78d4c..1251ddd9a0273 100644 --- a/src/tools/clippy/clippy_lints/src/partialeq_ne_impl.rs +++ b/src/tools/clippy/clippy_lints/src/partialeq_ne_impl.rs @@ -1,4 +1,5 @@ -use crate::utils::{is_automatically_derived, span_lint_hir}; +use clippy_utils::diagnostics::span_lint_hir; +use clippy_utils::is_automatically_derived; use if_chain::if_chain; use rustc_hir::{Impl, Item, ItemKind}; use rustc_lint::{LateContext, LateLintPass}; @@ -35,7 +36,8 @@ impl<'tcx> LateLintPass<'tcx> for PartialEqNeImpl { fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) { if_chain! { if let ItemKind::Impl(Impl { of_trait: Some(ref trait_ref), items: impl_items, .. }) = item.kind; - if !is_automatically_derived(&*item.attrs); + let attrs = cx.tcx.hir().attrs(item.hir_id()); + if !is_automatically_derived(attrs); if let Some(eq_trait) = cx.tcx.lang_items().eq_trait(); if trait_ref.path.res.def_id() == eq_trait; then { diff --git a/src/tools/clippy/clippy_lints/src/pass_by_ref_or_value.rs b/src/tools/clippy/clippy_lints/src/pass_by_ref_or_value.rs index b9ba32001b513..6f80e447c7ddd 100644 --- a/src/tools/clippy/clippy_lints/src/pass_by_ref_or_value.rs +++ b/src/tools/clippy/clippy_lints/src/pass_by_ref_or_value.rs @@ -1,6 +1,10 @@ use std::cmp; +use std::iter; -use crate::utils::{is_copy, is_self_ty, snippet, span_lint_and_sugg}; +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::is_self_ty; +use clippy_utils::source::snippet; +use clippy_utils::ty::is_copy; use if_chain::if_chain; use rustc_ast::attr; use rustc_errors::Applicability; @@ -119,7 +123,7 @@ impl<'tcx> PassByRefOrValue { let fn_body = cx.enclosing_body.map(|id| cx.tcx.hir().body(id)); - for (index, (input, &ty)) in decl.inputs.iter().zip(fn_sig.inputs()).enumerate() { + for (index, (input, &ty)) in iter::zip(decl.inputs, fn_sig.inputs()).enumerate() { // All spans generated from a proc-macro invocation are the same... match span { Some(s) if s == input.span => return, @@ -138,11 +142,11 @@ impl<'tcx> PassByRefOrValue { }; if_chain! { - if !output_lts.contains(&input_lt); + if !output_lts.contains(input_lt); if is_copy(cx, ty); if let Some(size) = cx.layout_of(ty).ok().map(|l| l.size.bytes()); if size <= self.ref_min_size; - if let hir::TyKind::Rptr(_, MutTy { ty: ref decl_ty, .. }) = input.kind; + if let hir::TyKind::Rptr(_, MutTy { ty: decl_ty, .. }) = input.kind; then { let value_type = if is_self_ty(decl_ty) { "self".into() @@ -224,10 +228,11 @@ impl<'tcx> LateLintPass<'tcx> for PassByRefOrValue { } match kind { - FnKind::ItemFn(.., header, _, attrs) => { + FnKind::ItemFn(.., header, _) => { if header.abi != Abi::Rust { return; } + let attrs = cx.tcx.hir().attrs(hir_id); for a in attrs { if let Some(meta_items) = a.meta_item_list() { if a.has_name(sym::proc_macro_derive) @@ -239,7 +244,7 @@ impl<'tcx> LateLintPass<'tcx> for PassByRefOrValue { } }, FnKind::Method(..) => (), - FnKind::Closure(..) => return, + FnKind::Closure => return, } // Exclude non-inherent impls diff --git a/src/tools/clippy/clippy_lints/src/path_buf_push_overwrite.rs b/src/tools/clippy/clippy_lints/src/path_buf_push_overwrite.rs index 6eeb031d383c8..0024592638104 100644 --- a/src/tools/clippy/clippy_lints/src/path_buf_push_overwrite.rs +++ b/src/tools/clippy/clippy_lints/src/path_buf_push_overwrite.rs @@ -1,10 +1,12 @@ -use crate::utils::{match_type, paths, span_lint_and_sugg}; +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::ty::is_type_diagnostic_item; use if_chain::if_chain; use rustc_ast::ast::LitKind; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::symbol::sym; use std::path::{Component, Path}; declare_clippy_lint! { @@ -43,10 +45,10 @@ declare_lint_pass!(PathBufPushOverwrite => [PATH_BUF_PUSH_OVERWRITE]); impl<'tcx> LateLintPass<'tcx> for PathBufPushOverwrite { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { if_chain! { - if let ExprKind::MethodCall(ref path, _, ref args, _) = expr.kind; + if let ExprKind::MethodCall(path, _, args, _) = expr.kind; if path.ident.name == sym!(push); if args.len() == 2; - if match_type(cx, cx.typeck_results().expr_ty(&args[0]).peel_refs(), &paths::PATH_BUF); + if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(&args[0]).peel_refs(), sym::PathBuf); if let Some(get_index_arg) = args.get(1); if let ExprKind::Lit(ref lit) = get_index_arg.kind; if let LitKind::Str(ref path_lit, _) = lit.node; diff --git a/src/tools/clippy/clippy_lints/src/pattern_type_mismatch.rs b/src/tools/clippy/clippy_lints/src/pattern_type_mismatch.rs index 5539331d0460b..9bab783998aa5 100644 --- a/src/tools/clippy/clippy_lints/src/pattern_type_mismatch.rs +++ b/src/tools/clippy/clippy_lints/src/pattern_type_mismatch.rs @@ -1,6 +1,7 @@ -use crate::utils::{last_path_segment, span_lint_and_help}; +use clippy_utils::diagnostics::span_lint_and_help; +use clippy_utils::last_path_segment; use rustc_hir::{ - intravisit, Body, Expr, ExprKind, FieldPat, FnDecl, HirId, LocalSource, MatchSource, Mutability, Pat, PatKind, + intravisit, Body, Expr, ExprKind, FnDecl, HirId, LocalSource, MatchSource, Mutability, Pat, PatField, PatKind, QPath, Stmt, StmtKind, }; use rustc_lint::{LateContext, LateLintPass, LintContext}; @@ -9,6 +10,7 @@ use rustc_middle::ty::subst::SubstsRef; use rustc_middle::ty::{AdtDef, FieldDef, Ty, TyKind, VariantDef}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::source_map::Span; +use std::iter; declare_clippy_lint! { /// **What it does:** Checks for patterns that aren't exact representations of the types @@ -85,7 +87,7 @@ declare_lint_pass!(PatternTypeMismatch => [PATTERN_TYPE_MISMATCH]); impl<'tcx> LateLintPass<'tcx> for PatternTypeMismatch { fn check_stmt(&mut self, cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'_>) { - if let StmtKind::Local(ref local) = stmt.kind { + if let StmtKind::Local(local) = stmt.kind { if let Some(init) = &local.init { if let Some(init_ty) = cx.typeck_results().node_type_opt(init.hir_id) { let pat = &local.pat; @@ -103,7 +105,7 @@ impl<'tcx> LateLintPass<'tcx> for PatternTypeMismatch { } fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - if let ExprKind::Match(ref expr, arms, source) = expr.kind { + if let ExprKind::Match(expr, arms, source) = expr.kind { match source { MatchSource::Normal | MatchSource::IfLetDesugar { .. } | MatchSource::WhileLetDesugar => { if let Some(expr_ty) = cx.typeck_results().node_type_opt(expr.hir_id) { @@ -133,8 +135,8 @@ impl<'tcx> LateLintPass<'tcx> for PatternTypeMismatch { hir_id: HirId, ) { if let Some(fn_sig) = cx.typeck_results().liberated_fn_sigs().get(hir_id) { - for (param, ty) in body.params.iter().zip(fn_sig.inputs().iter()) { - apply_lint(cx, ¶m.pat, ty, DerefPossible::Impossible); + for (param, ty) in iter::zip(body.params, fn_sig.inputs()) { + apply_lint(cx, param.pat, ty, DerefPossible::Impossible); } } } @@ -186,7 +188,7 @@ fn find_first_mismatch<'tcx>( ty: Ty<'tcx>, level: Level, ) -> Option<(Span, Mutability, Level)> { - if let PatKind::Ref(ref sub_pat, _) = pat.kind { + if let PatKind::Ref(sub_pat, _) = pat.kind { if let TyKind::Ref(_, sub_ty, _) = ty.kind() { return find_first_mismatch(cx, sub_pat, sub_ty, Level::Lower); } @@ -198,8 +200,8 @@ fn find_first_mismatch<'tcx>( } } - if let PatKind::Struct(ref qpath, ref field_pats, _) = pat.kind { - if let TyKind::Adt(ref adt_def, ref substs_ref) = ty.kind() { + if let PatKind::Struct(ref qpath, field_pats, _) = pat.kind { + if let TyKind::Adt(adt_def, substs_ref) = ty.kind() { if let Some(variant) = get_variant(adt_def, qpath) { let field_defs = &variant.fields; return find_first_mismatch_in_struct(cx, field_pats, field_defs, substs_ref); @@ -207,8 +209,8 @@ fn find_first_mismatch<'tcx>( } } - if let PatKind::TupleStruct(ref qpath, ref pats, _) = pat.kind { - if let TyKind::Adt(ref adt_def, ref substs_ref) = ty.kind() { + if let PatKind::TupleStruct(ref qpath, pats, _) = pat.kind { + if let TyKind::Adt(adt_def, substs_ref) = ty.kind() { if let Some(variant) = get_variant(adt_def, qpath) { let field_defs = &variant.fields; let ty_iter = field_defs.iter().map(|field_def| field_def.ty(cx.tcx, substs_ref)); @@ -217,7 +219,7 @@ fn find_first_mismatch<'tcx>( } } - if let PatKind::Tuple(ref pats, _) = pat.kind { + if let PatKind::Tuple(pats, _) = pat.kind { if let TyKind::Tuple(..) = ty.kind() { return find_first_mismatch_in_tuple(cx, pats, ty.tuple_fields()); } @@ -281,7 +283,7 @@ where fn find_first_mismatch_in_struct<'tcx>( cx: &LateContext<'tcx>, - field_pats: &[FieldPat<'_>], + field_pats: &[PatField<'_>], field_defs: &[FieldDef], substs_ref: SubstsRef<'tcx>, ) -> Option<(Span, Mutability, Level)> { diff --git a/src/tools/clippy/clippy_lints/src/precedence.rs b/src/tools/clippy/clippy_lints/src/precedence.rs index c9d18c3cb7287..9cf00c953b95f 100644 --- a/src/tools/clippy/clippy_lints/src/precedence.rs +++ b/src/tools/clippy/clippy_lints/src/precedence.rs @@ -1,4 +1,5 @@ -use crate::utils::{snippet_with_applicability, span_lint_and_sugg}; +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::source::snippet_with_applicability; use if_chain::if_chain; use rustc_ast::ast::{BinOpKind, Expr, ExprKind, LitKind, UnOp}; use rustc_errors::Applicability; diff --git a/src/tools/clippy/clippy_lints/src/ptr.rs b/src/tools/clippy/clippy_lints/src/ptr.rs index de2fb8decb715..09fcdb5faf842 100644 --- a/src/tools/clippy/clippy_lints/src/ptr.rs +++ b/src/tools/clippy/clippy_lints/src/ptr.rs @@ -1,10 +1,10 @@ //! Checks for usage of `&Vec[_]` and `&String`. -use crate::utils::ptr::get_spans; -use crate::utils::{ - is_allowed, is_type_diagnostic_item, match_qpath, match_type, paths, snippet_opt, span_lint, span_lint_and_sugg, - span_lint_and_then, walk_ptrs_hir_ty, -}; +use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg, span_lint_and_then}; +use clippy_utils::ptr::get_spans; +use clippy_utils::source::snippet_opt; +use clippy_utils::ty::{is_type_diagnostic_item, match_type, walk_ptrs_hir_ty}; +use clippy_utils::{is_allowed, match_qpath, paths}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::{ @@ -124,7 +124,7 @@ declare_lint_pass!(Ptr => [PTR_ARG, CMP_NULL, MUT_FROM_REF]); impl<'tcx> LateLintPass<'tcx> for Ptr { fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) { if let ItemKind::Fn(ref sig, _, body_id) = item.kind { - check_fn(cx, &sig.decl, item.hir_id(), Some(body_id)); + check_fn(cx, sig.decl, item.hir_id(), Some(body_id)); } } @@ -136,7 +136,7 @@ impl<'tcx> LateLintPass<'tcx> for Ptr { return; // ignore trait impls } } - check_fn(cx, &sig.decl, item.hir_id(), Some(body_id)); + check_fn(cx, sig.decl, item.hir_id(), Some(body_id)); } } @@ -147,12 +147,12 @@ impl<'tcx> LateLintPass<'tcx> for Ptr { } else { None }; - check_fn(cx, &sig.decl, item.hir_id(), body_id); + check_fn(cx, sig.decl, item.hir_id(), body_id); } } fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - if let ExprKind::Binary(ref op, ref l, ref r) = expr.kind { + if let ExprKind::Binary(ref op, l, r) = expr.kind { if (op.node == BinOpKind::Eq || op.node == BinOpKind::Ne) && (is_null_path(l) || is_null_path(r)) { span_lint( cx, @@ -188,7 +188,7 @@ fn check_fn(cx: &LateContext<'_>, decl: &FnDecl<'_>, fn_id: HirId, opt_body_id: PTR_ARG, arg.span, "writing `&Vec<_>` instead of `&[_]` involves one more reference and cannot be used \ - with non-Vec-based slices.", + with non-Vec-based slices", |diag| { if let Some(ref snippet) = get_only_generic_arg_snippet(cx, arg) { diag.span_suggestion( @@ -217,7 +217,7 @@ fn check_fn(cx: &LateContext<'_>, decl: &FnDecl<'_>, fn_id: HirId, opt_body_id: cx, PTR_ARG, arg.span, - "writing `&String` instead of `&str` involves a new object where a slice will do.", + "writing `&String` instead of `&str` involves a new object where a slice will do", |diag| { diag.span_suggestion(arg.span, "change this to", "&str".into(), Applicability::Unspecified); for (clonespan, suggestion) in spans { @@ -233,13 +233,13 @@ fn check_fn(cx: &LateContext<'_>, decl: &FnDecl<'_>, fn_id: HirId, opt_body_id: }, ); } - } else if match_type(cx, ty, &paths::PATH_BUF) { + } else if is_type_diagnostic_item(cx, ty, sym::PathBuf) { if let Some(spans) = get_spans(cx, opt_body_id, idx, &[("clone", ".to_path_buf()"), ("as_path", "")]) { span_lint_and_then( cx, PTR_ARG, arg.span, - "writing `&PathBuf` instead of `&Path` involves a new object where a slice will do.", + "writing `&PathBuf` instead of `&Path` involves a new object where a slice will do", |diag| { diag.span_suggestion( arg.span, @@ -262,35 +262,34 @@ fn check_fn(cx: &LateContext<'_>, decl: &FnDecl<'_>, fn_id: HirId, opt_body_id: } } else if match_type(cx, ty, &paths::COW) { if_chain! { - if let TyKind::Rptr(_, MutTy { ref ty, ..} ) = arg.kind; - if let TyKind::Path(QPath::Resolved(None, ref pp)) = ty.kind; + if let TyKind::Rptr(_, MutTy { ty, ..} ) = arg.kind; + if let TyKind::Path(QPath::Resolved(None, pp)) = ty.kind; if let [ref bx] = *pp.segments; - if let Some(ref params) = bx.args; + if let Some(params) = bx.args; if !params.parenthesized; if let Some(inner) = params.args.iter().find_map(|arg| match arg { GenericArg::Type(ty) => Some(ty), _ => None, }); + let replacement = snippet_opt(cx, inner.span); + if let Some(r) = replacement; then { - let replacement = snippet_opt(cx, inner.span); - if let Some(r) = replacement { - span_lint_and_sugg( - cx, - PTR_ARG, - arg.span, - "using a reference to `Cow` is not recommended.", - "change this to", - "&".to_owned() + &r, - Applicability::Unspecified, - ); - } + span_lint_and_sugg( + cx, + PTR_ARG, + arg.span, + "using a reference to `Cow` is not recommended", + "change this to", + "&".to_owned() + &r, + Applicability::Unspecified, + ); } } } } } - if let FnRetTy::Return(ref ty) = decl.output { + if let FnRetTy::Return(ty) = decl.output { if let Some((out, Mutability::Mut, _)) = get_rptr_lm(ty) { let mut immutables = vec![]; for (_, ref mutbl, ref argspan) in decl @@ -323,8 +322,8 @@ fn check_fn(cx: &LateContext<'_>, decl: &FnDecl<'_>, fn_id: HirId, opt_body_id: fn get_only_generic_arg_snippet(cx: &LateContext<'_>, arg: &Ty<'_>) -> Option<String> { if_chain! { - if let TyKind::Path(QPath::Resolved(_, ref path)) = walk_ptrs_hir_ty(arg).kind; - if let Some(&PathSegment{args: Some(ref parameters), ..}) = path.segments.last(); + if let TyKind::Path(QPath::Resolved(_, path)) = walk_ptrs_hir_ty(arg).kind; + if let Some(&PathSegment{args: Some(parameters), ..}) = path.segments.last(); let types: Vec<_> = parameters.args.iter().filter_map(|arg| match arg { GenericArg::Type(ty) => Some(ty), _ => None, @@ -347,7 +346,7 @@ fn get_rptr_lm<'tcx>(ty: &'tcx Ty<'tcx>) -> Option<(&'tcx Lifetime, Mutability, } fn is_null_path(expr: &Expr<'_>) -> bool { - if let ExprKind::Call(ref pathexp, ref args) = expr.kind { + if let ExprKind::Call(pathexp, args) = expr.kind { if args.is_empty() { if let ExprKind::Path(ref path) = pathexp.kind { return match_qpath(path, &paths::PTR_NULL) || match_qpath(path, &paths::PTR_NULL_MUT); diff --git a/src/tools/clippy/clippy_lints/src/ptr_eq.rs b/src/tools/clippy/clippy_lints/src/ptr_eq.rs index 3be792ce5e4fa..77cfa3f6b176c 100644 --- a/src/tools/clippy/clippy_lints/src/ptr_eq.rs +++ b/src/tools/clippy/clippy_lints/src/ptr_eq.rs @@ -1,4 +1,6 @@ -use crate::utils; +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::in_macro; +use clippy_utils::source::snippet_opt; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::{BinOpKind, Expr, ExprKind}; @@ -40,24 +42,24 @@ static LINT_MSG: &str = "use `std::ptr::eq` when comparing raw pointers"; impl LateLintPass<'_> for PtrEq { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - if utils::in_macro(expr.span) { + if in_macro(expr.span) { return; } - if let ExprKind::Binary(ref op, ref left, ref right) = expr.kind { + if let ExprKind::Binary(ref op, left, right) = expr.kind { if BinOpKind::Eq == op.node { let (left, right) = match (expr_as_cast_to_usize(cx, left), expr_as_cast_to_usize(cx, right)) { (Some(lhs), Some(rhs)) => (lhs, rhs), - _ => (&**left, &**right), + _ => (left, right), }; if_chain! { if let Some(left_var) = expr_as_cast_to_raw_pointer(cx, left); if let Some(right_var) = expr_as_cast_to_raw_pointer(cx, right); - if let Some(left_snip) = utils::snippet_opt(cx, left_var.span); - if let Some(right_snip) = utils::snippet_opt(cx, right_var.span); + if let Some(left_snip) = snippet_opt(cx, left_var.span); + if let Some(right_snip) = snippet_opt(cx, right_var.span); then { - utils::span_lint_and_sugg( + span_lint_and_sugg( cx, PTR_EQ, expr.span, @@ -77,7 +79,7 @@ impl LateLintPass<'_> for PtrEq { // E.g., `foo as *const _ as usize` returns `foo as *const _`. fn expr_as_cast_to_usize<'tcx>(cx: &LateContext<'tcx>, cast_expr: &'tcx Expr<'_>) -> Option<&'tcx Expr<'tcx>> { if cx.typeck_results().expr_ty(cast_expr) == cx.tcx.types.usize { - if let ExprKind::Cast(ref expr, _) = cast_expr.kind { + if let ExprKind::Cast(expr, _) = cast_expr.kind { return Some(expr); } } @@ -88,7 +90,7 @@ fn expr_as_cast_to_usize<'tcx>(cx: &LateContext<'tcx>, cast_expr: &'tcx Expr<'_> // E.g., `foo as *const _` returns `foo`. fn expr_as_cast_to_raw_pointer<'tcx>(cx: &LateContext<'tcx>, cast_expr: &'tcx Expr<'_>) -> Option<&'tcx Expr<'tcx>> { if cx.typeck_results().expr_ty(cast_expr).is_unsafe_ptr() { - if let ExprKind::Cast(ref expr, _) = cast_expr.kind { + if let ExprKind::Cast(expr, _) = cast_expr.kind { return Some(expr); } } diff --git a/src/tools/clippy/clippy_lints/src/ptr_offset_with_cast.rs b/src/tools/clippy/clippy_lints/src/ptr_offset_with_cast.rs index e0996804a5934..afb198f49559a 100644 --- a/src/tools/clippy/clippy_lints/src/ptr_offset_with_cast.rs +++ b/src/tools/clippy/clippy_lints/src/ptr_offset_with_cast.rs @@ -1,4 +1,5 @@ -use crate::utils::{snippet_opt, span_lint, span_lint_and_sugg}; +use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg}; +use clippy_utils::source::snippet_opt; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; @@ -77,8 +78,8 @@ impl<'tcx> LateLintPass<'tcx> for PtrOffsetWithCast { // If the given expression is a cast from a usize, return the lhs of the cast fn expr_as_cast_from_usize<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> Option<&'tcx Expr<'tcx>> { - if let ExprKind::Cast(ref cast_lhs_expr, _) = expr.kind { - if is_expr_ty_usize(cx, &cast_lhs_expr) { + if let ExprKind::Cast(cast_lhs_expr, _) = expr.kind { + if is_expr_ty_usize(cx, cast_lhs_expr) { return Some(cast_lhs_expr); } } @@ -91,7 +92,7 @@ fn expr_as_ptr_offset_call<'tcx>( cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, ) -> Option<(&'tcx Expr<'tcx>, &'tcx Expr<'tcx>, Method)> { - if let ExprKind::MethodCall(ref path_segment, _, ref args, _) = expr.kind { + if let ExprKind::MethodCall(path_segment, _, args, _) = expr.kind { if is_expr_ty_raw_ptr(cx, &args[0]) { if path_segment.ident.name == sym::offset { return Some((&args[0], &args[1], Method::Offset)); diff --git a/src/tools/clippy/clippy_lints/src/question_mark.rs b/src/tools/clippy/clippy_lints/src/question_mark.rs index 6c480d48c7561..6d720f43851a0 100644 --- a/src/tools/clippy/clippy_lints/src/question_mark.rs +++ b/src/tools/clippy/clippy_lints/src/question_mark.rs @@ -1,3 +1,8 @@ +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::source::snippet_with_applicability; +use clippy_utils::sugg::Sugg; +use clippy_utils::ty::is_type_diagnostic_item; +use clippy_utils::{eq_expr_value, match_def_path, match_qpath, paths}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::def::{DefKind, Res}; @@ -6,12 +11,6 @@ use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::sym; -use crate::utils::sugg::Sugg; -use crate::utils::{ - eq_expr_value, is_type_diagnostic_item, match_def_path, match_qpath, paths, snippet_with_applicability, - span_lint_and_sugg, -}; - declare_clippy_lint! { /// **What it does:** Checks for expressions that could be replaced by the question mark operator. /// @@ -149,14 +148,14 @@ impl QuestionMark { fn expression_returns_none(cx: &LateContext<'_>, expression: &Expr<'_>) -> bool { match expression.kind { - ExprKind::Block(ref block, _) => { + ExprKind::Block(block, _) => { if let Some(return_expression) = Self::return_expression(block) { - return Self::expression_returns_none(cx, &return_expression); + return Self::expression_returns_none(cx, return_expression); } false }, - ExprKind::Ret(Some(ref expr)) => Self::expression_returns_none(cx, expr), + ExprKind::Ret(Some(expr)) => Self::expression_returns_none(cx, expr), ExprKind::Path(ref qp) => { if let Res::Def(DefKind::Ctor(def::CtorOf::Variant, def::CtorKind::Const), def_id) = cx.qpath_res(qp, expression.hir_id) @@ -175,7 +174,7 @@ impl QuestionMark { if_chain! { if block.stmts.len() == 1; if let Some(expr) = block.stmts.iter().last(); - if let StmtKind::Semi(ref expr) = expr.kind; + if let StmtKind::Semi(expr) = expr.kind; if let ExprKind::Ret(Some(ret_expr)) = expr.kind; then { diff --git a/src/tools/clippy/clippy_lints/src/ranges.rs b/src/tools/clippy/clippy_lints/src/ranges.rs index 59503817c0fcc..1c3c125e57913 100644 --- a/src/tools/clippy/clippy_lints/src/ranges.rs +++ b/src/tools/clippy/clippy_lints/src/ranges.rs @@ -1,4 +1,9 @@ use crate::consts::{constant, Constant}; +use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg, span_lint_and_then}; +use clippy_utils::source::{snippet, snippet_opt, snippet_with_applicability}; +use clippy_utils::sugg::Sugg; +use clippy_utils::{get_parent_expr, in_constant, is_integer_const, meets_msrv, single_segment_path}; +use clippy_utils::{higher, SpanlessEq}; use if_chain::if_chain; use rustc_ast::ast::RangeLimits; use rustc_errors::Applicability; @@ -12,13 +17,6 @@ use rustc_span::sym; use rustc_span::symbol::Ident; use std::cmp::Ordering; -use crate::utils::sugg::Sugg; -use crate::utils::{ - get_parent_expr, in_constant, is_integer_const, meets_msrv, single_segment_path, snippet, snippet_opt, - snippet_with_applicability, span_lint, span_lint_and_sugg, span_lint_and_then, -}; -use crate::utils::{higher, SpanlessEq}; - declare_clippy_lint! { /// **What it does:** Checks for zipping a collection with the range of /// `0.._.len()`. @@ -185,10 +183,10 @@ impl_lint_pass!(Ranges => [ impl<'tcx> LateLintPass<'tcx> for Ranges { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { match expr.kind { - ExprKind::MethodCall(ref path, _, ref args, _) => { + ExprKind::MethodCall(path, _, args, _) => { check_range_zip_with_len(cx, path, args, expr.span); }, - ExprKind::Binary(ref op, ref l, ref r) => { + ExprKind::Binary(ref op, l, r) => { if meets_msrv(self.msrv.as_ref(), &MANUAL_RANGE_CONTAINS_MSRV) { check_possible_range_contains(cx, op.node, l, r, expr); } @@ -289,7 +287,7 @@ fn check_possible_range_contains(cx: &LateContext<'_>, op: BinOpKind, l: &Expr<' } fn check_range_bounds(cx: &LateContext<'_>, ex: &Expr<'_>) -> Option<(Constant, Ident, Span, Span, Ordering, bool)> { - if let ExprKind::Binary(ref op, ref l, ref r) = ex.kind { + if let ExprKind::Binary(ref op, l, r) = ex.kind { let (inclusive, ordering) = match op.node { BinOpKind::Gt => (false, Ordering::Greater), BinOpKind::Ge => (true, Ordering::Greater), @@ -322,32 +320,29 @@ fn match_ident(e: &Expr<'_>) -> Option<Ident> { } fn check_range_zip_with_len(cx: &LateContext<'_>, path: &PathSegment<'_>, args: &[Expr<'_>], span: Span) { - let name = path.ident.as_str(); - if name == "zip" && args.len() == 2 { - let iter = &args[0].kind; - let zip_arg = &args[1]; - if_chain! { - // `.iter()` call - if let ExprKind::MethodCall(ref iter_path, _, ref iter_args, _) = *iter; - if iter_path.ident.name == sym::iter; - // range expression in `.zip()` call: `0..x.len()` - if let Some(higher::Range { start: Some(start), end: Some(end), .. }) = higher::range(zip_arg); - if is_integer_const(cx, start, 0); - // `.len()` call - if let ExprKind::MethodCall(ref len_path, _, ref len_args, _) = end.kind; - if len_path.ident.name == sym!(len) && len_args.len() == 1; - // `.iter()` and `.len()` called on same `Path` - if let ExprKind::Path(QPath::Resolved(_, ref iter_path)) = iter_args[0].kind; - if let ExprKind::Path(QPath::Resolved(_, ref len_path)) = len_args[0].kind; - if SpanlessEq::new(cx).eq_path_segments(&iter_path.segments, &len_path.segments); - then { - span_lint(cx, - RANGE_ZIP_WITH_LEN, - span, - &format!("it is more idiomatic to use `{}.iter().enumerate()`", - snippet(cx, iter_args[0].span, "_")) - ); - } + if_chain! { + if path.ident.as_str() == "zip"; + if let [iter, zip_arg] = args; + // `.iter()` call + if let ExprKind::MethodCall(iter_path, _, iter_args, _) = iter.kind; + if iter_path.ident.name == sym::iter; + // range expression in `.zip()` call: `0..x.len()` + if let Some(higher::Range { start: Some(start), end: Some(end), .. }) = higher::range(zip_arg); + if is_integer_const(cx, start, 0); + // `.len()` call + if let ExprKind::MethodCall(len_path, _, len_args, _) = end.kind; + if len_path.ident.name == sym!(len) && len_args.len() == 1; + // `.iter()` and `.len()` called on same `Path` + if let ExprKind::Path(QPath::Resolved(_, iter_path)) = iter_args[0].kind; + if let ExprKind::Path(QPath::Resolved(_, len_path)) = len_args[0].kind; + if SpanlessEq::new(cx).eq_path_segments(iter_path.segments, len_path.segments); + then { + span_lint(cx, + RANGE_ZIP_WITH_LEN, + span, + &format!("it is more idiomatic to use `{}.iter().enumerate()`", + snippet(cx, iter_args[0].span, "_")) + ); } } } @@ -513,8 +508,8 @@ fn y_plus_one<'t>(cx: &LateContext<'_>, expr: &'t Expr<'_>) -> Option<&'t Expr<' Spanned { node: BinOpKind::Add, .. }, - ref lhs, - ref rhs, + lhs, + rhs, ) => { if is_integer_const(cx, lhs, 1) { Some(rhs) @@ -534,8 +529,8 @@ fn y_minus_one<'t>(cx: &LateContext<'_>, expr: &'t Expr<'_>) -> Option<&'t Expr< Spanned { node: BinOpKind::Sub, .. }, - ref lhs, - ref rhs, + lhs, + rhs, ) if is_integer_const(cx, rhs, 1) => Some(lhs), _ => None, } diff --git a/src/tools/clippy/clippy_lints/src/redundant_clone.rs b/src/tools/clippy/clippy_lints/src/redundant_clone.rs index 06adbb523d706..7dafce60d5eda 100644 --- a/src/tools/clippy/clippy_lints/src/redundant_clone.rs +++ b/src/tools/clippy/clippy_lints/src/redundant_clone.rs @@ -1,7 +1,7 @@ -use crate::utils::{ - fn_has_unsatisfiable_preds, has_drop, is_copy, is_type_diagnostic_item, match_def_path, match_type, paths, - snippet_opt, span_lint_hir, span_lint_hir_and_then, walk_ptrs_ty_depth, -}; +use clippy_utils::diagnostics::{span_lint_hir, span_lint_hir_and_then}; +use clippy_utils::source::snippet_opt; +use clippy_utils::ty::{has_drop, is_copy, is_type_diagnostic_item, walk_ptrs_ty_depth}; +use clippy_utils::{fn_has_unsatisfiable_preds, match_def_path, paths}; use if_chain::if_chain; use rustc_data_structures::{fx::FxHashMap, transitive_relation::TransitiveRelation}; use rustc_errors::Applicability; @@ -94,7 +94,7 @@ impl<'tcx> LateLintPass<'tcx> for RedundantClone { .into_results_cursor(mir); let mut possible_borrower = { let mut vis = PossibleBorrowerVisitor::new(cx, mir); - vis.visit_body(&mir); + vis.visit_body(mir); vis.into_map(cx, maybe_storage_live_result) }; @@ -126,7 +126,7 @@ impl<'tcx> LateLintPass<'tcx> for RedundantClone { continue; } - if let ty::Adt(ref def, _) = arg_ty.kind() { + if let ty::Adt(def, _) = arg_ty.kind() { if match_def_path(cx, def.did, &paths::MEM_MANUALLY_DROP) { continue; } @@ -165,9 +165,9 @@ impl<'tcx> LateLintPass<'tcx> for RedundantClone { if let Some((pred_fn_def_id, pred_arg, pred_arg_ty, res)) = is_call_with_ref_arg(cx, mir, &pred_terminator.kind); if res == cloned; - if match_def_path(cx, pred_fn_def_id, &paths::DEREF_TRAIT_METHOD); - if match_type(cx, pred_arg_ty, &paths::PATH_BUF) - || match_type(cx, pred_arg_ty, &paths::OS_STRING); + if cx.tcx.is_diagnostic_item(sym::deref_method, pred_fn_def_id); + if is_type_diagnostic_item(cx, pred_arg_ty, sym::PathBuf) + || is_type_diagnostic_item(cx, pred_arg_ty, sym::OsString); then { (pred_arg, res) } else { @@ -199,79 +199,72 @@ impl<'tcx> LateLintPass<'tcx> for RedundantClone { (local, deref_clone_ret) }; - let is_temp = mir.local_kind(ret_local) == mir::LocalKind::Temp; - - // 1. `local` can be moved out if it is not used later. - // 2. If `ret_local` is a temporary and is neither consumed nor mutated, we can remove this `clone` - // call anyway. - let (used, consumed_or_mutated) = traversal::ReversePostorder::new(&mir, bb).skip(1).fold( - (false, !is_temp), - |(used, consumed), (tbb, tdata)| { - // Short-circuit - if (used && consumed) || - // Give up on loops - tdata.terminator().successors().any(|s| *s == bb) - { - return (true, true); + let clone_usage = if local == ret_local { + CloneUsage { + cloned_used: false, + cloned_consume_or_mutate_loc: None, + clone_consumed_or_mutated: true, + } + } else { + let clone_usage = visit_clone_usage(local, ret_local, mir, bb); + if clone_usage.cloned_used && clone_usage.clone_consumed_or_mutated { + // cloned value is used, and the clone is modified or moved + 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) { + continue; } + } + clone_usage + }; - let mut vis = LocalUseVisitor { - used: (local, false), - consumed_or_mutated: (ret_local, false), - }; - vis.visit_basic_block_data(tbb, tdata); - (used || vis.used.1, consumed || vis.consumed_or_mutated.1) - }, - ); - - if !used || !consumed_or_mutated { - let span = terminator.source_info.span; - let scope = terminator.source_info.scope; - let node = mir.source_scopes[scope] - .local_data - .as_ref() - .assert_crate_local() - .lint_root; - - if_chain! { - if let Some(snip) = snippet_opt(cx, span); - if let Some(dot) = snip.rfind('.'); - then { - let sugg_span = span.with_lo( - span.lo() + BytePos(u32::try_from(dot).unwrap()) - ); - let mut app = Applicability::MaybeIncorrect; - - let call_snip = &snip[dot + 1..]; - // Machine applicable when `call_snip` looks like `foobar()` - if let Some(call_snip) = call_snip.strip_suffix("()").map(str::trim) { - if call_snip.as_bytes().iter().all(|b| b.is_ascii_alphabetic() || *b == b'_') { - app = Applicability::MachineApplicable; - } + let span = terminator.source_info.span; + let scope = terminator.source_info.scope; + let node = mir.source_scopes[scope] + .local_data + .as_ref() + .assert_crate_local() + .lint_root; + + if_chain! { + if let Some(snip) = snippet_opt(cx, span); + if let Some(dot) = snip.rfind('.'); + then { + let sugg_span = span.with_lo( + span.lo() + BytePos(u32::try_from(dot).unwrap()) + ); + let mut app = Applicability::MaybeIncorrect; + + let call_snip = &snip[dot + 1..]; + // Machine applicable when `call_snip` looks like `foobar()` + if let Some(call_snip) = call_snip.strip_suffix("()").map(str::trim) { + if call_snip.as_bytes().iter().all(|b| b.is_ascii_alphabetic() || *b == b'_') { + app = Applicability::MachineApplicable; } + } - span_lint_hir_and_then(cx, REDUNDANT_CLONE, node, sugg_span, "redundant clone", |diag| { - diag.span_suggestion( - sugg_span, - "remove this", - String::new(), - app, + span_lint_hir_and_then(cx, REDUNDANT_CLONE, node, sugg_span, "redundant clone", |diag| { + diag.span_suggestion( + sugg_span, + "remove this", + String::new(), + app, + ); + if clone_usage.cloned_used { + diag.span_note( + span, + "cloned value is neither consumed nor mutated", ); - if used { - diag.span_note( - span, - "cloned value is neither consumed nor mutated", - ); - } else { - diag.span_note( - span.with_hi(span.lo() + BytePos(u32::try_from(dot).unwrap())), - "this value is dropped without further use", - ); - } - }); - } else { - span_lint_hir(cx, REDUNDANT_CLONE, node, span, "redundant clone"); - } + } else { + diag.span_note( + span.with_hi(span.lo() + BytePos(u32::try_from(dot).unwrap())), + "this value is dropped without further use", + ); + } + }); + } else { + span_lint_hir(cx, REDUNDANT_CLONE, node, span, "redundant clone"); } } } @@ -365,49 +358,97 @@ fn base_local_and_movability<'tcx>( (local, deref || field || slice) } -struct LocalUseVisitor { - used: (mir::Local, bool), - consumed_or_mutated: (mir::Local, bool), +#[derive(Default)] +struct CloneUsage { + /// Whether the cloned value is used after the clone. + cloned_used: bool, + /// The first location where the cloned value is consumed or mutated, if any. + cloned_consume_or_mutate_loc: Option<mir::Location>, + /// Whether the clone value is mutated. + clone_consumed_or_mutated: bool, } - -impl<'tcx> mir::visit::Visitor<'tcx> for LocalUseVisitor { - fn visit_basic_block_data(&mut self, block: mir::BasicBlock, data: &mir::BasicBlockData<'tcx>) { - let statements = &data.statements; - for (statement_index, statement) in statements.iter().enumerate() { - self.visit_statement(statement, mir::Location { block, statement_index }); - } - - self.visit_terminator( - data.terminator(), - mir::Location { - block, - statement_index: statements.len(), - }, - ); +fn visit_clone_usage(cloned: mir::Local, clone: mir::Local, mir: &mir::Body<'_>, bb: mir::BasicBlock) -> CloneUsage { + struct V { + cloned: mir::Local, + clone: mir::Local, + result: CloneUsage, } + impl<'tcx> mir::visit::Visitor<'tcx> for V { + fn visit_basic_block_data(&mut self, block: mir::BasicBlock, data: &mir::BasicBlockData<'tcx>) { + let statements = &data.statements; + for (statement_index, statement) in statements.iter().enumerate() { + self.visit_statement(statement, mir::Location { block, statement_index }); + } - fn visit_place(&mut self, place: &mir::Place<'tcx>, ctx: PlaceContext, _: mir::Location) { - let local = place.local; - - if local == self.used.0 - && !matches!( - ctx, - PlaceContext::MutatingUse(MutatingUseContext::Drop) | PlaceContext::NonUse(_) - ) - { - self.used.1 = true; + self.visit_terminator( + data.terminator(), + mir::Location { + block, + statement_index: statements.len(), + }, + ); } - if local == self.consumed_or_mutated.0 { - match ctx { - PlaceContext::NonMutatingUse(NonMutatingUseContext::Move) - | PlaceContext::MutatingUse(MutatingUseContext::Borrow) => { - self.consumed_or_mutated.1 = true; - }, - _ => {}, + fn visit_place(&mut self, place: &mir::Place<'tcx>, ctx: PlaceContext, loc: mir::Location) { + let local = place.local; + + if local == self.cloned + && !matches!( + ctx, + PlaceContext::MutatingUse(MutatingUseContext::Drop) | PlaceContext::NonUse(_) + ) + { + self.result.cloned_used = true; + self.result.cloned_consume_or_mutate_loc = self.result.cloned_consume_or_mutate_loc.or_else(|| { + matches!( + ctx, + PlaceContext::NonMutatingUse(NonMutatingUseContext::Move) + | PlaceContext::MutatingUse(MutatingUseContext::Borrow) + ) + .then(|| loc) + }); + } else if local == self.clone { + match ctx { + PlaceContext::NonMutatingUse(NonMutatingUseContext::Move) + | PlaceContext::MutatingUse(MutatingUseContext::Borrow) => { + self.result.clone_consumed_or_mutated = true; + }, + _ => {}, + } } } } + + let init = CloneUsage { + cloned_used: false, + cloned_consume_or_mutate_loc: None, + // Consider non-temporary clones consumed. + // TODO: Actually check for mutation of non-temporaries. + clone_consumed_or_mutated: mir.local_kind(clone) != mir::LocalKind::Temp, + }; + traversal::ReversePostorder::new(mir, bb) + .skip(1) + .fold(init, |usage, (tbb, tdata)| { + // Short-circuit + if (usage.cloned_used && usage.clone_consumed_or_mutated) || + // Give up on loops + tdata.terminator().successors().any(|s| *s == bb) + { + return CloneUsage { + cloned_used: true, + clone_consumed_or_mutated: true, + ..usage + }; + } + + let mut v = V { + cloned, + clone, + result: usage, + }; + v.visit_basic_block_data(tbb, tdata); + v.result + }) } /// Determines liveness of each local purely based on `StorageLive`/`Dead`. @@ -547,7 +588,7 @@ impl<'a, 'tcx> mir::visit::Visitor<'tcx> for PossibleBorrowerVisitor<'a, 'tcx> { // If the call returns something with lifetimes, // let's conservatively assume the returned value contains lifetime of all the arguments. // For example, given `let y: Foo<'a> = foo(x)`, `y` is considered to be a possible borrower of `x`. - if ContainsRegion.visit_ty(&self.body.local_decls[*dest].ty).is_continue() { + if ContainsRegion.visit_ty(self.body.local_decls[*dest].ty).is_continue() { return; } @@ -556,7 +597,7 @@ impl<'a, 'tcx> mir::visit::Visitor<'tcx> for PossibleBorrowerVisitor<'a, 'tcx> { mir::Operand::Copy(p) | mir::Operand::Move(p) => { self.possible_borrower.add(p.local, *dest); }, - _ => (), + mir::Operand::Constant(..) => (), } } } @@ -578,16 +619,16 @@ fn rvalue_locals(rvalue: &mir::Rvalue<'_>, mut visit: impl FnMut(mir::Local)) { let mut visit_op = |op: &mir::Operand<'_>| match op { mir::Operand::Copy(p) | mir::Operand::Move(p) => visit(p.local), - _ => (), + mir::Operand::Constant(..) => (), }; match rvalue { Use(op) | Repeat(op, _) | Cast(_, op, _) | UnaryOp(_, op) => visit_op(op), Aggregate(_, ops) => ops.iter().for_each(visit_op), - BinaryOp(_, lhs, rhs) | CheckedBinaryOp(_, lhs, rhs) => { + BinaryOp(_, box (lhs, rhs)) | CheckedBinaryOp(_, box (lhs, rhs)) => { visit_op(lhs); visit_op(rhs); - }, + } _ => (), } } @@ -623,4 +664,9 @@ impl PossibleBorrowerMap<'_, '_> { self.bitset.0 == self.bitset.1 } + + fn 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_closure_call.rs b/src/tools/clippy/clippy_lints/src/redundant_closure_call.rs index f398b3fff25a3..92921bedf4dff 100644 --- a/src/tools/clippy/clippy_lints/src/redundant_closure_call.rs +++ b/src/tools/clippy/clippy_lints/src/redundant_closure_call.rs @@ -1,4 +1,5 @@ -use crate::utils::{snippet_with_applicability, span_lint, span_lint_and_then}; +use clippy_utils::diagnostics::{span_lint, span_lint_and_then}; +use clippy_utils::source::snippet_with_applicability; use if_chain::if_chain; use rustc_ast::ast; use rustc_ast::visit as ast_visit; @@ -110,10 +111,10 @@ impl<'tcx> LateLintPass<'tcx> for RedundantClosureCall { fn visit_expr(&mut self, expr: &'tcx hir::Expr<'tcx>) { if_chain! { - if let hir::ExprKind::Call(ref closure, _) = expr.kind; - if let hir::ExprKind::Path(hir::QPath::Resolved(_, ref path)) = closure.kind; - if self.path.segments[0].ident == path.segments[0].ident - && self.path.res == path.res; + if let hir::ExprKind::Call(closure, _) = expr.kind; + if let hir::ExprKind::Path(hir::QPath::Resolved(_, path)) = closure.kind; + if self.path.segments[0].ident == path.segments[0].ident; + if self.path.res == path.res; then { self.count += 1; } @@ -132,14 +133,14 @@ impl<'tcx> LateLintPass<'tcx> for RedundantClosureCall { for w in block.stmts.windows(2) { if_chain! { - if let hir::StmtKind::Local(ref local) = w[0].kind; - if let Option::Some(ref t) = local.init; + if let hir::StmtKind::Local(local) = w[0].kind; + if let Option::Some(t) = local.init; if let hir::ExprKind::Closure(..) = t.kind; if let hir::PatKind::Binding(_, _, ident, _) = local.pat.kind; - if let hir::StmtKind::Semi(ref second) = w[1].kind; - if let hir::ExprKind::Assign(_, ref call, _) = second.kind; - if let hir::ExprKind::Call(ref closure, _) = call.kind; - if let hir::ExprKind::Path(hir::QPath::Resolved(_, ref path)) = closure.kind; + if let hir::StmtKind::Semi(second) = w[1].kind; + if let hir::ExprKind::Assign(_, call, _) = second.kind; + if let hir::ExprKind::Call(closure, _) = call.kind; + if let hir::ExprKind::Path(hir::QPath::Resolved(_, path)) = closure.kind; if ident == path.segments[0].ident; if count_closure_usage(cx, block, path) == 1; then { diff --git a/src/tools/clippy/clippy_lints/src/redundant_else.rs b/src/tools/clippy/clippy_lints/src/redundant_else.rs index 3d585cd27a3d0..061526c6f09fa 100644 --- a/src/tools/clippy/clippy_lints/src/redundant_else.rs +++ b/src/tools/clippy/clippy_lints/src/redundant_else.rs @@ -1,4 +1,4 @@ -use crate::utils::span_lint_and_help; +use clippy_utils::diagnostics::span_lint_and_help; use rustc_ast::ast::{Block, Expr, ExprKind, Stmt, StmtKind}; use rustc_ast::visit::{walk_expr, Visitor}; use rustc_lint::{EarlyContext, EarlyLintPass}; diff --git a/src/tools/clippy/clippy_lints/src/redundant_field_names.rs b/src/tools/clippy/clippy_lints/src/redundant_field_names.rs index 38dcf7a192c82..abebd4227975e 100644 --- a/src/tools/clippy/clippy_lints/src/redundant_field_names.rs +++ b/src/tools/clippy/clippy_lints/src/redundant_field_names.rs @@ -1,4 +1,5 @@ -use crate::utils::{meets_msrv, span_lint_and_sugg}; +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::meets_msrv; use rustc_ast::ast::{Expr, ExprKind}; use rustc_errors::Applicability; use rustc_lint::{EarlyContext, EarlyLintPass}; @@ -58,8 +59,8 @@ impl EarlyLintPass for RedundantFieldNames { if in_external_macro(cx.sess, expr.span) { return; } - if let ExprKind::Struct(_, ref fields, _) = expr.kind { - for field in fields { + if let ExprKind::Struct(ref se) = expr.kind { + for field in &se.fields { if field.is_shorthand { continue; } diff --git a/src/tools/clippy/clippy_lints/src/redundant_pub_crate.rs b/src/tools/clippy/clippy_lints/src/redundant_pub_crate.rs index c876bae2303ad..e091095de136a 100644 --- a/src/tools/clippy/clippy_lints/src/redundant_pub_crate.rs +++ b/src/tools/clippy/clippy_lints/src/redundant_pub_crate.rs @@ -1,4 +1,4 @@ -use crate::utils::span_lint_and_then; +use clippy_utils::diagnostics::span_lint_and_then; use rustc_errors::Applicability; use rustc_hir::{Item, ItemKind, VisibilityKind}; use rustc_lint::{LateContext, LateLintPass}; diff --git a/src/tools/clippy/clippy_lints/src/redundant_slicing.rs b/src/tools/clippy/clippy_lints/src/redundant_slicing.rs index e5ced13514f79..9c6cd7b4fa625 100644 --- a/src/tools/clippy/clippy_lints/src/redundant_slicing.rs +++ b/src/tools/clippy/clippy_lints/src/redundant_slicing.rs @@ -1,12 +1,14 @@ +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::source::snippet_with_context; +use clippy_utils::ty::is_type_lang_item; +use clippy_utils::{get_parent_expr, in_macro}; use if_chain::if_chain; use rustc_errors::Applicability; -use rustc_hir::{Expr, ExprKind, LangItem}; -use rustc_lint::{LateContext, LateLintPass, LintContext}; -use rustc_middle::{lint::in_external_macro, ty::TyS}; +use rustc_hir::{BorrowKind, Expr, ExprKind, LangItem, Mutability}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::ty::TyS; use rustc_session::{declare_lint_pass, declare_tool_lint}; -use crate::utils::{is_type_lang_item, snippet_with_applicability, span_lint_and_sugg}; - declare_clippy_lint! { /// **What it does:** Checks for redundant slicing expressions which use the full range, and /// do not change the type. @@ -39,26 +41,44 @@ declare_lint_pass!(RedundantSlicing => [REDUNDANT_SLICING]); impl LateLintPass<'_> for RedundantSlicing { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - if in_external_macro(cx.sess(), expr.span) { + if in_macro(expr.span) { return; } + let ctxt = expr.span.ctxt(); if_chain! { - if let ExprKind::AddrOf(_, _, addressee) = expr.kind; + if let ExprKind::AddrOf(BorrowKind::Ref, mutability, addressee) = expr.kind; + if addressee.span.ctxt() == ctxt; if let ExprKind::Index(indexed, range) = addressee.kind; if is_type_lang_item(cx, cx.typeck_results().expr_ty_adjusted(range), LangItem::RangeFull); if TyS::same_type(cx.typeck_results().expr_ty(expr), cx.typeck_results().expr_ty(indexed)); then { let mut app = Applicability::MachineApplicable; - let hint = snippet_with_applicability(cx, indexed.span, "..", &mut app).into_owned(); + let snip = snippet_with_context(cx, indexed.span, ctxt, "..", &mut app).0; + + let (reborrow_str, help_str) = if mutability == Mutability::Mut { + // The slice was used to reborrow the mutable reference. + ("&mut *", "reborrow the original value instead") + } else if matches!( + get_parent_expr(cx, expr), + Some(Expr { + kind: ExprKind::AddrOf(BorrowKind::Ref, Mutability::Mut, _), + .. + }) + ) { + // The slice was used to make a temporary reference. + ("&*", "reborrow the original value instead") + } else { + ("", "use the original value instead") + }; span_lint_and_sugg( cx, REDUNDANT_SLICING, expr.span, "redundant slicing of the whole range", - "use the original slice instead", - hint, + help_str, + format!("{}{}", reborrow_str, snip), app, ); } diff --git a/src/tools/clippy/clippy_lints/src/redundant_static_lifetimes.rs b/src/tools/clippy/clippy_lints/src/redundant_static_lifetimes.rs index fcfa3c12755af..32b57698ec54d 100644 --- a/src/tools/clippy/clippy_lints/src/redundant_static_lifetimes.rs +++ b/src/tools/clippy/clippy_lints/src/redundant_static_lifetimes.rs @@ -1,4 +1,6 @@ -use crate::utils::{meets_msrv, snippet, span_lint_and_then}; +use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::meets_msrv; +use clippy_utils::source::snippet; use rustc_ast::ast::{Item, ItemKind, Ty, TyKind}; use rustc_errors::Applicability; use rustc_lint::{EarlyContext, EarlyLintPass}; diff --git a/src/tools/clippy/clippy_lints/src/ref_option_ref.rs b/src/tools/clippy/clippy_lints/src/ref_option_ref.rs index 8cd6692ce03a0..0cf4e0ce7fe22 100644 --- a/src/tools/clippy/clippy_lints/src/ref_option_ref.rs +++ b/src/tools/clippy/clippy_lints/src/ref_option_ref.rs @@ -1,12 +1,13 @@ -use crate::utils::{last_path_segment, snippet, span_lint_and_sugg}; +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::last_path_segment; +use clippy_utils::source::snippet; +use if_chain::if_chain; +use rustc_errors::Applicability; use rustc_hir::{GenericArg, Mutability, Ty, TyKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::symbol::sym; -use if_chain::if_chain; -use rustc_errors::Applicability; - declare_clippy_lint! { /// **What it does:** Checks for usage of `&Option<&T>`. /// @@ -43,7 +44,7 @@ impl<'tcx> LateLintPass<'tcx> for RefOptionRef { if let Some(def_id) = res.opt_def_id(); if cx.tcx.is_diagnostic_item(sym::option_type, def_id); - if let Some(ref params) = last_path_segment(qpath).args ; + if let Some(params) = last_path_segment(qpath).args ; if !params.parenthesized; if let Some(inner_ty) = params.args.iter().find_map(|arg| match arg { GenericArg::Type(inner_ty) => Some(inner_ty), diff --git a/src/tools/clippy/clippy_lints/src/reference.rs b/src/tools/clippy/clippy_lints/src/reference.rs index e1450466a7c22..d6336389b0af1 100644 --- a/src/tools/clippy/clippy_lints/src/reference.rs +++ b/src/tools/clippy/clippy_lints/src/reference.rs @@ -1,5 +1,7 @@ -use crate::utils::sugg::Sugg; -use crate::utils::{in_macro, snippet_opt, snippet_with_applicability, span_lint_and_sugg}; +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::in_macro; +use clippy_utils::source::{snippet_opt, snippet_with_applicability}; +use clippy_utils::sugg::Sugg; use if_chain::if_chain; use rustc_ast::ast::{Expr, ExprKind, Mutability, UnOp}; use rustc_errors::Applicability; diff --git a/src/tools/clippy/clippy_lints/src/regex.rs b/src/tools/clippy/clippy_lints/src/regex.rs index 1edea61314893..4b5306de58ecd 100644 --- a/src/tools/clippy/clippy_lints/src/regex.rs +++ b/src/tools/clippy/clippy_lints/src/regex.rs @@ -1,5 +1,6 @@ use crate::consts::{constant, Constant}; -use crate::utils::{match_def_path, paths, span_lint, span_lint_and_help}; +use clippy_utils::diagnostics::{span_lint, span_lint_and_help}; +use clippy_utils::{match_def_path, paths}; use if_chain::if_chain; use rustc_ast::ast::{LitKind, StrStyle}; use rustc_data_structures::fx::FxHashSet; @@ -59,7 +60,7 @@ impl_lint_pass!(Regex => [INVALID_REGEX, TRIVIAL_REGEX]); impl<'tcx> LateLintPass<'tcx> for Regex { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { if_chain! { - if let ExprKind::Call(ref fun, ref args) = expr.kind; + if let ExprKind::Call(fun, args) = expr.kind; if let ExprKind::Path(ref qpath) = fun.kind; if args.len() == 1; if let Some(def_id) = cx.qpath_res(qpath, fun.hir_id).opt_def_id(); @@ -133,7 +134,7 @@ fn is_trivial_regex(s: ®ex_syntax::hir::Hir) -> Option<&'static str> { fn check_set<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, utf8: bool) { if_chain! { - if let ExprKind::AddrOf(BorrowKind::Ref, _, ref expr) = expr.kind; + if let ExprKind::AddrOf(BorrowKind::Ref, _, expr) = expr.kind; if let ExprKind::Array(exprs) = expr.kind; then { for expr in exprs { diff --git a/src/tools/clippy/clippy_lints/src/repeat_once.rs b/src/tools/clippy/clippy_lints/src/repeat_once.rs index d34e744eb944c..560a5e7c9200a 100644 --- a/src/tools/clippy/clippy_lints/src/repeat_once.rs +++ b/src/tools/clippy/clippy_lints/src/repeat_once.rs @@ -1,5 +1,8 @@ use crate::consts::{constant_context, Constant}; -use crate::utils::{in_macro, is_type_diagnostic_item, snippet, span_lint_and_sugg}; +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::in_macro; +use clippy_utils::source::snippet; +use clippy_utils::ty::is_type_diagnostic_item; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind}; @@ -42,10 +45,10 @@ impl<'tcx> LateLintPass<'tcx> for RepeatOnce { if_chain! { if let ExprKind::MethodCall(path, _, [receiver, count], _) = &expr.kind; if path.ident.name == sym!(repeat); - if let Some(Constant::Int(1)) = constant_context(cx, cx.typeck_results()).expr(&count); + if let Some(Constant::Int(1)) = constant_context(cx, cx.typeck_results()).expr(count); if !in_macro(receiver.span); then { - let ty = cx.typeck_results().expr_ty(&receiver).peel_refs(); + let ty = cx.typeck_results().expr_ty(receiver).peel_refs(); if ty.is_str() { span_lint_and_sugg( cx, diff --git a/src/tools/clippy/clippy_lints/src/returns.rs b/src/tools/clippy/clippy_lints/src/returns.rs index e438f92b136ac..af772cf4a145d 100644 --- a/src/tools/clippy/clippy_lints/src/returns.rs +++ b/src/tools/clippy/clippy_lints/src/returns.rs @@ -1,3 +1,6 @@ +use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then}; +use clippy_utils::source::snippet_opt; +use clippy_utils::{fn_def_id, in_macro, match_qpath}; use if_chain::if_chain; use rustc_ast::ast::Attribute; use rustc_errors::Applicability; @@ -11,8 +14,6 @@ use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::source_map::Span; use rustc_span::sym; -use crate::utils::{fn_def_id, in_macro, match_qpath, snippet_opt, span_lint_and_sugg, span_lint_and_then}; - declare_clippy_lint! { /// **What it does:** Checks for `let`-bindings, which are subsequently /// returned. @@ -81,7 +82,7 @@ impl<'tcx> LateLintPass<'tcx> for Return { if let Some(stmt) = block.stmts.iter().last(); if let StmtKind::Local(local) = &stmt.kind; if local.ty.is_none(); - if local.attrs.is_empty(); + if cx.tcx.hir().attrs(local.hir_id).is_empty(); if let Some(initexpr) = &local.init; if let PatKind::Binding(.., ident, _) = local.pat.kind; if let ExprKind::Path(qpath) = &retexpr.kind; @@ -101,7 +102,7 @@ impl<'tcx> LateLintPass<'tcx> for Return { err.span_label(local.span, "unnecessary `let` binding"); if let Some(mut snippet) = snippet_opt(cx, initexpr.span) { - if !cx.typeck_results().expr_adjustments(&retexpr).is_empty() { + if !cx.typeck_results().expr_adjustments(retexpr).is_empty() { snippet.push_str(" as _"); } err.multipart_suggestion( @@ -131,7 +132,7 @@ impl<'tcx> LateLintPass<'tcx> for Return { _: HirId, ) { match kind { - FnKind::Closure(_) => { + FnKind::Closure => { // when returning without value in closure, replace this `return` // with an empty block to prevent invalid suggestion (see #6501) let replacement = if let ExprKind::Ret(None) = &body.value.kind { @@ -142,7 +143,7 @@ impl<'tcx> LateLintPass<'tcx> for Return { check_final_expr(cx, &body.value, Some(body.value.span), replacement) }, FnKind::ItemFn(..) | FnKind::Method(..) => { - if let ExprKind::Block(ref block, _) = body.value.kind { + if let ExprKind::Block(block, _) = body.value.kind { check_block_return(cx, block); } }, @@ -159,7 +160,7 @@ fn check_block_return<'tcx>(cx: &LateContext<'tcx>, block: &Block<'tcx>) { check_final_expr(cx, expr, Some(expr.span), RetReplacement::Empty); } else if let Some(stmt) = block.stmts.iter().last() { match stmt.kind { - StmtKind::Expr(ref expr) | StmtKind::Semi(ref expr) => { + StmtKind::Expr(expr) | StmtKind::Semi(expr) => { check_final_expr(cx, expr, Some(stmt.span), RetReplacement::Empty); }, _ => (), @@ -177,7 +178,8 @@ fn check_final_expr<'tcx>( // simple return is always "bad" ExprKind::Ret(ref inner) => { // allow `#[cfg(a)] return a; #[cfg(b)] return b;` - if !expr.attrs.iter().any(attr_is_cfg) { + let attrs = cx.tcx.hir().attrs(expr.hir_id); + if !attrs.iter().any(attr_is_cfg) { let borrows = inner.map_or(false, |inner| last_statement_borrows(cx, inner)); if !borrows { emit_return_lint( @@ -190,11 +192,11 @@ fn check_final_expr<'tcx>( } }, // a whole block? check it! - ExprKind::Block(ref block, _) => { + ExprKind::Block(block, _) => { check_block_return(cx, block); }, ExprKind::If(_, then, else_clause_opt) => { - if let ExprKind::Block(ref ifblock, _) = then.kind { + if let ExprKind::Block(ifblock, _) = then.kind { check_block_return(cx, ifblock); } if let Some(else_clause) = else_clause_opt { @@ -205,16 +207,16 @@ fn check_final_expr<'tcx>( // an if/if let expr, check both exprs // note, if without else is going to be a type checking error anyways // (except for unit type functions) so we don't match it - ExprKind::Match(_, ref arms, source) => match source { + ExprKind::Match(_, arms, source) => match source { MatchSource::Normal => { for arm in arms.iter() { - check_final_expr(cx, &arm.body, Some(arm.body.span), RetReplacement::Block); + check_final_expr(cx, arm.body, Some(arm.body.span), RetReplacement::Block); } }, MatchSource::IfLetDesugar { contains_else_clause: true, } => { - if let ExprKind::Block(ref ifblock, _) = arms[0].body.kind { + if let ExprKind::Block(ifblock, _) = arms[0].body.kind { check_block_return(cx, ifblock); } check_final_expr(cx, arms[1].body, None, RetReplacement::Empty); diff --git a/src/tools/clippy/clippy_lints/src/self_assignment.rs b/src/tools/clippy/clippy_lints/src/self_assignment.rs index e096c9aebc122..e7925c4fbdeff 100644 --- a/src/tools/clippy/clippy_lints/src/self_assignment.rs +++ b/src/tools/clippy/clippy_lints/src/self_assignment.rs @@ -1,4 +1,6 @@ -use crate::utils::{eq_expr_value, snippet, span_lint}; +use clippy_utils::diagnostics::span_lint; +use clippy_utils::eq_expr_value; +use clippy_utils::source::snippet; use rustc_hir::{Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; 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 839c995e52562..553987a426b57 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 @@ -1,4 +1,6 @@ -use crate::utils::{in_macro, snippet_with_macro_callsite, span_lint_and_sugg, sugg}; +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::source::snippet_with_macro_callsite; +use clippy_utils::{in_macro, sugg}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::{Block, ExprKind}; @@ -49,7 +51,7 @@ impl LateLintPass<'_> for SemicolonIfNothingReturned { return; } - let sugg = sugg::Sugg::hir_with_macro_callsite(cx, &expr, ".."); + let sugg = sugg::Sugg::hir_with_macro_callsite(cx, expr, ".."); let suggestion = format!("{0};", sugg); span_lint_and_sugg( cx, diff --git a/src/tools/clippy/clippy_lints/src/serde_api.rs b/src/tools/clippy/clippy_lints/src/serde_api.rs index 90cf1b6c86135..169f7d26285cd 100644 --- a/src/tools/clippy/clippy_lints/src/serde_api.rs +++ b/src/tools/clippy/clippy_lints/src/serde_api.rs @@ -1,4 +1,5 @@ -use crate::utils::{get_trait_def_id, paths, span_lint}; +use clippy_utils::diagnostics::span_lint; +use clippy_utils::{get_trait_def_id, paths}; use rustc_hir::{Impl, Item, ItemKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; diff --git a/src/tools/clippy/clippy_lints/src/shadow.rs b/src/tools/clippy/clippy_lints/src/shadow.rs index 32f6bc74642ca..d6101bd5e36a2 100644 --- a/src/tools/clippy/clippy_lints/src/shadow.rs +++ b/src/tools/clippy/clippy_lints/src/shadow.rs @@ -1,4 +1,6 @@ -use crate::utils::{contains_name, higher, iter_input_pats, snippet, span_lint_and_then}; +use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::source::snippet; +use clippy_utils::{contains_name, higher, iter_input_pats}; use rustc_hir::intravisit::FnKind; use rustc_hir::{ Block, Body, Expr, ExprKind, FnDecl, Guard, HirId, Local, MutTy, Pat, PatKind, Path, QPath, StmtKind, Ty, TyKind, @@ -128,12 +130,12 @@ fn check_block<'tcx>(cx: &LateContext<'tcx>, block: &'tcx Block<'_>, bindings: & let len = bindings.len(); for stmt in block.stmts { match stmt.kind { - StmtKind::Local(ref local) => check_local(cx, local, bindings), - StmtKind::Expr(ref e) | StmtKind::Semi(ref e) => check_expr(cx, e, bindings), + StmtKind::Local(local) => check_local(cx, local, bindings), + StmtKind::Expr(e) | StmtKind::Semi(e) => check_expr(cx, e, bindings), StmtKind::Item(..) => {}, } } - if let Some(ref o) = block.expr { + if let Some(o) = block.expr { check_expr(cx, o, bindings); } bindings.truncate(len); @@ -147,16 +149,16 @@ fn check_local<'tcx>(cx: &LateContext<'tcx>, local: &'tcx Local<'_>, bindings: & return; } let Local { - ref pat, + pat, ref ty, ref init, span, .. } = *local; - if let Some(ref t) = *ty { + if let Some(t) = *ty { check_ty(cx, t, bindings) } - if let Some(ref o) = *init { + if let Some(o) = *init { check_expr(cx, o, bindings); check_pat(cx, pat, Some(o), span, bindings); } else { @@ -194,34 +196,34 @@ fn check_pat<'tcx>( bindings.push((name, ident.span)); } } - if let Some(ref p) = *inner { + if let Some(p) = *inner { check_pat(cx, p, init, span, bindings); } }, PatKind::Struct(_, pfields, _) => { if let Some(init_struct) = init { - if let ExprKind::Struct(_, ref efields, _) = init_struct.kind { + if let ExprKind::Struct(_, efields, _) = init_struct.kind { for field in pfields { let name = field.ident.name; let efield = efields .iter() .find_map(|f| if f.ident.name == name { Some(&*f.expr) } else { None }); - check_pat(cx, &field.pat, efield, span, bindings); + check_pat(cx, field.pat, efield, span, bindings); } } else { for field in pfields { - check_pat(cx, &field.pat, init, span, bindings); + check_pat(cx, field.pat, init, span, bindings); } } } else { for field in pfields { - check_pat(cx, &field.pat, None, span, bindings); + check_pat(cx, field.pat, None, span, bindings); } } }, PatKind::Tuple(inner, _) => { if let Some(init_tup) = init { - if let ExprKind::Tup(ref tup) = init_tup.kind { + if let ExprKind::Tup(tup) = init_tup.kind { for (i, p) in inner.iter().enumerate() { check_pat(cx, p, Some(&tup[i]), p.span, bindings); } @@ -236,10 +238,10 @@ fn check_pat<'tcx>( } } }, - PatKind::Box(ref inner) => { + PatKind::Box(inner) => { if let Some(initp) = init { - if let ExprKind::Box(ref inner_init) = initp.kind { - check_pat(cx, inner, Some(&**inner_init), span, bindings); + if let ExprKind::Box(inner_init) = initp.kind { + check_pat(cx, inner, Some(inner_init), span, bindings); } else { check_pat(cx, inner, init, span, bindings); } @@ -247,7 +249,7 @@ fn check_pat<'tcx>( check_pat(cx, inner, init, span, bindings); } }, - PatKind::Ref(ref inner, _) => check_pat(cx, inner, init, span, bindings), + PatKind::Ref(inner, _) => check_pat(cx, inner, init, span, bindings), // PatVec(Vec<P<Pat>>, Option<P<Pat>>, Vec<P<Pat>>), _ => (), } @@ -321,11 +323,10 @@ fn check_expr<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, bindings: &mut return; } match expr.kind { - ExprKind::Unary(_, ref e) - | ExprKind::Field(ref e, _) - | ExprKind::AddrOf(_, _, ref e) - | ExprKind::Box(ref e) => check_expr(cx, e, bindings), - ExprKind::Block(ref block, _) | ExprKind::Loop(ref block, ..) => check_block(cx, block, bindings), + ExprKind::Unary(_, e) | ExprKind::Field(e, _) | ExprKind::AddrOf(_, _, e) | ExprKind::Box(e) => { + check_expr(cx, e, bindings) + }, + ExprKind::Block(block, _) | ExprKind::Loop(block, ..) => check_block(cx, block, bindings), // ExprKind::Call // ExprKind::MethodCall ExprKind::Array(v) | ExprKind::Tup(v) => { @@ -333,18 +334,18 @@ fn check_expr<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, bindings: &mut check_expr(cx, e, bindings) } }, - ExprKind::If(ref cond, ref then, ref otherwise) => { + ExprKind::If(cond, then, ref otherwise) => { check_expr(cx, cond, bindings); - check_expr(cx, &**then, bindings); - if let Some(ref o) = *otherwise { + check_expr(cx, then, bindings); + if let Some(o) = *otherwise { check_expr(cx, o, bindings); } }, - ExprKind::Match(ref init, arms, _) => { + ExprKind::Match(init, arms, _) => { check_expr(cx, init, bindings); let len = bindings.len(); for arm in arms { - check_pat(cx, &arm.pat, Some(&**init), arm.pat.span, bindings); + check_pat(cx, arm.pat, Some(init), arm.pat.span, bindings); // This is ugly, but needed to get the right type if let Some(ref guard) = arm.guard { match guard { @@ -355,7 +356,7 @@ fn check_expr<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, bindings: &mut }, } } - check_expr(cx, &arm.body, bindings); + check_expr(cx, arm.body, bindings); bindings.truncate(len); } }, @@ -365,14 +366,12 @@ fn check_expr<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, bindings: &mut fn check_ty<'tcx>(cx: &LateContext<'tcx>, ty: &'tcx Ty<'_>, bindings: &mut Vec<(Symbol, Span)>) { match ty.kind { - TyKind::Slice(ref sty) => check_ty(cx, sty, bindings), - TyKind::Array(ref fty, ref anon_const) => { + TyKind::Slice(sty) => check_ty(cx, sty, bindings), + TyKind::Array(fty, ref anon_const) => { check_ty(cx, fty, bindings); check_expr(cx, &cx.tcx.hir().body(anon_const.body).value, bindings); }, - TyKind::Ptr(MutTy { ty: ref mty, .. }) | TyKind::Rptr(_, MutTy { ty: ref mty, .. }) => { - check_ty(cx, mty, bindings) - }, + TyKind::Ptr(MutTy { ty: mty, .. }) | TyKind::Rptr(_, MutTy { ty: mty, .. }) => check_ty(cx, mty, bindings), TyKind::Tup(tup) => { for t in tup { check_ty(cx, t, bindings) @@ -385,12 +384,12 @@ fn check_ty<'tcx>(cx: &LateContext<'tcx>, ty: &'tcx Ty<'_>, bindings: &mut Vec<( fn is_self_shadow(name: Symbol, expr: &Expr<'_>) -> bool { match expr.kind { - ExprKind::Box(ref inner) | ExprKind::AddrOf(_, _, ref inner) => is_self_shadow(name, inner), - ExprKind::Block(ref block, _) => { + ExprKind::Box(inner) | ExprKind::AddrOf(_, _, inner) => is_self_shadow(name, inner), + ExprKind::Block(block, _) => { block.stmts.is_empty() && block.expr.as_ref().map_or(false, |e| is_self_shadow(name, e)) }, - ExprKind::Unary(op, ref inner) => (UnOp::Deref == op) && is_self_shadow(name, inner), - ExprKind::Path(QPath::Resolved(_, ref path)) => path_eq_name(name, path), + ExprKind::Unary(op, inner) => (UnOp::Deref == op) && is_self_shadow(name, inner), + ExprKind::Path(QPath::Resolved(_, path)) => path_eq_name(name, path), _ => false, } } diff --git a/src/tools/clippy/clippy_lints/src/single_component_path_imports.rs b/src/tools/clippy/clippy_lints/src/single_component_path_imports.rs index 1fc4ff5c2e61f..c9d72aabb6a3c 100644 --- a/src/tools/clippy/clippy_lints/src/single_component_path_imports.rs +++ b/src/tools/clippy/clippy_lints/src/single_component_path_imports.rs @@ -1,4 +1,5 @@ -use crate::utils::{in_macro, span_lint_and_sugg}; +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::in_macro; use if_chain::if_chain; use rustc_ast::{Item, ItemKind, UseTreeKind}; use rustc_errors::Applicability; 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 87e386baadc54..09e0086681556 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 @@ -1,7 +1,8 @@ //! Lint on use of `size_of` or `size_of_val` of T in an expression //! expecting a count of T -use crate::utils::{match_def_path, paths, span_lint_and_help}; +use clippy_utils::diagnostics::span_lint_and_help; +use clippy_utils::{match_def_path, paths}; use if_chain::if_chain; use rustc_hir::BinOpKind; use rustc_hir::{Expr, ExprKind}; diff --git a/src/tools/clippy/clippy_lints/src/slow_vector_initialization.rs b/src/tools/clippy/clippy_lints/src/slow_vector_initialization.rs index 96f6881556cf3..8cf89ae456ee8 100644 --- a/src/tools/clippy/clippy_lints/src/slow_vector_initialization.rs +++ b/src/tools/clippy/clippy_lints/src/slow_vector_initialization.rs @@ -1,5 +1,6 @@ -use crate::utils::sugg::Sugg; -use crate::utils::{get_enclosing_block, match_qpath, span_lint_and_then, SpanlessEq}; +use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::sugg::Sugg; +use clippy_utils::{get_enclosing_block, match_qpath, SpanlessEq}; use if_chain::if_chain; use rustc_ast::ast::LitKind; use rustc_errors::Applicability; @@ -69,14 +70,14 @@ impl<'tcx> LateLintPass<'tcx> for SlowVectorInit { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { // Matches initialization on reassignements. For example: `vec = Vec::with_capacity(100)` if_chain! { - if let ExprKind::Assign(ref left, ref right, _) = expr.kind; + if let ExprKind::Assign(left, right, _) = expr.kind; // Extract variable name - if let ExprKind::Path(QPath::Resolved(_, ref path)) = left.kind; + if let ExprKind::Path(QPath::Resolved(_, path)) = left.kind; if let Some(variable_name) = path.segments.get(0); // Extract len argument - if let Some(ref len_arg) = Self::is_vec_with_capacity(right); + if let Some(len_arg) = Self::is_vec_with_capacity(right); then { let vi = VecAllocation { @@ -93,10 +94,10 @@ impl<'tcx> LateLintPass<'tcx> for SlowVectorInit { fn check_stmt(&mut self, cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'_>) { // Matches statements which initializes vectors. For example: `let mut vec = Vec::with_capacity(10)` if_chain! { - if let StmtKind::Local(ref local) = stmt.kind; + if let StmtKind::Local(local) = stmt.kind; if let PatKind::Binding(BindingAnnotation::Mutable, .., variable_name, None) = local.pat.kind; - if let Some(ref init) = local.init; - if let Some(ref len_arg) = Self::is_vec_with_capacity(init); + if let Some(init) = local.init; + if let Some(len_arg) = Self::is_vec_with_capacity(init); then { let vi = VecAllocation { @@ -116,7 +117,7 @@ impl SlowVectorInit { /// of the first argument of `with_capacity` call if it matches or `None` if it does not. fn is_vec_with_capacity<'tcx>(expr: &Expr<'tcx>) -> Option<&'tcx Expr<'tcx>> { if_chain! { - if let ExprKind::Call(ref func, ref args) = expr.kind; + if let ExprKind::Call(func, args) = expr.kind; if let ExprKind::Path(ref path) = func.kind; if match_qpath(path, &["Vec", "with_capacity"]); if args.len() == 1; @@ -207,11 +208,11 @@ impl<'a, 'tcx> VectorInitializationVisitor<'a, 'tcx> { fn search_slow_extend_filling(&mut self, expr: &'tcx Expr<'_>) { if_chain! { if self.initialization_found; - if let ExprKind::MethodCall(ref path, _, ref args, _) = expr.kind; + if let ExprKind::MethodCall(path, _, args, _) = expr.kind; if let ExprKind::Path(ref qpath_subj) = args[0].kind; - if match_qpath(&qpath_subj, &[&*self.vec_alloc.variable_name.as_str()]); + if match_qpath(qpath_subj, &[&*self.vec_alloc.variable_name.as_str()]); if path.ident.name == sym!(extend); - if let Some(ref extend_arg) = args.get(1); + if let Some(extend_arg) = args.get(1); if self.is_repeat_take(extend_arg); then { @@ -224,11 +225,11 @@ impl<'a, 'tcx> VectorInitializationVisitor<'a, 'tcx> { fn search_slow_resize_filling(&mut self, expr: &'tcx Expr<'_>) { if_chain! { if self.initialization_found; - if let ExprKind::MethodCall(ref path, _, ref args, _) = expr.kind; + if let ExprKind::MethodCall(path, _, args, _) = expr.kind; if let ExprKind::Path(ref qpath_subj) = args[0].kind; - if match_qpath(&qpath_subj, &[&*self.vec_alloc.variable_name.as_str()]); + if match_qpath(qpath_subj, &[&*self.vec_alloc.variable_name.as_str()]); if path.ident.name == sym!(resize); - if let (Some(ref len_arg), Some(fill_arg)) = (args.get(1), args.get(2)); + if let (Some(len_arg), Some(fill_arg)) = (args.get(1), args.get(2)); // Check that is filled with 0 if let ExprKind::Lit(ref lit) = fill_arg.kind; @@ -246,15 +247,15 @@ impl<'a, 'tcx> VectorInitializationVisitor<'a, 'tcx> { /// Returns `true` if give expression is `repeat(0).take(...)` fn is_repeat_take(&self, expr: &Expr<'_>) -> bool { if_chain! { - if let ExprKind::MethodCall(ref take_path, _, ref take_args, _) = expr.kind; + if let ExprKind::MethodCall(take_path, _, take_args, _) = expr.kind; if take_path.ident.name == sym!(take); // Check that take is applied to `repeat(0)` - if let Some(ref repeat_expr) = take_args.get(0); + if let Some(repeat_expr) = take_args.get(0); if Self::is_repeat_zero(repeat_expr); // Check that len expression is equals to `with_capacity` expression - if let Some(ref len_arg) = take_args.get(1); + if let Some(len_arg) = take_args.get(1); if SpanlessEq::new(self.cx).eq_expr(len_arg, self.vec_alloc.len_expr); then { @@ -268,10 +269,10 @@ impl<'a, 'tcx> VectorInitializationVisitor<'a, 'tcx> { /// Returns `true` if given expression is `repeat(0)` fn is_repeat_zero(expr: &Expr<'_>) -> bool { if_chain! { - if let ExprKind::Call(ref fn_expr, ref repeat_args) = expr.kind; + if let ExprKind::Call(fn_expr, repeat_args) = expr.kind; if let ExprKind::Path(ref qpath_repeat) = fn_expr.kind; - if match_qpath(&qpath_repeat, &["repeat"]); - if let Some(ref repeat_arg) = repeat_args.get(0); + if match_qpath(qpath_repeat, &["repeat"]); + if let Some(repeat_arg) = repeat_args.get(0); if let ExprKind::Lit(ref lit) = repeat_arg.kind; if let LitKind::Int(0, _) = lit.node; @@ -290,7 +291,7 @@ impl<'a, 'tcx> Visitor<'tcx> for VectorInitializationVisitor<'a, 'tcx> { fn visit_stmt(&mut self, stmt: &'tcx Stmt<'_>) { if self.initialization_found { match stmt.kind { - StmtKind::Expr(ref expr) | StmtKind::Semi(ref expr) => { + StmtKind::Expr(expr) | StmtKind::Semi(expr) => { self.search_slow_extend_filling(expr); self.search_slow_resize_filling(expr); }, @@ -305,7 +306,7 @@ impl<'a, 'tcx> Visitor<'tcx> for VectorInitializationVisitor<'a, 'tcx> { fn visit_block(&mut self, block: &'tcx Block<'_>) { if self.initialization_found { - if let Some(ref s) = block.stmts.get(0) { + if let Some(s) = block.stmts.get(0) { self.visit_stmt(s) } diff --git a/src/tools/clippy/clippy_lints/src/stable_sort_primitive.rs b/src/tools/clippy/clippy_lints/src/stable_sort_primitive.rs index 276a9338819d9..65790375c7379 100644 --- a/src/tools/clippy/clippy_lints/src/stable_sort_primitive.rs +++ b/src/tools/clippy/clippy_lints/src/stable_sort_primitive.rs @@ -1,7 +1,6 @@ -use crate::utils::{is_slice_of_primitives, span_lint_and_then, sugg::Sugg}; - +use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::{is_slice_of_primitives, sugg::Sugg}; use if_chain::if_chain; - use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; diff --git a/src/tools/clippy/clippy_lints/src/strings.rs b/src/tools/clippy/clippy_lints/src/strings.rs index 31dd5965473d3..9d91b53e1bbc1 100644 --- a/src/tools/clippy/clippy_lints/src/strings.rs +++ b/src/tools/clippy/clippy_lints/src/strings.rs @@ -1,3 +1,9 @@ +use clippy_utils::diagnostics::{span_lint, span_lint_and_help, span_lint_and_sugg}; +use clippy_utils::source::{snippet, snippet_with_applicability}; +use clippy_utils::ty::is_type_diagnostic_item; +use clippy_utils::SpanlessEq; +use clippy_utils::{get_parent_expr, is_allowed, match_function_call, method_calls, paths}; +use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::{BinOpKind, BorrowKind, Expr, ExprKind, LangItem, QPath}; use rustc_lint::{LateContext, LateLintPass, LintContext}; @@ -7,14 +13,6 @@ use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::source_map::Spanned; use rustc_span::sym; -use if_chain::if_chain; - -use crate::utils::SpanlessEq; -use crate::utils::{ - get_parent_expr, is_allowed, is_type_diagnostic_item, match_function_call, method_calls, paths, span_lint, - span_lint_and_help, span_lint_and_sugg, -}; - declare_clippy_lint! { /// **What it does:** Checks for string appends of the form `x = x + y` (without /// `let`!). @@ -121,7 +119,7 @@ impl<'tcx> LateLintPass<'tcx> for StringAdd { Spanned { node: BinOpKind::Add, .. }, - ref left, + left, _, ) = e.kind { @@ -129,7 +127,7 @@ impl<'tcx> LateLintPass<'tcx> for StringAdd { if !is_allowed(cx, STRING_ADD_ASSIGN, e.hir_id) { let parent = get_parent_expr(cx, e); if let Some(p) = parent { - if let ExprKind::Assign(ref target, _, _) = p.kind { + if let ExprKind::Assign(target, _, _) = p.kind { // avoid duplicate matches if SpanlessEq::new(cx).eq_expr(target, left) { return; @@ -144,7 +142,7 @@ impl<'tcx> LateLintPass<'tcx> for StringAdd { "you added something to a string. Consider using `String::push_str()` instead", ); } - } else if let ExprKind::Assign(ref target, ref src, _) = e.kind { + } else if let ExprKind::Assign(target, src, _) = e.kind { if is_string(cx, target) && is_add(cx, src, target) { span_lint( cx, @@ -168,10 +166,10 @@ fn is_add(cx: &LateContext<'_>, src: &Expr<'_>, target: &Expr<'_>) -> bool { Spanned { node: BinOpKind::Add, .. }, - ref left, + left, _, ) => SpanlessEq::new(cx).eq_expr(target, left), - ExprKind::Block(ref block, _) => { + ExprKind::Block(block, _) => { block.stmts.is_empty() && block.expr.as_ref().map_or(false, |expr| is_add(cx, expr, target)) }, _ => false, @@ -205,7 +203,6 @@ declare_lint_pass!(StringLitAsBytes => [STRING_LIT_AS_BYTES, STRING_FROM_UTF8_AS impl<'tcx> LateLintPass<'tcx> for StringLitAsBytes { fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) { - use crate::utils::{snippet, snippet_with_applicability}; use rustc_ast::LitKind; if_chain! { @@ -213,8 +210,8 @@ impl<'tcx> LateLintPass<'tcx> for StringLitAsBytes { if let Some(args) = match_function_call(cx, e, &paths::STR_FROM_UTF8); // Find string::as_bytes - if let ExprKind::AddrOf(BorrowKind::Ref, _, ref args) = args[0].kind; - if let ExprKind::Index(ref left, ref right) = args.kind; + if let ExprKind::AddrOf(BorrowKind::Ref, _, args) = args[0].kind; + if let ExprKind::Index(left, right) = args.kind; let (method_names, expressions, _) = method_calls(left, 1); if method_names.len() == 1; if expressions.len() == 1; @@ -287,6 +284,35 @@ impl<'tcx> LateLintPass<'tcx> for StringLitAsBytes { } } } + + if_chain! { + if let ExprKind::MethodCall(path, _, [recv], _) = &e.kind; + if path.ident.name == sym!(into_bytes); + if let ExprKind::MethodCall(path, _, [recv], _) = &recv.kind; + if matches!(&*path.ident.name.as_str(), "to_owned" | "to_string"); + if let ExprKind::Lit(lit) = &recv.kind; + if let LitKind::Str(lit_content, _) = &lit.node; + + if lit_content.as_str().is_ascii(); + if lit_content.as_str().len() <= MAX_LENGTH_BYTE_STRING_LIT; + if !recv.span.from_expansion(); + then { + let mut applicability = Applicability::MachineApplicable; + + span_lint_and_sugg( + cx, + STRING_LIT_AS_BYTES, + e.span, + "calling `into_bytes()` on a string literal", + "consider using a byte string literal instead", + format!( + "b{}.to_vec()", + snippet_with_applicability(cx, recv.span, r#""..""#, &mut applicability) + ), + applicability, + ); + } + } } } 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 cccd24ccf9401..cb2237e531262 100644 --- a/src/tools/clippy/clippy_lints/src/suspicious_operation_groupings.rs +++ b/src/tools/clippy/clippy_lints/src/suspicious_operation_groupings.rs @@ -1,5 +1,6 @@ -use crate::utils::ast_utils::{eq_id, is_useless_with_eq_exprs, IdentIter}; -use crate::utils::{snippet_with_applicability, span_lint_and_sugg}; +use clippy_utils::ast_utils::{eq_id, is_useless_with_eq_exprs, IdentIter}; +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::source::snippet_with_applicability; use core::ops::{Add, AddAssign}; use if_chain::if_chain; use rustc_ast::ast::{BinOpKind, Expr, ExprKind, StmtKind}; @@ -224,7 +225,7 @@ fn attempt_to_emit_no_difference_lint( emit_suggestion( cx, binop.span, - replace_left_sugg(cx, &binop, &sugg, &mut applicability), + replace_left_sugg(cx, binop, &sugg, &mut applicability), applicability, ); return; @@ -246,7 +247,7 @@ fn attempt_to_emit_no_difference_lint( emit_suggestion( cx, binop.span, - replace_right_sugg(cx, &binop, &sugg, &mut applicability), + replace_right_sugg(cx, binop, &sugg, &mut applicability), applicability, ); return; @@ -261,8 +262,8 @@ fn emit_suggestion(cx: &EarlyContext<'_>, span: Span, sugg: String, applicabilit cx, SUSPICIOUS_OPERATION_GROUPINGS, span, - "This sequence of operators looks suspiciously like a bug.", - "I think you meant", + "this sequence of operators looks suspiciously like a bug", + "did you mean", sugg, applicability, ) @@ -275,8 +276,8 @@ fn ident_swap_sugg( location: IdentLocation, applicability: &mut Applicability, ) -> Option<String> { - let left_ident = get_ident(&binop.left, location)?; - let right_ident = get_ident(&binop.right, location)?; + let left_ident = get_ident(binop.left, location)?; + let right_ident = get_ident(binop.right, location)?; let sugg = match ( paired_identifiers.contains(&left_ident), @@ -292,8 +293,7 @@ fn ident_swap_sugg( // ends up duplicating a clause, the `logic_bug` lint // should catch it. - let right_suggestion = - suggestion_with_swapped_ident(cx, &binop.right, location, left_ident, applicability)?; + let right_suggestion = suggestion_with_swapped_ident(cx, binop.right, location, left_ident, applicability)?; replace_right_sugg(cx, binop, &right_suggestion, applicability) }, @@ -301,15 +301,14 @@ fn ident_swap_sugg( // We haven't seen a pair involving the left one, so // it's probably what is wanted. - let right_suggestion = - suggestion_with_swapped_ident(cx, &binop.right, location, left_ident, applicability)?; + let right_suggestion = suggestion_with_swapped_ident(cx, binop.right, location, left_ident, applicability)?; replace_right_sugg(cx, binop, &right_suggestion, applicability) }, (true, false) => { // We haven't seen a pair involving the right one, so // it's probably what is wanted. - let left_suggestion = suggestion_with_swapped_ident(cx, &binop.left, location, right_ident, applicability)?; + let left_suggestion = suggestion_with_swapped_ident(cx, binop.left, location, right_ident, applicability)?; replace_left_sugg(cx, binop, &left_suggestion, applicability) }, @@ -564,7 +563,7 @@ fn ident_difference_expr_with_base_location( | (Try(_), Try(_)) | (Paren(_), Paren(_)) | (Repeat(_, _), Repeat(_, _)) - | (Struct(_, _, _), Struct(_, _, _)) + | (Struct(_), Struct(_)) | (MacCall(_), MacCall(_)) | (LlvmInlineAsm(_), LlvmInlineAsm(_)) | (InlineAsm(_), InlineAsm(_)) diff --git a/src/tools/clippy/clippy_lints/src/suspicious_trait_impl.rs b/src/tools/clippy/clippy_lints/src/suspicious_trait_impl.rs index 0b7d08cb1645a..512abde11a634 100644 --- a/src/tools/clippy/clippy_lints/src/suspicious_trait_impl.rs +++ b/src/tools/clippy/clippy_lints/src/suspicious_trait_impl.rs @@ -1,4 +1,5 @@ -use crate::utils::{get_trait_def_id, span_lint, trait_ref_of_method}; +use clippy_utils::diagnostics::span_lint; +use clippy_utils::{get_trait_def_id, paths, trait_ref_of_method}; use if_chain::if_chain; use rustc_hir as hir; use rustc_hir::intravisit::{walk_expr, NestedVisitorMap, Visitor}; @@ -67,14 +68,13 @@ impl<'tcx> LateLintPass<'tcx> for SuspiciousImpl { // Check for more than one binary operation in the implemented function // Linting when multiple operations are involved can result in false positives + let parent_fn = cx.tcx.hir().get_parent_item(expr.hir_id); if_chain! { - let parent_fn = cx.tcx.hir().get_parent_item(expr.hir_id); if let hir::Node::ImplItem(impl_item) = cx.tcx.hir().get(parent_fn); if let hir::ImplItemKind::Fn(_, body_id) = impl_item.kind; - let body = cx.tcx.hir().body(body_id); - let mut visitor = BinaryExprVisitor { nb_binops: 0 }; - then { + let body = cx.tcx.hir().body(body_id); + let mut visitor = BinaryExprVisitor { nb_binops: 0 }; walk_expr(&mut visitor, &body.value); if visitor.nb_binops > 1 { return; @@ -158,7 +158,7 @@ fn check_binop( expected_ops: &[hir::BinOpKind], ) -> Option<&'static str> { let mut trait_ids = vec![]; - let [krate, module] = crate::utils::paths::OPS_MODULE; + let [krate, module] = paths::OPS_MODULE; for &t in traits { let path = [krate, module, t]; diff --git a/src/tools/clippy/clippy_lints/src/swap.rs b/src/tools/clippy/clippy_lints/src/swap.rs index 9d8a0c248334f..19967e2c97014 100644 --- a/src/tools/clippy/clippy_lints/src/swap.rs +++ b/src/tools/clippy/clippy_lints/src/swap.rs @@ -1,7 +1,8 @@ -use crate::utils::sugg::Sugg; -use crate::utils::{ - differing_macro_contexts, eq_expr_value, is_type_diagnostic_item, snippet_with_applicability, span_lint_and_then, -}; +use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::source::snippet_with_applicability; +use clippy_utils::sugg::Sugg; +use clippy_utils::ty::is_type_diagnostic_item; +use clippy_utils::{differing_macro_contexts, eq_expr_value}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::{Block, Expr, ExprKind, PatKind, QPath, StmtKind}; @@ -77,26 +78,26 @@ fn check_manual_swap(cx: &LateContext<'_>, block: &Block<'_>) { for w in block.stmts.windows(3) { if_chain! { // let t = foo(); - if let StmtKind::Local(ref tmp) = w[0].kind; - if let Some(ref tmp_init) = tmp.init; + if let StmtKind::Local(tmp) = w[0].kind; + if let Some(tmp_init) = tmp.init; if let PatKind::Binding(.., ident, None) = tmp.pat.kind; // foo() = bar(); - if let StmtKind::Semi(ref first) = w[1].kind; - if let ExprKind::Assign(ref lhs1, ref rhs1, _) = first.kind; + if let StmtKind::Semi(first) = w[1].kind; + if let ExprKind::Assign(lhs1, rhs1, _) = first.kind; // bar() = t; - if let StmtKind::Semi(ref second) = w[2].kind; - if let ExprKind::Assign(ref lhs2, ref rhs2, _) = second.kind; - if let ExprKind::Path(QPath::Resolved(None, ref rhs2)) = rhs2.kind; + if let StmtKind::Semi(second) = w[2].kind; + if let ExprKind::Assign(lhs2, rhs2, _) = second.kind; + if let ExprKind::Path(QPath::Resolved(None, rhs2)) = rhs2.kind; if rhs2.segments.len() == 1; if ident.name == rhs2.segments[0].ident.name; if eq_expr_value(cx, tmp_init, lhs1); if eq_expr_value(cx, rhs1, lhs2); then { - if let ExprKind::Field(ref lhs1, _) = lhs1.kind { - if let ExprKind::Field(ref lhs2, _) = lhs2.kind { + if let ExprKind::Field(lhs1, _) = lhs1.kind { + if let ExprKind::Field(lhs2, _) = lhs2.kind { if lhs1.hir_id.owner == lhs2.hir_id.owner { return; } @@ -191,8 +192,8 @@ enum Slice<'a> { /// Checks if both expressions are index operations into "slice-like" types. fn check_for_slice<'a>(cx: &LateContext<'_>, lhs1: &'a Expr<'_>, lhs2: &'a Expr<'_>) -> Slice<'a> { - if let ExprKind::Index(ref lhs1, ref idx1) = lhs1.kind { - if let ExprKind::Index(ref lhs2, ref idx2) = lhs2.kind { + if let ExprKind::Index(lhs1, idx1) = lhs1.kind { + if let ExprKind::Index(lhs2, idx2) = lhs2.kind { if eq_expr_value(cx, lhs1, lhs2) { let ty = cx.typeck_results().expr_ty(lhs1).peel_refs(); @@ -216,11 +217,11 @@ fn check_for_slice<'a>(cx: &LateContext<'_>, lhs1: &'a Expr<'_>, lhs2: &'a Expr< fn check_suspicious_swap(cx: &LateContext<'_>, block: &Block<'_>) { for w in block.stmts.windows(2) { if_chain! { - if let StmtKind::Semi(ref first) = w[0].kind; - if let StmtKind::Semi(ref second) = w[1].kind; + if let StmtKind::Semi(first) = w[0].kind; + if let StmtKind::Semi(second) = w[1].kind; if !differing_macro_contexts(first.span, second.span); - if let ExprKind::Assign(ref lhs0, ref rhs0, _) = first.kind; - if let ExprKind::Assign(ref lhs1, ref rhs1, _) = second.kind; + if let ExprKind::Assign(lhs0, rhs0, _) = first.kind; + if let ExprKind::Assign(lhs1, rhs1, _) = second.kind; if eq_expr_value(cx, lhs0, rhs1); if eq_expr_value(cx, lhs1, rhs0); then { diff --git a/src/tools/clippy/clippy_lints/src/tabs_in_doc_comments.rs b/src/tools/clippy/clippy_lints/src/tabs_in_doc_comments.rs index 74ccd9235de85..a0492a88f912a 100644 --- a/src/tools/clippy/clippy_lints/src/tabs_in_doc_comments.rs +++ b/src/tools/clippy/clippy_lints/src/tabs_in_doc_comments.rs @@ -1,4 +1,4 @@ -use crate::utils::span_lint_and_sugg; +use clippy_utils::diagnostics::span_lint_and_sugg; use rustc_ast::ast; use rustc_errors::Applicability; use rustc_lint::{EarlyContext, EarlyLintPass}; @@ -86,7 +86,7 @@ impl TabsInDocComments { impl EarlyLintPass for TabsInDocComments { fn check_attribute(&mut self, cx: &EarlyContext<'_>, attribute: &ast::Attribute) { - Self::warn_if_tabs_in_doc(cx, &attribute); + Self::warn_if_tabs_in_doc(cx, attribute); } } diff --git a/src/tools/clippy/clippy_lints/src/temporary_assignment.rs b/src/tools/clippy/clippy_lints/src/temporary_assignment.rs index fb891866364cc..8ef25dc816c65 100644 --- a/src/tools/clippy/clippy_lints/src/temporary_assignment.rs +++ b/src/tools/clippy/clippy_lints/src/temporary_assignment.rs @@ -1,4 +1,5 @@ -use crate::utils::{is_adjusted, span_lint}; +use clippy_utils::diagnostics::span_lint; +use clippy_utils::is_adjusted; use rustc_hir::{Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; diff --git a/src/tools/clippy/clippy_lints/src/to_digit_is_some.rs b/src/tools/clippy/clippy_lints/src/to_digit_is_some.rs index eeda39bfa2087..c66a596c78461 100644 --- a/src/tools/clippy/clippy_lints/src/to_digit_is_some.rs +++ b/src/tools/clippy/clippy_lints/src/to_digit_is_some.rs @@ -1,4 +1,6 @@ -use crate::utils::{match_def_path, snippet_with_applicability, span_lint_and_sugg}; +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::match_def_path; +use clippy_utils::source::snippet_with_applicability; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir as hir; diff --git a/src/tools/clippy/clippy_lints/src/to_string_in_display.rs b/src/tools/clippy/clippy_lints/src/to_string_in_display.rs index fdd105e624605..ae05a8da37bc9 100644 --- a/src/tools/clippy/clippy_lints/src/to_string_in_display.rs +++ b/src/tools/clippy/clippy_lints/src/to_string_in_display.rs @@ -1,8 +1,10 @@ -use crate::utils::{match_def_path, match_trait_method, path_to_local_id, paths, span_lint}; +use clippy_utils::diagnostics::span_lint; +use clippy_utils::{is_diagnostic_assoc_item, match_def_path, path_to_local_id, paths}; use if_chain::if_chain; use rustc_hir::{Expr, ExprKind, HirId, Impl, ImplItem, ImplItemKind, Item, ItemKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_tool_lint, impl_lint_pass}; +use rustc_span::symbol::sym; declare_clippy_lint! { /// **What it does:** Checks for uses of `to_string()` in `Display` traits. @@ -90,9 +92,10 @@ impl LateLintPass<'_> for ToStringInDisplay { if_chain! { if self.in_display_impl; if let Some(self_hir_id) = self.self_hir_id; - if let ExprKind::MethodCall(ref path, _, args, _) = expr.kind; + if let ExprKind::MethodCall(path, _, args, _) = expr.kind; if path.ident.name == sym!(to_string); - if match_trait_method(cx, expr, &paths::TO_STRING); + if let Some(expr_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id); + if is_diagnostic_assoc_item(cx, expr_def_id, sym::ToString); if path_to_local_id(&args[0], self_hir_id); then { span_lint( diff --git a/src/tools/clippy/clippy_lints/src/trait_bounds.rs b/src/tools/clippy/clippy_lints/src/trait_bounds.rs index daff5f81e8c34..b0589b0512ef5 100644 --- a/src/tools/clippy/clippy_lints/src/trait_bounds.rs +++ b/src/tools/clippy/clippy_lints/src/trait_bounds.rs @@ -1,4 +1,6 @@ -use crate::utils::{in_macro, snippet, snippet_with_applicability, span_lint_and_help, SpanlessHash}; +use clippy_utils::diagnostics::span_lint_and_help; +use clippy_utils::source::{snippet, snippet_with_applicability}; +use clippy_utils::{in_macro, SpanlessHash}; use if_chain::if_chain; use rustc_data_structures::fx::FxHashMap; use rustc_errors::Applicability; @@ -105,7 +107,7 @@ impl TraitBounds { if let WherePredicate::BoundPredicate(ref p) = bound; if p.bounds.len() as u64 <= self.max_trait_bounds; if !in_macro(p.span); - let h = hash(&p.bounded_ty); + let h = hash(p.bounded_ty); if let Some(ref v) = map.insert(h, p.bounds.iter().collect::<Vec<_>>()); then { @@ -168,7 +170,7 @@ fn check_trait_bound_duplication(cx: &LateContext<'_>, gen: &'_ Generics<'_>) { if_chain! { if let WherePredicate::BoundPredicate(ref bound_predicate) = predicate; if !in_macro(bound_predicate.span); - if let TyKind::Path(QPath::Resolved(_, Path { ref segments, .. })) = bound_predicate.bounded_ty.kind; + if let TyKind::Path(QPath::Resolved(_, Path { segments, .. })) = bound_predicate.bounded_ty.kind; if let Some(segment) = segments.first(); if let Some(trait_resolutions_direct) = map.get(&segment.ident); then { diff --git a/src/tools/clippy/clippy_lints/src/transmute.rs b/src/tools/clippy/clippy_lints/src/transmute.rs deleted file mode 100644 index dc938ed02383d..0000000000000 --- a/src/tools/clippy/clippy_lints/src/transmute.rs +++ /dev/null @@ -1,763 +0,0 @@ -use crate::utils::{ - in_constant, is_normalizable, last_path_segment, match_def_path, paths, snippet, span_lint, span_lint_and_sugg, - span_lint_and_then, sugg, -}; -use if_chain::if_chain; -use rustc_ast as ast; -use rustc_errors::Applicability; -use rustc_hir::{Expr, ExprKind, GenericArg, Mutability, QPath, TyKind, UnOp}; -use rustc_lint::{LateContext, LateLintPass}; -use rustc_middle::ty::{self, cast::CastKind, Ty}; -use rustc_session::{declare_lint_pass, declare_tool_lint}; -use rustc_span::DUMMY_SP; -use rustc_typeck::check::{cast::CastCheck, FnCtxt, Inherited}; -use std::borrow::Cow; - -declare_clippy_lint! { - /// **What it does:** Checks for transmutes that can't ever be correct on any - /// architecture. - /// - /// **Why is this bad?** It's basically guaranteed to be undefined behaviour. - /// - /// **Known problems:** When accessing C, users might want to store pointer - /// sized objects in `extradata` arguments to save an allocation. - /// - /// **Example:** - /// ```ignore - /// let ptr: *const T = core::intrinsics::transmute('x') - /// ``` - pub WRONG_TRANSMUTE, - correctness, - "transmutes that are confusing at best, undefined behaviour at worst and always useless" -} - -// FIXME: Move this to `complexity` again, after #5343 is fixed -declare_clippy_lint! { - /// **What it does:** Checks for transmutes to the original type of the object - /// and transmutes that could be a cast. - /// - /// **Why is this bad?** Readability. The code tricks people into thinking that - /// something complex is going on. - /// - /// **Known problems:** None. - /// - /// **Example:** - /// ```rust,ignore - /// core::intrinsics::transmute(t); // where the result type is the same as `t`'s - /// ``` - pub USELESS_TRANSMUTE, - nursery, - "transmutes that have the same to and from types or could be a cast/coercion" -} - -// FIXME: Merge this lint with USELESS_TRANSMUTE once that is out of the nursery. -declare_clippy_lint! { - /// **What it does:**Checks for transmutes that could be a pointer cast. - /// - /// **Why is this bad?** Readability. The code tricks people into thinking that - /// something complex is going on. - /// - /// **Known problems:** None. - /// - /// **Example:** - /// - /// ```rust - /// # let p: *const [i32] = &[]; - /// unsafe { std::mem::transmute::<*const [i32], *const [u16]>(p) }; - /// ``` - /// Use instead: - /// ```rust - /// # let p: *const [i32] = &[]; - /// p as *const [u16]; - /// ``` - pub TRANSMUTES_EXPRESSIBLE_AS_PTR_CASTS, - complexity, - "transmutes that could be a pointer cast" -} - -declare_clippy_lint! { - /// **What it does:** Checks for transmutes between a type `T` and `*T`. - /// - /// **Why is this bad?** It's easy to mistakenly transmute between a type and a - /// pointer to that type. - /// - /// **Known problems:** None. - /// - /// **Example:** - /// ```rust,ignore - /// core::intrinsics::transmute(t) // where the result type is the same as - /// // `*t` or `&t`'s - /// ``` - pub CROSSPOINTER_TRANSMUTE, - complexity, - "transmutes that have to or from types that are a pointer to the other" -} - -declare_clippy_lint! { - /// **What it does:** Checks for transmutes from a pointer to a reference. - /// - /// **Why is this bad?** This can always be rewritten with `&` and `*`. - /// - /// **Known problems:** - /// - `mem::transmute` in statics and constants is stable from Rust 1.46.0, - /// while dereferencing raw pointer is not stable yet. - /// If you need to do this in those places, - /// you would have to use `transmute` instead. - /// - /// **Example:** - /// ```rust,ignore - /// unsafe { - /// let _: &T = std::mem::transmute(p); // where p: *const T - /// } - /// - /// // can be written: - /// let _: &T = &*p; - /// ``` - pub TRANSMUTE_PTR_TO_REF, - complexity, - "transmutes from a pointer to a reference type" -} - -declare_clippy_lint! { - /// **What it does:** Checks for transmutes from an integer to a `char`. - /// - /// **Why is this bad?** Not every integer is a Unicode scalar value. - /// - /// **Known problems:** - /// - [`from_u32`] which this lint suggests using is slower than `transmute` - /// as it needs to validate the input. - /// If you are certain that the input is always a valid Unicode scalar value, - /// use [`from_u32_unchecked`] which is as fast as `transmute` - /// but has a semantically meaningful name. - /// - You might want to handle `None` returned from [`from_u32`] instead of calling `unwrap`. - /// - /// [`from_u32`]: https://doc.rust-lang.org/std/char/fn.from_u32.html - /// [`from_u32_unchecked`]: https://doc.rust-lang.org/std/char/fn.from_u32_unchecked.html - /// - /// **Example:** - /// ```rust - /// let x = 1_u32; - /// unsafe { - /// let _: char = std::mem::transmute(x); // where x: u32 - /// } - /// - /// // should be: - /// let _ = std::char::from_u32(x).unwrap(); - /// ``` - pub TRANSMUTE_INT_TO_CHAR, - complexity, - "transmutes from an integer to a `char`" -} - -declare_clippy_lint! { - /// **What it does:** Checks for transmutes from a `&[u8]` to a `&str`. - /// - /// **Why is this bad?** Not every byte slice is a valid UTF-8 string. - /// - /// **Known problems:** - /// - [`from_utf8`] which this lint suggests using is slower than `transmute` - /// as it needs to validate the input. - /// If you are certain that the input is always a valid UTF-8, - /// use [`from_utf8_unchecked`] which is as fast as `transmute` - /// but has a semantically meaningful name. - /// - You might want to handle errors returned from [`from_utf8`] instead of calling `unwrap`. - /// - /// [`from_utf8`]: https://doc.rust-lang.org/std/str/fn.from_utf8.html - /// [`from_utf8_unchecked`]: https://doc.rust-lang.org/std/str/fn.from_utf8_unchecked.html - /// - /// **Example:** - /// ```rust - /// let b: &[u8] = &[1_u8, 2_u8]; - /// unsafe { - /// let _: &str = std::mem::transmute(b); // where b: &[u8] - /// } - /// - /// // should be: - /// let _ = std::str::from_utf8(b).unwrap(); - /// ``` - pub TRANSMUTE_BYTES_TO_STR, - complexity, - "transmutes from a `&[u8]` to a `&str`" -} - -declare_clippy_lint! { - /// **What it does:** Checks for transmutes from an integer to a `bool`. - /// - /// **Why is this bad?** This might result in an invalid in-memory representation of a `bool`. - /// - /// **Known problems:** None. - /// - /// **Example:** - /// ```rust - /// let x = 1_u8; - /// unsafe { - /// let _: bool = std::mem::transmute(x); // where x: u8 - /// } - /// - /// // should be: - /// let _: bool = x != 0; - /// ``` - pub TRANSMUTE_INT_TO_BOOL, - complexity, - "transmutes from an integer to a `bool`" -} - -declare_clippy_lint! { - /// **What it does:** Checks for transmutes from an integer to a float. - /// - /// **Why is this bad?** Transmutes are dangerous and error-prone, whereas `from_bits` is intuitive - /// and safe. - /// - /// **Known problems:** None. - /// - /// **Example:** - /// ```rust - /// unsafe { - /// let _: f32 = std::mem::transmute(1_u32); // where x: u32 - /// } - /// - /// // should be: - /// let _: f32 = f32::from_bits(1_u32); - /// ``` - pub TRANSMUTE_INT_TO_FLOAT, - complexity, - "transmutes from an integer to a float" -} - -declare_clippy_lint! { - /// **What it does:** Checks for transmutes from a float to an integer. - /// - /// **Why is this bad?** Transmutes are dangerous and error-prone, whereas `to_bits` is intuitive - /// and safe. - /// - /// **Known problems:** None. - /// - /// **Example:** - /// ```rust - /// unsafe { - /// let _: u32 = std::mem::transmute(1f32); - /// } - /// - /// // should be: - /// let _: u32 = 1f32.to_bits(); - /// ``` - pub TRANSMUTE_FLOAT_TO_INT, - complexity, - "transmutes from a float to an integer" -} - -declare_clippy_lint! { - /// **What it does:** Checks for transmutes from a pointer to a pointer, or - /// from a reference to a reference. - /// - /// **Why is this bad?** Transmutes are dangerous, and these can instead be - /// written as casts. - /// - /// **Known problems:** None. - /// - /// **Example:** - /// ```rust - /// let ptr = &1u32 as *const u32; - /// unsafe { - /// // pointer-to-pointer transmute - /// let _: *const f32 = std::mem::transmute(ptr); - /// // ref-ref transmute - /// let _: &f32 = std::mem::transmute(&1u32); - /// } - /// // These can be respectively written: - /// let _ = ptr as *const f32; - /// let _ = unsafe{ &*(&1u32 as *const u32 as *const f32) }; - /// ``` - pub TRANSMUTE_PTR_TO_PTR, - complexity, - "transmutes from a pointer to a pointer / a reference to a reference" -} - -declare_clippy_lint! { - /// **What it does:** Checks for transmutes between collections whose - /// types have different ABI, size or alignment. - /// - /// **Why is this bad?** This is undefined behavior. - /// - /// **Known problems:** Currently, we cannot know whether a type is a - /// collection, so we just lint the ones that come with `std`. - /// - /// **Example:** - /// ```rust - /// // different size, therefore likely out-of-bounds memory access - /// // You absolutely do not want this in your code! - /// unsafe { - /// std::mem::transmute::<_, Vec<u32>>(vec![2_u16]) - /// }; - /// ``` - /// - /// You must always iterate, map and collect the values: - /// - /// ```rust - /// vec![2_u16].into_iter().map(u32::from).collect::<Vec<_>>(); - /// ``` - pub UNSOUND_COLLECTION_TRANSMUTE, - correctness, - "transmute between collections of layout-incompatible types" -} - -declare_lint_pass!(Transmute => [ - CROSSPOINTER_TRANSMUTE, - TRANSMUTE_PTR_TO_REF, - TRANSMUTE_PTR_TO_PTR, - USELESS_TRANSMUTE, - WRONG_TRANSMUTE, - TRANSMUTE_INT_TO_CHAR, - TRANSMUTE_BYTES_TO_STR, - TRANSMUTE_INT_TO_BOOL, - TRANSMUTE_INT_TO_FLOAT, - TRANSMUTE_FLOAT_TO_INT, - UNSOUND_COLLECTION_TRANSMUTE, - TRANSMUTES_EXPRESSIBLE_AS_PTR_CASTS, -]); - -// used to check for UNSOUND_COLLECTION_TRANSMUTE -static COLLECTIONS: &[&[&str]] = &[ - &paths::VEC, - &paths::VEC_DEQUE, - &paths::BINARY_HEAP, - &paths::BTREESET, - &paths::BTREEMAP, - &paths::HASHSET, - &paths::HASHMAP, -]; -impl<'tcx> LateLintPass<'tcx> for Transmute { - #[allow(clippy::similar_names, clippy::too_many_lines)] - fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) { - if_chain! { - if let ExprKind::Call(ref path_expr, ref args) = e.kind; - if let ExprKind::Path(ref qpath) = path_expr.kind; - if let Some(def_id) = cx.qpath_res(qpath, path_expr.hir_id).opt_def_id(); - if match_def_path(cx, def_id, &paths::TRANSMUTE); - then { - // Avoid suggesting from/to bits and dereferencing raw pointers in const contexts. - // See https://github.com/rust-lang/rust/issues/73736 for progress on making them `const fn`. - // And see https://github.com/rust-lang/rust/issues/51911 for dereferencing raw pointers. - let const_context = in_constant(cx, e.hir_id); - - let from_ty = cx.typeck_results().expr_ty(&args[0]); - let to_ty = cx.typeck_results().expr_ty(e); - - match (&from_ty.kind(), &to_ty.kind()) { - _ if from_ty == to_ty => span_lint( - cx, - USELESS_TRANSMUTE, - e.span, - &format!("transmute from a type (`{}`) to itself", from_ty), - ), - (ty::Ref(_, rty, rty_mutbl), ty::RawPtr(ptr_ty)) => span_lint_and_then( - cx, - USELESS_TRANSMUTE, - e.span, - "transmute from a reference to a pointer", - |diag| { - if let Some(arg) = sugg::Sugg::hir_opt(cx, &args[0]) { - let rty_and_mut = ty::TypeAndMut { - ty: rty, - mutbl: *rty_mutbl, - }; - - let sugg = if *ptr_ty == rty_and_mut { - arg.as_ty(to_ty) - } else { - arg.as_ty(cx.tcx.mk_ptr(rty_and_mut)).as_ty(to_ty) - }; - - diag.span_suggestion(e.span, "try", sugg.to_string(), Applicability::Unspecified); - } - }, - ), - (ty::Int(_) | ty::Uint(_), ty::RawPtr(_)) => span_lint_and_then( - cx, - USELESS_TRANSMUTE, - e.span, - "transmute from an integer to a pointer", - |diag| { - if let Some(arg) = sugg::Sugg::hir_opt(cx, &args[0]) { - diag.span_suggestion( - e.span, - "try", - arg.as_ty(&to_ty.to_string()).to_string(), - Applicability::Unspecified, - ); - } - }, - ), - (ty::Float(_) | ty::Char, ty::Ref(..) | ty::RawPtr(_)) => span_lint( - cx, - WRONG_TRANSMUTE, - e.span, - &format!("transmute from a `{}` to a pointer", from_ty), - ), - (ty::RawPtr(from_ptr), _) if from_ptr.ty == to_ty => span_lint( - cx, - CROSSPOINTER_TRANSMUTE, - e.span, - &format!( - "transmute from a type (`{}`) to the type that it points to (`{}`)", - from_ty, to_ty - ), - ), - (_, ty::RawPtr(to_ptr)) if to_ptr.ty == from_ty => span_lint( - cx, - CROSSPOINTER_TRANSMUTE, - e.span, - &format!( - "transmute from a type (`{}`) to a pointer to that type (`{}`)", - from_ty, to_ty - ), - ), - (ty::RawPtr(from_pty), ty::Ref(_, to_ref_ty, mutbl)) => span_lint_and_then( - cx, - TRANSMUTE_PTR_TO_REF, - e.span, - &format!( - "transmute from a pointer type (`{}`) to a reference type \ - (`{}`)", - from_ty, to_ty - ), - |diag| { - let arg = sugg::Sugg::hir(cx, &args[0], ".."); - let (deref, cast) = if *mutbl == Mutability::Mut { - ("&mut *", "*mut") - } else { - ("&*", "*const") - }; - - let arg = if from_pty.ty == *to_ref_ty { - arg - } else { - arg.as_ty(&format!("{} {}", cast, get_type_snippet(cx, qpath, to_ref_ty))) - }; - - diag.span_suggestion( - e.span, - "try", - sugg::make_unop(deref, arg).to_string(), - Applicability::Unspecified, - ); - }, - ), - (ty::Int(ty::IntTy::I32) | ty::Uint(ty::UintTy::U32), &ty::Char) => { - span_lint_and_then( - cx, - TRANSMUTE_INT_TO_CHAR, - e.span, - &format!("transmute from a `{}` to a `char`", from_ty), - |diag| { - let arg = sugg::Sugg::hir(cx, &args[0], ".."); - let arg = if let ty::Int(_) = from_ty.kind() { - arg.as_ty(ast::UintTy::U32.name_str()) - } else { - arg - }; - diag.span_suggestion( - e.span, - "consider using", - format!("std::char::from_u32({}).unwrap()", arg.to_string()), - Applicability::Unspecified, - ); - }, - ) - }, - (ty::Ref(_, ty_from, from_mutbl), ty::Ref(_, ty_to, to_mutbl)) => { - if_chain! { - if let (&ty::Slice(slice_ty), &ty::Str) = (&ty_from.kind(), &ty_to.kind()); - if let ty::Uint(ty::UintTy::U8) = slice_ty.kind(); - if from_mutbl == to_mutbl; - then { - let postfix = if *from_mutbl == Mutability::Mut { - "_mut" - } else { - "" - }; - - span_lint_and_sugg( - cx, - TRANSMUTE_BYTES_TO_STR, - e.span, - &format!("transmute from a `{}` to a `{}`", from_ty, to_ty), - "consider using", - format!( - "std::str::from_utf8{}({}).unwrap()", - postfix, - snippet(cx, args[0].span, ".."), - ), - Applicability::Unspecified, - ); - } else { - if (cx.tcx.erase_regions(from_ty) != cx.tcx.erase_regions(to_ty)) - && !const_context { - span_lint_and_then( - cx, - TRANSMUTE_PTR_TO_PTR, - e.span, - "transmute from a reference to a reference", - |diag| if let Some(arg) = sugg::Sugg::hir_opt(cx, &args[0]) { - let ty_from_and_mut = ty::TypeAndMut { - ty: ty_from, - mutbl: *from_mutbl - }; - let ty_to_and_mut = ty::TypeAndMut { ty: ty_to, mutbl: *to_mutbl }; - let sugg_paren = arg - .as_ty(cx.tcx.mk_ptr(ty_from_and_mut)) - .as_ty(cx.tcx.mk_ptr(ty_to_and_mut)); - let sugg = if *to_mutbl == Mutability::Mut { - sugg_paren.mut_addr_deref() - } else { - sugg_paren.addr_deref() - }; - diag.span_suggestion( - e.span, - "try", - sugg.to_string(), - Applicability::Unspecified, - ); - }, - ) - } - } - } - }, - (ty::RawPtr(_), ty::RawPtr(to_ty)) => span_lint_and_then( - cx, - TRANSMUTE_PTR_TO_PTR, - e.span, - "transmute from a pointer to a pointer", - |diag| { - if let Some(arg) = sugg::Sugg::hir_opt(cx, &args[0]) { - let sugg = arg.as_ty(cx.tcx.mk_ptr(*to_ty)); - diag.span_suggestion(e.span, "try", sugg.to_string(), Applicability::Unspecified); - } - }, - ), - (ty::Int(ty::IntTy::I8) | ty::Uint(ty::UintTy::U8), ty::Bool) => { - span_lint_and_then( - cx, - TRANSMUTE_INT_TO_BOOL, - e.span, - &format!("transmute from a `{}` to a `bool`", from_ty), - |diag| { - let arg = sugg::Sugg::hir(cx, &args[0], ".."); - let zero = sugg::Sugg::NonParen(Cow::from("0")); - diag.span_suggestion( - e.span, - "consider using", - sugg::make_binop(ast::BinOpKind::Ne, &arg, &zero).to_string(), - Applicability::Unspecified, - ); - }, - ) - }, - (ty::Int(_) | ty::Uint(_), ty::Float(_)) if !const_context => span_lint_and_then( - cx, - TRANSMUTE_INT_TO_FLOAT, - e.span, - &format!("transmute from a `{}` to a `{}`", from_ty, to_ty), - |diag| { - let arg = sugg::Sugg::hir(cx, &args[0], ".."); - let arg = if let ty::Int(int_ty) = from_ty.kind() { - arg.as_ty(format!( - "u{}", - int_ty.bit_width().map_or_else(|| "size".to_string(), |v| v.to_string()) - )) - } else { - arg - }; - diag.span_suggestion( - e.span, - "consider using", - format!("{}::from_bits({})", to_ty, arg.to_string()), - Applicability::Unspecified, - ); - }, - ), - (ty::Float(float_ty), ty::Int(_) | ty::Uint(_)) if !const_context => span_lint_and_then( - cx, - TRANSMUTE_FLOAT_TO_INT, - e.span, - &format!("transmute from a `{}` to a `{}`", from_ty, to_ty), - |diag| { - let mut expr = &args[0]; - let mut arg = sugg::Sugg::hir(cx, expr, ".."); - - if let ExprKind::Unary(UnOp::Neg, inner_expr) = &expr.kind { - expr = &inner_expr; - } - - if_chain! { - // if the expression is a float literal and it is unsuffixed then - // add a suffix so the suggestion is valid and unambiguous - let op = format!("{}{}", arg, float_ty.name_str()).into(); - if let ExprKind::Lit(lit) = &expr.kind; - if let ast::LitKind::Float(_, ast::LitFloatType::Unsuffixed) = lit.node; - then { - match arg { - sugg::Sugg::MaybeParen(_) => arg = sugg::Sugg::MaybeParen(op), - _ => arg = sugg::Sugg::NonParen(op) - } - } - } - - arg = sugg::Sugg::NonParen(format!("{}.to_bits()", arg.maybe_par()).into()); - - // cast the result of `to_bits` if `to_ty` is signed - arg = if let ty::Int(int_ty) = to_ty.kind() { - arg.as_ty(int_ty.name_str().to_string()) - } else { - arg - }; - - diag.span_suggestion( - e.span, - "consider using", - arg.to_string(), - Applicability::Unspecified, - ); - }, - ), - (ty::Adt(from_adt, from_substs), ty::Adt(to_adt, to_substs)) => { - if from_adt.did != to_adt.did || - !COLLECTIONS.iter().any(|path| match_def_path(cx, to_adt.did, path)) { - return; - } - if from_substs.types().zip(to_substs.types()) - .any(|(from_ty, to_ty)| is_layout_incompatible(cx, from_ty, to_ty)) { - span_lint( - cx, - UNSOUND_COLLECTION_TRANSMUTE, - e.span, - &format!( - "transmute from `{}` to `{}` with mismatched layout is unsound", - from_ty, - to_ty - ) - ); - } - }, - (_, _) if can_be_expressed_as_pointer_cast(cx, e, from_ty, to_ty) => span_lint_and_then( - cx, - TRANSMUTES_EXPRESSIBLE_AS_PTR_CASTS, - e.span, - &format!( - "transmute from `{}` to `{}` which could be expressed as a pointer cast instead", - from_ty, - to_ty - ), - |diag| { - if let Some(arg) = sugg::Sugg::hir_opt(cx, &args[0]) { - let sugg = arg.as_ty(&to_ty.to_string()).to_string(); - diag.span_suggestion(e.span, "try", sugg, Applicability::MachineApplicable); - } - } - ), - _ => { - return; - }, - } - } - } - } -} - -/// Gets the snippet of `Bar` in `…::transmute<Foo, &Bar>`. If that snippet is -/// not available , use -/// the type's `ToString` implementation. In weird cases it could lead to types -/// with invalid `'_` -/// lifetime, but it should be rare. -fn get_type_snippet(cx: &LateContext<'_>, path: &QPath<'_>, to_ref_ty: Ty<'_>) -> String { - let seg = last_path_segment(path); - if_chain! { - if let Some(ref params) = seg.args; - if !params.parenthesized; - if let Some(to_ty) = params.args.iter().filter_map(|arg| match arg { - GenericArg::Type(ty) => Some(ty), - _ => None, - }).nth(1); - if let TyKind::Rptr(_, ref to_ty) = to_ty.kind; - then { - return snippet(cx, to_ty.ty.span, &to_ref_ty.to_string()).to_string(); - } - } - - to_ref_ty.to_string() -} - -// check if the component types of the transmuted collection and the result have different ABI, -// size or alignment -fn is_layout_incompatible<'tcx>(cx: &LateContext<'tcx>, from: Ty<'tcx>, to: Ty<'tcx>) -> bool { - let empty_param_env = ty::ParamEnv::empty(); - // check if `from` and `to` are normalizable to avoid ICE (#4968) - if !(is_normalizable(cx, empty_param_env, from) && is_normalizable(cx, empty_param_env, to)) { - return false; - } - let from_ty_layout = cx.tcx.layout_of(empty_param_env.and(from)); - let to_ty_layout = cx.tcx.layout_of(empty_param_env.and(to)); - if let (Ok(from_layout), Ok(to_layout)) = (from_ty_layout, to_ty_layout) { - from_layout.size != to_layout.size || from_layout.align != to_layout.align || from_layout.abi != to_layout.abi - } else { - // no idea about layout, so don't lint - false - } -} - -/// Check if the type conversion can be expressed as a pointer cast, instead of -/// a transmute. In certain cases, including some invalid casts from array -/// references to pointers, this may cause additional errors to be emitted and/or -/// ICE error messages. This function will panic if that occurs. -fn can_be_expressed_as_pointer_cast<'tcx>( - cx: &LateContext<'tcx>, - e: &'tcx Expr<'_>, - from_ty: Ty<'tcx>, - to_ty: Ty<'tcx>, -) -> bool { - use CastKind::{AddrPtrCast, ArrayPtrCast, FnPtrAddrCast, FnPtrPtrCast, PtrAddrCast, PtrPtrCast}; - matches!( - check_cast(cx, e, from_ty, to_ty), - Some(PtrPtrCast | PtrAddrCast | AddrPtrCast | ArrayPtrCast | FnPtrPtrCast | FnPtrAddrCast) - ) -} - -/// If a cast from `from_ty` to `to_ty` is valid, returns an Ok containing the kind of -/// the cast. In certain cases, including some invalid casts from array references -/// to pointers, this may cause additional errors to be emitted and/or ICE error -/// messages. This function will panic if that occurs. -fn check_cast<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>, from_ty: Ty<'tcx>, to_ty: Ty<'tcx>) -> Option<CastKind> { - let hir_id = e.hir_id; - let local_def_id = hir_id.owner; - - Inherited::build(cx.tcx, local_def_id).enter(|inherited| { - let fn_ctxt = FnCtxt::new(&inherited, cx.param_env, hir_id); - - // If we already have errors, we can't be sure we can pointer cast. - assert!( - !fn_ctxt.errors_reported_since_creation(), - "Newly created FnCtxt contained errors" - ); - - if let Ok(check) = CastCheck::new( - &fn_ctxt, e, from_ty, to_ty, - // We won't show any error to the user, so we don't care what the span is here. - DUMMY_SP, DUMMY_SP, - ) { - let res = check.do_check(&fn_ctxt); - - // do_check's documentation says that it might return Ok and create - // errors in the fcx instead of returing Err in some cases. Those cases - // should be filtered out before getting here. - assert!( - !fn_ctxt.errors_reported_since_creation(), - "`fn_ctxt` contained errors after cast check!" - ); - - res.ok() - } else { - None - } - }) -} diff --git a/src/tools/clippy/clippy_lints/src/transmute/crosspointer_transmute.rs b/src/tools/clippy/clippy_lints/src/transmute/crosspointer_transmute.rs new file mode 100644 index 0000000000000..25d0543c8611c --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/transmute/crosspointer_transmute.rs @@ -0,0 +1,37 @@ +use super::CROSSPOINTER_TRANSMUTE; +use clippy_utils::diagnostics::span_lint; +use rustc_hir::Expr; +use rustc_lint::LateContext; +use rustc_middle::ty::{self, Ty}; + +/// Checks for `crosspointer_transmute` lint. +/// Returns `true` if it's triggered, otherwise returns `false`. +pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>, from_ty: Ty<'tcx>, to_ty: Ty<'tcx>) -> bool { + match (&from_ty.kind(), &to_ty.kind()) { + (ty::RawPtr(from_ptr), _) if from_ptr.ty == to_ty => { + span_lint( + cx, + CROSSPOINTER_TRANSMUTE, + e.span, + &format!( + "transmute from a type (`{}`) to the type that it points to (`{}`)", + from_ty, to_ty + ), + ); + true + }, + (_, ty::RawPtr(to_ptr)) if to_ptr.ty == from_ty => { + span_lint( + cx, + CROSSPOINTER_TRANSMUTE, + e.span, + &format!( + "transmute from a type (`{}`) to a pointer to that type (`{}`)", + from_ty, to_ty + ), + ); + true + }, + _ => false, + } +} diff --git a/src/tools/clippy/clippy_lints/src/transmute/mod.rs b/src/tools/clippy/clippy_lints/src/transmute/mod.rs new file mode 100644 index 0000000000000..86ac916df6cba --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/transmute/mod.rs @@ -0,0 +1,363 @@ +mod crosspointer_transmute; +mod transmute_float_to_int; +mod transmute_int_to_bool; +mod transmute_int_to_char; +mod transmute_int_to_float; +mod transmute_ptr_to_ptr; +mod transmute_ptr_to_ref; +mod transmute_ref_to_ref; +mod transmutes_expressible_as_ptr_casts; +mod unsound_collection_transmute; +mod useless_transmute; +mod utils; +mod wrong_transmute; + +use clippy_utils::{in_constant, match_def_path, paths}; +use if_chain::if_chain; +use rustc_hir::{Expr, ExprKind}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; + +declare_clippy_lint! { + /// **What it does:** Checks for transmutes that can't ever be correct on any + /// architecture. + /// + /// **Why is this bad?** It's basically guaranteed to be undefined behaviour. + /// + /// **Known problems:** When accessing C, users might want to store pointer + /// sized objects in `extradata` arguments to save an allocation. + /// + /// **Example:** + /// ```ignore + /// let ptr: *const T = core::intrinsics::transmute('x') + /// ``` + pub WRONG_TRANSMUTE, + correctness, + "transmutes that are confusing at best, undefined behaviour at worst and always useless" +} + +// FIXME: Move this to `complexity` again, after #5343 is fixed +declare_clippy_lint! { + /// **What it does:** Checks for transmutes to the original type of the object + /// and transmutes that could be a cast. + /// + /// **Why is this bad?** Readability. The code tricks people into thinking that + /// something complex is going on. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// ```rust,ignore + /// core::intrinsics::transmute(t); // where the result type is the same as `t`'s + /// ``` + pub USELESS_TRANSMUTE, + nursery, + "transmutes that have the same to and from types or could be a cast/coercion" +} + +// FIXME: Merge this lint with USELESS_TRANSMUTE once that is out of the nursery. +declare_clippy_lint! { + /// **What it does:**Checks for transmutes that could be a pointer cast. + /// + /// **Why is this bad?** Readability. The code tricks people into thinking that + /// something complex is going on. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// + /// ```rust + /// # let p: *const [i32] = &[]; + /// unsafe { std::mem::transmute::<*const [i32], *const [u16]>(p) }; + /// ``` + /// Use instead: + /// ```rust + /// # let p: *const [i32] = &[]; + /// p as *const [u16]; + /// ``` + pub TRANSMUTES_EXPRESSIBLE_AS_PTR_CASTS, + complexity, + "transmutes that could be a pointer cast" +} + +declare_clippy_lint! { + /// **What it does:** Checks for transmutes between a type `T` and `*T`. + /// + /// **Why is this bad?** It's easy to mistakenly transmute between a type and a + /// pointer to that type. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// ```rust,ignore + /// core::intrinsics::transmute(t) // where the result type is the same as + /// // `*t` or `&t`'s + /// ``` + pub CROSSPOINTER_TRANSMUTE, + complexity, + "transmutes that have to or from types that are a pointer to the other" +} + +declare_clippy_lint! { + /// **What it does:** Checks for transmutes from a pointer to a reference. + /// + /// **Why is this bad?** This can always be rewritten with `&` and `*`. + /// + /// **Known problems:** + /// - `mem::transmute` in statics and constants is stable from Rust 1.46.0, + /// while dereferencing raw pointer is not stable yet. + /// If you need to do this in those places, + /// you would have to use `transmute` instead. + /// + /// **Example:** + /// ```rust,ignore + /// unsafe { + /// let _: &T = std::mem::transmute(p); // where p: *const T + /// } + /// + /// // can be written: + /// let _: &T = &*p; + /// ``` + pub TRANSMUTE_PTR_TO_REF, + complexity, + "transmutes from a pointer to a reference type" +} + +declare_clippy_lint! { + /// **What it does:** Checks for transmutes from an integer to a `char`. + /// + /// **Why is this bad?** Not every integer is a Unicode scalar value. + /// + /// **Known problems:** + /// - [`from_u32`] which this lint suggests using is slower than `transmute` + /// as it needs to validate the input. + /// If you are certain that the input is always a valid Unicode scalar value, + /// use [`from_u32_unchecked`] which is as fast as `transmute` + /// but has a semantically meaningful name. + /// - You might want to handle `None` returned from [`from_u32`] instead of calling `unwrap`. + /// + /// [`from_u32`]: https://doc.rust-lang.org/std/char/fn.from_u32.html + /// [`from_u32_unchecked`]: https://doc.rust-lang.org/std/char/fn.from_u32_unchecked.html + /// + /// **Example:** + /// ```rust + /// let x = 1_u32; + /// unsafe { + /// let _: char = std::mem::transmute(x); // where x: u32 + /// } + /// + /// // should be: + /// let _ = std::char::from_u32(x).unwrap(); + /// ``` + pub TRANSMUTE_INT_TO_CHAR, + complexity, + "transmutes from an integer to a `char`" +} + +declare_clippy_lint! { + /// **What it does:** Checks for transmutes from a `&[u8]` to a `&str`. + /// + /// **Why is this bad?** Not every byte slice is a valid UTF-8 string. + /// + /// **Known problems:** + /// - [`from_utf8`] which this lint suggests using is slower than `transmute` + /// as it needs to validate the input. + /// If you are certain that the input is always a valid UTF-8, + /// use [`from_utf8_unchecked`] which is as fast as `transmute` + /// but has a semantically meaningful name. + /// - You might want to handle errors returned from [`from_utf8`] instead of calling `unwrap`. + /// + /// [`from_utf8`]: https://doc.rust-lang.org/std/str/fn.from_utf8.html + /// [`from_utf8_unchecked`]: https://doc.rust-lang.org/std/str/fn.from_utf8_unchecked.html + /// + /// **Example:** + /// ```rust + /// let b: &[u8] = &[1_u8, 2_u8]; + /// unsafe { + /// let _: &str = std::mem::transmute(b); // where b: &[u8] + /// } + /// + /// // should be: + /// let _ = std::str::from_utf8(b).unwrap(); + /// ``` + pub TRANSMUTE_BYTES_TO_STR, + complexity, + "transmutes from a `&[u8]` to a `&str`" +} + +declare_clippy_lint! { + /// **What it does:** Checks for transmutes from an integer to a `bool`. + /// + /// **Why is this bad?** This might result in an invalid in-memory representation of a `bool`. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// ```rust + /// let x = 1_u8; + /// unsafe { + /// let _: bool = std::mem::transmute(x); // where x: u8 + /// } + /// + /// // should be: + /// let _: bool = x != 0; + /// ``` + pub TRANSMUTE_INT_TO_BOOL, + complexity, + "transmutes from an integer to a `bool`" +} + +declare_clippy_lint! { + /// **What it does:** Checks for transmutes from an integer to a float. + /// + /// **Why is this bad?** Transmutes are dangerous and error-prone, whereas `from_bits` is intuitive + /// and safe. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// ```rust + /// unsafe { + /// let _: f32 = std::mem::transmute(1_u32); // where x: u32 + /// } + /// + /// // should be: + /// let _: f32 = f32::from_bits(1_u32); + /// ``` + pub TRANSMUTE_INT_TO_FLOAT, + complexity, + "transmutes from an integer to a float" +} + +declare_clippy_lint! { + /// **What it does:** Checks for transmutes from a float to an integer. + /// + /// **Why is this bad?** Transmutes are dangerous and error-prone, whereas `to_bits` is intuitive + /// and safe. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// ```rust + /// unsafe { + /// let _: u32 = std::mem::transmute(1f32); + /// } + /// + /// // should be: + /// let _: u32 = 1f32.to_bits(); + /// ``` + pub TRANSMUTE_FLOAT_TO_INT, + complexity, + "transmutes from a float to an integer" +} + +declare_clippy_lint! { + /// **What it does:** Checks for transmutes from a pointer to a pointer, or + /// from a reference to a reference. + /// + /// **Why is this bad?** Transmutes are dangerous, and these can instead be + /// written as casts. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// ```rust + /// let ptr = &1u32 as *const u32; + /// unsafe { + /// // pointer-to-pointer transmute + /// let _: *const f32 = std::mem::transmute(ptr); + /// // ref-ref transmute + /// let _: &f32 = std::mem::transmute(&1u32); + /// } + /// // These can be respectively written: + /// let _ = ptr as *const f32; + /// let _ = unsafe{ &*(&1u32 as *const u32 as *const f32) }; + /// ``` + pub TRANSMUTE_PTR_TO_PTR, + complexity, + "transmutes from a pointer to a pointer / a reference to a reference" +} + +declare_clippy_lint! { + /// **What it does:** Checks for transmutes between collections whose + /// types have different ABI, size or alignment. + /// + /// **Why is this bad?** This is undefined behavior. + /// + /// **Known problems:** Currently, we cannot know whether a type is a + /// collection, so we just lint the ones that come with `std`. + /// + /// **Example:** + /// ```rust + /// // different size, therefore likely out-of-bounds memory access + /// // You absolutely do not want this in your code! + /// unsafe { + /// std::mem::transmute::<_, Vec<u32>>(vec![2_u16]) + /// }; + /// ``` + /// + /// You must always iterate, map and collect the values: + /// + /// ```rust + /// vec![2_u16].into_iter().map(u32::from).collect::<Vec<_>>(); + /// ``` + pub UNSOUND_COLLECTION_TRANSMUTE, + correctness, + "transmute between collections of layout-incompatible types" +} + +declare_lint_pass!(Transmute => [ + CROSSPOINTER_TRANSMUTE, + TRANSMUTE_PTR_TO_REF, + TRANSMUTE_PTR_TO_PTR, + USELESS_TRANSMUTE, + WRONG_TRANSMUTE, + TRANSMUTE_INT_TO_CHAR, + TRANSMUTE_BYTES_TO_STR, + TRANSMUTE_INT_TO_BOOL, + TRANSMUTE_INT_TO_FLOAT, + TRANSMUTE_FLOAT_TO_INT, + UNSOUND_COLLECTION_TRANSMUTE, + TRANSMUTES_EXPRESSIBLE_AS_PTR_CASTS, +]); + +impl<'tcx> LateLintPass<'tcx> for Transmute { + #[allow(clippy::similar_names, clippy::too_many_lines)] + fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) { + if_chain! { + if let ExprKind::Call(path_expr, args) = e.kind; + if let ExprKind::Path(ref qpath) = path_expr.kind; + if let Some(def_id) = cx.qpath_res(qpath, path_expr.hir_id).opt_def_id(); + if match_def_path(cx, def_id, &paths::TRANSMUTE); + then { + // Avoid suggesting from/to bits and dereferencing raw pointers in const contexts. + // See https://github.com/rust-lang/rust/issues/73736 for progress on making them `const fn`. + // And see https://github.com/rust-lang/rust/issues/51911 for dereferencing raw pointers. + let const_context = in_constant(cx, e.hir_id); + + let from_ty = cx.typeck_results().expr_ty(&args[0]); + let to_ty = cx.typeck_results().expr_ty(e); + + // If useless_transmute is triggered, the other lints can be skipped. + if useless_transmute::check(cx, e, from_ty, to_ty, args) { + return; + } + + let mut linted = wrong_transmute::check(cx, e, from_ty, to_ty); + linted |= crosspointer_transmute::check(cx, e, from_ty, to_ty); + linted |= transmute_ptr_to_ref::check(cx, e, from_ty, to_ty, args, qpath); + linted |= transmute_int_to_char::check(cx, e, from_ty, to_ty, args); + linted |= transmute_ref_to_ref::check(cx, e, from_ty, to_ty, args, const_context); + linted |= transmute_ptr_to_ptr::check(cx, e, from_ty, to_ty, args); + linted |= transmute_int_to_bool::check(cx, e, from_ty, to_ty, args); + linted |= transmute_int_to_float::check(cx, e, from_ty, to_ty, args, const_context); + linted |= transmute_float_to_int::check(cx, e, from_ty, to_ty, args, const_context); + linted |= unsound_collection_transmute::check(cx, e, from_ty, to_ty); + + if !linted { + transmutes_expressible_as_ptr_casts::check(cx, e, from_ty, to_ty, args); + } + } + } + } +} diff --git a/src/tools/clippy/clippy_lints/src/transmute/transmute_float_to_int.rs b/src/tools/clippy/clippy_lints/src/transmute/transmute_float_to_int.rs new file mode 100644 index 0000000000000..3aa3c393ba57c --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/transmute/transmute_float_to_int.rs @@ -0,0 +1,66 @@ +use super::TRANSMUTE_FLOAT_TO_INT; +use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::sugg; +use if_chain::if_chain; +use rustc_ast as ast; +use rustc_errors::Applicability; +use rustc_hir::{Expr, ExprKind, UnOp}; +use rustc_lint::LateContext; +use rustc_middle::ty::{self, Ty}; + +/// Checks for `transmute_float_to_int` lint. +/// Returns `true` if it's triggered, otherwise returns `false`. +pub(super) fn check<'tcx>( + cx: &LateContext<'tcx>, + e: &'tcx Expr<'_>, + from_ty: Ty<'tcx>, + to_ty: Ty<'tcx>, + args: &'tcx [Expr<'_>], + const_context: bool, +) -> bool { + match (&from_ty.kind(), &to_ty.kind()) { + (ty::Float(float_ty), ty::Int(_) | ty::Uint(_)) if !const_context => { + span_lint_and_then( + cx, + TRANSMUTE_FLOAT_TO_INT, + e.span, + &format!("transmute from a `{}` to a `{}`", from_ty, to_ty), + |diag| { + let mut expr = &args[0]; + let mut arg = sugg::Sugg::hir(cx, expr, ".."); + + if let ExprKind::Unary(UnOp::Neg, inner_expr) = &expr.kind { + expr = inner_expr; + } + + if_chain! { + // if the expression is a float literal and it is unsuffixed then + // add a suffix so the suggestion is valid and unambiguous + if let ExprKind::Lit(lit) = &expr.kind; + if let ast::LitKind::Float(_, ast::LitFloatType::Unsuffixed) = lit.node; + then { + let op = format!("{}{}", arg, float_ty.name_str()).into(); + match arg { + sugg::Sugg::MaybeParen(_) => arg = sugg::Sugg::MaybeParen(op), + _ => arg = sugg::Sugg::NonParen(op) + } + } + } + + arg = sugg::Sugg::NonParen(format!("{}.to_bits()", arg.maybe_par()).into()); + + // cast the result of `to_bits` if `to_ty` is signed + arg = if let ty::Int(int_ty) = to_ty.kind() { + arg.as_ty(int_ty.name_str().to_string()) + } else { + arg + }; + + diag.span_suggestion(e.span, "consider using", arg.to_string(), Applicability::Unspecified); + }, + ); + true + }, + _ => false, + } +} diff --git a/src/tools/clippy/clippy_lints/src/transmute/transmute_int_to_bool.rs b/src/tools/clippy/clippy_lints/src/transmute/transmute_int_to_bool.rs new file mode 100644 index 0000000000000..cc0a5643e2a7d --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/transmute/transmute_int_to_bool.rs @@ -0,0 +1,42 @@ +use super::TRANSMUTE_INT_TO_BOOL; +use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::sugg; +use rustc_ast as ast; +use rustc_errors::Applicability; +use rustc_hir::Expr; +use rustc_lint::LateContext; +use rustc_middle::ty::{self, Ty}; +use std::borrow::Cow; + +/// Checks for `transmute_int_to_bool` lint. +/// Returns `true` if it's triggered, otherwise returns `false`. +pub(super) fn check<'tcx>( + cx: &LateContext<'tcx>, + e: &'tcx Expr<'_>, + from_ty: Ty<'tcx>, + to_ty: Ty<'tcx>, + args: &'tcx [Expr<'_>], +) -> bool { + match (&from_ty.kind(), &to_ty.kind()) { + (ty::Int(ty::IntTy::I8) | ty::Uint(ty::UintTy::U8), ty::Bool) => { + span_lint_and_then( + cx, + TRANSMUTE_INT_TO_BOOL, + e.span, + &format!("transmute from a `{}` to a `bool`", from_ty), + |diag| { + let arg = sugg::Sugg::hir(cx, &args[0], ".."); + let zero = sugg::Sugg::NonParen(Cow::from("0")); + diag.span_suggestion( + e.span, + "consider using", + sugg::make_binop(ast::BinOpKind::Ne, &arg, &zero).to_string(), + Applicability::Unspecified, + ); + }, + ); + true + }, + _ => false, + } +} diff --git a/src/tools/clippy/clippy_lints/src/transmute/transmute_int_to_char.rs b/src/tools/clippy/clippy_lints/src/transmute/transmute_int_to_char.rs new file mode 100644 index 0000000000000..8f884e6a4a17b --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/transmute/transmute_int_to_char.rs @@ -0,0 +1,45 @@ +use super::TRANSMUTE_INT_TO_CHAR; +use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::sugg; +use rustc_ast as ast; +use rustc_errors::Applicability; +use rustc_hir::Expr; +use rustc_lint::LateContext; +use rustc_middle::ty::{self, Ty}; + +/// Checks for `transmute_int_to_char` lint. +/// Returns `true` if it's triggered, otherwise returns `false`. +pub(super) fn check<'tcx>( + cx: &LateContext<'tcx>, + e: &'tcx Expr<'_>, + from_ty: Ty<'tcx>, + to_ty: Ty<'tcx>, + args: &'tcx [Expr<'_>], +) -> bool { + match (&from_ty.kind(), &to_ty.kind()) { + (ty::Int(ty::IntTy::I32) | ty::Uint(ty::UintTy::U32), &ty::Char) => { + span_lint_and_then( + cx, + TRANSMUTE_INT_TO_CHAR, + e.span, + &format!("transmute from a `{}` to a `char`", from_ty), + |diag| { + let arg = sugg::Sugg::hir(cx, &args[0], ".."); + let arg = if let ty::Int(_) = from_ty.kind() { + arg.as_ty(ast::UintTy::U32.name_str()) + } else { + arg + }; + diag.span_suggestion( + e.span, + "consider using", + format!("std::char::from_u32({}).unwrap()", arg.to_string()), + Applicability::Unspecified, + ); + }, + ); + true + }, + _ => false, + } +} diff --git a/src/tools/clippy/clippy_lints/src/transmute/transmute_int_to_float.rs b/src/tools/clippy/clippy_lints/src/transmute/transmute_int_to_float.rs new file mode 100644 index 0000000000000..2b6a4cff81eb5 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/transmute/transmute_int_to_float.rs @@ -0,0 +1,48 @@ +use super::TRANSMUTE_INT_TO_FLOAT; +use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::sugg; +use rustc_errors::Applicability; +use rustc_hir::Expr; +use rustc_lint::LateContext; +use rustc_middle::ty::{self, Ty}; + +/// Checks for `transmute_int_to_float` lint. +/// Returns `true` if it's triggered, otherwise returns `false`. +pub(super) fn check<'tcx>( + cx: &LateContext<'tcx>, + e: &'tcx Expr<'_>, + from_ty: Ty<'tcx>, + to_ty: Ty<'tcx>, + args: &'tcx [Expr<'_>], + const_context: bool, +) -> bool { + match (&from_ty.kind(), &to_ty.kind()) { + (ty::Int(_) | ty::Uint(_), ty::Float(_)) if !const_context => { + span_lint_and_then( + cx, + TRANSMUTE_INT_TO_FLOAT, + e.span, + &format!("transmute from a `{}` to a `{}`", from_ty, to_ty), + |diag| { + let arg = sugg::Sugg::hir(cx, &args[0], ".."); + let arg = if let ty::Int(int_ty) = from_ty.kind() { + arg.as_ty(format!( + "u{}", + int_ty.bit_width().map_or_else(|| "size".to_string(), |v| v.to_string()) + )) + } else { + arg + }; + diag.span_suggestion( + e.span, + "consider using", + format!("{}::from_bits({})", to_ty, arg.to_string()), + Applicability::Unspecified, + ); + }, + ); + true + }, + _ => false, + } +} diff --git a/src/tools/clippy/clippy_lints/src/transmute/transmute_ptr_to_ptr.rs b/src/tools/clippy/clippy_lints/src/transmute/transmute_ptr_to_ptr.rs new file mode 100644 index 0000000000000..7b646bfc0c6d1 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/transmute/transmute_ptr_to_ptr.rs @@ -0,0 +1,36 @@ +use super::TRANSMUTE_PTR_TO_PTR; +use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::sugg; +use rustc_errors::Applicability; +use rustc_hir::Expr; +use rustc_lint::LateContext; +use rustc_middle::ty::{self, Ty}; + +/// Checks for `transmute_ptr_to_ptr` lint. +/// Returns `true` if it's triggered, otherwise returns `false`. +pub(super) fn check<'tcx>( + cx: &LateContext<'tcx>, + e: &'tcx Expr<'_>, + from_ty: Ty<'tcx>, + to_ty: Ty<'tcx>, + args: &'tcx [Expr<'_>], +) -> bool { + match (&from_ty.kind(), &to_ty.kind()) { + (ty::RawPtr(_), ty::RawPtr(to_ty)) => { + span_lint_and_then( + cx, + TRANSMUTE_PTR_TO_PTR, + e.span, + "transmute from a pointer to a pointer", + |diag| { + if let Some(arg) = sugg::Sugg::hir_opt(cx, &args[0]) { + let sugg = arg.as_ty(cx.tcx.mk_ptr(*to_ty)); + diag.span_suggestion(e.span, "try", sugg.to_string(), Applicability::Unspecified); + } + }, + ); + true + }, + _ => false, + } +} diff --git a/src/tools/clippy/clippy_lints/src/transmute/transmute_ptr_to_ref.rs b/src/tools/clippy/clippy_lints/src/transmute/transmute_ptr_to_ref.rs new file mode 100644 index 0000000000000..f14eef9364531 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/transmute/transmute_ptr_to_ref.rs @@ -0,0 +1,56 @@ +use super::utils::get_type_snippet; +use super::TRANSMUTE_PTR_TO_REF; +use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::sugg; +use rustc_errors::Applicability; +use rustc_hir::{Expr, Mutability, QPath}; +use rustc_lint::LateContext; +use rustc_middle::ty::{self, Ty}; + +/// Checks for `transmute_ptr_to_ref` lint. +/// Returns `true` if it's triggered, otherwise returns `false`. +pub(super) fn check<'tcx>( + cx: &LateContext<'tcx>, + e: &'tcx Expr<'_>, + from_ty: Ty<'tcx>, + to_ty: Ty<'tcx>, + args: &'tcx [Expr<'_>], + qpath: &'tcx QPath<'_>, +) -> bool { + match (&from_ty.kind(), &to_ty.kind()) { + (ty::RawPtr(from_ptr_ty), ty::Ref(_, to_ref_ty, mutbl)) => { + span_lint_and_then( + cx, + TRANSMUTE_PTR_TO_REF, + e.span, + &format!( + "transmute from a pointer type (`{}`) to a reference type (`{}`)", + from_ty, to_ty + ), + |diag| { + let arg = sugg::Sugg::hir(cx, &args[0], ".."); + let (deref, cast) = if *mutbl == Mutability::Mut { + ("&mut *", "*mut") + } else { + ("&*", "*const") + }; + + let arg = if from_ptr_ty.ty == *to_ref_ty { + arg + } else { + arg.as_ty(&format!("{} {}", cast, get_type_snippet(cx, qpath, to_ref_ty))) + }; + + diag.span_suggestion( + e.span, + "try", + sugg::make_unop(deref, arg).to_string(), + Applicability::Unspecified, + ); + }, + ); + true + }, + _ => false, + } +} diff --git a/src/tools/clippy/clippy_lints/src/transmute/transmute_ref_to_ref.rs b/src/tools/clippy/clippy_lints/src/transmute/transmute_ref_to_ref.rs new file mode 100644 index 0000000000000..d105e37abf9c0 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/transmute/transmute_ref_to_ref.rs @@ -0,0 +1,87 @@ +use super::{TRANSMUTE_BYTES_TO_STR, TRANSMUTE_PTR_TO_PTR}; +use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then}; +use clippy_utils::source::snippet; +use clippy_utils::sugg; +use if_chain::if_chain; +use rustc_errors::Applicability; +use rustc_hir::{Expr, Mutability}; +use rustc_lint::LateContext; +use rustc_middle::ty::{self, Ty}; + +/// Checks for `transmute_bytes_to_str` and `transmute_ptr_to_ptr` lints. +/// Returns `true` if either one triggered, otherwise returns `false`. +pub(super) fn check<'tcx>( + cx: &LateContext<'tcx>, + e: &'tcx Expr<'_>, + from_ty: Ty<'tcx>, + to_ty: Ty<'tcx>, + args: &'tcx [Expr<'_>], + const_context: bool, +) -> bool { + let mut triggered = false; + + if let (ty::Ref(_, ty_from, from_mutbl), ty::Ref(_, ty_to, to_mutbl)) = (&from_ty.kind(), &to_ty.kind()) { + if_chain! { + if let (&ty::Slice(slice_ty), &ty::Str) = (&ty_from.kind(), &ty_to.kind()); + if let ty::Uint(ty::UintTy::U8) = slice_ty.kind(); + if from_mutbl == to_mutbl; + then { + let postfix = if *from_mutbl == Mutability::Mut { + "_mut" + } else { + "" + }; + + span_lint_and_sugg( + cx, + TRANSMUTE_BYTES_TO_STR, + e.span, + &format!("transmute from a `{}` to a `{}`", from_ty, to_ty), + "consider using", + format!( + "std::str::from_utf8{}({}).unwrap()", + postfix, + snippet(cx, args[0].span, ".."), + ), + Applicability::Unspecified, + ); + triggered = true; + } else { + if (cx.tcx.erase_regions(from_ty) != cx.tcx.erase_regions(to_ty)) + && !const_context { + span_lint_and_then( + cx, + TRANSMUTE_PTR_TO_PTR, + e.span, + "transmute from a reference to a reference", + |diag| if let Some(arg) = sugg::Sugg::hir_opt(cx, &args[0]) { + let ty_from_and_mut = ty::TypeAndMut { + ty: ty_from, + mutbl: *from_mutbl + }; + let ty_to_and_mut = ty::TypeAndMut { ty: ty_to, mutbl: *to_mutbl }; + let sugg_paren = arg + .as_ty(cx.tcx.mk_ptr(ty_from_and_mut)) + .as_ty(cx.tcx.mk_ptr(ty_to_and_mut)); + let sugg = if *to_mutbl == Mutability::Mut { + sugg_paren.mut_addr_deref() + } else { + sugg_paren.addr_deref() + }; + diag.span_suggestion( + e.span, + "try", + sugg.to_string(), + Applicability::Unspecified, + ); + }, + ); + + triggered = true; + } + } + } + } + + triggered +} diff --git a/src/tools/clippy/clippy_lints/src/transmute/transmutes_expressible_as_ptr_casts.rs b/src/tools/clippy/clippy_lints/src/transmute/transmutes_expressible_as_ptr_casts.rs new file mode 100644 index 0000000000000..e2c6d130f3c9c --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/transmute/transmutes_expressible_as_ptr_casts.rs @@ -0,0 +1,39 @@ +use super::utils::can_be_expressed_as_pointer_cast; +use super::TRANSMUTES_EXPRESSIBLE_AS_PTR_CASTS; +use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::sugg; +use rustc_errors::Applicability; +use rustc_hir::Expr; +use rustc_lint::LateContext; +use rustc_middle::ty::Ty; + +/// Checks for `transmutes_expressible_as_ptr_casts` lint. +/// Returns `true` if it's triggered, otherwise returns `false`. +pub(super) fn check<'tcx>( + cx: &LateContext<'tcx>, + e: &'tcx Expr<'_>, + from_ty: Ty<'tcx>, + to_ty: Ty<'tcx>, + args: &'tcx [Expr<'_>], +) -> bool { + if can_be_expressed_as_pointer_cast(cx, e, from_ty, to_ty) { + span_lint_and_then( + cx, + TRANSMUTES_EXPRESSIBLE_AS_PTR_CASTS, + e.span, + &format!( + "transmute from `{}` to `{}` which could be expressed as a pointer cast instead", + from_ty, to_ty + ), + |diag| { + if let Some(arg) = sugg::Sugg::hir_opt(cx, &args[0]) { + let sugg = arg.as_ty(&to_ty.to_string()).to_string(); + diag.span_suggestion(e.span, "try", sugg, Applicability::MachineApplicable); + } + }, + ); + true + } else { + false + } +} diff --git a/src/tools/clippy/clippy_lints/src/transmute/unsound_collection_transmute.rs b/src/tools/clippy/clippy_lints/src/transmute/unsound_collection_transmute.rs new file mode 100644 index 0000000000000..de9277e016e3a --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/transmute/unsound_collection_transmute.rs @@ -0,0 +1,49 @@ +use super::utils::is_layout_incompatible; +use super::UNSOUND_COLLECTION_TRANSMUTE; +use clippy_utils::diagnostics::span_lint; +use clippy_utils::{match_def_path, paths}; +use rustc_hir::Expr; +use rustc_lint::LateContext; +use rustc_middle::ty::{self, Ty}; + +// used to check for UNSOUND_COLLECTION_TRANSMUTE +static COLLECTIONS: &[&[&str]] = &[ + &paths::VEC, + &paths::VEC_DEQUE, + &paths::BINARY_HEAP, + &paths::BTREESET, + &paths::BTREEMAP, + &paths::HASHSET, + &paths::HASHMAP, +]; + +/// Checks for `unsound_collection_transmute` lint. +/// Returns `true` if it's triggered, otherwise returns `false`. +pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>, from_ty: Ty<'tcx>, to_ty: Ty<'tcx>) -> bool { + match (&from_ty.kind(), &to_ty.kind()) { + (ty::Adt(from_adt, from_substs), ty::Adt(to_adt, to_substs)) => { + if from_adt.did != to_adt.did || !COLLECTIONS.iter().any(|path| match_def_path(cx, to_adt.did, path)) { + return false; + } + if from_substs + .types() + .zip(to_substs.types()) + .any(|(from_ty, to_ty)| is_layout_incompatible(cx, from_ty, to_ty)) + { + span_lint( + cx, + UNSOUND_COLLECTION_TRANSMUTE, + e.span, + &format!( + "transmute from `{}` to `{}` with mismatched layout is unsound", + from_ty, to_ty + ), + ); + true + } else { + false + } + }, + _ => false, + } +} diff --git a/src/tools/clippy/clippy_lints/src/transmute/useless_transmute.rs b/src/tools/clippy/clippy_lints/src/transmute/useless_transmute.rs new file mode 100644 index 0000000000000..445bcf60fa71a --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/transmute/useless_transmute.rs @@ -0,0 +1,74 @@ +use super::USELESS_TRANSMUTE; +use clippy_utils::diagnostics::{span_lint, span_lint_and_then}; +use clippy_utils::sugg; +use rustc_errors::Applicability; +use rustc_hir::Expr; +use rustc_lint::LateContext; +use rustc_middle::ty::{self, Ty}; + +/// Checks for `useless_transmute` lint. +/// Returns `true` if it's triggered, otherwise returns `false`. +pub(super) fn check<'tcx>( + cx: &LateContext<'tcx>, + e: &'tcx Expr<'_>, + from_ty: Ty<'tcx>, + to_ty: Ty<'tcx>, + args: &'tcx [Expr<'_>], +) -> bool { + match (&from_ty.kind(), &to_ty.kind()) { + _ if from_ty == to_ty => { + span_lint( + cx, + USELESS_TRANSMUTE, + e.span, + &format!("transmute from a type (`{}`) to itself", from_ty), + ); + true + }, + (ty::Ref(_, rty, rty_mutbl), ty::RawPtr(ptr_ty)) => { + span_lint_and_then( + cx, + USELESS_TRANSMUTE, + e.span, + "transmute from a reference to a pointer", + |diag| { + if let Some(arg) = sugg::Sugg::hir_opt(cx, &args[0]) { + let rty_and_mut = ty::TypeAndMut { + ty: rty, + mutbl: *rty_mutbl, + }; + + let sugg = if *ptr_ty == rty_and_mut { + arg.as_ty(to_ty) + } else { + arg.as_ty(cx.tcx.mk_ptr(rty_and_mut)).as_ty(to_ty) + }; + + diag.span_suggestion(e.span, "try", sugg.to_string(), Applicability::Unspecified); + } + }, + ); + true + }, + (ty::Int(_) | ty::Uint(_), ty::RawPtr(_)) => { + span_lint_and_then( + cx, + USELESS_TRANSMUTE, + e.span, + "transmute from an integer to a pointer", + |diag| { + if let Some(arg) = sugg::Sugg::hir_opt(cx, &args[0]) { + diag.span_suggestion( + e.span, + "try", + arg.as_ty(&to_ty.to_string()).to_string(), + Applicability::Unspecified, + ); + } + }, + ); + true + }, + _ => false, + } +} diff --git a/src/tools/clippy/clippy_lints/src/transmute/utils.rs b/src/tools/clippy/clippy_lints/src/transmute/utils.rs new file mode 100644 index 0000000000000..f359b606e4548 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/transmute/utils.rs @@ -0,0 +1,106 @@ +use clippy_utils::last_path_segment; +use clippy_utils::source::snippet; +use clippy_utils::ty::is_normalizable; +use if_chain::if_chain; +use rustc_hir::{Expr, GenericArg, QPath, TyKind}; +use rustc_lint::LateContext; +use rustc_middle::ty::{self, cast::CastKind, Ty}; +use rustc_span::DUMMY_SP; +use rustc_typeck::check::{cast::CastCheck, FnCtxt, Inherited}; + +/// Gets the snippet of `Bar` in `…::transmute<Foo, &Bar>`. If that snippet is +/// not available , use +/// the type's `ToString` implementation. In weird cases it could lead to types +/// with invalid `'_` +/// lifetime, but it should be rare. +pub(super) fn get_type_snippet(cx: &LateContext<'_>, path: &QPath<'_>, to_ref_ty: Ty<'_>) -> String { + let seg = last_path_segment(path); + if_chain! { + if let Some(params) = seg.args; + if !params.parenthesized; + if let Some(to_ty) = params.args.iter().filter_map(|arg| match arg { + GenericArg::Type(ty) => Some(ty), + _ => None, + }).nth(1); + if let TyKind::Rptr(_, ref to_ty) = to_ty.kind; + then { + return snippet(cx, to_ty.ty.span, &to_ref_ty.to_string()).to_string(); + } + } + + to_ref_ty.to_string() +} + +// check if the component types of the transmuted collection and the result have different ABI, +// size or alignment +pub(super) fn is_layout_incompatible<'tcx>(cx: &LateContext<'tcx>, from: Ty<'tcx>, to: Ty<'tcx>) -> bool { + let empty_param_env = ty::ParamEnv::empty(); + // check if `from` and `to` are normalizable to avoid ICE (#4968) + if !(is_normalizable(cx, empty_param_env, from) && is_normalizable(cx, empty_param_env, to)) { + return false; + } + let from_ty_layout = cx.tcx.layout_of(empty_param_env.and(from)); + let to_ty_layout = cx.tcx.layout_of(empty_param_env.and(to)); + if let (Ok(from_layout), Ok(to_layout)) = (from_ty_layout, to_ty_layout) { + from_layout.size != to_layout.size || from_layout.align != to_layout.align || from_layout.abi != to_layout.abi + } else { + // no idea about layout, so don't lint + false + } +} + +/// Check if the type conversion can be expressed as a pointer cast, instead of +/// a transmute. In certain cases, including some invalid casts from array +/// references to pointers, this may cause additional errors to be emitted and/or +/// ICE error messages. This function will panic if that occurs. +pub(super) fn can_be_expressed_as_pointer_cast<'tcx>( + cx: &LateContext<'tcx>, + e: &'tcx Expr<'_>, + from_ty: Ty<'tcx>, + to_ty: Ty<'tcx>, +) -> bool { + use CastKind::{AddrPtrCast, ArrayPtrCast, FnPtrAddrCast, FnPtrPtrCast, PtrAddrCast, PtrPtrCast}; + matches!( + check_cast(cx, e, from_ty, to_ty), + Some(PtrPtrCast | PtrAddrCast | AddrPtrCast | ArrayPtrCast | FnPtrPtrCast | FnPtrAddrCast) + ) +} + +/// If a cast from `from_ty` to `to_ty` is valid, returns an Ok containing the kind of +/// the cast. In certain cases, including some invalid casts from array references +/// to pointers, this may cause additional errors to be emitted and/or ICE error +/// messages. This function will panic if that occurs. +fn check_cast<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>, from_ty: Ty<'tcx>, to_ty: Ty<'tcx>) -> Option<CastKind> { + let hir_id = e.hir_id; + let local_def_id = hir_id.owner; + + Inherited::build(cx.tcx, local_def_id).enter(|inherited| { + let fn_ctxt = FnCtxt::new(&inherited, cx.param_env, hir_id); + + // If we already have errors, we can't be sure we can pointer cast. + assert!( + !fn_ctxt.errors_reported_since_creation(), + "Newly created FnCtxt contained errors" + ); + + if let Ok(check) = CastCheck::new( + &fn_ctxt, e, from_ty, to_ty, + // We won't show any error to the user, so we don't care what the span is here. + DUMMY_SP, DUMMY_SP, + ) { + let res = check.do_check(&fn_ctxt); + + // do_check's documentation says that it might return Ok and create + // errors in the fcx instead of returing Err in some cases. Those cases + // should be filtered out before getting here. + assert!( + !fn_ctxt.errors_reported_since_creation(), + "`fn_ctxt` contained errors after cast check!" + ); + + res.ok() + } else { + None + } + }) +} diff --git a/src/tools/clippy/clippy_lints/src/transmute/wrong_transmute.rs b/src/tools/clippy/clippy_lints/src/transmute/wrong_transmute.rs new file mode 100644 index 0000000000000..2118f3d695004 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/transmute/wrong_transmute.rs @@ -0,0 +1,22 @@ +use super::WRONG_TRANSMUTE; +use clippy_utils::diagnostics::span_lint; +use rustc_hir::Expr; +use rustc_lint::LateContext; +use rustc_middle::ty::{self, Ty}; + +/// Checks for `wrong_transmute` lint. +/// Returns `true` if it's triggered, otherwise returns `false`. +pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>, from_ty: Ty<'tcx>, to_ty: Ty<'tcx>) -> bool { + match (&from_ty.kind(), &to_ty.kind()) { + (ty::Float(_) | ty::Char, ty::Ref(..) | ty::RawPtr(_)) => { + span_lint( + cx, + WRONG_TRANSMUTE, + e.span, + &format!("transmute from a `{}` to a pointer", from_ty), + ); + true + }, + _ => false, + } +} diff --git a/src/tools/clippy/clippy_lints/src/transmuting_null.rs b/src/tools/clippy/clippy_lints/src/transmuting_null.rs index 6b171a0fa1af2..0be05d3e0cf3f 100644 --- a/src/tools/clippy/clippy_lints/src/transmuting_null.rs +++ b/src/tools/clippy/clippy_lints/src/transmuting_null.rs @@ -1,5 +1,6 @@ use crate::consts::{constant_context, Constant}; -use crate::utils::{match_qpath, paths, span_lint}; +use clippy_utils::diagnostics::span_lint; +use clippy_utils::{match_qpath, paths}; use if_chain::if_chain; use rustc_ast::LitKind; use rustc_hir::{Expr, ExprKind}; @@ -27,7 +28,7 @@ declare_clippy_lint! { declare_lint_pass!(TransmutingNull => [TRANSMUTING_NULL]); -const LINT_MSG: &str = "transmuting a known null pointer into a reference."; +const LINT_MSG: &str = "transmuting a known null pointer into a reference"; impl<'tcx> LateLintPass<'tcx> for TransmutingNull { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { @@ -36,7 +37,7 @@ impl<'tcx> LateLintPass<'tcx> for TransmutingNull { } if_chain! { - if let ExprKind::Call(ref func, ref args) = expr.kind; + if let ExprKind::Call(func, args) = expr.kind; if let ExprKind::Path(ref path) = func.kind; if match_qpath(path, &paths::STD_MEM_TRANSMUTE); if args.len() == 1; @@ -57,7 +58,7 @@ impl<'tcx> LateLintPass<'tcx> for TransmutingNull { // Catching: // `std::mem::transmute(0 as *const i32)` if_chain! { - if let ExprKind::Cast(ref inner_expr, ref _cast_ty) = args[0].kind; + if let ExprKind::Cast(inner_expr, _cast_ty) = args[0].kind; if let ExprKind::Lit(ref lit) = inner_expr.kind; if let LitKind::Int(0, _) = lit.node; then { @@ -68,7 +69,7 @@ impl<'tcx> LateLintPass<'tcx> for TransmutingNull { // Catching: // `std::mem::transmute(std::ptr::null::<i32>())` if_chain! { - if let ExprKind::Call(ref func1, ref args1) = args[0].kind; + if let ExprKind::Call(func1, args1) = args[0].kind; if let ExprKind::Path(ref path1) = func1.kind; if match_qpath(path1, &paths::STD_PTR_NULL); if args1.is_empty(); diff --git a/src/tools/clippy/clippy_lints/src/try_err.rs b/src/tools/clippy/clippy_lints/src/try_err.rs index 73e3a04aec987..23a1953ffaceb 100644 --- a/src/tools/clippy/clippy_lints/src/try_err.rs +++ b/src/tools/clippy/clippy_lints/src/try_err.rs @@ -1,7 +1,7 @@ -use crate::utils::{ - differing_macro_contexts, in_macro, is_type_diagnostic_item, match_def_path, match_qpath, paths, snippet, - snippet_with_macro_callsite, span_lint_and_sugg, -}; +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::source::{snippet, snippet_with_macro_callsite}; +use clippy_utils::ty::is_type_diagnostic_item; +use clippy_utils::{differing_macro_contexts, in_macro, match_def_path, match_qpath, paths}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind, LangItem, MatchSource, QPath}; @@ -60,13 +60,13 @@ impl<'tcx> LateLintPass<'tcx> for TryErr { // }; if_chain! { if !in_external_macro(cx.tcx.sess, expr.span); - if let ExprKind::Match(ref match_arg, _, MatchSource::TryDesugar) = expr.kind; - if let ExprKind::Call(ref match_fun, ref try_args) = match_arg.kind; + if let ExprKind::Match(match_arg, _, MatchSource::TryDesugar) = expr.kind; + if let ExprKind::Call(match_fun, try_args) = match_arg.kind; if let ExprKind::Path(ref match_fun_path) = match_fun.kind; if matches!(match_fun_path, QPath::LangItem(LangItem::TryIntoResult, _)); - if let Some(ref try_arg) = try_args.get(0); - if let ExprKind::Call(ref err_fun, ref err_args) = try_arg.kind; - if let Some(ref err_arg) = err_args.get(0); + if let Some(try_arg) = try_args.get(0); + if let ExprKind::Call(err_fun, err_args) = try_arg.kind; + if let Some(err_arg) = err_args.get(0); if let ExprKind::Path(ref err_fun_path) = err_fun.kind; if match_qpath(err_fun_path, &paths::RESULT_ERR); if let Some(return_ty) = find_return_type(cx, &expr.kind); @@ -123,9 +123,9 @@ impl<'tcx> LateLintPass<'tcx> for TryErr { /// Finds function return type by examining return expressions in match arms. fn find_return_type<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx ExprKind<'_>) -> Option<Ty<'tcx>> { - if let ExprKind::Match(_, ref arms, MatchSource::TryDesugar) = expr { + if let ExprKind::Match(_, arms, MatchSource::TryDesugar) = expr { for arm in arms.iter() { - if let ExprKind::Ret(Some(ref ret)) = arm.body.kind { + if let ExprKind::Ret(Some(ret)) = arm.body.kind { return Some(cx.typeck_results().expr_ty(ret)); } } @@ -138,9 +138,8 @@ fn result_error_type<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option<Ty<'t if_chain! { if let ty::Adt(_, subst) = ty.kind(); if is_type_diagnostic_item(cx, ty, sym::result_type); - let err_ty = subst.type_at(1); then { - Some(err_ty) + Some(subst.type_at(1)) } else { None } @@ -156,10 +155,8 @@ fn poll_result_error_type<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option< if let ty::Adt(ready_def, ready_subst) = ready_ty.kind(); if cx.tcx.is_diagnostic_item(sym::result_type, ready_def.did); - let err_ty = ready_subst.type_at(1); - then { - Some(err_ty) + Some(ready_subst.type_at(1)) } else { None } @@ -179,10 +176,8 @@ fn poll_option_result_error_type<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> if let ty::Adt(some_def, some_subst) = some_ty.kind(); if cx.tcx.is_diagnostic_item(sym::result_type, some_def.did); - let err_ty = some_subst.type_at(1); - then { - Some(err_ty) + Some(some_subst.type_at(1)) } else { None } diff --git a/src/tools/clippy/clippy_lints/src/types.rs b/src/tools/clippy/clippy_lints/src/types.rs deleted file mode 100644 index eb2016db3dc2c..0000000000000 --- a/src/tools/clippy/clippy_lints/src/types.rs +++ /dev/null @@ -1,2979 +0,0 @@ -#![allow(rustc::default_hash_types)] - -use std::borrow::Cow; -use std::cmp::Ordering; -use std::collections::BTreeMap; - -use if_chain::if_chain; -use rustc_ast::{LitFloatType, LitIntType, LitKind}; -use rustc_errors::{Applicability, DiagnosticBuilder}; -use rustc_hir as hir; -use rustc_hir::intravisit::{walk_body, walk_expr, walk_ty, FnKind, NestedVisitorMap, Visitor}; -use rustc_hir::{ - BinOpKind, Block, Body, Expr, ExprKind, FnDecl, FnRetTy, FnSig, GenericArg, GenericBounds, GenericParamKind, HirId, - ImplItem, ImplItemKind, Item, ItemKind, Lifetime, Lit, Local, MatchSource, MutTy, Mutability, Node, QPath, Stmt, - StmtKind, SyntheticTyParamKind, TraitFn, TraitItem, TraitItemKind, TyKind, UnOp, -}; -use rustc_lint::{LateContext, LateLintPass, LintContext}; -use rustc_middle::hir::map::Map; -use rustc_middle::lint::in_external_macro; -use rustc_middle::ty::TypeFoldable; -use rustc_middle::ty::{self, FloatTy, InferTy, IntTy, Ty, TyCtxt, TyS, TypeAndMut, TypeckResults, UintTy}; -use rustc_semver::RustcVersion; -use rustc_session::{declare_lint_pass, declare_tool_lint, impl_lint_pass}; -use rustc_span::hygiene::{ExpnKind, MacroKind}; -use rustc_span::source_map::Span; -use rustc_span::symbol::sym; -use rustc_target::abi::LayoutOf; -use rustc_target::spec::abi::Abi; -use rustc_typeck::hir_ty_to_ty; - -use crate::consts::{constant, Constant}; -use crate::utils::paths; -use crate::utils::sugg::Sugg; -use crate::utils::{ - clip, comparisons, differing_macro_contexts, higher, in_constant, indent_of, int_bits, is_hir_ty_cfg_dependant, - is_type_diagnostic_item, last_path_segment, match_def_path, match_path, meets_msrv, method_chain_args, - multispan_sugg, numeric_literal::NumericLiteral, reindent_multiline, sext, snippet, snippet_opt, - snippet_with_applicability, snippet_with_macro_callsite, span_lint, span_lint_and_help, span_lint_and_sugg, - span_lint_and_then, unsext, -}; - -declare_clippy_lint! { - /// **What it does:** Checks for use of `Box<Vec<_>>` anywhere in the code. - /// Check the [Box documentation](https://doc.rust-lang.org/std/boxed/index.html) for more information. - /// - /// **Why is this bad?** `Vec` already keeps its contents in a separate area on - /// the heap. So if you `Box` it, you just add another level of indirection - /// without any benefit whatsoever. - /// - /// **Known problems:** None. - /// - /// **Example:** - /// ```rust,ignore - /// struct X { - /// values: Box<Vec<Foo>>, - /// } - /// ``` - /// - /// Better: - /// - /// ```rust,ignore - /// struct X { - /// values: Vec<Foo>, - /// } - /// ``` - pub BOX_VEC, - perf, - "usage of `Box<Vec<T>>`, vector elements are already on the heap" -} - -declare_clippy_lint! { - /// **What it does:** Checks for use of `Vec<Box<T>>` where T: Sized anywhere in the code. - /// Check the [Box documentation](https://doc.rust-lang.org/std/boxed/index.html) for more information. - /// - /// **Why is this bad?** `Vec` already keeps its contents in a separate area on - /// the heap. So if you `Box` its contents, you just add another level of indirection. - /// - /// **Known problems:** Vec<Box<T: Sized>> makes sense if T is a large type (see [#3530](https://github.com/rust-lang/rust-clippy/issues/3530), - /// 1st comment). - /// - /// **Example:** - /// ```rust - /// struct X { - /// values: Vec<Box<i32>>, - /// } - /// ``` - /// - /// Better: - /// - /// ```rust - /// struct X { - /// values: Vec<i32>, - /// } - /// ``` - pub VEC_BOX, - complexity, - "usage of `Vec<Box<T>>` where T: Sized, vector elements are already on the heap" -} - -declare_clippy_lint! { - /// **What it does:** Checks for use of `Option<Option<_>>` in function signatures and type - /// definitions - /// - /// **Why is this bad?** `Option<_>` represents an optional value. `Option<Option<_>>` - /// represents an optional optional value which is logically the same thing as an optional - /// value but has an unneeded extra level of wrapping. - /// - /// If you have a case where `Some(Some(_))`, `Some(None)` and `None` are distinct cases, - /// consider a custom `enum` instead, with clear names for each case. - /// - /// **Known problems:** None. - /// - /// **Example** - /// ```rust - /// fn get_data() -> Option<Option<u32>> { - /// None - /// } - /// ``` - /// - /// Better: - /// - /// ```rust - /// pub enum Contents { - /// Data(Vec<u8>), // Was Some(Some(Vec<u8>)) - /// NotYetFetched, // Was Some(None) - /// None, // Was None - /// } - /// - /// fn get_data() -> Contents { - /// Contents::None - /// } - /// ``` - pub OPTION_OPTION, - pedantic, - "usage of `Option<Option<T>>`" -} - -declare_clippy_lint! { - /// **What it does:** Checks for usage of any `LinkedList`, suggesting to use a - /// `Vec` or a `VecDeque` (formerly called `RingBuf`). - /// - /// **Why is this bad?** Gankro says: - /// - /// > The TL;DR of `LinkedList` is that it's built on a massive amount of - /// pointers and indirection. - /// > It wastes memory, it has terrible cache locality, and is all-around slow. - /// `RingBuf`, while - /// > "only" amortized for push/pop, should be faster in the general case for - /// almost every possible - /// > workload, and isn't even amortized at all if you can predict the capacity - /// you need. - /// > - /// > `LinkedList`s are only really good if you're doing a lot of merging or - /// splitting of lists. - /// > This is because they can just mangle some pointers instead of actually - /// copying the data. Even - /// > if you're doing a lot of insertion in the middle of the list, `RingBuf` - /// can still be better - /// > because of how expensive it is to seek to the middle of a `LinkedList`. - /// - /// **Known problems:** False positives – the instances where using a - /// `LinkedList` makes sense are few and far between, but they can still happen. - /// - /// **Example:** - /// ```rust - /// # use std::collections::LinkedList; - /// let x: LinkedList<usize> = LinkedList::new(); - /// ``` - pub LINKEDLIST, - pedantic, - "usage of LinkedList, usually a vector is faster, or a more specialized data structure like a `VecDeque`" -} - -declare_clippy_lint! { - /// **What it does:** Checks for use of `&Box<T>` anywhere in the code. - /// 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. - /// - /// **Known problems:** None. - /// - /// **Example:** - /// ```rust,ignore - /// fn foo(bar: &Box<T>) { ... } - /// ``` - /// - /// Better: - /// - /// ```rust,ignore - /// fn foo(bar: &T) { ... } - /// ``` - pub BORROWED_BOX, - complexity, - "a borrow of a boxed type" -} - -declare_clippy_lint! { - /// **What it does:** Checks for use of redundant allocations anywhere in the code. - /// - /// **Why is this bad?** Expressions such as `Rc<&T>`, `Rc<Rc<T>>`, `Rc<Box<T>>`, `Box<&T>` - /// add an unnecessary level of indirection. - /// - /// **Known problems:** None. - /// - /// **Example:** - /// ```rust - /// # use std::rc::Rc; - /// fn foo(bar: Rc<&usize>) {} - /// ``` - /// - /// Better: - /// - /// ```rust - /// fn foo(bar: &usize) {} - /// ``` - pub REDUNDANT_ALLOCATION, - perf, - "redundant allocation" -} - -declare_clippy_lint! { - /// **What it does:** Checks for `Rc<T>` and `Arc<T>` when `T` is a mutable buffer type such as `String` or `Vec`. - /// - /// **Why is this bad?** Expressions such as `Rc<String>` usually have no advantage over `Rc<str>`, since - /// it is larger and involves an extra level of indirection, and doesn't implement `Borrow<str>`. - /// - /// While mutating a buffer type would still be possible with `Rc::get_mut()`, it only - /// works if there are no additional references yet, which usually defeats the purpose of - /// enclosing it in a shared ownership type. Instead, additionally wrapping the inner - /// type with an interior mutable container (such as `RefCell` or `Mutex`) would normally - /// be used. - /// - /// **Known problems:** This pattern can be desirable to avoid the overhead of a `RefCell` or `Mutex` for - /// cases where mutation only happens before there are any additional references. - /// - /// **Example:** - /// ```rust,ignore - /// # use std::rc::Rc; - /// fn foo(interned: Rc<String>) { ... } - /// ``` - /// - /// Better: - /// - /// ```rust,ignore - /// fn foo(interned: Rc<str>) { ... } - /// ``` - pub RC_BUFFER, - restriction, - "shared ownership of a buffer type" -} - -pub struct Types { - vec_box_size_threshold: u64, -} - -impl_lint_pass!(Types => [BOX_VEC, VEC_BOX, OPTION_OPTION, LINKEDLIST, BORROWED_BOX, REDUNDANT_ALLOCATION, RC_BUFFER]); - -impl<'tcx> LateLintPass<'tcx> for Types { - fn check_fn(&mut self, cx: &LateContext<'_>, _: FnKind<'_>, decl: &FnDecl<'_>, _: &Body<'_>, _: Span, id: HirId) { - // Skip trait implementations; see issue #605. - if let Some(hir::Node::Item(item)) = cx.tcx.hir().find(cx.tcx.hir().get_parent_item(id)) { - if let ItemKind::Impl(hir::Impl { of_trait: Some(_), .. }) = item.kind { - return; - } - } - - self.check_fn_decl(cx, decl); - } - - fn check_struct_field(&mut self, cx: &LateContext<'_>, field: &hir::StructField<'_>) { - self.check_ty(cx, &field.ty, false); - } - - fn check_trait_item(&mut self, cx: &LateContext<'_>, item: &TraitItem<'_>) { - match item.kind { - TraitItemKind::Const(ref ty, _) | TraitItemKind::Type(_, Some(ref ty)) => self.check_ty(cx, ty, false), - TraitItemKind::Fn(ref sig, _) => self.check_fn_decl(cx, &sig.decl), - _ => (), - } - } - - fn check_local(&mut self, cx: &LateContext<'_>, local: &Local<'_>) { - if let Some(ref ty) = local.ty { - self.check_ty(cx, ty, true); - } - } -} - -/// Checks if `qpath` has last segment with type parameter matching `path` -fn match_type_parameter(cx: &LateContext<'_>, qpath: &QPath<'_>, path: &[&str]) -> Option<Span> { - let last = last_path_segment(qpath); - if_chain! { - if let Some(ref params) = last.args; - if !params.parenthesized; - if let Some(ty) = params.args.iter().find_map(|arg| match arg { - GenericArg::Type(ty) => Some(ty), - _ => None, - }); - if let TyKind::Path(ref qpath) = ty.kind; - if let Some(did) = cx.qpath_res(qpath, ty.hir_id).opt_def_id(); - if match_def_path(cx, did, path); - then { - return Some(ty.span); - } - } - None -} - -fn match_buffer_type(cx: &LateContext<'_>, qpath: &QPath<'_>) -> Option<&'static str> { - if match_type_parameter(cx, qpath, &paths::STRING).is_some() { - return Some("str"); - } - if match_type_parameter(cx, qpath, &paths::OS_STRING).is_some() { - return Some("std::ffi::OsStr"); - } - if match_type_parameter(cx, qpath, &paths::PATH_BUF).is_some() { - return Some("std::path::Path"); - } - None -} - -fn match_borrows_parameter(_cx: &LateContext<'_>, qpath: &QPath<'_>) -> Option<Span> { - let last = last_path_segment(qpath); - if_chain! { - if let Some(ref params) = last.args; - if !params.parenthesized; - if let Some(ty) = params.args.iter().find_map(|arg| match arg { - GenericArg::Type(ty) => Some(ty), - _ => None, - }); - if let TyKind::Rptr(..) = ty.kind; - then { - return Some(ty.span); - } - } - None -} - -impl Types { - pub fn new(vec_box_size_threshold: u64) -> Self { - Self { vec_box_size_threshold } - } - - fn check_fn_decl(&mut self, cx: &LateContext<'_>, decl: &FnDecl<'_>) { - for input in decl.inputs { - self.check_ty(cx, input, false); - } - - if let FnRetTy::Return(ref ty) = decl.output { - self.check_ty(cx, ty, false); - } - } - - /// Recursively check for `TypePass` lints in the given type. Stop at the first - /// lint found. - /// - /// The parameter `is_local` distinguishes the context of the type; types from - /// local bindings should only be checked for the `BORROWED_BOX` lint. - #[allow(clippy::too_many_lines)] - fn check_ty(&mut self, cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, is_local: bool) { - if hir_ty.span.from_expansion() { - return; - } - match hir_ty.kind { - TyKind::Path(ref qpath) if !is_local => { - let hir_id = hir_ty.hir_id; - let res = cx.qpath_res(qpath, hir_id); - if let Some(def_id) = res.opt_def_id() { - if Some(def_id) == cx.tcx.lang_items().owned_box() { - if let Some(span) = match_borrows_parameter(cx, qpath) { - let mut applicability = Applicability::MachineApplicable; - span_lint_and_sugg( - cx, - REDUNDANT_ALLOCATION, - hir_ty.span, - "usage of `Box<&T>`", - "try", - snippet_with_applicability(cx, span, "..", &mut applicability).to_string(), - applicability, - ); - return; // don't recurse into the type - } - if match_type_parameter(cx, qpath, &paths::VEC).is_some() { - span_lint_and_help( - cx, - BOX_VEC, - hir_ty.span, - "you seem to be trying to use `Box<Vec<T>>`. Consider using just `Vec<T>`", - None, - "`Vec<T>` is already on the heap, `Box<Vec<T>>` makes an extra allocation.", - ); - return; // don't recurse into the type - } - } else if cx.tcx.is_diagnostic_item(sym::Rc, def_id) { - if let Some(span) = match_type_parameter(cx, qpath, &paths::RC) { - let mut applicability = Applicability::MachineApplicable; - span_lint_and_sugg( - cx, - REDUNDANT_ALLOCATION, - hir_ty.span, - "usage of `Rc<Rc<T>>`", - "try", - snippet_with_applicability(cx, span, "..", &mut applicability).to_string(), - applicability, - ); - return; // don't recurse into the type - } - if match_type_parameter(cx, qpath, &paths::BOX).is_some() { - let box_ty = match &last_path_segment(qpath).args.unwrap().args[0] { - GenericArg::Type(ty) => match &ty.kind { - TyKind::Path(qpath) => qpath, - _ => return, - }, - _ => return, - }; - let inner_span = match &last_path_segment(&box_ty).args.unwrap().args[0] { - GenericArg::Type(ty) => ty.span, - _ => return, - }; - let mut applicability = Applicability::MachineApplicable; - span_lint_and_sugg( - cx, - REDUNDANT_ALLOCATION, - hir_ty.span, - "usage of `Rc<Box<T>>`", - "try", - format!( - "Rc<{}>", - snippet_with_applicability(cx, inner_span, "..", &mut applicability) - ), - applicability, - ); - return; // don't recurse into the type - } - if let Some(alternate) = match_buffer_type(cx, qpath) { - span_lint_and_sugg( - cx, - RC_BUFFER, - hir_ty.span, - "usage of `Rc<T>` when T is a buffer type", - "try", - format!("Rc<{}>", alternate), - Applicability::MachineApplicable, - ); - return; // don't recurse into the type - } - if match_type_parameter(cx, qpath, &paths::VEC).is_some() { - let vec_ty = match &last_path_segment(qpath).args.unwrap().args[0] { - GenericArg::Type(ty) => match &ty.kind { - TyKind::Path(qpath) => qpath, - _ => return, - }, - _ => return, - }; - let inner_span = match &last_path_segment(&vec_ty).args.unwrap().args[0] { - GenericArg::Type(ty) => ty.span, - _ => return, - }; - let mut applicability = Applicability::MachineApplicable; - span_lint_and_sugg( - cx, - RC_BUFFER, - hir_ty.span, - "usage of `Rc<T>` when T is a buffer type", - "try", - format!( - "Rc<[{}]>", - snippet_with_applicability(cx, inner_span, "..", &mut applicability) - ), - Applicability::MachineApplicable, - ); - return; // don't recurse into the type - } - if let Some(span) = match_borrows_parameter(cx, qpath) { - let mut applicability = Applicability::MachineApplicable; - span_lint_and_sugg( - cx, - REDUNDANT_ALLOCATION, - hir_ty.span, - "usage of `Rc<&T>`", - "try", - snippet_with_applicability(cx, span, "..", &mut applicability).to_string(), - applicability, - ); - return; // don't recurse into the type - } - } else if cx.tcx.is_diagnostic_item(sym::Arc, def_id) { - if let Some(alternate) = match_buffer_type(cx, qpath) { - span_lint_and_sugg( - cx, - RC_BUFFER, - hir_ty.span, - "usage of `Arc<T>` when T is a buffer type", - "try", - format!("Arc<{}>", alternate), - Applicability::MachineApplicable, - ); - return; // don't recurse into the type - } - if match_type_parameter(cx, qpath, &paths::VEC).is_some() { - let vec_ty = match &last_path_segment(qpath).args.unwrap().args[0] { - GenericArg::Type(ty) => match &ty.kind { - TyKind::Path(qpath) => qpath, - _ => return, - }, - _ => return, - }; - let inner_span = match &last_path_segment(&vec_ty).args.unwrap().args[0] { - GenericArg::Type(ty) => ty.span, - _ => return, - }; - let mut applicability = Applicability::MachineApplicable; - span_lint_and_sugg( - cx, - RC_BUFFER, - hir_ty.span, - "usage of `Arc<T>` when T is a buffer type", - "try", - format!( - "Arc<[{}]>", - snippet_with_applicability(cx, inner_span, "..", &mut applicability) - ), - Applicability::MachineApplicable, - ); - return; // don't recurse into the type - } - } else if cx.tcx.is_diagnostic_item(sym::vec_type, def_id) { - if_chain! { - // Get the _ part of Vec<_> - if let Some(ref last) = last_path_segment(qpath).args; - if let Some(ty) = last.args.iter().find_map(|arg| match arg { - GenericArg::Type(ty) => Some(ty), - _ => None, - }); - // ty is now _ at this point - if let TyKind::Path(ref ty_qpath) = ty.kind; - let res = cx.qpath_res(ty_qpath, ty.hir_id); - if let Some(def_id) = res.opt_def_id(); - if Some(def_id) == cx.tcx.lang_items().owned_box(); - // At this point, we know ty is Box<T>, now get T - if let Some(ref last) = last_path_segment(ty_qpath).args; - if let Some(boxed_ty) = last.args.iter().find_map(|arg| match arg { - GenericArg::Type(ty) => Some(ty), - _ => None, - }); - let ty_ty = hir_ty_to_ty(cx.tcx, boxed_ty); - if !ty_ty.has_escaping_bound_vars(); - if ty_ty.is_sized(cx.tcx.at(ty.span), cx.param_env); - if let Ok(ty_ty_size) = cx.layout_of(ty_ty).map(|l| l.size.bytes()); - if ty_ty_size <= self.vec_box_size_threshold; - then { - span_lint_and_sugg( - cx, - VEC_BOX, - hir_ty.span, - "`Vec<T>` is already on the heap, the boxing is unnecessary.", - "try", - format!("Vec<{}>", snippet(cx, boxed_ty.span, "..")), - Applicability::MachineApplicable, - ); - return; // don't recurse into the type - } - } - } else if cx.tcx.is_diagnostic_item(sym::option_type, def_id) { - if match_type_parameter(cx, qpath, &paths::OPTION).is_some() { - span_lint( - cx, - OPTION_OPTION, - hir_ty.span, - "consider using `Option<T>` instead of `Option<Option<T>>` or a custom \ - enum if you need to distinguish all 3 cases", - ); - return; // don't recurse into the type - } - } else if match_def_path(cx, def_id, &paths::LINKED_LIST) { - span_lint_and_help( - cx, - LINKEDLIST, - hir_ty.span, - "I see you're using a LinkedList! Perhaps you meant some other data structure?", - None, - "a `VecDeque` might work", - ); - return; // don't recurse into the type - } - } - match *qpath { - QPath::Resolved(Some(ref ty), ref p) => { - self.check_ty(cx, ty, is_local); - for ty in p.segments.iter().flat_map(|seg| { - seg.args - .as_ref() - .map_or_else(|| [].iter(), |params| params.args.iter()) - .filter_map(|arg| match arg { - GenericArg::Type(ty) => Some(ty), - _ => None, - }) - }) { - self.check_ty(cx, ty, is_local); - } - }, - QPath::Resolved(None, ref p) => { - for ty in p.segments.iter().flat_map(|seg| { - seg.args - .as_ref() - .map_or_else(|| [].iter(), |params| params.args.iter()) - .filter_map(|arg| match arg { - GenericArg::Type(ty) => Some(ty), - _ => None, - }) - }) { - self.check_ty(cx, ty, is_local); - } - }, - QPath::TypeRelative(ref ty, ref seg) => { - self.check_ty(cx, ty, is_local); - if let Some(ref params) = seg.args { - for ty in params.args.iter().filter_map(|arg| match arg { - GenericArg::Type(ty) => Some(ty), - _ => None, - }) { - self.check_ty(cx, ty, is_local); - } - } - }, - QPath::LangItem(..) => {}, - } - }, - TyKind::Rptr(ref lt, ref mut_ty) => self.check_ty_rptr(cx, hir_ty, is_local, lt, mut_ty), - // recurse - TyKind::Slice(ref ty) | TyKind::Array(ref ty, _) | TyKind::Ptr(MutTy { ref ty, .. }) => { - self.check_ty(cx, ty, is_local) - }, - TyKind::Tup(tys) => { - for ty in tys { - self.check_ty(cx, ty, is_local); - } - }, - _ => {}, - } - } - - fn check_ty_rptr( - &mut self, - cx: &LateContext<'_>, - hir_ty: &hir::Ty<'_>, - is_local: bool, - lt: &Lifetime, - mut_ty: &MutTy<'_>, - ) { - match mut_ty.ty.kind { - TyKind::Path(ref qpath) => { - let hir_id = mut_ty.ty.hir_id; - let def = cx.qpath_res(qpath, hir_id); - if_chain! { - if let Some(def_id) = def.opt_def_id(); - if Some(def_id) == cx.tcx.lang_items().owned_box(); - if let QPath::Resolved(None, ref path) = *qpath; - if let [ref bx] = *path.segments; - if let Some(ref params) = bx.args; - if !params.parenthesized; - if let Some(inner) = params.args.iter().find_map(|arg| match arg { - GenericArg::Type(ty) => Some(ty), - _ => None, - }); - then { - if is_any_trait(inner) { - // Ignore `Box<Any>` types; see issue #1884 for details. - return; - } - - let ltopt = if lt.is_elided() { - String::new() - } else { - format!("{} ", lt.name.ident().as_str()) - }; - - if mut_ty.mutbl == Mutability::Mut { - // Ignore `&mut Box<T>` types; see issue #2907 for - // details. - return; - } - - // When trait objects or opaque types have lifetime or auto-trait bounds, - // we need to add parentheses to avoid a syntax error due to its ambiguity. - // Originally reported as the issue #3128. - let inner_snippet = snippet(cx, inner.span, ".."); - let suggestion = match &inner.kind { - TyKind::TraitObject(bounds, lt_bound) if bounds.len() > 1 || !lt_bound.is_elided() => { - format!("&{}({})", ltopt, &inner_snippet) - }, - TyKind::Path(qpath) - if get_bounds_if_impl_trait(cx, qpath, inner.hir_id) - .map_or(false, |bounds| bounds.len() > 1) => - { - format!("&{}({})", ltopt, &inner_snippet) - }, - _ => format!("&{}{}", ltopt, &inner_snippet), - }; - span_lint_and_sugg( - cx, - BORROWED_BOX, - hir_ty.span, - "you seem to be trying to use `&Box<T>`. Consider using just `&T`", - "try", - suggestion, - // To make this `MachineApplicable`, at least one needs to check if it isn't a trait item - // because the trait impls of it will break otherwise; - // and there may be other cases that result in invalid code. - // For example, type coercion doesn't work nicely. - Applicability::Unspecified, - ); - return; // don't recurse into the type - } - }; - self.check_ty(cx, &mut_ty.ty, is_local); - }, - _ => self.check_ty(cx, &mut_ty.ty, is_local), - } - } -} - -// Returns true if given type is `Any` trait. -fn is_any_trait(t: &hir::Ty<'_>) -> bool { - if_chain! { - if let TyKind::TraitObject(ref traits, _) = t.kind; - if !traits.is_empty(); - // Only Send/Sync can be used as additional traits, so it is enough to - // check only the first trait. - if match_path(&traits[0].trait_ref.path, &paths::ANY_TRAIT); - then { - return true; - } - } - - false -} - -fn get_bounds_if_impl_trait<'tcx>(cx: &LateContext<'tcx>, qpath: &QPath<'_>, id: HirId) -> Option<GenericBounds<'tcx>> { - if_chain! { - if let Some(did) = cx.qpath_res(qpath, id).opt_def_id(); - if let Some(Node::GenericParam(generic_param)) = cx.tcx.hir().get_if_local(did); - if let GenericParamKind::Type { synthetic, .. } = generic_param.kind; - if synthetic == Some(SyntheticTyParamKind::ImplTrait); - then { - Some(generic_param.bounds) - } else { - None - } - } -} - -declare_clippy_lint! { - /// **What it does:** Checks for binding a unit value. - /// - /// **Why is this bad?** A unit value cannot usefully be used anywhere. So - /// binding one is kind of pointless. - /// - /// **Known problems:** None. - /// - /// **Example:** - /// ```rust - /// let x = { - /// 1; - /// }; - /// ``` - pub LET_UNIT_VALUE, - pedantic, - "creating a `let` binding to a value of unit type, which usually can't be used afterwards" -} - -declare_lint_pass!(LetUnitValue => [LET_UNIT_VALUE]); - -impl<'tcx> LateLintPass<'tcx> for LetUnitValue { - fn check_stmt(&mut self, cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'_>) { - if let StmtKind::Local(ref local) = stmt.kind { - if is_unit(cx.typeck_results().pat_ty(&local.pat)) { - if in_external_macro(cx.sess(), stmt.span) || local.pat.span.from_expansion() { - return; - } - if higher::is_from_for_desugar(local) { - return; - } - span_lint_and_then( - cx, - LET_UNIT_VALUE, - stmt.span, - "this let-binding has unit value", - |diag| { - if let Some(expr) = &local.init { - let snip = snippet_with_macro_callsite(cx, expr.span, "()"); - diag.span_suggestion( - stmt.span, - "omit the `let` binding", - format!("{};", snip), - Applicability::MachineApplicable, // snippet - ); - } - }, - ); - } - } - } -} - -declare_clippy_lint! { - /// **What it does:** Checks for comparisons to unit. This includes all binary - /// comparisons (like `==` and `<`) and asserts. - /// - /// **Why is this bad?** Unit is always equal to itself, and thus is just a - /// clumsily written constant. Mostly this happens when someone accidentally - /// adds semicolons at the end of the operands. - /// - /// **Known problems:** None. - /// - /// **Example:** - /// ```rust - /// # fn foo() {}; - /// # fn bar() {}; - /// # fn baz() {}; - /// if { - /// foo(); - /// } == { - /// bar(); - /// } { - /// baz(); - /// } - /// ``` - /// is equal to - /// ```rust - /// # fn foo() {}; - /// # fn bar() {}; - /// # fn baz() {}; - /// { - /// foo(); - /// bar(); - /// baz(); - /// } - /// ``` - /// - /// For asserts: - /// ```rust - /// # fn foo() {}; - /// # fn bar() {}; - /// assert_eq!({ foo(); }, { bar(); }); - /// ``` - /// will always succeed - pub UNIT_CMP, - correctness, - "comparing unit values" -} - -declare_lint_pass!(UnitCmp => [UNIT_CMP]); - -impl<'tcx> LateLintPass<'tcx> for UnitCmp { - fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { - 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, ref left, _) = expr.kind { - let op = cmp.node; - if op.is_comparison() && is_unit(cx.typeck_results().expr_ty(left)) { - 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 - ), - ); - } - } - } - } - return; - } - if let ExprKind::Binary(ref cmp, ref left, _) = expr.kind { - let op = cmp.node; - if op.is_comparison() && is_unit(cx.typeck_results().expr_ty(left)) { - let result = match op { - BinOpKind::Eq | BinOpKind::Le | BinOpKind::Ge => "true", - _ => "false", - }; - span_lint( - cx, - UNIT_CMP, - expr.span, - &format!( - "{}-comparison of unit values detected. This will always be {}", - op.as_str(), - result - ), - ); - } - } - } -} - -declare_clippy_lint! { - /// **What it does:** Checks for passing a unit value as an argument to a function without using a - /// unit literal (`()`). - /// - /// **Why is this bad?** This is likely the result of an accidental semicolon. - /// - /// **Known problems:** None. - /// - /// **Example:** - /// ```rust,ignore - /// foo({ - /// let a = bar(); - /// baz(a); - /// }) - /// ``` - pub UNIT_ARG, - complexity, - "passing unit to a function" -} - -declare_lint_pass!(UnitArg => [UNIT_ARG]); - -impl<'tcx> LateLintPass<'tcx> for UnitArg { - fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - if expr.span.from_expansion() { - return; - } - - // apparently stuff in the desugaring of `?` can trigger this - // so check for that here - // only the calls to `Try::from_error` is marked as desugared, - // so we need to check both the current Expr and its parent. - if is_questionmark_desugar_marked_call(expr) { - return; - } - if_chain! { - let map = &cx.tcx.hir(); - let opt_parent_node = map.find(map.get_parent_node(expr.hir_id)); - if let Some(hir::Node::Expr(parent_expr)) = opt_parent_node; - if is_questionmark_desugar_marked_call(parent_expr); - then { - return; - } - } - - match expr.kind { - ExprKind::Call(_, args) | ExprKind::MethodCall(_, _, args, _) => { - let args_to_recover = args - .iter() - .filter(|arg| { - if is_unit(cx.typeck_results().expr_ty(arg)) && !is_unit_literal(arg) { - !matches!(&arg.kind, ExprKind::Match(.., MatchSource::TryDesugar)) - } else { - false - } - }) - .collect::<Vec<_>>(); - if !args_to_recover.is_empty() { - lint_unit_args(cx, expr, &args_to_recover); - } - }, - _ => (), - } - } -} - -fn fmt_stmts_and_call( - cx: &LateContext<'_>, - call_expr: &Expr<'_>, - call_snippet: &str, - args_snippets: &[impl AsRef<str>], - non_empty_block_args_snippets: &[impl AsRef<str>], -) -> String { - let call_expr_indent = indent_of(cx, call_expr.span).unwrap_or(0); - let call_snippet_with_replacements = args_snippets - .iter() - .fold(call_snippet.to_owned(), |acc, arg| acc.replacen(arg.as_ref(), "()", 1)); - - let mut stmts_and_call = non_empty_block_args_snippets - .iter() - .map(|it| it.as_ref().to_owned()) - .collect::<Vec<_>>(); - stmts_and_call.push(call_snippet_with_replacements); - stmts_and_call = stmts_and_call - .into_iter() - .map(|v| reindent_multiline(v.into(), true, Some(call_expr_indent)).into_owned()) - .collect(); - - let mut stmts_and_call_snippet = stmts_and_call.join(&format!("{}{}", ";\n", " ".repeat(call_expr_indent))); - // expr is not in a block statement or result expression position, wrap in a block - let parent_node = cx.tcx.hir().find(cx.tcx.hir().get_parent_node(call_expr.hir_id)); - if !matches!(parent_node, Some(Node::Block(_))) && !matches!(parent_node, Some(Node::Stmt(_))) { - let block_indent = call_expr_indent + 4; - stmts_and_call_snippet = - reindent_multiline(stmts_and_call_snippet.into(), true, Some(block_indent)).into_owned(); - stmts_and_call_snippet = format!( - "{{\n{}{}\n{}}}", - " ".repeat(block_indent), - &stmts_and_call_snippet, - " ".repeat(call_expr_indent) - ); - } - stmts_and_call_snippet -} - -fn lint_unit_args(cx: &LateContext<'_>, expr: &Expr<'_>, args_to_recover: &[&Expr<'_>]) { - let mut applicability = Applicability::MachineApplicable; - let (singular, plural) = if args_to_recover.len() > 1 { - ("", "s") - } else { - ("a ", "") - }; - span_lint_and_then( - cx, - UNIT_ARG, - expr.span, - &format!("passing {}unit value{} to a function", singular, plural), - |db| { - let mut or = ""; - args_to_recover - .iter() - .filter_map(|arg| { - if_chain! { - if let ExprKind::Block(block, _) = arg.kind; - if block.expr.is_none(); - if let Some(last_stmt) = block.stmts.iter().last(); - if let StmtKind::Semi(last_expr) = last_stmt.kind; - if let Some(snip) = snippet_opt(cx, last_expr.span); - then { - Some(( - last_stmt.span, - snip, - )) - } - else { - None - } - } - }) - .for_each(|(span, sugg)| { - db.span_suggestion( - span, - "remove the semicolon from the last statement in the block", - sugg, - Applicability::MaybeIncorrect, - ); - or = "or "; - applicability = Applicability::MaybeIncorrect; - }); - - let arg_snippets: Vec<String> = args_to_recover - .iter() - .filter_map(|arg| snippet_opt(cx, arg.span)) - .collect(); - let arg_snippets_without_empty_blocks: Vec<String> = args_to_recover - .iter() - .filter(|arg| !is_empty_block(arg)) - .filter_map(|arg| snippet_opt(cx, arg.span)) - .collect(); - - if let Some(call_snippet) = snippet_opt(cx, expr.span) { - let sugg = fmt_stmts_and_call( - cx, - expr, - &call_snippet, - &arg_snippets, - &arg_snippets_without_empty_blocks, - ); - - if arg_snippets_without_empty_blocks.is_empty() { - db.multipart_suggestion( - &format!("use {}unit literal{} instead", singular, plural), - args_to_recover - .iter() - .map(|arg| (arg.span, "()".to_string())) - .collect::<Vec<_>>(), - applicability, - ); - } else { - let plural = arg_snippets_without_empty_blocks.len() > 1; - let empty_or_s = if plural { "s" } else { "" }; - let it_or_them = if plural { "them" } else { "it" }; - db.span_suggestion( - expr.span, - &format!( - "{}move the expression{} in front of the call and replace {} with the unit literal `()`", - or, empty_or_s, it_or_them - ), - sugg, - applicability, - ); - } - } - }, - ); -} - -fn is_empty_block(expr: &Expr<'_>) -> bool { - matches!( - expr.kind, - ExprKind::Block( - Block { - stmts: &[], - expr: None, - .. - }, - _, - ) - ) -} - -fn is_questionmark_desugar_marked_call(expr: &Expr<'_>) -> bool { - use rustc_span::hygiene::DesugaringKind; - if let ExprKind::Call(ref callee, _) = expr.kind { - callee.span.is_desugaring(DesugaringKind::QuestionMark) - } else { - false - } -} - -fn is_unit(ty: Ty<'_>) -> bool { - matches!(ty.kind(), ty::Tuple(slice) if slice.is_empty()) -} - -fn is_unit_literal(expr: &Expr<'_>) -> bool { - matches!(expr.kind, ExprKind::Tup(ref slice) if slice.is_empty()) -} - -declare_clippy_lint! { - /// **What it does:** Checks for casts from any numerical to a float type where - /// the receiving type cannot store all values from the original type without - /// rounding errors. This possible rounding is to be expected, so this lint is - /// `Allow` by default. - /// - /// Basically, this warns on casting any integer with 32 or more bits to `f32` - /// or any 64-bit integer to `f64`. - /// - /// **Why is this bad?** It's not bad at all. But in some applications it can be - /// helpful to know where precision loss can take place. This lint can help find - /// those places in the code. - /// - /// **Known problems:** None. - /// - /// **Example:** - /// ```rust - /// let x = u64::MAX; - /// x as f64; - /// ``` - pub CAST_PRECISION_LOSS, - pedantic, - "casts that cause loss of precision, e.g., `x as f32` where `x: u64`" -} - -declare_clippy_lint! { - /// **What it does:** Checks for casts from a signed to an unsigned numerical - /// type. In this case, negative values wrap around to large positive values, - /// which can be quite surprising in practice. However, as the cast works as - /// defined, this lint is `Allow` by default. - /// - /// **Why is this bad?** Possibly surprising results. You can activate this lint - /// as a one-time check to see where numerical wrapping can arise. - /// - /// **Known problems:** None. - /// - /// **Example:** - /// ```rust - /// let y: i8 = -1; - /// y as u128; // will return 18446744073709551615 - /// ``` - pub CAST_SIGN_LOSS, - pedantic, - "casts from signed types to unsigned types, e.g., `x as u32` where `x: i32`" -} - -declare_clippy_lint! { - /// **What it does:** Checks for casts between numerical types that may - /// truncate large values. This is expected behavior, so the cast is `Allow` by - /// default. - /// - /// **Why is this bad?** In some problem domains, it is good practice to avoid - /// truncation. This lint can be activated to help assess where additional - /// checks could be beneficial. - /// - /// **Known problems:** None. - /// - /// **Example:** - /// ```rust - /// fn as_u8(x: u64) -> u8 { - /// x as u8 - /// } - /// ``` - pub CAST_POSSIBLE_TRUNCATION, - pedantic, - "casts that may cause truncation of the value, e.g., `x as u8` where `x: u32`, or `x as i32` where `x: f32`" -} - -declare_clippy_lint! { - /// **What it does:** Checks for casts from an unsigned type to a signed type of - /// the same size. Performing such a cast is a 'no-op' for the compiler, - /// i.e., nothing is changed at the bit level, and the binary representation of - /// the value is reinterpreted. This can cause wrapping if the value is too big - /// for the target signed type. However, the cast works as defined, so this lint - /// is `Allow` by default. - /// - /// **Why is this bad?** While such a cast is not bad in itself, the results can - /// be surprising when this is not the intended behavior, as demonstrated by the - /// example below. - /// - /// **Known problems:** None. - /// - /// **Example:** - /// ```rust - /// u32::MAX as i32; // will yield a value of `-1` - /// ``` - pub CAST_POSSIBLE_WRAP, - pedantic, - "casts that may cause wrapping around the value, e.g., `x as i32` where `x: u32` and `x > i32::MAX`" -} - -declare_clippy_lint! { - /// **What it does:** Checks for casts between numerical types that may - /// be replaced by safe conversion functions. - /// - /// **Why is this bad?** Rust's `as` keyword will perform many kinds of - /// conversions, including silently lossy conversions. Conversion functions such - /// as `i32::from` will only perform lossless conversions. Using the conversion - /// functions prevents conversions from turning into silent lossy conversions if - /// the types of the input expressions ever change, and make it easier for - /// people reading the code to know that the conversion is lossless. - /// - /// **Known problems:** None. - /// - /// **Example:** - /// ```rust - /// fn as_u64(x: u8) -> u64 { - /// x as u64 - /// } - /// ``` - /// - /// Using `::from` would look like this: - /// - /// ```rust - /// fn as_u64(x: u8) -> u64 { - /// u64::from(x) - /// } - /// ``` - pub CAST_LOSSLESS, - pedantic, - "casts using `as` that are known to be lossless, e.g., `x as u64` where `x: u8`" -} - -declare_clippy_lint! { - /// **What it does:** Checks for casts to the same type, casts of int literals to integer types - /// and casts of float literals to float types. - /// - /// **Why is this bad?** It's just unnecessary. - /// - /// **Known problems:** None. - /// - /// **Example:** - /// ```rust - /// let _ = 2i32 as i32; - /// let _ = 0.5 as f32; - /// ``` - /// - /// Better: - /// - /// ```rust - /// let _ = 2_i32; - /// let _ = 0.5_f32; - /// ``` - pub UNNECESSARY_CAST, - complexity, - "cast to the same type, e.g., `x as i32` where `x: i32`" -} - -declare_clippy_lint! { - /// **What it does:** Checks for casts, using `as` or `pointer::cast`, - /// from a less-strictly-aligned pointer to a more-strictly-aligned pointer - /// - /// **Why is this bad?** Dereferencing the resulting pointer may be undefined - /// behavior. - /// - /// **Known problems:** Using `std::ptr::read_unaligned` and `std::ptr::write_unaligned` or similar - /// on the resulting pointer is fine. Is over-zealous: Casts with manual alignment checks or casts like - /// u64-> u8 -> u16 can be fine. Miri is able to do a more in-depth analysis. - /// - /// **Example:** - /// ```rust - /// let _ = (&1u8 as *const u8) as *const u16; - /// let _ = (&mut 1u8 as *mut u8) as *mut u16; - /// - /// (&1u8 as *const u8).cast::<u16>(); - /// (&mut 1u8 as *mut u8).cast::<u16>(); - /// ``` - pub CAST_PTR_ALIGNMENT, - pedantic, - "cast from a pointer to a more-strictly-aligned pointer" -} - -declare_clippy_lint! { - /// **What it does:** Checks for casts of function pointers to something other than usize - /// - /// **Why is this bad?** - /// Casting a function pointer to anything other than usize/isize is not portable across - /// architectures, because you end up losing bits if the target type is too small or end up with a - /// bunch of extra bits that waste space and add more instructions to the final binary than - /// strictly necessary for the problem - /// - /// Casting to isize also doesn't make sense since there are no signed addresses. - /// - /// **Example** - /// - /// ```rust - /// // Bad - /// fn fun() -> i32 { 1 } - /// let a = fun as i64; - /// - /// // Good - /// fn fun2() -> i32 { 1 } - /// let a = fun2 as usize; - /// ``` - pub FN_TO_NUMERIC_CAST, - style, - "casting a function pointer to a numeric type other than usize" -} - -declare_clippy_lint! { - /// **What it does:** Checks for casts of a function pointer to a numeric type not wide enough to - /// store address. - /// - /// **Why is this bad?** - /// Such a cast discards some bits of the function's address. If this is intended, it would be more - /// clearly expressed by casting to usize first, then casting the usize to the intended type (with - /// a comment) to perform the truncation. - /// - /// **Example** - /// - /// ```rust - /// // Bad - /// fn fn1() -> i16 { - /// 1 - /// }; - /// let _ = fn1 as i32; - /// - /// // Better: Cast to usize first, then comment with the reason for the truncation - /// fn fn2() -> i16 { - /// 1 - /// }; - /// let fn_ptr = fn2 as usize; - /// let fn_ptr_truncated = fn_ptr as i32; - /// ``` - pub FN_TO_NUMERIC_CAST_WITH_TRUNCATION, - style, - "casting a function pointer to a numeric type not wide enough to store the address" -} - -/// Returns the size in bits of an integral type. -/// Will return 0 if the type is not an int or uint variant -fn int_ty_to_nbits(typ: Ty<'_>, tcx: TyCtxt<'_>) -> u64 { - match typ.kind() { - ty::Int(i) => match i { - IntTy::Isize => tcx.data_layout.pointer_size.bits(), - IntTy::I8 => 8, - IntTy::I16 => 16, - IntTy::I32 => 32, - IntTy::I64 => 64, - IntTy::I128 => 128, - }, - ty::Uint(i) => match i { - UintTy::Usize => tcx.data_layout.pointer_size.bits(), - UintTy::U8 => 8, - UintTy::U16 => 16, - UintTy::U32 => 32, - UintTy::U64 => 64, - UintTy::U128 => 128, - }, - _ => 0, - } -} - -fn is_isize_or_usize(typ: Ty<'_>) -> bool { - matches!(typ.kind(), ty::Int(IntTy::Isize) | ty::Uint(UintTy::Usize)) -} - -fn span_precision_loss_lint(cx: &LateContext<'_>, expr: &Expr<'_>, cast_from: Ty<'_>, cast_to_f64: bool) { - let mantissa_nbits = if cast_to_f64 { 52 } else { 23 }; - let arch_dependent = is_isize_or_usize(cast_from) && cast_to_f64; - let arch_dependent_str = "on targets with 64-bit wide pointers "; - let from_nbits_str = if arch_dependent { - "64".to_owned() - } else if is_isize_or_usize(cast_from) { - "32 or 64".to_owned() - } else { - int_ty_to_nbits(cast_from, cx.tcx).to_string() - }; - span_lint( - cx, - CAST_PRECISION_LOSS, - expr.span, - &format!( - "casting `{0}` to `{1}` causes a loss of precision {2}(`{0}` is {3} bits wide, \ - but `{1}`'s mantissa is only {4} bits wide)", - cast_from, - if cast_to_f64 { "f64" } else { "f32" }, - if arch_dependent { arch_dependent_str } else { "" }, - from_nbits_str, - mantissa_nbits - ), - ); -} - -fn should_strip_parens(op: &Expr<'_>, snip: &str) -> bool { - if let ExprKind::Binary(_, _, _) = op.kind { - if snip.starts_with('(') && snip.ends_with(')') { - return true; - } - } - false -} - -fn span_lossless_lint(cx: &LateContext<'_>, expr: &Expr<'_>, op: &Expr<'_>, cast_from: Ty<'_>, cast_to: Ty<'_>) { - // Do not suggest using From in consts/statics until it is valid to do so (see #2267). - if in_constant(cx, expr.hir_id) { - return; - } - // The suggestion is to use a function call, so if the original expression - // has parens on the outside, they are no longer needed. - let mut applicability = Applicability::MachineApplicable; - let opt = snippet_opt(cx, op.span); - let sugg = opt.as_ref().map_or_else( - || { - applicability = Applicability::HasPlaceholders; - ".." - }, - |snip| { - if should_strip_parens(op, snip) { - &snip[1..snip.len() - 1] - } else { - snip.as_str() - } - }, - ); - - span_lint_and_sugg( - cx, - CAST_LOSSLESS, - expr.span, - &format!( - "casting `{}` to `{}` may become silently lossy if you later change the type", - cast_from, cast_to - ), - "try", - format!("{}::from({})", cast_to, sugg), - applicability, - ); -} - -enum ArchSuffix { - _32, - _64, - None, -} - -fn check_loss_of_sign(cx: &LateContext<'_>, expr: &Expr<'_>, op: &Expr<'_>, cast_from: Ty<'_>, cast_to: Ty<'_>) { - if !cast_from.is_signed() || cast_to.is_signed() { - return; - } - - // don't lint for positive constants - let const_val = constant(cx, &cx.typeck_results(), op); - if_chain! { - if let Some((Constant::Int(n), _)) = const_val; - if let ty::Int(ity) = *cast_from.kind(); - if sext(cx.tcx, n, ity) >= 0; - then { - return - } - } - - // don't lint for the result of methods that always return non-negative values - if let ExprKind::MethodCall(ref path, _, _, _) = op.kind { - let mut method_name = path.ident.name.as_str(); - let allowed_methods = ["abs", "checked_abs", "rem_euclid", "checked_rem_euclid"]; - - if_chain! { - if method_name == "unwrap"; - if let Some(arglist) = method_chain_args(op, &["unwrap"]); - if let ExprKind::MethodCall(ref inner_path, _, _, _) = &arglist[0][0].kind; - then { - method_name = inner_path.ident.name.as_str(); - } - } - - if allowed_methods.iter().any(|&name| method_name == name) { - return; - } - } - - span_lint( - cx, - CAST_SIGN_LOSS, - expr.span, - &format!( - "casting `{}` to `{}` may lose the sign of the value", - cast_from, cast_to - ), - ); -} - -fn check_truncation_and_wrapping(cx: &LateContext<'_>, expr: &Expr<'_>, cast_from: Ty<'_>, cast_to: Ty<'_>) { - let arch_64_suffix = " on targets with 64-bit wide pointers"; - let arch_32_suffix = " on targets with 32-bit wide pointers"; - let cast_unsigned_to_signed = !cast_from.is_signed() && cast_to.is_signed(); - let from_nbits = int_ty_to_nbits(cast_from, cx.tcx); - let to_nbits = int_ty_to_nbits(cast_to, cx.tcx); - let (span_truncation, suffix_truncation, span_wrap, suffix_wrap) = - match (is_isize_or_usize(cast_from), is_isize_or_usize(cast_to)) { - (true, true) | (false, false) => ( - to_nbits < from_nbits, - ArchSuffix::None, - to_nbits == from_nbits && cast_unsigned_to_signed, - ArchSuffix::None, - ), - (true, false) => ( - to_nbits <= 32, - if to_nbits == 32 { - ArchSuffix::_64 - } else { - ArchSuffix::None - }, - to_nbits <= 32 && cast_unsigned_to_signed, - ArchSuffix::_32, - ), - (false, true) => ( - from_nbits == 64, - ArchSuffix::_32, - cast_unsigned_to_signed, - if from_nbits == 64 { - ArchSuffix::_64 - } else { - ArchSuffix::_32 - }, - ), - }; - if span_truncation { - span_lint( - cx, - CAST_POSSIBLE_TRUNCATION, - expr.span, - &format!( - "casting `{}` to `{}` may truncate the value{}", - cast_from, - cast_to, - match suffix_truncation { - ArchSuffix::_32 => arch_32_suffix, - ArchSuffix::_64 => arch_64_suffix, - ArchSuffix::None => "", - } - ), - ); - } - if span_wrap { - span_lint( - cx, - CAST_POSSIBLE_WRAP, - expr.span, - &format!( - "casting `{}` to `{}` may wrap around the value{}", - cast_from, - cast_to, - match suffix_wrap { - ArchSuffix::_32 => arch_32_suffix, - ArchSuffix::_64 => arch_64_suffix, - ArchSuffix::None => "", - } - ), - ); - } -} - -fn check_lossless(cx: &LateContext<'_>, expr: &Expr<'_>, op: &Expr<'_>, cast_from: Ty<'_>, cast_to: Ty<'_>) { - let cast_signed_to_unsigned = cast_from.is_signed() && !cast_to.is_signed(); - let from_nbits = int_ty_to_nbits(cast_from, cx.tcx); - let to_nbits = int_ty_to_nbits(cast_to, cx.tcx); - if !is_isize_or_usize(cast_from) && !is_isize_or_usize(cast_to) && from_nbits < to_nbits && !cast_signed_to_unsigned - { - span_lossless_lint(cx, expr, op, cast_from, cast_to); - } -} - -declare_lint_pass!(Casts => [ - CAST_PRECISION_LOSS, - CAST_SIGN_LOSS, - CAST_POSSIBLE_TRUNCATION, - CAST_POSSIBLE_WRAP, - CAST_LOSSLESS, - UNNECESSARY_CAST, - CAST_PTR_ALIGNMENT, - FN_TO_NUMERIC_CAST, - FN_TO_NUMERIC_CAST_WITH_TRUNCATION, -]); - -// Check if the given type is either `core::ffi::c_void` or -// one of the platform specific `libc::<platform>::c_void` of libc. -fn is_c_void(cx: &LateContext<'_>, ty: Ty<'_>) -> bool { - if let ty::Adt(adt, _) = ty.kind() { - let names = cx.get_def_path(adt.did); - - if names.is_empty() { - return false; - } - if names[0] == sym::libc || names[0] == sym::core && *names.last().unwrap() == sym!(c_void) { - return true; - } - } - false -} - -/// Returns the mantissa bits wide of a fp type. -/// Will return 0 if the type is not a fp -fn fp_ty_mantissa_nbits(typ: Ty<'_>) -> u32 { - match typ.kind() { - ty::Float(FloatTy::F32) => 23, - ty::Float(FloatTy::F64) | ty::Infer(InferTy::FloatVar(_)) => 52, - _ => 0, - } -} - -impl<'tcx> LateLintPass<'tcx> for Casts { - fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - if expr.span.from_expansion() { - return; - } - if let ExprKind::Cast(ref ex, cast_to) = expr.kind { - if is_hir_ty_cfg_dependant(cx, cast_to) { - return; - } - let (cast_from, cast_to) = (cx.typeck_results().expr_ty(ex), cx.typeck_results().expr_ty(expr)); - lint_fn_to_numeric_cast(cx, expr, ex, cast_from, cast_to); - if let Some(lit) = get_numeric_literal(ex) { - let literal_str = snippet_opt(cx, ex.span).unwrap_or_default(); - - if_chain! { - if let LitKind::Int(n, _) = lit.node; - if let Some(src) = snippet_opt(cx, lit.span); - if cast_to.is_floating_point(); - if let Some(num_lit) = NumericLiteral::from_lit_kind(&src, &lit.node); - let from_nbits = 128 - n.leading_zeros(); - let to_nbits = fp_ty_mantissa_nbits(cast_to); - if from_nbits != 0 && to_nbits != 0 && from_nbits <= to_nbits && num_lit.is_decimal(); - then { - let literal_str = if is_unary_neg(ex) { format!("-{}", num_lit.integer) } else { num_lit.integer.into() }; - show_unnecessary_cast(cx, expr, &literal_str, cast_from, cast_to); - return; - } - } - - match lit.node { - LitKind::Int(_, LitIntType::Unsuffixed) if cast_to.is_integral() => { - show_unnecessary_cast(cx, expr, &literal_str, cast_from, cast_to); - }, - LitKind::Float(_, LitFloatType::Unsuffixed) if cast_to.is_floating_point() => { - show_unnecessary_cast(cx, expr, &literal_str, cast_from, cast_to); - }, - LitKind::Int(_, LitIntType::Unsuffixed) | LitKind::Float(_, LitFloatType::Unsuffixed) => {}, - _ => { - if cast_from.kind() == cast_to.kind() && !in_external_macro(cx.sess(), expr.span) { - span_lint( - cx, - UNNECESSARY_CAST, - expr.span, - &format!( - "casting to the same type is unnecessary (`{}` -> `{}`)", - cast_from, cast_to - ), - ); - } - }, - } - } - if cast_from.is_numeric() && cast_to.is_numeric() && !in_external_macro(cx.sess(), expr.span) { - lint_numeric_casts(cx, expr, ex, cast_from, cast_to); - } - - lint_cast_ptr_alignment(cx, expr, cast_from, cast_to); - } else if let ExprKind::MethodCall(method_path, _, args, _) = expr.kind { - if_chain! { - if method_path.ident.name == sym!(cast); - if let Some(generic_args) = method_path.args; - if let [GenericArg::Type(cast_to)] = generic_args.args; - // There probably is no obvious reason to do this, just to be consistent with `as` cases. - if !is_hir_ty_cfg_dependant(cx, cast_to); - then { - let (cast_from, cast_to) = - (cx.typeck_results().expr_ty(&args[0]), cx.typeck_results().expr_ty(expr)); - lint_cast_ptr_alignment(cx, expr, cast_from, cast_to); - } - } - } - } -} - -fn is_unary_neg(expr: &Expr<'_>) -> bool { - matches!(expr.kind, ExprKind::Unary(UnOp::Neg, _)) -} - -fn get_numeric_literal<'e>(expr: &'e Expr<'e>) -> Option<&'e Lit> { - match expr.kind { - ExprKind::Lit(ref lit) => Some(lit), - ExprKind::Unary(UnOp::Neg, e) => { - if let ExprKind::Lit(ref lit) = e.kind { - Some(lit) - } else { - None - } - }, - _ => None, - } -} - -fn show_unnecessary_cast(cx: &LateContext<'_>, expr: &Expr<'_>, literal_str: &str, cast_from: Ty<'_>, cast_to: Ty<'_>) { - let literal_kind_name = if cast_from.is_integral() { "integer" } else { "float" }; - span_lint_and_sugg( - cx, - UNNECESSARY_CAST, - expr.span, - &format!("casting {} literal to `{}` is unnecessary", literal_kind_name, cast_to), - "try", - format!("{}_{}", literal_str.trim_end_matches('.'), cast_to), - Applicability::MachineApplicable, - ); -} - -fn lint_numeric_casts<'tcx>( - cx: &LateContext<'tcx>, - expr: &Expr<'tcx>, - cast_expr: &Expr<'_>, - cast_from: Ty<'tcx>, - cast_to: Ty<'tcx>, -) { - match (cast_from.is_integral(), cast_to.is_integral()) { - (true, false) => { - let from_nbits = int_ty_to_nbits(cast_from, cx.tcx); - let to_nbits = if let ty::Float(FloatTy::F32) = cast_to.kind() { - 32 - } else { - 64 - }; - if is_isize_or_usize(cast_from) || from_nbits >= to_nbits { - span_precision_loss_lint(cx, expr, cast_from, to_nbits == 64); - } - if from_nbits < to_nbits { - span_lossless_lint(cx, expr, cast_expr, cast_from, cast_to); - } - }, - (false, true) => { - span_lint( - cx, - CAST_POSSIBLE_TRUNCATION, - expr.span, - &format!("casting `{}` to `{}` may truncate the value", cast_from, cast_to), - ); - if !cast_to.is_signed() { - span_lint( - cx, - CAST_SIGN_LOSS, - expr.span, - &format!( - "casting `{}` to `{}` may lose the sign of the value", - cast_from, cast_to - ), - ); - } - }, - (true, true) => { - check_loss_of_sign(cx, expr, cast_expr, cast_from, cast_to); - check_truncation_and_wrapping(cx, expr, cast_from, cast_to); - check_lossless(cx, expr, cast_expr, cast_from, cast_to); - }, - (false, false) => { - if let (&ty::Float(FloatTy::F64), &ty::Float(FloatTy::F32)) = (&cast_from.kind(), &cast_to.kind()) { - span_lint( - cx, - CAST_POSSIBLE_TRUNCATION, - expr.span, - "casting `f64` to `f32` may truncate the value", - ); - } - if let (&ty::Float(FloatTy::F32), &ty::Float(FloatTy::F64)) = (&cast_from.kind(), &cast_to.kind()) { - span_lossless_lint(cx, expr, cast_expr, cast_from, cast_to); - } - }, - } -} - -fn lint_cast_ptr_alignment<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>, cast_from: Ty<'tcx>, cast_to: Ty<'tcx>) { - if_chain! { - if let ty::RawPtr(from_ptr_ty) = &cast_from.kind(); - if let ty::RawPtr(to_ptr_ty) = &cast_to.kind(); - if let Ok(from_layout) = cx.layout_of(from_ptr_ty.ty); - if let Ok(to_layout) = cx.layout_of(to_ptr_ty.ty); - if from_layout.align.abi < to_layout.align.abi; - // with c_void, we inherently need to trust the user - if !is_c_void(cx, from_ptr_ty.ty); - // when casting from a ZST, we don't know enough to properly lint - if !from_layout.is_zst(); - then { - span_lint( - cx, - CAST_PTR_ALIGNMENT, - expr.span, - &format!( - "casting from `{}` to a more-strictly-aligned pointer (`{}`) ({} < {} bytes)", - cast_from, - cast_to, - from_layout.align.abi.bytes(), - to_layout.align.abi.bytes(), - ), - ); - } - } -} - -fn lint_fn_to_numeric_cast( - cx: &LateContext<'_>, - expr: &Expr<'_>, - cast_expr: &Expr<'_>, - cast_from: Ty<'_>, - cast_to: Ty<'_>, -) { - // We only want to check casts to `ty::Uint` or `ty::Int` - match cast_to.kind() { - ty::Uint(_) | ty::Int(..) => { /* continue on */ }, - _ => return, - } - match cast_from.kind() { - ty::FnDef(..) | ty::FnPtr(_) => { - let mut applicability = Applicability::MaybeIncorrect; - let from_snippet = snippet_with_applicability(cx, cast_expr.span, "x", &mut applicability); - - let to_nbits = int_ty_to_nbits(cast_to, cx.tcx); - if to_nbits < cx.tcx.data_layout.pointer_size.bits() { - span_lint_and_sugg( - cx, - FN_TO_NUMERIC_CAST_WITH_TRUNCATION, - expr.span, - &format!( - "casting function pointer `{}` to `{}`, which truncates the value", - from_snippet, cast_to - ), - "try", - format!("{} as usize", from_snippet), - applicability, - ); - } else if *cast_to.kind() != ty::Uint(UintTy::Usize) { - span_lint_and_sugg( - cx, - FN_TO_NUMERIC_CAST, - expr.span, - &format!("casting function pointer `{}` to `{}`", from_snippet, cast_to), - "try", - format!("{} as usize", from_snippet), - applicability, - ); - } - }, - _ => {}, - } -} - -declare_clippy_lint! { - /// **What it does:** Checks for types used in structs, parameters and `let` - /// declarations above a certain complexity threshold. - /// - /// **Why is this bad?** Too complex types make the code less readable. Consider - /// using a `type` definition to simplify them. - /// - /// **Known problems:** None. - /// - /// **Example:** - /// ```rust - /// # use std::rc::Rc; - /// struct Foo { - /// inner: Rc<Vec<Vec<Box<(u32, u32, u32, u32)>>>>, - /// } - /// ``` - pub TYPE_COMPLEXITY, - complexity, - "usage of very complex types that might be better factored into `type` definitions" -} - -pub struct TypeComplexity { - threshold: u64, -} - -impl TypeComplexity { - #[must_use] - pub fn new(threshold: u64) -> Self { - Self { threshold } - } -} - -impl_lint_pass!(TypeComplexity => [TYPE_COMPLEXITY]); - -impl<'tcx> LateLintPass<'tcx> for TypeComplexity { - fn check_fn( - &mut self, - cx: &LateContext<'tcx>, - _: FnKind<'tcx>, - decl: &'tcx FnDecl<'_>, - _: &'tcx Body<'_>, - _: Span, - _: HirId, - ) { - self.check_fndecl(cx, decl); - } - - fn check_struct_field(&mut self, cx: &LateContext<'tcx>, field: &'tcx hir::StructField<'_>) { - // enum variants are also struct fields now - self.check_type(cx, &field.ty); - } - - fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) { - match item.kind { - ItemKind::Static(ref ty, _, _) | ItemKind::Const(ref ty, _) => self.check_type(cx, ty), - // functions, enums, structs, impls and traits are covered - _ => (), - } - } - - fn check_trait_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx TraitItem<'_>) { - match item.kind { - TraitItemKind::Const(ref ty, _) | TraitItemKind::Type(_, Some(ref ty)) => self.check_type(cx, ty), - TraitItemKind::Fn(FnSig { ref decl, .. }, TraitFn::Required(_)) => self.check_fndecl(cx, decl), - // methods with default impl are covered by check_fn - _ => (), - } - } - - fn check_impl_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx ImplItem<'_>) { - match item.kind { - ImplItemKind::Const(ref ty, _) | ImplItemKind::TyAlias(ref ty) => self.check_type(cx, ty), - // methods are covered by check_fn - _ => (), - } - } - - fn check_local(&mut self, cx: &LateContext<'tcx>, local: &'tcx Local<'_>) { - if let Some(ref ty) = local.ty { - self.check_type(cx, ty); - } - } -} - -impl<'tcx> TypeComplexity { - fn check_fndecl(&self, cx: &LateContext<'tcx>, decl: &'tcx FnDecl<'_>) { - for arg in decl.inputs { - self.check_type(cx, arg); - } - if let FnRetTy::Return(ref ty) = decl.output { - self.check_type(cx, ty); - } - } - - fn check_type(&self, cx: &LateContext<'_>, ty: &hir::Ty<'_>) { - if ty.span.from_expansion() { - return; - } - let score = { - let mut visitor = TypeComplexityVisitor { score: 0, nest: 1 }; - visitor.visit_ty(ty); - visitor.score - }; - - if score > self.threshold { - span_lint( - cx, - TYPE_COMPLEXITY, - ty.span, - "very complex type used. Consider factoring parts into `type` definitions", - ); - } - } -} - -/// Walks a type and assigns a complexity score to it. -struct TypeComplexityVisitor { - /// total complexity score of the type - score: u64, - /// current nesting level - nest: u64, -} - -impl<'tcx> Visitor<'tcx> for TypeComplexityVisitor { - type Map = Map<'tcx>; - - fn visit_ty(&mut self, ty: &'tcx hir::Ty<'_>) { - let (add_score, sub_nest) = match ty.kind { - // _, &x and *x have only small overhead; don't mess with nesting level - TyKind::Infer | TyKind::Ptr(..) | TyKind::Rptr(..) => (1, 0), - - // the "normal" components of a type: named types, arrays/tuples - TyKind::Path(..) | TyKind::Slice(..) | TyKind::Tup(..) | TyKind::Array(..) => (10 * self.nest, 1), - - // function types bring a lot of overhead - TyKind::BareFn(ref bare) if bare.abi == Abi::Rust => (50 * self.nest, 1), - - TyKind::TraitObject(ref param_bounds, _) => { - let has_lifetime_parameters = param_bounds.iter().any(|bound| { - bound - .bound_generic_params - .iter() - .any(|gen| matches!(gen.kind, GenericParamKind::Lifetime { .. })) - }); - if has_lifetime_parameters { - // complex trait bounds like A<'a, 'b> - (50 * self.nest, 1) - } else { - // simple trait bounds like A + B - (20 * self.nest, 0) - } - }, - - _ => (0, 0), - }; - self.score += add_score; - self.nest += sub_nest; - walk_ty(self, ty); - self.nest -= sub_nest; - } - fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> { - NestedVisitorMap::None - } -} - -declare_clippy_lint! { - /// **What it does:** Checks for expressions where a character literal is cast - /// to `u8` and suggests using a byte literal instead. - /// - /// **Why is this bad?** In general, casting values to smaller types is - /// error-prone and should be avoided where possible. In the particular case of - /// converting a character literal to u8, it is easy to avoid by just using a - /// byte literal instead. As an added bonus, `b'a'` is even slightly shorter - /// than `'a' as u8`. - /// - /// **Known problems:** None. - /// - /// **Example:** - /// ```rust,ignore - /// 'x' as u8 - /// ``` - /// - /// A better version, using the byte literal: - /// - /// ```rust,ignore - /// b'x' - /// ``` - pub CHAR_LIT_AS_U8, - complexity, - "casting a character literal to `u8` truncates" -} - -declare_lint_pass!(CharLitAsU8 => [CHAR_LIT_AS_U8]); - -impl<'tcx> LateLintPass<'tcx> for CharLitAsU8 { - fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - if_chain! { - if !expr.span.from_expansion(); - if let ExprKind::Cast(e, _) = &expr.kind; - if let ExprKind::Lit(l) = &e.kind; - if let LitKind::Char(c) = l.node; - if ty::Uint(UintTy::U8) == *cx.typeck_results().expr_ty(expr).kind(); - then { - let mut applicability = Applicability::MachineApplicable; - let snippet = snippet_with_applicability(cx, e.span, "'x'", &mut applicability); - - span_lint_and_then( - cx, - CHAR_LIT_AS_U8, - expr.span, - "casting a character literal to `u8` truncates", - |diag| { - diag.note("`char` is four bytes wide, but `u8` is a single byte"); - - if c.is_ascii() { - diag.span_suggestion( - expr.span, - "use a byte literal instead", - format!("b{}", snippet), - applicability, - ); - } - }); - } - } - } -} - -declare_clippy_lint! { - /// **What it does:** Checks for comparisons where one side of the relation is - /// either the minimum or maximum value for its type and warns if it involves a - /// case that is always true or always false. Only integer and boolean types are - /// checked. - /// - /// **Why is this bad?** An expression like `min <= x` may misleadingly imply - /// that it is possible for `x` to be less than the minimum. Expressions like - /// `max < x` are probably mistakes. - /// - /// **Known problems:** For `usize` the size of the current compile target will - /// be assumed (e.g., 64 bits on 64 bit systems). This means code that uses such - /// a comparison to detect target pointer width will trigger this lint. One can - /// use `mem::sizeof` and compare its value or conditional compilation - /// attributes - /// like `#[cfg(target_pointer_width = "64")] ..` instead. - /// - /// **Example:** - /// - /// ```rust - /// let vec: Vec<isize> = Vec::new(); - /// if vec.len() <= 0 {} - /// if 100 > i32::MAX {} - /// ``` - pub ABSURD_EXTREME_COMPARISONS, - correctness, - "a comparison with a maximum or minimum value that is always true or false" -} - -declare_lint_pass!(AbsurdExtremeComparisons => [ABSURD_EXTREME_COMPARISONS]); - -enum ExtremeType { - Minimum, - Maximum, -} - -struct ExtremeExpr<'a> { - which: ExtremeType, - expr: &'a Expr<'a>, -} - -enum AbsurdComparisonResult { - AlwaysFalse, - AlwaysTrue, - InequalityImpossible, -} - -fn is_cast_between_fixed_and_target<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> bool { - if let ExprKind::Cast(ref cast_exp, _) = expr.kind { - let precast_ty = cx.typeck_results().expr_ty(cast_exp); - let cast_ty = cx.typeck_results().expr_ty(expr); - - return is_isize_or_usize(precast_ty) != is_isize_or_usize(cast_ty); - } - - false -} - -fn detect_absurd_comparison<'tcx>( - cx: &LateContext<'tcx>, - op: BinOpKind, - lhs: &'tcx Expr<'_>, - rhs: &'tcx Expr<'_>, -) -> Option<(ExtremeExpr<'tcx>, AbsurdComparisonResult)> { - use crate::types::AbsurdComparisonResult::{AlwaysFalse, AlwaysTrue, InequalityImpossible}; - use crate::types::ExtremeType::{Maximum, Minimum}; - use crate::utils::comparisons::{normalize_comparison, Rel}; - - // absurd comparison only makes sense on primitive types - // primitive types don't implement comparison operators with each other - if cx.typeck_results().expr_ty(lhs) != cx.typeck_results().expr_ty(rhs) { - return None; - } - - // comparisons between fix sized types and target sized types are considered unanalyzable - if is_cast_between_fixed_and_target(cx, lhs) || is_cast_between_fixed_and_target(cx, rhs) { - return None; - } - - let (rel, normalized_lhs, normalized_rhs) = normalize_comparison(op, lhs, rhs)?; - - let lx = detect_extreme_expr(cx, normalized_lhs); - let rx = detect_extreme_expr(cx, normalized_rhs); - - Some(match rel { - Rel::Lt => { - match (lx, rx) { - (Some(l @ ExtremeExpr { which: Maximum, .. }), _) => (l, AlwaysFalse), // max < x - (_, Some(r @ ExtremeExpr { which: Minimum, .. })) => (r, AlwaysFalse), // x < min - _ => return None, - } - }, - Rel::Le => { - match (lx, rx) { - (Some(l @ ExtremeExpr { which: Minimum, .. }), _) => (l, AlwaysTrue), // min <= x - (Some(l @ ExtremeExpr { which: Maximum, .. }), _) => (l, InequalityImpossible), // max <= x - (_, Some(r @ ExtremeExpr { which: Minimum, .. })) => (r, InequalityImpossible), // x <= min - (_, Some(r @ ExtremeExpr { which: Maximum, .. })) => (r, AlwaysTrue), // x <= max - _ => return None, - } - }, - Rel::Ne | Rel::Eq => return None, - }) -} - -fn detect_extreme_expr<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option<ExtremeExpr<'tcx>> { - use crate::types::ExtremeType::{Maximum, Minimum}; - - let ty = cx.typeck_results().expr_ty(expr); - - let cv = constant(cx, cx.typeck_results(), expr)?.0; - - let which = match (ty.kind(), cv) { - (&ty::Bool, Constant::Bool(false)) | (&ty::Uint(_), Constant::Int(0)) => Minimum, - (&ty::Int(ity), Constant::Int(i)) if i == unsext(cx.tcx, i128::MIN >> (128 - int_bits(cx.tcx, ity)), ity) => { - Minimum - }, - - (&ty::Bool, Constant::Bool(true)) => Maximum, - (&ty::Int(ity), Constant::Int(i)) if i == unsext(cx.tcx, i128::MAX >> (128 - int_bits(cx.tcx, ity)), ity) => { - Maximum - }, - (&ty::Uint(uty), Constant::Int(i)) if clip(cx.tcx, u128::MAX, uty) == i => Maximum, - - _ => return None, - }; - Some(ExtremeExpr { which, expr }) -} - -impl<'tcx> LateLintPass<'tcx> for AbsurdExtremeComparisons { - fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - use crate::types::AbsurdComparisonResult::{AlwaysFalse, AlwaysTrue, InequalityImpossible}; - use crate::types::ExtremeType::{Maximum, Minimum}; - - if let ExprKind::Binary(ref cmp, ref lhs, ref rhs) = expr.kind { - if let Some((culprit, result)) = detect_absurd_comparison(cx, cmp.node, lhs, rhs) { - if !expr.span.from_expansion() { - let msg = "this comparison involving the minimum or maximum element for this \ - type contains a case that is always true or always false"; - - let conclusion = match result { - AlwaysFalse => "this comparison is always false".to_owned(), - AlwaysTrue => "this comparison is always true".to_owned(), - InequalityImpossible => format!( - "the case where the two sides are not equal never occurs, consider using `{} == {}` \ - instead", - snippet(cx, lhs.span, "lhs"), - snippet(cx, rhs.span, "rhs") - ), - }; - - let help = format!( - "because `{}` is the {} value for this type, {}", - snippet(cx, culprit.expr.span, "x"), - match culprit.which { - Minimum => "minimum", - Maximum => "maximum", - }, - conclusion - ); - - span_lint_and_help(cx, ABSURD_EXTREME_COMPARISONS, expr.span, msg, None, &help); - } - } - } - } -} - -declare_clippy_lint! { - /// **What it does:** Checks for comparisons where the relation is always either - /// true or false, but where one side has been upcast so that the comparison is - /// necessary. Only integer types are checked. - /// - /// **Why is this bad?** An expression like `let x : u8 = ...; (x as u32) > 300` - /// will mistakenly imply that it is possible for `x` to be outside the range of - /// `u8`. - /// - /// **Known problems:** - /// https://github.com/rust-lang/rust-clippy/issues/886 - /// - /// **Example:** - /// ```rust - /// let x: u8 = 1; - /// (x as u32) > 300; - /// ``` - pub INVALID_UPCAST_COMPARISONS, - pedantic, - "a comparison involving an upcast which is always true or false" -} - -declare_lint_pass!(InvalidUpcastComparisons => [INVALID_UPCAST_COMPARISONS]); - -#[derive(Copy, Clone, Debug, Eq)] -enum FullInt { - S(i128), - U(u128), -} - -impl FullInt { - #[allow(clippy::cast_sign_loss)] - #[must_use] - fn cmp_s_u(s: i128, u: u128) -> Ordering { - if s < 0 { - Ordering::Less - } else if u > (i128::MAX as u128) { - Ordering::Greater - } else { - (s as u128).cmp(&u) - } - } -} - -impl PartialEq for FullInt { - #[must_use] - fn eq(&self, other: &Self) -> bool { - self.partial_cmp(other).expect("`partial_cmp` only returns `Some(_)`") == Ordering::Equal - } -} - -impl PartialOrd for FullInt { - #[must_use] - fn partial_cmp(&self, other: &Self) -> Option<Ordering> { - Some(match (self, other) { - (&Self::S(s), &Self::S(o)) => s.cmp(&o), - (&Self::U(s), &Self::U(o)) => s.cmp(&o), - (&Self::S(s), &Self::U(o)) => Self::cmp_s_u(s, o), - (&Self::U(s), &Self::S(o)) => Self::cmp_s_u(o, s).reverse(), - }) - } -} - -impl Ord for FullInt { - #[must_use] - fn cmp(&self, other: &Self) -> Ordering { - self.partial_cmp(other) - .expect("`partial_cmp` for FullInt can never return `None`") - } -} - -fn numeric_cast_precast_bounds<'a>(cx: &LateContext<'_>, expr: &'a Expr<'_>) -> Option<(FullInt, FullInt)> { - if let ExprKind::Cast(ref cast_exp, _) = expr.kind { - let pre_cast_ty = cx.typeck_results().expr_ty(cast_exp); - let cast_ty = cx.typeck_results().expr_ty(expr); - // if it's a cast from i32 to u32 wrapping will invalidate all these checks - if cx.layout_of(pre_cast_ty).ok().map(|l| l.size) == cx.layout_of(cast_ty).ok().map(|l| l.size) { - return None; - } - match pre_cast_ty.kind() { - ty::Int(int_ty) => Some(match int_ty { - IntTy::I8 => (FullInt::S(i128::from(i8::MIN)), FullInt::S(i128::from(i8::MAX))), - IntTy::I16 => (FullInt::S(i128::from(i16::MIN)), FullInt::S(i128::from(i16::MAX))), - IntTy::I32 => (FullInt::S(i128::from(i32::MIN)), FullInt::S(i128::from(i32::MAX))), - IntTy::I64 => (FullInt::S(i128::from(i64::MIN)), FullInt::S(i128::from(i64::MAX))), - IntTy::I128 => (FullInt::S(i128::MIN), FullInt::S(i128::MAX)), - IntTy::Isize => (FullInt::S(isize::MIN as i128), FullInt::S(isize::MAX as i128)), - }), - ty::Uint(uint_ty) => Some(match uint_ty { - UintTy::U8 => (FullInt::U(u128::from(u8::MIN)), FullInt::U(u128::from(u8::MAX))), - UintTy::U16 => (FullInt::U(u128::from(u16::MIN)), FullInt::U(u128::from(u16::MAX))), - UintTy::U32 => (FullInt::U(u128::from(u32::MIN)), FullInt::U(u128::from(u32::MAX))), - UintTy::U64 => (FullInt::U(u128::from(u64::MIN)), FullInt::U(u128::from(u64::MAX))), - UintTy::U128 => (FullInt::U(u128::MIN), FullInt::U(u128::MAX)), - UintTy::Usize => (FullInt::U(usize::MIN as u128), FullInt::U(usize::MAX as u128)), - }), - _ => None, - } - } else { - None - } -} - -fn node_as_const_fullint<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option<FullInt> { - let val = constant(cx, cx.typeck_results(), expr)?.0; - if let Constant::Int(const_int) = val { - match *cx.typeck_results().expr_ty(expr).kind() { - ty::Int(ity) => Some(FullInt::S(sext(cx.tcx, const_int, ity))), - ty::Uint(_) => Some(FullInt::U(const_int)), - _ => None, - } - } else { - None - } -} - -fn err_upcast_comparison(cx: &LateContext<'_>, span: Span, expr: &Expr<'_>, always: bool) { - if let ExprKind::Cast(ref cast_val, _) = expr.kind { - span_lint( - cx, - INVALID_UPCAST_COMPARISONS, - span, - &format!( - "because of the numeric bounds on `{}` prior to casting, this expression is always {}", - snippet(cx, cast_val.span, "the expression"), - if always { "true" } else { "false" }, - ), - ); - } -} - -fn upcast_comparison_bounds_err<'tcx>( - cx: &LateContext<'tcx>, - span: Span, - rel: comparisons::Rel, - lhs_bounds: Option<(FullInt, FullInt)>, - lhs: &'tcx Expr<'_>, - rhs: &'tcx Expr<'_>, - invert: bool, -) { - use crate::utils::comparisons::Rel; - - if let Some((lb, ub)) = lhs_bounds { - if let Some(norm_rhs_val) = node_as_const_fullint(cx, rhs) { - if rel == Rel::Eq || rel == Rel::Ne { - if norm_rhs_val < lb || norm_rhs_val > ub { - err_upcast_comparison(cx, span, lhs, rel == Rel::Ne); - } - } else if match rel { - Rel::Lt => { - if invert { - norm_rhs_val < lb - } else { - ub < norm_rhs_val - } - }, - Rel::Le => { - if invert { - norm_rhs_val <= lb - } else { - ub <= norm_rhs_val - } - }, - Rel::Eq | Rel::Ne => unreachable!(), - } { - err_upcast_comparison(cx, span, lhs, true) - } else if match rel { - Rel::Lt => { - if invert { - norm_rhs_val >= ub - } else { - lb >= norm_rhs_val - } - }, - Rel::Le => { - if invert { - norm_rhs_val > ub - } else { - lb > norm_rhs_val - } - }, - Rel::Eq | Rel::Ne => unreachable!(), - } { - err_upcast_comparison(cx, span, lhs, false) - } - } - } -} - -impl<'tcx> LateLintPass<'tcx> for InvalidUpcastComparisons { - fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - if let ExprKind::Binary(ref cmp, ref lhs, ref rhs) = expr.kind { - let normalized = comparisons::normalize_comparison(cmp.node, lhs, rhs); - let (rel, normalized_lhs, normalized_rhs) = if let Some(val) = normalized { - val - } else { - return; - }; - - let lhs_bounds = numeric_cast_precast_bounds(cx, normalized_lhs); - let rhs_bounds = numeric_cast_precast_bounds(cx, normalized_rhs); - - upcast_comparison_bounds_err(cx, expr.span, rel, lhs_bounds, normalized_lhs, normalized_rhs, false); - upcast_comparison_bounds_err(cx, expr.span, rel, rhs_bounds, normalized_rhs, normalized_lhs, true); - } - } -} - -declare_clippy_lint! { - /// **What it does:** Checks for public `impl` or `fn` missing generalization - /// over different hashers and implicitly defaulting to the default hashing - /// algorithm (`SipHash`). - /// - /// **Why is this bad?** `HashMap` or `HashSet` with custom hashers cannot be - /// used with them. - /// - /// **Known problems:** Suggestions for replacing constructors can contain - /// false-positives. Also applying suggestions can require modification of other - /// pieces of code, possibly including external crates. - /// - /// **Example:** - /// ```rust - /// # use std::collections::HashMap; - /// # use std::hash::{Hash, BuildHasher}; - /// # trait Serialize {}; - /// impl<K: Hash + Eq, V> Serialize for HashMap<K, V> { } - /// - /// pub fn foo(map: &mut HashMap<i32, i32>) { } - /// ``` - /// could be rewritten as - /// ```rust - /// # use std::collections::HashMap; - /// # use std::hash::{Hash, BuildHasher}; - /// # trait Serialize {}; - /// impl<K: Hash + Eq, V, S: BuildHasher> Serialize for HashMap<K, V, S> { } - /// - /// pub fn foo<S: BuildHasher>(map: &mut HashMap<i32, i32, S>) { } - /// ``` - pub IMPLICIT_HASHER, - pedantic, - "missing generalization over different hashers" -} - -declare_lint_pass!(ImplicitHasher => [IMPLICIT_HASHER]); - -impl<'tcx> LateLintPass<'tcx> for ImplicitHasher { - #[allow(clippy::cast_possible_truncation, clippy::too_many_lines)] - fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) { - use rustc_span::BytePos; - - fn suggestion<'tcx>( - cx: &LateContext<'tcx>, - diag: &mut DiagnosticBuilder<'_>, - generics_span: Span, - generics_suggestion_span: Span, - target: &ImplicitHasherType<'_>, - vis: ImplicitHasherConstructorVisitor<'_, '_, '_>, - ) { - let generics_snip = snippet(cx, generics_span, ""); - // trim `<` `>` - let generics_snip = if generics_snip.is_empty() { - "" - } else { - &generics_snip[1..generics_snip.len() - 1] - }; - - multispan_sugg( - diag, - "consider adding a type parameter", - vec![ - ( - generics_suggestion_span, - format!( - "<{}{}S: ::std::hash::BuildHasher{}>", - generics_snip, - if generics_snip.is_empty() { "" } else { ", " }, - if vis.suggestions.is_empty() { - "" - } else { - // request users to add `Default` bound so that generic constructors can be used - " + Default" - }, - ), - ), - ( - target.span(), - format!("{}<{}, S>", target.type_name(), target.type_arguments(),), - ), - ], - ); - - if !vis.suggestions.is_empty() { - multispan_sugg(diag, "...and use generic constructor", vis.suggestions); - } - } - - if !cx.access_levels.is_exported(item.hir_id()) { - return; - } - - match item.kind { - ItemKind::Impl(ref impl_) => { - let mut vis = ImplicitHasherTypeVisitor::new(cx); - vis.visit_ty(impl_.self_ty); - - for target in &vis.found { - if differing_macro_contexts(item.span, target.span()) { - return; - } - - let generics_suggestion_span = impl_.generics.span.substitute_dummy({ - let pos = snippet_opt(cx, item.span.until(target.span())) - .and_then(|snip| Some(item.span.lo() + BytePos(snip.find("impl")? as u32 + 4))); - if let Some(pos) = pos { - Span::new(pos, pos, item.span.data().ctxt) - } else { - return; - } - }); - - let mut ctr_vis = ImplicitHasherConstructorVisitor::new(cx, target); - for item in impl_.items.iter().map(|item| cx.tcx.hir().impl_item(item.id)) { - ctr_vis.visit_impl_item(item); - } - - span_lint_and_then( - cx, - IMPLICIT_HASHER, - target.span(), - &format!( - "impl for `{}` should be generalized over different hashers", - target.type_name() - ), - move |diag| { - suggestion(cx, diag, impl_.generics.span, generics_suggestion_span, target, ctr_vis); - }, - ); - } - }, - ItemKind::Fn(ref sig, ref generics, body_id) => { - let body = cx.tcx.hir().body(body_id); - - for ty in sig.decl.inputs { - let mut vis = ImplicitHasherTypeVisitor::new(cx); - vis.visit_ty(ty); - - for target in &vis.found { - if in_external_macro(cx.sess(), generics.span) { - continue; - } - let generics_suggestion_span = generics.span.substitute_dummy({ - let pos = snippet_opt(cx, item.span.until(body.params[0].pat.span)) - .and_then(|snip| { - let i = snip.find("fn")?; - Some(item.span.lo() + BytePos((i + (&snip[i..]).find('(')?) as u32)) - }) - .expect("failed to create span for type parameters"); - Span::new(pos, pos, item.span.data().ctxt) - }); - - let mut ctr_vis = ImplicitHasherConstructorVisitor::new(cx, target); - ctr_vis.visit_body(body); - - span_lint_and_then( - cx, - IMPLICIT_HASHER, - target.span(), - &format!( - "parameter of type `{}` should be generalized over different hashers", - target.type_name() - ), - move |diag| { - suggestion(cx, diag, generics.span, generics_suggestion_span, target, ctr_vis); - }, - ); - } - } - }, - _ => {}, - } - } -} - -enum ImplicitHasherType<'tcx> { - HashMap(Span, Ty<'tcx>, Cow<'static, str>, Cow<'static, str>), - HashSet(Span, Ty<'tcx>, Cow<'static, str>), -} - -impl<'tcx> ImplicitHasherType<'tcx> { - /// Checks that `ty` is a target type without a `BuildHasher`. - fn new(cx: &LateContext<'tcx>, hir_ty: &hir::Ty<'_>) -> Option<Self> { - if let TyKind::Path(QPath::Resolved(None, ref path)) = hir_ty.kind { - let params: Vec<_> = path - .segments - .last() - .as_ref()? - .args - .as_ref()? - .args - .iter() - .filter_map(|arg| match arg { - GenericArg::Type(ty) => Some(ty), - _ => None, - }) - .collect(); - let params_len = params.len(); - - let ty = hir_ty_to_ty(cx.tcx, hir_ty); - - if is_type_diagnostic_item(cx, ty, sym::hashmap_type) && params_len == 2 { - Some(ImplicitHasherType::HashMap( - hir_ty.span, - ty, - snippet(cx, params[0].span, "K"), - snippet(cx, params[1].span, "V"), - )) - } else if is_type_diagnostic_item(cx, ty, sym::hashset_type) && params_len == 1 { - Some(ImplicitHasherType::HashSet( - hir_ty.span, - ty, - snippet(cx, params[0].span, "T"), - )) - } else { - None - } - } else { - None - } - } - - fn type_name(&self) -> &'static str { - match *self { - ImplicitHasherType::HashMap(..) => "HashMap", - ImplicitHasherType::HashSet(..) => "HashSet", - } - } - - fn type_arguments(&self) -> String { - match *self { - ImplicitHasherType::HashMap(.., ref k, ref v) => format!("{}, {}", k, v), - ImplicitHasherType::HashSet(.., ref t) => format!("{}", t), - } - } - - fn ty(&self) -> Ty<'tcx> { - match *self { - ImplicitHasherType::HashMap(_, ty, ..) | ImplicitHasherType::HashSet(_, ty, ..) => ty, - } - } - - fn span(&self) -> Span { - match *self { - ImplicitHasherType::HashMap(span, ..) | ImplicitHasherType::HashSet(span, ..) => span, - } - } -} - -struct ImplicitHasherTypeVisitor<'a, 'tcx> { - cx: &'a LateContext<'tcx>, - found: Vec<ImplicitHasherType<'tcx>>, -} - -impl<'a, 'tcx> ImplicitHasherTypeVisitor<'a, 'tcx> { - fn new(cx: &'a LateContext<'tcx>) -> Self { - Self { cx, found: vec![] } - } -} - -impl<'a, 'tcx> Visitor<'tcx> for ImplicitHasherTypeVisitor<'a, 'tcx> { - type Map = Map<'tcx>; - - fn visit_ty(&mut self, t: &'tcx hir::Ty<'_>) { - if let Some(target) = ImplicitHasherType::new(self.cx, t) { - self.found.push(target); - } - - walk_ty(self, t); - } - - fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> { - NestedVisitorMap::None - } -} - -/// Looks for default-hasher-dependent constructors like `HashMap::new`. -struct ImplicitHasherConstructorVisitor<'a, 'b, 'tcx> { - cx: &'a LateContext<'tcx>, - maybe_typeck_results: Option<&'tcx TypeckResults<'tcx>>, - target: &'b ImplicitHasherType<'tcx>, - suggestions: BTreeMap<Span, String>, -} - -impl<'a, 'b, 'tcx> ImplicitHasherConstructorVisitor<'a, 'b, 'tcx> { - fn new(cx: &'a LateContext<'tcx>, target: &'b ImplicitHasherType<'tcx>) -> Self { - Self { - cx, - maybe_typeck_results: cx.maybe_typeck_results(), - target, - suggestions: BTreeMap::new(), - } - } -} - -impl<'a, 'b, 'tcx> Visitor<'tcx> for ImplicitHasherConstructorVisitor<'a, 'b, 'tcx> { - type Map = Map<'tcx>; - - fn visit_body(&mut self, body: &'tcx Body<'_>) { - let old_maybe_typeck_results = self.maybe_typeck_results.replace(self.cx.tcx.typeck_body(body.id())); - walk_body(self, body); - self.maybe_typeck_results = old_maybe_typeck_results; - } - - fn visit_expr(&mut self, e: &'tcx Expr<'_>) { - if_chain! { - if let ExprKind::Call(ref fun, ref args) = e.kind; - if let ExprKind::Path(QPath::TypeRelative(ref ty, ref method)) = fun.kind; - if let TyKind::Path(QPath::Resolved(None, ty_path)) = ty.kind; - then { - if !TyS::same_type(self.target.ty(), self.maybe_typeck_results.unwrap().expr_ty(e)) { - return; - } - - if match_path(ty_path, &paths::HASHMAP) { - if method.ident.name == sym::new { - self.suggestions - .insert(e.span, "HashMap::default()".to_string()); - } else if method.ident.name == sym!(with_capacity) { - self.suggestions.insert( - e.span, - format!( - "HashMap::with_capacity_and_hasher({}, Default::default())", - snippet(self.cx, args[0].span, "capacity"), - ), - ); - } - } else if match_path(ty_path, &paths::HASHSET) { - if method.ident.name == sym::new { - self.suggestions - .insert(e.span, "HashSet::default()".to_string()); - } else if method.ident.name == sym!(with_capacity) { - self.suggestions.insert( - e.span, - format!( - "HashSet::with_capacity_and_hasher({}, Default::default())", - snippet(self.cx, args[0].span, "capacity"), - ), - ); - } - } - } - } - - walk_expr(self, e); - } - - fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> { - NestedVisitorMap::OnlyBodies(self.cx.tcx.hir()) - } -} - -declare_clippy_lint! { - /// **What it does:** Checks for casts of `&T` to `&mut T` anywhere in the code. - /// - /// **Why is this bad?** It’s basically guaranteed to be undefined behaviour. - /// `UnsafeCell` is the only way to obtain aliasable data that is considered - /// mutable. - /// - /// **Known problems:** None. - /// - /// **Example:** - /// ```rust,ignore - /// fn x(r: &i32) { - /// unsafe { - /// *(r as *const _ as *mut _) += 1; - /// } - /// } - /// ``` - /// - /// Instead consider using interior mutability types. - /// - /// ```rust - /// use std::cell::UnsafeCell; - /// - /// fn x(r: &UnsafeCell<i32>) { - /// unsafe { - /// *r.get() += 1; - /// } - /// } - /// ``` - pub CAST_REF_TO_MUT, - correctness, - "a cast of reference to a mutable pointer" -} - -declare_lint_pass!(RefToMut => [CAST_REF_TO_MUT]); - -impl<'tcx> LateLintPass<'tcx> for RefToMut { - fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - if_chain! { - if let ExprKind::Unary(UnOp::Deref, e) = &expr.kind; - if let ExprKind::Cast(e, t) = &e.kind; - if let TyKind::Ptr(MutTy { mutbl: Mutability::Mut, .. }) = t.kind; - if let ExprKind::Cast(e, t) = &e.kind; - if let TyKind::Ptr(MutTy { mutbl: Mutability::Not, .. }) = t.kind; - if let ty::Ref(..) = cx.typeck_results().node_type(e.hir_id).kind(); - then { - span_lint( - cx, - CAST_REF_TO_MUT, - expr.span, - "casting `&T` to `&mut T` may cause undefined behavior, consider instead using an `UnsafeCell`", - ); - } - } - } -} - -const PTR_AS_PTR_MSRV: RustcVersion = RustcVersion::new(1, 38, 0); - -declare_clippy_lint! { - /// **What it does:** - /// Checks for `as` casts between raw pointers without changing its mutability, - /// namely `*const T` to `*const U` and `*mut T` to `*mut U`. - /// - /// **Why is this bad?** - /// Though `as` casts between raw pointers is not terrible, `pointer::cast` is safer because - /// it cannot accidentally change the pointer's mutability nor cast the pointer to other types like `usize`. - /// - /// **Known problems:** None. - /// - /// **Example:** - /// - /// ```rust - /// let ptr: *const u32 = &42_u32; - /// let mut_ptr: *mut u32 = &mut 42_u32; - /// let _ = ptr as *const i32; - /// let _ = mut_ptr as *mut i32; - /// ``` - /// Use instead: - /// ```rust - /// let ptr: *const u32 = &42_u32; - /// let mut_ptr: *mut u32 = &mut 42_u32; - /// let _ = ptr.cast::<i32>(); - /// let _ = mut_ptr.cast::<i32>(); - /// ``` - pub PTR_AS_PTR, - pedantic, - "casting using `as` from and to raw pointers that doesn't change its mutability, where `pointer::cast` could take the place of `as`" -} - -pub struct PtrAsPtr { - msrv: Option<RustcVersion>, -} - -impl PtrAsPtr { - #[must_use] - pub fn new(msrv: Option<RustcVersion>) -> Self { - Self { msrv } - } -} - -impl_lint_pass!(PtrAsPtr => [PTR_AS_PTR]); - -impl<'tcx> LateLintPass<'tcx> for PtrAsPtr { - fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - if !meets_msrv(self.msrv.as_ref(), &PTR_AS_PTR_MSRV) { - return; - } - - if expr.span.from_expansion() { - return; - } - - if_chain! { - if let ExprKind::Cast(cast_expr, cast_to_hir_ty) = expr.kind; - let (cast_from, cast_to) = (cx.typeck_results().expr_ty(cast_expr), cx.typeck_results().expr_ty(expr)); - if let ty::RawPtr(TypeAndMut { mutbl: from_mutbl, .. }) = cast_from.kind(); - if let ty::RawPtr(TypeAndMut { ty: to_pointee_ty, mutbl: to_mutbl }) = cast_to.kind(); - if matches!((from_mutbl, to_mutbl), - (Mutability::Not, Mutability::Not) | (Mutability::Mut, Mutability::Mut)); - // The `U` in `pointer::cast` have to be `Sized` - // as explained here: https://github.com/rust-lang/rust/issues/60602. - if to_pointee_ty.is_sized(cx.tcx.at(expr.span), cx.param_env); - then { - let mut applicability = Applicability::MachineApplicable; - let cast_expr_sugg = Sugg::hir_with_applicability(cx, cast_expr, "_", &mut applicability); - let turbofish = match &cast_to_hir_ty.kind { - TyKind::Infer => Cow::Borrowed(""), - TyKind::Ptr(mut_ty) if matches!(mut_ty.ty.kind, TyKind::Infer) => Cow::Borrowed(""), - _ => Cow::Owned(format!("::<{}>", to_pointee_ty)), - }; - span_lint_and_sugg( - cx, - PTR_AS_PTR, - expr.span, - "`as` casting between raw pointers without changing its mutability", - "try `pointer::cast`, a safer alternative", - format!("{}.cast{}()", cast_expr_sugg.maybe_par(), turbofish), - applicability, - ); - } - } - } - - extract_msrv_attr!(LateContext); -} diff --git a/src/tools/clippy/clippy_lints/src/types/borrowed_box.rs b/src/tools/clippy/clippy_lints/src/types/borrowed_box.rs new file mode 100644 index 0000000000000..1425d8f3f37ed --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/types/borrowed_box.rs @@ -0,0 +1,114 @@ +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::source::snippet; +use clippy_utils::{match_path, paths}; +use if_chain::if_chain; +use rustc_errors::Applicability; +use rustc_hir::{ + self as hir, GenericArg, GenericBounds, GenericParamKind, HirId, Lifetime, MutTy, Mutability, Node, QPath, + SyntheticTyParamKind, TyKind, +}; +use rustc_lint::LateContext; + +use super::BORROWED_BOX; + +pub(super) fn check(cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, lt: &Lifetime, mut_ty: &MutTy<'_>) -> bool { + match mut_ty.ty.kind { + TyKind::Path(ref qpath) => { + let hir_id = mut_ty.ty.hir_id; + let def = cx.qpath_res(qpath, hir_id); + if_chain! { + if let Some(def_id) = def.opt_def_id(); + if Some(def_id) == cx.tcx.lang_items().owned_box(); + if let QPath::Resolved(None, path) = *qpath; + if let [ref bx] = *path.segments; + if let Some(params) = bx.args; + if !params.parenthesized; + if let Some(inner) = params.args.iter().find_map(|arg| match arg { + GenericArg::Type(ty) => Some(ty), + _ => None, + }); + then { + if is_any_trait(inner) { + // Ignore `Box<Any>` types; see issue #1884 for details. + return false; + } + + let ltopt = if lt.is_elided() { + String::new() + } else { + format!("{} ", lt.name.ident().as_str()) + }; + + if mut_ty.mutbl == Mutability::Mut { + // Ignore `&mut Box<T>` types; see issue #2907 for + // details. + return false; + } + + // When trait objects or opaque types have lifetime or auto-trait bounds, + // we need to add parentheses to avoid a syntax error due to its ambiguity. + // Originally reported as the issue #3128. + let inner_snippet = snippet(cx, inner.span, ".."); + let suggestion = match &inner.kind { + TyKind::TraitObject(bounds, lt_bound, _) if bounds.len() > 1 || !lt_bound.is_elided() => { + format!("&{}({})", ltopt, &inner_snippet) + }, + TyKind::Path(qpath) + if get_bounds_if_impl_trait(cx, qpath, inner.hir_id) + .map_or(false, |bounds| bounds.len() > 1) => + { + format!("&{}({})", ltopt, &inner_snippet) + }, + _ => format!("&{}{}", ltopt, &inner_snippet), + }; + span_lint_and_sugg( + cx, + BORROWED_BOX, + hir_ty.span, + "you seem to be trying to use `&Box<T>`. Consider using just `&T`", + "try", + suggestion, + // To make this `MachineApplicable`, at least one needs to check if it isn't a trait item + // because the trait impls of it will break otherwise; + // and there may be other cases that result in invalid code. + // For example, type coercion doesn't work nicely. + Applicability::Unspecified, + ); + return true; + } + }; + false + }, + _ => false, + } +} + +// Returns true if given type is `Any` trait. +fn is_any_trait(t: &hir::Ty<'_>) -> bool { + if_chain! { + if let TyKind::TraitObject(traits, ..) = t.kind; + if !traits.is_empty(); + // Only Send/Sync can be used as additional traits, so it is enough to + // check only the first trait. + if match_path(traits[0].trait_ref.path, &paths::ANY_TRAIT); + then { + return true; + } + } + + false +} + +fn get_bounds_if_impl_trait<'tcx>(cx: &LateContext<'tcx>, qpath: &QPath<'_>, id: HirId) -> Option<GenericBounds<'tcx>> { + if_chain! { + if let Some(did) = cx.qpath_res(qpath, id).opt_def_id(); + if let Some(Node::GenericParam(generic_param)) = cx.tcx.hir().get_if_local(did); + if let GenericParamKind::Type { synthetic, .. } = generic_param.kind; + if synthetic == Some(SyntheticTyParamKind::ImplTrait); + then { + Some(generic_param.bounds) + } else { + None + } + } +} diff --git a/src/tools/clippy/clippy_lints/src/types/box_vec.rs b/src/tools/clippy/clippy_lints/src/types/box_vec.rs new file mode 100644 index 0000000000000..d8b1953457ccc --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/types/box_vec.rs @@ -0,0 +1,25 @@ +use clippy_utils::diagnostics::span_lint_and_help; +use clippy_utils::is_ty_param_diagnostic_item; +use rustc_hir::{self as hir, def_id::DefId, QPath}; +use rustc_lint::LateContext; +use rustc_span::symbol::sym; + +use super::BOX_VEC; + +pub(super) fn check(cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, qpath: &QPath<'_>, def_id: DefId) -> bool { + if Some(def_id) == cx.tcx.lang_items().owned_box() + && is_ty_param_diagnostic_item(cx, qpath, sym::vec_type).is_some() + { + span_lint_and_help( + cx, + BOX_VEC, + hir_ty.span, + "you seem to be trying to use `Box<Vec<T>>`. Consider using just `Vec<T>`", + None, + "`Vec<T>` is already on the heap, `Box<Vec<T>>` makes an extra allocation", + ); + true + } else { + false + } +} diff --git a/src/tools/clippy/clippy_lints/src/types/linked_list.rs b/src/tools/clippy/clippy_lints/src/types/linked_list.rs new file mode 100644 index 0000000000000..a9fbe7aa315c8 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/types/linked_list.rs @@ -0,0 +1,22 @@ +use clippy_utils::diagnostics::span_lint_and_help; +use clippy_utils::{match_def_path, paths}; +use rustc_hir::{self as hir, def_id::DefId}; +use rustc_lint::LateContext; + +use super::LINKEDLIST; + +pub(super) fn check(cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, def_id: DefId) -> bool { + if match_def_path(cx, def_id, &paths::LINKED_LIST) { + span_lint_and_help( + cx, + LINKEDLIST, + hir_ty.span, + "you seem to be using a `LinkedList`! Perhaps you meant some other data structure?", + None, + "a `VecDeque` might work", + ); + true + } else { + false + } +} diff --git a/src/tools/clippy/clippy_lints/src/types/mod.rs b/src/tools/clippy/clippy_lints/src/types/mod.rs new file mode 100644 index 0000000000000..d9b47a699dc3c --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/types/mod.rs @@ -0,0 +1,454 @@ +mod borrowed_box; +mod box_vec; +mod linked_list; +mod option_option; +mod rc_buffer; +mod redundant_allocation; +mod type_complexity; +mod utils; +mod vec_box; + +use rustc_hir as hir; +use rustc_hir::intravisit::FnKind; +use rustc_hir::{ + Body, FnDecl, FnRetTy, GenericArg, HirId, ImplItem, ImplItemKind, Item, ItemKind, Local, MutTy, QPath, TraitItem, + TraitItemKind, TyKind, +}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_session::{declare_tool_lint, impl_lint_pass}; +use rustc_span::source_map::Span; + +declare_clippy_lint! { + /// **What it does:** Checks for use of `Box<Vec<_>>` anywhere in the code. + /// Check the [Box documentation](https://doc.rust-lang.org/std/boxed/index.html) for more information. + /// + /// **Why is this bad?** `Vec` already keeps its contents in a separate area on + /// the heap. So if you `Box` it, you just add another level of indirection + /// without any benefit whatsoever. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// ```rust,ignore + /// struct X { + /// values: Box<Vec<Foo>>, + /// } + /// ``` + /// + /// Better: + /// + /// ```rust,ignore + /// struct X { + /// values: Vec<Foo>, + /// } + /// ``` + pub BOX_VEC, + perf, + "usage of `Box<Vec<T>>`, vector elements are already on the heap" +} + +declare_clippy_lint! { + /// **What it does:** Checks for use of `Vec<Box<T>>` where T: Sized anywhere in the code. + /// Check the [Box documentation](https://doc.rust-lang.org/std/boxed/index.html) for more information. + /// + /// **Why is this bad?** `Vec` already keeps its contents in a separate area on + /// the heap. So if you `Box` its contents, you just add another level of indirection. + /// + /// **Known problems:** Vec<Box<T: Sized>> makes sense if T is a large type (see [#3530](https://github.com/rust-lang/rust-clippy/issues/3530), + /// 1st comment). + /// + /// **Example:** + /// ```rust + /// struct X { + /// values: Vec<Box<i32>>, + /// } + /// ``` + /// + /// Better: + /// + /// ```rust + /// struct X { + /// values: Vec<i32>, + /// } + /// ``` + pub VEC_BOX, + complexity, + "usage of `Vec<Box<T>>` where T: Sized, vector elements are already on the heap" +} + +declare_clippy_lint! { + /// **What it does:** Checks for use of `Option<Option<_>>` in function signatures and type + /// definitions + /// + /// **Why is this bad?** `Option<_>` represents an optional value. `Option<Option<_>>` + /// represents an optional optional value which is logically the same thing as an optional + /// value but has an unneeded extra level of wrapping. + /// + /// If you have a case where `Some(Some(_))`, `Some(None)` and `None` are distinct cases, + /// consider a custom `enum` instead, with clear names for each case. + /// + /// **Known problems:** None. + /// + /// **Example** + /// ```rust + /// fn get_data() -> Option<Option<u32>> { + /// None + /// } + /// ``` + /// + /// Better: + /// + /// ```rust + /// pub enum Contents { + /// Data(Vec<u8>), // Was Some(Some(Vec<u8>)) + /// NotYetFetched, // Was Some(None) + /// None, // Was None + /// } + /// + /// fn get_data() -> Contents { + /// Contents::None + /// } + /// ``` + pub OPTION_OPTION, + pedantic, + "usage of `Option<Option<T>>`" +} + +declare_clippy_lint! { + /// **What it does:** Checks for usage of any `LinkedList`, suggesting to use a + /// `Vec` or a `VecDeque` (formerly called `RingBuf`). + /// + /// **Why is this bad?** Gankro says: + /// + /// > The TL;DR of `LinkedList` is that it's built on a massive amount of + /// pointers and indirection. + /// > It wastes memory, it has terrible cache locality, and is all-around slow. + /// `RingBuf`, while + /// > "only" amortized for push/pop, should be faster in the general case for + /// almost every possible + /// > workload, and isn't even amortized at all if you can predict the capacity + /// you need. + /// > + /// > `LinkedList`s are only really good if you're doing a lot of merging or + /// splitting of lists. + /// > This is because they can just mangle some pointers instead of actually + /// copying the data. Even + /// > if you're doing a lot of insertion in the middle of the list, `RingBuf` + /// can still be better + /// > because of how expensive it is to seek to the middle of a `LinkedList`. + /// + /// **Known problems:** False positives – the instances where using a + /// `LinkedList` makes sense are few and far between, but they can still happen. + /// + /// **Example:** + /// ```rust + /// # use std::collections::LinkedList; + /// let x: LinkedList<usize> = LinkedList::new(); + /// ``` + pub LINKEDLIST, + pedantic, + "usage of LinkedList, usually a vector is faster, or a more specialized data structure like a `VecDeque`" +} + +declare_clippy_lint! { + /// **What it does:** Checks for use of `&Box<T>` anywhere in the code. + /// 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. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// ```rust,ignore + /// fn foo(bar: &Box<T>) { ... } + /// ``` + /// + /// Better: + /// + /// ```rust,ignore + /// fn foo(bar: &T) { ... } + /// ``` + pub BORROWED_BOX, + complexity, + "a borrow of a boxed type" +} + +declare_clippy_lint! { + /// **What it does:** Checks for use of redundant allocations anywhere in the code. + /// + /// **Why is this bad?** Expressions such as `Rc<&T>`, `Rc<Rc<T>>`, `Rc<Box<T>>`, `Box<&T>` + /// add an unnecessary level of indirection. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// ```rust + /// # use std::rc::Rc; + /// fn foo(bar: Rc<&usize>) {} + /// ``` + /// + /// Better: + /// + /// ```rust + /// fn foo(bar: &usize) {} + /// ``` + pub REDUNDANT_ALLOCATION, + perf, + "redundant allocation" +} + +declare_clippy_lint! { + /// **What it does:** Checks for `Rc<T>` and `Arc<T>` when `T` is a mutable buffer type such as `String` or `Vec`. + /// + /// **Why is this bad?** Expressions such as `Rc<String>` usually have no advantage over `Rc<str>`, since + /// it is larger and involves an extra level of indirection, and doesn't implement `Borrow<str>`. + /// + /// While mutating a buffer type would still be possible with `Rc::get_mut()`, it only + /// works if there are no additional references yet, which usually defeats the purpose of + /// enclosing it in a shared ownership type. Instead, additionally wrapping the inner + /// type with an interior mutable container (such as `RefCell` or `Mutex`) would normally + /// be used. + /// + /// **Known problems:** This pattern can be desirable to avoid the overhead of a `RefCell` or `Mutex` for + /// cases where mutation only happens before there are any additional references. + /// + /// **Example:** + /// ```rust,ignore + /// # use std::rc::Rc; + /// fn foo(interned: Rc<String>) { ... } + /// ``` + /// + /// Better: + /// + /// ```rust,ignore + /// fn foo(interned: Rc<str>) { ... } + /// ``` + pub RC_BUFFER, + restriction, + "shared ownership of a buffer type" +} + +declare_clippy_lint! { + /// **What it does:** Checks for types used in structs, parameters and `let` + /// declarations above a certain complexity threshold. + /// + /// **Why is this bad?** Too complex types make the code less readable. Consider + /// using a `type` definition to simplify them. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// ```rust + /// # use std::rc::Rc; + /// struct Foo { + /// inner: Rc<Vec<Vec<Box<(u32, u32, u32, u32)>>>>, + /// } + /// ``` + pub TYPE_COMPLEXITY, + complexity, + "usage of very complex types that might be better factored into `type` definitions" +} + +pub struct Types { + vec_box_size_threshold: u64, + type_complexity_threshold: u64, +} + +impl_lint_pass!(Types => [BOX_VEC, VEC_BOX, OPTION_OPTION, LINKEDLIST, BORROWED_BOX, REDUNDANT_ALLOCATION, RC_BUFFER, TYPE_COMPLEXITY]); + +impl<'tcx> LateLintPass<'tcx> for Types { + fn check_fn(&mut self, cx: &LateContext<'_>, _: FnKind<'_>, decl: &FnDecl<'_>, _: &Body<'_>, _: Span, id: HirId) { + let is_in_trait_impl = if let Some(hir::Node::Item(item)) = cx.tcx.hir().find(cx.tcx.hir().get_parent_item(id)) + { + matches!(item.kind, ItemKind::Impl(hir::Impl { of_trait: Some(_), .. })) + } else { + false + }; + + self.check_fn_decl( + cx, + decl, + CheckTyContext { + is_in_trait_impl, + ..CheckTyContext::default() + }, + ); + } + + fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) { + match item.kind { + ItemKind::Static(ty, _, _) | ItemKind::Const(ty, _) => self.check_ty(cx, ty, CheckTyContext::default()), + // functions, enums, structs, impls and traits are covered + _ => (), + } + } + + fn check_impl_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx ImplItem<'_>) { + match item.kind { + ImplItemKind::Const(ty, _) | ImplItemKind::TyAlias(ty) => self.check_ty( + cx, + ty, + CheckTyContext { + is_in_trait_impl: true, + ..CheckTyContext::default() + }, + ), + // methods are covered by check_fn + ImplItemKind::Fn(..) => (), + } + } + + fn check_field_def(&mut self, cx: &LateContext<'_>, field: &hir::FieldDef<'_>) { + self.check_ty(cx, field.ty, CheckTyContext::default()); + } + + fn check_trait_item(&mut self, cx: &LateContext<'_>, item: &TraitItem<'_>) { + match item.kind { + TraitItemKind::Const(ty, _) | TraitItemKind::Type(_, Some(ty)) => { + self.check_ty(cx, ty, CheckTyContext::default()) + }, + TraitItemKind::Fn(ref sig, _) => self.check_fn_decl(cx, sig.decl, CheckTyContext::default()), + TraitItemKind::Type(..) => (), + } + } + + fn check_local(&mut self, cx: &LateContext<'_>, local: &Local<'_>) { + if let Some(ty) = local.ty { + self.check_ty( + cx, + ty, + CheckTyContext { + is_local: true, + ..CheckTyContext::default() + }, + ); + } + } +} + +impl Types { + pub fn new(vec_box_size_threshold: u64, type_complexity_threshold: u64) -> Self { + Self { + vec_box_size_threshold, + type_complexity_threshold, + } + } + + fn check_fn_decl(&mut self, cx: &LateContext<'_>, decl: &FnDecl<'_>, context: CheckTyContext) { + for input in decl.inputs { + self.check_ty(cx, input, context); + } + + if let FnRetTy::Return(ty) = decl.output { + self.check_ty(cx, ty, context); + } + } + + /// Recursively check for `TypePass` lints in the given type. Stop at the first + /// lint found. + /// + /// The parameter `is_local` distinguishes the context of the type. + fn check_ty(&mut self, cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, mut context: CheckTyContext) { + if hir_ty.span.from_expansion() { + return; + } + + if !context.is_nested_call && type_complexity::check(cx, hir_ty, self.type_complexity_threshold) { + return; + } + + // Skip trait implementations; see issue #605. + if context.is_in_trait_impl { + return; + } + + match hir_ty.kind { + TyKind::Path(ref qpath) if !context.is_local => { + let hir_id = hir_ty.hir_id; + let res = cx.qpath_res(qpath, hir_id); + if let Some(def_id) = res.opt_def_id() { + let mut triggered = false; + triggered |= box_vec::check(cx, hir_ty, qpath, def_id); + triggered |= redundant_allocation::check(cx, hir_ty, qpath, def_id); + triggered |= rc_buffer::check(cx, hir_ty, qpath, def_id); + triggered |= vec_box::check(cx, hir_ty, qpath, def_id, self.vec_box_size_threshold); + triggered |= option_option::check(cx, hir_ty, qpath, def_id); + triggered |= linked_list::check(cx, hir_ty, def_id); + + if triggered { + return; + } + } + match *qpath { + QPath::Resolved(Some(ty), p) => { + context.is_nested_call = true; + self.check_ty(cx, ty, context); + for ty in p.segments.iter().flat_map(|seg| { + seg.args + .as_ref() + .map_or_else(|| [].iter(), |params| params.args.iter()) + .filter_map(|arg| match arg { + GenericArg::Type(ty) => Some(ty), + _ => None, + }) + }) { + self.check_ty(cx, ty, context); + } + }, + QPath::Resolved(None, p) => { + context.is_nested_call = true; + for ty in p.segments.iter().flat_map(|seg| { + seg.args + .as_ref() + .map_or_else(|| [].iter(), |params| params.args.iter()) + .filter_map(|arg| match arg { + GenericArg::Type(ty) => Some(ty), + _ => None, + }) + }) { + self.check_ty(cx, ty, context); + } + }, + QPath::TypeRelative(ty, seg) => { + context.is_nested_call = true; + self.check_ty(cx, ty, context); + if let Some(params) = seg.args { + for ty in params.args.iter().filter_map(|arg| match arg { + GenericArg::Type(ty) => Some(ty), + _ => None, + }) { + self.check_ty(cx, ty, context); + } + } + }, + QPath::LangItem(..) => {}, + } + }, + TyKind::Rptr(ref lt, ref mut_ty) => { + context.is_nested_call = true; + if !borrowed_box::check(cx, hir_ty, lt, mut_ty) { + self.check_ty(cx, mut_ty.ty, context); + } + }, + TyKind::Slice(ty) | TyKind::Array(ty, _) | TyKind::Ptr(MutTy { ty, .. }) => { + context.is_nested_call = true; + self.check_ty(cx, ty, context) + }, + TyKind::Tup(tys) => { + context.is_nested_call = true; + for ty in tys { + self.check_ty(cx, ty, context); + } + }, + _ => {}, + } + } +} + +#[derive(Clone, Copy, Default)] +struct CheckTyContext { + is_in_trait_impl: bool, + is_local: bool, + is_nested_call: bool, +} diff --git a/src/tools/clippy/clippy_lints/src/types/option_option.rs b/src/tools/clippy/clippy_lints/src/types/option_option.rs new file mode 100644 index 0000000000000..b2692c48076b6 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/types/option_option.rs @@ -0,0 +1,24 @@ +use clippy_utils::diagnostics::span_lint; +use clippy_utils::is_ty_param_diagnostic_item; +use rustc_hir::{self as hir, def_id::DefId, QPath}; +use rustc_lint::LateContext; +use rustc_span::symbol::sym; + +use super::OPTION_OPTION; + +pub(super) fn check(cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, qpath: &QPath<'_>, def_id: DefId) -> bool { + if cx.tcx.is_diagnostic_item(sym::option_type, def_id) + && is_ty_param_diagnostic_item(cx, qpath, sym::option_type).is_some() + { + span_lint( + cx, + OPTION_OPTION, + hir_ty.span, + "consider using `Option<T>` instead of `Option<Option<T>>` or a custom \ + enum if you need to distinguish all 3 cases", + ); + true + } else { + false + } +} diff --git a/src/tools/clippy/clippy_lints/src/types/rc_buffer.rs b/src/tools/clippy/clippy_lints/src/types/rc_buffer.rs new file mode 100644 index 0000000000000..ef629a35d107e --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/types/rc_buffer.rs @@ -0,0 +1,97 @@ +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::source::snippet_with_applicability; +use clippy_utils::{get_qpath_generic_tys, is_ty_param_diagnostic_item}; +use rustc_errors::Applicability; +use rustc_hir::{self as hir, def_id::DefId, QPath, TyKind}; +use rustc_lint::LateContext; +use rustc_span::symbol::sym; + +use super::RC_BUFFER; + +pub(super) fn check(cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, qpath: &QPath<'_>, def_id: DefId) -> bool { + if cx.tcx.is_diagnostic_item(sym::Rc, def_id) { + if let Some(alternate) = match_buffer_type(cx, qpath) { + span_lint_and_sugg( + cx, + RC_BUFFER, + hir_ty.span, + "usage of `Rc<T>` when T is a buffer type", + "try", + format!("Rc<{}>", alternate), + Applicability::MachineApplicable, + ); + } else if let Some(ty) = is_ty_param_diagnostic_item(cx, qpath, sym::vec_type) { + let qpath = match &ty.kind { + TyKind::Path(qpath) => qpath, + _ => return false, + }; + let inner_span = match get_qpath_generic_tys(qpath).next() { + Some(ty) => ty.span, + None => return false, + }; + let mut applicability = Applicability::MachineApplicable; + span_lint_and_sugg( + cx, + RC_BUFFER, + hir_ty.span, + "usage of `Rc<T>` when T is a buffer type", + "try", + format!( + "Rc<[{}]>", + snippet_with_applicability(cx, inner_span, "..", &mut applicability) + ), + Applicability::MachineApplicable, + ); + return true; + } + } else if cx.tcx.is_diagnostic_item(sym::Arc, def_id) { + if let Some(alternate) = match_buffer_type(cx, qpath) { + span_lint_and_sugg( + cx, + RC_BUFFER, + hir_ty.span, + "usage of `Arc<T>` when T is a buffer type", + "try", + format!("Arc<{}>", alternate), + Applicability::MachineApplicable, + ); + } else if let Some(ty) = is_ty_param_diagnostic_item(cx, qpath, sym::vec_type) { + let qpath = match &ty.kind { + TyKind::Path(qpath) => qpath, + _ => return false, + }; + let inner_span = match get_qpath_generic_tys(qpath).next() { + Some(ty) => ty.span, + None => return false, + }; + let mut applicability = Applicability::MachineApplicable; + span_lint_and_sugg( + cx, + RC_BUFFER, + hir_ty.span, + "usage of `Arc<T>` when T is a buffer type", + "try", + format!( + "Arc<[{}]>", + snippet_with_applicability(cx, inner_span, "..", &mut applicability) + ), + Applicability::MachineApplicable, + ); + return true; + } + } + + false +} + +fn match_buffer_type(cx: &LateContext<'_>, qpath: &QPath<'_>) -> Option<&'static str> { + if is_ty_param_diagnostic_item(cx, qpath, sym::string_type).is_some() { + Some("str") + } else if is_ty_param_diagnostic_item(cx, qpath, sym::OsString).is_some() { + Some("std::ffi::OsStr") + } else if is_ty_param_diagnostic_item(cx, qpath, sym::PathBuf).is_some() { + Some("std::path::Path") + } else { + None + } +} diff --git a/src/tools/clippy/clippy_lints/src/types/redundant_allocation.rs b/src/tools/clippy/clippy_lints/src/types/redundant_allocation.rs new file mode 100644 index 0000000000000..c0c1f340583c1 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/types/redundant_allocation.rs @@ -0,0 +1,82 @@ +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::source::snippet_with_applicability; +use clippy_utils::{get_qpath_generic_tys, is_ty_param_diagnostic_item, is_ty_param_lang_item}; +use rustc_errors::Applicability; +use rustc_hir::{self as hir, def_id::DefId, LangItem, QPath, TyKind}; +use rustc_lint::LateContext; +use rustc_span::symbol::sym; + +use super::{utils, REDUNDANT_ALLOCATION}; + +pub(super) fn check(cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, qpath: &QPath<'_>, def_id: DefId) -> bool { + if Some(def_id) == cx.tcx.lang_items().owned_box() { + if let Some(span) = utils::match_borrows_parameter(cx, qpath) { + let mut applicability = Applicability::MachineApplicable; + span_lint_and_sugg( + cx, + REDUNDANT_ALLOCATION, + hir_ty.span, + "usage of `Box<&T>`", + "try", + snippet_with_applicability(cx, span, "..", &mut applicability).to_string(), + applicability, + ); + return true; + } + } + + if cx.tcx.is_diagnostic_item(sym::Rc, def_id) { + if let Some(ty) = is_ty_param_diagnostic_item(cx, qpath, sym::Rc) { + let mut applicability = Applicability::MachineApplicable; + span_lint_and_sugg( + cx, + REDUNDANT_ALLOCATION, + hir_ty.span, + "usage of `Rc<Rc<T>>`", + "try", + snippet_with_applicability(cx, ty.span, "..", &mut applicability).to_string(), + applicability, + ); + true + } else if let Some(ty) = is_ty_param_lang_item(cx, qpath, LangItem::OwnedBox) { + let qpath = match &ty.kind { + TyKind::Path(qpath) => qpath, + _ => return false, + }; + let inner_span = match get_qpath_generic_tys(qpath).next() { + Some(ty) => ty.span, + None => return false, + }; + let mut applicability = Applicability::MachineApplicable; + span_lint_and_sugg( + cx, + REDUNDANT_ALLOCATION, + hir_ty.span, + "usage of `Rc<Box<T>>`", + "try", + format!( + "Rc<{}>", + snippet_with_applicability(cx, inner_span, "..", &mut applicability) + ), + applicability, + ); + true + } else { + utils::match_borrows_parameter(cx, qpath).map_or(false, |span| { + let mut applicability = Applicability::MachineApplicable; + span_lint_and_sugg( + cx, + REDUNDANT_ALLOCATION, + hir_ty.span, + "usage of `Rc<&T>`", + "try", + snippet_with_applicability(cx, span, "..", &mut applicability).to_string(), + applicability, + ); + true + }) + } + } else { + false + } +} diff --git a/src/tools/clippy/clippy_lints/src/types/type_complexity.rs b/src/tools/clippy/clippy_lints/src/types/type_complexity.rs new file mode 100644 index 0000000000000..d8c4b67520d11 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/types/type_complexity.rs @@ -0,0 +1,79 @@ +use clippy_utils::diagnostics::span_lint; +use rustc_hir as hir; +use rustc_hir::intravisit::{walk_ty, NestedVisitorMap, Visitor}; +use rustc_hir::{GenericParamKind, TyKind}; +use rustc_lint::LateContext; +use rustc_middle::hir::map::Map; +use rustc_target::spec::abi::Abi; + +use super::TYPE_COMPLEXITY; + +pub(super) fn check(cx: &LateContext<'_>, ty: &hir::Ty<'_>, type_complexity_threshold: u64) -> bool { + let score = { + let mut visitor = TypeComplexityVisitor { score: 0, nest: 1 }; + visitor.visit_ty(ty); + visitor.score + }; + + if score > type_complexity_threshold { + span_lint( + cx, + TYPE_COMPLEXITY, + ty.span, + "very complex type used. Consider factoring parts into `type` definitions", + ); + true + } else { + false + } +} + +/// Walks a type and assigns a complexity score to it. +struct TypeComplexityVisitor { + /// total complexity score of the type + score: u64, + /// current nesting level + nest: u64, +} + +impl<'tcx> Visitor<'tcx> for TypeComplexityVisitor { + type Map = Map<'tcx>; + + fn visit_ty(&mut self, ty: &'tcx hir::Ty<'_>) { + let (add_score, sub_nest) = match ty.kind { + // _, &x and *x have only small overhead; don't mess with nesting level + TyKind::Infer | TyKind::Ptr(..) | TyKind::Rptr(..) => (1, 0), + + // the "normal" components of a type: named types, arrays/tuples + TyKind::Path(..) | TyKind::Slice(..) | TyKind::Tup(..) | TyKind::Array(..) => (10 * self.nest, 1), + + // function types bring a lot of overhead + TyKind::BareFn(bare) if bare.abi == Abi::Rust => (50 * self.nest, 1), + + TyKind::TraitObject(param_bounds, _, _) => { + let has_lifetime_parameters = param_bounds.iter().any(|bound| { + bound + .bound_generic_params + .iter() + .any(|gen| matches!(gen.kind, GenericParamKind::Lifetime { .. })) + }); + if has_lifetime_parameters { + // complex trait bounds like A<'a, 'b> + (50 * self.nest, 1) + } else { + // simple trait bounds like A + B + (20 * self.nest, 0) + } + }, + + _ => (0, 0), + }; + self.score += add_score; + self.nest += sub_nest; + walk_ty(self, ty); + self.nest -= sub_nest; + } + fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> { + NestedVisitorMap::None + } +} diff --git a/src/tools/clippy/clippy_lints/src/types/utils.rs b/src/tools/clippy/clippy_lints/src/types/utils.rs new file mode 100644 index 0000000000000..0fa75f8f0a9be --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/types/utils.rs @@ -0,0 +1,22 @@ +use clippy_utils::last_path_segment; +use if_chain::if_chain; +use rustc_hir::{GenericArg, QPath, TyKind}; +use rustc_lint::LateContext; +use rustc_span::source_map::Span; + +pub(super) fn match_borrows_parameter(_cx: &LateContext<'_>, qpath: &QPath<'_>) -> Option<Span> { + let last = last_path_segment(qpath); + if_chain! { + if let Some(params) = last.args; + if !params.parenthesized; + if let Some(ty) = params.args.iter().find_map(|arg| match arg { + GenericArg::Type(ty) => Some(ty), + _ => None, + }); + if let TyKind::Rptr(..) = ty.kind; + then { + return Some(ty.span); + } + } + None +} diff --git a/src/tools/clippy/clippy_lints/src/types/vec_box.rs b/src/tools/clippy/clippy_lints/src/types/vec_box.rs new file mode 100644 index 0000000000000..7a444174626f4 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/types/vec_box.rs @@ -0,0 +1,64 @@ +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::last_path_segment; +use clippy_utils::source::snippet; +use if_chain::if_chain; +use rustc_errors::Applicability; +use rustc_hir::{self as hir, def_id::DefId, GenericArg, QPath, TyKind}; +use rustc_lint::LateContext; +use rustc_middle::ty::TypeFoldable; +use rustc_span::symbol::sym; +use rustc_target::abi::LayoutOf; +use rustc_typeck::hir_ty_to_ty; + +use super::VEC_BOX; + +pub(super) fn check( + cx: &LateContext<'_>, + hir_ty: &hir::Ty<'_>, + qpath: &QPath<'_>, + def_id: DefId, + box_size_threshold: u64, +) -> bool { + if cx.tcx.is_diagnostic_item(sym::vec_type, def_id) { + if_chain! { + // Get the _ part of Vec<_> + if let Some(last) = last_path_segment(qpath).args; + if let Some(ty) = last.args.iter().find_map(|arg| match arg { + GenericArg::Type(ty) => Some(ty), + _ => None, + }); + // ty is now _ at this point + if let TyKind::Path(ref ty_qpath) = ty.kind; + let res = cx.qpath_res(ty_qpath, ty.hir_id); + if let Some(def_id) = res.opt_def_id(); + if Some(def_id) == cx.tcx.lang_items().owned_box(); + // At this point, we know ty is Box<T>, now get T + if let Some(last) = last_path_segment(ty_qpath).args; + if let Some(boxed_ty) = last.args.iter().find_map(|arg| match arg { + GenericArg::Type(ty) => Some(ty), + _ => None, + }); + let ty_ty = hir_ty_to_ty(cx.tcx, boxed_ty); + if !ty_ty.has_escaping_bound_vars(); + if ty_ty.is_sized(cx.tcx.at(ty.span), cx.param_env); + if let Ok(ty_ty_size) = cx.layout_of(ty_ty).map(|l| l.size.bytes()); + if ty_ty_size <= box_size_threshold; + then { + span_lint_and_sugg( + cx, + VEC_BOX, + hir_ty.span, + "`Vec<T>` is already on the heap, the boxing is unnecessary", + "try", + format!("Vec<{}>", snippet(cx, boxed_ty.span, "..")), + Applicability::MachineApplicable, + ); + true + } else { + false + } + } + } else { + false + } +} 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 5443f1601fcbb..f4f5e1233e351 100644 --- a/src/tools/clippy/clippy_lints/src/undropped_manually_drops.rs +++ b/src/tools/clippy/clippy_lints/src/undropped_manually_drops.rs @@ -1,4 +1,6 @@ -use crate::utils::{is_type_lang_item, match_function_call, paths, span_lint_and_help}; +use clippy_utils::diagnostics::span_lint_and_help; +use clippy_utils::ty::is_type_lang_item; +use clippy_utils::{match_function_call, paths}; use rustc_hir::{lang_items, Expr}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; @@ -33,7 +35,7 @@ declare_lint_pass!(UndroppedManuallyDrops => [UNDROPPED_MANUALLY_DROPS]); impl LateLintPass<'tcx> for UndroppedManuallyDrops { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - if let Some(ref args) = match_function_call(cx, expr, &paths::DROP) { + if let Some(args) = match_function_call(cx, expr, &paths::DROP) { let ty = cx.typeck_results().expr_ty(&args[0]); if is_type_lang_item(cx, ty, lang_items::LangItem::ManuallyDrop) { span_lint_and_help( diff --git a/src/tools/clippy/clippy_lints/src/unicode.rs b/src/tools/clippy/clippy_lints/src/unicode.rs index 93d59cc7fcd17..d81e31f5a21d4 100644 --- a/src/tools/clippy/clippy_lints/src/unicode.rs +++ b/src/tools/clippy/clippy_lints/src/unicode.rs @@ -1,4 +1,6 @@ -use crate::utils::{is_allowed, snippet, span_lint_and_sugg}; +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::is_allowed; +use clippy_utils::source::snippet; use rustc_ast::ast::LitKind; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind, HirId}; diff --git a/src/tools/clippy/clippy_lints/src/unit_return_expecting_ord.rs b/src/tools/clippy/clippy_lints/src/unit_return_expecting_ord.rs index c6ae8b9b59837..1c420a5042721 100644 --- a/src/tools/clippy/clippy_lints/src/unit_return_expecting_ord.rs +++ b/src/tools/clippy/clippy_lints/src/unit_return_expecting_ord.rs @@ -1,4 +1,5 @@ -use crate::utils::{get_trait_def_id, paths, span_lint, span_lint_and_help}; +use clippy_utils::diagnostics::{span_lint, span_lint_and_help}; +use clippy_utils::{get_trait_def_id, paths}; use if_chain::if_chain; use rustc_hir::def_id::DefId; use rustc_hir::{Expr, ExprKind, StmtKind}; @@ -43,7 +44,7 @@ fn get_trait_predicates_for_trait_id<'tcx>( for (pred, _) in generics.predicates { if_chain! { if let PredicateKind::Trait(poly_trait_pred, _) = pred.kind().skip_binder(); - let trait_pred = cx.tcx.erase_late_bound_regions(ty::Binder::bind(poly_trait_pred)); + let trait_pred = cx.tcx.erase_late_bound_regions(pred.kind().rebind(poly_trait_pred)); if let Some(trait_def_id) = trait_id; if trait_def_id == trait_pred.trait_ref.def_id; then { @@ -57,12 +58,12 @@ fn get_trait_predicates_for_trait_id<'tcx>( fn get_projection_pred<'tcx>( cx: &LateContext<'tcx>, generics: GenericPredicates<'tcx>, - pred: TraitPredicate<'tcx>, + trait_pred: TraitPredicate<'tcx>, ) -> Option<ProjectionPredicate<'tcx>> { generics.predicates.iter().find_map(|(proj_pred, _)| { - if let ty::PredicateKind::Projection(proj_pred) = proj_pred.kind().skip_binder() { - let projection_pred = cx.tcx.erase_late_bound_regions(ty::Binder::bind(proj_pred)); - if projection_pred.projection_ty.substs == pred.trait_ref.substs { + if let ty::PredicateKind::Projection(pred) = proj_pred.kind().skip_binder() { + let projection_pred = cx.tcx.erase_late_bound_regions(proj_pred.kind().rebind(pred)); + if projection_pred.projection_ty.substs == trait_pred.trait_ref.substs { return Some(projection_pred); } } @@ -115,8 +116,8 @@ fn check_arg<'tcx>(cx: &LateContext<'tcx>, arg: &'tcx Expr<'tcx>) -> Option<(Spa let ty = cx.tcx.erase_late_bound_regions(ret_ty); if ty.is_unit(); then { + let body = cx.tcx.hir().body(body_id); if_chain! { - let body = cx.tcx.hir().body(body_id); if let ExprKind::Block(block, _) = body.value.kind; if block.expr.is_none(); if let Some(stmt) = block.stmts.last(); @@ -137,7 +138,7 @@ fn check_arg<'tcx>(cx: &LateContext<'tcx>, arg: &'tcx Expr<'tcx>) -> Option<(Spa impl<'tcx> LateLintPass<'tcx> for UnitReturnExpectingOrd { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { - if let ExprKind::MethodCall(_, _, ref args, _) = expr.kind { + if let ExprKind::MethodCall(_, _, args, _) = expr.kind { let arg_indices = get_args_to_check(cx, expr); for (i, trait_name) in arg_indices { if i < args.len() { diff --git a/src/tools/clippy/clippy_lints/src/unit_types/let_unit_value.rs b/src/tools/clippy/clippy_lints/src/unit_types/let_unit_value.rs new file mode 100644 index 0000000000000..fad647dfb2662 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/unit_types/let_unit_value.rs @@ -0,0 +1,39 @@ +use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::higher; +use clippy_utils::source::snippet_with_macro_callsite; +use rustc_errors::Applicability; +use rustc_hir::{Stmt, StmtKind}; +use rustc_lint::{LateContext, LintContext}; +use rustc_middle::lint::in_external_macro; + +use super::LET_UNIT_VALUE; + +pub(super) fn check(cx: &LateContext<'_>, stmt: &Stmt<'_>) { + if let StmtKind::Local(local) = stmt.kind { + if cx.typeck_results().pat_ty(local.pat).is_unit() { + if in_external_macro(cx.sess(), stmt.span) || local.pat.span.from_expansion() { + return; + } + if higher::is_from_for_desugar(local) { + return; + } + span_lint_and_then( + cx, + LET_UNIT_VALUE, + stmt.span, + "this let-binding has unit value", + |diag| { + if let Some(expr) = &local.init { + let snip = snippet_with_macro_callsite(cx, expr.span, "()"); + diag.span_suggestion( + stmt.span, + "omit the `let` binding", + format!("{};", snip), + Applicability::MachineApplicable, // snippet + ); + } + }, + ); + } + } +} diff --git a/src/tools/clippy/clippy_lints/src/unit_types/mod.rs b/src/tools/clippy/clippy_lints/src/unit_types/mod.rs new file mode 100644 index 0000000000000..64420a0393349 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/unit_types/mod.rs @@ -0,0 +1,107 @@ +mod let_unit_value; +mod unit_arg; +mod unit_cmp; +mod utils; + +use rustc_hir::{Expr, Stmt}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; + +declare_clippy_lint! { + /// **What it does:** Checks for binding a unit value. + /// + /// **Why is this bad?** A unit value cannot usefully be used anywhere. So + /// binding one is kind of pointless. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// ```rust + /// let x = { + /// 1; + /// }; + /// ``` + pub LET_UNIT_VALUE, + pedantic, + "creating a `let` binding to a value of unit type, which usually can't be used afterwards" +} + +declare_clippy_lint! { + /// **What it does:** Checks for comparisons to unit. This includes all binary + /// comparisons (like `==` and `<`) and asserts. + /// + /// **Why is this bad?** Unit is always equal to itself, and thus is just a + /// clumsily written constant. Mostly this happens when someone accidentally + /// adds semicolons at the end of the operands. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// ```rust + /// # fn foo() {}; + /// # fn bar() {}; + /// # fn baz() {}; + /// if { + /// foo(); + /// } == { + /// bar(); + /// } { + /// baz(); + /// } + /// ``` + /// is equal to + /// ```rust + /// # fn foo() {}; + /// # fn bar() {}; + /// # fn baz() {}; + /// { + /// foo(); + /// bar(); + /// baz(); + /// } + /// ``` + /// + /// For asserts: + /// ```rust + /// # fn foo() {}; + /// # fn bar() {}; + /// assert_eq!({ foo(); }, { bar(); }); + /// ``` + /// will always succeed + pub UNIT_CMP, + correctness, + "comparing unit values" +} + +declare_clippy_lint! { + /// **What it does:** Checks for passing a unit value as an argument to a function without using a + /// unit literal (`()`). + /// + /// **Why is this bad?** This is likely the result of an accidental semicolon. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// ```rust,ignore + /// foo({ + /// let a = bar(); + /// baz(a); + /// }) + /// ``` + pub UNIT_ARG, + complexity, + "passing unit to a function" +} + +declare_lint_pass!(UnitTypes => [LET_UNIT_VALUE, UNIT_CMP, UNIT_ARG]); + +impl LateLintPass<'_> for UnitTypes { + fn check_stmt(&mut self, cx: &LateContext<'_>, stmt: &Stmt<'_>) { + let_unit_value::check(cx, stmt); + } + + fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) { + unit_cmp::check(cx, expr); + unit_arg::check(cx, expr); + } +} diff --git a/src/tools/clippy/clippy_lints/src/unit_types/unit_arg.rs b/src/tools/clippy/clippy_lints/src/unit_types/unit_arg.rs new file mode 100644 index 0000000000000..57be2d2f674cf --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/unit_types/unit_arg.rs @@ -0,0 +1,207 @@ +use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::source::{indent_of, reindent_multiline, snippet_opt}; +use if_chain::if_chain; +use rustc_errors::Applicability; +use rustc_hir::{self as hir, Block, Expr, ExprKind, MatchSource, Node, StmtKind}; +use rustc_lint::LateContext; + +use super::{utils, UNIT_ARG}; + +pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>) { + if expr.span.from_expansion() { + return; + } + + // apparently stuff in the desugaring of `?` can trigger this + // so check for that here + // only the calls to `Try::from_error` is marked as desugared, + // so we need to check both the current Expr and its parent. + if is_questionmark_desugar_marked_call(expr) { + return; + } + let map = &cx.tcx.hir(); + let opt_parent_node = map.find(map.get_parent_node(expr.hir_id)); + if_chain! { + if let Some(hir::Node::Expr(parent_expr)) = opt_parent_node; + if is_questionmark_desugar_marked_call(parent_expr); + then { + return; + } + } + + match expr.kind { + ExprKind::Call(_, args) | ExprKind::MethodCall(_, _, args, _) => { + let args_to_recover = args + .iter() + .filter(|arg| { + if cx.typeck_results().expr_ty(arg).is_unit() && !utils::is_unit_literal(arg) { + !matches!( + &arg.kind, + ExprKind::Match(.., MatchSource::TryDesugar) | ExprKind::Path(..) + ) + } else { + false + } + }) + .collect::<Vec<_>>(); + if !args_to_recover.is_empty() { + lint_unit_args(cx, expr, &args_to_recover); + } + }, + _ => (), + } +} + +fn is_questionmark_desugar_marked_call(expr: &Expr<'_>) -> bool { + use rustc_span::hygiene::DesugaringKind; + if let ExprKind::Call(callee, _) = expr.kind { + callee.span.is_desugaring(DesugaringKind::QuestionMark) + } else { + false + } +} + +fn lint_unit_args(cx: &LateContext<'_>, expr: &Expr<'_>, args_to_recover: &[&Expr<'_>]) { + let mut applicability = Applicability::MachineApplicable; + let (singular, plural) = if args_to_recover.len() > 1 { + ("", "s") + } else { + ("a ", "") + }; + span_lint_and_then( + cx, + UNIT_ARG, + expr.span, + &format!("passing {}unit value{} to a function", singular, plural), + |db| { + let mut or = ""; + args_to_recover + .iter() + .filter_map(|arg| { + if_chain! { + if let ExprKind::Block(block, _) = arg.kind; + if block.expr.is_none(); + if let Some(last_stmt) = block.stmts.iter().last(); + if let StmtKind::Semi(last_expr) = last_stmt.kind; + if let Some(snip) = snippet_opt(cx, last_expr.span); + then { + Some(( + last_stmt.span, + snip, + )) + } + else { + None + } + } + }) + .for_each(|(span, sugg)| { + db.span_suggestion( + span, + "remove the semicolon from the last statement in the block", + sugg, + Applicability::MaybeIncorrect, + ); + or = "or "; + applicability = Applicability::MaybeIncorrect; + }); + + let arg_snippets: Vec<String> = args_to_recover + .iter() + .filter_map(|arg| snippet_opt(cx, arg.span)) + .collect(); + let arg_snippets_without_empty_blocks: Vec<String> = args_to_recover + .iter() + .filter(|arg| !is_empty_block(arg)) + .filter_map(|arg| snippet_opt(cx, arg.span)) + .collect(); + + if let Some(call_snippet) = snippet_opt(cx, expr.span) { + let sugg = fmt_stmts_and_call( + cx, + expr, + &call_snippet, + &arg_snippets, + &arg_snippets_without_empty_blocks, + ); + + if arg_snippets_without_empty_blocks.is_empty() { + db.multipart_suggestion( + &format!("use {}unit literal{} instead", singular, plural), + args_to_recover + .iter() + .map(|arg| (arg.span, "()".to_string())) + .collect::<Vec<_>>(), + applicability, + ); + } else { + let plural = arg_snippets_without_empty_blocks.len() > 1; + let empty_or_s = if plural { "s" } else { "" }; + let it_or_them = if plural { "them" } else { "it" }; + db.span_suggestion( + expr.span, + &format!( + "{}move the expression{} in front of the call and replace {} with the unit literal `()`", + or, empty_or_s, it_or_them + ), + sugg, + applicability, + ); + } + } + }, + ); +} + +fn is_empty_block(expr: &Expr<'_>) -> bool { + matches!( + expr.kind, + ExprKind::Block( + Block { + stmts: &[], + expr: None, + .. + }, + _, + ) + ) +} + +fn fmt_stmts_and_call( + cx: &LateContext<'_>, + call_expr: &Expr<'_>, + call_snippet: &str, + args_snippets: &[impl AsRef<str>], + non_empty_block_args_snippets: &[impl AsRef<str>], +) -> String { + let call_expr_indent = indent_of(cx, call_expr.span).unwrap_or(0); + let call_snippet_with_replacements = args_snippets + .iter() + .fold(call_snippet.to_owned(), |acc, arg| acc.replacen(arg.as_ref(), "()", 1)); + + let mut stmts_and_call = non_empty_block_args_snippets + .iter() + .map(|it| it.as_ref().to_owned()) + .collect::<Vec<_>>(); + stmts_and_call.push(call_snippet_with_replacements); + stmts_and_call = stmts_and_call + .into_iter() + .map(|v| reindent_multiline(v.into(), true, Some(call_expr_indent)).into_owned()) + .collect(); + + let mut stmts_and_call_snippet = stmts_and_call.join(&format!("{}{}", ";\n", " ".repeat(call_expr_indent))); + // expr is not in a block statement or result expression position, wrap in a block + let parent_node = cx.tcx.hir().find(cx.tcx.hir().get_parent_node(call_expr.hir_id)); + if !matches!(parent_node, Some(Node::Block(_))) && !matches!(parent_node, Some(Node::Stmt(_))) { + let block_indent = call_expr_indent + 4; + stmts_and_call_snippet = + reindent_multiline(stmts_and_call_snippet.into(), true, Some(block_indent)).into_owned(); + stmts_and_call_snippet = format!( + "{{\n{}{}\n{}}}", + " ".repeat(block_indent), + &stmts_and_call_snippet, + " ".repeat(call_expr_indent) + ); + } + stmts_and_call_snippet +} 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 new file mode 100644 index 0000000000000..85257f3113cb7 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/unit_types/unit_cmp.rs @@ -0,0 +1,56 @@ +use clippy_utils::diagnostics::span_lint; +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 + ), + ); + } + } + } + } + return; + } + + 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 op { + BinOpKind::Eq | BinOpKind::Le | BinOpKind::Ge => "true", + _ => "false", + }; + span_lint( + cx, + UNIT_CMP, + expr.span, + &format!( + "{}-comparison of unit values detected. This will always be {}", + op.as_str(), + result + ), + ); + } + } +} diff --git a/src/tools/clippy/clippy_lints/src/unit_types/utils.rs b/src/tools/clippy/clippy_lints/src/unit_types/utils.rs new file mode 100644 index 0000000000000..9a3750b2356bc --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/unit_types/utils.rs @@ -0,0 +1,5 @@ +use rustc_hir::{Expr, ExprKind}; + +pub(super) fn is_unit_literal(expr: &Expr<'_>) -> bool { + matches!(expr.kind, ExprKind::Tup(slice) if slice.is_empty()) +} diff --git a/src/tools/clippy/clippy_lints/src/unnamed_address.rs b/src/tools/clippy/clippy_lints/src/unnamed_address.rs index 9582c162e77b2..9cca05b1f1a65 100644 --- a/src/tools/clippy/clippy_lints/src/unnamed_address.rs +++ b/src/tools/clippy/clippy_lints/src/unnamed_address.rs @@ -1,4 +1,5 @@ -use crate::utils::{match_def_path, paths, span_lint, span_lint_and_help}; +use clippy_utils::diagnostics::{span_lint, span_lint_and_help}; +use clippy_utils::{match_def_path, paths}; use if_chain::if_chain; use rustc_hir::{BinOpKind, Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; @@ -76,7 +77,7 @@ impl LateLintPass<'_> for UnnamedAddress { } if_chain! { - if let ExprKind::Binary(binop, ref left, ref right) = expr.kind; + if let ExprKind::Binary(binop, left, right) = expr.kind; if is_comparison(binop.node); if is_trait_ptr(cx, left) && is_trait_ptr(cx, right); then { @@ -92,7 +93,7 @@ impl LateLintPass<'_> for UnnamedAddress { } if_chain! { - if let ExprKind::Call(ref func, [ref _left, ref _right]) = expr.kind; + if let ExprKind::Call(func, [ref _left, ref _right]) = expr.kind; if let ExprKind::Path(ref func_qpath) = func.kind; if let Some(def_id) = cx.qpath_res(func_qpath, func.hir_id).opt_def_id(); if match_def_path(cx, def_id, &paths::PTR_EQ) || @@ -113,10 +114,10 @@ impl LateLintPass<'_> for UnnamedAddress { } if_chain! { - if let ExprKind::Binary(binop, ref left, ref right) = expr.kind; + if let ExprKind::Binary(binop, left, right) = expr.kind; if is_comparison(binop.node); - if cx.typeck_results().expr_ty_adjusted(left).is_fn_ptr() && - cx.typeck_results().expr_ty_adjusted(right).is_fn_ptr(); + if cx.typeck_results().expr_ty_adjusted(left).is_fn_ptr(); + if cx.typeck_results().expr_ty_adjusted(right).is_fn_ptr(); if is_fn_def(cx, left) || is_fn_def(cx, right); then { span_lint( diff --git a/src/tools/clippy/clippy_lints/src/unnecessary_sort_by.rs b/src/tools/clippy/clippy_lints/src/unnecessary_sort_by.rs index 00a707107bce9..347d858b64026 100644 --- a/src/tools/clippy/clippy_lints/src/unnecessary_sort_by.rs +++ b/src/tools/clippy/clippy_lints/src/unnecessary_sort_by.rs @@ -1,5 +1,6 @@ -use crate::utils; -use crate::utils::sugg::Sugg; +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::sugg::Sugg; +use clippy_utils::ty::is_type_diagnostic_item; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind, Mutability, Param, Pat, PatKind, Path, PathSegment, QPath}; @@ -8,6 +9,7 @@ use rustc_middle::ty::{self, subst::GenericArgKind}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::sym; use rustc_span::symbol::Ident; +use std::iter; declare_clippy_lint! { /// **What it does:** @@ -78,17 +80,14 @@ fn mirrored_exprs( mirrored_exprs(cx, left_expr, a_ident, right_expr, b_ident) }, // Two arrays with mirrored contents - (ExprKind::Array(left_exprs), ExprKind::Array(right_exprs)) => left_exprs - .iter() - .zip(right_exprs.iter()) - .all(|(left, right)| mirrored_exprs(cx, left, a_ident, right, b_ident)), + (ExprKind::Array(left_exprs), ExprKind::Array(right_exprs)) => { + iter::zip(*left_exprs, *right_exprs).all(|(left, right)| mirrored_exprs(cx, left, a_ident, right, b_ident)) + }, // The two exprs are function calls. // Check to see that the function itself and its arguments are mirrored (ExprKind::Call(left_expr, left_args), ExprKind::Call(right_expr, right_args)) => { mirrored_exprs(cx, left_expr, a_ident, right_expr, b_ident) - && left_args - .iter() - .zip(right_args.iter()) + && iter::zip(*left_args, *right_args) .all(|(left, right)| mirrored_exprs(cx, left, a_ident, right, b_ident)) }, // The two exprs are method calls. @@ -99,16 +98,13 @@ fn mirrored_exprs( ExprKind::MethodCall(right_segment, _, right_args, _), ) => { left_segment.ident == right_segment.ident - && left_args - .iter() - .zip(right_args.iter()) + && iter::zip(*left_args, *right_args) .all(|(left, right)| mirrored_exprs(cx, left, a_ident, right, b_ident)) }, // Two tuples with mirrored contents - (ExprKind::Tup(left_exprs), ExprKind::Tup(right_exprs)) => left_exprs - .iter() - .zip(right_exprs.iter()) - .all(|(left, right)| mirrored_exprs(cx, left, a_ident, right, b_ident)), + (ExprKind::Tup(left_exprs), ExprKind::Tup(right_exprs)) => { + iter::zip(*left_exprs, *right_exprs).all(|(left, right)| mirrored_exprs(cx, left, a_ident, right, b_ident)) + }, // Two binary ops, which are the same operation and which have mirrored arguments (ExprKind::Binary(left_op, left_left, left_right), ExprKind::Binary(right_op, right_left, right_right)) => { left_op.node == right_op.node @@ -145,10 +141,7 @@ fn mirrored_exprs( }, )), ) => { - (left_segments - .iter() - .zip(right_segments.iter()) - .all(|(left, right)| left.ident == right.ident) + (iter::zip(*left_segments, *right_segments).all(|(left, right)| left.ident == right.ident) && left_segments .iter() .all(|seg| &seg.ident != a_ident && &seg.ident != b_ident)) @@ -176,7 +169,7 @@ fn detect_lint(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<LintTrigger> { if let name = name_ident.ident.name.to_ident_string(); if name == "sort_by" || name == "sort_unstable_by"; if let [vec, Expr { kind: ExprKind::Closure(_, _, closure_body_id, _, _), .. }] = args; - if utils::is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(vec), sym::vec_type); + if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(vec), sym::vec_type); if let closure_body = cx.tcx.hir().body(*closure_body_id); if let &[ Param { pat: Pat { kind: PatKind::Binding(_, _, left_ident, _), .. }, ..}, @@ -186,15 +179,15 @@ fn detect_lint(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<LintTrigger> { if method_path.ident.name == sym::cmp; then { let (closure_body, closure_arg, reverse) = if mirrored_exprs( - &cx, - &left_expr, - &left_ident, - &right_expr, - &right_ident + cx, + left_expr, + left_ident, + right_expr, + right_ident ) { - (Sugg::hir(cx, &left_expr, "..").to_string(), left_ident.name.to_string(), false) - } else if mirrored_exprs(&cx, &left_expr, &right_ident, &right_expr, &left_ident) { - (Sugg::hir(cx, &left_expr, "..").to_string(), right_ident.name.to_string(), true) + (Sugg::hir(cx, left_expr, "..").to_string(), left_ident.name.to_string(), false) + } else if mirrored_exprs(cx, left_expr, right_ident, right_expr, left_ident) { + (Sugg::hir(cx, left_expr, "..").to_string(), right_ident.name.to_string(), true) } else { return None; }; @@ -232,7 +225,7 @@ fn expr_borrows(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { impl LateLintPass<'_> for UnnecessarySortBy { fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) { match detect_lint(cx, expr) { - Some(LintTrigger::SortByKey(trigger)) => utils::span_lint_and_sugg( + Some(LintTrigger::SortByKey(trigger)) => span_lint_and_sugg( cx, UNNECESSARY_SORT_BY, expr.span, @@ -255,7 +248,7 @@ impl LateLintPass<'_> for UnnecessarySortBy { Applicability::MachineApplicable }, ), - Some(LintTrigger::Sort(trigger)) => utils::span_lint_and_sugg( + Some(LintTrigger::Sort(trigger)) => span_lint_and_sugg( cx, UNNECESSARY_SORT_BY, expr.span, diff --git a/src/tools/clippy/clippy_lints/src/unnecessary_wraps.rs b/src/tools/clippy/clippy_lints/src/unnecessary_wraps.rs index 1e58576d0599c..5bb417cb1be4b 100644 --- a/src/tools/clippy/clippy_lints/src/unnecessary_wraps.rs +++ b/src/tools/clippy/clippy_lints/src/unnecessary_wraps.rs @@ -1,7 +1,6 @@ -use crate::utils::{ - contains_return, in_macro, match_qpath, paths, return_ty, snippet, span_lint_and_then, - visitors::find_all_ret_expressions, -}; +use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::source::snippet; +use clippy_utils::{contains_return, in_macro, match_qpath, paths, return_ty, visitors::find_all_ret_expressions}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::intravisit::FnKind; @@ -66,13 +65,13 @@ impl<'tcx> LateLintPass<'tcx> for UnnecessaryWraps { ) { // Abort if public function/method or closure. match fn_kind { - FnKind::ItemFn(.., visibility, _) | FnKind::Method(.., Some(visibility), _) => { + FnKind::ItemFn(.., visibility) | FnKind::Method(.., Some(visibility)) => { if visibility.node.is_pub() { return; } }, - FnKind::Closure(..) => return, - _ => (), + FnKind::Closure => return, + FnKind::Method(..) => (), } // Abort if the method is implementing a trait or of it a trait method. @@ -104,7 +103,7 @@ impl<'tcx> LateLintPass<'tcx> for UnnecessaryWraps { if_chain! { if !in_macro(ret_expr.span); // Check if a function call. - if let ExprKind::Call(ref func, ref args) = ret_expr.kind; + if let ExprKind::Call(func, args) = ret_expr.kind; // Get the Path of the function call. if let ExprKind::Path(ref qpath) = func.kind; // Check if OPTION_SOME or RESULT_OK, depending on return type. diff --git a/src/tools/clippy/clippy_lints/src/unnested_or_patterns.rs b/src/tools/clippy/clippy_lints/src/unnested_or_patterns.rs index 7f4f16f8faf96..9376a2cf66a90 100644 --- a/src/tools/clippy/clippy_lints/src/unnested_or_patterns.rs +++ b/src/tools/clippy/clippy_lints/src/unnested_or_patterns.rs @@ -1,14 +1,19 @@ #![allow(clippy::wildcard_imports, clippy::enum_glob_use)] -use crate::utils::ast_utils::{eq_field_pat, eq_id, eq_pat, eq_path}; -use crate::utils::{over, span_lint_and_then}; +use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::over; +use clippy_utils::{ + ast_utils::{eq_field_pat, eq_id, eq_pat, eq_path}, + meets_msrv, +}; use rustc_ast::mut_visit::*; use rustc_ast::ptr::P; use rustc_ast::{self as ast, Pat, PatKind, PatKind::*, DUMMY_NODE_ID}; use rustc_ast_pretty::pprust; use rustc_errors::Applicability; use rustc_lint::{EarlyContext, EarlyLintPass}; -use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_semver::RustcVersion; +use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::DUMMY_SP; use std::cell::Cell; @@ -49,34 +54,53 @@ declare_clippy_lint! { "unnested or-patterns, e.g., `Foo(Bar) | Foo(Baz) instead of `Foo(Bar | Baz)`" } -declare_lint_pass!(UnnestedOrPatterns => [UNNESTED_OR_PATTERNS]); +const UNNESTED_OR_PATTERNS_MSRV: RustcVersion = RustcVersion::new(1, 53, 0); + +#[derive(Clone, Copy)] +pub struct UnnestedOrPatterns { + msrv: Option<RustcVersion>, +} + +impl UnnestedOrPatterns { + #[must_use] + pub fn new(msrv: Option<RustcVersion>) -> Self { + Self { msrv } + } +} + +impl_lint_pass!(UnnestedOrPatterns => [UNNESTED_OR_PATTERNS]); impl EarlyLintPass for UnnestedOrPatterns { fn check_arm(&mut self, cx: &EarlyContext<'_>, a: &ast::Arm) { - lint_unnested_or_patterns(cx, &a.pat); + if meets_msrv(self.msrv.as_ref(), &UNNESTED_OR_PATTERNS_MSRV) { + lint_unnested_or_patterns(cx, &a.pat); + } } fn check_expr(&mut self, cx: &EarlyContext<'_>, e: &ast::Expr) { - if let ast::ExprKind::Let(pat, _) = &e.kind { - lint_unnested_or_patterns(cx, pat); + if meets_msrv(self.msrv.as_ref(), &UNNESTED_OR_PATTERNS_MSRV) { + if let ast::ExprKind::Let(pat, _) = &e.kind { + lint_unnested_or_patterns(cx, pat); + } } } fn check_param(&mut self, cx: &EarlyContext<'_>, p: &ast::Param) { - lint_unnested_or_patterns(cx, &p.pat); + if meets_msrv(self.msrv.as_ref(), &UNNESTED_OR_PATTERNS_MSRV) { + lint_unnested_or_patterns(cx, &p.pat); + } } fn check_local(&mut self, cx: &EarlyContext<'_>, l: &ast::Local) { - lint_unnested_or_patterns(cx, &l.pat); + if meets_msrv(self.msrv.as_ref(), &UNNESTED_OR_PATTERNS_MSRV) { + lint_unnested_or_patterns(cx, &l.pat); + } } + + extract_msrv_attr!(EarlyContext); } fn lint_unnested_or_patterns(cx: &EarlyContext<'_>, pat: &Pat) { - if !cx.sess.features_untracked().or_patterns { - // Do not suggest nesting the patterns if the feature `or_patterns` is not enabled. - return; - } - if let Ident(.., None) | Lit(_) | Wild | Path(..) | Range(..) | Rest | MacCall(_) = pat.kind { // This is a leaf pattern, so cloning is unprofitable. return; @@ -276,7 +300,7 @@ fn transform_with_focus_on_idx(alternatives: &mut Vec<P<Pat>>, focus_idx: usize) /// and check that all `fp_i` where `i ∈ ((0...n) \ k)` between two patterns are equal. fn extend_with_struct_pat( path1: &ast::Path, - fps1: &mut Vec<ast::FieldPat>, + fps1: &mut Vec<ast::PatField>, rest1: bool, start: usize, alternatives: &mut Vec<P<Pat>>, diff --git a/src/tools/clippy/clippy_lints/src/unsafe_removed_from_name.rs b/src/tools/clippy/clippy_lints/src/unsafe_removed_from_name.rs index 154082a0fdb53..16ad9d2dfd32c 100644 --- a/src/tools/clippy/clippy_lints/src/unsafe_removed_from_name.rs +++ b/src/tools/clippy/clippy_lints/src/unsafe_removed_from_name.rs @@ -1,4 +1,4 @@ -use crate::utils::span_lint; +use clippy_utils::diagnostics::span_lint; use rustc_ast::ast::{Item, ItemKind, UseTree, UseTreeKind}; use rustc_lint::{EarlyContext, EarlyLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; 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 43166d26787a7..024ab03fd418e 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,5 @@ -use crate::utils::{is_try, match_trait_method, paths, span_lint}; +use clippy_utils::diagnostics::span_lint; +use clippy_utils::{is_try, match_trait_method, paths}; use rustc_hir as hir; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; @@ -35,13 +36,13 @@ declare_lint_pass!(UnusedIoAmount => [UNUSED_IO_AMOUNT]); impl<'tcx> LateLintPass<'tcx> for UnusedIoAmount { fn check_stmt(&mut self, cx: &LateContext<'_>, s: &hir::Stmt<'_>) { let expr = match s.kind { - hir::StmtKind::Semi(ref expr) | hir::StmtKind::Expr(ref expr) => &**expr, + hir::StmtKind::Semi(expr) | hir::StmtKind::Expr(expr) => expr, _ => return, }; match expr.kind { - hir::ExprKind::Match(ref res, _, _) if is_try(expr).is_some() => { - if let hir::ExprKind::Call(ref func, ref args) = res.kind { + hir::ExprKind::Match(res, _, _) if is_try(expr).is_some() => { + if let hir::ExprKind::Call(func, args) = res.kind { if matches!( func.kind, hir::ExprKind::Path(hir::QPath::LangItem(hir::LangItem::TryIntoResult, _)) @@ -53,7 +54,7 @@ impl<'tcx> LateLintPass<'tcx> for UnusedIoAmount { } }, - hir::ExprKind::MethodCall(ref path, _, ref args, _) => match &*path.ident.as_str() { + hir::ExprKind::MethodCall(path, _, args, _) => match &*path.ident.as_str() { "expect" | "unwrap" | "unwrap_or" | "unwrap_or_else" => { check_method_call(cx, &args[0], expr); }, @@ -66,7 +67,7 @@ impl<'tcx> LateLintPass<'tcx> for UnusedIoAmount { } fn check_method_call(cx: &LateContext<'_>, call: &hir::Expr<'_>, expr: &hir::Expr<'_>) { - if let hir::ExprKind::MethodCall(ref path, _, _, _) = call.kind { + 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); diff --git a/src/tools/clippy/clippy_lints/src/unused_self.rs b/src/tools/clippy/clippy_lints/src/unused_self.rs index 812482cf5cfb6..15343cf90f231 100644 --- a/src/tools/clippy/clippy_lints/src/unused_self.rs +++ b/src/tools/clippy/clippy_lints/src/unused_self.rs @@ -1,11 +1,10 @@ +use clippy_utils::diagnostics::span_lint_and_help; +use clippy_utils::visitors::LocalUsedVisitor; use if_chain::if_chain; use rustc_hir::{Impl, ImplItem, ImplItemKind, ItemKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; -use crate::utils::span_lint_and_help; -use crate::utils::visitors::LocalUsedVisitor; - declare_clippy_lint! { /// **What it does:** Checks methods that contain a `self` argument but don't use it /// @@ -50,21 +49,18 @@ impl<'tcx> LateLintPass<'tcx> for UnusedSelf { if assoc_item.fn_has_self_parameter; if let ImplItemKind::Fn(.., body_id) = &impl_item.kind; let body = cx.tcx.hir().body(*body_id); - if !body.params.is_empty(); + if let [self_param, ..] = body.params; + let self_hir_id = self_param.pat.hir_id; + if !LocalUsedVisitor::new(cx, self_hir_id).check_body(body); then { - let self_param = &body.params[0]; - let self_hir_id = self_param.pat.hir_id; - if !LocalUsedVisitor::new(cx, self_hir_id).check_body(body) { - span_lint_and_help( - cx, - UNUSED_SELF, - self_param.span, - "unused `self` argument", - None, - "consider refactoring to a associated function", - ); - return; - } + span_lint_and_help( + cx, + UNUSED_SELF, + self_param.span, + "unused `self` argument", + None, + "consider refactoring to a associated function", + ); } } } diff --git a/src/tools/clippy/clippy_lints/src/unused_unit.rs b/src/tools/clippy/clippy_lints/src/unused_unit.rs index a31cd5fda849e..ce2d0b3ab2f24 100644 --- a/src/tools/clippy/clippy_lints/src/unused_unit.rs +++ b/src/tools/clippy/clippy_lints/src/unused_unit.rs @@ -1,3 +1,5 @@ +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::source::position_before_rarrow; use if_chain::if_chain; use rustc_ast::ast; use rustc_ast::visit::FnKind; @@ -7,8 +9,6 @@ use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::source_map::Span; use rustc_span::BytePos; -use crate::utils::{position_before_rarrow, span_lint_and_sugg}; - declare_clippy_lint! { /// **What it does:** Checks for unit (`()`) expressions that can be removed. /// @@ -45,7 +45,7 @@ impl EarlyLintPass for UnusedUnit { fn check_block(&mut self, cx: &EarlyContext<'_>, block: &ast::Block) { if_chain! { - if let Some(ref stmt) = block.stmts.last(); + if let Some(stmt) = block.stmts.last(); if let ast::StmtKind::Expr(ref expr) = stmt.kind; if is_unit_expr(expr) && !stmt.span.from_expansion(); then { diff --git a/src/tools/clippy/clippy_lints/src/unwrap.rs b/src/tools/clippy/clippy_lints/src/unwrap.rs index 2fb0463c5a6c2..d4efee56efff5 100644 --- a/src/tools/clippy/clippy_lints/src/unwrap.rs +++ b/src/tools/clippy/clippy_lints/src/unwrap.rs @@ -1,6 +1,6 @@ -use crate::utils::{ - differing_macro_contexts, is_type_diagnostic_item, span_lint_and_then, usage::is_potentially_mutated, -}; +use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::ty::is_type_diagnostic_item; +use clippy_utils::{differing_macro_contexts, usage::is_potentially_mutated}; use if_chain::if_chain; use rustc_hir::intravisit::{walk_expr, walk_fn, FnKind, NestedVisitorMap, Visitor}; use rustc_hir::{BinOpKind, Body, Expr, ExprKind, FnDecl, HirId, Path, QPath, UnOp}; @@ -166,8 +166,8 @@ impl<'a, 'tcx> Visitor<'tcx> for UnwrappableVariablesVisitor<'a, 'tcx> { } else { // find `unwrap[_err]()` calls: if_chain! { - if let ExprKind::MethodCall(ref method_name, _, ref args, _) = expr.kind; - if let ExprKind::Path(QPath::Resolved(None, ref path)) = args[0].kind; + if let ExprKind::MethodCall(method_name, _, args, _) = expr.kind; + if let ExprKind::Path(QPath::Resolved(None, path)) = args[0].kind; if [sym::unwrap, sym!(unwrap_err)].contains(&method_name.ident.name); let call_to_unwrap = method_name.ident.name == sym::unwrap; if let Some(unwrappable) = self.unwrappables.iter() diff --git a/src/tools/clippy/clippy_lints/src/unwrap_in_result.rs b/src/tools/clippy/clippy_lints/src/unwrap_in_result.rs index 8cb7429849da6..d17aa6d842411 100644 --- a/src/tools/clippy/clippy_lints/src/unwrap_in_result.rs +++ b/src/tools/clippy/clippy_lints/src/unwrap_in_result.rs @@ -1,6 +1,10 @@ -use crate::utils::{is_type_diagnostic_item, method_chain_args, return_ty, span_lint_and_then}; +use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::ty::is_type_diagnostic_item; +use clippy_utils::{method_chain_args, return_ty}; use if_chain::if_chain; use rustc_hir as hir; +use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor}; +use rustc_hir::{Expr, ImplItemKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::hir::map::Map; use rustc_middle::ty; @@ -66,9 +70,6 @@ impl<'tcx> LateLintPass<'tcx> for UnwrapInResult { } } -use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor}; -use rustc_hir::{Expr, ImplItemKind}; - struct FindExpectUnwrap<'a, 'tcx> { lcx: &'a LateContext<'tcx>, typeck_results: &'tcx ty::TypeckResults<'tcx>, @@ -109,31 +110,27 @@ impl<'a, 'tcx> Visitor<'tcx> for FindExpectUnwrap<'a, 'tcx> { } fn lint_impl_body<'tcx>(cx: &LateContext<'tcx>, impl_span: Span, impl_item: &'tcx hir::ImplItem<'_>) { - if_chain! { - - if let ImplItemKind::Fn(_, body_id) = impl_item.kind; - then { - let body = cx.tcx.hir().body(body_id); - let mut fpu = FindExpectUnwrap { - lcx: cx, - typeck_results: cx.tcx.typeck(impl_item.def_id), - result: Vec::new(), - }; - fpu.visit_expr(&body.value); + if let ImplItemKind::Fn(_, body_id) = impl_item.kind { + let body = cx.tcx.hir().body(body_id); + let mut fpu = FindExpectUnwrap { + lcx: cx, + typeck_results: cx.tcx.typeck(impl_item.def_id), + result: Vec::new(), + }; + fpu.visit_expr(&body.value); - // if we've found one, lint - if !fpu.result.is_empty() { - span_lint_and_then( - cx, - UNWRAP_IN_RESULT, - impl_span, - "used unwrap or expect in a function that returns result or option", - move |diag| { - diag.help( - "unwrap and expect should not be used in a function that returns result or option" ); - diag.span_note(fpu.result, "potential non-recoverable error(s)"); - }); - } + // if we've found one, lint + if !fpu.result.is_empty() { + span_lint_and_then( + cx, + UNWRAP_IN_RESULT, + impl_span, + "used unwrap or expect in a function that returns result or option", + move |diag| { + diag.help("unwrap and expect should not be used in a function that returns result or option"); + diag.span_note(fpu.result, "potential non-recoverable error(s)"); + }, + ); } } } diff --git a/src/tools/clippy/clippy_lints/src/upper_case_acronyms.rs b/src/tools/clippy/clippy_lints/src/upper_case_acronyms.rs index 0470e1dbbb812..4ac2ec55b987d 100644 --- a/src/tools/clippy/clippy_lints/src/upper_case_acronyms.rs +++ b/src/tools/clippy/clippy_lints/src/upper_case_acronyms.rs @@ -1,7 +1,6 @@ -use crate::utils::span_lint_and_sugg; -use if_chain::if_chain; +use clippy_utils::diagnostics::span_lint_and_sugg; use itertools::Itertools; -use rustc_ast::ast::{Item, ItemKind, Variant}; +use rustc_ast::ast::{Item, ItemKind, VisibilityKind}; use rustc_errors::Applicability; use rustc_lint::{EarlyContext, EarlyLintPass, LintContext}; use rustc_middle::lint::in_external_macro; @@ -81,7 +80,7 @@ fn check_ident(cx: &EarlyContext<'_>, ident: &Ident, be_aggressive: bool) { // assume that two-letter words are some kind of valid abbreviation like FP for false positive // (and don't warn) if (ident.chars().all(|c| c.is_ascii_uppercase()) && ident.len() > 2) - // otherwise, warn if we have SOmeTHING lIKE THIs but only warn with the aggressive + // otherwise, warn if we have SOmeTHING lIKE THIs but only warn with the aggressive // upper-case-acronyms-aggressive config option enabled || (be_aggressive && ident != &corrected) { @@ -99,19 +98,21 @@ fn check_ident(cx: &EarlyContext<'_>, ident: &Ident, be_aggressive: bool) { impl EarlyLintPass for UpperCaseAcronyms { fn check_item(&mut self, cx: &EarlyContext<'_>, it: &Item) { - if_chain! { - if !in_external_macro(cx.sess(), it.span); + // do not lint public items or in macros + if !in_external_macro(cx.sess(), it.span) && !matches!(it.vis.kind, VisibilityKind::Public) { if matches!( it.kind, - ItemKind::TyAlias(..) | ItemKind::Enum(..) | ItemKind::Struct(..) | ItemKind::Trait(..) - ); - then { + ItemKind::TyAlias(..) | ItemKind::Struct(..) | ItemKind::Trait(..) + ) { check_ident(cx, &it.ident, self.upper_case_acronyms_aggressive); + } else if let ItemKind::Enum(ref enumdef, _) = it.kind { + // check enum variants seperately because again we only want to lint on private enums and + // the fn check_variant does not know about the vis of the enum of its variants + enumdef + .variants + .iter() + .for_each(|variant| check_ident(cx, &variant.ident, self.upper_case_acronyms_aggressive)); } } } - - fn check_variant(&mut self, cx: &EarlyContext<'_>, v: &Variant) { - check_ident(cx, &v.ident, self.upper_case_acronyms_aggressive); - } } diff --git a/src/tools/clippy/clippy_lints/src/use_self.rs b/src/tools/clippy/clippy_lints/src/use_self.rs index be7b9e9ff2dcc..c6a3c58a9a2a4 100644 --- a/src/tools/clippy/clippy_lints/src/use_self.rs +++ b/src/tools/clippy/clippy_lints/src/use_self.rs @@ -1,6 +1,7 @@ -use crate::utils::{in_macro, meets_msrv, snippet_opt, span_lint_and_sugg}; +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::source::snippet_opt; +use clippy_utils::{in_macro, meets_msrv}; use if_chain::if_chain; - use rustc_errors::Applicability; use rustc_hir as hir; use rustc_hir::def::DefKind; @@ -103,7 +104,7 @@ impl<'tcx> LateLintPass<'tcx> for UseSelf { of_trait, .. }) => { - let should_check = if let TyKind::Path(QPath::Resolved(_, ref item_path)) = hir_self_ty.kind { + let should_check = if let TyKind::Path(QPath::Resolved(_, item_path)) = hir_self_ty.kind { let parameters = &item_path.segments.last().expect(SEGMENTS_MSG).args; parameters.as_ref().map_or(true, |params| { !params.parenthesized && !params.args.iter().any(|arg| matches!(arg, GenericArg::Lifetime(_))) @@ -196,7 +197,7 @@ impl<'tcx> LateLintPass<'tcx> for UseSelf { for (impl_hir_ty, trait_sem_ty) in impl_inputs_outputs.zip(trait_method_sig.inputs_and_output) { if trait_sem_ty.walk().any(|inner| inner == self_ty.into()) { let mut visitor = SkipTyCollector::default(); - visitor.visit_ty(&impl_hir_ty); + visitor.visit_ty(impl_hir_ty); types_to_skip.extend(visitor.types_to_skip); } } @@ -262,12 +263,17 @@ impl<'tcx> LateLintPass<'tcx> for UseSelf { // FIXME: this span manipulation should not be necessary // @flip1995 found an ast lowering issue in // https://github.com/rust-lang/rust/blob/master/src/librustc_ast_lowering/path.rs#l142-l162 - match cx.tcx.hir().find(cx.tcx.hir().get_parent_node(hir_ty.hir_id)) { - Some(Node::Expr(Expr { - kind: ExprKind::Path(QPath::TypeRelative(_, segment)), - .. - })) => span_lint_until_last_segment(cx, hir_ty.span, segment), - _ => span_lint(cx, hir_ty.span), + let hir = cx.tcx.hir(); + let id = hir.get_parent_node(hir_ty.hir_id); + + if !hir.opt_span(id).map_or(false, in_macro) { + match hir.find(id) { + Some(Node::Expr(Expr { + kind: ExprKind::Path(QPath::TypeRelative(_, segment)), + .. + })) => span_lint_until_last_segment(cx, hir_ty.span, segment), + _ => span_lint(cx, hir_ty.span), + } } } } @@ -327,7 +333,7 @@ impl<'tcx> LateLintPass<'tcx> for UseSelf { // unit enum variants (`Enum::A`) ExprKind::Path(qpath) => { if expr_ty_matches(cx, expr, self_ty) { - span_lint_on_qpath_resolved(cx, &qpath, true); + span_lint_on_qpath_resolved(cx, qpath, true); } }, _ => (), diff --git a/src/tools/clippy/clippy_lints/src/useless_conversion.rs b/src/tools/clippy/clippy_lints/src/useless_conversion.rs index c533485398605..7edb280be73ea 100644 --- a/src/tools/clippy/clippy_lints/src/useless_conversion.rs +++ b/src/tools/clippy/clippy_lints/src/useless_conversion.rs @@ -1,8 +1,8 @@ -use crate::utils::sugg::Sugg; -use crate::utils::{ - get_parent_expr, is_type_diagnostic_item, match_def_path, match_trait_method, paths, snippet, - snippet_with_macro_callsite, span_lint_and_help, span_lint_and_sugg, -}; +use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_sugg}; +use clippy_utils::source::{snippet, snippet_with_macro_callsite}; +use clippy_utils::sugg::Sugg; +use clippy_utils::ty::is_type_diagnostic_item; +use clippy_utils::{get_parent_expr, match_def_path, match_trait_method, paths}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind, HirId, MatchSource}; @@ -53,17 +53,17 @@ impl<'tcx> LateLintPass<'tcx> for UselessConversion { } match e.kind { - ExprKind::Match(_, ref arms, MatchSource::TryDesugar) => { + ExprKind::Match(_, arms, MatchSource::TryDesugar) => { let e = match arms[0].body.kind { - ExprKind::Ret(Some(ref e)) | ExprKind::Break(_, Some(ref e)) => e, + ExprKind::Ret(Some(e)) | ExprKind::Break(_, Some(e)) => e, _ => return, }; - if let ExprKind::Call(_, ref args) = e.kind { + if let ExprKind::Call(_, args) = e.kind { self.try_desugar_arm.push(args[0].hir_id); } }, - ExprKind::MethodCall(ref name, .., ref args, _) => { + ExprKind::MethodCall(name, .., args, _) => { if match_trait_method(cx, e, &paths::INTO) && &*name.ident.as_str() == "into" { let a = cx.typeck_results().expr_ty(e); let b = cx.typeck_results().expr_ty(&args[0]); @@ -82,7 +82,7 @@ impl<'tcx> LateLintPass<'tcx> for UselessConversion { } if match_trait_method(cx, e, &paths::INTO_ITERATOR) && name.ident.name == sym::into_iter { if let Some(parent_expr) = get_parent_expr(cx, e) { - if let ExprKind::MethodCall(ref parent_name, ..) = parent_expr.kind { + if let ExprKind::MethodCall(parent_name, ..) = parent_expr.kind { if parent_name.ident.name != sym::into_iter { return; } @@ -103,38 +103,35 @@ impl<'tcx> LateLintPass<'tcx> for UselessConversion { ); } } - if match_trait_method(cx, e, &paths::TRY_INTO_TRAIT) && &*name.ident.as_str() == "try_into" { - if_chain! { - let a = cx.typeck_results().expr_ty(e); - let b = cx.typeck_results().expr_ty(&args[0]); - if is_type_diagnostic_item(cx, a, sym::result_type); - if let ty::Adt(_, substs) = a.kind(); - if let Some(a_type) = substs.types().next(); - if TyS::same_type(a_type, b); - - then { - span_lint_and_help( - cx, - USELESS_CONVERSION, - e.span, - &format!("useless conversion to the same type: `{}`", b), - None, - "consider removing `.try_into()`", - ); - } + if_chain! { + if match_trait_method(cx, e, &paths::TRY_INTO_TRAIT) && &*name.ident.as_str() == "try_into"; + let a = cx.typeck_results().expr_ty(e); + let b = cx.typeck_results().expr_ty(&args[0]); + if is_type_diagnostic_item(cx, a, sym::result_type); + if let ty::Adt(_, substs) = a.kind(); + if let Some(a_type) = substs.types().next(); + if TyS::same_type(a_type, b); + then { + span_lint_and_help( + cx, + USELESS_CONVERSION, + e.span, + &format!("useless conversion to the same type: `{}`", b), + None, + "consider removing `.try_into()`", + ); } } }, - ExprKind::Call(ref path, ref args) => { + ExprKind::Call(path, args) => { if_chain! { if args.len() == 1; if let ExprKind::Path(ref qpath) = path.kind; if let Some(def_id) = cx.qpath_res(qpath, path.hir_id).opt_def_id(); - let a = cx.typeck_results().expr_ty(e); - let b = cx.typeck_results().expr_ty(&args[0]); - then { + let a = cx.typeck_results().expr_ty(e); + let b = cx.typeck_results().expr_ty(&args[0]); if_chain! { if match_def_path(cx, def_id, &paths::TRY_FROM); if is_type_diagnostic_item(cx, a, sym::result_type); diff --git a/src/tools/clippy/clippy_lints/src/utils/author.rs b/src/tools/clippy/clippy_lints/src/utils/author.rs index 6e3d4fde10777..e70f8a09ebef1 100644 --- a/src/tools/clippy/clippy_lints/src/utils/author.rs +++ b/src/tools/clippy/clippy_lints/src/utils/author.rs @@ -1,8 +1,8 @@ //! A group of attributes that can be attached to Rust code in order //! to generate a clippy lint detecting said code automatically. -use crate::utils::get_attr; -use rustc_ast::ast::{Attribute, LitFloatType, LitKind}; +use clippy_utils::get_attr; +use rustc_ast::ast::{LitFloatType, LitKind}; use rustc_ast::walk_list; use rustc_data_structures::fx::FxHashMap; use rustc_hir as hir; @@ -10,7 +10,6 @@ use rustc_hir::intravisit::{NestedVisitorMap, Visitor}; use rustc_hir::{BindingAnnotation, Block, Expr, ExprKind, Pat, PatKind, QPath, Stmt, StmtKind, TyKind}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::hir::map::Map; -use rustc_session::Session; use rustc_session::{declare_lint_pass, declare_tool_lint}; declare_clippy_lint! { @@ -66,7 +65,7 @@ fn done() { impl<'tcx> LateLintPass<'tcx> for Author { fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) { - if !has_attr(cx.sess(), &item.attrs) { + if !has_attr(cx, item.hir_id()) { return; } prelude(); @@ -75,7 +74,7 @@ impl<'tcx> LateLintPass<'tcx> for Author { } fn check_impl_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::ImplItem<'_>) { - if !has_attr(cx.sess(), &item.attrs) { + if !has_attr(cx, item.hir_id()) { return; } prelude(); @@ -84,7 +83,7 @@ impl<'tcx> LateLintPass<'tcx> for Author { } fn check_trait_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::TraitItem<'_>) { - if !has_attr(cx.sess(), &item.attrs) { + if !has_attr(cx, item.hir_id()) { return; } prelude(); @@ -93,7 +92,7 @@ impl<'tcx> LateLintPass<'tcx> for Author { } fn check_variant(&mut self, cx: &LateContext<'tcx>, var: &'tcx hir::Variant<'_>) { - if !has_attr(cx.sess(), &var.attrs) { + if !has_attr(cx, var.id) { return; } prelude(); @@ -102,17 +101,17 @@ impl<'tcx> LateLintPass<'tcx> for Author { done(); } - fn check_struct_field(&mut self, cx: &LateContext<'tcx>, field: &'tcx hir::StructField<'_>) { - if !has_attr(cx.sess(), &field.attrs) { + fn check_field_def(&mut self, cx: &LateContext<'tcx>, field: &'tcx hir::FieldDef<'_>) { + if !has_attr(cx, field.hir_id) { return; } prelude(); - PrintVisitor::new("field").visit_struct_field(field); + PrintVisitor::new("field").visit_field_def(field); done(); } fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) { - if !has_attr(cx.sess(), &expr.attrs) { + if !has_attr(cx, expr.hir_id) { return; } prelude(); @@ -121,7 +120,7 @@ impl<'tcx> LateLintPass<'tcx> for Author { } fn check_arm(&mut self, cx: &LateContext<'tcx>, arm: &'tcx hir::Arm<'_>) { - if !has_attr(cx.sess(), &arm.attrs) { + if !has_attr(cx, arm.hir_id) { return; } prelude(); @@ -130,7 +129,7 @@ impl<'tcx> LateLintPass<'tcx> for Author { } fn check_stmt(&mut self, cx: &LateContext<'tcx>, stmt: &'tcx hir::Stmt<'_>) { - if !has_attr(cx.sess(), stmt.kind.attrs(|id| cx.tcx.hir().item(id))) { + if !has_attr(cx, stmt.hir_id) { return; } prelude(); @@ -139,7 +138,7 @@ impl<'tcx> LateLintPass<'tcx> for Author { } fn check_foreign_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::ForeignItem<'_>) { - if !has_attr(cx.sess(), &item.attrs) { + if !has_attr(cx, item.hir_id()) { return; } prelude(); @@ -204,13 +203,13 @@ impl<'tcx> Visitor<'tcx> for PrintVisitor { print!(" if let ExprKind::"); let current = format!("{}.kind", self.current); match expr.kind { - ExprKind::Box(ref inner) => { + ExprKind::Box(inner) => { let inner_pat = self.next("inner"); println!("Box(ref {}) = {};", inner_pat, current); self.current = inner_pat; self.visit_expr(inner); }, - ExprKind::Array(ref elements) => { + ExprKind::Array(elements) => { let elements_pat = self.next("elements"); println!("Array(ref {}) = {};", elements_pat, current); println!(" if {}.len() == {};", elements_pat, elements.len()); @@ -219,7 +218,7 @@ impl<'tcx> Visitor<'tcx> for PrintVisitor { self.visit_expr(element); } }, - ExprKind::Call(ref func, ref args) => { + ExprKind::Call(func, args) => { let func_pat = self.next("func"); let args_pat = self.next("args"); println!("Call(ref {}, ref {}) = {};", func_pat, args_pat, current); @@ -231,14 +230,14 @@ impl<'tcx> Visitor<'tcx> for PrintVisitor { self.visit_expr(arg); } }, - ExprKind::MethodCall(ref _method_name, ref _generics, ref _args, ref _fn_span) => { + ExprKind::MethodCall(_method_name, ref _generics, _args, ref _fn_span) => { println!( "MethodCall(ref method_name, ref generics, ref args, ref fn_span) = {};", current ); println!(" // unimplemented: `ExprKind::MethodCall` is not further destructured at the moment"); }, - ExprKind::Tup(ref elements) => { + ExprKind::Tup(elements) => { let elements_pat = self.next("elements"); println!("Tup(ref {}) = {};", elements_pat, current); println!(" if {}.len() == {};", elements_pat, elements.len()); @@ -247,7 +246,7 @@ impl<'tcx> Visitor<'tcx> for PrintVisitor { self.visit_expr(element); } }, - ExprKind::Binary(ref op, ref left, ref right) => { + ExprKind::Binary(ref op, left, right) => { let op_pat = self.next("op"); let left_pat = self.next("left"); let right_pat = self.next("right"); @@ -261,7 +260,7 @@ impl<'tcx> Visitor<'tcx> for PrintVisitor { self.current = right_pat; self.visit_expr(right); }, - ExprKind::Unary(ref op, ref inner) => { + ExprKind::Unary(ref op, inner) => { let inner_pat = self.next("inner"); println!("Unary(UnOp::{:?}, ref {}) = {};", op, inner_pat, current); self.current = inner_pat; @@ -297,7 +296,7 @@ impl<'tcx> Visitor<'tcx> for PrintVisitor { }, } }, - ExprKind::Cast(ref expr, ref ty) => { + ExprKind::Cast(expr, ty) => { let cast_pat = self.next("expr"); let cast_ty = self.next("cast_ty"); let qp_label = self.next("qp"); @@ -311,13 +310,13 @@ impl<'tcx> Visitor<'tcx> for PrintVisitor { self.current = cast_pat; self.visit_expr(expr); }, - ExprKind::Type(ref expr, ref _ty) => { + ExprKind::Type(expr, _ty) => { let cast_pat = self.next("expr"); println!("Type(ref {}, _) = {};", cast_pat, current); self.current = cast_pat; self.visit_expr(expr); }, - ExprKind::Loop(ref body, _, desugaring, _) => { + ExprKind::Loop(body, _, desugaring, _) => { let body_pat = self.next("body"); let des = loop_desugaring_name(desugaring); let label_pat = self.next("label"); @@ -325,10 +324,10 @@ impl<'tcx> Visitor<'tcx> for PrintVisitor { self.current = body_pat; self.visit_block(body); }, - ExprKind::If(ref cond, ref then, ref opt_else) => { + ExprKind::If(cond, then, ref opt_else) => { let cond_pat = self.next("cond"); let then_pat = self.next("then"); - if let Some(ref else_) = *opt_else { + if let Some(else_) = *opt_else { let else_pat = self.next("else_"); println!( "If(ref {}, ref {}, Some(ref {})) = {};", @@ -344,7 +343,7 @@ impl<'tcx> Visitor<'tcx> for PrintVisitor { self.current = then_pat; self.visit_expr(then); }, - ExprKind::Match(ref expr, ref arms, desugaring) => { + ExprKind::Match(expr, arms, desugaring) => { let des = desugaring_name(desugaring); let expr_pat = self.next("expr"); let arms_pat = self.next("arms"); @@ -354,18 +353,18 @@ impl<'tcx> Visitor<'tcx> for PrintVisitor { println!(" if {}.len() == {};", arms_pat, arms.len()); for (i, arm) in arms.iter().enumerate() { self.current = format!("{}[{}].body", arms_pat, i); - self.visit_expr(&arm.body); + self.visit_expr(arm.body); if let Some(ref guard) = arm.guard { let guard_pat = self.next("guard"); println!(" if let Some(ref {}) = {}[{}].guard;", guard_pat, arms_pat, i); match guard { - hir::Guard::If(ref if_expr) => { + hir::Guard::If(if_expr) => { let if_expr_pat = self.next("expr"); println!(" if let Guard::If(ref {}) = {};", if_expr_pat, guard_pat); self.current = if_expr_pat; self.visit_expr(if_expr); }, - hir::Guard::IfLet(ref if_let_pat, ref if_let_expr) => { + hir::Guard::IfLet(if_let_pat, if_let_expr) => { let if_let_pat_pat = self.next("pat"); let if_let_expr_pat = self.next("expr"); println!( @@ -380,26 +379,26 @@ impl<'tcx> Visitor<'tcx> for PrintVisitor { } } self.current = format!("{}[{}].pat", arms_pat, i); - self.visit_pat(&arm.pat); + self.visit_pat(arm.pat); } }, - ExprKind::Closure(ref _capture_clause, ref _func, _, _, _) => { + ExprKind::Closure(ref _capture_clause, _func, _, _, _) => { println!("Closure(ref capture_clause, ref func, _, _, _) = {};", current); println!(" // unimplemented: `ExprKind::Closure` is not further destructured at the moment"); }, - ExprKind::Yield(ref sub, _) => { + ExprKind::Yield(sub, _) => { let sub_pat = self.next("sub"); println!("Yield(ref sub) = {};", current); self.current = sub_pat; self.visit_expr(sub); }, - ExprKind::Block(ref block, _) => { + ExprKind::Block(block, _) => { let block_pat = self.next("block"); println!("Block(ref {}) = {};", block_pat, current); self.current = block_pat; self.visit_block(block); }, - ExprKind::Assign(ref target, ref value, _) => { + ExprKind::Assign(target, value, _) => { let target_pat = self.next("target"); let value_pat = self.next("value"); println!( @@ -411,7 +410,7 @@ impl<'tcx> Visitor<'tcx> for PrintVisitor { self.current = value_pat; self.visit_expr(value); }, - ExprKind::AssignOp(ref op, ref target, ref value) => { + ExprKind::AssignOp(ref op, target, value) => { let op_pat = self.next("op"); let target_pat = self.next("target"); let value_pat = self.next("value"); @@ -425,7 +424,7 @@ impl<'tcx> Visitor<'tcx> for PrintVisitor { self.current = value_pat; self.visit_expr(value); }, - ExprKind::Field(ref object, ref field_ident) => { + ExprKind::Field(object, ref field_ident) => { let obj_pat = self.next("object"); let field_name_pat = self.next("field_name"); println!("Field(ref {}, ref {}) = {};", obj_pat, field_name_pat, current); @@ -433,7 +432,7 @@ impl<'tcx> Visitor<'tcx> for PrintVisitor { self.current = obj_pat; self.visit_expr(object); }, - ExprKind::Index(ref object, ref index) => { + ExprKind::Index(object, index) => { let object_pat = self.next("object"); let index_pat = self.next("index"); println!("Index(ref {}, ref {}) = {};", object_pat, index_pat, current); @@ -448,7 +447,7 @@ impl<'tcx> Visitor<'tcx> for PrintVisitor { self.current = path_pat; self.print_qpath(path); }, - ExprKind::AddrOf(kind, mutability, ref inner) => { + ExprKind::AddrOf(kind, mutability, inner) => { let inner_pat = self.next("inner"); println!( "AddrOf(BorrowKind::{:?}, Mutability::{:?}, ref {}) = {};", @@ -459,7 +458,7 @@ impl<'tcx> Visitor<'tcx> for PrintVisitor { }, ExprKind::Break(ref _destination, ref opt_value) => { let destination_pat = self.next("destination"); - if let Some(ref value) = *opt_value { + if let Some(value) = *opt_value { let value_pat = self.next("value"); println!("Break(ref {}, Some(ref {})) = {};", destination_pat, value_pat, current); self.current = value_pat; @@ -475,7 +474,7 @@ impl<'tcx> Visitor<'tcx> for PrintVisitor { // FIXME: implement label printing }, ExprKind::Ret(ref opt_value) => { - if let Some(ref value) = *opt_value { + if let Some(value) = *opt_value { let value_pat = self.next("value"); println!("Ret(Some(ref {})) = {};", value_pat, current); self.current = value_pat; @@ -492,10 +491,10 @@ impl<'tcx> Visitor<'tcx> for PrintVisitor { println!("LlvmInlineAsm(_) = {};", current); println!(" // unimplemented: `ExprKind::LlvmInlineAsm` is not further destructured at the moment"); }, - ExprKind::Struct(ref path, ref fields, ref opt_base) => { + ExprKind::Struct(path, fields, ref opt_base) => { let path_pat = self.next("path"); let fields_pat = self.next("fields"); - if let Some(ref base) = *opt_base { + if let Some(base) = *opt_base { let base_pat = self.next("base"); println!( "Struct(ref {}, ref {}, Some(ref {})) = {};", @@ -517,7 +516,7 @@ impl<'tcx> Visitor<'tcx> for PrintVisitor { self.current = value_pat; }, // FIXME: compute length (needs type info) - ExprKind::Repeat(ref value, _) => { + ExprKind::Repeat(value, _) => { let value_pat = self.next("value"); println!("Repeat(ref {}, _) = {};", value_pat, current); println!("// unimplemented: repeat count check"); @@ -527,7 +526,7 @@ impl<'tcx> Visitor<'tcx> for PrintVisitor { ExprKind::Err => { println!("Err = {}", current); }, - ExprKind::DropTemps(ref expr) => { + ExprKind::DropTemps(expr) => { let expr_pat = self.next("expr"); println!("DropTemps(ref {}) = {};", expr_pat, current); self.current = expr_pat; @@ -561,7 +560,7 @@ impl<'tcx> Visitor<'tcx> for PrintVisitor { BindingAnnotation::RefMut => "BindingAnnotation::RefMut", }; let name_pat = self.next("name"); - if let Some(ref sub) = *sub { + if let Some(sub) = *sub { let sub_pat = self.next("sub"); println!( "Binding({}, _, {}, Some(ref {})) = {};", @@ -574,7 +573,7 @@ impl<'tcx> Visitor<'tcx> for PrintVisitor { } println!(" if {}.as_str() == \"{}\";", name_pat, ident.as_str()); }, - PatKind::Struct(ref path, ref fields, ignore) => { + PatKind::Struct(ref path, fields, ignore) => { let path_pat = self.next("path"); let fields_pat = self.next("fields"); println!( @@ -586,13 +585,13 @@ impl<'tcx> Visitor<'tcx> for PrintVisitor { println!(" if {}.len() == {};", fields_pat, fields.len()); println!(" // unimplemented: field checks"); }, - PatKind::Or(ref fields) => { + PatKind::Or(fields) => { let fields_pat = self.next("fields"); println!("Or(ref {}) = {};", fields_pat, current); println!(" if {}.len() == {};", fields_pat, fields.len()); println!(" // unimplemented: field checks"); }, - PatKind::TupleStruct(ref path, ref fields, skip_pos) => { + PatKind::TupleStruct(ref path, fields, skip_pos) => { let path_pat = self.next("path"); let fields_pat = self.next("fields"); println!( @@ -610,25 +609,25 @@ impl<'tcx> Visitor<'tcx> for PrintVisitor { self.current = path_pat; self.print_qpath(path); }, - PatKind::Tuple(ref fields, skip_pos) => { + PatKind::Tuple(fields, skip_pos) => { let fields_pat = self.next("fields"); println!("Tuple(ref {}, {:?}) = {};", fields_pat, skip_pos, current); println!(" if {}.len() == {};", fields_pat, fields.len()); println!(" // unimplemented: field checks"); }, - PatKind::Box(ref pat) => { + PatKind::Box(pat) => { let pat_pat = self.next("pat"); println!("Box(ref {}) = {};", pat_pat, current); self.current = pat_pat; self.visit_pat(pat); }, - PatKind::Ref(ref pat, muta) => { + PatKind::Ref(pat, muta) => { let pat_pat = self.next("pat"); println!("Ref(ref {}, Mutability::{:?}) = {};", pat_pat, muta, current); self.current = pat_pat; self.visit_pat(pat); }, - PatKind::Lit(ref lit_expr) => { + PatKind::Lit(lit_expr) => { let lit_expr_pat = self.next("lit_expr"); println!("Lit(ref {}) = {}", lit_expr_pat, current); self.current = lit_expr_pat; @@ -646,10 +645,10 @@ impl<'tcx> Visitor<'tcx> for PrintVisitor { self.current = end_pat; walk_list!(self, visit_expr, end); }, - PatKind::Slice(ref start, ref middle, ref end) => { + PatKind::Slice(start, ref middle, end) => { let start_pat = self.next("start"); let end_pat = self.next("end"); - if let Some(ref middle) = middle { + if let Some(middle) = middle { let middle_pat = self.next("middle"); println!( "Slice(ref {}, Some(ref {}), ref {}) = {};", @@ -679,17 +678,17 @@ impl<'tcx> Visitor<'tcx> for PrintVisitor { let current = format!("{}.kind", self.current); match s.kind { // A local (let) binding: - StmtKind::Local(ref local) => { + StmtKind::Local(local) => { let local_pat = self.next("local"); println!("Local(ref {}) = {};", local_pat, current); - if let Some(ref init) = local.init { + if let Some(init) = local.init { let init_pat = self.next("init"); println!(" if let Some(ref {}) = {}.init;", init_pat, local_pat); self.current = init_pat; self.visit_expr(init); } self.current = format!("{}.pat", local_pat); - self.visit_pat(&local.pat); + self.visit_pat(local.pat); }, // An item binding: StmtKind::Item(_) => { @@ -697,7 +696,7 @@ impl<'tcx> Visitor<'tcx> for PrintVisitor { }, // Expr without trailing semi-colon (must have unit type): - StmtKind::Expr(ref e) => { + StmtKind::Expr(e) => { let e_pat = self.next("e"); println!("Expr(ref {}, _) = {}", e_pat, current); self.current = e_pat; @@ -705,7 +704,7 @@ impl<'tcx> Visitor<'tcx> for PrintVisitor { }, // Expr with trailing semi-colon (may have any type): - StmtKind::Semi(ref e) => { + StmtKind::Semi(e) => { let e_pat = self.next("e"); println!("Semi(ref {}, _) = {}", e_pat, current); self.current = e_pat; @@ -719,8 +718,9 @@ impl<'tcx> Visitor<'tcx> for PrintVisitor { } } -fn has_attr(sess: &Session, attrs: &[Attribute]) -> bool { - get_attr(sess, attrs, "author").count() > 0 +fn has_attr(cx: &LateContext<'_>, hir_id: hir::HirId) -> bool { + let attrs = cx.tcx.hir().attrs(hir_id); + get_attr(cx.sess(), attrs, "author").count() > 0 } #[must_use] @@ -752,7 +752,7 @@ fn loop_desugaring_name(des: hir::LoopSource) -> &'static str { fn print_path(path: &QPath<'_>, first: &mut bool) { match *path { - QPath::Resolved(_, ref path) => { + QPath::Resolved(_, path) => { for segment in path.segments { if *first { *first = false; @@ -762,7 +762,7 @@ fn print_path(path: &QPath<'_>, first: &mut bool) { print!("{:?}", segment.ident.as_str()); } }, - QPath::TypeRelative(ref ty, ref segment) => match ty.kind { + QPath::TypeRelative(ty, segment) => match ty.kind { hir::TyKind::Path(ref inner_path) => { print_path(inner_path, first); if *first { diff --git a/src/tools/clippy/clippy_lints/src/utils/conf.rs b/src/tools/clippy/clippy_lints/src/utils/conf.rs index 9139a0966c557..147f823491d6d 100644 --- a/src/tools/clippy/clippy_lints/src/utils/conf.rs +++ b/src/tools/clippy/clippy_lints/src/utils/conf.rs @@ -106,7 +106,7 @@ macro_rules! define_Conf { pub use self::helpers::Conf; define_Conf! { - /// Lint: REDUNDANT_FIELD_NAMES, REDUNDANT_STATIC_LIFETIMES, FILTER_MAP_NEXT, CHECKED_CONVERSIONS, MANUAL_RANGE_CONTAINS, USE_SELF, MEM_REPLACE_WITH_DEFAULT, MANUAL_NON_EXHAUSTIVE, OPTION_AS_REF_DEREF, MAP_UNWRAP_OR, MATCH_LIKE_MATCHES_MACRO, MANUAL_STRIP, MISSING_CONST_FOR_FN. The minimum rust version that the project supports + /// Lint: REDUNDANT_FIELD_NAMES, REDUNDANT_STATIC_LIFETIMES, FILTER_MAP_NEXT, CHECKED_CONVERSIONS, MANUAL_RANGE_CONTAINS, USE_SELF, MEM_REPLACE_WITH_DEFAULT, MANUAL_NON_EXHAUSTIVE, OPTION_AS_REF_DEREF, MAP_UNWRAP_OR, MATCH_LIKE_MATCHES_MACRO, MANUAL_STRIP, MISSING_CONST_FOR_FN, UNNESTED_OR_PATTERNS, FROM_OVER_INTO, NEEDLESS_QUESTION_MARK, PTR_AS_PTR. The minimum rust version that the project supports (msrv, "msrv": Option<String>, None), /// Lint: BLACKLISTED_NAME. The list of blacklisted names to lint about. NB: `bar` is not here since it has legitimate uses (blacklisted_names, "blacklisted_names": Vec<String>, ["foo", "baz", "quux"].iter().map(ToString::to_string).collect()), diff --git a/src/tools/clippy/clippy_lints/src/utils/inspector.rs b/src/tools/clippy/clippy_lints/src/utils/inspector.rs index 9c1d98cd70745..4665eeeff7b21 100644 --- a/src/tools/clippy/clippy_lints/src/utils/inspector.rs +++ b/src/tools/clippy/clippy_lints/src/utils/inspector.rs @@ -1,6 +1,6 @@ //! checks for attributes -use crate::utils::get_attr; +use clippy_utils::get_attr; use rustc_ast::ast::{Attribute, InlineAsmTemplatePiece}; use rustc_hir as hir; use rustc_lint::{LateContext, LateLintPass, LintContext}; @@ -33,21 +33,21 @@ declare_lint_pass!(DeepCodeInspector => [DEEP_CODE_INSPECTION]); impl<'tcx> LateLintPass<'tcx> for DeepCodeInspector { fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) { - if !has_attr(cx.sess(), &item.attrs) { + if !has_attr(cx.sess(), cx.tcx.hir().attrs(item.hir_id())) { return; } print_item(cx, item); } fn check_impl_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::ImplItem<'_>) { - if !has_attr(cx.sess(), &item.attrs) { + if !has_attr(cx.sess(), cx.tcx.hir().attrs(item.hir_id())) { return; } println!("impl item `{}`", item.ident.name); match item.vis.node { hir::VisibilityKind::Public => println!("public"), hir::VisibilityKind::Crate(_) => println!("visible crate wide"), - hir::VisibilityKind::Restricted { ref path, .. } => println!( + hir::VisibilityKind::Restricted { path, .. } => println!( "visible in module `{}`", rustc_hir_pretty::to_string(rustc_hir_pretty::NO_ANN, |s| s.print_path(path, false)) ), @@ -80,8 +80,8 @@ impl<'tcx> LateLintPass<'tcx> for DeepCodeInspector { // } // } // - // fn check_struct_field(&mut self, cx: &LateContext<'tcx>, field: &'tcx - // hir::StructField) { + // fn check_field_def(&mut self, cx: &LateContext<'tcx>, field: &'tcx + // hir::FieldDef) { // if !has_attr(&field.attrs) { // return; // } @@ -89,41 +89,41 @@ impl<'tcx> LateLintPass<'tcx> for DeepCodeInspector { // fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) { - if !has_attr(cx.sess(), &expr.attrs) { + if !has_attr(cx.sess(), cx.tcx.hir().attrs(expr.hir_id)) { return; } print_expr(cx, expr, 0); } fn check_arm(&mut self, cx: &LateContext<'tcx>, arm: &'tcx hir::Arm<'_>) { - if !has_attr(cx.sess(), &arm.attrs) { + if !has_attr(cx.sess(), cx.tcx.hir().attrs(arm.hir_id)) { return; } - print_pat(cx, &arm.pat, 1); + print_pat(cx, arm.pat, 1); if let Some(ref guard) = arm.guard { println!("guard:"); print_guard(cx, guard, 1); } println!("body:"); - print_expr(cx, &arm.body, 1); + print_expr(cx, arm.body, 1); } fn check_stmt(&mut self, cx: &LateContext<'tcx>, stmt: &'tcx hir::Stmt<'_>) { - if !has_attr(cx.sess(), stmt.kind.attrs(|id| cx.tcx.hir().item(id))) { + if !has_attr(cx.sess(), cx.tcx.hir().attrs(stmt.hir_id)) { return; } match stmt.kind { - hir::StmtKind::Local(ref local) => { + hir::StmtKind::Local(local) => { println!("local variable of type {}", cx.typeck_results().node_type(local.hir_id)); println!("pattern:"); - print_pat(cx, &local.pat, 0); - if let Some(ref e) = local.init { + print_pat(cx, local.pat, 0); + if let Some(e) = local.init { println!("init expression:"); print_expr(cx, e, 0); } }, hir::StmtKind::Item(_) => println!("item decl"), - hir::StmtKind::Expr(ref e) | hir::StmtKind::Semi(ref e) => print_expr(cx, e, 0), + hir::StmtKind::Expr(e) | hir::StmtKind::Semi(e) => print_expr(cx, e, 0), } } // fn check_foreign_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx @@ -151,7 +151,7 @@ fn print_expr(cx: &LateContext<'_>, expr: &hir::Expr<'_>, indent: usize) { cx.typeck_results().adjustments().get(expr.hir_id) ); match expr.kind { - hir::ExprKind::Box(ref e) => { + hir::ExprKind::Box(e) => { println!("{}Box", ind); print_expr(cx, e, indent + 1); }, @@ -161,7 +161,7 @@ fn print_expr(cx: &LateContext<'_>, expr: &hir::Expr<'_>, indent: usize) { print_expr(cx, e, indent + 1); } }, - hir::ExprKind::Call(ref func, args) => { + hir::ExprKind::Call(func, args) => { println!("{}Call", ind); println!("{}function:", ind); print_expr(cx, func, indent + 1); @@ -170,7 +170,7 @@ fn print_expr(cx: &LateContext<'_>, expr: &hir::Expr<'_>, indent: usize) { print_expr(cx, arg, indent + 1); } }, - hir::ExprKind::MethodCall(ref path, _, args, _) => { + hir::ExprKind::MethodCall(path, _, args, _) => { println!("{}MethodCall", ind); println!("{}method name: {}", ind, path.ident.name); for arg in args { @@ -183,7 +183,7 @@ fn print_expr(cx: &LateContext<'_>, expr: &hir::Expr<'_>, indent: usize) { print_expr(cx, e, indent + 1); } }, - hir::ExprKind::Binary(op, ref lhs, ref rhs) => { + hir::ExprKind::Binary(op, lhs, rhs) => { println!("{}Binary", ind); println!("{}op: {:?}", ind, op.node); println!("{}lhs:", ind); @@ -191,7 +191,7 @@ fn print_expr(cx: &LateContext<'_>, expr: &hir::Expr<'_>, indent: usize) { println!("{}rhs:", ind); print_expr(cx, rhs, indent + 1); }, - hir::ExprKind::Unary(op, ref inner) => { + hir::ExprKind::Unary(op, inner) => { println!("{}Unary", ind); println!("{}op: {:?}", ind, op); print_expr(cx, inner, indent + 1); @@ -200,12 +200,12 @@ fn print_expr(cx: &LateContext<'_>, expr: &hir::Expr<'_>, indent: usize) { println!("{}Lit", ind); println!("{}{:?}", ind, lit); }, - hir::ExprKind::Cast(ref e, ref target) => { + hir::ExprKind::Cast(e, target) => { println!("{}Cast", ind); print_expr(cx, e, indent + 1); println!("{}target type: {:?}", ind, target); }, - hir::ExprKind::Type(ref e, ref target) => { + hir::ExprKind::Type(e, target) => { println!("{}Type", ind); print_expr(cx, e, indent + 1); println!("{}target type: {:?}", ind, target); @@ -213,16 +213,16 @@ fn print_expr(cx: &LateContext<'_>, expr: &hir::Expr<'_>, indent: usize) { hir::ExprKind::Loop(..) => { println!("{}Loop", ind); }, - hir::ExprKind::If(ref cond, _, ref else_opt) => { + hir::ExprKind::If(cond, _, ref else_opt) => { println!("{}If", ind); println!("{}condition:", ind); print_expr(cx, cond, indent + 1); - if let Some(ref els) = *else_opt { + if let Some(els) = *else_opt { println!("{}else:", ind); print_expr(cx, els, indent + 1); } }, - hir::ExprKind::Match(ref cond, _, ref source) => { + hir::ExprKind::Match(cond, _, ref source) => { println!("{}Match", ind); println!("{}condition:", ind); print_expr(cx, cond, indent + 1); @@ -232,21 +232,21 @@ fn print_expr(cx: &LateContext<'_>, expr: &hir::Expr<'_>, indent: usize) { println!("{}Closure", ind); println!("{}clause: {:?}", ind, clause); }, - hir::ExprKind::Yield(ref sub, _) => { + hir::ExprKind::Yield(sub, _) => { println!("{}Yield", ind); print_expr(cx, sub, indent + 1); }, hir::ExprKind::Block(_, _) => { println!("{}Block", ind); }, - hir::ExprKind::Assign(ref lhs, ref rhs, _) => { + hir::ExprKind::Assign(lhs, rhs, _) => { println!("{}Assign", ind); println!("{}lhs:", ind); print_expr(cx, lhs, indent + 1); println!("{}rhs:", ind); print_expr(cx, rhs, indent + 1); }, - hir::ExprKind::AssignOp(ref binop, ref lhs, ref rhs) => { + hir::ExprKind::AssignOp(ref binop, lhs, rhs) => { println!("{}AssignOp", ind); println!("{}op: {:?}", ind, binop.node); println!("{}lhs:", ind); @@ -254,31 +254,31 @@ fn print_expr(cx: &LateContext<'_>, expr: &hir::Expr<'_>, indent: usize) { println!("{}rhs:", ind); print_expr(cx, rhs, indent + 1); }, - hir::ExprKind::Field(ref e, ident) => { + hir::ExprKind::Field(e, ident) => { println!("{}Field", ind); println!("{}field name: {}", ind, ident.name); println!("{}struct expr:", ind); print_expr(cx, e, indent + 1); }, - hir::ExprKind::Index(ref arr, ref idx) => { + hir::ExprKind::Index(arr, idx) => { println!("{}Index", ind); println!("{}array expr:", ind); print_expr(cx, arr, indent + 1); println!("{}index expr:", ind); print_expr(cx, idx, indent + 1); }, - hir::ExprKind::Path(hir::QPath::Resolved(ref ty, ref path)) => { + hir::ExprKind::Path(hir::QPath::Resolved(ref ty, path)) => { println!("{}Resolved Path, {:?}", ind, ty); println!("{}path: {:?}", ind, path); }, - hir::ExprKind::Path(hir::QPath::TypeRelative(ref ty, ref seg)) => { + hir::ExprKind::Path(hir::QPath::TypeRelative(ty, seg)) => { println!("{}Relative Path, {:?}", ind, ty); println!("{}seg: {:?}", ind, seg); }, hir::ExprKind::Path(hir::QPath::LangItem(lang_item, ..)) => { println!("{}Lang Item Path, {:?}", ind, lang_item.name()); }, - hir::ExprKind::AddrOf(kind, ref muta, ref e) => { + hir::ExprKind::AddrOf(kind, ref muta, e) => { println!("{}AddrOf", ind); println!("kind: {:?}", kind); println!("mutability: {:?}", muta); @@ -286,18 +286,18 @@ fn print_expr(cx: &LateContext<'_>, expr: &hir::Expr<'_>, indent: usize) { }, hir::ExprKind::Break(_, ref e) => { println!("{}Break", ind); - if let Some(ref e) = *e { + if let Some(e) = *e { print_expr(cx, e, indent + 1); } }, hir::ExprKind::Continue(_) => println!("{}Again", ind), hir::ExprKind::Ret(ref e) => { println!("{}Ret", ind); - if let Some(ref e) = *e { + if let Some(e) = *e { print_expr(cx, e, indent + 1); } }, - hir::ExprKind::InlineAsm(ref asm) => { + hir::ExprKind::InlineAsm(asm) => { println!("{}InlineAsm", ind); println!("{}template: {}", ind, InlineAsmTemplatePiece::to_string(asm.template)); println!("{}options: {:?}", ind, asm.options); @@ -306,7 +306,6 @@ fn print_expr(cx: &LateContext<'_>, expr: &hir::Expr<'_>, indent: usize) { match op { hir::InlineAsmOperand::In { expr, .. } | hir::InlineAsmOperand::InOut { expr, .. } - | hir::InlineAsmOperand::Const { expr } | hir::InlineAsmOperand::Sym { expr } => print_expr(cx, expr, indent + 1), hir::InlineAsmOperand::Out { expr, .. } => { if let Some(expr) = expr { @@ -319,10 +318,14 @@ fn print_expr(cx: &LateContext<'_>, expr: &hir::Expr<'_>, indent: usize) { print_expr(cx, out_expr, indent + 1); } }, + hir::InlineAsmOperand::Const { anon_const } => { + println!("{}anon_const:", ind); + print_expr(cx, &cx.tcx.hir().body(anon_const.body).value, indent + 1); + }, } } }, - hir::ExprKind::LlvmInlineAsm(ref asm) => { + hir::ExprKind::LlvmInlineAsm(asm) => { let inputs = &asm.inputs_exprs; let outputs = &asm.outputs_exprs; println!("{}LlvmInlineAsm", ind); @@ -335,14 +338,14 @@ fn print_expr(cx: &LateContext<'_>, expr: &hir::Expr<'_>, indent: usize) { print_expr(cx, e, indent + 1); } }, - hir::ExprKind::Struct(ref path, fields, ref base) => { + hir::ExprKind::Struct(path, fields, ref base) => { println!("{}Struct", ind); println!("{}path: {:?}", ind, path); for field in fields { println!("{}field \"{}\":", ind, field.ident.name); - print_expr(cx, &field.expr, indent + 1); + print_expr(cx, field.expr, indent + 1); } - if let Some(ref base) = *base { + if let Some(base) = *base { println!("{}base:", ind); print_expr(cx, base, indent + 1); } @@ -352,7 +355,7 @@ fn print_expr(cx: &LateContext<'_>, expr: &hir::Expr<'_>, indent: usize) { println!("{}anon_const:", ind); print_expr(cx, &cx.tcx.hir().body(anon_const.body).value, indent + 1); }, - hir::ExprKind::Repeat(ref val, ref anon_const) => { + hir::ExprKind::Repeat(val, ref anon_const) => { println!("{}Repeat", ind); println!("{}value:", ind); print_expr(cx, val, indent + 1); @@ -362,7 +365,7 @@ fn print_expr(cx: &LateContext<'_>, expr: &hir::Expr<'_>, indent: usize) { hir::ExprKind::Err => { println!("{}Err", ind); }, - hir::ExprKind::DropTemps(ref e) => { + hir::ExprKind::DropTemps(e) => { println!("{}DropTemps", ind); print_expr(cx, e, indent + 1); }, @@ -375,7 +378,7 @@ fn print_item(cx: &LateContext<'_>, item: &hir::Item<'_>) { match item.vis.node { hir::VisibilityKind::Public => println!("public"), hir::VisibilityKind::Crate(_) => println!("visible crate wide"), - hir::VisibilityKind::Restricted { ref path, .. } => println!( + hir::VisibilityKind::Restricted { path, .. } => println!( "visible in module `{}`", rustc_hir_pretty::to_string(rustc_hir_pretty::NO_ANN, |s| s.print_path(path, false)) ), @@ -395,7 +398,7 @@ fn print_item(cx: &LateContext<'_>, item: &hir::Item<'_>) { println!("weird extern crate without a crate id"); } }, - hir::ItemKind::Use(ref path, ref kind) => println!("{:?}, {:?}", path, kind), + hir::ItemKind::Use(path, ref kind) => println!("{:?}, {:?}", path, kind), hir::ItemKind::Static(..) => println!("static item of type {:#?}", cx.tcx.type_of(did)), hir::ItemKind::Const(..) => println!("const item of type {:#?}", cx.tcx.type_of(did)), hir::ItemKind::Fn(..) => { @@ -404,7 +407,7 @@ fn print_item(cx: &LateContext<'_>, item: &hir::Item<'_>) { }, hir::ItemKind::Mod(..) => println!("module"), hir::ItemKind::ForeignMod { abi, .. } => println!("foreign module with abi: {}", abi), - hir::ItemKind::GlobalAsm(ref asm) => println!("global asm: {:?}", asm), + hir::ItemKind::GlobalAsm(asm) => println!("global asm: {:?}", asm), hir::ItemKind::TyAlias(..) => { println!("type alias for {:?}", cx.tcx.type_of(did)); }, @@ -454,7 +457,7 @@ fn print_pat(cx: &LateContext<'_>, pat: &hir::Pat<'_>, indent: usize) { println!("{}Binding", ind); println!("{}mode: {:?}", ind, mode); println!("{}name: {}", ind, ident.name); - if let Some(ref inner) = *inner { + if let Some(inner) = *inner { println!("{}inner:", ind); print_pat(cx, inner, indent + 1); } @@ -479,7 +482,7 @@ fn print_pat(cx: &LateContext<'_>, pat: &hir::Pat<'_>, indent: usize) { if field.is_shorthand { println!("{} in shorthand notation", ind); } - print_pat(cx, &field.pat, indent + 1); + print_pat(cx, field.pat, indent + 1); } }, hir::PatKind::TupleStruct(ref path, fields, opt_dots_position) => { @@ -496,11 +499,11 @@ fn print_pat(cx: &LateContext<'_>, pat: &hir::Pat<'_>, indent: usize) { print_pat(cx, field, indent + 1); } }, - hir::PatKind::Path(hir::QPath::Resolved(ref ty, ref path)) => { + hir::PatKind::Path(hir::QPath::Resolved(ref ty, path)) => { println!("{}Resolved Path, {:?}", ind, ty); println!("{}path: {:?}", ind, path); }, - hir::PatKind::Path(hir::QPath::TypeRelative(ref ty, ref seg)) => { + hir::PatKind::Path(hir::QPath::TypeRelative(ty, seg)) => { println!("{}Relative Path, {:?}", ind, ty); println!("{}seg: {:?}", ind, seg); }, @@ -516,16 +519,16 @@ fn print_pat(cx: &LateContext<'_>, pat: &hir::Pat<'_>, indent: usize) { print_pat(cx, field, indent + 1); } }, - hir::PatKind::Box(ref inner) => { + hir::PatKind::Box(inner) => { println!("{}Box", ind); print_pat(cx, inner, indent + 1); }, - hir::PatKind::Ref(ref inner, ref muta) => { + hir::PatKind::Ref(inner, ref muta) => { println!("{}Ref", ind); println!("{}mutability: {:?}", ind, muta); print_pat(cx, inner, indent + 1); }, - hir::PatKind::Lit(ref e) => { + hir::PatKind::Lit(e) => { println!("{}Lit", ind); print_expr(cx, e, indent + 1); }, @@ -549,7 +552,7 @@ fn print_pat(cx: &LateContext<'_>, pat: &hir::Pat<'_>, indent: usize) { print_pat(cx, pat, indent + 1); } println!("i:"); - if let Some(ref pat) = *range { + if let Some(pat) = *range { print_pat(cx, pat, indent + 1); } println!("[y, z]:"); 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 0a347516c3ad2..cf8039d6059b6 100644 --- a/src/tools/clippy/clippy_lints/src/utils/internal_lints.rs +++ b/src/tools/clippy/clippy_lints/src/utils/internal_lints.rs @@ -1,7 +1,9 @@ use crate::consts::{constant_simple, Constant}; -use crate::utils::{ - is_expn_of, match_def_path, match_qpath, match_type, method_calls, path_to_res, paths, run_lints, snippet, - span_lint, span_lint_and_help, span_lint_and_sugg, SpanlessEq, +use clippy_utils::diagnostics::{span_lint, span_lint_and_help, span_lint_and_sugg, span_lint_and_then}; +use clippy_utils::source::snippet; +use clippy_utils::ty::match_type; +use clippy_utils::{ + is_else_clause, is_expn_of, match_def_path, match_qpath, method_calls, path_to_res, paths, run_lints, SpanlessEq, }; use if_chain::if_chain; use rustc_ast::ast::{Crate as AstCrate, ItemKind, LitKind, ModKind, NodeId}; @@ -14,15 +16,17 @@ use rustc_hir::def_id::DefId; use rustc_hir::hir_id::CRATE_HIR_ID; use rustc_hir::intravisit::{NestedVisitorMap, Visitor}; use rustc_hir::{ - BinOpKind, Crate, Expr, ExprKind, HirId, Item, MutTy, Mutability, Node, Path, StmtKind, Ty, TyKind, UnOp, + BinOpKind, Block, Crate, Expr, ExprKind, HirId, Item, Local, MatchSource, MutTy, Mutability, Node, Path, Stmt, + StmtKind, Ty, TyKind, UnOp, }; -use rustc_lint::{EarlyContext, EarlyLintPass, LateContext, LateLintPass}; +use rustc_lint::{EarlyContext, EarlyLintPass, LateContext, LateLintPass, LintContext}; use rustc_middle::hir::map::Map; use rustc_middle::mir::interpret::ConstValue; use rustc_middle::ty; use rustc_session::{declare_lint_pass, declare_tool_lint, impl_lint_pass}; -use rustc_span::source_map::{Span, Spanned}; +use rustc_span::source_map::Spanned; use rustc_span::symbol::{Symbol, SymbolStr}; +use rustc_span::{BytePos, Span}; use rustc_typeck::hir_ty_to_ty; use std::borrow::{Borrow, Cow}; @@ -297,6 +301,13 @@ declare_clippy_lint! { "unnecessary conversion between Symbol and string" } +declare_clippy_lint! { + /// Finds unidiomatic usage of `if_chain!` + pub IF_CHAIN_STYLE, + internal, + "non-idiomatic `if_chain!` usage" +} + declare_lint_pass!(ClippyLintsInternal => [CLIPPY_LINTS_INTERNAL]); impl EarlyLintPass for ClippyLintsInternal { @@ -342,12 +353,12 @@ impl<'tcx> LateLintPass<'tcx> for LintWithoutLintPass { return; } - if let hir::ItemKind::Static(ref ty, Mutability::Not, body_id) = item.kind { + if let hir::ItemKind::Static(ty, Mutability::Not, body_id) = item.kind { if is_lint_ref_type(cx, ty) { let expr = &cx.tcx.hir().body(body_id).value; if_chain! { - if let ExprKind::AddrOf(_, _, ref inner_exp) = expr.kind; - if let ExprKind::Struct(_, ref fields, _) = inner_exp.kind; + if let ExprKind::AddrOf(_, _, inner_exp) = expr.kind; + if let ExprKind::Struct(_, fields, _) = inner_exp.kind; let field = fields .iter() .find(|f| f.ident.as_str() == "desc") @@ -374,7 +385,7 @@ impl<'tcx> LateLintPass<'tcx> for LintWithoutLintPass { { if let hir::ItemKind::Impl(hir::Impl { of_trait: None, - items: ref impl_item_refs, + items: impl_item_refs, .. }) = item.kind { @@ -426,7 +437,7 @@ fn is_lint_ref_type<'tcx>(cx: &LateContext<'tcx>, ty: &Ty<'_>) -> bool { if let TyKind::Rptr( _, MutTy { - ty: ref inner, + ty: inner, mutbl: Mutability::Not, }, ) = ty.kind @@ -487,7 +498,7 @@ impl<'tcx> LateLintPass<'tcx> for CompilerLintFunctions { } if_chain! { - if let ExprKind::MethodCall(ref path, _, ref args, _) = expr.kind; + if let ExprKind::MethodCall(path, _, args, _) = expr.kind; let fn_name = path.ident; if let Some(sugg) = self.map.get(&*fn_name.as_str()); let ty = cx.typeck_results().expr_ty(&args[0]).peel_refs(); @@ -566,7 +577,7 @@ impl<'tcx> LateLintPass<'tcx> for CollapsibleCalls { } if_chain! { - if let ExprKind::Call(ref func, ref and_then_args) = expr.kind; + if let ExprKind::Call(func, and_then_args) = expr.kind; if let ExprKind::Path(ref path) = func.kind; if match_qpath(path, &["span_lint_and_then"]); if and_then_args.len() == 5; @@ -576,10 +587,10 @@ impl<'tcx> LateLintPass<'tcx> for CollapsibleCalls { let stmts = &block.stmts; if stmts.len() == 1 && block.expr.is_none(); if let StmtKind::Semi(only_expr) = &stmts[0].kind; - if let ExprKind::MethodCall(ref ps, _, ref span_call_args, _) = &only_expr.kind; - let and_then_snippets = get_and_then_snippets(cx, and_then_args); - let mut sle = SpanlessEq::new(cx).deny_side_effects(); + if let ExprKind::MethodCall(ps, _, span_call_args, _) = &only_expr.kind; then { + let and_then_snippets = get_and_then_snippets(cx, and_then_args); + let mut sle = SpanlessEq::new(cx).deny_side_effects(); match &*ps.ident.as_str() { "span_suggestion" if sle.eq_expr(&and_then_args[2], &span_call_args[1]) => { suggest_suggestion(cx, expr, &and_then_snippets, &span_suggestion_snippets(cx, span_call_args)); @@ -751,7 +762,7 @@ impl<'tcx> LateLintPass<'tcx> for MatchTypeOnDiagItem { // Check if this is a call to utils::match_type() if let ExprKind::Call(fn_path, [context, ty, ty_path]) = expr.kind; if let ExprKind::Path(fn_qpath) = &fn_path.kind; - if match_qpath(&fn_qpath, &["utils", "match_type"]); + if match_qpath(fn_qpath, &["utils", "match_type"]); // Extract the path to the matched type if let Some(segments) = path_to_matched_type(cx, ty_path); let segments: Vec<&str> = segments.iter().map(|sym| &**sym).collect(); @@ -1063,3 +1074,159 @@ impl<'tcx> SymbolStrExpr<'tcx> { } } } + +declare_lint_pass!(IfChainStyle => [IF_CHAIN_STYLE]); + +impl<'tcx> LateLintPass<'tcx> for IfChainStyle { + fn check_block(&mut self, cx: &LateContext<'tcx>, block: &'tcx hir::Block<'_>) { + let (local, after, if_chain_span) = if_chain! { + if let [Stmt { kind: StmtKind::Local(local), .. }, after @ ..] = block.stmts; + if let Some(if_chain_span) = is_expn_of(block.span, "if_chain"); + then { (local, after, if_chain_span) } else { return } + }; + if is_first_if_chain_expr(cx, block.hir_id, if_chain_span) { + span_lint( + cx, + IF_CHAIN_STYLE, + if_chain_local_span(cx, local, if_chain_span), + "`let` expression should be above the `if_chain!`", + ); + } else if local.span.ctxt() == block.span.ctxt() && is_if_chain_then(after, block.expr, if_chain_span) { + span_lint( + cx, + IF_CHAIN_STYLE, + if_chain_local_span(cx, local, if_chain_span), + "`let` expression should be inside `then { .. }`", + ) + } + } + + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) { + let (cond, then, els) = match expr.kind { + ExprKind::If(cond, then, els) => (Some(cond), then, els.is_some()), + ExprKind::Match( + _, + [arm, ..], + MatchSource::IfLetDesugar { + contains_else_clause: els, + }, + ) => (None, arm.body, els), + _ => return, + }; + let then_block = match then.kind { + ExprKind::Block(block, _) => block, + _ => return, + }; + let if_chain_span = is_expn_of(expr.span, "if_chain"); + if !els { + check_nested_if_chains(cx, expr, then_block, if_chain_span); + } + let if_chain_span = match if_chain_span { + None => return, + Some(span) => span, + }; + // check for `if a && b;` + if_chain! { + if let Some(cond) = cond; + if let ExprKind::Binary(op, _, _) = cond.kind; + if op.node == BinOpKind::And; + if cx.sess().source_map().is_multiline(cond.span); + then { + span_lint(cx, IF_CHAIN_STYLE, cond.span, "`if a && b;` should be `if a; if b;`"); + } + } + if is_first_if_chain_expr(cx, expr.hir_id, if_chain_span) + && is_if_chain_then(then_block.stmts, then_block.expr, if_chain_span) + { + span_lint(cx, IF_CHAIN_STYLE, expr.span, "`if_chain!` only has one `if`") + } + } +} + +fn check_nested_if_chains( + cx: &LateContext<'_>, + if_expr: &Expr<'_>, + then_block: &Block<'_>, + if_chain_span: Option<Span>, +) { + #[rustfmt::skip] + let (head, tail) = match *then_block { + Block { stmts, expr: Some(tail), .. } => (stmts, tail), + Block { + stmts: &[ + ref head @ .., + Stmt { kind: StmtKind::Expr(tail) | StmtKind::Semi(tail), .. } + ], + .. + } => (head, tail), + _ => return, + }; + if_chain! { + if matches!(tail.kind, + ExprKind::If(_, _, None) + | ExprKind::Match(.., MatchSource::IfLetDesugar { contains_else_clause: false })); + let sm = cx.sess().source_map(); + if head + .iter() + .all(|stmt| matches!(stmt.kind, StmtKind::Local(..)) && !sm.is_multiline(stmt.span)); + if if_chain_span.is_some() || !is_else_clause(cx.tcx, if_expr); + then {} else { return } + } + let (span, msg) = match (if_chain_span, is_expn_of(tail.span, "if_chain")) { + (None, Some(_)) => (if_expr.span, "this `if` can be part of the inner `if_chain!`"), + (Some(_), None) => (tail.span, "this `if` can be part of the outer `if_chain!`"), + (Some(a), Some(b)) if a != b => (b, "this `if_chain!` can be merged with the outer `if_chain!`"), + _ => return, + }; + span_lint_and_then(cx, IF_CHAIN_STYLE, span, msg, |diag| { + let (span, msg) = match head { + [] => return, + [stmt] => (stmt.span, "this `let` statement can also be in the `if_chain!`"), + [a, .., b] => ( + a.span.to(b.span), + "these `let` statements can also be in the `if_chain!`", + ), + }; + diag.span_help(span, msg); + }); +} + +fn is_first_if_chain_expr(cx: &LateContext<'_>, hir_id: HirId, if_chain_span: Span) -> bool { + cx.tcx + .hir() + .parent_iter(hir_id) + .find(|(_, node)| { + #[rustfmt::skip] + !matches!(node, Node::Expr(Expr { kind: ExprKind::Block(..), .. }) | Node::Stmt(_)) + }) + .map_or(false, |(id, _)| { + is_expn_of(cx.tcx.hir().span(id), "if_chain") != Some(if_chain_span) + }) +} + +/// Checks a trailing slice of statements and expression of a `Block` to see if they are part +/// of the `then {..}` portion of an `if_chain!` +fn is_if_chain_then(stmts: &[Stmt<'_>], expr: Option<&Expr<'_>>, if_chain_span: Span) -> bool { + let span = if let [stmt, ..] = stmts { + stmt.span + } else if let Some(expr) = expr { + expr.span + } else { + // empty `then {}` + return true; + }; + is_expn_of(span, "if_chain").map_or(true, |span| span != if_chain_span) +} + +/// Creates a `Span` for `let x = ..;` in an `if_chain!` call. +fn if_chain_local_span(cx: &LateContext<'_>, local: &Local<'_>, if_chain_span: Span) -> Span { + let mut span = local.pat.span; + if let Some(init) = local.init { + span = span.to(init.span); + } + span.adjust(if_chain_span.ctxt().outer_expn()); + let sm = cx.sess().source_map(); + let span = sm.span_extend_to_prev_str(span, "let", false); + let span = sm.span_extend_to_next_char(span, ';', false); + Span::new(span.lo() - BytePos(3), span.hi() + BytePos(1), span.ctxt()) +} diff --git a/src/tools/clippy/clippy_lints/src/utils/mod.rs b/src/tools/clippy/clippy_lints/src/utils/mod.rs index be9a07f8d7c9e..d8b31344e6d8b 100644 --- a/src/tools/clippy/clippy_lints/src/utils/mod.rs +++ b/src/tools/clippy/clippy_lints/src/utils/mod.rs @@ -3,5 +3,3 @@ pub mod conf; pub mod inspector; #[cfg(feature = "internal-lints")] pub mod internal_lints; - -pub use clippy_utils::*; diff --git a/src/tools/clippy/clippy_lints/src/vec.rs b/src/tools/clippy/clippy_lints/src/vec.rs index c132e4de4f67b..febd4b6ff7b3c 100644 --- a/src/tools/clippy/clippy_lints/src/vec.rs +++ b/src/tools/clippy/clippy_lints/src/vec.rs @@ -1,9 +1,12 @@ use crate::consts::{constant, Constant}; use crate::rustc_target::abi::LayoutOf; -use crate::utils::{higher, is_copy, snippet_with_applicability, span_lint_and_sugg}; +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::higher; +use clippy_utils::source::snippet_with_applicability; +use clippy_utils::ty::is_copy; use if_chain::if_chain; use rustc_errors::Applicability; -use rustc_hir::{BorrowKind, Expr, ExprKind}; +use rustc_hir::{BorrowKind, Expr, ExprKind, Mutability}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::{self, Ty}; use rustc_session::{declare_tool_lint, impl_lint_pass}; @@ -46,10 +49,10 @@ impl<'tcx> LateLintPass<'tcx> for UselessVec { if_chain! { if let ty::Ref(_, ty, _) = cx.typeck_results().expr_ty_adjusted(expr).kind(); if let ty::Slice(..) = ty.kind(); - if let ExprKind::AddrOf(BorrowKind::Ref, _, ref addressee) = expr.kind; + if let ExprKind::AddrOf(BorrowKind::Ref, mutability, addressee) = expr.kind; if let Some(vec_args) = higher::vec_macro(cx, addressee); then { - self.check_vec_macro(cx, &vec_args, expr.span); + self.check_vec_macro(cx, &vec_args, mutability, expr.span); } } @@ -67,14 +70,20 @@ impl<'tcx> LateLintPass<'tcx> for UselessVec { .ctxt() .outer_expn_data() .call_site; - self.check_vec_macro(cx, &vec_args, span); + self.check_vec_macro(cx, &vec_args, Mutability::Not, span); } } } } impl UselessVec { - fn check_vec_macro<'tcx>(self, cx: &LateContext<'tcx>, vec_args: &higher::VecArgs<'tcx>, span: Span) { + fn check_vec_macro<'tcx>( + self, + cx: &LateContext<'tcx>, + vec_args: &higher::VecArgs<'tcx>, + mutability: Mutability, + span: Span, + ) { let mut applicability = Applicability::MachineApplicable; let snippet = match *vec_args { higher::VecArgs::Repeat(elem, len) => { @@ -84,11 +93,22 @@ impl UselessVec { return; } - format!( - "&[{}; {}]", - snippet_with_applicability(cx, elem.span, "elem", &mut applicability), - snippet_with_applicability(cx, len.span, "len", &mut applicability) - ) + match mutability { + Mutability::Mut => { + format!( + "&mut [{}; {}]", + snippet_with_applicability(cx, elem.span, "elem", &mut applicability), + snippet_with_applicability(cx, len.span, "len", &mut applicability) + ) + }, + Mutability::Not => { + format!( + "&[{}; {}]", + snippet_with_applicability(cx, elem.span, "elem", &mut applicability), + snippet_with_applicability(cx, len.span, "len", &mut applicability) + ) + }, + } } else { return; } @@ -101,9 +121,22 @@ impl UselessVec { } let span = args[0].span.to(last.span); - format!("&[{}]", snippet_with_applicability(cx, span, "..", &mut applicability)) + match mutability { + Mutability::Mut => { + format!( + "&mut [{}]", + snippet_with_applicability(cx, span, "..", &mut applicability) + ) + }, + Mutability::Not => { + format!("&[{}]", snippet_with_applicability(cx, span, "..", &mut applicability)) + }, + } } else { - "&[]".into() + match mutability { + Mutability::Mut => "&mut []".into(), + Mutability::Not => "&[]".into(), + } } }, }; 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 8d111f98add9a..c7190e2f9798c 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 @@ -1,6 +1,7 @@ -use crate::utils::{ - is_type_diagnostic_item, match_def_path, path_to_local, path_to_local_id, paths, snippet, span_lint_and_sugg, -}; +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::source::snippet; +use clippy_utils::ty::is_type_diagnostic_item; +use clippy_utils::{match_def_path, path_to_local, path_to_local_id, paths}; use if_chain::if_chain; use rustc_ast::ast::LitKind; use rustc_errors::Applicability; @@ -107,22 +108,21 @@ impl LateLintPass<'_> for VecInitThenPush { } fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - if self.searcher.is_none() { - if_chain! { - if !in_external_macro(cx.sess(), expr.span); - if let ExprKind::Assign(left, right, _) = expr.kind; - if let Some(id) = path_to_local(left); - if let Some(init_kind) = get_vec_init_kind(cx, right); - then { - self.searcher = Some(VecPushSearcher { - local_id: id, - init: init_kind, - lhs_is_local: false, - lhs_span: left.span, - err_span: expr.span, - found: 0, - }); - } + if_chain! { + if self.searcher.is_none(); + if !in_external_macro(cx.sess(), expr.span); + if let ExprKind::Assign(left, right, _) = expr.kind; + if let Some(id) = path_to_local(left); + if let Some(init_kind) = get_vec_init_kind(cx, right); + then { + self.searcher = Some(VecPushSearcher { + local_id: id, + init: init_kind, + lhs_is_local: false, + lhs_span: left.span, + err_span: expr.span, + found: 0, + }); } } } diff --git a/src/tools/clippy/clippy_lints/src/vec_resize_to_zero.rs b/src/tools/clippy/clippy_lints/src/vec_resize_to_zero.rs index d2494b321efcd..5540e87405ff9 100644 --- a/src/tools/clippy/clippy_lints/src/vec_resize_to_zero.rs +++ b/src/tools/clippy/clippy_lints/src/vec_resize_to_zero.rs @@ -1,15 +1,14 @@ -use crate::utils::span_lint_and_then; +use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::{match_def_path, paths}; use if_chain::if_chain; +use rustc_ast::LitKind; use rustc_errors::Applicability; +use rustc_hir as hir; use rustc_hir::{Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::source_map::Spanned; -use crate::utils::{match_def_path, paths}; -use rustc_ast::LitKind; -use rustc_hir as hir; - declare_clippy_lint! { /// **What it does:** Finds occurrences of `Vec::resize(0, an_int)` /// @@ -31,7 +30,7 @@ declare_lint_pass!(VecResizeToZero => [VEC_RESIZE_TO_ZERO]); impl<'tcx> LateLintPass<'tcx> for VecResizeToZero { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { if_chain! { - if let hir::ExprKind::MethodCall(path_segment, _, ref args, _) = expr.kind; + if let hir::ExprKind::MethodCall(path_segment, _, args, _) = expr.kind; if let Some(method_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id); if match_def_path(cx, method_def_id, &paths::VEC_RESIZE) && args.len() == 3; if let ExprKind::Lit(Spanned { node: LitKind::Int(0, _), .. }) = args[1].kind; diff --git a/src/tools/clippy/clippy_lints/src/verbose_file_reads.rs b/src/tools/clippy/clippy_lints/src/verbose_file_reads.rs index 32574d9d6c9a8..ec209b309513e 100644 --- a/src/tools/clippy/clippy_lints/src/verbose_file_reads.rs +++ b/src/tools/clippy/clippy_lints/src/verbose_file_reads.rs @@ -1,4 +1,6 @@ -use crate::utils::{match_type, paths, span_lint_and_help}; +use clippy_utils::diagnostics::span_lint_and_help; +use clippy_utils::paths; +use clippy_utils::ty::match_type; use if_chain::if_chain; use rustc_hir::{Expr, ExprKind, QPath}; use rustc_lint::{LateContext, LateLintPass}; diff --git a/src/tools/clippy/clippy_lints/src/wildcard_dependencies.rs b/src/tools/clippy/clippy_lints/src/wildcard_dependencies.rs index cd1864f461d3b..60c3489a449b6 100644 --- a/src/tools/clippy/clippy_lints/src/wildcard_dependencies.rs +++ b/src/tools/clippy/clippy_lints/src/wildcard_dependencies.rs @@ -1,4 +1,5 @@ -use crate::utils::{run_lints, span_lint}; +use clippy_utils::diagnostics::span_lint; +use clippy_utils::run_lints; use rustc_hir::{hir_id::CRATE_HIR_ID, Crate}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; diff --git a/src/tools/clippy/clippy_lints/src/wildcard_imports.rs b/src/tools/clippy/clippy_lints/src/wildcard_imports.rs index 094b1a42346c2..51c1117d20641 100644 --- a/src/tools/clippy/clippy_lints/src/wildcard_imports.rs +++ b/src/tools/clippy/clippy_lints/src/wildcard_imports.rs @@ -1,4 +1,6 @@ -use crate::utils::{in_macro, snippet, snippet_with_applicability, span_lint_and_sugg}; +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::in_macro; +use clippy_utils::source::{snippet, snippet_with_applicability}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::{ diff --git a/src/tools/clippy/clippy_lints/src/write.rs b/src/tools/clippy/clippy_lints/src/write.rs index 553e6b000ebbc..12a47a6b7036d 100644 --- a/src/tools/clippy/clippy_lints/src/write.rs +++ b/src/tools/clippy/clippy_lints/src/write.rs @@ -1,20 +1,19 @@ use std::borrow::Cow; -use std::ops::Range; - -use crate::utils::{snippet_with_applicability, span_lint, span_lint_and_sugg, span_lint_and_then}; -use if_chain::if_chain; -use rustc_ast::ast::{ - Expr, ExprKind, ImplKind, Item, ItemKind, LitKind, MacCall, StrLit, StrStyle, -}; -use rustc_ast::token; +use std::iter; +use std::ops::{Deref, Range}; + +use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg, span_lint_and_then}; +use clippy_utils::source::{snippet_opt, snippet_with_applicability}; +use rustc_ast::ast::{Expr, ExprKind, ImplKind, Item, ItemKind, MacCall, Path, StrLit, StrStyle}; +use rustc_ast::token::{self, LitKind}; use rustc_ast::tokenstream::TokenStream; use rustc_errors::Applicability; use rustc_lexer::unescape::{self, EscapeError}; use rustc_lint::{EarlyContext, EarlyLintPass}; use rustc_parse::parser; use rustc_session::{declare_tool_lint, impl_lint_pass}; -use rustc_span::symbol::kw; -use rustc_span::{sym, BytePos, Span}; +use rustc_span::symbol::{kw, Symbol}; +use rustc_span::{sym, BytePos, Span, DUMMY_SP}; declare_clippy_lint! { /// **What it does:** This lint warns when you use `println!("")` to @@ -355,7 +354,120 @@ fn newline_span(fmtstr: &StrLit) -> Span { sp.with_lo(newline_sp_hi - newline_sp_len).with_hi(newline_sp_hi) } +/// Stores a list of replacement spans for each argument, but only if all the replacements used an +/// empty format string. +#[derive(Default)] +struct SimpleFormatArgs { + unnamed: Vec<Vec<Span>>, + named: Vec<(Symbol, Vec<Span>)>, +} +impl SimpleFormatArgs { + fn get_unnamed(&self) -> impl Iterator<Item = &[Span]> { + self.unnamed.iter().map(|x| match x.as_slice() { + // Ignore the dummy span added from out of order format arguments. + [DUMMY_SP] => &[], + x => x, + }) + } + + fn get_named(&self, n: &Path) -> &[Span] { + self.named.iter().find(|x| *n == x.0).map_or(&[], |x| x.1.as_slice()) + } + + fn push(&mut self, arg: rustc_parse_format::Argument<'_>, span: Span) { + use rustc_parse_format::{ + AlignUnknown, ArgumentImplicitlyIs, ArgumentIs, ArgumentNamed, CountImplied, FormatSpec, + }; + + const SIMPLE: FormatSpec<'_> = FormatSpec { + fill: None, + align: AlignUnknown, + flags: 0, + precision: CountImplied, + precision_span: None, + width: CountImplied, + width_span: None, + ty: "", + ty_span: None, + }; + + match arg.position { + ArgumentIs(n) | ArgumentImplicitlyIs(n) => { + if self.unnamed.len() <= n { + // Use a dummy span to mark all unseen arguments. + self.unnamed.resize_with(n, || vec![DUMMY_SP]); + if arg.format == SIMPLE { + self.unnamed.push(vec![span]); + } else { + self.unnamed.push(Vec::new()); + } + } else { + let args = &mut self.unnamed[n]; + match (args.as_mut_slice(), arg.format == SIMPLE) { + // A non-empty format string has been seen already. + ([], _) => (), + // Replace the dummy span, if it exists. + ([dummy @ DUMMY_SP], true) => *dummy = span, + ([_, ..], true) => args.push(span), + ([_, ..], false) => *args = Vec::new(), + } + } + }, + ArgumentNamed(n) => { + if let Some(x) = self.named.iter_mut().find(|x| x.0 == n) { + match x.1.as_slice() { + // A non-empty format string has been seen already. + [] => (), + [_, ..] if arg.format == SIMPLE => x.1.push(span), + [_, ..] => x.1 = Vec::new(), + } + } else if arg.format == SIMPLE { + self.named.push((n, vec![span])); + } else { + self.named.push((n, Vec::new())); + } + }, + }; + } +} + impl Write { + /// Parses a format string into a collection of spans for each argument. This only keeps track + /// of empty format arguments. Will also lint usages of debug format strings outside of debug + /// impls. + fn parse_fmt_string(&self, cx: &EarlyContext<'_>, str_lit: &StrLit) -> Option<SimpleFormatArgs> { + use rustc_parse_format::{ParseMode, Parser, Piece}; + + let str_sym = str_lit.symbol_unescaped.as_str(); + let style = match str_lit.style { + StrStyle::Cooked => None, + StrStyle::Raw(n) => Some(n as usize), + }; + + let mut parser = Parser::new(&str_sym, style, snippet_opt(cx, str_lit.span), false, ParseMode::Format); + let mut args = SimpleFormatArgs::default(); + + while let Some(arg) = parser.next() { + let arg = match arg { + Piece::String(_) => continue, + Piece::NextArgument(arg) => arg, + }; + let span = parser + .arg_places + .last() + .map_or(DUMMY_SP, |&x| str_lit.span.from_inner(x)); + + if !self.in_debug_impl && arg.format.ty == "?" { + // FIXME: modify rustc's fmt string parser to give us the current span + span_lint(cx, USE_DEBUG, span, "use of `Debug`-based formatting"); + } + + args.push(arg, span); + } + + parser.errors.is_empty().then(move || args) + } + /// Checks the arguments of `print[ln]!` and `write[ln]!` calls. It will return a tuple of two /// `Option`s. The first `Option` of the tuple is the macro's format string. It includes /// the contents of the string, whether it's a raw string, and the span of the literal in the @@ -376,124 +488,97 @@ impl Write { /// (Some("string to write: {}"), Some(buf)) /// ``` #[allow(clippy::too_many_lines)] - fn check_tts<'a>( - &self, - cx: &EarlyContext<'a>, - tts: TokenStream, - is_write: bool, - ) -> (Option<StrLit>, Option<Expr>) { - use rustc_parse_format::{ - AlignUnknown, ArgumentImplicitlyIs, ArgumentIs, ArgumentNamed, CountImplied, - FormatSpec, ParseMode, Parser, Piece, - }; - + fn check_tts<'a>(&self, cx: &EarlyContext<'a>, tts: TokenStream, is_write: bool) -> (Option<StrLit>, Option<Expr>) { let mut parser = parser::Parser::new(&cx.sess.parse_sess, tts, false, None); - let mut expr: Option<Expr> = None; - if is_write { - expr = match parser.parse_expr().map_err(|mut err| err.cancel()) { - Ok(p) => Some(p.into_inner()), - Err(_) => return (None, None), - }; - // might be `writeln!(foo)` - if parser.expect(&token::Comma).map_err(|mut err| err.cancel()).is_err() { - return (None, expr); + let expr = if is_write { + match parser + .parse_expr() + .map(rustc_ast::ptr::P::into_inner) + .map_err(|mut e| e.cancel()) + { + // write!(e, ...) + Ok(p) if parser.eat(&token::Comma) => Some(p), + // write!(e) or error + e => return (None, e.ok()), } - } + } else { + None + }; let fmtstr = match parser.parse_str_lit() { Ok(fmtstr) => fmtstr, Err(_) => return (None, expr), }; - let tmp = fmtstr.symbol.as_str(); - let mut args = vec![]; - let mut fmt_parser = Parser::new(&tmp, None, None, false, ParseMode::Format); - while let Some(piece) = fmt_parser.next() { - if !fmt_parser.errors.is_empty() { - return (None, expr); - } - if let Piece::NextArgument(arg) = piece { - if !self.in_debug_impl && arg.format.ty == "?" { - // FIXME: modify rustc's fmt string parser to give us the current span - span_lint( - cx, - USE_DEBUG, - parser.prev_token.span, - "use of `Debug`-based formatting", - ); - } - args.push(arg); - } - } + + let args = match self.parse_fmt_string(cx, &fmtstr) { + Some(args) => args, + None => return (Some(fmtstr), expr), + }; + let lint = if is_write { WRITE_LITERAL } else { PRINT_LITERAL }; - let mut idx = 0; + let mut unnamed_args = args.get_unnamed(); loop { - const SIMPLE: FormatSpec<'_> = FormatSpec { - fill: None, - align: AlignUnknown, - flags: 0, - precision: CountImplied, - precision_span: None, - width: CountImplied, - width_span: None, - ty: "", - ty_span: None, - }; if !parser.eat(&token::Comma) { return (Some(fmtstr), expr); } + + let comma_span = parser.prev_token.span; let token_expr = if let Ok(expr) = parser.parse_expr().map_err(|mut err| err.cancel()) { expr } else { return (Some(fmtstr), None); }; - match &token_expr.kind { - ExprKind::Lit(lit) - if !matches!(lit.kind, LitKind::Int(..) | LitKind::Float(..)) => - { - let mut all_simple = true; - let mut seen = false; - for arg in &args { - match arg.position { - ArgumentImplicitlyIs(n) | ArgumentIs(n) => { - if n == idx { - all_simple &= arg.format == SIMPLE; - seen = true; - } - } - ArgumentNamed(_) => {} - } - } - if all_simple && seen { - span_lint(cx, lint, token_expr.span, "literal with an empty format string"); - } - idx += 1; - } - ExprKind::Assign(lhs, rhs, _) => { - if_chain! { - if let ExprKind::Lit(ref lit) = rhs.kind; - if !matches!(lit.kind, LitKind::Int(..) | LitKind::Float(..)); - if let ExprKind::Path(_, p) = &lhs.kind; - then { - let mut all_simple = true; - let mut seen = false; - for arg in &args { - match arg.position { - ArgumentImplicitlyIs(_) | ArgumentIs(_) => {}, - ArgumentNamed(name) => { - if *p == name { - seen = true; - all_simple &= arg.format == SIMPLE; - } - }, - } - } - if all_simple && seen { - span_lint(cx, lint, rhs.span, "literal with an empty format string"); - } - } - } + let (fmt_spans, lit) = match &token_expr.kind { + ExprKind::Lit(lit) => (unnamed_args.next().unwrap_or(&[]), lit), + ExprKind::Assign(lhs, rhs, _) => match (&lhs.kind, &rhs.kind) { + (ExprKind::Path(_, p), ExprKind::Lit(lit)) => (args.get_named(p), lit), + _ => continue, + }, + _ => { + unnamed_args.next(); + continue; + }, + }; + + let replacement: String = match lit.token.kind { + LitKind::Integer | LitKind::Float | LitKind::Err => continue, + LitKind::StrRaw(_) | LitKind::ByteStrRaw(_) if matches!(fmtstr.style, StrStyle::Raw(_)) => { + lit.token.symbol.as_str().replace("{", "{{").replace("}", "}}") + }, + LitKind::Str | LitKind::ByteStr if matches!(fmtstr.style, StrStyle::Cooked) => { + lit.token.symbol.as_str().replace("{", "{{").replace("}", "}}") + }, + LitKind::StrRaw(_) | LitKind::Str | LitKind::ByteStrRaw(_) | LitKind::ByteStr => continue, + LitKind::Byte | LitKind::Char => match &*lit.token.symbol.as_str() { + "\"" if matches!(fmtstr.style, StrStyle::Cooked) => "\\\"", + "\"" if matches!(fmtstr.style, StrStyle::Raw(0)) => continue, + "\\\\" if matches!(fmtstr.style, StrStyle::Raw(_)) => "\\", + "\\'" => "'", + "{" => "{{", + "}" => "}}", + x if matches!(fmtstr.style, StrStyle::Raw(_)) && x.starts_with('\\') => continue, + x => x, } - _ => idx += 1, + .into(), + LitKind::Bool => lit.token.symbol.as_str().deref().into(), + }; + + if !fmt_spans.is_empty() { + span_lint_and_then( + cx, + lint, + token_expr.span, + "literal with an empty format string", + |diag| { + diag.multipart_suggestion( + "try this", + iter::once((comma_span.to(token_expr.span), String::new())) + .chain(fmt_spans.iter().cloned().zip(iter::repeat(replacement))) + .collect(), + Applicability::MachineApplicable, + ); + }, + ); } } } @@ -524,17 +609,11 @@ impl Write { cx, PRINT_WITH_NEWLINE, mac.span(), - &format!( - "using `{}!()` with a format string that ends in a single newline", - name - ), + &format!("using `{}!()` with a format string that ends in a single newline", name), |err| { err.multipart_suggestion( &format!("use `{}!` instead", suggested), - vec![ - (mac.path.span, suggested), - (newline_span(&fmt_str), String::new()), - ], + vec![(mac.path.span, suggested), (newline_span(&fmt_str), String::new())], Applicability::MachineApplicable, ); }, diff --git a/src/tools/clippy/clippy_lints/src/zero_div_zero.rs b/src/tools/clippy/clippy_lints/src/zero_div_zero.rs index 4b81a27632d8d..350b1cf25ff05 100644 --- a/src/tools/clippy/clippy_lints/src/zero_div_zero.rs +++ b/src/tools/clippy/clippy_lints/src/zero_div_zero.rs @@ -1,5 +1,5 @@ use crate::consts::{constant_simple, Constant}; -use crate::utils::span_lint_and_help; +use clippy_utils::diagnostics::span_lint_and_help; use if_chain::if_chain; use rustc_hir::{BinOpKind, Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; @@ -31,7 +31,7 @@ impl<'tcx> LateLintPass<'tcx> for ZeroDiv { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { // check for instances of 0.0/0.0 if_chain! { - if let ExprKind::Binary(ref op, ref left, ref right) = expr.kind; + if let ExprKind::Binary(ref op, left, right) = expr.kind; if let BinOpKind::Div = op.node; // TODO - constant_simple does not fold many operations involving floats. // That's probably fine for this lint - it's pretty unlikely that someone would @@ -55,7 +55,7 @@ impl<'tcx> LateLintPass<'tcx> for ZeroDiv { "constant division of `0.0` with `0.0` will always result in NaN", None, &format!( - "Consider using `{}::NAN` if you would like a constant representing NaN", + "consider using `{}::NAN` if you would like a constant representing NaN", float_type, ), ); diff --git a/src/tools/clippy/clippy_lints/src/zero_sized_map_values.rs b/src/tools/clippy/clippy_lints/src/zero_sized_map_values.rs index 316b8d820a715..f93f0047f514f 100644 --- a/src/tools/clippy/clippy_lints/src/zero_sized_map_values.rs +++ b/src/tools/clippy/clippy_lints/src/zero_sized_map_values.rs @@ -1,13 +1,14 @@ +use clippy_utils::diagnostics::span_lint_and_help; +use clippy_utils::paths; +use clippy_utils::ty::{is_normalizable, is_type_diagnostic_item, match_type}; use if_chain::if_chain; use rustc_hir::{self as hir, HirId, ItemKind, Node}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::{Adt, Ty}; use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::sym; use rustc_target::abi::LayoutOf as _; use rustc_typeck::hir_ty_to_ty; -use rustc_span::sym; - -use crate::utils::{is_normalizable, is_type_diagnostic_item, match_type, paths, span_lint_and_help}; declare_clippy_lint! { /// **What it does:** Checks for maps with zero-sized value types anywhere in the code. @@ -49,7 +50,7 @@ impl LateLintPass<'_> for ZeroSizedMapValues { if !in_trait_impl(cx, hir_ty.hir_id); let ty = ty_from_hir_ty(cx, hir_ty); if is_type_diagnostic_item(cx, ty, sym::hashmap_type) || match_type(cx, ty, &paths::BTREEMAP); - if let Adt(_, ref substs) = ty.kind(); + if let Adt(_, substs) = ty.kind(); let ty = substs.type_at(1); // Do this to prevent `layout_of` crashing, being unable to fully normalize `ty`. if is_normalizable(cx, cx.param_env, ty); diff --git a/src/tools/clippy/clippy_utils/Cargo.toml b/src/tools/clippy/clippy_utils/Cargo.toml index 9c01badb04cc9..d04c5f889dda0 100644 --- a/src/tools/clippy/clippy_utils/Cargo.toml +++ b/src/tools/clippy/clippy_utils/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "clippy_utils" -version = "0.1.52" +version = "0.1.53" authors = ["The Rust Clippy Developers"] edition = "2018" publish = false @@ -10,10 +10,12 @@ if_chain = "1.0.0" itertools = "0.9" regex-syntax = "0.6" serde = { version = "1.0", features = ["derive"] } -smallvec = { version = "1", features = ["union"] } -toml = "0.5.3" unicode-normalization = "0.1" rustc-semver="1.1.0" [features] internal-lints = [] + +[package.metadata.rust-analyzer] +# This crate uses #[feature(rustc_private)] +rustc_private = true diff --git a/src/tools/clippy/clippy_utils/src/ast_utils.rs b/src/tools/clippy/clippy_utils/src/ast_utils.rs index 9ef1557ec061d..eaea3e636f9c3 100644 --- a/src/tools/clippy/clippy_utils/src/ast_utils.rs +++ b/src/tools/clippy/clippy_utils/src/ast_utils.rs @@ -66,7 +66,7 @@ pub fn eq_range_end(l: &RangeEnd, r: &RangeEnd) -> bool { } } -pub fn eq_field_pat(l: &FieldPat, r: &FieldPat) -> bool { +pub fn eq_field_pat(l: &PatField, r: &PatField) -> bool { l.is_placeholder == r.is_placeholder && eq_id(l.ident, r.ident) && eq_pat(&l.pat, &r.pat) @@ -168,14 +168,16 @@ pub fn eq_expr(l: &Expr, r: &Expr) -> bool { (AddrOf(lbk, lm, le), AddrOf(rbk, rm, re)) => lbk == rbk && lm == rm && eq_expr(le, re), (Path(lq, lp), Path(rq, rp)) => both(lq, rq, |l, r| eq_qself(l, r)) && eq_path(lp, rp), (MacCall(l), MacCall(r)) => eq_mac_call(l, r), - (Struct(lp, lfs, lb), Struct(rp, rfs, rb)) => { - eq_path(lp, rp) && eq_struct_rest(lb, rb) && unordered_over(lfs, rfs, |l, r| eq_field(l, r)) + (Struct(lse), Struct(rse)) => { + eq_path(&lse.path, &rse.path) + && eq_struct_rest(&lse.rest, &rse.rest) + && unordered_over(&lse.fields, &rse.fields, |l, r| eq_field(l, r)) }, _ => false, } } -pub fn eq_field(l: &Field, r: &Field) -> bool { +pub fn eq_field(l: &ExprField, r: &ExprField) -> bool { l.is_placeholder == r.is_placeholder && eq_id(l.ident, r.ident) && eq_expr(&l.expr, &r.expr) @@ -359,7 +361,7 @@ pub fn eq_variant_data(l: &VariantData, r: &VariantData) -> bool { } } -pub fn eq_struct_field(l: &StructField, r: &StructField) -> bool { +pub fn eq_struct_field(l: &FieldDef, r: &FieldDef) -> bool { l.is_placeholder == r.is_placeholder && over(&l.attrs, &r.attrs, |l, r| eq_attr(l, r)) && eq_vis(&l.vis, &r.vis) @@ -406,6 +408,10 @@ pub fn eq_use_tree(l: &UseTree, r: &UseTree) -> bool { eq_path(&l.prefix, &r.prefix) && eq_use_tree_kind(&l.kind, &r.kind) } +pub fn eq_anon_const(l: &AnonConst, r: &AnonConst) -> bool { + eq_expr(&l.value, &r.value) +} + pub fn eq_use_tree_kind(l: &UseTreeKind, r: &UseTreeKind) -> bool { use UseTreeKind::*; match (l, r) { @@ -416,10 +422,6 @@ pub fn eq_use_tree_kind(l: &UseTreeKind, r: &UseTreeKind) -> bool { } } -pub fn eq_anon_const(l: &AnonConst, r: &AnonConst) -> bool { - eq_expr(&l.value, &r.value) -} - pub fn eq_defaultness(l: Defaultness, r: Defaultness) -> bool { matches!( (l, r), diff --git a/src/tools/clippy/clippy_utils/src/attrs.rs b/src/tools/clippy/clippy_utils/src/attrs.rs index 8d28421d70d70..7ec8452bf4c67 100644 --- a/src/tools/clippy/clippy_utils/src/attrs.rs +++ b/src/tools/clippy/clippy_utils/src/attrs.rs @@ -1,4 +1,4 @@ -use rustc_ast::ast; +use rustc_ast::{ast, attr}; use rustc_errors::Applicability; use rustc_session::Session; use rustc_span::sym; @@ -148,3 +148,13 @@ pub fn get_unique_inner_attr(sess: &Session, attrs: &[ast::Attribute], name: &'s pub fn is_proc_macro(sess: &Session, attrs: &[ast::Attribute]) -> bool { attrs.iter().any(|attr| sess.is_proc_macro_attr(attr)) } + +/// Return true if the attributes contain `#[doc(hidden)]` +pub fn is_doc_hidden(attrs: &[ast::Attribute]) -> bool { + #[allow(clippy::filter_map)] + attrs + .iter() + .filter(|attr| attr.has_name(sym::doc)) + .flat_map(ast::Attribute::meta_item_list) + .any(|l| attr::list_contains_name(&l, sym::hidden)) +} diff --git a/src/tools/clippy/clippy_utils/src/camel_case.rs b/src/tools/clippy/clippy_utils/src/camel_case.rs index ba1c01ebc9f76..a6636e391374e 100644 --- a/src/tools/clippy/clippy_utils/src/camel_case.rs +++ b/src/tools/clippy/clippy_utils/src/camel_case.rs @@ -25,11 +25,7 @@ pub fn until(s: &str) -> usize { return i; } } - if up { - last_i - } else { - s.len() - } + if up { last_i } else { s.len() } } /// Returns index of the last camel-case component of `s`. diff --git a/src/tools/clippy/clippy_utils/src/consts.rs b/src/tools/clippy/clippy_utils/src/consts.rs index 802c01055a68c..2a305d8bcbe0d 100644 --- a/src/tools/clippy/clippy_utils/src/consts.rs +++ b/src/tools/clippy/clippy_utils/src/consts.rs @@ -15,6 +15,7 @@ use rustc_span::symbol::Symbol; use std::cmp::Ordering::{self, Equal}; use std::convert::TryInto; use std::hash::{Hash, Hasher}; +use std::iter; /// A `LitKind`-like enum to fold constant `Expr`s into. #[derive(Debug, Clone)] @@ -139,9 +140,7 @@ impl Constant { (&Self::F64(l), &Self::F64(r)) => l.partial_cmp(&r), (&Self::F32(l), &Self::F32(r)) => l.partial_cmp(&r), (&Self::Bool(ref l), &Self::Bool(ref r)) => Some(l.cmp(r)), - (&Self::Tuple(ref l), &Self::Tuple(ref r)) | (&Self::Vec(ref l), &Self::Vec(ref r)) => l - .iter() - .zip(r.iter()) + (&Self::Tuple(ref l), &Self::Tuple(ref r)) | (&Self::Vec(ref l), &Self::Vec(ref r)) => iter::zip(l, r) .map(|(li, ri)| Self::partial_cmp(tcx, cmp_type, li, ri)) .find(|r| r.map_or(true, |o| o != Ordering::Equal)) .unwrap_or_else(|| Some(l.len().cmp(&r.len()))), @@ -341,9 +340,11 @@ impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> { .tcx .const_eval_resolve( self.param_env, - ty::WithOptConstParam::unknown(def_id), - substs, - None, + ty::Unevaluated { + def: ty::WithOptConstParam::unknown(def_id), + substs, + promoted: None, + }, None, ) .ok() diff --git a/src/tools/clippy/clippy_utils/src/diagnostics.rs b/src/tools/clippy/clippy_utils/src/diagnostics.rs index 269be217c2d87..7f827f1759d2f 100644 --- a/src/tools/clippy/clippy_utils/src/diagnostics.rs +++ b/src/tools/clippy/clippy_utils/src/diagnostics.rs @@ -8,14 +8,16 @@ use std::env; fn docs_link(diag: &mut DiagnosticBuilder<'_>, lint: &'static Lint) { if env::var("CLIPPY_DISABLE_DOCS_LINKS").is_err() { - diag.help(&format!( - "for further information visit https://rust-lang.github.io/rust-clippy/{}/index.html#{}", - &option_env!("RUST_RELEASE_NUM").map_or("master".to_string(), |n| { - // extract just major + minor version and ignore patch versions - format!("rust-{}", n.rsplitn(2, '.').nth(1).unwrap()) - }), - lint.name_lower().replacen("clippy::", "", 1) - )); + if let Some(lint) = lint.name_lower().strip_prefix("clippy::") { + diag.help(&format!( + "for further information visit https://rust-lang.github.io/rust-clippy/{}/index.html#{}", + &option_env!("RUST_RELEASE_NUM").map_or("master".to_string(), |n| { + // extract just major + minor version and ignore patch versions + format!("rust-{}", n.rsplitn(2, '.').nth(1).unwrap()) + }), + lint + )); + } } } @@ -131,9 +133,11 @@ pub fn span_lint_and_note<'a, T: LintContext>( /// /// If you need to customize your lint output a lot, use this function. /// If you change the signature, remember to update the internal lint `CollapsibleCalls` -pub fn span_lint_and_then<'a, T: LintContext, F>(cx: &'a T, lint: &'static Lint, sp: Span, msg: &str, f: F) +pub fn span_lint_and_then<C, S, F>(cx: &C, lint: &'static Lint, sp: S, msg: &str, f: F) where - F: for<'b> FnOnce(&mut DiagnosticBuilder<'b>), + C: LintContext, + S: Into<MultiSpan>, + F: FnOnce(&mut DiagnosticBuilder<'_>), { cx.struct_span_lint(lint, sp, |diag| { let mut diag = diag.build(msg); @@ -214,6 +218,11 @@ where multispan_sugg_with_applicability(diag, help_msg, Applicability::Unspecified, sugg) } +/// Create a suggestion made from several `span → replacement`. +/// +/// rustfix currently doesn't support the automatic application of suggestions with +/// multiple spans. This is tracked in issue [rustfix#141](https://github.com/rust-lang/rustfix/issues/141). +/// Suggestions with multiple spans will be silently ignored. pub fn multispan_sugg_with_applicability<I>( diag: &mut DiagnosticBuilder<'_>, help_msg: &str, 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 81cd99c0558dc..88b115a63d788 100644 --- a/src/tools/clippy/clippy_utils/src/eager_or_lazy.rs +++ b/src/tools/clippy/clippy_utils/src/eager_or_lazy.rs @@ -9,7 +9,8 @@ //! - or-fun-call //! - option-if-let-else -use crate::{is_ctor_or_promotable_const_function, is_type_diagnostic_item, match_type, paths}; +use crate::is_ctor_or_promotable_const_function; +use crate::ty::is_type_diagnostic_item; use rustc_hir::def::{DefKind, Res}; use rustc_hir::intravisit; @@ -101,7 +102,7 @@ fn identify_some_potentially_expensive_patterns<'tcx>(cx: &LateContext<'tcx>, ex ExprKind::Index(obj, _) => { let ty = self.cx.typeck_results().expr_ty(obj); is_type_diagnostic_item(self.cx, ty, sym::hashmap_type) - || match_type(self.cx, ty, &paths::BTREEMAP) + || is_type_diagnostic_item(self.cx, ty, sym::BTreeMap) }, ExprKind::MethodCall(..) => true, _ => false, diff --git a/src/tools/clippy/clippy_utils/src/higher.rs b/src/tools/clippy/clippy_utils/src/higher.rs index be22df7109af7..0c0e4d3b4ce80 100644 --- a/src/tools/clippy/clippy_utils/src/higher.rs +++ b/src/tools/clippy/clippy_utils/src/higher.rs @@ -51,7 +51,7 @@ pub struct Range<'a> { pub fn range<'a>(expr: &'a hir::Expr<'_>) -> Option<Range<'a>> { /// Finds the field named `name` in the field. Always return `Some` for /// convenience. - fn get_field<'c>(name: &str, fields: &'c [hir::Field<'_>]) -> Option<&'c hir::Expr<'c>> { + fn get_field<'c>(name: &str, fields: &'c [hir::ExprField<'_>]) -> Option<&'c hir::Expr<'c>> { let expr = &fields.iter().find(|field| field.ident.name.as_str() == name)?.expr; Some(expr) diff --git a/src/tools/clippy/clippy_utils/src/hir_utils.rs b/src/tools/clippy/clippy_utils/src/hir_utils.rs index 81be9254cbe1a..f695f1a61e716 100644 --- a/src/tools/clippy/clippy_utils/src/hir_utils.rs +++ b/src/tools/clippy/clippy_utils/src/hir_utils.rs @@ -1,14 +1,16 @@ use crate::consts::{constant_context, constant_simple}; use crate::differing_macro_contexts; +use crate::source::snippet_opt; use rustc_ast::ast::InlineAsmTemplatePiece; -use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; use rustc_hir::def::Res; +use rustc_hir::HirIdMap; use rustc_hir::{ - BinOpKind, Block, BlockCheckMode, BodyId, BorrowKind, CaptureBy, Expr, ExprKind, Field, FieldPat, FnRetTy, - GenericArg, GenericArgs, Guard, HirId, InlineAsmOperand, Lifetime, LifetimeName, ParamName, Pat, PatKind, Path, + BinOpKind, Block, BlockCheckMode, BodyId, BorrowKind, CaptureBy, Expr, ExprField, ExprKind, FnRetTy, GenericArg, + GenericArgs, Guard, HirId, InlineAsmOperand, Lifetime, LifetimeName, ParamName, Pat, PatField, PatKind, Path, PathSegment, QPath, Stmt, StmtKind, Ty, TyKind, TypeBinding, }; +use rustc_lexer::{tokenize, TokenKind}; use rustc_lint::LateContext; use rustc_middle::ich::StableHashingContextProvider; use rustc_middle::ty::TypeckResults; @@ -56,13 +58,14 @@ impl<'a, 'tcx> SpanlessEq<'a, 'tcx> { /// Use this method to wrap comparisons that may involve inter-expression context. /// See `self.locals`. - fn inter_expr(&mut self) -> HirEqInterExpr<'_, 'a, 'tcx> { + pub fn inter_expr(&mut self) -> HirEqInterExpr<'_, 'a, 'tcx> { HirEqInterExpr { inner: self, - locals: FxHashMap::default(), + locals: HirIdMap::default(), } } + #[allow(dead_code)] pub fn eq_block(&mut self, left: &Block<'_>, right: &Block<'_>) -> bool { self.inter_expr().eq_block(left, right) } @@ -78,28 +81,26 @@ impl<'a, 'tcx> SpanlessEq<'a, 'tcx> { pub fn eq_path_segments(&mut self, left: &[PathSegment<'_>], right: &[PathSegment<'_>]) -> bool { self.inter_expr().eq_path_segments(left, right) } - - pub fn eq_ty_kind(&mut self, left: &TyKind<'_>, right: &TyKind<'_>) -> bool { - self.inter_expr().eq_ty_kind(left, right) - } } -struct HirEqInterExpr<'a, 'b, 'tcx> { +pub struct HirEqInterExpr<'a, 'b, 'tcx> { inner: &'a mut SpanlessEq<'b, 'tcx>, // When binding are declared, the binding ID in the left expression is mapped to the one on the // right. For example, when comparing `{ let x = 1; x + 2 }` and `{ let y = 1; y + 2 }`, // these blocks are considered equal since `x` is mapped to `y`. - locals: FxHashMap<HirId, HirId>, + locals: HirIdMap<HirId>, } impl HirEqInterExpr<'_, '_, '_> { - fn eq_stmt(&mut self, left: &Stmt<'_>, right: &Stmt<'_>) -> bool { + pub fn eq_stmt(&mut self, left: &Stmt<'_>, right: &Stmt<'_>) -> bool { match (&left.kind, &right.kind) { (&StmtKind::Local(ref l), &StmtKind::Local(ref r)) => { - self.eq_pat(&l.pat, &r.pat) + // eq_pat adds the HirIds to the locals map. We therefor call it last to make sure that + // these only get added if the init and type is equal. + both(&l.init, &r.init, |l, r| self.eq_expr(l, r)) && both(&l.ty, &r.ty, |l, r| self.eq_ty(l, r)) - && both(&l.init, &r.init, |l, r| self.eq_expr(l, r)) + && self.eq_pat(&l.pat, &r.pat) }, (&StmtKind::Expr(ref l), &StmtKind::Expr(ref r)) | (&StmtKind::Semi(ref l), &StmtKind::Semi(ref r)) => { self.eq_expr(l, r) @@ -110,12 +111,58 @@ impl HirEqInterExpr<'_, '_, '_> { /// Checks whether two blocks are the same. fn eq_block(&mut self, left: &Block<'_>, right: &Block<'_>) -> bool { - over(&left.stmts, &right.stmts, |l, r| self.eq_stmt(l, r)) - && both(&left.expr, &right.expr, |l, r| self.eq_expr(l, r)) + match (left.stmts, left.expr, right.stmts, right.expr) { + ([], None, [], None) => { + // For empty blocks, check to see if the tokens are equal. This will catch the case where a macro + // expanded to nothing, or the cfg attribute was used. + let (left, right) = match ( + snippet_opt(self.inner.cx, left.span), + snippet_opt(self.inner.cx, right.span), + ) { + (Some(left), Some(right)) => (left, right), + _ => return true, + }; + let mut left_pos = 0; + let left = tokenize(&left) + .map(|t| { + let end = left_pos + t.len; + let s = &left[left_pos..end]; + left_pos = end; + (t, s) + }) + .filter(|(t, _)| { + !matches!( + t.kind, + TokenKind::LineComment { .. } | TokenKind::BlockComment { .. } | TokenKind::Whitespace + ) + }) + .map(|(_, s)| s); + let mut right_pos = 0; + let right = tokenize(&right) + .map(|t| { + let end = right_pos + t.len; + let s = &right[right_pos..end]; + right_pos = end; + (t, s) + }) + .filter(|(t, _)| { + !matches!( + t.kind, + TokenKind::LineComment { .. } | TokenKind::BlockComment { .. } | TokenKind::Whitespace + ) + }) + .map(|(_, s)| s); + left.eq(right) + }, + _ => { + over(&left.stmts, &right.stmts, |l, r| self.eq_stmt(l, r)) + && both(&left.expr, &right.expr, |l, r| self.eq_expr(l, r)) + }, + } } #[allow(clippy::similar_names)] - fn eq_expr(&mut self, left: &Expr<'_>, right: &Expr<'_>) -> bool { + pub fn eq_expr(&mut self, left: &Expr<'_>, right: &Expr<'_>) -> bool { if !self.inner.allow_side_effects && differing_macro_contexts(left.span, right.span) { return false; } @@ -131,7 +178,10 @@ impl HirEqInterExpr<'_, '_, '_> { } } - let is_eq = match (&reduce_exprkind(&left.kind), &reduce_exprkind(&right.kind)) { + let is_eq = match ( + &reduce_exprkind(self.inner.cx, &left.kind), + &reduce_exprkind(self.inner.cx, &right.kind), + ) { (&ExprKind::AddrOf(lb, l_mut, ref le), &ExprKind::AddrOf(rb, r_mut, ref re)) => { lb == rb && l_mut == r_mut && self.eq_expr(le, re) }, @@ -201,7 +251,7 @@ impl HirEqInterExpr<'_, '_, '_> { (&ExprKind::Struct(ref l_path, ref lf, ref lo), &ExprKind::Struct(ref r_path, ref rf, ref ro)) => { self.eq_qpath(l_path, r_path) && both(lo, ro, |l, r| self.eq_expr(l, r)) - && over(lf, rf, |l, r| self.eq_field(l, r)) + && over(lf, rf, |l, r| self.eq_expr_field(l, r)) }, (&ExprKind::Tup(l_tup), &ExprKind::Tup(r_tup)) => self.eq_exprs(l_tup, r_tup), (&ExprKind::Unary(l_op, ref le), &ExprKind::Unary(r_op, ref re)) => l_op == r_op && self.eq_expr(le, re), @@ -216,7 +266,7 @@ impl HirEqInterExpr<'_, '_, '_> { over(left, right, |l, r| self.eq_expr(l, r)) } - fn eq_field(&mut self, left: &Field<'_>, right: &Field<'_>) -> bool { + fn eq_expr_field(&mut self, left: &ExprField<'_>, right: &ExprField<'_>) -> bool { left.ident.name == right.ident.name && self.eq_expr(&left.expr, &right.expr) } @@ -240,8 +290,8 @@ impl HirEqInterExpr<'_, '_, '_> { left.name == right.name } - fn eq_fieldpat(&mut self, left: &FieldPat<'_>, right: &FieldPat<'_>) -> bool { - let (FieldPat { ident: li, pat: lp, .. }, FieldPat { ident: ri, pat: rp, .. }) = (&left, &right); + fn eq_pat_field(&mut self, left: &PatField<'_>, right: &PatField<'_>) -> bool { + let (PatField { ident: li, pat: lp, .. }, PatField { ident: ri, pat: rp, .. }) = (&left, &right); li.name == ri.name && self.eq_pat(lp, rp) } @@ -250,7 +300,7 @@ impl HirEqInterExpr<'_, '_, '_> { match (&left.kind, &right.kind) { (&PatKind::Box(ref l), &PatKind::Box(ref r)) => self.eq_pat(l, r), (&PatKind::Struct(ref lp, ref la, ..), &PatKind::Struct(ref rp, ref ra, ..)) => { - self.eq_qpath(lp, rp) && over(la, ra, |l, r| self.eq_fieldpat(l, r)) + self.eq_qpath(lp, rp) && over(la, ra, |l, r| self.eq_pat_field(l, r)) }, (&PatKind::TupleStruct(ref lp, ref la, ls), &PatKind::TupleStruct(ref rp, ref ra, rs)) => { self.eq_qpath(lp, rp) && over(la, ra, |l, r| self.eq_pat(l, r)) && ls == rs @@ -327,13 +377,9 @@ impl HirEqInterExpr<'_, '_, '_> { left.ident.name == right.ident.name && both(&left.args, &right.args, |l, r| self.eq_path_parameters(l, r)) } - fn eq_ty(&mut self, left: &Ty<'_>, right: &Ty<'_>) -> bool { - self.eq_ty_kind(&left.kind, &right.kind) - } - #[allow(clippy::similar_names)] - fn eq_ty_kind(&mut self, left: &TyKind<'_>, right: &TyKind<'_>) -> bool { - match (left, right) { + fn eq_ty(&mut self, left: &Ty<'_>, right: &Ty<'_>) -> bool { + match (&left.kind, &right.kind) { (&TyKind::Slice(ref l_vec), &TyKind::Slice(ref r_vec)) => self.eq_ty(l_vec, r_vec), (&TyKind::Array(ref lt, ref ll_id), &TyKind::Array(ref rt, ref rl_id)) => { let cx = self.inner.cx; @@ -360,11 +406,30 @@ impl HirEqInterExpr<'_, '_, '_> { } /// Some simple reductions like `{ return }` => `return` -fn reduce_exprkind<'hir>(kind: &'hir ExprKind<'hir>) -> &ExprKind<'hir> { +fn reduce_exprkind<'hir>(cx: &LateContext<'_>, kind: &'hir ExprKind<'hir>) -> &'hir ExprKind<'hir> { if let ExprKind::Block(block, _) = kind { match (block.stmts, block.expr) { + // From an `if let` expression without an `else` block. The arm for the implicit wild pattern is an empty + // block with an empty span. + ([], None) if block.span.is_empty() => &ExprKind::Tup(&[]), // `{}` => `()` - ([], None) => &ExprKind::Tup(&[]), + ([], None) => match snippet_opt(cx, block.span) { + // Don't reduce if there are any tokens contained in the braces + Some(snip) + if tokenize(&snip) + .map(|t| t.kind) + .filter(|t| { + !matches!( + t, + TokenKind::LineComment { .. } | TokenKind::BlockComment { .. } | TokenKind::Whitespace + ) + }) + .ne([TokenKind::OpenBrace, TokenKind::CloseBrace].iter().cloned()) => + { + kind + }, + _ => &ExprKind::Tup(&[]), + }, ([], Some(expr)) => match expr.kind { // `{ return .. }` => `return ..` ExprKind::Ret(..) => &expr.kind, @@ -421,6 +486,15 @@ pub fn over<X>(left: &[X], right: &[X], mut eq_fn: impl FnMut(&X, &X) -> bool) - left.len() == right.len() && left.iter().zip(right).all(|(x, y)| eq_fn(x, y)) } +/// Counts how many elements of the slices are equal as per `eq_fn`. +pub fn count_eq<X: Sized>( + left: &mut dyn Iterator<Item = X>, + right: &mut dyn Iterator<Item = X>, + mut eq_fn: impl FnMut(&X, &X) -> bool, +) -> usize { + left.zip(right).take_while(|(l, r)| eq_fn(l, r)).count() +} + /// Checks if two expressions evaluate to the same value, and don't contain any side effects. pub fn eq_expr_value(cx: &LateContext<'_>, left: &Expr<'_>, right: &Expr<'_>) -> bool { SpanlessEq::new(cx).deny_side_effects().eq_expr(left, right) @@ -601,7 +675,8 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> { self.hash_expr(out_expr); } }, - InlineAsmOperand::Const { expr } | InlineAsmOperand::Sym { expr } => self.hash_expr(expr), + InlineAsmOperand::Const { anon_const } => self.hash_body(anon_const.body), + InlineAsmOperand::Sym { expr } => self.hash_expr(expr), } } }, @@ -823,7 +898,7 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> { TyKind::OpaqueDef(_, arg_list) => { self.hash_generic_args(arg_list); }, - TyKind::TraitObject(_, lifetime) => { + TyKind::TraitObject(_, lifetime, _) => { self.hash_lifetime(lifetime); }, TyKind::Typeof(anon_const) => { diff --git a/src/tools/clippy/clippy_utils/src/lib.rs b/src/tools/clippy/clippy_utils/src/lib.rs index 42512cadfb18d..c847712ec2e9e 100644 --- a/src/tools/clippy/clippy_utils/src/lib.rs +++ b/src/tools/clippy/clippy_utils/src/lib.rs @@ -1,6 +1,7 @@ #![feature(box_patterns)] #![feature(in_band_lifetimes)] -#![feature(or_patterns)] +#![feature(iter_zip)] +#![cfg_attr(bootstrap, feature(or_patterns))] #![feature(rustc_private)] #![recursion_limit = "512"] #![allow(clippy::missing_errors_doc, clippy::missing_panics_doc, clippy::must_use_candidate)] @@ -13,6 +14,7 @@ extern crate rustc_data_structures; extern crate rustc_errors; extern crate rustc_hir; extern crate rustc_infer; +extern crate rustc_lexer; extern crate rustc_lint; extern crate rustc_middle; extern crate rustc_mir; @@ -31,7 +33,7 @@ pub mod attrs; pub mod camel_case; pub mod comparisons; pub mod consts; -mod diagnostics; +pub mod diagnostics; pub mod eager_or_lazy; pub mod higher; mod hir_utils; @@ -39,49 +41,46 @@ pub mod numeric_literal; pub mod paths; pub mod ptr; pub mod qualify_min_const_fn; +pub mod source; pub mod sugg; +pub mod ty; pub mod usage; pub mod visitors; pub use self::attrs::*; -pub use self::diagnostics::*; -pub use self::hir_utils::{both, eq_expr_value, over, SpanlessEq, SpanlessHash}; +pub use self::hir_utils::{both, count_eq, eq_expr_value, over, SpanlessEq, SpanlessHash}; -use std::borrow::Cow; use std::collections::hash_map::Entry; use std::hash::BuildHasherDefault; use if_chain::if_chain; -use rustc_ast::ast::{self, Attribute, BorrowKind, LitKind, Mutability}; +use rustc_ast::ast::{self, Attribute, BorrowKind, LitKind}; use rustc_data_structures::fx::FxHashMap; -use rustc_errors::Applicability; use rustc_hir as hir; use rustc_hir::def::{CtorKind, CtorOf, DefKind, Res}; use rustc_hir::def_id::{DefId, LOCAL_CRATE}; use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor}; -use rustc_hir::Node; use rustc_hir::{ - def, Arm, Block, Body, Constness, Crate, Expr, ExprKind, FnDecl, HirId, ImplItem, ImplItemKind, Item, ItemKind, - MatchSource, Param, Pat, PatKind, Path, PathSegment, QPath, TraitItem, TraitItemKind, TraitRef, TyKind, Unsafety, + def, Arm, BindingAnnotation, Block, Body, Constness, Expr, ExprKind, FnDecl, GenericArgs, HirId, Impl, ImplItem, + ImplItemKind, Item, ItemKind, LangItem, MatchSource, Node, Param, Pat, PatKind, Path, PathSegment, QPath, + TraitItem, TraitItemKind, TraitRef, TyKind, }; -use rustc_infer::infer::TyCtxtInferExt; use rustc_lint::{LateContext, Level, Lint, LintContext}; use rustc_middle::hir::exports::Export; use rustc_middle::hir::map::Map; -use rustc_middle::ty::subst::{GenericArg, GenericArgKind}; -use rustc_middle::ty::{self, layout::IntegerExt, DefIdTree, Ty, TyCtxt, TypeFoldable}; +use rustc_middle::ty as rustc_ty; +use rustc_middle::ty::{layout::IntegerExt, DefIdTree, Ty, TyCtxt, TypeFoldable}; use rustc_semver::RustcVersion; use rustc_session::Session; use rustc_span::hygiene::{ExpnKind, MacroKind}; use rustc_span::source_map::original_sp; use rustc_span::sym; use rustc_span::symbol::{kw, Symbol}; -use rustc_span::{BytePos, Pos, Span, DUMMY_SP}; +use rustc_span::{Span, DUMMY_SP}; use rustc_target::abi::Integer; -use rustc_trait_selection::traits::query::normalize::AtExt; -use smallvec::SmallVec; use crate::consts::{constant, Constant}; +use crate::ty::is_recursively_primitive_type; pub fn parse_msrv(msrv: &str, sess: Option<&Session>, span: Option<Span>) -> Option<RustcVersion> { if let Ok(version) = RustcVersion::parse(msrv) { @@ -134,6 +133,58 @@ pub fn differing_macro_contexts(lhs: Span, rhs: Span) -> bool { rhs.ctxt() != lhs.ctxt() } +/// If the given expression is a local binding, find the initializer expression. +/// If that initializer expression is another local binding, find its initializer again. +/// This process repeats as long as possible (but usually no more than once). Initializer +/// expressions with adjustments are ignored. If this is not desired, use [`find_binding_init`] +/// instead. +/// +/// Examples: +/// ```ignore +/// let abc = 1; +/// // ^ output +/// let def = abc; +/// dbg!(def) +/// // ^^^ input +/// +/// // or... +/// let abc = 1; +/// let def = abc + 2; +/// // ^^^^^^^ output +/// dbg!(def) +/// // ^^^ input +/// ``` +pub fn expr_or_init<'a, 'b, 'tcx: 'b>(cx: &LateContext<'tcx>, mut expr: &'a Expr<'b>) -> &'a Expr<'b> { + while let Some(init) = path_to_local(expr) + .and_then(|id| find_binding_init(cx, id)) + .filter(|init| cx.typeck_results().expr_adjustments(init).is_empty()) + { + expr = init; + } + expr +} + +/// Finds the initializer expression for a local binding. Returns `None` if the binding is mutable. +/// By only considering immutable bindings, we guarantee that the returned expression represents the +/// value of the binding wherever it is referenced. +/// +/// Example: For `let x = 1`, if the `HirId` of `x` is provided, the `Expr` `1` is returned. +/// Note: If you have an expression that references a binding `x`, use `path_to_local` to get the +/// canonical binding `HirId`. +pub fn find_binding_init<'tcx>(cx: &LateContext<'tcx>, hir_id: HirId) -> Option<&'tcx Expr<'tcx>> { + let hir = cx.tcx.hir(); + if_chain! { + if let Some(Node::Binding(pat)) = hir.find(hir_id); + if matches!(pat.kind, PatKind::Binding(BindingAnnotation::Unannotated, ..)); + let parent = hir.get_parent_node(hir_id); + if let Some(Node::Local(local)) = hir.find(parent); + then { + return local.init; + } + } + None +} + /// Returns `true` if the given `NodeId` is inside a constant context /// /// # Example @@ -181,61 +232,76 @@ pub fn in_macro(span: Span) -> bool { } } -// If the snippet is empty, it's an attribute that was inserted during macro -// expansion and we want to ignore those, because they could come from external -// sources that the user has no control over. -// For some reason these attributes don't have any expansion info on them, so -// we have to check it this way until there is a better way. -pub fn is_present_in_source<T: LintContext>(cx: &T, span: Span) -> bool { - if let Some(snippet) = snippet_opt(cx, span) { - if snippet.is_empty() { - return false; - } - } - true -} - /// Checks if given pattern is a wildcard (`_`) pub fn is_wild<'tcx>(pat: &impl std::ops::Deref<Target = Pat<'tcx>>) -> bool { matches!(pat.kind, PatKind::Wild) } -/// Checks if type is struct, enum or union type with the given def path. -/// -/// If the type is a diagnostic item, use `is_type_diagnostic_item` instead. -/// If you change the signature, remember to update the internal lint `MatchTypeOnDiagItem` -pub fn match_type(cx: &LateContext<'_>, ty: Ty<'_>, path: &[&str]) -> bool { - match ty.kind() { - ty::Adt(adt, _) => match_def_path(cx, adt.did, path), - _ => false, - } -} +/// 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>> { + let ty = get_qpath_generic_tys(qpath).next()?; -/// Checks if the type is equal to a diagnostic item -/// -/// If you change the signature, remember to update the internal lint `MatchTypeOnDiagItem` -pub fn is_type_diagnostic_item(cx: &LateContext<'_>, ty: Ty<'_>, diag_item: Symbol) -> bool { - match ty.kind() { - ty::Adt(adt, _) => cx.tcx.is_diagnostic_item(diag_item, adt.did), - _ => false, + if let TyKind::Path(qpath) = &ty.kind { + cx.qpath_res(qpath, ty.hir_id) + .opt_def_id() + .map_or(false, |id| { + cx.tcx.lang_items().require(item).map_or(false, |lang_id| id == lang_id) + }) + .then(|| ty) + } else { + None } } -/// Checks if the type is equal to a lang item -pub fn is_type_lang_item(cx: &LateContext<'_>, ty: Ty<'_>, lang_item: hir::LangItem) -> bool { - match ty.kind() { - ty::Adt(adt, _) => cx.tcx.lang_items().require(lang_item).unwrap() == adt.did, - _ => false, +/// Checks if the first type parameter is a diagnostic item. +pub fn is_ty_param_diagnostic_item( + cx: &LateContext<'_>, + qpath: &QPath<'tcx>, + item: Symbol, +) -> Option<&'tcx hir::Ty<'tcx>> { + let ty = get_qpath_generic_tys(qpath).next()?; + + if let TyKind::Path(qpath) = &ty.kind { + cx.qpath_res(qpath, ty.hir_id) + .opt_def_id() + .map_or(false, |id| cx.tcx.is_diagnostic_item(item, id)) + .then(|| ty) + } else { + None } } /// Checks if the method call given in `expr` belongs to the given trait. +/// This is a deprecated function, consider using [`is_trait_method`]. pub fn match_trait_method(cx: &LateContext<'_>, expr: &Expr<'_>, path: &[&str]) -> bool { let def_id = cx.typeck_results().type_dependent_def_id(expr.hir_id).unwrap(); let trt_id = cx.tcx.trait_of_item(def_id); trt_id.map_or(false, |trt_id| match_def_path(cx, trt_id, path)) } +/// Checks if the method call given in `def_id` belongs to a trait or other container with a given +/// diagnostic item +pub fn is_diagnostic_assoc_item(cx: &LateContext<'_>, def_id: DefId, diag_item: Symbol) -> bool { + cx.tcx + .opt_associated_item(def_id) + .and_then(|associated_item| match associated_item.container { + rustc_ty::TraitContainer(assoc_def_id) => Some(assoc_def_id), + rustc_ty::ImplContainer(assoc_def_id) => match cx.tcx.type_of(assoc_def_id).kind() { + rustc_ty::Adt(adt, _) => Some(adt.did), + rustc_ty::Slice(_) => cx.tcx.get_diagnostic_item(sym::slice), // this isn't perfect but it works + _ => None, + }, + }) + .map_or(false, |assoc_def_id| cx.tcx.is_diagnostic_item(diag_item, assoc_def_id)) +} + +/// Checks if the method call given in `expr` belongs to the given trait. +pub fn is_trait_method(cx: &LateContext<'_>, expr: &Expr<'_>, diag_item: Symbol) -> bool { + cx.typeck_results() + .type_dependent_def_id(expr.hir_id) + .map_or(false, |did| is_diagnostic_assoc_item(cx, did, diag_item)) +} + /// Checks if an expression references a variable of the given name. pub fn match_var(expr: &Expr<'_>, var: Symbol) -> bool { if let ExprKind::Path(QPath::Resolved(None, ref path)) = expr.kind { @@ -254,6 +320,27 @@ pub fn last_path_segment<'tcx>(path: &QPath<'tcx>) -> &'tcx PathSegment<'tcx> { } } +pub fn get_qpath_generics(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, + QPath::LangItem(..) => None, + } +} + +pub fn get_qpath_generic_tys(path: &QPath<'tcx>) -> impl Iterator<Item = &'tcx hir::Ty<'tcx>> { + get_qpath_generics(path) + .map_or([].as_ref(), |a| a.args) + .iter() + .filter_map(|a| { + if let hir::GenericArg::Type(ty) = a { + Some(ty) + } else { + None + } + }) +} + pub fn single_segment_path<'tcx>(path: &QPath<'tcx>) -> Option<&'tcx PathSegment<'tcx>> { match *path { QPath::Resolved(_, ref path) => path.segments.get(0), @@ -262,6 +349,10 @@ pub fn single_segment_path<'tcx>(path: &QPath<'tcx>) -> Option<&'tcx PathSegment } } +/// THIS METHOD IS DEPRECATED and will eventually be removed since it does not match against the +/// entire path or resolved `DefId`. Prefer using `match_def_path`. Consider getting a `DefId` from +/// `QPath::Resolved.1.res.opt_def_id()`. +/// /// Matches a `QPath` against a slice of segment string literals. /// /// There is also `match_path` if you are dealing with a `rustc_hir::Path` instead of a @@ -289,6 +380,10 @@ pub fn match_qpath(path: &QPath<'_>, segments: &[&str]) -> bool { } } +/// THIS METHOD IS DEPRECATED and will eventually be removed since it does not match against the +/// entire path or resolved `DefId`. Prefer using `match_def_path`. Consider getting a `DefId` from +/// `QPath::Resolved.1.res.opt_def_id()`. +/// /// Matches a `Path` against a slice of segment string literals. /// /// There is also `match_qpath` if you are dealing with a `rustc_hir::QPath` instead of a @@ -400,26 +495,6 @@ pub fn get_trait_def_id(cx: &LateContext<'_>, path: &[&str]) -> Option<DefId> { } } -/// Checks whether a type implements a trait. -/// See also `get_trait_def_id`. -pub fn implements_trait<'tcx>( - cx: &LateContext<'tcx>, - ty: Ty<'tcx>, - trait_id: DefId, - ty_params: &[GenericArg<'tcx>], -) -> bool { - // Do not check on infer_types to avoid panic in evaluate_obligation. - if ty.has_infer_types() { - return false; - } - let ty = cx.tcx.erase_regions(ty); - if ty.has_escaping_bound_vars() { - return false; - } - let ty_params = cx.tcx.mk_substs(ty_params.iter()); - cx.tcx.type_implements_trait((trait_id, ty, ty_params, cx.param_env)) -} - /// Gets the `hir::TraitRef` of the trait the given method is implemented for. /// /// Use this if you want to find the `TraitRef` of the `Add` trait in this example: @@ -447,14 +522,6 @@ pub fn trait_ref_of_method<'tcx>(cx: &LateContext<'tcx>, hir_id: HirId) -> Optio None } -/// Checks whether this type implements `Drop`. -pub fn has_drop<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool { - match ty.ty_adt_def() { - Some(def) => def.has_dtor(cx.tcx), - None => false, - } -} - /// Returns the method names and argument list of nested method call expressions that make up /// `expr`. method/span lists are sorted with the most recent call first. pub fn method_calls<'tcx>( @@ -550,9 +617,9 @@ pub fn get_pat_name(pat: &Pat<'_>) -> Option<Symbol> { } } -struct ContainsName { - name: Symbol, - result: bool, +pub struct ContainsName { + pub name: Symbol, + pub result: bool, } impl<'tcx> Visitor<'tcx> for ContainsName { @@ -636,177 +703,6 @@ pub fn find_macro_calls(names: &[&str], body: &Body<'_>) -> Vec<Span> { fmc.result } -/// Converts a span to a code snippet if available, otherwise use default. -/// -/// This is useful if you want to provide suggestions for your lint or more generally, if you want -/// to convert a given `Span` to a `str`. -/// -/// # Example -/// ```rust,ignore -/// snippet(cx, expr.span, "..") -/// ``` -pub fn snippet<'a, T: LintContext>(cx: &T, span: Span, default: &'a str) -> Cow<'a, str> { - snippet_opt(cx, span).map_or_else(|| Cow::Borrowed(default), From::from) -} - -/// Same as `snippet`, but it adapts the applicability level by following rules: -/// -/// - Applicability level `Unspecified` will never be changed. -/// - If the span is inside a macro, change the applicability level to `MaybeIncorrect`. -/// - If the default value is used and the applicability level is `MachineApplicable`, change it to -/// `HasPlaceholders` -pub fn snippet_with_applicability<'a, T: LintContext>( - cx: &T, - span: Span, - default: &'a str, - applicability: &mut Applicability, -) -> Cow<'a, str> { - if *applicability != Applicability::Unspecified && span.from_expansion() { - *applicability = Applicability::MaybeIncorrect; - } - snippet_opt(cx, span).map_or_else( - || { - if *applicability == Applicability::MachineApplicable { - *applicability = Applicability::HasPlaceholders; - } - Cow::Borrowed(default) - }, - From::from, - ) -} - -/// Same as `snippet`, but should only be used when it's clear that the input span is -/// not a macro argument. -pub fn snippet_with_macro_callsite<'a, T: LintContext>(cx: &T, span: Span, default: &'a str) -> Cow<'a, str> { - snippet(cx, span.source_callsite(), default) -} - -/// Converts a span to a code snippet. Returns `None` if not available. -pub fn snippet_opt<T: LintContext>(cx: &T, span: Span) -> Option<String> { - cx.sess().source_map().span_to_snippet(span).ok() -} - -/// Converts a span (from a block) to a code snippet if available, otherwise use default. -/// -/// This trims the code of indentation, except for the first line. Use it for blocks or block-like -/// things which need to be printed as such. -/// -/// The `indent_relative_to` arg can be used, to provide a span, where the indentation of the -/// resulting snippet of the given span. -/// -/// # Example -/// -/// ```rust,ignore -/// snippet_block(cx, block.span, "..", None) -/// // where, `block` is the block of the if expr -/// if x { -/// y; -/// } -/// // will return the snippet -/// { -/// y; -/// } -/// ``` -/// -/// ```rust,ignore -/// snippet_block(cx, block.span, "..", Some(if_expr.span)) -/// // where, `block` is the block of the if expr -/// if x { -/// y; -/// } -/// // will return the snippet -/// { -/// y; -/// } // aligned with `if` -/// ``` -/// Note that the first line of the snippet always has 0 indentation. -pub fn snippet_block<'a, T: LintContext>( - cx: &T, - span: Span, - default: &'a str, - indent_relative_to: Option<Span>, -) -> Cow<'a, str> { - let snip = snippet(cx, span, default); - let indent = indent_relative_to.and_then(|s| indent_of(cx, s)); - reindent_multiline(snip, true, indent) -} - -/// Same as `snippet_block`, but adapts the applicability level by the rules of -/// `snippet_with_applicability`. -pub fn snippet_block_with_applicability<'a, T: LintContext>( - cx: &T, - span: Span, - default: &'a str, - indent_relative_to: Option<Span>, - applicability: &mut Applicability, -) -> Cow<'a, str> { - let snip = snippet_with_applicability(cx, span, default, applicability); - let indent = indent_relative_to.and_then(|s| indent_of(cx, s)); - reindent_multiline(snip, true, indent) -} - -/// Returns a new Span that extends the original Span to the first non-whitespace char of the first -/// line. -/// -/// ```rust,ignore -/// let x = (); -/// // ^^ -/// // will be converted to -/// let x = (); -/// // ^^^^^^^^^^ -/// ``` -pub fn first_line_of_span<T: LintContext>(cx: &T, span: Span) -> Span { - first_char_in_first_line(cx, span).map_or(span, |first_char_pos| span.with_lo(first_char_pos)) -} - -fn first_char_in_first_line<T: LintContext>(cx: &T, span: Span) -> Option<BytePos> { - let line_span = line_span(cx, span); - snippet_opt(cx, line_span).and_then(|snip| { - snip.find(|c: char| !c.is_whitespace()) - .map(|pos| line_span.lo() + BytePos::from_usize(pos)) - }) -} - -/// Returns the indentation of the line of a span -/// -/// ```rust,ignore -/// let x = (); -/// // ^^ -- will return 0 -/// let x = (); -/// // ^^ -- will return 4 -/// ``` -pub fn indent_of<T: LintContext>(cx: &T, span: Span) -> Option<usize> { - snippet_opt(cx, line_span(cx, span)).and_then(|snip| snip.find(|c: char| !c.is_whitespace())) -} - -/// Returns the positon just before rarrow -/// -/// ```rust,ignore -/// fn into(self) -> () {} -/// ^ -/// // in case of unformatted code -/// fn into2(self)-> () {} -/// ^ -/// fn into3(self) -> () {} -/// ^ -/// ``` -pub fn position_before_rarrow(s: &str) -> Option<usize> { - s.rfind("->").map(|rpos| { - let mut rpos = rpos; - let chars: Vec<char> = s.chars().collect(); - while rpos > 1 { - if let Some(c) = chars.get(rpos - 1) { - if c.is_whitespace() { - rpos -= 1; - continue; - } - } - break; - } - rpos - }) -} - /// Extends the span to the beginning of the spans line, incl. whitespaces. /// /// ```rust,ignore @@ -824,81 +720,17 @@ fn line_span<T: LintContext>(cx: &T, span: Span) -> Span { Span::new(line_start, span.hi(), span.ctxt()) } -/// Like `snippet_block`, but add braces if the expr is not an `ExprKind::Block`. -/// Also takes an `Option<String>` which can be put inside the braces. -pub fn expr_block<'a, T: LintContext>( - cx: &T, - expr: &Expr<'_>, - option: Option<String>, - default: &'a str, - indent_relative_to: Option<Span>, -) -> Cow<'a, str> { - let code = snippet_block(cx, expr.span, default, indent_relative_to); - let string = option.unwrap_or_default(); - if expr.span.from_expansion() { - Cow::Owned(format!("{{ {} }}", snippet_with_macro_callsite(cx, expr.span, default))) - } else if let ExprKind::Block(_, _) = expr.kind { - Cow::Owned(format!("{}{}", code, string)) - } else if string.is_empty() { - Cow::Owned(format!("{{ {} }}", code)) - } else { - Cow::Owned(format!("{{\n{};\n{}\n}}", code, string)) - } -} - -/// Reindent a multiline string with possibility of ignoring the first line. -#[allow(clippy::needless_pass_by_value)] -pub fn reindent_multiline(s: Cow<'_, str>, ignore_first: bool, indent: Option<usize>) -> Cow<'_, str> { - let s_space = reindent_multiline_inner(&s, ignore_first, indent, ' '); - let s_tab = reindent_multiline_inner(&s_space, ignore_first, indent, '\t'); - reindent_multiline_inner(&s_tab, ignore_first, indent, ' ').into() -} - -fn reindent_multiline_inner(s: &str, ignore_first: bool, indent: Option<usize>, ch: char) -> String { - let x = s - .lines() - .skip(ignore_first as usize) - .filter_map(|l| { - if l.is_empty() { - None - } else { - // ignore empty lines - Some(l.char_indices().find(|&(_, x)| x != ch).unwrap_or((l.len(), ch)).0) - } - }) - .min() - .unwrap_or(0); - let indent = indent.unwrap_or(0); - s.lines() - .enumerate() - .map(|(i, l)| { - if (ignore_first && i == 0) || l.is_empty() { - l.to_owned() - } else if x > indent { - l.split_at(x - indent).1.to_owned() - } else { - " ".repeat(indent - x) + l - } - }) - .collect::<Vec<String>>() - .join("\n") +/// Gets the parent node, if any. +pub fn get_parent_node(tcx: TyCtxt<'_>, id: HirId) -> Option<Node<'_>> { + tcx.hir().parent_iter(id).next().map(|(_, node)| node) } /// Gets the parent expression, if any –- this is useful to constrain a lint. pub fn get_parent_expr<'tcx>(cx: &LateContext<'tcx>, e: &Expr<'_>) -> Option<&'tcx Expr<'tcx>> { - let map = &cx.tcx.hir(); - let hir_id = e.hir_id; - let parent_id = map.get_parent_node(hir_id); - if hir_id == parent_id { - return None; + match get_parent_node(cx.tcx, e.hir_id) { + Some(Node::Expr(parent)) => Some(parent), + _ => None, } - map.find(parent_id).and_then(|node| { - if let Node::Expr(parent) = node { - Some(parent) - } else { - None - } - }) } pub fn get_enclosing_block<'tcx>(cx: &LateContext<'tcx>, hir_id: HirId) -> Option<&'tcx Block<'tcx>> { @@ -923,24 +755,46 @@ pub fn get_enclosing_block<'tcx>(cx: &LateContext<'tcx>, hir_id: HirId) -> Optio }) } -/// Returns the base type for HIR references and pointers. -pub fn walk_ptrs_hir_ty<'tcx>(ty: &'tcx hir::Ty<'tcx>) -> &'tcx hir::Ty<'tcx> { - match ty.kind { - TyKind::Ptr(ref mut_ty) | TyKind::Rptr(_, ref mut_ty) => walk_ptrs_hir_ty(&mut_ty.ty), - _ => ty, +/// Gets the parent node if it's an impl block. +pub fn get_parent_as_impl(tcx: TyCtxt<'_>, id: HirId) -> Option<&Impl<'_>> { + let map = tcx.hir(); + match map.parent_iter(id).next() { + Some(( + _, + Node::Item(Item { + kind: ItemKind::Impl(imp), + .. + }), + )) => Some(imp), + _ => None, } } -/// Returns the base type for references and raw pointers, and count reference -/// depth. -pub fn walk_ptrs_ty_depth(ty: Ty<'_>) -> (Ty<'_>, usize) { - fn inner(ty: Ty<'_>, depth: usize) -> (Ty<'_>, usize) { - match ty.kind() { - ty::Ref(_, ty, _) => inner(ty, depth + 1), - _ => (ty, depth), - } +/// Checks if the given expression is the else clause of either an `if` or `if let` expression. +pub fn is_else_clause(tcx: TyCtxt<'_>, expr: &Expr<'_>) -> bool { + let map = tcx.hir(); + let mut iter = map.parent_iter(expr.hir_id); + match iter.next() { + Some((arm_id, Node::Arm(..))) => matches!( + iter.next(), + Some(( + _, + Node::Expr(Expr { + kind: ExprKind::Match(_, [_, else_arm], MatchSource::IfLetDesugar { .. }), + .. + }) + )) + if else_arm.hir_id == arm_id + ), + Some(( + _, + Node::Expr(Expr { + kind: ExprKind::If(_, _, Some(else_expr)), + .. + }), + )) => else_expr.hir_id == expr.hir_id, + _ => false, } - inner(ty, 0) } /// Checks whether the given expression is a constant integer of the given value. @@ -1038,26 +892,6 @@ pub fn return_ty<'tcx>(cx: &LateContext<'tcx>, fn_item: hir::HirId) -> Ty<'tcx> cx.tcx.erase_late_bound_regions(ret_ty) } -/// Walks into `ty` and returns `true` if any inner type is the same as `other_ty` -pub fn contains_ty(ty: Ty<'_>, other_ty: Ty<'_>) -> bool { - ty.walk().any(|inner| match inner.unpack() { - GenericArgKind::Type(inner_ty) => ty::TyS::same_type(other_ty, inner_ty), - GenericArgKind::Lifetime(_) | GenericArgKind::Const(_) => false, - }) -} - -/// Returns `true` if the given type is an `unsafe` function. -pub fn type_is_unsafe_function<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool { - match ty.kind() { - ty::FnDef(..) | ty::FnPtr(_) => ty.fn_sig(cx.tcx).unsafety() == Unsafety::Unsafe, - _ => false, - } -} - -pub fn is_copy<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool { - ty.is_copy_modulo_regions(cx.tcx.at(DUMMY_SP), cx.param_env) -} - /// Checks if an expression is constructing a tuple-like enum variant or struct pub fn is_ctor_or_promotable_const_function(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { if let ExprKind::Call(ref fun, _) = expr.kind { @@ -1106,11 +940,13 @@ pub fn is_refutable(cx: &LateContext<'_>, pat: &Pat<'_>) -> bool { }, PatKind::Slice(ref head, ref middle, ref tail) => { match &cx.typeck_results().node_type(pat.hir_id).kind() { - ty::Slice(..) => { + rustc_ty::Slice(..) => { // [..] is the only irrefutable slice pattern. !head.is_empty() || middle.is_none() || !tail.is_empty() }, - ty::Array(..) => are_refutable(cx, head.iter().chain(middle).chain(tail.iter()).map(|pat| &**pat)), + rustc_ty::Array(..) => { + are_refutable(cx, head.iter().chain(middle).chain(tail.iter()).map(|pat| &**pat)) + }, _ => { // unreachable!() true @@ -1120,6 +956,16 @@ pub fn is_refutable(cx: &LateContext<'_>, pat: &Pat<'_>) -> bool { } } +/// If the pattern is an `or` pattern, call the function once for each sub pattern. Otherwise, call +/// the function once on the given pattern. +pub fn recurse_or_patterns<'tcx, F: FnMut(&'tcx Pat<'tcx>)>(pat: &'tcx Pat<'tcx>, mut f: F) { + if let PatKind::Or(pats) = pat.kind { + pats.iter().cloned().for_each(f) + } else { + f(pat) + } +} + /// Checks for the `#[automatically_derived]` attribute all `#[derive]`d /// implementations have. pub fn is_automatically_derived(attrs: &[ast::Attribute]) -> bool { @@ -1222,64 +1068,31 @@ pub fn strip_pat_refs<'hir>(mut pat: &'hir Pat<'hir>) -> &'hir Pat<'hir> { pat } -pub fn int_bits(tcx: TyCtxt<'_>, ity: ty::IntTy) -> u64 { +pub fn int_bits(tcx: TyCtxt<'_>, ity: rustc_ty::IntTy) -> u64 { Integer::from_int_ty(&tcx, ity).size().bits() } #[allow(clippy::cast_possible_wrap)] /// Turn a constant int byte representation into an i128 -pub fn sext(tcx: TyCtxt<'_>, u: u128, ity: ty::IntTy) -> i128 { +pub fn sext(tcx: TyCtxt<'_>, u: u128, ity: rustc_ty::IntTy) -> i128 { let amt = 128 - int_bits(tcx, ity); ((u as i128) << amt) >> amt } #[allow(clippy::cast_sign_loss)] /// clip unused bytes -pub fn unsext(tcx: TyCtxt<'_>, u: i128, ity: ty::IntTy) -> u128 { +pub fn unsext(tcx: TyCtxt<'_>, u: i128, ity: rustc_ty::IntTy) -> u128 { let amt = 128 - int_bits(tcx, ity); ((u as u128) << amt) >> amt } /// clip unused bytes -pub fn clip(tcx: TyCtxt<'_>, u: u128, ity: ty::UintTy) -> u128 { +pub fn clip(tcx: TyCtxt<'_>, u: u128, ity: rustc_ty::UintTy) -> u128 { let bits = Integer::from_uint_ty(&tcx, ity).size().bits(); let amt = 128 - bits; (u << amt) >> amt } -/// Removes block comments from the given `Vec` of lines. -/// -/// # Examples -/// -/// ```rust,ignore -/// without_block_comments(vec!["/*", "foo", "*/"]); -/// // => vec![] -/// -/// without_block_comments(vec!["bar", "/*", "foo", "*/"]); -/// // => vec!["bar"] -/// ``` -pub fn without_block_comments(lines: Vec<&str>) -> Vec<&str> { - let mut without = vec![]; - - let mut nest_level = 0; - - for line in lines { - if line.contains("/*") { - nest_level += 1; - continue; - } else if line.contains("*/") { - nest_level -= 1; - continue; - } - - if nest_level == 0 { - without.push(line); - } - } - - without -} - pub fn any_parent_is_automatically_derived(tcx: TyCtxt<'_>, node: HirId) -> bool { let map = &tcx.hir(); let mut prev_enclosing_node = None; @@ -1294,47 +1107,6 @@ pub fn any_parent_is_automatically_derived(tcx: TyCtxt<'_>, node: HirId) -> bool false } -/// Returns true if ty has `iter` or `iter_mut` methods -pub fn has_iter_method(cx: &LateContext<'_>, probably_ref_ty: Ty<'_>) -> Option<Symbol> { - // FIXME: instead of this hard-coded list, we should check if `<adt>::iter` - // exists and has the desired signature. Unfortunately FnCtxt is not exported - // so we can't use its `lookup_method` method. - let into_iter_collections: &[Symbol] = &[ - sym::vec_type, - sym::option_type, - sym::result_type, - sym::BTreeMap, - sym::BTreeSet, - sym::vecdeque_type, - sym::LinkedList, - sym::BinaryHeap, - sym::hashset_type, - sym::hashmap_type, - sym::PathBuf, - sym::Path, - sym::Receiver, - ]; - - let ty_to_check = match probably_ref_ty.kind() { - ty::Ref(_, ty_to_check, _) => ty_to_check, - _ => probably_ref_ty, - }; - - let def_id = match ty_to_check.kind() { - ty::Array(..) => return Some(sym::array), - ty::Slice(..) => return Some(sym::slice), - ty::Adt(adt, _) => adt.did, - _ => return None, - }; - - for &name in into_iter_collections { - if cx.tcx.is_diagnostic_item(name, def_id) { - return Some(cx.tcx.item_name(def_id)); - } - } - None -} - /// Matches a function call with the given path and returns the arguments. /// /// Usage: @@ -1359,15 +1131,6 @@ pub fn match_function_call<'tcx>( None } -/// Checks if `Ty` is normalizable. This function is useful -/// to avoid crashes on `layout_of`. -pub fn is_normalizable<'tcx>(cx: &LateContext<'tcx>, param_env: ty::ParamEnv<'tcx>, ty: Ty<'tcx>) -> bool { - cx.tcx.infer_ctxt().enter(|infcx| { - let cause = rustc_middle::traits::ObligationCause::dummy(); - infcx.at(&cause, param_env).normalize(ty).is_ok() - }) -} - pub fn match_def_path<'tcx>(cx: &LateContext<'tcx>, did: DefId, syms: &[&str]) -> bool { // We have to convert `syms` to `&[Symbol]` here because rustc's `match_def_path` // accepts only that. We should probably move to Symbols in Clippy as well. @@ -1397,11 +1160,9 @@ pub fn match_panic_def_id(cx: &LateContext<'_>, did: DefId) -> bool { /// sequence of `if/else`. /// E.g., this returns `([a, b], [c, d, e])` for the expression /// `if a { c } else if b { d } else { e }`. -pub fn if_sequence<'tcx>( - mut expr: &'tcx Expr<'tcx>, -) -> (SmallVec<[&'tcx Expr<'tcx>; 1]>, SmallVec<[&'tcx Block<'tcx>; 1]>) { - let mut conds = SmallVec::new(); - let mut blocks: SmallVec<[&Block<'_>; 1]> = SmallVec::new(); +pub fn if_sequence<'tcx>(mut expr: &'tcx Expr<'tcx>) -> (Vec<&'tcx Expr<'tcx>>, Vec<&'tcx Block<'tcx>>) { + let mut conds = Vec::new(); + let mut blocks: Vec<&Block<'_>> = Vec::new(); while let ExprKind::If(ref cond, ref then_expr, ref else_expr) = expr.kind { conds.push(&**cond); @@ -1428,6 +1189,8 @@ pub fn if_sequence<'tcx>( (conds, blocks) } +/// This function returns true if the given expression is the `else` or `if else` part of an if +/// statement pub fn parent_node_is_if_expr(expr: &Expr<'_>, cx: &LateContext<'_>) -> bool { let map = cx.tcx.hir(); let parent_id = map.get_parent_node(expr.hir_id); @@ -1441,54 +1204,9 @@ pub fn parent_node_is_if_expr(expr: &Expr<'_>, cx: &LateContext<'_>) -> bool { ) } -// Finds the attribute with the given name, if any -pub fn attr_by_name<'a>(attrs: &'a [Attribute], name: &'_ str) -> Option<&'a Attribute> { - attrs - .iter() - .find(|attr| attr.ident().map_or(false, |ident| ident.as_str() == name)) -} - // Finds the `#[must_use]` attribute, if any pub fn must_use_attr(attrs: &[Attribute]) -> Option<&Attribute> { - attr_by_name(attrs, "must_use") -} - -// Returns whether the type has #[must_use] attribute -pub fn is_must_use_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool { - match ty.kind() { - ty::Adt(ref adt, _) => must_use_attr(&cx.tcx.get_attrs(adt.did)).is_some(), - ty::Foreign(ref did) => must_use_attr(&cx.tcx.get_attrs(*did)).is_some(), - ty::Slice(ref ty) - | ty::Array(ref ty, _) - | ty::RawPtr(ty::TypeAndMut { ref ty, .. }) - | ty::Ref(_, ref ty, _) => { - // for the Array case we don't need to care for the len == 0 case - // because we don't want to lint functions returning empty arrays - is_must_use_ty(cx, *ty) - }, - ty::Tuple(ref substs) => substs.types().any(|ty| is_must_use_ty(cx, ty)), - ty::Opaque(ref def_id, _) => { - for (predicate, _) in cx.tcx.explicit_item_bounds(*def_id) { - if let ty::PredicateKind::Trait(trait_predicate, _) = predicate.kind().skip_binder() { - if must_use_attr(&cx.tcx.get_attrs(trait_predicate.trait_ref.def_id)).is_some() { - return true; - } - } - } - false - }, - ty::Dynamic(binder, _) => { - for predicate in binder.iter() { - if let ty::ExistentialPredicate::Trait(ref trait_ref) = predicate.skip_binder() { - if must_use_attr(&cx.tcx.get_attrs(trait_ref.def_id)).is_some() { - return true; - } - } - } - false - }, - _ => false, - } + attrs.iter().find(|a| a.has_name(sym::must_use)) } // check if expr is calling method or function with #[must_use] attribute @@ -1510,8 +1228,8 @@ pub fn is_must_use_func_call(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { did.map_or(false, |did| must_use_attr(&cx.tcx.get_attrs(did)).is_some()) } -pub fn is_no_std_crate(krate: &Crate<'_>) -> bool { - krate.item.attrs.iter().any(|attr| { +pub fn is_no_std_crate(cx: &LateContext<'_>) -> bool { + cx.tcx.hir().attrs(hir::CRATE_HIR_ID).iter().any(|attr| { if let ast::AttrKind::Normal(ref attr, _) = attr.kind { attr.path == sym::no_std } else { @@ -1546,12 +1264,12 @@ pub fn is_trait_impl_item(cx: &LateContext<'_>, hir_id: HirId) -> bool { /// ``` pub fn fn_has_unsatisfiable_preds(cx: &LateContext<'_>, did: DefId) -> bool { use rustc_trait_selection::traits; - let predicates = - cx.tcx - .predicates_of(did) - .predicates - .iter() - .filter_map(|(p, _)| if p.is_global() { Some(*p) } else { None }); + let predicates = cx + .tcx + .predicates_of(did) + .predicates + .iter() + .filter_map(|(p, _)| if p.is_global() { Some(*p) } else { None }); traits::impossible_predicates( cx.tcx, traits::elaborate_predicates(cx.tcx, predicates) @@ -1576,6 +1294,16 @@ pub fn fn_def_id(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<DefId> { } } +/// This function checks if any of the lints in the slice is enabled for the provided `HirId`. +/// A lint counts as enabled with any of the levels: `Level::Forbid` | `Level::Deny` | `Level::Warn` +/// +/// ```ignore +/// #[deny(clippy::YOUR_AWESOME_LINT)] +/// println!("Hello, World!"); // <- Clippy code: run_lints(cx, &[YOUR_AWESOME_LINT], id) == true +/// +/// #[allow(clippy::YOUR_AWESOME_LINT)] +/// println!("See you soon!"); // <- Clippy code: run_lints(cx, &[YOUR_AWESOME_LINT], id) == false +/// ``` pub fn run_lints(cx: &LateContext<'_>, lints: &[&'static Lint], id: HirId) -> bool { lints.iter().any(|lint| { matches!( @@ -1585,18 +1313,6 @@ pub fn run_lints(cx: &LateContext<'_>, lints: &[&'static Lint], id: HirId) -> bo }) } -/// Returns true iff the given type is a primitive (a bool or char, any integer or floating-point -/// number type, a str, or an array, slice, or tuple of those types). -pub fn is_recursively_primitive_type(ty: Ty<'_>) -> bool { - match ty.kind() { - ty::Bool | ty::Char | ty::Int(_) | ty::Uint(_) | ty::Float(_) | ty::Str => true, - ty::Ref(_, inner, _) if *inner.kind() == ty::Str => true, - ty::Array(inner_type, _) | ty::Slice(inner_type) => is_recursively_primitive_type(inner_type), - ty::Tuple(inner_types) => inner_types.types().all(is_recursively_primitive_type), - _ => false, - } -} - /// Returns Option<String> where String is a textual representation of the type encapsulated in the /// slice iff the given expression is a slice of primitives (as defined in the /// `is_recursively_primitive_type` function) and None otherwise. @@ -1604,9 +1320,9 @@ pub fn is_slice_of_primitives(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<S let expr_type = cx.typeck_results().expr_ty_adjusted(expr); let expr_kind = expr_type.kind(); let is_primitive = match expr_kind { - ty::Slice(element_type) => is_recursively_primitive_type(element_type), - ty::Ref(_, inner_ty, _) if matches!(inner_ty.kind(), &ty::Slice(_)) => { - if let ty::Slice(element_type) = inner_ty.kind() { + rustc_ty::Slice(element_type) => is_recursively_primitive_type(element_type), + rustc_ty::Ref(_, inner_ty, _) if matches!(inner_ty.kind(), &rustc_ty::Slice(_)) => { + if let rustc_ty::Slice(element_type) = inner_ty.kind() { is_recursively_primitive_type(element_type) } else { unreachable!() @@ -1619,9 +1335,9 @@ pub fn is_slice_of_primitives(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<S // if we have wrappers like Array, Slice or Tuple, print these // and get the type enclosed in the slice ref match expr_type.peel_refs().walk().nth(1).unwrap().expect_ty().kind() { - ty::Slice(..) => return Some("slice".into()), - ty::Array(..) => return Some("array".into()), - ty::Tuple(..) => return Some("tuple".into()), + rustc_ty::Slice(..) => return Some("slice".into()), + rustc_ty::Array(..) => return Some("array".into()), + rustc_ty::Tuple(..) => return Some("tuple".into()), _ => { // is_recursively_primitive_type() should have taken care // of the rest and we can rely on the type that is found @@ -1705,32 +1421,6 @@ pub fn peel_hir_expr_refs(expr: &'a Expr<'a>) -> (&'a Expr<'a>, usize) { f(expr, 0) } -/// Peels off all references on the type. Returns the underlying type and the number of references -/// removed. -pub fn peel_mid_ty_refs(ty: Ty<'_>) -> (Ty<'_>, usize) { - fn peel(ty: Ty<'_>, count: usize) -> (Ty<'_>, usize) { - if let ty::Ref(_, ty, _) = ty.kind() { - peel(ty, count + 1) - } else { - (ty, count) - } - } - peel(ty, 0) -} - -/// Peels off all references on the type.Returns the underlying type, the number of references -/// removed, and whether the pointer is ultimately mutable or not. -pub fn peel_mid_ty_refs_is_mutable(ty: Ty<'_>) -> (Ty<'_>, usize, Mutability) { - fn f(ty: Ty<'_>, count: usize, mutability: Mutability) -> (Ty<'_>, usize, Mutability) { - match ty.kind() { - ty::Ref(_, ty, Mutability::Mut) => f(ty, count + 1, mutability), - ty::Ref(_, ty, Mutability::Not) => f(ty, count + 1, Mutability::Not), - _ => (ty, count, mutability), - } - } - f(ty, 0, Mutability::Mut) -} - #[macro_export] macro_rules! unwrap_cargo_metadata { ($cx: ident, $lint: ident, $deps: expr) => {{ @@ -1784,100 +1474,3 @@ pub fn is_some_ctor(cx: &LateContext<'_>, res: Res) -> bool { } false } - -#[cfg(test)] -mod test { - use super::{reindent_multiline, without_block_comments}; - - #[test] - fn test_reindent_multiline_single_line() { - assert_eq!("", reindent_multiline("".into(), false, None)); - assert_eq!("...", reindent_multiline("...".into(), false, None)); - assert_eq!("...", reindent_multiline(" ...".into(), false, None)); - assert_eq!("...", reindent_multiline("\t...".into(), false, None)); - assert_eq!("...", reindent_multiline("\t\t...".into(), false, None)); - } - - #[test] - #[rustfmt::skip] - fn test_reindent_multiline_block() { - assert_eq!("\ - if x { - y - } else { - z - }", reindent_multiline(" if x { - y - } else { - z - }".into(), false, None)); - assert_eq!("\ - if x { - \ty - } else { - \tz - }", reindent_multiline(" if x { - \ty - } else { - \tz - }".into(), false, None)); - } - - #[test] - #[rustfmt::skip] - fn test_reindent_multiline_empty_line() { - assert_eq!("\ - if x { - y - - } else { - z - }", reindent_multiline(" if x { - y - - } else { - z - }".into(), false, None)); - } - - #[test] - #[rustfmt::skip] - fn test_reindent_multiline_lines_deeper() { - assert_eq!("\ - if x { - y - } else { - z - }", reindent_multiline("\ - if x { - y - } else { - z - }".into(), true, Some(8))); - } - - #[test] - fn test_without_block_comments_lines_without_block_comments() { - let result = without_block_comments(vec!["/*", "", "*/"]); - println!("result: {:?}", result); - assert!(result.is_empty()); - - let result = without_block_comments(vec!["", "/*", "", "*/", "#[crate_type = \"lib\"]", "/*", "", "*/", ""]); - assert_eq!(result, vec!["", "#[crate_type = \"lib\"]", ""]); - - let result = without_block_comments(vec!["/* rust", "", "*/"]); - assert!(result.is_empty()); - - let result = without_block_comments(vec!["/* one-line comment */"]); - assert!(result.is_empty()); - - let result = without_block_comments(vec!["/* nested", "/* multi-line", "comment", "*/", "test", "*/"]); - assert!(result.is_empty()); - - let result = without_block_comments(vec!["/* nested /* inline /* comment */ test */ */"]); - assert!(result.is_empty()); - - let result = without_block_comments(vec!["foo", "bar", "baz"]); - assert_eq!(result, vec!["foo", "bar", "baz"]); - } -} diff --git a/src/tools/clippy/clippy_utils/src/numeric_literal.rs b/src/tools/clippy/clippy_utils/src/numeric_literal.rs index d02603d7702c7..268bc5b320533 100644 --- a/src/tools/clippy/clippy_utils/src/numeric_literal.rs +++ b/src/tools/clippy/clippy_utils/src/numeric_literal.rs @@ -1,4 +1,5 @@ use rustc_ast::ast::{Lit, LitFloatType, LitIntType, LitKind}; +use std::iter; #[derive(Debug, PartialEq, Copy, Clone)] pub enum Radix { @@ -192,7 +193,7 @@ impl<'a> NumericLiteral<'a> { } } - for (c, i) in digits.zip((0..group_size).cycle()) { + for (c, i) in iter::zip(digits, (0..group_size).cycle()) { if i == 0 { output.push('_'); } diff --git a/src/tools/clippy/clippy_utils/src/paths.rs b/src/tools/clippy/clippy_utils/src/paths.rs index c2da1f9b7c9f9..3b4c4070c0ed3 100644 --- a/src/tools/clippy/clippy_utils/src/paths.rs +++ b/src/tools/clippy/clippy_utils/src/paths.rs @@ -12,11 +12,9 @@ pub(super) const BEGIN_PANIC: [&str; 3] = ["std", "panicking", "begin_panic"]; pub(super) const BEGIN_PANIC_FMT: [&str; 3] = ["std", "panicking", "begin_panic_fmt"]; pub const BINARY_HEAP: [&str; 4] = ["alloc", "collections", "binary_heap", "BinaryHeap"]; pub const BORROW_TRAIT: [&str; 3] = ["core", "borrow", "Borrow"]; -pub const BOX: [&str; 3] = ["alloc", "boxed", "Box"]; pub const BTREEMAP: [&str; 5] = ["alloc", "collections", "btree", "map", "BTreeMap"]; pub const BTREEMAP_ENTRY: [&str; 6] = ["alloc", "collections", "btree", "map", "entry", "Entry"]; pub const BTREESET: [&str; 5] = ["alloc", "collections", "btree", "set", "BTreeSet"]; -pub const CLONE_TRAIT: [&str; 3] = ["core", "clone", "Clone"]; pub const CLONE_TRAIT_METHOD: [&str; 4] = ["core", "clone", "Clone", "clone"]; pub const CMP_MAX: [&str; 3] = ["core", "cmp", "max"]; pub const CMP_MIN: [&str; 3] = ["core", "cmp", "min"]; @@ -28,6 +26,7 @@ pub const DEFAULT_TRAIT: [&str; 3] = ["core", "default", "Default"]; pub const DEFAULT_TRAIT_METHOD: [&str; 4] = ["core", "default", "Default", "default"]; pub const DEREF_MUT_TRAIT_METHOD: [&str; 5] = ["core", "ops", "deref", "DerefMut", "deref_mut"]; pub const DEREF_TRAIT_METHOD: [&str; 5] = ["core", "ops", "deref", "Deref", "deref"]; +pub const DIR_BUILDER: [&str; 3] = ["std", "fs", "DirBuilder"]; pub const DISPLAY_FMT_METHOD: [&str; 4] = ["core", "fmt", "Display", "fmt"]; pub const DISPLAY_TRAIT: [&str; 3] = ["core", "fmt", "Display"]; pub const DOUBLE_ENDED_ITERATOR: [&str; 4] = ["core", "iter", "traits", "DoubleEndedIterator"]; @@ -43,9 +42,6 @@ pub const FILE_TYPE: [&str; 3] = ["std", "fs", "FileType"]; pub const FMT_ARGUMENTS_NEW_V1: [&str; 4] = ["core", "fmt", "Arguments", "new_v1"]; pub const FMT_ARGUMENTS_NEW_V1_FORMATTED: [&str; 4] = ["core", "fmt", "Arguments", "new_v1_formatted"]; pub const FMT_ARGUMENTV1_NEW: [&str; 4] = ["core", "fmt", "ArgumentV1", "new"]; -pub const FN: [&str; 3] = ["core", "ops", "Fn"]; -pub const FN_MUT: [&str; 3] = ["core", "ops", "FnMut"]; -pub const FN_ONCE: [&str; 3] = ["core", "ops", "FnOnce"]; pub const FROM_FROM: [&str; 4] = ["core", "convert", "From", "from"]; pub const FROM_ITERATOR: [&str; 5] = ["core", "iter", "traits", "collect", "FromIterator"]; pub const FUTURE_FROM_GENERATOR: [&str; 3] = ["core", "future", "from_generator"]; @@ -66,7 +62,6 @@ pub const IO_READ: [&str; 3] = ["std", "io", "Read"]; pub const IO_WRITE: [&str; 3] = ["std", "io", "Write"]; pub const IPADDR_V4: [&str; 4] = ["std", "net", "IpAddr", "V4"]; pub const IPADDR_V6: [&str; 4] = ["std", "net", "IpAddr", "V6"]; -pub const ITERATOR: [&str; 5] = ["core", "iter", "traits", "iterator", "Iterator"]; #[cfg(feature = "internal-lints")] pub const KW_MODULE: [&str; 3] = ["rustc_span", "symbol", "kw"]; #[cfg(feature = "internal-lints")] @@ -89,7 +84,6 @@ pub const OPTION: [&str; 3] = ["core", "option", "Option"]; pub const OPTION_NONE: [&str; 4] = ["core", "option", "Option", "None"]; pub const OPTION_SOME: [&str; 4] = ["core", "option", "Option", "Some"]; pub const ORD: [&str; 3] = ["core", "cmp", "Ord"]; -pub const OS_STRING: [&str; 4] = ["std", "ffi", "os_str", "OsString"]; 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"]; @@ -99,9 +93,10 @@ pub(super) const PANIC_ANY: [&str; 3] = ["std", "panic", "panic_any"]; pub const PARKING_LOT_MUTEX_GUARD: [&str; 2] = ["parking_lot", "MutexGuard"]; pub const PARKING_LOT_RWLOCK_READ_GUARD: [&str; 2] = ["parking_lot", "RwLockReadGuard"]; pub const PARKING_LOT_RWLOCK_WRITE_GUARD: [&str; 2] = ["parking_lot", "RwLockWriteGuard"]; -pub const PATH_BUF: [&str; 3] = ["std", "path", "PathBuf"]; 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"]; +pub const PERMISSIONS_FROM_MODE: [&str; 7] = ["std", "sys", "unix", "ext", "fs", "PermissionsExt", "from_mode"]; pub const POLL: [&str; 4] = ["core", "task", "poll", "Poll"]; pub const POLL_PENDING: [&str; 5] = ["core", "task", "poll", "Poll", "Pending"]; pub const POLL_READY: [&str; 5] = ["core", "task", "poll", "Poll", "Ready"]; @@ -113,7 +108,6 @@ pub const PTR_SLICE_FROM_RAW_PARTS_MUT: [&str; 3] = ["core", "ptr", "slice_from_ pub const PTR_SWAP_NONOVERLAPPING: [&str; 3] = ["core", "ptr", "swap_nonoverlapping"]; pub const PUSH_STR: [&str; 4] = ["alloc", "string", "String", "push_str"]; pub const RANGE_ARGUMENT_TRAIT: [&str; 3] = ["core", "ops", "RangeBounds"]; -pub const RC: [&str; 3] = ["alloc", "rc", "Rc"]; pub const RC_PTR_EQ: [&str; 4] = ["alloc", "rc", "Rc", "ptr_eq"]; pub const REFCELL_REF: [&str; 3] = ["core", "cell", "Ref"]; pub const REFCELL_REFMUT: [&str; 3] = ["core", "cell", "RefMut"]; @@ -141,7 +135,6 @@ pub const STD_CONVERT_IDENTITY: [&str; 3] = ["std", "convert", "identity"]; pub const STD_FS_CREATE_DIR: [&str; 3] = ["std", "fs", "create_dir"]; pub const STD_MEM_TRANSMUTE: [&str; 3] = ["std", "mem", "transmute"]; pub const STD_PTR_NULL: [&str; 3] = ["std", "ptr", "null"]; -pub const STRING: [&str; 3] = ["alloc", "string", "String"]; pub const STRING_AS_MUT_STR: [&str; 4] = ["alloc", "string", "String", "as_mut_str"]; pub const STRING_AS_STR: [&str; 4] = ["alloc", "string", "String", "as_str"]; pub const STR_ENDS_WITH: [&str; 4] = ["core", "str", "<impl str>", "ends_with"]; @@ -160,9 +153,7 @@ pub const SYMBOL_TO_IDENT_STRING: [&str; 4] = ["rustc_span", "symbol", "Symbol", pub const SYM_MODULE: [&str; 3] = ["rustc_span", "symbol", "sym"]; #[cfg(feature = "internal-lints")] pub const SYNTAX_CONTEXT: [&str; 3] = ["rustc_span", "hygiene", "SyntaxContext"]; -pub const TO_OWNED: [&str; 3] = ["alloc", "borrow", "ToOwned"]; pub const TO_OWNED_METHOD: [&str; 4] = ["alloc", "borrow", "ToOwned", "to_owned"]; -pub const TO_STRING: [&str; 3] = ["alloc", "string", "ToString"]; pub const TO_STRING_METHOD: [&str; 4] = ["alloc", "string", "ToString", "to_string"]; pub const TRANSMUTE: [&str; 4] = ["core", "intrinsics", "", "transmute"]; pub const TRY_FROM: [&str; 4] = ["core", "convert", "TryFrom", "try_from"]; diff --git a/src/tools/clippy/clippy_utils/src/ptr.rs b/src/tools/clippy/clippy_utils/src/ptr.rs index baeff08e02cd8..5885cc83560ff 100644 --- a/src/tools/clippy/clippy_utils/src/ptr.rs +++ b/src/tools/clippy/clippy_utils/src/ptr.rs @@ -1,4 +1,5 @@ -use crate::{get_pat_name, match_var, snippet}; +use crate::source::snippet; +use crate::{get_pat_name, match_var}; use rustc_hir::intravisit::{walk_expr, NestedVisitorMap, Visitor}; use rustc_hir::{Body, BodyId, Expr, ExprKind, Param}; use rustc_lint::LateContext; @@ -36,11 +37,7 @@ fn extract_clone_suggestions<'tcx>( abort: false, }; visitor.visit_body(body); - if visitor.abort { - None - } else { - Some(visitor.spans) - } + if visitor.abort { None } else { Some(visitor.spans) } } struct PtrCloneVisitor<'a, 'tcx> { 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 a482017afeb13..b52cbf31e35aa 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 @@ -172,7 +172,7 @@ fn check_rvalue(tcx: TyCtxt<'tcx>, body: &Body<'tcx>, def_id: DefId, rvalue: &Rv } }, // binops are fine on integers - Rvalue::BinaryOp(_, lhs, rhs) | Rvalue::CheckedBinaryOp(_, lhs, rhs) => { + Rvalue::BinaryOp(_, box (lhs, rhs)) | Rvalue::CheckedBinaryOp(_, box (lhs, rhs)) => { check_operand(tcx, lhs, span, body)?; check_operand(tcx, rhs, span, body)?; let ty = lhs.ty(body, tcx); @@ -212,12 +212,17 @@ fn check_statement(tcx: TyCtxt<'tcx>, body: &Body<'tcx>, def_id: DefId, statemen check_rvalue(tcx, body, def_id, rval, span) }, - StatementKind::FakeRead(_, place) | + StatementKind::FakeRead(box (_, place)) => check_place(tcx, *place, span, body), // just an assignment StatementKind::SetDiscriminant { place, .. } => check_place(tcx, **place, span, body), StatementKind::LlvmInlineAsm { .. } => Err((span, "cannot use inline assembly in const fn".into())), + StatementKind::CopyNonOverlapping(box rustc_middle::mir::CopyNonOverlapping { dst, src, count }) => { + check_operand(tcx, dst, span, body)?; + check_operand(tcx, src, span, body)?; + check_operand(tcx, count, span, body) + }, // These are all NOPs StatementKind::StorageLive(_) | StatementKind::StorageDead(_) diff --git a/src/tools/clippy/clippy_utils/src/source.rs b/src/tools/clippy/clippy_utils/src/source.rs new file mode 100644 index 0000000000000..2d794d48dc5ff --- /dev/null +++ b/src/tools/clippy/clippy_utils/src/source.rs @@ -0,0 +1,420 @@ +//! Utils for extracting, inspecting or transforming source code + +#![allow(clippy::module_name_repetitions)] + +use crate::line_span; +use rustc_errors::Applicability; +use rustc_hir::{Expr, ExprKind}; +use rustc_lint::{LateContext, LintContext}; +use rustc_span::hygiene; +use rustc_span::{BytePos, Pos, Span, SyntaxContext}; +use std::borrow::Cow; + +/// Like `snippet_block`, but add braces if the expr is not an `ExprKind::Block`. +/// Also takes an `Option<String>` which can be put inside the braces. +pub fn expr_block<'a, T: LintContext>( + cx: &T, + expr: &Expr<'_>, + option: Option<String>, + default: &'a str, + indent_relative_to: Option<Span>, +) -> Cow<'a, str> { + let code = snippet_block(cx, expr.span, default, indent_relative_to); + let string = option.unwrap_or_default(); + if expr.span.from_expansion() { + Cow::Owned(format!("{{ {} }}", snippet_with_macro_callsite(cx, expr.span, default))) + } else if let ExprKind::Block(_, _) = expr.kind { + Cow::Owned(format!("{}{}", code, string)) + } else if string.is_empty() { + Cow::Owned(format!("{{ {} }}", code)) + } else { + Cow::Owned(format!("{{\n{};\n{}\n}}", code, string)) + } +} + +/// Returns a new Span that extends the original Span to the first non-whitespace char of the first +/// line. +/// +/// ```rust,ignore +/// let x = (); +/// // ^^ +/// // will be converted to +/// let x = (); +/// // ^^^^^^^^^^ +/// ``` +pub fn first_line_of_span<T: LintContext>(cx: &T, span: Span) -> Span { + first_char_in_first_line(cx, span).map_or(span, |first_char_pos| span.with_lo(first_char_pos)) +} + +fn first_char_in_first_line<T: LintContext>(cx: &T, span: Span) -> Option<BytePos> { + let line_span = line_span(cx, span); + snippet_opt(cx, line_span).and_then(|snip| { + snip.find(|c: char| !c.is_whitespace()) + .map(|pos| line_span.lo() + BytePos::from_usize(pos)) + }) +} + +/// Returns the indentation of the line of a span +/// +/// ```rust,ignore +/// let x = (); +/// // ^^ -- will return 0 +/// let x = (); +/// // ^^ -- will return 4 +/// ``` +pub fn indent_of<T: LintContext>(cx: &T, span: Span) -> Option<usize> { + snippet_opt(cx, line_span(cx, span)).and_then(|snip| snip.find(|c: char| !c.is_whitespace())) +} + +// If the snippet is empty, it's an attribute that was inserted during macro +// expansion and we want to ignore those, because they could come from external +// sources that the user has no control over. +// For some reason these attributes don't have any expansion info on them, so +// we have to check it this way until there is a better way. +pub fn is_present_in_source<T: LintContext>(cx: &T, span: Span) -> bool { + if let Some(snippet) = snippet_opt(cx, span) { + if snippet.is_empty() { + return false; + } + } + true +} + +/// Returns the positon just before rarrow +/// +/// ```rust,ignore +/// fn into(self) -> () {} +/// ^ +/// // in case of unformatted code +/// fn into2(self)-> () {} +/// ^ +/// fn into3(self) -> () {} +/// ^ +/// ``` +pub fn position_before_rarrow(s: &str) -> Option<usize> { + s.rfind("->").map(|rpos| { + let mut rpos = rpos; + let chars: Vec<char> = s.chars().collect(); + while rpos > 1 { + if let Some(c) = chars.get(rpos - 1) { + if c.is_whitespace() { + rpos -= 1; + continue; + } + } + break; + } + rpos + }) +} + +/// Reindent a multiline string with possibility of ignoring the first line. +#[allow(clippy::needless_pass_by_value)] +pub fn reindent_multiline(s: Cow<'_, str>, ignore_first: bool, indent: Option<usize>) -> Cow<'_, str> { + let s_space = reindent_multiline_inner(&s, ignore_first, indent, ' '); + let s_tab = reindent_multiline_inner(&s_space, ignore_first, indent, '\t'); + reindent_multiline_inner(&s_tab, ignore_first, indent, ' ').into() +} + +fn reindent_multiline_inner(s: &str, ignore_first: bool, indent: Option<usize>, ch: char) -> String { + let x = s + .lines() + .skip(ignore_first as usize) + .filter_map(|l| { + if l.is_empty() { + None + } else { + // ignore empty lines + Some(l.char_indices().find(|&(_, x)| x != ch).unwrap_or((l.len(), ch)).0) + } + }) + .min() + .unwrap_or(0); + let indent = indent.unwrap_or(0); + s.lines() + .enumerate() + .map(|(i, l)| { + if (ignore_first && i == 0) || l.is_empty() { + l.to_owned() + } else if x > indent { + l.split_at(x - indent).1.to_owned() + } else { + " ".repeat(indent - x) + l + } + }) + .collect::<Vec<String>>() + .join("\n") +} + +/// Converts a span to a code snippet if available, otherwise use default. +/// +/// This is useful if you want to provide suggestions for your lint or more generally, if you want +/// to convert a given `Span` to a `str`. +/// +/// # Example +/// ```rust,ignore +/// snippet(cx, expr.span, "..") +/// ``` +pub fn snippet<'a, T: LintContext>(cx: &T, span: Span, default: &'a str) -> Cow<'a, str> { + snippet_opt(cx, span).map_or_else(|| Cow::Borrowed(default), From::from) +} + +/// Same as `snippet`, but it adapts the applicability level by following rules: +/// +/// - Applicability level `Unspecified` will never be changed. +/// - If the span is inside a macro, change the applicability level to `MaybeIncorrect`. +/// - If the default value is used and the applicability level is `MachineApplicable`, change it to +/// `HasPlaceholders` +pub fn snippet_with_applicability<'a, T: LintContext>( + cx: &T, + span: Span, + default: &'a str, + applicability: &mut Applicability, +) -> Cow<'a, str> { + if *applicability != Applicability::Unspecified && span.from_expansion() { + *applicability = Applicability::MaybeIncorrect; + } + snippet_opt(cx, span).map_or_else( + || { + if *applicability == Applicability::MachineApplicable { + *applicability = Applicability::HasPlaceholders; + } + Cow::Borrowed(default) + }, + From::from, + ) +} + +/// Same as `snippet`, but should only be used when it's clear that the input span is +/// not a macro argument. +pub fn snippet_with_macro_callsite<'a, T: LintContext>(cx: &T, span: Span, default: &'a str) -> Cow<'a, str> { + snippet(cx, span.source_callsite(), default) +} + +/// Converts a span to a code snippet. Returns `None` if not available. +pub fn snippet_opt<T: LintContext>(cx: &T, span: Span) -> Option<String> { + cx.sess().source_map().span_to_snippet(span).ok() +} + +/// Converts a span (from a block) to a code snippet if available, otherwise use default. +/// +/// This trims the code of indentation, except for the first line. Use it for blocks or block-like +/// things which need to be printed as such. +/// +/// The `indent_relative_to` arg can be used, to provide a span, where the indentation of the +/// resulting snippet of the given span. +/// +/// # Example +/// +/// ```rust,ignore +/// snippet_block(cx, block.span, "..", None) +/// // where, `block` is the block of the if expr +/// if x { +/// y; +/// } +/// // will return the snippet +/// { +/// y; +/// } +/// ``` +/// +/// ```rust,ignore +/// snippet_block(cx, block.span, "..", Some(if_expr.span)) +/// // where, `block` is the block of the if expr +/// if x { +/// y; +/// } +/// // will return the snippet +/// { +/// y; +/// } // aligned with `if` +/// ``` +/// Note that the first line of the snippet always has 0 indentation. +pub fn snippet_block<'a, T: LintContext>( + cx: &T, + span: Span, + default: &'a str, + indent_relative_to: Option<Span>, +) -> Cow<'a, str> { + let snip = snippet(cx, span, default); + let indent = indent_relative_to.and_then(|s| indent_of(cx, s)); + reindent_multiline(snip, true, indent) +} + +/// Same as `snippet_block`, but adapts the applicability level by the rules of +/// `snippet_with_applicability`. +pub fn snippet_block_with_applicability<'a, T: LintContext>( + cx: &T, + span: Span, + default: &'a str, + indent_relative_to: Option<Span>, + applicability: &mut Applicability, +) -> Cow<'a, str> { + let snip = snippet_with_applicability(cx, span, default, applicability); + let indent = indent_relative_to.and_then(|s| indent_of(cx, s)); + reindent_multiline(snip, true, indent) +} + +/// Same as `snippet_with_applicability`, but first walks the span up to the given context. This +/// will result in the macro call, rather then the expansion, if the span is from a child context. +/// If the span is not from a child context, it will be used directly instead. +/// +/// e.g. Given the expression `&vec![]`, getting a snippet from the span for `vec![]` as a HIR node +/// would result in `box []`. If given the context of the address of expression, this function will +/// correctly get a snippet of `vec![]`. +/// +/// This will also return whether or not the snippet is a macro call. +pub fn snippet_with_context( + cx: &LateContext<'_>, + span: Span, + outer: SyntaxContext, + default: &'a str, + applicability: &mut Applicability, +) -> (Cow<'a, str>, bool) { + let outer_span = hygiene::walk_chain(span, outer); + let (span, is_macro_call) = if outer_span.ctxt() == outer { + (outer_span, span.ctxt() != outer) + } else { + // The span is from a macro argument, and the outer context is the macro using the argument + if *applicability != Applicability::Unspecified { + *applicability = Applicability::MaybeIncorrect; + } + // TODO: get the argument span. + (span, false) + }; + + ( + snippet_with_applicability(cx, span, default, applicability), + is_macro_call, + ) +} + +/// Removes block comments from the given `Vec` of lines. +/// +/// # Examples +/// +/// ```rust,ignore +/// without_block_comments(vec!["/*", "foo", "*/"]); +/// // => vec![] +/// +/// without_block_comments(vec!["bar", "/*", "foo", "*/"]); +/// // => vec!["bar"] +/// ``` +pub fn without_block_comments(lines: Vec<&str>) -> Vec<&str> { + let mut without = vec![]; + + let mut nest_level = 0; + + for line in lines { + if line.contains("/*") { + nest_level += 1; + continue; + } else if line.contains("*/") { + nest_level -= 1; + continue; + } + + if nest_level == 0 { + without.push(line); + } + } + + without +} + +#[cfg(test)] +mod test { + use super::{reindent_multiline, without_block_comments}; + + #[test] + fn test_reindent_multiline_single_line() { + assert_eq!("", reindent_multiline("".into(), false, None)); + assert_eq!("...", reindent_multiline("...".into(), false, None)); + assert_eq!("...", reindent_multiline(" ...".into(), false, None)); + assert_eq!("...", reindent_multiline("\t...".into(), false, None)); + assert_eq!("...", reindent_multiline("\t\t...".into(), false, None)); + } + + #[test] + #[rustfmt::skip] + fn test_reindent_multiline_block() { + assert_eq!("\ + if x { + y + } else { + z + }", reindent_multiline(" if x { + y + } else { + z + }".into(), false, None)); + assert_eq!("\ + if x { + \ty + } else { + \tz + }", reindent_multiline(" if x { + \ty + } else { + \tz + }".into(), false, None)); + } + + #[test] + #[rustfmt::skip] + fn test_reindent_multiline_empty_line() { + assert_eq!("\ + if x { + y + + } else { + z + }", reindent_multiline(" if x { + y + + } else { + z + }".into(), false, None)); + } + + #[test] + #[rustfmt::skip] + fn test_reindent_multiline_lines_deeper() { + assert_eq!("\ + if x { + y + } else { + z + }", reindent_multiline("\ + if x { + y + } else { + z + }".into(), true, Some(8))); + } + + #[test] + fn test_without_block_comments_lines_without_block_comments() { + let result = without_block_comments(vec!["/*", "", "*/"]); + println!("result: {:?}", result); + assert!(result.is_empty()); + + let result = without_block_comments(vec!["", "/*", "", "*/", "#[crate_type = \"lib\"]", "/*", "", "*/", ""]); + assert_eq!(result, vec!["", "#[crate_type = \"lib\"]", ""]); + + let result = without_block_comments(vec!["/* rust", "", "*/"]); + assert!(result.is_empty()); + + let result = without_block_comments(vec!["/* one-line comment */"]); + assert!(result.is_empty()); + + let result = without_block_comments(vec!["/* nested", "/* multi-line", "comment", "*/", "test", "*/"]); + assert!(result.is_empty()); + + let result = without_block_comments(vec!["/* nested /* inline /* comment */ test */ */"]); + assert!(result.is_empty()); + + let result = without_block_comments(vec!["foo", "bar", "baz"]); + assert_eq!(result, vec!["foo", "bar", "baz"]); + } +} diff --git a/src/tools/clippy/clippy_utils/src/sugg.rs b/src/tools/clippy/clippy_utils/src/sugg.rs index d4f6f4281d368..0633a19391f86 100644 --- a/src/tools/clippy/clippy_utils/src/sugg.rs +++ b/src/tools/clippy/clippy_utils/src/sugg.rs @@ -1,7 +1,8 @@ //! Contains utility functions to generate suggestions. #![deny(clippy::missing_docs_in_private_items)] -use crate::{higher, snippet, snippet_opt, snippet_with_macro_callsite}; +use crate::higher; +use crate::source::{snippet, snippet_opt, snippet_with_macro_callsite}; use rustc_ast::util::parser::AssocOp; use rustc_ast::{ast, token}; use rustc_ast_pretty::pprust::token_kind_to_string; @@ -266,17 +267,44 @@ impl<'a> Sugg<'a> { Sugg::NonParen(..) => self, // `(x)` and `(x).y()` both don't need additional parens. Sugg::MaybeParen(sugg) => { - if sugg.starts_with('(') && sugg.ends_with(')') { + if has_enclosing_paren(&sugg) { Sugg::MaybeParen(sugg) } else { Sugg::NonParen(format!("({})", sugg).into()) } }, - Sugg::BinOp(_, sugg) => Sugg::NonParen(format!("({})", sugg).into()), + Sugg::BinOp(_, sugg) => { + if has_enclosing_paren(&sugg) { + Sugg::NonParen(sugg) + } else { + Sugg::NonParen(format!("({})", sugg).into()) + } + }, } } } +/// Return `true` if `sugg` is enclosed in parenthesis. +fn has_enclosing_paren(sugg: impl AsRef<str>) -> bool { + let mut chars = sugg.as_ref().chars(); + if let Some('(') = chars.next() { + let mut depth = 1; + while let Some(c) = chars.next() { + if c == '(' { + depth += 1; + } else if c == ')' { + depth -= 1; + } + if depth == 0 { + break; + } + } + chars.next().is_none() + } else { + false + } +} + // Copied from the rust standart library, and then edited macro_rules! forward_binop_impls_to_ref { (impl $imp:ident, $method:ident for $t:ty, type Output = $o:ty) => { @@ -667,6 +695,8 @@ impl<T: LintContext> DiagnosticBuilderExt<T> for rustc_errors::DiagnosticBuilder #[cfg(test)] mod test { use super::Sugg; + + use rustc_ast::util::parser::AssocOp; use std::borrow::Cow; const SUGGESTION: Sugg<'static> = Sugg::NonParen(Cow::Borrowed("function_call()")); @@ -680,4 +710,13 @@ mod test { fn blockify_transforms_sugg_into_a_block() { assert_eq!("{ function_call() }", SUGGESTION.blockify().to_string()); } + + #[test] + fn binop_maybe_par() { + let sugg = Sugg::BinOp(AssocOp::Add, "(1 + 1)".into()); + assert_eq!("(1 + 1)", sugg.maybe_par().to_string()); + + let sugg = Sugg::BinOp(AssocOp::Add, "(1 + 1) + (1 + 1)".into()); + assert_eq!("((1 + 1) + (1 + 1))", sugg.maybe_par().to_string()); + } } diff --git a/src/tools/clippy/clippy_utils/src/ty.rs b/src/tools/clippy/clippy_utils/src/ty.rs new file mode 100644 index 0000000000000..807cfbc4c7f1f --- /dev/null +++ b/src/tools/clippy/clippy_utils/src/ty.rs @@ -0,0 +1,305 @@ +//! Util methods for [`rustc_middle::ty`] + +#![allow(clippy::module_name_repetitions)] + +use std::collections::HashMap; + +use rustc_ast::ast::Mutability; +use rustc_hir as hir; +use rustc_hir::def_id::DefId; +use rustc_hir::{TyKind, Unsafety}; +use rustc_infer::infer::TyCtxtInferExt; +use rustc_lint::LateContext; +use rustc_middle::ty::subst::{GenericArg, GenericArgKind}; +use rustc_middle::ty::{self, AdtDef, IntTy, Ty, TypeFoldable, UintTy}; +use rustc_span::sym; +use rustc_span::symbol::Symbol; +use rustc_span::DUMMY_SP; +use rustc_trait_selection::traits::query::normalize::AtExt; + +use crate::{match_def_path, must_use_attr}; + +pub fn is_copy<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool { + ty.is_copy_modulo_regions(cx.tcx.at(DUMMY_SP), cx.param_env) +} + +/// Checks whether a type can be partially moved. +pub fn can_partially_move_ty(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool { + if has_drop(cx, ty) || is_copy(cx, ty) { + return false; + } + match ty.kind() { + ty::Param(_) => false, + ty::Adt(def, subs) => def.all_fields().any(|f| !is_copy(cx, f.ty(cx.tcx, subs))), + _ => true, + } +} + +/// Walks into `ty` and returns `true` if any inner type is the same as `other_ty` +pub fn contains_ty(ty: Ty<'_>, other_ty: Ty<'_>) -> bool { + ty.walk().any(|inner| match inner.unpack() { + GenericArgKind::Type(inner_ty) => ty::TyS::same_type(other_ty, inner_ty), + GenericArgKind::Lifetime(_) | GenericArgKind::Const(_) => false, + }) +} + +/// Walks into `ty` and returns `true` if any inner type is an instance of the given adt +/// constructor. +pub fn contains_adt_constructor(ty: Ty<'_>, adt: &AdtDef) -> bool { + ty.walk().any(|inner| match inner.unpack() { + GenericArgKind::Type(inner_ty) => inner_ty.ty_adt_def() == Some(adt), + GenericArgKind::Lifetime(_) | GenericArgKind::Const(_) => false, + }) +} + +/// Returns true if ty has `iter` or `iter_mut` methods +pub fn has_iter_method(cx: &LateContext<'_>, probably_ref_ty: Ty<'_>) -> Option<Symbol> { + // FIXME: instead of this hard-coded list, we should check if `<adt>::iter` + // exists and has the desired signature. Unfortunately FnCtxt is not exported + // so we can't use its `lookup_method` method. + let into_iter_collections: &[Symbol] = &[ + sym::vec_type, + sym::option_type, + sym::result_type, + sym::BTreeMap, + sym::BTreeSet, + sym::vecdeque_type, + sym::LinkedList, + sym::BinaryHeap, + sym::hashset_type, + sym::hashmap_type, + sym::PathBuf, + sym::Path, + sym::Receiver, + ]; + + let ty_to_check = match probably_ref_ty.kind() { + ty::Ref(_, ty_to_check, _) => ty_to_check, + _ => probably_ref_ty, + }; + + let def_id = match ty_to_check.kind() { + ty::Array(..) => return Some(sym::array), + ty::Slice(..) => return Some(sym::slice), + ty::Adt(adt, _) => adt.did, + _ => return None, + }; + + for &name in into_iter_collections { + if cx.tcx.is_diagnostic_item(name, def_id) { + return Some(cx.tcx.item_name(def_id)); + } + } + None +} + +/// Checks whether a type implements a trait. +/// See also `get_trait_def_id`. +pub fn implements_trait<'tcx>( + cx: &LateContext<'tcx>, + ty: Ty<'tcx>, + trait_id: DefId, + ty_params: &[GenericArg<'tcx>], +) -> bool { + // Do not check on infer_types to avoid panic in evaluate_obligation. + if ty.has_infer_types() { + return false; + } + let ty = cx.tcx.erase_regions(ty); + if ty.has_escaping_bound_vars() { + return false; + } + let ty_params = cx.tcx.mk_substs(ty_params.iter()); + cx.tcx.type_implements_trait((trait_id, ty, ty_params, cx.param_env)) +} + +/// Checks whether this type implements `Drop`. +pub fn has_drop<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool { + match ty.ty_adt_def() { + Some(def) => def.has_dtor(cx.tcx), + None => false, + } +} + +// Returns whether the type has #[must_use] attribute +pub fn is_must_use_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool { + match ty.kind() { + ty::Adt(ref adt, _) => must_use_attr(&cx.tcx.get_attrs(adt.did)).is_some(), + ty::Foreign(ref did) => must_use_attr(&cx.tcx.get_attrs(*did)).is_some(), + ty::Slice(ref ty) + | ty::Array(ref ty, _) + | ty::RawPtr(ty::TypeAndMut { ref ty, .. }) + | ty::Ref(_, ref ty, _) => { + // for the Array case we don't need to care for the len == 0 case + // because we don't want to lint functions returning empty arrays + is_must_use_ty(cx, *ty) + }, + ty::Tuple(ref substs) => substs.types().any(|ty| is_must_use_ty(cx, ty)), + ty::Opaque(ref def_id, _) => { + for (predicate, _) in cx.tcx.explicit_item_bounds(*def_id) { + if let ty::PredicateKind::Trait(trait_predicate, _) = predicate.kind().skip_binder() { + if must_use_attr(&cx.tcx.get_attrs(trait_predicate.trait_ref.def_id)).is_some() { + return true; + } + } + } + false + }, + ty::Dynamic(binder, _) => { + for predicate in binder.iter() { + if let ty::ExistentialPredicate::Trait(ref trait_ref) = predicate.skip_binder() { + if must_use_attr(&cx.tcx.get_attrs(trait_ref.def_id)).is_some() { + return true; + } + } + } + false + }, + _ => false, + } +} + +// FIXME: Per https://doc.rust-lang.org/nightly/nightly-rustc/rustc_trait_selection/infer/at/struct.At.html#method.normalize +// this function can be removed once the `normalizie` method does not panic when normalization does +// not succeed +/// Checks if `Ty` is normalizable. This function is useful +/// to avoid crashes on `layout_of`. +pub fn is_normalizable<'tcx>(cx: &LateContext<'tcx>, param_env: ty::ParamEnv<'tcx>, ty: Ty<'tcx>) -> bool { + is_normalizable_helper(cx, param_env, ty, &mut HashMap::new()) +} + +fn is_normalizable_helper<'tcx>( + cx: &LateContext<'tcx>, + param_env: ty::ParamEnv<'tcx>, + ty: Ty<'tcx>, + cache: &mut HashMap<Ty<'tcx>, bool>, +) -> bool { + if let Some(&cached_result) = cache.get(ty) { + return cached_result; + } + // prevent recursive loops, false-negative is better than endless loop leading to stack overflow + cache.insert(ty, false); + let result = cx.tcx.infer_ctxt().enter(|infcx| { + let cause = rustc_middle::traits::ObligationCause::dummy(); + if infcx.at(&cause, param_env).normalize(ty).is_ok() { + match ty.kind() { + ty::Adt(def, substs) => def.variants.iter().all(|variant| { + variant + .fields + .iter() + .all(|field| is_normalizable_helper(cx, param_env, field.ty(cx.tcx, substs), cache)) + }), + _ => ty.walk().all(|generic_arg| match generic_arg.unpack() { + GenericArgKind::Type(inner_ty) if inner_ty != ty => { + is_normalizable_helper(cx, param_env, inner_ty, cache) + }, + _ => true, // if inner_ty == ty, we've already checked it + }), + } + } else { + false + } + }); + cache.insert(ty, result); + result +} + +/// Returns true iff the given type is a primitive (a bool or char, any integer or floating-point +/// number type, a str, or an array, slice, or tuple of those types). +pub fn is_recursively_primitive_type(ty: Ty<'_>) -> bool { + match ty.kind() { + ty::Bool | ty::Char | ty::Int(_) | ty::Uint(_) | ty::Float(_) | ty::Str => true, + ty::Ref(_, inner, _) if *inner.kind() == ty::Str => true, + ty::Array(inner_type, _) | ty::Slice(inner_type) => is_recursively_primitive_type(inner_type), + ty::Tuple(inner_types) => inner_types.types().all(is_recursively_primitive_type), + _ => false, + } +} + +/// Checks if the type is equal to a diagnostic item +/// +/// If you change the signature, remember to update the internal lint `MatchTypeOnDiagItem` +pub fn is_type_diagnostic_item(cx: &LateContext<'_>, ty: Ty<'_>, diag_item: Symbol) -> bool { + match ty.kind() { + ty::Adt(adt, _) => cx.tcx.is_diagnostic_item(diag_item, adt.did), + _ => false, + } +} + +/// Checks if the type is equal to a lang item +pub fn is_type_lang_item(cx: &LateContext<'_>, ty: Ty<'_>, lang_item: hir::LangItem) -> bool { + match ty.kind() { + ty::Adt(adt, _) => cx.tcx.lang_items().require(lang_item).unwrap() == adt.did, + _ => false, + } +} + +/// Return `true` if the passed `typ` is `isize` or `usize`. +pub fn is_isize_or_usize(typ: Ty<'_>) -> bool { + matches!(typ.kind(), ty::Int(IntTy::Isize) | ty::Uint(UintTy::Usize)) +} + +/// Checks if type is struct, enum or union type with the given def path. +/// +/// If the type is a diagnostic item, use `is_type_diagnostic_item` instead. +/// If you change the signature, remember to update the internal lint `MatchTypeOnDiagItem` +pub fn match_type(cx: &LateContext<'_>, ty: Ty<'_>, path: &[&str]) -> bool { + match ty.kind() { + ty::Adt(adt, _) => match_def_path(cx, adt.did, path), + _ => false, + } +} + +/// Peels off all references on the type. Returns the underlying type and the number of references +/// removed. +pub fn peel_mid_ty_refs(ty: Ty<'_>) -> (Ty<'_>, usize) { + fn peel(ty: Ty<'_>, count: usize) -> (Ty<'_>, usize) { + if let ty::Ref(_, ty, _) = ty.kind() { + peel(ty, count + 1) + } else { + (ty, count) + } + } + peel(ty, 0) +} + +/// Peels off all references on the type.Returns the underlying type, the number of references +/// removed, and whether the pointer is ultimately mutable or not. +pub fn peel_mid_ty_refs_is_mutable(ty: Ty<'_>) -> (Ty<'_>, usize, Mutability) { + fn f(ty: Ty<'_>, count: usize, mutability: Mutability) -> (Ty<'_>, usize, Mutability) { + match ty.kind() { + ty::Ref(_, ty, Mutability::Mut) => f(ty, count + 1, mutability), + ty::Ref(_, ty, Mutability::Not) => f(ty, count + 1, Mutability::Not), + _ => (ty, count, mutability), + } + } + f(ty, 0, Mutability::Mut) +} + +/// Returns `true` if the given type is an `unsafe` function. +pub fn type_is_unsafe_function<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool { + match ty.kind() { + ty::FnDef(..) | ty::FnPtr(_) => ty.fn_sig(cx.tcx).unsafety() == Unsafety::Unsafe, + _ => false, + } +} + +/// Returns the base type for HIR references and pointers. +pub fn walk_ptrs_hir_ty<'tcx>(ty: &'tcx hir::Ty<'tcx>) -> &'tcx hir::Ty<'tcx> { + match ty.kind { + TyKind::Ptr(ref mut_ty) | TyKind::Rptr(_, ref mut_ty) => walk_ptrs_hir_ty(&mut_ty.ty), + _ => ty, + } +} + +/// Returns the base type for references and raw pointers, and count reference +/// depth. +pub fn walk_ptrs_ty_depth(ty: Ty<'_>) -> (Ty<'_>, usize) { + fn inner(ty: Ty<'_>, depth: usize) -> (Ty<'_>, usize) { + match ty.kind() { + ty::Ref(_, ty, _) => inner(ty, depth + 1), + _ => (ty, depth), + } + } + inner(ty, 0) +} diff --git a/src/tools/clippy/clippy_utils/src/usage.rs b/src/tools/clippy/clippy_utils/src/usage.rs index d577827dcf3cc..650b70c63af95 100644 --- a/src/tools/clippy/clippy_utils/src/usage.rs +++ b/src/tools/clippy/clippy_utils/src/usage.rs @@ -1,20 +1,21 @@ use crate as utils; -use rustc_data_structures::fx::FxHashSet; use rustc_hir as hir; use rustc_hir::def::Res; use rustc_hir::intravisit; use rustc_hir::intravisit::{NestedVisitorMap, Visitor}; +use rustc_hir::HirIdSet; use rustc_hir::{Expr, ExprKind, HirId, Path}; use rustc_infer::infer::TyCtxtInferExt; use rustc_lint::LateContext; use rustc_middle::hir::map::Map; +use rustc_middle::mir::FakeReadCause; use rustc_middle::ty; use rustc_typeck::expr_use_visitor::{ConsumeMode, Delegate, ExprUseVisitor, PlaceBase, PlaceWithHirId}; /// Returns a set of mutated local variable IDs, or `None` if mutations could not be determined. -pub fn mutated_variables<'tcx>(expr: &'tcx Expr<'_>, cx: &LateContext<'tcx>) -> Option<FxHashSet<HirId>> { +pub fn mutated_variables<'tcx>(expr: &'tcx Expr<'_>, cx: &LateContext<'tcx>) -> Option<HirIdSet> { let mut delegate = MutVarsDelegate { - used_mutably: FxHashSet::default(), + used_mutably: HirIdSet::default(), skip: false, }; cx.tcx.infer_ctxt().enter(|infcx| { @@ -43,7 +44,7 @@ pub fn is_potentially_mutated<'tcx>(variable: &'tcx Path<'_>, expr: &'tcx Expr<' } struct MutVarsDelegate { - used_mutably: FxHashSet<HirId>, + used_mutably: HirIdSet, skip: bool, } @@ -77,6 +78,8 @@ impl<'tcx> Delegate<'tcx> for MutVarsDelegate { fn mutate(&mut self, cmt: &PlaceWithHirId<'tcx>, _: HirId) { self.update(&cmt) } + + fn fake_read(&mut self, _: rustc_typeck::expr_use_visitor::Place<'tcx>, _: FakeReadCause, _: HirId) {} } pub struct ParamBindingIdCollector { diff --git a/src/tools/clippy/doc/adding_lints.md b/src/tools/clippy/doc/adding_lints.md index f62c2d29c707e..99b86953d51a6 100644 --- a/src/tools/clippy/doc/adding_lints.md +++ b/src/tools/clippy/doc/adding_lints.md @@ -18,6 +18,7 @@ because that's clearly a non-descriptive name. - [Lint passes](#lint-passes) - [Emitting a lint](#emitting-a-lint) - [Adding the lint logic](#adding-the-lint-logic) + - [Specifying the lint's minimum supported Rust version (MSRV)](#specifying-the-lints-minimum-supported-rust-version-msrv) - [Author lint](#author-lint) - [Documentation](#documentation) - [Running rustfmt](#running-rustfmt) @@ -387,18 +388,19 @@ pass. [`FnKind::Fn`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_ast/visit/enum.FnKind.html#variant.Fn [ident]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_span/symbol/struct.Ident.html -## Specifying the lint's minimum supported Rust version (msrv) +## Specifying the lint's minimum supported Rust version (MSRV) -Projects supporting older versions of Rust would need to disable a lint if it targets features -present in later versions. Support for this can be added by specifying an msrv in your lint like so, +Projects supporting older versions of Rust would need to disable a lint if it +targets features present in later versions. Support for this can be added by +specifying an MSRV in your lint like so, ```rust const MANUAL_STRIP_MSRV: RustcVersion = RustcVersion::new(1, 45, 0); ``` -The project's msrv will also have to be an attribute in the lint so you'll have to add a struct -and constructor for your lint. The project's msrv needs to be passed when the lint is registered -in `lib.rs` +The project's MSRV will also have to be an attribute in the lint so you'll have +to add a struct and constructor for your lint. The project's MSRV needs to be +passed when the lint is registered in `lib.rs` ```rust pub struct ManualStrip { @@ -413,8 +415,8 @@ impl ManualStrip { } ``` -The project's msrv can then be matched against the lint's msrv in the LintPass using the `meets_msrv` utility -function. +The project's MSRV can then be matched against the lint's `msrv` in the LintPass +using the `meets_msrv` utility function. ``` rust if !meets_msrv(self.msrv.as_ref(), &MANUAL_STRIP_MSRV) { @@ -422,9 +424,10 @@ if !meets_msrv(self.msrv.as_ref(), &MANUAL_STRIP_MSRV) { } ``` -The project's msrv can also be specified as an inner attribute, which overrides the value from -`clippy.toml`. This can be accounted for using the `extract_msrv_attr!(LintContext)` macro and passing -LateContext/EarlyContext. +The project's MSRV can also be specified as an inner attribute, which overrides +the value from `clippy.toml`. This can be accounted for using the +`extract_msrv_attr!(LintContext)` macro and passing +`LateContext`/`EarlyContext`. ```rust impl<'tcx> LateLintPass<'tcx> for ManualStrip { @@ -435,8 +438,20 @@ impl<'tcx> LateLintPass<'tcx> for ManualStrip { } ``` -Once the msrv is added to the lint, a relevant test case should be added to `tests/ui/min_rust_version_attr.rs` -which verifies that the lint isn't emitted if the project's msrv is lower. +Once the `msrv` is added to the lint, a relevant test case should be added to +`tests/ui/min_rust_version_attr.rs` which verifies that the lint isn't emitted +if the project's MSRV is lower. + +As a last step, the lint should be added to the lint documentation. This is done +in `clippy_lints/src/utils/conf.rs`: + +```rust +define_Conf! { + /// Lint: LIST, OF, LINTS, <THE_NEWLY_ADDED_LINT>. The minimum rust version that the project supports + (msrv, "msrv": Option<String>, None), + ... +} +``` ## Author lint @@ -532,9 +547,9 @@ Before submitting your PR make sure you followed all of the basic requirements: ## Adding configuration to a lint -Clippy supports the configuration of lints values using a `clippy.toml` file in the workspace +Clippy supports the configuration of lints values using a `clippy.toml` file in the workspace directory. Adding a configuration to a lint can be useful for thresholds or to constrain some -behavior that can be seen as a false positive for some users. Adding a configuration is done +behavior that can be seen as a false positive for some users. Adding a configuration is done in the following steps: 1. Adding a new configuration entry to [clippy_utils::conf](/clippy_utils/src/conf.rs) @@ -543,10 +558,10 @@ in the following steps: /// Lint: LINT_NAME. <The configuration field doc comment> (configuration_ident, "configuration_value": Type, DefaultValue), ``` - The configuration value and identifier should usually be the same. The doc comment will be + The configuration value and identifier should usually be the same. The doc comment will be automatically added to the lint documentation. 2. Adding the configuration value to the lint impl struct: - 1. This first requires the definition of a lint impl struct. Lint impl structs are usually + 1. This first requires the definition of a lint impl struct. Lint impl structs are usually generated with the `declare_lint_pass!` macro. This struct needs to be defined manually to add some kind of metadata to it: ```rust @@ -563,7 +578,7 @@ in the following steps: LINT_NAME ]); ``` - + 2. Next add the configuration value and a corresponding creation method like this: ```rust #[derive(Copy, Clone)] @@ -583,7 +598,7 @@ in the following steps: ``` 3. Passing the configuration value to the lint impl struct: - First find the struct construction in the [clippy_lints lib file](/clippy_lints/src/lib.rs). + First find the struct construction in the [clippy_lints lib file](/clippy_lints/src/lib.rs). The configuration value is now cloned or copied into a local value that is then passed to the impl struct like this: ```rust @@ -600,9 +615,9 @@ in the following steps: 4. Adding tests: 1. The default configured value can be tested like any normal lint in [`tests/ui`](/tests/ui). - 2. The configuration itself will be tested separately in [`tests/ui-toml`](/tests/ui-toml). - Simply add a new subfolder with a fitting name. This folder contains a `clippy.toml` file - with the configuration value and a rust file that should be linted by Clippy. The test can + 2. The configuration itself will be tested separately in [`tests/ui-toml`](/tests/ui-toml). + Simply add a new subfolder with a fitting name. This folder contains a `clippy.toml` file + with the configuration value and a rust file that should be linted by Clippy. The test can otherwise be written as usual. ## Cheatsheet diff --git a/src/tools/clippy/doc/basics.md b/src/tools/clippy/doc/basics.md index a9416f3b20b7a..5226875cc210a 100644 --- a/src/tools/clippy/doc/basics.md +++ b/src/tools/clippy/doc/basics.md @@ -11,8 +11,9 @@ the codebase take a look at [Adding Lints] or [Common Tools]. - [Get the Code](#get-the-code) - [Building and Testing](#building-and-testing) - [`cargo dev`](#cargo-dev) - - [Common Abbreviations](#common-abbreviations) + - [lintcheck](#lintcheck) - [PR](#pr) + - [Common Abbreviations](#common-abbreviations) ## Get the Code @@ -87,10 +88,20 @@ cargo dev fmt cargo dev update_lints # create a new lint and register it cargo dev new_lint -# (experimental) Setup Clippy to work with rust-analyzer -cargo dev ra_setup +# (experimental) Setup Clippy to work with IntelliJ-Rust +cargo dev ide_setup ``` +## lintcheck +`cargo lintcheck` will build and run clippy on a fixed set of crates and generate a log of the results. +You can `git diff` the updated log against its previous version and +see what impact your lint made on a small set of crates. +If you add a new lint, please audit the resulting warnings and make sure +there are no false positives and that the suggestions are valid. + +Refer to the tools [README] for more details. + +[README]: https://github.com/rust-lang/rust-clippy/blob/master/lintcheck/README.md ## PR We follow a rustc no merge-commit policy. diff --git a/src/tools/clippy/lintcheck-logs/lintcheck_crates_logs.txt b/src/tools/clippy/lintcheck-logs/lintcheck_crates_logs.txt index c23dd926f621d..8f22bd656836c 100644 --- a/src/tools/clippy/lintcheck-logs/lintcheck_crates_logs.txt +++ b/src/tools/clippy/lintcheck-logs/lintcheck_crates_logs.txt @@ -1,3398 +1,3761 @@ -clippy 0.1.52 (697f3b6d4 2021-02-22) +clippy 0.1.53 (0b7671963 2021-03-31) -cargo-0.49.0/build.rs:1:null clippy::cargo_common_metadata "package `cargo` is missing `package.categories` metadata" -cargo-0.49.0/build.rs:1:null clippy::cargo_common_metadata "package `cargo` is missing `package.keywords` metadata" -cargo-0.49.0/src/bin/cargo/cli.rs:104:34 clippy::single_match_else "you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let`" -cargo-0.49.0/src/bin/cargo/cli.rs:121:5 clippy::single_char_add_str "calling `push_str()` using a single-character string literal" -cargo-0.49.0/src/bin/cargo/cli.rs:157:30 clippy::redundant_closure_for_method_calls "redundant closure found" -cargo-0.49.0/src/bin/cargo/cli.rs:184:41 clippy::cast_possible_truncation "casting `u64` to `u32` may truncate the value" -cargo-0.49.0/src/bin/cargo/cli.rs:196:42 clippy::redundant_closure_for_method_calls "redundant closure found" -cargo-0.49.0/src/bin/cargo/cli.rs:200:39 clippy::redundant_closure_for_method_calls "redundant closure found" -cargo-0.49.0/src/bin/cargo/cli.rs:231:1 clippy::struct_excessive_bools "more than 3 bools in a struct" -cargo-0.49.0/src/bin/cargo/cli.rs:245:22 clippy::cast_possible_truncation "casting `u64` to `u32` may truncate the value" -cargo-0.49.0/src/bin/cargo/cli.rs:247:47 clippy::redundant_closure_for_method_calls "redundant closure found" -cargo-0.49.0/src/bin/cargo/cli.rs:257:22 clippy::redundant_closure_for_method_calls "redundant closure found" -cargo-0.49.0/src/bin/cargo/cli.rs:26:20 clippy::redundant_else "redundant else block" -cargo-0.49.0/src/bin/cargo/cli.rs:7:5 clippy::wildcard_imports "usage of wildcard import" -cargo-0.49.0/src/bin/cargo/commands/bench.rs:1:5 clippy::wildcard_imports "usage of wildcard import" -cargo-0.49.0/src/bin/cargo/commands/bench.rs:76:59 clippy::redundant_closure_for_method_calls "redundant closure found" -cargo-0.49.0/src/bin/cargo/commands/build.rs:1:5 clippy::wildcard_imports "usage of wildcard import" -cargo-0.49.0/src/bin/cargo/commands/check.rs:1:5 clippy::wildcard_imports "usage of wildcard import" -cargo-0.49.0/src/bin/cargo/commands/clean.rs:1:5 clippy::wildcard_imports "usage of wildcard import" -cargo-0.49.0/src/bin/cargo/commands/doc.rs:1:5 clippy::wildcard_imports "usage of wildcard import" -cargo-0.49.0/src/bin/cargo/commands/fetch.rs:1:5 clippy::wildcard_imports "usage of wildcard import" -cargo-0.49.0/src/bin/cargo/commands/fetch.rs:22:5 clippy::let_underscore_drop "non-binding `let` on a type that implements `Drop`" -cargo-0.49.0/src/bin/cargo/commands/fix.rs:1:5 clippy::wildcard_imports "usage of wildcard import" -cargo-0.49.0/src/bin/cargo/commands/generate_lockfile.rs:1:5 clippy::wildcard_imports "usage of wildcard import" -cargo-0.49.0/src/bin/cargo/commands/git_checkout.rs:1:5 clippy::wildcard_imports "usage of wildcard import" -cargo-0.49.0/src/bin/cargo/commands/help.rs:20:1 clippy::module_name_repetitions "item name ends with its containing module's name" -cargo-0.49.0/src/bin/cargo/commands/init.rs:1:5 clippy::wildcard_imports "usage of wildcard import" -cargo-0.49.0/src/bin/cargo/commands/install.rs:1:5 clippy::wildcard_imports "usage of wildcard import" -cargo-0.49.0/src/bin/cargo/commands/install.rs:97:16 clippy::option_if_let_else "use Option::map_or instead of an if let/else" -cargo-0.49.0/src/bin/cargo/commands/locate_project.rs:1:5 clippy::wildcard_imports "usage of wildcard import" -cargo-0.49.0/src/bin/cargo/commands/login.rs:1:5 clippy::wildcard_imports "usage of wildcard import" -cargo-0.49.0/src/bin/cargo/commands/metadata.rs:1:5 clippy::wildcard_imports "usage of wildcard import" -cargo-0.49.0/src/bin/cargo/commands/mod.rs:1:5 clippy::wildcard_imports "usage of wildcard import" -cargo-0.49.0/src/bin/cargo/commands/new.rs:1:5 clippy::wildcard_imports "usage of wildcard import" -cargo-0.49.0/src/bin/cargo/commands/new.rs:20:24 clippy::option_if_let_else "use Option::map_or instead of an if let/else" -cargo-0.49.0/src/bin/cargo/commands/owner.rs:1:5 clippy::wildcard_imports "usage of wildcard import" -cargo-0.49.0/src/bin/cargo/commands/owner.rs:38:43 clippy::redundant_closure_for_method_calls "redundant closure found" -cargo-0.49.0/src/bin/cargo/commands/owner.rs:39:43 clippy::redundant_closure_for_method_calls "redundant closure found" -cargo-0.49.0/src/bin/cargo/commands/owner.rs:40:43 clippy::redundant_closure_for_method_calls "redundant closure found" -cargo-0.49.0/src/bin/cargo/commands/owner.rs:43:30 clippy::redundant_closure_for_method_calls "redundant closure found" -cargo-0.49.0/src/bin/cargo/commands/owner.rs:46:30 clippy::redundant_closure_for_method_calls "redundant closure found" -cargo-0.49.0/src/bin/cargo/commands/package.rs:1:5 clippy::wildcard_imports "usage of wildcard import" -cargo-0.49.0/src/bin/cargo/commands/pkgid.rs:1:5 clippy::wildcard_imports "usage of wildcard import" -cargo-0.49.0/src/bin/cargo/commands/publish.rs:1:5 clippy::wildcard_imports "usage of wildcard import" -cargo-0.49.0/src/bin/cargo/commands/publish.rs:40:47 clippy::redundant_closure_for_method_calls "redundant closure found" -cargo-0.49.0/src/bin/cargo/commands/read_manifest.rs:1:5 clippy::wildcard_imports "usage of wildcard import" -cargo-0.49.0/src/bin/cargo/commands/run.rs:1:5 clippy::wildcard_imports "usage of wildcard import" -cargo-0.49.0/src/bin/cargo/commands/rustc.rs:1:5 clippy::wildcard_imports "usage of wildcard import" -cargo-0.49.0/src/bin/cargo/commands/rustdoc.rs:3:5 clippy::wildcard_imports "usage of wildcard import" -cargo-0.49.0/src/bin/cargo/commands/search.rs:1:5 clippy::wildcard_imports "usage of wildcard import" -cargo-0.49.0/src/bin/cargo/commands/test.rs:127:54 clippy::redundant_closure_for_method_calls "redundant closure found" -cargo-0.49.0/src/bin/cargo/commands/test.rs:1:5 clippy::wildcard_imports "usage of wildcard import" -cargo-0.49.0/src/bin/cargo/commands/tree.rs:149:49 clippy::redundant_closure_for_method_calls "redundant closure found" -cargo-0.49.0/src/bin/cargo/commands/tree.rs:2:5 clippy::wildcard_imports "usage of wildcard import" -cargo-0.49.0/src/bin/cargo/commands/uninstall.rs:1:5 clippy::wildcard_imports "usage of wildcard import" -cargo-0.49.0/src/bin/cargo/commands/update.rs:1:5 clippy::wildcard_imports "usage of wildcard import" -cargo-0.49.0/src/bin/cargo/commands/vendor.rs:1:5 clippy::wildcard_imports "usage of wildcard import" -cargo-0.49.0/src/bin/cargo/commands/vendor.rs:96:16 clippy::map_unwrap_or "called `map(<f>).unwrap_or_else(<g>)` on an `Option` value. This can be done more directly by calling `map_or_else(<g>, <f>)` instead" -cargo-0.49.0/src/bin/cargo/commands/verify_project.rs:1:5 clippy::wildcard_imports "usage of wildcard import" -cargo-0.49.0/src/bin/cargo/commands/version.rs:2:5 clippy::wildcard_imports "usage of wildcard import" -cargo-0.49.0/src/bin/cargo/commands/yank.rs:1:5 clippy::wildcard_imports "usage of wildcard import" -cargo-0.49.0/src/bin/cargo/commands/yank.rs:32:36 clippy::redundant_closure_for_method_calls "redundant closure found" -cargo-0.49.0/src/bin/cargo/commands/yank.rs:33:35 clippy::redundant_closure_for_method_calls "redundant closure found" -cargo-0.49.0/src/bin/cargo/commands/yank.rs:34:36 clippy::redundant_closure_for_method_calls "redundant closure found" -cargo-0.49.0/src/bin/cargo/commands/yank.rs:35:36 clippy::redundant_closure_for_method_calls "redundant closure found" -cargo-0.49.0/src/bin/cargo/main.rs:100:17 clippy::match_wildcard_for_single_variants "wildcard match will miss any future added variants" -cargo-0.49.0/src/bin/cargo/main.rs:118:41 clippy::redundant_closure_for_method_calls "redundant closure found" -cargo-0.49.0/src/bin/cargo/main.rs:137:43 clippy::redundant_closure_for_method_calls "redundant closure found" -cargo-0.49.0/src/bin/cargo/main.rs:148:19 clippy::single_match_else "you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let`" -cargo-0.49.0/src/bin/cargo/main.rs:174:57 clippy::redundant_closure_for_method_calls "redundant closure found" -cargo-0.49.0/src/bin/cargo/main.rs:18:5 clippy::wildcard_imports "usage of wildcard import" -cargo-0.49.0/src/bin/cargo/main.rs:1:null clippy::cargo_common_metadata "package `cargo` is missing `package.categories` metadata" -cargo-0.49.0/src/bin/cargo/main.rs:1:null clippy::cargo_common_metadata "package `cargo` is missing `package.keywords` metadata" -cargo-0.49.0/src/bin/cargo/main.rs:1:null clippy::multiple_crate_versions "multiple versions for dependency `crossbeam-utils`: 0.6.6, 0.7.2" -cargo-0.49.0/src/bin/cargo/main.rs:1:null clippy::multiple_crate_versions "multiple versions for dependency `hex`: 0.3.2, 0.4.0" -cargo-0.49.0/src/bin/cargo/main.rs:1:null clippy::multiple_crate_versions "multiple versions for dependency `humantime`: 1.3.0, 2.0.0" -cargo-0.49.0/src/bin/cargo/main.rs:72:22 clippy::redundant_closure_for_method_calls "redundant closure found" -cargo-0.49.0/src/bin/cargo/main.rs:94:13 clippy::match_wildcard_for_single_variants "wildcard match will miss any future added variants" -cargo-0.49.0/src/bin/cargo/main.rs:96:41 clippy::redundant_closure_for_method_calls "redundant closure found" -cargo-0.49.0/src/bin/cargo/main.rs:98:60 clippy::redundant_closure_for_method_calls "redundant closure found" -cargo-0.49.0/src/cargo/core/compiler/build_config.rs:155:13 clippy::enum_glob_use "usage of wildcard import for enum variants" -cargo-0.49.0/src/cargo/core/compiler/build_config.rs:170:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/core/compiler/build_config.rs:175:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/core/compiler/build_config.rs:180:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/core/compiler/build_config.rs:186:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/core/compiler/build_config.rs:197:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/core/compiler/build_config.rs:205:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/core/compiler/build_config.rs:51:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -cargo-0.49.0/src/cargo/core/compiler/build_config.rs:69:48 clippy::cast_possible_truncation "casting `usize` to `u32` may truncate the value on targets with 64-bit wide pointers" -cargo-0.49.0/src/cargo/core/compiler/build_config.rs:96:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -cargo-0.49.0/src/cargo/core/compiler/build_context/mod.rs:44:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -cargo-0.49.0/src/cargo/core/compiler/build_context/mod.rs:83:20 clippy::doc_markdown "you should put `x86_64` between ticks in the documentation" -cargo-0.49.0/src/cargo/core/compiler/build_context/target_info.rs:108:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/core/compiler/build_context/target_info.rs:121:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -cargo-0.49.0/src/cargo/core/compiler/build_context/target_info.rs:149:9 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope" -cargo-0.49.0/src/cargo/core/compiler/build_context/target_info.rs:411:9 clippy::needless_question_mark "Question mark operator is useless here" -cargo-0.49.0/src/cargo/core/compiler/build_context/target_info.rs:420:69 clippy::doc_markdown "you should put `mode/target_kind` between ticks in the documentation" -cargo-0.49.0/src/cargo/core/compiler/build_context/target_info.rs:423:19 clippy::doc_markdown "you should put `CrateTypes` between ticks in the documentation" -cargo-0.49.0/src/cargo/core/compiler/build_context/target_info.rs:424:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -cargo-0.49.0/src/cargo/core/compiler/build_context/target_info.rs:469:58 clippy::redundant_closure_for_method_calls "redundant closure found" -cargo-0.49.0/src/cargo/core/compiler/build_context/target_info.rs:603:19 clippy::redundant_closure_for_method_calls "redundant closure found" -cargo-0.49.0/src/cargo/core/compiler/build_context/target_info.rs:665:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -cargo-0.49.0/src/cargo/core/compiler/build_context/target_info.rs:697:12 clippy::inconsistent_struct_constructor "inconsistent struct constructor" -cargo-0.49.0/src/cargo/core/compiler/build_context/target_info.rs:82:31 clippy::doc_markdown "you should put `FileType` between ticks in the documentation" -cargo-0.49.0/src/cargo/core/compiler/build_context/target_info.rs:83:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/core/compiler/build_context/target_info.rs:84:9 clippy::single_match_else "you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let`" -cargo-0.49.0/src/cargo/core/compiler/build_context/target_info.rs:96:31 clippy::doc_markdown "you should put `FileType` between ticks in the documentation" -cargo-0.49.0/src/cargo/core/compiler/build_context/target_info.rs:98:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/core/compiler/build_plan.rs:4:9 clippy::doc_markdown "you should put `BuildPlan` between ticks in the documentation" -cargo-0.49.0/src/cargo/core/compiler/build_plan.rs:5:66 clippy::doc_markdown "you should put `BuildPlan` between ticks in the documentation" -cargo-0.49.0/src/cargo/core/compiler/build_plan.rs:66:40 clippy::ptr_arg "writing `&PathBuf` instead of `&Path` involves a new object where a slice will do." -cargo-0.49.0/src/cargo/core/compiler/compilation.rs:150:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -cargo-0.49.0/src/cargo/core/compiler/compilation.rs:150:5 clippy::missing_panics_doc "docs for function which may panic missing `# Panics` section" -cargo-0.49.0/src/cargo/core/compiler/compilation.rs:169:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -cargo-0.49.0/src/cargo/core/compiler/compilation.rs:185:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -cargo-0.49.0/src/cargo/core/compiler/compilation.rs:193:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/core/compiler/compilation.rs:194:49 clippy::redundant_closure_for_method_calls "redundant closure found" -cargo-0.49.0/src/cargo/core/compiler/compilation.rs:198:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -cargo-0.49.0/src/cargo/core/compiler/compilation.rs:314:16 clippy::doc_markdown "you should put `rustc_tool` between ticks in the documentation" -cargo-0.49.0/src/cargo/core/compiler/compilation.rs:91:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -cargo-0.49.0/src/cargo/core/compiler/compile_kind.rs:118:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -cargo-0.49.0/src/cargo/core/compiler/compile_kind.rs:123:18 clippy::case_sensitive_file_extension_comparisons "case-sensitive file extension comparison" -cargo-0.49.0/src/cargo/core/compiler/compile_kind.rs:147:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/core/compiler/compile_kind.rs:157:5 clippy::missing_panics_doc "docs for function which may panic missing `# Panics` section" -cargo-0.49.0/src/cargo/core/compiler/compile_kind.rs:157:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/core/compiler/compile_kind.rs:29:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/core/compiler/compile_kind.rs:33:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/core/compiler/compile_kind.rs:49:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -cargo-0.49.0/src/cargo/core/compiler/compile_kind.rs:69:48 clippy::case_sensitive_file_extension_comparisons "case-sensitive file extension comparison" -cargo-0.49.0/src/cargo/core/compiler/context/compilation_files.rs:204:9 clippy::single_match_else "you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let`" -cargo-0.49.0/src/cargo/core/compiler/context/compilation_files.rs:277:22 clippy::doc_markdown "you should put `OUT_DIR` between ticks in the documentation" -cargo-0.49.0/src/cargo/core/compiler/context/compilation_files.rs:324:66 clippy::doc_markdown "you should put `FileType` between ticks in the documentation" -cargo-0.49.0/src/cargo/core/compiler/context/compilation_files.rs:393:37 clippy::match_same_arms "this `match` has identical arm bodies" -cargo-0.49.0/src/cargo/core/compiler/context/compilation_files.rs:426:71 clippy::redundant_closure_for_method_calls "redundant closure found" -cargo-0.49.0/src/cargo/core/compiler/context/mod.rs:125:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -cargo-0.49.0/src/cargo/core/compiler/context/mod.rs:125:5 clippy::missing_panics_doc "docs for function which may panic missing `# Panics` section" -cargo-0.49.0/src/cargo/core/compiler/context/mod.rs:125:5 clippy::too_many_lines "this function has too many lines (107/100)" -cargo-0.49.0/src/cargo/core/compiler/context/mod.rs:270:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -cargo-0.49.0/src/cargo/core/compiler/context/mod.rs:286:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -cargo-0.49.0/src/cargo/core/compiler/context/mod.rs:308:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -cargo-0.49.0/src/cargo/core/compiler/context/mod.rs:308:5 clippy::missing_panics_doc "docs for function which may panic missing `# Panics` section" -cargo-0.49.0/src/cargo/core/compiler/context/mod.rs:340:5 clippy::missing_panics_doc "docs for function which may panic missing `# Panics` section" -cargo-0.49.0/src/cargo/core/compiler/context/mod.rs:340:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/core/compiler/context/mod.rs:349:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -cargo-0.49.0/src/cargo/core/compiler/context/mod.rs:349:5 clippy::missing_panics_doc "docs for function which may panic missing `# Panics` section" -cargo-0.49.0/src/cargo/core/compiler/context/mod.rs:354:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/core/compiler/context/mod.rs:358:21 clippy::doc_markdown "you should put `RunCustomBuild` between ticks in the documentation" -cargo-0.49.0/src/cargo/core/compiler/context/mod.rs:361:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/core/compiler/context/mod.rs:374:43 clippy::doc_markdown "you should put `RunCustomBuild` between ticks in the documentation" -cargo-0.49.0/src/cargo/core/compiler/context/mod.rs:378:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/core/compiler/context/mod.rs:383:41 clippy::doc_markdown "you should put `RunCustomBuild` between ticks in the documentation" -cargo-0.49.0/src/cargo/core/compiler/context/mod.rs:384:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/core/compiler/context/mod.rs:391:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/core/compiler/context/mod.rs:397:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -cargo-0.49.0/src/cargo/core/compiler/context/mod.rs:523:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/core/compiler/context/mod.rs:538:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/core/compiler/context/mod.rs:542:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -cargo-0.49.0/src/cargo/core/compiler/context/mod.rs:83:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -cargo-0.49.0/src/cargo/core/compiler/context/mod.rs:92:25 clippy::single_match_else "you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let`" -cargo-0.49.0/src/cargo/core/compiler/crate_type.rs:16:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/core/compiler/crate_type.rs:29:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/core/compiler/crate_type.rs:40:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/core/compiler/crate_type.rs:49:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/core/compiler/crate_type.rs:60:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/core/compiler/custom_build.rs:150:1 clippy::too_many_lines "this function has too many lines (230/100)" -cargo-0.49.0/src/cargo/core/compiler/custom_build.rs:353:56 clippy::manual_strip "stripping a prefix manually" -cargo-0.49.0/src/cargo/core/compiler/custom_build.rs:448:27 clippy::needless_pass_by_value "this argument is passed by value, but not consumed in the function body" -cargo-0.49.0/src/cargo/core/compiler/custom_build.rs:464:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -cargo-0.49.0/src/cargo/core/compiler/custom_build.rs:481:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -cargo-0.49.0/src/cargo/core/compiler/custom_build.rs:481:5 clippy::missing_panics_doc "docs for function which may panic missing `# Panics` section" -cargo-0.49.0/src/cargo/core/compiler/custom_build.rs:48:56 clippy::doc_markdown "you should put `RunCustomBuild` between ticks in the documentation" -cargo-0.49.0/src/cargo/core/compiler/custom_build.rs:561:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -cargo-0.49.0/src/cargo/core/compiler/custom_build.rs:567:20 clippy::redundant_closure_for_method_calls "redundant closure found" -cargo-0.49.0/src/cargo/core/compiler/custom_build.rs:576:28 clippy::shadow_unrelated "`mut value` is being shadowed" -cargo-0.49.0/src/cargo/core/compiler/custom_build.rs:606:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -cargo-0.49.0/src/cargo/core/compiler/custom_build.rs:688:5 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope" -cargo-0.49.0/src/cargo/core/compiler/custom_build.rs:756:5 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope" -cargo-0.49.0/src/cargo/core/compiler/custom_build.rs:762:5 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope" -cargo-0.49.0/src/cargo/core/compiler/custom_build.rs:762:5 clippy::unnecessary_wraps "this function's return value is unnecessary" -cargo-0.49.0/src/cargo/core/compiler/custom_build.rs:823:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/core/compiler/fingerprint.rs:1021:51 clippy::redundant_closure_for_method_calls "redundant closure found" -cargo-0.49.0/src/cargo/core/compiler/fingerprint.rs:1656:16 clippy::single_match_else "you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let`" -cargo-0.49.0/src/cargo/core/compiler/fingerprint.rs:1664:5 clippy::field_reassign_with_default "field assignment outside of initializer for an instance created with Default::default()" -cargo-0.49.0/src/cargo/core/compiler/fingerprint.rs:1787:5 clippy::similar_names "binding's name is too similar to existing binding" -cargo-0.49.0/src/cargo/core/compiler/fingerprint.rs:1795:5 clippy::field_reassign_with_default "field assignment outside of initializer for an instance created with Default::default()" -cargo-0.49.0/src/cargo/core/compiler/fingerprint.rs:1882:17 clippy::similar_names "binding's name is too similar to existing binding" -cargo-0.49.0/src/cargo/core/compiler/fingerprint.rs:1894:17 clippy::similar_names "binding's name is too similar to existing binding" -cargo-0.49.0/src/cargo/core/compiler/fingerprint.rs:1906:9 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope" -cargo-0.49.0/src/cargo/core/compiler/fingerprint.rs:1917:9 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope" -cargo-0.49.0/src/cargo/core/compiler/fingerprint.rs:1923:9 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope" -cargo-0.49.0/src/cargo/core/compiler/fingerprint.rs:1956:9 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope" -cargo-0.49.0/src/cargo/core/compiler/fingerprint.rs:1962:9 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope" -cargo-0.49.0/src/cargo/core/compiler/fingerprint.rs:1963:22 clippy::cast_possible_truncation "casting `usize` to `u8` may truncate the value" -cargo-0.49.0/src/cargo/core/compiler/fingerprint.rs:1964:22 clippy::cast_possible_truncation "casting `usize` to `u8` may truncate the value" -cargo-0.49.0/src/cargo/core/compiler/fingerprint.rs:1965:22 clippy::cast_possible_truncation "casting `usize` to `u8` may truncate the value" -cargo-0.49.0/src/cargo/core/compiler/fingerprint.rs:1966:22 clippy::cast_possible_truncation "casting `usize` to `u8` may truncate the value" -cargo-0.49.0/src/cargo/core/compiler/fingerprint.rs:1980:17 clippy::similar_names "binding's name is too similar to existing binding" -cargo-0.49.0/src/cargo/core/compiler/fingerprint.rs:1980:24 clippy::manual_strip "stripping a prefix manually" -cargo-0.49.0/src/cargo/core/compiler/fingerprint.rs:1986:17 clippy::similar_names "binding's name is too similar to existing binding" -cargo-0.49.0/src/cargo/core/compiler/fingerprint.rs:2016:5 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope" -cargo-0.49.0/src/cargo/core/compiler/fingerprint.rs:61:5 clippy::doc_markdown "you should put `CompileMode` between ticks in the documentation" -cargo-0.49.0/src/cargo/core/compiler/fingerprint.rs:63:12 clippy::doc_markdown "you should put `CompileKind` between ticks in the documentation" -cargo-0.49.0/src/cargo/core/compiler/fingerprint.rs:67:7 clippy::doc_markdown "you should put `CARGO_DEFAULT_LIB_METADATA[^4` between ticks in the documentation" -cargo-0.49.0/src/cargo/core/compiler/fingerprint.rs:68:5 clippy::doc_markdown "you should put `package_id` between ticks in the documentation" -cargo-0.49.0/src/cargo/core/compiler/fingerprint.rs:71:19 clippy::doc_markdown "you should put `test/bench/for_host/edition` between ticks in the documentation" -cargo-0.49.0/src/cargo/core/compiler/fingerprint.rs:755:52 clippy::match_same_arms "this `match` has identical arm bodies" -cargo-0.49.0/src/cargo/core/compiler/fingerprint.rs:77:5 clippy::doc_markdown "you should put `is_std` between ticks in the documentation" -cargo-0.49.0/src/cargo/core/compiler/fingerprint.rs:816:5 clippy::too_many_lines "this function has too many lines (127/100)" -cargo-0.49.0/src/cargo/core/compiler/fingerprint.rs:863:64 clippy::similar_names "binding's name is too similar to existing binding" -cargo-0.49.0/src/cargo/core/compiler/fingerprint.rs:875:33 clippy::similar_names "binding's name is too similar to existing binding" -cargo-0.49.0/src/cargo/core/compiler/fingerprint.rs:876:32 clippy::similar_names "binding's name is too similar to existing binding" -cargo-0.49.0/src/cargo/core/compiler/fingerprint.rs:896:30 clippy::similar_names "binding's name is too similar to existing binding" -cargo-0.49.0/src/cargo/core/compiler/fingerprint.rs:897:30 clippy::similar_names "binding's name is too similar to existing binding" -cargo-0.49.0/src/cargo/core/compiler/fingerprint.rs:991:37 clippy::single_match_else "you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let`" -cargo-0.49.0/src/cargo/core/compiler/job_queue.rs:12:5 clippy::doc_markdown "you should put `src/librustc_jobserver/lib.rs` between ticks in the documentation" -cargo-0.49.0/src/cargo/core/compiler/job_queue.rs:329:13 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope" -cargo-0.49.0/src/cargo/core/compiler/job_queue.rs:332:23 clippy::needless_pass_by_value "this argument is passed by value, but not consumed in the function body" -cargo-0.49.0/src/cargo/core/compiler/job_queue.rs:34:53 clippy::doc_markdown "you should put `NeedsToken` between ticks in the documentation" -cargo-0.49.0/src/cargo/core/compiler/job_queue.rs:35:6 clippy::doc_markdown "you should put `ReleaseToken` between ticks in the documentation" -cargo-0.49.0/src/cargo/core/compiler/job_queue.rs:37:6 clippy::doc_markdown "you should put `NeedsToken` between ticks in the documentation" -cargo-0.49.0/src/cargo/core/compiler/job_queue.rs:40:5 clippy::doc_markdown "you should put `NeedsToken` between ticks in the documentation" -cargo-0.49.0/src/cargo/core/compiler/job_queue.rs:40:56 clippy::doc_markdown "you should put `NeedsToken` between ticks in the documentation" -cargo-0.49.0/src/cargo/core/compiler/job_queue.rs:43:6 clippy::doc_markdown "you should put `ReleaseToken` between ticks in the documentation" -cargo-0.49.0/src/cargo/core/compiler/job_queue.rs:748:16 clippy::collapsible_else_if "this `else { if .. }` block can be collapsed" -cargo-0.49.0/src/cargo/core/compiler/job_queue.rs:749:13 clippy::if_not_else "unnecessary boolean `not` operation" -cargo-0.49.0/src/cargo/core/compiler/job_queue.rs:786:26 clippy::unused_self "unused `self` argument" -cargo-0.49.0/src/cargo/core/compiler/job_queue.rs:81:61 clippy::doc_markdown "you should put `DrainState` between ticks in the documentation" -cargo-0.49.0/src/cargo/core/compiler/job_queue.rs:865:13 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope" -cargo-0.49.0/src/cargo/core/compiler/job_queue.rs:871:13 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope" -cargo-0.49.0/src/cargo/core/compiler/job_queue.rs:890:9 clippy::unused_self "unused `self` argument" -cargo-0.49.0/src/cargo/core/compiler/job_queue.rs:93:24 clippy::doc_markdown "you should put `JobQueue` between ticks in the documentation" -cargo-0.49.0/src/cargo/core/compiler/links.rs:8:1 clippy::module_name_repetitions "item name ends with its containing module's name" -cargo-0.49.0/src/cargo/core/compiler/mod.rs:1016:1 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -cargo-0.49.0/src/cargo/core/compiler/mod.rs:1094:19 clippy::redundant_closure_for_method_calls "redundant closure found" -cargo-0.49.0/src/cargo/core/compiler/mod.rs:1131:1 clippy::unnecessary_wraps "this function's return value is unnecessary" -cargo-0.49.0/src/cargo/core/compiler/mod.rs:1268:34 clippy::case_sensitive_file_extension_comparisons "case-sensitive file extension comparison" -cargo-0.49.0/src/cargo/core/compiler/mod.rs:1277:5 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope" -cargo-0.49.0/src/cargo/core/compiler/mod.rs:179:1 clippy::too_many_lines "this function has too many lines (162/100)" -cargo-0.49.0/src/cargo/core/compiler/mod.rs:198:78 clippy::redundant_closure_for_method_calls "redundant closure found" -cargo-0.49.0/src/cargo/core/compiler/mod.rs:201:25 clippy::single_match_else "you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let`" -cargo-0.49.0/src/cargo/core/compiler/mod.rs:267:9 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope" -cargo-0.49.0/src/cargo/core/compiler/mod.rs:324:5 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope" -cargo-0.49.0/src/cargo/core/compiler/mod.rs:364:5 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope" -cargo-0.49.0/src/cargo/core/compiler/mod.rs:364:5 clippy::unnecessary_wraps "this function's return value is unnecessary" -cargo-0.49.0/src/cargo/core/compiler/mod.rs:392:45 clippy::redundant_closure_for_method_calls "redundant closure found" -cargo-0.49.0/src/cargo/core/compiler/mod.rs:415:23 clippy::single_match_else "you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let`" -cargo-0.49.0/src/cargo/core/compiler/mod.rs:464:18 clippy::ptr_arg "writing `&PathBuf` instead of `&Path` involves a new object where a slice will do." -cargo-0.49.0/src/cargo/core/compiler/mod.rs:488:61 clippy::ptr_arg "writing `&PathBuf` instead of `&Path` involves a new object where a slice will do." -cargo-0.49.0/src/cargo/core/compiler/mod.rs:667:15 clippy::similar_names "binding's name is too similar to existing binding" -cargo-0.49.0/src/cargo/core/compiler/mod.rs:693:1 clippy::unnecessary_wraps "this function's return value is unnecessary" -cargo-0.49.0/src/cargo/core/compiler/mod.rs:725:42 clippy::match_same_arms "this `match` has identical arm bodies" -cargo-0.49.0/src/cargo/core/compiler/mod.rs:736:1 clippy::too_many_lines "this function has too many lines (141/100)" -cargo-0.49.0/src/cargo/core/compiler/mod.rs:73:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -cargo-0.49.0/src/cargo/core/compiler/mod.rs:777:12 clippy::if_not_else "unnecessary boolean `not` operation" -cargo-0.49.0/src/cargo/core/compiler/mod.rs:873:5 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope" -cargo-0.49.0/src/cargo/core/compiler/output_depinfo.rs:41:13 clippy::match_wildcard_for_single_variants "wildcard match will miss any future added variants" -cargo-0.49.0/src/cargo/core/compiler/rustdoc.rs:16:1 clippy::module_name_repetitions "item name starts with its containing module's name" -cargo-0.49.0/src/cargo/core/compiler/rustdoc.rs:57:1 clippy::module_name_repetitions "item name starts with its containing module's name" -cargo-0.49.0/src/cargo/core/compiler/rustdoc.rs:72:1 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -cargo-0.49.0/src/cargo/core/compiler/standard_lib.rs:134:1 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -cargo-0.49.0/src/cargo/core/compiler/standard_lib.rs:16:1 clippy::must_use_candidate "this function could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/core/compiler/standard_lib.rs:30:28 clippy::redundant_closure_for_method_calls "redundant closure found" -cargo-0.49.0/src/cargo/core/compiler/standard_lib.rs:34:1 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -cargo-0.49.0/src/cargo/core/compiler/standard_lib.rs:34:1 clippy::missing_panics_doc "docs for function which may panic missing `# Panics` section" -cargo-0.49.0/src/cargo/core/compiler/timings.rs:16:1 clippy::struct_excessive_bools "more than 3 bools in a struct" -cargo-0.49.0/src/cargo/core/compiler/timings.rs:192:64 clippy::needless_pass_by_value "this argument is passed by value, but not consumed in the function body" -cargo-0.49.0/src/cargo/core/compiler/timings.rs:212:58 clippy::needless_pass_by_value "this argument is passed by value, but not consumed in the function body" -cargo-0.49.0/src/cargo/core/compiler/timings.rs:234:13 clippy::let_underscore_drop "non-binding `let` on a type that implements `Drop`" -cargo-0.49.0/src/cargo/core/compiler/timings.rs:355:13 clippy::cast_possible_truncation "casting `f64` to `u32` may truncate the value" -cargo-0.49.0/src/cargo/core/compiler/timings.rs:355:13 clippy::cast_sign_loss "casting `f64` to `u32` may lose the sign of the value" -cargo-0.49.0/src/cargo/core/compiler/timings.rs:397:38 clippy::cast_possible_truncation "casting `f64` to `u32` may truncate the value" -cargo-0.49.0/src/cargo/core/compiler/timings.rs:397:38 clippy::cast_sign_loss "casting `f64` to `u32` may lose the sign of the value" -cargo-0.49.0/src/cargo/core/compiler/timings.rs:484:9 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope" -cargo-0.49.0/src/cargo/core/compiler/timings.rs:605:38 clippy::doc_markdown "you should put `rmeta_time` between ticks in the documentation" -cargo-0.49.0/src/cargo/core/compiler/timings.rs:605:50 clippy::doc_markdown "you should put `codegen_time` between ticks in the documentation" -cargo-0.49.0/src/cargo/core/compiler/timings.rs:641:26 clippy::non_ascii_literal "literal non-ASCII character detected" -cargo-0.49.0/src/cargo/core/compiler/unit.rs:100:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/core/compiler/unit.rs:151:1 clippy::module_name_repetitions "item name starts with its containing module's name" -cargo-0.49.0/src/cargo/core/compiler/unit.rs:161:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/core/compiler/unit.rs:35:1 clippy::module_name_repetitions "item name starts with its containing module's name" -cargo-0.49.0/src/cargo/core/compiler/unit_dependencies.rs:154:29 clippy::doc_markdown "you should put `state.unit_dependencies` between ticks in the documentation" -cargo-0.49.0/src/cargo/core/compiler/unit_dependencies.rs:213:1 clippy::too_many_lines "this function has too many lines (110/100)" -cargo-0.49.0/src/cargo/core/compiler/unit_dependencies.rs:52:1 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -cargo-0.49.0/src/cargo/core/compiler/unit_dependencies.rs:52:1 clippy::module_name_repetitions "item name ends with its containing module's name" -cargo-0.49.0/src/cargo/core/compiler/unit_graph.rs:65:1 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -cargo-0.49.0/src/cargo/core/compiler/unit_graph.rs:65:1 clippy::module_name_repetitions "item name ends with its containing module's name" -cargo-0.49.0/src/cargo/core/dependency.rs:157:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -cargo-0.49.0/src/cargo/core/dependency.rs:182:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -cargo-0.49.0/src/cargo/core/dependency.rs:203:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/core/dependency.rs:224:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/core/dependency.rs:23:1 clippy::struct_excessive_bools "more than 3 bools in a struct" -cargo-0.49.0/src/cargo/core/dependency.rs:248:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/core/dependency.rs:270:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/core/dependency.rs:274:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/core/dependency.rs:278:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/core/dependency.rs:287:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/core/dependency.rs:291:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/core/dependency.rs:305:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/core/dependency.rs:311:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/core/dependency.rs:319:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/core/dependency.rs:337:75 clippy::redundant_closure_for_method_calls "redundant closure found" -cargo-0.49.0/src/cargo/core/dependency.rs:397:56 clippy::redundant_closure_for_method_calls "redundant closure found" -cargo-0.49.0/src/cargo/core/dependency.rs:403:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/core/dependency.rs:408:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/core/dependency.rs:415:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/core/dependency.rs:419:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/core/dependency.rs:424:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/core/dependency.rs:428:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/core/dependency.rs:433:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/core/dependency.rs:438:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/core/dependency.rs:443:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/core/dependency.rs:449:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/core/dependency.rs:450:9 clippy::if_not_else "unnecessary `!=` operation" -cargo-0.49.0/src/cargo/core/features.rs:119:17 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/core/features.rs:229:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -cargo-0.49.0/src/cargo/core/features.rs:274:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/core/features.rs:278:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -cargo-0.49.0/src/cargo/core/features.rs:306:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/core/features.rs:338:1 clippy::struct_excessive_bools "more than 3 bools in a struct" -cargo-0.49.0/src/cargo/core/features.rs:362:25 clippy::option_option "consider using `Option<T>` instead of `Option<Option<T>>` or a custom enum if you need to distinguish all 3 cases" -cargo-0.49.0/src/cargo/core/features.rs:380:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -cargo-0.49.0/src/cargo/core/features.rs:401:9 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope" -cargo-0.49.0/src/cargo/core/features.rs:409:9 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope" -cargo-0.49.0/src/cargo/core/features.rs:412:45 clippy::redundant_closure_for_method_calls "redundant closure found" -cargo-0.49.0/src/cargo/core/features.rs:416:9 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope" -cargo-0.49.0/src/cargo/core/features.rs:419:45 clippy::redundant_closure_for_method_calls "redundant closure found" -cargo-0.49.0/src/cargo/core/features.rs:424:9 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope" -cargo-0.49.0/src/cargo/core/features.rs:431:9 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope" -cargo-0.49.0/src/cargo/core/features.rs:477:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -cargo-0.49.0/src/cargo/core/features.rs:509:1 clippy::must_use_candidate "this function could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/core/features.rs:518:5 clippy::map_unwrap_or "called `map(<f>).unwrap_or_else(<g>)` on an `Option` value. This can be done more directly by calling `map_or_else(<g>, <f>)` instead" -cargo-0.49.0/src/cargo/core/features.rs:542:1 clippy::must_use_candidate "this function could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/core/features.rs:543:37 clippy::redundant_closure_for_method_calls "redundant closure found" -cargo-0.49.0/src/cargo/core/features.rs:547:60 clippy::redundant_closure_for_method_calls "redundant closure found" -cargo-0.49.0/src/cargo/core/features.rs:556:1 clippy::module_name_repetitions "item name ends with its containing module's name" -cargo-0.49.0/src/cargo/core/features.rs:563:1 clippy::module_name_repetitions "item name ends with its containing module's name" -cargo-0.49.0/src/cargo/core/manifest.rs:116:13 clippy::enum_glob_use "usage of wildcard import for enum variants" -cargo-0.49.0/src/cargo/core/manifest.rs:118:58 clippy::redundant_closure_for_method_calls "redundant closure found" -cargo-0.49.0/src/cargo/core/manifest.rs:130:13 clippy::enum_glob_use "usage of wildcard import for enum variants" -cargo-0.49.0/src/cargo/core/manifest.rs:143:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/core/manifest.rs:159:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/core/manifest.rs:162:34 clippy::redundant_closure_for_method_calls "redundant closure found" -cargo-0.49.0/src/cargo/core/manifest.rs:169:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/core/manifest.rs:17:5 clippy::wildcard_imports "usage of wildcard import" -cargo-0.49.0/src/cargo/core/manifest.rs:189:1 clippy::struct_excessive_bools "more than 3 bools in a struct" -cargo-0.49.0/src/cargo/core/manifest.rs:215:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/core/manifest.rs:222:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/core/manifest.rs:22:1 clippy::module_name_repetitions "item name ends with its containing module's name" -cargo-0.49.0/src/cargo/core/manifest.rs:360:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/core/manifest.rs:407:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/core/manifest.rs:410:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/core/manifest.rs:413:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/core/manifest.rs:416:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/core/manifest.rs:419:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/core/manifest.rs:422:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/core/manifest.rs:425:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/core/manifest.rs:431:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/core/manifest.rs:438:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/core/manifest.rs:444:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/core/manifest.rs:447:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/core/manifest.rs:450:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/core/manifest.rs:453:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/core/manifest.rs:456:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/core/manifest.rs:459:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/core/manifest.rs:462:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/core/manifest.rs:466:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/core/manifest.rs:470:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/core/manifest.rs:477:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/core/manifest.rs:481:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/core/manifest.rs:488:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -cargo-0.49.0/src/cargo/core/manifest.rs:512:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/core/manifest.rs:516:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/core/manifest.rs:520:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/core/manifest.rs:524:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/core/manifest.rs:528:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/core/manifest.rs:538:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/core/manifest.rs:557:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/core/manifest.rs:561:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/core/manifest.rs:565:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/core/manifest.rs:569:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/core/manifest.rs:577:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/core/manifest.rs:581:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/core/manifest.rs:588:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/core/manifest.rs:617:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/core/manifest.rs:632:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/core/manifest.rs:648:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/core/manifest.rs:659:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/core/manifest.rs:66:1 clippy::module_name_repetitions "item name ends with its containing module's name" -cargo-0.49.0/src/cargo/core/manifest.rs:670:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/core/manifest.rs:693:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/core/manifest.rs:708:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/core/manifest.rs:723:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/core/manifest.rs:726:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/core/manifest.rs:729:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/core/manifest.rs:735:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/core/manifest.rs:738:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/core/manifest.rs:741:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/core/manifest.rs:744:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/core/manifest.rs:747:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/core/manifest.rs:751:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/core/manifest.rs:754:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/core/manifest.rs:757:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/core/manifest.rs:760:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/core/manifest.rs:763:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/core/manifest.rs:767:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/core/manifest.rs:776:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/core/manifest.rs:780:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/core/manifest.rs:787:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/core/manifest.rs:798:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/core/manifest.rs:800:56 clippy::redundant_closure_for_method_calls "redundant closure found" -cargo-0.49.0/src/cargo/core/manifest.rs:805:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/core/manifest.rs:809:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/core/manifest.rs:818:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/core/manifest.rs:823:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/core/manifest.rs:828:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/core/manifest.rs:831:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/core/manifest.rs:834:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/core/manifest.rs:839:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/core/manifest.rs:85:1 clippy::module_name_repetitions "item name starts with its containing module's name" -cargo-0.49.0/src/cargo/core/manifest.rs:888:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/core/manifest.rs:936:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/core/package.rs:1075:28 clippy::redundant_closure_for_method_calls "redundant closure found" -cargo-0.49.0/src/cargo/core/package.rs:160:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/core/package.rs:170:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/core/package.rs:174:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/core/package.rs:182:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/core/package.rs:186:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/core/package.rs:190:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/core/package.rs:194:5 clippy::missing_panics_doc "docs for function which may panic missing `# Panics` section" -cargo-0.49.0/src/cargo/core/package.rs:194:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/core/package.rs:198:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/core/package.rs:202:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/core/package.rs:206:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/core/package.rs:210:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/core/package.rs:217:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/core/package.rs:221:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/core/package.rs:222:35 clippy::redundant_closure_for_method_calls "redundant closure found" -cargo-0.49.0/src/cargo/core/package.rs:226:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/core/package.rs:227:35 clippy::redundant_closure_for_method_calls "redundant closure found" -cargo-0.49.0/src/cargo/core/package.rs:230:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/core/package.rs:239:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -cargo-0.49.0/src/cargo/core/package.rs:249:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/core/package.rs:287:1 clippy::module_name_repetitions "item name starts with its containing module's name" -cargo-0.49.0/src/cargo/core/package.rs:385:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -cargo-0.49.0/src/cargo/core/package.rs:421:5 clippy::needless_lifetimes "explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration)" -cargo-0.49.0/src/cargo/core/package.rs:425:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -cargo-0.49.0/src/cargo/core/package.rs:452:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -cargo-0.49.0/src/cargo/core/package.rs:453:60 clippy::redundant_closure_for_method_calls "redundant closure found" -cargo-0.49.0/src/cargo/core/package.rs:459:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -cargo-0.49.0/src/cargo/core/package.rs:473:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -cargo-0.49.0/src/cargo/core/package.rs:587:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -cargo-0.49.0/src/cargo/core/package.rs:588:9 clippy::needless_question_mark "Question mark operator is useless here" -cargo-0.49.0/src/cargo/core/package.rs:682:46 clippy::cast_possible_truncation "casting `f64` to `u64` may truncate the value" -cargo-0.49.0/src/cargo/core/package.rs:682:46 clippy::cast_sign_loss "casting `f64` to `u64` may lose the sign of the value" -cargo-0.49.0/src/cargo/core/package.rs:682:63 clippy::cast_possible_truncation "casting `f64` to `u64` may truncate the value" -cargo-0.49.0/src/cargo/core/package.rs:682:63 clippy::cast_sign_loss "casting `f64` to `u64` may lose the sign of the value" -cargo-0.49.0/src/cargo/core/package.rs:731:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -cargo-0.49.0/src/cargo/core/package.rs:790:13 clippy::single_match_else "you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let`" -cargo-0.49.0/src/cargo/core/package.rs:988:9 clippy::single_match_else "you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let`" -cargo-0.49.0/src/cargo/core/package_id.rs:115:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -cargo-0.49.0/src/cargo/core/package_id.rs:124:5 clippy::missing_panics_doc "docs for function which may panic missing `# Panics` section" -cargo-0.49.0/src/cargo/core/package_id.rs:124:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/core/package_id.rs:139:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/core/package_id.rs:142:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/core/package_id.rs:145:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/core/package_id.rs:149:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/core/package_id.rs:157:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/core/package_id.rs:161:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/core/package_id.rs:169:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/core/package_id.rs:174:1 clippy::module_name_repetitions "item name starts with its containing module's name" -cargo-0.49.0/src/cargo/core/package_id_spec.rs:101:39 clippy::redundant_closure_for_method_calls "redundant closure found" -cargo-0.49.0/src/cargo/core/package_id_spec.rs:143:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/core/package_id_spec.rs:147:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/core/package_id_spec.rs:151:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/core/package_id_spec.rs:160:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/core/package_id_spec.rs:179:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -cargo-0.49.0/src/cargo/core/package_id_spec.rs:212:9 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope" -cargo-0.49.0/src/cargo/core/package_id_spec.rs:231:9 clippy::single_match_else "you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let`" -cargo-0.49.0/src/cargo/core/package_id_spec.rs:51:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -cargo-0.49.0/src/cargo/core/package_id_spec.rs:51:5 clippy::missing_panics_doc "docs for function which may panic missing `# Panics` section" -cargo-0.49.0/src/cargo/core/package_id_spec.rs:77:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -cargo-0.49.0/src/cargo/core/package_id_spec.rs:88:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/core/profiles.rs:1004:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/core/profiles.rs:1014:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/core/profiles.rs:1018:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/core/profiles.rs:1028:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/core/profiles.rs:106:9 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope" -cargo-0.49.0/src/cargo/core/profiles.rs:143:5 clippy::unnecessary_wraps "this function's return value is unnecessary" -cargo-0.49.0/src/cargo/core/profiles.rs:286:5 clippy::missing_panics_doc "docs for function which may panic missing `# Panics` section" -cargo-0.49.0/src/cargo/core/profiles.rs:286:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/core/profiles.rs:294:40 clippy::if_not_else "unnecessary boolean `not` operation" -cargo-0.49.0/src/cargo/core/profiles.rs:30:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -cargo-0.49.0/src/cargo/core/profiles.rs:342:25 clippy::shadow_unrelated "`maker` is being shadowed" -cargo-0.49.0/src/cargo/core/profiles.rs:370:41 clippy::unused_self "unused `self` argument" -cargo-0.49.0/src/cargo/core/profiles.rs:370:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/core/profiles.rs:372:9 clippy::field_reassign_with_default "field assignment outside of initializer for an instance created with Default::default()" -cargo-0.49.0/src/cargo/core/profiles.rs:382:5 clippy::missing_panics_doc "docs for function which may panic missing `# Panics` section" -cargo-0.49.0/src/cargo/core/profiles.rs:382:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/core/profiles.rs:383:28 clippy::if_not_else "unnecessary boolean `not` operation" -cargo-0.49.0/src/cargo/core/profiles.rs:397:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/core/profiles.rs:405:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -cargo-0.49.0/src/cargo/core/profiles.rs:607:1 clippy::struct_excessive_bools "more than 3 bools in a struct" -cargo-0.49.0/src/cargo/core/profiles.rs:909:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/core/profiles.rs:923:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/core/profiles.rs:934:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/core/profiles.rs:987:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/core/registry.rs:111:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -cargo-0.49.0/src/cargo/core/registry.rs:127:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -cargo-0.49.0/src/cargo/core/registry.rs:168:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -cargo-0.49.0/src/cargo/core/registry.rs:19:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -cargo-0.49.0/src/cargo/core/registry.rs:240:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -cargo-0.49.0/src/cargo/core/registry.rs:26:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -cargo-0.49.0/src/cargo/core/registry.rs:344:49 clippy::redundant_closure_for_method_calls "redundant closure found" -cargo-0.49.0/src/cargo/core/registry.rs:369:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/core/registry.rs:424:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/core/registry.rs:49:1 clippy::module_name_repetitions "item name ends with its containing module's name" -cargo-0.49.0/src/cargo/core/registry.rs:520:17 clippy::single_match_else "you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let`" -cargo-0.49.0/src/cargo/core/registry.rs:763:53 clippy::redundant_closure_for_method_calls "redundant closure found" -cargo-0.49.0/src/cargo/core/registry.rs:765:53 clippy::redundant_closure_for_method_calls "redundant closure found" -cargo-0.49.0/src/cargo/core/registry.rs:807:14 clippy::redundant_closure_for_method_calls "redundant closure found" -cargo-0.49.0/src/cargo/core/registry.rs:814:53 clippy::redundant_closure_for_method_calls "redundant closure found" -cargo-0.49.0/src/cargo/core/resolver/conflict_cache.rs:197:29 clippy::redundant_closure_for_method_calls "redundant closure found" -cargo-0.49.0/src/cargo/core/resolver/conflict_cache.rs:41:38 clippy::map_unwrap_or "called `map(<f>).unwrap_or_else(<g>)` on an `Option` value. This can be done more directly by calling `map_or_else(<g>, <f>)` instead" -cargo-0.49.0/src/cargo/core/resolver/context.rs:274:53 clippy::redundant_closure_for_method_calls "redundant closure found" -cargo-0.49.0/src/cargo/core/resolver/context.rs:42:1 clippy::module_name_repetitions "item name starts with its containing module's name" -cargo-0.49.0/src/cargo/core/resolver/context.rs:74:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/core/resolver/encode.rs:156:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -cargo-0.49.0/src/cargo/core/resolver/encode.rs:156:5 clippy::too_many_lines "this function has too many lines (164/100)" -cargo-0.49.0/src/cargo/core/resolver/encode.rs:339:17 clippy::match_wildcard_for_single_variants "wildcard match will miss any future added variants" -cargo-0.49.0/src/cargo/core/resolver/encode.rs:438:5 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope" -cargo-0.49.0/src/cargo/core/resolver/encode.rs:449:5 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope" -cargo-0.49.0/src/cargo/core/resolver/encode.rs:529:34 clippy::redundant_closure_for_method_calls "redundant closure found" -cargo-0.49.0/src/cargo/core/resolver/encode.rs:602:59 clippy::redundant_closure_for_method_calls "redundant closure found" -cargo-0.49.0/src/cargo/core/resolver/encode.rs:623:1 clippy::module_name_repetitions "item name starts with its containing module's name" -cargo-0.49.0/src/cargo/core/resolver/encode.rs:652:27 clippy::single_match_else "you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let`" -cargo-0.49.0/src/cargo/core/resolver/encode.rs:674:51 clippy::redundant_closure_for_method_calls "redundant closure found" -cargo-0.49.0/src/cargo/core/resolver/errors.rs:103:22 clippy::redundant_closure_for_method_calls "redundant closure found" -cargo-0.49.0/src/cargo/core/resolver/errors.rs:104:22 clippy::redundant_closure_for_method_calls "redundant closure found" -cargo-0.49.0/src/cargo/core/resolver/errors.rs:206:9 clippy::if_not_else "unnecessary boolean `not` operation" -cargo-0.49.0/src/cargo/core/resolver/errors.rs:257:45 clippy::redundant_closure_for_method_calls "redundant closure found" -cargo-0.49.0/src/cargo/core/resolver/errors.rs:27:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/core/resolver/errors.rs:305:17 clippy::single_char_add_str "calling `push_str()` using a single-character string literal" -cargo-0.49.0/src/cargo/core/resolver/errors.rs:70:1 clippy::too_many_lines "this function has too many lines (207/100)" -cargo-0.49.0/src/cargo/core/resolver/features.rs:104:1 clippy::module_name_repetitions "item name starts with its containing module's name" -cargo-0.49.0/src/cargo/core/resolver/features.rs:111:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/core/resolver/features.rs:162:56 clippy::redundant_closure_for_method_calls "redundant closure found" -cargo-0.49.0/src/cargo/core/resolver/features.rs:179:1 clippy::module_name_repetitions "item name ends with its containing module's name" -cargo-0.49.0/src/cargo/core/resolver/features.rs:186:23 clippy::doc_markdown "you should put `RequestedFeatures` between ticks in the documentation" -cargo-0.49.0/src/cargo/core/resolver/features.rs:187:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/core/resolver/features.rs:199:23 clippy::doc_markdown "you should put `RequestedFeatures` between ticks in the documentation" -cargo-0.49.0/src/cargo/core/resolver/features.rs:200:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/core/resolver/features.rs:221:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/core/resolver/features.rs:231:21 clippy::doc_markdown "you should put `pkg_id/is_build` between ticks in the documentation" -cargo-0.49.0/src/cargo/core/resolver/features.rs:233:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/core/resolver/features.rs:247:58 clippy::redundant_closure_for_method_calls "redundant closure found" -cargo-0.49.0/src/cargo/core/resolver/features.rs:278:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -cargo-0.49.0/src/cargo/core/resolver/features.rs:394:27 clippy::doc_markdown "you should put `FeatureValue` between ticks in the documentation" -cargo-0.49.0/src/cargo/core/resolver/features.rs:460:19 clippy::single_match_else "you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let`" -cargo-0.49.0/src/cargo/core/resolver/features.rs:480:24 clippy::doc_markdown "you should put `FeatureValues` between ticks in the documentation" -cargo-0.49.0/src/cargo/core/resolver/features.rs:496:24 clippy::doc_markdown "you should put `FeatureValues` between ticks in the documentation" -cargo-0.49.0/src/cargo/core/resolver/features.rs:58:1 clippy::module_name_repetitions "item name ends with its containing module's name" -cargo-0.49.0/src/cargo/core/resolver/features.rs:67:1 clippy::struct_excessive_bools "more than 3 bools in a struct" -cargo-0.49.0/src/cargo/core/resolver/mod.rs:1017:5 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope" -cargo-0.49.0/src/cargo/core/resolver/mod.rs:1045:57 clippy::redundant_closure_for_method_calls "redundant closure found" -cargo-0.49.0/src/cargo/core/resolver/mod.rs:122:1 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -cargo-0.49.0/src/cargo/core/resolver/mod.rs:142:44 clippy::redundant_closure_for_method_calls "redundant closure found" -cargo-0.49.0/src/cargo/core/resolver/mod.rs:180:1 clippy::too_many_lines "this function has too many lines (225/100)" -cargo-0.49.0/src/cargo/core/resolver/mod.rs:311:17 clippy::single_match_else "you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let`" -cargo-0.49.0/src/cargo/core/resolver/mod.rs:421:52 clippy::filter_map_next "called `filter_map(..).next()` on an `Iterator`. This is more succinctly expressed by calling `.find_map(..)` instead." -cargo-0.49.0/src/cargo/core/resolver/mod.rs:457:69 clippy::filter_map_next "called `filter_map(..).next()` on an `Iterator`. This is more succinctly expressed by calling `.find_map(..)` instead." -cargo-0.49.0/src/cargo/core/resolver/mod.rs:470:37 clippy::similar_names "binding's name is too similar to existing binding" -cargo-0.49.0/src/cargo/core/resolver/mod.rs:607:11 clippy::needless_pass_by_value "this argument is passed by value, but not consumed in the function body" -cargo-0.49.0/src/cargo/core/resolver/mod.rs:631:21 clippy::single_match_else "you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let`" -cargo-0.49.0/src/cargo/core/resolver/mod.rs:942:15 clippy::if_not_else "unnecessary boolean `not` operation" -cargo-0.49.0/src/cargo/core/resolver/mod.rs:988:20 clippy::redundant_else "redundant else block" -cargo-0.49.0/src/cargo/core/resolver/resolve.rs:120:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/core/resolver/resolve.rs:132:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -cargo-0.49.0/src/cargo/core/resolver/resolve.rs:199:24 clippy::redundant_else "redundant else block" -cargo-0.49.0/src/cargo/core/resolver/resolve.rs:235:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/core/resolver/resolve.rs:239:5 clippy::needless_lifetimes "explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration)" -cargo-0.49.0/src/cargo/core/resolver/resolve.rs:255:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/core/resolver/resolve.rs:259:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/core/resolver/resolve.rs:263:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/core/resolver/resolve.rs:269:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/core/resolver/resolve.rs:273:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/core/resolver/resolve.rs:274:9 clippy::map_unwrap_or "called `map(<f>).unwrap_or_else(<g>)` on an `Option` value. This can be done more directly by calling `map_or_else(<g>, <f>)` instead" -cargo-0.49.0/src/cargo/core/resolver/resolve.rs:280:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -cargo-0.49.0/src/cargo/core/resolver/resolve.rs:284:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -cargo-0.49.0/src/cargo/core/resolver/resolve.rs:288:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/core/resolver/resolve.rs:292:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/core/resolver/resolve.rs:296:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/core/resolver/resolve.rs:300:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -cargo-0.49.0/src/cargo/core/resolver/resolve.rs:315:13 clippy::map_unwrap_or "called `map(<f>).unwrap_or_else(<g>)` on an `Option` value. This can be done more directly by calling `map_or_else(<g>, <f>)` instead" -cargo-0.49.0/src/cargo/core/resolver/resolve.rs:354:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/core/resolver/resolve.rs:362:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/core/resolver/resolve.rs:60:1 clippy::module_name_repetitions "item name starts with its containing module's name" -cargo-0.49.0/src/cargo/core/resolver/resolve.rs:76:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/core/resolver/types.rs:111:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -cargo-0.49.0/src/cargo/core/resolver/types.rs:121:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/core/resolver/types.rs:141:19 clippy::doc_markdown "you should put `ResolveOpts` between ticks in the documentation" -cargo-0.49.0/src/cargo/core/resolver/types.rs:142:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/core/resolver/types.rs:149:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/core/resolver/types.rs:181:9 clippy::map_unwrap_or "called `map(<f>).unwrap_or(<a>)` on an `Option` value. This can be done more directly by calling `map_or(<a>, <f>)` instead" -cargo-0.49.0/src/cargo/core/resolver/types.rs:187:5 clippy::needless_lifetimes "explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration)" -cargo-0.49.0/src/cargo/core/resolver/types.rs:261:5 clippy::needless_lifetimes "explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration)" -cargo-0.49.0/src/cargo/core/shell.rs:113:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/core/shell.rs:130:9 clippy::single_match_else "you seem to be trying to use `match` for an equality check. Consider using `if`" -cargo-0.49.0/src/cargo/core/shell.rs:148:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/core/shell.rs:153:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/core/shell.rs:163:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/core/shell.rs:18:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/core/shell.rs:198:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -cargo-0.49.0/src/cargo/core/shell.rs:206:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -cargo-0.49.0/src/cargo/core/shell.rs:214:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -cargo-0.49.0/src/cargo/core/shell.rs:228:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -cargo-0.49.0/src/cargo/core/shell.rs:239:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -cargo-0.49.0/src/cargo/core/shell.rs:250:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -cargo-0.49.0/src/cargo/core/shell.rs:259:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -cargo-0.49.0/src/cargo/core/shell.rs:267:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -cargo-0.49.0/src/cargo/core/shell.rs:26:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/core/shell.rs:277:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/core/shell.rs:282:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -cargo-0.49.0/src/cargo/core/shell.rs:314:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/core/shell.rs:322:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/core/shell.rs:330:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -cargo-0.49.0/src/cargo/core/shell.rs:345:5 clippy::missing_panics_doc "docs for function which may panic missing `# Panics` section" -cargo-0.49.0/src/cargo/core/shell.rs:459:9 clippy::let_underscore_drop "non-binding `let` on a type that implements `Drop`" -cargo-0.49.0/src/cargo/core/shell.rs:98:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/core/source/mod.rs:103:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -cargo-0.49.0/src/cargo/core/source/mod.rs:247:1 clippy::module_name_repetitions "item name starts with its containing module's name" -cargo-0.49.0/src/cargo/core/source/mod.rs:261:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/core/source/mod.rs:268:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/core/source/mod.rs:273:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/core/source/mod.rs:291:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/core/source/mod.rs:302:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/core/source/mod.rs:307:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/core/source/mod.rs:31:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -cargo-0.49.0/src/cargo/core/source/mod.rs:37:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -cargo-0.49.0/src/cargo/core/source/mod.rs:39:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -cargo-0.49.0/src/cargo/core/source/mod.rs:47:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -cargo-0.49.0/src/cargo/core/source/mod.rs:50:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -cargo-0.49.0/src/cargo/core/source/mod.rs:52:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -cargo-0.49.0/src/cargo/core/source/mod.rs:63:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -cargo-0.49.0/src/cargo/core/source/mod.rs:74:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -cargo-0.49.0/src/cargo/core/source/mod.rs:83:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -cargo-0.49.0/src/cargo/core/source/source_id.rs:107:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -cargo-0.49.0/src/cargo/core/source/source_id.rs:107:5 clippy::missing_panics_doc "docs for function which may panic missing `# Panics` section" -cargo-0.49.0/src/cargo/core/source/source_id.rs:128:50 clippy::redundant_closure_for_method_calls "redundant closure found" -cargo-0.49.0/src/cargo/core/source/source_id.rs:147:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/core/source/source_id.rs:156:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -cargo-0.49.0/src/cargo/core/source/source_id.rs:162:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -cargo-0.49.0/src/cargo/core/source/source_id.rs:166:19 clippy::doc_markdown "you should put `SourceId` between ticks in the documentation" -cargo-0.49.0/src/cargo/core/source/source_id.rs:167:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -cargo-0.49.0/src/cargo/core/source/source_id.rs:171:19 clippy::doc_markdown "you should put `SourceId` between ticks in the documentation" -cargo-0.49.0/src/cargo/core/source/source_id.rs:172:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -cargo-0.49.0/src/cargo/core/source/source_id.rs:178:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -cargo-0.49.0/src/cargo/core/source/source_id.rs:187:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -cargo-0.49.0/src/cargo/core/source/source_id.rs:187:5 clippy::missing_panics_doc "docs for function which may panic missing `# Panics` section" -cargo-0.49.0/src/cargo/core/source/source_id.rs:18:74 clippy::default_trait_access "calling `std::sync::Mutex::default()` is more clear than this expression" -cargo-0.49.0/src/cargo/core/source/source_id.rs:195:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -cargo-0.49.0/src/cargo/core/source/source_id.rs:207:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/core/source/source_id.rs:213:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/core/source/source_id.rs:217:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/core/source/source_id.rs:225:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/core/source/source_id.rs:228:16 clippy::option_if_let_else "use Option::map_or_else instead of an if let/else" -cargo-0.49.0/src/cargo/core/source/source_id.rs:236:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/core/source/source_id.rs:241:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/core/source/source_id.rs:252:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/core/source/source_id.rs:257:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/core/source/source_id.rs:262:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -cargo-0.49.0/src/cargo/core/source/source_id.rs:305:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/core/source/source_id.rs:310:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/core/source/source_id.rs:318:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/core/source/source_id.rs:326:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/core/source/source_id.rs:338:5 clippy::missing_panics_doc "docs for function which may panic missing `# Panics` section" -cargo-0.49.0/src/cargo/core/source/source_id.rs:355:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/core/source/source_id.rs:393:61 clippy::match_same_arms "this `match` has identical arm bodies" -cargo-0.49.0/src/cargo/core/source/source_id.rs:394:42 clippy::match_same_arms "this `match` has identical arm bodies" -cargo-0.49.0/src/cargo/core/source/source_id.rs:395:42 clippy::match_same_arms "this `match` has identical arm bodies" -cargo-0.49.0/src/cargo/core/source/source_id.rs:397:71 clippy::match_same_arms "this `match` has identical arm bodies" -cargo-0.49.0/src/cargo/core/source/source_id.rs:397:71 clippy::match_same_arms "this `match` has identical arm bodies" -cargo-0.49.0/src/cargo/core/source/source_id.rs:398:47 clippy::match_same_arms "this `match` has identical arm bodies" -cargo-0.49.0/src/cargo/core/source/source_id.rs:398:47 clippy::match_same_arms "this `match` has identical arm bodies" -cargo-0.49.0/src/cargo/core/source/source_id.rs:399:47 clippy::match_same_arms "this `match` has identical arm bodies" -cargo-0.49.0/src/cargo/core/source/source_id.rs:399:47 clippy::match_same_arms "this `match` has identical arm bodies" -cargo-0.49.0/src/cargo/core/source/source_id.rs:401:63 clippy::match_same_arms "this `match` has identical arm bodies" -cargo-0.49.0/src/cargo/core/source/source_id.rs:401:63 clippy::match_same_arms "this `match` has identical arm bodies" -cargo-0.49.0/src/cargo/core/source/source_id.rs:401:63 clippy::match_same_arms "this `match` has identical arm bodies" -cargo-0.49.0/src/cargo/core/source/source_id.rs:402:43 clippy::match_same_arms "this `match` has identical arm bodies" -cargo-0.49.0/src/cargo/core/source/source_id.rs:402:43 clippy::match_same_arms "this `match` has identical arm bodies" -cargo-0.49.0/src/cargo/core/source/source_id.rs:402:43 clippy::match_same_arms "this `match` has identical arm bodies" -cargo-0.49.0/src/cargo/core/source/source_id.rs:403:43 clippy::match_same_arms "this `match` has identical arm bodies" -cargo-0.49.0/src/cargo/core/source/source_id.rs:403:43 clippy::match_same_arms "this `match` has identical arm bodies" -cargo-0.49.0/src/cargo/core/source/source_id.rs:403:43 clippy::match_same_arms "this `match` has identical arm bodies" -cargo-0.49.0/src/cargo/core/source/source_id.rs:406:21 clippy::enum_glob_use "usage of wildcard import for enum variants" -cargo-0.49.0/src/cargo/core/source/source_id.rs:412:41 clippy::match_same_arms "this `match` has identical arm bodies" -cargo-0.49.0/src/cargo/core/source/source_id.rs:413:36 clippy::match_same_arms "this `match` has identical arm bodies" -cargo-0.49.0/src/cargo/core/source/source_id.rs:414:36 clippy::match_same_arms "this `match` has identical arm bodies" -cargo-0.49.0/src/cargo/core/source/source_id.rs:420:47 clippy::match_same_arms "this `match` has identical arm bodies" -cargo-0.49.0/src/cargo/core/source/source_id.rs:420:47 clippy::match_same_arms "this `match` has identical arm bodies" -cargo-0.49.0/src/cargo/core/source/source_id.rs:512:17 clippy::unseparated_literal_suffix "integer type suffix should be separated by an underscore" -cargo-0.49.0/src/cargo/core/source/source_id.rs:513:17 clippy::unseparated_literal_suffix "integer type suffix should be separated by an underscore" -cargo-0.49.0/src/cargo/core/source/source_id.rs:517:17 clippy::unseparated_literal_suffix "integer type suffix should be separated by an underscore" -cargo-0.49.0/src/cargo/core/source/source_id.rs:518:17 clippy::unseparated_literal_suffix "integer type suffix should be separated by an underscore" -cargo-0.49.0/src/cargo/core/source/source_id.rs:525:17 clippy::unseparated_literal_suffix "integer type suffix should be separated by an underscore" -cargo-0.49.0/src/cargo/core/source/source_id.rs:526:17 clippy::unseparated_literal_suffix "integer type suffix should be separated by an underscore" -cargo-0.49.0/src/cargo/core/source/source_id.rs:530:17 clippy::unseparated_literal_suffix "integer type suffix should be separated by an underscore" -cargo-0.49.0/src/cargo/core/source/source_id.rs:531:17 clippy::unseparated_literal_suffix "integer type suffix should be separated by an underscore" -cargo-0.49.0/src/cargo/core/source/source_id.rs:535:33 clippy::unseparated_literal_suffix "integer type suffix should be separated by an underscore" -cargo-0.49.0/src/cargo/core/source/source_id.rs:536:37 clippy::unseparated_literal_suffix "integer type suffix should be separated by an underscore" -cargo-0.49.0/src/cargo/core/source/source_id.rs:537:42 clippy::unseparated_literal_suffix "integer type suffix should be separated by an underscore" -cargo-0.49.0/src/cargo/core/source/source_id.rs:538:38 clippy::unseparated_literal_suffix "integer type suffix should be separated by an underscore" -cargo-0.49.0/src/cargo/core/source/source_id.rs:548:1 clippy::module_name_repetitions "item name starts with its containing module's name" -cargo-0.49.0/src/cargo/core/source/source_id.rs:597:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/core/summary.rs:103:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/core/summary.rs:123:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/core/summary.rs:150:1 clippy::too_many_lines "this function has too many lines (141/100)" -cargo-0.49.0/src/cargo/core/summary.rs:158:9 clippy::enum_glob_use "usage of wildcard import for enum variants" -cargo-0.49.0/src/cargo/core/summary.rs:181:21 clippy::if_not_else "unnecessary boolean `not` operation" -cargo-0.49.0/src/cargo/core/summary.rs:192:28 clippy::redundant_else "redundant else block" -cargo-0.49.0/src/cargo/core/summary.rs:258:32 clippy::redundant_else "redundant else block" -cargo-0.49.0/src/cargo/core/summary.rs:281:28 clippy::redundant_else "redundant else block" -cargo-0.49.0/src/cargo/core/summary.rs:303:28 clippy::redundant_else "redundant else block" -cargo-0.49.0/src/cargo/core/summary.rs:321:51 clippy::match_same_arms "this `match` has identical arm bodies" -cargo-0.49.0/src/cargo/core/summary.rs:344:5 clippy::doc_markdown "you should put `FeatureValue` between ticks in the documentation" -cargo-0.49.0/src/cargo/core/summary.rs:350:85 clippy::doc_markdown "you should put `FeatureValue` between ticks in the documentation" -cargo-0.49.0/src/cargo/core/summary.rs:36:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -cargo-0.49.0/src/cargo/core/summary.rs:378:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/core/summary.rs:386:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/core/summary.rs:387:13 clippy::enum_glob_use "usage of wildcard import for enum variants" -cargo-0.49.0/src/cargo/core/summary.rs:407:13 clippy::enum_glob_use "usage of wildcard import for enum variants" -cargo-0.49.0/src/cargo/core/summary.rs:69:34 clippy::redundant_closure_for_method_calls "redundant closure found" -cargo-0.49.0/src/cargo/core/summary.rs:75:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/core/summary.rs:78:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/core/summary.rs:81:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/core/summary.rs:84:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/core/summary.rs:87:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/core/summary.rs:90:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/core/summary.rs:93:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/core/summary.rs:96:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/core/summary.rs:99:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/core/workspace.rs:1056:5 clippy::unnecessary_wraps "this function's return value is unnecessarily wrapped by `Result`" -cargo-0.49.0/src/cargo/core/workspace.rs:113:1 clippy::module_name_repetitions "item name starts with its containing module's name" -cargo-0.49.0/src/cargo/core/workspace.rs:1157:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/core/workspace.rs:128:1 clippy::module_name_repetitions "item name starts with its containing module's name" -cargo-0.49.0/src/cargo/core/workspace.rs:150:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -cargo-0.49.0/src/cargo/core/workspace.rs:159:16 clippy::redundant_else "redundant else block" -cargo-0.49.0/src/cargo/core/workspace.rs:197:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -cargo-0.49.0/src/cargo/core/workspace.rs:225:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -cargo-0.49.0/src/cargo/core/workspace.rs:225:5 clippy::missing_panics_doc "docs for function which may panic missing `# Panics` section" -cargo-0.49.0/src/cargo/core/workspace.rs:255:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -cargo-0.49.0/src/cargo/core/workspace.rs:267:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -cargo-0.49.0/src/cargo/core/workspace.rs:317:5 clippy::missing_panics_doc "docs for function which may panic missing `# Panics` section" -cargo-0.49.0/src/cargo/core/workspace.rs:329:37 clippy::doc_markdown "you should put `VirtualManifest` between ticks in the documentation" -cargo-0.49.0/src/cargo/core/workspace.rs:410:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -cargo-0.49.0/src/cargo/core/workspace.rs:440:9 clippy::unnecessary_wraps "this function's return value is unnecessarily wrapped by `Result`" -cargo-0.49.0/src/cargo/core/workspace.rs:511:32 clippy::single_match_else "you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let`" -cargo-0.49.0/src/cargo/core/workspace.rs:561:25 clippy::non_ascii_literal "literal non-ASCII character detected" -cargo-0.49.0/src/cargo/core/workspace.rs:613:13 clippy::filter_map "called `filter_map(..).map(..)` on an `Iterator`" -cargo-0.49.0/src/cargo/core/workspace.rs:615:22 clippy::redundant_closure_for_method_calls "redundant closure found" -cargo-0.49.0/src/cargo/core/workspace.rs:762:27 clippy::single_match_else "you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let`" -cargo-0.49.0/src/cargo/core/workspace.rs:784:17 clippy::if_not_else "unnecessary boolean `not` operation" -cargo-0.49.0/src/cargo/core/workspace.rs:849:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -cargo-0.49.0/src/cargo/core/workspace.rs:849:5 clippy::missing_panics_doc "docs for function which may panic missing `# Panics` section" -cargo-0.49.0/src/cargo/core/workspace.rs:893:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -cargo-0.49.0/src/cargo/core/workspace.rs:906:24 clippy::redundant_else "redundant else block" -cargo-0.49.0/src/cargo/core/workspace.rs:932:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -cargo-0.49.0/src/cargo/lib.rs:177:1 clippy::missing_panics_doc "docs for function which may panic missing `# Panics` section" -cargo-0.49.0/src/cargo/lib.rs:177:1 clippy::must_use_candidate "this function could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/lib.rs:180:36 clippy::redundant_closure_for_method_calls "redundant closure found" -cargo-0.49.0/src/cargo/lib.rs:180:36 clippy::redundant_closure_for_method_calls "redundant closure found" -cargo-0.49.0/src/cargo/lib.rs:180:36 clippy::redundant_closure_for_method_calls "redundant closure found" -cargo-0.49.0/src/cargo/lib.rs:180:36 clippy::redundant_closure_for_method_calls "redundant closure found" -cargo-0.49.0/src/cargo/lib.rs:180:36 clippy::redundant_closure_for_method_calls "redundant closure found" -cargo-0.49.0/src/cargo/lib.rs:1:null clippy::cargo_common_metadata "package `cargo` is missing `package.categories` metadata" -cargo-0.49.0/src/cargo/lib.rs:1:null clippy::cargo_common_metadata "package `cargo` is missing `package.keywords` metadata" -cargo-0.49.0/src/cargo/lib.rs:1:null clippy::multiple_crate_versions "multiple versions for dependency `crossbeam-utils`: 0.6.6, 0.7.2" -cargo-0.49.0/src/cargo/lib.rs:1:null clippy::multiple_crate_versions "multiple versions for dependency `hex`: 0.3.2, 0.4.0" -cargo-0.49.0/src/cargo/lib.rs:1:null clippy::multiple_crate_versions "multiple versions for dependency `humantime`: 1.3.0, 2.0.0" -cargo-0.49.0/src/cargo/ops/cargo_clean.rs:205:23 clippy::redundant_closure_for_method_calls "redundant closure found" -cargo-0.49.0/src/cargo/ops/cargo_clean.rs:27:1 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -cargo-0.49.0/src/cargo/ops/cargo_clean.rs:27:1 clippy::too_many_lines "this function has too many lines (120/100)" -cargo-0.49.0/src/cargo/ops/cargo_compile.rs:1078:14 clippy::redundant_closure_for_method_calls "redundant closure found" -cargo-0.49.0/src/cargo/ops/cargo_compile.rs:109:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -cargo-0.49.0/src/cargo/ops/cargo_compile.rs:119:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -cargo-0.49.0/src/cargo/ops/cargo_compile.rs:1227:17 clippy::needless_pass_by_value "this argument is passed by value, but not consumed in the function body" -cargo-0.49.0/src/cargo/ops/cargo_compile.rs:127:35 clippy::from_iter_instead_of_collect "usage of `FromIterator::from_iter`" -cargo-0.49.0/src/cargo/ops/cargo_compile.rs:173:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -cargo-0.49.0/src/cargo/ops/cargo_compile.rs:205:36 clippy::match_same_arms "this `match` has identical arm bodies" -cargo-0.49.0/src/cargo/ops/cargo_compile.rs:242:1 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -cargo-0.49.0/src/cargo/ops/cargo_compile.rs:249:1 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -cargo-0.49.0/src/cargo/ops/cargo_compile.rs:258:1 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -cargo-0.49.0/src/cargo/ops/cargo_compile.rs:267:16 clippy::needless_question_mark "Question mark operator is useless here" -cargo-0.49.0/src/cargo/ops/cargo_compile.rs:275:1 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -cargo-0.49.0/src/cargo/ops/cargo_compile.rs:275:1 clippy::too_many_lines "this function has too many lines (219/100)" -cargo-0.49.0/src/cargo/ops/cargo_compile.rs:468:9 clippy::default_trait_access "calling `std::collections::HashMap::default()` is more clear than this expression" -cargo-0.49.0/src/cargo/ops/cargo_compile.rs:548:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/ops/cargo_compile.rs:556:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/ops/cargo_compile.rs:574:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/ops/cargo_compile.rs:583:21 clippy::doc_markdown "you should put `CompileFilter` between ticks in the documentation" -cargo-0.49.0/src/cargo/ops/cargo_compile.rs:584:5 clippy::fn_params_excessive_bools "more than 3 bools in function parameters" -cargo-0.49.0/src/cargo/ops/cargo_compile.rs:584:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/ops/cargo_compile.rs:592:9 clippy::similar_names "binding's name is too similar to existing binding" -cargo-0.49.0/src/cargo/ops/cargo_compile.rs:593:9 clippy::similar_names "binding's name is too similar to existing binding" -cargo-0.49.0/src/cargo/ops/cargo_compile.rs:607:13 clippy::similar_names "binding's name is too similar to existing binding" -cargo-0.49.0/src/cargo/ops/cargo_compile.rs:612:21 clippy::doc_markdown "you should put `CompileFilter` between ticks in the documentation" -cargo-0.49.0/src/cargo/ops/cargo_compile.rs:613:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/ops/cargo_compile.rs:618:9 clippy::similar_names "binding's name is too similar to existing binding" -cargo-0.49.0/src/cargo/ops/cargo_compile.rs:641:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/ops/cargo_compile.rs:652:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/ops/cargo_compile.rs:655:50 clippy::match_same_arms "this `match` has identical arm bodies" -cargo-0.49.0/src/cargo/ops/cargo_compile.rs:673:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/ops/cargo_compile.rs:692:49 clippy::match_same_arms "this `match` has identical arm bodies" -cargo-0.49.0/src/cargo/ops/cargo_compile.rs:703:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/ops/cargo_compile.rs:729:1 clippy::too_many_lines "this function has too many lines (205/100)" -cargo-0.49.0/src/cargo/ops/cargo_compile.rs:82:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -cargo-0.49.0/src/cargo/ops/cargo_compile.rs:874:69 clippy::redundant_closure_for_method_calls "redundant closure found" -cargo-0.49.0/src/cargo/ops/cargo_doc.rs:20:1 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -cargo-0.49.0/src/cargo/ops/cargo_fetch.rs:15:1 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -cargo-0.49.0/src/cargo/ops/cargo_fetch.rs:27:46 clippy::redundant_closure_for_method_calls "redundant closure found" -cargo-0.49.0/src/cargo/ops/cargo_generate_lockfile.rs:160:5 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope" -cargo-0.49.0/src/cargo/ops/cargo_generate_lockfile.rs:175:5 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope" -cargo-0.49.0/src/cargo/ops/cargo_generate_lockfile.rs:22:1 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -cargo-0.49.0/src/cargo/ops/cargo_generate_lockfile.rs:37:1 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -cargo-0.49.0/src/cargo/ops/cargo_generate_lockfile.rs:37:1 clippy::missing_panics_doc "docs for function which may panic missing `# Panics` section" -cargo-0.49.0/src/cargo/ops/cargo_generate_lockfile.rs:37:1 clippy::too_many_lines "this function has too many lines (171/100)" -cargo-0.49.0/src/cargo/ops/cargo_install.rs:13:5 clippy::wildcard_imports "usage of wildcard import" -cargo-0.49.0/src/cargo/ops/cargo_install.rs:148:1 clippy::fn_params_excessive_bools "more than 3 bools in function parameters" -cargo-0.49.0/src/cargo/ops/cargo_install.rs:148:1 clippy::too_many_lines "this function has too many lines (316/100)" -cargo-0.49.0/src/cargo/ops/cargo_install.rs:178:24 clippy::collapsible_else_if "this `else { if .. }` block can be collapsed" -cargo-0.49.0/src/cargo/ops/cargo_install.rs:202:17 clippy::redundant_closure_for_method_calls "redundant closure found" -cargo-0.49.0/src/cargo/ops/cargo_install.rs:236:16 clippy::collapsible_else_if "this `else { if .. }` block can be collapsed" -cargo-0.49.0/src/cargo/ops/cargo_install.rs:312:64 clippy::redundant_closure_for_method_calls "redundant closure found" -cargo-0.49.0/src/cargo/ops/cargo_install.rs:32:13 clippy::let_underscore_drop "non-binding `let` on a type that implements `Drop`" -cargo-0.49.0/src/cargo/ops/cargo_install.rs:339:12 clippy::collapsible_else_if "this `else { if .. }` block can be collapsed" -cargo-0.49.0/src/cargo/ops/cargo_install.rs:37:1 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -cargo-0.49.0/src/cargo/ops/cargo_install.rs:454:22 clippy::redundant_closure_for_method_calls "redundant closure found" -cargo-0.49.0/src/cargo/ops/cargo_install.rs:483:5 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope" -cargo-0.49.0/src/cargo/ops/cargo_install.rs:683:1 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -cargo-0.49.0/src/cargo/ops/cargo_install.rs:708:5 clippy::manual_flatten "unnecessary `if let` since only the `Some` variant of the iterator element is used" -cargo-0.49.0/src/cargo/ops/cargo_new.rs:101:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -cargo-0.49.0/src/cargo/ops/cargo_new.rs:245:5 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope" -cargo-0.49.0/src/cargo/ops/cargo_new.rs:251:5 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope" -cargo-0.49.0/src/cargo/ops/cargo_new.rs:367:1 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -cargo-0.49.0/src/cargo/ops/cargo_new.rs:405:1 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -cargo-0.49.0/src/cargo/ops/cargo_new.rs:489:5 clippy::doc_markdown "you should put `IgnoreList` between ticks in the documentation" -cargo-0.49.0/src/cargo/ops/cargo_new.rs:525:47 clippy::doc_markdown "you should put `IgnoreList` between ticks in the documentation" -cargo-0.49.0/src/cargo/ops/cargo_new.rs:525:9 clippy::doc_markdown "you should put `format_existing` between ticks in the documentation" -cargo-0.49.0/src/cargo/ops/cargo_new.rs:572:34 clippy::match_same_arms "this `match` has identical arm bodies" -cargo-0.49.0/src/cargo/ops/cargo_new.rs:623:1 clippy::too_many_lines "this function has too many lines (130/100)" -cargo-0.49.0/src/cargo/ops/cargo_new.rs:781:5 clippy::filter_map_next "called `filter_map(..).next()` on an `Iterator`. This is more succinctly expressed by calling `.find_map(..)` instead." -cargo-0.49.0/src/cargo/ops/cargo_new.rs:800:16 clippy::single_match_else "you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let`" -cargo-0.49.0/src/cargo/ops/cargo_output_metadata.rs:163:36 clippy::redundant_closure_for_method_calls "redundant closure found" -cargo-0.49.0/src/cargo/ops/cargo_output_metadata.rs:27:1 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -cargo-0.49.0/src/cargo/ops/cargo_output_metadata.rs:45:45 clippy::redundant_closure_for_method_calls "redundant closure found" -cargo-0.49.0/src/cargo/ops/cargo_package.rs:144:1 clippy::too_many_lines "this function has too many lines (112/100)" -cargo-0.49.0/src/cargo/ops/cargo_package.rs:207:13 clippy::single_match_else "you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let`" -cargo-0.49.0/src/cargo/ops/cargo_package.rs:25:1 clippy::struct_excessive_bools "more than 3 bools in a struct" -cargo-0.49.0/src/cargo/ops/cargo_package.rs:307:54 clippy::redundant_closure_for_method_calls "redundant closure found" -cargo-0.49.0/src/cargo/ops/cargo_package.rs:394:5 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope" -cargo-0.49.0/src/cargo/ops/cargo_package.rs:425:61 clippy::redundant_closure_for_method_calls "redundant closure found" -cargo-0.49.0/src/cargo/ops/cargo_package.rs:459:5 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope" -cargo-0.49.0/src/cargo/ops/cargo_package.rs:66:1 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -cargo-0.49.0/src/cargo/ops/cargo_package.rs:69:9 clippy::let_underscore_drop "non-binding `let` on a type that implements `Drop`" -cargo-0.49.0/src/cargo/ops/cargo_package.rs:93:20 clippy::if_not_else "unnecessary boolean `not` operation" -cargo-0.49.0/src/cargo/ops/cargo_pkgid.rs:5:1 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -cargo-0.49.0/src/cargo/ops/cargo_read_manifest.rs:14:1 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -cargo-0.49.0/src/cargo/ops/cargo_read_manifest.rs:171:5 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope" -cargo-0.49.0/src/cargo/ops/cargo_read_manifest.rs:37:1 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -cargo-0.49.0/src/cargo/ops/cargo_read_manifest.rs:37:1 clippy::missing_panics_doc "docs for function which may panic missing `# Panics` section" -cargo-0.49.0/src/cargo/ops/cargo_read_manifest.rs:57:49 clippy::redundant_closure_for_method_calls "redundant closure found" -cargo-0.49.0/src/cargo/ops/cargo_read_manifest.rs:69:37 clippy::redundant_closure_for_method_calls "redundant closure found" -cargo-0.49.0/src/cargo/ops/cargo_run.rs:25:24 clippy::if_not_else "unnecessary boolean `not` operation" -cargo-0.49.0/src/cargo/ops/cargo_run.rs:35:9 clippy::if_not_else "unnecessary boolean `not` operation" -cargo-0.49.0/src/cargo/ops/cargo_run.rs:37:16 clippy::redundant_else "redundant else block" -cargo-0.49.0/src/cargo/ops/cargo_run.rs:53:9 clippy::if_not_else "unnecessary boolean `not` operation" -cargo-0.49.0/src/cargo/ops/cargo_run.rs:65:16 clippy::redundant_else "redundant else block" -cargo-0.49.0/src/cargo/ops/cargo_run.rs:9:1 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -cargo-0.49.0/src/cargo/ops/cargo_test.rs:16:1 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -cargo-0.49.0/src/cargo/ops/cargo_test.rs:43:1 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -cargo-0.49.0/src/cargo/ops/cargo_test.rs:84:17 clippy::similar_names "binding's name is too similar to existing binding" -cargo-0.49.0/src/cargo/ops/cargo_uninstall.rs:14:1 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -cargo-0.49.0/src/cargo/ops/cargo_uninstall.rs:7:5 clippy::wildcard_imports "usage of wildcard import" -cargo-0.49.0/src/cargo/ops/common_for_install_and_uninstall.rs:147:9 clippy::doc_markdown "you should put `PackageId` between ticks in the documentation" -cargo-0.49.0/src/cargo/ops/common_for_install_and_uninstall.rs:233:21 clippy::single_char_add_str "calling `push_str()` using a single-character string literal" -cargo-0.49.0/src/cargo/ops/common_for_install_and_uninstall.rs:244:22 clippy::doc_markdown "you should put `PackageId` between ticks in the documentation" -cargo-0.49.0/src/cargo/ops/common_for_install_and_uninstall.rs:244:63 clippy::doc_markdown "you should put `PackageId` between ticks in the documentation" -cargo-0.49.0/src/cargo/ops/common_for_install_and_uninstall.rs:253:17 clippy::if_not_else "unnecessary boolean `not` operation" -cargo-0.49.0/src/cargo/ops/common_for_install_and_uninstall.rs:370:5 clippy::unnecessary_wraps "this function's return value is unnecessary" -cargo-0.49.0/src/cargo/ops/common_for_install_and_uninstall.rs:505:8 clippy::map_unwrap_or "called `map(<f>).unwrap_or_else(<g>)` on an `Option` value. This can be done more directly by calling `map_or_else(<g>, <f>)` instead" -cargo-0.49.0/src/cargo/ops/common_for_install_and_uninstall.rs:525:10 clippy::needless_pass_by_value "this argument is passed by value, but not consumed in the function body" -cargo-0.49.0/src/cargo/ops/common_for_install_and_uninstall.rs:542:27 clippy::redundant_closure_for_method_calls "redundant closure found" -cargo-0.49.0/src/cargo/ops/common_for_install_and_uninstall.rs:542:5 clippy::single_match_else "you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let`" -cargo-0.49.0/src/cargo/ops/common_for_install_and_uninstall.rs:561:20 clippy::redundant_else "redundant else block" -cargo-0.49.0/src/cargo/ops/common_for_install_and_uninstall.rs:613:5 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope" -cargo-0.49.0/src/cargo/ops/common_for_install_and_uninstall.rs:645:41 clippy::doc_markdown "you should put `BTreeSet` between ticks in the documentation" -cargo-0.49.0/src/cargo/ops/common_for_install_and_uninstall.rs:92:19 clippy::doc_markdown "you should put `InstallTracker` between ticks in the documentation" -cargo-0.49.0/src/cargo/ops/fix.rs:200:1 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -cargo-0.49.0/src/cargo/ops/fix.rs:200:1 clippy::module_name_repetitions "item name starts with its containing module's name" -cargo-0.49.0/src/cargo/ops/fix.rs:424:20 clippy::map_unwrap_or "called `map(<f>).unwrap_or(<a>)` on an `Option` value. This can be done more directly by calling `map_or(<a>, <f>)` instead" -cargo-0.49.0/src/cargo/ops/fix.rs:455:13 clippy::similar_names "binding's name is too similar to existing binding" -cargo-0.49.0/src/cargo/ops/fix.rs:506:17 clippy::similar_names "binding's name is too similar to existing binding" -cargo-0.49.0/src/cargo/ops/fix.rs:608:9 clippy::field_reassign_with_default "field assignment outside of initializer for an instance created with Default::default()" -cargo-0.49.0/src/cargo/ops/fix.rs:612:42 clippy::redundant_closure_for_method_calls "redundant closure found" -cargo-0.49.0/src/cargo/ops/fix.rs:619:48 clippy::manual_strip "stripping a prefix manually" -cargo-0.49.0/src/cargo/ops/fix.rs:66:1 clippy::module_name_repetitions "item name starts with its containing module's name" -cargo-0.49.0/src/cargo/ops/fix.rs:66:1 clippy::struct_excessive_bools "more than 3 bools in a struct" -cargo-0.49.0/src/cargo/ops/fix.rs:708:18 clippy::match_same_arms "this `match` has identical arm bodies" -cargo-0.49.0/src/cargo/ops/fix.rs:77:1 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -cargo-0.49.0/src/cargo/ops/lockfile.rs:154:13 clippy::single_char_add_str "calling `push_str()` using a single-character string literal" -cargo-0.49.0/src/cargo/ops/lockfile.rs:217:9 clippy::single_char_add_str "calling `push_str()` using a single-character string literal" -cargo-0.49.0/src/cargo/ops/lockfile.rs:30:1 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -cargo-0.49.0/src/cargo/ops/lockfile.rs:35:1 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -cargo-0.49.0/src/cargo/ops/lockfile.rs:35:1 clippy::module_name_repetitions "item name ends with its containing module's name" -cargo-0.49.0/src/cargo/ops/lockfile.rs:87:1 clippy::unnecessary_wraps "this function's return value is unnecessarily wrapped by `Result`" -cargo-0.49.0/src/cargo/ops/lockfile.rs:8:1 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -cargo-0.49.0/src/cargo/ops/lockfile.rs:8:1 clippy::module_name_repetitions "item name ends with its containing module's name" -cargo-0.49.0/src/cargo/ops/registry.rs:150:21 clippy::redundant_closure_for_method_calls "redundant closure found" -cargo-0.49.0/src/cargo/ops/registry.rs:188:1 clippy::too_many_lines "this function has too many lines (130/100)" -cargo-0.49.0/src/cargo/ops/registry.rs:212:32 clippy::if_not_else "unnecessary `!=` operation" -cargo-0.49.0/src/cargo/ops/registry.rs:222:53 clippy::redundant_closure_for_method_calls "redundant closure found" -cargo-0.49.0/src/cargo/ops/registry.rs:224:44 clippy::redundant_closure_for_method_calls "redundant closure found" -cargo-0.49.0/src/cargo/ops/registry.rs:31:1 clippy::module_name_repetitions "item name starts with its containing module's name" -cargo-0.49.0/src/cargo/ops/registry.rs:346:1 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -cargo-0.49.0/src/cargo/ops/registry.rs:346:1 clippy::module_name_repetitions "item name starts with its containing module's name" -cargo-0.49.0/src/cargo/ops/registry.rs:351:26 clippy::single_match_else "you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let`" -cargo-0.49.0/src/cargo/ops/registry.rs:385:12 clippy::needless_pass_by_value "this argument is passed by value, but not consumed in the function body" -cargo-0.49.0/src/cargo/ops/registry.rs:386:15 clippy::needless_pass_by_value "this argument is passed by value, but not consumed in the function body" -cargo-0.49.0/src/cargo/ops/registry.rs:38:1 clippy::struct_excessive_bools "more than 3 bools in a struct" -cargo-0.49.0/src/cargo/ops/registry.rs:477:1 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -cargo-0.49.0/src/cargo/ops/registry.rs:483:1 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -cargo-0.49.0/src/cargo/ops/registry.rs:503:1 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -cargo-0.49.0/src/cargo/ops/registry.rs:505:38 clippy::default_trait_access "calling `util::config::CargoHttpConfig::default()` is more clear than this expression" -cargo-0.49.0/src/cargo/ops/registry.rs:510:1 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -cargo-0.49.0/src/cargo/ops/registry.rs:529:5 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope" -cargo-0.49.0/src/cargo/ops/registry.rs:53:1 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -cargo-0.49.0/src/cargo/ops/registry.rs:53:1 clippy::missing_panics_doc "docs for function which may panic missing `# Panics` section" -cargo-0.49.0/src/cargo/ops/registry.rs:573:22 clippy::match_same_arms "this `match` has identical arm bodies" -cargo-0.49.0/src/cargo/ops/registry.rs:608:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -cargo-0.49.0/src/cargo/ops/registry.rs:621:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -cargo-0.49.0/src/cargo/ops/registry.rs:671:1 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -cargo-0.49.0/src/cargo/ops/registry.rs:671:1 clippy::module_name_repetitions "item name starts with its containing module's name" -cargo-0.49.0/src/cargo/ops/registry.rs:674:10 clippy::needless_pass_by_value "this argument is passed by value, but not consumed in the function body" -cargo-0.49.0/src/cargo/ops/registry.rs:678:17 clippy::single_match_else "you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let`" -cargo-0.49.0/src/cargo/ops/registry.rs:730:1 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -cargo-0.49.0/src/cargo/ops/registry.rs:731:16 clippy::single_match_else "you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let`" -cargo-0.49.0/src/cargo/ops/registry.rs:785:1 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -cargo-0.49.0/src/cargo/ops/registry.rs:794:16 clippy::single_match_else "you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let`" -cargo-0.49.0/src/cargo/ops/registry.rs:828:14 clippy::doc_markdown "you should put `SourceId` between ticks in the documentation" -cargo-0.49.0/src/cargo/ops/registry.rs:848:1 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -cargo-0.49.0/src/cargo/ops/resolve.rs:199:1 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -cargo-0.49.0/src/cargo/ops/resolve.rs:199:1 clippy::module_name_repetitions "item name starts with its containing module's name" -cargo-0.49.0/src/cargo/ops/resolve.rs:199:1 clippy::too_many_lines "this function has too many lines (137/100)" -cargo-0.49.0/src/cargo/ops/resolve.rs:241:28 clippy::single_match_else "you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let`" -cargo-0.49.0/src/cargo/ops/resolve.rs:28:1 clippy::module_name_repetitions "item name ends with its containing module's name" -cargo-0.49.0/src/cargo/ops/resolve.rs:384:1 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -cargo-0.49.0/src/cargo/ops/resolve.rs:417:1 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -cargo-0.49.0/src/cargo/ops/resolve.rs:589:9 clippy::shadow_unrelated "`keep` is being shadowed" -cargo-0.49.0/src/cargo/ops/resolve.rs:58:1 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -cargo-0.49.0/src/cargo/ops/resolve.rs:58:1 clippy::module_name_repetitions "item name starts with its containing module's name" -cargo-0.49.0/src/cargo/ops/resolve.rs:602:5 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope" -cargo-0.49.0/src/cargo/ops/resolve.rs:75:1 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -cargo-0.49.0/src/cargo/ops/resolve.rs:75:1 clippy::module_name_repetitions "item name starts with its containing module's name" -cargo-0.49.0/src/cargo/ops/tree/graph.rs:129:26 clippy::doc_markdown "you should put `PackageIds` between ticks in the documentation" -cargo-0.49.0/src/cargo/ops/tree/graph.rs:152:15 clippy::match_on_vec_items "indexing into a vector may panic" -cargo-0.49.0/src/cargo/ops/tree/graph.rs:173:9 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope" -cargo-0.49.0/src/cargo/ops/tree/graph.rs:234:46 clippy::filter_map "called `filter(..).flat_map(..)` on an `Iterator`" -cargo-0.49.0/src/cargo/ops/tree/graph.rs:328:44 clippy::match_same_arms "this `match` has identical arm bodies" -cargo-0.49.0/src/cargo/ops/tree/graph.rs:330:50 clippy::match_same_arms "this `match` has identical arm bodies" -cargo-0.49.0/src/cargo/ops/tree/graph.rs:563:35 clippy::single_match_else "you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let`" -cargo-0.49.0/src/cargo/ops/tree/mod.rs:112:11 clippy::non_ascii_literal "literal non-ASCII character detected" -cargo-0.49.0/src/cargo/ops/tree/mod.rs:113:10 clippy::non_ascii_literal "literal non-ASCII character detected" -cargo-0.49.0/src/cargo/ops/tree/mod.rs:114:10 clippy::non_ascii_literal "literal non-ASCII character detected" -cargo-0.49.0/src/cargo/ops/tree/mod.rs:115:12 clippy::non_ascii_literal "literal non-ASCII character detected" -cargo-0.49.0/src/cargo/ops/tree/mod.rs:126:1 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -cargo-0.49.0/src/cargo/ops/tree/mod.rs:21:1 clippy::module_name_repetitions "item name starts with its containing module's name" -cargo-0.49.0/src/cargo/ops/tree/mod.rs:21:1 clippy::struct_excessive_bools "more than 3 bools in a struct" -cargo-0.49.0/src/cargo/ops/tree/mod.rs:360:30 clippy::match_same_arms "this `match` has identical arm bodies" -cargo-0.49.0/src/cargo/ops/tree/mod.rs:58:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/ops/vendor.rs:14:1 clippy::module_name_repetitions "item name starts with its containing module's name" -cargo-0.49.0/src/cargo/ops/vendor.rs:215:9 clippy::let_underscore_drop "non-binding `let` on a type that implements `Drop`" -cargo-0.49.0/src/cargo/ops/vendor.rs:21:1 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -cargo-0.49.0/src/cargo/ops/vendor.rs:21:1 clippy::missing_panics_doc "docs for function which may panic missing `# Panics` section" -cargo-0.49.0/src/cargo/ops/vendor.rs:314:34 clippy::match_same_arms "this `match` has identical arm bodies" -cargo-0.49.0/src/cargo/ops/vendor.rs:320:29 clippy::case_sensitive_file_extension_comparisons "case-sensitive file extension comparison" -cargo-0.49.0/src/cargo/ops/vendor.rs:320:60 clippy::case_sensitive_file_extension_comparisons "case-sensitive file extension comparison" -cargo-0.49.0/src/cargo/ops/vendor.rs:324:13 clippy::match_wildcard_for_single_variants "wildcard match will miss any future added variants" -cargo-0.49.0/src/cargo/ops/vendor.rs:70:1 clippy::too_many_lines "this function has too many lines (175/100)" -cargo-0.49.0/src/cargo/sources/config.rs:102:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -cargo-0.49.0/src/cargo/sources/config.rs:111:28 clippy::needless_question_mark "Question mark operator is useless here" -cargo-0.49.0/src/cargo/sources/config.rs:133:48 clippy::needless_question_mark "Question mark operator is useless here" -cargo-0.49.0/src/cargo/sources/config.rs:135:67 clippy::redundant_closure_for_method_calls "redundant closure found" -cargo-0.49.0/src/cargo/sources/config.rs:206:36 clippy::needless_pass_by_value "this argument is passed by value, but not consumed in the function body" -cargo-0.49.0/src/cargo/sources/config.rs:282:9 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope" -cargo-0.49.0/src/cargo/sources/config.rs:70:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -cargo-0.49.0/src/cargo/sources/config.rs:81:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -cargo-0.49.0/src/cargo/sources/config.rs:97:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/sources/directory.rs:14:1 clippy::module_name_repetitions "item name starts with its containing module's name" -cargo-0.49.0/src/cargo/sources/directory.rs:90:56 clippy::redundant_closure_for_method_calls "redundant closure found" -cargo-0.49.0/src/cargo/sources/git/source.rs:14:1 clippy::module_name_repetitions "item name ends with its containing module's name" -cargo-0.49.0/src/cargo/sources/git/source.rs:25:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -cargo-0.49.0/src/cargo/sources/git/source.rs:49:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/sources/git/source.rs:53:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -cargo-0.49.0/src/cargo/sources/git/source.rs:53:5 clippy::missing_panics_doc "docs for function which may panic missing `# Panics` section" -cargo-0.49.0/src/cargo/sources/git/source.rs:69:20 clippy::comparison_to_empty "comparison to empty slice" -cargo-0.49.0/src/cargo/sources/git/utils.rs:1025:19 clippy::single_match_else "you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let`" -cargo-0.49.0/src/cargo/sources/git/utils.rs:1157:36 clippy::case_sensitive_file_extension_comparisons "case-sensitive file extension comparison" -cargo-0.49.0/src/cargo/sources/git/utils.rs:1158:9 clippy::manual_strip "stripping a suffix manually" -cargo-0.49.0/src/cargo/sources/git/utils.rs:134:12 clippy::upper_case_acronyms "name `GitShortID` contains a capitalized acronym" -cargo-0.49.0/src/cargo/sources/git/utils.rs:176:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/sources/git/utils.rs:180:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/sources/git/utils.rs:184:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -cargo-0.49.0/src/cargo/sources/git/utils.rs:188:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -cargo-0.49.0/src/cargo/sources/git/utils.rs:242:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -cargo-0.49.0/src/cargo/sources/git/utils.rs:253:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -cargo-0.49.0/src/cargo/sources/git/utils.rs:262:13 clippy::if_not_else "unnecessary boolean `not` operation" -cargo-0.49.0/src/cargo/sources/git/utils.rs:289:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -cargo-0.49.0/src/cargo/sources/git/utils.rs:294:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/sources/git/utils.rs:298:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -cargo-0.49.0/src/cargo/sources/git/utils.rs:308:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -cargo-0.49.0/src/cargo/sources/git/utils.rs:472:9 clippy::let_underscore_drop "non-binding `let` on a type that implements `Drop`" -cargo-0.49.0/src/cargo/sources/git/utils.rs:489:9 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope" -cargo-0.49.0/src/cargo/sources/git/utils.rs:503:9 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope" -cargo-0.49.0/src/cargo/sources/git/utils.rs:528:28 clippy::single_match_else "you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let`" -cargo-0.49.0/src/cargo/sources/git/utils.rs:537:21 clippy::let_underscore_drop "non-binding `let` on a type that implements `Drop`" -cargo-0.49.0/src/cargo/sources/git/utils.rs:588:1 clippy::too_many_lines "this function has too many lines (135/100)" -cargo-0.49.0/src/cargo/sources/git/utils.rs:692:9 clippy::vec_init_then_push "calls to `push` immediately after creation" -cargo-0.49.0/src/cargo/sources/git/utils.rs:758:9 clippy::single_char_add_str "calling `push_str()` using a single-character string literal" -cargo-0.49.0/src/cargo/sources/git/utils.rs:858:1 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -cargo-0.49.0/src/cargo/sources/path.rs:129:44 clippy::match_same_arms "this `match` has identical arm bodies" -cargo-0.49.0/src/cargo/sources/path.rs:143:44 clippy::match_same_arms "this `match` has identical arm bodies" -cargo-0.49.0/src/cargo/sources/path.rs:15:1 clippy::module_name_repetitions "item name starts with its containing module's name" -cargo-0.49.0/src/cargo/sources/path.rs:282:50 clippy::redundant_closure_for_method_calls "redundant closure found" -cargo-0.49.0/src/cargo/sources/path.rs:313:21 clippy::similar_names "binding's name is too similar to existing binding" -cargo-0.49.0/src/cargo/sources/path.rs:314:21 clippy::similar_names "binding's name is too similar to existing binding" -cargo-0.49.0/src/cargo/sources/path.rs:319:21 clippy::similar_names "binding's name is too similar to existing binding" -cargo-0.49.0/src/cargo/sources/path.rs:339:9 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope" -cargo-0.49.0/src/cargo/sources/path.rs:339:9 clippy::unnecessary_wraps "this function's return value is unnecessarily wrapped by `Result`" -cargo-0.49.0/src/cargo/sources/path.rs:380:9 clippy::unused_self "unused `self` argument" -cargo-0.49.0/src/cargo/sources/path.rs:419:50 clippy::redundant_closure_for_method_calls "redundant closure found" -cargo-0.49.0/src/cargo/sources/path.rs:429:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -cargo-0.49.0/src/cargo/sources/path.rs:460:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/sources/path.rs:473:43 clippy::redundant_closure_for_method_calls "redundant closure found" -cargo-0.49.0/src/cargo/sources/path.rs:482:43 clippy::redundant_closure_for_method_calls "redundant closure found" -cargo-0.49.0/src/cargo/sources/path.rs:63:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -cargo-0.49.0/src/cargo/sources/path.rs:77:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -cargo-0.49.0/src/cargo/sources/path.rs:98:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -cargo-0.49.0/src/cargo/sources/registry/index.rs:117:23 clippy::unseparated_literal_suffix "integer type suffix should be separated by an underscore" -cargo-0.49.0/src/cargo/sources/registry/index.rs:121:70 clippy::unseparated_literal_suffix "integer type suffix should be separated by an underscore" -cargo-0.49.0/src/cargo/sources/registry/index.rs:167:1 clippy::module_name_repetitions "item name ends with its containing module's name" -cargo-0.49.0/src/cargo/sources/registry/index.rs:215:1 clippy::module_name_repetitions "item name starts with its containing module's name" -cargo-0.49.0/src/cargo/sources/registry/index.rs:324:23 clippy::redundant_closure_for_method_calls "redundant closure found" -cargo-0.49.0/src/cargo/sources/registry/index.rs:468:40 clippy::doc_markdown "you should put `SourceId` between ticks in the documentation" -cargo-0.49.0/src/cargo/sources/registry/index.rs:590:9 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope" -cargo-0.49.0/src/cargo/sources/registry/index.rs:648:17 clippy::similar_names "binding's name is too similar to existing binding" -cargo-0.49.0/src/cargo/sources/registry/index.rs:736:1 clippy::needless_lifetimes "explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration)" -cargo-0.49.0/src/cargo/sources/registry/index.rs:95:37 clippy::cast_possible_truncation "casting `usize` to `u32` may truncate the value on targets with 64-bit wide pointers" -cargo-0.49.0/src/cargo/sources/registry/local.rs:12:1 clippy::module_name_repetitions "item name starts with its containing module's name" -cargo-0.49.0/src/cargo/sources/registry/mod.rs:192:1 clippy::module_name_repetitions "item name starts with its containing module's name" -cargo-0.49.0/src/cargo/sources/registry/mod.rs:203:1 clippy::module_name_repetitions "item name starts with its containing module's name" -cargo-0.49.0/src/cargo/sources/registry/mod.rs:229:1 clippy::module_name_repetitions "item name starts with its containing module's name" -cargo-0.49.0/src/cargo/sources/registry/mod.rs:372:1 clippy::module_name_repetitions "item name starts with its containing module's name" -cargo-0.49.0/src/cargo/sources/registry/mod.rs:373:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -cargo-0.49.0/src/cargo/sources/registry/mod.rs:375:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -cargo-0.49.0/src/cargo/sources/registry/mod.rs:381:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -cargo-0.49.0/src/cargo/sources/registry/mod.rs:382:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -cargo-0.49.0/src/cargo/sources/registry/mod.rs:383:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -cargo-0.49.0/src/cargo/sources/registry/mod.rs:384:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -cargo-0.49.0/src/cargo/sources/registry/mod.rs:452:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -cargo-0.49.0/src/cargo/sources/registry/mod.rs:582:20 clippy::redundant_else "redundant else block" -cargo-0.49.0/src/cargo/sources/registry/mod.rs:621:9 clippy::if_not_else "unnecessary `!=` operation" -cargo-0.49.0/src/cargo/sources/registry/remote.rs:139:17 clippy::unused_self "unused `self` argument" -cargo-0.49.0/src/cargo/sources/registry/remote.rs:32:1 clippy::module_name_repetitions "item name starts with its containing module's name" -cargo-0.49.0/src/cargo/sources/registry/remote.rs:72:13 clippy::single_match_else "you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let`" -cargo-0.49.0/src/cargo/sources/replaced.rs:12:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/sources/replaced.rs:5:1 clippy::module_name_repetitions "item name starts with its containing module's name" -cargo-0.49.0/src/cargo/util/canonical_url.rs:19:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -cargo-0.49.0/src/cargo/util/canonical_url.rs:19:5 clippy::missing_panics_doc "docs for function which may panic missing `# Panics` section" -cargo-0.49.0/src/cargo/util/canonical_url.rs:50:41 clippy::case_sensitive_file_extension_comparisons "case-sensitive file extension comparison" -cargo-0.49.0/src/cargo/util/canonical_url.rs:65:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/util/command_prelude.rs:218:1 clippy::must_use_candidate "this function could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/util/command_prelude.rs:222:1 clippy::must_use_candidate "this function could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/util/command_prelude.rs:234:1 clippy::must_use_candidate "this function could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/util/command_prelude.rs:249:1 clippy::must_use_candidate "this function could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/util/command_prelude.rs:264:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -cargo-0.49.0/src/cargo/util/command_prelude.rs:279:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -cargo-0.49.0/src/cargo/util/command_prelude.rs:298:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -cargo-0.49.0/src/cargo/util/command_prelude.rs:320:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -cargo-0.49.0/src/cargo/util/command_prelude.rs:328:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -cargo-0.49.0/src/cargo/util/command_prelude.rs:352:13 clippy::if_not_else "unnecessary boolean `not` operation" -cargo-0.49.0/src/cargo/util/command_prelude.rs:363:13 clippy::if_not_else "unnecessary boolean `not` operation" -cargo-0.49.0/src/cargo/util/command_prelude.rs:378:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -cargo-0.49.0/src/cargo/util/command_prelude.rs:387:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -cargo-0.49.0/src/cargo/util/command_prelude.rs:387:5 clippy::too_many_lines "this function has too many lines (104/100)" -cargo-0.49.0/src/cargo/util/command_prelude.rs:39:20 clippy::doc_markdown "you should put `arg_package_spec` between ticks in the documentation" -cargo-0.49.0/src/cargo/util/command_prelude.rs:504:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -cargo-0.49.0/src/cargo/util/command_prelude.rs:516:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -cargo-0.49.0/src/cargo/util/command_prelude.rs:530:40 clippy::redundant_closure_for_method_calls "redundant closure found" -cargo-0.49.0/src/cargo/util/command_prelude.rs:531:43 clippy::redundant_closure_for_method_calls "redundant closure found" -cargo-0.49.0/src/cargo/util/command_prelude.rs:536:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -cargo-0.49.0/src/cargo/util/command_prelude.rs:556:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -cargo-0.49.0/src/cargo/util/command_prelude.rs:575:49 clippy::redundant_closure_for_method_calls "redundant closure found" -cargo-0.49.0/src/cargo/util/command_prelude.rs:580:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -cargo-0.49.0/src/cargo/util/command_prelude.rs:631:18 clippy::redundant_closure_for_method_calls "redundant closure found" -cargo-0.49.0/src/cargo/util/command_prelude.rs:638:18 clippy::redundant_closure_for_method_calls "redundant closure found" -cargo-0.49.0/src/cargo/util/command_prelude.rs:647:1 clippy::must_use_candidate "this function could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/util/command_prelude.rs:651:1 clippy::must_use_candidate "this function could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/util/command_prelude.rs:662:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/util/command_prelude.rs:665:51 clippy::match_same_arms "this `match` has identical arm bodies" -cargo-0.49.0/src/cargo/util/config/de.rs:420:16 clippy::needless_pass_by_value "this argument is passed by value, but not consumed in the function body" -cargo-0.49.0/src/cargo/util/config/de.rs:46:25 clippy::doc_markdown "you should put `CV::List` between ticks in the documentation" -cargo-0.49.0/src/cargo/util/config/de.rs:47:24 clippy::doc_markdown "you should put `ConfigSeqAccess` between ticks in the documentation" -cargo-0.49.0/src/cargo/util/config/de.rs:527:53 clippy::unseparated_literal_suffix "integer type suffix should be separated by an underscore" -cargo-0.49.0/src/cargo/util/config/de.rs:530:53 clippy::unseparated_literal_suffix "integer type suffix should be separated by an underscore" -cargo-0.49.0/src/cargo/util/config/de.rs:532:68 clippy::unseparated_literal_suffix "integer type suffix should be separated by an underscore" -cargo-0.49.0/src/cargo/util/config/key.rs:11:1 clippy::module_name_repetitions "item name ends with its containing module's name" -cargo-0.49.0/src/cargo/util/config/key.rs:69:9 clippy::single_char_add_str "calling `push_str()` using a single-character string literal" -cargo-0.49.0/src/cargo/util/config/mod.rs:100:71 clippy::doc_markdown "you should put `OptValue` between ticks in the documentation" -cargo-0.49.0/src/cargo/util/config/mod.rs:100:71 clippy::doc_markdown "you should put `OptValue` between ticks in the documentation" -cargo-0.49.0/src/cargo/util/config/mod.rs:100:71 clippy::doc_markdown "you should put `OptValue` between ticks in the documentation" -cargo-0.49.0/src/cargo/util/config/mod.rs:1049:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -cargo-0.49.0/src/cargo/util/config/mod.rs:1064:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -cargo-0.49.0/src/cargo/util/config/mod.rs:1090:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -cargo-0.49.0/src/cargo/util/config/mod.rs:1166:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -cargo-0.49.0/src/cargo/util/config/mod.rs:1179:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -cargo-0.49.0/src/cargo/util/config/mod.rs:1181:33 clippy::needless_question_mark "Question mark operator is useless here" -cargo-0.49.0/src/cargo/util/config/mod.rs:1184:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -cargo-0.49.0/src/cargo/util/config/mod.rs:1186:33 clippy::needless_question_mark "Question mark operator is useless here" -cargo-0.49.0/src/cargo/util/config/mod.rs:1189:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -cargo-0.49.0/src/cargo/util/config/mod.rs:1191:33 clippy::needless_question_mark "Question mark operator is useless here" -cargo-0.49.0/src/cargo/util/config/mod.rs:1203:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -cargo-0.49.0/src/cargo/util/config/mod.rs:1211:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -cargo-0.49.0/src/cargo/util/config/mod.rs:1216:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -cargo-0.49.0/src/cargo/util/config/mod.rs:1225:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -cargo-0.49.0/src/cargo/util/config/mod.rs:1229:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -cargo-0.49.0/src/cargo/util/config/mod.rs:124:1 clippy::struct_excessive_bools "more than 3 bools in a struct" -cargo-0.49.0/src/cargo/util/config/mod.rs:1254:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -cargo-0.49.0/src/cargo/util/config/mod.rs:1279:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -cargo-0.49.0/src/cargo/util/config/mod.rs:1281:9 clippy::single_match_else "you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let`" -cargo-0.49.0/src/cargo/util/config/mod.rs:1323:9 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope" -cargo-0.49.0/src/cargo/util/config/mod.rs:1339:39 clippy::unused_self "unused `self` argument" -cargo-0.49.0/src/cargo/util/config/mod.rs:1344:1 clippy::module_name_repetitions "item name starts with its containing module's name" -cargo-0.49.0/src/cargo/util/config/mod.rs:1420:1 clippy::module_name_repetitions "item name starts with its containing module's name" -cargo-0.49.0/src/cargo/util/config/mod.rs:1553:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -cargo-0.49.0/src/cargo/util/config/mod.rs:1560:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -cargo-0.49.0/src/cargo/util/config/mod.rs:1567:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -cargo-0.49.0/src/cargo/util/config/mod.rs:1574:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -cargo-0.49.0/src/cargo/util/config/mod.rs:1581:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -cargo-0.49.0/src/cargo/util/config/mod.rs:1588:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/util/config/mod.rs:1598:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/util/config/mod.rs:1619:1 clippy::must_use_candidate "this function could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/util/config/mod.rs:1623:1 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -cargo-0.49.0/src/cargo/util/config/mod.rs:1623:1 clippy::missing_panics_doc "docs for function which may panic missing `# Panics` section" -cargo-0.49.0/src/cargo/util/config/mod.rs:1623:64 clippy::needless_pass_by_value "this argument is passed by value, but not consumed in the function body" -cargo-0.49.0/src/cargo/util/config/mod.rs:1649:9 clippy::option_if_let_else "use Option::map_or_else instead of an if let/else" -cargo-0.49.0/src/cargo/util/config/mod.rs:1699:5 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope" -cargo-0.49.0/src/cargo/util/config/mod.rs:1730:1 clippy::module_name_repetitions "item name ends with its containing module's name" -cargo-0.49.0/src/cargo/util/config/mod.rs:1757:1 clippy::module_name_repetitions "item name ends with its containing module's name" -cargo-0.49.0/src/cargo/util/config/mod.rs:1770:1 clippy::module_name_repetitions "item name ends with its containing module's name" -cargo-0.49.0/src/cargo/util/config/mod.rs:1778:1 clippy::module_name_repetitions "item name ends with its containing module's name" -cargo-0.49.0/src/cargo/util/config/mod.rs:1804:1 clippy::module_name_repetitions "item name ends with its containing module's name" -cargo-0.49.0/src/cargo/util/config/mod.rs:1896:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/util/config/mod.rs:1901:5 clippy::doc_markdown "you should put `StringList` between ticks in the documentation" -cargo-0.49.0/src/cargo/util/config/mod.rs:214:13 clippy::match_wildcard_for_single_variants "wildcard match will miss any future added variants" -cargo-0.49.0/src/cargo/util/config/mod.rs:259:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -cargo-0.49.0/src/cargo/util/config/mod.rs:298:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -cargo-0.49.0/src/cargo/util/config/mod.rs:311:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -cargo-0.49.0/src/cargo/util/config/mod.rs:318:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -cargo-0.49.0/src/cargo/util/config/mod.rs:353:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -cargo-0.49.0/src/cargo/util/config/mod.rs:401:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -cargo-0.49.0/src/cargo/util/config/mod.rs:411:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -cargo-0.49.0/src/cargo/util/config/mod.rs:419:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -cargo-0.49.0/src/cargo/util/config/mod.rs:431:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -cargo-0.49.0/src/cargo/util/config/mod.rs:449:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -cargo-0.49.0/src/cargo/util/config/mod.rs:454:16 clippy::option_if_let_else "use Option::map_or instead of an if let/else" -cargo-0.49.0/src/cargo/util/config/mod.rs:547:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -cargo-0.49.0/src/cargo/util/config/mod.rs:556:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -cargo-0.49.0/src/cargo/util/config/mod.rs:582:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -cargo-0.49.0/src/cargo/util/config/mod.rs:595:20 clippy::doc_markdown "you should put `StringList` between ticks in the documentation" -cargo-0.49.0/src/cargo/util/config/mod.rs:689:20 clippy::unused_self "unused `self` argument" -cargo-0.49.0/src/cargo/util/config/mod.rs:699:5 clippy::fn_params_excessive_bools "more than 3 bools in function parameters" -cargo-0.49.0/src/cargo/util/config/mod.rs:699:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -cargo-0.49.0/src/cargo/util/config/mod.rs:719:58 clippy::redundant_closure_for_method_calls "redundant closure found" -cargo-0.49.0/src/cargo/util/config/mod.rs:816:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -cargo-0.49.0/src/cargo/util/config/path.rs:10:1 clippy::module_name_repetitions "item name ends with its containing module's name" -cargo-0.49.0/src/cargo/util/config/path.rs:14:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/util/config/path.rs:48:1 clippy::module_name_repetitions "item name starts with its containing module's name" -cargo-0.49.0/src/cargo/util/config/target.rs:12:1 clippy::module_name_repetitions "item name starts with its containing module's name" -cargo-0.49.0/src/cargo/util/config/target.rs:24:1 clippy::module_name_repetitions "item name starts with its containing module's name" -cargo-0.49.0/src/cargo/util/config/value.rs:29:1 clippy::module_name_repetitions "item name ends with its containing module's name" -cargo-0.49.0/src/cargo/util/config/value.rs:70:5 clippy::missing_panics_doc "docs for function which may panic missing `# Panics` section" -cargo-0.49.0/src/cargo/util/config/value.rs:80:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/util/config/value.rs:81:9 clippy::match_like_matches_macro "match expression looks like `matches!` macro" -cargo-0.49.0/src/cargo/util/cpu.rs:11:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -cargo-0.49.0/src/cargo/util/cpu.rs:22:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/util/cpu.rs:82:25 clippy::cast_precision_loss "casting `u64` to `f64` causes a loss of precision (`u64` is 64 bits wide, but `f64`'s mantissa is only 52 bits wide)" -cargo-0.49.0/src/cargo/util/cpu.rs:82:9 clippy::cast_precision_loss "casting `u64` to `f64` causes a loss of precision (`u64` is 64 bits wide, but `f64`'s mantissa is only 52 bits wide)" -cargo-0.49.0/src/cargo/util/dependency_queue.rs:109:27 clippy::redundant_closure_for_method_calls "redundant closure found" -cargo-0.49.0/src/cargo/util/dependency_queue.rs:125:5 clippy::missing_panics_doc "docs for function which may panic missing `# Panics` section" -cargo-0.49.0/src/cargo/util/dependency_queue.rs:151:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/util/dependency_queue.rs:156:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/util/dependency_queue.rs:168:5 clippy::missing_panics_doc "docs for function which may panic missing `# Panics` section" -cargo-0.49.0/src/cargo/util/dependency_queue.rs:46:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/util/dependency_queue.rs:91:9 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope" -cargo-0.49.0/src/cargo/util/diagnostic_server.rs:218:1 clippy::module_name_repetitions "item name ends with its containing module's name" -cargo-0.49.0/src/cargo/util/diagnostic_server.rs:230:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -cargo-0.49.0/src/cargo/util/diagnostic_server.rs:242:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -cargo-0.49.0/src/cargo/util/diagnostic_server.rs:58:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -cargo-0.49.0/src/cargo/util/diagnostic_server.rs:96:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -cargo-0.49.0/src/cargo/util/diagnostic_server.rs:96:5 clippy::too_many_lines "this function has too many lines (110/100)" -cargo-0.49.0/src/cargo/util/diagnostic_server.rs:99:21 clippy::shadow_unrelated "`msg` is being shadowed" -cargo-0.49.0/src/cargo/util/errors.rs:101:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/util/errors.rs:143:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/util/errors.rs:150:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/util/errors.rs:15:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -cargo-0.49.0/src/cargo/util/errors.rs:237:5 clippy::pub_enum_variant_names "variant name ends with the enum's name" -cargo-0.49.0/src/cargo/util/errors.rs:245:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/util/errors.rs:321:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/util/errors.rs:328:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/util/errors.rs:356:1 clippy::must_use_candidate "this function could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/util/errors.rs:391:5 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope" -cargo-0.49.0/src/cargo/util/errors.rs:392:13 clippy::wildcard_imports "usage of wildcard import" -cargo-0.49.0/src/cargo/util/errors.rs:465:1 clippy::must_use_candidate "this function could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/util/errors.rs:473:5 clippy::manual_range_contains "manual `RangeInclusive::contains` implementation" -cargo-0.49.0/src/cargo/util/errors.rs:66:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/util/flock.rs:115:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/util/flock.rs:11:5 clippy::wildcard_imports "usage of wildcard import" -cargo-0.49.0/src/cargo/util/flock.rs:134:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/util/flock.rs:142:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/util/flock.rs:150:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -cargo-0.49.0/src/cargo/util/flock.rs:156:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/util/flock.rs:170:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -cargo-0.49.0/src/cargo/util/flock.rs:192:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -cargo-0.49.0/src/cargo/util/flock.rs:29:5 clippy::missing_panics_doc "docs for function which may panic missing `# Panics` section" -cargo-0.49.0/src/cargo/util/flock.rs:29:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/util/flock.rs:321:5 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope" -cargo-0.49.0/src/cargo/util/flock.rs:335:23 clippy::cast_possible_truncation "casting `i64` to `u32` may truncate the value" -cargo-0.49.0/src/cargo/util/flock.rs:335:23 clippy::cast_sign_loss "casting `i64` to `u32` may lose the sign of the value" -cargo-0.49.0/src/cargo/util/flock.rs:335:44 clippy::cast_possible_truncation "casting `i64` to `u32` may truncate the value" -cargo-0.49.0/src/cargo/util/flock.rs:379:35 clippy::match_same_arms "this `match` has identical arm bodies" -cargo-0.49.0/src/cargo/util/flock.rs:37:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/util/flock.rs:43:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/util/flock.rs:52:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -cargo-0.49.0/src/cargo/util/flock.rs:52:5 clippy::missing_panics_doc "docs for function which may panic missing `# Panics` section" -cargo-0.49.0/src/cargo/util/flock.rs:96:17 clippy::let_underscore_drop "non-binding `let` on a type that implements `Drop`" -cargo-0.49.0/src/cargo/util/graph.rs:10:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/util/graph.rs:41:51 clippy::redundant_closure_for_method_calls "redundant closure found" -cargo-0.49.0/src/cargo/util/graph.rs:45:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/util/hasher.rs:12:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/util/hasher.rs:9:1 clippy::module_name_repetitions "item name ends with its containing module's name" -cargo-0.49.0/src/cargo/util/hex.rs:10:9 clippy::cast_possible_truncation "casting `u64` to `u8` may truncate the value" -cargo-0.49.0/src/cargo/util/hex.rs:11:9 clippy::cast_possible_truncation "casting `u64` to `u8` may truncate the value" -cargo-0.49.0/src/cargo/util/hex.rs:12:9 clippy::cast_possible_truncation "casting `u64` to `u8` may truncate the value" -cargo-0.49.0/src/cargo/util/hex.rs:13:9 clippy::cast_possible_truncation "casting `u64` to `u8` may truncate the value" -cargo-0.49.0/src/cargo/util/hex.rs:14:9 clippy::cast_possible_truncation "casting `u64` to `u8` may truncate the value" -cargo-0.49.0/src/cargo/util/hex.rs:15:9 clippy::cast_possible_truncation "casting `u64` to `u8` may truncate the value" -cargo-0.49.0/src/cargo/util/hex.rs:25:1 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -cargo-0.49.0/src/cargo/util/hex.rs:6:1 clippy::module_name_repetitions "item name ends with its containing module's name" -cargo-0.49.0/src/cargo/util/hex.rs:6:1 clippy::must_use_candidate "this function could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/util/hex.rs:8:9 clippy::cast_possible_truncation "casting `u64` to `u8` may truncate the value" -cargo-0.49.0/src/cargo/util/hex.rs:9:9 clippy::cast_possible_truncation "casting `u64` to `u8` may truncate the value" -cargo-0.49.0/src/cargo/util/important_paths.rs:23:1 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -cargo-0.49.0/src/cargo/util/important_paths.rs:6:1 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -cargo-0.49.0/src/cargo/util/interning.rs:66:5 clippy::missing_panics_doc "docs for function which may panic missing `# Panics` section" -cargo-0.49.0/src/cargo/util/interning.rs:66:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/util/interning.rs:77:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/util/into_url.rs:10:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -cargo-0.49.0/src/cargo/util/into_url_with_base.rs:9:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -cargo-0.49.0/src/cargo/util/job.rs:20:1 clippy::must_use_candidate "this function could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/util/lev_distance.rs:3:1 clippy::must_use_candidate "this function could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/util/lockserver.rs:111:32 clippy::redundant_else "redundant else block" -cargo-0.49.0/src/cargo/util/lockserver.rs:158:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -cargo-0.49.0/src/cargo/util/lockserver.rs:46:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -cargo-0.49.0/src/cargo/util/lockserver.rs:58:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/util/lockserver.rs:62:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -cargo-0.49.0/src/cargo/util/mod.rs:68:1 clippy::must_use_candidate "this function could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/util/mod.rs:79:1 clippy::must_use_candidate "this function could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/util/network.rs:12:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -cargo-0.49.0/src/cargo/util/network.rs:19:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -cargo-0.49.0/src/cargo/util/network.rs:84:1 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -cargo-0.49.0/src/cargo/util/paths.rs:109:12 clippy::redundant_else "redundant else block" -cargo-0.49.0/src/cargo/util/paths.rs:114:1 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -cargo-0.49.0/src/cargo/util/paths.rs:121:1 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -cargo-0.49.0/src/cargo/util/paths.rs:125:1 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -cargo-0.49.0/src/cargo/util/paths.rs:130:1 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -cargo-0.49.0/src/cargo/util/paths.rs:14:1 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -cargo-0.49.0/src/cargo/util/paths.rs:14:1 clippy::module_name_repetitions "item name ends with its containing module's name" -cargo-0.49.0/src/cargo/util/paths.rs:151:1 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -cargo-0.49.0/src/cargo/util/paths.rs:167:1 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -cargo-0.49.0/src/cargo/util/paths.rs:173:1 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -cargo-0.49.0/src/cargo/util/paths.rs:178:1 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -cargo-0.49.0/src/cargo/util/paths.rs:185:1 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -cargo-0.49.0/src/cargo/util/paths.rs:199:1 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -cargo-0.49.0/src/cargo/util/paths.rs:215:1 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -cargo-0.49.0/src/cargo/util/paths.rs:228:1 clippy::must_use_candidate "this function could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/util/paths.rs:251:9 clippy::option_if_let_else "use Option::map_or instead of an if let/else" -cargo-0.49.0/src/cargo/util/paths.rs:267:1 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -cargo-0.49.0/src/cargo/util/paths.rs:276:1 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -cargo-0.49.0/src/cargo/util/paths.rs:29:1 clippy::must_use_candidate "this function could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/util/paths.rs:303:1 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -cargo-0.49.0/src/cargo/util/paths.rs:312:1 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -cargo-0.49.0/src/cargo/util/paths.rs:346:1 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -cargo-0.49.0/src/cargo/util/paths.rs:415:1 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -cargo-0.49.0/src/cargo/util/paths.rs:445:1 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -cargo-0.49.0/src/cargo/util/paths.rs:459:45 clippy::redundant_closure_for_method_calls "redundant closure found" -cargo-0.49.0/src/cargo/util/paths.rs:469:1 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -cargo-0.49.0/src/cargo/util/paths.rs:469:1 clippy::missing_panics_doc "docs for function which may panic missing `# Panics` section" -cargo-0.49.0/src/cargo/util/paths.rs:514:5 clippy::let_underscore_drop "non-binding `let` on a type that implements `Drop`" -cargo-0.49.0/src/cargo/util/paths.rs:54:1 clippy::must_use_candidate "this function could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/util/paths.rs:61:1 clippy::must_use_candidate "this function could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/util/paths.rs:63:19 clippy::option_if_let_else "use Option::map_or_else instead of an if let/else" -cargo-0.49.0/src/cargo/util/paths.rs:88:1 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -cargo-0.49.0/src/cargo/util/paths.rs:93:31 clippy::comparison_to_empty "comparison to empty slice" -cargo-0.49.0/src/cargo/util/process_builder.rs:106:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/util/process_builder.rs:111:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/util/process_builder.rs:122:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/util/process_builder.rs:132:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/util/process_builder.rs:152:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -cargo-0.49.0/src/cargo/util/process_builder.rs:185:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -cargo-0.49.0/src/cargo/util/process_builder.rs:190:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -cargo-0.49.0/src/cargo/util/process_builder.rs:218:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -cargo-0.49.0/src/cargo/util/process_builder.rs:218:5 clippy::missing_panics_doc "docs for function which may panic missing `# Panics` section" -cargo-0.49.0/src/cargo/util/process_builder.rs:278:22 clippy::inconsistent_struct_constructor "inconsistent struct constructor" -cargo-0.49.0/src/cargo/util/process_builder.rs:307:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/util/process_builder.rs:343:39 clippy::needless_pass_by_value "this argument is passed by value, but not consumed in the function body" -cargo-0.49.0/src/cargo/util/progress.rs:122:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -cargo-0.49.0/src/cargo/util/progress.rs:136:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -cargo-0.49.0/src/cargo/util/progress.rs:15:1 clippy::module_name_repetitions "item name starts with its containing module's name" -cargo-0.49.0/src/cargo/util/progress.rs:249:19 clippy::cast_precision_loss "casting `usize` to `f64` causes a loss of precision on targets with 64-bit wide pointers (`usize` is 64 bits wide, but `f64`'s mantissa is only 52 bits wide)" -cargo-0.49.0/src/cargo/util/progress.rs:249:34 clippy::cast_precision_loss "casting `usize` to `f64` causes a loss of precision on targets with 64-bit wide pointers (`usize` is 64 bits wide, but `f64`'s mantissa is only 52 bits wide)" -cargo-0.49.0/src/cargo/util/progress.rs:250:19 clippy::if_not_else "unnecessary boolean `not` operation" -cargo-0.49.0/src/cargo/util/progress.rs:263:22 clippy::cast_precision_loss "casting `usize` to `f64` causes a loss of precision on targets with 64-bit wide pointers (`usize` is 64 bits wide, but `f64`'s mantissa is only 52 bits wide)" -cargo-0.49.0/src/cargo/util/progress.rs:264:22 clippy::cast_possible_truncation "casting `f64` to `usize` may truncate the value" -cargo-0.49.0/src/cargo/util/progress.rs:264:22 clippy::cast_sign_loss "casting `f64` to `usize` may lose the sign of the value" -cargo-0.49.0/src/cargo/util/progress.rs:269:17 clippy::single_char_add_str "calling `push_str()` using a single-character string literal" -cargo-0.49.0/src/cargo/util/progress.rs:272:17 clippy::single_char_add_str "calling `push_str()` using a single-character string literal" -cargo-0.49.0/src/cargo/util/progress.rs:274:17 clippy::single_char_add_str "calling `push_str()` using a single-character string literal" -cargo-0.49.0/src/cargo/util/progress.rs:280:13 clippy::single_char_add_str "calling `push_str()` using a single-character string literal" -cargo-0.49.0/src/cargo/util/progress.rs:282:9 clippy::single_char_add_str "calling `push_str()` using a single-character string literal" -cargo-0.49.0/src/cargo/util/progress.rs:89:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/util/progress.rs:97:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -cargo-0.49.0/src/cargo/util/queue.rs:25:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/util/queue.rs:36:5 clippy::missing_panics_doc "docs for function which may panic missing `# Panics` section" -cargo-0.49.0/src/cargo/util/queue.rs:42:5 clippy::missing_panics_doc "docs for function which may panic missing `# Panics` section" -cargo-0.49.0/src/cargo/util/queue.rs:52:5 clippy::missing_panics_doc "docs for function which may panic missing `# Panics` section" -cargo-0.49.0/src/cargo/util/queue.rs:69:5 clippy::missing_panics_doc "docs for function which may panic missing `# Panics` section" -cargo-0.49.0/src/cargo/util/read2.rs:11:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -cargo-0.49.0/src/cargo/util/read2.rs:31:17 clippy::similar_names "binding's name is too similar to existing binding" -cargo-0.49.0/src/cargo/util/restricted_names.rs:13:1 clippy::must_use_candidate "this function could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/util/restricted_names.rs:26:1 clippy::must_use_candidate "this function could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/util/restricted_names.rs:35:1 clippy::must_use_candidate "this function could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/util/restricted_names.rs:45:1 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -cargo-0.49.0/src/cargo/util/restricted_names.rs:87:1 clippy::missing_panics_doc "docs for function which may panic missing `# Panics` section" -cargo-0.49.0/src/cargo/util/restricted_names.rs:87:1 clippy::must_use_candidate "this function could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/util/restricted_names.rs:89:21 clippy::redundant_closure_for_method_calls "redundant closure found" -cargo-0.49.0/src/cargo/util/restricted_names.rs:8:1 clippy::must_use_candidate "this function could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/util/rustc.rs:103:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -cargo-0.49.0/src/cargo/util/rustc.rs:103:5 clippy::missing_panics_doc "docs for function which may panic missing `# Panics` section" -cargo-0.49.0/src/cargo/util/rustc.rs:114:5 clippy::doc_markdown "you should put bare URLs between `<`/`>` or make a proper Markdown link" -cargo-0.49.0/src/cargo/util/rustc.rs:115:5 clippy::doc_markdown "you should put bare URLs between `<`/`>` or make a proper Markdown link" -cargo-0.49.0/src/cargo/util/rustc.rs:162:17 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope" -cargo-0.49.0/src/cargo/util/rustc.rs:39:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -cargo-0.49.0/src/cargo/util/sha256.rs:10:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/util/sha256.rs:16:9 clippy::let_underscore_drop "non-binding `let` on a type that implements `Drop`" -cargo-0.49.0/src/cargo/util/sha256.rs:20:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -cargo-0.49.0/src/cargo/util/sha256.rs:31:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -cargo-0.49.0/src/cargo/util/sha256.rs:40:24 clippy::unseparated_literal_suffix "integer type suffix should be separated by an underscore" -cargo-0.49.0/src/cargo/util/to_semver.rs:5:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -cargo-0.49.0/src/cargo/util/toml/mod.rs:1005:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -cargo-0.49.0/src/cargo/util/toml/mod.rs:1005:5 clippy::too_many_lines "this function has too many lines (282/100)" -cargo-0.49.0/src/cargo/util/toml/mod.rs:1094:36 clippy::redundant_closure_for_method_calls "redundant closure found" -cargo-0.49.0/src/cargo/util/toml/mod.rs:1121:13 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope" -cargo-0.49.0/src/cargo/util/toml/mod.rs:1197:32 clippy::map_unwrap_or "called `map(<f>).unwrap_or_else(<g>)` on an `Option` value. This can be done more directly by calling `map_or_else(<g>, <f>)` instead" -cargo-0.49.0/src/cargo/util/toml/mod.rs:124:1 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -cargo-0.49.0/src/cargo/util/toml/mod.rs:1504:9 clippy::unused_self "unused `self` argument" -cargo-0.49.0/src/cargo/util/toml/mod.rs:1526:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/util/toml/mod.rs:1582:19 clippy::default_trait_access "calling `util::toml::DetailedTomlDependency::default()` is more clear than this expression" -cargo-0.49.0/src/cargo/util/toml/mod.rs:1598:5 clippy::too_many_lines "this function has too many lines (153/100)" -cargo-0.49.0/src/cargo/util/toml/mod.rs:1687:33 clippy::unnecessary_lazy_evaluations "unnecessary closure used to substitute value for `Option::None`" -cargo-0.49.0/src/cargo/util/toml/mod.rs:178:1 clippy::module_name_repetitions "item name starts with its containing module's name" -cargo-0.49.0/src/cargo/util/toml/mod.rs:248:1 clippy::module_name_repetitions "item name starts with its containing module's name" -cargo-0.49.0/src/cargo/util/toml/mod.rs:274:1 clippy::module_name_repetitions "item name starts with its containing module's name" -cargo-0.49.0/src/cargo/util/toml/mod.rs:277:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/util/toml/mod.rs:281:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/util/toml/mod.rs:285:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -cargo-0.49.0/src/cargo/util/toml/mod.rs:294:1 clippy::module_name_repetitions "item name starts with its containing module's name" -cargo-0.49.0/src/cargo/util/toml/mod.rs:31:1 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -cargo-0.49.0/src/cargo/util/toml/mod.rs:381:35 clippy::cast_possible_truncation "casting `i64` to `u32` may truncate the value" -cargo-0.49.0/src/cargo/util/toml/mod.rs:381:35 clippy::cast_sign_loss "casting `i64` to `u32` may lose the sign of the value" -cargo-0.49.0/src/cargo/util/toml/mod.rs:388:35 clippy::cast_possible_truncation "casting `u64` to `u32` may truncate the value" -cargo-0.49.0/src/cargo/util/toml/mod.rs:398:1 clippy::module_name_repetitions "item name starts with its containing module's name" -cargo-0.49.0/src/cargo/util/toml/mod.rs:450:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -cargo-0.49.0/src/cargo/util/toml/mod.rs:536:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -cargo-0.49.0/src/cargo/util/toml/mod.rs:783:1 clippy::module_name_repetitions "item name starts with its containing module's name" -cargo-0.49.0/src/cargo/util/toml/mod.rs:824:1 clippy::module_name_repetitions "item name starts with its containing module's name" -cargo-0.49.0/src/cargo/util/toml/mod.rs:834:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -cargo-0.49.0/src/cargo/util/toml/mod.rs:83:42 clippy::redundant_closure_for_method_calls "redundant closure found" -cargo-0.49.0/src/cargo/util/toml/mod.rs:852:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -cargo-0.49.0/src/cargo/util/toml/mod.rs:852:5 clippy::missing_panics_doc "docs for function which may panic missing `# Panics` section" -cargo-0.49.0/src/cargo/util/toml/mod.rs:852:5 clippy::too_many_lines "this function has too many lines (138/100)" -cargo-0.49.0/src/cargo/util/toml/mod.rs:962:9 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope" -cargo-0.49.0/src/cargo/util/toml/mod.rs:979:9 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope" -cargo-0.49.0/src/cargo/util/toml/mod.rs:98:5 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope" -cargo-0.49.0/src/cargo/util/toml/mod.rs:999:23 clippy::default_trait_access "calling `util::toml::DetailedTomlDependency::default()` is more clear than this expression" -cargo-0.49.0/src/cargo/util/toml/targets.rs:112:27 clippy::redundant_closure_for_method_calls "redundant closure found" -cargo-0.49.0/src/cargo/util/toml/targets.rs:325:5 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope" -cargo-0.49.0/src/cargo/util/toml/targets.rs:586:21 clippy::redundant_closure_for_method_calls "redundant closure found" -cargo-0.49.0/src/cargo/util/toml/targets.rs:593:42 clippy::redundant_closure_for_method_calls "redundant closure found" -cargo-0.49.0/src/cargo/util/toml/targets.rs:605:19 clippy::redundant_closure_for_method_calls "redundant closure found" -cargo-0.49.0/src/cargo/util/toml/targets.rs:612:42 clippy::redundant_closure_for_method_calls "redundant closure found" -cargo-0.49.0/src/cargo/util/toml/targets.rs:756:36 clippy::redundant_closure_for_method_calls "redundant closure found" -cargo-0.49.0/src/cargo/util/vcs.rs:10:1 clippy::must_use_candidate "this function could have a `#[must_use]` attribute" -cargo-0.49.0/src/cargo/util/vcs.rs:33:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -cargo-0.49.0/src/cargo/util/vcs.rs:37:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -cargo-0.49.0/src/cargo/util/vcs.rs:43:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -cargo-0.49.0/src/cargo/util/vcs.rs:47:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -cargo-0.49.0/src/cargo/util/vcs.rs:59:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -cargo-0.49.0/src/cargo/util/vcs.rs:66:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -cargo-0.49.0/src/cargo/util/workspace.rs:52:1 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -cargo-0.49.0/src/cargo/util/workspace.rs:56:1 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -cargo-0.49.0/src/cargo/util/workspace.rs:60:1 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -cargo-0.49.0/src/cargo/util/workspace.rs:64:1 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -iron-0.6.1/src/error.rs:24:1 clippy::module_name_repetitions "item name ends with its containing module's name" -iron-0.6.1/src/iron.rs:105:13 clippy::redundant_field_names "redundant field names in struct initialization" -iron-0.6.1/src/iron.rs:119:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -iron-0.6.1/src/iron.rs:133:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -iron-0.6.1/src/iron.rs:143:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -iron-0.6.1/src/iron.rs:149:13 clippy::redundant_field_names "redundant field names in struct initialization" -iron-0.6.1/src/iron.rs:167:49 clippy::similar_names "binding's name is too similar to existing binding" -iron-0.6.1/src/iron.rs:196:9 clippy::let_underscore_drop "non-binding `let` on a type that implements `Drop`" -iron-0.6.1/src/iron.rs:80:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -iron-0.6.1/src/iron.rs:85:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -iron-0.6.1/src/iron.rs:90:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -iron-0.6.1/src/lib.rs:1:null clippy::cargo_common_metadata "package `iron` is missing `package.categories` metadata" -iron-0.6.1/src/lib.rs:1:null clippy::cargo_common_metadata "package `iron` is missing `package.keywords` metadata" -iron-0.6.1/src/lib.rs:1:null clippy::multiple_crate_versions "multiple versions for dependency `log`: 0.3.9, 0.4.8" -iron-0.6.1/src/middleware/mod.rs:137:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -iron-0.6.1/src/middleware/mod.rs:150:1 clippy::module_name_repetitions "item name ends with its containing module's name" -iron-0.6.1/src/middleware/mod.rs:152:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -iron-0.6.1/src/middleware/mod.rs:159:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -iron-0.6.1/src/middleware/mod.rs:171:1 clippy::module_name_repetitions "item name ends with its containing module's name" -iron-0.6.1/src/middleware/mod.rs:173:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -iron-0.6.1/src/middleware/mod.rs:182:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -iron-0.6.1/src/middleware/mod.rs:192:1 clippy::module_name_repetitions "item name ends with its containing module's name" -iron-0.6.1/src/middleware/mod.rs:217:25 clippy::doc_markdown "you should put `ChainBuilder` between ticks in the documentation" -iron-0.6.1/src/middleware/mod.rs:264:5 clippy::missing_panics_doc "docs for function which may panic missing `# Panics` section" -iron-0.6.1/src/middleware/mod.rs:328:20 clippy::similar_names "binding's name is too similar to existing binding" -iron-0.6.1/src/middleware/mod.rs:360:16 clippy::similar_names "binding's name is too similar to existing binding" -iron-0.6.1/src/middleware/mod.rs:368:33 clippy::similar_names "binding's name is too similar to existing binding" -iron-0.6.1/src/middleware/mod.rs:428:40 clippy::similar_names "binding's name is too similar to existing binding" -iron-0.6.1/src/middleware/mod.rs:434:40 clippy::similar_names "binding's name is too similar to existing binding" -iron-0.6.1/src/middleware/mod.rs:444:40 clippy::similar_names "binding's name is too similar to existing binding" -iron-0.6.1/src/modifiers.rs:132:14 clippy::expect_fun_call "use of `expect` followed by a function call" -iron-0.6.1/src/request/mod.rs:113:24 clippy::similar_names "binding's name is too similar to existing binding" -iron-0.6.1/src/request/mod.rs:121:13 clippy::redundant_field_names "redundant field names in struct initialization" -iron-0.6.1/src/request/mod.rs:123:13 clippy::redundant_field_names "redundant field names in struct initialization" -iron-0.6.1/src/request/mod.rs:124:13 clippy::redundant_field_names "redundant field names in struct initialization" -iron-0.6.1/src/request/mod.rs:126:13 clippy::redundant_field_names "redundant field names in struct initialization" -iron-0.6.1/src/request/mod.rs:128:13 clippy::redundant_field_names "redundant field names in struct initialization" -iron-0.6.1/src/request/mod.rs:153:69 clippy::doc_markdown "you should put `HttpReader` between ticks in the documentation" -iron-0.6.1/src/request/mod.rs:154:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -iron-0.6.1/src/request/mod.rs:32:1 clippy::manual_non_exhaustive "this seems like a manual implementation of the non-exhaustive pattern" -iron-0.6.1/src/request/mod.rs:75:34 clippy::doc_markdown "you should put `HttpRequest` between ticks in the documentation" -iron-0.6.1/src/request/mod.rs:77:39 clippy::doc_markdown "you should put `HttpRequest` between ticks in the documentation" -iron-0.6.1/src/request/mod.rs:78:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -iron-0.6.1/src/request/mod.rs:82:13 clippy::similar_names "binding's name is too similar to existing binding" -iron-0.6.1/src/request/mod.rs:83:29 clippy::similar_names "binding's name is too similar to existing binding" -iron-0.6.1/src/request/mod.rs:85:24 clippy::similar_names "binding's name is too similar to existing binding" -iron-0.6.1/src/request/url.rs:109:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -iron-0.6.1/src/request/url.rs:117:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -iron-0.6.1/src/request/url.rs:129:1 clippy::from_over_into "an implementation of `From` is preferred since it gives you `Into<_>` for free where the reverse isn't true" -iron-0.6.1/src/request/url.rs:21:14 clippy::doc_markdown "you should put bare URLs between `<`/`>` or make a proper Markdown link" -iron-0.6.1/src/request/url.rs:22:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -iron-0.6.1/src/request/url.rs:31:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -iron-0.6.1/src/request/url.rs:47:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -iron-0.6.1/src/request/url.rs:52:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -iron-0.6.1/src/request/url.rs:57:5 clippy::missing_panics_doc "docs for function which may panic missing `# Panics` section" -iron-0.6.1/src/request/url.rs:57:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -iron-0.6.1/src/request/url.rs:63:5 clippy::missing_panics_doc "docs for function which may panic missing `# Panics` section" -iron-0.6.1/src/request/url.rs:63:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -iron-0.6.1/src/request/url.rs:73:5 clippy::missing_panics_doc "docs for function which may panic missing `# Panics` section" -iron-0.6.1/src/request/url.rs:73:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -iron-0.6.1/src/request/url.rs:83:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -iron-0.6.1/src/request/url.rs:96:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -iron-0.6.1/src/response.rs:121:19 clippy::single_match_else "you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let`" -iron-0.6.1/src/response.rs:125:43 clippy::redundant_closure_for_method_calls "redundant closure found" -iron-0.6.1/src/response.rs:139:41 clippy::redundant_closure_for_method_calls "redundant closure found" -iron-0.6.1/src/response.rs:24:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -iron-0.6.1/src/response.rs:95:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -iron-0.6.1/src/response.rs:95:5 clippy::new_without_default "you should consider adding a `Default` implementation for `response::Response`" -libc-0.2.81/build.rs:114:19 clippy::map_unwrap_or "called `map(<f>).unwrap_or(<a>)` on an `Option` value. This can be done more directly by calling `map_or(<a>, <f>)` instead" -libc-0.2.81/build.rs:124:5 clippy::question_mark "this block may be rewritten with the `?` operator" -libc-0.2.81/build.rs:133:5 clippy::question_mark "this block may be rewritten with the `?` operator" -libc-0.2.81/src/macros.rs:243:17 clippy::missing_safety_doc "unsafe function's docs miss `# Safety` section" -libc-0.2.81/src/macros.rs:243:17 clippy::missing_safety_doc "unsafe function's docs miss `# Safety` section" -libc-0.2.81/src/macros.rs:243:17 clippy::missing_safety_doc "unsafe function's docs miss `# Safety` section" -libc-0.2.81/src/macros.rs:243:null clippy::must_use_candidate "this function could have a `#[must_use]` attribute" -libc-0.2.81/src/macros.rs:243:null clippy::must_use_candidate "this function could have a `#[must_use]` attribute" -libc-0.2.81/src/macros.rs:243:null clippy::must_use_candidate "this function could have a `#[must_use]` attribute" -libc-0.2.81/src/macros.rs:243:null clippy::must_use_candidate "this function could have a `#[must_use]` attribute" -libc-0.2.81/src/macros.rs:243:null clippy::must_use_candidate "this function could have a `#[must_use]` attribute" -libc-0.2.81/src/macros.rs:243:null clippy::must_use_candidate "this function could have a `#[must_use]` attribute" -libc-0.2.81/src/macros.rs:243:null clippy::must_use_candidate "this function could have a `#[must_use]` attribute" -libc-0.2.81/src/macros.rs:243:null clippy::must_use_candidate "this function could have a `#[must_use]` attribute" -libc-0.2.81/src/macros.rs:243:null clippy::must_use_candidate "this function could have a `#[must_use]` attribute" -libc-0.2.81/src/macros.rs:243:null clippy::must_use_candidate "this function could have a `#[must_use]` attribute" -libc-0.2.81/src/macros.rs:243:null clippy::must_use_candidate "this function could have a `#[must_use]` attribute" -libc-0.2.81/src/macros.rs:243:null clippy::must_use_candidate "this function could have a `#[must_use]` attribute" -libc-0.2.81/src/macros.rs:243:null clippy::must_use_candidate "this function could have a `#[must_use]` attribute" -libc-0.2.81/src/macros.rs:243:null clippy::must_use_candidate "this function could have a `#[must_use]` attribute" -libc-0.2.81/src/macros.rs:243:null clippy::must_use_candidate "this function could have a `#[must_use]` attribute" -libc-0.2.81/src/macros.rs:243:null clippy::must_use_candidate "this function could have a `#[must_use]` attribute" -libc-0.2.81/src/macros.rs:243:null clippy::must_use_candidate "this function could have a `#[must_use]` attribute" -libc-0.2.81/src/macros.rs:243:null clippy::must_use_candidate "this function could have a `#[must_use]` attribute" -libc-0.2.81/src/macros.rs:243:null clippy::must_use_candidate "this function could have a `#[must_use]` attribute" -libc-0.2.81/src/macros.rs:243:null clippy::must_use_candidate "this function could have a `#[must_use]` attribute" -libc-0.2.81/src/macros.rs:259:null clippy::must_use_candidate "this function could have a `#[must_use]` attribute" -libc-0.2.81/src/macros.rs:259:null clippy::must_use_candidate "this function could have a `#[must_use]` attribute" -libc-0.2.81/src/macros.rs:259:null clippy::must_use_candidate "this function could have a `#[must_use]` attribute" -libc-0.2.81/src/macros.rs:259:null clippy::must_use_candidate "this function could have a `#[must_use]` attribute" -libc-0.2.81/src/macros.rs:259:null clippy::must_use_candidate "this function could have a `#[must_use]` attribute" -libc-0.2.81/src/macros.rs:259:null clippy::must_use_candidate "this function could have a `#[must_use]` attribute" -libc-0.2.81/src/macros.rs:259:null clippy::must_use_candidate "this function could have a `#[must_use]` attribute" -libc-0.2.81/src/macros.rs:259:null clippy::must_use_candidate "this function could have a `#[must_use]` attribute" -libc-0.2.81/src/macros.rs:259:null clippy::must_use_candidate "this function could have a `#[must_use]` attribute" -libc-0.2.81/src/macros.rs:259:null clippy::must_use_candidate "this function could have a `#[must_use]` attribute" -libc-0.2.81/src/macros.rs:259:null clippy::must_use_candidate "this function could have a `#[must_use]` attribute" -libc-0.2.81/src/macros.rs:259:null clippy::must_use_candidate "this function could have a `#[must_use]` attribute" -libc-0.2.81/src/macros.rs:259:null clippy::must_use_candidate "this function could have a `#[must_use]` attribute" -libc-0.2.81/src/macros.rs:259:null clippy::must_use_candidate "this function could have a `#[must_use]` attribute" -libc-0.2.81/src/macros.rs:259:null clippy::must_use_candidate "this function could have a `#[must_use]` attribute" -libc-0.2.81/src/unix/linux_like/linux/gnu/b64/x86_64/mod.rs:428:29 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/linux/gnu/b64/x86_64/mod.rs:429:30 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/linux/gnu/b64/x86_64/mod.rs:431:30 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/linux/gnu/b64/x86_64/mod.rs:432:32 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/linux/gnu/b64/x86_64/mod.rs:433:29 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/linux/gnu/b64/x86_64/mod.rs:434:32 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/linux/gnu/b64/x86_64/mod.rs:595:33 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/linux/gnu/b64/x86_64/mod.rs:596:33 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/linux/gnu/b64/x86_64/mod.rs:597:35 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/linux/gnu/b64/x86_64/mod.rs:622:32 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/linux/gnu/b64/x86_64/mod.rs:673:34 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/linux/gnu/b64/x86_64/mod.rs:696:33 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/linux/gnu/b64/x86_64/mod.rs:697:35 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/linux/gnu/b64/x86_64/mod.rs:698:33 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/linux/gnu/b64/x86_64/mod.rs:699:35 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/linux/gnu/b64/x86_64/mod.rs:712:34 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/linux/gnu/b64/x86_64/mod.rs:721:35 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/linux/gnu/b64/x86_64/mod.rs:722:32 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/linux/gnu/b64/x86_64/mod.rs:723:32 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/linux/gnu/b64/x86_64/mod.rs:751:31 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/linux/gnu/b64/x86_64/mod.rs:752:30 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/linux/gnu/b64/x86_64/mod.rs:753:30 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/linux/gnu/b64/x86_64/mod.rs:754:30 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/linux/gnu/b64/x86_64/mod.rs:755:29 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/linux/gnu/b64/x86_64/mod.rs:756:29 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/linux/gnu/b64/x86_64/mod.rs:757:29 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/linux/gnu/b64/x86_64/mod.rs:758:29 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/linux/gnu/b64/x86_64/mod.rs:759:29 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/linux/gnu/b64/x86_64/mod.rs:760:29 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/linux/gnu/b64/x86_64/mod.rs:768:30 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/linux/gnu/b64/x86_64/mod.rs:769:31 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/linux/gnu/b64/x86_64/mod.rs:771:31 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/linux/gnu/b64/x86_64/mod.rs:772:29 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/linux/gnu/b64/x86_64/mod.rs:773:29 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/linux/gnu/b64/x86_64/mod.rs:774:29 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/linux/gnu/b64/x86_64/mod.rs:775:32 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/linux/gnu/b64/x86_64/mod.rs:776:31 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/linux/gnu/b64/x86_64/mod.rs:777:32 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/linux/gnu/b64/x86_64/mod.rs:778:32 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/linux/gnu/b64/x86_64/mod.rs:779:31 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/linux/gnu/b64/x86_64/mod.rs:780:32 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/linux/gnu/b64/x86_64/mod.rs:781:32 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/linux/gnu/b64/x86_64/mod.rs:782:31 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/linux/gnu/b64/x86_64/mod.rs:783:31 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/linux/gnu/b64/x86_64/mod.rs:784:32 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/linux/gnu/b64/x86_64/mod.rs:785:33 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/linux/gnu/b64/x86_64/mod.rs:786:33 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/linux/gnu/b64/x86_64/mod.rs:787:30 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/linux/gnu/b64/x86_64/mod.rs:788:32 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/linux/gnu/b64/x86_64/mod.rs:789:32 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/linux/gnu/b64/x86_64/mod.rs:790:32 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/linux/gnu/b64/x86_64/mod.rs:791:32 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/linux/gnu/b64/x86_64/mod.rs:792:33 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/linux/gnu/b64/x86_64/mod.rs:794:31 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/linux/gnu/b64/x86_64/mod.rs:795:31 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/linux/gnu/b64/x86_64/mod.rs:796:31 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/linux/gnu/b64/x86_64/mod.rs:797:32 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/linux/gnu/b64/x86_64/mod.rs:798:31 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/linux/gnu/b64/x86_64/mod.rs:799:31 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/linux/gnu/b64/x86_64/mod.rs:800:31 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/linux/gnu/b64/x86_64/mod.rs:801:31 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/linux/gnu/b64/x86_64/mod.rs:803:27 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/linux/gnu/b64/x86_64/mod.rs:804:28 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/linux/gnu/b64/x86_64/mod.rs:805:28 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/linux/gnu/b64/x86_64/mod.rs:806:29 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/linux/gnu/b64/x86_64/mod.rs:807:29 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/linux/gnu/b64/x86_64/mod.rs:808:29 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/linux/gnu/b64/x86_64/mod.rs:809:29 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/linux/gnu/b64/x86_64/mod.rs:810:29 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/linux/gnu/b64/x86_64/mod.rs:811:29 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/linux/gnu/b64/x86_64/mod.rs:812:30 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/linux/gnu/b64/x86_64/mod.rs:813:30 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/linux/gnu/b64/x86_64/mod.rs:814:30 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/linux/gnu/b64/x86_64/mod.rs:815:30 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/linux/gnu/b64/x86_64/mod.rs:816:30 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/linux/gnu/b64/x86_64/mod.rs:817:31 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/linux/gnu/b64/x86_64/mod.rs:818:31 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/linux/gnu/b64/x86_64/mod.rs:821:31 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/linux/gnu/b64/x86_64/mod.rs:822:31 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/linux/gnu/b64/x86_64/mod.rs:823:32 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/linux/gnu/b64/x86_64/mod.rs:824:32 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/linux/gnu/b64/x86_64/mod.rs:825:32 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/linux/gnu/b64/x86_64/mod.rs:826:32 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/linux/gnu/b64/x86_64/mod.rs:827:32 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/linux/gnu/b64/x86_64/mod.rs:828:32 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/linux/gnu/b64/x86_64/mod.rs:829:33 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/linux/gnu/b64/x86_64/mod.rs:830:33 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/linux/gnu/b64/x86_64/mod.rs:831:33 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/linux/gnu/b64/x86_64/mod.rs:832:33 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/linux/gnu/b64/x86_64/mod.rs:833:33 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/linux/gnu/b64/x86_64/mod.rs:834:33 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/linux/gnu/b64/x86_64/mod.rs:835:33 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/linux/gnu/b64/x86_64/mod.rs:836:33 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/linux/gnu/b64/x86_64/mod.rs:841:32 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/linux/gnu/b64/x86_64/mod.rs:842:32 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/linux/gnu/b64/x86_64/mod.rs:843:32 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/linux/gnu/b64/x86_64/mod.rs:844:33 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/linux/gnu/mod.rs:1120:38 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/linux/gnu/mod.rs:178:34 clippy::unseparated_literal_suffix "integer type suffix should be separated by an underscore" -libc-0.2.81/src/unix/linux_like/linux/gnu/mod.rs:291:5 clippy::missing_safety_doc "unsafe function's docs miss `# Safety` section" -libc-0.2.81/src/unix/linux_like/linux/gnu/mod.rs:291:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -libc-0.2.81/src/unix/linux_like/linux/gnu/mod.rs:299:11 clippy::ptr_as_ptr "`as` casting between raw pointers without changing its mutability" -libc-0.2.81/src/unix/linux_like/linux/gnu/mod.rs:302:5 clippy::missing_safety_doc "unsafe function's docs miss `# Safety` section" -libc-0.2.81/src/unix/linux_like/linux/gnu/mod.rs:302:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -libc-0.2.81/src/unix/linux_like/linux/gnu/mod.rs:312:11 clippy::ptr_as_ptr "`as` casting between raw pointers without changing its mutability" -libc-0.2.81/src/unix/linux_like/linux/gnu/mod.rs:352:20 clippy::ptr_as_ptr "`as` casting between raw pointers without changing its mutability" -libc-0.2.81/src/unix/linux_like/linux/gnu/mod.rs:355:13 clippy::missing_safety_doc "unsafe function's docs miss `# Safety` section" -libc-0.2.81/src/unix/linux_like/linux/gnu/mod.rs:355:13 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -libc-0.2.81/src/unix/linux_like/linux/gnu/mod.rs:359:13 clippy::missing_safety_doc "unsafe function's docs miss `# Safety` section" -libc-0.2.81/src/unix/linux_like/linux/gnu/mod.rs:359:13 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -libc-0.2.81/src/unix/linux_like/linux/gnu/mod.rs:363:13 clippy::missing_safety_doc "unsafe function's docs miss `# Safety` section" -libc-0.2.81/src/unix/linux_like/linux/gnu/mod.rs:363:13 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -libc-0.2.81/src/unix/linux_like/linux/gnu/mod.rs:367:13 clippy::missing_safety_doc "unsafe function's docs miss `# Safety` section" -libc-0.2.81/src/unix/linux_like/linux/gnu/mod.rs:367:13 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -libc-0.2.81/src/unix/linux_like/linux/gnu/mod.rs:371:13 clippy::missing_safety_doc "unsafe function's docs miss `# Safety` section" -libc-0.2.81/src/unix/linux_like/linux/gnu/mod.rs:371:13 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -libc-0.2.81/src/unix/linux_like/linux/gnu/mod.rs:534:36 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/linux/gnu/mod.rs:645:31 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/linux/gnu/mod.rs:727:40 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/linux/gnu/mod.rs:728:40 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/linux/gnu/mod.rs:729:39 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/linux/gnu/mod.rs:731:44 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/linux/gnu/mod.rs:732:36 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/linux/gnu/mod.rs:733:41 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/linux/gnu/mod.rs:734:43 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/linux/gnu/mod.rs:735:42 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/linux/gnu/mod.rs:736:40 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/linux/gnu/mod.rs:737:36 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/linux/gnu/mod.rs:738:37 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/linux/gnu/mod.rs:741:39 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/linux/gnu/mod.rs:742:40 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/linux/gnu/mod.rs:743:40 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/linux/gnu/mod.rs:744:40 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/linux/gnu/mod.rs:745:40 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/linux/gnu/mod.rs:746:43 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/linux/gnu/mod.rs:747:42 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/linux/gnu/mod.rs:748:40 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/linux/gnu/mod.rs:749:39 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/linux/gnu/mod.rs:750:41 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/linux/gnu/mod.rs:751:41 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/linux/gnu/mod.rs:752:43 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/linux/gnu/mod.rs:753:42 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/linux/gnu/mod.rs:755:42 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/linux/gnu/mod.rs:756:41 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/linux/gnu/mod.rs:757:41 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/linux/gnu/mod.rs:758:39 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/linux/gnu/mod.rs:759:39 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/linux/gnu/mod.rs:761:41 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/linux/gnu/mod.rs:762:44 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/linux/gnu/mod.rs:763:45 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/linux/gnu/mod.rs:764:40 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/linux/gnu/mod.rs:765:40 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/linux/gnu/mod.rs:766:40 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/linux/gnu/mod.rs:767:44 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/linux/gnu/mod.rs:768:44 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/linux/gnu/mod.rs:769:39 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/linux/gnu/mod.rs:770:35 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/linux/gnu/mod.rs:771:35 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/linux/gnu/mod.rs:772:37 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/linux/gnu/mod.rs:773:39 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/linux/gnu/mod.rs:774:45 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/linux/gnu/mod.rs:775:41 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/linux/gnu/mod.rs:776:39 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/linux/gnu/mod.rs:803:34 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/linux/gnu/mod.rs:841:30 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/linux/gnu/mod.rs:842:37 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/linux/gnu/mod.rs:982:40 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/linux/gnu/mod.rs:984:46 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/linux/mod.rs:1209:36 clippy::cast_possible_truncation "casting `i32` to `i16` may truncate the value" -libc-0.2.81/src/unix/linux_like/linux/mod.rs:1210:36 clippy::cast_possible_truncation "casting `i32` to `i16` may truncate the value" -libc-0.2.81/src/unix/linux_like/linux/mod.rs:1235:39 clippy::unseparated_literal_suffix "integer type suffix should be separated by an underscore" -libc-0.2.81/src/unix/linux_like/linux/mod.rs:1236:41 clippy::unseparated_literal_suffix "integer type suffix should be separated by an underscore" -libc-0.2.81/src/unix/linux_like/linux/mod.rs:1274:42 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/linux/mod.rs:1324:31 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/linux/mod.rs:1333:37 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/linux/mod.rs:1334:35 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/linux/mod.rs:1346:34 clippy::cast_possible_wrap "casting `u32` to `i32` may wrap around the value" -libc-0.2.81/src/unix/linux_like/linux/mod.rs:1346:34 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/linux/mod.rs:1346:34 clippy::unseparated_literal_suffix "integer type suffix should be separated by an underscore" -libc-0.2.81/src/unix/linux_like/linux/mod.rs:1347:37 clippy::cast_possible_wrap "casting `u32` to `i32` may wrap around the value" -libc-0.2.81/src/unix/linux_like/linux/mod.rs:1347:37 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/linux/mod.rs:1347:37 clippy::unseparated_literal_suffix "integer type suffix should be separated by an underscore" -libc-0.2.81/src/unix/linux_like/linux/mod.rs:1348:36 clippy::cast_possible_wrap "casting `u32` to `i32` may wrap around the value" -libc-0.2.81/src/unix/linux_like/linux/mod.rs:1348:36 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/linux/mod.rs:1348:36 clippy::unseparated_literal_suffix "integer type suffix should be separated by an underscore" -libc-0.2.81/src/unix/linux_like/linux/mod.rs:1349:37 clippy::cast_possible_wrap "casting `u32` to `i32` may wrap around the value" -libc-0.2.81/src/unix/linux_like/linux/mod.rs:1349:37 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/linux/mod.rs:1349:37 clippy::unseparated_literal_suffix "integer type suffix should be separated by an underscore" -libc-0.2.81/src/unix/linux_like/linux/mod.rs:1350:35 clippy::cast_possible_wrap "casting `u32` to `i32` may wrap around the value" -libc-0.2.81/src/unix/linux_like/linux/mod.rs:1350:35 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/linux/mod.rs:1350:35 clippy::unseparated_literal_suffix "integer type suffix should be separated by an underscore" -libc-0.2.81/src/unix/linux_like/linux/mod.rs:1351:36 clippy::cast_possible_wrap "casting `u32` to `i32` may wrap around the value" -libc-0.2.81/src/unix/linux_like/linux/mod.rs:1351:36 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/linux/mod.rs:1351:36 clippy::unseparated_literal_suffix "integer type suffix should be separated by an underscore" -libc-0.2.81/src/unix/linux_like/linux/mod.rs:1352:31 clippy::cast_possible_wrap "casting `u32` to `i32` may wrap around the value" -libc-0.2.81/src/unix/linux_like/linux/mod.rs:1352:31 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/linux/mod.rs:1352:31 clippy::unseparated_literal_suffix "integer type suffix should be separated by an underscore" -libc-0.2.81/src/unix/linux_like/linux/mod.rs:1419:36 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/linux/mod.rs:1420:36 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/linux/mod.rs:1421:36 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/linux/mod.rs:1422:36 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/linux/mod.rs:1423:36 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/linux/mod.rs:1490:37 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/linux/mod.rs:1561:46 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/linux/mod.rs:1562:45 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/linux/mod.rs:1567:31 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/linux/mod.rs:1568:32 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/linux/mod.rs:1586:26 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/linux/mod.rs:1587:34 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/linux/mod.rs:1588:31 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/linux/mod.rs:1589:31 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/linux/mod.rs:1897:38 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/linux/mod.rs:1898:51 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/linux/mod.rs:1900:39 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/linux/mod.rs:1969:34 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/linux/mod.rs:1970:34 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/linux/mod.rs:1971:36 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/linux/mod.rs:1972:36 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/linux/mod.rs:1973:36 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/linux/mod.rs:1974:37 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/linux/mod.rs:1975:37 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/linux/mod.rs:1976:36 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/linux/mod.rs:1977:36 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/linux/mod.rs:1978:39 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/linux/mod.rs:1979:39 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/linux/mod.rs:1980:39 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/linux/mod.rs:1981:39 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/linux/mod.rs:1982:39 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/linux/mod.rs:1983:39 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/linux/mod.rs:1984:38 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/linux/mod.rs:1985:38 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/linux/mod.rs:1986:35 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/linux/mod.rs:1987:35 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/linux/mod.rs:1988:35 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/linux/mod.rs:1989:35 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/linux/mod.rs:1990:38 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/linux/mod.rs:1991:37 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/linux/mod.rs:1992:37 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/linux/mod.rs:1993:38 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/linux/mod.rs:1994:37 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/linux/mod.rs:1995:37 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/linux/mod.rs:1996:37 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/linux/mod.rs:1997:37 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/linux/mod.rs:1998:33 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/linux/mod.rs:1999:33 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/linux/mod.rs:2000:33 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/linux/mod.rs:2001:34 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/linux/mod.rs:2002:34 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/linux/mod.rs:2003:34 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/linux/mod.rs:2004:35 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/linux/mod.rs:2005:35 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/linux/mod.rs:2032:30 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/linux/mod.rs:2033:32 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/linux/mod.rs:2034:31 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/linux/mod.rs:2035:29 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/linux/mod.rs:2036:32 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/linux/mod.rs:2037:28 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/linux/mod.rs:2038:27 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/linux/mod.rs:2039:29 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/linux/mod.rs:2041:29 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/linux/mod.rs:2042:28 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/linux/mod.rs:2043:27 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/linux/mod.rs:2044:34 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/linux/mod.rs:2045:27 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/linux/mod.rs:2046:33 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/linux/mod.rs:2048:28 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/linux/mod.rs:2049:32 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/linux/mod.rs:2050:32 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/linux/mod.rs:2051:32 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/linux/mod.rs:2052:26 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/linux/mod.rs:2053:36 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/linux/mod.rs:2318:42 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/linux/mod.rs:2321:38 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/linux/mod.rs:2331:39 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/linux/mod.rs:2487:42 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/linux/mod.rs:2488:42 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/linux/mod.rs:2489:43 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/linux/mod.rs:2490:43 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/linux/mod.rs:2491:43 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/linux/mod.rs:2493:47 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/linux/mod.rs:2494:44 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/linux/mod.rs:2495:46 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/linux/mod.rs:2496:47 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/linux/mod.rs:2497:49 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/linux/mod.rs:2498:48 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/linux/mod.rs:2499:50 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/linux/mod.rs:2500:45 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/linux/mod.rs:2572:9 clippy::needless_return "unneeded `return` statement" -libc-0.2.81/src/unix/linux_like/linux/mod.rs:2578:20 clippy::zero_ptr "`0 as *mut _` detected" -libc-0.2.81/src/unix/linux_like/linux/mod.rs:2588:13 clippy::zero_ptr "`0 as *mut _` detected" -libc-0.2.81/src/unix/linux_like/linux/mod.rs:2590:13 clippy::ptr_as_ptr "`as` casting between raw pointers without changing its mutability" -libc-0.2.81/src/unix/linux_like/linux/mod.rs:2596:52 clippy::used_underscore_binding "used binding `_dummy` which is prefixed with an underscore. A leading underscore signals that a binding will not be used." -libc-0.2.81/src/unix/linux_like/linux/mod.rs:2597:11 clippy::cast_sign_loss "casting `i32` to `usize` may lose the sign of the value" -libc-0.2.81/src/unix/linux_like/linux/mod.rs:2601:21 clippy::explicit_iter_loop "it is more concise to loop over references to containers instead of using explicit iteration methods" -libc-0.2.81/src/unix/linux_like/linux/mod.rs:2611:9 clippy::unused_unit "unneeded unit expression" -libc-0.2.81/src/unix/linux_like/linux/mod.rs:2619:9 clippy::unused_unit "unneeded unit expression" -libc-0.2.81/src/unix/linux_like/linux/mod.rs:2634:9 clippy::cast_possible_wrap "casting `u32` to `i32` may wrap around the value" -libc-0.2.81/src/unix/linux_like/linux/mod.rs:2647:25 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/linux/mod.rs:2648:25 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/linux/mod.rs:2649:9 clippy::cast_possible_truncation "casting `u64` to `u32` may truncate the value" -libc-0.2.81/src/unix/linux_like/linux/mod.rs:2654:18 clippy::identity_op "the operation is ineffective. Consider reducing it to `(dev & 0x00000000000000ff)`" -libc-0.2.81/src/unix/linux_like/linux/mod.rs:2654:25 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/linux/mod.rs:2655:25 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/linux/mod.rs:2656:9 clippy::cast_possible_truncation "casting `u64` to `u32` may truncate the value" -libc-0.2.81/src/unix/linux_like/linux/mod.rs:2660:21 clippy::cast_lossless "casting `u32` to `u64` may become silently lossy if you later change the type" -libc-0.2.81/src/unix/linux_like/linux/mod.rs:2661:21 clippy::cast_lossless "casting `u32` to `u64` may become silently lossy if you later change the type" -libc-0.2.81/src/unix/linux_like/linux/mod.rs:2663:25 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/linux/mod.rs:2664:25 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/linux/mod.rs:2665:16 clippy::identity_op "the operation is ineffective. Consider reducing it to `(minor & 0x000000ff)`" -libc-0.2.81/src/unix/linux_like/linux/mod.rs:2665:25 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/linux/mod.rs:2666:25 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/linux/mod.rs:954:34 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/mod.rs:1000:31 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/mod.rs:1001:32 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/mod.rs:1002:32 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/mod.rs:1016:29 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/mod.rs:1017:32 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/mod.rs:1018:33 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/mod.rs:1019:33 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/mod.rs:1020:33 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/mod.rs:1029:29 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/mod.rs:1030:29 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/mod.rs:1031:30 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/mod.rs:1032:29 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/mod.rs:1033:29 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/mod.rs:1034:29 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/mod.rs:1035:29 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/mod.rs:1041:32 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/mod.rs:1042:32 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/mod.rs:1043:32 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/mod.rs:1044:32 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/mod.rs:1045:31 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/mod.rs:1046:32 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/mod.rs:1047:31 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/mod.rs:1048:31 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/mod.rs:1049:31 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/mod.rs:1050:31 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/mod.rs:1051:33 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/mod.rs:1053:29 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/mod.rs:1054:33 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/mod.rs:1055:30 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/mod.rs:1056:31 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/mod.rs:1057:31 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/mod.rs:1058:32 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/mod.rs:1059:31 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/mod.rs:1060:31 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/mod.rs:1073:42 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/mod.rs:1074:43 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/mod.rs:1075:37 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/mod.rs:1076:37 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/mod.rs:1077:41 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/mod.rs:1078:35 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/mod.rs:1079:35 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/mod.rs:1080:36 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/mod.rs:1081:35 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/mod.rs:1082:35 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/mod.rs:1083:31 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/mod.rs:1084:38 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/mod.rs:1086:30 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/mod.rs:1087:32 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/mod.rs:1089:30 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/mod.rs:1090:33 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/mod.rs:1091:30 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/mod.rs:1094:40 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/mod.rs:1095:37 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/mod.rs:1096:41 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/mod.rs:1097:40 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/mod.rs:1098:39 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/mod.rs:1099:34 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/mod.rs:1100:36 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/mod.rs:1101:38 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/mod.rs:1102:37 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/mod.rs:1105:44 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/mod.rs:1106:41 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/mod.rs:1107:42 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/mod.rs:1108:42 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/mod.rs:1109:41 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/mod.rs:1110:46 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/mod.rs:1111:41 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/mod.rs:1112:44 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/mod.rs:1113:40 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/mod.rs:1114:47 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/mod.rs:1115:36 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/mod.rs:1126:34 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/mod.rs:1127:29 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/mod.rs:1128:31 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/mod.rs:1179:32 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/mod.rs:1180:31 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/mod.rs:1218:27 clippy::identity_op "the operation is ineffective. Consider reducing it to `IPOPT_CONTROL`" -libc-0.2.81/src/unix/linux_like/mod.rs:1314:9 clippy::precedence "operator precedence can trip the unwary" -libc-0.2.81/src/unix/linux_like/mod.rs:1321:13 clippy::ptr_as_ptr "`as` casting between raw pointers without changing its mutability" -libc-0.2.81/src/unix/linux_like/mod.rs:1323:13 clippy::zero_ptr "`0 as *mut _` detected" -libc-0.2.81/src/unix/linux_like/mod.rs:1332:9 clippy::cast_possible_truncation "casting `usize` to `u32` may truncate the value on targets with 64-bit wide pointers" -libc-0.2.81/src/unix/linux_like/mod.rs:1337:9 clippy::cast_possible_truncation "casting `usize` to `u32` may truncate the value on targets with 64-bit wide pointers" -libc-0.2.81/src/unix/linux_like/mod.rs:1341:18 clippy::cast_sign_loss "casting `i32` to `usize` may lose the sign of the value" -libc-0.2.81/src/unix/linux_like/mod.rs:1344:9 clippy::needless_return "unneeded `return` statement" -libc-0.2.81/src/unix/linux_like/mod.rs:1348:18 clippy::cast_sign_loss "casting `i32` to `usize` may lose the sign of the value" -libc-0.2.81/src/unix/linux_like/mod.rs:1350:9 clippy::needless_return "unneeded `return` statement" -libc-0.2.81/src/unix/linux_like/mod.rs:1354:18 clippy::cast_sign_loss "casting `i32` to `usize` may lose the sign of the value" -libc-0.2.81/src/unix/linux_like/mod.rs:1357:9 clippy::needless_return "unneeded `return` statement" -libc-0.2.81/src/unix/linux_like/mod.rs:1361:21 clippy::explicit_iter_loop "it is more concise to loop over references to containers instead of using explicit iteration methods" -libc-0.2.81/src/unix/linux_like/mod.rs:1381:9 clippy::cast_possible_truncation "casting `i32` to `i8` may truncate the value" -libc-0.2.81/src/unix/linux_like/mod.rs:1389:9 clippy::verbose_bit_mask "bit mask could be simplified with a call to `trailing_zeros`" -libc-0.2.81/src/unix/linux_like/mod.rs:446:31 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/mod.rs:591:36 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/mod.rs:592:38 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/mod.rs:593:35 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/mod.rs:594:33 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/mod.rs:595:34 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/mod.rs:596:36 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/mod.rs:597:37 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/mod.rs:598:37 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/mod.rs:599:39 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/mod.rs:600:34 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/mod.rs:601:35 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/mod.rs:602:35 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/mod.rs:607:37 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/mod.rs:608:35 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/mod.rs:764:35 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/mod.rs:765:39 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/linux_like/mod.rs:991:30 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/mod.rs:198:29 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/mod.rs:199:28 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/mod.rs:201:35 clippy::unnecessary_cast "casting integer literal to `usize` is unnecessary" -libc-0.2.81/src/unix/mod.rs:202:35 clippy::unnecessary_cast "casting integer literal to `usize` is unnecessary" -libc-0.2.81/src/unix/mod.rs:282:40 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/mod.rs:284:41 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/mod.rs:285:36 clippy::unreadable_literal "long literal lacking separators" -libc-0.2.81/src/unix/mod.rs:34:10 clippy::upper_case_acronyms "name `DIR` contains a capitalized acronym" -libc-0.2.81/src/unix/mod.rs:386:10 clippy::upper_case_acronyms "name `FILE` contains a capitalized acronym" -log-0.4.11/src/lib.rs:1047:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -log-0.4.11/src/lib.rs:1053:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -log-0.4.11/src/lib.rs:1059:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -log-0.4.11/src/lib.rs:1093:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -log-0.4.11/src/lib.rs:1093:5 clippy::new_without_default "you should consider adding a `Default` implementation for `MetadataBuilder<'a>`" -log-0.4.11/src/lib.rs:1118:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -log-0.4.11/src/lib.rs:1177:1 clippy::inline_always "you have declared `#[inline(always)]` on `max_level`. This is usually a bad idea" -log-0.4.11/src/lib.rs:1178:1 clippy::must_use_candidate "this function could have a `#[must_use]` attribute" -log-0.4.11/src/lib.rs:1306:1 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -log-0.4.11/src/lib.rs:1358:1 clippy::must_use_candidate "this function could have a `#[must_use]` attribute" -log-0.4.11/src/lib.rs:1359:5 clippy::if_not_else "unnecessary `!=` operation" -log-0.4.11/src/lib.rs:1407:1 clippy::must_use_candidate "this function could have a `#[must_use]` attribute" -log-0.4.11/src/lib.rs:356:1 clippy::expl_impl_clone_on_copy "you are implementing `Clone` explicitly on a `Copy` type" -log-0.4.11/src/lib.rs:448:12 clippy::manual_range_contains "manual `RangeInclusive::contains` implementation" -log-0.4.11/src/lib.rs:500:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -log-0.4.11/src/lib.rs:506:28 clippy::trivially_copy_pass_by_ref "this argument (8 byte) is passed by reference, but would be more efficient if passed by value (limit: 8 byte)" -log-0.4.11/src/lib.rs:506:5 clippy::missing_panics_doc "docs for function which may panic missing `# Panics` section" -log-0.4.11/src/lib.rs:506:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -log-0.4.11/src/lib.rs:538:1 clippy::expl_impl_clone_on_copy "you are implementing `Clone` explicitly on a `Copy` type" -log-0.4.11/src/lib.rs:653:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -log-0.4.11/src/lib.rs:661:21 clippy::trivially_copy_pass_by_ref "this argument (8 byte) is passed by reference, but would be more efficient if passed by value (limit: 8 byte)" -log-0.4.11/src/lib.rs:661:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -log-0.4.11/src/lib.rs:677:44 clippy::match_same_arms "this `match` has identical arm bodies" -log-0.4.11/src/lib.rs:758:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -log-0.4.11/src/lib.rs:764:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -log-0.4.11/src/lib.rs:770:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -log-0.4.11/src/lib.rs:776:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -log-0.4.11/src/lib.rs:782:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -log-0.4.11/src/lib.rs:788:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -log-0.4.11/src/lib.rs:794:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -log-0.4.11/src/lib.rs:803:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -log-0.4.11/src/lib.rs:809:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -log-0.4.11/src/lib.rs:818:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -log-0.4.11/src/lib.rs:908:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -log-0.4.11/src/lib.rs:908:5 clippy::new_without_default "you should consider adding a `Default` implementation for `RecordBuilder<'a>`" -log-0.4.11/src/lib.rs:995:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -proc-macro2-1.0.24/src/detection.rs:2:5 clippy::wildcard_imports "usage of wildcard import" -proc-macro2-1.0.24/src/fallback.rs:108:17 clippy::match_wildcard_for_single_variants "wildcard match will miss any future added variants" -proc-macro2-1.0.24/src/fallback.rs:269:20 clippy::unused_self "unused `self` argument" -proc-macro2-1.0.24/src/fallback.rs:430:24 clippy::trivially_copy_pass_by_ref "this argument (0 byte) is passed by reference, but would be more efficient if passed by value (limit: 8 byte)" -proc-macro2-1.0.24/src/fallback.rs:437:23 clippy::trivially_copy_pass_by_ref "this argument (0 byte) is passed by reference, but would be more efficient if passed by value (limit: 8 byte)" -proc-macro2-1.0.24/src/fallback.rs:437:23 clippy::unused_self "unused `self` argument" -proc-macro2-1.0.24/src/fallback.rs:471:17 clippy::trivially_copy_pass_by_ref "this argument (0 byte) is passed by reference, but would be more efficient if passed by value (limit: 8 byte)" -proc-macro2-1.0.24/src/fallback.rs:471:17 clippy::unused_self "unused `self` argument" -proc-macro2-1.0.24/src/fallback.rs:654:5 clippy::manual_range_contains "manual `RangeInclusive::contains` implementation" -proc-macro2-1.0.24/src/fallback.rs:655:12 clippy::manual_range_contains "manual `RangeInclusive::contains` implementation" -proc-macro2-1.0.24/src/fallback.rs:661:5 clippy::manual_range_contains "manual `RangeInclusive::contains` implementation" -proc-macro2-1.0.24/src/fallback.rs:662:12 clippy::manual_range_contains "manual `RangeInclusive::contains` implementation" -proc-macro2-1.0.24/src/fallback.rs:664:12 clippy::manual_range_contains "manual `RangeInclusive::contains` implementation" -proc-macro2-1.0.24/src/fallback.rs:674:37 clippy::manual_range_contains "manual `RangeInclusive::contains` implementation" -proc-macro2-1.0.24/src/fallback.rs:678:5 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope" -proc-macro2-1.0.24/src/fallback.rs:85:9 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope" -proc-macro2-1.0.24/src/fallback.rs:882:43 clippy::unused_self "unused `self` argument" -proc-macro2-1.0.24/src/lib.rs:1017:9 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -proc-macro2-1.0.24/src/lib.rs:1081:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -proc-macro2-1.0.24/src/lib.rs:1099:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -proc-macro2-1.0.24/src/lib.rs:1117:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -proc-macro2-1.0.24/src/lib.rs:1135:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -proc-macro2-1.0.24/src/lib.rs:1141:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -proc-macro2-1.0.24/src/lib.rs:1146:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -proc-macro2-1.0.24/src/lib.rs:1151:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -proc-macro2-1.0.24/src/lib.rs:1156:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -proc-macro2-1.0.24/src/lib.rs:152:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -proc-macro2-1.0.24/src/lib.rs:157:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -proc-macro2-1.0.24/src/lib.rs:373:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -proc-macro2-1.0.24/src/lib.rs:383:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -proc-macro2-1.0.24/src/lib.rs:397:24 clippy::trivially_copy_pass_by_ref "this argument (4 byte) is passed by reference, but would be more efficient if passed by value (limit: 8 byte)" -proc-macro2-1.0.24/src/lib.rs:397:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -proc-macro2-1.0.24/src/lib.rs:403:23 clippy::trivially_copy_pass_by_ref "this argument (4 byte) is passed by reference, but would be more efficient if passed by value (limit: 8 byte)" -proc-macro2-1.0.24/src/lib.rs:403:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -proc-macro2-1.0.24/src/lib.rs:418:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -proc-macro2-1.0.24/src/lib.rs:425:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -proc-macro2-1.0.24/src/lib.rs:464:17 clippy::trivially_copy_pass_by_ref "this argument (4 byte) is passed by reference, but would be more efficient if passed by value (limit: 8 byte)" -proc-macro2-1.0.24/src/lib.rs:500:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -proc-macro2-1.0.24/src/lib.rs:626:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -proc-macro2-1.0.24/src/lib.rs:633:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -proc-macro2-1.0.24/src/lib.rs:641:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -proc-macro2-1.0.24/src/lib.rs:652:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -proc-macro2-1.0.24/src/lib.rs:662:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -proc-macro2-1.0.24/src/lib.rs:672:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -proc-macro2-1.0.24/src/lib.rs:734:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -proc-macro2-1.0.24/src/lib.rs:743:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -proc-macro2-1.0.24/src/lib.rs:752:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -proc-macro2-1.0.24/src/lib.rs:757:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -proc-macro2-1.0.24/src/lib.rs:788:19 clippy::doc_markdown "you should put `XID_Start` between ticks in the documentation" -proc-macro2-1.0.24/src/lib.rs:788:69 clippy::doc_markdown "you should put `XID_Continue` between ticks in the documentation" -proc-macro2-1.0.24/src/lib.rs:891:36 clippy::doc_markdown "you should put `syn::parse_str` between ticks in the documentation" -proc-macro2-1.0.24/src/lib.rs:894:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -proc-macro2-1.0.24/src/lib.rs:911:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -proc-macro2-1.0.24/src/lib.rs:996:9 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -proc-macro2-1.0.24/src/parse.rs:552:5 clippy::while_let_on_iterator "this loop could be written as a `for` loop" -proc-macro2-1.0.24/src/parse.rs:584:21 clippy::manual_range_contains "manual `RangeInclusive::contains` implementation" -proc-macro2-1.0.24/src/parse.rs:602:20 clippy::map_unwrap_or "called `map(<f>).unwrap_or(<a>)` on an `Option` value. This can be done more directly by calling `map_or(<a>, <f>)` instead" -proc-macro2-1.0.24/src/parse.rs:696:29 clippy::cast_lossless "casting `u8` to `u64` may become silently lossy if you later change the type" -proc-macro2-1.0.24/src/parse.rs:702:34 clippy::cast_lossless "casting `u8` to `u64` may become silently lossy if you later change the type" -proc-macro2-1.0.24/src/parse.rs:708:34 clippy::cast_lossless "casting `u8` to `u64` may become silently lossy if you later change the type" -proc-macro2-1.0.24/src/parse.rs:793:5 clippy::vec_init_then_push "calls to `push` immediately after creation" -proc-macro2-1.0.24/src/parse.rs:803:15 clippy::explicit_iter_loop "it is more concise to loop over references to containers instead of using explicit iteration methods" -proc-macro2-1.0.24/src/parse.rs:808:15 clippy::explicit_iter_loop "it is more concise to loop over references to containers instead of using explicit iteration methods" -proc-macro2-1.0.24/src/wrapper.rs:415:24 clippy::trivially_copy_pass_by_ref "this argument (4 byte) is passed by reference, but would be more efficient if passed by value (limit: 8 byte)" -proc-macro2-1.0.24/src/wrapper.rs:429:23 clippy::trivially_copy_pass_by_ref "this argument (4 byte) is passed by reference, but would be more efficient if passed by value (limit: 8 byte)" -proc-macro2-1.0.24/src/wrapper.rs:492:17 clippy::trivially_copy_pass_by_ref "this argument (4 byte) is passed by reference, but would be more efficient if passed by value (limit: 8 byte)" -puffin-02dd4a3/puffin-imgui/src/ui.rs:158:15 clippy::cast_precision_loss "casting `i64` to `f32` causes a loss of precision (`i64` is 64 bits wide, but `f32`'s mantissa is only 23 bits wide)" -puffin-02dd4a3/puffin-imgui/src/ui.rs:175:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -puffin-02dd4a3/puffin-imgui/src/ui.rs:183:5 clippy::too_many_lines "this function has too many lines (115/100)" -puffin-02dd4a3/puffin-imgui/src/ui.rs:1:5 clippy::wildcard_imports "usage of wildcard import" -puffin-02dd4a3/puffin-imgui/src/ui.rs:207:16 clippy::collapsible_else_if "this `else { if .. }` block can be collapsed" -puffin-02dd4a3/puffin-imgui/src/ui.rs:271:67 clippy::cast_precision_loss "casting `i64` to `f32` causes a loss of precision (`i64` is 64 bits wide, but `f32`'s mantissa is only 23 bits wide)" -puffin-02dd4a3/puffin-imgui/src/ui.rs:2:5 clippy::wildcard_imports "usage of wildcard import" -puffin-02dd4a3/puffin-imgui/src/ui.rs:376:29 clippy::cast_precision_loss "casting `i64` to `f32` causes a loss of precision (`i64` is 64 bits wide, but `f32`'s mantissa is only 23 bits wide)" -puffin-02dd4a3/puffin-imgui/src/ui.rs:381:44 clippy::cast_precision_loss "casting `i64` to `f32` causes a loss of precision (`i64` is 64 bits wide, but `f32`'s mantissa is only 23 bits wide)" -puffin-02dd4a3/puffin-imgui/src/ui.rs:453:9 clippy::similar_names "binding's name is too similar to existing binding" -puffin-02dd4a3/puffin-imgui/src/ui.rs:540:14 clippy::cast_possible_truncation "casting `f64` to `f32` may truncate the value" -puffin-02dd4a3/puffin-imgui/src/ui.rs:551:5 clippy::cast_precision_loss "casting `i64` to `f64` causes a loss of precision (`i64` is 64 bits wide, but `f64`'s mantissa is only 52 bits wide)" -puffin-02dd4a3/puffin-imgui/src/ui.rs:584:39 clippy::cast_precision_loss "casting `usize` to `f32` causes a loss of precision (`usize` is 32 or 64 bits wide, but `f32`'s mantissa is only 23 bits wide)" -puffin-02dd4a3/puffin-imgui/src/ui.rs:59:26 clippy::unsafe_derive_deserialize "you are deriving `serde::Deserialize` on a type that has methods using `unsafe`" -puffin-02dd4a3/puffin-imgui/src/ui.rs:61:1 clippy::module_name_repetitions "item name ends with its containing module's name" -puffin-02dd4a3/puffin-imgui/src/ui.rs:627:39 clippy::cast_precision_loss "casting `usize` to `f32` causes a loss of precision (`usize` is 32 or 64 bits wide, but `f32`'s mantissa is only 23 bits wide)" -puffin-02dd4a3/puffin-imgui/src/ui.rs:674:47 clippy::cast_precision_loss "casting `usize` to `f64` causes a loss of precision on targets with 64-bit wide pointers (`usize` is 64 bits wide, but `f64`'s mantissa is only 52 bits wide)" -puffin-02dd4a3/puffin-imgui/src/ui.rs:690:9 clippy::cast_precision_loss "casting `usize` to `f32` causes a loss of precision (`usize` is 32 or 64 bits wide, but `f32`'s mantissa is only 23 bits wide)" -puffin-02dd4a3/puffin/src/data.rs:102:25 clippy::cast_possible_truncation "casting `usize` to `u8` may truncate the value" -puffin-02dd4a3/puffin/src/data.rs:112:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -puffin-02dd4a3/puffin/src/data.rs:116:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -puffin-02dd4a3/puffin/src/data.rs:137:24 clippy::match_same_arms "this `match` has identical arm bodies" -puffin-02dd4a3/puffin/src/data.rs:177:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -puffin-02dd4a3/puffin/src/data.rs:211:21 clippy::cast_possible_truncation "casting `u64` to `usize` may truncate the value on targets with 32-bit wide pointers" -puffin-02dd4a3/puffin/src/data.rs:24:5 clippy::wildcard_imports "usage of wildcard import" -puffin-02dd4a3/puffin/src/lib.rs:113:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -puffin-02dd4a3/puffin/src/lib.rs:147:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -puffin-02dd4a3/puffin/src/lib.rs:147:5 clippy::missing_panics_doc "docs for function which may panic missing `# Panics` section" -puffin-02dd4a3/puffin/src/lib.rs:165:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -puffin-02dd4a3/puffin/src/lib.rs:200:21 clippy::default_trait_access "calling `Stream::default()` is more clear than this expression" -puffin-02dd4a3/puffin/src/lib.rs:257:78 clippy::default_trait_access "calling `std::cell::RefCell::default()` is more clear than this expression" -puffin-02dd4a3/puffin/src/lib.rs:297:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -puffin-02dd4a3/puffin/src/lib.rs:302:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -puffin-02dd4a3/puffin/src/lib.rs:308:28 clippy::default_trait_access "calling `FullProfileData::default()` is more clear than this expression" -puffin-02dd4a3/puffin/src/lib.rs:316:1 clippy::must_use_candidate "this function could have a `#[must_use]` attribute" -puffin-02dd4a3/puffin/src/lib.rs:321:5 clippy::cast_possible_truncation "casting `u128` to `i64` may truncate the value" -puffin-02dd4a3/puffin/src/lib.rs:348:28 clippy::default_trait_access "calling `std::marker::PhantomData::default()` is more clear than this expression" -puffin-02dd4a3/puffin/src/lib.rs:359:1 clippy::must_use_candidate "this function could have a `#[must_use]` attribute" -puffin-02dd4a3/puffin/src/lib.rs:375:1 clippy::must_use_candidate "this function could have a `#[must_use]` attribute" -puffin-02dd4a3/puffin/src/lib.rs:376:5 clippy::option_if_let_else "use Option::map_or instead of an if let/else" -puffin-02dd4a3/puffin/src/lib.rs:377:9 clippy::option_if_let_else "use Option::map_or instead of an if let/else" -puffin-02dd4a3/puffin/src/lib.rs:406:1 clippy::must_use_candidate "this function could have a `#[must_use]` attribute" -puffin-02dd4a3/puffin/src/lib.rs:408:5 clippy::option_if_let_else "use Option::map_or instead of an if let/else" -puffin-02dd4a3/puffin/src/lib.rs:69:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -puffin-02dd4a3/puffin/src/lib.rs:73:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -puffin-02dd4a3/puffin/src/lib.rs:77:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -puffin-02dd4a3/puffin/src/merge.rs:21:1 clippy::module_name_repetitions "item name starts with its containing module's name" -puffin-02dd4a3/puffin/src/merge.rs:28:1 clippy::module_name_repetitions "item name starts with its containing module's name" -puffin-02dd4a3/puffin/src/merge.rs:28:1 clippy::must_use_candidate "this function could have a `#[must_use]` attribute" -puffin-02dd4a3/puffin/src/merge.rs:35:1 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -puffin-02dd4a3/puffin/src/merge.rs:35:1 clippy::module_name_repetitions "item name starts with its containing module's name" -puffin-02dd4a3/puffin/src/merge.rs:64:43 clippy::default_trait_access "calling `std::vec::Vec::default()` is more clear than this expression" -puffin-02dd4a3/puffin/src/merge.rs:65:54 clippy::default_trait_access "calling `std::collections::HashMap::default()` is more clear than this expression" -puffin-02dd4a3/puffin/src/merge.rs:9:1 clippy::module_name_repetitions "item name starts with its containing module's name" -quote-1.0.7/src/ext.rs:10:1 clippy::module_name_repetitions "item name ends with its containing module's name" -quote-1.0.7/src/ext.rs:7:5 clippy::doc_markdown "you should put `TokenStream` between ticks in the documentation" -quote-1.0.7/src/ident_fragment.rs:13:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -quote-1.0.7/src/ident_fragment.rs:51:31 clippy::manual_strip "stripping a prefix manually" -quote-1.0.7/src/runtime.rs:52:5 clippy::module_name_repetitions "item name ends with its containing module's name" -quote-1.0.7/src/runtime.rs:63:5 clippy::module_name_repetitions "item name ends with its containing module's name" -quote-1.0.7/src/runtime.rs:66:33 clippy::doc_markdown "you should put `DoesNotHaveIter` between ticks in the documentation" -quote-1.0.7/src/runtime.rs:80:5 clippy::module_name_repetitions "item name ends with its containing module's name" -rand-0.7.3/src/distributions/bernoulli.rs:103:20 clippy::cast_possible_truncation "casting `f64` to `u64` may truncate the value" -rand-0.7.3/src/distributions/bernoulli.rs:103:20 clippy::cast_sign_loss "casting `f64` to `u64` may lose the sign of the value" -rand-0.7.3/src/distributions/bernoulli.rs:116:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -rand-0.7.3/src/distributions/bernoulli.rs:123:21 clippy::cast_possible_truncation "casting `f64` to `u64` may truncate the value" -rand-0.7.3/src/distributions/bernoulli.rs:123:21 clippy::cast_sign_loss "casting `f64` to `u64` may lose the sign of the value" -rand-0.7.3/src/distributions/bernoulli.rs:63:26 clippy::cast_precision_loss "casting `u64` to `f64` causes a loss of precision (`u64` is 64 bits wide, but `f64`'s mantissa is only 52 bits wide)" -rand-0.7.3/src/distributions/bernoulli.rs:63:27 clippy::unseparated_literal_suffix "integer type suffix should be separated by an underscore" -rand-0.7.3/src/distributions/bernoulli.rs:67:1 clippy::module_name_repetitions "item name starts with its containing module's name" -rand-0.7.3/src/distributions/bernoulli.rs:95:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -rand-0.7.3/src/distributions/bernoulli.rs:96:13 clippy::manual_range_contains "manual `Range::contains` implementation" -rand-0.7.3/src/distributions/binomial.rs:107:23 clippy::cast_precision_loss "casting `i64` to `f64` causes a loss of precision (`i64` is 64 bits wide, but `f64`'s mantissa is only 52 bits wide)" -rand-0.7.3/src/distributions/binomial.rs:112:44 clippy::cast_precision_loss "casting `i64` to `f64` causes a loss of precision (`i64` is 64 bits wide, but `f64`'s mantissa is only 52 bits wide)" -rand-0.7.3/src/distributions/binomial.rs:116:13 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope" -rand-0.7.3/src/distributions/binomial.rs:150:28 clippy::redundant_else "redundant else block" -rand-0.7.3/src/distributions/binomial.rs:153:24 clippy::if_not_else "unnecessary boolean `not` operation" -rand-0.7.3/src/distributions/binomial.rs:158:28 clippy::redundant_else "redundant else block" -rand-0.7.3/src/distributions/binomial.rs:164:33 clippy::cast_sign_loss "casting `i64` to `u64` may lose the sign of the value" -rand-0.7.3/src/distributions/binomial.rs:166:28 clippy::redundant_else "redundant else block" -rand-0.7.3/src/distributions/binomial.rs:175:47 clippy::cast_precision_loss "casting `i64` to `f64` causes a loss of precision (`i64` is 64 bits wide, but `f64`'s mantissa is only 52 bits wide)" -rand-0.7.3/src/distributions/binomial.rs:185:38 clippy::cast_precision_loss "casting `i64` to `f64` causes a loss of precision (`i64` is 64 bits wide, but `f64`'s mantissa is only 52 bits wide)" -rand-0.7.3/src/distributions/binomial.rs:194:38 clippy::cast_precision_loss "casting `i64` to `f64` causes a loss of precision (`i64` is 64 bits wide, but `f64`'s mantissa is only 52 bits wide)" -rand-0.7.3/src/distributions/binomial.rs:202:28 clippy::redundant_else "redundant else block" -rand-0.7.3/src/distributions/binomial.rs:209:25 clippy::cast_precision_loss "casting `i64` to `f64` causes a loss of precision (`i64` is 64 bits wide, but `f64`'s mantissa is only 52 bits wide)" -rand-0.7.3/src/distributions/binomial.rs:221:26 clippy::cast_precision_loss "casting `i64` to `f64` causes a loss of precision (`i64` is 64 bits wide, but `f64`'s mantissa is only 52 bits wide)" -rand-0.7.3/src/distributions/binomial.rs:222:26 clippy::cast_precision_loss "casting `i64` to `f64` causes a loss of precision (`i64` is 64 bits wide, but `f64`'s mantissa is only 52 bits wide)" -rand-0.7.3/src/distributions/binomial.rs:223:25 clippy::cast_precision_loss "casting `i64` to `f64` causes a loss of precision (`i64` is 64 bits wide, but `f64`'s mantissa is only 52 bits wide)" -rand-0.7.3/src/distributions/binomial.rs:224:25 clippy::cast_precision_loss "casting `i64` to `f64` causes a loss of precision (`i64` is 64 bits wide, but `f64`'s mantissa is only 52 bits wide)" -rand-0.7.3/src/distributions/binomial.rs:226:17 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope" -rand-0.7.3/src/distributions/binomial.rs:233:32 clippy::cast_precision_loss "casting `i64` to `f64` causes a loss of precision (`i64` is 64 bits wide, but `f64`'s mantissa is only 52 bits wide)" -rand-0.7.3/src/distributions/binomial.rs:234:27 clippy::cast_precision_loss "casting `i64` to `f64` causes a loss of precision (`i64` is 64 bits wide, but `f64`'s mantissa is only 52 bits wide)" -rand-0.7.3/src/distributions/binomial.rs:251:22 clippy::cast_sign_loss "casting `i64` to `u64` may lose the sign of the value" -rand-0.7.3/src/distributions/binomial.rs:255:9 clippy::if_not_else "unnecessary `!=` operation" -rand-0.7.3/src/distributions/binomial.rs:35:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -rand-0.7.3/src/distributions/binomial.rs:45:17 clippy::cast_precision_loss "casting `i64` to `f64` causes a loss of precision (`i64` is 64 bits wide, but `f64`'s mantissa is only 52 bits wide)" -rand-0.7.3/src/distributions/binomial.rs:46:5 clippy::cast_possible_truncation "casting `f64` to `i64` may truncate the value" -rand-0.7.3/src/distributions/binomial.rs:50:5 clippy::too_many_lines "this function has too many lines (143/100)" -rand-0.7.3/src/distributions/binomial.rs:76:9 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope" -rand-0.7.3/src/distributions/binomial.rs:78:12 clippy::cast_precision_loss "casting `u64` to `f64` causes a loss of precision (`u64` is 64 bits wide, but `f64`'s mantissa is only 52 bits wide)" -rand-0.7.3/src/distributions/binomial.rs:81:21 clippy::cast_precision_loss "casting `u64` to `f64` causes a loss of precision (`u64` is 64 bits wide, but `f64`'s mantissa is only 52 bits wide)" -rand-0.7.3/src/distributions/binomial.rs:82:32 clippy::cast_possible_truncation "casting `u64` to `i32` may truncate the value" -rand-0.7.3/src/distributions/binomial.rs:88:26 clippy::cast_precision_loss "casting `u64` to `f64` causes a loss of precision (`u64` is 64 bits wide, but `f64`'s mantissa is only 52 bits wide)" -rand-0.7.3/src/distributions/binomial.rs:99:21 clippy::cast_precision_loss "casting `u64` to `f64` causes a loss of precision (`u64` is 64 bits wide, but `f64`'s mantissa is only 52 bits wide)" -rand-0.7.3/src/distributions/cauchy.rs:33:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -rand-0.7.3/src/distributions/dirichlet.rs:52:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -rand-0.7.3/src/distributions/dirichlet.rs:64:32 clippy::unseparated_literal_suffix "float type suffix should be separated by an underscore" -rand-0.7.3/src/distributions/dirichlet.rs:65:23 clippy::unseparated_literal_suffix "float type suffix should be separated by an underscore" -rand-0.7.3/src/distributions/exponential.rs:76:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -rand-0.7.3/src/distributions/float.rs:73:1 clippy::module_name_repetitions "item name ends with its containing module's name" -rand-0.7.3/src/distributions/gamma.rs:13:5 clippy::enum_glob_use "usage of wildcard import for enum variants" -rand-0.7.3/src/distributions/gamma.rs:14:5 clippy::enum_glob_use "usage of wildcard import for enum variants" -rand-0.7.3/src/distributions/gamma.rs:189:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -rand-0.7.3/src/distributions/gamma.rs:230:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -rand-0.7.3/src/distributions/gamma.rs:259:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -rand-0.7.3/src/distributions/gamma.rs:287:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -rand-0.7.3/src/distributions/gamma.rs:90:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -rand-0.7.3/src/distributions/integer.rs:23:9 clippy::cast_possible_truncation "casting `u32` to `u8` may truncate the value" -rand-0.7.3/src/distributions/integer.rs:30:9 clippy::cast_possible_truncation "casting `u32` to `u16` may truncate the value" -rand-0.7.3/src/distributions/integer.rs:69:9 clippy::cast_possible_truncation "casting `u64` to `usize` may truncate the value on targets with 32-bit wide pointers" -rand-0.7.3/src/distributions/mod.rs:263:5 clippy::inline_always "you have declared `#[inline(always)]` on `next`. This is usually a bad idea" -rand-0.7.3/src/distributions/normal.rs:100:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -rand-0.7.3/src/distributions/normal.rs:119:1 clippy::module_name_repetitions "item name ends with its containing module's name" -rand-0.7.3/src/distributions/normal.rs:131:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -rand-0.7.3/src/distributions/normal.rs:31:1 clippy::module_name_repetitions "item name ends with its containing module's name" -rand-0.7.3/src/distributions/normal.rs:47:25 clippy::unseparated_literal_suffix "float type suffix should be separated by an underscore" -rand-0.7.3/src/distributions/normal.rs:48:25 clippy::unseparated_literal_suffix "float type suffix should be separated by an underscore" -rand-0.7.3/src/distributions/other.rs:89:9 clippy::cast_possible_wrap "casting `u32` to `i32` may wrap around the value" -rand-0.7.3/src/distributions/pareto.rs:32:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -rand-0.7.3/src/distributions/poisson.rs:35:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -rand-0.7.3/src/distributions/poisson.rs:87:30 clippy::cast_possible_truncation "casting `f64` to `u64` may truncate the value" -rand-0.7.3/src/distributions/poisson.rs:87:30 clippy::cast_sign_loss "casting `f64` to `u64` may lose the sign of the value" -rand-0.7.3/src/distributions/triangular.rs:32:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -rand-0.7.3/src/distributions/uniform.rs:146:4 clippy::needless_doctest_main "needless `fn main` in doctest" -rand-0.7.3/src/distributions/uniform.rs:199:1 clippy::module_name_repetitions "item name ends with its containing module's name" -rand-0.7.3/src/distributions/uniform.rs:214:1 clippy::module_name_repetitions "item name starts with its containing module's name" -rand-0.7.3/src/distributions/uniform.rs:283:14 clippy::doc_markdown "you should put `SampleUniform` between ticks in the documentation" -rand-0.7.3/src/distributions/uniform.rs:283:46 clippy::doc_markdown "you should put `SampleUniform` between ticks in the documentation" -rand-0.7.3/src/distributions/uniform.rs:296:5 clippy::inline_always "you have declared `#[inline(always)]` on `borrow`. This is usually a bad idea" -rand-0.7.3/src/distributions/uniform.rs:304:5 clippy::inline_always "you have declared `#[inline(always)]` on `borrow`. This is usually a bad idea" -rand-0.7.3/src/distributions/uniform.rs:350:1 clippy::module_name_repetitions "item name starts with its containing module's name" -rand-0.7.3/src/distributions/uniform.rs:407:21 clippy::redundant_field_names "redundant field names in struct initialization" -rand-0.7.3/src/distributions/uniform.rs:407:21 clippy::redundant_field_names "redundant field names in struct initialization" -rand-0.7.3/src/distributions/uniform.rs:407:21 clippy::redundant_field_names "redundant field names in struct initialization" -rand-0.7.3/src/distributions/uniform.rs:407:21 clippy::redundant_field_names "redundant field names in struct initialization" -rand-0.7.3/src/distributions/uniform.rs:407:21 clippy::redundant_field_names "redundant field names in struct initialization" -rand-0.7.3/src/distributions/uniform.rs:407:21 clippy::redundant_field_names "redundant field names in struct initialization" -rand-0.7.3/src/distributions/uniform.rs:407:21 clippy::redundant_field_names "redundant field names in struct initialization" -rand-0.7.3/src/distributions/uniform.rs:407:21 clippy::redundant_field_names "redundant field names in struct initialization" -rand-0.7.3/src/distributions/uniform.rs:407:21 clippy::redundant_field_names "redundant field names in struct initialization" -rand-0.7.3/src/distributions/uniform.rs:407:21 clippy::redundant_field_names "redundant field names in struct initialization" -rand-0.7.3/src/distributions/uniform.rs:407:21 clippy::redundant_field_names "redundant field names in struct initialization" -rand-0.7.3/src/distributions/uniform.rs:407:21 clippy::redundant_field_names "redundant field names in struct initialization" -rand-0.7.3/src/distributions/uniform.rs:441:31 clippy::invalid_upcast_comparisons "because of the numeric bounds on `::core::u16::MAX` prior to casting, this expression is always false" -rand-0.7.3/src/distributions/uniform.rs:441:31 clippy::invalid_upcast_comparisons "because of the numeric bounds on `::core::u16::MAX` prior to casting, this expression is always false" -rand-0.7.3/src/distributions/uniform.rs:441:31 clippy::invalid_upcast_comparisons "because of the numeric bounds on `::core::u16::MAX` prior to casting, this expression is always false" -rand-0.7.3/src/distributions/uniform.rs:441:31 clippy::invalid_upcast_comparisons "because of the numeric bounds on `::core::u16::MAX` prior to casting, this expression is always false" -rand-0.7.3/src/distributions/uniform.rs:441:31 clippy::invalid_upcast_comparisons "because of the numeric bounds on `::core::u16::MAX` prior to casting, this expression is always false" -rand-0.7.3/src/distributions/uniform.rs:441:31 clippy::invalid_upcast_comparisons "because of the numeric bounds on `::core::u16::MAX` prior to casting, this expression is always false" -rand-0.7.3/src/distributions/uniform.rs:441:31 clippy::invalid_upcast_comparisons "because of the numeric bounds on `::core::u16::MAX` prior to casting, this expression is always false" -rand-0.7.3/src/distributions/uniform.rs:441:31 clippy::invalid_upcast_comparisons "because of the numeric bounds on `::core::u16::MAX` prior to casting, this expression is always false" -rand-0.7.3/src/distributions/uniform.rs:56:10 clippy::doc_markdown "you should put `SampleBorrow` between ticks in the documentation" -rand-0.7.3/src/distributions/uniform.rs:647:1 clippy::module_name_repetitions "item name starts with its containing module's name" -rand-0.7.3/src/distributions/uniform.rs:840:1 clippy::module_name_repetitions "item name starts with its containing module's name" -rand-0.7.3/src/distributions/uniform.rs:913:13 clippy::option_if_let_else "use Option::map_or_else instead of an if let/else" -rand-0.7.3/src/distributions/uniform.rs:943:54 clippy::cast_possible_truncation "casting `u64` to `u32` may truncate the value" -rand-0.7.3/src/distributions/unit_circle.rs:30:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -rand-0.7.3/src/distributions/unit_sphere.rs:24:1 clippy::module_name_repetitions "item name starts with its containing module's name" -rand-0.7.3/src/distributions/unit_sphere.rs:29:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -rand-0.7.3/src/distributions/utils.rs:218:18 clippy::upper_case_acronyms "name `FloatSIMDUtils` contains a capitalized acronym" -rand-0.7.3/src/distributions/utils.rs:247:15 clippy::wrong_self_convention "methods called `is_*` usually take self by reference or no self; consider choosing a less ambiguous name" -rand-0.7.3/src/distributions/utils.rs:248:20 clippy::wrong_self_convention "methods called `is_*` usually take self by reference or no self; consider choosing a less ambiguous name" -rand-0.7.3/src/distributions/utils.rs:249:18 clippy::wrong_self_convention "methods called `is_*` usually take self by reference or no self; consider choosing a less ambiguous name" -rand-0.7.3/src/distributions/utils.rs:253:18 clippy::upper_case_acronyms "name `FloatAsSIMD` contains a capitalized acronym" -rand-0.7.3/src/distributions/utils.rs:254:5 clippy::inline_always "you have declared `#[inline(always)]` on `lanes`. This is usually a bad idea" -rand-0.7.3/src/distributions/utils.rs:258:5 clippy::inline_always "you have declared `#[inline(always)]` on `splat`. This is usually a bad idea" -rand-0.7.3/src/distributions/utils.rs:262:5 clippy::inline_always "you have declared `#[inline(always)]` on `extract`. This is usually a bad idea" -rand-0.7.3/src/distributions/utils.rs:267:5 clippy::inline_always "you have declared `#[inline(always)]` on `replace`. This is usually a bad idea" -rand-0.7.3/src/distributions/utils.rs:274:18 clippy::upper_case_acronyms "name `BoolAsSIMD` contains a capitalized acronym" -rand-0.7.3/src/distributions/utils.rs:281:5 clippy::inline_always "you have declared `#[inline(always)]` on `any`. This is usually a bad idea" -rand-0.7.3/src/distributions/utils.rs:286:5 clippy::inline_always "you have declared `#[inline(always)]` on `all`. This is usually a bad idea" -rand-0.7.3/src/distributions/utils.rs:291:5 clippy::inline_always "you have declared `#[inline(always)]` on `none`. This is usually a bad idea" -rand-0.7.3/src/distributions/utils.rs:488:17 clippy::doc_markdown "you should put `x_i` between ticks in the documentation" -rand-0.7.3/src/distributions/utils.rs:489:50 clippy::doc_markdown "you should put `x_i` between ticks in the documentation" -rand-0.7.3/src/distributions/utils.rs:489:63 clippy::doc_markdown "you should put `f(x_i` between ticks in the documentation" -rand-0.7.3/src/distributions/utils.rs:490:40 clippy::doc_markdown "you should put `f(x_i` between ticks in the documentation" -rand-0.7.3/src/distributions/utils.rs:490:49 clippy::doc_markdown "you should put `f(x_{i+1` between ticks in the documentation" -rand-0.7.3/src/distributions/utils.rs:518:17 clippy::cast_possible_truncation "casting `u64` to `usize` may truncate the value on targets with 32-bit wide pointers" -rand-0.7.3/src/distributions/weibull.rs:29:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -rand-0.7.3/src/distributions/weighted/alias_method.rs:113:21 clippy::explicit_iter_loop "it is more concise to loop over references to containers instead of using explicit iteration methods" -rand-0.7.3/src/distributions/weighted/alias_method.rs:125:9 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope" -rand-0.7.3/src/distributions/weighted/alias_method.rs:131:9 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope" -rand-0.7.3/src/distributions/weighted/alias_method.rs:180:36 clippy::cast_possible_truncation "casting `usize` to `u32` may truncate the value on targets with 64-bit wide pointers" -rand-0.7.3/src/distributions/weighted/alias_method.rs:182:34 clippy::cast_possible_truncation "casting `usize` to `u32` may truncate the value on targets with 64-bit wide pointers" -rand-0.7.3/src/distributions/weighted/alias_method.rs:259:28 clippy::clone_on_copy "using `clone` on type `distributions::uniform::Uniform<u32>` which implements the `Copy` trait" -rand-0.7.3/src/distributions/weighted/alias_method.rs:296:9 clippy::map_clone "you are using an explicit closure for copying elements" -rand-0.7.3/src/distributions/weighted/alias_method.rs:321:9 clippy::map_clone "you are using an explicit closure for copying elements" -rand-0.7.3/src/distributions/weighted/alias_method.rs:78:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -rand-0.7.3/src/distributions/weighted/alias_method.rs:78:5 clippy::missing_panics_doc "docs for function which may panic missing `# Panics` section" -rand-0.7.3/src/distributions/weighted/alias_method.rs:78:5 clippy::too_many_lines "this function has too many lines (106/100)" -rand-0.7.3/src/distributions/weighted/alias_method.rs:85:17 clippy::cast_possible_truncation "casting `usize` to `u32` may truncate the value on targets with 64-bit wide pointers" -rand-0.7.3/src/distributions/weighted/alias_method.rs:87:31 clippy::map_unwrap_or "called `map(<f>).unwrap_or(<a>)` on an `Option` value. This can be done more directly by calling `map_or(<a>, <f>)` instead" -rand-0.7.3/src/distributions/weighted/mod.rs:100:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -rand-0.7.3/src/distributions/weighted/mod.rs:144:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -rand-0.7.3/src/distributions/weighted/mod.rs:144:5 clippy::missing_panics_doc "docs for function which may panic missing `# Panics` section" -rand-0.7.3/src/distributions/weighted/mod.rs:169:16 clippy::int_plus_one "unnecessary `>= y + 1` or `x - 1 >=`" -rand-0.7.3/src/distributions/weighted/mod.rs:386:1 clippy::module_name_repetitions "item name starts with its containing module's name" -rand-0.7.3/src/distributions/weighted/mod.rs:85:1 clippy::module_name_repetitions "item name starts with its containing module's name" -rand-0.7.3/src/lib.rs:333:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -rand-0.7.3/src/lib.rs:404:14 clippy::wrong_self_convention "methods called `to_*` usually take self by reference; consider choosing a less ambiguous name" -rand-0.7.3/src/lib.rs:552:1 clippy::must_use_candidate "this function could have a `#[must_use]` attribute" -rand-0.7.3/src/rngs/adapter/read.rs:47:1 clippy::module_name_repetitions "item name starts with its containing module's name" -rand-0.7.3/src/rngs/adapter/read.rs:89:1 clippy::module_name_repetitions "item name starts with its containing module's name" -rand-0.7.3/src/rngs/adapter/reseeding.rs:100:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -rand-0.7.3/src/rngs/adapter/reseeding.rs:112:5 clippy::inline_always "you have declared `#[inline(always)]` on `next_u32`. This is usually a bad idea" -rand-0.7.3/src/rngs/adapter/reseeding.rs:117:5 clippy::inline_always "you have declared `#[inline(always)]` on `next_u64`. This is usually a bad idea" -rand-0.7.3/src/rngs/adapter/reseeding.rs:198:13 clippy::cast_possible_wrap "casting `u64` to `i64` may wrap around the value" -rand-0.7.3/src/rngs/adapter/reseeding.rs:231:9 clippy::cast_possible_wrap "casting `usize` to `isize` may wrap around the value" -rand-0.7.3/src/rngs/adapter/reseeding.rs:249:13 clippy::let_underscore_drop "non-binding `let` on a type that implements `Drop`" -rand-0.7.3/src/rngs/adapter/reseeding.rs:27:28 clippy::doc_markdown "you should put `ChaCha` between ticks in the documentation" -rand-0.7.3/src/rngs/adapter/reseeding.rs:79:1 clippy::module_name_repetitions "item name starts with its containing module's name" -rand-0.7.3/src/rngs/entropy.rs:24:1 clippy::module_name_repetitions "item name starts with its containing module's name" -rand-0.7.3/src/rngs/entropy.rs:34:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -rand-0.7.3/src/rngs/mock.rs:36:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -rand-0.7.3/src/rngs/mock.rs:47:9 clippy::cast_possible_truncation "casting `u64` to `u32` may truncate the value" -rand-0.7.3/src/rngs/mod.rs:61:74 clippy::doc_markdown "you should put `ChaCha20` between ticks in the documentation" -rand-0.7.3/src/rngs/std.rs:25:39 clippy::doc_markdown "you should put `ChaCha` between ticks in the documentation" -rand-0.7.3/src/rngs/std.rs:32:10 clippy::doc_markdown "you should put `rand_chacha` between ticks in the documentation" -rand-0.7.3/src/rngs/std.rs:36:1 clippy::module_name_repetitions "item name starts with its containing module's name" -rand-0.7.3/src/rngs/std.rs:39:5 clippy::inline_always "you have declared `#[inline(always)]` on `next_u32`. This is usually a bad idea" -rand-0.7.3/src/rngs/std.rs:44:5 clippy::inline_always "you have declared `#[inline(always)]` on `next_u64`. This is usually a bad idea" -rand-0.7.3/src/rngs/std.rs:49:5 clippy::inline_always "you have declared `#[inline(always)]` on `fill_bytes`. This is usually a bad idea" -rand-0.7.3/src/rngs/std.rs:54:5 clippy::inline_always "you have declared `#[inline(always)]` on `try_fill_bytes`. This is usually a bad idea" -rand-0.7.3/src/rngs/std.rs:63:5 clippy::inline_always "you have declared `#[inline(always)]` on `from_seed`. This is usually a bad idea" -rand-0.7.3/src/rngs/std.rs:68:5 clippy::inline_always "you have declared `#[inline(always)]` on `from_rng`. This is usually a bad idea" -rand-0.7.3/src/rngs/thread.rs:57:1 clippy::module_name_repetitions "item name starts with its containing module's name" -rand-0.7.3/src/rngs/thread.rs:80:1 clippy::missing_panics_doc "docs for function which may panic missing `# Panics` section" -rand-0.7.3/src/rngs/thread.rs:80:1 clippy::module_name_repetitions "item name starts with its containing module's name" -rand-0.7.3/src/rngs/thread.rs:80:1 clippy::must_use_candidate "this function could have a `#[must_use]` attribute" -rand-0.7.3/src/rngs/thread.rs:81:35 clippy::redundant_closure_for_method_calls "redundant closure found" -rand-0.7.3/src/rngs/thread.rs:93:5 clippy::inline_always "you have declared `#[inline(always)]` on `next_u32`. This is usually a bad idea" -rand-0.7.3/src/rngs/thread.rs:98:5 clippy::inline_always "you have declared `#[inline(always)]` on `next_u64`. This is usually a bad idea" -rand-0.7.3/src/seq/index.rs:127:1 clippy::module_name_repetitions "item name starts with its containing module's name" -rand-0.7.3/src/seq/index.rs:139:13 clippy::enum_glob_use "usage of wildcard import for enum variants" -rand-0.7.3/src/seq/index.rs:159:1 clippy::module_name_repetitions "item name starts with its containing module's name" -rand-0.7.3/src/seq/index.rs:171:13 clippy::enum_glob_use "usage of wildcard import for enum variants" -rand-0.7.3/src/seq/index.rs:180:13 clippy::enum_glob_use "usage of wildcard import for enum variants" -rand-0.7.3/src/seq/index.rs:223:18 clippy::cast_possible_truncation "casting `usize` to `u32` may truncate the value on targets with 64-bit wide pointers" -rand-0.7.3/src/seq/index.rs:224:18 clippy::cast_possible_truncation "casting `usize` to `u32` may truncate the value on targets with 64-bit wide pointers" -rand-0.7.3/src/seq/index.rs:233:25 clippy::cast_precision_loss "casting `u32` to `f32` causes a loss of precision (`u32` is 32 bits wide, but `f32`'s mantissa is only 23 bits wide)" -rand-0.7.3/src/seq/index.rs:236:27 clippy::cast_precision_loss "casting `u32` to `f32` causes a loss of precision (`u32` is 32 bits wide, but `f32`'s mantissa is only 23 bits wide)" -rand-0.7.3/src/seq/index.rs:244:12 clippy::cast_precision_loss "casting `u32` to `f32` causes a loss of precision (`u32` is 32 bits wide, but `f32`'s mantissa is only 23 bits wide)" -rand-0.7.3/src/seq/index.rs:244:37 clippy::cast_precision_loss "casting `u32` to `f32` causes a loss of precision (`u32` is 32 bits wide, but `f32`'s mantissa is only 23 bits wide)" -rand-0.7.3/src/seq/index.rs:29:1 clippy::module_name_repetitions "item name starts with its containing module's name" -rand-0.7.3/src/seq/index.rs:39:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -rand-0.7.3/src/seq/index.rs:48:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -rand-0.7.3/src/seq/index.rs:60:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -rand-0.7.3/src/seq/index.rs:69:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -rand-0.7.3/src/seq/index.rs:78:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -rand-0.7.3/src/seq/index.rs:87:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -rand-0.7.3/src/seq/index.rs:87:5 clippy::should_implement_trait "method `into_iter` can be confused for the standard trait method `std::iter::IntoIterator::into_iter`" -rand-0.7.3/src/seq/index.rs:97:13 clippy::enum_glob_use "usage of wildcard import for enum variants" -rand-0.7.3/src/seq/mod.rs:141:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -rand-0.7.3/src/seq/mod.rs:168:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -rand-0.7.3/src/seq/mod.rs:229:4 clippy::needless_doctest_main "needless `fn main` in doctest" -rand-0.7.3/src/seq/mod.rs:292:29 clippy::cast_precision_loss "casting `usize` to `f64` causes a loss of precision on targets with 64-bit wide pointers (`usize` is 64 bits wide, but `f64`'s mantissa is only 52 bits wide)" -rand-0.7.3/src/seq/mod.rs:410:23 clippy::default_trait_access "calling `std::marker::PhantomData::default()` is more clear than this expression" -rand-0.7.3/src/seq/mod.rs:45:4 clippy::needless_doctest_main "needless `fn main` in doctest" -rand-0.7.3/src/seq/mod.rs:527:26 clippy::cast_possible_truncation "casting `usize` to `u32` may truncate the value on targets with 64-bit wide pointers" -rand_core-0.6.0/src/block.rs:117:1 clippy::module_name_repetitions "item name starts with its containing module's name" -rand_core-0.6.0/src/block.rs:153:5 clippy::inline_always "you have declared `#[inline(always)]` on `index`. This is usually a bad idea" -rand_core-0.6.0/src/block.rs:230:5 clippy::inline_always "you have declared `#[inline(always)]` on `try_fill_bytes`. This is usually a bad idea" -rand_core-0.6.0/src/block.rs:240:5 clippy::inline_always "you have declared `#[inline(always)]` on `from_seed`. This is usually a bad idea" -rand_core-0.6.0/src/block.rs:245:5 clippy::inline_always "you have declared `#[inline(always)]` on `seed_from_u64`. This is usually a bad idea" -rand_core-0.6.0/src/block.rs:250:5 clippy::inline_always "you have declared `#[inline(always)]` on `from_rng`. This is usually a bad idea" -rand_core-0.6.0/src/block.rs:280:1 clippy::module_name_repetitions "item name starts with its containing module's name" -rand_core-0.6.0/src/block.rs:319:5 clippy::inline_always "you have declared `#[inline(always)]` on `index`. This is usually a bad idea" -rand_core-0.6.0/src/block.rs:405:5 clippy::inline_always "you have declared `#[inline(always)]` on `try_fill_bytes`. This is usually a bad idea" -rand_core-0.6.0/src/block.rs:415:5 clippy::inline_always "you have declared `#[inline(always)]` on `from_seed`. This is usually a bad idea" -rand_core-0.6.0/src/block.rs:420:5 clippy::inline_always "you have declared `#[inline(always)]` on `seed_from_u64`. This is usually a bad idea" -rand_core-0.6.0/src/block.rs:425:5 clippy::inline_always "you have declared `#[inline(always)]` on `from_rng`. This is usually a bad idea" -rand_core-0.6.0/src/block.rs:67:14 clippy::doc_markdown "you should put `module][crate::block` between ticks in the documentation" -rand_core-0.6.0/src/block.rs:68:1 clippy::module_name_repetitions "item name starts with its containing module's name" -rand_core-0.6.0/src/error.rs:106:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -rand_core-0.6.0/src/error.rs:87:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -rand_core-0.6.0/src/error.rs:95:74 clippy::cast_possible_wrap "casting `u32` to `i32` may wrap around the value" -rand_core-0.6.0/src/lib.rs:179:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -rand_core-0.6.0/src/lib.rs:301:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -rand_core-0.6.0/src/lib.rs:303:26 clippy::unreadable_literal "long literal lacking separators" -rand_core-0.6.0/src/lib.rs:304:26 clippy::unreadable_literal "long literal lacking separators" -rand_core-0.6.0/src/lib.rs:313:30 clippy::cast_possible_truncation "casting `u64` to `u32` may truncate the value" -rand_core-0.6.0/src/lib.rs:314:23 clippy::cast_possible_truncation "casting `u64` to `u32` may truncate the value" -rand_core-0.6.0/src/lib.rs:346:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -rand_core-0.6.0/src/lib.rs:381:5 clippy::inline_always "you have declared `#[inline(always)]` on `next_u32`. This is usually a bad idea" -rand_core-0.6.0/src/lib.rs:386:5 clippy::inline_always "you have declared `#[inline(always)]` on `next_u64`. This is usually a bad idea" -rand_core-0.6.0/src/lib.rs:391:5 clippy::inline_always "you have declared `#[inline(always)]` on `fill_bytes`. This is usually a bad idea" -rand_core-0.6.0/src/lib.rs:396:5 clippy::inline_always "you have declared `#[inline(always)]` on `try_fill_bytes`. This is usually a bad idea" -rayon-1.5.0/src/collections/binary_heap.rs:7:5 clippy::wildcard_imports "usage of wildcard import" -rayon-1.5.0/src/collections/binary_heap.rs:8:5 clippy::wildcard_imports "usage of wildcard import" -rayon-1.5.0/src/collections/btree_map.rs:7:5 clippy::wildcard_imports "usage of wildcard import" -rayon-1.5.0/src/collections/btree_map.rs:8:5 clippy::wildcard_imports "usage of wildcard import" -rayon-1.5.0/src/collections/btree_set.rs:7:5 clippy::wildcard_imports "usage of wildcard import" -rayon-1.5.0/src/collections/btree_set.rs:8:5 clippy::wildcard_imports "usage of wildcard import" -rayon-1.5.0/src/collections/hash_map.rs:10:5 clippy::wildcard_imports "usage of wildcard import" -rayon-1.5.0/src/collections/hash_map.rs:9:5 clippy::wildcard_imports "usage of wildcard import" -rayon-1.5.0/src/collections/hash_set.rs:10:5 clippy::wildcard_imports "usage of wildcard import" -rayon-1.5.0/src/collections/hash_set.rs:9:5 clippy::wildcard_imports "usage of wildcard import" -rayon-1.5.0/src/collections/linked_list.rs:7:5 clippy::wildcard_imports "usage of wildcard import" -rayon-1.5.0/src/collections/linked_list.rs:8:5 clippy::wildcard_imports "usage of wildcard import" -rayon-1.5.0/src/collections/mod.rs:59:32 clippy::mem_replace_with_default "replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take`" -rayon-1.5.0/src/collections/vec_deque.rs:8:5 clippy::wildcard_imports "usage of wildcard import" -rayon-1.5.0/src/collections/vec_deque.rs:9:5 clippy::wildcard_imports "usage of wildcard import" -rayon-1.5.0/src/compile_fail/cannot_collect_filtermap_data.rs:2:1 clippy::needless_doctest_main "needless `fn main` in doctest" -rayon-1.5.0/src/compile_fail/cannot_zip_filtered_data.rs:2:1 clippy::needless_doctest_main "needless `fn main` in doctest" -rayon-1.5.0/src/compile_fail/cell_par_iter.rs:2:1 clippy::needless_doctest_main "needless `fn main` in doctest" -rayon-1.5.0/src/compile_fail/no_send_par_iter.rs:25:1 clippy::needless_doctest_main "needless `fn main` in doctest" -rayon-1.5.0/src/compile_fail/no_send_par_iter.rs:46:1 clippy::needless_doctest_main "needless `fn main` in doctest" -rayon-1.5.0/src/compile_fail/no_send_par_iter.rs:4:1 clippy::needless_doctest_main "needless `fn main` in doctest" -rayon-1.5.0/src/compile_fail/rc_par_iter.rs:2:1 clippy::needless_doctest_main "needless `fn main` in doctest" -rayon-1.5.0/src/iter/chain.rs:103:9 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope" -rayon-1.5.0/src/iter/chain.rs:122:9 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope" -rayon-1.5.0/src/iter/chain.rs:128:9 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope" -rayon-1.5.0/src/iter/chain.rs:1:5 clippy::wildcard_imports "usage of wildcard import" -rayon-1.5.0/src/iter/chain.rs:221:36 clippy::doc_markdown "you should put `ExactSizeIterator` between ticks in the documentation" -rayon-1.5.0/src/iter/chain.rs:2:5 clippy::wildcard_imports "usage of wildcard import" -rayon-1.5.0/src/iter/chain.rs:51:38 clippy::option_if_let_else "use Option::map_or_else instead of an if let/else" -rayon-1.5.0/src/iter/chain.rs:58:14 clippy::shadow_unrelated "`a` is being shadowed" -rayon-1.5.0/src/iter/chain.rs:58:17 clippy::shadow_unrelated "`b` is being shadowed" -rayon-1.5.0/src/iter/chain.rs:78:14 clippy::shadow_unrelated "`a` is being shadowed" -rayon-1.5.0/src/iter/chain.rs:78:17 clippy::shadow_unrelated "`b` is being shadowed" -rayon-1.5.0/src/iter/chain.rs:97:9 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope" -rayon-1.5.0/src/iter/chunks.rs:29:9 clippy::inconsistent_struct_constructor "inconsistent struct constructor" -rayon-1.5.0/src/iter/chunks.rs:3:5 clippy::wildcard_imports "usage of wildcard import" -rayon-1.5.0/src/iter/chunks.rs:4:5 clippy::wildcard_imports "usage of wildcard import" -rayon-1.5.0/src/iter/chunks.rs:77:9 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope" -rayon-1.5.0/src/iter/chunks.rs:83:9 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope" -rayon-1.5.0/src/iter/cloned.rs:1:5 clippy::wildcard_imports "usage of wildcard import" -rayon-1.5.0/src/iter/cloned.rs:2:5 clippy::wildcard_imports "usage of wildcard import" -rayon-1.5.0/src/iter/cloned.rs:71:9 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope" -rayon-1.5.0/src/iter/cloned.rs:75:9 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope" -rayon-1.5.0/src/iter/collect/consumer.rs:141:5 clippy::doc_markdown "you should put `CollectReducer` between ticks in the documentation" -rayon-1.5.0/src/iter/collect/consumer.rs:1:5 clippy::wildcard_imports "usage of wildcard import" -rayon-1.5.0/src/iter/collect/consumer.rs:28:5 clippy::doc_markdown "you should put `CollectResult` between ticks in the documentation" -rayon-1.5.0/src/iter/collect/consumer.rs:36:37 clippy::mut_mut "generally you want to avoid `&mut &mut _` if possible" -rayon-1.5.0/src/iter/collect/consumer.rs:36:37 clippy::mut_mut "generally you want to avoid `&mut &mut _` if possible" -rayon-1.5.0/src/iter/collect/mod.rs:154:9 clippy::single_match_else "you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let`" -rayon-1.5.0/src/iter/copied.rs:1:5 clippy::wildcard_imports "usage of wildcard import" -rayon-1.5.0/src/iter/copied.rs:2:5 clippy::wildcard_imports "usage of wildcard import" -rayon-1.5.0/src/iter/copied.rs:71:9 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope" -rayon-1.5.0/src/iter/copied.rs:75:9 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope" -rayon-1.5.0/src/iter/empty.rs:1:5 clippy::wildcard_imports "usage of wildcard import" -rayon-1.5.0/src/iter/empty.rs:24:1 clippy::must_use_candidate "this function could have a `#[must_use]` attribute" -rayon-1.5.0/src/iter/empty.rs:2:5 clippy::wildcard_imports "usage of wildcard import" -rayon-1.5.0/src/iter/enumerate.rs:1:5 clippy::wildcard_imports "usage of wildcard import" -rayon-1.5.0/src/iter/enumerate.rs:2:5 clippy::wildcard_imports "usage of wildcard import" -rayon-1.5.0/src/iter/enumerate.rs:64:9 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope" -rayon-1.5.0/src/iter/enumerate.rs:68:9 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope" -rayon-1.5.0/src/iter/extend.rs:143:63 clippy::linkedlist "I see you're using a LinkedList! Perhaps you meant some other data structure?" -rayon-1.5.0/src/iter/extend.rs:182:57 clippy::linkedlist "I see you're using a LinkedList! Perhaps you meant some other data structure?" -rayon-1.5.0/src/iter/extend.rs:218:32 clippy::linkedlist "I see you're using a LinkedList! Perhaps you meant some other data structure?" -rayon-1.5.0/src/iter/extend.rs:218:59 clippy::linkedlist "I see you're using a LinkedList! Perhaps you meant some other data structure?" -rayon-1.5.0/src/iter/extend.rs:25:42 clippy::linkedlist "I see you're using a LinkedList! Perhaps you meant some other data structure?" -rayon-1.5.0/src/iter/extend.rs:287:62 clippy::linkedlist "I see you're using a LinkedList! Perhaps you meant some other data structure?" -rayon-1.5.0/src/iter/extend.rs:322:56 clippy::linkedlist "I see you're using a LinkedList! Perhaps you meant some other data structure?" -rayon-1.5.0/src/iter/extend.rs:41:27 clippy::linkedlist "I see you're using a LinkedList! Perhaps you meant some other data structure?" -rayon-1.5.0/src/iter/extend.rs:47:30 clippy::linkedlist "I see you're using a LinkedList! Perhaps you meant some other data structure?" -rayon-1.5.0/src/iter/extend.rs:47:56 clippy::linkedlist "I see you're using a LinkedList! Perhaps you meant some other data structure?" -rayon-1.5.0/src/iter/extend.rs:47:74 clippy::linkedlist "I see you're using a LinkedList! Perhaps you meant some other data structure?" -rayon-1.5.0/src/iter/extend.rs:53:29 clippy::linkedlist "I see you're using a LinkedList! Perhaps you meant some other data structure?" -rayon-1.5.0/src/iter/extend.rs:57:36 clippy::linkedlist "I see you're using a LinkedList! Perhaps you meant some other data structure?" -rayon-1.5.0/src/iter/extend.rs:59:61 clippy::linkedlist "I see you're using a LinkedList! Perhaps you meant some other data structure?" -rayon-1.5.0/src/iter/filter.rs:1:5 clippy::wildcard_imports "usage of wildcard import" -rayon-1.5.0/src/iter/filter.rs:2:5 clippy::wildcard_imports "usage of wildcard import" -rayon-1.5.0/src/iter/filter_map.rs:123:9 clippy::option_if_let_else "use Option::map_or instead of an if let/else" -rayon-1.5.0/src/iter/filter_map.rs:1:5 clippy::wildcard_imports "usage of wildcard import" -rayon-1.5.0/src/iter/filter_map.rs:2:5 clippy::wildcard_imports "usage of wildcard import" -rayon-1.5.0/src/iter/find.rs:1:5 clippy::wildcard_imports "usage of wildcard import" -rayon-1.5.0/src/iter/find.rs:2:5 clippy::wildcard_imports "usage of wildcard import" -rayon-1.5.0/src/iter/find_first_last/mod.rs:1:5 clippy::wildcard_imports "usage of wildcard import" -rayon-1.5.0/src/iter/find_first_last/mod.rs:2:5 clippy::wildcard_imports "usage of wildcard import" -rayon-1.5.0/src/iter/find_first_last/mod.rs:32:67 clippy::doc_markdown "you should put `MatchPosition` between ticks in the documentation" -rayon-1.5.0/src/iter/flat_map.rs:1:5 clippy::wildcard_imports "usage of wildcard import" -rayon-1.5.0/src/iter/flat_map.rs:2:5 clippy::wildcard_imports "usage of wildcard import" -rayon-1.5.0/src/iter/flat_map_iter.rs:1:5 clippy::wildcard_imports "usage of wildcard import" -rayon-1.5.0/src/iter/flat_map_iter.rs:2:5 clippy::wildcard_imports "usage of wildcard import" -rayon-1.5.0/src/iter/flatten.rs:1:5 clippy::wildcard_imports "usage of wildcard import" -rayon-1.5.0/src/iter/flatten.rs:2:5 clippy::wildcard_imports "usage of wildcard import" -rayon-1.5.0/src/iter/flatten_iter.rs:1:5 clippy::wildcard_imports "usage of wildcard import" -rayon-1.5.0/src/iter/flatten_iter.rs:2:5 clippy::wildcard_imports "usage of wildcard import" -rayon-1.5.0/src/iter/fold.rs:158:13 clippy::similar_names "binding's name is too similar to existing binding" -rayon-1.5.0/src/iter/fold.rs:1:5 clippy::wildcard_imports "usage of wildcard import" -rayon-1.5.0/src/iter/fold.rs:204:1 clippy::module_name_repetitions "item name starts with its containing module's name" -rayon-1.5.0/src/iter/fold.rs:2:5 clippy::wildcard_imports "usage of wildcard import" -rayon-1.5.0/src/iter/for_each.rs:1:5 clippy::wildcard_imports "usage of wildcard import" -rayon-1.5.0/src/iter/for_each.rs:2:5 clippy::wildcard_imports "usage of wildcard import" -rayon-1.5.0/src/iter/inspect.rs:1:5 clippy::wildcard_imports "usage of wildcard import" -rayon-1.5.0/src/iter/inspect.rs:2:5 clippy::wildcard_imports "usage of wildcard import" -rayon-1.5.0/src/iter/inspect.rs:83:9 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope" -rayon-1.5.0/src/iter/inspect.rs:88:9 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope" -rayon-1.5.0/src/iter/interleave.rs:111:9 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope" -rayon-1.5.0/src/iter/interleave.rs:119:9 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope" -rayon-1.5.0/src/iter/interleave.rs:195:30 clippy::doc_markdown "you should put `self.i_len` between ticks in the documentation" -rayon-1.5.0/src/iter/interleave.rs:195:43 clippy::doc_markdown "you should put `self.j_len` between ticks in the documentation" -rayon-1.5.0/src/iter/interleave.rs:199:23 clippy::doc_markdown "you should put `self.i_len` between ticks in the documentation" -rayon-1.5.0/src/iter/interleave.rs:1:5 clippy::wildcard_imports "usage of wildcard import" -rayon-1.5.0/src/iter/interleave.rs:200:23 clippy::doc_markdown "you should put `self.j_len` between ticks in the documentation" -rayon-1.5.0/src/iter/interleave.rs:249:41 clippy::doc_markdown "you should put `DoubleEndedIterator` between ticks in the documentation" -rayon-1.5.0/src/iter/interleave.rs:250:5 clippy::doc_markdown "you should put `ExactSizeIterator` between ticks in the documentation" -rayon-1.5.0/src/iter/interleave.rs:263:33 clippy::doc_markdown "you should put `InterleaveSeq` between ticks in the documentation" -rayon-1.5.0/src/iter/interleave.rs:280:17 clippy::match_wildcard_for_single_variants "wildcard match will miss any future added variants" -rayon-1.5.0/src/iter/interleave.rs:285:17 clippy::match_wildcard_for_single_variants "wildcard match will miss any future added variants" -rayon-1.5.0/src/iter/interleave.rs:2:5 clippy::wildcard_imports "usage of wildcard import" -rayon-1.5.0/src/iter/interleave.rs:313:9 clippy::comparison_chain "`if` chain can be rewritten with `match`" -rayon-1.5.0/src/iter/interleave.rs:82:9 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope" -rayon-1.5.0/src/iter/interleave.rs:90:9 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope" -rayon-1.5.0/src/iter/interleave_shortest.rs:1:5 clippy::wildcard_imports "usage of wildcard import" -rayon-1.5.0/src/iter/intersperse.rs:1:5 clippy::wildcard_imports "usage of wildcard import" -rayon-1.5.0/src/iter/intersperse.rs:2:5 clippy::wildcard_imports "usage of wildcard import" -rayon-1.5.0/src/iter/intersperse.rs:90:9 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope" -rayon-1.5.0/src/iter/intersperse.rs:96:9 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope" -rayon-1.5.0/src/iter/len.rs:12:1 clippy::module_name_repetitions "item name ends with its containing module's name" -rayon-1.5.0/src/iter/len.rs:146:1 clippy::module_name_repetitions "item name ends with its containing module's name" -rayon-1.5.0/src/iter/len.rs:1:5 clippy::wildcard_imports "usage of wildcard import" -rayon-1.5.0/src/iter/len.rs:200:9 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope" -rayon-1.5.0/src/iter/len.rs:205:9 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope" -rayon-1.5.0/src/iter/len.rs:2:5 clippy::wildcard_imports "usage of wildcard import" -rayon-1.5.0/src/iter/len.rs:66:9 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope" -rayon-1.5.0/src/iter/len.rs:71:9 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope" -rayon-1.5.0/src/iter/map.rs:1:5 clippy::wildcard_imports "usage of wildcard import" -rayon-1.5.0/src/iter/map.rs:2:5 clippy::wildcard_imports "usage of wildcard import" -rayon-1.5.0/src/iter/map.rs:84:9 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope" -rayon-1.5.0/src/iter/map.rs:89:9 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope" -rayon-1.5.0/src/iter/map_with.rs:1:5 clippy::wildcard_imports "usage of wildcard import" -rayon-1.5.0/src/iter/map_with.rs:2:5 clippy::wildcard_imports "usage of wildcard import" -rayon-1.5.0/src/iter/map_with.rs:419:9 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope" -rayon-1.5.0/src/iter/map_with.rs:425:9 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope" -rayon-1.5.0/src/iter/map_with.rs:90:9 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope" -rayon-1.5.0/src/iter/map_with.rs:96:9 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope" -rayon-1.5.0/src/iter/mod.rs:1874:24 clippy::trivially_copy_pass_by_ref "this argument (1 byte) is passed by reference, but would be more efficient if passed by value (limit: 8 byte)" -rayon-1.5.0/src/iter/mod.rs:2171:1 clippy::len_without_is_empty "trait `IndexedParallelIterator` has a `len` method but no (possibly inherited) `is_empty` method" -rayon-1.5.0/src/iter/mod.rs:2371:26 clippy::trivially_copy_pass_by_ref "this argument (1 byte) is passed by reference, but would be more efficient if passed by value (limit: 8 byte)" -rayon-1.5.0/src/iter/mod.rs:2411:26 clippy::trivially_copy_pass_by_ref "this argument (1 byte) is passed by reference, but would be more efficient if passed by value (limit: 8 byte)" -rayon-1.5.0/src/iter/mod.rs:82:5 clippy::wildcard_imports "usage of wildcard import" -rayon-1.5.0/src/iter/multizip.rs:1:5 clippy::wildcard_imports "usage of wildcard import" -rayon-1.5.0/src/iter/multizip.rs:2:5 clippy::wildcard_imports "usage of wildcard import" -rayon-1.5.0/src/iter/noop.rs:1:5 clippy::wildcard_imports "usage of wildcard import" -rayon-1.5.0/src/iter/once.rs:1:5 clippy::wildcard_imports "usage of wildcard import" -rayon-1.5.0/src/iter/once.rs:2:5 clippy::wildcard_imports "usage of wildcard import" -rayon-1.5.0/src/iter/panic_fuse.rs:102:9 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope" -rayon-1.5.0/src/iter/panic_fuse.rs:1:5 clippy::wildcard_imports "usage of wildcard import" -rayon-1.5.0/src/iter/panic_fuse.rs:2:5 clippy::wildcard_imports "usage of wildcard import" -rayon-1.5.0/src/iter/panic_fuse.rs:98:9 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope" -rayon-1.5.0/src/iter/par_bridge.rs:136:28 clippy::redundant_else "redundant else block" -rayon-1.5.0/src/iter/par_bridge.rs:163:28 clippy::redundant_else "redundant else block" -rayon-1.5.0/src/iter/plumbing/mod.rs:216:58 clippy::doc_markdown "you should put `find_first` between ticks in the documentation" -rayon-1.5.0/src/iter/plumbing/mod.rs:359:5 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope" -rayon-1.5.0/src/iter/plumbing/mod.rs:364:5 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope" -rayon-1.5.0/src/iter/plumbing/mod.rs:399:5 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope" -rayon-1.5.0/src/iter/plumbing/mod.rs:53:19 clippy::doc_markdown "you should put `DoubleEndedIterator` between ticks in the documentation" -rayon-1.5.0/src/iter/plumbing/mod.rs:53:43 clippy::doc_markdown "you should put `ExactSizeIterator` between ticks in the documentation" -rayon-1.5.0/src/iter/plumbing/mod.rs:54:31 clippy::doc_markdown "you should put `IntoIterator` between ticks in the documentation" -rayon-1.5.0/src/iter/plumbing/mod.rs:55:5 clippy::doc_markdown "you should put `IntoIterator` between ticks in the documentation" -rayon-1.5.0/src/iter/positions.rs:1:5 clippy::wildcard_imports "usage of wildcard import" -rayon-1.5.0/src/iter/positions.rs:2:5 clippy::wildcard_imports "usage of wildcard import" -rayon-1.5.0/src/iter/product.rs:1:5 clippy::wildcard_imports "usage of wildcard import" -rayon-1.5.0/src/iter/reduce.rs:1:5 clippy::wildcard_imports "usage of wildcard import" -rayon-1.5.0/src/iter/repeat.rs:103:1 clippy::module_name_repetitions "item name starts with its containing module's name" -rayon-1.5.0/src/iter/repeat.rs:1:5 clippy::wildcard_imports "usage of wildcard import" -rayon-1.5.0/src/iter/repeat.rs:2:5 clippy::wildcard_imports "usage of wildcard import" -rayon-1.5.0/src/iter/rev.rs:1:5 clippy::wildcard_imports "usage of wildcard import" -rayon-1.5.0/src/iter/rev.rs:2:5 clippy::wildcard_imports "usage of wildcard import" -rayon-1.5.0/src/iter/rev.rs:63:9 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope" -rayon-1.5.0/src/iter/rev.rs:68:9 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope" -rayon-1.5.0/src/iter/skip.rs:2:5 clippy::wildcard_imports "usage of wildcard import" -rayon-1.5.0/src/iter/skip.rs:3:5 clippy::wildcard_imports "usage of wildcard import" -rayon-1.5.0/src/iter/skip.rs:68:9 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope" -rayon-1.5.0/src/iter/skip.rs:73:9 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope" -rayon-1.5.0/src/iter/splitter.rs:1:5 clippy::wildcard_imports "usage of wildcard import" -rayon-1.5.0/src/iter/splitter.rs:2:5 clippy::wildcard_imports "usage of wildcard import" -rayon-1.5.0/src/iter/step_by.rs:4:5 clippy::wildcard_imports "usage of wildcard import" -rayon-1.5.0/src/iter/step_by.rs:5:5 clippy::wildcard_imports "usage of wildcard import" -rayon-1.5.0/src/iter/step_by.rs:73:9 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope" -rayon-1.5.0/src/iter/step_by.rs:79:9 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope" -rayon-1.5.0/src/iter/sum.rs:1:5 clippy::wildcard_imports "usage of wildcard import" -rayon-1.5.0/src/iter/take.rs:1:5 clippy::wildcard_imports "usage of wildcard import" -rayon-1.5.0/src/iter/take.rs:2:5 clippy::wildcard_imports "usage of wildcard import" -rayon-1.5.0/src/iter/take.rs:67:9 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope" -rayon-1.5.0/src/iter/take.rs:72:9 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope" -rayon-1.5.0/src/iter/try_fold.rs:190:1 clippy::module_name_repetitions "item name starts with its containing module's name" -rayon-1.5.0/src/iter/try_fold.rs:1:5 clippy::wildcard_imports "usage of wildcard import" -rayon-1.5.0/src/iter/try_fold.rs:2:5 clippy::wildcard_imports "usage of wildcard import" -rayon-1.5.0/src/iter/try_reduce.rs:1:5 clippy::wildcard_imports "usage of wildcard import" -rayon-1.5.0/src/iter/try_reduce_with.rs:1:5 clippy::wildcard_imports "usage of wildcard import" -rayon-1.5.0/src/iter/unzip.rs:1:5 clippy::wildcard_imports "usage of wildcard import" -rayon-1.5.0/src/iter/unzip.rs:2:5 clippy::wildcard_imports "usage of wildcard import" -rayon-1.5.0/src/iter/update.rs:1:5 clippy::wildcard_imports "usage of wildcard import" -rayon-1.5.0/src/iter/update.rs:2:5 clippy::wildcard_imports "usage of wildcard import" -rayon-1.5.0/src/iter/update.rs:82:9 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope" -rayon-1.5.0/src/iter/update.rs:87:9 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope" -rayon-1.5.0/src/iter/while_some.rs:130:22 clippy::single_match_else "you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let`" -rayon-1.5.0/src/iter/while_some.rs:1:5 clippy::wildcard_imports "usage of wildcard import" -rayon-1.5.0/src/iter/while_some.rs:2:5 clippy::wildcard_imports "usage of wildcard import" -rayon-1.5.0/src/iter/zip.rs:102:9 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope" -rayon-1.5.0/src/iter/zip.rs:1:5 clippy::wildcard_imports "usage of wildcard import" -rayon-1.5.0/src/iter/zip.rs:2:5 clippy::wildcard_imports "usage of wildcard import" -rayon-1.5.0/src/iter/zip.rs:74:9 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope" -rayon-1.5.0/src/iter/zip.rs:79:9 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope" -rayon-1.5.0/src/iter/zip.rs:97:9 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope" -rayon-1.5.0/src/iter/zip_eq.rs:1:5 clippy::wildcard_imports "usage of wildcard import" -rayon-1.5.0/src/iter/zip_eq.rs:2:5 clippy::wildcard_imports "usage of wildcard import" -rayon-1.5.0/src/option.rs:8:5 clippy::wildcard_imports "usage of wildcard import" -rayon-1.5.0/src/option.rs:9:5 clippy::wildcard_imports "usage of wildcard import" -rayon-1.5.0/src/par_either.rs:1:5 clippy::wildcard_imports "usage of wildcard import" -rayon-1.5.0/src/par_either.rs:3:5 clippy::wildcard_imports "usage of wildcard import" -rayon-1.5.0/src/private.rs:9:1 clippy::module_name_repetitions "item name starts with its containing module's name" -rayon-1.5.0/src/range.rs:19:5 clippy::wildcard_imports "usage of wildcard import" -rayon-1.5.0/src/range.rs:20:5 clippy::wildcard_imports "usage of wildcard import" -rayon-1.5.0/src/range_inclusive.rs:194:9 clippy::range_plus_one "an inclusive range would be more readable" -rayon-1.5.0/src/range_inclusive.rs:194:9 clippy::range_plus_one "an inclusive range would be more readable" -rayon-1.5.0/src/range_inclusive.rs:19:5 clippy::wildcard_imports "usage of wildcard import" -rayon-1.5.0/src/range_inclusive.rs:209:9 clippy::range_plus_one "an inclusive range would be more readable" -rayon-1.5.0/src/range_inclusive.rs:209:9 clippy::range_plus_one "an inclusive range would be more readable" -rayon-1.5.0/src/range_inclusive.rs:20:5 clippy::wildcard_imports "usage of wildcard import" -rayon-1.5.0/src/range_inclusive.rs:231:9 clippy::range_plus_one "an inclusive range would be more readable" -rayon-1.5.0/src/range_inclusive.rs:231:9 clippy::range_plus_one "an inclusive range would be more readable" -rayon-1.5.0/src/result.rs:8:5 clippy::wildcard_imports "usage of wildcard import" -rayon-1.5.0/src/result.rs:9:5 clippy::wildcard_imports "usage of wildcard import" -rayon-1.5.0/src/slice/mergesort.rs:102:5 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope" -rayon-1.5.0/src/slice/mergesort.rs:109:5 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope" -rayon-1.5.0/src/slice/mergesort.rs:114:5 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope" -rayon-1.5.0/src/slice/mergesort.rs:211:5 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope" -rayon-1.5.0/src/slice/mergesort.rs:217:5 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope" -rayon-1.5.0/src/slice/mergesort.rs:251:5 clippy::doc_markdown "you should put `TimSort` between ticks in the documentation" -rayon-1.5.0/src/slice/mergesort.rs:252:5 clippy::doc_markdown "you should put bare URLs between `<`/`>` or make a proper Markdown link" -rayon-1.5.0/src/slice/mergesort.rs:286:59 clippy::doc_markdown "you should put `TimSort` between ticks in the documentation" -rayon-1.5.0/src/slice/mergesort.rs:333:24 clippy::redundant_else "redundant else block" -rayon-1.5.0/src/slice/mergesort.rs:513:5 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope" -rayon-1.5.0/src/slice/mergesort.rs:521:5 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope" -rayon-1.5.0/src/slice/mergesort.rs:7:5 clippy::wildcard_imports "usage of wildcard import" -rayon-1.5.0/src/slice/mergesort.rs:98:5 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope" -rayon-1.5.0/src/slice/mod.rs:15:5 clippy::wildcard_imports "usage of wildcard import" -rayon-1.5.0/src/slice/mod.rs:16:5 clippy::wildcard_imports "usage of wildcard import" -rayon-1.5.0/src/slice/mod.rs:17:5 clippy::wildcard_imports "usage of wildcard import" -rayon-1.5.0/src/slice/mod.rs:25:1 clippy::module_name_repetitions "item name ends with its containing module's name" -rayon-1.5.0/src/slice/mod.rs:657:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -rayon-1.5.0/src/slice/mod.rs:971:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -rayon-1.5.0/src/slice/quicksort.rs:230:36 clippy::doc_markdown "you should put `BlockQuicksort` between ticks in the documentation" -rayon-1.5.0/src/slice/quicksort.rs:233:1 clippy::too_many_lines "this function has too many lines (117/100)" -rayon-1.5.0/src/slice/quicksort.rs:258:26 clippy::unseparated_literal_suffix "integer type suffix should be separated by an underscore" -rayon-1.5.0/src/slice/quicksort.rs:265:26 clippy::unseparated_literal_suffix "integer type suffix should be separated by an underscore" -rayon-1.5.0/src/slice/quicksort.rs:268:5 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope" -rayon-1.5.0/src/slice/quicksort.rs:308:30 clippy::cast_possible_truncation "casting `usize` to `u8` may truncate the value" -rayon-1.5.0/src/slice/quicksort.rs:325:30 clippy::cast_possible_truncation "casting `usize` to `u8` may truncate the value" -rayon-1.5.0/src/slice/quicksort.rs:393:36 clippy::cast_possible_wrap "casting `u8` to `isize` may wrap around the value on targets with 32-bit wide pointers" -rayon-1.5.0/src/slice/quicksort.rs:405:40 clippy::cast_possible_wrap "casting `u8` to `isize` may wrap around the value on targets with 32-bit wide pointers" -rayon-1.5.0/src/slice/quicksort.rs:430:14 clippy::shadow_unrelated "`pivot` is being shadowed" -rayon-1.5.0/src/slice/quicksort.rs:439:13 clippy::shadow_unrelated "`pivot` is being shadowed" -rayon-1.5.0/src/slice/quicksort.rs:482:10 clippy::shadow_unrelated "`pivot` is being shadowed" -rayon-1.5.0/src/slice/quicksort.rs:491:9 clippy::shadow_unrelated "`pivot` is being shadowed" -rayon-1.5.0/src/slice/quicksort.rs:534:26 clippy::cast_possible_truncation "casting `usize` to `u32` may truncate the value on targets with 64-bit wide pointers" -rayon-1.5.0/src/slice/quicksort.rs:545:17 clippy::cast_possible_truncation "casting `u64` to `usize` may truncate the value on targets with 32-bit wide pointers" -rayon-1.5.0/src/slice/quicksort.rs:588:17 clippy::identity_op "the operation is ineffective. Consider reducing it to `len / 4`" -rayon-1.5.0/src/slice/quicksort.rs:716:14 clippy::shadow_unrelated "`pivot` is being shadowed" -rayon-1.5.0/src/split_producer.rs:56:16 clippy::option_if_let_else "use Option::map_or_else instead of an if let/else" -rayon-1.5.0/src/split_producer.rs:92:9 clippy::option_if_let_else "use Option::map_or instead of an if let/else" -rayon-1.5.0/src/str.rs:16:5 clippy::wildcard_imports "usage of wildcard import" -rayon-1.5.0/src/str.rs:17:5 clippy::wildcard_imports "usage of wildcard import" -rayon-1.5.0/src/str.rs:18:5 clippy::wildcard_imports "usage of wildcard import" -rayon-1.5.0/src/str.rs:25:5 clippy::cast_possible_wrap "casting `u8` to `i8` may wrap around the value" -rayon-1.5.0/src/str.rs:715:9 clippy::manual_strip "stripping a suffix manually" -rayon-1.5.0/src/string.rs:5:5 clippy::wildcard_imports "usage of wildcard import" -rayon-1.5.0/src/vec.rs:137:12 clippy::len_zero "length comparison to zero" -rayon-1.5.0/src/vec.rs:8:5 clippy::wildcard_imports "usage of wildcard import" -rayon-1.5.0/src/vec.rs:9:5 clippy::wildcard_imports "usage of wildcard import" -regex-1.3.2/src/backtrack.rs:100:13 clippy::redundant_field_names "redundant field names in struct initialization" -regex-1.3.2/src/backtrack.rs:133:17 clippy::same_item_push "it looks like the same item is being pushed into this Vec" -regex-1.3.2/src/backtrack.rs:145:20 clippy::if_not_else "unnecessary boolean `not` operation" -regex-1.3.2/src/backtrack.rs:199:13 clippy::enum_glob_use "usage of wildcard import for enum variants" -regex-1.3.2/src/backtrack.rs:223:29 clippy::redundant_field_names "redundant field names in struct initialization" -regex-1.3.2/src/backtrack.rs:230:66 clippy::redundant_field_names "redundant field names in struct initialization" -regex-1.3.2/src/backtrack.rs:284:21 clippy::cast_lossless "casting `u32` to `u64` may become silently lossy if you later change the type" -regex-1.3.2/src/backtrack.rs:287:5 clippy::cast_possible_truncation "casting `usize` to `u32` may truncate the value on targets with 64-bit wide pointers" -regex-1.3.2/src/backtrack.rs:97:13 clippy::redundant_field_names "redundant field names in struct initialization" -regex-1.3.2/src/backtrack.rs:98:13 clippy::redundant_field_names "redundant field names in struct initialization" -regex-1.3.2/src/backtrack.rs:99:13 clippy::redundant_field_names "redundant field names in struct initialization" -regex-1.3.2/src/compile.rs:1005:32 clippy::unreadable_literal "long literal lacking separators" -regex-1.3.2/src/compile.rs:1006:21 clippy::unreadable_literal "long literal lacking separators" -regex-1.3.2/src/compile.rs:1008:18 clippy::cast_lossless "casting `u8` to `u64` may become silently lossy if you later change the type" -regex-1.3.2/src/compile.rs:1009:18 clippy::cast_lossless "casting `u8` to `u64` may become silently lossy if you later change the type" -regex-1.3.2/src/compile.rs:1010:9 clippy::cast_possible_truncation "casting `u64` to `usize` may truncate the value on targets with 32-bit wide pointers" -regex-1.3.2/src/compile.rs:102:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -regex-1.3.2/src/compile.rs:1037:37 clippy::cast_possible_truncation "casting `u16` to `u8` may truncate the value" -regex-1.3.2/src/compile.rs:1037:55 clippy::cast_possible_truncation "casting `u16` to `u8` may truncate the value" -regex-1.3.2/src/compile.rs:1040:28 clippy::cast_possible_truncation "casting `u16` to `u8` may truncate the value" -regex-1.3.2/src/compile.rs:1040:38 clippy::cast_possible_truncation "casting `u16` to `u8` may truncate the value" -regex-1.3.2/src/compile.rs:1051:25 clippy::unseparated_literal_suffix "integer type suffix should be separated by an underscore" -regex-1.3.2/src/compile.rs:1071:8 clippy::cast_lossless "casting `u32` to `u64` may become silently lossy if you later change the type" -regex-1.3.2/src/compile.rs:112:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -regex-1.3.2/src/compile.rs:154:30 clippy::redundant_closure_for_method_calls "redundant closure found" -regex-1.3.2/src/compile.rs:156:30 clippy::redundant_closure_for_method_calls "redundant closure found" -regex-1.3.2/src/compile.rs:185:5 clippy::unnecessary_wraps "this function's return value is unnecessarily wrapped by `Result`" -regex-1.3.2/src/compile.rs:187:40 clippy::redundant_closure_for_method_calls "redundant closure found" -regex-1.3.2/src/compile.rs:201:53 clippy::doc_markdown "you should put `MaybeInsts` between ticks in the documentation" -regex-1.3.2/src/compile.rs:241:63 clippy::doc_markdown "you should put `c_concat` between ticks in the documentation" -regex-1.3.2/src/compile.rs:245:5 clippy::too_many_lines "this function has too many lines (111/100)" -regex-1.3.2/src/compile.rs:247:13 clippy::enum_glob_use "usage of wildcard import for enum variants" -regex-1.3.2/src/compile.rs:373:24 clippy::redundant_field_names "redundant field names in struct initialization" -regex-1.3.2/src/compile.rs:373:36 clippy::redundant_field_names "redundant field names in struct initialization" -regex-1.3.2/src/compile.rs:378:12 clippy::if_not_else "unnecessary boolean `not` operation" -regex-1.3.2/src/compile.rs:400:37 clippy::redundant_field_names "redundant field names in struct initialization" -regex-1.3.2/src/compile.rs:407:51 clippy::redundant_field_names "redundant field names in struct initialization" -regex-1.3.2/src/compile.rs:409:24 clippy::redundant_field_names "redundant field names in struct initialization" -regex-1.3.2/src/compile.rs:417:5 clippy::unnecessary_wraps "this function's return value is unnecessarily wrapped by `Result`" -regex-1.3.2/src/compile.rs:42:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -regex-1.3.2/src/compile.rs:42:5 clippy::new_without_default "you should consider adding a `Default` implementation for `compile::Compiler`" -regex-1.3.2/src/compile.rs:444:5 clippy::unnecessary_wraps "this function's return value is unnecessarily wrapped by `Result`" -regex-1.3.2/src/compile.rs:445:57 clippy::redundant_field_names "redundant field names in struct initialization" -regex-1.3.2/src/compile.rs:446:20 clippy::redundant_field_names "redundant field names in struct initialization" -regex-1.3.2/src/compile.rs:466:20 clippy::redundant_field_names "redundant field names in struct initialization" -regex-1.3.2/src/compile.rs:466:32 clippy::redundant_field_names "redundant field names in struct initialization" -regex-1.3.2/src/compile.rs:519:13 clippy::enum_glob_use "usage of wildcard import for enum variants" -regex-1.3.2/src/compile.rs:55:57 clippy::doc_markdown "you should put `size_limit` between ticks in the documentation" -regex-1.3.2/src/compile.rs:58:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -regex-1.3.2/src/compile.rs:748:41 clippy::redundant_field_names "redundant field names in struct initialization" -regex-1.3.2/src/compile.rs:74:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -regex-1.3.2/src/compile.rs:751:54 clippy::redundant_field_names "redundant field names in struct initialization" -regex-1.3.2/src/compile.rs:765:41 clippy::redundant_field_names "redundant field names in struct initialization" -regex-1.3.2/src/compile.rs:765:55 clippy::redundant_field_names "redundant field names in struct initialization" -regex-1.3.2/src/compile.rs:825:39 clippy::redundant_field_names "redundant field names in struct initialization" -regex-1.3.2/src/compile.rs:825:51 clippy::redundant_field_names "redundant field names in struct initialization" -regex-1.3.2/src/compile.rs:828:49 clippy::redundant_field_names "redundant field names in struct initialization" -regex-1.3.2/src/compile.rs:828:61 clippy::redundant_field_names "redundant field names in struct initialization" -regex-1.3.2/src/compile.rs:830:59 clippy::redundant_field_names "redundant field names in struct initialization" -regex-1.3.2/src/compile.rs:830:71 clippy::redundant_field_names "redundant field names in struct initialization" -regex-1.3.2/src/compile.rs:832:43 clippy::redundant_field_names "redundant field names in struct initialization" -regex-1.3.2/src/compile.rs:835:41 clippy::redundant_field_names "redundant field names in struct initialization" -regex-1.3.2/src/compile.rs:835:53 clippy::redundant_field_names "redundant field names in struct initialization" -regex-1.3.2/src/compile.rs:835:67 clippy::redundant_field_names "redundant field names in struct initialization" -regex-1.3.2/src/compile.rs:83:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -regex-1.3.2/src/compile.rs:896:5 clippy::unnecessary_wraps "this function's return value is unnecessarily wrapped by `Result`" -regex-1.3.2/src/compile.rs:905:17 clippy::redundant_field_names "redundant field names in struct initialization" -regex-1.3.2/src/compile.rs:953:17 clippy::doc_markdown "you should put `HashMap` between ticks in the documentation" -regex-1.3.2/src/compile.rs:95:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -regex-1.3.2/src/compile.rs:980:26 clippy::unseparated_literal_suffix "integer type suffix should be separated by an underscore" -regex-1.3.2/src/compile.rs:994:44 clippy::redundant_field_names "redundant field names in struct initialization" -regex-1.3.2/src/compile.rs:994:54 clippy::redundant_field_names "redundant field names in struct initialization" -regex-1.3.2/src/dfa.rs:1007:17 clippy::similar_names "binding's name is too similar to existing binding" -regex-1.3.2/src/dfa.rs:1010:22 clippy::similar_names "binding's name is too similar to existing binding" -regex-1.3.2/src/dfa.rs:1059:13 clippy::enum_glob_use "usage of wildcard import for enum variants" -regex-1.3.2/src/dfa.rs:1060:13 clippy::enum_glob_use "usage of wildcard import for enum variants" -regex-1.3.2/src/dfa.rs:1084:38 clippy::cast_possible_truncation "casting `usize` to `u32` may truncate the value on targets with 64-bit wide pointers" -regex-1.3.2/src/dfa.rs:1087:38 clippy::cast_possible_truncation "casting `usize` to `u32` may truncate the value on targets with 64-bit wide pointers" -regex-1.3.2/src/dfa.rs:1090:38 clippy::cast_possible_truncation "casting `usize` to `u32` may truncate the value on targets with 64-bit wide pointers" -regex-1.3.2/src/dfa.rs:1093:38 clippy::cast_possible_truncation "casting `usize` to `u32` may truncate the value on targets with 64-bit wide pointers" -regex-1.3.2/src/dfa.rs:1096:38 clippy::cast_possible_truncation "casting `usize` to `u32` may truncate the value on targets with 64-bit wide pointers" -regex-1.3.2/src/dfa.rs:1101:38 clippy::cast_possible_truncation "casting `usize` to `u32` may truncate the value on targets with 64-bit wide pointers" -regex-1.3.2/src/dfa.rs:1104:38 clippy::cast_possible_truncation "casting `usize` to `u32` may truncate the value on targets with 64-bit wide pointers" -regex-1.3.2/src/dfa.rs:1107:38 clippy::cast_possible_truncation "casting `usize` to `u32` may truncate the value on targets with 64-bit wide pointers" -regex-1.3.2/src/dfa.rs:1117:30 clippy::cast_possible_truncation "casting `usize` to `u32` may truncate the value on targets with 64-bit wide pointers" -regex-1.3.2/src/dfa.rs:1120:47 clippy::cast_possible_truncation "casting `usize` to `u32` may truncate the value on targets with 64-bit wide pointers" -regex-1.3.2/src/dfa.rs:1121:30 clippy::cast_possible_truncation "casting `usize` to `u32` may truncate the value on targets with 64-bit wide pointers" -regex-1.3.2/src/dfa.rs:1129:13 clippy::doc_markdown "you should put `is_match` between ticks in the documentation" -regex-1.3.2/src/dfa.rs:1134:13 clippy::doc_markdown "you should put `is_match` between ticks in the documentation" -regex-1.3.2/src/dfa.rs:1185:68 clippy::doc_markdown "you should put `is_match` between ticks in the documentation" -regex-1.3.2/src/dfa.rs:1193:13 clippy::enum_glob_use "usage of wildcard import for enum variants" -regex-1.3.2/src/dfa.rs:1244:50 clippy::doc_markdown "you should put `current_state` between ticks in the documentation" -regex-1.3.2/src/dfa.rs:1338:58 clippy::doc_markdown "you should put `STATE_DEAD` between ticks in the documentation" -regex-1.3.2/src/dfa.rs:1339:9 clippy::doc_markdown "you should put `STATE_UNKNOWN` between ticks in the documentation" -regex-1.3.2/src/dfa.rs:1366:25 clippy::doc_markdown "you should put `STATE_DEAD` between ticks in the documentation" -regex-1.3.2/src/dfa.rs:1366:46 clippy::doc_markdown "you should put `STATE_UNKNOWN` between ticks in the documentation" -regex-1.3.2/src/dfa.rs:1367:41 clippy::inline_always "you have declared `#[inline(always)]` on `start_state`. This is usually a bad idea" -regex-1.3.2/src/dfa.rs:1380:14 clippy::identity_op "the operation is ineffective. Consider reducing it to `(empty_flags.start as u8)`" -regex-1.3.2/src/dfa.rs:1388:15 clippy::match_on_vec_items "indexing into a vector may panic" -regex-1.3.2/src/dfa.rs:1412:20 clippy::unused_self "unused `self` argument" -regex-1.3.2/src/dfa.rs:1438:9 clippy::unused_self "unused `self` argument" -regex-1.3.2/src/dfa.rs:1472:9 clippy::doc_markdown "you should put `StatePtr` between ticks in the documentation" -regex-1.3.2/src/dfa.rs:1490:54 clippy::cast_possible_truncation "casting `i32` to `u8` may truncate the value" -regex-1.3.2/src/dfa.rs:1490:54 clippy::cast_sign_loss "casting `i32` to `u8` may lose the sign of the value" -regex-1.3.2/src/dfa.rs:1521:20 clippy::doc_markdown "you should put `num_byte_classes` between ticks in the documentation" -regex-1.3.2/src/dfa.rs:1529:41 clippy::inline_always "you have declared `#[inline(always)]` on `byte_class`. This is usually a bad idea" -regex-1.3.2/src/dfa.rs:1537:14 clippy::doc_markdown "you should put `byte_class` between ticks in the documentation" -regex-1.3.2/src/dfa.rs:1538:41 clippy::inline_always "you have declared `#[inline(always)]` on `u8_class`. This is usually a bad idea" -regex-1.3.2/src/dfa.rs:1562:18 clippy::doc_markdown "you should put `STATE_START` between ticks in the documentation" -regex-1.3.2/src/dfa.rs:1614:13 clippy::redundant_field_names "redundant field names in struct initialization" -regex-1.3.2/src/dfa.rs:1651:38 clippy::redundant_field_names "redundant field names in struct initialization" -regex-1.3.2/src/dfa.rs:1700:17 clippy::trivially_copy_pass_by_ref "this argument (1 byte) is passed by reference, but would be more efficient if passed by value (limit: 8 byte)" -regex-1.3.2/src/dfa.rs:1701:18 clippy::unusual_byte_groupings "digits of hex or binary literal not grouped by four" -regex-1.3.2/src/dfa.rs:1705:19 clippy::unusual_byte_groupings "digits of hex or binary literal not grouped by four" -regex-1.3.2/src/dfa.rs:1708:16 clippy::trivially_copy_pass_by_ref "this argument (1 byte) is passed by reference, but would be more efficient if passed by value (limit: 8 byte)" -regex-1.3.2/src/dfa.rs:1709:18 clippy::unusual_byte_groupings "digits of hex or binary literal not grouped by four" -regex-1.3.2/src/dfa.rs:1713:19 clippy::unusual_byte_groupings "digits of hex or binary literal not grouped by four" -regex-1.3.2/src/dfa.rs:1716:18 clippy::trivially_copy_pass_by_ref "this argument (1 byte) is passed by reference, but would be more efficient if passed by value (limit: 8 byte)" -regex-1.3.2/src/dfa.rs:1717:18 clippy::unusual_byte_groupings "digits of hex or binary literal not grouped by four" -regex-1.3.2/src/dfa.rs:1721:19 clippy::unusual_byte_groupings "digits of hex or binary literal not grouped by four" -regex-1.3.2/src/dfa.rs:1727:14 clippy::cast_lossless "casting `u8` to `u16` may become silently lossy if you later change the type" -regex-1.3.2/src/dfa.rs:1732:15 clippy::trivially_copy_pass_by_ref "this argument (2 byte) is passed by reference, but would be more efficient if passed by value (limit: 8 byte)" -regex-1.3.2/src/dfa.rs:1736:22 clippy::trivially_copy_pass_by_ref "this argument (2 byte) is passed by reference, but would be more efficient if passed by value (limit: 8 byte)" -regex-1.3.2/src/dfa.rs:1741:9 clippy::match_like_matches_macro "match expression looks like `matches!` macro" -regex-1.3.2/src/dfa.rs:1747:16 clippy::trivially_copy_pass_by_ref "this argument (2 byte) is passed by reference, but would be more efficient if passed by value (limit: 8 byte)" -regex-1.3.2/src/dfa.rs:1751:18 clippy::cast_possible_truncation "casting `u16` to `u8` may truncate the value" -regex-1.3.2/src/dfa.rs:1815:38 clippy::cast_possible_truncation "casting `usize` to `u8` may truncate the value" -regex-1.3.2/src/dfa.rs:1821:21 clippy::cast_lossless "casting `u32` to `u64` may become silently lossy if you later change the type" -regex-1.3.2/src/dfa.rs:1824:5 clippy::cast_possible_truncation "casting `usize` to `u32` may truncate the value on targets with 64-bit wide pointers" -regex-1.3.2/src/dfa.rs:1848:5 clippy::doc_markdown "you should put bare URLs between `<`/`>` or make a proper Markdown link" -regex-1.3.2/src/dfa.rs:1850:18 clippy::cast_sign_loss "casting `i32` to `u32` may lose the sign of the value" -regex-1.3.2/src/dfa.rs:1857:5 clippy::doc_markdown "you should put bare URLs between `<`/`>` or make a proper Markdown link" -regex-1.3.2/src/dfa.rs:1860:17 clippy::cast_possible_wrap "casting `u32` to `i32` may wrap around the value" -regex-1.3.2/src/dfa.rs:1867:5 clippy::doc_markdown "you should put bare URLs between `<`/`>` or make a proper Markdown link" -regex-1.3.2/src/dfa.rs:1870:19 clippy::cast_possible_truncation "casting `u32` to `u8` may truncate the value" -regex-1.3.2/src/dfa.rs:1873:15 clippy::cast_possible_truncation "casting `u32` to `u8` may truncate the value" -regex-1.3.2/src/dfa.rs:1876:5 clippy::doc_markdown "you should put bare URLs between `<`/`>` or make a proper Markdown link" -regex-1.3.2/src/dfa.rs:1882:26 clippy::cast_lossless "casting `u8` to `u32` may become silently lossy if you later change the type" -regex-1.3.2/src/dfa.rs:1884:15 clippy::cast_lossless "casting `u8` to `u32` may become silently lossy if you later change the type" -regex-1.3.2/src/dfa.rs:277:17 clippy::cast_possible_wrap "casting `u32` to `i32` may wrap around the value" -regex-1.3.2/src/dfa.rs:277:31 clippy::cast_possible_wrap "casting `u32` to `i32` may wrap around the value" -regex-1.3.2/src/dfa.rs:295:20 clippy::cast_possible_truncation "casting `usize` to `i32` may truncate the value on targets with 64-bit wide pointers" -regex-1.3.2/src/dfa.rs:295:20 clippy::cast_possible_wrap "casting `usize` to `i32` may wrap around the value on targets with 32-bit wide pointers" -regex-1.3.2/src/dfa.rs:299:21 clippy::cast_sign_loss "casting `i32` to `usize` may lose the sign of the value" -regex-1.3.2/src/dfa.rs:34:46 clippy::doc_markdown "you should put bare URLs between `<`/`>` or make a proper Markdown link" -regex-1.3.2/src/dfa.rs:398:1 clippy::struct_excessive_bools "more than 3 bools in a struct" -regex-1.3.2/src/dfa.rs:446:41 clippy::inline_always "you have declared `#[inline(always)]` on `forward`. This is usually a bad idea" -regex-1.3.2/src/dfa.rs:457:13 clippy::redundant_field_names "redundant field names in struct initialization" -regex-1.3.2/src/dfa.rs:459:13 clippy::redundant_field_names "redundant field names in struct initialization" -regex-1.3.2/src/dfa.rs:460:13 clippy::redundant_field_names "redundant field names in struct initialization" -regex-1.3.2/src/dfa.rs:476:41 clippy::inline_always "you have declared `#[inline(always)]` on `reverse`. This is usually a bad idea" -regex-1.3.2/src/dfa.rs:487:13 clippy::redundant_field_names "redundant field names in struct initialization" -regex-1.3.2/src/dfa.rs:489:13 clippy::redundant_field_names "redundant field names in struct initialization" -regex-1.3.2/src/dfa.rs:490:13 clippy::redundant_field_names "redundant field names in struct initialization" -regex-1.3.2/src/dfa.rs:506:41 clippy::inline_always "you have declared `#[inline(always)]` on `forward_many`. This is usually a bad idea" -regex-1.3.2/src/dfa.rs:518:13 clippy::redundant_field_names "redundant field names in struct initialization" -regex-1.3.2/src/dfa.rs:520:13 clippy::redundant_field_names "redundant field names in struct initialization" -regex-1.3.2/src/dfa.rs:554:41 clippy::inline_always "you have declared `#[inline(always)]` on `exec_at`. This is usually a bad idea" -regex-1.3.2/src/dfa.rs:555:5 clippy::too_many_lines "this function has too many lines (101/100)" -regex-1.3.2/src/dfa.rs:58:9 clippy::enum_glob_use "usage of wildcard import for enum variants" -regex-1.3.2/src/dfa.rs:667:21 clippy::similar_names "binding's name is too similar to existing binding" -regex-1.3.2/src/dfa.rs:747:41 clippy::inline_always "you have declared `#[inline(always)]` on `exec_at_reverse`. This is usually a bad idea" -regex-1.3.2/src/dfa.rs:795:21 clippy::similar_names "binding's name is too similar to existing binding" -regex-1.3.2/src/dfa.rs:848:9 clippy::doc_markdown "you should put `next_si` between ticks in the documentation" -regex-1.3.2/src/dfa.rs:852:41 clippy::inline_always "you have declared `#[inline(always)]` on `next_si`. This is usually a bad idea" -regex-1.3.2/src/dfa.rs:885:12 clippy::doc_markdown "you should put `STATE_DEAD` between ticks in the documentation" -regex-1.3.2/src/dfa.rs:889:9 clippy::doc_markdown "you should put `STATE_UNKNOWN` between ticks in the documentation" -regex-1.3.2/src/dfa.rs:897:13 clippy::enum_glob_use "usage of wildcard import for enum variants" -regex-1.3.2/src/dfa.rs:979:29 clippy::cast_possible_truncation "casting `usize` to `u32` may truncate the value on targets with 64-bit wide pointers" -regex-1.3.2/src/error.rs:6:1 clippy::manual_non_exhaustive "this seems like a manual implementation of the non-exhaustive pattern" -regex-1.3.2/src/exec.rs:1000:14 clippy::doc_markdown "you should put `captures_nfa` between ticks in the documentation" -regex-1.3.2/src/exec.rs:100:1 clippy::module_name_repetitions "item name starts with its containing module's name" -regex-1.3.2/src/exec.rs:1028:5 clippy::too_many_arguments "this function has too many arguments (9/7)" -regex-1.3.2/src/exec.rs:1039:13 clippy::enum_glob_use "usage of wildcard import for enum variants" -regex-1.3.2/src/exec.rs:1144:13 clippy::enum_glob_use "usage of wildcard import for enum variants" -regex-1.3.2/src/exec.rs:1179:26 clippy::match_same_arms "this `match` has identical arm bodies" -regex-1.3.2/src/exec.rs:122:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -regex-1.3.2/src/exec.rs:1250:41 clippy::inline_always "you have declared `#[inline(always)]` on `searcher`. This is usually a bad idea" -regex-1.3.2/src/exec.rs:1260:41 clippy::inline_always "you have declared `#[inline(always)]` on `searcher_str`. This is usually a bad idea" -regex-1.3.2/src/exec.rs:1270:17 clippy::doc_markdown "you should put `RegexSet` between ticks in the documentation" -regex-1.3.2/src/exec.rs:1280:17 clippy::doc_markdown "you should put `RegexSet` between ticks in the documentation" -regex-1.3.2/src/exec.rs:137:9 clippy::field_reassign_with_default "field assignment outside of initializer for an instance created with Default::default()" -regex-1.3.2/src/exec.rs:142:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -regex-1.3.2/src/exec.rs:1493:5 clippy::upper_case_acronyms "name `PikeVM` contains a capitalized acronym" -regex-1.3.2/src/exec.rs:158:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -regex-1.3.2/src/exec.rs:168:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -regex-1.3.2/src/exec.rs:181:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -regex-1.3.2/src/exec.rs:195:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -regex-1.3.2/src/exec.rs:204:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -regex-1.3.2/src/exec.rs:210:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -regex-1.3.2/src/exec.rs:245:62 clippy::if_same_then_else "this `if` has identical blocks" -regex-1.3.2/src/exec.rs:251:21 clippy::if_not_else "unnecessary boolean `not` operation" -regex-1.3.2/src/exec.rs:262:60 clippy::if_same_then_else "this `if` has identical blocks" -regex-1.3.2/src/exec.rs:268:21 clippy::if_not_else "unnecessary boolean `not` operation" -regex-1.3.2/src/exec.rs:278:13 clippy::redundant_field_names "redundant field names in struct initialization" -regex-1.3.2/src/exec.rs:281:13 clippy::redundant_field_names "redundant field names in struct initialization" -regex-1.3.2/src/exec.rs:286:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -regex-1.3.2/src/exec.rs:300:30 clippy::redundant_field_names "redundant field names in struct initialization" -regex-1.3.2/src/exec.rs:308:17 clippy::similar_names "binding's name is too similar to existing binding" -regex-1.3.2/src/exec.rs:329:13 clippy::redundant_field_names "redundant field names in struct initialization" -regex-1.3.2/src/exec.rs:330:13 clippy::redundant_field_names "redundant field names in struct initialization" -regex-1.3.2/src/exec.rs:331:13 clippy::redundant_field_names "redundant field names in struct initialization" -regex-1.3.2/src/exec.rs:334:13 clippy::redundant_field_names "redundant field names in struct initialization" -regex-1.3.2/src/exec.rs:340:19 clippy::redundant_field_names "redundant field names in struct initialization" -regex-1.3.2/src/exec.rs:344:27 clippy::unused_self "unused `self` argument" -regex-1.3.2/src/exec.rs:383:41 clippy::inline_always "you have declared `#[inline(always)]` on `shortest_match_at`. This is usually a bad idea" -regex-1.3.2/src/exec.rs:388:41 clippy::inline_always "you have declared `#[inline(always)]` on `is_match_at`. This is usually a bad idea" -regex-1.3.2/src/exec.rs:393:41 clippy::inline_always "you have declared `#[inline(always)]` on `find_at`. This is usually a bad idea" -regex-1.3.2/src/exec.rs:398:41 clippy::inline_always "you have declared `#[inline(always)]` on `captures_read_at`. This is usually a bad idea" -regex-1.3.2/src/exec.rs:425:41 clippy::inline_always "you have declared `#[inline(always)]` on `shortest_match_at`. This is usually a bad idea" -regex-1.3.2/src/exec.rs:44:1 clippy::module_name_repetitions "item name starts with its containing module's name" -regex-1.3.2/src/exec.rs:473:9 clippy::doc_markdown "you should put `shortest_match(...).is_some` between ticks in the documentation" -regex-1.3.2/src/exec.rs:474:41 clippy::inline_always "you have declared `#[inline(always)]` on `is_match_at`. This is usually a bad idea" -regex-1.3.2/src/exec.rs:524:41 clippy::inline_always "you have declared `#[inline(always)]` on `find_at`. This is usually a bad idea" -regex-1.3.2/src/exec.rs:52:1 clippy::module_name_repetitions "item name starts with its containing module's name" -regex-1.3.2/src/exec.rs:686:13 clippy::enum_glob_use "usage of wildcard import for enum variants" -regex-1.3.2/src/exec.rs:727:13 clippy::enum_glob_use "usage of wildcard import for enum variants" -regex-1.3.2/src/exec.rs:767:13 clippy::enum_glob_use "usage of wildcard import for enum variants" -regex-1.3.2/src/exec.rs:783:41 clippy::inline_always "you have declared `#[inline(always)]` on `shortest_dfa`. This is usually a bad idea" -regex-1.3.2/src/exec.rs:791:41 clippy::inline_always "you have declared `#[inline(always)]` on `shortest_dfa_reverse_suffix`. This is usually a bad idea" -regex-1.3.2/src/exec.rs:823:13 clippy::enum_glob_use "usage of wildcard import for enum variants" -regex-1.3.2/src/exec.rs:868:13 clippy::enum_glob_use "usage of wildcard import for enum variants" -regex-1.3.2/src/exec.rs:897:31 clippy::doc_markdown "you should put `shortest_nfa(...).is_some` between ticks in the documentation" -regex-1.3.2/src/exec.rs:899:9 clippy::doc_markdown "you should put `shortest_nfa` between ticks in the documentation" -regex-1.3.2/src/exec.rs:905:14 clippy::doc_markdown "you should put `match_nfa` between ticks in the documentation" -regex-1.3.2/src/exec.rs:930:14 clippy::doc_markdown "you should put `shortest_nfa` between ticks in the documentation" -regex-1.3.2/src/exec.rs:981:14 clippy::doc_markdown "you should put `find_nfa` between ticks in the documentation" -regex-1.3.2/src/expand.rs:170:27 clippy::trivially_copy_pass_by_ref "this argument (1 byte) is passed by reference, but would be more efficient if passed by value (limit: 8 byte)" -regex-1.3.2/src/expand.rs:171:5 clippy::match_like_matches_macro "match expression looks like `matches!` macro" -regex-1.3.2/src/expand.rs:22:13 clippy::single_char_add_str "calling `push_str()` using a single-character string literal" -regex-1.3.2/src/expand.rs:27:23 clippy::single_match_else "you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let`" -regex-1.3.2/src/expand.rs:30:17 clippy::single_char_add_str "calling `push_str()` using a single-character string literal" -regex-1.3.2/src/expand.rs:38:30 clippy::map_unwrap_or "called `map(<f>).unwrap_or(<a>)` on an `Option` value. This can be done more directly by calling `map_or(<a>, <f>)` instead" -regex-1.3.2/src/expand.rs:42:21 clippy::map_unwrap_or "called `map(<f>).unwrap_or(<a>)` on an `Option` value. This can be done more directly by calling `map_or(<a>, <f>)` instead" -regex-1.3.2/src/expand.rs:50:1 clippy::module_name_repetitions "item name starts with its containing module's name" -regex-1.3.2/src/expand.rs:69:23 clippy::single_match_else "you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let`" -regex-1.3.2/src/expand.rs:80:28 clippy::map_unwrap_or "called `map(<f>).unwrap_or(<a>)` on an `Option` value. This can be done more directly by calling `map_or(<a>, <f>)` instead" -regex-1.3.2/src/expand.rs:84:21 clippy::map_unwrap_or "called `map(<f>).unwrap_or(<a>)` on an `Option` value. This can be done more directly by calling `map_or(<a>, <f>)` instead" -regex-1.3.2/src/expand.rs:8:1 clippy::module_name_repetitions "item name starts with its containing module's name" -regex-1.3.2/src/input.rs:142:1 clippy::module_name_repetitions "item name ends with its containing module's name" -regex-1.3.2/src/input.rs:146:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -regex-1.3.2/src/input.rs:15:1 clippy::module_name_repetitions "item name starts with its containing module's name" -regex-1.3.2/src/input.rs:165:31 clippy::redundant_field_names "redundant field names in struct initialization" -regex-1.3.2/src/input.rs:178:13 clippy::enum_glob_use "usage of wildcard import for enum variants" -regex-1.3.2/src/input.rs:228:1 clippy::module_name_repetitions "item name ends with its containing module's name" -regex-1.3.2/src/input.rs:236:21 clippy::redundant_field_names "redundant field names in struct initialization" -regex-1.3.2/src/input.rs:236:33 clippy::redundant_field_names "redundant field names in struct initialization" -regex-1.3.2/src/input.rs:24:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -regex-1.3.2/src/input.rs:271:13 clippy::enum_glob_use "usage of wildcard import for enum variants" -regex-1.3.2/src/input.rs:29:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -regex-1.3.2/src/input.rs:362:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -regex-1.3.2/src/input.rs:370:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -regex-1.3.2/src/input.rs:371:42 clippy::redundant_closure_for_method_calls "redundant closure found" -regex-1.3.2/src/input.rs:37:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -regex-1.3.2/src/input.rs:388:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -regex-1.3.2/src/input.rs:42:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -regex-1.3.2/src/input.rs:47:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -regex-1.3.2/src/input.rs:53:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -regex-1.3.2/src/input.rs:58:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -regex-1.3.2/src/input.rs:63:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -regex-1.3.2/src/lib.rs:1:null clippy::cargo_common_metadata "package `regex` is missing `package.keywords` metadata" -regex-1.3.2/src/literal/imp.rs:101:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -regex-1.3.2/src/literal/imp.rs:114:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -regex-1.3.2/src/literal/imp.rs:127:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -regex-1.3.2/src/literal/imp.rs:139:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -regex-1.3.2/src/literal/imp.rs:144:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -regex-1.3.2/src/literal/imp.rs:149:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -regex-1.3.2/src/literal/imp.rs:154:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -regex-1.3.2/src/literal/imp.rs:155:13 clippy::enum_glob_use "usage of wildcard import for enum variants" -regex-1.3.2/src/literal/imp.rs:160:30 clippy::match_same_arms "this `match` has identical arm bodies" -regex-1.3.2/src/literal/imp.rs:167:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -regex-1.3.2/src/literal/imp.rs:168:13 clippy::enum_glob_use "usage of wildcard import for enum variants" -regex-1.3.2/src/literal/imp.rs:211:20 clippy::redundant_else "redundant else block" -regex-1.3.2/src/literal/imp.rs:239:5 clippy::upper_case_acronyms "name `AC` contains a capitalized acronym" -regex-1.3.2/src/literal/imp.rs:276:50 clippy::match_same_arms "this `match` has identical arm bodies" -regex-1.3.2/src/literal/imp.rs:342:41 clippy::inline_always "you have declared `#[inline(always)]` on `find`. This is usually a bad idea" -regex-1.3.2/src/literal/imp.rs:34:5 clippy::upper_case_acronyms "name `AC` contains a capitalized acronym" -regex-1.3.2/src/literal/imp.rs:435:13 clippy::redundant_field_names "redundant field names in struct initialization" -regex-1.3.2/src/literal/imp.rs:436:13 clippy::redundant_field_names "redundant field names in struct initialization" -regex-1.3.2/src/literal/imp.rs:437:13 clippy::redundant_field_names "redundant field names in struct initialization" -regex-1.3.2/src/literal/imp.rs:438:13 clippy::redundant_field_names "redundant field names in struct initialization" -regex-1.3.2/src/literal/imp.rs:439:13 clippy::redundant_field_names "redundant field names in struct initialization" -regex-1.3.2/src/literal/imp.rs:440:13 clippy::redundant_field_names "redundant field names in struct initialization" -regex-1.3.2/src/literal/imp.rs:455:41 clippy::inline_always "you have declared `#[inline(always)]` on `find`. This is usually a bad idea" -regex-1.3.2/src/literal/imp.rs:46:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -regex-1.3.2/src/literal/imp.rs:481:41 clippy::inline_always "you have declared `#[inline(always)]` on `is_suffix`. This is usually a bad idea" -regex-1.3.2/src/literal/imp.rs:51:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -regex-1.3.2/src/literal/imp.rs:579:13 clippy::redundant_field_names "redundant field names in struct initialization" -regex-1.3.2/src/literal/imp.rs:57:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -regex-1.3.2/src/literal/imp.rs:580:13 clippy::redundant_field_names "redundant field names in struct initialization" -regex-1.3.2/src/literal/imp.rs:583:13 clippy::redundant_field_names "redundant field names in struct initialization" -regex-1.3.2/src/literal/imp.rs:602:9 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope" -regex-1.3.2/src/literal/imp.rs:622:24 clippy::redundant_else "redundant else block" -regex-1.3.2/src/literal/imp.rs:62:18 clippy::needless_pass_by_value "this argument is passed by value, but not consumed in the function body" -regex-1.3.2/src/literal/imp.rs:637:24 clippy::redundant_else "redundant else block" -regex-1.3.2/src/literal/imp.rs:648:9 clippy::needless_return "unneeded `return` statement" -regex-1.3.2/src/literal/imp.rs:651:44 clippy::doc_markdown "you should put `BoyerMooreSearch` between ticks in the documentation" -regex-1.3.2/src/literal/imp.rs:65:13 clippy::redundant_field_names "redundant field names in struct initialization" -regex-1.3.2/src/literal/imp.rs:68:13 clippy::redundant_field_names "redundant field names in struct initialization" -regex-1.3.2/src/literal/imp.rs:783:32 clippy::redundant_else "redundant else block" -regex-1.3.2/src/literal/imp.rs:786:42 clippy::manual_saturating_arithmetic "manual saturating arithmetic" -regex-1.3.2/src/literal/imp.rs:78:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -regex-1.3.2/src/literal/imp.rs:84:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -regex-1.3.2/src/literal/imp.rs:850:20 clippy::unreadable_literal "long literal lacking separators" -regex-1.3.2/src/literal/imp.rs:85:13 clippy::enum_glob_use "usage of wildcard import for enum variants" -regex-1.3.2/src/pikevm.rs:103:15 clippy::redundant_field_names "redundant field names in struct initialization" -regex-1.3.2/src/pikevm.rs:103:52 clippy::redundant_field_names "redundant field names in struct initialization" -regex-1.3.2/src/pikevm.rs:114:5 clippy::too_many_arguments "this function has too many arguments (8/7)" -regex-1.3.2/src/pikevm.rs:117:13 clippy::similar_names "binding's name is too similar to existing binding" -regex-1.3.2/src/pikevm.rs:124:17 clippy::similar_names "binding's name is too similar to existing binding" -regex-1.3.2/src/pikevm.rs:220:9 clippy::doc_markdown "you should put `thread_caps` between ticks in the documentation" -regex-1.3.2/src/pikevm.rs:222:16 clippy::doc_markdown "you should put `at_next` between ticks in the documentation" -regex-1.3.2/src/pikevm.rs:223:9 clippy::doc_markdown "you should put `at_next` between ticks in the documentation" -regex-1.3.2/src/pikevm.rs:224:5 clippy::too_many_arguments "this function has too many arguments (8/7)" -regex-1.3.2/src/pikevm.rs:234:13 clippy::enum_glob_use "usage of wildcard import for enum variants" -regex-1.3.2/src/pikevm.rs:303:13 clippy::enum_glob_use "usage of wildcard import for enum variants" -regex-1.3.2/src/pikevm.rs:331:29 clippy::mut_mut "this expression mutably borrows a mutable reference. Consider reborrowing" -regex-1.3.2/src/pikevm.rs:70:5 clippy::upper_case_acronyms "name `IP` contains a capitalized acronym" -regex-1.3.2/src/pikevm.rs:88:5 clippy::too_many_arguments "this function has too many arguments (8/7)" -regex-1.3.2/src/prog.rs:102:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -regex-1.3.2/src/prog.rs:113:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -regex-1.3.2/src/prog.rs:120:9 clippy::match_like_matches_macro "match expression looks like `matches!` macro" -regex-1.3.2/src/prog.rs:128:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -regex-1.3.2/src/prog.rs:134:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -regex-1.3.2/src/prog.rs:141:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -regex-1.3.2/src/prog.rs:147:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -regex-1.3.2/src/prog.rs:164:41 clippy::inline_always "you have declared `#[inline(always)]` on `deref`. This is usually a bad idea" -regex-1.3.2/src/prog.rs:172:13 clippy::enum_glob_use "usage of wildcard import for enum variants" -regex-1.3.2/src/prog.rs:18:1 clippy::struct_excessive_bools "more than 3 bools in a struct" -regex-1.3.2/src/prog.rs:236:13 clippy::write_with_newline "using `write!()` with a format string that ends in a single newline" -regex-1.3.2/src/prog.rs:300:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -regex-1.3.2/src/prog.rs:301:9 clippy::match_like_matches_macro "match expression looks like `matches!` macro" -regex-1.3.2/src/prog.rs:382:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -regex-1.3.2/src/prog.rs:409:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -regex-1.3.2/src/prog.rs:80:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -regex-1.3.2/src/prog.rs:80:5 clippy::new_without_default "you should consider adding a `Default` implementation for `prog::Program`" -regex-1.3.2/src/re_builder.rs:267:17 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -regex-1.3.2/src/re_builder.rs:267:17 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -regex-1.3.2/src/re_builder.rs:4:1 clippy::struct_excessive_bools "more than 3 bools in a struct" -regex-1.3.2/src/re_builder.rs:57:17 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -regex-1.3.2/src/re_builder.rs:57:17 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -regex-1.3.2/src/re_builder.rs:68:17 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -regex-1.3.2/src/re_builder.rs:68:17 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -regex-1.3.2/src/re_bytes.rs:1017:9 clippy::map_unwrap_or "called `map(<f>).unwrap_or_else(<g>)` on an `Option` value. This can be done more directly by calling `map_or_else(<g>, <f>)` instead" -regex-1.3.2/src/re_bytes.rs:1039:9 clippy::map_unwrap_or "called `map(<f>).unwrap_or_else(<g>)` on an `Option` value. This can be done more directly by calling `map_or_else(<g>, <f>)` instead" -regex-1.3.2/src/re_bytes.rs:1093:5 clippy::needless_lifetimes "explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration)" -regex-1.3.2/src/re_bytes.rs:1118:5 clippy::needless_lifetimes "explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration)" -regex-1.3.2/src/re_bytes.rs:1133:5 clippy::needless_lifetimes "explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration)" -regex-1.3.2/src/re_bytes.rs:118:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -regex-1.3.2/src/re_bytes.rs:256:13 clippy::redundant_field_names "redundant field names in struct initialization" -regex-1.3.2/src/re_bytes.rs:29:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -regex-1.3.2/src/re_bytes.rs:35:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -regex-1.3.2/src/re_bytes.rs:42:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -regex-1.3.2/src/re_bytes.rs:483:5 clippy::missing_panics_doc "docs for function which may panic missing `# Panics` section" -regex-1.3.2/src/re_bytes.rs:48:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -regex-1.3.2/src/re_bytes.rs:558:29 clippy::doc_markdown "you should put `shortest_match` between ticks in the documentation" -regex-1.3.2/src/re_bytes.rs:55:33 clippy::redundant_field_names "redundant field names in struct initialization" -regex-1.3.2/src/re_bytes.rs:55:47 clippy::redundant_field_names "redundant field names in struct initialization" -regex-1.3.2/src/re_bytes.rs:572:29 clippy::doc_markdown "you should put `is_match` between ticks in the documentation" -regex-1.3.2/src/re_bytes.rs:720:13 clippy::redundant_field_names "redundant field names in struct initialization" -regex-1.3.2/src/re_bytes.rs:817:5 clippy::doc_markdown "you should put `CaptureLocations` between ticks in the documentation" -regex-1.3.2/src/re_bytes.rs:843:1 clippy::len_without_is_empty "item `re_bytes::CaptureLocations` has a public `len` method but no corresponding `is_empty` method" -regex-1.3.2/src/re_bytes.rs:849:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -regex-1.3.2/src/re_bytes.rs:858:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -regex-1.3.2/src/re_bytes.rs:869:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -regex-1.3.2/src/re_bytes.rs:891:1 clippy::len_without_is_empty "item `re_bytes::Captures<'t>` has a public `len` method but no corresponding `is_empty` method" -regex-1.3.2/src/re_bytes.rs:911:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -regex-1.3.2/src/re_bytes.rs:917:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -regex-1.3.2/src/re_bytes.rs:926:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -regex-1.3.2/src/re_bytes.rs:955:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -regex-1.3.2/src/re_set.rs:179:13 clippy::redundant_field_names "redundant field names in struct initialization" -regex-1.3.2/src/re_set.rs:179:13 clippy::redundant_field_names "redundant field names in struct initialization" -regex-1.3.2/src/re_set.rs:251:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -regex-1.3.2/src/re_set.rs:251:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -regex-1.3.2/src/re_set.rs:263:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -regex-1.3.2/src/re_set.rs:263:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -regex-1.3.2/src/re_set.rs:268:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -regex-1.3.2/src/re_set.rs:268:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -regex-1.3.2/src/re_set.rs:277:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -regex-1.3.2/src/re_set.rs:277:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -regex-1.3.2/src/re_set.rs:94:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -regex-1.3.2/src/re_set.rs:94:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -regex-1.3.2/src/re_trait.rs:136:29 clippy::redundant_field_names "redundant field names in struct initialization" -regex-1.3.2/src/re_unicode.rs:1019:9 clippy::map_unwrap_or "called `map(<f>).unwrap_or_else(<g>)` on an `Option` value. This can be done more directly by calling `map_or_else(<g>, <f>)` instead" -regex-1.3.2/src/re_unicode.rs:1041:9 clippy::map_unwrap_or "called `map(<f>).unwrap_or_else(<g>)` on an `Option` value. This can be done more directly by calling `map_or_else(<g>, <f>)` instead" -regex-1.3.2/src/re_unicode.rs:1088:13 clippy::redundant_field_names "redundant field names in struct initialization" -regex-1.3.2/src/re_unicode.rs:1135:5 clippy::needless_lifetimes "explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration)" -regex-1.3.2/src/re_unicode.rs:1160:5 clippy::needless_lifetimes "explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration)" -regex-1.3.2/src/re_unicode.rs:174:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" -regex-1.3.2/src/re_unicode.rs:21:1 clippy::must_use_candidate "this function could have a `#[must_use]` attribute" -regex-1.3.2/src/re_unicode.rs:313:13 clippy::redundant_field_names "redundant field names in struct initialization" -regex-1.3.2/src/re_unicode.rs:38:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -regex-1.3.2/src/re_unicode.rs:44:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -regex-1.3.2/src/re_unicode.rs:51:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -regex-1.3.2/src/re_unicode.rs:533:5 clippy::missing_panics_doc "docs for function which may panic missing `# Panics` section" -regex-1.3.2/src/re_unicode.rs:57:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -regex-1.3.2/src/re_unicode.rs:617:29 clippy::doc_markdown "you should put `shortest_match` between ticks in the documentation" -regex-1.3.2/src/re_unicode.rs:631:29 clippy::doc_markdown "you should put `is_match` between ticks in the documentation" -regex-1.3.2/src/re_unicode.rs:64:33 clippy::redundant_field_names "redundant field names in struct initialization" -regex-1.3.2/src/re_unicode.rs:64:47 clippy::redundant_field_names "redundant field names in struct initialization" -regex-1.3.2/src/re_unicode.rs:834:5 clippy::doc_markdown "you should put `CaptureLocations` between ticks in the documentation" -regex-1.3.2/src/re_unicode.rs:860:1 clippy::len_without_is_empty "item `re_unicode::CaptureLocations` has a public `len` method but no corresponding `is_empty` method" -regex-1.3.2/src/re_unicode.rs:866:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -regex-1.3.2/src/re_unicode.rs:875:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -regex-1.3.2/src/re_unicode.rs:886:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -regex-1.3.2/src/re_unicode.rs:908:1 clippy::len_without_is_empty "item `re_unicode::Captures<'t>` has a public `len` method but no corresponding `is_empty` method" -regex-1.3.2/src/re_unicode.rs:928:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -regex-1.3.2/src/re_unicode.rs:934:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -regex-1.3.2/src/re_unicode.rs:943:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -regex-1.3.2/src/re_unicode.rs:972:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" -regex-1.3.2/src/sparse.rs:10:37 clippy::doc_markdown "you should put bare URLs between `<`/`>` or make a proper Markdown link" -regex-1.3.2/src/sparse.rs:15:1 clippy::module_name_repetitions "item name starts with its containing module's name" -regex-1.3.2/src/utf8.rs:100:16 clippy::unusual_byte_groupings "digits of hex or binary literal not grouped by four" -regex-1.3.2/src/utf8.rs:103:16 clippy::unusual_byte_groupings "digits of hex or binary literal not grouped by four" -regex-1.3.2/src/utf8.rs:106:22 clippy::cast_lossless "casting `u8` to `u32` may become silently lossy if you later change the type" -regex-1.3.2/src/utf8.rs:107:19 clippy::cast_lossless "casting `u8` to `u32` may become silently lossy if you later change the type" -regex-1.3.2/src/utf8.rs:108:19 clippy::cast_lossless "casting `u8` to `u32` may become silently lossy if you later change the type" -regex-1.3.2/src/utf8.rs:109:19 clippy::cast_lossless "casting `u8` to `u32` may become silently lossy if you later change the type" -regex-1.3.2/src/utf8.rs:111:27 clippy::unreadable_literal "long literal lacking separators" -regex-1.3.2/src/utf8.rs:121:1 clippy::module_name_repetitions "item name ends with its containing module's name" -regex-1.3.2/src/utf8.rs:143:24 clippy::unusual_byte_groupings "digits of hex or binary literal not grouped by four" -regex-1.3.2/src/utf8.rs:143:9 clippy::unusual_byte_groupings "digits of hex or binary literal not grouped by four" -regex-1.3.2/src/utf8.rs:23:1 clippy::module_name_repetitions "item name ends with its containing module's name" -regex-1.3.2/src/utf8.rs:30:20 clippy::unusual_byte_groupings "digits of hex or binary literal not grouped by four" -regex-1.3.2/src/utf8.rs:51:1 clippy::module_name_repetitions "item name ends with its containing module's name" -regex-1.3.2/src/utf8.rs:58:23 clippy::unusual_byte_groupings "digits of hex or binary literal not grouped by four" -regex-1.3.2/src/utf8.rs:58:9 clippy::unusual_byte_groupings "digits of hex or binary literal not grouped by four" -regex-1.3.2/src/utf8.rs:63:16 clippy::unusual_byte_groupings "digits of hex or binary literal not grouped by four" -regex-1.3.2/src/utf8.rs:66:22 clippy::cast_lossless "casting `u8` to `u32` may become silently lossy if you later change the type" -regex-1.3.2/src/utf8.rs:66:54 clippy::cast_lossless "casting `u8` to `u32` may become silently lossy if you later change the type" -regex-1.3.2/src/utf8.rs:77:16 clippy::unusual_byte_groupings "digits of hex or binary literal not grouped by four" -regex-1.3.2/src/utf8.rs:80:16 clippy::unusual_byte_groupings "digits of hex or binary literal not grouped by four" -regex-1.3.2/src/utf8.rs:83:22 clippy::cast_lossless "casting `u8` to `u32` may become silently lossy if you later change the type" -regex-1.3.2/src/utf8.rs:84:19 clippy::cast_lossless "casting `u8` to `u32` may become silently lossy if you later change the type" -regex-1.3.2/src/utf8.rs:85:19 clippy::cast_lossless "casting `u8` to `u32` may become silently lossy if you later change the type" -regex-1.3.2/src/utf8.rs:92:23 clippy::unusual_byte_groupings "digits of hex or binary literal not grouped by four" -regex-1.3.2/src/utf8.rs:92:9 clippy::unusual_byte_groupings "digits of hex or binary literal not grouped by four" -regex-1.3.2/src/utf8.rs:97:16 clippy::unusual_byte_groupings "digits of hex or binary literal not grouped by four" -ripgrep-12.1.1/build.rs:133:19 clippy::option_as_ref_deref "called `.as_ref().map(|x| &**x)` on an Option value. This can be done more directly by calling `githash.as_deref()` instead" -ripgrep-12.1.1/build.rs:18:18 clippy::single_match_else "you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let`" -ripgrep-12.1.1/build.rs:225:14 clippy::redundant_closure_for_method_calls "redundant closure found" -ripgrep-12.1.1/build.rs:92:19 clippy::option_as_ref_deref "called `.as_ref().map(|x| &**x)` on an Option value. This can be done more directly by calling `githash.as_deref()` instead" -ripgrep-12.1.1/crates/core/app.rs:1408:5 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope" -ripgrep-12.1.1/crates/core/app.rs:1408:5 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope" -ripgrep-12.1.1/crates/core/app.rs:1409:5 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope" -ripgrep-12.1.1/crates/core/app.rs:1409:5 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope" -ripgrep-12.1.1/crates/core/app.rs:152:32 clippy::doc_markdown "you should put `clap::Arg` between ticks in the documentation" -ripgrep-12.1.1/crates/core/app.rs:152:32 clippy::doc_markdown "you should put `clap::Arg` between ticks in the documentation" -ripgrep-12.1.1/crates/core/app.rs:156:39 clippy::doc_markdown "you should put `clap::Arg` between ticks in the documentation" -ripgrep-12.1.1/crates/core/app.rs:156:39 clippy::doc_markdown "you should put `clap::Arg` between ticks in the documentation" -ripgrep-12.1.1/crates/core/app.rs:156:5 clippy::doc_markdown "you should put `RGArg` between ticks in the documentation" -ripgrep-12.1.1/crates/core/app.rs:156:5 clippy::doc_markdown "you should put `RGArg` between ticks in the documentation" -ripgrep-12.1.1/crates/core/app.rs:164:12 clippy::upper_case_acronyms "name `RGArg` contains a capitalized acronym" -ripgrep-12.1.1/crates/core/app.rs:164:12 clippy::upper_case_acronyms "name `RGArg` contains a capitalized acronym" -ripgrep-12.1.1/crates/core/app.rs:1668:5 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope" -ripgrep-12.1.1/crates/core/app.rs:1668:5 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope" -ripgrep-12.1.1/crates/core/app.rs:1669:5 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope" -ripgrep-12.1.1/crates/core/app.rs:1669:5 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope" -ripgrep-12.1.1/crates/core/app.rs:1821:5 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope" -ripgrep-12.1.1/crates/core/app.rs:1821:5 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope" -ripgrep-12.1.1/crates/core/app.rs:1822:5 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope" -ripgrep-12.1.1/crates/core/app.rs:1822:5 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope" -ripgrep-12.1.1/crates/core/app.rs:212:10 clippy::upper_case_acronyms "name `RGArgKind` contains a capitalized acronym" -ripgrep-12.1.1/crates/core/app.rs:212:10 clippy::upper_case_acronyms "name `RGArgKind` contains a capitalized acronym" -ripgrep-12.1.1/crates/core/app.rs:2999:5 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope" -ripgrep-12.1.1/crates/core/app.rs:2999:5 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope" -ripgrep-12.1.1/crates/core/app.rs:3000:5 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope" -ripgrep-12.1.1/crates/core/app.rs:3000:5 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope" -ripgrep-12.1.1/crates/core/app.rs:367:54 clippy::match_same_arms "this `match` has identical arm bodies" -ripgrep-12.1.1/crates/core/app.rs:367:54 clippy::match_same_arms "this `match` has identical arm bodies" -ripgrep-12.1.1/crates/core/app.rs:414:59 clippy::match_same_arms "this `match` has identical arm bodies" -ripgrep-12.1.1/crates/core/app.rs:414:59 clippy::match_same_arms "this `match` has identical arm bodies" -ripgrep-12.1.1/crates/core/app.rs:417:57 clippy::match_same_arms "this `match` has identical arm bodies" -ripgrep-12.1.1/crates/core/app.rs:417:57 clippy::match_same_arms "this `match` has identical arm bodies" -ripgrep-12.1.1/crates/core/app.rs:417:57 clippy::match_same_arms "this `match` has identical arm bodies" -ripgrep-12.1.1/crates/core/app.rs:417:57 clippy::match_same_arms "this `match` has identical arm bodies" -ripgrep-12.1.1/crates/core/app.rs:75:9 clippy::doc_markdown "you should put `RIPGREP_BUILD_GIT_HASH` between ticks in the documentation" -ripgrep-12.1.1/crates/core/app.rs:75:9 clippy::doc_markdown "you should put `RIPGREP_BUILD_GIT_HASH` between ticks in the documentation" -ripgrep-12.1.1/crates/core/app.rs:87:5 clippy::if_not_else "unnecessary boolean `not` operation" -ripgrep-12.1.1/crates/core/app.rs:87:5 clippy::if_not_else "unnecessary boolean `not` operation" -ripgrep-12.1.1/crates/core/args.rs:1143:22 clippy::unused_self "unused `self` argument" -ripgrep-12.1.1/crates/core/args.rs:11:1 clippy::single_component_path_imports "this import is redundant" -ripgrep-12.1.1/crates/core/args.rs:1209:74 clippy::if_same_then_else "this `if` has identical blocks" -ripgrep-12.1.1/crates/core/args.rs:1282:13 clippy::similar_names "binding's name is too similar to existing binding" -ripgrep-12.1.1/crates/core/args.rs:1430:22 clippy::unused_self "unused `self` argument" -ripgrep-12.1.1/crates/core/args.rs:1438:21 clippy::doc_markdown "you should put `OsStr` between ticks in the documentation" -ripgrep-12.1.1/crates/core/args.rs:1520:44 clippy::redundant_closure_for_method_calls "redundant closure found" -ripgrep-12.1.1/crates/core/args.rs:1524:5 clippy::unnecessary_wraps "this function's return value is unnecessarily wrapped by `Result`" -ripgrep-12.1.1/crates/core/args.rs:1635:14 clippy::doc_markdown "you should put `values_of_lossy` between ticks in the documentation" -ripgrep-12.1.1/crates/core/args.rs:1693:41 clippy::redundant_closure_for_method_calls "redundant closure found" -ripgrep-12.1.1/crates/core/args.rs:1770:17 clippy::cast_possible_truncation "casting `u64` to `usize` may truncate the value on targets with 32-bit wide pointers" -ripgrep-12.1.1/crates/core/args.rs:1829:5 clippy::let_underscore_drop "non-binding `let` on a type that implements `Drop`" -ripgrep-12.1.1/crates/core/args.rs:287:13 clippy::similar_names "binding's name is too similar to existing binding" -ripgrep-12.1.1/crates/core/args.rs:33:1 clippy::single_component_path_imports "this import is redundant" -ripgrep-12.1.1/crates/core/args.rs:34:1 clippy::single_component_path_imports "this import is redundant" -ripgrep-12.1.1/crates/core/args.rs:35:1 clippy::single_component_path_imports "this import is redundant" -ripgrep-12.1.1/crates/core/args.rs:369:5 clippy::upper_case_acronyms "name `JSON` contains a capitalized acronym" -ripgrep-12.1.1/crates/core/args.rs:410:14 clippy::trivially_copy_pass_by_ref "this argument (2 byte) is passed by reference, but would be more efficient if passed by value (limit: 8 byte)" -ripgrep-12.1.1/crates/core/args.rs:475:18 clippy::match_same_arms "this `match` has identical arm bodies" -ripgrep-12.1.1/crates/core/args.rs:512:19 clippy::doc_markdown "you should put `ArgMatches` between ticks in the documentation" -ripgrep-12.1.1/crates/core/args.rs:549:16 clippy::wrong_self_convention "methods called `to_*` usually take self by reference; consider choosing a less ambiguous name" -ripgrep-12.1.1/crates/core/args.rs:71:5 clippy::upper_case_acronyms "name `PCRE2Version` contains a capitalized acronym" -ripgrep-12.1.1/crates/core/args.rs:76:18 clippy::trivially_copy_pass_by_ref "this argument (1 byte) is passed by reference, but would be more efficient if passed by value (limit: 8 byte)" -ripgrep-12.1.1/crates/core/args.rs:77:13 clippy::enum_glob_use "usage of wildcard import for enum variants" -ripgrep-12.1.1/crates/core/args.rs:923:42 clippy::doc_markdown "you should put `BinaryDetection::quit` between ticks in the documentation" -ripgrep-12.1.1/crates/core/config.rs:13:1 clippy::single_component_path_imports "this import is redundant" -ripgrep-12.1.1/crates/core/config.rs:58:6 clippy::type_complexity "very complex type used. Consider factoring parts into `type` definitions" -ripgrep-12.1.1/crates/core/config.rs:79:6 clippy::type_complexity "very complex type used. Consider factoring parts into `type` definitions" -ripgrep-12.1.1/crates/core/logger.rs:11:30 clippy::doc_markdown "you should put `max_level` between ticks in the documentation" -ripgrep-12.1.1/crates/core/logger.rs:15:16 clippy::redundant_static_lifetimes "constants have by default a `'static` lifetime" -ripgrep-12.1.1/crates/core/main.rs:114:9 clippy::let_underscore_drop "non-binding `let` on a type that implements `Drop`" -ripgrep-12.1.1/crates/core/main.rs:189:9 clippy::let_underscore_drop "non-binding `let` on a type that implements `Drop`" -ripgrep-12.1.1/crates/core/main.rs:55:19 clippy::needless_pass_by_value "this argument is passed by value, but not consumed in the function body" -ripgrep-12.1.1/crates/core/main.rs:56:9 clippy::enum_glob_use "usage of wildcard import for enum variants" -ripgrep-12.1.1/crates/core/messages.rs:46:1 clippy::module_name_repetitions "item name ends with its containing module's name" -ripgrep-12.1.1/crates/core/messages.rs:51:1 clippy::module_name_repetitions "item name ends with its containing module's name" -ripgrep-12.1.1/crates/core/messages.rs:62:1 clippy::module_name_repetitions "item name ends with its containing module's name" -ripgrep-12.1.1/crates/core/path_printer.rs:27:1 clippy::module_name_repetitions "item name starts with its containing module's name" -ripgrep-12.1.1/crates/core/path_printer.rs:89:9 clippy::if_not_else "unnecessary boolean `not` operation" -ripgrep-12.1.1/crates/core/search.rs:185:1 clippy::module_name_repetitions "item name starts with its containing module's name" -ripgrep-12.1.1/crates/core/search.rs:224:5 clippy::upper_case_acronyms "name `JSON` contains a capitalized acronym" -ripgrep-12.1.1/crates/core/search.rs:292:9 clippy::write_with_newline "using `write!()` with a format string that ends in a single newline" -ripgrep-12.1.1/crates/core/search.rs:311:1 clippy::module_name_repetitions "item name starts with its containing module's name" -ripgrep-12.1.1/crates/core/search.rs:377:12 clippy::nonminimal_bool "this boolean expression can be simplified" -ripgrep-12.1.1/crates/core/search.rs:423:13 clippy::enum_glob_use "usage of wildcard import for enum variants" -ripgrep-12.1.1/crates/core/search.rs:447:13 clippy::enum_glob_use "usage of wildcard import for enum variants" -ripgrep-12.1.1/crates/core/search.rs:472:24 clippy::map_clone "you are using an explicit closure for cloning elements" -ripgrep-12.1.1/crates/core/search.rs:472:41 clippy::redundant_closure_for_method_calls "redundant closure found" -ripgrep-12.1.1/crates/core/search.rs:480:24 clippy::map_clone "you are using an explicit closure for cloning elements" -ripgrep-12.1.1/crates/core/search.rs:480:41 clippy::redundant_closure_for_method_calls "redundant closure found" -ripgrep-12.1.1/crates/core/search.rs:49:1 clippy::module_name_repetitions "item name starts with its containing module's name" -ripgrep-12.1.1/crates/core/search.rs:509:24 clippy::map_clone "you are using an explicit closure for cloning elements" -ripgrep-12.1.1/crates/core/search.rs:509:41 clippy::redundant_closure_for_method_calls "redundant closure found" -ripgrep-12.1.1/crates/core/search.rs:517:24 clippy::map_clone "you are using an explicit closure for cloning elements" -ripgrep-12.1.1/crates/core/search.rs:517:41 clippy::redundant_closure_for_method_calls "redundant closure found" -ripgrep-12.1.1/crates/core/search.rs:533:36 clippy::cast_lossless "casting `u32` to `f64` may become silently lossy if you later change the type" -ripgrep-12.1.1/crates/core/search.rs:533:5 clippy::cast_precision_loss "casting `u64` to `f64` causes a loss of precision (`u64` is 64 bits wide, but `f64`'s mantissa is only 52 bits wide)" -ripgrep-12.1.1/crates/core/subject.rs:20:1 clippy::module_name_repetitions "item name starts with its containing module's name" -ripgrep-12.1.1/crates/core/subject.rs:4:1 clippy::single_component_path_imports "this import is redundant" -serde-1.0.118/src/de/mod.rs:1592:9 clippy::let_underscore_drop "non-binding `let` on a type that implements `Drop`" -serde-1.0.118/src/de/mod.rs:1616:9 clippy::let_underscore_drop "non-binding `let` on a type that implements `Drop`" -serde-1.0.118/src/de/mod.rs:1627:9 clippy::let_underscore_drop "non-binding `let` on a type that implements `Drop`" -serde-1.0.118/src/de/mod.rs:1638:9 clippy::let_underscore_drop "non-binding `let` on a type that implements `Drop`" -serde-1.0.118/src/de/mod.rs:1649:9 clippy::let_underscore_drop "non-binding `let` on a type that implements `Drop`" -serde-1.0.118/src/de/mod.rs:952:13 clippy::let_underscore_drop "non-binding `let` on a type that implements `Drop`" -serde-1.0.118/src/de/mod.rs:986:13 clippy::let_underscore_drop "non-binding `let` on a type that implements `Drop`" -syn-1.0.54/build.rs:1:null clippy::cargo_common_metadata "package `syn` is missing `package.keywords` metadata" -syn-1.0.54/src/lib.rs:1:null clippy::cargo_common_metadata "package `syn` is missing `package.keywords` metadata" -syn-1.0.54/src/lit.rs:1397:40 clippy::redundant_else "redundant else block" -syn-1.0.54/src/lit.rs:1405:28 clippy::redundant_else "redundant else block" -syn-1.0.54/src/lit.rs:1485:32 clippy::redundant_else "redundant else block" -unicode-xid-0.2.1/src/lib.rs:1:null clippy::cargo_common_metadata "package `unicode-xid` is missing `package.categories` metadata" -unicode-xid-0.2.1/src/lib.rs:56:11 clippy::upper_case_acronyms "name `UnicodeXID` contains a capitalized acronym" -unicode-xid-0.2.1/src/lib.rs:57:64 clippy::doc_markdown "you should put `XID_Start` between ticks in the documentation" -unicode-xid-0.2.1/src/lib.rs:60:10 clippy::doc_markdown "you should put `XID_Start` between ticks in the documentation" -unicode-xid-0.2.1/src/lib.rs:62:27 clippy::doc_markdown "you should put `ID_Start` between ticks in the documentation" -unicode-xid-0.2.1/src/lib.rs:62:67 clippy::doc_markdown "you should put `NFKx` between ticks in the documentation" -unicode-xid-0.2.1/src/lib.rs:63:21 clippy::wrong_self_convention "methods called `is_*` usually take self by reference or no self; consider choosing a less ambiguous name" -unicode-xid-0.2.1/src/lib.rs:65:61 clippy::doc_markdown "you should put `XID_Continue` between ticks in the documentation" -unicode-xid-0.2.1/src/lib.rs:68:10 clippy::doc_markdown "you should put `XID_Continue` between ticks in the documentation" -unicode-xid-0.2.1/src/lib.rs:70:28 clippy::doc_markdown "you should put `ID_Continue` between ticks in the documentation" -unicode-xid-0.2.1/src/lib.rs:70:72 clippy::doc_markdown "you should put `NFKx` between ticks in the documentation" -unicode-xid-0.2.1/src/lib.rs:71:24 clippy::wrong_self_convention "methods called `is_*` usually take self by reference or no self; consider choosing a less ambiguous name" -xsv-0.13.0/src/cmd/cat.rs:101:34 clippy::redundant_closure_for_method_calls "redundant closure found" -xsv-0.13.0/src/cmd/cat.rs:42:1 clippy::struct_excessive_bools "more than 3 bools in a struct" -xsv-0.13.0/src/cmd/cat.rs:53:9 clippy::similar_names "binding's name is too similar to existing binding" -xsv-0.13.0/src/cmd/cat.rs:7:16 clippy::redundant_static_lifetimes "statics have by default a `'static` lifetime" -xsv-0.13.0/src/cmd/count.rs:32:9 clippy::similar_names "binding's name is too similar to existing binding" -xsv-0.13.0/src/cmd/count.rs:38:9 clippy::single_match_else "you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let`" -xsv-0.13.0/src/cmd/count.rs:42:33 clippy::unseparated_literal_suffix "integer type suffix should be separated by an underscore" -xsv-0.13.0/src/cmd/count.rs:7:16 clippy::redundant_static_lifetimes "statics have by default a `'static` lifetime" -xsv-0.13.0/src/cmd/fixlengths.rs:45:9 clippy::similar_names "binding's name is too similar to existing binding" -xsv-0.13.0/src/cmd/fixlengths.rs:50:18 clippy::single_match_else "you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let`" -xsv-0.13.0/src/cmd/fixlengths.rs:62:30 clippy::unseparated_literal_suffix "integer type suffix should be separated by an underscore" -xsv-0.13.0/src/cmd/fixlengths.rs:9:16 clippy::redundant_static_lifetimes "statics have by default a `'static` lifetime" -xsv-0.13.0/src/cmd/flatten.rs:10:16 clippy::redundant_static_lifetimes "statics have by default a `'static` lifetime" -xsv-0.13.0/src/cmd/flatten.rs:51:9 clippy::similar_names "binding's name is too similar to existing binding" -xsv-0.13.0/src/cmd/fmt.rs:50:9 clippy::similar_names "binding's name is too similar to existing binding" -xsv-0.13.0/src/cmd/fmt.rs:55:13 clippy::similar_names "binding's name is too similar to existing binding" -xsv-0.13.0/src/cmd/fmt.rs:7:16 clippy::redundant_static_lifetimes "statics have by default a `'static` lifetime" -xsv-0.13.0/src/cmd/frequency.rs:148:43 clippy::cast_possible_truncation "casting `u64` to `usize` may truncate the value on targets with 32-bit wide pointers" -xsv-0.13.0/src/cmd/frequency.rs:149:43 clippy::cast_possible_truncation "casting `u64` to `usize` may truncate the value on targets with 32-bit wide pointers" -xsv-0.13.0/src/cmd/frequency.rs:15:16 clippy::redundant_static_lifetimes "statics have by default a `'static` lifetime" -xsv-0.13.0/src/cmd/frequency.rs:169:13 clippy::similar_names "binding's name is too similar to existing binding" -xsv-0.13.0/src/cmd/frequency.rs:176:17 clippy::if_not_else "unnecessary boolean `not` operation" -xsv-0.13.0/src/cmd/frequency.rs:178:24 clippy::collapsible_else_if "this `else { if .. }` block can be collapsed" -xsv-0.13.0/src/cmd/frequency.rs:77:9 clippy::similar_names "binding's name is too similar to existing binding" -xsv-0.13.0/src/cmd/frequency.rs:93:31 clippy::explicit_into_iter_loop "it is more concise to loop over containers instead of using explicit iteration methods" -xsv-0.13.0/src/cmd/headers.rs:43:9 clippy::similar_names "binding's name is too similar to existing binding" -xsv-0.13.0/src/cmd/headers.rs:49:17 clippy::explicit_into_iter_loop "it is more concise to loop over containers instead of using explicit iteration methods" -xsv-0.13.0/src/cmd/headers.rs:9:16 clippy::redundant_static_lifetimes "statics have by default a `'static` lifetime" -xsv-0.13.0/src/cmd/index.rs:11:16 clippy::redundant_static_lifetimes "statics have by default a `'static` lifetime" -xsv-0.13.0/src/cmd/index.rs:45:9 clippy::similar_names "binding's name is too similar to existing binding" -xsv-0.13.0/src/cmd/input.rs:42:9 clippy::similar_names "binding's name is too similar to existing binding" -xsv-0.13.0/src/cmd/input.rs:47:9 clippy::similar_names "binding's name is too similar to existing binding" -xsv-0.13.0/src/cmd/input.rs:7:16 clippy::redundant_static_lifetimes "statics have by default a `'static` lifetime" -xsv-0.13.0/src/cmd/join.rs:17:16 clippy::redundant_static_lifetimes "statics have by default a `'static` lifetime" -xsv-0.13.0/src/cmd/join.rs:194:29 clippy::similar_names "binding's name is too similar to existing binding" -xsv-0.13.0/src/cmd/join.rs:224:22 clippy::similar_names "binding's name is too similar to existing binding" -xsv-0.13.0/src/cmd/join.rs:293:14 clippy::similar_names "binding's name is too similar to existing binding" -xsv-0.13.0/src/cmd/join.rs:293:20 clippy::similar_names "binding's name is too similar to existing binding" -xsv-0.13.0/src/cmd/join.rs:297:13 clippy::redundant_field_names "redundant field names in struct initialization" -xsv-0.13.0/src/cmd/join.rs:298:13 clippy::redundant_field_names "redundant field names in struct initialization" -xsv-0.13.0/src/cmd/join.rs:299:13 clippy::redundant_field_names "redundant field names in struct initialization" -xsv-0.13.0/src/cmd/join.rs:300:13 clippy::redundant_field_names "redundant field names in struct initialization" -xsv-0.13.0/src/cmd/join.rs:308:9 clippy::unused_self "unused `self` argument" -xsv-0.13.0/src/cmd/join.rs:342:38 clippy::unseparated_literal_suffix "integer type suffix should be separated by an underscore" -xsv-0.13.0/src/cmd/join.rs:342:46 clippy::unseparated_literal_suffix "integer type suffix should be separated by an underscore" -xsv-0.13.0/src/cmd/join.rs:347:9 clippy::if_not_else "unnecessary boolean `not` operation" -xsv-0.13.0/src/cmd/join.rs:372:44 clippy::redundant_closure_for_method_calls "redundant closure found" -xsv-0.13.0/src/cmd/join.rs:375:33 clippy::similar_names "binding's name is too similar to existing binding" -xsv-0.13.0/src/cmd/join.rs:392:13 clippy::redundant_field_names "redundant field names in struct initialization" -xsv-0.13.0/src/cmd/join.rs:403:29 clippy::explicit_into_iter_loop "it is more concise to loop over containers instead of using explicit iteration methods" -xsv-0.13.0/src/cmd/join.rs:426:13 clippy::if_not_else "unnecessary boolean `not` operation" -xsv-0.13.0/src/cmd/join.rs:77:1 clippy::struct_excessive_bools "more than 3 bools in a struct" -xsv-0.13.0/src/cmd/join.rs:94:9 clippy::similar_names "binding's name is too similar to existing binding" -xsv-0.13.0/src/cmd/partition.rs:105:22 clippy::similar_names "binding's name is too similar to existing binding" -xsv-0.13.0/src/cmd/partition.rs:106:22 clippy::redundant_slicing "redundant slicing of the whole range" -xsv-0.13.0/src/cmd/partition.rs:139:13 clippy::redundant_field_names "redundant field names in struct initialization" -xsv-0.13.0/src/cmd/partition.rs:15:16 clippy::redundant_static_lifetimes "statics have by default a `'static` lifetime" -xsv-0.13.0/src/cmd/partition.rs:169:9 clippy::if_not_else "unnecessary boolean `not` operation" -xsv-0.13.0/src/cmd/partition.rs:56:9 clippy::similar_names "binding's name is too similar to existing binding" -xsv-0.13.0/src/cmd/partition.rs:77:9 clippy::unused_self "unused `self` argument" -xsv-0.13.0/src/cmd/sample.rs:105:44 clippy::cast_possible_truncation "casting `u64` to `usize` may truncate the value on targets with 32-bit wide pointers" -xsv-0.13.0/src/cmd/sample.rs:115:21 clippy::cast_possible_truncation "casting `u64` to `usize` may truncate the value on targets with 32-bit wide pointers" -xsv-0.13.0/src/cmd/sample.rs:11:16 clippy::redundant_static_lifetimes "statics have by default a `'static` lifetime" -xsv-0.13.0/src/cmd/sample.rs:51:9 clippy::similar_names "binding's name is too similar to existing binding" -xsv-0.13.0/src/cmd/sample.rs:58:19 clippy::single_match_else "you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let`" -xsv-0.13.0/src/cmd/sample.rs:69:9 clippy::match_wildcard_for_single_variants "wildcard match will miss any future added variants" -xsv-0.13.0/src/cmd/sample.rs:75:16 clippy::explicit_into_iter_loop "it is more concise to loop over containers instead of using explicit iteration methods" -xsv-0.13.0/src/cmd/sample.rs:91:42 clippy::cast_possible_truncation "casting `u64` to `usize` may truncate the value on targets with 32-bit wide pointers" -xsv-0.13.0/src/cmd/sample.rs:92:43 clippy::cast_possible_truncation "casting `u64` to `usize` may truncate the value on targets with 32-bit wide pointers" -xsv-0.13.0/src/cmd/search.rs:51:9 clippy::similar_names "binding's name is too similar to existing binding" -xsv-0.13.0/src/cmd/search.rs:9:16 clippy::redundant_static_lifetimes "statics have by default a `'static` lifetime" -xsv-0.13.0/src/cmd/select.rs:60:9 clippy::similar_names "binding's name is too similar to existing binding" -xsv-0.13.0/src/cmd/select.rs:8:16 clippy::redundant_static_lifetimes "statics have by default a `'static` lifetime" -xsv-0.13.0/src/cmd/slice.rs:57:9 clippy::similar_names "binding's name is too similar to existing binding" -xsv-0.13.0/src/cmd/slice.rs:9:16 clippy::redundant_static_lifetimes "statics have by default a `'static` lifetime" -xsv-0.13.0/src/cmd/sort.rs:11:16 clippy::redundant_static_lifetimes "statics have by default a `'static` lifetime" -xsv-0.13.0/src/cmd/sort.rs:138:47 clippy::cast_precision_loss "casting `i64` to `f64` causes a loss of precision (`i64` is 64 bits wide, but `f64`'s mantissa is only 52 bits wide)" -xsv-0.13.0/src/cmd/sort.rs:139:51 clippy::cast_precision_loss "casting `i64` to `f64` causes a loss of precision (`i64` is 64 bits wide, but `f64`'s mantissa is only 52 bits wide)" -xsv-0.13.0/src/cmd/sort.rs:48:9 clippy::similar_names "binding's name is too similar to existing binding" -xsv-0.13.0/src/cmd/sort.rs:91:14 clippy::explicit_into_iter_loop "it is more concise to loop over containers instead of using explicit iteration methods" -xsv-0.13.0/src/cmd/split.rs:14:16 clippy::redundant_static_lifetimes "statics have by default a `'static` lifetime" -xsv-0.13.0/src/cmd/split.rs:61:9 clippy::similar_names "binding's name is too similar to existing binding" -xsv-0.13.0/src/cmd/split.rs:94:5 clippy::unnecessary_wraps "this function's return value is unnecessary" -xsv-0.13.0/src/cmd/split.rs:96:14 clippy::needless_pass_by_value "this argument is passed by value, but not consumed in the function body" -xsv-0.13.0/src/cmd/split.rs:99:13 clippy::cast_possible_truncation "casting `u64` to `usize` may truncate the value on targets with 32-bit wide pointers" -xsv-0.13.0/src/cmd/stats.rs:110:36 clippy::redundant_closure_for_method_calls "redundant closure found" -xsv-0.13.0/src/cmd/stats.rs:127:14 clippy::needless_pass_by_value "this argument is passed by value, but not consumed in the function body" -xsv-0.13.0/src/cmd/stats.rs:138:43 clippy::cast_possible_truncation "casting `u64` to `usize` may truncate the value on targets with 32-bit wide pointers" -xsv-0.13.0/src/cmd/stats.rs:139:43 clippy::cast_possible_truncation "casting `u64` to `usize` may truncate the value on targets with 32-bit wide pointers" -xsv-0.13.0/src/cmd/stats.rs:162:25 clippy::explicit_into_iter_loop "it is more concise to loop over containers instead of using explicit iteration methods" -xsv-0.13.0/src/cmd/stats.rs:22:16 clippy::redundant_static_lifetimes "statics have by default a `'static` lifetime" -xsv-0.13.0/src/cmd/stats.rs:231:1 clippy::struct_excessive_bools "more than 3 bools in a struct" -xsv-0.13.0/src/cmd/stats.rs:262:35 clippy::default_trait_access "calling `cmd::stats::TypedSum::default()` is more clear than this expression" -xsv-0.13.0/src/cmd/stats.rs:263:40 clippy::default_trait_access "calling `cmd::stats::TypedMinMax::default()` is more clear than this expression" -xsv-0.13.0/src/cmd/stats.rs:264:39 clippy::default_trait_access "calling `stats::OnlineStats::default()` is more clear than this expression" -xsv-0.13.0/src/cmd/stats.rs:265:58 clippy::default_trait_access "calling `stats::Unsorted::default()` is more clear than this expression" -xsv-0.13.0/src/cmd/stats.rs:266:41 clippy::default_trait_access "calling `stats::Unsorted::default()` is more clear than this expression" -xsv-0.13.0/src/cmd/stats.rs:268:18 clippy::default_trait_access "calling `cmd::stats::FieldType::default()` is more clear than this expression" -xsv-0.13.0/src/cmd/stats.rs:269:13 clippy::redundant_field_names "redundant field names in struct initialization" -xsv-0.13.0/src/cmd/stats.rs:270:13 clippy::redundant_field_names "redundant field names in struct initialization" -xsv-0.13.0/src/cmd/stats.rs:271:13 clippy::redundant_field_names "redundant field names in struct initialization" -xsv-0.13.0/src/cmd/stats.rs:272:13 clippy::redundant_field_names "redundant field names in struct initialization" -xsv-0.13.0/src/cmd/stats.rs:273:13 clippy::redundant_field_names "redundant field names in struct initialization" -xsv-0.13.0/src/cmd/stats.rs:274:13 clippy::redundant_field_names "redundant field names in struct initialization" -xsv-0.13.0/src/cmd/stats.rs:283:9 clippy::option_map_unit_fn "called `map(f)` on an `Option` value where `f` is a closure that returns the unit type `()`" -xsv-0.13.0/src/cmd/stats.rs:284:9 clippy::option_map_unit_fn "called `map(f)` on an `Option` value where `f` is a closure that returns the unit type `()`" -xsv-0.13.0/src/cmd/stats.rs:285:9 clippy::option_map_unit_fn "called `map(f)` on an `Option` value where `f` is a closure that returns the unit type `()`" -xsv-0.13.0/src/cmd/stats.rs:290:21 clippy::option_map_unit_fn "called `map(f)` on an `Option` value where `f` is a closure that returns the unit type `()`" -xsv-0.13.0/src/cmd/stats.rs:293:25 clippy::match_same_arms "this `match` has identical arm bodies" -xsv-0.13.0/src/cmd/stats.rs:297:25 clippy::option_map_unit_fn "called `map(f)` on an `Option` value where `f` is a closure that returns the unit type `()`" -xsv-0.13.0/src/cmd/stats.rs:301:21 clippy::option_map_unit_fn "called `map(f)` on an `Option` value where `f` is a closure that returns the unit type `()`" -xsv-0.13.0/src/cmd/stats.rs:302:21 clippy::option_map_unit_fn "called `map(f)` on an `Option` value where `f` is a closure that returns the unit type `()`" -xsv-0.13.0/src/cmd/stats.rs:308:18 clippy::wrong_self_convention "methods called `to_*` usually take self by reference; consider choosing a less ambiguous name" -xsv-0.13.0/src/cmd/stats.rs:318:9 clippy::single_match_else "you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let`" -xsv-0.13.0/src/cmd/stats.rs:322:45 clippy::redundant_closure_for_method_calls "redundant closure found" -xsv-0.13.0/src/cmd/stats.rs:322:9 clippy::single_match_else "you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let`" -xsv-0.13.0/src/cmd/stats.rs:327:9 clippy::if_not_else "unnecessary boolean `not` operation" -xsv-0.13.0/src/cmd/stats.rs:330:13 clippy::single_match_else "you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let`" -xsv-0.13.0/src/cmd/stats.rs:338:45 clippy::redundant_closure_for_method_calls "redundant closure found" -xsv-0.13.0/src/cmd/stats.rs:402:16 clippy::redundant_pattern_matching "redundant pattern matching, consider using `is_ok()`" -xsv-0.13.0/src/cmd/stats.rs:403:16 clippy::redundant_pattern_matching "redundant pattern matching, consider using `is_ok()`" -xsv-0.13.0/src/cmd/stats.rs:407:18 clippy::trivially_copy_pass_by_ref "this argument (1 byte) is passed by reference, but would be more efficient if passed by value (limit: 8 byte)" -xsv-0.13.0/src/cmd/stats.rs:411:16 clippy::trivially_copy_pass_by_ref "this argument (1 byte) is passed by reference, but would be more efficient if passed by value (limit: 8 byte)" -xsv-0.13.0/src/cmd/stats.rs:427:56 clippy::match_same_arms "this `match` has identical arm bodies" -xsv-0.13.0/src/cmd/stats.rs:429:56 clippy::match_same_arms "this `match` has identical arm bodies" -xsv-0.13.0/src/cmd/stats.rs:430:60 clippy::match_same_arms "this `match` has identical arm bodies" -xsv-0.13.0/src/cmd/stats.rs:430:60 clippy::match_same_arms "this `match` has identical arm bodies" -xsv-0.13.0/src/cmd/stats.rs:454:5 clippy::doc_markdown "you should put `TypedSum` between ticks in the documentation" -xsv-0.13.0/src/cmd/stats.rs:473:43 clippy::cast_precision_loss "casting `i64` to `f64` causes a loss of precision (`i64` is 64 bits wide, but `f64`'s mantissa is only 52 bits wide)" -xsv-0.13.0/src/cmd/stats.rs:504:56 clippy::cast_precision_loss "casting `i64` to `f64` causes a loss of precision (`i64` is 64 bits wide, but `f64`'s mantissa is only 52 bits wide)" -xsv-0.13.0/src/cmd/stats.rs:505:51 clippy::cast_precision_loss "casting `i64` to `f64` causes a loss of precision (`i64` is 64 bits wide, but `f64`'s mantissa is only 52 bits wide)" -xsv-0.13.0/src/cmd/stats.rs:511:5 clippy::doc_markdown "you should put `TypedMinMax` between ticks in the documentation" -xsv-0.13.0/src/cmd/stats.rs:536:35 clippy::cast_possible_truncation "casting `f64` to `i64` may truncate the value" -xsv-0.13.0/src/cmd/stats.rs:544:33 clippy::cast_precision_loss "casting `i64` to `f64` causes a loss of precision (`i64` is 64 bits wide, but `f64`'s mantissa is only 52 bits wide)" -xsv-0.13.0/src/cmd/stats.rs:592:22 clippy::default_trait_access "calling `stats::MinMax::default()` is more clear than this expression" -xsv-0.13.0/src/cmd/stats.rs:593:22 clippy::default_trait_access "calling `stats::MinMax::default()` is more clear than this expression" -xsv-0.13.0/src/cmd/stats.rs:594:23 clippy::default_trait_access "calling `stats::MinMax::default()` is more clear than this expression" -xsv-0.13.0/src/cmd/stats.rs:595:21 clippy::default_trait_access "calling `stats::MinMax::default()` is more clear than this expression" -xsv-0.13.0/src/cmd/stats.rs:71:1 clippy::struct_excessive_bools "more than 3 bools in a struct" -xsv-0.13.0/src/cmd/stats.rs:86:9 clippy::similar_names "binding's name is too similar to existing binding" -xsv-0.13.0/src/cmd/table.rs:10:16 clippy::redundant_static_lifetimes "statics have by default a `'static` lifetime" -xsv-0.13.0/src/cmd/table.rs:50:9 clippy::similar_names "binding's name is too similar to existing binding" -xsv-0.13.0/src/cmd/table.rs:54:9 clippy::similar_names "binding's name is too similar to existing binding" -xsv-0.13.0/src/config.rs:113:43 clippy::or_fun_call "use of `unwrap_or` followed by a function call" -xsv-0.13.0/src/config.rs:58:1 clippy::struct_excessive_bools "more than 3 bools in a struct" -xsv-0.13.0/src/config.rs:77:28 clippy::explicit_deref_methods "explicit deref method call" -xsv-0.13.0/src/config.rs:90:13 clippy::redundant_field_names "redundant field names in struct initialization" -xsv-0.13.0/src/index.rs:31:13 clippy::redundant_field_names "redundant field names in struct initialization" -xsv-0.13.0/src/main.rs:164:49 clippy::redundant_clone "redundant clone" -xsv-0.13.0/src/main.rs:1:null clippy::cargo_common_metadata "package `xsv` is missing `package.categories` metadata" -xsv-0.13.0/src/main.rs:1:null clippy::multiple_crate_versions "multiple versions for dependency `rand_core`: 0.3.1, 0.4.2" -xsv-0.13.0/src/main.rs:1:null clippy::multiple_crate_versions "multiple versions for dependency `rand`: 0.3.23, 0.4.6" -xsv-0.13.0/src/main.rs:75:16 clippy::redundant_static_lifetimes "statics have by default a `'static` lifetime" -xsv-0.13.0/src/select.rs:13:1 clippy::module_name_repetitions "item name starts with its containing module's name" -xsv-0.13.0/src/select.rs:154:5 clippy::unnecessary_wraps "this function's return value is unnecessarily wrapped by `Result`" -xsv-0.13.0/src/select.rs:250:33 clippy::similar_names "binding's name is too similar to existing binding" -xsv-0.13.0/src/select.rs:250:43 clippy::similar_names "binding's name is too similar to existing binding" -xsv-0.13.0/src/select.rs:255:39 clippy::range_plus_one "an inclusive range would be more readable" -xsv-0.13.0/src/select.rs:280:20 clippy::len_zero "length comparison to zero" -xsv-0.13.0/src/select.rs:29:13 clippy::redundant_field_names "redundant field names in struct initialization" -xsv-0.13.0/src/select.rs:360:62 clippy::trivially_copy_pass_by_ref "this argument (8 byte) is passed by reference, but would be more efficient if passed by value (limit: 8 byte)" -xsv-0.13.0/src/select.rs:360:9 clippy::unnecessary_wraps "this function's return value is unnecessarily wrapped by `Option`" -xsv-0.13.0/src/select.rs:375:9 clippy::stable_sort_primitive "used `sort` on primitive type `usize`" -xsv-0.13.0/src/select.rs:379:18 clippy::explicit_into_iter_loop "it is more concise to loop over containers instead of using explicit iteration methods" -xsv-0.13.0/src/select.rs:416:5 clippy::needless_lifetimes "explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration)" -xsv-0.13.0/src/select.rs:419:9 clippy::unnecessary_wraps "this function's return value is unnecessarily wrapped by `Option`" -xsv-0.13.0/src/select.rs:420:27 clippy::option_option "consider using `Option<T>` instead of `Option<Option<T>>` or a custom enum if you need to distinguish all 3 cases" -xsv-0.13.0/src/select.rs:99:17 clippy::similar_names "binding's name is too similar to existing binding" -xsv-0.13.0/src/util.rs:150:5 clippy::doc_markdown "you should put bare URLs between `<`/`>` or make a proper Markdown link" -xsv-0.13.0/src/util.rs:37:33 clippy::map_clone "you are using an explicit closure for copying elements" -xsv-0.13.0/src/util.rs:90:1 clippy::needless_lifetimes "explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration)" +target/lintcheck/sources/anyhow-1.0.38/build.rs:1:null clippy::cargo_common_metadata "package `anyhow` is missing `package.keywords` metadata" +target/lintcheck/sources/anyhow-1.0.38/src/error.rs:350:5 clippy::missing_panics_doc "docs for function which may panic missing `# Panics` section" +target/lintcheck/sources/anyhow-1.0.38/src/lib.rs:1:null clippy::cargo_common_metadata "package `anyhow` is missing `package.keywords` metadata" +target/lintcheck/sources/async-trait-0.1.42/src/expand.rs:130:1 clippy::too_many_lines "this function has too many lines (104/100)" +target/lintcheck/sources/async-trait-0.1.42/src/expand.rs:156:26 clippy::default_trait_access "calling `syn::token::Where::default()` is more clear than this expression" +target/lintcheck/sources/async-trait-0.1.42/src/expand.rs:259:1 clippy::too_many_lines "this function has too many lines (204/100)" +target/lintcheck/sources/async-trait-0.1.42/src/expand.rs:387:17 clippy::match_wildcard_for_single_variants "wildcard matches only a single variant and will also match any future added variants" +target/lintcheck/sources/async-trait-0.1.42/src/expand.rs:414:35 clippy::shadow_unrelated "`generics` is being shadowed" +target/lintcheck/sources/async-trait-0.1.42/src/expand.rs:464:32 clippy::if_not_else "unnecessary `!=` operation" +target/lintcheck/sources/async-trait-0.1.42/src/lib.rs:102:7 clippy::doc_markdown "you should put `async_trait` between ticks in the documentation" +target/lintcheck/sources/async-trait-0.1.42/src/lib.rs:159:64 clippy::doc_markdown "you should put `async_trait` between ticks in the documentation" +target/lintcheck/sources/async-trait-0.1.42/src/lib.rs:1:null clippy::cargo_common_metadata "package `async-trait` is missing `package.categories` metadata" +target/lintcheck/sources/async-trait-0.1.42/src/lib.rs:1:null clippy::cargo_common_metadata "package `async-trait` is missing `package.keywords` metadata" +target/lintcheck/sources/async-trait-0.1.42/src/lib.rs:240:15 clippy::doc_markdown "you should put `async_trait` between ticks in the documentation" +target/lintcheck/sources/async-trait-0.1.42/src/lifetime.rs:5:1 clippy::module_name_repetitions "item name ends with its containing module's name" +target/lintcheck/sources/async-trait-0.1.42/src/receiver.rs:102:34 clippy::similar_names "binding's name is too similar to existing binding" +target/lintcheck/sources/async-trait-0.1.42/src/receiver.rs:107:29 clippy::similar_names "binding's name is too similar to existing binding" +target/lintcheck/sources/async-trait-0.1.42/src/receiver.rs:137:38 clippy::default_trait_access "calling `syn::token::Colon2::default()` is more clear than this expression" +target/lintcheck/sources/async-trait-0.1.42/src/receiver.rs:162:55 clippy::default_trait_access "calling `syn::token::Colon2::default()` is more clear than this expression" +target/lintcheck/sources/async-trait-0.1.42/src/receiver.rs:167:42 clippy::default_trait_access "calling `syn::token::Colon2::default()` is more clear than this expression" +target/lintcheck/sources/async-trait-0.1.42/src/receiver.rs:73:1 clippy::module_name_repetitions "item name ends with its containing module's name" +target/lintcheck/sources/async-trait-0.1.42/src/receiver.rs:97:34 clippy::similar_names "binding's name is too similar to existing binding" +target/lintcheck/sources/cargo-0.49.0/build.rs:1:null clippy::cargo_common_metadata "package `cargo` is missing `package.categories` metadata" +target/lintcheck/sources/cargo-0.49.0/build.rs:1:null clippy::cargo_common_metadata "package `cargo` is missing `package.keywords` metadata" +target/lintcheck/sources/cargo-0.49.0/src/bin/cargo/cli.rs:104:34 clippy::single_match_else "you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let`" +target/lintcheck/sources/cargo-0.49.0/src/bin/cargo/cli.rs:121:5 clippy::single_char_add_str "calling `push_str()` using a single-character string literal" +target/lintcheck/sources/cargo-0.49.0/src/bin/cargo/cli.rs:157:30 clippy::redundant_closure_for_method_calls "redundant closure" +target/lintcheck/sources/cargo-0.49.0/src/bin/cargo/cli.rs:184:41 clippy::cast_possible_truncation "casting `u64` to `u32` may truncate the value" +target/lintcheck/sources/cargo-0.49.0/src/bin/cargo/cli.rs:196:42 clippy::redundant_closure_for_method_calls "redundant closure" +target/lintcheck/sources/cargo-0.49.0/src/bin/cargo/cli.rs:200:39 clippy::redundant_closure_for_method_calls "redundant closure" +target/lintcheck/sources/cargo-0.49.0/src/bin/cargo/cli.rs:231:1 clippy::struct_excessive_bools "more than 3 bools in a struct" +target/lintcheck/sources/cargo-0.49.0/src/bin/cargo/cli.rs:23:56 clippy::implicit_clone "implicitly cloning a `String` by calling `to_owned` on its dereferenced type" +target/lintcheck/sources/cargo-0.49.0/src/bin/cargo/cli.rs:245:22 clippy::cast_possible_truncation "casting `u64` to `u32` may truncate the value" +target/lintcheck/sources/cargo-0.49.0/src/bin/cargo/cli.rs:247:47 clippy::redundant_closure_for_method_calls "redundant closure" +target/lintcheck/sources/cargo-0.49.0/src/bin/cargo/cli.rs:257:22 clippy::redundant_closure_for_method_calls "redundant closure" +target/lintcheck/sources/cargo-0.49.0/src/bin/cargo/cli.rs:26:20 clippy::redundant_else "redundant else block" +target/lintcheck/sources/cargo-0.49.0/src/bin/cargo/cli.rs:7:5 clippy::wildcard_imports "usage of wildcard import" +target/lintcheck/sources/cargo-0.49.0/src/bin/cargo/commands/bench.rs:1:5 clippy::wildcard_imports "usage of wildcard import" +target/lintcheck/sources/cargo-0.49.0/src/bin/cargo/commands/bench.rs:76:59 clippy::redundant_closure_for_method_calls "redundant closure" +target/lintcheck/sources/cargo-0.49.0/src/bin/cargo/commands/build.rs:1:5 clippy::wildcard_imports "usage of wildcard import" +target/lintcheck/sources/cargo-0.49.0/src/bin/cargo/commands/check.rs:1:5 clippy::wildcard_imports "usage of wildcard import" +target/lintcheck/sources/cargo-0.49.0/src/bin/cargo/commands/clean.rs:1:5 clippy::wildcard_imports "usage of wildcard import" +target/lintcheck/sources/cargo-0.49.0/src/bin/cargo/commands/doc.rs:1:5 clippy::wildcard_imports "usage of wildcard import" +target/lintcheck/sources/cargo-0.49.0/src/bin/cargo/commands/fetch.rs:1:5 clippy::wildcard_imports "usage of wildcard import" +target/lintcheck/sources/cargo-0.49.0/src/bin/cargo/commands/fetch.rs:22:5 clippy::let_underscore_drop "non-binding `let` on a type that implements `Drop`" +target/lintcheck/sources/cargo-0.49.0/src/bin/cargo/commands/fix.rs:1:5 clippy::wildcard_imports "usage of wildcard import" +target/lintcheck/sources/cargo-0.49.0/src/bin/cargo/commands/generate_lockfile.rs:1:5 clippy::wildcard_imports "usage of wildcard import" +target/lintcheck/sources/cargo-0.49.0/src/bin/cargo/commands/git_checkout.rs:1:5 clippy::wildcard_imports "usage of wildcard import" +target/lintcheck/sources/cargo-0.49.0/src/bin/cargo/commands/help.rs:20:1 clippy::module_name_repetitions "item name ends with its containing module's name" +target/lintcheck/sources/cargo-0.49.0/src/bin/cargo/commands/init.rs:1:5 clippy::wildcard_imports "usage of wildcard import" +target/lintcheck/sources/cargo-0.49.0/src/bin/cargo/commands/install.rs:1:5 clippy::wildcard_imports "usage of wildcard import" +target/lintcheck/sources/cargo-0.49.0/src/bin/cargo/commands/install.rs:97:16 clippy::option_if_let_else "use Option::map_or instead of an if let/else" +target/lintcheck/sources/cargo-0.49.0/src/bin/cargo/commands/locate_project.rs:1:5 clippy::wildcard_imports "usage of wildcard import" +target/lintcheck/sources/cargo-0.49.0/src/bin/cargo/commands/login.rs:1:5 clippy::wildcard_imports "usage of wildcard import" +target/lintcheck/sources/cargo-0.49.0/src/bin/cargo/commands/metadata.rs:1:5 clippy::wildcard_imports "usage of wildcard import" +target/lintcheck/sources/cargo-0.49.0/src/bin/cargo/commands/mod.rs:1:5 clippy::wildcard_imports "usage of wildcard import" +target/lintcheck/sources/cargo-0.49.0/src/bin/cargo/commands/new.rs:1:5 clippy::wildcard_imports "usage of wildcard import" +target/lintcheck/sources/cargo-0.49.0/src/bin/cargo/commands/new.rs:20:24 clippy::option_if_let_else "use Option::map_or instead of an if let/else" +target/lintcheck/sources/cargo-0.49.0/src/bin/cargo/commands/owner.rs:1:5 clippy::wildcard_imports "usage of wildcard import" +target/lintcheck/sources/cargo-0.49.0/src/bin/cargo/commands/owner.rs:38:43 clippy::redundant_closure_for_method_calls "redundant closure" +target/lintcheck/sources/cargo-0.49.0/src/bin/cargo/commands/owner.rs:39:43 clippy::redundant_closure_for_method_calls "redundant closure" +target/lintcheck/sources/cargo-0.49.0/src/bin/cargo/commands/owner.rs:40:43 clippy::redundant_closure_for_method_calls "redundant closure" +target/lintcheck/sources/cargo-0.49.0/src/bin/cargo/commands/owner.rs:43:30 clippy::redundant_closure_for_method_calls "redundant closure" +target/lintcheck/sources/cargo-0.49.0/src/bin/cargo/commands/owner.rs:46:30 clippy::redundant_closure_for_method_calls "redundant closure" +target/lintcheck/sources/cargo-0.49.0/src/bin/cargo/commands/package.rs:1:5 clippy::wildcard_imports "usage of wildcard import" +target/lintcheck/sources/cargo-0.49.0/src/bin/cargo/commands/pkgid.rs:1:5 clippy::wildcard_imports "usage of wildcard import" +target/lintcheck/sources/cargo-0.49.0/src/bin/cargo/commands/publish.rs:1:5 clippy::wildcard_imports "usage of wildcard import" +target/lintcheck/sources/cargo-0.49.0/src/bin/cargo/commands/publish.rs:40:47 clippy::redundant_closure_for_method_calls "redundant closure" +target/lintcheck/sources/cargo-0.49.0/src/bin/cargo/commands/read_manifest.rs:1:5 clippy::wildcard_imports "usage of wildcard import" +target/lintcheck/sources/cargo-0.49.0/src/bin/cargo/commands/run.rs:1:5 clippy::wildcard_imports "usage of wildcard import" +target/lintcheck/sources/cargo-0.49.0/src/bin/cargo/commands/rustc.rs:1:5 clippy::wildcard_imports "usage of wildcard import" +target/lintcheck/sources/cargo-0.49.0/src/bin/cargo/commands/rustdoc.rs:3:5 clippy::wildcard_imports "usage of wildcard import" +target/lintcheck/sources/cargo-0.49.0/src/bin/cargo/commands/search.rs:1:5 clippy::wildcard_imports "usage of wildcard import" +target/lintcheck/sources/cargo-0.49.0/src/bin/cargo/commands/test.rs:127:54 clippy::redundant_closure_for_method_calls "redundant closure" +target/lintcheck/sources/cargo-0.49.0/src/bin/cargo/commands/test.rs:1:5 clippy::wildcard_imports "usage of wildcard import" +target/lintcheck/sources/cargo-0.49.0/src/bin/cargo/commands/tree.rs:149:49 clippy::redundant_closure_for_method_calls "redundant closure" +target/lintcheck/sources/cargo-0.49.0/src/bin/cargo/commands/tree.rs:2:5 clippy::wildcard_imports "usage of wildcard import" +target/lintcheck/sources/cargo-0.49.0/src/bin/cargo/commands/uninstall.rs:1:5 clippy::wildcard_imports "usage of wildcard import" +target/lintcheck/sources/cargo-0.49.0/src/bin/cargo/commands/update.rs:1:5 clippy::wildcard_imports "usage of wildcard import" +target/lintcheck/sources/cargo-0.49.0/src/bin/cargo/commands/vendor.rs:1:5 clippy::wildcard_imports "usage of wildcard import" +target/lintcheck/sources/cargo-0.49.0/src/bin/cargo/commands/vendor.rs:96:16 clippy::map_unwrap_or "called `map(<f>).unwrap_or_else(<g>)` on an `Option` value. This can be done more directly by calling `map_or_else(<g>, <f>)` instead" +target/lintcheck/sources/cargo-0.49.0/src/bin/cargo/commands/verify_project.rs:1:5 clippy::wildcard_imports "usage of wildcard import" +target/lintcheck/sources/cargo-0.49.0/src/bin/cargo/commands/version.rs:2:5 clippy::wildcard_imports "usage of wildcard import" +target/lintcheck/sources/cargo-0.49.0/src/bin/cargo/commands/yank.rs:1:5 clippy::wildcard_imports "usage of wildcard import" +target/lintcheck/sources/cargo-0.49.0/src/bin/cargo/commands/yank.rs:32:36 clippy::redundant_closure_for_method_calls "redundant closure" +target/lintcheck/sources/cargo-0.49.0/src/bin/cargo/commands/yank.rs:33:35 clippy::redundant_closure_for_method_calls "redundant closure" +target/lintcheck/sources/cargo-0.49.0/src/bin/cargo/commands/yank.rs:34:36 clippy::redundant_closure_for_method_calls "redundant closure" +target/lintcheck/sources/cargo-0.49.0/src/bin/cargo/commands/yank.rs:35:36 clippy::redundant_closure_for_method_calls "redundant closure" +target/lintcheck/sources/cargo-0.49.0/src/bin/cargo/main.rs:118:41 clippy::redundant_closure_for_method_calls "redundant closure" +target/lintcheck/sources/cargo-0.49.0/src/bin/cargo/main.rs:137:43 clippy::redundant_closure_for_method_calls "redundant closure" +target/lintcheck/sources/cargo-0.49.0/src/bin/cargo/main.rs:148:19 clippy::single_match_else "you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let`" +target/lintcheck/sources/cargo-0.49.0/src/bin/cargo/main.rs:174:57 clippy::redundant_closure_for_method_calls "redundant closure" +target/lintcheck/sources/cargo-0.49.0/src/bin/cargo/main.rs:18:5 clippy::wildcard_imports "usage of wildcard import" +target/lintcheck/sources/cargo-0.49.0/src/bin/cargo/main.rs:1:null clippy::cargo_common_metadata "package `cargo` is missing `package.categories` metadata" +target/lintcheck/sources/cargo-0.49.0/src/bin/cargo/main.rs:1:null clippy::cargo_common_metadata "package `cargo` is missing `package.keywords` metadata" +target/lintcheck/sources/cargo-0.49.0/src/bin/cargo/main.rs:1:null clippy::multiple_crate_versions "multiple versions for dependency `crossbeam-utils`: 0.6.6, 0.7.2" +target/lintcheck/sources/cargo-0.49.0/src/bin/cargo/main.rs:1:null clippy::multiple_crate_versions "multiple versions for dependency `hex`: 0.3.2, 0.4.0" +target/lintcheck/sources/cargo-0.49.0/src/bin/cargo/main.rs:1:null clippy::multiple_crate_versions "multiple versions for dependency `humantime`: 1.3.0, 2.0.0" +target/lintcheck/sources/cargo-0.49.0/src/bin/cargo/main.rs:72:22 clippy::redundant_closure_for_method_calls "redundant closure" +target/lintcheck/sources/cargo-0.49.0/src/bin/cargo/main.rs:79:40 clippy::manual_map "manual implementation of `Option::map`" +target/lintcheck/sources/cargo-0.49.0/src/bin/cargo/main.rs:98:60 clippy::redundant_closure_for_method_calls "redundant closure" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/compiler/build_config.rs:155:13 clippy::enum_glob_use "usage of wildcard import for enum variants" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/compiler/build_config.rs:170:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/compiler/build_config.rs:175:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/compiler/build_config.rs:180:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/compiler/build_config.rs:186:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/compiler/build_config.rs:197:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/compiler/build_config.rs:205:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/compiler/build_config.rs:51:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/compiler/build_config.rs:69:48 clippy::cast_possible_truncation "casting `usize` to `u32` may truncate the value on targets with 64-bit wide pointers" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/compiler/build_config.rs:96:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/compiler/build_context/mod.rs:44:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/compiler/build_context/mod.rs:83:20 clippy::doc_markdown "you should put `x86_64` between ticks in the documentation" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/compiler/build_context/target_info.rs:108:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/compiler/build_context/target_info.rs:121:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/compiler/build_context/target_info.rs:149:9 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/compiler/build_context/target_info.rs:411:9 clippy::needless_question_mark "question mark operator is useless here" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/compiler/build_context/target_info.rs:420:69 clippy::doc_markdown "you should put `mode/target_kind` between ticks in the documentation" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/compiler/build_context/target_info.rs:423:19 clippy::doc_markdown "you should put `CrateTypes` between ticks in the documentation" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/compiler/build_context/target_info.rs:424:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/compiler/build_context/target_info.rs:469:58 clippy::redundant_closure_for_method_calls "redundant closure" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/compiler/build_context/target_info.rs:603:19 clippy::redundant_closure_for_method_calls "redundant closure" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/compiler/build_context/target_info.rs:665:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/compiler/build_context/target_info.rs:697:12 clippy::inconsistent_struct_constructor "struct constructor field order is inconsistent with struct definition field order" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/compiler/build_context/target_info.rs:82:31 clippy::doc_markdown "you should put `FileType` between ticks in the documentation" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/compiler/build_context/target_info.rs:83:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/compiler/build_context/target_info.rs:84:9 clippy::single_match_else "you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let`" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/compiler/build_context/target_info.rs:96:31 clippy::doc_markdown "you should put `FileType` between ticks in the documentation" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/compiler/build_context/target_info.rs:98:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/compiler/build_plan.rs:4:9 clippy::doc_markdown "you should put `BuildPlan` between ticks in the documentation" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/compiler/build_plan.rs:5:66 clippy::doc_markdown "you should put `BuildPlan` between ticks in the documentation" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/compiler/build_plan.rs:66:40 clippy::ptr_arg "writing `&PathBuf` instead of `&Path` involves a new object where a slice will do" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/compiler/compilation.rs:150:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/compiler/compilation.rs:150:5 clippy::missing_panics_doc "docs for function which may panic missing `# Panics` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/compiler/compilation.rs:169:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/compiler/compilation.rs:185:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/compiler/compilation.rs:193:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/compiler/compilation.rs:194:49 clippy::redundant_closure_for_method_calls "redundant closure" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/compiler/compilation.rs:198:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/compiler/compilation.rs:314:16 clippy::doc_markdown "you should put `rustc_tool` between ticks in the documentation" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/compiler/compilation.rs:91:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/compiler/compile_kind.rs:118:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/compiler/compile_kind.rs:123:18 clippy::case_sensitive_file_extension_comparisons "case-sensitive file extension comparison" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/compiler/compile_kind.rs:147:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/compiler/compile_kind.rs:157:5 clippy::missing_panics_doc "docs for function which may panic missing `# Panics` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/compiler/compile_kind.rs:157:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/compiler/compile_kind.rs:29:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/compiler/compile_kind.rs:33:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/compiler/compile_kind.rs:49:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/compiler/compile_kind.rs:69:48 clippy::case_sensitive_file_extension_comparisons "case-sensitive file extension comparison" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/compiler/context/compilation_files.rs:204:9 clippy::single_match_else "you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let`" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/compiler/context/compilation_files.rs:277:22 clippy::doc_markdown "you should put `OUT_DIR` between ticks in the documentation" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/compiler/context/compilation_files.rs:324:66 clippy::doc_markdown "you should put `FileType` between ticks in the documentation" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/compiler/context/compilation_files.rs:393:37 clippy::match_same_arms "this `match` has identical arm bodies" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/compiler/context/compilation_files.rs:426:71 clippy::redundant_closure_for_method_calls "redundant closure" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/compiler/context/mod.rs:125:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/compiler/context/mod.rs:125:5 clippy::missing_panics_doc "docs for function which may panic missing `# Panics` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/compiler/context/mod.rs:125:5 clippy::too_many_lines "this function has too many lines (107/100)" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/compiler/context/mod.rs:270:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/compiler/context/mod.rs:286:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/compiler/context/mod.rs:308:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/compiler/context/mod.rs:308:5 clippy::missing_panics_doc "docs for function which may panic missing `# Panics` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/compiler/context/mod.rs:340:5 clippy::missing_panics_doc "docs for function which may panic missing `# Panics` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/compiler/context/mod.rs:340:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/compiler/context/mod.rs:349:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/compiler/context/mod.rs:349:5 clippy::missing_panics_doc "docs for function which may panic missing `# Panics` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/compiler/context/mod.rs:354:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/compiler/context/mod.rs:358:21 clippy::doc_markdown "you should put `RunCustomBuild` between ticks in the documentation" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/compiler/context/mod.rs:361:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/compiler/context/mod.rs:374:43 clippy::doc_markdown "you should put `RunCustomBuild` between ticks in the documentation" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/compiler/context/mod.rs:378:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/compiler/context/mod.rs:383:41 clippy::doc_markdown "you should put `RunCustomBuild` between ticks in the documentation" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/compiler/context/mod.rs:384:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/compiler/context/mod.rs:391:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/compiler/context/mod.rs:397:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/compiler/context/mod.rs:523:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/compiler/context/mod.rs:538:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/compiler/context/mod.rs:542:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/compiler/context/mod.rs:83:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/compiler/context/mod.rs:92:25 clippy::single_match_else "you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let`" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/compiler/crate_type.rs:16:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/compiler/crate_type.rs:29:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/compiler/crate_type.rs:40:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/compiler/crate_type.rs:49:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/compiler/crate_type.rs:60:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/compiler/custom_build.rs:150:1 clippy::too_many_lines "this function has too many lines (230/100)" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/compiler/custom_build.rs:353:56 clippy::manual_strip "stripping a prefix manually" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/compiler/custom_build.rs:448:27 clippy::needless_pass_by_value "this argument is passed by value, but not consumed in the function body" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/compiler/custom_build.rs:464:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/compiler/custom_build.rs:481:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/compiler/custom_build.rs:481:5 clippy::missing_panics_doc "docs for function which may panic missing `# Panics` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/compiler/custom_build.rs:48:56 clippy::doc_markdown "you should put `RunCustomBuild` between ticks in the documentation" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/compiler/custom_build.rs:561:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/compiler/custom_build.rs:567:20 clippy::redundant_closure_for_method_calls "redundant closure" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/compiler/custom_build.rs:576:28 clippy::shadow_unrelated "`mut value` is being shadowed" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/compiler/custom_build.rs:606:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/compiler/custom_build.rs:688:5 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/compiler/custom_build.rs:756:5 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/compiler/custom_build.rs:762:5 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/compiler/custom_build.rs:762:5 clippy::unnecessary_wraps "this function's return value is unnecessary" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/compiler/custom_build.rs:823:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/compiler/fingerprint.rs:1021:51 clippy::redundant_closure_for_method_calls "redundant closure" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/compiler/fingerprint.rs:1656:16 clippy::single_match_else "you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let`" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/compiler/fingerprint.rs:1664:5 clippy::field_reassign_with_default "field assignment outside of initializer for an instance created with Default::default()" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/compiler/fingerprint.rs:1787:5 clippy::similar_names "binding's name is too similar to existing binding" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/compiler/fingerprint.rs:1795:5 clippy::field_reassign_with_default "field assignment outside of initializer for an instance created with Default::default()" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/compiler/fingerprint.rs:1882:17 clippy::similar_names "binding's name is too similar to existing binding" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/compiler/fingerprint.rs:1894:17 clippy::similar_names "binding's name is too similar to existing binding" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/compiler/fingerprint.rs:1906:9 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/compiler/fingerprint.rs:1917:9 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/compiler/fingerprint.rs:1923:9 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/compiler/fingerprint.rs:1956:9 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/compiler/fingerprint.rs:1962:9 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/compiler/fingerprint.rs:1963:22 clippy::cast_possible_truncation "casting `usize` to `u8` may truncate the value" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/compiler/fingerprint.rs:1964:22 clippy::cast_possible_truncation "casting `usize` to `u8` may truncate the value" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/compiler/fingerprint.rs:1965:22 clippy::cast_possible_truncation "casting `usize` to `u8` may truncate the value" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/compiler/fingerprint.rs:1966:22 clippy::cast_possible_truncation "casting `usize` to `u8` may truncate the value" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/compiler/fingerprint.rs:1980:17 clippy::similar_names "binding's name is too similar to existing binding" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/compiler/fingerprint.rs:1980:24 clippy::manual_strip "stripping a prefix manually" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/compiler/fingerprint.rs:1986:17 clippy::similar_names "binding's name is too similar to existing binding" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/compiler/fingerprint.rs:2016:5 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/compiler/fingerprint.rs:61:5 clippy::doc_markdown "you should put `CompileMode` between ticks in the documentation" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/compiler/fingerprint.rs:63:12 clippy::doc_markdown "you should put `CompileKind` between ticks in the documentation" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/compiler/fingerprint.rs:67:7 clippy::doc_markdown "you should put `CARGO_DEFAULT_LIB_METADATA[^4` between ticks in the documentation" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/compiler/fingerprint.rs:68:5 clippy::doc_markdown "you should put `package_id` between ticks in the documentation" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/compiler/fingerprint.rs:71:19 clippy::doc_markdown "you should put `test/bench/for_host/edition` between ticks in the documentation" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/compiler/fingerprint.rs:755:52 clippy::match_same_arms "this `match` has identical arm bodies" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/compiler/fingerprint.rs:77:5 clippy::doc_markdown "you should put `is_std` between ticks in the documentation" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/compiler/fingerprint.rs:816:5 clippy::too_many_lines "this function has too many lines (127/100)" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/compiler/fingerprint.rs:863:64 clippy::similar_names "binding's name is too similar to existing binding" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/compiler/fingerprint.rs:875:33 clippy::similar_names "binding's name is too similar to existing binding" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/compiler/fingerprint.rs:876:32 clippy::similar_names "binding's name is too similar to existing binding" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/compiler/fingerprint.rs:896:30 clippy::similar_names "binding's name is too similar to existing binding" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/compiler/fingerprint.rs:897:30 clippy::similar_names "binding's name is too similar to existing binding" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/compiler/fingerprint.rs:991:37 clippy::single_match_else "you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let`" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/compiler/job_queue.rs:12:5 clippy::doc_markdown "you should put `src/librustc_jobserver/lib.rs` between ticks in the documentation" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/compiler/job_queue.rs:329:13 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/compiler/job_queue.rs:332:23 clippy::needless_pass_by_value "this argument is passed by value, but not consumed in the function body" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/compiler/job_queue.rs:34:53 clippy::doc_markdown "you should put `NeedsToken` between ticks in the documentation" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/compiler/job_queue.rs:35:6 clippy::doc_markdown "you should put `ReleaseToken` between ticks in the documentation" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/compiler/job_queue.rs:37:6 clippy::doc_markdown "you should put `NeedsToken` between ticks in the documentation" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/compiler/job_queue.rs:40:5 clippy::doc_markdown "you should put `NeedsToken` between ticks in the documentation" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/compiler/job_queue.rs:40:56 clippy::doc_markdown "you should put `NeedsToken` between ticks in the documentation" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/compiler/job_queue.rs:43:6 clippy::doc_markdown "you should put `ReleaseToken` between ticks in the documentation" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/compiler/job_queue.rs:748:16 clippy::collapsible_else_if "this `else { if .. }` block can be collapsed" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/compiler/job_queue.rs:749:13 clippy::if_not_else "unnecessary boolean `not` operation" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/compiler/job_queue.rs:786:26 clippy::unused_self "unused `self` argument" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/compiler/job_queue.rs:81:61 clippy::doc_markdown "you should put `DrainState` between ticks in the documentation" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/compiler/job_queue.rs:865:13 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/compiler/job_queue.rs:871:13 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/compiler/job_queue.rs:890:9 clippy::unused_self "unused `self` argument" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/compiler/job_queue.rs:93:24 clippy::doc_markdown "you should put `JobQueue` between ticks in the documentation" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/compiler/links.rs:8:1 clippy::module_name_repetitions "item name ends with its containing module's name" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/compiler/lto.rs:130:13 clippy::unnested_or_patterns "unnested or-patterns" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/compiler/lto.rs:135:13 clippy::unnested_or_patterns "unnested or-patterns" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/compiler/mod.rs:1016:1 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/compiler/mod.rs:1094:19 clippy::redundant_closure_for_method_calls "redundant closure" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/compiler/mod.rs:1131:1 clippy::unnecessary_wraps "this function's return value is unnecessary" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/compiler/mod.rs:1268:34 clippy::case_sensitive_file_extension_comparisons "case-sensitive file extension comparison" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/compiler/mod.rs:1277:5 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/compiler/mod.rs:179:1 clippy::too_many_lines "this function has too many lines (162/100)" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/compiler/mod.rs:198:78 clippy::redundant_closure_for_method_calls "redundant closure" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/compiler/mod.rs:201:25 clippy::single_match_else "you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let`" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/compiler/mod.rs:267:9 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/compiler/mod.rs:324:5 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/compiler/mod.rs:364:5 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/compiler/mod.rs:364:5 clippy::unnecessary_wraps "this function's return value is unnecessary" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/compiler/mod.rs:392:45 clippy::redundant_closure_for_method_calls "redundant closure" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/compiler/mod.rs:415:23 clippy::single_match_else "you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let`" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/compiler/mod.rs:464:18 clippy::ptr_arg "writing `&PathBuf` instead of `&Path` involves a new object where a slice will do" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/compiler/mod.rs:488:61 clippy::ptr_arg "writing `&PathBuf` instead of `&Path` involves a new object where a slice will do" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/compiler/mod.rs:498:21 clippy::unnested_or_patterns "unnested or-patterns" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/compiler/mod.rs:654:46 clippy::implicit_clone "implicitly cloning a `PathBuf` by calling `to_path_buf` on its dereferenced type" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/compiler/mod.rs:667:15 clippy::similar_names "binding's name is too similar to existing binding" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/compiler/mod.rs:693:1 clippy::unnecessary_wraps "this function's return value is unnecessary" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/compiler/mod.rs:725:42 clippy::match_same_arms "this `match` has identical arm bodies" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/compiler/mod.rs:736:1 clippy::too_many_lines "this function has too many lines (141/100)" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/compiler/mod.rs:73:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/compiler/mod.rs:777:12 clippy::if_not_else "unnecessary boolean `not` operation" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/compiler/mod.rs:873:5 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/compiler/rustdoc.rs:16:1 clippy::module_name_repetitions "item name starts with its containing module's name" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/compiler/rustdoc.rs:57:1 clippy::module_name_repetitions "item name starts with its containing module's name" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/compiler/rustdoc.rs:72:1 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/compiler/standard_lib.rs:134:1 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/compiler/standard_lib.rs:16:1 clippy::must_use_candidate "this function could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/compiler/standard_lib.rs:30:28 clippy::redundant_closure_for_method_calls "redundant closure" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/compiler/standard_lib.rs:34:1 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/compiler/standard_lib.rs:34:1 clippy::missing_panics_doc "docs for function which may panic missing `# Panics` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/compiler/timings.rs:16:1 clippy::struct_excessive_bools "more than 3 bools in a struct" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/compiler/timings.rs:192:64 clippy::needless_pass_by_value "this argument is passed by value, but not consumed in the function body" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/compiler/timings.rs:212:58 clippy::needless_pass_by_value "this argument is passed by value, but not consumed in the function body" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/compiler/timings.rs:234:13 clippy::let_underscore_drop "non-binding `let` on a type that implements `Drop`" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/compiler/timings.rs:355:13 clippy::cast_possible_truncation "casting `f64` to `u32` may truncate the value" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/compiler/timings.rs:355:13 clippy::cast_sign_loss "casting `f64` to `u32` may lose the sign of the value" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/compiler/timings.rs:397:38 clippy::cast_possible_truncation "casting `f64` to `u32` may truncate the value" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/compiler/timings.rs:397:38 clippy::cast_sign_loss "casting `f64` to `u32` may lose the sign of the value" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/compiler/timings.rs:484:9 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/compiler/timings.rs:605:38 clippy::doc_markdown "you should put `rmeta_time` between ticks in the documentation" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/compiler/timings.rs:605:50 clippy::doc_markdown "you should put `codegen_time` between ticks in the documentation" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/compiler/timings.rs:641:26 clippy::non_ascii_literal "literal non-ASCII character detected" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/compiler/unit.rs:100:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/compiler/unit.rs:151:1 clippy::module_name_repetitions "item name starts with its containing module's name" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/compiler/unit.rs:161:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/compiler/unit.rs:35:1 clippy::module_name_repetitions "item name starts with its containing module's name" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/compiler/unit_dependencies.rs:154:29 clippy::doc_markdown "you should put `state.unit_dependencies` between ticks in the documentation" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/compiler/unit_dependencies.rs:213:1 clippy::too_many_lines "this function has too many lines (110/100)" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/compiler/unit_dependencies.rs:52:1 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/compiler/unit_dependencies.rs:52:1 clippy::module_name_repetitions "item name ends with its containing module's name" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/compiler/unit_graph.rs:65:1 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/compiler/unit_graph.rs:65:1 clippy::module_name_repetitions "item name ends with its containing module's name" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/dependency.rs:157:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/dependency.rs:182:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/dependency.rs:203:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/dependency.rs:224:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/dependency.rs:23:1 clippy::struct_excessive_bools "more than 3 bools in a struct" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/dependency.rs:248:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/dependency.rs:270:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/dependency.rs:274:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/dependency.rs:278:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/dependency.rs:287:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/dependency.rs:291:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/dependency.rs:305:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/dependency.rs:311:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/dependency.rs:319:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/dependency.rs:337:75 clippy::redundant_closure_for_method_calls "redundant closure" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/dependency.rs:397:56 clippy::redundant_closure_for_method_calls "redundant closure" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/dependency.rs:403:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/dependency.rs:408:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/dependency.rs:415:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/dependency.rs:419:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/dependency.rs:424:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/dependency.rs:428:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/dependency.rs:433:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/dependency.rs:438:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/dependency.rs:443:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/dependency.rs:449:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/dependency.rs:450:9 clippy::if_not_else "unnecessary `!=` operation" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/features.rs:119:17 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/features.rs:229:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/features.rs:274:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/features.rs:278:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/features.rs:306:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/features.rs:338:1 clippy::struct_excessive_bools "more than 3 bools in a struct" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/features.rs:362:25 clippy::option_option "consider using `Option<T>` instead of `Option<Option<T>>` or a custom enum if you need to distinguish all 3 cases" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/features.rs:380:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/features.rs:401:9 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/features.rs:409:9 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/features.rs:412:45 clippy::redundant_closure_for_method_calls "redundant closure" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/features.rs:416:9 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/features.rs:419:45 clippy::redundant_closure_for_method_calls "redundant closure" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/features.rs:424:9 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/features.rs:431:9 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/features.rs:477:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/features.rs:509:1 clippy::must_use_candidate "this function could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/features.rs:518:5 clippy::map_unwrap_or "called `map(<f>).unwrap_or_else(<g>)` on an `Option` value. This can be done more directly by calling `map_or_else(<g>, <f>)` instead" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/features.rs:542:1 clippy::must_use_candidate "this function could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/features.rs:543:37 clippy::redundant_closure_for_method_calls "redundant closure" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/features.rs:547:60 clippy::redundant_closure_for_method_calls "redundant closure" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/features.rs:556:1 clippy::module_name_repetitions "item name ends with its containing module's name" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/features.rs:563:1 clippy::module_name_repetitions "item name ends with its containing module's name" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/manifest.rs:116:13 clippy::enum_glob_use "usage of wildcard import for enum variants" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/manifest.rs:118:58 clippy::redundant_closure_for_method_calls "redundant closure" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/manifest.rs:130:13 clippy::enum_glob_use "usage of wildcard import for enum variants" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/manifest.rs:143:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/manifest.rs:159:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/manifest.rs:162:34 clippy::redundant_closure_for_method_calls "redundant closure" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/manifest.rs:169:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/manifest.rs:17:5 clippy::wildcard_imports "usage of wildcard import" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/manifest.rs:189:1 clippy::struct_excessive_bools "more than 3 bools in a struct" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/manifest.rs:215:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/manifest.rs:222:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/manifest.rs:22:1 clippy::module_name_repetitions "item name ends with its containing module's name" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/manifest.rs:320:46 clippy::implicit_clone "implicitly cloning a `PathBuf` by calling `to_path_buf` on its dereferenced type" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/manifest.rs:360:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/manifest.rs:407:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/manifest.rs:410:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/manifest.rs:413:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/manifest.rs:416:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/manifest.rs:419:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/manifest.rs:422:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/manifest.rs:425:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/manifest.rs:431:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/manifest.rs:438:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/manifest.rs:444:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/manifest.rs:447:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/manifest.rs:450:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/manifest.rs:453:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/manifest.rs:456:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/manifest.rs:459:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/manifest.rs:462:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/manifest.rs:466:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/manifest.rs:470:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/manifest.rs:477:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/manifest.rs:481:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/manifest.rs:488:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/manifest.rs:512:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/manifest.rs:516:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/manifest.rs:520:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/manifest.rs:524:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/manifest.rs:528:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/manifest.rs:538:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/manifest.rs:557:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/manifest.rs:561:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/manifest.rs:565:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/manifest.rs:569:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/manifest.rs:577:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/manifest.rs:581:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/manifest.rs:588:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/manifest.rs:617:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/manifest.rs:632:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/manifest.rs:648:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/manifest.rs:659:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/manifest.rs:66:1 clippy::module_name_repetitions "item name ends with its containing module's name" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/manifest.rs:670:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/manifest.rs:693:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/manifest.rs:708:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/manifest.rs:723:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/manifest.rs:726:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/manifest.rs:729:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/manifest.rs:735:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/manifest.rs:738:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/manifest.rs:741:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/manifest.rs:744:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/manifest.rs:747:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/manifest.rs:751:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/manifest.rs:754:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/manifest.rs:757:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/manifest.rs:760:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/manifest.rs:763:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/manifest.rs:767:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/manifest.rs:776:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/manifest.rs:780:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/manifest.rs:787:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/manifest.rs:798:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/manifest.rs:800:56 clippy::redundant_closure_for_method_calls "redundant closure" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/manifest.rs:805:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/manifest.rs:809:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/manifest.rs:818:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/manifest.rs:823:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/manifest.rs:828:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/manifest.rs:831:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/manifest.rs:834:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/manifest.rs:839:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/manifest.rs:85:1 clippy::module_name_repetitions "item name starts with its containing module's name" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/manifest.rs:888:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/manifest.rs:936:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/package.rs:1075:28 clippy::redundant_closure_for_method_calls "redundant closure" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/package.rs:160:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/package.rs:170:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/package.rs:174:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/package.rs:182:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/package.rs:186:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/package.rs:190:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/package.rs:194:5 clippy::missing_panics_doc "docs for function which may panic missing `# Panics` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/package.rs:194:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/package.rs:198:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/package.rs:202:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/package.rs:206:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/package.rs:210:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/package.rs:217:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/package.rs:221:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/package.rs:222:35 clippy::redundant_closure_for_method_calls "redundant closure" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/package.rs:226:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/package.rs:227:35 clippy::redundant_closure_for_method_calls "redundant closure" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/package.rs:230:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/package.rs:239:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/package.rs:249:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/package.rs:287:1 clippy::module_name_repetitions "item name starts with its containing module's name" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/package.rs:385:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/package.rs:421:5 clippy::needless_lifetimes "explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration)" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/package.rs:425:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/package.rs:452:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/package.rs:453:60 clippy::redundant_closure_for_method_calls "redundant closure" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/package.rs:459:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/package.rs:473:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/package.rs:587:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/package.rs:588:9 clippy::needless_question_mark "question mark operator is useless here" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/package.rs:682:46 clippy::cast_possible_truncation "casting `f64` to `u64` may truncate the value" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/package.rs:682:46 clippy::cast_sign_loss "casting `f64` to `u64` may lose the sign of the value" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/package.rs:682:63 clippy::cast_possible_truncation "casting `f64` to `u64` may truncate the value" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/package.rs:682:63 clippy::cast_sign_loss "casting `f64` to `u64` may lose the sign of the value" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/package.rs:731:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/package.rs:790:13 clippy::single_match_else "you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let`" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/package.rs:988:9 clippy::single_match_else "you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let`" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/package_id.rs:115:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/package_id.rs:124:5 clippy::missing_panics_doc "docs for function which may panic missing `# Panics` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/package_id.rs:124:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/package_id.rs:139:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/package_id.rs:142:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/package_id.rs:145:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/package_id.rs:149:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/package_id.rs:157:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/package_id.rs:161:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/package_id.rs:169:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/package_id.rs:174:1 clippy::module_name_repetitions "item name starts with its containing module's name" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/package_id_spec.rs:101:39 clippy::redundant_closure_for_method_calls "redundant closure" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/package_id_spec.rs:143:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/package_id_spec.rs:147:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/package_id_spec.rs:151:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/package_id_spec.rs:160:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/package_id_spec.rs:179:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/package_id_spec.rs:212:9 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/package_id_spec.rs:231:9 clippy::single_match_else "you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let`" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/package_id_spec.rs:51:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/package_id_spec.rs:51:5 clippy::missing_panics_doc "docs for function which may panic missing `# Panics` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/package_id_spec.rs:77:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/package_id_spec.rs:88:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/profiles.rs:1004:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/profiles.rs:1014:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/profiles.rs:1018:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/profiles.rs:1028:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/profiles.rs:106:9 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/profiles.rs:143:5 clippy::unnecessary_wraps "this function's return value is unnecessary" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/profiles.rs:204:54 clippy::implicit_clone "implicitly cloning a `InternedString` by calling `to_owned` on its dereferenced type" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/profiles.rs:286:5 clippy::missing_panics_doc "docs for function which may panic missing `# Panics` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/profiles.rs:286:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/profiles.rs:294:40 clippy::if_not_else "unnecessary boolean `not` operation" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/profiles.rs:30:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/profiles.rs:342:25 clippy::shadow_unrelated "`maker` is being shadowed" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/profiles.rs:370:41 clippy::unused_self "unused `self` argument" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/profiles.rs:370:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/profiles.rs:372:9 clippy::field_reassign_with_default "field assignment outside of initializer for an instance created with Default::default()" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/profiles.rs:382:5 clippy::missing_panics_doc "docs for function which may panic missing `# Panics` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/profiles.rs:382:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/profiles.rs:383:28 clippy::if_not_else "unnecessary boolean `not` operation" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/profiles.rs:397:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/profiles.rs:405:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/profiles.rs:607:1 clippy::struct_excessive_bools "more than 3 bools in a struct" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/profiles.rs:909:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/profiles.rs:923:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/profiles.rs:934:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/profiles.rs:987:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/registry.rs:111:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/registry.rs:127:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/registry.rs:168:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/registry.rs:19:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/registry.rs:240:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/registry.rs:26:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/registry.rs:344:49 clippy::redundant_closure_for_method_calls "redundant closure" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/registry.rs:369:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/registry.rs:424:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/registry.rs:49:1 clippy::module_name_repetitions "item name ends with its containing module's name" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/registry.rs:520:17 clippy::single_match_else "you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let`" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/registry.rs:763:53 clippy::redundant_closure_for_method_calls "redundant closure" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/registry.rs:765:53 clippy::redundant_closure_for_method_calls "redundant closure" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/registry.rs:807:14 clippy::redundant_closure_for_method_calls "redundant closure" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/registry.rs:814:53 clippy::redundant_closure_for_method_calls "redundant closure" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/resolver/conflict_cache.rs:197:29 clippy::redundant_closure_for_method_calls "redundant closure" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/resolver/conflict_cache.rs:41:38 clippy::map_unwrap_or "called `map(<f>).unwrap_or_else(<g>)` on an `Option` value. This can be done more directly by calling `map_or_else(<g>, <f>)` instead" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/resolver/context.rs:274:53 clippy::redundant_closure_for_method_calls "redundant closure" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/resolver/context.rs:42:1 clippy::module_name_repetitions "item name starts with its containing module's name" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/resolver/context.rs:74:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/resolver/encode.rs:156:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/resolver/encode.rs:156:5 clippy::too_many_lines "this function has too many lines (164/100)" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/resolver/encode.rs:438:5 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/resolver/encode.rs:449:5 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/resolver/encode.rs:529:34 clippy::redundant_closure_for_method_calls "redundant closure" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/resolver/encode.rs:602:59 clippy::redundant_closure_for_method_calls "redundant closure" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/resolver/encode.rs:623:1 clippy::module_name_repetitions "item name starts with its containing module's name" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/resolver/encode.rs:652:27 clippy::single_match_else "you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let`" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/resolver/encode.rs:674:51 clippy::redundant_closure_for_method_calls "redundant closure" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/resolver/errors.rs:103:22 clippy::redundant_closure_for_method_calls "redundant closure" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/resolver/errors.rs:104:22 clippy::redundant_closure_for_method_calls "redundant closure" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/resolver/errors.rs:206:9 clippy::if_not_else "unnecessary boolean `not` operation" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/resolver/errors.rs:257:45 clippy::redundant_closure_for_method_calls "redundant closure" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/resolver/errors.rs:27:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/resolver/errors.rs:305:17 clippy::single_char_add_str "calling `push_str()` using a single-character string literal" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/resolver/errors.rs:70:1 clippy::too_many_lines "this function has too many lines (207/100)" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/resolver/features.rs:104:1 clippy::module_name_repetitions "item name starts with its containing module's name" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/resolver/features.rs:111:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/resolver/features.rs:162:56 clippy::redundant_closure_for_method_calls "redundant closure" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/resolver/features.rs:179:1 clippy::module_name_repetitions "item name ends with its containing module's name" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/resolver/features.rs:186:23 clippy::doc_markdown "you should put `RequestedFeatures` between ticks in the documentation" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/resolver/features.rs:187:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/resolver/features.rs:199:23 clippy::doc_markdown "you should put `RequestedFeatures` between ticks in the documentation" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/resolver/features.rs:200:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/resolver/features.rs:221:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/resolver/features.rs:231:21 clippy::doc_markdown "you should put `pkg_id/is_build` between ticks in the documentation" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/resolver/features.rs:233:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/resolver/features.rs:247:58 clippy::redundant_closure_for_method_calls "redundant closure" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/resolver/features.rs:278:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/resolver/features.rs:394:27 clippy::doc_markdown "you should put `FeatureValue` between ticks in the documentation" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/resolver/features.rs:460:19 clippy::single_match_else "you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let`" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/resolver/features.rs:480:24 clippy::doc_markdown "you should put `FeatureValues` between ticks in the documentation" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/resolver/features.rs:496:24 clippy::doc_markdown "you should put `FeatureValues` between ticks in the documentation" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/resolver/features.rs:58:1 clippy::module_name_repetitions "item name ends with its containing module's name" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/resolver/features.rs:67:1 clippy::struct_excessive_bools "more than 3 bools in a struct" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/resolver/mod.rs:1017:5 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/resolver/mod.rs:1045:57 clippy::redundant_closure_for_method_calls "redundant closure" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/resolver/mod.rs:122:1 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/resolver/mod.rs:142:44 clippy::redundant_closure_for_method_calls "redundant closure" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/resolver/mod.rs:180:1 clippy::too_many_lines "this function has too many lines (225/100)" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/resolver/mod.rs:311:17 clippy::single_match_else "you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let`" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/resolver/mod.rs:421:52 clippy::filter_map_next "called `filter_map(..).next()` on an `Iterator`. This is more succinctly expressed by calling `.find_map(..)` instead" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/resolver/mod.rs:457:69 clippy::filter_map_next "called `filter_map(..).next()` on an `Iterator`. This is more succinctly expressed by calling `.find_map(..)` instead" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/resolver/mod.rs:470:37 clippy::similar_names "binding's name is too similar to existing binding" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/resolver/mod.rs:607:11 clippy::needless_pass_by_value "this argument is passed by value, but not consumed in the function body" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/resolver/mod.rs:631:21 clippy::single_match_else "you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let`" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/resolver/mod.rs:942:15 clippy::if_not_else "unnecessary boolean `not` operation" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/resolver/mod.rs:988:20 clippy::redundant_else "redundant else block" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/resolver/resolve.rs:120:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/resolver/resolve.rs:132:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/resolver/resolve.rs:199:24 clippy::redundant_else "redundant else block" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/resolver/resolve.rs:235:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/resolver/resolve.rs:239:5 clippy::needless_lifetimes "explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration)" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/resolver/resolve.rs:255:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/resolver/resolve.rs:259:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/resolver/resolve.rs:263:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/resolver/resolve.rs:269:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/resolver/resolve.rs:273:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/resolver/resolve.rs:274:9 clippy::map_unwrap_or "called `map(<f>).unwrap_or_else(<g>)` on an `Option` value. This can be done more directly by calling `map_or_else(<g>, <f>)` instead" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/resolver/resolve.rs:280:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/resolver/resolve.rs:284:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/resolver/resolve.rs:288:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/resolver/resolve.rs:292:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/resolver/resolve.rs:296:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/resolver/resolve.rs:300:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/resolver/resolve.rs:315:13 clippy::map_unwrap_or "called `map(<f>).unwrap_or_else(<g>)` on an `Option` value. This can be done more directly by calling `map_or_else(<g>, <f>)` instead" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/resolver/resolve.rs:354:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/resolver/resolve.rs:362:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/resolver/resolve.rs:60:1 clippy::module_name_repetitions "item name starts with its containing module's name" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/resolver/resolve.rs:76:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/resolver/types.rs:111:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/resolver/types.rs:121:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/resolver/types.rs:141:19 clippy::doc_markdown "you should put `ResolveOpts` between ticks in the documentation" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/resolver/types.rs:142:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/resolver/types.rs:149:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/resolver/types.rs:181:9 clippy::map_unwrap_or "called `map(<f>).unwrap_or(<a>)` on an `Option` value. This can be done more directly by calling `map_or(<a>, <f>)` instead" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/resolver/types.rs:187:5 clippy::needless_lifetimes "explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration)" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/resolver/types.rs:261:5 clippy::needless_lifetimes "explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration)" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/shell.rs:113:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/shell.rs:130:9 clippy::single_match_else "you seem to be trying to use `match` for an equality check. Consider using `if`" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/shell.rs:148:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/shell.rs:153:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/shell.rs:163:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/shell.rs:166:13 clippy::match_wildcard_for_single_variants "wildcard matches only a single variant and will also match any future added variants" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/shell.rs:18:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/shell.rs:198:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/shell.rs:206:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/shell.rs:214:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/shell.rs:228:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/shell.rs:239:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/shell.rs:250:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/shell.rs:259:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/shell.rs:267:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/shell.rs:26:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/shell.rs:277:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/shell.rs:282:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/shell.rs:314:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/shell.rs:322:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/shell.rs:330:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/shell.rs:345:5 clippy::missing_panics_doc "docs for function which may panic missing `# Panics` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/shell.rs:459:9 clippy::let_underscore_drop "non-binding `let` on a type that implements `Drop`" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/shell.rs:98:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/source/mod.rs:103:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/source/mod.rs:247:1 clippy::module_name_repetitions "item name starts with its containing module's name" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/source/mod.rs:261:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/source/mod.rs:268:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/source/mod.rs:273:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/source/mod.rs:291:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/source/mod.rs:302:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/source/mod.rs:307:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/source/mod.rs:31:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/source/mod.rs:37:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/source/mod.rs:39:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/source/mod.rs:47:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/source/mod.rs:50:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/source/mod.rs:52:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/source/mod.rs:63:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/source/mod.rs:74:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/source/mod.rs:83:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/source/source_id.rs:107:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/source/source_id.rs:107:5 clippy::missing_panics_doc "docs for function which may panic missing `# Panics` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/source/source_id.rs:128:50 clippy::redundant_closure_for_method_calls "redundant closure" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/source/source_id.rs:147:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/source/source_id.rs:156:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/source/source_id.rs:162:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/source/source_id.rs:166:19 clippy::doc_markdown "you should put `SourceId` between ticks in the documentation" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/source/source_id.rs:167:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/source/source_id.rs:171:19 clippy::doc_markdown "you should put `SourceId` between ticks in the documentation" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/source/source_id.rs:172:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/source/source_id.rs:178:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/source/source_id.rs:187:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/source/source_id.rs:187:5 clippy::missing_panics_doc "docs for function which may panic missing `# Panics` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/source/source_id.rs:18:74 clippy::default_trait_access "calling `std::sync::Mutex::default()` is more clear than this expression" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/source/source_id.rs:195:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/source/source_id.rs:207:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/source/source_id.rs:213:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/source/source_id.rs:217:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/source/source_id.rs:225:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/source/source_id.rs:228:16 clippy::option_if_let_else "use Option::map_or_else instead of an if let/else" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/source/source_id.rs:236:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/source/source_id.rs:241:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/source/source_id.rs:252:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/source/source_id.rs:257:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/source/source_id.rs:262:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/source/source_id.rs:305:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/source/source_id.rs:310:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/source/source_id.rs:318:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/source/source_id.rs:326:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/source/source_id.rs:338:5 clippy::missing_panics_doc "docs for function which may panic missing `# Panics` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/source/source_id.rs:355:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/source/source_id.rs:393:61 clippy::match_same_arms "this `match` has identical arm bodies" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/source/source_id.rs:394:42 clippy::match_same_arms "this `match` has identical arm bodies" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/source/source_id.rs:395:42 clippy::match_same_arms "this `match` has identical arm bodies" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/source/source_id.rs:397:71 clippy::match_same_arms "this `match` has identical arm bodies" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/source/source_id.rs:397:71 clippy::match_same_arms "this `match` has identical arm bodies" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/source/source_id.rs:398:47 clippy::match_same_arms "this `match` has identical arm bodies" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/source/source_id.rs:398:47 clippy::match_same_arms "this `match` has identical arm bodies" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/source/source_id.rs:399:47 clippy::match_same_arms "this `match` has identical arm bodies" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/source/source_id.rs:399:47 clippy::match_same_arms "this `match` has identical arm bodies" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/source/source_id.rs:401:63 clippy::match_same_arms "this `match` has identical arm bodies" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/source/source_id.rs:401:63 clippy::match_same_arms "this `match` has identical arm bodies" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/source/source_id.rs:401:63 clippy::match_same_arms "this `match` has identical arm bodies" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/source/source_id.rs:402:43 clippy::match_same_arms "this `match` has identical arm bodies" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/source/source_id.rs:402:43 clippy::match_same_arms "this `match` has identical arm bodies" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/source/source_id.rs:402:43 clippy::match_same_arms "this `match` has identical arm bodies" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/source/source_id.rs:403:43 clippy::match_same_arms "this `match` has identical arm bodies" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/source/source_id.rs:403:43 clippy::match_same_arms "this `match` has identical arm bodies" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/source/source_id.rs:403:43 clippy::match_same_arms "this `match` has identical arm bodies" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/source/source_id.rs:406:21 clippy::enum_glob_use "usage of wildcard import for enum variants" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/source/source_id.rs:412:41 clippy::match_same_arms "this `match` has identical arm bodies" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/source/source_id.rs:413:36 clippy::match_same_arms "this `match` has identical arm bodies" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/source/source_id.rs:414:36 clippy::match_same_arms "this `match` has identical arm bodies" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/source/source_id.rs:420:47 clippy::match_same_arms "this `match` has identical arm bodies" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/source/source_id.rs:420:47 clippy::match_same_arms "this `match` has identical arm bodies" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/source/source_id.rs:512:17 clippy::unseparated_literal_suffix "integer type suffix should be separated by an underscore" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/source/source_id.rs:513:17 clippy::unseparated_literal_suffix "integer type suffix should be separated by an underscore" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/source/source_id.rs:517:17 clippy::unseparated_literal_suffix "integer type suffix should be separated by an underscore" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/source/source_id.rs:518:17 clippy::unseparated_literal_suffix "integer type suffix should be separated by an underscore" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/source/source_id.rs:525:17 clippy::unseparated_literal_suffix "integer type suffix should be separated by an underscore" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/source/source_id.rs:526:17 clippy::unseparated_literal_suffix "integer type suffix should be separated by an underscore" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/source/source_id.rs:530:17 clippy::unseparated_literal_suffix "integer type suffix should be separated by an underscore" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/source/source_id.rs:531:17 clippy::unseparated_literal_suffix "integer type suffix should be separated by an underscore" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/source/source_id.rs:535:33 clippy::unseparated_literal_suffix "integer type suffix should be separated by an underscore" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/source/source_id.rs:536:37 clippy::unseparated_literal_suffix "integer type suffix should be separated by an underscore" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/source/source_id.rs:537:42 clippy::unseparated_literal_suffix "integer type suffix should be separated by an underscore" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/source/source_id.rs:538:38 clippy::unseparated_literal_suffix "integer type suffix should be separated by an underscore" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/source/source_id.rs:548:1 clippy::module_name_repetitions "item name starts with its containing module's name" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/source/source_id.rs:597:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/summary.rs:103:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/summary.rs:123:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/summary.rs:150:1 clippy::too_many_lines "this function has too many lines (141/100)" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/summary.rs:158:9 clippy::enum_glob_use "usage of wildcard import for enum variants" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/summary.rs:181:21 clippy::if_not_else "unnecessary boolean `not` operation" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/summary.rs:192:28 clippy::redundant_else "redundant else block" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/summary.rs:258:32 clippy::redundant_else "redundant else block" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/summary.rs:281:28 clippy::redundant_else "redundant else block" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/summary.rs:303:28 clippy::redundant_else "redundant else block" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/summary.rs:321:51 clippy::match_same_arms "this `match` has identical arm bodies" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/summary.rs:344:5 clippy::doc_markdown "you should put `FeatureValue` between ticks in the documentation" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/summary.rs:350:85 clippy::doc_markdown "you should put `FeatureValue` between ticks in the documentation" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/summary.rs:36:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/summary.rs:378:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/summary.rs:386:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/summary.rs:387:13 clippy::enum_glob_use "usage of wildcard import for enum variants" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/summary.rs:407:13 clippy::enum_glob_use "usage of wildcard import for enum variants" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/summary.rs:69:34 clippy::redundant_closure_for_method_calls "redundant closure" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/summary.rs:75:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/summary.rs:78:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/summary.rs:81:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/summary.rs:84:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/summary.rs:87:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/summary.rs:90:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/summary.rs:93:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/summary.rs:96:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/summary.rs:99:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/workspace.rs:1056:5 clippy::unnecessary_wraps "this function's return value is unnecessarily wrapped by `Result`" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/workspace.rs:113:1 clippy::module_name_repetitions "item name starts with its containing module's name" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/workspace.rs:1157:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/workspace.rs:128:1 clippy::module_name_repetitions "item name starts with its containing module's name" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/workspace.rs:150:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/workspace.rs:159:16 clippy::redundant_else "redundant else block" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/workspace.rs:197:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/workspace.rs:225:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/workspace.rs:225:5 clippy::missing_panics_doc "docs for function which may panic missing `# Panics` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/workspace.rs:255:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/workspace.rs:267:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/workspace.rs:317:5 clippy::missing_panics_doc "docs for function which may panic missing `# Panics` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/workspace.rs:329:37 clippy::doc_markdown "you should put `VirtualManifest` between ticks in the documentation" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/workspace.rs:410:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/workspace.rs:420:17 clippy::match_wildcard_for_single_variants "wildcard matches only a single variant and will also match any future added variants" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/workspace.rs:440:9 clippy::unnecessary_wraps "this function's return value is unnecessarily wrapped by `Result`" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/workspace.rs:511:32 clippy::single_match_else "you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let`" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/workspace.rs:561:25 clippy::non_ascii_literal "literal non-ASCII character detected" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/workspace.rs:613:13 clippy::filter_map "called `filter_map(..).map(..)` on an `Iterator`" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/workspace.rs:615:22 clippy::redundant_closure_for_method_calls "redundant closure" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/workspace.rs:762:27 clippy::single_match_else "you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let`" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/workspace.rs:784:17 clippy::if_not_else "unnecessary boolean `not` operation" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/workspace.rs:849:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/workspace.rs:849:5 clippy::missing_panics_doc "docs for function which may panic missing `# Panics` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/workspace.rs:893:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/workspace.rs:906:24 clippy::redundant_else "redundant else block" +target/lintcheck/sources/cargo-0.49.0/src/cargo/core/workspace.rs:932:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/lib.rs:177:1 clippy::missing_panics_doc "docs for function which may panic missing `# Panics` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/lib.rs:177:1 clippy::must_use_candidate "this function could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/lib.rs:180:36 clippy::redundant_closure_for_method_calls "redundant closure" +target/lintcheck/sources/cargo-0.49.0/src/cargo/lib.rs:180:36 clippy::redundant_closure_for_method_calls "redundant closure" +target/lintcheck/sources/cargo-0.49.0/src/cargo/lib.rs:180:36 clippy::redundant_closure_for_method_calls "redundant closure" +target/lintcheck/sources/cargo-0.49.0/src/cargo/lib.rs:180:36 clippy::redundant_closure_for_method_calls "redundant closure" +target/lintcheck/sources/cargo-0.49.0/src/cargo/lib.rs:180:36 clippy::redundant_closure_for_method_calls "redundant closure" +target/lintcheck/sources/cargo-0.49.0/src/cargo/lib.rs:1:null clippy::cargo_common_metadata "package `cargo` is missing `package.categories` metadata" +target/lintcheck/sources/cargo-0.49.0/src/cargo/lib.rs:1:null clippy::cargo_common_metadata "package `cargo` is missing `package.keywords` metadata" +target/lintcheck/sources/cargo-0.49.0/src/cargo/lib.rs:1:null clippy::multiple_crate_versions "multiple versions for dependency `crossbeam-utils`: 0.6.6, 0.7.2" +target/lintcheck/sources/cargo-0.49.0/src/cargo/lib.rs:1:null clippy::multiple_crate_versions "multiple versions for dependency `hex`: 0.3.2, 0.4.0" +target/lintcheck/sources/cargo-0.49.0/src/cargo/lib.rs:1:null clippy::multiple_crate_versions "multiple versions for dependency `humantime`: 1.3.0, 2.0.0" +target/lintcheck/sources/cargo-0.49.0/src/cargo/ops/cargo_clean.rs:205:23 clippy::redundant_closure_for_method_calls "redundant closure" +target/lintcheck/sources/cargo-0.49.0/src/cargo/ops/cargo_clean.rs:27:1 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/ops/cargo_clean.rs:27:1 clippy::too_many_lines "this function has too many lines (120/100)" +target/lintcheck/sources/cargo-0.49.0/src/cargo/ops/cargo_compile.rs:1078:14 clippy::redundant_closure_for_method_calls "redundant closure" +target/lintcheck/sources/cargo-0.49.0/src/cargo/ops/cargo_compile.rs:109:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/ops/cargo_compile.rs:119:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/ops/cargo_compile.rs:1227:17 clippy::needless_pass_by_value "this argument is passed by value, but not consumed in the function body" +target/lintcheck/sources/cargo-0.49.0/src/cargo/ops/cargo_compile.rs:127:35 clippy::from_iter_instead_of_collect "usage of `FromIterator::from_iter`" +target/lintcheck/sources/cargo-0.49.0/src/cargo/ops/cargo_compile.rs:173:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/ops/cargo_compile.rs:205:36 clippy::match_same_arms "this `match` has identical arm bodies" +target/lintcheck/sources/cargo-0.49.0/src/cargo/ops/cargo_compile.rs:242:1 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/ops/cargo_compile.rs:249:1 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/ops/cargo_compile.rs:258:1 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/ops/cargo_compile.rs:267:16 clippy::needless_question_mark "question mark operator is useless here" +target/lintcheck/sources/cargo-0.49.0/src/cargo/ops/cargo_compile.rs:275:1 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/ops/cargo_compile.rs:275:1 clippy::too_many_lines "this function has too many lines (219/100)" +target/lintcheck/sources/cargo-0.49.0/src/cargo/ops/cargo_compile.rs:468:9 clippy::default_trait_access "calling `std::collections::HashMap::default()` is more clear than this expression" +target/lintcheck/sources/cargo-0.49.0/src/cargo/ops/cargo_compile.rs:548:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/ops/cargo_compile.rs:556:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/ops/cargo_compile.rs:574:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/ops/cargo_compile.rs:583:21 clippy::doc_markdown "you should put `CompileFilter` between ticks in the documentation" +target/lintcheck/sources/cargo-0.49.0/src/cargo/ops/cargo_compile.rs:584:5 clippy::fn_params_excessive_bools "more than 3 bools in function parameters" +target/lintcheck/sources/cargo-0.49.0/src/cargo/ops/cargo_compile.rs:584:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/ops/cargo_compile.rs:592:9 clippy::similar_names "binding's name is too similar to existing binding" +target/lintcheck/sources/cargo-0.49.0/src/cargo/ops/cargo_compile.rs:593:9 clippy::similar_names "binding's name is too similar to existing binding" +target/lintcheck/sources/cargo-0.49.0/src/cargo/ops/cargo_compile.rs:607:13 clippy::similar_names "binding's name is too similar to existing binding" +target/lintcheck/sources/cargo-0.49.0/src/cargo/ops/cargo_compile.rs:612:21 clippy::doc_markdown "you should put `CompileFilter` between ticks in the documentation" +target/lintcheck/sources/cargo-0.49.0/src/cargo/ops/cargo_compile.rs:613:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/ops/cargo_compile.rs:618:9 clippy::similar_names "binding's name is too similar to existing binding" +target/lintcheck/sources/cargo-0.49.0/src/cargo/ops/cargo_compile.rs:641:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/ops/cargo_compile.rs:652:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/ops/cargo_compile.rs:655:50 clippy::match_same_arms "this `match` has identical arm bodies" +target/lintcheck/sources/cargo-0.49.0/src/cargo/ops/cargo_compile.rs:673:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/ops/cargo_compile.rs:692:49 clippy::match_same_arms "this `match` has identical arm bodies" +target/lintcheck/sources/cargo-0.49.0/src/cargo/ops/cargo_compile.rs:703:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/ops/cargo_compile.rs:729:1 clippy::too_many_lines "this function has too many lines (205/100)" +target/lintcheck/sources/cargo-0.49.0/src/cargo/ops/cargo_compile.rs:82:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/ops/cargo_compile.rs:874:69 clippy::redundant_closure_for_method_calls "redundant closure" +target/lintcheck/sources/cargo-0.49.0/src/cargo/ops/cargo_doc.rs:20:1 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/ops/cargo_fetch.rs:15:1 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/ops/cargo_fetch.rs:27:46 clippy::redundant_closure_for_method_calls "redundant closure" +target/lintcheck/sources/cargo-0.49.0/src/cargo/ops/cargo_generate_lockfile.rs:160:5 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope" +target/lintcheck/sources/cargo-0.49.0/src/cargo/ops/cargo_generate_lockfile.rs:175:5 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope" +target/lintcheck/sources/cargo-0.49.0/src/cargo/ops/cargo_generate_lockfile.rs:22:1 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/ops/cargo_generate_lockfile.rs:37:1 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/ops/cargo_generate_lockfile.rs:37:1 clippy::missing_panics_doc "docs for function which may panic missing `# Panics` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/ops/cargo_generate_lockfile.rs:37:1 clippy::too_many_lines "this function has too many lines (171/100)" +target/lintcheck/sources/cargo-0.49.0/src/cargo/ops/cargo_install.rs:13:5 clippy::wildcard_imports "usage of wildcard import" +target/lintcheck/sources/cargo-0.49.0/src/cargo/ops/cargo_install.rs:148:1 clippy::fn_params_excessive_bools "more than 3 bools in function parameters" +target/lintcheck/sources/cargo-0.49.0/src/cargo/ops/cargo_install.rs:148:1 clippy::too_many_lines "this function has too many lines (316/100)" +target/lintcheck/sources/cargo-0.49.0/src/cargo/ops/cargo_install.rs:178:24 clippy::collapsible_else_if "this `else { if .. }` block can be collapsed" +target/lintcheck/sources/cargo-0.49.0/src/cargo/ops/cargo_install.rs:202:17 clippy::redundant_closure_for_method_calls "redundant closure" +target/lintcheck/sources/cargo-0.49.0/src/cargo/ops/cargo_install.rs:236:16 clippy::collapsible_else_if "this `else { if .. }` block can be collapsed" +target/lintcheck/sources/cargo-0.49.0/src/cargo/ops/cargo_install.rs:312:64 clippy::redundant_closure_for_method_calls "redundant closure" +target/lintcheck/sources/cargo-0.49.0/src/cargo/ops/cargo_install.rs:32:13 clippy::let_underscore_drop "non-binding `let` on a type that implements `Drop`" +target/lintcheck/sources/cargo-0.49.0/src/cargo/ops/cargo_install.rs:339:12 clippy::collapsible_else_if "this `else { if .. }` block can be collapsed" +target/lintcheck/sources/cargo-0.49.0/src/cargo/ops/cargo_install.rs:37:1 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/ops/cargo_install.rs:454:22 clippy::redundant_closure_for_method_calls "redundant closure" +target/lintcheck/sources/cargo-0.49.0/src/cargo/ops/cargo_install.rs:483:5 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope" +target/lintcheck/sources/cargo-0.49.0/src/cargo/ops/cargo_install.rs:683:1 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/ops/cargo_install.rs:708:5 clippy::manual_flatten "unnecessary `if let` since only the `Some` variant of the iterator element is used" +target/lintcheck/sources/cargo-0.49.0/src/cargo/ops/cargo_new.rs:101:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/ops/cargo_new.rs:245:5 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope" +target/lintcheck/sources/cargo-0.49.0/src/cargo/ops/cargo_new.rs:251:5 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope" +target/lintcheck/sources/cargo-0.49.0/src/cargo/ops/cargo_new.rs:367:1 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/ops/cargo_new.rs:405:1 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/ops/cargo_new.rs:489:5 clippy::doc_markdown "you should put `IgnoreList` between ticks in the documentation" +target/lintcheck/sources/cargo-0.49.0/src/cargo/ops/cargo_new.rs:525:47 clippy::doc_markdown "you should put `IgnoreList` between ticks in the documentation" +target/lintcheck/sources/cargo-0.49.0/src/cargo/ops/cargo_new.rs:525:9 clippy::doc_markdown "you should put `format_existing` between ticks in the documentation" +target/lintcheck/sources/cargo-0.49.0/src/cargo/ops/cargo_new.rs:572:34 clippy::match_same_arms "this `match` has identical arm bodies" +target/lintcheck/sources/cargo-0.49.0/src/cargo/ops/cargo_new.rs:623:1 clippy::too_many_lines "this function has too many lines (130/100)" +target/lintcheck/sources/cargo-0.49.0/src/cargo/ops/cargo_new.rs:781:5 clippy::filter_map_next "called `filter_map(..).next()` on an `Iterator`. This is more succinctly expressed by calling `.find_map(..)` instead" +target/lintcheck/sources/cargo-0.49.0/src/cargo/ops/cargo_new.rs:800:16 clippy::single_match_else "you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let`" +target/lintcheck/sources/cargo-0.49.0/src/cargo/ops/cargo_output_metadata.rs:163:36 clippy::redundant_closure_for_method_calls "redundant closure" +target/lintcheck/sources/cargo-0.49.0/src/cargo/ops/cargo_output_metadata.rs:27:1 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/ops/cargo_output_metadata.rs:45:45 clippy::redundant_closure_for_method_calls "redundant closure" +target/lintcheck/sources/cargo-0.49.0/src/cargo/ops/cargo_package.rs:144:1 clippy::too_many_lines "this function has too many lines (112/100)" +target/lintcheck/sources/cargo-0.49.0/src/cargo/ops/cargo_package.rs:207:13 clippy::single_match_else "you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let`" +target/lintcheck/sources/cargo-0.49.0/src/cargo/ops/cargo_package.rs:25:1 clippy::struct_excessive_bools "more than 3 bools in a struct" +target/lintcheck/sources/cargo-0.49.0/src/cargo/ops/cargo_package.rs:307:54 clippy::redundant_closure_for_method_calls "redundant closure" +target/lintcheck/sources/cargo-0.49.0/src/cargo/ops/cargo_package.rs:394:5 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope" +target/lintcheck/sources/cargo-0.49.0/src/cargo/ops/cargo_package.rs:425:61 clippy::redundant_closure_for_method_calls "redundant closure" +target/lintcheck/sources/cargo-0.49.0/src/cargo/ops/cargo_package.rs:459:5 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope" +target/lintcheck/sources/cargo-0.49.0/src/cargo/ops/cargo_package.rs:66:1 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/ops/cargo_package.rs:69:9 clippy::let_underscore_drop "non-binding `let` on a type that implements `Drop`" +target/lintcheck/sources/cargo-0.49.0/src/cargo/ops/cargo_package.rs:93:20 clippy::if_not_else "unnecessary boolean `not` operation" +target/lintcheck/sources/cargo-0.49.0/src/cargo/ops/cargo_pkgid.rs:5:1 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/ops/cargo_read_manifest.rs:14:1 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/ops/cargo_read_manifest.rs:171:5 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope" +target/lintcheck/sources/cargo-0.49.0/src/cargo/ops/cargo_read_manifest.rs:37:1 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/ops/cargo_read_manifest.rs:37:1 clippy::missing_panics_doc "docs for function which may panic missing `# Panics` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/ops/cargo_read_manifest.rs:57:49 clippy::redundant_closure_for_method_calls "redundant closure" +target/lintcheck/sources/cargo-0.49.0/src/cargo/ops/cargo_read_manifest.rs:69:37 clippy::redundant_closure_for_method_calls "redundant closure" +target/lintcheck/sources/cargo-0.49.0/src/cargo/ops/cargo_run.rs:25:24 clippy::if_not_else "unnecessary boolean `not` operation" +target/lintcheck/sources/cargo-0.49.0/src/cargo/ops/cargo_run.rs:35:9 clippy::if_not_else "unnecessary boolean `not` operation" +target/lintcheck/sources/cargo-0.49.0/src/cargo/ops/cargo_run.rs:37:16 clippy::redundant_else "redundant else block" +target/lintcheck/sources/cargo-0.49.0/src/cargo/ops/cargo_run.rs:53:9 clippy::if_not_else "unnecessary boolean `not` operation" +target/lintcheck/sources/cargo-0.49.0/src/cargo/ops/cargo_run.rs:65:16 clippy::redundant_else "redundant else block" +target/lintcheck/sources/cargo-0.49.0/src/cargo/ops/cargo_run.rs:82:23 clippy::implicit_clone "implicitly cloning a `PathBuf` by calling `to_path_buf` on its dereferenced type" +target/lintcheck/sources/cargo-0.49.0/src/cargo/ops/cargo_run.rs:9:1 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/ops/cargo_test.rs:16:1 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/ops/cargo_test.rs:43:1 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/ops/cargo_test.rs:84:17 clippy::similar_names "binding's name is too similar to existing binding" +target/lintcheck/sources/cargo-0.49.0/src/cargo/ops/cargo_uninstall.rs:14:1 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/ops/cargo_uninstall.rs:7:5 clippy::wildcard_imports "usage of wildcard import" +target/lintcheck/sources/cargo-0.49.0/src/cargo/ops/common_for_install_and_uninstall.rs:147:9 clippy::doc_markdown "you should put `PackageId` between ticks in the documentation" +target/lintcheck/sources/cargo-0.49.0/src/cargo/ops/common_for_install_and_uninstall.rs:233:21 clippy::single_char_add_str "calling `push_str()` using a single-character string literal" +target/lintcheck/sources/cargo-0.49.0/src/cargo/ops/common_for_install_and_uninstall.rs:244:22 clippy::doc_markdown "you should put `PackageId` between ticks in the documentation" +target/lintcheck/sources/cargo-0.49.0/src/cargo/ops/common_for_install_and_uninstall.rs:244:63 clippy::doc_markdown "you should put `PackageId` between ticks in the documentation" +target/lintcheck/sources/cargo-0.49.0/src/cargo/ops/common_for_install_and_uninstall.rs:253:17 clippy::if_not_else "unnecessary boolean `not` operation" +target/lintcheck/sources/cargo-0.49.0/src/cargo/ops/common_for_install_and_uninstall.rs:370:5 clippy::unnecessary_wraps "this function's return value is unnecessary" +target/lintcheck/sources/cargo-0.49.0/src/cargo/ops/common_for_install_and_uninstall.rs:505:8 clippy::map_unwrap_or "called `map(<f>).unwrap_or_else(<g>)` on an `Option` value. This can be done more directly by calling `map_or_else(<g>, <f>)` instead" +target/lintcheck/sources/cargo-0.49.0/src/cargo/ops/common_for_install_and_uninstall.rs:525:10 clippy::needless_pass_by_value "this argument is passed by value, but not consumed in the function body" +target/lintcheck/sources/cargo-0.49.0/src/cargo/ops/common_for_install_and_uninstall.rs:542:27 clippy::redundant_closure_for_method_calls "redundant closure" +target/lintcheck/sources/cargo-0.49.0/src/cargo/ops/common_for_install_and_uninstall.rs:542:5 clippy::single_match_else "you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let`" +target/lintcheck/sources/cargo-0.49.0/src/cargo/ops/common_for_install_and_uninstall.rs:561:20 clippy::redundant_else "redundant else block" +target/lintcheck/sources/cargo-0.49.0/src/cargo/ops/common_for_install_and_uninstall.rs:613:5 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope" +target/lintcheck/sources/cargo-0.49.0/src/cargo/ops/common_for_install_and_uninstall.rs:645:41 clippy::doc_markdown "you should put `BTreeSet` between ticks in the documentation" +target/lintcheck/sources/cargo-0.49.0/src/cargo/ops/common_for_install_and_uninstall.rs:92:19 clippy::doc_markdown "you should put `InstallTracker` between ticks in the documentation" +target/lintcheck/sources/cargo-0.49.0/src/cargo/ops/fix.rs:200:1 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/ops/fix.rs:200:1 clippy::module_name_repetitions "item name starts with its containing module's name" +target/lintcheck/sources/cargo-0.49.0/src/cargo/ops/fix.rs:424:20 clippy::map_unwrap_or "called `map(<f>).unwrap_or(<a>)` on an `Option` value. This can be done more directly by calling `map_or(<a>, <f>)` instead" +target/lintcheck/sources/cargo-0.49.0/src/cargo/ops/fix.rs:455:13 clippy::similar_names "binding's name is too similar to existing binding" +target/lintcheck/sources/cargo-0.49.0/src/cargo/ops/fix.rs:506:17 clippy::similar_names "binding's name is too similar to existing binding" +target/lintcheck/sources/cargo-0.49.0/src/cargo/ops/fix.rs:608:9 clippy::field_reassign_with_default "field assignment outside of initializer for an instance created with Default::default()" +target/lintcheck/sources/cargo-0.49.0/src/cargo/ops/fix.rs:612:42 clippy::redundant_closure_for_method_calls "redundant closure" +target/lintcheck/sources/cargo-0.49.0/src/cargo/ops/fix.rs:619:48 clippy::manual_strip "stripping a prefix manually" +target/lintcheck/sources/cargo-0.49.0/src/cargo/ops/fix.rs:66:1 clippy::module_name_repetitions "item name starts with its containing module's name" +target/lintcheck/sources/cargo-0.49.0/src/cargo/ops/fix.rs:66:1 clippy::struct_excessive_bools "more than 3 bools in a struct" +target/lintcheck/sources/cargo-0.49.0/src/cargo/ops/fix.rs:708:18 clippy::match_same_arms "this `match` has identical arm bodies" +target/lintcheck/sources/cargo-0.49.0/src/cargo/ops/fix.rs:77:1 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/ops/lockfile.rs:154:13 clippy::single_char_add_str "calling `push_str()` using a single-character string literal" +target/lintcheck/sources/cargo-0.49.0/src/cargo/ops/lockfile.rs:217:9 clippy::single_char_add_str "calling `push_str()` using a single-character string literal" +target/lintcheck/sources/cargo-0.49.0/src/cargo/ops/lockfile.rs:30:1 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/ops/lockfile.rs:35:1 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/ops/lockfile.rs:35:1 clippy::module_name_repetitions "item name ends with its containing module's name" +target/lintcheck/sources/cargo-0.49.0/src/cargo/ops/lockfile.rs:87:1 clippy::unnecessary_wraps "this function's return value is unnecessarily wrapped by `Result`" +target/lintcheck/sources/cargo-0.49.0/src/cargo/ops/lockfile.rs:8:1 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/ops/lockfile.rs:8:1 clippy::module_name_repetitions "item name ends with its containing module's name" +target/lintcheck/sources/cargo-0.49.0/src/cargo/ops/registry.rs:150:21 clippy::redundant_closure_for_method_calls "redundant closure" +target/lintcheck/sources/cargo-0.49.0/src/cargo/ops/registry.rs:188:1 clippy::too_many_lines "this function has too many lines (130/100)" +target/lintcheck/sources/cargo-0.49.0/src/cargo/ops/registry.rs:212:32 clippy::if_not_else "unnecessary `!=` operation" +target/lintcheck/sources/cargo-0.49.0/src/cargo/ops/registry.rs:222:53 clippy::redundant_closure_for_method_calls "redundant closure" +target/lintcheck/sources/cargo-0.49.0/src/cargo/ops/registry.rs:224:44 clippy::redundant_closure_for_method_calls "redundant closure" +target/lintcheck/sources/cargo-0.49.0/src/cargo/ops/registry.rs:31:1 clippy::module_name_repetitions "item name starts with its containing module's name" +target/lintcheck/sources/cargo-0.49.0/src/cargo/ops/registry.rs:346:1 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/ops/registry.rs:346:1 clippy::module_name_repetitions "item name starts with its containing module's name" +target/lintcheck/sources/cargo-0.49.0/src/cargo/ops/registry.rs:351:26 clippy::single_match_else "you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let`" +target/lintcheck/sources/cargo-0.49.0/src/cargo/ops/registry.rs:385:12 clippy::needless_pass_by_value "this argument is passed by value, but not consumed in the function body" +target/lintcheck/sources/cargo-0.49.0/src/cargo/ops/registry.rs:386:15 clippy::needless_pass_by_value "this argument is passed by value, but not consumed in the function body" +target/lintcheck/sources/cargo-0.49.0/src/cargo/ops/registry.rs:38:1 clippy::struct_excessive_bools "more than 3 bools in a struct" +target/lintcheck/sources/cargo-0.49.0/src/cargo/ops/registry.rs:477:1 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/ops/registry.rs:483:1 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/ops/registry.rs:503:1 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/ops/registry.rs:505:38 clippy::default_trait_access "calling `util::config::CargoHttpConfig::default()` is more clear than this expression" +target/lintcheck/sources/cargo-0.49.0/src/cargo/ops/registry.rs:510:1 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/ops/registry.rs:529:5 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope" +target/lintcheck/sources/cargo-0.49.0/src/cargo/ops/registry.rs:53:1 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/ops/registry.rs:53:1 clippy::missing_panics_doc "docs for function which may panic missing `# Panics` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/ops/registry.rs:573:17 clippy::match_wildcard_for_single_variants "wildcard matches only a single variant and will also match any future added variants" +target/lintcheck/sources/cargo-0.49.0/src/cargo/ops/registry.rs:573:22 clippy::match_same_arms "this `match` has identical arm bodies" +target/lintcheck/sources/cargo-0.49.0/src/cargo/ops/registry.rs:608:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/ops/registry.rs:621:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/ops/registry.rs:671:1 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/ops/registry.rs:671:1 clippy::module_name_repetitions "item name starts with its containing module's name" +target/lintcheck/sources/cargo-0.49.0/src/cargo/ops/registry.rs:674:10 clippy::needless_pass_by_value "this argument is passed by value, but not consumed in the function body" +target/lintcheck/sources/cargo-0.49.0/src/cargo/ops/registry.rs:678:17 clippy::single_match_else "you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let`" +target/lintcheck/sources/cargo-0.49.0/src/cargo/ops/registry.rs:730:1 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/ops/registry.rs:731:16 clippy::single_match_else "you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let`" +target/lintcheck/sources/cargo-0.49.0/src/cargo/ops/registry.rs:785:1 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/ops/registry.rs:794:16 clippy::single_match_else "you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let`" +target/lintcheck/sources/cargo-0.49.0/src/cargo/ops/registry.rs:828:14 clippy::doc_markdown "you should put `SourceId` between ticks in the documentation" +target/lintcheck/sources/cargo-0.49.0/src/cargo/ops/registry.rs:848:1 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/ops/resolve.rs:199:1 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/ops/resolve.rs:199:1 clippy::module_name_repetitions "item name starts with its containing module's name" +target/lintcheck/sources/cargo-0.49.0/src/cargo/ops/resolve.rs:199:1 clippy::too_many_lines "this function has too many lines (137/100)" +target/lintcheck/sources/cargo-0.49.0/src/cargo/ops/resolve.rs:241:28 clippy::single_match_else "you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let`" +target/lintcheck/sources/cargo-0.49.0/src/cargo/ops/resolve.rs:28:1 clippy::module_name_repetitions "item name ends with its containing module's name" +target/lintcheck/sources/cargo-0.49.0/src/cargo/ops/resolve.rs:384:1 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/ops/resolve.rs:417:1 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/ops/resolve.rs:589:9 clippy::shadow_unrelated "`keep` is being shadowed" +target/lintcheck/sources/cargo-0.49.0/src/cargo/ops/resolve.rs:58:1 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/ops/resolve.rs:58:1 clippy::module_name_repetitions "item name starts with its containing module's name" +target/lintcheck/sources/cargo-0.49.0/src/cargo/ops/resolve.rs:602:5 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope" +target/lintcheck/sources/cargo-0.49.0/src/cargo/ops/resolve.rs:75:1 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/ops/resolve.rs:75:1 clippy::module_name_repetitions "item name starts with its containing module's name" +target/lintcheck/sources/cargo-0.49.0/src/cargo/ops/tree/format/mod.rs:103:21 clippy::match_wildcard_for_single_variants "wildcard matches only a single variant and will also match any future added variants" +target/lintcheck/sources/cargo-0.49.0/src/cargo/ops/tree/graph.rs:129:26 clippy::doc_markdown "you should put `PackageIds` between ticks in the documentation" +target/lintcheck/sources/cargo-0.49.0/src/cargo/ops/tree/graph.rs:137:17 clippy::match_wildcard_for_single_variants "wildcard matches only a single variant and will also match any future added variants" +target/lintcheck/sources/cargo-0.49.0/src/cargo/ops/tree/graph.rs:152:15 clippy::match_on_vec_items "indexing into a vector may panic" +target/lintcheck/sources/cargo-0.49.0/src/cargo/ops/tree/graph.rs:173:9 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope" +target/lintcheck/sources/cargo-0.49.0/src/cargo/ops/tree/graph.rs:234:46 clippy::filter_map "called `filter(..).flat_map(..)` on an `Iterator`" +target/lintcheck/sources/cargo-0.49.0/src/cargo/ops/tree/graph.rs:328:44 clippy::match_same_arms "this `match` has identical arm bodies" +target/lintcheck/sources/cargo-0.49.0/src/cargo/ops/tree/graph.rs:330:50 clippy::match_same_arms "this `match` has identical arm bodies" +target/lintcheck/sources/cargo-0.49.0/src/cargo/ops/tree/graph.rs:563:35 clippy::single_match_else "you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let`" +target/lintcheck/sources/cargo-0.49.0/src/cargo/ops/tree/mod.rs:112:11 clippy::non_ascii_literal "literal non-ASCII character detected" +target/lintcheck/sources/cargo-0.49.0/src/cargo/ops/tree/mod.rs:113:10 clippy::non_ascii_literal "literal non-ASCII character detected" +target/lintcheck/sources/cargo-0.49.0/src/cargo/ops/tree/mod.rs:114:10 clippy::non_ascii_literal "literal non-ASCII character detected" +target/lintcheck/sources/cargo-0.49.0/src/cargo/ops/tree/mod.rs:115:12 clippy::non_ascii_literal "literal non-ASCII character detected" +target/lintcheck/sources/cargo-0.49.0/src/cargo/ops/tree/mod.rs:126:1 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/ops/tree/mod.rs:21:1 clippy::module_name_repetitions "item name starts with its containing module's name" +target/lintcheck/sources/cargo-0.49.0/src/cargo/ops/tree/mod.rs:21:1 clippy::struct_excessive_bools "more than 3 bools in a struct" +target/lintcheck/sources/cargo-0.49.0/src/cargo/ops/tree/mod.rs:360:30 clippy::match_same_arms "this `match` has identical arm bodies" +target/lintcheck/sources/cargo-0.49.0/src/cargo/ops/tree/mod.rs:58:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/ops/vendor.rs:14:1 clippy::module_name_repetitions "item name starts with its containing module's name" +target/lintcheck/sources/cargo-0.49.0/src/cargo/ops/vendor.rs:215:9 clippy::let_underscore_drop "non-binding `let` on a type that implements `Drop`" +target/lintcheck/sources/cargo-0.49.0/src/cargo/ops/vendor.rs:21:1 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/ops/vendor.rs:21:1 clippy::missing_panics_doc "docs for function which may panic missing `# Panics` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/ops/vendor.rs:311:13 clippy::unnested_or_patterns "unnested or-patterns" +target/lintcheck/sources/cargo-0.49.0/src/cargo/ops/vendor.rs:314:34 clippy::match_same_arms "this `match` has identical arm bodies" +target/lintcheck/sources/cargo-0.49.0/src/cargo/ops/vendor.rs:320:29 clippy::case_sensitive_file_extension_comparisons "case-sensitive file extension comparison" +target/lintcheck/sources/cargo-0.49.0/src/cargo/ops/vendor.rs:320:60 clippy::case_sensitive_file_extension_comparisons "case-sensitive file extension comparison" +target/lintcheck/sources/cargo-0.49.0/src/cargo/ops/vendor.rs:70:1 clippy::too_many_lines "this function has too many lines (175/100)" +target/lintcheck/sources/cargo-0.49.0/src/cargo/sources/config.rs:102:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/sources/config.rs:111:28 clippy::needless_question_mark "question mark operator is useless here" +target/lintcheck/sources/cargo-0.49.0/src/cargo/sources/config.rs:133:48 clippy::needless_question_mark "question mark operator is useless here" +target/lintcheck/sources/cargo-0.49.0/src/cargo/sources/config.rs:135:67 clippy::redundant_closure_for_method_calls "redundant closure" +target/lintcheck/sources/cargo-0.49.0/src/cargo/sources/config.rs:206:36 clippy::needless_pass_by_value "this argument is passed by value, but not consumed in the function body" +target/lintcheck/sources/cargo-0.49.0/src/cargo/sources/config.rs:282:9 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope" +target/lintcheck/sources/cargo-0.49.0/src/cargo/sources/config.rs:70:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/sources/config.rs:81:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/sources/config.rs:97:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/sources/directory.rs:14:1 clippy::module_name_repetitions "item name starts with its containing module's name" +target/lintcheck/sources/cargo-0.49.0/src/cargo/sources/directory.rs:90:56 clippy::redundant_closure_for_method_calls "redundant closure" +target/lintcheck/sources/cargo-0.49.0/src/cargo/sources/git/source.rs:14:1 clippy::module_name_repetitions "item name ends with its containing module's name" +target/lintcheck/sources/cargo-0.49.0/src/cargo/sources/git/source.rs:25:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/sources/git/source.rs:49:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/sources/git/source.rs:53:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/sources/git/source.rs:53:5 clippy::missing_panics_doc "docs for function which may panic missing `# Panics` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/sources/git/source.rs:69:20 clippy::comparison_to_empty "comparison to empty slice" +target/lintcheck/sources/cargo-0.49.0/src/cargo/sources/git/utils.rs:1025:19 clippy::single_match_else "you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let`" +target/lintcheck/sources/cargo-0.49.0/src/cargo/sources/git/utils.rs:1157:36 clippy::case_sensitive_file_extension_comparisons "case-sensitive file extension comparison" +target/lintcheck/sources/cargo-0.49.0/src/cargo/sources/git/utils.rs:1158:9 clippy::manual_strip "stripping a suffix manually" +target/lintcheck/sources/cargo-0.49.0/src/cargo/sources/git/utils.rs:176:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/sources/git/utils.rs:180:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/sources/git/utils.rs:184:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/sources/git/utils.rs:188:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/sources/git/utils.rs:242:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/sources/git/utils.rs:253:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/sources/git/utils.rs:262:13 clippy::if_not_else "unnecessary boolean `not` operation" +target/lintcheck/sources/cargo-0.49.0/src/cargo/sources/git/utils.rs:289:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/sources/git/utils.rs:294:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/sources/git/utils.rs:298:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/sources/git/utils.rs:308:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/sources/git/utils.rs:472:9 clippy::let_underscore_drop "non-binding `let` on a type that implements `Drop`" +target/lintcheck/sources/cargo-0.49.0/src/cargo/sources/git/utils.rs:489:9 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope" +target/lintcheck/sources/cargo-0.49.0/src/cargo/sources/git/utils.rs:503:9 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope" +target/lintcheck/sources/cargo-0.49.0/src/cargo/sources/git/utils.rs:528:28 clippy::single_match_else "you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let`" +target/lintcheck/sources/cargo-0.49.0/src/cargo/sources/git/utils.rs:537:21 clippy::let_underscore_drop "non-binding `let` on a type that implements `Drop`" +target/lintcheck/sources/cargo-0.49.0/src/cargo/sources/git/utils.rs:588:1 clippy::too_many_lines "this function has too many lines (135/100)" +target/lintcheck/sources/cargo-0.49.0/src/cargo/sources/git/utils.rs:692:9 clippy::vec_init_then_push "calls to `push` immediately after creation" +target/lintcheck/sources/cargo-0.49.0/src/cargo/sources/git/utils.rs:758:9 clippy::single_char_add_str "calling `push_str()` using a single-character string literal" +target/lintcheck/sources/cargo-0.49.0/src/cargo/sources/git/utils.rs:858:1 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/sources/path.rs:129:44 clippy::match_same_arms "this `match` has identical arm bodies" +target/lintcheck/sources/cargo-0.49.0/src/cargo/sources/path.rs:143:44 clippy::match_same_arms "this `match` has identical arm bodies" +target/lintcheck/sources/cargo-0.49.0/src/cargo/sources/path.rs:15:1 clippy::module_name_repetitions "item name starts with its containing module's name" +target/lintcheck/sources/cargo-0.49.0/src/cargo/sources/path.rs:282:50 clippy::redundant_closure_for_method_calls "redundant closure" +target/lintcheck/sources/cargo-0.49.0/src/cargo/sources/path.rs:313:21 clippy::similar_names "binding's name is too similar to existing binding" +target/lintcheck/sources/cargo-0.49.0/src/cargo/sources/path.rs:314:21 clippy::similar_names "binding's name is too similar to existing binding" +target/lintcheck/sources/cargo-0.49.0/src/cargo/sources/path.rs:319:21 clippy::similar_names "binding's name is too similar to existing binding" +target/lintcheck/sources/cargo-0.49.0/src/cargo/sources/path.rs:339:9 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope" +target/lintcheck/sources/cargo-0.49.0/src/cargo/sources/path.rs:339:9 clippy::unnecessary_wraps "this function's return value is unnecessarily wrapped by `Result`" +target/lintcheck/sources/cargo-0.49.0/src/cargo/sources/path.rs:380:9 clippy::unused_self "unused `self` argument" +target/lintcheck/sources/cargo-0.49.0/src/cargo/sources/path.rs:419:50 clippy::redundant_closure_for_method_calls "redundant closure" +target/lintcheck/sources/cargo-0.49.0/src/cargo/sources/path.rs:429:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/sources/path.rs:460:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/sources/path.rs:63:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/sources/path.rs:77:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/sources/path.rs:98:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/sources/registry/index.rs:117:23 clippy::unseparated_literal_suffix "integer type suffix should be separated by an underscore" +target/lintcheck/sources/cargo-0.49.0/src/cargo/sources/registry/index.rs:121:70 clippy::unseparated_literal_suffix "integer type suffix should be separated by an underscore" +target/lintcheck/sources/cargo-0.49.0/src/cargo/sources/registry/index.rs:167:1 clippy::module_name_repetitions "item name ends with its containing module's name" +target/lintcheck/sources/cargo-0.49.0/src/cargo/sources/registry/index.rs:215:1 clippy::module_name_repetitions "item name starts with its containing module's name" +target/lintcheck/sources/cargo-0.49.0/src/cargo/sources/registry/index.rs:324:23 clippy::redundant_closure_for_method_calls "redundant closure" +target/lintcheck/sources/cargo-0.49.0/src/cargo/sources/registry/index.rs:468:40 clippy::doc_markdown "you should put `SourceId` between ticks in the documentation" +target/lintcheck/sources/cargo-0.49.0/src/cargo/sources/registry/index.rs:590:9 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope" +target/lintcheck/sources/cargo-0.49.0/src/cargo/sources/registry/index.rs:648:17 clippy::similar_names "binding's name is too similar to existing binding" +target/lintcheck/sources/cargo-0.49.0/src/cargo/sources/registry/index.rs:736:1 clippy::needless_lifetimes "explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration)" +target/lintcheck/sources/cargo-0.49.0/src/cargo/sources/registry/index.rs:95:37 clippy::cast_possible_truncation "casting `usize` to `u32` may truncate the value on targets with 64-bit wide pointers" +target/lintcheck/sources/cargo-0.49.0/src/cargo/sources/registry/local.rs:12:1 clippy::module_name_repetitions "item name starts with its containing module's name" +target/lintcheck/sources/cargo-0.49.0/src/cargo/sources/registry/mod.rs:192:1 clippy::module_name_repetitions "item name starts with its containing module's name" +target/lintcheck/sources/cargo-0.49.0/src/cargo/sources/registry/mod.rs:203:1 clippy::module_name_repetitions "item name starts with its containing module's name" +target/lintcheck/sources/cargo-0.49.0/src/cargo/sources/registry/mod.rs:229:1 clippy::module_name_repetitions "item name starts with its containing module's name" +target/lintcheck/sources/cargo-0.49.0/src/cargo/sources/registry/mod.rs:372:1 clippy::module_name_repetitions "item name starts with its containing module's name" +target/lintcheck/sources/cargo-0.49.0/src/cargo/sources/registry/mod.rs:373:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/sources/registry/mod.rs:375:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/sources/registry/mod.rs:381:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/sources/registry/mod.rs:382:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/sources/registry/mod.rs:383:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/sources/registry/mod.rs:384:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/sources/registry/mod.rs:452:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/sources/registry/mod.rs:582:20 clippy::redundant_else "redundant else block" +target/lintcheck/sources/cargo-0.49.0/src/cargo/sources/registry/mod.rs:621:9 clippy::if_not_else "unnecessary `!=` operation" +target/lintcheck/sources/cargo-0.49.0/src/cargo/sources/registry/remote.rs:139:17 clippy::unused_self "unused `self` argument" +target/lintcheck/sources/cargo-0.49.0/src/cargo/sources/registry/remote.rs:32:1 clippy::module_name_repetitions "item name starts with its containing module's name" +target/lintcheck/sources/cargo-0.49.0/src/cargo/sources/registry/remote.rs:72:13 clippy::single_match_else "you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let`" +target/lintcheck/sources/cargo-0.49.0/src/cargo/sources/replaced.rs:12:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/sources/replaced.rs:5:1 clippy::module_name_repetitions "item name starts with its containing module's name" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/canonical_url.rs:19:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/canonical_url.rs:19:5 clippy::missing_panics_doc "docs for function which may panic missing `# Panics` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/canonical_url.rs:50:41 clippy::case_sensitive_file_extension_comparisons "case-sensitive file extension comparison" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/canonical_url.rs:65:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/command_prelude.rs:218:1 clippy::must_use_candidate "this function could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/command_prelude.rs:222:1 clippy::must_use_candidate "this function could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/command_prelude.rs:234:1 clippy::must_use_candidate "this function could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/command_prelude.rs:249:1 clippy::must_use_candidate "this function could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/command_prelude.rs:264:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/command_prelude.rs:279:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/command_prelude.rs:298:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/command_prelude.rs:320:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/command_prelude.rs:328:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/command_prelude.rs:352:13 clippy::if_not_else "unnecessary boolean `not` operation" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/command_prelude.rs:363:13 clippy::if_not_else "unnecessary boolean `not` operation" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/command_prelude.rs:378:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/command_prelude.rs:387:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/command_prelude.rs:387:5 clippy::too_many_lines "this function has too many lines (104/100)" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/command_prelude.rs:39:20 clippy::doc_markdown "you should put `arg_package_spec` between ticks in the documentation" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/command_prelude.rs:504:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/command_prelude.rs:516:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/command_prelude.rs:530:40 clippy::redundant_closure_for_method_calls "redundant closure" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/command_prelude.rs:531:43 clippy::redundant_closure_for_method_calls "redundant closure" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/command_prelude.rs:536:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/command_prelude.rs:556:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/command_prelude.rs:575:49 clippy::redundant_closure_for_method_calls "redundant closure" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/command_prelude.rs:580:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/command_prelude.rs:631:18 clippy::redundant_closure_for_method_calls "redundant closure" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/command_prelude.rs:638:18 clippy::redundant_closure_for_method_calls "redundant closure" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/command_prelude.rs:647:1 clippy::must_use_candidate "this function could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/command_prelude.rs:651:1 clippy::must_use_candidate "this function could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/command_prelude.rs:662:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/command_prelude.rs:665:51 clippy::match_same_arms "this `match` has identical arm bodies" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/config/de.rs:420:16 clippy::needless_pass_by_value "this argument is passed by value, but not consumed in the function body" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/config/de.rs:46:25 clippy::doc_markdown "you should put `CV::List` between ticks in the documentation" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/config/de.rs:47:24 clippy::doc_markdown "you should put `ConfigSeqAccess` between ticks in the documentation" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/config/de.rs:527:53 clippy::unseparated_literal_suffix "integer type suffix should be separated by an underscore" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/config/de.rs:530:53 clippy::unseparated_literal_suffix "integer type suffix should be separated by an underscore" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/config/de.rs:532:68 clippy::unseparated_literal_suffix "integer type suffix should be separated by an underscore" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/config/key.rs:11:1 clippy::module_name_repetitions "item name ends with its containing module's name" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/config/key.rs:69:9 clippy::single_char_add_str "calling `push_str()` using a single-character string literal" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/config/mod.rs:100:71 clippy::doc_markdown "you should put `OptValue` between ticks in the documentation" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/config/mod.rs:100:71 clippy::doc_markdown "you should put `OptValue` between ticks in the documentation" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/config/mod.rs:100:71 clippy::doc_markdown "you should put `OptValue` between ticks in the documentation" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/config/mod.rs:1049:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/config/mod.rs:1064:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/config/mod.rs:1090:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/config/mod.rs:1166:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/config/mod.rs:1179:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/config/mod.rs:1181:33 clippy::needless_question_mark "question mark operator is useless here" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/config/mod.rs:1184:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/config/mod.rs:1186:33 clippy::needless_question_mark "question mark operator is useless here" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/config/mod.rs:1189:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/config/mod.rs:1191:33 clippy::needless_question_mark "question mark operator is useless here" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/config/mod.rs:1203:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/config/mod.rs:1211:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/config/mod.rs:1216:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/config/mod.rs:1225:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/config/mod.rs:1229:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/config/mod.rs:124:1 clippy::struct_excessive_bools "more than 3 bools in a struct" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/config/mod.rs:1254:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/config/mod.rs:1279:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/config/mod.rs:1281:9 clippy::single_match_else "you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let`" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/config/mod.rs:1323:9 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/config/mod.rs:1339:39 clippy::unused_self "unused `self` argument" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/config/mod.rs:1344:1 clippy::module_name_repetitions "item name starts with its containing module's name" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/config/mod.rs:1420:1 clippy::module_name_repetitions "item name starts with its containing module's name" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/config/mod.rs:1531:13 clippy::unnested_or_patterns "unnested or-patterns" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/config/mod.rs:1553:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/config/mod.rs:1560:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/config/mod.rs:1567:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/config/mod.rs:1574:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/config/mod.rs:1581:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/config/mod.rs:1588:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/config/mod.rs:1598:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/config/mod.rs:1619:1 clippy::must_use_candidate "this function could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/config/mod.rs:1623:1 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/config/mod.rs:1623:1 clippy::missing_panics_doc "docs for function which may panic missing `# Panics` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/config/mod.rs:1623:64 clippy::needless_pass_by_value "this argument is passed by value, but not consumed in the function body" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/config/mod.rs:1649:9 clippy::option_if_let_else "use Option::map_or_else instead of an if let/else" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/config/mod.rs:1699:5 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/config/mod.rs:1730:1 clippy::module_name_repetitions "item name ends with its containing module's name" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/config/mod.rs:1757:1 clippy::module_name_repetitions "item name ends with its containing module's name" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/config/mod.rs:1770:1 clippy::module_name_repetitions "item name ends with its containing module's name" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/config/mod.rs:1778:1 clippy::module_name_repetitions "item name ends with its containing module's name" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/config/mod.rs:1804:1 clippy::module_name_repetitions "item name ends with its containing module's name" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/config/mod.rs:1896:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/config/mod.rs:1901:5 clippy::doc_markdown "you should put `StringList` between ticks in the documentation" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/config/mod.rs:259:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/config/mod.rs:298:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/config/mod.rs:311:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/config/mod.rs:318:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/config/mod.rs:353:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/config/mod.rs:401:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/config/mod.rs:411:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/config/mod.rs:419:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/config/mod.rs:431:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/config/mod.rs:449:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/config/mod.rs:454:16 clippy::option_if_let_else "use Option::map_or instead of an if let/else" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/config/mod.rs:547:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/config/mod.rs:556:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/config/mod.rs:582:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/config/mod.rs:595:20 clippy::doc_markdown "you should put `StringList` between ticks in the documentation" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/config/mod.rs:689:20 clippy::unused_self "unused `self` argument" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/config/mod.rs:699:5 clippy::fn_params_excessive_bools "more than 3 bools in function parameters" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/config/mod.rs:699:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/config/mod.rs:719:58 clippy::redundant_closure_for_method_calls "redundant closure" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/config/mod.rs:748:30 clippy::manual_map "manual implementation of `Option::map`" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/config/mod.rs:816:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/config/path.rs:10:1 clippy::module_name_repetitions "item name ends with its containing module's name" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/config/path.rs:14:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/config/path.rs:48:1 clippy::module_name_repetitions "item name starts with its containing module's name" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/config/target.rs:12:1 clippy::module_name_repetitions "item name starts with its containing module's name" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/config/target.rs:24:1 clippy::module_name_repetitions "item name starts with its containing module's name" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/config/value.rs:29:1 clippy::module_name_repetitions "item name ends with its containing module's name" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/config/value.rs:70:5 clippy::missing_panics_doc "docs for function which may panic missing `# Panics` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/config/value.rs:80:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/config/value.rs:81:9 clippy::match_like_matches_macro "match expression looks like `matches!` macro" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/cpu.rs:11:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/cpu.rs:22:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/cpu.rs:82:25 clippy::cast_precision_loss "casting `u64` to `f64` causes a loss of precision (`u64` is 64 bits wide, but `f64`'s mantissa is only 52 bits wide)" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/cpu.rs:82:9 clippy::cast_precision_loss "casting `u64` to `f64` causes a loss of precision (`u64` is 64 bits wide, but `f64`'s mantissa is only 52 bits wide)" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/dependency_queue.rs:109:27 clippy::redundant_closure_for_method_calls "redundant closure" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/dependency_queue.rs:125:5 clippy::missing_panics_doc "docs for function which may panic missing `# Panics` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/dependency_queue.rs:151:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/dependency_queue.rs:156:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/dependency_queue.rs:168:5 clippy::missing_panics_doc "docs for function which may panic missing `# Panics` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/dependency_queue.rs:46:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/dependency_queue.rs:91:9 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/diagnostic_server.rs:218:1 clippy::module_name_repetitions "item name ends with its containing module's name" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/diagnostic_server.rs:230:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/diagnostic_server.rs:242:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/diagnostic_server.rs:58:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/diagnostic_server.rs:96:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/diagnostic_server.rs:96:5 clippy::too_many_lines "this function has too many lines (110/100)" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/diagnostic_server.rs:99:21 clippy::shadow_unrelated "`msg` is being shadowed" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/errors.rs:101:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/errors.rs:143:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/errors.rs:150:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/errors.rs:15:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/errors.rs:237:5 clippy::pub_enum_variant_names "variant name ends with the enum's name" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/errors.rs:245:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/errors.rs:289:21 clippy::match_wildcard_for_single_variants "wildcard matches only a single variant and will also match any future added variants" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/errors.rs:293:13 clippy::match_wildcard_for_single_variants "wildcard matches only a single variant and will also match any future added variants" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/errors.rs:321:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/errors.rs:328:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/errors.rs:356:1 clippy::must_use_candidate "this function could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/errors.rs:391:5 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/errors.rs:392:13 clippy::wildcard_imports "usage of wildcard import" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/errors.rs:465:1 clippy::must_use_candidate "this function could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/errors.rs:473:5 clippy::manual_range_contains "manual `RangeInclusive::contains` implementation" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/errors.rs:66:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/flock.rs:115:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/flock.rs:11:5 clippy::wildcard_imports "usage of wildcard import" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/flock.rs:134:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/flock.rs:142:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/flock.rs:150:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/flock.rs:156:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/flock.rs:170:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/flock.rs:192:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/flock.rs:29:5 clippy::missing_panics_doc "docs for function which may panic missing `# Panics` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/flock.rs:29:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/flock.rs:321:5 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/flock.rs:335:23 clippy::cast_possible_truncation "casting `i64` to `u32` may truncate the value" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/flock.rs:335:23 clippy::cast_sign_loss "casting `i64` to `u32` may lose the sign of the value" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/flock.rs:335:44 clippy::cast_possible_truncation "casting `i64` to `u32` may truncate the value" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/flock.rs:379:35 clippy::match_same_arms "this `match` has identical arm bodies" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/flock.rs:37:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/flock.rs:43:5 clippy::missing_panics_doc "docs for function which may panic missing `# Panics` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/flock.rs:43:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/flock.rs:52:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/flock.rs:52:5 clippy::missing_panics_doc "docs for function which may panic missing `# Panics` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/flock.rs:96:17 clippy::let_underscore_drop "non-binding `let` on a type that implements `Drop`" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/graph.rs:10:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/graph.rs:41:51 clippy::redundant_closure_for_method_calls "redundant closure" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/graph.rs:45:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/hasher.rs:12:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/hasher.rs:9:1 clippy::module_name_repetitions "item name ends with its containing module's name" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/hex.rs:10:9 clippy::cast_possible_truncation "casting `u64` to `u8` may truncate the value" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/hex.rs:11:9 clippy::cast_possible_truncation "casting `u64` to `u8` may truncate the value" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/hex.rs:12:9 clippy::cast_possible_truncation "casting `u64` to `u8` may truncate the value" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/hex.rs:13:9 clippy::cast_possible_truncation "casting `u64` to `u8` may truncate the value" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/hex.rs:14:9 clippy::cast_possible_truncation "casting `u64` to `u8` may truncate the value" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/hex.rs:15:9 clippy::cast_possible_truncation "casting `u64` to `u8` may truncate the value" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/hex.rs:25:1 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/hex.rs:6:1 clippy::module_name_repetitions "item name ends with its containing module's name" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/hex.rs:6:1 clippy::must_use_candidate "this function could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/hex.rs:8:9 clippy::cast_possible_truncation "casting `u64` to `u8` may truncate the value" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/hex.rs:9:9 clippy::cast_possible_truncation "casting `u64` to `u8` may truncate the value" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/important_paths.rs:23:1 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/important_paths.rs:6:1 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/interning.rs:66:5 clippy::missing_panics_doc "docs for function which may panic missing `# Panics` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/interning.rs:66:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/interning.rs:77:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/into_url.rs:10:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/into_url_with_base.rs:9:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/job.rs:20:1 clippy::must_use_candidate "this function could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/lev_distance.rs:3:1 clippy::must_use_candidate "this function could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/lockserver.rs:111:32 clippy::redundant_else "redundant else block" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/lockserver.rs:158:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/lockserver.rs:46:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/lockserver.rs:58:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/lockserver.rs:62:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/mod.rs:68:1 clippy::must_use_candidate "this function could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/mod.rs:79:1 clippy::must_use_candidate "this function could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/network.rs:12:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/network.rs:19:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/network.rs:84:1 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/paths.rs:109:12 clippy::redundant_else "redundant else block" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/paths.rs:114:1 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/paths.rs:121:1 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/paths.rs:125:1 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/paths.rs:130:1 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/paths.rs:14:1 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/paths.rs:14:1 clippy::module_name_repetitions "item name ends with its containing module's name" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/paths.rs:151:1 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/paths.rs:167:1 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/paths.rs:173:1 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/paths.rs:178:1 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/paths.rs:185:1 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/paths.rs:199:1 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/paths.rs:215:1 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/paths.rs:228:1 clippy::must_use_candidate "this function could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/paths.rs:251:9 clippy::option_if_let_else "use Option::map_or instead of an if let/else" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/paths.rs:267:1 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/paths.rs:276:1 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/paths.rs:29:1 clippy::must_use_candidate "this function could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/paths.rs:303:1 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/paths.rs:312:1 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/paths.rs:346:1 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/paths.rs:415:1 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/paths.rs:445:1 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/paths.rs:459:45 clippy::redundant_closure_for_method_calls "redundant closure" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/paths.rs:469:1 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/paths.rs:469:1 clippy::missing_panics_doc "docs for function which may panic missing `# Panics` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/paths.rs:514:5 clippy::let_underscore_drop "non-binding `let` on a type that implements `Drop`" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/paths.rs:54:1 clippy::must_use_candidate "this function could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/paths.rs:61:1 clippy::must_use_candidate "this function could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/paths.rs:63:19 clippy::option_if_let_else "use Option::map_or_else instead of an if let/else" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/paths.rs:88:1 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/paths.rs:93:31 clippy::comparison_to_empty "comparison to empty slice" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/process_builder.rs:106:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/process_builder.rs:111:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/process_builder.rs:122:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/process_builder.rs:132:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/process_builder.rs:152:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/process_builder.rs:185:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/process_builder.rs:190:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/process_builder.rs:218:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/process_builder.rs:218:5 clippy::missing_panics_doc "docs for function which may panic missing `# Panics` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/process_builder.rs:278:22 clippy::inconsistent_struct_constructor "struct constructor field order is inconsistent with struct definition field order" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/process_builder.rs:307:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/process_builder.rs:343:39 clippy::needless_pass_by_value "this argument is passed by value, but not consumed in the function body" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/progress.rs:122:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/progress.rs:136:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/progress.rs:15:1 clippy::module_name_repetitions "item name starts with its containing module's name" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/progress.rs:249:19 clippy::cast_precision_loss "casting `usize` to `f64` causes a loss of precision on targets with 64-bit wide pointers (`usize` is 64 bits wide, but `f64`'s mantissa is only 52 bits wide)" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/progress.rs:249:34 clippy::cast_precision_loss "casting `usize` to `f64` causes a loss of precision on targets with 64-bit wide pointers (`usize` is 64 bits wide, but `f64`'s mantissa is only 52 bits wide)" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/progress.rs:250:19 clippy::if_not_else "unnecessary boolean `not` operation" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/progress.rs:263:22 clippy::cast_precision_loss "casting `usize` to `f64` causes a loss of precision on targets with 64-bit wide pointers (`usize` is 64 bits wide, but `f64`'s mantissa is only 52 bits wide)" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/progress.rs:264:22 clippy::cast_possible_truncation "casting `f64` to `usize` may truncate the value" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/progress.rs:264:22 clippy::cast_sign_loss "casting `f64` to `usize` may lose the sign of the value" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/progress.rs:269:17 clippy::single_char_add_str "calling `push_str()` using a single-character string literal" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/progress.rs:272:17 clippy::single_char_add_str "calling `push_str()` using a single-character string literal" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/progress.rs:274:17 clippy::single_char_add_str "calling `push_str()` using a single-character string literal" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/progress.rs:280:13 clippy::single_char_add_str "calling `push_str()` using a single-character string literal" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/progress.rs:282:9 clippy::single_char_add_str "calling `push_str()` using a single-character string literal" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/progress.rs:89:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/progress.rs:97:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/queue.rs:25:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/queue.rs:36:5 clippy::missing_panics_doc "docs for function which may panic missing `# Panics` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/queue.rs:42:5 clippy::missing_panics_doc "docs for function which may panic missing `# Panics` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/queue.rs:52:5 clippy::missing_panics_doc "docs for function which may panic missing `# Panics` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/queue.rs:69:5 clippy::missing_panics_doc "docs for function which may panic missing `# Panics` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/read2.rs:11:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/read2.rs:31:17 clippy::similar_names "binding's name is too similar to existing binding" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/restricted_names.rs:13:1 clippy::must_use_candidate "this function could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/restricted_names.rs:26:1 clippy::must_use_candidate "this function could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/restricted_names.rs:35:1 clippy::must_use_candidate "this function could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/restricted_names.rs:45:1 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/restricted_names.rs:87:1 clippy::missing_panics_doc "docs for function which may panic missing `# Panics` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/restricted_names.rs:87:1 clippy::must_use_candidate "this function could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/restricted_names.rs:89:21 clippy::redundant_closure_for_method_calls "redundant closure" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/restricted_names.rs:8:1 clippy::must_use_candidate "this function could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/rustc.rs:103:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/rustc.rs:103:5 clippy::missing_panics_doc "docs for function which may panic missing `# Panics` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/rustc.rs:114:5 clippy::doc_markdown "you should put bare URLs between `<`/`>` or make a proper Markdown link" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/rustc.rs:115:5 clippy::doc_markdown "you should put bare URLs between `<`/`>` or make a proper Markdown link" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/rustc.rs:162:17 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/rustc.rs:39:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/sha256.rs:10:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/sha256.rs:16:9 clippy::let_underscore_drop "non-binding `let` on a type that implements `Drop`" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/sha256.rs:20:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/sha256.rs:31:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/sha256.rs:40:24 clippy::unseparated_literal_suffix "integer type suffix should be separated by an underscore" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/to_semver.rs:5:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/toml/mod.rs:1005:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/toml/mod.rs:1005:5 clippy::too_many_lines "this function has too many lines (282/100)" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/toml/mod.rs:1094:36 clippy::redundant_closure_for_method_calls "redundant closure" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/toml/mod.rs:1121:13 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/toml/mod.rs:1197:32 clippy::map_unwrap_or "called `map(<f>).unwrap_or_else(<g>)` on an `Option` value. This can be done more directly by calling `map_or_else(<g>, <f>)` instead" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/toml/mod.rs:124:1 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/toml/mod.rs:1504:9 clippy::unused_self "unused `self` argument" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/toml/mod.rs:1526:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/toml/mod.rs:1582:19 clippy::default_trait_access "calling `util::toml::DetailedTomlDependency::default()` is more clear than this expression" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/toml/mod.rs:1598:5 clippy::too_many_lines "this function has too many lines (153/100)" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/toml/mod.rs:1687:33 clippy::unnecessary_lazy_evaluations "unnecessary closure used to substitute value for `Option::None`" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/toml/mod.rs:178:1 clippy::module_name_repetitions "item name starts with its containing module's name" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/toml/mod.rs:248:1 clippy::module_name_repetitions "item name starts with its containing module's name" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/toml/mod.rs:274:1 clippy::module_name_repetitions "item name starts with its containing module's name" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/toml/mod.rs:277:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/toml/mod.rs:281:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/toml/mod.rs:285:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/toml/mod.rs:294:1 clippy::module_name_repetitions "item name starts with its containing module's name" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/toml/mod.rs:31:1 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/toml/mod.rs:381:35 clippy::cast_possible_truncation "casting `i64` to `u32` may truncate the value" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/toml/mod.rs:381:35 clippy::cast_sign_loss "casting `i64` to `u32` may lose the sign of the value" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/toml/mod.rs:388:35 clippy::cast_possible_truncation "casting `u64` to `u32` may truncate the value" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/toml/mod.rs:398:1 clippy::module_name_repetitions "item name starts with its containing module's name" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/toml/mod.rs:450:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/toml/mod.rs:536:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/toml/mod.rs:783:1 clippy::module_name_repetitions "item name starts with its containing module's name" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/toml/mod.rs:824:1 clippy::module_name_repetitions "item name starts with its containing module's name" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/toml/mod.rs:834:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/toml/mod.rs:83:42 clippy::redundant_closure_for_method_calls "redundant closure" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/toml/mod.rs:852:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/toml/mod.rs:852:5 clippy::missing_panics_doc "docs for function which may panic missing `# Panics` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/toml/mod.rs:852:5 clippy::too_many_lines "this function has too many lines (138/100)" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/toml/mod.rs:962:9 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/toml/mod.rs:979:9 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/toml/mod.rs:98:5 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/toml/mod.rs:999:23 clippy::default_trait_access "calling `util::toml::DetailedTomlDependency::default()` is more clear than this expression" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/toml/targets.rs:112:27 clippy::redundant_closure_for_method_calls "redundant closure" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/toml/targets.rs:325:5 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/toml/targets.rs:586:21 clippy::redundant_closure_for_method_calls "redundant closure" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/toml/targets.rs:593:42 clippy::redundant_closure_for_method_calls "redundant closure" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/toml/targets.rs:605:19 clippy::redundant_closure_for_method_calls "redundant closure" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/toml/targets.rs:612:42 clippy::redundant_closure_for_method_calls "redundant closure" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/vcs.rs:10:1 clippy::must_use_candidate "this function could have a `#[must_use]` attribute" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/vcs.rs:33:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/vcs.rs:37:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/vcs.rs:43:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/vcs.rs:47:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/vcs.rs:59:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/vcs.rs:66:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/workspace.rs:52:1 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/workspace.rs:56:1 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/workspace.rs:60:1 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/cargo-0.49.0/src/cargo/util/workspace.rs:64:1 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/cfg-expr-0.7.1/src/error.rs:107:13 clippy::enum_glob_use "usage of wildcard import for enum variants" +target/lintcheck/sources/cfg-expr-0.7.1/src/error.rs:55:13 clippy::unnested_or_patterns "unnested or-patterns" +target/lintcheck/sources/cfg-expr-0.7.1/src/error.rs:58:13 clippy::unnested_or_patterns "unnested or-patterns" +target/lintcheck/sources/cfg-expr-0.7.1/src/error.rs:5:1 clippy::module_name_repetitions "item name ends with its containing module's name" +target/lintcheck/sources/cfg-expr-0.7.1/src/error.rs:74:13 clippy::enum_glob_use "usage of wildcard import for enum variants" +target/lintcheck/sources/cfg-expr-0.7.1/src/error.rs:91:24 clippy::if_not_else "unnecessary boolean `not` operation" +target/lintcheck/sources/cfg-expr-0.7.1/src/expr/lexer.rs:102:9 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope" +target/lintcheck/sources/cfg-expr-0.7.1/src/expr/lexer.rs:125:33 clippy::redundant_slicing "redundant slicing of the whole range" +target/lintcheck/sources/cfg-expr-0.7.1/src/expr/lexer.rs:4:5 clippy::doc_markdown "you should put bare URLs between `<`/`>` or make a proper Markdown link" +target/lintcheck/sources/cfg-expr-0.7.1/src/expr/lexer.rs:58:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/cfg-expr-0.7.1/src/expr/lexer.rs:76:1 clippy::module_name_repetitions "item name starts with its containing module's name" +target/lintcheck/sources/cfg-expr-0.7.1/src/expr/lexer.rs:97:9 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope" +target/lintcheck/sources/cfg-expr-0.7.1/src/expr/mod.rs:351:13 clippy::enum_glob_use "usage of wildcard import for enum variants" +target/lintcheck/sources/cfg-expr-0.7.1/src/expr/mod.rs:408:13 clippy::match_wildcard_for_single_variants "wildcard matches only a single variant and will also match any future added variants" +target/lintcheck/sources/cfg-expr-0.7.1/src/expr/mod.rs:464:5 clippy::missing_panics_doc "docs for function which may panic missing `# Panics` section" +target/lintcheck/sources/cfg-expr-0.7.1/src/expr/mod.rs:57:13 clippy::enum_glob_use "usage of wildcard import for enum variants" +target/lintcheck/sources/cfg-expr-0.7.1/src/expr/mod.rs:586:33 clippy::match_same_arms "this `match` has identical arm bodies" +target/lintcheck/sources/cfg-expr-0.7.1/src/expr/mod.rs:599:32 clippy::match_same_arms "this `match` has identical arm bodies" +target/lintcheck/sources/cfg-expr-0.7.1/src/expr/mod.rs:609:9 clippy::manual_map "manual implementation of `Option::map`" +target/lintcheck/sources/cfg-expr-0.7.1/src/expr/parser.rs:116:31 clippy::similar_names "binding's name is too similar to existing binding" +target/lintcheck/sources/cfg-expr-0.7.1/src/expr/parser.rs:124:36 clippy::similar_names "binding's name is too similar to existing binding" +target/lintcheck/sources/cfg-expr-0.7.1/src/expr/parser.rs:17:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/cfg-expr-0.7.1/src/expr/parser.rs:17:5 clippy::too_many_lines "this function has too many lines (345/100)" +target/lintcheck/sources/cfg-expr-0.7.1/src/expr/parser.rs:211:21 clippy::unnested_or_patterns "unnested or-patterns" +target/lintcheck/sources/cfg-expr-0.7.1/src/expr/parser.rs:211:21 clippy::unnested_or_patterns "unnested or-patterns" +target/lintcheck/sources/cfg-expr-0.7.1/src/expr/parser.rs:211:21 clippy::unnested_or_patterns "unnested or-patterns" +target/lintcheck/sources/cfg-expr-0.7.1/src/expr/parser.rs:211:21 clippy::unnested_or_patterns "unnested or-patterns" +target/lintcheck/sources/cfg-expr-0.7.1/src/expr/parser.rs:211:21 clippy::unnested_or_patterns "unnested or-patterns" +target/lintcheck/sources/cfg-expr-0.7.1/src/expr/parser.rs:211:21 clippy::unnested_or_patterns "unnested or-patterns" +target/lintcheck/sources/cfg-expr-0.7.1/src/expr/parser.rs:211:21 clippy::unnested_or_patterns "unnested or-patterns" +target/lintcheck/sources/cfg-expr-0.7.1/src/expr/parser.rs:22:13 clippy::shadow_unrelated "`original` is being shadowed" +target/lintcheck/sources/cfg-expr-0.7.1/src/expr/parser.rs:238:21 clippy::unnested_or_patterns "unnested or-patterns" +target/lintcheck/sources/cfg-expr-0.7.1/src/expr/parser.rs:243:36 clippy::single_match_else "you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let`" +target/lintcheck/sources/cfg-expr-0.7.1/src/expr/parser.rs:254:34 clippy::single_match_else "you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let`" +target/lintcheck/sources/cfg-expr-0.7.1/src/expr/parser.rs:259:21 clippy::unnested_or_patterns "unnested or-patterns" +target/lintcheck/sources/cfg-expr-0.7.1/src/expr/parser.rs:25:9 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope" +target/lintcheck/sources/cfg-expr-0.7.1/src/expr/parser.rs:284:21 clippy::unnested_or_patterns "unnested or-patterns" +target/lintcheck/sources/cfg-expr-0.7.1/src/expr/parser.rs:292:21 clippy::unnested_or_patterns "unnested or-patterns" +target/lintcheck/sources/cfg-expr-0.7.1/src/expr/parser.rs:347:21 clippy::unnested_or_patterns "unnested or-patterns" +target/lintcheck/sources/cfg-expr-0.7.1/src/expr/parser.rs:390:9 clippy::single_match_else "you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let`" +target/lintcheck/sources/cfg-expr-0.7.1/src/expr/parser.rs:392:17 clippy::if_not_else "unnecessary `!=` operation" +target/lintcheck/sources/cfg-expr-0.7.1/src/expr/parser.rs:39:31 clippy::too_many_lines "this function has too many lines (136/100)" +target/lintcheck/sources/cfg-expr-0.7.1/src/expr/parser.rs:67:13 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope" +target/lintcheck/sources/cfg-expr-0.7.1/src/lib.rs:1:null clippy::cargo_common_metadata "package `cfg-expr` is missing `package.categories` metadata" +target/lintcheck/sources/cfg-expr-0.7.1/src/targets/builtins.rs:11:5 clippy::wildcard_imports "usage of wildcard import" +target/lintcheck/sources/cfg-expr-0.7.1/src/targets/mod.rs:139:1 clippy::must_use_candidate "this function could have a `#[must_use]` attribute" +target/lintcheck/sources/cfg-expr-0.7.1/src/targets/mod.rs:153:1 clippy::must_use_candidate "this function could have a `#[must_use]` attribute" +target/lintcheck/sources/cxx-1.0.32/src/rust_string.rs:15:20 clippy::ptr_as_ptr "`as` casting between raw pointers without changing its mutability" +target/lintcheck/sources/cxx-1.0.32/src/rust_string.rs:19:24 clippy::ptr_as_ptr "`as` casting between raw pointers without changing its mutability" +target/lintcheck/sources/cxx-1.0.32/src/rust_vec.rs:21:20 clippy::ptr_as_ptr "`as` casting between raw pointers without changing its mutability" +target/lintcheck/sources/cxx-1.0.32/src/rust_vec.rs:25:24 clippy::ptr_as_ptr "`as` casting between raw pointers without changing its mutability" +target/lintcheck/sources/cxx-1.0.32/src/rust_vec.rs:74:35 clippy::ptr_as_ptr "`as` casting between raw pointers without changing its mutability" +target/lintcheck/sources/cxx-1.0.32/src/rust_vec.rs:78:39 clippy::ptr_as_ptr "`as` casting between raw pointers without changing its mutability" +target/lintcheck/sources/cxx-1.0.32/src/rust_vec.rs:90:20 clippy::ptr_as_ptr "`as` casting between raw pointers without changing its mutability" +target/lintcheck/sources/cxx-1.0.32/src/rust_vec.rs:94:24 clippy::ptr_as_ptr "`as` casting between raw pointers without changing its mutability" +target/lintcheck/sources/cxx-1.0.32/src/shared_ptr.rs:108:20 clippy::ptr_as_ptr "`as` casting between raw pointers without changing its mutability" +target/lintcheck/sources/cxx-1.0.32/src/shared_ptr.rs:165:9 clippy::let_underscore_drop "non-binding `let` on a type that implements `Drop`" +target/lintcheck/sources/cxx-1.0.32/src/shared_ptr.rs:54:20 clippy::ptr_as_ptr "`as` casting between raw pointers without changing its mutability" +target/lintcheck/sources/cxx-1.0.32/src/shared_ptr.rs:62:20 clippy::ptr_as_ptr "`as` casting between raw pointers without changing its mutability" +target/lintcheck/sources/cxx-1.0.32/src/shared_ptr.rs:75:20 clippy::ptr_as_ptr "`as` casting between raw pointers without changing its mutability" +target/lintcheck/sources/cxx-1.0.32/src/unique_ptr.rs:185:9 clippy::let_underscore_drop "non-binding `let` on a type that implements `Drop`" +target/lintcheck/sources/cxx-1.0.32/src/unwind.rs:22:5 clippy::let_underscore_drop "non-binding `let` on a type that implements `Drop`" +target/lintcheck/sources/cxx-1.0.32/src/weak_ptr.rs:47:20 clippy::ptr_as_ptr "`as` casting between raw pointers without changing its mutability" +target/lintcheck/sources/cxx-1.0.32/src/weak_ptr.rs:80:20 clippy::ptr_as_ptr "`as` casting between raw pointers without changing its mutability" +target/lintcheck/sources/iron-0.6.1/src/error.rs:24:1 clippy::module_name_repetitions "item name ends with its containing module's name" +target/lintcheck/sources/iron-0.6.1/src/iron.rs:105:13 clippy::redundant_field_names "redundant field names in struct initialization" +target/lintcheck/sources/iron-0.6.1/src/iron.rs:119:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/iron-0.6.1/src/iron.rs:133:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/iron-0.6.1/src/iron.rs:143:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/iron-0.6.1/src/iron.rs:149:13 clippy::redundant_field_names "redundant field names in struct initialization" +target/lintcheck/sources/iron-0.6.1/src/iron.rs:167:49 clippy::similar_names "binding's name is too similar to existing binding" +target/lintcheck/sources/iron-0.6.1/src/iron.rs:196:9 clippy::let_underscore_drop "non-binding `let` on a type that implements `Drop`" +target/lintcheck/sources/iron-0.6.1/src/iron.rs:80:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/iron-0.6.1/src/iron.rs:85:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/iron-0.6.1/src/iron.rs:90:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/iron-0.6.1/src/lib.rs:1:null clippy::cargo_common_metadata "package `iron` is missing `package.categories` metadata" +target/lintcheck/sources/iron-0.6.1/src/lib.rs:1:null clippy::cargo_common_metadata "package `iron` is missing `package.keywords` metadata" +target/lintcheck/sources/iron-0.6.1/src/lib.rs:1:null clippy::multiple_crate_versions "multiple versions for dependency `log`: 0.3.9, 0.4.8" +target/lintcheck/sources/iron-0.6.1/src/middleware/mod.rs:137:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/iron-0.6.1/src/middleware/mod.rs:150:1 clippy::module_name_repetitions "item name ends with its containing module's name" +target/lintcheck/sources/iron-0.6.1/src/middleware/mod.rs:152:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/iron-0.6.1/src/middleware/mod.rs:159:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/iron-0.6.1/src/middleware/mod.rs:171:1 clippy::module_name_repetitions "item name ends with its containing module's name" +target/lintcheck/sources/iron-0.6.1/src/middleware/mod.rs:173:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/iron-0.6.1/src/middleware/mod.rs:182:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/iron-0.6.1/src/middleware/mod.rs:192:1 clippy::module_name_repetitions "item name ends with its containing module's name" +target/lintcheck/sources/iron-0.6.1/src/middleware/mod.rs:217:25 clippy::doc_markdown "you should put `ChainBuilder` between ticks in the documentation" +target/lintcheck/sources/iron-0.6.1/src/middleware/mod.rs:264:5 clippy::missing_panics_doc "docs for function which may panic missing `# Panics` section" +target/lintcheck/sources/iron-0.6.1/src/middleware/mod.rs:328:20 clippy::similar_names "binding's name is too similar to existing binding" +target/lintcheck/sources/iron-0.6.1/src/middleware/mod.rs:360:16 clippy::similar_names "binding's name is too similar to existing binding" +target/lintcheck/sources/iron-0.6.1/src/middleware/mod.rs:368:33 clippy::similar_names "binding's name is too similar to existing binding" +target/lintcheck/sources/iron-0.6.1/src/middleware/mod.rs:428:40 clippy::similar_names "binding's name is too similar to existing binding" +target/lintcheck/sources/iron-0.6.1/src/middleware/mod.rs:434:40 clippy::similar_names "binding's name is too similar to existing binding" +target/lintcheck/sources/iron-0.6.1/src/middleware/mod.rs:444:40 clippy::similar_names "binding's name is too similar to existing binding" +target/lintcheck/sources/iron-0.6.1/src/modifiers.rs:132:14 clippy::expect_fun_call "use of `expect` followed by a function call" +target/lintcheck/sources/iron-0.6.1/src/request/mod.rs:113:24 clippy::similar_names "binding's name is too similar to existing binding" +target/lintcheck/sources/iron-0.6.1/src/request/mod.rs:121:13 clippy::redundant_field_names "redundant field names in struct initialization" +target/lintcheck/sources/iron-0.6.1/src/request/mod.rs:123:13 clippy::redundant_field_names "redundant field names in struct initialization" +target/lintcheck/sources/iron-0.6.1/src/request/mod.rs:124:13 clippy::redundant_field_names "redundant field names in struct initialization" +target/lintcheck/sources/iron-0.6.1/src/request/mod.rs:126:13 clippy::redundant_field_names "redundant field names in struct initialization" +target/lintcheck/sources/iron-0.6.1/src/request/mod.rs:128:13 clippy::redundant_field_names "redundant field names in struct initialization" +target/lintcheck/sources/iron-0.6.1/src/request/mod.rs:153:69 clippy::doc_markdown "you should put `HttpReader` between ticks in the documentation" +target/lintcheck/sources/iron-0.6.1/src/request/mod.rs:154:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/iron-0.6.1/src/request/mod.rs:32:1 clippy::manual_non_exhaustive "this seems like a manual implementation of the non-exhaustive pattern" +target/lintcheck/sources/iron-0.6.1/src/request/mod.rs:75:34 clippy::doc_markdown "you should put `HttpRequest` between ticks in the documentation" +target/lintcheck/sources/iron-0.6.1/src/request/mod.rs:77:39 clippy::doc_markdown "you should put `HttpRequest` between ticks in the documentation" +target/lintcheck/sources/iron-0.6.1/src/request/mod.rs:78:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/iron-0.6.1/src/request/mod.rs:82:13 clippy::similar_names "binding's name is too similar to existing binding" +target/lintcheck/sources/iron-0.6.1/src/request/mod.rs:83:29 clippy::similar_names "binding's name is too similar to existing binding" +target/lintcheck/sources/iron-0.6.1/src/request/mod.rs:85:24 clippy::similar_names "binding's name is too similar to existing binding" +target/lintcheck/sources/iron-0.6.1/src/request/url.rs:109:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/iron-0.6.1/src/request/url.rs:117:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/iron-0.6.1/src/request/url.rs:129:1 clippy::from_over_into "an implementation of `From` is preferred since it gives you `Into<_>` for free where the reverse isn't true" +target/lintcheck/sources/iron-0.6.1/src/request/url.rs:21:14 clippy::doc_markdown "you should put bare URLs between `<`/`>` or make a proper Markdown link" +target/lintcheck/sources/iron-0.6.1/src/request/url.rs:22:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/iron-0.6.1/src/request/url.rs:31:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/iron-0.6.1/src/request/url.rs:47:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/iron-0.6.1/src/request/url.rs:52:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/iron-0.6.1/src/request/url.rs:57:5 clippy::missing_panics_doc "docs for function which may panic missing `# Panics` section" +target/lintcheck/sources/iron-0.6.1/src/request/url.rs:57:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/iron-0.6.1/src/request/url.rs:63:5 clippy::missing_panics_doc "docs for function which may panic missing `# Panics` section" +target/lintcheck/sources/iron-0.6.1/src/request/url.rs:63:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/iron-0.6.1/src/request/url.rs:73:5 clippy::missing_panics_doc "docs for function which may panic missing `# Panics` section" +target/lintcheck/sources/iron-0.6.1/src/request/url.rs:73:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/iron-0.6.1/src/request/url.rs:83:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/iron-0.6.1/src/request/url.rs:96:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/iron-0.6.1/src/response.rs:121:19 clippy::single_match_else "you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let`" +target/lintcheck/sources/iron-0.6.1/src/response.rs:125:43 clippy::redundant_closure_for_method_calls "redundant closure" +target/lintcheck/sources/iron-0.6.1/src/response.rs:139:41 clippy::redundant_closure_for_method_calls "redundant closure" +target/lintcheck/sources/iron-0.6.1/src/response.rs:24:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/iron-0.6.1/src/response.rs:95:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/iron-0.6.1/src/response.rs:95:5 clippy::new_without_default "you should consider adding a `Default` implementation for `response::Response`" +target/lintcheck/sources/libc-0.2.81/build.rs:114:19 clippy::map_unwrap_or "called `map(<f>).unwrap_or(<a>)` on an `Option` value. This can be done more directly by calling `map_or(<a>, <f>)` instead" +target/lintcheck/sources/libc-0.2.81/build.rs:124:5 clippy::question_mark "this block may be rewritten with the `?` operator" +target/lintcheck/sources/libc-0.2.81/build.rs:133:5 clippy::question_mark "this block may be rewritten with the `?` operator" +target/lintcheck/sources/libc-0.2.81/src/macros.rs:120:9 clippy::expl_impl_clone_on_copy "you are implementing `Clone` explicitly on a `Copy` type" +target/lintcheck/sources/libc-0.2.81/src/macros.rs:120:9 clippy::expl_impl_clone_on_copy "you are implementing `Clone` explicitly on a `Copy` type" +target/lintcheck/sources/libc-0.2.81/src/macros.rs:120:9 clippy::expl_impl_clone_on_copy "you are implementing `Clone` explicitly on a `Copy` type" +target/lintcheck/sources/libc-0.2.81/src/macros.rs:120:9 clippy::expl_impl_clone_on_copy "you are implementing `Clone` explicitly on a `Copy` type" +target/lintcheck/sources/libc-0.2.81/src/macros.rs:120:9 clippy::expl_impl_clone_on_copy "you are implementing `Clone` explicitly on a `Copy` type" +target/lintcheck/sources/libc-0.2.81/src/macros.rs:120:9 clippy::expl_impl_clone_on_copy "you are implementing `Clone` explicitly on a `Copy` type" +target/lintcheck/sources/libc-0.2.81/src/macros.rs:120:9 clippy::expl_impl_clone_on_copy "you are implementing `Clone` explicitly on a `Copy` type" +target/lintcheck/sources/libc-0.2.81/src/macros.rs:120:9 clippy::expl_impl_clone_on_copy "you are implementing `Clone` explicitly on a `Copy` type" +target/lintcheck/sources/libc-0.2.81/src/macros.rs:120:9 clippy::expl_impl_clone_on_copy "you are implementing `Clone` explicitly on a `Copy` type" +target/lintcheck/sources/libc-0.2.81/src/macros.rs:120:9 clippy::expl_impl_clone_on_copy "you are implementing `Clone` explicitly on a `Copy` type" +target/lintcheck/sources/libc-0.2.81/src/macros.rs:120:9 clippy::expl_impl_clone_on_copy "you are implementing `Clone` explicitly on a `Copy` type" +target/lintcheck/sources/libc-0.2.81/src/macros.rs:120:9 clippy::expl_impl_clone_on_copy "you are implementing `Clone` explicitly on a `Copy` type" +target/lintcheck/sources/libc-0.2.81/src/macros.rs:120:9 clippy::expl_impl_clone_on_copy "you are implementing `Clone` explicitly on a `Copy` type" +target/lintcheck/sources/libc-0.2.81/src/macros.rs:120:9 clippy::expl_impl_clone_on_copy "you are implementing `Clone` explicitly on a `Copy` type" +target/lintcheck/sources/libc-0.2.81/src/macros.rs:120:9 clippy::expl_impl_clone_on_copy "you are implementing `Clone` explicitly on a `Copy` type" +target/lintcheck/sources/libc-0.2.81/src/macros.rs:120:9 clippy::expl_impl_clone_on_copy "you are implementing `Clone` explicitly on a `Copy` type" +target/lintcheck/sources/libc-0.2.81/src/macros.rs:120:9 clippy::expl_impl_clone_on_copy "you are implementing `Clone` explicitly on a `Copy` type" +target/lintcheck/sources/libc-0.2.81/src/macros.rs:120:9 clippy::expl_impl_clone_on_copy "you are implementing `Clone` explicitly on a `Copy` type" +target/lintcheck/sources/libc-0.2.81/src/macros.rs:243:17 clippy::missing_safety_doc "unsafe function's docs miss `# Safety` section" +target/lintcheck/sources/libc-0.2.81/src/macros.rs:243:17 clippy::missing_safety_doc "unsafe function's docs miss `# Safety` section" +target/lintcheck/sources/libc-0.2.81/src/macros.rs:243:17 clippy::missing_safety_doc "unsafe function's docs miss `# Safety` section" +target/lintcheck/sources/libc-0.2.81/src/macros.rs:243:null clippy::must_use_candidate "this function could have a `#[must_use]` attribute" +target/lintcheck/sources/libc-0.2.81/src/macros.rs:243:null clippy::must_use_candidate "this function could have a `#[must_use]` attribute" +target/lintcheck/sources/libc-0.2.81/src/macros.rs:243:null clippy::must_use_candidate "this function could have a `#[must_use]` attribute" +target/lintcheck/sources/libc-0.2.81/src/macros.rs:243:null clippy::must_use_candidate "this function could have a `#[must_use]` attribute" +target/lintcheck/sources/libc-0.2.81/src/macros.rs:243:null clippy::must_use_candidate "this function could have a `#[must_use]` attribute" +target/lintcheck/sources/libc-0.2.81/src/macros.rs:243:null clippy::must_use_candidate "this function could have a `#[must_use]` attribute" +target/lintcheck/sources/libc-0.2.81/src/macros.rs:243:null clippy::must_use_candidate "this function could have a `#[must_use]` attribute" +target/lintcheck/sources/libc-0.2.81/src/macros.rs:243:null clippy::must_use_candidate "this function could have a `#[must_use]` attribute" +target/lintcheck/sources/libc-0.2.81/src/macros.rs:243:null clippy::must_use_candidate "this function could have a `#[must_use]` attribute" +target/lintcheck/sources/libc-0.2.81/src/macros.rs:243:null clippy::must_use_candidate "this function could have a `#[must_use]` attribute" +target/lintcheck/sources/libc-0.2.81/src/macros.rs:243:null clippy::must_use_candidate "this function could have a `#[must_use]` attribute" +target/lintcheck/sources/libc-0.2.81/src/macros.rs:243:null clippy::must_use_candidate "this function could have a `#[must_use]` attribute" +target/lintcheck/sources/libc-0.2.81/src/macros.rs:243:null clippy::must_use_candidate "this function could have a `#[must_use]` attribute" +target/lintcheck/sources/libc-0.2.81/src/macros.rs:243:null clippy::must_use_candidate "this function could have a `#[must_use]` attribute" +target/lintcheck/sources/libc-0.2.81/src/macros.rs:243:null clippy::must_use_candidate "this function could have a `#[must_use]` attribute" +target/lintcheck/sources/libc-0.2.81/src/macros.rs:243:null clippy::must_use_candidate "this function could have a `#[must_use]` attribute" +target/lintcheck/sources/libc-0.2.81/src/macros.rs:243:null clippy::must_use_candidate "this function could have a `#[must_use]` attribute" +target/lintcheck/sources/libc-0.2.81/src/macros.rs:243:null clippy::must_use_candidate "this function could have a `#[must_use]` attribute" +target/lintcheck/sources/libc-0.2.81/src/macros.rs:243:null clippy::must_use_candidate "this function could have a `#[must_use]` attribute" +target/lintcheck/sources/libc-0.2.81/src/macros.rs:243:null clippy::must_use_candidate "this function could have a `#[must_use]` attribute" +target/lintcheck/sources/libc-0.2.81/src/macros.rs:259:null clippy::must_use_candidate "this function could have a `#[must_use]` attribute" +target/lintcheck/sources/libc-0.2.81/src/macros.rs:259:null clippy::must_use_candidate "this function could have a `#[must_use]` attribute" +target/lintcheck/sources/libc-0.2.81/src/macros.rs:259:null clippy::must_use_candidate "this function could have a `#[must_use]` attribute" +target/lintcheck/sources/libc-0.2.81/src/macros.rs:259:null clippy::must_use_candidate "this function could have a `#[must_use]` attribute" +target/lintcheck/sources/libc-0.2.81/src/macros.rs:259:null clippy::must_use_candidate "this function could have a `#[must_use]` attribute" +target/lintcheck/sources/libc-0.2.81/src/macros.rs:259:null clippy::must_use_candidate "this function could have a `#[must_use]` attribute" +target/lintcheck/sources/libc-0.2.81/src/macros.rs:259:null clippy::must_use_candidate "this function could have a `#[must_use]` attribute" +target/lintcheck/sources/libc-0.2.81/src/macros.rs:259:null clippy::must_use_candidate "this function could have a `#[must_use]` attribute" +target/lintcheck/sources/libc-0.2.81/src/macros.rs:259:null clippy::must_use_candidate "this function could have a `#[must_use]` attribute" +target/lintcheck/sources/libc-0.2.81/src/macros.rs:259:null clippy::must_use_candidate "this function could have a `#[must_use]` attribute" +target/lintcheck/sources/libc-0.2.81/src/macros.rs:259:null clippy::must_use_candidate "this function could have a `#[must_use]` attribute" +target/lintcheck/sources/libc-0.2.81/src/macros.rs:259:null clippy::must_use_candidate "this function could have a `#[must_use]` attribute" +target/lintcheck/sources/libc-0.2.81/src/macros.rs:259:null clippy::must_use_candidate "this function could have a `#[must_use]` attribute" +target/lintcheck/sources/libc-0.2.81/src/macros.rs:259:null clippy::must_use_candidate "this function could have a `#[must_use]` attribute" +target/lintcheck/sources/libc-0.2.81/src/macros.rs:259:null clippy::must_use_candidate "this function could have a `#[must_use]` attribute" +target/lintcheck/sources/libc-0.2.81/src/macros.rs:84:9 clippy::expl_impl_clone_on_copy "you are implementing `Clone` explicitly on a `Copy` type" +target/lintcheck/sources/libc-0.2.81/src/macros.rs:84:9 clippy::expl_impl_clone_on_copy "you are implementing `Clone` explicitly on a `Copy` type" +target/lintcheck/sources/libc-0.2.81/src/macros.rs:84:9 clippy::expl_impl_clone_on_copy "you are implementing `Clone` explicitly on a `Copy` type" +target/lintcheck/sources/libc-0.2.81/src/macros.rs:84:9 clippy::expl_impl_clone_on_copy "you are implementing `Clone` explicitly on a `Copy` type" +target/lintcheck/sources/libc-0.2.81/src/macros.rs:84:9 clippy::expl_impl_clone_on_copy "you are implementing `Clone` explicitly on a `Copy` type" +target/lintcheck/sources/libc-0.2.81/src/macros.rs:84:9 clippy::expl_impl_clone_on_copy "you are implementing `Clone` explicitly on a `Copy` type" +target/lintcheck/sources/libc-0.2.81/src/macros.rs:84:9 clippy::expl_impl_clone_on_copy "you are implementing `Clone` explicitly on a `Copy` type" +target/lintcheck/sources/libc-0.2.81/src/macros.rs:84:9 clippy::expl_impl_clone_on_copy "you are implementing `Clone` explicitly on a `Copy` type" +target/lintcheck/sources/libc-0.2.81/src/macros.rs:84:9 clippy::expl_impl_clone_on_copy "you are implementing `Clone` explicitly on a `Copy` type" +target/lintcheck/sources/libc-0.2.81/src/macros.rs:84:9 clippy::expl_impl_clone_on_copy "you are implementing `Clone` explicitly on a `Copy` type" +target/lintcheck/sources/libc-0.2.81/src/macros.rs:84:9 clippy::expl_impl_clone_on_copy "you are implementing `Clone` explicitly on a `Copy` type" +target/lintcheck/sources/libc-0.2.81/src/macros.rs:84:9 clippy::expl_impl_clone_on_copy "you are implementing `Clone` explicitly on a `Copy` type" +target/lintcheck/sources/libc-0.2.81/src/macros.rs:84:9 clippy::expl_impl_clone_on_copy "you are implementing `Clone` explicitly on a `Copy` type" +target/lintcheck/sources/libc-0.2.81/src/macros.rs:84:9 clippy::expl_impl_clone_on_copy "you are implementing `Clone` explicitly on a `Copy` type" +target/lintcheck/sources/libc-0.2.81/src/macros.rs:84:9 clippy::expl_impl_clone_on_copy "you are implementing `Clone` explicitly on a `Copy` type" +target/lintcheck/sources/libc-0.2.81/src/macros.rs:84:9 clippy::expl_impl_clone_on_copy "you are implementing `Clone` explicitly on a `Copy` type" +target/lintcheck/sources/libc-0.2.81/src/macros.rs:84:9 clippy::expl_impl_clone_on_copy "you are implementing `Clone` explicitly on a `Copy` type" +target/lintcheck/sources/libc-0.2.81/src/macros.rs:84:9 clippy::expl_impl_clone_on_copy "you are implementing `Clone` explicitly on a `Copy` type" +target/lintcheck/sources/libc-0.2.81/src/macros.rs:84:9 clippy::expl_impl_clone_on_copy "you are implementing `Clone` explicitly on a `Copy` type" +target/lintcheck/sources/libc-0.2.81/src/macros.rs:84:9 clippy::expl_impl_clone_on_copy "you are implementing `Clone` explicitly on a `Copy` type" +target/lintcheck/sources/libc-0.2.81/src/macros.rs:84:9 clippy::expl_impl_clone_on_copy "you are implementing `Clone` explicitly on a `Copy` type" +target/lintcheck/sources/libc-0.2.81/src/macros.rs:84:9 clippy::expl_impl_clone_on_copy "you are implementing `Clone` explicitly on a `Copy` type" +target/lintcheck/sources/libc-0.2.81/src/macros.rs:84:9 clippy::expl_impl_clone_on_copy "you are implementing `Clone` explicitly on a `Copy` type" +target/lintcheck/sources/libc-0.2.81/src/macros.rs:84:9 clippy::expl_impl_clone_on_copy "you are implementing `Clone` explicitly on a `Copy` type" +target/lintcheck/sources/libc-0.2.81/src/macros.rs:84:9 clippy::expl_impl_clone_on_copy "you are implementing `Clone` explicitly on a `Copy` type" +target/lintcheck/sources/libc-0.2.81/src/macros.rs:84:9 clippy::expl_impl_clone_on_copy "you are implementing `Clone` explicitly on a `Copy` type" +target/lintcheck/sources/libc-0.2.81/src/macros.rs:84:9 clippy::expl_impl_clone_on_copy "you are implementing `Clone` explicitly on a `Copy` type" +target/lintcheck/sources/libc-0.2.81/src/macros.rs:84:9 clippy::expl_impl_clone_on_copy "you are implementing `Clone` explicitly on a `Copy` type" +target/lintcheck/sources/libc-0.2.81/src/macros.rs:84:9 clippy::expl_impl_clone_on_copy "you are implementing `Clone` explicitly on a `Copy` type" +target/lintcheck/sources/libc-0.2.81/src/macros.rs:84:9 clippy::expl_impl_clone_on_copy "you are implementing `Clone` explicitly on a `Copy` type" +target/lintcheck/sources/libc-0.2.81/src/macros.rs:84:9 clippy::expl_impl_clone_on_copy "you are implementing `Clone` explicitly on a `Copy` type" +target/lintcheck/sources/libc-0.2.81/src/macros.rs:84:9 clippy::expl_impl_clone_on_copy "you are implementing `Clone` explicitly on a `Copy` type" +target/lintcheck/sources/libc-0.2.81/src/macros.rs:84:9 clippy::expl_impl_clone_on_copy "you are implementing `Clone` explicitly on a `Copy` type" +target/lintcheck/sources/libc-0.2.81/src/macros.rs:84:9 clippy::expl_impl_clone_on_copy "you are implementing `Clone` explicitly on a `Copy` type" +target/lintcheck/sources/libc-0.2.81/src/macros.rs:84:9 clippy::expl_impl_clone_on_copy "you are implementing `Clone` explicitly on a `Copy` type" +target/lintcheck/sources/libc-0.2.81/src/macros.rs:84:9 clippy::expl_impl_clone_on_copy "you are implementing `Clone` explicitly on a `Copy` type" +target/lintcheck/sources/libc-0.2.81/src/macros.rs:84:9 clippy::expl_impl_clone_on_copy "you are implementing `Clone` explicitly on a `Copy` type" +target/lintcheck/sources/libc-0.2.81/src/macros.rs:84:9 clippy::expl_impl_clone_on_copy "you are implementing `Clone` explicitly on a `Copy` type" +target/lintcheck/sources/libc-0.2.81/src/macros.rs:84:9 clippy::expl_impl_clone_on_copy "you are implementing `Clone` explicitly on a `Copy` type" +target/lintcheck/sources/libc-0.2.81/src/macros.rs:84:9 clippy::expl_impl_clone_on_copy "you are implementing `Clone` explicitly on a `Copy` type" +target/lintcheck/sources/libc-0.2.81/src/macros.rs:84:9 clippy::expl_impl_clone_on_copy "you are implementing `Clone` explicitly on a `Copy` type" +target/lintcheck/sources/libc-0.2.81/src/macros.rs:84:9 clippy::expl_impl_clone_on_copy "you are implementing `Clone` explicitly on a `Copy` type" +target/lintcheck/sources/libc-0.2.81/src/macros.rs:84:9 clippy::expl_impl_clone_on_copy "you are implementing `Clone` explicitly on a `Copy` type" +target/lintcheck/sources/libc-0.2.81/src/macros.rs:84:9 clippy::expl_impl_clone_on_copy "you are implementing `Clone` explicitly on a `Copy` type" +target/lintcheck/sources/libc-0.2.81/src/macros.rs:84:9 clippy::expl_impl_clone_on_copy "you are implementing `Clone` explicitly on a `Copy` type" +target/lintcheck/sources/libc-0.2.81/src/macros.rs:84:9 clippy::expl_impl_clone_on_copy "you are implementing `Clone` explicitly on a `Copy` type" +target/lintcheck/sources/libc-0.2.81/src/macros.rs:84:9 clippy::expl_impl_clone_on_copy "you are implementing `Clone` explicitly on a `Copy` type" +target/lintcheck/sources/libc-0.2.81/src/macros.rs:84:9 clippy::expl_impl_clone_on_copy "you are implementing `Clone` explicitly on a `Copy` type" +target/lintcheck/sources/libc-0.2.81/src/macros.rs:84:9 clippy::expl_impl_clone_on_copy "you are implementing `Clone` explicitly on a `Copy` type" +target/lintcheck/sources/libc-0.2.81/src/macros.rs:84:9 clippy::expl_impl_clone_on_copy "you are implementing `Clone` explicitly on a `Copy` type" +target/lintcheck/sources/libc-0.2.81/src/macros.rs:84:9 clippy::expl_impl_clone_on_copy "you are implementing `Clone` explicitly on a `Copy` type" +target/lintcheck/sources/libc-0.2.81/src/macros.rs:84:9 clippy::expl_impl_clone_on_copy "you are implementing `Clone` explicitly on a `Copy` type" +target/lintcheck/sources/libc-0.2.81/src/macros.rs:84:9 clippy::expl_impl_clone_on_copy "you are implementing `Clone` explicitly on a `Copy` type" +target/lintcheck/sources/libc-0.2.81/src/macros.rs:84:9 clippy::expl_impl_clone_on_copy "you are implementing `Clone` explicitly on a `Copy` type" +target/lintcheck/sources/libc-0.2.81/src/macros.rs:84:9 clippy::expl_impl_clone_on_copy "you are implementing `Clone` explicitly on a `Copy` type" +target/lintcheck/sources/libc-0.2.81/src/macros.rs:84:9 clippy::expl_impl_clone_on_copy "you are implementing `Clone` explicitly on a `Copy` type" +target/lintcheck/sources/libc-0.2.81/src/macros.rs:84:9 clippy::expl_impl_clone_on_copy "you are implementing `Clone` explicitly on a `Copy` type" +target/lintcheck/sources/libc-0.2.81/src/macros.rs:84:9 clippy::expl_impl_clone_on_copy "you are implementing `Clone` explicitly on a `Copy` type" +target/lintcheck/sources/libc-0.2.81/src/macros.rs:84:9 clippy::expl_impl_clone_on_copy "you are implementing `Clone` explicitly on a `Copy` type" +target/lintcheck/sources/libc-0.2.81/src/macros.rs:84:9 clippy::expl_impl_clone_on_copy "you are implementing `Clone` explicitly on a `Copy` type" +target/lintcheck/sources/libc-0.2.81/src/macros.rs:84:9 clippy::expl_impl_clone_on_copy "you are implementing `Clone` explicitly on a `Copy` type" +target/lintcheck/sources/libc-0.2.81/src/macros.rs:84:9 clippy::expl_impl_clone_on_copy "you are implementing `Clone` explicitly on a `Copy` type" +target/lintcheck/sources/libc-0.2.81/src/macros.rs:84:9 clippy::expl_impl_clone_on_copy "you are implementing `Clone` explicitly on a `Copy` type" +target/lintcheck/sources/libc-0.2.81/src/macros.rs:84:9 clippy::expl_impl_clone_on_copy "you are implementing `Clone` explicitly on a `Copy` type" +target/lintcheck/sources/libc-0.2.81/src/macros.rs:84:9 clippy::expl_impl_clone_on_copy "you are implementing `Clone` explicitly on a `Copy` type" +target/lintcheck/sources/libc-0.2.81/src/macros.rs:84:9 clippy::expl_impl_clone_on_copy "you are implementing `Clone` explicitly on a `Copy` type" +target/lintcheck/sources/libc-0.2.81/src/macros.rs:84:9 clippy::expl_impl_clone_on_copy "you are implementing `Clone` explicitly on a `Copy` type" +target/lintcheck/sources/libc-0.2.81/src/macros.rs:84:9 clippy::expl_impl_clone_on_copy "you are implementing `Clone` explicitly on a `Copy` type" +target/lintcheck/sources/libc-0.2.81/src/macros.rs:84:9 clippy::expl_impl_clone_on_copy "you are implementing `Clone` explicitly on a `Copy` type" +target/lintcheck/sources/libc-0.2.81/src/macros.rs:84:9 clippy::expl_impl_clone_on_copy "you are implementing `Clone` explicitly on a `Copy` type" +target/lintcheck/sources/libc-0.2.81/src/macros.rs:84:9 clippy::expl_impl_clone_on_copy "you are implementing `Clone` explicitly on a `Copy` type" +target/lintcheck/sources/libc-0.2.81/src/macros.rs:84:9 clippy::expl_impl_clone_on_copy "you are implementing `Clone` explicitly on a `Copy` type" +target/lintcheck/sources/libc-0.2.81/src/macros.rs:84:9 clippy::expl_impl_clone_on_copy "you are implementing `Clone` explicitly on a `Copy` type" +target/lintcheck/sources/libc-0.2.81/src/macros.rs:84:9 clippy::expl_impl_clone_on_copy "you are implementing `Clone` explicitly on a `Copy` type" +target/lintcheck/sources/libc-0.2.81/src/macros.rs:84:9 clippy::expl_impl_clone_on_copy "you are implementing `Clone` explicitly on a `Copy` type" +target/lintcheck/sources/libc-0.2.81/src/macros.rs:84:9 clippy::expl_impl_clone_on_copy "you are implementing `Clone` explicitly on a `Copy` type" +target/lintcheck/sources/libc-0.2.81/src/macros.rs:84:9 clippy::expl_impl_clone_on_copy "you are implementing `Clone` explicitly on a `Copy` type" +target/lintcheck/sources/libc-0.2.81/src/macros.rs:84:9 clippy::expl_impl_clone_on_copy "you are implementing `Clone` explicitly on a `Copy` type" +target/lintcheck/sources/libc-0.2.81/src/macros.rs:84:9 clippy::expl_impl_clone_on_copy "you are implementing `Clone` explicitly on a `Copy` type" +target/lintcheck/sources/libc-0.2.81/src/macros.rs:84:9 clippy::expl_impl_clone_on_copy "you are implementing `Clone` explicitly on a `Copy` type" +target/lintcheck/sources/libc-0.2.81/src/macros.rs:84:9 clippy::expl_impl_clone_on_copy "you are implementing `Clone` explicitly on a `Copy` type" +target/lintcheck/sources/libc-0.2.81/src/macros.rs:84:9 clippy::expl_impl_clone_on_copy "you are implementing `Clone` explicitly on a `Copy` type" +target/lintcheck/sources/libc-0.2.81/src/macros.rs:84:9 clippy::expl_impl_clone_on_copy "you are implementing `Clone` explicitly on a `Copy` type" +target/lintcheck/sources/libc-0.2.81/src/macros.rs:84:9 clippy::expl_impl_clone_on_copy "you are implementing `Clone` explicitly on a `Copy` type" +target/lintcheck/sources/libc-0.2.81/src/macros.rs:84:9 clippy::expl_impl_clone_on_copy "you are implementing `Clone` explicitly on a `Copy` type" +target/lintcheck/sources/libc-0.2.81/src/macros.rs:84:9 clippy::expl_impl_clone_on_copy "you are implementing `Clone` explicitly on a `Copy` type" +target/lintcheck/sources/libc-0.2.81/src/macros.rs:84:9 clippy::expl_impl_clone_on_copy "you are implementing `Clone` explicitly on a `Copy` type" +target/lintcheck/sources/libc-0.2.81/src/macros.rs:84:9 clippy::expl_impl_clone_on_copy "you are implementing `Clone` explicitly on a `Copy` type" +target/lintcheck/sources/libc-0.2.81/src/macros.rs:84:9 clippy::expl_impl_clone_on_copy "you are implementing `Clone` explicitly on a `Copy` type" +target/lintcheck/sources/libc-0.2.81/src/macros.rs:84:9 clippy::expl_impl_clone_on_copy "you are implementing `Clone` explicitly on a `Copy` type" +target/lintcheck/sources/libc-0.2.81/src/macros.rs:84:9 clippy::expl_impl_clone_on_copy "you are implementing `Clone` explicitly on a `Copy` type" +target/lintcheck/sources/libc-0.2.81/src/macros.rs:84:9 clippy::expl_impl_clone_on_copy "you are implementing `Clone` explicitly on a `Copy` type" +target/lintcheck/sources/libc-0.2.81/src/macros.rs:84:9 clippy::expl_impl_clone_on_copy "you are implementing `Clone` explicitly on a `Copy` type" +target/lintcheck/sources/libc-0.2.81/src/macros.rs:84:9 clippy::expl_impl_clone_on_copy "you are implementing `Clone` explicitly on a `Copy` type" +target/lintcheck/sources/libc-0.2.81/src/macros.rs:84:9 clippy::expl_impl_clone_on_copy "you are implementing `Clone` explicitly on a `Copy` type" +target/lintcheck/sources/libc-0.2.81/src/macros.rs:84:9 clippy::expl_impl_clone_on_copy "you are implementing `Clone` explicitly on a `Copy` type" +target/lintcheck/sources/libc-0.2.81/src/macros.rs:84:9 clippy::expl_impl_clone_on_copy "you are implementing `Clone` explicitly on a `Copy` type" +target/lintcheck/sources/libc-0.2.81/src/macros.rs:84:9 clippy::expl_impl_clone_on_copy "you are implementing `Clone` explicitly on a `Copy` type" +target/lintcheck/sources/libc-0.2.81/src/macros.rs:84:9 clippy::expl_impl_clone_on_copy "you are implementing `Clone` explicitly on a `Copy` type" +target/lintcheck/sources/libc-0.2.81/src/macros.rs:84:9 clippy::expl_impl_clone_on_copy "you are implementing `Clone` explicitly on a `Copy` type" +target/lintcheck/sources/libc-0.2.81/src/macros.rs:84:9 clippy::expl_impl_clone_on_copy "you are implementing `Clone` explicitly on a `Copy` type" +target/lintcheck/sources/libc-0.2.81/src/macros.rs:84:9 clippy::expl_impl_clone_on_copy "you are implementing `Clone` explicitly on a `Copy` type" +target/lintcheck/sources/libc-0.2.81/src/macros.rs:84:9 clippy::expl_impl_clone_on_copy "you are implementing `Clone` explicitly on a `Copy` type" +target/lintcheck/sources/libc-0.2.81/src/macros.rs:84:9 clippy::expl_impl_clone_on_copy "you are implementing `Clone` explicitly on a `Copy` type" +target/lintcheck/sources/libc-0.2.81/src/macros.rs:84:9 clippy::expl_impl_clone_on_copy "you are implementing `Clone` explicitly on a `Copy` type" +target/lintcheck/sources/libc-0.2.81/src/macros.rs:84:9 clippy::expl_impl_clone_on_copy "you are implementing `Clone` explicitly on a `Copy` type" +target/lintcheck/sources/libc-0.2.81/src/macros.rs:84:9 clippy::expl_impl_clone_on_copy "you are implementing `Clone` explicitly on a `Copy` type" +target/lintcheck/sources/libc-0.2.81/src/macros.rs:84:9 clippy::expl_impl_clone_on_copy "you are implementing `Clone` explicitly on a `Copy` type" +target/lintcheck/sources/libc-0.2.81/src/macros.rs:84:9 clippy::expl_impl_clone_on_copy "you are implementing `Clone` explicitly on a `Copy` type" +target/lintcheck/sources/libc-0.2.81/src/macros.rs:84:9 clippy::expl_impl_clone_on_copy "you are implementing `Clone` explicitly on a `Copy` type" +target/lintcheck/sources/libc-0.2.81/src/macros.rs:84:9 clippy::expl_impl_clone_on_copy "you are implementing `Clone` explicitly on a `Copy` type" +target/lintcheck/sources/libc-0.2.81/src/macros.rs:84:9 clippy::expl_impl_clone_on_copy "you are implementing `Clone` explicitly on a `Copy` type" +target/lintcheck/sources/libc-0.2.81/src/macros.rs:84:9 clippy::expl_impl_clone_on_copy "you are implementing `Clone` explicitly on a `Copy` type" +target/lintcheck/sources/libc-0.2.81/src/macros.rs:84:9 clippy::expl_impl_clone_on_copy "you are implementing `Clone` explicitly on a `Copy` type" +target/lintcheck/sources/libc-0.2.81/src/macros.rs:84:9 clippy::expl_impl_clone_on_copy "you are implementing `Clone` explicitly on a `Copy` type" +target/lintcheck/sources/libc-0.2.81/src/macros.rs:84:9 clippy::expl_impl_clone_on_copy "you are implementing `Clone` explicitly on a `Copy` type" +target/lintcheck/sources/libc-0.2.81/src/macros.rs:84:9 clippy::expl_impl_clone_on_copy "you are implementing `Clone` explicitly on a `Copy` type" +target/lintcheck/sources/libc-0.2.81/src/macros.rs:84:9 clippy::expl_impl_clone_on_copy "you are implementing `Clone` explicitly on a `Copy` type" +target/lintcheck/sources/libc-0.2.81/src/macros.rs:84:9 clippy::expl_impl_clone_on_copy "you are implementing `Clone` explicitly on a `Copy` type" +target/lintcheck/sources/libc-0.2.81/src/macros.rs:84:9 clippy::expl_impl_clone_on_copy "you are implementing `Clone` explicitly on a `Copy` type" +target/lintcheck/sources/libc-0.2.81/src/macros.rs:84:9 clippy::expl_impl_clone_on_copy "you are implementing `Clone` explicitly on a `Copy` type" +target/lintcheck/sources/libc-0.2.81/src/macros.rs:84:9 clippy::expl_impl_clone_on_copy "you are implementing `Clone` explicitly on a `Copy` type" +target/lintcheck/sources/libc-0.2.81/src/macros.rs:84:9 clippy::expl_impl_clone_on_copy "you are implementing `Clone` explicitly on a `Copy` type" +target/lintcheck/sources/libc-0.2.81/src/macros.rs:84:9 clippy::expl_impl_clone_on_copy "you are implementing `Clone` explicitly on a `Copy` type" +target/lintcheck/sources/libc-0.2.81/src/macros.rs:84:9 clippy::expl_impl_clone_on_copy "you are implementing `Clone` explicitly on a `Copy` type" +target/lintcheck/sources/libc-0.2.81/src/macros.rs:84:9 clippy::expl_impl_clone_on_copy "you are implementing `Clone` explicitly on a `Copy` type" +target/lintcheck/sources/libc-0.2.81/src/macros.rs:84:9 clippy::expl_impl_clone_on_copy "you are implementing `Clone` explicitly on a `Copy` type" +target/lintcheck/sources/libc-0.2.81/src/macros.rs:84:9 clippy::expl_impl_clone_on_copy "you are implementing `Clone` explicitly on a `Copy` type" +target/lintcheck/sources/libc-0.2.81/src/macros.rs:84:9 clippy::expl_impl_clone_on_copy "you are implementing `Clone` explicitly on a `Copy` type" +target/lintcheck/sources/libc-0.2.81/src/macros.rs:84:9 clippy::expl_impl_clone_on_copy "you are implementing `Clone` explicitly on a `Copy` type" +target/lintcheck/sources/libc-0.2.81/src/macros.rs:84:9 clippy::expl_impl_clone_on_copy "you are implementing `Clone` explicitly on a `Copy` type" +target/lintcheck/sources/libc-0.2.81/src/macros.rs:84:9 clippy::expl_impl_clone_on_copy "you are implementing `Clone` explicitly on a `Copy` type" +target/lintcheck/sources/libc-0.2.81/src/macros.rs:84:9 clippy::expl_impl_clone_on_copy "you are implementing `Clone` explicitly on a `Copy` type" +target/lintcheck/sources/libc-0.2.81/src/macros.rs:84:9 clippy::expl_impl_clone_on_copy "you are implementing `Clone` explicitly on a `Copy` type" +target/lintcheck/sources/libc-0.2.81/src/macros.rs:84:9 clippy::expl_impl_clone_on_copy "you are implementing `Clone` explicitly on a `Copy` type" +target/lintcheck/sources/libc-0.2.81/src/macros.rs:84:9 clippy::expl_impl_clone_on_copy "you are implementing `Clone` explicitly on a `Copy` type" +target/lintcheck/sources/libc-0.2.81/src/macros.rs:84:9 clippy::expl_impl_clone_on_copy "you are implementing `Clone` explicitly on a `Copy` type" +target/lintcheck/sources/libc-0.2.81/src/macros.rs:84:9 clippy::expl_impl_clone_on_copy "you are implementing `Clone` explicitly on a `Copy` type" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/linux/gnu/b64/x86_64/mod.rs:428:29 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/linux/gnu/b64/x86_64/mod.rs:429:30 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/linux/gnu/b64/x86_64/mod.rs:431:30 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/linux/gnu/b64/x86_64/mod.rs:432:32 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/linux/gnu/b64/x86_64/mod.rs:433:29 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/linux/gnu/b64/x86_64/mod.rs:434:32 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/linux/gnu/b64/x86_64/mod.rs:595:33 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/linux/gnu/b64/x86_64/mod.rs:596:33 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/linux/gnu/b64/x86_64/mod.rs:597:35 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/linux/gnu/b64/x86_64/mod.rs:622:32 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/linux/gnu/b64/x86_64/mod.rs:673:34 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/linux/gnu/b64/x86_64/mod.rs:696:33 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/linux/gnu/b64/x86_64/mod.rs:697:35 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/linux/gnu/b64/x86_64/mod.rs:698:33 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/linux/gnu/b64/x86_64/mod.rs:699:35 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/linux/gnu/b64/x86_64/mod.rs:712:34 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/linux/gnu/b64/x86_64/mod.rs:721:35 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/linux/gnu/b64/x86_64/mod.rs:722:32 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/linux/gnu/b64/x86_64/mod.rs:723:32 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/linux/gnu/b64/x86_64/mod.rs:751:31 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/linux/gnu/b64/x86_64/mod.rs:752:30 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/linux/gnu/b64/x86_64/mod.rs:753:30 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/linux/gnu/b64/x86_64/mod.rs:754:30 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/linux/gnu/b64/x86_64/mod.rs:755:29 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/linux/gnu/b64/x86_64/mod.rs:756:29 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/linux/gnu/b64/x86_64/mod.rs:757:29 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/linux/gnu/b64/x86_64/mod.rs:758:29 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/linux/gnu/b64/x86_64/mod.rs:759:29 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/linux/gnu/b64/x86_64/mod.rs:760:29 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/linux/gnu/b64/x86_64/mod.rs:768:30 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/linux/gnu/b64/x86_64/mod.rs:769:31 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/linux/gnu/b64/x86_64/mod.rs:771:31 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/linux/gnu/b64/x86_64/mod.rs:772:29 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/linux/gnu/b64/x86_64/mod.rs:773:29 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/linux/gnu/b64/x86_64/mod.rs:774:29 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/linux/gnu/b64/x86_64/mod.rs:775:32 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/linux/gnu/b64/x86_64/mod.rs:776:31 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/linux/gnu/b64/x86_64/mod.rs:777:32 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/linux/gnu/b64/x86_64/mod.rs:778:32 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/linux/gnu/b64/x86_64/mod.rs:779:31 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/linux/gnu/b64/x86_64/mod.rs:780:32 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/linux/gnu/b64/x86_64/mod.rs:781:32 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/linux/gnu/b64/x86_64/mod.rs:782:31 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/linux/gnu/b64/x86_64/mod.rs:783:31 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/linux/gnu/b64/x86_64/mod.rs:784:32 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/linux/gnu/b64/x86_64/mod.rs:785:33 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/linux/gnu/b64/x86_64/mod.rs:786:33 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/linux/gnu/b64/x86_64/mod.rs:787:30 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/linux/gnu/b64/x86_64/mod.rs:788:32 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/linux/gnu/b64/x86_64/mod.rs:789:32 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/linux/gnu/b64/x86_64/mod.rs:790:32 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/linux/gnu/b64/x86_64/mod.rs:791:32 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/linux/gnu/b64/x86_64/mod.rs:792:33 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/linux/gnu/b64/x86_64/mod.rs:794:31 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/linux/gnu/b64/x86_64/mod.rs:795:31 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/linux/gnu/b64/x86_64/mod.rs:796:31 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/linux/gnu/b64/x86_64/mod.rs:797:32 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/linux/gnu/b64/x86_64/mod.rs:798:31 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/linux/gnu/b64/x86_64/mod.rs:799:31 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/linux/gnu/b64/x86_64/mod.rs:800:31 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/linux/gnu/b64/x86_64/mod.rs:801:31 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/linux/gnu/b64/x86_64/mod.rs:803:27 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/linux/gnu/b64/x86_64/mod.rs:804:28 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/linux/gnu/b64/x86_64/mod.rs:805:28 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/linux/gnu/b64/x86_64/mod.rs:806:29 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/linux/gnu/b64/x86_64/mod.rs:807:29 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/linux/gnu/b64/x86_64/mod.rs:808:29 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/linux/gnu/b64/x86_64/mod.rs:809:29 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/linux/gnu/b64/x86_64/mod.rs:810:29 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/linux/gnu/b64/x86_64/mod.rs:811:29 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/linux/gnu/b64/x86_64/mod.rs:812:30 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/linux/gnu/b64/x86_64/mod.rs:813:30 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/linux/gnu/b64/x86_64/mod.rs:814:30 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/linux/gnu/b64/x86_64/mod.rs:815:30 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/linux/gnu/b64/x86_64/mod.rs:816:30 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/linux/gnu/b64/x86_64/mod.rs:817:31 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/linux/gnu/b64/x86_64/mod.rs:818:31 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/linux/gnu/b64/x86_64/mod.rs:821:31 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/linux/gnu/b64/x86_64/mod.rs:822:31 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/linux/gnu/b64/x86_64/mod.rs:823:32 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/linux/gnu/b64/x86_64/mod.rs:824:32 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/linux/gnu/b64/x86_64/mod.rs:825:32 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/linux/gnu/b64/x86_64/mod.rs:826:32 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/linux/gnu/b64/x86_64/mod.rs:827:32 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/linux/gnu/b64/x86_64/mod.rs:828:32 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/linux/gnu/b64/x86_64/mod.rs:829:33 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/linux/gnu/b64/x86_64/mod.rs:830:33 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/linux/gnu/b64/x86_64/mod.rs:831:33 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/linux/gnu/b64/x86_64/mod.rs:832:33 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/linux/gnu/b64/x86_64/mod.rs:833:33 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/linux/gnu/b64/x86_64/mod.rs:834:33 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/linux/gnu/b64/x86_64/mod.rs:835:33 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/linux/gnu/b64/x86_64/mod.rs:836:33 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/linux/gnu/b64/x86_64/mod.rs:841:32 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/linux/gnu/b64/x86_64/mod.rs:842:32 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/linux/gnu/b64/x86_64/mod.rs:843:32 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/linux/gnu/b64/x86_64/mod.rs:844:33 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/linux/gnu/mod.rs:1120:38 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/linux/gnu/mod.rs:178:34 clippy::unseparated_literal_suffix "integer type suffix should be separated by an underscore" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/linux/gnu/mod.rs:291:5 clippy::missing_safety_doc "unsafe function's docs miss `# Safety` section" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/linux/gnu/mod.rs:291:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/linux/gnu/mod.rs:299:11 clippy::ptr_as_ptr "`as` casting between raw pointers without changing its mutability" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/linux/gnu/mod.rs:302:5 clippy::missing_safety_doc "unsafe function's docs miss `# Safety` section" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/linux/gnu/mod.rs:302:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/linux/gnu/mod.rs:312:11 clippy::ptr_as_ptr "`as` casting between raw pointers without changing its mutability" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/linux/gnu/mod.rs:328:9 clippy::expl_impl_clone_on_copy "you are implementing `Clone` explicitly on a `Copy` type" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/linux/gnu/mod.rs:352:20 clippy::ptr_as_ptr "`as` casting between raw pointers without changing its mutability" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/linux/gnu/mod.rs:355:13 clippy::missing_safety_doc "unsafe function's docs miss `# Safety` section" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/linux/gnu/mod.rs:355:13 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/linux/gnu/mod.rs:359:13 clippy::missing_safety_doc "unsafe function's docs miss `# Safety` section" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/linux/gnu/mod.rs:359:13 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/linux/gnu/mod.rs:363:13 clippy::missing_safety_doc "unsafe function's docs miss `# Safety` section" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/linux/gnu/mod.rs:363:13 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/linux/gnu/mod.rs:367:13 clippy::missing_safety_doc "unsafe function's docs miss `# Safety` section" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/linux/gnu/mod.rs:367:13 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/linux/gnu/mod.rs:371:13 clippy::missing_safety_doc "unsafe function's docs miss `# Safety` section" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/linux/gnu/mod.rs:371:13 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/linux/gnu/mod.rs:534:36 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/linux/gnu/mod.rs:645:31 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/linux/gnu/mod.rs:727:40 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/linux/gnu/mod.rs:728:40 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/linux/gnu/mod.rs:729:39 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/linux/gnu/mod.rs:731:44 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/linux/gnu/mod.rs:732:36 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/linux/gnu/mod.rs:733:41 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/linux/gnu/mod.rs:734:43 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/linux/gnu/mod.rs:735:42 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/linux/gnu/mod.rs:736:40 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/linux/gnu/mod.rs:737:36 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/linux/gnu/mod.rs:738:37 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/linux/gnu/mod.rs:741:39 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/linux/gnu/mod.rs:742:40 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/linux/gnu/mod.rs:743:40 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/linux/gnu/mod.rs:744:40 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/linux/gnu/mod.rs:745:40 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/linux/gnu/mod.rs:746:43 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/linux/gnu/mod.rs:747:42 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/linux/gnu/mod.rs:748:40 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/linux/gnu/mod.rs:749:39 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/linux/gnu/mod.rs:750:41 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/linux/gnu/mod.rs:751:41 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/linux/gnu/mod.rs:752:43 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/linux/gnu/mod.rs:753:42 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/linux/gnu/mod.rs:755:42 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/linux/gnu/mod.rs:756:41 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/linux/gnu/mod.rs:757:41 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/linux/gnu/mod.rs:758:39 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/linux/gnu/mod.rs:759:39 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/linux/gnu/mod.rs:761:41 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/linux/gnu/mod.rs:762:44 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/linux/gnu/mod.rs:763:45 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/linux/gnu/mod.rs:764:40 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/linux/gnu/mod.rs:765:40 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/linux/gnu/mod.rs:766:40 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/linux/gnu/mod.rs:767:44 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/linux/gnu/mod.rs:768:44 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/linux/gnu/mod.rs:769:39 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/linux/gnu/mod.rs:770:35 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/linux/gnu/mod.rs:771:35 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/linux/gnu/mod.rs:772:37 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/linux/gnu/mod.rs:773:39 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/linux/gnu/mod.rs:774:45 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/linux/gnu/mod.rs:775:41 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/linux/gnu/mod.rs:776:39 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/linux/gnu/mod.rs:803:34 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/linux/gnu/mod.rs:841:30 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/linux/gnu/mod.rs:842:37 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/linux/gnu/mod.rs:982:40 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/linux/gnu/mod.rs:984:46 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/linux/mod.rs:1209:36 clippy::cast_possible_truncation "casting `i32` to `i16` may truncate the value" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/linux/mod.rs:1210:36 clippy::cast_possible_truncation "casting `i32` to `i16` may truncate the value" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/linux/mod.rs:1235:39 clippy::unseparated_literal_suffix "integer type suffix should be separated by an underscore" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/linux/mod.rs:1236:41 clippy::unseparated_literal_suffix "integer type suffix should be separated by an underscore" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/linux/mod.rs:1274:42 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/linux/mod.rs:1324:31 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/linux/mod.rs:1333:37 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/linux/mod.rs:1334:35 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/linux/mod.rs:1346:34 clippy::cast_possible_wrap "casting `u32` to `i32` may wrap around the value" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/linux/mod.rs:1346:34 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/linux/mod.rs:1346:34 clippy::unseparated_literal_suffix "integer type suffix should be separated by an underscore" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/linux/mod.rs:1347:37 clippy::cast_possible_wrap "casting `u32` to `i32` may wrap around the value" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/linux/mod.rs:1347:37 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/linux/mod.rs:1347:37 clippy::unseparated_literal_suffix "integer type suffix should be separated by an underscore" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/linux/mod.rs:1348:36 clippy::cast_possible_wrap "casting `u32` to `i32` may wrap around the value" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/linux/mod.rs:1348:36 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/linux/mod.rs:1348:36 clippy::unseparated_literal_suffix "integer type suffix should be separated by an underscore" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/linux/mod.rs:1349:37 clippy::cast_possible_wrap "casting `u32` to `i32` may wrap around the value" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/linux/mod.rs:1349:37 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/linux/mod.rs:1349:37 clippy::unseparated_literal_suffix "integer type suffix should be separated by an underscore" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/linux/mod.rs:1350:35 clippy::cast_possible_wrap "casting `u32` to `i32` may wrap around the value" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/linux/mod.rs:1350:35 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/linux/mod.rs:1350:35 clippy::unseparated_literal_suffix "integer type suffix should be separated by an underscore" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/linux/mod.rs:1351:36 clippy::cast_possible_wrap "casting `u32` to `i32` may wrap around the value" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/linux/mod.rs:1351:36 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/linux/mod.rs:1351:36 clippy::unseparated_literal_suffix "integer type suffix should be separated by an underscore" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/linux/mod.rs:1352:31 clippy::cast_possible_wrap "casting `u32` to `i32` may wrap around the value" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/linux/mod.rs:1352:31 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/linux/mod.rs:1352:31 clippy::unseparated_literal_suffix "integer type suffix should be separated by an underscore" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/linux/mod.rs:1419:36 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/linux/mod.rs:1420:36 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/linux/mod.rs:1421:36 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/linux/mod.rs:1422:36 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/linux/mod.rs:1423:36 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/linux/mod.rs:1490:37 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/linux/mod.rs:1561:46 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/linux/mod.rs:1562:45 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/linux/mod.rs:1567:31 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/linux/mod.rs:1568:32 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/linux/mod.rs:1586:26 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/linux/mod.rs:1587:34 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/linux/mod.rs:1588:31 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/linux/mod.rs:1589:31 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/linux/mod.rs:1897:38 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/linux/mod.rs:1898:51 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/linux/mod.rs:1900:39 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/linux/mod.rs:1969:34 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/linux/mod.rs:1970:34 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/linux/mod.rs:1971:36 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/linux/mod.rs:1972:36 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/linux/mod.rs:1973:36 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/linux/mod.rs:1974:37 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/linux/mod.rs:1975:37 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/linux/mod.rs:1976:36 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/linux/mod.rs:1977:36 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/linux/mod.rs:1978:39 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/linux/mod.rs:1979:39 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/linux/mod.rs:1980:39 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/linux/mod.rs:1981:39 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/linux/mod.rs:1982:39 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/linux/mod.rs:1983:39 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/linux/mod.rs:1984:38 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/linux/mod.rs:1985:38 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/linux/mod.rs:1986:35 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/linux/mod.rs:1987:35 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/linux/mod.rs:1988:35 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/linux/mod.rs:1989:35 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/linux/mod.rs:1990:38 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/linux/mod.rs:1991:37 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/linux/mod.rs:1992:37 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/linux/mod.rs:1993:38 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/linux/mod.rs:1994:37 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/linux/mod.rs:1995:37 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/linux/mod.rs:1996:37 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/linux/mod.rs:1997:37 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/linux/mod.rs:1998:33 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/linux/mod.rs:1999:33 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/linux/mod.rs:2000:33 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/linux/mod.rs:2001:34 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/linux/mod.rs:2002:34 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/linux/mod.rs:2003:34 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/linux/mod.rs:2004:35 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/linux/mod.rs:2005:35 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/linux/mod.rs:2032:30 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/linux/mod.rs:2033:32 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/linux/mod.rs:2034:31 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/linux/mod.rs:2035:29 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/linux/mod.rs:2036:32 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/linux/mod.rs:2037:28 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/linux/mod.rs:2038:27 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/linux/mod.rs:2039:29 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/linux/mod.rs:2041:29 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/linux/mod.rs:2042:28 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/linux/mod.rs:2043:27 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/linux/mod.rs:2044:34 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/linux/mod.rs:2045:27 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/linux/mod.rs:2046:33 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/linux/mod.rs:2048:28 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/linux/mod.rs:2049:32 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/linux/mod.rs:2050:32 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/linux/mod.rs:2051:32 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/linux/mod.rs:2052:26 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/linux/mod.rs:2053:36 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/linux/mod.rs:2318:42 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/linux/mod.rs:2321:38 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/linux/mod.rs:2331:39 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/linux/mod.rs:2487:42 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/linux/mod.rs:2488:42 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/linux/mod.rs:2489:43 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/linux/mod.rs:2490:43 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/linux/mod.rs:2491:43 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/linux/mod.rs:2493:47 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/linux/mod.rs:2494:44 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/linux/mod.rs:2495:46 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/linux/mod.rs:2496:47 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/linux/mod.rs:2497:49 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/linux/mod.rs:2498:48 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/linux/mod.rs:2499:50 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/linux/mod.rs:2500:45 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/linux/mod.rs:2572:9 clippy::needless_return "unneeded `return` statement" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/linux/mod.rs:2578:20 clippy::zero_ptr "`0 as *mut _` detected" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/linux/mod.rs:2588:13 clippy::zero_ptr "`0 as *mut _` detected" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/linux/mod.rs:2590:13 clippy::ptr_as_ptr "`as` casting between raw pointers without changing its mutability" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/linux/mod.rs:2596:52 clippy::used_underscore_binding "used binding `_dummy` which is prefixed with an underscore. A leading underscore signals that a binding will not be used" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/linux/mod.rs:2597:11 clippy::cast_sign_loss "casting `i32` to `usize` may lose the sign of the value" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/linux/mod.rs:2601:21 clippy::explicit_iter_loop "it is more concise to loop over references to containers instead of using explicit iteration methods" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/linux/mod.rs:2611:9 clippy::unused_unit "unneeded unit expression" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/linux/mod.rs:2619:9 clippy::unused_unit "unneeded unit expression" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/linux/mod.rs:2634:9 clippy::cast_possible_wrap "casting `u32` to `i32` may wrap around the value" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/linux/mod.rs:2647:25 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/linux/mod.rs:2648:25 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/linux/mod.rs:2649:9 clippy::cast_possible_truncation "casting `u64` to `u32` may truncate the value" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/linux/mod.rs:2654:18 clippy::identity_op "the operation is ineffective. Consider reducing it to `(dev & 0x00000000000000ff)`" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/linux/mod.rs:2654:25 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/linux/mod.rs:2655:25 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/linux/mod.rs:2656:9 clippy::cast_possible_truncation "casting `u64` to `u32` may truncate the value" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/linux/mod.rs:2660:21 clippy::cast_lossless "casting `u32` to `u64` may become silently lossy if you later change the type" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/linux/mod.rs:2661:21 clippy::cast_lossless "casting `u32` to `u64` may become silently lossy if you later change the type" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/linux/mod.rs:2663:25 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/linux/mod.rs:2664:25 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/linux/mod.rs:2665:16 clippy::identity_op "the operation is ineffective. Consider reducing it to `(minor & 0x000000ff)`" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/linux/mod.rs:2665:25 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/linux/mod.rs:2666:25 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/linux/mod.rs:42:1 clippy::expl_impl_clone_on_copy "you are implementing `Clone` explicitly on a `Copy` type" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/linux/mod.rs:954:34 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/mod.rs:1000:31 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/mod.rs:1001:32 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/mod.rs:1002:32 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/mod.rs:1016:29 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/mod.rs:1017:32 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/mod.rs:1018:33 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/mod.rs:1019:33 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/mod.rs:1020:33 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/mod.rs:1029:29 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/mod.rs:1030:29 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/mod.rs:1031:30 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/mod.rs:1032:29 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/mod.rs:1033:29 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/mod.rs:1034:29 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/mod.rs:1035:29 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/mod.rs:1041:32 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/mod.rs:1042:32 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/mod.rs:1043:32 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/mod.rs:1044:32 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/mod.rs:1045:31 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/mod.rs:1046:32 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/mod.rs:1047:31 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/mod.rs:1048:31 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/mod.rs:1049:31 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/mod.rs:1050:31 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/mod.rs:1051:33 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/mod.rs:1053:29 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/mod.rs:1054:33 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/mod.rs:1055:30 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/mod.rs:1056:31 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/mod.rs:1057:31 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/mod.rs:1058:32 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/mod.rs:1059:31 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/mod.rs:1060:31 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/mod.rs:1073:42 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/mod.rs:1074:43 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/mod.rs:1075:37 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/mod.rs:1076:37 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/mod.rs:1077:41 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/mod.rs:1078:35 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/mod.rs:1079:35 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/mod.rs:1080:36 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/mod.rs:1081:35 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/mod.rs:1082:35 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/mod.rs:1083:31 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/mod.rs:1084:38 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/mod.rs:1086:30 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/mod.rs:1087:32 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/mod.rs:1089:30 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/mod.rs:1090:33 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/mod.rs:1091:30 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/mod.rs:1094:40 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/mod.rs:1095:37 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/mod.rs:1096:41 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/mod.rs:1097:40 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/mod.rs:1098:39 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/mod.rs:1099:34 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/mod.rs:1100:36 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/mod.rs:1101:38 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/mod.rs:1102:37 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/mod.rs:1105:44 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/mod.rs:1106:41 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/mod.rs:1107:42 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/mod.rs:1108:42 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/mod.rs:1109:41 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/mod.rs:1110:46 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/mod.rs:1111:41 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/mod.rs:1112:44 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/mod.rs:1113:40 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/mod.rs:1114:47 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/mod.rs:1115:36 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/mod.rs:1126:34 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/mod.rs:1127:29 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/mod.rs:1128:31 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/mod.rs:1179:32 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/mod.rs:1180:31 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/mod.rs:11:1 clippy::expl_impl_clone_on_copy "you are implementing `Clone` explicitly on a `Copy` type" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/mod.rs:1218:27 clippy::identity_op "the operation is ineffective. Consider reducing it to `IPOPT_CONTROL`" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/mod.rs:1314:9 clippy::precedence "operator precedence can trip the unwary" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/mod.rs:1321:13 clippy::ptr_as_ptr "`as` casting between raw pointers without changing its mutability" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/mod.rs:1323:13 clippy::zero_ptr "`0 as *mut _` detected" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/mod.rs:1332:9 clippy::cast_possible_truncation "casting `usize` to `u32` may truncate the value on targets with 64-bit wide pointers" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/mod.rs:1337:9 clippy::cast_possible_truncation "casting `usize` to `u32` may truncate the value on targets with 64-bit wide pointers" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/mod.rs:1341:18 clippy::cast_sign_loss "casting `i32` to `usize` may lose the sign of the value" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/mod.rs:1344:9 clippy::needless_return "unneeded `return` statement" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/mod.rs:1348:18 clippy::cast_sign_loss "casting `i32` to `usize` may lose the sign of the value" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/mod.rs:1350:9 clippy::needless_return "unneeded `return` statement" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/mod.rs:1354:18 clippy::cast_sign_loss "casting `i32` to `usize` may lose the sign of the value" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/mod.rs:1357:9 clippy::needless_return "unneeded `return` statement" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/mod.rs:1361:21 clippy::explicit_iter_loop "it is more concise to loop over references to containers instead of using explicit iteration methods" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/mod.rs:1381:9 clippy::cast_possible_truncation "casting `i32` to `i8` may truncate the value" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/mod.rs:1389:9 clippy::verbose_bit_mask "bit mask could be simplified with a call to `trailing_zeros`" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/mod.rs:446:31 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/mod.rs:591:36 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/mod.rs:592:38 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/mod.rs:593:35 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/mod.rs:594:33 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/mod.rs:595:34 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/mod.rs:596:36 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/mod.rs:597:37 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/mod.rs:598:37 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/mod.rs:599:39 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/mod.rs:600:34 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/mod.rs:601:35 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/mod.rs:602:35 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/mod.rs:607:37 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/mod.rs:608:35 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/mod.rs:764:35 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/mod.rs:765:39 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/linux_like/mod.rs:991:30 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/mod.rs:198:29 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/mod.rs:199:28 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/mod.rs:201:35 clippy::unnecessary_cast "casting integer literal to `usize` is unnecessary" +target/lintcheck/sources/libc-0.2.81/src/unix/mod.rs:202:35 clippy::unnecessary_cast "casting integer literal to `usize` is unnecessary" +target/lintcheck/sources/libc-0.2.81/src/unix/mod.rs:282:40 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/mod.rs:284:41 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/mod.rs:285:36 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/libc-0.2.81/src/unix/mod.rs:36:1 clippy::expl_impl_clone_on_copy "you are implementing `Clone` explicitly on a `Copy` type" +target/lintcheck/sources/libc-0.2.81/src/unix/mod.rs:388:1 clippy::expl_impl_clone_on_copy "you are implementing `Clone` explicitly on a `Copy` type" +target/lintcheck/sources/libc-0.2.81/src/unix/mod.rs:396:1 clippy::expl_impl_clone_on_copy "you are implementing `Clone` explicitly on a `Copy` type" +target/lintcheck/sources/log-0.4.11/src/lib.rs:1047:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/log-0.4.11/src/lib.rs:1053:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/log-0.4.11/src/lib.rs:1059:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/log-0.4.11/src/lib.rs:1093:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/log-0.4.11/src/lib.rs:1093:5 clippy::new_without_default "you should consider adding a `Default` implementation for `MetadataBuilder<'a>`" +target/lintcheck/sources/log-0.4.11/src/lib.rs:1118:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/log-0.4.11/src/lib.rs:1177:1 clippy::inline_always "you have declared `#[inline(always)]` on `max_level`. This is usually a bad idea" +target/lintcheck/sources/log-0.4.11/src/lib.rs:1178:1 clippy::must_use_candidate "this function could have a `#[must_use]` attribute" +target/lintcheck/sources/log-0.4.11/src/lib.rs:1306:1 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/log-0.4.11/src/lib.rs:1358:1 clippy::must_use_candidate "this function could have a `#[must_use]` attribute" +target/lintcheck/sources/log-0.4.11/src/lib.rs:1359:5 clippy::if_not_else "unnecessary `!=` operation" +target/lintcheck/sources/log-0.4.11/src/lib.rs:1407:1 clippy::must_use_candidate "this function could have a `#[must_use]` attribute" +target/lintcheck/sources/log-0.4.11/src/lib.rs:356:1 clippy::expl_impl_clone_on_copy "you are implementing `Clone` explicitly on a `Copy` type" +target/lintcheck/sources/log-0.4.11/src/lib.rs:448:12 clippy::manual_range_contains "manual `RangeInclusive::contains` implementation" +target/lintcheck/sources/log-0.4.11/src/lib.rs:500:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/log-0.4.11/src/lib.rs:506:28 clippy::trivially_copy_pass_by_ref "this argument (8 byte) is passed by reference, but would be more efficient if passed by value (limit: 8 byte)" +target/lintcheck/sources/log-0.4.11/src/lib.rs:506:28 clippy::wrong_self_convention "methods with the following characteristics: (`to_*` and `self` type is `Copy`) usually take `self` by value" +target/lintcheck/sources/log-0.4.11/src/lib.rs:506:5 clippy::missing_panics_doc "docs for function which may panic missing `# Panics` section" +target/lintcheck/sources/log-0.4.11/src/lib.rs:506:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/log-0.4.11/src/lib.rs:538:1 clippy::expl_impl_clone_on_copy "you are implementing `Clone` explicitly on a `Copy` type" +target/lintcheck/sources/log-0.4.11/src/lib.rs:653:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/log-0.4.11/src/lib.rs:661:21 clippy::trivially_copy_pass_by_ref "this argument (8 byte) is passed by reference, but would be more efficient if passed by value (limit: 8 byte)" +target/lintcheck/sources/log-0.4.11/src/lib.rs:661:21 clippy::wrong_self_convention "methods with the following characteristics: (`to_*` and `self` type is `Copy`) usually take `self` by value" +target/lintcheck/sources/log-0.4.11/src/lib.rs:661:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/log-0.4.11/src/lib.rs:677:44 clippy::match_same_arms "this `match` has identical arm bodies" +target/lintcheck/sources/log-0.4.11/src/lib.rs:758:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/log-0.4.11/src/lib.rs:764:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/log-0.4.11/src/lib.rs:770:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/log-0.4.11/src/lib.rs:776:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/log-0.4.11/src/lib.rs:782:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/log-0.4.11/src/lib.rs:788:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/log-0.4.11/src/lib.rs:794:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/log-0.4.11/src/lib.rs:803:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/log-0.4.11/src/lib.rs:809:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/log-0.4.11/src/lib.rs:818:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/log-0.4.11/src/lib.rs:908:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/log-0.4.11/src/lib.rs:908:5 clippy::new_without_default "you should consider adding a `Default` implementation for `RecordBuilder<'a>`" +target/lintcheck/sources/log-0.4.11/src/lib.rs:995:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/proc-macro2-1.0.24/src/detection.rs:2:5 clippy::wildcard_imports "usage of wildcard import" +target/lintcheck/sources/proc-macro2-1.0.24/src/fallback.rs:108:17 clippy::match_wildcard_for_single_variants "wildcard matches only a single variant and will also match any future added variants" +target/lintcheck/sources/proc-macro2-1.0.24/src/fallback.rs:269:20 clippy::unused_self "unused `self` argument" +target/lintcheck/sources/proc-macro2-1.0.24/src/fallback.rs:430:24 clippy::trivially_copy_pass_by_ref "this argument (0 byte) is passed by reference, but would be more efficient if passed by value (limit: 8 byte)" +target/lintcheck/sources/proc-macro2-1.0.24/src/fallback.rs:437:23 clippy::trivially_copy_pass_by_ref "this argument (0 byte) is passed by reference, but would be more efficient if passed by value (limit: 8 byte)" +target/lintcheck/sources/proc-macro2-1.0.24/src/fallback.rs:437:23 clippy::unused_self "unused `self` argument" +target/lintcheck/sources/proc-macro2-1.0.24/src/fallback.rs:471:17 clippy::trivially_copy_pass_by_ref "this argument (0 byte) is passed by reference, but would be more efficient if passed by value (limit: 8 byte)" +target/lintcheck/sources/proc-macro2-1.0.24/src/fallback.rs:471:17 clippy::unused_self "unused `self` argument" +target/lintcheck/sources/proc-macro2-1.0.24/src/fallback.rs:50:9 clippy::mem_replace_with_default "replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take`" +target/lintcheck/sources/proc-macro2-1.0.24/src/fallback.rs:654:5 clippy::manual_range_contains "manual `RangeInclusive::contains` implementation" +target/lintcheck/sources/proc-macro2-1.0.24/src/fallback.rs:655:12 clippy::manual_range_contains "manual `RangeInclusive::contains` implementation" +target/lintcheck/sources/proc-macro2-1.0.24/src/fallback.rs:661:5 clippy::manual_range_contains "manual `RangeInclusive::contains` implementation" +target/lintcheck/sources/proc-macro2-1.0.24/src/fallback.rs:662:12 clippy::manual_range_contains "manual `RangeInclusive::contains` implementation" +target/lintcheck/sources/proc-macro2-1.0.24/src/fallback.rs:664:12 clippy::manual_range_contains "manual `RangeInclusive::contains` implementation" +target/lintcheck/sources/proc-macro2-1.0.24/src/fallback.rs:674:37 clippy::manual_range_contains "manual `RangeInclusive::contains` implementation" +target/lintcheck/sources/proc-macro2-1.0.24/src/fallback.rs:678:5 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope" +target/lintcheck/sources/proc-macro2-1.0.24/src/fallback.rs:85:9 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope" +target/lintcheck/sources/proc-macro2-1.0.24/src/fallback.rs:882:43 clippy::unused_self "unused `self` argument" +target/lintcheck/sources/proc-macro2-1.0.24/src/lib.rs:1017:9 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/proc-macro2-1.0.24/src/lib.rs:1081:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/proc-macro2-1.0.24/src/lib.rs:1099:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/proc-macro2-1.0.24/src/lib.rs:1117:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/proc-macro2-1.0.24/src/lib.rs:1135:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/proc-macro2-1.0.24/src/lib.rs:1141:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/proc-macro2-1.0.24/src/lib.rs:1146:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/proc-macro2-1.0.24/src/lib.rs:1151:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/proc-macro2-1.0.24/src/lib.rs:1156:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/proc-macro2-1.0.24/src/lib.rs:152:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/proc-macro2-1.0.24/src/lib.rs:157:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/proc-macro2-1.0.24/src/lib.rs:373:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/proc-macro2-1.0.24/src/lib.rs:383:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/proc-macro2-1.0.24/src/lib.rs:397:24 clippy::trivially_copy_pass_by_ref "this argument (4 byte) is passed by reference, but would be more efficient if passed by value (limit: 8 byte)" +target/lintcheck/sources/proc-macro2-1.0.24/src/lib.rs:397:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/proc-macro2-1.0.24/src/lib.rs:403:23 clippy::trivially_copy_pass_by_ref "this argument (4 byte) is passed by reference, but would be more efficient if passed by value (limit: 8 byte)" +target/lintcheck/sources/proc-macro2-1.0.24/src/lib.rs:403:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/proc-macro2-1.0.24/src/lib.rs:418:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/proc-macro2-1.0.24/src/lib.rs:425:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/proc-macro2-1.0.24/src/lib.rs:464:17 clippy::trivially_copy_pass_by_ref "this argument (4 byte) is passed by reference, but would be more efficient if passed by value (limit: 8 byte)" +target/lintcheck/sources/proc-macro2-1.0.24/src/lib.rs:500:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/proc-macro2-1.0.24/src/lib.rs:626:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/proc-macro2-1.0.24/src/lib.rs:633:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/proc-macro2-1.0.24/src/lib.rs:641:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/proc-macro2-1.0.24/src/lib.rs:652:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/proc-macro2-1.0.24/src/lib.rs:662:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/proc-macro2-1.0.24/src/lib.rs:672:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/proc-macro2-1.0.24/src/lib.rs:734:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/proc-macro2-1.0.24/src/lib.rs:743:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/proc-macro2-1.0.24/src/lib.rs:752:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/proc-macro2-1.0.24/src/lib.rs:757:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/proc-macro2-1.0.24/src/lib.rs:788:19 clippy::doc_markdown "you should put `XID_Start` between ticks in the documentation" +target/lintcheck/sources/proc-macro2-1.0.24/src/lib.rs:788:69 clippy::doc_markdown "you should put `XID_Continue` between ticks in the documentation" +target/lintcheck/sources/proc-macro2-1.0.24/src/lib.rs:891:36 clippy::doc_markdown "you should put `syn::parse_str` between ticks in the documentation" +target/lintcheck/sources/proc-macro2-1.0.24/src/lib.rs:894:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/proc-macro2-1.0.24/src/lib.rs:911:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/proc-macro2-1.0.24/src/lib.rs:996:9 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/proc-macro2-1.0.24/src/parse.rs:353:17 clippy::unnested_or_patterns "unnested or-patterns" +target/lintcheck/sources/proc-macro2-1.0.24/src/parse.rs:360:17 clippy::unnested_or_patterns "unnested or-patterns" +target/lintcheck/sources/proc-macro2-1.0.24/src/parse.rs:411:17 clippy::unnested_or_patterns "unnested or-patterns" +target/lintcheck/sources/proc-macro2-1.0.24/src/parse.rs:413:17 clippy::unnested_or_patterns "unnested or-patterns" +target/lintcheck/sources/proc-macro2-1.0.24/src/parse.rs:476:13 clippy::unnested_or_patterns "unnested or-patterns" +target/lintcheck/sources/proc-macro2-1.0.24/src/parse.rs:500:13 clippy::unnested_or_patterns "unnested or-patterns" +target/lintcheck/sources/proc-macro2-1.0.24/src/parse.rs:552:5 clippy::while_let_on_iterator "this loop could be written as a `for` loop" +target/lintcheck/sources/proc-macro2-1.0.24/src/parse.rs:584:21 clippy::manual_range_contains "manual `RangeInclusive::contains` implementation" +target/lintcheck/sources/proc-macro2-1.0.24/src/parse.rs:602:20 clippy::map_unwrap_or "called `map(<f>).unwrap_or(<a>)` on an `Option` value. This can be done more directly by calling `map_or(<a>, <f>)` instead" +target/lintcheck/sources/proc-macro2-1.0.24/src/parse.rs:696:29 clippy::cast_lossless "casting `u8` to `u64` may become silently lossy if you later change the type" +target/lintcheck/sources/proc-macro2-1.0.24/src/parse.rs:702:34 clippy::cast_lossless "casting `u8` to `u64` may become silently lossy if you later change the type" +target/lintcheck/sources/proc-macro2-1.0.24/src/parse.rs:708:34 clippy::cast_lossless "casting `u8` to `u64` may become silently lossy if you later change the type" +target/lintcheck/sources/proc-macro2-1.0.24/src/parse.rs:793:5 clippy::vec_init_then_push "calls to `push` immediately after creation" +target/lintcheck/sources/proc-macro2-1.0.24/src/parse.rs:803:15 clippy::explicit_iter_loop "it is more concise to loop over references to containers instead of using explicit iteration methods" +target/lintcheck/sources/proc-macro2-1.0.24/src/parse.rs:808:15 clippy::explicit_iter_loop "it is more concise to loop over references to containers instead of using explicit iteration methods" +target/lintcheck/sources/proc-macro2-1.0.24/src/wrapper.rs:415:24 clippy::trivially_copy_pass_by_ref "this argument (4 byte) is passed by reference, but would be more efficient if passed by value (limit: 8 byte)" +target/lintcheck/sources/proc-macro2-1.0.24/src/wrapper.rs:429:23 clippy::trivially_copy_pass_by_ref "this argument (4 byte) is passed by reference, but would be more efficient if passed by value (limit: 8 byte)" +target/lintcheck/sources/proc-macro2-1.0.24/src/wrapper.rs:492:17 clippy::trivially_copy_pass_by_ref "this argument (4 byte) is passed by reference, but would be more efficient if passed by value (limit: 8 byte)" +target/lintcheck/sources/puffin-02dd4a3/puffin-imgui/src/ui.rs:158:15 clippy::cast_precision_loss "casting `i64` to `f32` causes a loss of precision (`i64` is 64 bits wide, but `f32`'s mantissa is only 23 bits wide)" +target/lintcheck/sources/puffin-02dd4a3/puffin-imgui/src/ui.rs:175:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/puffin-02dd4a3/puffin-imgui/src/ui.rs:183:5 clippy::too_many_lines "this function has too many lines (115/100)" +target/lintcheck/sources/puffin-02dd4a3/puffin-imgui/src/ui.rs:1:5 clippy::wildcard_imports "usage of wildcard import" +target/lintcheck/sources/puffin-02dd4a3/puffin-imgui/src/ui.rs:207:16 clippy::collapsible_else_if "this `else { if .. }` block can be collapsed" +target/lintcheck/sources/puffin-02dd4a3/puffin-imgui/src/ui.rs:271:67 clippy::cast_precision_loss "casting `i64` to `f32` causes a loss of precision (`i64` is 64 bits wide, but `f32`'s mantissa is only 23 bits wide)" +target/lintcheck/sources/puffin-02dd4a3/puffin-imgui/src/ui.rs:2:5 clippy::wildcard_imports "usage of wildcard import" +target/lintcheck/sources/puffin-02dd4a3/puffin-imgui/src/ui.rs:376:29 clippy::cast_precision_loss "casting `i64` to `f32` causes a loss of precision (`i64` is 64 bits wide, but `f32`'s mantissa is only 23 bits wide)" +target/lintcheck/sources/puffin-02dd4a3/puffin-imgui/src/ui.rs:381:44 clippy::cast_precision_loss "casting `i64` to `f32` causes a loss of precision (`i64` is 64 bits wide, but `f32`'s mantissa is only 23 bits wide)" +target/lintcheck/sources/puffin-02dd4a3/puffin-imgui/src/ui.rs:453:9 clippy::similar_names "binding's name is too similar to existing binding" +target/lintcheck/sources/puffin-02dd4a3/puffin-imgui/src/ui.rs:540:14 clippy::cast_possible_truncation "casting `f64` to `f32` may truncate the value" +target/lintcheck/sources/puffin-02dd4a3/puffin-imgui/src/ui.rs:551:5 clippy::cast_precision_loss "casting `i64` to `f64` causes a loss of precision (`i64` is 64 bits wide, but `f64`'s mantissa is only 52 bits wide)" +target/lintcheck/sources/puffin-02dd4a3/puffin-imgui/src/ui.rs:584:39 clippy::cast_precision_loss "casting `usize` to `f32` causes a loss of precision (`usize` is 32 or 64 bits wide, but `f32`'s mantissa is only 23 bits wide)" +target/lintcheck/sources/puffin-02dd4a3/puffin-imgui/src/ui.rs:59:26 clippy::unsafe_derive_deserialize "you are deriving `serde::Deserialize` on a type that has methods using `unsafe`" +target/lintcheck/sources/puffin-02dd4a3/puffin-imgui/src/ui.rs:61:1 clippy::module_name_repetitions "item name ends with its containing module's name" +target/lintcheck/sources/puffin-02dd4a3/puffin-imgui/src/ui.rs:627:39 clippy::cast_precision_loss "casting `usize` to `f32` causes a loss of precision (`usize` is 32 or 64 bits wide, but `f32`'s mantissa is only 23 bits wide)" +target/lintcheck/sources/puffin-02dd4a3/puffin-imgui/src/ui.rs:674:47 clippy::cast_precision_loss "casting `usize` to `f64` causes a loss of precision on targets with 64-bit wide pointers (`usize` is 64 bits wide, but `f64`'s mantissa is only 52 bits wide)" +target/lintcheck/sources/puffin-02dd4a3/puffin-imgui/src/ui.rs:690:9 clippy::cast_precision_loss "casting `usize` to `f32` causes a loss of precision (`usize` is 32 or 64 bits wide, but `f32`'s mantissa is only 23 bits wide)" +target/lintcheck/sources/puffin-02dd4a3/puffin/src/data.rs:102:25 clippy::cast_possible_truncation "casting `usize` to `u8` may truncate the value" +target/lintcheck/sources/puffin-02dd4a3/puffin/src/data.rs:112:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/puffin-02dd4a3/puffin/src/data.rs:116:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/puffin-02dd4a3/puffin/src/data.rs:137:24 clippy::match_same_arms "this `match` has identical arm bodies" +target/lintcheck/sources/puffin-02dd4a3/puffin/src/data.rs:177:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/puffin-02dd4a3/puffin/src/data.rs:211:21 clippy::cast_possible_truncation "casting `u64` to `usize` may truncate the value on targets with 32-bit wide pointers" +target/lintcheck/sources/puffin-02dd4a3/puffin/src/data.rs:24:5 clippy::wildcard_imports "usage of wildcard import" +target/lintcheck/sources/puffin-02dd4a3/puffin/src/lib.rs:113:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/puffin-02dd4a3/puffin/src/lib.rs:147:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/puffin-02dd4a3/puffin/src/lib.rs:147:5 clippy::missing_panics_doc "docs for function which may panic missing `# Panics` section" +target/lintcheck/sources/puffin-02dd4a3/puffin/src/lib.rs:165:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/puffin-02dd4a3/puffin/src/lib.rs:200:21 clippy::default_trait_access "calling `Stream::default()` is more clear than this expression" +target/lintcheck/sources/puffin-02dd4a3/puffin/src/lib.rs:257:78 clippy::default_trait_access "calling `std::cell::RefCell::default()` is more clear than this expression" +target/lintcheck/sources/puffin-02dd4a3/puffin/src/lib.rs:297:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/puffin-02dd4a3/puffin/src/lib.rs:302:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/puffin-02dd4a3/puffin/src/lib.rs:308:28 clippy::default_trait_access "calling `FullProfileData::default()` is more clear than this expression" +target/lintcheck/sources/puffin-02dd4a3/puffin/src/lib.rs:316:1 clippy::must_use_candidate "this function could have a `#[must_use]` attribute" +target/lintcheck/sources/puffin-02dd4a3/puffin/src/lib.rs:321:5 clippy::cast_possible_truncation "casting `u128` to `i64` may truncate the value" +target/lintcheck/sources/puffin-02dd4a3/puffin/src/lib.rs:348:28 clippy::default_trait_access "calling `std::marker::PhantomData::default()` is more clear than this expression" +target/lintcheck/sources/puffin-02dd4a3/puffin/src/lib.rs:359:1 clippy::must_use_candidate "this function could have a `#[must_use]` attribute" +target/lintcheck/sources/puffin-02dd4a3/puffin/src/lib.rs:375:1 clippy::must_use_candidate "this function could have a `#[must_use]` attribute" +target/lintcheck/sources/puffin-02dd4a3/puffin/src/lib.rs:376:5 clippy::option_if_let_else "use Option::map_or instead of an if let/else" +target/lintcheck/sources/puffin-02dd4a3/puffin/src/lib.rs:377:9 clippy::option_if_let_else "use Option::map_or instead of an if let/else" +target/lintcheck/sources/puffin-02dd4a3/puffin/src/lib.rs:406:1 clippy::must_use_candidate "this function could have a `#[must_use]` attribute" +target/lintcheck/sources/puffin-02dd4a3/puffin/src/lib.rs:408:5 clippy::option_if_let_else "use Option::map_or instead of an if let/else" +target/lintcheck/sources/puffin-02dd4a3/puffin/src/lib.rs:69:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/puffin-02dd4a3/puffin/src/lib.rs:73:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/puffin-02dd4a3/puffin/src/lib.rs:77:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/puffin-02dd4a3/puffin/src/merge.rs:21:1 clippy::module_name_repetitions "item name starts with its containing module's name" +target/lintcheck/sources/puffin-02dd4a3/puffin/src/merge.rs:28:1 clippy::module_name_repetitions "item name starts with its containing module's name" +target/lintcheck/sources/puffin-02dd4a3/puffin/src/merge.rs:28:1 clippy::must_use_candidate "this function could have a `#[must_use]` attribute" +target/lintcheck/sources/puffin-02dd4a3/puffin/src/merge.rs:35:1 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/puffin-02dd4a3/puffin/src/merge.rs:35:1 clippy::module_name_repetitions "item name starts with its containing module's name" +target/lintcheck/sources/puffin-02dd4a3/puffin/src/merge.rs:64:43 clippy::default_trait_access "calling `std::vec::Vec::default()` is more clear than this expression" +target/lintcheck/sources/puffin-02dd4a3/puffin/src/merge.rs:65:54 clippy::default_trait_access "calling `std::collections::HashMap::default()` is more clear than this expression" +target/lintcheck/sources/puffin-02dd4a3/puffin/src/merge.rs:9:1 clippy::module_name_repetitions "item name starts with its containing module's name" +target/lintcheck/sources/quote-1.0.7/src/ext.rs:10:1 clippy::module_name_repetitions "item name ends with its containing module's name" +target/lintcheck/sources/quote-1.0.7/src/ext.rs:7:5 clippy::doc_markdown "you should put `TokenStream` between ticks in the documentation" +target/lintcheck/sources/quote-1.0.7/src/ident_fragment.rs:13:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/quote-1.0.7/src/ident_fragment.rs:51:31 clippy::manual_strip "stripping a prefix manually" +target/lintcheck/sources/quote-1.0.7/src/runtime.rs:52:5 clippy::module_name_repetitions "item name ends with its containing module's name" +target/lintcheck/sources/quote-1.0.7/src/runtime.rs:63:5 clippy::module_name_repetitions "item name ends with its containing module's name" +target/lintcheck/sources/quote-1.0.7/src/runtime.rs:66:33 clippy::doc_markdown "you should put `DoesNotHaveIter` between ticks in the documentation" +target/lintcheck/sources/quote-1.0.7/src/runtime.rs:80:5 clippy::module_name_repetitions "item name ends with its containing module's name" +target/lintcheck/sources/quote-1.0.7/src/to_tokens.rs:132:26 clippy::wrong_self_convention "methods with the following characteristics: (`to_*` and `self` type is `Copy`) usually take `self` by value" +target/lintcheck/sources/quote-1.0.7/src/to_tokens.rs:159:18 clippy::wrong_self_convention "methods with the following characteristics: (`to_*` and `self` type is `Copy`) usually take `self` by value" +target/lintcheck/sources/quote-1.0.7/src/to_tokens.rs:165:18 clippy::wrong_self_convention "methods with the following characteristics: (`to_*` and `self` type is `Copy`) usually take `self` by value" +target/lintcheck/sources/quote-1.0.7/src/to_tokens.rs:80:18 clippy::wrong_self_convention "methods with the following characteristics: (`to_*` and `self` type is `Copy`) usually take `self` by value" +target/lintcheck/sources/rand-0.7.3/src/distributions/bernoulli.rs:103:20 clippy::cast_possible_truncation "casting `f64` to `u64` may truncate the value" +target/lintcheck/sources/rand-0.7.3/src/distributions/bernoulli.rs:103:20 clippy::cast_sign_loss "casting `f64` to `u64` may lose the sign of the value" +target/lintcheck/sources/rand-0.7.3/src/distributions/bernoulli.rs:116:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/rand-0.7.3/src/distributions/bernoulli.rs:123:21 clippy::cast_possible_truncation "casting `f64` to `u64` may truncate the value" +target/lintcheck/sources/rand-0.7.3/src/distributions/bernoulli.rs:123:21 clippy::cast_sign_loss "casting `f64` to `u64` may lose the sign of the value" +target/lintcheck/sources/rand-0.7.3/src/distributions/bernoulli.rs:63:26 clippy::cast_precision_loss "casting `u64` to `f64` causes a loss of precision (`u64` is 64 bits wide, but `f64`'s mantissa is only 52 bits wide)" +target/lintcheck/sources/rand-0.7.3/src/distributions/bernoulli.rs:63:27 clippy::unseparated_literal_suffix "integer type suffix should be separated by an underscore" +target/lintcheck/sources/rand-0.7.3/src/distributions/bernoulli.rs:67:1 clippy::module_name_repetitions "item name starts with its containing module's name" +target/lintcheck/sources/rand-0.7.3/src/distributions/bernoulli.rs:95:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/rand-0.7.3/src/distributions/bernoulli.rs:96:13 clippy::manual_range_contains "manual `Range::contains` implementation" +target/lintcheck/sources/rand-0.7.3/src/distributions/binomial.rs:107:23 clippy::cast_precision_loss "casting `i64` to `f64` causes a loss of precision (`i64` is 64 bits wide, but `f64`'s mantissa is only 52 bits wide)" +target/lintcheck/sources/rand-0.7.3/src/distributions/binomial.rs:112:44 clippy::cast_precision_loss "casting `i64` to `f64` causes a loss of precision (`i64` is 64 bits wide, but `f64`'s mantissa is only 52 bits wide)" +target/lintcheck/sources/rand-0.7.3/src/distributions/binomial.rs:116:13 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope" +target/lintcheck/sources/rand-0.7.3/src/distributions/binomial.rs:150:28 clippy::redundant_else "redundant else block" +target/lintcheck/sources/rand-0.7.3/src/distributions/binomial.rs:153:24 clippy::if_not_else "unnecessary boolean `not` operation" +target/lintcheck/sources/rand-0.7.3/src/distributions/binomial.rs:158:28 clippy::redundant_else "redundant else block" +target/lintcheck/sources/rand-0.7.3/src/distributions/binomial.rs:164:33 clippy::cast_sign_loss "casting `i64` to `u64` may lose the sign of the value" +target/lintcheck/sources/rand-0.7.3/src/distributions/binomial.rs:166:28 clippy::redundant_else "redundant else block" +target/lintcheck/sources/rand-0.7.3/src/distributions/binomial.rs:175:47 clippy::cast_precision_loss "casting `i64` to `f64` causes a loss of precision (`i64` is 64 bits wide, but `f64`'s mantissa is only 52 bits wide)" +target/lintcheck/sources/rand-0.7.3/src/distributions/binomial.rs:185:38 clippy::cast_precision_loss "casting `i64` to `f64` causes a loss of precision (`i64` is 64 bits wide, but `f64`'s mantissa is only 52 bits wide)" +target/lintcheck/sources/rand-0.7.3/src/distributions/binomial.rs:194:38 clippy::cast_precision_loss "casting `i64` to `f64` causes a loss of precision (`i64` is 64 bits wide, but `f64`'s mantissa is only 52 bits wide)" +target/lintcheck/sources/rand-0.7.3/src/distributions/binomial.rs:202:28 clippy::redundant_else "redundant else block" +target/lintcheck/sources/rand-0.7.3/src/distributions/binomial.rs:209:25 clippy::cast_precision_loss "casting `i64` to `f64` causes a loss of precision (`i64` is 64 bits wide, but `f64`'s mantissa is only 52 bits wide)" +target/lintcheck/sources/rand-0.7.3/src/distributions/binomial.rs:221:26 clippy::cast_precision_loss "casting `i64` to `f64` causes a loss of precision (`i64` is 64 bits wide, but `f64`'s mantissa is only 52 bits wide)" +target/lintcheck/sources/rand-0.7.3/src/distributions/binomial.rs:222:26 clippy::cast_precision_loss "casting `i64` to `f64` causes a loss of precision (`i64` is 64 bits wide, but `f64`'s mantissa is only 52 bits wide)" +target/lintcheck/sources/rand-0.7.3/src/distributions/binomial.rs:223:25 clippy::cast_precision_loss "casting `i64` to `f64` causes a loss of precision (`i64` is 64 bits wide, but `f64`'s mantissa is only 52 bits wide)" +target/lintcheck/sources/rand-0.7.3/src/distributions/binomial.rs:224:25 clippy::cast_precision_loss "casting `i64` to `f64` causes a loss of precision (`i64` is 64 bits wide, but `f64`'s mantissa is only 52 bits wide)" +target/lintcheck/sources/rand-0.7.3/src/distributions/binomial.rs:226:17 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope" +target/lintcheck/sources/rand-0.7.3/src/distributions/binomial.rs:233:32 clippy::cast_precision_loss "casting `i64` to `f64` causes a loss of precision (`i64` is 64 bits wide, but `f64`'s mantissa is only 52 bits wide)" +target/lintcheck/sources/rand-0.7.3/src/distributions/binomial.rs:234:27 clippy::cast_precision_loss "casting `i64` to `f64` causes a loss of precision (`i64` is 64 bits wide, but `f64`'s mantissa is only 52 bits wide)" +target/lintcheck/sources/rand-0.7.3/src/distributions/binomial.rs:251:22 clippy::cast_sign_loss "casting `i64` to `u64` may lose the sign of the value" +target/lintcheck/sources/rand-0.7.3/src/distributions/binomial.rs:255:9 clippy::if_not_else "unnecessary `!=` operation" +target/lintcheck/sources/rand-0.7.3/src/distributions/binomial.rs:35:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/rand-0.7.3/src/distributions/binomial.rs:45:17 clippy::cast_precision_loss "casting `i64` to `f64` causes a loss of precision (`i64` is 64 bits wide, but `f64`'s mantissa is only 52 bits wide)" +target/lintcheck/sources/rand-0.7.3/src/distributions/binomial.rs:46:5 clippy::cast_possible_truncation "casting `f64` to `i64` may truncate the value" +target/lintcheck/sources/rand-0.7.3/src/distributions/binomial.rs:50:5 clippy::too_many_lines "this function has too many lines (143/100)" +target/lintcheck/sources/rand-0.7.3/src/distributions/binomial.rs:76:9 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope" +target/lintcheck/sources/rand-0.7.3/src/distributions/binomial.rs:78:12 clippy::cast_precision_loss "casting `u64` to `f64` causes a loss of precision (`u64` is 64 bits wide, but `f64`'s mantissa is only 52 bits wide)" +target/lintcheck/sources/rand-0.7.3/src/distributions/binomial.rs:81:21 clippy::cast_precision_loss "casting `u64` to `f64` causes a loss of precision (`u64` is 64 bits wide, but `f64`'s mantissa is only 52 bits wide)" +target/lintcheck/sources/rand-0.7.3/src/distributions/binomial.rs:82:32 clippy::cast_possible_truncation "casting `u64` to `i32` may truncate the value" +target/lintcheck/sources/rand-0.7.3/src/distributions/binomial.rs:88:26 clippy::cast_precision_loss "casting `u64` to `f64` causes a loss of precision (`u64` is 64 bits wide, but `f64`'s mantissa is only 52 bits wide)" +target/lintcheck/sources/rand-0.7.3/src/distributions/binomial.rs:99:21 clippy::cast_precision_loss "casting `u64` to `f64` causes a loss of precision (`u64` is 64 bits wide, but `f64`'s mantissa is only 52 bits wide)" +target/lintcheck/sources/rand-0.7.3/src/distributions/cauchy.rs:33:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/rand-0.7.3/src/distributions/dirichlet.rs:52:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/rand-0.7.3/src/distributions/dirichlet.rs:64:32 clippy::unseparated_literal_suffix "float type suffix should be separated by an underscore" +target/lintcheck/sources/rand-0.7.3/src/distributions/dirichlet.rs:65:23 clippy::unseparated_literal_suffix "float type suffix should be separated by an underscore" +target/lintcheck/sources/rand-0.7.3/src/distributions/exponential.rs:76:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/rand-0.7.3/src/distributions/float.rs:73:1 clippy::module_name_repetitions "item name ends with its containing module's name" +target/lintcheck/sources/rand-0.7.3/src/distributions/gamma.rs:13:5 clippy::enum_glob_use "usage of wildcard import for enum variants" +target/lintcheck/sources/rand-0.7.3/src/distributions/gamma.rs:14:5 clippy::enum_glob_use "usage of wildcard import for enum variants" +target/lintcheck/sources/rand-0.7.3/src/distributions/gamma.rs:189:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/rand-0.7.3/src/distributions/gamma.rs:230:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/rand-0.7.3/src/distributions/gamma.rs:259:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/rand-0.7.3/src/distributions/gamma.rs:287:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/rand-0.7.3/src/distributions/gamma.rs:90:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/rand-0.7.3/src/distributions/integer.rs:23:9 clippy::cast_possible_truncation "casting `u32` to `u8` may truncate the value" +target/lintcheck/sources/rand-0.7.3/src/distributions/integer.rs:30:9 clippy::cast_possible_truncation "casting `u32` to `u16` may truncate the value" +target/lintcheck/sources/rand-0.7.3/src/distributions/integer.rs:69:9 clippy::cast_possible_truncation "casting `u64` to `usize` may truncate the value on targets with 32-bit wide pointers" +target/lintcheck/sources/rand-0.7.3/src/distributions/mod.rs:263:5 clippy::inline_always "you have declared `#[inline(always)]` on `next`. This is usually a bad idea" +target/lintcheck/sources/rand-0.7.3/src/distributions/normal.rs:100:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/rand-0.7.3/src/distributions/normal.rs:119:1 clippy::module_name_repetitions "item name ends with its containing module's name" +target/lintcheck/sources/rand-0.7.3/src/distributions/normal.rs:131:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/rand-0.7.3/src/distributions/normal.rs:31:1 clippy::module_name_repetitions "item name ends with its containing module's name" +target/lintcheck/sources/rand-0.7.3/src/distributions/normal.rs:47:25 clippy::unseparated_literal_suffix "float type suffix should be separated by an underscore" +target/lintcheck/sources/rand-0.7.3/src/distributions/normal.rs:48:25 clippy::unseparated_literal_suffix "float type suffix should be separated by an underscore" +target/lintcheck/sources/rand-0.7.3/src/distributions/other.rs:89:9 clippy::cast_possible_wrap "casting `u32` to `i32` may wrap around the value" +target/lintcheck/sources/rand-0.7.3/src/distributions/pareto.rs:32:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/rand-0.7.3/src/distributions/poisson.rs:35:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/rand-0.7.3/src/distributions/poisson.rs:87:30 clippy::cast_possible_truncation "casting `f64` to `u64` may truncate the value" +target/lintcheck/sources/rand-0.7.3/src/distributions/poisson.rs:87:30 clippy::cast_sign_loss "casting `f64` to `u64` may lose the sign of the value" +target/lintcheck/sources/rand-0.7.3/src/distributions/triangular.rs:32:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/rand-0.7.3/src/distributions/uniform.rs:146:4 clippy::needless_doctest_main "needless `fn main` in doctest" +target/lintcheck/sources/rand-0.7.3/src/distributions/uniform.rs:199:1 clippy::module_name_repetitions "item name ends with its containing module's name" +target/lintcheck/sources/rand-0.7.3/src/distributions/uniform.rs:214:1 clippy::module_name_repetitions "item name starts with its containing module's name" +target/lintcheck/sources/rand-0.7.3/src/distributions/uniform.rs:283:14 clippy::doc_markdown "you should put `SampleUniform` between ticks in the documentation" +target/lintcheck/sources/rand-0.7.3/src/distributions/uniform.rs:283:46 clippy::doc_markdown "you should put `SampleUniform` between ticks in the documentation" +target/lintcheck/sources/rand-0.7.3/src/distributions/uniform.rs:296:5 clippy::inline_always "you have declared `#[inline(always)]` on `borrow`. This is usually a bad idea" +target/lintcheck/sources/rand-0.7.3/src/distributions/uniform.rs:304:5 clippy::inline_always "you have declared `#[inline(always)]` on `borrow`. This is usually a bad idea" +target/lintcheck/sources/rand-0.7.3/src/distributions/uniform.rs:350:1 clippy::module_name_repetitions "item name starts with its containing module's name" +target/lintcheck/sources/rand-0.7.3/src/distributions/uniform.rs:407:21 clippy::redundant_field_names "redundant field names in struct initialization" +target/lintcheck/sources/rand-0.7.3/src/distributions/uniform.rs:407:21 clippy::redundant_field_names "redundant field names in struct initialization" +target/lintcheck/sources/rand-0.7.3/src/distributions/uniform.rs:407:21 clippy::redundant_field_names "redundant field names in struct initialization" +target/lintcheck/sources/rand-0.7.3/src/distributions/uniform.rs:407:21 clippy::redundant_field_names "redundant field names in struct initialization" +target/lintcheck/sources/rand-0.7.3/src/distributions/uniform.rs:407:21 clippy::redundant_field_names "redundant field names in struct initialization" +target/lintcheck/sources/rand-0.7.3/src/distributions/uniform.rs:407:21 clippy::redundant_field_names "redundant field names in struct initialization" +target/lintcheck/sources/rand-0.7.3/src/distributions/uniform.rs:407:21 clippy::redundant_field_names "redundant field names in struct initialization" +target/lintcheck/sources/rand-0.7.3/src/distributions/uniform.rs:407:21 clippy::redundant_field_names "redundant field names in struct initialization" +target/lintcheck/sources/rand-0.7.3/src/distributions/uniform.rs:407:21 clippy::redundant_field_names "redundant field names in struct initialization" +target/lintcheck/sources/rand-0.7.3/src/distributions/uniform.rs:407:21 clippy::redundant_field_names "redundant field names in struct initialization" +target/lintcheck/sources/rand-0.7.3/src/distributions/uniform.rs:407:21 clippy::redundant_field_names "redundant field names in struct initialization" +target/lintcheck/sources/rand-0.7.3/src/distributions/uniform.rs:407:21 clippy::redundant_field_names "redundant field names in struct initialization" +target/lintcheck/sources/rand-0.7.3/src/distributions/uniform.rs:441:31 clippy::invalid_upcast_comparisons "because of the numeric bounds on `::core::u16::MAX` prior to casting, this expression is always false" +target/lintcheck/sources/rand-0.7.3/src/distributions/uniform.rs:441:31 clippy::invalid_upcast_comparisons "because of the numeric bounds on `::core::u16::MAX` prior to casting, this expression is always false" +target/lintcheck/sources/rand-0.7.3/src/distributions/uniform.rs:441:31 clippy::invalid_upcast_comparisons "because of the numeric bounds on `::core::u16::MAX` prior to casting, this expression is always false" +target/lintcheck/sources/rand-0.7.3/src/distributions/uniform.rs:441:31 clippy::invalid_upcast_comparisons "because of the numeric bounds on `::core::u16::MAX` prior to casting, this expression is always false" +target/lintcheck/sources/rand-0.7.3/src/distributions/uniform.rs:441:31 clippy::invalid_upcast_comparisons "because of the numeric bounds on `::core::u16::MAX` prior to casting, this expression is always false" +target/lintcheck/sources/rand-0.7.3/src/distributions/uniform.rs:441:31 clippy::invalid_upcast_comparisons "because of the numeric bounds on `::core::u16::MAX` prior to casting, this expression is always false" +target/lintcheck/sources/rand-0.7.3/src/distributions/uniform.rs:441:31 clippy::invalid_upcast_comparisons "because of the numeric bounds on `::core::u16::MAX` prior to casting, this expression is always false" +target/lintcheck/sources/rand-0.7.3/src/distributions/uniform.rs:441:31 clippy::invalid_upcast_comparisons "because of the numeric bounds on `::core::u16::MAX` prior to casting, this expression is always false" +target/lintcheck/sources/rand-0.7.3/src/distributions/uniform.rs:56:10 clippy::doc_markdown "you should put `SampleBorrow` between ticks in the documentation" +target/lintcheck/sources/rand-0.7.3/src/distributions/uniform.rs:647:1 clippy::module_name_repetitions "item name starts with its containing module's name" +target/lintcheck/sources/rand-0.7.3/src/distributions/uniform.rs:840:1 clippy::module_name_repetitions "item name starts with its containing module's name" +target/lintcheck/sources/rand-0.7.3/src/distributions/uniform.rs:913:13 clippy::option_if_let_else "use Option::map_or_else instead of an if let/else" +target/lintcheck/sources/rand-0.7.3/src/distributions/uniform.rs:943:54 clippy::cast_possible_truncation "casting `u64` to `u32` may truncate the value" +target/lintcheck/sources/rand-0.7.3/src/distributions/unit_circle.rs:30:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/rand-0.7.3/src/distributions/unit_sphere.rs:24:1 clippy::module_name_repetitions "item name starts with its containing module's name" +target/lintcheck/sources/rand-0.7.3/src/distributions/unit_sphere.rs:29:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/rand-0.7.3/src/distributions/utils.rs:247:15 clippy::wrong_self_convention "methods called `is_*` usually take `self` by reference or no `self`" +target/lintcheck/sources/rand-0.7.3/src/distributions/utils.rs:248:20 clippy::wrong_self_convention "methods called `is_*` usually take `self` by reference or no `self`" +target/lintcheck/sources/rand-0.7.3/src/distributions/utils.rs:249:18 clippy::wrong_self_convention "methods called `is_*` usually take `self` by reference or no `self`" +target/lintcheck/sources/rand-0.7.3/src/distributions/utils.rs:254:5 clippy::inline_always "you have declared `#[inline(always)]` on `lanes`. This is usually a bad idea" +target/lintcheck/sources/rand-0.7.3/src/distributions/utils.rs:258:5 clippy::inline_always "you have declared `#[inline(always)]` on `splat`. This is usually a bad idea" +target/lintcheck/sources/rand-0.7.3/src/distributions/utils.rs:262:5 clippy::inline_always "you have declared `#[inline(always)]` on `extract`. This is usually a bad idea" +target/lintcheck/sources/rand-0.7.3/src/distributions/utils.rs:267:5 clippy::inline_always "you have declared `#[inline(always)]` on `replace`. This is usually a bad idea" +target/lintcheck/sources/rand-0.7.3/src/distributions/utils.rs:281:5 clippy::inline_always "you have declared `#[inline(always)]` on `any`. This is usually a bad idea" +target/lintcheck/sources/rand-0.7.3/src/distributions/utils.rs:286:5 clippy::inline_always "you have declared `#[inline(always)]` on `all`. This is usually a bad idea" +target/lintcheck/sources/rand-0.7.3/src/distributions/utils.rs:291:5 clippy::inline_always "you have declared `#[inline(always)]` on `none`. This is usually a bad idea" +target/lintcheck/sources/rand-0.7.3/src/distributions/utils.rs:488:17 clippy::doc_markdown "you should put `x_i` between ticks in the documentation" +target/lintcheck/sources/rand-0.7.3/src/distributions/utils.rs:489:50 clippy::doc_markdown "you should put `x_i` between ticks in the documentation" +target/lintcheck/sources/rand-0.7.3/src/distributions/utils.rs:489:63 clippy::doc_markdown "you should put `f(x_i` between ticks in the documentation" +target/lintcheck/sources/rand-0.7.3/src/distributions/utils.rs:490:40 clippy::doc_markdown "you should put `f(x_i` between ticks in the documentation" +target/lintcheck/sources/rand-0.7.3/src/distributions/utils.rs:490:49 clippy::doc_markdown "you should put `f(x_{i+1` between ticks in the documentation" +target/lintcheck/sources/rand-0.7.3/src/distributions/utils.rs:518:17 clippy::cast_possible_truncation "casting `u64` to `usize` may truncate the value on targets with 32-bit wide pointers" +target/lintcheck/sources/rand-0.7.3/src/distributions/weibull.rs:29:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/rand-0.7.3/src/distributions/weighted/alias_method.rs:113:21 clippy::explicit_iter_loop "it is more concise to loop over references to containers instead of using explicit iteration methods" +target/lintcheck/sources/rand-0.7.3/src/distributions/weighted/alias_method.rs:125:9 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope" +target/lintcheck/sources/rand-0.7.3/src/distributions/weighted/alias_method.rs:131:9 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope" +target/lintcheck/sources/rand-0.7.3/src/distributions/weighted/alias_method.rs:180:36 clippy::cast_possible_truncation "casting `usize` to `u32` may truncate the value on targets with 64-bit wide pointers" +target/lintcheck/sources/rand-0.7.3/src/distributions/weighted/alias_method.rs:182:34 clippy::cast_possible_truncation "casting `usize` to `u32` may truncate the value on targets with 64-bit wide pointers" +target/lintcheck/sources/rand-0.7.3/src/distributions/weighted/alias_method.rs:259:28 clippy::clone_on_copy "using `clone` on type `distributions::uniform::Uniform<u32>` which implements the `Copy` trait" +target/lintcheck/sources/rand-0.7.3/src/distributions/weighted/alias_method.rs:296:9 clippy::map_clone "you are using an explicit closure for copying elements" +target/lintcheck/sources/rand-0.7.3/src/distributions/weighted/alias_method.rs:321:9 clippy::map_clone "you are using an explicit closure for copying elements" +target/lintcheck/sources/rand-0.7.3/src/distributions/weighted/alias_method.rs:78:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/rand-0.7.3/src/distributions/weighted/alias_method.rs:78:5 clippy::missing_panics_doc "docs for function which may panic missing `# Panics` section" +target/lintcheck/sources/rand-0.7.3/src/distributions/weighted/alias_method.rs:78:5 clippy::too_many_lines "this function has too many lines (106/100)" +target/lintcheck/sources/rand-0.7.3/src/distributions/weighted/alias_method.rs:85:17 clippy::cast_possible_truncation "casting `usize` to `u32` may truncate the value on targets with 64-bit wide pointers" +target/lintcheck/sources/rand-0.7.3/src/distributions/weighted/alias_method.rs:87:31 clippy::map_unwrap_or "called `map(<f>).unwrap_or(<a>)` on an `Option` value. This can be done more directly by calling `map_or(<a>, <f>)` instead" +target/lintcheck/sources/rand-0.7.3/src/distributions/weighted/mod.rs:100:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/rand-0.7.3/src/distributions/weighted/mod.rs:144:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/rand-0.7.3/src/distributions/weighted/mod.rs:144:5 clippy::missing_panics_doc "docs for function which may panic missing `# Panics` section" +target/lintcheck/sources/rand-0.7.3/src/distributions/weighted/mod.rs:169:16 clippy::int_plus_one "unnecessary `>= y + 1` or `x - 1 >=`" +target/lintcheck/sources/rand-0.7.3/src/distributions/weighted/mod.rs:386:1 clippy::module_name_repetitions "item name starts with its containing module's name" +target/lintcheck/sources/rand-0.7.3/src/distributions/weighted/mod.rs:85:1 clippy::module_name_repetitions "item name starts with its containing module's name" +target/lintcheck/sources/rand-0.7.3/src/lib.rs:333:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/rand-0.7.3/src/lib.rs:412:14 clippy::wrong_self_convention "methods with the following characteristics: (`to_*` and `self` type is not `Copy`) usually take `self` by reference" +target/lintcheck/sources/rand-0.7.3/src/lib.rs:435:22 clippy::wrong_self_convention "methods with the following characteristics: (`to_*` and `self` type is not `Copy`) usually take `self` by reference" +target/lintcheck/sources/rand-0.7.3/src/lib.rs:435:22 clippy::wrong_self_convention "methods with the following characteristics: (`to_*` and `self` type is not `Copy`) usually take `self` by reference" +target/lintcheck/sources/rand-0.7.3/src/lib.rs:435:22 clippy::wrong_self_convention "methods with the following characteristics: (`to_*` and `self` type is not `Copy`) usually take `self` by reference" +target/lintcheck/sources/rand-0.7.3/src/lib.rs:435:22 clippy::wrong_self_convention "methods with the following characteristics: (`to_*` and `self` type is not `Copy`) usually take `self` by reference" +target/lintcheck/sources/rand-0.7.3/src/lib.rs:435:22 clippy::wrong_self_convention "methods with the following characteristics: (`to_*` and `self` type is not `Copy`) usually take `self` by reference" +target/lintcheck/sources/rand-0.7.3/src/lib.rs:435:22 clippy::wrong_self_convention "methods with the following characteristics: (`to_*` and `self` type is not `Copy`) usually take `self` by reference" +target/lintcheck/sources/rand-0.7.3/src/lib.rs:435:22 clippy::wrong_self_convention "methods with the following characteristics: (`to_*` and `self` type is not `Copy`) usually take `self` by reference" +target/lintcheck/sources/rand-0.7.3/src/lib.rs:435:22 clippy::wrong_self_convention "methods with the following characteristics: (`to_*` and `self` type is not `Copy`) usually take `self` by reference" +target/lintcheck/sources/rand-0.7.3/src/lib.rs:435:22 clippy::wrong_self_convention "methods with the following characteristics: (`to_*` and `self` type is not `Copy`) usually take `self` by reference" +target/lintcheck/sources/rand-0.7.3/src/lib.rs:435:22 clippy::wrong_self_convention "methods with the following characteristics: (`to_*` and `self` type is not `Copy`) usually take `self` by reference" +target/lintcheck/sources/rand-0.7.3/src/lib.rs:435:22 clippy::wrong_self_convention "methods with the following characteristics: (`to_*` and `self` type is not `Copy`) usually take `self` by reference" +target/lintcheck/sources/rand-0.7.3/src/lib.rs:459:22 clippy::wrong_self_convention "methods with the following characteristics: (`to_*` and `self` type is not `Copy`) usually take `self` by reference" +target/lintcheck/sources/rand-0.7.3/src/lib.rs:459:22 clippy::wrong_self_convention "methods with the following characteristics: (`to_*` and `self` type is not `Copy`) usually take `self` by reference" +target/lintcheck/sources/rand-0.7.3/src/lib.rs:459:22 clippy::wrong_self_convention "methods with the following characteristics: (`to_*` and `self` type is not `Copy`) usually take `self` by reference" +target/lintcheck/sources/rand-0.7.3/src/lib.rs:459:22 clippy::wrong_self_convention "methods with the following characteristics: (`to_*` and `self` type is not `Copy`) usually take `self` by reference" +target/lintcheck/sources/rand-0.7.3/src/lib.rs:459:22 clippy::wrong_self_convention "methods with the following characteristics: (`to_*` and `self` type is not `Copy`) usually take `self` by reference" +target/lintcheck/sources/rand-0.7.3/src/lib.rs:459:22 clippy::wrong_self_convention "methods with the following characteristics: (`to_*` and `self` type is not `Copy`) usually take `self` by reference" +target/lintcheck/sources/rand-0.7.3/src/lib.rs:459:22 clippy::wrong_self_convention "methods with the following characteristics: (`to_*` and `self` type is not `Copy`) usually take `self` by reference" +target/lintcheck/sources/rand-0.7.3/src/lib.rs:459:22 clippy::wrong_self_convention "methods with the following characteristics: (`to_*` and `self` type is not `Copy`) usually take `self` by reference" +target/lintcheck/sources/rand-0.7.3/src/lib.rs:459:22 clippy::wrong_self_convention "methods with the following characteristics: (`to_*` and `self` type is not `Copy`) usually take `self` by reference" +target/lintcheck/sources/rand-0.7.3/src/lib.rs:459:22 clippy::wrong_self_convention "methods with the following characteristics: (`to_*` and `self` type is not `Copy`) usually take `self` by reference" +target/lintcheck/sources/rand-0.7.3/src/lib.rs:459:22 clippy::wrong_self_convention "methods with the following characteristics: (`to_*` and `self` type is not `Copy`) usually take `self` by reference" +target/lintcheck/sources/rand-0.7.3/src/lib.rs:489:22 clippy::wrong_self_convention "methods with the following characteristics: (`to_*` and `self` type is not `Copy`) usually take `self` by reference" +target/lintcheck/sources/rand-0.7.3/src/lib.rs:489:22 clippy::wrong_self_convention "methods with the following characteristics: (`to_*` and `self` type is not `Copy`) usually take `self` by reference" +target/lintcheck/sources/rand-0.7.3/src/lib.rs:489:22 clippy::wrong_self_convention "methods with the following characteristics: (`to_*` and `self` type is not `Copy`) usually take `self` by reference" +target/lintcheck/sources/rand-0.7.3/src/lib.rs:489:22 clippy::wrong_self_convention "methods with the following characteristics: (`to_*` and `self` type is not `Copy`) usually take `self` by reference" +target/lintcheck/sources/rand-0.7.3/src/lib.rs:489:22 clippy::wrong_self_convention "methods with the following characteristics: (`to_*` and `self` type is not `Copy`) usually take `self` by reference" +target/lintcheck/sources/rand-0.7.3/src/lib.rs:489:22 clippy::wrong_self_convention "methods with the following characteristics: (`to_*` and `self` type is not `Copy`) usually take `self` by reference" +target/lintcheck/sources/rand-0.7.3/src/lib.rs:489:22 clippy::wrong_self_convention "methods with the following characteristics: (`to_*` and `self` type is not `Copy`) usually take `self` by reference" +target/lintcheck/sources/rand-0.7.3/src/lib.rs:489:22 clippy::wrong_self_convention "methods with the following characteristics: (`to_*` and `self` type is not `Copy`) usually take `self` by reference" +target/lintcheck/sources/rand-0.7.3/src/lib.rs:489:22 clippy::wrong_self_convention "methods with the following characteristics: (`to_*` and `self` type is not `Copy`) usually take `self` by reference" +target/lintcheck/sources/rand-0.7.3/src/lib.rs:489:22 clippy::wrong_self_convention "methods with the following characteristics: (`to_*` and `self` type is not `Copy`) usually take `self` by reference" +target/lintcheck/sources/rand-0.7.3/src/lib.rs:489:22 clippy::wrong_self_convention "methods with the following characteristics: (`to_*` and `self` type is not `Copy`) usually take `self` by reference" +target/lintcheck/sources/rand-0.7.3/src/lib.rs:489:22 clippy::wrong_self_convention "methods with the following characteristics: (`to_*` and `self` type is not `Copy`) usually take `self` by reference" +target/lintcheck/sources/rand-0.7.3/src/lib.rs:489:22 clippy::wrong_self_convention "methods with the following characteristics: (`to_*` and `self` type is not `Copy`) usually take `self` by reference" +target/lintcheck/sources/rand-0.7.3/src/lib.rs:489:22 clippy::wrong_self_convention "methods with the following characteristics: (`to_*` and `self` type is not `Copy`) usually take `self` by reference" +target/lintcheck/sources/rand-0.7.3/src/lib.rs:489:22 clippy::wrong_self_convention "methods with the following characteristics: (`to_*` and `self` type is not `Copy`) usually take `self` by reference" +target/lintcheck/sources/rand-0.7.3/src/lib.rs:489:22 clippy::wrong_self_convention "methods with the following characteristics: (`to_*` and `self` type is not `Copy`) usually take `self` by reference" +target/lintcheck/sources/rand-0.7.3/src/lib.rs:489:22 clippy::wrong_self_convention "methods with the following characteristics: (`to_*` and `self` type is not `Copy`) usually take `self` by reference" +target/lintcheck/sources/rand-0.7.3/src/lib.rs:489:22 clippy::wrong_self_convention "methods with the following characteristics: (`to_*` and `self` type is not `Copy`) usually take `self` by reference" +target/lintcheck/sources/rand-0.7.3/src/lib.rs:489:22 clippy::wrong_self_convention "methods with the following characteristics: (`to_*` and `self` type is not `Copy`) usually take `self` by reference" +target/lintcheck/sources/rand-0.7.3/src/lib.rs:489:22 clippy::wrong_self_convention "methods with the following characteristics: (`to_*` and `self` type is not `Copy`) usually take `self` by reference" +target/lintcheck/sources/rand-0.7.3/src/lib.rs:489:22 clippy::wrong_self_convention "methods with the following characteristics: (`to_*` and `self` type is not `Copy`) usually take `self` by reference" +target/lintcheck/sources/rand-0.7.3/src/lib.rs:489:22 clippy::wrong_self_convention "methods with the following characteristics: (`to_*` and `self` type is not `Copy`) usually take `self` by reference" +target/lintcheck/sources/rand-0.7.3/src/lib.rs:489:22 clippy::wrong_self_convention "methods with the following characteristics: (`to_*` and `self` type is not `Copy`) usually take `self` by reference" +target/lintcheck/sources/rand-0.7.3/src/lib.rs:489:22 clippy::wrong_self_convention "methods with the following characteristics: (`to_*` and `self` type is not `Copy`) usually take `self` by reference" +target/lintcheck/sources/rand-0.7.3/src/lib.rs:489:22 clippy::wrong_self_convention "methods with the following characteristics: (`to_*` and `self` type is not `Copy`) usually take `self` by reference" +target/lintcheck/sources/rand-0.7.3/src/lib.rs:489:22 clippy::wrong_self_convention "methods with the following characteristics: (`to_*` and `self` type is not `Copy`) usually take `self` by reference" +target/lintcheck/sources/rand-0.7.3/src/lib.rs:489:22 clippy::wrong_self_convention "methods with the following characteristics: (`to_*` and `self` type is not `Copy`) usually take `self` by reference" +target/lintcheck/sources/rand-0.7.3/src/lib.rs:489:22 clippy::wrong_self_convention "methods with the following characteristics: (`to_*` and `self` type is not `Copy`) usually take `self` by reference" +target/lintcheck/sources/rand-0.7.3/src/lib.rs:489:22 clippy::wrong_self_convention "methods with the following characteristics: (`to_*` and `self` type is not `Copy`) usually take `self` by reference" +target/lintcheck/sources/rand-0.7.3/src/lib.rs:489:22 clippy::wrong_self_convention "methods with the following characteristics: (`to_*` and `self` type is not `Copy`) usually take `self` by reference" +target/lintcheck/sources/rand-0.7.3/src/lib.rs:489:22 clippy::wrong_self_convention "methods with the following characteristics: (`to_*` and `self` type is not `Copy`) usually take `self` by reference" +target/lintcheck/sources/rand-0.7.3/src/lib.rs:489:22 clippy::wrong_self_convention "methods with the following characteristics: (`to_*` and `self` type is not `Copy`) usually take `self` by reference" +target/lintcheck/sources/rand-0.7.3/src/lib.rs:489:22 clippy::wrong_self_convention "methods with the following characteristics: (`to_*` and `self` type is not `Copy`) usually take `self` by reference" +target/lintcheck/sources/rand-0.7.3/src/lib.rs:489:22 clippy::wrong_self_convention "methods with the following characteristics: (`to_*` and `self` type is not `Copy`) usually take `self` by reference" +target/lintcheck/sources/rand-0.7.3/src/lib.rs:489:22 clippy::wrong_self_convention "methods with the following characteristics: (`to_*` and `self` type is not `Copy`) usually take `self` by reference" +target/lintcheck/sources/rand-0.7.3/src/lib.rs:489:22 clippy::wrong_self_convention "methods with the following characteristics: (`to_*` and `self` type is not `Copy`) usually take `self` by reference" +target/lintcheck/sources/rand-0.7.3/src/lib.rs:489:22 clippy::wrong_self_convention "methods with the following characteristics: (`to_*` and `self` type is not `Copy`) usually take `self` by reference" +target/lintcheck/sources/rand-0.7.3/src/lib.rs:489:22 clippy::wrong_self_convention "methods with the following characteristics: (`to_*` and `self` type is not `Copy`) usually take `self` by reference" +target/lintcheck/sources/rand-0.7.3/src/lib.rs:489:22 clippy::wrong_self_convention "methods with the following characteristics: (`to_*` and `self` type is not `Copy`) usually take `self` by reference" +target/lintcheck/sources/rand-0.7.3/src/lib.rs:489:22 clippy::wrong_self_convention "methods with the following characteristics: (`to_*` and `self` type is not `Copy`) usually take `self` by reference" +target/lintcheck/sources/rand-0.7.3/src/lib.rs:552:1 clippy::must_use_candidate "this function could have a `#[must_use]` attribute" +target/lintcheck/sources/rand-0.7.3/src/rngs/adapter/read.rs:47:1 clippy::module_name_repetitions "item name starts with its containing module's name" +target/lintcheck/sources/rand-0.7.3/src/rngs/adapter/read.rs:89:1 clippy::module_name_repetitions "item name starts with its containing module's name" +target/lintcheck/sources/rand-0.7.3/src/rngs/adapter/reseeding.rs:100:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/rand-0.7.3/src/rngs/adapter/reseeding.rs:112:5 clippy::inline_always "you have declared `#[inline(always)]` on `next_u32`. This is usually a bad idea" +target/lintcheck/sources/rand-0.7.3/src/rngs/adapter/reseeding.rs:117:5 clippy::inline_always "you have declared `#[inline(always)]` on `next_u64`. This is usually a bad idea" +target/lintcheck/sources/rand-0.7.3/src/rngs/adapter/reseeding.rs:198:13 clippy::cast_possible_wrap "casting `u64` to `i64` may wrap around the value" +target/lintcheck/sources/rand-0.7.3/src/rngs/adapter/reseeding.rs:231:9 clippy::cast_possible_wrap "casting `usize` to `isize` may wrap around the value" +target/lintcheck/sources/rand-0.7.3/src/rngs/adapter/reseeding.rs:249:13 clippy::let_underscore_drop "non-binding `let` on a type that implements `Drop`" +target/lintcheck/sources/rand-0.7.3/src/rngs/adapter/reseeding.rs:27:28 clippy::doc_markdown "you should put `ChaCha` between ticks in the documentation" +target/lintcheck/sources/rand-0.7.3/src/rngs/adapter/reseeding.rs:79:1 clippy::module_name_repetitions "item name starts with its containing module's name" +target/lintcheck/sources/rand-0.7.3/src/rngs/entropy.rs:24:1 clippy::module_name_repetitions "item name starts with its containing module's name" +target/lintcheck/sources/rand-0.7.3/src/rngs/entropy.rs:34:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/rand-0.7.3/src/rngs/mock.rs:36:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/rand-0.7.3/src/rngs/mock.rs:47:9 clippy::cast_possible_truncation "casting `u64` to `u32` may truncate the value" +target/lintcheck/sources/rand-0.7.3/src/rngs/mod.rs:61:74 clippy::doc_markdown "you should put `ChaCha20` between ticks in the documentation" +target/lintcheck/sources/rand-0.7.3/src/rngs/std.rs:25:39 clippy::doc_markdown "you should put `ChaCha` between ticks in the documentation" +target/lintcheck/sources/rand-0.7.3/src/rngs/std.rs:32:10 clippy::doc_markdown "you should put `rand_chacha` between ticks in the documentation" +target/lintcheck/sources/rand-0.7.3/src/rngs/std.rs:36:1 clippy::module_name_repetitions "item name starts with its containing module's name" +target/lintcheck/sources/rand-0.7.3/src/rngs/std.rs:39:5 clippy::inline_always "you have declared `#[inline(always)]` on `next_u32`. This is usually a bad idea" +target/lintcheck/sources/rand-0.7.3/src/rngs/std.rs:44:5 clippy::inline_always "you have declared `#[inline(always)]` on `next_u64`. This is usually a bad idea" +target/lintcheck/sources/rand-0.7.3/src/rngs/std.rs:49:5 clippy::inline_always "you have declared `#[inline(always)]` on `fill_bytes`. This is usually a bad idea" +target/lintcheck/sources/rand-0.7.3/src/rngs/std.rs:54:5 clippy::inline_always "you have declared `#[inline(always)]` on `try_fill_bytes`. This is usually a bad idea" +target/lintcheck/sources/rand-0.7.3/src/rngs/std.rs:63:5 clippy::inline_always "you have declared `#[inline(always)]` on `from_seed`. This is usually a bad idea" +target/lintcheck/sources/rand-0.7.3/src/rngs/std.rs:68:5 clippy::inline_always "you have declared `#[inline(always)]` on `from_rng`. This is usually a bad idea" +target/lintcheck/sources/rand-0.7.3/src/rngs/thread.rs:57:1 clippy::module_name_repetitions "item name starts with its containing module's name" +target/lintcheck/sources/rand-0.7.3/src/rngs/thread.rs:80:1 clippy::missing_panics_doc "docs for function which may panic missing `# Panics` section" +target/lintcheck/sources/rand-0.7.3/src/rngs/thread.rs:80:1 clippy::module_name_repetitions "item name starts with its containing module's name" +target/lintcheck/sources/rand-0.7.3/src/rngs/thread.rs:80:1 clippy::must_use_candidate "this function could have a `#[must_use]` attribute" +target/lintcheck/sources/rand-0.7.3/src/rngs/thread.rs:81:35 clippy::redundant_closure_for_method_calls "redundant closure" +target/lintcheck/sources/rand-0.7.3/src/rngs/thread.rs:93:5 clippy::inline_always "you have declared `#[inline(always)]` on `next_u32`. This is usually a bad idea" +target/lintcheck/sources/rand-0.7.3/src/rngs/thread.rs:98:5 clippy::inline_always "you have declared `#[inline(always)]` on `next_u64`. This is usually a bad idea" +target/lintcheck/sources/rand-0.7.3/src/seq/index.rs:127:1 clippy::module_name_repetitions "item name starts with its containing module's name" +target/lintcheck/sources/rand-0.7.3/src/seq/index.rs:139:13 clippy::enum_glob_use "usage of wildcard import for enum variants" +target/lintcheck/sources/rand-0.7.3/src/seq/index.rs:159:1 clippy::module_name_repetitions "item name starts with its containing module's name" +target/lintcheck/sources/rand-0.7.3/src/seq/index.rs:171:13 clippy::enum_glob_use "usage of wildcard import for enum variants" +target/lintcheck/sources/rand-0.7.3/src/seq/index.rs:180:13 clippy::enum_glob_use "usage of wildcard import for enum variants" +target/lintcheck/sources/rand-0.7.3/src/seq/index.rs:223:18 clippy::cast_possible_truncation "casting `usize` to `u32` may truncate the value on targets with 64-bit wide pointers" +target/lintcheck/sources/rand-0.7.3/src/seq/index.rs:224:18 clippy::cast_possible_truncation "casting `usize` to `u32` may truncate the value on targets with 64-bit wide pointers" +target/lintcheck/sources/rand-0.7.3/src/seq/index.rs:233:25 clippy::cast_precision_loss "casting `u32` to `f32` causes a loss of precision (`u32` is 32 bits wide, but `f32`'s mantissa is only 23 bits wide)" +target/lintcheck/sources/rand-0.7.3/src/seq/index.rs:236:27 clippy::cast_precision_loss "casting `u32` to `f32` causes a loss of precision (`u32` is 32 bits wide, but `f32`'s mantissa is only 23 bits wide)" +target/lintcheck/sources/rand-0.7.3/src/seq/index.rs:244:12 clippy::cast_precision_loss "casting `u32` to `f32` causes a loss of precision (`u32` is 32 bits wide, but `f32`'s mantissa is only 23 bits wide)" +target/lintcheck/sources/rand-0.7.3/src/seq/index.rs:244:37 clippy::cast_precision_loss "casting `u32` to `f32` causes a loss of precision (`u32` is 32 bits wide, but `f32`'s mantissa is only 23 bits wide)" +target/lintcheck/sources/rand-0.7.3/src/seq/index.rs:29:1 clippy::module_name_repetitions "item name starts with its containing module's name" +target/lintcheck/sources/rand-0.7.3/src/seq/index.rs:39:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/rand-0.7.3/src/seq/index.rs:48:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/rand-0.7.3/src/seq/index.rs:60:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/rand-0.7.3/src/seq/index.rs:69:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/rand-0.7.3/src/seq/index.rs:78:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/rand-0.7.3/src/seq/index.rs:87:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/rand-0.7.3/src/seq/index.rs:87:5 clippy::should_implement_trait "method `into_iter` can be confused for the standard trait method `std::iter::IntoIterator::into_iter`" +target/lintcheck/sources/rand-0.7.3/src/seq/index.rs:97:13 clippy::enum_glob_use "usage of wildcard import for enum variants" +target/lintcheck/sources/rand-0.7.3/src/seq/mod.rs:141:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/rand-0.7.3/src/seq/mod.rs:168:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/rand-0.7.3/src/seq/mod.rs:229:4 clippy::needless_doctest_main "needless `fn main` in doctest" +target/lintcheck/sources/rand-0.7.3/src/seq/mod.rs:292:29 clippy::cast_precision_loss "casting `usize` to `f64` causes a loss of precision on targets with 64-bit wide pointers (`usize` is 64 bits wide, but `f64`'s mantissa is only 52 bits wide)" +target/lintcheck/sources/rand-0.7.3/src/seq/mod.rs:410:23 clippy::default_trait_access "calling `std::marker::PhantomData::default()` is more clear than this expression" +target/lintcheck/sources/rand-0.7.3/src/seq/mod.rs:45:4 clippy::needless_doctest_main "needless `fn main` in doctest" +target/lintcheck/sources/rand-0.7.3/src/seq/mod.rs:527:26 clippy::cast_possible_truncation "casting `usize` to `u32` may truncate the value on targets with 64-bit wide pointers" +target/lintcheck/sources/rand_core-0.6.0/src/block.rs:117:1 clippy::module_name_repetitions "item name starts with its containing module's name" +target/lintcheck/sources/rand_core-0.6.0/src/block.rs:153:5 clippy::inline_always "you have declared `#[inline(always)]` on `index`. This is usually a bad idea" +target/lintcheck/sources/rand_core-0.6.0/src/block.rs:230:5 clippy::inline_always "you have declared `#[inline(always)]` on `try_fill_bytes`. This is usually a bad idea" +target/lintcheck/sources/rand_core-0.6.0/src/block.rs:240:5 clippy::inline_always "you have declared `#[inline(always)]` on `from_seed`. This is usually a bad idea" +target/lintcheck/sources/rand_core-0.6.0/src/block.rs:245:5 clippy::inline_always "you have declared `#[inline(always)]` on `seed_from_u64`. This is usually a bad idea" +target/lintcheck/sources/rand_core-0.6.0/src/block.rs:250:5 clippy::inline_always "you have declared `#[inline(always)]` on `from_rng`. This is usually a bad idea" +target/lintcheck/sources/rand_core-0.6.0/src/block.rs:280:1 clippy::module_name_repetitions "item name starts with its containing module's name" +target/lintcheck/sources/rand_core-0.6.0/src/block.rs:319:5 clippy::inline_always "you have declared `#[inline(always)]` on `index`. This is usually a bad idea" +target/lintcheck/sources/rand_core-0.6.0/src/block.rs:405:5 clippy::inline_always "you have declared `#[inline(always)]` on `try_fill_bytes`. This is usually a bad idea" +target/lintcheck/sources/rand_core-0.6.0/src/block.rs:415:5 clippy::inline_always "you have declared `#[inline(always)]` on `from_seed`. This is usually a bad idea" +target/lintcheck/sources/rand_core-0.6.0/src/block.rs:420:5 clippy::inline_always "you have declared `#[inline(always)]` on `seed_from_u64`. This is usually a bad idea" +target/lintcheck/sources/rand_core-0.6.0/src/block.rs:425:5 clippy::inline_always "you have declared `#[inline(always)]` on `from_rng`. This is usually a bad idea" +target/lintcheck/sources/rand_core-0.6.0/src/block.rs:67:14 clippy::doc_markdown "you should put `module][crate::block` between ticks in the documentation" +target/lintcheck/sources/rand_core-0.6.0/src/block.rs:68:1 clippy::module_name_repetitions "item name starts with its containing module's name" +target/lintcheck/sources/rand_core-0.6.0/src/error.rs:106:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/rand_core-0.6.0/src/error.rs:87:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/rand_core-0.6.0/src/error.rs:95:74 clippy::cast_possible_wrap "casting `u32` to `i32` may wrap around the value" +target/lintcheck/sources/rand_core-0.6.0/src/lib.rs:179:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/rand_core-0.6.0/src/lib.rs:301:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/rand_core-0.6.0/src/lib.rs:303:26 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/rand_core-0.6.0/src/lib.rs:304:26 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/rand_core-0.6.0/src/lib.rs:313:30 clippy::cast_possible_truncation "casting `u64` to `u32` may truncate the value" +target/lintcheck/sources/rand_core-0.6.0/src/lib.rs:314:23 clippy::cast_possible_truncation "casting `u64` to `u32` may truncate the value" +target/lintcheck/sources/rand_core-0.6.0/src/lib.rs:346:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/rand_core-0.6.0/src/lib.rs:381:5 clippy::inline_always "you have declared `#[inline(always)]` on `next_u32`. This is usually a bad idea" +target/lintcheck/sources/rand_core-0.6.0/src/lib.rs:386:5 clippy::inline_always "you have declared `#[inline(always)]` on `next_u64`. This is usually a bad idea" +target/lintcheck/sources/rand_core-0.6.0/src/lib.rs:391:5 clippy::inline_always "you have declared `#[inline(always)]` on `fill_bytes`. This is usually a bad idea" +target/lintcheck/sources/rand_core-0.6.0/src/lib.rs:396:5 clippy::inline_always "you have declared `#[inline(always)]` on `try_fill_bytes`. This is usually a bad idea" +target/lintcheck/sources/rayon-1.5.0/src/collections/binary_heap.rs:7:5 clippy::wildcard_imports "usage of wildcard import" +target/lintcheck/sources/rayon-1.5.0/src/collections/binary_heap.rs:8:5 clippy::wildcard_imports "usage of wildcard import" +target/lintcheck/sources/rayon-1.5.0/src/collections/btree_map.rs:7:5 clippy::wildcard_imports "usage of wildcard import" +target/lintcheck/sources/rayon-1.5.0/src/collections/btree_map.rs:8:5 clippy::wildcard_imports "usage of wildcard import" +target/lintcheck/sources/rayon-1.5.0/src/collections/btree_set.rs:7:5 clippy::wildcard_imports "usage of wildcard import" +target/lintcheck/sources/rayon-1.5.0/src/collections/btree_set.rs:8:5 clippy::wildcard_imports "usage of wildcard import" +target/lintcheck/sources/rayon-1.5.0/src/collections/hash_map.rs:10:5 clippy::wildcard_imports "usage of wildcard import" +target/lintcheck/sources/rayon-1.5.0/src/collections/hash_map.rs:9:5 clippy::wildcard_imports "usage of wildcard import" +target/lintcheck/sources/rayon-1.5.0/src/collections/hash_set.rs:10:5 clippy::wildcard_imports "usage of wildcard import" +target/lintcheck/sources/rayon-1.5.0/src/collections/hash_set.rs:9:5 clippy::wildcard_imports "usage of wildcard import" +target/lintcheck/sources/rayon-1.5.0/src/collections/linked_list.rs:7:5 clippy::wildcard_imports "usage of wildcard import" +target/lintcheck/sources/rayon-1.5.0/src/collections/linked_list.rs:8:5 clippy::wildcard_imports "usage of wildcard import" +target/lintcheck/sources/rayon-1.5.0/src/collections/mod.rs:59:32 clippy::mem_replace_with_default "replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take`" +target/lintcheck/sources/rayon-1.5.0/src/collections/mod.rs:68:40 clippy::mem_replace_with_default "replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take`" +target/lintcheck/sources/rayon-1.5.0/src/collections/vec_deque.rs:8:5 clippy::wildcard_imports "usage of wildcard import" +target/lintcheck/sources/rayon-1.5.0/src/collections/vec_deque.rs:9:5 clippy::wildcard_imports "usage of wildcard import" +target/lintcheck/sources/rayon-1.5.0/src/compile_fail/cannot_collect_filtermap_data.rs:2:1 clippy::needless_doctest_main "needless `fn main` in doctest" +target/lintcheck/sources/rayon-1.5.0/src/compile_fail/cannot_zip_filtered_data.rs:2:1 clippy::needless_doctest_main "needless `fn main` in doctest" +target/lintcheck/sources/rayon-1.5.0/src/compile_fail/cell_par_iter.rs:2:1 clippy::needless_doctest_main "needless `fn main` in doctest" +target/lintcheck/sources/rayon-1.5.0/src/compile_fail/no_send_par_iter.rs:25:1 clippy::needless_doctest_main "needless `fn main` in doctest" +target/lintcheck/sources/rayon-1.5.0/src/compile_fail/no_send_par_iter.rs:46:1 clippy::needless_doctest_main "needless `fn main` in doctest" +target/lintcheck/sources/rayon-1.5.0/src/compile_fail/no_send_par_iter.rs:4:1 clippy::needless_doctest_main "needless `fn main` in doctest" +target/lintcheck/sources/rayon-1.5.0/src/compile_fail/rc_par_iter.rs:2:1 clippy::needless_doctest_main "needless `fn main` in doctest" +target/lintcheck/sources/rayon-1.5.0/src/iter/chain.rs:103:9 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope" +target/lintcheck/sources/rayon-1.5.0/src/iter/chain.rs:122:9 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope" +target/lintcheck/sources/rayon-1.5.0/src/iter/chain.rs:128:9 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope" +target/lintcheck/sources/rayon-1.5.0/src/iter/chain.rs:1:5 clippy::wildcard_imports "usage of wildcard import" +target/lintcheck/sources/rayon-1.5.0/src/iter/chain.rs:221:36 clippy::doc_markdown "you should put `ExactSizeIterator` between ticks in the documentation" +target/lintcheck/sources/rayon-1.5.0/src/iter/chain.rs:2:5 clippy::wildcard_imports "usage of wildcard import" +target/lintcheck/sources/rayon-1.5.0/src/iter/chain.rs:51:38 clippy::option_if_let_else "use Option::map_or_else instead of an if let/else" +target/lintcheck/sources/rayon-1.5.0/src/iter/chain.rs:58:14 clippy::shadow_unrelated "`a` is being shadowed" +target/lintcheck/sources/rayon-1.5.0/src/iter/chain.rs:58:17 clippy::shadow_unrelated "`b` is being shadowed" +target/lintcheck/sources/rayon-1.5.0/src/iter/chain.rs:78:14 clippy::shadow_unrelated "`a` is being shadowed" +target/lintcheck/sources/rayon-1.5.0/src/iter/chain.rs:78:17 clippy::shadow_unrelated "`b` is being shadowed" +target/lintcheck/sources/rayon-1.5.0/src/iter/chain.rs:97:9 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope" +target/lintcheck/sources/rayon-1.5.0/src/iter/chunks.rs:29:9 clippy::inconsistent_struct_constructor "struct constructor field order is inconsistent with struct definition field order" +target/lintcheck/sources/rayon-1.5.0/src/iter/chunks.rs:3:5 clippy::wildcard_imports "usage of wildcard import" +target/lintcheck/sources/rayon-1.5.0/src/iter/chunks.rs:4:5 clippy::wildcard_imports "usage of wildcard import" +target/lintcheck/sources/rayon-1.5.0/src/iter/chunks.rs:77:9 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope" +target/lintcheck/sources/rayon-1.5.0/src/iter/chunks.rs:83:9 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope" +target/lintcheck/sources/rayon-1.5.0/src/iter/cloned.rs:1:5 clippy::wildcard_imports "usage of wildcard import" +target/lintcheck/sources/rayon-1.5.0/src/iter/cloned.rs:2:5 clippy::wildcard_imports "usage of wildcard import" +target/lintcheck/sources/rayon-1.5.0/src/iter/cloned.rs:71:9 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope" +target/lintcheck/sources/rayon-1.5.0/src/iter/cloned.rs:75:9 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope" +target/lintcheck/sources/rayon-1.5.0/src/iter/collect/consumer.rs:141:5 clippy::doc_markdown "you should put `CollectReducer` between ticks in the documentation" +target/lintcheck/sources/rayon-1.5.0/src/iter/collect/consumer.rs:1:5 clippy::wildcard_imports "usage of wildcard import" +target/lintcheck/sources/rayon-1.5.0/src/iter/collect/consumer.rs:28:5 clippy::doc_markdown "you should put `CollectResult` between ticks in the documentation" +target/lintcheck/sources/rayon-1.5.0/src/iter/collect/consumer.rs:36:37 clippy::mut_mut "generally you want to avoid `&mut &mut _` if possible" +target/lintcheck/sources/rayon-1.5.0/src/iter/collect/consumer.rs:36:37 clippy::mut_mut "generally you want to avoid `&mut &mut _` if possible" +target/lintcheck/sources/rayon-1.5.0/src/iter/collect/mod.rs:154:9 clippy::single_match_else "you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let`" +target/lintcheck/sources/rayon-1.5.0/src/iter/copied.rs:1:5 clippy::wildcard_imports "usage of wildcard import" +target/lintcheck/sources/rayon-1.5.0/src/iter/copied.rs:2:5 clippy::wildcard_imports "usage of wildcard import" +target/lintcheck/sources/rayon-1.5.0/src/iter/copied.rs:71:9 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope" +target/lintcheck/sources/rayon-1.5.0/src/iter/copied.rs:75:9 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope" +target/lintcheck/sources/rayon-1.5.0/src/iter/empty.rs:1:5 clippy::wildcard_imports "usage of wildcard import" +target/lintcheck/sources/rayon-1.5.0/src/iter/empty.rs:24:1 clippy::must_use_candidate "this function could have a `#[must_use]` attribute" +target/lintcheck/sources/rayon-1.5.0/src/iter/empty.rs:2:5 clippy::wildcard_imports "usage of wildcard import" +target/lintcheck/sources/rayon-1.5.0/src/iter/enumerate.rs:1:5 clippy::wildcard_imports "usage of wildcard import" +target/lintcheck/sources/rayon-1.5.0/src/iter/enumerate.rs:2:5 clippy::wildcard_imports "usage of wildcard import" +target/lintcheck/sources/rayon-1.5.0/src/iter/enumerate.rs:64:9 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope" +target/lintcheck/sources/rayon-1.5.0/src/iter/enumerate.rs:68:9 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope" +target/lintcheck/sources/rayon-1.5.0/src/iter/extend.rs:143:63 clippy::linkedlist "you seem to be using a `LinkedList`! Perhaps you meant some other data structure?" +target/lintcheck/sources/rayon-1.5.0/src/iter/extend.rs:182:57 clippy::linkedlist "you seem to be using a `LinkedList`! Perhaps you meant some other data structure?" +target/lintcheck/sources/rayon-1.5.0/src/iter/extend.rs:218:32 clippy::linkedlist "you seem to be using a `LinkedList`! Perhaps you meant some other data structure?" +target/lintcheck/sources/rayon-1.5.0/src/iter/extend.rs:218:59 clippy::linkedlist "you seem to be using a `LinkedList`! Perhaps you meant some other data structure?" +target/lintcheck/sources/rayon-1.5.0/src/iter/extend.rs:25:42 clippy::linkedlist "you seem to be using a `LinkedList`! Perhaps you meant some other data structure?" +target/lintcheck/sources/rayon-1.5.0/src/iter/extend.rs:287:62 clippy::linkedlist "you seem to be using a `LinkedList`! Perhaps you meant some other data structure?" +target/lintcheck/sources/rayon-1.5.0/src/iter/extend.rs:322:56 clippy::linkedlist "you seem to be using a `LinkedList`! Perhaps you meant some other data structure?" +target/lintcheck/sources/rayon-1.5.0/src/iter/extend.rs:41:27 clippy::linkedlist "you seem to be using a `LinkedList`! Perhaps you meant some other data structure?" +target/lintcheck/sources/rayon-1.5.0/src/iter/extend.rs:47:30 clippy::linkedlist "you seem to be using a `LinkedList`! Perhaps you meant some other data structure?" +target/lintcheck/sources/rayon-1.5.0/src/iter/extend.rs:47:56 clippy::linkedlist "you seem to be using a `LinkedList`! Perhaps you meant some other data structure?" +target/lintcheck/sources/rayon-1.5.0/src/iter/extend.rs:47:74 clippy::linkedlist "you seem to be using a `LinkedList`! Perhaps you meant some other data structure?" +target/lintcheck/sources/rayon-1.5.0/src/iter/extend.rs:53:29 clippy::linkedlist "you seem to be using a `LinkedList`! Perhaps you meant some other data structure?" +target/lintcheck/sources/rayon-1.5.0/src/iter/extend.rs:57:36 clippy::linkedlist "you seem to be using a `LinkedList`! Perhaps you meant some other data structure?" +target/lintcheck/sources/rayon-1.5.0/src/iter/extend.rs:59:61 clippy::linkedlist "you seem to be using a `LinkedList`! Perhaps you meant some other data structure?" +target/lintcheck/sources/rayon-1.5.0/src/iter/filter.rs:1:5 clippy::wildcard_imports "usage of wildcard import" +target/lintcheck/sources/rayon-1.5.0/src/iter/filter.rs:2:5 clippy::wildcard_imports "usage of wildcard import" +target/lintcheck/sources/rayon-1.5.0/src/iter/filter_map.rs:123:9 clippy::option_if_let_else "use Option::map_or instead of an if let/else" +target/lintcheck/sources/rayon-1.5.0/src/iter/filter_map.rs:1:5 clippy::wildcard_imports "usage of wildcard import" +target/lintcheck/sources/rayon-1.5.0/src/iter/filter_map.rs:2:5 clippy::wildcard_imports "usage of wildcard import" +target/lintcheck/sources/rayon-1.5.0/src/iter/find.rs:1:5 clippy::wildcard_imports "usage of wildcard import" +target/lintcheck/sources/rayon-1.5.0/src/iter/find.rs:2:5 clippy::wildcard_imports "usage of wildcard import" +target/lintcheck/sources/rayon-1.5.0/src/iter/find_first_last/mod.rs:1:5 clippy::wildcard_imports "usage of wildcard import" +target/lintcheck/sources/rayon-1.5.0/src/iter/find_first_last/mod.rs:2:5 clippy::wildcard_imports "usage of wildcard import" +target/lintcheck/sources/rayon-1.5.0/src/iter/find_first_last/mod.rs:32:67 clippy::doc_markdown "you should put `MatchPosition` between ticks in the documentation" +target/lintcheck/sources/rayon-1.5.0/src/iter/flat_map.rs:1:5 clippy::wildcard_imports "usage of wildcard import" +target/lintcheck/sources/rayon-1.5.0/src/iter/flat_map.rs:2:5 clippy::wildcard_imports "usage of wildcard import" +target/lintcheck/sources/rayon-1.5.0/src/iter/flat_map_iter.rs:1:5 clippy::wildcard_imports "usage of wildcard import" +target/lintcheck/sources/rayon-1.5.0/src/iter/flat_map_iter.rs:2:5 clippy::wildcard_imports "usage of wildcard import" +target/lintcheck/sources/rayon-1.5.0/src/iter/flatten.rs:1:5 clippy::wildcard_imports "usage of wildcard import" +target/lintcheck/sources/rayon-1.5.0/src/iter/flatten.rs:2:5 clippy::wildcard_imports "usage of wildcard import" +target/lintcheck/sources/rayon-1.5.0/src/iter/flatten_iter.rs:1:5 clippy::wildcard_imports "usage of wildcard import" +target/lintcheck/sources/rayon-1.5.0/src/iter/flatten_iter.rs:2:5 clippy::wildcard_imports "usage of wildcard import" +target/lintcheck/sources/rayon-1.5.0/src/iter/fold.rs:158:13 clippy::similar_names "binding's name is too similar to existing binding" +target/lintcheck/sources/rayon-1.5.0/src/iter/fold.rs:1:5 clippy::wildcard_imports "usage of wildcard import" +target/lintcheck/sources/rayon-1.5.0/src/iter/fold.rs:204:1 clippy::module_name_repetitions "item name starts with its containing module's name" +target/lintcheck/sources/rayon-1.5.0/src/iter/fold.rs:2:5 clippy::wildcard_imports "usage of wildcard import" +target/lintcheck/sources/rayon-1.5.0/src/iter/for_each.rs:1:5 clippy::wildcard_imports "usage of wildcard import" +target/lintcheck/sources/rayon-1.5.0/src/iter/for_each.rs:2:5 clippy::wildcard_imports "usage of wildcard import" +target/lintcheck/sources/rayon-1.5.0/src/iter/inspect.rs:1:5 clippy::wildcard_imports "usage of wildcard import" +target/lintcheck/sources/rayon-1.5.0/src/iter/inspect.rs:2:5 clippy::wildcard_imports "usage of wildcard import" +target/lintcheck/sources/rayon-1.5.0/src/iter/inspect.rs:83:9 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope" +target/lintcheck/sources/rayon-1.5.0/src/iter/inspect.rs:88:9 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope" +target/lintcheck/sources/rayon-1.5.0/src/iter/interleave.rs:111:9 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope" +target/lintcheck/sources/rayon-1.5.0/src/iter/interleave.rs:119:9 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope" +target/lintcheck/sources/rayon-1.5.0/src/iter/interleave.rs:195:30 clippy::doc_markdown "you should put `self.i_len` between ticks in the documentation" +target/lintcheck/sources/rayon-1.5.0/src/iter/interleave.rs:195:43 clippy::doc_markdown "you should put `self.j_len` between ticks in the documentation" +target/lintcheck/sources/rayon-1.5.0/src/iter/interleave.rs:199:23 clippy::doc_markdown "you should put `self.i_len` between ticks in the documentation" +target/lintcheck/sources/rayon-1.5.0/src/iter/interleave.rs:1:5 clippy::wildcard_imports "usage of wildcard import" +target/lintcheck/sources/rayon-1.5.0/src/iter/interleave.rs:200:23 clippy::doc_markdown "you should put `self.j_len` between ticks in the documentation" +target/lintcheck/sources/rayon-1.5.0/src/iter/interleave.rs:249:41 clippy::doc_markdown "you should put `DoubleEndedIterator` between ticks in the documentation" +target/lintcheck/sources/rayon-1.5.0/src/iter/interleave.rs:250:5 clippy::doc_markdown "you should put `ExactSizeIterator` between ticks in the documentation" +target/lintcheck/sources/rayon-1.5.0/src/iter/interleave.rs:263:33 clippy::doc_markdown "you should put `InterleaveSeq` between ticks in the documentation" +target/lintcheck/sources/rayon-1.5.0/src/iter/interleave.rs:2:5 clippy::wildcard_imports "usage of wildcard import" +target/lintcheck/sources/rayon-1.5.0/src/iter/interleave.rs:313:9 clippy::comparison_chain "`if` chain can be rewritten with `match`" +target/lintcheck/sources/rayon-1.5.0/src/iter/interleave.rs:82:9 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope" +target/lintcheck/sources/rayon-1.5.0/src/iter/interleave.rs:90:9 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope" +target/lintcheck/sources/rayon-1.5.0/src/iter/interleave_shortest.rs:1:5 clippy::wildcard_imports "usage of wildcard import" +target/lintcheck/sources/rayon-1.5.0/src/iter/intersperse.rs:1:5 clippy::wildcard_imports "usage of wildcard import" +target/lintcheck/sources/rayon-1.5.0/src/iter/intersperse.rs:2:5 clippy::wildcard_imports "usage of wildcard import" +target/lintcheck/sources/rayon-1.5.0/src/iter/intersperse.rs:90:9 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope" +target/lintcheck/sources/rayon-1.5.0/src/iter/intersperse.rs:96:9 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope" +target/lintcheck/sources/rayon-1.5.0/src/iter/len.rs:12:1 clippy::module_name_repetitions "item name ends with its containing module's name" +target/lintcheck/sources/rayon-1.5.0/src/iter/len.rs:146:1 clippy::module_name_repetitions "item name ends with its containing module's name" +target/lintcheck/sources/rayon-1.5.0/src/iter/len.rs:1:5 clippy::wildcard_imports "usage of wildcard import" +target/lintcheck/sources/rayon-1.5.0/src/iter/len.rs:200:9 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope" +target/lintcheck/sources/rayon-1.5.0/src/iter/len.rs:205:9 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope" +target/lintcheck/sources/rayon-1.5.0/src/iter/len.rs:2:5 clippy::wildcard_imports "usage of wildcard import" +target/lintcheck/sources/rayon-1.5.0/src/iter/len.rs:66:9 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope" +target/lintcheck/sources/rayon-1.5.0/src/iter/len.rs:71:9 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope" +target/lintcheck/sources/rayon-1.5.0/src/iter/map.rs:1:5 clippy::wildcard_imports "usage of wildcard import" +target/lintcheck/sources/rayon-1.5.0/src/iter/map.rs:2:5 clippy::wildcard_imports "usage of wildcard import" +target/lintcheck/sources/rayon-1.5.0/src/iter/map.rs:84:9 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope" +target/lintcheck/sources/rayon-1.5.0/src/iter/map.rs:89:9 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope" +target/lintcheck/sources/rayon-1.5.0/src/iter/map_with.rs:1:5 clippy::wildcard_imports "usage of wildcard import" +target/lintcheck/sources/rayon-1.5.0/src/iter/map_with.rs:2:5 clippy::wildcard_imports "usage of wildcard import" +target/lintcheck/sources/rayon-1.5.0/src/iter/map_with.rs:419:9 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope" +target/lintcheck/sources/rayon-1.5.0/src/iter/map_with.rs:425:9 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope" +target/lintcheck/sources/rayon-1.5.0/src/iter/map_with.rs:90:9 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope" +target/lintcheck/sources/rayon-1.5.0/src/iter/map_with.rs:96:9 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope" +target/lintcheck/sources/rayon-1.5.0/src/iter/mod.rs:1874:24 clippy::trivially_copy_pass_by_ref "this argument (1 byte) is passed by reference, but would be more efficient if passed by value (limit: 8 byte)" +target/lintcheck/sources/rayon-1.5.0/src/iter/mod.rs:2171:1 clippy::len_without_is_empty "trait `IndexedParallelIterator` has a `len` method but no (possibly inherited) `is_empty` method" +target/lintcheck/sources/rayon-1.5.0/src/iter/mod.rs:2371:26 clippy::trivially_copy_pass_by_ref "this argument (1 byte) is passed by reference, but would be more efficient if passed by value (limit: 8 byte)" +target/lintcheck/sources/rayon-1.5.0/src/iter/mod.rs:2411:26 clippy::trivially_copy_pass_by_ref "this argument (1 byte) is passed by reference, but would be more efficient if passed by value (limit: 8 byte)" +target/lintcheck/sources/rayon-1.5.0/src/iter/mod.rs:82:5 clippy::wildcard_imports "usage of wildcard import" +target/lintcheck/sources/rayon-1.5.0/src/iter/multizip.rs:1:5 clippy::wildcard_imports "usage of wildcard import" +target/lintcheck/sources/rayon-1.5.0/src/iter/multizip.rs:2:5 clippy::wildcard_imports "usage of wildcard import" +target/lintcheck/sources/rayon-1.5.0/src/iter/noop.rs:1:5 clippy::wildcard_imports "usage of wildcard import" +target/lintcheck/sources/rayon-1.5.0/src/iter/once.rs:1:5 clippy::wildcard_imports "usage of wildcard import" +target/lintcheck/sources/rayon-1.5.0/src/iter/once.rs:2:5 clippy::wildcard_imports "usage of wildcard import" +target/lintcheck/sources/rayon-1.5.0/src/iter/panic_fuse.rs:102:9 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope" +target/lintcheck/sources/rayon-1.5.0/src/iter/panic_fuse.rs:1:5 clippy::wildcard_imports "usage of wildcard import" +target/lintcheck/sources/rayon-1.5.0/src/iter/panic_fuse.rs:2:5 clippy::wildcard_imports "usage of wildcard import" +target/lintcheck/sources/rayon-1.5.0/src/iter/panic_fuse.rs:98:9 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope" +target/lintcheck/sources/rayon-1.5.0/src/iter/par_bridge.rs:136:28 clippy::redundant_else "redundant else block" +target/lintcheck/sources/rayon-1.5.0/src/iter/par_bridge.rs:163:28 clippy::redundant_else "redundant else block" +target/lintcheck/sources/rayon-1.5.0/src/iter/plumbing/mod.rs:216:58 clippy::doc_markdown "you should put `find_first` between ticks in the documentation" +target/lintcheck/sources/rayon-1.5.0/src/iter/plumbing/mod.rs:359:5 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope" +target/lintcheck/sources/rayon-1.5.0/src/iter/plumbing/mod.rs:364:5 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope" +target/lintcheck/sources/rayon-1.5.0/src/iter/plumbing/mod.rs:399:5 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope" +target/lintcheck/sources/rayon-1.5.0/src/iter/plumbing/mod.rs:53:19 clippy::doc_markdown "you should put `DoubleEndedIterator` between ticks in the documentation" +target/lintcheck/sources/rayon-1.5.0/src/iter/plumbing/mod.rs:53:43 clippy::doc_markdown "you should put `ExactSizeIterator` between ticks in the documentation" +target/lintcheck/sources/rayon-1.5.0/src/iter/plumbing/mod.rs:54:31 clippy::doc_markdown "you should put `IntoIterator` between ticks in the documentation" +target/lintcheck/sources/rayon-1.5.0/src/iter/plumbing/mod.rs:55:5 clippy::doc_markdown "you should put `IntoIterator` between ticks in the documentation" +target/lintcheck/sources/rayon-1.5.0/src/iter/positions.rs:1:5 clippy::wildcard_imports "usage of wildcard import" +target/lintcheck/sources/rayon-1.5.0/src/iter/positions.rs:2:5 clippy::wildcard_imports "usage of wildcard import" +target/lintcheck/sources/rayon-1.5.0/src/iter/product.rs:1:5 clippy::wildcard_imports "usage of wildcard import" +target/lintcheck/sources/rayon-1.5.0/src/iter/reduce.rs:1:5 clippy::wildcard_imports "usage of wildcard import" +target/lintcheck/sources/rayon-1.5.0/src/iter/reduce.rs:67:19 clippy::wrong_self_convention "methods with the following characteristics: (`to_*` and `self` type is `Copy`) usually take `self` by value" +target/lintcheck/sources/rayon-1.5.0/src/iter/repeat.rs:103:1 clippy::module_name_repetitions "item name starts with its containing module's name" +target/lintcheck/sources/rayon-1.5.0/src/iter/repeat.rs:1:5 clippy::wildcard_imports "usage of wildcard import" +target/lintcheck/sources/rayon-1.5.0/src/iter/repeat.rs:2:5 clippy::wildcard_imports "usage of wildcard import" +target/lintcheck/sources/rayon-1.5.0/src/iter/rev.rs:1:5 clippy::wildcard_imports "usage of wildcard import" +target/lintcheck/sources/rayon-1.5.0/src/iter/rev.rs:2:5 clippy::wildcard_imports "usage of wildcard import" +target/lintcheck/sources/rayon-1.5.0/src/iter/rev.rs:63:9 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope" +target/lintcheck/sources/rayon-1.5.0/src/iter/rev.rs:68:9 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope" +target/lintcheck/sources/rayon-1.5.0/src/iter/skip.rs:2:5 clippy::wildcard_imports "usage of wildcard import" +target/lintcheck/sources/rayon-1.5.0/src/iter/skip.rs:3:5 clippy::wildcard_imports "usage of wildcard import" +target/lintcheck/sources/rayon-1.5.0/src/iter/skip.rs:68:9 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope" +target/lintcheck/sources/rayon-1.5.0/src/iter/skip.rs:73:9 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope" +target/lintcheck/sources/rayon-1.5.0/src/iter/splitter.rs:1:5 clippy::wildcard_imports "usage of wildcard import" +target/lintcheck/sources/rayon-1.5.0/src/iter/splitter.rs:2:5 clippy::wildcard_imports "usage of wildcard import" +target/lintcheck/sources/rayon-1.5.0/src/iter/step_by.rs:4:5 clippy::wildcard_imports "usage of wildcard import" +target/lintcheck/sources/rayon-1.5.0/src/iter/step_by.rs:5:5 clippy::wildcard_imports "usage of wildcard import" +target/lintcheck/sources/rayon-1.5.0/src/iter/step_by.rs:73:9 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope" +target/lintcheck/sources/rayon-1.5.0/src/iter/step_by.rs:79:9 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope" +target/lintcheck/sources/rayon-1.5.0/src/iter/sum.rs:1:5 clippy::wildcard_imports "usage of wildcard import" +target/lintcheck/sources/rayon-1.5.0/src/iter/take.rs:1:5 clippy::wildcard_imports "usage of wildcard import" +target/lintcheck/sources/rayon-1.5.0/src/iter/take.rs:2:5 clippy::wildcard_imports "usage of wildcard import" +target/lintcheck/sources/rayon-1.5.0/src/iter/take.rs:67:9 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope" +target/lintcheck/sources/rayon-1.5.0/src/iter/take.rs:72:9 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope" +target/lintcheck/sources/rayon-1.5.0/src/iter/try_fold.rs:190:1 clippy::module_name_repetitions "item name starts with its containing module's name" +target/lintcheck/sources/rayon-1.5.0/src/iter/try_fold.rs:1:5 clippy::wildcard_imports "usage of wildcard import" +target/lintcheck/sources/rayon-1.5.0/src/iter/try_fold.rs:2:5 clippy::wildcard_imports "usage of wildcard import" +target/lintcheck/sources/rayon-1.5.0/src/iter/try_reduce.rs:1:5 clippy::wildcard_imports "usage of wildcard import" +target/lintcheck/sources/rayon-1.5.0/src/iter/try_reduce.rs:74:19 clippy::wrong_self_convention "methods with the following characteristics: (`to_*` and `self` type is `Copy`) usually take `self` by value" +target/lintcheck/sources/rayon-1.5.0/src/iter/try_reduce_with.rs:1:5 clippy::wildcard_imports "usage of wildcard import" +target/lintcheck/sources/rayon-1.5.0/src/iter/try_reduce_with.rs:69:19 clippy::wrong_self_convention "methods with the following characteristics: (`to_*` and `self` type is `Copy`) usually take `self` by value" +target/lintcheck/sources/rayon-1.5.0/src/iter/unzip.rs:1:5 clippy::wildcard_imports "usage of wildcard import" +target/lintcheck/sources/rayon-1.5.0/src/iter/unzip.rs:2:5 clippy::wildcard_imports "usage of wildcard import" +target/lintcheck/sources/rayon-1.5.0/src/iter/update.rs:1:5 clippy::wildcard_imports "usage of wildcard import" +target/lintcheck/sources/rayon-1.5.0/src/iter/update.rs:2:5 clippy::wildcard_imports "usage of wildcard import" +target/lintcheck/sources/rayon-1.5.0/src/iter/update.rs:82:9 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope" +target/lintcheck/sources/rayon-1.5.0/src/iter/update.rs:87:9 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope" +target/lintcheck/sources/rayon-1.5.0/src/iter/while_some.rs:130:22 clippy::single_match_else "you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let`" +target/lintcheck/sources/rayon-1.5.0/src/iter/while_some.rs:1:5 clippy::wildcard_imports "usage of wildcard import" +target/lintcheck/sources/rayon-1.5.0/src/iter/while_some.rs:2:5 clippy::wildcard_imports "usage of wildcard import" +target/lintcheck/sources/rayon-1.5.0/src/iter/zip.rs:102:9 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope" +target/lintcheck/sources/rayon-1.5.0/src/iter/zip.rs:1:5 clippy::wildcard_imports "usage of wildcard import" +target/lintcheck/sources/rayon-1.5.0/src/iter/zip.rs:2:5 clippy::wildcard_imports "usage of wildcard import" +target/lintcheck/sources/rayon-1.5.0/src/iter/zip.rs:74:9 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope" +target/lintcheck/sources/rayon-1.5.0/src/iter/zip.rs:79:9 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope" +target/lintcheck/sources/rayon-1.5.0/src/iter/zip.rs:97:9 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope" +target/lintcheck/sources/rayon-1.5.0/src/iter/zip_eq.rs:1:5 clippy::wildcard_imports "usage of wildcard import" +target/lintcheck/sources/rayon-1.5.0/src/iter/zip_eq.rs:2:5 clippy::wildcard_imports "usage of wildcard import" +target/lintcheck/sources/rayon-1.5.0/src/option.rs:8:5 clippy::wildcard_imports "usage of wildcard import" +target/lintcheck/sources/rayon-1.5.0/src/option.rs:9:5 clippy::wildcard_imports "usage of wildcard import" +target/lintcheck/sources/rayon-1.5.0/src/par_either.rs:1:5 clippy::wildcard_imports "usage of wildcard import" +target/lintcheck/sources/rayon-1.5.0/src/par_either.rs:3:5 clippy::wildcard_imports "usage of wildcard import" +target/lintcheck/sources/rayon-1.5.0/src/private.rs:9:1 clippy::module_name_repetitions "item name starts with its containing module's name" +target/lintcheck/sources/rayon-1.5.0/src/range.rs:19:5 clippy::wildcard_imports "usage of wildcard import" +target/lintcheck/sources/rayon-1.5.0/src/range.rs:20:5 clippy::wildcard_imports "usage of wildcard import" +target/lintcheck/sources/rayon-1.5.0/src/range_inclusive.rs:194:9 clippy::range_plus_one "an inclusive range would be more readable" +target/lintcheck/sources/rayon-1.5.0/src/range_inclusive.rs:194:9 clippy::range_plus_one "an inclusive range would be more readable" +target/lintcheck/sources/rayon-1.5.0/src/range_inclusive.rs:19:5 clippy::wildcard_imports "usage of wildcard import" +target/lintcheck/sources/rayon-1.5.0/src/range_inclusive.rs:209:9 clippy::range_plus_one "an inclusive range would be more readable" +target/lintcheck/sources/rayon-1.5.0/src/range_inclusive.rs:209:9 clippy::range_plus_one "an inclusive range would be more readable" +target/lintcheck/sources/rayon-1.5.0/src/range_inclusive.rs:20:5 clippy::wildcard_imports "usage of wildcard import" +target/lintcheck/sources/rayon-1.5.0/src/range_inclusive.rs:231:9 clippy::range_plus_one "an inclusive range would be more readable" +target/lintcheck/sources/rayon-1.5.0/src/range_inclusive.rs:231:9 clippy::range_plus_one "an inclusive range would be more readable" +target/lintcheck/sources/rayon-1.5.0/src/result.rs:8:5 clippy::wildcard_imports "usage of wildcard import" +target/lintcheck/sources/rayon-1.5.0/src/result.rs:9:5 clippy::wildcard_imports "usage of wildcard import" +target/lintcheck/sources/rayon-1.5.0/src/slice/mergesort.rs:102:5 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope" +target/lintcheck/sources/rayon-1.5.0/src/slice/mergesort.rs:109:5 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope" +target/lintcheck/sources/rayon-1.5.0/src/slice/mergesort.rs:114:5 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope" +target/lintcheck/sources/rayon-1.5.0/src/slice/mergesort.rs:211:5 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope" +target/lintcheck/sources/rayon-1.5.0/src/slice/mergesort.rs:217:5 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope" +target/lintcheck/sources/rayon-1.5.0/src/slice/mergesort.rs:251:5 clippy::doc_markdown "you should put `TimSort` between ticks in the documentation" +target/lintcheck/sources/rayon-1.5.0/src/slice/mergesort.rs:252:5 clippy::doc_markdown "you should put bare URLs between `<`/`>` or make a proper Markdown link" +target/lintcheck/sources/rayon-1.5.0/src/slice/mergesort.rs:286:59 clippy::doc_markdown "you should put `TimSort` between ticks in the documentation" +target/lintcheck/sources/rayon-1.5.0/src/slice/mergesort.rs:333:24 clippy::redundant_else "redundant else block" +target/lintcheck/sources/rayon-1.5.0/src/slice/mergesort.rs:513:5 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope" +target/lintcheck/sources/rayon-1.5.0/src/slice/mergesort.rs:521:5 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope" +target/lintcheck/sources/rayon-1.5.0/src/slice/mergesort.rs:7:5 clippy::wildcard_imports "usage of wildcard import" +target/lintcheck/sources/rayon-1.5.0/src/slice/mergesort.rs:98:5 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope" +target/lintcheck/sources/rayon-1.5.0/src/slice/mod.rs:15:5 clippy::wildcard_imports "usage of wildcard import" +target/lintcheck/sources/rayon-1.5.0/src/slice/mod.rs:16:5 clippy::wildcard_imports "usage of wildcard import" +target/lintcheck/sources/rayon-1.5.0/src/slice/mod.rs:17:5 clippy::wildcard_imports "usage of wildcard import" +target/lintcheck/sources/rayon-1.5.0/src/slice/mod.rs:25:1 clippy::module_name_repetitions "item name ends with its containing module's name" +target/lintcheck/sources/rayon-1.5.0/src/slice/mod.rs:657:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/rayon-1.5.0/src/slice/mod.rs:971:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/rayon-1.5.0/src/slice/quicksort.rs:230:36 clippy::doc_markdown "you should put `BlockQuicksort` between ticks in the documentation" +target/lintcheck/sources/rayon-1.5.0/src/slice/quicksort.rs:233:1 clippy::too_many_lines "this function has too many lines (117/100)" +target/lintcheck/sources/rayon-1.5.0/src/slice/quicksort.rs:258:26 clippy::unseparated_literal_suffix "integer type suffix should be separated by an underscore" +target/lintcheck/sources/rayon-1.5.0/src/slice/quicksort.rs:265:26 clippy::unseparated_literal_suffix "integer type suffix should be separated by an underscore" +target/lintcheck/sources/rayon-1.5.0/src/slice/quicksort.rs:268:5 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope" +target/lintcheck/sources/rayon-1.5.0/src/slice/quicksort.rs:308:30 clippy::cast_possible_truncation "casting `usize` to `u8` may truncate the value" +target/lintcheck/sources/rayon-1.5.0/src/slice/quicksort.rs:325:30 clippy::cast_possible_truncation "casting `usize` to `u8` may truncate the value" +target/lintcheck/sources/rayon-1.5.0/src/slice/quicksort.rs:393:36 clippy::cast_possible_wrap "casting `u8` to `isize` may wrap around the value on targets with 32-bit wide pointers" +target/lintcheck/sources/rayon-1.5.0/src/slice/quicksort.rs:405:40 clippy::cast_possible_wrap "casting `u8` to `isize` may wrap around the value on targets with 32-bit wide pointers" +target/lintcheck/sources/rayon-1.5.0/src/slice/quicksort.rs:430:14 clippy::shadow_unrelated "`pivot` is being shadowed" +target/lintcheck/sources/rayon-1.5.0/src/slice/quicksort.rs:439:13 clippy::shadow_unrelated "`pivot` is being shadowed" +target/lintcheck/sources/rayon-1.5.0/src/slice/quicksort.rs:482:10 clippy::shadow_unrelated "`pivot` is being shadowed" +target/lintcheck/sources/rayon-1.5.0/src/slice/quicksort.rs:491:9 clippy::shadow_unrelated "`pivot` is being shadowed" +target/lintcheck/sources/rayon-1.5.0/src/slice/quicksort.rs:534:26 clippy::cast_possible_truncation "casting `usize` to `u32` may truncate the value on targets with 64-bit wide pointers" +target/lintcheck/sources/rayon-1.5.0/src/slice/quicksort.rs:545:17 clippy::cast_possible_truncation "casting `u64` to `usize` may truncate the value on targets with 32-bit wide pointers" +target/lintcheck/sources/rayon-1.5.0/src/slice/quicksort.rs:588:17 clippy::identity_op "the operation is ineffective. Consider reducing it to `len / 4`" +target/lintcheck/sources/rayon-1.5.0/src/slice/quicksort.rs:716:14 clippy::shadow_unrelated "`pivot` is being shadowed" +target/lintcheck/sources/rayon-1.5.0/src/split_producer.rs:56:16 clippy::option_if_let_else "use Option::map_or_else instead of an if let/else" +target/lintcheck/sources/rayon-1.5.0/src/split_producer.rs:92:9 clippy::option_if_let_else "use Option::map_or instead of an if let/else" +target/lintcheck/sources/rayon-1.5.0/src/str.rs:16:5 clippy::wildcard_imports "usage of wildcard import" +target/lintcheck/sources/rayon-1.5.0/src/str.rs:17:5 clippy::wildcard_imports "usage of wildcard import" +target/lintcheck/sources/rayon-1.5.0/src/str.rs:18:5 clippy::wildcard_imports "usage of wildcard import" +target/lintcheck/sources/rayon-1.5.0/src/str.rs:25:5 clippy::cast_possible_wrap "casting `u8` to `i8` may wrap around the value" +target/lintcheck/sources/rayon-1.5.0/src/str.rs:715:9 clippy::manual_strip "stripping a suffix manually" +target/lintcheck/sources/rayon-1.5.0/src/string.rs:5:5 clippy::wildcard_imports "usage of wildcard import" +target/lintcheck/sources/rayon-1.5.0/src/vec.rs:137:12 clippy::len_zero "length comparison to zero" +target/lintcheck/sources/rayon-1.5.0/src/vec.rs:8:5 clippy::wildcard_imports "usage of wildcard import" +target/lintcheck/sources/rayon-1.5.0/src/vec.rs:9:5 clippy::wildcard_imports "usage of wildcard import" +target/lintcheck/sources/regex-1.3.2/src/backtrack.rs:100:13 clippy::redundant_field_names "redundant field names in struct initialization" +target/lintcheck/sources/regex-1.3.2/src/backtrack.rs:133:17 clippy::same_item_push "it looks like the same item is being pushed into this Vec" +target/lintcheck/sources/regex-1.3.2/src/backtrack.rs:145:20 clippy::if_not_else "unnecessary boolean `not` operation" +target/lintcheck/sources/regex-1.3.2/src/backtrack.rs:199:13 clippy::enum_glob_use "usage of wildcard import for enum variants" +target/lintcheck/sources/regex-1.3.2/src/backtrack.rs:223:29 clippy::redundant_field_names "redundant field names in struct initialization" +target/lintcheck/sources/regex-1.3.2/src/backtrack.rs:230:66 clippy::redundant_field_names "redundant field names in struct initialization" +target/lintcheck/sources/regex-1.3.2/src/backtrack.rs:284:21 clippy::cast_lossless "casting `u32` to `u64` may become silently lossy if you later change the type" +target/lintcheck/sources/regex-1.3.2/src/backtrack.rs:287:5 clippy::cast_possible_truncation "casting `usize` to `u32` may truncate the value on targets with 64-bit wide pointers" +target/lintcheck/sources/regex-1.3.2/src/backtrack.rs:97:13 clippy::redundant_field_names "redundant field names in struct initialization" +target/lintcheck/sources/regex-1.3.2/src/backtrack.rs:98:13 clippy::redundant_field_names "redundant field names in struct initialization" +target/lintcheck/sources/regex-1.3.2/src/backtrack.rs:99:13 clippy::redundant_field_names "redundant field names in struct initialization" +target/lintcheck/sources/regex-1.3.2/src/compile.rs:1005:32 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/regex-1.3.2/src/compile.rs:1006:21 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/regex-1.3.2/src/compile.rs:1008:18 clippy::cast_lossless "casting `u8` to `u64` may become silently lossy if you later change the type" +target/lintcheck/sources/regex-1.3.2/src/compile.rs:1009:18 clippy::cast_lossless "casting `u8` to `u64` may become silently lossy if you later change the type" +target/lintcheck/sources/regex-1.3.2/src/compile.rs:1010:9 clippy::cast_possible_truncation "casting `u64` to `usize` may truncate the value on targets with 32-bit wide pointers" +target/lintcheck/sources/regex-1.3.2/src/compile.rs:102:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/regex-1.3.2/src/compile.rs:1037:37 clippy::cast_possible_truncation "casting `u16` to `u8` may truncate the value" +target/lintcheck/sources/regex-1.3.2/src/compile.rs:1037:55 clippy::cast_possible_truncation "casting `u16` to `u8` may truncate the value" +target/lintcheck/sources/regex-1.3.2/src/compile.rs:1040:28 clippy::cast_possible_truncation "casting `u16` to `u8` may truncate the value" +target/lintcheck/sources/regex-1.3.2/src/compile.rs:1040:38 clippy::cast_possible_truncation "casting `u16` to `u8` may truncate the value" +target/lintcheck/sources/regex-1.3.2/src/compile.rs:1051:25 clippy::unseparated_literal_suffix "integer type suffix should be separated by an underscore" +target/lintcheck/sources/regex-1.3.2/src/compile.rs:1071:8 clippy::cast_lossless "casting `u32` to `u64` may become silently lossy if you later change the type" +target/lintcheck/sources/regex-1.3.2/src/compile.rs:112:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/regex-1.3.2/src/compile.rs:154:30 clippy::redundant_closure_for_method_calls "redundant closure" +target/lintcheck/sources/regex-1.3.2/src/compile.rs:156:30 clippy::redundant_closure_for_method_calls "redundant closure" +target/lintcheck/sources/regex-1.3.2/src/compile.rs:185:5 clippy::unnecessary_wraps "this function's return value is unnecessarily wrapped by `Result`" +target/lintcheck/sources/regex-1.3.2/src/compile.rs:187:40 clippy::redundant_closure_for_method_calls "redundant closure" +target/lintcheck/sources/regex-1.3.2/src/compile.rs:201:53 clippy::doc_markdown "you should put `MaybeInsts` between ticks in the documentation" +target/lintcheck/sources/regex-1.3.2/src/compile.rs:241:63 clippy::doc_markdown "you should put `c_concat` between ticks in the documentation" +target/lintcheck/sources/regex-1.3.2/src/compile.rs:245:5 clippy::too_many_lines "this function has too many lines (111/100)" +target/lintcheck/sources/regex-1.3.2/src/compile.rs:247:13 clippy::enum_glob_use "usage of wildcard import for enum variants" +target/lintcheck/sources/regex-1.3.2/src/compile.rs:373:24 clippy::redundant_field_names "redundant field names in struct initialization" +target/lintcheck/sources/regex-1.3.2/src/compile.rs:373:36 clippy::redundant_field_names "redundant field names in struct initialization" +target/lintcheck/sources/regex-1.3.2/src/compile.rs:378:12 clippy::if_not_else "unnecessary boolean `not` operation" +target/lintcheck/sources/regex-1.3.2/src/compile.rs:400:37 clippy::redundant_field_names "redundant field names in struct initialization" +target/lintcheck/sources/regex-1.3.2/src/compile.rs:407:51 clippy::redundant_field_names "redundant field names in struct initialization" +target/lintcheck/sources/regex-1.3.2/src/compile.rs:409:24 clippy::redundant_field_names "redundant field names in struct initialization" +target/lintcheck/sources/regex-1.3.2/src/compile.rs:417:5 clippy::unnecessary_wraps "this function's return value is unnecessarily wrapped by `Result`" +target/lintcheck/sources/regex-1.3.2/src/compile.rs:42:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/regex-1.3.2/src/compile.rs:42:5 clippy::new_without_default "you should consider adding a `Default` implementation for `compile::Compiler`" +target/lintcheck/sources/regex-1.3.2/src/compile.rs:444:5 clippy::unnecessary_wraps "this function's return value is unnecessarily wrapped by `Result`" +target/lintcheck/sources/regex-1.3.2/src/compile.rs:445:57 clippy::redundant_field_names "redundant field names in struct initialization" +target/lintcheck/sources/regex-1.3.2/src/compile.rs:446:20 clippy::redundant_field_names "redundant field names in struct initialization" +target/lintcheck/sources/regex-1.3.2/src/compile.rs:466:20 clippy::redundant_field_names "redundant field names in struct initialization" +target/lintcheck/sources/regex-1.3.2/src/compile.rs:466:32 clippy::redundant_field_names "redundant field names in struct initialization" +target/lintcheck/sources/regex-1.3.2/src/compile.rs:519:13 clippy::enum_glob_use "usage of wildcard import for enum variants" +target/lintcheck/sources/regex-1.3.2/src/compile.rs:55:57 clippy::doc_markdown "you should put `size_limit` between ticks in the documentation" +target/lintcheck/sources/regex-1.3.2/src/compile.rs:58:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/regex-1.3.2/src/compile.rs:748:41 clippy::redundant_field_names "redundant field names in struct initialization" +target/lintcheck/sources/regex-1.3.2/src/compile.rs:74:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/regex-1.3.2/src/compile.rs:751:54 clippy::redundant_field_names "redundant field names in struct initialization" +target/lintcheck/sources/regex-1.3.2/src/compile.rs:765:41 clippy::redundant_field_names "redundant field names in struct initialization" +target/lintcheck/sources/regex-1.3.2/src/compile.rs:765:55 clippy::redundant_field_names "redundant field names in struct initialization" +target/lintcheck/sources/regex-1.3.2/src/compile.rs:825:39 clippy::redundant_field_names "redundant field names in struct initialization" +target/lintcheck/sources/regex-1.3.2/src/compile.rs:825:51 clippy::redundant_field_names "redundant field names in struct initialization" +target/lintcheck/sources/regex-1.3.2/src/compile.rs:828:49 clippy::redundant_field_names "redundant field names in struct initialization" +target/lintcheck/sources/regex-1.3.2/src/compile.rs:828:61 clippy::redundant_field_names "redundant field names in struct initialization" +target/lintcheck/sources/regex-1.3.2/src/compile.rs:830:59 clippy::redundant_field_names "redundant field names in struct initialization" +target/lintcheck/sources/regex-1.3.2/src/compile.rs:830:71 clippy::redundant_field_names "redundant field names in struct initialization" +target/lintcheck/sources/regex-1.3.2/src/compile.rs:832:43 clippy::redundant_field_names "redundant field names in struct initialization" +target/lintcheck/sources/regex-1.3.2/src/compile.rs:835:41 clippy::redundant_field_names "redundant field names in struct initialization" +target/lintcheck/sources/regex-1.3.2/src/compile.rs:835:53 clippy::redundant_field_names "redundant field names in struct initialization" +target/lintcheck/sources/regex-1.3.2/src/compile.rs:835:67 clippy::redundant_field_names "redundant field names in struct initialization" +target/lintcheck/sources/regex-1.3.2/src/compile.rs:83:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/regex-1.3.2/src/compile.rs:896:5 clippy::unnecessary_wraps "this function's return value is unnecessarily wrapped by `Result`" +target/lintcheck/sources/regex-1.3.2/src/compile.rs:905:17 clippy::redundant_field_names "redundant field names in struct initialization" +target/lintcheck/sources/regex-1.3.2/src/compile.rs:953:17 clippy::doc_markdown "you should put `HashMap` between ticks in the documentation" +target/lintcheck/sources/regex-1.3.2/src/compile.rs:95:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/regex-1.3.2/src/compile.rs:980:26 clippy::unseparated_literal_suffix "integer type suffix should be separated by an underscore" +target/lintcheck/sources/regex-1.3.2/src/compile.rs:994:44 clippy::redundant_field_names "redundant field names in struct initialization" +target/lintcheck/sources/regex-1.3.2/src/compile.rs:994:54 clippy::redundant_field_names "redundant field names in struct initialization" +target/lintcheck/sources/regex-1.3.2/src/dfa.rs:1007:17 clippy::similar_names "binding's name is too similar to existing binding" +target/lintcheck/sources/regex-1.3.2/src/dfa.rs:1010:22 clippy::similar_names "binding's name is too similar to existing binding" +target/lintcheck/sources/regex-1.3.2/src/dfa.rs:1059:13 clippy::enum_glob_use "usage of wildcard import for enum variants" +target/lintcheck/sources/regex-1.3.2/src/dfa.rs:1060:13 clippy::enum_glob_use "usage of wildcard import for enum variants" +target/lintcheck/sources/regex-1.3.2/src/dfa.rs:1084:38 clippy::cast_possible_truncation "casting `usize` to `u32` may truncate the value on targets with 64-bit wide pointers" +target/lintcheck/sources/regex-1.3.2/src/dfa.rs:1087:38 clippy::cast_possible_truncation "casting `usize` to `u32` may truncate the value on targets with 64-bit wide pointers" +target/lintcheck/sources/regex-1.3.2/src/dfa.rs:1090:38 clippy::cast_possible_truncation "casting `usize` to `u32` may truncate the value on targets with 64-bit wide pointers" +target/lintcheck/sources/regex-1.3.2/src/dfa.rs:1093:38 clippy::cast_possible_truncation "casting `usize` to `u32` may truncate the value on targets with 64-bit wide pointers" +target/lintcheck/sources/regex-1.3.2/src/dfa.rs:1096:38 clippy::cast_possible_truncation "casting `usize` to `u32` may truncate the value on targets with 64-bit wide pointers" +target/lintcheck/sources/regex-1.3.2/src/dfa.rs:1101:38 clippy::cast_possible_truncation "casting `usize` to `u32` may truncate the value on targets with 64-bit wide pointers" +target/lintcheck/sources/regex-1.3.2/src/dfa.rs:1104:38 clippy::cast_possible_truncation "casting `usize` to `u32` may truncate the value on targets with 64-bit wide pointers" +target/lintcheck/sources/regex-1.3.2/src/dfa.rs:1107:38 clippy::cast_possible_truncation "casting `usize` to `u32` may truncate the value on targets with 64-bit wide pointers" +target/lintcheck/sources/regex-1.3.2/src/dfa.rs:1117:30 clippy::cast_possible_truncation "casting `usize` to `u32` may truncate the value on targets with 64-bit wide pointers" +target/lintcheck/sources/regex-1.3.2/src/dfa.rs:1120:47 clippy::cast_possible_truncation "casting `usize` to `u32` may truncate the value on targets with 64-bit wide pointers" +target/lintcheck/sources/regex-1.3.2/src/dfa.rs:1121:30 clippy::cast_possible_truncation "casting `usize` to `u32` may truncate the value on targets with 64-bit wide pointers" +target/lintcheck/sources/regex-1.3.2/src/dfa.rs:1129:13 clippy::doc_markdown "you should put `is_match` between ticks in the documentation" +target/lintcheck/sources/regex-1.3.2/src/dfa.rs:1134:13 clippy::doc_markdown "you should put `is_match` between ticks in the documentation" +target/lintcheck/sources/regex-1.3.2/src/dfa.rs:1185:68 clippy::doc_markdown "you should put `is_match` between ticks in the documentation" +target/lintcheck/sources/regex-1.3.2/src/dfa.rs:1193:13 clippy::enum_glob_use "usage of wildcard import for enum variants" +target/lintcheck/sources/regex-1.3.2/src/dfa.rs:1205:13 clippy::mem_replace_with_default "replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take`" +target/lintcheck/sources/regex-1.3.2/src/dfa.rs:1244:50 clippy::doc_markdown "you should put `current_state` between ticks in the documentation" +target/lintcheck/sources/regex-1.3.2/src/dfa.rs:1338:58 clippy::doc_markdown "you should put `STATE_DEAD` between ticks in the documentation" +target/lintcheck/sources/regex-1.3.2/src/dfa.rs:1339:9 clippy::doc_markdown "you should put `STATE_UNKNOWN` between ticks in the documentation" +target/lintcheck/sources/regex-1.3.2/src/dfa.rs:1366:25 clippy::doc_markdown "you should put `STATE_DEAD` between ticks in the documentation" +target/lintcheck/sources/regex-1.3.2/src/dfa.rs:1366:46 clippy::doc_markdown "you should put `STATE_UNKNOWN` between ticks in the documentation" +target/lintcheck/sources/regex-1.3.2/src/dfa.rs:1367:41 clippy::inline_always "you have declared `#[inline(always)]` on `start_state`. This is usually a bad idea" +target/lintcheck/sources/regex-1.3.2/src/dfa.rs:1380:14 clippy::identity_op "the operation is ineffective. Consider reducing it to `(empty_flags.start as u8)`" +target/lintcheck/sources/regex-1.3.2/src/dfa.rs:1388:15 clippy::match_on_vec_items "indexing into a vector may panic" +target/lintcheck/sources/regex-1.3.2/src/dfa.rs:1412:20 clippy::unused_self "unused `self` argument" +target/lintcheck/sources/regex-1.3.2/src/dfa.rs:1438:9 clippy::unused_self "unused `self` argument" +target/lintcheck/sources/regex-1.3.2/src/dfa.rs:1472:9 clippy::doc_markdown "you should put `StatePtr` between ticks in the documentation" +target/lintcheck/sources/regex-1.3.2/src/dfa.rs:1490:54 clippy::cast_possible_truncation "casting `i32` to `u8` may truncate the value" +target/lintcheck/sources/regex-1.3.2/src/dfa.rs:1490:54 clippy::cast_sign_loss "casting `i32` to `u8` may lose the sign of the value" +target/lintcheck/sources/regex-1.3.2/src/dfa.rs:1521:20 clippy::doc_markdown "you should put `num_byte_classes` between ticks in the documentation" +target/lintcheck/sources/regex-1.3.2/src/dfa.rs:1529:41 clippy::inline_always "you have declared `#[inline(always)]` on `byte_class`. This is usually a bad idea" +target/lintcheck/sources/regex-1.3.2/src/dfa.rs:1537:14 clippy::doc_markdown "you should put `byte_class` between ticks in the documentation" +target/lintcheck/sources/regex-1.3.2/src/dfa.rs:1538:41 clippy::inline_always "you have declared `#[inline(always)]` on `u8_class`. This is usually a bad idea" +target/lintcheck/sources/regex-1.3.2/src/dfa.rs:1562:18 clippy::doc_markdown "you should put `STATE_START` between ticks in the documentation" +target/lintcheck/sources/regex-1.3.2/src/dfa.rs:1614:13 clippy::redundant_field_names "redundant field names in struct initialization" +target/lintcheck/sources/regex-1.3.2/src/dfa.rs:1651:38 clippy::redundant_field_names "redundant field names in struct initialization" +target/lintcheck/sources/regex-1.3.2/src/dfa.rs:1700:17 clippy::trivially_copy_pass_by_ref "this argument (1 byte) is passed by reference, but would be more efficient if passed by value (limit: 8 byte)" +target/lintcheck/sources/regex-1.3.2/src/dfa.rs:1701:18 clippy::unusual_byte_groupings "digits of hex or binary literal not grouped by four" +target/lintcheck/sources/regex-1.3.2/src/dfa.rs:1705:19 clippy::unusual_byte_groupings "digits of hex or binary literal not grouped by four" +target/lintcheck/sources/regex-1.3.2/src/dfa.rs:1708:16 clippy::trivially_copy_pass_by_ref "this argument (1 byte) is passed by reference, but would be more efficient if passed by value (limit: 8 byte)" +target/lintcheck/sources/regex-1.3.2/src/dfa.rs:1709:18 clippy::unusual_byte_groupings "digits of hex or binary literal not grouped by four" +target/lintcheck/sources/regex-1.3.2/src/dfa.rs:1713:19 clippy::unusual_byte_groupings "digits of hex or binary literal not grouped by four" +target/lintcheck/sources/regex-1.3.2/src/dfa.rs:1716:18 clippy::trivially_copy_pass_by_ref "this argument (1 byte) is passed by reference, but would be more efficient if passed by value (limit: 8 byte)" +target/lintcheck/sources/regex-1.3.2/src/dfa.rs:1717:18 clippy::unusual_byte_groupings "digits of hex or binary literal not grouped by four" +target/lintcheck/sources/regex-1.3.2/src/dfa.rs:1721:19 clippy::unusual_byte_groupings "digits of hex or binary literal not grouped by four" +target/lintcheck/sources/regex-1.3.2/src/dfa.rs:1727:14 clippy::cast_lossless "casting `u8` to `u16` may become silently lossy if you later change the type" +target/lintcheck/sources/regex-1.3.2/src/dfa.rs:1732:15 clippy::trivially_copy_pass_by_ref "this argument (2 byte) is passed by reference, but would be more efficient if passed by value (limit: 8 byte)" +target/lintcheck/sources/regex-1.3.2/src/dfa.rs:1736:22 clippy::trivially_copy_pass_by_ref "this argument (2 byte) is passed by reference, but would be more efficient if passed by value (limit: 8 byte)" +target/lintcheck/sources/regex-1.3.2/src/dfa.rs:1741:9 clippy::match_like_matches_macro "match expression looks like `matches!` macro" +target/lintcheck/sources/regex-1.3.2/src/dfa.rs:1747:16 clippy::trivially_copy_pass_by_ref "this argument (2 byte) is passed by reference, but would be more efficient if passed by value (limit: 8 byte)" +target/lintcheck/sources/regex-1.3.2/src/dfa.rs:1751:18 clippy::cast_possible_truncation "casting `u16` to `u8` may truncate the value" +target/lintcheck/sources/regex-1.3.2/src/dfa.rs:1815:38 clippy::cast_possible_truncation "casting `usize` to `u8` may truncate the value" +target/lintcheck/sources/regex-1.3.2/src/dfa.rs:1821:21 clippy::cast_lossless "casting `u32` to `u64` may become silently lossy if you later change the type" +target/lintcheck/sources/regex-1.3.2/src/dfa.rs:1824:5 clippy::cast_possible_truncation "casting `usize` to `u32` may truncate the value on targets with 64-bit wide pointers" +target/lintcheck/sources/regex-1.3.2/src/dfa.rs:1848:5 clippy::doc_markdown "you should put bare URLs between `<`/`>` or make a proper Markdown link" +target/lintcheck/sources/regex-1.3.2/src/dfa.rs:1850:18 clippy::cast_sign_loss "casting `i32` to `u32` may lose the sign of the value" +target/lintcheck/sources/regex-1.3.2/src/dfa.rs:1857:5 clippy::doc_markdown "you should put bare URLs between `<`/`>` or make a proper Markdown link" +target/lintcheck/sources/regex-1.3.2/src/dfa.rs:1860:17 clippy::cast_possible_wrap "casting `u32` to `i32` may wrap around the value" +target/lintcheck/sources/regex-1.3.2/src/dfa.rs:1867:5 clippy::doc_markdown "you should put bare URLs between `<`/`>` or make a proper Markdown link" +target/lintcheck/sources/regex-1.3.2/src/dfa.rs:1870:19 clippy::cast_possible_truncation "casting `u32` to `u8` may truncate the value" +target/lintcheck/sources/regex-1.3.2/src/dfa.rs:1873:15 clippy::cast_possible_truncation "casting `u32` to `u8` may truncate the value" +target/lintcheck/sources/regex-1.3.2/src/dfa.rs:1876:5 clippy::doc_markdown "you should put bare URLs between `<`/`>` or make a proper Markdown link" +target/lintcheck/sources/regex-1.3.2/src/dfa.rs:1882:26 clippy::cast_lossless "casting `u8` to `u32` may become silently lossy if you later change the type" +target/lintcheck/sources/regex-1.3.2/src/dfa.rs:1884:15 clippy::cast_lossless "casting `u8` to `u32` may become silently lossy if you later change the type" +target/lintcheck/sources/regex-1.3.2/src/dfa.rs:277:17 clippy::cast_possible_wrap "casting `u32` to `i32` may wrap around the value" +target/lintcheck/sources/regex-1.3.2/src/dfa.rs:277:31 clippy::cast_possible_wrap "casting `u32` to `i32` may wrap around the value" +target/lintcheck/sources/regex-1.3.2/src/dfa.rs:295:20 clippy::cast_possible_truncation "casting `usize` to `i32` may truncate the value on targets with 64-bit wide pointers" +target/lintcheck/sources/regex-1.3.2/src/dfa.rs:295:20 clippy::cast_possible_wrap "casting `usize` to `i32` may wrap around the value on targets with 32-bit wide pointers" +target/lintcheck/sources/regex-1.3.2/src/dfa.rs:299:21 clippy::cast_sign_loss "casting `i32` to `usize` may lose the sign of the value" +target/lintcheck/sources/regex-1.3.2/src/dfa.rs:34:46 clippy::doc_markdown "you should put bare URLs between `<`/`>` or make a proper Markdown link" +target/lintcheck/sources/regex-1.3.2/src/dfa.rs:398:1 clippy::struct_excessive_bools "more than 3 bools in a struct" +target/lintcheck/sources/regex-1.3.2/src/dfa.rs:446:41 clippy::inline_always "you have declared `#[inline(always)]` on `forward`. This is usually a bad idea" +target/lintcheck/sources/regex-1.3.2/src/dfa.rs:457:13 clippy::redundant_field_names "redundant field names in struct initialization" +target/lintcheck/sources/regex-1.3.2/src/dfa.rs:459:13 clippy::redundant_field_names "redundant field names in struct initialization" +target/lintcheck/sources/regex-1.3.2/src/dfa.rs:460:13 clippy::redundant_field_names "redundant field names in struct initialization" +target/lintcheck/sources/regex-1.3.2/src/dfa.rs:476:41 clippy::inline_always "you have declared `#[inline(always)]` on `reverse`. This is usually a bad idea" +target/lintcheck/sources/regex-1.3.2/src/dfa.rs:487:13 clippy::redundant_field_names "redundant field names in struct initialization" +target/lintcheck/sources/regex-1.3.2/src/dfa.rs:489:13 clippy::redundant_field_names "redundant field names in struct initialization" +target/lintcheck/sources/regex-1.3.2/src/dfa.rs:490:13 clippy::redundant_field_names "redundant field names in struct initialization" +target/lintcheck/sources/regex-1.3.2/src/dfa.rs:506:41 clippy::inline_always "you have declared `#[inline(always)]` on `forward_many`. This is usually a bad idea" +target/lintcheck/sources/regex-1.3.2/src/dfa.rs:518:13 clippy::redundant_field_names "redundant field names in struct initialization" +target/lintcheck/sources/regex-1.3.2/src/dfa.rs:520:13 clippy::redundant_field_names "redundant field names in struct initialization" +target/lintcheck/sources/regex-1.3.2/src/dfa.rs:554:41 clippy::inline_always "you have declared `#[inline(always)]` on `exec_at`. This is usually a bad idea" +target/lintcheck/sources/regex-1.3.2/src/dfa.rs:555:5 clippy::too_many_lines "this function has too many lines (101/100)" +target/lintcheck/sources/regex-1.3.2/src/dfa.rs:58:9 clippy::enum_glob_use "usage of wildcard import for enum variants" +target/lintcheck/sources/regex-1.3.2/src/dfa.rs:667:21 clippy::similar_names "binding's name is too similar to existing binding" +target/lintcheck/sources/regex-1.3.2/src/dfa.rs:747:41 clippy::inline_always "you have declared `#[inline(always)]` on `exec_at_reverse`. This is usually a bad idea" +target/lintcheck/sources/regex-1.3.2/src/dfa.rs:795:21 clippy::similar_names "binding's name is too similar to existing binding" +target/lintcheck/sources/regex-1.3.2/src/dfa.rs:848:9 clippy::doc_markdown "you should put `next_si` between ticks in the documentation" +target/lintcheck/sources/regex-1.3.2/src/dfa.rs:852:41 clippy::inline_always "you have declared `#[inline(always)]` on `next_si`. This is usually a bad idea" +target/lintcheck/sources/regex-1.3.2/src/dfa.rs:885:12 clippy::doc_markdown "you should put `STATE_DEAD` between ticks in the documentation" +target/lintcheck/sources/regex-1.3.2/src/dfa.rs:889:9 clippy::doc_markdown "you should put `STATE_UNKNOWN` between ticks in the documentation" +target/lintcheck/sources/regex-1.3.2/src/dfa.rs:897:13 clippy::enum_glob_use "usage of wildcard import for enum variants" +target/lintcheck/sources/regex-1.3.2/src/dfa.rs:979:29 clippy::cast_possible_truncation "casting `usize` to `u32` may truncate the value on targets with 64-bit wide pointers" +target/lintcheck/sources/regex-1.3.2/src/error.rs:6:1 clippy::manual_non_exhaustive "this seems like a manual implementation of the non-exhaustive pattern" +target/lintcheck/sources/regex-1.3.2/src/exec.rs:1000:14 clippy::doc_markdown "you should put `captures_nfa` between ticks in the documentation" +target/lintcheck/sources/regex-1.3.2/src/exec.rs:100:1 clippy::module_name_repetitions "item name starts with its containing module's name" +target/lintcheck/sources/regex-1.3.2/src/exec.rs:1028:5 clippy::too_many_arguments "this function has too many arguments (9/7)" +target/lintcheck/sources/regex-1.3.2/src/exec.rs:1039:13 clippy::enum_glob_use "usage of wildcard import for enum variants" +target/lintcheck/sources/regex-1.3.2/src/exec.rs:1144:13 clippy::enum_glob_use "usage of wildcard import for enum variants" +target/lintcheck/sources/regex-1.3.2/src/exec.rs:1179:26 clippy::match_same_arms "this `match` has identical arm bodies" +target/lintcheck/sources/regex-1.3.2/src/exec.rs:122:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/regex-1.3.2/src/exec.rs:1250:41 clippy::inline_always "you have declared `#[inline(always)]` on `searcher`. This is usually a bad idea" +target/lintcheck/sources/regex-1.3.2/src/exec.rs:1260:41 clippy::inline_always "you have declared `#[inline(always)]` on `searcher_str`. This is usually a bad idea" +target/lintcheck/sources/regex-1.3.2/src/exec.rs:1270:17 clippy::doc_markdown "you should put `RegexSet` between ticks in the documentation" +target/lintcheck/sources/regex-1.3.2/src/exec.rs:1280:17 clippy::doc_markdown "you should put `RegexSet` between ticks in the documentation" +target/lintcheck/sources/regex-1.3.2/src/exec.rs:137:9 clippy::field_reassign_with_default "field assignment outside of initializer for an instance created with Default::default()" +target/lintcheck/sources/regex-1.3.2/src/exec.rs:142:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/regex-1.3.2/src/exec.rs:158:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/regex-1.3.2/src/exec.rs:168:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/regex-1.3.2/src/exec.rs:181:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/regex-1.3.2/src/exec.rs:195:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/regex-1.3.2/src/exec.rs:204:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/regex-1.3.2/src/exec.rs:210:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/regex-1.3.2/src/exec.rs:245:62 clippy::if_same_then_else "this `if` has identical blocks" +target/lintcheck/sources/regex-1.3.2/src/exec.rs:251:21 clippy::if_not_else "unnecessary boolean `not` operation" +target/lintcheck/sources/regex-1.3.2/src/exec.rs:262:60 clippy::if_same_then_else "this `if` has identical blocks" +target/lintcheck/sources/regex-1.3.2/src/exec.rs:268:21 clippy::if_not_else "unnecessary boolean `not` operation" +target/lintcheck/sources/regex-1.3.2/src/exec.rs:278:13 clippy::redundant_field_names "redundant field names in struct initialization" +target/lintcheck/sources/regex-1.3.2/src/exec.rs:281:13 clippy::redundant_field_names "redundant field names in struct initialization" +target/lintcheck/sources/regex-1.3.2/src/exec.rs:286:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/regex-1.3.2/src/exec.rs:300:30 clippy::redundant_field_names "redundant field names in struct initialization" +target/lintcheck/sources/regex-1.3.2/src/exec.rs:308:17 clippy::similar_names "binding's name is too similar to existing binding" +target/lintcheck/sources/regex-1.3.2/src/exec.rs:329:13 clippy::redundant_field_names "redundant field names in struct initialization" +target/lintcheck/sources/regex-1.3.2/src/exec.rs:330:13 clippy::redundant_field_names "redundant field names in struct initialization" +target/lintcheck/sources/regex-1.3.2/src/exec.rs:331:13 clippy::redundant_field_names "redundant field names in struct initialization" +target/lintcheck/sources/regex-1.3.2/src/exec.rs:334:13 clippy::redundant_field_names "redundant field names in struct initialization" +target/lintcheck/sources/regex-1.3.2/src/exec.rs:340:19 clippy::redundant_field_names "redundant field names in struct initialization" +target/lintcheck/sources/regex-1.3.2/src/exec.rs:344:27 clippy::unused_self "unused `self` argument" +target/lintcheck/sources/regex-1.3.2/src/exec.rs:383:41 clippy::inline_always "you have declared `#[inline(always)]` on `shortest_match_at`. This is usually a bad idea" +target/lintcheck/sources/regex-1.3.2/src/exec.rs:388:41 clippy::inline_always "you have declared `#[inline(always)]` on `is_match_at`. This is usually a bad idea" +target/lintcheck/sources/regex-1.3.2/src/exec.rs:393:41 clippy::inline_always "you have declared `#[inline(always)]` on `find_at`. This is usually a bad idea" +target/lintcheck/sources/regex-1.3.2/src/exec.rs:398:41 clippy::inline_always "you have declared `#[inline(always)]` on `captures_read_at`. This is usually a bad idea" +target/lintcheck/sources/regex-1.3.2/src/exec.rs:425:41 clippy::inline_always "you have declared `#[inline(always)]` on `shortest_match_at`. This is usually a bad idea" +target/lintcheck/sources/regex-1.3.2/src/exec.rs:44:1 clippy::module_name_repetitions "item name starts with its containing module's name" +target/lintcheck/sources/regex-1.3.2/src/exec.rs:473:9 clippy::doc_markdown "you should put `shortest_match(...).is_some` between ticks in the documentation" +target/lintcheck/sources/regex-1.3.2/src/exec.rs:474:41 clippy::inline_always "you have declared `#[inline(always)]` on `is_match_at`. This is usually a bad idea" +target/lintcheck/sources/regex-1.3.2/src/exec.rs:524:41 clippy::inline_always "you have declared `#[inline(always)]` on `find_at`. This is usually a bad idea" +target/lintcheck/sources/regex-1.3.2/src/exec.rs:52:1 clippy::module_name_repetitions "item name starts with its containing module's name" +target/lintcheck/sources/regex-1.3.2/src/exec.rs:686:13 clippy::enum_glob_use "usage of wildcard import for enum variants" +target/lintcheck/sources/regex-1.3.2/src/exec.rs:727:13 clippy::enum_glob_use "usage of wildcard import for enum variants" +target/lintcheck/sources/regex-1.3.2/src/exec.rs:767:13 clippy::enum_glob_use "usage of wildcard import for enum variants" +target/lintcheck/sources/regex-1.3.2/src/exec.rs:783:41 clippy::inline_always "you have declared `#[inline(always)]` on `shortest_dfa`. This is usually a bad idea" +target/lintcheck/sources/regex-1.3.2/src/exec.rs:791:41 clippy::inline_always "you have declared `#[inline(always)]` on `shortest_dfa_reverse_suffix`. This is usually a bad idea" +target/lintcheck/sources/regex-1.3.2/src/exec.rs:823:13 clippy::enum_glob_use "usage of wildcard import for enum variants" +target/lintcheck/sources/regex-1.3.2/src/exec.rs:868:13 clippy::enum_glob_use "usage of wildcard import for enum variants" +target/lintcheck/sources/regex-1.3.2/src/exec.rs:897:31 clippy::doc_markdown "you should put `shortest_nfa(...).is_some` between ticks in the documentation" +target/lintcheck/sources/regex-1.3.2/src/exec.rs:899:9 clippy::doc_markdown "you should put `shortest_nfa` between ticks in the documentation" +target/lintcheck/sources/regex-1.3.2/src/exec.rs:905:14 clippy::doc_markdown "you should put `match_nfa` between ticks in the documentation" +target/lintcheck/sources/regex-1.3.2/src/exec.rs:930:14 clippy::doc_markdown "you should put `shortest_nfa` between ticks in the documentation" +target/lintcheck/sources/regex-1.3.2/src/exec.rs:981:14 clippy::doc_markdown "you should put `find_nfa` between ticks in the documentation" +target/lintcheck/sources/regex-1.3.2/src/expand.rs:170:27 clippy::trivially_copy_pass_by_ref "this argument (1 byte) is passed by reference, but would be more efficient if passed by value (limit: 8 byte)" +target/lintcheck/sources/regex-1.3.2/src/expand.rs:171:5 clippy::match_like_matches_macro "match expression looks like `matches!` macro" +target/lintcheck/sources/regex-1.3.2/src/expand.rs:22:13 clippy::single_char_add_str "calling `push_str()` using a single-character string literal" +target/lintcheck/sources/regex-1.3.2/src/expand.rs:27:23 clippy::single_match_else "you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let`" +target/lintcheck/sources/regex-1.3.2/src/expand.rs:30:17 clippy::single_char_add_str "calling `push_str()` using a single-character string literal" +target/lintcheck/sources/regex-1.3.2/src/expand.rs:38:30 clippy::map_unwrap_or "called `map(<f>).unwrap_or(<a>)` on an `Option` value. This can be done more directly by calling `map_or(<a>, <f>)` instead" +target/lintcheck/sources/regex-1.3.2/src/expand.rs:42:21 clippy::map_unwrap_or "called `map(<f>).unwrap_or(<a>)` on an `Option` value. This can be done more directly by calling `map_or(<a>, <f>)` instead" +target/lintcheck/sources/regex-1.3.2/src/expand.rs:50:1 clippy::module_name_repetitions "item name starts with its containing module's name" +target/lintcheck/sources/regex-1.3.2/src/expand.rs:69:23 clippy::single_match_else "you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let`" +target/lintcheck/sources/regex-1.3.2/src/expand.rs:80:28 clippy::map_unwrap_or "called `map(<f>).unwrap_or(<a>)` on an `Option` value. This can be done more directly by calling `map_or(<a>, <f>)` instead" +target/lintcheck/sources/regex-1.3.2/src/expand.rs:84:21 clippy::map_unwrap_or "called `map(<f>).unwrap_or(<a>)` on an `Option` value. This can be done more directly by calling `map_or(<a>, <f>)` instead" +target/lintcheck/sources/regex-1.3.2/src/expand.rs:8:1 clippy::module_name_repetitions "item name starts with its containing module's name" +target/lintcheck/sources/regex-1.3.2/src/input.rs:142:1 clippy::module_name_repetitions "item name ends with its containing module's name" +target/lintcheck/sources/regex-1.3.2/src/input.rs:146:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/regex-1.3.2/src/input.rs:15:1 clippy::module_name_repetitions "item name starts with its containing module's name" +target/lintcheck/sources/regex-1.3.2/src/input.rs:165:31 clippy::redundant_field_names "redundant field names in struct initialization" +target/lintcheck/sources/regex-1.3.2/src/input.rs:178:13 clippy::enum_glob_use "usage of wildcard import for enum variants" +target/lintcheck/sources/regex-1.3.2/src/input.rs:228:1 clippy::module_name_repetitions "item name ends with its containing module's name" +target/lintcheck/sources/regex-1.3.2/src/input.rs:236:21 clippy::redundant_field_names "redundant field names in struct initialization" +target/lintcheck/sources/regex-1.3.2/src/input.rs:236:33 clippy::redundant_field_names "redundant field names in struct initialization" +target/lintcheck/sources/regex-1.3.2/src/input.rs:24:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/regex-1.3.2/src/input.rs:271:13 clippy::enum_glob_use "usage of wildcard import for enum variants" +target/lintcheck/sources/regex-1.3.2/src/input.rs:29:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/regex-1.3.2/src/input.rs:362:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/regex-1.3.2/src/input.rs:370:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/regex-1.3.2/src/input.rs:371:42 clippy::redundant_closure_for_method_calls "redundant closure" +target/lintcheck/sources/regex-1.3.2/src/input.rs:37:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/regex-1.3.2/src/input.rs:388:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/regex-1.3.2/src/input.rs:42:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/regex-1.3.2/src/input.rs:47:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/regex-1.3.2/src/input.rs:53:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/regex-1.3.2/src/input.rs:58:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/regex-1.3.2/src/input.rs:63:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/regex-1.3.2/src/lib.rs:1:null clippy::cargo_common_metadata "package `regex` is missing `package.keywords` metadata" +target/lintcheck/sources/regex-1.3.2/src/literal/imp.rs:101:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/regex-1.3.2/src/literal/imp.rs:114:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/regex-1.3.2/src/literal/imp.rs:127:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/regex-1.3.2/src/literal/imp.rs:139:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/regex-1.3.2/src/literal/imp.rs:144:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/regex-1.3.2/src/literal/imp.rs:149:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/regex-1.3.2/src/literal/imp.rs:154:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/regex-1.3.2/src/literal/imp.rs:155:13 clippy::enum_glob_use "usage of wildcard import for enum variants" +target/lintcheck/sources/regex-1.3.2/src/literal/imp.rs:160:30 clippy::match_same_arms "this `match` has identical arm bodies" +target/lintcheck/sources/regex-1.3.2/src/literal/imp.rs:167:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/regex-1.3.2/src/literal/imp.rs:168:13 clippy::enum_glob_use "usage of wildcard import for enum variants" +target/lintcheck/sources/regex-1.3.2/src/literal/imp.rs:211:20 clippy::redundant_else "redundant else block" +target/lintcheck/sources/regex-1.3.2/src/literal/imp.rs:276:50 clippy::match_same_arms "this `match` has identical arm bodies" +target/lintcheck/sources/regex-1.3.2/src/literal/imp.rs:342:41 clippy::inline_always "you have declared `#[inline(always)]` on `find`. This is usually a bad idea" +target/lintcheck/sources/regex-1.3.2/src/literal/imp.rs:435:13 clippy::redundant_field_names "redundant field names in struct initialization" +target/lintcheck/sources/regex-1.3.2/src/literal/imp.rs:436:13 clippy::redundant_field_names "redundant field names in struct initialization" +target/lintcheck/sources/regex-1.3.2/src/literal/imp.rs:437:13 clippy::redundant_field_names "redundant field names in struct initialization" +target/lintcheck/sources/regex-1.3.2/src/literal/imp.rs:438:13 clippy::redundant_field_names "redundant field names in struct initialization" +target/lintcheck/sources/regex-1.3.2/src/literal/imp.rs:439:13 clippy::redundant_field_names "redundant field names in struct initialization" +target/lintcheck/sources/regex-1.3.2/src/literal/imp.rs:440:13 clippy::redundant_field_names "redundant field names in struct initialization" +target/lintcheck/sources/regex-1.3.2/src/literal/imp.rs:455:41 clippy::inline_always "you have declared `#[inline(always)]` on `find`. This is usually a bad idea" +target/lintcheck/sources/regex-1.3.2/src/literal/imp.rs:46:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/regex-1.3.2/src/literal/imp.rs:481:41 clippy::inline_always "you have declared `#[inline(always)]` on `is_suffix`. This is usually a bad idea" +target/lintcheck/sources/regex-1.3.2/src/literal/imp.rs:51:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/regex-1.3.2/src/literal/imp.rs:579:13 clippy::redundant_field_names "redundant field names in struct initialization" +target/lintcheck/sources/regex-1.3.2/src/literal/imp.rs:57:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/regex-1.3.2/src/literal/imp.rs:580:13 clippy::redundant_field_names "redundant field names in struct initialization" +target/lintcheck/sources/regex-1.3.2/src/literal/imp.rs:583:13 clippy::redundant_field_names "redundant field names in struct initialization" +target/lintcheck/sources/regex-1.3.2/src/literal/imp.rs:602:9 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope" +target/lintcheck/sources/regex-1.3.2/src/literal/imp.rs:622:24 clippy::redundant_else "redundant else block" +target/lintcheck/sources/regex-1.3.2/src/literal/imp.rs:62:18 clippy::needless_pass_by_value "this argument is passed by value, but not consumed in the function body" +target/lintcheck/sources/regex-1.3.2/src/literal/imp.rs:637:24 clippy::redundant_else "redundant else block" +target/lintcheck/sources/regex-1.3.2/src/literal/imp.rs:648:9 clippy::needless_return "unneeded `return` statement" +target/lintcheck/sources/regex-1.3.2/src/literal/imp.rs:651:44 clippy::doc_markdown "you should put `BoyerMooreSearch` between ticks in the documentation" +target/lintcheck/sources/regex-1.3.2/src/literal/imp.rs:65:13 clippy::redundant_field_names "redundant field names in struct initialization" +target/lintcheck/sources/regex-1.3.2/src/literal/imp.rs:68:13 clippy::redundant_field_names "redundant field names in struct initialization" +target/lintcheck/sources/regex-1.3.2/src/literal/imp.rs:783:32 clippy::redundant_else "redundant else block" +target/lintcheck/sources/regex-1.3.2/src/literal/imp.rs:786:42 clippy::manual_saturating_arithmetic "manual saturating arithmetic" +target/lintcheck/sources/regex-1.3.2/src/literal/imp.rs:78:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/regex-1.3.2/src/literal/imp.rs:84:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/regex-1.3.2/src/literal/imp.rs:850:20 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/regex-1.3.2/src/literal/imp.rs:85:13 clippy::enum_glob_use "usage of wildcard import for enum variants" +target/lintcheck/sources/regex-1.3.2/src/pikevm.rs:103:15 clippy::redundant_field_names "redundant field names in struct initialization" +target/lintcheck/sources/regex-1.3.2/src/pikevm.rs:103:52 clippy::redundant_field_names "redundant field names in struct initialization" +target/lintcheck/sources/regex-1.3.2/src/pikevm.rs:114:5 clippy::too_many_arguments "this function has too many arguments (8/7)" +target/lintcheck/sources/regex-1.3.2/src/pikevm.rs:117:13 clippy::similar_names "binding's name is too similar to existing binding" +target/lintcheck/sources/regex-1.3.2/src/pikevm.rs:124:17 clippy::similar_names "binding's name is too similar to existing binding" +target/lintcheck/sources/regex-1.3.2/src/pikevm.rs:220:9 clippy::doc_markdown "you should put `thread_caps` between ticks in the documentation" +target/lintcheck/sources/regex-1.3.2/src/pikevm.rs:222:16 clippy::doc_markdown "you should put `at_next` between ticks in the documentation" +target/lintcheck/sources/regex-1.3.2/src/pikevm.rs:223:9 clippy::doc_markdown "you should put `at_next` between ticks in the documentation" +target/lintcheck/sources/regex-1.3.2/src/pikevm.rs:224:5 clippy::too_many_arguments "this function has too many arguments (8/7)" +target/lintcheck/sources/regex-1.3.2/src/pikevm.rs:234:13 clippy::enum_glob_use "usage of wildcard import for enum variants" +target/lintcheck/sources/regex-1.3.2/src/pikevm.rs:303:13 clippy::enum_glob_use "usage of wildcard import for enum variants" +target/lintcheck/sources/regex-1.3.2/src/pikevm.rs:331:29 clippy::mut_mut "this expression mutably borrows a mutable reference. Consider reborrowing" +target/lintcheck/sources/regex-1.3.2/src/pikevm.rs:88:5 clippy::too_many_arguments "this function has too many arguments (8/7)" +target/lintcheck/sources/regex-1.3.2/src/prog.rs:102:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/regex-1.3.2/src/prog.rs:113:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/regex-1.3.2/src/prog.rs:120:9 clippy::match_like_matches_macro "match expression looks like `matches!` macro" +target/lintcheck/sources/regex-1.3.2/src/prog.rs:128:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/regex-1.3.2/src/prog.rs:134:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/regex-1.3.2/src/prog.rs:141:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/regex-1.3.2/src/prog.rs:147:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/regex-1.3.2/src/prog.rs:164:41 clippy::inline_always "you have declared `#[inline(always)]` on `deref`. This is usually a bad idea" +target/lintcheck/sources/regex-1.3.2/src/prog.rs:172:13 clippy::enum_glob_use "usage of wildcard import for enum variants" +target/lintcheck/sources/regex-1.3.2/src/prog.rs:18:1 clippy::struct_excessive_bools "more than 3 bools in a struct" +target/lintcheck/sources/regex-1.3.2/src/prog.rs:236:13 clippy::write_with_newline "using `write!()` with a format string that ends in a single newline" +target/lintcheck/sources/regex-1.3.2/src/prog.rs:300:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/regex-1.3.2/src/prog.rs:301:9 clippy::match_like_matches_macro "match expression looks like `matches!` macro" +target/lintcheck/sources/regex-1.3.2/src/prog.rs:382:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/regex-1.3.2/src/prog.rs:409:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/regex-1.3.2/src/prog.rs:80:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/regex-1.3.2/src/prog.rs:80:5 clippy::new_without_default "you should consider adding a `Default` implementation for `prog::Program`" +target/lintcheck/sources/regex-1.3.2/src/re_builder.rs:267:17 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/regex-1.3.2/src/re_builder.rs:267:17 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/regex-1.3.2/src/re_builder.rs:4:1 clippy::struct_excessive_bools "more than 3 bools in a struct" +target/lintcheck/sources/regex-1.3.2/src/re_builder.rs:57:17 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/regex-1.3.2/src/re_builder.rs:57:17 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/regex-1.3.2/src/re_builder.rs:68:17 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/regex-1.3.2/src/re_builder.rs:68:17 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/regex-1.3.2/src/re_bytes.rs:1017:9 clippy::map_unwrap_or "called `map(<f>).unwrap_or_else(<g>)` on an `Option` value. This can be done more directly by calling `map_or_else(<g>, <f>)` instead" +target/lintcheck/sources/regex-1.3.2/src/re_bytes.rs:1039:9 clippy::map_unwrap_or "called `map(<f>).unwrap_or_else(<g>)` on an `Option` value. This can be done more directly by calling `map_or_else(<g>, <f>)` instead" +target/lintcheck/sources/regex-1.3.2/src/re_bytes.rs:1093:5 clippy::needless_lifetimes "explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration)" +target/lintcheck/sources/regex-1.3.2/src/re_bytes.rs:1118:5 clippy::needless_lifetimes "explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration)" +target/lintcheck/sources/regex-1.3.2/src/re_bytes.rs:1133:5 clippy::needless_lifetimes "explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration)" +target/lintcheck/sources/regex-1.3.2/src/re_bytes.rs:118:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/regex-1.3.2/src/re_bytes.rs:256:13 clippy::redundant_field_names "redundant field names in struct initialization" +target/lintcheck/sources/regex-1.3.2/src/re_bytes.rs:29:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/regex-1.3.2/src/re_bytes.rs:35:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/regex-1.3.2/src/re_bytes.rs:42:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/regex-1.3.2/src/re_bytes.rs:483:5 clippy::missing_panics_doc "docs for function which may panic missing `# Panics` section" +target/lintcheck/sources/regex-1.3.2/src/re_bytes.rs:48:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/regex-1.3.2/src/re_bytes.rs:558:29 clippy::doc_markdown "you should put `shortest_match` between ticks in the documentation" +target/lintcheck/sources/regex-1.3.2/src/re_bytes.rs:55:33 clippy::redundant_field_names "redundant field names in struct initialization" +target/lintcheck/sources/regex-1.3.2/src/re_bytes.rs:55:47 clippy::redundant_field_names "redundant field names in struct initialization" +target/lintcheck/sources/regex-1.3.2/src/re_bytes.rs:572:29 clippy::doc_markdown "you should put `is_match` between ticks in the documentation" +target/lintcheck/sources/regex-1.3.2/src/re_bytes.rs:720:13 clippy::redundant_field_names "redundant field names in struct initialization" +target/lintcheck/sources/regex-1.3.2/src/re_bytes.rs:817:5 clippy::doc_markdown "you should put `CaptureLocations` between ticks in the documentation" +target/lintcheck/sources/regex-1.3.2/src/re_bytes.rs:849:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/regex-1.3.2/src/re_bytes.rs:858:5 clippy::len_without_is_empty "struct `CaptureLocations` has a public `len` method, but no `is_empty` method" +target/lintcheck/sources/regex-1.3.2/src/re_bytes.rs:858:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/regex-1.3.2/src/re_bytes.rs:869:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/regex-1.3.2/src/re_bytes.rs:911:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/regex-1.3.2/src/re_bytes.rs:917:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/regex-1.3.2/src/re_bytes.rs:926:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/regex-1.3.2/src/re_bytes.rs:955:5 clippy::len_without_is_empty "struct `Captures` has a public `len` method, but no `is_empty` method" +target/lintcheck/sources/regex-1.3.2/src/re_bytes.rs:955:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/regex-1.3.2/src/re_set.rs:179:13 clippy::redundant_field_names "redundant field names in struct initialization" +target/lintcheck/sources/regex-1.3.2/src/re_set.rs:179:13 clippy::redundant_field_names "redundant field names in struct initialization" +target/lintcheck/sources/regex-1.3.2/src/re_set.rs:206:5 clippy::len_without_is_empty "struct `RegexSet` has a public `len` method, but no `is_empty` method" +target/lintcheck/sources/regex-1.3.2/src/re_set.rs:206:5 clippy::len_without_is_empty "struct `RegexSet` has a public `len` method, but no `is_empty` method" +target/lintcheck/sources/regex-1.3.2/src/re_set.rs:251:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/regex-1.3.2/src/re_set.rs:251:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/regex-1.3.2/src/re_set.rs:263:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/regex-1.3.2/src/re_set.rs:263:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/regex-1.3.2/src/re_set.rs:268:5 clippy::len_without_is_empty "struct `SetMatches` has a public `len` method, but no `is_empty` method" +target/lintcheck/sources/regex-1.3.2/src/re_set.rs:268:5 clippy::len_without_is_empty "struct `SetMatches` has a public `len` method, but no `is_empty` method" +target/lintcheck/sources/regex-1.3.2/src/re_set.rs:268:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/regex-1.3.2/src/re_set.rs:268:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/regex-1.3.2/src/re_set.rs:277:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/regex-1.3.2/src/re_set.rs:277:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/regex-1.3.2/src/re_set.rs:94:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/regex-1.3.2/src/re_set.rs:94:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/regex-1.3.2/src/re_trait.rs:136:29 clippy::redundant_field_names "redundant field names in struct initialization" +target/lintcheck/sources/regex-1.3.2/src/re_unicode.rs:1019:9 clippy::map_unwrap_or "called `map(<f>).unwrap_or_else(<g>)` on an `Option` value. This can be done more directly by calling `map_or_else(<g>, <f>)` instead" +target/lintcheck/sources/regex-1.3.2/src/re_unicode.rs:1041:9 clippy::map_unwrap_or "called `map(<f>).unwrap_or_else(<g>)` on an `Option` value. This can be done more directly by calling `map_or_else(<g>, <f>)` instead" +target/lintcheck/sources/regex-1.3.2/src/re_unicode.rs:1088:13 clippy::redundant_field_names "redundant field names in struct initialization" +target/lintcheck/sources/regex-1.3.2/src/re_unicode.rs:1135:5 clippy::needless_lifetimes "explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration)" +target/lintcheck/sources/regex-1.3.2/src/re_unicode.rs:1160:5 clippy::needless_lifetimes "explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration)" +target/lintcheck/sources/regex-1.3.2/src/re_unicode.rs:174:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/regex-1.3.2/src/re_unicode.rs:21:1 clippy::must_use_candidate "this function could have a `#[must_use]` attribute" +target/lintcheck/sources/regex-1.3.2/src/re_unicode.rs:313:13 clippy::redundant_field_names "redundant field names in struct initialization" +target/lintcheck/sources/regex-1.3.2/src/re_unicode.rs:38:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/regex-1.3.2/src/re_unicode.rs:44:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/regex-1.3.2/src/re_unicode.rs:51:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/regex-1.3.2/src/re_unicode.rs:533:5 clippy::missing_panics_doc "docs for function which may panic missing `# Panics` section" +target/lintcheck/sources/regex-1.3.2/src/re_unicode.rs:57:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/regex-1.3.2/src/re_unicode.rs:617:29 clippy::doc_markdown "you should put `shortest_match` between ticks in the documentation" +target/lintcheck/sources/regex-1.3.2/src/re_unicode.rs:631:29 clippy::doc_markdown "you should put `is_match` between ticks in the documentation" +target/lintcheck/sources/regex-1.3.2/src/re_unicode.rs:64:33 clippy::redundant_field_names "redundant field names in struct initialization" +target/lintcheck/sources/regex-1.3.2/src/re_unicode.rs:64:47 clippy::redundant_field_names "redundant field names in struct initialization" +target/lintcheck/sources/regex-1.3.2/src/re_unicode.rs:834:5 clippy::doc_markdown "you should put `CaptureLocations` between ticks in the documentation" +target/lintcheck/sources/regex-1.3.2/src/re_unicode.rs:866:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/regex-1.3.2/src/re_unicode.rs:875:5 clippy::len_without_is_empty "struct `CaptureLocations` has a public `len` method, but no `is_empty` method" +target/lintcheck/sources/regex-1.3.2/src/re_unicode.rs:875:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/regex-1.3.2/src/re_unicode.rs:886:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/regex-1.3.2/src/re_unicode.rs:928:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/regex-1.3.2/src/re_unicode.rs:934:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/regex-1.3.2/src/re_unicode.rs:943:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/regex-1.3.2/src/re_unicode.rs:972:5 clippy::len_without_is_empty "struct `Captures` has a public `len` method, but no `is_empty` method" +target/lintcheck/sources/regex-1.3.2/src/re_unicode.rs:972:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/regex-1.3.2/src/sparse.rs:10:37 clippy::doc_markdown "you should put bare URLs between `<`/`>` or make a proper Markdown link" +target/lintcheck/sources/regex-1.3.2/src/sparse.rs:15:1 clippy::module_name_repetitions "item name starts with its containing module's name" +target/lintcheck/sources/regex-1.3.2/src/utf8.rs:100:16 clippy::unusual_byte_groupings "digits of hex or binary literal not grouped by four" +target/lintcheck/sources/regex-1.3.2/src/utf8.rs:103:16 clippy::unusual_byte_groupings "digits of hex or binary literal not grouped by four" +target/lintcheck/sources/regex-1.3.2/src/utf8.rs:106:22 clippy::cast_lossless "casting `u8` to `u32` may become silently lossy if you later change the type" +target/lintcheck/sources/regex-1.3.2/src/utf8.rs:107:19 clippy::cast_lossless "casting `u8` to `u32` may become silently lossy if you later change the type" +target/lintcheck/sources/regex-1.3.2/src/utf8.rs:108:19 clippy::cast_lossless "casting `u8` to `u32` may become silently lossy if you later change the type" +target/lintcheck/sources/regex-1.3.2/src/utf8.rs:109:19 clippy::cast_lossless "casting `u8` to `u32` may become silently lossy if you later change the type" +target/lintcheck/sources/regex-1.3.2/src/utf8.rs:111:27 clippy::unreadable_literal "long literal lacking separators" +target/lintcheck/sources/regex-1.3.2/src/utf8.rs:121:1 clippy::module_name_repetitions "item name ends with its containing module's name" +target/lintcheck/sources/regex-1.3.2/src/utf8.rs:143:24 clippy::unusual_byte_groupings "digits of hex or binary literal not grouped by four" +target/lintcheck/sources/regex-1.3.2/src/utf8.rs:143:9 clippy::unusual_byte_groupings "digits of hex or binary literal not grouped by four" +target/lintcheck/sources/regex-1.3.2/src/utf8.rs:23:1 clippy::module_name_repetitions "item name ends with its containing module's name" +target/lintcheck/sources/regex-1.3.2/src/utf8.rs:30:20 clippy::unusual_byte_groupings "digits of hex or binary literal not grouped by four" +target/lintcheck/sources/regex-1.3.2/src/utf8.rs:51:1 clippy::module_name_repetitions "item name ends with its containing module's name" +target/lintcheck/sources/regex-1.3.2/src/utf8.rs:58:23 clippy::unusual_byte_groupings "digits of hex or binary literal not grouped by four" +target/lintcheck/sources/regex-1.3.2/src/utf8.rs:58:9 clippy::unusual_byte_groupings "digits of hex or binary literal not grouped by four" +target/lintcheck/sources/regex-1.3.2/src/utf8.rs:63:16 clippy::unusual_byte_groupings "digits of hex or binary literal not grouped by four" +target/lintcheck/sources/regex-1.3.2/src/utf8.rs:66:22 clippy::cast_lossless "casting `u8` to `u32` may become silently lossy if you later change the type" +target/lintcheck/sources/regex-1.3.2/src/utf8.rs:66:54 clippy::cast_lossless "casting `u8` to `u32` may become silently lossy if you later change the type" +target/lintcheck/sources/regex-1.3.2/src/utf8.rs:77:16 clippy::unusual_byte_groupings "digits of hex or binary literal not grouped by four" +target/lintcheck/sources/regex-1.3.2/src/utf8.rs:80:16 clippy::unusual_byte_groupings "digits of hex or binary literal not grouped by four" +target/lintcheck/sources/regex-1.3.2/src/utf8.rs:83:22 clippy::cast_lossless "casting `u8` to `u32` may become silently lossy if you later change the type" +target/lintcheck/sources/regex-1.3.2/src/utf8.rs:84:19 clippy::cast_lossless "casting `u8` to `u32` may become silently lossy if you later change the type" +target/lintcheck/sources/regex-1.3.2/src/utf8.rs:85:19 clippy::cast_lossless "casting `u8` to `u32` may become silently lossy if you later change the type" +target/lintcheck/sources/regex-1.3.2/src/utf8.rs:92:23 clippy::unusual_byte_groupings "digits of hex or binary literal not grouped by four" +target/lintcheck/sources/regex-1.3.2/src/utf8.rs:92:9 clippy::unusual_byte_groupings "digits of hex or binary literal not grouped by four" +target/lintcheck/sources/regex-1.3.2/src/utf8.rs:97:16 clippy::unusual_byte_groupings "digits of hex or binary literal not grouped by four" +target/lintcheck/sources/ripgrep-12.1.1/build.rs:133:19 clippy::option_as_ref_deref "called `.as_ref().map(|x| &**x)` on an Option value. This can be done more directly by calling `githash.as_deref()` instead" +target/lintcheck/sources/ripgrep-12.1.1/build.rs:18:18 clippy::single_match_else "you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let`" +target/lintcheck/sources/ripgrep-12.1.1/build.rs:225:14 clippy::redundant_closure_for_method_calls "redundant closure" +target/lintcheck/sources/ripgrep-12.1.1/build.rs:92:19 clippy::option_as_ref_deref "called `.as_ref().map(|x| &**x)` on an Option value. This can be done more directly by calling `githash.as_deref()` instead" +target/lintcheck/sources/ripgrep-12.1.1/crates/core/app.rs:1408:5 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope" +target/lintcheck/sources/ripgrep-12.1.1/crates/core/app.rs:1408:5 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope" +target/lintcheck/sources/ripgrep-12.1.1/crates/core/app.rs:1409:5 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope" +target/lintcheck/sources/ripgrep-12.1.1/crates/core/app.rs:1409:5 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope" +target/lintcheck/sources/ripgrep-12.1.1/crates/core/app.rs:152:32 clippy::doc_markdown "you should put `clap::Arg` between ticks in the documentation" +target/lintcheck/sources/ripgrep-12.1.1/crates/core/app.rs:152:32 clippy::doc_markdown "you should put `clap::Arg` between ticks in the documentation" +target/lintcheck/sources/ripgrep-12.1.1/crates/core/app.rs:156:39 clippy::doc_markdown "you should put `clap::Arg` between ticks in the documentation" +target/lintcheck/sources/ripgrep-12.1.1/crates/core/app.rs:156:39 clippy::doc_markdown "you should put `clap::Arg` between ticks in the documentation" +target/lintcheck/sources/ripgrep-12.1.1/crates/core/app.rs:156:5 clippy::doc_markdown "you should put `RGArg` between ticks in the documentation" +target/lintcheck/sources/ripgrep-12.1.1/crates/core/app.rs:156:5 clippy::doc_markdown "you should put `RGArg` between ticks in the documentation" +target/lintcheck/sources/ripgrep-12.1.1/crates/core/app.rs:1668:5 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope" +target/lintcheck/sources/ripgrep-12.1.1/crates/core/app.rs:1668:5 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope" +target/lintcheck/sources/ripgrep-12.1.1/crates/core/app.rs:1669:5 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope" +target/lintcheck/sources/ripgrep-12.1.1/crates/core/app.rs:1669:5 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope" +target/lintcheck/sources/ripgrep-12.1.1/crates/core/app.rs:1821:5 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope" +target/lintcheck/sources/ripgrep-12.1.1/crates/core/app.rs:1821:5 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope" +target/lintcheck/sources/ripgrep-12.1.1/crates/core/app.rs:1822:5 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope" +target/lintcheck/sources/ripgrep-12.1.1/crates/core/app.rs:1822:5 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope" +target/lintcheck/sources/ripgrep-12.1.1/crates/core/app.rs:2999:5 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope" +target/lintcheck/sources/ripgrep-12.1.1/crates/core/app.rs:2999:5 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope" +target/lintcheck/sources/ripgrep-12.1.1/crates/core/app.rs:3000:5 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope" +target/lintcheck/sources/ripgrep-12.1.1/crates/core/app.rs:3000:5 clippy::items_after_statements "adding items after statements is confusing, since items exist from the start of the scope" +target/lintcheck/sources/ripgrep-12.1.1/crates/core/app.rs:367:54 clippy::match_same_arms "this `match` has identical arm bodies" +target/lintcheck/sources/ripgrep-12.1.1/crates/core/app.rs:367:54 clippy::match_same_arms "this `match` has identical arm bodies" +target/lintcheck/sources/ripgrep-12.1.1/crates/core/app.rs:414:59 clippy::match_same_arms "this `match` has identical arm bodies" +target/lintcheck/sources/ripgrep-12.1.1/crates/core/app.rs:414:59 clippy::match_same_arms "this `match` has identical arm bodies" +target/lintcheck/sources/ripgrep-12.1.1/crates/core/app.rs:417:57 clippy::match_same_arms "this `match` has identical arm bodies" +target/lintcheck/sources/ripgrep-12.1.1/crates/core/app.rs:417:57 clippy::match_same_arms "this `match` has identical arm bodies" +target/lintcheck/sources/ripgrep-12.1.1/crates/core/app.rs:417:57 clippy::match_same_arms "this `match` has identical arm bodies" +target/lintcheck/sources/ripgrep-12.1.1/crates/core/app.rs:417:57 clippy::match_same_arms "this `match` has identical arm bodies" +target/lintcheck/sources/ripgrep-12.1.1/crates/core/app.rs:75:9 clippy::doc_markdown "you should put `RIPGREP_BUILD_GIT_HASH` between ticks in the documentation" +target/lintcheck/sources/ripgrep-12.1.1/crates/core/app.rs:75:9 clippy::doc_markdown "you should put `RIPGREP_BUILD_GIT_HASH` between ticks in the documentation" +target/lintcheck/sources/ripgrep-12.1.1/crates/core/app.rs:87:5 clippy::if_not_else "unnecessary boolean `not` operation" +target/lintcheck/sources/ripgrep-12.1.1/crates/core/app.rs:87:5 clippy::if_not_else "unnecessary boolean `not` operation" +target/lintcheck/sources/ripgrep-12.1.1/crates/core/args.rs:1143:22 clippy::unused_self "unused `self` argument" +target/lintcheck/sources/ripgrep-12.1.1/crates/core/args.rs:11:1 clippy::single_component_path_imports "this import is redundant" +target/lintcheck/sources/ripgrep-12.1.1/crates/core/args.rs:1209:74 clippy::if_same_then_else "this `if` has identical blocks" +target/lintcheck/sources/ripgrep-12.1.1/crates/core/args.rs:1282:13 clippy::similar_names "binding's name is too similar to existing binding" +target/lintcheck/sources/ripgrep-12.1.1/crates/core/args.rs:1430:22 clippy::unused_self "unused `self` argument" +target/lintcheck/sources/ripgrep-12.1.1/crates/core/args.rs:1438:21 clippy::doc_markdown "you should put `OsStr` between ticks in the documentation" +target/lintcheck/sources/ripgrep-12.1.1/crates/core/args.rs:1520:44 clippy::redundant_closure_for_method_calls "redundant closure" +target/lintcheck/sources/ripgrep-12.1.1/crates/core/args.rs:1524:5 clippy::unnecessary_wraps "this function's return value is unnecessarily wrapped by `Result`" +target/lintcheck/sources/ripgrep-12.1.1/crates/core/args.rs:1635:14 clippy::doc_markdown "you should put `values_of_lossy` between ticks in the documentation" +target/lintcheck/sources/ripgrep-12.1.1/crates/core/args.rs:1693:41 clippy::redundant_closure_for_method_calls "redundant closure" +target/lintcheck/sources/ripgrep-12.1.1/crates/core/args.rs:1770:17 clippy::cast_possible_truncation "casting `u64` to `usize` may truncate the value on targets with 32-bit wide pointers" +target/lintcheck/sources/ripgrep-12.1.1/crates/core/args.rs:1829:5 clippy::let_underscore_drop "non-binding `let` on a type that implements `Drop`" +target/lintcheck/sources/ripgrep-12.1.1/crates/core/args.rs:287:13 clippy::similar_names "binding's name is too similar to existing binding" +target/lintcheck/sources/ripgrep-12.1.1/crates/core/args.rs:33:1 clippy::single_component_path_imports "this import is redundant" +target/lintcheck/sources/ripgrep-12.1.1/crates/core/args.rs:34:1 clippy::single_component_path_imports "this import is redundant" +target/lintcheck/sources/ripgrep-12.1.1/crates/core/args.rs:35:1 clippy::single_component_path_imports "this import is redundant" +target/lintcheck/sources/ripgrep-12.1.1/crates/core/args.rs:369:5 clippy::upper_case_acronyms "name `JSON` contains a capitalized acronym" +target/lintcheck/sources/ripgrep-12.1.1/crates/core/args.rs:410:14 clippy::trivially_copy_pass_by_ref "this argument (2 byte) is passed by reference, but would be more efficient if passed by value (limit: 8 byte)" +target/lintcheck/sources/ripgrep-12.1.1/crates/core/args.rs:475:18 clippy::match_same_arms "this `match` has identical arm bodies" +target/lintcheck/sources/ripgrep-12.1.1/crates/core/args.rs:512:19 clippy::doc_markdown "you should put `ArgMatches` between ticks in the documentation" +target/lintcheck/sources/ripgrep-12.1.1/crates/core/args.rs:549:16 clippy::wrong_self_convention "methods with the following characteristics: (`to_*` and `self` type is not `Copy`) usually take `self` by reference" +target/lintcheck/sources/ripgrep-12.1.1/crates/core/args.rs:76:18 clippy::trivially_copy_pass_by_ref "this argument (1 byte) is passed by reference, but would be more efficient if passed by value (limit: 8 byte)" +target/lintcheck/sources/ripgrep-12.1.1/crates/core/args.rs:77:13 clippy::enum_glob_use "usage of wildcard import for enum variants" +target/lintcheck/sources/ripgrep-12.1.1/crates/core/args.rs:923:42 clippy::doc_markdown "you should put `BinaryDetection::quit` between ticks in the documentation" +target/lintcheck/sources/ripgrep-12.1.1/crates/core/config.rs:13:1 clippy::single_component_path_imports "this import is redundant" +target/lintcheck/sources/ripgrep-12.1.1/crates/core/config.rs:58:6 clippy::type_complexity "very complex type used. Consider factoring parts into `type` definitions" +target/lintcheck/sources/ripgrep-12.1.1/crates/core/config.rs:79:6 clippy::type_complexity "very complex type used. Consider factoring parts into `type` definitions" +target/lintcheck/sources/ripgrep-12.1.1/crates/core/logger.rs:11:30 clippy::doc_markdown "you should put `max_level` between ticks in the documentation" +target/lintcheck/sources/ripgrep-12.1.1/crates/core/logger.rs:15:16 clippy::redundant_static_lifetimes "constants have by default a `'static` lifetime" +target/lintcheck/sources/ripgrep-12.1.1/crates/core/main.rs:114:9 clippy::let_underscore_drop "non-binding `let` on a type that implements `Drop`" +target/lintcheck/sources/ripgrep-12.1.1/crates/core/main.rs:189:9 clippy::let_underscore_drop "non-binding `let` on a type that implements `Drop`" +target/lintcheck/sources/ripgrep-12.1.1/crates/core/main.rs:55:19 clippy::needless_pass_by_value "this argument is passed by value, but not consumed in the function body" +target/lintcheck/sources/ripgrep-12.1.1/crates/core/main.rs:56:9 clippy::enum_glob_use "usage of wildcard import for enum variants" +target/lintcheck/sources/ripgrep-12.1.1/crates/core/messages.rs:46:1 clippy::module_name_repetitions "item name ends with its containing module's name" +target/lintcheck/sources/ripgrep-12.1.1/crates/core/messages.rs:51:1 clippy::module_name_repetitions "item name ends with its containing module's name" +target/lintcheck/sources/ripgrep-12.1.1/crates/core/messages.rs:62:1 clippy::module_name_repetitions "item name ends with its containing module's name" +target/lintcheck/sources/ripgrep-12.1.1/crates/core/path_printer.rs:27:1 clippy::module_name_repetitions "item name starts with its containing module's name" +target/lintcheck/sources/ripgrep-12.1.1/crates/core/path_printer.rs:89:9 clippy::if_not_else "unnecessary boolean `not` operation" +target/lintcheck/sources/ripgrep-12.1.1/crates/core/search.rs:185:1 clippy::module_name_repetitions "item name starts with its containing module's name" +target/lintcheck/sources/ripgrep-12.1.1/crates/core/search.rs:224:5 clippy::upper_case_acronyms "name `JSON` contains a capitalized acronym" +target/lintcheck/sources/ripgrep-12.1.1/crates/core/search.rs:292:9 clippy::write_with_newline "using `write!()` with a format string that ends in a single newline" +target/lintcheck/sources/ripgrep-12.1.1/crates/core/search.rs:311:1 clippy::module_name_repetitions "item name starts with its containing module's name" +target/lintcheck/sources/ripgrep-12.1.1/crates/core/search.rs:377:12 clippy::nonminimal_bool "this boolean expression can be simplified" +target/lintcheck/sources/ripgrep-12.1.1/crates/core/search.rs:423:13 clippy::enum_glob_use "usage of wildcard import for enum variants" +target/lintcheck/sources/ripgrep-12.1.1/crates/core/search.rs:447:13 clippy::enum_glob_use "usage of wildcard import for enum variants" +target/lintcheck/sources/ripgrep-12.1.1/crates/core/search.rs:472:24 clippy::map_clone "you are using an explicit closure for cloning elements" +target/lintcheck/sources/ripgrep-12.1.1/crates/core/search.rs:472:41 clippy::redundant_closure_for_method_calls "redundant closure" +target/lintcheck/sources/ripgrep-12.1.1/crates/core/search.rs:480:24 clippy::map_clone "you are using an explicit closure for cloning elements" +target/lintcheck/sources/ripgrep-12.1.1/crates/core/search.rs:480:41 clippy::redundant_closure_for_method_calls "redundant closure" +target/lintcheck/sources/ripgrep-12.1.1/crates/core/search.rs:49:1 clippy::module_name_repetitions "item name starts with its containing module's name" +target/lintcheck/sources/ripgrep-12.1.1/crates/core/search.rs:509:24 clippy::map_clone "you are using an explicit closure for cloning elements" +target/lintcheck/sources/ripgrep-12.1.1/crates/core/search.rs:509:41 clippy::redundant_closure_for_method_calls "redundant closure" +target/lintcheck/sources/ripgrep-12.1.1/crates/core/search.rs:517:24 clippy::map_clone "you are using an explicit closure for cloning elements" +target/lintcheck/sources/ripgrep-12.1.1/crates/core/search.rs:517:41 clippy::redundant_closure_for_method_calls "redundant closure" +target/lintcheck/sources/ripgrep-12.1.1/crates/core/search.rs:533:36 clippy::cast_lossless "casting `u32` to `f64` may become silently lossy if you later change the type" +target/lintcheck/sources/ripgrep-12.1.1/crates/core/search.rs:533:5 clippy::cast_precision_loss "casting `u64` to `f64` causes a loss of precision (`u64` is 64 bits wide, but `f64`'s mantissa is only 52 bits wide)" +target/lintcheck/sources/ripgrep-12.1.1/crates/core/subject.rs:20:1 clippy::module_name_repetitions "item name starts with its containing module's name" +target/lintcheck/sources/ripgrep-12.1.1/crates/core/subject.rs:4:1 clippy::single_component_path_imports "this import is redundant" +target/lintcheck/sources/rpmalloc-0.2.0/src/lib.rs:103:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/rpmalloc-0.2.0/src/lib.rs:114:5 clippy::must_use_candidate "this method could have a `#[must_use]` attribute" +target/lintcheck/sources/rpmalloc-0.2.0/src/lib.rs:71:73 clippy::doc_markdown "you should put bare URLs between `<`/`>` or make a proper Markdown link" +target/lintcheck/sources/rpmalloc-0.2.0/src/lib.rs:72:50 clippy::doc_markdown "you should put bare URLs between `<`/`>` or make a proper Markdown link" +target/lintcheck/sources/rpmalloc-0.2.0/src/lib.rs:92:9 clippy::ptr_as_ptr "`as` casting between raw pointers without changing its mutability" +target/lintcheck/sources/rpmalloc-0.2.0/src/lib.rs:95:21 clippy::ptr_as_ptr "`as` casting between raw pointers without changing its mutability" +target/lintcheck/sources/serde-1.0.118/src/de/from_primitive.rs:103:24 clippy::wrong_self_convention "methods called `from_*` usually take no `self`" +target/lintcheck/sources/serde-1.0.118/src/de/from_primitive.rs:107:25 clippy::wrong_self_convention "methods called `from_*` usually take no `self`" +target/lintcheck/sources/serde-1.0.118/src/de/from_primitive.rs:111:25 clippy::wrong_self_convention "methods called `from_*` usually take no `self`" +target/lintcheck/sources/serde-1.0.118/src/de/from_primitive.rs:115:25 clippy::wrong_self_convention "methods called `from_*` usually take no `self`" +target/lintcheck/sources/serde-1.0.118/src/de/from_primitive.rs:48:24 clippy::wrong_self_convention "methods called `from_*` usually take no `self`" +target/lintcheck/sources/serde-1.0.118/src/de/from_primitive.rs:52:25 clippy::wrong_self_convention "methods called `from_*` usually take no `self`" +target/lintcheck/sources/serde-1.0.118/src/de/from_primitive.rs:56:25 clippy::wrong_self_convention "methods called `from_*` usually take no `self`" +target/lintcheck/sources/serde-1.0.118/src/de/from_primitive.rs:60:25 clippy::wrong_self_convention "methods called `from_*` usually take no `self`" +target/lintcheck/sources/serde-1.0.118/src/de/mod.rs:1592:9 clippy::let_underscore_drop "non-binding `let` on a type that implements `Drop`" +target/lintcheck/sources/serde-1.0.118/src/de/mod.rs:1616:9 clippy::let_underscore_drop "non-binding `let` on a type that implements `Drop`" +target/lintcheck/sources/serde-1.0.118/src/de/mod.rs:1627:9 clippy::let_underscore_drop "non-binding `let` on a type that implements `Drop`" +target/lintcheck/sources/serde-1.0.118/src/de/mod.rs:1638:9 clippy::let_underscore_drop "non-binding `let` on a type that implements `Drop`" +target/lintcheck/sources/serde-1.0.118/src/de/mod.rs:1649:9 clippy::let_underscore_drop "non-binding `let` on a type that implements `Drop`" +target/lintcheck/sources/serde-1.0.118/src/de/mod.rs:952:13 clippy::let_underscore_drop "non-binding `let` on a type that implements `Drop`" +target/lintcheck/sources/serde-1.0.118/src/de/mod.rs:986:13 clippy::let_underscore_drop "non-binding `let` on a type that implements `Drop`" +target/lintcheck/sources/serde_yaml-0.8.17/src/lib.rs:1:null clippy::cargo_common_metadata "package `serde_yaml` is missing `package.categories` metadata" +target/lintcheck/sources/syn-1.0.54/build.rs:1:null clippy::cargo_common_metadata "package `syn` is missing `package.keywords` metadata" +target/lintcheck/sources/syn-1.0.54/src/custom_keyword.rs:177:26 clippy::wrong_self_convention "methods with the following characteristics: (`to_*` and `self` type is `Copy`) usually take `self` by value" +target/lintcheck/sources/syn-1.0.54/src/gen/clone.rs:1900:13 clippy::match_wildcard_for_single_variants "wildcard matches only a single variant and will also match any future added variants" +target/lintcheck/sources/syn-1.0.54/src/generics.rs:1227:22 clippy::wrong_self_convention "methods with the following characteristics: (`to_*` and `self` type is `Copy`) usually take `self` by value" +target/lintcheck/sources/syn-1.0.54/src/lib.rs:1:null clippy::cargo_common_metadata "package `syn` is missing `package.keywords` metadata" +target/lintcheck/sources/syn-1.0.54/src/lit.rs:1397:40 clippy::redundant_else "redundant else block" +target/lintcheck/sources/syn-1.0.54/src/lit.rs:1405:28 clippy::redundant_else "redundant else block" +target/lintcheck/sources/syn-1.0.54/src/lit.rs:1485:32 clippy::redundant_else "redundant else block" +target/lintcheck/sources/syn-1.0.54/src/op.rs:190:22 clippy::wrong_self_convention "methods with the following characteristics: (`to_*` and `self` type is `Copy`) usually take `self` by value" +target/lintcheck/sources/syn-1.0.54/src/op.rs:226:22 clippy::wrong_self_convention "methods with the following characteristics: (`to_*` and `self` type is `Copy`) usually take `self` by value" +target/lintcheck/sources/syn-1.0.54/src/token.rs:311:30 clippy::wrong_self_convention "methods with the following characteristics: (`to_*` and `self` type is `Copy`) usually take `self` by value" +target/lintcheck/sources/syn-1.0.54/src/token.rs:446:30 clippy::wrong_self_convention "methods with the following characteristics: (`to_*` and `self` type is `Copy`) usually take `self` by value" +target/lintcheck/sources/syn-1.0.54/src/token.rs:563:18 clippy::wrong_self_convention "methods with the following characteristics: (`to_*` and `self` type is `Copy`) usually take `self` by value" +target/lintcheck/sources/syn-1.0.54/src/token.rs:974:5 clippy::missing_panics_doc "docs for function which may panic missing `# Panics` section" +target/lintcheck/sources/tame-oidc-0.1.0/src/errors.rs:9:5 clippy::upper_case_acronyms "name `HTTP` contains a capitalized acronym" +target/lintcheck/sources/tame-oidc-0.1.0/src/lib.rs:1:null clippy::cargo_common_metadata "package `tame-oidc` is missing `package.categories` metadata" +target/lintcheck/sources/tame-oidc-0.1.0/src/oidc.rs:111:1 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/tame-oidc-0.1.0/src/oidc.rs:127:1 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/tame-oidc-0.1.0/src/oidc.rs:52:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/tame-oidc-0.1.0/src/oidc.rs:60:1 clippy::from_over_into "an implementation of `From` is preferred since it gives you `Into<_>` for free where the reverse isn't true" +target/lintcheck/sources/tame-oidc-0.1.0/src/oidc.rs:76:1 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/tame-oidc-0.1.0/src/provider.rs:107:1 clippy::missing_panics_doc "docs for function which may panic missing `# Panics` section" +target/lintcheck/sources/tame-oidc-0.1.0/src/provider.rs:107:1 clippy::must_use_candidate "this function could have a `#[must_use]` attribute" +target/lintcheck/sources/tame-oidc-0.1.0/src/provider.rs:118:1 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/tame-oidc-0.1.0/src/provider.rs:143:1 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/tame-oidc-0.1.0/src/provider.rs:159:1 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/tame-oidc-0.1.0/src/provider.rs:26:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/tame-oidc-0.1.0/src/provider.rs:38:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/tame-oidc-0.1.0/src/provider.rs:57:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/tame-oidc-0.1.0/src/provider.rs:71:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/tame-oidc-0.1.0/src/provider.rs:95:5 clippy::missing_errors_doc "docs for function returning `Result` missing `# Errors` section" +target/lintcheck/sources/thiserror-1.0.24/src/lib.rs:1:null clippy::cargo_common_metadata "package `thiserror` is missing `package.keywords` metadata" +target/lintcheck/sources/unicode-xid-0.2.1/src/lib.rs:1:null clippy::cargo_common_metadata "package `unicode-xid` is missing `package.categories` metadata" +target/lintcheck/sources/unicode-xid-0.2.1/src/lib.rs:57:64 clippy::doc_markdown "you should put `XID_Start` between ticks in the documentation" +target/lintcheck/sources/unicode-xid-0.2.1/src/lib.rs:60:10 clippy::doc_markdown "you should put `XID_Start` between ticks in the documentation" +target/lintcheck/sources/unicode-xid-0.2.1/src/lib.rs:62:27 clippy::doc_markdown "you should put `ID_Start` between ticks in the documentation" +target/lintcheck/sources/unicode-xid-0.2.1/src/lib.rs:62:67 clippy::doc_markdown "you should put `NFKx` between ticks in the documentation" +target/lintcheck/sources/unicode-xid-0.2.1/src/lib.rs:63:21 clippy::wrong_self_convention "methods called `is_*` usually take `self` by reference or no `self`" +target/lintcheck/sources/unicode-xid-0.2.1/src/lib.rs:65:61 clippy::doc_markdown "you should put `XID_Continue` between ticks in the documentation" +target/lintcheck/sources/unicode-xid-0.2.1/src/lib.rs:68:10 clippy::doc_markdown "you should put `XID_Continue` between ticks in the documentation" +target/lintcheck/sources/unicode-xid-0.2.1/src/lib.rs:70:28 clippy::doc_markdown "you should put `ID_Continue` between ticks in the documentation" +target/lintcheck/sources/unicode-xid-0.2.1/src/lib.rs:70:72 clippy::doc_markdown "you should put `NFKx` between ticks in the documentation" +target/lintcheck/sources/unicode-xid-0.2.1/src/lib.rs:71:24 clippy::wrong_self_convention "methods called `is_*` usually take `self` by reference or no `self`" +target/lintcheck/sources/xsv-0.13.0/src/cmd/cat.rs:101:34 clippy::redundant_closure_for_method_calls "redundant closure" +target/lintcheck/sources/xsv-0.13.0/src/cmd/cat.rs:42:1 clippy::struct_excessive_bools "more than 3 bools in a struct" +target/lintcheck/sources/xsv-0.13.0/src/cmd/cat.rs:53:9 clippy::similar_names "binding's name is too similar to existing binding" +target/lintcheck/sources/xsv-0.13.0/src/cmd/cat.rs:7:16 clippy::redundant_static_lifetimes "statics have by default a `'static` lifetime" +target/lintcheck/sources/xsv-0.13.0/src/cmd/count.rs:32:9 clippy::similar_names "binding's name is too similar to existing binding" +target/lintcheck/sources/xsv-0.13.0/src/cmd/count.rs:38:9 clippy::single_match_else "you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let`" +target/lintcheck/sources/xsv-0.13.0/src/cmd/count.rs:42:33 clippy::unseparated_literal_suffix "integer type suffix should be separated by an underscore" +target/lintcheck/sources/xsv-0.13.0/src/cmd/count.rs:7:16 clippy::redundant_static_lifetimes "statics have by default a `'static` lifetime" +target/lintcheck/sources/xsv-0.13.0/src/cmd/fixlengths.rs:45:9 clippy::similar_names "binding's name is too similar to existing binding" +target/lintcheck/sources/xsv-0.13.0/src/cmd/fixlengths.rs:50:18 clippy::single_match_else "you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let`" +target/lintcheck/sources/xsv-0.13.0/src/cmd/fixlengths.rs:62:30 clippy::unseparated_literal_suffix "integer type suffix should be separated by an underscore" +target/lintcheck/sources/xsv-0.13.0/src/cmd/fixlengths.rs:9:16 clippy::redundant_static_lifetimes "statics have by default a `'static` lifetime" +target/lintcheck/sources/xsv-0.13.0/src/cmd/flatten.rs:10:16 clippy::redundant_static_lifetimes "statics have by default a `'static` lifetime" +target/lintcheck/sources/xsv-0.13.0/src/cmd/flatten.rs:51:9 clippy::similar_names "binding's name is too similar to existing binding" +target/lintcheck/sources/xsv-0.13.0/src/cmd/fmt.rs:50:9 clippy::similar_names "binding's name is too similar to existing binding" +target/lintcheck/sources/xsv-0.13.0/src/cmd/fmt.rs:55:13 clippy::similar_names "binding's name is too similar to existing binding" +target/lintcheck/sources/xsv-0.13.0/src/cmd/fmt.rs:7:16 clippy::redundant_static_lifetimes "statics have by default a `'static` lifetime" +target/lintcheck/sources/xsv-0.13.0/src/cmd/frequency.rs:148:43 clippy::cast_possible_truncation "casting `u64` to `usize` may truncate the value on targets with 32-bit wide pointers" +target/lintcheck/sources/xsv-0.13.0/src/cmd/frequency.rs:149:43 clippy::cast_possible_truncation "casting `u64` to `usize` may truncate the value on targets with 32-bit wide pointers" +target/lintcheck/sources/xsv-0.13.0/src/cmd/frequency.rs:15:16 clippy::redundant_static_lifetimes "statics have by default a `'static` lifetime" +target/lintcheck/sources/xsv-0.13.0/src/cmd/frequency.rs:169:13 clippy::similar_names "binding's name is too similar to existing binding" +target/lintcheck/sources/xsv-0.13.0/src/cmd/frequency.rs:176:17 clippy::if_not_else "unnecessary boolean `not` operation" +target/lintcheck/sources/xsv-0.13.0/src/cmd/frequency.rs:178:24 clippy::collapsible_else_if "this `else { if .. }` block can be collapsed" +target/lintcheck/sources/xsv-0.13.0/src/cmd/frequency.rs:77:9 clippy::similar_names "binding's name is too similar to existing binding" +target/lintcheck/sources/xsv-0.13.0/src/cmd/frequency.rs:93:31 clippy::explicit_into_iter_loop "it is more concise to loop over containers instead of using explicit iteration methods" +target/lintcheck/sources/xsv-0.13.0/src/cmd/headers.rs:43:9 clippy::similar_names "binding's name is too similar to existing binding" +target/lintcheck/sources/xsv-0.13.0/src/cmd/headers.rs:49:17 clippy::explicit_into_iter_loop "it is more concise to loop over containers instead of using explicit iteration methods" +target/lintcheck/sources/xsv-0.13.0/src/cmd/headers.rs:9:16 clippy::redundant_static_lifetimes "statics have by default a `'static` lifetime" +target/lintcheck/sources/xsv-0.13.0/src/cmd/index.rs:11:16 clippy::redundant_static_lifetimes "statics have by default a `'static` lifetime" +target/lintcheck/sources/xsv-0.13.0/src/cmd/index.rs:45:9 clippy::similar_names "binding's name is too similar to existing binding" +target/lintcheck/sources/xsv-0.13.0/src/cmd/input.rs:42:9 clippy::similar_names "binding's name is too similar to existing binding" +target/lintcheck/sources/xsv-0.13.0/src/cmd/input.rs:47:9 clippy::similar_names "binding's name is too similar to existing binding" +target/lintcheck/sources/xsv-0.13.0/src/cmd/input.rs:7:16 clippy::redundant_static_lifetimes "statics have by default a `'static` lifetime" +target/lintcheck/sources/xsv-0.13.0/src/cmd/join.rs:17:16 clippy::redundant_static_lifetimes "statics have by default a `'static` lifetime" +target/lintcheck/sources/xsv-0.13.0/src/cmd/join.rs:194:29 clippy::similar_names "binding's name is too similar to existing binding" +target/lintcheck/sources/xsv-0.13.0/src/cmd/join.rs:224:22 clippy::similar_names "binding's name is too similar to existing binding" +target/lintcheck/sources/xsv-0.13.0/src/cmd/join.rs:293:14 clippy::similar_names "binding's name is too similar to existing binding" +target/lintcheck/sources/xsv-0.13.0/src/cmd/join.rs:293:20 clippy::similar_names "binding's name is too similar to existing binding" +target/lintcheck/sources/xsv-0.13.0/src/cmd/join.rs:297:13 clippy::redundant_field_names "redundant field names in struct initialization" +target/lintcheck/sources/xsv-0.13.0/src/cmd/join.rs:298:13 clippy::redundant_field_names "redundant field names in struct initialization" +target/lintcheck/sources/xsv-0.13.0/src/cmd/join.rs:299:13 clippy::redundant_field_names "redundant field names in struct initialization" +target/lintcheck/sources/xsv-0.13.0/src/cmd/join.rs:300:13 clippy::redundant_field_names "redundant field names in struct initialization" +target/lintcheck/sources/xsv-0.13.0/src/cmd/join.rs:308:9 clippy::unused_self "unused `self` argument" +target/lintcheck/sources/xsv-0.13.0/src/cmd/join.rs:342:38 clippy::unseparated_literal_suffix "integer type suffix should be separated by an underscore" +target/lintcheck/sources/xsv-0.13.0/src/cmd/join.rs:342:46 clippy::unseparated_literal_suffix "integer type suffix should be separated by an underscore" +target/lintcheck/sources/xsv-0.13.0/src/cmd/join.rs:347:9 clippy::if_not_else "unnecessary boolean `not` operation" +target/lintcheck/sources/xsv-0.13.0/src/cmd/join.rs:372:44 clippy::redundant_closure_for_method_calls "redundant closure" +target/lintcheck/sources/xsv-0.13.0/src/cmd/join.rs:375:33 clippy::similar_names "binding's name is too similar to existing binding" +target/lintcheck/sources/xsv-0.13.0/src/cmd/join.rs:392:13 clippy::redundant_field_names "redundant field names in struct initialization" +target/lintcheck/sources/xsv-0.13.0/src/cmd/join.rs:403:29 clippy::explicit_into_iter_loop "it is more concise to loop over containers instead of using explicit iteration methods" +target/lintcheck/sources/xsv-0.13.0/src/cmd/join.rs:406:57 clippy::implicit_clone "implicitly cloning a `Vec` by calling `to_vec` on its dereferenced type" +target/lintcheck/sources/xsv-0.13.0/src/cmd/join.rs:426:13 clippy::if_not_else "unnecessary boolean `not` operation" +target/lintcheck/sources/xsv-0.13.0/src/cmd/join.rs:77:1 clippy::struct_excessive_bools "more than 3 bools in a struct" +target/lintcheck/sources/xsv-0.13.0/src/cmd/join.rs:94:9 clippy::similar_names "binding's name is too similar to existing binding" +target/lintcheck/sources/xsv-0.13.0/src/cmd/partition.rs:105:22 clippy::similar_names "binding's name is too similar to existing binding" +target/lintcheck/sources/xsv-0.13.0/src/cmd/partition.rs:106:22 clippy::redundant_slicing "redundant slicing of the whole range" +target/lintcheck/sources/xsv-0.13.0/src/cmd/partition.rs:139:13 clippy::redundant_field_names "redundant field names in struct initialization" +target/lintcheck/sources/xsv-0.13.0/src/cmd/partition.rs:15:16 clippy::redundant_static_lifetimes "statics have by default a `'static` lifetime" +target/lintcheck/sources/xsv-0.13.0/src/cmd/partition.rs:169:9 clippy::if_not_else "unnecessary boolean `not` operation" +target/lintcheck/sources/xsv-0.13.0/src/cmd/partition.rs:56:9 clippy::similar_names "binding's name is too similar to existing binding" +target/lintcheck/sources/xsv-0.13.0/src/cmd/partition.rs:77:9 clippy::unused_self "unused `self` argument" +target/lintcheck/sources/xsv-0.13.0/src/cmd/sample.rs:105:44 clippy::cast_possible_truncation "casting `u64` to `usize` may truncate the value on targets with 32-bit wide pointers" +target/lintcheck/sources/xsv-0.13.0/src/cmd/sample.rs:115:21 clippy::cast_possible_truncation "casting `u64` to `usize` may truncate the value on targets with 32-bit wide pointers" +target/lintcheck/sources/xsv-0.13.0/src/cmd/sample.rs:11:16 clippy::redundant_static_lifetimes "statics have by default a `'static` lifetime" +target/lintcheck/sources/xsv-0.13.0/src/cmd/sample.rs:51:9 clippy::similar_names "binding's name is too similar to existing binding" +target/lintcheck/sources/xsv-0.13.0/src/cmd/sample.rs:58:19 clippy::single_match_else "you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let`" +target/lintcheck/sources/xsv-0.13.0/src/cmd/sample.rs:75:16 clippy::explicit_into_iter_loop "it is more concise to loop over containers instead of using explicit iteration methods" +target/lintcheck/sources/xsv-0.13.0/src/cmd/sample.rs:91:42 clippy::cast_possible_truncation "casting `u64` to `usize` may truncate the value on targets with 32-bit wide pointers" +target/lintcheck/sources/xsv-0.13.0/src/cmd/sample.rs:92:43 clippy::cast_possible_truncation "casting `u64` to `usize` may truncate the value on targets with 32-bit wide pointers" +target/lintcheck/sources/xsv-0.13.0/src/cmd/search.rs:51:9 clippy::similar_names "binding's name is too similar to existing binding" +target/lintcheck/sources/xsv-0.13.0/src/cmd/search.rs:9:16 clippy::redundant_static_lifetimes "statics have by default a `'static` lifetime" +target/lintcheck/sources/xsv-0.13.0/src/cmd/select.rs:60:9 clippy::similar_names "binding's name is too similar to existing binding" +target/lintcheck/sources/xsv-0.13.0/src/cmd/select.rs:8:16 clippy::redundant_static_lifetimes "statics have by default a `'static` lifetime" +target/lintcheck/sources/xsv-0.13.0/src/cmd/slice.rs:57:9 clippy::similar_names "binding's name is too similar to existing binding" +target/lintcheck/sources/xsv-0.13.0/src/cmd/slice.rs:9:16 clippy::redundant_static_lifetimes "statics have by default a `'static` lifetime" +target/lintcheck/sources/xsv-0.13.0/src/cmd/sort.rs:11:16 clippy::redundant_static_lifetimes "statics have by default a `'static` lifetime" +target/lintcheck/sources/xsv-0.13.0/src/cmd/sort.rs:138:47 clippy::cast_precision_loss "casting `i64` to `f64` causes a loss of precision (`i64` is 64 bits wide, but `f64`'s mantissa is only 52 bits wide)" +target/lintcheck/sources/xsv-0.13.0/src/cmd/sort.rs:139:51 clippy::cast_precision_loss "casting `i64` to `f64` causes a loss of precision (`i64` is 64 bits wide, but `f64`'s mantissa is only 52 bits wide)" +target/lintcheck/sources/xsv-0.13.0/src/cmd/sort.rs:48:9 clippy::similar_names "binding's name is too similar to existing binding" +target/lintcheck/sources/xsv-0.13.0/src/cmd/sort.rs:91:14 clippy::explicit_into_iter_loop "it is more concise to loop over containers instead of using explicit iteration methods" +target/lintcheck/sources/xsv-0.13.0/src/cmd/split.rs:14:16 clippy::redundant_static_lifetimes "statics have by default a `'static` lifetime" +target/lintcheck/sources/xsv-0.13.0/src/cmd/split.rs:61:9 clippy::similar_names "binding's name is too similar to existing binding" +target/lintcheck/sources/xsv-0.13.0/src/cmd/split.rs:94:5 clippy::unnecessary_wraps "this function's return value is unnecessary" +target/lintcheck/sources/xsv-0.13.0/src/cmd/split.rs:96:14 clippy::needless_pass_by_value "this argument is passed by value, but not consumed in the function body" +target/lintcheck/sources/xsv-0.13.0/src/cmd/split.rs:99:13 clippy::cast_possible_truncation "casting `u64` to `usize` may truncate the value on targets with 32-bit wide pointers" +target/lintcheck/sources/xsv-0.13.0/src/cmd/stats.rs:110:36 clippy::redundant_closure_for_method_calls "redundant closure" +target/lintcheck/sources/xsv-0.13.0/src/cmd/stats.rs:127:14 clippy::needless_pass_by_value "this argument is passed by value, but not consumed in the function body" +target/lintcheck/sources/xsv-0.13.0/src/cmd/stats.rs:138:43 clippy::cast_possible_truncation "casting `u64` to `usize` may truncate the value on targets with 32-bit wide pointers" +target/lintcheck/sources/xsv-0.13.0/src/cmd/stats.rs:139:43 clippy::cast_possible_truncation "casting `u64` to `usize` may truncate the value on targets with 32-bit wide pointers" +target/lintcheck/sources/xsv-0.13.0/src/cmd/stats.rs:162:25 clippy::explicit_into_iter_loop "it is more concise to loop over containers instead of using explicit iteration methods" +target/lintcheck/sources/xsv-0.13.0/src/cmd/stats.rs:22:16 clippy::redundant_static_lifetimes "statics have by default a `'static` lifetime" +target/lintcheck/sources/xsv-0.13.0/src/cmd/stats.rs:231:1 clippy::struct_excessive_bools "more than 3 bools in a struct" +target/lintcheck/sources/xsv-0.13.0/src/cmd/stats.rs:262:35 clippy::default_trait_access "calling `cmd::stats::TypedSum::default()` is more clear than this expression" +target/lintcheck/sources/xsv-0.13.0/src/cmd/stats.rs:263:40 clippy::default_trait_access "calling `cmd::stats::TypedMinMax::default()` is more clear than this expression" +target/lintcheck/sources/xsv-0.13.0/src/cmd/stats.rs:264:39 clippy::default_trait_access "calling `stats::OnlineStats::default()` is more clear than this expression" +target/lintcheck/sources/xsv-0.13.0/src/cmd/stats.rs:265:58 clippy::default_trait_access "calling `stats::Unsorted::default()` is more clear than this expression" +target/lintcheck/sources/xsv-0.13.0/src/cmd/stats.rs:266:41 clippy::default_trait_access "calling `stats::Unsorted::default()` is more clear than this expression" +target/lintcheck/sources/xsv-0.13.0/src/cmd/stats.rs:268:18 clippy::default_trait_access "calling `cmd::stats::FieldType::default()` is more clear than this expression" +target/lintcheck/sources/xsv-0.13.0/src/cmd/stats.rs:269:13 clippy::redundant_field_names "redundant field names in struct initialization" +target/lintcheck/sources/xsv-0.13.0/src/cmd/stats.rs:270:13 clippy::redundant_field_names "redundant field names in struct initialization" +target/lintcheck/sources/xsv-0.13.0/src/cmd/stats.rs:271:13 clippy::redundant_field_names "redundant field names in struct initialization" +target/lintcheck/sources/xsv-0.13.0/src/cmd/stats.rs:272:13 clippy::redundant_field_names "redundant field names in struct initialization" +target/lintcheck/sources/xsv-0.13.0/src/cmd/stats.rs:273:13 clippy::redundant_field_names "redundant field names in struct initialization" +target/lintcheck/sources/xsv-0.13.0/src/cmd/stats.rs:274:13 clippy::redundant_field_names "redundant field names in struct initialization" +target/lintcheck/sources/xsv-0.13.0/src/cmd/stats.rs:283:9 clippy::option_map_unit_fn "called `map(f)` on an `Option` value where `f` is a closure that returns the unit type `()`" +target/lintcheck/sources/xsv-0.13.0/src/cmd/stats.rs:284:9 clippy::option_map_unit_fn "called `map(f)` on an `Option` value where `f` is a closure that returns the unit type `()`" +target/lintcheck/sources/xsv-0.13.0/src/cmd/stats.rs:285:9 clippy::option_map_unit_fn "called `map(f)` on an `Option` value where `f` is a closure that returns the unit type `()`" +target/lintcheck/sources/xsv-0.13.0/src/cmd/stats.rs:290:21 clippy::option_map_unit_fn "called `map(f)` on an `Option` value where `f` is a closure that returns the unit type `()`" +target/lintcheck/sources/xsv-0.13.0/src/cmd/stats.rs:293:25 clippy::match_same_arms "this `match` has identical arm bodies" +target/lintcheck/sources/xsv-0.13.0/src/cmd/stats.rs:297:25 clippy::option_map_unit_fn "called `map(f)` on an `Option` value where `f` is a closure that returns the unit type `()`" +target/lintcheck/sources/xsv-0.13.0/src/cmd/stats.rs:301:21 clippy::option_map_unit_fn "called `map(f)` on an `Option` value where `f` is a closure that returns the unit type `()`" +target/lintcheck/sources/xsv-0.13.0/src/cmd/stats.rs:302:21 clippy::option_map_unit_fn "called `map(f)` on an `Option` value where `f` is a closure that returns the unit type `()`" +target/lintcheck/sources/xsv-0.13.0/src/cmd/stats.rs:308:18 clippy::wrong_self_convention "methods with the following characteristics: (`to_*` and `self` type is not `Copy`) usually take `self` by reference" +target/lintcheck/sources/xsv-0.13.0/src/cmd/stats.rs:318:9 clippy::single_match_else "you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let`" +target/lintcheck/sources/xsv-0.13.0/src/cmd/stats.rs:322:45 clippy::redundant_closure_for_method_calls "redundant closure" +target/lintcheck/sources/xsv-0.13.0/src/cmd/stats.rs:322:9 clippy::single_match_else "you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let`" +target/lintcheck/sources/xsv-0.13.0/src/cmd/stats.rs:327:9 clippy::if_not_else "unnecessary boolean `not` operation" +target/lintcheck/sources/xsv-0.13.0/src/cmd/stats.rs:330:13 clippy::single_match_else "you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let`" +target/lintcheck/sources/xsv-0.13.0/src/cmd/stats.rs:338:45 clippy::redundant_closure_for_method_calls "redundant closure" +target/lintcheck/sources/xsv-0.13.0/src/cmd/stats.rs:402:16 clippy::redundant_pattern_matching "redundant pattern matching, consider using `is_ok()`" +target/lintcheck/sources/xsv-0.13.0/src/cmd/stats.rs:403:16 clippy::redundant_pattern_matching "redundant pattern matching, consider using `is_ok()`" +target/lintcheck/sources/xsv-0.13.0/src/cmd/stats.rs:407:18 clippy::trivially_copy_pass_by_ref "this argument (1 byte) is passed by reference, but would be more efficient if passed by value (limit: 8 byte)" +target/lintcheck/sources/xsv-0.13.0/src/cmd/stats.rs:411:16 clippy::trivially_copy_pass_by_ref "this argument (1 byte) is passed by reference, but would be more efficient if passed by value (limit: 8 byte)" +target/lintcheck/sources/xsv-0.13.0/src/cmd/stats.rs:427:56 clippy::match_same_arms "this `match` has identical arm bodies" +target/lintcheck/sources/xsv-0.13.0/src/cmd/stats.rs:429:56 clippy::match_same_arms "this `match` has identical arm bodies" +target/lintcheck/sources/xsv-0.13.0/src/cmd/stats.rs:430:60 clippy::match_same_arms "this `match` has identical arm bodies" +target/lintcheck/sources/xsv-0.13.0/src/cmd/stats.rs:430:60 clippy::match_same_arms "this `match` has identical arm bodies" +target/lintcheck/sources/xsv-0.13.0/src/cmd/stats.rs:454:5 clippy::doc_markdown "you should put `TypedSum` between ticks in the documentation" +target/lintcheck/sources/xsv-0.13.0/src/cmd/stats.rs:473:43 clippy::cast_precision_loss "casting `i64` to `f64` causes a loss of precision (`i64` is 64 bits wide, but `f64`'s mantissa is only 52 bits wide)" +target/lintcheck/sources/xsv-0.13.0/src/cmd/stats.rs:504:56 clippy::cast_precision_loss "casting `i64` to `f64` causes a loss of precision (`i64` is 64 bits wide, but `f64`'s mantissa is only 52 bits wide)" +target/lintcheck/sources/xsv-0.13.0/src/cmd/stats.rs:505:51 clippy::cast_precision_loss "casting `i64` to `f64` causes a loss of precision (`i64` is 64 bits wide, but `f64`'s mantissa is only 52 bits wide)" +target/lintcheck/sources/xsv-0.13.0/src/cmd/stats.rs:511:5 clippy::doc_markdown "you should put `TypedMinMax` between ticks in the documentation" +target/lintcheck/sources/xsv-0.13.0/src/cmd/stats.rs:536:35 clippy::cast_possible_truncation "casting `f64` to `i64` may truncate the value" +target/lintcheck/sources/xsv-0.13.0/src/cmd/stats.rs:544:33 clippy::cast_precision_loss "casting `i64` to `f64` causes a loss of precision (`i64` is 64 bits wide, but `f64`'s mantissa is only 52 bits wide)" +target/lintcheck/sources/xsv-0.13.0/src/cmd/stats.rs:592:22 clippy::default_trait_access "calling `stats::MinMax::default()` is more clear than this expression" +target/lintcheck/sources/xsv-0.13.0/src/cmd/stats.rs:593:22 clippy::default_trait_access "calling `stats::MinMax::default()` is more clear than this expression" +target/lintcheck/sources/xsv-0.13.0/src/cmd/stats.rs:594:23 clippy::default_trait_access "calling `stats::MinMax::default()` is more clear than this expression" +target/lintcheck/sources/xsv-0.13.0/src/cmd/stats.rs:595:21 clippy::default_trait_access "calling `stats::MinMax::default()` is more clear than this expression" +target/lintcheck/sources/xsv-0.13.0/src/cmd/stats.rs:71:1 clippy::struct_excessive_bools "more than 3 bools in a struct" +target/lintcheck/sources/xsv-0.13.0/src/cmd/stats.rs:86:9 clippy::similar_names "binding's name is too similar to existing binding" +target/lintcheck/sources/xsv-0.13.0/src/cmd/table.rs:10:16 clippy::redundant_static_lifetimes "statics have by default a `'static` lifetime" +target/lintcheck/sources/xsv-0.13.0/src/cmd/table.rs:50:9 clippy::similar_names "binding's name is too similar to existing binding" +target/lintcheck/sources/xsv-0.13.0/src/cmd/table.rs:54:9 clippy::similar_names "binding's name is too similar to existing binding" +target/lintcheck/sources/xsv-0.13.0/src/config.rs:113:43 clippy::or_fun_call "use of `unwrap_or` followed by a function call" +target/lintcheck/sources/xsv-0.13.0/src/config.rs:58:1 clippy::struct_excessive_bools "more than 3 bools in a struct" +target/lintcheck/sources/xsv-0.13.0/src/config.rs:77:28 clippy::explicit_deref_methods "explicit `deref` method call" +target/lintcheck/sources/xsv-0.13.0/src/config.rs:90:13 clippy::redundant_field_names "redundant field names in struct initialization" +target/lintcheck/sources/xsv-0.13.0/src/index.rs:31:13 clippy::redundant_field_names "redundant field names in struct initialization" +target/lintcheck/sources/xsv-0.13.0/src/main.rs:164:49 clippy::redundant_clone "redundant clone" +target/lintcheck/sources/xsv-0.13.0/src/main.rs:164:50 clippy::implicit_clone "implicitly cloning a `String` by calling `to_owned` on its dereferenced type" +target/lintcheck/sources/xsv-0.13.0/src/main.rs:1:null clippy::cargo_common_metadata "package `xsv` is missing `package.categories` metadata" +target/lintcheck/sources/xsv-0.13.0/src/main.rs:1:null clippy::multiple_crate_versions "multiple versions for dependency `rand_core`: 0.3.1, 0.4.2" +target/lintcheck/sources/xsv-0.13.0/src/main.rs:1:null clippy::multiple_crate_versions "multiple versions for dependency `rand`: 0.3.23, 0.4.6" +target/lintcheck/sources/xsv-0.13.0/src/main.rs:75:16 clippy::redundant_static_lifetimes "statics have by default a `'static` lifetime" +target/lintcheck/sources/xsv-0.13.0/src/select.rs:13:1 clippy::module_name_repetitions "item name starts with its containing module's name" +target/lintcheck/sources/xsv-0.13.0/src/select.rs:154:5 clippy::unnecessary_wraps "this function's return value is unnecessarily wrapped by `Result`" +target/lintcheck/sources/xsv-0.13.0/src/select.rs:250:33 clippy::similar_names "binding's name is too similar to existing binding" +target/lintcheck/sources/xsv-0.13.0/src/select.rs:250:43 clippy::similar_names "binding's name is too similar to existing binding" +target/lintcheck/sources/xsv-0.13.0/src/select.rs:255:39 clippy::range_plus_one "an inclusive range would be more readable" +target/lintcheck/sources/xsv-0.13.0/src/select.rs:280:20 clippy::len_zero "length comparison to zero" +target/lintcheck/sources/xsv-0.13.0/src/select.rs:29:13 clippy::redundant_field_names "redundant field names in struct initialization" +target/lintcheck/sources/xsv-0.13.0/src/select.rs:360:62 clippy::trivially_copy_pass_by_ref "this argument (8 byte) is passed by reference, but would be more efficient if passed by value (limit: 8 byte)" +target/lintcheck/sources/xsv-0.13.0/src/select.rs:360:9 clippy::unnecessary_wraps "this function's return value is unnecessarily wrapped by `Option`" +target/lintcheck/sources/xsv-0.13.0/src/select.rs:375:9 clippy::stable_sort_primitive "used `sort` on primitive type `usize`" +target/lintcheck/sources/xsv-0.13.0/src/select.rs:379:18 clippy::explicit_into_iter_loop "it is more concise to loop over containers instead of using explicit iteration methods" +target/lintcheck/sources/xsv-0.13.0/src/select.rs:416:5 clippy::needless_lifetimes "explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration)" +target/lintcheck/sources/xsv-0.13.0/src/select.rs:419:9 clippy::unnecessary_wraps "this function's return value is unnecessarily wrapped by `Option`" +target/lintcheck/sources/xsv-0.13.0/src/select.rs:420:27 clippy::option_option "consider using `Option<T>` instead of `Option<Option<T>>` or a custom enum if you need to distinguish all 3 cases" +target/lintcheck/sources/xsv-0.13.0/src/select.rs:99:17 clippy::similar_names "binding's name is too similar to existing binding" +target/lintcheck/sources/xsv-0.13.0/src/util.rs:150:5 clippy::doc_markdown "you should put bare URLs between `<`/`>` or make a proper Markdown link" +target/lintcheck/sources/xsv-0.13.0/src/util.rs:37:33 clippy::map_clone "you are using an explicit closure for copying elements" +target/lintcheck/sources/xsv-0.13.0/src/util.rs:90:1 clippy::needless_lifetimes "explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration)" -Stats - +Stats: clippy::clone_on_copy 1 clippy::comparison_chain 1 clippy::expect_fun_call 1 clippy::explicit_deref_methods 1 clippy::from_iter_instead_of_collect 1 -clippy::from_over_into 1 clippy::int_plus_one 1 clippy::manual_flatten 1 clippy::manual_saturating_arithmetic 1 -clippy::mem_replace_with_default 1 clippy::nonminimal_bool 1 clippy::or_fun_call 1 clippy::precedence 1 clippy::pub_enum_variant_names 1 clippy::redundant_clone 1 -clippy::redundant_slicing 1 clippy::same_item_push 1 clippy::should_implement_trait 1 clippy::stable_sort_primitive 1 @@ -3402,8 +3765,8 @@ clippy::used_underscore_binding 1 clippy::verbose_bit_mask 1 clippy::while_let_on_iterator 1 clippy::comparison_to_empty 2 -clippy::expl_impl_clone_on_copy 2 clippy::filter_map 2 +clippy::from_over_into 2 clippy::len_zero 2 clippy::manual_non_exhaustive 2 clippy::match_on_vec_items 2 @@ -3411,6 +3774,7 @@ clippy::option_as_ref_deref 2 clippy::option_option 2 clippy::question_mark 2 clippy::redundant_pattern_matching 2 +clippy::redundant_slicing 2 clippy::type_complexity 2 clippy::unnecessary_cast 2 clippy::unused_unit 2 @@ -3420,43 +3784,43 @@ clippy::filter_map_next 3 clippy::fn_params_excessive_bools 3 clippy::if_same_then_else 3 clippy::inconsistent_struct_constructor 3 +clippy::manual_map 3 clippy::mut_mut 3 clippy::ptr_arg 3 +clippy::upper_case_acronyms 3 clippy::zero_ptr 3 +clippy::mem_replace_with_default 4 clippy::too_many_arguments 4 clippy::explicit_iter_loop 5 clippy::field_reassign_with_default 5 clippy::identity_op 5 -clippy::len_without_is_empty 5 clippy::match_like_matches_macro 5 clippy::needless_return 5 clippy::new_without_default 5 -clippy::ptr_as_ptr 5 clippy::collapsible_else_if 6 clippy::manual_strip 6 clippy::non_ascii_literal 6 clippy::single_component_path_imports 6 clippy::case_sensitive_file_extension_comparisons 7 clippy::explicit_into_iter_loop 7 +clippy::implicit_clone 7 clippy::map_clone 7 clippy::option_map_unit_fn 7 clippy::range_plus_one 7 clippy::invalid_upcast_comparisons 8 clippy::needless_question_mark 8 -clippy::wrong_self_convention 8 +clippy::len_without_is_empty 9 clippy::multiple_crate_versions 9 clippy::manual_range_contains 10 -clippy::match_wildcard_for_single_variants 10 clippy::missing_safety_doc 10 clippy::needless_doctest_main 10 +clippy::match_wildcard_for_single_variants 11 clippy::needless_lifetimes 12 -clippy::cargo_common_metadata 13 -clippy::shadow_unrelated 13 clippy::linkedlist 14 clippy::single_char_add_str 14 clippy::option_if_let_else 15 +clippy::shadow_unrelated 15 clippy::needless_pass_by_value 18 -clippy::upper_case_acronyms 18 clippy::cast_possible_wrap 19 clippy::cast_sign_loss 19 clippy::unnecessary_wraps 19 @@ -3464,30 +3828,35 @@ clippy::unused_self 19 clippy::unusual_byte_groupings 19 clippy::map_unwrap_or 20 clippy::struct_excessive_bools 20 +clippy::cargo_common_metadata 21 +clippy::ptr_as_ptr 21 clippy::redundant_static_lifetimes 21 -clippy::default_trait_access 22 clippy::cast_lossless 23 -clippy::let_underscore_drop 23 +clippy::unnested_or_patterns 25 +clippy::default_trait_access 26 +clippy::let_underscore_drop 26 clippy::trivially_copy_pass_by_ref 26 clippy::redundant_else 29 -clippy::too_many_lines 32 -clippy::if_not_else 35 -clippy::enum_glob_use 40 +clippy::too_many_lines 36 +clippy::if_not_else 38 clippy::unseparated_literal_suffix 41 clippy::cast_precision_loss 44 -clippy::single_match_else 45 -clippy::missing_panics_doc 54 +clippy::enum_glob_use 44 +clippy::single_match_else 48 clippy::inline_always 59 -clippy::match_same_arms 60 -clippy::similar_names 78 +clippy::missing_panics_doc 59 +clippy::match_same_arms 62 +clippy::similar_names 83 +clippy::wrong_self_convention 94 clippy::cast_possible_truncation 95 clippy::redundant_field_names 111 -clippy::redundant_closure_for_method_calls 135 -clippy::items_after_statements 139 -clippy::module_name_repetitions 142 -clippy::wildcard_imports 163 -clippy::doc_markdown 178 -clippy::missing_errors_doc 343 +clippy::redundant_closure_for_method_calls 131 +clippy::items_after_statements 143 +clippy::module_name_repetitions 146 +clippy::expl_impl_clone_on_copy 164 +clippy::wildcard_imports 164 +clippy::doc_markdown 184 +clippy::missing_errors_doc 356 clippy::unreadable_literal 365 -clippy::must_use_candidate 565 +clippy::must_use_candidate 571 ICEs: diff --git a/src/tools/clippy/lintcheck/Cargo.toml b/src/tools/clippy/lintcheck/Cargo.toml new file mode 100644 index 0000000000000..8db6d28e5acae --- /dev/null +++ b/src/tools/clippy/lintcheck/Cargo.toml @@ -0,0 +1,25 @@ +[package] +name = "lintcheck" +version = "0.0.1" +authors = ["The Rust Clippy Developers"] +description = "tool to monitor impact of changes in Clippys lints on a part of the ecosystem" +readme = "README.md" +license = "MIT OR Apache-2.0" +repository = "https://github.com/rust-lang/rust-clippy" +categories = ["development-tools"] +edition = "2018" +publish = false + +[dependencies] +clap = "2.33" +flate2 = {version = "1.0.19"} +fs_extra = {version = "1.2.0"} +rayon = {version = "1.5.0"} +serde = {version = "1.0", features = ["derive"]} +serde_json = {version = "1.0"} +tar = {version = "0.4.30"} +toml = {version = "0.5"} +ureq = {version = "2.0.0-rc3"} + +[features] +deny-warnings = [] diff --git a/src/tools/clippy/lintcheck/README.md b/src/tools/clippy/lintcheck/README.md new file mode 100644 index 0000000000000..52bbcc0a8317d --- /dev/null +++ b/src/tools/clippy/lintcheck/README.md @@ -0,0 +1,77 @@ +## `cargo lintcheck` + +Runs clippy on a fixed set of crates read from +`lintcheck/lintcheck_crates.toml` and saves logs of the lint warnings into the +repo. We can then check the diff and spot new or disappearing warnings. + +From the repo root, run: + +``` +cargo run --target-dir lintcheck/target --manifest-path lintcheck/Cargo.toml +``` + +or + +``` +cargo lintcheck +``` + +By default the logs will be saved into +`lintcheck-logs/lintcheck_crates_logs.txt`. + +You can set a custom sources.toml by adding `--crates-toml custom.toml` or using +`LINTCHECK_TOML="custom.toml"` where `custom.toml` must be a relative path from +the repo root. + +The results will then be saved to `lintcheck-logs/custom_logs.toml`. + +### Configuring the Crate Sources + +The sources to check are saved in a `toml` file. There are three types of +sources. + +1. Crates-io Source + + ```toml + bitflags = {name = "bitflags", versions = ['1.2.1']} + ``` + Requires a "name" and one or multiple "versions" to be checked. + +2. `git` Source + ````toml + puffin = {name = "puffin", git_url = "https://github.com/EmbarkStudios/puffin", git_hash = "02dd4a3"} + ```` + Requires a name, the url to the repo and unique identifier of a commit, + branch or tag which is checked out before linting. There is no way to always + check `HEAD` because that would lead to changing lint-results as the repo + would get updated. If `git_url` or `git_hash` is missing, an error will be + thrown. + +3. Local Dependency + ```toml + clippy = {name = "clippy", path = "/home/user/clippy"} + ``` + For when you want to add a repository that is not published yet. + +#### Command Line Options (optional) + +```toml +bitflags = {name = "bitflags", versions = ['1.2.1'], options = ['-Wclippy::pedantic', '-Wclippy::cargo']} +``` + +It is possible to specify command line options for each crate. This makes it +possible to only check a crate for certain lint groups. If no options are +specified, the lint groups `clippy::all`, `clippy::pedantic`, and +`clippy::cargo` are checked. If an empty array is specified only `clippy::all` +is checked. + +**Note:** `-Wclippy::all` is always enabled by default, unless `-Aclippy::all` +is explicitly specified in the options. + +### Fix mode +You can run `./lintcheck/target/debug/lintcheck --fix` which will run Clippy with `-Zunstable-options --fix` and +print a warning if Clippys suggestions fail to apply (if the resulting code does not build). +This lets us spot bad suggestions or false positives automatically in some cases. + +Please note that the target dir should be cleaned afterwards since clippy will modify +the downloaded sources which can lead to unexpected results when running lintcheck again afterwards. diff --git a/src/tools/clippy/lintcheck/lintcheck_crates.toml b/src/tools/clippy/lintcheck/lintcheck_crates.toml new file mode 100644 index 0000000000000..dfee28f1a8712 --- /dev/null +++ b/src/tools/clippy/lintcheck/lintcheck_crates.toml @@ -0,0 +1,35 @@ +[crates] +# some of these are from cargotest +cargo = {name = "cargo", versions = ['0.49.0']} +iron = {name = "iron", versions = ['0.6.1']} +ripgrep = {name = "ripgrep", versions = ['12.1.1']} +xsv = {name = "xsv", versions = ['0.13.0']} +# commented out because of 173K clippy::match_same_arms msgs in language_type.rs +#tokei = { name = "tokei", versions = ['12.0.4']} +rayon = {name = "rayon", versions = ['1.5.0']} +serde = {name = "serde", versions = ['1.0.118']} +# top 10 crates.io dls +bitflags = {name = "bitflags", versions = ['1.2.1']} +# crash = {name = "clippy_crash", path = "/tmp/clippy_crash"} +libc = {name = "libc", versions = ['0.2.81']} +log = {name = "log", versions = ['0.4.11']} +proc-macro2 = {name = "proc-macro2", versions = ['1.0.24']} +quote = {name = "quote", versions = ['1.0.7']} +rand = {name = "rand", versions = ['0.7.3']} +rand_core = {name = "rand_core", versions = ['0.6.0']} +regex = {name = "regex", versions = ['1.3.2']} +syn = {name = "syn", versions = ['1.0.54']} +unicode-xid = {name = "unicode-xid", versions = ['0.2.1']} +# some more of dtolnays crates +anyhow = {name = "anyhow", versions = ['1.0.38']} +async-trait = {name = "async-trait", versions = ['0.1.42']} +cxx = {name = "cxx", versions = ['1.0.32']} +ryu = {name = "ryu", version = ['1.0.5']} +serde_yaml = {name = "serde_yaml", versions = ['0.8.17']} +thiserror = {name = "thiserror", versions = ['1.0.24']} +# some embark crates, there are other interesting crates but +# unfortunately adding them increases lintcheck runtime drastically +cfg-expr = {name = "cfg-expr", versions = ['0.7.1']} +puffin = {name = "puffin", git_url = "https://github.com/EmbarkStudios/puffin", git_hash = "02dd4a3"} +rpmalloc = {name = "rpmalloc", versions = ['0.2.0']} +tame-oidc = {name = "tame-oidc", versions = ['0.1.0']} diff --git a/src/tools/clippy/lintcheck/src/main.rs b/src/tools/clippy/lintcheck/src/main.rs new file mode 100644 index 0000000000000..2041aed2b97c7 --- /dev/null +++ b/src/tools/clippy/lintcheck/src/main.rs @@ -0,0 +1,925 @@ +// Run clippy on a fixed set of crates and collect the warnings. +// This helps observing the impact clippy changes have on a set of real-world code (and not just our +// testsuite). +// +// When a new lint is introduced, we can search the results for new warnings and check for false +// positives. + +#![allow(clippy::filter_map, clippy::collapsible_else_if)] + +use std::ffi::OsStr; +use std::process::Command; +use std::sync::atomic::{AtomicUsize, Ordering}; +use std::{collections::HashMap, io::ErrorKind}; +use std::{ + env, fmt, + fs::write, + path::{Path, PathBuf}, +}; + +use clap::{App, Arg, ArgMatches}; +use rayon::prelude::*; +use serde::{Deserialize, Serialize}; +use serde_json::Value; + +const CLIPPY_DRIVER_PATH: &str = "target/debug/clippy-driver"; +const CARGO_CLIPPY_PATH: &str = "target/debug/cargo-clippy"; + +const LINTCHECK_DOWNLOADS: &str = "target/lintcheck/downloads"; +const LINTCHECK_SOURCES: &str = "target/lintcheck/sources"; + +/// List of sources to check, loaded from a .toml file +#[derive(Debug, Serialize, Deserialize)] +struct SourceList { + crates: HashMap<String, TomlCrate>, +} + +/// A crate source stored inside the .toml +/// will be translated into on one of the `CrateSource` variants +#[derive(Debug, Serialize, Deserialize)] +struct TomlCrate { + name: String, + versions: Option<Vec<String>>, + git_url: Option<String>, + git_hash: Option<String>, + path: Option<String>, + options: Option<Vec<String>>, +} + +/// Represents an archive we download from crates.io, or a git repo, or a local repo/folder +/// Once processed (downloaded/extracted/cloned/copied...), this will be translated into a `Crate` +#[derive(Debug, Serialize, Deserialize, Eq, Hash, PartialEq, Ord, PartialOrd)] +enum CrateSource { + CratesIo { + name: String, + version: String, + options: Option<Vec<String>>, + }, + Git { + name: String, + url: String, + commit: String, + options: Option<Vec<String>>, + }, + Path { + name: String, + path: PathBuf, + options: Option<Vec<String>>, + }, +} + +/// Represents the actual source code of a crate that we ran "cargo clippy" on +#[derive(Debug)] +struct Crate { + version: String, + name: String, + // path to the extracted sources that clippy can check + path: PathBuf, + options: Option<Vec<String>>, +} + +/// A single warning that clippy issued while checking a `Crate` +#[derive(Debug)] +struct ClippyWarning { + crate_name: String, + crate_version: String, + file: String, + line: String, + column: String, + linttype: String, + message: String, + is_ice: bool, +} + +impl std::fmt::Display for ClippyWarning { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + writeln!( + f, + r#"target/lintcheck/sources/{}-{}/{}:{}:{} {} "{}""#, + &self.crate_name, &self.crate_version, &self.file, &self.line, &self.column, &self.linttype, &self.message + ) + } +} + +impl CrateSource { + /// Makes the sources available on the disk for clippy to check. + /// Clones a git repo and checks out the specified commit or downloads a crate from crates.io or + /// copies a local folder + fn download_and_extract(&self) -> Crate { + match self { + CrateSource::CratesIo { name, version, options } => { + let extract_dir = PathBuf::from(LINTCHECK_SOURCES); + let krate_download_dir = PathBuf::from(LINTCHECK_DOWNLOADS); + + // url to download the crate from crates.io + let url = format!("https://crates.io/api/v1/crates/{}/{}/download", name, version); + println!("Downloading and extracting {} {} from {}", name, version, url); + create_dirs(&krate_download_dir, &extract_dir); + + let krate_file_path = krate_download_dir.join(format!("{}-{}.crate.tar.gz", name, version)); + // don't download/extract if we already have done so + if !krate_file_path.is_file() { + // create a file path to download and write the crate data into + let mut krate_dest = std::fs::File::create(&krate_file_path).unwrap(); + let mut krate_req = ureq::get(&url).call().unwrap().into_reader(); + // copy the crate into the file + std::io::copy(&mut krate_req, &mut krate_dest).unwrap(); + + // unzip the tarball + let ungz_tar = flate2::read::GzDecoder::new(std::fs::File::open(&krate_file_path).unwrap()); + // extract the tar archive + let mut archive = tar::Archive::new(ungz_tar); + archive.unpack(&extract_dir).expect("Failed to extract!"); + } + // crate is extracted, return a new Krate object which contains the path to the extracted + // sources that clippy can check + Crate { + version: version.clone(), + name: name.clone(), + path: extract_dir.join(format!("{}-{}/", name, version)), + options: options.clone(), + } + }, + CrateSource::Git { + name, + url, + commit, + options, + } => { + let repo_path = { + let mut repo_path = PathBuf::from(LINTCHECK_SOURCES); + // add a -git suffix in case we have the same crate from crates.io and a git repo + repo_path.push(format!("{}-git", name)); + repo_path + }; + // clone the repo if we have not done so + if !repo_path.is_dir() { + println!("Cloning {} and checking out {}", url, commit); + if !Command::new("git") + .arg("clone") + .arg(url) + .arg(&repo_path) + .status() + .expect("Failed to clone git repo!") + .success() + { + eprintln!("Failed to clone {} into {}", url, repo_path.display()) + } + } + // check out the commit/branch/whatever + if !Command::new("git") + .arg("checkout") + .arg(commit) + .current_dir(&repo_path) + .status() + .expect("Failed to check out commit") + .success() + { + eprintln!("Failed to checkout {} of repo at {}", commit, repo_path.display()) + } + + Crate { + version: commit.clone(), + name: name.clone(), + path: repo_path, + options: options.clone(), + } + }, + CrateSource::Path { name, path, options } => { + use fs_extra::dir; + + // simply copy the entire directory into our target dir + let copy_dest = PathBuf::from(format!("{}/", LINTCHECK_SOURCES)); + + // the source path of the crate we copied, ${copy_dest}/crate_name + let crate_root = copy_dest.join(name); // .../crates/local_crate + + if crate_root.exists() { + println!( + "Not copying {} to {}, destination already exists", + path.display(), + crate_root.display() + ); + } else { + println!("Copying {} to {}", path.display(), copy_dest.display()); + + dir::copy(path, ©_dest, &dir::CopyOptions::new()).unwrap_or_else(|_| { + panic!("Failed to copy from {}, to {}", path.display(), crate_root.display()) + }); + } + + Crate { + version: String::from("local"), + name: name.clone(), + path: crate_root, + options: options.clone(), + } + }, + } + } +} + +impl Crate { + /// Run `cargo clippy` on the `Crate` and collect and return all the lint warnings that clippy + /// issued + fn run_clippy_lints( + &self, + cargo_clippy_path: &Path, + target_dir_index: &AtomicUsize, + thread_limit: usize, + total_crates_to_lint: usize, + fix: bool, + ) -> Vec<ClippyWarning> { + // advance the atomic index by one + let index = target_dir_index.fetch_add(1, Ordering::SeqCst); + // "loop" the index within 0..thread_limit + let thread_index = index % thread_limit; + let perc = (index * 100) / total_crates_to_lint; + + if thread_limit == 1 { + println!( + "{}/{} {}% Linting {} {}", + index, total_crates_to_lint, perc, &self.name, &self.version + ); + } else { + println!( + "{}/{} {}% Linting {} {} in target dir {:?}", + index, total_crates_to_lint, perc, &self.name, &self.version, thread_index + ); + } + + let cargo_clippy_path = std::fs::canonicalize(cargo_clippy_path).unwrap(); + + let shared_target_dir = clippy_project_root().join("target/lintcheck/shared_target_dir"); + + let mut args = if fix { + vec![ + "-Zunstable-options", + "--fix", + "-Zunstable-options", + "--allow-no-vcs", + "--", + "--cap-lints=warn", + ] + } else { + vec!["--", "--message-format=json", "--", "--cap-lints=warn"] + }; + + if let Some(options) = &self.options { + for opt in options { + args.push(opt); + } + } else { + args.extend(&["-Wclippy::pedantic", "-Wclippy::cargo"]) + } + + let all_output = std::process::Command::new(&cargo_clippy_path) + // use the looping index to create individual target dirs + .env( + "CARGO_TARGET_DIR", + shared_target_dir.join(format!("_{:?}", thread_index)), + ) + // lint warnings will look like this: + // src/cargo/ops/cargo_compile.rs:127:35: warning: usage of `FromIterator::from_iter` + .args(&args) + .current_dir(&self.path) + .output() + .unwrap_or_else(|error| { + panic!( + "Encountered error:\n{:?}\ncargo_clippy_path: {}\ncrate path:{}\n", + error, + &cargo_clippy_path.display(), + &self.path.display() + ); + }); + let stdout = String::from_utf8_lossy(&all_output.stdout); + let stderr = String::from_utf8_lossy(&all_output.stderr); + let status = &all_output.status; + + if !status.success() { + eprintln!( + "\nWARNING: bad exit status after checking {} {} \n", + self.name, self.version + ); + } + + if fix { + if let Some(stderr) = stderr + .lines() + .find(|line| line.contains("failed to automatically apply fixes suggested by rustc to crate")) + { + let subcrate = &stderr[63..]; + println!( + "ERROR: failed to apply some suggetion to {} / to (sub)crate {}", + self.name, subcrate + ); + } + // fast path, we don't need the warnings anyway + return Vec::new(); + } + + let output_lines = stdout.lines(); + let warnings: Vec<ClippyWarning> = output_lines + .into_iter() + // get all clippy warnings and ICEs + .filter(|line| filter_clippy_warnings(&line)) + .map(|json_msg| parse_json_message(json_msg, &self)) + .collect(); + + warnings + } +} + +#[derive(Debug)] +struct LintcheckConfig { + // max number of jobs to spawn (default 1) + max_jobs: usize, + // we read the sources to check from here + sources_toml_path: PathBuf, + // we save the clippy lint results here + lintcheck_results_path: PathBuf, + // whether to just run --fix and not collect all the warnings + fix: bool, +} + +impl LintcheckConfig { + fn from_clap(clap_config: &ArgMatches) -> Self { + // first, check if we got anything passed via the LINTCHECK_TOML env var, + // if not, ask clap if we got any value for --crates-toml <foo> + // if not, use the default "lintcheck/lintcheck_crates.toml" + let sources_toml = env::var("LINTCHECK_TOML").unwrap_or_else(|_| { + clap_config + .value_of("crates-toml") + .clone() + .unwrap_or("lintcheck/lintcheck_crates.toml") + .to_string() + }); + + let sources_toml_path = PathBuf::from(sources_toml); + + // for the path where we save the lint results, get the filename without extension (so for + // wasd.toml, use "wasd"...) + let filename: PathBuf = sources_toml_path.file_stem().unwrap().into(); + let lintcheck_results_path = PathBuf::from(format!("lintcheck-logs/{}_logs.txt", filename.display())); + + // look at the --threads arg, if 0 is passed, ask rayon rayon how many threads it would spawn and + // use half of that for the physical core count + // by default use a single thread + let max_jobs = match clap_config.value_of("threads") { + Some(threads) => { + let threads: usize = threads + .parse() + .unwrap_or_else(|_| panic!("Failed to parse '{}' to a digit", threads)); + if threads == 0 { + // automatic choice + // Rayon seems to return thread count so half that for core count + (rayon::current_num_threads() / 2) as usize + } else { + threads + } + }, + // no -j passed, use a single thread + None => 1, + }; + let fix: bool = clap_config.is_present("fix"); + + LintcheckConfig { + max_jobs, + sources_toml_path, + lintcheck_results_path, + fix, + } + } +} + +/// takes a single json-formatted clippy warnings and returns true (we are interested in that line) +/// or false (we aren't) +fn filter_clippy_warnings(line: &str) -> bool { + // we want to collect ICEs because clippy might have crashed. + // these are summarized later + if line.contains("internal compiler error: ") { + return true; + } + // in general, we want all clippy warnings + // however due to some kind of bug, sometimes there are absolute paths + // to libcore files inside the message + // or we end up with cargo-metadata output (https://github.com/rust-lang/rust-clippy/issues/6508) + + // filter out these message to avoid unnecessary noise in the logs + if line.contains("clippy::") + && !(line.contains("could not read cargo metadata") + || (line.contains(".rustup") && line.contains("toolchains"))) + { + return true; + } + false +} + +/// Builds clippy inside the repo to make sure we have a clippy executable we can use. +fn build_clippy() { + let status = Command::new("cargo") + .arg("build") + .status() + .expect("Failed to build clippy!"); + if !status.success() { + eprintln!("Error: Failed to compile Clippy!"); + std::process::exit(1); + } +} + +/// Read a `toml` file and return a list of `CrateSources` that we want to check with clippy +fn read_crates(toml_path: &Path) -> Vec<CrateSource> { + let toml_content: String = + std::fs::read_to_string(&toml_path).unwrap_or_else(|_| panic!("Failed to read {}", toml_path.display())); + let crate_list: SourceList = + toml::from_str(&toml_content).unwrap_or_else(|e| panic!("Failed to parse {}: \n{}", toml_path.display(), e)); + // parse the hashmap of the toml file into a list of crates + let tomlcrates: Vec<TomlCrate> = crate_list + .crates + .into_iter() + .map(|(_cratename, tomlcrate)| tomlcrate) + .collect(); + + // flatten TomlCrates into CrateSources (one TomlCrates may represent several versions of a crate => + // multiple Cratesources) + let mut crate_sources = Vec::new(); + tomlcrates.into_iter().for_each(|tk| { + if let Some(ref path) = tk.path { + crate_sources.push(CrateSource::Path { + name: tk.name.clone(), + path: PathBuf::from(path), + options: tk.options.clone(), + }); + } + + // if we have multiple versions, save each one + if let Some(ref versions) = tk.versions { + versions.iter().for_each(|ver| { + crate_sources.push(CrateSource::CratesIo { + name: tk.name.clone(), + version: ver.to_string(), + options: tk.options.clone(), + }); + }) + } + // otherwise, we should have a git source + if tk.git_url.is_some() && tk.git_hash.is_some() { + crate_sources.push(CrateSource::Git { + name: tk.name.clone(), + url: tk.git_url.clone().unwrap(), + commit: tk.git_hash.clone().unwrap(), + options: tk.options.clone(), + }); + } + // if we have a version as well as a git data OR only one git data, something is funky + if tk.versions.is_some() && (tk.git_url.is_some() || tk.git_hash.is_some()) + || tk.git_hash.is_some() != tk.git_url.is_some() + { + eprintln!("tomlkrate: {:?}", tk); + if tk.git_hash.is_some() != tk.git_url.is_some() { + panic!("Error: Encountered TomlCrate with only one of git_hash and git_url!"); + } + if tk.path.is_some() && (tk.git_hash.is_some() || tk.versions.is_some()) { + panic!("Error: TomlCrate can only have one of 'git_.*', 'version' or 'path' fields"); + } + unreachable!("Failed to translate TomlCrate into CrateSource!"); + } + }); + // sort the crates + crate_sources.sort(); + + crate_sources +} + +/// Parse the json output of clippy and return a `ClippyWarning` +fn parse_json_message(json_message: &str, krate: &Crate) -> ClippyWarning { + let jmsg: Value = serde_json::from_str(&json_message).unwrap_or_else(|e| panic!("Failed to parse json:\n{:?}", e)); + + let file: String = jmsg["message"]["spans"][0]["file_name"] + .to_string() + .trim_matches('"') + .into(); + + let file = if file.contains(".cargo") { + // if we deal with macros, a filename may show the origin of a macro which can be inside a dep from + // the registry. + // don't show the full path in that case. + + // /home/matthias/.cargo/registry/src/jackfan.us.kg-1ecc6299db9ec823/syn-1.0.63/src/custom_keyword.rs + let path = PathBuf::from(file); + let mut piter = path.iter(); + // consume all elements until we find ".cargo", so that "/home/matthias" is skipped + let _: Option<&OsStr> = piter.find(|x| x == &std::ffi::OsString::from(".cargo")); + // collect the remaining segments + let file = piter.collect::<PathBuf>(); + format!("{}", file.display()) + } else { + file + }; + + ClippyWarning { + crate_name: krate.name.to_string(), + crate_version: krate.version.to_string(), + file, + line: jmsg["message"]["spans"][0]["line_start"] + .to_string() + .trim_matches('"') + .into(), + column: jmsg["message"]["spans"][0]["text"][0]["highlight_start"] + .to_string() + .trim_matches('"') + .into(), + linttype: jmsg["message"]["code"]["code"].to_string().trim_matches('"').into(), + message: jmsg["message"]["message"].to_string().trim_matches('"').into(), + is_ice: json_message.contains("internal compiler error: "), + } +} + +/// Generate a short list of occuring lints-types and their count +fn gather_stats(clippy_warnings: &[ClippyWarning]) -> (String, HashMap<&String, usize>) { + // count lint type occurrences + let mut counter: HashMap<&String, usize> = HashMap::new(); + clippy_warnings + .iter() + .for_each(|wrn| *counter.entry(&wrn.linttype).or_insert(0) += 1); + + // collect into a tupled list for sorting + let mut stats: Vec<(&&String, &usize)> = counter.iter().map(|(lint, count)| (lint, count)).collect(); + // sort by "000{count} {clippy::lintname}" + // to not have a lint with 200 and 2 warnings take the same spot + stats.sort_by_key(|(lint, count)| format!("{:0>4}, {}", count, lint)); + + let stats_string = stats + .iter() + .map(|(lint, count)| format!("{} {}\n", lint, count)) + .collect::<String>(); + + (stats_string, counter) +} + +/// check if the latest modification of the logfile is older than the modification date of the +/// clippy binary, if this is true, we should clean the lintchec shared target directory and recheck +fn lintcheck_needs_rerun(lintcheck_logs_path: &Path) -> bool { + if !lintcheck_logs_path.exists() { + return true; + } + + let clippy_modified: std::time::SystemTime = { + let mut times = [CLIPPY_DRIVER_PATH, CARGO_CLIPPY_PATH].iter().map(|p| { + std::fs::metadata(p) + .expect("failed to get metadata of file") + .modified() + .expect("failed to get modification date") + }); + // the oldest modification of either of the binaries + std::cmp::max(times.next().unwrap(), times.next().unwrap()) + }; + + let logs_modified: std::time::SystemTime = std::fs::metadata(lintcheck_logs_path) + .expect("failed to get metadata of file") + .modified() + .expect("failed to get modification date"); + + // time is represented in seconds since X + // logs_modified 2 and clippy_modified 5 means clippy binary is older and we need to recheck + 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 +/// +/// This function panics if the clippy binaries don't exist +/// 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() { + eprintln!("lintcheck needs to be run from clippys repo root!\nUse `cargo lintcheck` alternatively."); + std::process::exit(3); + } + + let clap_config = &get_clap_config(); + + let config = LintcheckConfig::from_clap(clap_config); + + println!("Compiling clippy..."); + build_clippy(); + println!("Done compiling"); + + // if the clippy bin is newer than our logs, throw away target dirs to force clippy to + // refresh the logs + if lintcheck_needs_rerun(&config.lintcheck_results_path) { + let shared_target_dir = "target/lintcheck/shared_target_dir"; + // if we get an Err here, the shared target dir probably does simply not exist + if let Ok(metadata) = std::fs::metadata(&shared_target_dir) { + if metadata.is_dir() { + println!("Clippy is newer than lint check logs, clearing lintcheck shared target dir..."); + std::fs::remove_dir_all(&shared_target_dir) + .expect("failed to remove target/lintcheck/shared_target_dir"); + } + } + } + + let cargo_clippy_path: PathBuf = PathBuf::from(CARGO_CLIPPY_PATH) + .canonicalize() + .expect("failed to canonicalize path to clippy binary"); + + // assert that clippy is found + assert!( + cargo_clippy_path.is_file(), + "target/debug/cargo-clippy binary not found! {}", + cargo_clippy_path.display() + ); + + let clippy_ver = std::process::Command::new(CARGO_CLIPPY_PATH) + .arg("--version") + .output() + .map(|o| String::from_utf8_lossy(&o.stdout).into_owned()) + .expect("could not get clippy version!"); + + // download and extract the crates, then run clippy on them and collect clippys warnings + // flatten into one big list of warnings + + let crates = read_crates(&config.sources_toml_path); + let old_stats = read_stats_from_file(&config.lintcheck_results_path); + + let counter = AtomicUsize::new(1); + + let clippy_warnings: Vec<ClippyWarning> = if let Some(only_one_crate) = clap_config.value_of("only") { + // if we don't have the specified crate in the .toml, throw an error + if !crates.iter().any(|krate| { + let name = match krate { + CrateSource::CratesIo { name, .. } | CrateSource::Git { name, .. } | CrateSource::Path { name, .. } => { + name + }, + }; + name == only_one_crate + }) { + eprintln!( + "ERROR: could not find crate '{}' in lintcheck/lintcheck_crates.toml", + only_one_crate + ); + std::process::exit(1); + } + + // only check a single crate that was passed via cmdline + crates + .into_iter() + .map(|krate| krate.download_and_extract()) + .filter(|krate| krate.name == only_one_crate) + .flat_map(|krate| krate.run_clippy_lints(&cargo_clippy_path, &AtomicUsize::new(0), 1, 1, config.fix)) + .collect() + } else { + if config.max_jobs > 1 { + // run parallel with rayon + + // Ask rayon for thread count. Assume that half of that is the number of physical cores + // Use one target dir for each core so that we can run N clippys in parallel. + // We need to use different target dirs because cargo would lock them for a single build otherwise, + // killing the parallelism. However this also means that deps will only be reused half/a + // quarter of the time which might result in a longer wall clock runtime + + // This helps when we check many small crates with dep-trees that don't have a lot of branches in + // order to achive some kind of parallelism + + // by default, use a single thread + let num_cpus = config.max_jobs; + let num_crates = crates.len(); + + // check all crates (default) + crates + .into_par_iter() + .map(|krate| krate.download_and_extract()) + .flat_map(|krate| { + krate.run_clippy_lints(&cargo_clippy_path, &counter, num_cpus, num_crates, config.fix) + }) + .collect() + } else { + // run sequential + let num_crates = crates.len(); + crates + .into_iter() + .map(|krate| krate.download_and_extract()) + .flat_map(|krate| krate.run_clippy_lints(&cargo_clippy_path, &counter, 1, num_crates, config.fix)) + .collect() + } + }; + + // if we are in --fix mode, don't change the log files, terminate here + if config.fix { + return; + } + + // generate some stats + let (stats_formatted, new_stats) = gather_stats(&clippy_warnings); + + // grab crashes/ICEs, save the crate name and the ice message + let ices: Vec<(&String, &String)> = clippy_warnings + .iter() + .filter(|warning| warning.is_ice) + .map(|w| (&w.crate_name, &w.message)) + .collect(); + + let mut all_msgs: Vec<String> = clippy_warnings.iter().map(ToString::to_string).collect(); + all_msgs.sort(); + all_msgs.push("\n\n\n\nStats:\n".into()); + all_msgs.push(stats_formatted); + + // save the text into lintcheck-logs/logs.txt + let mut text = clippy_ver; // clippy version number on top + text.push_str(&format!("\n{}", all_msgs.join(""))); + text.push_str("ICEs:\n"); + ices.iter() + .for_each(|(cratename, msg)| text.push_str(&format!("{}: '{}'", cratename, msg))); + + println!("Writing logs to {}", config.lintcheck_results_path.display()); + write(&config.lintcheck_results_path, text).unwrap(); + + print_stats(old_stats, new_stats); +} + +/// read the previous stats from the lintcheck-log file +fn read_stats_from_file(file_path: &Path) -> HashMap<String, usize> { + let file_content: String = match std::fs::read_to_string(file_path).ok() { + Some(content) => content, + None => { + return HashMap::new(); + }, + }; + + let lines: Vec<String> = file_content.lines().map(ToString::to_string).collect(); + + // search for the beginning "Stats:" and the end "ICEs:" of the section we want + let start = lines.iter().position(|line| line == "Stats:").unwrap(); + let end = lines.iter().position(|line| line == "ICEs:").unwrap(); + + let stats_lines = &lines[start + 1..end]; + + stats_lines + .iter() + .map(|line| { + let mut spl = line.split(' '); + ( + spl.next().unwrap().to_string(), + spl.next().unwrap().parse::<usize>().unwrap(), + ) + }) + .collect::<HashMap<String, usize>>() +} + +/// print how lint counts changed between runs +fn print_stats(old_stats: HashMap<String, usize>, new_stats: HashMap<&String, usize>) { + let same_in_both_hashmaps = old_stats + .iter() + .filter(|(old_key, old_val)| new_stats.get::<&String>(&old_key) == Some(old_val)) + .map(|(k, v)| (k.to_string(), *v)) + .collect::<Vec<(String, usize)>>(); + + let mut old_stats_deduped = old_stats; + let mut new_stats_deduped = new_stats; + + // remove duplicates from both hashmaps + same_in_both_hashmaps.iter().for_each(|(k, v)| { + assert!(old_stats_deduped.remove(k) == Some(*v)); + assert!(new_stats_deduped.remove(k) == Some(*v)); + }); + + println!("\nStats:"); + + // list all new counts (key is in new stats but not in old stats) + new_stats_deduped + .iter() + .filter(|(new_key, _)| old_stats_deduped.get::<str>(&new_key).is_none()) + .for_each(|(new_key, new_value)| { + println!("{} 0 => {}", new_key, new_value); + }); + + // list all changed counts (key is in both maps but value differs) + new_stats_deduped + .iter() + .filter(|(new_key, _new_val)| old_stats_deduped.get::<str>(&new_key).is_some()) + .for_each(|(new_key, new_val)| { + let old_val = old_stats_deduped.get::<str>(&new_key).unwrap(); + println!("{} {} => {}", new_key, old_val, new_val); + }); + + // list all gone counts (key is in old status but not in new stats) + old_stats_deduped + .iter() + .filter(|(old_key, _)| new_stats_deduped.get::<&String>(&old_key).is_none()) + .for_each(|(old_key, old_value)| { + println!("{} {} => 0", old_key, old_value); + }); +} + +/// Create necessary directories to run the lintcheck tool. +/// +/// # Panics +/// +/// This function panics if creating one of the dirs fails. +fn create_dirs(krate_download_dir: &Path, extract_dir: &Path) { + std::fs::create_dir("target/lintcheck/").unwrap_or_else(|err| { + if err.kind() != ErrorKind::AlreadyExists { + panic!("cannot create lintcheck target dir"); + } + }); + std::fs::create_dir(&krate_download_dir).unwrap_or_else(|err| { + if err.kind() != ErrorKind::AlreadyExists { + panic!("cannot create crate download dir"); + } + }); + std::fs::create_dir(&extract_dir).unwrap_or_else(|err| { + if err.kind() != ErrorKind::AlreadyExists { + panic!("cannot create crate extraction dir"); + } + }); +} + +fn get_clap_config<'a>() -> ArgMatches<'a> { + App::new("lintcheck") + .about("run clippy on a set of crates and check output") + .arg( + Arg::with_name("only") + .takes_value(true) + .value_name("CRATE") + .long("only") + .help("only process a single crate of the list"), + ) + .arg( + Arg::with_name("crates-toml") + .takes_value(true) + .value_name("CRATES-SOURCES-TOML-PATH") + .long("crates-toml") + .help("set the path for a crates.toml where lintcheck should read the sources from"), + ) + .arg( + Arg::with_name("threads") + .takes_value(true) + .value_name("N") + .short("j") + .long("jobs") + .help("number of threads to use, 0 automatic choice"), + ) + .arg( + Arg::with_name("fix") + .long("--fix") + .help("runs cargo clippy --fix and checks if all suggestions apply"), + ) + .get_matches() +} + +/// Returns the path to the Clippy project directory +/// +/// # Panics +/// +/// Panics if the current directory could not be retrieved, there was an error reading any of the +/// Cargo.toml files or ancestor directory is the clippy root directory +#[must_use] +pub fn clippy_project_root() -> PathBuf { + let current_dir = std::env::current_dir().unwrap(); + for path in current_dir.ancestors() { + let result = std::fs::read_to_string(path.join("Cargo.toml")); + if let Err(err) = &result { + if err.kind() == std::io::ErrorKind::NotFound { + continue; + } + } + + let content = result.unwrap(); + if content.contains("[package]\nname = \"clippy\"") { + return path.to_path_buf(); + } + } + panic!("error: Can't determine root of project. Please run inside a Clippy working dir."); +} + +#[test] +fn lintcheck_test() { + let args = [ + "run", + "--target-dir", + "lintcheck/target", + "--manifest-path", + "./lintcheck/Cargo.toml", + "--", + "--crates-toml", + "lintcheck/test_sources.toml", + ]; + let status = std::process::Command::new("cargo") + .args(&args) + .current_dir("..") // repo root + .status(); + //.output(); + + assert!(status.unwrap().success()); +} diff --git a/src/tools/clippy/lintcheck/test_sources.toml b/src/tools/clippy/lintcheck/test_sources.toml new file mode 100644 index 0000000000000..4b0eb71ef4bfe --- /dev/null +++ b/src/tools/clippy/lintcheck/test_sources.toml @@ -0,0 +1,4 @@ +[crates] +cc = {name = "cc", versions = ['1.0.67']} +home = {name = "home", git_url = "https://github.com/brson/home", git_hash = "32044e53dfbdcd32bafad3109d1fbab805fc0f40"} +rustc_tools_util = {name = "rustc_tools_util", versions = ['0.2.0']} diff --git a/src/tools/clippy/mini-macro/Cargo.toml b/src/tools/clippy/mini-macro/Cargo.toml index 75ab17588a7f4..0d95c86aef030 100644 --- a/src/tools/clippy/mini-macro/Cargo.toml +++ b/src/tools/clippy/mini-macro/Cargo.toml @@ -1,13 +1,7 @@ [package] name = "clippy-mini-macro-test" version = "0.2.0" -authors = [ - "Manish Goregaokar <manishsmail@gmail.com>", - "Andre Bogus <bogusandre@gmail.com>", - "Georg Brandl <georg@python.org>", - "Martin Carton <cartonmartin@gmail.com>", - "Oliver Schneider <clippy-iethah7aipeen8neex1a@oli-obk.de>" -] +authors = ["The Rust Clippy Developers"] license = "MIT OR Apache-2.0" description = "A macro to test clippy's procedural macro checks" repository = "https://github.com/rust-lang/rust-clippy" diff --git a/src/tools/clippy/rust-toolchain b/src/tools/clippy/rust-toolchain index 865043b46d179..7e4d12b8632be 100644 --- a/src/tools/clippy/rust-toolchain +++ b/src/tools/clippy/rust-toolchain @@ -1,3 +1,3 @@ [toolchain] -channel = "nightly-2021-02-25" +channel = "nightly-2021-04-08" components = ["llvm-tools-preview", "rustc-dev", "rust-src"] diff --git a/src/tools/clippy/rustc_tools_util/Cargo.toml b/src/tools/clippy/rustc_tools_util/Cargo.toml index 6f0fc5bee8f09..2972bc6d51ca8 100644 --- a/src/tools/clippy/rustc_tools_util/Cargo.toml +++ b/src/tools/clippy/rustc_tools_util/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "rustc_tools_util" version = "0.2.0" -authors = ["Matthias Krüger <matthias.krueger@famsik.de>"] +authors = ["The Rust Clippy Developers"] description = "small helper to generate version information for git packages" repository = "https://github.com/rust-lang/rust-clippy" readme = "README.md" diff --git a/src/tools/clippy/rustfmt.toml b/src/tools/clippy/rustfmt.toml index f1241e74b0a3d..4b415a31b272e 100644 --- a/src/tools/clippy/rustfmt.toml +++ b/src/tools/clippy/rustfmt.toml @@ -4,3 +4,4 @@ match_block_trailing_comma = true wrap_comments = true edition = "2018" error_on_line_overflow = true +version = "Two" diff --git a/src/tools/clippy/src/driver.rs b/src/tools/clippy/src/driver.rs index d5143e1438ee0..fa0c5f01430b6 100644 --- a/src/tools/clippy/src/driver.rs +++ b/src/tools/clippy/src/driver.rs @@ -11,8 +11,12 @@ extern crate rustc_driver; extern crate rustc_errors; extern crate rustc_interface; +extern crate rustc_session; +extern crate rustc_span; use rustc_interface::interface; +use rustc_session::parse::ParseSess; +use rustc_span::symbol::Symbol; use rustc_tools_util::VersionInfo; use std::borrow::Cow; @@ -59,31 +63,60 @@ fn test_arg_value() { assert_eq!(arg_value(args, "--foo", |_| true), None); } +fn track_clippy_args(parse_sess: &mut ParseSess, args_env_var: &Option<String>) { + parse_sess.env_depinfo.get_mut().insert(( + Symbol::intern("CLIPPY_ARGS"), + args_env_var.as_deref().map(Symbol::intern), + )); +} + struct DefaultCallbacks; impl rustc_driver::Callbacks for DefaultCallbacks {} -struct ClippyCallbacks; +/// This is different from `DefaultCallbacks` that it will inform Cargo to track the value of +/// `CLIPPY_ARGS` environment variable. +struct RustcCallbacks { + clippy_args_var: Option<String>, +} + +impl rustc_driver::Callbacks for RustcCallbacks { + fn config(&mut self, config: &mut interface::Config) { + let clippy_args_var = self.clippy_args_var.take(); + config.parse_sess_created = Some(Box::new(move |parse_sess| { + track_clippy_args(parse_sess, &clippy_args_var); + })); + } +} + +struct ClippyCallbacks { + clippy_args_var: Option<String>, +} + impl rustc_driver::Callbacks for ClippyCallbacks { fn config(&mut self, config: &mut interface::Config) { let previous = config.register_lints.take(); - config.register_lints = Some(Box::new(move |sess, mut lint_store| { + let clippy_args_var = self.clippy_args_var.take(); + config.parse_sess_created = Some(Box::new(move |parse_sess| { + track_clippy_args(parse_sess, &clippy_args_var); + })); + config.register_lints = Some(Box::new(move |sess, lint_store| { // technically we're ~guaranteed that this is none but might as well call anything that // is there already. Certainly it can't hurt. if let Some(previous) = &previous { (previous)(sess, lint_store); } - let conf = clippy_lints::read_conf(&[], &sess); - clippy_lints::register_plugins(&mut lint_store, &sess, &conf); - clippy_lints::register_pre_expansion_lints(&mut lint_store); - clippy_lints::register_renamed(&mut lint_store); + let conf = clippy_lints::read_conf(&[], sess); + clippy_lints::register_plugins(lint_store, sess, &conf); + clippy_lints::register_pre_expansion_lints(lint_store); + clippy_lints::register_renamed(lint_store); })); // FIXME: #4825; This is required, because Clippy lints that are based on MIR have to be // run on the unoptimized MIR. On the other hand this results in some false negatives. If // MIR passes can be enabled / disabled separately, we should figure out, what passes to // use for Clippy. - config.opts.debugging_opts.mir_opt_level = 0; + config.opts.debugging_opts.mir_opt_level = Some(0); } } @@ -158,7 +191,7 @@ fn report_clippy_ice(info: &panic::PanicInfo<'_>, bug_report_url: &str) { ]; for note in &xs { - handler.note_without_error(¬e); + handler.note_without_error(note); } // If backtraces are enabled, also print the query stack @@ -277,7 +310,9 @@ pub fn main() { }; let mut no_deps = false; - let clippy_args = env::var("CLIPPY_ARGS") + let clippy_args_var = env::var("CLIPPY_ARGS").ok(); + let clippy_args = clippy_args_var + .as_deref() .unwrap_or_default() .split("__CLIPPY_HACKERY__") .filter_map(|s| match s { @@ -305,11 +340,10 @@ pub fn main() { args.extend(clippy_args); } - let mut clippy = ClippyCallbacks; - let mut default = DefaultCallbacks; - let callbacks: &mut (dyn rustc_driver::Callbacks + Send) = - if clippy_enabled { &mut clippy } else { &mut default }; - - rustc_driver::RunCompiler::new(&args, callbacks).run() + if clippy_enabled { + rustc_driver::RunCompiler::new(&args, &mut ClippyCallbacks { clippy_args_var }).run() + } else { + rustc_driver::RunCompiler::new(&args, &mut RustcCallbacks { clippy_args_var }).run() + } })) } diff --git a/src/tools/clippy/src/main.rs b/src/tools/clippy/src/main.rs index d13a831f5ff7d..7bb80b1196e59 100644 --- a/src/tools/clippy/src/main.rs +++ b/src/tools/clippy/src/main.rs @@ -92,12 +92,6 @@ impl ClippyCmd { panic!("Usage of `--fix` requires `-Z unstable-options`"); } - // Run the dogfood tests directly on nightly cargo. This is required due - // to a bug in rustup.rs when running cargo on custom toolchains. See issue #3118. - if env::var_os("CLIPPY_DOGFOOD").is_some() && cfg!(windows) { - args.insert(0, "+nightly".to_string()); - } - let mut clippy_args: Vec<String> = old_args.collect(); if cargo_subcommand == "fix" && !clippy_args.iter().any(|arg| arg == "--no-deps") { clippy_args.push("--no-deps".into()); diff --git a/src/tools/clippy/tests/compile-test.rs b/src/tools/clippy/tests/compile-test.rs index 0594663786ce2..f4d354f0bf942 100644 --- a/src/tools/clippy/tests/compile-test.rs +++ b/src/tools/clippy/tests/compile-test.rs @@ -98,7 +98,7 @@ fn default_config() -> compiletest::Config { fn run_mode(cfg: &mut compiletest::Config) { cfg.mode = TestMode::Ui; cfg.src_base = Path::new("tests").join("ui"); - compiletest::run_tests(&cfg); + compiletest::run_tests(cfg); } fn run_internal_tests(cfg: &mut compiletest::Config) { @@ -108,7 +108,7 @@ fn run_internal_tests(cfg: &mut compiletest::Config) { } cfg.mode = TestMode::Ui; cfg.src_base = Path::new("tests").join("ui-internal"); - compiletest::run_tests(&cfg); + compiletest::run_tests(cfg); } fn run_ui_toml(config: &mut compiletest::Config) { @@ -136,7 +136,7 @@ fn run_ui_toml(config: &mut compiletest::Config) { base: config.src_base.clone(), relative_dir: dir_path.file_name().unwrap().into(), }; - let test_name = compiletest::make_test_name(&config, &paths); + let test_name = compiletest::make_test_name(config, &paths); let index = tests .iter() .position(|test| test.desc.name == test_name) @@ -150,10 +150,10 @@ fn run_ui_toml(config: &mut compiletest::Config) { config.mode = TestMode::Ui; config.src_base = Path::new("tests").join("ui-toml").canonicalize().unwrap(); - let tests = compiletest::make_tests(&config); + let tests = compiletest::make_tests(config); let manifest_dir = var("CARGO_MANIFEST_DIR").unwrap_or_default(); - let res = run_tests(&config, tests); + let res = run_tests(config, tests); set_var("CARGO_MANIFEST_DIR", &manifest_dir); match res { Ok(true) => {}, @@ -221,7 +221,7 @@ fn run_ui_cargo(config: &mut compiletest::Config) { base: config.src_base.clone(), relative_dir: src_path.strip_prefix(&config.src_base).unwrap().into(), }; - let test_name = compiletest::make_test_name(&config, &paths); + let test_name = compiletest::make_test_name(config, &paths); let index = tests .iter() .position(|test| test.desc.name == test_name) @@ -240,11 +240,11 @@ fn run_ui_cargo(config: &mut compiletest::Config) { config.mode = TestMode::Ui; config.src_base = Path::new("tests").join("ui-cargo").canonicalize().unwrap(); - let tests = compiletest::make_tests(&config); + let tests = compiletest::make_tests(config); let current_dir = env::current_dir().unwrap(); let conf_dir = var("CLIPPY_CONF_DIR").unwrap_or_default(); - let res = run_tests(&config, &config.filters, tests); + let res = run_tests(config, &config.filters, tests); env::set_current_dir(current_dir).unwrap(); set_var("CLIPPY_CONF_DIR", conf_dir); diff --git a/src/tools/clippy/tests/dogfood.rs b/src/tools/clippy/tests/dogfood.rs index 052223d6d6ff7..d92530f073f56 100644 --- a/src/tools/clippy/tests/dogfood.rs +++ b/src/tools/clippy/tests/dogfood.rs @@ -3,7 +3,7 @@ #![feature(once_cell)] use std::lazy::SyncLazy; -use std::path::{Path, PathBuf}; +use std::path::PathBuf; use std::process::Command; mod cargo; @@ -23,7 +23,7 @@ fn dogfood_clippy() { .current_dir(root_dir) .env("CLIPPY_DOGFOOD", "1") .env("CARGO_INCREMENTAL", "0") - .arg("clippy-preview") + .arg("clippy") .arg("--all-targets") .arg("--all-features") .arg("--") @@ -45,32 +45,46 @@ fn dogfood_clippy() { assert!(output.status.success()); } -#[test] -fn dogfood_subprojects() { - fn test_no_deps_ignores_path_deps_in_workspaces() { - fn clean(cwd: &Path, target_dir: &Path) { - Command::new("cargo") - .current_dir(cwd) - .env("CARGO_TARGET_DIR", target_dir) - .arg("clean") - .args(&["-p", "subcrate"]) - .args(&["-p", "path_dep"]) - .output() - .unwrap(); - } - - if cargo::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"); +fn test_no_deps_ignores_path_deps_in_workspaces() { + if cargo::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("CLIPPY_DOGFOOD", "1") + .env("CARGO_INCREMENTAL", "0") + .arg("clippy") + .args(&["-p", "subcrate"]) + .arg("--") + .arg("--no-deps") + .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)); - // Make sure we start with a clean state - clean(&cwd, &target_dir); + assert!(output.status.success()); - // `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 lint_path_dep = || { + // Test that without the `--no-deps` argument, `path_dep` is linted. let output = Command::new(&*CLIPPY_PATH) .current_dir(&cwd) .env("CLIPPY_DOGFOOD", "1") @@ -78,7 +92,6 @@ fn dogfood_subprojects() { .arg("clippy") .args(&["-p", "subcrate"]) .arg("--") - .arg("--no-deps") .arg("-Cdebuginfo=0") // disable debuginfo to generate less data in the target dir .args(&["--cfg", r#"feature="primary_package_test""#]) .output() @@ -87,12 +100,18 @@ fn dogfood_subprojects() { println!("stdout: {}", String::from_utf8_lossy(&output.stdout)); println!("stderr: {}", String::from_utf8_lossy(&output.stderr)); - assert!(output.status.success()); + assert!(!output.status.success()); + assert!( + String::from_utf8(output.stderr) + .unwrap() + .contains("error: empty `loop {}` wastes CPU cycles") + ); + }; - // Make sure we start with a clean state - clean(&cwd, &target_dir); + // Make sure Cargo is aware of the removal of `--no-deps`. + lint_path_dep(); - // Test that without the `--no-deps` argument, `path_dep` is linted. + let successful_build = || { let output = Command::new(&*CLIPPY_PATH) .current_dir(&cwd) .env("CLIPPY_DOGFOOD", "1") @@ -101,16 +120,32 @@ fn dogfood_subprojects() { .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!(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; @@ -124,19 +159,30 @@ fn dogfood_subprojects() { "clippy_workspace_tests/subcrate", "clippy_workspace_tests/subcrate/src", "clippy_dev", + "clippy_lints", + "clippy_utils", "rustc_tools_util", ] { - let output = Command::new(&*CLIPPY_PATH) + let mut command = Command::new(&*CLIPPY_PATH); + command .current_dir(root_dir.join(d)) .env("CLIPPY_DOGFOOD", "1") .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 - .output() - .unwrap(); + .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)); diff --git a/src/tools/clippy/tests/lint_message_convention.rs b/src/tools/clippy/tests/lint_message_convention.rs new file mode 100644 index 0000000000000..2f8989c8e1140 --- /dev/null +++ b/src/tools/clippy/tests/lint_message_convention.rs @@ -0,0 +1,108 @@ +use std::ffi::OsStr; +use std::path::PathBuf; + +use regex::RegexSet; + +#[derive(Debug)] +struct Message { + path: PathBuf, + bad_lines: Vec<String>, +} + +impl Message { + fn new(path: PathBuf) -> Self { + let content: String = std::fs::read_to_string(&path).unwrap(); + // we don't want the first letter after "error: ", "help: " ... to be capitalized + // also no puncutation (except for "?" ?) at the end of a line + let regex_set: RegexSet = RegexSet::new(&[ + r"error: [A-Z]", + r"help: [A-Z]", + r"warning: [A-Z]", + r"note: [A-Z]", + r"try this: [A-Z]", + r"error: .*[.!]$", + r"help: .*[.!]$", + r"warning: .*[.!]$", + r"note: .*[.!]$", + r"try this: .*[.!]$", + ]) + .unwrap(); + + // sometimes the first character is capitalized and it is legal (like in "C-like enum variants") or + // we want to ask a question ending in "?" + let exceptions_set: RegexSet = RegexSet::new(&[ + r".*C-like enum variant discriminant is not portable to 32-bit targets", + r".*did you mean `unix`?", + r".*the arguments may be inverted...", + r".*Intel x86 assembly syntax used", + r".*AT&T x86 assembly syntax used", + r".*remove .*the return type...", + r"note: Clippy version: .*", + r"the compiler unexpectedly panicked. this is a bug.", + ]) + .unwrap(); + + let bad_lines = content + .lines() + .filter(|line| regex_set.matches(line).matched_any()) + // ignore exceptions + .filter(|line| !exceptions_set.matches(line).matched_any()) + .map(ToOwned::to_owned) + .collect::<Vec<String>>(); + + Message { path, bad_lines } + } +} + +#[test] +fn lint_message_convention() { + // disable the test inside the rustc test suite + if option_env!("RUSTC_TEST_SUITE").is_some() { + return; + } + + // make sure that lint messages: + // * are not capitalized + // * don't have puncuation at the end of the last sentence + + // these directories have interesting tests + let test_dirs = ["ui", "ui-cargo", "ui-internal", "ui-toml"] + .iter() + .map(PathBuf::from) + .map(|p| { + let base = PathBuf::from("tests"); + base.join(p) + }); + + // gather all .stderr files + let tests = test_dirs + .flat_map(|dir| { + std::fs::read_dir(dir) + .expect("failed to read dir") + .map(|direntry| direntry.unwrap().path()) + }) + .filter(|file| matches!(file.extension().map(OsStr::to_str), Some(Some("stderr")))); + + // get all files that have any "bad lines" in them + let bad_tests: Vec<Message> = tests + .map(Message::new) + .filter(|message| !message.bad_lines.is_empty()) + .collect(); + + for message in &bad_tests { + eprintln!( + "error: the test '{}' contained the following nonconforming lines :", + message.path.display() + ); + message.bad_lines.iter().for_each(|line| eprintln!("{}", line)); + eprintln!("\n\n"); + } + + eprintln!( + "\n\n\nLint message should not start with a capital letter and should not have punctuation at the end of the message unless multiple sentences are needed." + ); + eprintln!("Check out the rustc-dev-guide for more information:"); + eprintln!("https://rustc-dev-guide.rust-lang.org/diagnostics.html#diagnostic-structure\n\n\n"); + + assert!(bad_tests.is_empty()); +} diff --git a/src/tools/clippy/tests/ui-cargo/cargo_common_metadata/fail/src/main.stderr b/src/tools/clippy/tests/ui-cargo/cargo_common_metadata/fail/src/main.stderr index c8ae6c820df9d..5e9aa8dc36a6e 100644 --- a/src/tools/clippy/tests/ui-cargo/cargo_common_metadata/fail/src/main.stderr +++ b/src/tools/clippy/tests/ui-cargo/cargo_common_metadata/fail/src/main.stderr @@ -1,9 +1,7 @@ -error: package `cargo_common_metadata` is missing `package.authors` metadata +error: package `cargo_common_metadata` is missing `package.description` metadata | = note: `-D clippy::cargo-common-metadata` implied by `-D warnings` -error: package `cargo_common_metadata` is missing `package.description` metadata - error: package `cargo_common_metadata` is missing `either package.license or package.license_file` metadata error: package `cargo_common_metadata` is missing `package.repository` metadata @@ -14,5 +12,5 @@ error: package `cargo_common_metadata` is missing `package.keywords` metadata error: package `cargo_common_metadata` is missing `package.categories` metadata -error: aborting due to 7 previous errors +error: aborting due to 6 previous errors diff --git a/src/tools/clippy/tests/ui-cargo/cargo_common_metadata/fail_publish/src/main.stderr b/src/tools/clippy/tests/ui-cargo/cargo_common_metadata/fail_publish/src/main.stderr index c8ae6c820df9d..5e9aa8dc36a6e 100644 --- a/src/tools/clippy/tests/ui-cargo/cargo_common_metadata/fail_publish/src/main.stderr +++ b/src/tools/clippy/tests/ui-cargo/cargo_common_metadata/fail_publish/src/main.stderr @@ -1,9 +1,7 @@ -error: package `cargo_common_metadata` is missing `package.authors` metadata +error: package `cargo_common_metadata` is missing `package.description` metadata | = note: `-D clippy::cargo-common-metadata` implied by `-D warnings` -error: package `cargo_common_metadata` is missing `package.description` metadata - error: package `cargo_common_metadata` is missing `either package.license or package.license_file` metadata error: package `cargo_common_metadata` is missing `package.repository` metadata @@ -14,5 +12,5 @@ error: package `cargo_common_metadata` is missing `package.keywords` metadata error: package `cargo_common_metadata` is missing `package.categories` metadata -error: aborting due to 7 previous errors +error: aborting due to 6 previous errors diff --git a/src/tools/clippy/tests/ui-cargo/cargo_common_metadata/fail_publish_true/src/main.stderr b/src/tools/clippy/tests/ui-cargo/cargo_common_metadata/fail_publish_true/src/main.stderr index c8ae6c820df9d..5e9aa8dc36a6e 100644 --- a/src/tools/clippy/tests/ui-cargo/cargo_common_metadata/fail_publish_true/src/main.stderr +++ b/src/tools/clippy/tests/ui-cargo/cargo_common_metadata/fail_publish_true/src/main.stderr @@ -1,9 +1,7 @@ -error: package `cargo_common_metadata` is missing `package.authors` metadata +error: package `cargo_common_metadata` is missing `package.description` metadata | = note: `-D clippy::cargo-common-metadata` implied by `-D warnings` -error: package `cargo_common_metadata` is missing `package.description` metadata - error: package `cargo_common_metadata` is missing `either package.license or package.license_file` metadata error: package `cargo_common_metadata` is missing `package.repository` metadata @@ -14,5 +12,5 @@ error: package `cargo_common_metadata` is missing `package.keywords` metadata error: package `cargo_common_metadata` is missing `package.categories` metadata -error: aborting due to 7 previous errors +error: aborting due to 6 previous errors diff --git a/src/tools/clippy/tests/ui-cargo/cargo_common_metadata/pass/Cargo.toml b/src/tools/clippy/tests/ui-cargo/cargo_common_metadata/pass/Cargo.toml index 737e84e963c95..cb4774d43a2c1 100644 --- a/src/tools/clippy/tests/ui-cargo/cargo_common_metadata/pass/Cargo.toml +++ b/src/tools/clippy/tests/ui-cargo/cargo_common_metadata/pass/Cargo.toml @@ -2,7 +2,6 @@ name = "cargo_common_metadata" version = "0.1.0" publish = false -authors = ["Random person from the Internet <someone@someplace.org>"] description = "A test package for the cargo_common_metadata lint" repository = "https://github.com/someone/cargo_common_metadata" readme = "README.md" diff --git a/src/tools/clippy/tests/ui-internal/if_chain_style.rs b/src/tools/clippy/tests/ui-internal/if_chain_style.rs new file mode 100644 index 0000000000000..8e871707aa8f3 --- /dev/null +++ b/src/tools/clippy/tests/ui-internal/if_chain_style.rs @@ -0,0 +1,92 @@ +#![warn(clippy::if_chain_style)] +#![allow(clippy::no_effect)] + +extern crate if_chain; + +use if_chain::if_chain; + +fn main() { + if true { + let x = ""; + // `if_chain!` inside `if` + if_chain! { + if true; + if true; + then {} + } + } + if_chain! { + if true + // multi-line AND'ed conditions + && false; + if let Some(1) = Some(1); + // `let` before `then` + let x = ""; + then { + (); + } + } + if_chain! { + // single `if` condition + if true; + then { + let x = ""; + // nested if + if true {} + } + } + if_chain! { + // starts with `let ..` + let x = ""; + if let Some(1) = Some(1); + then { + let x = ""; + let x = ""; + // nested if_chain! + if_chain! { + if true; + if true; + then {} + } + } + } +} + +fn negative() { + if true { + (); + if_chain! { + if true; + if true; + then { (); } + } + } + if_chain! { + if true; + let x = ""; + if true; + then { (); } + } + if_chain! { + if true; + if true; + then { + if true { 1 } else { 2 } + } else { + 3 + } + }; + if true { + if_chain! { + if true; + if true; + then {} + } + } else if false { + if_chain! { + if true; + if false; + then {} + } + } +} diff --git a/src/tools/clippy/tests/ui-internal/if_chain_style.stderr b/src/tools/clippy/tests/ui-internal/if_chain_style.stderr new file mode 100644 index 0000000000000..b53c3ea05da9f --- /dev/null +++ b/src/tools/clippy/tests/ui-internal/if_chain_style.stderr @@ -0,0 +1,85 @@ +error: this `if` can be part of the inner `if_chain!` + --> $DIR/if_chain_style.rs:9:5 + | +LL | / if true { +LL | | let x = ""; +LL | | // `if_chain!` inside `if` +LL | | if_chain! { +... | +LL | | } +LL | | } + | |_____^ + | + = note: `-D clippy::if-chain-style` implied by `-D warnings` +help: this `let` statement can also be in the `if_chain!` + --> $DIR/if_chain_style.rs:10:9 + | +LL | let x = ""; + | ^^^^^^^^^^^ + +error: `if a && b;` should be `if a; if b;` + --> $DIR/if_chain_style.rs:19:12 + | +LL | if true + | ____________^ +LL | | // multi-line AND'ed conditions +LL | | && false; + | |____________________^ + +error: `let` expression should be inside `then { .. }` + --> $DIR/if_chain_style.rs:24:9 + | +LL | let x = ""; + | ^^^^^^^^^^^ + +error: this `if` can be part of the outer `if_chain!` + --> $DIR/if_chain_style.rs:35:13 + | +LL | if true {} + | ^^^^^^^^^^ + | +help: this `let` statement can also be in the `if_chain!` + --> $DIR/if_chain_style.rs:33:13 + | +LL | let x = ""; + | ^^^^^^^^^^^ + +error: `if_chain!` only has one `if` + --> $DIR/if_chain_style.rs:29:5 + | +LL | / if_chain! { +LL | | // single `if` condition +LL | | if true; +LL | | then { +... | +LL | | } +LL | | } + | |_____^ + | + = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: `let` expression should be above the `if_chain!` + --> $DIR/if_chain_style.rs:40:9 + | +LL | let x = ""; + | ^^^^^^^^^^^ + +error: this `if_chain!` can be merged with the outer `if_chain!` + --> $DIR/if_chain_style.rs:46:13 + | +LL | / if_chain! { +LL | | if true; +LL | | if true; +LL | | then {} +LL | | } + | |_____________^ + | +help: these `let` statements can also be in the `if_chain!` + --> $DIR/if_chain_style.rs:43:13 + | +LL | / let x = ""; +LL | | let x = ""; + | |_______________________^ + +error: aborting due to 7 previous errors + diff --git a/src/tools/clippy/tests/ui-toml/upper_case_acronyms_aggressive/upper_case_acronyms.rs b/src/tools/clippy/tests/ui-toml/upper_case_acronyms_aggressive/upper_case_acronyms.rs index fdf8905f812f6..1a5cf1b1947c5 100644 --- a/src/tools/clippy/tests/ui-toml/upper_case_acronyms_aggressive/upper_case_acronyms.rs +++ b/src/tools/clippy/tests/ui-toml/upper_case_acronyms_aggressive/upper_case_acronyms.rs @@ -16,7 +16,29 @@ enum Flags { FIN, } -struct GCCLLVMSomething; // linted with cfg option, beware that lint suggests `GccllvmSomething` instead of - // `GccLlvmSomething` +// linted with cfg option, beware that lint suggests `GccllvmSomething` instead of +// `GccLlvmSomething` +struct GCCLLVMSomething; + +// don't warn on public items +pub struct MIXEDCapital; + +pub struct FULLCAPITAL; + +// enum variants should not be linted if the num is pub +pub enum ParseError<T> { + FULLCAPITAL(u8), + MIXEDCapital(String), + Utf8(std::string::FromUtf8Error), + Parse(T, String), +} + +// private, do lint here +enum ParseErrorPrivate<T> { + WASD(u8), + WASDMixed(String), + Utf8(std::string::FromUtf8Error), + Parse(T, String), +} fn main() {} diff --git a/src/tools/clippy/tests/ui-toml/upper_case_acronyms_aggressive/upper_case_acronyms.stderr b/src/tools/clippy/tests/ui-toml/upper_case_acronyms_aggressive/upper_case_acronyms.stderr index 1cc59dc45f2aa..02f29bbefe14a 100644 --- a/src/tools/clippy/tests/ui-toml/upper_case_acronyms_aggressive/upper_case_acronyms.stderr +++ b/src/tools/clippy/tests/ui-toml/upper_case_acronyms_aggressive/upper_case_acronyms.stderr @@ -61,10 +61,22 @@ LL | FIN, | ^^^ help: consider making the acronym lowercase, except the initial letter: `Fin` error: name `GCCLLVMSomething` contains a capitalized acronym - --> $DIR/upper_case_acronyms.rs:19:8 + --> $DIR/upper_case_acronyms.rs:21:8 | -LL | struct GCCLLVMSomething; // linted with cfg option, beware that lint suggests `GccllvmSomething` instead of +LL | struct GCCLLVMSomething; | ^^^^^^^^^^^^^^^^ help: consider making the acronym lowercase, except the initial letter: `GccllvmSomething` -error: aborting due to 11 previous errors +error: name `WASD` contains a capitalized acronym + --> $DIR/upper_case_acronyms.rs:38:5 + | +LL | WASD(u8), + | ^^^^ help: consider making the acronym lowercase, except the initial letter: `Wasd` + +error: name `WASDMixed` contains a capitalized acronym + --> $DIR/upper_case_acronyms.rs:39:5 + | +LL | WASDMixed(String), + | ^^^^^^^^^ help: consider making the acronym lowercase, except the initial letter: `WasdMixed` + +error: aborting due to 13 previous errors diff --git a/src/tools/clippy/tests/ui-toml/vec_box_sized/test.stderr b/src/tools/clippy/tests/ui-toml/vec_box_sized/test.stderr index 3bdeca0bc8774..cf194de3c5537 100644 --- a/src/tools/clippy/tests/ui-toml/vec_box_sized/test.stderr +++ b/src/tools/clippy/tests/ui-toml/vec_box_sized/test.stderr @@ -1,4 +1,4 @@ -error: `Vec<T>` is already on the heap, the boxing is unnecessary. +error: `Vec<T>` is already on the heap, the boxing is unnecessary --> $DIR/test.rs:9:12 | LL | struct Foo(Vec<Box<u8>>); @@ -6,13 +6,13 @@ LL | struct Foo(Vec<Box<u8>>); | = note: `-D clippy::vec-box` implied by `-D warnings` -error: `Vec<T>` is already on the heap, the boxing is unnecessary. +error: `Vec<T>` is already on the heap, the boxing is unnecessary --> $DIR/test.rs:10:12 | LL | struct Bar(Vec<Box<u32>>); | ^^^^^^^^^^^^^ help: try: `Vec<u32>` -error: `Vec<T>` is already on the heap, the boxing is unnecessary. +error: `Vec<T>` is already on the heap, the boxing is unnecessary --> $DIR/test.rs:13:18 | LL | struct FooBarBaz(Vec<Box<C>>); diff --git a/src/tools/clippy/tests/ui/assign_ops2.stderr b/src/tools/clippy/tests/ui/assign_ops2.stderr index 70b15d18a568b..e40668ed339f2 100644 --- a/src/tools/clippy/tests/ui/assign_ops2.stderr +++ b/src/tools/clippy/tests/ui/assign_ops2.stderr @@ -5,7 +5,7 @@ LL | a += a + 1; | ^^^^^^^^^^ | = note: `-D clippy::misrefactored-assign-op` implied by `-D warnings` -help: Did you mean `a = a + 1` or `a = a + a + 1`? Consider replacing it with +help: did you mean `a = a + 1` or `a = a + a + 1`? Consider replacing it with | LL | a += 1; | ^^^^^^ @@ -20,7 +20,7 @@ error: variable appears on both sides of an assignment operation LL | a += 1 + a; | ^^^^^^^^^^ | -help: Did you mean `a = a + 1` or `a = a + 1 + a`? Consider replacing it with +help: did you mean `a = a + 1` or `a = a + 1 + a`? Consider replacing it with | LL | a += 1; | ^^^^^^ @@ -35,7 +35,7 @@ error: variable appears on both sides of an assignment operation LL | a -= a - 1; | ^^^^^^^^^^ | -help: Did you mean `a = a - 1` or `a = a - (a - 1)`? Consider replacing it with +help: did you mean `a = a - 1` or `a = a - (a - 1)`? Consider replacing it with | LL | a -= 1; | ^^^^^^ @@ -50,7 +50,7 @@ error: variable appears on both sides of an assignment operation LL | a *= a * 99; | ^^^^^^^^^^^ | -help: Did you mean `a = a * 99` or `a = a * a * 99`? Consider replacing it with +help: did you mean `a = a * 99` or `a = a * a * 99`? Consider replacing it with | LL | a *= 99; | ^^^^^^^ @@ -65,7 +65,7 @@ error: variable appears on both sides of an assignment operation LL | a *= 42 * a; | ^^^^^^^^^^^ | -help: Did you mean `a = a * 42` or `a = a * 42 * a`? Consider replacing it with +help: did you mean `a = a * 42` or `a = a * 42 * a`? Consider replacing it with | LL | a *= 42; | ^^^^^^^ @@ -80,7 +80,7 @@ error: variable appears on both sides of an assignment operation LL | a /= a / 2; | ^^^^^^^^^^ | -help: Did you mean `a = a / 2` or `a = a / (a / 2)`? Consider replacing it with +help: did you mean `a = a / 2` or `a = a / (a / 2)`? Consider replacing it with | LL | a /= 2; | ^^^^^^ @@ -95,7 +95,7 @@ error: variable appears on both sides of an assignment operation LL | a %= a % 5; | ^^^^^^^^^^ | -help: Did you mean `a = a % 5` or `a = a % (a % 5)`? Consider replacing it with +help: did you mean `a = a % 5` or `a = a % (a % 5)`? Consider replacing it with | LL | a %= 5; | ^^^^^^ @@ -110,7 +110,7 @@ error: variable appears on both sides of an assignment operation LL | a &= a & 1; | ^^^^^^^^^^ | -help: Did you mean `a = a & 1` or `a = a & a & 1`? Consider replacing it with +help: did you mean `a = a & 1` or `a = a & a & 1`? Consider replacing it with | LL | a &= 1; | ^^^^^^ @@ -125,7 +125,7 @@ error: variable appears on both sides of an assignment operation LL | a *= a * a; | ^^^^^^^^^^ | -help: Did you mean `a = a * a` or `a = a * a * a`? Consider replacing it with +help: did you mean `a = a * a` or `a = a * a * a`? Consider replacing it with | LL | a *= a; | ^^^^^^ diff --git a/src/tools/clippy/tests/ui/auxiliary/macro_rules.rs b/src/tools/clippy/tests/ui/auxiliary/macro_rules.rs index d6ecd8568ce78..d4470d3f40708 100644 --- a/src/tools/clippy/tests/ui/auxiliary/macro_rules.rs +++ b/src/tools/clippy/tests/ui/auxiliary/macro_rules.rs @@ -23,11 +23,7 @@ macro_rules! try_err { pub fn try_err_fn() -> Result<i32, i32> { let err: i32 = 1; // To avoid warnings during rustfix - if true { - Err(err)? - } else { - Ok(2) - } + if true { Err(err)? } else { Ok(2) } } }; } diff --git a/src/tools/clippy/tests/ui/auxiliary/option_helpers.rs b/src/tools/clippy/tests/ui/auxiliary/option_helpers.rs index ed11c41e21c1a..7dc3f4ebd4d46 100644 --- a/src/tools/clippy/tests/ui/auxiliary/option_helpers.rs +++ b/src/tools/clippy/tests/ui/auxiliary/option_helpers.rs @@ -48,4 +48,8 @@ impl IteratorFalsePositives { pub fn skip_while(self) -> IteratorFalsePositives { self } + + pub fn count(self) -> usize { + self.foo as usize + } } diff --git a/src/tools/clippy/tests/ui/await_holding_lock.stderr b/src/tools/clippy/tests/ui/await_holding_lock.stderr index 21bf49d16f048..a5fcff7e0e443 100644 --- a/src/tools/clippy/tests/ui/await_holding_lock.stderr +++ b/src/tools/clippy/tests/ui/await_holding_lock.stderr @@ -1,4 +1,4 @@ -error: this MutexGuard is held across an 'await' point. Consider using an async-aware Mutex type or ensuring the MutexGuard is dropped before calling await. +error: this MutexGuard is held across an 'await' point. Consider using an async-aware Mutex type or ensuring the MutexGuard is dropped before calling await --> $DIR/await_holding_lock.rs:7:9 | LL | let guard = x.lock().unwrap(); @@ -13,7 +13,7 @@ LL | | baz().await LL | | } | |_^ -error: this MutexGuard is held across an 'await' point. Consider using an async-aware Mutex type or ensuring the MutexGuard is dropped before calling await. +error: this MutexGuard is held across an 'await' point. Consider using an async-aware Mutex type or ensuring the MutexGuard is dropped before calling await --> $DIR/await_holding_lock.rs:28:9 | LL | let guard = x.lock().unwrap(); @@ -31,7 +31,7 @@ LL | | first + second + third LL | | } | |_^ -error: this MutexGuard is held across an 'await' point. Consider using an async-aware Mutex type or ensuring the MutexGuard is dropped before calling await. +error: this MutexGuard is held across an 'await' point. Consider using an async-aware Mutex type or ensuring the MutexGuard is dropped before calling await --> $DIR/await_holding_lock.rs:41:13 | LL | let guard = x.lock().unwrap(); @@ -45,7 +45,7 @@ LL | | baz().await LL | | }; | |_____^ -error: this MutexGuard is held across an 'await' point. Consider using an async-aware Mutex type or ensuring the MutexGuard is dropped before calling await. +error: this MutexGuard is held across an 'await' point. Consider using an async-aware Mutex type or ensuring the MutexGuard is dropped before calling await --> $DIR/await_holding_lock.rs:53:13 | LL | let guard = x.lock().unwrap(); diff --git a/src/tools/clippy/tests/ui/await_holding_refcell_ref.stderr b/src/tools/clippy/tests/ui/await_holding_refcell_ref.stderr index b504f04549136..55e41dbca96f8 100644 --- a/src/tools/clippy/tests/ui/await_holding_refcell_ref.stderr +++ b/src/tools/clippy/tests/ui/await_holding_refcell_ref.stderr @@ -1,4 +1,4 @@ -error: this RefCell Ref is held across an 'await' point. Consider ensuring the Ref is dropped before calling await. +error: this RefCell Ref is held across an 'await' point. Consider ensuring the Ref is dropped before calling await --> $DIR/await_holding_refcell_ref.rs:7:9 | LL | let b = x.borrow(); @@ -13,7 +13,7 @@ LL | | baz().await LL | | } | |_^ -error: this RefCell Ref is held across an 'await' point. Consider ensuring the Ref is dropped before calling await. +error: this RefCell Ref is held across an 'await' point. Consider ensuring the Ref is dropped before calling await --> $DIR/await_holding_refcell_ref.rs:12:9 | LL | let b = x.borrow_mut(); @@ -27,7 +27,7 @@ LL | | baz().await LL | | } | |_^ -error: this RefCell Ref is held across an 'await' point. Consider ensuring the Ref is dropped before calling await. +error: this RefCell Ref is held across an 'await' point. Consider ensuring the Ref is dropped before calling await --> $DIR/await_holding_refcell_ref.rs:33:9 | LL | let b = x.borrow_mut(); @@ -45,7 +45,7 @@ LL | | first + second + third LL | | } | |_^ -error: this RefCell Ref is held across an 'await' point. Consider ensuring the Ref is dropped before calling await. +error: this RefCell Ref is held across an 'await' point. Consider ensuring the Ref is dropped before calling await --> $DIR/await_holding_refcell_ref.rs:45:9 | LL | let b = x.borrow_mut(); @@ -63,7 +63,7 @@ LL | | first + second + third LL | | } | |_^ -error: this RefCell Ref is held across an 'await' point. Consider ensuring the Ref is dropped before calling await. +error: this RefCell Ref is held across an 'await' point. Consider ensuring the Ref is dropped before calling await --> $DIR/await_holding_refcell_ref.rs:60:13 | LL | let b = x.borrow_mut(); @@ -77,7 +77,7 @@ LL | | baz().await LL | | }; | |_____^ -error: this RefCell Ref is held across an 'await' point. Consider ensuring the Ref is dropped before calling await. +error: this RefCell Ref is held across an 'await' point. Consider ensuring the Ref is dropped before calling await --> $DIR/await_holding_refcell_ref.rs:72:13 | LL | let b = x.borrow_mut(); diff --git a/src/tools/clippy/tests/ui/blocks_in_if_conditions.fixed b/src/tools/clippy/tests/ui/blocks_in_if_conditions.fixed index 14562c4d32c12..e6e40a9948c91 100644 --- a/src/tools/clippy/tests/ui/blocks_in_if_conditions.fixed +++ b/src/tools/clippy/tests/ui/blocks_in_if_conditions.fixed @@ -4,9 +4,7 @@ #![warn(clippy::nonminimal_bool)] macro_rules! blocky { - () => {{ - true - }}; + () => {{ true }}; } macro_rules! blocky_too { @@ -34,20 +32,12 @@ fn condition_has_block() -> i32 { } fn condition_has_block_with_single_expression() -> i32 { - if true { - 6 - } else { - 10 - } + if true { 6 } else { 10 } } fn condition_is_normal() -> i32 { let x = 3; - if x == 3 { - 6 - } else { - 10 - } + if x == 3 { 6 } else { 10 } } fn condition_is_unsafe_block() { @@ -61,14 +51,15 @@ fn condition_is_unsafe_block() { fn block_in_assert() { let opt = Some(42); - assert!(opt - .as_ref() - .map(|val| { - let mut v = val * 2; - v -= 1; - v * 3 - }) - .is_some()); + assert!( + opt.as_ref() + .map(|val| { + let mut v = val * 2; + v -= 1; + v * 3 + }) + .is_some() + ); } fn main() {} diff --git a/src/tools/clippy/tests/ui/blocks_in_if_conditions.rs b/src/tools/clippy/tests/ui/blocks_in_if_conditions.rs index bda87650f6da2..69387ff5782b3 100644 --- a/src/tools/clippy/tests/ui/blocks_in_if_conditions.rs +++ b/src/tools/clippy/tests/ui/blocks_in_if_conditions.rs @@ -4,9 +4,7 @@ #![warn(clippy::nonminimal_bool)] macro_rules! blocky { - () => {{ - true - }}; + () => {{ true }}; } macro_rules! blocky_too { @@ -34,20 +32,12 @@ fn condition_has_block() -> i32 { } fn condition_has_block_with_single_expression() -> i32 { - if { true } { - 6 - } else { - 10 - } + if { true } { 6 } else { 10 } } fn condition_is_normal() -> i32 { let x = 3; - if true && x == 3 { - 6 - } else { - 10 - } + if true && x == 3 { 6 } else { 10 } } fn condition_is_unsafe_block() { @@ -61,14 +51,15 @@ fn condition_is_unsafe_block() { fn block_in_assert() { let opt = Some(42); - assert!(opt - .as_ref() - .map(|val| { - let mut v = val * 2; - v -= 1; - v * 3 - }) - .is_some()); + assert!( + opt.as_ref() + .map(|val| { + let mut v = val * 2; + v -= 1; + v * 3 + }) + .is_some() + ); } fn main() {} diff --git a/src/tools/clippy/tests/ui/blocks_in_if_conditions.stderr b/src/tools/clippy/tests/ui/blocks_in_if_conditions.stderr index 9bdddc8e15248..9328492733fd8 100644 --- a/src/tools/clippy/tests/ui/blocks_in_if_conditions.stderr +++ b/src/tools/clippy/tests/ui/blocks_in_if_conditions.stderr @@ -1,5 +1,5 @@ error: in an `if` condition, avoid complex blocks or closures with blocks; instead, move the block or closure higher and bind it with a `let` - --> $DIR/blocks_in_if_conditions.rs:26:5 + --> $DIR/blocks_in_if_conditions.rs:24:5 | LL | / if { LL | | let x = 3; @@ -17,15 +17,15 @@ LL | }; if res { | error: omit braces around single expression condition - --> $DIR/blocks_in_if_conditions.rs:37:8 + --> $DIR/blocks_in_if_conditions.rs:35:8 | -LL | if { true } { +LL | if { true } { 6 } else { 10 } | ^^^^^^^^ help: try: `true` error: this boolean expression can be simplified - --> $DIR/blocks_in_if_conditions.rs:46:8 + --> $DIR/blocks_in_if_conditions.rs:40:8 | -LL | if true && x == 3 { +LL | if true && x == 3 { 6 } else { 10 } | ^^^^^^^^^^^^^^ help: try: `x == 3` | = note: `-D clippy::nonminimal-bool` implied by `-D warnings` diff --git a/src/tools/clippy/tests/ui/borrow_interior_mutable_const/others.rs b/src/tools/clippy/tests/ui/borrow_interior_mutable_const/others.rs index ea25729d11d4d..4327f12c37c8e 100644 --- a/src/tools/clippy/tests/ui/borrow_interior_mutable_const/others.rs +++ b/src/tools/clippy/tests/ui/borrow_interior_mutable_const/others.rs @@ -1,5 +1,9 @@ #![warn(clippy::borrow_interior_mutable_const)] -#![allow(clippy::declare_interior_mutable_const, clippy::ref_in_deref)] +#![allow( + clippy::declare_interior_mutable_const, + clippy::ref_in_deref, + clippy::needless_borrow +)] #![allow(const_item_mutation)] use std::borrow::Cow; diff --git a/src/tools/clippy/tests/ui/borrow_interior_mutable_const/others.stderr b/src/tools/clippy/tests/ui/borrow_interior_mutable_const/others.stderr index 9a908cf30e945..f146b97cf6116 100644 --- a/src/tools/clippy/tests/ui/borrow_interior_mutable_const/others.stderr +++ b/src/tools/clippy/tests/ui/borrow_interior_mutable_const/others.stderr @@ -1,5 +1,5 @@ error: a `const` item with interior mutability should not be borrowed - --> $DIR/others.rs:54:5 + --> $DIR/others.rs:58:5 | LL | ATOMIC.store(1, Ordering::SeqCst); //~ ERROR interior mutability | ^^^^^^ @@ -8,7 +8,7 @@ LL | ATOMIC.store(1, Ordering::SeqCst); //~ ERROR interior mutability = help: assign this const to a local or static variable, and use the variable here error: a `const` item with interior mutability should not be borrowed - --> $DIR/others.rs:55:16 + --> $DIR/others.rs:59:16 | LL | assert_eq!(ATOMIC.load(Ordering::SeqCst), 5); //~ ERROR interior mutability | ^^^^^^ @@ -16,7 +16,7 @@ LL | assert_eq!(ATOMIC.load(Ordering::SeqCst), 5); //~ ERROR interior mutabi = help: assign this const to a local or static variable, and use the variable here error: a `const` item with interior mutability should not be borrowed - --> $DIR/others.rs:58:22 + --> $DIR/others.rs:62:22 | LL | let _once_ref = &ONCE_INIT; //~ ERROR interior mutability | ^^^^^^^^^ @@ -24,7 +24,7 @@ LL | let _once_ref = &ONCE_INIT; //~ ERROR interior mutability = help: assign this const to a local or static variable, and use the variable here error: a `const` item with interior mutability should not be borrowed - --> $DIR/others.rs:59:25 + --> $DIR/others.rs:63:25 | LL | let _once_ref_2 = &&ONCE_INIT; //~ ERROR interior mutability | ^^^^^^^^^ @@ -32,7 +32,7 @@ LL | let _once_ref_2 = &&ONCE_INIT; //~ ERROR interior mutability = help: assign this const to a local or static variable, and use the variable here error: a `const` item with interior mutability should not be borrowed - --> $DIR/others.rs:60:27 + --> $DIR/others.rs:64:27 | LL | let _once_ref_4 = &&&&ONCE_INIT; //~ ERROR interior mutability | ^^^^^^^^^ @@ -40,7 +40,7 @@ LL | let _once_ref_4 = &&&&ONCE_INIT; //~ ERROR interior mutability = help: assign this const to a local or static variable, and use the variable here error: a `const` item with interior mutability should not be borrowed - --> $DIR/others.rs:61:26 + --> $DIR/others.rs:65:26 | LL | let _once_mut = &mut ONCE_INIT; //~ ERROR interior mutability | ^^^^^^^^^ @@ -48,7 +48,7 @@ LL | let _once_mut = &mut ONCE_INIT; //~ ERROR interior mutability = help: assign this const to a local or static variable, and use the variable here error: a `const` item with interior mutability should not be borrowed - --> $DIR/others.rs:72:14 + --> $DIR/others.rs:76:14 | LL | let _ = &ATOMIC_TUPLE; //~ ERROR interior mutability | ^^^^^^^^^^^^ @@ -56,7 +56,7 @@ LL | let _ = &ATOMIC_TUPLE; //~ ERROR interior mutability = help: assign this const to a local or static variable, and use the variable here error: a `const` item with interior mutability should not be borrowed - --> $DIR/others.rs:73:14 + --> $DIR/others.rs:77:14 | LL | let _ = &ATOMIC_TUPLE.0; //~ ERROR interior mutability | ^^^^^^^^^^^^ @@ -64,7 +64,7 @@ LL | let _ = &ATOMIC_TUPLE.0; //~ ERROR interior mutability = help: assign this const to a local or static variable, and use the variable here error: a `const` item with interior mutability should not be borrowed - --> $DIR/others.rs:74:19 + --> $DIR/others.rs:78:19 | LL | let _ = &(&&&&ATOMIC_TUPLE).0; //~ ERROR interior mutability | ^^^^^^^^^^^^ @@ -72,7 +72,7 @@ LL | let _ = &(&&&&ATOMIC_TUPLE).0; //~ ERROR interior mutability = help: assign this const to a local or static variable, and use the variable here error: a `const` item with interior mutability should not be borrowed - --> $DIR/others.rs:75:14 + --> $DIR/others.rs:79:14 | LL | let _ = &ATOMIC_TUPLE.0[0]; //~ ERROR interior mutability | ^^^^^^^^^^^^ @@ -80,7 +80,7 @@ LL | let _ = &ATOMIC_TUPLE.0[0]; //~ ERROR interior mutability = help: assign this const to a local or static variable, and use the variable here error: a `const` item with interior mutability should not be borrowed - --> $DIR/others.rs:76:13 + --> $DIR/others.rs:80:13 | LL | let _ = ATOMIC_TUPLE.0[0].load(Ordering::SeqCst); //~ ERROR interior mutability | ^^^^^^^^^^^^ @@ -88,7 +88,7 @@ LL | let _ = ATOMIC_TUPLE.0[0].load(Ordering::SeqCst); //~ ERROR interior mu = help: assign this const to a local or static variable, and use the variable here error: a `const` item with interior mutability should not be borrowed - --> $DIR/others.rs:82:13 + --> $DIR/others.rs:86:13 | LL | let _ = ATOMIC_TUPLE.0[0]; //~ ERROR interior mutability | ^^^^^^^^^^^^ @@ -96,7 +96,7 @@ LL | let _ = ATOMIC_TUPLE.0[0]; //~ ERROR interior mutability = help: assign this const to a local or static variable, and use the variable here error: a `const` item with interior mutability should not be borrowed - --> $DIR/others.rs:87:5 + --> $DIR/others.rs:91:5 | LL | CELL.set(2); //~ ERROR interior mutability | ^^^^ @@ -104,7 +104,7 @@ LL | CELL.set(2); //~ ERROR interior mutability = help: assign this const to a local or static variable, and use the variable here error: a `const` item with interior mutability should not be borrowed - --> $DIR/others.rs:88:16 + --> $DIR/others.rs:92:16 | LL | assert_eq!(CELL.get(), 6); //~ ERROR interior mutability | ^^^^ diff --git a/src/tools/clippy/tests/ui/box_vec.stderr b/src/tools/clippy/tests/ui/box_vec.stderr index fca12eddd573f..9b789334baeec 100644 --- a/src/tools/clippy/tests/ui/box_vec.stderr +++ b/src/tools/clippy/tests/ui/box_vec.stderr @@ -5,7 +5,7 @@ LL | pub fn test(foo: Box<Vec<bool>>) { | ^^^^^^^^^^^^^^ | = note: `-D clippy::box-vec` implied by `-D warnings` - = help: `Vec<T>` is already on the heap, `Box<Vec<T>>` makes an extra allocation. + = help: `Vec<T>` is already on the heap, `Box<Vec<T>>` makes an extra allocation error: aborting due to previous error diff --git a/src/tools/clippy/tests/ui/branches_sharing_code/shared_at_bottom.rs b/src/tools/clippy/tests/ui/branches_sharing_code/shared_at_bottom.rs new file mode 100644 index 0000000000000..c389c243d447e --- /dev/null +++ b/src/tools/clippy/tests/ui/branches_sharing_code/shared_at_bottom.rs @@ -0,0 +1,209 @@ +#![allow(dead_code)] +#![deny(clippy::if_same_then_else, clippy::branches_sharing_code)] + +// This tests the branches_sharing_code lint at the end of blocks + +fn simple_examples() { + let x = 1; + + let _ = if x == 7 { + println!("Branch I"); + let start_value = 0; + println!("=^.^="); + + // Same but not moveable due to `start_value` + let _ = start_value; + + // The rest is self contained and moveable => Only lint the rest + let result = false; + println!("Block end!"); + result + } else { + println!("Branch II"); + let start_value = 8; + println!("xD"); + + // Same but not moveable due to `start_value` + let _ = start_value; + + // The rest is self contained and moveable => Only lint the rest + let result = false; + println!("Block end!"); + result + }; + + // Else if block + if x == 9 { + println!("The index is: 6"); + + println!("Same end of block"); + } else if x == 8 { + println!("The index is: 4"); + + // We should only get a lint trigger for the last statement + println!("This is also eq with the else block"); + println!("Same end of block"); + } else { + println!("This is also eq with the else block"); + println!("Same end of block"); + } + + // Use of outer scope value + let outer_scope_value = "I'm outside the if block"; + if x < 99 { + let z = "How are you"; + println!("I'm a local because I use the value `z`: `{}`", z); + + println!( + "I'm moveable because I know: `outer_scope_value`: '{}'", + outer_scope_value + ); + } else { + let z = 45678000; + println!("I'm a local because I use the value `z`: `{}`", z); + + println!( + "I'm moveable because I know: `outer_scope_value`: '{}'", + outer_scope_value + ); + } + + if x == 9 { + if x == 8 { + // No parent!! + println!("---"); + println!("Hello World"); + } else { + println!("Hello World"); + } + } +} + +/// Simple examples where the move can cause some problems due to moved values +fn simple_but_suggestion_is_invalid() { + let x = 16; + + // Local value + let later_used_value = 17; + if x == 9 { + let _ = 9; + let later_used_value = "A string value"; + println!("{}", later_used_value); + } else { + let later_used_value = "A string value"; + println!("{}", later_used_value); + // I'm expecting a note about this + } + println!("{}", later_used_value); + + // outer function + if x == 78 { + let simple_examples = "I now identify as a &str :)"; + println!("This is the new simple_example: {}", simple_examples); + } else { + println!("Separator print statement"); + + let simple_examples = "I now identify as a &str :)"; + println!("This is the new simple_example: {}", simple_examples); + } + simple_examples(); +} + +/// Tests where the blocks are not linted due to the used value scope +fn not_moveable_due_to_value_scope() { + let x = 18; + + // Using a local value in the moved code + if x == 9 { + let y = 18; + println!("y is: `{}`", y); + } else { + let y = "A string"; + println!("y is: `{}`", y); + } + + // Using a local value in the expression + let _ = if x == 0 { + let mut result = x + 1; + + println!("1. Doing some calculations"); + println!("2. Some more calculations"); + println!("3. Setting result"); + + result + } else { + let mut result = x - 1; + + println!("1. Doing some calculations"); + println!("2. Some more calculations"); + println!("3. Setting result"); + + result + }; + + let _ = if x == 7 { + let z1 = 100; + println!("z1: {}", z1); + + let z2 = z1; + println!("z2: {}", z2); + + z2 + } else { + let z1 = 300; + println!("z1: {}", z1); + + let z2 = z1; + println!("z2: {}", z2); + + z2 + }; +} + +/// This should add a note to the lint msg since the moved expression is not `()` +fn added_note_for_expression_use() -> u32 { + let x = 9; + + let _ = if x == 7 { + x << 2 + } else { + let _ = 6; + x << 2 + }; + + if x == 9 { + x * 4 + } else { + let _ = 17; + x * 4 + } +} + +#[rustfmt::skip] +fn test_suggestion_with_weird_formatting() { + let x = 9; + let mut a = 0; + let mut b = 0; + + // The error message still looks weird tbh but this is the best I can do + // for weird formatting + if x == 17 { b = 1; a = 0x99; } else { a = 0x99; } +} + +fn fp_test() { + let x = 17; + + if x == 18 { + let y = 19; + if y < x { + println!("Trigger") + } + } else { + let z = 166; + if z < x { + println!("Trigger") + } + } +} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/branches_sharing_code/shared_at_bottom.stderr b/src/tools/clippy/tests/ui/branches_sharing_code/shared_at_bottom.stderr new file mode 100644 index 0000000000000..271fcd8b6c129 --- /dev/null +++ b/src/tools/clippy/tests/ui/branches_sharing_code/shared_at_bottom.stderr @@ -0,0 +1,143 @@ +error: all if blocks contain the same code at the end + --> $DIR/shared_at_bottom.rs:30:5 + | +LL | / let result = false; +LL | | println!("Block end!"); +LL | | result +LL | | }; + | |_____^ + | +note: the lint level is defined here + --> $DIR/shared_at_bottom.rs:2:36 + | +LL | #![deny(clippy::if_same_then_else, clippy::branches_sharing_code)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + = note: The end suggestion probably needs some adjustments to use the expression result correctly +help: consider moving the end statements out like this + | +LL | } +LL | let result = false; +LL | println!("Block end!"); +LL | result; + | + +error: all if blocks contain the same code at the end + --> $DIR/shared_at_bottom.rs:48:5 + | +LL | / println!("Same end of block"); +LL | | } + | |_____^ + | +help: consider moving the end statements out like this + | +LL | } +LL | println!("Same end of block"); + | + +error: all if blocks contain the same code at the end + --> $DIR/shared_at_bottom.rs:65:5 + | +LL | / println!( +LL | | "I'm moveable because I know: `outer_scope_value`: '{}'", +LL | | outer_scope_value +LL | | ); +LL | | } + | |_____^ + | +help: consider moving the end statements out like this + | +LL | } +LL | println!( +LL | "I'm moveable because I know: `outer_scope_value`: '{}'", +LL | outer_scope_value +LL | ); + | + +error: all if blocks contain the same code at the end + --> $DIR/shared_at_bottom.rs:77:9 + | +LL | / println!("Hello World"); +LL | | } + | |_________^ + | +help: consider moving the end statements out like this + | +LL | } +LL | println!("Hello World"); + | + +error: all if blocks contain the same code at the end + --> $DIR/shared_at_bottom.rs:93:5 + | +LL | / let later_used_value = "A string value"; +LL | | println!("{}", later_used_value); +LL | | // I'm expecting a note about this +LL | | } + | |_____^ + | + = warning: Some moved values might need to be renamed to avoid wrong references +help: consider moving the end statements out like this + | +LL | } +LL | let later_used_value = "A string value"; +LL | println!("{}", later_used_value); + | + +error: all if blocks contain the same code at the end + --> $DIR/shared_at_bottom.rs:106:5 + | +LL | / let simple_examples = "I now identify as a &str :)"; +LL | | println!("This is the new simple_example: {}", simple_examples); +LL | | } + | |_____^ + | + = warning: Some moved values might need to be renamed to avoid wrong references +help: consider moving the end statements out like this + | +LL | } +LL | let simple_examples = "I now identify as a &str :)"; +LL | println!("This is the new simple_example: {}", simple_examples); + | + +error: all if blocks contain the same code at the end + --> $DIR/shared_at_bottom.rs:171:5 + | +LL | / x << 2 +LL | | }; + | |_____^ + | + = note: The end suggestion probably needs some adjustments to use the expression result correctly +help: consider moving the end statements out like this + | +LL | } +LL | x << 2; + | + +error: all if blocks contain the same code at the end + --> $DIR/shared_at_bottom.rs:178:5 + | +LL | / x * 4 +LL | | } + | |_____^ + | + = note: The end suggestion probably needs some adjustments to use the expression result correctly +help: consider moving the end statements out like this + | +LL | } +LL | x * 4 + | + +error: all if blocks contain the same code at the end + --> $DIR/shared_at_bottom.rs:190:44 + | +LL | if x == 17 { b = 1; a = 0x99; } else { a = 0x99; } + | ^^^^^^^^^^^ + | +help: consider moving the end statements out like this + | +LL | if x == 17 { b = 1; a = 0x99; } else { } +LL | a = 0x99; + | + +error: aborting due to 9 previous errors + diff --git a/src/tools/clippy/tests/ui/branches_sharing_code/shared_at_top.rs b/src/tools/clippy/tests/ui/branches_sharing_code/shared_at_top.rs new file mode 100644 index 0000000000000..e65bcfd78737f --- /dev/null +++ b/src/tools/clippy/tests/ui/branches_sharing_code/shared_at_top.rs @@ -0,0 +1,103 @@ +#![allow(dead_code, clippy::eval_order_dependence)] +#![deny(clippy::if_same_then_else, clippy::branches_sharing_code)] + +// This tests the branches_sharing_code lint at the start of blocks + +fn simple_examples() { + let x = 0; + + // Simple + if true { + println!("Hello World!"); + println!("I'm branch nr: 1"); + } else { + println!("Hello World!"); + println!("I'm branch nr: 2"); + } + + // Else if + if x == 0 { + let y = 9; + println!("The value y was set to: `{}`", y); + let _z = y; + + println!("I'm the true start index of arrays"); + } else if x == 1 { + let y = 9; + println!("The value y was set to: `{}`", y); + let _z = y; + + println!("I start counting from 1 so my array starts from `1`"); + } else { + let y = 9; + println!("The value y was set to: `{}`", y); + let _z = y; + + println!("Ha, Pascal allows you to start the array where you want") + } + + // Return a value + let _ = if x == 7 { + let y = 16; + println!("What can I say except: \"you're welcome?\""); + let _ = y; + x + } else { + let y = 16; + println!("Thank you"); + y + }; +} + +/// Simple examples where the move can cause some problems due to moved values +fn simple_but_suggestion_is_invalid() { + let x = 10; + + // Can't be automatically moved because used_value_name is getting used again + let used_value_name = 19; + if x == 10 { + let used_value_name = "Different type"; + println!("Str: {}", used_value_name); + let _ = 1; + } else { + let used_value_name = "Different type"; + println!("Str: {}", used_value_name); + let _ = 2; + } + let _ = used_value_name; + + // This can be automatically moved as `can_be_overridden` is not used again + let can_be_overridden = 8; + let _ = can_be_overridden; + if x == 11 { + let can_be_overridden = "Move me"; + println!("I'm also moveable"); + let _ = 111; + } else { + let can_be_overridden = "Move me"; + println!("I'm also moveable"); + let _ = 222; + } +} + +/// This function tests that the `IS_SAME_THAN_ELSE` only covers the lint if it's enabled. +fn check_if_same_than_else_mask() { + let x = 2021; + + #[allow(clippy::if_same_then_else)] + if x == 2020 { + println!("This should trigger the `SHARED_CODE_IN_IF_BLOCKS` lint."); + println!("Because `IF_SAME_THEN_ELSE` is allowed here"); + } else { + println!("This should trigger the `SHARED_CODE_IN_IF_BLOCKS` lint."); + println!("Because `IF_SAME_THEN_ELSE` is allowed here"); + } + + if x == 2019 { + println!("This should trigger `IS_SAME_THAN_ELSE` as usual"); + } else { + println!("This should trigger `IS_SAME_THAN_ELSE` as usual"); + } +} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/branches_sharing_code/shared_at_top.stderr b/src/tools/clippy/tests/ui/branches_sharing_code/shared_at_top.stderr new file mode 100644 index 0000000000000..15867e9ea020a --- /dev/null +++ b/src/tools/clippy/tests/ui/branches_sharing_code/shared_at_top.stderr @@ -0,0 +1,121 @@ +error: all if blocks contain the same code at the start + --> $DIR/shared_at_top.rs:10:5 + | +LL | / if true { +LL | | println!("Hello World!"); + | |_________________________________^ + | +note: the lint level is defined here + --> $DIR/shared_at_top.rs:2:36 + | +LL | #![deny(clippy::if_same_then_else, clippy::branches_sharing_code)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +help: consider moving the start statements out like this + | +LL | println!("Hello World!"); +LL | if true { + | + +error: all if blocks contain the same code at the start + --> $DIR/shared_at_top.rs:19:5 + | +LL | / if x == 0 { +LL | | let y = 9; +LL | | println!("The value y was set to: `{}`", y); +LL | | let _z = y; + | |___________________^ + | + = warning: Some moved values might need to be renamed to avoid wrong references +help: consider moving the start statements out like this + | +LL | let y = 9; +LL | println!("The value y was set to: `{}`", y); +LL | let _z = y; +LL | if x == 0 { + | + +error: all if blocks contain the same code at the start + --> $DIR/shared_at_top.rs:40:5 + | +LL | / let _ = if x == 7 { +LL | | let y = 16; + | |___________________^ + | +help: consider moving the start statements out like this + | +LL | let y = 16; +LL | let _ = if x == 7 { + | + +error: all if blocks contain the same code at the start + --> $DIR/shared_at_top.rs:58:5 + | +LL | / if x == 10 { +LL | | let used_value_name = "Different type"; +LL | | println!("Str: {}", used_value_name); + | |_____________________________________________^ + | + = warning: Some moved values might need to be renamed to avoid wrong references +help: consider moving the start statements out like this + | +LL | let used_value_name = "Different type"; +LL | println!("Str: {}", used_value_name); +LL | if x == 10 { + | + +error: all if blocks contain the same code at the start + --> $DIR/shared_at_top.rs:72:5 + | +LL | / if x == 11 { +LL | | let can_be_overridden = "Move me"; +LL | | println!("I'm also moveable"); + | |______________________________________^ + | + = warning: Some moved values might need to be renamed to avoid wrong references +help: consider moving the start statements out like this + | +LL | let can_be_overridden = "Move me"; +LL | println!("I'm also moveable"); +LL | if x == 11 { + | + +error: all if blocks contain the same code at the start + --> $DIR/shared_at_top.rs:88:5 + | +LL | / if x == 2020 { +LL | | println!("This should trigger the `SHARED_CODE_IN_IF_BLOCKS` lint."); +LL | | println!("Because `IF_SAME_THEN_ELSE` is allowed here"); + | |________________________________________________________________^ + | +help: consider moving the start statements out like this + | +LL | println!("This should trigger the `SHARED_CODE_IN_IF_BLOCKS` lint."); +LL | println!("Because `IF_SAME_THEN_ELSE` is allowed here"); +LL | if x == 2020 { + | + +error: this `if` has identical blocks + --> $DIR/shared_at_top.rs:96:18 + | +LL | if x == 2019 { + | __________________^ +LL | | println!("This should trigger `IS_SAME_THAN_ELSE` as usual"); +LL | | } else { + | |_____^ + | +note: the lint level is defined here + --> $DIR/shared_at_top.rs:2:9 + | +LL | #![deny(clippy::if_same_then_else, clippy::branches_sharing_code)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ +note: same as this + --> $DIR/shared_at_top.rs:98:12 + | +LL | } else { + | ____________^ +LL | | println!("This should trigger `IS_SAME_THAN_ELSE` as usual"); +LL | | } + | |_____^ + +error: aborting due to 7 previous errors + diff --git a/src/tools/clippy/tests/ui/branches_sharing_code/shared_at_top_and_bottom.rs b/src/tools/clippy/tests/ui/branches_sharing_code/shared_at_top_and_bottom.rs new file mode 100644 index 0000000000000..deefdad32c9dd --- /dev/null +++ b/src/tools/clippy/tests/ui/branches_sharing_code/shared_at_top_and_bottom.rs @@ -0,0 +1,119 @@ +#![allow(dead_code)] +#![deny(clippy::if_same_then_else, clippy::branches_sharing_code)] + +// branches_sharing_code at the top and bottom of the if blocks + +struct DataPack { + id: u32, + name: String, + some_data: Vec<u8>, +} + +fn overlapping_eq_regions() { + let x = 9; + + // Overlap with separator + if x == 7 { + let t = 7; + let _overlap_start = t * 2; + let _overlap_end = 2 * t; + let _u = 9; + } else { + let t = 7; + let _overlap_start = t * 2; + let _overlap_end = 2 * t; + println!("Overlap separator"); + let _overlap_start = t * 2; + let _overlap_end = 2 * t; + let _u = 9; + } + + // Overlap with separator + if x == 99 { + let r = 7; + let _overlap_start = r; + let _overlap_middle = r * r; + let _overlap_end = r * r * r; + let z = "end"; + } else { + let r = 7; + let _overlap_start = r; + let _overlap_middle = r * r; + let _overlap_middle = r * r; + let _overlap_end = r * r * r; + let z = "end"; + } +} + +fn complexer_example() { + fn gen_id(x: u32, y: u32) -> u32 { + let x = x & 0x0000_ffff; + let y = (y & 0xffff_0000) << 16; + x | y + } + + fn process_data(data: DataPack) { + let _ = data; + } + + let x = 8; + let y = 9; + if (x > 7 && y < 13) || (x + y) % 2 == 1 { + let a = 0xcafe; + let b = 0xffff00ff; + let e_id = gen_id(a, b); + + println!("From the a `{}` to the b `{}`", a, b); + + let pack = DataPack { + id: e_id, + name: "Player 1".to_string(), + some_data: vec![0x12, 0x34, 0x56, 0x78, 0x90], + }; + process_data(pack); + } else { + let a = 0xcafe; + let b = 0xffff00ff; + let e_id = gen_id(a, b); + + println!("The new ID is '{}'", e_id); + + let pack = DataPack { + id: e_id, + name: "Player 1".to_string(), + some_data: vec![0x12, 0x34, 0x56, 0x78, 0x90], + }; + process_data(pack); + } +} + +/// This should add a note to the lint msg since the moved expression is not `()` +fn added_note_for_expression_use() -> u32 { + let x = 9; + + let _ = if x == 7 { + let _ = 19; + + let _splitter = 6; + + x << 2 + } else { + let _ = 19; + + x << 2 + }; + + if x == 9 { + let _ = 17; + + let _splitter = 6; + + x * 4 + } else { + let _ = 17; + + x * 4 + } +} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/branches_sharing_code/shared_at_top_and_bottom.stderr b/src/tools/clippy/tests/ui/branches_sharing_code/shared_at_top_and_bottom.stderr new file mode 100644 index 0000000000000..212cfb2f1d180 --- /dev/null +++ b/src/tools/clippy/tests/ui/branches_sharing_code/shared_at_top_and_bottom.stderr @@ -0,0 +1,154 @@ +error: all if blocks contain the same code at the start and the end. Here at the start + --> $DIR/shared_at_top_and_bottom.rs:16:5 + | +LL | / if x == 7 { +LL | | let t = 7; +LL | | let _overlap_start = t * 2; +LL | | let _overlap_end = 2 * t; + | |_________________________________^ + | +note: the lint level is defined here + --> $DIR/shared_at_top_and_bottom.rs:2:36 + | +LL | #![deny(clippy::if_same_then_else, clippy::branches_sharing_code)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +note: and here at the end + --> $DIR/shared_at_top_and_bottom.rs:28:5 + | +LL | / let _u = 9; +LL | | } + | |_____^ +help: consider moving the start statements out like this + | +LL | let t = 7; +LL | let _overlap_start = t * 2; +LL | let _overlap_end = 2 * t; +LL | if x == 7 { + | +help: and consider moving the end statements out like this + | +LL | } +LL | let _u = 9; + | + +error: all if blocks contain the same code at the start and the end. Here at the start + --> $DIR/shared_at_top_and_bottom.rs:32:5 + | +LL | / if x == 99 { +LL | | let r = 7; +LL | | let _overlap_start = r; +LL | | let _overlap_middle = r * r; + | |____________________________________^ + | +note: and here at the end + --> $DIR/shared_at_top_and_bottom.rs:43:5 + | +LL | / let _overlap_end = r * r * r; +LL | | let z = "end"; +LL | | } + | |_____^ + = warning: Some moved values might need to be renamed to avoid wrong references +help: consider moving the start statements out like this + | +LL | let r = 7; +LL | let _overlap_start = r; +LL | let _overlap_middle = r * r; +LL | if x == 99 { + | +help: and consider moving the end statements out like this + | +LL | } +LL | let _overlap_end = r * r * r; +LL | let z = "end"; + | + +error: all if blocks contain the same code at the start and the end. Here at the start + --> $DIR/shared_at_top_and_bottom.rs:61:5 + | +LL | / if (x > 7 && y < 13) || (x + y) % 2 == 1 { +LL | | let a = 0xcafe; +LL | | let b = 0xffff00ff; +LL | | let e_id = gen_id(a, b); + | |________________________________^ + | +note: and here at the end + --> $DIR/shared_at_top_and_bottom.rs:81:5 + | +LL | / let pack = DataPack { +LL | | id: e_id, +LL | | name: "Player 1".to_string(), +LL | | some_data: vec![0x12, 0x34, 0x56, 0x78, 0x90], +LL | | }; +LL | | process_data(pack); +LL | | } + | |_____^ + = warning: Some moved values might need to be renamed to avoid wrong references +help: consider moving the start statements out like this + | +LL | let a = 0xcafe; +LL | let b = 0xffff00ff; +LL | let e_id = gen_id(a, b); +LL | if (x > 7 && y < 13) || (x + y) % 2 == 1 { + | +help: and consider moving the end statements out like this + | +LL | } +LL | let pack = DataPack { +LL | id: e_id, +LL | name: "Player 1".to_string(), +LL | some_data: vec![0x12, 0x34, 0x56, 0x78, 0x90], +LL | }; + ... + +error: all if blocks contain the same code at the start and the end. Here at the start + --> $DIR/shared_at_top_and_bottom.rs:94:5 + | +LL | / let _ = if x == 7 { +LL | | let _ = 19; + | |___________________^ + | +note: and here at the end + --> $DIR/shared_at_top_and_bottom.rs:103:5 + | +LL | / x << 2 +LL | | }; + | |_____^ + = note: The end suggestion probably needs some adjustments to use the expression result correctly +help: consider moving the start statements out like this + | +LL | let _ = 19; +LL | let _ = if x == 7 { + | +help: and consider moving the end statements out like this + | +LL | } +LL | x << 2; + | + +error: all if blocks contain the same code at the start and the end. Here at the start + --> $DIR/shared_at_top_and_bottom.rs:106:5 + | +LL | / if x == 9 { +LL | | let _ = 17; + | |___________________^ + | +note: and here at the end + --> $DIR/shared_at_top_and_bottom.rs:115:5 + | +LL | / x * 4 +LL | | } + | |_____^ + = note: The end suggestion probably needs some adjustments to use the expression result correctly +help: consider moving the start statements out like this + | +LL | let _ = 17; +LL | if x == 9 { + | +help: and consider moving the end statements out like this + | +LL | } +LL | x * 4 + | + +error: aborting due to 5 previous errors + diff --git a/src/tools/clippy/tests/ui/branches_sharing_code/valid_if_blocks.rs b/src/tools/clippy/tests/ui/branches_sharing_code/valid_if_blocks.rs new file mode 100644 index 0000000000000..0c70e3748ec16 --- /dev/null +++ b/src/tools/clippy/tests/ui/branches_sharing_code/valid_if_blocks.rs @@ -0,0 +1,155 @@ +#![allow(dead_code, clippy::eval_order_dependence)] +#![deny(clippy::if_same_then_else, clippy::branches_sharing_code)] + +// This tests valid if blocks that shouldn't trigger the lint + +// Tests with value references are includes in "shared_code_at_bottom.rs" + +fn valid_examples() { + let x = 2; + + // The edge statements are different + if x == 9 { + let y = 1 << 5; + + println!("This is the same: vvv"); + let _z = y; + println!("The block expression is different"); + + println!("Different end 1"); + } else { + let y = 1 << 7; + + println!("This is the same: vvv"); + let _z = y; + println!("The block expression is different"); + + println!("Different end 2"); + } + + // No else + if x == 2 { + println!("Hello world!"); + println!("Hello back, how are you?"); + + // This is different vvvv + println!("Howdy stranger =^.^="); + + println!("Bye Bye World"); + } else if x == 9 { + println!("Hello world!"); + println!("Hello back, how are you?"); + + // This is different vvvv + println!("Hello reviewer :D"); + + println!("Bye Bye World"); + } + + // Overlapping statements only in else if blocks -> Don't lint + if x == 0 { + println!("I'm important!") + } else if x == 17 { + println!("I share code in else if"); + + println!("x is 17"); + } else { + println!("I share code in else if"); + + println!("x is nether x nor 17"); + } + + // Mutability is different + if x == 13 { + let mut y = 9; + println!("Value y is: {}", y); + y += 16; + let _z1 = y; + } else { + let y = 9; + println!("Value y is: {}", y); + let _z2 = y; + } + + // Same blocks but at start and bottom so no `if_same_then_else` lint + if x == 418 { + let y = 9; + let z = 8; + let _ = (x, y, z); + // Don't tell the programmer, my code is also in the else block + } else if x == 419 { + println!("+-----------+"); + println!("| |"); + println!("| O O |"); + println!("| ° |"); + println!("| \\_____/ |"); + println!("| |"); + println!("+-----------+"); + } else { + let y = 9; + let z = 8; + let _ = (x, y, z); + // I'm so much better than the x == 418 block. Trust me + } + + let x = 1; + if true { + println!("{}", x); + } else { + let x = 2; + println!("{}", x); + } + + // Let's test empty blocks + if false { + } else { + } +} + +/// This makes sure that the `if_same_then_else` masks the `shared_code_in_if_blocks` lint +fn trigger_other_lint() { + let x = 0; + let y = 1; + + // Same block + if x == 0 { + let u = 19; + println!("How are u today?"); + let _ = "This is a string"; + } else { + let u = 19; + println!("How are u today?"); + let _ = "This is a string"; + } + + // Only same expression + let _ = if x == 6 { 7 } else { 7 }; + + // Same in else if block + let _ = if x == 67 { + println!("Well I'm the most important block"); + "I'm a pretty string" + } else if x == 68 { + println!("I'm a doppelgänger"); + // Don't listen to my clone below + + if y == 90 { "=^.^=" } else { ":D" } + } else { + // Don't listen to my clone above + println!("I'm a doppelgänger"); + + if y == 90 { "=^.^=" } else { ":D" } + }; + + if x == 0 { + println!("I'm single"); + } else if x == 68 { + println!("I'm a doppelgänger"); + // Don't listen to my clone below + } else { + // Don't listen to my clone above + println!("I'm a doppelgänger"); + } +} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/branches_sharing_code/valid_if_blocks.stderr b/src/tools/clippy/tests/ui/branches_sharing_code/valid_if_blocks.stderr new file mode 100644 index 0000000000000..a815995e71721 --- /dev/null +++ b/src/tools/clippy/tests/ui/branches_sharing_code/valid_if_blocks.stderr @@ -0,0 +1,101 @@ +error: this `if` has identical blocks + --> $DIR/valid_if_blocks.rs:104:14 + | +LL | if false { + | ______________^ +LL | | } else { + | |_____^ + | +note: the lint level is defined here + --> $DIR/valid_if_blocks.rs:2:9 + | +LL | #![deny(clippy::if_same_then_else, clippy::branches_sharing_code)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ +note: same as this + --> $DIR/valid_if_blocks.rs:105:12 + | +LL | } else { + | ____________^ +LL | | } + | |_____^ + +error: this `if` has identical blocks + --> $DIR/valid_if_blocks.rs:115:15 + | +LL | if x == 0 { + | _______________^ +LL | | let u = 19; +LL | | println!("How are u today?"); +LL | | let _ = "This is a string"; +LL | | } else { + | |_____^ + | +note: same as this + --> $DIR/valid_if_blocks.rs:119:12 + | +LL | } else { + | ____________^ +LL | | let u = 19; +LL | | println!("How are u today?"); +LL | | let _ = "This is a string"; +LL | | } + | |_____^ + +error: this `if` has identical blocks + --> $DIR/valid_if_blocks.rs:126:23 + | +LL | let _ = if x == 6 { 7 } else { 7 }; + | ^^^^^ + | +note: same as this + --> $DIR/valid_if_blocks.rs:126:34 + | +LL | let _ = if x == 6 { 7 } else { 7 }; + | ^^^^^ + +error: this `if` has identical blocks + --> $DIR/valid_if_blocks.rs:132:23 + | +LL | } else if x == 68 { + | _______________________^ +LL | | println!("I'm a doppelgänger"); +LL | | // Don't listen to my clone below +LL | | +LL | | if y == 90 { "=^.^=" } else { ":D" } +LL | | } else { + | |_____^ + | +note: same as this + --> $DIR/valid_if_blocks.rs:137:12 + | +LL | } else { + | ____________^ +LL | | // Don't listen to my clone above +LL | | println!("I'm a doppelgänger"); +LL | | +LL | | if y == 90 { "=^.^=" } else { ":D" } +LL | | }; + | |_____^ + +error: this `if` has identical blocks + --> $DIR/valid_if_blocks.rs:146:23 + | +LL | } else if x == 68 { + | _______________________^ +LL | | println!("I'm a doppelgänger"); +LL | | // Don't listen to my clone below +LL | | } else { + | |_____^ + | +note: same as this + --> $DIR/valid_if_blocks.rs:149:12 + | +LL | } else { + | ____________^ +LL | | // Don't listen to my clone above +LL | | println!("I'm a doppelgänger"); +LL | | } + | |_____^ + +error: aborting due to 5 previous errors + diff --git a/src/tools/clippy/tests/ui/checked_unwrap/complex_conditionals.rs b/src/tools/clippy/tests/ui/checked_unwrap/complex_conditionals.rs index c986c992a07a6..ec082c73b44c0 100644 --- a/src/tools/clippy/tests/ui/checked_unwrap/complex_conditionals.rs +++ b/src/tools/clippy/tests/ui/checked_unwrap/complex_conditionals.rs @@ -1,5 +1,5 @@ #![deny(clippy::panicking_unwrap, clippy::unnecessary_unwrap)] -#![allow(clippy::if_same_then_else)] +#![allow(clippy::if_same_then_else, clippy::branches_sharing_code)] fn test_complex_conditions() { let x: Result<(), ()> = Ok(()); diff --git a/src/tools/clippy/tests/ui/checked_unwrap/complex_conditionals_nested.rs b/src/tools/clippy/tests/ui/checked_unwrap/complex_conditionals_nested.rs index 2307996a48ffc..043ea4148dc51 100644 --- a/src/tools/clippy/tests/ui/checked_unwrap/complex_conditionals_nested.rs +++ b/src/tools/clippy/tests/ui/checked_unwrap/complex_conditionals_nested.rs @@ -1,5 +1,5 @@ #![deny(clippy::panicking_unwrap, clippy::unnecessary_unwrap)] -#![allow(clippy::if_same_then_else)] +#![allow(clippy::if_same_then_else, clippy::branches_sharing_code)] fn test_nested() { fn nested() { diff --git a/src/tools/clippy/tests/ui/checked_unwrap/simple_conditionals.rs b/src/tools/clippy/tests/ui/checked_unwrap/simple_conditionals.rs index 3e7b4b390bad4..8f23fb28827a2 100644 --- a/src/tools/clippy/tests/ui/checked_unwrap/simple_conditionals.rs +++ b/src/tools/clippy/tests/ui/checked_unwrap/simple_conditionals.rs @@ -1,5 +1,5 @@ #![deny(clippy::panicking_unwrap, clippy::unnecessary_unwrap)] -#![allow(clippy::if_same_then_else)] +#![allow(clippy::if_same_then_else, clippy::branches_sharing_code)] macro_rules! m { ($a:expr) => { @@ -66,14 +66,16 @@ fn main() { } if x.is_ok() { x = Err(()); - x.unwrap(); // not unnecessary because of mutation of x - // it will always panic but the lint is not smart enough to see this (it only - // checks if conditions). + // not unnecessary because of mutation of x + // it will always panic but the lint is not smart enough to see this (it only + // checks if conditions). + x.unwrap(); } else { x = Ok(()); - x.unwrap_err(); // not unnecessary because of mutation of x - // it will always panic but the lint is not smart enough to see this (it - // only checks if conditions). + // not unnecessary because of mutation of x + // it will always panic but the lint is not smart enough to see this (it + // only checks if conditions). + x.unwrap_err(); } assert!(x.is_ok(), "{:?}", x.unwrap_err()); // ok, it's a common test pattern diff --git a/src/tools/clippy/tests/ui/clone_on_copy.fixed b/src/tools/clippy/tests/ui/clone_on_copy.fixed index d924625132eb0..8d43f64768d41 100644 --- a/src/tools/clippy/tests/ui/clone_on_copy.fixed +++ b/src/tools/clippy/tests/ui/clone_on_copy.fixed @@ -6,7 +6,8 @@ clippy::deref_addrof, clippy::no_effect, clippy::unnecessary_operation, - clippy::vec_init_then_push + clippy::vec_init_then_push, + clippy::toplevel_ref_arg )] use std::cell::RefCell; @@ -29,6 +30,37 @@ fn clone_on_copy() { let rc = RefCell::new(0); *rc.borrow(); + let x = 0u32; + x.rotate_left(1); + + #[derive(Clone, Copy)] + struct Foo; + impl Foo { + fn clone(&self) -> u32 { + 0 + } + } + Foo.clone(); // ok, this is not the clone trait + + macro_rules! m { + ($e:expr) => {{ $e }}; + } + m!(42); + + struct Wrap([u32; 2]); + impl core::ops::Deref for Wrap { + type Target = [u32; 2]; + fn deref(&self) -> &[u32; 2] { + &self.0 + } + } + let x = Wrap([0, 0]); + (*x)[0]; + + let x = 42; + let ref y = x.clone(); // ok, binds by reference + let ref mut y = x.clone(); // ok, binds by reference + // Issue #4348 let mut x = 43; let _ = &x.clone(); // ok, getting a ref diff --git a/src/tools/clippy/tests/ui/clone_on_copy.rs b/src/tools/clippy/tests/ui/clone_on_copy.rs index 97f4946724458..f15501f71844f 100644 --- a/src/tools/clippy/tests/ui/clone_on_copy.rs +++ b/src/tools/clippy/tests/ui/clone_on_copy.rs @@ -6,7 +6,8 @@ clippy::deref_addrof, clippy::no_effect, clippy::unnecessary_operation, - clippy::vec_init_then_push + clippy::vec_init_then_push, + clippy::toplevel_ref_arg )] use std::cell::RefCell; @@ -29,6 +30,37 @@ fn clone_on_copy() { let rc = RefCell::new(0); rc.borrow().clone(); + let x = 0u32; + x.clone().rotate_left(1); + + #[derive(Clone, Copy)] + struct Foo; + impl Foo { + fn clone(&self) -> u32 { + 0 + } + } + Foo.clone(); // ok, this is not the clone trait + + macro_rules! m { + ($e:expr) => {{ $e }}; + } + m!(42).clone(); + + struct Wrap([u32; 2]); + impl core::ops::Deref for Wrap { + type Target = [u32; 2]; + fn deref(&self) -> &[u32; 2] { + &self.0 + } + } + let x = Wrap([0, 0]); + x.clone()[0]; + + let x = 42; + let ref y = x.clone(); // ok, binds by reference + let ref mut y = x.clone(); // ok, binds by reference + // Issue #4348 let mut x = 43; let _ = &x.clone(); // ok, getting a ref diff --git a/src/tools/clippy/tests/ui/clone_on_copy.stderr b/src/tools/clippy/tests/ui/clone_on_copy.stderr index 7a706884fb0e7..e7d28b4320bc8 100644 --- a/src/tools/clippy/tests/ui/clone_on_copy.stderr +++ b/src/tools/clippy/tests/ui/clone_on_copy.stderr @@ -1,5 +1,5 @@ error: using `clone` on type `i32` which implements the `Copy` trait - --> $DIR/clone_on_copy.rs:23:5 + --> $DIR/clone_on_copy.rs:24:5 | LL | 42.clone(); | ^^^^^^^^^^ help: try removing the `clone` call: `42` @@ -7,28 +7,46 @@ LL | 42.clone(); = note: `-D clippy::clone-on-copy` implied by `-D warnings` error: using `clone` on type `i32` which implements the `Copy` trait - --> $DIR/clone_on_copy.rs:27:5 + --> $DIR/clone_on_copy.rs:28:5 | LL | (&42).clone(); | ^^^^^^^^^^^^^ help: try dereferencing it: `*(&42)` error: using `clone` on type `i32` which implements the `Copy` trait - --> $DIR/clone_on_copy.rs:30:5 + --> $DIR/clone_on_copy.rs:31:5 | LL | rc.borrow().clone(); | ^^^^^^^^^^^^^^^^^^^ help: try dereferencing it: `*rc.borrow()` +error: using `clone` on type `u32` which implements the `Copy` trait + --> $DIR/clone_on_copy.rs:34:5 + | +LL | x.clone().rotate_left(1); + | ^^^^^^^^^ help: try removing the `clone` call: `x` + +error: using `clone` on type `i32` which implements the `Copy` trait + --> $DIR/clone_on_copy.rs:48:5 + | +LL | m!(42).clone(); + | ^^^^^^^^^^^^^^ help: try removing the `clone` call: `m!(42)` + +error: using `clone` on type `[u32; 2]` which implements the `Copy` trait + --> $DIR/clone_on_copy.rs:58:5 + | +LL | x.clone()[0]; + | ^^^^^^^^^ help: try dereferencing it: `(*x)` + error: using `clone` on type `char` which implements the `Copy` trait - --> $DIR/clone_on_copy.rs:36:14 + --> $DIR/clone_on_copy.rs:68:14 | LL | is_ascii('z'.clone()); | ^^^^^^^^^^^ help: try removing the `clone` call: `'z'` error: using `clone` on type `i32` which implements the `Copy` trait - --> $DIR/clone_on_copy.rs:40:14 + --> $DIR/clone_on_copy.rs:72:14 | LL | vec.push(42.clone()); | ^^^^^^^^^^ help: try removing the `clone` call: `42` -error: aborting due to 5 previous errors +error: aborting due to 8 previous errors diff --git a/src/tools/clippy/tests/ui/clone_on_copy_mut.rs b/src/tools/clippy/tests/ui/clone_on_copy_mut.rs deleted file mode 100644 index 5bfa256623b6b..0000000000000 --- a/src/tools/clippy/tests/ui/clone_on_copy_mut.rs +++ /dev/null @@ -1,18 +0,0 @@ -pub fn dec_read_dec(i: &mut i32) -> i32 { - *i -= 1; - let ret = *i; - *i -= 1; - ret -} - -pub fn minus_1(i: &i32) -> i32 { - dec_read_dec(&mut i.clone()) -} - -fn main() { - let mut i = 10; - assert_eq!(minus_1(&i), 9); - assert_eq!(i, 10); - assert_eq!(dec_read_dec(&mut i), 9); - assert_eq!(i, 8); -} diff --git a/src/tools/clippy/tests/ui/cmp_owned/without_suggestion.rs b/src/tools/clippy/tests/ui/cmp_owned/without_suggestion.rs index 9ab8795474c67..f44a3901fb487 100644 --- a/src/tools/clippy/tests/ui/cmp_owned/without_suggestion.rs +++ b/src/tools/clippy/tests/ui/cmp_owned/without_suggestion.rs @@ -1,4 +1,5 @@ #[allow(clippy::unnecessary_operation)] +#[allow(clippy::implicit_clone)] fn main() { let x = &Baz; diff --git a/src/tools/clippy/tests/ui/cmp_owned/without_suggestion.stderr b/src/tools/clippy/tests/ui/cmp_owned/without_suggestion.stderr index 6e8a5ad2a17b5..2ea3d8fac0d1a 100644 --- a/src/tools/clippy/tests/ui/cmp_owned/without_suggestion.stderr +++ b/src/tools/clippy/tests/ui/cmp_owned/without_suggestion.stderr @@ -1,5 +1,5 @@ error: this creates an owned instance just for comparison - --> $DIR/without_suggestion.rs:6:5 + --> $DIR/without_suggestion.rs:7:5 | LL | y.to_owned() == *x; | ^^^^^^^^^^^^^^^^^^ try implementing the comparison without allocating @@ -7,13 +7,13 @@ LL | y.to_owned() == *x; = note: `-D clippy::cmp-owned` implied by `-D warnings` error: this creates an owned instance just for comparison - --> $DIR/without_suggestion.rs:10:5 + --> $DIR/without_suggestion.rs:11:5 | LL | y.to_owned() == **x; | ^^^^^^^^^^^^^^^^^^^ try implementing the comparison without allocating error: this creates an owned instance just for comparison - --> $DIR/without_suggestion.rs:17:9 + --> $DIR/without_suggestion.rs:18:9 | LL | self.to_owned() == *other | ^^^^^^^^^^^^^^^^^^^^^^^^^ try implementing the comparison without allocating diff --git a/src/tools/clippy/tests/ui/collapsible_match2.rs b/src/tools/clippy/tests/ui/collapsible_match2.rs index 8372a21247734..542e69484276d 100644 --- a/src/tools/clippy/tests/ui/collapsible_match2.rs +++ b/src/tools/clippy/tests/ui/collapsible_match2.rs @@ -1,5 +1,10 @@ #![warn(clippy::collapsible_match)] -#![allow(clippy::needless_return, clippy::no_effect, clippy::single_match)] +#![allow( + clippy::needless_return, + clippy::no_effect, + clippy::single_match, + clippy::needless_borrow +)] fn lint_cases(opt_opt: Option<Option<u32>>, res_opt: Result<Option<u32>, String>) { // if guards on outer match diff --git a/src/tools/clippy/tests/ui/collapsible_match2.stderr b/src/tools/clippy/tests/ui/collapsible_match2.stderr index c8a445ef369d9..ffef32d1fde9a 100644 --- a/src/tools/clippy/tests/ui/collapsible_match2.stderr +++ b/src/tools/clippy/tests/ui/collapsible_match2.stderr @@ -1,5 +1,5 @@ error: unnecessary nested match - --> $DIR/collapsible_match2.rs:8:34 + --> $DIR/collapsible_match2.rs:13:34 | LL | Ok(val) if make() => match val { | __________________________________^ @@ -10,7 +10,7 @@ LL | | }, | = note: `-D clippy::collapsible-match` implied by `-D warnings` help: the outer pattern can be modified to include the inner pattern - --> $DIR/collapsible_match2.rs:8:16 + --> $DIR/collapsible_match2.rs:13:16 | LL | Ok(val) if make() => match val { | ^^^ replace this binding @@ -18,7 +18,7 @@ LL | Some(n) => foo(n), | ^^^^^^^ with this pattern error: unnecessary nested match - --> $DIR/collapsible_match2.rs:15:24 + --> $DIR/collapsible_match2.rs:20:24 | LL | Ok(val) => match val { | ________________________^ @@ -28,7 +28,7 @@ LL | | }, | |_____________^ | help: the outer pattern can be modified to include the inner pattern - --> $DIR/collapsible_match2.rs:15:16 + --> $DIR/collapsible_match2.rs:20:16 | LL | Ok(val) => match val { | ^^^ replace this binding @@ -36,7 +36,7 @@ LL | Some(n) => foo(n), | ^^^^^^^ with this pattern error: unnecessary nested match - --> $DIR/collapsible_match2.rs:29:29 + --> $DIR/collapsible_match2.rs:34:29 | LL | $pat => match $e { | _____________________________^ @@ -49,7 +49,7 @@ LL | mac!(res_opt => Ok(val), val => Some(n), foo(n)); | ------------------------------------------------- in this macro invocation | help: the outer pattern can be modified to include the inner pattern - --> $DIR/collapsible_match2.rs:41:28 + --> $DIR/collapsible_match2.rs:46:28 | LL | mac!(res_opt => Ok(val), val => Some(n), foo(n)); | ^^^ ^^^^^^^ with this pattern @@ -58,7 +58,7 @@ LL | mac!(res_opt => Ok(val), val => Some(n), foo(n)); = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) error: unnecessary nested match - --> $DIR/collapsible_match2.rs:46:20 + --> $DIR/collapsible_match2.rs:51:20 | LL | Some(s) => match *s { | ____________________^ @@ -68,7 +68,7 @@ LL | | }, | |_________^ | help: the outer pattern can be modified to include the inner pattern - --> $DIR/collapsible_match2.rs:46:14 + --> $DIR/collapsible_match2.rs:51:14 | LL | Some(s) => match *s { | ^ replace this binding @@ -76,7 +76,7 @@ LL | [n] => foo(n), | ^^^ with this pattern error: unnecessary nested match - --> $DIR/collapsible_match2.rs:55:24 + --> $DIR/collapsible_match2.rs:60:24 | LL | Some(ref s) => match &*s { | ________________________^ @@ -86,7 +86,7 @@ LL | | }, | |_________^ | help: the outer pattern can be modified to include the inner pattern - --> $DIR/collapsible_match2.rs:55:14 + --> $DIR/collapsible_match2.rs:60:14 | LL | Some(ref s) => match &*s { | ^^^^^ replace this binding diff --git a/src/tools/clippy/tests/ui/comparison_chain.stderr b/src/tools/clippy/tests/ui/comparison_chain.stderr index 69db88b03b5b5..be25a80dde0a1 100644 --- a/src/tools/clippy/tests/ui/comparison_chain.stderr +++ b/src/tools/clippy/tests/ui/comparison_chain.stderr @@ -9,7 +9,7 @@ LL | | } | |_____^ | = note: `-D clippy::comparison-chain` implied by `-D warnings` - = help: Consider rewriting the `if` chain to use `cmp` and `match`. + = help: consider rewriting the `if` chain to use `cmp` and `match` error: `if` chain can be rewritten with `match` --> $DIR/comparison_chain.rs:27:5 @@ -23,7 +23,7 @@ LL | | c() LL | | } | |_____^ | - = help: Consider rewriting the `if` chain to use `cmp` and `match`. + = help: consider rewriting the `if` chain to use `cmp` and `match` error: `if` chain can be rewritten with `match` --> $DIR/comparison_chain.rs:35:5 @@ -37,7 +37,7 @@ LL | | c() LL | | } | |_____^ | - = help: Consider rewriting the `if` chain to use `cmp` and `match`. + = help: consider rewriting the `if` chain to use `cmp` and `match` error: `if` chain can be rewritten with `match` --> $DIR/comparison_chain.rs:43:5 @@ -51,7 +51,7 @@ LL | | c() LL | | } | |_____^ | - = help: Consider rewriting the `if` chain to use `cmp` and `match`. + = help: consider rewriting the `if` chain to use `cmp` and `match` error: `if` chain can be rewritten with `match` --> $DIR/comparison_chain.rs:117:5 @@ -63,7 +63,7 @@ LL | | b() LL | | } | |_____^ | - = help: Consider rewriting the `if` chain to use `cmp` and `match`. + = help: consider rewriting the `if` chain to use `cmp` and `match` error: `if` chain can be rewritten with `match` --> $DIR/comparison_chain.rs:123:5 @@ -77,7 +77,7 @@ LL | | c() LL | | } | |_____^ | - = help: Consider rewriting the `if` chain to use `cmp` and `match`. + = help: consider rewriting the `if` chain to use `cmp` and `match` error: `if` chain can be rewritten with `match` --> $DIR/comparison_chain.rs:131:5 @@ -91,7 +91,7 @@ LL | | c() LL | | } | |_____^ | - = help: Consider rewriting the `if` chain to use `cmp` and `match`. + = help: consider rewriting the `if` chain to use `cmp` and `match` error: aborting due to 7 previous errors diff --git a/src/tools/clippy/tests/ui/complex_types.stderr b/src/tools/clippy/tests/ui/complex_types.stderr deleted file mode 100644 index 7fcbb4bce8836..0000000000000 --- a/src/tools/clippy/tests/ui/complex_types.stderr +++ /dev/null @@ -1,94 +0,0 @@ -error: very complex type used. Consider factoring parts into `type` definitions - --> $DIR/complex_types.rs:7:12 - | -LL | const CST: (u32, (u32, (u32, (u32, u32)))) = (0, (0, (0, (0, 0)))); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: `-D clippy::type-complexity` implied by `-D warnings` - -error: very complex type used. Consider factoring parts into `type` definitions - --> $DIR/complex_types.rs:8:12 - | -LL | static ST: (u32, (u32, (u32, (u32, u32)))) = (0, (0, (0, (0, 0)))); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: very complex type used. Consider factoring parts into `type` definitions - --> $DIR/complex_types.rs:11:8 - | -LL | f: Vec<Vec<Box<(u32, u32, u32, u32)>>>, - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: very complex type used. Consider factoring parts into `type` definitions - --> $DIR/complex_types.rs:14:11 - | -LL | struct Ts(Vec<Vec<Box<(u32, u32, u32, u32)>>>); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: very complex type used. Consider factoring parts into `type` definitions - --> $DIR/complex_types.rs:17:11 - | -LL | Tuple(Vec<Vec<Box<(u32, u32, u32, u32)>>>), - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: very complex type used. Consider factoring parts into `type` definitions - --> $DIR/complex_types.rs:18:17 - | -LL | Struct { f: Vec<Vec<Box<(u32, u32, u32, u32)>>> }, - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: very complex type used. Consider factoring parts into `type` definitions - --> $DIR/complex_types.rs:22:14 - | -LL | const A: (u32, (u32, (u32, (u32, u32)))) = (0, (0, (0, (0, 0)))); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: very complex type used. Consider factoring parts into `type` definitions - --> $DIR/complex_types.rs:23:30 - | -LL | fn impl_method(&self, p: Vec<Vec<Box<(u32, u32, u32, u32)>>>) {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: very complex type used. Consider factoring parts into `type` definitions - --> $DIR/complex_types.rs:27:14 - | -LL | const A: Vec<Vec<Box<(u32, u32, u32, u32)>>>; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: very complex type used. Consider factoring parts into `type` definitions - --> $DIR/complex_types.rs:28:14 - | -LL | type B = Vec<Vec<Box<(u32, u32, u32, u32)>>>; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: very complex type used. Consider factoring parts into `type` definitions - --> $DIR/complex_types.rs:29:25 - | -LL | fn method(&self, p: Vec<Vec<Box<(u32, u32, u32, u32)>>>); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: very complex type used. Consider factoring parts into `type` definitions - --> $DIR/complex_types.rs:30:29 - | -LL | fn def_method(&self, p: Vec<Vec<Box<(u32, u32, u32, u32)>>>) {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: very complex type used. Consider factoring parts into `type` definitions - --> $DIR/complex_types.rs:33:15 - | -LL | fn test1() -> Vec<Vec<Box<(u32, u32, u32, u32)>>> { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: very complex type used. Consider factoring parts into `type` definitions - --> $DIR/complex_types.rs:37:14 - | -LL | fn test2(_x: Vec<Vec<Box<(u32, u32, u32, u32)>>>) {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: very complex type used. Consider factoring parts into `type` definitions - --> $DIR/complex_types.rs:40:13 - | -LL | let _y: Vec<Vec<Box<(u32, u32, u32, u32)>>> = vec![]; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: aborting due to 15 previous errors - diff --git a/src/tools/clippy/tests/ui/crashes/ice-6251.stderr b/src/tools/clippy/tests/ui/crashes/ice-6251.stderr index 9a7cf4b0919f6..8498c0407808d 100644 --- a/src/tools/clippy/tests/ui/crashes/ice-6251.stderr +++ b/src/tools/clippy/tests/ui/crashes/ice-6251.stderr @@ -16,8 +16,8 @@ LL | fn bug<T>() -> impl Iterator<Item = [(); { |x: [u8]| x }]> { = help: unsized fn params are gated as an unstable feature help: function arguments must have a statically known size, borrowed types always have a known size | -LL | fn bug<T>() -> impl Iterator<Item = [(); { |&x: [u8]| x }]> { - | ^ +LL | fn bug<T>() -> impl Iterator<Item = [(); { |x: &[u8]| x }]> { + | ^ error[E0277]: the size for values of type `[u8]` cannot be known at compilation time --> $DIR/ice-6251.rs:4:54 diff --git a/src/tools/clippy/tests/ui/crashes/ice-6256.rs b/src/tools/clippy/tests/ui/crashes/ice-6256.rs index 5409f36b3f1ed..67308263dadda 100644 --- a/src/tools/clippy/tests/ui/crashes/ice-6256.rs +++ b/src/tools/clippy/tests/ui/crashes/ice-6256.rs @@ -8,6 +8,7 @@ impl dyn TT { fn func(&self) {} } +#[rustfmt::skip] fn main() { let f = |x: &dyn TT| x.func(); //[default]~ ERROR: mismatched types //[nll]~^ ERROR: borrowed data escapes outside of closure diff --git a/src/tools/clippy/tests/ui/crashes/ice-6256.stderr b/src/tools/clippy/tests/ui/crashes/ice-6256.stderr index d1a8bdc3c8d8c..d35d459168f23 100644 --- a/src/tools/clippy/tests/ui/crashes/ice-6256.stderr +++ b/src/tools/clippy/tests/ui/crashes/ice-6256.stderr @@ -1,13 +1,13 @@ error[E0308]: mismatched types - --> $DIR/ice-6256.rs:12:28 + --> $DIR/ice-6256.rs:13:28 | LL | let f = |x: &dyn TT| x.func(); //[default]~ ERROR: mismatched types | ^^^^ lifetime mismatch | = note: expected reference `&(dyn TT + 'static)` found reference `&dyn TT` -note: the anonymous lifetime #1 defined on the body at 12:13... - --> $DIR/ice-6256.rs:12:13 +note: the anonymous lifetime #1 defined on the body at 13:13... + --> $DIR/ice-6256.rs:13:13 | LL | let f = |x: &dyn TT| x.func(); //[default]~ ERROR: mismatched types | ^^^^^^^^^^^^^^^^^^^^^ diff --git a/src/tools/clippy/tests/ui/crashes/ice-6792.rs b/src/tools/clippy/tests/ui/crashes/ice-6792.rs new file mode 100644 index 0000000000000..0e2ab1a39b82f --- /dev/null +++ b/src/tools/clippy/tests/ui/crashes/ice-6792.rs @@ -0,0 +1,20 @@ +//! This is a reproducer for the ICE 6792: https://github.com/rust-lang/rust-clippy/issues/6792. +//! The ICE is caused by using `TyCtxt::type_of(assoc_type_id)`. + +trait Trait { + type Ty; + + fn broken() -> Self::Ty; +} + +struct Foo {} + +impl Trait for Foo { + type Ty = Foo; + + fn broken() -> Self::Ty { + Self::Ty {} + } +} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/crashes/ice-6793.rs b/src/tools/clippy/tests/ui/crashes/ice-6793.rs new file mode 100644 index 0000000000000..12a4a0d25ef5d --- /dev/null +++ b/src/tools/clippy/tests/ui/crashes/ice-6793.rs @@ -0,0 +1,23 @@ +//! This is a reproducer for the ICE 6793: https://github.com/rust-lang/rust-clippy/issues/6793. +//! The ICE is caused by using `TyCtxt::type_of(assoc_type_id)`, which is the same as the ICE 6792. + +trait Trait { + type Ty: 'static + Clone; + + fn broken() -> Self::Ty; +} + +#[derive(Clone)] +struct MyType { + x: i32, +} + +impl Trait for MyType { + type Ty = MyType; + + fn broken() -> Self::Ty { + Self::Ty { x: 1 } + } +} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/crashes/ice-6840.rs b/src/tools/clippy/tests/ui/crashes/ice-6840.rs new file mode 100644 index 0000000000000..d789f60c5d5a0 --- /dev/null +++ b/src/tools/clippy/tests/ui/crashes/ice-6840.rs @@ -0,0 +1,31 @@ +//! This is a reproducer for the ICE 6840: https://github.com/rust-lang/rust-clippy/issues/6840. +//! The ICE is caused by `TyCtxt::layout_of` and `is_normalizable` not being strict enough +#![allow(dead_code)] +use std::collections::HashMap; + +pub trait Rule { + type DependencyKey; +} + +pub struct RuleEdges<R: Rule> { + dependencies: R::DependencyKey, +} + +type RuleDependencyEdges<R> = HashMap<u32, RuleEdges<R>>; + +// reproducer from the GitHub issue ends here +// but check some additional variants +type RuleDependencyEdgesArray<R> = HashMap<u32, [RuleEdges<R>; 8]>; +type RuleDependencyEdgesSlice<R> = HashMap<u32, &'static [RuleEdges<R>]>; +type RuleDependencyEdgesRef<R> = HashMap<u32, &'static RuleEdges<R>>; +type RuleDependencyEdgesRaw<R> = HashMap<u32, *const RuleEdges<R>>; +type RuleDependencyEdgesTuple<R> = HashMap<u32, (RuleEdges<R>, RuleEdges<R>)>; + +// and an additional checks to make sure fix doesn't have stack-overflow issue +// on self-containing types +pub struct SelfContaining { + inner: Box<SelfContaining>, +} +type SelfContainingEdges = HashMap<u32, SelfContaining>; + +fn main() {} diff --git a/src/tools/clippy/tests/ui/crashes/ice-7012.rs b/src/tools/clippy/tests/ui/crashes/ice-7012.rs new file mode 100644 index 0000000000000..60bdbc4f124e7 --- /dev/null +++ b/src/tools/clippy/tests/ui/crashes/ice-7012.rs @@ -0,0 +1,17 @@ +#![allow(clippy::all)] + +enum _MyOption { + None, + Some(()), +} + +impl _MyOption { + fn _foo(&self) { + match self { + &Self::Some(_) => {}, + _ => {}, + } + } +} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/crate_level_checks/entrypoint_recursion.rs b/src/tools/clippy/tests/ui/crate_level_checks/entrypoint_recursion.rs index 995787c533668..7ff5a16ed86ca 100644 --- a/src/tools/clippy/tests/ui/crate_level_checks/entrypoint_recursion.rs +++ b/src/tools/clippy/tests/ui/crate_level_checks/entrypoint_recursion.rs @@ -1,11 +1,11 @@ // ignore-macos // ignore-windows -#![feature(main)] +#![feature(rustc_attrs)] #[warn(clippy::main_recursion)] #[allow(unconditional_recursion)] -#[main] +#[rustc_main] fn a() { println!("Hello, World!"); a(); diff --git a/src/tools/clippy/tests/ui/crate_level_checks/no_std_main_recursion.rs b/src/tools/clippy/tests/ui/crate_level_checks/no_std_main_recursion.rs index 25b1417be9766..be4348e2bb71c 100644 --- a/src/tools/clippy/tests/ui/crate_level_checks/no_std_main_recursion.rs +++ b/src/tools/clippy/tests/ui/crate_level_checks/no_std_main_recursion.rs @@ -1,8 +1,8 @@ +// compile-flags: -Clink-arg=-nostartfiles // ignore-macos // ignore-windows -#![feature(lang_items, link_args, start, libc)] -#![link_args = "-nostartfiles"] +#![feature(lang_items, start, libc)] #![no_std] use core::panic::PanicInfo; diff --git a/src/tools/clippy/tests/ui/dbg_macro.rs b/src/tools/clippy/tests/ui/dbg_macro.rs index d2df7fbd3e84c..d74e2611ee1fd 100644 --- a/src/tools/clippy/tests/ui/dbg_macro.rs +++ b/src/tools/clippy/tests/ui/dbg_macro.rs @@ -1,11 +1,7 @@ #![warn(clippy::dbg_macro)] fn foo(n: u32) -> u32 { - if let Some(n) = dbg!(n.checked_sub(4)) { - n - } else { - n - } + if let Some(n) = dbg!(n.checked_sub(4)) { n } else { n } } fn factorial(n: u32) -> u32 { diff --git a/src/tools/clippy/tests/ui/dbg_macro.stderr b/src/tools/clippy/tests/ui/dbg_macro.stderr index b8aafe9667846..bdf372af29075 100644 --- a/src/tools/clippy/tests/ui/dbg_macro.stderr +++ b/src/tools/clippy/tests/ui/dbg_macro.stderr @@ -1,17 +1,17 @@ error: `dbg!` macro is intended as a debugging tool --> $DIR/dbg_macro.rs:4:22 | -LL | if let Some(n) = dbg!(n.checked_sub(4)) { +LL | if let Some(n) = dbg!(n.checked_sub(4)) { n } else { n } | ^^^^^^^^^^^^^^^^^^^^^^ | = note: `-D clippy::dbg-macro` implied by `-D warnings` help: ensure to avoid having uses of it in version control | -LL | if let Some(n) = n.checked_sub(4) { +LL | if let Some(n) = n.checked_sub(4) { n } else { n } | ^^^^^^^^^^^^^^^^ error: `dbg!` macro is intended as a debugging tool - --> $DIR/dbg_macro.rs:12:8 + --> $DIR/dbg_macro.rs:8:8 | LL | if dbg!(n <= 1) { | ^^^^^^^^^^^^ @@ -22,7 +22,7 @@ LL | if n <= 1 { | ^^^^^^ error: `dbg!` macro is intended as a debugging tool - --> $DIR/dbg_macro.rs:13:9 + --> $DIR/dbg_macro.rs:9:9 | LL | dbg!(1) | ^^^^^^^ @@ -33,7 +33,7 @@ LL | 1 | error: `dbg!` macro is intended as a debugging tool - --> $DIR/dbg_macro.rs:15:9 + --> $DIR/dbg_macro.rs:11:9 | LL | dbg!(n * factorial(n - 1)) | ^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -44,7 +44,7 @@ LL | n * factorial(n - 1) | error: `dbg!` macro is intended as a debugging tool - --> $DIR/dbg_macro.rs:20:5 + --> $DIR/dbg_macro.rs:16:5 | LL | dbg!(42); | ^^^^^^^^ @@ -55,7 +55,7 @@ LL | 42; | ^^ error: `dbg!` macro is intended as a debugging tool - --> $DIR/dbg_macro.rs:21:5 + --> $DIR/dbg_macro.rs:17:5 | LL | dbg!(dbg!(dbg!(42))); | ^^^^^^^^^^^^^^^^^^^^ @@ -66,7 +66,7 @@ LL | dbg!(dbg!(42)); | ^^^^^^^^^^^^^^ error: `dbg!` macro is intended as a debugging tool - --> $DIR/dbg_macro.rs:22:14 + --> $DIR/dbg_macro.rs:18:14 | LL | foo(3) + dbg!(factorial(4)); | ^^^^^^^^^^^^^^^^^^ diff --git a/src/tools/clippy/tests/ui/def_id_nocore.stderr b/src/tools/clippy/tests/ui/def_id_nocore.stderr index ed87a50547d17..702684f6b43a6 100644 --- a/src/tools/clippy/tests/ui/def_id_nocore.stderr +++ b/src/tools/clippy/tests/ui/def_id_nocore.stderr @@ -1,10 +1,11 @@ -error: methods called `as_*` usually take self by reference or self by mutable reference; consider choosing a less ambiguous name +error: methods called `as_*` usually take `self` by reference or `self` by mutable reference --> $DIR/def_id_nocore.rs:26:19 | LL | pub fn as_ref(self) -> &'static str { | ^^^^ | = note: `-D clippy::wrong-self-convention` implied by `-D warnings` + = help: consider choosing a less ambiguous name error: aborting due to previous error diff --git a/src/tools/clippy/tests/ui/default_numeric_fallback.rs b/src/tools/clippy/tests/ui/default_numeric_fallback.rs index 0b3758952ac6d..43468872db0b4 100644 --- a/src/tools/clippy/tests/ui/default_numeric_fallback.rs +++ b/src/tools/clippy/tests/ui/default_numeric_fallback.rs @@ -3,6 +3,7 @@ #![allow(clippy::never_loop)] #![allow(clippy::no_effect)] #![allow(clippy::unnecessary_operation)] +#![allow(clippy::branches_sharing_code)] mod basic_expr { fn test() { diff --git a/src/tools/clippy/tests/ui/default_numeric_fallback.stderr b/src/tools/clippy/tests/ui/default_numeric_fallback.stderr index b31aa4ebcf8e4..d1c4c8203dd83 100644 --- a/src/tools/clippy/tests/ui/default_numeric_fallback.stderr +++ b/src/tools/clippy/tests/ui/default_numeric_fallback.stderr @@ -1,5 +1,5 @@ error: default numeric fallback might occur - --> $DIR/default_numeric_fallback.rs:10:17 + --> $DIR/default_numeric_fallback.rs:11:17 | LL | let x = 22; | ^^ help: consider adding suffix: `22_i32` @@ -7,139 +7,139 @@ LL | let x = 22; = note: `-D clippy::default-numeric-fallback` implied by `-D warnings` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback.rs:11:18 + --> $DIR/default_numeric_fallback.rs:12:18 | LL | let x = [1, 2, 3]; | ^ help: consider adding suffix: `1_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback.rs:11:21 + --> $DIR/default_numeric_fallback.rs:12:21 | LL | let x = [1, 2, 3]; | ^ help: consider adding suffix: `2_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback.rs:11:24 + --> $DIR/default_numeric_fallback.rs:12:24 | LL | let x = [1, 2, 3]; | ^ help: consider adding suffix: `3_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback.rs:12:28 + --> $DIR/default_numeric_fallback.rs:13:28 | LL | let x = if true { (1, 2) } else { (3, 4) }; | ^ help: consider adding suffix: `1_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback.rs:12:31 + --> $DIR/default_numeric_fallback.rs:13:31 | LL | let x = if true { (1, 2) } else { (3, 4) }; | ^ help: consider adding suffix: `2_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback.rs:12:44 + --> $DIR/default_numeric_fallback.rs:13:44 | LL | let x = if true { (1, 2) } else { (3, 4) }; | ^ help: consider adding suffix: `3_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback.rs:12:47 + --> $DIR/default_numeric_fallback.rs:13:47 | LL | let x = if true { (1, 2) } else { (3, 4) }; | ^ help: consider adding suffix: `4_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback.rs:13:23 + --> $DIR/default_numeric_fallback.rs:14:23 | LL | let x = match 1 { | ^ help: consider adding suffix: `1_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback.rs:14:13 + --> $DIR/default_numeric_fallback.rs:15:13 | LL | 1 => 1, | ^ help: consider adding suffix: `1_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback.rs:14:18 + --> $DIR/default_numeric_fallback.rs:15:18 | LL | 1 => 1, | ^ help: consider adding suffix: `1_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback.rs:15:18 + --> $DIR/default_numeric_fallback.rs:16:18 | LL | _ => 2, | ^ help: consider adding suffix: `2_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback.rs:19:17 + --> $DIR/default_numeric_fallback.rs:20:17 | LL | let x = 0.12; | ^^^^ help: consider adding suffix: `0.12_f64` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback.rs:37:21 + --> $DIR/default_numeric_fallback.rs:38:21 | LL | let y = 1; | ^ help: consider adding suffix: `1_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback.rs:45:21 + --> $DIR/default_numeric_fallback.rs:46:21 | LL | let y = 1; | ^ help: consider adding suffix: `1_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback.rs:51:21 + --> $DIR/default_numeric_fallback.rs:52:21 | LL | let y = 1; | ^ help: consider adding suffix: `1_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback.rs:63:9 + --> $DIR/default_numeric_fallback.rs:64:9 | LL | 1 | ^ help: consider adding suffix: `1_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback.rs:69:27 + --> $DIR/default_numeric_fallback.rs:70:27 | LL | let f = || -> _ { 1 }; | ^ help: consider adding suffix: `1_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback.rs:73:29 + --> $DIR/default_numeric_fallback.rs:74:29 | LL | let f = || -> i32 { 1 }; | ^ help: consider adding suffix: `1_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback.rs:87:21 + --> $DIR/default_numeric_fallback.rs:88:21 | LL | generic_arg(1); | ^ help: consider adding suffix: `1_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback.rs:90:32 + --> $DIR/default_numeric_fallback.rs:91:32 | LL | let x: _ = generic_arg(1); | ^ help: consider adding suffix: `1_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback.rs:108:28 + --> $DIR/default_numeric_fallback.rs:109:28 | LL | GenericStruct { x: 1 }; | ^ help: consider adding suffix: `1_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback.rs:111:36 + --> $DIR/default_numeric_fallback.rs:112:36 | LL | let _ = GenericStruct { x: 1 }; | ^ help: consider adding suffix: `1_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback.rs:131:23 + --> $DIR/default_numeric_fallback.rs:132:23 | LL | s.generic_arg(1); | ^ help: consider adding suffix: `1_i32` diff --git a/src/tools/clippy/tests/ui/default_trait_access.fixed b/src/tools/clippy/tests/ui/default_trait_access.fixed index d05567a3f8249..4c80cabc72305 100644 --- a/src/tools/clippy/tests/ui/default_trait_access.fixed +++ b/src/tools/clippy/tests/ui/default_trait_access.fixed @@ -48,25 +48,7 @@ fn main() { println!( "[{}] [{}] [{}] [{}] [{}] [{}] [{}] [{}] [{}] [{:?}] [{:?}] [{:?}] [{:?}] [{:?}] [{:?}] [{:?}] [{:?}] [{:?}], [{:?}]", - s1, - s2, - s3, - s4, - s5, - s6, - s7, - s8, - s9, - s10, - s11, - s12, - s13, - s14, - s15, - s16, - s17, - s18, - s19, + s1, s2, s3, s4, s5, s6, s7, s8, s9, s10, s11, s12, s13, s14, s15, s16, s17, s18, s19, ); } diff --git a/src/tools/clippy/tests/ui/default_trait_access.rs b/src/tools/clippy/tests/ui/default_trait_access.rs index 447e70c0bbbea..a68b6455c0416 100644 --- a/src/tools/clippy/tests/ui/default_trait_access.rs +++ b/src/tools/clippy/tests/ui/default_trait_access.rs @@ -48,25 +48,7 @@ fn main() { println!( "[{}] [{}] [{}] [{}] [{}] [{}] [{}] [{}] [{}] [{:?}] [{:?}] [{:?}] [{:?}] [{:?}] [{:?}] [{:?}] [{:?}] [{:?}], [{:?}]", - s1, - s2, - s3, - s4, - s5, - s6, - s7, - s8, - s9, - s10, - s11, - s12, - s13, - s14, - s15, - s16, - s17, - s18, - s19, + s1, s2, s3, s4, s5, s6, s7, s8, s9, s10, s11, s12, s13, s14, s15, s16, s17, s18, s19, ); } diff --git a/src/tools/clippy/tests/ui/dereference.fixed b/src/tools/clippy/tests/ui/dereference.fixed deleted file mode 100644 index 459ca91b93b9e..0000000000000 --- a/src/tools/clippy/tests/ui/dereference.fixed +++ /dev/null @@ -1,93 +0,0 @@ -// run-rustfix - -#![allow(unused_variables, clippy::many_single_char_names, clippy::clone_double_ref)] -#![warn(clippy::explicit_deref_methods)] - -use std::ops::{Deref, DerefMut}; - -fn concat(deref_str: &str) -> String { - format!("{}bar", deref_str) -} - -fn just_return(deref_str: &str) -> &str { - deref_str -} - -struct CustomVec(Vec<u8>); -impl Deref for CustomVec { - type Target = Vec<u8>; - - fn deref(&self) -> &Vec<u8> { - &self.0 - } -} - -fn main() { - let a: &mut String = &mut String::from("foo"); - - // these should require linting - - let b: &str = &*a; - - let b: &mut str = &mut *a; - - // both derefs should get linted here - let b: String = format!("{}, {}", &*a, &*a); - - println!("{}", &*a); - - #[allow(clippy::match_single_binding)] - match &*a { - _ => (), - } - - let b: String = concat(&*a); - - let b = &*just_return(a); - - let b: String = concat(&*just_return(a)); - - let b: &str = &*a.deref(); - - let opt_a = Some(a.clone()); - let b = &*opt_a.unwrap(); - - // following should not require linting - - let cv = CustomVec(vec![0, 42]); - let c = cv.deref()[0]; - - let b: &str = &*a.deref(); - - let b: String = a.deref().clone(); - - let b: usize = a.deref_mut().len(); - - let b: &usize = &a.deref().len(); - - let b: &str = &*a; - - let b: &mut str = &mut *a; - - macro_rules! expr_deref { - ($body:expr) => { - $body.deref() - }; - } - let b: &str = expr_deref!(a); - - // The struct does not implement Deref trait - #[derive(Copy, Clone)] - struct NoLint(u32); - impl NoLint { - pub fn deref(self) -> u32 { - self.0 - } - pub fn deref_mut(self) -> u32 { - self.0 - } - } - let no_lint = NoLint(42); - let b = no_lint.deref(); - let b = no_lint.deref_mut(); -} diff --git a/src/tools/clippy/tests/ui/dereference.rs b/src/tools/clippy/tests/ui/dereference.rs deleted file mode 100644 index 8dc5272e67fa5..0000000000000 --- a/src/tools/clippy/tests/ui/dereference.rs +++ /dev/null @@ -1,93 +0,0 @@ -// run-rustfix - -#![allow(unused_variables, clippy::many_single_char_names, clippy::clone_double_ref)] -#![warn(clippy::explicit_deref_methods)] - -use std::ops::{Deref, DerefMut}; - -fn concat(deref_str: &str) -> String { - format!("{}bar", deref_str) -} - -fn just_return(deref_str: &str) -> &str { - deref_str -} - -struct CustomVec(Vec<u8>); -impl Deref for CustomVec { - type Target = Vec<u8>; - - fn deref(&self) -> &Vec<u8> { - &self.0 - } -} - -fn main() { - let a: &mut String = &mut String::from("foo"); - - // these should require linting - - let b: &str = a.deref(); - - let b: &mut str = a.deref_mut(); - - // both derefs should get linted here - let b: String = format!("{}, {}", a.deref(), a.deref()); - - println!("{}", a.deref()); - - #[allow(clippy::match_single_binding)] - match a.deref() { - _ => (), - } - - let b: String = concat(a.deref()); - - let b = just_return(a).deref(); - - let b: String = concat(just_return(a).deref()); - - let b: &str = a.deref().deref(); - - let opt_a = Some(a.clone()); - let b = opt_a.unwrap().deref(); - - // following should not require linting - - let cv = CustomVec(vec![0, 42]); - let c = cv.deref()[0]; - - let b: &str = &*a.deref(); - - let b: String = a.deref().clone(); - - let b: usize = a.deref_mut().len(); - - let b: &usize = &a.deref().len(); - - let b: &str = &*a; - - let b: &mut str = &mut *a; - - macro_rules! expr_deref { - ($body:expr) => { - $body.deref() - }; - } - let b: &str = expr_deref!(a); - - // The struct does not implement Deref trait - #[derive(Copy, Clone)] - struct NoLint(u32); - impl NoLint { - pub fn deref(self) -> u32 { - self.0 - } - pub fn deref_mut(self) -> u32 { - self.0 - } - } - let no_lint = NoLint(42); - let b = no_lint.deref(); - let b = no_lint.deref_mut(); -} diff --git a/src/tools/clippy/tests/ui/dereference.stderr b/src/tools/clippy/tests/ui/dereference.stderr deleted file mode 100644 index d26b462a43362..0000000000000 --- a/src/tools/clippy/tests/ui/dereference.stderr +++ /dev/null @@ -1,70 +0,0 @@ -error: explicit deref method call - --> $DIR/dereference.rs:30:19 - | -LL | let b: &str = a.deref(); - | ^^^^^^^^^ help: try this: `&*a` - | - = note: `-D clippy::explicit-deref-methods` implied by `-D warnings` - -error: explicit deref_mut method call - --> $DIR/dereference.rs:32:23 - | -LL | let b: &mut str = a.deref_mut(); - | ^^^^^^^^^^^^^ help: try this: `&mut *a` - -error: explicit deref method call - --> $DIR/dereference.rs:35:39 - | -LL | let b: String = format!("{}, {}", a.deref(), a.deref()); - | ^^^^^^^^^ help: try this: `&*a` - -error: explicit deref method call - --> $DIR/dereference.rs:35:50 - | -LL | let b: String = format!("{}, {}", a.deref(), a.deref()); - | ^^^^^^^^^ help: try this: `&*a` - -error: explicit deref method call - --> $DIR/dereference.rs:37:20 - | -LL | println!("{}", a.deref()); - | ^^^^^^^^^ help: try this: `&*a` - -error: explicit deref method call - --> $DIR/dereference.rs:40:11 - | -LL | match a.deref() { - | ^^^^^^^^^ help: try this: `&*a` - -error: explicit deref method call - --> $DIR/dereference.rs:44:28 - | -LL | let b: String = concat(a.deref()); - | ^^^^^^^^^ help: try this: `&*a` - -error: explicit deref method call - --> $DIR/dereference.rs:46:13 - | -LL | let b = just_return(a).deref(); - | ^^^^^^^^^^^^^^^^^^^^^^ help: try this: `&*just_return(a)` - -error: explicit deref method call - --> $DIR/dereference.rs:48:28 - | -LL | let b: String = concat(just_return(a).deref()); - | ^^^^^^^^^^^^^^^^^^^^^^ help: try this: `&*just_return(a)` - -error: explicit deref method call - --> $DIR/dereference.rs:50:19 - | -LL | let b: &str = a.deref().deref(); - | ^^^^^^^^^^^^^^^^^ help: try this: `&*a.deref()` - -error: explicit deref method call - --> $DIR/dereference.rs:53:13 - | -LL | let b = opt_a.unwrap().deref(); - | ^^^^^^^^^^^^^^^^^^^^^^ help: try this: `&*opt_a.unwrap()` - -error: aborting due to 11 previous errors - diff --git a/src/tools/clippy/tests/ui/derive.rs b/src/tools/clippy/tests/ui/derive.rs index 8fcb0e8b28d09..4e46bf1399173 100644 --- a/src/tools/clippy/tests/ui/derive.rs +++ b/src/tools/clippy/tests/ui/derive.rs @@ -35,7 +35,6 @@ impl<'a> Clone for Lt<'a> { } } -// Ok, `Clone` cannot be derived because of the big array #[derive(Copy)] struct BigArray { a: [u8; 65], @@ -47,7 +46,6 @@ impl Clone for BigArray { } } -// Ok, function pointers are not always Clone #[derive(Copy)] struct FnPtr { a: fn() -> !, @@ -59,7 +57,7 @@ impl Clone for FnPtr { } } -// Ok, generics +// Ok, Clone trait impl doesn't have constrained generics. #[derive(Copy)] struct Generic<T> { a: T, @@ -71,4 +69,21 @@ impl<T> Clone for Generic<T> { } } +#[derive(Copy)] +struct Generic2<T>(T); +impl<T: Clone> Clone for Generic2<T> { + fn clone(&self) -> Self { + Self(self.0.clone()) + } +} + +// Ok, Clone trait impl doesn't have constrained generics. +#[derive(Copy)] +struct GenericRef<'a, T, U>(T, &'a U); +impl<T: Clone, U> Clone for GenericRef<'_, T, U> { + fn clone(&self) -> Self { + Self(self.0.clone(), self.1) + } +} + fn main() {} diff --git a/src/tools/clippy/tests/ui/derive.stderr b/src/tools/clippy/tests/ui/derive.stderr index 1328a9b31077e..82a70ceecc368 100644 --- a/src/tools/clippy/tests/ui/derive.stderr +++ b/src/tools/clippy/tests/ui/derive.stderr @@ -40,7 +40,7 @@ LL | | } | |_^ error: you are implementing `Clone` explicitly on a `Copy` type - --> $DIR/derive.rs:44:1 + --> $DIR/derive.rs:43:1 | LL | / impl Clone for BigArray { LL | | fn clone(&self) -> Self { @@ -50,7 +50,7 @@ LL | | } | |_^ | note: consider deriving `Clone` or removing `Copy` - --> $DIR/derive.rs:44:1 + --> $DIR/derive.rs:43:1 | LL | / impl Clone for BigArray { LL | | fn clone(&self) -> Self { @@ -60,7 +60,7 @@ LL | | } | |_^ error: you are implementing `Clone` explicitly on a `Copy` type - --> $DIR/derive.rs:56:1 + --> $DIR/derive.rs:54:1 | LL | / impl Clone for FnPtr { LL | | fn clone(&self) -> Self { @@ -70,7 +70,7 @@ LL | | } | |_^ | note: consider deriving `Clone` or removing `Copy` - --> $DIR/derive.rs:56:1 + --> $DIR/derive.rs:54:1 | LL | / impl Clone for FnPtr { LL | | fn clone(&self) -> Self { @@ -79,5 +79,25 @@ LL | | } LL | | } | |_^ -error: aborting due to 4 previous errors +error: you are implementing `Clone` explicitly on a `Copy` type + --> $DIR/derive.rs:74:1 + | +LL | / impl<T: Clone> Clone for Generic2<T> { +LL | | fn clone(&self) -> Self { +LL | | Self(self.0.clone()) +LL | | } +LL | | } + | |_^ + | +note: consider deriving `Clone` or removing `Copy` + --> $DIR/derive.rs:74:1 + | +LL | / impl<T: Clone> Clone for Generic2<T> { +LL | | fn clone(&self) -> Self { +LL | | Self(self.0.clone()) +LL | | } +LL | | } + | |_^ + +error: aborting due to 5 previous errors diff --git a/src/tools/clippy/tests/ui/dlist.rs b/src/tools/clippy/tests/ui/dlist.rs deleted file mode 100644 index 2940d2d290110..0000000000000 --- a/src/tools/clippy/tests/ui/dlist.rs +++ /dev/null @@ -1,40 +0,0 @@ -#![feature(associated_type_defaults)] -#![warn(clippy::linkedlist)] -#![allow(dead_code, clippy::needless_pass_by_value)] - -extern crate alloc; -use alloc::collections::linked_list::LinkedList; - -trait Foo { - type Baz = LinkedList<u8>; - fn foo(_: LinkedList<u8>); - const BAR: Option<LinkedList<u8>>; -} - -// Ok, we don’t want to warn for implementations; see issue #605. -impl Foo for LinkedList<u8> { - fn foo(_: LinkedList<u8>) {} - const BAR: Option<LinkedList<u8>> = None; -} - -struct Bar; -impl Bar { - fn foo(_: LinkedList<u8>) {} -} - -pub fn test(my_favourite_linked_list: LinkedList<u8>) { - println!("{:?}", my_favourite_linked_list) -} - -pub fn test_ret() -> Option<LinkedList<u8>> { - unimplemented!(); -} - -pub fn test_local_not_linted() { - let _: LinkedList<u8>; -} - -fn main() { - test(LinkedList::new()); - test_local_not_linted(); -} diff --git a/src/tools/clippy/tests/ui/dlist.stderr b/src/tools/clippy/tests/ui/dlist.stderr deleted file mode 100644 index 64fde33c64f52..0000000000000 --- a/src/tools/clippy/tests/ui/dlist.stderr +++ /dev/null @@ -1,51 +0,0 @@ -error: I see you're using a LinkedList! Perhaps you meant some other data structure? - --> $DIR/dlist.rs:9:16 - | -LL | type Baz = LinkedList<u8>; - | ^^^^^^^^^^^^^^ - | - = note: `-D clippy::linkedlist` implied by `-D warnings` - = help: a `VecDeque` might work - -error: I see you're using a LinkedList! Perhaps you meant some other data structure? - --> $DIR/dlist.rs:10:15 - | -LL | fn foo(_: LinkedList<u8>); - | ^^^^^^^^^^^^^^ - | - = help: a `VecDeque` might work - -error: I see you're using a LinkedList! Perhaps you meant some other data structure? - --> $DIR/dlist.rs:11:23 - | -LL | const BAR: Option<LinkedList<u8>>; - | ^^^^^^^^^^^^^^ - | - = help: a `VecDeque` might work - -error: I see you're using a LinkedList! Perhaps you meant some other data structure? - --> $DIR/dlist.rs:22:15 - | -LL | fn foo(_: LinkedList<u8>) {} - | ^^^^^^^^^^^^^^ - | - = help: a `VecDeque` might work - -error: I see you're using a LinkedList! Perhaps you meant some other data structure? - --> $DIR/dlist.rs:25:39 - | -LL | pub fn test(my_favourite_linked_list: LinkedList<u8>) { - | ^^^^^^^^^^^^^^ - | - = help: a `VecDeque` might work - -error: I see you're using a LinkedList! Perhaps you meant some other data structure? - --> $DIR/dlist.rs:29:29 - | -LL | pub fn test_ret() -> Option<LinkedList<u8>> { - | ^^^^^^^^^^^^^^ - | - = help: a `VecDeque` might work - -error: aborting due to 6 previous errors - diff --git a/src/tools/clippy/tests/ui/doc.rs b/src/tools/clippy/tests/ui/doc.rs index d2c666bd2901c..c946a047f1b0a 100644 --- a/src/tools/clippy/tests/ui/doc.rs +++ b/src/tools/clippy/tests/ui/doc.rs @@ -1,8 +1,8 @@ //! This file tests for the `DOC_MARKDOWN` lint. -#![allow(dead_code)] +#![allow(dead_code, incomplete_features)] #![warn(clippy::doc_markdown)] -#![feature(custom_inner_attributes)] +#![feature(custom_inner_attributes, const_generics, const_evaluatable_checked, const_option)] #![rustfmt::skip] /// The foo_bar function does _nothing_. See also foo::bar. (note the dot there) @@ -202,3 +202,20 @@ fn issue_2343() {} /// This should not cause an ICE: /// __|_ _|__||_| fn pulldown_cmark_crash() {} + +// issue #7033 - const_evaluatable_checked ICE +struct S<T, const N: usize> +where [(); N.checked_next_power_of_two().unwrap()]: { + arr: [T; N.checked_next_power_of_two().unwrap()], + n: usize, +} + +impl<T: Copy + Default, const N: usize> S<T, N> +where [(); N.checked_next_power_of_two().unwrap()]: { + fn new() -> Self { + Self { + arr: [T::default(); N.checked_next_power_of_two().unwrap()], + n: 0, + } + } +} diff --git a/src/tools/clippy/tests/ui/doc_panics.rs b/src/tools/clippy/tests/ui/doc_panics.rs deleted file mode 100644 index 3008c2d5b8538..0000000000000 --- a/src/tools/clippy/tests/ui/doc_panics.rs +++ /dev/null @@ -1,122 +0,0 @@ -#![warn(clippy::missing_panics_doc)] -#![allow(clippy::option_map_unit_fn)] - -fn main() {} - -/// This needs to be documented -pub fn unwrap() { - let result = Err("Hi"); - result.unwrap() -} - -/// This needs to be documented -pub fn panic() { - panic!("This function panics") -} - -/// This needs to be documented -pub fn todo() { - todo!() -} - -/// This needs to be documented -pub fn inner_body(opt: Option<u32>) { - opt.map(|x| { - if x == 10 { - panic!() - } - }); -} - -/// This needs to be documented -pub fn unreachable_and_panic() { - if true { - unreachable!() - } else { - panic!() - } -} - -/// This is documented -/// -/// # Panics -/// -/// Panics if `result` if an error -pub fn unwrap_documented() { - let result = Err("Hi"); - result.unwrap() -} - -/// This is documented -/// -/// # Panics -/// -/// Panics just because -pub fn panic_documented() { - panic!("This function panics") -} - -/// This is documented -/// -/// # Panics -/// -/// Panics if `opt` is Just(10) -pub fn inner_body_documented(opt: Option<u32>) { - opt.map(|x| { - if x == 10 { - panic!() - } - }); -} - -/// This is documented -/// -/// # Panics -/// -/// We still need to do this part -pub fn todo_documented() { - todo!() -} - -/// This is documented -/// -/// # Panics -/// -/// We still need to do this part -pub fn unreachable_amd_panic_documented() { - if true { - unreachable!() - } else { - panic!() - } -} - -/// This is okay because it is private -fn unwrap_private() { - let result = Err("Hi"); - result.unwrap() -} - -/// This is okay because it is private -fn panic_private() { - panic!("This function panics") -} - -/// This is okay because it is private -fn todo_private() { - todo!() -} - -/// This is okay because it is private -fn inner_body_private(opt: Option<u32>) { - opt.map(|x| { - if x == 10 { - panic!() - } - }); -} - -/// This is okay because unreachable -pub fn unreachable() { - unreachable!("This function panics") -} diff --git a/src/tools/clippy/tests/ui/doc_panics.stderr b/src/tools/clippy/tests/ui/doc_panics.stderr deleted file mode 100644 index 287148690d27a..0000000000000 --- a/src/tools/clippy/tests/ui/doc_panics.stderr +++ /dev/null @@ -1,86 +0,0 @@ -error: docs for function which may panic missing `# Panics` section - --> $DIR/doc_panics.rs:7:1 - | -LL | / pub fn unwrap() { -LL | | let result = Err("Hi"); -LL | | result.unwrap() -LL | | } - | |_^ - | - = note: `-D clippy::missing-panics-doc` implied by `-D warnings` -note: first possible panic found here - --> $DIR/doc_panics.rs:9:5 - | -LL | result.unwrap() - | ^^^^^^^^^^^^^^^ - -error: docs for function which may panic missing `# Panics` section - --> $DIR/doc_panics.rs:13:1 - | -LL | / pub fn panic() { -LL | | panic!("This function panics") -LL | | } - | |_^ - | -note: first possible panic found here - --> $DIR/doc_panics.rs:14:5 - | -LL | panic!("This function panics") - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) - -error: docs for function which may panic missing `# Panics` section - --> $DIR/doc_panics.rs:18:1 - | -LL | / pub fn todo() { -LL | | todo!() -LL | | } - | |_^ - | -note: first possible panic found here - --> $DIR/doc_panics.rs:19:5 - | -LL | todo!() - | ^^^^^^^ - = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) - -error: docs for function which may panic missing `# Panics` section - --> $DIR/doc_panics.rs:23:1 - | -LL | / pub fn inner_body(opt: Option<u32>) { -LL | | opt.map(|x| { -LL | | if x == 10 { -LL | | panic!() -LL | | } -LL | | }); -LL | | } - | |_^ - | -note: first possible panic found here - --> $DIR/doc_panics.rs:26:13 - | -LL | panic!() - | ^^^^^^^^ - = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) - -error: docs for function which may panic missing `# Panics` section - --> $DIR/doc_panics.rs:32:1 - | -LL | / pub fn unreachable_and_panic() { -LL | | if true { -LL | | unreachable!() -LL | | } else { -LL | | panic!() -LL | | } -LL | | } - | |_^ - | -note: first possible panic found here - --> $DIR/doc_panics.rs:36:9 - | -LL | panic!() - | ^^^^^^^^ - = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) - -error: aborting due to 5 previous errors - diff --git a/src/tools/clippy/tests/ui/drop_forget_copy.stderr b/src/tools/clippy/tests/ui/drop_forget_copy.stderr index 82a4f047ba858..01de0be7caea9 100644 --- a/src/tools/clippy/tests/ui/drop_forget_copy.stderr +++ b/src/tools/clippy/tests/ui/drop_forget_copy.stderr @@ -1,4 +1,4 @@ -error: calls to `std::mem::drop` with a value that implements `Copy`. Dropping a copy leaves the original intact. +error: calls to `std::mem::drop` with a value that implements `Copy`. Dropping a copy leaves the original intact --> $DIR/drop_forget_copy.rs:33:5 | LL | drop(s1); @@ -11,7 +11,7 @@ note: argument has type SomeStruct LL | drop(s1); | ^^ -error: calls to `std::mem::drop` with a value that implements `Copy`. Dropping a copy leaves the original intact. +error: calls to `std::mem::drop` with a value that implements `Copy`. Dropping a copy leaves the original intact --> $DIR/drop_forget_copy.rs:34:5 | LL | drop(s2); @@ -23,7 +23,7 @@ note: argument has type SomeStruct LL | drop(s2); | ^^ -error: calls to `std::mem::drop` with a value that implements `Copy`. Dropping a copy leaves the original intact. +error: calls to `std::mem::drop` with a value that implements `Copy`. Dropping a copy leaves the original intact --> $DIR/drop_forget_copy.rs:36:5 | LL | drop(s4); @@ -35,7 +35,7 @@ note: argument has type SomeStruct LL | drop(s4); | ^^ -error: calls to `std::mem::forget` with a value that implements `Copy`. Forgetting a copy leaves the original intact. +error: calls to `std::mem::forget` with a value that implements `Copy`. Forgetting a copy leaves the original intact --> $DIR/drop_forget_copy.rs:39:5 | LL | forget(s1); @@ -48,7 +48,7 @@ note: argument has type SomeStruct LL | forget(s1); | ^^ -error: calls to `std::mem::forget` with a value that implements `Copy`. Forgetting a copy leaves the original intact. +error: calls to `std::mem::forget` with a value that implements `Copy`. Forgetting a copy leaves the original intact --> $DIR/drop_forget_copy.rs:40:5 | LL | forget(s2); @@ -60,7 +60,7 @@ note: argument has type SomeStruct LL | forget(s2); | ^^ -error: calls to `std::mem::forget` with a value that implements `Copy`. Forgetting a copy leaves the original intact. +error: calls to `std::mem::forget` with a value that implements `Copy`. Forgetting a copy leaves the original intact --> $DIR/drop_forget_copy.rs:42:5 | LL | forget(s4); diff --git a/src/tools/clippy/tests/ui/drop_ref.stderr b/src/tools/clippy/tests/ui/drop_ref.stderr index 10087cb4820a7..531849f0680ae 100644 --- a/src/tools/clippy/tests/ui/drop_ref.stderr +++ b/src/tools/clippy/tests/ui/drop_ref.stderr @@ -1,4 +1,4 @@ -error: calls to `std::mem::drop` with a reference instead of an owned value. Dropping a reference does nothing. +error: calls to `std::mem::drop` with a reference instead of an owned value. Dropping a reference does nothing --> $DIR/drop_ref.rs:11:5 | LL | drop(&SomeStruct); @@ -11,7 +11,7 @@ note: argument has type `&SomeStruct` LL | drop(&SomeStruct); | ^^^^^^^^^^^ -error: calls to `std::mem::drop` with a reference instead of an owned value. Dropping a reference does nothing. +error: calls to `std::mem::drop` with a reference instead of an owned value. Dropping a reference does nothing --> $DIR/drop_ref.rs:14:5 | LL | drop(&owned1); @@ -23,7 +23,7 @@ note: argument has type `&SomeStruct` LL | drop(&owned1); | ^^^^^^^ -error: calls to `std::mem::drop` with a reference instead of an owned value. Dropping a reference does nothing. +error: calls to `std::mem::drop` with a reference instead of an owned value. Dropping a reference does nothing --> $DIR/drop_ref.rs:15:5 | LL | drop(&&owned1); @@ -35,7 +35,7 @@ note: argument has type `&&SomeStruct` LL | drop(&&owned1); | ^^^^^^^^ -error: calls to `std::mem::drop` with a reference instead of an owned value. Dropping a reference does nothing. +error: calls to `std::mem::drop` with a reference instead of an owned value. Dropping a reference does nothing --> $DIR/drop_ref.rs:16:5 | LL | drop(&mut owned1); @@ -47,7 +47,7 @@ note: argument has type `&mut SomeStruct` LL | drop(&mut owned1); | ^^^^^^^^^^^ -error: calls to `std::mem::drop` with a reference instead of an owned value. Dropping a reference does nothing. +error: calls to `std::mem::drop` with a reference instead of an owned value. Dropping a reference does nothing --> $DIR/drop_ref.rs:20:5 | LL | drop(reference1); @@ -59,7 +59,7 @@ note: argument has type `&SomeStruct` LL | drop(reference1); | ^^^^^^^^^^ -error: calls to `std::mem::drop` with a reference instead of an owned value. Dropping a reference does nothing. +error: calls to `std::mem::drop` with a reference instead of an owned value. Dropping a reference does nothing --> $DIR/drop_ref.rs:23:5 | LL | drop(reference2); @@ -71,7 +71,7 @@ note: argument has type `&mut SomeStruct` LL | drop(reference2); | ^^^^^^^^^^ -error: calls to `std::mem::drop` with a reference instead of an owned value. Dropping a reference does nothing. +error: calls to `std::mem::drop` with a reference instead of an owned value. Dropping a reference does nothing --> $DIR/drop_ref.rs:26:5 | LL | drop(reference3); @@ -83,7 +83,7 @@ note: argument has type `&SomeStruct` LL | drop(reference3); | ^^^^^^^^^^ -error: calls to `std::mem::drop` with a reference instead of an owned value. Dropping a reference does nothing. +error: calls to `std::mem::drop` with a reference instead of an owned value. Dropping a reference does nothing --> $DIR/drop_ref.rs:31:5 | LL | drop(&val); @@ -95,7 +95,7 @@ note: argument has type `&T` LL | drop(&val); | ^^^^ -error: calls to `std::mem::drop` with a reference instead of an owned value. Dropping a reference does nothing. +error: calls to `std::mem::drop` with a reference instead of an owned value. Dropping a reference does nothing --> $DIR/drop_ref.rs:39:5 | LL | std::mem::drop(&SomeStruct); diff --git a/src/tools/clippy/tests/ui/empty_loop_no_std.rs b/src/tools/clippy/tests/ui/empty_loop_no_std.rs index 4553d3ec505a3..235e0fc51799f 100644 --- a/src/tools/clippy/tests/ui/empty_loop_no_std.rs +++ b/src/tools/clippy/tests/ui/empty_loop_no_std.rs @@ -1,9 +1,9 @@ +// compile-flags: -Clink-arg=-nostartfiles // ignore-macos // ignore-windows #![warn(clippy::empty_loop)] -#![feature(lang_items, link_args, start, libc)] -#![link_args = "-nostartfiles"] +#![feature(lang_items, start, libc)] #![no_std] use core::panic::PanicInfo; diff --git a/src/tools/clippy/tests/ui/escape_analysis.rs b/src/tools/clippy/tests/ui/escape_analysis.rs index d26f48fc68f85..13e2b6c7a2e76 100644 --- a/src/tools/clippy/tests/ui/escape_analysis.rs +++ b/src/tools/clippy/tests/ui/escape_analysis.rs @@ -101,7 +101,7 @@ fn warn_match() { let x = box A; match &x { // not moved - ref y => (), + y => (), } } @@ -111,7 +111,7 @@ fn nowarn_large_array() { let x = box [1; 10000]; match &x { // not moved - ref y => (), + y => (), } } diff --git a/src/tools/clippy/tests/ui/eta.fixed b/src/tools/clippy/tests/ui/eta.fixed index 1b34c2f74eba1..2be2283e3fdd2 100644 --- a/src/tools/clippy/tests/ui/eta.fixed +++ b/src/tools/clippy/tests/ui/eta.fixed @@ -16,10 +16,25 @@ use std::path::PathBuf; +macro_rules! mac { + () => { + foobar() + }; +} + +macro_rules! closure_mac { + () => { + |n| foo(n) + }; +} + fn main() { let a = Some(1u8).map(foo); meta(foo); let c = Some(1u8).map(|a| {1+2; foo}(a)); + true.then(|| mac!()); // don't lint function in macro expansion + Some(1).map(closure_mac!()); // don't lint closure in macro expansion + let _: Option<Vec<u8>> = true.then(std::vec::Vec::new); // special case vec! let d = Some(1u8).map(|a| foo((|b| foo2(b))(a))); //is adjusted? all(&[1, 2, 3], &2, |x, y| below(x, y)); //is adjusted unsafe { diff --git a/src/tools/clippy/tests/ui/eta.rs b/src/tools/clippy/tests/ui/eta.rs index 4f050bd8479ae..f0373f9ccf673 100644 --- a/src/tools/clippy/tests/ui/eta.rs +++ b/src/tools/clippy/tests/ui/eta.rs @@ -16,10 +16,25 @@ use std::path::PathBuf; +macro_rules! mac { + () => { + foobar() + }; +} + +macro_rules! closure_mac { + () => { + |n| foo(n) + }; +} + fn main() { let a = Some(1u8).map(|a| foo(a)); meta(|a| foo(a)); let c = Some(1u8).map(|a| {1+2; foo}(a)); + true.then(|| mac!()); // don't lint function in macro expansion + Some(1).map(closure_mac!()); // don't lint closure in macro expansion + let _: Option<Vec<u8>> = true.then(|| vec![]); // special case vec! let d = Some(1u8).map(|a| foo((|b| foo2(b))(a))); //is adjusted? all(&[1, 2, 3], &&2, |x, y| below(x, y)); //is adjusted unsafe { diff --git a/src/tools/clippy/tests/ui/eta.stderr b/src/tools/clippy/tests/ui/eta.stderr index 16aa1b07733d5..57ed65279666a 100644 --- a/src/tools/clippy/tests/ui/eta.stderr +++ b/src/tools/clippy/tests/ui/eta.stderr @@ -1,80 +1,86 @@ -error: redundant closure found - --> $DIR/eta.rs:20:27 +error: redundant closure + --> $DIR/eta.rs:32:27 | LL | let a = Some(1u8).map(|a| foo(a)); - | ^^^^^^^^^^ help: remove closure as shown: `foo` + | ^^^^^^^^^^ help: replace the closure with the function itself: `foo` | = note: `-D clippy::redundant-closure` implied by `-D warnings` -error: redundant closure found - --> $DIR/eta.rs:21:10 +error: redundant closure + --> $DIR/eta.rs:33:10 | LL | meta(|a| foo(a)); - | ^^^^^^^^^^ help: remove closure as shown: `foo` + | ^^^^^^^^^^ help: replace the closure with the function itself: `foo` + +error: redundant closure + --> $DIR/eta.rs:37:40 + | +LL | let _: Option<Vec<u8>> = true.then(|| vec![]); // special case vec! + | ^^^^^^^^^ help: replace the closure with `Vec::new`: `std::vec::Vec::new` error: this expression borrows a reference (`&u8`) that is immediately dereferenced by the compiler - --> $DIR/eta.rs:24:21 + --> $DIR/eta.rs:39:21 | LL | all(&[1, 2, 3], &&2, |x, y| below(x, y)); //is adjusted | ^^^ help: change this to: `&2` | = note: `-D clippy::needless-borrow` implied by `-D warnings` -error: redundant closure found - --> $DIR/eta.rs:31:27 +error: redundant closure + --> $DIR/eta.rs:46:27 | LL | let e = Some(1u8).map(|a| generic(a)); - | ^^^^^^^^^^^^^^ help: remove closure as shown: `generic` + | ^^^^^^^^^^^^^^ help: replace the closure with the function itself: `generic` -error: redundant closure found - --> $DIR/eta.rs:74:51 +error: redundant closure + --> $DIR/eta.rs:89:51 | LL | let e = Some(TestStruct { some_ref: &i }).map(|a| a.foo()); - | ^^^^^^^^^^^ help: remove closure as shown: `TestStruct::foo` + | ^^^^^^^^^^^ help: replace the closure with the method itself: `TestStruct::foo` | = note: `-D clippy::redundant-closure-for-method-calls` implied by `-D warnings` -error: redundant closure found - --> $DIR/eta.rs:76:51 +error: redundant closure + --> $DIR/eta.rs:91:51 | LL | let e = Some(TestStruct { some_ref: &i }).map(|a| a.trait_foo()); - | ^^^^^^^^^^^^^^^^^ help: remove closure as shown: `TestTrait::trait_foo` + | ^^^^^^^^^^^^^^^^^ help: replace the closure with the method itself: `TestTrait::trait_foo` -error: redundant closure found - --> $DIR/eta.rs:79:42 +error: redundant closure + --> $DIR/eta.rs:94:42 | LL | let e = Some(&mut vec![1, 2, 3]).map(|v| v.clear()); - | ^^^^^^^^^^^^^ help: remove closure as shown: `std::vec::Vec::clear` + | ^^^^^^^^^^^^^ help: replace the closure with the method itself: `std::vec::Vec::clear` -error: redundant closure found - --> $DIR/eta.rs:84:29 +error: redundant closure + --> $DIR/eta.rs:99:29 | LL | let e = Some("str").map(|s| s.to_string()); - | ^^^^^^^^^^^^^^^^^ help: remove closure as shown: `std::string::ToString::to_string` + | ^^^^^^^^^^^^^^^^^ help: replace the closure with the method itself: `std::string::ToString::to_string` -error: redundant closure found - --> $DIR/eta.rs:86:27 +error: redundant closure + --> $DIR/eta.rs:101:27 | LL | let e = Some('a').map(|s| s.to_uppercase()); - | ^^^^^^^^^^^^^^^^^^^^ help: remove closure as shown: `char::to_uppercase` + | ^^^^^^^^^^^^^^^^^^^^ help: replace the closure with the method itself: `char::to_uppercase` -error: redundant closure found - --> $DIR/eta.rs:89:65 +error: redundant closure + --> $DIR/eta.rs:104:65 | LL | let e: std::vec::Vec<char> = vec!['a', 'b', 'c'].iter().map(|c| c.to_ascii_uppercase()).collect(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove closure as shown: `char::to_ascii_uppercase` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace the closure with the method itself: `char::to_ascii_uppercase` -error: redundant closure found - --> $DIR/eta.rs:172:27 +error: redundant closure + --> $DIR/eta.rs:187:27 | LL | let a = Some(1u8).map(|a| foo_ptr(a)); - | ^^^^^^^^^^^^^^ help: remove closure as shown: `foo_ptr` + | ^^^^^^^^^^^^^^ help: replace the closure with the function itself: `foo_ptr` -error: redundant closure found - --> $DIR/eta.rs:177:27 +error: redundant closure + --> $DIR/eta.rs:192:27 | LL | let a = Some(1u8).map(|a| closure(a)); - | ^^^^^^^^^^^^^^ help: remove closure as shown: `closure` + | ^^^^^^^^^^^^^^ help: replace the closure with the function itself: `closure` -error: aborting due to 12 previous errors +error: aborting due to 13 previous errors diff --git a/src/tools/clippy/tests/ui/explicit_counter_loop.stderr b/src/tools/clippy/tests/ui/explicit_counter_loop.stderr index 931af46efe663..4cbacffe87bf4 100644 --- a/src/tools/clippy/tests/ui/explicit_counter_loop.stderr +++ b/src/tools/clippy/tests/ui/explicit_counter_loop.stderr @@ -1,4 +1,4 @@ -error: the variable `_index` is used as a loop counter. +error: the variable `_index` is used as a loop counter --> $DIR/explicit_counter_loop.rs:6:5 | LL | for _v in &vec { @@ -6,37 +6,37 @@ LL | for _v in &vec { | = note: `-D clippy::explicit-counter-loop` implied by `-D warnings` -error: the variable `_index` is used as a loop counter. +error: the variable `_index` is used as a loop counter --> $DIR/explicit_counter_loop.rs:12:5 | LL | for _v in &vec { | ^^^^^^^^^^^^^^ help: consider using: `for (_index, _v) in vec.iter().enumerate()` -error: the variable `_index` is used as a loop counter. +error: the variable `_index` is used as a loop counter --> $DIR/explicit_counter_loop.rs:17:5 | LL | for _v in &mut vec { | ^^^^^^^^^^^^^^^^^^ help: consider using: `for (_index, _v) in vec.iter_mut().enumerate()` -error: the variable `_index` is used as a loop counter. +error: the variable `_index` is used as a loop counter --> $DIR/explicit_counter_loop.rs:22:5 | LL | for _v in vec { | ^^^^^^^^^^^^^ help: consider using: `for (_index, _v) in vec.into_iter().enumerate()` -error: the variable `count` is used as a loop counter. +error: the variable `count` is used as a loop counter --> $DIR/explicit_counter_loop.rs:61:9 | LL | for ch in text.chars() { | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `for (count, ch) in text.chars().enumerate()` -error: the variable `count` is used as a loop counter. +error: the variable `count` is used as a loop counter --> $DIR/explicit_counter_loop.rs:72:9 | LL | for ch in text.chars() { | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `for (count, ch) in text.chars().enumerate()` -error: the variable `count` is used as a loop counter. +error: the variable `count` is used as a loop counter --> $DIR/explicit_counter_loop.rs:130:9 | LL | for _i in 3..10 { diff --git a/src/tools/clippy/tests/ui/explicit_deref_methods.fixed b/src/tools/clippy/tests/ui/explicit_deref_methods.fixed new file mode 100644 index 0000000000000..51d0468e47ca4 --- /dev/null +++ b/src/tools/clippy/tests/ui/explicit_deref_methods.fixed @@ -0,0 +1,95 @@ +// run-rustfix + +#![allow(unused_variables, clippy::many_single_char_names, clippy::clone_double_ref)] +#![warn(clippy::explicit_deref_methods)] + +use std::ops::{Deref, DerefMut}; + +fn concat(deref_str: &str) -> String { + format!("{}bar", deref_str) +} + +fn just_return(deref_str: &str) -> &str { + deref_str +} + +struct CustomVec(Vec<u8>); +impl Deref for CustomVec { + type Target = Vec<u8>; + + fn deref(&self) -> &Vec<u8> { + &self.0 + } +} + +fn main() { + let a: &mut String = &mut String::from("foo"); + + // these should require linting + + let b: &str = &*a; + + let b: &mut str = &mut **a; + + // both derefs should get linted here + let b: String = format!("{}, {}", &*a, &*a); + + println!("{}", &*a); + + #[allow(clippy::match_single_binding)] + match &*a { + _ => (), + } + + let b: String = concat(&*a); + + let b = just_return(a); + + let b: String = concat(just_return(a)); + + let b: &str = &**a; + + let opt_a = Some(a.clone()); + let b = &*opt_a.unwrap(); + + // following should not require linting + + let cv = CustomVec(vec![0, 42]); + let c = cv.deref()[0]; + + let b: &str = &*a.deref(); + + let b: String = a.deref().clone(); + + let b: usize = a.deref_mut().len(); + + let b: &usize = &a.deref().len(); + + let b: &str = &*a; + + let b: &mut str = &mut *a; + + macro_rules! expr_deref { + ($body:expr) => { + $body.deref() + }; + } + let b: &str = expr_deref!(a); + + let b: &str = expr_deref!(&*a); + + // The struct does not implement Deref trait + #[derive(Copy, Clone)] + struct NoLint(u32); + impl NoLint { + pub fn deref(self) -> u32 { + self.0 + } + pub fn deref_mut(self) -> u32 { + self.0 + } + } + let no_lint = NoLint(42); + let b = no_lint.deref(); + let b = no_lint.deref_mut(); +} diff --git a/src/tools/clippy/tests/ui/explicit_deref_methods.rs b/src/tools/clippy/tests/ui/explicit_deref_methods.rs new file mode 100644 index 0000000000000..680664bd4f6f2 --- /dev/null +++ b/src/tools/clippy/tests/ui/explicit_deref_methods.rs @@ -0,0 +1,95 @@ +// run-rustfix + +#![allow(unused_variables, clippy::many_single_char_names, clippy::clone_double_ref)] +#![warn(clippy::explicit_deref_methods)] + +use std::ops::{Deref, DerefMut}; + +fn concat(deref_str: &str) -> String { + format!("{}bar", deref_str) +} + +fn just_return(deref_str: &str) -> &str { + deref_str +} + +struct CustomVec(Vec<u8>); +impl Deref for CustomVec { + type Target = Vec<u8>; + + fn deref(&self) -> &Vec<u8> { + &self.0 + } +} + +fn main() { + let a: &mut String = &mut String::from("foo"); + + // these should require linting + + let b: &str = a.deref(); + + let b: &mut str = a.deref_mut(); + + // both derefs should get linted here + let b: String = format!("{}, {}", a.deref(), a.deref()); + + println!("{}", a.deref()); + + #[allow(clippy::match_single_binding)] + match a.deref() { + _ => (), + } + + let b: String = concat(a.deref()); + + let b = just_return(a).deref(); + + let b: String = concat(just_return(a).deref()); + + let b: &str = a.deref().deref(); + + let opt_a = Some(a.clone()); + let b = opt_a.unwrap().deref(); + + // following should not require linting + + let cv = CustomVec(vec![0, 42]); + let c = cv.deref()[0]; + + let b: &str = &*a.deref(); + + let b: String = a.deref().clone(); + + let b: usize = a.deref_mut().len(); + + let b: &usize = &a.deref().len(); + + let b: &str = &*a; + + let b: &mut str = &mut *a; + + macro_rules! expr_deref { + ($body:expr) => { + $body.deref() + }; + } + let b: &str = expr_deref!(a); + + let b: &str = expr_deref!(a.deref()); + + // The struct does not implement Deref trait + #[derive(Copy, Clone)] + struct NoLint(u32); + impl NoLint { + pub fn deref(self) -> u32 { + self.0 + } + pub fn deref_mut(self) -> u32 { + self.0 + } + } + let no_lint = NoLint(42); + let b = no_lint.deref(); + let b = no_lint.deref_mut(); +} diff --git a/src/tools/clippy/tests/ui/explicit_deref_methods.stderr b/src/tools/clippy/tests/ui/explicit_deref_methods.stderr new file mode 100644 index 0000000000000..8035d77d18d5c --- /dev/null +++ b/src/tools/clippy/tests/ui/explicit_deref_methods.stderr @@ -0,0 +1,76 @@ +error: explicit `deref` method call + --> $DIR/explicit_deref_methods.rs:30:19 + | +LL | let b: &str = a.deref(); + | ^^^^^^^^^ help: try this: `&*a` + | + = note: `-D clippy::explicit-deref-methods` implied by `-D warnings` + +error: explicit `deref_mut` method call + --> $DIR/explicit_deref_methods.rs:32:23 + | +LL | let b: &mut str = a.deref_mut(); + | ^^^^^^^^^^^^^ help: try this: `&mut **a` + +error: explicit `deref` method call + --> $DIR/explicit_deref_methods.rs:35:39 + | +LL | let b: String = format!("{}, {}", a.deref(), a.deref()); + | ^^^^^^^^^ help: try this: `&*a` + +error: explicit `deref` method call + --> $DIR/explicit_deref_methods.rs:35:50 + | +LL | let b: String = format!("{}, {}", a.deref(), a.deref()); + | ^^^^^^^^^ help: try this: `&*a` + +error: explicit `deref` method call + --> $DIR/explicit_deref_methods.rs:37:20 + | +LL | println!("{}", a.deref()); + | ^^^^^^^^^ help: try this: `&*a` + +error: explicit `deref` method call + --> $DIR/explicit_deref_methods.rs:40:11 + | +LL | match a.deref() { + | ^^^^^^^^^ help: try this: `&*a` + +error: explicit `deref` method call + --> $DIR/explicit_deref_methods.rs:44:28 + | +LL | let b: String = concat(a.deref()); + | ^^^^^^^^^ help: try this: `&*a` + +error: explicit `deref` method call + --> $DIR/explicit_deref_methods.rs:46:13 + | +LL | let b = just_return(a).deref(); + | ^^^^^^^^^^^^^^^^^^^^^^ help: try this: `just_return(a)` + +error: explicit `deref` method call + --> $DIR/explicit_deref_methods.rs:48:28 + | +LL | let b: String = concat(just_return(a).deref()); + | ^^^^^^^^^^^^^^^^^^^^^^ help: try this: `just_return(a)` + +error: explicit `deref` method call + --> $DIR/explicit_deref_methods.rs:50:19 + | +LL | let b: &str = a.deref().deref(); + | ^^^^^^^^^^^^^^^^^ help: try this: `&**a` + +error: explicit `deref` method call + --> $DIR/explicit_deref_methods.rs:53:13 + | +LL | let b = opt_a.unwrap().deref(); + | ^^^^^^^^^^^^^^^^^^^^^^ help: try this: `&*opt_a.unwrap()` + +error: explicit `deref` method call + --> $DIR/explicit_deref_methods.rs:79:31 + | +LL | let b: &str = expr_deref!(a.deref()); + | ^^^^^^^^^ help: try this: `&*a` + +error: aborting due to 12 previous errors + diff --git a/src/tools/clippy/tests/ui/fallible_impl_from.stderr b/src/tools/clippy/tests/ui/fallible_impl_from.stderr index f787b30bdabc5..a938d234fa07b 100644 --- a/src/tools/clippy/tests/ui/fallible_impl_from.stderr +++ b/src/tools/clippy/tests/ui/fallible_impl_from.stderr @@ -13,7 +13,7 @@ note: the lint level is defined here | LL | #![deny(clippy::fallible_impl_from)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ - = help: `From` is intended for infallible conversions only. Use `TryFrom` if there's a possibility for the conversion to fail. + = help: `From` is intended for infallible conversions only. Use `TryFrom` if there's a possibility for the conversion to fail note: potential failure(s) --> $DIR/fallible_impl_from.rs:7:13 | @@ -32,7 +32,7 @@ LL | | } LL | | } | |_^ | - = help: `From` is intended for infallible conversions only. Use `TryFrom` if there's a possibility for the conversion to fail. + = help: `From` is intended for infallible conversions only. Use `TryFrom` if there's a possibility for the conversion to fail note: potential failure(s) --> $DIR/fallible_impl_from.rs:29:13 | @@ -52,7 +52,7 @@ LL | | } LL | | } | |_^ | - = help: `From` is intended for infallible conversions only. Use `TryFrom` if there's a possibility for the conversion to fail. + = help: `From` is intended for infallible conversions only. Use `TryFrom` if there's a possibility for the conversion to fail note: potential failure(s) --> $DIR/fallible_impl_from.rs:37:17 | @@ -79,7 +79,7 @@ LL | | } LL | | } | |_^ | - = help: `From` is intended for infallible conversions only. Use `TryFrom` if there's a possibility for the conversion to fail. + = help: `From` is intended for infallible conversions only. Use `TryFrom` if there's a possibility for the conversion to fail note: potential failure(s) --> $DIR/fallible_impl_from.rs:55:12 | diff --git a/src/tools/clippy/tests/ui/field_reassign_with_default.rs b/src/tools/clippy/tests/ui/field_reassign_with_default.rs index 9fc208f5332a5..1368c5d798480 100644 --- a/src/tools/clippy/tests/ui/field_reassign_with_default.rs +++ b/src/tools/clippy/tests/ui/field_reassign_with_default.rs @@ -136,6 +136,13 @@ fn main() { // Don't lint in external macros field_reassign_with_default!(); + + // be sure suggestion is correct with generics + let mut a: Wrapper<bool> = Default::default(); + a.i = true; + + let mut a: WrapperMulti<i32, i64> = Default::default(); + a.i = 42; } mod m { @@ -145,3 +152,14 @@ mod m { b: u64, } } + +#[derive(Default)] +struct Wrapper<T> { + i: T, +} + +#[derive(Default)] +struct WrapperMulti<T, U> { + i: T, + j: U, +} diff --git a/src/tools/clippy/tests/ui/field_reassign_with_default.stderr b/src/tools/clippy/tests/ui/field_reassign_with_default.stderr index 2f0f28f7bb724..dd7c0360bb1e2 100644 --- a/src/tools/clippy/tests/ui/field_reassign_with_default.stderr +++ b/src/tools/clippy/tests/ui/field_reassign_with_default.stderr @@ -83,5 +83,29 @@ note: consider initializing the variable with `C { i: vec![1], ..Default::defaul LL | let mut a: C = C::default(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: aborting due to 7 previous errors +error: field assignment outside of initializer for an instance created with Default::default() + --> $DIR/field_reassign_with_default.rs:142:5 + | +LL | a.i = true; + | ^^^^^^^^^^^ + | +note: consider initializing the variable with `Wrapper::<bool> { i: true }` and removing relevant reassignments + --> $DIR/field_reassign_with_default.rs:141:5 + | +LL | let mut a: Wrapper<bool> = Default::default(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: field assignment outside of initializer for an instance created with Default::default() + --> $DIR/field_reassign_with_default.rs:145:5 + | +LL | a.i = 42; + | ^^^^^^^^^ + | +note: consider initializing the variable with `WrapperMulti::<i32, i64> { i: 42, ..Default::default() }` and removing relevant reassignments + --> $DIR/field_reassign_with_default.rs:144:5 + | +LL | let mut a: WrapperMulti<i32, i64> = Default::default(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 9 previous errors diff --git a/src/tools/clippy/tests/ui/filter_map_next.stderr b/src/tools/clippy/tests/ui/filter_map_next.stderr index 45427684d96e1..ddc982c93fe6d 100644 --- a/src/tools/clippy/tests/ui/filter_map_next.stderr +++ b/src/tools/clippy/tests/ui/filter_map_next.stderr @@ -1,4 +1,4 @@ -error: called `filter_map(..).next()` on an `Iterator`. This is more succinctly expressed by calling `.find_map(..)` instead. +error: called `filter_map(..).next()` on an `Iterator`. This is more succinctly expressed by calling `.find_map(..)` instead --> $DIR/filter_map_next.rs:7:26 | LL | let _: Option<u32> = vec![1, 2, 3, 4, 5, 6] diff --git a/src/tools/clippy/tests/ui/filter_map_next_fixable.stderr b/src/tools/clippy/tests/ui/filter_map_next_fixable.stderr index 6c2530e0379e4..3bb062ffd7a32 100644 --- a/src/tools/clippy/tests/ui/filter_map_next_fixable.stderr +++ b/src/tools/clippy/tests/ui/filter_map_next_fixable.stderr @@ -1,4 +1,4 @@ -error: called `filter_map(..).next()` on an `Iterator`. This is more succinctly expressed by calling `.find_map(..)` instead. +error: called `filter_map(..).next()` on an `Iterator`. This is more succinctly expressed by calling `.find_map(..)` instead --> $DIR/filter_map_next_fixable.rs:8:32 | LL | let element: Option<i32> = a.iter().filter_map(|s| s.parse().ok()).next(); diff --git a/src/tools/clippy/tests/ui/filter_methods.rs b/src/tools/clippy/tests/ui/filter_methods.rs index 5145024161926..96121b114ce6c 100644 --- a/src/tools/clippy/tests/ui/filter_methods.rs +++ b/src/tools/clippy/tests/ui/filter_methods.rs @@ -1,5 +1,5 @@ #![warn(clippy::all, clippy::pedantic)] -#![allow(clippy::clippy::let_underscore_drop)] +#![allow(clippy::let_underscore_drop)] #![allow(clippy::missing_docs_in_private_items)] fn main() { diff --git a/src/tools/clippy/tests/ui/float_cmp.rs b/src/tools/clippy/tests/ui/float_cmp.rs index 586784b73e697..ad5d1a09c0345 100644 --- a/src/tools/clippy/tests/ui/float_cmp.rs +++ b/src/tools/clippy/tests/ui/float_cmp.rs @@ -21,19 +21,11 @@ where } fn eq_fl(x: f32, y: f32) -> bool { - if x.is_nan() { - y.is_nan() - } else { - x == y - } // no error, inside "eq" fn + if x.is_nan() { y.is_nan() } else { x == y } // no error, inside "eq" fn } fn fl_eq(x: f32, y: f32) -> bool { - if x.is_nan() { - y.is_nan() - } else { - x == y - } // no error, inside "eq" fn + if x.is_nan() { y.is_nan() } else { x == y } // no error, inside "eq" fn } struct X { diff --git a/src/tools/clippy/tests/ui/float_cmp.stderr b/src/tools/clippy/tests/ui/float_cmp.stderr index bb4051c466201..cb5b68b2e9585 100644 --- a/src/tools/clippy/tests/ui/float_cmp.stderr +++ b/src/tools/clippy/tests/ui/float_cmp.stderr @@ -1,5 +1,5 @@ error: strict comparison of `f32` or `f64` - --> $DIR/float_cmp.rs:66:5 + --> $DIR/float_cmp.rs:58:5 | LL | ONE as f64 != 2.0; | ^^^^^^^^^^^^^^^^^ help: consider comparing them within some margin of error: `(ONE as f64 - 2.0).abs() > error_margin` @@ -8,7 +8,7 @@ LL | ONE as f64 != 2.0; = note: `f32::EPSILON` and `f64::EPSILON` are available for the `error_margin` error: strict comparison of `f32` or `f64` - --> $DIR/float_cmp.rs:71:5 + --> $DIR/float_cmp.rs:63:5 | LL | x == 1.0; | ^^^^^^^^ help: consider comparing them within some margin of error: `(x - 1.0).abs() < error_margin` @@ -16,7 +16,7 @@ LL | x == 1.0; = note: `f32::EPSILON` and `f64::EPSILON` are available for the `error_margin` error: strict comparison of `f32` or `f64` - --> $DIR/float_cmp.rs:74:5 + --> $DIR/float_cmp.rs:66:5 | LL | twice(x) != twice(ONE as f64); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider comparing them within some margin of error: `(twice(x) - twice(ONE as f64)).abs() > error_margin` @@ -24,7 +24,7 @@ LL | twice(x) != twice(ONE as f64); = note: `f32::EPSILON` and `f64::EPSILON` are available for the `error_margin` error: strict comparison of `f32` or `f64` - --> $DIR/float_cmp.rs:94:5 + --> $DIR/float_cmp.rs:86:5 | LL | NON_ZERO_ARRAY[i] == NON_ZERO_ARRAY[j]; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider comparing them within some margin of error: `(NON_ZERO_ARRAY[i] - NON_ZERO_ARRAY[j]).abs() < error_margin` @@ -32,7 +32,7 @@ LL | NON_ZERO_ARRAY[i] == NON_ZERO_ARRAY[j]; = note: `f32::EPSILON` and `f64::EPSILON` are available for the `error_margin` error: strict comparison of `f32` or `f64` arrays - --> $DIR/float_cmp.rs:99:5 + --> $DIR/float_cmp.rs:91:5 | LL | a1 == a2; | ^^^^^^^^ @@ -40,7 +40,7 @@ LL | a1 == a2; = note: `f32::EPSILON` and `f64::EPSILON` are available for the `error_margin` error: strict comparison of `f32` or `f64` - --> $DIR/float_cmp.rs:100:5 + --> $DIR/float_cmp.rs:92:5 | LL | a1[0] == a2[0]; | ^^^^^^^^^^^^^^ help: consider comparing them within some margin of error: `(a1[0] - a2[0]).abs() < error_margin` diff --git a/src/tools/clippy/tests/ui/float_cmp_const.rs b/src/tools/clippy/tests/ui/float_cmp_const.rs index 263d9a7b92ddb..86ce3bf3bd992 100644 --- a/src/tools/clippy/tests/ui/float_cmp_const.rs +++ b/src/tools/clippy/tests/ui/float_cmp_const.rs @@ -8,11 +8,7 @@ const ONE: f32 = 1.0; const TWO: f32 = 2.0; fn eq_one(x: f32) -> bool { - if x.is_nan() { - false - } else { - x == ONE - } // no error, inside "eq" fn + if x.is_nan() { false } else { x == ONE } // no error, inside "eq" fn } fn main() { diff --git a/src/tools/clippy/tests/ui/float_cmp_const.stderr b/src/tools/clippy/tests/ui/float_cmp_const.stderr index 5d0455363e8e0..d8182cf855b08 100644 --- a/src/tools/clippy/tests/ui/float_cmp_const.stderr +++ b/src/tools/clippy/tests/ui/float_cmp_const.stderr @@ -1,5 +1,5 @@ error: strict comparison of `f32` or `f64` constant - --> $DIR/float_cmp_const.rs:20:5 + --> $DIR/float_cmp_const.rs:16:5 | LL | 1f32 == ONE; | ^^^^^^^^^^^ help: consider comparing them within some margin of error: `(1f32 - ONE).abs() < error_margin` @@ -8,7 +8,7 @@ LL | 1f32 == ONE; = note: `f32::EPSILON` and `f64::EPSILON` are available for the `error_margin` error: strict comparison of `f32` or `f64` constant - --> $DIR/float_cmp_const.rs:21:5 + --> $DIR/float_cmp_const.rs:17:5 | LL | TWO == ONE; | ^^^^^^^^^^ help: consider comparing them within some margin of error: `(TWO - ONE).abs() < error_margin` @@ -16,7 +16,7 @@ LL | TWO == ONE; = note: `f32::EPSILON` and `f64::EPSILON` are available for the `error_margin` error: strict comparison of `f32` or `f64` constant - --> $DIR/float_cmp_const.rs:22:5 + --> $DIR/float_cmp_const.rs:18:5 | LL | TWO != ONE; | ^^^^^^^^^^ help: consider comparing them within some margin of error: `(TWO - ONE).abs() > error_margin` @@ -24,7 +24,7 @@ LL | TWO != ONE; = note: `f32::EPSILON` and `f64::EPSILON` are available for the `error_margin` error: strict comparison of `f32` or `f64` constant - --> $DIR/float_cmp_const.rs:23:5 + --> $DIR/float_cmp_const.rs:19:5 | LL | ONE + ONE == TWO; | ^^^^^^^^^^^^^^^^ help: consider comparing them within some margin of error: `(ONE + ONE - TWO).abs() < error_margin` @@ -32,7 +32,7 @@ LL | ONE + ONE == TWO; = note: `f32::EPSILON` and `f64::EPSILON` are available for the `error_margin` error: strict comparison of `f32` or `f64` constant - --> $DIR/float_cmp_const.rs:25:5 + --> $DIR/float_cmp_const.rs:21:5 | LL | x as f32 == ONE; | ^^^^^^^^^^^^^^^ help: consider comparing them within some margin of error: `(x as f32 - ONE).abs() < error_margin` @@ -40,7 +40,7 @@ LL | x as f32 == ONE; = note: `f32::EPSILON` and `f64::EPSILON` are available for the `error_margin` error: strict comparison of `f32` or `f64` constant - --> $DIR/float_cmp_const.rs:28:5 + --> $DIR/float_cmp_const.rs:24:5 | LL | v == ONE; | ^^^^^^^^ help: consider comparing them within some margin of error: `(v - ONE).abs() < error_margin` @@ -48,7 +48,7 @@ LL | v == ONE; = note: `f32::EPSILON` and `f64::EPSILON` are available for the `error_margin` error: strict comparison of `f32` or `f64` constant - --> $DIR/float_cmp_const.rs:29:5 + --> $DIR/float_cmp_const.rs:25:5 | LL | v != ONE; | ^^^^^^^^ help: consider comparing them within some margin of error: `(v - ONE).abs() > error_margin` @@ -56,7 +56,7 @@ LL | v != ONE; = note: `f32::EPSILON` and `f64::EPSILON` are available for the `error_margin` error: strict comparison of `f32` or `f64` constant arrays - --> $DIR/float_cmp_const.rs:61:5 + --> $DIR/float_cmp_const.rs:57:5 | LL | NON_ZERO_ARRAY == NON_ZERO_ARRAY2; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/src/tools/clippy/tests/ui/floating_point_abs.fixed b/src/tools/clippy/tests/ui/floating_point_abs.fixed index b623e4988e7d4..cea727257c430 100644 --- a/src/tools/clippy/tests/ui/floating_point_abs.fixed +++ b/src/tools/clippy/tests/ui/floating_point_abs.fixed @@ -42,43 +42,23 @@ fn fake_nabs3(a: A) -> A { } fn not_fake_abs1(num: f64) -> f64 { - if num > 0.0 { - num - } else { - -num - 1f64 - } + if num > 0.0 { num } else { -num - 1f64 } } fn not_fake_abs2(num: f64) -> f64 { - if num > 0.0 { - num + 1.0 - } else { - -(num + 1.0) - } + if num > 0.0 { num + 1.0 } else { -(num + 1.0) } } fn not_fake_abs3(num1: f64, num2: f64) -> f64 { - if num1 > 0.0 { - num2 - } else { - -num2 - } + if num1 > 0.0 { num2 } else { -num2 } } fn not_fake_abs4(a: A) -> f64 { - if a.a > 0.0 { - a.b - } else { - -a.b - } + if a.a > 0.0 { a.b } else { -a.b } } fn not_fake_abs5(a: A) -> f64 { - if a.a > 0.0 { - a.a - } else { - -a.b - } + if a.a > 0.0 { a.a } else { -a.b } } fn main() { diff --git a/src/tools/clippy/tests/ui/floating_point_abs.rs b/src/tools/clippy/tests/ui/floating_point_abs.rs index cbf9c94e41e6a..ba8a8f18fa231 100644 --- a/src/tools/clippy/tests/ui/floating_point_abs.rs +++ b/src/tools/clippy/tests/ui/floating_point_abs.rs @@ -7,59 +7,31 @@ struct A { } fn fake_abs1(num: f64) -> f64 { - if num >= 0.0 { - num - } else { - -num - } + if num >= 0.0 { num } else { -num } } fn fake_abs2(num: f64) -> f64 { - if 0.0 < num { - num - } else { - -num - } + if 0.0 < num { num } else { -num } } fn fake_abs3(a: A) -> f64 { - if a.a > 0.0 { - a.a - } else { - -a.a - } + if a.a > 0.0 { a.a } else { -a.a } } fn fake_abs4(num: f64) -> f64 { - if 0.0 >= num { - -num - } else { - num - } + if 0.0 >= num { -num } else { num } } fn fake_abs5(a: A) -> f64 { - if a.a < 0.0 { - -a.a - } else { - a.a - } + if a.a < 0.0 { -a.a } else { a.a } } fn fake_nabs1(num: f64) -> f64 { - if num < 0.0 { - num - } else { - -num - } + if num < 0.0 { num } else { -num } } fn fake_nabs2(num: f64) -> f64 { - if 0.0 >= num { - num - } else { - -num - } + if 0.0 >= num { num } else { -num } } fn fake_nabs3(a: A) -> A { @@ -70,43 +42,23 @@ fn fake_nabs3(a: A) -> A { } fn not_fake_abs1(num: f64) -> f64 { - if num > 0.0 { - num - } else { - -num - 1f64 - } + if num > 0.0 { num } else { -num - 1f64 } } fn not_fake_abs2(num: f64) -> f64 { - if num > 0.0 { - num + 1.0 - } else { - -(num + 1.0) - } + if num > 0.0 { num + 1.0 } else { -(num + 1.0) } } fn not_fake_abs3(num1: f64, num2: f64) -> f64 { - if num1 > 0.0 { - num2 - } else { - -num2 - } + if num1 > 0.0 { num2 } else { -num2 } } fn not_fake_abs4(a: A) -> f64 { - if a.a > 0.0 { - a.b - } else { - -a.b - } + if a.a > 0.0 { a.b } else { -a.b } } fn not_fake_abs5(a: A) -> f64 { - if a.a > 0.0 { - a.a - } else { - -a.b - } + if a.a > 0.0 { a.a } else { -a.b } } fn main() { diff --git a/src/tools/clippy/tests/ui/floating_point_abs.stderr b/src/tools/clippy/tests/ui/floating_point_abs.stderr index 74a71f2ca7c57..35af70201fada 100644 --- a/src/tools/clippy/tests/ui/floating_point_abs.stderr +++ b/src/tools/clippy/tests/ui/floating_point_abs.stderr @@ -1,77 +1,49 @@ error: manual implementation of `abs` method --> $DIR/floating_point_abs.rs:10:5 | -LL | / if num >= 0.0 { -LL | | num -LL | | } else { -LL | | -num -LL | | } - | |_____^ help: try: `num.abs()` +LL | if num >= 0.0 { num } else { -num } + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `num.abs()` | = note: `-D clippy::suboptimal-flops` implied by `-D warnings` error: manual implementation of `abs` method - --> $DIR/floating_point_abs.rs:18:5 + --> $DIR/floating_point_abs.rs:14:5 | -LL | / if 0.0 < num { -LL | | num -LL | | } else { -LL | | -num -LL | | } - | |_____^ help: try: `num.abs()` +LL | if 0.0 < num { num } else { -num } + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `num.abs()` error: manual implementation of `abs` method - --> $DIR/floating_point_abs.rs:26:5 + --> $DIR/floating_point_abs.rs:18:5 | -LL | / if a.a > 0.0 { -LL | | a.a -LL | | } else { -LL | | -a.a -LL | | } - | |_____^ help: try: `a.a.abs()` +LL | if a.a > 0.0 { a.a } else { -a.a } + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `a.a.abs()` error: manual implementation of `abs` method - --> $DIR/floating_point_abs.rs:34:5 + --> $DIR/floating_point_abs.rs:22:5 | -LL | / if 0.0 >= num { -LL | | -num -LL | | } else { -LL | | num -LL | | } - | |_____^ help: try: `num.abs()` +LL | if 0.0 >= num { -num } else { num } + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `num.abs()` error: manual implementation of `abs` method - --> $DIR/floating_point_abs.rs:42:5 + --> $DIR/floating_point_abs.rs:26:5 | -LL | / if a.a < 0.0 { -LL | | -a.a -LL | | } else { -LL | | a.a -LL | | } - | |_____^ help: try: `a.a.abs()` +LL | if a.a < 0.0 { -a.a } else { a.a } + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `a.a.abs()` error: manual implementation of negation of `abs` method - --> $DIR/floating_point_abs.rs:50:5 + --> $DIR/floating_point_abs.rs:30:5 | -LL | / if num < 0.0 { -LL | | num -LL | | } else { -LL | | -num -LL | | } - | |_____^ help: try: `-num.abs()` +LL | if num < 0.0 { num } else { -num } + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `-num.abs()` error: manual implementation of negation of `abs` method - --> $DIR/floating_point_abs.rs:58:5 + --> $DIR/floating_point_abs.rs:34:5 | -LL | / if 0.0 >= num { -LL | | num -LL | | } else { -LL | | -num -LL | | } - | |_____^ help: try: `-num.abs()` +LL | if 0.0 >= num { num } else { -num } + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `-num.abs()` error: manual implementation of negation of `abs` method - --> $DIR/floating_point_abs.rs:67:12 + --> $DIR/floating_point_abs.rs:39:12 | LL | a: if a.a >= 0.0 { -a.a } else { a.a }, | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `-a.a.abs()` diff --git a/src/tools/clippy/tests/ui/floating_point_log.fixed b/src/tools/clippy/tests/ui/floating_point_log.fixed index 7dc7ee94affc0..5b487bb8fcf79 100644 --- a/src/tools/clippy/tests/ui/floating_point_log.fixed +++ b/src/tools/clippy/tests/ui/floating_point_log.fixed @@ -27,7 +27,7 @@ fn check_ln1p() { let _ = (x / 2.0).ln_1p(); let _ = x.powi(3).ln_1p(); let _ = (x.powi(3) / 2.0).ln_1p(); - let _ = ((std::f32::consts::E - 1.0)).ln_1p(); + let _ = (std::f32::consts::E - 1.0).ln_1p(); let _ = x.ln_1p(); let _ = x.powi(3).ln_1p(); let _ = (x + 2.0).ln_1p(); diff --git a/src/tools/clippy/tests/ui/floating_point_log.stderr b/src/tools/clippy/tests/ui/floating_point_log.stderr index 900dc2b79336a..96e5a15444170 100644 --- a/src/tools/clippy/tests/ui/floating_point_log.stderr +++ b/src/tools/clippy/tests/ui/floating_point_log.stderr @@ -90,7 +90,7 @@ error: ln(1 + x) can be computed more accurately --> $DIR/floating_point_log.rs:30:13 | LL | let _ = (1.0 + (std::f32::consts::E - 1.0)).ln(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `((std::f32::consts::E - 1.0)).ln_1p()` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `(std::f32::consts::E - 1.0).ln_1p()` error: ln(1 + x) can be computed more accurately --> $DIR/floating_point_log.rs:31:13 diff --git a/src/tools/clippy/tests/ui/for_loops_over_fallibles.stderr b/src/tools/clippy/tests/ui/for_loops_over_fallibles.stderr index bef228d4b93af..52b94875aec4d 100644 --- a/src/tools/clippy/tests/ui/for_loops_over_fallibles.stderr +++ b/src/tools/clippy/tests/ui/for_loops_over_fallibles.stderr @@ -1,4 +1,4 @@ -error: for loop over `option`, which is an `Option`. This is more readably written as an `if let` statement. +error: for loop over `option`, which is an `Option`. This is more readably written as an `if let` statement --> $DIR/for_loops_over_fallibles.rs:9:14 | LL | for x in option { @@ -7,7 +7,7 @@ LL | for x in option { = note: `-D clippy::for-loops-over-fallibles` implied by `-D warnings` = help: consider replacing `for x in option` with `if let Some(x) = option` -error: for loop over `result`, which is a `Result`. This is more readably written as an `if let` statement. +error: for loop over `result`, which is a `Result`. This is more readably written as an `if let` statement --> $DIR/for_loops_over_fallibles.rs:14:14 | LL | for x in result { @@ -15,7 +15,7 @@ LL | for x in result { | = help: consider replacing `for x in result` with `if let Ok(x) = result` -error: for loop over `option.ok_or("x not found")`, which is a `Result`. This is more readably written as an `if let` statement. +error: for loop over `option.ok_or("x not found")`, which is a `Result`. This is more readably written as an `if let` statement --> $DIR/for_loops_over_fallibles.rs:18:14 | LL | for x in option.ok_or("x not found") { @@ -31,7 +31,7 @@ LL | for x in v.iter().next() { | = note: `#[deny(clippy::iter_next_loop)]` on by default -error: for loop over `v.iter().next().and(Some(0))`, which is an `Option`. This is more readably written as an `if let` statement. +error: for loop over `v.iter().next().and(Some(0))`, which is an `Option`. This is more readably written as an `if let` statement --> $DIR/for_loops_over_fallibles.rs:29:14 | LL | for x in v.iter().next().and(Some(0)) { @@ -39,7 +39,7 @@ LL | for x in v.iter().next().and(Some(0)) { | = help: consider replacing `for x in v.iter().next().and(Some(0))` with `if let Some(x) = v.iter().next().and(Some(0))` -error: for loop over `v.iter().next().ok_or("x not found")`, which is a `Result`. This is more readably written as an `if let` statement. +error: for loop over `v.iter().next().ok_or("x not found")`, which is a `Result`. This is more readably written as an `if let` statement --> $DIR/for_loops_over_fallibles.rs:33:14 | LL | for x in v.iter().next().ok_or("x not found") { diff --git a/src/tools/clippy/tests/ui/forget_ref.stderr b/src/tools/clippy/tests/ui/forget_ref.stderr index b2c7f2023bfbf..73409388ed16a 100644 --- a/src/tools/clippy/tests/ui/forget_ref.stderr +++ b/src/tools/clippy/tests/ui/forget_ref.stderr @@ -1,4 +1,4 @@ -error: calls to `std::mem::forget` with a reference instead of an owned value. Forgetting a reference does nothing. +error: calls to `std::mem::forget` with a reference instead of an owned value. Forgetting a reference does nothing --> $DIR/forget_ref.rs:10:5 | LL | forget(&SomeStruct); @@ -11,7 +11,7 @@ note: argument has type `&SomeStruct` LL | forget(&SomeStruct); | ^^^^^^^^^^^ -error: calls to `std::mem::forget` with a reference instead of an owned value. Forgetting a reference does nothing. +error: calls to `std::mem::forget` with a reference instead of an owned value. Forgetting a reference does nothing --> $DIR/forget_ref.rs:13:5 | LL | forget(&owned); @@ -23,7 +23,7 @@ note: argument has type `&SomeStruct` LL | forget(&owned); | ^^^^^^ -error: calls to `std::mem::forget` with a reference instead of an owned value. Forgetting a reference does nothing. +error: calls to `std::mem::forget` with a reference instead of an owned value. Forgetting a reference does nothing --> $DIR/forget_ref.rs:14:5 | LL | forget(&&owned); @@ -35,7 +35,7 @@ note: argument has type `&&SomeStruct` LL | forget(&&owned); | ^^^^^^^ -error: calls to `std::mem::forget` with a reference instead of an owned value. Forgetting a reference does nothing. +error: calls to `std::mem::forget` with a reference instead of an owned value. Forgetting a reference does nothing --> $DIR/forget_ref.rs:15:5 | LL | forget(&mut owned); @@ -47,7 +47,7 @@ note: argument has type `&mut SomeStruct` LL | forget(&mut owned); | ^^^^^^^^^^ -error: calls to `std::mem::forget` with a reference instead of an owned value. Forgetting a reference does nothing. +error: calls to `std::mem::forget` with a reference instead of an owned value. Forgetting a reference does nothing --> $DIR/forget_ref.rs:19:5 | LL | forget(&*reference1); @@ -59,7 +59,7 @@ note: argument has type `&SomeStruct` LL | forget(&*reference1); | ^^^^^^^^^^^^ -error: calls to `std::mem::forget` with a reference instead of an owned value. Forgetting a reference does nothing. +error: calls to `std::mem::forget` with a reference instead of an owned value. Forgetting a reference does nothing --> $DIR/forget_ref.rs:22:5 | LL | forget(reference2); @@ -71,7 +71,7 @@ note: argument has type `&mut SomeStruct` LL | forget(reference2); | ^^^^^^^^^^ -error: calls to `std::mem::forget` with a reference instead of an owned value. Forgetting a reference does nothing. +error: calls to `std::mem::forget` with a reference instead of an owned value. Forgetting a reference does nothing --> $DIR/forget_ref.rs:25:5 | LL | forget(reference3); @@ -83,7 +83,7 @@ note: argument has type `&SomeStruct` LL | forget(reference3); | ^^^^^^^^^^ -error: calls to `std::mem::forget` with a reference instead of an owned value. Forgetting a reference does nothing. +error: calls to `std::mem::forget` with a reference instead of an owned value. Forgetting a reference does nothing --> $DIR/forget_ref.rs:30:5 | LL | forget(&val); @@ -95,7 +95,7 @@ note: argument has type `&T` LL | forget(&val); | ^^^^ -error: calls to `std::mem::forget` with a reference instead of an owned value. Forgetting a reference does nothing. +error: calls to `std::mem::forget` with a reference instead of an owned value. Forgetting a reference does nothing --> $DIR/forget_ref.rs:38:5 | LL | std::mem::forget(&SomeStruct); diff --git a/src/tools/clippy/tests/ui/from_str_radix_10.stderr b/src/tools/clippy/tests/ui/from_str_radix_10.stderr index 471bf52a9a7ed..da5c16f8d01a8 100644 --- a/src/tools/clippy/tests/ui/from_str_radix_10.stderr +++ b/src/tools/clippy/tests/ui/from_str_radix_10.stderr @@ -28,7 +28,7 @@ error: this call to `from_str_radix` can be replaced with a call to `str::parse` --> $DIR/from_str_radix_10.rs:32:5 | LL | u16::from_str_radix(&("10".to_owned() + "5"), 10)?; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `(("10".to_owned() + "5")).parse::<u16>()` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `("10".to_owned() + "5").parse::<u16>()` error: this call to `from_str_radix` can be replaced with a call to `str::parse` --> $DIR/from_str_radix_10.rs:33:5 diff --git a/src/tools/clippy/tests/ui/if_let_some_result.fixed b/src/tools/clippy/tests/ui/if_let_some_result.fixed index 80505fd997f42..62a25ce2d128c 100644 --- a/src/tools/clippy/tests/ui/if_let_some_result.fixed +++ b/src/tools/clippy/tests/ui/if_let_some_result.fixed @@ -3,19 +3,11 @@ #![warn(clippy::if_let_some_result)] fn str_to_int(x: &str) -> i32 { - if let Ok(y) = x.parse() { - y - } else { - 0 - } + if let Ok(y) = x.parse() { y } else { 0 } } fn str_to_int_ok(x: &str) -> i32 { - if let Ok(y) = x.parse() { - y - } else { - 0 - } + if let Ok(y) = x.parse() { y } else { 0 } } #[rustfmt::skip] diff --git a/src/tools/clippy/tests/ui/if_let_some_result.rs b/src/tools/clippy/tests/ui/if_let_some_result.rs index ecac13574456c..234ff5e9e80e2 100644 --- a/src/tools/clippy/tests/ui/if_let_some_result.rs +++ b/src/tools/clippy/tests/ui/if_let_some_result.rs @@ -3,19 +3,11 @@ #![warn(clippy::if_let_some_result)] fn str_to_int(x: &str) -> i32 { - if let Some(y) = x.parse().ok() { - y - } else { - 0 - } + if let Some(y) = x.parse().ok() { y } else { 0 } } fn str_to_int_ok(x: &str) -> i32 { - if let Ok(y) = x.parse() { - y - } else { - 0 - } + if let Ok(y) = x.parse() { y } else { 0 } } #[rustfmt::skip] diff --git a/src/tools/clippy/tests/ui/if_let_some_result.stderr b/src/tools/clippy/tests/ui/if_let_some_result.stderr index 6afee0f36b9da..0646dd27f35e8 100644 --- a/src/tools/clippy/tests/ui/if_let_some_result.stderr +++ b/src/tools/clippy/tests/ui/if_let_some_result.stderr @@ -1,17 +1,17 @@ error: matching on `Some` with `ok()` is redundant --> $DIR/if_let_some_result.rs:6:5 | -LL | if let Some(y) = x.parse().ok() { +LL | if let Some(y) = x.parse().ok() { y } else { 0 } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: `-D clippy::if-let-some-result` implied by `-D warnings` help: consider matching on `Ok(y)` and removing the call to `ok` instead | -LL | if let Ok(y) = x.parse() { +LL | if let Ok(y) = x.parse() { y } else { 0 } | ^^^^^^^^^^^^^^^^^^^^^^^^ error: matching on `Some` with `ok()` is redundant - --> $DIR/if_let_some_result.rs:24:9 + --> $DIR/if_let_some_result.rs:16:9 | LL | if let Some(y) = x . parse() . ok () { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/src/tools/clippy/tests/ui/if_same_then_else.rs b/src/tools/clippy/tests/ui/if_same_then_else.rs index 9c5fe02f7519b..ef9567455008b 100644 --- a/src/tools/clippy/tests/ui/if_same_then_else.rs +++ b/src/tools/clippy/tests/ui/if_same_then_else.rs @@ -5,7 +5,8 @@ clippy::never_loop, clippy::no_effect, clippy::unused_unit, - clippy::zero_divided_by_zero + clippy::zero_divided_by_zero, + clippy::branches_sharing_code )] struct Foo { diff --git a/src/tools/clippy/tests/ui/if_same_then_else.stderr b/src/tools/clippy/tests/ui/if_same_then_else.stderr index d9fdf06fa8b73..2f38052fc209e 100644 --- a/src/tools/clippy/tests/ui/if_same_then_else.stderr +++ b/src/tools/clippy/tests/ui/if_same_then_else.stderr @@ -1,111 +1,111 @@ error: this `if` has identical blocks - --> $DIR/if_same_then_else.rs:28:12 + --> $DIR/if_same_then_else.rs:21:13 | -LL | } else { - | ____________^ -LL | | //~ ERROR same body as `if` block +LL | if true { + | _____________^ LL | | Foo { bar: 42 }; LL | | 0..10; +LL | | ..; ... | LL | | foo(); -LL | | } +LL | | } else { | |_____^ | = note: `-D clippy::if-same-then-else` implied by `-D warnings` note: same as this - --> $DIR/if_same_then_else.rs:20:13 + --> $DIR/if_same_then_else.rs:29:12 | -LL | if true { - | _____________^ +LL | } else { + | ____________^ +LL | | //~ ERROR same body as `if` block LL | | Foo { bar: 42 }; LL | | 0..10; -LL | | ..; ... | LL | | foo(); -LL | | } else { +LL | | } | |_____^ error: this `if` has identical blocks - --> $DIR/if_same_then_else.rs:66:12 - | -LL | } else { - | ____________^ -LL | | //~ ERROR same body as `if` block -LL | | 0.0 -LL | | }; - | |_____^ - | -note: same as this - --> $DIR/if_same_then_else.rs:64:21 + --> $DIR/if_same_then_else.rs:65:21 | LL | let _ = if true { | _____________________^ LL | | 0.0 LL | | } else { | |_____^ - -error: this `if` has identical blocks - --> $DIR/if_same_then_else.rs:73:12 + | +note: same as this + --> $DIR/if_same_then_else.rs:67:12 | LL | } else { | ____________^ LL | | //~ ERROR same body as `if` block -LL | | -0.0 +LL | | 0.0 LL | | }; | |_____^ - | -note: same as this - --> $DIR/if_same_then_else.rs:71:21 + +error: this `if` has identical blocks + --> $DIR/if_same_then_else.rs:72:21 | LL | let _ = if true { | _____________________^ LL | | -0.0 LL | | } else { | |_____^ - -error: this `if` has identical blocks - --> $DIR/if_same_then_else.rs:89:12 + | +note: same as this + --> $DIR/if_same_then_else.rs:74:12 | LL | } else { | ____________^ LL | | //~ ERROR same body as `if` block -LL | | 42 +LL | | -0.0 LL | | }; | |_____^ - | -note: same as this - --> $DIR/if_same_then_else.rs:87:21 + +error: this `if` has identical blocks + --> $DIR/if_same_then_else.rs:88:21 | LL | let _ = if true { | _____________________^ LL | | 42 LL | | } else { | |_____^ - -error: this `if` has identical blocks - --> $DIR/if_same_then_else.rs:101:12 + | +note: same as this + --> $DIR/if_same_then_else.rs:90:12 | LL | } else { | ____________^ LL | | //~ ERROR same body as `if` block +LL | | 42 +LL | | }; + | |_____^ + +error: this `if` has identical blocks + --> $DIR/if_same_then_else.rs:95:13 + | +LL | if true { + | _____________^ LL | | let bar = if true { 42 } else { 43 }; LL | | +LL | | while foo() { ... | LL | | bar + 1; -LL | | } +LL | | } else { | |_____^ | note: same as this - --> $DIR/if_same_then_else.rs:94:13 + --> $DIR/if_same_then_else.rs:102:12 | -LL | if true { - | _____________^ +LL | } else { + | ____________^ +LL | | //~ ERROR same body as `if` block LL | | let bar = if true { 42 } else { 43 }; LL | | -LL | | while foo() { ... | LL | | bar + 1; -LL | | } else { +LL | | } | |_____^ error: aborting due to 5 previous errors diff --git a/src/tools/clippy/tests/ui/if_same_then_else2.rs b/src/tools/clippy/tests/ui/if_same_then_else2.rs index a2ff1b741ca25..e4dc5b647dfd2 100644 --- a/src/tools/clippy/tests/ui/if_same_then_else2.rs +++ b/src/tools/clippy/tests/ui/if_same_then_else2.rs @@ -5,7 +5,8 @@ clippy::collapsible_if, clippy::ifs_same_cond, clippy::needless_return, - clippy::single_element_loop + clippy::single_element_loop, + clippy::branches_sharing_code )] fn if_same_then_else2() -> Result<&'static str, ()> { diff --git a/src/tools/clippy/tests/ui/if_same_then_else2.stderr b/src/tools/clippy/tests/ui/if_same_then_else2.stderr index 454322d8aacda..6524be0af8517 100644 --- a/src/tools/clippy/tests/ui/if_same_then_else2.stderr +++ b/src/tools/clippy/tests/ui/if_same_then_else2.stderr @@ -1,19 +1,5 @@ error: this `if` has identical blocks - --> $DIR/if_same_then_else2.rs:21:12 - | -LL | } else { - | ____________^ -LL | | //~ ERROR same body as `if` block -LL | | for _ in &[42] { -LL | | let bar: &Option<_> = &Some::<u8>(42); -... | -LL | | } -LL | | } - | |_____^ - | - = note: `-D clippy::if-same-then-else` implied by `-D warnings` -note: same as this - --> $DIR/if_same_then_else2.rs:12:13 + --> $DIR/if_same_then_else2.rs:13:13 | LL | if true { | _____________^ @@ -24,95 +10,99 @@ LL | | if foo.is_some() { LL | | } LL | | } else { | |_____^ - -error: this `if` has identical blocks - --> $DIR/if_same_then_else2.rs:35:12 + | + = note: `-D clippy::if-same-then-else` implied by `-D warnings` +note: same as this + --> $DIR/if_same_then_else2.rs:22:12 | LL | } else { | ____________^ LL | | //~ ERROR same body as `if` block -LL | | if let Some(a) = Some(42) {} +LL | | for _ in &[42] { +LL | | let bar: &Option<_> = &Some::<u8>(42); +... | +LL | | } LL | | } | |_____^ - | -note: same as this - --> $DIR/if_same_then_else2.rs:33:13 + +error: this `if` has identical blocks + --> $DIR/if_same_then_else2.rs:34:13 | LL | if true { | _____________^ LL | | if let Some(a) = Some(42) {} LL | | } else { | |_____^ - -error: this `if` has identical blocks - --> $DIR/if_same_then_else2.rs:42:12 + | +note: same as this + --> $DIR/if_same_then_else2.rs:36:12 | LL | } else { | ____________^ LL | | //~ ERROR same body as `if` block -LL | | if let (1, .., 3) = (1, 2, 3) {} +LL | | if let Some(a) = Some(42) {} LL | | } | |_____^ - | -note: same as this - --> $DIR/if_same_then_else2.rs:40:13 + +error: this `if` has identical blocks + --> $DIR/if_same_then_else2.rs:41:13 | LL | if true { | _____________^ LL | | if let (1, .., 3) = (1, 2, 3) {} LL | | } else { | |_____^ - -error: this `if` has identical blocks - --> $DIR/if_same_then_else2.rs:92:12 + | +note: same as this + --> $DIR/if_same_then_else2.rs:43:12 | LL | } else { | ____________^ LL | | //~ ERROR same body as `if` block -LL | | f32::NAN -LL | | }; +LL | | if let (1, .., 3) = (1, 2, 3) {} +LL | | } | |_____^ - | -note: same as this - --> $DIR/if_same_then_else2.rs:90:21 + +error: this `if` has identical blocks + --> $DIR/if_same_then_else2.rs:91:21 | LL | let _ = if true { | _____________________^ LL | | f32::NAN LL | | } else { | |_____^ - -error: this `if` has identical blocks - --> $DIR/if_same_then_else2.rs:99:12 + | +note: same as this + --> $DIR/if_same_then_else2.rs:93:12 | LL | } else { | ____________^ LL | | //~ ERROR same body as `if` block -LL | | Ok("foo")?; -LL | | } +LL | | f32::NAN +LL | | }; | |_____^ - | -note: same as this - --> $DIR/if_same_then_else2.rs:97:13 + +error: this `if` has identical blocks + --> $DIR/if_same_then_else2.rs:98:13 | LL | if true { | _____________^ LL | | Ok("foo")?; LL | | } else { | |_____^ - -error: this `if` has identical blocks - --> $DIR/if_same_then_else2.rs:124:12 + | +note: same as this + --> $DIR/if_same_then_else2.rs:100:12 | LL | } else { | ____________^ -LL | | let foo = ""; -LL | | return Ok(&foo[0..]); +LL | | //~ ERROR same body as `if` block +LL | | Ok("foo")?; LL | | } | |_____^ - | -note: same as this - --> $DIR/if_same_then_else2.rs:121:20 + +error: this `if` has identical blocks + --> $DIR/if_same_then_else2.rs:122:20 | LL | } else if true { | ____________________^ @@ -120,6 +110,16 @@ LL | | let foo = ""; LL | | return Ok(&foo[0..]); LL | | } else { | |_____^ + | +note: same as this + --> $DIR/if_same_then_else2.rs:125:12 + | +LL | } else { + | ____________^ +LL | | let foo = ""; +LL | | return Ok(&foo[0..]); +LL | | } + | |_____^ error: aborting due to 6 previous errors diff --git a/src/tools/clippy/tests/ui/if_then_some_else_none.rs b/src/tools/clippy/tests/ui/if_then_some_else_none.rs new file mode 100644 index 0000000000000..54789bf93209d --- /dev/null +++ b/src/tools/clippy/tests/ui/if_then_some_else_none.rs @@ -0,0 +1,104 @@ +#![warn(clippy::if_then_some_else_none)] +#![feature(custom_inner_attributes)] + +fn main() { + // Should issue an error. + let _ = if foo() { + println!("true!"); + Some("foo") + } else { + None + }; + + // Should issue an error when macros are used. + let _ = if matches!(true, true) { + println!("true!"); + Some(matches!(true, false)) + } else { + None + }; + + // Should issue an error. Binary expression `o < 32` should be parenthesized. + let x = Some(5); + let _ = x.and_then(|o| if o < 32 { Some(o) } else { None }); + + // Should issue an error. Unary expression `!x` should be parenthesized. + let x = true; + let _ = if !x { Some(0) } else { None }; + + // Should not issue an error since the `else` block has a statement besides `None`. + let _ = if foo() { + println!("true!"); + Some("foo") + } else { + eprintln!("false..."); + None + }; + + // Should not issue an error since there are more than 2 blocks in the if-else chain. + let _ = if foo() { + println!("foo true!"); + Some("foo") + } else if bar() { + println!("bar true!"); + Some("bar") + } else { + None + }; + + let _ = if foo() { + println!("foo true!"); + Some("foo") + } else { + bar().then(|| { + println!("bar true!"); + "bar" + }) + }; + + // Should not issue an error since the `then` block has `None`, not `Some`. + let _ = if foo() { None } else { Some("foo is false") }; + + // Should not issue an error since the `else` block doesn't use `None` directly. + let _ = if foo() { Some("foo is true") } else { into_none() }; + + // Should not issue an error since the `then` block doesn't use `Some` directly. + let _ = if foo() { into_some("foo") } else { None }; +} + +fn _msrv_1_49() { + #![clippy::msrv = "1.49"] + // `bool::then` was stabilized in 1.50. Do not lint this + let _ = if foo() { + println!("true!"); + Some(149) + } else { + None + }; +} + +fn _msrv_1_50() { + #![clippy::msrv = "1.50"] + let _ = if foo() { + println!("true!"); + Some(150) + } else { + None + }; +} + +fn foo() -> bool { + unimplemented!() +} + +fn bar() -> bool { + unimplemented!() +} + +fn into_some<T>(v: T) -> Option<T> { + Some(v) +} + +fn into_none<T>() -> Option<T> { + None +} diff --git a/src/tools/clippy/tests/ui/if_then_some_else_none.stderr b/src/tools/clippy/tests/ui/if_then_some_else_none.stderr new file mode 100644 index 0000000000000..8cb22d569f4cc --- /dev/null +++ b/src/tools/clippy/tests/ui/if_then_some_else_none.stderr @@ -0,0 +1,61 @@ +error: this could be simplified with `bool::then` + --> $DIR/if_then_some_else_none.rs:6:13 + | +LL | let _ = if foo() { + | _____________^ +LL | | println!("true!"); +LL | | Some("foo") +LL | | } else { +LL | | None +LL | | }; + | |_____^ + | + = note: `-D clippy::if-then-some-else-none` implied by `-D warnings` + = help: consider using `bool::then` like: `foo().then(|| { /* snippet */ "foo" })` + +error: this could be simplified with `bool::then` + --> $DIR/if_then_some_else_none.rs:14:13 + | +LL | let _ = if matches!(true, true) { + | _____________^ +LL | | println!("true!"); +LL | | Some(matches!(true, false)) +LL | | } else { +LL | | None +LL | | }; + | |_____^ + | + = help: consider using `bool::then` like: `matches!(true, true).then(|| { /* snippet */ matches!(true, false) })` + +error: this could be simplified with `bool::then` + --> $DIR/if_then_some_else_none.rs:23:28 + | +LL | let _ = x.and_then(|o| if o < 32 { Some(o) } else { None }); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider using `bool::then` like: `(o < 32).then(|| o)` + +error: this could be simplified with `bool::then` + --> $DIR/if_then_some_else_none.rs:27:13 + | +LL | let _ = if !x { Some(0) } else { None }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider using `bool::then` like: `(!x).then(|| 0)` + +error: this could be simplified with `bool::then` + --> $DIR/if_then_some_else_none.rs:82:13 + | +LL | let _ = if foo() { + | _____________^ +LL | | println!("true!"); +LL | | Some(150) +LL | | } else { +LL | | None +LL | | }; + | |_____^ + | + = help: consider using `bool::then` like: `foo().then(|| { /* snippet */ 150 })` + +error: aborting due to 5 previous errors + diff --git a/src/tools/clippy/tests/ui/implicit_clone.rs b/src/tools/clippy/tests/ui/implicit_clone.rs new file mode 100644 index 0000000000000..cef71cf79d797 --- /dev/null +++ b/src/tools/clippy/tests/ui/implicit_clone.rs @@ -0,0 +1,108 @@ +#![warn(clippy::implicit_clone)] +#![allow(clippy::redundant_clone)] +use std::borrow::Borrow; +use std::ffi::{OsStr, OsString}; +use std::path::PathBuf; + +fn return_owned_from_slice(slice: &[u32]) -> Vec<u32> { + slice.to_owned() +} + +pub fn own_same<T>(v: T) -> T +where + T: ToOwned<Owned = T>, +{ + v.to_owned() +} + +pub fn own_same_from_ref<T>(v: &T) -> T +where + T: ToOwned<Owned = T>, +{ + v.to_owned() +} + +pub fn own_different<T, U>(v: T) -> U +where + T: ToOwned<Owned = U>, +{ + v.to_owned() +} + +#[derive(Copy, Clone)] +struct Kitten {} +impl Kitten { + // badly named method + fn to_vec(self) -> Kitten { + Kitten {} + } +} +impl Borrow<BorrowedKitten> for Kitten { + fn borrow(&self) -> &BorrowedKitten { + static VALUE: BorrowedKitten = BorrowedKitten {}; + &VALUE + } +} + +struct BorrowedKitten {} +impl ToOwned for BorrowedKitten { + type Owned = Kitten; + fn to_owned(&self) -> Kitten { + Kitten {} + } +} + +mod weird { + #[allow(clippy::ptr_arg)] + pub fn to_vec(v: &Vec<u32>) -> Vec<u32> { + v.clone() + } +} + +fn main() { + let vec = vec![5]; + let _ = return_owned_from_slice(&vec); + let _ = vec.to_owned(); + let _ = vec.to_vec(); + + let vec_ref = &vec; + let _ = return_owned_from_slice(vec_ref); + let _ = vec_ref.to_owned(); + let _ = vec_ref.to_vec(); + + // we expect no lint for this + let _ = weird::to_vec(&vec); + + // we expect no lints for this + let slice: &[u32] = &[1, 2, 3, 4, 5]; + let _ = return_owned_from_slice(slice); + let _ = slice.to_owned(); + let _ = slice.to_vec(); + + let str = "hello world".to_string(); + let _ = str.to_owned(); + + // testing w/ an arbitrary type + let kitten = Kitten {}; + let _ = kitten.to_owned(); + let _ = own_same_from_ref(&kitten); + // this shouln't lint + let _ = kitten.to_vec(); + + // we expect no lints for this + let borrowed = BorrowedKitten {}; + let _ = borrowed.to_owned(); + + let pathbuf = PathBuf::new(); + let _ = pathbuf.to_owned(); + let _ = pathbuf.to_path_buf(); + + let os_string = OsString::from("foo"); + let _ = os_string.to_owned(); + let _ = os_string.to_os_string(); + + // we expect no lints for this + let os_str = OsStr::new("foo"); + let _ = os_str.to_owned(); + let _ = os_str.to_os_string(); +} diff --git a/src/tools/clippy/tests/ui/implicit_clone.stderr b/src/tools/clippy/tests/ui/implicit_clone.stderr new file mode 100644 index 0000000000000..e6f7527b67219 --- /dev/null +++ b/src/tools/clippy/tests/ui/implicit_clone.stderr @@ -0,0 +1,64 @@ +error: implicitly cloning a `Vec` by calling `to_owned` on its dereferenced type + --> $DIR/implicit_clone.rs:65:17 + | +LL | let _ = vec.to_owned(); + | ^^^^^^^^ help: consider using: `clone` + | + = note: `-D clippy::implicit-clone` implied by `-D warnings` + +error: implicitly cloning a `Vec` by calling `to_vec` on its dereferenced type + --> $DIR/implicit_clone.rs:66:17 + | +LL | let _ = vec.to_vec(); + | ^^^^^^ help: consider using: `clone` + +error: implicitly cloning a `Vec` by calling `to_owned` on its dereferenced type + --> $DIR/implicit_clone.rs:70:21 + | +LL | let _ = vec_ref.to_owned(); + | ^^^^^^^^ help: consider using: `clone` + +error: implicitly cloning a `Vec` by calling `to_vec` on its dereferenced type + --> $DIR/implicit_clone.rs:71:21 + | +LL | let _ = vec_ref.to_vec(); + | ^^^^^^ help: consider using: `clone` + +error: implicitly cloning a `String` by calling `to_owned` on its dereferenced type + --> $DIR/implicit_clone.rs:83:17 + | +LL | let _ = str.to_owned(); + | ^^^^^^^^ help: consider using: `clone` + +error: implicitly cloning a `Kitten` by calling `to_owned` on its dereferenced type + --> $DIR/implicit_clone.rs:87:20 + | +LL | let _ = kitten.to_owned(); + | ^^^^^^^^ help: consider using: `clone` + +error: implicitly cloning a `PathBuf` by calling `to_owned` on its dereferenced type + --> $DIR/implicit_clone.rs:97:21 + | +LL | let _ = pathbuf.to_owned(); + | ^^^^^^^^ help: consider using: `clone` + +error: implicitly cloning a `PathBuf` by calling `to_path_buf` on its dereferenced type + --> $DIR/implicit_clone.rs:98:21 + | +LL | let _ = pathbuf.to_path_buf(); + | ^^^^^^^^^^^ help: consider using: `clone` + +error: implicitly cloning a `OsString` by calling `to_owned` on its dereferenced type + --> $DIR/implicit_clone.rs:101:23 + | +LL | let _ = os_string.to_owned(); + | ^^^^^^^^ help: consider using: `clone` + +error: implicitly cloning a `OsString` by calling `to_os_string` on its dereferenced type + --> $DIR/implicit_clone.rs:102:23 + | +LL | let _ = os_string.to_os_string(); + | ^^^^^^^^^^^^ help: consider using: `clone` + +error: aborting due to 10 previous errors + diff --git a/src/tools/clippy/tests/ui/implicit_return.fixed b/src/tools/clippy/tests/ui/implicit_return.fixed index 9066dc3fedfd6..59f7ad9c10624 100644 --- a/src/tools/clippy/tests/ui/implicit_return.fixed +++ b/src/tools/clippy/tests/ui/implicit_return.fixed @@ -14,11 +14,7 @@ fn test_end_of_fn() -> bool { #[allow(clippy::needless_bool)] fn test_if_block() -> bool { - if true { - return true - } else { - return false - } + if true { return true } else { return false } } #[rustfmt::skip] diff --git a/src/tools/clippy/tests/ui/implicit_return.rs b/src/tools/clippy/tests/ui/implicit_return.rs index c0d70ecf502ed..2c1bc04651508 100644 --- a/src/tools/clippy/tests/ui/implicit_return.rs +++ b/src/tools/clippy/tests/ui/implicit_return.rs @@ -14,11 +14,7 @@ fn test_end_of_fn() -> bool { #[allow(clippy::needless_bool)] fn test_if_block() -> bool { - if true { - true - } else { - false - } + if true { true } else { false } } #[rustfmt::skip] diff --git a/src/tools/clippy/tests/ui/implicit_return.stderr b/src/tools/clippy/tests/ui/implicit_return.stderr index fb2ec90276454..3608319e5bd2c 100644 --- a/src/tools/clippy/tests/ui/implicit_return.stderr +++ b/src/tools/clippy/tests/ui/implicit_return.stderr @@ -7,61 +7,61 @@ LL | true = note: `-D clippy::implicit-return` implied by `-D warnings` error: missing `return` statement - --> $DIR/implicit_return.rs:18:9 + --> $DIR/implicit_return.rs:17:15 | -LL | true - | ^^^^ help: add `return` as shown: `return true` +LL | if true { true } else { false } + | ^^^^ help: add `return` as shown: `return true` error: missing `return` statement - --> $DIR/implicit_return.rs:20:9 + --> $DIR/implicit_return.rs:17:29 | -LL | false - | ^^^^^ help: add `return` as shown: `return false` +LL | if true { true } else { false } + | ^^^^^ help: add `return` as shown: `return false` error: missing `return` statement - --> $DIR/implicit_return.rs:27:17 + --> $DIR/implicit_return.rs:23:17 | LL | true => false, | ^^^^^ help: add `return` as shown: `return false` error: missing `return` statement - --> $DIR/implicit_return.rs:28:20 + --> $DIR/implicit_return.rs:24:20 | LL | false => { true }, | ^^^^ help: add `return` as shown: `return true` error: missing `return` statement - --> $DIR/implicit_return.rs:43:9 + --> $DIR/implicit_return.rs:39:9 | LL | break true; | ^^^^^^^^^^ help: change `break` to `return` as shown: `return true` error: missing `return` statement - --> $DIR/implicit_return.rs:51:13 + --> $DIR/implicit_return.rs:47:13 | LL | break true; | ^^^^^^^^^^ help: change `break` to `return` as shown: `return true` error: missing `return` statement - --> $DIR/implicit_return.rs:60:13 + --> $DIR/implicit_return.rs:56:13 | LL | break true; | ^^^^^^^^^^ help: change `break` to `return` as shown: `return true` error: missing `return` statement - --> $DIR/implicit_return.rs:78:18 + --> $DIR/implicit_return.rs:74:18 | LL | let _ = || { true }; | ^^^^ help: add `return` as shown: `return true` error: missing `return` statement - --> $DIR/implicit_return.rs:79:16 + --> $DIR/implicit_return.rs:75:16 | LL | let _ = || true; | ^^^^ help: add `return` as shown: `return true` error: missing `return` statement - --> $DIR/implicit_return.rs:87:5 + --> $DIR/implicit_return.rs:83:5 | LL | format!("test {}", "test") | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: add `return` as shown: `return format!("test {}", "test")` diff --git a/src/tools/clippy/tests/ui/inconsistent_struct_constructor.stderr b/src/tools/clippy/tests/ui/inconsistent_struct_constructor.stderr index d7abe44f25408..d021bb19579f4 100644 --- a/src/tools/clippy/tests/ui/inconsistent_struct_constructor.stderr +++ b/src/tools/clippy/tests/ui/inconsistent_struct_constructor.stderr @@ -1,4 +1,4 @@ -error: inconsistent struct constructor +error: struct constructor field order is inconsistent with struct definition field order --> $DIR/inconsistent_struct_constructor.rs:25:9 | LL | Foo { y, x, z }; @@ -6,7 +6,7 @@ LL | Foo { y, x, z }; | = note: `-D clippy::inconsistent-struct-constructor` implied by `-D warnings` -error: inconsistent struct constructor +error: struct constructor field order is inconsistent with struct definition field order --> $DIR/inconsistent_struct_constructor.rs:43:9 | LL | / Foo { diff --git a/src/tools/clippy/tests/ui/indexing_slicing_index.stderr b/src/tools/clippy/tests/ui/indexing_slicing_index.stderr index 2f6c9e2f4e5a0..76ecec3348400 100644 --- a/src/tools/clippy/tests/ui/indexing_slicing_index.stderr +++ b/src/tools/clippy/tests/ui/indexing_slicing_index.stderr @@ -1,51 +1,51 @@ -error: indexing may panic. +error: indexing may panic --> $DIR/indexing_slicing_index.rs:10:5 | LL | x[index]; | ^^^^^^^^ | = note: `-D clippy::indexing-slicing` implied by `-D warnings` - = help: Consider using `.get(n)` or `.get_mut(n)` instead + = help: consider using `.get(n)` or `.get_mut(n)` instead -error: indexing may panic. +error: indexing may panic --> $DIR/indexing_slicing_index.rs:22:5 | LL | v[0]; | ^^^^ | - = help: Consider using `.get(n)` or `.get_mut(n)` instead + = help: consider using `.get(n)` or `.get_mut(n)` instead -error: indexing may panic. +error: indexing may panic --> $DIR/indexing_slicing_index.rs:23:5 | LL | v[10]; | ^^^^^ | - = help: Consider using `.get(n)` or `.get_mut(n)` instead + = help: consider using `.get(n)` or `.get_mut(n)` instead -error: indexing may panic. +error: indexing may panic --> $DIR/indexing_slicing_index.rs:24:5 | LL | v[1 << 3]; | ^^^^^^^^^ | - = help: Consider using `.get(n)` or `.get_mut(n)` instead + = help: consider using `.get(n)` or `.get_mut(n)` instead -error: indexing may panic. +error: indexing may panic --> $DIR/indexing_slicing_index.rs:30:5 | LL | v[N]; | ^^^^ | - = help: Consider using `.get(n)` or `.get_mut(n)` instead + = help: consider using `.get(n)` or `.get_mut(n)` instead -error: indexing may panic. +error: indexing may panic --> $DIR/indexing_slicing_index.rs:31:5 | LL | v[M]; | ^^^^ | - = help: Consider using `.get(n)` or `.get_mut(n)` instead + = help: consider using `.get(n)` or `.get_mut(n)` instead error: aborting due to 6 previous errors diff --git a/src/tools/clippy/tests/ui/indexing_slicing_slice.stderr b/src/tools/clippy/tests/ui/indexing_slicing_slice.stderr index 2231deee833ee..f70722b92a5b8 100644 --- a/src/tools/clippy/tests/ui/indexing_slicing_slice.stderr +++ b/src/tools/clippy/tests/ui/indexing_slicing_slice.stderr @@ -1,51 +1,51 @@ -error: slicing may panic. +error: slicing may panic --> $DIR/indexing_slicing_slice.rs:12:6 | LL | &x[index..]; | ^^^^^^^^^^ | = note: `-D clippy::indexing-slicing` implied by `-D warnings` - = help: Consider using `.get(n..)` or .get_mut(n..)` instead + = help: consider using `.get(n..)` or .get_mut(n..)` instead -error: slicing may panic. +error: slicing may panic --> $DIR/indexing_slicing_slice.rs:13:6 | LL | &x[..index]; | ^^^^^^^^^^ | - = help: Consider using `.get(..n)`or `.get_mut(..n)` instead + = help: consider using `.get(..n)`or `.get_mut(..n)` instead -error: slicing may panic. +error: slicing may panic --> $DIR/indexing_slicing_slice.rs:14:6 | LL | &x[index_from..index_to]; | ^^^^^^^^^^^^^^^^^^^^^^^ | - = help: Consider using `.get(n..m)` or `.get_mut(n..m)` instead + = help: consider using `.get(n..m)` or `.get_mut(n..m)` instead -error: slicing may panic. +error: slicing may panic --> $DIR/indexing_slicing_slice.rs:15:6 | LL | &x[index_from..][..index_to]; // Two lint reports, one for [index_from..] and another for [..index_to]. | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = help: Consider using `.get(..n)`or `.get_mut(..n)` instead + = help: consider using `.get(..n)`or `.get_mut(..n)` instead -error: slicing may panic. +error: slicing may panic --> $DIR/indexing_slicing_slice.rs:15:6 | LL | &x[index_from..][..index_to]; // Two lint reports, one for [index_from..] and another for [..index_to]. | ^^^^^^^^^^^^^^^ | - = help: Consider using `.get(n..)` or .get_mut(n..)` instead + = help: consider using `.get(n..)` or .get_mut(n..)` instead -error: slicing may panic. +error: slicing may panic --> $DIR/indexing_slicing_slice.rs:16:6 | LL | &x[5..][..10]; // Two lint reports, one for out of bounds [5..] and another for slicing [..10]. | ^^^^^^^^^^^^ | - = help: Consider using `.get(..n)`or `.get_mut(..n)` instead + = help: consider using `.get(..n)`or `.get_mut(..n)` instead error: range is out of bounds --> $DIR/indexing_slicing_slice.rs:16:8 @@ -55,21 +55,21 @@ LL | &x[5..][..10]; // Two lint reports, one for out of bounds [5..] and ano | = note: `-D clippy::out-of-bounds-indexing` implied by `-D warnings` -error: slicing may panic. +error: slicing may panic --> $DIR/indexing_slicing_slice.rs:17:6 | LL | &x[0..][..3]; | ^^^^^^^^^^^ | - = help: Consider using `.get(..n)`or `.get_mut(..n)` instead + = help: consider using `.get(..n)`or `.get_mut(..n)` instead -error: slicing may panic. +error: slicing may panic --> $DIR/indexing_slicing_slice.rs:18:6 | LL | &x[1..][..5]; | ^^^^^^^^^^^ | - = help: Consider using `.get(..n)`or `.get_mut(..n)` instead + = help: consider using `.get(..n)`or `.get_mut(..n)` instead error: range is out of bounds --> $DIR/indexing_slicing_slice.rs:25:12 @@ -83,21 +83,21 @@ error: range is out of bounds LL | &y[..=4]; | ^ -error: slicing may panic. +error: slicing may panic --> $DIR/indexing_slicing_slice.rs:31:6 | LL | &v[10..100]; | ^^^^^^^^^^ | - = help: Consider using `.get(n..m)` or `.get_mut(n..m)` instead + = help: consider using `.get(n..m)` or `.get_mut(n..m)` instead -error: slicing may panic. +error: slicing may panic --> $DIR/indexing_slicing_slice.rs:32:6 | LL | &x[10..][..100]; // Two lint reports, one for [10..] and another for [..100]. | ^^^^^^^^^^^^^^ | - = help: Consider using `.get(..n)`or `.get_mut(..n)` instead + = help: consider using `.get(..n)`or `.get_mut(..n)` instead error: range is out of bounds --> $DIR/indexing_slicing_slice.rs:32:8 @@ -105,21 +105,21 @@ error: range is out of bounds LL | &x[10..][..100]; // Two lint reports, one for [10..] and another for [..100]. | ^^ -error: slicing may panic. +error: slicing may panic --> $DIR/indexing_slicing_slice.rs:33:6 | LL | &v[10..]; | ^^^^^^^ | - = help: Consider using `.get(n..)` or .get_mut(n..)` instead + = help: consider using `.get(n..)` or .get_mut(n..)` instead -error: slicing may panic. +error: slicing may panic --> $DIR/indexing_slicing_slice.rs:34:6 | LL | &v[..100]; | ^^^^^^^^ | - = help: Consider using `.get(..n)`or `.get_mut(..n)` instead + = help: consider using `.get(..n)`or `.get_mut(..n)` instead error: aborting due to 16 previous errors diff --git a/src/tools/clippy/tests/ui/integer_division.stderr b/src/tools/clippy/tests/ui/integer_division.stderr index 72a232ef3d750..cbb7f88142495 100644 --- a/src/tools/clippy/tests/ui/integer_division.stderr +++ b/src/tools/clippy/tests/ui/integer_division.stderr @@ -5,7 +5,7 @@ LL | let n = 1 / 2; | ^^^^^ | = note: `-D clippy::integer-division` implied by `-D warnings` - = help: division of integers may cause loss of precision. consider using floats. + = help: division of integers may cause loss of precision. consider using floats error: integer division --> $DIR/integer_division.rs:6:13 @@ -13,7 +13,7 @@ error: integer division LL | let o = 1 / two; | ^^^^^^^ | - = help: division of integers may cause loss of precision. consider using floats. + = help: division of integers may cause loss of precision. consider using floats error: integer division --> $DIR/integer_division.rs:7:13 @@ -21,7 +21,7 @@ error: integer division LL | let p = two / 4; | ^^^^^^^ | - = help: division of integers may cause loss of precision. consider using floats. + = help: division of integers may cause loss of precision. consider using floats error: aborting due to 3 previous errors diff --git a/src/tools/clippy/tests/ui/into_iter_on_ref.fixed b/src/tools/clippy/tests/ui/into_iter_on_ref.fixed index 7f92d0dbdc973..b77f17944d89a 100644 --- a/src/tools/clippy/tests/ui/into_iter_on_ref.fixed +++ b/src/tools/clippy/tests/ui/into_iter_on_ref.fixed @@ -1,5 +1,5 @@ // run-rustfix -#![allow(clippy::useless_vec)] +#![allow(clippy::useless_vec, clippy::needless_borrow)] #![warn(clippy::into_iter_on_ref)] struct X; diff --git a/src/tools/clippy/tests/ui/into_iter_on_ref.rs b/src/tools/clippy/tests/ui/into_iter_on_ref.rs index 416056d3fdb9c..3854bb05af8fb 100644 --- a/src/tools/clippy/tests/ui/into_iter_on_ref.rs +++ b/src/tools/clippy/tests/ui/into_iter_on_ref.rs @@ -1,5 +1,5 @@ // run-rustfix -#![allow(clippy::useless_vec)] +#![allow(clippy::useless_vec, clippy::needless_borrow)] #![warn(clippy::into_iter_on_ref)] struct X; diff --git a/src/tools/clippy/tests/ui/iter_count.fixed b/src/tools/clippy/tests/ui/iter_count.fixed new file mode 100644 index 0000000000000..b11dadda6c24e --- /dev/null +++ b/src/tools/clippy/tests/ui/iter_count.fixed @@ -0,0 +1,86 @@ +// run-rustfix +// aux-build:option_helpers.rs + +#![warn(clippy::iter_count)] +#![allow( + unused_variables, + array_into_iter, + unused_mut, + clippy::into_iter_on_ref, + clippy::unnecessary_operation +)] + +extern crate option_helpers; + +use option_helpers::IteratorFalsePositives; +use std::collections::{BTreeMap, BTreeSet, BinaryHeap, HashMap, HashSet, LinkedList, VecDeque}; + +/// Struct to generate false positives for things with `.iter()`. +#[derive(Copy, Clone)] +struct HasIter; + +impl HasIter { + fn iter(self) -> IteratorFalsePositives { + IteratorFalsePositives { foo: 0 } + } + + fn iter_mut(self) -> IteratorFalsePositives { + IteratorFalsePositives { foo: 0 } + } + + fn into_iter(self) -> IteratorFalsePositives { + IteratorFalsePositives { foo: 0 } + } +} + +fn main() { + let mut vec = vec![0, 1, 2, 3]; + let mut boxed_slice: Box<[u8]> = Box::new([0, 1, 2, 3]); + let mut vec_deque: VecDeque<_> = vec.iter().cloned().collect(); + let mut hash_set = HashSet::new(); + let mut hash_map = HashMap::new(); + let mut b_tree_map = BTreeMap::new(); + let mut b_tree_set = BTreeSet::new(); + let mut linked_list = LinkedList::new(); + let mut binary_heap = BinaryHeap::new(); + hash_set.insert(1); + hash_map.insert(1, 2); + b_tree_map.insert(1, 2); + b_tree_set.insert(1); + linked_list.push_back(1); + binary_heap.push(1); + + &vec[..].len(); + vec.len(); + boxed_slice.len(); + vec_deque.len(); + hash_set.len(); + hash_map.len(); + b_tree_map.len(); + b_tree_set.len(); + linked_list.len(); + binary_heap.len(); + + vec.len(); + &vec[..].len(); + vec_deque.len(); + hash_map.len(); + b_tree_map.len(); + linked_list.len(); + + &vec[..].len(); + vec.len(); + vec_deque.len(); + hash_set.len(); + hash_map.len(); + b_tree_map.len(); + b_tree_set.len(); + linked_list.len(); + binary_heap.len(); + + // Make sure we don't lint for non-relevant types. + let false_positive = HasIter; + false_positive.iter().count(); + false_positive.iter_mut().count(); + false_positive.into_iter().count(); +} diff --git a/src/tools/clippy/tests/ui/iter_count.rs b/src/tools/clippy/tests/ui/iter_count.rs new file mode 100644 index 0000000000000..7d49c6a3dbbb9 --- /dev/null +++ b/src/tools/clippy/tests/ui/iter_count.rs @@ -0,0 +1,86 @@ +// run-rustfix +// aux-build:option_helpers.rs + +#![warn(clippy::iter_count)] +#![allow( + unused_variables, + array_into_iter, + unused_mut, + clippy::into_iter_on_ref, + clippy::unnecessary_operation +)] + +extern crate option_helpers; + +use option_helpers::IteratorFalsePositives; +use std::collections::{BTreeMap, BTreeSet, BinaryHeap, HashMap, HashSet, LinkedList, VecDeque}; + +/// Struct to generate false positives for things with `.iter()`. +#[derive(Copy, Clone)] +struct HasIter; + +impl HasIter { + fn iter(self) -> IteratorFalsePositives { + IteratorFalsePositives { foo: 0 } + } + + fn iter_mut(self) -> IteratorFalsePositives { + IteratorFalsePositives { foo: 0 } + } + + fn into_iter(self) -> IteratorFalsePositives { + IteratorFalsePositives { foo: 0 } + } +} + +fn main() { + let mut vec = vec![0, 1, 2, 3]; + let mut boxed_slice: Box<[u8]> = Box::new([0, 1, 2, 3]); + let mut vec_deque: VecDeque<_> = vec.iter().cloned().collect(); + let mut hash_set = HashSet::new(); + let mut hash_map = HashMap::new(); + let mut b_tree_map = BTreeMap::new(); + let mut b_tree_set = BTreeSet::new(); + let mut linked_list = LinkedList::new(); + let mut binary_heap = BinaryHeap::new(); + hash_set.insert(1); + hash_map.insert(1, 2); + b_tree_map.insert(1, 2); + b_tree_set.insert(1); + linked_list.push_back(1); + binary_heap.push(1); + + &vec[..].iter().count(); + vec.iter().count(); + boxed_slice.iter().count(); + vec_deque.iter().count(); + hash_set.iter().count(); + hash_map.iter().count(); + b_tree_map.iter().count(); + b_tree_set.iter().count(); + linked_list.iter().count(); + binary_heap.iter().count(); + + vec.iter_mut().count(); + &vec[..].iter_mut().count(); + vec_deque.iter_mut().count(); + hash_map.iter_mut().count(); + b_tree_map.iter_mut().count(); + linked_list.iter_mut().count(); + + &vec[..].into_iter().count(); + vec.into_iter().count(); + vec_deque.into_iter().count(); + hash_set.into_iter().count(); + hash_map.into_iter().count(); + b_tree_map.into_iter().count(); + b_tree_set.into_iter().count(); + linked_list.into_iter().count(); + binary_heap.into_iter().count(); + + // Make sure we don't lint for non-relevant types. + let false_positive = HasIter; + false_positive.iter().count(); + false_positive.iter_mut().count(); + false_positive.into_iter().count(); +} diff --git a/src/tools/clippy/tests/ui/iter_count.stderr b/src/tools/clippy/tests/ui/iter_count.stderr new file mode 100644 index 0000000000000..f3fb98e65b990 --- /dev/null +++ b/src/tools/clippy/tests/ui/iter_count.stderr @@ -0,0 +1,154 @@ +error: called `.iter().count()` on a `slice` + --> $DIR/iter_count.rs:53:6 + | +LL | &vec[..].iter().count(); + | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `vec[..].len()` + | + = note: `-D clippy::iter-count` implied by `-D warnings` + +error: called `.iter().count()` on a `Vec` + --> $DIR/iter_count.rs:54:5 + | +LL | vec.iter().count(); + | ^^^^^^^^^^^^^^^^^^ help: try: `vec.len()` + +error: called `.iter().count()` on a `slice` + --> $DIR/iter_count.rs:55:5 + | +LL | boxed_slice.iter().count(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `boxed_slice.len()` + +error: called `.iter().count()` on a `VecDeque` + --> $DIR/iter_count.rs:56:5 + | +LL | vec_deque.iter().count(); + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `vec_deque.len()` + +error: called `.iter().count()` on a `HashSet` + --> $DIR/iter_count.rs:57:5 + | +LL | hash_set.iter().count(); + | ^^^^^^^^^^^^^^^^^^^^^^^ help: try: `hash_set.len()` + +error: called `.iter().count()` on a `HashMap` + --> $DIR/iter_count.rs:58:5 + | +LL | hash_map.iter().count(); + | ^^^^^^^^^^^^^^^^^^^^^^^ help: try: `hash_map.len()` + +error: called `.iter().count()` on a `BTreeMap` + --> $DIR/iter_count.rs:59:5 + | +LL | b_tree_map.iter().count(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `b_tree_map.len()` + +error: called `.iter().count()` on a `BTreeSet` + --> $DIR/iter_count.rs:60:5 + | +LL | b_tree_set.iter().count(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `b_tree_set.len()` + +error: called `.iter().count()` on a `LinkedList` + --> $DIR/iter_count.rs:61:5 + | +LL | linked_list.iter().count(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `linked_list.len()` + +error: called `.iter().count()` on a `BinaryHeap` + --> $DIR/iter_count.rs:62:5 + | +LL | binary_heap.iter().count(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `binary_heap.len()` + +error: called `.iter_mut().count()` on a `Vec` + --> $DIR/iter_count.rs:64:5 + | +LL | vec.iter_mut().count(); + | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `vec.len()` + +error: called `.iter_mut().count()` on a `slice` + --> $DIR/iter_count.rs:65:6 + | +LL | &vec[..].iter_mut().count(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `vec[..].len()` + +error: called `.iter_mut().count()` on a `VecDeque` + --> $DIR/iter_count.rs:66:5 + | +LL | vec_deque.iter_mut().count(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `vec_deque.len()` + +error: called `.iter_mut().count()` on a `HashMap` + --> $DIR/iter_count.rs:67:5 + | +LL | hash_map.iter_mut().count(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `hash_map.len()` + +error: called `.iter_mut().count()` on a `BTreeMap` + --> $DIR/iter_count.rs:68:5 + | +LL | b_tree_map.iter_mut().count(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `b_tree_map.len()` + +error: called `.iter_mut().count()` on a `LinkedList` + --> $DIR/iter_count.rs:69:5 + | +LL | linked_list.iter_mut().count(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `linked_list.len()` + +error: called `.into_iter().count()` on a `slice` + --> $DIR/iter_count.rs:71:6 + | +LL | &vec[..].into_iter().count(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `vec[..].len()` + +error: called `.into_iter().count()` on a `Vec` + --> $DIR/iter_count.rs:72:5 + | +LL | vec.into_iter().count(); + | ^^^^^^^^^^^^^^^^^^^^^^^ help: try: `vec.len()` + +error: called `.into_iter().count()` on a `VecDeque` + --> $DIR/iter_count.rs:73:5 + | +LL | vec_deque.into_iter().count(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `vec_deque.len()` + +error: called `.into_iter().count()` on a `HashSet` + --> $DIR/iter_count.rs:74:5 + | +LL | hash_set.into_iter().count(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `hash_set.len()` + +error: called `.into_iter().count()` on a `HashMap` + --> $DIR/iter_count.rs:75:5 + | +LL | hash_map.into_iter().count(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `hash_map.len()` + +error: called `.into_iter().count()` on a `BTreeMap` + --> $DIR/iter_count.rs:76:5 + | +LL | b_tree_map.into_iter().count(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `b_tree_map.len()` + +error: called `.into_iter().count()` on a `BTreeSet` + --> $DIR/iter_count.rs:77:5 + | +LL | b_tree_set.into_iter().count(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `b_tree_set.len()` + +error: called `.into_iter().count()` on a `LinkedList` + --> $DIR/iter_count.rs:78:5 + | +LL | linked_list.into_iter().count(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `linked_list.len()` + +error: called `.into_iter().count()` on a `BinaryHeap` + --> $DIR/iter_count.rs:79:5 + | +LL | binary_heap.into_iter().count(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `binary_heap.len()` + +error: aborting due to 25 previous errors + diff --git a/src/tools/clippy/tests/ui/iterator_step_by_zero.stderr b/src/tools/clippy/tests/ui/iterator_step_by_zero.stderr index c2c6803b3e6e7..d792aea11dfa0 100644 --- a/src/tools/clippy/tests/ui/iterator_step_by_zero.stderr +++ b/src/tools/clippy/tests/ui/iterator_step_by_zero.stderr @@ -1,4 +1,4 @@ -error: Iterator::step_by(0) will panic at runtime +error: `Iterator::step_by(0)` will panic at runtime --> $DIR/iterator_step_by_zero.rs:3:13 | LL | let _ = vec!["A", "B", "B"].iter().step_by(0); @@ -6,37 +6,37 @@ LL | let _ = vec!["A", "B", "B"].iter().step_by(0); | = note: `-D clippy::iterator-step-by-zero` implied by `-D warnings` -error: Iterator::step_by(0) will panic at runtime +error: `Iterator::step_by(0)` will panic at runtime --> $DIR/iterator_step_by_zero.rs:4:13 | LL | let _ = "XXX".chars().step_by(0); | ^^^^^^^^^^^^^^^^^^^^^^^^ -error: Iterator::step_by(0) will panic at runtime +error: `Iterator::step_by(0)` will panic at runtime --> $DIR/iterator_step_by_zero.rs:5:13 | LL | let _ = (0..1).step_by(0); | ^^^^^^^^^^^^^^^^^ -error: Iterator::step_by(0) will panic at runtime +error: `Iterator::step_by(0)` will panic at runtime --> $DIR/iterator_step_by_zero.rs:14:13 | LL | let _ = (1..).step_by(0); | ^^^^^^^^^^^^^^^^ -error: Iterator::step_by(0) will panic at runtime +error: `Iterator::step_by(0)` will panic at runtime --> $DIR/iterator_step_by_zero.rs:15:13 | LL | let _ = (1..=2).step_by(0); | ^^^^^^^^^^^^^^^^^^ -error: Iterator::step_by(0) will panic at runtime +error: `Iterator::step_by(0)` will panic at runtime --> $DIR/iterator_step_by_zero.rs:18:13 | LL | let _ = x.step_by(0); | ^^^^^^^^^^^^ -error: Iterator::step_by(0) will panic at runtime +error: `Iterator::step_by(0)` will panic at runtime --> $DIR/iterator_step_by_zero.rs:22:13 | LL | let _ = v1.iter().step_by(2 / 3); diff --git a/src/tools/clippy/tests/ui/len_without_is_empty.rs b/src/tools/clippy/tests/ui/len_without_is_empty.rs index b5211318a1504..b9d66347c2748 100644 --- a/src/tools/clippy/tests/ui/len_without_is_empty.rs +++ b/src/tools/clippy/tests/ui/len_without_is_empty.rs @@ -1,3 +1,5 @@ +// edition:2018 + #![warn(clippy::len_without_is_empty)] #![allow(dead_code, unused)] @@ -34,6 +36,24 @@ impl PubAllowed { } } +pub struct PubAllowedFn; + +impl PubAllowedFn { + #[allow(clippy::len_without_is_empty)] + pub fn len(&self) -> isize { + 1 + } +} + +#[allow(clippy::len_without_is_empty)] +pub struct PubAllowedStruct; + +impl PubAllowedStruct { + pub fn len(&self) -> isize { + 1 + } +} + pub trait PubTraitsToo { fn len(&self) -> isize; } @@ -68,6 +88,18 @@ impl HasWrongIsEmpty { } } +pub struct MismatchedSelf; + +impl MismatchedSelf { + pub fn len(self) -> isize { + 1 + } + + pub fn is_empty(&self) -> bool { + false + } +} + struct NotPubOne; impl NotPubOne { @@ -142,4 +174,114 @@ pub trait DependsOnFoo: Foo { fn len(&mut self) -> usize; } +// issue #1562 +pub struct MultipleImpls; + +impl MultipleImpls { + pub fn len(&self) -> usize { + 1 + } +} + +impl MultipleImpls { + pub fn is_empty(&self) -> bool { + false + } +} + +// issue #6958 +pub struct OptionalLen; + +impl OptionalLen { + pub fn len(&self) -> Option<usize> { + Some(0) + } + + pub fn is_empty(&self) -> Option<bool> { + Some(true) + } +} + +pub struct OptionalLen2; +impl OptionalLen2 { + pub fn len(&self) -> Option<usize> { + Some(0) + } + + pub fn is_empty(&self) -> bool { + true + } +} + +pub struct OptionalLen3; +impl OptionalLen3 { + pub fn len(&self) -> usize { + 0 + } + + // should lint, len is not an option + pub fn is_empty(&self) -> Option<bool> { + None + } +} + +pub struct ResultLen; +impl ResultLen { + pub fn len(&self) -> Result<usize, ()> { + Ok(0) + } + + // Differing result types + pub fn is_empty(&self) -> Option<bool> { + Some(true) + } +} + +pub struct ResultLen2; +impl ResultLen2 { + pub fn len(&self) -> Result<usize, ()> { + Ok(0) + } + + pub fn is_empty(&self) -> Result<bool, ()> { + Ok(true) + } +} + +pub struct ResultLen3; +impl ResultLen3 { + pub fn len(&self) -> Result<usize, ()> { + Ok(0) + } + + // Non-fallible result is ok. + pub fn is_empty(&self) -> bool { + true + } +} + +pub struct OddLenSig; +impl OddLenSig { + // don't lint + pub fn len(&self) -> bool { + true + } +} + +// issue #6958 +pub struct AsyncLen; +impl AsyncLen { + async fn async_task(&self) -> bool { + true + } + + pub async fn len(&self) -> usize { + if self.async_task().await { 0 } else { 1 } + } + + pub async fn is_empty(&self) -> bool { + self.len().await == 0 + } +} + fn main() {} diff --git a/src/tools/clippy/tests/ui/len_without_is_empty.stderr b/src/tools/clippy/tests/ui/len_without_is_empty.stderr index d79c300c07445..3282709bcd671 100644 --- a/src/tools/clippy/tests/ui/len_without_is_empty.stderr +++ b/src/tools/clippy/tests/ui/len_without_is_empty.stderr @@ -1,54 +1,123 @@ -error: item `PubOne` has a public `len` method but no corresponding `is_empty` method - --> $DIR/len_without_is_empty.rs:6:1 +error: struct `PubOne` has a public `len` method, but no `is_empty` method + --> $DIR/len_without_is_empty.rs:9:5 | -LL | / impl PubOne { -LL | | pub fn len(&self) -> isize { -LL | | 1 -LL | | } -LL | | } - | |_^ +LL | pub fn len(&self) -> isize { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: `-D clippy::len-without-is-empty` implied by `-D warnings` error: trait `PubTraitsToo` has a `len` method but no (possibly inherited) `is_empty` method - --> $DIR/len_without_is_empty.rs:37:1 + --> $DIR/len_without_is_empty.rs:57:1 | LL | / pub trait PubTraitsToo { LL | | fn len(&self) -> isize; LL | | } | |_^ -error: item `HasIsEmpty` has a public `len` method but a private `is_empty` method - --> $DIR/len_without_is_empty.rs:49:1 +error: struct `HasIsEmpty` has a public `len` method, but a private `is_empty` method + --> $DIR/len_without_is_empty.rs:70:5 | -LL | / impl HasIsEmpty { -LL | | pub fn len(&self) -> isize { -LL | | 1 -LL | | } -... | -LL | | } -LL | | } - | |_^ +LL | pub fn len(&self) -> isize { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: `is_empty` defined here + --> $DIR/len_without_is_empty.rs:74:5 + | +LL | fn is_empty(&self) -> bool { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: item `HasWrongIsEmpty` has a public `len` method but no corresponding `is_empty` method - --> $DIR/len_without_is_empty.rs:61:1 +error: struct `HasWrongIsEmpty` has a public `len` method, but the `is_empty` method has an unexpected signature + --> $DIR/len_without_is_empty.rs:82:5 | -LL | / impl HasWrongIsEmpty { -LL | | pub fn len(&self) -> isize { -LL | | 1 -LL | | } -... | -LL | | } -LL | | } - | |_^ +LL | pub fn len(&self) -> isize { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: `is_empty` defined here + --> $DIR/len_without_is_empty.rs:86:5 + | +LL | pub fn is_empty(&self, x: u32) -> bool { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + = note: expected signature: `(&self) -> bool` + +error: struct `MismatchedSelf` has a public `len` method, but the `is_empty` method has an unexpected signature + --> $DIR/len_without_is_empty.rs:94:5 + | +LL | pub fn len(self) -> isize { + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: `is_empty` defined here + --> $DIR/len_without_is_empty.rs:98:5 + | +LL | pub fn is_empty(&self) -> bool { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + = note: expected signature: `(self) -> bool` error: trait `DependsOnFoo` has a `len` method but no (possibly inherited) `is_empty` method - --> $DIR/len_without_is_empty.rs:141:1 + --> $DIR/len_without_is_empty.rs:173:1 | LL | / pub trait DependsOnFoo: Foo { LL | | fn len(&mut self) -> usize; LL | | } | |_^ -error: aborting due to 5 previous errors +error: struct `OptionalLen3` has a public `len` method, but the `is_empty` method has an unexpected signature + --> $DIR/len_without_is_empty.rs:218:5 + | +LL | pub fn len(&self) -> usize { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: `is_empty` defined here + --> $DIR/len_without_is_empty.rs:223:5 + | +LL | pub fn is_empty(&self) -> Option<bool> { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + = note: expected signature: `(&self) -> bool` + +error: struct `ResultLen` has a public `len` method, but the `is_empty` method has an unexpected signature + --> $DIR/len_without_is_empty.rs:230:5 + | +LL | pub fn len(&self) -> Result<usize, ()> { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: `is_empty` defined here + --> $DIR/len_without_is_empty.rs:235:5 + | +LL | pub fn is_empty(&self) -> Option<bool> { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + = note: expected signature: `(&self) -> bool` or `(&self) -> Result<bool> + +error: this returns a `Result<_, ()>` + --> $DIR/len_without_is_empty.rs:230:5 + | +LL | pub fn len(&self) -> Result<usize, ()> { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::result-unit-err` implied by `-D warnings` + = help: use a custom `Error` type instead + +error: this returns a `Result<_, ()>` + --> $DIR/len_without_is_empty.rs:242:5 + | +LL | pub fn len(&self) -> Result<usize, ()> { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: use a custom `Error` type instead + +error: this returns a `Result<_, ()>` + --> $DIR/len_without_is_empty.rs:246:5 + | +LL | pub fn is_empty(&self) -> Result<bool, ()> { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: use a custom `Error` type instead + +error: this returns a `Result<_, ()>` + --> $DIR/len_without_is_empty.rs:253:5 + | +LL | pub fn len(&self) -> Result<usize, ()> { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: use a custom `Error` type instead + +error: aborting due to 12 previous errors diff --git a/src/tools/clippy/tests/ui/let_if_seq.rs b/src/tools/clippy/tests/ui/let_if_seq.rs index 32a67f181df43..2d8f3c2f0e7aa 100644 --- a/src/tools/clippy/tests/ui/let_if_seq.rs +++ b/src/tools/clippy/tests/ui/let_if_seq.rs @@ -2,7 +2,8 @@ unused_variables, unused_assignments, clippy::similar_names, - clippy::blacklisted_name + clippy::blacklisted_name, + clippy::branches_sharing_code )] #![warn(clippy::useless_let_if_seq)] diff --git a/src/tools/clippy/tests/ui/let_if_seq.stderr b/src/tools/clippy/tests/ui/let_if_seq.stderr index 7de560c73486b..9cf2e10a5ee56 100644 --- a/src/tools/clippy/tests/ui/let_if_seq.stderr +++ b/src/tools/clippy/tests/ui/let_if_seq.stderr @@ -1,5 +1,5 @@ error: `if _ { .. } else { .. }` is an expression - --> $DIR/let_if_seq.rs:64:5 + --> $DIR/let_if_seq.rs:65:5 | LL | / let mut foo = 0; LL | | if f() { @@ -11,7 +11,7 @@ LL | | } = note: you might not need `mut` at all error: `if _ { .. } else { .. }` is an expression - --> $DIR/let_if_seq.rs:69:5 + --> $DIR/let_if_seq.rs:70:5 | LL | / let mut bar = 0; LL | | if f() { @@ -25,7 +25,7 @@ LL | | } = note: you might not need `mut` at all error: `if _ { .. } else { .. }` is an expression - --> $DIR/let_if_seq.rs:77:5 + --> $DIR/let_if_seq.rs:78:5 | LL | / let quz; LL | | if f() { @@ -36,7 +36,7 @@ LL | | } | |_____^ help: it is more idiomatic to write: `let quz = if f() { 42 } else { 0 };` error: `if _ { .. } else { .. }` is an expression - --> $DIR/let_if_seq.rs:106:5 + --> $DIR/let_if_seq.rs:107:5 | LL | / let mut baz = 0; LL | | if f() { diff --git a/src/tools/clippy/tests/ui/linkedlist.rs b/src/tools/clippy/tests/ui/linkedlist.rs new file mode 100644 index 0000000000000..2c3b25cd45e80 --- /dev/null +++ b/src/tools/clippy/tests/ui/linkedlist.rs @@ -0,0 +1,43 @@ +#![feature(associated_type_defaults)] +#![warn(clippy::linkedlist)] +#![allow(dead_code, clippy::needless_pass_by_value)] + +extern crate alloc; +use alloc::collections::linked_list::LinkedList; + +const C: LinkedList<i32> = LinkedList::new(); +static S: LinkedList<i32> = LinkedList::new(); + +trait Foo { + type Baz = LinkedList<u8>; + fn foo(_: LinkedList<u8>); + const BAR: Option<LinkedList<u8>>; +} + +// Ok, we don’t want to warn for implementations; see issue #605. +impl Foo for LinkedList<u8> { + fn foo(_: LinkedList<u8>) {} + const BAR: Option<LinkedList<u8>> = None; +} + +struct Bar; +impl Bar { + fn foo(_: LinkedList<u8>) {} +} + +pub fn test(my_favourite_linked_list: LinkedList<u8>) { + println!("{:?}", my_favourite_linked_list) +} + +pub fn test_ret() -> Option<LinkedList<u8>> { + unimplemented!(); +} + +pub fn test_local_not_linted() { + let _: LinkedList<u8>; +} + +fn main() { + test(LinkedList::new()); + test_local_not_linted(); +} diff --git a/src/tools/clippy/tests/ui/linkedlist.stderr b/src/tools/clippy/tests/ui/linkedlist.stderr new file mode 100644 index 0000000000000..38ae71714d662 --- /dev/null +++ b/src/tools/clippy/tests/ui/linkedlist.stderr @@ -0,0 +1,67 @@ +error: you seem to be using a `LinkedList`! Perhaps you meant some other data structure? + --> $DIR/linkedlist.rs:8:10 + | +LL | const C: LinkedList<i32> = LinkedList::new(); + | ^^^^^^^^^^^^^^^ + | + = note: `-D clippy::linkedlist` implied by `-D warnings` + = help: a `VecDeque` might work + +error: you seem to be using a `LinkedList`! Perhaps you meant some other data structure? + --> $DIR/linkedlist.rs:9:11 + | +LL | static S: LinkedList<i32> = LinkedList::new(); + | ^^^^^^^^^^^^^^^ + | + = help: a `VecDeque` might work + +error: you seem to be using a `LinkedList`! Perhaps you meant some other data structure? + --> $DIR/linkedlist.rs:12:16 + | +LL | type Baz = LinkedList<u8>; + | ^^^^^^^^^^^^^^ + | + = help: a `VecDeque` might work + +error: you seem to be using a `LinkedList`! Perhaps you meant some other data structure? + --> $DIR/linkedlist.rs:13:15 + | +LL | fn foo(_: LinkedList<u8>); + | ^^^^^^^^^^^^^^ + | + = help: a `VecDeque` might work + +error: you seem to be using a `LinkedList`! Perhaps you meant some other data structure? + --> $DIR/linkedlist.rs:14:23 + | +LL | const BAR: Option<LinkedList<u8>>; + | ^^^^^^^^^^^^^^ + | + = help: a `VecDeque` might work + +error: you seem to be using a `LinkedList`! Perhaps you meant some other data structure? + --> $DIR/linkedlist.rs:25:15 + | +LL | fn foo(_: LinkedList<u8>) {} + | ^^^^^^^^^^^^^^ + | + = help: a `VecDeque` might work + +error: you seem to be using a `LinkedList`! Perhaps you meant some other data structure? + --> $DIR/linkedlist.rs:28:39 + | +LL | pub fn test(my_favourite_linked_list: LinkedList<u8>) { + | ^^^^^^^^^^^^^^ + | + = help: a `VecDeque` might work + +error: you seem to be using a `LinkedList`! Perhaps you meant some other data structure? + --> $DIR/linkedlist.rs:32:29 + | +LL | pub fn test_ret() -> Option<LinkedList<u8>> { + | ^^^^^^^^^^^^^^ + | + = help: a `VecDeque` might work + +error: aborting due to 8 previous errors + diff --git a/src/tools/clippy/tests/ui/manual_flatten.rs b/src/tools/clippy/tests/ui/manual_flatten.rs index cff68eca93374..b5bd35a68785a 100644 --- a/src/tools/clippy/tests/ui/manual_flatten.rs +++ b/src/tools/clippy/tests/ui/manual_flatten.rs @@ -1,4 +1,5 @@ #![warn(clippy::manual_flatten)] +#![allow(clippy::useless_vec)] fn main() { // Test for loop over implicitly adjusted `Iterator` with `if let` expression @@ -69,6 +70,27 @@ fn main() { } } + let vec_of_ref = vec![&Some(1)]; + for n in &vec_of_ref { + if let Some(n) = n { + println!("{:?}", n); + } + } + + let vec_of_ref = &vec_of_ref; + for n in vec_of_ref { + if let Some(n) = n { + println!("{:?}", n); + } + } + + let slice_of_ref = &[&Some(1)]; + for n in slice_of_ref { + if let Some(n) = n { + println!("{:?}", n); + } + } + // Using manual flatten should not trigger the lint for n in vec![Some(1), Some(2), Some(3)].iter().flatten() { println!("{}", n); diff --git a/src/tools/clippy/tests/ui/manual_flatten.stderr b/src/tools/clippy/tests/ui/manual_flatten.stderr index 855dd9130e2fb..be5f8a1d81884 100644 --- a/src/tools/clippy/tests/ui/manual_flatten.stderr +++ b/src/tools/clippy/tests/ui/manual_flatten.stderr @@ -1,5 +1,5 @@ error: unnecessary `if let` since only the `Some` variant of the iterator element is used - --> $DIR/manual_flatten.rs:6:5 + --> $DIR/manual_flatten.rs:7:5 | LL | for n in x { | ^ - help: try: `x.into_iter().flatten()` @@ -13,7 +13,7 @@ LL | | } | = note: `-D clippy::manual-flatten` implied by `-D warnings` help: ...and remove the `if let` statement in the for loop - --> $DIR/manual_flatten.rs:7:9 + --> $DIR/manual_flatten.rs:8:9 | LL | / if let Some(y) = n { LL | | println!("{}", y); @@ -21,7 +21,7 @@ LL | | } | |_________^ error: unnecessary `if let` since only the `Ok` variant of the iterator element is used - --> $DIR/manual_flatten.rs:14:5 + --> $DIR/manual_flatten.rs:15:5 | LL | for n in y.clone() { | ^ --------- help: try: `y.clone().into_iter().flatten()` @@ -34,7 +34,7 @@ LL | | } | |_____^ | help: ...and remove the `if let` statement in the for loop - --> $DIR/manual_flatten.rs:15:9 + --> $DIR/manual_flatten.rs:16:9 | LL | / if let Ok(n) = n { LL | | println!("{}", n); @@ -42,7 +42,7 @@ LL | | }; | |_________^ error: unnecessary `if let` since only the `Ok` variant of the iterator element is used - --> $DIR/manual_flatten.rs:21:5 + --> $DIR/manual_flatten.rs:22:5 | LL | for n in &y { | ^ -- help: try: `y.iter().flatten()` @@ -55,7 +55,7 @@ LL | | } | |_____^ | help: ...and remove the `if let` statement in the for loop - --> $DIR/manual_flatten.rs:22:9 + --> $DIR/manual_flatten.rs:23:9 | LL | / if let Ok(n) = n { LL | | println!("{}", n); @@ -63,7 +63,7 @@ LL | | } | |_________^ error: unnecessary `if let` since only the `Ok` variant of the iterator element is used - --> $DIR/manual_flatten.rs:31:5 + --> $DIR/manual_flatten.rs:32:5 | LL | for n in z { | ^ - help: try: `z.into_iter().flatten()` @@ -76,7 +76,7 @@ LL | | } | |_____^ | help: ...and remove the `if let` statement in the for loop - --> $DIR/manual_flatten.rs:32:9 + --> $DIR/manual_flatten.rs:33:9 | LL | / if let Ok(n) = n { LL | | println!("{}", n); @@ -84,7 +84,7 @@ LL | | } | |_________^ error: unnecessary `if let` since only the `Some` variant of the iterator element is used - --> $DIR/manual_flatten.rs:40:5 + --> $DIR/manual_flatten.rs:41:5 | LL | for n in z { | ^ - help: try: `z.flatten()` @@ -97,12 +97,75 @@ LL | | } | |_____^ | help: ...and remove the `if let` statement in the for loop - --> $DIR/manual_flatten.rs:41:9 + --> $DIR/manual_flatten.rs:42:9 | LL | / if let Some(m) = n { LL | | println!("{}", m); LL | | } | |_________^ -error: aborting due to 5 previous errors +error: unnecessary `if let` since only the `Some` variant of the iterator element is used + --> $DIR/manual_flatten.rs:74:5 + | +LL | for n in &vec_of_ref { + | ^ ----------- help: try: `vec_of_ref.iter().copied().flatten()` + | _____| + | | +LL | | if let Some(n) = n { +LL | | println!("{:?}", n); +LL | | } +LL | | } + | |_____^ + | +help: ...and remove the `if let` statement in the for loop + --> $DIR/manual_flatten.rs:75:9 + | +LL | / if let Some(n) = n { +LL | | println!("{:?}", n); +LL | | } + | |_________^ + +error: unnecessary `if let` since only the `Some` variant of the iterator element is used + --> $DIR/manual_flatten.rs:81:5 + | +LL | for n in vec_of_ref { + | ^ ---------- help: try: `vec_of_ref.into_iter().copied().flatten()` + | _____| + | | +LL | | if let Some(n) = n { +LL | | println!("{:?}", n); +LL | | } +LL | | } + | |_____^ + | +help: ...and remove the `if let` statement in the for loop + --> $DIR/manual_flatten.rs:82:9 + | +LL | / if let Some(n) = n { +LL | | println!("{:?}", n); +LL | | } + | |_________^ + +error: unnecessary `if let` since only the `Some` variant of the iterator element is used + --> $DIR/manual_flatten.rs:88:5 + | +LL | for n in slice_of_ref { + | ^ ------------ help: try: `slice_of_ref.into_iter().copied().flatten()` + | _____| + | | +LL | | if let Some(n) = n { +LL | | println!("{:?}", n); +LL | | } +LL | | } + | |_____^ + | +help: ...and remove the `if let` statement in the for loop + --> $DIR/manual_flatten.rs:89:9 + | +LL | / if let Some(n) = n { +LL | | println!("{:?}", n); +LL | | } + | |_________^ + +error: aborting due to 8 previous errors diff --git a/src/tools/clippy/tests/ui/manual_map_option.fixed b/src/tools/clippy/tests/ui/manual_map_option.fixed index 1935090675825..ee01584577786 100644 --- a/src/tools/clippy/tests/ui/manual_map_option.fixed +++ b/src/tools/clippy/tests/ui/manual_map_option.fixed @@ -1,7 +1,15 @@ +// edition:2018 // run-rustfix #![warn(clippy::manual_map)] -#![allow(clippy::no_effect, clippy::map_identity, clippy::unit_arg, clippy::match_ref_pats)] +#![allow( + clippy::no_effect, + clippy::map_identity, + clippy::unit_arg, + clippy::match_ref_pats, + clippy::redundant_pattern_matching, + dead_code +)] fn main() { Some(0).map(|_| 2); @@ -67,4 +75,75 @@ fn main() { Some(Some((x, 1))) => Some(x), _ => None, }; + + // #6795 + fn f1() -> Result<(), ()> { + let _ = match Some(Ok(())) { + Some(x) => Some(x?), + None => None, + }; + Ok(()) + } + + for &x in Some(Some(true)).iter() { + let _ = match x { + Some(x) => Some(if x { continue } else { x }), + None => None, + }; + } + + // #6797 + let x1 = (Some(String::new()), 0); + let x2 = x1.0; + match x2 { + Some(x) => Some((x, x1.1)), + None => None, + }; + + struct S1 { + x: Option<String>, + y: u32, + } + impl S1 { + fn f(self) -> Option<(String, u32)> { + match self.x { + Some(x) => Some((x, self.y)), + None => None, + } + } + } + + // #6811 + Some(0).map(|x| vec![x]); + + option_env!("").map(String::from); + + // #6819 + async fn f2(x: u32) -> u32 { + x + } + + async fn f3() { + match Some(0) { + Some(x) => Some(f2(x).await), + None => None, + }; + } + + // #6847 + if let Some(_) = Some(0) { + Some(0) + } else { Some(0).map(|x| x + 1) }; + + if true { + Some(0) + } else { Some(0).map(|x| x + 1) }; + + // #6967 + const fn f4() { + match Some(0) { + Some(x) => Some(x + 1), + None => None, + }; + } } diff --git a/src/tools/clippy/tests/ui/manual_map_option.rs b/src/tools/clippy/tests/ui/manual_map_option.rs index 8b8187db0a979..29509bddfd94d 100644 --- a/src/tools/clippy/tests/ui/manual_map_option.rs +++ b/src/tools/clippy/tests/ui/manual_map_option.rs @@ -1,7 +1,15 @@ +// edition:2018 // run-rustfix #![warn(clippy::manual_map)] -#![allow(clippy::no_effect, clippy::map_identity, clippy::unit_arg, clippy::match_ref_pats)] +#![allow( + clippy::no_effect, + clippy::map_identity, + clippy::unit_arg, + clippy::match_ref_pats, + clippy::redundant_pattern_matching, + dead_code +)] fn main() { match Some(0) { @@ -119,4 +127,89 @@ fn main() { Some(Some((x, 1))) => Some(x), _ => None, }; + + // #6795 + fn f1() -> Result<(), ()> { + let _ = match Some(Ok(())) { + Some(x) => Some(x?), + None => None, + }; + Ok(()) + } + + for &x in Some(Some(true)).iter() { + let _ = match x { + Some(x) => Some(if x { continue } else { x }), + None => None, + }; + } + + // #6797 + let x1 = (Some(String::new()), 0); + let x2 = x1.0; + match x2 { + Some(x) => Some((x, x1.1)), + None => None, + }; + + struct S1 { + x: Option<String>, + y: u32, + } + impl S1 { + fn f(self) -> Option<(String, u32)> { + match self.x { + Some(x) => Some((x, self.y)), + None => None, + } + } + } + + // #6811 + match Some(0) { + Some(x) => Some(vec![x]), + None => None, + }; + + match option_env!("") { + Some(x) => Some(String::from(x)), + None => None, + }; + + // #6819 + async fn f2(x: u32) -> u32 { + x + } + + async fn f3() { + match Some(0) { + Some(x) => Some(f2(x).await), + None => None, + }; + } + + // #6847 + if let Some(_) = Some(0) { + Some(0) + } else if let Some(x) = Some(0) { + Some(x + 1) + } else { + None + }; + + if true { + Some(0) + } else if let Some(x) = Some(0) { + Some(x + 1) + } else { + None + }; + + // #6967 + const fn f4() { + match Some(0) { + Some(x) => Some(x + 1), + None => None, + }; + } } diff --git a/src/tools/clippy/tests/ui/manual_map_option.stderr b/src/tools/clippy/tests/ui/manual_map_option.stderr index 210a30d05d40f..cdc2c0e62a9b9 100644 --- a/src/tools/clippy/tests/ui/manual_map_option.stderr +++ b/src/tools/clippy/tests/ui/manual_map_option.stderr @@ -1,5 +1,5 @@ error: manual implementation of `Option::map` - --> $DIR/manual_map_option.rs:7:5 + --> $DIR/manual_map_option.rs:15:5 | LL | / match Some(0) { LL | | Some(_) => Some(2), @@ -10,7 +10,7 @@ LL | | }; = note: `-D clippy::manual-map` implied by `-D warnings` error: manual implementation of `Option::map` - --> $DIR/manual_map_option.rs:12:5 + --> $DIR/manual_map_option.rs:20:5 | LL | / match Some(0) { LL | | Some(x) => Some(x + 1), @@ -19,7 +19,7 @@ LL | | }; | |_____^ help: try this: `Some(0).map(|x| x + 1)` error: manual implementation of `Option::map` - --> $DIR/manual_map_option.rs:17:5 + --> $DIR/manual_map_option.rs:25:5 | LL | / match Some("") { LL | | Some(x) => Some(x.is_empty()), @@ -28,7 +28,7 @@ LL | | }; | |_____^ help: try this: `Some("").map(|x| x.is_empty())` error: manual implementation of `Option::map` - --> $DIR/manual_map_option.rs:22:5 + --> $DIR/manual_map_option.rs:30:5 | LL | / if let Some(x) = Some(0) { LL | | Some(!x) @@ -38,7 +38,7 @@ LL | | }; | |_____^ help: try this: `Some(0).map(|x| !x)` error: manual implementation of `Option::map` - --> $DIR/manual_map_option.rs:29:5 + --> $DIR/manual_map_option.rs:37:5 | LL | / match Some(0) { LL | | Some(x) => { Some(std::convert::identity(x)) } @@ -47,7 +47,7 @@ LL | | }; | |_____^ help: try this: `Some(0).map(std::convert::identity)` error: manual implementation of `Option::map` - --> $DIR/manual_map_option.rs:34:5 + --> $DIR/manual_map_option.rs:42:5 | LL | / match Some(&String::new()) { LL | | Some(x) => Some(str::len(x)), @@ -56,7 +56,7 @@ LL | | }; | |_____^ help: try this: `Some(&String::new()).map(|x| str::len(x))` error: manual implementation of `Option::map` - --> $DIR/manual_map_option.rs:44:5 + --> $DIR/manual_map_option.rs:52:5 | LL | / match &Some([0, 1]) { LL | | Some(x) => Some(x[0]), @@ -65,7 +65,7 @@ LL | | }; | |_____^ help: try this: `Some([0, 1]).as_ref().map(|x| x[0])` error: manual implementation of `Option::map` - --> $DIR/manual_map_option.rs:49:5 + --> $DIR/manual_map_option.rs:57:5 | LL | / match &Some(0) { LL | | &Some(x) => Some(x * 2), @@ -74,7 +74,7 @@ LL | | }; | |_____^ help: try this: `Some(0).map(|x| x * 2)` error: manual implementation of `Option::map` - --> $DIR/manual_map_option.rs:54:5 + --> $DIR/manual_map_option.rs:62:5 | LL | / match Some(String::new()) { LL | | Some(ref x) => Some(x.is_empty()), @@ -83,7 +83,7 @@ LL | | }; | |_____^ help: try this: `Some(String::new()).as_ref().map(|x| x.is_empty())` error: manual implementation of `Option::map` - --> $DIR/manual_map_option.rs:59:5 + --> $DIR/manual_map_option.rs:67:5 | LL | / match &&Some(String::new()) { LL | | Some(x) => Some(x.len()), @@ -92,7 +92,7 @@ LL | | }; | |_____^ help: try this: `Some(String::new()).as_ref().map(|x| x.len())` error: manual implementation of `Option::map` - --> $DIR/manual_map_option.rs:64:5 + --> $DIR/manual_map_option.rs:72:5 | LL | / match &&Some(0) { LL | | &&Some(x) => Some(x + x), @@ -101,7 +101,7 @@ LL | | }; | |_____^ help: try this: `Some(0).map(|x| x + x)` error: manual implementation of `Option::map` - --> $DIR/manual_map_option.rs:77:9 + --> $DIR/manual_map_option.rs:85:9 | LL | / match &mut Some(String::new()) { LL | | Some(x) => Some(x.push_str("")), @@ -110,7 +110,7 @@ LL | | }; | |_________^ help: try this: `Some(String::new()).as_mut().map(|x| x.push_str(""))` error: manual implementation of `Option::map` - --> $DIR/manual_map_option.rs:83:5 + --> $DIR/manual_map_option.rs:91:5 | LL | / match &mut Some(String::new()) { LL | | Some(ref x) => Some(x.len()), @@ -119,7 +119,7 @@ LL | | }; | |_____^ help: try this: `Some(String::new()).as_ref().map(|x| x.len())` error: manual implementation of `Option::map` - --> $DIR/manual_map_option.rs:88:5 + --> $DIR/manual_map_option.rs:96:5 | LL | / match &mut &Some(String::new()) { LL | | Some(x) => Some(x.is_empty()), @@ -128,7 +128,7 @@ LL | | }; | |_____^ help: try this: `Some(String::new()).as_ref().map(|x| x.is_empty())` error: manual implementation of `Option::map` - --> $DIR/manual_map_option.rs:93:5 + --> $DIR/manual_map_option.rs:101:5 | LL | / match Some((0, 1, 2)) { LL | | Some((x, y, z)) => Some(x + y + z), @@ -137,7 +137,7 @@ LL | | }; | |_____^ help: try this: `Some((0, 1, 2)).map(|(x, y, z)| x + y + z)` error: manual implementation of `Option::map` - --> $DIR/manual_map_option.rs:98:5 + --> $DIR/manual_map_option.rs:106:5 | LL | / match Some([1, 2, 3]) { LL | | Some([first, ..]) => Some(first), @@ -146,7 +146,7 @@ LL | | }; | |_____^ help: try this: `Some([1, 2, 3]).map(|[first, ..]| first)` error: manual implementation of `Option::map` - --> $DIR/manual_map_option.rs:103:5 + --> $DIR/manual_map_option.rs:111:5 | LL | / match &Some((String::new(), "test")) { LL | | Some((x, y)) => Some((y, x)), @@ -154,5 +154,45 @@ LL | | None => None, LL | | }; | |_____^ help: try this: `Some((String::new(), "test")).as_ref().map(|(x, y)| (y, x))` -error: aborting due to 17 previous errors +error: manual implementation of `Option::map` + --> $DIR/manual_map_option.rs:169:5 + | +LL | / match Some(0) { +LL | | Some(x) => Some(vec![x]), +LL | | None => None, +LL | | }; + | |_____^ help: try this: `Some(0).map(|x| vec![x])` + +error: manual implementation of `Option::map` + --> $DIR/manual_map_option.rs:174:5 + | +LL | / match option_env!("") { +LL | | Some(x) => Some(String::from(x)), +LL | | None => None, +LL | | }; + | |_____^ help: try this: `option_env!("").map(String::from)` + +error: manual implementation of `Option::map` + --> $DIR/manual_map_option.rs:194:12 + | +LL | } else if let Some(x) = Some(0) { + | ____________^ +LL | | Some(x + 1) +LL | | } else { +LL | | None +LL | | }; + | |_____^ help: try this: `{ Some(0).map(|x| x + 1) }` + +error: manual implementation of `Option::map` + --> $DIR/manual_map_option.rs:202:12 + | +LL | } else if let Some(x) = Some(0) { + | ____________^ +LL | | Some(x + 1) +LL | | } else { +LL | | None +LL | | }; + | |_____^ help: try this: `{ Some(0).map(|x| x + 1) }` + +error: aborting due to 21 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 2547b19f5d1d4..a2f2dfce168d5 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 @@ -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())].clone_from_slice(&src[..((3 + src.len()) - 3)]);` error: it looks like you're manually copying between slices --> $DIR/with_loop_counters.rs:35:5 diff --git a/src/tools/clippy/tests/ui/manual_unwrap_or.fixed b/src/tools/clippy/tests/ui/manual_unwrap_or.fixed index 81d903c15d32a..f1d3252230bc2 100644 --- a/src/tools/clippy/tests/ui/manual_unwrap_or.fixed +++ b/src/tools/clippy/tests/ui/manual_unwrap_or.fixed @@ -136,4 +136,19 @@ fn result_unwrap_or() { }; } +// don't lint in const fn +const fn const_fn_option_unwrap_or() { + match Some(1) { + Some(s) => s, + None => 0, + }; +} + +const fn const_fn_result_unwrap_or() { + match Ok::<&str, &str>("Alice") { + Ok(s) => s, + Err(_) => "Bob", + }; +} + fn main() {} diff --git a/src/tools/clippy/tests/ui/manual_unwrap_or.rs b/src/tools/clippy/tests/ui/manual_unwrap_or.rs index 16105d379c305..c9eee25a5b153 100644 --- a/src/tools/clippy/tests/ui/manual_unwrap_or.rs +++ b/src/tools/clippy/tests/ui/manual_unwrap_or.rs @@ -175,4 +175,19 @@ fn result_unwrap_or() { }; } +// don't lint in const fn +const fn const_fn_option_unwrap_or() { + match Some(1) { + Some(s) => s, + None => 0, + }; +} + +const fn const_fn_result_unwrap_or() { + match Ok::<&str, &str>("Alice") { + Ok(s) => s, + Err(_) => "Bob", + }; +} + fn main() {} diff --git a/src/tools/clippy/tests/ui/match_ref_pats.stderr b/src/tools/clippy/tests/ui/match_ref_pats.stderr index 52cb4a14b72bc..67474e65cde4f 100644 --- a/src/tools/clippy/tests/ui/match_ref_pats.stderr +++ b/src/tools/clippy/tests/ui/match_ref_pats.stderr @@ -46,6 +46,14 @@ LL | Some(v) => println!("{:?}", v), LL | None => println!("none"), | +error: redundant pattern matching, consider using `is_none()` + --> $DIR/match_ref_pats.rs:35:12 + | +LL | if let &None = a { + | -------^^^^^---- help: try this: `if a.is_none()` + | + = note: `-D clippy::redundant-pattern-matching` implied by `-D warnings` + error: you don't need to add `&` to all patterns --> $DIR/match_ref_pats.rs:35:5 | @@ -59,6 +67,12 @@ help: instead of prefixing all patterns with `&`, you can dereference the expres LL | if let None = *a { | ^^^^ ^^ +error: redundant pattern matching, consider using `is_none()` + --> $DIR/match_ref_pats.rs:40:12 + | +LL | if let &None = &b { + | -------^^^^^----- help: try this: `if b.is_none()` + error: you don't need to add `&` to both the expression and the patterns --> $DIR/match_ref_pats.rs:40:5 | @@ -87,5 +101,5 @@ LL | match *foo_variant!(0) { LL | Foo::A => println!("A"), | -error: aborting due to 6 previous errors +error: aborting due to 8 previous errors diff --git a/src/tools/clippy/tests/ui/match_same_arms2.rs b/src/tools/clippy/tests/ui/match_same_arms2.rs index 06d91497242e1..da4e3020d5b83 100644 --- a/src/tools/clippy/tests/ui/match_same_arms2.rs +++ b/src/tools/clippy/tests/ui/match_same_arms2.rs @@ -120,6 +120,35 @@ fn match_same_arms() { }, } + // False positive #1390 + macro_rules! empty { + ($e:expr) => {}; + } + match 0 { + 0 => { + empty!(0); + }, + 1 => { + empty!(1); + }, + x => { + empty!(x); + }, + }; + + // still lint if the tokens are the same + match 0 { + 0 => { + empty!(0); + }, + 1 => { + empty!(0); + }, + x => { + empty!(x); + }, + } + match_expr_like_matches_macro_priority(); } diff --git a/src/tools/clippy/tests/ui/match_same_arms2.stderr b/src/tools/clippy/tests/ui/match_same_arms2.stderr index fccaf805616b4..95f9494cdc99e 100644 --- a/src/tools/clippy/tests/ui/match_same_arms2.stderr +++ b/src/tools/clippy/tests/ui/match_same_arms2.stderr @@ -141,8 +141,31 @@ LL | Ok(3) => println!("ok"), | ^^^^^ = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) +error: this `match` has identical arm bodies + --> $DIR/match_same_arms2.rs:144:14 + | +LL | 1 => { + | ______________^ +LL | | empty!(0); +LL | | }, + | |_________^ + | +note: same as this + --> $DIR/match_same_arms2.rs:141:14 + | +LL | 0 => { + | ______________^ +LL | | empty!(0); +LL | | }, + | |_________^ +help: consider refactoring into `0 | 1` + --> $DIR/match_same_arms2.rs:141:9 + | +LL | 0 => { + | ^ + error: match expression looks like `matches!` macro - --> $DIR/match_same_arms2.rs:133:16 + --> $DIR/match_same_arms2.rs:162:16 | LL | let _ans = match x { | ________________^ @@ -154,5 +177,5 @@ LL | | }; | = note: `-D clippy::match-like-matches-macro` implied by `-D warnings` -error: aborting due to 8 previous errors +error: aborting due to 9 previous errors diff --git a/src/tools/clippy/tests/ui/match_single_binding2.fixed b/src/tools/clippy/tests/ui/match_single_binding2.fixed new file mode 100644 index 0000000000000..e73a85b73d7c6 --- /dev/null +++ b/src/tools/clippy/tests/ui/match_single_binding2.fixed @@ -0,0 +1,37 @@ +// run-rustfix + +#![warn(clippy::match_single_binding)] +#![allow(unused_variables)] + +fn main() { + // Lint (additional curly braces needed, see #6572) + struct AppendIter<I> + where + I: Iterator, + { + inner: Option<(I, <I as Iterator>::Item)>, + } + + #[allow(dead_code)] + fn size_hint<I: Iterator>(iter: &AppendIter<I>) -> (usize, Option<usize>) { + match &iter.inner { + Some((iter, _item)) => { + let (min, max) = iter.size_hint(); + (min.saturating_add(1), max.and_then(|max| max.checked_add(1))) + }, + None => (0, Some(0)), + } + } + + // Lint (no additional curly braces needed) + let opt = Some((5, 2)); + let get_tup = || -> (i32, i32) { (1, 2) }; + match opt { + #[rustfmt::skip] + Some((first, _second)) => { + let (a, b) = get_tup(); + println!("a {:?} and b {:?}", a, b); + }, + None => println!("nothing"), + } +} diff --git a/src/tools/clippy/tests/ui/match_single_binding2.rs b/src/tools/clippy/tests/ui/match_single_binding2.rs new file mode 100644 index 0000000000000..7362cb390e5e8 --- /dev/null +++ b/src/tools/clippy/tests/ui/match_single_binding2.rs @@ -0,0 +1,37 @@ +// run-rustfix + +#![warn(clippy::match_single_binding)] +#![allow(unused_variables)] + +fn main() { + // Lint (additional curly braces needed, see #6572) + struct AppendIter<I> + where + I: Iterator, + { + inner: Option<(I, <I as Iterator>::Item)>, + } + + #[allow(dead_code)] + fn size_hint<I: Iterator>(iter: &AppendIter<I>) -> (usize, Option<usize>) { + match &iter.inner { + Some((iter, _item)) => match iter.size_hint() { + (min, max) => (min.saturating_add(1), max.and_then(|max| max.checked_add(1))), + }, + None => (0, Some(0)), + } + } + + // Lint (no additional curly braces needed) + let opt = Some((5, 2)); + let get_tup = || -> (i32, i32) { (1, 2) }; + match opt { + #[rustfmt::skip] + Some((first, _second)) => { + match get_tup() { + (a, b) => println!("a {:?} and b {:?}", a, b), + } + }, + None => println!("nothing"), + } +} diff --git a/src/tools/clippy/tests/ui/match_single_binding2.stderr b/src/tools/clippy/tests/ui/match_single_binding2.stderr new file mode 100644 index 0000000000000..bc18d191aa3f8 --- /dev/null +++ b/src/tools/clippy/tests/ui/match_single_binding2.stderr @@ -0,0 +1,34 @@ +error: this match could be written as a `let` statement + --> $DIR/match_single_binding2.rs:18:36 + | +LL | Some((iter, _item)) => match iter.size_hint() { + | ____________________________________^ +LL | | (min, max) => (min.saturating_add(1), max.and_then(|max| max.checked_add(1))), +LL | | }, + | |_____________^ + | + = note: `-D clippy::match-single-binding` implied by `-D warnings` +help: consider using `let` statement + | +LL | Some((iter, _item)) => { +LL | let (min, max) = iter.size_hint(); +LL | (min.saturating_add(1), max.and_then(|max| max.checked_add(1))) +LL | }, + | + +error: this match could be written as a `let` statement + --> $DIR/match_single_binding2.rs:31:13 + | +LL | / match get_tup() { +LL | | (a, b) => println!("a {:?} and b {:?}", a, b), +LL | | } + | |_____________^ + | +help: consider using `let` statement + | +LL | let (a, b) = get_tup(); +LL | println!("a {:?} and b {:?}", a, b); + | + +error: aborting due to 2 previous errors + diff --git a/src/tools/clippy/tests/ui/match_wildcard_for_single_variants.fixed b/src/tools/clippy/tests/ui/match_wildcard_for_single_variants.fixed index 519200977a798..31b743f7a18d7 100644 --- a/src/tools/clippy/tests/ui/match_wildcard_for_single_variants.fixed +++ b/src/tools/clippy/tests/ui/match_wildcard_for_single_variants.fixed @@ -15,6 +15,16 @@ enum Color { Blue, Rgb(u8, u8, u8), } +impl Color { + fn f(self) { + match self { + Self::Red => (), + Self::Green => (), + Self::Blue => (), + Self::Rgb(..) => (), + }; + } +} fn main() { let f = Foo::A; @@ -56,4 +66,62 @@ fn main() { Color::Rgb(255, _, _) => {}, _ => {}, } + + // References shouldn't change anything + match &color { + &Color::Red => (), + Color::Green => (), + &Color::Rgb(..) => (), + Color::Blue => (), + } + + use self::Color as C; + + match color { + C::Red => (), + C::Green => (), + C::Rgb(..) => (), + C::Blue => (), + } + + match color { + C::Red => (), + Color::Green => (), + Color::Rgb(..) => (), + Color::Blue => (), + } + + match Some(0) { + Some(0) => 0, + Some(_) => 1, + _ => 2, + }; + + #[non_exhaustive] + enum Bar { + A, + B, + C, + } + match Bar::A { + Bar::A => (), + Bar::B => (), + _ => (), + }; + + //#6984 + { + #![allow(clippy::manual_non_exhaustive)] + pub enum Enum { + A, + B, + #[doc(hidden)] + __Private, + } + match Enum::A { + Enum::A => (), + Enum::B => (), + _ => (), + } + } } diff --git a/src/tools/clippy/tests/ui/match_wildcard_for_single_variants.rs b/src/tools/clippy/tests/ui/match_wildcard_for_single_variants.rs index 1df917e085c71..d19e6ab7eb2e1 100644 --- a/src/tools/clippy/tests/ui/match_wildcard_for_single_variants.rs +++ b/src/tools/clippy/tests/ui/match_wildcard_for_single_variants.rs @@ -15,6 +15,16 @@ enum Color { Blue, Rgb(u8, u8, u8), } +impl Color { + fn f(self) { + match self { + Self::Red => (), + Self::Green => (), + Self::Blue => (), + _ => (), + }; + } +} fn main() { let f = Foo::A; @@ -56,4 +66,62 @@ fn main() { Color::Rgb(255, _, _) => {}, _ => {}, } + + // References shouldn't change anything + match &color { + &Color::Red => (), + Color::Green => (), + &Color::Rgb(..) => (), + &_ => (), + } + + use self::Color as C; + + match color { + C::Red => (), + C::Green => (), + C::Rgb(..) => (), + _ => (), + } + + match color { + C::Red => (), + Color::Green => (), + Color::Rgb(..) => (), + _ => (), + } + + match Some(0) { + Some(0) => 0, + Some(_) => 1, + _ => 2, + }; + + #[non_exhaustive] + enum Bar { + A, + B, + C, + } + match Bar::A { + Bar::A => (), + Bar::B => (), + _ => (), + }; + + //#6984 + { + #![allow(clippy::manual_non_exhaustive)] + pub enum Enum { + A, + B, + #[doc(hidden)] + __Private, + } + match Enum::A { + Enum::A => (), + Enum::B => (), + _ => (), + } + } } diff --git a/src/tools/clippy/tests/ui/match_wildcard_for_single_variants.stderr b/src/tools/clippy/tests/ui/match_wildcard_for_single_variants.stderr index 82790aa9e80bb..34538dea8e5f4 100644 --- a/src/tools/clippy/tests/ui/match_wildcard_for_single_variants.stderr +++ b/src/tools/clippy/tests/ui/match_wildcard_for_single_variants.stderr @@ -1,28 +1,52 @@ -error: wildcard match will miss any future added variants - --> $DIR/match_wildcard_for_single_variants.rs:24:9 +error: wildcard matches only a single variant and will also match any future added variants + --> $DIR/match_wildcard_for_single_variants.rs:24:13 | -LL | _ => {}, - | ^ help: try this: `Foo::C` +LL | _ => (), + | ^ help: try this: `Self::Rgb(..)` | = note: `-D clippy::match-wildcard-for-single-variants` implied by `-D warnings` -error: wildcard match will miss any future added variants +error: wildcard matches only a single variant and will also match any future added variants --> $DIR/match_wildcard_for_single_variants.rs:34:9 | +LL | _ => {}, + | ^ help: try this: `Foo::C` + +error: wildcard matches only a single variant and will also match any future added variants + --> $DIR/match_wildcard_for_single_variants.rs:44:9 + | LL | _ => {}, | ^ help: try this: `Color::Blue` -error: wildcard match will miss any future added variants - --> $DIR/match_wildcard_for_single_variants.rs:42:9 +error: wildcard matches only a single variant and will also match any future added variants + --> $DIR/match_wildcard_for_single_variants.rs:52:9 | LL | _ => {}, | ^ help: try this: `Color::Blue` -error: wildcard match will miss any future added variants - --> $DIR/match_wildcard_for_single_variants.rs:48:9 +error: wildcard matches only a single variant and will also match any future added variants + --> $DIR/match_wildcard_for_single_variants.rs:58:9 | LL | _ => {}, | ^ help: try this: `Color::Blue` -error: aborting due to 4 previous errors +error: wildcard matches only a single variant and will also match any future added variants + --> $DIR/match_wildcard_for_single_variants.rs:75:9 + | +LL | &_ => (), + | ^^ help: try this: `Color::Blue` + +error: wildcard matches only a single variant and will also match any future added variants + --> $DIR/match_wildcard_for_single_variants.rs:84:9 + | +LL | _ => (), + | ^ help: try this: `C::Blue` + +error: wildcard matches only a single variant and will also match any future added variants + --> $DIR/match_wildcard_for_single_variants.rs:91:9 + | +LL | _ => (), + | ^ help: try this: `Color::Blue` + +error: aborting due to 8 previous errors diff --git a/src/tools/clippy/tests/ui/mem_replace.fixed b/src/tools/clippy/tests/ui/mem_replace.fixed index 54e962e7116e8..3b6224254a0a7 100644 --- a/src/tools/clippy/tests/ui/mem_replace.fixed +++ b/src/tools/clippy/tests/ui/mem_replace.fixed @@ -7,6 +7,7 @@ clippy::mem_replace_with_default )] +use std::collections::{BTreeMap, BTreeSet, BinaryHeap, HashMap, HashSet, LinkedList, VecDeque}; use std::mem; fn replace_option_with_none() { @@ -19,9 +20,37 @@ fn replace_option_with_none() { fn replace_with_default() { let mut s = String::from("foo"); let _ = std::mem::take(&mut s); + let s = &mut String::from("foo"); let _ = std::mem::take(s); let _ = std::mem::take(s); + + let mut v = vec![123]; + let _ = std::mem::take(&mut v); + let _ = std::mem::take(&mut v); + let _ = std::mem::take(&mut v); + let _ = std::mem::take(&mut v); + + let mut hash_map: HashMap<i32, i32> = HashMap::new(); + let _ = std::mem::take(&mut hash_map); + + let mut btree_map: BTreeMap<i32, i32> = BTreeMap::new(); + let _ = std::mem::take(&mut btree_map); + + let mut vd: VecDeque<i32> = VecDeque::new(); + let _ = std::mem::take(&mut vd); + + let mut hash_set: HashSet<&str> = HashSet::new(); + let _ = std::mem::take(&mut hash_set); + + let mut btree_set: BTreeSet<&str> = BTreeSet::new(); + let _ = std::mem::take(&mut btree_set); + + let mut list: LinkedList<i32> = LinkedList::new(); + let _ = std::mem::take(&mut list); + + let mut binary_heap: BinaryHeap<i32> = BinaryHeap::new(); + let _ = std::mem::take(&mut binary_heap); } fn main() { diff --git a/src/tools/clippy/tests/ui/mem_replace.rs b/src/tools/clippy/tests/ui/mem_replace.rs index 60f527810716f..0a36db9e92159 100644 --- a/src/tools/clippy/tests/ui/mem_replace.rs +++ b/src/tools/clippy/tests/ui/mem_replace.rs @@ -7,6 +7,7 @@ clippy::mem_replace_with_default )] +use std::collections::{BTreeMap, BTreeSet, BinaryHeap, HashMap, HashSet, LinkedList, VecDeque}; use std::mem; fn replace_option_with_none() { @@ -19,9 +20,37 @@ fn replace_option_with_none() { fn replace_with_default() { let mut s = String::from("foo"); let _ = std::mem::replace(&mut s, String::default()); + let s = &mut String::from("foo"); let _ = std::mem::replace(s, String::default()); let _ = std::mem::replace(s, Default::default()); + + let mut v = vec![123]; + let _ = std::mem::replace(&mut v, Vec::default()); + let _ = std::mem::replace(&mut v, Default::default()); + let _ = std::mem::replace(&mut v, Vec::new()); + let _ = std::mem::replace(&mut v, vec![]); + + let mut hash_map: HashMap<i32, i32> = HashMap::new(); + let _ = std::mem::replace(&mut hash_map, HashMap::new()); + + let mut btree_map: BTreeMap<i32, i32> = BTreeMap::new(); + let _ = std::mem::replace(&mut btree_map, BTreeMap::new()); + + let mut vd: VecDeque<i32> = VecDeque::new(); + let _ = std::mem::replace(&mut vd, VecDeque::new()); + + let mut hash_set: HashSet<&str> = HashSet::new(); + let _ = std::mem::replace(&mut hash_set, HashSet::new()); + + let mut btree_set: BTreeSet<&str> = BTreeSet::new(); + let _ = std::mem::replace(&mut btree_set, BTreeSet::new()); + + let mut list: LinkedList<i32> = LinkedList::new(); + let _ = std::mem::replace(&mut list, LinkedList::new()); + + let mut binary_heap: BinaryHeap<i32> = BinaryHeap::new(); + let _ = std::mem::replace(&mut binary_heap, BinaryHeap::new()); } fn main() { diff --git a/src/tools/clippy/tests/ui/mem_replace.stderr b/src/tools/clippy/tests/ui/mem_replace.stderr index 245d33aa4f260..f8aa1538bffa8 100644 --- a/src/tools/clippy/tests/ui/mem_replace.stderr +++ b/src/tools/clippy/tests/ui/mem_replace.stderr @@ -1,5 +1,5 @@ error: replacing an `Option` with `None` - --> $DIR/mem_replace.rs:14:13 + --> $DIR/mem_replace.rs:15:13 | LL | let _ = mem::replace(&mut an_option, None); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider `Option::take()` instead: `an_option.take()` @@ -7,13 +7,13 @@ LL | let _ = mem::replace(&mut an_option, None); = note: `-D clippy::mem-replace-option-with-none` implied by `-D warnings` error: replacing an `Option` with `None` - --> $DIR/mem_replace.rs:16:13 + --> $DIR/mem_replace.rs:17:13 | LL | let _ = mem::replace(an_option, None); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider `Option::take()` instead: `an_option.take()` error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take` - --> $DIR/mem_replace.rs:21:13 + --> $DIR/mem_replace.rs:22:13 | LL | let _ = std::mem::replace(&mut s, String::default()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut s)` @@ -21,16 +21,82 @@ LL | let _ = std::mem::replace(&mut s, String::default()); = note: `-D clippy::mem-replace-with-default` implied by `-D warnings` error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take` - --> $DIR/mem_replace.rs:23:13 + --> $DIR/mem_replace.rs:25:13 | LL | let _ = std::mem::replace(s, String::default()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(s)` error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take` - --> $DIR/mem_replace.rs:24:13 + --> $DIR/mem_replace.rs:26:13 | LL | let _ = std::mem::replace(s, Default::default()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(s)` -error: aborting due to 5 previous errors +error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take` + --> $DIR/mem_replace.rs:29:13 + | +LL | let _ = std::mem::replace(&mut v, Vec::default()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut v)` + +error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take` + --> $DIR/mem_replace.rs:30:13 + | +LL | let _ = std::mem::replace(&mut v, Default::default()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut v)` + +error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take` + --> $DIR/mem_replace.rs:31:13 + | +LL | let _ = std::mem::replace(&mut v, Vec::new()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut v)` + +error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take` + --> $DIR/mem_replace.rs:32:13 + | +LL | let _ = std::mem::replace(&mut v, vec![]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut v)` + +error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take` + --> $DIR/mem_replace.rs:35:13 + | +LL | let _ = std::mem::replace(&mut hash_map, HashMap::new()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut hash_map)` + +error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take` + --> $DIR/mem_replace.rs:38:13 + | +LL | let _ = std::mem::replace(&mut btree_map, BTreeMap::new()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut btree_map)` + +error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take` + --> $DIR/mem_replace.rs:41:13 + | +LL | let _ = std::mem::replace(&mut vd, VecDeque::new()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut vd)` + +error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take` + --> $DIR/mem_replace.rs:44:13 + | +LL | let _ = std::mem::replace(&mut hash_set, HashSet::new()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut hash_set)` + +error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take` + --> $DIR/mem_replace.rs:47:13 + | +LL | let _ = std::mem::replace(&mut btree_set, BTreeSet::new()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut btree_set)` + +error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take` + --> $DIR/mem_replace.rs:50:13 + | +LL | let _ = std::mem::replace(&mut list, LinkedList::new()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut list)` + +error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take` + --> $DIR/mem_replace.rs:53:13 + | +LL | let _ = std::mem::replace(&mut binary_heap, BinaryHeap::new()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut binary_heap)` + +error: aborting due to 16 previous errors diff --git a/src/tools/clippy/tests/ui/methods.stderr b/src/tools/clippy/tests/ui/methods.stderr index 33aba630a5304..4643e09e27028 100644 --- a/src/tools/clippy/tests/ui/methods.stderr +++ b/src/tools/clippy/tests/ui/methods.stderr @@ -8,7 +8,7 @@ LL | | } | = note: `-D clippy::new-ret-no-self` implied by `-D warnings` -error: called `filter(..).next()` on an `Iterator`. This is more succinctly expressed by calling `.find(..)` instead. +error: called `filter(..).next()` on an `Iterator`. This is more succinctly expressed by calling `.find(..)` instead --> $DIR/methods.rs:126:13 | LL | let _ = v.iter().filter(|&x| { diff --git a/src/tools/clippy/tests/ui/methods_fixable.stderr b/src/tools/clippy/tests/ui/methods_fixable.stderr index 70e7c3dea545b..852f48e32d678 100644 --- a/src/tools/clippy/tests/ui/methods_fixable.stderr +++ b/src/tools/clippy/tests/ui/methods_fixable.stderr @@ -1,4 +1,4 @@ -error: called `filter(..).next()` on an `Iterator`. This is more succinctly expressed by calling `.find(..)` instead. +error: called `filter(..).next()` on an `Iterator`. This is more succinctly expressed by calling `.find(..)` instead --> $DIR/methods_fixable.rs:10:13 | LL | let _ = v.iter().filter(|&x| *x < 0).next(); diff --git a/src/tools/clippy/tests/ui/min_rust_version_attr.rs b/src/tools/clippy/tests/ui/min_rust_version_attr.rs index 0f47f1cbc4026..49ace1ca128c1 100644 --- a/src/tools/clippy/tests/ui/min_rust_version_attr.rs +++ b/src/tools/clippy/tests/ui/min_rust_version_attr.rs @@ -123,6 +123,11 @@ fn missing_const_for_fn() -> i32 { 1 } +fn unnest_or_patterns() { + struct TS(u8, u8); + if let TS(0, x) | TS(1, x) = TS(0, 0) {} +} + fn main() { filter_map_next(); checked_conversion(); @@ -138,6 +143,7 @@ fn main() { replace_with_default(); map_unwrap_or(); missing_const_for_fn(); + unnest_or_patterns(); } mod meets_msrv { diff --git a/src/tools/clippy/tests/ui/min_rust_version_attr.stderr b/src/tools/clippy/tests/ui/min_rust_version_attr.stderr index e3e3b335cbe16..8d3575c2da83b 100644 --- a/src/tools/clippy/tests/ui/min_rust_version_attr.stderr +++ b/src/tools/clippy/tests/ui/min_rust_version_attr.stderr @@ -1,12 +1,12 @@ error: stripping a prefix manually - --> $DIR/min_rust_version_attr.rs:150:24 + --> $DIR/min_rust_version_attr.rs:156:24 | LL | assert_eq!(s["hello, ".len()..].to_uppercase(), "WORLD!"); | ^^^^^^^^^^^^^^^^^^^^ | = note: `-D clippy::manual-strip` implied by `-D warnings` note: the prefix was tested here - --> $DIR/min_rust_version_attr.rs:149:9 + --> $DIR/min_rust_version_attr.rs:155:9 | LL | if s.starts_with("hello, ") { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -17,13 +17,13 @@ LL | assert_eq!(<stripped>.to_uppercase(), "WORLD!"); | error: stripping a prefix manually - --> $DIR/min_rust_version_attr.rs:162:24 + --> $DIR/min_rust_version_attr.rs:168:24 | LL | assert_eq!(s["hello, ".len()..].to_uppercase(), "WORLD!"); | ^^^^^^^^^^^^^^^^^^^^ | note: the prefix was tested here - --> $DIR/min_rust_version_attr.rs:161:9 + --> $DIR/min_rust_version_attr.rs:167:9 | LL | if s.starts_with("hello, ") { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/src/tools/clippy/tests/ui/mismatched_target_os_unix.stderr b/src/tools/clippy/tests/ui/mismatched_target_os_unix.stderr index fe9aeedb59c45..ea39f5b5577be 100644 --- a/src/tools/clippy/tests/ui/mismatched_target_os_unix.stderr +++ b/src/tools/clippy/tests/ui/mismatched_target_os_unix.stderr @@ -7,7 +7,7 @@ LL | #[cfg(linux)] | help: try: `target_os = "linux"` | = note: `-D clippy::mismatched-target-os` implied by `-D warnings` - = help: Did you mean `unix`? + = help: did you mean `unix`? error: operating system used in target family position --> $DIR/mismatched_target_os_unix.rs:9:1 @@ -17,7 +17,7 @@ LL | #[cfg(freebsd)] | | | help: try: `target_os = "freebsd"` | - = help: Did you mean `unix`? + = help: did you mean `unix`? error: operating system used in target family position --> $DIR/mismatched_target_os_unix.rs:12:1 @@ -27,7 +27,7 @@ LL | #[cfg(dragonfly)] | | | help: try: `target_os = "dragonfly"` | - = help: Did you mean `unix`? + = help: did you mean `unix`? error: operating system used in target family position --> $DIR/mismatched_target_os_unix.rs:15:1 @@ -37,7 +37,7 @@ LL | #[cfg(openbsd)] | | | help: try: `target_os = "openbsd"` | - = help: Did you mean `unix`? + = help: did you mean `unix`? error: operating system used in target family position --> $DIR/mismatched_target_os_unix.rs:18:1 @@ -47,7 +47,7 @@ LL | #[cfg(netbsd)] | | | help: try: `target_os = "netbsd"` | - = help: Did you mean `unix`? + = help: did you mean `unix`? error: operating system used in target family position --> $DIR/mismatched_target_os_unix.rs:21:1 @@ -57,7 +57,7 @@ LL | #[cfg(macos)] | | | help: try: `target_os = "macos"` | - = help: Did you mean `unix`? + = help: did you mean `unix`? error: operating system used in target family position --> $DIR/mismatched_target_os_unix.rs:24:1 @@ -67,7 +67,7 @@ LL | #[cfg(ios)] | | | help: try: `target_os = "ios"` | - = help: Did you mean `unix`? + = help: did you mean `unix`? error: operating system used in target family position --> $DIR/mismatched_target_os_unix.rs:27:1 @@ -77,7 +77,7 @@ LL | #[cfg(android)] | | | help: try: `target_os = "android"` | - = help: Did you mean `unix`? + = help: did you mean `unix`? error: operating system used in target family position --> $DIR/mismatched_target_os_unix.rs:30:1 @@ -87,7 +87,7 @@ LL | #[cfg(emscripten)] | | | help: try: `target_os = "emscripten"` | - = help: Did you mean `unix`? + = help: did you mean `unix`? error: operating system used in target family position --> $DIR/mismatched_target_os_unix.rs:33:1 @@ -97,7 +97,7 @@ LL | #[cfg(fuchsia)] | | | help: try: `target_os = "fuchsia"` | - = help: Did you mean `unix`? + = help: did you mean `unix`? error: operating system used in target family position --> $DIR/mismatched_target_os_unix.rs:36:1 @@ -107,7 +107,7 @@ LL | #[cfg(haiku)] | | | help: try: `target_os = "haiku"` | - = help: Did you mean `unix`? + = help: did you mean `unix`? error: operating system used in target family position --> $DIR/mismatched_target_os_unix.rs:39:1 @@ -117,7 +117,7 @@ LL | #[cfg(illumos)] | | | help: try: `target_os = "illumos"` | - = help: Did you mean `unix`? + = help: did you mean `unix`? error: operating system used in target family position --> $DIR/mismatched_target_os_unix.rs:42:1 @@ -127,7 +127,7 @@ LL | #[cfg(l4re)] | | | help: try: `target_os = "l4re"` | - = help: Did you mean `unix`? + = help: did you mean `unix`? error: operating system used in target family position --> $DIR/mismatched_target_os_unix.rs:45:1 @@ -137,7 +137,7 @@ LL | #[cfg(redox)] | | | help: try: `target_os = "redox"` | - = help: Did you mean `unix`? + = help: did you mean `unix`? error: operating system used in target family position --> $DIR/mismatched_target_os_unix.rs:48:1 @@ -147,7 +147,7 @@ LL | #[cfg(solaris)] | | | help: try: `target_os = "solaris"` | - = help: Did you mean `unix`? + = help: did you mean `unix`? error: operating system used in target family position --> $DIR/mismatched_target_os_unix.rs:51:1 @@ -157,7 +157,7 @@ LL | #[cfg(vxworks)] | | | help: try: `target_os = "vxworks"` | - = help: Did you mean `unix`? + = help: did you mean `unix`? error: operating system used in target family position --> $DIR/mismatched_target_os_unix.rs:55:1 @@ -165,7 +165,7 @@ error: operating system used in target family position LL | #[cfg(all(not(any(solaris, linux)), freebsd))] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = help: Did you mean `unix`? + = help: did you mean `unix`? help: try | LL | #[cfg(all(not(any(target_os = "solaris", linux)), freebsd))] diff --git a/src/tools/clippy/tests/ui/missing_inline_executable.rs b/src/tools/clippy/tests/ui/missing_inline_executable.rs new file mode 100644 index 0000000000000..6e0400ac935bb --- /dev/null +++ b/src/tools/clippy/tests/ui/missing_inline_executable.rs @@ -0,0 +1,5 @@ +#![warn(clippy::missing_inline_in_public_items)] + +pub fn foo() {} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/missing_inline_proc_macro.rs b/src/tools/clippy/tests/ui/missing_inline_proc_macro.rs new file mode 100644 index 0000000000000..3c68fb905f12d --- /dev/null +++ b/src/tools/clippy/tests/ui/missing_inline_proc_macro.rs @@ -0,0 +1,23 @@ +#![warn(clippy::missing_inline_in_public_items)] +#![crate_type = "proc-macro"] + +extern crate proc_macro; + +use proc_macro::TokenStream; + +fn _foo() {} + +#[proc_macro] +pub fn function_like(_: TokenStream) -> TokenStream { + TokenStream::new() +} + +#[proc_macro_attribute] +pub fn attribute(_: TokenStream, _: TokenStream) -> TokenStream { + TokenStream::new() +} + +#[proc_macro_derive(Derive)] +pub fn derive(_: TokenStream) -> TokenStream { + TokenStream::new() +} diff --git a/src/tools/clippy/tests/ui/missing_panics_doc.rs b/src/tools/clippy/tests/ui/missing_panics_doc.rs new file mode 100644 index 0000000000000..2e1379a58a67b --- /dev/null +++ b/src/tools/clippy/tests/ui/missing_panics_doc.rs @@ -0,0 +1,154 @@ +#![warn(clippy::missing_panics_doc)] +#![allow(clippy::option_map_unit_fn)] + +fn main() {} + +/// This needs to be documented +pub fn unwrap() { + let result = Err("Hi"); + result.unwrap() +} + +/// This needs to be documented +pub fn panic() { + panic!("This function panics") +} + +/// This needs to be documented +pub fn todo() { + todo!() +} + +/// This needs to be documented +pub fn inner_body(opt: Option<u32>) { + opt.map(|x| { + if x == 10 { + panic!() + } + }); +} + +/// This needs to be documented +pub fn unreachable_and_panic() { + if true { unreachable!() } else { panic!() } +} + +/// This needs to be documented +pub fn assert_eq() { + let x = 0; + assert_eq!(x, 0); +} + +/// This needs to be documented +pub fn assert_ne() { + let x = 0; + assert_ne!(x, 0); +} + +/// This is documented +/// +/// # Panics +/// +/// Panics if `result` if an error +pub fn unwrap_documented() { + let result = Err("Hi"); + result.unwrap() +} + +/// This is documented +/// +/// # Panics +/// +/// Panics just because +pub fn panic_documented() { + panic!("This function panics") +} + +/// This is documented +/// +/// # Panics +/// +/// Panics if `opt` is Just(10) +pub fn inner_body_documented(opt: Option<u32>) { + opt.map(|x| { + if x == 10 { + panic!() + } + }); +} + +/// This is documented +/// +/// # Panics +/// +/// We still need to do this part +pub fn todo_documented() { + todo!() +} + +/// This is documented +/// +/// # Panics +/// +/// We still need to do this part +pub fn unreachable_amd_panic_documented() { + if true { unreachable!() } else { panic!() } +} + +/// This is documented +/// +/// # Panics +/// +/// Panics if `x` is not 0. +pub fn assert_eq_documented() { + let x = 0; + assert_eq!(x, 0); +} + +/// This is documented +/// +/// # Panics +/// +/// Panics if `x` is 0. +pub fn assert_ne_documented() { + let x = 0; + assert_ne!(x, 0); +} + +/// This is okay because it is private +fn unwrap_private() { + let result = Err("Hi"); + result.unwrap() +} + +/// This is okay because it is private +fn panic_private() { + panic!("This function panics") +} + +/// This is okay because it is private +fn todo_private() { + todo!() +} + +/// This is okay because it is private +fn inner_body_private(opt: Option<u32>) { + opt.map(|x| { + if x == 10 { + panic!() + } + }); +} + +/// This is okay because unreachable +pub fn unreachable() { + unreachable!("This function panics") +} + +/// #6970. +/// This is okay because it is expansion of `debug_assert` family. +pub fn debug_assertions() { + debug_assert!(false); + debug_assert_eq!(1, 2); + debug_assert_ne!(1, 2); +} diff --git a/src/tools/clippy/tests/ui/missing_panics_doc.stderr b/src/tools/clippy/tests/ui/missing_panics_doc.stderr new file mode 100644 index 0000000000000..ba96a6a12b5dc --- /dev/null +++ b/src/tools/clippy/tests/ui/missing_panics_doc.stderr @@ -0,0 +1,114 @@ +error: docs for function which may panic missing `# Panics` section + --> $DIR/missing_panics_doc.rs:7:1 + | +LL | / pub fn unwrap() { +LL | | let result = Err("Hi"); +LL | | result.unwrap() +LL | | } + | |_^ + | + = note: `-D clippy::missing-panics-doc` implied by `-D warnings` +note: first possible panic found here + --> $DIR/missing_panics_doc.rs:9:5 + | +LL | result.unwrap() + | ^^^^^^^^^^^^^^^ + +error: docs for function which may panic missing `# Panics` section + --> $DIR/missing_panics_doc.rs:13:1 + | +LL | / pub fn panic() { +LL | | panic!("This function panics") +LL | | } + | |_^ + | +note: first possible panic found here + --> $DIR/missing_panics_doc.rs:14:5 + | +LL | panic!("This function panics") + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + = note: this error originates in a macro (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:18:1 + | +LL | / pub fn todo() { +LL | | todo!() +LL | | } + | |_^ + | +note: first possible panic found here + --> $DIR/missing_panics_doc.rs:19:5 + | +LL | todo!() + | ^^^^^^^ + = note: this error originates in a macro (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:23:1 + | +LL | / pub fn inner_body(opt: Option<u32>) { +LL | | opt.map(|x| { +LL | | if x == 10 { +LL | | panic!() +LL | | } +LL | | }); +LL | | } + | |_^ + | +note: first possible panic found here + --> $DIR/missing_panics_doc.rs:26:13 + | +LL | panic!() + | ^^^^^^^^ + = note: this error originates in a macro (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:32:1 + | +LL | / pub fn unreachable_and_panic() { +LL | | if true { unreachable!() } else { panic!() } +LL | | } + | |_^ + | +note: first possible panic found here + --> $DIR/missing_panics_doc.rs:33:39 + | +LL | if true { unreachable!() } else { panic!() } + | ^^^^^^^^ + = note: this error originates in a macro (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:37:1 + | +LL | / pub fn assert_eq() { +LL | | let x = 0; +LL | | assert_eq!(x, 0); +LL | | } + | |_^ + | +note: first possible panic found here + --> $DIR/missing_panics_doc.rs:39:5 + | +LL | assert_eq!(x, 0); + | ^^^^^^^^^^^^^^^^^ + = note: this error originates in a macro (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:43:1 + | +LL | / pub fn assert_ne() { +LL | | let x = 0; +LL | | assert_ne!(x, 0); +LL | | } + | |_^ + | +note: first possible panic found here + --> $DIR/missing_panics_doc.rs:45:5 + | +LL | assert_ne!(x, 0); + | ^^^^^^^^^^^^^^^^^ + = note: this error originates in a macro (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/needless_bool/simple.rs b/src/tools/clippy/tests/ui/needless_bool/simple.rs index e9f1428fc3a43..588bb88f44613 100644 --- a/src/tools/clippy/tests/ui/needless_bool/simple.rs +++ b/src/tools/clippy/tests/ui/needless_bool/simple.rs @@ -4,7 +4,8 @@ dead_code, clippy::no_effect, clippy::if_same_then_else, - clippy::needless_return + clippy::needless_return, + clippy::branches_sharing_code )] fn main() { diff --git a/src/tools/clippy/tests/ui/needless_bool/simple.stderr b/src/tools/clippy/tests/ui/needless_bool/simple.stderr index c57a8a042fb88..0ccc9416bcd58 100644 --- a/src/tools/clippy/tests/ui/needless_bool/simple.stderr +++ b/src/tools/clippy/tests/ui/needless_bool/simple.stderr @@ -1,5 +1,5 @@ error: this if-then-else expression will always return true - --> $DIR/simple.rs:13:5 + --> $DIR/simple.rs:14:5 | LL | / if x { LL | | true @@ -11,7 +11,7 @@ LL | | }; = note: `-D clippy::needless-bool` implied by `-D warnings` error: this if-then-else expression will always return false - --> $DIR/simple.rs:18:5 + --> $DIR/simple.rs:19:5 | LL | / if x { LL | | false @@ -21,7 +21,7 @@ LL | | }; | |_____^ error: this if-then-else expression will always return true - --> $DIR/simple.rs:33:5 + --> $DIR/simple.rs:34:5 | LL | / if x { LL | | return true; @@ -31,7 +31,7 @@ LL | | }; | |_____^ error: this if-then-else expression will always return false - --> $DIR/simple.rs:41:5 + --> $DIR/simple.rs:42:5 | LL | / if x { LL | | return false; diff --git a/src/tools/clippy/tests/ui/needless_collect.fixed b/src/tools/clippy/tests/ui/needless_collect.fixed index 7f2fcf02f6b5b..af6c7bf15ea6c 100644 --- a/src/tools/clippy/tests/ui/needless_collect.fixed +++ b/src/tools/clippy/tests/ui/needless_collect.fixed @@ -1,6 +1,6 @@ // run-rustfix -#![allow(unused, clippy::suspicious_map)] +#![allow(unused, clippy::suspicious_map, clippy::iter_count)] use std::collections::{BTreeSet, HashMap, HashSet}; diff --git a/src/tools/clippy/tests/ui/needless_collect.rs b/src/tools/clippy/tests/ui/needless_collect.rs index 788a9eb3264ee..6ae14f370b14b 100644 --- a/src/tools/clippy/tests/ui/needless_collect.rs +++ b/src/tools/clippy/tests/ui/needless_collect.rs @@ -1,6 +1,6 @@ // run-rustfix -#![allow(unused, clippy::suspicious_map)] +#![allow(unused, clippy::suspicious_map, clippy::iter_count)] use std::collections::{BTreeSet, HashMap, HashSet}; diff --git a/src/tools/clippy/tests/ui/needless_collect_indirect.stderr b/src/tools/clippy/tests/ui/needless_collect_indirect.stderr index fb807da5f8abe..c773b841f3b4b 100644 --- a/src/tools/clippy/tests/ui/needless_collect_indirect.stderr +++ b/src/tools/clippy/tests/ui/needless_collect_indirect.stderr @@ -1,64 +1,69 @@ error: avoid using `collect()` when not needed - --> $DIR/needless_collect_indirect.rs:5:5 + --> $DIR/needless_collect_indirect.rs:5:39 | -LL | / let indirect_iter = sample.iter().collect::<Vec<_>>(); -LL | | indirect_iter.into_iter().map(|x| (x, x + 1)).collect::<HashMap<_, _>>(); - | |____^ +LL | let indirect_iter = sample.iter().collect::<Vec<_>>(); + | ^^^^^^^ +LL | indirect_iter.into_iter().map(|x| (x, x + 1)).collect::<HashMap<_, _>>(); + | ------------------------- the iterator could be used here instead | = note: `-D clippy::needless-collect` implied by `-D warnings` -help: Use the original Iterator instead of collecting it and then producing a new one +help: use the original Iterator instead of collecting it and then producing a new one | LL | LL | sample.iter().map(|x| (x, x + 1)).collect::<HashMap<_, _>>(); | error: avoid using `collect()` when not needed - --> $DIR/needless_collect_indirect.rs:7:5 + --> $DIR/needless_collect_indirect.rs:7:38 | -LL | / let indirect_len = sample.iter().collect::<VecDeque<_>>(); -LL | | indirect_len.len(); - | |____^ +LL | let indirect_len = sample.iter().collect::<VecDeque<_>>(); + | ^^^^^^^ +LL | indirect_len.len(); + | ------------------ the iterator could be used here instead | -help: Take the original Iterator's count instead of collecting it and finding the length +help: take the original Iterator's count instead of collecting it and finding the length | LL | LL | sample.iter().count(); | error: avoid using `collect()` when not needed - --> $DIR/needless_collect_indirect.rs:9:5 + --> $DIR/needless_collect_indirect.rs:9:40 | -LL | / let indirect_empty = sample.iter().collect::<VecDeque<_>>(); -LL | | indirect_empty.is_empty(); - | |____^ +LL | let indirect_empty = sample.iter().collect::<VecDeque<_>>(); + | ^^^^^^^ +LL | indirect_empty.is_empty(); + | ------------------------- the iterator could be used here instead | -help: Check if the original Iterator has anything instead of collecting it and seeing if it's empty +help: check if the original Iterator has anything instead of collecting it and seeing if it's empty | LL | LL | sample.iter().next().is_none(); | error: avoid using `collect()` when not needed - --> $DIR/needless_collect_indirect.rs:11:5 + --> $DIR/needless_collect_indirect.rs:11:43 | -LL | / let indirect_contains = sample.iter().collect::<VecDeque<_>>(); -LL | | indirect_contains.contains(&&5); - | |____^ +LL | let indirect_contains = sample.iter().collect::<VecDeque<_>>(); + | ^^^^^^^ +LL | indirect_contains.contains(&&5); + | ------------------------------- the iterator could be used here instead | -help: Check if the original Iterator contains an element instead of collecting then checking +help: check if the original Iterator contains an element instead of collecting then checking | LL | LL | sample.iter().any(|x| x == &5); | error: avoid using `collect()` when not needed - --> $DIR/needless_collect_indirect.rs:23:5 + --> $DIR/needless_collect_indirect.rs:23:48 | -LL | / let non_copy_contains = sample.into_iter().collect::<Vec<_>>(); -LL | | non_copy_contains.contains(&a); - | |____^ +LL | let non_copy_contains = sample.into_iter().collect::<Vec<_>>(); + | ^^^^^^^ +LL | non_copy_contains.contains(&a); + | ------------------------------ the iterator could be used here instead | -help: Check if the original Iterator contains an element instead of collecting then checking +help: check if the original Iterator contains an element instead of collecting then checking | LL | LL | sample.into_iter().any(|x| x == a); diff --git a/src/tools/clippy/tests/ui/needless_for_each_fixable.fixed b/src/tools/clippy/tests/ui/needless_for_each_fixable.fixed new file mode 100644 index 0000000000000..f00f9ee4c331b --- /dev/null +++ b/src/tools/clippy/tests/ui/needless_for_each_fixable.fixed @@ -0,0 +1,113 @@ +// run-rustfix +#![warn(clippy::needless_for_each)] +#![allow(unused, clippy::needless_return, clippy::match_single_binding)] + +use std::collections::HashMap; + +fn should_lint() { + let v: Vec<i32> = Vec::new(); + let mut acc = 0; + for elem in v.iter() { + acc += elem; + } + for elem in v.into_iter() { + acc += elem; + } + + for elem in [1, 2, 3].iter() { + acc += elem; + } + + let mut hash_map: HashMap<i32, i32> = HashMap::new(); + for (k, v) in hash_map.iter() { + acc += k + v; + } + for (k, v) in hash_map.iter_mut() { + acc += *k + *v; + } + for k in hash_map.keys() { + acc += k; + } + for v in hash_map.values() { + acc += v; + } + + fn my_vec() -> Vec<i32> { + Vec::new() + } + for elem in my_vec().iter() { + acc += elem; + } +} + +fn should_not_lint() { + let v: Vec<i32> = Vec::new(); + let mut acc = 0; + + // `for_each` argument is not closure. + fn print(x: &i32) { + println!("{}", x); + } + v.iter().for_each(print); + + // User defined type. + struct MyStruct { + v: Vec<i32>, + } + impl MyStruct { + fn iter(&self) -> impl Iterator<Item = &i32> { + self.v.iter() + } + } + let s = MyStruct { v: Vec::new() }; + s.iter().for_each(|elem| { + acc += elem; + }); + + // `for_each` follows long iterator chain. + v.iter().chain(v.iter()).for_each(|v| { + acc += v; + }); + v.as_slice().iter().for_each(|v| { + acc += v; + }); + s.v.iter().for_each(|v| { + acc += v; + }); + + // `return` is used in `Loop` of the closure. + v.iter().for_each(|v| { + for i in 0..*v { + if i == 10 { + return; + } else { + println!("{}", v); + } + } + if *v == 20 { + return; + } else { + println!("{}", v); + } + }); + + // Previously transformed iterator variable. + let it = v.iter(); + it.chain(v.iter()).for_each(|elem| { + acc += elem; + }); + + // `for_each` is not directly in a statement. + match 1 { + _ => v.iter().for_each(|elem| { + acc += elem; + }), + } + + // `for_each` is in a let bingind. + let _ = v.iter().for_each(|elem| { + acc += elem; + }); +} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/needless_for_each_fixable.rs b/src/tools/clippy/tests/ui/needless_for_each_fixable.rs new file mode 100644 index 0000000000000..1bd400d348ba9 --- /dev/null +++ b/src/tools/clippy/tests/ui/needless_for_each_fixable.rs @@ -0,0 +1,113 @@ +// run-rustfix +#![warn(clippy::needless_for_each)] +#![allow(unused, clippy::needless_return, clippy::match_single_binding)] + +use std::collections::HashMap; + +fn should_lint() { + let v: Vec<i32> = Vec::new(); + let mut acc = 0; + v.iter().for_each(|elem| { + acc += elem; + }); + v.into_iter().for_each(|elem| { + acc += elem; + }); + + [1, 2, 3].iter().for_each(|elem| { + acc += elem; + }); + + let mut hash_map: HashMap<i32, i32> = HashMap::new(); + hash_map.iter().for_each(|(k, v)| { + acc += k + v; + }); + hash_map.iter_mut().for_each(|(k, v)| { + acc += *k + *v; + }); + hash_map.keys().for_each(|k| { + acc += k; + }); + hash_map.values().for_each(|v| { + acc += v; + }); + + fn my_vec() -> Vec<i32> { + Vec::new() + } + my_vec().iter().for_each(|elem| { + acc += elem; + }); +} + +fn should_not_lint() { + let v: Vec<i32> = Vec::new(); + let mut acc = 0; + + // `for_each` argument is not closure. + fn print(x: &i32) { + println!("{}", x); + } + v.iter().for_each(print); + + // User defined type. + struct MyStruct { + v: Vec<i32>, + } + impl MyStruct { + fn iter(&self) -> impl Iterator<Item = &i32> { + self.v.iter() + } + } + let s = MyStruct { v: Vec::new() }; + s.iter().for_each(|elem| { + acc += elem; + }); + + // `for_each` follows long iterator chain. + v.iter().chain(v.iter()).for_each(|v| { + acc += v; + }); + v.as_slice().iter().for_each(|v| { + acc += v; + }); + s.v.iter().for_each(|v| { + acc += v; + }); + + // `return` is used in `Loop` of the closure. + v.iter().for_each(|v| { + for i in 0..*v { + if i == 10 { + return; + } else { + println!("{}", v); + } + } + if *v == 20 { + return; + } else { + println!("{}", v); + } + }); + + // Previously transformed iterator variable. + let it = v.iter(); + it.chain(v.iter()).for_each(|elem| { + acc += elem; + }); + + // `for_each` is not directly in a statement. + match 1 { + _ => v.iter().for_each(|elem| { + acc += elem; + }), + } + + // `for_each` is in a let bingind. + let _ = v.iter().for_each(|elem| { + acc += elem; + }); +} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/needless_for_each_fixable.stderr b/src/tools/clippy/tests/ui/needless_for_each_fixable.stderr new file mode 100644 index 0000000000000..483a5e6d61d72 --- /dev/null +++ b/src/tools/clippy/tests/ui/needless_for_each_fixable.stderr @@ -0,0 +1,123 @@ +error: needless use of `for_each` + --> $DIR/needless_for_each_fixable.rs:10:5 + | +LL | / v.iter().for_each(|elem| { +LL | | acc += elem; +LL | | }); + | |_______^ + | + = note: `-D clippy::needless-for-each` implied by `-D warnings` +help: try + | +LL | for elem in v.iter() { +LL | acc += elem; +LL | } + | + +error: needless use of `for_each` + --> $DIR/needless_for_each_fixable.rs:13:5 + | +LL | / v.into_iter().for_each(|elem| { +LL | | acc += elem; +LL | | }); + | |_______^ + | +help: try + | +LL | for elem in v.into_iter() { +LL | acc += elem; +LL | } + | + +error: needless use of `for_each` + --> $DIR/needless_for_each_fixable.rs:17:5 + | +LL | / [1, 2, 3].iter().for_each(|elem| { +LL | | acc += elem; +LL | | }); + | |_______^ + | +help: try + | +LL | for elem in [1, 2, 3].iter() { +LL | acc += elem; +LL | } + | + +error: needless use of `for_each` + --> $DIR/needless_for_each_fixable.rs:22:5 + | +LL | / hash_map.iter().for_each(|(k, v)| { +LL | | acc += k + v; +LL | | }); + | |_______^ + | +help: try + | +LL | for (k, v) in hash_map.iter() { +LL | acc += k + v; +LL | } + | + +error: needless use of `for_each` + --> $DIR/needless_for_each_fixable.rs:25:5 + | +LL | / hash_map.iter_mut().for_each(|(k, v)| { +LL | | acc += *k + *v; +LL | | }); + | |_______^ + | +help: try + | +LL | for (k, v) in hash_map.iter_mut() { +LL | acc += *k + *v; +LL | } + | + +error: needless use of `for_each` + --> $DIR/needless_for_each_fixable.rs:28:5 + | +LL | / hash_map.keys().for_each(|k| { +LL | | acc += k; +LL | | }); + | |_______^ + | +help: try + | +LL | for k in hash_map.keys() { +LL | acc += k; +LL | } + | + +error: needless use of `for_each` + --> $DIR/needless_for_each_fixable.rs:31:5 + | +LL | / hash_map.values().for_each(|v| { +LL | | acc += v; +LL | | }); + | |_______^ + | +help: try + | +LL | for v in hash_map.values() { +LL | acc += v; +LL | } + | + +error: needless use of `for_each` + --> $DIR/needless_for_each_fixable.rs:38:5 + | +LL | / my_vec().iter().for_each(|elem| { +LL | | acc += elem; +LL | | }); + | |_______^ + | +help: try + | +LL | for elem in my_vec().iter() { +LL | acc += elem; +LL | } + | + +error: aborting due to 8 previous errors + diff --git a/src/tools/clippy/tests/ui/needless_for_each_unfixable.rs b/src/tools/clippy/tests/ui/needless_for_each_unfixable.rs new file mode 100644 index 0000000000000..d765d7dab65c9 --- /dev/null +++ b/src/tools/clippy/tests/ui/needless_for_each_unfixable.rs @@ -0,0 +1,14 @@ +#![warn(clippy::needless_for_each)] +#![allow(clippy::needless_return)] + +fn main() { + let v: Vec<i32> = Vec::new(); + // This is unfixable because the closure includes `return`. + v.iter().for_each(|v| { + if *v == 10 { + return; + } else { + println!("{}", v); + } + }); +} diff --git a/src/tools/clippy/tests/ui/needless_for_each_unfixable.stderr b/src/tools/clippy/tests/ui/needless_for_each_unfixable.stderr new file mode 100644 index 0000000000000..8c4507d23283c --- /dev/null +++ b/src/tools/clippy/tests/ui/needless_for_each_unfixable.stderr @@ -0,0 +1,29 @@ +error: needless use of `for_each` + --> $DIR/needless_for_each_unfixable.rs:7:5 + | +LL | / v.iter().for_each(|v| { +LL | | if *v == 10 { +LL | | return; +LL | | } else { +LL | | println!("{}", v); +LL | | } +LL | | }); + | |_______^ + | + = note: `-D clippy::needless-for-each` implied by `-D warnings` +help: try + | +LL | for v in v.iter() { +LL | if *v == 10 { +LL | return; +LL | } else { +LL | println!("{}", v); +LL | } + ... +help: ...and replace `return` with `continue` + | +LL | continue; + | ^^^^^^^^ + +error: aborting due to previous error + diff --git a/src/tools/clippy/tests/ui/needless_lifetimes.rs b/src/tools/clippy/tests/ui/needless_lifetimes.rs index 44972c8c63964..bda0801e51c7f 100644 --- a/src/tools/clippy/tests/ui/needless_lifetimes.rs +++ b/src/tools/clippy/tests/ui/needless_lifetimes.rs @@ -105,11 +105,7 @@ fn fn_bound_3_cannot_elide() { // No error; multiple input refs. fn fn_bound_4<'a, F: FnOnce() -> &'a ()>(cond: bool, x: &'a (), f: F) -> &'a () { - if cond { - x - } else { - f() - } + if cond { x } else { f() } } struct X { diff --git a/src/tools/clippy/tests/ui/needless_lifetimes.stderr b/src/tools/clippy/tests/ui/needless_lifetimes.stderr index c8a2e8b81c019..33a6de1618d12 100644 --- a/src/tools/clippy/tests/ui/needless_lifetimes.stderr +++ b/src/tools/clippy/tests/ui/needless_lifetimes.stderr @@ -43,109 +43,109 @@ LL | fn fn_bound_2<'a, F, I>(_m: Lt<'a, I>, _f: F) -> Lt<'a, I> | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) - --> $DIR/needless_lifetimes.rs:120:5 + --> $DIR/needless_lifetimes.rs:116:5 | LL | fn self_and_out<'s>(&'s self) -> &'s u8 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) - --> $DIR/needless_lifetimes.rs:129:5 + --> $DIR/needless_lifetimes.rs:125:5 | LL | fn distinct_self_and_in<'s, 't>(&'s self, _x: &'t u8) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) - --> $DIR/needless_lifetimes.rs:148:1 + --> $DIR/needless_lifetimes.rs:144:1 | LL | fn struct_with_lt<'a>(_foo: Foo<'a>) -> &'a str { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) - --> $DIR/needless_lifetimes.rs:178:1 + --> $DIR/needless_lifetimes.rs:174:1 | LL | fn trait_obj_elided2<'a>(_arg: &'a dyn Drop) -> &'a str { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) - --> $DIR/needless_lifetimes.rs:184:1 + --> $DIR/needless_lifetimes.rs:180:1 | LL | fn alias_with_lt<'a>(_foo: FooAlias<'a>) -> &'a str { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) - --> $DIR/needless_lifetimes.rs:203:1 + --> $DIR/needless_lifetimes.rs:199:1 | LL | fn named_input_elided_output<'a>(_arg: &'a str) -> &str { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) - --> $DIR/needless_lifetimes.rs:211:1 + --> $DIR/needless_lifetimes.rs:207:1 | LL | fn trait_bound_ok<'a, T: WithLifetime<'static>>(_: &'a u8, _: T) { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) - --> $DIR/needless_lifetimes.rs:247:1 + --> $DIR/needless_lifetimes.rs:243:1 | LL | fn out_return_type_lts<'a>(e: &'a str) -> Cow<'a> { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) - --> $DIR/needless_lifetimes.rs:254:9 + --> $DIR/needless_lifetimes.rs:250:9 | LL | fn needless_lt<'a>(x: &'a u8) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) - --> $DIR/needless_lifetimes.rs:258:9 + --> $DIR/needless_lifetimes.rs:254:9 | LL | fn needless_lt<'a>(_x: &'a u8) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) - --> $DIR/needless_lifetimes.rs:271:9 + --> $DIR/needless_lifetimes.rs:267:9 | LL | fn baz<'a>(&'a self) -> impl Foo + 'a { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) - --> $DIR/needless_lifetimes.rs:300:5 + --> $DIR/needless_lifetimes.rs:296:5 | LL | fn impl_trait_elidable_nested_named_lifetimes<'a>(i: &'a i32, f: impl for<'b> Fn(&'b i32) -> &'b i32) -> &'a i32 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) - --> $DIR/needless_lifetimes.rs:303:5 + --> $DIR/needless_lifetimes.rs:299:5 | LL | fn impl_trait_elidable_nested_anonymous_lifetimes<'a>(i: &'a i32, f: impl Fn(&i32) -> &i32) -> &'a i32 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) - --> $DIR/needless_lifetimes.rs:312:5 + --> $DIR/needless_lifetimes.rs:308:5 | LL | fn generics_elidable<'a, T: Fn(&i32) -> &i32>(i: &'a i32, f: T) -> &'a i32 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) - --> $DIR/needless_lifetimes.rs:324:5 + --> $DIR/needless_lifetimes.rs:320:5 | LL | fn where_clause_elidadable<'a, T>(i: &'a i32, f: T) -> &'a i32 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) - --> $DIR/needless_lifetimes.rs:339:5 + --> $DIR/needless_lifetimes.rs:335:5 | LL | fn pointer_fn_elidable<'a>(i: &'a i32, f: fn(&i32) -> &i32) -> &'a i32 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) - --> $DIR/needless_lifetimes.rs:352:5 + --> $DIR/needless_lifetimes.rs:348:5 | LL | fn nested_fn_pointer_3<'a>(_: &'a i32) -> fn(fn(&i32) -> &i32) -> i32 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) - --> $DIR/needless_lifetimes.rs:355:5 + --> $DIR/needless_lifetimes.rs:351:5 | LL | fn nested_fn_pointer_4<'a>(_: &'a i32) -> impl Fn(fn(&i32)) { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/src/tools/clippy/tests/ui/needless_question_mark.fixed b/src/tools/clippy/tests/ui/needless_question_mark.fixed index 71fb3565224e4..fd8433870bb1b 100644 --- a/src/tools/clippy/tests/ui/needless_question_mark.fixed +++ b/src/tools/clippy/tests/ui/needless_question_mark.fixed @@ -167,3 +167,28 @@ mod question_mark_both { needless_question_mark_result(); } } + +// #6921 if a macro wraps an expr in Some( ) and the ? is in the macro use, +// the suggestion fails to apply; do not lint +macro_rules! some_in_macro { + ($expr:expr) => { + || -> _ { Some($expr) }() + }; +} + +pub fn test1() { + let x = Some(3); + let _x = some_in_macro!(x?); +} + +// this one is ok because both the ? and the Some are both inside the macro def +macro_rules! some_and_qmark_in_macro { + ($expr:expr) => { + || -> Option<_> { Some($expr) }() + }; +} + +pub fn test2() { + let x = Some(3); + let _x = some_and_qmark_in_macro!(x?); +} diff --git a/src/tools/clippy/tests/ui/needless_question_mark.rs b/src/tools/clippy/tests/ui/needless_question_mark.rs index e31f6f48fa7c7..36d45ac7e03e2 100644 --- a/src/tools/clippy/tests/ui/needless_question_mark.rs +++ b/src/tools/clippy/tests/ui/needless_question_mark.rs @@ -167,3 +167,28 @@ mod question_mark_both { needless_question_mark_result(); } } + +// #6921 if a macro wraps an expr in Some( ) and the ? is in the macro use, +// the suggestion fails to apply; do not lint +macro_rules! some_in_macro { + ($expr:expr) => { + || -> _ { Some($expr) }() + }; +} + +pub fn test1() { + let x = Some(3); + let _x = some_in_macro!(x?); +} + +// this one is ok because both the ? and the Some are both inside the macro def +macro_rules! some_and_qmark_in_macro { + ($expr:expr) => { + || -> Option<_> { Some(Some($expr)?) }() + }; +} + +pub fn test2() { + let x = Some(3); + let _x = some_and_qmark_in_macro!(x?); +} diff --git a/src/tools/clippy/tests/ui/needless_question_mark.stderr b/src/tools/clippy/tests/ui/needless_question_mark.stderr index 567bc518a3fda..7cbf1e505adf6 100644 --- a/src/tools/clippy/tests/ui/needless_question_mark.stderr +++ b/src/tools/clippy/tests/ui/needless_question_mark.stderr @@ -1,4 +1,4 @@ -error: Question mark operator is useless here +error: question mark operator is useless here --> $DIR/needless_question_mark.rs:23:12 | LL | return Some(to.magic?); @@ -6,83 +6,94 @@ LL | return Some(to.magic?); | = note: `-D clippy::needless-question-mark` implied by `-D warnings` -error: Question mark operator is useless here +error: question mark operator is useless here --> $DIR/needless_question_mark.rs:31:12 | LL | return Some(to.magic?) | ^^^^^^^^^^^^^^^ help: try: `to.magic` -error: Question mark operator is useless here +error: question mark operator is useless here --> $DIR/needless_question_mark.rs:36:5 | LL | Some(to.magic?) | ^^^^^^^^^^^^^^^ help: try: `to.magic` -error: Question mark operator is useless here +error: question mark operator is useless here --> $DIR/needless_question_mark.rs:41:21 | LL | to.and_then(|t| Some(t.magic?)) | ^^^^^^^^^^^^^^ help: try: `t.magic` -error: Question mark operator is useless here +error: question mark operator is useless here --> $DIR/needless_question_mark.rs:50:9 | LL | Some(t.magic?) | ^^^^^^^^^^^^^^ help: try: `t.magic` -error: Question mark operator is useless here +error: question mark operator is useless here --> $DIR/needless_question_mark.rs:55:12 | LL | return Ok(tr.magic?); | ^^^^^^^^^^^^^ help: try: `tr.magic` -error: Question mark operator is useless here +error: question mark operator is useless here --> $DIR/needless_question_mark.rs:62:12 | LL | return Ok(tr.magic?) | ^^^^^^^^^^^^^ help: try: `tr.magic` -error: Question mark operator is useless here +error: question mark operator is useless here --> $DIR/needless_question_mark.rs:66:5 | LL | Ok(tr.magic?) | ^^^^^^^^^^^^^ help: try: `tr.magic` -error: Question mark operator is useless here +error: question mark operator is useless here --> $DIR/needless_question_mark.rs:70:21 | LL | tr.and_then(|t| Ok(t.magic?)) | ^^^^^^^^^^^^ help: try: `t.magic` -error: Question mark operator is useless here +error: question mark operator is useless here --> $DIR/needless_question_mark.rs:78:9 | LL | Ok(t.magic?) | ^^^^^^^^^^^^ help: try: `t.magic` -error: Question mark operator is useless here +error: question mark operator is useless here --> $DIR/needless_question_mark.rs:85:16 | LL | return Ok(t.magic?); | ^^^^^^^^^^^^ help: try: `t.magic` -error: Question mark operator is useless here +error: question mark operator is useless here --> $DIR/needless_question_mark.rs:138:9 | LL | Ok(to.magic?) // should be triggered | ^^^^^^^^^^^^^ help: try: `to.magic` -error: Question mark operator is useless here +error: question mark operator is useless here --> $DIR/needless_question_mark.rs:154:9 | LL | Some(to.magic?) // should be triggered | ^^^^^^^^^^^^^^^ help: try: `to.magic` -error: Question mark operator is useless here +error: question mark operator is useless here --> $DIR/needless_question_mark.rs:162:9 | LL | Ok(to.magic?) // should be triggered | ^^^^^^^^^^^^^ help: try: `to.magic` -error: aborting due to 14 previous errors +error: question mark operator is useless here + --> $DIR/needless_question_mark.rs:187:27 + | +LL | || -> Option<_> { Some(Some($expr)?) }() + | ^^^^^^^^^^^^^^^^^^ help: try: `Some($expr)` +... +LL | let _x = some_and_qmark_in_macro!(x?); + | ---------------------------- in this macro invocation + | + = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: aborting due to 15 previous errors diff --git a/src/tools/clippy/tests/ui/needless_range_loop.stderr b/src/tools/clippy/tests/ui/needless_range_loop.stderr index c50c4931fb4cc..c898cd64a9392 100644 --- a/src/tools/clippy/tests/ui/needless_range_loop.stderr +++ b/src/tools/clippy/tests/ui/needless_range_loop.stderr @@ -1,4 +1,4 @@ -error: the loop variable `i` is only used to index `vec`. +error: the loop variable `i` is only used to index `vec` --> $DIR/needless_range_loop.rs:10:14 | LL | for i in 0..vec.len() { @@ -10,7 +10,7 @@ help: consider using an iterator LL | for <item> in &vec { | ^^^^^^ ^^^^ -error: the loop variable `i` is only used to index `vec`. +error: the loop variable `i` is only used to index `vec` --> $DIR/needless_range_loop.rs:19:14 | LL | for i in 0..vec.len() { @@ -21,7 +21,7 @@ help: consider using an iterator LL | for <item> in &vec { | ^^^^^^ ^^^^ -error: the loop variable `j` is only used to index `STATIC`. +error: the loop variable `j` is only used to index `STATIC` --> $DIR/needless_range_loop.rs:24:14 | LL | for j in 0..4 { @@ -32,7 +32,7 @@ help: consider using an iterator LL | for <item> in &STATIC { | ^^^^^^ ^^^^^^^ -error: the loop variable `j` is only used to index `CONST`. +error: the loop variable `j` is only used to index `CONST` --> $DIR/needless_range_loop.rs:28:14 | LL | for j in 0..4 { @@ -54,7 +54,7 @@ help: consider using an iterator LL | for (i, <item>) in vec.iter().enumerate() { | ^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^ -error: the loop variable `i` is only used to index `vec2`. +error: the loop variable `i` is only used to index `vec2` --> $DIR/needless_range_loop.rs:40:14 | LL | for i in 0..vec.len() { @@ -65,7 +65,7 @@ help: consider using an iterator LL | for <item> in vec2.iter().take(vec.len()) { | ^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: the loop variable `i` is only used to index `vec`. +error: the loop variable `i` is only used to index `vec` --> $DIR/needless_range_loop.rs:44:14 | LL | for i in 5..vec.len() { @@ -76,7 +76,7 @@ help: consider using an iterator LL | for <item> in vec.iter().skip(5) { | ^^^^^^ ^^^^^^^^^^^^^^^^^^ -error: the loop variable `i` is only used to index `vec`. +error: the loop variable `i` is only used to index `vec` --> $DIR/needless_range_loop.rs:48:14 | LL | for i in 0..MAX_LEN { @@ -87,7 +87,7 @@ help: consider using an iterator LL | for <item> in vec.iter().take(MAX_LEN) { | ^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^ -error: the loop variable `i` is only used to index `vec`. +error: the loop variable `i` is only used to index `vec` --> $DIR/needless_range_loop.rs:52:14 | LL | for i in 0..=MAX_LEN { @@ -98,7 +98,7 @@ help: consider using an iterator LL | for <item> in vec.iter().take(MAX_LEN + 1) { | ^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: the loop variable `i` is only used to index `vec`. +error: the loop variable `i` is only used to index `vec` --> $DIR/needless_range_loop.rs:56:14 | LL | for i in 5..10 { @@ -109,7 +109,7 @@ help: consider using an iterator LL | for <item> in vec.iter().take(10).skip(5) { | ^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: the loop variable `i` is only used to index `vec`. +error: the loop variable `i` is only used to index `vec` --> $DIR/needless_range_loop.rs:60:14 | LL | for i in 5..=10 { diff --git a/src/tools/clippy/tests/ui/needless_range_loop2.stderr b/src/tools/clippy/tests/ui/needless_range_loop2.stderr index c54ab5ec9809a..2e1f0fd0299b4 100644 --- a/src/tools/clippy/tests/ui/needless_range_loop2.stderr +++ b/src/tools/clippy/tests/ui/needless_range_loop2.stderr @@ -1,4 +1,4 @@ -error: the loop variable `i` is only used to index `ns`. +error: the loop variable `i` is only used to index `ns` --> $DIR/needless_range_loop2.rs:10:14 | LL | for i in 3..10 { @@ -10,7 +10,7 @@ help: consider using an iterator LL | for <item> in ns.iter().take(10).skip(3) { | ^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: the loop variable `i` is only used to index `ms`. +error: the loop variable `i` is only used to index `ms` --> $DIR/needless_range_loop2.rs:31:14 | LL | for i in 0..ms.len() { @@ -21,7 +21,7 @@ help: consider using an iterator LL | for <item> in &mut ms { | ^^^^^^ ^^^^^^^ -error: the loop variable `i` is only used to index `ms`. +error: the loop variable `i` is only used to index `ms` --> $DIR/needless_range_loop2.rs:37:14 | LL | for i in 0..ms.len() { @@ -32,7 +32,7 @@ help: consider using an iterator LL | for <item> in &mut ms { | ^^^^^^ ^^^^^^^ -error: the loop variable `i` is only used to index `vec`. +error: the loop variable `i` is only used to index `vec` --> $DIR/needless_range_loop2.rs:61:14 | LL | for i in x..x + 4 { @@ -43,7 +43,7 @@ help: consider using an iterator LL | for <item> in vec.iter_mut().skip(x).take(4) { | ^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: the loop variable `i` is only used to index `vec`. +error: the loop variable `i` is only used to index `vec` --> $DIR/needless_range_loop2.rs:68:14 | LL | for i in x..=x + 4 { @@ -54,7 +54,7 @@ help: consider using an iterator LL | for <item> in vec.iter_mut().skip(x).take(4 + 1) { | ^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: the loop variable `i` is only used to index `arr`. +error: the loop variable `i` is only used to index `arr` --> $DIR/needless_range_loop2.rs:74:14 | LL | for i in 0..3 { @@ -65,7 +65,7 @@ help: consider using an iterator LL | for <item> in &arr { | ^^^^^^ ^^^^ -error: the loop variable `i` is only used to index `arr`. +error: the loop variable `i` is only used to index `arr` --> $DIR/needless_range_loop2.rs:78:14 | LL | for i in 0..2 { @@ -76,7 +76,7 @@ help: consider using an iterator LL | for <item> in arr.iter().take(2) { | ^^^^^^ ^^^^^^^^^^^^^^^^^^ -error: the loop variable `i` is only used to index `arr`. +error: the loop variable `i` is only used to index `arr` --> $DIR/needless_range_loop2.rs:82:14 | LL | for i in 1..3 { diff --git a/src/tools/clippy/tests/ui/needless_return.fixed b/src/tools/clippy/tests/ui/needless_return.fixed index 990475fcb587e..82d95cc041fb3 100644 --- a/src/tools/clippy/tests/ui/needless_return.fixed +++ b/src/tools/clippy/tests/ui/needless_return.fixed @@ -1,7 +1,12 @@ // run-rustfix -#![allow(unused, clippy::needless_bool)] -#![allow(clippy::if_same_then_else, clippy::single_match)] +#![allow(unused)] +#![allow( + clippy::if_same_then_else, + clippy::single_match, + clippy::branches_sharing_code, + clippy::needless_bool +)] #![warn(clippy::needless_return)] macro_rules! the_answer { diff --git a/src/tools/clippy/tests/ui/needless_return.rs b/src/tools/clippy/tests/ui/needless_return.rs index dec3d84a02046..8a471f802e111 100644 --- a/src/tools/clippy/tests/ui/needless_return.rs +++ b/src/tools/clippy/tests/ui/needless_return.rs @@ -1,7 +1,12 @@ // run-rustfix -#![allow(unused, clippy::needless_bool)] -#![allow(clippy::if_same_then_else, clippy::single_match)] +#![allow(unused)] +#![allow( + clippy::if_same_then_else, + clippy::single_match, + clippy::branches_sharing_code, + clippy::needless_bool +)] #![warn(clippy::needless_return)] macro_rules! the_answer { diff --git a/src/tools/clippy/tests/ui/needless_return.stderr b/src/tools/clippy/tests/ui/needless_return.stderr index ae31d6075416e..075db22456f73 100644 --- a/src/tools/clippy/tests/ui/needless_return.stderr +++ b/src/tools/clippy/tests/ui/needless_return.stderr @@ -1,5 +1,5 @@ error: unneeded `return` statement - --> $DIR/needless_return.rs:18:5 + --> $DIR/needless_return.rs:23:5 | LL | return true; | ^^^^^^^^^^^^ help: remove `return`: `true` @@ -7,103 +7,103 @@ LL | return true; = note: `-D clippy::needless-return` implied by `-D warnings` error: unneeded `return` statement - --> $DIR/needless_return.rs:22:5 + --> $DIR/needless_return.rs:27:5 | LL | return true; | ^^^^^^^^^^^^ help: remove `return`: `true` error: unneeded `return` statement - --> $DIR/needless_return.rs:27:9 + --> $DIR/needless_return.rs:32:9 | LL | return true; | ^^^^^^^^^^^^ help: remove `return`: `true` error: unneeded `return` statement - --> $DIR/needless_return.rs:29:9 + --> $DIR/needless_return.rs:34:9 | LL | return false; | ^^^^^^^^^^^^^ help: remove `return`: `false` error: unneeded `return` statement - --> $DIR/needless_return.rs:35:17 + --> $DIR/needless_return.rs:40:17 | LL | true => return false, | ^^^^^^^^^^^^ help: remove `return`: `false` error: unneeded `return` statement - --> $DIR/needless_return.rs:37:13 + --> $DIR/needless_return.rs:42:13 | LL | return true; | ^^^^^^^^^^^^ help: remove `return`: `true` error: unneeded `return` statement - --> $DIR/needless_return.rs:44:9 + --> $DIR/needless_return.rs:49:9 | LL | return true; | ^^^^^^^^^^^^ help: remove `return`: `true` error: unneeded `return` statement - --> $DIR/needless_return.rs:46:16 + --> $DIR/needless_return.rs:51:16 | LL | let _ = || return true; | ^^^^^^^^^^^ help: remove `return`: `true` error: unneeded `return` statement - --> $DIR/needless_return.rs:54:5 + --> $DIR/needless_return.rs:59:5 | LL | return; | ^^^^^^^ help: remove `return` error: unneeded `return` statement - --> $DIR/needless_return.rs:59:9 + --> $DIR/needless_return.rs:64:9 | LL | return; | ^^^^^^^ help: remove `return` error: unneeded `return` statement - --> $DIR/needless_return.rs:61:9 + --> $DIR/needless_return.rs:66:9 | LL | return; | ^^^^^^^ help: remove `return` error: unneeded `return` statement - --> $DIR/needless_return.rs:68:14 + --> $DIR/needless_return.rs:73:14 | LL | _ => return, | ^^^^^^ help: replace `return` with an empty block: `{}` error: unneeded `return` statement - --> $DIR/needless_return.rs:83:9 + --> $DIR/needless_return.rs:88:9 | LL | return String::from("test"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove `return`: `String::from("test")` error: unneeded `return` statement - --> $DIR/needless_return.rs:85:9 + --> $DIR/needless_return.rs:90:9 | LL | return String::new(); | ^^^^^^^^^^^^^^^^^^^^^ help: remove `return`: `String::new()` error: unneeded `return` statement - --> $DIR/needless_return.rs:106:32 + --> $DIR/needless_return.rs:111:32 | LL | bar.unwrap_or_else(|_| return) | ^^^^^^ help: replace `return` with an empty block: `{}` error: unneeded `return` statement - --> $DIR/needless_return.rs:111:13 + --> $DIR/needless_return.rs:116:13 | LL | return; | ^^^^^^^ help: remove `return` error: unneeded `return` statement - --> $DIR/needless_return.rs:113:20 + --> $DIR/needless_return.rs:118:20 | LL | let _ = || return; | ^^^^^^ help: replace `return` with an empty block: `{}` error: unneeded `return` statement - --> $DIR/needless_return.rs:119:32 + --> $DIR/needless_return.rs:124:32 | LL | res.unwrap_or_else(|_| return Foo) | ^^^^^^^^^^ help: remove `return`: `Foo` diff --git a/src/tools/clippy/tests/ui/new_ret_no_self.rs b/src/tools/clippy/tests/ui/new_ret_no_self.rs index e82873629a54b..2f315ffe2983e 100644 --- a/src/tools/clippy/tests/ui/new_ret_no_self.rs +++ b/src/tools/clippy/tests/ui/new_ret_no_self.rs @@ -340,3 +340,13 @@ mod issue5435 { } } } + +// issue #1724 +struct RetOtherSelf<T>(T); +struct RetOtherSelfWrapper<T>(T); + +impl RetOtherSelf<T> { + fn new(t: T) -> RetOtherSelf<RetOtherSelfWrapper<T>> { + RetOtherSelf(RetOtherSelfWrapper(t)) + } +} diff --git a/src/tools/clippy/tests/ui/new_without_default.rs b/src/tools/clippy/tests/ui/new_without_default.rs index 3b6041823d878..64659b63f4699 100644 --- a/src/tools/clippy/tests/ui/new_without_default.rs +++ b/src/tools/clippy/tests/ui/new_without_default.rs @@ -159,4 +159,19 @@ impl NewNotEqualToDerive { } } +// see #6933 +pub struct FooGenerics<T>(std::marker::PhantomData<T>); +impl<T> FooGenerics<T> { + pub fn new() -> Self { + Self(Default::default()) + } +} + +pub struct BarGenerics<T>(std::marker::PhantomData<T>); +impl<T: Copy> BarGenerics<T> { + pub fn new() -> Self { + Self(Default::default()) + } +} + fn main() {} diff --git a/src/tools/clippy/tests/ui/new_without_default.stderr b/src/tools/clippy/tests/ui/new_without_default.stderr index e529e441eb735..973836f75a905 100644 --- a/src/tools/clippy/tests/ui/new_without_default.stderr +++ b/src/tools/clippy/tests/ui/new_without_default.stderr @@ -43,7 +43,7 @@ LL | | } | help: try this | -LL | impl Default for LtKo<'c> { +LL | impl<'c> Default for LtKo<'c> { LL | fn default() -> Self { LL | Self::new() LL | } @@ -67,5 +67,39 @@ LL | } LL | } | -error: aborting due to 4 previous errors +error: you should consider adding a `Default` implementation for `FooGenerics<T>` + --> $DIR/new_without_default.rs:165:5 + | +LL | / pub fn new() -> Self { +LL | | Self(Default::default()) +LL | | } + | |_____^ + | +help: try this + | +LL | impl<T> Default for FooGenerics<T> { +LL | fn default() -> Self { +LL | Self::new() +LL | } +LL | } + | + +error: you should consider adding a `Default` implementation for `BarGenerics<T>` + --> $DIR/new_without_default.rs:172:5 + | +LL | / pub fn new() -> Self { +LL | | Self(Default::default()) +LL | | } + | |_____^ + | +help: try this + | +LL | impl<T: Copy> Default for BarGenerics<T> { +LL | fn default() -> Self { +LL | Self::new() +LL | } +LL | } + | + +error: aborting due to 6 previous errors diff --git a/src/tools/clippy/tests/ui/non_octal_unix_permissions.fixed b/src/tools/clippy/tests/ui/non_octal_unix_permissions.fixed new file mode 100644 index 0000000000000..a9b2dcfb08569 --- /dev/null +++ b/src/tools/clippy/tests/ui/non_octal_unix_permissions.fixed @@ -0,0 +1,33 @@ +// ignore-windows +// run-rustfix +#![warn(clippy::non_octal_unix_permissions)] +use std::fs::{DirBuilder, File, OpenOptions, Permissions}; +use std::os::unix::fs::{DirBuilderExt, OpenOptionsExt, PermissionsExt}; + +fn main() { + let permissions = 0o760; + + // OpenOptionsExt::mode + let mut options = OpenOptions::new(); + options.mode(0o440); + options.mode(0o400); + options.mode(permissions); + + // PermissionsExt::from_mode + let _permissions = Permissions::from_mode(0o647); + let _permissions = Permissions::from_mode(0o000); + let _permissions = Permissions::from_mode(permissions); + + // PermissionsExt::set_mode + let f = File::create("foo.txt").unwrap(); + let metadata = f.metadata().unwrap(); + let mut permissions = metadata.permissions(); + + permissions.set_mode(0o644); + permissions.set_mode(0o704); + + // DirBuilderExt::mode + let mut builder = DirBuilder::new(); + builder.mode(0o755); + builder.mode(0o406); +} diff --git a/src/tools/clippy/tests/ui/non_octal_unix_permissions.rs b/src/tools/clippy/tests/ui/non_octal_unix_permissions.rs new file mode 100644 index 0000000000000..7d2922f494e1e --- /dev/null +++ b/src/tools/clippy/tests/ui/non_octal_unix_permissions.rs @@ -0,0 +1,33 @@ +// ignore-windows +// run-rustfix +#![warn(clippy::non_octal_unix_permissions)] +use std::fs::{DirBuilder, File, OpenOptions, Permissions}; +use std::os::unix::fs::{DirBuilderExt, OpenOptionsExt, PermissionsExt}; + +fn main() { + let permissions = 0o760; + + // OpenOptionsExt::mode + let mut options = OpenOptions::new(); + options.mode(440); + options.mode(0o400); + options.mode(permissions); + + // PermissionsExt::from_mode + let _permissions = Permissions::from_mode(647); + let _permissions = Permissions::from_mode(0o000); + let _permissions = Permissions::from_mode(permissions); + + // PermissionsExt::set_mode + let f = File::create("foo.txt").unwrap(); + let metadata = f.metadata().unwrap(); + let mut permissions = metadata.permissions(); + + permissions.set_mode(644); + permissions.set_mode(0o704); + + // DirBuilderExt::mode + let mut builder = DirBuilder::new(); + builder.mode(755); + builder.mode(0o406); +} diff --git a/src/tools/clippy/tests/ui/non_octal_unix_permissions.stderr b/src/tools/clippy/tests/ui/non_octal_unix_permissions.stderr new file mode 100644 index 0000000000000..32845d0659415 --- /dev/null +++ b/src/tools/clippy/tests/ui/non_octal_unix_permissions.stderr @@ -0,0 +1,28 @@ +error: using a non-octal value to set unix file permissions + --> $DIR/non_octal_unix_permissions.rs:12:18 + | +LL | options.mode(440); + | ^^^ help: consider using an octal literal instead: `0o440` + | + = note: `-D clippy::non-octal-unix-permissions` implied by `-D warnings` + +error: using a non-octal value to set unix file permissions + --> $DIR/non_octal_unix_permissions.rs:17:47 + | +LL | let _permissions = Permissions::from_mode(647); + | ^^^ help: consider using an octal literal instead: `0o647` + +error: using a non-octal value to set unix file permissions + --> $DIR/non_octal_unix_permissions.rs:26:26 + | +LL | permissions.set_mode(644); + | ^^^ help: consider using an octal literal instead: `0o644` + +error: using a non-octal value to set unix file permissions + --> $DIR/non_octal_unix_permissions.rs:31:18 + | +LL | builder.mode(755); + | ^^^ help: consider using an octal literal instead: `0o755` + +error: aborting due to 4 previous errors + diff --git a/src/tools/clippy/tests/ui/option_filter_map.fixed b/src/tools/clippy/tests/ui/option_filter_map.fixed new file mode 100644 index 0000000000000..f9d1825ade054 --- /dev/null +++ b/src/tools/clippy/tests/ui/option_filter_map.fixed @@ -0,0 +1,23 @@ +#![warn(clippy::option_filter_map)] +// run-rustfix +fn odds_out(x: i32) -> Option<i32> { + if x % 2 == 0 { Some(x) } else { None } +} + +fn main() { + let _ = Some(Some(1)).flatten(); + let _ = Some(Some(1)).flatten(); + let _ = Some(1).map(odds_out).flatten(); + let _ = Some(1).map(odds_out).flatten(); + + let _ = vec![Some(1)].into_iter().flatten(); + let _ = vec![Some(1)].into_iter().flatten(); + let _ = vec![1] + .into_iter() + .map(odds_out) + .flatten(); + let _ = vec![1] + .into_iter() + .map(odds_out) + .flatten(); +} diff --git a/src/tools/clippy/tests/ui/option_filter_map.rs b/src/tools/clippy/tests/ui/option_filter_map.rs new file mode 100644 index 0000000000000..588e1ccccce20 --- /dev/null +++ b/src/tools/clippy/tests/ui/option_filter_map.rs @@ -0,0 +1,25 @@ +#![warn(clippy::option_filter_map)] +// run-rustfix +fn odds_out(x: i32) -> Option<i32> { + if x % 2 == 0 { Some(x) } else { None } +} + +fn main() { + let _ = Some(Some(1)).filter(Option::is_some).map(Option::unwrap); + let _ = Some(Some(1)).filter(|o| o.is_some()).map(|o| o.unwrap()); + let _ = Some(1).map(odds_out).filter(Option::is_some).map(Option::unwrap); + let _ = Some(1).map(odds_out).filter(|o| o.is_some()).map(|o| o.unwrap()); + + let _ = vec![Some(1)].into_iter().filter(Option::is_some).map(Option::unwrap); + let _ = vec![Some(1)].into_iter().filter(|o| o.is_some()).map(|o| o.unwrap()); + let _ = vec![1] + .into_iter() + .map(odds_out) + .filter(Option::is_some) + .map(Option::unwrap); + let _ = vec![1] + .into_iter() + .map(odds_out) + .filter(|o| o.is_some()) + .map(|o| o.unwrap()); +} diff --git a/src/tools/clippy/tests/ui/option_filter_map.stderr b/src/tools/clippy/tests/ui/option_filter_map.stderr new file mode 100644 index 0000000000000..31a82969d5a1c --- /dev/null +++ b/src/tools/clippy/tests/ui/option_filter_map.stderr @@ -0,0 +1,56 @@ +error: `filter` for `Some` followed by `unwrap` + --> $DIR/option_filter_map.rs:8:27 + | +LL | let _ = Some(Some(1)).filter(Option::is_some).map(Option::unwrap); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `flatten` instead: `flatten()` + | + = note: `-D clippy::option-filter-map` implied by `-D warnings` + +error: `filter` for `Some` followed by `unwrap` + --> $DIR/option_filter_map.rs:9:27 + | +LL | let _ = Some(Some(1)).filter(|o| o.is_some()).map(|o| o.unwrap()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `flatten` instead: `flatten()` + +error: `filter` for `Some` followed by `unwrap` + --> $DIR/option_filter_map.rs:10:35 + | +LL | let _ = Some(1).map(odds_out).filter(Option::is_some).map(Option::unwrap); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `flatten` instead: `flatten()` + +error: `filter` for `Some` followed by `unwrap` + --> $DIR/option_filter_map.rs:11:35 + | +LL | let _ = Some(1).map(odds_out).filter(|o| o.is_some()).map(|o| o.unwrap()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `flatten` instead: `flatten()` + +error: `filter` for `Some` followed by `unwrap` + --> $DIR/option_filter_map.rs:13:39 + | +LL | let _ = vec![Some(1)].into_iter().filter(Option::is_some).map(Option::unwrap); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `flatten` instead: `flatten()` + +error: `filter` for `Some` followed by `unwrap` + --> $DIR/option_filter_map.rs:14:39 + | +LL | let _ = vec![Some(1)].into_iter().filter(|o| o.is_some()).map(|o| o.unwrap()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `flatten` instead: `flatten()` + +error: `filter` for `Some` followed by `unwrap` + --> $DIR/option_filter_map.rs:18:10 + | +LL | .filter(Option::is_some) + | __________^ +LL | | .map(Option::unwrap); + | |____________________________^ help: consider using `flatten` instead: `flatten()` + +error: `filter` for `Some` followed by `unwrap` + --> $DIR/option_filter_map.rs:23:10 + | +LL | .filter(|o| o.is_some()) + | __________^ +LL | | .map(|o| o.unwrap()); + | |____________________________^ help: consider using `flatten` instead: `flatten()` + +error: aborting due to 8 previous errors + diff --git a/src/tools/clippy/tests/ui/option_option.rs b/src/tools/clippy/tests/ui/option_option.rs index 6859ba8e5bb8f..2faab9e035d99 100644 --- a/src/tools/clippy/tests/ui/option_option.rs +++ b/src/tools/clippy/tests/ui/option_option.rs @@ -1,6 +1,9 @@ #![deny(clippy::option_option)] #![allow(clippy::unnecessary_wraps)] +const C: Option<Option<i32>> = None; +static S: Option<Option<i32>> = None; + fn input(_: Option<Option<u8>>) {} fn output() -> Option<Option<u8>> { diff --git a/src/tools/clippy/tests/ui/option_option.stderr b/src/tools/clippy/tests/ui/option_option.stderr index ad7f081c71392..a925bb35b04d8 100644 --- a/src/tools/clippy/tests/ui/option_option.stderr +++ b/src/tools/clippy/tests/ui/option_option.stderr @@ -1,8 +1,8 @@ error: consider using `Option<T>` instead of `Option<Option<T>>` or a custom enum if you need to distinguish all 3 cases - --> $DIR/option_option.rs:4:13 + --> $DIR/option_option.rs:4:10 | -LL | fn input(_: Option<Option<u8>>) {} - | ^^^^^^^^^^^^^^^^^^ +LL | const C: Option<Option<i32>> = None; + | ^^^^^^^^^^^^^^^^^^^ | note: the lint level is defined here --> $DIR/option_option.rs:1:9 @@ -11,58 +11,70 @@ LL | #![deny(clippy::option_option)] | ^^^^^^^^^^^^^^^^^^^^^ error: consider using `Option<T>` instead of `Option<Option<T>>` or a custom enum if you need to distinguish all 3 cases - --> $DIR/option_option.rs:6:16 + --> $DIR/option_option.rs:5:11 + | +LL | static S: Option<Option<i32>> = None; + | ^^^^^^^^^^^^^^^^^^^ + +error: consider using `Option<T>` instead of `Option<Option<T>>` or a custom enum if you need to distinguish all 3 cases + --> $DIR/option_option.rs:7:13 + | +LL | fn input(_: Option<Option<u8>>) {} + | ^^^^^^^^^^^^^^^^^^ + +error: consider using `Option<T>` instead of `Option<Option<T>>` or a custom enum if you need to distinguish all 3 cases + --> $DIR/option_option.rs:9:16 | LL | fn output() -> Option<Option<u8>> { | ^^^^^^^^^^^^^^^^^^ error: consider using `Option<T>` instead of `Option<Option<T>>` or a custom enum if you need to distinguish all 3 cases - --> $DIR/option_option.rs:10:27 + --> $DIR/option_option.rs:13:27 | LL | fn output_nested() -> Vec<Option<Option<u8>>> { | ^^^^^^^^^^^^^^^^^^ error: consider using `Option<T>` instead of `Option<Option<T>>` or a custom enum if you need to distinguish all 3 cases - --> $DIR/option_option.rs:15:30 + --> $DIR/option_option.rs:18:30 | LL | fn output_nested_nested() -> Option<Option<Option<u8>>> { | ^^^^^^^^^^^^^^^^^^^^^^^^^^ error: consider using `Option<T>` instead of `Option<Option<T>>` or a custom enum if you need to distinguish all 3 cases - --> $DIR/option_option.rs:20:8 + --> $DIR/option_option.rs:23:8 | LL | x: Option<Option<u8>>, | ^^^^^^^^^^^^^^^^^^ error: consider using `Option<T>` instead of `Option<Option<T>>` or a custom enum if you need to distinguish all 3 cases - --> $DIR/option_option.rs:24:23 + --> $DIR/option_option.rs:27:23 | LL | fn struct_fn() -> Option<Option<u8>> { | ^^^^^^^^^^^^^^^^^^ error: consider using `Option<T>` instead of `Option<Option<T>>` or a custom enum if you need to distinguish all 3 cases - --> $DIR/option_option.rs:30:22 + --> $DIR/option_option.rs:33:22 | LL | fn trait_fn() -> Option<Option<u8>>; | ^^^^^^^^^^^^^^^^^^ error: consider using `Option<T>` instead of `Option<Option<T>>` or a custom enum if you need to distinguish all 3 cases - --> $DIR/option_option.rs:34:11 + --> $DIR/option_option.rs:37:11 | LL | Tuple(Option<Option<u8>>), | ^^^^^^^^^^^^^^^^^^ error: consider using `Option<T>` instead of `Option<Option<T>>` or a custom enum if you need to distinguish all 3 cases - --> $DIR/option_option.rs:35:17 + --> $DIR/option_option.rs:38:17 | LL | Struct { x: Option<Option<u8>> }, | ^^^^^^^^^^^^^^^^^^ error: consider using `Option<T>` instead of `Option<Option<T>>` or a custom enum if you need to distinguish all 3 cases - --> $DIR/option_option.rs:76:14 + --> $DIR/option_option.rs:79:14 | LL | foo: Option<Option<Cow<'a, str>>>, | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: aborting due to 10 previous errors +error: aborting due to 12 previous errors diff --git a/src/tools/clippy/tests/ui/or_fun_call.fixed b/src/tools/clippy/tests/ui/or_fun_call.fixed index 2a63318c8c7a8..4390ff7dc3044 100644 --- a/src/tools/clippy/tests/ui/or_fun_call.fixed +++ b/src/tools/clippy/tests/ui/or_fun_call.fixed @@ -62,9 +62,15 @@ fn or_fun_call() { let mut map = HashMap::<u64, String>::new(); map.entry(42).or_insert_with(String::new); + let mut map_vec = HashMap::<u64, Vec<i32>>::new(); + map_vec.entry(42).or_insert_with(Vec::new); + let mut btree = BTreeMap::<u64, String>::new(); btree.entry(42).or_insert_with(String::new); + let mut btree_vec = BTreeMap::<u64, Vec<i32>>::new(); + btree_vec.entry(42).or_insert_with(Vec::new); + let stringy = Some(String::from("")); let _ = stringy.unwrap_or_else(|| "".to_owned()); @@ -114,6 +120,9 @@ fn test_or_with_ctors() { let slice = &["foo"][..]; let _ = opt.ok_or(slice.len()); + + let string = "foo"; + let _ = opt.ok_or(string.len()); } // Issue 4514 - early return @@ -126,4 +135,18 @@ fn f() -> Option<()> { Some(()) } +mod issue6675 { + unsafe fn foo() { + let mut s = "test".to_owned(); + None.unwrap_or_else(|| s.as_mut_vec()); + } + + fn bar() { + let mut s = "test".to_owned(); + None.unwrap_or_else(|| unsafe { s.as_mut_vec() }); + #[rustfmt::skip] + None.unwrap_or_else(|| unsafe { s.as_mut_vec() }); + } +} + fn main() {} diff --git a/src/tools/clippy/tests/ui/or_fun_call.rs b/src/tools/clippy/tests/ui/or_fun_call.rs index 026ef437caa1a..75908c974cc9d 100644 --- a/src/tools/clippy/tests/ui/or_fun_call.rs +++ b/src/tools/clippy/tests/ui/or_fun_call.rs @@ -62,9 +62,15 @@ fn or_fun_call() { let mut map = HashMap::<u64, String>::new(); map.entry(42).or_insert(String::new()); + let mut map_vec = HashMap::<u64, Vec<i32>>::new(); + map_vec.entry(42).or_insert(vec![]); + let mut btree = BTreeMap::<u64, String>::new(); btree.entry(42).or_insert(String::new()); + let mut btree_vec = BTreeMap::<u64, Vec<i32>>::new(); + btree_vec.entry(42).or_insert(vec![]); + let stringy = Some(String::from("")); let _ = stringy.unwrap_or("".to_owned()); @@ -114,6 +120,9 @@ fn test_or_with_ctors() { let slice = &["foo"][..]; let _ = opt.ok_or(slice.len()); + + let string = "foo"; + let _ = opt.ok_or(string.len()); } // Issue 4514 - early return @@ -126,4 +135,18 @@ fn f() -> Option<()> { Some(()) } +mod issue6675 { + unsafe fn foo() { + let mut s = "test".to_owned(); + None.unwrap_or(s.as_mut_vec()); + } + + fn bar() { + let mut s = "test".to_owned(); + None.unwrap_or(unsafe { s.as_mut_vec() }); + #[rustfmt::skip] + None.unwrap_or( unsafe { s.as_mut_vec() } ); + } +} + fn main() {} diff --git a/src/tools/clippy/tests/ui/or_fun_call.stderr b/src/tools/clippy/tests/ui/or_fun_call.stderr index fb8bf339828f4..9905029ce91fa 100644 --- a/src/tools/clippy/tests/ui/or_fun_call.stderr +++ b/src/tools/clippy/tests/ui/or_fun_call.stderr @@ -67,40 +67,70 @@ LL | map.entry(42).or_insert(String::new()); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `or_insert_with(String::new)` error: use of `or_insert` followed by a function call - --> $DIR/or_fun_call.rs:66:21 + --> $DIR/or_fun_call.rs:66:23 + | +LL | map_vec.entry(42).or_insert(vec![]); + | ^^^^^^^^^^^^^^^^^ help: try this: `or_insert_with(Vec::new)` + +error: use of `or_insert` followed by a function call + --> $DIR/or_fun_call.rs:69:21 | LL | btree.entry(42).or_insert(String::new()); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `or_insert_with(String::new)` +error: use of `or_insert` followed by a function call + --> $DIR/or_fun_call.rs:72:25 + | +LL | btree_vec.entry(42).or_insert(vec![]); + | ^^^^^^^^^^^^^^^^^ help: try this: `or_insert_with(Vec::new)` + error: use of `unwrap_or` followed by a function call - --> $DIR/or_fun_call.rs:69:21 + --> $DIR/or_fun_call.rs:75:21 | LL | let _ = stringy.unwrap_or("".to_owned()); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| "".to_owned())` error: use of `unwrap_or` followed by a function call - --> $DIR/or_fun_call.rs:77:21 + --> $DIR/or_fun_call.rs:83:21 | LL | let _ = Some(1).unwrap_or(map[&1]); | ^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| map[&1])` error: use of `unwrap_or` followed by a function call - --> $DIR/or_fun_call.rs:79:21 + --> $DIR/or_fun_call.rs:85:21 | LL | let _ = Some(1).unwrap_or(map[&1]); | ^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| map[&1])` error: use of `or` followed by a function call - --> $DIR/or_fun_call.rs:103:35 + --> $DIR/or_fun_call.rs:109:35 | LL | let _ = Some("a".to_string()).or(Some("b".to_string())); | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `or_else(|| Some("b".to_string()))` error: use of `or` followed by a function call - --> $DIR/or_fun_call.rs:107:10 + --> $DIR/or_fun_call.rs:113:10 | LL | .or(Some(Bar(b, Duration::from_secs(2)))); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `or_else(|| Some(Bar(b, Duration::from_secs(2))))` -error: aborting due to 17 previous errors +error: use of `unwrap_or` followed by a function call + --> $DIR/or_fun_call.rs:141:14 + | +LL | None.unwrap_or(s.as_mut_vec()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| s.as_mut_vec())` + +error: use of `unwrap_or` followed by a function call + --> $DIR/or_fun_call.rs:146:14 + | +LL | None.unwrap_or(unsafe { s.as_mut_vec() }); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| unsafe { s.as_mut_vec() })` + +error: use of `unwrap_or` followed by a function call + --> $DIR/or_fun_call.rs:148:14 + | +LL | None.unwrap_or( unsafe { s.as_mut_vec() } ); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| unsafe { s.as_mut_vec() })` + +error: aborting due to 22 previous errors diff --git a/src/tools/clippy/tests/ui/print.stderr b/src/tools/clippy/tests/ui/print.stderr index 208d953262851..1754c418381a1 100644 --- a/src/tools/clippy/tests/ui/print.stderr +++ b/src/tools/clippy/tests/ui/print.stderr @@ -1,8 +1,8 @@ error: use of `Debug`-based formatting - --> $DIR/print.rs:11:19 + --> $DIR/print.rs:11:20 | LL | write!(f, "{:?}", 43.1415) - | ^^^^^^ + | ^^^^ | = note: `-D clippy::use-debug` implied by `-D warnings` @@ -33,10 +33,10 @@ LL | print!("Hello {:?}", "World"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: use of `Debug`-based formatting - --> $DIR/print.rs:28:12 + --> $DIR/print.rs:28:19 | LL | print!("Hello {:?}", "World"); - | ^^^^^^^^^^^^ + | ^^^^ error: use of `print!` --> $DIR/print.rs:30:5 @@ -45,10 +45,10 @@ LL | print!("Hello {:#?}", "#orld"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: use of `Debug`-based formatting - --> $DIR/print.rs:30:12 + --> $DIR/print.rs:30:19 | LL | print!("Hello {:#?}", "#orld"); - | ^^^^^^^^^^^^^ + | ^^^^^ error: aborting due to 8 previous errors diff --git a/src/tools/clippy/tests/ui/print_literal.stderr b/src/tools/clippy/tests/ui/print_literal.stderr index e284aece236fa..54a4084c89e11 100644 --- a/src/tools/clippy/tests/ui/print_literal.stderr +++ b/src/tools/clippy/tests/ui/print_literal.stderr @@ -5,66 +5,120 @@ LL | print!("Hello {}", "world"); | ^^^^^^^ | = note: `-D clippy::print-literal` implied by `-D warnings` +help: try this + | +LL | print!("Hello world"); + | ^^^^^-- error: literal with an empty format string --> $DIR/print_literal.rs:26:36 | LL | println!("Hello {} {}", world, "world"); | ^^^^^^^ + | +help: try this + | +LL | println!("Hello {} world", world); + | ^^^^^ -- error: literal with an empty format string --> $DIR/print_literal.rs:27:26 | LL | println!("Hello {}", "world"); | ^^^^^^^ + | +help: try this + | +LL | println!("Hello world"); + | ^^^^^-- error: literal with an empty format string --> $DIR/print_literal.rs:32:25 | LL | println!("{0} {1}", "hello", "world"); | ^^^^^^^ + | +help: try this + | +LL | println!("hello {1}", "world"); + | ^^^^^ -- error: literal with an empty format string --> $DIR/print_literal.rs:32:34 | LL | println!("{0} {1}", "hello", "world"); | ^^^^^^^ + | +help: try this + | +LL | println!("{0} world", "hello"); + | ^^^^^ -- error: literal with an empty format string --> $DIR/print_literal.rs:33:25 | LL | println!("{1} {0}", "hello", "world"); | ^^^^^^^ + | +help: try this + | +LL | println!("{1} hello", "world"); + | ^^^^^-- error: literal with an empty format string --> $DIR/print_literal.rs:33:34 | LL | println!("{1} {0}", "hello", "world"); | ^^^^^^^ + | +help: try this + | +LL | println!("world {0}", "hello"); + | ^^^^^ -- error: literal with an empty format string - --> $DIR/print_literal.rs:36:35 + --> $DIR/print_literal.rs:36:29 | LL | println!("{foo} {bar}", foo = "hello", bar = "world"); - | ^^^^^^^ + | ^^^^^^^^^^^^^ + | +help: try this + | +LL | println!("hello {bar}", bar = "world"); + | ^^^^^ -- error: literal with an empty format string - --> $DIR/print_literal.rs:36:50 + --> $DIR/print_literal.rs:36:44 | LL | println!("{foo} {bar}", foo = "hello", bar = "world"); - | ^^^^^^^ + | ^^^^^^^^^^^^^ + | +help: try this + | +LL | println!("{foo} world", foo = "hello"); + | ^^^^^ -- error: literal with an empty format string - --> $DIR/print_literal.rs:37:35 + --> $DIR/print_literal.rs:37:29 | LL | println!("{bar} {foo}", foo = "hello", bar = "world"); - | ^^^^^^^ + | ^^^^^^^^^^^^^ + | +help: try this + | +LL | println!("{bar} hello", bar = "world"); + | ^^^^^-- error: literal with an empty format string - --> $DIR/print_literal.rs:37:50 + --> $DIR/print_literal.rs:37:44 | LL | println!("{bar} {foo}", foo = "hello", bar = "world"); - | ^^^^^^^ + | ^^^^^^^^^^^^^ + | +help: try this + | +LL | println!("world {foo}", foo = "hello"); + | ^^^^^ -- error: aborting due to 11 previous errors diff --git a/src/tools/clippy/tests/ui/ptr_arg.stderr b/src/tools/clippy/tests/ui/ptr_arg.stderr index 708318bbe295c..d302b16d4b72a 100644 --- a/src/tools/clippy/tests/ui/ptr_arg.stderr +++ b/src/tools/clippy/tests/ui/ptr_arg.stderr @@ -1,4 +1,4 @@ -error: writing `&Vec<_>` instead of `&[_]` involves one more reference and cannot be used with non-Vec-based slices. +error: writing `&Vec<_>` instead of `&[_]` involves one more reference and cannot be used with non-Vec-based slices --> $DIR/ptr_arg.rs:7:14 | LL | fn do_vec(x: &Vec<i64>) { @@ -6,25 +6,25 @@ LL | fn do_vec(x: &Vec<i64>) { | = note: `-D clippy::ptr-arg` implied by `-D warnings` -error: writing `&String` instead of `&str` involves a new object where a slice will do. +error: writing `&String` instead of `&str` involves a new object where a slice will do --> $DIR/ptr_arg.rs:16:14 | LL | fn do_str(x: &String) { | ^^^^^^^ help: change this to: `&str` -error: writing `&PathBuf` instead of `&Path` involves a new object where a slice will do. +error: writing `&PathBuf` instead of `&Path` involves a new object where a slice will do --> $DIR/ptr_arg.rs:25:15 | LL | fn do_path(x: &PathBuf) { | ^^^^^^^^ help: change this to: `&Path` -error: writing `&Vec<_>` instead of `&[_]` involves one more reference and cannot be used with non-Vec-based slices. +error: writing `&Vec<_>` instead of `&[_]` involves one more reference and cannot be used with non-Vec-based slices --> $DIR/ptr_arg.rs:38:18 | LL | fn do_vec(x: &Vec<i64>); | ^^^^^^^^^ help: change this to: `&[i64]` -error: writing `&Vec<_>` instead of `&[_]` involves one more reference and cannot be used with non-Vec-based slices. +error: writing `&Vec<_>` instead of `&[_]` involves one more reference and cannot be used with non-Vec-based slices --> $DIR/ptr_arg.rs:51:14 | LL | fn cloned(x: &Vec<u8>) -> Vec<u8> { @@ -43,7 +43,7 @@ help: change `x.clone()` to LL | x.to_owned() | -error: writing `&String` instead of `&str` involves a new object where a slice will do. +error: writing `&String` instead of `&str` involves a new object where a slice will do --> $DIR/ptr_arg.rs:60:18 | LL | fn str_cloned(x: &String) -> String { @@ -66,7 +66,7 @@ help: change `x.clone()` to LL | x.to_string() | -error: writing `&PathBuf` instead of `&Path` involves a new object where a slice will do. +error: writing `&PathBuf` instead of `&Path` involves a new object where a slice will do --> $DIR/ptr_arg.rs:68:19 | LL | fn path_cloned(x: &PathBuf) -> PathBuf { @@ -89,7 +89,7 @@ help: change `x.clone()` to LL | x.to_path_buf() | -error: writing `&String` instead of `&str` involves a new object where a slice will do. +error: writing `&String` instead of `&str` involves a new object where a slice will do --> $DIR/ptr_arg.rs:76:44 | LL | fn false_positive_capacity(x: &Vec<u8>, y: &String) { @@ -108,13 +108,13 @@ help: change `y.as_str()` to LL | let c = y; | ^ -error: using a reference to `Cow` is not recommended. +error: using a reference to `Cow` is not recommended --> $DIR/ptr_arg.rs:90:25 | LL | fn test_cow_with_ref(c: &Cow<[i32]>) {} | ^^^^^^^^^^^ help: change this to: `&[i32]` -error: writing `&Vec<_>` instead of `&[_]` involves one more reference and cannot be used with non-Vec-based slices. +error: writing `&Vec<_>` instead of `&[_]` involves one more reference and cannot be used with non-Vec-based slices --> $DIR/ptr_arg.rs:143:21 | LL | fn foo_vec(vec: &Vec<u8>) { @@ -133,7 +133,7 @@ help: change `vec.clone()` to LL | let _ = vec.to_owned().clone(); | ^^^^^^^^^^^^^^ -error: writing `&PathBuf` instead of `&Path` involves a new object where a slice will do. +error: writing `&PathBuf` instead of `&Path` involves a new object where a slice will do --> $DIR/ptr_arg.rs:148:23 | LL | fn foo_path(path: &PathBuf) { @@ -152,7 +152,7 @@ help: change `path.clone()` to LL | let _ = path.to_path_buf().clone(); | ^^^^^^^^^^^^^^^^^^ -error: writing `&PathBuf` instead of `&Path` involves a new object where a slice will do. +error: writing `&PathBuf` instead of `&Path` involves a new object where a slice will do --> $DIR/ptr_arg.rs:153:21 | LL | fn foo_str(str: &PathBuf) { diff --git a/src/tools/clippy/tests/ui/redundant_clone.fixed b/src/tools/clippy/tests/ui/redundant_clone.fixed index cdeefda4c234c..f5da703cd1dea 100644 --- a/src/tools/clippy/tests/ui/redundant_clone.fixed +++ b/src/tools/clippy/tests/ui/redundant_clone.fixed @@ -1,6 +1,7 @@ // run-rustfix // rustfix-only-machine-applicable +#![allow(clippy::implicit_clone)] use std::ffi::OsString; use std::path::Path; @@ -53,16 +54,13 @@ fn main() { not_consumed(); issue_5405(); manually_drop(); + clone_then_move_cloned(); } #[derive(Clone)] struct Alpha; fn with_branch(a: Alpha, b: bool) -> (Alpha, Alpha) { - if b { - (a.clone(), a) - } else { - (Alpha, a) - } + if b { (a.clone(), a) } else { (Alpha, a) } } fn cannot_double_move(a: Alpha) -> (Alpha, Alpha) { @@ -185,3 +183,26 @@ fn manually_drop() { Arc::from_raw(p); } } + +fn clone_then_move_cloned() { + // issue #5973 + let x = Some(String::new()); + // ok, x is moved while the clone is in use. + assert_eq!(x.clone(), None, "not equal {}", x.unwrap()); + + // issue #5595 + fn foo<F: Fn()>(_: &Alpha, _: F) {} + let x = Alpha; + // ok, data is moved while the clone is in use. + foo(&x.clone(), move || { + let _ = x; + }); + + // issue #6998 + struct S(String); + impl S { + fn m(&mut self) {} + } + let mut x = S(String::new()); + x.0.clone().chars().for_each(|_| x.m()); +} diff --git a/src/tools/clippy/tests/ui/redundant_clone.rs b/src/tools/clippy/tests/ui/redundant_clone.rs index acb7ffb305f2a..fd7f31a1cc5b6 100644 --- a/src/tools/clippy/tests/ui/redundant_clone.rs +++ b/src/tools/clippy/tests/ui/redundant_clone.rs @@ -1,6 +1,7 @@ // run-rustfix // rustfix-only-machine-applicable +#![allow(clippy::implicit_clone)] use std::ffi::OsString; use std::path::Path; @@ -53,16 +54,13 @@ fn main() { not_consumed(); issue_5405(); manually_drop(); + clone_then_move_cloned(); } #[derive(Clone)] struct Alpha; fn with_branch(a: Alpha, b: bool) -> (Alpha, Alpha) { - if b { - (a.clone(), a.clone()) - } else { - (Alpha, a) - } + if b { (a.clone(), a.clone()) } else { (Alpha, a) } } fn cannot_double_move(a: Alpha) -> (Alpha, Alpha) { @@ -185,3 +183,26 @@ fn manually_drop() { Arc::from_raw(p); } } + +fn clone_then_move_cloned() { + // issue #5973 + let x = Some(String::new()); + // ok, x is moved while the clone is in use. + assert_eq!(x.clone(), None, "not equal {}", x.unwrap()); + + // issue #5595 + fn foo<F: Fn()>(_: &Alpha, _: F) {} + let x = Alpha; + // ok, data is moved while the clone is in use. + foo(&x.clone(), move || { + let _ = x; + }); + + // issue #6998 + struct S(String); + impl S { + fn m(&mut self) {} + } + let mut x = S(String::new()); + x.0.clone().chars().for_each(|_| x.m()); +} diff --git a/src/tools/clippy/tests/ui/redundant_clone.stderr b/src/tools/clippy/tests/ui/redundant_clone.stderr index 89b3925429910..529a6de91e266 100644 --- a/src/tools/clippy/tests/ui/redundant_clone.stderr +++ b/src/tools/clippy/tests/ui/redundant_clone.stderr @@ -1,168 +1,168 @@ error: redundant clone - --> $DIR/redundant_clone.rs:8:42 + --> $DIR/redundant_clone.rs:9:42 | LL | let _s = ["lorem", "ipsum"].join(" ").to_string(); | ^^^^^^^^^^^^ help: remove this | = note: `-D clippy::redundant-clone` implied by `-D warnings` note: this value is dropped without further use - --> $DIR/redundant_clone.rs:8:14 + --> $DIR/redundant_clone.rs:9:14 | LL | let _s = ["lorem", "ipsum"].join(" ").to_string(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: redundant clone - --> $DIR/redundant_clone.rs:11:15 + --> $DIR/redundant_clone.rs:12:15 | LL | let _s = s.clone(); | ^^^^^^^^ help: remove this | note: this value is dropped without further use - --> $DIR/redundant_clone.rs:11:14 + --> $DIR/redundant_clone.rs:12:14 | LL | let _s = s.clone(); | ^ error: redundant clone - --> $DIR/redundant_clone.rs:14:15 + --> $DIR/redundant_clone.rs:15:15 | LL | let _s = s.to_string(); | ^^^^^^^^^^^^ help: remove this | note: this value is dropped without further use - --> $DIR/redundant_clone.rs:14:14 + --> $DIR/redundant_clone.rs:15:14 | LL | let _s = s.to_string(); | ^ error: redundant clone - --> $DIR/redundant_clone.rs:17:15 + --> $DIR/redundant_clone.rs:18:15 | LL | let _s = s.to_owned(); | ^^^^^^^^^^^ help: remove this | note: this value is dropped without further use - --> $DIR/redundant_clone.rs:17:14 + --> $DIR/redundant_clone.rs:18:14 | LL | let _s = s.to_owned(); | ^ error: redundant clone - --> $DIR/redundant_clone.rs:19:42 + --> $DIR/redundant_clone.rs:20:42 | LL | let _s = Path::new("/a/b/").join("c").to_owned(); | ^^^^^^^^^^^ help: remove this | note: this value is dropped without further use - --> $DIR/redundant_clone.rs:19:14 + --> $DIR/redundant_clone.rs:20:14 | LL | let _s = Path::new("/a/b/").join("c").to_owned(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: redundant clone - --> $DIR/redundant_clone.rs:21:42 + --> $DIR/redundant_clone.rs:22:42 | LL | let _s = Path::new("/a/b/").join("c").to_path_buf(); | ^^^^^^^^^^^^^^ help: remove this | note: this value is dropped without further use - --> $DIR/redundant_clone.rs:21:14 + --> $DIR/redundant_clone.rs:22:14 | LL | let _s = Path::new("/a/b/").join("c").to_path_buf(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: redundant clone - --> $DIR/redundant_clone.rs:23:29 + --> $DIR/redundant_clone.rs:24:29 | LL | let _s = OsString::new().to_owned(); | ^^^^^^^^^^^ help: remove this | note: this value is dropped without further use - --> $DIR/redundant_clone.rs:23:14 + --> $DIR/redundant_clone.rs:24:14 | LL | let _s = OsString::new().to_owned(); | ^^^^^^^^^^^^^^^ error: redundant clone - --> $DIR/redundant_clone.rs:25:29 + --> $DIR/redundant_clone.rs:26:29 | LL | let _s = OsString::new().to_os_string(); | ^^^^^^^^^^^^^^^ help: remove this | note: this value is dropped without further use - --> $DIR/redundant_clone.rs:25:14 + --> $DIR/redundant_clone.rs:26:14 | LL | let _s = OsString::new().to_os_string(); | ^^^^^^^^^^^^^^^ error: redundant clone - --> $DIR/redundant_clone.rs:32:19 + --> $DIR/redundant_clone.rs:33:19 | LL | let _t = tup.0.clone(); | ^^^^^^^^ help: remove this | note: this value is dropped without further use - --> $DIR/redundant_clone.rs:32:14 + --> $DIR/redundant_clone.rs:33:14 | LL | let _t = tup.0.clone(); | ^^^^^ error: redundant clone - --> $DIR/redundant_clone.rs:62:22 + --> $DIR/redundant_clone.rs:63:25 | -LL | (a.clone(), a.clone()) - | ^^^^^^^^ help: remove this +LL | if b { (a.clone(), a.clone()) } else { (Alpha, a) } + | ^^^^^^^^ help: remove this | note: this value is dropped without further use - --> $DIR/redundant_clone.rs:62:21 + --> $DIR/redundant_clone.rs:63:24 | -LL | (a.clone(), a.clone()) - | ^ +LL | if b { (a.clone(), a.clone()) } else { (Alpha, a) } + | ^ error: redundant clone - --> $DIR/redundant_clone.rs:122:15 + --> $DIR/redundant_clone.rs:120:15 | LL | let _s = s.clone(); | ^^^^^^^^ help: remove this | note: this value is dropped without further use - --> $DIR/redundant_clone.rs:122:14 + --> $DIR/redundant_clone.rs:120:14 | LL | let _s = s.clone(); | ^ error: redundant clone - --> $DIR/redundant_clone.rs:123:15 + --> $DIR/redundant_clone.rs:121:15 | LL | let _t = t.clone(); | ^^^^^^^^ help: remove this | note: this value is dropped without further use - --> $DIR/redundant_clone.rs:123:14 + --> $DIR/redundant_clone.rs:121:14 | LL | let _t = t.clone(); | ^ error: redundant clone - --> $DIR/redundant_clone.rs:133:19 + --> $DIR/redundant_clone.rs:131:19 | LL | let _f = f.clone(); | ^^^^^^^^ help: remove this | note: this value is dropped without further use - --> $DIR/redundant_clone.rs:133:18 + --> $DIR/redundant_clone.rs:131:18 | LL | let _f = f.clone(); | ^ error: redundant clone - --> $DIR/redundant_clone.rs:145:14 + --> $DIR/redundant_clone.rs:143:14 | LL | let y = x.clone().join("matthias"); | ^^^^^^^^ help: remove this | note: cloned value is neither consumed nor mutated - --> $DIR/redundant_clone.rs:145:13 + --> $DIR/redundant_clone.rs:143:13 | LL | let y = x.clone().join("matthias"); | ^^^^^^^^^ diff --git a/src/tools/clippy/tests/ui/redundant_slicing.rs b/src/tools/clippy/tests/ui/redundant_slicing.rs index 922b8b4ce57f6..554b6ba36ae0d 100644 --- a/src/tools/clippy/tests/ui/redundant_slicing.rs +++ b/src/tools/clippy/tests/ui/redundant_slicing.rs @@ -2,10 +2,31 @@ #![warn(clippy::redundant_slicing)] fn main() { - let x: &[u32] = &[0]; - let err = &x[..]; + let slice: &[u32] = &[0]; + let _ = &slice[..]; let v = vec![0]; - let ok = &v[..]; - let err = &(&v[..])[..]; + let _ = &v[..]; // Changes the type + let _ = &(&v[..])[..]; // Outer borrow is redundant + + static S: &[u8] = &[0, 1, 2]; + let err = &mut &S[..]; // Should reborrow instead of slice + + let mut vec = vec![0]; + let mut_slice = &mut *vec; + let _ = &mut mut_slice[..]; // Should reborrow instead of slice + + macro_rules! m { + ($e:expr) => { + $e + }; + } + let _ = &m!(slice)[..]; + + macro_rules! m2 { + ($e:expr) => { + &$e[..] + }; + } + let _ = m2!(slice); // Don't lint in a macro } diff --git a/src/tools/clippy/tests/ui/redundant_slicing.stderr b/src/tools/clippy/tests/ui/redundant_slicing.stderr index 9efd6484ad0c1..bbd10eafbbe78 100644 --- a/src/tools/clippy/tests/ui/redundant_slicing.stderr +++ b/src/tools/clippy/tests/ui/redundant_slicing.stderr @@ -1,16 +1,34 @@ error: redundant slicing of the whole range - --> $DIR/redundant_slicing.rs:6:15 + --> $DIR/redundant_slicing.rs:6:13 | -LL | let err = &x[..]; - | ^^^^^^ help: use the original slice instead: `x` +LL | let _ = &slice[..]; + | ^^^^^^^^^^ help: use the original value instead: `slice` | = note: `-D clippy::redundant-slicing` implied by `-D warnings` error: redundant slicing of the whole range - --> $DIR/redundant_slicing.rs:10:15 + --> $DIR/redundant_slicing.rs:10:13 | -LL | let err = &(&v[..])[..]; - | ^^^^^^^^^^^^^ help: use the original slice instead: `(&v[..])` +LL | let _ = &(&v[..])[..]; // Outer borrow is redundant + | ^^^^^^^^^^^^^ help: use the original value instead: `(&v[..])` -error: aborting due to 2 previous errors +error: redundant slicing of the whole range + --> $DIR/redundant_slicing.rs:13:20 + | +LL | let err = &mut &S[..]; // Should reborrow instead of slice + | ^^^^^^ help: reborrow the original value instead: `&*S` + +error: redundant slicing of the whole range + --> $DIR/redundant_slicing.rs:17:13 + | +LL | let _ = &mut mut_slice[..]; // Should reborrow instead of slice + | ^^^^^^^^^^^^^^^^^^ help: reborrow the original value instead: `&mut *mut_slice` + +error: redundant slicing of the whole range + --> $DIR/redundant_slicing.rs:24:13 + | +LL | let _ = &m!(slice)[..]; + | ^^^^^^^^^^^^^^ help: use the original value instead: `slice` + +error: aborting due to 5 previous errors diff --git a/src/tools/clippy/tests/ui/ref_option_ref.rs b/src/tools/clippy/tests/ui/ref_option_ref.rs index b2c275d68afa1..2df45c927d716 100644 --- a/src/tools/clippy/tests/ui/ref_option_ref.rs +++ b/src/tools/clippy/tests/ui/ref_option_ref.rs @@ -9,7 +9,7 @@ static THRESHOLD: i32 = 10; static REF_THRESHOLD: &Option<&i32> = &Some(&THRESHOLD); const CONST_THRESHOLD: &i32 = &10; -const REF_CONST: &Option<&i32> = &Some(&CONST_THRESHOLD); +const REF_CONST: &Option<&i32> = &Some(CONST_THRESHOLD); type RefOptRefU32<'a> = &'a Option<&'a u32>; type RefOptRef<'a, T> = &'a Option<&'a T>; diff --git a/src/tools/clippy/tests/ui/ref_option_ref.stderr b/src/tools/clippy/tests/ui/ref_option_ref.stderr index 4e7fc80006118..b61334758e854 100644 --- a/src/tools/clippy/tests/ui/ref_option_ref.stderr +++ b/src/tools/clippy/tests/ui/ref_option_ref.stderr @@ -9,7 +9,7 @@ LL | static REF_THRESHOLD: &Option<&i32> = &Some(&THRESHOLD); error: since `&` implements the `Copy` trait, `&Option<&T>` can be simplified to `Option<&T>` --> $DIR/ref_option_ref.rs:12:18 | -LL | const REF_CONST: &Option<&i32> = &Some(&CONST_THRESHOLD); +LL | const REF_CONST: &Option<&i32> = &Some(CONST_THRESHOLD); | ^^^^^^^^^^^^^ help: try: `Option<&i32>` error: since `&` implements the `Copy` trait, `&Option<&T>` can be simplified to `Option<&T>` diff --git a/src/tools/clippy/tests/ui/result_unit_error.stderr b/src/tools/clippy/tests/ui/result_unit_error.stderr index 41d8b0a7cb7f8..8c7573eabda94 100644 --- a/src/tools/clippy/tests/ui/result_unit_error.stderr +++ b/src/tools/clippy/tests/ui/result_unit_error.stderr @@ -1,43 +1,43 @@ -error: this returns a `Result<_, ()> +error: this returns a `Result<_, ()>` --> $DIR/result_unit_error.rs:3:1 | LL | pub fn returns_unit_error() -> Result<u32, ()> { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: `-D clippy::result-unit-err` implied by `-D warnings` - = help: use a custom Error type instead + = help: use a custom `Error` type instead -error: this returns a `Result<_, ()> +error: this returns a `Result<_, ()>` --> $DIR/result_unit_error.rs:12:5 | LL | fn get_that_error(&self) -> Result<bool, ()>; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = help: use a custom Error type instead + = help: use a custom `Error` type instead -error: this returns a `Result<_, ()> +error: this returns a `Result<_, ()>` --> $DIR/result_unit_error.rs:14:5 | LL | fn get_this_one_too(&self) -> Result<bool, ()> { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = help: use a custom Error type instead + = help: use a custom `Error` type instead -error: this returns a `Result<_, ()> +error: this returns a `Result<_, ()>` --> $DIR/result_unit_error.rs:32:5 | LL | pub fn unit_error(&self) -> Result<usize, ()> { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = help: use a custom Error type instead + = help: use a custom `Error` type instead -error: this returns a `Result<_, ()> +error: this returns a `Result<_, ()>` --> $DIR/result_unit_error.rs:41:5 | LL | pub fn should_lint() -> ResInv<(), usize> { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = help: use a custom Error type instead + = help: use a custom `Error` type instead error: aborting due to 5 previous errors diff --git a/src/tools/clippy/tests/ui/same_item_push.rs b/src/tools/clippy/tests/ui/same_item_push.rs index a37c8782ec330..9d420ec672a26 100644 --- a/src/tools/clippy/tests/ui/same_item_push.rs +++ b/src/tools/clippy/tests/ui/same_item_push.rs @@ -148,4 +148,11 @@ fn main() { }; vec.push(item); } + + // Fix #6987 + let mut vec = Vec::new(); + for _ in 0..10 { + vec.push(1); + vec.extend(&[2]); + } } diff --git a/src/tools/clippy/tests/ui/search_is_some.rs b/src/tools/clippy/tests/ui/search_is_some.rs index f0dc3b3d06bbc..72bc6ef35d317 100644 --- a/src/tools/clippy/tests/ui/search_is_some.rs +++ b/src/tools/clippy/tests/ui/search_is_some.rs @@ -1,8 +1,9 @@ // aux-build:option_helpers.rs +#![warn(clippy::search_is_some)] +#![allow(dead_code)] extern crate option_helpers; use option_helpers::IteratorFalsePositives; -#[warn(clippy::search_is_some)] #[rustfmt::skip] fn main() { let v = vec![3, 2, 1, 0, -1, -2, -3]; @@ -36,3 +37,37 @@ fn main() { // `Pattern` that is not a string let _ = "hello world".find(|c: char| c == 'o' || c == 'l').is_some(); } + +#[rustfmt::skip] +fn is_none() { + let v = vec![3, 2, 1, 0, -1, -2, -3]; + let y = &&42; + + + // Check `find().is_none()`, multi-line case. + let _ = v.iter().find(|&x| { + *x < 0 + } + ).is_none(); + + // Check `position().is_none()`, multi-line case. + let _ = v.iter().position(|&x| { + x < 0 + } + ).is_none(); + + // Check `rposition().is_none()`, multi-line case. + let _ = v.iter().rposition(|&x| { + x < 0 + } + ).is_none(); + + // Check that we don't lint if the caller is not an `Iterator` or string + let falsepos = IteratorFalsePositives { foo: 0 }; + let _ = falsepos.find().is_none(); + let _ = falsepos.position().is_none(); + let _ = falsepos.rposition().is_none(); + // check that we don't lint if `find()` is called with + // `Pattern` that is not a string + let _ = "hello world".find(|c: char| c == 'o' || c == 'l').is_none(); +} diff --git a/src/tools/clippy/tests/ui/search_is_some.stderr b/src/tools/clippy/tests/ui/search_is_some.stderr index c601f568c609b..f3c758e451ef1 100644 --- a/src/tools/clippy/tests/ui/search_is_some.stderr +++ b/src/tools/clippy/tests/ui/search_is_some.stderr @@ -1,5 +1,5 @@ error: called `is_some()` after searching an `Iterator` with `find` - --> $DIR/search_is_some.rs:13:13 + --> $DIR/search_is_some.rs:14:13 | LL | let _ = v.iter().find(|&x| { | _____________^ @@ -12,7 +12,7 @@ LL | | ).is_some(); = help: this is more succinctly expressed by calling `any()` error: called `is_some()` after searching an `Iterator` with `position` - --> $DIR/search_is_some.rs:19:13 + --> $DIR/search_is_some.rs:20:13 | LL | let _ = v.iter().position(|&x| { | _____________^ @@ -24,7 +24,7 @@ LL | | ).is_some(); = help: this is more succinctly expressed by calling `any()` error: called `is_some()` after searching an `Iterator` with `rposition` - --> $DIR/search_is_some.rs:25:13 + --> $DIR/search_is_some.rs:26:13 | LL | let _ = v.iter().rposition(|&x| { | _____________^ @@ -35,5 +35,41 @@ LL | | ).is_some(); | = help: this is more succinctly expressed by calling `any()` -error: aborting due to 3 previous errors +error: called `is_none()` after searching an `Iterator` with `find` + --> $DIR/search_is_some.rs:48:13 + | +LL | let _ = v.iter().find(|&x| { + | _____________^ +LL | | *x < 0 +LL | | } +LL | | ).is_none(); + | |______________________________^ + | + = help: this is more succinctly expressed by calling `any()` with negation + +error: called `is_none()` after searching an `Iterator` with `position` + --> $DIR/search_is_some.rs:54:13 + | +LL | let _ = v.iter().position(|&x| { + | _____________^ +LL | | x < 0 +LL | | } +LL | | ).is_none(); + | |______________________________^ + | + = help: this is more succinctly expressed by calling `any()` with negation + +error: called `is_none()` after searching an `Iterator` with `rposition` + --> $DIR/search_is_some.rs:60:13 + | +LL | let _ = v.iter().rposition(|&x| { + | _____________^ +LL | | x < 0 +LL | | } +LL | | ).is_none(); + | |______________________________^ + | + = help: this is more succinctly expressed by calling `any()` with negation + +error: aborting due to 6 previous errors diff --git a/src/tools/clippy/tests/ui/search_is_some_fixable.fixed b/src/tools/clippy/tests/ui/search_is_some_fixable.fixed index dc3f290e56246..62ff16f67f41b 100644 --- a/src/tools/clippy/tests/ui/search_is_some_fixable.fixed +++ b/src/tools/clippy/tests/ui/search_is_some_fixable.fixed @@ -1,5 +1,5 @@ // run-rustfix - +#![allow(dead_code)] #![warn(clippy::search_is_some)] fn main() { @@ -33,3 +33,36 @@ fn main() { let _ = s1[2..].contains(&s2); let _ = s1[2..].contains(&s2[2..]); } + +fn is_none() { + let v = vec![3, 2, 1, 0, -1, -2, -3]; + let y = &&42; + + // Check `find().is_none()`, single-line case. + let _ = !v.iter().any(|x| *x < 0); + let _ = !(0..1).any(|x| **y == x); // one dereference less + let _ = !(0..1).any(|x| x == 0); + let _ = !v.iter().any(|x| *x == 0); + + // Check `position().is_none()`, single-line case. + let _ = !v.iter().any(|&x| x < 0); + + // Check `rposition().is_none()`, single-line case. + let _ = !v.iter().any(|&x| x < 0); + + let s1 = String::from("hello world"); + let s2 = String::from("world"); + + // caller of `find()` is a `&`static str` + let _ = !"hello world".contains("world"); + let _ = !"hello world".contains(&s2); + let _ = !"hello world".contains(&s2[2..]); + // caller of `find()` is a `String` + let _ = !s1.contains("world"); + let _ = !s1.contains(&s2); + let _ = !s1.contains(&s2[2..]); + // caller of `find()` is slice of `String` + let _ = !s1[2..].contains("world"); + let _ = !s1[2..].contains(&s2); + let _ = !s1[2..].contains(&s2[2..]); +} diff --git a/src/tools/clippy/tests/ui/search_is_some_fixable.rs b/src/tools/clippy/tests/ui/search_is_some_fixable.rs index 146cf5adf1b0f..8407f71664740 100644 --- a/src/tools/clippy/tests/ui/search_is_some_fixable.rs +++ b/src/tools/clippy/tests/ui/search_is_some_fixable.rs @@ -1,5 +1,5 @@ // run-rustfix - +#![allow(dead_code)] #![warn(clippy::search_is_some)] fn main() { @@ -33,3 +33,36 @@ fn main() { let _ = s1[2..].find(&s2).is_some(); let _ = s1[2..].find(&s2[2..]).is_some(); } + +fn is_none() { + let v = vec![3, 2, 1, 0, -1, -2, -3]; + let y = &&42; + + // Check `find().is_none()`, single-line case. + let _ = v.iter().find(|&x| *x < 0).is_none(); + let _ = (0..1).find(|x| **y == *x).is_none(); // one dereference less + let _ = (0..1).find(|x| *x == 0).is_none(); + let _ = v.iter().find(|x| **x == 0).is_none(); + + // Check `position().is_none()`, single-line case. + let _ = v.iter().position(|&x| x < 0).is_none(); + + // Check `rposition().is_none()`, single-line case. + let _ = v.iter().rposition(|&x| x < 0).is_none(); + + let s1 = String::from("hello world"); + let s2 = String::from("world"); + + // caller of `find()` is a `&`static str` + let _ = "hello world".find("world").is_none(); + let _ = "hello world".find(&s2).is_none(); + let _ = "hello world".find(&s2[2..]).is_none(); + // caller of `find()` is a `String` + let _ = s1.find("world").is_none(); + let _ = s1.find(&s2).is_none(); + let _ = s1.find(&s2[2..]).is_none(); + // caller of `find()` is slice of `String` + let _ = s1[2..].find("world").is_none(); + let _ = s1[2..].find(&s2).is_none(); + let _ = s1[2..].find(&s2[2..]).is_none(); +} diff --git a/src/tools/clippy/tests/ui/search_is_some_fixable.stderr b/src/tools/clippy/tests/ui/search_is_some_fixable.stderr index 23c1d9a901b97..bd1b6955a9728 100644 --- a/src/tools/clippy/tests/ui/search_is_some_fixable.stderr +++ b/src/tools/clippy/tests/ui/search_is_some_fixable.stderr @@ -90,5 +90,95 @@ error: called `is_some()` after calling `find()` on a string LL | let _ = s1[2..].find(&s2[2..]).is_some(); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: use `contains()` instead: `contains(&s2[2..])` -error: aborting due to 15 previous errors +error: called `is_none()` after searching an `Iterator` with `find` + --> $DIR/search_is_some_fixable.rs:42:13 + | +LL | let _ = v.iter().find(|&x| *x < 0).is_none(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.any()` instead: `!v.iter().any(|x| *x < 0)` + +error: called `is_none()` after searching an `Iterator` with `find` + --> $DIR/search_is_some_fixable.rs:43:13 + | +LL | let _ = (0..1).find(|x| **y == *x).is_none(); // one dereference less + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.any()` instead: `!(0..1).any(|x| **y == x)` + +error: called `is_none()` after searching an `Iterator` with `find` + --> $DIR/search_is_some_fixable.rs:44:13 + | +LL | let _ = (0..1).find(|x| *x == 0).is_none(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.any()` instead: `!(0..1).any(|x| x == 0)` + +error: called `is_none()` after searching an `Iterator` with `find` + --> $DIR/search_is_some_fixable.rs:45:13 + | +LL | let _ = v.iter().find(|x| **x == 0).is_none(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.any()` instead: `!v.iter().any(|x| *x == 0)` + +error: called `is_none()` after searching an `Iterator` with `position` + --> $DIR/search_is_some_fixable.rs:48:13 + | +LL | let _ = v.iter().position(|&x| x < 0).is_none(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.any()` instead: `!v.iter().any(|&x| x < 0)` + +error: called `is_none()` after searching an `Iterator` with `rposition` + --> $DIR/search_is_some_fixable.rs:51:13 + | +LL | let _ = v.iter().rposition(|&x| x < 0).is_none(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.any()` instead: `!v.iter().any(|&x| x < 0)` + +error: called `is_none()` after calling `find()` on a string + --> $DIR/search_is_some_fixable.rs:57:13 + | +LL | let _ = "hello world".find("world").is_none(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.contains()` instead: `!"hello world".contains("world")` + +error: called `is_none()` after calling `find()` on a string + --> $DIR/search_is_some_fixable.rs:58:13 + | +LL | let _ = "hello world".find(&s2).is_none(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.contains()` instead: `!"hello world".contains(&s2)` + +error: called `is_none()` after calling `find()` on a string + --> $DIR/search_is_some_fixable.rs:59:13 + | +LL | let _ = "hello world".find(&s2[2..]).is_none(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.contains()` instead: `!"hello world".contains(&s2[2..])` + +error: called `is_none()` after calling `find()` on a string + --> $DIR/search_is_some_fixable.rs:61:13 + | +LL | let _ = s1.find("world").is_none(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.contains()` instead: `!s1.contains("world")` + +error: called `is_none()` after calling `find()` on a string + --> $DIR/search_is_some_fixable.rs:62:13 + | +LL | let _ = s1.find(&s2).is_none(); + | ^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.contains()` instead: `!s1.contains(&s2)` + +error: called `is_none()` after calling `find()` on a string + --> $DIR/search_is_some_fixable.rs:63:13 + | +LL | let _ = s1.find(&s2[2..]).is_none(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.contains()` instead: `!s1.contains(&s2[2..])` + +error: called `is_none()` after calling `find()` on a string + --> $DIR/search_is_some_fixable.rs:65:13 + | +LL | let _ = s1[2..].find("world").is_none(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.contains()` instead: `!s1[2..].contains("world")` + +error: called `is_none()` after calling `find()` on a string + --> $DIR/search_is_some_fixable.rs:66:13 + | +LL | let _ = s1[2..].find(&s2).is_none(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.contains()` instead: `!s1[2..].contains(&s2)` + +error: called `is_none()` after calling `find()` on a string + --> $DIR/search_is_some_fixable.rs:67:13 + | +LL | let _ = s1[2..].find(&s2[2..]).is_none(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.contains()` instead: `!s1[2..].contains(&s2[2..])` + +error: aborting due to 30 previous errors diff --git a/src/tools/clippy/tests/ui/stable_sort_primitive.fixed b/src/tools/clippy/tests/ui/stable_sort_primitive.fixed index 8f8f566593152..f5f18169df295 100644 --- a/src/tools/clippy/tests/ui/stable_sort_primitive.fixed +++ b/src/tools/clippy/tests/ui/stable_sort_primitive.fixed @@ -20,7 +20,7 @@ fn main() { // Negative examples: behavior changes if made unstable let mut vec = vec![1, 3, 2]; vec.sort_by_key(|i| i / 2); - vec.sort_by(|a, b| (a + b).cmp(&b)); + vec.sort_by(|&a, &b| (a + b).cmp(&b)); // negative examples - Not of a primitive type let mut vec_of_complex = vec![String::from("hello"), String::from("world!")]; vec_of_complex.sort(); diff --git a/src/tools/clippy/tests/ui/stable_sort_primitive.rs b/src/tools/clippy/tests/ui/stable_sort_primitive.rs index f9bd977906718..8149c5638e0f7 100644 --- a/src/tools/clippy/tests/ui/stable_sort_primitive.rs +++ b/src/tools/clippy/tests/ui/stable_sort_primitive.rs @@ -20,7 +20,7 @@ fn main() { // Negative examples: behavior changes if made unstable let mut vec = vec![1, 3, 2]; vec.sort_by_key(|i| i / 2); - vec.sort_by(|a, b| (a + b).cmp(&b)); + vec.sort_by(|&a, &b| (a + b).cmp(&b)); // negative examples - Not of a primitive type let mut vec_of_complex = vec![String::from("hello"), String::from("world!")]; vec_of_complex.sort(); diff --git a/src/tools/clippy/tests/ui/string_lit_as_bytes.fixed b/src/tools/clippy/tests/ui/string_lit_as_bytes.fixed index ccf8f61c4a92c..dd22bfa5c53ef 100644 --- a/src/tools/clippy/tests/ui/string_lit_as_bytes.fixed +++ b/src/tools/clippy/tests/ui/string_lit_as_bytes.fixed @@ -8,10 +8,16 @@ fn str_lit_as_bytes() { let bs = br###"raw string with 3# plus " ""###; + let bs = b"lit to string".to_vec(); + let bs = b"lit to owned".to_vec(); + // no warning, because these cannot be written as byte string literals: let ubs = "☃".as_bytes(); let ubs = "hello there! this is a very long string".as_bytes(); + let ubs = "☃".to_string().into_bytes(); + let ubs = "this is also too long and shouldn't be fixed".to_string().into_bytes(); + let strify = stringify!(foobar).as_bytes(); let current_version = env!("CARGO_PKG_VERSION").as_bytes(); diff --git a/src/tools/clippy/tests/ui/string_lit_as_bytes.rs b/src/tools/clippy/tests/ui/string_lit_as_bytes.rs index 178df08e249ef..d2a710ed6b8ca 100644 --- a/src/tools/clippy/tests/ui/string_lit_as_bytes.rs +++ b/src/tools/clippy/tests/ui/string_lit_as_bytes.rs @@ -8,10 +8,16 @@ fn str_lit_as_bytes() { let bs = r###"raw string with 3# plus " ""###.as_bytes(); + let bs = "lit to string".to_string().into_bytes(); + let bs = "lit to owned".to_owned().into_bytes(); + // no warning, because these cannot be written as byte string literals: let ubs = "☃".as_bytes(); let ubs = "hello there! this is a very long string".as_bytes(); + let ubs = "☃".to_string().into_bytes(); + let ubs = "this is also too long and shouldn't be fixed".to_string().into_bytes(); + let strify = stringify!(foobar).as_bytes(); let current_version = env!("CARGO_PKG_VERSION").as_bytes(); diff --git a/src/tools/clippy/tests/ui/string_lit_as_bytes.stderr b/src/tools/clippy/tests/ui/string_lit_as_bytes.stderr index 99c512354d589..e0ddb070b5044 100644 --- a/src/tools/clippy/tests/ui/string_lit_as_bytes.stderr +++ b/src/tools/clippy/tests/ui/string_lit_as_bytes.stderr @@ -12,17 +12,29 @@ error: calling `as_bytes()` on a string literal LL | let bs = r###"raw string with 3# plus " ""###.as_bytes(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using a byte string literal instead: `br###"raw string with 3# plus " ""###` +error: calling `into_bytes()` on a string literal + --> $DIR/string_lit_as_bytes.rs:11:14 + | +LL | let bs = "lit to string".to_string().into_bytes(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using a byte string literal instead: `b"lit to string".to_vec()` + +error: calling `into_bytes()` on a string literal + --> $DIR/string_lit_as_bytes.rs:12:14 + | +LL | let bs = "lit to owned".to_owned().into_bytes(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using a byte string literal instead: `b"lit to owned".to_vec()` + error: calling `as_bytes()` on `include_str!(..)` - --> $DIR/string_lit_as_bytes.rs:19:22 + --> $DIR/string_lit_as_bytes.rs:25:22 | LL | let includestr = include_str!("entry_unfixable.rs").as_bytes(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `include_bytes!(..)` instead: `include_bytes!("entry_unfixable.rs")` error: calling `as_bytes()` on a string literal - --> $DIR/string_lit_as_bytes.rs:21:13 + --> $DIR/string_lit_as_bytes.rs:27:13 | LL | let _ = "string with newline/t/n".as_bytes(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using a byte string literal instead: `b"string with newline/t/n"` -error: aborting due to 4 previous errors +error: aborting due to 6 previous errors diff --git a/src/tools/clippy/tests/ui/suspicious_map.rs b/src/tools/clippy/tests/ui/suspicious_map.rs index d838d8fde2105..3a2a10cf09ea5 100644 --- a/src/tools/clippy/tests/ui/suspicious_map.rs +++ b/src/tools/clippy/tests/ui/suspicious_map.rs @@ -2,4 +2,31 @@ fn main() { let _ = (0..3).map(|x| x + 2).count(); + + let f = |x| x + 1; + let _ = (0..3).map(f).count(); +} + +fn negative() { + // closure with side effects + let mut sum = 0; + let _ = (0..3).map(|x| sum += x).count(); + + // closure variable with side effects + let ext_closure = |x| sum += x; + let _ = (0..3).map(ext_closure).count(); + + // closure that returns unit + let _ = (0..3) + .map(|x| { + // do nothing + }) + .count(); + + // external function + let _ = (0..3).map(do_something).count(); +} + +fn do_something<T>(t: T) -> String { + unimplemented!() } diff --git a/src/tools/clippy/tests/ui/suspicious_map.stderr b/src/tools/clippy/tests/ui/suspicious_map.stderr index e1b4ba40376f8..8c3f36584a5bd 100644 --- a/src/tools/clippy/tests/ui/suspicious_map.stderr +++ b/src/tools/clippy/tests/ui/suspicious_map.stderr @@ -7,5 +7,13 @@ LL | let _ = (0..3).map(|x| x + 2).count(); = note: `-D clippy::suspicious-map` implied by `-D warnings` = help: make sure you did not confuse `map` with `filter` or `for_each` -error: aborting due to previous error +error: this call to `map()` won't have an effect on the call to `count()` + --> $DIR/suspicious_map.rs:7:13 + | +LL | let _ = (0..3).map(f).count(); + | ^^^^^^^^^^^^^^^^^^^^^ + | + = help: make sure you did not confuse `map` with `filter` or `for_each` + +error: aborting due to 2 previous errors diff --git a/src/tools/clippy/tests/ui/suspicious_operation_groupings.stderr b/src/tools/clippy/tests/ui/suspicious_operation_groupings.stderr index ce7108217f18d..96065699d321a 100644 --- a/src/tools/clippy/tests/ui/suspicious_operation_groupings.stderr +++ b/src/tools/clippy/tests/ui/suspicious_operation_groupings.stderr @@ -1,166 +1,166 @@ -error: This sequence of operators looks suspiciously like a bug. +error: this sequence of operators looks suspiciously like a bug --> $DIR/suspicious_operation_groupings.rs:14:9 | LL | self.x == other.y && self.y == other.y && self.z == other.z - | ^^^^^^^^^^^^^^^^^ help: I think you meant: `self.x == other.x` + | ^^^^^^^^^^^^^^^^^ help: did you mean: `self.x == other.x` | = note: `-D clippy::suspicious-operation-groupings` implied by `-D warnings` -error: This sequence of operators looks suspiciously like a bug. +error: this sequence of operators looks suspiciously like a bug --> $DIR/suspicious_operation_groupings.rs:14:9 | LL | self.x == other.y && self.y == other.y && self.z == other.z - | ^^^^^^^^^^^^^^^^^ help: I think you meant: `self.x == other.x` + | ^^^^^^^^^^^^^^^^^ help: did you mean: `self.x == other.x` -error: This sequence of operators looks suspiciously like a bug. +error: this sequence of operators looks suspiciously like a bug --> $DIR/suspicious_operation_groupings.rs:27:20 | LL | s1.a < s2.a && s1.a < s2.b - | ^^^^^^^^^^^ help: I think you meant: `s1.b < s2.b` + | ^^^^^^^^^^^ help: did you mean: `s1.b < s2.b` -error: This sequence of operators looks suspiciously like a bug. +error: this sequence of operators looks suspiciously like a bug --> $DIR/suspicious_operation_groupings.rs:75:33 | LL | s1.a * s2.a + s1.b * s2.b + s1.c * s2.b + s1.d * s2.d - | ^^^^^^^^^^^ help: I think you meant: `s1.c * s2.c` + | ^^^^^^^^^^^ help: did you mean: `s1.c * s2.c` -error: This sequence of operators looks suspiciously like a bug. +error: this sequence of operators looks suspiciously like a bug --> $DIR/suspicious_operation_groupings.rs:80:19 | LL | s1.a * s2.a + s1.b * s2.c + s1.c * s2.c - | ^^^^^^^^^^^ help: I think you meant: `s1.b * s2.b` + | ^^^^^^^^^^^ help: did you mean: `s1.b * s2.b` -error: This sequence of operators looks suspiciously like a bug. +error: this sequence of operators looks suspiciously like a bug --> $DIR/suspicious_operation_groupings.rs:80:19 | LL | s1.a * s2.a + s1.b * s2.c + s1.c * s2.c - | ^^^^^^^^^^^ help: I think you meant: `s1.b * s2.b` + | ^^^^^^^^^^^ help: did you mean: `s1.b * s2.b` -error: This sequence of operators looks suspiciously like a bug. +error: this sequence of operators looks suspiciously like a bug --> $DIR/suspicious_operation_groupings.rs:85:19 | LL | s1.a * s2.a + s2.b * s2.b + s1.c * s2.c - | ^^^^^^^^^^^ help: I think you meant: `s1.b * s2.b` + | ^^^^^^^^^^^ help: did you mean: `s1.b * s2.b` -error: This sequence of operators looks suspiciously like a bug. +error: this sequence of operators looks suspiciously like a bug --> $DIR/suspicious_operation_groupings.rs:90:19 | LL | s1.a * s2.a + s1.b * s1.b + s1.c * s2.c - | ^^^^^^^^^^^ help: I think you meant: `s1.b * s2.b` + | ^^^^^^^^^^^ help: did you mean: `s1.b * s2.b` -error: This sequence of operators looks suspiciously like a bug. +error: this sequence of operators looks suspiciously like a bug --> $DIR/suspicious_operation_groupings.rs:95:5 | LL | s1.a * s1.a + s1.b * s2.b + s1.c * s2.c - | ^^^^^^^^^^^ help: I think you meant: `s1.a * s2.a` + | ^^^^^^^^^^^ help: did you mean: `s1.a * s2.a` -error: This sequence of operators looks suspiciously like a bug. +error: this sequence of operators looks suspiciously like a bug --> $DIR/suspicious_operation_groupings.rs:100:33 | LL | s1.a * s2.a + s1.b * s2.b + s1.c * s1.c - | ^^^^^^^^^^^ help: I think you meant: `s1.c * s2.c` + | ^^^^^^^^^^^ help: did you mean: `s1.c * s2.c` -error: This sequence of operators looks suspiciously like a bug. +error: this sequence of operators looks suspiciously like a bug --> $DIR/suspicious_operation_groupings.rs:113:20 | LL | (s1.a * s2.a + s1.b * s1.b) - | ^^^^^^^^^^^ help: I think you meant: `s1.b * s2.b` + | ^^^^^^^^^^^ help: did you mean: `s1.b * s2.b` -error: This sequence of operators looks suspiciously like a bug. +error: this sequence of operators looks suspiciously like a bug --> $DIR/suspicious_operation_groupings.rs:118:34 | LL | (s1.a * s2.a + s1.b * s2.b + s1.c * s2.b + s1.d * s2.d) - | ^^^^^^^^^^^ help: I think you meant: `s1.c * s2.c` + | ^^^^^^^^^^^ help: did you mean: `s1.c * s2.c` -error: This sequence of operators looks suspiciously like a bug. +error: this sequence of operators looks suspiciously like a bug --> $DIR/suspicious_operation_groupings.rs:123:38 | LL | (s1.a * s2.a) + (s1.b * s2.b) + (s1.c * s2.b) + (s1.d * s2.d) - | ^^^^^^^^^^^ help: I think you meant: `s1.c * s2.c` + | ^^^^^^^^^^^ help: did you mean: `s1.c * s2.c` -error: This sequence of operators looks suspiciously like a bug. +error: this sequence of operators looks suspiciously like a bug --> $DIR/suspicious_operation_groupings.rs:128:39 | LL | ((s1.a * s2.a) + (s1.b * s2.b) + (s1.c * s2.b) + (s1.d * s2.d)) - | ^^^^^^^^^^^ help: I think you meant: `s1.c * s2.c` + | ^^^^^^^^^^^ help: did you mean: `s1.c * s2.c` -error: This sequence of operators looks suspiciously like a bug. +error: this sequence of operators looks suspiciously like a bug --> $DIR/suspicious_operation_groupings.rs:133:42 | LL | (((s1.a * s2.a) + (s1.b * s2.b)) + ((s1.c * s2.b) + (s1.d * s2.d))) - | ^^^^^^^^^^^ help: I think you meant: `s1.c * s2.c` + | ^^^^^^^^^^^ help: did you mean: `s1.c * s2.c` -error: This sequence of operators looks suspiciously like a bug. +error: this sequence of operators looks suspiciously like a bug --> $DIR/suspicious_operation_groupings.rs:133:42 | LL | (((s1.a * s2.a) + (s1.b * s2.b)) + ((s1.c * s2.b) + (s1.d * s2.d))) - | ^^^^^^^^^^^ help: I think you meant: `s1.c * s2.c` + | ^^^^^^^^^^^ help: did you mean: `s1.c * s2.c` -error: This sequence of operators looks suspiciously like a bug. +error: this sequence of operators looks suspiciously like a bug --> $DIR/suspicious_operation_groupings.rs:138:40 | LL | (((s1.a * s2.a) + (s1.b * s2.b) + (s1.c * s2.b)) + (s1.d * s2.d)) - | ^^^^^^^^^^^ help: I think you meant: `s1.c * s2.c` + | ^^^^^^^^^^^ help: did you mean: `s1.c * s2.c` -error: This sequence of operators looks suspiciously like a bug. +error: this sequence of operators looks suspiciously like a bug --> $DIR/suspicious_operation_groupings.rs:143:40 | LL | ((s1.a * s2.a) + ((s1.b * s2.b) + (s1.c * s2.b) + (s1.d * s2.d))) - | ^^^^^^^^^^^ help: I think you meant: `s1.c * s2.c` + | ^^^^^^^^^^^ help: did you mean: `s1.c * s2.c` -error: This sequence of operators looks suspiciously like a bug. +error: this sequence of operators looks suspiciously like a bug --> $DIR/suspicious_operation_groupings.rs:148:20 | LL | (s1.a * s2.a + s2.b * s2.b) / 2 - | ^^^^^^^^^^^ help: I think you meant: `s1.b * s2.b` + | ^^^^^^^^^^^ help: did you mean: `s1.b * s2.b` -error: This sequence of operators looks suspiciously like a bug. +error: this sequence of operators looks suspiciously like a bug --> $DIR/suspicious_operation_groupings.rs:153:35 | LL | i32::swap_bytes(s1.a * s2.a + s2.b * s2.b) - | ^^^^^^^^^^^ help: I think you meant: `s1.b * s2.b` + | ^^^^^^^^^^^ help: did you mean: `s1.b * s2.b` -error: This sequence of operators looks suspiciously like a bug. +error: this sequence of operators looks suspiciously like a bug --> $DIR/suspicious_operation_groupings.rs:158:29 | LL | s1.a > 0 && s1.b > 0 && s1.d == s2.c && s1.d == s2.d - | ^^^^^^^^^^^^ help: I think you meant: `s1.c == s2.c` + | ^^^^^^^^^^^^ help: did you mean: `s1.c == s2.c` -error: This sequence of operators looks suspiciously like a bug. +error: this sequence of operators looks suspiciously like a bug --> $DIR/suspicious_operation_groupings.rs:163:17 | LL | s1.a > 0 && s1.d == s2.c && s1.b > 0 && s1.d == s2.d - | ^^^^^^^^^^^^ help: I think you meant: `s1.c == s2.c` + | ^^^^^^^^^^^^ help: did you mean: `s1.c == s2.c` -error: This sequence of operators looks suspiciously like a bug. +error: this sequence of operators looks suspiciously like a bug --> $DIR/suspicious_operation_groupings.rs:172:77 | LL | (n1.inner.0).0 == (n2.inner.0).0 && (n1.inner.1).0 == (n2.inner.1).0 && (n1.inner.2).0 == (n2.inner.1).0 - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: I think you meant: `(n1.inner.2).0 == (n2.inner.2).0` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: did you mean: `(n1.inner.2).0 == (n2.inner.2).0` -error: This sequence of operators looks suspiciously like a bug. +error: this sequence of operators looks suspiciously like a bug --> $DIR/suspicious_operation_groupings.rs:186:25 | LL | s1.a <= s2.a && s1.a <= s2.b - | ^^^^^^^^^^^^ help: I think you meant: `s1.b <= s2.b` + | ^^^^^^^^^^^^ help: did you mean: `s1.b <= s2.b` -error: This sequence of operators looks suspiciously like a bug. +error: this sequence of operators looks suspiciously like a bug --> $DIR/suspicious_operation_groupings.rs:192:23 | LL | if s1.a < s2.a && s1.a < s2.b { - | ^^^^^^^^^^^ help: I think you meant: `s1.b < s2.b` + | ^^^^^^^^^^^ help: did you mean: `s1.b < s2.b` -error: This sequence of operators looks suspiciously like a bug. +error: this sequence of operators looks suspiciously like a bug --> $DIR/suspicious_operation_groupings.rs:199:48 | LL | -(-(-s1.a * -s2.a) + (-(-s1.b * -s2.b) + -(-s1.c * -s2.b) + -(-s1.d * -s2.d))) - | ^^^^^^^^^^^^^ help: I think you meant: `-s1.c * -s2.c` + | ^^^^^^^^^^^^^ help: did you mean: `-s1.c * -s2.c` -error: This sequence of operators looks suspiciously like a bug. +error: this sequence of operators looks suspiciously like a bug --> $DIR/suspicious_operation_groupings.rs:204:27 | LL | -(if -s1.a < -s2.a && -s1.a < -s2.b { s1.c } else { s2.a }) - | ^^^^^^^^^^^^^ help: I think you meant: `-s1.b < -s2.b` + | ^^^^^^^^^^^^^ help: did you mean: `-s1.b < -s2.b` error: aborting due to 27 previous errors diff --git a/src/tools/clippy/tests/ui/toplevel_ref_arg_non_rustfix.stderr b/src/tools/clippy/tests/ui/toplevel_ref_arg_non_rustfix.stderr index 6c36141a58c65..b8cfd98739497 100644 --- a/src/tools/clippy/tests/ui/toplevel_ref_arg_non_rustfix.stderr +++ b/src/tools/clippy/tests/ui/toplevel_ref_arg_non_rustfix.stderr @@ -1,4 +1,4 @@ -error: `ref` directly on a function argument is ignored. Consider using a reference type instead. +error: `ref` directly on a function argument is ignored. Consider using a reference type instead --> $DIR/toplevel_ref_arg_non_rustfix.rs:9:15 | LL | fn the_answer(ref mut x: u8) { @@ -6,7 +6,7 @@ LL | fn the_answer(ref mut x: u8) { | = note: `-D clippy::toplevel-ref-arg` implied by `-D warnings` -error: `ref` directly on a function argument is ignored. Consider using a reference type instead. +error: `ref` directly on a function argument is ignored. Consider using a reference type instead --> $DIR/toplevel_ref_arg_non_rustfix.rs:15:24 | LL | fn fun_example(ref _x: usize) {} diff --git a/src/tools/clippy/tests/ui/transmuting_null.stderr b/src/tools/clippy/tests/ui/transmuting_null.stderr index 05f91ee2adaa8..1848fc2490a00 100644 --- a/src/tools/clippy/tests/ui/transmuting_null.stderr +++ b/src/tools/clippy/tests/ui/transmuting_null.stderr @@ -1,4 +1,4 @@ -error: transmuting a known null pointer into a reference. +error: transmuting a known null pointer into a reference --> $DIR/transmuting_null.rs:10:23 | LL | let _: &u64 = std::mem::transmute(0 as *const u64); @@ -6,13 +6,13 @@ LL | let _: &u64 = std::mem::transmute(0 as *const u64); | = note: `-D clippy::transmuting-null` implied by `-D warnings` -error: transmuting a known null pointer into a reference. +error: transmuting a known null pointer into a reference --> $DIR/transmuting_null.rs:11:23 | LL | let _: &u64 = std::mem::transmute(std::ptr::null::<u64>()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: transmuting a known null pointer into a reference. +error: transmuting a known null pointer into a reference --> $DIR/transmuting_null.rs:21:23 | LL | let _: &u64 = std::mem::transmute(ZPTR); diff --git a/src/tools/clippy/tests/ui/complex_types.rs b/src/tools/clippy/tests/ui/type_complexity.rs similarity index 100% rename from src/tools/clippy/tests/ui/complex_types.rs rename to src/tools/clippy/tests/ui/type_complexity.rs diff --git a/src/tools/clippy/tests/ui/type_complexity.stderr b/src/tools/clippy/tests/ui/type_complexity.stderr new file mode 100644 index 0000000000000..7879233fdf283 --- /dev/null +++ b/src/tools/clippy/tests/ui/type_complexity.stderr @@ -0,0 +1,94 @@ +error: very complex type used. Consider factoring parts into `type` definitions + --> $DIR/type_complexity.rs:7:12 + | +LL | const CST: (u32, (u32, (u32, (u32, u32)))) = (0, (0, (0, (0, 0)))); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::type-complexity` implied by `-D warnings` + +error: very complex type used. Consider factoring parts into `type` definitions + --> $DIR/type_complexity.rs:8:12 + | +LL | static ST: (u32, (u32, (u32, (u32, u32)))) = (0, (0, (0, (0, 0)))); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: very complex type used. Consider factoring parts into `type` definitions + --> $DIR/type_complexity.rs:11:8 + | +LL | f: Vec<Vec<Box<(u32, u32, u32, u32)>>>, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: very complex type used. Consider factoring parts into `type` definitions + --> $DIR/type_complexity.rs:14:11 + | +LL | struct Ts(Vec<Vec<Box<(u32, u32, u32, u32)>>>); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: very complex type used. Consider factoring parts into `type` definitions + --> $DIR/type_complexity.rs:17:11 + | +LL | Tuple(Vec<Vec<Box<(u32, u32, u32, u32)>>>), + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: very complex type used. Consider factoring parts into `type` definitions + --> $DIR/type_complexity.rs:18:17 + | +LL | Struct { f: Vec<Vec<Box<(u32, u32, u32, u32)>>> }, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: very complex type used. Consider factoring parts into `type` definitions + --> $DIR/type_complexity.rs:22:14 + | +LL | const A: (u32, (u32, (u32, (u32, u32)))) = (0, (0, (0, (0, 0)))); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: very complex type used. Consider factoring parts into `type` definitions + --> $DIR/type_complexity.rs:23:30 + | +LL | fn impl_method(&self, p: Vec<Vec<Box<(u32, u32, u32, u32)>>>) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: very complex type used. Consider factoring parts into `type` definitions + --> $DIR/type_complexity.rs:27:14 + | +LL | const A: Vec<Vec<Box<(u32, u32, u32, u32)>>>; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: very complex type used. Consider factoring parts into `type` definitions + --> $DIR/type_complexity.rs:28:14 + | +LL | type B = Vec<Vec<Box<(u32, u32, u32, u32)>>>; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: very complex type used. Consider factoring parts into `type` definitions + --> $DIR/type_complexity.rs:29:25 + | +LL | fn method(&self, p: Vec<Vec<Box<(u32, u32, u32, u32)>>>); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: very complex type used. Consider factoring parts into `type` definitions + --> $DIR/type_complexity.rs:30:29 + | +LL | fn def_method(&self, p: Vec<Vec<Box<(u32, u32, u32, u32)>>>) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: very complex type used. Consider factoring parts into `type` definitions + --> $DIR/type_complexity.rs:33:15 + | +LL | fn test1() -> Vec<Vec<Box<(u32, u32, u32, u32)>>> { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: very complex type used. Consider factoring parts into `type` definitions + --> $DIR/type_complexity.rs:37:14 + | +LL | fn test2(_x: Vec<Vec<Box<(u32, u32, u32, u32)>>>) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: very complex type used. Consider factoring parts into `type` definitions + --> $DIR/type_complexity.rs:40:13 + | +LL | let _y: Vec<Vec<Box<(u32, u32, u32, u32)>>> = vec![]; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 15 previous errors + diff --git a/src/tools/clippy/tests/ui/unit_arg.rs b/src/tools/clippy/tests/ui/unit_arg.rs index b6a7bc5a1cc95..938cc3c785978 100644 --- a/src/tools/clippy/tests/ui/unit_arg.rs +++ b/src/tools/clippy/tests/ui/unit_arg.rs @@ -27,6 +27,30 @@ impl Bar { } } +fn baz<T: Debug>(t: T) { + foo(t); +} + +trait Tr { + type Args; + fn do_it(args: Self::Args); +} + +struct A; +impl Tr for A { + type Args = (); + fn do_it(_: Self::Args) {} +} + +struct B; +impl Tr for B { + type Args = <A as Tr>::Args; + + fn do_it(args: Self::Args) { + A::do_it(args) + } +} + fn bad() { foo({ 1; @@ -59,7 +83,7 @@ fn bad() { None.or(Some(foo(2))); // in this case, the suggestion can be inlined, no need for a surrounding block // foo(()); foo(()) instead of { foo(()); foo(()) } - foo(foo(())) + foo(foo(())); } fn ok() { @@ -71,6 +95,10 @@ fn ok() { b.bar({ 1 }); b.bar(()); question_mark(); + let named_unit_arg = (); + foo(named_unit_arg); + baz(()); + B::do_it(()); } fn question_mark() -> Result<(), ()> { diff --git a/src/tools/clippy/tests/ui/unit_arg.stderr b/src/tools/clippy/tests/ui/unit_arg.stderr index 094cff8c98591..354fd51cd6b60 100644 --- a/src/tools/clippy/tests/ui/unit_arg.stderr +++ b/src/tools/clippy/tests/ui/unit_arg.stderr @@ -1,5 +1,5 @@ error: passing a unit value to a function - --> $DIR/unit_arg.rs:31:5 + --> $DIR/unit_arg.rs:55:5 | LL | / foo({ LL | | 1; @@ -20,7 +20,7 @@ LL | foo(()); | error: passing a unit value to a function - --> $DIR/unit_arg.rs:34:5 + --> $DIR/unit_arg.rs:58:5 | LL | foo(foo(1)); | ^^^^^^^^^^^ @@ -32,7 +32,7 @@ LL | foo(()); | error: passing a unit value to a function - --> $DIR/unit_arg.rs:35:5 + --> $DIR/unit_arg.rs:59:5 | LL | / foo({ LL | | foo(1); @@ -54,7 +54,7 @@ LL | foo(()); | error: passing a unit value to a function - --> $DIR/unit_arg.rs:40:5 + --> $DIR/unit_arg.rs:64:5 | LL | / b.bar({ LL | | 1; @@ -74,7 +74,7 @@ LL | b.bar(()); | error: passing unit values to a function - --> $DIR/unit_arg.rs:43:5 + --> $DIR/unit_arg.rs:67:5 | LL | taking_multiple_units(foo(0), foo(1)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -87,7 +87,7 @@ LL | taking_multiple_units((), ()); | error: passing unit values to a function - --> $DIR/unit_arg.rs:44:5 + --> $DIR/unit_arg.rs:68:5 | LL | / taking_multiple_units(foo(0), { LL | | foo(1); @@ -110,7 +110,7 @@ LL | taking_multiple_units((), ()); | error: passing unit values to a function - --> $DIR/unit_arg.rs:48:5 + --> $DIR/unit_arg.rs:72:5 | LL | / taking_multiple_units( LL | | { @@ -140,7 +140,7 @@ LL | foo(2); ... error: passing a unit value to a function - --> $DIR/unit_arg.rs:59:13 + --> $DIR/unit_arg.rs:83:13 | LL | None.or(Some(foo(2))); | ^^^^^^^^^^^^ @@ -154,19 +154,19 @@ LL | }); | error: passing a unit value to a function - --> $DIR/unit_arg.rs:62:5 + --> $DIR/unit_arg.rs:86:5 | -LL | foo(foo(())) +LL | foo(foo(())); | ^^^^^^^^^^^^ | help: move the expression in front of the call and replace it with the unit literal `()` | LL | foo(()); -LL | foo(()) +LL | foo(()); | error: passing a unit value to a function - --> $DIR/unit_arg.rs:95:5 + --> $DIR/unit_arg.rs:123:5 | LL | Some(foo(1)) | ^^^^^^^^^^^^ diff --git a/src/tools/clippy/tests/ui/unnecessary_cast.stderr b/src/tools/clippy/tests/ui/unnecessary_cast.stderr index 8981d13e8eabb..70aa448af68ee 100644 --- a/src/tools/clippy/tests/ui/unnecessary_cast.stderr +++ b/src/tools/clippy/tests/ui/unnecessary_cast.stderr @@ -1,22 +1,22 @@ -error: casting to the same type is unnecessary (`i32` -> `i32`) +error: casting integer literal to `i32` is unnecessary --> $DIR/unnecessary_cast.rs:6:5 | LL | 1i32 as i32; - | ^^^^^^^^^^^ + | ^^^^^^^^^^^ help: try: `1_i32` | = note: `-D clippy::unnecessary-cast` implied by `-D warnings` -error: casting to the same type is unnecessary (`f32` -> `f32`) +error: casting float literal to `f32` is unnecessary --> $DIR/unnecessary_cast.rs:7:5 | LL | 1f32 as f32; - | ^^^^^^^^^^^ + | ^^^^^^^^^^^ help: try: `1_f32` error: casting to the same type is unnecessary (`bool` -> `bool`) --> $DIR/unnecessary_cast.rs:8:5 | LL | false as bool; - | ^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^ help: try: `false` error: aborting due to 3 previous errors diff --git a/src/tools/clippy/tests/ui/unnecessary_lazy_eval.stderr b/src/tools/clippy/tests/ui/unnecessary_lazy_eval.stderr index 44dcd0cafbb6e..cc94bd5cd9e16 100644 --- a/src/tools/clippy/tests/ui/unnecessary_lazy_eval.stderr +++ b/src/tools/clippy/tests/ui/unnecessary_lazy_eval.stderr @@ -2,7 +2,7 @@ error: unnecessary closure used to substitute value for `Option::None` --> $DIR/unnecessary_lazy_eval.rs:35:13 | LL | let _ = opt.unwrap_or_else(|| 2); - | ^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `unwrap_or` instead: `opt.unwrap_or(2)` + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: use `unwrap_or` instead: `opt.unwrap_or(2)` | = note: `-D clippy::unnecessary-lazy-evaluations` implied by `-D warnings` @@ -10,187 +10,187 @@ error: unnecessary closure used to substitute value for `Option::None` --> $DIR/unnecessary_lazy_eval.rs:36:13 | LL | let _ = opt.unwrap_or_else(|| astronomers_pi); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `unwrap_or` instead: `opt.unwrap_or(astronomers_pi)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `unwrap_or` instead: `opt.unwrap_or(astronomers_pi)` error: unnecessary closure used to substitute value for `Option::None` --> $DIR/unnecessary_lazy_eval.rs:37:13 | LL | let _ = opt.unwrap_or_else(|| ext_str.some_field); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `unwrap_or` instead: `opt.unwrap_or(ext_str.some_field)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `unwrap_or` instead: `opt.unwrap_or(ext_str.some_field)` error: unnecessary closure used to substitute value for `Option::None` --> $DIR/unnecessary_lazy_eval.rs:39:13 | LL | let _ = opt.and_then(|_| ext_opt); - | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `and` instead: `opt.and(ext_opt)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `and` instead: `opt.and(ext_opt)` error: unnecessary closure used to substitute value for `Option::None` --> $DIR/unnecessary_lazy_eval.rs:40:13 | LL | let _ = opt.or_else(|| ext_opt); - | ^^^^^^^^^^^^^^^^^^^^^^^ help: Use `or` instead: `opt.or(ext_opt)` + | ^^^^^^^^^^^^^^^^^^^^^^^ help: use `or` instead: `opt.or(ext_opt)` error: unnecessary closure used to substitute value for `Option::None` --> $DIR/unnecessary_lazy_eval.rs:41:13 | LL | let _ = opt.or_else(|| None); - | ^^^^^^^^^^^^^^^^^^^^ help: Use `or` instead: `opt.or(None)` + | ^^^^^^^^^^^^^^^^^^^^ help: use `or` instead: `opt.or(None)` error: unnecessary closure used to substitute value for `Option::None` --> $DIR/unnecessary_lazy_eval.rs:42:13 | LL | let _ = opt.get_or_insert_with(|| 2); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `get_or_insert` instead: `opt.get_or_insert(2)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `get_or_insert` instead: `opt.get_or_insert(2)` error: unnecessary closure used to substitute value for `Option::None` --> $DIR/unnecessary_lazy_eval.rs:43:13 | LL | let _ = opt.ok_or_else(|| 2); - | ^^^^^^^^^^^^^^^^^^^^ help: Use `ok_or` instead: `opt.ok_or(2)` + | ^^^^^^^^^^^^^^^^^^^^ help: use `ok_or` instead: `opt.ok_or(2)` error: unnecessary closure used to substitute value for `Option::None` --> $DIR/unnecessary_lazy_eval.rs:44:13 | LL | let _ = nested_tuple_opt.unwrap_or_else(|| Some((1, 2))); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `unwrap_or` instead: `nested_tuple_opt.unwrap_or(Some((1, 2)))` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `unwrap_or` instead: `nested_tuple_opt.unwrap_or(Some((1, 2)))` error: unnecessary closure used to substitute value for `Option::None` --> $DIR/unnecessary_lazy_eval.rs:47:13 | LL | let _ = Some(10).unwrap_or_else(|| 2); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `unwrap_or` instead: `Some(10).unwrap_or(2)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `unwrap_or` instead: `Some(10).unwrap_or(2)` error: unnecessary closure used to substitute value for `Option::None` --> $DIR/unnecessary_lazy_eval.rs:48:13 | LL | let _ = Some(10).and_then(|_| ext_opt); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `and` instead: `Some(10).and(ext_opt)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `and` instead: `Some(10).and(ext_opt)` error: unnecessary closure used to substitute value for `Option::None` --> $DIR/unnecessary_lazy_eval.rs:49:28 | LL | let _: Option<usize> = None.or_else(|| ext_opt); - | ^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `or` instead: `None.or(ext_opt)` + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: use `or` instead: `None.or(ext_opt)` error: unnecessary closure used to substitute value for `Option::None` --> $DIR/unnecessary_lazy_eval.rs:50:13 | LL | let _ = None.get_or_insert_with(|| 2); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `get_or_insert` instead: `None.get_or_insert(2)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `get_or_insert` instead: `None.get_or_insert(2)` error: unnecessary closure used to substitute value for `Option::None` --> $DIR/unnecessary_lazy_eval.rs:51:35 | LL | let _: Result<usize, usize> = None.ok_or_else(|| 2); - | ^^^^^^^^^^^^^^^^^^^^^ help: Use `ok_or` instead: `None.ok_or(2)` + | ^^^^^^^^^^^^^^^^^^^^^ help: use `ok_or` instead: `None.ok_or(2)` error: unnecessary closure used to substitute value for `Option::None` --> $DIR/unnecessary_lazy_eval.rs:52:28 | LL | let _: Option<usize> = None.or_else(|| None); - | ^^^^^^^^^^^^^^^^^^^^^ help: Use `or` instead: `None.or(None)` + | ^^^^^^^^^^^^^^^^^^^^^ help: use `or` instead: `None.or(None)` error: unnecessary closure used to substitute value for `Option::None` --> $DIR/unnecessary_lazy_eval.rs:55:13 | LL | let _ = deep.0.unwrap_or_else(|| 2); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `unwrap_or` instead: `deep.0.unwrap_or(2)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `unwrap_or` instead: `deep.0.unwrap_or(2)` error: unnecessary closure used to substitute value for `Option::None` --> $DIR/unnecessary_lazy_eval.rs:56:13 | LL | let _ = deep.0.and_then(|_| ext_opt); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `and` instead: `deep.0.and(ext_opt)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `and` instead: `deep.0.and(ext_opt)` error: unnecessary closure used to substitute value for `Option::None` --> $DIR/unnecessary_lazy_eval.rs:57:13 | LL | let _ = deep.0.or_else(|| None); - | ^^^^^^^^^^^^^^^^^^^^^^^ help: Use `or` instead: `deep.0.or(None)` + | ^^^^^^^^^^^^^^^^^^^^^^^ help: use `or` instead: `deep.0.or(None)` error: unnecessary closure used to substitute value for `Option::None` --> $DIR/unnecessary_lazy_eval.rs:58:13 | LL | let _ = deep.0.get_or_insert_with(|| 2); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `get_or_insert` instead: `deep.0.get_or_insert(2)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `get_or_insert` instead: `deep.0.get_or_insert(2)` error: unnecessary closure used to substitute value for `Option::None` --> $DIR/unnecessary_lazy_eval.rs:59:13 | LL | let _ = deep.0.ok_or_else(|| 2); - | ^^^^^^^^^^^^^^^^^^^^^^^ help: Use `ok_or` instead: `deep.0.ok_or(2)` + | ^^^^^^^^^^^^^^^^^^^^^^^ help: use `ok_or` instead: `deep.0.ok_or(2)` error: unnecessary closure used to substitute value for `Option::None` --> $DIR/unnecessary_lazy_eval.rs:79:28 | LL | let _: Option<usize> = None.or_else(|| Some(3)); - | ^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `or` instead: `None.or(Some(3))` + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: use `or` instead: `None.or(Some(3))` error: unnecessary closure used to substitute value for `Option::None` --> $DIR/unnecessary_lazy_eval.rs:80:13 | LL | let _ = deep.0.or_else(|| Some(3)); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `or` instead: `deep.0.or(Some(3))` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `or` instead: `deep.0.or(Some(3))` error: unnecessary closure used to substitute value for `Option::None` --> $DIR/unnecessary_lazy_eval.rs:81:13 | LL | let _ = opt.or_else(|| Some(3)); - | ^^^^^^^^^^^^^^^^^^^^^^^ help: Use `or` instead: `opt.or(Some(3))` + | ^^^^^^^^^^^^^^^^^^^^^^^ help: use `or` instead: `opt.or(Some(3))` error: unnecessary closure used to substitute value for `Result::Err` --> $DIR/unnecessary_lazy_eval.rs:87:13 | LL | let _ = res2.unwrap_or_else(|_| 2); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `unwrap_or` instead: `res2.unwrap_or(2)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `unwrap_or` instead: `res2.unwrap_or(2)` error: unnecessary closure used to substitute value for `Result::Err` --> $DIR/unnecessary_lazy_eval.rs:88:13 | LL | let _ = res2.unwrap_or_else(|_| astronomers_pi); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `unwrap_or` instead: `res2.unwrap_or(astronomers_pi)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `unwrap_or` instead: `res2.unwrap_or(astronomers_pi)` error: unnecessary closure used to substitute value for `Result::Err` --> $DIR/unnecessary_lazy_eval.rs:89:13 | LL | let _ = res2.unwrap_or_else(|_| ext_str.some_field); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `unwrap_or` instead: `res2.unwrap_or(ext_str.some_field)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `unwrap_or` instead: `res2.unwrap_or(ext_str.some_field)` error: unnecessary closure used to substitute value for `Result::Err` --> $DIR/unnecessary_lazy_eval.rs:111:35 | LL | let _: Result<usize, usize> = res.and_then(|_| Err(2)); - | ^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `and` instead: `res.and(Err(2))` + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: use `and` instead: `res.and(Err(2))` error: unnecessary closure used to substitute value for `Result::Err` --> $DIR/unnecessary_lazy_eval.rs:112:35 | LL | let _: Result<usize, usize> = res.and_then(|_| Err(astronomers_pi)); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `and` instead: `res.and(Err(astronomers_pi))` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `and` instead: `res.and(Err(astronomers_pi))` error: unnecessary closure used to substitute value for `Result::Err` --> $DIR/unnecessary_lazy_eval.rs:113:35 | LL | let _: Result<usize, usize> = res.and_then(|_| Err(ext_str.some_field)); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `and` instead: `res.and(Err(ext_str.some_field))` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `and` instead: `res.and(Err(ext_str.some_field))` error: unnecessary closure used to substitute value for `Result::Err` --> $DIR/unnecessary_lazy_eval.rs:115:35 | LL | let _: Result<usize, usize> = res.or_else(|_| Ok(2)); - | ^^^^^^^^^^^^^^^^^^^^^^ help: Use `or` instead: `res.or(Ok(2))` + | ^^^^^^^^^^^^^^^^^^^^^^ help: use `or` instead: `res.or(Ok(2))` error: unnecessary closure used to substitute value for `Result::Err` --> $DIR/unnecessary_lazy_eval.rs:116:35 | LL | let _: Result<usize, usize> = res.or_else(|_| Ok(astronomers_pi)); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `or` instead: `res.or(Ok(astronomers_pi))` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `or` instead: `res.or(Ok(astronomers_pi))` error: unnecessary closure used to substitute value for `Result::Err` --> $DIR/unnecessary_lazy_eval.rs:117:35 | LL | let _: Result<usize, usize> = res.or_else(|_| Ok(ext_str.some_field)); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `or` instead: `res.or(Ok(ext_str.some_field))` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `or` instead: `res.or(Ok(ext_str.some_field))` error: aborting due to 32 previous errors diff --git a/src/tools/clippy/tests/ui/unnecessary_lazy_eval_unfixable.stderr b/src/tools/clippy/tests/ui/unnecessary_lazy_eval_unfixable.stderr index 581d641cbf54b..75674b0a9d20a 100644 --- a/src/tools/clippy/tests/ui/unnecessary_lazy_eval_unfixable.stderr +++ b/src/tools/clippy/tests/ui/unnecessary_lazy_eval_unfixable.stderr @@ -2,7 +2,7 @@ error: unnecessary closure used to substitute value for `Result::Err` --> $DIR/unnecessary_lazy_eval_unfixable.rs:12:13 | LL | let _ = Ok(1).unwrap_or_else(|()| 2); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `unwrap_or` instead: `Ok(1).unwrap_or(2)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `unwrap_or` instead: `Ok(1).unwrap_or(2)` | = note: `-D clippy::unnecessary-lazy-evaluations` implied by `-D warnings` @@ -10,13 +10,13 @@ error: unnecessary closure used to substitute value for `Result::Err` --> $DIR/unnecessary_lazy_eval_unfixable.rs:16:13 | LL | let _ = Ok(1).unwrap_or_else(|e::E| 2); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `unwrap_or` instead: `Ok(1).unwrap_or(2)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `unwrap_or` instead: `Ok(1).unwrap_or(2)` error: unnecessary closure used to substitute value for `Result::Err` --> $DIR/unnecessary_lazy_eval_unfixable.rs:17:13 | LL | let _ = Ok(1).unwrap_or_else(|SomeStruct { .. }| 2); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `unwrap_or` instead: `Ok(1).unwrap_or(2)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `unwrap_or` instead: `Ok(1).unwrap_or(2)` error: aborting due to 3 previous errors diff --git a/src/tools/clippy/tests/ui/unnecessary_wraps.rs b/src/tools/clippy/tests/ui/unnecessary_wraps.rs index a510263e67da1..54f22e3ee6a4a 100644 --- a/src/tools/clippy/tests/ui/unnecessary_wraps.rs +++ b/src/tools/clippy/tests/ui/unnecessary_wraps.rs @@ -22,29 +22,17 @@ fn func2(a: bool, b: bool) -> Option<i32> { if a && b { return Some(10); } - if a { - Some(20) - } else { - Some(30) - } + if a { Some(20) } else { Some(30) } } // public fns should not be linted pub fn func3(a: bool) -> Option<i32> { - if a { - Some(1) - } else { - Some(1) - } + if a { Some(1) } else { Some(1) } } // should not be linted fn func4(a: bool) -> Option<i32> { - if a { - Some(1) - } else { - None - } + if a { Some(1) } else { None } } // should be linted @@ -64,11 +52,7 @@ fn func7() -> Result<i32, ()> { // should not be linted fn func8(a: bool) -> Result<i32, ()> { - if a { - Ok(1) - } else { - Err(()) - } + if a { Ok(1) } else { Err(()) } } // should not be linted @@ -143,20 +127,12 @@ fn issue_6640_2(a: bool, b: bool) -> Result<(), i32> { // should not be linted fn issue_6640_3() -> Option<()> { - if true { - Some(()) - } else { - None - } + if true { Some(()) } else { None } } // should not be linted fn issue_6640_4() -> Result<(), ()> { - if true { - Ok(()) - } else { - Err(()) - } + if true { Ok(()) } else { Err(()) } } fn main() { diff --git a/src/tools/clippy/tests/ui/unnecessary_wraps.stderr b/src/tools/clippy/tests/ui/unnecessary_wraps.stderr index 9a861c61a4679..0e570397e2a29 100644 --- a/src/tools/clippy/tests/ui/unnecessary_wraps.stderr +++ b/src/tools/clippy/tests/ui/unnecessary_wraps.stderr @@ -32,8 +32,7 @@ LL | / fn func2(a: bool, b: bool) -> Option<i32> { LL | | if a && b { LL | | return Some(10); LL | | } -... | -LL | | } +LL | | if a { Some(20) } else { Some(30) } LL | | } | |_^ | @@ -45,14 +44,11 @@ help: ...and then change returning expressions | LL | return 10; LL | } -LL | if a { -LL | 20 -LL | } else { -LL | 30 +LL | if a { 20 } else { 30 } | error: this function's return value is unnecessarily wrapped by `Option` - --> $DIR/unnecessary_wraps.rs:51:1 + --> $DIR/unnecessary_wraps.rs:39:1 | LL | / fn func5() -> Option<i32> { LL | | Some(1) @@ -69,7 +65,7 @@ LL | 1 | error: this function's return value is unnecessarily wrapped by `Result` - --> $DIR/unnecessary_wraps.rs:61:1 + --> $DIR/unnecessary_wraps.rs:49:1 | LL | / fn func7() -> Result<i32, ()> { LL | | Ok(1) @@ -86,7 +82,7 @@ LL | 1 | error: this function's return value is unnecessarily wrapped by `Option` - --> $DIR/unnecessary_wraps.rs:93:5 + --> $DIR/unnecessary_wraps.rs:77:5 | LL | / fn func12() -> Option<i32> { LL | | Some(1) @@ -103,7 +99,7 @@ LL | 1 | error: this function's return value is unnecessary - --> $DIR/unnecessary_wraps.rs:120:1 + --> $DIR/unnecessary_wraps.rs:104:1 | LL | / fn issue_6640_1(a: bool, b: bool) -> Option<()> { LL | | if a && b { @@ -129,7 +125,7 @@ LL | } else { ... error: this function's return value is unnecessary - --> $DIR/unnecessary_wraps.rs:133:1 + --> $DIR/unnecessary_wraps.rs:117:1 | LL | / fn issue_6640_2(a: bool, b: bool) -> Result<(), i32> { LL | | if a && b { diff --git a/src/tools/clippy/tests/ui/unnested_or_patterns.fixed b/src/tools/clippy/tests/ui/unnested_or_patterns.fixed index 13a036cd800bc..46463a29e9b20 100644 --- a/src/tools/clippy/tests/ui/unnested_or_patterns.fixed +++ b/src/tools/clippy/tests/ui/unnested_or_patterns.fixed @@ -1,6 +1,5 @@ // run-rustfix -#![feature(or_patterns)] #![feature(box_patterns)] #![warn(clippy::unnested_or_patterns)] #![allow(clippy::cognitive_complexity, clippy::match_ref_pats, clippy::upper_case_acronyms)] diff --git a/src/tools/clippy/tests/ui/unnested_or_patterns.rs b/src/tools/clippy/tests/ui/unnested_or_patterns.rs index 4a10cc702c401..8ce0738bfc27b 100644 --- a/src/tools/clippy/tests/ui/unnested_or_patterns.rs +++ b/src/tools/clippy/tests/ui/unnested_or_patterns.rs @@ -1,6 +1,5 @@ // run-rustfix -#![feature(or_patterns)] #![feature(box_patterns)] #![warn(clippy::unnested_or_patterns)] #![allow(clippy::cognitive_complexity, clippy::match_ref_pats, clippy::upper_case_acronyms)] diff --git a/src/tools/clippy/tests/ui/unnested_or_patterns.stderr b/src/tools/clippy/tests/ui/unnested_or_patterns.stderr index 1899dc657dfee..f7cb513c15c90 100644 --- a/src/tools/clippy/tests/ui/unnested_or_patterns.stderr +++ b/src/tools/clippy/tests/ui/unnested_or_patterns.stderr @@ -1,5 +1,5 @@ error: unnested or-patterns - --> $DIR/unnested_or_patterns.rs:10:12 + --> $DIR/unnested_or_patterns.rs:9:12 | LL | if let box 0 | box 2 = Box::new(0) {} | ^^^^^^^^^^^^^ @@ -11,7 +11,7 @@ LL | if let box (0 | 2) = Box::new(0) {} | ^^^^^^^^^^^ error: unnested or-patterns - --> $DIR/unnested_or_patterns.rs:11:12 + --> $DIR/unnested_or_patterns.rs:10:12 | LL | if let box ((0 | 1)) | box (2 | 3) | box 4 = Box::new(0) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -22,7 +22,7 @@ LL | if let box (0 | 1 | 2 | 3 | 4) = Box::new(0) {} | ^^^^^^^^^^^^^^^^^^^^^^^ error: unnested or-patterns - --> $DIR/unnested_or_patterns.rs:13:12 + --> $DIR/unnested_or_patterns.rs:12:12 | LL | if let &0 | C0 | &2 = &0 {} | ^^^^^^^^^^^^ @@ -33,7 +33,7 @@ LL | if let &(0 | 2) | C0 = &0 {} | ^^^^^^^^^^^^^ error: unnested or-patterns - --> $DIR/unnested_or_patterns.rs:14:12 + --> $DIR/unnested_or_patterns.rs:13:12 | LL | if let &mut 0 | &mut 2 = &mut 0 {} | ^^^^^^^^^^^^^^^ @@ -44,7 +44,7 @@ LL | if let &mut (0 | 2) = &mut 0 {} | ^^^^^^^^^^^^ error: unnested or-patterns - --> $DIR/unnested_or_patterns.rs:15:12 + --> $DIR/unnested_or_patterns.rs:14:12 | LL | if let x @ 0 | x @ 2 = 0 {} | ^^^^^^^^^^^^^ @@ -55,7 +55,7 @@ LL | if let x @ (0 | 2) = 0 {} | ^^^^^^^^^^^ error: unnested or-patterns - --> $DIR/unnested_or_patterns.rs:16:12 + --> $DIR/unnested_or_patterns.rs:15:12 | LL | if let (0, 1) | (0, 2) | (0, 3) = (0, 0) {} | ^^^^^^^^^^^^^^^^^^^^^^^^ @@ -66,7 +66,7 @@ LL | if let (0, 1 | 2 | 3) = (0, 0) {} | ^^^^^^^^^^^^^^ error: unnested or-patterns - --> $DIR/unnested_or_patterns.rs:17:12 + --> $DIR/unnested_or_patterns.rs:16:12 | LL | if let (1, 0) | (2, 0) | (3, 0) = (0, 0) {} | ^^^^^^^^^^^^^^^^^^^^^^^^ @@ -77,7 +77,7 @@ LL | if let (1 | 2 | 3, 0) = (0, 0) {} | ^^^^^^^^^^^^^^ error: unnested or-patterns - --> $DIR/unnested_or_patterns.rs:18:12 + --> $DIR/unnested_or_patterns.rs:17:12 | LL | if let (x, ..) | (x, 1) | (x, 2) = (0, 1) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -88,7 +88,7 @@ LL | if let (x, ..) | (x, 1 | 2) = (0, 1) {} | ^^^^^^^^^^^^^^^^^^^^ error: unnested or-patterns - --> $DIR/unnested_or_patterns.rs:19:12 + --> $DIR/unnested_or_patterns.rs:18:12 | LL | if let [0] | [1] = [0] {} | ^^^^^^^^^ @@ -99,7 +99,7 @@ LL | if let [0 | 1] = [0] {} | ^^^^^^^ error: unnested or-patterns - --> $DIR/unnested_or_patterns.rs:20:12 + --> $DIR/unnested_or_patterns.rs:19:12 | LL | if let [x, 0] | [x, 1] = [0, 1] {} | ^^^^^^^^^^^^^^^ @@ -110,7 +110,7 @@ LL | if let [x, 0 | 1] = [0, 1] {} | ^^^^^^^^^^ error: unnested or-patterns - --> $DIR/unnested_or_patterns.rs:21:12 + --> $DIR/unnested_or_patterns.rs:20:12 | LL | if let [x, 0] | [x, 1] | [x, 2] = [0, 1] {} | ^^^^^^^^^^^^^^^^^^^^^^^^ @@ -121,7 +121,7 @@ LL | if let [x, 0 | 1 | 2] = [0, 1] {} | ^^^^^^^^^^^^^^ error: unnested or-patterns - --> $DIR/unnested_or_patterns.rs:22:12 + --> $DIR/unnested_or_patterns.rs:21:12 | LL | if let [x, ..] | [x, 1] | [x, 2] = [0, 1] {} | ^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -132,7 +132,7 @@ LL | if let [x, ..] | [x, 1 | 2] = [0, 1] {} | ^^^^^^^^^^^^^^^^^^^^ error: unnested or-patterns - --> $DIR/unnested_or_patterns.rs:24:12 + --> $DIR/unnested_or_patterns.rs:23:12 | LL | if let TS(0, x) | TS(1, x) = TS(0, 0) {} | ^^^^^^^^^^^^^^^^^^^ @@ -143,7 +143,7 @@ LL | if let TS(0 | 1, x) = TS(0, 0) {} | ^^^^^^^^^^^^ error: unnested or-patterns - --> $DIR/unnested_or_patterns.rs:25:12 + --> $DIR/unnested_or_patterns.rs:24:12 | LL | if let TS(1, 0) | TS(2, 0) | TS(3, 0) = TS(0, 0) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -154,7 +154,7 @@ LL | if let TS(1 | 2 | 3, 0) = TS(0, 0) {} | ^^^^^^^^^^^^^^^^ error: unnested or-patterns - --> $DIR/unnested_or_patterns.rs:26:12 + --> $DIR/unnested_or_patterns.rs:25:12 | LL | if let TS(x, ..) | TS(x, 1) | TS(x, 2) = TS(0, 0) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -165,7 +165,7 @@ LL | if let TS(x, ..) | TS(x, 1 | 2) = TS(0, 0) {} | ^^^^^^^^^^^^^^^^^^^^^^^^ error: unnested or-patterns - --> $DIR/unnested_or_patterns.rs:31:12 + --> $DIR/unnested_or_patterns.rs:30:12 | LL | if let S { x: 0, y } | S { y, x: 1 } = (S { x: 0, y: 1 }) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/src/tools/clippy/tests/ui/unnested_or_patterns2.fixed b/src/tools/clippy/tests/ui/unnested_or_patterns2.fixed index 02a129c55a3f5..d3539d798157b 100644 --- a/src/tools/clippy/tests/ui/unnested_or_patterns2.fixed +++ b/src/tools/clippy/tests/ui/unnested_or_patterns2.fixed @@ -1,6 +1,5 @@ // run-rustfix -#![feature(or_patterns)] #![feature(box_patterns)] #![warn(clippy::unnested_or_patterns)] #![allow(clippy::cognitive_complexity, clippy::match_ref_pats)] diff --git a/src/tools/clippy/tests/ui/unnested_or_patterns2.rs b/src/tools/clippy/tests/ui/unnested_or_patterns2.rs index acf3158989dcc..9cea5cdea6997 100644 --- a/src/tools/clippy/tests/ui/unnested_or_patterns2.rs +++ b/src/tools/clippy/tests/ui/unnested_or_patterns2.rs @@ -1,6 +1,5 @@ // run-rustfix -#![feature(or_patterns)] #![feature(box_patterns)] #![warn(clippy::unnested_or_patterns)] #![allow(clippy::cognitive_complexity, clippy::match_ref_pats)] diff --git a/src/tools/clippy/tests/ui/unnested_or_patterns2.stderr b/src/tools/clippy/tests/ui/unnested_or_patterns2.stderr index 1847fd8e098c7..9042c9c00b1ac 100644 --- a/src/tools/clippy/tests/ui/unnested_or_patterns2.stderr +++ b/src/tools/clippy/tests/ui/unnested_or_patterns2.stderr @@ -1,5 +1,5 @@ error: unnested or-patterns - --> $DIR/unnested_or_patterns2.rs:10:12 + --> $DIR/unnested_or_patterns2.rs:9:12 | LL | if let Some(Some(0)) | Some(Some(1)) = None {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -11,7 +11,7 @@ LL | if let Some(Some(0 | 1)) = None {} | ^^^^^^^^^^^^^^^^^ error: unnested or-patterns - --> $DIR/unnested_or_patterns2.rs:11:12 + --> $DIR/unnested_or_patterns2.rs:10:12 | LL | if let Some(Some(0)) | Some(Some(1) | Some(2)) = None {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -22,7 +22,7 @@ LL | if let Some(Some(0 | 1 | 2)) = None {} | ^^^^^^^^^^^^^^^^^^^^^ error: unnested or-patterns - --> $DIR/unnested_or_patterns2.rs:12:12 + --> $DIR/unnested_or_patterns2.rs:11:12 | LL | if let Some(Some(0 | 1) | Some(2)) | Some(Some(3) | Some(4)) = None {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -33,7 +33,7 @@ LL | if let Some(Some(0 | 1 | 2 | 3 | 4)) = None {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: unnested or-patterns - --> $DIR/unnested_or_patterns2.rs:13:12 + --> $DIR/unnested_or_patterns2.rs:12:12 | LL | if let Some(Some(0) | Some(1 | 2)) = None {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -44,7 +44,7 @@ LL | if let Some(Some(0 | 1 | 2)) = None {} | ^^^^^^^^^^^^^^^^^^^^^ error: unnested or-patterns - --> $DIR/unnested_or_patterns2.rs:14:12 + --> $DIR/unnested_or_patterns2.rs:13:12 | LL | if let ((0,),) | ((1,) | (2,),) = ((0,),) {} | ^^^^^^^^^^^^^^^^^^^^^^^^ @@ -55,7 +55,7 @@ LL | if let ((0 | 1 | 2,),) = ((0,),) {} | ^^^^^^^^^^^^^^^ error: unnested or-patterns - --> $DIR/unnested_or_patterns2.rs:15:12 + --> $DIR/unnested_or_patterns2.rs:14:12 | LL | if let 0 | (1 | 2) = 0 {} | ^^^^^^^^^^^ @@ -66,7 +66,7 @@ LL | if let 0 | 1 | 2 = 0 {} | ^^^^^^^^^ error: unnested or-patterns - --> $DIR/unnested_or_patterns2.rs:16:12 + --> $DIR/unnested_or_patterns2.rs:15:12 | LL | if let box (0 | 1) | (box 2 | box (3 | 4)) = Box::new(0) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -77,7 +77,7 @@ LL | if let box (0 | 1 | 2 | 3 | 4) = Box::new(0) {} | ^^^^^^^^^^^^^^^^^^^^^^^ error: unnested or-patterns - --> $DIR/unnested_or_patterns2.rs:17:12 + --> $DIR/unnested_or_patterns2.rs:16:12 | LL | if let box box 0 | box (box 2 | box 4) = Box::new(Box::new(0)) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/src/tools/clippy/tests/ui/unnested_or_patterns3.rs b/src/tools/clippy/tests/ui/unnested_or_patterns3.rs deleted file mode 100644 index 6bd35057bfad1..0000000000000 --- a/src/tools/clippy/tests/ui/unnested_or_patterns3.rs +++ /dev/null @@ -1,6 +0,0 @@ -#![warn(clippy::unnested_or_patterns)] - -// Test that `unnested_or_patterns` does not trigger without enabling `or_patterns` -fn main() { - if let (0, 1) | (0, 2) | (0, 3) = (0, 0) {} -} diff --git a/src/tools/clippy/tests/ui/upper_case_acronyms.rs b/src/tools/clippy/tests/ui/upper_case_acronyms.rs index fdf8905f812f6..48bb9e54b122b 100644 --- a/src/tools/clippy/tests/ui/upper_case_acronyms.rs +++ b/src/tools/clippy/tests/ui/upper_case_acronyms.rs @@ -16,7 +16,26 @@ enum Flags { FIN, } -struct GCCLLVMSomething; // linted with cfg option, beware that lint suggests `GccllvmSomething` instead of - // `GccLlvmSomething` +// linted with cfg option, beware that lint suggests `GccllvmSomething` instead of +// `GccLlvmSomething` +struct GCCLLVMSomething; + +// public items must not be linted +pub struct NOWARNINGHERE; +pub struct ALSONoWarningHERE; + +// enum variants should not be linted if the num is pub +pub enum ParseError<T> { + YDB(u8), + Utf8(std::string::FromUtf8Error), + Parse(T, String), +} + +// private, do lint here +enum ParseErrorPrivate<T> { + WASD(u8), + Utf8(std::string::FromUtf8Error), + Parse(T, String), +} fn main() {} diff --git a/src/tools/clippy/tests/ui/upper_case_acronyms.stderr b/src/tools/clippy/tests/ui/upper_case_acronyms.stderr index bbe38991e5271..250b196a99eb2 100644 --- a/src/tools/clippy/tests/ui/upper_case_acronyms.stderr +++ b/src/tools/clippy/tests/ui/upper_case_acronyms.stderr @@ -48,5 +48,11 @@ error: name `FIN` contains a capitalized acronym LL | FIN, | ^^^ help: consider making the acronym lowercase, except the initial letter: `Fin` -error: aborting due to 8 previous errors +error: name `WASD` contains a capitalized acronym + --> $DIR/upper_case_acronyms.rs:36:5 + | +LL | WASD(u8), + | ^^^^ help: consider making the acronym lowercase, except the initial letter: `Wasd` + +error: aborting due to 9 previous errors diff --git a/src/tools/clippy/tests/ui/use_self.fixed b/src/tools/clippy/tests/ui/use_self.fixed index 95e7bc754310f..1282befdfb36b 100644 --- a/src/tools/clippy/tests/ui/use_self.fixed +++ b/src/tools/clippy/tests/ui/use_self.fixed @@ -75,13 +75,13 @@ mod lifetimes { mod issue2894 { trait IntoBytes { - fn to_bytes(&self) -> Vec<u8>; + fn to_bytes(self) -> Vec<u8>; } // This should not be linted impl IntoBytes for u8 { - fn to_bytes(&self) -> Vec<u8> { - vec![*self] + fn to_bytes(self) -> Vec<u8> { + vec![self] } } } @@ -312,28 +312,25 @@ mod issue4140 { fn try_from(value: T) -> Result<Self, Error<Self::From, Self::To>>; } - impl<F, T> TryFrom<F> for T - where - T: From<F>, - { - type From = Self; - type To = Self; + // FIXME: Suggested fix results in infinite recursion. + // impl<F, T> TryFrom<F> for T + // where + // T: From<F>, + // { + // type From = Self::From; + // type To = Self::To; - fn try_from(value: F) -> Result<Self, Error<Self::From, Self::To>> { - Ok(From::from(value)) - } - } + // fn try_from(value: F) -> Result<Self, Error<Self::From, Self::To>> { + // Ok(From::from(value)) + // } + // } impl From<bool> for i64 { type From = bool; type To = Self; fn from(value: bool) -> Self { - if value { - 100 - } else { - 0 - } + if value { 100 } else { 0 } } } } @@ -458,3 +455,10 @@ mod nested_paths { } } } + +mod issue6818 { + #[derive(serde::Deserialize)] + struct A { + a: i32, + } +} diff --git a/src/tools/clippy/tests/ui/use_self.rs b/src/tools/clippy/tests/ui/use_self.rs index 75424f341597d..7aaac7b2414e6 100644 --- a/src/tools/clippy/tests/ui/use_self.rs +++ b/src/tools/clippy/tests/ui/use_self.rs @@ -75,13 +75,13 @@ mod lifetimes { mod issue2894 { trait IntoBytes { - fn to_bytes(&self) -> Vec<u8>; + fn to_bytes(self) -> Vec<u8>; } // This should not be linted impl IntoBytes for u8 { - fn to_bytes(&self) -> Vec<u8> { - vec![*self] + fn to_bytes(self) -> Vec<u8> { + vec![self] } } } @@ -312,28 +312,25 @@ mod issue4140 { fn try_from(value: T) -> Result<Self, Error<Self::From, Self::To>>; } - impl<F, T> TryFrom<F> for T - where - T: From<F>, - { - type From = T::From; - type To = T::To; + // FIXME: Suggested fix results in infinite recursion. + // impl<F, T> TryFrom<F> for T + // where + // T: From<F>, + // { + // type From = Self::From; + // type To = Self::To; - fn try_from(value: F) -> Result<Self, Error<Self::From, Self::To>> { - Ok(From::from(value)) - } - } + // fn try_from(value: F) -> Result<Self, Error<Self::From, Self::To>> { + // Ok(From::from(value)) + // } + // } impl From<bool> for i64 { type From = bool; type To = Self; fn from(value: bool) -> Self { - if value { - 100 - } else { - 0 - } + if value { 100 } else { 0 } } } } @@ -458,3 +455,10 @@ mod nested_paths { } } } + +mod issue6818 { + #[derive(serde::Deserialize)] + struct A { + a: i32, + } +} diff --git a/src/tools/clippy/tests/ui/use_self.stderr b/src/tools/clippy/tests/ui/use_self.stderr index 37dfef7cfe0e5..a32a9b9157d74 100644 --- a/src/tools/clippy/tests/ui/use_self.stderr +++ b/src/tools/clippy/tests/ui/use_self.stderr @@ -157,22 +157,10 @@ LL | Foo { value } | ^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:319:21 - | -LL | type From = T::From; - | ^^^^^^^ help: use the applicable keyword: `Self` - -error: unnecessary structure name repetition - --> $DIR/use_self.rs:320:19 - | -LL | type To = T::To; - | ^^^^^ help: use the applicable keyword: `Self` - -error: unnecessary structure name repetition - --> $DIR/use_self.rs:457:13 + --> $DIR/use_self.rs:454:13 | LL | A::new::<submod::B>(submod::B {}) | ^ help: use the applicable keyword: `Self` -error: aborting due to 29 previous errors +error: aborting due to 27 previous errors diff --git a/src/tools/clippy/tests/ui/used_underscore_binding.stderr b/src/tools/clippy/tests/ui/used_underscore_binding.stderr index 68e96148093d2..2cbfc5ca2e270 100644 --- a/src/tools/clippy/tests/ui/used_underscore_binding.stderr +++ b/src/tools/clippy/tests/ui/used_underscore_binding.stderr @@ -1,4 +1,4 @@ -error: used binding `_foo` which is prefixed with an underscore. A leading underscore signals that a binding will not be used. +error: used binding `_foo` which is prefixed with an underscore. A leading underscore signals that a binding will not be used --> $DIR/used_underscore_binding.rs:26:5 | LL | _foo + 1 @@ -6,31 +6,31 @@ LL | _foo + 1 | = note: `-D clippy::used-underscore-binding` implied by `-D warnings` -error: used binding `_foo` which is prefixed with an underscore. A leading underscore signals that a binding will not be used. +error: used binding `_foo` which is prefixed with an underscore. A leading underscore signals that a binding will not be used --> $DIR/used_underscore_binding.rs:31:20 | LL | println!("{}", _foo); | ^^^^ -error: used binding `_foo` which is prefixed with an underscore. A leading underscore signals that a binding will not be used. +error: used binding `_foo` which is prefixed with an underscore. A leading underscore signals that a binding will not be used --> $DIR/used_underscore_binding.rs:32:16 | LL | assert_eq!(_foo, _foo); | ^^^^ -error: used binding `_foo` which is prefixed with an underscore. A leading underscore signals that a binding will not be used. +error: used binding `_foo` which is prefixed with an underscore. A leading underscore signals that a binding will not be used --> $DIR/used_underscore_binding.rs:32:22 | LL | assert_eq!(_foo, _foo); | ^^^^ -error: used binding `_underscore_field` which is prefixed with an underscore. A leading underscore signals that a binding will not be used. +error: used binding `_underscore_field` which is prefixed with an underscore. A leading underscore signals that a binding will not be used --> $DIR/used_underscore_binding.rs:45:5 | LL | s._underscore_field += 1; | ^^^^^^^^^^^^^^^^^^^ -error: used binding `_i` which is prefixed with an underscore. A leading underscore signals that a binding will not be used. +error: used binding `_i` which is prefixed with an underscore. A leading underscore signals that a binding will not be used --> $DIR/used_underscore_binding.rs:100:16 | LL | uses_i(_i); diff --git a/src/tools/clippy/tests/ui/vec.fixed b/src/tools/clippy/tests/ui/vec.fixed index 856771596202e..da35f2e5c1bbe 100644 --- a/src/tools/clippy/tests/ui/vec.fixed +++ b/src/tools/clippy/tests/ui/vec.fixed @@ -6,9 +6,14 @@ struct NonCopy; fn on_slice(_: &[u8]) {} + +fn on_mut_slice(_: &mut [u8]) {} + #[allow(clippy::ptr_arg)] fn on_vec(_: &Vec<u8>) {} +fn on_mut_vec(_: &mut Vec<u8>) {} + struct Line { length: usize, } @@ -22,28 +27,38 @@ impl Line { fn main() { on_slice(&[]); on_slice(&[]); + on_mut_slice(&mut []); on_slice(&[1, 2]); on_slice(&[1, 2]); + on_mut_slice(&mut [1, 2]); on_slice(&[1, 2]); on_slice(&[1, 2]); + on_mut_slice(&mut [1, 2]); #[rustfmt::skip] on_slice(&[1, 2]); on_slice(&[1, 2]); + on_mut_slice(&mut [1, 2]); on_slice(&[1; 2]); on_slice(&[1; 2]); + on_mut_slice(&mut [1; 2]); on_vec(&vec![]); on_vec(&vec![1, 2]); on_vec(&vec![1; 2]); + on_mut_vec(&mut vec![]); + on_mut_vec(&mut vec![1, 2]); + on_mut_vec(&mut vec![1; 2]); // Now with non-constant expressions let line = Line { length: 2 }; on_slice(&vec![2; line.length]); on_slice(&vec![2; line.length()]); + on_mut_slice(&mut vec![2; line.length]); + on_mut_slice(&mut vec![2; line.length()]); for a in &[1, 2, 3] { println!("{:?}", a); @@ -54,6 +69,7 @@ fn main() { } on_vec(&vec![1; 201]); // Ok, size of `vec` higher than `too_large_for_stack` + on_mut_vec(&mut vec![1; 201]); // Ok, size of `vec` higher than `too_large_for_stack` // Ok for a in vec![1; 201] { diff --git a/src/tools/clippy/tests/ui/vec.rs b/src/tools/clippy/tests/ui/vec.rs index 03b8ee816658c..e9ed83e5c5aef 100644 --- a/src/tools/clippy/tests/ui/vec.rs +++ b/src/tools/clippy/tests/ui/vec.rs @@ -6,9 +6,14 @@ struct NonCopy; fn on_slice(_: &[u8]) {} + +fn on_mut_slice(_: &mut [u8]) {} + #[allow(clippy::ptr_arg)] fn on_vec(_: &Vec<u8>) {} +fn on_mut_vec(_: &mut Vec<u8>) {} + struct Line { length: usize, } @@ -22,28 +27,38 @@ impl Line { fn main() { on_slice(&vec![]); on_slice(&[]); + on_mut_slice(&mut vec![]); on_slice(&vec![1, 2]); on_slice(&[1, 2]); + on_mut_slice(&mut vec![1, 2]); on_slice(&vec![1, 2]); on_slice(&[1, 2]); + on_mut_slice(&mut vec![1, 2]); #[rustfmt::skip] on_slice(&vec!(1, 2)); on_slice(&[1, 2]); + on_mut_slice(&mut vec![1, 2]); on_slice(&vec![1; 2]); on_slice(&[1; 2]); + on_mut_slice(&mut vec![1; 2]); on_vec(&vec![]); on_vec(&vec![1, 2]); on_vec(&vec![1; 2]); + on_mut_vec(&mut vec![]); + on_mut_vec(&mut vec![1, 2]); + on_mut_vec(&mut vec![1; 2]); // Now with non-constant expressions let line = Line { length: 2 }; on_slice(&vec![2; line.length]); on_slice(&vec![2; line.length()]); + on_mut_slice(&mut vec![2; line.length]); + on_mut_slice(&mut vec![2; line.length()]); for a in vec![1, 2, 3] { println!("{:?}", a); @@ -54,6 +69,7 @@ fn main() { } on_vec(&vec![1; 201]); // Ok, size of `vec` higher than `too_large_for_stack` + on_mut_vec(&mut vec![1; 201]); // Ok, size of `vec` higher than `too_large_for_stack` // Ok for a in vec![1; 201] { diff --git a/src/tools/clippy/tests/ui/vec.stderr b/src/tools/clippy/tests/ui/vec.stderr index 37e28ebddb553..7d1de05a5c836 100644 --- a/src/tools/clippy/tests/ui/vec.stderr +++ b/src/tools/clippy/tests/ui/vec.stderr @@ -1,5 +1,5 @@ error: useless use of `vec!` - --> $DIR/vec.rs:23:14 + --> $DIR/vec.rs:28:14 | LL | on_slice(&vec![]); | ^^^^^^^ help: you can use a slice directly: `&[]` @@ -7,34 +7,64 @@ LL | on_slice(&vec![]); = note: `-D clippy::useless-vec` implied by `-D warnings` error: useless use of `vec!` - --> $DIR/vec.rs:26:14 + --> $DIR/vec.rs:30:18 + | +LL | on_mut_slice(&mut vec![]); + | ^^^^^^^^^^^ help: you can use a slice directly: `&mut []` + +error: useless use of `vec!` + --> $DIR/vec.rs:32:14 | LL | on_slice(&vec![1, 2]); | ^^^^^^^^^^^ help: you can use a slice directly: `&[1, 2]` error: useless use of `vec!` - --> $DIR/vec.rs:29:14 + --> $DIR/vec.rs:34:18 + | +LL | on_mut_slice(&mut vec![1, 2]); + | ^^^^^^^^^^^^^^^ help: you can use a slice directly: `&mut [1, 2]` + +error: useless use of `vec!` + --> $DIR/vec.rs:36:14 | LL | on_slice(&vec![1, 2]); | ^^^^^^^^^^^ help: you can use a slice directly: `&[1, 2]` error: useless use of `vec!` - --> $DIR/vec.rs:32:14 + --> $DIR/vec.rs:38:18 + | +LL | on_mut_slice(&mut vec![1, 2]); + | ^^^^^^^^^^^^^^^ help: you can use a slice directly: `&mut [1, 2]` + +error: useless use of `vec!` + --> $DIR/vec.rs:40:14 | LL | on_slice(&vec!(1, 2)); | ^^^^^^^^^^^ help: you can use a slice directly: `&[1, 2]` error: useless use of `vec!` - --> $DIR/vec.rs:35:14 + --> $DIR/vec.rs:42:18 + | +LL | on_mut_slice(&mut vec![1, 2]); + | ^^^^^^^^^^^^^^^ help: you can use a slice directly: `&mut [1, 2]` + +error: useless use of `vec!` + --> $DIR/vec.rs:44:14 | LL | on_slice(&vec![1; 2]); | ^^^^^^^^^^^ help: you can use a slice directly: `&[1; 2]` error: useless use of `vec!` - --> $DIR/vec.rs:48:14 + --> $DIR/vec.rs:46:18 + | +LL | on_mut_slice(&mut vec![1; 2]); + | ^^^^^^^^^^^^^^^ help: you can use a slice directly: `&mut [1; 2]` + +error: useless use of `vec!` + --> $DIR/vec.rs:63:14 | LL | for a in vec![1, 2, 3] { | ^^^^^^^^^^^^^ help: you can use a slice directly: `&[1, 2, 3]` -error: aborting due to 6 previous errors +error: aborting due to 11 previous errors diff --git a/src/tools/clippy/tests/ui/vec_box_sized.fixed b/src/tools/clippy/tests/ui/vec_box_sized.fixed index 4fa28b525c3c2..a40d91fdb18ac 100644 --- a/src/tools/clippy/tests/ui/vec_box_sized.fixed +++ b/src/tools/clippy/tests/ui/vec_box_sized.fixed @@ -9,6 +9,8 @@ struct BigStruct([i32; 10000]); /// The following should trigger the lint mod should_trigger { use super::SizedStruct; + const C: Vec<i32> = Vec::new(); + static S: Vec<i32> = Vec::new(); struct StructWithVecBox { sized_type: Vec<SizedStruct>, diff --git a/src/tools/clippy/tests/ui/vec_box_sized.rs b/src/tools/clippy/tests/ui/vec_box_sized.rs index 7dc735cd90be7..843bbb64e7190 100644 --- a/src/tools/clippy/tests/ui/vec_box_sized.rs +++ b/src/tools/clippy/tests/ui/vec_box_sized.rs @@ -9,6 +9,8 @@ struct BigStruct([i32; 10000]); /// The following should trigger the lint mod should_trigger { use super::SizedStruct; + const C: Vec<Box<i32>> = Vec::new(); + static S: Vec<Box<i32>> = Vec::new(); struct StructWithVecBox { sized_type: Vec<Box<SizedStruct>>, diff --git a/src/tools/clippy/tests/ui/vec_box_sized.stderr b/src/tools/clippy/tests/ui/vec_box_sized.stderr index 57e2f1fdf9a74..c518267f04189 100644 --- a/src/tools/clippy/tests/ui/vec_box_sized.stderr +++ b/src/tools/clippy/tests/ui/vec_box_sized.stderr @@ -1,28 +1,40 @@ -error: `Vec<T>` is already on the heap, the boxing is unnecessary. - --> $DIR/vec_box_sized.rs:14:21 +error: `Vec<T>` is already on the heap, the boxing is unnecessary + --> $DIR/vec_box_sized.rs:12:14 | -LL | sized_type: Vec<Box<SizedStruct>>, - | ^^^^^^^^^^^^^^^^^^^^^ help: try: `Vec<SizedStruct>` +LL | const C: Vec<Box<i32>> = Vec::new(); + | ^^^^^^^^^^^^^ help: try: `Vec<i32>` | = note: `-D clippy::vec-box` implied by `-D warnings` -error: `Vec<T>` is already on the heap, the boxing is unnecessary. - --> $DIR/vec_box_sized.rs:17:14 +error: `Vec<T>` is already on the heap, the boxing is unnecessary + --> $DIR/vec_box_sized.rs:13:15 + | +LL | static S: Vec<Box<i32>> = Vec::new(); + | ^^^^^^^^^^^^^ help: try: `Vec<i32>` + +error: `Vec<T>` is already on the heap, the boxing is unnecessary + --> $DIR/vec_box_sized.rs:16:21 + | +LL | sized_type: Vec<Box<SizedStruct>>, + | ^^^^^^^^^^^^^^^^^^^^^ help: try: `Vec<SizedStruct>` + +error: `Vec<T>` is already on the heap, the boxing is unnecessary + --> $DIR/vec_box_sized.rs:19:14 | LL | struct A(Vec<Box<SizedStruct>>); | ^^^^^^^^^^^^^^^^^^^^^ help: try: `Vec<SizedStruct>` -error: `Vec<T>` is already on the heap, the boxing is unnecessary. - --> $DIR/vec_box_sized.rs:18:18 +error: `Vec<T>` is already on the heap, the boxing is unnecessary + --> $DIR/vec_box_sized.rs:20:18 | LL | struct B(Vec<Vec<Box<(u32)>>>); | ^^^^^^^^^^^^^^^ help: try: `Vec<u32>` -error: `Vec<T>` is already on the heap, the boxing is unnecessary. - --> $DIR/vec_box_sized.rs:46:23 +error: `Vec<T>` is already on the heap, the boxing is unnecessary + --> $DIR/vec_box_sized.rs:48:23 | LL | pub fn f() -> Vec<Box<S>> { | ^^^^^^^^^^^ help: try: `Vec<S>` -error: aborting due to 4 previous errors +error: aborting due to 6 previous errors 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 e99c98ac79f2a..749393db124b6 100644 --- a/src/tools/clippy/tests/ui/while_let_on_iterator.fixed +++ b/src/tools/clippy/tests/ui/while_let_on_iterator.fixed @@ -2,7 +2,6 @@ #![warn(clippy::while_let_on_iterator)] #![allow(clippy::never_loop, unreachable_code, unused_mut)] -#![feature(or_patterns)] fn base() { let mut iter = 1..20; 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 ba13172428e13..30e3b82a7ccdd 100644 --- a/src/tools/clippy/tests/ui/while_let_on_iterator.rs +++ b/src/tools/clippy/tests/ui/while_let_on_iterator.rs @@ -2,7 +2,6 @@ #![warn(clippy::while_let_on_iterator)] #![allow(clippy::never_loop, unreachable_code, unused_mut)] -#![feature(or_patterns)] fn base() { let mut iter = 1..20; 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 aa980d9965c76..6554977c798bc 100644 --- a/src/tools/clippy/tests/ui/while_let_on_iterator.stderr +++ b/src/tools/clippy/tests/ui/while_let_on_iterator.stderr @@ -1,5 +1,5 @@ error: this loop could be written as a `for` loop - --> $DIR/while_let_on_iterator.rs:9:5 + --> $DIR/while_let_on_iterator.rs:8:5 | LL | while let Option::Some(x) = iter.next() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for x in iter` @@ -7,37 +7,37 @@ LL | while let Option::Some(x) = iter.next() { = note: `-D clippy::while-let-on-iterator` implied by `-D warnings` error: this loop could be written as a `for` loop - --> $DIR/while_let_on_iterator.rs:14:5 + --> $DIR/while_let_on_iterator.rs:13:5 | LL | while let Some(x) = iter.next() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for x in iter` error: this loop could be written as a `for` loop - --> $DIR/while_let_on_iterator.rs:19:5 + --> $DIR/while_let_on_iterator.rs:18:5 | LL | while let Some(_) = iter.next() {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for _ in iter` error: this loop could be written as a `for` loop - --> $DIR/while_let_on_iterator.rs:102:9 + --> $DIR/while_let_on_iterator.rs:101:9 | LL | while let Some([..]) = it.next() {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for [..] in it` error: this loop could be written as a `for` loop - --> $DIR/while_let_on_iterator.rs:109:9 + --> $DIR/while_let_on_iterator.rs:108:9 | LL | while let Some([_x]) = it.next() {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for [_x] in it` error: this loop could be written as a `for` loop - --> $DIR/while_let_on_iterator.rs:122:9 + --> $DIR/while_let_on_iterator.rs:121:9 | LL | while let Some(x @ [_]) = it.next() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for x @ [_] in it` error: this loop could be written as a `for` loop - --> $DIR/while_let_on_iterator.rs:154:9 + --> $DIR/while_let_on_iterator.rs:153:9 | LL | while let Some(_) = y.next() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for _ in y` diff --git a/src/tools/clippy/tests/ui/wild_in_or_pats.stderr b/src/tools/clippy/tests/ui/wild_in_or_pats.stderr index 33c34cbbd4088..45b87aa0f20b9 100644 --- a/src/tools/clippy/tests/ui/wild_in_or_pats.stderr +++ b/src/tools/clippy/tests/ui/wild_in_or_pats.stderr @@ -1,35 +1,35 @@ -error: wildcard pattern covers any other pattern as it will match anyway. +error: wildcard pattern covers any other pattern as it will match anyway --> $DIR/wild_in_or_pats.rs:8:9 | LL | "bar" | _ => { | ^^^^^^^^^ | = note: `-D clippy::wildcard-in-or-patterns` implied by `-D warnings` - = help: Consider handling `_` separately. + = help: consider handling `_` separately -error: wildcard pattern covers any other pattern as it will match anyway. +error: wildcard pattern covers any other pattern as it will match anyway --> $DIR/wild_in_or_pats.rs:16:9 | LL | "bar" | "bar2" | _ => { | ^^^^^^^^^^^^^^^^^^ | - = help: Consider handling `_` separately. + = help: consider handling `_` separately -error: wildcard pattern covers any other pattern as it will match anyway. +error: wildcard pattern covers any other pattern as it will match anyway --> $DIR/wild_in_or_pats.rs:24:9 | LL | _ | "bar" | _ => { | ^^^^^^^^^^^^^ | - = help: Consider handling `_` separately. + = help: consider handling `_` separately -error: wildcard pattern covers any other pattern as it will match anyway. +error: wildcard pattern covers any other pattern as it will match anyway --> $DIR/wild_in_or_pats.rs:32:9 | LL | _ | "bar" => { | ^^^^^^^^^ | - = help: Consider handling `_` separately. + = help: consider handling `_` separately error: aborting due to 4 previous errors diff --git a/src/tools/clippy/tests/ui/wildcard_enum_match_arm.fixed b/src/tools/clippy/tests/ui/wildcard_enum_match_arm.fixed index c266f684a36f9..129d82652d759 100644 --- a/src/tools/clippy/tests/ui/wildcard_enum_match_arm.fixed +++ b/src/tools/clippy/tests/ui/wildcard_enum_match_arm.fixed @@ -77,7 +77,7 @@ fn main() { let error_kind = ErrorKind::NotFound; match error_kind { ErrorKind::NotFound => {}, - std::io::ErrorKind::PermissionDenied | std::io::ErrorKind::ConnectionRefused | std::io::ErrorKind::ConnectionReset | std::io::ErrorKind::ConnectionAborted | std::io::ErrorKind::NotConnected | std::io::ErrorKind::AddrInUse | std::io::ErrorKind::AddrNotAvailable | std::io::ErrorKind::BrokenPipe | std::io::ErrorKind::AlreadyExists | std::io::ErrorKind::WouldBlock | std::io::ErrorKind::InvalidInput | std::io::ErrorKind::InvalidData | std::io::ErrorKind::TimedOut | std::io::ErrorKind::WriteZero | std::io::ErrorKind::Interrupted | std::io::ErrorKind::Other | std::io::ErrorKind::UnexpectedEof | _ => {}, + ErrorKind::PermissionDenied | ErrorKind::ConnectionRefused | ErrorKind::ConnectionReset | ErrorKind::ConnectionAborted | ErrorKind::NotConnected | ErrorKind::AddrInUse | ErrorKind::AddrNotAvailable | ErrorKind::BrokenPipe | ErrorKind::AlreadyExists | ErrorKind::WouldBlock | ErrorKind::InvalidInput | ErrorKind::InvalidData | ErrorKind::TimedOut | ErrorKind::WriteZero | ErrorKind::Interrupted | ErrorKind::Other | ErrorKind::UnexpectedEof | ErrorKind::Unsupported | _ => {}, } match error_kind { ErrorKind::NotFound => {}, @@ -98,6 +98,7 @@ fn main() { ErrorKind::Interrupted => {}, ErrorKind::Other => {}, ErrorKind::UnexpectedEof => {}, + ErrorKind::Unsupported => {}, _ => {}, } } diff --git a/src/tools/clippy/tests/ui/wildcard_enum_match_arm.rs b/src/tools/clippy/tests/ui/wildcard_enum_match_arm.rs index 2dbf726d5d072..028ecb63e7e6d 100644 --- a/src/tools/clippy/tests/ui/wildcard_enum_match_arm.rs +++ b/src/tools/clippy/tests/ui/wildcard_enum_match_arm.rs @@ -98,6 +98,7 @@ fn main() { ErrorKind::Interrupted => {}, ErrorKind::Other => {}, ErrorKind::UnexpectedEof => {}, + ErrorKind::Unsupported => {}, _ => {}, } } diff --git a/src/tools/clippy/tests/ui/wildcard_enum_match_arm.stderr b/src/tools/clippy/tests/ui/wildcard_enum_match_arm.stderr index 0da2b68ba0b2f..fd45cad00d6b5 100644 --- a/src/tools/clippy/tests/ui/wildcard_enum_match_arm.stderr +++ b/src/tools/clippy/tests/ui/wildcard_enum_match_arm.stderr @@ -1,4 +1,4 @@ -error: wildcard match will miss any future added variants +error: wildcard match will also match any future added variants --> $DIR/wildcard_enum_match_arm.rs:39:9 | LL | _ => eprintln!("Not red"), @@ -10,29 +10,29 @@ note: the lint level is defined here LL | #![deny(clippy::wildcard_enum_match_arm)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: wildcard match will miss any future added variants +error: wildcard match will also match any future added variants --> $DIR/wildcard_enum_match_arm.rs:43:9 | LL | _not_red => eprintln!("Not red"), | ^^^^^^^^ help: try this: `_not_red @ Color::Green | _not_red @ Color::Blue | _not_red @ Color::Rgb(..) | _not_red @ Color::Cyan` -error: wildcard match will miss any future added variants +error: wildcard match will also match any future added variants --> $DIR/wildcard_enum_match_arm.rs:47:9 | LL | not_red => format!("{:?}", not_red), | ^^^^^^^ help: try this: `not_red @ Color::Green | not_red @ Color::Blue | not_red @ Color::Rgb(..) | not_red @ Color::Cyan` -error: wildcard match will miss any future added variants +error: wildcard match will also match any future added variants --> $DIR/wildcard_enum_match_arm.rs:63:9 | LL | _ => "No red", | ^ help: try this: `Color::Red | Color::Green | Color::Blue | Color::Rgb(..) | Color::Cyan` -error: match on non-exhaustive enum doesn't explicitly match all known variants +error: wildcard matches known variants and will also match future added variants --> $DIR/wildcard_enum_match_arm.rs:80:9 | LL | _ => {}, - | ^ help: try this: `std::io::ErrorKind::PermissionDenied | std::io::ErrorKind::ConnectionRefused | std::io::ErrorKind::ConnectionReset | std::io::ErrorKind::ConnectionAborted | std::io::ErrorKind::NotConnected | std::io::ErrorKind::AddrInUse | std::io::ErrorKind::AddrNotAvailable | std::io::ErrorKind::BrokenPipe | std::io::ErrorKind::AlreadyExists | std::io::ErrorKind::WouldBlock | std::io::ErrorKind::InvalidInput | std::io::ErrorKind::InvalidData | std::io::ErrorKind::TimedOut | std::io::ErrorKind::WriteZero | std::io::ErrorKind::Interrupted | std::io::ErrorKind::Other | std::io::ErrorKind::UnexpectedEof | _` + | ^ help: try this: `ErrorKind::PermissionDenied | ErrorKind::ConnectionRefused | ErrorKind::ConnectionReset | ErrorKind::ConnectionAborted | ErrorKind::NotConnected | ErrorKind::AddrInUse | ErrorKind::AddrNotAvailable | ErrorKind::BrokenPipe | ErrorKind::AlreadyExists | ErrorKind::WouldBlock | ErrorKind::InvalidInput | ErrorKind::InvalidData | ErrorKind::TimedOut | ErrorKind::WriteZero | ErrorKind::Interrupted | ErrorKind::Other | ErrorKind::UnexpectedEof | ErrorKind::Unsupported | _` error: aborting due to 5 previous errors diff --git a/src/tools/clippy/tests/ui/write_literal.stderr b/src/tools/clippy/tests/ui/write_literal.stderr index e54d89ecf29e6..507a78e828050 100644 --- a/src/tools/clippy/tests/ui/write_literal.stderr +++ b/src/tools/clippy/tests/ui/write_literal.stderr @@ -5,66 +5,120 @@ LL | write!(&mut v, "Hello {}", "world"); | ^^^^^^^ | = note: `-D clippy::write-literal` implied by `-D warnings` +help: try this + | +LL | write!(&mut v, "Hello world"); + | ^^^^^-- error: literal with an empty format string --> $DIR/write_literal.rs:31:44 | LL | writeln!(&mut v, "Hello {} {}", world, "world"); | ^^^^^^^ + | +help: try this + | +LL | writeln!(&mut v, "Hello {} world", world); + | ^^^^^ -- error: literal with an empty format string --> $DIR/write_literal.rs:32:34 | LL | writeln!(&mut v, "Hello {}", "world"); | ^^^^^^^ + | +help: try this + | +LL | writeln!(&mut v, "Hello world"); + | ^^^^^-- error: literal with an empty format string --> $DIR/write_literal.rs:37:33 | LL | writeln!(&mut v, "{0} {1}", "hello", "world"); | ^^^^^^^ + | +help: try this + | +LL | writeln!(&mut v, "hello {1}", "world"); + | ^^^^^ -- error: literal with an empty format string --> $DIR/write_literal.rs:37:42 | LL | writeln!(&mut v, "{0} {1}", "hello", "world"); | ^^^^^^^ + | +help: try this + | +LL | writeln!(&mut v, "{0} world", "hello"); + | ^^^^^ -- error: literal with an empty format string --> $DIR/write_literal.rs:38:33 | LL | writeln!(&mut v, "{1} {0}", "hello", "world"); | ^^^^^^^ + | +help: try this + | +LL | writeln!(&mut v, "{1} hello", "world"); + | ^^^^^-- error: literal with an empty format string --> $DIR/write_literal.rs:38:42 | LL | writeln!(&mut v, "{1} {0}", "hello", "world"); | ^^^^^^^ + | +help: try this + | +LL | writeln!(&mut v, "world {0}", "hello"); + | ^^^^^ -- error: literal with an empty format string - --> $DIR/write_literal.rs:41:43 + --> $DIR/write_literal.rs:41:37 | LL | writeln!(&mut v, "{foo} {bar}", foo = "hello", bar = "world"); - | ^^^^^^^ + | ^^^^^^^^^^^^^ + | +help: try this + | +LL | writeln!(&mut v, "hello {bar}", bar = "world"); + | ^^^^^ -- error: literal with an empty format string - --> $DIR/write_literal.rs:41:58 + --> $DIR/write_literal.rs:41:52 | LL | writeln!(&mut v, "{foo} {bar}", foo = "hello", bar = "world"); - | ^^^^^^^ + | ^^^^^^^^^^^^^ + | +help: try this + | +LL | writeln!(&mut v, "{foo} world", foo = "hello"); + | ^^^^^ -- error: literal with an empty format string - --> $DIR/write_literal.rs:42:43 + --> $DIR/write_literal.rs:42:37 | LL | writeln!(&mut v, "{bar} {foo}", foo = "hello", bar = "world"); - | ^^^^^^^ + | ^^^^^^^^^^^^^ + | +help: try this + | +LL | writeln!(&mut v, "{bar} hello", bar = "world"); + | ^^^^^-- error: literal with an empty format string - --> $DIR/write_literal.rs:42:58 + --> $DIR/write_literal.rs:42:52 | LL | writeln!(&mut v, "{bar} {foo}", foo = "hello", bar = "world"); - | ^^^^^^^ + | ^^^^^^^^^^^^^ + | +help: try this + | +LL | writeln!(&mut v, "world {foo}", foo = "hello"); + | ^^^^^ -- error: aborting due to 11 previous errors diff --git a/src/tools/clippy/tests/ui/write_literal_2.rs b/src/tools/clippy/tests/ui/write_literal_2.rs new file mode 100644 index 0000000000000..f341e8215e1ca --- /dev/null +++ b/src/tools/clippy/tests/ui/write_literal_2.rs @@ -0,0 +1,27 @@ +#![allow(unused_must_use)] +#![warn(clippy::write_literal)] + +use std::io::Write; + +fn main() { + let mut v = Vec::new(); + + writeln!(&mut v, "{}", "{hello}"); + writeln!(&mut v, r"{}", r"{hello}"); + writeln!(&mut v, "{}", '\''); + writeln!(&mut v, "{}", '"'); + writeln!(&mut v, r"{}", '"'); // don't lint + writeln!(&mut v, r"{}", '\''); + writeln!( + &mut v, + "some {}", + "hello \ + world!" + ); + writeln!( + &mut v, + "some {}\ + {} \\ {}", + "1", "2", "3", + ); +} diff --git a/src/tools/clippy/tests/ui/write_literal_2.stderr b/src/tools/clippy/tests/ui/write_literal_2.stderr new file mode 100644 index 0000000000000..0aa1b55e58c56 --- /dev/null +++ b/src/tools/clippy/tests/ui/write_literal_2.stderr @@ -0,0 +1,107 @@ +error: literal with an empty format string + --> $DIR/write_literal_2.rs:9:28 + | +LL | writeln!(&mut v, "{}", "{hello}"); + | ^^^^^^^^^ + | + = note: `-D clippy::write-literal` implied by `-D warnings` +help: try this + | +LL | writeln!(&mut v, "{{hello}}"); + | ^^^^^^^^^-- + +error: literal with an empty format string + --> $DIR/write_literal_2.rs:10:29 + | +LL | writeln!(&mut v, r"{}", r"{hello}"); + | ^^^^^^^^^^ + | +help: try this + | +LL | writeln!(&mut v, r"{{hello}}"); + | ^^^^^^^^^-- + +error: literal with an empty format string + --> $DIR/write_literal_2.rs:11:28 + | +LL | writeln!(&mut v, "{}", '/''); + | ^^^^ + | +help: try this + | +LL | writeln!(&mut v, "'"); + | ^-- + +error: literal with an empty format string + --> $DIR/write_literal_2.rs:12:28 + | +LL | writeln!(&mut v, "{}", '"'); + | ^^^ + | +help: try this + | +LL | writeln!(&mut v, "/""); + | ^^-- + +error: literal with an empty format string + --> $DIR/write_literal_2.rs:14:29 + | +LL | writeln!(&mut v, r"{}", '/''); + | ^^^^ + | +help: try this + | +LL | writeln!(&mut v, r"'"); + | ^-- + +error: literal with an empty format string + --> $DIR/write_literal_2.rs:18:9 + | +LL | / "hello / +LL | | world!" + | |_______________^ + | +help: try this + | +LL | "some hello / +LL | world!" + | + +error: literal with an empty format string + --> $DIR/write_literal_2.rs:25:9 + | +LL | "1", "2", "3", + | ^^^ + | +help: try this + | +LL | "some 1/ +LL | {} / {}", "2", "3", + | + +error: literal with an empty format string + --> $DIR/write_literal_2.rs:25:14 + | +LL | "1", "2", "3", + | ^^^ + | +help: try this + | +LL | 2 / {}", +LL | "1", "3", + | + +error: literal with an empty format string + --> $DIR/write_literal_2.rs:25:19 + | +LL | "1", "2", "3", + | ^^^ + | +help: try this + | +LL | {} / 3", +LL | "1", "2", + | + +error: aborting due to 9 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 6cfc0fcb4cae4..cdfbdb8b0db3e 100644 --- a/src/tools/clippy/tests/ui/wrong_self_convention.rs +++ b/src/tools/clippy/tests/ui/wrong_self_convention.rs @@ -163,3 +163,30 @@ mod issue6307 { fn to_mut(&mut self); } } + +mod issue6727 { + #[derive(Clone, Copy)] + struct FooCopy; + + impl FooCopy { + fn to_u64(self) -> u64 { + 1 + } + // trigger lint + fn to_u64_v2(&self) -> u64 { + 1 + } + } + + struct FooNoCopy; + + impl FooNoCopy { + // trigger lint + fn to_u64(self) -> u64 { + 2 + } + fn to_u64_v2(&self) -> u64 { + 2 + } + } +} diff --git a/src/tools/clippy/tests/ui/wrong_self_convention.stderr b/src/tools/clippy/tests/ui/wrong_self_convention.stderr index 32bd9075bd5e1..29f5ba8269545 100644 --- a/src/tools/clippy/tests/ui/wrong_self_convention.stderr +++ b/src/tools/clippy/tests/ui/wrong_self_convention.stderr @@ -1,148 +1,195 @@ -error: methods called `from_*` usually take no self; consider choosing a less ambiguous name +error: methods called `from_*` usually take no `self` --> $DIR/wrong_self_convention.rs:18:17 | LL | fn from_i32(self) {} | ^^^^ | = note: `-D clippy::wrong-self-convention` implied by `-D warnings` + = help: consider choosing a less ambiguous name -error: methods called `from_*` usually take no self; consider choosing a less ambiguous name +error: methods called `from_*` usually take no `self` --> $DIR/wrong_self_convention.rs:24:21 | LL | pub fn from_i64(self) {} | ^^^^ + | + = help: consider choosing a less ambiguous name -error: methods called `as_*` usually take self by reference or self by mutable reference; consider choosing a less ambiguous name +error: methods called `as_*` usually take `self` by reference or `self` by mutable reference --> $DIR/wrong_self_convention.rs:36:15 | LL | fn as_i32(self) {} | ^^^^ + | + = help: consider choosing a less ambiguous name -error: methods called `into_*` usually take self by value; consider choosing a less ambiguous name +error: methods called `into_*` usually take `self` by value --> $DIR/wrong_self_convention.rs:38:17 | LL | fn into_i32(&self) {} | ^^^^^ + | + = help: consider choosing a less ambiguous name -error: methods called `is_*` usually take self by reference or no self; consider choosing a less ambiguous name +error: methods called `is_*` usually take `self` by reference or no `self` --> $DIR/wrong_self_convention.rs:40:15 | LL | fn is_i32(self) {} | ^^^^ + | + = help: consider choosing a less ambiguous name -error: methods called `to_*` usually take self by reference; consider choosing a less ambiguous name +error: methods with the following characteristics: (`to_*` and `self` type is not `Copy`) usually take `self` by reference --> $DIR/wrong_self_convention.rs:42:15 | LL | fn to_i32(self) {} | ^^^^ + | + = help: consider choosing a less ambiguous name -error: methods called `from_*` usually take no self; consider choosing a less ambiguous name +error: methods called `from_*` usually take no `self` --> $DIR/wrong_self_convention.rs:44:17 | LL | fn from_i32(self) {} | ^^^^ + | + = help: consider choosing a less ambiguous name -error: methods called `as_*` usually take self by reference or self by mutable reference; consider choosing a less ambiguous name +error: methods called `as_*` usually take `self` by reference or `self` by mutable reference --> $DIR/wrong_self_convention.rs:46:19 | LL | pub fn as_i64(self) {} | ^^^^ + | + = help: consider choosing a less ambiguous name -error: methods called `into_*` usually take self by value; consider choosing a less ambiguous name +error: methods called `into_*` usually take `self` by value --> $DIR/wrong_self_convention.rs:47:21 | LL | pub fn into_i64(&self) {} | ^^^^^ + | + = help: consider choosing a less ambiguous name -error: methods called `is_*` usually take self by reference or no self; consider choosing a less ambiguous name +error: methods called `is_*` usually take `self` by reference or no `self` --> $DIR/wrong_self_convention.rs:48:19 | LL | pub fn is_i64(self) {} | ^^^^ + | + = help: consider choosing a less ambiguous name -error: methods called `to_*` usually take self by reference; consider choosing a less ambiguous name +error: methods with the following characteristics: (`to_*` and `self` type is not `Copy`) usually take `self` by reference --> $DIR/wrong_self_convention.rs:49:19 | LL | pub fn to_i64(self) {} | ^^^^ + | + = help: consider choosing a less ambiguous name -error: methods called `from_*` usually take no self; consider choosing a less ambiguous name +error: methods called `from_*` usually take no `self` --> $DIR/wrong_self_convention.rs:50:21 | LL | pub fn from_i64(self) {} | ^^^^ + | + = help: consider choosing a less ambiguous name -error: methods called `as_*` usually take self by reference or self by mutable reference; consider choosing a less ambiguous name +error: methods called `as_*` usually take `self` by reference or `self` by mutable reference --> $DIR/wrong_self_convention.rs:95:19 | LL | fn as_i32(self) {} | ^^^^ + | + = help: consider choosing a less ambiguous name -error: methods called `into_*` usually take self by value; consider choosing a less ambiguous name +error: methods called `into_*` usually take `self` by value --> $DIR/wrong_self_convention.rs:98:25 | LL | fn into_i32_ref(&self) {} | ^^^^^ + | + = help: consider choosing a less ambiguous name -error: methods called `is_*` usually take self by reference or no self; consider choosing a less ambiguous name +error: methods called `is_*` usually take `self` by reference or no `self` --> $DIR/wrong_self_convention.rs:100:19 | LL | fn is_i32(self) {} | ^^^^ - -error: methods called `to_*` usually take self by reference; consider choosing a less ambiguous name - --> $DIR/wrong_self_convention.rs:102:19 | -LL | fn to_i32(self) {} - | ^^^^ + = help: consider choosing a less ambiguous name -error: methods called `from_*` usually take no self; consider choosing a less ambiguous name +error: methods called `from_*` usually take no `self` --> $DIR/wrong_self_convention.rs:104:21 | LL | fn from_i32(self) {} | ^^^^ + | + = help: consider choosing a less ambiguous name -error: methods called `as_*` usually take self by reference or self by mutable reference; consider choosing a less ambiguous name +error: methods called `as_*` usually take `self` by reference or `self` by mutable reference --> $DIR/wrong_self_convention.rs:119:19 | LL | fn as_i32(self); | ^^^^ + | + = help: consider choosing a less ambiguous name -error: methods called `into_*` usually take self by value; consider choosing a less ambiguous name +error: methods called `into_*` usually take `self` by value --> $DIR/wrong_self_convention.rs:122:25 | LL | fn into_i32_ref(&self); | ^^^^^ + | + = help: consider choosing a less ambiguous name -error: methods called `is_*` usually take self by reference or no self; consider choosing a less ambiguous name +error: methods called `is_*` usually take `self` by reference or no `self` --> $DIR/wrong_self_convention.rs:124:19 | LL | fn is_i32(self); | ^^^^ - -error: methods called `to_*` usually take self by reference; consider choosing a less ambiguous name - --> $DIR/wrong_self_convention.rs:126:19 | -LL | fn to_i32(self); - | ^^^^ + = help: consider choosing a less ambiguous name -error: methods called `from_*` usually take no self; consider choosing a less ambiguous name +error: methods called `from_*` usually take no `self` --> $DIR/wrong_self_convention.rs:128:21 | LL | fn from_i32(self); | ^^^^ + | + = help: consider choosing a less ambiguous name -error: methods called `into_*` usually take self by value; consider choosing a less ambiguous name +error: methods called `into_*` usually take `self` by value --> $DIR/wrong_self_convention.rs:146:25 | LL | fn into_i32_ref(&self); | ^^^^^ + | + = help: consider choosing a less ambiguous name -error: methods called `from_*` usually take no self; consider choosing a less ambiguous name +error: methods called `from_*` usually take no `self` --> $DIR/wrong_self_convention.rs:152:21 | LL | fn from_i32(self); | ^^^^ + | + = help: consider choosing a less ambiguous name + +error: methods with the following characteristics: (`to_*` and `self` type is `Copy`) usually take `self` by value + --> $DIR/wrong_self_convention.rs:176:22 + | +LL | fn to_u64_v2(&self) -> u64 { + | ^^^^^ + | + = help: consider choosing a less ambiguous name + +error: methods with the following characteristics: (`to_*` and `self` type is not `Copy`) usually take `self` by reference + --> $DIR/wrong_self_convention.rs:185:19 + | +LL | fn to_u64(self) -> u64 { + | ^^^^ + | + = help: consider choosing a less ambiguous name error: aborting due to 24 previous errors diff --git a/src/tools/clippy/tests/ui/wrong_self_convention2.rs b/src/tools/clippy/tests/ui/wrong_self_convention2.rs new file mode 100644 index 0000000000000..8b42aa59e1323 --- /dev/null +++ b/src/tools/clippy/tests/ui/wrong_self_convention2.rs @@ -0,0 +1,32 @@ +// edition:2018 +#![warn(clippy::wrong_self_convention)] +#![warn(clippy::wrong_pub_self_convention)] +#![allow(dead_code)] + +fn main() {} + +mod issue6983 { + pub struct Thing; + pub trait Trait { + fn to_thing(&self) -> Thing; + } + + impl Trait for u8 { + // don't trigger, e.g. `ToString` from `std` requires `&self` + fn to_thing(&self) -> Thing { + Thing + } + } + + trait ToU64 { + fn to_u64(self) -> u64; + } + + struct FooNoCopy; + // trigger lint + impl ToU64 for FooNoCopy { + fn to_u64(self) -> u64 { + 2 + } + } +} diff --git a/src/tools/clippy/tests/ui/wrong_self_convention2.stderr b/src/tools/clippy/tests/ui/wrong_self_convention2.stderr new file mode 100644 index 0000000000000..0ca1a390974a0 --- /dev/null +++ b/src/tools/clippy/tests/ui/wrong_self_convention2.stderr @@ -0,0 +1,11 @@ +error: methods with the following characteristics: (`to_*` and `self` type is not `Copy`) usually take `self` by reference + --> $DIR/wrong_self_convention2.rs:28:19 + | +LL | fn to_u64(self) -> u64 { + | ^^^^ + | + = note: `-D clippy::wrong-self-convention` implied by `-D warnings` + = help: consider choosing a less ambiguous name + +error: aborting due to previous error + diff --git a/src/tools/clippy/tests/ui/wrong_self_conventions_mut.rs b/src/tools/clippy/tests/ui/wrong_self_conventions_mut.rs new file mode 100644 index 0000000000000..486a0d7723585 --- /dev/null +++ b/src/tools/clippy/tests/ui/wrong_self_conventions_mut.rs @@ -0,0 +1,30 @@ +// edition:2018 +#![warn(clippy::wrong_self_convention)] +#![allow(dead_code)] + +fn main() {} + +mod issue6758 { + pub enum Test<T> { + One(T), + Many(Vec<T>), + } + + impl<T> Test<T> { + // If a method starts with `to_` and not ends with `_mut` it should expect `&self` + pub fn to_many(&mut self) -> Option<&mut [T]> { + match self { + Self::Many(data) => Some(data), + _ => None, + } + } + + // If a method starts with `to_` and ends with `_mut` it should expect `&mut self` + pub fn to_many_mut(&self) -> Option<&[T]> { + match self { + Self::Many(data) => Some(data), + _ => None, + } + } + } +} diff --git a/src/tools/clippy/tests/ui/wrong_self_conventions_mut.stderr b/src/tools/clippy/tests/ui/wrong_self_conventions_mut.stderr new file mode 100644 index 0000000000000..6ce37c5949111 --- /dev/null +++ b/src/tools/clippy/tests/ui/wrong_self_conventions_mut.stderr @@ -0,0 +1,19 @@ +error: methods with the following characteristics: (`to_*` and `self` type is not `Copy`) usually take `self` by reference + --> $DIR/wrong_self_conventions_mut.rs:15:24 + | +LL | pub fn to_many(&mut self) -> Option<&mut [T]> { + | ^^^^^^^^^ + | + = note: `-D clippy::wrong-self-convention` implied by `-D warnings` + = help: consider choosing a less ambiguous name + +error: methods with the following characteristics: (`to_*` and `*_mut`) usually take `self` by mutable reference + --> $DIR/wrong_self_conventions_mut.rs:23:28 + | +LL | pub fn to_many_mut(&self) -> Option<&[T]> { + | ^^^^^ + | + = help: consider choosing a less ambiguous name + +error: aborting due to 2 previous errors + diff --git a/src/tools/clippy/tests/ui/zero_div_zero.stderr b/src/tools/clippy/tests/ui/zero_div_zero.stderr index d0e88f3c5a546..0931dd32e7af2 100644 --- a/src/tools/clippy/tests/ui/zero_div_zero.stderr +++ b/src/tools/clippy/tests/ui/zero_div_zero.stderr @@ -13,7 +13,7 @@ LL | let nan = 0.0 / 0.0; | ^^^^^^^^^ | = note: `-D clippy::zero-divided-by-zero` implied by `-D warnings` - = help: Consider using `f64::NAN` if you would like a constant representing NaN + = help: consider using `f64::NAN` if you would like a constant representing NaN error: equal expressions as operands to `/` --> $DIR/zero_div_zero.rs:5:19 @@ -27,7 +27,7 @@ error: constant division of `0.0` with `0.0` will always result in NaN LL | let f64_nan = 0.0 / 0.0f64; | ^^^^^^^^^^^^ | - = help: Consider using `f64::NAN` if you would like a constant representing NaN + = help: consider using `f64::NAN` if you would like a constant representing NaN error: equal expressions as operands to `/` --> $DIR/zero_div_zero.rs:6:25 @@ -41,7 +41,7 @@ error: constant division of `0.0` with `0.0` will always result in NaN LL | let other_f64_nan = 0.0f64 / 0.0; | ^^^^^^^^^^^^ | - = help: Consider using `f64::NAN` if you would like a constant representing NaN + = help: consider using `f64::NAN` if you would like a constant representing NaN error: equal expressions as operands to `/` --> $DIR/zero_div_zero.rs:7:28 @@ -55,7 +55,7 @@ error: constant division of `0.0` with `0.0` will always result in NaN LL | let one_more_f64_nan = 0.0f64 / 0.0f64; | ^^^^^^^^^^^^^^^ | - = help: Consider using `f64::NAN` if you would like a constant representing NaN + = help: consider using `f64::NAN` if you would like a constant representing NaN error: aborting due to 8 previous errors diff --git a/src/tools/clippy/tests/ui/zero_sized_btreemap_values.stderr b/src/tools/clippy/tests/ui/zero_sized_btreemap_values.stderr index 334d921a9af3f..d924f33797d29 100644 --- a/src/tools/clippy/tests/ui/zero_sized_btreemap_values.stderr +++ b/src/tools/clippy/tests/ui/zero_sized_btreemap_values.stderr @@ -83,7 +83,7 @@ error: map with zero-sized value type --> $DIR/zero_sized_btreemap_values.rs:64:35 | LL | let _: BTreeMap<String, ()> = BTreeMap::new(); - | ^^^^^^^^^^^^^ + | ^^^^^^^^ | = help: consider using a set instead diff --git a/src/tools/clippy/tests/ui/zero_sized_hashmap_values.stderr b/src/tools/clippy/tests/ui/zero_sized_hashmap_values.stderr index 43987b3d01d16..79770bf90d701 100644 --- a/src/tools/clippy/tests/ui/zero_sized_hashmap_values.stderr +++ b/src/tools/clippy/tests/ui/zero_sized_hashmap_values.stderr @@ -83,7 +83,7 @@ error: map with zero-sized value type --> $DIR/zero_sized_hashmap_values.rs:64:34 | LL | let _: HashMap<String, ()> = HashMap::new(); - | ^^^^^^^^^^^^ + | ^^^^^^^ | = help: consider using a set instead diff --git a/src/tools/clippy/tests/versioncheck.rs b/src/tools/clippy/tests/versioncheck.rs index 922a8207cea86..1eaec4a50a6a6 100644 --- a/src/tools/clippy/tests/versioncheck.rs +++ b/src/tools/clippy/tests/versioncheck.rs @@ -15,15 +15,15 @@ fn check_that_clippy_lints_and_clippy_utils_have_the_same_version_as_clippy() { .expect("could not obtain cargo metadata"); for krate in &["clippy_lints", "clippy_utils"] { - let krate_meta = clippy_meta - .packages - .iter() - .find(|package| package.name == *krate) + let krate_meta = cargo_metadata::MetadataCommand::new() + .current_dir(std::env::current_dir().unwrap().join(krate)) + .no_deps() + .exec() .expect("could not obtain cargo metadata"); - assert_eq!(krate_meta.version, clippy_meta.packages[0].version); + assert_eq!(krate_meta.packages[0].version, clippy_meta.packages[0].version); for package in &clippy_meta.packages[0].dependencies { if package.name == *krate { - assert!(package.req.matches(&krate_meta.version)); + assert!(package.req.matches(&krate_meta.packages[0].version)); break; } } diff --git a/src/tools/clippy/util/gh-pages/index.html b/src/tools/clippy/util/gh-pages/index.html index 1852fb6640ecc..082cb35c2e034 100644 --- a/src/tools/clippy/util/gh-pages/index.html +++ b/src/tools/clippy/util/gh-pages/index.html @@ -8,6 +8,12 @@ <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.6/css/bootstrap.min.css"/> <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.5.0/styles/github.min.css"/> + + <!-- The files are not copied over into the Clippy project since they use the MPL-2.0 License --> + <link rel="stylesheet" href="https://rust-lang.github.io/mdBook/css/variables.css"/> + <link id="styleHighlight" rel="stylesheet" href="https://rust-lang.github.io/mdBook/highlight.css"> + <link id="styleNight" rel="stylesheet" href="https://rust-lang.github.io/mdBook/tomorrow-night.css" disabled="true"> + <link id="styleAyu" rel="stylesheet" href="https://rust-lang.github.io/mdBook/ayu-highlight.css" disabled="true"> <style> blockquote { font-size: 1em; } [ng\:cloak], [ng-cloak], [data-ng-cloak], [x-ng-cloak], .ng-cloak, .x-ng-cloak { display: none !important; } @@ -15,7 +21,6 @@ .form-inline .checkbox { margin-right: 0.6em } .panel-heading { cursor: pointer; } - .panel-heading:hover { background-color: #eee; } .panel-title { display: flex; } .panel-title .label { display: inline-block; } @@ -24,10 +29,134 @@ .panel-title-name span { vertical-align: bottom; } .panel .panel-title-name .anchor { display: none; } - .panel:hover .panel-title-name .anchor { display: inline; color: #fff; } + .panel:hover .panel-title-name .anchor { display: inline;} + + </style> + <style> + /* Expanding the mdBoom theme*/ + .light { + --inline-code-bg: #f6f7f6; + } + .rust { + --inline-code-bg: #f6f7f6; + } + .coal { + --inline-code-bg: #1d1f21; + } + .navy { + --inline-code-bg: #1d1f21; + } + .ayu { + --inline-code-bg: #191f26; + } + + /* Applying the mdBook theme */ + .theme-icon { + position: absolute; + text-align: center; + width: 2em; + height: 2em; + margin: 0.7em; + line-height: 2em; + border: solid 1px var(--icons); + border-radius: 5px; + user-select: none; + cursor: pointer; + } + .theme-icon:hover { + background: var(--theme-hover); + } + .theme-choice { + position: absolute; + margin-top: calc(2em + 0.7em); + margin-left: 0.7em; + list-style: none; + border: 1px solid var(--theme-popup-border); + border-radius: 5px; + color: var(--fg); + background: var(--theme-popup-bg); + padding: 0 0; + } + .theme-choice > li { + padding: 5px 10px; + font-size: 0.8em; + user-select: none; + cursor: pointer; + } + .theme-choice > li:hover { + background: var(--theme-hover); + } + + .alert { + color: var(--fg); + background: var(--theme-hover); + border: 1px solid var(--theme-popup-border); + } + .page-header { + border-color: var(--theme-popup-border); + } + .panel-default > .panel-heading { + background: var(--theme-hover); + color: var(--fg); + border: 1px solid var(--theme-popup-border); + } + .panel-default > .panel-heading:hover { + filter: brightness(90%); + } + .list-group-item { + background: 0%; + border: 1px solid var(--theme-popup-border); + } + .panel, pre, hr { + background: var(--bg); + border: 1px solid var(--theme-popup-border); + } + + #filter-label, #filter-clear { + background: var(--searchbar-bg); + color: var(--searchbar-fg); + border-color: var(--theme-popup-border); + filter: brightness(95%); + } + #filter-label:hover, #filter-clear:hover { + filter: brightness(90%); + } + #filter-input { + background: var(--searchbar-bg); + color: var(--searchbar-fg); + border-color: var(--theme-popup-border); + } + + #filter-input::-webkit-input-placeholder, + #filter-input::-moz-placeholder { + color: var(--searchbar-fg); + opacity: 30%; + } + + p > code { + color: var(--inline-code-color); + background-color: var(--inline-code-bg); + } + html { + scrollbar-color: var(--scrollbar) var(--bg); + } + body { + background: var(--bg); + color: var(--fg); + } + </style> </head> <body> + <div id="theme-icon" class="theme-icon">🖌</div> + <ul id="theme-menu" class="theme-choice" style="display: none;"> + <li id="light">Light</li> + <li id="rust">Rust</li> + <li id="coal">Coal</li> + <li id="navy">Navy</li> + <li id="ayu">Ayu</li> + </ul> + <div class="container" ng-app="clippy" ng-controller="lintList"> <div class="page-header"> <h1>ALL the Clippy Lints</h1> @@ -49,7 +178,7 @@ <h1>ALL the Clippy Lints</h1> </div> <div class="panel panel-default" ng-show="data"> - <div class="panel-body row"> + <div class="panel-body row filter-panel"> <div class="col-md-6 form-inline"> <div class="form-group form-group-lg"> <p class="h4">Lint levels</p> @@ -79,7 +208,7 @@ <h1>ALL the Clippy Lints</h1> <label class="input-group-addon" id="filter-label" for="filter-input">Filter:</label> <input type="text" class="form-control" placeholder="Keywords or search string" id="filter-input" ng-model="search" ng-model-options="{debounce: 50}"/> <span class="input-group-btn"> - <button class="btn btn-default" type="button" ng-click="search = ''"> + <button id="filter-clear" class="btn" type="button" ng-click="search = ''"> Clear </button> </span> @@ -320,6 +449,60 @@ <h4 class="list-group-item-heading"> } } } + + function setupListeners() { + let themeIcon = document.getElementById("theme-icon"); + let themeMenu = document.getElementById("theme-menu"); + themeIcon.addEventListener("click", function(e) { + if (themeMenu.style.display == "none") { + themeMenu.style.display = "block"; + } else { + themeMenu.style.display = "none"; + } + }); + + let children = themeMenu.children; + for (let index = 0; index < children.length; index++) { + let child = children[index]; + child.addEventListener("click", function(e) { + setTheme(child.id, true); + }); + } + } + + setupListeners(); + + function setTheme(theme, store) { + let enableHighlight = false; + let enableNight = false; + let enableAyu = false; + + if (theme == "ayu") { + enableAyu = true; + } else if (theme == "coal" || theme == "navy") { + enableNight = true; + } else if (theme == "rust") { + enableHighlight = true; + } else { + enableHighlight = true; + // this makes sure that an unknown theme request gets set to a known one + theme = "light"; + } + document.getElementsByTagName("body")[0].className = theme; + + document.getElementById("styleHighlight").disabled = !enableHighlight; + document.getElementById("styleNight").disabled = !enableNight; + document.getElementById("styleAyu").disabled = !enableAyu; + + if (store) { + try { + localStorage.setItem('clippy-lint-list-theme', theme); + } catch (e) { } + } + } + + // loading the theme after the initial load + setTheme(localStorage.getItem('clippy-lint-list-theme'), false); </script> </body> </html> diff --git a/src/tools/compiletest/src/common.rs b/src/tools/compiletest/src/common.rs index 99cbcf316a25c..b7693a3cb1431 100644 --- a/src/tools/compiletest/src/common.rs +++ b/src/tools/compiletest/src/common.rs @@ -376,12 +376,24 @@ pub fn expected_output_path( testpaths.file.with_extension(extension) } -pub const UI_EXTENSIONS: &[&str] = &[UI_STDERR, UI_STDOUT, UI_FIXED, UI_RUN_STDERR, UI_RUN_STDOUT]; +pub const UI_EXTENSIONS: &[&str] = &[ + UI_STDERR, + UI_STDOUT, + UI_FIXED, + UI_RUN_STDERR, + UI_RUN_STDOUT, + UI_STDERR_64, + UI_STDERR_32, + UI_STDERR_16, +]; pub const UI_STDERR: &str = "stderr"; pub const UI_STDOUT: &str = "stdout"; pub const UI_FIXED: &str = "fixed"; pub const UI_RUN_STDERR: &str = "run.stderr"; pub const UI_RUN_STDOUT: &str = "run.stdout"; +pub const UI_STDERR_64: &str = "64bit.stderr"; +pub const UI_STDERR_32: &str = "32bit.stderr"; +pub const UI_STDERR_16: &str = "16bit.stderr"; /// Absolute path to the directory where all output for all tests in the given /// `relative_dir` group should reside. Example: diff --git a/src/tools/compiletest/src/header.rs b/src/tools/compiletest/src/header.rs index ff0d845be937c..f31a24738df6c 100644 --- a/src/tools/compiletest/src/header.rs +++ b/src/tools/compiletest/src/header.rs @@ -44,6 +44,7 @@ impl EarlyProps { let mut props = EarlyProps::default(); let rustc_has_profiler_support = env::var_os("RUSTC_PROFILER_SUPPORT").is_some(); let rustc_has_sanitizer_support = env::var_os("RUSTC_SANITIZER_SUPPORT").is_some(); + let has_asm_support = util::has_asm_support(&config.target); let has_asan = util::ASAN_SUPPORTED_TARGETS.contains(&&*config.target); let has_lsan = util::LSAN_SUPPORTED_TARGETS.contains(&&*config.target); let has_msan = util::MSAN_SUPPORTED_TARGETS.contains(&&*config.target); @@ -76,6 +77,10 @@ impl EarlyProps { props.ignore = true; } + if !has_asm_support && config.parse_name_directive(ln, "needs-asm-support") { + props.ignore = true; + } + if !rustc_has_profiler_support && config.parse_needs_profiler_support(ln) { props.ignore = true; } @@ -131,9 +136,7 @@ impl EarlyProps { props.aux_crate.push(ac); } - if let Some(r) = config.parse_revisions(ln) { - props.revisions.extend(r); - } + config.parse_and_update_revisions(ln, &mut props.revisions); props.should_fail = props.should_fail || config.parse_name_directive(ln, "should-fail"); }); @@ -427,9 +430,7 @@ impl TestProps { self.compile_flags.push(format!("--edition={}", edition)); } - if let Some(r) = config.parse_revisions(ln) { - self.revisions.extend(r); - } + config.parse_and_update_revisions(ln, &mut self.revisions); if self.run_flags.is_none() { self.run_flags = config.parse_run_flags(ln); @@ -708,8 +709,8 @@ impl Config { self.parse_name_value_directive(line, "aux-crate").map(|r| { let mut parts = r.trim().splitn(2, '='); ( - parts.next().expect("aux-crate name").to_string(), - parts.next().expect("aux-crate value").to_string(), + parts.next().expect("missing aux-crate name (e.g. log=log.rs)").to_string(), + parts.next().expect("missing aux-crate value (e.g. log=log.rs)").to_string(), ) }) } @@ -718,9 +719,16 @@ impl Config { self.parse_name_value_directive(line, "compile-flags") } - fn parse_revisions(&self, line: &str) -> Option<Vec<String>> { - self.parse_name_value_directive(line, "revisions") - .map(|r| r.split_whitespace().map(|t| t.to_string()).collect()) + fn parse_and_update_revisions(&self, line: &str, existing: &mut Vec<String>) { + if let Some(raw) = self.parse_name_value_directive(line, "revisions") { + let mut duplicates: HashSet<_> = existing.iter().cloned().collect(); + for revision in raw.split_whitespace().map(|r| r.to_string()) { + if !duplicates.insert(revision.clone()) { + panic!("Duplicate revision: `{}` in line `{}`", revision, raw); + } + existing.push(revision); + } + } } fn parse_run_flags(&self, line: &str) -> Option<String> { @@ -973,7 +981,11 @@ fn parse_normalization_string(line: &mut &str) -> Option<String> { } pub fn extract_llvm_version(version: &str) -> Option<u32> { - let version_without_suffix = version.trim_end_matches("git").split('-').next().unwrap(); + let pat = |c: char| !c.is_ascii_digit() && c != '.'; + let version_without_suffix = match version.find(pat) { + Some(pos) => &version[..pos], + None => version, + }; let components: Vec<u32> = version_without_suffix .split('.') .map(|s| s.parse().expect("Malformed version component")) diff --git a/src/tools/compiletest/src/header/tests.rs b/src/tools/compiletest/src/header/tests.rs index ec99fde0df9c2..ca7458d255c3c 100644 --- a/src/tools/compiletest/src/header/tests.rs +++ b/src/tools/compiletest/src/header/tests.rs @@ -223,6 +223,17 @@ fn sanitizers() { assert!(parse_rs(&config, "// needs-sanitizer-thread").ignore); } +#[test] +fn asm_support() { + let mut config = config(); + + config.target = "avr-unknown-gnu-atmega328".to_owned(); + assert!(parse_rs(&config, "// needs-asm-support").ignore); + + config.target = "i686-unknown-netbsd".to_owned(); + assert!(!parse_rs(&config, "// needs-asm-support").ignore); +} + #[test] fn test_extract_version_range() { use super::{extract_llvm_version, extract_version_range}; @@ -237,3 +248,10 @@ fn test_extract_version_range() { assert_eq!(extract_version_range(" - 4.5.6", extract_llvm_version), None); assert_eq!(extract_version_range("0 -", extract_llvm_version), None); } + +#[test] +#[should_panic(expected = "Duplicate revision: `rpass1` in line ` rpass1 rpass1`")] +fn test_duplicate_revisions() { + let config = config(); + parse_rs(&config, "// revisions: rpass1 rpass1"); +} diff --git a/src/tools/compiletest/src/main.rs b/src/tools/compiletest/src/main.rs index b32a6f08638cc..480916018619d 100644 --- a/src/tools/compiletest/src/main.rs +++ b/src/tools/compiletest/src/main.rs @@ -44,7 +44,7 @@ fn main() { } if !config.has_tidy && config.mode == Mode::Rustdoc { - eprintln!("warning: `tidy` is not installed; generated diffs will be harder to read"); + eprintln!("warning: `tidy` is not installed; diffs will not be generated"); } log_config(&config); @@ -909,7 +909,8 @@ fn extract_gdb_version(full_version_line: &str) -> Option<u32> { // This particular form is documented in the GNU coding standards: // https://www.gnu.org/prep/standards/html_node/_002d_002dversion.html#g_t_002d_002dversion - let mut splits = full_version_line.rsplit(' '); + let unbracketed_part = full_version_line.split('[').next().unwrap(); + let mut splits = unbracketed_part.trim_end().rsplit(' '); let version_string = splits.next().unwrap(); let mut splits = version_string.split('.'); diff --git a/src/tools/compiletest/src/runtest.rs b/src/tools/compiletest/src/runtest.rs index 2e7b42b6c7cf2..ecbaccf744dcd 100644 --- a/src/tools/compiletest/src/runtest.rs +++ b/src/tools/compiletest/src/runtest.rs @@ -1600,7 +1600,7 @@ impl<'test> TestCx<'test> { .args(&self.props.compile_flags); if self.config.mode == RustdocJson { - rustdoc.arg("--output-format").arg("json"); + rustdoc.arg("--output-format").arg("json").arg("-Zunstable-options"); } if let Some(ref linker) = self.config.linker { @@ -1909,8 +1909,7 @@ impl<'test> TestCx<'test> { } else { Command::new(&self.config.rustdoc_path.clone().expect("no rustdoc built yet")) }; - // FIXME Why is -L here? - rustc.arg(input_file); //.arg("-L").arg(&self.config.build_base); + rustc.arg(input_file); // Use a single thread for efficiency and a deterministic error message order rustc.arg("-Zthreads=1"); @@ -1952,6 +1951,7 @@ impl<'test> TestCx<'test> { if !self.props.compile_flags.iter().any(|s| s.starts_with("--error-format")) { rustc.args(&["--error-format", "json"]); } + rustc.arg("-Ccodegen-units=1"); rustc.arg("-Zui-testing"); rustc.arg("-Zdeduplicate-diagnostics=no"); rustc.arg("-Zemit-future-incompat-report"); @@ -1960,7 +1960,7 @@ impl<'test> TestCx<'test> { rustc.args(&[ "-Copt-level=1", "-Zdump-mir=all", - "-Zmir-opt-level=3", + "-Zmir-opt-level=4", "-Zvalidate-mir", "-Zdump-mir-exclude-pass-number", ]); @@ -2069,6 +2069,8 @@ impl<'test> TestCx<'test> { f = f.with_extra_extension("js"); } else if self.config.target.contains("wasm32") { f = f.with_extra_extension("wasm"); + } else if self.config.target.contains("spirv") { + f = f.with_extra_extension("spv"); } else if !env::consts::EXE_SUFFIX.is_empty() { f = f.with_extra_extension(env::consts::EXE_SUFFIX); } @@ -2365,6 +2367,9 @@ impl<'test> TestCx<'test> { } fn compare_to_default_rustdoc(&mut self, out_dir: &Path) { + if !self.config.has_tidy { + return; + } println!("info: generating a diff against nightly rustdoc"); let suffix = @@ -2426,10 +2431,8 @@ impl<'test> TestCx<'test> { } } }; - if self.config.has_tidy { - tidy_dir(out_dir); - tidy_dir(&compare_dir); - } + tidy_dir(out_dir); + tidy_dir(&compare_dir); let pager = { let output = Command::new("git").args(&["config", "--get", "core.pager"]).output().ok(); @@ -3137,8 +3140,14 @@ impl<'test> TestCx<'test> { output_kind: TestOutput, explicit_format: bool, ) -> usize { + let stderr_bits = format!("{}.stderr", get_pointer_width(&self.config.target)); let (stderr_kind, stdout_kind) = match output_kind { - TestOutput::Compile => (UI_STDERR, UI_STDOUT), + TestOutput::Compile => ( + { + if self.props.stderr_per_bitwidth { &stderr_bits } else { UI_STDERR } + }, + UI_STDOUT, + ), TestOutput::Run => (UI_RUN_STDERR, UI_RUN_STDOUT), }; @@ -3178,15 +3187,12 @@ impl<'test> TestCx<'test> { match output_kind { TestOutput::Compile => { if !self.props.dont_check_compiler_stdout { - errors += self.compare_output("stdout", &normalized_stdout, &expected_stdout); + errors += + self.compare_output(stdout_kind, &normalized_stdout, &expected_stdout); } if !self.props.dont_check_compiler_stderr { - let kind = if self.props.stderr_per_bitwidth { - format!("{}bit.stderr", get_pointer_width(&self.config.target)) - } else { - String::from("stderr") - }; - errors += self.compare_output(&kind, &normalized_stderr, &expected_stderr); + errors += + self.compare_output(stderr_kind, &normalized_stderr, &expected_stderr); } } TestOutput::Run => { @@ -3644,6 +3650,8 @@ impl<'test> TestCx<'test> { // Remove test annotations like `//~ ERROR text` from the output, // since they duplicate actual errors and make the output hard to read. + // This mirrors the regex in src/tools/tidy/src/style.rs, please update + // both if either are changed. normalized = Regex::new("\\s*//(\\[.*\\])?~.*").unwrap().replace_all(&normalized, "").into_owned(); @@ -3759,6 +3767,13 @@ impl<'test> TestCx<'test> { let mut files = vec![output_file]; if self.config.bless { + // Delete non-revision .stderr/.stdout file if revisions are used. + // Without this, we'd just generate the new files and leave the old files around. + if self.revision.is_some() { + let old = + expected_output_path(self.testpaths, None, &self.config.compare_mode, kind); + self.delete_file(&old); + } files.push(expected_output_path( self.testpaths, self.revision, diff --git a/src/tools/compiletest/src/tests.rs b/src/tools/compiletest/src/tests.rs index ea9bc1c1a5b7f..e6725dba26051 100644 --- a/src/tools/compiletest/src/tests.rs +++ b/src/tools/compiletest/src/tests.rs @@ -39,6 +39,9 @@ fn test_extract_gdb_version() { 7012000: "GNU gdb (GDB) 7.12", 7012000: "GNU gdb (GDB) 7.12.20161027-git", 7012050: "GNU gdb (GDB) 7.12.50.20161027-git", + + 9002000: "GNU gdb (Ubuntu 9.2-0ubuntu1~20.04) 9.2", + 10001000: "GNU gdb (GDB) 10.1 [GDB v10.1 for FreeBSD]", } } @@ -68,4 +71,8 @@ fn test_extract_llvm_version() { assert_eq!(extract_llvm_version("9.0.1-rust-1.43.0-dev"), Some(90001)); assert_eq!(extract_llvm_version("9.3.1-rust-1.43.0-dev"), Some(90301)); assert_eq!(extract_llvm_version("10.0.0-rust"), Some(100000)); + assert_eq!(extract_llvm_version("11.1.0"), Some(110100)); + assert_eq!(extract_llvm_version("12.0.0libcxx"), Some(120000)); + assert_eq!(extract_llvm_version("12.0.0-rc3"), Some(120000)); + assert_eq!(extract_llvm_version("13.0.0git"), Some(130000)); } diff --git a/src/tools/compiletest/src/util.rs b/src/tools/compiletest/src/util.rs index b302953708c18..7dbd70948b84d 100644 --- a/src/tools/compiletest/src/util.rs +++ b/src/tools/compiletest/src/util.rs @@ -128,6 +128,15 @@ const BIG_ENDIAN: &[&str] = &[ "sparcv9", ]; +static ASM_SUPPORTED_ARCHS: &[&str] = &[ + "x86", "x86_64", "arm", "aarch64", "riscv32", "riscv64", "nvptx64", "hexagon", "mips", + "mips64", "spirv", "wasm32", +]; + +pub fn has_asm_support(triple: &str) -> bool { + ASM_SUPPORTED_ARCHS.contains(&get_arch(triple)) +} + pub fn matches_os(triple: &str, name: &str) -> bool { // For the wasm32 bare target we ignore anything also ignored on emscripten // and then we also recognize `wasm32-bare` as the os for the target diff --git a/src/tools/jsondocck/Cargo.toml b/src/tools/jsondocck/Cargo.toml index 97052ef58d6f2..a6efc4c9a6b5b 100644 --- a/src/tools/jsondocck/Cargo.toml +++ b/src/tools/jsondocck/Cargo.toml @@ -12,3 +12,4 @@ lazy_static = "1.4" shlex = "0.1" serde = "1.0" serde_json = "1.0" +fs-err = "2.5.0" diff --git a/src/tools/jsondocck/src/cache.rs b/src/tools/jsondocck/src/cache.rs index 8a6a911321c34..a188750c56ae3 100644 --- a/src/tools/jsondocck/src/cache.rs +++ b/src/tools/jsondocck/src/cache.rs @@ -1,8 +1,10 @@ use crate::error::CkError; use serde_json::Value; use std::collections::HashMap; +use std::io; use std::path::{Path, PathBuf}; -use std::{fs, io}; + +use fs_err as fs; #[derive(Debug)] pub struct Cache { @@ -31,7 +33,11 @@ impl Cache { self.last_path = Some(resolve.clone()); resolve } else { - self.last_path.as_ref().unwrap().clone() + self.last_path + .as_ref() + // FIXME: Point to a line number + .expect("No last path set. Make sure to specify a full path before using `-`") + .clone() } } diff --git a/src/tools/jsondocck/src/main.rs b/src/tools/jsondocck/src/main.rs index 5020a4917a00a..216890d59ad6c 100644 --- a/src/tools/jsondocck/src/main.rs +++ b/src/tools/jsondocck/src/main.rs @@ -205,7 +205,21 @@ fn check_command(command: Command, cache: &mut Cache) -> Result<(), CkError> { let val = cache.get_value(&command.args[0])?; let results = select(&val, &command.args[1]).unwrap(); let pat = string_to_value(&command.args[2], cache); - results.contains(&pat.as_ref()) + let has = results.contains(&pat.as_ref()); + // Give better error for when @has check fails + if !command.negated && !has { + return Err(CkError::FailedCheck( + format!( + "{} matched to {:?} but didn't have {:?}", + &command.args[1], + results, + pat.as_ref() + ), + command, + )); + } else { + has + } } _ => unreachable!(), } @@ -225,7 +239,20 @@ fn check_command(command: Command, cache: &mut Cache) -> Result<(), CkError> { let val = cache.get_value(&command.args[0])?; let results = select(&val, &command.args[1]).unwrap(); let pat = string_to_value(&command.args[2], cache); - results.len() == 1 && results[0] == pat.as_ref() + let is = results.len() == 1 && results[0] == pat.as_ref(); + if !command.negated && !is { + return Err(CkError::FailedCheck( + format!( + "{} matched to {:?}, but expected {:?}", + &command.args[1], + results, + pat.as_ref() + ), + command, + )); + } else { + is + } } CommandKind::Set => { // @set <name> = <path> <jsonpath> @@ -233,7 +260,13 @@ fn check_command(command: Command, cache: &mut Cache) -> Result<(), CkError> { assert_eq!(command.args[1], "=", "Expected an `=`"); let val = cache.get_value(&command.args[2])?; let results = select(&val, &command.args[3]).unwrap(); - assert_eq!(results.len(), 1); + assert_eq!( + results.len(), + 1, + "Didn't get 1 result for `{}`: got {:?}", + command.args[3], + results + ); match results.len() { 0 => false, 1 => { @@ -279,7 +312,10 @@ fn check_command(command: Command, cache: &mut Cache) -> Result<(), CkError> { fn string_to_value<'a>(s: &str, cache: &'a Cache) -> Cow<'a, Value> { if s.starts_with("$") { - Cow::Borrowed(&cache.variables[&s[1..]]) + Cow::Borrowed(&cache.variables.get(&s[1..]).unwrap_or_else(|| { + // FIXME(adotinthevoid): Show line number + panic!("No variable: `{}`. Current state: `{:?}`", &s[1..], cache.variables) + })) } else { Cow::Owned(serde_json::from_str(s).unwrap()) } diff --git a/src/tools/linkchecker/main.rs b/src/tools/linkchecker/main.rs index 01e067ea0b803..c677d04917eaf 100644 --- a/src/tools/linkchecker/main.rs +++ b/src/tools/linkchecker/main.rs @@ -14,8 +14,6 @@ //! A few exceptions are allowed as there's known bugs in rustdoc, but this //! should catch the majority of "broken link" cases. -#![cfg_attr(bootstrap, feature(str_split_once))] - use std::collections::hash_map::Entry; use std::collections::{HashMap, HashSet}; use std::env; @@ -33,11 +31,6 @@ use crate::Redirect::*; // are cases where that does not work // [(generated_documentation_page, &[broken_links])] const LINKCHECK_EXCEPTIONS: &[(&str, &[&str])] = &[ - // These are methods on slice, and `Self` does not work on primitive impls - // in intra-doc links (primitive impls are weird) - // https://github.com/rust-lang/rust/issues/62834 is necessary to be - // able to link to slices - ("std/io/struct.IoSlice.html", &["#method.as_mut_ptr", "#method.sort_by_key"]), // These try to link to std::collections, but are defined in alloc // https://github.com/rust-lang/rust/issues/74481 ("std/collections/btree_map/struct.BTreeMap.html", &["#insert-and-complex-keys"]), diff --git a/src/tools/miri b/src/tools/miri index 2670839e1af54..a86eab3e6c3d6 160000 --- a/src/tools/miri +++ b/src/tools/miri @@ -1 +1 @@ -Subproject commit 2670839e1af540a496a0d889fce9ad42529ecc11 +Subproject commit a86eab3e6c3d645769bfdb2d09efd84d1e1fcbc0 diff --git a/src/tools/rls b/src/tools/rls index 3bd7215d48ba0..32c0fe006dcdc 160000 --- a/src/tools/rls +++ b/src/tools/rls @@ -1 +1 @@ -Subproject commit 3bd7215d48ba05f18401cc340ae8d71af002ba6d +Subproject commit 32c0fe006dcdc13e1ca0ca31de543e4436c1299e diff --git a/src/tools/rust-analyzer b/src/tools/rust-analyzer index 5df3ee8274fdb..7570212a544b8 160000 --- a/src/tools/rust-analyzer +++ b/src/tools/rust-analyzer @@ -1 +1 @@ -Subproject commit 5df3ee8274fdb7cdeb2b0871b4efea8cbf4724a1 +Subproject commit 7570212a544b8e973a7d57be3657aae6465028a7 diff --git a/src/tools/rust-demangler/Cargo.toml b/src/tools/rust-demangler/Cargo.toml index ac684a3c47e42..b7bc11253191b 100644 --- a/src/tools/rust-demangler/Cargo.toml +++ b/src/tools/rust-demangler/Cargo.toml @@ -8,6 +8,10 @@ edition = "2018" regex = "1.0" rustc-demangle = "0.1.17" +[lib] +name = "rust_demangler" +doctest = false + [[bin]] name = "rust-demangler" -path = "main.rs" +test = false diff --git a/src/tools/rust-demangler/README.md b/src/tools/rust-demangler/README.md new file mode 100644 index 0000000000000..4e8a689a13a47 --- /dev/null +++ b/src/tools/rust-demangler/README.md @@ -0,0 +1,36 @@ +# rust-demangler + +_Demangles rustc mangled names._ + +`rust-demangler` supports the requirements of the [`llvm-cov show -Xdemangler` +option](https://llvm.org/docs/CommandGuide/llvm-cov.html#cmdoption-llvm-cov-show-xdemangler), +to perform Rust-specific symbol demangling: + +> _The demangler is expected to read a newline-separated list of symbols from +> stdin and write a newline-separated list of the same length to stdout._ + +To use `rust-demangler` with `llvm-cov` for example: + +```shell +$ TARGET="${PWD}/build/x86_64-unknown-linux-gnu" +$ "${TARGET}"/llvm/bin/llvm-cov show \ + --Xdemangler=path/to/rust-demangler \ + --instr-profile=main.profdata ./main --show-line-counts-or-regions +``` + +`rust-demangler` is a Rust "extended tool", used in Rust compiler tests, and +optionally included in Rust distributions that enable coverage profiling. Symbol +demangling is implemented using the +[rustc-demangle](https://crates.io/crates/rustc-demangle) crate. + +_(Note, for Rust developers, the third-party tool +[`rustfilt`](https://crates.io/crates/rustfilt) also supports `llvm-cov` symbol +demangling. `rustfilt` is a more generalized tool that searches any body of +text, using pattern matching, to find and demangle Rust symbols.)_ + +## License + +Rust-demangler is distributed under the terms of both the MIT license and the +Apache License (Version 2.0). + +See [LICENSE-APACHE](/LICENSE-APACHE) and [LICENSE-MIT](/LICENSE-MIT) for details. diff --git a/src/tools/rust-demangler/main.rs b/src/tools/rust-demangler/main.rs deleted file mode 100644 index fd031ccb25249..0000000000000 --- a/src/tools/rust-demangler/main.rs +++ /dev/null @@ -1,130 +0,0 @@ -//! Demangles rustc mangled names. -//! -//! This tool uses https://crates.io/crates/rustc-demangle to convert an input buffer of -//! newline-separated mangled names into their demangled translations. -//! -//! This tool can be leveraged by other applications that support third-party demanglers. -//! It takes a list of mangled names (one per line) on standard input, and prints a corresponding -//! list of demangled names. The tool is designed to support other programs that can leverage a -//! third-party demangler, such as `llvm-cov`, via the `-Xdemangler=<path-to-demangler>` option. -//! -//! To use `rust-demangler`, first build the tool with: -//! -//! ```shell -//! $ ./x.py build rust-demangler -//! ``` -//! -//! Then, with `llvm-cov` for example, add the `-Xdemangler=...` option: -//! -//! ```shell -//! $ TARGET="${PWD}/build/x86_64-unknown-linux-gnu" -//! $ "${TARGET}"/llvm/bin/llvm-cov show --Xdemangler="${TARGET}"/stage0-tools-bin/rust-demangler \ -//! --instr-profile=main.profdata ./main --show-line-counts-or-regions -//! ``` -//! -//! Note regarding crate disambiguators: -//! -//! Some demangled symbol paths can include "crate disambiguator" suffixes, represented as a large -//! hexadecimal value enclosed in square braces, and appended to the name of the crate. a suffix to the -//! original crate name. For example, the `core` crate, here, includes a disambiguator: -//! -//! ```rust -//! <generics::Firework<f64> as core[a7a74cee373f048]::ops::drop::Drop>::drop -//! ``` -//! -//! These disambiguators are known to vary depending on environmental circumstances. As a result, -//! tests that compare results including demangled names can fail across development environments, -//! particularly with cross-platform testing. Also, the resulting crate paths are not syntactically -//! valid, and don't match the original source symbol paths, which can impact development tools. -//! -//! For these reasons, by default, `rust-demangler` uses a heuristic to remove crate disambiguators -//! from their original demangled representation before printing them to standard output. If crate -//! disambiguators are required, add the `-d` (or `--disambiguators`) flag, and the disambiguators -//! will not be removed. -//! -//! Also note that the disambiguators are stripped by a Regex pattern that is tolerant to some -//! variation in the number of hexadecimal digits. The disambiguators come from a hash value, which -//! typically generates a 16-digit hex representation on a 64-bit architecture; however, leading -//! zeros are not included, which can shorten the hex digit length, and a different hash algorithm -//! that might also be dependent on the architecture, might shorten the length even further. A -//! minimum length of 5 digits is assumed, which should be more than sufficient to support hex -//! representations that generate only 8-digits of precision with an extremely rare (but not -//! impossible) result with up to 3 leading zeros. -//! -//! Using a minimum number of digits less than 5 risks the possibility of stripping demangled name -//! components with a similar pattern. For example, some closures instantiated multiple times -//! include their own disambiguators, demangled as non-hashed zero-based indexes in square brackets. -//! These disambiguators seem to have more analytical value (for instance, in coverage analysis), so -//! they are not removed. - -use regex::Regex; -use rustc_demangle::demangle; -use std::io::{self, Read, Write}; - -const REPLACE_COLONS: &str = "::"; - -fn main() -> io::Result<()> { - // FIXME(richkadel): In Issue #77615 discussed updating the `rustc-demangle` library, to provide - // an option to generate demangled names without including crate disambiguators. If that - // happens, update this tool to use that option (if the `-d` flag is not set) instead stripping - // them via the Regex heuristic. The update the doc comments and help. - - // Strip hashed hexadecimal crate disambiguators. Leading zeros are not enforced, and can be - // different across different platform/architecture types, so while 16 hex digits are common, - // they can also be shorter. - // - // Also note that a demangled symbol path may include the `[<digits>]` pattern, with zero-based - // indexes (such as for closures, and possibly for types defined in anonymous scopes). Preferably - // these should not be stripped. - // - // The minimum length of 5 digits supports the possibility that some target architecture (maybe - // a 32-bit or smaller architecture) could generate a hash value with a maximum of 8 digits, - // and more than three leading zeros should be extremely unlikely. Conversely, it should be - // sufficient to assume the zero-based indexes for closures and anonymous scopes will never - // exceed the value 9999. - let mut strip_crate_disambiguators = Some(Regex::new(r"\[[a-f0-9]{5,16}\]::").unwrap()); - - let mut args = std::env::args(); - let progname = args.next().unwrap(); - for arg in args { - if arg == "--disambiguators" || arg == "-d" { - strip_crate_disambiguators = None; - } else { - eprintln!(); - eprintln!("Usage: {} [-d|--disambiguators]", progname); - eprintln!(); - eprintln!( - "This tool converts a list of Rust mangled symbols (one per line) into a\n\ - corresponding list of demangled symbols." - ); - eprintln!(); - eprintln!( - "With -d (--disambiguators), Rust symbols mangled with the v0 symbol mangler may\n\ - include crate disambiguators (a hexadecimal hash value, typically up to 16 digits\n\ - long, enclosed in square brackets)." - ); - eprintln!(); - eprintln!( - "By default, crate disambiguators are removed, using a heuristics-based regular\n\ - expression. (See the `rust-demangler` doc comments for more information.)" - ); - eprintln!(); - std::process::exit(1) - } - } - - let mut buffer = String::new(); - io::stdin().read_to_string(&mut buffer)?; - let lines = buffer.lines(); - let mut demangled_lines = Vec::new(); - for mangled in lines { - let mut demangled = demangle(mangled).to_string(); - if let Some(re) = &strip_crate_disambiguators { - demangled = re.replace_all(&demangled, REPLACE_COLONS).to_string(); - } - demangled_lines.push(demangled); - } - demangled_lines.push("".to_string()); - io::stdout().write_all(demangled_lines.join("\n").as_bytes())?; - Ok(()) -} diff --git a/src/tools/rust-demangler/src/lib.rs b/src/tools/rust-demangler/src/lib.rs new file mode 100644 index 0000000000000..1d972229d9536 --- /dev/null +++ b/src/tools/rust-demangler/src/lib.rs @@ -0,0 +1,21 @@ +use regex::Regex; +use rustc_demangle::demangle; +use std::str::Lines; + +const REPLACE_COLONS: &str = "::"; + +pub fn create_disambiguator_re() -> Regex { + Regex::new(r"\[[a-f0-9]{5,16}\]::").unwrap() +} + +pub fn demangle_lines(lines: Lines<'_>, strip_crate_disambiguators: Option<Regex>) -> Vec<String> { + let mut demangled_lines = Vec::new(); + for mangled in lines { + let mut demangled = demangle(mangled).to_string(); + if let Some(re) = &strip_crate_disambiguators { + demangled = re.replace_all(&demangled, REPLACE_COLONS).to_string(); + } + demangled_lines.push(demangled); + } + demangled_lines +} diff --git a/src/tools/rust-demangler/src/main.rs b/src/tools/rust-demangler/src/main.rs new file mode 100644 index 0000000000000..1b5ef5d2442ba --- /dev/null +++ b/src/tools/rust-demangler/src/main.rs @@ -0,0 +1,97 @@ +//! Demangles rustc mangled names. +//! +//! Note regarding crate disambiguators: +//! +//! Some demangled symbol paths can include "crate disambiguator" suffixes, represented as a large +//! hexadecimal value enclosed in square braces, and appended to the name of the crate. a suffix to the +//! original crate name. For example, the `core` crate, here, includes a disambiguator: +//! +//! ```rust +//! <generics::Firework<f64> as core[a7a74cee373f048]::ops::drop::Drop>::drop +//! ``` +//! +//! These disambiguators are known to vary depending on environmental circumstances. As a result, +//! tests that compare results including demangled names can fail across development environments, +//! particularly with cross-platform testing. Also, the resulting crate paths are not syntactically +//! valid, and don't match the original source symbol paths, which can impact development tools. +//! +//! For these reasons, by default, `rust-demangler` uses a heuristic to remove crate disambiguators +//! from their original demangled representation before printing them to standard output. If crate +//! disambiguators are required, add the `-d` (or `--disambiguators`) flag, and the disambiguators +//! will not be removed. +//! +//! Also note that the disambiguators are stripped by a Regex pattern that is tolerant to some +//! variation in the number of hexadecimal digits. The disambiguators come from a hash value, which +//! typically generates a 16-digit hex representation on a 64-bit architecture; however, leading +//! zeros are not included, which can shorten the hex digit length, and a different hash algorithm +//! that might also be dependent on the architecture, might shorten the length even further. A +//! minimum length of 5 digits is assumed, which should be more than sufficient to support hex +//! representations that generate only 8-digits of precision with an extremely rare (but not +//! impossible) result with up to 3 leading zeros. +//! +//! Using a minimum number of digits less than 5 risks the possibility of stripping demangled name +//! components with a similar pattern. For example, some closures instantiated multiple times +//! include their own disambiguators, demangled as non-hashed zero-based indexes in square brackets. +//! These disambiguators seem to have more analytical value (for instance, in coverage analysis), so +//! they are not removed. + +use rust_demangler::*; +use std::io::{self, Read, Write}; + +fn main() -> io::Result<()> { + // FIXME(richkadel): In Issue #77615 discussed updating the `rustc-demangle` library, to provide + // an option to generate demangled names without including crate disambiguators. If that + // happens, update this tool to use that option (if the `-d` flag is not set) instead stripping + // them via the Regex heuristic. The update the doc comments and help. + + // Strip hashed hexadecimal crate disambiguators. Leading zeros are not enforced, and can be + // different across different platform/architecture types, so while 16 hex digits are common, + // they can also be shorter. + // + // Also note that a demangled symbol path may include the `[<digits>]` pattern, with zero-based + // indexes (such as for closures, and possibly for types defined in anonymous scopes). Preferably + // these should not be stripped. + // + // The minimum length of 5 digits supports the possibility that some target architecture (maybe + // a 32-bit or smaller architecture) could generate a hash value with a maximum of 8 digits, + // and more than three leading zeros should be extremely unlikely. Conversely, it should be + // sufficient to assume the zero-based indexes for closures and anonymous scopes will never + // exceed the value 9999. + let mut strip_crate_disambiguators = Some(create_disambiguator_re()); + + let mut args = std::env::args(); + let progname = args.next().unwrap(); + for arg in args { + if arg == "--disambiguators" || arg == "-d" { + strip_crate_disambiguators = None; + } else { + eprintln!(); + eprintln!("Usage: {} [-d|--disambiguators]", progname); + eprintln!(); + eprintln!( + "This tool converts a list of Rust mangled symbols (one per line) into a\n\ + corresponding list of demangled symbols." + ); + eprintln!(); + eprintln!( + "With -d (--disambiguators), Rust symbols mangled with the v0 symbol mangler may\n\ + include crate disambiguators (a hexadecimal hash value, typically up to 16 digits\n\ + long, enclosed in square brackets)." + ); + eprintln!(); + eprintln!( + "By default, crate disambiguators are removed, using a heuristics-based regular\n\ + expression. (See the `rust-demangler` doc comments for more information.)" + ); + eprintln!(); + std::process::exit(1) + } + } + + let mut buffer = String::new(); + io::stdin().read_to_string(&mut buffer)?; + let mut demangled_lines = demangle_lines(buffer.lines(), strip_crate_disambiguators); + demangled_lines.push("".to_string()); // ensure a trailing newline + io::stdout().write_all(demangled_lines.join("\n").as_bytes())?; + Ok(()) +} diff --git a/src/tools/rust-demangler/tests/lib.rs b/src/tools/rust-demangler/tests/lib.rs new file mode 100644 index 0000000000000..5a67b42322535 --- /dev/null +++ b/src/tools/rust-demangler/tests/lib.rs @@ -0,0 +1,84 @@ +use rust_demangler::*; + +const MANGLED_INPUT: &str = r" +_RNvC6_123foo3bar +_RNqCs4fqI2P2rA04_11utf8_identsu30____7hkackfecea1cbdathfdh9hlq6y +_RNCNCNgCs6DXkGYLi8lr_2cc5spawn00B5_ +_RNCINkXs25_NgCsbmNqQUJIY6D_4core5sliceINyB9_4IterhENuNgNoBb_4iter8iterator8Iterator9rpositionNCNgNpB9_6memchr7memrchrs_0E0Bb_ +_RINbNbCskIICzLVDPPb_5alloc5alloc8box_freeDINbNiB4_5boxed5FnBoxuEp6OutputuEL_ECs1iopQbuBiw2_3std +INtC8arrayvec8ArrayVechKj7b_E +_RMCs4fqI2P2rA04_13const_genericINtB0_8UnsignedKhb_E +_RMCs4fqI2P2rA04_13const_genericINtB0_6SignedKs98_E +_RMCs4fqI2P2rA04_13const_genericINtB0_6SignedKanb_E +_RMCs4fqI2P2rA04_13const_genericINtB0_4BoolKb0_E +_RMCs4fqI2P2rA04_13const_genericINtB0_4BoolKb1_E +_RMCs4fqI2P2rA04_13const_genericINtB0_4CharKc76_E +_RMCs4fqI2P2rA04_13const_genericINtB0_4CharKca_E +_RMCs4fqI2P2rA04_13const_genericINtB0_4CharKc2202_E +_RNvNvMCs4fqI2P2rA04_13const_genericINtB4_3FooKpE3foo3FOO +_RC3foo.llvm.9D1C9369 +_RC3foo.llvm.9D1C9369@@16 +_RNvC9backtrace3foo.llvm.A5310EB9 +_RNvNtNtNtNtCs92dm3009vxr_4rand4rngs7adapter9reseeding4fork23FORK_HANDLER_REGISTERED.0.0 +"; + +const DEMANGLED_OUTPUT: &str = r" +123foo[0]::bar +utf8_idents[317d481089b8c8fe]::საჭმელად_გემრიელი_სადილი +cc[4d6468d6c9fd4bb3]::spawn::{closure#0}::{closure#0} +<core[846817f741e54dfd]::slice::Iter<u8> as core[846817f741e54dfd]::iter::iterator::Iterator>::rposition::<core[846817f741e54dfd]::slice::memchr::memrchr::{closure#1}>::{closure#0} +alloc[f15a878b47eb696b]::alloc::box_free::<dyn alloc[f15a878b47eb696b]::boxed::FnBox<(), Output = ()>> +INtC8arrayvec8ArrayVechKj7b_E +<const_generic[317d481089b8c8fe]::Unsigned<11: u8>> +<const_generic[317d481089b8c8fe]::Signed<152: i16>> +<const_generic[317d481089b8c8fe]::Signed<-11: i8>> +<const_generic[317d481089b8c8fe]::Bool<false: bool>> +<const_generic[317d481089b8c8fe]::Bool<true: bool>> +<const_generic[317d481089b8c8fe]::Char<'v': char>> +<const_generic[317d481089b8c8fe]::Char<'\n': char>> +<const_generic[317d481089b8c8fe]::Char<'∂': char>> +<const_generic[317d481089b8c8fe]::Foo<_>>::foo::FOO +foo[0] +foo[0] +backtrace[0]::foo +rand[693ea8e72247470f]::rngs::adapter::reseeding::fork::FORK_HANDLER_REGISTERED.0.0 +"; + +const DEMANGLED_OUTPUT_NO_CRATE_DISAMBIGUATORS: &str = r" +123foo[0]::bar +utf8_idents::საჭმელად_გემრიელი_სადილი +cc::spawn::{closure#0}::{closure#0} +<core::slice::Iter<u8> as core::iter::iterator::Iterator>::rposition::<core::slice::memchr::memrchr::{closure#1}>::{closure#0} +alloc::alloc::box_free::<dyn alloc::boxed::FnBox<(), Output = ()>> +INtC8arrayvec8ArrayVechKj7b_E +<const_generic::Unsigned<11: u8>> +<const_generic::Signed<152: i16>> +<const_generic::Signed<-11: i8>> +<const_generic::Bool<false: bool>> +<const_generic::Bool<true: bool>> +<const_generic::Char<'v': char>> +<const_generic::Char<'\n': char>> +<const_generic::Char<'∂': char>> +<const_generic::Foo<_>>::foo::FOO +foo[0] +foo[0] +backtrace[0]::foo +rand::rngs::adapter::reseeding::fork::FORK_HANDLER_REGISTERED.0.0 +"; + +#[test] +fn test_demangle_lines() { + let demangled_lines = demangle_lines(MANGLED_INPUT.lines(), None); + for (expected, actual) in DEMANGLED_OUTPUT.lines().zip(demangled_lines) { + assert_eq!(expected, actual); + } +} + +#[test] +fn test_demangle_lines_no_crate_disambiguators() { + let demangled_lines = demangle_lines(MANGLED_INPUT.lines(), Some(create_disambiguator_re())); + for (expected, actual) in DEMANGLED_OUTPUT_NO_CRATE_DISAMBIGUATORS.lines().zip(demangled_lines) + { + assert_eq!(expected, actual); + } +} diff --git a/src/tools/rustc-workspace-hack/Cargo.toml b/src/tools/rustc-workspace-hack/Cargo.toml index 8da7db2dfddb4..5776d56d187f8 100644 --- a/src/tools/rustc-workspace-hack/Cargo.toml +++ b/src/tools/rustc-workspace-hack/Cargo.toml @@ -71,7 +71,6 @@ proc-macro2 = { version = "1", features = ["default"] } quote = { version = "1", features = ["default"] } serde = { version = "1.0.82", features = ['derive'] } serde_json = { version = "1.0.31", features = ["raw_value", "unbounded_depth"] } -smallvec-0_6 = { package = "smallvec", version = "0.6.14", features = ['union', 'may_dangle'] } smallvec = { version = "1.6.1", features = ['union', 'may_dangle'] } syn = { version = "1", features = ['fold', 'full', 'extra-traits', 'visit', 'visit-mut'] } url = { version = "2.0", features = ['serde'] } diff --git a/src/tools/rustdoc-js/tester.js b/src/tools/rustdoc-js/tester.js index c21277de33513..e583bd225a9eb 100644 --- a/src/tools/rustdoc-js/tester.js +++ b/src/tools/rustdoc-js/tester.js @@ -246,7 +246,7 @@ function lookForEntry(entry, data) { return null; } -function loadMainJsAndIndex(mainJs, searchIndex, storageJs, crate) { +function loadSearchJsAndIndex(searchJs, searchIndex, storageJs, crate) { if (searchIndex[searchIndex.length - 1].length === 0) { searchIndex.pop(); } @@ -264,14 +264,15 @@ function loadMainJsAndIndex(mainJs, searchIndex, storageJs, crate) { // execQuery last parameter is built in buildIndex. // buildIndex requires the hashmap from search-index. var functionsToLoad = ["buildHrefAndPath", "pathSplitter", "levenshtein", "validateResult", - "handleAliases", "getQuery", "buildIndex", "execQuery", "execSearch"]; + "handleAliases", "getQuery", "buildIndex", "execQuery", "execSearch", + "removeEmptyStringsFromArray"]; ALIASES = {}; finalJS += 'window = { "currentCrate": "' + crate + '", rootPath: "../" };\n'; finalJS += loadThings(["hasOwnProperty", "onEach"], 'function', extractFunction, storageJs); - finalJS += loadThings(arraysToLoad, 'array', extractArrayVariable, mainJs); - finalJS += loadThings(variablesToLoad, 'variable', extractVariable, mainJs); - finalJS += loadThings(functionsToLoad, 'function', extractFunction, mainJs); + finalJS += loadThings(arraysToLoad, 'array', extractArrayVariable, searchJs); + finalJS += loadThings(variablesToLoad, 'variable', extractVariable, searchJs); + finalJS += loadThings(functionsToLoad, 'function', extractFunction, searchJs); var loaded = loadContent(finalJS); var index = loaded.buildIndex(searchIndex.rawSearchIndex); @@ -381,12 +382,12 @@ function runChecks(testFile, loaded, index) { } function load_files(doc_folder, resource_suffix, crate) { - var mainJs = readFile(path.join(doc_folder, "main" + resource_suffix + ".js")); + var searchJs = readFile(path.join(doc_folder, "search" + resource_suffix + ".js")); var storageJs = readFile(path.join(doc_folder, "storage" + resource_suffix + ".js")); var searchIndex = readFile( path.join(doc_folder, "search-index" + resource_suffix + ".js")).split("\n"); - return loadMainJsAndIndex(mainJs, searchIndex, storageJs, crate); + return loadSearchJsAndIndex(searchJs, searchIndex, storageJs, crate); } function showHelp() { diff --git a/src/tools/rustdoc/Cargo.toml b/src/tools/rustdoc/Cargo.toml index 36aa5916da733..d0c047ad6df14 100644 --- a/src/tools/rustdoc/Cargo.toml +++ b/src/tools/rustdoc/Cargo.toml @@ -13,3 +13,6 @@ path = "main.rs" [dependencies] rustdoc = { path = "../../librustdoc" } + +[features] +jemalloc = ['rustdoc/jemalloc'] diff --git a/src/tools/rustfmt b/src/tools/rustfmt index 7de6968ee2269..0bd2b1927c2b0 160000 --- a/src/tools/rustfmt +++ b/src/tools/rustfmt @@ -1 +1 @@ -Subproject commit 7de6968ee22696b7feb6b477a05656de89275291 +Subproject commit 0bd2b1927c2b02a6fe7447d58e897cf1f1a1d41f diff --git a/src/tools/tidy/Cargo.toml b/src/tools/tidy/Cargo.toml index 777d7be8fdc43..58c32993cb6ef 100644 --- a/src/tools/tidy/Cargo.toml +++ b/src/tools/tidy/Cargo.toml @@ -10,6 +10,7 @@ cargo_metadata = "0.11" regex = "1" lazy_static = "1" walkdir = "2" +crossbeam-utils = "0.8.0" [[bin]] name = "rust-tidy" diff --git a/src/tools/tidy/src/bins.rs b/src/tools/tidy/src/bins.rs index 62cfa85577f98..1d5ec5c31c67f 100644 --- a/src/tools/tidy/src/bins.rs +++ b/src/tools/tidy/src/bins.rs @@ -5,91 +5,126 @@ //! huge amount of bloat to the Git history, so it's good to just ensure we //! don't do that again. -use std::path::Path; +pub use os_impl::*; // All files are executable on Windows, so just check on Unix. #[cfg(windows)] -pub fn check(_path: &Path, _output: &Path, _bad: &mut bool) {} +mod os_impl { + use std::path::Path; + + pub fn check_filesystem_support(_sources: &[&Path], _output: &Path) -> bool { + return false; + } + + pub fn check(_path: &Path, _bad: &mut bool) {} +} #[cfg(unix)] -pub fn check(path: &Path, output: &Path, bad: &mut bool) { +mod os_impl { use std::fs; use std::os::unix::prelude::*; + use std::path::Path; use std::process::{Command, Stdio}; + enum FilesystemSupport { + Supported, + Unsupported, + ReadOnlyFs, + } + + use FilesystemSupport::*; + fn is_executable(path: &Path) -> std::io::Result<bool> { Ok(path.metadata()?.mode() & 0o111 != 0) } - // We want to avoid false positives on filesystems that do not support the - // executable bit. This occurs on some versions of Window's linux subsystem, - // for example. - // - // We try to create the temporary file first in the src directory, which is - // the preferred location as it's most likely to be on the same filesystem, - // and then in the output (`build`) directory if that fails. Sometimes we - // see the source directory mounted as read-only which means we can't - // readily create a file there to test. - // - // See #36706 and #74753 for context. - let mut temp_path = path.join("tidy-test-file"); - match fs::File::create(&temp_path).or_else(|_| { - temp_path = output.join("tidy-test-file"); - fs::File::create(&temp_path) - }) { - Ok(file) => { - let exec = is_executable(&temp_path).unwrap_or(false); - std::mem::drop(file); - std::fs::remove_file(&temp_path).expect("Deleted temp file"); - if exec { - // If the file is executable, then we assume that this - // filesystem does not track executability, so skip this check. - return; - } + pub fn check_filesystem_support(sources: &[&Path], output: &Path) -> bool { + // We want to avoid false positives on filesystems that do not support the + // executable bit. This occurs on some versions of Window's linux subsystem, + // for example. + // + // We try to create the temporary file first in the src directory, which is + // the preferred location as it's most likely to be on the same filesystem, + // and then in the output (`build`) directory if that fails. Sometimes we + // see the source directory mounted as read-only which means we can't + // readily create a file there to test. + // + // See #36706 and #74753 for context. + + fn check_dir(dir: &Path) -> FilesystemSupport { + let path = dir.join("tidy-test-file"); + match fs::File::create(&path) { + Ok(file) => { + let exec = is_executable(&path).unwrap_or(false); + std::mem::drop(file); + std::fs::remove_file(&path).expect("Deleted temp file"); + // If the file is executable, then we assume that this + // filesystem does not track executability, so skip this check. + return if exec { Unsupported } else { Supported }; + } + Err(e) => { + // If the directory is read-only or we otherwise don't have rights, + // just don't run this check. + // + // 30 is the "Read-only filesystem" code at least in one CI + // environment. + if e.raw_os_error() == Some(30) { + eprintln!("tidy: Skipping binary file check, read-only filesystem"); + return ReadOnlyFs; + } + + panic!("unable to create temporary file `{:?}`: {:?}", path, e); + } + }; } - Err(e) => { - // If the directory is read-only or we otherwise don't have rights, - // just don't run this check. - // - // 30 is the "Read-only filesystem" code at least in one CI - // environment. - if e.raw_os_error() == Some(30) { - eprintln!("tidy: Skipping binary file check, read-only filesystem"); - return; - } - panic!("unable to create temporary file `{:?}`: {:?}", temp_path, e); + for &source_dir in sources { + match check_dir(source_dir) { + Unsupported => return false, + ReadOnlyFs => { + return match check_dir(output) { + Supported => true, + _ => false, + }; + } + _ => {} + } } + + return true; } - super::walk_no_read( - path, - &mut |path| super::filter_dirs(path) || path.ends_with("src/etc"), - &mut |entry| { - let file = entry.path(); - let filename = file.file_name().unwrap().to_string_lossy(); - let extensions = [".py", ".sh"]; - if extensions.iter().any(|e| filename.ends_with(e)) { - return; - } + #[cfg(unix)] + pub fn check(path: &Path, bad: &mut bool) { + crate::walk_no_read( + path, + &mut |path| crate::filter_dirs(path) || path.ends_with("src/etc"), + &mut |entry| { + let file = entry.path(); + let filename = file.file_name().unwrap().to_string_lossy(); + let extensions = [".py", ".sh"]; + if extensions.iter().any(|e| filename.ends_with(e)) { + return; + } - if t!(is_executable(&file), file) { - let rel_path = file.strip_prefix(path).unwrap(); - let git_friendly_path = rel_path.to_str().unwrap().replace("\\", "/"); - let output = Command::new("git") - .arg("ls-files") - .arg(&git_friendly_path) - .current_dir(path) - .stderr(Stdio::null()) - .output() - .unwrap_or_else(|e| { - panic!("could not run git ls-files: {}", e); - }); - let path_bytes = rel_path.as_os_str().as_bytes(); - if output.status.success() && output.stdout.starts_with(path_bytes) { - tidy_error!(bad, "binary checked into source: {}", file.display()); + if t!(is_executable(&file), file) { + let rel_path = file.strip_prefix(path).unwrap(); + let git_friendly_path = rel_path.to_str().unwrap().replace("\\", "/"); + let output = Command::new("git") + .arg("ls-files") + .arg(&git_friendly_path) + .current_dir(path) + .stderr(Stdio::null()) + .output() + .unwrap_or_else(|e| { + panic!("could not run git ls-files: {}", e); + }); + let path_bytes = rel_path.as_os_str().as_bytes(); + if output.status.success() && output.stdout.starts_with(path_bytes) { + tidy_error!(bad, "binary checked into source: {}", file.display()); + } } - } - }, - ) + }, + ) + } } diff --git a/src/tools/tidy/src/deps.rs b/src/tools/tidy/src/deps.rs index 06dc16f767660..51f48d8f2ecc7 100644 --- a/src/tools/tidy/src/deps.rs +++ b/src/tools/tidy/src/deps.rs @@ -32,7 +32,6 @@ const EXCEPTIONS: &[(&str, &str)] = &[ ("fuchsia-zircon", "BSD-3-Clause"), // rustdoc, rustc, cargo (jobserver & tempdir) ("colored", "MPL-2.0"), // rustfmt ("ordslice", "Apache-2.0"), // rls - ("cloudabi", "BSD-2-Clause"), // (rls -> crossbeam-channel 0.2 -> rand 0.5) ("ryu", "Apache-2.0 OR BSL-1.0"), // rls/cargo/... (because of serde) ("bytesize", "Apache-2.0"), // cargo ("im-rc", "MPL-2.0+"), // cargo @@ -76,7 +75,6 @@ const PERMITTED_DEPENDENCIES: &[&str] = &[ "cfg-if", "chalk-derive", "chalk-ir", - "cloudabi", "cmake", "compiler_builtins", "cpuid-bool", @@ -123,6 +121,7 @@ const PERMITTED_DEPENDENCIES: &[&str] = &[ "measureme", "memchr", "memmap", + "memmap2", "memoffset", "miniz_oxide", "num_cpus", diff --git a/src/tools/tidy/src/error_codes_check.rs b/src/tools/tidy/src/error_codes_check.rs index a7199fdfce66b..55f824b63f2eb 100644 --- a/src/tools/tidy/src/error_codes_check.rs +++ b/src/tools/tidy/src/error_codes_check.rs @@ -48,6 +48,8 @@ fn check_error_code_explanation( } fn check_if_error_code_is_test_in_explanation(f: &str, err_code: &str) -> bool { + let mut ignore_found = false; + for line in f.lines() { let s = line.trim(); if s.starts_with("#### Note: this error code is no longer emitted by the compiler") { @@ -56,13 +58,13 @@ fn check_if_error_code_is_test_in_explanation(f: &str, err_code: &str) -> bool { if s.starts_with("```") { if s.contains("compile_fail") && s.contains(err_code) { return true; - } else if s.contains('(') { + } else if s.contains("ignore") { // It's very likely that we can't actually make it fail compilation... - return true; + ignore_found = true; } } } - false + ignore_found } macro_rules! some_or_continue { @@ -164,18 +166,32 @@ fn extract_error_codes_from_tests(f: &str, error_codes: &mut HashMap<String, boo } } -pub fn check(path: &Path, bad: &mut bool) { +pub fn check(paths: &[&Path], bad: &mut bool) { let mut errors = Vec::new(); + let mut found_explanations = 0; + let mut found_tests = 0; println!("Checking which error codes lack tests..."); let mut error_codes: HashMap<String, bool> = HashMap::new(); - super::walk(path, &mut |path| super::filter_dirs(path), &mut |entry, contents| { - let file_name = entry.file_name(); - if file_name == "error_codes.rs" { - extract_error_codes(contents, &mut error_codes, entry.path(), &mut errors); - } else if entry.path().extension() == Some(OsStr::new("stderr")) { - extract_error_codes_from_tests(contents, &mut error_codes); - } - }); + for path in paths { + super::walk(path, &mut |path| super::filter_dirs(path), &mut |entry, contents| { + let file_name = entry.file_name(); + if file_name == "error_codes.rs" { + extract_error_codes(contents, &mut error_codes, entry.path(), &mut errors); + found_explanations += 1; + } else if entry.path().extension() == Some(OsStr::new("stderr")) { + extract_error_codes_from_tests(contents, &mut error_codes); + found_tests += 1; + } + }); + } + if found_explanations == 0 { + eprintln!("No error code explanation was tested!"); + *bad = true; + } + if found_tests == 0 { + eprintln!("No error code was found in compilation errors!"); + *bad = true; + } if errors.is_empty() { println!("Found {} error codes", error_codes.len()); diff --git a/src/tools/tidy/src/lib.rs b/src/tools/tidy/src/lib.rs index 11d36751f67bb..fcb27dae9ea90 100644 --- a/src/tools/tidy/src/lib.rs +++ b/src/tools/tidy/src/lib.rs @@ -3,8 +3,6 @@ //! This library contains the tidy lints and exposes it //! to be used by tools. -#![cfg_attr(bootstrap, feature(str_split_once))] - use std::fs::File; use std::io::Read; use walkdir::{DirEntry, WalkDir}; @@ -55,6 +53,7 @@ pub mod unstable_book; fn filter_dirs(path: &Path) -> bool { let skip = [ + "tidy-test-file", "compiler/rustc_codegen_cranelift", "src/llvm-project", "library/backtrace", diff --git a/src/tools/tidy/src/main.rs b/src/tools/tidy/src/main.rs index 2ac96e404acb9..10356a2fdc5fc 100644 --- a/src/tools/tidy/src/main.rs +++ b/src/tools/tidy/src/main.rs @@ -6,15 +6,23 @@ use tidy::*; +use crossbeam_utils::thread::{scope, ScopedJoinHandle}; +use std::collections::VecDeque; use std::env; +use std::num::NonZeroUsize; use std::path::PathBuf; use std::process; +use std::str::FromStr; +use std::sync::atomic::{AtomicBool, Ordering}; fn main() { let root_path: PathBuf = env::args_os().nth(1).expect("need path to root of repo").into(); let cargo: PathBuf = env::args_os().nth(2).expect("need path to cargo").into(); let output_directory: PathBuf = env::args_os().nth(3).expect("need path to output directory").into(); + let concurrency: NonZeroUsize = + FromStr::from_str(&env::args().nth(4).expect("need concurrency")) + .expect("concurrency must be a number"); let src_path = root_path.join("src"); let library_path = root_path.join("library"); @@ -22,45 +30,84 @@ fn main() { let args: Vec<String> = env::args().skip(1).collect(); - let mut bad = false; let verbose = args.iter().any(|s| *s == "--verbose"); - // Checks over tests. - debug_artifacts::check(&src_path, &mut bad); - ui_tests::check(&src_path, &mut bad); - - // Checks that only make sense for the compiler. - errors::check(&compiler_path, &mut bad); - error_codes_check::check(&src_path, &mut bad); - - // Checks that only make sense for the std libs. - pal::check(&library_path, &mut bad); - - // Checks that need to be done for both the compiler and std libraries. - unit_tests::check(&src_path, &mut bad); - unit_tests::check(&compiler_path, &mut bad); - unit_tests::check(&library_path, &mut bad); - - bins::check(&src_path, &output_directory, &mut bad); - bins::check(&compiler_path, &output_directory, &mut bad); - bins::check(&library_path, &output_directory, &mut bad); - - style::check(&src_path, &mut bad); - style::check(&compiler_path, &mut bad); - style::check(&library_path, &mut bad); - - edition::check(&src_path, &mut bad); - edition::check(&compiler_path, &mut bad); - edition::check(&library_path, &mut bad); - - let collected = features::check(&src_path, &compiler_path, &library_path, &mut bad, verbose); - unstable_book::check(&src_path, collected, &mut bad); - - // Checks that are done on the cargo workspace. - deps::check(&root_path, &cargo, &mut bad); - extdeps::check(&root_path, &mut bad); - - if bad { + let bad = std::sync::Arc::new(AtomicBool::new(false)); + + scope(|s| { + let mut handles: VecDeque<ScopedJoinHandle<'_, ()>> = + VecDeque::with_capacity(concurrency.get()); + + macro_rules! check { + ($p:ident $(, $args:expr)* ) => { + while handles.len() >= concurrency.get() { + handles.pop_front().unwrap().join().unwrap(); + } + + let handle = s.spawn(|_| { + let mut flag = false; + $p::check($($args),* , &mut flag); + if (flag) { + bad.store(true, Ordering::Relaxed); + } + }); + handles.push_back(handle); + } + } + + // Checks that are done on the cargo workspace. + check!(deps, &root_path, &cargo); + check!(extdeps, &root_path); + + // Checks over tests. + check!(debug_artifacts, &src_path); + check!(ui_tests, &src_path); + + // Checks that only make sense for the compiler. + check!(errors, &compiler_path); + check!(error_codes_check, &[&src_path, &compiler_path]); + + // Checks that only make sense for the std libs. + check!(pal, &library_path); + + // Checks that need to be done for both the compiler and std libraries. + check!(unit_tests, &src_path); + check!(unit_tests, &compiler_path); + check!(unit_tests, &library_path); + + if bins::check_filesystem_support( + &[&src_path, &compiler_path, &library_path], + &output_directory, + ) { + check!(bins, &src_path); + check!(bins, &compiler_path); + check!(bins, &library_path); + } + + check!(style, &src_path); + check!(style, &compiler_path); + check!(style, &library_path); + + check!(edition, &src_path); + check!(edition, &compiler_path); + check!(edition, &library_path); + + let collected = { + while handles.len() >= concurrency.get() { + handles.pop_front().unwrap().join().unwrap(); + } + let mut flag = false; + let r = features::check(&src_path, &compiler_path, &library_path, &mut flag, verbose); + if flag { + bad.store(true, Ordering::Relaxed); + } + r + }; + check!(unstable_book, &src_path, collected); + }) + .unwrap(); + + if bad.load(Ordering::Relaxed) { eprintln!("some tidy checks failed"); process::exit(1); } diff --git a/src/tools/tidy/src/pal.rs b/src/tools/tidy/src/pal.rs index 1dba6b73b9361..144529d8641ee 100644 --- a/src/tools/tidy/src/pal.rs +++ b/src/tools/tidy/src/pal.rs @@ -40,35 +40,20 @@ const EXCEPTION_PATHS: &[&str] = &[ "library/panic_abort", "library/panic_unwind", "library/unwind", - // black_box implementation is LLVM-version specific and it uses - // target_os to tell targets with different LLVM-versions apart - // (e.g. `wasm32-unknown-emscripten` vs `wasm32-unknown-unknown`): - "library/core/src/hint.rs", "library/std/src/sys/", // Platform-specific code for std lives here. // This has the trailing slash so that sys_common is not excepted. "library/std/src/os", // Platform-specific public interfaces "library/rtstartup", // Not sure what to do about this. magic stuff for mingw - // temporary exceptions - "library/std/src/lib.rs", - "library/std/src/path.rs", - "library/std/src/f32.rs", - "library/std/src/f64.rs", // Integration test for platform-specific run-time feature detection: "library/std/tests/run-time-detect.rs", "library/std/src/net/test.rs", "library/std/src/net/addr", "library/std/src/net/udp", - "library/std/src/sys_common/mod.rs", - "library/std/src/sys_common/net.rs", - "library/std/src/sys_common/backtrace.rs", "library/std/src/sys_common/remutex.rs", "library/std/src/sync/mutex.rs", "library/std/src/sync/rwlock.rs", - // panic_unwind shims - "library/std/src/panicking.rs", "library/term", // Not sure how to make this crate portable, but test crate needs it. "library/test", // Probably should defer to unstable `std::sys` APIs. - "library/std/src/sync/mpsc", // some tests are only run on non-emscripten // std testing crates, okay for now at least "library/core/tests", "library/alloc/tests/lib.rs", @@ -79,13 +64,6 @@ const EXCEPTION_PATHS: &[&str] = &[ // we must use `#[cfg(windows)]` to conditionally compile the // correct `VaList` structure for windows. "library/core/src/ffi.rs", - // non-std crates - "src/test", - "src/tools", - "src/librustc", - "src/librustdoc", - "src/librustc_ast", - "src/bootstrap", ]; pub fn check(path: &Path, bad: &mut bool) { diff --git a/src/tools/tidy/src/style.rs b/src/tools/tidy/src/style.rs index 75c43343023f9..3d5f39e8c9049 100644 --- a/src/tools/tidy/src/style.rs +++ b/src/tools/tidy/src/style.rs @@ -16,6 +16,7 @@ //! A number of these checks can be opted-out of with various directives of the form: //! `// ignore-tidy-CHECK-NAME`. +use regex::Regex; use std::path::Path; /// Error code markdown is restricted to 80 columns because they can be @@ -41,6 +42,19 @@ C++ code used llvm_unreachable, which triggers undefined behavior when executed when assertions are disabled. Use llvm::report_fatal_error for increased robustness."; +const ANNOTATIONS_TO_IGNORE: &[&str] = &[ + "// @!has", + "// @has", + "// @matches", + "// CHECK", + "// EMIT_MIR", + "// compile-flags", + "// error-pattern", + "// gdb", + "// lldb", + "// normalize-stderr-test", +]; + /// Parser states for `line_is_url`. #[derive(Clone, Copy, PartialEq)] #[allow(non_camel_case_types)] @@ -92,12 +106,20 @@ fn line_is_url(is_error_code: bool, columns: usize, line: &str) -> bool { state == EXP_END } +/// Returns `true` if `line` can be ignored. This is the case when it contains +/// an annotation that is explicitly ignored. +fn should_ignore(line: &str) -> bool { + // Matches test annotations like `//~ ERROR text`. + // This mirrors the regex in src/tools/compiletest/src/runtest.rs, please + // update both if either are changed. + let re = Regex::new("\\s*//(\\[.*\\])?~.*").unwrap(); + re.is_match(line) || ANNOTATIONS_TO_IGNORE.iter().any(|a| line.contains(a)) +} + /// Returns `true` if `line` is allowed to be longer than the normal limit. -/// Currently there is only one exception, for long URLs, but more -/// may be added in the future. fn long_line_is_ok(extension: &str, is_error_code: bool, max_columns: usize, line: &str) -> bool { if extension != "md" || is_error_code { - if line_is_url(is_error_code, max_columns, line) { + if line_is_url(is_error_code, max_columns, line) || should_ignore(line) { return true; } } else if extension == "md" { diff --git a/src/tools/tidy/src/ui_tests.rs b/src/tools/tidy/src/ui_tests.rs index 659d5ae03c9d8..8334bc68ae729 100644 --- a/src/tools/tidy/src/ui_tests.rs +++ b/src/tools/tidy/src/ui_tests.rs @@ -7,8 +7,8 @@ use std::path::Path; const ENTRY_LIMIT: usize = 1000; // FIXME: The following limits should be reduced eventually. -const ROOT_ENTRY_LIMIT: usize = 1418; -const ISSUES_ENTRY_LIMIT: usize = 2582; +const ROOT_ENTRY_LIMIT: usize = 1388; +const ISSUES_ENTRY_LIMIT: usize = 2551; fn check_entries(path: &Path, bad: &mut bool) { let dirs = walkdir::WalkDir::new(&path.join("test/ui")) diff --git a/src/version b/src/version index a63cb35e6f0b4..3f4830156cbef 100644 --- a/src/version +++ b/src/version @@ -1 +1 @@ -1.52.0 +1.53.0

(&self, mut pred: P) -> usize + where + P: FnMut(&T) -> bool, + { + let (front, back) = self.as_slices(); + + if let Some(true) = back.first().map(|v| pred(v)) { + back.partition_point(pred) + front.len() + } else { + front.partition_point(pred) + } + } } impl VecDeque { @@ -2783,27 +2849,26 @@ impl From> for VecDeque { /// This avoids reallocating where possible, but the conditions for that are /// strict, and subject to change, and so shouldn't be relied upon unless the /// `Vec` came from `From>` and hasn't been reallocated. - fn from(other: Vec) -> Self { - unsafe { - let mut other = ManuallyDrop::new(other); - let other_buf = other.as_mut_ptr(); - let mut buf = RawVec::from_raw_parts(other_buf, other.capacity()); - let len = other.len(); - - // We need to extend the buf if it's not a power of two, too small - // or doesn't have at least one free space. - // We check if `T` is a ZST in the first condition, - // because `usize::MAX` (the capacity returned by `capacity()` for ZST) - // is not a power of two and thus it'll always try - // to reserve more memory which will panic for ZST (rust-lang/rust#78532) - if (!buf.capacity().is_power_of_two() && mem::size_of::() != 0) - || (buf.capacity() < (MINIMUM_CAPACITY + 1)) - || (buf.capacity() == len) - { - let cap = cmp::max(buf.capacity() + 1, MINIMUM_CAPACITY + 1).next_power_of_two(); - buf.reserve_exact(len, cap - len); + fn from(mut other: Vec) -> Self { + let len = other.len(); + if mem::size_of::() == 0 { + // There's no actual allocation for ZSTs to worry about capacity, + // but `VecDeque` can't handle as much length as `Vec`. + assert!(len < MAXIMUM_ZST_CAPACITY, "capacity overflow"); + } else { + // We need to resize if the capacity is not a power of two, too small or + // doesn't have at least one free space. We do this while it's still in + // the `Vec` so the items will drop on panic. + let min_cap = cmp::max(MINIMUM_CAPACITY, len) + 1; + let cap = cmp::max(min_cap, other.capacity()).next_power_of_two(); + if other.capacity() != cap { + other.reserve_exact(cap - len); } + } + unsafe { + let (other_buf, len, capacity) = other.into_raw_parts(); + let buf = RawVec::from_raw_parts(other_buf, capacity); VecDeque { tail: 0, head: len, buf } } } diff --git a/library/alloc/src/collections/vec_deque/tests.rs b/library/alloc/src/collections/vec_deque/tests.rs index 87e06fa394d38..6116cfe1d0110 100644 --- a/library/alloc/src/collections/vec_deque/tests.rs +++ b/library/alloc/src/collections/vec_deque/tests.rs @@ -457,6 +457,21 @@ fn test_from_vec() { assert!(vd.into_iter().eq(vec)); } } + + let vec = Vec::from([(); MAXIMUM_ZST_CAPACITY - 1]); + let vd = VecDeque::from(vec.clone()); + assert!(vd.cap().is_power_of_two()); + assert_eq!(vd.len(), vec.len()); +} + +#[test] +#[should_panic = "capacity overflow"] +fn test_from_vec_zst_overflow() { + use crate::vec::Vec; + let vec = Vec::from([(); MAXIMUM_ZST_CAPACITY]); + let vd = VecDeque::from(vec.clone()); // no room for +1 + assert!(vd.cap().is_power_of_two()); + assert_eq!(vd.len(), vec.len()); } #[test] diff --git a/library/alloc/src/fmt.rs b/library/alloc/src/fmt.rs index f9424b1d74744..3ef55f06e5198 100644 --- a/library/alloc/src/fmt.rs +++ b/library/alloc/src/fmt.rs @@ -19,6 +19,10 @@ //! format!("{value}", value=4); // => "4" //! format!("{} {}", 1, 2); // => "1 2" //! format!("{:04}", 42); // => "0042" with leading zeros +//! format!("{:#?}", (100, 200)); // => "( +//! // 100, +//! // 200, +//! // )" //! ``` //! //! From these, you can see that the first argument is a format string. It is @@ -157,13 +161,12 @@ //! //! * `+` - This is intended for numeric types and indicates that the sign //! should always be printed. Positive signs are never printed by -//! default, and the negative sign is only printed by default for the -//! `Signed` trait. This flag indicates that the correct sign (`+` or `-`) -//! should always be printed. +//! default, and the negative sign is only printed by default for signed values. +//! This flag indicates that the correct sign (`+` or `-`) should always be printed. //! * `-` - Currently not used //! * `#` - This flag indicates that the "alternate" form of printing should //! be used. The alternate forms are: -//! * `#?` - pretty-print the [`Debug`] formatting +//! * `#?` - pretty-print the [`Debug`] formatting (adds linebreaks and indentation) //! * `#x` - precedes the argument with a `0x` //! * `#X` - precedes the argument with a `0x` //! * `#b` - precedes the argument with a `0b` diff --git a/library/alloc/src/lib.rs b/library/alloc/src/lib.rs index e702e02d2e426..14cb1d3b405c2 100644 --- a/library/alloc/src/lib.rs +++ b/library/alloc/src/lib.rs @@ -103,12 +103,12 @@ #![feature(fn_traits)] #![feature(fundamental)] #![feature(inplace_iteration)] -#![feature(int_bits_const)] // Technically, this is a bug in rustdoc: rustdoc sees the documentation on `#[lang = slice_alloc]` // blocks is for `&[T]`, which also has documentation using this feature in `core`, and gets mad // that the feature-gate isn't enabled. Ideally, it wouldn't check for the feature gate for docs // from other crates, but since this can only appear for lang items, it doesn't seem worth fixing. #![feature(intra_doc_pointers)] +#![feature(iter_zip)] #![feature(lang_items)] #![feature(layout_for_ptr)] #![feature(maybe_uninit_ref)] @@ -118,7 +118,7 @@ #![feature(nonnull_slice_from_raw_parts)] #![feature(auto_traits)] #![feature(option_result_unwrap_unchecked)] -#![feature(or_patterns)] +#![cfg_attr(bootstrap, feature(or_patterns))] #![feature(pattern)] #![feature(ptr_internals)] #![feature(rustc_attrs)] @@ -133,7 +133,6 @@ #![feature(trusted_len)] #![feature(unboxed_closures)] #![feature(unicode_internals)] -#![feature(unsafe_block_in_unsafe_fn)] #![feature(unsize)] #![feature(unsized_fn_params)] #![feature(allocator_internals)] @@ -142,7 +141,7 @@ #![feature(alloc_layout_extra)] #![feature(trusted_random_access)] #![feature(try_trait)] -#![feature(type_alias_impl_trait)] +#![feature(min_type_alias_impl_trait)] #![feature(associated_type_bounds)] #![feature(slice_group_by)] #![feature(decl_macro)] diff --git a/library/alloc/src/raw_vec.rs b/library/alloc/src/raw_vec.rs index 56f4ebe57f8af..dc02c9c883ea0 100644 --- a/library/alloc/src/raw_vec.rs +++ b/library/alloc/src/raw_vec.rs @@ -315,8 +315,24 @@ impl RawVec { /// # vector.push_all(&[1, 3, 5, 7, 9]); /// # } /// ``` + #[inline] pub fn reserve(&mut self, len: usize, additional: usize) { - handle_reserve(self.try_reserve(len, additional)); + // Callers expect this function to be very cheap when there is already sufficient capacity. + // Therefore, we move all the resizing and error-handling logic from grow_amortized and + // handle_reserve behind a call, while making sure that the this function is likely to be + // inlined as just a comparison and a call if the comparison fails. + #[cold] + fn do_reserve_and_handle( + slf: &mut RawVec, + len: usize, + additional: usize, + ) { + handle_reserve(slf.grow_amortized(len, additional)); + } + + if self.needs_to_grow(len, additional) { + do_reserve_and_handle(self, len, additional); + } } /// The same as `reserve`, but returns on errors instead of panicking or aborting. diff --git a/library/alloc/src/rc.rs b/library/alloc/src/rc.rs index dac4acc4692a2..cb4af7c5cd151 100644 --- a/library/alloc/src/rc.rs +++ b/library/alloc/src/rc.rs @@ -910,6 +910,73 @@ impl Rc { this.inner().strong() } + /// Increments the strong reference count on the `Rc` associated with the + /// provided pointer by one. + /// + /// # Safety + /// + /// The pointer must have been obtained through `Rc::into_raw`, and the + /// associated `Rc` instance must be valid (i.e. the strong count must be at + /// least 1) for the duration of this method. + /// + /// # Examples + /// + /// ``` + /// use std::rc::Rc; + /// + /// let five = Rc::new(5); + /// + /// unsafe { + /// let ptr = Rc::into_raw(five); + /// Rc::increment_strong_count(ptr); + /// + /// let five = Rc::from_raw(ptr); + /// assert_eq!(2, Rc::strong_count(&five)); + /// } + /// ``` + #[inline] + #[stable(feature = "rc_mutate_strong_count", since = "1.53.0")] + pub unsafe fn increment_strong_count(ptr: *const T) { + // Retain Rc, but don't touch refcount by wrapping in ManuallyDrop + let rc = unsafe { mem::ManuallyDrop::new(Rc::::from_raw(ptr)) }; + // Now increase refcount, but don't drop new refcount either + let _rc_clone: mem::ManuallyDrop<_> = rc.clone(); + } + + /// Decrements the strong reference count on the `Rc` associated with the + /// provided pointer by one. + /// + /// # Safety + /// + /// The pointer must have been obtained through `Rc::into_raw`, and the + /// associated `Rc` instance must be valid (i.e. the strong count must be at + /// least 1) when invoking this method. This method can be used to release + /// the final `Rc` and backing storage, but **should not** be called after + /// the final `Rc` has been released. + /// + /// # Examples + /// + /// ``` + /// use std::rc::Rc; + /// + /// let five = Rc::new(5); + /// + /// unsafe { + /// let ptr = Rc::into_raw(five); + /// Rc::increment_strong_count(ptr); + /// + /// let five = Rc::from_raw(ptr); + /// assert_eq!(2, Rc::strong_count(&five)); + /// Rc::decrement_strong_count(ptr); + /// assert_eq!(1, Rc::strong_count(&five)); + /// } + /// ``` + #[inline] + #[stable(feature = "rc_mutate_strong_count", since = "1.53.0")] + pub unsafe fn decrement_strong_count(ptr: *const T) { + unsafe { mem::drop(Rc::from_raw(ptr)) }; + } + /// Returns `true` if there are no other `Rc` or [`Weak`] pointers to /// this allocation. #[inline] @@ -1848,8 +1915,11 @@ impl> ToRcSlice for I { Rc::from_iter_exact(self, low) } } else { - // Fall back to normal implementation. - self.collect::>().into() + // TrustedLen contract guarantees that `upper_bound == `None` implies an iterator + // length exceeding `usize::MAX`. + // The default implementation would collect into a vec which would panic. + // Thus we panic here immediately without invoking `Vec` code. + panic!("capacity overflow"); } } } @@ -2260,8 +2330,8 @@ impl fmt::Debug for Weak { #[stable(feature = "downgraded_weak", since = "1.10.0")] impl Default for Weak { - /// Constructs a new `Weak`, allocating memory for `T` without initializing - /// it. Calling [`upgrade`] on the return value always gives [`None`]. + /// Constructs a new `Weak`, without allocating any memory. + /// Calling [`upgrade`] on the return value always gives [`None`]. /// /// [`None`]: Option /// [`upgrade`]: Weak::upgrade diff --git a/library/alloc/src/slice.rs b/library/alloc/src/slice.rs index 0c911cea1bb1e..036b84bb1d55f 100644 --- a/library/alloc/src/slice.rs +++ b/library/alloc/src/slice.rs @@ -231,7 +231,7 @@ impl [T] { /// /// When applicable, unstable sorting is preferred because it is generally faster than stable /// sorting and it doesn't allocate auxiliary memory. - /// See [`sort_unstable`](#method.sort_unstable). + /// See [`sort_unstable`](slice::sort_unstable). /// /// # Current implementation /// @@ -282,7 +282,7 @@ impl [T] { /// /// When applicable, unstable sorting is preferred because it is generally faster than stable /// sorting and it doesn't allocate auxiliary memory. - /// See [`sort_unstable_by`](#method.sort_unstable_by). + /// See [`sort_unstable_by`](slice::sort_unstable_by). /// /// # Current implementation /// @@ -320,12 +320,12 @@ impl [T] { /// worst-case, where the key function is *O*(*m*). /// /// For expensive key functions (e.g. functions that are not simple property accesses or - /// basic operations), [`sort_by_cached_key`](#method.sort_by_cached_key) is likely to be + /// basic operations), [`sort_by_cached_key`](slice::sort_by_cached_key) is likely to be /// significantly faster, as it does not recompute element keys. /// /// When applicable, unstable sorting is preferred because it is generally faster than stable /// sorting and it doesn't allocate auxiliary memory. - /// See [`sort_unstable_by_key`](#method.sort_unstable_by_key). + /// See [`sort_unstable_by_key`](slice::sort_unstable_by_key). /// /// # Current implementation /// @@ -363,7 +363,7 @@ impl [T] { /// worst-case, where the key function is *O*(*m*). /// /// For simple key functions (e.g., functions that are property accesses or - /// basic operations), [`sort_by_key`](#method.sort_by_key) is likely to be + /// basic operations), [`sort_by_key`](slice::sort_by_key) is likely to be /// faster. /// /// # Current implementation @@ -642,7 +642,7 @@ impl [u8] { /// /// To uppercase the value in-place, use [`make_ascii_uppercase`]. /// - /// [`make_ascii_uppercase`]: u8::make_ascii_uppercase + /// [`make_ascii_uppercase`]: slice::make_ascii_uppercase #[stable(feature = "ascii_methods_on_intrinsics", since = "1.23.0")] #[inline] pub fn to_ascii_uppercase(&self) -> Vec { @@ -659,7 +659,7 @@ impl [u8] { /// /// To lowercase the value in-place, use [`make_ascii_lowercase`]. /// - /// [`make_ascii_lowercase`]: u8::make_ascii_lowercase + /// [`make_ascii_lowercase`]: slice::make_ascii_lowercase #[stable(feature = "ascii_methods_on_intrinsics", since = "1.23.0")] #[inline] pub fn to_ascii_lowercase(&self) -> Vec { diff --git a/library/alloc/src/str.rs b/library/alloc/src/str.rs index 8c64bc0801259..879af7cf4dd4d 100644 --- a/library/alloc/src/str.rs +++ b/library/alloc/src/str.rs @@ -46,6 +46,8 @@ pub use core::str::pattern; pub use core::str::EncodeUtf16; #[stable(feature = "split_ascii_whitespace", since = "1.34.0")] pub use core::str::SplitAsciiWhitespace; +#[stable(feature = "split_inclusive", since = "1.53.0")] +pub use core::str::SplitInclusive; #[stable(feature = "rust1", since = "1.0.0")] pub use core::str::SplitWhitespace; #[stable(feature = "rust1", since = "1.0.0")] @@ -90,8 +92,8 @@ impl> Join<&str> for [S] { } } -macro_rules! spezialize_for_lengths { - ($separator:expr, $target:expr, $iter:expr; $($num:expr),*) => { +macro_rules! specialize_for_lengths { + ($separator:expr, $target:expr, $iter:expr; $($num:expr),*) => {{ let mut target = $target; let iter = $iter; let sep_bytes = $separator; @@ -102,7 +104,8 @@ macro_rules! spezialize_for_lengths { $num => { for s in iter { copy_slice_and_advance!(target, sep_bytes); - copy_slice_and_advance!(target, s.borrow().as_ref()); + let content_bytes = s.borrow().as_ref(); + copy_slice_and_advance!(target, content_bytes); } }, )* @@ -110,11 +113,13 @@ macro_rules! spezialize_for_lengths { // arbitrary non-zero size fallback for s in iter { copy_slice_and_advance!(target, sep_bytes); - copy_slice_and_advance!(target, s.borrow().as_ref()); + let content_bytes = s.borrow().as_ref(); + copy_slice_and_advance!(target, content_bytes); } } } - }; + target + }} } macro_rules! copy_slice_and_advance { @@ -153,30 +158,33 @@ where // if the `len` calculation overflows, we'll panic // we would have run out of memory anyway and the rest of the function requires // the entire Vec pre-allocated for safety - let len = sep_len + let reserved_len = sep_len .checked_mul(iter.len()) .and_then(|n| { slice.iter().map(|s| s.borrow().as_ref().len()).try_fold(n, usize::checked_add) }) .expect("attempt to join into collection with len > usize::MAX"); - // crucial for safety - let mut result = Vec::with_capacity(len); - assert!(result.capacity() >= len); + // prepare an uninitialized buffer + let mut result = Vec::with_capacity(reserved_len); + debug_assert!(result.capacity() >= reserved_len); result.extend_from_slice(first.borrow().as_ref()); unsafe { - { - let pos = result.len(); - let target = result.get_unchecked_mut(pos..len); - - // copy separator and slices over without bounds checks - // generate loops with hardcoded offsets for small separators - // massive improvements possible (~ x2) - spezialize_for_lengths!(sep, target, iter; 0, 1, 2, 3, 4); - } - result.set_len(len); + let pos = result.len(); + let target = result.get_unchecked_mut(pos..reserved_len); + + // copy separator and slices over without bounds checks + // generate loops with hardcoded offsets for small separators + // massive improvements possible (~ x2) + let remain = specialize_for_lengths!(sep, target, iter; 0, 1, 2, 3, 4); + + // A weird borrow implementation may return different + // slices for the length calculation and the actual copy. + // Make sure we don't expose uninitialized bytes to the caller. + let result_len = reserved_len - remain.len(); + result.set_len(result_len); } result } diff --git a/library/alloc/src/string.rs b/library/alloc/src/string.rs index b567d0a2fe2d9..f4ec4a36ffd2b 100644 --- a/library/alloc/src/string.rs +++ b/library/alloc/src/string.rs @@ -360,7 +360,7 @@ impl String { /// let s = String::new(); /// ``` #[inline] - #[rustc_const_stable(feature = "const_string_new", since = "1.32.0")] + #[rustc_const_stable(feature = "const_string_new", since = "1.39.0")] #[stable(feature = "rust1", since = "1.0.0")] pub const fn new() -> String { String { vec: Vec::new() } @@ -1202,6 +1202,62 @@ impl String { ch } + /// Remove all matches of pattern `pat` in the `String`. + /// + /// # Examples + /// + /// ``` + /// #![feature(string_remove_matches)] + /// let mut s = String::from("Trees are not green, the sky is not blue."); + /// s.remove_matches("not "); + /// assert_eq!("Trees are green, the sky is blue.", s); + /// ``` + /// + /// Matches will be detected and removed iteratively, so in cases where + /// patterns overlap, only the first pattern will be removed: + /// + /// ``` + /// #![feature(string_remove_matches)] + /// let mut s = String::from("banana"); + /// s.remove_matches("ana"); + /// assert_eq!("bna", s); + /// ``` + #[unstable(feature = "string_remove_matches", reason = "new API", issue = "72826")] + pub fn remove_matches<'a, P>(&'a mut self, pat: P) + where + P: for<'x> Pattern<'x>, + { + use core::str::pattern::Searcher; + + let matches = { + let mut searcher = pat.into_searcher(self); + let mut matches = Vec::new(); + + while let Some(m) = searcher.next_match() { + matches.push(m); + } + + matches + }; + + let len = self.len(); + let mut shrunk_by = 0; + + // SAFETY: start and end will be on utf8 byte boundaries per + // the Searcher docs + unsafe { + for (start, end) in matches { + ptr::copy( + self.vec.as_mut_ptr().add(end - shrunk_by), + self.vec.as_mut_ptr().add(start - shrunk_by), + len - end, + ); + shrunk_by += end - start; + } + self.vec.set_len(len - shrunk_by); + } + } + /// Retains only the characters specified by the predicate. /// /// In other words, remove all characters `c` such that `f(c)` returns `false`. @@ -1233,37 +1289,44 @@ impl String { where F: FnMut(char) -> bool, { - let len = self.len(); - let mut del_bytes = 0; - let mut idx = 0; + struct SetLenOnDrop<'a> { + s: &'a mut String, + idx: usize, + del_bytes: usize, + } - unsafe { - self.vec.set_len(0); + impl<'a> Drop for SetLenOnDrop<'a> { + fn drop(&mut self) { + let new_len = self.idx - self.del_bytes; + debug_assert!(new_len <= self.s.len()); + unsafe { self.s.vec.set_len(new_len) }; + } } - while idx < len { - let ch = unsafe { self.get_unchecked(idx..len).chars().next().unwrap() }; + let len = self.len(); + let mut guard = SetLenOnDrop { s: self, idx: 0, del_bytes: 0 }; + + while guard.idx < len { + let ch = unsafe { guard.s.get_unchecked(guard.idx..len).chars().next().unwrap() }; let ch_len = ch.len_utf8(); if !f(ch) { - del_bytes += ch_len; - } else if del_bytes > 0 { + guard.del_bytes += ch_len; + } else if guard.del_bytes > 0 { unsafe { ptr::copy( - self.vec.as_ptr().add(idx), - self.vec.as_mut_ptr().add(idx - del_bytes), + guard.s.vec.as_ptr().add(guard.idx), + guard.s.vec.as_mut_ptr().add(guard.idx - guard.del_bytes), ch_len, ); } } // Point idx to the next char - idx += ch_len; + guard.idx += ch_len; } - unsafe { - self.vec.set_len(len - del_bytes); - } + drop(guard); } /// Inserts a character into this `String` at a byte position. @@ -2352,6 +2415,16 @@ impl<'a> From> for String { #[stable(feature = "rust1", since = "1.0.0")] impl<'a> From<&'a str> for Cow<'a, str> { + /// Converts a string slice into a Borrowed variant. + /// No heap allocation is performed, and the string + /// is not copied. + /// + /// # Example + /// + /// ``` + /// # use std::borrow::Cow; + /// assert_eq!(Cow::from("eggplant"), Cow::Borrowed("eggplant")); + /// ``` #[inline] fn from(s: &'a str) -> Cow<'a, str> { Cow::Borrowed(s) @@ -2360,6 +2433,18 @@ impl<'a> From<&'a str> for Cow<'a, str> { #[stable(feature = "rust1", since = "1.0.0")] impl<'a> From for Cow<'a, str> { + /// Converts a String into an Owned variant. + /// No heap allocation is performed, and the string + /// is not copied. + /// + /// # Example + /// + /// ``` + /// # use std::borrow::Cow; + /// let s = "eggplant".to_string(); + /// let s2 = "eggplant".to_string(); + /// assert_eq!(Cow::from(s), Cow::<'static, str>::Owned(s2)); + /// ``` #[inline] fn from(s: String) -> Cow<'a, str> { Cow::Owned(s) @@ -2368,6 +2453,17 @@ impl<'a> From for Cow<'a, str> { #[stable(feature = "cow_from_string_ref", since = "1.28.0")] impl<'a> From<&'a String> for Cow<'a, str> { + /// Converts a String reference into a Borrowed variant. + /// No heap allocation is performed, and the string + /// is not copied. + /// + /// # Example + /// + /// ``` + /// # use std::borrow::Cow; + /// let s = "eggplant".to_string(); + /// assert_eq!(Cow::from(&s), Cow::Borrowed("eggplant")); + /// ``` #[inline] fn from(s: &'a String) -> Cow<'a, str> { Cow::Borrowed(s.as_str()) diff --git a/library/alloc/src/sync.rs b/library/alloc/src/sync.rs index aeae888dddc03..1b7e656cefd9a 100644 --- a/library/alloc/src/sync.rs +++ b/library/alloc/src/sync.rs @@ -2481,8 +2481,11 @@ impl> ToArcSlice for I { Arc::from_iter_exact(self, low) } } else { - // Fall back to normal implementation. - self.collect::>().into() + // TrustedLen contract guarantees that `upper_bound == `None` implies an iterator + // length exceeding `usize::MAX`. + // The default implementation would collect into a vec which would panic. + // Thus we panic here immediately without invoking `Vec` code. + panic!("capacity overflow"); } } } diff --git a/library/alloc/src/task.rs b/library/alloc/src/task.rs index 58a9ae77244fb..528ee4ff1542c 100644 --- a/library/alloc/src/task.rs +++ b/library/alloc/src/task.rs @@ -87,6 +87,9 @@ pub trait Wake { #[stable(feature = "wake_trait", since = "1.51.0")] impl From> for Waker { + /// Use a `Wake`-able type as a `Waker`. + /// + /// No heap allocations or atomic operations are used for this conversion. fn from(waker: Arc) -> Waker { // SAFETY: This is safe because raw_waker safely constructs // a RawWaker from Arc. @@ -96,6 +99,9 @@ impl From> for Waker { #[stable(feature = "wake_trait", since = "1.51.0")] impl From> for RawWaker { + /// Use a `Wake`-able type as a `RawWaker`. + /// + /// No heap allocations or atomic operations are used for this conversion. fn from(waker: Arc) -> RawWaker { raw_waker(waker) } diff --git a/library/alloc/src/tests.rs b/library/alloc/src/tests.rs index bddaab0c76188..b4741c35c583f 100644 --- a/library/alloc/src/tests.rs +++ b/library/alloc/src/tests.rs @@ -49,17 +49,17 @@ fn test_show() { let b = Box::new(Test) as Box; let a_str = format!("{:?}", a); let b_str = format!("{:?}", b); - assert_eq!(a_str, "Any"); - assert_eq!(b_str, "Any"); + assert_eq!(a_str, "Any { .. }"); + assert_eq!(b_str, "Any { .. }"); static EIGHT: usize = 8; static TEST: Test = Test; let a = &EIGHT as &dyn Any; let b = &TEST as &dyn Any; let s = format!("{:?}", a); - assert_eq!(s, "Any"); + assert_eq!(s, "Any { .. }"); let s = format!("{:?}", b); - assert_eq!(s, "Any"); + assert_eq!(s, "Any { .. }"); } #[test] diff --git a/library/alloc/src/vec/into_iter.rs b/library/alloc/src/vec/into_iter.rs index bcbdffabc7fbe..324e894bafd23 100644 --- a/library/alloc/src/vec/into_iter.rs +++ b/library/alloc/src/vec/into_iter.rs @@ -85,20 +85,29 @@ impl IntoIter { ptr::slice_from_raw_parts_mut(self.ptr as *mut T, self.len()) } - pub(super) fn drop_remaining(&mut self) { - unsafe { - ptr::drop_in_place(self.as_mut_slice()); - } - self.ptr = self.end; - } + /// Drops remaining elements and relinquishes the backing allocation. + /// + /// This is roughly equivalent to the following, but more efficient + /// + /// ``` + /// # let mut into_iter = Vec::::with_capacity(10).into_iter(); + /// (&mut into_iter).for_each(core::mem::drop); + /// unsafe { core::ptr::write(&mut into_iter, Vec::new().into_iter()); } + /// ``` + pub(super) fn forget_allocation_drop_remaining(&mut self) { + let remaining = self.as_raw_mut_slice(); - /// Relinquishes the backing allocation, equivalent to - /// `ptr::write(&mut self, Vec::new().into_iter())` - pub(super) fn forget_allocation(&mut self) { + // overwrite the individual fields instead of creating a new + // struct and then overwriting &mut self. + // this creates less assembly self.cap = 0; self.buf = unsafe { NonNull::new_unchecked(RawVec::NEW.ptr()) }; self.ptr = self.buf.as_ptr(); self.end = self.buf.as_ptr(); + + unsafe { + ptr::drop_in_place(remaining); + } } } diff --git a/library/alloc/src/vec/mod.rs b/library/alloc/src/vec/mod.rs index 0028e290fac4e..e459442dfcfc5 100644 --- a/library/alloc/src/vec/mod.rs +++ b/library/alloc/src/vec/mod.rs @@ -58,7 +58,7 @@ use core::convert::TryFrom; use core::fmt; use core::hash::{Hash, Hasher}; use core::intrinsics::{arith_offset, assume}; -use core::iter::FromIterator; +use core::iter::{self, FromIterator}; use core::marker::PhantomData; use core::mem::{self, ManuallyDrop, MaybeUninit}; use core::ops::{self, Index, IndexMut, Range, RangeBounds}; @@ -126,7 +126,7 @@ use self::spec_extend::SpecExtend; mod spec_extend; -/// A contiguous growable array type, written `Vec` but pronounced 'vector'. +/// A contiguous growable array type, written as `Vec` and pronounced 'vector'. /// /// # Examples /// @@ -215,7 +215,7 @@ mod spec_extend; /// /// # Slicing /// -/// A `Vec` can be mutable. Slices, on the other hand, are read-only objects. +/// A `Vec` can be mutable. On the other hand, slices are read-only objects. /// To get a [slice][prim@slice], use [`&`]. Example: /// /// ``` @@ -267,12 +267,12 @@ mod spec_extend; /// unspecified, and you should use the appropriate methods to modify these. /// The pointer will never be null, so this type is null-pointer-optimized. /// -/// However, the pointer may not actually point to allocated memory. In particular, +/// However, the pointer might not actually point to allocated memory. In particular, /// if you construct a `Vec` with capacity 0 via [`Vec::new`], [`vec![]`][`vec!`], /// [`Vec::with_capacity(0)`][`Vec::with_capacity`], or by calling [`shrink_to_fit`] /// on an empty Vec, it will not allocate memory. Similarly, if you store zero-sized /// types inside a `Vec`, it will not allocate space for them. *Note that in this case -/// the `Vec` may not report a [`capacity`] of 0*. `Vec` will allocate if and only +/// the `Vec` might not report a [`capacity`] of 0*. `Vec` will allocate if and only /// if [`mem::size_of::`]`() * capacity() > 0`. In general, `Vec`'s allocation /// details are very subtle — if you intend to allocate memory using a `Vec` /// and use it for something else (either to pass to unsafe code, or to build your @@ -347,12 +347,12 @@ mod spec_extend; /// whatever is most efficient or otherwise easy to implement. Do not rely on /// removed data to be erased for security purposes. Even if you drop a `Vec`, its /// buffer may simply be reused by another `Vec`. Even if you zero a `Vec`'s memory -/// first, that may not actually happen because the optimizer does not consider +/// first, that might not actually happen because the optimizer does not consider /// this a side-effect that must be preserved. There is one case which we will /// not break, however: using `unsafe` code to write to the excess capacity, /// and then increasing the length to match, is always valid. /// -/// `Vec` does not currently guarantee the order in which elements are dropped. +/// Currently, `Vec` does not guarantee the order in which elements are dropped. /// The order has changed in the past and may change again. /// /// [`get`]: ../../std/vec/struct.Vec.html#method.get @@ -410,6 +410,10 @@ impl Vec { /// /// [Capacity and reallocation]: #capacity-and-reallocation /// + /// # Panics + /// + /// Panics if the new capacity exceeds `isize::MAX` bytes. + /// /// # Examples /// /// ``` @@ -541,6 +545,10 @@ impl Vec { /// /// [Capacity and reallocation]: #capacity-and-reallocation /// + /// # Panics + /// + /// Panics if the new capacity exceeds `isize::MAX` bytes. + /// /// # Examples /// /// ``` @@ -1512,15 +1520,98 @@ impl Vec { /// assert_eq!(vec, ["foo", "bar", "baz", "bar"]); /// ``` #[stable(feature = "dedup_by", since = "1.16.0")] - pub fn dedup_by(&mut self, same_bucket: F) + pub fn dedup_by(&mut self, mut same_bucket: F) where F: FnMut(&mut T, &mut T) -> bool, { - let len = { - let (dedup, _) = self.as_mut_slice().partition_dedup_by(same_bucket); - dedup.len() - }; - self.truncate(len); + let len = self.len(); + if len <= 1 { + return; + } + + /* INVARIANT: vec.len() > read >= write > write-1 >= 0 */ + struct FillGapOnDrop<'a, T, A: core::alloc::Allocator> { + /* Offset of the element we want to check if it is duplicate */ + read: usize, + + /* Offset of the place where we want to place the non-duplicate + * when we find it. */ + write: usize, + + /* The Vec that would need correction if `same_bucket` panicked */ + vec: &'a mut Vec, + } + + impl<'a, T, A: core::alloc::Allocator> Drop for FillGapOnDrop<'a, T, A> { + fn drop(&mut self) { + /* This code gets executed when `same_bucket` panics */ + + /* SAFETY: invariant guarantees that `read - write` + * and `len - read` never overflow and that the copy is always + * in-bounds. */ + unsafe { + let ptr = self.vec.as_mut_ptr(); + let len = self.vec.len(); + + /* How many items were left when `same_bucket` paniced. + * Basically vec[read..].len() */ + let items_left = len.wrapping_sub(self.read); + + /* Pointer to first item in vec[write..write+items_left] slice */ + let dropped_ptr = ptr.add(self.write); + /* Pointer to first item in vec[read..] slice */ + let valid_ptr = ptr.add(self.read); + + /* Copy `vec[read..]` to `vec[write..write+items_left]`. + * The slices can overlap, so `copy_nonoverlapping` cannot be used */ + ptr::copy(valid_ptr, dropped_ptr, items_left); + + /* How many items have been already dropped + * Basically vec[read..write].len() */ + let dropped = self.read.wrapping_sub(self.write); + + self.vec.set_len(len - dropped); + } + } + } + + let mut gap = FillGapOnDrop { read: 1, write: 1, vec: self }; + let ptr = gap.vec.as_mut_ptr(); + + /* Drop items while going through Vec, it should be more efficient than + * doing slice partition_dedup + truncate */ + + /* SAFETY: Because of the invariant, read_ptr, prev_ptr and write_ptr + * are always in-bounds and read_ptr never aliases prev_ptr */ + unsafe { + while gap.read < len { + let read_ptr = ptr.add(gap.read); + let prev_ptr = ptr.add(gap.write.wrapping_sub(1)); + + if same_bucket(&mut *read_ptr, &mut *prev_ptr) { + /* We have found duplicate, drop it in-place */ + ptr::drop_in_place(read_ptr); + } else { + let write_ptr = ptr.add(gap.write); + + /* Because `read_ptr` can be equal to `write_ptr`, we either + * have to use `copy` or conditional `copy_nonoverlapping`. + * Looks like the first option is faster. */ + ptr::copy(read_ptr, write_ptr, 1); + + /* We have filled that place, so go further */ + gap.write += 1; + } + + gap.read += 1; + } + + /* Technically we could let `gap` clean up with its Drop, but + * when `same_bucket` is guaranteed to not panic, this bloats a little + * the codegen, so we just do it manually */ + gap.vec.set_len(gap.write); + mem::forget(gap); + } } /// Appends an element to the back of a collection. @@ -1942,6 +2033,18 @@ impl Vec { #[unstable(feature = "vec_split_at_spare", issue = "81944")] #[inline] pub fn split_at_spare_mut(&mut self) -> (&mut [T], &mut [MaybeUninit]) { + // SAFETY: + // - len is ignored and so never changed + let (init, spare, _) = unsafe { self.split_at_spare_mut_with_len() }; + (init, spare) + } + + /// Safety: changing returned .2 (&mut usize) is considered the same as calling `.set_len(_)`. + /// + /// This method provides unique access to all vec parts at once in `extend_from_within`. + unsafe fn split_at_spare_mut_with_len( + &mut self, + ) -> (&mut [T], &mut [MaybeUninit], &mut usize) { let Range { start: ptr, end: spare_ptr } = self.as_mut_ptr_range(); let spare_ptr = spare_ptr.cast::>(); let spare_len = self.buf.capacity() - self.len; @@ -1953,7 +2056,7 @@ impl Vec { let initialized = slice::from_raw_parts_mut(ptr, self.len); let spare = slice::from_raw_parts_mut(spare_ptr, spare_len); - (initialized, spare) + (initialized, spare, &mut self.len) } } } @@ -2165,22 +2268,20 @@ trait ExtendFromWithinSpec { impl ExtendFromWithinSpec for Vec { default unsafe fn spec_extend_from_within(&mut self, src: Range) { - let initialized = { - let (this, spare) = self.split_at_spare_mut(); - - // SAFETY: - // - caller guaratees that src is a valid index - let to_clone = unsafe { this.get_unchecked(src) }; - - to_clone.iter().cloned().zip(spare.iter_mut()).map(|(e, s)| s.write(e)).count() - }; + // SAFETY: + // - len is increased only after initializing elements + let (this, spare, len) = unsafe { self.split_at_spare_mut_with_len() }; // SAFETY: - // - elements were just initialized - unsafe { - let new_len = self.len() + initialized; - self.set_len(new_len); - } + // - caller guaratees that src is a valid index + let to_clone = unsafe { this.get_unchecked(src) }; + + iter::zip(to_clone, spare) + .map(|(src, dst)| dst.write(src.clone())) + // Note: + // - Element was just initialized with `MaybeUninit::write`, so it's ok to increase len + // - len is increased after each element to prevent leaks (see issue #82533) + .for_each(|_| *len += 1); } } @@ -2466,7 +2567,7 @@ impl Vec { /// # let some_predicate = |x: &mut i32| { *x == 2 || *x == 3 || *x == 6 }; /// # let mut vec = vec![1, 2, 3, 4, 5, 6]; /// let mut i = 0; - /// while i != vec.len() { + /// while i < vec.len() { /// if some_predicate(&mut vec[i]) { /// let val = vec.remove(i); /// // your code here @@ -2616,6 +2717,13 @@ impl AsMut<[T]> for Vec { #[stable(feature = "rust1", since = "1.0.0")] impl From<&[T]> for Vec { + /// Allocate a `Vec` and fill it by cloning `s`'s items. + /// + /// # Examples + /// + /// ``` + /// assert_eq!(Vec::from(&[1, 2, 3][..]), vec![1, 2, 3]); + /// ``` #[cfg(not(test))] fn from(s: &[T]) -> Vec { s.to_vec() @@ -2628,6 +2736,13 @@ impl From<&[T]> for Vec { #[stable(feature = "vec_from_mut", since = "1.19.0")] impl From<&mut [T]> for Vec { + /// Allocate a `Vec` and fill it by cloning `s`'s items. + /// + /// # Examples + /// + /// ``` + /// assert_eq!(Vec::from(&mut [1, 2, 3][..]), vec![1, 2, 3]); + /// ``` #[cfg(not(test))] fn from(s: &mut [T]) -> Vec { s.to_vec() @@ -2644,6 +2759,13 @@ impl From<[T; N]> for Vec { fn from(s: [T; N]) -> Vec { <[T]>::into_vec(box s) } + /// Allocate a `Vec` and move `s`'s items into it. + /// + /// # Examples + /// + /// ``` + /// assert_eq!(Vec::from([1, 2, 3]), vec![1, 2, 3]); + /// ``` #[cfg(test)] fn from(s: [T; N]) -> Vec { crate::slice::into_vec(box s) @@ -2655,6 +2777,20 @@ impl<'a, T> From> for Vec where [T]: ToOwned>, { + /// Convert a clone-on-write slice into a vector. + /// + /// If `s` already owns a `Vec`, it will be returned directly. + /// If `s` is borrowing a slice, a new `Vec` will be allocated and + /// filled by cloning `s`'s items into it. + /// + /// # Examples + /// + /// ``` + /// # use std::borrow::Cow; + /// let o: Cow<[i32]> = Cow::Owned(vec![1, 2, 3]); + /// let b: Cow<[i32]> = Cow::Borrowed(&[1, 2, 3]); + /// assert_eq!(Vec::from(o), Vec::from(b)); + /// ``` fn from(s: Cow<'a, [T]>) -> Vec { s.into_owned() } @@ -2664,9 +2800,17 @@ where #[cfg(not(test))] #[stable(feature = "vec_from_box", since = "1.18.0")] impl From> for Vec { + /// Convert a boxed slice into a vector by transferring ownership of + /// the existing heap allocation. + /// + /// # Examples + /// + /// ``` + /// let b: Box<[i32]> = vec![1, 2, 3].into_boxed_slice(); + /// assert_eq!(Vec::from(b), vec![1, 2, 3]); + /// ``` fn from(s: Box<[T], A>) -> Self { - let len = s.len(); - Self { buf: RawVec::from_box(s), len } + s.into_vec() } } @@ -2674,6 +2818,16 @@ impl From> for Vec { #[cfg(not(test))] #[stable(feature = "box_from_vec", since = "1.20.0")] impl From> for Box<[T], A> { + /// Convert a vector into a boxed slice. + /// + /// If `v` has excess capacity, its items will be moved into a + /// newly-allocated buffer with exactly the right capacity. + /// + /// # Examples + /// + /// ``` + /// assert_eq!(Box::from(vec![1, 2, 3]), vec![1, 2, 3].into_boxed_slice()); + /// ``` fn from(v: Vec) -> Self { v.into_boxed_slice() } @@ -2681,6 +2835,13 @@ impl From> for Box<[T], A> { #[stable(feature = "rust1", since = "1.0.0")] impl From<&str> for Vec { + /// Allocate a `Vec` and fill it with a UTF-8 string. + /// + /// # Examples + /// + /// ``` + /// assert_eq!(Vec::from("123"), vec![b'1', b'2', b'3']); + /// ``` fn from(s: &str) -> Vec { From::from(s.as_bytes()) } diff --git a/library/alloc/src/vec/source_iter_marker.rs b/library/alloc/src/vec/source_iter_marker.rs index 8c0e95559fa15..e857d284d3ab6 100644 --- a/library/alloc/src/vec/source_iter_marker.rs +++ b/library/alloc/src/vec/source_iter_marker.rs @@ -1,4 +1,4 @@ -use core::iter::{InPlaceIterable, SourceIter}; +use core::iter::{InPlaceIterable, SourceIter, TrustedRandomAccess}; use core::mem::{self, ManuallyDrop}; use core::ptr::{self}; @@ -52,16 +52,7 @@ where ) }; - // use try-fold since - // - it vectorizes better for some iterator adapters - // - unlike most internal iteration methods, it only takes a &mut self - // - it lets us thread the write pointer through its innards and get it back in the end - let sink = InPlaceDrop { inner: dst_buf, dst: dst_buf }; - let sink = iterator - .try_fold::<_, _, Result<_, !>>(sink, write_in_place_with_drop(dst_end)) - .unwrap(); - // iteration succeeded, don't drop head - let dst = ManuallyDrop::new(sink).dst; + let len = SpecInPlaceCollect::collect_in_place(&mut iterator, dst_buf, dst_end); let src = unsafe { iterator.as_inner().as_into_iter() }; // check if SourceIter contract was upheld @@ -72,20 +63,17 @@ where // then the source pointer will stay in its initial position and we can't use it as reference if src.ptr != src_ptr { debug_assert!( - dst as *const _ <= src.ptr, + unsafe { dst_buf.add(len) as *const _ } <= src.ptr, "InPlaceIterable contract violation, write pointer advanced beyond read pointer" ); } // drop any remaining values at the tail of the source - src.drop_remaining(); // but prevent drop of the allocation itself once IntoIter goes out of scope - src.forget_allocation(); + // if the drop panics then we also leak any elements collected into dst_buf + src.forget_allocation_drop_remaining(); - let vec = unsafe { - let len = dst.offset_from(dst_buf) as usize; - Vec::from_raw_parts(dst_buf, len, cap) - }; + let vec = unsafe { Vec::from_raw_parts(dst_buf, len, cap) }; vec } @@ -106,3 +94,52 @@ fn write_in_place_with_drop( Ok(sink) } } + +/// Helper trait to hold specialized implementations of the in-place iterate-collect loop +trait SpecInPlaceCollect: Iterator { + /// Collects an iterator (`self`) into the destination buffer (`dst`) and returns the number of items + /// collected. `end` is the last writable element of the allocation and used for bounds checks. + fn collect_in_place(&mut self, dst: *mut T, end: *const T) -> usize; +} + +impl SpecInPlaceCollect for I +where + I: Iterator, +{ + #[inline] + default fn collect_in_place(&mut self, dst_buf: *mut T, end: *const T) -> usize { + // use try-fold since + // - it vectorizes better for some iterator adapters + // - unlike most internal iteration methods, it only takes a &mut self + // - it lets us thread the write pointer through its innards and get it back in the end + let sink = InPlaceDrop { inner: dst_buf, dst: dst_buf }; + let sink = + self.try_fold::<_, _, Result<_, !>>(sink, write_in_place_with_drop(end)).unwrap(); + // iteration succeeded, don't drop head + unsafe { ManuallyDrop::new(sink).dst.offset_from(dst_buf) as usize } + } +} + +impl SpecInPlaceCollect for I +where + I: Iterator + TrustedRandomAccess, +{ + #[inline] + fn collect_in_place(&mut self, dst_buf: *mut T, end: *const T) -> usize { + let len = self.size(); + let mut drop_guard = InPlaceDrop { inner: dst_buf, dst: dst_buf }; + for i in 0..len { + // Safety: InplaceIterable contract guarantees that for every element we read + // one slot in the underlying storage will have been freed up and we can immediately + // write back the result. + unsafe { + let dst = dst_buf.offset(i as isize); + debug_assert!(dst as *const _ <= end, "InPlaceIterable contract violation"); + ptr::write(dst, self.__iterator_get_unchecked(i)); + drop_guard.dst = dst.add(1); + } + } + mem::forget(drop_guard); + len + } +} diff --git a/library/alloc/src/vec/spec_extend.rs b/library/alloc/src/vec/spec_extend.rs index b6186a7ebaf73..c6f4f22a01f6e 100644 --- a/library/alloc/src/vec/spec_extend.rs +++ b/library/alloc/src/vec/spec_extend.rs @@ -26,15 +26,13 @@ where default fn spec_extend(&mut self, iterator: I) { // This is the case for a TrustedLen iterator. let (low, high) = iterator.size_hint(); - if let Some(high_value) = high { + if let Some(additional) = high { debug_assert_eq!( low, - high_value, + additional, "TrustedLen iterator's size hint is not exact: {:?}", (low, high) ); - } - if let Some(additional) = high { self.reserve(additional); unsafe { let mut ptr = self.as_mut_ptr().add(self.len()); @@ -47,7 +45,12 @@ where }); } } else { - self.extend_desugared(iterator) + // Per TrustedLen contract a `None` upper bound means that the iterator length + // truly exceeds usize::MAX, which would eventually lead to a capacity overflow anyway. + // Since the other branch already panics eagerly (via `reserve()`) we do the same here. + // This avoids additional codegen for a fallback code path which would eventually + // panic anyway. + panic!("capacity overflow"); } } } diff --git a/library/alloc/src/vec/spec_from_iter_nested.rs b/library/alloc/src/vec/spec_from_iter_nested.rs index ec390c62165a5..948cf044197c2 100644 --- a/library/alloc/src/vec/spec_from_iter_nested.rs +++ b/library/alloc/src/vec/spec_from_iter_nested.rs @@ -46,10 +46,13 @@ where fn from_iter(iterator: I) -> Self { let mut vector = match iterator.size_hint() { (_, Some(upper)) => Vec::with_capacity(upper), - _ => Vec::new(), + // TrustedLen contract guarantees that `size_hint() == (_, None)` means that there + // are more than `usize::MAX` elements. + // Since the previous branch would eagerly panic if the capacity is too large + // (via `with_capacity`) we do the same here. + _ => panic!("capacity overflow"), }; - // must delegate to spec_extend() since extend() itself delegates - // to spec_from for empty Vecs + // reuse extend specialization for TrustedLen vector.spec_extend(iterator); vector } diff --git a/library/alloc/tests/binary_heap.rs b/library/alloc/tests/binary_heap.rs index ce794a9a4afa2..a7913dcd28740 100644 --- a/library/alloc/tests/binary_heap.rs +++ b/library/alloc/tests/binary_heap.rs @@ -386,10 +386,23 @@ fn assert_covariance() { #[test] fn test_retain() { - let mut a = BinaryHeap::from(vec![-10, -5, 1, 2, 4, 13]); - a.retain(|x| x % 2 == 0); + let mut a = BinaryHeap::from(vec![100, 10, 50, 1, 2, 20, 30]); + a.retain(|&x| x != 2); - assert_eq!(a.into_sorted_vec(), [-10, 2, 4]) + // Check that 20 moved into 10's place. + assert_eq!(a.clone().into_vec(), [100, 20, 50, 1, 10, 30]); + + a.retain(|_| true); + + assert_eq!(a.clone().into_vec(), [100, 20, 50, 1, 10, 30]); + + a.retain(|&x| x < 50); + + assert_eq!(a.clone().into_vec(), [30, 20, 10, 1]); + + a.retain(|_| false); + + assert!(a.is_empty()); } // old binaryheap failed this test diff --git a/library/alloc/tests/fmt.rs b/library/alloc/tests/fmt.rs index 757fddd241857..27ab6c07e4309 100644 --- a/library/alloc/tests/fmt.rs +++ b/library/alloc/tests/fmt.rs @@ -68,10 +68,7 @@ fn test_format_macro_interface() { t!(format!("{:?}", 10_usize), "10"); t!(format!("{:?}", "true"), "\"true\""); t!(format!("{:?}", "foo\nbar"), "\"foo\\nbar\""); - t!( - format!("{:?}", "foo\n\"bar\"\r\n\'baz\'\t\\qux\\"), - r#""foo\n\"bar\"\r\n\'baz\'\t\\qux\\""# - ); + t!(format!("{:?}", "foo\n\"bar\"\r\n\'baz\'\t\\qux\\"), r#""foo\n\"bar\"\r\n'baz'\t\\qux\\""#); t!(format!("{:?}", "foo\0bar\x01baz\u{7f}q\u{75}x"), r#""foo\u{0}bar\u{1}baz\u{7f}qux""#); t!(format!("{:o}", 10_usize), "12"); t!(format!("{:x}", 10_usize), "a"); @@ -154,8 +151,7 @@ fn test_format_macro_interface() { t!(format!("{:+10.3e}", -1.2345e6f64), " -1.234e6"); // Float edge cases - t!(format!("{}", -0.0), "0"); - t!(format!("{:?}", -0.0), "-0.0"); + t!(format!("{}", -0.0), "-0"); t!(format!("{:?}", 0.0), "0.0"); // sign aware zero padding diff --git a/library/alloc/tests/lib.rs b/library/alloc/tests/lib.rs index 799499b9b771f..7e1194cc4aa20 100644 --- a/library/alloc/tests/lib.rs +++ b/library/alloc/tests/lib.rs @@ -14,13 +14,15 @@ #![feature(binary_heap_drain_sorted)] #![feature(slice_ptr_get)] #![feature(binary_heap_retain)] +#![feature(binary_heap_as_slice)] #![feature(inplace_iteration)] #![feature(iter_map_while)] -#![feature(int_bits_const)] #![feature(vecdeque_binary_search)] #![feature(slice_group_by)] +#![feature(slice_partition_dedup)] #![feature(vec_extend_from_within)] #![feature(vec_spare_capacity)] +#![feature(string_remove_matches)] use std::collections::hash_map::DefaultHasher; use std::hash::{Hash, Hasher}; diff --git a/library/alloc/tests/str.rs b/library/alloc/tests/str.rs index 604835e6cc4a6..6df8d8c2f354f 100644 --- a/library/alloc/tests/str.rs +++ b/library/alloc/tests/str.rs @@ -160,6 +160,36 @@ fn test_join_for_different_lengths_with_long_separator() { test_join!("~~~~~a~~~~~bc", ["", "a", "bc"], "~~~~~"); } +#[test] +fn test_join_isue_80335() { + use core::{borrow::Borrow, cell::Cell}; + + struct WeirdBorrow { + state: Cell, + } + + impl Default for WeirdBorrow { + fn default() -> Self { + WeirdBorrow { state: Cell::new(false) } + } + } + + impl Borrow for WeirdBorrow { + fn borrow(&self) -> &str { + let state = self.state.get(); + if state { + "0" + } else { + self.state.set(true); + "123456" + } + } + } + + let arr: [WeirdBorrow; 3] = Default::default(); + test_join!("0-0-0", arr, "-"); +} + #[test] #[cfg_attr(miri, ignore)] // Miri is too slow fn test_unsafe_slice() { diff --git a/library/alloc/tests/string.rs b/library/alloc/tests/string.rs index f3d74e0514d29..9ec0ee97ab926 100644 --- a/library/alloc/tests/string.rs +++ b/library/alloc/tests/string.rs @@ -365,6 +365,33 @@ fn remove_bad() { "ศ".to_string().remove(1); } +#[test] +fn test_remove_matches() { + let mut s = "abc".to_string(); + + s.remove_matches('b'); + assert_eq!(s, "ac"); + s.remove_matches('b'); + assert_eq!(s, "ac"); + + let mut s = "abcb".to_string(); + + s.remove_matches('b'); + assert_eq!(s, "ac"); + + let mut s = "ศไทย中华Việt Nam; foobarศ".to_string(); + s.remove_matches('ศ'); + assert_eq!(s, "ไทย中华Việt Nam; foobar"); + + let mut s = "".to_string(); + s.remove_matches(""); + assert_eq!(s, ""); + + let mut s = "aaaaa".to_string(); + s.remove_matches('a'); + assert_eq!(s, ""); +} + #[test] fn test_retain() { let mut s = String::from("α_β_γ"); diff --git a/library/alloc/tests/vec.rs b/library/alloc/tests/vec.rs index fab450285854d..4dcc5d30debf7 100644 --- a/library/alloc/tests/vec.rs +++ b/library/alloc/tests/vec.rs @@ -7,6 +7,7 @@ use std::mem::{size_of, swap}; use std::ops::Bound::*; use std::panic::{catch_unwind, AssertUnwindSafe}; use std::rc::Rc; +use std::sync::atomic::{AtomicU32, Ordering}; use std::vec::{Drain, IntoIter}; struct DropCounter<'a> { @@ -1003,9 +1004,9 @@ fn test_from_iter_specialization_with_iterator_adapters() { .map_while(Option::Some) .peekable() .skip(1) - .map(|e| std::num::NonZeroUsize::new(e)); + .map(|e| if e != usize::MAX { Ok(std::num::NonZeroUsize::new(e)) } else { Err(()) }); assert_in_place_trait(&iter); - let sink = iter.collect::>(); + let sink = iter.collect::, _>>().unwrap(); let sinkptr = sink.as_ptr(); assert_eq!(srcptr, sinkptr as *const usize); } @@ -1026,7 +1027,7 @@ fn test_from_iter_specialization_head_tail_drop() { } #[test] -fn test_from_iter_specialization_panic_drop() { +fn test_from_iter_specialization_panic_during_iteration_drops() { let drop_count: Vec<_> = (0..=2).map(|_| Rc::new(())).collect(); let src: Vec<_> = drop_count.iter().cloned().collect(); let iter = src.into_iter(); @@ -1049,6 +1050,51 @@ fn test_from_iter_specialization_panic_drop() { ); } +#[test] +fn test_from_iter_specialization_panic_during_drop_leaks() { + static mut DROP_COUNTER: usize = 0; + + #[derive(Debug)] + enum Droppable { + DroppedTwice(Box), + PanicOnDrop, + } + + impl Drop for Droppable { + fn drop(&mut self) { + match self { + Droppable::DroppedTwice(_) => { + unsafe { + DROP_COUNTER += 1; + } + println!("Dropping!") + } + Droppable::PanicOnDrop => { + if !std::thread::panicking() { + panic!(); + } + } + } + } + } + + let mut to_free: *mut Droppable = core::ptr::null_mut(); + let mut cap = 0; + + let _ = std::panic::catch_unwind(AssertUnwindSafe(|| { + let mut v = vec![Droppable::DroppedTwice(Box::new(123)), Droppable::PanicOnDrop]; + to_free = v.as_mut_ptr(); + cap = v.capacity(); + let _ = v.into_iter().take(0).collect::>(); + })); + + assert_eq!(unsafe { DROP_COUNTER }, 1); + // clean up the leak to keep miri happy + unsafe { + drop(Vec::from_raw_parts(to_free, 0, cap)); + } +} + #[test] fn test_cow_from() { let borrowed: &[_] = &["borrowed", "(slice)"]; @@ -2100,3 +2146,170 @@ fn test_extend_from_within() { assert_eq!(v, ["a", "b", "c", "b", "c", "a", "b"]); } + +#[test] +fn test_vec_dedup_by() { + let mut vec: Vec = vec![1, -1, 2, 3, 1, -5, 5, -2, 2]; + + vec.dedup_by(|a, b| a.abs() == b.abs()); + + assert_eq!(vec, [1, 2, 3, 1, -5, -2]); +} + +#[test] +fn test_vec_dedup_empty() { + let mut vec: Vec = Vec::new(); + + vec.dedup(); + + assert_eq!(vec, []); +} + +#[test] +fn test_vec_dedup_one() { + let mut vec = vec![12i32]; + + vec.dedup(); + + assert_eq!(vec, [12]); +} + +#[test] +fn test_vec_dedup_multiple_ident() { + let mut vec = vec![12, 12, 12, 12, 12, 11, 11, 11, 11, 11, 11]; + + vec.dedup(); + + assert_eq!(vec, [12, 11]); +} + +#[test] +fn test_vec_dedup_partialeq() { + #[derive(Debug)] + struct Foo(i32, i32); + + impl PartialEq for Foo { + fn eq(&self, other: &Foo) -> bool { + self.0 == other.0 + } + } + + let mut vec = vec![Foo(0, 1), Foo(0, 5), Foo(1, 7), Foo(1, 9)]; + + vec.dedup(); + assert_eq!(vec, [Foo(0, 1), Foo(1, 7)]); +} + +#[test] +fn test_vec_dedup() { + let mut vec: Vec = Vec::with_capacity(8); + let mut template = vec.clone(); + + for x in 0u8..255u8 { + vec.clear(); + template.clear(); + + let iter = (0..8).map(move |bit| (x >> bit) & 1 == 1); + vec.extend(iter); + template.extend_from_slice(&vec); + + let (dedup, _) = template.partition_dedup(); + vec.dedup(); + + assert_eq!(vec, dedup); + } +} + +#[test] +fn test_vec_dedup_panicking() { + #[derive(Debug)] + struct Panic { + drop_counter: &'static AtomicU32, + value: bool, + index: usize, + } + + impl PartialEq for Panic { + fn eq(&self, other: &Self) -> bool { + self.value == other.value + } + } + + impl Drop for Panic { + fn drop(&mut self) { + let x = self.drop_counter.fetch_add(1, Ordering::SeqCst); + assert!(x != 4); + } + } + + static DROP_COUNTER: AtomicU32 = AtomicU32::new(0); + let expected = [ + Panic { drop_counter: &DROP_COUNTER, value: false, index: 0 }, + Panic { drop_counter: &DROP_COUNTER, value: false, index: 5 }, + Panic { drop_counter: &DROP_COUNTER, value: true, index: 6 }, + Panic { drop_counter: &DROP_COUNTER, value: true, index: 7 }, + ]; + let mut vec = vec![ + Panic { drop_counter: &DROP_COUNTER, value: false, index: 0 }, + // these elements get deduplicated + Panic { drop_counter: &DROP_COUNTER, value: false, index: 1 }, + Panic { drop_counter: &DROP_COUNTER, value: false, index: 2 }, + Panic { drop_counter: &DROP_COUNTER, value: false, index: 3 }, + Panic { drop_counter: &DROP_COUNTER, value: false, index: 4 }, + // here it panics + Panic { drop_counter: &DROP_COUNTER, value: false, index: 5 }, + Panic { drop_counter: &DROP_COUNTER, value: true, index: 6 }, + Panic { drop_counter: &DROP_COUNTER, value: true, index: 7 }, + ]; + + let _ = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| { + vec.dedup(); + })); + + let ok = vec.iter().zip(expected.iter()).all(|(x, y)| x.index == y.index); + + if !ok { + panic!("expected: {:?}\ngot: {:?}\n", expected, vec); + } +} + +// Regression test for issue #82533 +#[test] +fn test_extend_from_within_panicing_clone() { + struct Panic<'dc> { + drop_count: &'dc AtomicU32, + aaaaa: bool, + } + + impl Clone for Panic<'_> { + fn clone(&self) -> Self { + if self.aaaaa { + panic!("panic! at the clone"); + } + + Self { ..*self } + } + } + + impl Drop for Panic<'_> { + fn drop(&mut self) { + self.drop_count.fetch_add(1, Ordering::SeqCst); + } + } + + let count = core::sync::atomic::AtomicU32::new(0); + let mut vec = vec![ + Panic { drop_count: &count, aaaaa: false }, + Panic { drop_count: &count, aaaaa: true }, + Panic { drop_count: &count, aaaaa: false }, + ]; + + // This should clone&append one Panic{..} at the end, and then panic while + // cloning second Panic{..}. This means that `Panic::drop` should be called + // 4 times (3 for items already in vector, 1 for just appended). + // + // Previously just appended item was leaked, making drop_count = 3, instead of 4. + std::panic::catch_unwind(move || vec.extend_from_within(..)).unwrap_err(); + + assert_eq!(count.load(Ordering::SeqCst), 4); +} diff --git a/library/alloc/tests/vec_deque.rs b/library/alloc/tests/vec_deque.rs index 0919b1325bceb..d7140cf97593c 100644 --- a/library/alloc/tests/vec_deque.rs +++ b/library/alloc/tests/vec_deque.rs @@ -1699,6 +1699,24 @@ fn test_binary_search_by_key() { assert_eq!(deque.binary_search_by_key(&4, |&(v,)| v), Err(3)); } +#[test] +fn test_partition_point() { + // Contiguous (front only) search: + let deque: VecDeque<_> = vec![1, 2, 3, 5, 6].into(); + assert!(deque.as_slices().1.is_empty()); + assert_eq!(deque.partition_point(|&v| v <= 3), 3); + + // Split search (both front & back non-empty): + let mut deque: VecDeque<_> = vec![5, 6].into(); + deque.push_front(3); + deque.push_front(2); + deque.push_front(1); + deque.push_back(10); + assert!(!deque.as_slices().0.is_empty()); + assert!(!deque.as_slices().1.is_empty()); + assert_eq!(deque.partition_point(|&v| v <= 5), 4); +} + #[test] fn test_zero_sized_push() { const N: usize = 8; diff --git a/library/backtrace b/library/backtrace index af078ecc0b069..710fc18ddcb6c 160000 --- a/library/backtrace +++ b/library/backtrace @@ -1 +1 @@ -Subproject commit af078ecc0b069ec594982f92d4c6c58af99efbb5 +Subproject commit 710fc18ddcb6c7677b3c96359abb35da37f2a488 diff --git a/library/core/Cargo.toml b/library/core/Cargo.toml index c1596012eac24..b6aaf078cf00d 100644 --- a/library/core/Cargo.toml +++ b/library/core/Cargo.toml @@ -2,6 +2,9 @@ authors = ["The Rust Project Developers"] name = "core" version = "0.0.0" +license = "MIT OR Apache-2.0" +repository = "https://github.com/rust-lang/rust.git" +description = "The Rust Core Library" autotests = false autobenches = false edition = "2018" @@ -25,3 +28,6 @@ rand = "0.7" [features] # Make panics and failed asserts immediately abort without formatting any message panic_immediate_abort = [] +# Make `RefCell` store additional debugging information, which is printed out when +# a borrow error occurs +debug_refcell = [] diff --git a/library/core/benches/char/methods.rs b/library/core/benches/char/methods.rs index de4b63030fa7c..9408f83c32f7c 100644 --- a/library/core/benches/char/methods.rs +++ b/library/core/benches/char/methods.rs @@ -45,3 +45,33 @@ fn bench_to_ascii_uppercase(b: &mut Bencher) { fn bench_to_ascii_lowercase(b: &mut Bencher) { b.iter(|| CHARS.iter().cycle().take(10_000).map(|c| c.to_ascii_lowercase()).min()) } + +#[bench] +fn bench_ascii_mix_to_uppercase(b: &mut Bencher) { + b.iter(|| (0..=255).cycle().take(10_000).map(|b| char::from(b).to_uppercase()).count()) +} + +#[bench] +fn bench_ascii_mix_to_lowercase(b: &mut Bencher) { + b.iter(|| (0..=255).cycle().take(10_000).map(|b| char::from(b).to_lowercase()).count()) +} + +#[bench] +fn bench_ascii_char_to_uppercase(b: &mut Bencher) { + b.iter(|| (0..=127).cycle().take(10_000).map(|b| char::from(b).to_uppercase()).count()) +} + +#[bench] +fn bench_ascii_char_to_lowercase(b: &mut Bencher) { + b.iter(|| (0..=127).cycle().take(10_000).map(|b| char::from(b).to_lowercase()).count()) +} + +#[bench] +fn bench_non_ascii_char_to_uppercase(b: &mut Bencher) { + b.iter(|| (128..=255).cycle().take(10_000).map(|b| char::from(b).to_uppercase()).count()) +} + +#[bench] +fn bench_non_ascii_char_to_lowercase(b: &mut Bencher) { + b.iter(|| (128..=255).cycle().take(10_000).map(|b| char::from(b).to_lowercase()).count()) +} diff --git a/library/core/benches/slice.rs b/library/core/benches/slice.rs index 06b37cb08448c..04efa52078778 100644 --- a/library/core/benches/slice.rs +++ b/library/core/benches/slice.rs @@ -7,15 +7,21 @@ enum Cache { L3, } +impl Cache { + fn size(&self) -> usize { + match self { + Cache::L1 => 1000, // 8kb + Cache::L2 => 10_000, // 80kb + Cache::L3 => 1_000_000, // 8Mb + } + } +} + fn binary_search(b: &mut Bencher, cache: Cache, mapper: F) where F: Fn(usize) -> usize, { - let size = match cache { - Cache::L1 => 1000, // 8kb - Cache::L2 => 10_000, // 80kb - Cache::L3 => 1_000_000, // 8Mb - }; + let size = cache.size(); let v = (0..size).map(&mapper).collect::>(); let mut r = 0usize; b.iter(move || { @@ -24,7 +30,18 @@ where // Lookup the whole range to get 50% hits and 50% misses. let i = mapper(r % size); black_box(v.binary_search(&i).is_ok()); - }) + }); +} + +fn binary_search_worst_case(b: &mut Bencher, cache: Cache) { + let size = cache.size(); + + let mut v = vec![0; size]; + let i = 1; + v[size - 1] = i; + b.iter(move || { + black_box(v.binary_search(&i).is_ok()); + }); } #[bench] @@ -57,6 +74,21 @@ fn binary_search_l3_with_dups(b: &mut Bencher) { binary_search(b, Cache::L3, |i| i / 16 * 16); } +#[bench] +fn binary_search_l1_worst_case(b: &mut Bencher) { + binary_search_worst_case(b, Cache::L1); +} + +#[bench] +fn binary_search_l2_worst_case(b: &mut Bencher) { + binary_search_worst_case(b, Cache::L2); +} + +#[bench] +fn binary_search_l3_worst_case(b: &mut Bencher) { + binary_search_worst_case(b, Cache::L3); +} + macro_rules! rotate { ($fn:ident, $n:expr, $mapper:expr) => { #[bench] @@ -82,3 +114,16 @@ rotate!(rotate_16_usize_4, 16, |i| [i; 4]); rotate!(rotate_16_usize_5, 16, |i| [i; 5]); rotate!(rotate_64_usize_4, 64, |i| [i; 4]); rotate!(rotate_64_usize_5, 64, |i| [i; 5]); + +#[bench] +fn fill_byte_sized(b: &mut Bencher) { + #[derive(Copy, Clone)] + struct NewType(u8); + + let mut ary = [NewType(0); 1024]; + + b.iter(|| { + let slice = &mut ary[..]; + black_box(slice.fill(black_box(NewType(42)))); + }); +} diff --git a/library/core/src/alloc/layout.rs b/library/core/src/alloc/layout.rs index 8b95b70396bbd..0e7667dd89e64 100644 --- a/library/core/src/alloc/layout.rs +++ b/library/core/src/alloc/layout.rs @@ -93,7 +93,7 @@ impl Layout { /// This function is unsafe as it does not verify the preconditions from /// [`Layout::from_size_align`]. #[stable(feature = "alloc_layout", since = "1.28.0")] - #[rustc_const_stable(feature = "alloc_layout", since = "1.28.0")] + #[rustc_const_stable(feature = "alloc_layout", since = "1.36.0")] #[inline] pub const unsafe fn from_size_align_unchecked(size: usize, align: usize) -> Self { // SAFETY: the caller must ensure that `align` is greater than zero. diff --git a/library/core/src/any.rs b/library/core/src/any.rs index eef8f2046d33e..5e1725cfc7a63 100644 --- a/library/core/src/any.rs +++ b/library/core/src/any.rs @@ -141,7 +141,7 @@ impl Any for T { #[stable(feature = "rust1", since = "1.0.0")] impl fmt::Debug for dyn Any { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.pad("Any") + f.debug_struct("Any").finish_non_exhaustive() } } @@ -151,14 +151,14 @@ impl fmt::Debug for dyn Any { #[stable(feature = "rust1", since = "1.0.0")] impl fmt::Debug for dyn Any + Send { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.pad("Any") + f.debug_struct("Any").finish_non_exhaustive() } } #[stable(feature = "any_send_sync_methods", since = "1.28.0")] impl fmt::Debug for dyn Any + Send + Sync { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.pad("Any") + f.debug_struct("Any").finish_non_exhaustive() } } @@ -285,7 +285,7 @@ impl dyn Any + Send { #[stable(feature = "rust1", since = "1.0.0")] #[inline] pub fn is(&self) -> bool { - Any::is::(self) + ::is::(self) } /// Forwards to the method defined on the type `Any`. @@ -309,7 +309,7 @@ impl dyn Any + Send { #[stable(feature = "rust1", since = "1.0.0")] #[inline] pub fn downcast_ref(&self) -> Option<&T> { - Any::downcast_ref::(self) + ::downcast_ref::(self) } /// Forwards to the method defined on the type `Any`. @@ -337,7 +337,7 @@ impl dyn Any + Send { #[stable(feature = "rust1", since = "1.0.0")] #[inline] pub fn downcast_mut(&mut self) -> Option<&mut T> { - Any::downcast_mut::(self) + ::downcast_mut::(self) } } @@ -363,7 +363,7 @@ impl dyn Any + Send + Sync { #[stable(feature = "any_send_sync_methods", since = "1.28.0")] #[inline] pub fn is(&self) -> bool { - Any::is::(self) + ::is::(self) } /// Forwards to the method defined on the type `Any`. @@ -387,7 +387,7 @@ impl dyn Any + Send + Sync { #[stable(feature = "any_send_sync_methods", since = "1.28.0")] #[inline] pub fn downcast_ref(&self) -> Option<&T> { - Any::downcast_ref::(self) + ::downcast_ref::(self) } /// Forwards to the method defined on the type `Any`. @@ -415,7 +415,7 @@ impl dyn Any + Send + Sync { #[stable(feature = "any_send_sync_methods", since = "1.28.0")] #[inline] pub fn downcast_mut(&mut self) -> Option<&mut T> { - Any::downcast_mut::(self) + ::downcast_mut::(self) } } diff --git a/library/core/src/array/iter.rs b/library/core/src/array/iter.rs index 4472fba26b99b..c36542f631488 100644 --- a/library/core/src/array/iter.rs +++ b/library/core/src/array/iter.rs @@ -2,7 +2,7 @@ use crate::{ fmt, - iter::{ExactSizeIterator, FusedIterator, TrustedLen}, + iter::{self, ExactSizeIterator, FusedIterator, TrustedLen, TrustedRandomAccess}, mem::{self, MaybeUninit}, ops::Range, ptr, @@ -130,6 +130,18 @@ impl Iterator for IntoIter { fn last(mut self) -> Option { self.next_back() } + + #[inline] + unsafe fn __iterator_get_unchecked(&mut self, idx: usize) -> Self::Item + where + Self: TrustedRandomAccess, + { + // SAFETY: Callers are only allowed to pass an index that is in bounds + // Additionally Self: TrustedRandomAccess is only implemented for T: Copy which means even + // multiple repeated reads of the same index would be safe and the + // values aree !Drop, thus won't suffer from double drops. + unsafe { self.data.get_unchecked(self.alive.start + idx).assume_init_read() } + } } #[stable(feature = "array_value_iter_impls", since = "1.40.0")] @@ -184,6 +196,17 @@ impl FusedIterator for IntoIter {} #[stable(feature = "array_value_iter_impls", since = "1.40.0")] unsafe impl TrustedLen for IntoIter {} +#[doc(hidden)] +#[unstable(feature = "trusted_random_access", issue = "none")] +// T: Copy as approximation for !Drop since get_unchecked does not update the pointers +// and thus we can't implement drop-handling +unsafe impl TrustedRandomAccess for IntoIter +where + T: Copy, +{ + const MAY_HAVE_SIDE_EFFECT: bool = false; +} + #[stable(feature = "array_value_iter_impls", since = "1.40.0")] impl Clone for IntoIter { fn clone(&self) -> Self { @@ -192,7 +215,7 @@ impl Clone for IntoIter { let mut new = Self { data: MaybeUninit::uninit_array(), alive: 0..0 }; // Clone all alive elements. - for (src, dst) in self.as_slice().iter().zip(&mut new.data) { + for (src, dst) in iter::zip(self.as_slice(), &mut new.data) { // Write a clone into the new array, then update its alive range. // If cloning panics, we'll correctly drop the previous items. dst.write(src.clone()); diff --git a/library/core/src/array/mod.rs b/library/core/src/array/mod.rs index 20251edf6f7f4..b6ce825e2477a 100644 --- a/library/core/src/array/mod.rs +++ b/library/core/src/array/mod.rs @@ -12,7 +12,6 @@ use crate::convert::{Infallible, TryFrom}; use crate::fmt; use crate::hash::{self, Hash}; use crate::iter::TrustedLen; -use crate::marker::Unsize; use crate::mem::{self, MaybeUninit}; use crate::ops::{Index, IndexMut}; use crate::slice::{Iter, IterMut}; @@ -36,41 +35,6 @@ pub fn from_mut(s: &mut T) -> &mut [T; 1] { unsafe { &mut *(s as *mut T).cast::<[T; 1]>() } } -/// Utility trait implemented only on arrays of fixed size -/// -/// This trait can be used to implement other traits on fixed-size arrays -/// without causing much metadata bloat. -/// -/// The trait is marked unsafe in order to restrict implementors to fixed-size -/// arrays. User of this trait can assume that implementors have the exact -/// layout in memory of a fixed size array (for example, for unsafe -/// initialization). -/// -/// Note that the traits [`AsRef`] and [`AsMut`] provide similar methods for types that -/// may not be fixed-size arrays. Implementors should prefer those traits -/// instead. -#[unstable(feature = "fixed_size_array", issue = "27778")] -pub unsafe trait FixedSizeArray { - /// Converts the array to immutable slice - #[unstable(feature = "fixed_size_array", issue = "27778")] - fn as_slice(&self) -> &[T]; - /// Converts the array to mutable slice - #[unstable(feature = "fixed_size_array", issue = "27778")] - fn as_mut_slice(&mut self) -> &mut [T]; -} - -#[unstable(feature = "fixed_size_array", issue = "27778")] -unsafe impl> FixedSizeArray for A { - #[inline] - fn as_slice(&self) -> &[T] { - self - } - #[inline] - fn as_mut_slice(&mut self) -> &mut [T] { - self - } -} - /// The error type returned when a conversion from a slice to an array fails. #[stable(feature = "try_from", since = "1.34.0")] #[derive(Debug, Copy, Clone)] @@ -489,7 +453,7 @@ impl [T; N] { /// ``` /// /// This method is particularly useful if combined with other methods, like - /// [`map`](#method.map). This way, you can can avoid moving the original + /// [`map`](#method.map). This way, you can avoid moving the original /// array if its elements are not `Copy`. /// /// ``` @@ -564,7 +528,7 @@ where /// yields fewer than `N` items, `None` is returned and all already yielded /// items are dropped. /// -/// Since the iterator is passed as mutable reference and this function calls +/// Since the iterator is passed as a mutable reference and this function calls /// `next` at most `N` times, the iterator can still be used afterwards to /// retrieve the remaining items. /// diff --git a/library/core/src/ascii.rs b/library/core/src/ascii.rs index a8a25f927163c..4780d8dc7883f 100644 --- a/library/core/src/ascii.rs +++ b/library/core/src/ascii.rs @@ -145,6 +145,6 @@ impl fmt::Display for EscapeDefault { #[stable(feature = "std_debug", since = "1.16.0")] impl fmt::Debug for EscapeDefault { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.pad("EscapeDefault { .. }") + f.debug_struct("EscapeDefault").finish_non_exhaustive() } } diff --git a/library/core/src/borrow.rs b/library/core/src/borrow.rs index c9040cd0a1670..f28be20aaa1e6 100644 --- a/library/core/src/borrow.rs +++ b/library/core/src/borrow.rs @@ -153,6 +153,7 @@ /// [`HashMap`]: ../../std/collections/struct.HashMap.html /// [`String`]: ../../std/string/struct.String.html #[stable(feature = "rust1", since = "1.0.0")] +#[rustc_diagnostic_item = "Borrow"] pub trait Borrow { /// Immutably borrows from an owned value. /// @@ -205,6 +206,7 @@ pub trait BorrowMut: Borrow { #[stable(feature = "rust1", since = "1.0.0")] impl Borrow for T { + #[rustc_diagnostic_item = "noop_method_borrow"] fn borrow(&self) -> &T { self } diff --git a/library/core/src/cell.rs b/library/core/src/cell.rs index cce1242d84f52..4820588df25cd 100644 --- a/library/core/src/cell.rs +++ b/library/core/src/cell.rs @@ -188,7 +188,7 @@ //! [`Rc`]: ../../std/rc/struct.Rc.html //! [`RwLock`]: ../../std/sync/struct.RwLock.html //! [`Mutex`]: ../../std/sync/struct.Mutex.html -//! [`atomic`]: ../../core/sync/atomic/index.html +//! [`atomic`]: crate::sync::atomic #![stable(feature = "rust1", since = "1.0.0")] @@ -325,7 +325,7 @@ impl Cell { /// let c = Cell::new(5); /// ``` #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_stable(feature = "const_cell_new", since = "1.32.0")] + #[rustc_const_stable(feature = "const_cell_new", since = "1.24.0")] #[inline] pub const fn new(value: T) -> Cell { Cell { value: UnsafeCell::new(value) } @@ -378,7 +378,7 @@ impl Cell { } } - /// Replaces the contained value, and returns it. + /// Replaces the contained value with `val`, and returns the old contained value. /// /// # Examples /// @@ -575,6 +575,12 @@ impl Cell<[T]> { #[stable(feature = "rust1", since = "1.0.0")] pub struct RefCell { borrow: Cell, + // Stores the location of the earliest currently active borrow. + // This gets updated whenver we go from having zero borrows + // to having a single borrow. When a borrow occurs, this gets included + // in the generated `BorroeError/`BorrowMutError` + #[cfg(feature = "debug_refcell")] + borrowed_at: Cell>>, value: UnsafeCell, } @@ -582,12 +588,19 @@ pub struct RefCell { #[stable(feature = "try_borrow", since = "1.13.0")] pub struct BorrowError { _private: (), + #[cfg(feature = "debug_refcell")] + location: &'static crate::panic::Location<'static>, } #[stable(feature = "try_borrow", since = "1.13.0")] impl Debug for BorrowError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("BorrowError").finish() + let mut builder = f.debug_struct("BorrowError"); + + #[cfg(feature = "debug_refcell")] + builder.field("location", self.location); + + builder.finish() } } @@ -602,12 +615,19 @@ impl Display for BorrowError { #[stable(feature = "try_borrow", since = "1.13.0")] pub struct BorrowMutError { _private: (), + #[cfg(feature = "debug_refcell")] + location: &'static crate::panic::Location<'static>, } #[stable(feature = "try_borrow", since = "1.13.0")] impl Debug for BorrowMutError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("BorrowMutError").finish() + let mut builder = f.debug_struct("BorrowMutError"); + + #[cfg(feature = "debug_refcell")] + builder.field("location", self.location); + + builder.finish() } } @@ -655,10 +675,15 @@ impl RefCell { /// let c = RefCell::new(5); /// ``` #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_stable(feature = "const_refcell_new", since = "1.32.0")] + #[rustc_const_stable(feature = "const_refcell_new", since = "1.24.0")] #[inline] pub const fn new(value: T) -> RefCell { - RefCell { value: UnsafeCell::new(value), borrow: Cell::new(UNUSED) } + RefCell { + value: UnsafeCell::new(value), + borrow: Cell::new(UNUSED), + #[cfg(feature = "debug_refcell")] + borrowed_at: Cell::new(None), + } } /// Consumes the `RefCell`, returning the wrapped value. @@ -823,12 +848,29 @@ impl RefCell { /// ``` #[stable(feature = "try_borrow", since = "1.13.0")] #[inline] + #[cfg_attr(feature = "debug_refcell", track_caller)] pub fn try_borrow(&self) -> Result, BorrowError> { match BorrowRef::new(&self.borrow) { - // SAFETY: `BorrowRef` ensures that there is only immutable access - // to the value while borrowed. - Some(b) => Ok(Ref { value: unsafe { &*self.value.get() }, borrow: b }), - None => Err(BorrowError { _private: () }), + Some(b) => { + #[cfg(feature = "debug_refcell")] + { + // `borrowed_at` is always the *first* active borrow + if b.borrow.get() == 1 { + self.borrowed_at.set(Some(crate::panic::Location::caller())); + } + } + + // SAFETY: `BorrowRef` ensures that there is only immutable access + // to the value while borrowed. + Ok(Ref { value: unsafe { &*self.value.get() }, borrow: b }) + } + None => Err(BorrowError { + _private: (), + // If a borrow occured, then we must already have an outstanding borrow, + // so `borrowed_at` will be `Some` + #[cfg(feature = "debug_refcell")] + location: self.borrowed_at.get().unwrap(), + }), } } @@ -896,11 +938,25 @@ impl RefCell { /// ``` #[stable(feature = "try_borrow", since = "1.13.0")] #[inline] + #[cfg_attr(feature = "debug_refcell", track_caller)] pub fn try_borrow_mut(&self) -> Result, BorrowMutError> { match BorrowRefMut::new(&self.borrow) { - // SAFETY: `BorrowRef` guarantees unique access. - Some(b) => Ok(RefMut { value: unsafe { &mut *self.value.get() }, borrow: b }), - None => Err(BorrowMutError { _private: () }), + Some(b) => { + #[cfg(feature = "debug_refcell")] + { + self.borrowed_at.set(Some(crate::panic::Location::caller())); + } + + // SAFETY: `BorrowRef` guarantees unique access. + Ok(RefMut { value: unsafe { &mut *self.value.get() }, borrow: b }) + } + None => Err(BorrowMutError { + _private: (), + // If a borrow occured, then we must already have an outstanding borrow, + // so `borrowed_at` will be `Some` + #[cfg(feature = "debug_refcell")] + location: self.borrowed_at.get().unwrap(), + }), } } @@ -1016,7 +1072,13 @@ impl RefCell { // and is thus guaranteed to be valid for the lifetime of `self`. Ok(unsafe { &*self.value.get() }) } else { - Err(BorrowError { _private: () }) + Err(BorrowError { + _private: (), + // If a borrow occured, then we must already have an outstanding borrow, + // so `borrowed_at` will be `Some` + #[cfg(feature = "debug_refcell")] + location: self.borrowed_at.get().unwrap(), + }) } } } @@ -1753,7 +1815,7 @@ impl UnsafeCell { /// ``` #[stable(feature = "rust1", since = "1.0.0")] #[rustc_const_stable(feature = "const_unsafe_cell_new", since = "1.32.0")] - #[inline] + #[inline(always)] pub const fn new(value: T) -> UnsafeCell { UnsafeCell { value } } @@ -1769,7 +1831,7 @@ impl UnsafeCell { /// /// let five = uc.into_inner(); /// ``` - #[inline] + #[inline(always)] #[stable(feature = "rust1", since = "1.0.0")] #[rustc_const_unstable(feature = "const_cell_into_inner", issue = "78729")] pub const fn into_inner(self) -> T { @@ -1794,7 +1856,7 @@ impl UnsafeCell { /// /// let five = uc.get(); /// ``` - #[inline] + #[inline(always)] #[stable(feature = "rust1", since = "1.0.0")] #[rustc_const_stable(feature = "const_unsafecell_get", since = "1.32.0")] pub const fn get(&self) -> *mut T { @@ -1819,7 +1881,7 @@ impl UnsafeCell { /// /// assert_eq!(*c.get_mut(), 6); /// ``` - #[inline] + #[inline(always)] #[stable(feature = "unsafe_cell_get_mut", since = "1.50.0")] pub fn get_mut(&mut self) -> &mut T { &mut self.value @@ -1852,7 +1914,7 @@ impl UnsafeCell { /// /// assert_eq!(uc.into_inner(), 5); /// ``` - #[inline] + #[inline(always)] #[unstable(feature = "unsafe_cell_raw_get", issue = "66358")] pub const fn raw_get(this: *const Self) -> *mut T { // We can just cast the pointer from `UnsafeCell` to `T` because of diff --git a/library/core/src/char/methods.rs b/library/core/src/char/methods.rs index b3e1becf570a7..dcab2cd2d9db1 100644 --- a/library/core/src/char/methods.rs +++ b/library/core/src/char/methods.rs @@ -18,7 +18,7 @@ impl char { /// /// [Unicode Scalar Value]: http://www.unicode.org/glossary/#unicode_scalar_value /// [Code Point]: http://www.unicode.org/glossary/#code_point - #[unstable(feature = "assoc_char_consts", reason = "recently added", issue = "71763")] + #[stable(feature = "assoc_char_consts", since = "1.52.0")] pub const MAX: char = '\u{10ffff}'; /// `U+FFFD REPLACEMENT CHARACTER` (�) is used in Unicode to represent a @@ -26,7 +26,7 @@ impl char { /// /// It can occur, for example, when giving ill-formed UTF-8 bytes to /// [`String::from_utf8_lossy`](string/struct.String.html#method.from_utf8_lossy). - #[unstable(feature = "assoc_char_consts", reason = "recently added", issue = "71763")] + #[stable(feature = "assoc_char_consts", since = "1.52.0")] pub const REPLACEMENT_CHARACTER: char = '\u{FFFD}'; /// The version of [Unicode](http://www.unicode.org/) that the Unicode parts of @@ -39,7 +39,7 @@ impl char { /// /// The version numbering scheme is explained in /// [Unicode 11.0 or later, Section 3.1 Versions of the Unicode Standard](https://www.unicode.org/versions/Unicode11.0.0/ch03.pdf#page=4). - #[unstable(feature = "assoc_char_consts", reason = "recently added", issue = "71763")] + #[stable(feature = "assoc_char_consts", since = "1.52.0")] pub const UNICODE_VERSION: (u8, u8, u8) = crate::unicode::UNICODE_VERSION; /// Creates an iterator over the UTF-16 encoded code points in `iter`, @@ -88,7 +88,7 @@ impl char { /// "𝄞mus�ic�" /// ); /// ``` - #[unstable(feature = "assoc_char_funcs", reason = "recently added", issue = "71763")] + #[stable(feature = "assoc_char_funcs", since = "1.52.0")] #[inline] pub fn decode_utf16>(iter: I) -> DecodeUtf16 { super::decode::decode_utf16(iter) @@ -136,7 +136,7 @@ impl char { /// /// assert_eq!(None, c); /// ``` - #[unstable(feature = "assoc_char_funcs", reason = "recently added", issue = "71763")] + #[stable(feature = "assoc_char_funcs", since = "1.52.0")] #[inline] pub fn from_u32(i: u32) -> Option { super::convert::from_u32(i) @@ -177,7 +177,7 @@ impl char { /// /// assert_eq!('❤', c); /// ``` - #[unstable(feature = "assoc_char_funcs", reason = "recently added", issue = "71763")] + #[stable(feature = "assoc_char_funcs", since = "1.52.0")] #[inline] pub unsafe fn from_u32_unchecked(i: u32) -> char { // SAFETY: the safety contract must be upheld by the caller. @@ -233,7 +233,7 @@ impl char { /// // this panics /// char::from_digit(1, 37); /// ``` - #[unstable(feature = "assoc_char_funcs", reason = "recently added", issue = "71763")] + #[stable(feature = "assoc_char_funcs", since = "1.52.0")] #[inline] pub fn from_digit(num: u32, radix: u32) -> Option { super::convert::from_digit(num, radix) @@ -403,16 +403,20 @@ impl char { } /// An extended version of `escape_debug` that optionally permits escaping - /// Extended Grapheme codepoints. This allows us to format characters like - /// nonspacing marks better when they're at the start of a string. + /// Extended Grapheme codepoints, single quotes, and double quotes. This + /// allows us to format characters like nonspacing marks better when they're + /// at the start of a string, and allows escaping single quotes in + /// characters, and double quotes in strings. #[inline] - pub(crate) fn escape_debug_ext(self, escape_grapheme_extended: bool) -> EscapeDebug { + pub(crate) fn escape_debug_ext(self, args: EscapeDebugExtArgs) -> EscapeDebug { let init_state = match self { '\t' => EscapeDefaultState::Backslash('t'), '\r' => EscapeDefaultState::Backslash('r'), '\n' => EscapeDefaultState::Backslash('n'), - '\\' | '\'' | '"' => EscapeDefaultState::Backslash(self), - _ if escape_grapheme_extended && self.is_grapheme_extended() => { + '\\' => EscapeDefaultState::Backslash(self), + '"' if args.escape_double_quote => EscapeDefaultState::Backslash(self), + '\'' if args.escape_single_quote => EscapeDefaultState::Backslash(self), + _ if args.escape_grapheme_extended && self.is_grapheme_extended() => { EscapeDefaultState::Unicode(self.escape_unicode()) } _ if is_printable(self) => EscapeDefaultState::Char(self), @@ -458,7 +462,7 @@ impl char { #[stable(feature = "char_escape_debug", since = "1.20.0")] #[inline] pub fn escape_debug(self) -> EscapeDebug { - self.escape_debug_ext(true) + self.escape_debug_ext(EscapeDebugExtArgs::ESCAPE_ALL) } /// Returns an iterator that yields the literal escape code of a character @@ -1565,6 +1569,25 @@ impl char { } } +pub(crate) struct EscapeDebugExtArgs { + /// Escape Extended Grapheme codepoints? + pub(crate) escape_grapheme_extended: bool, + + /// Escape single quotes? + pub(crate) escape_single_quote: bool, + + /// Escape double quotes? + pub(crate) escape_double_quote: bool, +} + +impl EscapeDebugExtArgs { + pub(crate) const ESCAPE_ALL: Self = Self { + escape_grapheme_extended: true, + escape_single_quote: true, + escape_double_quote: true, + }; +} + #[inline] const fn len_utf8(code: u32) -> usize { if code < MAX_ONE_B { diff --git a/library/core/src/char/mod.rs b/library/core/src/char/mod.rs index 788fafa0adc5c..25a7c1de9de4b 100644 --- a/library/core/src/char/mod.rs +++ b/library/core/src/char/mod.rs @@ -45,6 +45,8 @@ pub use self::methods::encode_utf8_raw; use crate::fmt::{self, Write}; use crate::iter::FusedIterator; +pub(crate) use self::methods::EscapeDebugExtArgs; + // UTF-8 ranges and tags for encoding characters const TAG_CONT: u8 = 0b1000_0000; const TAG_TWO_B: u8 = 0b1100_0000; diff --git a/library/core/src/clone.rs b/library/core/src/clone.rs index a953a3a4182bc..51a2dc03de318 100644 --- a/library/core/src/clone.rs +++ b/library/core/src/clone.rs @@ -104,12 +104,14 @@ /// [impls]: #implementors #[stable(feature = "rust1", since = "1.0.0")] #[lang = "clone"] +#[rustc_diagnostic_item = "Clone"] pub trait Clone: Sized { /// Returns a copy of the value. /// /// # Examples /// /// ``` + /// # #![allow(noop_method_call)] /// let hello = "Hello"; // &str implements Clone /// /// assert_eq!("Hello", hello.clone()); @@ -221,6 +223,7 @@ mod impls { #[stable(feature = "rust1", since = "1.0.0")] impl Clone for &T { #[inline] + #[rustc_diagnostic_item = "noop_method_clone"] fn clone(&self) -> Self { *self } diff --git a/library/core/src/cmp.rs b/library/core/src/cmp.rs index 4a15b185a83e7..67dd1d83415bd 100644 --- a/library/core/src/cmp.rs +++ b/library/core/src/cmp.rs @@ -579,6 +579,7 @@ impl Ordering { /// ``` #[derive(PartialEq, Eq, Debug, Copy, Clone, Default, Hash)] #[stable(feature = "reverse_cmp_key", since = "1.19.0")] +#[repr(transparent)] pub struct Reverse(#[stable(feature = "reverse_cmp_key", since = "1.19.0")] pub T); #[stable(feature = "reverse_cmp_key", since = "1.19.0")] @@ -980,7 +981,11 @@ pub trait PartialOrd: PartialEq { #[must_use] #[stable(feature = "rust1", since = "1.0.0")] fn le(&self, other: &Rhs) -> bool { - matches!(self.partial_cmp(other), Some(Less | Equal)) + // Pattern `Some(Less | Eq)` optimizes worse than negating `None | Some(Greater)`. + // FIXME: The root cause was fixed upstream in LLVM with: + // https://github.com/llvm/llvm-project/commit/9bad7de9a3fb844f1ca2965f35d0c2a3d1e11775 + // Revert this workaround once support for LLVM 12 gets dropped. + !matches!(self.partial_cmp(other), None | Some(Greater)) } /// This method tests greater than (for `self` and `other`) and is used by the `>` operator. @@ -1057,8 +1062,6 @@ pub fn min(v1: T, v2: T) -> T { /// # Examples /// /// ``` -/// #![feature(cmp_min_max_by)] -/// /// use std::cmp; /// /// assert_eq!(cmp::min_by(-2, 1, |x: &i32, y: &i32| x.abs().cmp(&y.abs())), 1); @@ -1066,7 +1069,7 @@ pub fn min(v1: T, v2: T) -> T { /// ``` #[inline] #[must_use] -#[unstable(feature = "cmp_min_max_by", issue = "64460")] +#[stable(feature = "cmp_min_max_by", since = "1.53.0")] pub fn min_by Ordering>(v1: T, v2: T, compare: F) -> T { match compare(&v1, &v2) { Ordering::Less | Ordering::Equal => v1, @@ -1081,8 +1084,6 @@ pub fn min_by Ordering>(v1: T, v2: T, compare: F) -> T { /// # Examples /// /// ``` -/// #![feature(cmp_min_max_by)] -/// /// use std::cmp; /// /// assert_eq!(cmp::min_by_key(-2, 1, |x: &i32| x.abs()), 1); @@ -1090,7 +1091,7 @@ pub fn min_by Ordering>(v1: T, v2: T, compare: F) -> T { /// ``` #[inline] #[must_use] -#[unstable(feature = "cmp_min_max_by", issue = "64460")] +#[stable(feature = "cmp_min_max_by", since = "1.53.0")] pub fn min_by_key K, K: Ord>(v1: T, v2: T, mut f: F) -> T { min_by(v1, v2, |v1, v2| f(v1).cmp(&f(v2))) } @@ -1123,8 +1124,6 @@ pub fn max(v1: T, v2: T) -> T { /// # Examples /// /// ``` -/// #![feature(cmp_min_max_by)] -/// /// use std::cmp; /// /// assert_eq!(cmp::max_by(-2, 1, |x: &i32, y: &i32| x.abs().cmp(&y.abs())), -2); @@ -1132,7 +1131,7 @@ pub fn max(v1: T, v2: T) -> T { /// ``` #[inline] #[must_use] -#[unstable(feature = "cmp_min_max_by", issue = "64460")] +#[stable(feature = "cmp_min_max_by", since = "1.53.0")] pub fn max_by Ordering>(v1: T, v2: T, compare: F) -> T { match compare(&v1, &v2) { Ordering::Less | Ordering::Equal => v2, @@ -1147,8 +1146,6 @@ pub fn max_by Ordering>(v1: T, v2: T, compare: F) -> T { /// # Examples /// /// ``` -/// #![feature(cmp_min_max_by)] -/// /// use std::cmp; /// /// assert_eq!(cmp::max_by_key(-2, 1, |x: &i32| x.abs()), -2); @@ -1156,7 +1153,7 @@ pub fn max_by Ordering>(v1: T, v2: T, compare: F) -> T { /// ``` #[inline] #[must_use] -#[unstable(feature = "cmp_min_max_by", issue = "64460")] +#[stable(feature = "cmp_min_max_by", since = "1.53.0")] pub fn max_by_key K, K: Ord>(v1: T, v2: T, mut f: F) -> T { max_by(v1, v2, |v1, v2| f(v1).cmp(&f(v2))) } diff --git a/library/core/src/convert/num.rs b/library/core/src/convert/num.rs index 5b113610a5d3f..a522b7da3bd1c 100644 --- a/library/core/src/convert/num.rs +++ b/library/core/src/convert/num.rs @@ -45,8 +45,10 @@ impl_float_to_int!(f64 => u8 u16 u32 u64 u128 usize i8 i16 i32 i64 i128 isize); macro_rules! impl_from { ($Small: ty, $Large: ty, #[$attr:meta], $doc: expr) => { #[$attr] - #[doc = $doc] impl From<$Small> for $Large { + // Rustdocs on the impl block show a "[+] show undocumented items" toggle. + // Rustdocs on functions do not. + #[doc = $doc] #[inline] fn from(small: $Small) -> Self { small as Self @@ -383,8 +385,10 @@ use crate::num::NonZeroUsize; macro_rules! nzint_impl_from { ($Small: ty, $Large: ty, #[$attr:meta], $doc: expr) => { #[$attr] - #[doc = $doc] impl From<$Small> for $Large { + // Rustdocs on the impl block show a "[+] show undocumented items" toggle. + // Rustdocs on functions do not. + #[doc = $doc] #[inline] fn from(small: $Small) -> Self { // SAFETY: input type guarantees the value is non-zero @@ -450,10 +454,12 @@ nzint_impl_from! { NonZeroU64, NonZeroI128, #[stable(feature = "nz_int_conv", si macro_rules! nzint_impl_try_from_int { ($Int: ty, $NonZeroInt: ty, #[$attr:meta], $doc: expr) => { #[$attr] - #[doc = $doc] impl TryFrom<$Int> for $NonZeroInt { type Error = TryFromIntError; + // Rustdocs on the impl block show a "[+] show undocumented items" toggle. + // Rustdocs on functions do not. + #[doc = $doc] #[inline] fn try_from(value: $Int) -> Result { Self::new(value).ok_or(TryFromIntError(())) @@ -489,10 +495,12 @@ nzint_impl_try_from_int! { isize, NonZeroIsize, #[stable(feature = "nzint_try_fr macro_rules! nzint_impl_try_from_nzint { ($From:ty => $To:ty, $doc: expr) => { #[stable(feature = "nzint_try_from_nzint_conv", since = "1.49.0")] - #[doc = $doc] impl TryFrom<$From> for $To { type Error = TryFromIntError; + // Rustdocs on the impl block show a "[+] show undocumented items" toggle. + // Rustdocs on functions do not. + #[doc = $doc] #[inline] fn try_from(value: $From) -> Result { TryFrom::try_from(value.get()).map(|v| { diff --git a/library/core/src/default.rs b/library/core/src/default.rs index 28ec3279459ab..fd7159d35fa7f 100644 --- a/library/core/src/default.rs +++ b/library/core/src/default.rs @@ -80,6 +80,7 @@ /// bar: f32, /// } /// ``` +#[cfg_attr(not(test), rustc_diagnostic_item = "Default")] #[stable(feature = "rust1", since = "1.0.0")] pub trait Default: Sized { /// Returns the "default value" for a type. diff --git a/library/core/src/ffi.rs b/library/core/src/ffi.rs index 9302baa823bc5..b208ddd4b272f 100644 --- a/library/core/src/ffi.rs +++ b/library/core/src/ffi.rs @@ -53,7 +53,7 @@ pub enum c_void { #[stable(feature = "std_debug", since = "1.16.0")] impl fmt::Debug for c_void { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.pad("c_void") + f.debug_struct("c_void").finish() } } diff --git a/library/core/src/fmt/builders.rs b/library/core/src/fmt/builders.rs index d95d43f085448..b660788c0515f 100644 --- a/library/core/src/fmt/builders.rs +++ b/library/core/src/fmt/builders.rs @@ -165,7 +165,6 @@ impl<'a, 'b: 'a> DebugStruct<'a, 'b> { /// # Examples /// /// ``` - /// # #![feature(debug_non_exhaustive)] /// use std::fmt; /// /// struct Bar { @@ -186,31 +185,22 @@ impl<'a, 'b: 'a> DebugStruct<'a, 'b> { /// "Bar { bar: 10, .. }", /// ); /// ``` - #[unstable(feature = "debug_non_exhaustive", issue = "67364")] + #[stable(feature = "debug_non_exhaustive", since = "1.53.0")] pub fn finish_non_exhaustive(&mut self) -> fmt::Result { self.result = self.result.and_then(|_| { - // Draw non-exhaustive dots (`..`), and open brace if necessary (no fields). - if self.is_pretty() { - if !self.has_fields { - self.fmt.write_str(" {\n")?; - } - let mut slot = None; - let mut state = Default::default(); - let mut writer = PadAdapter::wrap(&mut self.fmt, &mut slot, &mut state); - writer.write_str("..\n")?; - } else { - if self.has_fields { - self.fmt.write_str(", ..")?; + if self.has_fields { + if self.is_pretty() { + let mut slot = None; + let mut state = Default::default(); + let mut writer = PadAdapter::wrap(&mut self.fmt, &mut slot, &mut state); + writer.write_str("..\n")?; + self.fmt.write_str("}") } else { - self.fmt.write_str(" { ..")?; + self.fmt.write_str(", .. }") } - } - if self.is_pretty() { - self.fmt.write_str("}")? } else { - self.fmt.write_str(" }")?; + self.fmt.write_str(" { .. }") } - Ok(()) }); self.result } diff --git a/library/core/src/fmt/float.rs b/library/core/src/fmt/float.rs index 5908da477e11e..ece3cde001580 100644 --- a/library/core/src/fmt/float.rs +++ b/library/core/src/fmt/float.rs @@ -54,21 +54,14 @@ where } // Common code of floating point Debug and Display. -fn float_to_decimal_common( - fmt: &mut Formatter<'_>, - num: &T, - negative_zero: bool, - min_precision: usize, -) -> Result +fn float_to_decimal_common(fmt: &mut Formatter<'_>, num: &T, min_precision: usize) -> Result where T: flt2dec::DecodableFloat, { let force_sign = fmt.sign_plus(); - let sign = match (force_sign, negative_zero) { - (false, false) => flt2dec::Sign::Minus, - (false, true) => flt2dec::Sign::MinusRaw, - (true, false) => flt2dec::Sign::MinusPlus, - (true, true) => flt2dec::Sign::MinusPlusRaw, + let sign = match force_sign { + false => flt2dec::Sign::Minus, + true => flt2dec::Sign::MinusPlus, }; if let Some(precision) = fmt.precision { @@ -156,14 +149,14 @@ macro_rules! floating { #[stable(feature = "rust1", since = "1.0.0")] impl Debug for $ty { fn fmt(&self, fmt: &mut Formatter<'_>) -> Result { - float_to_decimal_common(fmt, self, true, 1) + float_to_decimal_common(fmt, self, 1) } } #[stable(feature = "rust1", since = "1.0.0")] impl Display for $ty { fn fmt(&self, fmt: &mut Formatter<'_>) -> Result { - float_to_decimal_common(fmt, self, false, 0) + float_to_decimal_common(fmt, self, 0) } } diff --git a/library/core/src/fmt/mod.rs b/library/core/src/fmt/mod.rs index 2df5e562745d0..87042d95fbef0 100644 --- a/library/core/src/fmt/mod.rs +++ b/library/core/src/fmt/mod.rs @@ -3,6 +3,8 @@ #![stable(feature = "rust1", since = "1.0.0")] use crate::cell::{Cell, Ref, RefCell, RefMut, UnsafeCell}; +use crate::char::EscapeDebugExtArgs; +use crate::iter; use crate::marker::PhantomData; use crate::mem; use crate::num::flt2dec; @@ -1087,7 +1089,7 @@ pub fn write(output: &mut dyn Write, args: Arguments<'_>) -> Result { match args.fmt { None => { // We can use default formatting parameters for all arguments. - for (arg, piece) in args.args.iter().zip(args.pieces.iter()) { + for (arg, piece) in iter::zip(args.args, args.pieces) { formatter.buf.write_str(*piece)?; (arg.formatter)(arg.value, &mut formatter)?; idx += 1; @@ -1096,7 +1098,7 @@ pub fn write(output: &mut dyn Write, args: Arguments<'_>) -> Result { Some(fmt) => { // Every spec has a corresponding argument that is preceded by // a string piece. - for (arg, piece) in fmt.iter().zip(args.pieces.iter()) { + for (arg, piece) in iter::zip(fmt, args.pieces) { formatter.buf.write_str(*piece)?; // SAFETY: arg and args.args come from the same Arguments, // which guarantees the indexes are always within bounds. @@ -2054,7 +2056,11 @@ impl Debug for str { f.write_char('"')?; let mut from = 0; for (i, c) in self.char_indices() { - let esc = c.escape_debug(); + let esc = c.escape_debug_ext(EscapeDebugExtArgs { + escape_grapheme_extended: true, + escape_single_quote: false, + escape_double_quote: true, + }); // If char needs escaping, flush backlog so far and write, else skip if esc.len() != 1 { f.write_str(&self[from..i])?; @@ -2080,7 +2086,11 @@ impl Display for str { impl Debug for char { fn fmt(&self, f: &mut Formatter<'_>) -> Result { f.write_char('\'')?; - for c in self.escape_debug() { + for c in self.escape_debug_ext(EscapeDebugExtArgs { + escape_grapheme_extended: true, + escape_single_quote: true, + escape_double_quote: false, + }) { f.write_char(c)? } f.write_char('\'') @@ -2210,7 +2220,7 @@ impl Debug for () { #[stable(feature = "rust1", since = "1.0.0")] impl Debug for PhantomData { fn fmt(&self, f: &mut Formatter<'_>) -> Result { - f.pad("PhantomData") + f.debug_struct("PhantomData").finish() } } @@ -2258,9 +2268,9 @@ impl Debug for RefMut<'_, T> { } #[stable(feature = "core_impl_debug", since = "1.9.0")] -impl Debug for UnsafeCell { +impl Debug for UnsafeCell { fn fmt(&self, f: &mut Formatter<'_>) -> Result { - f.pad("UnsafeCell") + f.debug_struct("UnsafeCell").finish_non_exhaustive() } } diff --git a/library/core/src/future/future.rs b/library/core/src/future/future.rs index e9a99ddb6b1bd..cc4cf54a2f764 100644 --- a/library/core/src/future/future.rs +++ b/library/core/src/future/future.rs @@ -24,7 +24,8 @@ use crate::task::{Context, Poll}; /// `.await` the value. /// /// [`Waker`]: crate::task::Waker -#[doc(spotlight)] +#[cfg_attr(bootstrap, doc(spotlight))] +#[cfg_attr(not(bootstrap), doc(notable_trait))] #[must_use = "futures do nothing unless you `.await` or poll them"] #[stable(feature = "futures_api", since = "1.36.0")] #[lang = "future_trait"] diff --git a/library/core/src/hash/mod.rs b/library/core/src/hash/mod.rs index cd47f97496a4a..7dbd68b73f05a 100644 --- a/library/core/src/hash/mod.rs +++ b/library/core/src/hash/mod.rs @@ -1,7 +1,13 @@ //! Generic hashing support. //! -//! This module provides a generic way to compute the hash of a value. The -//! simplest way to make a type hashable is to use `#[derive(Hash)]`: +//! This module provides a generic way to compute the [hash] of a value. +//! Hashes are most commonly used with [`HashMap`] and [`HashSet`]. +//! +//! [hash]: https://en.wikipedia.org/wiki/Hash_function +//! [`HashMap`]: ../../std/collections/struct.HashMap.html +//! [`HashSet`]: ../../std/collections/struct.HashSet.html +//! +//! The simplest way to make a type hashable is to use `#[derive(Hash)]`: //! //! # Examples //! @@ -501,7 +507,7 @@ pub struct BuildHasherDefault(marker::PhantomData); #[stable(since = "1.9.0", feature = "core_impl_debug")] impl fmt::Debug for BuildHasherDefault { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.pad("BuildHasherDefault") + f.debug_struct("BuildHasherDefault").finish() } } @@ -548,10 +554,12 @@ mod impls { ($(($ty:ident, $meth:ident),)*) => {$( #[stable(feature = "rust1", since = "1.0.0")] impl Hash for $ty { + #[inline] fn hash(&self, state: &mut H) { state.$meth(*self) } + #[inline] fn hash_slice(data: &[$ty], state: &mut H) { let newlen = data.len() * mem::size_of::<$ty>(); let ptr = data.as_ptr() as *const u8; @@ -582,6 +590,7 @@ mod impls { #[stable(feature = "rust1", since = "1.0.0")] impl Hash for bool { + #[inline] fn hash(&self, state: &mut H) { state.write_u8(*self as u8) } @@ -589,6 +598,7 @@ mod impls { #[stable(feature = "rust1", since = "1.0.0")] impl Hash for char { + #[inline] fn hash(&self, state: &mut H) { state.write_u32(*self as u32) } @@ -596,6 +606,7 @@ mod impls { #[stable(feature = "rust1", since = "1.0.0")] impl Hash for str { + #[inline] fn hash(&self, state: &mut H) { state.write(self.as_bytes()); state.write_u8(0xff) @@ -604,6 +615,7 @@ mod impls { #[stable(feature = "never_hash", since = "1.29.0")] impl Hash for ! { + #[inline] fn hash(&self, _: &mut H) { *self } @@ -613,6 +625,7 @@ mod impls { () => ( #[stable(feature = "rust1", since = "1.0.0")] impl Hash for () { + #[inline] fn hash(&self, _state: &mut H) {} } ); @@ -621,6 +634,7 @@ mod impls { #[stable(feature = "rust1", since = "1.0.0")] impl<$($name: Hash),+> Hash for ($($name,)+) where last_type!($($name,)+): ?Sized { #[allow(non_snake_case)] + #[inline] fn hash(&self, state: &mut S) { let ($(ref $name,)+) = *self; $($name.hash(state);)+ @@ -650,6 +664,7 @@ mod impls { #[stable(feature = "rust1", since = "1.0.0")] impl Hash for [T] { + #[inline] fn hash(&self, state: &mut H) { self.len().hash(state); Hash::hash_slice(self, state) @@ -658,6 +673,7 @@ mod impls { #[stable(feature = "rust1", since = "1.0.0")] impl Hash for &T { + #[inline] fn hash(&self, state: &mut H) { (**self).hash(state); } @@ -665,6 +681,7 @@ mod impls { #[stable(feature = "rust1", since = "1.0.0")] impl Hash for &mut T { + #[inline] fn hash(&self, state: &mut H) { (**self).hash(state); } @@ -672,59 +689,21 @@ mod impls { #[stable(feature = "rust1", since = "1.0.0")] impl Hash for *const T { + #[inline] fn hash(&self, state: &mut H) { - #[cfg(not(bootstrap))] - { - let (address, metadata) = self.to_raw_parts(); - state.write_usize(address as usize); - metadata.hash(state); - } - #[cfg(bootstrap)] - { - if mem::size_of::() == mem::size_of::() { - // Thin pointer - state.write_usize(*self as *const () as usize); - } else { - // Fat pointer - // SAFETY: we are accessing the memory occupied by `self` - // which is guaranteed to be valid. - // This assumes a fat pointer can be represented by a `(usize, usize)`, - // which is safe to do in `std` because it is shipped and kept in sync - // with the implementation of fat pointers in `rustc`. - let (a, b) = unsafe { *(self as *const Self as *const (usize, usize)) }; - state.write_usize(a); - state.write_usize(b); - } - } + let (address, metadata) = self.to_raw_parts(); + state.write_usize(address as usize); + metadata.hash(state); } } #[stable(feature = "rust1", since = "1.0.0")] impl Hash for *mut T { + #[inline] fn hash(&self, state: &mut H) { - #[cfg(not(bootstrap))] - { - let (address, metadata) = self.to_raw_parts(); - state.write_usize(address as usize); - metadata.hash(state); - } - #[cfg(bootstrap)] - { - if mem::size_of::() == mem::size_of::() { - // Thin pointer - state.write_usize(*self as *const () as usize); - } else { - // Fat pointer - // SAFETY: we are accessing the memory occupied by `self` - // which is guaranteed to be valid. - // This assumes a fat pointer can be represented by a `(usize, usize)`, - // which is safe to do in `std` because it is shipped and kept in sync - // with the implementation of fat pointers in `rustc`. - let (a, b) = unsafe { *(self as *const Self as *const (usize, usize)) }; - state.write_usize(a); - state.write_usize(b); - } - } + let (address, metadata) = self.to_raw_parts(); + state.write_usize(address as usize); + metadata.hash(state); } } } diff --git a/library/core/src/intrinsics.rs b/library/core/src/intrinsics.rs index afea8aa8ff262..1ba0b23ae5be3 100644 --- a/library/core/src/intrinsics.rs +++ b/library/core/src/intrinsics.rs @@ -65,9 +65,13 @@ use crate::sync::atomic::{self, AtomicBool, AtomicI32, AtomicIsize, AtomicU32, O #[stable(feature = "drop_in_place", since = "1.8.0")] #[rustc_deprecated( reason = "no longer an intrinsic - use `ptr::drop_in_place` directly", - since = "1.18.0" + since = "1.52.0" )] -pub use crate::ptr::drop_in_place; +#[inline] +pub unsafe fn drop_in_place(to_drop: *mut T) { + // SAFETY: see `ptr::drop_in_place` + unsafe { crate::ptr::drop_in_place(to_drop) } +} extern "rust-intrinsic" { // N.B., these intrinsics take raw pointers because they mutate aliased @@ -846,6 +850,12 @@ extern "rust-intrinsic" { /// destination value, then forgets the original. It's equivalent to C's /// `memcpy` under the hood, just like `transmute_copy`. /// + /// Because `transmute` is a by-value operation, alignment of the *transmuted values + /// themselves* is not a concern. As with any other function, the compiler already ensures + /// both `T` and `U` are properly aligned. However, when transmuting values that *point + /// elsewhere* (such as pointers, references, boxes…), the caller has to ensure proper + /// alignment of the pointed-to values. + /// /// `transmute` is **incredibly** unsafe. There are a vast number of ways to /// cause [undefined behavior][ub] with this function. `transmute` should be /// the absolute last resort. @@ -965,7 +975,13 @@ extern "rust-intrinsic" { /// assert_eq!(b"Rust", &[82, 117, 115, 116]); /// ``` /// - /// Turning a `Vec<&T>` into a `Vec>`: + /// Turning a `Vec<&T>` into a `Vec>`. + /// + /// To transmute the inner type of the contents of a container, you must make sure to not + /// violate any of the container's invariants. For `Vec`, this means that both the size + /// *and alignment* of the inner types have to match. Other containers might rely on the + /// size of the type, alignment, or even the `TypeId`, in which case transmuting wouldn't + /// be possible at all without violating the container invariants. /// /// ``` /// let store = [0, 1, 2, 3]; @@ -991,14 +1007,11 @@ extern "rust-intrinsic" { /// /// let v_clone = v_orig.clone(); /// - /// // The no-copy, unsafe way, still using transmute, but not relying on the data layout. - /// // Like the first approach, this reuses the `Vec` internals. - /// // Therefore, the new inner type must have the - /// // exact same size, *and the same alignment*, as the old type. - /// // The same caveats exist for this method as transmute, for - /// // the original inner type (`&i32`) to the converted inner type - /// // (`Option<&i32>`), so read the nomicon pages linked above and also - /// // consult the [`from_raw_parts`] documentation. + /// // This is the proper no-copy, unsafe way of "transmuting" a `Vec`, without relying on the + /// // data layout. Instead of literally calling `transmute`, we perform a pointer cast, but + /// // in terms of converting the original inner type (`&i32`) to the new one (`Option<&i32>`), + /// // this has all the same caveats. Besides the information provided above, also consult the + /// // [`from_raw_parts`] documentation. /// let v_from_raw = unsafe { // FIXME Update this when vec_into_raw_parts is stabilized /// // Ensure the original vector is not dropped. @@ -1530,7 +1543,7 @@ extern "rust-intrinsic" { /// let num_trailing = unsafe { cttz_nonzero(x) }; /// assert_eq!(num_trailing, 3); /// ``` - #[rustc_const_unstable(feature = "const_cttz", issue = "none")] + #[rustc_const_stable(feature = "const_cttz", since = "1.53.0")] pub fn cttz_nonzero(x: T) -> T; /// Reverses the bytes in an integer type `T`. @@ -1893,18 +1906,6 @@ pub(crate) fn is_aligned_and_not_null(ptr: *const T) -> bool { !ptr.is_null() && ptr as usize % mem::align_of::() == 0 } -/// Checks whether the regions of memory starting at `src` and `dst` of size -/// `count * size_of::()` do *not* overlap. -pub(crate) fn is_nonoverlapping(src: *const T, dst: *const T, count: usize) -> bool { - let src_usize = src as usize; - let dst_usize = dst as usize; - let size = mem::size_of::().checked_mul(count).unwrap(); - let diff = if src_usize > dst_usize { src_usize - dst_usize } else { dst_usize - src_usize }; - // If the absolute distance between the ptrs is at least as big as the size of the buffer, - // they do not overlap. - diff >= size -} - /// Sets `count * size_of::()` bytes of memory starting at `dst` to /// `val`. /// diff --git a/library/core/src/iter/adapters/filter.rs b/library/core/src/iter/adapters/filter.rs index f8d684fcdda46..0337892b9e8e1 100644 --- a/library/core/src/iter/adapters/filter.rs +++ b/library/core/src/iter/adapters/filter.rs @@ -13,7 +13,8 @@ use crate::ops::Try; #[stable(feature = "rust1", since = "1.0.0")] #[derive(Clone)] pub struct Filter { - iter: I, + // Used for `SplitWhitespace` and `SplitAsciiWhitespace` `as_str` methods + pub(crate) iter: I, predicate: P, } impl Filter { diff --git a/library/core/src/iter/adapters/map.rs b/library/core/src/iter/adapters/map.rs index 2d997cfe50946..2a4b7efd5e665 100644 --- a/library/core/src/iter/adapters/map.rs +++ b/library/core/src/iter/adapters/map.rs @@ -57,7 +57,8 @@ use crate::ops::Try; #[stable(feature = "rust1", since = "1.0.0")] #[derive(Clone)] pub struct Map { - iter: I, + // Used for `SplitWhitespace` and `SplitAsciiWhitespace` `as_str` methods + pub(crate) iter: I, f: F, } diff --git a/library/core/src/iter/adapters/mod.rs b/library/core/src/iter/adapters/mod.rs index 41a7b13232adf..61d8351d59f09 100644 --- a/library/core/src/iter/adapters/mod.rs +++ b/library/core/src/iter/adapters/mod.rs @@ -51,6 +51,9 @@ pub use self::map_while::MapWhile; #[unstable(feature = "trusted_random_access", issue = "none")] pub use self::zip::TrustedRandomAccess; +#[unstable(feature = "iter_zip", issue = "83574")] +pub use self::zip::zip; + /// This trait provides transitive access to source-stage in an interator-adapter pipeline /// under the conditions that /// * the iterator source `S` itself implements `SourceIter` @@ -191,3 +194,26 @@ where self.try_fold(init, ok(fold)).unwrap() } } + +#[unstable(issue = "none", feature = "inplace_iteration")] +unsafe impl SourceIter for ResultShunt<'_, I, E> +where + I: SourceIter, +{ + type Source = S; + + #[inline] + unsafe fn as_inner(&mut self) -> &mut S { + // SAFETY: unsafe function forwarding to unsafe function with the same requirements + unsafe { SourceIter::as_inner(&mut self.iter) } + } +} + +// SAFETY: ResultShunt::next calls I::find, which has to advance `iter` in order to +// return `Some(_)`. Since `iter` has type `I: InPlaceIterable` it's guaranteed that +// at least one item will be moved out from the underlying source. +#[unstable(issue = "none", feature = "inplace_iteration")] +unsafe impl InPlaceIterable for ResultShunt<'_, I, E> where + I: Iterator> + InPlaceIterable +{ +} diff --git a/library/core/src/iter/adapters/peekable.rs b/library/core/src/iter/adapters/peekable.rs index 43301444e3e2c..21386e28a9643 100644 --- a/library/core/src/iter/adapters/peekable.rs +++ b/library/core/src/iter/adapters/peekable.rs @@ -233,7 +233,6 @@ impl Peekable { /// Basic usage: /// /// ``` - /// #![feature(peekable_peek_mut)] /// let mut iter = [1, 2, 3].iter().peekable(); /// /// // Like with `peek()`, we can see into the future without advancing the iterator. @@ -251,7 +250,7 @@ impl Peekable { /// assert_eq!(iter.collect::>(), vec![&5, &3]); /// ``` #[inline] - #[unstable(feature = "peekable_peek_mut", issue = "78302")] + #[stable(feature = "peekable_peek_mut", since = "1.53.0")] pub fn peek_mut(&mut self) -> Option<&mut I::Item> { let iter = &mut self.iter; self.peeked.get_or_insert_with(|| iter.next()).as_mut() diff --git a/library/core/src/iter/adapters/take.rs b/library/core/src/iter/adapters/take.rs index 9efc7a480aeb4..54a47f1323ebf 100644 --- a/library/core/src/iter/adapters/take.rs +++ b/library/core/src/iter/adapters/take.rs @@ -1,5 +1,8 @@ use crate::cmp; -use crate::iter::{adapters::SourceIter, FusedIterator, InPlaceIterable, TrustedLen}; +use crate::iter::{ + adapters::zip::try_get_unchecked, adapters::SourceIter, FusedIterator, InPlaceIterable, + TrustedLen, TrustedRandomAccess, +}; use crate::ops::{ControlFlow, Try}; /// An iterator that only iterates over the first `n` iterations of `iter`. @@ -111,6 +114,15 @@ where self.try_fold(init, ok(fold)).unwrap() } + + unsafe fn __iterator_get_unchecked(&mut self, idx: usize) -> ::Item + where + Self: TrustedRandomAccess, + { + // SAFETY: the caller must uphold the contract for + // `Iterator::__iterator_get_unchecked`. + unsafe { try_get_unchecked(&mut self.iter, idx) } + } } #[unstable(issue = "none", feature = "inplace_iteration")] @@ -207,3 +219,12 @@ impl FusedIterator for Take where I: FusedIterator {} #[unstable(feature = "trusted_len", issue = "37572")] unsafe impl TrustedLen for Take {} + +#[doc(hidden)] +#[unstable(feature = "trusted_random_access", issue = "none")] +unsafe impl TrustedRandomAccess for Take +where + I: TrustedRandomAccess, +{ + const MAY_HAVE_SIDE_EFFECT: bool = I::MAY_HAVE_SIDE_EFFECT; +} diff --git a/library/core/src/iter/adapters/zip.rs b/library/core/src/iter/adapters/zip.rs index 9d0f4e3618fc5..2f8f504d8fcaa 100644 --- a/library/core/src/iter/adapters/zip.rs +++ b/library/core/src/iter/adapters/zip.rs @@ -5,17 +5,18 @@ use crate::iter::{InPlaceIterable, SourceIter, TrustedLen}; /// An iterator that iterates two other iterators simultaneously. /// -/// This `struct` is created by [`Iterator::zip`]. See its documentation -/// for more. +/// This `struct` is created by [`zip`] or [`Iterator::zip`]. +/// See their documentation for more. #[derive(Clone)] #[must_use = "iterators are lazy and do nothing unless consumed"] #[stable(feature = "rust1", since = "1.0.0")] pub struct Zip { a: A, b: B, - // index and len are only used by the specialized version of zip + // index, len and a_len are only used by the specialized version of zip index: usize, len: usize, + a_len: usize, } impl Zip { pub(in crate::iter) fn new(a: A, b: B) -> Zip { @@ -32,6 +33,37 @@ impl Zip { } } +/// Converts the arguments to iterators and zips them. +/// +/// See the documentation of [`Iterator::zip`] for more. +/// +/// # Examples +/// +/// ``` +/// #![feature(iter_zip)] +/// use std::iter::zip; +/// +/// let xs = [1, 2, 3]; +/// let ys = [4, 5, 6]; +/// for (x, y) in zip(&xs, &ys) { +/// println!("x:{}, y:{}", x, y); +/// } +/// +/// // Nested zips are also possible: +/// let zs = [7, 8, 9]; +/// for ((x, y), z) in zip(zip(&xs, &ys), &zs) { +/// println!("x:{}, y:{}, z:{}", x, y, z); +/// } +/// ``` +#[unstable(feature = "iter_zip", issue = "83574")] +pub fn zip(a: A, b: B) -> Zip +where + A: IntoIterator, + B: IntoIterator, +{ + ZipImpl::new(a.into_iter(), b.into_iter()) +} + #[stable(feature = "rust1", since = "1.0.0")] impl Iterator for Zip where @@ -110,6 +142,7 @@ where b, index: 0, // unused len: 0, // unused + a_len: 0, // unused } } @@ -184,8 +217,9 @@ where B: TrustedRandomAccess + Iterator, { fn new(a: A, b: B) -> Self { - let len = cmp::min(a.size(), b.size()); - Zip { a, b, index: 0, len } + let a_len = a.size(); + let len = cmp::min(a_len, b.size()); + Zip { a, b, index: 0, len, a_len } } #[inline] @@ -197,9 +231,10 @@ where unsafe { Some((self.a.__iterator_get_unchecked(i), self.b.__iterator_get_unchecked(i))) } - } else if A::MAY_HAVE_SIDE_EFFECT && self.index < self.a.size() { + } else if A::MAY_HAVE_SIDE_EFFECT && self.index < self.a_len { let i = self.index; self.index += 1; + self.len += 1; // match the base implementation's potential side effects // SAFETY: we just checked that `i` < `self.a.len()` unsafe { @@ -258,9 +293,10 @@ where if sz_a != sz_b { let sz_a = self.a.size(); if A::MAY_HAVE_SIDE_EFFECT && sz_a > self.len { - for _ in 0..sz_a - cmp::max(self.len, self.index) { + for _ in 0..sz_a - self.len { self.a.next_back(); } + self.a_len = self.len; } let sz_b = self.b.size(); if B::MAY_HAVE_SIDE_EFFECT && sz_b > self.len { @@ -272,6 +308,7 @@ where } if self.index < self.len { self.len -= 1; + self.a_len -= 1; let i = self.len; // SAFETY: `i` is smaller than the previous value of `self.len`, // which is also smaller than or equal to `self.a.len()` and `self.b.len()` diff --git a/library/core/src/iter/mod.rs b/library/core/src/iter/mod.rs index c57ba2bf62645..2a179f0b1d77b 100644 --- a/library/core/src/iter/mod.rs +++ b/library/core/src/iter/mod.rs @@ -389,6 +389,8 @@ pub use self::traits::{ DoubleEndedIterator, ExactSizeIterator, Extend, FromIterator, IntoIterator, Product, Sum, }; +#[unstable(feature = "iter_zip", issue = "83574")] +pub use self::adapters::zip; #[stable(feature = "iter_cloned", since = "1.1.0")] pub use self::adapters::Cloned; #[stable(feature = "iter_copied", since = "1.36.0")] diff --git a/library/core/src/iter/range.rs b/library/core/src/iter/range.rs index cc80e06decd48..4b293c596e7af 100644 --- a/library/core/src/iter/range.rs +++ b/library/core/src/iter/range.rs @@ -3,7 +3,7 @@ use crate::convert::TryFrom; use crate::mem; use crate::ops::{self, Try}; -use super::{FusedIterator, TrustedLen}; +use super::{FusedIterator, TrustedLen, TrustedRandomAccess}; /// Objects that have a notion of *successor* and *predecessor* operations. /// @@ -493,6 +493,18 @@ macro_rules! range_exact_iter_impl { )*) } +/// Safety: This macro must only be used on types that are `Copy` and result in ranges +/// which have an exact `size_hint()` where the upper bound must not be `None`. +macro_rules! unsafe_range_trusted_random_access_impl { + ($($t:ty)*) => ($( + #[doc(hidden)] + #[unstable(feature = "trusted_random_access", issue = "none")] + unsafe impl TrustedRandomAccess for ops::Range<$t> { + const MAY_HAVE_SIDE_EFFECT: bool = false; + } + )*) +} + macro_rules! range_incl_exact_iter_impl { ($($t:ty)*) => ($( #[stable(feature = "inclusive_range", since = "1.26.0")] @@ -553,6 +565,18 @@ impl Iterator for ops::Range { fn max(mut self) -> Option { self.next_back() } + + #[inline] + unsafe fn __iterator_get_unchecked(&mut self, idx: usize) -> Self::Item + where + Self: TrustedRandomAccess, + { + // SAFETY: The TrustedRandomAccess contract requires that callers only pass an index + // that is in bounds. + // Additionally Self: TrustedRandomAccess is only implemented for Copy types + // which means even repeated reads of the same index would be safe. + unsafe { Step::forward_unchecked(self.start.clone(), idx) } + } } // These macros generate `ExactSizeIterator` impls for various range types. @@ -574,6 +598,23 @@ range_exact_iter_impl! { u32 i32 } + +unsafe_range_trusted_random_access_impl! { + usize u8 u16 + isize i8 i16 +} + +#[cfg(target_pointer_width = "32")] +unsafe_range_trusted_random_access_impl! { + u32 i32 +} + +#[cfg(target_pointer_width = "64")] +unsafe_range_trusted_random_access_impl! { + u32 i32 + u64 i64 +} + range_incl_exact_iter_impl! { u8 i8 diff --git a/library/core/src/iter/sources/empty.rs b/library/core/src/iter/sources/empty.rs index 5d4a9fe8c6cc0..919c564f2872a 100644 --- a/library/core/src/iter/sources/empty.rs +++ b/library/core/src/iter/sources/empty.rs @@ -36,7 +36,7 @@ unsafe impl Sync for Empty {} #[stable(feature = "core_impl_debug", since = "1.9.0")] impl fmt::Debug for Empty { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.pad("Empty") + f.debug_struct("Empty").finish() } } diff --git a/library/core/src/iter/traits/iterator.rs b/library/core/src/iter/traits/iterator.rs index f28c4673cc033..7977d599ae725 100644 --- a/library/core/src/iter/traits/iterator.rs +++ b/library/core/src/iter/traits/iterator.rs @@ -81,8 +81,8 @@ fn _assert_is_object_safe(_: &dyn Iterator) {} ), on( _Self = "[]", - label = "borrow the array with `&` or call `.iter()` on it to iterate over it", - note = "arrays are not iterators, but slices like the following are: `&[1, 2, 3]`" + label = "arrays do not yet implement `IntoIterator`; try using `std::array::IntoIter::new(arr)`", + note = "see for more details" ), on( _Self = "{integral}", @@ -92,7 +92,9 @@ fn _assert_is_object_safe(_: &dyn Iterator) {} label = "`{Self}` is not an iterator", message = "`{Self}` is not an iterator" )] -#[doc(spotlight)] +#[cfg_attr(bootstrap, doc(spotlight))] +#[cfg_attr(not(bootstrap), doc(notable_trait))] +#[rustc_diagnostic_item = "Iterator"] #[must_use = "iterators are lazy and do nothing unless consumed"] pub trait Iterator { /// The type of the elements being iterated over. @@ -935,20 +937,16 @@ pub trait Iterator { Enumerate::new(self) } - /// Creates an iterator which can use [`peek`] to look at the next element of - /// the iterator without consuming it. + /// Creates an iterator which can use the [`peek`] and [`peek_mut`] methods + /// to look at the next element of the iterator without consuming it. See + /// their documentation for more information. /// - /// Adds a [`peek`] method to an iterator. See its documentation for - /// more information. + /// Note that the underlying iterator is still advanced when [`peek`] or + /// [`peek_mut`] are called for the first time: In order to retrieve the + /// next element, [`next`] is called on the underlying iterator, hence any + /// side effects (i.e. anything other than fetching the next value) of + /// the [`next`] method will occur. /// - /// Note that the underlying iterator is still advanced when [`peek`] is - /// called for the first time: In order to retrieve the next element, - /// [`next`] is called on the underlying iterator, hence any side effects (i.e. - /// anything other than fetching the next value) of the [`next`] method - /// will occur. - /// - /// [`peek`]: Peekable::peek - /// [`next`]: Iterator::next /// /// # Examples /// @@ -975,6 +973,32 @@ pub trait Iterator { /// assert_eq!(iter.peek(), None); /// assert_eq!(iter.next(), None); /// ``` + /// + /// Using [`peek_mut`] to mutate the next item without advancing the + /// iterator: + /// + /// ``` + /// let xs = [1, 2, 3]; + /// + /// let mut iter = xs.iter().peekable(); + /// + /// // `peek_mut()` lets us see into the future + /// assert_eq!(iter.peek_mut(), Some(&mut &1)); + /// assert_eq!(iter.peek_mut(), Some(&mut &1)); + /// assert_eq!(iter.next(), Some(&1)); + /// + /// if let Some(mut p) = iter.peek_mut() { + /// assert_eq!(*p, &2); + /// // put a value into the iterator + /// *p = &1000; + /// } + /// + /// // The value reappears as the iterator continues + /// assert_eq!(iter.collect::>(), vec![&1000, &3]); + /// ``` + /// [`peek`]: Peekable::peek + /// [`peek_mut`]: Peekable::peek_mut + /// [`next`]: Iterator::next #[inline] #[stable(feature = "rust1", since = "1.0.0")] fn peekable(self) -> Peekable @@ -1011,7 +1035,7 @@ pub trait Iterator { /// /// Because the closure passed to `skip_while()` takes a reference, and many /// iterators iterate over references, this leads to a possibly confusing - /// situation, where the type of the closure is a double reference: + /// situation, where the type of the closure argument is a double reference: /// /// ``` /// let a = [-1, 0, 1]; @@ -1227,7 +1251,11 @@ pub trait Iterator { /// Creates an iterator that skips the first `n` elements. /// - /// After they have been consumed, the rest of the elements are yielded. + /// `skip(n)` skips elements until `n` elements are skipped or the end of the + /// iterator is reached (whichever happens first). After that, all the remaining + /// elements are yielded. In particular, if the original iterator is too short, + /// then the returned iterator is empty. + /// /// Rather than overriding this method directly, instead override the `nth` method. /// /// # Examples @@ -1251,7 +1279,14 @@ pub trait Iterator { Skip::new(self, n) } - /// Creates an iterator that yields its first `n` elements. + /// Creates an iterator that yields the first `n` elements, or fewer + /// if the underlying iterator ends sooner. + /// + /// `take(n)` yields elements until `n` elements are yielded or the end of + /// the iterator is reached (whichever happens first). + /// The returned iterator is a prefix of length `n` if the original iterator + /// contains at least `n` elements, otherwise it contains all of the + /// (fewer than `n`) elements of the original iterator. /// /// # Examples /// @@ -1460,7 +1495,12 @@ pub trait Iterator { /// [`Some(T)`] again. `fuse()` adapts an iterator, ensuring that after a /// [`None`] is given, it will always return [`None`] forever. /// + /// Note that the [`Fuse`] wrapper is a no-op on iterators that implement + /// the [`FusedIterator`] trait. `fuse()` may therefore behave incorrectly + /// if the [`FusedIterator`] trait is improperly implemented. + /// /// [`Some(T)`]: Some + /// [`FusedIterator`]: crate::iter::FusedIterator /// /// # Examples /// @@ -1611,31 +1651,16 @@ pub trait Iterator { /// Basic usage: /// /// ``` - /// let a = [1, 2, 3]; - /// - /// let iter = a.iter(); - /// - /// let sum: i32 = iter.take(5).fold(0, |acc, i| acc + i); - /// - /// assert_eq!(sum, 6); + /// let mut words = vec!["hello", "world", "of", "Rust"].into_iter(); /// - /// // if we try to use iter again, it won't work. The following line - /// // gives "error: use of moved value: `iter` - /// // assert_eq!(iter.next(), None); + /// // Take the first two words. + /// let hello_world: Vec<_> = words.by_ref().take(2).collect(); + /// assert_eq!(hello_world, vec!["hello", "world"]); /// - /// // let's try that again - /// let a = [1, 2, 3]; - /// - /// let mut iter = a.iter(); - /// - /// // instead, we add in a .by_ref() - /// let sum: i32 = iter.by_ref().take(2).fold(0, |acc, i| acc + i); - /// - /// assert_eq!(sum, 3); - /// - /// // now this is just fine: - /// assert_eq!(iter.next(), Some(&3)); - /// assert_eq!(iter.next(), None); + /// // Collect the rest of the words. + /// // We can only do this because we used `by_ref` earlier. + /// let of_rust: Vec<_> = words.collect(); + /// assert_eq!(of_rust, vec!["of", "Rust"]); /// ``` #[stable(feature = "rust1", since = "1.0.0")] fn by_ref(&mut self) -> &mut Self @@ -2736,6 +2761,7 @@ pub trait Iterator { /// assert_eq!(iter.next(), None); /// ``` #[inline] + #[doc(alias = "reverse")] #[stable(feature = "rust1", since = "1.0.0")] fn rev(self) -> Rev where diff --git a/library/core/src/lib.rs b/library/core/src/lib.rs index 0492612372913..9f8a6f3d3b54f 100644 --- a/library/core/src/lib.rs +++ b/library/core/src/lib.rs @@ -79,7 +79,6 @@ #![feature(const_int_unchecked_arith)] #![feature(const_mut_refs)] #![feature(const_refs_to_cell)] -#![feature(const_cttz)] #![feature(const_panic)] #![feature(const_pin)] #![feature(const_fn)] @@ -98,6 +97,7 @@ #![feature(const_slice_from_raw_parts)] #![feature(const_slice_ptr_len)] #![feature(const_size_of_val)] +#![feature(const_swap)] #![feature(const_align_of_val)] #![feature(const_type_id)] #![feature(const_type_name)] @@ -108,9 +108,9 @@ #![feature(custom_inner_attributes)] #![feature(decl_macro)] #![feature(doc_cfg)] -#![feature(doc_spotlight)] +#![cfg_attr(bootstrap, feature(doc_spotlight))] +#![cfg_attr(not(bootstrap), feature(doc_notable_trait))] #![feature(duration_consts_2)] -#![feature(duration_saturating_ops)] #![feature(extended_key_value_attributes)] #![feature(extern_types)] #![feature(fundamental)] @@ -125,9 +125,9 @@ #![feature(exhaustive_patterns)] #![feature(no_core)] #![feature(auto_traits)] -#![feature(or_patterns)] +#![cfg_attr(bootstrap, feature(or_patterns))] #![feature(prelude_import)] -#![cfg_attr(not(bootstrap), feature(ptr_metadata))] +#![feature(ptr_metadata)] #![feature(repr_simd, platform_intrinsics)] #![feature(rustc_attrs)] #![feature(simd_ffi)] @@ -137,6 +137,7 @@ #![feature(stmt_expr_attributes)] #![feature(str_split_as_str)] #![feature(str_split_inclusive_as_str)] +#![feature(char_indices_offset)] #![feature(trait_alias)] #![feature(transparent_unions)] #![feature(try_blocks)] @@ -164,7 +165,6 @@ #![feature(const_caller_location)] #![feature(slice_ptr_get)] #![feature(no_niche)] // rust-lang/rust#68303 -#![feature(unsafe_block_in_unsafe_fn)] #![feature(int_error_matching)] #![deny(unsafe_op_in_unsafe_fn)] @@ -297,8 +297,8 @@ pub mod primitive; unused_imports, unsafe_op_in_unsafe_fn )] -#[cfg_attr(bootstrap, allow(non_autolinks))] -#[cfg_attr(not(bootstrap), allow(rustdoc::non_autolinks))] +#[cfg_attr(bootstrap, allow(rustdoc::non_autolinks))] +#[cfg_attr(not(bootstrap), allow(rustdoc::bare_urls))] // FIXME: This annotation should be moved into rust-lang/stdarch after clashing_extern_declarations is // merged. It currently cannot because bootstrap fails as the lint hasn't been defined yet. #[allow(clashing_extern_declarations)] diff --git a/library/core/src/macros/mod.rs b/library/core/src/macros/mod.rs index 9a54921f07b49..5d9b0f80d3a6f 100644 --- a/library/core/src/macros/mod.rs +++ b/library/core/src/macros/mod.rs @@ -110,6 +110,60 @@ macro_rules! assert_ne { }); } +/// Asserts that an expression matches any of the given patterns. +/// +/// Like in a `match` expression, the pattern can be optionally followed by `if` +/// and a guard expression that has access to names bound by the pattern. +/// +/// On panic, this macro will print the value of the expression with its +/// debug representation. +/// +/// Like [`assert!`], this macro has a second form, where a custom +/// panic message can be provided. +/// +/// # Examples +/// +/// ``` +/// #![feature(assert_matches)] +/// +/// let a = 1u32.checked_add(2); +/// let b = 1u32.checked_sub(2); +/// assert_matches!(a, Some(_)); +/// assert_matches!(b, None); +/// +/// let c = Ok("abc".to_string()); +/// assert_matches!(c, Ok(x) | Err(x) if x.len() < 100); +/// ``` +#[macro_export] +#[unstable(feature = "assert_matches", issue = "82775")] +#[allow_internal_unstable(core_panic)] +macro_rules! assert_matches { + ($left:expr, $( $pattern:pat )|+ $( if $guard: expr )? $(,)?) => ({ + match $left { + $( $pattern )|+ $( if $guard )? => {} + ref left_val => { + $crate::panicking::assert_matches_failed( + left_val, + $crate::stringify!($($pattern)|+ $(if $guard)?), + $crate::option::Option::None + ); + } + } + }); + ($left:expr, $( $pattern:pat )|+ $( if $guard: expr )?, $($arg:tt)+) => ({ + match $left { + $( $pattern )|+ $( if $guard )? => {} + ref left_val => { + $crate::panicking::assert_matches_failed( + left_val, + $crate::stringify!($($pattern)|+ $(if $guard)?), + $crate::option::Option::Some($crate::format_args!($($arg)+)) + ); + } + } + }); +} + /// Asserts that a boolean expression is `true` at runtime. /// /// This will invoke the [`panic!`] macro if the provided expression cannot be @@ -208,6 +262,42 @@ macro_rules! debug_assert_ne { ($($arg:tt)*) => (if $crate::cfg!(debug_assertions) { $crate::assert_ne!($($arg)*); }) } +/// Asserts that an expression matches any of the given patterns. +/// +/// Like in a `match` expression, the pattern can be optionally followed by `if` +/// and a guard expression that has access to names bound by the pattern. +/// +/// On panic, this macro will print the value of the expression with its +/// debug representation. +/// +/// Unlike [`assert_matches!`], `debug_assert_matches!` statements are only +/// enabled in non optimized builds by default. An optimized build will not +/// execute `debug_assert_matches!` statements unless `-C debug-assertions` is +/// passed to the compiler. This makes `debug_assert_matches!` useful for +/// checks that are too expensive to be present in a release build but may be +/// helpful during development. The result of expanding `debug_assert_matches!` +/// is always type checked. +/// +/// # Examples +/// +/// ``` +/// #![feature(assert_matches)] +/// +/// let a = 1u32.checked_add(2); +/// let b = 1u32.checked_sub(2); +/// debug_assert_matches!(a, Some(_)); +/// debug_assert_matches!(b, None); +/// +/// let c = Ok("abc".to_string()); +/// debug_assert_matches!(c, Ok(x) | Err(x) if x.len() < 100); +/// ``` +#[macro_export] +#[unstable(feature = "assert_matches", issue = "82775")] +#[allow_internal_unstable(assert_matches)] +macro_rules! debug_assert_matches { + ($($arg:tt)*) => (if $crate::cfg!(debug_assertions) { $crate::assert_matches!($($arg)*); }) +} + /// Returns whether the given expression matches any of the given patterns. /// /// Like in a `match` expression, the pattern can be optionally followed by `if` @@ -1301,7 +1391,6 @@ pub(crate) mod builtin { } /// Attribute macro used to apply derive macros. - #[cfg(not(bootstrap))] #[stable(feature = "rust1", since = "1.0.0")] #[rustc_builtin_macro] pub macro derive($item:item) { @@ -1362,10 +1451,25 @@ pub(crate) mod builtin { /* compiler built-in */ } + /// Expands all `#[cfg]` and `#[cfg_attr]` attributes in the code fragment it's applied to. + #[unstable( + feature = "cfg_eval", + issue = "82679", + reason = "`cfg_eval` is a recently implemented feature" + )] + #[rustc_builtin_macro] + pub macro cfg_eval($($tt:tt)*) { + /* compiler built-in */ + } + /// Unstable implementation detail of the `rustc` compiler, do not use. #[rustc_builtin_macro] #[stable(feature = "rust1", since = "1.0.0")] #[allow_internal_unstable(core_intrinsics, libstd_sys_internals)] + #[rustc_deprecated( + since = "1.52.0", + reason = "rustc-serialize is deprecated and no longer supported" + )] pub macro RustcDecodable($item:item) { /* compiler built-in */ } @@ -1374,6 +1478,10 @@ pub(crate) mod builtin { #[rustc_builtin_macro] #[stable(feature = "rust1", since = "1.0.0")] #[allow_internal_unstable(core_intrinsics)] + #[rustc_deprecated( + since = "1.52.0", + reason = "rustc-serialize is deprecated and no longer supported" + )] pub macro RustcEncodable($item:item) { /* compiler built-in */ } diff --git a/library/core/src/macros/panic.md b/library/core/src/macros/panic.md index 6e502426df906..5127a16bbfd86 100644 --- a/library/core/src/macros/panic.md +++ b/library/core/src/macros/panic.md @@ -9,11 +9,15 @@ tests. `panic!` is closely tied with the `unwrap` method of both [`Option`][ounwrap] and [`Result`][runwrap] enums. Both implementations call `panic!` when they are set to [`None`] or [`Err`] variants. -This macro is used to inject panic into a Rust thread, causing the thread to -panic entirely. This macro panics with a string and uses the [`format!`] syntax -for building the message. - -Each thread's panic can be reaped as the [`Box`]`<`[`Any`]`>` type, +When using `panic!()` you can specify a string payload, that is built using +the [`format!`] syntax. That payload is used when injecting the panic into +the calling Rust thread, causing the thread to panic entirely. + +The behavior of the default `std` hook, i.e. the code that runs directly +after the panic is invoked, is to print the message payload to +`stderr` along with the file/line/column information of the `panic!()` +call. You can override the panic hook using [`std::panic::set_hook()`]. +Inside the hook a panic can be accessed as a `&dyn Any + Send`, which contains either a `&str` or `String` for regular `panic!()` invocations. To panic with a value of another other type, [`panic_any`] can be used. @@ -26,6 +30,7 @@ See also the macro [`compile_error!`], for raising errors during compilation. [ounwrap]: Option::unwrap [runwrap]: Result::unwrap +[`std::panic::set_hook()`]: ../std/panic/fn.set_hook.html [`panic_any`]: ../std/panic/fn.panic_any.html [`Box`]: ../std/boxed/struct.Box.html [`Any`]: crate::any::Any diff --git a/library/core/src/marker.rs b/library/core/src/marker.rs index 85e0e7200875d..fb957348bebd3 100644 --- a/library/core/src/marker.rs +++ b/library/core/src/marker.rs @@ -765,7 +765,7 @@ unsafe impl Freeze for &mut T {} /// [`pin` module]: crate::pin #[stable(feature = "pin", since = "1.33.0")] #[rustc_on_unimplemented( - on(_Self = "std::future::Future", note = "consider using `Box::pin`",), + note = "consider using `Box::pin`", message = "`{Self}` cannot be unpinned" )] #[lang = "unpin"] diff --git a/library/core/src/mem/maybe_uninit.rs b/library/core/src/mem/maybe_uninit.rs index 26314213ff730..4d7d47579ee40 100644 --- a/library/core/src/mem/maybe_uninit.rs +++ b/library/core/src/mem/maybe_uninit.rs @@ -39,10 +39,11 @@ use crate::ptr; /// let b: bool = unsafe { MaybeUninit::uninit().assume_init() }; // undefined behavior! ⚠️ /// ``` /// -/// Moreover, uninitialized memory is special in that the compiler knows that -/// it does not have a fixed value. This makes it undefined behavior to have -/// uninitialized data in a variable even if that variable has an integer type, -/// which otherwise can hold any *fixed* bit pattern: +/// Moreover, uninitialized memory is special in that it does not have a fixed value ("fixed" +/// meaning "it won't change without being written to"). Reading the same uninitialized byte +/// multiple times can give different results. This makes it undefined behavior to have +/// uninitialized data in a variable even if that variable has an integer type, which otherwise can +/// hold any *fixed* bit pattern: /// /// ```rust,no_run /// # #![allow(invalid_value)] @@ -189,6 +190,8 @@ use crate::ptr; /// let ptr = uninit.as_mut_ptr(); /// /// // Initializing the `name` field +/// // Using `write` instead of assignment via `=` to not call `drop` on the +/// // old, uninitialized value. /// unsafe { addr_of_mut!((*ptr).name).write("Bob".to_string()); } /// /// // Initializing the `list` field @@ -318,9 +321,9 @@ impl MaybeUninit { /// Create a new array of `MaybeUninit` items, in an uninitialized state. /// /// Note: in a future Rust version this method may become unnecessary - /// when array literal syntax allows - /// [repeating const expressions](https://github.com/rust-lang/rust/issues/49147). - /// The example below could then use `let mut buf = [MaybeUninit::::uninit(); 32];`. + /// when Rust allows + /// [inline const expressions](https://github.com/rust-lang/rust/issues/76001). + /// The example below could then use `let mut buf = [const { MaybeUninit::::uninit() }; 32];`. /// /// # Examples /// @@ -733,22 +736,22 @@ impl MaybeUninit { /// #![feature(maybe_uninit_ref)] /// use std::mem::MaybeUninit; /// - /// # unsafe extern "C" fn initialize_buffer(buf: *mut [u8; 2048]) { *buf = [0; 2048] } + /// # unsafe extern "C" fn initialize_buffer(buf: *mut [u8; 1024]) { *buf = [0; 1024] } /// # #[cfg(FALSE)] /// extern "C" { /// /// Initializes *all* the bytes of the input buffer. - /// fn initialize_buffer(buf: *mut [u8; 2048]); + /// fn initialize_buffer(buf: *mut [u8; 1024]); /// } /// - /// let mut buf = MaybeUninit::<[u8; 2048]>::uninit(); + /// let mut buf = MaybeUninit::<[u8; 1024]>::uninit(); /// /// // Initialize `buf`: /// unsafe { initialize_buffer(buf.as_mut_ptr()); } /// // Now we know that `buf` has been initialized, so we could `.assume_init()` it. - /// // However, using `.assume_init()` may trigger a `memcpy` of the 2048 bytes. + /// // However, using `.assume_init()` may trigger a `memcpy` of the 1024 bytes. /// // To assert our buffer has been initialized without copying it, we upgrade - /// // the `&mut MaybeUninit<[u8; 2048]>` to a `&mut [u8; 2048]`: - /// let buf: &mut [u8; 2048] = unsafe { + /// // the `&mut MaybeUninit<[u8; 1024]>` to a `&mut [u8; 1024]`: + /// let buf: &mut [u8; 1024] = unsafe { /// // SAFETY: `buf` has been initialized. /// buf.assume_init_mut() /// }; diff --git a/library/core/src/mem/mod.rs b/library/core/src/mem/mod.rs index afce6e55b8f6a..5bf47c3951da2 100644 --- a/library/core/src/mem/mod.rs +++ b/library/core/src/mem/mod.rs @@ -297,7 +297,7 @@ pub fn forget_unsized(t: T) { #[inline(always)] #[stable(feature = "rust1", since = "1.0.0")] #[rustc_promotable] -#[rustc_const_stable(feature = "const_size_of", since = "1.32.0")] +#[rustc_const_stable(feature = "const_size_of", since = "1.24.0")] pub const fn size_of() -> usize { intrinsics::size_of::() } @@ -440,7 +440,7 @@ pub fn min_align_of_val(val: &T) -> usize { #[inline(always)] #[stable(feature = "rust1", since = "1.0.0")] #[rustc_promotable] -#[rustc_const_stable(feature = "const_align_of", since = "1.32.0")] +#[rustc_const_stable(feature = "const_align_of", since = "1.24.0")] pub const fn align_of() -> usize { intrinsics::min_align_of::() } @@ -682,7 +682,8 @@ pub unsafe fn uninitialized() -> T { /// ``` #[inline] #[stable(feature = "rust1", since = "1.0.0")] -pub fn swap(x: &mut T, y: &mut T) { +#[rustc_const_unstable(feature = "const_swap", issue = "83163")] +pub const fn swap(x: &mut T, y: &mut T) { // SAFETY: the raw pointers have been created from safe mutable references satisfying all the // constraints on `ptr::swap_nonoverlapping_one` unsafe { @@ -812,9 +813,16 @@ pub fn take(dest: &mut T) -> T { #[inline] #[stable(feature = "rust1", since = "1.0.0")] #[must_use = "if you don't need the old value, you can just assign the new value directly"] -pub fn replace(dest: &mut T, mut src: T) -> T { - swap(dest, &mut src); - src +#[rustc_const_unstable(feature = "const_replace", issue = "83164")] +pub const fn replace(dest: &mut T, src: T) -> T { + // SAFETY: We read from `dest` but directly write `src` into it afterwards, + // such that the old value is not duplicated. Nothing is dropped and + // nothing here can panic. + unsafe { + let result = ptr::read(dest); + ptr::write(dest, src); + result + } } /// Disposes of a value. @@ -878,7 +886,6 @@ pub fn replace(dest: &mut T, mut src: T) -> T { /// ``` /// /// [`RefCell`]: crate::cell::RefCell -#[doc(alias = "delete")] #[inline] #[stable(feature = "rust1", since = "1.0.0")] pub fn drop(_x: T) {} @@ -925,7 +932,8 @@ pub fn drop(_x: T) {} /// ``` #[inline] #[stable(feature = "rust1", since = "1.0.0")] -pub unsafe fn transmute_copy(src: &T) -> U { +#[rustc_const_unstable(feature = "const_transmute_copy", issue = "83165")] +pub const unsafe fn transmute_copy(src: &T) -> U { // If U has a higher alignment requirement, src may not be suitably aligned. if align_of::() > align_of::() { // SAFETY: `src` is a reference which is guaranteed to be valid for reads. diff --git a/library/core/src/num/bignum.rs b/library/core/src/num/bignum.rs index 6a1a1e1976160..197b85fba1fd7 100644 --- a/library/core/src/num/bignum.rs +++ b/library/core/src/num/bignum.rs @@ -181,11 +181,12 @@ macro_rules! define_bignum { /// Adds `other` to itself and returns its own mutable reference. pub fn add<'a>(&'a mut self, other: &$name) -> &'a mut $name { use crate::cmp; + use crate::iter; use crate::num::bignum::FullOps; let mut sz = cmp::max(self.size, other.size); let mut carry = false; - for (a, b) in self.base[..sz].iter_mut().zip(&other.base[..sz]) { + for (a, b) in iter::zip(&mut self.base[..sz], &other.base[..sz]) { let (c, v) = (*a).full_add(*b, carry); *a = v; carry = c; @@ -219,11 +220,12 @@ macro_rules! define_bignum { /// Subtracts `other` from itself and returns its own mutable reference. pub fn sub<'a>(&'a mut self, other: &$name) -> &'a mut $name { use crate::cmp; + use crate::iter; use crate::num::bignum::FullOps; let sz = cmp::max(self.size, other.size); let mut noborrow = true; - for (a, b) in self.base[..sz].iter_mut().zip(&other.base[..sz]) { + for (a, b) in iter::zip(&mut self.base[..sz], &other.base[..sz]) { let (c, v) = (*a).full_add(!*b, noborrow); *a = v; noborrow = c; diff --git a/library/core/src/num/dec2flt/algorithm.rs b/library/core/src/num/dec2flt/algorithm.rs index a5fbdc6ee2d48..313b6870ac9d3 100644 --- a/library/core/src/num/dec2flt/algorithm.rs +++ b/library/core/src/num/dec2flt/algorithm.rs @@ -62,10 +62,9 @@ mod fpu_precision { // any `u16` unsafe { asm!( - "fldcw ({})", + "fldcw word ptr [{}]", in(reg) &cw, - // FIXME: We are using ATT syntax to support LLVM 8 and LLVM 9. - options(att_syntax, nostack), + options(nostack), ) } } @@ -87,10 +86,9 @@ mod fpu_precision { // any `u16` unsafe { asm!( - "fnstcw ({})", + "fnstcw word ptr [{}]", in(reg) &mut cw, - // FIXME: We are using ATT syntax to support LLVM 8 and LLVM 9. - options(att_syntax, nostack), + options(nostack), ) } diff --git a/library/core/src/num/dec2flt/mod.rs b/library/core/src/num/dec2flt/mod.rs index 20ac165c6c798..f008a64ffe653 100644 --- a/library/core/src/num/dec2flt/mod.rs +++ b/library/core/src/num/dec2flt/mod.rs @@ -239,13 +239,15 @@ fn dec2flt(s: &str) -> Result { ParseResult::Valid(decimal) => convert(decimal)?, ParseResult::ShortcutToInf => T::INFINITY, ParseResult::ShortcutToZero => T::ZERO, - ParseResult::Invalid => match s { - "inf" => T::INFINITY, - "NaN" => T::NAN, - _ => { + ParseResult::Invalid => { + if s.eq_ignore_ascii_case("nan") { + T::NAN + } else if s.eq_ignore_ascii_case("inf") || s.eq_ignore_ascii_case("infinity") { + T::INFINITY + } else { return Err(pfe_invalid()); } - }, + } }; match sign { diff --git a/library/core/src/num/f32.rs b/library/core/src/num/f32.rs index 690247bc96df9..0d6d919d9984d 100644 --- a/library/core/src/num/f32.rs +++ b/library/core/src/num/f32.rs @@ -500,7 +500,6 @@ impl f32 { /// Returns `true` if the number is [subnormal]. /// /// ``` - /// #![feature(is_subnormal)] /// let min = f32::MIN_POSITIVE; // 1.17549435e-38f32 /// let max = f32::MAX; /// let lower_than_min = 1.0e-40_f32; @@ -516,7 +515,7 @@ impl f32 { /// assert!(lower_than_min.is_subnormal()); /// ``` /// [subnormal]: https://en.wikipedia.org/wiki/Denormal_number - #[unstable(feature = "is_subnormal", issue = "79288")] + #[stable(feature = "is_subnormal", since = "1.53.0")] #[rustc_const_unstable(feature = "const_float_classify", issue = "72505")] #[inline] pub const fn is_subnormal(self) -> bool { diff --git a/library/core/src/num/f64.rs b/library/core/src/num/f64.rs index 6d3737224c0b6..42214e7b50de0 100644 --- a/library/core/src/num/f64.rs +++ b/library/core/src/num/f64.rs @@ -499,7 +499,6 @@ impl f64 { /// Returns `true` if the number is [subnormal]. /// /// ``` - /// #![feature(is_subnormal)] /// let min = f64::MIN_POSITIVE; // 2.2250738585072014e-308_f64 /// let max = f64::MAX; /// let lower_than_min = 1.0e-308_f64; @@ -515,7 +514,7 @@ impl f64 { /// assert!(lower_than_min.is_subnormal()); /// ``` /// [subnormal]: https://en.wikipedia.org/wiki/Denormal_number - #[unstable(feature = "is_subnormal", issue = "79288")] + #[stable(feature = "is_subnormal", since = "1.53.0")] #[rustc_const_unstable(feature = "const_float_classify", issue = "72505")] #[inline] pub const fn is_subnormal(self) -> bool { diff --git a/library/core/src/num/flt2dec/mod.rs b/library/core/src/num/flt2dec/mod.rs index e8f9d6574e2d0..93bdf5040e08b 100644 --- a/library/core/src/num/flt2dec/mod.rs +++ b/library/core/src/num/flt2dec/mod.rs @@ -399,14 +399,10 @@ fn digits_to_exp_str<'a>( /// Sign formatting options. #[derive(Copy, Clone, PartialEq, Eq, Debug)] pub enum Sign { - /// Prints `-` only for the negative non-zero values. - Minus, // -inf -1 0 0 1 inf nan - /// Prints `-` only for any negative values (including the negative zero). - MinusRaw, // -inf -1 -0 0 1 inf nan - /// Prints `-` for the negative non-zero values, or `+` otherwise. - MinusPlus, // -inf -1 +0 +0 +1 +inf nan - /// Prints `-` for any negative values (including the negative zero), or `+` otherwise. - MinusPlusRaw, // -inf -1 -0 +0 +1 +inf nan + /// Prints `-` for any negative value. + Minus, // -inf -1 -0 0 1 inf nan + /// Prints `-` for any negative value, or `+` otherwise. + MinusPlus, // -inf -1 -0 +0 +1 +inf nan } /// Returns the static byte string corresponding to the sign to be formatted. @@ -414,30 +410,14 @@ pub enum Sign { fn determine_sign(sign: Sign, decoded: &FullDecoded, negative: bool) -> &'static str { match (*decoded, sign) { (FullDecoded::Nan, _) => "", - (FullDecoded::Zero, Sign::Minus) => "", - (FullDecoded::Zero, Sign::MinusRaw) => { + (_, Sign::Minus) => { if negative { "-" } else { "" } } - (FullDecoded::Zero, Sign::MinusPlus) => "+", - (FullDecoded::Zero, Sign::MinusPlusRaw) => { - if negative { - "-" - } else { - "+" - } - } - (_, Sign::Minus | Sign::MinusRaw) => { - if negative { - "-" - } else { - "" - } - } - (_, Sign::MinusPlus | Sign::MinusPlusRaw) => { + (_, Sign::MinusPlus) => { if negative { "-" } else { diff --git a/library/core/src/num/int_macros.rs b/library/core/src/num/int_macros.rs index ce9dca39d0ea4..608deb49a9dbf 100644 --- a/library/core/src/num/int_macros.rs +++ b/library/core/src/num/int_macros.rs @@ -32,10 +32,9 @@ macro_rules! int_impl { /// # Examples /// /// ``` - /// #![feature(int_bits_const)] #[doc = concat!("assert_eq!(", stringify!($SelfT), "::BITS, ", stringify!($BITS), ");")] /// ``` - #[unstable(feature = "int_bits_const", issue = "76904")] + #[stable(feature = "int_bits_const", since = "1.53.0")] pub const BITS: u32 = $BITS; /// Converts a string slice in a given base to an integer. @@ -80,7 +79,7 @@ macro_rules! int_impl { #[rustc_const_stable(feature = "const_int_methods", since = "1.32.0")] #[doc(alias = "popcount")] #[doc(alias = "popcnt")] - #[inline] + #[inline(always)] pub const fn count_ones(self) -> u32 { (self as $UnsignedT).count_ones() } /// Returns the number of zeros in the binary representation of `self`. @@ -94,7 +93,7 @@ macro_rules! int_impl { /// ``` #[stable(feature = "rust1", since = "1.0.0")] #[rustc_const_stable(feature = "const_int_methods", since = "1.32.0")] - #[inline] + #[inline(always)] pub const fn count_zeros(self) -> u32 { (!self).count_ones() } @@ -112,7 +111,7 @@ macro_rules! int_impl { /// ``` #[stable(feature = "rust1", since = "1.0.0")] #[rustc_const_stable(feature = "const_int_methods", since = "1.32.0")] - #[inline] + #[inline(always)] pub const fn leading_zeros(self) -> u32 { (self as $UnsignedT).leading_zeros() } @@ -130,7 +129,7 @@ macro_rules! int_impl { /// ``` #[stable(feature = "rust1", since = "1.0.0")] #[rustc_const_stable(feature = "const_int_methods", since = "1.32.0")] - #[inline] + #[inline(always)] pub const fn trailing_zeros(self) -> u32 { (self as $UnsignedT).trailing_zeros() } @@ -148,7 +147,7 @@ macro_rules! int_impl { /// ``` #[stable(feature = "leading_trailing_ones", since = "1.46.0")] #[rustc_const_stable(feature = "leading_trailing_ones", since = "1.46.0")] - #[inline] + #[inline(always)] pub const fn leading_ones(self) -> u32 { (self as $UnsignedT).leading_ones() } @@ -166,7 +165,7 @@ macro_rules! int_impl { /// ``` #[stable(feature = "leading_trailing_ones", since = "1.46.0")] #[rustc_const_stable(feature = "leading_trailing_ones", since = "1.46.0")] - #[inline] + #[inline(always)] pub const fn trailing_ones(self) -> u32 { (self as $UnsignedT).trailing_ones() } @@ -190,7 +189,7 @@ macro_rules! int_impl { #[rustc_const_stable(feature = "const_int_methods", since = "1.32.0")] #[must_use = "this returns the result of the operation, \ without modifying the original"] - #[inline] + #[inline(always)] pub const fn rotate_left(self, n: u32) -> Self { (self as $UnsignedT).rotate_left(n) as Self } @@ -215,7 +214,7 @@ macro_rules! int_impl { #[rustc_const_stable(feature = "const_int_methods", since = "1.32.0")] #[must_use = "this returns the result of the operation, \ without modifying the original"] - #[inline] + #[inline(always)] pub const fn rotate_right(self, n: u32) -> Self { (self as $UnsignedT).rotate_right(n) as Self } @@ -235,7 +234,7 @@ macro_rules! int_impl { /// ``` #[stable(feature = "rust1", since = "1.0.0")] #[rustc_const_stable(feature = "const_int_methods", since = "1.32.0")] - #[inline] + #[inline(always)] pub const fn swap_bytes(self) -> Self { (self as $UnsignedT).swap_bytes() as Self } @@ -256,7 +255,7 @@ macro_rules! int_impl { /// ``` #[stable(feature = "reverse_bits", since = "1.37.0")] #[rustc_const_stable(feature = "const_int_methods", since = "1.32.0")] - #[inline] + #[inline(always)] #[must_use] pub const fn reverse_bits(self) -> Self { (self as $UnsignedT).reverse_bits() as Self @@ -417,7 +416,7 @@ macro_rules! int_impl { )] #[must_use = "this returns the result of the operation, \ without modifying the original"] - #[inline] + #[inline(always)] pub unsafe fn unchecked_add(self, rhs: Self) -> Self { // SAFETY: the caller must uphold the safety contract for // `unchecked_add`. @@ -455,7 +454,7 @@ macro_rules! int_impl { )] #[must_use = "this returns the result of the operation, \ without modifying the original"] - #[inline] + #[inline(always)] pub unsafe fn unchecked_sub(self, rhs: Self) -> Self { // SAFETY: the caller must uphold the safety contract for // `unchecked_sub`. @@ -493,7 +492,7 @@ macro_rules! int_impl { )] #[must_use = "this returns the result of the operation, \ without modifying the original"] - #[inline] + #[inline(always)] pub unsafe fn unchecked_mul(self, rhs: Self) -> Self { // SAFETY: the caller must uphold the safety contract for // `unchecked_mul`. @@ -742,7 +741,7 @@ macro_rules! int_impl { #[rustc_const_stable(feature = "const_saturating_int_methods", since = "1.47.0")] #[must_use = "this returns the result of the operation, \ without modifying the original"] - #[inline] + #[inline(always)] pub const fn saturating_add(self, rhs: Self) -> Self { intrinsics::saturating_add(self, rhs) } @@ -763,7 +762,7 @@ macro_rules! int_impl { #[rustc_const_stable(feature = "const_saturating_int_methods", since = "1.47.0")] #[must_use = "this returns the result of the operation, \ without modifying the original"] - #[inline] + #[inline(always)] pub const fn saturating_sub(self, rhs: Self) -> Self { intrinsics::saturating_sub(self, rhs) } @@ -784,7 +783,7 @@ macro_rules! int_impl { #[stable(feature = "saturating_neg", since = "1.45.0")] #[rustc_const_stable(feature = "const_saturating_int_methods", since = "1.47.0")] - #[inline] + #[inline(always)] pub const fn saturating_neg(self) -> Self { intrinsics::saturating_sub(0, self) } @@ -884,7 +883,7 @@ macro_rules! int_impl { #[rustc_const_stable(feature = "const_int_methods", since = "1.32.0")] #[must_use = "this returns the result of the operation, \ without modifying the original"] - #[inline] + #[inline(always)] pub const fn wrapping_add(self, rhs: Self) -> Self { intrinsics::wrapping_add(self, rhs) } @@ -904,7 +903,7 @@ macro_rules! int_impl { #[rustc_const_stable(feature = "const_int_methods", since = "1.32.0")] #[must_use = "this returns the result of the operation, \ without modifying the original"] - #[inline] + #[inline(always)] pub const fn wrapping_sub(self, rhs: Self) -> Self { intrinsics::wrapping_sub(self, rhs) } @@ -924,7 +923,7 @@ macro_rules! int_impl { #[rustc_const_stable(feature = "const_int_methods", since = "1.32.0")] #[must_use = "this returns the result of the operation, \ without modifying the original"] - #[inline] + #[inline(always)] pub const fn wrapping_mul(self, rhs: Self) -> Self { intrinsics::wrapping_mul(self, rhs) } @@ -1082,7 +1081,7 @@ macro_rules! int_impl { #[rustc_const_stable(feature = "const_int_methods", since = "1.32.0")] #[must_use = "this returns the result of the operation, \ without modifying the original"] - #[inline] + #[inline(always)] pub const fn wrapping_shl(self, rhs: u32) -> Self { // SAFETY: the masking by the bitsize of the type ensures that we do not shift // out of bounds @@ -1111,7 +1110,7 @@ macro_rules! int_impl { #[rustc_const_stable(feature = "const_int_methods", since = "1.32.0")] #[must_use = "this returns the result of the operation, \ without modifying the original"] - #[inline] + #[inline(always)] pub const fn wrapping_shr(self, rhs: u32) -> Self { // SAFETY: the masking by the bitsize of the type ensures that we do not shift // out of bounds @@ -1226,7 +1225,7 @@ macro_rules! int_impl { #[rustc_const_stable(feature = "const_int_methods", since = "1.32.0")] #[must_use = "this returns the result of the operation, \ without modifying the original"] - #[inline] + #[inline(always)] pub const fn overflowing_add(self, rhs: Self) -> (Self, bool) { let (a, b) = intrinsics::add_with_overflow(self as $ActualT, rhs as $ActualT); (a as Self, b) @@ -1250,7 +1249,7 @@ macro_rules! int_impl { #[rustc_const_stable(feature = "const_int_methods", since = "1.32.0")] #[must_use = "this returns the result of the operation, \ without modifying the original"] - #[inline] + #[inline(always)] pub const fn overflowing_sub(self, rhs: Self) -> (Self, bool) { let (a, b) = intrinsics::sub_with_overflow(self as $ActualT, rhs as $ActualT); (a as Self, b) @@ -1273,7 +1272,7 @@ macro_rules! int_impl { #[rustc_const_stable(feature = "const_int_methods", since = "1.32.0")] #[must_use = "this returns the result of the operation, \ without modifying the original"] - #[inline] + #[inline(always)] pub const fn overflowing_mul(self, rhs: Self) -> (Self, bool) { let (a, b) = intrinsics::mul_with_overflow(self as $ActualT, rhs as $ActualT); (a as Self, b) @@ -1589,11 +1588,11 @@ macro_rules! int_impl { /// Calculates the quotient of Euclidean division of `self` by `rhs`. /// - /// This computes the integer `n` such that `self = n * rhs + self.rem_euclid(rhs)`, - /// with `0 <= self.rem_euclid(rhs) < rhs`. + /// This computes the integer `q` such that `self = q * rhs + r`, with + /// `r = self.rem_euclid(rhs)` and `0 <= r < abs(rhs)`. /// - /// In other words, the result is `self / rhs` rounded to the integer `n` - /// such that `self >= n * rhs`. + /// In other words, the result is `self / rhs` rounded to the integer `q` + /// such that `self >= q * rhs`. /// If `self > 0`, this is equal to round towards zero (the default in Rust); /// if `self < 0`, this is equal to round towards +/- infinity. /// @@ -1726,7 +1725,7 @@ macro_rules! int_impl { /// ``` #[stable(feature = "rust1", since = "1.0.0")] #[rustc_const_stable(feature = "const_int_sign", since = "1.47.0")] - #[inline] + #[inline(always)] pub const fn signum(self) -> Self { match self { n if n > 0 => 1, @@ -1748,7 +1747,7 @@ macro_rules! int_impl { /// ``` #[stable(feature = "rust1", since = "1.0.0")] #[rustc_const_stable(feature = "const_int_methods", since = "1.32.0")] - #[inline] + #[inline(always)] pub const fn is_positive(self) -> bool { self > 0 } /// Returns `true` if `self` is negative and `false` if the number is zero or @@ -1764,7 +1763,7 @@ macro_rules! int_impl { /// ``` #[stable(feature = "rust1", since = "1.0.0")] #[rustc_const_stable(feature = "const_int_methods", since = "1.32.0")] - #[inline] + #[inline(always)] pub const fn is_negative(self) -> bool { self < 0 } /// Return the memory representation of this integer as a byte array in diff --git a/library/core/src/num/mod.rs b/library/core/src/num/mod.rs index f0bd976ba83d5..6032dc9a2d371 100644 --- a/library/core/src/num/mod.rs +++ b/library/core/src/num/mod.rs @@ -2,6 +2,7 @@ #![stable(feature = "rust1", since = "1.0.0")] +use crate::ascii; use crate::intrinsics; use crate::mem; use crate::str::FromStr; @@ -661,6 +662,31 @@ impl u8 { pub const fn is_ascii_control(&self) -> bool { matches!(*self, b'\0'..=b'\x1F' | b'\x7F') } + + /// Returns an iterator that produces an escaped version of a `u8`, + /// treating it as an ASCII character. + /// + /// The behavior is identical to [`ascii::escape_default`]. + /// + /// # Examples + /// + /// ``` + /// #![feature(inherent_ascii_escape)] + /// + /// assert_eq!("0", b'0'.escape_ascii().to_string()); + /// assert_eq!("\\t", b'\t'.escape_ascii().to_string()); + /// assert_eq!("\\r", b'\r'.escape_ascii().to_string()); + /// assert_eq!("\\n", b'\n'.escape_ascii().to_string()); + /// assert_eq!("\\'", b'\''.escape_ascii().to_string()); + /// assert_eq!("\\\"", b'"'.escape_ascii().to_string()); + /// assert_eq!("\\\\", b'\\'.escape_ascii().to_string()); + /// assert_eq!("\\x9d", b'\x9d'.escape_ascii().to_string()); + /// ``` + #[unstable(feature = "inherent_ascii_escape", issue = "77174")] + #[inline] + pub fn escape_ascii(&self) -> ascii::EscapeDefault { + ascii::escape_default(*self) + } } #[lang = "u16"] diff --git a/library/core/src/num/nonzero.rs b/library/core/src/num/nonzero.rs index 111feb7dbec69..6b9b435d47fe9 100644 --- a/library/core/src/num/nonzero.rs +++ b/library/core/src/num/nonzero.rs @@ -23,7 +23,7 @@ macro_rules! impl_nonzero_fmt { } macro_rules! nonzero_integers { - ( $( #[$stability: meta] $Ty: ident($Int: ty); )+ ) => { + ( $( #[$stability: meta] #[$const_new_unchecked_stability: meta] $Ty: ident($Int: ty); )+ ) => { $( /// An integer that is known not to equal zero. /// @@ -48,7 +48,7 @@ macro_rules! nonzero_integers { /// /// The value must not be zero. #[$stability] - #[rustc_const_stable(feature = "nonzero", since = "1.34.0")] + #[$const_new_unchecked_stability] #[inline] pub const unsafe fn new_unchecked(n: $Int) -> Self { // SAFETY: this is guaranteed to be safe by the caller. @@ -146,18 +146,18 @@ macro_rules! nonzero_integers { } nonzero_integers! { - #[stable(feature = "nonzero", since = "1.28.0")] NonZeroU8(u8); - #[stable(feature = "nonzero", since = "1.28.0")] NonZeroU16(u16); - #[stable(feature = "nonzero", since = "1.28.0")] NonZeroU32(u32); - #[stable(feature = "nonzero", since = "1.28.0")] NonZeroU64(u64); - #[stable(feature = "nonzero", since = "1.28.0")] NonZeroU128(u128); - #[stable(feature = "nonzero", since = "1.28.0")] NonZeroUsize(usize); - #[stable(feature = "signed_nonzero", since = "1.34.0")] NonZeroI8(i8); - #[stable(feature = "signed_nonzero", since = "1.34.0")] NonZeroI16(i16); - #[stable(feature = "signed_nonzero", since = "1.34.0")] NonZeroI32(i32); - #[stable(feature = "signed_nonzero", since = "1.34.0")] NonZeroI64(i64); - #[stable(feature = "signed_nonzero", since = "1.34.0")] NonZeroI128(i128); - #[stable(feature = "signed_nonzero", since = "1.34.0")] NonZeroIsize(isize); + #[stable(feature = "nonzero", since = "1.28.0")] #[rustc_const_stable(feature = "nonzero", since = "1.28.0")] NonZeroU8(u8); + #[stable(feature = "nonzero", since = "1.28.0")] #[rustc_const_stable(feature = "nonzero", since = "1.28.0")] NonZeroU16(u16); + #[stable(feature = "nonzero", since = "1.28.0")] #[rustc_const_stable(feature = "nonzero", since = "1.28.0")] NonZeroU32(u32); + #[stable(feature = "nonzero", since = "1.28.0")] #[rustc_const_stable(feature = "nonzero", since = "1.28.0")] NonZeroU64(u64); + #[stable(feature = "nonzero", since = "1.28.0")] #[rustc_const_stable(feature = "nonzero", since = "1.28.0")] NonZeroU128(u128); + #[stable(feature = "nonzero", since = "1.28.0")] #[rustc_const_stable(feature = "nonzero", since = "1.28.0")] NonZeroUsize(usize); + #[stable(feature = "signed_nonzero", since = "1.34.0")] #[rustc_const_stable(feature = "signed_nonzero", since = "1.34.0")] NonZeroI8(i8); + #[stable(feature = "signed_nonzero", since = "1.34.0")] #[rustc_const_stable(feature = "signed_nonzero", since = "1.34.0")] NonZeroI16(i16); + #[stable(feature = "signed_nonzero", since = "1.34.0")] #[rustc_const_stable(feature = "signed_nonzero", since = "1.34.0")] NonZeroI32(i32); + #[stable(feature = "signed_nonzero", since = "1.34.0")] #[rustc_const_stable(feature = "signed_nonzero", since = "1.34.0")] NonZeroI64(i64); + #[stable(feature = "signed_nonzero", since = "1.34.0")] #[rustc_const_stable(feature = "signed_nonzero", since = "1.34.0")] NonZeroI128(i128); + #[stable(feature = "signed_nonzero", since = "1.34.0")] #[rustc_const_stable(feature = "signed_nonzero", since = "1.34.0")] NonZeroIsize(isize); } macro_rules! from_str_radix_nzint_impl { @@ -191,13 +191,12 @@ macro_rules! nonzero_leading_trailing_zeros { /// Basic usage: /// /// ``` - /// #![feature(nonzero_leading_trailing_zeros)] #[doc = concat!("let n = std::num::", stringify!($Ty), "::new(", stringify!($LeadingTestExpr), ").unwrap();")] /// /// assert_eq!(n.leading_zeros(), 0); /// ``` - #[unstable(feature = "nonzero_leading_trailing_zeros", issue = "79143")] - #[rustc_const_unstable(feature = "nonzero_leading_trailing_zeros", issue = "79143")] + #[stable(feature = "nonzero_leading_trailing_zeros", since = "1.53.0")] + #[rustc_const_stable(feature = "nonzero_leading_trailing_zeros", since = "1.53.0")] #[inline] pub const fn leading_zeros(self) -> u32 { // SAFETY: since `self` can not be zero it is safe to call ctlz_nonzero @@ -214,13 +213,12 @@ macro_rules! nonzero_leading_trailing_zeros { /// Basic usage: /// /// ``` - /// #![feature(nonzero_leading_trailing_zeros)] #[doc = concat!("let n = std::num::", stringify!($Ty), "::new(0b0101000).unwrap();")] /// /// assert_eq!(n.trailing_zeros(), 3); /// ``` - #[unstable(feature = "nonzero_leading_trailing_zeros", issue = "79143")] - #[rustc_const_unstable(feature = "nonzero_leading_trailing_zeros", issue = "79143")] + #[stable(feature = "nonzero_leading_trailing_zeros", since = "1.53.0")] + #[rustc_const_stable(feature = "nonzero_leading_trailing_zeros", since = "1.53.0")] #[inline] pub const fn trailing_zeros(self) -> u32 { // SAFETY: since `self` can not be zero it is safe to call cttz_nonzero diff --git a/library/core/src/num/uint_macros.rs b/library/core/src/num/uint_macros.rs index 7393e7f923e1f..46638ea446316 100644 --- a/library/core/src/num/uint_macros.rs +++ b/library/core/src/num/uint_macros.rs @@ -32,10 +32,9 @@ macro_rules! uint_impl { /// # Examples /// /// ``` - /// #![feature(int_bits_const)] #[doc = concat!("assert_eq!(", stringify!($SelfT), "::BITS, ", stringify!($BITS), ");")] /// ``` - #[unstable(feature = "int_bits_const", issue = "76904")] + #[stable(feature = "int_bits_const", since = "1.53.0")] pub const BITS: u32 = $BITS; /// Converts a string slice in a given base to an integer. @@ -80,7 +79,7 @@ macro_rules! uint_impl { #[rustc_const_stable(feature = "const_math", since = "1.32.0")] #[doc(alias = "popcount")] #[doc(alias = "popcnt")] - #[inline] + #[inline(always)] pub const fn count_ones(self) -> u32 { intrinsics::ctpop(self as $ActualT) as u32 } @@ -96,7 +95,7 @@ macro_rules! uint_impl { /// ``` #[stable(feature = "rust1", since = "1.0.0")] #[rustc_const_stable(feature = "const_math", since = "1.32.0")] - #[inline] + #[inline(always)] pub const fn count_zeros(self) -> u32 { (!self).count_ones() } @@ -114,7 +113,7 @@ macro_rules! uint_impl { /// ``` #[stable(feature = "rust1", since = "1.0.0")] #[rustc_const_stable(feature = "const_math", since = "1.32.0")] - #[inline] + #[inline(always)] pub const fn leading_zeros(self) -> u32 { intrinsics::ctlz(self as $ActualT) as u32 } @@ -133,7 +132,7 @@ macro_rules! uint_impl { /// ``` #[stable(feature = "rust1", since = "1.0.0")] #[rustc_const_stable(feature = "const_math", since = "1.32.0")] - #[inline] + #[inline(always)] pub const fn trailing_zeros(self) -> u32 { intrinsics::cttz(self) as u32 } @@ -151,7 +150,7 @@ macro_rules! uint_impl { /// ``` #[stable(feature = "leading_trailing_ones", since = "1.46.0")] #[rustc_const_stable(feature = "leading_trailing_ones", since = "1.46.0")] - #[inline] + #[inline(always)] pub const fn leading_ones(self) -> u32 { (!self).leading_zeros() } @@ -170,7 +169,7 @@ macro_rules! uint_impl { /// ``` #[stable(feature = "leading_trailing_ones", since = "1.46.0")] #[rustc_const_stable(feature = "leading_trailing_ones", since = "1.46.0")] - #[inline] + #[inline(always)] pub const fn trailing_ones(self) -> u32 { (!self).trailing_zeros() } @@ -194,7 +193,7 @@ macro_rules! uint_impl { #[rustc_const_stable(feature = "const_math", since = "1.32.0")] #[must_use = "this returns the result of the operation, \ without modifying the original"] - #[inline] + #[inline(always)] pub const fn rotate_left(self, n: u32) -> Self { intrinsics::rotate_left(self, n as $SelfT) } @@ -219,7 +218,7 @@ macro_rules! uint_impl { #[rustc_const_stable(feature = "const_math", since = "1.32.0")] #[must_use = "this returns the result of the operation, \ without modifying the original"] - #[inline] + #[inline(always)] pub const fn rotate_right(self, n: u32) -> Self { intrinsics::rotate_right(self, n as $SelfT) } @@ -238,7 +237,7 @@ macro_rules! uint_impl { /// ``` #[stable(feature = "rust1", since = "1.0.0")] #[rustc_const_stable(feature = "const_math", since = "1.32.0")] - #[inline] + #[inline(always)] pub const fn swap_bytes(self) -> Self { intrinsics::bswap(self as $ActualT) as Self } @@ -259,7 +258,7 @@ macro_rules! uint_impl { /// ``` #[stable(feature = "reverse_bits", since = "1.37.0")] #[rustc_const_stable(feature = "const_math", since = "1.32.0")] - #[inline] + #[inline(always)] #[must_use] pub const fn reverse_bits(self) -> Self { intrinsics::bitreverse(self as $ActualT) as Self @@ -285,7 +284,7 @@ macro_rules! uint_impl { /// ``` #[stable(feature = "rust1", since = "1.0.0")] #[rustc_const_stable(feature = "const_math", since = "1.32.0")] - #[inline] + #[inline(always)] pub const fn from_be(x: Self) -> Self { #[cfg(target_endian = "big")] { @@ -317,7 +316,7 @@ macro_rules! uint_impl { /// ``` #[stable(feature = "rust1", since = "1.0.0")] #[rustc_const_stable(feature = "const_math", since = "1.32.0")] - #[inline] + #[inline(always)] pub const fn from_le(x: Self) -> Self { #[cfg(target_endian = "little")] { @@ -349,7 +348,7 @@ macro_rules! uint_impl { /// ``` #[stable(feature = "rust1", since = "1.0.0")] #[rustc_const_stable(feature = "const_math", since = "1.32.0")] - #[inline] + #[inline(always)] pub const fn to_be(self) -> Self { // or not to be? #[cfg(target_endian = "big")] { @@ -381,7 +380,7 @@ macro_rules! uint_impl { /// ``` #[stable(feature = "rust1", since = "1.0.0")] #[rustc_const_stable(feature = "const_math", since = "1.32.0")] - #[inline] + #[inline(always)] pub const fn to_le(self) -> Self { #[cfg(target_endian = "little")] { @@ -427,7 +426,7 @@ macro_rules! uint_impl { )] #[must_use = "this returns the result of the operation, \ without modifying the original"] - #[inline] + #[inline(always)] pub unsafe fn unchecked_add(self, rhs: Self) -> Self { // SAFETY: the caller must uphold the safety contract for // `unchecked_add`. @@ -465,7 +464,7 @@ macro_rules! uint_impl { )] #[must_use = "this returns the result of the operation, \ without modifying the original"] - #[inline] + #[inline(always)] pub unsafe fn unchecked_sub(self, rhs: Self) -> Self { // SAFETY: the caller must uphold the safety contract for // `unchecked_sub`. @@ -503,7 +502,7 @@ macro_rules! uint_impl { )] #[must_use = "this returns the result of the operation, \ without modifying the original"] - #[inline] + #[inline(always)] pub unsafe fn unchecked_mul(self, rhs: Self) -> Self { // SAFETY: the caller must uphold the safety contract for // `unchecked_mul`. @@ -728,7 +727,7 @@ macro_rules! uint_impl { #[must_use = "this returns the result of the operation, \ without modifying the original"] #[rustc_const_stable(feature = "const_saturating_int_methods", since = "1.47.0")] - #[inline] + #[inline(always)] pub const fn saturating_add(self, rhs: Self) -> Self { intrinsics::saturating_add(self, rhs) } @@ -748,7 +747,7 @@ macro_rules! uint_impl { #[must_use = "this returns the result of the operation, \ without modifying the original"] #[rustc_const_stable(feature = "const_saturating_int_methods", since = "1.47.0")] - #[inline] + #[inline(always)] pub const fn saturating_sub(self, rhs: Self) -> Self { intrinsics::saturating_sub(self, rhs) } @@ -814,7 +813,7 @@ macro_rules! uint_impl { #[rustc_const_stable(feature = "const_wrapping_math", since = "1.32.0")] #[must_use = "this returns the result of the operation, \ without modifying the original"] - #[inline] + #[inline(always)] pub const fn wrapping_add(self, rhs: Self) -> Self { intrinsics::wrapping_add(self, rhs) } @@ -834,7 +833,7 @@ macro_rules! uint_impl { #[rustc_const_stable(feature = "const_wrapping_math", since = "1.32.0")] #[must_use = "this returns the result of the operation, \ without modifying the original"] - #[inline] + #[inline(always)] pub const fn wrapping_sub(self, rhs: Self) -> Self { intrinsics::wrapping_sub(self, rhs) } @@ -857,7 +856,7 @@ macro_rules! uint_impl { #[rustc_const_stable(feature = "const_wrapping_math", since = "1.32.0")] #[must_use = "this returns the result of the operation, \ without modifying the original"] - #[inline] + #[inline(always)] pub const fn wrapping_mul(self, rhs: Self) -> Self { intrinsics::wrapping_mul(self, rhs) } @@ -879,7 +878,7 @@ macro_rules! uint_impl { #[rustc_const_stable(feature = "const_wrapping_int_methods", since = "1.52.0")] #[must_use = "this returns the result of the operation, \ without modifying the original"] - #[inline] + #[inline(always)] pub const fn wrapping_div(self, rhs: Self) -> Self { self / rhs } @@ -904,7 +903,7 @@ macro_rules! uint_impl { #[rustc_const_stable(feature = "const_euclidean_int_methods", since = "1.52.0")] #[must_use = "this returns the result of the operation, \ without modifying the original"] - #[inline] + #[inline(always)] pub const fn wrapping_div_euclid(self, rhs: Self) -> Self { self / rhs } @@ -927,7 +926,7 @@ macro_rules! uint_impl { #[rustc_const_stable(feature = "const_wrapping_int_methods", since = "1.52.0")] #[must_use = "this returns the result of the operation, \ without modifying the original"] - #[inline] + #[inline(always)] pub const fn wrapping_rem(self, rhs: Self) -> Self { self % rhs } @@ -953,7 +952,7 @@ macro_rules! uint_impl { #[rustc_const_stable(feature = "const_euclidean_int_methods", since = "1.52.0")] #[must_use = "this returns the result of the operation, \ without modifying the original"] - #[inline] + #[inline(always)] pub const fn wrapping_rem_euclid(self, rhs: Self) -> Self { self % rhs } @@ -1009,7 +1008,7 @@ macro_rules! uint_impl { #[rustc_const_stable(feature = "const_wrapping_math", since = "1.32.0")] #[must_use = "this returns the result of the operation, \ without modifying the original"] - #[inline] + #[inline(always)] pub const fn wrapping_shl(self, rhs: u32) -> Self { // SAFETY: the masking by the bitsize of the type ensures that we do not shift // out of bounds @@ -1041,7 +1040,7 @@ macro_rules! uint_impl { #[rustc_const_stable(feature = "const_wrapping_math", since = "1.32.0")] #[must_use = "this returns the result of the operation, \ without modifying the original"] - #[inline] + #[inline(always)] pub const fn wrapping_shr(self, rhs: u32) -> Self { // SAFETY: the masking by the bitsize of the type ensures that we do not shift // out of bounds @@ -1107,7 +1106,7 @@ macro_rules! uint_impl { #[rustc_const_stable(feature = "const_wrapping_math", since = "1.32.0")] #[must_use = "this returns the result of the operation, \ without modifying the original"] - #[inline] + #[inline(always)] pub const fn overflowing_add(self, rhs: Self) -> (Self, bool) { let (a, b) = intrinsics::add_with_overflow(self as $ActualT, rhs as $ActualT); (a as Self, b) @@ -1132,7 +1131,7 @@ macro_rules! uint_impl { #[rustc_const_stable(feature = "const_wrapping_math", since = "1.32.0")] #[must_use = "this returns the result of the operation, \ without modifying the original"] - #[inline] + #[inline(always)] pub const fn overflowing_sub(self, rhs: Self) -> (Self, bool) { let (a, b) = intrinsics::sub_with_overflow(self as $ActualT, rhs as $ActualT); (a as Self, b) @@ -1159,7 +1158,7 @@ macro_rules! uint_impl { #[rustc_const_stable(feature = "const_wrapping_math", since = "1.32.0")] #[must_use = "this returns the result of the operation, \ without modifying the original"] - #[inline] + #[inline(always)] pub const fn overflowing_mul(self, rhs: Self) -> (Self, bool) { let (a, b) = intrinsics::mul_with_overflow(self as $ActualT, rhs as $ActualT); (a as Self, b) @@ -1183,7 +1182,7 @@ macro_rules! uint_impl { /// ``` #[doc = concat!("assert_eq!(5", stringify!($SelfT), ".overflowing_div(2), (2, false));")] /// ``` - #[inline] + #[inline(always)] #[stable(feature = "wrapping", since = "1.7.0")] #[rustc_const_stable(feature = "const_overflowing_int_methods", since = "1.52.0")] #[must_use = "this returns the result of the operation, \ @@ -1213,7 +1212,7 @@ macro_rules! uint_impl { /// ``` #[doc = concat!("assert_eq!(5", stringify!($SelfT), ".overflowing_div_euclid(2), (2, false));")] /// ``` - #[inline] + #[inline(always)] #[stable(feature = "euclidean_division", since = "1.38.0")] #[rustc_const_stable(feature = "const_euclidean_int_methods", since = "1.52.0")] #[must_use = "this returns the result of the operation, \ @@ -1240,7 +1239,7 @@ macro_rules! uint_impl { /// ``` #[doc = concat!("assert_eq!(5", stringify!($SelfT), ".overflowing_rem(2), (1, false));")] /// ``` - #[inline] + #[inline(always)] #[stable(feature = "wrapping", since = "1.7.0")] #[rustc_const_stable(feature = "const_overflowing_int_methods", since = "1.52.0")] #[must_use = "this returns the result of the operation, \ @@ -1270,7 +1269,7 @@ macro_rules! uint_impl { /// ``` #[doc = concat!("assert_eq!(5", stringify!($SelfT), ".overflowing_rem_euclid(2), (1, false));")] /// ``` - #[inline] + #[inline(always)] #[stable(feature = "euclidean_division", since = "1.38.0")] #[rustc_const_stable(feature = "const_euclidean_int_methods", since = "1.52.0")] #[must_use = "this returns the result of the operation, \ @@ -1294,7 +1293,7 @@ macro_rules! uint_impl { #[doc = concat!("assert_eq!(0", stringify!($SelfT), ".overflowing_neg(), (0, false));")] #[doc = concat!("assert_eq!(2", stringify!($SelfT), ".overflowing_neg(), (-2i32 as ", stringify!($SelfT), ", true));")] /// ``` - #[inline] + #[inline(always)] #[stable(feature = "wrapping", since = "1.7.0")] #[rustc_const_stable(feature = "const_wrapping_math", since = "1.32.0")] pub const fn overflowing_neg(self) -> (Self, bool) { @@ -1321,7 +1320,7 @@ macro_rules! uint_impl { #[rustc_const_stable(feature = "const_wrapping_math", since = "1.32.0")] #[must_use = "this returns the result of the operation, \ without modifying the original"] - #[inline] + #[inline(always)] pub const fn overflowing_shl(self, rhs: u32) -> (Self, bool) { (self.wrapping_shl(rhs), (rhs > ($BITS - 1))) } @@ -1346,7 +1345,7 @@ macro_rules! uint_impl { #[rustc_const_stable(feature = "const_wrapping_math", since = "1.32.0")] #[must_use = "this returns the result of the operation, \ without modifying the original"] - #[inline] + #[inline(always)] pub const fn overflowing_shr(self, rhs: u32) -> (Self, bool) { (self.wrapping_shr(rhs), (rhs > ($BITS - 1))) } @@ -1459,7 +1458,7 @@ macro_rules! uint_impl { #[rustc_const_stable(feature = "const_euclidean_int_methods", since = "1.52.0")] #[must_use = "this returns the result of the operation, \ without modifying the original"] - #[inline] + #[inline(always)] #[rustc_inherit_overflow_checks] pub const fn div_euclid(self, rhs: Self) -> Self { self / rhs @@ -1487,7 +1486,7 @@ macro_rules! uint_impl { #[rustc_const_stable(feature = "const_euclidean_int_methods", since = "1.52.0")] #[must_use = "this returns the result of the operation, \ without modifying the original"] - #[inline] + #[inline(always)] #[rustc_inherit_overflow_checks] pub const fn rem_euclid(self, rhs: Self) -> Self { self % rhs @@ -1505,7 +1504,7 @@ macro_rules! uint_impl { /// ``` #[stable(feature = "rust1", since = "1.0.0")] #[rustc_const_stable(feature = "const_is_power_of_two", since = "1.32.0")] - #[inline] + #[inline(always)] pub const fn is_power_of_two(self) -> bool { self.count_ones() == 1 } diff --git a/library/core/src/ops/arith.rs b/library/core/src/ops/arith.rs index 92090d8e6fca7..a0577b287ce24 100644 --- a/library/core/src/ops/arith.rs +++ b/library/core/src/ops/arith.rs @@ -456,9 +456,13 @@ pub trait Div { } macro_rules! div_impl_integer { - ($($t:ty)*) => ($( + ($(($($t:ty)*) => $panic:expr),*) => ($($( /// This operation rounds towards zero, truncating any /// fractional part of the exact result. + /// + /// # Panics + /// + #[doc = $panic] #[stable(feature = "rust1", since = "1.0.0")] impl Div for $t { type Output = $t; @@ -468,10 +472,13 @@ macro_rules! div_impl_integer { } forward_ref_binop! { impl Div, div for $t, $t } - )*) + )*)*) } -div_impl_integer! { usize u8 u16 u32 u64 u128 isize i8 i16 i32 i64 i128 } +div_impl_integer! { + (usize u8 u16 u32 u64 u128) => "This operation will panic if `other == 0`.", + (isize i8 i16 i32 i64 i128) => "This operation will panic if `other == 0` or the division results in overflow." +} macro_rules! div_impl_float { ($($t:ty)*) => ($( @@ -549,9 +556,13 @@ pub trait Rem { } macro_rules! rem_impl_integer { - ($($t:ty)*) => ($( + ($(($($t:ty)*) => $panic:expr),*) => ($($( /// This operation satisfies `n % d == n - (n / d) * d`. The /// result has the same sign as the left operand. + /// + /// # Panics + /// + #[doc = $panic] #[stable(feature = "rust1", since = "1.0.0")] impl Rem for $t { type Output = $t; @@ -561,10 +572,13 @@ macro_rules! rem_impl_integer { } forward_ref_binop! { impl Rem, rem for $t, $t } - )*) + )*)*) } -rem_impl_integer! { usize u8 u16 u32 u64 u128 isize i8 i16 i32 i64 i128 } +rem_impl_integer! { + (usize u8 u16 u32 u64 u128) => "This operation will panic if `other == 0`.", + (isize i8 i16 i32 i64 i128) => "This operation will panic if `other == 0` or if `self / other` results in overflow." +} macro_rules! rem_impl_float { ($($t:ty)*) => ($( diff --git a/library/core/src/ops/deref.rs b/library/core/src/ops/deref.rs index 2419771eae212..dcf3ce070ec65 100644 --- a/library/core/src/ops/deref.rs +++ b/library/core/src/ops/deref.rs @@ -60,11 +60,12 @@ #[doc(alias = "*")] #[doc(alias = "&*")] #[stable(feature = "rust1", since = "1.0.0")] +#[rustc_diagnostic_item = "Deref"] pub trait Deref { /// The resulting type after dereferencing. #[stable(feature = "rust1", since = "1.0.0")] #[rustc_diagnostic_item = "deref_target"] - #[cfg_attr(not(bootstrap), lang = "deref_target")] + #[lang = "deref_target"] type Target: ?Sized; /// Dereferences the value. @@ -78,6 +79,7 @@ pub trait Deref { impl Deref for &T { type Target = T; + #[rustc_diagnostic_item = "noop_method_deref"] fn deref(&self) -> &T { *self } diff --git a/library/core/src/ops/drop.rs b/library/core/src/ops/drop.rs index ce7d1c3d06ddc..f4b1ec377d413 100644 --- a/library/core/src/ops/drop.rs +++ b/library/core/src/ops/drop.rs @@ -78,7 +78,7 @@ /// /// In other words, if you tried to explicitly call `Drop::drop` in the above example, you'd get a compiler error. /// -/// If you'd like explicitly call the destructor of a value, [`mem::drop`] can be used instead. +/// If you'd like to explicitly call the destructor of a value, [`mem::drop`] can be used instead. /// /// [`mem::drop`]: drop /// diff --git a/library/core/src/ops/index.rs b/library/core/src/ops/index.rs index a8dea4e9b4ea8..964378cc9c3c6 100644 --- a/library/core/src/ops/index.rs +++ b/library/core/src/ops/index.rs @@ -61,6 +61,10 @@ pub trait Index { type Output: ?Sized; /// Performs the indexing (`container[index]`) operation. + /// + /// # Panics + /// + /// May panic if the index is out of bounds. #[stable(feature = "rust1", since = "1.0.0")] #[track_caller] fn index(&self, index: Idx) -> &Self::Output; @@ -161,6 +165,10 @@ see chapter in The Book : Index { /// Performs the mutable indexing (`container[index]`) operation. + /// + /// # Panics + /// + /// May panic if the index is out of bounds. #[stable(feature = "rust1", since = "1.0.0")] #[track_caller] fn index_mut(&mut self, index: Idx) -> &mut Self::Output; diff --git a/library/core/src/option.rs b/library/core/src/option.rs index bcd2b207c4f6f..1c68abaf79d23 100644 --- a/library/core/src/option.rs +++ b/library/core/src/option.rs @@ -150,7 +150,7 @@ use crate::iter::{FromIterator, FusedIterator, TrustedLen}; use crate::pin::Pin; use crate::{ - convert, fmt, hint, mem, + hint, mem, ops::{self, Deref, DerefMut}, }; @@ -594,36 +594,6 @@ impl Option { } } - /// Inserts `value` into the option then returns a mutable reference to it. - /// - /// If the option already contains a value, the old value is dropped. - /// - /// # Example - /// - /// ``` - /// #![feature(option_insert)] - /// - /// let mut opt = None; - /// let val = opt.insert(1); - /// assert_eq!(*val, 1); - /// assert_eq!(opt.unwrap(), 1); - /// let val = opt.insert(2); - /// assert_eq!(*val, 2); - /// *val = 3; - /// assert_eq!(opt.unwrap(), 3); - /// ``` - #[inline] - #[unstable(feature = "option_insert", reason = "newly added", issue = "78271")] - pub fn insert(&mut self, value: T) -> &mut T { - *self = Some(value); - - match self { - Some(v) => v, - // SAFETY: the code above just filled the option - None => unsafe { hint::unreachable_unchecked() }, - } - } - ///////////////////////////////////////////////////////////////////////// // Iterator constructors ///////////////////////////////////////////////////////////////////////// @@ -851,12 +821,46 @@ impl Option { } ///////////////////////////////////////////////////////////////////////// - // Entry-like operations to insert if None and return a reference + // Entry-like operations to insert a value and return a reference ///////////////////////////////////////////////////////////////////////// + /// Inserts `value` into the option then returns a mutable reference to it. + /// + /// If the option already contains a value, the old value is dropped. + /// + /// See also [`Option::get_or_insert`], which doesn't update the value if + /// the option already contains [`Some`]. + /// + /// # Example + /// + /// ``` + /// let mut opt = None; + /// let val = opt.insert(1); + /// assert_eq!(*val, 1); + /// assert_eq!(opt.unwrap(), 1); + /// let val = opt.insert(2); + /// assert_eq!(*val, 2); + /// *val = 3; + /// assert_eq!(opt.unwrap(), 3); + /// ``` + #[inline] + #[stable(feature = "option_insert", since = "1.53.0")] + pub fn insert(&mut self, value: T) -> &mut T { + *self = Some(value); + + match self { + Some(v) => v, + // SAFETY: the code above just filled the option + None => unsafe { hint::unreachable_unchecked() }, + } + } + /// Inserts `value` into the option if it is [`None`], then /// returns a mutable reference to the contained value. /// + /// See also [`Option::insert`], which updates the value even if + /// the option already contains [`Some`]. + /// /// # Examples /// /// ``` @@ -877,6 +881,34 @@ impl Option { self.get_or_insert_with(|| value) } + /// Inserts the default value into the option if it is [`None`], then + /// returns a mutable reference to the contained value. + /// + /// # Examples + /// + /// ``` + /// #![feature(option_get_or_insert_default)] + /// + /// let mut x = None; + /// + /// { + /// let y: &mut u32 = x.get_or_insert_default(); + /// assert_eq!(y, &0); + /// + /// *y = 7; + /// } + /// + /// assert_eq!(x, Some(7)); + /// ``` + #[inline] + #[unstable(feature = "option_get_or_insert_default", issue = "82901")] + pub fn get_or_insert_default(&mut self) -> &mut T + where + T: Default, + { + self.get_or_insert_with(Default::default) + } + /// Inserts a value computed from `f` into the option if it is [`None`], /// then returns a mutable reference to the contained value. /// @@ -1093,90 +1125,6 @@ impl Option<&mut T> { } } -impl Option { - /// Consumes `self` while expecting [`None`] and returning nothing. - /// - /// # Panics - /// - /// Panics if the value is a [`Some`], with a panic message including the - /// passed message, and the content of the [`Some`]. - /// - /// # Examples - /// - /// ``` - /// #![feature(option_expect_none)] - /// - /// use std::collections::HashMap; - /// let mut squares = HashMap::new(); - /// for i in -10..=10 { - /// // This will not panic, since all keys are unique. - /// squares.insert(i, i * i).expect_none("duplicate key"); - /// } - /// ``` - /// - /// ```should_panic - /// #![feature(option_expect_none)] - /// - /// use std::collections::HashMap; - /// let mut sqrts = HashMap::new(); - /// for i in -10..=10 { - /// // This will panic, since both negative and positive `i` will - /// // insert the same `i * i` key, returning the old `Some(i)`. - /// sqrts.insert(i * i, i).expect_none("duplicate key"); - /// } - /// ``` - #[inline] - #[track_caller] - #[unstable(feature = "option_expect_none", reason = "newly added", issue = "62633")] - pub fn expect_none(self, msg: &str) { - if let Some(val) = self { - expect_none_failed(msg, &val); - } - } - - /// Consumes `self` while expecting [`None`] and returning nothing. - /// - /// # Panics - /// - /// Panics if the value is a [`Some`], with a custom panic message provided - /// by the [`Some`]'s value. - /// - /// [`Some(v)`]: Some - /// - /// # Examples - /// - /// ``` - /// #![feature(option_unwrap_none)] - /// - /// use std::collections::HashMap; - /// let mut squares = HashMap::new(); - /// for i in -10..=10 { - /// // This will not panic, since all keys are unique. - /// squares.insert(i, i * i).unwrap_none(); - /// } - /// ``` - /// - /// ```should_panic - /// #![feature(option_unwrap_none)] - /// - /// use std::collections::HashMap; - /// let mut sqrts = HashMap::new(); - /// for i in -10..=10 { - /// // This will panic, since both negative and positive `i` will - /// // insert the same `i * i` key, returning the old `Some(i)`. - /// sqrts.insert(i * i, i).unwrap_none(); - /// } - /// ``` - #[inline] - #[track_caller] - #[unstable(feature = "option_unwrap_none", reason = "newly added", issue = "62633")] - pub fn unwrap_none(self) { - if let Some(val) = self { - expect_none_failed("called `Option::unwrap_none()` on a `Some` value", &val); - } - } -} - impl Option { /// Returns the contained [`Some`] value or a default /// @@ -1275,7 +1223,8 @@ impl Option> { /// ``` #[inline] #[stable(feature = "transpose_result", since = "1.33.0")] - pub fn transpose(self) -> Result, E> { + #[rustc_const_unstable(feature = "const_option", issue = "67441")] + pub const fn transpose(self) -> Result, E> { match self { Some(Ok(x)) => Ok(Some(x)), Some(Err(e)) => Err(e), @@ -1292,14 +1241,6 @@ fn expect_failed(msg: &str) -> ! { panic!("{}", msg) } -// This is a separate function to reduce the code size of .expect_none() itself. -#[inline(never)] -#[cold] -#[track_caller] -fn expect_none_failed(msg: &str, value: &dyn fmt::Debug) -> ! { - panic!("{}: {:?}", msg, value) -} - ///////////////////////////////////////////////////////////////////////////// // Trait implementations ///////////////////////////////////////////////////////////////////////////// @@ -1750,7 +1691,11 @@ impl Option> { /// ``` #[inline] #[stable(feature = "option_flattening", since = "1.40.0")] - pub fn flatten(self) -> Option { - self.and_then(convert::identity) + #[rustc_const_unstable(feature = "const_option", issue = "67441")] + pub const fn flatten(self) -> Option { + match self { + Some(inner) => inner, + None => None, + } } } diff --git a/library/core/src/panicking.rs b/library/core/src/panicking.rs index af8a6101392a4..3e3e96fcd7f78 100644 --- a/library/core/src/panicking.rs +++ b/library/core/src/panicking.rs @@ -97,6 +97,7 @@ pub fn panic_fmt(fmt: fmt::Arguments<'_>) -> ! { pub enum AssertKind { Eq, Ne, + Match, } /// Internal function for `assert_eq!` and `assert_ne!` macros @@ -113,32 +114,54 @@ where T: fmt::Debug + ?Sized, U: fmt::Debug + ?Sized, { - #[track_caller] - fn inner( - kind: AssertKind, - left: &dyn fmt::Debug, - right: &dyn fmt::Debug, - args: Option>, - ) -> ! { - let op = match kind { - AssertKind::Eq => "==", - AssertKind::Ne => "!=", - }; - - match args { - Some(args) => panic!( - r#"assertion failed: `(left {} right)` + assert_failed_inner(kind, &left, &right, args) +} + +/// Internal function for `assert_match!` +#[cold] +#[track_caller] +#[doc(hidden)] +pub fn assert_matches_failed( + left: &T, + right: &str, + args: Option>, +) -> ! { + // Use the Display implementation to display the pattern. + struct Pattern<'a>(&'a str); + impl fmt::Debug for Pattern<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Display::fmt(self.0, f) + } + } + assert_failed_inner(AssertKind::Match, &left, &Pattern(right), args); +} + +/// Non-generic version of the above functions, to avoid code bloat. +#[track_caller] +fn assert_failed_inner( + kind: AssertKind, + left: &dyn fmt::Debug, + right: &dyn fmt::Debug, + args: Option>, +) -> ! { + let op = match kind { + AssertKind::Eq => "==", + AssertKind::Ne => "!=", + AssertKind::Match => "matches", + }; + + match args { + Some(args) => panic!( + r#"assertion failed: `(left {} right)` left: `{:?}`, - right: `{:?}: {}`"#, - op, left, right, args - ), - None => panic!( - r#"assertion failed: `(left {} right)` + right: `{:?}`: {}"#, + op, left, right, args + ), + None => panic!( + r#"assertion failed: `(left {} right)` left: `{:?}`, right: `{:?}`"#, - op, left, right, - ), - } + op, left, right, + ), } - inner(kind, &left, &right, args) } diff --git a/library/core/src/pin.rs b/library/core/src/pin.rs index b2de0e16a17bb..3d888299485b5 100644 --- a/library/core/src/pin.rs +++ b/library/core/src/pin.rs @@ -6,7 +6,7 @@ //! as moving an object with pointers to itself will invalidate them, which could cause undefined //! behavior. //! -//! At a high level, a [`Pin